evaluationDependsOn(":node:capsule") import com.github.dockerjava.api.model.Identifier import net.corda.build.docker.DockerImage import net.corda.build.docker.DockerUtils import net.corda.build.docker.ObjectInputStreamWithCustomClassLoader import java.nio.file.Files import java.nio.file.StandardCopyOption import java.time.LocalDateTime import java.time.format.DateTimeFormatter import java.util.stream.Collectors import java.util.stream.Stream apply plugin: 'org.jetbrains.kotlin.jvm' apply plugin: 'application' // We need to set mainClassName before applying the shadow plugin. mainClassName = 'net.corda.core.ConfigExporterMain' apply plugin: 'com.github.johnrengelman.shadow' dependencies{ implementation project(':node') implementation project(':node-api') implementation project(':common-configuration-parsing') implementation project(':common-validation') implementation "com.typesafe:config:$typesafe_config_version" } shadowJar { baseName = 'config-exporter' classifier = null version = null zip64 true exclude '**/Log4j2Plugins.dat' } enum ImageVariant { UBUNTU_ZULU("Dockerfile", "17", "zulu-openjdk"), AL_CORRETTO("DockerfileAL", "17", "amazonlinux2"), OFFICIAL(UBUNTU_ZULU) String dockerFile String javaVersion String baseImageFullName ImageVariant(ImageVariant other) { this.dockerFile = other.dockerFile this.javaVersion = other.javaVersion this.baseImageFullName = other.baseImageFullName } ImageVariant(String dockerFile, String javaVersion, String baseImageFullName) { this.dockerFile = dockerFile this.javaVersion = javaVersion this.baseImageFullName = baseImageFullName } static final String getRepository(Project project) { return project.properties.getOrDefault("docker.image.repository", "corda/corda") } Set buildTags(Project project) { return ["${project.version.toString().toLowerCase()}-${baseImageFullName}"].stream().map { toAppend -> "${getRepository(project)}:${toAppend}".toString() }.map(Identifier.&fromCompoundString).collect(Collectors.toSet()) } static Set toBeBuilt = Arrays.stream(values()).collect(Collectors.toSet()) } class BuildDockerFolderTask extends DefaultTask { @Option(option = "image", description = "Docker image variants that will be built") void setVariants(List variants) { ImageVariant.toBeBuilt = new HashSet<>(variants) } @OptionValues("image") Collection getAllVariants() { return EnumSet.allOf(ImageVariant.class) } @Input Iterable getVariantsToBuild() { return ImageVariant.toBeBuilt } @InputFiles FileCollection getDockerBuildFiles() { return project.fileTree("${project.projectDir}/src/docker/build") } @InputFiles FileCollection getShellScripts() { return project.fileTree("${project.projectDir}/src/bash") } private File cordaJar = project.findProject(":node:capsule").tasks.buildCordaJAR.outputs.files.filter { it.name.contains("corda") }.singleFile private File configExporter = project.tasks.shadowJar.outputs.files.singleFile private FileCollection getRequiredArtifacts() { FileCollection res = project.tasks.shadowJar.outputs.files def capsuleProject = project.findProject(":node:capsule") def capsuleTaksOutput = capsuleProject.tasks.buildCordaJAR.outputs.files res += capsuleTaksOutput return res } @OutputFiles FileCollection getDockerFiles() { return project.objects.fileCollection().from(ImageVariant.toBeBuilt.stream().map { new File(dockerBuildDir, it.dockerFile) }.collect(Collectors.toList())) } @OutputDirectory final File dockerBuildDir = project.file("${project.buildDir}/docker/build") @TaskAction def run() { for(ImageVariant imageVariant : ImageVariant.toBeBuilt) { def sourceFile = project.projectDir.toPath().resolve("src/docker/${imageVariant.dockerFile}") def destinationFile = dockerBuildDir.toPath().resolve(imageVariant.dockerFile) Files.copy(sourceFile, destinationFile, StandardCopyOption.REPLACE_EXISTING) } Files.copy(cordaJar.toPath(), dockerBuildDir.toPath().resolve("corda.jar"), StandardCopyOption.REPLACE_EXISTING) Files.copy(configExporter.toPath(), dockerBuildDir.toPath().resolve("config-exporter.jar"), StandardCopyOption.REPLACE_EXISTING) ["src/bash/run-corda.sh", "src/config/starting-node.conf", "src/bash/generate-config.sh"].forEach { def source = project.file(it).toPath() Files.copy(source, dockerBuildDir.toPath().resolve(source.fileName), StandardCopyOption.REPLACE_EXISTING) } } } class BuildDockerImageTask extends DefaultTask { @Option(option = "image", description = "Docker image variants that will be built") void setVariants(List variants) { ImageVariant.toBeBuilt = new HashSet<>(variants) } @OptionValues("image") Collection getAllVariants() { return EnumSet.allOf(ImageVariant.class) } @OutputDirectory final File dockerBuildDir = project.file("${project.buildDir}/docker/build") @OutputDirectory final File dockerBuiltImageDir = project.file("${project.buildDir}/docker/images") @Input String getRepository() { return ImageVariant.getRepository(project) } @InputFiles FileCollection dockerFiles private Map images @OutputFiles FileCollection getImageFiles() { return project.objects.fileCollection().from(ImageVariant.toBeBuilt.stream().map { imageVariant -> dockerBuiltImageDir.toPath().resolve(imageVariant.toString()).toFile() }.collect(Collectors.toList())) } void from(BuildDockerFolderTask buildDockerFolderTask) { dockerFiles = buildDockerFolderTask.outputs.files } @Override Task configure(Closure closure) { return super.configure(closure) } @TaskAction def run() { this.@images = ImageVariant.toBeBuilt.stream().map { imageVariant -> new Tuple2<>(imageVariant, new DockerImage( baseDir: dockerBuildDir, dockerFile: imageVariant.dockerFile, tags: imageVariant.buildTags(project))) }.collect(Collectors.toMap({it.first}, {it.second})) DockerUtils.buildImages(project, this.@images.values()) images.entrySet().forEach { entry -> ImageVariant imageVariant = entry.key def destinationFile = dockerBuiltImageDir.toPath().resolve(imageVariant.toString()) new ObjectOutputStream(Files.newOutputStream(destinationFile)).withStream { it.writeObject(entry.value) } } } } class PushDockerImage extends DefaultTask { @Option(option = "image", description = "Docker image variants that will be built") void setVariants(List variants) { ImageVariant.toBeBuilt = new HashSet<>(variants) } @OptionValues("image") Collection getAllVariants() { return EnumSet.allOf(ImageVariant.class) } private static String registryURL @Option(option = "registry-url", description = "Docker image registry where images will be pushed, defaults to DockerHub") void setRegistryURL(String registryURL) { PushDockerImage.registryURL = registryURL } @InputFiles FileCollection imageFiles def from(BuildDockerImageTask buildDockerImageTask) { imageFiles = buildDockerImageTask.outputs.files } @TaskAction def run() { def classLoader = DockerImage.class.classLoader Stream imageStream = imageFiles.files.stream().filter{ it.isFile() }.map { new ObjectInputStreamWithCustomClassLoader(Files.newInputStream(it.toPath()), classLoader).withStream { DockerImage image = it.readObject() if(PushDockerImage.registryURL) { image.destination = PushDockerImage.registryURL } image } } DockerUtils.pushImages(project, imageStream) } } def buildDockerFolderTask = tasks.register("buildDockerFolder", BuildDockerFolderTask) { dependsOn = [ tasks.named('shadowJar') ] } def buildDockerImageTask = tasks.register("buildDockerImage", BuildDockerImageTask) { from(buildDockerFolderTask.get()) } tasks.register("pushDockerImage", PushDockerImage) { from(buildDockerImageTask.get()) }