diff --git a/clouddriver-kubernetes/src/main/groovy/com/netflix/spinnaker/clouddriver/kubernetes/api/KubernetesClientApiAdapter.groovy b/clouddriver-kubernetes/src/main/groovy/com/netflix/spinnaker/clouddriver/kubernetes/api/KubernetesClientApiAdapter.groovy index 1345ae3fa1f..6b72051c7a1 100644 --- a/clouddriver-kubernetes/src/main/groovy/com/netflix/spinnaker/clouddriver/kubernetes/api/KubernetesClientApiAdapter.groovy +++ b/clouddriver-kubernetes/src/main/groovy/com/netflix/spinnaker/clouddriver/kubernetes/api/KubernetesClientApiAdapter.groovy @@ -25,6 +25,7 @@ import io.kubernetes.client.ApiException import io.kubernetes.client.Configuration import io.kubernetes.client.apis.AppsV1beta1Api import io.kubernetes.client.models.* +import io.kubernetes.client.apis.ExtensionsV1beta1Api import java.util.concurrent.TimeUnit @@ -40,6 +41,7 @@ class KubernetesClientApiAdapter { final Clock spectatorClock final ApiClient client final AppsV1beta1Api apiInstance + final ExtensionsV1beta1Api extApi public spectatorRegistry() { return spectatorRegistry } @@ -55,6 +57,7 @@ class KubernetesClientApiAdapter { client = config.getApiCient() Configuration.setDefaultApiClient(client) apiInstance = new AppsV1beta1Api(); + extApi = new ExtensionsV1beta1Api() } KubernetesOperationException formatException(String operation, String namespace, ApiException e) { @@ -134,4 +137,11 @@ class KubernetesClientApiAdapter { return statefulSets } } + + List getDaemonSets(String namespace) { + exceptionWrapper("daemonSets.list", "Get Daemon Sets", namespace) { + V1beta1DaemonSetList list = extApi.listNamespacedDaemonSet(namespace, null, null, null, null, API_CALL_TIMEOUT_SECONDS, null) + return list.items + } + } } diff --git a/clouddriver-kubernetes/src/main/groovy/com/netflix/spinnaker/clouddriver/kubernetes/api/KubernetesClientApiConverter.groovy b/clouddriver-kubernetes/src/main/groovy/com/netflix/spinnaker/clouddriver/kubernetes/api/KubernetesClientApiConverter.groovy index 6cd5c190084..c55c87d9d40 100644 --- a/clouddriver-kubernetes/src/main/groovy/com/netflix/spinnaker/clouddriver/kubernetes/api/KubernetesClientApiConverter.groovy +++ b/clouddriver-kubernetes/src/main/groovy/com/netflix/spinnaker/clouddriver/kubernetes/api/KubernetesClientApiConverter.groovy @@ -44,7 +44,7 @@ import com.netflix.spinnaker.clouddriver.kubernetes.deploy.description.servergro import com.netflix.spinnaker.clouddriver.kubernetes.deploy.description.servergroup.KubernetesHttpGetAction import com.netflix.spinnaker.clouddriver.kubernetes.deploy.description.servergroup.KeyValuePair import com.netflix.spinnaker.clouddriver.kubernetes.deploy.description.servergroup.KubernetesTcpSocketAction -import io.fabric8.kubernetes.api.model.KeyToPath +import io.kubernetes.client.models.V1KeyToPath import io.kubernetes.client.models.V1Container import io.kubernetes.client.models.V1Probe import io.kubernetes.client.models.V1Volume @@ -209,8 +209,8 @@ class KubernetesClientApiConverter { res.secret = new KubernetesSecretVolumeSource(secretName: volume.secret.secretName) } else if (volume.configMap) { res.type = KubernetesVolumeSourceType.ConfigMap - def items = volume.configMap.items?.collect { KeyToPath item -> - new KubernetesKeyToPath(key: item.key, path: item.path) + def items = volume.configMap.items?.collect { V1KeyToPath item -> + new KubernetesKeyToPath(key: item.key, path: item.path) } res.configMap = new KubernetesConfigMapVolumeSource(configMapName: volume.configMap.name, items: items) } else if (volume.awsElasticBlockStore) { diff --git a/clouddriver-kubernetes/src/main/groovy/com/netflix/spinnaker/clouddriver/kubernetes/model/KubernetesServerGroup.groovy b/clouddriver-kubernetes/src/main/groovy/com/netflix/spinnaker/clouddriver/kubernetes/model/KubernetesServerGroup.groovy index 0f9b5811bc7..f4f655ea66a 100644 --- a/clouddriver-kubernetes/src/main/groovy/com/netflix/spinnaker/clouddriver/kubernetes/model/KubernetesServerGroup.groovy +++ b/clouddriver-kubernetes/src/main/groovy/com/netflix/spinnaker/clouddriver/kubernetes/model/KubernetesServerGroup.groovy @@ -33,6 +33,7 @@ import io.fabric8.kubernetes.api.model.ReplicationController import io.fabric8.kubernetes.api.model.HorizontalPodAutoscaler import io.fabric8.kubernetes.api.model.extensions.ReplicaSet import io.fabric8.kubernetes.client.internal.SerializationUtils +import io.kubernetes.client.models.V1beta1DaemonSet import io.kubernetes.client.models.V1beta1StatefulSet @CompileStatic @@ -126,6 +127,31 @@ class KubernetesServerGroup implements ServerGroup, Serializable { } } + KubernetesServerGroup(V1beta1DaemonSet daemonSet, String account, List events) { + this.name = daemonSet.metadata?.name + this.account = account + this.region = daemonSet.metadata?.namespace + this.namespace = this.region + this.createdTime = daemonSet.metadata?.creationTimestamp?.getMillis() + this.zones = [this.region] as Set + this.securityGroups = [] + /** + * DaemonSetsSpec doesnot support Replicas : Checked the Documentation (V1beta1DaemonSetSpec.md) + * Thats why i think its not required + */ + //this.replicas = daemonSet.spec?.replicas ?: 0 + this.launchConfig = [:] + this.labels = daemonSet.spec?.template?.metadata?.labels + /** + * Will fetch this valu in next Pull Request + */ + //this.deployDescription = KubernetesClientApiConverter.fromStatefulSet(daemonSet) + this.kind = daemonSet.kind + this.events = events?.collect { + new KubernetesEvent(it) + } + } + KubernetesServerGroup(ReplicaSet replicaSet, String account, List events, HorizontalPodAutoscaler autoscaler) { this.name = replicaSet.metadata?.name this.account = account diff --git a/clouddriver-kubernetes/src/main/groovy/com/netflix/spinnaker/clouddriver/kubernetes/provider/agent/KubernetesControllersCachingAgent.groovy b/clouddriver-kubernetes/src/main/groovy/com/netflix/spinnaker/clouddriver/kubernetes/provider/agent/KubernetesControllersCachingAgent.groovy index 91004038dca..b3061135a55 100644 --- a/clouddriver-kubernetes/src/main/groovy/com/netflix/spinnaker/clouddriver/kubernetes/provider/agent/KubernetesControllersCachingAgent.groovy +++ b/clouddriver-kubernetes/src/main/groovy/com/netflix/spinnaker/clouddriver/kubernetes/provider/agent/KubernetesControllersCachingAgent.groovy @@ -35,6 +35,7 @@ import com.netflix.spinnaker.clouddriver.kubernetes.cache.Keys import com.netflix.spectator.api.Registry import groovy.util.logging.Slf4j import io.fabric8.kubernetes.api.model.Event +import io.kubernetes.client.models.V1beta1DaemonSet import io.kubernetes.client.models.V1beta1StatefulSet /** @@ -89,8 +90,12 @@ class KubernetesControllersCachingAgent extends KubernetesCachingAgent implement loadStatefulSets() } + V1beta1DaemonSet daemonSet = metricsSupport.readData { + loadDaemonSets() + } + CacheResult result = metricsSupport.transformData { - buildCacheResult([new StateFulSet(statefulSet: statefulSet)], [:], [], Long.MAX_VALUE) + buildCacheResult([new KubernetesController(controller: statefulSet,controller1: daemonSet)], [:], [], Long.MAX_VALUE) } def jsonResult = objectMapper.writeValueAsString(result.cacheResults) @@ -115,7 +120,7 @@ class KubernetesControllersCachingAgent extends KubernetesCachingAgent implement } } // Evict this server group if it no longer exists. - Map> evictions = statefulSet ? [:] : [ + Map> evictions = statefulSet | daemonSet ? [:] : [ (Keys.Namespace.SERVER_GROUPS.ns): [ Keys.getServerGroupKey(accountName, namespace, serverGroupName) ] @@ -154,8 +159,11 @@ class KubernetesControllersCachingAgent extends KubernetesCachingAgent implement reloadNamespaces() Long start = System.currentTimeMillis() List statefulSet = loadStatefulSets() - List serverGroups = (statefulSet.collect { - it ? new StateFulSet(statefulSet: it) : null + List daemonSet = loadDaemonSets() + List serverGroups = (statefulSet.collect { + it ? new KubernetesController(controller: it) : null + }+ daemonSet.collect { + it ? new KubernetesController(controller: it) : null } ) - null List evictFromOnDemand = [] @@ -198,7 +206,13 @@ class KubernetesControllersCachingAgent extends KubernetesCachingAgent implement }.flatten() } - private CacheResult buildCacheResult(List serverGroups, Map onDemandKeep, List onDemandEvict, Long start) { + List loadDaemonSets() { + namespaces.collect { String namespace -> + credentials.apiClientAdaptor.getDaemonSets(namespace) + }.flatten() + } + + private CacheResult buildCacheResult(List serverGroups, Map onDemandKeep, List onDemandEvict, Long start) { log.info("Describing items in ${agentType}") Map cachedApplications = MutableCacheData.mutableCacheMap() @@ -208,17 +222,19 @@ class KubernetesControllersCachingAgent extends KubernetesCachingAgent implement Map cachedLoadBalancers = MutableCacheData.mutableCacheMap() Map> stateFulsetEvents = [:].withDefault { _ -> [:] } + Map> daemonsetEvents = [:].withDefault { _ -> [:] } try { namespaces.each { String namespace -> stateFulsetEvents[namespace] = credentials.apiAdaptor.getEvents(namespace, "V1beta1StatefulSet") + daemonsetEvents[namespace] = credentials.apiAdaptor.getEvents(namespace, "V1beta1DaemonSet") } } catch (Exception e) { log.warn "Failure fetching events for all server groups in $namespaces", e } - for (StateFulSet serverGroup: serverGroups) { + for (KubernetesController serverGroup: serverGroups) { if (!serverGroup.exists()) { continue } @@ -262,11 +278,12 @@ class KubernetesControllersCachingAgent extends KubernetesCachingAgent implement def events = null attributes.name = serverGroupName - if (serverGroup.statefulSet) { - + if (serverGroup.controller instanceof V1beta1StatefulSet) { events = stateFulsetEvents[serverGroup.namespace][serverGroupName] + } else if (serverGroup.controller instanceof V1beta1DaemonSet) { + events = daemonsetEvents[serverGroup.namespace][serverGroupName] } - attributes.serverGroup = new KubernetesServerGroup(serverGroup.statefulSet, accountName, events) + attributes.serverGroup = new KubernetesServerGroup(serverGroup.controller, accountName, events) relationships[Keys.Namespace.APPLICATIONS.ns].add(applicationKey) relationships[Keys.Namespace.CLUSTERS.ns].add(clusterKey) relationships[Keys.Namespace.INSTANCES.ns].addAll(instanceKeys) @@ -307,28 +324,27 @@ class KubernetesControllersCachingAgent extends KubernetesCachingAgent implement } } - class StateFulSet{ + class KubernetesController{ - V1beta1StatefulSet statefulSet + def controller String getName() { - statefulSet.metadata.name + controller.metadata.name } String getNamespace() { - statefulSet.metadata.namespace + controller.metadata.namespace } Map getSelector() { - statefulSet.spec.selector.matchLabels + controller.spec.selector.matchLabels } boolean exists() { - statefulSet + controller } List getLoadBalancers() { - KubernetesUtil.getLoadBalancers(statefulSet.spec?.template?.metadata?.labels ?: [:]) + KubernetesUtil.getLoadBalancers(controller.spec?.template?.metadata?.labels ?: [:]) } - } }