mirror of
https://github.com/corda/corda.git
synced 2024-12-24 07:06:44 +00:00
INFRA-803 Rebuild Docker image tasks (#6804)
This commit is contained in:
parent
02018b75e9
commit
734d35b719
@ -127,6 +127,7 @@ buildscript {
|
|||||||
ext.commons_io_version = '2.6'
|
ext.commons_io_version = '2.6'
|
||||||
ext.controlsfx_version = '8.40.15'
|
ext.controlsfx_version = '8.40.15'
|
||||||
ext.detekt_version = constants.getProperty('detektVersion')
|
ext.detekt_version = constants.getProperty('detektVersion')
|
||||||
|
ext.docker_java_version = constants.getProperty("dockerJavaVersion")
|
||||||
if (JavaVersion.current().isJava8()) {
|
if (JavaVersion.current().isJava8()) {
|
||||||
ext.fontawesomefx_commons_version = '8.15'
|
ext.fontawesomefx_commons_version = '8.15'
|
||||||
ext.fontawesomefx_fontawesome_version = '4.7.0-5'
|
ext.fontawesomefx_fontawesome_version = '4.7.0-5'
|
||||||
|
11
buildSrc/build.gradle
Normal file
11
buildSrc/build.gradle
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
Properties constants = new Properties()
|
||||||
|
file("$rootDir/../constants.properties").withInputStream { constants.load(it) }
|
||||||
|
|
||||||
|
repositories {
|
||||||
|
mavenCentral()
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
compile group: 'com.github.docker-java', name: 'docker-java', version: constants.dockerJavaVersion
|
||||||
|
compile group: 'com.github.docker-java', name: 'docker-java-transport-httpclient5', version: constants.dockerJavaVersion
|
||||||
|
}
|
@ -0,0 +1,251 @@
|
|||||||
|
package net.corda.build.docker
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper
|
||||||
|
import com.fasterxml.jackson.databind.ObjectWriter
|
||||||
|
import com.fasterxml.jackson.databind.SerializationFeature
|
||||||
|
import com.github.dockerjava.api.DockerClient
|
||||||
|
import com.github.dockerjava.api.async.ResultCallback
|
||||||
|
import com.github.dockerjava.api.command.BuildImageResultCallback
|
||||||
|
import com.github.dockerjava.api.model.BuildResponseItem
|
||||||
|
import com.github.dockerjava.api.model.Identifier
|
||||||
|
import com.github.dockerjava.api.model.PushResponseItem
|
||||||
|
import com.github.dockerjava.api.model.Repository
|
||||||
|
import com.github.dockerjava.api.model.ResponseItem
|
||||||
|
import com.github.dockerjava.core.DefaultDockerClientConfig
|
||||||
|
import com.github.dockerjava.core.DockerClientBuilder
|
||||||
|
import com.github.dockerjava.core.DockerClientConfig
|
||||||
|
import com.github.dockerjava.httpclient5.ApacheDockerHttpClient
|
||||||
|
import com.github.dockerjava.transport.DockerHttpClient
|
||||||
|
import groovy.transform.CompileStatic
|
||||||
|
import groovy.transform.PackageScope
|
||||||
|
import org.gradle.api.GradleException
|
||||||
|
import org.gradle.api.Project
|
||||||
|
import org.gradle.api.logging.Logger
|
||||||
|
|
||||||
|
import java.util.function.Consumer
|
||||||
|
import java.util.stream.Collectors
|
||||||
|
import java.util.stream.Stream
|
||||||
|
|
||||||
|
class ObjectInputStreamWithCustomClassLoader extends ObjectInputStream {
|
||||||
|
private ClassLoader classLoader
|
||||||
|
|
||||||
|
ObjectInputStreamWithCustomClassLoader(InputStream ins, ClassLoader classLoader) {
|
||||||
|
super(ins)
|
||||||
|
this.classLoader = classLoader
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Class<?> resolveClass(ObjectStreamClass desc) {
|
||||||
|
return Class.forName(desc.getName(), false, classLoader)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class DockerError extends GradleException {
|
||||||
|
Integer code
|
||||||
|
String msg
|
||||||
|
|
||||||
|
@Override
|
||||||
|
String toString() {
|
||||||
|
return "Docker error${" " + code ?: ""}: $msg"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@CompileStatic
|
||||||
|
class DockerImage implements Serializable {
|
||||||
|
|
||||||
|
String id
|
||||||
|
|
||||||
|
File baseDir
|
||||||
|
|
||||||
|
Object dockerFile
|
||||||
|
|
||||||
|
String destination
|
||||||
|
|
||||||
|
Set<Identifier> tags = new HashSet<>()
|
||||||
|
|
||||||
|
private void writeObject(ObjectOutputStream oos) {
|
||||||
|
oos.writeObject(id)
|
||||||
|
oos.writeObject(baseDir)
|
||||||
|
oos.writeObject(dockerFile)
|
||||||
|
oos.writeObject(destination)
|
||||||
|
oos.writeInt(tags.size())
|
||||||
|
for(Identifier identifier in tags) {
|
||||||
|
oos.writeObject(identifier.repository)
|
||||||
|
oos.writeObject(identifier.tag.orElse(null))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void readObject(ObjectInputStream ois) {
|
||||||
|
id = ois.readObject() as String
|
||||||
|
baseDir = ois.readObject() as File
|
||||||
|
dockerFile = ois.readObject()
|
||||||
|
destination = ois.readObject()
|
||||||
|
int len = ois.readInt()
|
||||||
|
Set<Identifier> identifiers = new HashSet<>()
|
||||||
|
for(int i in 0..<len) {
|
||||||
|
Repository repository = ois.readObject() as Repository
|
||||||
|
String tag = ois.readObject()
|
||||||
|
identifiers.add(new Identifier(repository, tag))
|
||||||
|
}
|
||||||
|
tags = Collections.unmodifiableSet(identifiers)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@PackageScope
|
||||||
|
class BuildDockerImageCallback extends BuildImageResultCallback {
|
||||||
|
private static final ObjectMapper objectMapper = new ObjectMapper()
|
||||||
|
private static final ObjectWriter writer = objectMapper.writerWithDefaultPrettyPrinter()
|
||||||
|
.with(SerializationFeature.INDENT_OUTPUT)
|
||||||
|
private final Logger logger
|
||||||
|
|
||||||
|
BuildDockerImageCallback(Logger logger) {
|
||||||
|
this.logger = logger
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
void onNext(BuildResponseItem buildResponseItem) {
|
||||||
|
super.onNext(buildResponseItem)
|
||||||
|
if (buildResponseItem.errorIndicated) {
|
||||||
|
ResponseItem.ErrorDetail errorDetail = buildResponseItem.errorDetail
|
||||||
|
throw new DockerError(code : errorDetail.code, msg: errorDetail.message)
|
||||||
|
} else if(buildResponseItem.stream) {
|
||||||
|
String stream = buildResponseItem.stream
|
||||||
|
int sz = stream.size()
|
||||||
|
String msg
|
||||||
|
if (sz > 1) {
|
||||||
|
msg = stream.substring(0, sz - 1)
|
||||||
|
} else {
|
||||||
|
msg = null
|
||||||
|
}
|
||||||
|
if(msg) {
|
||||||
|
logger.info(buildResponseItem.stream.substring(0))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class DockerUtils {
|
||||||
|
|
||||||
|
@CompileStatic
|
||||||
|
static DockerClient fromConfig(DockerClientConfig cfg) {
|
||||||
|
DockerHttpClient httpClient = new ApacheDockerHttpClient.Builder()
|
||||||
|
.dockerHost(cfg.dockerHost)
|
||||||
|
.sslConfig(cfg.SSLConfig)
|
||||||
|
.build()
|
||||||
|
return DockerClientBuilder.getInstance(cfg)
|
||||||
|
.withDockerHttpClient(httpClient)
|
||||||
|
.build()
|
||||||
|
}
|
||||||
|
|
||||||
|
@CompileStatic
|
||||||
|
static identifier2string(Identifier identifier) {
|
||||||
|
return identifier.repository.name + identifier.tag.map { ":" + it}.orElse("")
|
||||||
|
}
|
||||||
|
|
||||||
|
@CompileStatic
|
||||||
|
static DefaultDockerClientConfig.Builder dockerClientConfigBuilder() {
|
||||||
|
def builder = DefaultDockerClientConfig.createDefaultConfigBuilder()
|
||||||
|
if(System.getProperty('os.name')?.startsWith('Windows')) {
|
||||||
|
builder.withDockerHost("npipe:////./pipe/docker_engine")
|
||||||
|
}
|
||||||
|
return builder
|
||||||
|
}
|
||||||
|
|
||||||
|
@CompileStatic
|
||||||
|
static def buildImage(Project project, DockerClient dockerClient, DockerImage img) {
|
||||||
|
BuildDockerImageCallback callback = new BuildDockerImageCallback(project.logger)
|
||||||
|
File dockerFile
|
||||||
|
switch(img.dockerFile) {
|
||||||
|
case String:
|
||||||
|
def candidate = new File(img.dockerFile as String)
|
||||||
|
if(candidate.isAbsolute()) {
|
||||||
|
dockerFile = candidate
|
||||||
|
} else {
|
||||||
|
dockerFile = new File(img.baseDir, img.dockerFile as String)
|
||||||
|
}
|
||||||
|
break
|
||||||
|
case File:
|
||||||
|
dockerFile = img.dockerFile as File
|
||||||
|
break
|
||||||
|
case null:
|
||||||
|
dockerFile = new File(img.baseDir, "Dockerfile")
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
throw new IllegalArgumentException("Unsupported object type for 'dockerFile': ${img.dockerFile.getClass().name}")
|
||||||
|
}
|
||||||
|
project.logger.info("Building image from ${img.baseDir.absolutePath} using Dockerfile: ${dockerFile}")
|
||||||
|
dockerClient.buildImageCmd(dockerFile)
|
||||||
|
.withTags(img.tags.collect { Identifier identifier ->
|
||||||
|
identifier.repository.name + identifier.tag.map { ':' + it}.orElse('')
|
||||||
|
}.toSet())
|
||||||
|
.exec(callback)
|
||||||
|
img.id = callback.awaitImageId()
|
||||||
|
}
|
||||||
|
|
||||||
|
@CompileStatic
|
||||||
|
static def buildImages(Project project, Iterable<DockerImage> images) {
|
||||||
|
DockerClientConfig cfg = dockerClientConfigBuilder().build()
|
||||||
|
DockerClient dockerClient = fromConfig(cfg)
|
||||||
|
images.each { buildImage(project, dockerClient, it) }
|
||||||
|
}
|
||||||
|
|
||||||
|
@CompileStatic
|
||||||
|
static def pushImages(Project project, Stream<DockerImage> imageStream) {
|
||||||
|
def logger = project.logger
|
||||||
|
Map<String, List<DockerImage>> destinationMap = imageStream.collect(
|
||||||
|
Collectors.<DockerImage, String>groupingBy{ DockerImage img -> img.destination ?: "" })
|
||||||
|
for(def entry in destinationMap.entrySet()) {
|
||||||
|
def destination = entry.key
|
||||||
|
List<DockerImage> images = entry.value
|
||||||
|
def configBuilder = dockerClientConfigBuilder()
|
||||||
|
System.getenv('DOCKER_USERNAME')?.with {
|
||||||
|
configBuilder.withRegistryUsername(it)
|
||||||
|
}
|
||||||
|
System.getenv('DOCKER_PASSWORD')?.with {
|
||||||
|
configBuilder.withRegistryPassword(it)
|
||||||
|
}
|
||||||
|
if(destination) {
|
||||||
|
configBuilder.withRegistryUrl(destination)
|
||||||
|
}
|
||||||
|
DockerClientConfig cfg = configBuilder.build()
|
||||||
|
DockerClient dockerClient = fromConfig(cfg)
|
||||||
|
logger.info("Ready to push to push to ${cfg.registryUrl}")
|
||||||
|
images.forEach(new Consumer<DockerImage>() {
|
||||||
|
@Override
|
||||||
|
void accept(DockerImage img) {
|
||||||
|
pushImage(project, dockerClient, img)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@CompileStatic
|
||||||
|
static def pushImage(Project project, DockerClient dockerClient, DockerImage image) {
|
||||||
|
def logger = project.logger
|
||||||
|
image.tags.each { identifier ->
|
||||||
|
ResultCallback<PushResponseItem> callback = new ResultCallback.Adapter<PushResponseItem>() {
|
||||||
|
@Override
|
||||||
|
void onNext(PushResponseItem item) {
|
||||||
|
if (item.errorIndicated) {
|
||||||
|
throw new DockerError(msg: item.errorDetail.message)
|
||||||
|
} else if (item.status == 'Preparing') {
|
||||||
|
logger.info("Preparing ${item.id}")
|
||||||
|
} else if (item.status == 'Waiting') {
|
||||||
|
logger.info("Waiting for ${item.id}")
|
||||||
|
} else if (item.status == 'Pushing') {
|
||||||
|
if (item.progressDetail) {
|
||||||
|
ResponseItem.ProgressDetail progressDetail = item.progressDetail
|
||||||
|
logger.debug("Pushing ${item.id}, progress ${progressDetail.current}/${progressDetail.total}")
|
||||||
|
}
|
||||||
|
} else if (item.status == 'Pushed') {
|
||||||
|
logger.info("Pushed ${item.id}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
dockerClient.pushImageCmd(identifier).exec(callback)
|
||||||
|
callback.awaitCompletion()
|
||||||
|
logger.info("Pushed ${identifier2string(identifier)}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -18,6 +18,7 @@ quasarVersion=0.7.13_r3
|
|||||||
# Quasar version to use with Java 11:
|
# Quasar version to use with Java 11:
|
||||||
quasarVersion11=0.8.1_r3
|
quasarVersion11=0.8.1_r3
|
||||||
jdkClassifier11=jdk11
|
jdkClassifier11=jdk11
|
||||||
|
dockerJavaVersion=3.2.5
|
||||||
proguardVersion=6.1.1
|
proguardVersion=6.1.1
|
||||||
bouncycastleVersion=1.66
|
bouncycastleVersion=1.66
|
||||||
classgraphVersion=4.8.90
|
classgraphVersion=4.8.90
|
||||||
@ -35,4 +36,3 @@ openSourceBranch=https://github.com/corda/corda/blob/release/os/4.4
|
|||||||
openSourceSamplesBranch=https://github.com/corda/samples/blob/release-V4
|
openSourceSamplesBranch=https://github.com/corda/samples/blob/release-V4
|
||||||
jolokiaAgentVersion=1.6.1
|
jolokiaAgentVersion=1.6.1
|
||||||
detektVersion=1.0.1
|
detektVersion=1.0.1
|
||||||
|
|
||||||
|
37
docker/README.md
Normal file
37
docker/README.md
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
# Building docker images
|
||||||
|
|
||||||
|
There are 3 Gradle tasks dedicated to this:
|
||||||
|
|
||||||
|
- `buildDockerFolder` will simply create the build folder with all the dockerfiles and the required
|
||||||
|
artifacts (like the `corda.jar`, `config-exporter.jar`).
|
||||||
|
This is an internal task and doesn't need to be explicitly invoked.
|
||||||
|
- `buildDockerImage` will build a docker image a publish it in the local docker cache
|
||||||
|
- `pushDockerImage` will build a docker image and push it to a remote docker registry.
|
||||||
|
It is possible to override the docker registry URL for all images using the `--registry-url`
|
||||||
|
command-line parameter, otherwise each image will be pushed to its own preconfigured docker registry
|
||||||
|
as specified in the `net.corda.build.docker.DockerImage.destination` field.
|
||||||
|
The latter field is currently left as `null` for all image variants, which means they are pushed to
|
||||||
|
[docker hub](https://hub.docker.com).
|
||||||
|
|
||||||
|
All 3 tasks use the command-line parameter `--image` to specify which image variant will be built
|
||||||
|
(it can be used multiple times to build multiple images).
|
||||||
|
To get a list of all supported variants simply launch with a random string:
|
||||||
|
```
|
||||||
|
gradlew docker:buildDockerImage --image NON_EXISTENT_IMAGE_VARIANT
|
||||||
|
```
|
||||||
|
results in
|
||||||
|
```
|
||||||
|
> Cannot convert string value 'NON_EXISTENT_IMAGE_VARIANT' to an enum value of type 'ImageVariant' (valid case insensitive values: UBUNTU_ZULU, UBUNTU_ZULU_11, AL_CORRETTO, OFFICIAL)
|
||||||
|
```
|
||||||
|
If no image variant is specified, all available image variants will be built.
|
||||||
|
|
||||||
|
The default repository for all images is `corda/corda` and you will need official R3 credentials
|
||||||
|
for Artifactory to push there. [Ross Nicoll](ross.nicoll@r3.com) (or other Artifactory administrators) can assist with this if needed,
|
||||||
|
otherwise you can override the repository name using the `docker.image.repository` Gradle property.
|
||||||
|
|
||||||
|
e.g.
|
||||||
|
```
|
||||||
|
gradlew docker:pushDockerImage -Pdocker.image.repository=MyOwnRepository/test --image OFFICIAL --registry-url registry.hub.docker.com
|
||||||
|
```
|
||||||
|
|
||||||
|
|
@ -1,14 +1,20 @@
|
|||||||
evaluationDependsOn(":node:capsule")
|
evaluationDependsOn(":node:capsule")
|
||||||
|
|
||||||
import com.bmuschko.gradle.docker.tasks.image.DockerBuildImage
|
|
||||||
import com.bmuschko.gradle.docker.tasks.image.DockerPushImage
|
|
||||||
|
|
||||||
|
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.LocalDateTime
|
||||||
import java.time.format.DateTimeFormatter
|
import java.time.format.DateTimeFormatter
|
||||||
|
import java.util.stream.Collectors
|
||||||
|
import java.util.stream.Stream
|
||||||
|
|
||||||
apply plugin: 'kotlin'
|
apply plugin: 'kotlin'
|
||||||
apply plugin: 'application'
|
apply plugin: 'application'
|
||||||
apply plugin: 'com.bmuschko.docker-remote-api'
|
|
||||||
|
|
||||||
// We need to set mainClassName before applying the shadow plugin.
|
// We need to set mainClassName before applying the shadow plugin.
|
||||||
mainClassName = 'net.corda.core.ConfigExporterMain'
|
mainClassName = 'net.corda.core.ConfigExporterMain'
|
||||||
@ -26,77 +32,239 @@ shadowJar {
|
|||||||
exclude '**/Log4j2Plugins.dat'
|
exclude '**/Log4j2Plugins.dat'
|
||||||
}
|
}
|
||||||
|
|
||||||
docker{
|
enum ImageVariant {
|
||||||
registryCredentials {
|
UBUNTU_ZULU("zulu", "Dockerfile", "1.8"),
|
||||||
url = System.env.DOCKER_URL ?: "hub.docker.com"
|
UBUNTU_ZULU_11("zulu", "Dockerfile11", "11"),
|
||||||
username = System.env.DOCKER_USERNAME
|
AL_CORRETTO("corretto", "DockerfileAL", "1.8"),
|
||||||
password = System.env.DOCKER_PASSWORD
|
OFFICIAL(UBUNTU_ZULU)
|
||||||
|
|
||||||
|
String knownAs
|
||||||
|
String dockerFile
|
||||||
|
String javaVersion
|
||||||
|
|
||||||
|
String versionString(String baseTag, String version) {
|
||||||
|
return "${baseTag}-${knownAs}" +
|
||||||
|
(knownAs.isEmpty() ? "" : "-") +
|
||||||
|
"java${javaVersion}-" + version
|
||||||
|
}
|
||||||
|
|
||||||
|
ImageVariant(ImageVariant other) {
|
||||||
|
this.knownAs = other.knownAs
|
||||||
|
this.dockerFile = other.dockerFile
|
||||||
|
this.javaVersion = other.javaVersion
|
||||||
|
}
|
||||||
|
|
||||||
|
ImageVariant(String knownAs, String dockerFile, String javaVersion) {
|
||||||
|
this.knownAs = knownAs
|
||||||
|
this.dockerFile = dockerFile
|
||||||
|
this.javaVersion = javaVersion
|
||||||
|
}
|
||||||
|
|
||||||
|
static final String getRepository(Project project) {
|
||||||
|
return project.properties.getOrDefault("docker.image.repository", "corda/corda")
|
||||||
|
}
|
||||||
|
|
||||||
|
static private final String runTime = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss"))
|
||||||
|
|
||||||
|
def getName(Project project) {
|
||||||
|
return versionString(getRepository(project), project.version.toString().toLowerCase())
|
||||||
|
}
|
||||||
|
|
||||||
|
Set<Identifier> buildTags(Project project) {
|
||||||
|
final String suffix = project.version.toString().toLowerCase().contains("snapshot") ? runTime : "RELEASE"
|
||||||
|
return [suffix, "latest"].stream().map {
|
||||||
|
toAppend -> "${getName(project)}:${toAppend}".toString()
|
||||||
|
}.map(Identifier.&fromCompoundString).collect(Collectors.toSet())
|
||||||
|
}
|
||||||
|
|
||||||
|
static Set<ImageVariant> 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<ImageVariant> variants) {
|
||||||
|
ImageVariant.toBeBuilt = new HashSet<>(variants)
|
||||||
|
}
|
||||||
|
|
||||||
|
@OptionValues("image")
|
||||||
|
Collection<ImageVariant> allVariants() {
|
||||||
|
return EnumSet.allOf(ImageVariant.class)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Input
|
||||||
|
Iterable<ImageVariant> variantsToBuild() {
|
||||||
|
return ImageVariant.toBeBuilt
|
||||||
|
}
|
||||||
|
|
||||||
|
@InputFiles
|
||||||
|
FileCollection getDockerBuildFiles() {
|
||||||
|
return project.fileTree("${project.projectDir}/src/docker/build")
|
||||||
|
}
|
||||||
|
|
||||||
|
@InputFiles
|
||||||
|
FileCollection getShellScripts() {
|
||||||
|
return project.fileTree("${project.projectDir}/src/bash")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Lazy
|
||||||
|
private File cordaJar = project.findProject(":node:capsule").tasks.buildCordaJAR.outputs.files.singleFile
|
||||||
|
|
||||||
|
@Lazy
|
||||||
|
private File configExporter = project.tasks.shadowJar.outputs.files.singleFile
|
||||||
|
|
||||||
|
@Lazy
|
||||||
|
private File dbMigrator = project.findProject(":tools:dbmigration").tasks.shadowJar.outputs.files.singleFile
|
||||||
|
|
||||||
|
@InputFiles
|
||||||
|
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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
final dockerTempDir = file("$buildDir/docker-temp")
|
|
||||||
|
|
||||||
task buildDockerFolder(type: Copy) {
|
class BuildDockerImageTask extends DefaultTask {
|
||||||
into dockerTempDir
|
|
||||||
from "src/bash/run-corda.sh"
|
@Option(option = "image", description = "Docker image variants that will be built")
|
||||||
from(project(':node:capsule').tasks.buildCordaJAR) {
|
void setVariants(List<ImageVariant> variants) {
|
||||||
rename 'corda-(.*)', 'corda.jar'
|
ImageVariant.toBeBuilt = new HashSet<>(variants)
|
||||||
}
|
}
|
||||||
from(shadowJar) {
|
|
||||||
rename 'config-exporter-(.*).jar', 'config-exporter.jar'
|
@OptionValues("image")
|
||||||
|
Collection<ImageVariant> allVariants() {
|
||||||
|
return EnumSet.allOf(ImageVariant.class)
|
||||||
|
}
|
||||||
|
|
||||||
|
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<ImageVariant, DockerImage> 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
from "src/config/starting-node.conf"
|
|
||||||
from "src/bash/generate-config.sh"
|
|
||||||
from "src/docker/DockerfileAL"
|
|
||||||
from "src/docker/Dockerfile"
|
|
||||||
from "src/docker/Dockerfile11"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
final String runTime = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss"))
|
class PushDockerImage extends DefaultTask {
|
||||||
final String suffix = project.version.toString().toLowerCase().contains("snapshot") ? runTime : "RELEASE"
|
@Option(option = "image", description = "Docker image variants that will be built")
|
||||||
final zuluBuildTags = ["corda/corda-zulu-java${JavaVersion.current()}-${project.version.toString().toLowerCase()}:${suffix}", "corda/corda-zulu-java${JavaVersion.current()}-${project.version.toString().toLowerCase()}:latest"]
|
void setVariants(List<ImageVariant> variants) {
|
||||||
final correttoBuildTags = ["corda/corda-corretto-java${JavaVersion.current()}-${project.version.toString().toLowerCase()}:${suffix}", "corda/corda-corretto-java${JavaVersion.current()}-${project.version.toString().toLowerCase()}:latest"]
|
ImageVariant.toBeBuilt = new HashSet<>(variants)
|
||||||
|
}
|
||||||
|
|
||||||
task buildOfficialZuluDockerImage(type: DockerBuildImage, dependsOn: [buildDockerFolder]) {
|
@OptionValues("image")
|
||||||
inputDir = dockerTempDir
|
Collection<ImageVariant> allVariants() {
|
||||||
tags = zuluBuildTags
|
return EnumSet.allOf(ImageVariant.class)
|
||||||
dockerFile = new File(dockerTempDir, "Dockerfile")
|
}
|
||||||
|
|
||||||
|
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<DockerImage> 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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
task buildOfficialZuluJDK11DockerImage(type: DockerBuildImage, dependsOn: [buildDockerFolder]) {
|
def buildDockerFolderTask = tasks.register("buildDockerFolder", BuildDockerFolderTask)
|
||||||
inputDir = dockerTempDir
|
def buildDockerImageTask = tasks.register("buildDockerImage", BuildDockerImageTask) {
|
||||||
tags = zuluBuildTags
|
from(buildDockerFolderTask.get())
|
||||||
dockerFile = new File(dockerTempDir, "Dockerfile11")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
task buildOfficialCorrettoDockerImage(type: DockerBuildImage, dependsOn: [buildDockerFolder]) {
|
tasks.register("pushDockerImage", PushDockerImage) {
|
||||||
inputDir = dockerTempDir
|
from(buildDockerImageTask.get())
|
||||||
tags = correttoBuildTags
|
|
||||||
dockerFile = new File(dockerTempDir, "DockerfileAL")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
task pushZuluTimeStampedTag('type': DockerPushImage, dependsOn: [buildOfficialZuluDockerImage]){
|
|
||||||
imageName = zuluBuildTags[0]
|
|
||||||
}
|
|
||||||
|
|
||||||
task pushZuluLatestTag('type': DockerPushImage, dependsOn: [buildOfficialZuluDockerImage]){
|
|
||||||
imageName = zuluBuildTags[1]
|
|
||||||
}
|
|
||||||
|
|
||||||
task pushZulu11TimeStampedTag('type': DockerPushImage, dependsOn: [buildOfficialZuluJDK11DockerImage]){
|
|
||||||
imageName = zuluBuildTags[0]
|
|
||||||
}
|
|
||||||
|
|
||||||
task pushZulu11LatestTag('type': DockerPushImage, dependsOn: [buildOfficialZuluJDK11DockerImage]){
|
|
||||||
imageName = zuluBuildTags[1]
|
|
||||||
}
|
|
||||||
|
|
||||||
task pushCorrettoTimeStampedTag('type': DockerPushImage, dependsOn: [buildOfficialCorrettoDockerImage]){
|
|
||||||
imageName = correttoBuildTags[0]
|
|
||||||
}
|
|
||||||
|
|
||||||
task pushCorrettoLatestTag('type': DockerPushImage, dependsOn: [buildOfficialCorrettoDockerImage]){
|
|
||||||
imageName = correttoBuildTags[1]
|
|
||||||
}
|
|
||||||
|
|
||||||
task pushOfficialImages(dependsOn: [pushZuluTimeStampedTag, pushZuluLatestTag, pushCorrettoTimeStampedTag, pushCorrettoLatestTag])
|
|
||||||
|
@ -41,7 +41,7 @@ configurations {
|
|||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
compile "com.microsoft.azure:azure:1.22.0"
|
compile "com.microsoft.azure:azure:1.22.0"
|
||||||
compile "com.github.docker-java:docker-java:3.0.6"
|
compile "com.github.docker-java:docker-java:$docker_java_version"
|
||||||
|
|
||||||
testCompile "org.jetbrains.kotlin:kotlin-test"
|
testCompile "org.jetbrains.kotlin:kotlin-test"
|
||||||
testCompile "org.jetbrains.kotlin:kotlin-test-junit"
|
testCompile "org.jetbrains.kotlin:kotlin-test-junit"
|
||||||
|
@ -45,14 +45,16 @@ class DockerInstantiator(private val volume: LocalVolume,
|
|||||||
val ports = (portsToOpen + Constants.NODE_RPC_ADMIN_PORT).map { ExposedPort.tcp(it) }
|
val ports = (portsToOpen + Constants.NODE_RPC_ADMIN_PORT).map { ExposedPort.tcp(it) }
|
||||||
.map { PortBinding(null, it) }
|
.map { PortBinding(null, it) }
|
||||||
.let { Ports(*it.toTypedArray()) }
|
.let { Ports(*it.toTypedArray()) }
|
||||||
|
val hostConfig = HostConfig()
|
||||||
|
.withBinds(Bind(volume.getPath(), nodeInfosVolume))
|
||||||
|
.withPortBindings(ports)
|
||||||
|
.withPublishAllPorts(true)
|
||||||
|
.withNetworkMode(networkId)
|
||||||
val createCmd = localClient.createContainerCmd(imageId)
|
val createCmd = localClient.createContainerCmd(imageId)
|
||||||
.withName(instanceName)
|
.withName(instanceName)
|
||||||
.withVolumes(nodeInfosVolume)
|
.withVolumes(nodeInfosVolume)
|
||||||
.withBinds(Bind(volume.getPath(), nodeInfosVolume))
|
.withHostConfig(hostConfig)
|
||||||
.withPortBindings(ports)
|
|
||||||
.withExposedPorts(ports.bindings.map { it.key })
|
.withExposedPorts(ports.bindings.map { it.key })
|
||||||
.withPublishAllPorts(true)
|
|
||||||
.withNetworkMode(networkId)
|
|
||||||
.withEnv(convertedEnv).exec()
|
.withEnv(convertedEnv).exec()
|
||||||
|
|
||||||
localClient.startContainerCmd(createCmd.id).exec()
|
localClient.startContainerCmd(createCmd.id).exec()
|
||||||
|
@ -16,7 +16,10 @@ object DockerUtils {
|
|||||||
|
|
||||||
fun createLocalDockerClient(): DockerClient {
|
fun createLocalDockerClient(): DockerClient {
|
||||||
return if (SystemUtils.IS_OS_WINDOWS) {
|
return if (SystemUtils.IS_OS_WINDOWS) {
|
||||||
DockerClientBuilder.getInstance("tcp://127.0.0.1:2375").build()
|
val cfg = DefaultDockerClientConfig.createDefaultConfigBuilder()
|
||||||
|
.withDockerHost("tcp://127.0.0.1:2375")
|
||||||
|
.build()
|
||||||
|
DockerClientBuilder.getInstance(cfg).build()
|
||||||
} else {
|
} else {
|
||||||
DockerClientBuilder.getInstance().build()
|
DockerClientBuilder.getInstance().build()
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
package net.corda.networkbuilder.nodes
|
package net.corda.networkbuilder.nodes
|
||||||
|
|
||||||
import com.github.dockerjava.api.model.BuildResponseItem
|
import com.github.dockerjava.api.model.BuildResponseItem
|
||||||
import com.github.dockerjava.core.async.ResultCallbackTemplate
|
import com.github.dockerjava.api.async.ResultCallbackTemplate
|
||||||
import com.github.dockerjava.core.command.BuildImageResultCallback
|
import com.github.dockerjava.api.command.BuildImageResultCallback
|
||||||
import com.typesafe.config.Config
|
import com.typesafe.config.Config
|
||||||
import com.typesafe.config.ConfigFactory
|
import com.typesafe.config.ConfigFactory
|
||||||
import com.typesafe.config.ConfigValueFactory
|
import com.typesafe.config.ConfigValueFactory
|
||||||
|
Loading…
Reference in New Issue
Block a user