From f28f2955c601246429c5e1eb2d4bd6d8fca84225 Mon Sep 17 00:00:00 2001 From: Edgar Garcia <63310723+edgarulg@users.noreply.github.com> Date: Tue, 16 Jul 2024 14:38:37 -0600 Subject: [PATCH] test(sql): Add extra test for pipelineRef feature flag (#4760) * test(sql): add extra tests in ExecutionLauncher for pipelineRef * style(sql): include comments to explain reason behind custom deserializer * test(Executionlauncher): rename tests without implementation details. --------- Co-authored-by: Jason --- orca-core/orca-core.gradle | 1 + .../orca/pipeline/ExecutionLauncherTest.java | 119 ++++++++++++++++++ .../pipeline-with-pipeline-trigger.json | 30 +++++ .../pipeline-with-pipelineRef-trigger.json | 18 +++ .../PipelineRefTriggerDeserializerSupplier.kt | 7 ++ 5 files changed, 175 insertions(+) create mode 100644 orca-core/src/test/resources/com/netflix/spinnaker/orca/pipeline/ad-hoc/pipeline-with-pipeline-trigger.json create mode 100644 orca-core/src/test/resources/com/netflix/spinnaker/orca/pipeline/ad-hoc/pipeline-with-pipelineRef-trigger.json diff --git a/orca-core/orca-core.gradle b/orca-core/orca-core.gradle index 1608c5e2ac..5ccdf97546 100644 --- a/orca-core/orca-core.gradle +++ b/orca-core/orca-core.gradle @@ -62,6 +62,7 @@ dependencies { testImplementation(project(":orca-test")) testImplementation(project(":orca-test-groovy")) + testImplementation(project(":orca-sql")) testImplementation("com.github.tomakehurst:wiremock-jre8-standalone") testImplementation("org.junit.jupiter:junit-jupiter-api") testImplementation("org.assertj:assertj-core") diff --git a/orca-core/src/test/java/com/netflix/spinnaker/orca/pipeline/ExecutionLauncherTest.java b/orca-core/src/test/java/com/netflix/spinnaker/orca/pipeline/ExecutionLauncherTest.java index 30c8437236..8e55d488b6 100644 --- a/orca-core/src/test/java/com/netflix/spinnaker/orca/pipeline/ExecutionLauncherTest.java +++ b/orca-core/src/test/java/com/netflix/spinnaker/orca/pipeline/ExecutionLauncherTest.java @@ -27,7 +27,12 @@ import com.netflix.spinnaker.orca.api.pipeline.models.ExecutionType; import com.netflix.spinnaker.orca.api.pipeline.models.PipelineExecution; import com.netflix.spinnaker.orca.config.ExecutionConfigurationProperties; +import com.netflix.spinnaker.orca.jackson.OrcaObjectMapper; +import com.netflix.spinnaker.orca.pipeline.model.PipelineTrigger; +import com.netflix.spinnaker.orca.pipeline.model.support.TriggerDeserializer; import com.netflix.spinnaker.orca.pipeline.persistence.ExecutionRepository; +import com.netflix.spinnaker.orca.sql.PipelineRefTriggerDeserializerSupplier; +import com.netflix.spinnaker.orca.sql.pipeline.persistence.PipelineRefTrigger; import com.netflix.spinnaker.orca.test.YamlFileApplicationContextInitializer; import java.time.Clock; import java.util.Map; @@ -332,6 +337,120 @@ public void testExcludeSpinnakerAccountsFromPipeline() throws Exception { assertThat(pipelineExecution.getAuthentication().getAllowedAccounts()).isEqualTo(Set.of()); } + @DisplayName( + "ExecutionLauncher can start a new execution when a customerTriggerSupplier is provided") + @Test + public void testPipelineRefCanBeDeserializeWhenEnabled() throws Exception { + // create the orcaObjectMapper to be able to deserialize triggers + ObjectMapper orcaMapper = OrcaObjectMapper.getInstance(); + // add the Custom Trigger Deserializer for PipelineRef + TriggerDeserializer.Companion.getCustomTriggerSuppliers().clear(); + TriggerDeserializer.Companion.getCustomTriggerSuppliers() + .add(new PipelineRefTriggerDeserializerSupplier(true)); + // setup + executionLauncher = + new ExecutionLauncher( + orcaMapper, + executionRepository, + executionRunner, + clock, + applicationEventPublisher, + pipelineValidator, + registry, + executionConfigurationProperties); + + // when + // childPipeline pipeline type should be able to run + PipelineExecution pipelineExecution = + executionLauncher.start( + ExecutionType.PIPELINE, getConfigJson("ad-hoc/pipeline-with-pipeline-trigger.json")); + + // then + // verify that the execution runner attempted to start the execution as expected + verify(executionRunner).start(pipelineExecution); + // verify that the PipelineTrigger is deserialized as PipelineRef + assertThat(pipelineExecution.getTrigger()).isInstanceOf(PipelineRefTrigger.class); + // verify that no errors were thrown such as the explicitly disabled ones + verify(executionRepository, never()).updateStatus(any(), anyString(), any()); + verify(executionRepository, never()).cancel(any(), anyString(), anyString(), anyString()); + } + + @DisplayName( + "ExecutionLauncher can start a new execution and the state of the trigger does not change when a customerTriggerSupplier is provided") + @Test + public void testPipelineTriggerIsNotDeserializedIntoPipelineRefWhenDisabled() throws Exception { + // create the orcaObjectMapper to be able to deserialize triggers + ObjectMapper orcaMapper = OrcaObjectMapper.getInstance(); + // add the Custom Trigger Deserializer for PipelineRef + TriggerDeserializer.Companion.getCustomTriggerSuppliers().clear(); + TriggerDeserializer.Companion.getCustomTriggerSuppliers() + .add(new PipelineRefTriggerDeserializerSupplier(false)); + // setup + executionLauncher = + new ExecutionLauncher( + orcaMapper, + executionRepository, + executionRunner, + clock, + applicationEventPublisher, + pipelineValidator, + registry, + executionConfigurationProperties); + + // when + // childPipeline pipeline type should be able to run + PipelineExecution pipelineExecution = + executionLauncher.start( + ExecutionType.PIPELINE, getConfigJson("ad-hoc/pipeline-with-pipeline-trigger.json")); + + // then + // verify that the execution runner attempted to start the execution as expected + verify(executionRunner).start(pipelineExecution); + // verify that the execution has PipelineTrigger + assertThat(pipelineExecution.getTrigger()).isInstanceOf(PipelineTrigger.class); + // verify that no errors were thrown such as the explicitly disabled ones + verify(executionRepository, never()).updateStatus(any(), anyString(), any()); + verify(executionRepository, never()).cancel(any(), anyString(), anyString(), anyString()); + } + + @DisplayName( + "ExecutionLauncher can start a new execution and process special triggers when a customerTriggerSupplier is provided") + @Test + public void testPipelineRefTriggerCanBeDeserializedEvenDisabled() throws Exception { + // create the orcaObjectMapper to be able to deserialize triggers + ObjectMapper orcaMapper = OrcaObjectMapper.getInstance(); + // add the Custom Trigger Deserializer for PipelineRef + TriggerDeserializer.Companion.getCustomTriggerSuppliers().clear(); + TriggerDeserializer.Companion.getCustomTriggerSuppliers() + .add(new PipelineRefTriggerDeserializerSupplier(false)); + // setup + executionLauncher = + new ExecutionLauncher( + orcaMapper, + executionRepository, + executionRunner, + clock, + applicationEventPublisher, + pipelineValidator, + registry, + executionConfigurationProperties); + + // when + // childPipeline pipeline type should be able to run + PipelineExecution pipelineExecution = + executionLauncher.start( + ExecutionType.PIPELINE, getConfigJson("ad-hoc/pipeline-with-pipelineRef-trigger.json")); + + // then + // verify that the execution runner attempted to start the execution as expected + verify(executionRunner).start(pipelineExecution); + // verify that the PipelineTrigger is deserialized as PipelineRef + assertThat(pipelineExecution.getTrigger()).isInstanceOf(PipelineRefTrigger.class); + // verify that no errors were thrown such as the explicitly disabled ones + verify(executionRepository, never()).updateStatus(any(), anyString(), any()); + verify(executionRepository, never()).cancel(any(), anyString(), anyString(), anyString()); + } + private Map getConfigJson(String resource) throws Exception { return objectMapper.readValue( ExecutionLauncherTest.class.getResourceAsStream(resource), Map.class); diff --git a/orca-core/src/test/resources/com/netflix/spinnaker/orca/pipeline/ad-hoc/pipeline-with-pipeline-trigger.json b/orca-core/src/test/resources/com/netflix/spinnaker/orca/pipeline/ad-hoc/pipeline-with-pipeline-trigger.json new file mode 100644 index 0000000000..f2c69ce7f5 --- /dev/null +++ b/orca-core/src/test/resources/com/netflix/spinnaker/orca/pipeline/ad-hoc/pipeline-with-pipeline-trigger.json @@ -0,0 +1,30 @@ +{ + "application":"edgartest", + "name":"wait stage", + "stages": [ + { + "name": "Wait", + "refId": "1", + "requisiteStageRefIds": [], + "type": "wait", + "waitTime": 30 + } + ], + "trigger": { + "type": "pipeline", + "parentExecution": { + "application": "edgartest", + "id": "parentID", + "stages": [ + { + "name": "Wait", + "refId": "1", + "requisiteStageRefIds": [], + "type": "wait", + "waitTime": 30 + } + ] + } + }, + "origin":"api" +} diff --git a/orca-core/src/test/resources/com/netflix/spinnaker/orca/pipeline/ad-hoc/pipeline-with-pipelineRef-trigger.json b/orca-core/src/test/resources/com/netflix/spinnaker/orca/pipeline/ad-hoc/pipeline-with-pipelineRef-trigger.json new file mode 100644 index 0000000000..aa9e12da4d --- /dev/null +++ b/orca-core/src/test/resources/com/netflix/spinnaker/orca/pipeline/ad-hoc/pipeline-with-pipelineRef-trigger.json @@ -0,0 +1,18 @@ +{ + "application":"edgartest", + "name":"wait stage", + "stages": [ + { + "name": "Wait", + "refId": "1", + "requisiteStageRefIds": [], + "type": "wait", + "waitTime": 30 + } + ], + "trigger": { + "type": "pipelineRef", + "parentExecutionId": "parentID" + }, + "origin":"api" +} diff --git a/orca-sql/src/main/kotlin/com/netflix/spinnaker/orca/sql/PipelineRefTriggerDeserializerSupplier.kt b/orca-sql/src/main/kotlin/com/netflix/spinnaker/orca/sql/PipelineRefTriggerDeserializerSupplier.kt index 1fdfa65f18..9654002042 100644 --- a/orca-sql/src/main/kotlin/com/netflix/spinnaker/orca/sql/PipelineRefTriggerDeserializerSupplier.kt +++ b/orca-sql/src/main/kotlin/com/netflix/spinnaker/orca/sql/PipelineRefTriggerDeserializerSupplier.kt @@ -32,6 +32,13 @@ class PipelineRefTriggerDeserializerSupplier( override val predicate: (node: JsonNode) -> Boolean get() = { node -> + // We need to deserialize always PipelineRef because + // 1. There is a single object mapper use for serialize/deserialize in + // multiple places (OperationsController, ExecutionLauncher, SqlExecutionRepository) + // 2. Insert executions with trigger works properly but update an existing execution fails because + // SqlExecutionRepository gets execution from database (deserialize) and we convert PipelineRef in its + // In-memory representation. This makes the SqlExecutionRepository to try to store again all execution + // context. We need to deserialize again to transform into PipelineRef properly. if (pipelineRefEnabled) { node.looksLikePipeline() || node.isPipelineRefTrigger() //if pipelineRef enabled we deserialize PipelineTrigger as PipelineRefTrigger } else {