diff --git a/orca-api/src/main/java/com/netflix/spinnaker/orca/api/pipeline/Task.java b/orca-api/src/main/java/com/netflix/spinnaker/orca/api/pipeline/Task.java index a29fcdd918..e4c43e5b53 100644 --- a/orca-api/src/main/java/com/netflix/spinnaker/orca/api/pipeline/Task.java +++ b/orca-api/src/main/java/com/netflix/spinnaker/orca/api/pipeline/Task.java @@ -26,6 +26,8 @@ import java.util.Arrays; import java.util.Collection; import java.util.Collections; +import java.util.Map; +import java.util.stream.Collectors; import javax.annotation.Nonnull; import javax.annotation.Nullable; @@ -91,6 +93,22 @@ default Collection aliases() { return Collections.emptyList(); } + /** + * method to filter certain keys from a stage's "context.outputs" key. This takes in the + * context.outputs map as an input, as well as a collection of keys to be filtered from it. It + * then returns the outputs map sans the keys to filter. + * + * @param outputs Map of a stage context's "outputs" + * @param keysToFilter Collection of keys that need to be filtered from outputs + * @return filtered map of stage context's "outputs" + */ + default Map filterContextOutputs( + Map outputs, Collection keysToFilter) { + return outputs.entrySet().stream() + .filter(map -> !keysToFilter.contains(map.getKey())) + .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); + } + /** Allows backwards compatibility of a task's "type", even through class renames / refactors. */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) diff --git a/orca-clouddriver/src/main/groovy/com/netflix/spinnaker/orca/clouddriver/tasks/job/WaitOnJobCompletion.groovy b/orca-clouddriver/src/main/groovy/com/netflix/spinnaker/orca/clouddriver/tasks/job/WaitOnJobCompletion.groovy index 25ab83f1d3..605bc07d71 100644 --- a/orca-clouddriver/src/main/groovy/com/netflix/spinnaker/orca/clouddriver/tasks/job/WaitOnJobCompletion.groovy +++ b/orca-clouddriver/src/main/groovy/com/netflix/spinnaker/orca/clouddriver/tasks/job/WaitOnJobCompletion.groovy @@ -26,6 +26,8 @@ import com.netflix.spinnaker.orca.api.pipeline.OverridableTimeoutRetryableTask import com.netflix.spinnaker.orca.api.pipeline.models.StageExecution import com.netflix.spinnaker.orca.api.pipeline.TaskResult import com.netflix.spinnaker.orca.clouddriver.KatoRestService +import com.netflix.spinnaker.orca.clouddriver.config.TaskConfigurationProperties +import com.netflix.spinnaker.orca.clouddriver.config.TaskConfigurationProperties.WaitOnJobCompletionTaskConfig import com.netflix.spinnaker.orca.clouddriver.utils.CloudProviderAware import com.netflix.spinnaker.orca.front50.Front50Service import com.netflix.spinnaker.orca.clouddriver.exception.JobFailedException @@ -53,6 +55,7 @@ public class WaitOnJobCompletion implements CloudProviderAware, OverridableTimeo private final RetrySupport retrySupport private final JobUtils jobUtils private final ExecutionRepository repository + private final WaitOnJobCompletionTaskConfig configProperties Front50Service front50Service static final String REFRESH_TYPE = "Job" @@ -68,13 +71,17 @@ public class WaitOnJobCompletion implements CloudProviderAware, OverridableTimeo RetrySupport retrySupport, JobUtils jobUtils, @Nullable Front50Service front50Service, + TaskConfigurationProperties configProperties, ExecutionRepository repository) { this.katoRestService = katoRestService this.objectMapper = objectMapper this.retrySupport = retrySupport this.jobUtils = jobUtils this.front50Service = front50Service + this.configProperties = configProperties.getWaitOnJobCompletionTask() this.repository = repository + + log.info("output keys to filter: {}", this.configProperties.getExcludeKeysFromOutputs()) } @Override @@ -190,7 +197,14 @@ public class WaitOnJobCompletion implements CloudProviderAware, OverridableTimeo } } - TaskResult.builder(status).context(outputs).outputs(outputs).build() + // exclude certain configured keys from being stored in the stage outputs + Map filteredOutputs = filterContextOutputs(outputs, configProperties.getExcludeKeysFromOutputs()) + log.info("context outputs will only contain: ${filteredOutputs.keySet()} keys") + + TaskResult.builder(status) + .context(outputs) + .outputs(filteredOutputs) + .build() } private Boolean applicationExists(String appName) { diff --git a/orca-clouddriver/src/main/java/com/netflix/spinnaker/orca/clouddriver/config/CloudDriverConfiguration.java b/orca-clouddriver/src/main/java/com/netflix/spinnaker/orca/clouddriver/config/CloudDriverConfiguration.java index 851475d912..ac8e04b5c1 100644 --- a/orca-clouddriver/src/main/java/com/netflix/spinnaker/orca/clouddriver/config/CloudDriverConfiguration.java +++ b/orca-clouddriver/src/main/java/com/netflix/spinnaker/orca/clouddriver/config/CloudDriverConfiguration.java @@ -57,7 +57,8 @@ }) @EnableConfigurationProperties({ CloudDriverConfigurationProperties.class, - PollerConfigurationProperties.class + PollerConfigurationProperties.class, + TaskConfigurationProperties.class }) @Slf4j public class CloudDriverConfiguration { diff --git a/orca-clouddriver/src/main/java/com/netflix/spinnaker/orca/clouddriver/config/TaskConfigurationProperties.java b/orca-clouddriver/src/main/java/com/netflix/spinnaker/orca/clouddriver/config/TaskConfigurationProperties.java new file mode 100644 index 0000000000..95d074334b --- /dev/null +++ b/orca-clouddriver/src/main/java/com/netflix/spinnaker/orca/clouddriver/config/TaskConfigurationProperties.java @@ -0,0 +1,69 @@ +/* + * Copyright 2021 Salesforce.com, 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.clouddriver.config; + +import com.netflix.spinnaker.orca.clouddriver.tasks.job.WaitOnJobCompletion; +import com.netflix.spinnaker.orca.clouddriver.tasks.manifest.PromoteManifestKatoOutputsTask; +import com.netflix.spinnaker.orca.clouddriver.tasks.manifest.ResolveDeploySourceManifestTask; +import java.util.Set; +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; + +@Data +@ConfigurationProperties("tasks.clouddriver") +/** configuration properties for various Orca tasks that are in the orca-clouddriver module */ +public class TaskConfigurationProperties { + + /** properties that pertain to {@link WaitOnJobCompletion} task. */ + private WaitOnJobCompletionTaskConfig waitOnJobCompletionTask = + new WaitOnJobCompletionTaskConfig(); + + /** properties that pertain to {@link PromoteManifestKatoOutputsTask} task */ + private PromoteManifestKatoOutputsTaskConfig promoteManifestKatoOutputsTask = + new PromoteManifestKatoOutputsTaskConfig(); + + /** properties that pertain to {@link ResolveDeploySourceManifestTask} task */ + private ResolveDeploySourceManifestTaskConfig resolveDeploySourceManifestTask = + new ResolveDeploySourceManifestTaskConfig(); + + @Data + public static class WaitOnJobCompletionTaskConfig { + /** + * set of keys that will be excluded from the "outputs" key in the stage execution context. + * Default or empty set means that no keys will be excluded. + */ + private Set excludeKeysFromOutputs = Set.of(); + } + + @Data + public static class PromoteManifestKatoOutputsTaskConfig { + /** + * set of keys that will be excluded from the "outputs" key in the stage execution context. + * Default or empty set means that no keys will be excluded. + */ + private Set excludeKeysFromOutputs = Set.of(); + } + + @Data + public static class ResolveDeploySourceManifestTaskConfig { + /** + * set of keys that will be excluded from the "outputs" key in the stage execution context. + * Default or empty set means that no keys will be excluded. + */ + private Set excludeKeysFromOutputs = Set.of(); + } +} diff --git a/orca-clouddriver/src/main/java/com/netflix/spinnaker/orca/clouddriver/tasks/manifest/PromoteManifestKatoOutputsTask.java b/orca-clouddriver/src/main/java/com/netflix/spinnaker/orca/clouddriver/tasks/manifest/PromoteManifestKatoOutputsTask.java index 7671be5599..7c03c8ec7b 100644 --- a/orca-clouddriver/src/main/java/com/netflix/spinnaker/orca/clouddriver/tasks/manifest/PromoteManifestKatoOutputsTask.java +++ b/orca-clouddriver/src/main/java/com/netflix/spinnaker/orca/clouddriver/tasks/manifest/PromoteManifestKatoOutputsTask.java @@ -24,6 +24,8 @@ import com.netflix.spinnaker.orca.api.pipeline.TaskResult; import com.netflix.spinnaker.orca.api.pipeline.models.ExecutionStatus; import com.netflix.spinnaker.orca.api.pipeline.models.StageExecution; +import com.netflix.spinnaker.orca.clouddriver.config.TaskConfigurationProperties; +import com.netflix.spinnaker.orca.clouddriver.config.TaskConfigurationProperties.PromoteManifestKatoOutputsTaskConfig; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; @@ -34,7 +36,6 @@ import java.util.stream.Collectors; import javax.annotation.Nonnull; import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @Component @@ -50,7 +51,15 @@ public class PromoteManifestKatoOutputsTask implements Task { private static final String CREATED_ARTIFACTS_KEY = "createdArtifacts"; private static final String ARTIFACTS_KEY = "artifacts"; - @Autowired ObjectMapper objectMapper; + private final ObjectMapper objectMapper; + private final PromoteManifestKatoOutputsTaskConfig configProperties; + + public PromoteManifestKatoOutputsTask( + ObjectMapper objectMapper, TaskConfigurationProperties configProperties) { + this.objectMapper = objectMapper; + this.configProperties = configProperties.getPromoteManifestKatoOutputsTask(); + log.info("output keys to filter: {}", this.configProperties.getExcludeKeysFromOutputs()); + } @Nonnull @Override @@ -77,7 +86,15 @@ public TaskResult execute(@Nonnull StageExecution stage) { addToOutputs(outputs, allResults, CREATED_ARTIFACTS_KEY, ARTIFACTS_KEY); convertKey(outputs, ARTIFACTS_KEY, artifactListType); - return TaskResult.builder(ExecutionStatus.SUCCEEDED).context(outputs).outputs(outputs).build(); + // exclude certain configured keys from being stored in the stage outputs + Map filteredOutputs = + filterContextOutputs(outputs, configProperties.getExcludeKeysFromOutputs()); + log.info("context outputs will only contain: {} keys", filteredOutputs.keySet()); + + return TaskResult.builder(ExecutionStatus.SUCCEEDED) + .context(outputs) + .outputs(filteredOutputs) + .build(); } private void convertKey(Map outputs, String key, TypeReference tr) { diff --git a/orca-clouddriver/src/main/java/com/netflix/spinnaker/orca/clouddriver/tasks/manifest/ResolveDeploySourceManifestTask.java b/orca-clouddriver/src/main/java/com/netflix/spinnaker/orca/clouddriver/tasks/manifest/ResolveDeploySourceManifestTask.java index 0f6caa657f..ee57ada169 100644 --- a/orca-clouddriver/src/main/java/com/netflix/spinnaker/orca/clouddriver/tasks/manifest/ResolveDeploySourceManifestTask.java +++ b/orca-clouddriver/src/main/java/com/netflix/spinnaker/orca/clouddriver/tasks/manifest/ResolveDeploySourceManifestTask.java @@ -22,9 +22,14 @@ import com.netflix.spinnaker.orca.api.pipeline.TaskResult; import com.netflix.spinnaker.orca.api.pipeline.models.ExecutionStatus; import com.netflix.spinnaker.orca.api.pipeline.models.StageExecution; +import com.netflix.spinnaker.orca.clouddriver.config.TaskConfigurationProperties; +import com.netflix.spinnaker.orca.clouddriver.config.TaskConfigurationProperties.ResolveDeploySourceManifestTaskConfig; import java.util.ArrayList; import java.util.List; +import java.util.Map; import javax.annotation.Nonnull; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @@ -32,12 +37,17 @@ @NonnullByDefault public final class ResolveDeploySourceManifestTask implements Task { public static final String TASK_NAME = "resolveDeploySourceManifest"; + private final Logger log = LoggerFactory.getLogger(getClass()); private final ManifestEvaluator manifestEvaluator; + private final ResolveDeploySourceManifestTaskConfig configProperties; @Autowired - public ResolveDeploySourceManifestTask(ManifestEvaluator manifestEvaluator) { + public ResolveDeploySourceManifestTask( + ManifestEvaluator manifestEvaluator, TaskConfigurationProperties configProperties) { this.manifestEvaluator = manifestEvaluator; + this.configProperties = configProperties.getResolveDeploySourceManifestTask(); + log.info("output keys to filter: {}", this.configProperties.getExcludeKeysFromOutputs()); } @Nonnull @@ -48,7 +58,16 @@ public TaskResult execute(@Nonnull StageExecution stage) { DeployManifestContext context = stage.mapTo(DeployManifestContext.class); ManifestEvaluator.Result result = manifestEvaluator.evaluate(stage, context); ImmutableMap outputs = getOutputs(result); - return TaskResult.builder(ExecutionStatus.SUCCEEDED).context(outputs).outputs(outputs).build(); + + // exclude certain configured keys from being stored in the stage outputs + Map filteredOutputs = + filterContextOutputs(outputs, configProperties.getExcludeKeysFromOutputs()); + log.info("context outputs will only contain: {} keys", filteredOutputs.keySet()); + + return TaskResult.builder(ExecutionStatus.SUCCEEDED) + .context(outputs) + .outputs(filteredOutputs) + .build(); } private ImmutableMap getOutputs(ManifestEvaluator.Result result) { diff --git a/orca-clouddriver/src/test/java/com/netflix/spinnaker/orca/clouddriver/tasks/job/WaitOnJobCompletionTest.java b/orca-clouddriver/src/test/java/com/netflix/spinnaker/orca/clouddriver/tasks/job/WaitOnJobCompletionTest.java index 644674460e..e9c58ed4a9 100644 --- a/orca-clouddriver/src/test/java/com/netflix/spinnaker/orca/clouddriver/tasks/job/WaitOnJobCompletionTest.java +++ b/orca-clouddriver/src/test/java/com/netflix/spinnaker/orca/clouddriver/tasks/job/WaitOnJobCompletionTest.java @@ -33,7 +33,9 @@ import com.netflix.spinnaker.orca.api.pipeline.TaskResult; import com.netflix.spinnaker.orca.api.pipeline.models.ExecutionStatus; import com.netflix.spinnaker.orca.api.pipeline.models.ExecutionType; +import com.netflix.spinnaker.orca.api.pipeline.models.StageExecution; import com.netflix.spinnaker.orca.clouddriver.KatoRestService; +import com.netflix.spinnaker.orca.clouddriver.config.TaskConfigurationProperties; import com.netflix.spinnaker.orca.clouddriver.exception.JobFailedException; import com.netflix.spinnaker.orca.front50.Front50Service; import com.netflix.spinnaker.orca.front50.model.Application; @@ -43,9 +45,17 @@ import java.io.IOException; import java.io.InputStream; import java.time.Duration; +import java.util.Arrays; import java.util.Collections; import java.util.HashMap; +import java.util.HashSet; import java.util.Map; +import java.util.Set; +import okhttp3.MediaType; +import okhttp3.Protocol; +import okhttp3.Request; +import okhttp3.Response; +import okhttp3.ResponseBody; import org.apache.commons.io.IOUtils; import org.assertj.core.api.AssertionsForClassTypes; import org.junit.jupiter.api.BeforeEach; @@ -53,8 +63,8 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; -import retrofit.client.Response; import retrofit.mime.TypedByteArray; +import retrofit.mime.TypedInput; public final class WaitOnJobCompletionTest { private ObjectMapper objectMapper; @@ -62,7 +72,7 @@ public final class WaitOnJobCompletionTest { private KatoRestService mockKatoRestService; private JobUtils mockJobUtils; private ExecutionRepository mockExecutionRepository; - + private TaskConfigurationProperties configProperties; private Front50Service mockFront50Service; WaitOnJobCompletion task; @@ -70,7 +80,10 @@ public final class WaitOnJobCompletionTest { public void setup() { objectMapper = new ObjectMapper(); retrySupport = new RetrySupport(); - + configProperties = new TaskConfigurationProperties(); + configProperties + .getWaitOnJobCompletionTask() + .setExcludeKeysFromOutputs(Set.of("completionDetails")); mockKatoRestService = mock(KatoRestService.class); mockJobUtils = mock(JobUtils.class); mockExecutionRepository = mock(ExecutionRepository.class); @@ -83,6 +96,7 @@ public void setup() { retrySupport, mockJobUtils, mockFront50Service, + configProperties, mockExecutionRepository); } @@ -102,8 +116,8 @@ void jobTimeoutSpecifiedByRunJobTask() { @Test void taskSearchJobByApplicationUsingContextApplication() { - Response mockResponse = - new Response( + retrofit.client.Response mockResponse = + new retrofit.client.Response( "test-url", 200, "test-reason", @@ -128,8 +142,8 @@ void taskSearchJobByApplicationUsingContextApplication() { @Test void taskSearchJobByApplicationUsingContextMoniker() { - Response mockResponse = - new Response( + retrofit.client.Response mockResponse = + new retrofit.client.Response( "test-url", 200, "test-reason", @@ -153,8 +167,8 @@ void taskSearchJobByApplicationUsingContextMoniker() { @Test void taskSearchJobByApplicationUsingParsedName() { - Response mockResponse = - new Response( + retrofit.client.Response mockResponse = + new retrofit.client.Response( "test-url", 200, "test-reason", @@ -177,8 +191,8 @@ void taskSearchJobByApplicationUsingParsedName() { @Test void taskSearchJobByApplicationUsingExecutionApp() { - Response mockResponse = - new Response( + retrofit.client.Response mockResponse = + new retrofit.client.Response( "test-url", 200, "test-reason", @@ -208,8 +222,8 @@ void testPropertyFileContentsHandlingForASuccessfulRunJob(boolean isPropertyFile InputStream jobStatusInputStream = getResourceAsStream("clouddriver/tasks/job/successful-runjob-status.json"); - Response mockResponse = - new Response( + retrofit.client.Response mockResponse = + new retrofit.client.Response( "test-url", 200, "test-reason", @@ -275,8 +289,8 @@ void testPropertyFileContentsErrorHandlingForASuccessfulRunJob() throws IOExcept InputStream jobStatusInputStream = getResourceAsStream("clouddriver/tasks/job/successful-runjob-status.json"); - Response mockResponse = - new Response( + retrofit.client.Response mockResponse = + new retrofit.client.Response( "test-url", 200, "test-reason", @@ -331,8 +345,8 @@ void testRunJobFailuresErrorHandling(boolean includePropertyFile) throws IOExcep InputStream jobStatusInputStream = getResourceAsStream("clouddriver/tasks/job/failed-runjob-status.json"); - Response mockResponse = - new Response( + retrofit.client.Response mockResponse = + new retrofit.client.Response( "test-url", 200, "test-reason", @@ -399,8 +413,8 @@ void testPropertyFileContentsHandlingForRunJobFailures(boolean isPropertyFileCon InputStream jobStatusInputStream = getResourceAsStream("clouddriver/tasks/job/failed-runjob-status.json"); - Response mockResponse = - new Response( + retrofit.client.Response mockResponse = + new retrofit.client.Response( "test-url", 200, "test-reason", @@ -471,8 +485,8 @@ void testPropertyFileContentsErrorHandlingForRunJobFailures() throws IOException InputStream jobStatusInputStream = getResourceAsStream("clouddriver/tasks/job/failed-runjob-status.json"); - Response mockResponse = - new Response( + retrofit.client.Response mockResponse = + new retrofit.client.Response( "test-url", 200, "test-reason", @@ -540,4 +554,93 @@ private StageExecutionImpl createStageWithContextWithoutExecutionApplication( return new StageExecutionImpl( new PipelineExecutionImpl(ExecutionType.PIPELINE, null), "test", new HashMap<>(context)); } + + @DisplayName( + "parameterized test to see how keys in the outputs object are filtered based on the inputs") + @ParameterizedTest(name = "{index} ==> keys to be excluded from outputs = {0}") + @ValueSource(strings = {"", "jobStatus,completionDetails"}) + void testOutputFilter(String keysToFilter) throws IOException { + // setup + Set expectedKeysToBeExcludedFromOutput = new HashSet<>(); + if (!keysToFilter.equals("")) { + expectedKeysToBeExcludedFromOutput = new HashSet<>(Arrays.asList(keysToFilter.split(","))); + } + + configProperties + .getWaitOnJobCompletionTask() + .setExcludeKeysFromOutputs(expectedKeysToBeExcludedFromOutput); + + InputStream inputStream = + getResourceAsStream("clouddriver/tasks/job/successful-run-job-state.json"); + + // mocked response from clouddriver + Response response = + new Response.Builder() + .code(200) + .message("some message") + .request(new Request.Builder().url("http://url").build()) + .protocol(Protocol.HTTP_1_0) + .body( + ResponseBody.create( + MediaType.parse("application/json"), IOUtils.toByteArray(inputStream))) + .addHeader("content-type", "application/json") + .build(); + + retrofit.client.Response retrofitResponse = + new retrofit.client.Response( + "http://url", + 200, + "", + Collections.emptyList(), + new TypedInput() { + @Override + public String mimeType() { + okhttp3.MediaType mediaType = response.body().contentType(); + return mediaType == null ? null : mediaType.toString(); + } + + @Override + public long length() { + return response.body().contentLength(); + } + + @Override + public InputStream in() { + return response.body().byteStream(); + } + }); + + when(mockKatoRestService.collectJob("testrep", "test-account", "test", "job testrep")) + .thenReturn(retrofitResponse); + + when(mockKatoRestService.getFileContents( + "testrep", "test-account", "test", "job testrep", "testrep")) + .thenReturn(Map.of("some-key", "some-value")); + + Map context = + getResource(objectMapper, "clouddriver/tasks/job/runjob-context-success.json", Map.class); + StageExecution stageExecution = + new StageExecutionImpl( + new PipelineExecutionImpl(ExecutionType.PIPELINE, "testrep"), "test", context); + + // when + TaskResult result = task.execute(stageExecution); + + // then + assertThat(result.getOutputs()).isNotEmpty(); + assertThat(result.getContext()).isNotEmpty(); + + // the 'outputs' key should not contain the values present in the input i.e. in `keysToFilter` + Set receivedOutputsKeySet = result.getOutputs().keySet(); + for (String excludedKey : expectedKeysToBeExcludedFromOutput) { + assertThat(receivedOutputsKeySet.contains(excludedKey)).isFalse(); + } + + // ensuring that the 'context' key still has the values present in the input i.e. in + // `keysToFilter` + Set receivedContextKeySet = result.getContext().keySet(); + for (String excludedKey : expectedKeysToBeExcludedFromOutput) { + assertThat(receivedContextKeySet.contains(excludedKey)).isTrue(); + } + } } diff --git a/orca-clouddriver/src/test/java/com/netflix/spinnaker/orca/clouddriver/tasks/manifest/PromoteManifestKatoOutputsTaskTest.java b/orca-clouddriver/src/test/java/com/netflix/spinnaker/orca/clouddriver/tasks/manifest/PromoteManifestKatoOutputsTaskTest.java new file mode 100644 index 0000000000..7946fec2dc --- /dev/null +++ b/orca-clouddriver/src/test/java/com/netflix/spinnaker/orca/clouddriver/tasks/manifest/PromoteManifestKatoOutputsTaskTest.java @@ -0,0 +1,102 @@ +/* + * Copyright 2021 Salesforce.com, 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.clouddriver.tasks.manifest; + +import static com.netflix.spinnaker.orca.TestUtils.getResource; +import static org.assertj.core.api.Assertions.assertThat; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.netflix.spinnaker.orca.api.pipeline.TaskResult; +import com.netflix.spinnaker.orca.api.pipeline.models.ExecutionType; +import com.netflix.spinnaker.orca.api.pipeline.models.StageExecution; +import com.netflix.spinnaker.orca.clouddriver.config.TaskConfigurationProperties; +import com.netflix.spinnaker.orca.pipeline.model.PipelineExecutionImpl; +import com.netflix.spinnaker.orca.pipeline.model.StageExecutionImpl; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; +import org.mockito.junit.jupiter.MockitoExtension; + +@ExtendWith(MockitoExtension.class) +public class PromoteManifestKatoOutputsTaskTest { + + private PromoteManifestKatoOutputsTask promoteManifestKatoOutputsTask; + private TaskConfigurationProperties configService; + private ObjectMapper objectMapper; + + @BeforeEach + public void setup() { + configService = new TaskConfigurationProperties(); + objectMapper = new ObjectMapper(); + } + + @DisplayName("test to see how keys in the outputs object are filtered based on the inputs") + @ParameterizedTest(name = "{index} ==> keys to be excluded from outputs = {0}") + @ValueSource( + strings = {"", "outputs.createdArtifacts,outputs.manifests,outputs.boundArtifacts,artifacts"}) + void testOutputFilter(String keysToFilter) { + + // setup + Set expectedKeysToBeExcludedFromOutput = new HashSet<>(); + if (!keysToFilter.equals("")) { + expectedKeysToBeExcludedFromOutput = new HashSet<>(Arrays.asList(keysToFilter.split(","))); + } + + configService + .getPromoteManifestKatoOutputsTask() + .setExcludeKeysFromOutputs(expectedKeysToBeExcludedFromOutput); + promoteManifestKatoOutputsTask = + new PromoteManifestKatoOutputsTask(objectMapper, configService); + + Map context = + getResource( + objectMapper, "clouddriver/tasks/manifest/deploy-manifest-context.json", Map.class); + StageExecution stageExecution = + new StageExecutionImpl( + new PipelineExecutionImpl(ExecutionType.PIPELINE, "test"), "test", context); + + // when + TaskResult result = promoteManifestKatoOutputsTask.execute(stageExecution); + + // then + assertThat(result.getOutputs()).isNotEmpty(); + assertThat(result.getContext()).isNotEmpty(); + + // the 'outputs' key should not contain the values present in the input i.e. in `keysToFilter` + Set receivedOutputsKeySet = result.getOutputs().keySet(); + for (String excludedKey : expectedKeysToBeExcludedFromOutput) { + if (!excludedKey.isEmpty()) { + assertThat(receivedOutputsKeySet.contains(excludedKey)).isFalse(); + } + } + + // ensuring that the 'context' key still has the values present in the input i.e. in + // `keysToFilter` + Set receivedContextKeySet = result.getContext().keySet(); + for (String excludedKey : expectedKeysToBeExcludedFromOutput) { + if (!excludedKey.isEmpty()) { + assertThat(receivedContextKeySet.contains(excludedKey)).isTrue(); + } + } + } +} diff --git a/orca-clouddriver/src/test/java/com/netflix/spinnaker/orca/clouddriver/tasks/manifest/ResolveDeploySourceManifestTaskTest.java b/orca-clouddriver/src/test/java/com/netflix/spinnaker/orca/clouddriver/tasks/manifest/ResolveDeploySourceManifestTaskTest.java index e7d4a59260..bef4fcd341 100644 --- a/orca-clouddriver/src/test/java/com/netflix/spinnaker/orca/clouddriver/tasks/manifest/ResolveDeploySourceManifestTaskTest.java +++ b/orca-clouddriver/src/test/java/com/netflix/spinnaker/orca/clouddriver/tasks/manifest/ResolveDeploySourceManifestTaskTest.java @@ -16,18 +16,31 @@ package com.netflix.spinnaker.orca.clouddriver.tasks.manifest; +import static com.netflix.spinnaker.orca.TestUtils.getResource; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.*; +import com.fasterxml.jackson.databind.ObjectMapper; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; +import com.netflix.spinnaker.kork.core.RetrySupport; import com.netflix.spinnaker.orca.api.pipeline.TaskResult; import com.netflix.spinnaker.orca.api.pipeline.models.ExecutionType; +import com.netflix.spinnaker.orca.api.pipeline.models.StageExecution; +import com.netflix.spinnaker.orca.clouddriver.OortService; +import com.netflix.spinnaker.orca.clouddriver.config.TaskConfigurationProperties; import com.netflix.spinnaker.orca.pipeline.model.PipelineExecutionImpl; import com.netflix.spinnaker.orca.pipeline.model.StageExecutionImpl; +import com.netflix.spinnaker.orca.pipeline.persistence.ExecutionRepository; +import com.netflix.spinnaker.orca.pipeline.util.ArtifactUtils; +import com.netflix.spinnaker.orca.pipeline.util.ContextParameterProcessor; import java.util.*; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; final class ResolveDeploySourceManifestTaskTest { @@ -35,11 +48,30 @@ final class ResolveDeploySourceManifestTaskTest { ImmutableMap.of("test-key-1", "test-value-1"); private static final Map MANIFEST_2 = ImmutableMap.of("test-key-2", "test-value-2"); + private final TaskConfigurationProperties configProperties = new TaskConfigurationProperties(); + + private ObjectMapper objectMapper; + private ArtifactUtils artifactUtils; + private ManifestEvaluator manifestEvaluator; + private OortService oortService = mock(OortService.class); + private ExecutionRepository executionRepository = mock(ExecutionRepository.class); + private ContextParameterProcessor contextParameterProcessor = + mock(ContextParameterProcessor.class); + + @BeforeEach + public void setup() { + objectMapper = new ObjectMapper(); + artifactUtils = new ArtifactUtils(objectMapper, executionRepository, contextParameterProcessor); + manifestEvaluator = + new ManifestEvaluator( + artifactUtils, contextParameterProcessor, oortService, new RetrySupport()); + } @Test void manifestsWithObjectsIsNotFlattened() { ManifestEvaluator manifestEvaluator = mock(ManifestEvaluator.class); - ResolveDeploySourceManifestTask task = new ResolveDeploySourceManifestTask(manifestEvaluator); + ResolveDeploySourceManifestTask task = + new ResolveDeploySourceManifestTask(manifestEvaluator, configProperties); StageExecutionImpl myStage = createStageWithManifests(ImmutableList.of(MANIFEST_1, MANIFEST_2)); @@ -59,7 +91,8 @@ void manifestsWithObjectsIsNotFlattened() { @Test void manifestsWithListIsFlattened() { ManifestEvaluator manifestEvaluator = mock(ManifestEvaluator.class); - ResolveDeploySourceManifestTask task = new ResolveDeploySourceManifestTask(manifestEvaluator); + ResolveDeploySourceManifestTask task = + new ResolveDeploySourceManifestTask(manifestEvaluator, configProperties); StageExecutionImpl myStage = createStageWithManifests(ImmutableList.of(ImmutableList.of(MANIFEST_1, MANIFEST_2))); @@ -80,7 +113,8 @@ void manifestsWithListIsFlattened() { @Test void manifestsWithListsAndObjectsIsFlattened() { ManifestEvaluator manifestEvaluator = mock(ManifestEvaluator.class); - ResolveDeploySourceManifestTask task = new ResolveDeploySourceManifestTask(manifestEvaluator); + ResolveDeploySourceManifestTask task = + new ResolveDeploySourceManifestTask(manifestEvaluator, configProperties); StageExecutionImpl myStage = createStageWithManifests(ImmutableList.of(ImmutableList.of(MANIFEST_1), MANIFEST_2)); @@ -101,7 +135,8 @@ void manifestsWithListsAndObjectsIsFlattened() { @Test void manifestsWithListsIsFlattened() { ManifestEvaluator manifestEvaluator = mock(ManifestEvaluator.class); - ResolveDeploySourceManifestTask task = new ResolveDeploySourceManifestTask(manifestEvaluator); + ResolveDeploySourceManifestTask task = + new ResolveDeploySourceManifestTask(manifestEvaluator, configProperties); StageExecutionImpl myStage = createStageWithManifests( @@ -123,7 +158,8 @@ void manifestsWithListsIsFlattened() { @Test void stageWithoutManifestsHandled() { ManifestEvaluator manifestEvaluator = mock(ManifestEvaluator.class); - ResolveDeploySourceManifestTask task = new ResolveDeploySourceManifestTask(manifestEvaluator); + ResolveDeploySourceManifestTask task = + new ResolveDeploySourceManifestTask(manifestEvaluator, configProperties); StageExecutionImpl myStage = createStageWithManifests(null); @@ -153,4 +189,57 @@ private static List> getManifests(TaskResult result) { .map(c -> (List>) c.get("manifests")) .orElse(ImmutableList.of()); } + + @DisplayName( + "parameterized test to see how keys in the outputs object are filtered based on the inputs") + @ParameterizedTest(name = "{index} ==> keys to be excluded from outputs = {0}") + @ValueSource(strings = {"", "manifests,requiredArtifacts,optionalArtifacts"}) + void testOutputFilter(String keysToFilter) { + + // setup + Set expectedKeysToBeExcludedFromOutput = new HashSet<>(); + if (!keysToFilter.equals("")) { + expectedKeysToBeExcludedFromOutput = new HashSet<>(Arrays.asList(keysToFilter.split(","))); + } + + configProperties + .getResolveDeploySourceManifestTask() + .setExcludeKeysFromOutputs(expectedKeysToBeExcludedFromOutput); + + ResolveDeploySourceManifestTask resolveDeploySourceManifestTask = + new ResolveDeploySourceManifestTask(manifestEvaluator, configProperties); + + Map context = + getResource( + objectMapper, "clouddriver/tasks/manifest/deploy-manifest-context.json", Map.class); + StageExecution stageExecution = + new StageExecutionImpl( + new PipelineExecutionImpl(ExecutionType.PIPELINE, "test"), "test", context); + + // when + TaskResult result = resolveDeploySourceManifestTask.execute(stageExecution); + + // then + + // the 'outputs' key should not contain the values present in the input i.e. in `keysToFilter` + if (expectedKeysToBeExcludedFromOutput.containsAll( + List.of("manifests", "requiredArtifacts", "optionalArtifacts"))) { + assertThat(result.getOutputs().isEmpty()); + } else { + assertThat(result.getOutputs()).isNotEmpty(); + } + assertThat(result.getContext()).isNotEmpty(); + + Set receivedOutputsKeySet = result.getOutputs().keySet(); + for (String excludedKey : expectedKeysToBeExcludedFromOutput) { + assertThat(receivedOutputsKeySet.contains(excludedKey)).isFalse(); + } + + // ensuring that the 'context' key still has the values present in the input i.e. in + // `keysToFilter` + Set receivedContextKeySet = result.getContext().keySet(); + for (String excludedKey : expectedKeysToBeExcludedFromOutput) { + assertThat(receivedContextKeySet.contains(excludedKey)).isTrue(); + } + } } diff --git a/orca-clouddriver/src/test/resources/com/netflix/spinnaker/orca/clouddriver/tasks/job/runjob-context-success.json b/orca-clouddriver/src/test/resources/com/netflix/spinnaker/orca/clouddriver/tasks/job/runjob-context-success.json new file mode 100644 index 0000000000..625ee0f898 --- /dev/null +++ b/orca-clouddriver/src/test/resources/com/netflix/spinnaker/orca/clouddriver/tasks/job/runjob-context-success.json @@ -0,0 +1,266 @@ +{ + "notification.type": "runjob", + "deploy.account.name": "test-account", + "outputs.createdArtifacts": [ + { + "customKind": false, + "reference": "testrep", + "metadata": { + "account": "test-account" + }, + "name": "testrep", + "location": "test", + "type": "kubernetes/job", + "version": "" + } + ], + "propertyFile": "testrep", + "consumeArtifactSource": "propertyFile", + "stageEnabled": {}, + "source": "text", + "dev": true, + "cloudProvider": "kubernetes", + "kato.result.expected": false, + "alias": "runJob", + "deploy.server.groups": {}, + "kato.last.task.id": { + "id": "123" + }, + "artifacts": [ + { + "customKind": false, + "reference": "testrep", + "metadata": { + "account": "test-account" + }, + "name": "testrep", + "location": "test", + "type": "kubernetes/job", + "version": "" + } + ], + "manifest": { + "metadata": { + "name": " testrep", + "namespace": "test", + "labels": { + "p_environment_type": "dev" + } + }, + "apiVersion": "batch/v1", + "kind": "Job", + "spec": { + "template": { + "spec": { + "dnsPolicy": "ClusterFirst", + "terminationGracePeriodSeconds": 30, + "automountServiceAccountToken": true, + "serviceAccountName": "test", + "volumes": [ + { + "configMap": { + "name": "configmap" + }, + "name": "configmap-volume" + }, + { + "configMap": { + "name": "configmap-1" + }, + "name": "configs-volume-1" + }, + { + "name": "creds", + "secret": { + "secretName": "secret" + } + }, + { + "emptyDir": {}, + "name": "secrets-volume" + } + ], + "containers": [ + { + "image": "main-app-image:1", + "terminationMessagePolicy": "FallbackToLogsOnError", + "name": "testrepl1", + "env": [ + { + "name": "LEVEL", + "value": "service" + } + ], + "volumeMounts": [ + { + "mountPath": "/usr/test/configs", + "name": "configmap-volume" + } + ] + } + ], + "securityContext": { + "fsGroup": 100 + }, + "restartPolicy": "Never", + "initContainers": [] + } + }, + "backoffLimit": 0, + "activeDeadlineSeconds": 900, + "ttlSecondsAfterFinished": 600 + } + }, + "kato.task.terminalRetryCount": 0, + "isNew": true, + "kato.task.firstNotFoundRetry": -1, + "failOnFailedExpressions": true, + "outputs.manifestNamesByNamespace": { + "test": [ + "job testrep" + ] + }, + "application": "testrep", + "outputs.boundArtifacts": [], + "credentails": "test-account", + "kato.tasks": [ + { + "outputs": [], + "resultObjects": [ + { + "createdArtifacts": [ + { + "customKind": false, + "reference": "testrep", + "metadata": { + "account": "test-account" + }, + "name": "testrep", + "location": "test", + "type": "kubernetes/job", + "version": "" + } + ], + "boundArtifacts": [] + } + ], + "id": "123", + "history": [ + { + "phase": "ORCHESTRATION", + "status": "Initializing Orchestration Task" + }, + { + "phase": "ORCHESTRATION", + "status": "Processing op: KubernetesRunJobOperation" + }, + { + "phase": "RUN_KUBERNETES_JOB", + "status": "Running Kubernetes job..." + }, + { + "phase": "DEPLOY_KUBERNETES_MANIFEST", + "status": "Beginning deployment of manifests in account test-account ..." + }, + { + "phase": "DEPLOY_KUBERNETES_MANIFEST", + "status": "Swapping out artifacts in job testrep from context..." + }, + { + "phase": "DEPLOY_KUBERNETES_MANIFEST", + "status": "Finding deployer for job..." + }, + { + "phase": "DEPLOY_KUBERNETES_MANIFEST", + "status": "Checking if all requested artifacts were bound..." + }, + { + "phase": "DEPLOY_KUBERNETES_MANIFEST", + "status": "Sorting manifests by priority..." + }, + { + "phase": "DEPLOY_KUBERNETES_MANIFEST", + "status": "Deploy order is: job testrep" + }, + { + "phase": "DEPLOY_KUBERNETES_MANIFEST", + "status": "Finding deployer for job..." + }, + { + "phase": "DEPLOY_KUBERNETES_MANIFEST", + "status": "Annotating manifest job testrep with artifact, relationships & moniker..." + }, + { + "phase": "DEPLOY_KUBERNETES_MANIFEST", + "status": "Swapping out artifacts in job testrep from other deployments..." + }, + { + "phase": "DEPLOY_KUBERNETES_MANIFEST", + "status": "Submitting manifest job testrep to kubernetes master..." + }, + { + "phase": "DEPLOY_KUBERNETES_MANIFEST", + "status": "Deploy manifest task completed successfully for manifest job testrep in account test-account" + }, + { + "phase": "DEPLOY_KUBERNETES_MANIFEST", + "status": "Deploy manifest task completed successfully for all manifests in account test-account" + }, + { + "phase": "ORCHESTRATION", + "status": "Orchestration completed." + }, + { + "phase": "ORCHESTRATION", + "status": "Orchestration completed." + } + ], + "status": { + "retryable": false, + "completed": true, + "failed": false + } + } + ], + "deploy.jobs": { + "test": [ + "job testrep" + ] + }, + "outputs.manifests": [ + { + "metadata": { + "uid": "eaf5237d-774e-49f6-b3a1-ac90a7e47756", + "resourceVersion": "172459430", + "creationTimestamp": "2021-08-11T01:40:00Z", + "name": "testrep", + "namespace": "test" + }, + "apiVersion": "batch/v1", + "kind": "Job", + "spec": { + "backoffLimit": 0, + "parallelism": 1, + "completions": 1, + "selector": { + "matchLabels": { + "controller-uid": "eaf" + } + }, + "activeDeadlineSeconds": 900 + }, + "status": {} + } + ], + "kato.task.notFoundRetryCount": 0, + "account": "test-account", + "kato.task.lastStatus": "SUCCEEDED", + "propertyFileContents": { + "dev": true, + "some_other_output": {} + }, + "output_from_runjob": { + "target_level": "service" + } + } +} diff --git a/orca-clouddriver/src/test/resources/com/netflix/spinnaker/orca/clouddriver/tasks/job/successful-run-job-state.json b/orca-clouddriver/src/test/resources/com/netflix/spinnaker/orca/clouddriver/tasks/job/successful-run-job-state.json new file mode 100644 index 0000000000..338a9ea57a --- /dev/null +++ b/orca-clouddriver/src/test/resources/com/netflix/spinnaker/orca/clouddriver/tasks/job/successful-run-job-state.json @@ -0,0 +1,564 @@ +{ +"provider": "kubernetes", +"completionDetails": { +"summary": "", +"reason": "", +"exitCode": "", +"message": "", +"signal": "" +}, +"jobState": "Succeeded", +"name": "testrep", +"createdTime": 1628646000000, +"location": "test", +"pods": [ +{ +"containerExecutionDetails": [], +"name": "testrep-lnk4x", +"status": { +"phase": "Succeeded", +"podIP": "1.1.1.1", +"containerStatuses": [ +{ +"image": "sha256:123", +"imageID": "docker-pullable://some-repo:1", +"restartCount": 0, +"ready": false, +"name": "testrepv2-l1", +"started": false, +"state": { +"terminated": { +"reason": "Completed", +"exitCode": 0, +"startedAt": { +"dayOfYear": 223, +"equalNow": false, +"year": 2021, +"weekyear": 2021, +"chronology": { +"zone": { +"fixed": true, +"id": "Etc/GMT" +} +}, +"weekOfWeekyear": 32, +"secondOfMinute": 24, +"millisOfDay": 6024000, +"monthOfYear": 8, +"beforeNow": true, +"dayOfWeek": 3, +"minuteOfDay": 100, +"dayOfMonth": 11, +"era": 1, +"zone": { +"fixed": true, +"id": "Etc/GMT" +}, +"yearOfCentury": 21, +"centuryOfEra": 20, +"hourOfDay": 1, +"secondOfDay": 6024, +"millis": 1628646024000, +"yearOfEra": 2021, +"minuteOfHour": 40, +"afterNow": false, +"millisOfSecond": 0 +}, +"containerID": "docker://456", +"finishedAt": { +"dayOfYear": 223, +"equalNow": false, +"year": 2021, +"weekyear": 2021, +"chronology": { +"zone": { +"fixed": true, +"id": "Etc/GMT" +} +}, +"weekOfWeekyear": 32, +"secondOfMinute": 27, +"millisOfDay": 6027000, +"monthOfYear": 8, +"beforeNow": true, +"dayOfWeek": 3, +"minuteOfDay": 100, +"dayOfMonth": 11, +"era": 1, +"zone": { +"fixed": true, +"id": "Etc/GMT" +}, +"yearOfCentury": 21, +"centuryOfEra": 20, +"hourOfDay": 1, +"secondOfDay": 6027, +"millis": 1628646027000, +"yearOfEra": 2021, +"minuteOfHour": 40, +"afterNow": false, +"millisOfSecond": 0 +} +} +}, +"containerID": "docker://456", +"lastState": {} +} +], +"hostIP": "1.1.1.1", +"startTime": { +"dayOfYear": 223, +"equalNow": false, +"year": 2021, +"weekyear": 2021, +"chronology": { +"zone": { +"fixed": true, +"id": "Etc/GMT" +} +}, +"weekOfWeekyear": 32, +"secondOfMinute": 1, +"millisOfDay": 6001000, +"monthOfYear": 8, +"beforeNow": true, +"dayOfWeek": 3, +"minuteOfDay": 100, +"dayOfMonth": 11, +"era": 1, +"zone": { +"fixed": true, +"id": "Etc/GMT" +}, +"yearOfCentury": 21, +"centuryOfEra": 20, +"hourOfDay": 1, +"secondOfDay": 6001, +"millis": 1628646001000, +"yearOfEra": 2021, +"minuteOfHour": 40, +"afterNow": false, +"millisOfSecond": 0 +}, +"qosClass": "Burstable", +"conditions": [ +{ +"reason": "PodCompleted", +"lastTransitionTime": { +"dayOfYear": 223, +"equalNow": false, +"year": 2021, +"weekyear": 2021, +"chronology": { +"zone": { +"fixed": true, +"id": "Etc/GMT" +} +}, +"weekOfWeekyear": 32, +"secondOfMinute": 23, +"millisOfDay": 6023000, +"monthOfYear": 8, +"beforeNow": true, +"dayOfWeek": 3, +"minuteOfDay": 100, +"dayOfMonth": 11, +"era": 1, +"zone": { +"fixed": true, +"id": "Etc/GMT" +}, +"yearOfCentury": 21, +"centuryOfEra": 20, +"hourOfDay": 1, +"secondOfDay": 6023, +"millis": 1628646023000, +"yearOfEra": 2021, +"minuteOfHour": 40, +"afterNow": false, +"millisOfSecond": 0 +}, +"type": "Initialized", +"status": "True" +}, +{ +"reason": "PodCompleted", +"lastTransitionTime": { +"dayOfYear": 223, +"equalNow": false, +"year": 2021, +"weekyear": 2021, +"chronology": { +"zone": { +"fixed": true, +"id": "Etc/GMT" +} +}, +"weekOfWeekyear": 32, +"secondOfMinute": 27, +"millisOfDay": 6027000, +"monthOfYear": 8, +"beforeNow": true, +"dayOfWeek": 3, +"minuteOfDay": 100, +"dayOfMonth": 11, +"era": 1, +"zone": { +"fixed": true, +"id": "Etc/GMT" +}, +"yearOfCentury": 21, +"centuryOfEra": 20, +"hourOfDay": 1, +"secondOfDay": 6027, +"millis": 1628646027000, +"yearOfEra": 2021, +"minuteOfHour": 40, +"afterNow": false, +"millisOfSecond": 0 +}, +"type": "Ready", +"status": "False" +}, +{ +"reason": "PodCompleted", +"lastTransitionTime": { +"dayOfYear": 223, +"equalNow": false, +"year": 2021, +"weekyear": 2021, +"chronology": { +"zone": { +"fixed": true, +"id": "Etc/GMT" +} +}, +"weekOfWeekyear": 32, +"secondOfMinute": 27, +"millisOfDay": 6027000, +"monthOfYear": 8, +"beforeNow": true, +"dayOfWeek": 3, +"minuteOfDay": 100, +"dayOfMonth": 11, +"era": 1, +"zone": { +"fixed": true, +"id": "Etc/GMT" +}, +"yearOfCentury": 21, +"centuryOfEra": 20, +"hourOfDay": 1, +"secondOfDay": 6027, +"millis": 1628646027000, +"yearOfEra": 2021, +"minuteOfHour": 40, +"afterNow": false, +"millisOfSecond": 0 +}, +"type": "ContainersReady", +"status": "False" +}, +{ +"lastTransitionTime": { +"dayOfYear": 223, +"equalNow": false, +"year": 2021, +"weekyear": 2021, +"chronology": { +"zone": { +"fixed": true, +"id": "Etc/GMT" +} +}, +"weekOfWeekyear": 32, +"secondOfMinute": 1, +"millisOfDay": 6001000, +"monthOfYear": 8, +"beforeNow": true, +"dayOfWeek": 3, +"minuteOfDay": 100, +"dayOfMonth": 11, +"era": 1, +"zone": { +"fixed": true, +"id": "Etc/GMT" +}, +"yearOfCentury": 21, +"centuryOfEra": 20, +"hourOfDay": 1, +"secondOfDay": 6001, +"millis": 1628646001000, +"yearOfEra": 2021, +"minuteOfHour": 40, +"afterNow": false, +"millisOfSecond": 0 +}, +"type": "PodScheduled", +"status": "True" +} +], +"initContainerStatuses": [ +{ +"image": "some-init-container-image:1", +"imageID": "docker-pullable://some-init-container-repo/some-init-container-image:1", +"restartCount": 0, +"ready": true, +"name": "init", +"state": { +"terminated": { +"reason": "Completed", +"exitCode": 0, +"startedAt": { +"dayOfYear": 223, +"equalNow": false, +"year": 2021, +"weekyear": 2021, +"chronology": { +"zone": { +"fixed": true, +"id": "Etc/GMT" +} +}, +"weekOfWeekyear": 32, +"secondOfMinute": 2, +"millisOfDay": 6002000, +"monthOfYear": 8, +"beforeNow": true, +"dayOfWeek": 3, +"minuteOfDay": 100, +"dayOfMonth": 11, +"era": 1, +"zone": { +"fixed": true, +"id": "Etc/GMT" +}, +"yearOfCentury": 21, +"centuryOfEra": 20, +"hourOfDay": 1, +"secondOfDay": 6002, +"millis": 1628646002000, +"yearOfEra": 2021, +"minuteOfHour": 40, +"afterNow": false, +"millisOfSecond": 0 +}, +"containerID": "docker://789", +"finishedAt": { +"dayOfYear": 223, +"equalNow": false, +"year": 2021, +"weekyear": 2021, +"chronology": { +"zone": { +"fixed": true, +"id": "Etc/GMT" +} +}, +"weekOfWeekyear": 32, +"secondOfMinute": 19, +"millisOfDay": 6019000, +"monthOfYear": 8, +"beforeNow": true, +"dayOfWeek": 3, +"minuteOfDay": 100, +"dayOfMonth": 11, +"era": 1, +"zone": { +"fixed": true, +"id": "Etc/GMT" +}, +"yearOfCentury": 21, +"centuryOfEra": 20, +"hourOfDay": 1, +"secondOfDay": 6019, +"millis": 1628646019000, +"yearOfEra": 2021, +"minuteOfHour": 40, +"afterNow": false, +"millisOfSecond": 0 +} +} +}, +"containerID": "docker://789", +"lastState": {} +}, +{ +"image": "init-container-2:1", +"imageID": "docker-pullable://some-repo/init-container-2:1", +"restartCount": 0, +"ready": true, +"name": "init2", +"state": { +"terminated": { +"reason": "Completed", +"exitCode": 0, +"startedAt": { +"dayOfYear": 223, +"equalNow": false, +"year": 2021, +"weekyear": 2021, +"chronology": { +"zone": { +"fixed": true, +"id": "Etc/GMT" +} +}, +"weekOfWeekyear": 32, +"secondOfMinute": 20, +"millisOfDay": 6020000, +"monthOfYear": 8, +"beforeNow": true, +"dayOfWeek": 3, +"minuteOfDay": 100, +"dayOfMonth": 11, +"era": 1, +"zone": { +"fixed": true, +"id": "Etc/GMT" +}, +"yearOfCentury": 21, +"centuryOfEra": 20, +"hourOfDay": 1, +"secondOfDay": 6020, +"millis": 1628646020000, +"yearOfEra": 2021, +"minuteOfHour": 40, +"afterNow": false, +"millisOfSecond": 0 +}, +"containerID": "docker://012", +"finishedAt": { +"dayOfYear": 223, +"equalNow": false, +"year": 2021, +"weekyear": 2021, +"chronology": { +"zone": { +"fixed": true, +"id": "Etc/GMT" +} +}, +"weekOfWeekyear": 32, +"secondOfMinute": 21, +"millisOfDay": 6021000, +"monthOfYear": 8, +"beforeNow": true, +"dayOfWeek": 3, +"minuteOfDay": 100, +"dayOfMonth": 11, +"era": 1, +"zone": { +"fixed": true, +"id": "Etc/GMT" +}, +"yearOfCentury": 21, +"centuryOfEra": 20, +"hourOfDay": 1, +"secondOfDay": 6021, +"millis": 1628646021000, +"yearOfEra": 2021, +"minuteOfHour": 40, +"afterNow": false, +"millisOfSecond": 0 +} +} +}, +"containerID": "docker://012", +"lastState": {} +}, +{ +"image": "init-container-3:1", +"imageID": "docker-pullable://some-repo/init-container-3:1", +"restartCount": 0, +"ready": true, +"name": "init-3", +"state": { +"terminated": { +"reason": "Completed", +"exitCode": 0, +"startedAt": { +"dayOfYear": 223, +"equalNow": false, +"year": 2021, +"weekyear": 2021, +"chronology": { +"zone": { +"fixed": true, +"id": "Etc/GMT" +} +}, +"weekOfWeekyear": 32, +"secondOfMinute": 21, +"millisOfDay": 6021000, +"monthOfYear": 8, +"beforeNow": true, +"dayOfWeek": 3, +"minuteOfDay": 100, +"dayOfMonth": 11, +"era": 1, +"zone": { +"fixed": true, +"id": "Etc/GMT" +}, +"yearOfCentury": 21, +"centuryOfEra": 20, +"hourOfDay": 1, +"secondOfDay": 6021, +"millis": 1628646021000, +"yearOfEra": 2021, +"minuteOfHour": 40, +"afterNow": false, +"millisOfSecond": 0 +}, +"containerID": "docker://345", +"finishedAt": { +"dayOfYear": 223, +"equalNow": false, +"year": 2021, +"weekyear": 2021, +"chronology": { +"zone": { +"fixed": true, +"id": "Etc/GMT" +} +}, +"weekOfWeekyear": 32, +"secondOfMinute": 23, +"millisOfDay": 6023000, +"monthOfYear": 8, +"beforeNow": true, +"dayOfWeek": 3, +"minuteOfDay": 100, +"dayOfMonth": 11, +"era": 1, +"zone": { +"fixed": true, +"id": "Etc/GMT" +}, +"yearOfCentury": 21, +"centuryOfEra": 20, +"hourOfDay": 1, +"secondOfDay": 6023, +"millis": 1628646023000, +"yearOfEra": 2021, +"minuteOfHour": 40, +"afterNow": false, +"millisOfSecond": 0 +} +} +}, +"containerID": "docker://345", +"lastState": {} +} +], +"podIPs": [ +{ +"ip": "10.33.167.185" +} +] +} +} +], +"account": "test-account" +} diff --git a/orca-clouddriver/src/test/resources/com/netflix/spinnaker/orca/clouddriver/tasks/manifest/deploy-manifest-context.json b/orca-clouddriver/src/test/resources/com/netflix/spinnaker/orca/clouddriver/tasks/manifest/deploy-manifest-context.json new file mode 100644 index 0000000000..f413f4d84a --- /dev/null +++ b/orca-clouddriver/src/test/resources/com/netflix/spinnaker/orca/clouddriver/tasks/manifest/deploy-manifest-context.json @@ -0,0 +1,1191 @@ +{ + "deploy.account.name": "k8s-v2-account", + "outputs.createdArtifacts": [ + { + "customKind": false, + "reference": "render-helm-output-manifest-sfcdtest-01fcm0tny37w064h7g4tjnyxxc-v000", + "metadata": { + "account": "k8s-v2-account" + }, + "name": "render-helm-output-manifest-sfcdtest-01fcm0tny37w064h7g4tjnyxxc", + "location": "test", + "type": "kubernetes/configMap", + "version": "v000" + }, + { + "customKind": false, + "reference": "test", + "metadata": { + "account": "k8s-v2-account" + }, + "name": "test", + "location": "", + "type": "kubernetes/namespace", + "version": "" + }, + { + "customKind": false, + "reference": "render-helm-output-manifest-sfcdtest-01fcm0tny37w064h7g4tjnyxxc", + "metadata": { + "account": "k8s-v2-account" + }, + "name": "render-helm-output-manifest-sfcdtest-01fcm0tny37w064h7g4tjnyxxc", + "location": "test", + "type": "kubernetes/deployment", + "version": "" + } + ], + "source": "text", + "stableManifests": [ + { + "manifestName": "namespace test", + "location": "" + }, + { + "manifestName": "configMap render-helm-output-manifest-sfcdtest-01fcm0tny37w064h7g4tjnyxxc-v000", + "location": "test" + }, + { + "manifestName": "deployment render-helm-output-manifest-sfcdtest-01fcm0tny37w064h7g4tjnyxxc", + "location": "test" + } + ], + "cloudProvider": "kubernetes", + "kato.result.expected": false, + "trafficManagement": { + "options": { + "enableTraffic": false + }, + "enabled": false + }, + "deploy.server.groups": {}, + "kato.last.task.id": { + "id": "01FCM0V6ECYWSVCPC0V3QWR1QW" + }, + "requiredArtifacts": [], + "artifacts": [ + { + "customKind": false, + "reference": "render-helm-output-manifest-sfcdtest-01fcm0tny37w064h7g4tjnyxxc-v000", + "metadata": { + "account": "k8s-v2-account" + }, + "name": "render-helm-output-manifest-sfcdtest-01fcm0tny37w064h7g4tjnyxxc", + "location": "test", + "type": "kubernetes/configMap", + "version": "v000" + }, + { + "customKind": false, + "reference": "test", + "metadata": { + "account": "k8s-v2-account" + }, + "name": "test", + "location": "", + "type": "kubernetes/namespace", + "version": "" + }, + { + "customKind": false, + "reference": "render-helm-output-manifest-sfcdtest-01fcm0tny37w064h7g4tjnyxxc", + "metadata": { + "account": "k8s-v2-account" + }, + "name": "render-helm-output-manifest-sfcdtest-01fcm0tny37w064h7g4tjnyxxc", + "location": "test", + "type": "kubernetes/deployment", + "version": "" + } + ], + "kato.task.terminalRetryCount": 0, + "moniker": { + "app": "sfcdservicetestapp" + }, + "kato.task.firstNotFoundRetry": -1, + "outputs.manifestNamesByNamespace": { + "": [ + "namespace test" + ], + "test": [ + "configMap render-helm-output-manifest-sfcdtest-01fcm0tny37w064h7g4tjnyxxc-v000", + "deployment render-helm-output-manifest-sfcdtest-01fcm0tny37w064h7g4tjnyxxc" + ] + }, + "optionalArtifacts": [], + "outputs.boundArtifacts": [ + { + "customKind": false, + "reference": "render-helm-output-manifest-sfcdtest-01fcm0tny37w064h7g4tjnyxxc-v000", + "metadata": { + "account": "k8s-v2-account" + }, + "name": "render-helm-output-manifest-sfcdtest-01fcm0tny37w064h7g4tjnyxxc", + "location": "test", + "type": "kubernetes/configMap", + "version": "v000" + } + ], + "manifests": [ + { + "metadata": { + "name": "test" + }, + "apiVersion": "v1", + "kind": "Namespace" + }, + { + "metadata": { + "name": "render-helm-output-manifest-sfcdtest-01fcm0tny37w064h7g4tjnyxxc", + "namespace": "test", + "labels": { + "helm.sh/chart": "nginx-0.0.2", + "app.kubernetes.io/managed-by": "Tiller", + "app.kubernetes.io/name": "sfcdtest", + "app.kubernetes.io/instance": "render-helm-output-manifest" + } + }, + "apiVersion": "v1", + "data": { + "nginx.conf": "events", + "index.html": "

