diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index d6e438c1f5..af85971da4 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -37,6 +37,19 @@ jobs: env: ORG_GRADLE_PROJECT_version: ${{ steps.build_variables.outputs.VERSION }} run: ./gradlew build --stacktrace ${{ steps.build_variables.outputs.REPO }}-web:installDist + - name: Build local slim container image for testing + uses: docker/build-push-action@v5 + with: + context: . + file: Dockerfile.slim + load: true + platforms: local + tags: | + "${{ steps.build_variables.outputs.REPO }}:${{ steps.build_variables.outputs.VERSION }}-unvalidated" + - name: Test local slim container image + env: + FULL_DOCKER_IMAGE_NAME: "${{ steps.build_variables.outputs.REPO }}:${{ steps.build_variables.outputs.VERSION }}-unvalidated" + run: ./gradlew ${{ steps.build_variables.outputs.REPO }}-integration:test - name: Login to GAR # Only run this on repositories in the 'spinnaker' org, not on forks. if: startsWith(github.repository, 'spinnaker/') diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index 4a01dca2ab..2b068dfafd 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -51,3 +51,16 @@ jobs: tags: | "${{ env.CONTAINER_REGISTRY }}/${{ steps.build_variables.outputs.REPO }}:latest-ubuntu" "${{ env.CONTAINER_REGISTRY }}/${{ steps.build_variables.outputs.REPO }}:${{ steps.build_variables.outputs.VERSION }}-ubuntu" + - name: Build local slim container image for testing + uses: docker/build-push-action@v5 + with: + context: . + file: Dockerfile.slim + load: true + platforms: local + tags: | + "${{ steps.build_variables.outputs.REPO }}:${{ steps.build_variables.outputs.VERSION }}" + - name: Test local slim container image + env: + FULL_DOCKER_IMAGE_NAME: "${{ steps.build_variables.outputs.REPO }}:${{ steps.build_variables.outputs.VERSION }}" + run: ./gradlew ${{ steps.build_variables.outputs.REPO }}-integration:test diff --git a/Dockerfile.slim b/Dockerfile.slim index dd5af23566..b2cd71021e 100644 --- a/Dockerfile.slim +++ b/Dockerfile.slim @@ -34,4 +34,6 @@ RUN adduser -S -G spinnaker -u 1000 spinnaker COPY halyard-web/build/install/halyard /opt/halyard USER spinnaker +HEALTHCHECK CMD curl http://localhost:8064/health | grep UP || exit 1 + CMD ["/opt/halyard/bin/halyard"] diff --git a/Dockerfile.ubuntu b/Dockerfile.ubuntu index 22fcbbd3d3..69be710006 100644 --- a/Dockerfile.ubuntu +++ b/Dockerfile.ubuntu @@ -30,4 +30,6 @@ RUN adduser --system --uid 1000 --group spinnaker COPY halyard-web/build/install/halyard /opt/halyard USER spinnaker +HEALTHCHECK CMD curl http://localhost:8064/health | grep UP || exit 1 + CMD ["/opt/halyard/bin/halyard"] diff --git a/halyard-config/src/main/java/com/netflix/spinnaker/halyard/config/services/v1/DeploymentService.java b/halyard-config/src/main/java/com/netflix/spinnaker/halyard/config/services/v1/DeploymentService.java index d1375b25d7..822c21cd4f 100644 --- a/halyard-config/src/main/java/com/netflix/spinnaker/halyard/config/services/v1/DeploymentService.java +++ b/halyard-config/src/main/java/com/netflix/spinnaker/halyard/config/services/v1/DeploymentService.java @@ -29,6 +29,7 @@ import com.netflix.spinnaker.halyard.core.problem.v1.ProblemSet; import java.util.List; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Component; /** @@ -43,7 +44,7 @@ public class DeploymentService { @Autowired private HalconfigParser halconfigParser; - @Autowired private PersistentStorageService storageService; + @Lazy @Autowired private PersistentStorageService storageService; public void setDeploymentConfiguration( String deploymentName, DeploymentConfiguration deploymentConfiguration) { diff --git a/halyard-config/src/main/java/com/netflix/spinnaker/halyard/config/validate/v1/ValidatorCollection.java b/halyard-config/src/main/java/com/netflix/spinnaker/halyard/config/validate/v1/ValidatorCollection.java index 5b35555aa8..9632269046 100644 --- a/halyard-config/src/main/java/com/netflix/spinnaker/halyard/config/validate/v1/ValidatorCollection.java +++ b/halyard-config/src/main/java/com/netflix/spinnaker/halyard/config/validate/v1/ValidatorCollection.java @@ -26,6 +26,7 @@ import java.util.List; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Component; /** @@ -35,6 +36,7 @@ @Slf4j @Component public class ValidatorCollection { + @Lazy @Autowired(required = false) private List validators = new ArrayList<>(); diff --git a/halyard-config/src/main/java/com/netflix/spinnaker/halyard/config/validate/v1/providers/ecs/EcsAccountValidator.java b/halyard-config/src/main/java/com/netflix/spinnaker/halyard/config/validate/v1/providers/ecs/EcsAccountValidator.java index 661fb12623..f0aba6d8e2 100644 --- a/halyard-config/src/main/java/com/netflix/spinnaker/halyard/config/validate/v1/providers/ecs/EcsAccountValidator.java +++ b/halyard-config/src/main/java/com/netflix/spinnaker/halyard/config/validate/v1/providers/ecs/EcsAccountValidator.java @@ -6,7 +6,6 @@ import com.netflix.spinnaker.halyard.config.problem.v1.ConfigProblemSetBuilder; import com.netflix.spinnaker.halyard.config.services.v1.AccountService; import com.netflix.spinnaker.halyard.config.services.v1.ConfigService; -import com.netflix.spinnaker.halyard.config.services.v1.ProviderService; import com.netflix.spinnaker.halyard.core.problem.v1.Problem.Severity; import java.util.List; import java.util.Optional; @@ -15,8 +14,6 @@ @Component public class EcsAccountValidator extends Validator { - @Autowired ProviderService providerService; - @Autowired AccountService accountService; @Autowired ConfigService configService; diff --git a/halyard-core/src/main/java/com/netflix/spinnaker/halyard/core/registry/v1/GoogleProfileReader.java b/halyard-core/src/main/java/com/netflix/spinnaker/halyard/core/registry/v1/GoogleProfileReader.java index 8ab634737b..9024dc8060 100644 --- a/halyard-core/src/main/java/com/netflix/spinnaker/halyard/core/registry/v1/GoogleProfileReader.java +++ b/halyard-core/src/main/java/com/netflix/spinnaker/halyard/core/registry/v1/GoogleProfileReader.java @@ -19,22 +19,15 @@ package com.netflix.spinnaker.halyard.core.registry.v1; import com.fasterxml.jackson.databind.ObjectMapper; -import com.google.api.client.http.HttpRequestInitializer; -import com.google.api.client.json.JsonFactory; -import com.google.api.client.json.jackson2.JacksonFactory; import com.google.api.services.storage.Storage; -import com.google.api.services.storage.StorageScopes; -import com.netflix.spinnaker.halyard.core.provider.v1.google.GoogleCredentials; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; -import java.util.Collections; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.context.ApplicationContext; -import org.springframework.context.annotation.Bean; import org.springframework.stereotype.Component; import org.yaml.snakeyaml.Yaml; @@ -56,16 +49,6 @@ private Yaml getYamlParser() { return applicationContext.getBean(Yaml.class); } - @Bean - public Storage applicationDefaultGoogleStorage() { - return createGoogleStorage(true); - } - - @Bean - public Storage unauthenticatedGoogleStorage() { - return createGoogleStorage(false); - } - public InputStream readProfile(String artifactName, String version, String profileName) throws IOException { String path = profilePath(artifactName, version, profileName); @@ -118,36 +101,4 @@ private InputStream getContents(String objectName) throws IOException { return new ByteArrayInputStream(output.toByteArray()); } - - private Storage createGoogleStorage(boolean useApplicationDefaultCreds) { - JsonFactory jsonFactory = JacksonFactory.getDefaultInstance(); - String applicationName = "Spinnaker/Halyard"; - HttpRequestInitializer requestInitializer = null; - - if (useApplicationDefaultCreds) { - try { - com.google.auth.oauth2.GoogleCredentials credentials = - com.google.auth.oauth2.GoogleCredentials.getApplicationDefault(); - if (credentials.createScopedRequired()) { - credentials = - credentials.createScoped( - Collections.singleton(StorageScopes.DEVSTORAGE_FULL_CONTROL)); - } - requestInitializer = GoogleCredentials.setHttpTimeout(credentials); - log.info("Loaded application default credential for reading BOMs & profiles."); - } catch (Exception e) { - log.debug( - "No application default credential could be loaded for reading BOMs & profiles. Continuing unauthenticated: {}", - e.getMessage()); - } - } - if (requestInitializer == null) { - requestInitializer = GoogleCredentials.retryRequestInitializer(); - } - - return new Storage.Builder( - GoogleCredentials.buildHttpTransport(), jsonFactory, requestInitializer) - .setApplicationName(applicationName) - .build(); - } } diff --git a/halyard-core/src/main/java/com/netflix/spinnaker/halyard/core/registry/v1/GoogleProfileReaderConfig.java b/halyard-core/src/main/java/com/netflix/spinnaker/halyard/core/registry/v1/GoogleProfileReaderConfig.java new file mode 100644 index 0000000000..213df27433 --- /dev/null +++ b/halyard-core/src/main/java/com/netflix/spinnaker/halyard/core/registry/v1/GoogleProfileReaderConfig.java @@ -0,0 +1,78 @@ +/* + * Copyright 2017 Google, 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.halyard.core.registry.v1; + +import com.google.api.client.http.HttpRequestInitializer; +import com.google.api.client.json.JsonFactory; +import com.google.api.client.json.jackson2.JacksonFactory; +import com.google.api.services.storage.Storage; +import com.google.api.services.storage.StorageScopes; +import com.netflix.spinnaker.halyard.core.provider.v1.google.GoogleCredentials; +import java.util.Collections; +import lombok.extern.slf4j.Slf4j; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +@ConditionalOnProperty("spinnaker.config.input.gcs.enabled") +@Slf4j +public class GoogleProfileReaderConfig { + @Bean + public Storage applicationDefaultGoogleStorage() { + return createGoogleStorage(true); + } + + @Bean + public Storage unauthenticatedGoogleStorage() { + return createGoogleStorage(false); + } + + private Storage createGoogleStorage(boolean useApplicationDefaultCreds) { + JsonFactory jsonFactory = JacksonFactory.getDefaultInstance(); + String applicationName = "Spinnaker/Halyard"; + HttpRequestInitializer requestInitializer = null; + + if (useApplicationDefaultCreds) { + try { + com.google.auth.oauth2.GoogleCredentials credentials = + com.google.auth.oauth2.GoogleCredentials.getApplicationDefault(); + if (credentials.createScopedRequired()) { + credentials = + credentials.createScoped( + Collections.singleton(StorageScopes.DEVSTORAGE_FULL_CONTROL)); + } + requestInitializer = GoogleCredentials.setHttpTimeout(credentials); + log.info("Loaded application default credential for reading BOMs & profiles."); + } catch (Exception e) { + log.debug( + "No application default credential could be loaded for reading BOMs & profiles. Continuing unauthenticated: {}", + e.getMessage()); + } + } + if (requestInitializer == null) { + requestInitializer = GoogleCredentials.retryRequestInitializer(); + } + + return new Storage.Builder( + GoogleCredentials.buildHttpTransport(), jsonFactory, requestInitializer) + .setApplicationName(applicationName) + .build(); + } +} diff --git a/halyard-deploy/src/main/java/com/netflix/spinnaker/halyard/deploy/spinnaker/v1/service/SpinnakerMonitoringDaemonService.java b/halyard-deploy/src/main/java/com/netflix/spinnaker/halyard/deploy/spinnaker/v1/service/SpinnakerMonitoringDaemonService.java index fe86dd5ad5..7c25a6f537 100644 --- a/halyard-deploy/src/main/java/com/netflix/spinnaker/halyard/deploy/spinnaker/v1/service/SpinnakerMonitoringDaemonService.java +++ b/halyard-deploy/src/main/java/com/netflix/spinnaker/halyard/deploy/spinnaker/v1/service/SpinnakerMonitoringDaemonService.java @@ -47,8 +47,6 @@ public abstract class SpinnakerMonitoringDaemonService @Autowired MetricRegistryProfileFactoryBuilder metricRegistryProfileFactoryBuilder; - @Autowired List services; - @Override public SpinnakerArtifact getArtifact() { return SpinnakerArtifact.SPINNAKER_MONITORING_DAEMON; diff --git a/halyard-deploy/src/main/java/com/netflix/spinnaker/halyard/deploy/spinnaker/v1/service/distributed/google/GoogleClouddriverBootstrapService.java b/halyard-deploy/src/main/java/com/netflix/spinnaker/halyard/deploy/spinnaker/v1/service/distributed/google/GoogleClouddriverBootstrapService.java index d585f9b345..749c08a37d 100644 --- a/halyard-deploy/src/main/java/com/netflix/spinnaker/halyard/deploy/spinnaker/v1/service/distributed/google/GoogleClouddriverBootstrapService.java +++ b/halyard-deploy/src/main/java/com/netflix/spinnaker/halyard/deploy/spinnaker/v1/service/distributed/google/GoogleClouddriverBootstrapService.java @@ -27,6 +27,7 @@ import lombok.EqualsAndHashCode; import lombok.experimental.Delegate; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Component; @EqualsAndHashCode(callSuper = true) @@ -37,7 +38,7 @@ public class GoogleClouddriverBootstrapService extends ClouddriverBootstrapServi final DeployPriority deployPriority = new DeployPriority(6); final boolean requiredToBootstrap = true; - @Delegate @Autowired GoogleDistributedServiceDelegate googleDistributedServiceDelegate; + @Lazy @Delegate @Autowired GoogleDistributedServiceDelegate googleDistributedServiceDelegate; @Override public List getSidecars(SpinnakerRuntimeSettings runtimeSettings) { diff --git a/halyard-deploy/src/main/java/com/netflix/spinnaker/halyard/deploy/spinnaker/v1/service/distributed/google/GoogleClouddriverService.java b/halyard-deploy/src/main/java/com/netflix/spinnaker/halyard/deploy/spinnaker/v1/service/distributed/google/GoogleClouddriverService.java index 7c41624f14..81c2d7c9ad 100644 --- a/halyard-deploy/src/main/java/com/netflix/spinnaker/halyard/deploy/spinnaker/v1/service/distributed/google/GoogleClouddriverService.java +++ b/halyard-deploy/src/main/java/com/netflix/spinnaker/halyard/deploy/spinnaker/v1/service/distributed/google/GoogleClouddriverService.java @@ -28,6 +28,7 @@ import lombok.EqualsAndHashCode; import lombok.experimental.Delegate; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Component; @EqualsAndHashCode(callSuper = true) @@ -38,7 +39,7 @@ public class GoogleClouddriverService extends ClouddriverService final DeployPriority deployPriority = new DeployPriority(4); final boolean requiredToBootstrap = false; - @Delegate @Autowired GoogleDistributedServiceDelegate googleDistributedServiceDelegate; + @Lazy @Delegate @Autowired GoogleDistributedServiceDelegate googleDistributedServiceDelegate; @Override public List getProfiles( diff --git a/halyard-deploy/src/main/java/com/netflix/spinnaker/halyard/deploy/spinnaker/v1/service/distributed/google/GoogleConsulServerService.java b/halyard-deploy/src/main/java/com/netflix/spinnaker/halyard/deploy/spinnaker/v1/service/distributed/google/GoogleConsulServerService.java index 2f88e1ef43..d35b1e0ca4 100644 --- a/halyard-deploy/src/main/java/com/netflix/spinnaker/halyard/deploy/spinnaker/v1/service/distributed/google/GoogleConsulServerService.java +++ b/halyard-deploy/src/main/java/com/netflix/spinnaker/halyard/deploy/spinnaker/v1/service/distributed/google/GoogleConsulServerService.java @@ -30,6 +30,7 @@ import lombok.EqualsAndHashCode; import lombok.experimental.Delegate; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Component; @EqualsAndHashCode(callSuper = true) @@ -37,7 +38,7 @@ @Data public class GoogleConsulServerService extends ConsulServerService implements GoogleDistributedService { - @Delegate @Autowired GoogleDistributedServiceDelegate googleDistributedServiceDelegate; + @Lazy @Delegate @Autowired GoogleDistributedServiceDelegate googleDistributedServiceDelegate; @Override public String getDefaultInstanceType() { diff --git a/halyard-deploy/src/main/java/com/netflix/spinnaker/halyard/deploy/spinnaker/v1/service/distributed/google/GoogleDeckService.java b/halyard-deploy/src/main/java/com/netflix/spinnaker/halyard/deploy/spinnaker/v1/service/distributed/google/GoogleDeckService.java index 8f8225f671..608bebf66e 100644 --- a/halyard-deploy/src/main/java/com/netflix/spinnaker/halyard/deploy/spinnaker/v1/service/distributed/google/GoogleDeckService.java +++ b/halyard-deploy/src/main/java/com/netflix/spinnaker/halyard/deploy/spinnaker/v1/service/distributed/google/GoogleDeckService.java @@ -27,6 +27,7 @@ import lombok.EqualsAndHashCode; import lombok.experimental.Delegate; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Component; @EqualsAndHashCode(callSuper = true) @@ -37,7 +38,7 @@ public class GoogleDeckService extends DeckService final DeployPriority deployPriority = new DeployPriority(4); final boolean requiredToBootstrap = false; - @Delegate @Autowired GoogleDistributedServiceDelegate googleDistributedServiceDelegate; + @Lazy @Delegate @Autowired GoogleDistributedServiceDelegate googleDistributedServiceDelegate; @Override public List getSidecars(SpinnakerRuntimeSettings runtimeSettings) { diff --git a/halyard-deploy/src/main/java/com/netflix/spinnaker/halyard/deploy/spinnaker/v1/service/distributed/google/GoogleEchoService.java b/halyard-deploy/src/main/java/com/netflix/spinnaker/halyard/deploy/spinnaker/v1/service/distributed/google/GoogleEchoService.java index 530782c3be..d902aba7fc 100644 --- a/halyard-deploy/src/main/java/com/netflix/spinnaker/halyard/deploy/spinnaker/v1/service/distributed/google/GoogleEchoService.java +++ b/halyard-deploy/src/main/java/com/netflix/spinnaker/halyard/deploy/spinnaker/v1/service/distributed/google/GoogleEchoService.java @@ -27,6 +27,7 @@ import lombok.EqualsAndHashCode; import lombok.experimental.Delegate; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Component; @EqualsAndHashCode(callSuper = true) @@ -37,7 +38,7 @@ public class GoogleEchoService extends EchoService final DeployPriority deployPriority = new DeployPriority(4); final boolean requiredToBootstrap = false; - @Delegate @Autowired GoogleDistributedServiceDelegate googleDistributedServiceDelegate; + @Lazy @Delegate @Autowired GoogleDistributedServiceDelegate googleDistributedServiceDelegate; @Override public List getSidecars(SpinnakerRuntimeSettings runtimeSettings) { diff --git a/halyard-deploy/src/main/java/com/netflix/spinnaker/halyard/deploy/spinnaker/v1/service/distributed/google/GoogleFiatService.java b/halyard-deploy/src/main/java/com/netflix/spinnaker/halyard/deploy/spinnaker/v1/service/distributed/google/GoogleFiatService.java index 74f162e5cf..a3abda27dc 100644 --- a/halyard-deploy/src/main/java/com/netflix/spinnaker/halyard/deploy/spinnaker/v1/service/distributed/google/GoogleFiatService.java +++ b/halyard-deploy/src/main/java/com/netflix/spinnaker/halyard/deploy/spinnaker/v1/service/distributed/google/GoogleFiatService.java @@ -27,6 +27,7 @@ import lombok.EqualsAndHashCode; import lombok.experimental.Delegate; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Component; @EqualsAndHashCode(callSuper = true) @@ -37,7 +38,7 @@ public class GoogleFiatService extends FiatService final DeployPriority deployPriority = new DeployPriority(4); final boolean requiredToBootstrap = false; - @Delegate @Autowired GoogleDistributedServiceDelegate googleDistributedServiceDelegate; + @Lazy @Delegate @Autowired GoogleDistributedServiceDelegate googleDistributedServiceDelegate; @Override public List getSidecars(SpinnakerRuntimeSettings runtimeSettings) { diff --git a/halyard-deploy/src/main/java/com/netflix/spinnaker/halyard/deploy/spinnaker/v1/service/distributed/google/GoogleFront50Service.java b/halyard-deploy/src/main/java/com/netflix/spinnaker/halyard/deploy/spinnaker/v1/service/distributed/google/GoogleFront50Service.java index da6d14247a..5e4a510bdb 100644 --- a/halyard-deploy/src/main/java/com/netflix/spinnaker/halyard/deploy/spinnaker/v1/service/distributed/google/GoogleFront50Service.java +++ b/halyard-deploy/src/main/java/com/netflix/spinnaker/halyard/deploy/spinnaker/v1/service/distributed/google/GoogleFront50Service.java @@ -28,6 +28,7 @@ import lombok.EqualsAndHashCode; import lombok.experimental.Delegate; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Component; @EqualsAndHashCode(callSuper = true) @@ -38,7 +39,7 @@ public class GoogleFront50Service extends Front50Service final DeployPriority deployPriority = new DeployPriority(4); final boolean requiredToBootstrap = false; - @Delegate @Autowired GoogleDistributedServiceDelegate googleDistributedServiceDelegate; + @Lazy @Delegate @Autowired GoogleDistributedServiceDelegate googleDistributedServiceDelegate; @Override public List getProfiles( diff --git a/halyard-deploy/src/main/java/com/netflix/spinnaker/halyard/deploy/spinnaker/v1/service/distributed/google/GoogleGateService.java b/halyard-deploy/src/main/java/com/netflix/spinnaker/halyard/deploy/spinnaker/v1/service/distributed/google/GoogleGateService.java index affd5e655c..7809bd829b 100644 --- a/halyard-deploy/src/main/java/com/netflix/spinnaker/halyard/deploy/spinnaker/v1/service/distributed/google/GoogleGateService.java +++ b/halyard-deploy/src/main/java/com/netflix/spinnaker/halyard/deploy/spinnaker/v1/service/distributed/google/GoogleGateService.java @@ -27,6 +27,7 @@ import lombok.EqualsAndHashCode; import lombok.experimental.Delegate; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Component; @EqualsAndHashCode(callSuper = true) @@ -37,7 +38,7 @@ public class GoogleGateService extends GateService final DeployPriority deployPriority = new DeployPriority(4); final boolean requiredToBootstrap = false; - @Delegate @Autowired GoogleDistributedServiceDelegate googleDistributedServiceDelegate; + @Lazy @Delegate @Autowired GoogleDistributedServiceDelegate googleDistributedServiceDelegate; @Override public List getSidecars(SpinnakerRuntimeSettings runtimeSettings) { diff --git a/halyard-deploy/src/main/java/com/netflix/spinnaker/halyard/deploy/spinnaker/v1/service/distributed/google/GoogleIgorService.java b/halyard-deploy/src/main/java/com/netflix/spinnaker/halyard/deploy/spinnaker/v1/service/distributed/google/GoogleIgorService.java index 6a94c88922..c0df95d26d 100644 --- a/halyard-deploy/src/main/java/com/netflix/spinnaker/halyard/deploy/spinnaker/v1/service/distributed/google/GoogleIgorService.java +++ b/halyard-deploy/src/main/java/com/netflix/spinnaker/halyard/deploy/spinnaker/v1/service/distributed/google/GoogleIgorService.java @@ -27,6 +27,7 @@ import lombok.EqualsAndHashCode; import lombok.experimental.Delegate; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Component; @EqualsAndHashCode(callSuper = true) @@ -37,7 +38,7 @@ public class GoogleIgorService extends IgorService final DeployPriority deployPriority = new DeployPriority(4); final boolean requiredToBootstrap = false; - @Delegate @Autowired GoogleDistributedServiceDelegate googleDistributedServiceDelegate; + @Lazy @Delegate @Autowired GoogleDistributedServiceDelegate googleDistributedServiceDelegate; @Override public List getSidecars(SpinnakerRuntimeSettings runtimeSettings) { diff --git a/halyard-deploy/src/main/java/com/netflix/spinnaker/halyard/deploy/spinnaker/v1/service/distributed/google/GoogleKayentaService.java b/halyard-deploy/src/main/java/com/netflix/spinnaker/halyard/deploy/spinnaker/v1/service/distributed/google/GoogleKayentaService.java index 8ca5eddaa6..ca3e664806 100644 --- a/halyard-deploy/src/main/java/com/netflix/spinnaker/halyard/deploy/spinnaker/v1/service/distributed/google/GoogleKayentaService.java +++ b/halyard-deploy/src/main/java/com/netflix/spinnaker/halyard/deploy/spinnaker/v1/service/distributed/google/GoogleKayentaService.java @@ -26,6 +26,7 @@ import lombok.EqualsAndHashCode; import lombok.experimental.Delegate; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Component; @EqualsAndHashCode(callSuper = true) @@ -36,7 +37,7 @@ public class GoogleKayentaService extends KayentaService final DeployPriority deployPriority = new DeployPriority(4); final boolean requiredToBootstrap = false; - @Delegate @Autowired GoogleDistributedServiceDelegate googleDistributedServiceDelegate; + @Lazy @Delegate @Autowired GoogleDistributedServiceDelegate googleDistributedServiceDelegate; @Override public List getSidecars(SpinnakerRuntimeSettings runtimeSettings) { diff --git a/halyard-deploy/src/main/java/com/netflix/spinnaker/halyard/deploy/spinnaker/v1/service/distributed/google/GoogleOrcaBootstrapService.java b/halyard-deploy/src/main/java/com/netflix/spinnaker/halyard/deploy/spinnaker/v1/service/distributed/google/GoogleOrcaBootstrapService.java index 3ca2976fbb..fa009596b2 100644 --- a/halyard-deploy/src/main/java/com/netflix/spinnaker/halyard/deploy/spinnaker/v1/service/distributed/google/GoogleOrcaBootstrapService.java +++ b/halyard-deploy/src/main/java/com/netflix/spinnaker/halyard/deploy/spinnaker/v1/service/distributed/google/GoogleOrcaBootstrapService.java @@ -28,6 +28,7 @@ import lombok.EqualsAndHashCode; import lombok.experimental.Delegate; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Component; @EqualsAndHashCode(callSuper = true) @@ -38,7 +39,7 @@ public class GoogleOrcaBootstrapService extends OrcaBootstrapService final DeployPriority deployPriority = new DeployPriority(6); final boolean requiredToBootstrap = true; - @Delegate @Autowired GoogleDistributedServiceDelegate googleDistributedServiceDelegate; + @Lazy @Delegate @Autowired GoogleDistributedServiceDelegate googleDistributedServiceDelegate; @Override public List getSidecars(SpinnakerRuntimeSettings runtimeSettings) { diff --git a/halyard-deploy/src/main/java/com/netflix/spinnaker/halyard/deploy/spinnaker/v1/service/distributed/google/GoogleOrcaService.java b/halyard-deploy/src/main/java/com/netflix/spinnaker/halyard/deploy/spinnaker/v1/service/distributed/google/GoogleOrcaService.java index fb4f593a96..f20fecd2f9 100644 --- a/halyard-deploy/src/main/java/com/netflix/spinnaker/halyard/deploy/spinnaker/v1/service/distributed/google/GoogleOrcaService.java +++ b/halyard-deploy/src/main/java/com/netflix/spinnaker/halyard/deploy/spinnaker/v1/service/distributed/google/GoogleOrcaService.java @@ -27,6 +27,7 @@ import lombok.EqualsAndHashCode; import lombok.experimental.Delegate; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Component; @EqualsAndHashCode(callSuper = true) @@ -38,7 +39,7 @@ public class GoogleOrcaService extends OrcaService final boolean requiredToBootstrap = false; final boolean stateful = true; - @Delegate @Autowired GoogleDistributedServiceDelegate googleDistributedServiceDelegate; + @Lazy @Delegate @Autowired GoogleDistributedServiceDelegate googleDistributedServiceDelegate; @Override public List getSidecars(SpinnakerRuntimeSettings runtimeSettings) { diff --git a/halyard-deploy/src/main/java/com/netflix/spinnaker/halyard/deploy/spinnaker/v1/service/distributed/google/GoogleRedisBootstrapService.java b/halyard-deploy/src/main/java/com/netflix/spinnaker/halyard/deploy/spinnaker/v1/service/distributed/google/GoogleRedisBootstrapService.java index bbd5022581..1007c8bc44 100644 --- a/halyard-deploy/src/main/java/com/netflix/spinnaker/halyard/deploy/spinnaker/v1/service/distributed/google/GoogleRedisBootstrapService.java +++ b/halyard-deploy/src/main/java/com/netflix/spinnaker/halyard/deploy/spinnaker/v1/service/distributed/google/GoogleRedisBootstrapService.java @@ -30,6 +30,7 @@ import lombok.EqualsAndHashCode; import lombok.experimental.Delegate; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Component; import redis.clients.jedis.Jedis; @@ -41,7 +42,7 @@ public class GoogleRedisBootstrapService extends RedisBootstrapService final DeployPriority deployPriority = new DeployPriority(8); final boolean requiredToBootstrap = true; - @Delegate @Autowired GoogleDistributedServiceDelegate googleDistributedServiceDelegate; + @Lazy @Delegate @Autowired GoogleDistributedServiceDelegate googleDistributedServiceDelegate; @Override public List getSidecars(SpinnakerRuntimeSettings runtimeSettings) { diff --git a/halyard-deploy/src/main/java/com/netflix/spinnaker/halyard/deploy/spinnaker/v1/service/distributed/google/GoogleRedisService.java b/halyard-deploy/src/main/java/com/netflix/spinnaker/halyard/deploy/spinnaker/v1/service/distributed/google/GoogleRedisService.java index f09323df1d..8b23f925ff 100644 --- a/halyard-deploy/src/main/java/com/netflix/spinnaker/halyard/deploy/spinnaker/v1/service/distributed/google/GoogleRedisService.java +++ b/halyard-deploy/src/main/java/com/netflix/spinnaker/halyard/deploy/spinnaker/v1/service/distributed/google/GoogleRedisService.java @@ -32,6 +32,7 @@ import lombok.EqualsAndHashCode; import lombok.experimental.Delegate; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Component; import redis.clients.jedis.Jedis; @@ -42,7 +43,7 @@ public class GoogleRedisService extends RedisService implements GoogleDistribute final DeployPriority deployPriority = new DeployPriority(8); final boolean requiredToBootstrap = true; - @Delegate @Autowired GoogleDistributedServiceDelegate googleDistributedServiceDelegate; + @Lazy @Delegate @Autowired GoogleDistributedServiceDelegate googleDistributedServiceDelegate; @Override public Jedis connectToPrimaryService( diff --git a/halyard-deploy/src/main/java/com/netflix/spinnaker/halyard/deploy/spinnaker/v1/service/distributed/google/GoogleRoscoService.java b/halyard-deploy/src/main/java/com/netflix/spinnaker/halyard/deploy/spinnaker/v1/service/distributed/google/GoogleRoscoService.java index fd74d724f4..ff834f3528 100644 --- a/halyard-deploy/src/main/java/com/netflix/spinnaker/halyard/deploy/spinnaker/v1/service/distributed/google/GoogleRoscoService.java +++ b/halyard-deploy/src/main/java/com/netflix/spinnaker/halyard/deploy/spinnaker/v1/service/distributed/google/GoogleRoscoService.java @@ -27,6 +27,7 @@ import lombok.EqualsAndHashCode; import lombok.experimental.Delegate; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Component; @EqualsAndHashCode(callSuper = true) @@ -38,7 +39,7 @@ public class GoogleRoscoService extends RoscoService final boolean requiredToBootstrap = false; final boolean stateful = true; - @Delegate @Autowired GoogleDistributedServiceDelegate googleDistributedServiceDelegate; + @Lazy @Delegate @Autowired GoogleDistributedServiceDelegate googleDistributedServiceDelegate; @Override public List getSidecars(SpinnakerRuntimeSettings runtimeSettings) { diff --git a/halyard-deploy/src/main/java/com/netflix/spinnaker/halyard/deploy/spinnaker/v1/service/distributed/google/GoogleVaultServerService.java b/halyard-deploy/src/main/java/com/netflix/spinnaker/halyard/deploy/spinnaker/v1/service/distributed/google/GoogleVaultServerService.java index 56108bc1f6..6b0c8bd9fb 100644 --- a/halyard-deploy/src/main/java/com/netflix/spinnaker/halyard/deploy/spinnaker/v1/service/distributed/google/GoogleVaultServerService.java +++ b/halyard-deploy/src/main/java/com/netflix/spinnaker/halyard/deploy/spinnaker/v1/service/distributed/google/GoogleVaultServerService.java @@ -29,6 +29,7 @@ import lombok.EqualsAndHashCode; import lombok.experimental.Delegate; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Component; @EqualsAndHashCode(callSuper = true) @@ -36,7 +37,7 @@ @Data public class GoogleVaultServerService extends VaultServerService implements GoogleDistributedService { - @Delegate @Autowired GoogleDistributedServiceDelegate googleDistributedServiceDelegate; + @Lazy @Delegate @Autowired GoogleDistributedServiceDelegate googleDistributedServiceDelegate; @Override public String getDefaultInstanceType() { diff --git a/halyard-deploy/src/main/java/com/netflix/spinnaker/halyard/deploy/spinnaker/v1/service/distributed/kubernetes/v2/KubernetesV2ServiceDelegate.java b/halyard-deploy/src/main/java/com/netflix/spinnaker/halyard/deploy/spinnaker/v1/service/distributed/kubernetes/v2/KubernetesV2ServiceDelegate.java index d22856915e..6d2674c866 100644 --- a/halyard-deploy/src/main/java/com/netflix/spinnaker/halyard/deploy/spinnaker/v1/service/distributed/kubernetes/v2/KubernetesV2ServiceDelegate.java +++ b/halyard-deploy/src/main/java/com/netflix/spinnaker/halyard/deploy/spinnaker/v1/service/distributed/kubernetes/v2/KubernetesV2ServiceDelegate.java @@ -25,6 +25,7 @@ import com.netflix.spinnaker.halyard.deploy.spinnaker.v1.SpinnakerArtifact; import lombok.Getter; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Component; @Component @@ -41,5 +42,5 @@ public String getDockerRegistry(String deploymentName, SpinnakerArtifact artifac @Autowired @Getter ObjectMapper objectMapper; - @Autowired @Getter KubernetesV2MonitoringDaemonService monitoringDaemonService; + @Lazy @Autowired @Getter KubernetesV2MonitoringDaemonService monitoringDaemonService; } diff --git a/halyard-integration/halyard-integration.gradle b/halyard-integration/halyard-integration.gradle new file mode 100644 index 0000000000..c860d5c9da --- /dev/null +++ b/halyard-integration/halyard-integration.gradle @@ -0,0 +1,26 @@ +dependencies { + testImplementation "com.fasterxml.jackson.core:jackson-databind" + testImplementation "org.assertj:assertj-core" + testImplementation "org.junit.jupiter:junit-jupiter-api" + testImplementation "org.slf4j:slf4j-api" + testImplementation "org.testcontainers:testcontainers" + testImplementation "org.testcontainers:junit-jupiter" + testImplementation "org.testcontainers:mysql" + testRuntimeOnly "ch.qos.logback:logback-classic" + testRuntimeOnly "mysql:mysql-connector-java" +} + +test.configure { + def fullDockerImageName = System.getenv('FULL_DOCKER_IMAGE_NAME') + onlyIf("there is a docker image to test") { + fullDockerImageName != null && fullDockerImageName.trim() != '' + } +} + +test { + // So stdout and stderr from the just-built container are available in CI + testLogging.showStandardStreams = true + + // Run the tests when the docker image changes + inputs.property 'fullDockerImageName', System.getenv('FULL_DOCKER_IMAGE_NAME') +} diff --git a/halyard-integration/src/test/java/com/netflix/spinnaker/halyard/StandaloneContainerTest.java b/halyard-integration/src/test/java/com/netflix/spinnaker/halyard/StandaloneContainerTest.java new file mode 100644 index 0000000000..435be1c124 --- /dev/null +++ b/halyard-integration/src/test/java/com/netflix/spinnaker/halyard/StandaloneContainerTest.java @@ -0,0 +1,108 @@ +/* + * 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.halyard; + +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.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.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 Logger logger = LoggerFactory.getLogger(StandaloneContainerTest.class); + + private static GenericContainer halyardContainer; + + @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); + + DockerImageName dockerImageName = DockerImageName.parse(fullDockerImageName); + + halyardContainer = + new GenericContainer(dockerImageName) + .withExposedPorts(8064) + .waitingFor(Wait.forHealthcheck()) + .withEnv("SPRING_APPLICATION_JSON", getSpringApplicationJson()); + + Slf4jLogConsumer logConsumer = new Slf4jLogConsumer(logger); + halyardContainer.start(); + halyardContainer.followOutput(logConsumer); + } + + private static String getSpringApplicationJson() throws JsonProcessingException { + Map properties = Map.of(); + ObjectMapper mapper = new ObjectMapper(); + return mapper.writeValueAsString(properties); + } + + @AfterAll + static void cleanupOnce() { + if (halyardContainer != null) { + halyardContainer.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://" + + halyardContainer.getHost() + + ":" + + halyardContainer.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/halyard-integration/src/test/resources/logback.xml b/halyard-integration/src/test/resources/logback.xml new file mode 100644 index 0000000000..d0573f0bfc --- /dev/null +++ b/halyard-integration/src/test/resources/logback.xml @@ -0,0 +1,36 @@ + + + + + + + %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n + + + + + + + + + + + + + diff --git a/settings.gradle b/settings.gradle index 4cad784879..bcb5f29644 100644 --- a/settings.gradle +++ b/settings.gradle @@ -29,6 +29,7 @@ include 'halyard-cli' include 'halyard-config' include 'halyard-core' include 'halyard-deploy' +include 'halyard-integration' include 'halyard-proto' include 'halyard-web'