From 2fdf7e1e0e703b3a750b88262a1952ff170b7c37 Mon Sep 17 00:00:00 2001 From: nanabell Date: Fri, 28 Aug 2020 10:08:51 +0200 Subject: [PATCH 01/15] Add support for DockerContext to connect to Docker via SSL --- .../agent/docker/config/AgentSSLConfig.kt | 26 +++++++++ .../agent/docker/service/DockerContext.kt | 56 ++++++++++++++++--- src/main/resources/templates/application.yml | 13 +++++ 3 files changed, 88 insertions(+), 7 deletions(-) create mode 100644 src/main/kotlin/com/ilunos/agent/docker/config/AgentSSLConfig.kt diff --git a/src/main/kotlin/com/ilunos/agent/docker/config/AgentSSLConfig.kt b/src/main/kotlin/com/ilunos/agent/docker/config/AgentSSLConfig.kt new file mode 100644 index 0000000..f4a9845 --- /dev/null +++ b/src/main/kotlin/com/ilunos/agent/docker/config/AgentSSLConfig.kt @@ -0,0 +1,26 @@ +package com.ilunos.agent.docker.config + +import com.fasterxml.jackson.annotation.JsonProperty +import io.micronaut.context.annotation.ConfigurationProperties +import java.nio.file.Path + +@ConfigurationProperties("agent.ssl") +class AgentSSLConfig { + + var type: AgentSSLType = AgentSSLType.NONE + + @JsonProperty("keystore-file") + var keystoreFile: Path? = null + + @JsonProperty("keystore-pass") + var keystorePass: String? = null + + @JsonProperty("pem-directory") + var pemDirectory: Path? = null + + enum class AgentSSLType { + NONE, + KEYSTORE, + PEM_FILES + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/ilunos/agent/docker/service/DockerContext.kt b/src/main/kotlin/com/ilunos/agent/docker/service/DockerContext.kt index ffa1e82..26c81d9 100644 --- a/src/main/kotlin/com/ilunos/agent/docker/service/DockerContext.kt +++ b/src/main/kotlin/com/ilunos/agent/docker/service/DockerContext.kt @@ -8,28 +8,31 @@ import com.github.dockerjava.api.model.Container import com.github.dockerjava.api.model.Image import com.github.dockerjava.api.model.Info import com.github.dockerjava.api.model.Version -import com.github.dockerjava.core.DefaultDockerClientConfig -import com.github.dockerjava.core.DockerClientBuilder +import com.github.dockerjava.core.* import com.github.dockerjava.httpclient5.ApacheDockerHttpClient import com.ilunos.agent.docker.config.AgentConfig +import com.ilunos.agent.docker.config.AgentSSLConfig import com.ilunos.agent.docker.exception.AgentNotConnectedException import com.ilunos.agent.docker.model.ConnectionStatus import io.micronaut.context.annotation.Context import io.micronaut.context.annotation.Infrastructure +import io.micronaut.context.exceptions.ConfigurationException import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.launch +import org.apache.commons.io.FilenameUtils import org.slf4j.Logger import org.slf4j.LoggerFactory +import java.nio.file.Files +import java.security.KeyStore import java.util.* @Context @Infrastructure -class DockerContext(private val agentConfig: AgentConfig) { +class DockerContext(private val agentConfig: AgentConfig, private val agentSSLConfig: AgentSSLConfig) { private val logger: Logger = LoggerFactory.getLogger(DockerContext::class.java) private lateinit var client: DockerClient - private var status: ConnectionStatus = ConnectionStatus.UNKNOWN init { @@ -157,9 +160,8 @@ class DockerContext(private val agentConfig: AgentConfig) { fun connect() { disconnect() - val clientConfig = DefaultDockerClientConfig.createDefaultConfigBuilder() - .withDockerHost(agentConfig.url) - .build() + val configBuilder = DefaultDockerClientConfig.createDefaultConfigBuilder().withDockerHost(agentConfig.url) + val clientConfig = loadSSLConfig(configBuilder) val httpClient = ApacheDockerHttpClient.Builder() .dockerHost(clientConfig.dockerHost) @@ -190,6 +192,46 @@ class DockerContext(private val agentConfig: AgentConfig) { this.status = ConnectionStatus.DISCONNECTED } + private fun loadSSLConfig(clientConfigBuilder: DefaultDockerClientConfig.Builder): DockerClientConfig { + if (agentSSLConfig.type == AgentSSLConfig.AgentSSLType.KEYSTORE) { + val keystoreFile = agentSSLConfig.keystoreFile + val keystorePass = agentSSLConfig.keystorePass + + if (keystoreFile == null || !Files.exists(keystoreFile)) + throw ConfigurationException("Required Property 'agent.ssl.keystore-file' is not set for type KEYSTORE!") + + if (!Files.isRegularFile(keystoreFile)) + throw ConfigurationException("Property 'agent.ssl.keystore-file': $keystoreFile is not a regular file!") + + if (keystorePass == null) + throw ConfigurationException("Required Property 'agent.ssl.keystore-pass' is not set for type KEYSTORE!") + + when (FilenameUtils.getExtension(keystoreFile.toString()).toLowerCase()) { + "jks" -> { + val keystore = KeyStore.getInstance(KeyStore.getDefaultType()) + keystore.load(Files.newInputStream(keystoreFile), keystorePass.toCharArray()) + clientConfigBuilder.withCustomSslConfig(KeystoreSSLConfig(keystore, keystorePass)) + } + "pkcs12" -> { + clientConfigBuilder.withCustomSslConfig(KeystoreSSLConfig(keystoreFile.toFile(), keystorePass)) + } + else -> throw ConfigurationException("Unknown Keystore FileExtension: ${FilenameUtils.getExtension(keystoreFile.toString())}, Supported: jks, pkcs12") + } + + } else if (agentSSLConfig.type == AgentSSLConfig.AgentSSLType.PEM_FILES) { + val directory = agentSSLConfig.pemDirectory + if (directory == null || !Files.exists(directory)) + throw ConfigurationException("Required Property 'agent.ssl.pem-directory' is not set for type PEM_FILES!") + + if (!Files.isDirectory(directory)) + throw ConfigurationException("Property 'agent.ssl.pem-directory': $directory is not a directory!") + + clientConfigBuilder.withCustomSslConfig(LocalDirectorySSLConfig(directory.normalize().toAbsolutePath().toString())) + } + + return clientConfigBuilder.build() + } + private fun requireConnection() { if (status != ConnectionStatus.CONNECTED) throw AgentNotConnectedException() diff --git a/src/main/resources/templates/application.yml b/src/main/resources/templates/application.yml index 2af7a37..639dd1c 100644 --- a/src/main/resources/templates/application.yml +++ b/src/main/resources/templates/application.yml @@ -118,6 +118,19 @@ agent: # If disabled you will manually call the /docker/connect endpoint to initiate the connection # auto-connect: true + ssl: + + # type of SSL configuration. Can be 'none', 'keystore' or 'pem_files' + # Setting this to none disables SSL configuration + type: none + + keystore-file: # Path to either a .jks or .pkcs12 file. + keystore-pass: # Password for the keystore or pkcs12 file. + + # Path to a directory containing a ca.pem, key.pem & cert.pem + # These files must be named and have the file extension as described above! + pem-directory: + orchestrator: # Base Url of the Orchestrator, The orchestrator needs to live somewhere from where it can reach the agent From 4f06d683cd45522e8fa575380cc5abc61fad7a7e Mon Sep 17 00:00:00 2001 From: nanabell Date: Sat, 26 Sep 2020 18:03:45 +0200 Subject: [PATCH 02/15] Outsource DockerClient Creation to DockerContextBuilder --- .../agent/docker/service/DockerContext.kt | 60 +--------------- .../docker/service/DockerContextBuilder.kt | 68 +++++++++++++++++++ src/main/resources/logback.xml | 3 +- 3 files changed, 72 insertions(+), 59 deletions(-) create mode 100644 src/main/kotlin/com/ilunos/agent/docker/service/DockerContextBuilder.kt diff --git a/src/main/kotlin/com/ilunos/agent/docker/service/DockerContext.kt b/src/main/kotlin/com/ilunos/agent/docker/service/DockerContext.kt index 26c81d9..1d5d7c5 100644 --- a/src/main/kotlin/com/ilunos/agent/docker/service/DockerContext.kt +++ b/src/main/kotlin/com/ilunos/agent/docker/service/DockerContext.kt @@ -8,22 +8,16 @@ import com.github.dockerjava.api.model.Container import com.github.dockerjava.api.model.Image import com.github.dockerjava.api.model.Info import com.github.dockerjava.api.model.Version -import com.github.dockerjava.core.* -import com.github.dockerjava.httpclient5.ApacheDockerHttpClient import com.ilunos.agent.docker.config.AgentConfig import com.ilunos.agent.docker.config.AgentSSLConfig import com.ilunos.agent.docker.exception.AgentNotConnectedException import com.ilunos.agent.docker.model.ConnectionStatus import io.micronaut.context.annotation.Context import io.micronaut.context.annotation.Infrastructure -import io.micronaut.context.exceptions.ConfigurationException import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.launch -import org.apache.commons.io.FilenameUtils import org.slf4j.Logger import org.slf4j.LoggerFactory -import java.nio.file.Files -import java.security.KeyStore import java.util.* @Context @@ -159,21 +153,13 @@ class DockerContext(private val agentConfig: AgentConfig, private val agentSSLCo fun connect() { disconnect() - - val configBuilder = DefaultDockerClientConfig.createDefaultConfigBuilder().withDockerHost(agentConfig.url) - val clientConfig = loadSSLConfig(configBuilder) - - val httpClient = ApacheDockerHttpClient.Builder() - .dockerHost(clientConfig.dockerHost) - .sslConfig(clientConfig.sslConfig) - .build() - this.status = ConnectionStatus.BUILDING try { - this.client = DockerClientBuilder.getInstance(clientConfig).withDockerHttpClient(httpClient).build() - client.pingCmd().exec() + this.client = DockerContextBuilder(agentConfig, agentSSLConfig).build() + logger.debug("Attempting to connect to Docker System at '${agentConfig.url}'") + client.pingCmd().exec() this.status = ConnectionStatus.CONNECTED logger.info("Connected to Docker System at ${agentConfig.url}") @@ -192,46 +178,6 @@ class DockerContext(private val agentConfig: AgentConfig, private val agentSSLCo this.status = ConnectionStatus.DISCONNECTED } - private fun loadSSLConfig(clientConfigBuilder: DefaultDockerClientConfig.Builder): DockerClientConfig { - if (agentSSLConfig.type == AgentSSLConfig.AgentSSLType.KEYSTORE) { - val keystoreFile = agentSSLConfig.keystoreFile - val keystorePass = agentSSLConfig.keystorePass - - if (keystoreFile == null || !Files.exists(keystoreFile)) - throw ConfigurationException("Required Property 'agent.ssl.keystore-file' is not set for type KEYSTORE!") - - if (!Files.isRegularFile(keystoreFile)) - throw ConfigurationException("Property 'agent.ssl.keystore-file': $keystoreFile is not a regular file!") - - if (keystorePass == null) - throw ConfigurationException("Required Property 'agent.ssl.keystore-pass' is not set for type KEYSTORE!") - - when (FilenameUtils.getExtension(keystoreFile.toString()).toLowerCase()) { - "jks" -> { - val keystore = KeyStore.getInstance(KeyStore.getDefaultType()) - keystore.load(Files.newInputStream(keystoreFile), keystorePass.toCharArray()) - clientConfigBuilder.withCustomSslConfig(KeystoreSSLConfig(keystore, keystorePass)) - } - "pkcs12" -> { - clientConfigBuilder.withCustomSslConfig(KeystoreSSLConfig(keystoreFile.toFile(), keystorePass)) - } - else -> throw ConfigurationException("Unknown Keystore FileExtension: ${FilenameUtils.getExtension(keystoreFile.toString())}, Supported: jks, pkcs12") - } - - } else if (agentSSLConfig.type == AgentSSLConfig.AgentSSLType.PEM_FILES) { - val directory = agentSSLConfig.pemDirectory - if (directory == null || !Files.exists(directory)) - throw ConfigurationException("Required Property 'agent.ssl.pem-directory' is not set for type PEM_FILES!") - - if (!Files.isDirectory(directory)) - throw ConfigurationException("Property 'agent.ssl.pem-directory': $directory is not a directory!") - - clientConfigBuilder.withCustomSslConfig(LocalDirectorySSLConfig(directory.normalize().toAbsolutePath().toString())) - } - - return clientConfigBuilder.build() - } - private fun requireConnection() { if (status != ConnectionStatus.CONNECTED) throw AgentNotConnectedException() diff --git a/src/main/kotlin/com/ilunos/agent/docker/service/DockerContextBuilder.kt b/src/main/kotlin/com/ilunos/agent/docker/service/DockerContextBuilder.kt new file mode 100644 index 0000000..e1059cb --- /dev/null +++ b/src/main/kotlin/com/ilunos/agent/docker/service/DockerContextBuilder.kt @@ -0,0 +1,68 @@ +package com.ilunos.agent.docker.service + +import com.github.dockerjava.api.DockerClient +import com.github.dockerjava.core.DefaultDockerClientConfig +import com.github.dockerjava.core.DockerClientBuilder +import com.github.dockerjava.core.DockerClientConfig +import com.github.dockerjava.core.LocalDirectorySSLConfig +import com.github.dockerjava.httpclient5.ApacheDockerHttpClient +import com.ilunos.agent.docker.config.AgentConfig +import com.ilunos.agent.docker.config.AgentSSLConfig +import org.slf4j.LoggerFactory +import java.nio.file.Files +import javax.naming.ConfigurationException + +class DockerContextBuilder(private val agentConfig: AgentConfig, private val agentSSLConfig: AgentSSLConfig) { + + private val logger = LoggerFactory.getLogger(DockerContextBuilder::class.java) + + fun build(): DockerClient { + logger.debug("Start building new DockerClient...") + + val configBuilder = DefaultDockerClientConfig.createDefaultConfigBuilder().withDockerHost(agentConfig.url) + val clientConfig = loadSSlConfig(configBuilder) + + val httpClient = ApacheDockerHttpClient.Builder() + .dockerHost(clientConfig.dockerHost) + .sslConfig(clientConfig.sslConfig) + .build() + + return DockerClientBuilder.getInstance(clientConfig).withDockerHttpClient(httpClient).build() + } + + + private fun loadSSlConfig(builder: DefaultDockerClientConfig.Builder): DockerClientConfig { + return when (agentSSLConfig.type) { + AgentSSLConfig.AgentSSLType.NONE -> loadNoSSL(builder) + AgentSSLConfig.AgentSSLType.KEYSTORE -> loadKeystoreSSl(builder) + AgentSSLConfig.AgentSSLType.PEM_FILES -> loadPemSSL(builder) + } + } + + private fun loadNoSSL(builder: DefaultDockerClientConfig.Builder): DockerClientConfig { + logger.debug("Building DockerClient without SSL Config") + + return builder.build() + } + + private fun loadKeystoreSSl(builder: DefaultDockerClientConfig.Builder): DockerClientConfig { + logger.debug("Building DockerClient with Keystore SSL Config") + + TODO("Keystore SSL Config is not yet implemented") + } + + private fun loadPemSSL(builder: DefaultDockerClientConfig.Builder): DockerClientConfig { + logger.debug("Building DockerClient with PemDirectory SSL Config") + + val directory = agentSSLConfig.pemDirectory + ?: throw ConfigurationException("Property 'agent.ssl.pem-directory' is required when using SSL Type 'PEM_FILES'!") + + if (!Files.isDirectory(directory)) + throw ConfigurationException("Property 'agent.ssl.pem-directory' does not exist or is not a directory!") + + logger.debug("Using PemDirectory at '$directory'") + return builder.withCustomSslConfig(LocalDirectorySSLConfig(directory.toAbsolutePath().toString())).build() + } + + +} \ No newline at end of file diff --git a/src/main/resources/logback.xml b/src/main/resources/logback.xml index 73e2514..1066ab4 100644 --- a/src/main/resources/logback.xml +++ b/src/main/resources/logback.xml @@ -5,8 +5,7 @@ - %cyan(%d{HH:mm:ss.SSS}) %gray([%thread]) %highlight(%-5level) %magenta(%logger{36}) - %msg%n - + %cyan(%d{HH:mm:ss.SSS}) %gray([%thread]) %highlight(%-5level) %magenta(%logger{36}) - %msg%n From 3453ab67f00e576cc56ca1f525377495a6e34617 Mon Sep 17 00:00:00 2001 From: nanabell Date: Sat, 26 Sep 2020 18:12:31 +0200 Subject: [PATCH 03/15] Enable tlsverify when using pem ssl config --- .../com/ilunos/agent/docker/service/DockerContextBuilder.kt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/main/kotlin/com/ilunos/agent/docker/service/DockerContextBuilder.kt b/src/main/kotlin/com/ilunos/agent/docker/service/DockerContextBuilder.kt index e1059cb..e5064ba 100644 --- a/src/main/kotlin/com/ilunos/agent/docker/service/DockerContextBuilder.kt +++ b/src/main/kotlin/com/ilunos/agent/docker/service/DockerContextBuilder.kt @@ -61,7 +61,10 @@ class DockerContextBuilder(private val agentConfig: AgentConfig, private val age throw ConfigurationException("Property 'agent.ssl.pem-directory' does not exist or is not a directory!") logger.debug("Using PemDirectory at '$directory'") - return builder.withCustomSslConfig(LocalDirectorySSLConfig(directory.toAbsolutePath().toString())).build() + return builder + .withCustomSslConfig(LocalDirectorySSLConfig(directory.toAbsolutePath().toString())) + .withDockerTlsVerify(true) + .build() } From 5c8d9541d9be0769acda58aa1529df418a34f934 Mon Sep 17 00:00:00 2001 From: nanabell Date: Sat, 26 Sep 2020 18:29:11 +0200 Subject: [PATCH 04/15] Change how DockerClient is instantiated hopefully loads ssl config correctly now --- .../ilunos/agent/docker/service/DockerContextBuilder.kt | 7 ++----- src/main/resources/logback.xml | 1 + 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/src/main/kotlin/com/ilunos/agent/docker/service/DockerContextBuilder.kt b/src/main/kotlin/com/ilunos/agent/docker/service/DockerContextBuilder.kt index e5064ba..cb085b7 100644 --- a/src/main/kotlin/com/ilunos/agent/docker/service/DockerContextBuilder.kt +++ b/src/main/kotlin/com/ilunos/agent/docker/service/DockerContextBuilder.kt @@ -1,10 +1,7 @@ package com.ilunos.agent.docker.service import com.github.dockerjava.api.DockerClient -import com.github.dockerjava.core.DefaultDockerClientConfig -import com.github.dockerjava.core.DockerClientBuilder -import com.github.dockerjava.core.DockerClientConfig -import com.github.dockerjava.core.LocalDirectorySSLConfig +import com.github.dockerjava.core.* import com.github.dockerjava.httpclient5.ApacheDockerHttpClient import com.ilunos.agent.docker.config.AgentConfig import com.ilunos.agent.docker.config.AgentSSLConfig @@ -27,7 +24,7 @@ class DockerContextBuilder(private val agentConfig: AgentConfig, private val age .sslConfig(clientConfig.sslConfig) .build() - return DockerClientBuilder.getInstance(clientConfig).withDockerHttpClient(httpClient).build() + return DockerClientImpl.getInstance(clientConfig, httpClient) } diff --git a/src/main/resources/logback.xml b/src/main/resources/logback.xml index 1066ab4..85b8943 100644 --- a/src/main/resources/logback.xml +++ b/src/main/resources/logback.xml @@ -13,5 +13,6 @@ + From 45992ab4ed5b5187da14eba0b1b015594b673b6a Mon Sep 17 00:00:00 2001 From: nanabell Date: Sat, 26 Sep 2020 18:29:37 +0200 Subject: [PATCH 05/15] Add a enabled property to OrchestratorConfig --- .../com/ilunos/agent/docker/config/OrchestratorConfig.kt | 2 ++ .../com/ilunos/agent/docker/service/OrchestratorRegister.kt | 3 ++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/main/kotlin/com/ilunos/agent/docker/config/OrchestratorConfig.kt b/src/main/kotlin/com/ilunos/agent/docker/config/OrchestratorConfig.kt index 611bac1..2233a44 100644 --- a/src/main/kotlin/com/ilunos/agent/docker/config/OrchestratorConfig.kt +++ b/src/main/kotlin/com/ilunos/agent/docker/config/OrchestratorConfig.kt @@ -11,6 +11,8 @@ class OrchestratorConfig { lateinit var url: URI + var enabled: Boolean = false + @Nullable var token: String? = null } diff --git a/src/main/kotlin/com/ilunos/agent/docker/service/OrchestratorRegister.kt b/src/main/kotlin/com/ilunos/agent/docker/service/OrchestratorRegister.kt index 5a0d917..57674b3 100644 --- a/src/main/kotlin/com/ilunos/agent/docker/service/OrchestratorRegister.kt +++ b/src/main/kotlin/com/ilunos/agent/docker/service/OrchestratorRegister.kt @@ -23,7 +23,8 @@ class OrchestratorRegister( private val logger = LoggerFactory.getLogger(OrchestratorRegister::class.java) init { - initialize() + if (config.enabled) + initialize() } private fun initialize() { From b939379c7d727eed405cac5f4355005d7cd82cec Mon Sep 17 00:00:00 2001 From: nanabell Date: Sat, 26 Sep 2020 18:30:22 +0200 Subject: [PATCH 06/15] Revert: Accidentally committed debug logback loggers --- src/main/resources/logback.xml | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/resources/logback.xml b/src/main/resources/logback.xml index 85b8943..1066ab4 100644 --- a/src/main/resources/logback.xml +++ b/src/main/resources/logback.xml @@ -13,6 +13,5 @@ - From eb1cb3cbe2412d39e2a91021667ae3b5e7150e4e Mon Sep 17 00:00:00 2001 From: nanabell Date: Sat, 26 Sep 2020 18:44:26 +0200 Subject: [PATCH 07/15] Add certs Directory to .gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index a7371df..2fb8a86 100644 --- a/.gitignore +++ b/.gitignore @@ -17,5 +17,6 @@ build/ target/ out/ +certs/ config/** !config/*.kt From 6f535226629981a7137656fa85fcaef42c9c6f38 Mon Sep 17 00:00:00 2001 From: nanabell Date: Sat, 26 Sep 2020 18:45:37 +0200 Subject: [PATCH 08/15] Cleanup logback.xml --- src/main/resources/logback.xml | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/main/resources/logback.xml b/src/main/resources/logback.xml index 1066ab4..2c9cb11 100644 --- a/src/main/resources/logback.xml +++ b/src/main/resources/logback.xml @@ -12,6 +12,4 @@ - - From 6c87c2ea871ebbac94d4fb1f0d9ca641cce63d6a Mon Sep 17 00:00:00 2001 From: nanabell Date: Sat, 26 Sep 2020 22:48:51 +0200 Subject: [PATCH 09/15] Update template application.yml to include orchestrator.enabled: false --- src/main/resources/templates/application.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/resources/templates/application.yml b/src/main/resources/templates/application.yml index 639dd1c..880e992 100644 --- a/src/main/resources/templates/application.yml +++ b/src/main/resources/templates/application.yml @@ -132,10 +132,11 @@ agent: pem-directory: orchestrator: + enabled: false # Base Url of the Orchestrator, The orchestrator needs to live somewhere from where it can reach the agent - # url: http://localhost:8080 + url: http://localhost:8080 # Set to the value of micronaut.security.register-token from your orchestrator configuration # !If token is not being used remove or leave commented out! - # token: my-super-secure-registration-password + token: my-super-secure-registration-password From 000f87ad8f7defac3c965893405e188f5280e79b Mon Sep 17 00:00:00 2001 From: nanabell Date: Sun, 27 Sep 2020 14:28:25 +0200 Subject: [PATCH 10/15] Mark gradlew as executable --- gradlew | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 gradlew diff --git a/gradlew b/gradlew old mode 100644 new mode 100755 From d48024e731e662de729cbf3c12aea4c34a34ddce Mon Sep 17 00:00:00 2001 From: nanabell Date: Mon, 28 Sep 2020 07:42:54 +0200 Subject: [PATCH 11/15] Fix wrong Indentation in orchestrator section - application.yml --- src/main/resources/templates/application.yml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/main/resources/templates/application.yml b/src/main/resources/templates/application.yml index 880e992..483d6a0 100644 --- a/src/main/resources/templates/application.yml +++ b/src/main/resources/templates/application.yml @@ -112,11 +112,11 @@ agent: # # TCP does currently not support TLS authenticated against the Docker Engine # and would require to open an unsecured tcp port on your machine. - # url: unix:///var/run/docker.sock + url: unix:///var/run/docker.sock # Attempt to connect to the specified docker url above on startup. # If disabled you will manually call the /docker/connect endpoint to initiate the connection - # auto-connect: true + auto-connect: true ssl: @@ -134,9 +134,9 @@ agent: orchestrator: enabled: false - # Base Url of the Orchestrator, The orchestrator needs to live somewhere from where it can reach the agent - url: http://localhost:8080 + # Base Url of the Orchestrator, The orchestrator needs to live somewhere from where it can reach the agent + url: http://localhost:8080 - # Set to the value of micronaut.security.register-token from your orchestrator configuration - # !If token is not being used remove or leave commented out! - token: my-super-secure-registration-password + # Set to the value of micronaut.security.register-token from your orchestrator configuration + # !If token is not being used remove or leave commented out! + token: my-super-secure-registration-password From 7cbcea56de85e36483e60be40739656116aefe80 Mon Sep 17 00:00:00 2001 From: nanabell Date: Mon, 28 Sep 2020 23:17:04 +0200 Subject: [PATCH 12/15] Migrate gralde dsl to kotlin gradle dsl --- build.gradle => build.gradle.kts | 118 ++++++++++++------------- settings.gradle => settings.gradle.kts | 0 2 files changed, 57 insertions(+), 61 deletions(-) rename build.gradle => build.gradle.kts (51%) rename settings.gradle => settings.gradle.kts (100%) diff --git a/build.gradle b/build.gradle.kts similarity index 51% rename from build.gradle rename to build.gradle.kts index 99e15f4..bc21b0b 100644 --- a/build.gradle +++ b/build.gradle.kts @@ -1,89 +1,74 @@ +object Dependency { + const val KOTLIN = "1.4.10" + const val MICRONAUT = "2.0.3" +} + plugins { - id "org.jetbrains.kotlin.jvm" version "${kotlinVersion}" - id "org.jetbrains.kotlin.kapt" version "${kotlinVersion}" - id "org.jetbrains.kotlin.plugin.allopen" version "${kotlinVersion}" - id "com.github.johnrengelman.shadow" version "6.0.0" - id "com.gorylenko.gradle-git-properties" version "2.2.3" - id "application" + kotlin("jvm") version "1.4.10" + kotlin("kapt") version "1.4.10" + kotlin("plugin.allopen") version "1.4.10" + id("com.github.johnrengelman.shadow") version "5.2.0" + application } -version "0.1" -group "com.ilunos.agent.docker" +version = "0.1" +group = "com.ilunos.agent.docker" repositories { + maven("https://dl.bintray.com/nanabell/ilunos") mavenCentral() jcenter() } -configurations { - // for dependencies that are needed for development only - developmentOnly -} +val developmentOnly = configurations.create("developmentOnly") + dependencies { - kapt(platform("io.micronaut:micronaut-bom:$micronautVersion")) + kapt(platform("io.micronaut:micronaut-bom:${Dependency.MICRONAUT}")) kapt("io.micronaut:micronaut-inject-java") kapt("io.micronaut:micronaut-validation") kapt("io.micronaut.security:micronaut-security-annotations") - implementation(platform("io.micronaut:micronaut-bom:$micronautVersion")) + + implementation(kotlin("stdlib-jdk8")) + implementation(kotlin("reflect")) + implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.8") + implementation("com.ilunos.common:common:1.0.1") + + implementation(platform("io.micronaut:micronaut-bom:${Dependency.MICRONAUT}")) implementation("io.micronaut:micronaut-inject") implementation("io.micronaut:micronaut-validation") implementation("io.micronaut:micronaut-management") - implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8") - implementation("org.jetbrains.kotlin:kotlin-reflect") - implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.8") implementation("io.micronaut.kotlin:micronaut-kotlin-runtime") implementation("io.micronaut:micronaut-runtime") - implementation("javax.annotation:javax.annotation-api") + implementation("io.micronaut:micronaut-http-server-netty") implementation("io.micronaut:micronaut-http-client") implementation("io.micronaut.security:micronaut-security") implementation("io.micronaut.security:micronaut-security-oauth2") implementation("io.micronaut.security:micronaut-security-jwt") implementation("io.micronaut.kotlin:micronaut-kotlin-extension-functions") + implementation("javax.annotation:javax.annotation-api") + implementation("com.github.docker-java:docker-java:3.2.5") implementation("com.github.docker-java:docker-java-transport-httpclient5:3.2.5") + runtimeOnly("ch.qos.logback:logback-classic") runtimeOnly("com.fasterxml.jackson.module:jackson-module-kotlin") - kaptTest(enforcedPlatform("io.micronaut:micronaut-bom:$micronautVersion")) + + kaptTest(enforcedPlatform("io.micronaut:micronaut-bom:${Dependency.MICRONAUT}")) kaptTest("io.micronaut:micronaut-inject-java") - testImplementation(enforcedPlatform("io.micronaut:micronaut-bom:$micronautVersion")) + + testImplementation(enforcedPlatform("io.micronaut:micronaut-bom:${Dependency.MICRONAUT}")) testImplementation("io.micronaut.test:micronaut-test-kotlintest") testImplementation("io.mockk:mockk:1.9.3") testImplementation("io.kotlintest:kotlintest-runner-junit5:3.3.2") } -test.classpath += configurations.developmentOnly - -mainClassName = "com.ilunos.agent.docker.IlunosKt" - -// use JUnit 5 platform -test { - useJUnitPlatform() -} - -java { - sourceCompatibility = JavaVersion.toVersion('13') -} - allOpen { - annotation("io.micronaut.aop.Around") -} -compileKotlin { - kotlinOptions { - jvmTarget = '13' - //Will retain parameter names for Java reflection - javaParameters = true - } -} -compileTestKotlin { - kotlinOptions { - jvmTarget = '13' - javaParameters = true - } + annotations("io.micronaut.aop.Around", "io.micronaut.scheduling.annotation.Scheduled") } + kapt { - correctErrorTypes = true arguments { arg("micronaut.processing.incremental", true) arg("micronaut.processing.annotations", "com.ilunos.agent.docker.*") @@ -92,21 +77,32 @@ kapt { } } -shadowJar { - mergeServiceFiles() -} +tasks { + compileKotlin { + kotlinOptions { + jvmTarget = "13" + javaParameters = true + } + } + compileTestKotlin { + kotlinOptions { + jvmTarget = "13" + javaParameters = true + } + } -tasks.withType(JavaExec) { - classpath += configurations.developmentOnly - jvmArgs('-XX:TieredStopAtLevel=1', '-Dcom.sun.management.jmxremote') - if (gradle.startParameter.continuous) { - systemProperties( - 'micronaut.io.watch.restart': 'true', - 'micronaut.io.watch.enabled': 'true', - "micronaut.io.watch.paths": "src/main" - ) + shadowJar { + mergeServiceFiles() } -} + // use JUnit 5 platform + test { + useJUnitPlatform() + } +} +tasks.withType { + classpath += developmentOnly + options.compilerArgs.addAll(arrayOf("-XX:TieredStopAtLevel=1", "-Dcom.sun.management.jmxremote")) +} \ No newline at end of file diff --git a/settings.gradle b/settings.gradle.kts similarity index 100% rename from settings.gradle rename to settings.gradle.kts From faba93ce7e25427a5d5b7f321448ad73e1058cde Mon Sep 17 00:00:00 2001 From: nanabell Date: Mon, 28 Sep 2020 23:17:39 +0200 Subject: [PATCH 13/15] use common package for determinating configurrations --- .../kotlin/com/ilunos/agent/docker/Ilunos.kt | 2 +- .../ilunos/agent/docker/util/ConfigUtils.kt | 34 ------------------- 2 files changed, 1 insertion(+), 35 deletions(-) delete mode 100644 src/main/kotlin/com/ilunos/agent/docker/util/ConfigUtils.kt diff --git a/src/main/kotlin/com/ilunos/agent/docker/Ilunos.kt b/src/main/kotlin/com/ilunos/agent/docker/Ilunos.kt index 22b9e49..2b97e9a 100644 --- a/src/main/kotlin/com/ilunos/agent/docker/Ilunos.kt +++ b/src/main/kotlin/com/ilunos/agent/docker/Ilunos.kt @@ -1,6 +1,6 @@ package com.ilunos.agent.docker -import com.ilunos.agent.docker.util.ConfigUtils +import com.ilunos.common.config.ConfigUtils import io.micronaut.context.ApplicationContext import io.micronaut.context.annotation.Context import io.micronaut.context.env.Environment diff --git a/src/main/kotlin/com/ilunos/agent/docker/util/ConfigUtils.kt b/src/main/kotlin/com/ilunos/agent/docker/util/ConfigUtils.kt deleted file mode 100644 index 53a23b5..0000000 --- a/src/main/kotlin/com/ilunos/agent/docker/util/ConfigUtils.kt +++ /dev/null @@ -1,34 +0,0 @@ -package com.ilunos.agent.docker.util - -import java.nio.file.Files -import java.nio.file.Path - -object ConfigUtils { - - private val config = System.getProperty("ilunos.configurationPath", "config") - - fun initialize() { - val configPath = Path.of(config) - - // Ensure Config Path exists, create if necessary - if (!Files.exists(configPath)) - Files.createDirectories(configPath) - - // Ensure Config/application.yml exists, copy template file of necessary - val appConfig = configPath.resolve("application.yml") - if (!Files.exists(appConfig)) - copyTemplateConfig(appConfig) - - // If config includes a custom logback.xml use that instead of the bundled - val loggerConfig = configPath.resolve("logback.xml") - if (Files.exists(loggerConfig)) - System.setProperty("logback.configurationFile", loggerConfig.toString()) - } - - private fun copyTemplateConfig(targetPath: Path) { - val stream = {}.javaClass.classLoader.getResourceAsStream("templates/application.yml") - ?: throw IllegalStateException("Unable to find 'templates/application.yml'!") - - Files.copy(stream, targetPath) - } -} \ No newline at end of file From 11384a96ea570f87511aa1bb2ee72dcb0c97ffc9 Mon Sep 17 00:00:00 2001 From: nanabell Date: Mon, 28 Sep 2020 23:17:56 +0200 Subject: [PATCH 14/15] Remove invalid @BooleanFlag --- src/main/kotlin/com/ilunos/agent/docker/config/AgentConfig.kt | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/main/kotlin/com/ilunos/agent/docker/config/AgentConfig.kt b/src/main/kotlin/com/ilunos/agent/docker/config/AgentConfig.kt index bc431b1..66d8316 100644 --- a/src/main/kotlin/com/ilunos/agent/docker/config/AgentConfig.kt +++ b/src/main/kotlin/com/ilunos/agent/docker/config/AgentConfig.kt @@ -5,14 +5,12 @@ import com.ilunos.agent.docker.Ilunos import edu.umd.cs.findbugs.annotations.NonNull import io.micronaut.context.annotation.ConfigurationProperties import io.micronaut.core.annotation.Introspected -import jdk.jfr.BooleanFlag import java.io.Serializable @Introspected @ConfigurationProperties("agent") class AgentConfig : Serializable { - @BooleanFlag @JsonProperty("auto-connect") var autoConnect: Boolean = true From 151b66cc3e11d159f6815d1710e744c33df5f52b Mon Sep 17 00:00:00 2001 From: nanabell Date: Mon, 28 Sep 2020 23:19:41 +0200 Subject: [PATCH 15/15] Implement development version of client side Heartbeating -> orchestrator --- .../domain/OrchestratorConnectRequest.kt | 9 +++ .../domain/OrchestratorConnectResponse.kt | 7 +++ .../docker/domain/OrchestratorRequest.kt | 7 --- .../docker/domain/OrchestratorResponse.kt | 6 -- .../agent/docker/service/HeartbeatService.kt | 59 +++++++++++++++++++ .../docker/service/OrchestratorRegister.kt | 53 ----------------- 6 files changed, 75 insertions(+), 66 deletions(-) create mode 100644 src/main/kotlin/com/ilunos/agent/docker/domain/OrchestratorConnectRequest.kt create mode 100644 src/main/kotlin/com/ilunos/agent/docker/domain/OrchestratorConnectResponse.kt delete mode 100644 src/main/kotlin/com/ilunos/agent/docker/domain/OrchestratorRequest.kt delete mode 100644 src/main/kotlin/com/ilunos/agent/docker/domain/OrchestratorResponse.kt create mode 100644 src/main/kotlin/com/ilunos/agent/docker/service/HeartbeatService.kt delete mode 100644 src/main/kotlin/com/ilunos/agent/docker/service/OrchestratorRegister.kt diff --git a/src/main/kotlin/com/ilunos/agent/docker/domain/OrchestratorConnectRequest.kt b/src/main/kotlin/com/ilunos/agent/docker/domain/OrchestratorConnectRequest.kt new file mode 100644 index 0000000..850328a --- /dev/null +++ b/src/main/kotlin/com/ilunos/agent/docker/domain/OrchestratorConnectRequest.kt @@ -0,0 +1,9 @@ +package com.ilunos.agent.docker.domain + +data class OrchestratorConnectRequest( + val name: String, + val token: String?, + val hostname: String, + val port: Int +) { +} \ No newline at end of file diff --git a/src/main/kotlin/com/ilunos/agent/docker/domain/OrchestratorConnectResponse.kt b/src/main/kotlin/com/ilunos/agent/docker/domain/OrchestratorConnectResponse.kt new file mode 100644 index 0000000..1581f3d --- /dev/null +++ b/src/main/kotlin/com/ilunos/agent/docker/domain/OrchestratorConnectResponse.kt @@ -0,0 +1,7 @@ +package com.ilunos.agent.docker.domain + +data class OrchestratorConnectResponse( + val token: String?, + val connected: Boolean, + val errorMessage: String? +) \ No newline at end of file diff --git a/src/main/kotlin/com/ilunos/agent/docker/domain/OrchestratorRequest.kt b/src/main/kotlin/com/ilunos/agent/docker/domain/OrchestratorRequest.kt deleted file mode 100644 index 4c131e8..0000000 --- a/src/main/kotlin/com/ilunos/agent/docker/domain/OrchestratorRequest.kt +++ /dev/null @@ -1,7 +0,0 @@ -package com.ilunos.agent.docker.domain - -data class OrchestratorRequest( - val name: String, - val url: String -) { -} \ No newline at end of file diff --git a/src/main/kotlin/com/ilunos/agent/docker/domain/OrchestratorResponse.kt b/src/main/kotlin/com/ilunos/agent/docker/domain/OrchestratorResponse.kt deleted file mode 100644 index 0a30faa..0000000 --- a/src/main/kotlin/com/ilunos/agent/docker/domain/OrchestratorResponse.kt +++ /dev/null @@ -1,6 +0,0 @@ -package com.ilunos.agent.docker.domain - -data class OrchestratorResponse( - val status: String -) { -} \ No newline at end of file diff --git a/src/main/kotlin/com/ilunos/agent/docker/service/HeartbeatService.kt b/src/main/kotlin/com/ilunos/agent/docker/service/HeartbeatService.kt new file mode 100644 index 0000000..f7f36b4 --- /dev/null +++ b/src/main/kotlin/com/ilunos/agent/docker/service/HeartbeatService.kt @@ -0,0 +1,59 @@ +package com.ilunos.agent.docker.service + +import com.ilunos.agent.docker.config.OrchestratorConfig +import com.ilunos.agent.docker.domain.OrchestratorConnectRequest +import com.ilunos.agent.docker.domain.OrchestratorConnectResponse +import io.micronaut.context.annotation.Requires +import io.micronaut.core.util.StringUtils +import io.micronaut.http.HttpRequest +import io.micronaut.http.HttpStatus +import io.micronaut.http.client.HttpClient +import io.micronaut.runtime.server.EmbeddedServer +import io.micronaut.scheduling.annotation.Async +import io.micronaut.scheduling.annotation.Scheduled +import io.reactivex.Flowable +import org.slf4j.LoggerFactory +import java.net.InetAddress +import javax.inject.Singleton + +@Singleton +@Requires(property = "agent.orchestrator.enabled", value = StringUtils.TRUE, defaultValue = StringUtils.FALSE) +open class HeartbeatService(private val config: OrchestratorConfig, private val server: EmbeddedServer) { + + private val logger = LoggerFactory.getLogger(HeartbeatService::class.java) + private val client = HttpClient.create(config.url.toURL()) + + private var token: String? = null + private var name: String = InetAddress.getLocalHost().hostName + + @Async + @Scheduled(initialDelay = "2s") + open fun connect() { + val request = HttpRequest.PUT("/agents", OrchestratorConnectRequest(name, token, InetAddress.getLocalHost().hostName, server.port)) + request.basicAuth("agent", config.token ?: "none") + + Flowable.fromPublisher(client.exchange(request, OrchestratorConnectResponse::class.java)).subscribe({ + if (it.status != HttpStatus.OK) { + logger.warn("Unexpected Server Response: ${it.status}") + return@subscribe + } + + val response = it.body() ?: throw IllegalStateException("OrchestratorConnectResponse Body is empty!?") + if (response.connected) { + this.token = response.token + + logger.info("Initial Heartbeat check successful. Orchestrator: ${config.url}") + return@subscribe + } + + logger.error("Failed Initial Heartbeat check: ${response.errorMessage}") + + }, { + if (logger.isDebugEnabled) + logger.error("Failed to connect to Orchestrator! Is it running?", it) + else + logger.error("Failed to connect to Orchestrator! Is it running?") + }) + } + +} \ No newline at end of file diff --git a/src/main/kotlin/com/ilunos/agent/docker/service/OrchestratorRegister.kt b/src/main/kotlin/com/ilunos/agent/docker/service/OrchestratorRegister.kt deleted file mode 100644 index 19c4a7b..0000000 --- a/src/main/kotlin/com/ilunos/agent/docker/service/OrchestratorRegister.kt +++ /dev/null @@ -1,53 +0,0 @@ -package com.ilunos.agent.docker.service - -import com.ilunos.agent.docker.config.OrchestratorConfig -import com.ilunos.agent.docker.domain.OrchestratorRequest -import com.ilunos.agent.docker.domain.OrchestratorResponse -import io.micronaut.context.annotation.Context -import io.micronaut.context.annotation.Requires -import io.micronaut.http.HttpRequest -import io.micronaut.http.HttpStatus -import io.micronaut.http.client.HttpClient -import io.micronaut.http.server.util.HttpHostResolver -import io.reactivex.Flowable -import org.slf4j.LoggerFactory -import java.net.InetAddress - -@Context -@Requires(property = "agent.orchestrator.url") -class OrchestratorRegister( - private val config: OrchestratorConfig, - private val resolver: HttpHostResolver -) { - - private val logger = LoggerFactory.getLogger(OrchestratorRegister::class.java) - - init { - if (config.enabled) - initialize() - } - - private fun initialize() { - val client = HttpClient.create(config.url.toURL()) - - val hostname = InetAddress.getLocalHost().hostName - val selfUrl = resolver.resolve(HttpRequest.GET("/")) - - val request = HttpRequest.PUT("/agents", OrchestratorRequest(hostname, selfUrl)) - request.basicAuth("agent", config.token ?: "none") - - Flowable.fromPublisher(client.exchange(request, OrchestratorResponse::class.java)).subscribe({ - if (it.status == HttpStatus.OK) { - when (it.body()?.status) { - "CREATED" -> logger.info("Registered Agent at Orchestrator at ${config.url}") - "ALREADY_EXISTING" -> logger.info("Connected to Orchestrator at ${config.url}") - else -> logger.warn("Unexpected response from Orchestrator. Status: ${it.body()?.status}") - } - } else { - logger.error("Failed to register at Orchestrator ${config.url}, Status: ${it.status}") - } - }, { - logger.error("Failed to register at Orchestrator! Is it Running and reachable?", it) - }) - } -}