├── .github
└── md
│ └── README.md
├── .gitignore
├── LICENSE.txt
├── README.md
├── client
├── pom.xml
└── src
│ └── main
│ └── java
│ └── io
│ └── metaloom
│ └── test
│ └── container
│ └── provider
│ └── client
│ ├── ClientAllocation.java
│ ├── JSON.java
│ ├── ProviderClient.java
│ ├── TestDatabaseProvider.java
│ └── WebsocketLinkListener.java
├── common
├── pom.xml
└── src
│ ├── main
│ ├── java
│ │ └── io
│ │ │ └── metaloom
│ │ │ └── test
│ │ │ └── container
│ │ │ └── provider
│ │ │ ├── common
│ │ │ ├── ClientEnv.java
│ │ │ ├── ServerEnv.java
│ │ │ ├── config
│ │ │ │ ├── AbstractDatabaseConfig.java
│ │ │ │ ├── DatabaseConfig.java
│ │ │ │ ├── PostgresqlConfig.java
│ │ │ │ ├── ProviderConfig.java
│ │ │ │ └── ProviderConfigHelper.java
│ │ │ └── version
│ │ │ │ ├── BuildInfo.java
│ │ │ │ └── Version.java
│ │ │ └── model
│ │ │ ├── AbstractDatabasePoolModel.java
│ │ │ ├── DatabaseAllocationResponse.java
│ │ │ ├── DatabasePoolConnection.java
│ │ │ ├── DatabasePoolListResponse.java
│ │ │ ├── DatabasePoolRequest.java
│ │ │ ├── DatabasePoolResponse.java
│ │ │ ├── DatabasePoolSettings.java
│ │ │ └── RestModel.java
│ └── resources
│ │ └── provider.build.properties
│ └── test
│ └── java
│ └── io
│ └── metaloom
│ └── test
│ └── container
│ └── provider
│ └── common
│ ├── config
│ └── ProviderConfigHelperTest.java
│ └── version
│ └── VersionTest.java
├── examples
├── complex
│ ├── README.md
│ ├── moduleA
│ │ ├── pom.xml
│ │ └── src
│ │ │ └── main
│ │ │ └── flyway
│ │ │ └── V1__initial_setup.sql
│ ├── moduleB
│ │ ├── pom.xml
│ │ └── src
│ │ │ └── test
│ │ │ └── java
│ │ │ └── io
│ │ │ └── metaloom
│ │ │ └── example
│ │ │ ├── ExampleJunit4Test.java
│ │ │ └── ExampleJunit5Test.java
│ └── pom.xml
├── dedicated-no-maven-plugin
│ ├── docker-compose.yml
│ ├── pom.xml
│ └── src
│ │ └── test
│ │ └── java
│ │ └── io
│ │ └── metaloom
│ │ └── example
│ │ ├── ExampleJunit4Test.java
│ │ ├── ExampleJunit5Test.java
│ │ └── PoolSetupAction.java
├── dedicated
│ ├── README.md
│ ├── docker-compose.yml
│ ├── pom.xml
│ └── src
│ │ └── test
│ │ └── java
│ │ └── io
│ │ └── metaloom
│ │ └── example
│ │ ├── ExampleJunit4Test.java
│ │ └── ExampleJunit5Test.java
├── minimal
│ ├── README.md
│ ├── pom.xml
│ └── src
│ │ ├── main
│ │ └── resources
│ │ │ └── db
│ │ │ └── migration
│ │ │ └── V1__initial_setup.sql
│ │ └── test
│ │ ├── java
│ │ └── io
│ │ │ └── metaloom
│ │ │ └── example
│ │ │ ├── ExampleJunit4Test.java
│ │ │ ├── ExampleJunit5Test.java
│ │ │ └── PoolSetupAction.java
│ │ └── resources
│ │ └── logback.xml
└── pom.xml
├── junit4
├── pom.xml
└── src
│ ├── main
│ └── java
│ │ └── io
│ │ └── metaloom
│ │ └── test
│ │ └── provider
│ │ └── junit4
│ │ └── DatabaseProviderRule.java
│ └── test
│ └── java
│ └── io
│ └── metaloom
│ └── test
│ └── provider
│ └── junit4
│ ├── DatabaseProviderRuleTest.java
│ └── PoolSetupAction.java
├── junit5
├── pom.xml
└── src
│ ├── main
│ └── java
│ │ └── io
│ │ └── metaloom
│ │ └── test
│ │ └── provider
│ │ └── junit5
│ │ └── ProviderExtension.java
│ └── test
│ └── java
│ └── io
│ └── metaloom
│ └── test
│ └── provider
│ └── junit5
│ └── DatabaseProviderExtensionTest.java
├── maven
├── pom.xml
└── src
│ ├── main
│ └── java
│ │ └── io
│ │ └── metaloom
│ │ ├── maven
│ │ └── provider
│ │ │ ├── AbstractProviderMojo.java
│ │ │ ├── PoolLimits.java
│ │ │ ├── PoolMavenConfiguration.java
│ │ │ ├── PostgresqlMavenConfiguration.java
│ │ │ ├── ProviderCleanMojo.java
│ │ │ ├── ProviderMavenConfiguration.java
│ │ │ ├── ProviderPoolMojo.java
│ │ │ ├── ProviderStartMojo.java
│ │ │ └── ProviderStopMojo.java
│ │ └── test
│ │ └── container
│ │ └── provider
│ │ └── container
│ │ └── DatabaseProviderContainer.java
│ └── test
│ └── java
│ └── io
│ └── metaloom
│ └── maven
│ └── provider
│ └── ProviderStartMojoTest.java
├── pom.xml
├── postgresql-db
├── pom.xml
└── src
│ ├── main
│ └── java
│ │ └── io
│ │ └── metaloom
│ │ └── maven
│ │ └── provider
│ │ └── container
│ │ └── PostgreSQLPoolContainer.java
│ └── test
│ └── java
│ └── io
│ └── metaloom
│ └── maven
│ └── provider
│ └── container
│ └── PostgreSQLPoolContainerTest.java
└── server
├── Dockerfile
├── pom.xml
├── src
├── main
│ └── java
│ │ └── io
│ │ └── metaloom
│ │ └── test
│ │ └── container
│ │ └── provider
│ │ ├── BootstrapInitializer.java
│ │ ├── Database.java
│ │ ├── DatabaseAllocation.java
│ │ ├── DatabaseJsonCommentModel.java
│ │ ├── DatabasePool.java
│ │ ├── DatabasePoolFactory.java
│ │ ├── DatabasePoolManager.java
│ │ ├── DatabaseSettings.java
│ │ ├── SQLUtils.java
│ │ └── server
│ │ ├── DatabaseProviderServer.java
│ │ ├── DatabaseProviderServerRunner.java
│ │ ├── JSON.java
│ │ ├── ModelHelper.java
│ │ ├── ProviderRequest.java
│ │ ├── ServerApi.java
│ │ ├── ServerConfiguration.java
│ │ ├── ServerConfigurationLoader.java
│ │ ├── ServerError.java
│ │ └── dagger
│ │ ├── ProviderModule.java
│ │ ├── ServerComponent.java
│ │ └── VertxModule.java
└── test
│ └── java
│ └── io
│ └── metaloom
│ └── test
│ └── container
│ └── server
│ ├── AbstractProviderServerTest.java
│ ├── AllocationTestHelper.java
│ ├── DatabasePoolManagerTest.java
│ ├── DatabasePoolTest.java
│ ├── DatabaseProviderTest.java
│ ├── DatabaseProviderTestServer.java
│ ├── JSONTest.java
│ ├── ModelHelperTest.java
│ ├── ProviderClientServerTest.java
│ └── TestSQLHelper.java
├── stop.sh
└── test.sh
/.gitignore:
--------------------------------------------------------------------------------
1 | .project
2 | .classpath
3 | .factorypath
4 | .settings
5 | target
6 | /out
7 | dependency-reduced-pom.xml
8 |
9 |
--------------------------------------------------------------------------------
/client/pom.xml:
--------------------------------------------------------------------------------
1 |
4 | 4.0.0
5 | testdatabase-provider-client
6 |
7 |
8 | io.metaloom.test
9 | testdatabase-provider
10 | 0.1.4
11 |
12 |
13 | Testdatabase Provider :: Client
14 |
15 |
16 |
17 |
18 | io.metaloom.test
19 | testdatabase-provider-common
20 | ${project.version}
21 |
22 |
23 | org.slf4j
24 | slf4j-api
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/client/src/main/java/io/metaloom/test/container/provider/client/ClientAllocation.java:
--------------------------------------------------------------------------------
1 | package io.metaloom.test.container.provider.client;
2 |
3 | import java.net.http.WebSocket;
4 |
5 | import org.slf4j.Logger;
6 | import org.slf4j.LoggerFactory;
7 |
8 | import io.metaloom.test.container.provider.model.DatabaseAllocationResponse;
9 |
10 | /**
11 | * Allocation that was returned by the {@link ProviderClient}.
12 | */
13 | public class ClientAllocation {
14 |
15 | public static final Logger log = LoggerFactory.getLogger(ClientAllocation.class);
16 |
17 | private WebSocket socket;
18 | private DatabaseAllocationResponse response;
19 |
20 | public ClientAllocation(WebSocket socket, DatabaseAllocationResponse response) {
21 | this.socket = socket;
22 | this.response = response;
23 | }
24 |
25 | /**
26 | * Release the allocation. This will terminate the websocket and thus let the provider server know that the database is no longer in use can be be removed.
27 | */
28 | public void release() {
29 | if (log.isDebugEnabled()) {
30 | String id = response == null ? "unknown" : response.getId();
31 | log.debug("Releasing allocation {}", id);
32 | }
33 | socket.abort();
34 | }
35 |
36 | /**
37 | * Returns the allocation response which contains the database settings.
38 | *
39 | * @return
40 | */
41 | public DatabaseAllocationResponse response() {
42 | return response;
43 | }
44 |
45 | }
46 |
--------------------------------------------------------------------------------
/client/src/main/java/io/metaloom/test/container/provider/client/JSON.java:
--------------------------------------------------------------------------------
1 | package io.metaloom.test.container.provider.client;
2 |
3 | import com.fasterxml.jackson.databind.JsonNode;
4 | import com.fasterxml.jackson.databind.ObjectMapper;
5 |
6 | import io.metaloom.test.container.provider.model.RestModel;
7 |
8 | public final class JSON {
9 |
10 | private static ObjectMapper mapper = new ObjectMapper();
11 |
12 | private JSON() {
13 |
14 | }
15 |
16 | public static String toString(RestModel request) {
17 | try {
18 | return mapper.writeValueAsString(request);
19 | } catch (Exception e) {
20 | throw new RuntimeException(e);
21 | }
22 | }
23 |
24 | public static T fromString(String json, Class clazzOfT) {
25 | try {
26 | return mapper.readValue(json, clazzOfT);
27 | } catch (Exception e) {
28 | throw new RuntimeException(e);
29 | }
30 | }
31 |
32 | public static JsonNode toJsonNode(String json) {
33 | try {
34 | return mapper.readTree(json);
35 | } catch (Exception e) {
36 | throw new RuntimeException(e);
37 | }
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/client/src/main/java/io/metaloom/test/container/provider/client/ProviderClient.java:
--------------------------------------------------------------------------------
1 | package io.metaloom.test.container.provider.client;
2 |
3 | import java.io.IOException;
4 | import java.net.URI;
5 | import java.net.URISyntaxException;
6 | import java.net.http.HttpClient;
7 | import java.net.http.HttpRequest;
8 | import java.net.http.HttpResponse;
9 | import java.net.http.HttpResponse.BodyHandlers;
10 | import java.util.concurrent.CompletableFuture;
11 |
12 | import org.slf4j.Logger;
13 | import org.slf4j.LoggerFactory;
14 |
15 | import io.metaloom.test.container.provider.model.DatabasePoolListResponse;
16 | import io.metaloom.test.container.provider.model.DatabasePoolRequest;
17 | import io.metaloom.test.container.provider.model.DatabasePoolResponse;
18 |
19 | public class ProviderClient {
20 |
21 | public static final Logger log = LoggerFactory.getLogger(ProviderClient.class);
22 | private String host;
23 | private int port;
24 | private HttpClient client;
25 |
26 | public ProviderClient(String host, int port) {
27 | this.host = host;
28 | this.port = port;
29 | this.client = HttpClient.newBuilder().build();
30 | }
31 |
32 | /**
33 | * Connect the test to the database provider. The provider will assign a test database which can be used by the caller.
34 | *
35 | * @param poolName
36 | * @param testRef
37 | * @return
38 | */
39 | public CompletableFuture link(String poolName, String testRef) {
40 | WebsocketLinkListener listener = new WebsocketLinkListener(poolName, testRef);
41 | HttpClient
42 | .newHttpClient()
43 | .newWebSocketBuilder()
44 | .buildAsync(URI.create("ws://" + host + ":" + port + "/connect/websocket"), listener)
45 | .join();
46 | return listener.allocation();
47 | }
48 |
49 | /**
50 | * List all pools that have been created.
51 | *
52 | * @return
53 | * @throws URISyntaxException
54 | */
55 | public CompletableFuture listPools() throws URISyntaxException {
56 | HttpRequest request = HttpRequest.newBuilder()
57 | .uri(uri("/pools"))
58 | .version(HttpClient.Version.HTTP_2)
59 | .GET()
60 | .build();
61 |
62 | CompletableFuture> response = client.sendAsync(request, BodyHandlers.ofString());
63 |
64 | return response.thenApply(resp -> resp.body())
65 | .thenApply(body -> {
66 | return JSON.fromString(body, DatabasePoolListResponse.class);
67 | });
68 | }
69 |
70 | /**
71 | * Load the pool with the given id.
72 | *
73 | * @param id
74 | * @return
75 | * @throws IOException
76 | * @throws InterruptedException
77 | * @throws URISyntaxException
78 | */
79 | public CompletableFuture loadPool(String id) throws IOException, InterruptedException, URISyntaxException {
80 | HttpRequest request = HttpRequest.newBuilder()
81 | .uri(uri("/pools/" + id))
82 | .version(HttpClient.Version.HTTP_2)
83 | .GET()
84 | .build();
85 |
86 | // HttpResponse response = client.send(request, BodyHandlers.ofString());
87 |
88 | CompletableFuture> asyncResp = client.sendAsync(request, BodyHandlers.ofString());
89 | return asyncResp
90 | .thenApply(resp -> resp.body())
91 | .thenApply(body -> {
92 | return JSON.fromString(body, DatabasePoolResponse.class);
93 | });
94 | }
95 |
96 | public CompletableFuture deletePool(String id) throws URISyntaxException {
97 | HttpRequest request = HttpRequest.newBuilder()
98 | .uri(uri("/pools/" + id))
99 | .version(HttpClient.Version.HTTP_2)
100 | .DELETE()
101 | .build();
102 |
103 | CompletableFuture> response = client.sendAsync(request, BodyHandlers.discarding());
104 | return response.thenApply(body -> {
105 | return body.body();
106 | });
107 |
108 | }
109 |
110 | public CompletableFuture createPool(String id, DatabasePoolRequest request) throws URISyntaxException {
111 |
112 | String json = JSON.toString(request);
113 | HttpRequest httpRequest = HttpRequest.newBuilder()
114 | .uri(uri("/pools/" + id))
115 | .version(HttpClient.Version.HTTP_2)
116 | .POST(HttpRequest.BodyPublishers.ofString(json))
117 | .build();
118 |
119 | CompletableFuture> response = client.sendAsync(httpRequest, HttpResponse.BodyHandlers.ofString());
120 |
121 | return response.thenApply(resp -> resp.body())
122 | .thenApply(body -> {
123 | return JSON.fromString(body, DatabasePoolResponse.class);
124 | });
125 | }
126 |
127 | private URI uri(String path) throws URISyntaxException {
128 | return new URI("http", null, host, port, path, null, null);
129 | }
130 |
131 | }
132 |
--------------------------------------------------------------------------------
/client/src/main/java/io/metaloom/test/container/provider/client/TestDatabaseProvider.java:
--------------------------------------------------------------------------------
1 | package io.metaloom.test.container.provider.client;
2 |
3 | import java.io.IOException;
4 | import java.sql.Connection;
5 | import java.sql.DriverManager;
6 | import java.sql.SQLException;
7 | import java.sql.Statement;
8 |
9 | import org.slf4j.Logger;
10 | import org.slf4j.LoggerFactory;
11 |
12 | import io.metaloom.test.container.provider.common.ClientEnv;
13 | import io.metaloom.test.container.provider.common.config.PostgresqlConfig;
14 | import io.metaloom.test.container.provider.common.config.ProviderConfig;
15 | import io.metaloom.test.container.provider.common.config.ProviderConfigHelper;
16 | import io.metaloom.test.container.provider.model.DatabasePoolConnection;
17 | import io.metaloom.test.container.provider.model.DatabasePoolRequest;
18 | import io.metaloom.test.container.provider.model.DatabasePoolResponse;
19 |
20 | public class TestDatabaseProvider {
21 |
22 | private static final Logger log = LoggerFactory.getLogger(TestDatabaseProvider.class);
23 |
24 | private static ProviderConfig localConfig = null;
25 |
26 | /**
27 | * Return the REST client for the given host and port.
28 | *
29 | * @param host
30 | * @param port
31 | * @return
32 | */
33 | public static ProviderClient client(String host, int port) {
34 | return new ProviderClient(host, port);
35 | }
36 |
37 | /**
38 | * Return the REST client which can be used to communicate with the provider server that manages the database pooling.
39 | *
40 | * @return
41 | * @throws IOException
42 | */
43 | public static ProviderClient client() throws IOException {
44 | String host = ClientEnv.getProviderHost();
45 | Integer port = ClientEnv.getProviderPort();
46 | if (host == null || port == null) {
47 | log.debug("Client host,port environment variables for provider not found");
48 | } else {
49 | return new ProviderClient(host, port);
50 | }
51 | ProviderConfig config = config();
52 | requireConfig(config);
53 | host = config.getProviderHost();
54 | port = config.getProviderPort();
55 | return client(host, port);
56 | }
57 |
58 | /**
59 | * Locate the config which was written by the testdatabase-provider-plugin
60 | *
61 | * @return
62 | */
63 | public static ProviderConfig config() {
64 | if (localConfig != null) {
65 | return localConfig;
66 | } else {
67 | return ProviderConfigHelper.readConfig();
68 | }
69 | }
70 |
71 | /**
72 | * Create a new testdatabase pool
73 | *
74 | * @param poolName
75 | * @param templateDatabaseName
76 | * @return
77 | * @throws Exception
78 | */
79 | public static DatabasePoolResponse createPool(String poolName, String templateDatabaseName) throws Exception {
80 | ProviderConfig config = config();
81 | requireConfig(config);
82 | DatabasePoolRequest request = new DatabasePoolRequest();
83 | request.setTemplateDatabaseName(templateDatabaseName);
84 | DatabasePoolConnection connection = new DatabasePoolConnection(config.getPostgresql());
85 | request.setConnection(connection);
86 | ProviderClient client = client();
87 | DatabasePoolResponse response = client.createPool(poolName, request).get();
88 | return response;
89 | }
90 |
91 | /**
92 | * Create a new empty database which can be used to setup a new pool.
93 | *
94 | * @param name
95 | * @throws SQLException
96 | */
97 | public static void createPostgreSQLDatabase(String name) throws SQLException {
98 | ProviderConfig config = config();
99 | requireConfig(config);
100 | PostgresqlConfig postgresConfig = config.getPostgresql();
101 | String sql = "CREATE DATABASE " + name;
102 | try (Connection connection = DriverManager.getConnection(config.getPostgresql().adminJdbcUrl(), postgresConfig.getUsername(),
103 | postgresConfig.getPassword())) {
104 | Statement statement1 = connection.createStatement();
105 | statement1.execute(sql);
106 | }
107 | }
108 |
109 | /**
110 | * Drop and create create the database with the given name.
111 | *
112 | * @param name
113 | * @throws SQLException
114 | */
115 | public static void dropCreatePostgreSQLDatabase(String name) throws SQLException {
116 | ProviderConfig config = config();
117 | requireConfig(config);
118 | PostgresqlConfig postgresConfig = config.getPostgresql();
119 | String dropSQL = "DROP DATABASE IF EXISTS " + name;
120 | String createSQL = "CREATE DATABASE " + name;
121 | try (Connection connection = DriverManager.getConnection(config.getPostgresql().adminJdbcUrl(), postgresConfig.getUsername(),
122 | postgresConfig.getPassword())) {
123 | connection.createStatement().execute(dropSQL);
124 | connection.createStatement().execute(createSQL);
125 | }
126 |
127 | }
128 |
129 | private static void requireConfig(ProviderConfig config) {
130 | if (config == null) {
131 | throw new RuntimeException(
132 | "Unable to locate provider configuration file in filesystem. Started search here: " + ProviderConfigHelper.currentConfigPath());
133 | }
134 |
135 | }
136 |
137 | /**
138 | * Set a local provider config which will supersede any other config (e.g. ENV, config file).
139 | *
140 | * @param config
141 | */
142 | public static void localConfig(ProviderConfig config) {
143 | TestDatabaseProvider.localConfig = config;
144 | }
145 |
146 | }
147 |
--------------------------------------------------------------------------------
/client/src/main/java/io/metaloom/test/container/provider/client/WebsocketLinkListener.java:
--------------------------------------------------------------------------------
1 | package io.metaloom.test.container.provider.client;
2 |
3 | import java.net.http.WebSocket;
4 | import java.nio.ByteBuffer;
5 | import java.nio.charset.StandardCharsets;
6 | import java.util.concurrent.CompletableFuture;
7 | import java.util.concurrent.CompletionStage;
8 | import java.util.concurrent.Executors;
9 | import java.util.concurrent.ScheduledExecutorService;
10 | import java.util.concurrent.ScheduledFuture;
11 | import java.util.concurrent.TimeUnit;
12 |
13 | import org.slf4j.Logger;
14 | import org.slf4j.LoggerFactory;
15 |
16 | import com.fasterxml.jackson.databind.JsonNode;
17 |
18 | import io.metaloom.test.container.provider.model.DatabaseAllocationResponse;
19 |
20 | public class WebsocketLinkListener implements WebSocket.Listener {
21 |
22 | public static final Logger log = LoggerFactory.getLogger(WebsocketLinkListener.class);
23 |
24 | private CompletableFuture allocationFuture = new CompletableFuture<>();
25 |
26 | private ScheduledExecutorService executorService = Executors.newScheduledThreadPool(1);
27 |
28 | private ScheduledFuture> pingExec;
29 |
30 | private final String testRef;
31 |
32 | private final String poolName;
33 |
34 | public WebsocketLinkListener(String poolName, String testRef) {
35 | this.poolName = poolName;
36 | this.testRef = testRef;
37 | }
38 |
39 | @Override
40 | public void onOpen(WebSocket webSocket) {
41 | log.debug("Opened websocket - requesting allocation");
42 |
43 | // Sending name of the currently executed test to the server.
44 | // It will allocate a database and send us the result.
45 | try {
46 | String id = poolName + "/" + testRef;
47 | webSocket.sendText(id, true).get(2000, TimeUnit.MILLISECONDS);
48 | } catch (Exception e) {
49 | log.error("Error while sending allocation request", e);
50 | }
51 |
52 | // Start sending pings
53 | this.pingExec = executorService.scheduleAtFixedRate(() -> {
54 | String data = "Ping";
55 | ByteBuffer payload = ByteBuffer.wrap(data.getBytes());
56 | webSocket.sendPing(payload);
57 | }, 1000, 500, TimeUnit.MILLISECONDS);
58 |
59 | WebSocket.Listener.super.onOpen(webSocket);
60 | }
61 |
62 | @Override
63 | public CompletionStage> onClose(WebSocket webSocket, int statusCode, String reason) {
64 | if (pingExec != null && !pingExec.isCancelled()) {
65 | pingExec.cancel(true);
66 | }
67 | return WebSocket.Listener.super.onClose(webSocket, statusCode, reason);
68 | }
69 |
70 | @Override
71 | public CompletionStage> onText(WebSocket webSocket, CharSequence data, boolean last) {
72 | System.out.println("onText received " + data);
73 | return WebSocket.Listener.super.onText(webSocket, data, last);
74 | }
75 |
76 | @Override
77 | public CompletionStage> onPong(WebSocket webSocket, ByteBuffer message) {
78 | log.debug("Got pong");
79 | return WebSocket.Listener.super.onPong(webSocket, message);
80 | }
81 |
82 | @Override
83 | public CompletionStage> onPing(WebSocket webSocket, ByteBuffer message) {
84 | log.debug("Got ping");
85 | return WebSocket.Listener.super.onPing(webSocket, message);
86 | }
87 |
88 | @Override
89 | public CompletionStage> onBinary(WebSocket webSocket, ByteBuffer data, boolean last) {
90 | String json = StandardCharsets.UTF_8.decode(data).toString();
91 | JsonNode node = JSON.toJsonNode(json);
92 | if (node.has("error")) {
93 | String error = node.get("error").asText();
94 | allocationFuture.completeExceptionally(new Exception("Got error from server {" + error + "}"));
95 | } else {
96 | // String json = new String(data.array(), Charset.defaultCharset());
97 | log.info("Got provider allocation info:\n{}", json);
98 | DatabaseAllocationResponse response = JSON.fromString(json, DatabaseAllocationResponse.class);
99 | ClientAllocation allocation = new ClientAllocation(webSocket, response);
100 | allocationFuture.complete(allocation);
101 | }
102 | // result.complete(allocation);
103 | return WebSocket.Listener.super.onBinary(webSocket, data, last);
104 | }
105 |
106 | @Override
107 | public void onError(WebSocket webSocket, Throwable error) {
108 | log.error("Error occured while handling the connection to the provider", error);
109 | if (pingExec != null && !pingExec.isCancelled()) {
110 | pingExec.cancel(true);
111 | }
112 | }
113 |
114 | public CompletableFuture allocation() {
115 | return allocationFuture;
116 | }
117 | }
118 |
--------------------------------------------------------------------------------
/common/pom.xml:
--------------------------------------------------------------------------------
1 |
4 | 4.0.0
5 | testdatabase-provider-common
6 |
7 |
8 | io.metaloom.test
9 | testdatabase-provider
10 | 0.1.4
11 |
12 |
13 | Testdatabase Provider :: Common
14 |
15 |
16 |
17 |
18 | com.fasterxml.jackson.core
19 | jackson-databind
20 | 2.14.2
21 |
22 |
23 | com.fasterxml.jackson.datatype
24 | jackson-datatype-jsr310
25 | 2.14.2
26 |
27 |
28 | org.slf4j
29 | slf4j-api
30 |
31 |
32 |
33 |
34 | org.junit.jupiter
35 | junit-jupiter-api
36 | test
37 |
38 |
39 | ch.qos.logback
40 | logback-classic
41 | test
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 | src/main/resources
50 | true
51 |
52 | **/provider.build.properties
53 |
54 |
55 |
56 | src/main/resources
57 | false
58 |
59 | **/*
60 |
61 |
62 |
63 |
64 |
--------------------------------------------------------------------------------
/common/src/main/java/io/metaloom/test/container/provider/common/ClientEnv.java:
--------------------------------------------------------------------------------
1 | package io.metaloom.test.container.provider.common;
2 |
3 | public final class ClientEnv {
4 |
5 | public static final String TESTDATABASE_PROVIDER_HOST_KEY = "TESTDATABASE_PROVIDER_HOST";
6 |
7 | public static final String TESTDATABASE_PROVIDER_PORT_KEY = "TESTDATABASE_PROVIDER_PORT";
8 |
9 | public static Integer getProviderPort() {
10 | String portStr = System.getenv(ClientEnv.TESTDATABASE_PROVIDER_PORT_KEY);
11 | if (portStr == null) {
12 | return null;
13 | }
14 | return Integer.parseInt(portStr);
15 | }
16 |
17 | public static String getProviderHost() {
18 | return System.getenv(ClientEnv.TESTDATABASE_PROVIDER_HOST_KEY);
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/common/src/main/java/io/metaloom/test/container/provider/common/ServerEnv.java:
--------------------------------------------------------------------------------
1 | package io.metaloom.test.container.provider.common;
2 |
3 | import org.slf4j.Logger;
4 | import org.slf4j.LoggerFactory;
5 |
6 | public final class ServerEnv {
7 |
8 | private static final Logger log = LoggerFactory.getLogger(ServerEnv.class);
9 |
10 | public static final int DEFAULT_POOL_MINIMUM = 10;
11 | public static final int DEFAULT_POOL_MAXIMUM = 20;
12 | public static final int DEFAULT_POOL_INCREMENT = 5;
13 | private static final int DEFAULT_HTTP_PORT = 8080;
14 |
15 | public static final String TESTDATABASE_PROVIDER_DATABASE_HOST_KEY = "TESTDATABASE_PROVIDER_DATABASE_HOST";
16 |
17 | public static final String TESTDATABASE_PROVIDER_DATABASE_PORT_KEY = "TESTDATABASE_PROVIDER_DATABASE_PORT";
18 |
19 | public static final String TESTDATABASE_PROVIDER_DATABASE_INTERNAL_HOST_KEY = "TESTDATABASE_PROVIDER_DATABASE_INTERNAL_HOST";
20 |
21 | public static final String TESTDATABASE_PROVIDER_DATABASE_INTERNAL_PORT_KEY = "TESTDATABASE_PROVIDER_DATABASE_INTERNAL_PORT";
22 |
23 | public static final String TESTDATABASE_PROVIDER_DATABASE_USERNAME_KEY = "TESTDATABASE_PROVIDER_DATABASE_USERNAME";
24 |
25 | public static final String TESTDATABASE_PROVIDER_DATABASE_PASSWORD_KEY = "TESTDATABASE_PROVIDER_DATABASE_PASSWORD";
26 |
27 | public static final String TESTDATABASE_PROVIDER_DATABASE_DBNAME_KEY = "TESTDATABASE_PROVIDER_DATABASE_DBNAME_KEY";
28 |
29 | public static final String TESTDATABASE_PROVIDER_POOL_MINIMUM_KEY = "TESTDATABASE_PROVIDER_POOL_MINIMUM";
30 |
31 | public static final String TESTDATABASE_PROVIDER_POOL_MAXIMUM_KEY = "TESTDATABASE_PROVIDER_POOL_MAXIMUM";
32 |
33 | public static final String TESTDATABASE_PROVIDER_POOL_INCREMENT_KEY = "TESTDATABASE_PROVIDER_POOL_INCREMENT";
34 |
35 | public static final String TESTDATABASE_PROVIDER_DATABASE_TEMPLATE_DBNAME_KEY = "TESTDATABASE_PROVIDER_POOL_TEMPLATE_NAME";
36 |
37 | private static final String TESTDATABASE_PROVIDER_HTTP_PORT_KEY = "TESTDATABASE_PROVIDER_HTTP_PORT";
38 |
39 |
40 | public static int getPoolMinimum() {
41 | String minimumStr = getEnv(ServerEnv.TESTDATABASE_PROVIDER_POOL_MINIMUM_KEY);
42 | if (minimumStr == null) {
43 | log.info("Using default pool minimum value {}", DEFAULT_POOL_MINIMUM);
44 | return DEFAULT_POOL_MINIMUM;
45 | } else {
46 | return Integer.parseInt(minimumStr);
47 | }
48 | }
49 |
50 | public static int getPoolMaximum() {
51 | String maximumStr = getEnv(ServerEnv.TESTDATABASE_PROVIDER_POOL_MAXIMUM_KEY);
52 | if (maximumStr == null) {
53 | log.info("Using default pool maximum value {}", DEFAULT_POOL_MAXIMUM);
54 | return DEFAULT_POOL_MAXIMUM;
55 | } else {
56 | return Integer.parseInt(maximumStr);
57 | }
58 | }
59 |
60 | public static int getPoolIncrement() {
61 | String incrementStr = getEnv(ServerEnv.TESTDATABASE_PROVIDER_POOL_INCREMENT_KEY);
62 | if (incrementStr == null) {
63 | log.info("Using default increment value {}", DEFAULT_POOL_INCREMENT);
64 | return DEFAULT_POOL_INCREMENT;
65 | } else {
66 | return Integer.parseInt(incrementStr);
67 | }
68 | }
69 |
70 | public static Integer getDatabasePort() {
71 | String portStr = getEnv(ServerEnv.TESTDATABASE_PROVIDER_DATABASE_PORT_KEY);
72 | if (portStr == null) {
73 | return null;
74 | }
75 | return Integer.parseInt(portStr);
76 | }
77 |
78 | public static String getDatabaseHost() {
79 | return getEnv(ServerEnv.TESTDATABASE_PROVIDER_DATABASE_HOST_KEY);
80 | }
81 |
82 | public static String getInternalDatabaseHost() {
83 | return getEnv(ServerEnv.TESTDATABASE_PROVIDER_DATABASE_INTERNAL_HOST_KEY);
84 | }
85 |
86 | public static Integer getInternalDatabasePort() {
87 | String portStr = getEnv(ServerEnv.TESTDATABASE_PROVIDER_DATABASE_INTERNAL_PORT_KEY);
88 | if (portStr == null) {
89 | return null;
90 | }
91 | return Integer.parseInt(portStr);
92 | }
93 |
94 | public static String getDatabasePassword() {
95 | return getEnv(ServerEnv.TESTDATABASE_PROVIDER_DATABASE_PASSWORD_KEY);
96 | }
97 |
98 | public static String getDatabaseUsername() {
99 | return getEnv(ServerEnv.TESTDATABASE_PROVIDER_DATABASE_USERNAME_KEY);
100 | }
101 |
102 | public static String getDatabaseName() {
103 | return getEnv(ServerEnv.TESTDATABASE_PROVIDER_DATABASE_DBNAME_KEY);
104 | }
105 |
106 | public static String getDatabaseTemplateName() {
107 | return getEnv(ServerEnv.TESTDATABASE_PROVIDER_DATABASE_TEMPLATE_DBNAME_KEY);
108 | }
109 |
110 | private static String getEnv(String key) {
111 | return System.getenv(key);
112 | }
113 |
114 | public static int getHttpPort() {
115 | String portStr = getEnv(ServerEnv.TESTDATABASE_PROVIDER_HTTP_PORT_KEY);
116 | if (portStr == null) {
117 | return DEFAULT_HTTP_PORT;
118 | }
119 | return Integer.parseInt(portStr);
120 | }
121 | }
122 |
--------------------------------------------------------------------------------
/common/src/main/java/io/metaloom/test/container/provider/common/config/AbstractDatabaseConfig.java:
--------------------------------------------------------------------------------
1 | package io.metaloom.test.container.provider.common.config;
2 |
3 | public abstract class AbstractDatabaseConfig implements DatabaseConfig {
4 |
5 | private String host;
6 | private Integer port;
7 |
8 | /**
9 | * When using docker the provider connects to the database via the internal hostname/port
10 | */
11 | private String internalHost;
12 | private Integer internalPort;
13 | private String username;
14 | private String password;
15 | private String databaseName;
16 | private String containerId;
17 |
18 | @Override
19 | public String getContainerId() {
20 | return containerId;
21 | }
22 |
23 | @Override
24 | public void setContainerId(String containerId) {
25 | this.containerId = containerId;
26 | }
27 |
28 | @Override
29 | public String getHost() {
30 | return host;
31 | }
32 |
33 | @Override
34 | public void setHost(String host) {
35 | this.host = host;
36 | }
37 |
38 | @Override
39 | public String getPassword() {
40 | return password;
41 | }
42 |
43 | @Override
44 | public void setPassword(String password) {
45 | this.password = password;
46 | }
47 |
48 | @Override
49 | public Integer getPort() {
50 | return port;
51 | }
52 |
53 | @Override
54 | public void setPort(Integer port) {
55 | this.port = port;
56 | }
57 |
58 | @Override
59 | public String getUsername() {
60 | return username;
61 | }
62 |
63 | @Override
64 | public void setUsername(String username) {
65 | this.username = username;
66 | }
67 |
68 | @Override
69 | public String getDatabaseName() {
70 | return databaseName;
71 | }
72 |
73 | @Override
74 | public void setDatabaseName(String databaseName) {
75 | this.databaseName = databaseName;
76 | }
77 |
78 | @Override
79 | public String getInternalHost() {
80 | return internalHost;
81 | }
82 |
83 | @Override
84 | public void setInternalHost(String internalHost) {
85 | this.internalHost = internalHost;
86 | }
87 |
88 | @Override
89 | public Integer getInternalPort() {
90 | return internalPort;
91 | }
92 |
93 | @Override
94 | public void setInternalPort(Integer internalPort) {
95 | this.internalPort = internalPort;
96 | }
97 |
98 | }
99 |
--------------------------------------------------------------------------------
/common/src/main/java/io/metaloom/test/container/provider/common/config/DatabaseConfig.java:
--------------------------------------------------------------------------------
1 | package io.metaloom.test.container.provider.common.config;
2 |
3 | public interface DatabaseConfig {
4 |
5 | String getContainerId();
6 |
7 | void setContainerId(String containerId);
8 |
9 | String getHost();
10 |
11 | void setHost(String host);
12 |
13 | Integer getPort();
14 |
15 | void setPort(Integer port);
16 |
17 | String getInternalHost();
18 |
19 | void setInternalHost(String internalHost);
20 |
21 | Integer getInternalPort();
22 |
23 | void setInternalPort(Integer internalPort);
24 |
25 | String getUsername();
26 |
27 | void setUsername(String username);
28 |
29 | String getPassword();
30 |
31 | void setPassword(String password);
32 |
33 | String getDatabaseName();
34 |
35 | void setDatabaseName(String databaseName);
36 |
37 | String adminJdbcUrl();
38 |
39 | String jdbcUrl(String databaseName);
40 |
41 | }
42 |
--------------------------------------------------------------------------------
/common/src/main/java/io/metaloom/test/container/provider/common/config/PostgresqlConfig.java:
--------------------------------------------------------------------------------
1 | package io.metaloom.test.container.provider.common.config;
2 |
3 | public class PostgresqlConfig extends AbstractDatabaseConfig {
4 |
5 | @Override
6 | public String adminJdbcUrl() {
7 | return jdbcUrl(getDatabaseName());
8 | }
9 |
10 | @Override
11 | public String jdbcUrl(String databaseName) {
12 | return ("jdbc:postgresql://" +
13 | getHost() +
14 | ":" +
15 | getPort() +
16 | "/" +
17 | databaseName);
18 | }
19 |
20 | }
21 |
--------------------------------------------------------------------------------
/common/src/main/java/io/metaloom/test/container/provider/common/config/ProviderConfig.java:
--------------------------------------------------------------------------------
1 | package io.metaloom.test.container.provider.common.config;
2 |
3 | public class ProviderConfig {
4 |
5 | private String providerHost;
6 | private int providerPort;
7 | private String providerContainerId;
8 |
9 | private PostgresqlConfig postgresql = new PostgresqlConfig();
10 |
11 | public String getProviderContainerId() {
12 | return providerContainerId;
13 | }
14 |
15 | public ProviderConfig setProviderContainerId(String containerId) {
16 | this.providerContainerId = containerId;
17 | return this;
18 | }
19 |
20 | public int getProviderPort() {
21 | return providerPort;
22 | }
23 |
24 | public ProviderConfig setProviderPort(int port) {
25 | this.providerPort = port;
26 | return this;
27 | }
28 |
29 | public String getProviderHost() {
30 | return providerHost;
31 | }
32 |
33 | public ProviderConfig setProviderHost(String host) {
34 | this.providerHost = host;
35 | return this;
36 | }
37 |
38 | public PostgresqlConfig getPostgresql() {
39 | return postgresql;
40 | }
41 |
42 | public ProviderConfig setPostgresql(PostgresqlConfig postgresql) {
43 | this.postgresql = postgresql;
44 | return this;
45 | }
46 |
47 | @Override
48 | public String toString() {
49 | return "provider: " + getProviderContainerId() + " " + getProviderHost() + ":" + getProviderPort();
50 | }
51 |
52 | }
53 |
--------------------------------------------------------------------------------
/common/src/main/java/io/metaloom/test/container/provider/common/config/ProviderConfigHelper.java:
--------------------------------------------------------------------------------
1 | package io.metaloom.test.container.provider.common.config;
2 |
3 | import java.io.File;
4 | import java.io.IOException;
5 | import java.nio.file.Files;
6 | import java.nio.file.Path;
7 | import java.nio.file.Paths;
8 |
9 | import org.slf4j.Logger;
10 | import org.slf4j.LoggerFactory;
11 |
12 | import com.fasterxml.jackson.databind.ObjectMapper;
13 |
14 | public final class ProviderConfigHelper {
15 |
16 | public static final Logger log = LoggerFactory.getLogger(ProviderConfigHelper.class);
17 |
18 | public static String PROVIDER_CONFIG_FILENAME = "testdatabase-provider.json";
19 |
20 | public static final String CONFIG_FOLDERNAME = "target";
21 |
22 | private ProviderConfigHelper() {
23 | }
24 |
25 | private static ObjectMapper mapper = new ObjectMapper();
26 |
27 | /**
28 | * Searches for the config in all parent directory and overwrites the config if found. Otherwise the config will be written in the current working directory
29 | * location for the config.
30 | *
31 | * @param config
32 | */
33 | public static void writeConfig(ProviderConfig config) {
34 | try {
35 | Path configPath = locateConfigPath();
36 | if (configPath == null) {
37 | configPath = currentConfigPath();
38 | ensureConfigFolder();
39 | }
40 | mapper.writerWithDefaultPrettyPrinter().writeValue(configPath.toFile(), config);
41 | } catch (Exception e) {
42 | throw new RuntimeException(e);
43 | }
44 | }
45 |
46 | /**
47 | * Config path which points to the location based on the current working dir.
48 | *
49 | * @return
50 | */
51 | public static Path currentConfigPath() {
52 | return Paths.get(CONFIG_FOLDERNAME, PROVIDER_CONFIG_FILENAME);
53 | }
54 |
55 | /**
56 | * Locate the config and delete it.
57 | *
58 | * @throws IOException
59 | */
60 | public static void deleteConfig() throws IOException {
61 | Path config = locateConfigPath();
62 | if (Files.isRegularFile(config)) {
63 | Files.delete(config);
64 | } else {
65 | throw new RuntimeException("The config " + config + " is not a rgular file");
66 | }
67 | }
68 |
69 | /**
70 | * Attempt to locate the config file by looking into parent directories if needed.
71 | *
72 | * @return
73 | */
74 | public static ProviderConfig readConfig() {
75 | try {
76 | Path configPath = locateConfigPath();
77 | if (configPath == null) {
78 | return null;
79 | } else {
80 | return readConfig(configPath);
81 | }
82 | } catch (Exception e) {
83 | throw new RuntimeException(e);
84 | }
85 | }
86 |
87 | private static Path locateConfigPath() {
88 | Path folder = Paths.get("").toAbsolutePath();
89 | while (folder != null) {
90 | log.debug("Looking for config file in {}", folder);
91 | Path path = locateIn(folder);
92 | if (path != null) {
93 | return path;
94 | }
95 | folder = folder.getParent();
96 | }
97 | return null;
98 | }
99 |
100 | private static Path locateIn(Path folder) {
101 | Path path = folder.resolve(Paths.get("target", PROVIDER_CONFIG_FILENAME));
102 | if (Files.exists(path)) {
103 | log.debug("Found config in {}", path);
104 | return path;
105 | }
106 | Path path2 = folder.resolve(Paths.get(PROVIDER_CONFIG_FILENAME));
107 | if (Files.exists(path2)) {
108 | log.debug("Found config in {}", path2);
109 | return path2;
110 | }
111 | return null;
112 | }
113 |
114 | private static void ensureConfigFolder() {
115 | File folder = new File(CONFIG_FOLDERNAME);
116 | if (!folder.exists()) {
117 | folder.mkdirs();
118 | }
119 | }
120 |
121 | private static ProviderConfig readConfig(Path path) throws IOException {
122 | if (Files.exists(path)) {
123 | return mapper.readValue(path.toFile(), ProviderConfig.class);
124 | } else {
125 | return null;
126 | }
127 | }
128 |
129 | }
--------------------------------------------------------------------------------
/common/src/main/java/io/metaloom/test/container/provider/common/version/BuildInfo.java:
--------------------------------------------------------------------------------
1 | package io.metaloom.test.container.provider.common.version;
2 |
3 | import java.util.Properties;
4 |
5 | public class BuildInfo {
6 | private String version;
7 | private String buildtimestamp;
8 |
9 | public BuildInfo(String version, String buildtimestamp) {
10 | this.version = version;
11 | this.buildtimestamp = buildtimestamp;
12 | }
13 |
14 | /**
15 | * Create a new build info using the provided properties.
16 | *
17 | * @param buildProperties
18 | */
19 | public BuildInfo(Properties buildProperties) {
20 | this(buildProperties.getProperty("provider.version"), buildProperties.getProperty("provider.build.timestamp"));
21 | }
22 |
23 | /**
24 | * Return the build timestamp.
25 | *
26 | * @return
27 | */
28 | public String getBuildtimestamp() {
29 | return buildtimestamp;
30 | }
31 |
32 | /**
33 | * Set the build timestamp
34 | *
35 | * @param buildtimestamp
36 | * @return Fluent API
37 | */
38 | public BuildInfo setBuildtimestamp(String buildtimestamp) {
39 | this.buildtimestamp = buildtimestamp;
40 | return this;
41 | }
42 |
43 | /**
44 | * Return the version string.
45 | *
46 | * @return
47 | */
48 | public String getVersion() {
49 | return version;
50 | }
51 |
52 | /**
53 | * Set the version string.
54 | *
55 | * @param version
56 | * @return
57 | */
58 | public BuildInfo setVersion(String version) {
59 | this.version = version;
60 | return this;
61 | }
62 |
63 | @Override
64 | public String toString() {
65 | return getVersion() + " " + getBuildtimestamp();
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/common/src/main/java/io/metaloom/test/container/provider/common/version/Version.java:
--------------------------------------------------------------------------------
1 | package io.metaloom.test.container.provider.common.version;
2 |
3 | import java.util.Properties;
4 |
5 | public final class Version {
6 |
7 | private static BuildInfo buildInfo = null;
8 |
9 | private Version() {
10 | }
11 |
12 | /**
13 | * Return the mesh build information.
14 | *
15 | * @return Provider version and build timestamp.
16 | */
17 | public static BuildInfo getBuildInfo() {
18 | try {
19 | if (buildInfo == null) {
20 | Properties buildProperties = new Properties();
21 | buildProperties.load(Version.class.getResourceAsStream("/provider.build.properties"));
22 | // Cache the build information
23 | buildInfo = new BuildInfo(buildProperties);
24 | }
25 | return buildInfo;
26 | } catch (Exception e) {
27 | return new BuildInfo("unknown", "unknown");
28 | }
29 | }
30 |
31 | /**
32 | * Return the mesh version (without build timestamp)
33 | *
34 | * @return Provider version
35 | */
36 | public static String getPlainVersion() {
37 | return getBuildInfo().getVersion();
38 | }
39 |
40 | }
41 |
--------------------------------------------------------------------------------
/common/src/main/java/io/metaloom/test/container/provider/model/AbstractDatabasePoolModel.java:
--------------------------------------------------------------------------------
1 | package io.metaloom.test.container.provider.model;
2 |
3 | public abstract class AbstractDatabasePoolModel implements RestModel {
4 |
5 | private DatabasePoolConnection connection;
6 |
7 | private DatabasePoolSettings settings;
8 |
9 | private String templateDatabaseName;
10 |
11 | public DatabasePoolConnection getConnection() {
12 | return connection;
13 | }
14 |
15 | public AbstractDatabasePoolModel setConnection(DatabasePoolConnection connection) {
16 | this.connection = connection;
17 | return this;
18 | }
19 |
20 | public String getTemplateDatabaseName() {
21 | return templateDatabaseName;
22 | }
23 |
24 | public AbstractDatabasePoolModel setTemplateDatabaseName(String templateDatabaseName) {
25 | this.templateDatabaseName = templateDatabaseName;
26 | return this;
27 | }
28 |
29 | public DatabasePoolSettings getSettings() {
30 | return settings;
31 | }
32 |
33 | public AbstractDatabasePoolModel setSettings(DatabasePoolSettings settings) {
34 | this.settings = settings;
35 | return this;
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/common/src/main/java/io/metaloom/test/container/provider/model/DatabaseAllocationResponse.java:
--------------------------------------------------------------------------------
1 | package io.metaloom.test.container.provider.model;
2 |
3 | public class DatabaseAllocationResponse implements RestModel {
4 |
5 | private String id;
6 | private String poolId;
7 |
8 | private String host;
9 | private int port;
10 | private String jdbcUrl;
11 | private String username;
12 | private String password;
13 | private String databaseName;
14 |
15 | public String getJdbcUrl() {
16 | return jdbcUrl;
17 | }
18 |
19 | public DatabaseAllocationResponse setJdbcUrl(String jdbcUrl) {
20 | this.jdbcUrl = jdbcUrl;
21 | return this;
22 | }
23 |
24 | public String getUsername() {
25 | return username;
26 | }
27 |
28 | public DatabaseAllocationResponse setUsername(String username) {
29 | this.username = username;
30 | return this;
31 | }
32 |
33 | public String getPassword() {
34 | return password;
35 | }
36 |
37 | public DatabaseAllocationResponse setPassword(String password) {
38 | this.password = password;
39 | return this;
40 | }
41 |
42 | public String getHost() {
43 | return host;
44 | }
45 |
46 | public DatabaseAllocationResponse setHost(String host) {
47 | this.host = host;
48 | return this;
49 | }
50 |
51 | public int getPort() {
52 | return port;
53 | }
54 |
55 | public DatabaseAllocationResponse setPort(int port) {
56 | this.port = port;
57 | return this;
58 | }
59 |
60 | public String getId() {
61 | return id;
62 | }
63 |
64 | public DatabaseAllocationResponse setId(String id) {
65 | this.id = id;
66 | return this;
67 | }
68 |
69 | public String getPoolId() {
70 | return poolId;
71 | }
72 |
73 | public DatabaseAllocationResponse setPoolId(String poolId) {
74 | this.poolId = poolId;
75 | return this;
76 | }
77 |
78 | public String getDatabaseName() {
79 | return databaseName;
80 | }
81 |
82 | public DatabaseAllocationResponse setDatabaseName(String databaseName) {
83 | this.databaseName = databaseName;
84 | return this;
85 | }
86 |
87 | @Override
88 | public String toString() {
89 | return "allocation: " + getId() + " of " + getPoolId() + " => " + getJdbcUrl();
90 | }
91 |
92 | }
93 |
--------------------------------------------------------------------------------
/common/src/main/java/io/metaloom/test/container/provider/model/DatabasePoolConnection.java:
--------------------------------------------------------------------------------
1 | package io.metaloom.test.container.provider.model;
2 |
3 | import io.metaloom.test.container.provider.common.config.PostgresqlConfig;
4 |
5 | public class DatabasePoolConnection implements RestModel {
6 |
7 | private Integer port;
8 | private String host;
9 |
10 | private String internalHost;
11 | private Integer internalPort;
12 |
13 | private String username;
14 | private String password;
15 | private String database;
16 |
17 | public DatabasePoolConnection() {
18 | }
19 |
20 | /**
21 | * Construct a new pool connection using the provided config
22 | *
23 | * @param config
24 | */
25 | public DatabasePoolConnection(PostgresqlConfig config) {
26 | this.host = config.getHost();
27 | this.port = config.getPort();
28 | this.internalHost = config.getInternalHost();
29 | this.internalPort = config.getInternalPort();
30 | this.username = config.getUsername();
31 | this.password = config.getPassword();
32 | this.database = config.getDatabaseName();
33 | }
34 |
35 | public String getHost() {
36 | return host;
37 | }
38 |
39 | public DatabasePoolConnection setHost(String host) {
40 | this.host = host;
41 | return this;
42 | }
43 |
44 | public Integer getPort() {
45 | return port;
46 | }
47 |
48 | public DatabasePoolConnection setPort(Integer port) {
49 | this.port = port;
50 | return this;
51 | }
52 |
53 | public String getInternalHost() {
54 | return internalHost;
55 | }
56 |
57 | public DatabasePoolConnection setInternalHost(String internalHost) {
58 | this.internalHost = internalHost;
59 | return this;
60 | }
61 |
62 | public Integer getInternalPort() {
63 | return internalPort;
64 | }
65 |
66 | public DatabasePoolConnection setInternalPort(Integer internalPort) {
67 | this.internalPort = internalPort;
68 | return this;
69 | }
70 |
71 | public String getUsername() {
72 | return username;
73 | }
74 |
75 | public DatabasePoolConnection setUsername(String username) {
76 | this.username = username;
77 | return this;
78 | }
79 |
80 | public String getPassword() {
81 | return password;
82 | }
83 |
84 | public DatabasePoolConnection setPassword(String password) {
85 | this.password = password;
86 | return this;
87 | }
88 |
89 | public String getDatabase() {
90 | return database;
91 | }
92 |
93 | public DatabasePoolConnection setDatabase(String database) {
94 | this.database = database;
95 | return this;
96 | }
97 |
98 | }
99 |
--------------------------------------------------------------------------------
/common/src/main/java/io/metaloom/test/container/provider/model/DatabasePoolListResponse.java:
--------------------------------------------------------------------------------
1 | package io.metaloom.test.container.provider.model;
2 |
3 | import java.util.ArrayList;
4 | import java.util.List;
5 |
6 | public class DatabasePoolListResponse implements RestModel {
7 |
8 | private List list = new ArrayList<>();
9 |
10 | public List getList() {
11 | return list;
12 | }
13 |
14 | public DatabasePoolListResponse setList(List list) {
15 | this.list = list;
16 | return this;
17 | }
18 |
19 | public void add(DatabasePoolResponse response) {
20 | list.add(response);
21 | }
22 |
23 | }
24 |
--------------------------------------------------------------------------------
/common/src/main/java/io/metaloom/test/container/provider/model/DatabasePoolRequest.java:
--------------------------------------------------------------------------------
1 | package io.metaloom.test.container.provider.model;
2 |
3 | public class DatabasePoolRequest extends AbstractDatabasePoolModel {
4 |
5 | }
6 |
--------------------------------------------------------------------------------
/common/src/main/java/io/metaloom/test/container/provider/model/DatabasePoolResponse.java:
--------------------------------------------------------------------------------
1 | package io.metaloom.test.container.provider.model;
2 |
3 | import java.time.LocalDateTime;
4 |
5 | import com.fasterxml.jackson.annotation.JsonFormat;
6 | import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
7 | import com.fasterxml.jackson.databind.annotation.JsonSerialize;
8 | import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
9 | import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
10 |
11 | public class DatabasePoolResponse extends AbstractDatabasePoolModel {
12 |
13 | private String id;
14 | private int level;
15 |
16 | private int allocationLevel;
17 | private boolean started;
18 |
19 | @JsonDeserialize(using = LocalDateTimeDeserializer.class)
20 | @JsonSerialize(using = LocalDateTimeSerializer.class)
21 | @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss")
22 | private LocalDateTime created;
23 |
24 | public int getLevel() {
25 | return level;
26 | }
27 |
28 | public DatabasePoolResponse setLevel(int level) {
29 | this.level = level;
30 | return this;
31 | }
32 |
33 | public int getAllocationLevel() {
34 | return allocationLevel;
35 | }
36 |
37 | public DatabasePoolResponse setAllocationLevel(int allocationLevel) {
38 | this.allocationLevel = allocationLevel;
39 | return this;
40 | }
41 |
42 | public boolean isStarted() {
43 | return started;
44 | }
45 |
46 | public DatabasePoolResponse setStarted(boolean started) {
47 | this.started = started;
48 | return this;
49 | }
50 |
51 | public String getId() {
52 | return id;
53 | }
54 |
55 | public DatabasePoolResponse setId(String id) {
56 | this.id = id;
57 | return this;
58 | }
59 |
60 | public LocalDateTime getCreated() {
61 | return created;
62 | }
63 |
64 | public void setCreated(LocalDateTime creationDate) {
65 | this.created = creationDate;
66 | }
67 |
68 | @Override
69 | public String toString() {
70 | return "pool: " + getId() + " " + getCreated() + ", started: " + isStarted() + ", level: " + getLevel() + ", already allocated: "
71 | + getAllocationLevel();
72 | }
73 |
74 | }
75 |
--------------------------------------------------------------------------------
/common/src/main/java/io/metaloom/test/container/provider/model/DatabasePoolSettings.java:
--------------------------------------------------------------------------------
1 | package io.metaloom.test.container.provider.model;
2 |
3 | public class DatabasePoolSettings implements RestModel {
4 |
5 | private Integer increment;
6 | private Integer minimum;
7 | private Integer maximum;
8 |
9 | public Integer getMinimum() {
10 | return minimum;
11 | }
12 |
13 | public DatabasePoolSettings setMinimum(Integer minimum) {
14 | this.minimum = minimum;
15 | return this;
16 | }
17 |
18 | public Integer getMaximum() {
19 | return maximum;
20 | }
21 |
22 | public DatabasePoolSettings setMaximum(Integer maximum) {
23 | this.maximum = maximum;
24 | return this;
25 | }
26 |
27 | public Integer getIncrement() {
28 | return increment;
29 | }
30 |
31 | public DatabasePoolSettings setIncrement(Integer increment) {
32 | this.increment = increment;
33 | return this;
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/common/src/main/java/io/metaloom/test/container/provider/model/RestModel.java:
--------------------------------------------------------------------------------
1 | package io.metaloom.test.container.provider.model;
2 |
3 | /**
4 | * Marker interface for REST model classes.
5 | */
6 | public interface RestModel {
7 |
8 | }
9 |
--------------------------------------------------------------------------------
/common/src/main/resources/provider.build.properties:
--------------------------------------------------------------------------------
1 | provider.version=${version}
2 | provider.build.timestamp=${provider.build.timestamp}
--------------------------------------------------------------------------------
/common/src/test/java/io/metaloom/test/container/provider/common/config/ProviderConfigHelperTest.java:
--------------------------------------------------------------------------------
1 | package io.metaloom.test.container.provider.common.config;
2 |
3 | import static org.junit.jupiter.api.Assertions.assertEquals;
4 | import static org.junit.jupiter.api.Assertions.assertNotNull;
5 | import static org.junit.jupiter.api.Assertions.assertTrue;
6 |
7 | import java.io.File;
8 | import java.io.IOException;
9 | import java.nio.file.Files;
10 |
11 | import org.junit.jupiter.api.Test;
12 |
13 | public class ProviderConfigHelperTest {
14 |
15 | @Test
16 | public void testReadFromParent() throws IOException {
17 | File testFile = writeConfigIntoParentFolder();
18 | ProviderConfig state = ProviderConfigHelper.readConfig();
19 | assertNotNull(state);
20 | assertEquals("test1234", state.getPostgresql().getHost());
21 | testFile.delete();
22 | }
23 |
24 | @Test
25 | public void testWriteCurrent() {
26 | File config = new File(ProviderConfigHelper.CONFIG_FOLDERNAME, ProviderConfigHelper.PROVIDER_CONFIG_FILENAME);
27 | if (config.exists()) {
28 | config.delete();
29 | }
30 | ProviderConfigHelper.writeConfig(new ProviderConfig());
31 | assertTrue(config.exists());
32 | config.delete();
33 | }
34 |
35 | @Test
36 | public void testWriteParent() throws IOException {
37 | writeConfigIntoParentFolder();
38 | ProviderConfigHelper.writeConfig(new ProviderConfig().setProviderContainerId("enemenemuh"));
39 | assertEquals("enemenemuh", ProviderConfigHelper.readConfig().getProviderContainerId());
40 | }
41 |
42 | private File writeConfigIntoParentFolder() throws IOException {
43 | ProviderConfigHelper.PROVIDER_CONFIG_FILENAME = "test-testdb-provider.json";
44 | File testFile = new File("../target/", ProviderConfigHelper.PROVIDER_CONFIG_FILENAME);
45 | if (testFile.exists()) {
46 | testFile.delete();
47 | }
48 | String json = """
49 | {
50 | "postgresql": {
51 | "host": "test1234"
52 | }
53 |
54 | }
55 | """;
56 | Files.writeString(testFile.toPath(), json);
57 | return testFile;
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/common/src/test/java/io/metaloom/test/container/provider/common/version/VersionTest.java:
--------------------------------------------------------------------------------
1 | package io.metaloom.test.container.provider.common.version;
2 |
3 | import static org.junit.jupiter.api.Assertions.assertNotNull;
4 |
5 | import org.junit.jupiter.api.Test;
6 |
7 | public class VersionTest {
8 |
9 | @Test
10 | public void testVersion() {
11 | String version = Version.getPlainVersion();
12 | System.out.println(version);
13 | assertNotNull(version);
14 | String buildTS = Version.getBuildInfo().getBuildtimestamp();
15 | assertNotNull(buildTS);
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/examples/complex/README.md:
--------------------------------------------------------------------------------
1 | # Complex Example
2 |
3 | This example shows how to use the `testdb-maven-plugin` in a multi-module maven build.
4 |
5 | It assumes two sub modules (`moduleA`, `moduleB`)
6 |
7 | ## moduleA
8 |
9 | This module contains the flyway migration resources. It prepares the provided database and initializes the testdb pool. Once the pool has been setup the provider daemon can hand out databases for tests.
10 |
11 | ## moduleB
12 |
13 | This module contains the tests for the project which make use of the created pool.
14 |
15 |
16 | ## Parent
17 |
18 | The parent project `testdatabase-provider-complex-example` contains the invocation of the `start` goal in order to setup the database container so that the flyway container can setup the db. The startup of the containers should be located in the parent module for all modules that want to access the database. Otherwise the configuration can't be located by tests. To prevent `start` from being invoked in submodules it is important to add the `false` tag to the execution.
--------------------------------------------------------------------------------
/examples/complex/moduleA/pom.xml:
--------------------------------------------------------------------------------
1 |
4 | 4.0.0
5 | testdatabase-provider-complex-example-modulea
6 |
7 |
8 | io.metaloom.test.example
9 | testdatabase-provider-complex-example
10 | 0.0.1-SNAPSHOT
11 |
12 |
13 | Testdatabase Provider :: Example :: Complex - Module A
14 | This module contains the database setup and pool creation.
15 |
16 |
17 |
18 |
19 | org.flywaydb
20 | flyway-maven-plugin
21 |
22 |
23 |
24 | generate-sources
25 |
26 | migrate
27 |
28 |
29 |
30 |
31 | ${maven.testdb.postgresql.jdbcurl}
32 | ${maven.testdb.postgresql.username}
33 | ${maven.testdb.postgresql.password}
34 |
35 | filesystem:src/main/flyway
36 |
37 |
38 |
39 |
40 | org.postgresql
41 | postgresql
42 | ${postgres.driver.version}
43 |
44 |
45 |
46 |
47 |
48 | io.metaloom.maven
49 | testdb-maven-plugin
50 |
51 |
53 |
54 |
55 | pool
56 | process-test-classes
57 |
58 | pool
59 |
60 |
61 |
62 |
63 | dummy
64 | test
65 |
66 | 10
67 | 30
68 | 5
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
--------------------------------------------------------------------------------
/examples/complex/moduleA/src/main/flyway/V1__initial_setup.sql:
--------------------------------------------------------------------------------
1 | DROP SCHEMA IF EXISTS helloworld CASCADE;
2 |
3 | CREATE SCHEMA helloworld;
4 |
5 | CREATE TABLE helloworld.test (
6 | id INT NOT NULL GENERATED ALWAYS AS IDENTITY,
7 | cd INT,
8 |
9 | CONSTRAINT pk_test PRIMARY KEY (id)
10 | );
--------------------------------------------------------------------------------
/examples/complex/moduleB/pom.xml:
--------------------------------------------------------------------------------
1 |
4 | 4.0.0
5 | testdatabase-provider-complex-example-moduleb
6 |
7 |
8 | io.metaloom.test.example
9 | testdatabase-provider-complex-example
10 | 0.0.1-SNAPSHOT
11 |
12 |
13 | Testdatabase Provider :: Example :: Complex - Module B
14 | This module contains the actual tests which utiilize the
15 | previously setup database
16 |
17 |
18 |
19 |
20 | io.metaloom.test
21 | testdatabase-provider-junit5
22 | test
23 |
24 |
25 |
26 |
27 | io.metaloom.test
28 | testdatabase-provider-junit4
29 | test
30 |
31 |
32 |
--------------------------------------------------------------------------------
/examples/complex/moduleB/src/test/java/io/metaloom/example/ExampleJunit4Test.java:
--------------------------------------------------------------------------------
1 | package io.metaloom.example;
2 |
3 | import org.junit.Rule;
4 | import org.junit.Test;
5 |
6 | import io.metaloom.test.provider.junit4.DatabaseProviderRule;
7 |
8 | public class ExampleJunit4Test {
9 | // SNIPPET START test_snippet
10 | @Rule
11 | public DatabaseProviderRule provider = DatabaseProviderRule.create("dummy");
12 |
13 | @Test
14 | public void testDB() throws Exception {
15 | System.out.println(provider.db());
16 | }
17 | // SNIPPET END test_snippet
18 |
19 | @Test
20 | public void testDB2() {
21 | System.out.println(provider.db());
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/examples/complex/moduleB/src/test/java/io/metaloom/example/ExampleJunit5Test.java:
--------------------------------------------------------------------------------
1 | package io.metaloom.example;
2 |
3 | import org.junit.jupiter.api.Test;
4 | import org.junit.jupiter.api.extension.RegisterExtension;
5 |
6 | import io.metaloom.test.provider.junit5.ProviderExtension;
7 |
8 | public class ExampleJunit5Test {
9 |
10 | // SNIPPET START test_snippet
11 | @RegisterExtension
12 | public static ProviderExtension ext = ProviderExtension.create("dummy");
13 |
14 | @Test
15 | public void testDB() throws Exception {
16 | System.out.println(ext.db());
17 | }
18 | // SNIPPET END test_snippet
19 |
20 | @Test
21 | public void testDB2() {
22 | System.out.println(ext.db());
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/examples/complex/pom.xml:
--------------------------------------------------------------------------------
1 |
4 | 4.0.0
5 | testdatabase-provider-complex-example
6 |
7 |
8 | io.metaloom.test.example
9 | testdatabase-provider-examples
10 | 0.0.1-SNAPSHOT
11 |
12 |
13 | Testdatabase Provider :: Complex Example
14 |
15 | pom
16 |
17 | moduleA
18 | moduleB
19 |
20 |
21 |
22 |
23 | org.postgresql
24 | postgresql
25 |
26 |
27 |
28 |
29 | io.metaloom.test
30 | testdatabase-provider-junit5
31 | test
32 |
33 |
34 | ch.qos.logback
35 | logback-classic
36 | test
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 | io.metaloom.maven
46 | testdb-maven-plugin
47 |
48 |
49 |
50 | false
51 | clean
52 |
53 | clean
54 |
55 |
56 |
57 |
58 | false
59 |
60 | start
61 | initialize
62 |
63 | start
64 |
65 |
66 | false
67 |
68 |
69 | 10
70 | 20
71 | 5
72 |
73 | false
74 | true
75 |
76 |
77 | postgres:13.2
78 | true
79 | 256
80 | sa
81 | sa
82 | test
83 |
89 |
90 | true
91 |
92 |
93 |
94 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
--------------------------------------------------------------------------------
/examples/dedicated-no-maven-plugin/docker-compose.yml:
--------------------------------------------------------------------------------
1 | version: '3'
2 | services:
3 | db:
4 | container_name: 'testprovider-postgresql'
5 | hostname: 'postgresql'
6 | image: 'postgres:13.2'
7 | ports:
8 | - '15432:5432/tcp'
9 | environment:
10 | - POSTGRES_USER=sa
11 | - POSTGRES_PASSWORD=sa
12 | - POSTGRES_DB=test
13 | command: [ "postgres", "-c", "fsync=off" ]
14 | restart: unless-stopped
15 | provider:
16 | container_name: 'testprovider-provider'
17 | hostname: 'provider'
18 | image: 'metaloom/testdatabase-provider:0.1.4'
19 | ports:
20 | - '7543:8080/tcp'
21 | environment:
22 | # Admin DB
23 | - TESTDATABASE_PROVIDER_DATABASE_DBNAME_KEY=test
24 | # Public DB connection details
25 | - TESTDATABASE_PROVIDER_DATABASE_HOST=localhost
26 | - TESTDATABASE_PROVIDER_DATABASE_PORT=15432
27 | # Internal DB connection details (inter container communication)
28 | - TESTDATABASE_PROVIDER_DATABASE_INTERNAL_HOST=postgresql
29 | - TESTDATABASE_PROVIDER_DATABASE_INTERNAL_PORT=5432
30 | - TESTDATABASE_PROVIDER_DATABASE_USERNAME=sa
31 | - TESTDATABASE_PROVIDER_DATABASE_PASSWORD=sa
32 | # Default pool settings
33 | - TESTDATABASE_PROVIDER_POOL_MAXIMUM=40
34 | - TESTDATABASE_PROVIDER_POOL_MINIMUM=20
35 | - TESTDATABASE_PROVIDER_POOL_INCREMENT=10
36 | restart: unless-stopped
37 |
--------------------------------------------------------------------------------
/examples/dedicated-no-maven-plugin/pom.xml:
--------------------------------------------------------------------------------
1 |
4 | 4.0.0
5 | testdatabase-provider-dedicated-no-mvn-example
6 |
7 |
8 | io.metaloom.test.example
9 | testdatabase-provider-examples
10 | 0.0.1-SNAPSHOT
11 |
12 |
13 | Testdatabase Provider :: Dedicated (No Maven Plugin) Example
14 |
15 |
16 |
17 | io.metaloom.test
18 | testdatabase-provider-junit5
19 | test
20 |
21 |
22 | io.metaloom.test
23 | testdatabase-provider-junit4
24 | test
25 |
26 |
27 | org.postgresql
28 | postgresql
29 | test
30 |
31 |
32 | ch.qos.logback
33 | logback-classic
34 | test
35 |
36 |
37 | org.flywaydb
38 | flyway-core
39 | test
40 |
41 |
42 |
--------------------------------------------------------------------------------
/examples/dedicated-no-maven-plugin/src/test/java/io/metaloom/example/ExampleJunit4Test.java:
--------------------------------------------------------------------------------
1 | package io.metaloom.example;
2 |
3 | import org.junit.Rule;
4 | import org.junit.Test;
5 |
6 | import io.metaloom.test.provider.junit4.DatabaseProviderRule;
7 |
8 | public class ExampleJunit4Test {
9 |
10 | // SNIPPET START provider
11 | @Rule
12 | public DatabaseProviderRule provider = DatabaseProviderRule.create("localhost", 7543, "dummy");
13 | // SNIPPET END provider
14 |
15 | @Test
16 | public void testDB() throws Exception {
17 | System.out.println(provider.db());
18 | }
19 |
20 | @Test
21 | public void testDB2() {
22 | System.out.println(provider.db());
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/examples/dedicated-no-maven-plugin/src/test/java/io/metaloom/example/ExampleJunit5Test.java:
--------------------------------------------------------------------------------
1 | package io.metaloom.example;
2 |
3 | import org.junit.jupiter.api.Test;
4 | import org.junit.jupiter.api.extension.RegisterExtension;
5 |
6 | import io.metaloom.test.provider.junit5.ProviderExtension;
7 |
8 | public class ExampleJunit5Test {
9 |
10 | // SNIPPET START provider
11 | @RegisterExtension
12 | public static ProviderExtension ext = ProviderExtension.create("localhost", 7543, "dummy");
13 | // SNIPPET END provider
14 |
15 | @Test
16 | public void testDB() throws Exception {
17 | System.out.println(ext.db());
18 | }
19 |
20 | @Test
21 | public void testDB2() {
22 | System.out.println(ext.db());
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/examples/dedicated-no-maven-plugin/src/test/java/io/metaloom/example/PoolSetupAction.java:
--------------------------------------------------------------------------------
1 | package io.metaloom.example;
2 |
3 | import org.flywaydb.core.Flyway;
4 | import org.flywaydb.core.api.output.MigrateResult;
5 |
6 | import io.metaloom.test.container.provider.client.TestDatabaseProvider;
7 | import io.metaloom.test.container.provider.common.config.ProviderConfig;
8 | import io.metaloom.test.container.provider.model.DatabasePoolResponse;
9 |
10 | /**
11 | * Example implementation for a custom pool setup operation.
12 | */
13 | public class PoolSetupAction {
14 |
15 | public static void main(String[] args) throws Exception {
16 |
17 |
18 | // 1. Set a local configuration that points to
19 | // the provider and database
20 | // SNIPPET START localconfig
21 | ProviderConfig config = new ProviderConfig();
22 | config.setProviderHost("localhost");
23 | config.setProviderPort(7543);
24 | config.getPostgresql().setPassword("sa");
25 | config.getPostgresql().setUsername("sa");
26 | config.getPostgresql().setDatabaseName("test");
27 | config.getPostgresql().setHost("saturn");
28 | config.getPostgresql().setPort(15432);
29 | TestDatabaseProvider.localConfig(config);
30 | // SNIPPET END localconfig
31 |
32 | // 2. Replace the old database with an empty one.
33 | // The settings will be taken from the database settings
34 | // which were defined in the testdb-maven-plugin section of your pom.xml
35 | String templateDBName = "template-database";
36 | TestDatabaseProvider.dropCreatePostgreSQLDatabase(templateDBName);
37 |
38 | // 3. Now setup your tables using the flyway migration.
39 | String url = config.getPostgresql().jdbcUrl(templateDBName);
40 | String user = config.getPostgresql().getUsername();
41 | String password = config.getPostgresql().getPassword();
42 | Flyway flyway = Flyway.configure().dataSource(url, user, password).load();
43 | MigrateResult result = flyway.migrate();
44 |
45 | System.out.println(result.success ? "Flyway migration OK" : "Flyway migration Failed");
46 |
47 | // 4. Now recreate the dummy pool. The pool will provide the new databases for our tests.
48 | DatabasePoolResponse response = TestDatabaseProvider.createPool("dummy", templateDBName);
49 | System.out.println("\nPool Created: " + response.toString());
50 |
51 | // 5. Now run your unit tests and happy testing
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/examples/dedicated/README.md:
--------------------------------------------------------------------------------
1 | # Dedicated Example
2 |
3 | This example shows how the needed database and provider services can be setup in a dedicated way which is not managed by the `testdb-maven-plugin`. This way one DB + daemon can be setup to be utilized by multiple projects or in a different hosting environment.
4 |
5 | The `testdb-maven-plugin` will in this case only be used to provide the needed configuration for the unit tests. It will write the configuration file in the `target` folder.
6 |
7 | Additionally it can be used to setup the test database pools.
8 |
--------------------------------------------------------------------------------
/examples/dedicated/docker-compose.yml:
--------------------------------------------------------------------------------
1 | version: '3'
2 | services:
3 | db:
4 | container_name: 'testprovider-postgresql'
5 | hostname: 'postgresql'
6 | image: 'postgres:13.2'
7 | ports:
8 | - '15432:5432/tcp'
9 | environment:
10 | - POSTGRES_USER=sa
11 | - POSTGRES_PASSWORD=sa
12 | - POSTGRES_DB=test
13 | command: [ "postgres", "-c", "fsync=off" ]
14 | restart: unless-stopped
15 | provider:
16 | container_name: 'testprovider-provider'
17 | hostname: 'provider'
18 | image: 'metaloom/testdatabase-provider:0.1.4'
19 | ports:
20 | - '7543:8080/tcp'
21 | environment:
22 | # Admin DB
23 | - TESTDATABASE_PROVIDER_DATABASE_DBNAME_KEY=test
24 | # Public DB connection details
25 | - TESTDATABASE_PROVIDER_DATABASE_HOST=localhost
26 | - TESTDATABASE_PROVIDER_DATABASE_PORT=15432
27 | # Internal DB connection details (inter container communication)
28 | - TESTDATABASE_PROVIDER_DATABASE_INTERNAL_HOST=postgresql
29 | - TESTDATABASE_PROVIDER_DATABASE_INTERNAL_PORT=5432
30 | - TESTDATABASE_PROVIDER_DATABASE_USERNAME=sa
31 | - TESTDATABASE_PROVIDER_DATABASE_PASSWORD=sa
32 | # Default pool settings
33 | - TESTDATABASE_PROVIDER_POOL_MAXIMUM=40
34 | - TESTDATABASE_PROVIDER_POOL_MINIMUM=20
35 | - TESTDATABASE_PROVIDER_POOL_INCREMENT=10
36 | restart: unless-stopped
37 |
--------------------------------------------------------------------------------
/examples/dedicated/pom.xml:
--------------------------------------------------------------------------------
1 |
4 | 4.0.0
5 | testdatabase-provider-dedicated-example
6 |
7 |
8 | io.metaloom.test.example
9 | testdatabase-provider-examples
10 | 0.0.1-SNAPSHOT
11 |
12 |
13 | Testdatabase Provider :: Dedicated Example
14 |
15 |
16 |
17 | io.metaloom.test
18 | testdatabase-provider-junit5
19 | test
20 |
21 |
22 | io.metaloom.test
23 | testdatabase-provider-junit4
24 | test
25 |
26 |
27 |
28 |
29 |
30 |
31 | io.metaloom.maven
32 | testdb-maven-plugin
33 |
34 |
35 |
36 | clean
37 |
38 |
39 |
41 |
42 |
43 | pool
44 | process-test-classes
45 |
46 | start
47 | pool
48 |
49 |
50 |
51 |
52 | dummy
53 | test
54 |
55 | 10
56 | 30
57 | 5
58 |
59 |
60 |
61 |
62 |
63 |
64 | stop
65 |
66 | post-integration-test
67 |
68 | stop
69 |
70 |
71 |
72 |
73 |
74 |
75 | false
76 | localhost
77 | 7543
78 |
79 |
80 | false
81 | sa
82 | sa
83 | test
84 | saturn
85 | 15432
86 | postgresql
87 | 5432
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
--------------------------------------------------------------------------------
/examples/dedicated/src/test/java/io/metaloom/example/ExampleJunit4Test.java:
--------------------------------------------------------------------------------
1 | package io.metaloom.example;
2 |
3 | import org.junit.Rule;
4 | import org.junit.Test;
5 |
6 | import io.metaloom.test.provider.junit4.DatabaseProviderRule;
7 |
8 | public class ExampleJunit4Test {
9 |
10 | @Rule
11 | public DatabaseProviderRule provider = DatabaseProviderRule.create("dummy");
12 |
13 | @Test
14 | public void testDB() throws Exception {
15 | System.out.println(provider.db());
16 | }
17 |
18 | @Test
19 | public void testDB2() {
20 | System.out.println(provider.db());
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/examples/dedicated/src/test/java/io/metaloom/example/ExampleJunit5Test.java:
--------------------------------------------------------------------------------
1 | package io.metaloom.example;
2 |
3 | import org.junit.jupiter.api.Test;
4 | import org.junit.jupiter.api.extension.RegisterExtension;
5 |
6 | import io.metaloom.test.provider.junit5.ProviderExtension;
7 |
8 | public class ExampleJunit5Test {
9 |
10 | @RegisterExtension
11 | public static ProviderExtension ext = ProviderExtension.create("dummy");
12 |
13 | @Test
14 | public void testDB() throws Exception {
15 | System.out.println(ext.db());
16 | }
17 |
18 | @Test
19 | public void testDB2() {
20 | System.out.println(ext.db());
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/examples/minimal/README.md:
--------------------------------------------------------------------------------
1 | # Minimal Example
2 |
3 | The minimal example only contains the bare minimum maven configuration.
4 |
5 | The maven configuration does:
6 |
7 | 1. Clean the environment from previously running instances
8 | 2. Startup the needed db + provider containers
9 | 3. Invoke flyway to run the db migration
10 | 4. Setup the testdb pool for the prepared database
11 | 5. Run the tests
12 | 6. Stop the containers
13 |
14 | This example also shows how a pooled database can be updated via the dedicated `PoolSetupAction` class. It recreates the db and runs flyway to setup the tables. Afterwards it re-created the testdatabase pool.
15 |
--------------------------------------------------------------------------------
/examples/minimal/pom.xml:
--------------------------------------------------------------------------------
1 |
4 | 4.0.0
5 | testdatabase-provider-minimal-example
6 |
7 |
8 | io.metaloom.test.example
9 | testdatabase-provider-examples
10 | 0.0.1-SNAPSHOT
11 |
12 |
13 | Testdatabase Provider :: Minimal Example
14 |
15 |
16 |
17 |
18 | io.metaloom.test
19 | testdatabase-provider-junit5
20 | test
21 |
22 |
23 |
24 | io.metaloom.test
25 | testdatabase-provider-junit4
26 | test
27 |
28 |
29 | org.postgresql
30 | postgresql
31 | test
32 |
33 |
34 | ch.qos.logback
35 | logback-classic
36 | test
37 |
38 |
39 | org.flywaydb
40 | flyway-core
41 | test
42 |
43 |
44 |
45 |
46 |
47 |
48 | org.flywaydb
49 | flyway-maven-plugin
50 |
51 |
52 |
53 |
54 | generate-sources
55 |
56 | migrate
57 |
58 |
59 |
60 |
61 | ${maven.testdb.postgresql.jdbcurl}
62 | ${maven.testdb.postgresql.username}
63 | ${maven.testdb.postgresql.password}
64 |
65 | filesystem:src/main/resources/db/migration
66 |
67 |
68 |
69 |
70 | org.postgresql
71 | postgresql
72 | ${postgres.driver.version}
73 |
74 |
75 |
76 |
77 |
78 | io.metaloom.maven
79 | testdb-maven-plugin
80 |
81 |
83 |
84 | cleanup
85 |
86 | clean
87 |
88 |
89 |
90 |
91 |
92 | setup
93 |
94 | start
95 |
96 |
97 |
99 |
100 |
101 | pool
102 | process-test-classes
103 |
104 | pool
105 |
106 |
107 |
108 |
109 | dummy
110 | postgres
111 |
112 | 10
113 | 30
114 | 5
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 | stop
123 |
124 | stop
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
--------------------------------------------------------------------------------
/examples/minimal/src/main/resources/db/migration/V1__initial_setup.sql:
--------------------------------------------------------------------------------
1 |
2 | CREATE TABLE users (id INT PRIMARY KEY, name TEXT);
3 |
--------------------------------------------------------------------------------
/examples/minimal/src/test/java/io/metaloom/example/ExampleJunit4Test.java:
--------------------------------------------------------------------------------
1 | package io.metaloom.example;
2 |
3 | import org.junit.Rule;
4 | import org.junit.Test;
5 |
6 | import io.metaloom.test.provider.junit4.DatabaseProviderRule;
7 |
8 | public class ExampleJunit4Test {
9 | // SNIPPET START test_snippet
10 | @Rule
11 | public DatabaseProviderRule provider = DatabaseProviderRule.create("dummy");
12 |
13 | @Test
14 | public void testDB() throws Exception {
15 | System.out.println(provider.db());
16 | }
17 | // SNIPPET END test_snippet
18 |
19 | @Test
20 | public void testDB2() {
21 | System.out.println(provider.db());
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/examples/minimal/src/test/java/io/metaloom/example/ExampleJunit5Test.java:
--------------------------------------------------------------------------------
1 | package io.metaloom.example;
2 |
3 | import static org.junit.jupiter.api.Assertions.assertEquals;
4 |
5 | import java.sql.Connection;
6 | import java.sql.DriverManager;
7 | import java.sql.PreparedStatement;
8 | import java.sql.SQLException;
9 |
10 | import org.junit.jupiter.api.Test;
11 | import org.junit.jupiter.api.extension.RegisterExtension;
12 |
13 | import io.metaloom.test.provider.junit5.ProviderExtension;
14 |
15 | public class ExampleJunit5Test {
16 |
17 | // SNIPPET START test_snippet
18 | @RegisterExtension
19 | public static ProviderExtension ext = ProviderExtension.create("dummy");
20 |
21 | @Test
22 | public void testDB() throws Exception {
23 | System.out.println(ext.db());
24 | }
25 | // SNIPPET END test_snippet
26 |
27 | @Test
28 | public void testDB2() throws SQLException {
29 | String INSERT_USER = "INSERT INTO users (id, name) VALUES (?, ?)";
30 |
31 | try (Connection connection = DriverManager.getConnection(ext.db().getJdbcUrl(), ext.db().getUsername(), ext.db().getPassword())) {
32 | PreparedStatement statement = connection.prepareStatement(INSERT_USER);
33 | statement.setInt(1, 42);
34 | statement.setString(2, "johannes");
35 | assertEquals(1, statement.executeUpdate(), "One row should have been updated");
36 | }
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/examples/minimal/src/test/java/io/metaloom/example/PoolSetupAction.java:
--------------------------------------------------------------------------------
1 | package io.metaloom.example;
2 |
3 | import org.flywaydb.core.Flyway;
4 | import org.flywaydb.core.api.output.MigrateResult;
5 |
6 | import io.metaloom.test.container.provider.client.TestDatabaseProvider;
7 | import io.metaloom.test.container.provider.common.config.ProviderConfig;
8 | import io.metaloom.test.container.provider.model.DatabasePoolResponse;
9 |
10 | /**
11 | * Example implementation for a custom pool setup operation.
12 | */
13 | public class PoolSetupAction {
14 |
15 | public static void main(String[] args) throws Exception {
16 |
17 | // SNIPPET START pool_setup
18 | String templateDBName = "test3";
19 |
20 | // 1. Replace the old database with an empty one.
21 | // The settings will be taken from the database settings
22 | // which were defined in the testdb-maven-plugin section of your pom.xml
23 | TestDatabaseProvider.dropCreatePostgreSQLDatabase(templateDBName);
24 |
25 | // 2. Now setup your tables using the flyway migration.
26 | ProviderConfig config = TestDatabaseProvider.config();
27 | String url = config.getPostgresql().jdbcUrl(templateDBName);
28 | String user = config.getPostgresql().getUsername();
29 | String password = config.getPostgresql().getPassword();
30 | Flyway flyway = Flyway.configure().dataSource(url, user, password).load();
31 | MigrateResult result = flyway.migrate();
32 |
33 | System.out.println(result.success ? "Flyway migration OK" : "Flyway migration Failed");
34 |
35 | // 3. Now recreate the dummy pool. The pool will provide the new databases for our tests.
36 | DatabasePoolResponse response = TestDatabaseProvider.createPool("dummy", templateDBName);
37 | System.out.println("\nPool Created: " + response.toString());
38 |
39 | // 5. Now run your unit tests and happy testing
40 | // SNIPPET END pool_setup
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/examples/minimal/src/test/resources/logback.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | %d{HH:mm:ss.SSS} [%thread] %-5level %logger - %msg%n
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/examples/pom.xml:
--------------------------------------------------------------------------------
1 |
4 | 4.0.0
5 |
6 | io.metaloom.test.example
7 | testdatabase-provider-examples
8 | 0.0.1-SNAPSHOT
9 |
10 |
11 | 42.2.2
12 | 1.17.6
13 | 0.1.4
14 | UTF-8
15 |
16 |
17 | Testdatabase Provider :: Examples
18 | pom
19 |
20 |
21 | minimal
22 | complex
23 | dedicated
24 | dedicated-no-maven-plugin
25 |
26 |
27 |
28 |
29 |
30 | org.postgresql
31 | postgresql
32 | ${postgres.driver.version}
33 |
34 |
35 | io.metaloom.test
36 | testdatabase-provider-junit5
37 | ${testdatabase-provider.version}
38 |
39 |
40 | io.metaloom.test
41 | testdatabase-provider-junit4
42 | ${testdatabase-provider.version}
43 |
44 |
45 | ch.qos.logback
46 | logback-classic
47 | 1.2.10
48 |
49 |
50 | org.flywaydb
51 | flyway-core
52 | 9.16.1
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 | io.metaloom.maven
62 | testdb-maven-plugin
63 | 0.1.4
64 |
65 |
66 | org.flywaydb
67 | flyway-maven-plugin
68 | 9.12.0
69 |
70 |
71 | org.apache.maven.plugins
72 | maven-compiler-plugin
73 | 3.11.0
74 |
75 | 17
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 | io.metaloom.maven
84 | testdb-maven-plugin
85 |
86 |
88 |
89 |
91 | false
92 | cleanup
93 |
94 | clean
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
--------------------------------------------------------------------------------
/junit4/pom.xml:
--------------------------------------------------------------------------------
1 |
4 | 4.0.0
5 | testdatabase-provider-junit4
6 |
7 |
8 | io.metaloom.test
9 | testdatabase-provider
10 | 0.1.4
11 |
12 |
13 | Testdatabase Provider :: JUnit 4
14 |
15 |
16 |
17 | io.metaloom.test
18 | testdatabase-provider-client
19 | ${project.version}
20 |
21 |
22 | junit
23 | junit
24 |
25 |
26 |
27 |
28 | io.metaloom.test
29 | testdatabase-provider-postgresql-db
30 | ${project.version}
31 | test
32 |
33 |
34 | io.metaloom.test
35 | testdatabase-provider-server
36 | ${project.version}
37 | test
38 |
39 |
40 | io.metaloom.test
41 | testdatabase-provider-server
42 | ${project.version}
43 | test-jar
44 | test
45 |
46 |
47 |
--------------------------------------------------------------------------------
/junit4/src/main/java/io/metaloom/test/provider/junit4/DatabaseProviderRule.java:
--------------------------------------------------------------------------------
1 | package io.metaloom.test.provider.junit4;
2 |
3 | import java.io.IOException;
4 |
5 | import org.junit.rules.TestRule;
6 | import org.junit.runner.Description;
7 | import org.junit.runners.model.Statement;
8 | import org.slf4j.Logger;
9 | import org.slf4j.LoggerFactory;
10 |
11 | import io.metaloom.test.container.provider.client.ClientAllocation;
12 | import io.metaloom.test.container.provider.client.ProviderClient;
13 | import io.metaloom.test.container.provider.client.TestDatabaseProvider;
14 | import io.metaloom.test.container.provider.model.DatabaseAllocationResponse;
15 |
16 | public class DatabaseProviderRule implements TestRule {
17 |
18 | public static final Logger log = LoggerFactory.getLogger(DatabaseProviderRule.class);
19 |
20 | private ProviderClient client;
21 | private ClientAllocation allocation;
22 | private String poolId;
23 |
24 | public DatabaseProviderRule(ProviderClient client, String poolId) {
25 | this.client = client;
26 | this.poolId = poolId;
27 | }
28 |
29 | public DatabaseProviderRule(String host, int port, String poolId) {
30 | this(new ProviderClient(host, port), poolId);
31 | }
32 |
33 | @Override
34 | public Statement apply(Statement base, Description description) {
35 | return new Statement() {
36 | @Override
37 | public void evaluate() throws Throwable {
38 | starting(description);
39 | try {
40 | base.evaluate();
41 | } finally {
42 | finished(description);
43 | }
44 | }
45 | };
46 | }
47 |
48 | protected void finished(Description description) {
49 | if (allocation != null) {
50 | allocation.release();
51 | allocation = null;
52 | }
53 | }
54 |
55 | protected void starting(Description description) {
56 | String testName = description.getMethodName();
57 | String testClass = description.getClassName();
58 | String testRef = testClass + "_" + testName;
59 | try {
60 | log.debug("Linking test {}. Requesting DB from {}", testRef, poolId);
61 | allocation = client.link(poolId, testRef).get();
62 | } catch (Exception e) {
63 | log.error("Error while linking test {}", testRef, e);
64 | throw new RuntimeException(e);
65 | }
66 | }
67 |
68 | public DatabaseAllocationResponse db() {
69 | return allocation == null ? null : allocation.response();
70 | }
71 |
72 | public static DatabaseProviderRule create(String host, int port, String poolId) {
73 | return new DatabaseProviderRule(host, port, poolId);
74 | }
75 |
76 | /**
77 | * Create a new extension which connects to the provider server.
78 | *
79 | * @param poolId
80 | * @return
81 | */
82 | public static DatabaseProviderRule create(String poolId) {
83 | try {
84 | ProviderClient client = TestDatabaseProvider.client();
85 | return new DatabaseProviderRule(client, poolId);
86 | } catch (IOException e) {
87 | throw new RuntimeException("Error while preparing client to connect to provider", e);
88 | }
89 | }
90 |
91 | }
92 |
--------------------------------------------------------------------------------
/junit4/src/test/java/io/metaloom/test/provider/junit4/DatabaseProviderRuleTest.java:
--------------------------------------------------------------------------------
1 | package io.metaloom.test.provider.junit4;
2 |
3 | import static org.junit.Assert.assertEquals;
4 |
5 | import org.junit.Rule;
6 | import org.junit.Test;
7 |
8 | import io.metaloom.test.container.server.AbstractProviderServerTest;
9 |
10 | public class DatabaseProviderRuleTest extends AbstractProviderServerTest {
11 |
12 | @Rule
13 | public DatabaseProviderRule provider = DatabaseProviderRule.create("default");
14 |
15 | @Test
16 | public void testA() {
17 | assertEquals("There should only be one allocation.", 1, server.getPool().allocationLevel());
18 | assertAllocation(server, provider.db(), "testA");
19 | }
20 |
21 | @Test
22 | public void testB() {
23 | assertEquals("There should only be one allocation.", 1, server.getPool().allocationLevel());
24 | assertAllocation(server, provider.db(), "testB");
25 | }
26 |
27 | }
28 |
--------------------------------------------------------------------------------
/junit4/src/test/java/io/metaloom/test/provider/junit4/PoolSetupAction.java:
--------------------------------------------------------------------------------
1 | package io.metaloom.test.provider.junit4;
2 |
3 | import io.metaloom.test.container.provider.client.TestDatabaseProvider;
4 | import io.metaloom.test.container.provider.common.config.ProviderConfig;
5 | import io.metaloom.test.container.server.TestSQLHelper;
6 |
7 | /**
8 | * Example implementation for a custom pool setup operation.
9 | */
10 | public class PoolSetupAction {
11 |
12 | public static void main(String[] args) throws Exception {
13 | // 1. Setup a new database - the settings will be taken from the database settings which were defined in the testprovider-plugin section of your pom.xml
14 | TestDatabaseProvider.createPostgreSQLDatabase("test2");
15 |
16 | // 3. Setup your tables (e.g. run flyway here)
17 | ProviderConfig config = TestDatabaseProvider.config();
18 | TestSQLHelper.setupTable(config.getPostgresql().adminJdbcUrl(), config.getPostgresql().getUsername(), config.getPostgresql().getPassword());
19 |
20 | // 4. Create pool to be used in tests
21 | TestDatabaseProvider.createPool("default", "test2");
22 |
23 | // 5. Now run your unit tests and happy testing
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/junit5/pom.xml:
--------------------------------------------------------------------------------
1 |
4 | 4.0.0
5 | testdatabase-provider-junit5
6 |
7 |
8 | io.metaloom.test
9 | testdatabase-provider
10 | 0.1.4
11 |
12 |
13 | Testdatabase Provider :: JUnit 5
14 |
15 |
16 |
17 | io.metaloom.test
18 | testdatabase-provider-client
19 | ${project.version}
20 |
21 |
22 | org.junit.jupiter
23 | junit-jupiter-api
24 |
25 |
26 |
27 |
28 | io.metaloom.test
29 | testdatabase-provider-postgresql-db
30 | ${project.version}
31 | test
32 |
33 |
34 | io.metaloom.test
35 | testdatabase-provider-server
36 | ${project.version}
37 | test
38 |
39 |
40 | io.metaloom.test
41 | testdatabase-provider-server
42 | ${project.version}
43 | test-jar
44 | test
45 |
46 |
47 |
--------------------------------------------------------------------------------
/junit5/src/main/java/io/metaloom/test/provider/junit5/ProviderExtension.java:
--------------------------------------------------------------------------------
1 | package io.metaloom.test.provider.junit5;
2 |
3 | import java.io.IOException;
4 |
5 | import org.junit.jupiter.api.extension.AfterEachCallback;
6 | import org.junit.jupiter.api.extension.BeforeEachCallback;
7 | import org.junit.jupiter.api.extension.ExtensionContext;
8 | import org.slf4j.Logger;
9 | import org.slf4j.LoggerFactory;
10 |
11 | import io.metaloom.test.container.provider.client.ClientAllocation;
12 | import io.metaloom.test.container.provider.client.ProviderClient;
13 | import io.metaloom.test.container.provider.client.TestDatabaseProvider;
14 | import io.metaloom.test.container.provider.model.DatabaseAllocationResponse;
15 |
16 | public class ProviderExtension implements BeforeEachCallback, AfterEachCallback {
17 |
18 | public static final Logger log = LoggerFactory.getLogger(ProviderExtension.class);
19 |
20 | private ProviderClient client;
21 | private ClientAllocation allocation;
22 | private String poolId;
23 |
24 | public ProviderExtension(ProviderClient client, String poolId) {
25 | this.client = client;
26 | this.poolId = poolId;
27 | }
28 |
29 | public ProviderExtension(String host, int port, String poolId) {
30 | this(new ProviderClient(host, port), poolId);
31 | }
32 |
33 | @Override
34 | public void beforeEach(ExtensionContext context) throws Exception {
35 | String testName = context.getRequiredTestMethod().getName();
36 | String testClass = context.getRequiredTestClass().getSimpleName();
37 | String testRef = testClass + "_" + testName;
38 | log.debug("Linking test {}. Requesting DB from {}", testRef, poolId);
39 | allocation = client.link(poolId, testRef).get();
40 | }
41 |
42 | @Override
43 | public void afterEach(ExtensionContext context) throws Exception {
44 | if (allocation != null) {
45 | allocation.release();
46 | }
47 | }
48 |
49 | public DatabaseAllocationResponse db() {
50 | return allocation == null ? null : allocation.response();
51 | }
52 |
53 | public static ProviderExtension create(String host, int port, String poolId) {
54 | return new ProviderExtension(host, port, poolId);
55 | }
56 |
57 | /**
58 | * Create a new extension which connects to the provider server.
59 | *
60 | * @param poolId
61 | * @return
62 | */
63 | public static ProviderExtension create(String poolId) {
64 | try {
65 | ProviderClient client = TestDatabaseProvider.client();
66 | return new ProviderExtension(client, poolId);
67 | } catch (IOException e) {
68 | throw new RuntimeException("Error while preparing client to connect to provider", e);
69 | }
70 | }
71 |
72 | }
73 |
--------------------------------------------------------------------------------
/junit5/src/test/java/io/metaloom/test/provider/junit5/DatabaseProviderExtensionTest.java:
--------------------------------------------------------------------------------
1 | package io.metaloom.test.provider.junit5;
2 |
3 | import static org.junit.Assert.assertEquals;
4 | import static org.junit.Assert.assertTrue;
5 |
6 | import org.junit.jupiter.api.Test;
7 | import org.junit.jupiter.api.extension.RegisterExtension;
8 |
9 | import io.metaloom.test.container.server.AbstractProviderServerTest;
10 |
11 | public class DatabaseProviderExtensionTest extends AbstractProviderServerTest {
12 |
13 | @RegisterExtension
14 | public static ProviderExtension ext = ProviderExtension.create("default");
15 |
16 | @Test
17 | public void testDB() throws Exception {
18 | Thread.sleep(2_000);
19 | assertEquals("There should only be one allocation.", 1, server.getPool().allocationLevel());
20 | assertTrue(server.getPool().isStarted());
21 | assertTrue(server.getPool().level() != 0);
22 | assertAllocation(server, ext.db(), getClass().getSimpleName() + "_testDB");
23 | }
24 |
25 | @Test
26 | public void testDB2() {
27 | assertEquals("There should only be one allocation.", 1, server.getPool().allocationLevel());
28 | assertAllocation(server, ext.db(), getClass().getSimpleName() + "_testDB2");
29 | }
30 |
31 | }
32 |
--------------------------------------------------------------------------------
/maven/pom.xml:
--------------------------------------------------------------------------------
1 |
4 | 4.0.0
5 | io.metaloom.maven
6 | testdb-maven-plugin
7 |
8 |
9 | io.metaloom.test
10 | testdatabase-provider
11 | 0.1.4
12 |
13 | maven-plugin
14 |
15 | A Maven Plugin to provide test databases via a dedicated provider
16 | server
17 | Testdatabase Provider :: Maven Plugin
18 |
19 |
20 | UTF-8
21 |
22 |
23 |
24 |
25 | io.metaloom.test
26 | testdatabase-provider-client
27 | ${project.version}
28 |
29 |
30 | io.metaloom.test
31 | testdatabase-provider-postgresql-db
32 | ${project.version}
33 |
34 |
35 |
36 |
37 | org.apache.maven
38 | maven-core
39 | 3.9.0
40 | provided
41 |
42 |
43 | org.apache.maven.plugin-tools
44 | maven-plugin-annotations
45 | 3.8.1
46 | provided
47 |
48 |
49 |
50 |
51 | org.junit.jupiter
52 | junit-jupiter-api
53 | test
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 | org.apache.maven.plugins
62 | maven-plugin-plugin
63 |
64 |
65 |
66 | generated-helpmojo
67 |
68 | helpmojo
69 |
70 |
71 |
72 |
73 | io.metaloom.maven.provider
74 |
75 |
76 |
77 | org.codehaus.mojo
78 | build-helper-maven-plugin
79 |
80 |
81 | add-source
82 | generate-sources
83 |
84 | add-source
85 |
86 |
87 |
88 | ${project.build.directory}/generated-sources/plugin
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 | org.apache.maven.plugins
101 | maven-plugin-report-plugin
102 |
103 |
104 | org.apache.maven.plugins
105 | maven-plugin-plugin
106 |
107 |
108 |
109 | report
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
--------------------------------------------------------------------------------
/maven/src/main/java/io/metaloom/maven/provider/PoolLimits.java:
--------------------------------------------------------------------------------
1 | package io.metaloom.maven.provider;
2 |
3 | import org.apache.maven.plugins.annotations.Parameter;
4 |
5 | import io.metaloom.test.container.provider.common.ServerEnv;
6 |
7 | public class PoolLimits {
8 |
9 | /**
10 | * Minimum level of test databases which the provider should allocate for the pool.
11 | */
12 | @Parameter
13 | private int minimum = ServerEnv.DEFAULT_POOL_MINIMUM;
14 |
15 | /**
16 | * Maximum level of test databases which the provider should allocate for the pool.
17 | */
18 | @Parameter
19 | private int maximum = ServerEnv.DEFAULT_POOL_MAXIMUM;
20 |
21 | /**
22 | * Incremental of new databases which the provider should create in one operation to increase the pool level.
23 | */
24 | @Parameter
25 | private int increment = ServerEnv.DEFAULT_POOL_INCREMENT;
26 |
27 | public int getMinimum() {
28 | return minimum;
29 | }
30 |
31 | public int getMaximum() {
32 | return maximum;
33 | }
34 |
35 | public int getIncrement() {
36 | return increment;
37 | }
38 |
39 | @Override
40 | public String toString() {
41 | return "limit: " + minimum + " to " + maximum + " inc: " + increment;
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/maven/src/main/java/io/metaloom/maven/provider/PoolMavenConfiguration.java:
--------------------------------------------------------------------------------
1 | package io.metaloom.maven.provider;
2 |
3 | import org.apache.maven.plugins.annotations.Parameter;
4 |
5 | public class PoolMavenConfiguration {
6 |
7 | /**
8 | * Id of the pool. Tests can reference this pool by id to get the desired databases.
9 | */
10 | @Parameter
11 | private String id;
12 |
13 | /**
14 | * Name of the database which should be copied in the pool.
15 | */
16 | @Parameter
17 | private String templateName;
18 |
19 | /**
20 | * Additional limits to tune the pool size.
21 | */
22 | @Parameter
23 | private PoolLimits limits = new PoolLimits();
24 |
25 | /**
26 | * Database host setting to be used by the pool which will be exposed to tests.
27 | */
28 | @Parameter
29 | private String host;
30 |
31 | /**
32 | * Database port setting to be used by the pool which will be exposed to tests.
33 | */
34 | @Parameter
35 | private Integer port;
36 |
37 | /**
38 | * Internal database host setting to be used by the pool for internal connections of the provider.
39 | */
40 | @Parameter
41 | private String internalHost;
42 |
43 | /**
44 | * Internal database port setting to be used by the pool for internal connections of the provider.
45 | */
46 | @Parameter
47 | private Integer internalPort;
48 |
49 | /**
50 | * Username for the database connection.
51 | */
52 | @Parameter
53 | private String username;
54 |
55 | /**
56 | * Password for the database connection.
57 | */
58 | @Parameter
59 | private String password;
60 |
61 | /**
62 | * Admin database to be used when exeuction drop database on no longer needed test databases.
63 | */
64 | @Parameter
65 | private String database;
66 |
67 | public String getId() {
68 | return id;
69 | }
70 |
71 | public String getTemplateName() {
72 | return templateName;
73 | }
74 |
75 | public PoolLimits getLimits() {
76 | return limits;
77 | }
78 |
79 | public String getHost() {
80 | return host;
81 | }
82 |
83 | public Integer getPort() {
84 | return port;
85 | }
86 |
87 | public String getDatabase() {
88 | return database;
89 | }
90 |
91 | public String getUsername() {
92 | return username;
93 | }
94 |
95 | public String getPassword() {
96 | return password;
97 | }
98 |
99 | public String getInternalHost() {
100 | return internalHost;
101 | }
102 |
103 | public Integer getInternalPort() {
104 | return internalPort;
105 | }
106 |
107 | @Override
108 | public String toString() {
109 | return "pool: " + getId() + " @ " + getHost() + ":" + getPort() + "/" + getDatabase() + " => " + getTemplateName() + "(Internal: "
110 | + getInternalHost() + ":" + getInternalPort() + "/" + getDatabase() + ")";
111 | }
112 |
113 | }
114 |
--------------------------------------------------------------------------------
/maven/src/main/java/io/metaloom/maven/provider/PostgresqlMavenConfiguration.java:
--------------------------------------------------------------------------------
1 | package io.metaloom.maven.provider;
2 |
3 | import org.apache.maven.plugins.annotations.Parameter;
4 |
5 | import io.metaloom.maven.provider.container.PostgreSQLPoolContainer;
6 |
7 | public class PostgresqlMavenConfiguration {
8 |
9 | public static final String POSTGRESQL_CONFIG_PROP_KEY = "maven.testdb.postgresql";
10 | public static final String POSTGRESQL_CONTAINER_ID_PROP_KEY = "maven.testdb.postgresql.container_id";
11 | public static final String POSTGRESQL_DB_PROP_KEY = "maven.testdb.postgresql.database";
12 | public static final String POSTGRESQL_USERNAME_PROP_KEY = "maven.testdb.postgresql.username";
13 | public static final String POSTGRESQL_PASSWORD_PROP_KEY = "maven.testdb.postgresql.password";
14 | public static final String POSTGRESQL_JDBCURL_PROP_KEY = "maven.testdb.postgresql.jdbcurl";
15 |
16 | public static final String POSTGRESQL_HOST_PROP_KEY = "maven.testdb.postgresql.host";
17 | public static final String POSTGRESQL_PORT_PROP_KEY = "maven.testdb.postgresql.port";
18 |
19 | public static final String POSTGRESQL_INTERNAL_HOST_PROP_KEY = "maven.testdb.postgresql.internal_host";
20 | public static final String POSTGRESQL_INTERNAL_PORT_PROP_KEY = "maven.testdb.postgresql.internal_port";
21 |
22 | public static final String POSTGRESQL_START_CONTAINER_PROP_KEY = "maven.testdb.postgresql.start_container";
23 | private static final String POSTGRESQL_CONTAINER_IMAGE_PROP_KEY = "maven.testdb.postgresql.container_image";
24 | private static final String POSTGRESQL_TMPFS_SIZE_PROP_KEY = "maven.testdb.postgresql.tmpfs_size_mb";
25 |
26 | /**
27 | * Whether a postgreSQL server should be started automatically. The properties maven.provider.db.url, maven.provider.db.username and
28 | * maven.provider.db.password will automatically be set and can be uses by other plugins after the execution of the plugin goal.
29 | */
30 | @Parameter(property = POSTGRESQL_START_CONTAINER_PROP_KEY, required = false, defaultValue = "true")
31 | private boolean startContainer = true;
32 |
33 | /**
34 | * Container image to be used to startup the postgreSQL.
35 | */
36 | @Parameter(property = POSTGRESQL_CONTAINER_IMAGE_PROP_KEY, required = false, defaultValue = PostgreSQLPoolContainer.DEFAULT_IMAGE)
37 | private String containerImage;
38 |
39 | /**
40 | * Host to be used for the provider to send to tests. This setting will not affect the started db container.
41 | */
42 | @Parameter(property = POSTGRESQL_HOST_PROP_KEY, required = false)
43 | private String host;
44 |
45 | /**
46 | * Port to be used for the provider to send to tests. This setting will not affect the started db container.
47 | */
48 | @Parameter(property = POSTGRESQL_PORT_PROP_KEY, required = false)
49 | private Integer port;
50 |
51 | /**
52 | * Internal host to be used for the provider to connect to the database. This setting will not affect the started db container.
53 | */
54 | @Parameter(property = POSTGRESQL_INTERNAL_HOST_PROP_KEY, required = false)
55 | private String internalHost;
56 |
57 | /**
58 | * Internal port to be used for the provider to connect to the database. This setting will not affect the started db container.
59 | */
60 | @Parameter(property = POSTGRESQL_INTERNAL_PORT_PROP_KEY, required = false)
61 | private Integer internalPort;
62 |
63 | /**
64 | * Username to be used for the provider to connect to the database. Additionally this username will be used when {@link #startContainer} is enabled.
65 | */
66 | @Parameter(property = POSTGRESQL_USERNAME_PROP_KEY, required = false, defaultValue = PostgreSQLPoolContainer.DEFAULT_USERNAME)
67 | private String username;
68 |
69 | /**
70 | * Password to be used for the provider to connect to the database. Additionally this password will be used when {@link #startContainer} is enabled.
71 | */
72 | @Parameter(property = POSTGRESQL_PASSWORD_PROP_KEY, required = false, defaultValue = PostgreSQLPoolContainer.DEFAULT_PASSWORD)
73 | private String password;
74 |
75 | /**
76 | * Name of the initial admin database being created when the container starts. This database will not be used to provide test databases.
77 | */
78 | @Parameter(property = POSTGRESQL_DB_PROP_KEY, required = false, defaultValue = PostgreSQLPoolContainer.DEFAULT_DATABASE_NAME)
79 | private String database;
80 |
81 | /**
82 | * Size in MB of the tmpfs filesystem to be used for the database container. Use 0 to disable tmpfs.
83 | */
84 | @Parameter(property = POSTGRESQL_TMPFS_SIZE_PROP_KEY, required = false, defaultValue = "0")
85 | private int tmpfsSizeMB = 0;
86 |
87 | public boolean isStartContainer() {
88 | return startContainer;
89 | }
90 |
91 | public String getContainerImage() {
92 | return containerImage;
93 | }
94 |
95 | public String getHost() {
96 | return host;
97 | }
98 |
99 | public Integer getPort() {
100 | return port;
101 | }
102 |
103 | public String getInternalHost() {
104 | return internalHost;
105 | }
106 |
107 | public Integer getInternalPort() {
108 | return internalPort;
109 | }
110 |
111 | public String getUsername() {
112 | return username;
113 | }
114 |
115 | public String getPassword() {
116 | return password;
117 | }
118 |
119 | public int getTmpfsSizeMB() {
120 | return tmpfsSizeMB;
121 | }
122 |
123 | public String getDatabase() {
124 | return database;
125 | }
126 |
127 | public boolean hasConnectionSettings() {
128 | return host != null && port != null && username != null && password != null && database != null;
129 | }
130 |
131 | public String getJdbcUrl() {
132 | return ("jdbc:postgresql://" + getHost() + ":" + getPort() + "/" + getDatabase());
133 | }
134 |
135 | }
136 |
--------------------------------------------------------------------------------
/maven/src/main/java/io/metaloom/maven/provider/ProviderCleanMojo.java:
--------------------------------------------------------------------------------
1 | package io.metaloom.maven.provider;
2 |
3 | import org.apache.maven.plugins.annotations.LifecyclePhase;
4 | import org.apache.maven.plugins.annotations.Mojo;
5 |
6 | /**
7 | * The stop all containers that were previously started and delete the test provider config file.
8 | */
9 | @Mojo(name = "clean", defaultPhase = LifecyclePhase.PRE_CLEAN)
10 | public class ProviderCleanMojo extends ProviderStopMojo {
11 | }
12 |
--------------------------------------------------------------------------------
/maven/src/main/java/io/metaloom/maven/provider/ProviderMavenConfiguration.java:
--------------------------------------------------------------------------------
1 | package io.metaloom.maven.provider;
2 |
3 | import org.apache.maven.plugins.annotations.Parameter;
4 |
5 | import io.metaloom.test.container.provider.container.DatabaseProviderContainer;
6 |
7 | public class ProviderMavenConfiguration {
8 |
9 | public static final String PROVIDER_CONFIG_PROP_KEY = "maven.testdb.provider";
10 | public static final String PROVIDER_HOST_PROP_KEY = "maven.testdb.provider.host";
11 | public static final String PROVIDER_PORT_PROP_KEY = "maven.testdb.provider.port";
12 | public static final String PROVIDER_LIMITS_PROP_KEY = "maven.testdb.provider.limits";
13 | public static final String PROVIDER_CREATE_POOL_PROP_KEY = "maven.testdb.createPool";
14 | public static final String PROVIDER_CONTAINER_IMAGE_PROP_KEY = "maven.testdb.provider.container_image";
15 | public static final String PROVIDER_START_CONTAINER_PROP_KEY = "maven.testdb.provider.start_container";
16 |
17 | /**
18 | * Default limits to be used for new pools.
19 | */
20 | @Parameter(property = PROVIDER_LIMITS_PROP_KEY)
21 | private PoolLimits limits = new PoolLimits();
22 |
23 | /**
24 | * Whether the test database provider should be started.
25 | */
26 | @Parameter(property = PROVIDER_START_CONTAINER_PROP_KEY, defaultValue = "true")
27 | private boolean startContainer = true;
28 |
29 | /**
30 | * Configure the used container image to be used when starting the provider container.
31 | */
32 | @Parameter(property = PROVIDER_CONTAINER_IMAGE_PROP_KEY)
33 | private String containerImage = DatabaseProviderContainer.DEFAULT_IMAGE;
34 |
35 | /**
36 | * Connection detail used to connect to the provider to manage it.
37 | */
38 | @Parameter(property = PROVIDER_HOST_PROP_KEY, defaultValue = "localhost")
39 | private String host;
40 |
41 | /**
42 | * Connection detail used to connect to the provider to manage it.
43 | */
44 | @Parameter(property = PROVIDER_PORT_PROP_KEY)
45 | private Integer port;
46 |
47 | /**
48 | * Whether to directly create a test database pool using the provided settings. Please note that the pool should be first created when the template database
49 | * is ready. You can create defer the pool creation using the "pool" goal.
50 | */
51 | @Parameter(property = PROVIDER_CREATE_POOL_PROP_KEY, defaultValue = "false")
52 | private boolean createPool = false;
53 |
54 | public String getHost() {
55 | return host;
56 | }
57 |
58 | public void setHost(String host) {
59 | this.host = host;
60 | }
61 |
62 | public Integer getPort() {
63 | return port;
64 | }
65 |
66 | public void setPort(Integer port) {
67 | this.port = port;
68 | }
69 |
70 | public String getContainerImage() {
71 | return containerImage;
72 | }
73 |
74 | public boolean isStartContainer() {
75 | return startContainer;
76 | }
77 |
78 | public PoolLimits getLimits() {
79 | return limits;
80 | }
81 |
82 | public boolean isCreatePool() {
83 | return createPool;
84 | }
85 |
86 | }
87 |
--------------------------------------------------------------------------------
/maven/src/main/java/io/metaloom/maven/provider/ProviderPoolMojo.java:
--------------------------------------------------------------------------------
1 | package io.metaloom.maven.provider;
2 |
3 | import static io.metaloom.test.container.provider.common.config.ProviderConfigHelper.readConfig;
4 |
5 | import java.util.List;
6 |
7 | import org.apache.maven.plugin.MojoExecutionException;
8 | import org.apache.maven.plugin.MojoFailureException;
9 | import org.apache.maven.plugins.annotations.LifecyclePhase;
10 | import org.apache.maven.plugins.annotations.Mojo;
11 | import org.apache.maven.plugins.annotations.Parameter;
12 |
13 | import io.metaloom.test.container.provider.client.JSON;
14 | import io.metaloom.test.container.provider.client.ProviderClient;
15 | import io.metaloom.test.container.provider.common.config.ProviderConfig;
16 | import io.metaloom.test.container.provider.common.config.ProviderConfigHelper;
17 | import io.metaloom.test.container.provider.model.DatabasePoolConnection;
18 | import io.metaloom.test.container.provider.model.DatabasePoolRequest;
19 | import io.metaloom.test.container.provider.model.DatabasePoolResponse;
20 | import io.metaloom.test.container.provider.model.DatabasePoolSettings;
21 |
22 | /**
23 | * The pool operation will setup a new test database pool. After this step the provider daemon will automatically populate the database with copies from the
24 | * template database and allow tests to allocate databases.
25 | */
26 | @Mojo(name = "pool", defaultPhase = LifecyclePhase.PROCESS_TEST_CLASSES)
27 | public class ProviderPoolMojo extends AbstractProviderMojo {
28 |
29 | /**
30 | * List of pool definitions which should be setup by the goal.
31 | */
32 | @Parameter(property = PROVIDER_POOLS_PROPS_KEY)
33 | private List pools;
34 |
35 | @Override
36 | public void execute() throws MojoExecutionException, MojoFailureException {
37 | if (skip) {
38 | getLog().info("Pool is skipped.");
39 | return;
40 | }
41 |
42 | try {
43 | ProviderConfig config = readConfig();
44 | if (config == null) {
45 | getLog().warn("Provider config file not found " + ProviderConfigHelper.currentConfigPath());
46 | updateProviderConfig(null, null);
47 | }
48 |
49 | getLog().info("Applying pool configuration");
50 | String host = merge("provider.host", providerMavenConfig.getHost(), config.getProviderHost());
51 | int port = config.getProviderPort();
52 |
53 | ProviderClient client = new ProviderClient(host, port);
54 | for (PoolMavenConfiguration pool : pools) {
55 | if (pool.getId() == null) {
56 | throw new MojoExecutionException("Pool id is missing for " + pool);
57 | }
58 |
59 | DatabasePoolSettings settings = new DatabasePoolSettings();
60 | PoolLimits limits = pool.getLimits();
61 | if (limits != null) {
62 | getLog().info("Using provided " + limits);
63 | settings.setMaximum(limits.getMaximum());
64 | settings.setMinimum(limits.getMinimum());
65 | settings.setIncrement(limits.getIncrement());
66 | }
67 | DatabasePoolConnection connection = new DatabasePoolConnection();
68 | connection.setHost(merge("postgresql.host", pool.getHost(), config.getPostgresql().getHost()));
69 | connection.setPort(merge("postgresql.port", pool.getPort(), config.getPostgresql().getPort()));
70 | connection.setUsername(merge("postgresql.username", pool.getUsername(), config.getPostgresql().getUsername()));
71 | connection.setPassword(merge("postgresql.password", pool.getPassword(), config.getPostgresql().getPassword()));
72 | connection.setDatabase(merge("postgresql.database", pool.getDatabase(), config.getPostgresql().getDatabaseName()));
73 | connection.setInternalHost(merge("postgresql.internalHost", pool.getInternalHost(), config.getPostgresql().getInternalHost()));
74 | connection.setInternalPort(merge("postgresql.internalPort", pool.getInternalPort(), config.getPostgresql().getInternalPort()));
75 | DatabasePoolRequest request = new DatabasePoolRequest();
76 | if (pool.getTemplateName() == null) {
77 | throw new MojoExecutionException("Database template name is missing in pool configuration for " + pool);
78 | }
79 | request.setTemplateDatabaseName(pool.getTemplateName());
80 |
81 | request.setSettings(settings);
82 | request.setConnection(connection);
83 | DatabasePoolResponse response = client.createPool(pool.getId(), request).get();
84 | getLog().debug("Response:\n" + JSON.toString(response));
85 | }
86 |
87 | } catch (Exception e) {
88 | throw new MojoExecutionException("Unexpected error while invoking pool setup.", e);
89 | }
90 | }
91 |
92 | /**
93 | * Merge the setting by first looking at the pool configuration. If no value can be found the state variable will be used.
94 | *
95 | * @param key
96 | * @param poolValue
97 | * @param stateValue
98 | * @return
99 | * @throws MojoExecutionException
100 | */
101 | private T merge(String key, T poolValue, T stateValue) throws MojoExecutionException {
102 | if (poolValue != null) {
103 | return poolValue;
104 | }
105 | if (stateValue != null) {
106 | getLog().debug("Using config value for " + key);
107 | return stateValue;
108 | }
109 |
110 | throw new MojoExecutionException(
111 | "Pool setting {" + key + "} is not present in pool configuration or global state file which was written by the start goal.");
112 | }
113 |
114 | }
115 |
--------------------------------------------------------------------------------
/maven/src/main/java/io/metaloom/maven/provider/ProviderStartMojo.java:
--------------------------------------------------------------------------------
1 | package io.metaloom.maven.provider;
2 |
3 | import static io.metaloom.test.container.provider.common.config.ProviderConfigHelper.readConfig;
4 |
5 | import org.apache.maven.plugin.MojoExecutionException;
6 | import org.apache.maven.plugin.MojoFailureException;
7 | import org.apache.maven.plugins.annotations.LifecyclePhase;
8 | import org.apache.maven.plugins.annotations.Mojo;
9 | import org.apache.maven.plugins.annotations.Parameter;
10 |
11 | import io.metaloom.maven.provider.container.PostgreSQLPoolContainer;
12 | import io.metaloom.test.container.provider.common.config.ProviderConfig;
13 | import io.metaloom.test.container.provider.container.DatabaseProviderContainer;
14 |
15 | /**
16 | * The start operation will provide the needed testdatabase provider daemon and optionally also a database which will automatically be configured to work in
17 | * conjunction with the started daemon.
18 | */
19 | @Mojo(name = "start", defaultPhase = LifecyclePhase.INITIALIZE)
20 | public class ProviderStartMojo extends AbstractProviderMojo {
21 |
22 | /**
23 | * Whether to re-use the started docker containers. If not enabled the container will be shut down when the maven command terminates. The settings
24 | * `testcontainers.reuse.enable=true` must be added to the .testcontainers.properties file in order to enable re-use of containers. A provider state file in
25 | * the build directory will keep track of containerIds and reuse them when goals are run again (if possible).
26 | */
27 | @Parameter(property = PROVIDER_REUSE_CONTAINERS_PROP_KEY, defaultValue = "true")
28 | private boolean reuseContainers = true;
29 |
30 | @Override
31 | public void execute() throws MojoExecutionException, MojoFailureException {
32 | if (skip) {
33 | getLog().info("Start is skipped.");
34 | return;
35 | }
36 |
37 | ProviderConfig providerConfig = readConfig();
38 | if (providerConfig != null) {
39 | getLog().warn(
40 | "Found config file. This means the provider is probably still running. You can stop containers via mvn testdatabase-provider:stop. Aborting start.");
41 | return;
42 | }
43 |
44 | PostgreSQLPoolContainer dbContainer = null;
45 | if (postgresqlMavenConfig != null) {
46 | if (postgresqlMavenConfig.isStartContainer()) {
47 | if (postgresqlMavenConfig.getPort() != null) {
48 | getLog().warn("Ignoring port setting. When starting a container the mapped port will be randomized.");
49 | }
50 | if (postgresqlMavenConfig.getHost() != null) {
51 | getLog().warn("Ignoring hostname setting. When starting a container the used host can't be selected.");
52 | }
53 | dbContainer = startPostgres(reuseContainers);
54 | } else {
55 | getLog().info("Not starting postgreSQL container");
56 | }
57 | } else {
58 | getLog().info("No postgreSQL settings found. Not starting database container.");
59 | }
60 |
61 | DatabaseProviderContainer providerContainer = null;
62 | if (providerMavenConfig != null && providerMavenConfig.isStartContainer()) {
63 | providerContainer = startProvider(reuseContainers, dbContainer);
64 | } else if (providerMavenConfig != null) {
65 | getLog().info(
66 | "Not starting testdatabase provider. Using " + providerMavenConfig.getHost() + ":" + providerMavenConfig.getPort() + " instead.");
67 | updateConfig(config -> {
68 | config.setProviderHost(providerMavenConfig.getHost());
69 | config.setProviderPort(providerMavenConfig.getPort());
70 | });
71 | } else {
72 | throw new MojoExecutionException("No provider config specified. Unable to execute goal without config.");
73 | }
74 | updateProviderConfig(providerContainer, dbContainer);
75 | }
76 |
77 | }
78 |
--------------------------------------------------------------------------------
/maven/src/main/java/io/metaloom/maven/provider/ProviderStopMojo.java:
--------------------------------------------------------------------------------
1 | package io.metaloom.maven.provider;
2 |
3 | import static io.metaloom.test.container.provider.common.config.ProviderConfigHelper.deleteConfig;
4 | import static io.metaloom.test.container.provider.common.config.ProviderConfigHelper.readConfig;
5 |
6 | import org.apache.maven.plugin.MojoExecutionException;
7 | import org.apache.maven.plugin.MojoFailureException;
8 | import org.apache.maven.plugins.annotations.LifecyclePhase;
9 | import org.apache.maven.plugins.annotations.Mojo;
10 | import org.testcontainers.DockerClientFactory;
11 |
12 | import com.github.dockerjava.api.DockerClient;
13 | import com.github.dockerjava.api.command.StopContainerCmd;
14 |
15 | import io.metaloom.test.container.provider.common.config.ProviderConfig;
16 | import io.metaloom.test.container.provider.common.config.ProviderConfigHelper;
17 |
18 | /**
19 | * The stop operation will terminate previously started databases and the testdatabase provider daemon container.
20 | */
21 | @Mojo(name = "stop", defaultPhase = LifecyclePhase.PREPARE_PACKAGE)
22 | public class ProviderStopMojo extends AbstractProviderMojo {
23 |
24 | @Override
25 | public void execute() throws MojoExecutionException, MojoFailureException {
26 |
27 | if (skip) {
28 | getLog().info("Stop is skipped.");
29 | return;
30 | }
31 |
32 | try {
33 | ProviderConfig config = readConfig();
34 | if (config == null) {
35 | getLog().warn("Unable to stop containers. Provider config file not found " + ProviderConfigHelper.currentConfigPath());
36 | return;
37 | }
38 | DockerClient client = DockerClientFactory.lazyClient();
39 | if (config.getProviderContainerId() != null) {
40 | stopProvider(client, config);
41 | }
42 | if (config.getPostgresql().getContainerId() != null) {
43 | stopDatabase(client, config);
44 | }
45 | deleteConfig();
46 | } catch (Exception e) {
47 | throw new MojoExecutionException("Error while stopping containers", e);
48 | }
49 | }
50 |
51 | private void stopDatabase(DockerClient client, ProviderConfig state) {
52 | try {
53 | getLog().info("Stopping postgreSQL container");
54 | try (StopContainerCmd cmd = client.stopContainerCmd(state.getPostgresql().getContainerId())) {
55 | cmd.exec();
56 | }
57 | } catch (Exception e) {
58 | getLog().error("Error while stopping database ", e);
59 | }
60 | }
61 |
62 | private void stopProvider(DockerClient client, ProviderConfig state) {
63 | try {
64 | getLog().info("Stopping database provider container");
65 | try (StopContainerCmd cmd = client.stopContainerCmd(state.getProviderContainerId())) {
66 | cmd.exec();
67 | }
68 | } catch (Exception e) {
69 | getLog().error("Error while stopping database provider", e);
70 | }
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/maven/src/main/java/io/metaloom/test/container/provider/container/DatabaseProviderContainer.java:
--------------------------------------------------------------------------------
1 | package io.metaloom.test.container.provider.container;
2 |
3 | import org.testcontainers.containers.GenericContainer;
4 | import org.testcontainers.utility.DockerImageName;
5 |
6 | import io.metaloom.test.container.provider.common.ServerEnv;
7 | import io.metaloom.test.container.provider.common.version.Version;
8 |
9 | /**
10 | * Provider testcontainer which can be used for tests.
11 | */
12 | public class DatabaseProviderContainer extends GenericContainer {
13 |
14 | public static final String DEFAULT_IMAGE = "metaloom/testdatabase-provider:" + Version.getPlainVersion();
15 |
16 | public DatabaseProviderContainer() {
17 | this(DEFAULT_IMAGE);
18 | }
19 |
20 | public DatabaseProviderContainer(String imageName) {
21 | super(DockerImageName.parse(imageName));
22 | withExposedPorts(8080);
23 | }
24 |
25 | public int getPort() {
26 | return getFirstMappedPort();
27 | }
28 |
29 | /**
30 | * Set the parameters that are needed to directly setup a testdatabase pool for the provided database.
31 | *
32 | * @param host
33 | * Host which will be exposed to tests
34 | * @param port
35 | * Port which will be exposed to tests
36 | * @param internalHost
37 | * Host which will only be used by the provider to handle allocation and cleanup
38 | * @param internalPort
39 | * Port which will only be used by the provider to handle allocation and cleanup
40 | * @param username
41 | * @param password
42 | * @param database
43 | * @return
44 | */
45 | public DatabaseProviderContainer withDefaultPoolDatabase(String host, int port, String internalHost, int internalPort, String username,
46 | String password, String database) {
47 | withEnv(ServerEnv.TESTDATABASE_PROVIDER_DATABASE_HOST_KEY, host);
48 | withEnv(ServerEnv.TESTDATABASE_PROVIDER_DATABASE_PORT_KEY, String.valueOf(port));
49 | withEnv(ServerEnv.TESTDATABASE_PROVIDER_DATABASE_INTERNAL_HOST_KEY, internalHost);
50 | withEnv(ServerEnv.TESTDATABASE_PROVIDER_DATABASE_INTERNAL_PORT_KEY, String.valueOf(internalPort));
51 | withEnv(ServerEnv.TESTDATABASE_PROVIDER_DATABASE_USERNAME_KEY, username);
52 | withEnv(ServerEnv.TESTDATABASE_PROVIDER_DATABASE_PASSWORD_KEY, password);
53 | withEnv(ServerEnv.TESTDATABASE_PROVIDER_DATABASE_DBNAME_KEY, database);
54 | return this;
55 | }
56 |
57 | public DatabaseProviderContainer withDefaultMinimum(int minimumDatabases) {
58 | withEnv(ServerEnv.TESTDATABASE_PROVIDER_POOL_MINIMUM_KEY, String.valueOf(minimumDatabases));
59 | return this;
60 | }
61 |
62 | public DatabaseProviderContainer withDefaultMaximum(int maximumDatabases) {
63 | withEnv(ServerEnv.TESTDATABASE_PROVIDER_POOL_MAXIMUM_KEY, String.valueOf(maximumDatabases));
64 | return this;
65 | }
66 |
67 | public DatabaseProviderContainer withDefaultIncrement(int increment) {
68 | withEnv(ServerEnv.TESTDATABASE_PROVIDER_POOL_INCREMENT_KEY, String.valueOf(increment));
69 | return this;
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/maven/src/test/java/io/metaloom/maven/provider/ProviderStartMojoTest.java:
--------------------------------------------------------------------------------
1 | package io.metaloom.maven.provider;
2 |
3 | import static org.junit.jupiter.api.Assertions.assertNotNull;
4 |
5 | import org.junit.jupiter.api.Test;
6 |
7 | import io.metaloom.test.container.provider.client.ProviderClient;
8 | import io.metaloom.test.container.provider.common.config.ProviderConfig;
9 | import io.metaloom.test.container.provider.common.config.ProviderConfigHelper;
10 | import io.metaloom.test.container.provider.model.DatabasePoolListResponse;
11 |
12 | public class ProviderStartMojoTest {
13 |
14 | @Test
15 | public void testStart() throws Exception {
16 | new ProviderStartMojo().execute();
17 | ProviderConfig config = ProviderConfigHelper.readConfig();
18 | assertNotNull(config);
19 | Thread.sleep(2000);
20 | System.out.println(config.toString());
21 | ProviderClient client = new ProviderClient(config.getProviderHost(), config.getProviderPort());
22 | DatabasePoolListResponse response = client.listPools().get();
23 | assertNotNull(response);
24 | assertNotNull(response.getList());
25 | new ProviderStopMojo().execute();
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
4 | 4.0.0
5 | io.metaloom.test
6 | testdatabase-provider
7 | 0.1.4
8 |
9 | pom
10 |
11 | io.metaloom
12 | maven-parent
13 | 2.0.2
14 |
15 |
16 | Testdatabase Provider
17 | 2023
18 |
19 |
20 |
21 | Apache-2.0
22 |
23 |
24 |
25 |
26 | Metaloom
27 | https://metaloom.io/
28 |
29 |
30 |
31 |
32 | jotschi
33 | Johannes Schüth
34 |
35 | Developer
36 |
37 |
38 |
39 |
40 |
41 | 4.4.0
42 | 42.2.2
43 | 1.17.6
44 | ${maven.build.timestamp}
45 | 2.45
46 | UTF-8
47 |
48 |
49 |
50 | common
51 | postgresql-db
52 | client
53 | junit4
54 | junit5
55 | server
56 | maven
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 | io.vertx
65 | vertx-core
66 | ${vertx.version}
67 |
68 |
69 | io.vertx
70 | vertx-web
71 | ${vertx.version}
72 |
73 |
74 | org.postgresql
75 | postgresql
76 | ${postgres.driver.version}
77 |
78 |
79 |
80 |
81 | org.testcontainers
82 | junit-jupiter
83 | ${testcontainer.version}
84 |
85 |
86 | org.testcontainers
87 | postgresql
88 | ${testcontainer.version}
89 |
90 |
91 | org.testcontainers
92 | testcontainers
93 | ${testcontainer.version}
94 |
95 |
96 |
97 |
98 | org.junit.jupiter
99 | junit-jupiter-api
100 | 5.9.2
101 |
102 |
103 |
104 |
105 | com.google.dagger
106 | dagger
107 | ${dagger.version}
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 | org.apache.maven.plugins
117 | maven-plugin-report-plugin
118 | 3.8.1
119 |
120 |
121 | org.apache.maven.plugins
122 | maven-plugin-plugin
123 | 3.8.1
124 |
125 |
126 | org.apache.maven.plugins
127 | maven-site-plugin
128 | 3.8.2
129 |
130 |
131 | io.fabric8
132 | docker-maven-plugin
133 | 0.42.0
134 |
135 |
136 |
137 |
138 |
139 | org.apache.maven.plugins
140 | maven-resources-plugin
141 | 3.3.1
142 | false
143 |
144 |
145 | readme-md
146 | clean
147 |
148 | copy-resources
149 |
150 |
151 | ${project.basedir}
152 |
153 |
154 | ${project.basedir}/.github/md
155 |
156 | README.md
157 |
158 | true
159 |
160 |
161 | UTF-8
162 |
163 | snippetFilter
164 |
165 |
166 |
167 |
168 |
169 |
170 | io.metaloom.maven
171 | snippet-resource-filter
172 | 0.1.1
173 |
174 |
175 |
176 |
177 | org.apache.maven.plugins
178 | maven-compiler-plugin
179 |
180 | 17
181 |
182 |
183 |
184 |
185 |
--------------------------------------------------------------------------------
/postgresql-db/pom.xml:
--------------------------------------------------------------------------------
1 |
4 | 4.0.0
5 | testdatabase-provider-postgresql-db
6 |
7 |
8 | io.metaloom.test
9 | testdatabase-provider
10 | 0.1.4
11 |
12 |
13 | Testdatabase Provider :: PostgreSQL DB
14 |
15 |
16 |
17 | org.testcontainers
18 | postgresql
19 |
20 |
21 |
22 |
23 | org.testcontainers
24 | junit-jupiter
25 | test
26 |
27 |
28 | org.junit.jupiter
29 | junit-jupiter-api
30 | test
31 |
32 |
33 |
--------------------------------------------------------------------------------
/postgresql-db/src/main/java/io/metaloom/maven/provider/container/PostgreSQLPoolContainer.java:
--------------------------------------------------------------------------------
1 | package io.metaloom.maven.provider.container;
2 |
3 | import java.util.HashMap;
4 | import java.util.Map;
5 |
6 | import org.testcontainers.containers.PostgreSQLContainer;
7 | import org.testcontainers.utility.DockerImageName;
8 |
9 | public class PostgreSQLPoolContainer extends PostgreSQLContainer {
10 |
11 | public static final String DEFAULT_IMAGE = "postgres:13.2";
12 |
13 | public static final String DEFAULT_USERNAME = "sa";
14 |
15 | public static final String DEFAULT_PASSWORD = "sa";
16 |
17 | public static final String DEFAULT_DATABASE_NAME = "postgres";
18 |
19 | public PostgreSQLPoolContainer() {
20 | this(DEFAULT_IMAGE);
21 | }
22 |
23 | public PostgreSQLPoolContainer(String imageName) {
24 | super(DockerImageName.parse(imageName).asCompatibleSubstituteFor(DEFAULT_IMAGE));
25 | withDatabaseName(DEFAULT_DATABASE_NAME);
26 | withUsername(DEFAULT_USERNAME);
27 | withPassword(DEFAULT_PASSWORD);
28 | }
29 |
30 | /**
31 | * Enable the usage of a tmpFs to store the actual database data.
32 | *
33 | * @param liveTmpFsSizeInMB
34 | * @return
35 | */
36 | public PostgreSQLPoolContainer withTmpFs(int liveTmpFsSizeInMB) {
37 | if (liveTmpFsSizeInMB != 0) {
38 | withEnv("PGDATA", "/live/pgdata");
39 | withTmpFs(tmpFs(liveTmpFsSizeInMB));
40 | }
41 | return this;
42 | }
43 |
44 | private Map tmpFs(int liveSizeMB) {
45 | Map mapping = new HashMap<>();
46 | mapping.put("/live", "rw,size=" + liveSizeMB + "m");
47 | return mapping;
48 | }
49 |
50 | public String getShortJdbcUrl() {
51 | return ("jdbc:postgresql://" +
52 | getHost() +
53 | ":" +
54 | getMappedPort(POSTGRESQL_PORT) +
55 | "/");
56 | }
57 |
58 | public int getPort() {
59 | return getFirstMappedPort();
60 | }
61 |
62 | }
63 |
--------------------------------------------------------------------------------
/postgresql-db/src/test/java/io/metaloom/maven/provider/container/PostgreSQLPoolContainerTest.java:
--------------------------------------------------------------------------------
1 | package io.metaloom.maven.provider.container;
2 |
3 | import static org.junit.Assert.assertNotNull;
4 |
5 | import org.junit.jupiter.api.Test;
6 | import org.testcontainers.junit.jupiter.Container;
7 | import org.testcontainers.junit.jupiter.Testcontainers;
8 |
9 | @Testcontainers
10 | public class PostgreSQLPoolContainerTest {
11 |
12 | @Container
13 | private static PostgreSQLPoolContainer container = new PostgreSQLPoolContainer().withTmpFs(128);
14 |
15 | @Test
16 | public void testSetup() {
17 | assertNotNull(container.getContainerIpAddress());
18 | assertNotNull(container.getHost());
19 | assertNotNull(container.getJdbcUrl());
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/server/Dockerfile:
--------------------------------------------------------------------------------
1 | # syntax=docker/dockerfile:1
2 | FROM eclipse-temurin:19-jre
3 |
4 | COPY maven/ /opt
5 | RUN mv /opt/*.jar /opt/server.jar
6 |
7 | EXPOSE 8080/tcp
8 |
9 | CMD ["java", "-jar", "/opt/server.jar"]
10 |
--------------------------------------------------------------------------------
/server/pom.xml:
--------------------------------------------------------------------------------
1 |
4 | 4.0.0
5 | testdatabase-provider-server
6 |
7 |
8 | io.metaloom.test
9 | testdatabase-provider
10 | 0.1.4
11 |
12 |
13 | Testdatabase Provider :: Server
14 |
15 |
16 |
17 | io.metaloom.test
18 | testdatabase-provider-common
19 | ${project.version}
20 |
21 |
22 | io.vertx
23 | vertx-web
24 |
25 |
26 | org.postgresql
27 | postgresql
28 |
29 |
30 | ch.qos.logback
31 | logback-classic
32 |
33 |
34 | org.slf4j
35 | slf4j-api
36 |
37 |
38 | com.google.dagger
39 | dagger
40 |
41 |
42 |
43 |
44 | io.metaloom.test
45 | testdatabase-provider-postgresql-db
46 | ${project.version}
47 | test
48 |
49 |
50 | io.metaloom.test
51 | testdatabase-provider-client
52 | ${project.version}
53 | test
54 |
55 |
56 | org.junit.jupiter
57 | junit-jupiter-api
58 | test
59 |
60 |
61 | org.testcontainers
62 | junit-jupiter
63 | test
64 |
65 |
66 |
67 |
68 |
69 |
70 | org.apache.maven.plugins
71 | maven-jar-plugin
72 |
73 |
74 |
75 | test-jar
76 |
77 |
78 |
79 |
80 |
81 | org.apache.maven.plugins
82 | maven-compiler-plugin
83 |
84 |
85 |
86 | com.google.dagger
87 | dagger-compiler
88 | ${dagger.version}
89 |
90 |
91 |
92 |
93 |
94 | org.apache.maven.plugins
95 | maven-shade-plugin
96 |
97 |
98 | package
99 |
100 | shade
101 |
102 |
103 |
104 |
106 |
107 | io.metaloom.test.container.provider.server.DatabaseProviderServerRunner
108 |
109 |
111 |
112 |
113 |
114 | *:*
115 |
116 | META-INF/*.SF
117 | META-INF/*.DSA
118 | META-INF/*.RSA
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 | io.fabric8
130 | docker-maven-plugin
131 |
132 |
133 | build-image
134 | package
135 |
136 | build
137 |
138 |
139 |
140 | ${project.version}
141 |
142 | true
143 |
144 |
145 |
146 | push-image
147 | deploy
148 |
149 | push
150 |
151 |
152 |
153 |
154 |
155 |
156 | metaloom/testdatabase-provider
157 |
158 | ${project.basedir}
159 |
160 | artifact
161 |
162 |
163 | ${project.version}
164 |
165 |
166 |
167 |
168 |
169 |
170 |
171 |
172 |
--------------------------------------------------------------------------------
/server/src/main/java/io/metaloom/test/container/provider/BootstrapInitializer.java:
--------------------------------------------------------------------------------
1 | package io.metaloom.test.container.provider;
2 |
3 | import java.sql.SQLException;
4 |
5 | import javax.inject.Inject;
6 | import javax.inject.Singleton;
7 |
8 | import org.slf4j.Logger;
9 | import org.slf4j.LoggerFactory;
10 |
11 | import io.metaloom.test.container.provider.common.ServerEnv;
12 | import io.metaloom.test.container.provider.server.DatabaseProviderServer;
13 | import io.metaloom.test.container.provider.server.ServerConfiguration;
14 |
15 | @Singleton
16 | public class BootstrapInitializer {
17 |
18 | private static final Logger log = LoggerFactory.getLogger(BootstrapInitializer.class);
19 |
20 | private final DatabasePoolManager manager;
21 |
22 | private final ServerConfiguration config;
23 |
24 | private final DatabaseProviderServer server;
25 |
26 | @Inject
27 | public BootstrapInitializer(DatabasePoolManager manager, ServerConfiguration config, DatabaseProviderServer server) {
28 | this.manager = manager;
29 | this.config = config;
30 | this.server = server;
31 | }
32 |
33 | public void start() {
34 | if (config.hasConnectionDetails()) {
35 | log.info("Searching for previously created pools and databases");
36 | try {
37 | manager.loadFromDB();
38 | } catch (SQLException e) {
39 | log.error("Error while loading databases", e);
40 | }
41 | } else {
42 | log.info("Skipping loading of existing databases because not all connection details have been provided.");
43 | }
44 |
45 | log.info("Starting server using port {}", config.httpPort());
46 | server.start();
47 |
48 | String templateDatabaseName = config.templateDatabaseName();
49 | if (templateDatabaseName != null) {
50 | log.info("Creating default pool for database " + config.host() + ":" + config.port() + "/" + templateDatabaseName + " using admin db "
51 | + config.adminDB());
52 | DatabasePool pool = manager.createPool("default", config.host(), config.port(), config.host(), config.port(), config.username(),
53 | config.password(), config.adminDB(), templateDatabaseName);
54 | pool.setTemplateDatabaseName(templateDatabaseName);
55 | pool.start();
56 | } else {
57 | log.debug("Skipping creation of default pool since no template db was specified via "
58 | + ServerEnv.TESTDATABASE_PROVIDER_DATABASE_TEMPLATE_DBNAME_KEY);
59 | }
60 |
61 | }
62 |
63 | public DatabaseProviderServer server() {
64 | return server;
65 | }
66 |
67 | }
68 |
--------------------------------------------------------------------------------
/server/src/main/java/io/metaloom/test/container/provider/Database.java:
--------------------------------------------------------------------------------
1 | package io.metaloom.test.container.provider;
2 |
3 | public record Database(DatabaseSettings settings, String name, DatabaseJsonCommentModel comment) {
4 |
5 | public String jdbcUrl() {
6 | return settings.jdbcUrl() + name;
7 | }
8 |
9 | }
--------------------------------------------------------------------------------
/server/src/main/java/io/metaloom/test/container/provider/DatabaseAllocation.java:
--------------------------------------------------------------------------------
1 | package io.metaloom.test.container.provider;
2 |
3 | import java.sql.SQLException;
4 |
5 | public class DatabaseAllocation {
6 |
7 | private final DatabasePool pool;
8 | private final String id;
9 | private final Database db;
10 |
11 | public DatabaseAllocation(DatabasePool pool, String id, Database db) {
12 | this.pool = pool;
13 | this.id = id;
14 | this.db = db;
15 | }
16 |
17 | public String id() {
18 | return id;
19 | }
20 |
21 | public DatabasePool getPool() {
22 | return pool;
23 | }
24 |
25 | public Database db() {
26 | return db;
27 | }
28 |
29 | public void release() throws SQLException {
30 | getPool().release(this);
31 | }
32 |
33 | @Override
34 | public String toString() {
35 | return "allocation: " + getPool().id() + " / " + id();
36 | }
37 |
38 | }
39 |
--------------------------------------------------------------------------------
/server/src/main/java/io/metaloom/test/container/provider/DatabaseJsonCommentModel.java:
--------------------------------------------------------------------------------
1 | package io.metaloom.test.container.provider;
2 |
3 | import java.time.LocalDateTime;
4 |
5 | import com.fasterxml.jackson.annotation.JsonFormat;
6 | import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
7 | import com.fasterxml.jackson.databind.annotation.JsonSerialize;
8 | import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
9 | import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
10 |
11 | public class DatabaseJsonCommentModel {
12 |
13 | private String origin;
14 |
15 | private String poolId;
16 |
17 | @JsonDeserialize(using = LocalDateTimeDeserializer.class)
18 | @JsonSerialize(using = LocalDateTimeSerializer.class)
19 | @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss")
20 | private LocalDateTime creationDate;
21 |
22 | public String getOrigin() {
23 | return origin;
24 | }
25 |
26 | public DatabaseJsonCommentModel setOrigin(String templateName) {
27 | this.origin = templateName;
28 | return this;
29 | }
30 |
31 | public String getPoolId() {
32 | return poolId;
33 | }
34 |
35 | public DatabaseJsonCommentModel setPoolId(String id) {
36 | this.poolId = id;
37 | return this;
38 | }
39 |
40 | public LocalDateTime getCreationDate() {
41 | return creationDate;
42 | }
43 |
44 | public DatabaseJsonCommentModel setCreationDate(LocalDateTime creationDate) {
45 | this.creationDate = creationDate;
46 | return this;
47 | }
48 |
49 | }
50 |
--------------------------------------------------------------------------------
/server/src/main/java/io/metaloom/test/container/provider/DatabasePool.java:
--------------------------------------------------------------------------------
1 | package io.metaloom.test.container.provider;
2 |
3 | import java.sql.SQLException;
4 | import java.time.LocalDateTime;
5 | import java.util.HashMap;
6 | import java.util.Map;
7 | import java.util.Stack;
8 | import java.util.UUID;
9 |
10 | import org.slf4j.Logger;
11 | import org.slf4j.LoggerFactory;
12 |
13 | import io.metaloom.test.container.provider.common.ServerEnv;
14 | import io.metaloom.test.container.provider.server.ServerConfiguration;
15 | import io.metaloom.test.container.provider.server.ServerError;
16 | import io.vertx.core.Vertx;
17 |
18 | /**
19 | * A database pool manages the database copies for a specific template database. The pool fulfills allocation requests and creates new databases to be
20 | * allocated.
21 | */
22 | public class DatabasePool {
23 |
24 | public static final Logger log = LoggerFactory.getLogger(DatabasePool.class);
25 |
26 | private static final String DB_PREFIX = "test_db_";
27 |
28 | private Stack databases = new Stack<>();
29 |
30 | private Map allocations = new HashMap<>();
31 |
32 | private final String id;
33 |
34 | private LocalDateTime creationDate;
35 |
36 | private int minimum = ServerEnv.DEFAULT_POOL_MINIMUM;
37 |
38 | private int maximum = ServerEnv.DEFAULT_POOL_MAXIMUM;
39 |
40 | private int increment = ServerEnv.DEFAULT_POOL_INCREMENT;
41 |
42 | private final Vertx vertx;
43 |
44 | private final ServerConfiguration config;
45 |
46 | private Long maintainPoolTimerId;
47 |
48 | private String templateName;
49 |
50 | private DatabaseSettings settings;
51 |
52 | /**
53 | * Create a new database pool with the specified levels.
54 | *
55 | * @param vertx
56 | * @param config
57 | * @param id
58 | * @param host
59 | * @param port
60 | * @param internalHost
61 | * @param internalPort
62 | * @param username
63 | * @param password
64 | * @param adminDB
65 | */
66 | protected DatabasePool(Vertx vertx, ServerConfiguration config, String id, String host, int port, String internalHost, int internalPort,
67 | String username, String password,
68 | String adminDB) {
69 | this.vertx = vertx;
70 | this.config = config;
71 | this.id = id;
72 | this.settings = new DatabaseSettings(host, port, internalHost, internalPort, username, password, adminDB);
73 | this.creationDate = LocalDateTime.now();
74 | }
75 |
76 | public void start() {
77 | if (isStarted()) {
78 | log.error("Pool already started. Ignoring start request.");
79 | return;
80 | }
81 | // TODO configure interval?
82 | this.maintainPoolTimerId = vertx.setPeriodic(2_000, th -> {
83 | try {
84 | preAllocate();
85 | } catch (SQLException e) {
86 | log.error("Error while preallocating database.", e);
87 | }
88 | });
89 | }
90 |
91 | public void stop() {
92 | if (maintainPoolTimerId != null) {
93 | log.info("Stopping pre-allocation process");
94 | vertx.cancelTimer(maintainPoolTimerId);
95 | maintainPoolTimerId = null;
96 | }
97 | }
98 |
99 | public DatabaseAllocation allocate(String testName) throws SQLException {
100 | if (databases.isEmpty()) {
101 | preAllocate();
102 | }
103 | if (!databases.isEmpty()) {
104 | Database database = databases.pop();
105 | String id = UUID.randomUUID()
106 | .toString()
107 | .substring(0, 4) + "#" + testName;
108 | DatabaseAllocation allocation = new DatabaseAllocation(this, id, database);
109 | allocations.put(id, allocation);
110 | return allocation;
111 | }
112 | return null;
113 | }
114 |
115 | public void preAllocate() throws SQLException {
116 | int size = level();
117 | if (templateName == null) {
118 | log.warn("Unable to preallocate database. No template name set.");
119 | return;
120 | }
121 | if (size < minimum && !(size > maximum) && templateName != null) {
122 | log.info("Need more databases. Got {} but need {} / {}", size, minimum, maximum);
123 | for (int i = 0; i < increment; i++) {
124 | String newName = DB_PREFIX + UUID.randomUUID()
125 | .toString()
126 | .substring(0, 4);
127 | log.debug("Creating " + newName + " from " + templateName);
128 | DatabaseJsonCommentModel comment = new DatabaseJsonCommentModel();
129 | comment.setOrigin(templateName);
130 | comment.setPoolId(id());
131 | comment.setCreationDate(LocalDateTime.now());
132 | Database database = SQLUtils.copyDatabase(settings, templateName, newName, comment);
133 | databases.push(database);
134 | }
135 | }
136 | }
137 |
138 | public boolean release(DatabaseAllocation allocation) throws SQLException {
139 | String id = allocation.id();
140 | log.info("Releasing allocation {}", id);
141 | if (allocations.remove(id) == null) {
142 | log.error("Allocation {} not found in pool {}", allocation, id());
143 | return false;
144 | } else {
145 | SQLUtils.dropDatabase(allocation.db());
146 | return true;
147 | }
148 | }
149 |
150 | public String id() {
151 | return id;
152 | }
153 |
154 | public LocalDateTime getCreationDate() {
155 | return creationDate;
156 | }
157 |
158 | /**
159 | * Return the count of database that the pool is ready to provide.
160 | *
161 | * @return
162 | */
163 | public int level() {
164 | return databases.size();
165 | }
166 |
167 | public int allocationLevel() {
168 | return allocations.size();
169 | }
170 |
171 | public String getTemplateName() {
172 | return templateName;
173 | }
174 |
175 | public DatabasePool setTemplateDatabaseName(String databaseName) {
176 | this.templateName = databaseName;
177 | return this;
178 | }
179 |
180 | public DatabaseSettings settings() {
181 | return settings;
182 | }
183 |
184 | public boolean isStarted() {
185 | return maintainPoolTimerId != null;
186 | }
187 |
188 | public int getIncrement() {
189 | return increment;
190 | }
191 |
192 | public int getMaximum() {
193 | return maximum;
194 | }
195 |
196 | public int getMinimum() {
197 | return minimum;
198 | }
199 |
200 | /**
201 | * Removes all databases that are managed by the pool (free and allocated). Does not touch the template database.
202 | */
203 | public void drain() {
204 | if (isStarted()) {
205 | throw new ServerError("Can't drain a running pool. Please ensure that the pool is stopped first.");
206 | }
207 | log.info("Draining allocations from pool {}", id());
208 | for (DatabaseAllocation allocation : allocations.values()) {
209 | try {
210 | log.info("Releasing allocation db {} from pool {}", allocation.db(), id());
211 | allocation.release();
212 | } catch (Exception e) {
213 | log.error("Error while dropping allocated db.", e);
214 | }
215 | }
216 | log.info("Draining free databases from pool {}", id());
217 | for (Database db : databases) {
218 | try {
219 | SQLUtils.dropDatabase(db);
220 | } catch (Exception e) {
221 | log.error("Error while dropping free db.", e);
222 | }
223 | }
224 | }
225 |
226 | public void setMinimum(int minimum) {
227 | this.minimum = minimum;
228 | }
229 |
230 | public void setMaximum(int maximum) {
231 | this.maximum = maximum;
232 | }
233 |
234 | public void setIncrement(int increment) {
235 | this.increment = increment;
236 | }
237 |
238 | public boolean hasDatabase(String databaseName) {
239 | for (Database database : databases) {
240 | if (database.name().equals(databaseName)) {
241 | log.debug("Found database {} in pool {}" + databaseName, id());
242 | return true;
243 | }
244 | }
245 | return false;
246 | }
247 |
248 | public void addDatabase(Database db) {
249 | databases.add(db);
250 | }
251 |
252 | }
253 |
--------------------------------------------------------------------------------
/server/src/main/java/io/metaloom/test/container/provider/DatabasePoolFactory.java:
--------------------------------------------------------------------------------
1 | package io.metaloom.test.container.provider;
2 |
3 | import javax.inject.Inject;
4 | import javax.inject.Singleton;
5 |
6 | import io.metaloom.test.container.provider.server.ServerConfiguration;
7 | import io.vertx.core.Vertx;
8 |
9 | @Singleton
10 | public class DatabasePoolFactory {
11 |
12 | private Vertx vertx;
13 |
14 | private ServerConfiguration config;
15 |
16 | @Inject
17 | public DatabasePoolFactory(Vertx vertx, ServerConfiguration config) {
18 | this.vertx = vertx;
19 | this.config = config;
20 | }
21 |
22 | public DatabasePool createPool(String id, String host, int port, String internalHost, int internalPort,
23 | String username, String password,
24 | String adminDB) {
25 | return new DatabasePool(vertx, config, id, host, port, internalHost, internalPort, username, password, adminDB);
26 | }
27 |
28 | }
29 |
--------------------------------------------------------------------------------
/server/src/main/java/io/metaloom/test/container/provider/DatabasePoolManager.java:
--------------------------------------------------------------------------------
1 | package io.metaloom.test.container.provider;
2 |
3 | import java.sql.SQLException;
4 | import java.util.Collection;
5 | import java.util.Collections;
6 | import java.util.HashMap;
7 | import java.util.List;
8 | import java.util.Map;
9 | import java.util.Stack;
10 |
11 | import javax.inject.Inject;
12 | import javax.inject.Singleton;
13 |
14 | import io.metaloom.test.container.provider.server.ServerConfiguration;
15 | import io.vertx.core.Vertx;
16 |
17 | @Singleton
18 | public class DatabasePoolManager {
19 |
20 | private Vertx vertx;
21 |
22 | private Map pools = new HashMap<>();
23 |
24 | private ServerConfiguration config;
25 |
26 | @Inject
27 | public DatabasePoolManager(Vertx vertx, ServerConfiguration config) {
28 | this.vertx = vertx;
29 | this.config = config;
30 | }
31 |
32 | public Collection getPools() {
33 | return Collections.unmodifiableCollection(pools.values());
34 | }
35 |
36 | public boolean contains(String id) {
37 | return pools.containsKey(id);
38 | }
39 |
40 | /**
41 | * Removes the pool from the list of pools, stops and drains it.
42 | *
43 | * @param id
44 | * @return
45 | */
46 | public boolean deletePool(String id) {
47 | DatabasePool pool = pools.remove(id);
48 | if (pool == null) {
49 | return false;
50 | } else {
51 | pool.stop();
52 | pool.drain();
53 | return true;
54 | }
55 | }
56 |
57 | public DatabasePool getPool(String id) {
58 | return pools.get(id);
59 | }
60 |
61 | public void release(DatabaseAllocation allocation) throws SQLException {
62 | allocation.release();
63 | }
64 |
65 | public DatabasePool createPool(String id, String host, int port, String internalHost, int internalPort, String username, String password,
66 | String adminDB) {
67 | DatabasePool pool = new DatabasePool(vertx, config, id, host, port, internalHost, internalPort, username, password, adminDB);
68 | pools.put(id, pool);
69 | return pool;
70 | }
71 |
72 | public DatabasePool createPool(String id, String host, int port, String internalHost, int internalPort, String username, String password,
73 | String adminDB, String templateName) {
74 | DatabasePool pool = new DatabasePool(vertx, config, id, host, port, internalHost, internalPort, username, password, adminDB);
75 | pool.setTemplateDatabaseName(templateName);
76 | pools.put(id, pool);
77 | return pool;
78 | }
79 |
80 | public int loadFromDB() throws SQLException {
81 | int importedDBs = 0;
82 | for (Database db : SQLUtils.listDatabasesWithComment(config.databaseSettings())) {
83 | String databaseName = db.name();
84 | DatabaseJsonCommentModel comment = db.comment();
85 | if (comment != null) {
86 | String poolId = comment.getPoolId();
87 | String templateName = comment.getOrigin();
88 | DatabasePool pool = getPool(poolId);
89 | if (pool == null) {
90 | pool = createPool(poolId, config.host(), config.port(), config.host(), config.port(), config.username(), config.password(),
91 | config.adminDB(), templateName);
92 | }
93 | if (!pool.hasDatabase(databaseName)) {
94 | pool.addDatabase(db);
95 | importedDBs++;
96 | }
97 | }
98 | System.out.println(db.name() + " " + (db.comment() != null ? db.comment().getPoolId() : ""));
99 | }
100 | return importedDBs;
101 | }
102 |
103 | }
104 |
--------------------------------------------------------------------------------
/server/src/main/java/io/metaloom/test/container/provider/DatabaseSettings.java:
--------------------------------------------------------------------------------
1 | package io.metaloom.test.container.provider;
2 |
3 | public record DatabaseSettings(String host, int port, String internalHost, int internalPort, String username, String password, String adminDB) {
4 |
5 | public String jdbcUrl() {
6 | return ("jdbc:postgresql://" +
7 | host() +
8 | ":" +
9 | port() +
10 | "/");
11 | }
12 |
13 | public String internalJdbcUrl() {
14 | return ("jdbc:postgresql://" +
15 | internalHost() +
16 | ":" +
17 | internalPort() +
18 | "/");
19 | }
20 |
21 | public String toString() {
22 | return "Host: " + host() + ":" + port() + " IntHost:" + internalHost() + ":" + internalPort() + " , username: " + username() + ", password: "
23 | + password() + ", adminDB: " + adminDB();
24 | }
25 |
26 | public String adminJdbcUrl() {
27 | return jdbcUrl() + adminDB;
28 | }
29 |
30 | public String internalAdminJdbcUrl() {
31 | return internalJdbcUrl() + adminDB;
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/server/src/main/java/io/metaloom/test/container/provider/SQLUtils.java:
--------------------------------------------------------------------------------
1 | package io.metaloom.test.container.provider;
2 |
3 | import java.sql.Connection;
4 | import java.sql.DriverManager;
5 | import java.sql.PreparedStatement;
6 | import java.sql.ResultSet;
7 | import java.sql.SQLException;
8 | import java.util.ArrayList;
9 | import java.util.List;
10 |
11 | import org.slf4j.Logger;
12 | import org.slf4j.LoggerFactory;
13 |
14 | import io.vertx.core.json.Json;
15 | import io.vertx.core.json.JsonObject;
16 |
17 | public final class SQLUtils {
18 |
19 | public static final String SELECT_DATABASES = "SELECT datname FROM pg_database";
20 |
21 | private static final Logger log = LoggerFactory.getLogger(SQLUtils.class);
22 |
23 | private SQLUtils() {
24 | }
25 |
26 | public static void dropDatabase(Database db) throws SQLException {
27 | DatabaseSettings settings = db.settings();
28 | try (Connection connection = DriverManager.getConnection(db.settings().internalAdminJdbcUrl(), settings.username(), settings.password())) {
29 | PreparedStatement statement = connection.prepareStatement("DROP DATABASE " + db.name());
30 | statement.executeUpdate();
31 | }
32 | }
33 |
34 | public static Database copyDatabase(DatabaseSettings settings, String sourceName, String targetName, DatabaseJsonCommentModel comment)
35 | throws SQLException {
36 | try (Connection connection = DriverManager.getConnection(settings.internalAdminJdbcUrl(), settings.username(), settings.password())) {
37 | String COPY_SQL = "CREATE DATABASE " + targetName + " WITH TEMPLATE " + sourceName + " OWNER " + settings.username();
38 | PreparedStatement statement = connection
39 | .prepareStatement(COPY_SQL);
40 | statement.executeUpdate();
41 |
42 | String json = JsonObject.mapFrom(comment).encode();
43 | String COMMENT_SQL = "COMMENT ON database " + targetName + " is '" + json + "'";
44 | PreparedStatement statement2 = connection
45 | .prepareStatement(COMMENT_SQL);
46 | statement2.executeUpdate();
47 | return new Database(settings, targetName, comment);
48 | }
49 | }
50 |
51 | public static List listDatabases(DatabaseSettings settings) throws SQLException {
52 | List databaseNames = new ArrayList<>();
53 | try (Connection connection = DriverManager.getConnection(settings.internalAdminJdbcUrl(), settings.username(), settings.password())) {
54 | PreparedStatement statement = connection.prepareStatement(SELECT_DATABASES);
55 | ResultSet rs = statement.executeQuery();
56 | while (rs.next()) {
57 | String name = rs.getString(1);
58 | databaseNames.add(name);
59 | }
60 | }
61 | return databaseNames;
62 | }
63 |
64 | public static void setDatabaseComment(DatabaseSettings settings, String databaseName, DatabaseJsonCommentModel comment) throws SQLException {
65 | String json = JsonObject.mapFrom(comment).encode();
66 | String SQL = "COMMENT ON database " + databaseName + " is '" + json + "'";
67 | try (Connection connection = DriverManager.getConnection(settings.internalAdminJdbcUrl(), settings.username(), settings.password())) {
68 | PreparedStatement statement = connection
69 | .prepareStatement(SQL);
70 | statement.executeUpdate();
71 | }
72 | }
73 |
74 | public static List listDatabasesWithComment(DatabaseSettings settings) throws SQLException {
75 | String SQL = "SELECT datname, pg_catalog.shobj_description(d.oid, 'pg_database') AS \"comment\" FROM pg_catalog.pg_database d;";
76 | try (Connection connection = DriverManager.getConnection(settings.internalAdminJdbcUrl(), settings.username(), settings.password())) {
77 | PreparedStatement statement = connection
78 | .prepareStatement(SQL);
79 | ResultSet rs = statement.executeQuery();
80 | List list = new ArrayList<>();
81 | while (rs.next()) {
82 | String name = rs.getString(1);
83 | String commentStr = rs.getString(2);
84 | DatabaseJsonCommentModel comment = null;
85 | try {
86 | comment = Json.decodeValue(commentStr, DatabaseJsonCommentModel.class);
87 | } catch (Exception e) {
88 | log.warn("Error while decoding comment {} of database {} to model", commentStr, name);
89 | log.debug("Error while decoding comment", e);
90 | }
91 | list.add(new Database(settings, name, comment));
92 | }
93 | return list;
94 | }
95 | }
96 |
97 | }
98 |
--------------------------------------------------------------------------------
/server/src/main/java/io/metaloom/test/container/provider/server/DatabaseProviderServer.java:
--------------------------------------------------------------------------------
1 | package io.metaloom.test.container.provider.server;
2 |
3 | import javax.inject.Inject;
4 |
5 | import org.slf4j.Logger;
6 | import org.slf4j.LoggerFactory;
7 |
8 | import io.metaloom.test.container.provider.DatabasePoolManager;
9 | import io.vertx.core.Future;
10 | import io.vertx.core.Vertx;
11 | import io.vertx.core.http.HttpMethod;
12 | import io.vertx.core.http.HttpServer;
13 | import io.vertx.core.http.HttpServerOptions;
14 | import io.vertx.ext.web.Router;
15 | import io.vertx.ext.web.handler.BodyHandler;
16 | import io.vertx.ext.web.handler.sockjs.SockJSHandler;
17 | import io.vertx.ext.web.handler.sockjs.SockJSHandlerOptions;
18 |
19 | public class DatabaseProviderServer {
20 |
21 | public static final Logger log = LoggerFactory.getLogger(DatabaseProviderServer.class);
22 |
23 | private final Vertx vertx;
24 |
25 | private final ServerConfiguration config;
26 |
27 | private final DatabasePoolManager manager;
28 |
29 | private final ServerApi api;
30 |
31 | private HttpServer server;
32 |
33 | @Inject
34 | public DatabaseProviderServer(Vertx vertx, ServerConfiguration config, DatabasePoolManager manager, ServerApi api) {
35 | this.vertx = vertx;
36 | this.config = config;
37 | this.manager = manager;
38 | this.api = api;
39 | }
40 |
41 | public Future start() {
42 | HttpServerOptions options = new HttpServerOptions();
43 | options.setPort(config.httpPort());
44 | options.setHost("0.0.0.0");
45 | this.server = vertx.createHttpServer(options);
46 |
47 | Router router = Router.router(vertx);
48 |
49 | SockJSHandlerOptions sockOptions = new SockJSHandlerOptions().setHeartbeatInterval(500);
50 |
51 | SockJSHandler sockJSHandler = SockJSHandler.create(vertx, sockOptions);
52 | Router sockRouter = sockJSHandler.socketHandler(api::websocketHandler);
53 | router.route("/connect/*")
54 | .subRouter(sockRouter);
55 | router.route()
56 | .handler(BodyHandler.create());
57 | router.route("/pools")
58 | .method(HttpMethod.GET)
59 | .handler(api::listPoolsHandler);
60 | router.route("/pools/:id")
61 | .method(HttpMethod.GET)
62 | .handler(api::loadPoolHandler);
63 | router.route("/pools/:id")
64 | .method(HttpMethod.DELETE)
65 | .handler(api::poolDeleteHandler);
66 | router.route("/pools/:id")
67 | .method(HttpMethod.POST)
68 | .handler(api::upsertPoolHandler);
69 | router.route()
70 | .failureHandler(api::failureHandler);
71 | server.requestHandler(router);
72 | return server.listen()
73 | .onSuccess(s -> {
74 | log.info("Server started. Listening on port {}", s.actualPort());
75 | });
76 | }
77 |
78 | public Future stop() {
79 | if (server != null) {
80 | return server.close();
81 | }
82 | return Future.succeededFuture();
83 | }
84 |
85 | public DatabasePoolManager getManager() {
86 | return manager;
87 | }
88 |
89 | }
90 |
--------------------------------------------------------------------------------
/server/src/main/java/io/metaloom/test/container/provider/server/DatabaseProviderServerRunner.java:
--------------------------------------------------------------------------------
1 | package io.metaloom.test.container.provider.server;
2 |
3 | import org.slf4j.Logger;
4 | import org.slf4j.LoggerFactory;
5 |
6 | import io.metaloom.test.container.provider.common.version.Version;
7 | import io.metaloom.test.container.provider.server.dagger.DaggerServerComponent;
8 | import io.metaloom.test.container.provider.server.dagger.ServerComponent;
9 |
10 | public class DatabaseProviderServerRunner {
11 |
12 | private static final Logger log = LoggerFactory.getLogger(DatabaseProviderServerRunner.class);
13 |
14 | public static void main(String[] args) {
15 | log.info("Starting Test Database Provider Server [" + Version.getPlainVersion() + "] - [" + Version.getBuildInfo().getBuildtimestamp() + "]");
16 | ServerConfiguration config = ServerConfigurationLoader.load();
17 | ServerComponent component = DaggerServerComponent.builder().configuration(config).build();
18 | component.boot().start();
19 | }
20 |
21 | }
22 |
--------------------------------------------------------------------------------
/server/src/main/java/io/metaloom/test/container/provider/server/JSON.java:
--------------------------------------------------------------------------------
1 | package io.metaloom.test.container.provider.server;
2 |
3 | import io.vertx.core.buffer.Buffer;
4 | import io.vertx.core.json.JsonObject;
5 |
6 | public final class JSON {
7 |
8 | private JSON() {
9 |
10 | }
11 |
12 | public static Buffer toBuffer(Object obj) {
13 | return JsonObject.mapFrom(obj).toBuffer();
14 | }
15 |
16 | public static T fromBuffer(Buffer buffer, Class classOfT) {
17 | JsonObject jsonObject = buffer.toJsonObject();
18 | T obj = jsonObject.mapTo(classOfT);
19 | return obj;
20 | }
21 |
22 | }
23 |
--------------------------------------------------------------------------------
/server/src/main/java/io/metaloom/test/container/provider/server/ModelHelper.java:
--------------------------------------------------------------------------------
1 | package io.metaloom.test.container.provider.server;
2 |
3 | import io.metaloom.test.container.provider.DatabaseAllocation;
4 | import io.metaloom.test.container.provider.DatabasePool;
5 | import io.metaloom.test.container.provider.model.DatabaseAllocationResponse;
6 | import io.metaloom.test.container.provider.model.DatabasePoolConnection;
7 | import io.metaloom.test.container.provider.model.DatabasePoolResponse;
8 | import io.metaloom.test.container.provider.model.DatabasePoolSettings;
9 |
10 | public final class ModelHelper {
11 |
12 | private ModelHelper() {
13 | }
14 |
15 | public static DatabasePoolResponse toModel(DatabasePool pool) {
16 | DatabasePoolConnection connection = new DatabasePoolConnection();
17 | connection.setDatabase(pool.settings().adminDB());
18 | connection.setHost(pool.settings().host());
19 | connection.setPort(pool.settings().port());
20 | connection.setInternalHost(pool.settings().internalHost());
21 | connection.setInternalPort(pool.settings().internalPort());
22 |
23 | connection.setUsername(pool.settings().username());
24 | connection.setPassword(pool.settings().password());
25 |
26 | DatabasePoolSettings settings = new DatabasePoolSettings();
27 | settings.setIncrement(pool.getIncrement());
28 | settings.setMaximum(pool.getMaximum());
29 | settings.setMinimum(pool.getMinimum());
30 |
31 | DatabasePoolResponse response = new DatabasePoolResponse();
32 | response.setCreated(pool.getCreationDate());
33 | response.setId(pool.id());
34 | response.setConnection(connection);
35 | response.setSettings(settings);
36 | response.setAllocationLevel(pool.allocationLevel());
37 | response.setLevel(pool.level());
38 | response.setTemplateDatabaseName(pool.getTemplateName());
39 | response.setStarted(pool.isStarted());
40 | return response;
41 | }
42 |
43 | public static DatabaseAllocationResponse toModel(DatabaseAllocation allocation) {
44 | DatabaseAllocationResponse response = new DatabaseAllocationResponse();
45 | response.setPoolId(allocation.getPool().id());
46 | response.setId(allocation.id());
47 | response.setHost(allocation.db().settings().host());
48 | response.setPort(allocation.db().settings().port());
49 | response.setJdbcUrl(allocation.db().jdbcUrl());
50 | response.setUsername(allocation.db().settings().username());
51 | response.setPassword(allocation.db().settings().password());
52 | response.setDatabaseName(allocation.db().name());
53 | return response;
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/server/src/main/java/io/metaloom/test/container/provider/server/ProviderRequest.java:
--------------------------------------------------------------------------------
1 | package io.metaloom.test.container.provider.server;
2 |
3 | public record ProviderRequest(String poolId, String testName) {
4 |
5 | public static ProviderRequest from(String id) {
6 | if (!id.contains("/")) {
7 | throw new ServerError("Id must contain pool name. Got: " + id);
8 | }
9 | String[] parts = id.split("/");
10 | String poolId = parts[0];
11 | String testName = parts[1];
12 | return new ProviderRequest(poolId, testName);
13 | }
14 |
15 | public String toString() {
16 | return poolId + " / " + testName;
17 | }
18 |
19 | }
20 |
--------------------------------------------------------------------------------
/server/src/main/java/io/metaloom/test/container/provider/server/ServerApi.java:
--------------------------------------------------------------------------------
1 | package io.metaloom.test.container.provider.server;
2 |
3 | import java.sql.SQLException;
4 | import java.util.concurrent.atomic.AtomicReference;
5 |
6 | import javax.inject.Inject;
7 | import javax.inject.Singleton;
8 |
9 | import org.slf4j.Logger;
10 | import org.slf4j.LoggerFactory;
11 |
12 | import io.metaloom.test.container.provider.DatabaseAllocation;
13 | import io.metaloom.test.container.provider.DatabasePool;
14 | import io.metaloom.test.container.provider.DatabasePoolManager;
15 | import io.metaloom.test.container.provider.DatabaseSettings;
16 | import io.metaloom.test.container.provider.model.DatabaseAllocationResponse;
17 | import io.metaloom.test.container.provider.model.DatabasePoolConnection;
18 | import io.metaloom.test.container.provider.model.DatabasePoolListResponse;
19 | import io.metaloom.test.container.provider.model.DatabasePoolRequest;
20 | import io.metaloom.test.container.provider.model.DatabasePoolResponse;
21 | import io.metaloom.test.container.provider.model.DatabasePoolSettings;
22 | import io.vertx.core.buffer.Buffer;
23 | import io.vertx.core.http.HttpHeaders;
24 | import io.vertx.core.json.JsonObject;
25 | import io.vertx.ext.web.RoutingContext;
26 | import io.vertx.ext.web.handler.sockjs.SockJSSocket;
27 |
28 | @Singleton
29 | public class ServerApi {
30 |
31 | private static final Logger log = LoggerFactory.getLogger(ServerApi.class);
32 |
33 | private final DatabasePoolManager manager;
34 |
35 | @Inject
36 | public ServerApi(DatabasePoolManager manager) {
37 | this.manager = manager;
38 | }
39 |
40 | public void poolDeleteHandler(RoutingContext rc) {
41 | String id = rc.pathParam("id");
42 | log.info("Deleting pool {}", id);
43 | boolean hasPool = manager.contains(id);
44 | if (!hasPool) {
45 | rc.response().setStatusCode(404).end();
46 | return;
47 | }
48 | boolean result = manager.deletePool(id);
49 | if (result) {
50 | log.info("Pool {} deleted", id);
51 | rc.response().setStatusCode(204).end();
52 | return;
53 | } else {
54 | log.error("Error while deleting pool {}", id);
55 | rc.response().setStatusCode(400).end();
56 | }
57 | }
58 |
59 | public void upsertPoolHandler(RoutingContext rc) {
60 | String id = rc.pathParam("id");
61 | DatabasePoolRequest model = rc.body().asPojo(DatabasePoolRequest.class);
62 | String templateDatabaseName = require(model.getTemplateDatabaseName(), "templateDatabaseName");
63 |
64 | DatabasePool pool = manager.getPool(id);
65 | if (pool == null) {
66 | DatabasePoolConnection connection = model.getConnection();
67 | DatabasePoolSettings settings = model.getSettings();
68 | log.info("Adding pool {}", id);
69 |
70 | require(connection, "connection");
71 | String host = require(connection.getHost(), "host");
72 | Integer port = require(connection.getPort(), "port");
73 | String internalHost = connection.getInternalHost() == null ? host : connection.getInternalHost();
74 | Integer internalPort = connection.getInternalPort() == null ? port : connection.getInternalPort();
75 | String username = require(connection.getUsername(), "username");
76 | String password = require(connection.getPassword(), "password");
77 | String adminDB = require(connection.getDatabase(), "database");
78 | pool = manager.createPool(id, host, port, internalHost, internalPort, username, password, adminDB);
79 |
80 | // Apply the custom setting if those were provided. Otherwise the manager will use the default values.
81 | if (settings != null) {
82 | Integer minimum = settings.getMinimum();
83 | if (minimum != null) {
84 | pool.setMinimum(minimum);
85 | }
86 | Integer maximum = settings.getMaximum();
87 | if (maximum != null) {
88 | pool.setMaximum(maximum);
89 | }
90 | Integer increment = settings.getIncrement();
91 | if (increment != null) {
92 | pool.setIncrement(increment);
93 | }
94 | }
95 | pool.setTemplateDatabaseName(templateDatabaseName);
96 | pool.start();
97 | } else {
98 | log.info("Recreating pool {}", id);
99 | DatabaseSettings settings = pool.settings();
100 | manager.deletePool(id);
101 | DatabasePool newPool = manager.createPool(id, settings.host(), settings.port(), settings.internalHost(), settings.internalPort(),
102 | settings.username(), settings.password(), settings.adminDB(), templateDatabaseName);
103 | if (!newPool.isStarted()) {
104 | newPool.start();
105 | }
106 | pool = newPool;
107 | }
108 |
109 | DatabasePoolResponse response = ModelHelper.toModel(pool);
110 | rc.response().putHeader(HttpHeaders.CONTENT_TYPE, "application/json").end(JSON.toBuffer(response));
111 |
112 | }
113 |
114 | private T require(T value, String key) {
115 | if (value == null) {
116 | throw new ServerError(String.format("Field %s is missing", key));
117 | }
118 | return value;
119 | }
120 |
121 | public void failureHandler(RoutingContext rc) {
122 | if (rc.failed() && rc.failure() instanceof ServerError se) {
123 | rc.response()
124 | .setStatusCode(400)
125 | .putHeader(HttpHeaders.CONTENT_TYPE, "application/json")
126 | .end(se.toJson().toBuffer());
127 | return;
128 | }
129 | log.error("Error while handling request for " + rc.normalizedPath(), rc.failure());
130 | rc.next();
131 | }
132 |
133 | public void loadPoolHandler(RoutingContext rc) {
134 | String id = rc.pathParam("id");
135 | DatabasePool pool = manager.getPool(id);
136 | if (pool == null) {
137 | log.info("Pool {} not found", id);
138 | rc.response().setStatusCode(404).end();
139 | } else {
140 | DatabasePoolResponse response = ModelHelper.toModel(pool);
141 | rc.response().putHeader(HttpHeaders.CONTENT_TYPE, "application/json").end(JSON.toBuffer(response));
142 | }
143 | }
144 |
145 | public void listPoolsHandler(RoutingContext rc) {
146 | log.info("Getting stat request");
147 |
148 | DatabasePoolListResponse response = new DatabasePoolListResponse();
149 | for (DatabasePool pool : manager.getPools()) {
150 | response.add(ModelHelper.toModel(pool));
151 | }
152 |
153 | Buffer buffer = JSON.toBuffer(response);
154 | rc.response().putHeader(HttpHeaders.CONTENT_TYPE, "application/json").end(buffer);
155 | }
156 |
157 | public void websocketHandler(SockJSSocket sock) {
158 |
159 | AtomicReference allocationRef = new AtomicReference<>();
160 |
161 | // Check the message - currently msgs will only be send from the client to request a new database
162 | sock.handler(msg -> {
163 | ProviderRequest providerRequest = ProviderRequest.from(msg.toString());
164 |
165 | log.info("Allocating db for {}", providerRequest);
166 | try {
167 | DatabasePool pool = manager.getPool(providerRequest.poolId());
168 | if (pool == null) {
169 | log.error("Unable to fullfil request. Provided pool for id {} not found", providerRequest.poolId());
170 | error(sock, "Pool not found {" + providerRequest.poolId() + "}");
171 | return;
172 | }
173 | DatabaseAllocation allocation = pool.allocate(providerRequest.testName());
174 | if (allocation == null) {
175 | error(sock, "Unable to allocate database from pool {" + providerRequest.poolId() + "}");
176 | return;
177 | }
178 | allocationRef.set(allocation);
179 | DatabaseAllocationResponse response = ModelHelper.toModel(allocation);
180 | sock.write(JsonObject.mapFrom(response).toBuffer());
181 | } catch (SQLException e) {
182 | log.error("Error while allocating database for test {}", providerRequest, e);
183 | error(sock, "Unknown error");
184 | }
185 | });
186 | sock.closeHandler(close -> {
187 | DatabaseAllocation allocation = allocationRef.get();
188 | if (allocation != null) {
189 | log.info("Releasing allocation {}", allocation);
190 | try {
191 | allocation.release();
192 | } catch (Exception e) {
193 | log.error("Error while releasing database for test {}", allocation.db().name(), e);
194 | }
195 | } else {
196 | log.debug("No allocation found. Just closing connection.");
197 | }
198 | });
199 | }
200 |
201 | private void error(SockJSSocket sock, String msg) {
202 | sock.write(new JsonObject().put("error", msg).toBuffer());
203 | }
204 |
205 | }
206 |
--------------------------------------------------------------------------------
/server/src/main/java/io/metaloom/test/container/provider/server/ServerConfiguration.java:
--------------------------------------------------------------------------------
1 | package io.metaloom.test.container.provider.server;
2 |
3 | import io.metaloom.test.container.provider.DatabaseSettings;
4 |
5 | public record ServerConfiguration(int httpPort, String host, Integer port, String username, String password, String adminDB,
6 | String templateDatabaseName, Integer defaultPoolMinimum, Integer defaultPoolMaximum,
7 | Integer defaultPoolIncrement) {
8 |
9 | public static ServerConfiguration create(int httpPort, String host, Integer port, String username, String password, String adminDB,
10 | String templateDatabaseName, Integer defaultPoolMinimum, Integer defaultPoolMaximum,
11 | Integer defaultPoolIncrement) {
12 | return new ServerConfiguration(httpPort, host, port, username, password, adminDB, templateDatabaseName, defaultPoolMinimum,
13 | defaultPoolMaximum, defaultPoolIncrement);
14 | }
15 |
16 | public static ServerConfiguration create(int httpPort) {
17 | return new ServerConfiguration(httpPort, null, null, null, null, null, null, null, null, null);
18 | }
19 |
20 | public DatabaseSettings databaseSettings() {
21 | return new DatabaseSettings(host, port, host, port, username, password, adminDB);
22 | }
23 |
24 | public boolean hasConnectionDetails() {
25 | return host != null && port != null && username != null && password != null && adminDB != null;
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/server/src/main/java/io/metaloom/test/container/provider/server/ServerConfigurationLoader.java:
--------------------------------------------------------------------------------
1 | package io.metaloom.test.container.provider.server;
2 |
3 | import io.metaloom.test.container.provider.common.ServerEnv;
4 |
5 | public final class ServerConfigurationLoader {
6 |
7 | private ServerConfigurationLoader() {
8 | }
9 |
10 | /**
11 | * Load the effective server configuration by examining env variables.
12 | *
13 | * @return
14 | */
15 | public static ServerConfiguration load() {
16 | String host = ServerEnv.getDatabaseHost();
17 | Integer port = ServerEnv.getDatabasePort();
18 | String username = ServerEnv.getDatabaseUsername();
19 | String password = ServerEnv.getDatabasePassword();
20 | String adminDB = ServerEnv.getDatabaseName();
21 | String templateDatabaseName = ServerEnv.getDatabaseTemplateName();
22 | int httpPort = ServerEnv.getHttpPort();
23 |
24 | if (templateDatabaseName != null) {
25 | requireEnv(host, ServerEnv.TESTDATABASE_PROVIDER_DATABASE_HOST_KEY, templateDatabaseName);
26 | requireEnv(port, ServerEnv.TESTDATABASE_PROVIDER_DATABASE_PORT_KEY, templateDatabaseName);
27 | requireEnv(username, ServerEnv.TESTDATABASE_PROVIDER_DATABASE_USERNAME_KEY, templateDatabaseName);
28 | requireEnv(password, ServerEnv.TESTDATABASE_PROVIDER_DATABASE_PASSWORD_KEY, templateDatabaseName);
29 | requireEnv(adminDB, ServerEnv.TESTDATABASE_PROVIDER_DATABASE_DBNAME_KEY, templateDatabaseName);
30 | }
31 |
32 | int defaultMinimum = ServerEnv.getPoolMinimum();
33 | int defaultMaximum = ServerEnv.getPoolMaximum();
34 | int defaultIncrement = ServerEnv.getPoolIncrement();
35 |
36 | return new ServerConfiguration(httpPort, host, port, username, password, adminDB, templateDatabaseName, defaultMinimum, defaultMaximum,
37 | defaultIncrement);
38 | }
39 |
40 | private static void requireEnv(Object value, String env, String templateDatabaseName) {
41 | if (value == null) {
42 | throw new ServerError("The env " + env + " must be set when a pool should be created for " + templateDatabaseName);
43 | }
44 | }
45 |
46 | }
47 |
--------------------------------------------------------------------------------
/server/src/main/java/io/metaloom/test/container/provider/server/ServerError.java:
--------------------------------------------------------------------------------
1 | package io.metaloom.test.container.provider.server;
2 |
3 | import io.vertx.core.json.JsonObject;
4 |
5 | public class ServerError extends RuntimeException {
6 |
7 | private static final long serialVersionUID = -6011015473604155831L;
8 |
9 | public ServerError(String msg) {
10 | super(msg);
11 | }
12 |
13 | public JsonObject toJson() {
14 | JsonObject json = new JsonObject();
15 | return json;
16 | }
17 |
18 | }
19 |
--------------------------------------------------------------------------------
/server/src/main/java/io/metaloom/test/container/provider/server/dagger/ProviderModule.java:
--------------------------------------------------------------------------------
1 | package io.metaloom.test.container.provider.server.dagger;
2 |
3 | import dagger.Module;
4 |
5 | @Module
6 | public class ProviderModule {
7 |
8 | }
9 |
--------------------------------------------------------------------------------
/server/src/main/java/io/metaloom/test/container/provider/server/dagger/ServerComponent.java:
--------------------------------------------------------------------------------
1 | package io.metaloom.test.container.provider.server.dagger;
2 |
3 | import javax.inject.Singleton;
4 |
5 | import dagger.BindsInstance;
6 | import dagger.Component;
7 | import io.metaloom.test.container.provider.BootstrapInitializer;
8 | import io.metaloom.test.container.provider.server.ServerConfiguration;
9 |
10 | /**
11 | * Central dagger component.
12 | */
13 | @Singleton
14 | @Component(modules = { VertxModule.class, ProviderModule.class })
15 | public interface ServerComponent {
16 |
17 | BootstrapInitializer boot();
18 |
19 | @Component.Builder
20 | interface Builder {
21 |
22 | /**
23 | * Inject configuration options.
24 | *
25 | * @param options
26 | * @return
27 | */
28 | @BindsInstance
29 | Builder configuration(ServerConfiguration options);
30 |
31 | /**
32 | * Build the component.
33 | *
34 | * @return
35 | */
36 | ServerComponent build();
37 |
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/server/src/main/java/io/metaloom/test/container/provider/server/dagger/VertxModule.java:
--------------------------------------------------------------------------------
1 | package io.metaloom.test.container.provider.server.dagger;
2 |
3 | import javax.inject.Singleton;
4 |
5 | import dagger.Module;
6 | import dagger.Provides;
7 | import io.vertx.core.Vertx;
8 | import io.vertx.core.file.FileSystem;
9 |
10 | @Module
11 | public class VertxModule {
12 |
13 | @Provides
14 | @Singleton
15 | public Vertx vertx() {
16 | return Vertx.vertx();
17 | }
18 |
19 | @Provides
20 | @Singleton
21 | public FileSystem filesystem(Vertx vertx) {
22 | return vertx.fileSystem();
23 | }
24 |
25 | }
26 |
--------------------------------------------------------------------------------
/server/src/test/java/io/metaloom/test/container/server/AbstractProviderServerTest.java:
--------------------------------------------------------------------------------
1 | package io.metaloom.test.container.server;
2 |
3 | public abstract class AbstractProviderServerTest implements AllocationTestHelper {
4 |
5 | public static DatabaseProviderTestServer server = new DatabaseProviderTestServer();
6 | }
7 |
--------------------------------------------------------------------------------
/server/src/test/java/io/metaloom/test/container/server/AllocationTestHelper.java:
--------------------------------------------------------------------------------
1 | package io.metaloom.test.container.server;
2 |
3 | import static org.junit.Assert.assertEquals;
4 | import static org.junit.Assert.assertTrue;
5 | import static org.junit.Assert.assertNotNull;
6 |
7 | import io.metaloom.test.container.provider.model.DatabaseAllocationResponse;
8 |
9 | public interface AllocationTestHelper {
10 |
11 | default void assertAllocation(DatabaseProviderTestServer server, DatabaseAllocationResponse db, String testName) {
12 | assertNotNull(db.getJdbcUrl());
13 | assertEquals("The database hostname did not match.", "localhost", db.getHost());
14 | assertEquals("The port did not match up with the container", server.db().getPort(), db.getPort());
15 | assertNotNull("The database name should not be null", db.getDatabaseName());
16 | assertEquals("The username did not match.", server.db().getUsername(), db.getUsername());
17 | assertEquals("The password did not match.", server.db().getPassword(), db.getPassword());
18 | assertNotNull("The test id must not be null", db.getId());
19 | assertTrue("The allocation id {" + db.getId() + "} did not contain the testname", db.getId().endsWith(testName));
20 | assertEquals("default", db.getPoolId());
21 | }
22 |
23 | }
24 |
--------------------------------------------------------------------------------
/server/src/test/java/io/metaloom/test/container/server/DatabasePoolManagerTest.java:
--------------------------------------------------------------------------------
1 | package io.metaloom.test.container.server;
2 |
3 | import static org.junit.Assert.assertEquals;
4 |
5 | import java.io.IOException;
6 | import java.sql.SQLException;
7 |
8 | import org.junit.jupiter.api.BeforeAll;
9 | import org.junit.jupiter.api.Test;
10 | import org.testcontainers.junit.jupiter.Container;
11 | import org.testcontainers.junit.jupiter.Testcontainers;
12 |
13 | import io.metaloom.maven.provider.container.PostgreSQLPoolContainer;
14 | import io.metaloom.test.container.provider.DatabasePool;
15 | import io.metaloom.test.container.provider.DatabasePoolManager;
16 | import io.metaloom.test.container.provider.server.ServerConfiguration;
17 | import io.vertx.core.Vertx;
18 |
19 | @Testcontainers
20 | public class DatabasePoolManagerTest {
21 |
22 | public static Vertx vertx = Vertx.vertx();
23 |
24 | public static ServerConfiguration config;
25 |
26 | @Container
27 | public static PostgreSQLPoolContainer container = new PostgreSQLPoolContainer().withTmpFs(128);
28 |
29 | @BeforeAll
30 | public static void setup() {
31 | config = ServerConfiguration.create(0, container.getHost(), container.getPort(), container.getUsername(), container.getPassword(),
32 | container.getDatabaseName(), null, 10, 25, 5);
33 | }
34 |
35 | @Test
36 | public void testPool() {
37 | DatabasePoolManager manager = new DatabasePoolManager(vertx, config);
38 | assertEquals(0, manager.getPools()
39 | .size());
40 | DatabasePool pool = createPool(manager, "dummy");
41 | pool.start();
42 | assertEquals(1, manager.getPools().size());
43 | manager.deletePool(pool.id());
44 | assertEquals(0, manager.getPools().size());
45 | }
46 |
47 | @Test
48 | public void testPoolRestartBehaviour() throws SQLException, IOException, InterruptedException {
49 | DatabasePool pool = createPool(new DatabasePoolManager(vertx, config), "dummy1234").setTemplateDatabaseName(container.getDatabaseName());
50 | pool.preAllocate();
51 | assertEquals(5, pool.level());
52 | DatabasePoolManager manager = new DatabasePoolManager(vertx, config);
53 | int importedDbs = manager.loadFromDB();
54 | assertEquals(5, importedDbs);
55 | assertEquals(1, manager.getPools().size());
56 | }
57 |
58 | private DatabasePool createPool(DatabasePoolManager manager, String name) {
59 | return manager.createPool("test123", container.getHost(), container.getPort(), container.getHost(), container.getPort(),
60 | container.getUsername(),
61 | container.getPassword(), container.getDatabaseName());
62 |
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/server/src/test/java/io/metaloom/test/container/server/DatabasePoolTest.java:
--------------------------------------------------------------------------------
1 | package io.metaloom.test.container.server;
2 |
3 | import static org.junit.Assert.assertEquals;
4 | import static org.junit.Assert.assertFalse;
5 | import static org.junit.Assert.assertNotNull;
6 | import static org.junit.Assert.assertTrue;
7 |
8 | import java.sql.SQLException;
9 | import java.util.List;
10 |
11 | import org.junit.jupiter.api.AfterEach;
12 | import org.junit.jupiter.api.BeforeEach;
13 | import org.junit.jupiter.api.Test;
14 | import org.testcontainers.junit.jupiter.Container;
15 | import org.testcontainers.junit.jupiter.Testcontainers;
16 |
17 | import io.metaloom.maven.provider.container.PostgreSQLPoolContainer;
18 | import io.metaloom.test.container.provider.DatabaseAllocation;
19 | import io.metaloom.test.container.provider.DatabasePool;
20 | import io.metaloom.test.container.provider.DatabasePoolFactory;
21 | import io.metaloom.test.container.provider.SQLUtils;
22 | import io.vertx.core.Vertx;
23 |
24 | @Testcontainers
25 | public class DatabasePoolTest {
26 |
27 | public static final Vertx vertx = Vertx.vertx();
28 |
29 | @Container
30 | public static PostgreSQLPoolContainer container = new PostgreSQLPoolContainer().withTmpFs(128);
31 |
32 | DatabasePool pool;
33 |
34 | @BeforeEach
35 | public void setup() throws SQLException {
36 | DatabasePoolFactory factory = new DatabasePoolFactory(vertx, null);
37 | this.pool = factory.createPool("dummy", container.getHost(), container.getPort(), container.getHost(), container.getPort(),
38 | container.getUsername(), container.getPassword(),
39 | container.getDatabaseName());
40 | String databaseName = TestSQLHelper.setupTable(pool.settings().jdbcUrl(), pool.settings().username(), pool.settings().password());
41 | pool.setTemplateDatabaseName(databaseName);
42 | }
43 |
44 | @AfterEach
45 | public void stop() {
46 | if (pool != null) {
47 | pool.stop();
48 | }
49 | }
50 |
51 | @Test
52 | public void testPool() throws SQLException, InterruptedException {
53 | Thread.sleep(2000);
54 | assertEquals("There should be no databases allocated yet.", 0, pool.allocationLevel());
55 | assertEquals("There should be no databases in the pool", 0, pool.level());
56 | assertNotNull("The template should already been set", pool.getTemplateName());
57 | assertFalse("The pool should still be not started.", pool.isStarted());
58 |
59 | pool.start();
60 | Thread.sleep(4000);
61 | assertTrue("The pool should have been started.", pool.isStarted());
62 | assertTrue("The pool should already started preparing databases.", pool.level() != 0);
63 | assertEquals("There should be no databases allocated yet.", 0, pool.allocationLevel());
64 |
65 | DatabaseAllocation allocation = pool.allocate("test123");
66 | assertTrue("The id was wrong. Got: " + allocation.id(), allocation.id()
67 | .endsWith("#test123"));
68 | assertEquals("One allocation should be listed", 1, pool.allocationLevel());
69 | assertTrue("The allocation could not be released", pool.release(allocation));
70 | List dbs = SQLUtils.listDatabases(pool.settings());
71 | for (String db : dbs) {
72 | System.out.println(db);
73 | }
74 | assertEquals("The allocation should now be gone", 0, pool.allocationLevel());
75 | assertTrue(dbs.size() > 1);
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/server/src/test/java/io/metaloom/test/container/server/DatabaseProviderTest.java:
--------------------------------------------------------------------------------
1 | package io.metaloom.test.container.server;
2 |
3 | import java.util.concurrent.CompletableFuture;
4 | import java.util.concurrent.ExecutionException;
5 |
6 | import org.junit.jupiter.api.Test;
7 |
8 | import io.metaloom.test.container.provider.DatabasePoolManager;
9 | import io.metaloom.test.container.provider.client.ClientAllocation;
10 | import io.metaloom.test.container.provider.client.ProviderClient;
11 | import io.metaloom.test.container.provider.server.DatabaseProviderServer;
12 | import io.metaloom.test.container.provider.server.ServerApi;
13 | import io.metaloom.test.container.provider.server.ServerConfiguration;
14 | import io.vertx.core.Future;
15 | import io.vertx.core.Vertx;
16 |
17 | public class DatabaseProviderTest {
18 |
19 | @Test
20 | public void testAcquire() throws InterruptedException, ExecutionException {
21 | Vertx vertx = Vertx.vertx();
22 | ServerConfiguration config = ServerConfiguration.create(0);
23 | DatabasePoolManager manager = new DatabasePoolManager(vertx, config);
24 | ServerApi api = new ServerApi(manager);
25 | Future future = new DatabaseProviderServer(vertx, config, manager, api).start().compose(server -> {
26 | ProviderClient client = new ProviderClient("localhost", server.actualPort());
27 | CompletableFuture fut = client.link("dummy", "test");
28 | return Future.fromCompletionStage(fut);
29 | });
30 |
31 | future.toCompletionStage().toCompletableFuture().get();
32 |
33 | Thread.sleep(5_000);
34 |
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/server/src/test/java/io/metaloom/test/container/server/DatabaseProviderTestServer.java:
--------------------------------------------------------------------------------
1 | package io.metaloom.test.container.server;
2 |
3 | import io.metaloom.maven.provider.container.PostgreSQLPoolContainer;
4 | import io.metaloom.test.container.provider.BootstrapInitializer;
5 | import io.metaloom.test.container.provider.DatabasePool;
6 | import io.metaloom.test.container.provider.server.DatabaseProviderServer;
7 | import io.metaloom.test.container.provider.server.ServerConfiguration;
8 | import io.metaloom.test.container.provider.server.dagger.DaggerServerComponent;
9 | import io.vertx.core.http.HttpServer;
10 |
11 | public class DatabaseProviderTestServer {
12 |
13 | private HttpServer httpServer;
14 | private DatabasePool pool;
15 | private PostgreSQLPoolContainer db;
16 |
17 | public DatabaseProviderTestServer() {
18 | try {
19 | db = new PostgreSQLPoolContainer();
20 | db.start();
21 | ServerConfiguration config = new ServerConfiguration(0, db.getHost(), db.getPort(), db.getUsername(), db.getPassword(), db.getDatabaseName(),
22 | null, null, null, null);
23 | BootstrapInitializer boot = DaggerServerComponent.builder().configuration(config).build().boot();
24 | DatabaseProviderServer server = boot.server();
25 | this.httpServer = server.start().toCompletionStage().toCompletableFuture().get();
26 | this.pool = server.getManager().createPool("default", "localhost", db.getPort(), "localhost", db.getPort(), db.getUsername(),
27 | db.getPassword(), db.getDatabaseName(), db.getDatabaseName());
28 | pool.start();
29 | } catch (Exception e) {
30 | e.printStackTrace();
31 | }
32 | }
33 |
34 | public DatabasePool getPool() {
35 | return pool;
36 | }
37 |
38 | public int getPort() {
39 | return httpServer.actualPort();
40 | }
41 |
42 | public PostgreSQLPoolContainer db() {
43 | return db;
44 | }
45 |
46 | }
47 |
--------------------------------------------------------------------------------
/server/src/test/java/io/metaloom/test/container/server/JSONTest.java:
--------------------------------------------------------------------------------
1 | package io.metaloom.test.container.server;
2 |
3 | import static org.junit.Assert.assertEquals;
4 |
5 | import org.junit.jupiter.api.Test;
6 |
7 | import io.metaloom.test.container.provider.model.DatabasePoolConnection;
8 | import io.metaloom.test.container.provider.model.DatabasePoolRequest;
9 | import io.metaloom.test.container.provider.server.JSON;
10 | import io.vertx.core.buffer.Buffer;
11 |
12 | public class JSONTest {
13 |
14 | @Test
15 | public void testJson() {
16 | Buffer buffer = JSON.toBuffer(new DatabasePoolRequest().setConnection(new DatabasePoolConnection().setHost("ABC")));
17 | System.out.println(buffer.toJsonObject().encodePrettily());
18 | DatabasePoolRequest obj = JSON.fromBuffer(buffer, DatabasePoolRequest.class);
19 | assertEquals("ABC", obj.getConnection().getHost());
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/server/src/test/java/io/metaloom/test/container/server/ModelHelperTest.java:
--------------------------------------------------------------------------------
1 | package io.metaloom.test.container.server;
2 |
3 | import org.junit.jupiter.api.Test;
4 |
5 | import io.metaloom.test.container.provider.DatabasePool;
6 | import io.metaloom.test.container.provider.DatabasePoolFactory;
7 | import io.metaloom.test.container.provider.model.DatabasePoolResponse;
8 | import io.metaloom.test.container.provider.server.ModelHelper;
9 | import io.vertx.core.json.JsonObject;
10 |
11 | public class ModelHelperTest {
12 |
13 | // static {
14 | // io.vertx.core.json.jackson.DatabindCodec codec = (io.vertx.core.json.jackson.DatabindCodec) io.vertx.core.json.Json.CODEC;
15 | // // returns the ObjectMapper used by Vert.x
16 | // ObjectMapper mapper = codec.mapper();
17 | // mapper.registerModule(new JavaTimeModule());
18 | // mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
19 | // }
20 |
21 | @Test
22 | public void testPoolModel() {
23 | DatabasePoolFactory factory = new DatabasePoolFactory(null, null);
24 | DatabasePool pool = factory.createPool("dummy", "localhost", 42, "localhost-int", 42, "user", "pw", "admin-db");
25 | DatabasePoolResponse model = ModelHelper.toModel(pool);
26 | JsonObject json = JsonObject.mapFrom(model);
27 | System.out.println(json.encodePrettily());
28 | System.out.println(json.encode());
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/server/src/test/java/io/metaloom/test/container/server/ProviderClientServerTest.java:
--------------------------------------------------------------------------------
1 | package io.metaloom.test.container.server;
2 |
3 | import static org.junit.Assert.assertEquals;
4 | import static org.junit.jupiter.api.Assertions.assertNotNull;
5 |
6 | import java.util.concurrent.CompletableFuture;
7 |
8 | import org.junit.jupiter.api.AfterAll;
9 | import org.junit.jupiter.api.BeforeAll;
10 | import org.junit.jupiter.api.Test;
11 | import org.testcontainers.junit.jupiter.Container;
12 | import org.testcontainers.junit.jupiter.Testcontainers;
13 |
14 | import io.metaloom.maven.provider.container.PostgreSQLPoolContainer;
15 | import io.metaloom.test.container.provider.DatabasePoolManager;
16 | import io.metaloom.test.container.provider.client.ClientAllocation;
17 | import io.metaloom.test.container.provider.client.ProviderClient;
18 | import io.metaloom.test.container.provider.model.DatabasePoolConnection;
19 | import io.metaloom.test.container.provider.model.DatabasePoolListResponse;
20 | import io.metaloom.test.container.provider.model.DatabasePoolRequest;
21 | import io.metaloom.test.container.provider.model.DatabasePoolResponse;
22 | import io.metaloom.test.container.provider.model.DatabasePoolSettings;
23 | import io.metaloom.test.container.provider.server.DatabaseProviderServer;
24 | import io.metaloom.test.container.provider.server.ServerApi;
25 | import io.metaloom.test.container.provider.server.ServerConfiguration;
26 | import io.vertx.core.Vertx;
27 | import io.vertx.core.http.HttpServer;
28 | import io.vertx.core.json.Json;
29 |
30 | @Testcontainers
31 | public class ProviderClientServerTest {
32 |
33 | @Container
34 | public static PostgreSQLPoolContainer db = new PostgreSQLPoolContainer().withTmpFs(128);
35 |
36 | private static ProviderClient client;
37 | private static DatabaseProviderServer server;
38 |
39 | @BeforeAll
40 | public static void setup() throws Exception {
41 | Vertx vertx = Vertx.vertx();
42 | DatabasePoolManager manager = new DatabasePoolManager(vertx, null);
43 | ServerApi api = new ServerApi(manager);
44 | server = new DatabaseProviderServer(vertx, ServerConfiguration.create(0), manager, api);
45 | HttpServer httpServer = server.start().toCompletionStage().toCompletableFuture().get();
46 | client = new ProviderClient("localhost", httpServer.actualPort());
47 | }
48 |
49 | @AfterAll
50 | public static void tearDown() throws Exception {
51 | server.stop().toCompletionStage().toCompletableFuture().get();
52 | }
53 |
54 | @Test
55 | public void testSetupPool() throws Exception {
56 |
57 | CompletableFuture result = client.createPool("dummy", poolCreateRequest());
58 | DatabasePoolResponse response = result.get();
59 | System.out.println(Json.encodePrettily(response));
60 |
61 | Thread.sleep(2000);
62 | DatabasePoolResponse result2 = client.loadPool("dummy").get();
63 | System.out.println(Json.encodePrettily(result2));
64 |
65 | DatabasePoolListResponse list = client.listPools().get();
66 | assertEquals(1, list.getList().size());
67 |
68 | client.deletePool(response.getId()).get();
69 | assertEquals(0, client.listPools().get().getList().size());
70 | }
71 |
72 | @Test
73 | public void testAcquire() throws Exception {
74 | assertNotNull(client.createPool("dummy", poolCreateRequest()).get());
75 | Thread.sleep(2_000);
76 |
77 | ClientAllocation allocation = client.link("dummy", "testAcquire").get();
78 | assertNotNull(allocation.response());
79 | allocation.release();
80 | }
81 |
82 | @Test
83 | public void testAcquire2() throws Exception {
84 | client.link("dummy", "testAcquire2").get();
85 | Thread.sleep(2_000);
86 | }
87 |
88 | private DatabasePoolRequest poolCreateRequest() {
89 | DatabasePoolRequest request = new DatabasePoolRequest();
90 | DatabasePoolConnection connection = new DatabasePoolConnection().setHost("localhost")
91 | .setHost("localhost")
92 | .setPort(db.getPort())
93 | .setPassword("sa")
94 | .setUsername("sa")
95 | .setDatabase("postgres");
96 |
97 | DatabasePoolSettings settings = new DatabasePoolSettings()
98 | .setMinimum(10)
99 | .setMaximum(20)
100 | .setIncrement(5);
101 |
102 | request.setConnection(connection)
103 | .setSettings(settings)
104 | .setTemplateDatabaseName("postgres");
105 | return request;
106 | }
107 | }
108 |
--------------------------------------------------------------------------------
/server/src/test/java/io/metaloom/test/container/server/TestSQLHelper.java:
--------------------------------------------------------------------------------
1 | package io.metaloom.test.container.server;
2 |
3 | import static org.junit.Assert.assertEquals;
4 |
5 | import java.sql.Connection;
6 | import java.sql.DriverManager;
7 | import java.sql.PreparedStatement;
8 | import java.sql.SQLException;
9 | import java.sql.Statement;
10 |
11 | import org.testcontainers.containers.PostgreSQLContainer;
12 |
13 | public final class TestSQLHelper {
14 |
15 | private TestSQLHelper() {
16 | }
17 |
18 | private static final String CREATE_TABLE = """
19 | CREATE TABLE users
20 | (id INT PRIMARY KEY, name TEXT)
21 | """;
22 |
23 | private static final String INSERT_USER = "INSERT INTO users (id, name) VALUES (?, ?)";
24 |
25 | private static final String SELECT_USERS = "SELECT id, name from users";
26 |
27 | private static final String DELETE_USERS = "DELETE FROM users";
28 |
29 | public static final int USER_COUNT = 50_000;
30 |
31 | private static final String CREATE_DB = "CREATE DATABASE test_template";
32 |
33 | public static String setupTable(String jdbcUrl, String username, String password) throws SQLException {
34 | String dbName = "test_template";
35 | try (Connection connection = DriverManager.getConnection(jdbcUrl + "postgres", username, password)) {
36 | Statement statement1 = connection.createStatement();
37 | statement1.execute(CREATE_DB);
38 | }
39 | try (Connection connection = DriverManager.getConnection(jdbcUrl + dbName, username, password)) {
40 | Statement statement2 = connection.createStatement();
41 | statement2.execute(CREATE_TABLE);
42 | }
43 | return dbName;
44 | }
45 |
46 | public static void insertUsers(PostgreSQLContainer db, int id, String name) throws SQLException {
47 | try (Connection connection = DriverManager.getConnection(db.getJdbcUrl(), db.getUsername(), db.getPassword())) {
48 | for (int i = 1; i <= USER_COUNT; i++) {
49 | PreparedStatement statement = connection.prepareStatement(INSERT_USER);
50 | statement.setInt(1, id + i);
51 | statement.setString(2, name + "_" + i);
52 | assertEquals(1, statement.executeUpdate());
53 | }
54 | }
55 | }
56 |
57 | public static void deleteUsers(PostgreSQLContainer db) throws SQLException {
58 | try (Connection connection = DriverManager.getConnection(db.getJdbcUrl(), db.getUsername(), db.getPassword())) {
59 | PreparedStatement statement = connection.prepareStatement(DELETE_USERS);
60 | statement.executeUpdate();
61 | }
62 |
63 | }
64 |
65 | }
66 |
--------------------------------------------------------------------------------
/server/stop.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | for i in $(docker ps | awk '{print $1}') ; do docker rm -f $i ; done
4 |
--------------------------------------------------------------------------------
/server/test.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | IMAGE=metaloom/testdatabase-provider
4 | TAG=0.1.0-SNAPSHOT
5 |
6 | docker run --rm \
7 | --env "TESTDATABASE_PROVIDER_DATABASE_HOST=localhost" \
8 | --env "TESTDATABASE_PROVIDER_DATABASE_PORT=1234" \
9 | --env "TESTDATABASE_PROVIDER_DATABASE_USERNAME=sa" \
10 | --env "TESTDATABASE_PROVIDER_DATABASE_PASSWORD=sa" \
11 | $IMAGE:$TAG
12 |
--------------------------------------------------------------------------------