diff --git a/clouddriver-kubernetes/src/main/groovy/com/netflix/spinnaker/clouddriver/kubernetes/config/KubernetesConfigurationProperties.groovy b/clouddriver-kubernetes/src/main/groovy/com/netflix/spinnaker/clouddriver/kubernetes/config/KubernetesConfigurationProperties.groovy index ed5595ca8be..b551ff2f552 100644 --- a/clouddriver-kubernetes/src/main/groovy/com/netflix/spinnaker/clouddriver/kubernetes/config/KubernetesConfigurationProperties.groovy +++ b/clouddriver-kubernetes/src/main/groovy/com/netflix/spinnaker/clouddriver/kubernetes/config/KubernetesConfigurationProperties.groovy @@ -49,6 +49,7 @@ class KubernetesConfigurationProperties { String namingStrategy = "kubernetesAnnotations" Boolean debug = false List customResources; + List cachingPolicies; List kinds List omitKinds } @@ -69,3 +70,9 @@ class CustomKubernetesResource { String deployPriority = "100" boolean versioned = false } + +@ToString(includeNames = true) +class KubernetesCachingPolicy { + String kubernetesKind + int maxEntriesPerAgent +} diff --git a/clouddriver-kubernetes/src/main/groovy/com/netflix/spinnaker/clouddriver/kubernetes/security/KubernetesNamedAccountCredentials.java b/clouddriver-kubernetes/src/main/groovy/com/netflix/spinnaker/clouddriver/kubernetes/security/KubernetesNamedAccountCredentials.java index fcfac11997a..7f69be00cff 100644 --- a/clouddriver-kubernetes/src/main/groovy/com/netflix/spinnaker/clouddriver/kubernetes/security/KubernetesNamedAccountCredentials.java +++ b/clouddriver-kubernetes/src/main/groovy/com/netflix/spinnaker/clouddriver/kubernetes/security/KubernetesNamedAccountCredentials.java @@ -19,6 +19,7 @@ import com.netflix.spectator.api.Registry; import com.netflix.spinnaker.clouddriver.kubernetes.KubernetesCloudProvider; import com.netflix.spinnaker.clouddriver.kubernetes.config.CustomKubernetesResource; +import com.netflix.spinnaker.clouddriver.kubernetes.config.KubernetesCachingPolicy; import com.netflix.spinnaker.clouddriver.kubernetes.config.LinkedDockerRegistryConfiguration; import com.netflix.spinnaker.clouddriver.kubernetes.v1.security.KubernetesV1Credentials; import com.netflix.spinnaker.clouddriver.kubernetes.v2.description.manifest.KubernetesManifest; @@ -200,6 +201,7 @@ static class Builder { KubectlJobExecutor jobExecutor; Namer namer; List customResources; + List cachingPolicies; List kinds; List omitKinds; boolean debug; @@ -347,6 +349,11 @@ Builder namer(Namer namer) { return this; } + Builder cachingPolicies(List cachingPolicies) { + this.cachingPolicies = cachingPolicies; + return this; + } + Builder customResources(List customResources) { this.customResources = customResources; return this; @@ -398,6 +405,7 @@ private C buildCredentials() { .omitNamespaces(omitNamespaces) .registry(spectatorRegistry) .customResources(customResources) + .cachingPolicies(cachingPolicies) .kinds(kinds) .omitKinds(omitKinds) .debug(debug) diff --git a/clouddriver-kubernetes/src/main/groovy/com/netflix/spinnaker/clouddriver/kubernetes/security/KubernetesNamedAccountCredentialsInitializer.groovy b/clouddriver-kubernetes/src/main/groovy/com/netflix/spinnaker/clouddriver/kubernetes/security/KubernetesNamedAccountCredentialsInitializer.groovy index 9c0cdd9aa54..7e05569d89f 100644 --- a/clouddriver-kubernetes/src/main/groovy/com/netflix/spinnaker/clouddriver/kubernetes/security/KubernetesNamedAccountCredentialsInitializer.groovy +++ b/clouddriver-kubernetes/src/main/groovy/com/netflix/spinnaker/clouddriver/kubernetes/security/KubernetesNamedAccountCredentialsInitializer.groovy @@ -103,6 +103,7 @@ class KubernetesNamedAccountCredentialsInitializer implements CredentialsInitial .jobExecutor(jobExecutor) .namer(namerRegistry.getNamingStrategy(managedAccount.namingStrategy)) .customResources(managedAccount.customResources) + .cachingPolicies(managedAccount.cachingPolicies) .kinds(managedAccount.kinds) .omitKinds(managedAccount.omitKinds) .debug(managedAccount.debug) diff --git a/clouddriver-kubernetes/src/main/groovy/com/netflix/spinnaker/clouddriver/kubernetes/v2/caching/agent/KubernetesV2CachingAgent.java b/clouddriver-kubernetes/src/main/groovy/com/netflix/spinnaker/clouddriver/kubernetes/v2/caching/agent/KubernetesV2CachingAgent.java index fe4842487db..9bf67f60617 100644 --- a/clouddriver-kubernetes/src/main/groovy/com/netflix/spinnaker/clouddriver/kubernetes/v2/caching/agent/KubernetesV2CachingAgent.java +++ b/clouddriver-kubernetes/src/main/groovy/com/netflix/spinnaker/clouddriver/kubernetes/v2/caching/agent/KubernetesV2CachingAgent.java @@ -25,6 +25,7 @@ import com.netflix.spinnaker.cats.provider.ProviderCache; import com.netflix.spinnaker.clouddriver.kubernetes.KubernetesCloudProvider; import com.netflix.spinnaker.clouddriver.kubernetes.caching.KubernetesCachingAgent; +import com.netflix.spinnaker.clouddriver.kubernetes.config.KubernetesCachingPolicy; import com.netflix.spinnaker.clouddriver.kubernetes.security.KubernetesNamedAccountCredentials; import com.netflix.spinnaker.clouddriver.kubernetes.v2.description.KubernetesResourcePropertyRegistry; import com.netflix.spinnaker.clouddriver.kubernetes.v2.description.RegistryUtils; @@ -73,7 +74,7 @@ protected List primaryKinds() { } protected Map> loadPrimaryResourceList() { - return namespaces.stream() + Map> result = namespaces.stream() .map(n -> { try { return credentials.list(primaryKinds(), n); @@ -85,6 +86,26 @@ protected Map> loadPrimaryResourceList( .filter(Objects::nonNull) .flatMap(Collection::stream) .collect(Collectors.groupingBy(KubernetesManifest::getKind)); + + for (KubernetesCachingPolicy policy : credentials.getCachingPolicies()) { + KubernetesKind policyKind = KubernetesKind.fromString(policy.getKubernetesKind()); + if (!result.containsKey(policyKind)) { + continue; + } + + List entries = result.get(policyKind); + if (entries == null) { + continue; + } + + if (entries.size() > policy.getMaxEntriesPerAgent()) { + log.warn("{}: Pruning {} entries from kind {}", getAgentType(), entries.size() - policy.getMaxEntriesPerAgent(), policyKind); + entries = entries.subList(0, policy.getMaxEntriesPerAgent()); + result.put(policyKind, entries); + } + } + + return result; } protected KubernetesManifest loadPrimaryResource(KubernetesKind kind, String namespace, String name) { diff --git a/clouddriver-kubernetes/src/main/groovy/com/netflix/spinnaker/clouddriver/kubernetes/v2/description/manifest/KubernetesKind.java b/clouddriver-kubernetes/src/main/groovy/com/netflix/spinnaker/clouddriver/kubernetes/v2/description/manifest/KubernetesKind.java index bb99e923982..7655044a7cc 100644 --- a/clouddriver-kubernetes/src/main/groovy/com/netflix/spinnaker/clouddriver/kubernetes/v2/description/manifest/KubernetesKind.java +++ b/clouddriver-kubernetes/src/main/groovy/com/netflix/spinnaker/clouddriver/kubernetes/v2/description/manifest/KubernetesKind.java @@ -20,6 +20,7 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonValue; import lombok.Getter; +import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import java.util.ArrayList; @@ -28,6 +29,7 @@ import java.util.Optional; import java.util.stream.Collectors; +@Slf4j public class KubernetesKind { public static KubernetesKind CLUSTER_ROLE = new KubernetesKind("clusterRole", false); public static KubernetesKind CLUSTER_ROLE_BINDING = new KubernetesKind("clusterRoleBinding", false); @@ -139,6 +141,7 @@ public static KubernetesKind fromString(String name, boolean registered, boolean // separate from the above chain to avoid concurrent modification of the values list return kindOptional.orElseGet(() -> { + log.info("Dynamically registering {}", name); KubernetesKind result = new KubernetesKind(name); result.isDynamic = true; result.isRegistered = registered; diff --git a/clouddriver-kubernetes/src/main/groovy/com/netflix/spinnaker/clouddriver/kubernetes/v2/security/KubernetesV2Credentials.java b/clouddriver-kubernetes/src/main/groovy/com/netflix/spinnaker/clouddriver/kubernetes/v2/security/KubernetesV2Credentials.java index 4874f2bef1f..f29891ebc55 100644 --- a/clouddriver-kubernetes/src/main/groovy/com/netflix/spinnaker/clouddriver/kubernetes/v2/security/KubernetesV2Credentials.java +++ b/clouddriver-kubernetes/src/main/groovy/com/netflix/spinnaker/clouddriver/kubernetes/v2/security/KubernetesV2Credentials.java @@ -22,6 +22,7 @@ import com.netflix.spectator.api.Clock; import com.netflix.spectator.api.Registry; import com.netflix.spinnaker.clouddriver.kubernetes.config.CustomKubernetesResource; +import com.netflix.spinnaker.clouddriver.kubernetes.config.KubernetesCachingPolicy; import com.netflix.spinnaker.clouddriver.kubernetes.security.KubernetesCredentials; import com.netflix.spinnaker.clouddriver.kubernetes.v2.description.KubernetesPatchOptions; import com.netflix.spinnaker.clouddriver.kubernetes.v2.description.manifest.KubernetesKind; @@ -62,6 +63,7 @@ public class KubernetesV2Credentials implements KubernetesCredentials { private final List kinds; private final List omitKinds; @Getter private final boolean serviceAccount; + @Getter private final List cachingPolicies; // TODO(lwander) make configurable private final static int namespaceExpirySeconds = 30; @@ -154,6 +156,7 @@ public static class Builder { Registry registry; KubectlJobExecutor jobExecutor; List customResources; + List cachingPolicies; List kinds; List omitKinds; boolean debug; @@ -204,6 +207,11 @@ public Builder jobExecutor(KubectlJobExecutor jobExecutor) { return this; } + public Builder cachingPolicies(List cachingPolicies) { + this.cachingPolicies = cachingPolicies; + return this; + } + public Builder customResources(List customResources) { this.customResources = customResources; return this; @@ -245,6 +253,7 @@ public KubernetesV2Credentials build() { customResources = customResources == null ? new ArrayList<>() : customResources; kinds = kinds == null ? new ArrayList<>() : kinds; omitKinds = omitKinds == null ? new ArrayList<>() : omitKinds; + cachingPolicies = cachingPolicies == null ? new ArrayList<>() : cachingPolicies; return new KubernetesV2Credentials( accountName, @@ -259,6 +268,7 @@ public KubernetesV2Credentials build() { oAuthScopes, serviceAccount, customResources, + cachingPolicies, KubernetesKind.registeredStringList(kinds), KubernetesKind.registeredStringList(omitKinds), debug @@ -278,6 +288,7 @@ private KubernetesV2Credentials(@NotNull String accountName, List oAuthScopes, boolean serviceAccount, @NotNull List customResources, + @NotNull List cachingPolicies, @NotNull List kinds, @NotNull List omitKinds, boolean debug) { @@ -295,6 +306,7 @@ private KubernetesV2Credentials(@NotNull String accountName, this.oAuthScopes = oAuthScopes; this.serviceAccount = serviceAccount; this.customResources = customResources; + this.cachingPolicies = cachingPolicies; this.kinds = kinds; this.omitKinds = omitKinds;