Hello

Test Service Version 2.0

" + }, + "kind": "ConfigMap" + }, + { + "metadata": { + "name": "render-helm-output-manifest-sfcdtest-01fcm0tny37w064h7g4tjnyxxc", + "namespace": "test", + "labels": { + "app.kubernetes.io/managed-by": "Tiller", + "app.kubernetes.io/name": "sfcdtest", + "app.kubernetes.io/instance": "render-helm-output-manifest" + } + }, + "apiVersion": "apps/v1", + "kind": "Deployment", + "spec": { + "template": { + "metadata": { + "annotations": { + "service": "sfcd-quality" + }, + "labels": { + "app.kubernetes.io/name": "sfcdtest", + "app.kubernetes.io/instance": "render-helm-output-manifest" + } + }, + "spec": { + "volumes": [ + { + "configMap": { + "name": "render-helm-output-manifest-sfcdtest-01fcm0tny37w064h7g4tjnyxxc" + }, + "name": "wwwdata-volume" + } + ], + "containers": [ + { + "image": "test-image-1", + "imagePullPolicy": "Always", + "name": "test", + "resources": { + "requests": { + "memory": "64Mi", + "cpu": "500m" + }, + "limits": { + "memory": "128Mi", + "cpu": "500m" + } + }, + "ports": [ + { + "protocol": "TCP", + "name": "https", + "containerPort": 443 + } + ], + "volumeMounts": [ + { + "mountPath": "/usr/share/nginx/html", + "name": "wwwdata-volume" + } + ] + } + ], + "securityContext": { + "runAsUser": 7447, + "fsGroup": 7447, + "runAsNonRoot": true + } + } + }, + "replicas": 3, + "selector": { + "matchLabels": { + "app.kubernetes.io/name": "sfcdtest", + "app.kubernetes.io/instance": "render-helm-output-manifest" + } + }, + "strategy": { + "type": "RollingUpdate" + } + } + } + ], + "messages": [], + "failedManifests": [], + "kato.tasks": [ + { + "resultObjects": [ + { + "createdArtifacts": [ + { + "customKind": false, + "reference": "render-helm-output-manifest-sfcdtest-01fcm0tny37w064h7g4tjnyxxc-v000", + "metadata": { + "account": "k8s-v2-account" + }, + "name": "render-helm-output-manifest-sfcdtest-01fcm0tny37w064h7g4tjnyxxc", + "location": "test", + "type": "kubernetes/configMap", + "version": "v000" + }, + { + "customKind": false, + "reference": "test", + "metadata": { + "account": "k8s-v2-account" + }, + "name": "test", + "location": "", + "type": "kubernetes/namespace", + "version": "" + }, + { + "customKind": false, + "reference": "render-helm-output-manifest-sfcdtest-01fcm0tny37w064h7g4tjnyxxc", + "metadata": { + "account": "k8s-v2-account" + }, + "name": "render-helm-output-manifest-sfcdtest-01fcm0tny37w064h7g4tjnyxxc", + "location": "test", + "type": "kubernetes/deployment", + "version": "" + } + ], + "manifests": [ + { + "metadata": { + "uid": "7ff137ec-97bc-4fb2-967b-eda32be6fafa", + "managedFields": [ + { + "apiVersion": "v1", + "fieldsV1": { + "f:data": { + "f:index.html": {}, + "f:nginx.conf": {}, + ".": {} + }, + "f:metadata": { + "f:annotations": { + "f:artifact.spinnaker.io/version": {}, + "f:artifact.spinnaker.io/name": {}, + "f:moniker.spinnaker.io/cluster": {}, + "f:kubectl.kubernetes.io/last-applied-configuration": {}, + "f:artifact.spinnaker.io/type": {}, + "f:moniker.spinnaker.io/sequence": {}, + "f:moniker.spinnaker.io/application": {}, + "f:artifact.spinnaker.io/location": {}, + ".": {} + }, + "f:labels": { + "f:helm.sh/chart": {}, + "f:app.kubernetes.io/instance": {}, + "f:app.kubernetes.io/name": {}, + "f:moniker.spinnaker.io/sequence": {}, + ".": {}, + "f:app.kubernetes.io/managed-by": {} + } + } + }, + "manager": "kubectl-client-side-apply", + "time": "2021-08-08T23:16:16Z", + "operation": "Update", + "fieldsType": "FieldsV1" + } + ], + "resourceVersion": "344741371", + "creationTimestamp": "2021-08-08T23:16:16Z", + "name": "render-helm-output-manifest-sfcdtest-01fcm0tny37w064h7g4tjnyxxc-v000", + "namespace": "test", + "annotations": { + "artifact.spinnaker.io/version": "v000", + "artifact.spinnaker.io/name": "render-helm-output-manifest-sfcdtest-01fcm0tny37w064h7g4tjnyxxc", + "artifact.spinnaker.io/location": "test", + "artifact.spinnaker.io/type": "kubernetes/configMap", + "kubectl.kubernetes.io/last-applied-configuration": "{\"apiVersion\":\"v1\",\"data\":{\"index.html\":\"\\u003ch1\\u003eHello\\u003c/h1\\u003e \\u003cp\\u003eTest Service Version 2.0\\u003c/p\\u003e\",\"nginx.conf\":\"events {\\n worker_connections 1024;\\n}\\nhttp {\\n server {\\n listen 80 default_server;\\n server_name _;\\n return 301 https://$host$request_uri;\\n }\\n server {\\n listen 443 ssl;\\n ssl_protocols TLSv1.2 TLSv1.3;\\n location / {\\n return 200 \\\"===============================\\\\n\\\\n This is your helm deploy 3.0! \\\\n\\\\n===============================\\\\n\\\";\\n }\\n }\\n}\\n\"},\"kind\":\"ConfigMap\",\"metadata\":{\"annotations\":{\"artifact.spinnaker.io/location\":\"test\",\"artifact.spinnaker.io/name\":\"render-helm-output-manifest-sfcdtest-01fcm0tny37w064h7g4tjnyxxc\",\"artifact.spinnaker.io/type\":\"kubernetes/configMap\",\"artifact.spinnaker.io/version\":\"v000\",\"moniker.spinnaker.io/application\":\"sfcdservicetestapp\",\"moniker.spinnaker.io/cluster\":\"configMap render-helm-output-manifest-sfcdtest-01fcm0tny37w064h7g4tjnyxxc\",\"moniker.spinnaker.io/sequence\":\"0\"},\"labels\":{\"app.kubernetes.io/instance\":\"render-helm-output-manifest\",\"app.kubernetes.io/managed-by\":\"spinnaker\",\"app.kubernetes.io/name\":\"sfcdtest\",\"helm.sh/chart\":\"nginx-0.0.2\",\"moniker.spinnaker.io/sequence\":\"0\"},\"name\":\"render-helm-output-manifest-sfcdtest-01fcm0tny37w064h7g4tjnyxxc-v000\",\"namespace\":\"test\"}}\n", + "moniker.spinnaker.io/cluster": "configMap render-helm-output-manifest-sfcdtest-01fcm0tny37w064h7g4tjnyxxc", + "moniker.spinnaker.io/sequence": "0", + "moniker.spinnaker.io/application": "sfcdservicetestapp" + }, + "labels": { + "helm.sh/chart": "nginx-0.0.2", + "app.kubernetes.io/managed-by": "spinnaker", + "app.kubernetes.io/name": "sfcdtest", + "app.kubernetes.io/instance": "render-helm-output-manifest", + "moniker.spinnaker.io/sequence": "0" + }, + "selfLink": "/api/v1/namespaces/test/configmaps/render-helm-output-manifest-sfcdtest-01fcm0tny37w064h7g4tjnyxxc-v000" + }, + "apiVersion": "v1", + "data": { + "nginx.conf": "events", + "index.html": "

Hello

Test Service Version 2.0

" + }, + "kind": "ConfigMap" + }, + { + "metadata": { + "uid": "ece1ef30-1bc4-11ea-9227-42010a3e0138", + "resourceVersion": "343481080", + "creationTimestamp": "2019-12-11T03:18:38Z", + "name": "test", + "annotations": { + "artifact.spinnaker.io/version": "", + "artifact.spinnaker.io/name": "test", + "artifact.spinnaker.io/location": "", + "artifact.spinnaker.io/type": "kubernetes/namespace", + "kubectl.kubernetes.io/last-applied-configuration": "{\"apiVersion\":\"v1\",\"kind\":\"Namespace\",\"metadata\":{\"annotations\":{\"artifact.spinnaker.io/location\":\"\",\"artifact.spinnaker.io/name\":\"test\",\"artifact.spinnaker.io/type\":\"kubernetes/namespace\",\"artifact.spinnaker.io/version\":\"\",\"moniker.spinnaker.io/application\":\"sfcdservicetestapp\",\"moniker.spinnaker.io/cluster\":\"namespace test\"},\"labels\":{\"app.kubernetes.io/managed-by\":\"spinnaker\",\"app.kubernetes.io/name\":\"sfcdservicetestapp\"},\"name\":\"test\"}}\n", + "moniker.spinnaker.io/cluster": "namespace test", + "moniker.spinnaker.io/application": "sfcdservicetestapp" + }, + "labels": { + "app.kubernetes.io/managed-by": "spinnaker", + "app.kubernetes.io/name": "sfcdservicetestapp" + }, + "selfLink": "/api/v1/namespaces/test" + }, + "apiVersion": "v1", + "kind": "Namespace", + "spec": { + "finalizers": [ + "kubernetes" + ] + }, + "status": { + "phase": "Active" + } + }, + { + "metadata": { + "generation": 1, + "uid": "914ec807-9ece-4029-8942-01631a192f57", + "managedFields": [ + { + "apiVersion": "apps/v1", + "fieldsV1": { + "f:metadata": { + "f:annotations": { + "f:artifact.spinnaker.io/version": {}, + "f:artifact.spinnaker.io/name": {}, + "f:moniker.spinnaker.io/cluster": {}, + "f:kubectl.kubernetes.io/last-applied-configuration": {}, + "f:artifact.spinnaker.io/type": {}, + "f:moniker.spinnaker.io/application": {}, + "f:artifact.spinnaker.io/location": {}, + ".": {} + }, + "f:labels": { + "f:app.kubernetes.io/instance": {}, + "f:app.kubernetes.io/name": {}, + ".": {}, + "f:app.kubernetes.io/managed-by": {} + } + }, + "f:spec": { + "f:template": { + "f:metadata": { + "f:annotations": { + "f:artifact.spinnaker.io/version": {}, + "f:artifact.spinnaker.io/name": {}, + "f:moniker.spinnaker.io/cluster": {}, + "f:artifact.spinnaker.io/type": {}, + "f:moniker.spinnaker.io/application": {}, + "f:artifact.spinnaker.io/location": {}, + "f:service": {}, + ".": {} + }, + "f:labels": { + "f:app.kubernetes.io/instance": {}, + "f:app.kubernetes.io/name": {}, + ".": {}, + "f:app.kubernetes.io/managed-by": {} + } + }, + "f:spec": { + "f:securityContext": { + "f:fsGroup": {}, + "f:runAsNonRoot": {}, + ".": {}, + "f:runAsUser": {} + }, + "f:restartPolicy": {}, + "f:schedulerName": {}, + "f:containers": { + "k:{\"name\":\"sfcdtest\"}": { + "f:imagePullPolicy": {}, + "f:volumeMounts": { + "k:{\"mountPath\":\"/usr/share/nginx/html\"}": { + "f:mountPath": {}, + "f:name": {}, + ".": {} + }, + ".": {} + }, + "f:terminationMessagePath": {}, + "f:terminationMessagePolicy": {}, + "f:image": {}, + "f:name": {}, + "f:ports": { + "k:{\"containerPort\":443,\"protocol\":\"TCP\"}": { + "f:protocol": {}, + "f:containerPort": {}, + "f:name": {}, + ".": {} + }, + ".": {} + }, + ".": {}, + "f:resources": { + "f:limits": { + "f:memory": {}, + "f:cpu": {}, + ".": {} + }, + "f:requests": { + "f:memory": {}, + "f:cpu": {}, + ".": {} + }, + ".": {} + } + } + }, + "f:dnsPolicy": {}, + "f:terminationGracePeriodSeconds": {}, + "f:volumes": { + "k:{\"name\":\"wwwdata-volume\"}": { + "f:configMap": { + "f:defaultMode": {}, + "f:name": {}, + ".": {} + }, + "f:name": {}, + ".": {} + }, + ".": {} + } + } + }, + "f:strategy": { + "f:rollingUpdate": { + "f:maxSurge": {}, + "f:maxUnavailable": {}, + ".": {} + }, + "f:type": {} + }, + "f:selector": { + "f:matchLabels": { + "f:app.kubernetes.io/instance": {}, + "f:app.kubernetes.io/name": {}, + ".": {} + } + }, + "f:progressDeadlineSeconds": {}, + "f:replicas": {}, + "f:revisionHistoryLimit": {} + } + }, + "manager": "kubectl-client-side-apply", + "time": "2021-08-08T23:16:17Z", + "operation": "Update", + "fieldsType": "FieldsV1" + } + ], + "resourceVersion": "344741384", + "creationTimestamp": "2021-08-08T23:16:17Z", + "name": "render-helm-output-manifest-sfcdtest-01fcm0tny37w064h7g4tjnyxxc", + "namespace": "test", + "annotations": { + "artifact.spinnaker.io/version": "", + "artifact.spinnaker.io/name": "render-helm-output-manifest-sfcdtest-01fcm0tny37w064h7g4tjnyxxc", + "artifact.spinnaker.io/location": "test", + "artifact.spinnaker.io/type": "kubernetes/deployment", + "kubectl.kubernetes.io/last-applied-configuration": "{\"apiVersion\":\"apps/v1\",\"kind\":\"Deployment\",\"metadata\":{\"annotations\":{\"artifact.spinnaker.io/location\":\"test\",\"artifact.spinnaker.io/name\":\"render-helm-output-manifest-sfcdtest-01fcm0tny37w064h7g4tjnyxxc\",\"artifact.spinnaker.io/type\":\"kubernetes/deployment\",\"artifact.spinnaker.io/version\":\"\",\"moniker.spinnaker.io/application\":\"sfcdservicetestapp\",\"moniker.spinnaker.io/cluster\":\"deployment render-helm-output-manifest-sfcdtest-01fcm0tny37w064h7g4tjnyxxc\"},\"labels\":{\"app.kubernetes.io/instance\":\"render-helm-output-manifest\",\"app.kubernetes.io/managed-by\":\"spinnaker\",\"app.kubernetes.io/name\":\"sfcdtest\"},\"name\":\"render-helm-output-manifest-sfcdtest-01fcm0tny37w064h7g4tjnyxxc\",\"namespace\":\"test\"},\"spec\":{\"replicas\":3,\"selector\":{\"matchLabels\":{\"app.kubernetes.io/instance\":\"render-helm-output-manifest\",\"app.kubernetes.io/name\":\"sfcdtest\"}},\"strategy\":{\"type\":\"RollingUpdate\"},\"template\":{\"metadata\":{\"annotations\":{\"artifact.spinnaker.io/location\":\"test\",\"artifact.spinnaker.io/name\":\"render-helm-output-manifest-sfcdtest-01fcm0tny37w064h7g4tjnyxxc\",\"artifact.spinnaker.io/type\":\"kubernetes/deployment\",\"artifact.spinnaker.io/version\":\"\",\"moniker.spinnaker.io/application\":\"sfcdservicetestapp\",\"moniker.spinnaker.io/cluster\":\"deployment render-helm-output-manifest-sfcdtest-01fcm0tny37w064h7g4tjnyxxc\",\"service\":\"sfcd-quality\"},\"labels\":{\"app.kubernetes.io/instance\":\"render-helm-output-manifest\",\"app.kubernetes.io/managed-by\":\"spinnaker\",\"app.kubernetes.io/name\":\"sfcdtest\"}},\"spec\":{\"containers\":[{\"image\":\"gcr.io/gsf-mgmt-devmvp-spinnaker/sfci/3pp/3pp/docker.io/nginxinc/nginx-unprivileged:1.18\",\"imagePullPolicy\":\"Always\",\"name\":\"sfcdtest\",\"ports\":[{\"containerPort\":443,\"name\":\"https\",\"protocol\":\"TCP\"}],\"resources\":{\"limits\":{\"cpu\":\"500m\",\"memory\":\"128Mi\"},\"requests\":{\"cpu\":\"500m\",\"memory\":\"64Mi\"}},\"volumeMounts\":[{\"mountPath\":\"/usr/share/nginx/html\",\"name\":\"wwwdata-volume\"}]}],\"securityContext\":{\"fsGroup\":7447,\"runAsNonRoot\":true,\"runAsUser\":7447},\"volumes\":[{\"configMap\":{\"name\":\"render-helm-output-manifest-sfcdtest-01fcm0tny37w064h7g4tjnyxxc-v000\"},\"name\":\"wwwdata-volume\"}]}}}}\n", + "moniker.spinnaker.io/cluster": "deployment render-helm-output-manifest-sfcdtest-01fcm0tny37w064h7g4tjnyxxc", + "moniker.spinnaker.io/application": "sfcdservicetestapp" + }, + "labels": { + "app.kubernetes.io/managed-by": "spinnaker", + "app.kubernetes.io/name": "sfcdtest", + "app.kubernetes.io/instance": "render-helm-output-manifest" + }, + "selfLink": "/apis/apps/v1/namespaces/test/deployments/render-helm-output-manifest-sfcdtest-01fcm0tny37w064h7g4tjnyxxc" + }, + "apiVersion": "apps/v1", + "kind": "Deployment", + "spec": { + "template": { + "metadata": { + "annotations": { + "artifact.spinnaker.io/version": "", + "artifact.spinnaker.io/name": "render-helm-output-manifest-sfcdtest-01fcm0tny37w064h7g4tjnyxxc", + "service": "sfcd-quality", + "artifact.spinnaker.io/location": "test", + "artifact.spinnaker.io/type": "kubernetes/deployment", + "moniker.spinnaker.io/cluster": "deployment render-helm-output-manifest-sfcdtest-01fcm0tny37w064h7g4tjnyxxc", + "moniker.spinnaker.io/application": "sfcdservicetestapp" + }, + "labels": { + "app.kubernetes.io/managed-by": "spinnaker", + "app.kubernetes.io/name": "sfcdtest", + "app.kubernetes.io/instance": "render-helm-output-manifest" + } + }, + "spec": { + "dnsPolicy": "ClusterFirst", + "terminationGracePeriodSeconds": 30, + "volumes": [ + { + "configMap": { + "defaultMode": 420, + "name": "render-helm-output-manifest-sfcdtest-01fcm0tny37w064h7g4tjnyxxc-v000" + }, + "name": "wwwdata-volume" + } + ], + "containers": [ + { + "image": "test-image:1", + "imagePullPolicy": "Always", + "terminationMessagePolicy": "File", + "terminationMessagePath": "/dev/termination-log", + "name": "sfcdtest", + "resources": { + "requests": { + "memory": "64Mi", + "cpu": "500m" + }, + "limits": { + "memory": "128Mi", + "cpu": "500m" + } + }, + "ports": [ + { + "protocol": "TCP", + "name": "https", + "containerPort": 443 + } + ], + "volumeMounts": [ + { + "mountPath": "/usr/share/nginx/html", + "name": "wwwdata-volume" + } + ] + } + ], + "securityContext": { + "runAsUser": 7447, + "fsGroup": 7447, + "runAsNonRoot": true + }, + "restartPolicy": "Always", + "schedulerName": "default-scheduler" + } + }, + "replicas": 3, + "revisionHistoryLimit": 10, + "selector": { + "matchLabels": { + "app.kubernetes.io/name": "sfcdtest", + "app.kubernetes.io/instance": "render-helm-output-manifest" + } + }, + "strategy": { + "type": "RollingUpdate", + "rollingUpdate": { + "maxSurge": "25%", + "maxUnavailable": "25%" + } + }, + "progressDeadlineSeconds": 600 + }, + "status": {} + } + ], + "manifestNamesByNamespace": { + "": [ + "namespace test" + ], + "test": [ + "configMap render-helm-output-manifest-sfcdtest-01fcm0tny37w064h7g4tjnyxxc-v000", + "deployment render-helm-output-manifest-sfcdtest-01fcm0tny37w064h7g4tjnyxxc" + ] + }, + "boundArtifacts": [ + { + "customKind": false, + "reference": "render-helm-output-manifest-sfcdtest-01fcm0tny37w064h7g4tjnyxxc-v000", + "metadata": { + "account": "k8s-v2-account" + }, + "name": "render-helm-output-manifest-sfcdtest-01fcm0tny37w064h7g4tjnyxxc", + "location": "test", + "type": "kubernetes/configMap", + "version": "v000" + } + ] + } + ], + "id": "01FCM0TR15FKVECTE4TGFGCEJV", + "history": [ + { + "phase": "ORCHESTRATION", + "status": "Initializing Orchestration Task" + }, + { + "phase": "ORCHESTRATION", + "status": "Processing op: KubernetesDeployManifestOperation" + }, + { + "phase": "DEPLOY_KUBERNETES_MANIFEST", + "status": "Beginning deployment of manifest..." + }, + { + "phase": "DEPLOY_KUBERNETES_MANIFEST", + "status": "Sorting manifests by priority..." + }, + { + "phase": "DEPLOY_KUBERNETES_MANIFEST", + "status": "Finding deployer for configMap..." + }, + { + "phase": "DEPLOY_KUBERNETES_MANIFEST", + "status": "Finding deployer for namespace..." + }, + { + "phase": "DEPLOY_KUBERNETES_MANIFEST", + "status": "Finding deployer for deployment..." + }, + { + "phase": "DEPLOY_KUBERNETES_MANIFEST", + "status": "Finding deployer for configMap..." + }, + { + "phase": "DEPLOY_KUBERNETES_MANIFEST", + "status": "Deploy order is: namespace test, configMap render-helm-output-manifest-sfcdtest-01fcm0tny37w064h7g4tjnyxxc, deployment render-helm-output-manifest-sfcdtest-01fcm0tny37w064h7g4tjnyxxc" + }, + { + "phase": "DEPLOY_KUBERNETES_MANIFEST", + "status": "Binding artifacts in namespace test..." + }, + { + "phase": "DEPLOY_KUBERNETES_MANIFEST", + "status": "Finding deployer for namespace..." + }, + { + "phase": "DEPLOY_KUBERNETES_MANIFEST", + "status": "Bound artifacts: []..." + }, + { + "phase": "DEPLOY_KUBERNETES_MANIFEST", + "status": "Finding deployer for namespace..." + }, + { + "phase": "DEPLOY_KUBERNETES_MANIFEST", + "status": "Annotating manifest namespace test with artifact, relationships & moniker..." + }, + { + "phase": "DEPLOY_KUBERNETES_MANIFEST", + "status": "Binding artifacts in configMap render-helm-output-manifest-sfcdtest-01fcm0tny37w064h7g4tjnyxxc..." + }, + { + "phase": "DEPLOY_KUBERNETES_MANIFEST", + "status": "Finding deployer for configMap..." + }, + { + "phase": "DEPLOY_KUBERNETES_MANIFEST", + "status": "Bound artifacts: []..." + }, + { + "phase": "DEPLOY_KUBERNETES_MANIFEST", + "status": "Finding deployer for configMap..." + }, + { + "phase": "DEPLOY_KUBERNETES_MANIFEST", + "status": "Annotating manifest configMap render-helm-output-manifest-sfcdtest-01fcm0tny37w064h7g4tjnyxxc with artifact, relationships & moniker..." + }, + { + "phase": "DEPLOY_KUBERNETES_MANIFEST", + "status": "Binding artifacts in deployment render-helm-output-manifest-sfcdtest-01fcm0tny37w064h7g4tjnyxxc..." + }, + { + "phase": "DEPLOY_KUBERNETES_MANIFEST", + "status": "Finding deployer for deployment..." + }, + { + "phase": "DEPLOY_KUBERNETES_MANIFEST", + "status": "Bound artifacts: [Artifact(type=kubernetes/configMap, customKind=false, name=render-helm-output-manifest-sfcdtest-01fcm0tny37w064h7g4tjnyxxc, version=v000, location=test, reference=render-helm-output-manifest-sfcdtest-01fcm0tny37w064h7g4tjnyxxc-v000, metadata={account=k8s-v2-account}, artifactAccount=null, provenance=null, uuid=null)]..." + }, + { + "phase": "DEPLOY_KUBERNETES_MANIFEST", + "status": "Finding deployer for deployment..." + }, + { + "phase": "DEPLOY_KUBERNETES_MANIFEST", + "status": "Annotating manifest deployment render-helm-output-manifest-sfcdtest-01fcm0tny37w064h7g4tjnyxxc with artifact, relationships & moniker..." + }, + { + "phase": "DEPLOY_KUBERNETES_MANIFEST", + "status": "Checking if all requested artifacts were bound..." + }, + { + "phase": "DEPLOY_KUBERNETES_MANIFEST", + "status": "Finding deployer for namespace..." + }, + { + "phase": "DEPLOY_KUBERNETES_MANIFEST", + "status": "Submitting manifest namespace test to kubernetes master..." + }, + { + "phase": "DEPLOY_KUBERNETES_MANIFEST", + "status": "Finding deployer for configMap..." + }, + { + "phase": "DEPLOY_KUBERNETES_MANIFEST", + "status": "Submitting manifest configMap render-helm-output-manifest-sfcdtest-01fcm0tny37w064h7g4tjnyxxc-v000 to kubernetes master..." + }, + { + "phase": "DEPLOY_KUBERNETES_MANIFEST", + "status": "Finding deployer for deployment..." + }, + { + "phase": "DEPLOY_KUBERNETES_MANIFEST", + "status": "Submitting manifest deployment render-helm-output-manifest-sfcdtest-01fcm0tny37w064h7g4tjnyxxc to kubernetes master..." + }, + { + "phase": "DEPLOY_KUBERNETES_MANIFEST", + "status": "Deploy manifest task completed successfully." + }, + { + "phase": "ORCHESTRATION", + "status": "Orchestration completed." + }, + { + "phase": "ORCHESTRATION", + "status": "Orchestration completed." + } + ], + "status": { + "retryable": false, + "completed": true, + "failed": false + } + }, + { + "resultObjects": [ + { + "createdArtifacts": [], + "manifestNamesByNamespace": {}, + "boundArtifacts": [] + } + ], + "id": "01FCM0V6ECYWSVCPC0V3QWR1QW", + "history": [ + { + "phase": "ORCHESTRATION", + "status": "Initializing Orchestration Task" + }, + { + "phase": "ORCHESTRATION", + "status": "Processing op: KubernetesCleanupArtifactsOperation" + }, + { + "phase": "ORCHESTRATION", + "status": "Orchestration completed." + }, + { + "phase": "ORCHESTRATION", + "status": "Orchestration completed." + } + ], + "status": { + "retryable": false, + "completed": true, + "failed": false + } + } + ], + "outputs.manifests": [ + { + "metadata": { + "uid": "7ff137ec-97bc-4fb2-967b-eda32be6fafa", + "managedFields": [ + { + "apiVersion": "v1", + "fieldsV1": { + "f:data": { + "f:index.html": {}, + "f:nginx.conf": {}, + ".": {} + }, + "f:metadata": { + "f:annotations": { + "f:artifact.spinnaker.io/version": {}, + "f:artifact.spinnaker.io/name": {}, + "f:moniker.spinnaker.io/cluster": {}, + "f:kubectl.kubernetes.io/last-applied-configuration": {}, + "f:artifact.spinnaker.io/type": {}, + "f:moniker.spinnaker.io/sequence": {}, + "f:moniker.spinnaker.io/application": {}, + "f:artifact.spinnaker.io/location": {}, + ".": {} + }, + "f:labels": { + "f:helm.sh/chart": {}, + "f:app.kubernetes.io/instance": {}, + "f:app.kubernetes.io/name": {}, + "f:moniker.spinnaker.io/sequence": {}, + ".": {}, + "f:app.kubernetes.io/managed-by": {} + } + } + }, + "manager": "kubectl-client-side-apply", + "time": "2021-08-08T23:16:16Z", + "operation": "Update", + "fieldsType": "FieldsV1" + } + ], + "resourceVersion": "344741371", + "creationTimestamp": "2021-08-08T23:16:16Z", + "name": "render-helm-output-manifest-sfcdtest-01fcm0tny37w064h7g4tjnyxxc-v000", + "namespace": "test", + "annotations": { + "artifact.spinnaker.io/version": "v000", + "artifact.spinnaker.io/name": "render-helm-output-manifest-sfcdtest-01fcm0tny37w064h7g4tjnyxxc", + "artifact.spinnaker.io/location": "test", + "artifact.spinnaker.io/type": "kubernetes/configMap", + "kubectl.kubernetes.io/last-applied-configuration": "{\"apiVersion\":\"v1\",\"data\":{\"index.html\":\"\\u003ch1\\u003eHello\\u003c/h1\\u003e \\u003cp\\u003eTest Service Version 2.0\\u003c/p\\u003e\",\"nginx.conf\":\"events {\\n worker_connections 1024;\\n}\\nhttp {\\n server {\\n listen 80 default_server;\\n server_name _;\\n return 301 https://$host$request_uri;\\n }\\n server {\\n listen 443 ssl;\\n ssl_protocols TLSv1.2 TLSv1.3;\\n location / {\\n return 200 \\\"===============================\\\\n\\\\n This is your helm deploy 3.0! \\\\n\\\\n===============================\\\\n\\\";\\n }\\n }\\n}\\n\"},\"kind\":\"ConfigMap\",\"metadata\":{\"annotations\":{\"artifact.spinnaker.io/location\":\"test\",\"artifact.spinnaker.io/name\":\"render-helm-output-manifest-sfcdtest-01fcm0tny37w064h7g4tjnyxxc\",\"artifact.spinnaker.io/type\":\"kubernetes/configMap\",\"artifact.spinnaker.io/version\":\"v000\",\"moniker.spinnaker.io/application\":\"sfcdservicetestapp\",\"moniker.spinnaker.io/cluster\":\"configMap render-helm-output-manifest-sfcdtest-01fcm0tny37w064h7g4tjnyxxc\",\"moniker.spinnaker.io/sequence\":\"0\"},\"labels\":{\"app.kubernetes.io/instance\":\"render-helm-output-manifest\",\"app.kubernetes.io/managed-by\":\"spinnaker\",\"app.kubernetes.io/name\":\"sfcdtest\",\"helm.sh/chart\":\"nginx-0.0.2\",\"moniker.spinnaker.io/sequence\":\"0\"},\"name\":\"render-helm-output-manifest-sfcdtest-01fcm0tny37w064h7g4tjnyxxc-v000\",\"namespace\":\"test\"}}\n", + "moniker.spinnaker.io/cluster": "configMap render-helm-output-manifest-sfcdtest-01fcm0tny37w064h7g4tjnyxxc", + "moniker.spinnaker.io/sequence": "0", + "moniker.spinnaker.io/application": "sfcdservicetestapp" + }, + "labels": { + "helm.sh/chart": "nginx-0.0.2", + "app.kubernetes.io/managed-by": "spinnaker", + "app.kubernetes.io/name": "sfcdtest", + "app.kubernetes.io/instance": "render-helm-output-manifest", + "moniker.spinnaker.io/sequence": "0" + }, + "selfLink": "/api/v1/namespaces/test/configmaps/render-helm-output-manifest-sfcdtest-01fcm0tny37w064h7g4tjnyxxc-v000" + }, + "apiVersion": "v1", + "data": { + "nginx.conf": "events", + "index.html": "

Hello

Test Service Version 2.0

" + }, + "kind": "ConfigMap" + }, + { + "metadata": { + "uid": "ece1ef30-1bc4-11ea-9227-42010a3e0138", + "resourceVersion": "343481080", + "creationTimestamp": "2019-12-11T03:18:38Z", + "name": "test", + "annotations": { + "artifact.spinnaker.io/version": "", + "artifact.spinnaker.io/name": "test", + "artifact.spinnaker.io/location": "", + "artifact.spinnaker.io/type": "kubernetes/namespace", + "kubectl.kubernetes.io/last-applied-configuration": "{\"apiVersion\":\"v1\",\"kind\":\"Namespace\",\"metadata\":{\"annotations\":{\"artifact.spinnaker.io/location\":\"\",\"artifact.spinnaker.io/name\":\"test\",\"artifact.spinnaker.io/type\":\"kubernetes/namespace\",\"artifact.spinnaker.io/version\":\"\",\"moniker.spinnaker.io/application\":\"sfcdservicetestapp\",\"moniker.spinnaker.io/cluster\":\"namespace test\"},\"labels\":{\"app.kubernetes.io/managed-by\":\"spinnaker\",\"app.kubernetes.io/name\":\"sfcdservicetestapp\"},\"name\":\"test\"}}\n", + "moniker.spinnaker.io/cluster": "namespace test", + "moniker.spinnaker.io/application": "sfcdservicetestapp" + }, + "labels": { + "app.kubernetes.io/managed-by": "spinnaker", + "app.kubernetes.io/name": "sfcdservicetestapp" + }, + "selfLink": "/api/v1/namespaces/test" + }, + "apiVersion": "v1", + "kind": "Namespace", + "spec": { + "finalizers": [ + "kubernetes" + ] + }, + "status": { + "phase": "Active" + } + }, + { + "metadata": { + "generation": 1, + "uid": "914ec807-9ece-4029-8942-01631a192f57", + "managedFields": [ + { + "apiVersion": "apps/v1", + "fieldsV1": { + "f:metadata": { + "f:annotations": { + "f:artifact.spinnaker.io/version": {}, + "f:artifact.spinnaker.io/name": {}, + "f:moniker.spinnaker.io/cluster": {}, + "f:kubectl.kubernetes.io/last-applied-configuration": {}, + "f:artifact.spinnaker.io/type": {}, + "f:moniker.spinnaker.io/application": {}, + "f:artifact.spinnaker.io/location": {}, + ".": {} + }, + "f:labels": { + "f:app.kubernetes.io/instance": {}, + "f:app.kubernetes.io/name": {}, + ".": {}, + "f:app.kubernetes.io/managed-by": {} + } + }, + "f:spec": { + "f:template": { + "f:metadata": { + "f:annotations": { + "f:artifact.spinnaker.io/version": {}, + "f:artifact.spinnaker.io/name": {}, + "f:moniker.spinnaker.io/cluster": {}, + "f:artifact.spinnaker.io/type": {}, + "f:moniker.spinnaker.io/application": {}, + "f:artifact.spinnaker.io/location": {}, + "f:service": {}, + ".": {} + }, + "f:labels": { + "f:app.kubernetes.io/instance": {}, + "f:app.kubernetes.io/name": {}, + ".": {}, + "f:app.kubernetes.io/managed-by": {} + } + }, + "f:spec": { + "f:securityContext": { + "f:fsGroup": {}, + "f:runAsNonRoot": {}, + ".": {}, + "f:runAsUser": {} + }, + "f:restartPolicy": {}, + "f:schedulerName": {}, + "f:containers": { + "k:{\"name\":\"sfcdtest\"}": { + "f:imagePullPolicy": {}, + "f:volumeMounts": { + "k:{\"mountPath\":\"/usr/share/nginx/html\"}": { + "f:mountPath": {}, + "f:name": {}, + ".": {} + }, + ".": {} + }, + "f:terminationMessagePath": {}, + "f:terminationMessagePolicy": {}, + "f:image": {}, + "f:name": {}, + "f:ports": { + "k:{\"containerPort\":443,\"protocol\":\"TCP\"}": { + "f:protocol": {}, + "f:containerPort": {}, + "f:name": {}, + ".": {} + }, + ".": {} + }, + ".": {}, + "f:resources": { + "f:limits": { + "f:memory": {}, + "f:cpu": {}, + ".": {} + }, + "f:requests": { + "f:memory": {}, + "f:cpu": {}, + ".": {} + }, + ".": {} + } + } + }, + "f:dnsPolicy": {}, + "f:terminationGracePeriodSeconds": {}, + "f:volumes": { + "k:{\"name\":\"wwwdata-volume\"}": { + "f:configMap": { + "f:defaultMode": {}, + "f:name": {}, + ".": {} + }, + "f:name": {}, + ".": {} + }, + ".": {} + } + } + }, + "f:strategy": { + "f:rollingUpdate": { + "f:maxSurge": {}, + "f:maxUnavailable": {}, + ".": {} + }, + "f:type": {} + }, + "f:selector": { + "f:matchLabels": { + "f:app.kubernetes.io/instance": {}, + "f:app.kubernetes.io/name": {}, + ".": {} + } + }, + "f:progressDeadlineSeconds": {}, + "f:replicas": {}, + "f:revisionHistoryLimit": {} + } + }, + "manager": "kubectl-client-side-apply", + "time": "2021-08-08T23:16:17Z", + "operation": "Update", + "fieldsType": "FieldsV1" + } + ], + "resourceVersion": "344741384", + "creationTimestamp": "2021-08-08T23:16:17Z", + "name": "render-helm-output-manifest-sfcdtest-01fcm0tny37w064h7g4tjnyxxc", + "namespace": "test", + "annotations": { + "artifact.spinnaker.io/version": "", + "artifact.spinnaker.io/name": "render-helm-output-manifest-sfcdtest-01fcm0tny37w064h7g4tjnyxxc", + "artifact.spinnaker.io/location": "test", + "artifact.spinnaker.io/type": "kubernetes/deployment", + "kubectl.kubernetes.io/last-applied-configuration": "{\"apiVersion\":\"apps/v1\",\"kind\":\"Deployment\",\"metadata\":{\"annotations\":{\"artifact.spinnaker.io/location\":\"test\",\"artifact.spinnaker.io/name\":\"render-helm-output-manifest-sfcdtest-01fcm0tny37w064h7g4tjnyxxc\",\"artifact.spinnaker.io/type\":\"kubernetes/deployment\",\"artifact.spinnaker.io/version\":\"\",\"moniker.spinnaker.io/application\":\"sfcdservicetestapp\",\"moniker.spinnaker.io/cluster\":\"deployment render-helm-output-manifest-sfcdtest-01fcm0tny37w064h7g4tjnyxxc\"},\"labels\":{\"app.kubernetes.io/instance\":\"render-helm-output-manifest\",\"app.kubernetes.io/managed-by\":\"spinnaker\",\"app.kubernetes.io/name\":\"sfcdtest\"},\"name\":\"render-helm-output-manifest-sfcdtest-01fcm0tny37w064h7g4tjnyxxc\",\"namespace\":\"test\"},\"spec\":{\"replicas\":3,\"selector\":{\"matchLabels\":{\"app.kubernetes.io/instance\":\"render-helm-output-manifest\",\"app.kubernetes.io/name\":\"sfcdtest\"}},\"strategy\":{\"type\":\"RollingUpdate\"},\"template\":{\"metadata\":{\"annotations\":{\"artifact.spinnaker.io/location\":\"test\",\"artifact.spinnaker.io/name\":\"render-helm-output-manifest-sfcdtest-01fcm0tny37w064h7g4tjnyxxc\",\"artifact.spinnaker.io/type\":\"kubernetes/deployment\",\"artifact.spinnaker.io/version\":\"\",\"moniker.spinnaker.io/application\":\"sfcdservicetestapp\",\"moniker.spinnaker.io/cluster\":\"deployment render-helm-output-manifest-sfcdtest-01fcm0tny37w064h7g4tjnyxxc\",\"service\":\"sfcd-quality\"},\"labels\":{\"app.kubernetes.io/instance\":\"render-helm-output-manifest\",\"app.kubernetes.io/managed-by\":\"spinnaker\",\"app.kubernetes.io/name\":\"sfcdtest\"}},\"spec\":{\"containers\":[{\"image\":\"gcr.io/gsf-mgmt-devmvp-spinnaker/sfci/3pp/3pp/docker.io/nginxinc/nginx-unprivileged:1.18\",\"imagePullPolicy\":\"Always\",\"name\":\"sfcdtest\",\"ports\":[{\"containerPort\":443,\"name\":\"https\",\"protocol\":\"TCP\"}],\"resources\":{\"limits\":{\"cpu\":\"500m\",\"memory\":\"128Mi\"},\"requests\":{\"cpu\":\"500m\",\"memory\":\"64Mi\"}},\"volumeMounts\":[{\"mountPath\":\"/usr/share/nginx/html\",\"name\":\"wwwdata-volume\"}]}],\"securityContext\":{\"fsGroup\":7447,\"runAsNonRoot\":true,\"runAsUser\":7447},\"volumes\":[{\"configMap\":{\"name\":\"render-helm-output-manifest-sfcdtest-01fcm0tny37w064h7g4tjnyxxc-v000\"},\"name\":\"wwwdata-volume\"}]}}}}\n", + "moniker.spinnaker.io/cluster": "deployment render-helm-output-manifest-sfcdtest-01fcm0tny37w064h7g4tjnyxxc", + "moniker.spinnaker.io/application": "sfcdservicetestapp" + }, + "labels": { + "app.kubernetes.io/managed-by": "spinnaker", + "app.kubernetes.io/name": "sfcdtest", + "app.kubernetes.io/instance": "render-helm-output-manifest" + }, + "selfLink": "/apis/apps/v1/namespaces/test/deployments/render-helm-output-manifest-sfcdtest-01fcm0tny37w064h7g4tjnyxxc" + }, + "apiVersion": "apps/v1", + "kind": "Deployment", + "spec": { + "template": { + "metadata": { + "annotations": { + "artifact.spinnaker.io/version": "", + "artifact.spinnaker.io/name": "render-helm-output-manifest-sfcdtest-01fcm0tny37w064h7g4tjnyxxc", + "service": "sfcd-quality", + "artifact.spinnaker.io/location": "test", + "artifact.spinnaker.io/type": "kubernetes/deployment", + "moniker.spinnaker.io/cluster": "deployment render-helm-output-manifest-sfcdtest-01fcm0tny37w064h7g4tjnyxxc", + "moniker.spinnaker.io/application": "sfcdservicetestapp" + }, + "labels": { + "app.kubernetes.io/managed-by": "spinnaker", + "app.kubernetes.io/name": "sfcdtest", + "app.kubernetes.io/instance": "render-helm-output-manifest" + } + }, + "spec": { + "dnsPolicy": "ClusterFirst", + "terminationGracePeriodSeconds": 30, + "volumes": [ + { + "configMap": { + "defaultMode": 420, + "name": "render-helm-output-manifest-sfcdtest-01fcm0tny37w064h7g4tjnyxxc-v000" + }, + "name": "wwwdata-volume" + } + ], + "containers": [ + { + "image": "test-image:1", + "imagePullPolicy": "Always", + "terminationMessagePolicy": "File", + "terminationMessagePath": "/dev/termination-log", + "name": "sfcdtest", + "resources": { + "requests": { + "memory": "64Mi", + "cpu": "500m" + }, + "limits": { + "memory": "128Mi", + "cpu": "500m" + } + }, + "ports": [ + { + "protocol": "TCP", + "name": "https", + "containerPort": 443 + } + ], + "volumeMounts": [ + { + "mountPath": "/usr/share/nginx/html", + "name": "wwwdata-volume" + } + ] + } + ], + "securityContext": { + "runAsUser": 7447, + "fsGroup": 7447, + "runAsNonRoot": true + }, + "restartPolicy": "Always", + "schedulerName": "default-scheduler" + } + }, + "replicas": 3, + "revisionHistoryLimit": 10, + "selector": { + "matchLabels": { + "app.kubernetes.io/name": "sfcdtest", + "app.kubernetes.io/instance": "render-helm-output-manifest" + } + }, + "strategy": { + "type": "RollingUpdate", + "rollingUpdate": { + "maxSurge": "25%", + "maxUnavailable": "25%" + } + }, + "progressDeadlineSeconds": 600 + }, + "status": {} + } + ], + "kato.task.notFoundRetryCount": 0, + "account": "k8s-v2-account", + "skipExpressionEvaluation": false, + "kato.task.lastStatus": "SUCCEEDED" +} diff --git a/orca-core/src/main/java/com/netflix/spinnaker/orca/config/OrcaConfiguration.java b/orca-core/src/main/java/com/netflix/spinnaker/orca/config/OrcaConfiguration.java index 1d81afee06..f7087ae7d4 100644 --- a/orca-core/src/main/java/com/netflix/spinnaker/orca/config/OrcaConfiguration.java +++ b/orca-core/src/main/java/com/netflix/spinnaker/orca/config/OrcaConfiguration.java @@ -96,7 +96,8 @@ @EnableConfigurationProperties({ TaskOverrideConfigurationProperties.class, ExecutionConfigurationProperties.class, - ExpressionProperties.class + ExpressionProperties.class, + TaskConfigurationProperties.class }) public class OrcaConfiguration { @Bean diff --git a/orca-core/src/main/java/com/netflix/spinnaker/orca/config/TaskConfigurationProperties.java b/orca-core/src/main/java/com/netflix/spinnaker/orca/config/TaskConfigurationProperties.java new file mode 100644 index 0000000000..1d99368789 --- /dev/null +++ b/orca-core/src/main/java/com/netflix/spinnaker/orca/config/TaskConfigurationProperties.java @@ -0,0 +1,41 @@ +/* + * Copyright 2021 Salesforce.com, 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.config; + +import com.netflix.spinnaker.orca.pipeline.tasks.artifacts.BindProducedArtifactsTask; +import java.util.Set; +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; + +@Data +@ConfigurationProperties("tasks.core") +/** configuration properties for various Orca tasks that are in the orca-core module */ +public class TaskConfigurationProperties { + + /** properties that pertain to {@link BindProducedArtifactsTask} task */ + private BindProducedArtifactsTaskConfig bindProducedArtifactsTask = + new BindProducedArtifactsTaskConfig(); + + @Data + public static class BindProducedArtifactsTaskConfig { + /** + * set of keys that will be excluded from the "outputs" key in the stage execution context. + * Default or empty set means that no keys will be excluded. + */ + private Set excludeKeysFromOutputs = Set.of(); + } +} diff --git a/orca-core/src/main/java/com/netflix/spinnaker/orca/pipeline/tasks/artifacts/BindProducedArtifactsTask.java b/orca-core/src/main/java/com/netflix/spinnaker/orca/pipeline/tasks/artifacts/BindProducedArtifactsTask.java index ab9e527480..a92cc0c6a3 100644 --- a/orca-core/src/main/java/com/netflix/spinnaker/orca/pipeline/tasks/artifacts/BindProducedArtifactsTask.java +++ b/orca-core/src/main/java/com/netflix/spinnaker/orca/pipeline/tasks/artifacts/BindProducedArtifactsTask.java @@ -25,6 +25,8 @@ import com.netflix.spinnaker.orca.api.pipeline.TaskResult; import com.netflix.spinnaker.orca.api.pipeline.models.ExecutionStatus; import com.netflix.spinnaker.orca.api.pipeline.models.StageExecution; +import com.netflix.spinnaker.orca.config.TaskConfigurationProperties; +import com.netflix.spinnaker.orca.config.TaskConfigurationProperties.BindProducedArtifactsTaskConfig; import com.netflix.spinnaker.orca.pipeline.util.ArtifactResolver; import com.netflix.spinnaker.orca.pipeline.util.ArtifactUtils; import java.util.HashMap; @@ -39,10 +41,20 @@ @Slf4j public class BindProducedArtifactsTask implements Task { public static final String TASK_NAME = "bindProducedArtifacts"; + private final ArtifactUtils artifactUtils; + private final ObjectMapper objectMapper; + private final BindProducedArtifactsTaskConfig configProperties; - @Autowired ArtifactUtils artifactUtils; - - @Autowired ObjectMapper objectMapper; + @Autowired + public BindProducedArtifactsTask( + ArtifactUtils artifactUtils, + ObjectMapper objectMapper, + TaskConfigurationProperties configProperties) { + this.artifactUtils = artifactUtils; + this.objectMapper = objectMapper; + this.configProperties = configProperties.getBindProducedArtifactsTask(); + log.info("output keys to filter: {}", this.configProperties.getExcludeKeysFromOutputs()); + } @Nonnull @Override @@ -66,6 +78,14 @@ public TaskResult execute(@Nonnull StageExecution stage) { outputs.put("artifacts", resolveResult.getResolvedArtifacts()); outputs.put("resolvedExpectedArtifacts", resolveResult.getResolvedExpectedArtifacts()); - return TaskResult.builder(ExecutionStatus.SUCCEEDED).context(outputs).outputs(outputs).build(); + // exclude certain configured keys from being stored in the stage outputs + Map filteredOutputs = + filterContextOutputs(outputs, configProperties.getExcludeKeysFromOutputs()); + log.info("context outputs will only contain: {} keys", filteredOutputs.keySet()); + + return TaskResult.builder(ExecutionStatus.SUCCEEDED) + .context(outputs) + .outputs(filteredOutputs) + .build(); } } diff --git a/orca-core/src/test/java/com/netflix/spinnaker/orca/pipeline/tasks/artifacts/BindProducedArtifactsTaskTest.java b/orca-core/src/test/java/com/netflix/spinnaker/orca/pipeline/tasks/artifacts/BindProducedArtifactsTaskTest.java new file mode 100644 index 0000000000..2d64457820 --- /dev/null +++ b/orca-core/src/test/java/com/netflix/spinnaker/orca/pipeline/tasks/artifacts/BindProducedArtifactsTaskTest.java @@ -0,0 +1,143 @@ +/* + * Copyright 2021 Salesforce.com, 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.pipeline.tasks.artifacts; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.netflix.spinnaker.orca.api.pipeline.TaskResult; +import com.netflix.spinnaker.orca.api.pipeline.models.ExecutionType; +import com.netflix.spinnaker.orca.api.pipeline.models.StageExecution; +import com.netflix.spinnaker.orca.config.TaskConfigurationProperties; +import com.netflix.spinnaker.orca.pipeline.model.PipelineExecutionImpl; +import com.netflix.spinnaker.orca.pipeline.model.StageExecutionImpl; +import com.netflix.spinnaker.orca.pipeline.persistence.ExecutionRepository; +import com.netflix.spinnaker.orca.pipeline.util.ArtifactUtils; +import com.netflix.spinnaker.orca.pipeline.util.ContextParameterProcessor; +import java.io.IOException; +import java.io.InputStream; +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +@ExtendWith(MockitoExtension.class) +public class BindProducedArtifactsTaskTest { + + @Mock private ContextParameterProcessor contextParameterProcessor; + @Mock private ExecutionRepository executionRepository; + private TaskConfigurationProperties configService; + private ObjectMapper objectMapper; + private BindProducedArtifactsTask bindProducedArtifactsTask; + private ArtifactUtils artifactUtils; + + @BeforeEach + public void setup() { + configService = new TaskConfigurationProperties(); + objectMapper = new ObjectMapper(); + artifactUtils = new ArtifactUtils(objectMapper, executionRepository, contextParameterProcessor); + } + + @DisplayName( + "parameterized test to see how keys in the outputs object are filtered for a bake manifest based on the inputs") + @ParameterizedTest(name = "{index} => keys to be excluded from outputs = {0}") + @ValueSource(strings = {"", "artifacts,resolvedExpectedArtifacts"}) + void testOutputFilterForBakeManifest(String keysToFilter) throws IOException { + + // setup + Set expectedKeysToBeExcludedFromOutput = new HashSet<>(); + if (!keysToFilter.equals("")) { + expectedKeysToBeExcludedFromOutput = new HashSet<>(Arrays.asList(keysToFilter.split(","))); + } + + configService + .getBindProducedArtifactsTask() + .setExcludeKeysFromOutputs(expectedKeysToBeExcludedFromOutput); + + bindProducedArtifactsTask = + new BindProducedArtifactsTask(artifactUtils, objectMapper, configService); + + Map context = + objectMapper.readValue(getResource("bake-manifest-context.json"), Map.class); + StageExecution stageExecution = + new StageExecutionImpl( + new PipelineExecutionImpl(ExecutionType.PIPELINE, "test"), "test", context); + + // when + TaskResult result = bindProducedArtifactsTask.execute(stageExecution); + + // then + + // the 'outputs' key should not contain the values present in the input i.e. in `keysToFilter` + if (expectedKeysToBeExcludedFromOutput.containsAll( + List.of("artifacts", "resolvedExpectedArtifacts"))) { + assertThat(result.getOutputs().isEmpty()); + } else { + assertThat(result.getOutputs()).isNotEmpty(); + } + assertThat(result.getContext()).isNotEmpty(); + + Set receivedOutputsKeySet = result.getOutputs().keySet(); + for (String excludedKey : expectedKeysToBeExcludedFromOutput) { + assertThat(receivedOutputsKeySet.contains(excludedKey)).isFalse(); + } + + // ensuring that the 'context' key still has the values present in the input i.e. in + // `keysToFilter` + Set receivedContextKeySet = result.getContext().keySet(); + for (String excludedKey : expectedKeysToBeExcludedFromOutput) { + assertThat(receivedContextKeySet.contains(excludedKey)).isTrue(); + } + } + + @DisplayName("test to see how what the task returns as output for a deploy manifest") + @Test + public void testOutputFilterForDeployManifest() throws IOException { + // setup + bindProducedArtifactsTask = + new BindProducedArtifactsTask(artifactUtils, objectMapper, configService); + + Map context = + objectMapper.readValue(getResource("deploy-manifest-context.json"), Map.class); + StageExecution stageExecution = + new StageExecutionImpl( + new PipelineExecutionImpl(ExecutionType.PIPELINE, "test"), "test", context); + + // when + TaskResult result = bindProducedArtifactsTask.execute(stageExecution); + + // then + + // for deploy manifests, there is no expected Artifacts present in the stage context, so the + // result won't contain either outputs or context properties + assertThat(result.getOutputs().isEmpty()); + assertThat(result.getContext().isEmpty()); + } + + private InputStream getResource(String name) { + return BindProducedArtifactsTaskTest.class.getResourceAsStream(name); + } +} diff --git a/orca-core/src/test/resources/com/netflix/spinnaker/orca/pipeline/tasks/artifacts/bake-manifest-context.json b/orca-core/src/test/resources/com/netflix/spinnaker/orca/pipeline/tasks/artifacts/bake-manifest-context.json new file mode 100644 index 0000000000..0d20915eb5 --- /dev/null +++ b/orca-core/src/test/resources/com/netflix/spinnaker/orca/pipeline/tasks/artifacts/bake-manifest-context.json @@ -0,0 +1,44 @@ +{ + "expectedArtifacts": [ + { + "usePriorArtifact": false, + "useDefaultArtifact": false, + "id": "artifact-test-render-helm-output-manifest", + "matchArtifact": { + "kind": "base64", + "name": "render-helm-output-manifest", + "artifactAccount": "embedded-artifact", + "type": "embedded/base64" + } + } + ], + "comments": "", + "outputName": "render-helm-output-manifest", + "stageEnabled": {}, + "overrides": { + "clusterName": "123" + }, + "evaluateOverrideExpressions": false, + "inputArtifacts": [ + { + "id": "artifact-testhelm", + "account": "sfcd-helm" + } + ], + "failOnFailedExpressions": false, + "cloudProvider": "kubernetes", + "namespace": "test", + "failPipeline": true, + "rawOverrides": false, + "templateRenderer": "HELM2", + "sendNotifications": false, + "restrictExecutionDuringTimeWindow": false, + "artifacts": [{ + "customKind": false, + "reference": "some reference", + "metadata": {}, + "name": "render-helm-output-manifest", + "type": "embedded/base64" + } + ] +} diff --git a/orca-core/src/test/resources/com/netflix/spinnaker/orca/pipeline/tasks/artifacts/deploy-manifest-context.json b/orca-core/src/test/resources/com/netflix/spinnaker/orca/pipeline/tasks/artifacts/deploy-manifest-context.json new file mode 100644 index 0000000000..f413f4d84a --- /dev/null +++ b/orca-core/src/test/resources/com/netflix/spinnaker/orca/pipeline/tasks/artifacts/deploy-manifest-context.json @@ -0,0 +1,1191 @@ +{ + "deploy.account.name": "k8s-v2-account", + "outputs.createdArtifacts": [ + { + "customKind": false, + "reference": "render-helm-output-manifest-sfcdtest-01fcm0tny37w064h7g4tjnyxxc-v000", + "metadata": { + "account": "k8s-v2-account" + }, + "name": "render-helm-output-manifest-sfcdtest-01fcm0tny37w064h7g4tjnyxxc", + "location": "test", + "type": "kubernetes/configMap", + "version": "v000" + }, + { + "customKind": false, + "reference": "test", + "metadata": { + "account": "k8s-v2-account" + }, + "name": "test", + "location": "", + "type": "kubernetes/namespace", + "version": "" + }, + { + "customKind": false, + "reference": "render-helm-output-manifest-sfcdtest-01fcm0tny37w064h7g4tjnyxxc", + "metadata": { + "account": "k8s-v2-account" + }, + "name": "render-helm-output-manifest-sfcdtest-01fcm0tny37w064h7g4tjnyxxc", + "location": "test", + "type": "kubernetes/deployment", + "version": "" + } + ], + "source": "text", + "stableManifests": [ + { + "manifestName": "namespace test", + "location": "" + }, + { + "manifestName": "configMap render-helm-output-manifest-sfcdtest-01fcm0tny37w064h7g4tjnyxxc-v000", + "location": "test" + }, + { + "manifestName": "deployment render-helm-output-manifest-sfcdtest-01fcm0tny37w064h7g4tjnyxxc", + "location": "test" + } + ], + "cloudProvider": "kubernetes", + "kato.result.expected": false, + "trafficManagement": { + "options": { + "enableTraffic": false + }, + "enabled": false + }, + "deploy.server.groups": {}, + "kato.last.task.id": { + "id": "01FCM0V6ECYWSVCPC0V3QWR1QW" + }, + "requiredArtifacts": [], + "artifacts": [ + { + "customKind": false, + "reference": "render-helm-output-manifest-sfcdtest-01fcm0tny37w064h7g4tjnyxxc-v000", + "metadata": { + "account": "k8s-v2-account" + }, + "name": "render-helm-output-manifest-sfcdtest-01fcm0tny37w064h7g4tjnyxxc", + "location": "test", + "type": "kubernetes/configMap", + "version": "v000" + }, + { + "customKind": false, + "reference": "test", + "metadata": { + "account": "k8s-v2-account" + }, + "name": "test", + "location": "", + "type": "kubernetes/namespace", + "version": "" + }, + { + "customKind": false, + "reference": "render-helm-output-manifest-sfcdtest-01fcm0tny37w064h7g4tjnyxxc", + "metadata": { + "account": "k8s-v2-account" + }, + "name": "render-helm-output-manifest-sfcdtest-01fcm0tny37w064h7g4tjnyxxc", + "location": "test", + "type": "kubernetes/deployment", + "version": "" + } + ], + "kato.task.terminalRetryCount": 0, + "moniker": { + "app": "sfcdservicetestapp" + }, + "kato.task.firstNotFoundRetry": -1, + "outputs.manifestNamesByNamespace": { + "": [ + "namespace test" + ], + "test": [ + "configMap render-helm-output-manifest-sfcdtest-01fcm0tny37w064h7g4tjnyxxc-v000", + "deployment render-helm-output-manifest-sfcdtest-01fcm0tny37w064h7g4tjnyxxc" + ] + }, + "optionalArtifacts": [], + "outputs.boundArtifacts": [ + { + "customKind": false, + "reference": "render-helm-output-manifest-sfcdtest-01fcm0tny37w064h7g4tjnyxxc-v000", + "metadata": { + "account": "k8s-v2-account" + }, + "name": "render-helm-output-manifest-sfcdtest-01fcm0tny37w064h7g4tjnyxxc", + "location": "test", + "type": "kubernetes/configMap", + "version": "v000" + } + ], + "manifests": [ + { + "metadata": { + "name": "test" + }, + "apiVersion": "v1", + "kind": "Namespace" + }, + { + "metadata": { + "name": "render-helm-output-manifest-sfcdtest-01fcm0tny37w064h7g4tjnyxxc", + "namespace": "test", + "labels": { + "helm.sh/chart": "nginx-0.0.2", + "app.kubernetes.io/managed-by": "Tiller", + "app.kubernetes.io/name": "sfcdtest", + "app.kubernetes.io/instance": "render-helm-output-manifest" + } + }, + "apiVersion": "v1", + "data": { + "nginx.conf": "events", + "index.html": "

Hello

Test Service Version 2.0

" + }, + "kind": "ConfigMap" + }, + { + "metadata": { + "name": "render-helm-output-manifest-sfcdtest-01fcm0tny37w064h7g4tjnyxxc", + "namespace": "test", + "labels": { + "app.kubernetes.io/managed-by": "Tiller", + "app.kubernetes.io/name": "sfcdtest", + "app.kubernetes.io/instance": "render-helm-output-manifest" + } + }, + "apiVersion": "apps/v1", + "kind": "Deployment", + "spec": { + "template": { + "metadata": { + "annotations": { + "service": "sfcd-quality" + }, + "labels": { + "app.kubernetes.io/name": "sfcdtest", + "app.kubernetes.io/instance": "render-helm-output-manifest" + } + }, + "spec": { + "volumes": [ + { + "configMap": { + "name": "render-helm-output-manifest-sfcdtest-01fcm0tny37w064h7g4tjnyxxc" + }, + "name": "wwwdata-volume" + } + ], + "containers": [ + { + "image": "test-image-1", + "imagePullPolicy": "Always", + "name": "test", + "resources": { + "requests": { + "memory": "64Mi", + "cpu": "500m" + }, + "limits": { + "memory": "128Mi", + "cpu": "500m" + } + }, + "ports": [ + { + "protocol": "TCP", + "name": "https", + "containerPort": 443 + } + ], + "volumeMounts": [ + { + "mountPath": "/usr/share/nginx/html", + "name": "wwwdata-volume" + } + ] + } + ], + "securityContext": { + "runAsUser": 7447, + "fsGroup": 7447, + "runAsNonRoot": true + } + } + }, + "replicas": 3, + "selector": { + "matchLabels": { + "app.kubernetes.io/name": "sfcdtest", + "app.kubernetes.io/instance": "render-helm-output-manifest" + } + }, + "strategy": { + "type": "RollingUpdate" + } + } + } + ], + "messages": [], + "failedManifests": [], + "kato.tasks": [ + { + "resultObjects": [ + { + "createdArtifacts": [ + { + "customKind": false, + "reference": "render-helm-output-manifest-sfcdtest-01fcm0tny37w064h7g4tjnyxxc-v000", + "metadata": { + "account": "k8s-v2-account" + }, + "name": "render-helm-output-manifest-sfcdtest-01fcm0tny37w064h7g4tjnyxxc", + "location": "test", + "type": "kubernetes/configMap", + "version": "v000" + }, + { + "customKind": false, + "reference": "test", + "metadata": { + "account": "k8s-v2-account" + }, + "name": "test", + "location": "", + "type": "kubernetes/namespace", + "version": "" + }, + { + "customKind": false, + "reference": "render-helm-output-manifest-sfcdtest-01fcm0tny37w064h7g4tjnyxxc", + "metadata": { + "account": "k8s-v2-account" + }, + "name": "render-helm-output-manifest-sfcdtest-01fcm0tny37w064h7g4tjnyxxc", + "location": "test", + "type": "kubernetes/deployment", + "version": "" + } + ], + "manifests": [ + { + "metadata": { + "uid": "7ff137ec-97bc-4fb2-967b-eda32be6fafa", + "managedFields": [ + { + "apiVersion": "v1", + "fieldsV1": { + "f:data": { + "f:index.html": {}, + "f:nginx.conf": {}, + ".": {} + }, + "f:metadata": { + "f:annotations": { + "f:artifact.spinnaker.io/version": {}, + "f:artifact.spinnaker.io/name": {}, + "f:moniker.spinnaker.io/cluster": {}, + "f:kubectl.kubernetes.io/last-applied-configuration": {}, + "f:artifact.spinnaker.io/type": {}, + "f:moniker.spinnaker.io/sequence": {}, + "f:moniker.spinnaker.io/application": {}, + "f:artifact.spinnaker.io/location": {}, + ".": {} + }, + "f:labels": { + "f:helm.sh/chart": {}, + "f:app.kubernetes.io/instance": {}, + "f:app.kubernetes.io/name": {}, + "f:moniker.spinnaker.io/sequence": {}, + ".": {}, + "f:app.kubernetes.io/managed-by": {} + } + } + }, + "manager": "kubectl-client-side-apply", + "time": "2021-08-08T23:16:16Z", + "operation": "Update", + "fieldsType": "FieldsV1" + } + ], + "resourceVersion": "344741371", + "creationTimestamp": "2021-08-08T23:16:16Z", + "name": "render-helm-output-manifest-sfcdtest-01fcm0tny37w064h7g4tjnyxxc-v000", + "namespace": "test", + "annotations": { + "artifact.spinnaker.io/version": "v000", + "artifact.spinnaker.io/name": "render-helm-output-manifest-sfcdtest-01fcm0tny37w064h7g4tjnyxxc", + "artifact.spinnaker.io/location": "test", + "artifact.spinnaker.io/type": "kubernetes/configMap", + "kubectl.kubernetes.io/last-applied-configuration": "{\"apiVersion\":\"v1\",\"data\":{\"index.html\":\"\\u003ch1\\u003eHello\\u003c/h1\\u003e \\u003cp\\u003eTest Service Version 2.0\\u003c/p\\u003e\",\"nginx.conf\":\"events {\\n worker_connections 1024;\\n}\\nhttp {\\n server {\\n listen 80 default_server;\\n server_name _;\\n return 301 https://$host$request_uri;\\n }\\n server {\\n listen 443 ssl;\\n ssl_protocols TLSv1.2 TLSv1.3;\\n location / {\\n return 200 \\\"===============================\\\\n\\\\n This is your helm deploy 3.0! \\\\n\\\\n===============================\\\\n\\\";\\n }\\n }\\n}\\n\"},\"kind\":\"ConfigMap\",\"metadata\":{\"annotations\":{\"artifact.spinnaker.io/location\":\"test\",\"artifact.spinnaker.io/name\":\"render-helm-output-manifest-sfcdtest-01fcm0tny37w064h7g4tjnyxxc\",\"artifact.spinnaker.io/type\":\"kubernetes/configMap\",\"artifact.spinnaker.io/version\":\"v000\",\"moniker.spinnaker.io/application\":\"sfcdservicetestapp\",\"moniker.spinnaker.io/cluster\":\"configMap render-helm-output-manifest-sfcdtest-01fcm0tny37w064h7g4tjnyxxc\",\"moniker.spinnaker.io/sequence\":\"0\"},\"labels\":{\"app.kubernetes.io/instance\":\"render-helm-output-manifest\",\"app.kubernetes.io/managed-by\":\"spinnaker\",\"app.kubernetes.io/name\":\"sfcdtest\",\"helm.sh/chart\":\"nginx-0.0.2\",\"moniker.spinnaker.io/sequence\":\"0\"},\"name\":\"render-helm-output-manifest-sfcdtest-01fcm0tny37w064h7g4tjnyxxc-v000\",\"namespace\":\"test\"}}\n", + "moniker.spinnaker.io/cluster": "configMap render-helm-output-manifest-sfcdtest-01fcm0tny37w064h7g4tjnyxxc", + "moniker.spinnaker.io/sequence": "0", + "moniker.spinnaker.io/application": "sfcdservicetestapp" + }, + "labels": { + "helm.sh/chart": "nginx-0.0.2", + "app.kubernetes.io/managed-by": "spinnaker", + "app.kubernetes.io/name": "sfcdtest", + "app.kubernetes.io/instance": "render-helm-output-manifest", + "moniker.spinnaker.io/sequence": "0" + }, + "selfLink": "/api/v1/namespaces/test/configmaps/render-helm-output-manifest-sfcdtest-01fcm0tny37w064h7g4tjnyxxc-v000" + }, + "apiVersion": "v1", + "data": { + "nginx.conf": "events", + "index.html": "

Hello

Test Service Version 2.0

" + }, + "kind": "ConfigMap" + }, + { + "metadata": { + "uid": "ece1ef30-1bc4-11ea-9227-42010a3e0138", + "resourceVersion": "343481080", + "creationTimestamp": "2019-12-11T03:18:38Z", + "name": "test", + "annotations": { + "artifact.spinnaker.io/version": "", + "artifact.spinnaker.io/name": "test", + "artifact.spinnaker.io/location": "", + "artifact.spinnaker.io/type": "kubernetes/namespace", + "kubectl.kubernetes.io/last-applied-configuration": "{\"apiVersion\":\"v1\",\"kind\":\"Namespace\",\"metadata\":{\"annotations\":{\"artifact.spinnaker.io/location\":\"\",\"artifact.spinnaker.io/name\":\"test\",\"artifact.spinnaker.io/type\":\"kubernetes/namespace\",\"artifact.spinnaker.io/version\":\"\",\"moniker.spinnaker.io/application\":\"sfcdservicetestapp\",\"moniker.spinnaker.io/cluster\":\"namespace test\"},\"labels\":{\"app.kubernetes.io/managed-by\":\"spinnaker\",\"app.kubernetes.io/name\":\"sfcdservicetestapp\"},\"name\":\"test\"}}\n", + "moniker.spinnaker.io/cluster": "namespace test", + "moniker.spinnaker.io/application": "sfcdservicetestapp" + }, + "labels": { + "app.kubernetes.io/managed-by": "spinnaker", + "app.kubernetes.io/name": "sfcdservicetestapp" + }, + "selfLink": "/api/v1/namespaces/test" + }, + "apiVersion": "v1", + "kind": "Namespace", + "spec": { + "finalizers": [ + "kubernetes" + ] + }, + "status": { + "phase": "Active" + } + }, + { + "metadata": { + "generation": 1, + "uid": "914ec807-9ece-4029-8942-01631a192f57", + "managedFields": [ + { + "apiVersion": "apps/v1", + "fieldsV1": { + "f:metadata": { + "f:annotations": { + "f:artifact.spinnaker.io/version": {}, + "f:artifact.spinnaker.io/name": {}, + "f:moniker.spinnaker.io/cluster": {}, + "f:kubectl.kubernetes.io/last-applied-configuration": {}, + "f:artifact.spinnaker.io/type": {}, + "f:moniker.spinnaker.io/application": {}, + "f:artifact.spinnaker.io/location": {}, + ".": {} + }, + "f:labels": { + "f:app.kubernetes.io/instance": {}, + "f:app.kubernetes.io/name": {}, + ".": {}, + "f:app.kubernetes.io/managed-by": {} + } + }, + "f:spec": { + "f:template": { + "f:metadata": { + "f:annotations": { + "f:artifact.spinnaker.io/version": {}, + "f:artifact.spinnaker.io/name": {}, + "f:moniker.spinnaker.io/cluster": {}, + "f:artifact.spinnaker.io/type": {}, + "f:moniker.spinnaker.io/application": {}, + "f:artifact.spinnaker.io/location": {}, + "f:service": {}, + ".": {} + }, + "f:labels": { + "f:app.kubernetes.io/instance": {}, + "f:app.kubernetes.io/name": {}, + ".": {}, + "f:app.kubernetes.io/managed-by": {} + } + }, + "f:spec": { + "f:securityContext": { + "f:fsGroup": {}, + "f:runAsNonRoot": {}, + ".": {}, + "f:runAsUser": {} + }, + "f:restartPolicy": {}, + "f:schedulerName": {}, + "f:containers": { + "k:{\"name\":\"sfcdtest\"}": { + "f:imagePullPolicy": {}, + "f:volumeMounts": { + "k:{\"mountPath\":\"/usr/share/nginx/html\"}": { + "f:mountPath": {}, + "f:name": {}, + ".": {} + }, + ".": {} + }, + "f:terminationMessagePath": {}, + "f:terminationMessagePolicy": {}, + "f:image": {}, + "f:name": {}, + "f:ports": { + "k:{\"containerPort\":443,\"protocol\":\"TCP\"}": { + "f:protocol": {}, + "f:containerPort": {}, + "f:name": {}, + ".": {} + }, + ".": {} + }, + ".": {}, + "f:resources": { + "f:limits": { + "f:memory": {}, + "f:cpu": {}, + ".": {} + }, + "f:requests": { + "f:memory": {}, + "f:cpu": {}, + ".": {} + }, + ".": {} + } + } + }, + "f:dnsPolicy": {}, + "f:terminationGracePeriodSeconds": {}, + "f:volumes": { + "k:{\"name\":\"wwwdata-volume\"}": { + "f:configMap": { + "f:defaultMode": {}, + "f:name": {}, + ".": {} + }, + "f:name": {}, + ".": {} + }, + ".": {} + } + } + }, + "f:strategy": { + "f:rollingUpdate": { + "f:maxSurge": {}, + "f:maxUnavailable": {}, + ".": {} + }, + "f:type": {} + }, + "f:selector": { + "f:matchLabels": { + "f:app.kubernetes.io/instance": {}, + "f:app.kubernetes.io/name": {}, + ".": {} + } + }, + "f:progressDeadlineSeconds": {}, + "f:replicas": {}, + "f:revisionHistoryLimit": {} + } + }, + "manager": "kubectl-client-side-apply", + "time": "2021-08-08T23:16:17Z", + "operation": "Update", + "fieldsType": "FieldsV1" + } + ], + "resourceVersion": "344741384", + "creationTimestamp": "2021-08-08T23:16:17Z", + "name": "render-helm-output-manifest-sfcdtest-01fcm0tny37w064h7g4tjnyxxc", + "namespace": "test", + "annotations": { + "artifact.spinnaker.io/version": "", + "artifact.spinnaker.io/name": "render-helm-output-manifest-sfcdtest-01fcm0tny37w064h7g4tjnyxxc", + "artifact.spinnaker.io/location": "test", + "artifact.spinnaker.io/type": "kubernetes/deployment", + "kubectl.kubernetes.io/last-applied-configuration": "{\"apiVersion\":\"apps/v1\",\"kind\":\"Deployment\",\"metadata\":{\"annotations\":{\"artifact.spinnaker.io/location\":\"test\",\"artifact.spinnaker.io/name\":\"render-helm-output-manifest-sfcdtest-01fcm0tny37w064h7g4tjnyxxc\",\"artifact.spinnaker.io/type\":\"kubernetes/deployment\",\"artifact.spinnaker.io/version\":\"\",\"moniker.spinnaker.io/application\":\"sfcdservicetestapp\",\"moniker.spinnaker.io/cluster\":\"deployment render-helm-output-manifest-sfcdtest-01fcm0tny37w064h7g4tjnyxxc\"},\"labels\":{\"app.kubernetes.io/instance\":\"render-helm-output-manifest\",\"app.kubernetes.io/managed-by\":\"spinnaker\",\"app.kubernetes.io/name\":\"sfcdtest\"},\"name\":\"render-helm-output-manifest-sfcdtest-01fcm0tny37w064h7g4tjnyxxc\",\"namespace\":\"test\"},\"spec\":{\"replicas\":3,\"selector\":{\"matchLabels\":{\"app.kubernetes.io/instance\":\"render-helm-output-manifest\",\"app.kubernetes.io/name\":\"sfcdtest\"}},\"strategy\":{\"type\":\"RollingUpdate\"},\"template\":{\"metadata\":{\"annotations\":{\"artifact.spinnaker.io/location\":\"test\",\"artifact.spinnaker.io/name\":\"render-helm-output-manifest-sfcdtest-01fcm0tny37w064h7g4tjnyxxc\",\"artifact.spinnaker.io/type\":\"kubernetes/deployment\",\"artifact.spinnaker.io/version\":\"\",\"moniker.spinnaker.io/application\":\"sfcdservicetestapp\",\"moniker.spinnaker.io/cluster\":\"deployment render-helm-output-manifest-sfcdtest-01fcm0tny37w064h7g4tjnyxxc\",\"service\":\"sfcd-quality\"},\"labels\":{\"app.kubernetes.io/instance\":\"render-helm-output-manifest\",\"app.kubernetes.io/managed-by\":\"spinnaker\",\"app.kubernetes.io/name\":\"sfcdtest\"}},\"spec\":{\"containers\":[{\"image\":\"gcr.io/gsf-mgmt-devmvp-spinnaker/sfci/3pp/3pp/docker.io/nginxinc/nginx-unprivileged:1.18\",\"imagePullPolicy\":\"Always\",\"name\":\"sfcdtest\",\"ports\":[{\"containerPort\":443,\"name\":\"https\",\"protocol\":\"TCP\"}],\"resources\":{\"limits\":{\"cpu\":\"500m\",\"memory\":\"128Mi\"},\"requests\":{\"cpu\":\"500m\",\"memory\":\"64Mi\"}},\"volumeMounts\":[{\"mountPath\":\"/usr/share/nginx/html\",\"name\":\"wwwdata-volume\"}]}],\"securityContext\":{\"fsGroup\":7447,\"runAsNonRoot\":true,\"runAsUser\":7447},\"volumes\":[{\"configMap\":{\"name\":\"render-helm-output-manifest-sfcdtest-01fcm0tny37w064h7g4tjnyxxc-v000\"},\"name\":\"wwwdata-volume\"}]}}}}\n", + "moniker.spinnaker.io/cluster": "deployment render-helm-output-manifest-sfcdtest-01fcm0tny37w064h7g4tjnyxxc", + "moniker.spinnaker.io/application": "sfcdservicetestapp" + }, + "labels": { + "app.kubernetes.io/managed-by": "spinnaker", + "app.kubernetes.io/name": "sfcdtest", + "app.kubernetes.io/instance": "render-helm-output-manifest" + }, + "selfLink": "/apis/apps/v1/namespaces/test/deployments/render-helm-output-manifest-sfcdtest-01fcm0tny37w064h7g4tjnyxxc" + }, + "apiVersion": "apps/v1", + "kind": "Deployment", + "spec": { + "template": { + "metadata": { + "annotations": { + "artifact.spinnaker.io/version": "", + "artifact.spinnaker.io/name": "render-helm-output-manifest-sfcdtest-01fcm0tny37w064h7g4tjnyxxc", + "service": "sfcd-quality", + "artifact.spinnaker.io/location": "test", + "artifact.spinnaker.io/type": "kubernetes/deployment", + "moniker.spinnaker.io/cluster": "deployment render-helm-output-manifest-sfcdtest-01fcm0tny37w064h7g4tjnyxxc", + "moniker.spinnaker.io/application": "sfcdservicetestapp" + }, + "labels": { + "app.kubernetes.io/managed-by": "spinnaker", + "app.kubernetes.io/name": "sfcdtest", + "app.kubernetes.io/instance": "render-helm-output-manifest" + } + }, + "spec": { + "dnsPolicy": "ClusterFirst", + "terminationGracePeriodSeconds": 30, + "volumes": [ + { + "configMap": { + "defaultMode": 420, + "name": "render-helm-output-manifest-sfcdtest-01fcm0tny37w064h7g4tjnyxxc-v000" + }, + "name": "wwwdata-volume" + } + ], + "containers": [ + { + "image": "test-image:1", + "imagePullPolicy": "Always", + "terminationMessagePolicy": "File", + "terminationMessagePath": "/dev/termination-log", + "name": "sfcdtest", + "resources": { + "requests": { + "memory": "64Mi", + "cpu": "500m" + }, + "limits": { + "memory": "128Mi", + "cpu": "500m" + } + }, + "ports": [ + { + "protocol": "TCP", + "name": "https", + "containerPort": 443 + } + ], + "volumeMounts": [ + { + "mountPath": "/usr/share/nginx/html", + "name": "wwwdata-volume" + } + ] + } + ], + "securityContext": { + "runAsUser": 7447, + "fsGroup": 7447, + "runAsNonRoot": true + }, + "restartPolicy": "Always", + "schedulerName": "default-scheduler" + } + }, + "replicas": 3, + "revisionHistoryLimit": 10, + "selector": { + "matchLabels": { + "app.kubernetes.io/name": "sfcdtest", + "app.kubernetes.io/instance": "render-helm-output-manifest" + } + }, + "strategy": { + "type": "RollingUpdate", + "rollingUpdate": { + "maxSurge": "25%", + "maxUnavailable": "25%" + } + }, + "progressDeadlineSeconds": 600 + }, + "status": {} + } + ], + "manifestNamesByNamespace": { + "": [ + "namespace test" + ], + "test": [ + "configMap render-helm-output-manifest-sfcdtest-01fcm0tny37w064h7g4tjnyxxc-v000", + "deployment render-helm-output-manifest-sfcdtest-01fcm0tny37w064h7g4tjnyxxc" + ] + }, + "boundArtifacts": [ + { + "customKind": false, + "reference": "render-helm-output-manifest-sfcdtest-01fcm0tny37w064h7g4tjnyxxc-v000", + "metadata": { + "account": "k8s-v2-account" + }, + "name": "render-helm-output-manifest-sfcdtest-01fcm0tny37w064h7g4tjnyxxc", + "location": "test", + "type": "kubernetes/configMap", + "version": "v000" + } + ] + } + ], + "id": "01FCM0TR15FKVECTE4TGFGCEJV", + "history": [ + { + "phase": "ORCHESTRATION", + "status": "Initializing Orchestration Task" + }, + { + "phase": "ORCHESTRATION", + "status": "Processing op: KubernetesDeployManifestOperation" + }, + { + "phase": "DEPLOY_KUBERNETES_MANIFEST", + "status": "Beginning deployment of manifest..." + }, + { + "phase": "DEPLOY_KUBERNETES_MANIFEST", + "status": "Sorting manifests by priority..." + }, + { + "phase": "DEPLOY_KUBERNETES_MANIFEST", + "status": "Finding deployer for configMap..." + }, + { + "phase": "DEPLOY_KUBERNETES_MANIFEST", + "status": "Finding deployer for namespace..." + }, + { + "phase": "DEPLOY_KUBERNETES_MANIFEST", + "status": "Finding deployer for deployment..." + }, + { + "phase": "DEPLOY_KUBERNETES_MANIFEST", + "status": "Finding deployer for configMap..." + }, + { + "phase": "DEPLOY_KUBERNETES_MANIFEST", + "status": "Deploy order is: namespace test, configMap render-helm-output-manifest-sfcdtest-01fcm0tny37w064h7g4tjnyxxc, deployment render-helm-output-manifest-sfcdtest-01fcm0tny37w064h7g4tjnyxxc" + }, + { + "phase": "DEPLOY_KUBERNETES_MANIFEST", + "status": "Binding artifacts in namespace test..." + }, + { + "phase": "DEPLOY_KUBERNETES_MANIFEST", + "status": "Finding deployer for namespace..." + }, + { + "phase": "DEPLOY_KUBERNETES_MANIFEST", + "status": "Bound artifacts: []..." + }, + { + "phase": "DEPLOY_KUBERNETES_MANIFEST", + "status": "Finding deployer for namespace..." + }, + { + "phase": "DEPLOY_KUBERNETES_MANIFEST", + "status": "Annotating manifest namespace test with artifact, relationships & moniker..." + }, + { + "phase": "DEPLOY_KUBERNETES_MANIFEST", + "status": "Binding artifacts in configMap render-helm-output-manifest-sfcdtest-01fcm0tny37w064h7g4tjnyxxc..." + }, + { + "phase": "DEPLOY_KUBERNETES_MANIFEST", + "status": "Finding deployer for configMap..." + }, + { + "phase": "DEPLOY_KUBERNETES_MANIFEST", + "status": "Bound artifacts: []..." + }, + { + "phase": "DEPLOY_KUBERNETES_MANIFEST", + "status": "Finding deployer for configMap..." + }, + { + "phase": "DEPLOY_KUBERNETES_MANIFEST", + "status": "Annotating manifest configMap render-helm-output-manifest-sfcdtest-01fcm0tny37w064h7g4tjnyxxc with artifact, relationships & moniker..." + }, + { + "phase": "DEPLOY_KUBERNETES_MANIFEST", + "status": "Binding artifacts in deployment render-helm-output-manifest-sfcdtest-01fcm0tny37w064h7g4tjnyxxc..." + }, + { + "phase": "DEPLOY_KUBERNETES_MANIFEST", + "status": "Finding deployer for deployment..." + }, + { + "phase": "DEPLOY_KUBERNETES_MANIFEST", + "status": "Bound artifacts: [Artifact(type=kubernetes/configMap, customKind=false, name=render-helm-output-manifest-sfcdtest-01fcm0tny37w064h7g4tjnyxxc, version=v000, location=test, reference=render-helm-output-manifest-sfcdtest-01fcm0tny37w064h7g4tjnyxxc-v000, metadata={account=k8s-v2-account}, artifactAccount=null, provenance=null, uuid=null)]..." + }, + { + "phase": "DEPLOY_KUBERNETES_MANIFEST", + "status": "Finding deployer for deployment..." + }, + { + "phase": "DEPLOY_KUBERNETES_MANIFEST", + "status": "Annotating manifest deployment render-helm-output-manifest-sfcdtest-01fcm0tny37w064h7g4tjnyxxc with artifact, relationships & moniker..." + }, + { + "phase": "DEPLOY_KUBERNETES_MANIFEST", + "status": "Checking if all requested artifacts were bound..." + }, + { + "phase": "DEPLOY_KUBERNETES_MANIFEST", + "status": "Finding deployer for namespace..." + }, + { + "phase": "DEPLOY_KUBERNETES_MANIFEST", + "status": "Submitting manifest namespace test to kubernetes master..." + }, + { + "phase": "DEPLOY_KUBERNETES_MANIFEST", + "status": "Finding deployer for configMap..." + }, + { + "phase": "DEPLOY_KUBERNETES_MANIFEST", + "status": "Submitting manifest configMap render-helm-output-manifest-sfcdtest-01fcm0tny37w064h7g4tjnyxxc-v000 to kubernetes master..." + }, + { + "phase": "DEPLOY_KUBERNETES_MANIFEST", + "status": "Finding deployer for deployment..." + }, + { + "phase": "DEPLOY_KUBERNETES_MANIFEST", + "status": "Submitting manifest deployment render-helm-output-manifest-sfcdtest-01fcm0tny37w064h7g4tjnyxxc to kubernetes master..." + }, + { + "phase": "DEPLOY_KUBERNETES_MANIFEST", + "status": "Deploy manifest task completed successfully." + }, + { + "phase": "ORCHESTRATION", + "status": "Orchestration completed." + }, + { + "phase": "ORCHESTRATION", + "status": "Orchestration completed." + } + ], + "status": { + "retryable": false, + "completed": true, + "failed": false + } + }, + { + "resultObjects": [ + { + "createdArtifacts": [], + "manifestNamesByNamespace": {}, + "boundArtifacts": [] + } + ], + "id": "01FCM0V6ECYWSVCPC0V3QWR1QW", + "history": [ + { + "phase": "ORCHESTRATION", + "status": "Initializing Orchestration Task" + }, + { + "phase": "ORCHESTRATION", + "status": "Processing op: KubernetesCleanupArtifactsOperation" + }, + { + "phase": "ORCHESTRATION", + "status": "Orchestration completed." + }, + { + "phase": "ORCHESTRATION", + "status": "Orchestration completed." + } + ], + "status": { + "retryable": false, + "completed": true, + "failed": false + } + } + ], + "outputs.manifests": [ + { + "metadata": { + "uid": "7ff137ec-97bc-4fb2-967b-eda32be6fafa", + "managedFields": [ + { + "apiVersion": "v1", + "fieldsV1": { + "f:data": { + "f:index.html": {}, + "f:nginx.conf": {}, + ".": {} + }, + "f:metadata": { + "f:annotations": { + "f:artifact.spinnaker.io/version": {}, + "f:artifact.spinnaker.io/name": {}, + "f:moniker.spinnaker.io/cluster": {}, + "f:kubectl.kubernetes.io/last-applied-configuration": {}, + "f:artifact.spinnaker.io/type": {}, + "f:moniker.spinnaker.io/sequence": {}, + "f:moniker.spinnaker.io/application": {}, + "f:artifact.spinnaker.io/location": {}, + ".": {} + }, + "f:labels": { + "f:helm.sh/chart": {}, + "f:app.kubernetes.io/instance": {}, + "f:app.kubernetes.io/name": {}, + "f:moniker.spinnaker.io/sequence": {}, + ".": {}, + "f:app.kubernetes.io/managed-by": {} + } + } + }, + "manager": "kubectl-client-side-apply", + "time": "2021-08-08T23:16:16Z", + "operation": "Update", + "fieldsType": "FieldsV1" + } + ], + "resourceVersion": "344741371", + "creationTimestamp": "2021-08-08T23:16:16Z", + "name": "render-helm-output-manifest-sfcdtest-01fcm0tny37w064h7g4tjnyxxc-v000", + "namespace": "test", + "annotations": { + "artifact.spinnaker.io/version": "v000", + "artifact.spinnaker.io/name": "render-helm-output-manifest-sfcdtest-01fcm0tny37w064h7g4tjnyxxc", + "artifact.spinnaker.io/location": "test", + "artifact.spinnaker.io/type": "kubernetes/configMap", + "kubectl.kubernetes.io/last-applied-configuration": "{\"apiVersion\":\"v1\",\"data\":{\"index.html\":\"\\u003ch1\\u003eHello\\u003c/h1\\u003e \\u003cp\\u003eTest Service Version 2.0\\u003c/p\\u003e\",\"nginx.conf\":\"events {\\n worker_connections 1024;\\n}\\nhttp {\\n server {\\n listen 80 default_server;\\n server_name _;\\n return 301 https://$host$request_uri;\\n }\\n server {\\n listen 443 ssl;\\n ssl_protocols TLSv1.2 TLSv1.3;\\n location / {\\n return 200 \\\"===============================\\\\n\\\\n This is your helm deploy 3.0! \\\\n\\\\n===============================\\\\n\\\";\\n }\\n }\\n}\\n\"},\"kind\":\"ConfigMap\",\"metadata\":{\"annotations\":{\"artifact.spinnaker.io/location\":\"test\",\"artifact.spinnaker.io/name\":\"render-helm-output-manifest-sfcdtest-01fcm0tny37w064h7g4tjnyxxc\",\"artifact.spinnaker.io/type\":\"kubernetes/configMap\",\"artifact.spinnaker.io/version\":\"v000\",\"moniker.spinnaker.io/application\":\"sfcdservicetestapp\",\"moniker.spinnaker.io/cluster\":\"configMap render-helm-output-manifest-sfcdtest-01fcm0tny37w064h7g4tjnyxxc\",\"moniker.spinnaker.io/sequence\":\"0\"},\"labels\":{\"app.kubernetes.io/instance\":\"render-helm-output-manifest\",\"app.kubernetes.io/managed-by\":\"spinnaker\",\"app.kubernetes.io/name\":\"sfcdtest\",\"helm.sh/chart\":\"nginx-0.0.2\",\"moniker.spinnaker.io/sequence\":\"0\"},\"name\":\"render-helm-output-manifest-sfcdtest-01fcm0tny37w064h7g4tjnyxxc-v000\",\"namespace\":\"test\"}}\n", + "moniker.spinnaker.io/cluster": "configMap render-helm-output-manifest-sfcdtest-01fcm0tny37w064h7g4tjnyxxc", + "moniker.spinnaker.io/sequence": "0", + "moniker.spinnaker.io/application": "sfcdservicetestapp" + }, + "labels": { + "helm.sh/chart": "nginx-0.0.2", + "app.kubernetes.io/managed-by": "spinnaker", + "app.kubernetes.io/name": "sfcdtest", + "app.kubernetes.io/instance": "render-helm-output-manifest", + "moniker.spinnaker.io/sequence": "0" + }, + "selfLink": "/api/v1/namespaces/test/configmaps/render-helm-output-manifest-sfcdtest-01fcm0tny37w064h7g4tjnyxxc-v000" + }, + "apiVersion": "v1", + "data": { + "nginx.conf": "events", + "index.html": "

Hello

Test Service Version 2.0

" + }, + "kind": "ConfigMap" + }, + { + "metadata": { + "uid": "ece1ef30-1bc4-11ea-9227-42010a3e0138", + "resourceVersion": "343481080", + "creationTimestamp": "2019-12-11T03:18:38Z", + "name": "test", + "annotations": { + "artifact.spinnaker.io/version": "", + "artifact.spinnaker.io/name": "test", + "artifact.spinnaker.io/location": "", + "artifact.spinnaker.io/type": "kubernetes/namespace", + "kubectl.kubernetes.io/last-applied-configuration": "{\"apiVersion\":\"v1\",\"kind\":\"Namespace\",\"metadata\":{\"annotations\":{\"artifact.spinnaker.io/location\":\"\",\"artifact.spinnaker.io/name\":\"test\",\"artifact.spinnaker.io/type\":\"kubernetes/namespace\",\"artifact.spinnaker.io/version\":\"\",\"moniker.spinnaker.io/application\":\"sfcdservicetestapp\",\"moniker.spinnaker.io/cluster\":\"namespace test\"},\"labels\":{\"app.kubernetes.io/managed-by\":\"spinnaker\",\"app.kubernetes.io/name\":\"sfcdservicetestapp\"},\"name\":\"test\"}}\n", + "moniker.spinnaker.io/cluster": "namespace test", + "moniker.spinnaker.io/application": "sfcdservicetestapp" + }, + "labels": { + "app.kubernetes.io/managed-by": "spinnaker", + "app.kubernetes.io/name": "sfcdservicetestapp" + }, + "selfLink": "/api/v1/namespaces/test" + }, + "apiVersion": "v1", + "kind": "Namespace", + "spec": { + "finalizers": [ + "kubernetes" + ] + }, + "status": { + "phase": "Active" + } + }, + { + "metadata": { + "generation": 1, + "uid": "914ec807-9ece-4029-8942-01631a192f57", + "managedFields": [ + { + "apiVersion": "apps/v1", + "fieldsV1": { + "f:metadata": { + "f:annotations": { + "f:artifact.spinnaker.io/version": {}, + "f:artifact.spinnaker.io/name": {}, + "f:moniker.spinnaker.io/cluster": {}, + "f:kubectl.kubernetes.io/last-applied-configuration": {}, + "f:artifact.spinnaker.io/type": {}, + "f:moniker.spinnaker.io/application": {}, + "f:artifact.spinnaker.io/location": {}, + ".": {} + }, + "f:labels": { + "f:app.kubernetes.io/instance": {}, + "f:app.kubernetes.io/name": {}, + ".": {}, + "f:app.kubernetes.io/managed-by": {} + } + }, + "f:spec": { + "f:template": { + "f:metadata": { + "f:annotations": { + "f:artifact.spinnaker.io/version": {}, + "f:artifact.spinnaker.io/name": {}, + "f:moniker.spinnaker.io/cluster": {}, + "f:artifact.spinnaker.io/type": {}, + "f:moniker.spinnaker.io/application": {}, + "f:artifact.spinnaker.io/location": {}, + "f:service": {}, + ".": {} + }, + "f:labels": { + "f:app.kubernetes.io/instance": {}, + "f:app.kubernetes.io/name": {}, + ".": {}, + "f:app.kubernetes.io/managed-by": {} + } + }, + "f:spec": { + "f:securityContext": { + "f:fsGroup": {}, + "f:runAsNonRoot": {}, + ".": {}, + "f:runAsUser": {} + }, + "f:restartPolicy": {}, + "f:schedulerName": {}, + "f:containers": { + "k:{\"name\":\"sfcdtest\"}": { + "f:imagePullPolicy": {}, + "f:volumeMounts": { + "k:{\"mountPath\":\"/usr/share/nginx/html\"}": { + "f:mountPath": {}, + "f:name": {}, + ".": {} + }, + ".": {} + }, + "f:terminationMessagePath": {}, + "f:terminationMessagePolicy": {}, + "f:image": {}, + "f:name": {}, + "f:ports": { + "k:{\"containerPort\":443,\"protocol\":\"TCP\"}": { + "f:protocol": {}, + "f:containerPort": {}, + "f:name": {}, + ".": {} + }, + ".": {} + }, + ".": {}, + "f:resources": { + "f:limits": { + "f:memory": {}, + "f:cpu": {}, + ".": {} + }, + "f:requests": { + "f:memory": {}, + "f:cpu": {}, + ".": {} + }, + ".": {} + } + } + }, + "f:dnsPolicy": {}, + "f:terminationGracePeriodSeconds": {}, + "f:volumes": { + "k:{\"name\":\"wwwdata-volume\"}": { + "f:configMap": { + "f:defaultMode": {}, + "f:name": {}, + ".": {} + }, + "f:name": {}, + ".": {} + }, + ".": {} + } + } + }, + "f:strategy": { + "f:rollingUpdate": { + "f:maxSurge": {}, + "f:maxUnavailable": {}, + ".": {} + }, + "f:type": {} + }, + "f:selector": { + "f:matchLabels": { + "f:app.kubernetes.io/instance": {}, + "f:app.kubernetes.io/name": {}, + ".": {} + } + }, + "f:progressDeadlineSeconds": {}, + "f:replicas": {}, + "f:revisionHistoryLimit": {} + } + }, + "manager": "kubectl-client-side-apply", + "time": "2021-08-08T23:16:17Z", + "operation": "Update", + "fieldsType": "FieldsV1" + } + ], + "resourceVersion": "344741384", + "creationTimestamp": "2021-08-08T23:16:17Z", + "name": "render-helm-output-manifest-sfcdtest-01fcm0tny37w064h7g4tjnyxxc", + "namespace": "test", + "annotations": { + "artifact.spinnaker.io/version": "", + "artifact.spinnaker.io/name": "render-helm-output-manifest-sfcdtest-01fcm0tny37w064h7g4tjnyxxc", + "artifact.spinnaker.io/location": "test", + "artifact.spinnaker.io/type": "kubernetes/deployment", + "kubectl.kubernetes.io/last-applied-configuration": "{\"apiVersion\":\"apps/v1\",\"kind\":\"Deployment\",\"metadata\":{\"annotations\":{\"artifact.spinnaker.io/location\":\"test\",\"artifact.spinnaker.io/name\":\"render-helm-output-manifest-sfcdtest-01fcm0tny37w064h7g4tjnyxxc\",\"artifact.spinnaker.io/type\":\"kubernetes/deployment\",\"artifact.spinnaker.io/version\":\"\",\"moniker.spinnaker.io/application\":\"sfcdservicetestapp\",\"moniker.spinnaker.io/cluster\":\"deployment render-helm-output-manifest-sfcdtest-01fcm0tny37w064h7g4tjnyxxc\"},\"labels\":{\"app.kubernetes.io/instance\":\"render-helm-output-manifest\",\"app.kubernetes.io/managed-by\":\"spinnaker\",\"app.kubernetes.io/name\":\"sfcdtest\"},\"name\":\"render-helm-output-manifest-sfcdtest-01fcm0tny37w064h7g4tjnyxxc\",\"namespace\":\"test\"},\"spec\":{\"replicas\":3,\"selector\":{\"matchLabels\":{\"app.kubernetes.io/instance\":\"render-helm-output-manifest\",\"app.kubernetes.io/name\":\"sfcdtest\"}},\"strategy\":{\"type\":\"RollingUpdate\"},\"template\":{\"metadata\":{\"annotations\":{\"artifact.spinnaker.io/location\":\"test\",\"artifact.spinnaker.io/name\":\"render-helm-output-manifest-sfcdtest-01fcm0tny37w064h7g4tjnyxxc\",\"artifact.spinnaker.io/type\":\"kubernetes/deployment\",\"artifact.spinnaker.io/version\":\"\",\"moniker.spinnaker.io/application\":\"sfcdservicetestapp\",\"moniker.spinnaker.io/cluster\":\"deployment render-helm-output-manifest-sfcdtest-01fcm0tny37w064h7g4tjnyxxc\",\"service\":\"sfcd-quality\"},\"labels\":{\"app.kubernetes.io/instance\":\"render-helm-output-manifest\",\"app.kubernetes.io/managed-by\":\"spinnaker\",\"app.kubernetes.io/name\":\"sfcdtest\"}},\"spec\":{\"containers\":[{\"image\":\"gcr.io/gsf-mgmt-devmvp-spinnaker/sfci/3pp/3pp/docker.io/nginxinc/nginx-unprivileged:1.18\",\"imagePullPolicy\":\"Always\",\"name\":\"sfcdtest\",\"ports\":[{\"containerPort\":443,\"name\":\"https\",\"protocol\":\"TCP\"}],\"resources\":{\"limits\":{\"cpu\":\"500m\",\"memory\":\"128Mi\"},\"requests\":{\"cpu\":\"500m\",\"memory\":\"64Mi\"}},\"volumeMounts\":[{\"mountPath\":\"/usr/share/nginx/html\",\"name\":\"wwwdata-volume\"}]}],\"securityContext\":{\"fsGroup\":7447,\"runAsNonRoot\":true,\"runAsUser\":7447},\"volumes\":[{\"configMap\":{\"name\":\"render-helm-output-manifest-sfcdtest-01fcm0tny37w064h7g4tjnyxxc-v000\"},\"name\":\"wwwdata-volume\"}]}}}}\n", + "moniker.spinnaker.io/cluster": "deployment render-helm-output-manifest-sfcdtest-01fcm0tny37w064h7g4tjnyxxc", + "moniker.spinnaker.io/application": "sfcdservicetestapp" + }, + "labels": { + "app.kubernetes.io/managed-by": "spinnaker", + "app.kubernetes.io/name": "sfcdtest", + "app.kubernetes.io/instance": "render-helm-output-manifest" + }, + "selfLink": "/apis/apps/v1/namespaces/test/deployments/render-helm-output-manifest-sfcdtest-01fcm0tny37w064h7g4tjnyxxc" + }, + "apiVersion": "apps/v1", + "kind": "Deployment", + "spec": { + "template": { + "metadata": { + "annotations": { + "artifact.spinnaker.io/version": "", + "artifact.spinnaker.io/name": "render-helm-output-manifest-sfcdtest-01fcm0tny37w064h7g4tjnyxxc", + "service": "sfcd-quality", + "artifact.spinnaker.io/location": "test", + "artifact.spinnaker.io/type": "kubernetes/deployment", + "moniker.spinnaker.io/cluster": "deployment render-helm-output-manifest-sfcdtest-01fcm0tny37w064h7g4tjnyxxc", + "moniker.spinnaker.io/application": "sfcdservicetestapp" + }, + "labels": { + "app.kubernetes.io/managed-by": "spinnaker", + "app.kubernetes.io/name": "sfcdtest", + "app.kubernetes.io/instance": "render-helm-output-manifest" + } + }, + "spec": { + "dnsPolicy": "ClusterFirst", + "terminationGracePeriodSeconds": 30, + "volumes": [ + { + "configMap": { + "defaultMode": 420, + "name": "render-helm-output-manifest-sfcdtest-01fcm0tny37w064h7g4tjnyxxc-v000" + }, + "name": "wwwdata-volume" + } + ], + "containers": [ + { + "image": "test-image:1", + "imagePullPolicy": "Always", + "terminationMessagePolicy": "File", + "terminationMessagePath": "/dev/termination-log", + "name": "sfcdtest", + "resources": { + "requests": { + "memory": "64Mi", + "cpu": "500m" + }, + "limits": { + "memory": "128Mi", + "cpu": "500m" + } + }, + "ports": [ + { + "protocol": "TCP", + "name": "https", + "containerPort": 443 + } + ], + "volumeMounts": [ + { + "mountPath": "/usr/share/nginx/html", + "name": "wwwdata-volume" + } + ] + } + ], + "securityContext": { + "runAsUser": 7447, + "fsGroup": 7447, + "runAsNonRoot": true + }, + "restartPolicy": "Always", + "schedulerName": "default-scheduler" + } + }, + "replicas": 3, + "revisionHistoryLimit": 10, + "selector": { + "matchLabels": { + "app.kubernetes.io/name": "sfcdtest", + "app.kubernetes.io/instance": "render-helm-output-manifest" + } + }, + "strategy": { + "type": "RollingUpdate", + "rollingUpdate": { + "maxSurge": "25%", + "maxUnavailable": "25%" + } + }, + "progressDeadlineSeconds": 600 + }, + "status": {} + } + ], + "kato.task.notFoundRetryCount": 0, + "account": "k8s-v2-account", + "skipExpressionEvaluation": false, + "kato.task.lastStatus": "SUCCEEDED" +} diff --git a/orca-igor/src/test/groovy/com/netflix/spinnaker/orca/igor/tasks/GetBuildPropertiesTaskSpec.groovy b/orca-igor/src/test/groovy/com/netflix/spinnaker/orca/igor/tasks/GetBuildPropertiesTaskSpec.groovy index c5ca50c3e2..f08971f289 100644 --- a/orca-igor/src/test/groovy/com/netflix/spinnaker/orca/igor/tasks/GetBuildPropertiesTaskSpec.groovy +++ b/orca-igor/src/test/groovy/com/netflix/spinnaker/orca/igor/tasks/GetBuildPropertiesTaskSpec.groovy @@ -18,6 +18,7 @@ package com.netflix.spinnaker.orca.igor.tasks import com.fasterxml.jackson.databind.ObjectMapper import com.netflix.spinnaker.kork.artifacts.model.Artifact +import com.netflix.spinnaker.orca.config.TaskConfigurationProperties import com.netflix.spinnaker.kork.exceptions.ConfigurationException import com.netflix.spinnaker.kork.retrofit.exceptions.SpinnakerHttpException import com.netflix.spinnaker.orca.api.pipeline.models.ExecutionStatus @@ -51,6 +52,10 @@ class GetBuildPropertiesTaskSpec extends Specification { @Shared def execution = Stub(PipelineExecutionImpl) + @Shared + private TaskConfigurationProperties configProperties = new TaskConfigurationProperties() + + def "retrieves values from a property file if specified"() { given: def stage = new StageExecutionImpl(execution, "jenkins", [master: MASTER, job: JOB, buildNumber: 4, propertyFile: PROPERTY_FILE]) @@ -89,8 +94,6 @@ class GetBuildPropertiesTaskSpec extends Specification { buildNumber : BUILD_NUMBER, propertyFile : PROPERTY_FILE, expectedArtifacts: [[matchArtifact: [type: "docker/image"]],]]) - def bindTask = new BindProducedArtifactsTask() - and: 1 * buildService.getPropertyFile(BUILD_NUMBER, PROPERTY_FILE, MASTER, JOB) >> [val1: "one", artifacts: [ @@ -98,8 +101,8 @@ class GetBuildPropertiesTaskSpec extends Specification { reference: "gcr.io/project/my-image@sha256:28f82eba", name: "gcr.io/project/my-image", version: "sha256:28f82eba"],]] - bindTask.artifactUtils = artifactUtils - bindTask.objectMapper = new ObjectMapper() + + def bindTask = new BindProducedArtifactsTask(artifactUtils, new ObjectMapper(), configProperties) when: def jenkinsResult = task.execute(stage)