diff --git a/clouddriver-kubernetes/src/main/groovy/com/netflix/spinnaker/clouddriver/kubernetes/v2/caching/Keys.java b/clouddriver-kubernetes/src/main/groovy/com/netflix/spinnaker/clouddriver/kubernetes/v2/caching/Keys.java index 5f275ef53bd..402acac17c9 100644 --- a/clouddriver-kubernetes/src/main/groovy/com/netflix/spinnaker/clouddriver/kubernetes/v2/caching/Keys.java +++ b/clouddriver-kubernetes/src/main/groovy/com/netflix/spinnaker/clouddriver/kubernetes/v2/caching/Keys.java @@ -44,18 +44,24 @@ public enum Kind { INFRASTRUCTURE, KUBERNETES_METRIC; + private final String lcName; + + Kind() { + this.lcName = name().toLowerCase(); + } + @Override public String toString() { - return name().toLowerCase(); + return lcName; } @JsonCreator public static Kind fromString(String name) { - return Arrays.stream(values()) - .filter(k -> k.toString().equalsIgnoreCase(name)) - .findFirst() - .orElseThrow( - () -> new IllegalArgumentException("No matching kind with name " + name + " exists")); + try { + return valueOf(name.toUpperCase()); + } catch (IllegalArgumentException e) { + throw new IllegalArgumentException("No matching kind with name " + name + " exists"); + } } } @@ -63,13 +69,19 @@ public enum LogicalKind { APPLICATIONS, CLUSTERS; + private final String lcName; + + LogicalKind() { + this.lcName = name().toLowerCase(); + } + public static boolean isLogicalGroup(String group) { return group.equals(APPLICATIONS.toString()) || group.equals(CLUSTERS.toString()); } @Override public String toString() { - return name().toLowerCase(); + return lcName; } public String singular() { @@ -79,11 +91,11 @@ public String singular() { @JsonCreator public static LogicalKind fromString(String name) { - return Arrays.stream(values()) - .filter(k -> k.toString().equalsIgnoreCase(name)) - .findFirst() - .orElseThrow( - () -> new IllegalArgumentException("No matching kind with name " + name + " exists")); + try { + return valueOf(name.toUpperCase()); + } catch (IllegalArgumentException e) { + throw new IllegalArgumentException("No matching kind with name " + name + " exists"); + } } } @@ -131,8 +143,10 @@ public static Optional parseKey(String key) { return Optional.empty(); } - for (String part : parts) { - part.replaceAll(";", ":"); + for (int i = 0; i < parts.length; i++) { + if (parts[i].contains(";")) { + parts[i] = parts[i].replaceAll(";", ":"); + } } try { diff --git a/clouddriver-kubernetes/src/test/groovy/com/netflix/spinnaker/clouddriver/kubernetes/v2/caching/KeysSpec.groovy b/clouddriver-kubernetes/src/test/groovy/com/netflix/spinnaker/clouddriver/kubernetes/v2/caching/KeysSpec.groovy index 41ac916337a..6a9c1936976 100644 --- a/clouddriver-kubernetes/src/test/groovy/com/netflix/spinnaker/clouddriver/kubernetes/v2/caching/KeysSpec.groovy +++ b/clouddriver-kubernetes/src/test/groovy/com/netflix/spinnaker/clouddriver/kubernetes/v2/caching/KeysSpec.groovy @@ -121,4 +121,74 @@ class KeysSpec extends Specification { KubernetesKind.SERVICE | KubernetesApiVersion.V1 | "account" | "namespace" | "" KubernetesKind.INGRESS | KubernetesApiVersion.EXTENSIONS_V1BETA1 | "ac" | "" | "nameer" } + + def "correctly unpacks resource names containing a ';' character"() { + when: + def key = "kubernetes.v2:infrastructure:clusterRole:k8s::system;controller;resourcequota-controller" + def parsed = Keys.parseKey(key).get() + + then: + parsed instanceof Keys.InfrastructureCacheKey + def parsedInfrastructureKey = (Keys.InfrastructureCacheKey) parsed + parsedInfrastructureKey.kubernetesKind == KubernetesKind.CLUSTER_ROLE + parsedInfrastructureKey.account == "k8s" + parsedInfrastructureKey.namespace == "" + parsedInfrastructureKey.name == "system:controller:resourcequota-controller" + } + + @Unroll + def "Kind fromString returns the correct kind"() { + expect: + result == Keys.Kind.fromString(input) + + where: + input | result + "logical" | Keys.Kind.LOGICAL + "LOGICAL" | Keys.Kind.LOGICAL + "lOgiCAl" | Keys.Kind.LOGICAL + "artifacT" | Keys.Kind.ARTIFACT + "InfraStructurE" | Keys.Kind.INFRASTRUCTURE + "KUBERNETES_METRIC" | Keys.Kind.KUBERNETES_METRIC + "kubernetes_metric" | Keys.Kind.KUBERNETES_METRIC + "kUbernetEs_meTriC" | Keys.Kind.KUBERNETES_METRIC + } + + @Unroll + def "Kind toString correctly serializes the kind to lowercase"() { + expect: + result == input.toString() + + where: + input | result + Keys.Kind.LOGICAL | "logical" + Keys.Kind.ARTIFACT | "artifact" + Keys.Kind.INFRASTRUCTURE | "infrastructure" + Keys.Kind.KUBERNETES_METRIC | "kubernetes_metric" + } + + @Unroll + def "LogicalKind toString returns the correct string"() { + expect: + result == Keys.LogicalKind.fromString(input) + + where: + input | result + "applications" | Keys.LogicalKind.APPLICATIONS + "APPLICATIONS" | Keys.LogicalKind.APPLICATIONS + "appliCatiOns" | Keys.LogicalKind.APPLICATIONS + "clusters" | Keys.LogicalKind.CLUSTERS + "CLUSTERS" | Keys.LogicalKind.CLUSTERS + "clUsTerS" | Keys.LogicalKind.CLUSTERS + } + + @Unroll + def "LogicalKind toString correctly serializes the logical kind to lowercase"() { + expect: + result == input.toString() + + where: + input | result + Keys.LogicalKind.APPLICATIONS | "applications" + Keys.LogicalKind.CLUSTERS | "clusters" + } }