diff --git a/agent/pom.xml b/agent/pom.xml index a1cff2027d..0d3c386718 100644 --- a/agent/pom.xml +++ b/agent/pom.xml @@ -129,6 +129,10 @@ com.fasterxml.jackson.datatype jackson-datatype-jdk8 + + com.fasterxml.jackson.datatype + jackson-datatype-jsr310 + com.google.code.findbugs jsr305 diff --git a/agent/src/main/java/com/walmartlabs/concord/agent/Agent.java b/agent/src/main/java/com/walmartlabs/concord/agent/Agent.java index cbf69159d1..c2c1e5c04f 100644 --- a/agent/src/main/java/com/walmartlabs/concord/agent/Agent.java +++ b/agent/src/main/java/com/walmartlabs/concord/agent/Agent.java @@ -37,18 +37,13 @@ import org.slf4j.LoggerFactory; import javax.inject.Inject; -import javax.inject.Named; -import javax.inject.Singleton; import java.io.IOException; -import java.nio.file.Files; import java.nio.file.Path; import java.util.Map; import java.util.UUID; import java.util.concurrent.*; import java.util.concurrent.atomic.AtomicBoolean; -@Named -@Singleton public class Agent { private static final Logger log = LoggerFactory.getLogger(Agent.class); diff --git a/agent/src/main/java/com/walmartlabs/concord/agent/AgentModule.java b/agent/src/main/java/com/walmartlabs/concord/agent/AgentModule.java new file mode 100644 index 0000000000..376083f01f --- /dev/null +++ b/agent/src/main/java/com/walmartlabs/concord/agent/AgentModule.java @@ -0,0 +1,81 @@ +package com.walmartlabs.concord.agent; + +/*- + * ***** + * Concord + * ----- + * Copyright (C) 2017 - 2023 Walmart Inc. + * ----- + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ===== + */ + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.google.inject.Binder; +import com.google.inject.Module; +import com.typesafe.config.Config; +import com.walmartlabs.concord.agent.cfg.*; +import com.walmartlabs.concord.agent.executors.runner.DefaultDependencies; +import com.walmartlabs.concord.agent.executors.runner.ProcessPool; +import com.walmartlabs.concord.agent.remote.ApiClientFactory; +import com.walmartlabs.concord.agent.remote.QueueClientProvider; +import com.walmartlabs.concord.server.queueclient.QueueClient; +import com.walmartlabs.ollie.config.ConfigurationProcessor; +import com.walmartlabs.ollie.config.Environment; +import com.walmartlabs.ollie.config.EnvironmentSelector; + +import javax.inject.Named; + +import static com.google.inject.Scopes.SINGLETON; + +@Named +public class AgentModule implements Module { + + private final Config config; + + public AgentModule() { + this(loadDefaultConfig()); + } + + public AgentModule(Config config) { + this.config = config; + } + + @Override + public void configure(Binder binder) { + binder.bind(ObjectMapper.class).toProvider(ObjectMapperProvider.class); + binder.bind(Config.class).toInstance(config); + + binder.bind(AgentConfiguration.class).in(SINGLETON); + binder.bind(DockerConfiguration.class).in(SINGLETON); + binder.bind(GitConfiguration.class).in(SINGLETON); + binder.bind(ImportConfiguration.class).in(SINGLETON); + binder.bind(PreForkConfiguration.class).in(SINGLETON); + binder.bind(RepositoryCacheConfiguration.class).in(SINGLETON); + binder.bind(RunnerV1Configuration.class).in(SINGLETON); + binder.bind(RunnerV2Configuration.class).in(SINGLETON); + binder.bind(ServerConfiguration.class).in(SINGLETON); + + binder.bind(DefaultDependencies.class).in(SINGLETON); + binder.bind(ProcessPool.class).in(SINGLETON); + binder.bind(ApiClientFactory.class).in(SINGLETON); + binder.bind(QueueClient.class).toProvider(QueueClientProvider.class).in(SINGLETON); + + binder.bind(Agent.class).in(SINGLETON); + } + + private static Config loadDefaultConfig() { + Environment env = new EnvironmentSelector().select(); + return new ConfigurationProcessor("concord-agent", env, null, null).process(); + } +} diff --git a/agent/src/main/java/com/walmartlabs/concord/agent/ObjectMapperProvider.java b/agent/src/main/java/com/walmartlabs/concord/agent/ObjectMapperProvider.java index 857544fc15..e54760d6c1 100644 --- a/agent/src/main/java/com/walmartlabs/concord/agent/ObjectMapperProvider.java +++ b/agent/src/main/java/com/walmartlabs/concord/agent/ObjectMapperProvider.java @@ -20,18 +20,34 @@ * ===== */ +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializationFeature; +import com.fasterxml.jackson.datatype.guava.GuavaModule; +import com.fasterxml.jackson.datatype.jdk8.Jdk8Module; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; -import javax.inject.Named; import javax.inject.Provider; -import javax.inject.Singleton; -@Named -@Singleton public class ObjectMapperProvider implements Provider { @Override public ObjectMapper get() { - return new ObjectMapper(); + ObjectMapper mapper = new ObjectMapper() + .registerModule(new Jdk8Module()) + .registerModule(new GuavaModule()) + .registerModule(new JavaTimeModule()); + + // Write dates as ISO-8601 + mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); + + // Ignore unknown properties + mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); + + // Ignore nulls + mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); + + return mapper; } } diff --git a/agent/src/main/java/com/walmartlabs/concord/agent/cfg/AgentConfiguration.java b/agent/src/main/java/com/walmartlabs/concord/agent/cfg/AgentConfiguration.java index eadd5478fc..4c5c99a486 100644 --- a/agent/src/main/java/com/walmartlabs/concord/agent/cfg/AgentConfiguration.java +++ b/agent/src/main/java/com/walmartlabs/concord/agent/cfg/AgentConfiguration.java @@ -25,8 +25,6 @@ import org.slf4j.LoggerFactory; import javax.inject.Inject; -import javax.inject.Named; -import javax.inject.Singleton; import java.nio.file.Path; import java.time.Duration; import java.util.List; @@ -37,8 +35,6 @@ import static com.walmartlabs.concord.agent.cfg.Utils.getOrCreatePath; import static com.walmartlabs.concord.agent.cfg.Utils.getStringOrDefault; -@Named -@Singleton public class AgentConfiguration { private static final Logger log = LoggerFactory.getLogger(AgentConfiguration.class); diff --git a/agent/src/main/java/com/walmartlabs/concord/agent/cfg/ConfigProvider.java b/agent/src/main/java/com/walmartlabs/concord/agent/cfg/ConfigProvider.java deleted file mode 100644 index efa3b9947e..0000000000 --- a/agent/src/main/java/com/walmartlabs/concord/agent/cfg/ConfigProvider.java +++ /dev/null @@ -1,41 +0,0 @@ -package com.walmartlabs.concord.agent.cfg; - -/*- - * ***** - * Concord - * ----- - * Copyright (C) 2017 - 2019 Walmart Inc. - * ----- - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * ===== - */ - -import com.typesafe.config.Config; -import com.walmartlabs.ollie.config.ConfigurationProcessor; -import com.walmartlabs.ollie.config.Environment; -import com.walmartlabs.ollie.config.EnvironmentSelector; - -import javax.inject.Named; -import javax.inject.Provider; -import javax.inject.Singleton; - -@Named -@Singleton -public class ConfigProvider implements Provider { - - @Override - public Config get() { - Environment env = new EnvironmentSelector().select(); - return new ConfigurationProcessor("concord-agent", env).process(); - } -} diff --git a/agent/src/main/java/com/walmartlabs/concord/agent/cfg/DockerConfiguration.java b/agent/src/main/java/com/walmartlabs/concord/agent/cfg/DockerConfiguration.java index 90fde7f7f9..da7bc9cca4 100644 --- a/agent/src/main/java/com/walmartlabs/concord/agent/cfg/DockerConfiguration.java +++ b/agent/src/main/java/com/walmartlabs/concord/agent/cfg/DockerConfiguration.java @@ -23,13 +23,9 @@ import com.typesafe.config.Config; import javax.inject.Inject; -import javax.inject.Named; -import javax.inject.Singleton; import java.util.List; import java.util.concurrent.TimeUnit; -@Named -@Singleton public class DockerConfiguration { private final String dockerHost; diff --git a/agent/src/main/java/com/walmartlabs/concord/agent/cfg/GitConfiguration.java b/agent/src/main/java/com/walmartlabs/concord/agent/cfg/GitConfiguration.java index 6dcce8e6e4..85bdcab673 100644 --- a/agent/src/main/java/com/walmartlabs/concord/agent/cfg/GitConfiguration.java +++ b/agent/src/main/java/com/walmartlabs/concord/agent/cfg/GitConfiguration.java @@ -29,8 +29,6 @@ import static com.walmartlabs.concord.agent.cfg.Utils.getStringOrDefault; -@Named -@Singleton public class GitConfiguration { private final String token; diff --git a/agent/src/main/java/com/walmartlabs/concord/agent/cfg/ImportConfiguration.java b/agent/src/main/java/com/walmartlabs/concord/agent/cfg/ImportConfiguration.java index 7cde1f8ca7..f1f5756ba0 100644 --- a/agent/src/main/java/com/walmartlabs/concord/agent/cfg/ImportConfiguration.java +++ b/agent/src/main/java/com/walmartlabs/concord/agent/cfg/ImportConfiguration.java @@ -23,14 +23,10 @@ import com.typesafe.config.Config; import javax.inject.Inject; -import javax.inject.Named; -import javax.inject.Singleton; import java.util.Collections; import java.util.HashSet; import java.util.Set; -@Named -@Singleton public class ImportConfiguration { private final Set disabledProcessors; diff --git a/agent/src/main/java/com/walmartlabs/concord/agent/cfg/PreForkConfiguration.java b/agent/src/main/java/com/walmartlabs/concord/agent/cfg/PreForkConfiguration.java index 68758b065c..de38930cee 100644 --- a/agent/src/main/java/com/walmartlabs/concord/agent/cfg/PreForkConfiguration.java +++ b/agent/src/main/java/com/walmartlabs/concord/agent/cfg/PreForkConfiguration.java @@ -23,12 +23,8 @@ import com.typesafe.config.Config; import javax.inject.Inject; -import javax.inject.Named; -import javax.inject.Singleton; import java.util.concurrent.TimeUnit; -@Named -@Singleton public class PreForkConfiguration { private final boolean enabled; diff --git a/agent/src/main/java/com/walmartlabs/concord/agent/cfg/RepositoryCacheConfiguration.java b/agent/src/main/java/com/walmartlabs/concord/agent/cfg/RepositoryCacheConfiguration.java index 81ef969da0..c1454e31a1 100644 --- a/agent/src/main/java/com/walmartlabs/concord/agent/cfg/RepositoryCacheConfiguration.java +++ b/agent/src/main/java/com/walmartlabs/concord/agent/cfg/RepositoryCacheConfiguration.java @@ -23,15 +23,11 @@ import com.typesafe.config.Config; import javax.inject.Inject; -import javax.inject.Named; -import javax.inject.Singleton; import java.nio.file.Path; import java.time.Duration; import static com.walmartlabs.concord.agent.cfg.Utils.getOrCreatePath; -@Named -@Singleton public class RepositoryCacheConfiguration { private final Path cacheDir; diff --git a/agent/src/main/java/com/walmartlabs/concord/agent/cfg/RunnerV1Configuration.java b/agent/src/main/java/com/walmartlabs/concord/agent/cfg/RunnerV1Configuration.java index 90246a638f..dc57172c37 100644 --- a/agent/src/main/java/com/walmartlabs/concord/agent/cfg/RunnerV1Configuration.java +++ b/agent/src/main/java/com/walmartlabs/concord/agent/cfg/RunnerV1Configuration.java @@ -23,11 +23,7 @@ import com.typesafe.config.Config; import javax.inject.Inject; -import javax.inject.Named; -import javax.inject.Singleton; -@Named -@Singleton public class RunnerV1Configuration extends AbstractRunnerConfiguration { @Inject diff --git a/agent/src/main/java/com/walmartlabs/concord/agent/cfg/RunnerV2Configuration.java b/agent/src/main/java/com/walmartlabs/concord/agent/cfg/RunnerV2Configuration.java index 1f12aee1a6..8128b7dd67 100644 --- a/agent/src/main/java/com/walmartlabs/concord/agent/cfg/RunnerV2Configuration.java +++ b/agent/src/main/java/com/walmartlabs/concord/agent/cfg/RunnerV2Configuration.java @@ -23,11 +23,7 @@ import com.typesafe.config.Config; import javax.inject.Inject; -import javax.inject.Named; -import javax.inject.Singleton; -@Named -@Singleton public class RunnerV2Configuration extends AbstractRunnerConfiguration { @Inject diff --git a/agent/src/main/java/com/walmartlabs/concord/agent/cfg/ServerConfiguration.java b/agent/src/main/java/com/walmartlabs/concord/agent/cfg/ServerConfiguration.java index eabe02609c..5e6bfb4523 100644 --- a/agent/src/main/java/com/walmartlabs/concord/agent/cfg/ServerConfiguration.java +++ b/agent/src/main/java/com/walmartlabs/concord/agent/cfg/ServerConfiguration.java @@ -25,14 +25,10 @@ import org.slf4j.LoggerFactory; import javax.inject.Inject; -import javax.inject.Named; -import javax.inject.Singleton; import java.util.concurrent.TimeUnit; import static com.walmartlabs.concord.agent.cfg.Utils.getStringOrDefault; -@Named -@Singleton public class ServerConfiguration { private static final Logger log = LoggerFactory.getLogger(ServerConfiguration.class); diff --git a/agent/src/main/java/com/walmartlabs/concord/agent/executors/runner/DefaultDependencies.java b/agent/src/main/java/com/walmartlabs/concord/agent/executors/runner/DefaultDependencies.java index 70ad0e3387..4c5d88f5e2 100644 --- a/agent/src/main/java/com/walmartlabs/concord/agent/executors/runner/DefaultDependencies.java +++ b/agent/src/main/java/com/walmartlabs/concord/agent/executors/runner/DefaultDependencies.java @@ -23,8 +23,6 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import javax.inject.Named; -import javax.inject.Singleton; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; @@ -37,8 +35,6 @@ import java.util.stream.Collectors; import java.util.stream.Stream; -@Named -@Singleton public class DefaultDependencies { private static final Logger log = LoggerFactory.getLogger(DefaultDependencies.class); diff --git a/agent/src/main/java/com/walmartlabs/concord/agent/executors/runner/ProcessPool.java b/agent/src/main/java/com/walmartlabs/concord/agent/executors/runner/ProcessPool.java index a389be7e8d..54dd575a65 100644 --- a/agent/src/main/java/com/walmartlabs/concord/agent/executors/runner/ProcessPool.java +++ b/agent/src/main/java/com/walmartlabs/concord/agent/executors/runner/ProcessPool.java @@ -29,16 +29,12 @@ import org.slf4j.LoggerFactory; import javax.inject.Inject; -import javax.inject.Named; -import javax.inject.Singleton; import java.io.IOException; import java.nio.file.Path; import java.util.*; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; -@Named -@Singleton public class ProcessPool { private static final Logger log = LoggerFactory.getLogger(ProcessPool.class); diff --git a/agent/src/main/java/com/walmartlabs/concord/agent/remote/ApiClientFactory.java b/agent/src/main/java/com/walmartlabs/concord/agent/remote/ApiClientFactory.java index 92e2fde99b..0b2403c212 100644 --- a/agent/src/main/java/com/walmartlabs/concord/agent/remote/ApiClientFactory.java +++ b/agent/src/main/java/com/walmartlabs/concord/agent/remote/ApiClientFactory.java @@ -30,8 +30,6 @@ import com.walmartlabs.concord.common.IOUtils; import javax.inject.Inject; -import javax.inject.Named; -import javax.inject.Singleton; import java.io.IOException; import java.nio.file.Path; import java.util.HashMap; @@ -39,8 +37,6 @@ import java.util.Map; import java.util.concurrent.TimeUnit; -@Named -@Singleton public class ApiClientFactory { private static final String SESSION_COOKIE_NAME = "JSESSIONID"; diff --git a/agent/src/main/java/com/walmartlabs/concord/agent/remote/QueueClientProvider.java b/agent/src/main/java/com/walmartlabs/concord/agent/remote/QueueClientProvider.java index 789a565b8a..63f6945bd2 100644 --- a/agent/src/main/java/com/walmartlabs/concord/agent/remote/QueueClientProvider.java +++ b/agent/src/main/java/com/walmartlabs/concord/agent/remote/QueueClientProvider.java @@ -26,13 +26,9 @@ import com.walmartlabs.concord.server.queueclient.QueueClientConfiguration; import javax.inject.Inject; -import javax.inject.Named; import javax.inject.Provider; -import javax.inject.Singleton; import java.net.URISyntaxException; -@Named -@Singleton public class QueueClientProvider implements Provider { private final AgentConfiguration agentCfg; diff --git a/it/testing-server/pom.xml b/it/testing-server/pom.xml index 23b03d94db..d4cba3fc79 100644 --- a/it/testing-server/pom.xml +++ b/it/testing-server/pom.xml @@ -35,6 +35,10 @@ + + com.walmartlabs.concord + concord-agent + org.testcontainers postgresql @@ -43,5 +47,16 @@ com.typesafe config + + + com.walmartlabs.concord + concord-client2 + test + + + org.junit.jupiter + junit-jupiter-api + test + diff --git a/it/testing-server/src/main/java/com/walmartlabs/concord/it/testingserver/TestingConcordAgent.java b/it/testing-server/src/main/java/com/walmartlabs/concord/it/testingserver/TestingConcordAgent.java new file mode 100644 index 0000000000..531378e33b --- /dev/null +++ b/it/testing-server/src/main/java/com/walmartlabs/concord/it/testingserver/TestingConcordAgent.java @@ -0,0 +1,97 @@ +package com.walmartlabs.concord.it.testingserver; + +/*- + * ***** + * Concord + * ----- + * Copyright (C) 2017 - 2023 Walmart Inc. + * ----- + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ===== + */ + +import com.google.inject.Guice; +import com.google.inject.Module; +import com.typesafe.config.Config; +import com.typesafe.config.ConfigFactory; +import com.typesafe.config.ConfigParseOptions; +import com.typesafe.config.ConfigResolveOptions; +import com.walmartlabs.concord.agent.Agent; +import com.walmartlabs.concord.agent.AgentModule; + +import java.util.List; +import java.util.Map; +import java.util.function.Function; +import java.util.stream.Stream; + +/** + * A helper class for running concord-agent. + * The agent runs in the same JVM as TestingConcordAgent. + */ +public class TestingConcordAgent implements AutoCloseable { + + private final Map extraConfiguration; + private final List> extraModules; + private final int apiPort; + private final String agentApiKey; + + private Agent agent; + + public TestingConcordAgent(TestingConcordServer testingConcordServer) { + this(testingConcordServer, Map.of(), List.of()); + } + + public TestingConcordAgent(TestingConcordServer testingConcordServer, Map extraConfiguration, List> extraModules) { + this.apiPort = testingConcordServer.getApiPort(); + this.agentApiKey = testingConcordServer.getAgentApiKey(); + this.extraConfiguration = extraConfiguration; + this.extraModules = extraModules; + } + + public synchronized void start() { + var config = prepareConfig(); + var system = new AgentModule(config); + var allModules = Stream.concat(extraModules.stream().map(f -> f.apply(config)), Stream.of(system)).toList(); + var injector = Guice.createInjector(allModules); + agent = injector.getInstance(Agent.class); + agent.start(); + } + + public synchronized void stop() { + if (agent != null) { + agent.stop(); + agent = null; + } + } + + @Override + public void close() { + this.stop(); + } + + private Config prepareConfig() { + var extraConfig = ConfigFactory.parseMap(this.extraConfiguration); + + var testConfig = ConfigFactory.parseMap(Map.of( + "maintenanceModeListenerPort", 0, + "server.apiBaseUrl", "http://localhost:" + apiPort, + "server.websocketUrl", "ws://localhost:" + apiPort + "/websocket", + "server.apiKey", agentApiKey + )); + + var defaultConfig = ConfigFactory.load("concord-agent.conf", ConfigParseOptions.defaults(), ConfigResolveOptions.defaults().setAllowUnresolved(true)) + .getConfig("concord-agent"); + + return extraConfig.withFallback(testConfig.withFallback(defaultConfig)).resolve(); + } +} diff --git a/it/testing-server/src/main/java/com/walmartlabs/concord/it/testingserver/TestingConcordServer.java b/it/testing-server/src/main/java/com/walmartlabs/concord/it/testingserver/TestingConcordServer.java index bcca549988..4f1260828e 100644 --- a/it/testing-server/src/main/java/com/walmartlabs/concord/it/testingserver/TestingConcordServer.java +++ b/it/testing-server/src/main/java/com/walmartlabs/concord/it/testingserver/TestingConcordServer.java @@ -20,6 +20,7 @@ * ===== */ +import com.google.common.collect.ImmutableMap; import com.google.inject.Module; import com.typesafe.config.Config; import com.typesafe.config.ConfigFactory; @@ -36,28 +37,39 @@ import java.util.function.Function; import java.util.stream.Stream; +import static com.google.common.base.Preconditions.checkArgument; import static java.util.Objects.requireNonNull; +/** + * A helper class for running concord-server. It runs PostgreSQL in Docker + * and the server runs in the same JVM as TestingConcordServer. + */ public class TestingConcordServer implements AutoCloseable { + private final PostgreSQLContainer db; private final Map extraConfiguration; private final List> extraModules; + private final int apiPort; + private final String adminApiKey; + private final String agentApiKey; - private PostgreSQLContainer db; private ConcordServer server; - public TestingConcordServer(Map extraConfiguration, List> extraModules) { - this.extraConfiguration = requireNonNull(extraConfiguration); - this.extraModules = requireNonNull(extraModules); + public TestingConcordServer(PostgreSQLContainer db) { + this(db, 8001, Map.of(), List.of()); } - public TestingConcordServer() { - this(Map.of(), List.of()); + public TestingConcordServer(PostgreSQLContainer db, int apiPort, Map extraConfiguration, List> extraModules) { + this.db = requireNonNull(db); + this.extraConfiguration = requireNonNull(extraConfiguration); + this.apiPort = apiPort; + this.extraModules = requireNonNull(extraModules); + this.adminApiKey = randomString(8); + this.agentApiKey = randomString(16); } public synchronized TestingConcordServer start() throws Exception { - db = new PostgreSQLContainer<>("postgres:15-alpine"); - db.start(); + checkArgument(db.isRunning(), "The database container is not running"); var config = prepareConfig(db); var system = new ConcordServerModule(config); @@ -68,21 +80,24 @@ public synchronized TestingConcordServer start() throws Exception { return this; } - @Override - public synchronized void close() throws Exception { - this.stop(); - } - - public void stop() throws Exception { + public synchronized void stop() throws Exception { if (server != null) { server.stop(); server = null; } + } - if (db != null) { - db.stop(); - db = null; - } + @Override + public void close() throws Exception { + this.stop(); + } + + public int getApiPort() { + return apiPort; + } + + public String getApiBaseUrl() { + return "http://localhost:" + apiPort; } public ConcordServer getServer() { @@ -93,20 +108,30 @@ public PostgreSQLContainer getDb() { return db; } + public String getAdminApiKey() { + return adminApiKey; + } + + public String getAgentApiKey() { + return agentApiKey; + } + private Config prepareConfig(PostgreSQLContainer db) { var extraConfig = ConfigFactory.parseMap(this.extraConfiguration); - var testConfig = ConfigFactory.parseMap(Map.of( - "db.url", db.getJdbcUrl(), - "db.appUsername", db.getUsername(), - "db.appPassword", db.getPassword(), - "db.inventoryUsername", db.getUsername(), - "db.inventoryPassword", db.getPassword(), - "db.changeLogParameters.defaultAdminToken", "foobar", - "secretStore.serverPassword", randomString(), - "secretStore.secretStoreSalt", randomString(), - "secretStore.projectSecretSalt", randomString() - )); + var testConfig = ConfigFactory.parseMap(ImmutableMap.builder() + .put("server.port", String.valueOf(apiPort)) + .put("db.url", db.getJdbcUrl()) + .put("db.appUsername", db.getUsername()) + .put("db.appPassword", db.getPassword()) + .put("db.inventoryUsername", db.getUsername()) + .put("db.inventoryPassword", db.getPassword()) + .put("db.changeLogParameters.defaultAdminToken", adminApiKey) + .put("db.changeLogParameters.defaultAgentToken", agentApiKey) + .put("secretStore.serverPassword", randomString(64)) + .put("secretStore.secretStoreSalt", randomString(64)) + .put("secretStore.projectSecretSalt", randomString(64)) + .build()); var defaultConfig = ConfigFactory.load("concord-server.conf", ConfigParseOptions.defaults(), ConfigResolveOptions.defaults().setAllowUnresolved(true)) .getConfig("concord-server"); @@ -114,8 +139,8 @@ private Config prepareConfig(PostgreSQLContainer db) { return extraConfig.withFallback(testConfig.withFallback(defaultConfig)).resolve(); } - private static String randomString() { - byte[] ab = new byte[64]; + private static String randomString(int minLength) { + byte[] ab = new byte[minLength]; new SecureRandom().nextBytes(ab); return Base64.getEncoder().encodeToString(ab); } @@ -124,20 +149,29 @@ private static String randomString() { * Just an example. */ public static void main(String[] args) throws Exception { - try (TestingConcordServer server = new TestingConcordServer(Map.of("process.watchdogPeriod", "10 seconds"), List.of())) { + try (var db = new PostgreSQLContainer<>("postgres:15-alpine"); + var server = new TestingConcordServer(db, 8001, Map.of("process.watchdogPeriod", "10 seconds"), List.of())) { + db.start(); server.start(); - - System.out.println(""" - ============================================================== + System.out.printf(""" + ============================================================== + + UI: http://localhost:8001/ + DB: + JDBC URL: %s + username: %s + password: %s - UI: http://localhost:8001/ - DB: - JDBC URL: %s - username: %s - password: %s - """.formatted(server.getDb().getJdbcUrl(), server.getDb().getUsername(), server.getDb().getPassword())); + admin API key: %s + agent API key: %s + %n""", db.getJdbcUrl(), + db.getUsername(), + db.getPassword(), + server.getAdminApiKey(), + server.getAgentApiKey()); Thread.currentThread().join(); } } } + diff --git a/it/testing-server/src/test/java/com/walmartlabs/concord/it/testingserver/TestingConcordIT.java b/it/testing-server/src/test/java/com/walmartlabs/concord/it/testingserver/TestingConcordIT.java new file mode 100644 index 0000000000..0e93548756 --- /dev/null +++ b/it/testing-server/src/test/java/com/walmartlabs/concord/it/testingserver/TestingConcordIT.java @@ -0,0 +1,109 @@ +package com.walmartlabs.concord.it.testingserver; + +/*- + * ***** + * Concord + * ----- + * Copyright (C) 2017 - 2023 Walmart Inc. + * ----- + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ===== + */ + +import com.walmartlabs.concord.client2.ApiClientConfiguration; +import com.walmartlabs.concord.client2.DefaultApiClientFactory; +import com.walmartlabs.concord.client2.ProcessApi; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.testcontainers.containers.PostgreSQLContainer; + +import java.io.IOException; +import java.net.ServerSocket; +import java.time.Duration; +import java.util.List; +import java.util.Map; + +import static com.walmartlabs.concord.client2.ProcessEntry.StatusEnum.FINISHED; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +/** + * A test that tests TestingConcordServer and TestingConcordAgent can run together in the same JVM. + */ +public class TestingConcordIT { + + private PostgreSQLContainer db; + private TestingConcordServer concordServer; + private TestingConcordAgent concordAgent; + + @Before + public void setUp() throws Exception { + db = new PostgreSQLContainer<>("postgres:15-alpine"); + db.start(); + + int apiPort = getFreePort(); + concordServer = new TestingConcordServer(db, apiPort, Map.of(), List.of()); + concordServer.start(); + + concordAgent = new TestingConcordAgent(concordServer); + concordAgent.start(); + } + + @After + public void tearDown() throws Exception { + if (concordAgent != null) { + concordAgent.close(); + concordAgent = null; + } + + if (concordServer != null) { + concordServer.close(); + concordServer = null; + } + + if (db != null) { + db.close(); + db = null; + } + } + + @Test(timeout = 120_000) + public void testRunningSimpleProcess() throws Exception { + var client = new DefaultApiClientFactory(concordServer.getApiBaseUrl()) + .create(ApiClientConfiguration.builder() + .apiKey(concordServer.getAdminApiKey()) + .build()); + + var processApi = new ProcessApi(client); + var response = processApi.startProcess(Map.of("concord.yml", """ + configuration: + runtime: "concord-v2" + flows: + default: + - log: "Hello!" + """.getBytes())); + assertNotNull(response.getInstanceId()); + + var process = processApi.waitForCompletion(response.getInstanceId(), Duration.ofSeconds(60).toMillis()); + assertEquals(FINISHED, process.getStatus()); + } + + private static int getFreePort() { + try (var socket = new ServerSocket(0)) { + return socket.getLocalPort(); + } catch (IOException e) { + throw new RuntimeException(e); + } + } +} diff --git a/server/impl/src/main/java/com/walmartlabs/concord/server/ApiServerModule.java b/server/impl/src/main/java/com/walmartlabs/concord/server/ApiServerModule.java index d0f13ab6ae..fb07707b21 100644 --- a/server/impl/src/main/java/com/walmartlabs/concord/server/ApiServerModule.java +++ b/server/impl/src/main/java/com/walmartlabs/concord/server/ApiServerModule.java @@ -20,8 +20,10 @@ * ===== */ +import com.fasterxml.jackson.databind.ObjectMapper; import com.google.inject.Binder; import com.google.inject.Module; +import com.google.inject.name.Names; import com.walmartlabs.concord.server.boot.*; import com.walmartlabs.concord.server.boot.filters.*; import com.walmartlabs.concord.server.boot.servlets.FormServletHolder; @@ -31,6 +33,8 @@ import org.apache.shiro.mgt.SecurityManager; import org.apache.shiro.web.mgt.WebSecurityManager; import org.eclipse.jetty.servlet.FilterHolder; +import org.sonatype.siesta.Component; +import org.sonatype.siesta.jackson2.ObjectMapperResolver; import javax.servlet.ServletContextListener; import javax.servlet.http.HttpServlet; @@ -44,7 +48,14 @@ public class ApiServerModule implements Module { @Override public void configure(Binder binder) { + // Jackson + + binder.bind(ObjectMapper.class).toProvider(ObjectMapperProvider.class).in(SINGLETON); + binder.bind(ObjectMapper.class).annotatedWith(Names.named("siesta")).toProvider(ObjectMapperProvider.class).in(SINGLETON); + newSetBinder(binder, Component.class).addBinding().to(ObjectMapperResolver.class); + // Jetty + binder.bind(HttpServer.class).in(SINGLETON); // Filter diff --git a/server/impl/src/main/java/com/walmartlabs/concord/server/ObjectMapperInitializer.java b/server/impl/src/main/java/com/walmartlabs/concord/server/ObjectMapperProvider.java similarity index 58% rename from server/impl/src/main/java/com/walmartlabs/concord/server/ObjectMapperInitializer.java rename to server/impl/src/main/java/com/walmartlabs/concord/server/ObjectMapperProvider.java index 4184391c0e..eaa5068ac2 100644 --- a/server/impl/src/main/java/com/walmartlabs/concord/server/ObjectMapperInitializer.java +++ b/server/impl/src/main/java/com/walmartlabs/concord/server/ObjectMapperProvider.java @@ -4,7 +4,7 @@ * ***** * Concord * ----- - * Copyright (C) 2017 - 2018 Walmart Inc. + * Copyright (C) 2017 - 2023 Walmart Inc. * ----- * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,24 +20,24 @@ * ===== */ +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.datatype.guava.GuavaModule; import com.fasterxml.jackson.datatype.jdk8.Jdk8Module; import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; -import org.eclipse.sisu.EagerSingleton; -import javax.inject.Inject; -import javax.inject.Named; +import javax.inject.Provider; -@Named -@EagerSingleton -public class ObjectMapperInitializer { +public class ObjectMapperProvider implements Provider { - @Inject - public ObjectMapperInitializer(@Named("siesta") javax.inject.Provider mapperProvider) { - ObjectMapper om = mapperProvider.get(); - om.registerModule(new GuavaModule()); - om.registerModule(new Jdk8Module()); - om.registerModule(new JavaTimeModule()); + @Override + public ObjectMapper get() { + return new ObjectMapper() + .registerModule(new GuavaModule()) + .registerModule(new Jdk8Module()) + .registerModule(new JavaTimeModule()) + .setSerializationInclusion(JsonInclude.Include.NON_NULL) + .disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); } } diff --git a/server/impl/src/test/java/com/walmartlabs/concord/server/TestObjectMapper.java b/server/impl/src/test/java/com/walmartlabs/concord/server/TestObjectMapper.java index c42e4a53b7..83306020f8 100644 --- a/server/impl/src/test/java/com/walmartlabs/concord/server/TestObjectMapper.java +++ b/server/impl/src/test/java/com/walmartlabs/concord/server/TestObjectMapper.java @@ -28,9 +28,7 @@ public final class TestObjectMapper { public static final ObjectMapper INSTANCE = createObjectMapper(); private static ObjectMapper createObjectMapper() { - ObjectMapper om = new ObjectMapperProvider().get(); - new ObjectMapperInitializer(() -> om); - return om; + return new ObjectMapperProvider().get(); } private TestObjectMapper() {