Skip to content

Commit

Permalink
fix(manifest): fixes deploy manifest SpEL evaluation toggle (#4128)
Browse files Browse the repository at this point in the history
* fix(manifest): fixes deploy manifest SpEL evaluation toggle

* add more tests
  • Loading branch information
danielpeach committed May 25, 2021
1 parent d9423bc commit a2acb5c
Show file tree
Hide file tree
Showing 4 changed files with 172 additions and 3 deletions.
1 change: 1 addition & 0 deletions orca-clouddriver/orca-clouddriver.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ dependencies {
testImplementation(project(":orca-test"))
testImplementation(project(":orca-test-groovy"))
testImplementation(project(":orca-api-tck"))
testImplementation(project(":orca-queue"))
testImplementation("com.github.tomakehurst:wiremock:2.15.0")
testImplementation("org.springframework:spring-test")
testImplementation("org.junit.jupiter:junit-jupiter-api")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
import static com.google.common.collect.ImmutableList.toImmutableList;

import com.google.common.collect.ImmutableList;
import com.netflix.spinnaker.orca.api.pipeline.graph.StageDefinitionBuilder;
import com.netflix.spinnaker.kork.expressions.ExpressionEvaluationSummary;
import com.netflix.spinnaker.orca.api.pipeline.graph.StageGraphBuilder;
import com.netflix.spinnaker.orca.api.pipeline.graph.TaskNode;
import com.netflix.spinnaker.orca.api.pipeline.models.StageExecution;
Expand All @@ -31,7 +31,10 @@
import com.netflix.spinnaker.orca.clouddriver.tasks.manifest.*;
import com.netflix.spinnaker.orca.clouddriver.tasks.manifest.DeployManifestContext.TrafficManagement;
import com.netflix.spinnaker.orca.clouddriver.tasks.manifest.ResolveDeploySourceManifestTask;
import com.netflix.spinnaker.orca.pipeline.ExpressionAwareStageDefinitionBuilder;
import com.netflix.spinnaker.orca.pipeline.tasks.artifacts.BindProducedArtifactsTask;
import com.netflix.spinnaker.orca.pipeline.util.ContextParameterProcessor;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
Expand All @@ -40,7 +43,7 @@
import org.springframework.stereotype.Component;

@Component
public class DeployManifestStage implements StageDefinitionBuilder {
public class DeployManifestStage extends ExpressionAwareStageDefinitionBuilder {
public static final String PIPELINE_CONFIG_TYPE = "deployManifest";

private final OortService oortService;
Expand Down Expand Up @@ -145,4 +148,18 @@ private ImmutableList<String> getOldManifestNames(
.map(ManifestCoordinates::getFullResourceName)
.collect(toImmutableList());
}

@Override
public boolean processExpressions(
@Nonnull StageExecution stage,
@Nonnull ContextParameterProcessor contextParameterProcessor,
@Nonnull ExpressionEvaluationSummary summary) {
DeployManifestContext context = stage.mapTo(DeployManifestContext.class);
if (context.isSkipExpressionEvaluation()) {
processDefaultEntries(
stage, contextParameterProcessor, summary, Collections.singletonList("manifests"));
return false;
}
return true;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
/*
* Copyright 2021 Armory, 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.pipeline.manifest

import com.netflix.spinnaker.orca.pipeline.expressions.PipelineExpressionEvaluator.SpelEvaluatorVersion
import com.netflix.spinnaker.orca.api.test.pipeline
import com.netflix.spinnaker.orca.api.test.stage
import com.netflix.spinnaker.orca.pipeline.StageDefinitionBuilderFactory
import com.netflix.spinnaker.orca.pipeline.util.ContextParameterProcessor
import com.netflix.spinnaker.orca.q.handler.ExpressionAware
import dev.minutest.junit.JUnit5Minutests
import dev.minutest.rootContext
import io.mockk.mockk
import strikt.api.expectThat
import strikt.assertions.isEqualTo

class DeployManifestExpressionEvaluationTest : JUnit5Minutests {
private val defaultSpelVersion = SpelEvaluatorVersion.Default().key

fun tests() = rootContext<Fixture> {
fixture { Fixture() }

listOf(defaultSpelVersion, "v4").forEach { spelVersion ->
test("should evaluate expression in manifest by default when SpEL $spelVersion is enabled") {
val pipeline = pipeline {
if (spelVersion != "") spelEvaluator = spelVersion
stage {
refId = "1"
type = "deployManifest"
context["manifests"] = listOf(
mapOf(
"metadata" to mapOf(
"name" to "my-k8s-manifest",
"should-evaluate-to-literal-true" to "\${#toBoolean('true')}"
)
)
)
}
}
val deployStage = pipeline.stageByRef("1")

expectThat(deployStage.withMergedContext().context["manifests"]).isEqualTo(listOf(mapOf(
"metadata" to mapOf(
"name" to "my-k8s-manifest",
"should-evaluate-to-literal-true" to true,
)
)))
}
}

// When the default SpEL version is bumped from v3 -> v4 this test will fail.
// At that point we should remove this test and add a test to verify that the
// default SpEL version handles the 'skipExpressionEvaluation' flag to protect against regressions in SpEL v5+.
test("should evaluate expressions even when 'skipExpressionEvaluation' context flag is enabled for default SpEL evaluator") {
val pipeline = pipeline {
spelEvaluator = defaultSpelVersion
stage {
refId = "1"
type = "deployManifest"
context["skipExpressionEvaluation"] = true
context["manifests"] = listOf(
mapOf(
"metadata" to mapOf(
"name" to "my-k8s-manifest",
"should-evaluate-to-literal-true" to "\${#toBoolean('true')}"
)
)
)
}
}
val deployStage = pipeline.stageByRef("1")

expectThat(deployStage.withMergedContext().context["manifests"]).isEqualTo(listOf(mapOf(
"metadata" to mapOf(
"name" to "my-k8s-manifest",
"should-evaluate-to-literal-true" to true
)
)))
}

test("should not evaluate expressions in manifests when 'skipExpressionEvaluation' context flag is enabled and SpEL evaluator is v4") {
val pipeline = pipeline {
spelEvaluator = "v4"
stage {
refId = "1"
type = "deployManifest"
context["skipExpressionEvaluation"] = true
context["manifests"] = listOf(
mapOf(
"metadata" to mapOf(
"name" to "my-k8s-manifest",
"should-not-evaluate-to-literal-true" to "\${#toBoolean('true')}"
)
)
)
}
}
val deployStage = pipeline.stageByRef("1")

expectThat(deployStage.withMergedContext().context["manifests"]).isEqualTo(listOf(mapOf(
"metadata" to mapOf(
"name" to "my-k8s-manifest",
"should-not-evaluate-to-literal-true" to "\${#toBoolean('true')}"
)
)))
}

listOf(defaultSpelVersion, "v4").forEach { spelVersion ->
listOf(true, false).forEach { skipExpressionEvaluation ->
test("non-manifest context values should be evaluated when SpEL $spelVersion is enabled and skipExpressionEvaluation=$skipExpressionEvaluation") {
val pipeline = pipeline {
if (spelVersion != "") spelEvaluator = spelVersion
stage {
refId = "1"
type = "deployManifest"
context["skipExpressionEvaluation"] = skipExpressionEvaluation
context["should-evaluate-to-literal-true"] = "\${#toBoolean('true')}"
}
}
val deployStage = pipeline.stageByRef("1")

expectThat(deployStage.withMergedContext().context["should-evaluate-to-literal-true"]).isEqualTo(true)
}
}
}
}

class Fixture : ExpressionAware {
override val contextParameterProcessor = ContextParameterProcessor()
override val stageDefinitionBuilderFactory = StageDefinitionBuilderFactory { execution ->
when (execution.type) {
"deployManifest" -> DeployManifestStage(mockk())
else -> throw IllegalArgumentException("Test factory can't make \"${execution.type}\" stages.")
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ public abstract boolean processExpressions(
* @param summary (should be same as passed into processExpressions)
* @param excludedKeys keys to exclude from processing
*/
final void processDefaultEntries(
public final void processDefaultEntries(
@Nonnull StageExecution stage,
@Nonnull ContextParameterProcessor contextParameterProcessor,
@Nonnull ExpressionEvaluationSummary summary,
Expand Down

0 comments on commit a2acb5c

Please sign in to comment.