From 6feb4e64a99a2e8b10dc2ab01dfc447d68b88142 Mon Sep 17 00:00:00 2001 From: Kiran Godishala <53332225+kirangodishala@users.noreply.github.com> Date: Mon, 10 Jun 2024 22:06:40 +0530 Subject: [PATCH] fix(tests): add new containerized integration tests to run orca with mysql and postgres (#4736) (cherry picked from commit 6dcc52281f050d9b77e6f0bef58bf480cfa60718) --- orca-integration/orca-integration.gradle | 5 + ...tainerTest.java => BaseContainerTest.java} | 72 ++-------- .../spinnaker/orca/MySqlContainerTest.java | 118 ++++++++++++++++ .../spinnaker/orca/PostgresContainerTest.java | 126 ++++++++++++++++++ .../spinnaker/orca/RedisContainerTest.java | 86 ++++++++++++ .../src/test/resources/mysql_init.sql | 28 ++++ .../src/test/resources/postgres_init.sql | 24 ++++ 7 files changed, 401 insertions(+), 58 deletions(-) rename orca-integration/src/test/java/com/netflix/spinnaker/orca/{StandaloneContainerTest.java => BaseContainerTest.java} (57%) create mode 100644 orca-integration/src/test/java/com/netflix/spinnaker/orca/MySqlContainerTest.java create mode 100644 orca-integration/src/test/java/com/netflix/spinnaker/orca/PostgresContainerTest.java create mode 100644 orca-integration/src/test/java/com/netflix/spinnaker/orca/RedisContainerTest.java create mode 100644 orca-integration/src/test/resources/mysql_init.sql create mode 100644 orca-integration/src/test/resources/postgres_init.sql diff --git a/orca-integration/orca-integration.gradle b/orca-integration/orca-integration.gradle index ed33fbbcdf..132d5d3882 100644 --- a/orca-integration/orca-integration.gradle +++ b/orca-integration/orca-integration.gradle @@ -3,10 +3,15 @@ dependencies { testImplementation "org.assertj:assertj-core" testImplementation "org.junit.jupiter:junit-jupiter-api" testImplementation "org.slf4j:slf4j-api" + testImplementation "org.testcontainers:mysql" + testImplementation "org.testcontainers:postgresql" testImplementation "org.testcontainers:testcontainers" testImplementation "org.testcontainers:junit-jupiter" testRuntimeOnly "ch.qos.logback:logback-classic" + testRuntimeOnly "mysql:mysql-connector-java" testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine" + testRuntimeOnly "org.postgresql:postgresql" + } test.configure { diff --git a/orca-integration/src/test/java/com/netflix/spinnaker/orca/StandaloneContainerTest.java b/orca-integration/src/test/java/com/netflix/spinnaker/orca/BaseContainerTest.java similarity index 57% rename from orca-integration/src/test/java/com/netflix/spinnaker/orca/StandaloneContainerTest.java rename to orca-integration/src/test/java/com/netflix/spinnaker/orca/BaseContainerTest.java index 7127d44ad7..0f8664c961 100644 --- a/orca-integration/src/test/java/com/netflix/spinnaker/orca/StandaloneContainerTest.java +++ b/orca-integration/src/test/java/com/netflix/spinnaker/orca/BaseContainerTest.java @@ -18,97 +18,53 @@ 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 { +class BaseContainerTest { - private static final String REDIS_NETWORK_ALIAS = "redisHost"; + private static final Logger logger = LoggerFactory.getLogger(BaseContainerTest.class); - private static final int REDIS_PORT = 6379; + protected final Network network = Network.newNetwork(); - private static final Logger logger = LoggerFactory.getLogger(StandaloneContainerTest.class); + private static final int ORCA_PORT = 8083; - private static final Network network = Network.newNetwork(); + protected GenericContainer orcaContainer; - 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 orcaContainer; + private static DockerImageName dockerImageName; @BeforeAll - static void setupOnce() throws Exception { + static void setupInit() { 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); - - orcaContainer = - new GenericContainer(dockerImageName) - .withNetwork(network) - .withExposedPorts(8083) - .dependsOn(redis) - .waitingFor(Wait.forHealthcheck().withStartupTimeout(Duration.ofSeconds(90))) - .withEnv("SPRING_APPLICATION_JSON", getSpringApplicationJson()); - - Slf4jLogConsumer logConsumer = new Slf4jLogConsumer(logger); - orcaContainer.start(); - orcaContainer.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 (orcaContainer != null) { - orcaContainer.stop(); - } - - if (redis != null) { - redis.stop(); - } + dockerImageName = DockerImageName.parse(fullDockerImageName); } @BeforeEach - void init(TestInfo testInfo) { + void init(TestInfo testInfo) throws Exception { System.out.println("--------------- Test " + testInfo.getDisplayName()); + orcaContainer = + new GenericContainer(dockerImageName) + .withNetwork(network) + .withExposedPorts(ORCA_PORT) + .waitingFor(Wait.forHealthcheck().withStartupTimeout(Duration.ofSeconds(120))); } - @Test void testHealthCheck() throws Exception { // hit an arbitrary endpoint HttpRequest request = diff --git a/orca-integration/src/test/java/com/netflix/spinnaker/orca/MySqlContainerTest.java b/orca-integration/src/test/java/com/netflix/spinnaker/orca/MySqlContainerTest.java new file mode 100644 index 0000000000..f07442c1fe --- /dev/null +++ b/orca-integration/src/test/java/com/netflix/spinnaker/orca/MySqlContainerTest.java @@ -0,0 +1,118 @@ +/* + * 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.orca; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import java.util.Map; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInstance; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.testcontainers.containers.MySQLContainer; +import org.testcontainers.containers.output.Slf4jLogConsumer; +import org.testcontainers.junit.jupiter.Testcontainers; + +@TestInstance(TestInstance.Lifecycle.PER_CLASS) +@Testcontainers +public class MySqlContainerTest extends BaseContainerTest { + + private static final Logger logger = LoggerFactory.getLogger(MySqlContainerTest.class); + + private static final String MYSQL_NETWORK_ALIAS = "mysqlHost"; + + private static final int MYSQL_PORT = 3306; + + private MySQLContainer mysql; + + private String jdbcUrl = ""; + + @BeforeEach + void setup() throws Exception { + mysql = + new MySQLContainer<>("mysql:8.0.37") + .withDatabaseName("orca") + .withUsername("root") + .withPassword("root") + .withNetwork(network) + .withNetworkAliases(MYSQL_NETWORK_ALIAS) + .withInitScript("mysql_init.sql"); + mysql.start(); + jdbcUrl = String.format("jdbc:mysql://%s:%d/orca", MYSQL_NETWORK_ALIAS, MYSQL_PORT); + orcaContainer + .dependsOn(mysql) + .withEnv("SPRING_APPLICATION_JSON", getSpringApplicationJson()) + .start(); + + Slf4jLogConsumer logConsumer = new Slf4jLogConsumer(logger); + orcaContainer.followOutput(logConsumer); + } + + private String getSpringApplicationJson() throws JsonProcessingException { + logger.info("--------- jdbcUrl: '{}'", jdbcUrl); + Map connectionPool = + Map.of("jdbcUrl", jdbcUrl, "user", "orca_service", "password", "0rcaPassw0rd"); + Map migration = + Map.of("jdbcUrl", jdbcUrl, "user", "orca_migrate", "password", "0rcaPassw0rd"); + Map sql = Map.of("enabled", true); + Map redis = Map.of("enabled", false); + Map pendingExecutionService = Map.of("sql", sql, "redis", redis); + Map executionRepository = Map.of("sql", sql, "redis", redis); + Map keiko = Map.of("sql", sql, "redis", redis); + + Map properties = + Map.of( + "sql.enabled", + "true", + "services.fiat.baseUrl", + "http://nowhere", + "sql.connectionPool", + connectionPool, + "redis.enabled", + "false", + "sql.migration", + migration, + "executionRepository", + executionRepository, + "keiko.queue", + keiko, + "queue.pendingExecutionService", + pendingExecutionService, + "monitor.activeExecutions.redis", + "false"); + ObjectMapper mapper = new ObjectMapper(); + return mapper.writeValueAsString(properties); + } + + @AfterAll + void cleanupOnce() { + if (orcaContainer != null) { + orcaContainer.stop(); + } + + if (mysql != null) { + mysql.stop(); + } + } + + @Test + void testHealthCheckWithMySql() throws Exception { + super.testHealthCheck(); + } +} diff --git a/orca-integration/src/test/java/com/netflix/spinnaker/orca/PostgresContainerTest.java b/orca-integration/src/test/java/com/netflix/spinnaker/orca/PostgresContainerTest.java new file mode 100644 index 0000000000..63c22af2fb --- /dev/null +++ b/orca-integration/src/test/java/com/netflix/spinnaker/orca/PostgresContainerTest.java @@ -0,0 +1,126 @@ +/* + * 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.orca; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import java.util.Map; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInstance; +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; + +@TestInstance(TestInstance.Lifecycle.PER_CLASS) +@Testcontainers +public class PostgresContainerTest extends BaseContainerTest { + + private static final Logger logger = LoggerFactory.getLogger(PostgresContainerTest.class); + + private static final String POSTGRES_NETWORK_ALIAS = "postgresHost"; + + private static final int POSTGRES_PORT = 5432; + + private PostgreSQLContainer postgres; + + private String jdbcUrl = ""; + + @BeforeEach + void setup() throws Exception { + postgres = + new PostgreSQLContainer<>("postgres:15") + .withDatabaseName("orca") + .withUsername("postgres") + .withPassword("postgres") + .withNetwork(network) + .withNetworkAliases(POSTGRES_NETWORK_ALIAS) + .withInitScript("postgres_init.sql"); + postgres.start(); + jdbcUrl = String.format("jdbc:postgresql://%s:%d/orca", POSTGRES_NETWORK_ALIAS, POSTGRES_PORT); + orcaContainer + .dependsOn(postgres) + .withEnv("SPRING_APPLICATION_JSON", getSpringApplicationJson()) + .start(); + + Slf4jLogConsumer logConsumer = new Slf4jLogConsumer(logger); + orcaContainer.followOutput(logConsumer); + } + + private String getSpringApplicationJson() throws JsonProcessingException { + logger.info("--------- jdbcUrl: '{}'", jdbcUrl); + Map connectionPool = + Map.of( + "dialect", + "POSTGRES", + "jdbcUrl", + jdbcUrl, + "user", + "orca_service", + "password", + "0rcaPassw0rd"); + Map migration = + Map.of("jdbcUrl", jdbcUrl, "user", "orca_migrate", "password", "0rcaPassw0rd"); + Map sql = Map.of("enabled", true); + Map redis = Map.of("enabled", false); + Map pendingExecutionService = Map.of("sql", sql, "redis", redis); + Map executionRepository = Map.of("sql", sql, "redis", redis); + Map keiko = Map.of("sql", sql, "redis", redis); + + Map properties = + Map.of( + "sql.enabled", + "true", + "services.fiat.baseUrl", + "http://nowhere", + "sql.connectionPool", + connectionPool, + "redis.enabled", + "false", + "sql.migration", + migration, + "executionRepository", + executionRepository, + "keiko.queue", + keiko, + "queue.pendingExecutionService", + pendingExecutionService, + "monitor.activeExecutions.redis", + "false"); + ObjectMapper mapper = new ObjectMapper(); + return mapper.writeValueAsString(properties); + } + + @AfterAll + void cleanupOnce() { + if (orcaContainer != null) { + orcaContainer.stop(); + } + + if (postgres != null) { + postgres.stop(); + } + } + + @Test + void testHealthCheckWithPostgres() throws Exception { + super.testHealthCheck(); + } +} diff --git a/orca-integration/src/test/java/com/netflix/spinnaker/orca/RedisContainerTest.java b/orca-integration/src/test/java/com/netflix/spinnaker/orca/RedisContainerTest.java new file mode 100644 index 0000000000..809a48ac50 --- /dev/null +++ b/orca-integration/src/test/java/com/netflix/spinnaker/orca/RedisContainerTest.java @@ -0,0 +1,86 @@ +/* + * Copyright 2024 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.orca; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import java.util.Map; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInstance; +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; + +@TestInstance(TestInstance.Lifecycle.PER_CLASS) +@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 GenericContainer redis; + + @BeforeEach + void setup() throws Exception { + redis = + new GenericContainer<>(DockerImageName.parse("library/redis:5-alpine")) + .withNetwork(network) + .withNetworkAliases(REDIS_NETWORK_ALIAS) + .withExposedPorts(REDIS_PORT); + redis.start(); + orcaContainer + .dependsOn(redis) + .withEnv("SPRING_APPLICATION_JSON", getSpringApplicationJson()) + .start(); + + Slf4jLogConsumer logConsumer = new Slf4jLogConsumer(logger); + orcaContainer.followOutput(logConsumer); + } + + private 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 + void cleanupOnce() { + if (orcaContainer != null) { + orcaContainer.stop(); + } + + if (redis != null) { + redis.stop(); + } + } + + @Test + void testHealthCheckWithRedis() throws Exception { + super.testHealthCheck(); + } +} diff --git a/orca-integration/src/test/resources/mysql_init.sql b/orca-integration/src/test/resources/mysql_init.sql new file mode 100644 index 0000000000..464edf9bca --- /dev/null +++ b/orca-integration/src/test/resources/mysql_init.sql @@ -0,0 +1,28 @@ +/* + * 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. + */ + +CREATE USER 'orca_service'@'%' IDENTIFIED BY '0rcaPassw0rd'; +CREATE USER 'orca_migrate'@'%' IDENTIFIED BY '0rcaPassw0rd'; + +GRANT +SELECT, INSERT, UPDATE, DELETE, CREATE, EXECUTE, SHOW VIEW +ON *.* + TO 'orca_service'@'%'; + +GRANT +SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, REFERENCES, INDEX, ALTER, LOCK TABLES, EXECUTE, SHOW VIEW +ON *.* + TO 'orca_migrate'@'%'; diff --git a/orca-integration/src/test/resources/postgres_init.sql b/orca-integration/src/test/resources/postgres_init.sql new file mode 100644 index 0000000000..1f3046f259 --- /dev/null +++ b/orca-integration/src/test/resources/postgres_init.sql @@ -0,0 +1,24 @@ +/* + * 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. + */ + +CREATE USER orca_service with PASSWORD '0rcaPassw0rd'; +CREATE USER orca_migrate with PASSWORD '0rcaPassw0rd'; + +grant create on schema public to orca_service; +grant create on schema public to orca_migrate; + +GRANT pg_read_all_data TO orca_service; +GRANT pg_write_all_data TO orca_service;