Skip to content

Commit

Permalink
testing-concord-server: add agent wrapper, simple test
Browse files Browse the repository at this point in the history
  • Loading branch information
ibodrov committed Nov 21, 2023
1 parent 3fc34ad commit bde42ee
Show file tree
Hide file tree
Showing 4 changed files with 248 additions and 34 deletions.
15 changes: 15 additions & 0 deletions it/testing-server/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.walmartlabs.concord</groupId>
<artifactId>concord-agent</artifactId>
</dependency>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>postgresql</artifactId>
Expand All @@ -43,5 +47,16 @@
<groupId>com.typesafe</groupId>
<artifactId>config</artifactId>
</dependency>

<dependency>
<groupId>com.walmartlabs.concord</groupId>
<artifactId>concord-client2</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
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<String, String> extraConfiguration;
private final List<Function<Config, Module>> extraModules;
private final String agentApiKey;

private Agent agent;

public TestingConcordAgent(String agentApiKey) {
this(agentApiKey, Map.of(), List.of());
}

public TestingConcordAgent(String agentApiKey, Map<String, String> extraConfiguration, List<Function<Config, Module>> extraModules) {
this.agentApiKey = agentApiKey;
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(
"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();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,28 +36,37 @@
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<String, String> extraConfiguration;
private final List<Function<Config, Module>> extraModules;
private final String adminApiKey;
private final String agentApiKey;

private PostgreSQLContainer<?> db;
private ConcordServer server;

public TestingConcordServer(Map<String, String> extraConfiguration, List<Function<Config, Module>> extraModules) {
this.extraConfiguration = requireNonNull(extraConfiguration);
this.extraModules = requireNonNull(extraModules);
public TestingConcordServer(PostgreSQLContainer<?> db) {
this(db, Map.of(), List.of());
}

public TestingConcordServer() {
this(Map.of(), List.of());
public TestingConcordServer(PostgreSQLContainer<?> db, Map<String, String> extraConfiguration, List<Function<Config, Module>> extraModules) {
this.db = requireNonNull(db);
this.extraConfiguration = requireNonNull(extraConfiguration);
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);
Expand All @@ -68,21 +77,16 @@ 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 ConcordServer getServer() {
Expand All @@ -93,6 +97,14 @@ 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);

Expand All @@ -102,10 +114,11 @@ private Config prepareConfig(PostgreSQLContainer<?> db) {
"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()
"db.changeLogParameters.defaultAdminToken", adminApiKey,
"db.changeLogParameters.defaultAgentToken", agentApiKey,
"secretStore.serverPassword", randomString(64),
"secretStore.secretStoreSalt", randomString(64),
"secretStore.projectSecretSalt", randomString(64)
));

var defaultConfig = ConfigFactory.load("concord-server.conf", ConfigParseOptions.defaults(), ConfigResolveOptions.defaults().setAllowUnresolved(true))
Expand All @@ -114,8 +127,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);
}
Expand All @@ -124,20 +137,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, 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
""".formatted(server.getDb().getJdbcUrl(), server.getDb().getUsername(), server.getDb().getPassword()));
UI: http://localhost:8001/
DB:
JDBC URL: %s
username: %s
password: %s
admin API key: %s
agent API key: %s
%n""", db.getJdbcUrl(),
db.getUsername(),
db.getPassword(),
server.getAdminApiKey(),
server.getAgentApiKey());

Thread.currentThread().join();
}
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
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 java.time.Duration;
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 TestingConcordServer concordServer;
private TestingConcordAgent concordAgent;

@Before
public void setUp() throws Exception {
concordServer = new TestingConcordServer();
concordServer.start();

concordAgent = new TestingConcordAgent(concordServer.getAgentApiKey());
concordAgent.start();
}

@After
public void tearDown() throws Exception {
if (concordAgent != null) {
concordAgent.close();
concordAgent = null;
}

if (concordServer != null) {
concordServer.close();
concordServer = null;
}
}

@Test(timeout = 120_000)
public void testRunningSimpleProcess() throws Exception {
var client = new DefaultApiClientFactory("http://localhost:8001")
.create(ApiClientConfiguration.builder()
.apiKey(concordServer.getAdminApiKey())
.build());

var processApi = new ProcessApi(client);
var response = processApi.startProcess(Map.of("concord.yml", """
flows:
default:
- log: "Hello!"
""".getBytes()));
assertNotNull(response.getInstanceId());

var process = processApi.waitForCompletion(response.getInstanceId(), Duration.ofSeconds(60).toMillis());
assertEquals(FINISHED, process.getStatus());
}
}

0 comments on commit bde42ee

Please sign in to comment.