diff --git a/orca-kayenta/src/main/java/com/netflix/spinnaker/orca/kayenta/pipeline/functions/KayentaConfigExpressionFunctionProvider.java b/orca-kayenta/src/main/java/com/netflix/spinnaker/orca/kayenta/pipeline/functions/KayentaConfigExpressionFunctionProvider.java new file mode 100644 index 0000000000..869fd48439 --- /dev/null +++ b/orca-kayenta/src/main/java/com/netflix/spinnaker/orca/kayenta/pipeline/functions/KayentaConfigExpressionFunctionProvider.java @@ -0,0 +1,87 @@ +/* + * Copyright 2019 Netflix, 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.kayenta.pipeline.functions; + +import com.google.common.base.Strings; +import com.netflix.spinnaker.kork.api.expressions.ExpressionFunctionProvider; +import com.netflix.spinnaker.kork.expressions.SpelHelperFunctionException; +import com.netflix.spinnaker.orca.kayenta.KayentaCanaryConfig; +import com.netflix.spinnaker.orca.kayenta.KayentaService; +import java.util.List; +import java.util.Optional; +import javax.annotation.Nullable; +import org.jetbrains.annotations.NotNull; +import org.springframework.stereotype.Component; + +@Component +public class KayentaConfigExpressionFunctionProvider implements ExpressionFunctionProvider { + + // Static because it's needed during expression eval (which is a static) + private static KayentaService kayentaService; + + public KayentaConfigExpressionFunctionProvider(KayentaService kayentaService) { + this.kayentaService = kayentaService; + } + + @Nullable + @Override + public String getNamespace() { + return null; + } + + @NotNull + @Override + public Functions getFunctions() { + return new Functions( + new FunctionDefinition( + "canaryConfigNameToId", + "Look up the canary config ID for the given config name and app", + new FunctionParameter(String.class, "name", "The name of the config"), + new FunctionParameter(String.class, "app", "The name of the app"))); + } + + /** + * SpEL expression used to convert the name of a canary config to the ID. + * + * @param name Name of the config. + * @param app Application which owns the config. + * @return The ID of the config which corresponds to the name and app provided. + */ + public static String canaryConfigNameToId(String name, String app) { + if (Strings.isNullOrEmpty(name)) { + throw new SpelHelperFunctionException( + "Config name is a required field for the canaryConfigNameToId function."); + } + if (Strings.isNullOrEmpty(app)) { + throw new SpelHelperFunctionException( + "App is a required field for the canaryConfigNameToId function."); + } + + List configs = kayentaService.getAllCanaryConfigs(); + Optional found = + configs.stream() + .filter(c -> c.getApplications().contains(app)) + .filter(c -> c.getName().equals(name)) + .findFirst(); + if (found.isPresent()) { + return found.get().getId(); + } + + throw new SpelHelperFunctionException( + "Unable to find config with name " + name + " for app " + app + "."); + } +} diff --git a/orca-kayenta/src/main/kotlin/com/netflix/spinnaker/orca/kayenta/config/KayentaConfiguration.kt b/orca-kayenta/src/main/kotlin/com/netflix/spinnaker/orca/kayenta/config/KayentaConfiguration.kt index cf2ca0358c..45b516d07b 100644 --- a/orca-kayenta/src/main/kotlin/com/netflix/spinnaker/orca/kayenta/config/KayentaConfiguration.kt +++ b/orca-kayenta/src/main/kotlin/com/netflix/spinnaker/orca/kayenta/config/KayentaConfiguration.kt @@ -17,6 +17,7 @@ package com.netflix.spinnaker.orca.kayenta.config import com.fasterxml.jackson.databind.SerializationFeature.WRITE_DATES_AS_TIMESTAMPS +import com.netflix.spinnaker.kork.api.expressions.ExpressionFunctionProvider import com.netflix.spinnaker.orca.jackson.OrcaObjectMapper import com.netflix.spinnaker.orca.kayenta.KayentaService import com.netflix.spinnaker.orca.retrofit.RetrofitConfiguration @@ -35,6 +36,8 @@ import retrofit.RestAdapter import retrofit.RestAdapter.LogLevel import retrofit.client.Client import retrofit.converter.JacksonConverter +import com.netflix.spinnaker.orca.kayenta.pipeline.functions.KayentaConfigExpressionFunctionProvider + @Configuration @Import(RetrofitConfiguration::class) @@ -77,4 +80,9 @@ class KayentaConfiguration { .build() .create(KayentaService::class.java) } + + @Bean + fun kayentaExpressionFunctionProvider(kayentaService: KayentaService): ExpressionFunctionProvider { + return KayentaConfigExpressionFunctionProvider(kayentaService) + } } diff --git a/orca-kayenta/src/test/java/com/netflix/spinnaker/orca/kayenta/pipeline/functions/KayentaConfigExpressionFunctionProviderTest.java b/orca-kayenta/src/test/java/com/netflix/spinnaker/orca/kayenta/pipeline/functions/KayentaConfigExpressionFunctionProviderTest.java new file mode 100644 index 0000000000..9d39e06d3b --- /dev/null +++ b/orca-kayenta/src/test/java/com/netflix/spinnaker/orca/kayenta/pipeline/functions/KayentaConfigExpressionFunctionProviderTest.java @@ -0,0 +1,60 @@ +package com.netflix.spinnaker.orca.kayenta.pipeline.functions; + +import static org.mockito.Mockito.when; + +import com.google.common.collect.Lists; +import com.netflix.spinnaker.kork.expressions.SpelHelperFunctionException; +import com.netflix.spinnaker.orca.kayenta.KayentaCanaryConfig; +import com.netflix.spinnaker.orca.kayenta.KayentaService; +import java.util.Collections; +import java.util.List; +import org.junit.Assert; +import org.junit.Test; +import org.mockito.Mockito; + +public class KayentaConfigExpressionFunctionProviderTest { + + @Test(expected = SpelHelperFunctionException.class) + public void missingName() { + KayentaConfigExpressionFunctionProvider provider = + new KayentaConfigExpressionFunctionProvider(Mockito.mock(KayentaService.class)); + provider.canaryConfigNameToId(null, "myapp"); + } + + @Test(expected = SpelHelperFunctionException.class) + public void missingApp() { + KayentaConfigExpressionFunctionProvider provider = + new KayentaConfigExpressionFunctionProvider(Mockito.mock(KayentaService.class)); + provider.canaryConfigNameToId("myname", null); + } + + @Test + public void conversionWorks() { + KayentaService kayentaService = Mockito.mock(KayentaService.class); + List canaryConfigs = Lists.newArrayList(); + KayentaCanaryConfig config = + new KayentaCanaryConfig("myconfig", "myname", 0L, null, Collections.singletonList("myapp")); + canaryConfigs.add(config); + when(kayentaService.getAllCanaryConfigs()).thenReturn(canaryConfigs); + + KayentaConfigExpressionFunctionProvider provider = + new KayentaConfigExpressionFunctionProvider(kayentaService); + String configId = provider.canaryConfigNameToId("myname", "myapp"); + + Assert.assertEquals("myconfig", configId); + } + + @Test(expected = SpelHelperFunctionException.class) + public void nothingFound() { + KayentaService kayentaService = Mockito.mock(KayentaService.class); + List canaryConfigs = Lists.newArrayList(); + KayentaCanaryConfig config = + new KayentaCanaryConfig("myconfig", "myname", 0L, null, Collections.singletonList("myapp")); + canaryConfigs.add(config); + when(kayentaService.getAllCanaryConfigs()).thenReturn(canaryConfigs); + + KayentaConfigExpressionFunctionProvider provider = + new KayentaConfigExpressionFunctionProvider(kayentaService); + provider.canaryConfigNameToId("someothername", "myapp"); + } +}