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 6266009
Show file tree
Hide file tree
Showing 4 changed files with 296 additions and 41 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,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<String, String> extraConfiguration;
private final List<Function<Config, Module>> 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<String, String> extraConfiguration, List<Function<Config, Module>> 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();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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<String, String> extraConfiguration;
private final List<Function<Config, Module>> extraModules;
private final int apiPort;
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, 8001, Map.of(), List.of());
}

public TestingConcordServer() {
this(Map.of(), List.of());
public TestingConcordServer(PostgreSQLContainer<?> db, int apiPort, Map<String, String> extraConfiguration, List<Function<Config, Module>> 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);
Expand All @@ -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() {
Expand All @@ -93,29 +108,39 @@ 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.<String, String>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");

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 +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();
}
}
}

Loading

0 comments on commit 6266009

Please sign in to comment.