diff --git a/kork-expressions/kork-expressions.gradle b/kork-expressions/kork-expressions.gradle index 67903e659..f1bf50d00 100644 --- a/kork-expressions/kork-expressions.gradle +++ b/kork-expressions/kork-expressions.gradle @@ -18,6 +18,7 @@ dependencies { testImplementation project(":kork-artifacts") testImplementation "org.assertj:assertj-core" testImplementation "org.junit.jupiter:junit-jupiter-api" + testImplementation "org.junit.jupiter:junit-jupiter-params" testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine" testRuntimeOnly "org.slf4j:slf4j-simple" } diff --git a/kork-expressions/src/main/java/com/netflix/spinnaker/kork/expressions/ExpressionsSupport.java b/kork-expressions/src/main/java/com/netflix/spinnaker/kork/expressions/ExpressionsSupport.java index ad7e1347d..3392ec73a 100644 --- a/kork-expressions/src/main/java/com/netflix/spinnaker/kork/expressions/ExpressionsSupport.java +++ b/kork-expressions/src/main/java/com/netflix/spinnaker/kork/expressions/ExpressionsSupport.java @@ -27,6 +27,7 @@ import com.netflix.spinnaker.kork.expressions.allowlist.MapPropertyAccessor; import com.netflix.spinnaker.kork.expressions.allowlist.ReturnTypeRestrictor; import com.netflix.spinnaker.kork.expressions.config.ExpressionProperties; +import com.netflix.spinnaker.kork.expressions.functions.ArtifactStoreFunctions; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Arrays; @@ -96,7 +97,9 @@ public ExpressionsSupport( expressionFunctionProviders = new ArrayList<>( Arrays.asList( - new JsonExpressionFunctionProvider(), new StringExpressionFunctionProvider())); + new ArtifactStoreFunctions(), + new JsonExpressionFunctionProvider(), + new StringExpressionFunctionProvider())); if (extraExpressionFunctionProviders != null) { expressionFunctionProviders.addAll(extraExpressionFunctionProviders); @@ -166,6 +169,7 @@ private StandardEvaluationContext createEvaluationContext( evaluationContext.setTypeLocator(new AllowListTypeLocator()); evaluationContext.setTypeConverter( new ArtifactUriToReferenceConverter(ArtifactStore.getInstance())); + evaluationContext.setMethodResolvers( Collections.singletonList(new FilteredMethodResolver(returnTypeRestrictor))); evaluationContext.setPropertyAccessors( diff --git a/kork-expressions/src/main/java/com/netflix/spinnaker/kork/expressions/functions/ArtifactStoreFunctions.java b/kork-expressions/src/main/java/com/netflix/spinnaker/kork/expressions/functions/ArtifactStoreFunctions.java new file mode 100644 index 000000000..827f8977a --- /dev/null +++ b/kork-expressions/src/main/java/com/netflix/spinnaker/kork/expressions/functions/ArtifactStoreFunctions.java @@ -0,0 +1,56 @@ +/* + * Copyright 2024 Apple 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.kork.expressions.functions; + +import com.netflix.spinnaker.kork.api.expressions.ExpressionFunctionProvider; +import org.jetbrains.annotations.Nullable; + +/** Houses all SpEL related functions that deal specifically with the artifact store */ +public class ArtifactStoreFunctions implements ExpressionFunctionProvider { + @Nullable + @Override + public String getNamespace() { + return null; + } + + /** + * Used to return the original based64 reference that was stored by the artifact store. It's + * important to note that utilizing this SpEL function will balloon the context, e.g. increasing + * the size of the execution context. + * + *
{@code ${ #fetchReference(#stage('Bake Manifest').context.artifacts[0].reference) }}
+ */
+ public static String fetchReference(String ref) {
+ // We do not have to do anything here since the artifact URI converter will
+ // convert any references to the original stored reference
+ //
+ // One caveat with this function is that if there is nothing to retrieve,
+ // the original string will just be returned
+ return ref;
+ }
+
+ @Override
+ public Functions getFunctions() {
+ return new Functions(
+ new FunctionDefinition(
+ "fetchReference",
+ "Retrieve artifact reference",
+ new FunctionParameter(
+ String.class,
+ "ref",
+ "Will return the associated artifact's base64 reference back.")));
+ }
+}
diff --git a/kork-expressions/src/test/java/com/netflix/spinnaker/kork/expressions/ExpressionsSupportTest.java b/kork-expressions/src/test/java/com/netflix/spinnaker/kork/expressions/ExpressionsSupportTest.java
index 19024ec83..1ae7dcd22 100644
--- a/kork-expressions/src/test/java/com/netflix/spinnaker/kork/expressions/ExpressionsSupportTest.java
+++ b/kork-expressions/src/test/java/com/netflix/spinnaker/kork/expressions/ExpressionsSupportTest.java
@@ -17,7 +17,6 @@
package com.netflix.spinnaker.kork.expressions;
import static org.assertj.core.api.Assertions.assertThat;
-import static org.junit.Assert.assertThat;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
@@ -32,7 +31,12 @@
import java.util.HashMap;
import java.util.Map;
import java.util.function.Function;
+import java.util.stream.Stream;
+import lombok.SneakyThrows;
import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.ParserContext;
import org.springframework.expression.common.TemplateParserContext;
@@ -107,29 +111,36 @@ public void testToJsonWhenComposedExpressionAndEvaluationContext() {
assertThat(evaluated).isEqualTo("{\"json_file\":\"${#toJson(#doNotEval(file_json))}\"}");
}
- @Test
- public void artifactReferenceInSpEL() {
+ @ParameterizedTest
+ @MethodSource("artifactReferenceArgs")
+ public void artifactReferenceInSpEL(String reference, String expected, String expr) {
MockArtifactStore artifactStore = new MockArtifactStore();
ArtifactStore.setInstance(artifactStore);
ExpressionProperties expressionProperties = new ExpressionProperties();
- String expectedValue = "Hello world";
- artifactStore.cache.put("ref://app/sha", expectedValue);
- String expr = "${#fromBase64(\"ref://app/sha\")}";
+ artifactStore.cache.put("ref://app/sha", reference);
Map