diff --git a/clouddriver-integration/clouddriver-integration.gradle b/clouddriver-integration/clouddriver-integration.gradle index d19d36f391..f00d3d628a 100644 --- a/clouddriver-integration/clouddriver-integration.gradle +++ b/clouddriver-integration/clouddriver-integration.gradle @@ -4,6 +4,7 @@ dependencies { testImplementation "org.junit.jupiter:junit-jupiter-api" testImplementation "org.slf4j:slf4j-api" testImplementation "org.testcontainers:testcontainers" + testImplementation "org.testcontainers:postgresql" testImplementation "org.testcontainers:junit-jupiter" testRuntimeOnly "ch.qos.logback:logback-classic" } diff --git a/clouddriver-integration/src/test/java/com/netflix/spinnaker/clouddriver/BaseContainerTest.java b/clouddriver-integration/src/test/java/com/netflix/spinnaker/clouddriver/BaseContainerTest.java new file mode 100644 index 0000000000..14f3686af3 --- /dev/null +++ b/clouddriver-integration/src/test/java/com/netflix/spinnaker/clouddriver/BaseContainerTest.java @@ -0,0 +1,90 @@ +/* + * Copyright 2024 Salesforce, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.netflix.spinnaker.clouddriver; + +import org.junit.jupiter.api.*; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.testcontainers.containers.GenericContainer; +import org.testcontainers.containers.Network; +import org.testcontainers.containers.wait.strategy.Wait; +import org.testcontainers.junit.jupiter.Testcontainers; +import org.testcontainers.utility.DockerImageName; + +import java.net.URI; +import java.net.http.HttpClient; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; +import java.time.Duration; +import java.util.Map; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assumptions.assumeTrue; + +@Testcontainers +public class BaseContainerTest { + + private static final Logger logger = LoggerFactory.getLogger(BaseContainerTest.class); + + protected static final Network network = Network.newNetwork(); + + protected static GenericContainer clouddriverContainer; + + @BeforeAll + static void setupInit() throws Exception { + String fullDockerImageName = System.getenv("FULL_DOCKER_IMAGE_NAME"); + + // Skip the tests if there's no docker image. This allows gradlew build to work. + assumeTrue(fullDockerImageName != null); + + DockerImageName dockerImageName = DockerImageName.parse(fullDockerImageName); + + clouddriverContainer = + new GenericContainer(dockerImageName) + .withNetwork(network) + .withExposedPorts(7002) + .waitingFor(Wait.forHealthcheck().withStartupTimeout(Duration.ofSeconds(90))); + + } + + @BeforeEach + void init(TestInfo testInfo) { + System.out.println("--------------- Test " + testInfo.getDisplayName()); + } + + void testHealthCheck() throws Exception { + // hit an arbitrary endpoint + HttpRequest request = + HttpRequest.newBuilder() + .uri( + new URI( + "http://" + + clouddriverContainer.getHost() + + ":" + + clouddriverContainer.getFirstMappedPort() + + "/health")) + .GET() + .build(); + + HttpClient client = HttpClient.newHttpClient(); + + HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); + assertThat(response).isNotNull(); + logger.info("response: {}, {}", response.statusCode(), response.body()); + assertThat(response.statusCode()).isEqualTo(200); + } +} diff --git a/clouddriver-integration/src/test/java/com/netflix/spinnaker/clouddriver/PostgresContainerTest.java b/clouddriver-integration/src/test/java/com/netflix/spinnaker/clouddriver/PostgresContainerTest.java new file mode 100644 index 0000000000..714489eb68 --- /dev/null +++ b/clouddriver-integration/src/test/java/com/netflix/spinnaker/clouddriver/PostgresContainerTest.java @@ -0,0 +1,104 @@ +/* + * Copyright 2024 Salesforce, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.netflix.spinnaker.clouddriver; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.testcontainers.containers.PostgreSQLContainer; +import org.testcontainers.containers.output.Slf4jLogConsumer; +import org.testcontainers.junit.jupiter.Testcontainers; + +import java.util.Map; + +@Testcontainers +public class PostgresContainerTest extends BaseContainerTest { + private static final String POSTGRES_NETWORK_ALIAS = "postgresHost"; + + private static final int POSTGRES_PORT = 5432; + + private static final Logger logger = LoggerFactory.getLogger(PostgresContainerTest.class); + + private static final PostgreSQLContainer postgres = + new PostgreSQLContainer<>("postgres:15") + .withDatabaseName("clouddriver") + .withUsername("postgres") + .withPassword("postgres") + .withNetwork(network) + .withNetworkAliases(POSTGRES_NETWORK_ALIAS) + .withInitScript("postgres_init.sql"); + + private static String jdbcUrl = ""; + + @BeforeAll + static void setup() throws Exception { + postgres.start(); + jdbcUrl = String.format("jdbc:postgresql://%s:%d/clouddriver", POSTGRES_NETWORK_ALIAS, POSTGRES_PORT); + clouddriverContainer + .dependsOn(postgres) + .withEnv("SPRING_APPLICATION_JSON", getSpringApplicationJson()) + .start(); + + Slf4jLogConsumer logConsumer = new Slf4jLogConsumer(logger); + clouddriverContainer.start(); + clouddriverContainer.followOutput(logConsumer); + } + + private static String getSpringApplicationJson() throws JsonProcessingException { + logger.info("***** jdbcUrl: '{}'", jdbcUrl); + Map connectionPool = + Map.of("jdbcUrl", jdbcUrl, "user", "clouddriver_service", "password", "c10uddriver"); + Map migration = + Map.of("jdbcUrl", jdbcUrl, "user", "clouddriver_migrate", "password", "c10uddriver"); + + Map properties = + Map.of( + "sql.enabled", + "true", + "services.fiat.baseUrl", + "http://nowhere", + "sql.connectionPool", + connectionPool, + "redis.enabled", + "false", + "sql.migration", + migration); + ObjectMapper mapper = new ObjectMapper(); + return mapper.writeValueAsString(properties); + } + + @AfterAll + static void cleanupOnce() { + if (clouddriverContainer != null) { + clouddriverContainer.stop(); + } + + if (postgres != null) { + postgres.stop(); + } + } + + @Test + void testHealthCheckWithPostgres() throws Exception { + super.testHealthCheck(); + } + +} diff --git a/clouddriver-integration/src/test/java/com/netflix/spinnaker/clouddriver/RedisContainerTest.java b/clouddriver-integration/src/test/java/com/netflix/spinnaker/clouddriver/RedisContainerTest.java new file mode 100644 index 0000000000..f84344b764 --- /dev/null +++ b/clouddriver-integration/src/test/java/com/netflix/spinnaker/clouddriver/RedisContainerTest.java @@ -0,0 +1,84 @@ +/* + * Copyright 2024 Salesforce, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.netflix.spinnaker.clouddriver; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.testcontainers.containers.GenericContainer; +import org.testcontainers.containers.output.Slf4jLogConsumer; +import org.testcontainers.junit.jupiter.Testcontainers; +import org.testcontainers.utility.DockerImageName; + +import java.util.Map; + +@Testcontainers +public class RedisContainerTest extends BaseContainerTest { + private static final Logger logger = LoggerFactory.getLogger(RedisContainerTest.class); + + private static final String REDIS_NETWORK_ALIAS = "redisHost"; + + private static final int REDIS_PORT = 6379; + + private static final GenericContainer redis = + new GenericContainer(DockerImageName.parse("library/redis:5-alpine")) + .withNetwork(network) + .withNetworkAliases(REDIS_NETWORK_ALIAS) + .withExposedPorts(REDIS_PORT); + + @BeforeAll + static void setup() throws Exception { + redis.start(); + clouddriverContainer + .dependsOn(redis) + .withEnv("SPRING_APPLICATION_JSON", getSpringApplicationJson()) + .start(); + + Slf4jLogConsumer logConsumer = new Slf4jLogConsumer(logger); + clouddriverContainer.start(); + clouddriverContainer.followOutput(logConsumer); + } + + private static String getSpringApplicationJson() throws JsonProcessingException { + String redisUrl = "redis://" + REDIS_NETWORK_ALIAS + ":" + REDIS_PORT; + logger.info("redisUrl: '{}'", redisUrl); + Map properties = + Map.of("redis.connection", redisUrl, "services.fiat.baseUrl", "http://nowhere"); + ObjectMapper mapper = new ObjectMapper(); + return mapper.writeValueAsString(properties); + } + + @AfterAll + static void cleanupOnce() { + if (clouddriverContainer != null) { + clouddriverContainer.stop(); + } + + if (redis != null) { + redis.stop(); + } + } + + @Test + void testHealthCheckWithRedis() throws Exception { + super.testHealthCheck(); + } +} diff --git a/clouddriver-integration/src/test/java/com/netflix/spinnaker/clouddriver/StandaloneContainerTest.java b/clouddriver-integration/src/test/java/com/netflix/spinnaker/clouddriver/StandaloneContainerTest.java deleted file mode 100644 index 59fe02b29d..0000000000 --- a/clouddriver-integration/src/test/java/com/netflix/spinnaker/clouddriver/StandaloneContainerTest.java +++ /dev/null @@ -1,133 +0,0 @@ -/* - * Copyright 2024 Salesforce, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.netflix.spinnaker.clouddriver; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.jupiter.api.Assumptions.assumeTrue; - -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; -import java.net.URI; -import java.net.http.HttpClient; -import java.net.http.HttpRequest; -import java.net.http.HttpResponse; -import java.time.Duration; -import java.util.Map; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.TestInfo; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.testcontainers.containers.GenericContainer; -import org.testcontainers.containers.Network; -import org.testcontainers.containers.output.Slf4jLogConsumer; -import org.testcontainers.containers.wait.strategy.Wait; -import org.testcontainers.junit.jupiter.Testcontainers; -import org.testcontainers.utility.DockerImageName; - -@Testcontainers -class StandaloneContainerTest { - - private static final String REDIS_NETWORK_ALIAS = "redisHost"; - - private static final int REDIS_PORT = 6379; - - private static final Logger logger = LoggerFactory.getLogger(StandaloneContainerTest.class); - - private static final Network network = Network.newNetwork(); - - private static final GenericContainer redis = - new GenericContainer(DockerImageName.parse("library/redis:5-alpine")) - .withNetwork(network) - .withNetworkAliases(REDIS_NETWORK_ALIAS) - .withExposedPorts(REDIS_PORT); - - private static GenericContainer clouddriverContainer; - - @BeforeAll - static void setupOnce() throws Exception { - String fullDockerImageName = System.getenv("FULL_DOCKER_IMAGE_NAME"); - - // Skip the tests if there's no docker image. This allows gradlew build to work. - assumeTrue(fullDockerImageName != null); - - redis.start(); - - DockerImageName dockerImageName = DockerImageName.parse(fullDockerImageName); - - clouddriverContainer = - new GenericContainer(dockerImageName) - .withNetwork(network) - .withExposedPorts(7002) - .dependsOn(redis) - .waitingFor(Wait.forHealthcheck().withStartupTimeout(Duration.ofSeconds(90))) - .withEnv("SPRING_APPLICATION_JSON", getSpringApplicationJson()); - - Slf4jLogConsumer logConsumer = new Slf4jLogConsumer(logger); - clouddriverContainer.start(); - clouddriverContainer.followOutput(logConsumer); - } - - private static String getSpringApplicationJson() throws JsonProcessingException { - String redisUrl = "redis://" + REDIS_NETWORK_ALIAS + ":" + REDIS_PORT; - logger.info("redisUrl: '{}'", redisUrl); - Map properties = - Map.of("redis.connection", redisUrl, "services.fiat.baseUrl", "http://nowhere"); - ObjectMapper mapper = new ObjectMapper(); - return mapper.writeValueAsString(properties); - } - - @AfterAll - static void cleanupOnce() { - if (clouddriverContainer != null) { - clouddriverContainer.stop(); - } - - if (redis != null) { - redis.stop(); - } - } - - @BeforeEach - void init(TestInfo testInfo) { - System.out.println("--------------- Test " + testInfo.getDisplayName()); - } - - @Test - void testHealthCheck() throws Exception { - // hit an arbitrary endpoint - HttpRequest request = - HttpRequest.newBuilder() - .uri( - new URI( - "http://" - + clouddriverContainer.getHost() - + ":" - + clouddriverContainer.getFirstMappedPort() - + "/health")) - .GET() - .build(); - - HttpClient client = HttpClient.newHttpClient(); - - HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); - assertThat(response).isNotNull(); - logger.info("response: {}, {}", response.statusCode(), response.body()); - assertThat(response.statusCode()).isEqualTo(200); - } -} diff --git a/clouddriver-integration/src/test/resources/postgres_init.sql b/clouddriver-integration/src/test/resources/postgres_init.sql new file mode 100644 index 0000000000..089e3669f3 --- /dev/null +++ b/clouddriver-integration/src/test/resources/postgres_init.sql @@ -0,0 +1,10 @@ +CREATE USER clouddriver_service with PASSWORD 'c10uddriver'; +CREATE USER clouddriver_migrate with PASSWORD 'c10uddriver'; + +grant create on schema public to clouddriver_service; +grant create on schema public to clouddriver_migrate; + +GRANT pg_read_all_data TO clouddriver_service; +GRANT pg_write_all_data TO clouddriver_service; + +