diff --git a/clouddriver-kubernetes/src/main/groovy/com/netflix/spinnaker/clouddriver/kubernetes/v2/artifact/ArtifactReplacer.java b/clouddriver-kubernetes/src/main/groovy/com/netflix/spinnaker/clouddriver/kubernetes/v2/artifact/ArtifactReplacer.java index 6e5e280fc44..513b94fc15f 100644 --- a/clouddriver-kubernetes/src/main/groovy/com/netflix/spinnaker/clouddriver/kubernetes/v2/artifact/ArtifactReplacer.java +++ b/clouddriver-kubernetes/src/main/groovy/com/netflix/spinnaker/clouddriver/kubernetes/v2/artifact/ArtifactReplacer.java @@ -17,37 +17,31 @@ package com.netflix.spinnaker.clouddriver.kubernetes.v2.artifact; +import static com.google.common.collect.ImmutableList.toImmutableList; +import static com.google.common.collect.ImmutableSet.toImmutableSet; + import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.node.ArrayNode; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableSet; import com.jayway.jsonpath.Configuration; import com.jayway.jsonpath.DocumentContext; import com.jayway.jsonpath.JsonPath; -import com.jayway.jsonpath.PathNotFoundException; import com.jayway.jsonpath.spi.json.JacksonJsonNodeJsonProvider; import com.jayway.jsonpath.spi.mapper.JacksonMappingProvider; -import com.netflix.spinnaker.clouddriver.artifacts.kubernetes.KubernetesArtifactType; import com.netflix.spinnaker.clouddriver.kubernetes.v2.description.manifest.KubernetesManifest; import com.netflix.spinnaker.kork.artifacts.model.Artifact; import java.io.IOException; -import java.util.ArrayList; import java.util.Collection; +import java.util.Collections; import java.util.List; -import java.util.Objects; -import java.util.Set; -import java.util.function.Function; -import java.util.regex.Matcher; -import java.util.regex.Pattern; -import java.util.stream.Collectors; -import java.util.stream.Stream; -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Getter; +import javax.annotation.Nonnull; +import javax.annotation.ParametersAreNonnullByDefault; import lombok.Value; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang.StringUtils; +@ParametersAreNonnullByDefault @Slf4j public class ArtifactReplacer { private static final ObjectMapper mapper = new ObjectMapper(); @@ -57,14 +51,13 @@ public class ArtifactReplacer { .mappingProvider(new JacksonMappingProvider()) .build(); - private final List replacers = new ArrayList<>(); + private final ImmutableList replacers; - public ArtifactReplacer addReplacer(Replacer replacer) { - replacers.add(replacer); - return this; + public ArtifactReplacer(Collection replacers) { + this.replacers = ImmutableList.copyOf(replacers); } - private static List filterKubernetesArtifactsByNamespaceAndAccount( + private static ImmutableList filterKubernetesArtifactsByNamespaceAndAccount( String namespace, String account, List artifacts) { return artifacts.stream() // Keep artifacts that either aren't k8s, or are in the same namespace and account as our @@ -98,14 +91,14 @@ private static List filterKubernetesArtifactsByNamespaceAndAccount( return accountMatches && locationMatches; }) - .collect(Collectors.toList()); + .collect(toImmutableList()); } + @Nonnull public ReplaceResult replaceAll( KubernetesManifest input, List artifacts, String namespace, String account) { log.debug("Doing replacement on {} using {}", input, artifacts); - // final to use in below lambda - final List finalArtifacts = + ImmutableList filteredArtifacts = filterKubernetesArtifactsByNamespaceAndAccount(namespace, account, artifacts); DocumentContext document; try { @@ -115,26 +108,23 @@ public ReplaceResult replaceAll( throw new RuntimeException(e); } - Set replacedArtifacts = - replacers.stream() - .map( - r -> - finalArtifacts.stream() - .filter(a -> r.replaceIfPossible(document, a)) - .collect(Collectors.toSet())) - .flatMap(Collection::stream) - .collect(Collectors.toSet()); + ImmutableSet.Builder replacedArtifacts = new ImmutableSet.Builder<>(); + replacers.forEach( + replacer -> + replacedArtifacts.addAll(replacer.replaceArtifacts(document, filteredArtifacts))); try { return new ReplaceResult( - mapper.readValue(document.jsonString(), KubernetesManifest.class), replacedArtifacts); + mapper.readValue(document.jsonString(), KubernetesManifest.class), + replacedArtifacts.build()); } catch (IOException e) { log.error("Malformed Document Context", e); throw new RuntimeException(e); } } - public Set findAll(KubernetesManifest input) { + @Nonnull + public ImmutableSet findAll(KubernetesManifest input) { DocumentContext document; try { document = JsonPath.using(configuration).parse(mapper.writeValueAsString(input)); @@ -146,25 +136,7 @@ public Set findAll(KubernetesManifest input) { .map( r -> { try { - return ((List) - mapper.convertValue( - r.findAll(document), new TypeReference>() {})) - .stream() - .map( - s -> { - String nameFromReference = r.getNameFromReference(s); - String name = nameFromReference == null ? s : nameFromReference; - if (r.namePattern == null || nameFromReference != null) { - return Artifact.builder() - .type(r.getType().getType()) - .reference(s) - .name(name) - .build(); - } else { - return null; - } - }) - .filter(Objects::nonNull); + return r.getArtifacts(document); } catch (Exception e) { // This happens when a manifest isn't fully defined (e.g. not all properties are // there) @@ -173,89 +145,16 @@ public Set findAll(KubernetesManifest input) { input.getFullResourceName(), r, e); - return Stream.empty(); + return Collections.emptyList(); } }) - .flatMap(x -> x) - .collect(Collectors.toSet()); - } - - @Slf4j - @Builder - @AllArgsConstructor - public static class Replacer { - private final String replacePath; - private final String findPath; - private final Pattern namePattern; // the first group should be the artifact name - private final Function nameFromReference; - - @Getter private final KubernetesArtifactType type; - - private static String substituteField(String result, String fieldName, String field) { - field = field == null ? "" : field; - return result.replace("{%" + fieldName + "%}", field); - } - - private static String processPath(String path, Artifact artifact) { - String result = substituteField(path, "name", artifact.getName()); - result = substituteField(result, "type", artifact.getType()); - result = substituteField(result, "version", artifact.getVersion()); - result = substituteField(result, "reference", artifact.getReference()); - return result; - } - - ArrayNode findAll(DocumentContext obj) { - return obj.read(findPath); - } - - String getNameFromReference(String reference) { - if (nameFromReference != null) { - return nameFromReference.apply(reference); - } else if (namePattern != null) { - Matcher m = namePattern.matcher(reference); - if (m.find() && m.groupCount() > 0 && StringUtils.isNotEmpty(m.group(1))) { - return m.group(1); - } else { - return null; - } - } else { - return null; - } - } - - boolean replaceIfPossible(DocumentContext obj, Artifact artifact) { - if (artifact == null || StringUtils.isEmpty(artifact.getType())) { - throw new IllegalArgumentException("Artifact and artifact type must be set."); - } - - if (!artifact.getType().equals(type.getType())) { - return false; - } - - String jsonPath = processPath(replacePath, artifact); - - log.debug("Processed jsonPath == {}", jsonPath); - - Object get; - try { - get = obj.read(jsonPath); - } catch (PathNotFoundException e) { - return false; - } - if (get == null || (get instanceof ArrayNode && ((ArrayNode) get).size() == 0)) { - return false; - } - - log.info("Found valid swap for " + artifact + " using " + jsonPath + ": " + get); - obj.set(jsonPath, artifact.getReference()); - - return true; - } + .flatMap(Collection::stream) + .collect(toImmutableSet()); } @Value public static class ReplaceResult { private final KubernetesManifest manifest; - private final Set boundArtifacts; + private final ImmutableSet boundArtifacts; } } diff --git a/clouddriver-kubernetes/src/main/groovy/com/netflix/spinnaker/clouddriver/kubernetes/v2/artifact/ArtifactReplacerFactory.java b/clouddriver-kubernetes/src/main/groovy/com/netflix/spinnaker/clouddriver/kubernetes/v2/artifact/ArtifactReplacerFactory.java deleted file mode 100644 index 8c6c903de8d..00000000000 --- a/clouddriver-kubernetes/src/main/groovy/com/netflix/spinnaker/clouddriver/kubernetes/v2/artifact/ArtifactReplacerFactory.java +++ /dev/null @@ -1,162 +0,0 @@ -/* - * Copyright 2018 Google, 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.clouddriver.kubernetes.v2.artifact; - -import com.netflix.spinnaker.clouddriver.artifacts.kubernetes.KubernetesArtifactType; -import com.netflix.spinnaker.clouddriver.kubernetes.v2.artifact.ArtifactReplacer.Replacer; -import java.util.regex.Pattern; - -public class ArtifactReplacerFactory { - // The following was derived from - // https://github.com/docker/distribution/blob/95daa793b83a21656fe6c13e6d5cf1c3999108c7/reference/regexp.go - private static final String DOCKER_NAME_COMPONENT = - "[a-z0-9]+(?:(?:(?:[._]|__|[-]*)[a-z0-9]+)+)?"; - private static final String DOCKER_OPTIONAL_TAG = "(?::[\\w][\\w.-]{0,127})?"; - private static final String DOCKER_OPTIONAL_DIGEST = - "(?:@[A-Za-z][A-Za-z0-9]*(?:[-_+.][A-Za-z][A-Za-z0-9]*)*[:][0-9A-Fa-f]{32,})?"; - private static final String DOCKER_DOMAIN = - "(?:[a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9])(?:(?:\\.(?:[a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9]))+)?"; - private static final String DOCKER_OPTIONAL_PORT = "(?::[0-9]+)?"; - private static final String DOCKER_OPTIONAL_DOMAIN_AND_PORT = - "(?:" + DOCKER_DOMAIN + DOCKER_OPTIONAL_PORT + "/)?"; - private static final String DOCKER_IMAGE_NAME = - "(" - + DOCKER_OPTIONAL_DOMAIN_AND_PORT - + DOCKER_NAME_COMPONENT - + "(?:/" - + DOCKER_NAME_COMPONENT - + ")*)"; - private static final String DOCKER_IMAGE_REFERENCE = - DOCKER_IMAGE_NAME + "(" + DOCKER_OPTIONAL_TAG + "|" + DOCKER_OPTIONAL_DIGEST + ")"; - - // the image reference pattern has two capture groups. - // - the first captures the image name - // - the second captures the image tag (including the leading ":") or digest (including the - // leading "@"). - public static final Pattern DOCKER_IMAGE_REFERENCE_PATTERN = - Pattern.compile("^" + DOCKER_IMAGE_REFERENCE + "$"); - - public static Replacer dockerImageReplacer() { - return Replacer.builder() - .replacePath( - "$..spec.template.spec['containers', 'initContainers'].[?( @.image == \"{%name%}\" )].image") - .findPath("$..spec.template.spec['containers', 'initContainers'].*.image") - .nameFromReference( - ref -> { - int atIndex = ref.indexOf('@'); - // @ can only show up in image references denoting a digest - // https://github.com/docker/distribution/blob/95daa793b83a21656fe6c13e6d5cf1c3999108c7/reference/regexp.go#L70 - if (atIndex >= 0) { - return ref.substring(0, atIndex); - } - - // : can be used to denote a port, part of a digest (already matched) or a tag - // https://github.com/docker/distribution/blob/95daa793b83a21656fe6c13e6d5cf1c3999108c7/reference/regexp.go#L69 - int lastColonIndex = ref.lastIndexOf(':'); - - if (lastColonIndex < 0) { - return ref; - } - - // we don't need to check if this is a tag, or a port. ports will be matched lazily if - // they are numeric, and are treated as tags first: - // https://github.com/docker/distribution/blob/95daa793b83a21656fe6c13e6d5cf1c3999108c7/reference/regexp.go#L34 - return ref.substring(0, lastColonIndex); - }) - .type(KubernetesArtifactType.DockerImage) - .build(); - } - - public static Replacer configMapVolumeReplacer() { - return Replacer.builder() - .replacePath( - "$..spec.template.spec.volumes.[?( @.configMap.name == \"{%name%}\" )].configMap.name") - .findPath("$..spec.template.spec.volumes.*.configMap.name") - .type(KubernetesArtifactType.ConfigMap) - .build(); - } - - public static Replacer secretVolumeReplacer() { - return Replacer.builder() - .replacePath( - "$..spec.template.spec.volumes.[?( @.secret.secretName == \"{%name%}\" )].secret.secretName") - .findPath("$..spec.template.spec.volumes.*.secret.secretName") - .type(KubernetesArtifactType.Secret) - .build(); - } - - public static Replacer configMapKeyValueFromReplacer() { - return Replacer.builder() - .replacePath( - "$..spec.template.spec['containers', 'initContainers'].*.env.[?( @.valueFrom.configMapKeyRef.name == \"{%name%}\" )].valueFrom.configMapKeyRef.name") - .findPath( - "$..spec.template.spec['containers', 'initContainers'].*.env.*.valueFrom.configMapKeyRef.name") - .type(KubernetesArtifactType.ConfigMap) - .build(); - } - - public static Replacer secretKeyValueFromReplacer() { - return Replacer.builder() - .replacePath( - "$..spec.template.spec['containers', 'initContainers'].*.env.[?( @.valueFrom.secretKeyRef.name == \"{%name%}\" )].valueFrom.secretKeyRef.name") - .findPath( - "$..spec.template.spec['containers', 'initContainers'].*.env.*.valueFrom.secretKeyRef.name") - .type(KubernetesArtifactType.Secret) - .build(); - } - - public static Replacer configMapEnvFromReplacer() { - return Replacer.builder() - .replacePath( - "$..spec.template.spec['containers', 'initContainers'].*.envFrom.[?( @.configMapRef.name == \"{%name%}\" )].configMapRef.name") - .findPath( - "$..spec.template.spec['containers', 'initContainers'].*.envFrom.*.configMapRef.name") - .type(KubernetesArtifactType.ConfigMap) - .build(); - } - - public static Replacer secretEnvFromReplacer() { - return Replacer.builder() - .replacePath( - "$..spec.template.spec['containers', 'initContainers'].*.envFrom.[?( @.secretRef.name == \"{%name%}\" )].secretRef.name") - .findPath( - "$..spec.template.spec['containers', 'initContainers'].*.envFrom.*.secretRef.name") - .type(KubernetesArtifactType.Secret) - .build(); - } - - public static Replacer hpaDeploymentReplacer() { - return Replacer.builder() - .replacePath( - "$[?( (@.spec.scaleTargetRef.kind == \"Deployment\" || @.spec.scaleTargetRef.kind == \"deployment\") && @.spec.scaleTargetRef.name == \"{%name%}\" )].spec.scaleTargetRef.name") - .findPath( - "$[?( @.spec.scaleTargetRef.kind == \"Deployment\" || @.spec.scaleTargetRef.kind == \"deployment\" )].spec.scaleTargetRef.name") - .type(KubernetesArtifactType.Deployment) - .build(); - } - - public static Replacer hpaReplicaSetReplacer() { - return Replacer.builder() - .replacePath( - "$[?( (@.spec.scaleTargetRef.kind == \"ReplicaSet\" || @.spec.scaleTargetRef.kind == \"replicaSet\") && @.spec.scaleTargetRef.name == \"{%name%}\" )].spec.scaleTargetRef.name") - .findPath( - "$[?( @.spec.scaleTargetRef.kind == \"ReplicaSet\" || @.spec.scaleTargetRef.kind == \"replicaSet\" )].spec.scaleTargetRef.name") - .type(KubernetesArtifactType.ReplicaSet) - .build(); - } -} diff --git a/clouddriver-kubernetes/src/main/groovy/com/netflix/spinnaker/clouddriver/kubernetes/v2/artifact/Replacer.java b/clouddriver-kubernetes/src/main/groovy/com/netflix/spinnaker/clouddriver/kubernetes/v2/artifact/Replacer.java new file mode 100644 index 00000000000..144cc3450f3 --- /dev/null +++ b/clouddriver-kubernetes/src/main/groovy/com/netflix/spinnaker/clouddriver/kubernetes/v2/artifact/Replacer.java @@ -0,0 +1,283 @@ +/* + * Copyright 2019 Google, 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.clouddriver.kubernetes.v2.artifact; + +import static com.google.common.collect.ImmutableList.toImmutableList; + +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ArrayNode; +import com.google.common.collect.ImmutableCollection; +import com.google.common.collect.ImmutableSet; +import com.jayway.jsonpath.DocumentContext; +import com.jayway.jsonpath.PathNotFoundException; +import com.netflix.spinnaker.clouddriver.artifacts.kubernetes.KubernetesArtifactType; +import com.netflix.spinnaker.kork.artifacts.model.Artifact; +import java.util.Collection; +import java.util.List; +import java.util.Optional; +import java.util.function.Function; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import javax.annotation.ParametersAreNonnullByDefault; +import lombok.AccessLevel; +import lombok.Builder; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang.StringUtils; + +@Builder(access = AccessLevel.PRIVATE) +@ParametersAreNonnullByDefault +@Slf4j +public class Replacer { + private static final ObjectMapper mapper = new ObjectMapper(); + + @Nonnull private final String replacePath; + @Nonnull private final String findPath; + @Nullable private final Function nameFromReference; + @Nonnull private final KubernetesArtifactType type; + + private static String substituteField(String result, String fieldName, @Nullable String field) { + return result.replace("{%" + fieldName + "%}", Optional.ofNullable(field).orElse("")); + } + + private static String processPath(String path, Artifact artifact) { + String result = substituteField(path, "name", artifact.getName()); + result = substituteField(result, "type", artifact.getType()); + result = substituteField(result, "version", artifact.getVersion()); + result = substituteField(result, "reference", artifact.getReference()); + return result; + } + + private ArrayNode findAll(DocumentContext obj) { + return obj.read(findPath); + } + + @Nonnull + private Artifact artifactFromReference(String s) { + return Artifact.builder().type(type.getType()).reference(s).name(nameFromReference(s)).build(); + } + + @Nonnull + private String nameFromReference(String s) { + if (nameFromReference != null) { + return nameFromReference.apply(s); + } else { + return s; + } + } + + @Nonnull + ImmutableCollection getArtifacts(DocumentContext document) { + return mapper + .>convertValue(findAll(document), new TypeReference>() {}) + .stream() + .map(this::artifactFromReference) + .collect(toImmutableList()); + } + + @Nonnull + ImmutableCollection replaceArtifacts( + DocumentContext obj, Collection artifacts) { + ImmutableSet.Builder replacedArtifacts = new ImmutableSet.Builder<>(); + artifacts.forEach( + artifact -> { + boolean wasReplaced = replaceIfPossible(obj, artifact); + if (wasReplaced) { + replacedArtifacts.add(artifact); + } + }); + return replacedArtifacts.build(); + } + + private boolean replaceIfPossible(DocumentContext obj, @Nullable Artifact artifact) { + if (artifact == null || StringUtils.isEmpty(artifact.getType())) { + throw new IllegalArgumentException("Artifact and artifact type must be set."); + } + + if (!artifact.getType().equals(type.getType())) { + return false; + } + + String jsonPath = processPath(replacePath, artifact); + + log.debug("Processed jsonPath == {}", jsonPath); + + Object get; + try { + get = obj.read(jsonPath); + } catch (PathNotFoundException e) { + return false; + } + if (get == null || (get instanceof ArrayNode && ((ArrayNode) get).size() == 0)) { + return false; + } + + log.info("Found valid swap for " + artifact + " using " + jsonPath + ": " + get); + obj.set(jsonPath, artifact.getReference()); + + return true; + } + + private static final Replacer DOCKER_IMAGE = + builder() + .replacePath( + "$..spec.template.spec['containers', 'initContainers'].[?( @.image == \"{%name%}\" )].image") + .findPath("$..spec.template.spec['containers', 'initContainers'].*.image") + .nameFromReference( + ref -> { + int atIndex = ref.indexOf('@'); + // @ can only show up in image references denoting a digest + // https://github.com/docker/distribution/blob/95daa793b83a21656fe6c13e6d5cf1c3999108c7/reference/regexp.go#L70 + if (atIndex >= 0) { + return ref.substring(0, atIndex); + } + + // : can be used to denote a port, part of a digest (already matched) or a tag + // https://github.com/docker/distribution/blob/95daa793b83a21656fe6c13e6d5cf1c3999108c7/reference/regexp.go#L69 + int lastColonIndex = ref.lastIndexOf(':'); + + if (lastColonIndex < 0) { + return ref; + } + + // we don't need to check if this is a tag, or a port. ports will be matched lazily + // if + // they are numeric, and are treated as tags first: + // https://github.com/docker/distribution/blob/95daa793b83a21656fe6c13e6d5cf1c3999108c7/reference/regexp.go#L34 + return ref.substring(0, lastColonIndex); + }) + .type(KubernetesArtifactType.DockerImage) + .build(); + private static final Replacer POD_DOCKER_IMAGE = + builder() + .replacePath("$.spec.containers.[?( @.image == \"{%name%}\" )].image") + .findPath("$.spec.containers.*.image") + .type(KubernetesArtifactType.DockerImage) + .build(); + private static final Replacer CONFIG_MAP_VOLUME = + builder() + .replacePath( + "$..spec.template.spec.volumes.[?( @.configMap.name == \"{%name%}\" )].configMap.name") + .findPath("$..spec.template.spec.volumes.*.configMap.name") + .type(KubernetesArtifactType.ConfigMap) + .build(); + private static final Replacer SECRET_VOLUME = + builder() + .replacePath( + "$..spec.template.spec.volumes.[?( @.secret.secretName == \"{%name%}\" )].secret.secretName") + .findPath("$..spec.template.spec.volumes.*.secret.secretName") + .type(KubernetesArtifactType.Secret) + .build(); + private static final Replacer CONFIG_MAP_KEY_VALUE = + builder() + .replacePath( + "$..spec.template.spec['containers', 'initContainers'].*.env.[?( @.valueFrom.configMapKeyRef.name == \"{%name%}\" )].valueFrom.configMapKeyRef.name") + .findPath( + "$..spec.template.spec['containers', 'initContainers'].*.env.*.valueFrom.configMapKeyRef.name") + .type(KubernetesArtifactType.ConfigMap) + .build(); + private static final Replacer SECRET_KEY_VALUE = + builder() + .replacePath( + "$..spec.template.spec['containers', 'initContainers'].*.env.[?( @.valueFrom.secretKeyRef.name == \"{%name%}\" )].valueFrom.secretKeyRef.name") + .findPath( + "$..spec.template.spec['containers', 'initContainers'].*.env.*.valueFrom.secretKeyRef.name") + .type(KubernetesArtifactType.Secret) + .build(); + private static final Replacer CONFIG_MAP_ENV = + builder() + .replacePath( + "$..spec.template.spec['containers', 'initContainers'].*.envFrom.[?( @.configMapRef.name == \"{%name%}\" )].configMapRef.name") + .findPath( + "$..spec.template.spec['containers', 'initContainers'].*.envFrom.*.configMapRef.name") + .type(KubernetesArtifactType.ConfigMap) + .build(); + private static final Replacer SECRET_ENV = + builder() + .replacePath( + "$..spec.template.spec['containers', 'initContainers'].*.envFrom.[?( @.secretRef.name == \"{%name%}\" )].secretRef.name") + .findPath( + "$..spec.template.spec['containers', 'initContainers'].*.envFrom.*.secretRef.name") + .type(KubernetesArtifactType.Secret) + .build(); + private static final Replacer HPA_DEPLOYMENT = + builder() + .replacePath( + "$[?( (@.spec.scaleTargetRef.kind == \"Deployment\" || @.spec.scaleTargetRef.kind == \"deployment\") && @.spec.scaleTargetRef.name == \"{%name%}\" )].spec.scaleTargetRef.name") + .findPath( + "$[?( @.spec.scaleTargetRef.kind == \"Deployment\" || @.spec.scaleTargetRef.kind == \"deployment\" )].spec.scaleTargetRef.name") + .type(KubernetesArtifactType.Deployment) + .build(); + private static final Replacer HPA_REPLICA_SET = + builder() + .replacePath( + "$[?( (@.spec.scaleTargetRef.kind == \"ReplicaSet\" || @.spec.scaleTargetRef.kind == \"replicaSet\") && @.spec.scaleTargetRef.name == \"{%name%}\" )].spec.scaleTargetRef.name") + .findPath( + "$[?( @.spec.scaleTargetRef.kind == \"ReplicaSet\" || @.spec.scaleTargetRef.kind == \"replicaSet\" )].spec.scaleTargetRef.name") + .type(KubernetesArtifactType.ReplicaSet) + .build(); + + @Nonnull + public static Replacer dockerImage() { + return DOCKER_IMAGE; + } + + @Nonnull + public static Replacer podDockerImage() { + return POD_DOCKER_IMAGE; + } + + @Nonnull + public static Replacer configMapVolume() { + return CONFIG_MAP_VOLUME; + } + + @Nonnull + public static Replacer secretVolume() { + return SECRET_VOLUME; + } + + @Nonnull + public static Replacer configMapKeyValue() { + return CONFIG_MAP_KEY_VALUE; + } + + @Nonnull + public static Replacer secretKeyValue() { + return SECRET_KEY_VALUE; + } + + @Nonnull + public static Replacer configMapEnv() { + return CONFIG_MAP_ENV; + } + + @Nonnull + public static Replacer secretEnv() { + return SECRET_ENV; + } + + @Nonnull + public static Replacer hpaDeployment() { + return HPA_DEPLOYMENT; + } + + @Nonnull + public static Replacer hpaReplicaSet() { + return HPA_REPLICA_SET; + } +} diff --git a/clouddriver-kubernetes/src/main/groovy/com/netflix/spinnaker/clouddriver/kubernetes/v2/caching/view/model/KubernetesV2ServerGroup.java b/clouddriver-kubernetes/src/main/groovy/com/netflix/spinnaker/clouddriver/kubernetes/v2/caching/view/model/KubernetesV2ServerGroup.java index 2d070d3e978..a2b110e9b31 100644 --- a/clouddriver-kubernetes/src/main/groovy/com/netflix/spinnaker/clouddriver/kubernetes/v2/caching/view/model/KubernetesV2ServerGroup.java +++ b/clouddriver-kubernetes/src/main/groovy/com/netflix/spinnaker/clouddriver/kubernetes/v2/caching/view/model/KubernetesV2ServerGroup.java @@ -20,12 +20,13 @@ import static java.util.Collections.singletonList; import com.fasterxml.jackson.annotation.JsonIgnore; +import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.primitives.Ints; import com.netflix.spinnaker.cats.cache.CacheData; import com.netflix.spinnaker.clouddriver.kubernetes.KubernetesCloudProvider; import com.netflix.spinnaker.clouddriver.kubernetes.v2.artifact.ArtifactReplacer; -import com.netflix.spinnaker.clouddriver.kubernetes.v2.artifact.ArtifactReplacerFactory; +import com.netflix.spinnaker.clouddriver.kubernetes.v2.artifact.Replacer; import com.netflix.spinnaker.clouddriver.kubernetes.v2.caching.Keys; import com.netflix.spinnaker.clouddriver.kubernetes.v2.caching.agent.KubernetesCacheDataConverter; import com.netflix.spinnaker.clouddriver.kubernetes.v2.caching.view.provider.data.KubernetesV2ServerGroupCacheData; @@ -69,12 +70,9 @@ public class KubernetesV2ServerGroup extends ManifestBasedModel implements Serve private KubernetesManifest manifest; private Keys.InfrastructureCacheKey key; - @JsonIgnore private static final ArtifactReplacer dockerImageReplacer; - - static { - dockerImageReplacer = new ArtifactReplacer(); - dockerImageReplacer.addReplacer(ArtifactReplacerFactory.dockerImageReplacer()); - } + @JsonIgnore + private static final ArtifactReplacer dockerImageReplacer = + new ArtifactReplacer(ImmutableList.of(Replacer.dockerImage())); @Override public ServerGroup.InstanceCounts getInstanceCounts() { diff --git a/clouddriver-kubernetes/src/main/groovy/com/netflix/spinnaker/clouddriver/kubernetes/v2/op/handler/KubernetesCronJobHandler.java b/clouddriver-kubernetes/src/main/groovy/com/netflix/spinnaker/clouddriver/kubernetes/v2/op/handler/KubernetesCronJobHandler.java index d67a1199ae3..48886bfe2c0 100644 --- a/clouddriver-kubernetes/src/main/groovy/com/netflix/spinnaker/clouddriver/kubernetes/v2/op/handler/KubernetesCronJobHandler.java +++ b/clouddriver-kubernetes/src/main/groovy/com/netflix/spinnaker/clouddriver/kubernetes/v2/op/handler/KubernetesCronJobHandler.java @@ -19,7 +19,8 @@ import static com.netflix.spinnaker.clouddriver.kubernetes.v2.op.handler.KubernetesHandler.DeployPriority.WORKLOAD_CONTROLLER_PRIORITY; -import com.netflix.spinnaker.clouddriver.kubernetes.v2.artifact.ArtifactReplacerFactory; +import com.google.common.collect.ImmutableList; +import com.netflix.spinnaker.clouddriver.kubernetes.v2.artifact.Replacer; import com.netflix.spinnaker.clouddriver.kubernetes.v2.caching.agent.KubernetesCacheDataConverter; import com.netflix.spinnaker.clouddriver.kubernetes.v2.caching.agent.KubernetesCoreCachingAgent; import com.netflix.spinnaker.clouddriver.kubernetes.v2.caching.agent.KubernetesV2CachingAgentFactory; @@ -36,14 +37,17 @@ public class KubernetesCronJobHandler extends KubernetesHandler implements CanDelete, ServerGroupHandler { - public KubernetesCronJobHandler() { - registerReplacer(ArtifactReplacerFactory.dockerImageReplacer()); - registerReplacer(ArtifactReplacerFactory.configMapVolumeReplacer()); - registerReplacer(ArtifactReplacerFactory.secretVolumeReplacer()); - registerReplacer(ArtifactReplacerFactory.configMapEnvFromReplacer()); - registerReplacer(ArtifactReplacerFactory.secretEnvFromReplacer()); - registerReplacer(ArtifactReplacerFactory.configMapKeyValueFromReplacer()); - registerReplacer(ArtifactReplacerFactory.secretKeyValueFromReplacer()); + @Nonnull + @Override + protected ImmutableList artifactReplacers() { + return ImmutableList.of( + Replacer.dockerImage(), + Replacer.configMapVolume(), + Replacer.secretVolume(), + Replacer.configMapEnv(), + Replacer.secretEnv(), + Replacer.configMapKeyValue(), + Replacer.secretKeyValue()); } @Override diff --git a/clouddriver-kubernetes/src/main/groovy/com/netflix/spinnaker/clouddriver/kubernetes/v2/op/handler/KubernetesDaemonSetHandler.java b/clouddriver-kubernetes/src/main/groovy/com/netflix/spinnaker/clouddriver/kubernetes/v2/op/handler/KubernetesDaemonSetHandler.java index 601dc021311..6b9fad98e9a 100644 --- a/clouddriver-kubernetes/src/main/groovy/com/netflix/spinnaker/clouddriver/kubernetes/v2/op/handler/KubernetesDaemonSetHandler.java +++ b/clouddriver-kubernetes/src/main/groovy/com/netflix/spinnaker/clouddriver/kubernetes/v2/op/handler/KubernetesDaemonSetHandler.java @@ -19,7 +19,8 @@ import static com.netflix.spinnaker.clouddriver.kubernetes.v2.op.handler.KubernetesHandler.DeployPriority.WORKLOAD_CONTROLLER_PRIORITY; -import com.netflix.spinnaker.clouddriver.kubernetes.v2.artifact.ArtifactReplacerFactory; +import com.google.common.collect.ImmutableList; +import com.netflix.spinnaker.clouddriver.kubernetes.v2.artifact.Replacer; import com.netflix.spinnaker.clouddriver.kubernetes.v2.caching.Keys; import com.netflix.spinnaker.clouddriver.kubernetes.v2.caching.agent.KubernetesCacheDataConverter; import com.netflix.spinnaker.clouddriver.kubernetes.v2.caching.agent.KubernetesCoreCachingAgent; @@ -39,14 +40,17 @@ public class KubernetesDaemonSetHandler extends KubernetesHandler implements CanResize, CanPauseRollout, CanResumeRollout, CanUndoRollout, ServerGroupHandler { - public KubernetesDaemonSetHandler() { - registerReplacer(ArtifactReplacerFactory.dockerImageReplacer()); - registerReplacer(ArtifactReplacerFactory.configMapVolumeReplacer()); - registerReplacer(ArtifactReplacerFactory.secretVolumeReplacer()); - registerReplacer(ArtifactReplacerFactory.configMapEnvFromReplacer()); - registerReplacer(ArtifactReplacerFactory.secretEnvFromReplacer()); - registerReplacer(ArtifactReplacerFactory.configMapKeyValueFromReplacer()); - registerReplacer(ArtifactReplacerFactory.secretKeyValueFromReplacer()); + @Nonnull + @Override + protected ImmutableList artifactReplacers() { + return ImmutableList.of( + Replacer.dockerImage(), + Replacer.configMapVolume(), + Replacer.secretVolume(), + Replacer.configMapEnv(), + Replacer.secretEnv(), + Replacer.configMapKeyValue(), + Replacer.secretKeyValue()); } @Override diff --git a/clouddriver-kubernetes/src/main/groovy/com/netflix/spinnaker/clouddriver/kubernetes/v2/op/handler/KubernetesDeploymentHandler.java b/clouddriver-kubernetes/src/main/groovy/com/netflix/spinnaker/clouddriver/kubernetes/v2/op/handler/KubernetesDeploymentHandler.java index 2e4e2e10ada..635f9fc94c7 100644 --- a/clouddriver-kubernetes/src/main/groovy/com/netflix/spinnaker/clouddriver/kubernetes/v2/op/handler/KubernetesDeploymentHandler.java +++ b/clouddriver-kubernetes/src/main/groovy/com/netflix/spinnaker/clouddriver/kubernetes/v2/op/handler/KubernetesDeploymentHandler.java @@ -22,7 +22,8 @@ import static com.netflix.spinnaker.clouddriver.kubernetes.v2.description.manifest.KubernetesApiVersion.EXTENSIONS_V1BETA1; import static com.netflix.spinnaker.clouddriver.kubernetes.v2.op.handler.KubernetesHandler.DeployPriority.WORKLOAD_CONTROLLER_PRIORITY; -import com.netflix.spinnaker.clouddriver.kubernetes.v2.artifact.ArtifactReplacerFactory; +import com.google.common.collect.ImmutableList; +import com.netflix.spinnaker.clouddriver.kubernetes.v2.artifact.Replacer; import com.netflix.spinnaker.clouddriver.kubernetes.v2.caching.agent.KubernetesCacheDataConverter; import com.netflix.spinnaker.clouddriver.kubernetes.v2.caching.agent.KubernetesCoreCachingAgent; import com.netflix.spinnaker.clouddriver.kubernetes.v2.caching.agent.KubernetesV2CachingAgentFactory; @@ -45,14 +46,17 @@ public class KubernetesDeploymentHandler extends KubernetesHandler CanUndoRollout, ServerGroupManagerHandler { - public KubernetesDeploymentHandler() { - registerReplacer(ArtifactReplacerFactory.dockerImageReplacer()); - registerReplacer(ArtifactReplacerFactory.configMapVolumeReplacer()); - registerReplacer(ArtifactReplacerFactory.secretVolumeReplacer()); - registerReplacer(ArtifactReplacerFactory.configMapEnvFromReplacer()); - registerReplacer(ArtifactReplacerFactory.secretEnvFromReplacer()); - registerReplacer(ArtifactReplacerFactory.configMapKeyValueFromReplacer()); - registerReplacer(ArtifactReplacerFactory.secretKeyValueFromReplacer()); + @Nonnull + @Override + protected ImmutableList artifactReplacers() { + return ImmutableList.of( + Replacer.dockerImage(), + Replacer.configMapVolume(), + Replacer.secretVolume(), + Replacer.configMapEnv(), + Replacer.secretEnv(), + Replacer.configMapKeyValue(), + Replacer.secretKeyValue()); } @Override diff --git a/clouddriver-kubernetes/src/main/groovy/com/netflix/spinnaker/clouddriver/kubernetes/v2/op/handler/KubernetesHandler.java b/clouddriver-kubernetes/src/main/groovy/com/netflix/spinnaker/clouddriver/kubernetes/v2/op/handler/KubernetesHandler.java index 07b3d47baa8..cb5eac8f490 100644 --- a/clouddriver-kubernetes/src/main/groovy/com/netflix/spinnaker/clouddriver/kubernetes/v2/op/handler/KubernetesHandler.java +++ b/clouddriver-kubernetes/src/main/groovy/com/netflix/spinnaker/clouddriver/kubernetes/v2/op/handler/KubernetesHandler.java @@ -19,10 +19,13 @@ import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableSet; import com.netflix.spectator.api.Registry; import com.netflix.spinnaker.clouddriver.kubernetes.security.KubernetesNamedAccountCredentials; import com.netflix.spinnaker.clouddriver.kubernetes.v2.artifact.ArtifactReplacer; import com.netflix.spinnaker.clouddriver.kubernetes.v2.artifact.ArtifactReplacer.ReplaceResult; +import com.netflix.spinnaker.clouddriver.kubernetes.v2.artifact.Replacer; import com.netflix.spinnaker.clouddriver.kubernetes.v2.caching.Keys; import com.netflix.spinnaker.clouddriver.kubernetes.v2.caching.agent.KubernetesV2CachingAgent; import com.netflix.spinnaker.clouddriver.kubernetes.v2.caching.agent.KubernetesV2CachingAgentFactory; @@ -44,7 +47,11 @@ public abstract class KubernetesHandler implements CanDeploy, CanDelete, CanPatch { protected static final ObjectMapper objectMapper = new ObjectMapper(); - private final ArtifactReplacer artifactReplacer = new ArtifactReplacer(); + private final ArtifactReplacer artifactReplacer; + + public KubernetesHandler() { + this.artifactReplacer = new ArtifactReplacer(artifactReplacers()); + } public abstract int deployPriority(); @@ -66,8 +73,9 @@ protected List sensitiveKeys() { return new ArrayList<>(); } - protected void registerReplacer(ArtifactReplacer.Replacer replacer) { - artifactReplacer.addReplacer(replacer); + @Nonnull + protected ImmutableList artifactReplacers() { + return ImmutableList.of(); } public ReplaceResult replaceArtifacts( @@ -82,7 +90,7 @@ public ReplaceResult replaceArtifacts( protected abstract KubernetesV2CachingAgentFactory cachingAgentFactory(); - public Set listArtifacts(KubernetesManifest manifest) { + public ImmutableSet listArtifacts(KubernetesManifest manifest) { return artifactReplacer.findAll(manifest); } diff --git a/clouddriver-kubernetes/src/main/groovy/com/netflix/spinnaker/clouddriver/kubernetes/v2/op/handler/KubernetesHorizontalPodAutoscalerHandler.java b/clouddriver-kubernetes/src/main/groovy/com/netflix/spinnaker/clouddriver/kubernetes/v2/op/handler/KubernetesHorizontalPodAutoscalerHandler.java index 8e4ded6c780..26d1b02db0c 100644 --- a/clouddriver-kubernetes/src/main/groovy/com/netflix/spinnaker/clouddriver/kubernetes/v2/op/handler/KubernetesHorizontalPodAutoscalerHandler.java +++ b/clouddriver-kubernetes/src/main/groovy/com/netflix/spinnaker/clouddriver/kubernetes/v2/op/handler/KubernetesHorizontalPodAutoscalerHandler.java @@ -19,7 +19,8 @@ import static com.netflix.spinnaker.clouddriver.kubernetes.v2.op.handler.KubernetesHandler.DeployPriority.WORKLOAD_ATTACHMENT_PRIORITY; -import com.netflix.spinnaker.clouddriver.kubernetes.v2.artifact.ArtifactReplacerFactory; +import com.google.common.collect.ImmutableList; +import com.netflix.spinnaker.clouddriver.kubernetes.v2.artifact.Replacer; import com.netflix.spinnaker.clouddriver.kubernetes.v2.caching.agent.KubernetesCoreCachingAgent; import com.netflix.spinnaker.clouddriver.kubernetes.v2.caching.agent.KubernetesV2CachingAgentFactory; import com.netflix.spinnaker.clouddriver.kubernetes.v2.description.KubernetesSpinnakerKindMap.SpinnakerKind; @@ -31,9 +32,10 @@ @Component public class KubernetesHorizontalPodAutoscalerHandler extends KubernetesHandler { - public KubernetesHorizontalPodAutoscalerHandler() { - registerReplacer(ArtifactReplacerFactory.hpaDeploymentReplacer()); - registerReplacer(ArtifactReplacerFactory.hpaReplicaSetReplacer()); + @Nonnull + @Override + protected ImmutableList artifactReplacers() { + return ImmutableList.of(Replacer.hpaDeployment(), Replacer.hpaReplicaSet()); } @Override diff --git a/clouddriver-kubernetes/src/main/groovy/com/netflix/spinnaker/clouddriver/kubernetes/v2/op/handler/KubernetesJobHandler.java b/clouddriver-kubernetes/src/main/groovy/com/netflix/spinnaker/clouddriver/kubernetes/v2/op/handler/KubernetesJobHandler.java index 61c9e520d62..cb4de721de1 100644 --- a/clouddriver-kubernetes/src/main/groovy/com/netflix/spinnaker/clouddriver/kubernetes/v2/op/handler/KubernetesJobHandler.java +++ b/clouddriver-kubernetes/src/main/groovy/com/netflix/spinnaker/clouddriver/kubernetes/v2/op/handler/KubernetesJobHandler.java @@ -19,7 +19,8 @@ import static com.netflix.spinnaker.clouddriver.kubernetes.v2.op.handler.KubernetesHandler.DeployPriority.WORKLOAD_CONTROLLER_PRIORITY; -import com.netflix.spinnaker.clouddriver.kubernetes.v2.artifact.ArtifactReplacerFactory; +import com.google.common.collect.ImmutableList; +import com.netflix.spinnaker.clouddriver.kubernetes.v2.artifact.Replacer; import com.netflix.spinnaker.clouddriver.kubernetes.v2.caching.agent.KubernetesCacheDataConverter; import com.netflix.spinnaker.clouddriver.kubernetes.v2.caching.agent.KubernetesCoreCachingAgent; import com.netflix.spinnaker.clouddriver.kubernetes.v2.caching.agent.KubernetesV2CachingAgentFactory; @@ -39,15 +40,17 @@ @Component public class KubernetesJobHandler extends KubernetesHandler implements ServerGroupHandler { - - public KubernetesJobHandler() { - registerReplacer(ArtifactReplacerFactory.dockerImageReplacer()); - registerReplacer(ArtifactReplacerFactory.configMapVolumeReplacer()); - registerReplacer(ArtifactReplacerFactory.secretVolumeReplacer()); - registerReplacer(ArtifactReplacerFactory.configMapEnvFromReplacer()); - registerReplacer(ArtifactReplacerFactory.secretEnvFromReplacer()); - registerReplacer(ArtifactReplacerFactory.configMapKeyValueFromReplacer()); - registerReplacer(ArtifactReplacerFactory.secretKeyValueFromReplacer()); + @Nonnull + @Override + protected ImmutableList artifactReplacers() { + return ImmutableList.of( + Replacer.dockerImage(), + Replacer.configMapVolume(), + Replacer.secretVolume(), + Replacer.configMapEnv(), + Replacer.secretEnv(), + Replacer.configMapKeyValue(), + Replacer.secretKeyValue()); } @Override diff --git a/clouddriver-kubernetes/src/main/groovy/com/netflix/spinnaker/clouddriver/kubernetes/v2/op/handler/KubernetesPodHandler.java b/clouddriver-kubernetes/src/main/groovy/com/netflix/spinnaker/clouddriver/kubernetes/v2/op/handler/KubernetesPodHandler.java index 0231dea66a7..ec7827cb794 100644 --- a/clouddriver-kubernetes/src/main/groovy/com/netflix/spinnaker/clouddriver/kubernetes/v2/op/handler/KubernetesPodHandler.java +++ b/clouddriver-kubernetes/src/main/groovy/com/netflix/spinnaker/clouddriver/kubernetes/v2/op/handler/KubernetesPodHandler.java @@ -19,8 +19,8 @@ import static com.netflix.spinnaker.clouddriver.kubernetes.v2.op.handler.KubernetesHandler.DeployPriority.WORKLOAD_PRIORITY; -import com.netflix.spinnaker.clouddriver.artifacts.kubernetes.KubernetesArtifactType; -import com.netflix.spinnaker.clouddriver.kubernetes.v2.artifact.ArtifactReplacer; +import com.google.common.collect.ImmutableList; +import com.netflix.spinnaker.clouddriver.kubernetes.v2.artifact.Replacer; import com.netflix.spinnaker.clouddriver.kubernetes.v2.caching.Keys; import com.netflix.spinnaker.clouddriver.kubernetes.v2.caching.agent.KubernetesCacheDataConverter; import com.netflix.spinnaker.clouddriver.kubernetes.v2.caching.agent.KubernetesCoreCachingAgent; @@ -38,13 +38,10 @@ @Component public class KubernetesPodHandler extends KubernetesHandler { - public KubernetesPodHandler() { - registerReplacer( - ArtifactReplacer.Replacer.builder() - .replacePath("$.spec.containers.[?( @.image == \"{%name%}\" )].image") - .findPath("$.spec.containers.*.image") - .type(KubernetesArtifactType.DockerImage) - .build()); + @Nonnull + @Override + protected ImmutableList artifactReplacers() { + return ImmutableList.of(Replacer.podDockerImage()); } @Override diff --git a/clouddriver-kubernetes/src/main/groovy/com/netflix/spinnaker/clouddriver/kubernetes/v2/op/handler/KubernetesReplicaSetHandler.java b/clouddriver-kubernetes/src/main/groovy/com/netflix/spinnaker/clouddriver/kubernetes/v2/op/handler/KubernetesReplicaSetHandler.java index da0e45abc11..a12265410ae 100644 --- a/clouddriver-kubernetes/src/main/groovy/com/netflix/spinnaker/clouddriver/kubernetes/v2/op/handler/KubernetesReplicaSetHandler.java +++ b/clouddriver-kubernetes/src/main/groovy/com/netflix/spinnaker/clouddriver/kubernetes/v2/op/handler/KubernetesReplicaSetHandler.java @@ -21,7 +21,8 @@ import static com.netflix.spinnaker.clouddriver.kubernetes.v2.description.manifest.KubernetesApiVersion.EXTENSIONS_V1BETA1; import static com.netflix.spinnaker.clouddriver.kubernetes.v2.op.handler.KubernetesHandler.DeployPriority.WORKLOAD_CONTROLLER_PRIORITY; -import com.netflix.spinnaker.clouddriver.kubernetes.v2.artifact.ArtifactReplacerFactory; +import com.google.common.collect.ImmutableList; +import com.netflix.spinnaker.clouddriver.kubernetes.v2.artifact.Replacer; import com.netflix.spinnaker.clouddriver.kubernetes.v2.caching.Keys; import com.netflix.spinnaker.clouddriver.kubernetes.v2.caching.agent.KubernetesCacheDataConverter; import com.netflix.spinnaker.clouddriver.kubernetes.v2.caching.agent.KubernetesCoreCachingAgent; @@ -46,15 +47,17 @@ @Component public class KubernetesReplicaSetHandler extends KubernetesHandler implements CanResize, CanScale, HasPods, ServerGroupHandler { - - public KubernetesReplicaSetHandler() { - registerReplacer(ArtifactReplacerFactory.dockerImageReplacer()); - registerReplacer(ArtifactReplacerFactory.configMapVolumeReplacer()); - registerReplacer(ArtifactReplacerFactory.secretVolumeReplacer()); - registerReplacer(ArtifactReplacerFactory.configMapEnvFromReplacer()); - registerReplacer(ArtifactReplacerFactory.secretEnvFromReplacer()); - registerReplacer(ArtifactReplacerFactory.configMapKeyValueFromReplacer()); - registerReplacer(ArtifactReplacerFactory.secretKeyValueFromReplacer()); + @Nonnull + @Override + protected ImmutableList artifactReplacers() { + return ImmutableList.of( + Replacer.dockerImage(), + Replacer.configMapVolume(), + Replacer.secretVolume(), + Replacer.configMapEnv(), + Replacer.secretEnv(), + Replacer.configMapKeyValue(), + Replacer.secretKeyValue()); } @Override diff --git a/clouddriver-kubernetes/src/main/groovy/com/netflix/spinnaker/clouddriver/kubernetes/v2/op/handler/KubernetesStatefulSetHandler.java b/clouddriver-kubernetes/src/main/groovy/com/netflix/spinnaker/clouddriver/kubernetes/v2/op/handler/KubernetesStatefulSetHandler.java index 84a1ff19117..3fb22893a98 100644 --- a/clouddriver-kubernetes/src/main/groovy/com/netflix/spinnaker/clouddriver/kubernetes/v2/op/handler/KubernetesStatefulSetHandler.java +++ b/clouddriver-kubernetes/src/main/groovy/com/netflix/spinnaker/clouddriver/kubernetes/v2/op/handler/KubernetesStatefulSetHandler.java @@ -21,7 +21,8 @@ import static com.netflix.spinnaker.clouddriver.kubernetes.v2.description.manifest.KubernetesKind.STATEFUL_SET; import static com.netflix.spinnaker.clouddriver.kubernetes.v2.op.handler.KubernetesHandler.DeployPriority.WORKLOAD_CONTROLLER_PRIORITY; -import com.netflix.spinnaker.clouddriver.kubernetes.v2.artifact.ArtifactReplacerFactory; +import com.google.common.collect.ImmutableList; +import com.netflix.spinnaker.clouddriver.kubernetes.v2.artifact.Replacer; import com.netflix.spinnaker.clouddriver.kubernetes.v2.caching.Keys; import com.netflix.spinnaker.clouddriver.kubernetes.v2.caching.agent.KubernetesCacheDataConverter; import com.netflix.spinnaker.clouddriver.kubernetes.v2.caching.agent.KubernetesCoreCachingAgent; @@ -52,15 +53,17 @@ public class KubernetesStatefulSetHandler extends KubernetesHandler CanResumeRollout, CanUndoRollout, ServerGroupHandler { - - public KubernetesStatefulSetHandler() { - registerReplacer(ArtifactReplacerFactory.dockerImageReplacer()); - registerReplacer(ArtifactReplacerFactory.configMapVolumeReplacer()); - registerReplacer(ArtifactReplacerFactory.secretVolumeReplacer()); - registerReplacer(ArtifactReplacerFactory.configMapEnvFromReplacer()); - registerReplacer(ArtifactReplacerFactory.secretEnvFromReplacer()); - registerReplacer(ArtifactReplacerFactory.configMapKeyValueFromReplacer()); - registerReplacer(ArtifactReplacerFactory.secretKeyValueFromReplacer()); + @Nonnull + @Override + protected ImmutableList artifactReplacers() { + return ImmutableList.of( + Replacer.dockerImage(), + Replacer.configMapVolume(), + Replacer.secretVolume(), + Replacer.configMapEnv(), + Replacer.secretEnv(), + Replacer.configMapKeyValue(), + Replacer.secretKeyValue()); } @Override diff --git a/clouddriver-kubernetes/src/test/groovy/com/netflix/spinnaker/clouddriver/kubernetes/v2/artifact/ArtifactReplacerSpec.groovy b/clouddriver-kubernetes/src/test/groovy/com/netflix/spinnaker/clouddriver/kubernetes/v2/artifact/ArtifactReplacerSpec.groovy index b1d797a9f96..0a9ee30d3e9 100644 --- a/clouddriver-kubernetes/src/test/groovy/com/netflix/spinnaker/clouddriver/kubernetes/v2/artifact/ArtifactReplacerSpec.groovy +++ b/clouddriver-kubernetes/src/test/groovy/com/netflix/spinnaker/clouddriver/kubernetes/v2/artifact/ArtifactReplacerSpec.groovy @@ -1,6 +1,7 @@ package com.netflix.spinnaker.clouddriver.kubernetes.v2.artifact import com.fasterxml.jackson.databind.ObjectMapper +import com.google.common.collect.ImmutableList import com.netflix.spinnaker.clouddriver.artifacts.kubernetes.KubernetesArtifactType import com.netflix.spinnaker.clouddriver.kubernetes.v2.description.manifest.KubernetesManifest import com.netflix.spinnaker.kork.artifacts.model.Artifact @@ -33,8 +34,7 @@ spec: kind: Deployment name: $name """ - def artifactReplacer = new ArtifactReplacer() - artifactReplacer.addReplacer(ArtifactReplacerFactory.hpaDeploymentReplacer()) + def artifactReplacer = new ArtifactReplacer(ImmutableList.of(Replacer.hpaDeployment())) def manifest = stringToManifest(hpaManifest) def artifacts = artifactReplacer.findAll(manifest) @@ -60,8 +60,7 @@ spec: kind: UNKNOWN name: $name """ - def artifactReplacer = new ArtifactReplacer() - artifactReplacer.addReplacer(ArtifactReplacerFactory.hpaDeploymentReplacer()) + def artifactReplacer = new ArtifactReplacer(ImmutableList.of(Replacer.hpaDeployment())) def manifest = stringToManifest(hpaManifest) def artifacts = artifactReplacer.findAll(manifest) @@ -71,7 +70,7 @@ spec: @Unroll def "correctly extracts Docker artifacts from image names"() { - expect: + when: def deploymentManifest = """ apiVersion: apps/v1 kind: Deployment @@ -95,11 +94,11 @@ spec: ports: - containerPort: 80 """ - def artifactReplacer = new ArtifactReplacer() - artifactReplacer.addReplacer(ArtifactReplacerFactory.dockerImageReplacer()) + def artifactReplacer = new ArtifactReplacer(ImmutableList.of(Replacer.dockerImage())) def manifest = stringToManifest(deploymentManifest) def artifacts = artifactReplacer.findAll(manifest) + then: artifacts.size() == 1 Artifact artifact = artifacts.toList().get(0) artifact.getType() == KubernetesArtifactType.DockerImage.type @@ -124,7 +123,7 @@ spec: @Unroll def "correctly extracts Docker artifacts from image names in initContainers"() { - expect: + when: def deploymentManifest = """ apiVersion: apps/v1 kind: Deployment @@ -148,11 +147,11 @@ spec: ports: - containerPort: 80 """ - def artifactReplacer = new ArtifactReplacer() - artifactReplacer.addReplacer(ArtifactReplacerFactory.dockerImageReplacer()) + def artifactReplacer = new ArtifactReplacer(ImmutableList.of(Replacer.dockerImage())) def manifest = stringToManifest(deploymentManifest) def artifacts = artifactReplacer.findAll(manifest) + then: artifacts.size() == 1 Artifact artifact = artifacts.toList().get(0) artifact.getType() == KubernetesArtifactType.DockerImage.type