From 6bc751d1a2cac86bfed73dcc32add5a5d70055e2 Mon Sep 17 00:00:00 2001 From: Soumya Date: Mon, 11 Aug 2025 03:48:47 +0530 Subject: [PATCH 01/11] Add kube_deployment_spec_topology_spread_constraints metric - Adds new metric to count topology spread constraints in deployment pod templates - Includes comprehensive test coverage for both cases (with/without constraints) - Follows existing patterns and stability guidelines --- internal/store/deployment.go | 1053 +++++++++++++++++++---------- internal/store/deployment_test.go | 415 +++++++++++- 2 files changed, 1114 insertions(+), 354 deletions(-) diff --git a/internal/store/deployment.go b/internal/store/deployment.go index 31e3b200a6..c710a35a3a 100644 --- a/internal/store/deployment.go +++ b/internal/store/deployment.go @@ -1,44 +1,69 @@ /* + Copyright 2016 The Kubernetes Authors All rights reserved. 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 +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 store import ( - "context" - basemetrics "k8s.io/component-base/metrics" +"context" + +"strconv" + +basemetrics "k8s.io/component-base/metrics" + +"k8s.io/kube-state-metrics/v2/pkg/metric" + +generator "k8s.io/kube-state-metrics/v2/pkg/metric_generator" - "k8s.io/kube-state-metrics/v2/pkg/metric" - generator "k8s.io/kube-state-metrics/v2/pkg/metric_generator" +v1 "k8s.io/api/apps/v1" + +metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + +"k8s.io/apimachinery/pkg/runtime" + +"k8s.io/apimachinery/pkg/util/intstr" + +"k8s.io/apimachinery/pkg/watch" + +clientset "k8s.io/client-go/kubernetes" + +"k8s.io/client-go/tools/cache" - v1 "k8s.io/api/apps/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/util/intstr" - "k8s.io/apimachinery/pkg/watch" - clientset "k8s.io/client-go/kubernetes" - "k8s.io/client-go/tools/cache" ) var ( - descDeploymentAnnotationsName = "kube_deployment_annotations" - descDeploymentAnnotationsHelp = "Kubernetes annotations converted to Prometheus labels." - descDeploymentLabelsName = "kube_deployment_labels" - descDeploymentLabelsHelp = "Kubernetes labels converted to Prometheus labels." - descDeploymentLabelsDefaultLabels = []string{"namespace", "deployment"} + +descDeploymentAnnotationsName = "kube_deployment_annotations" + +descDeploymentAnnotationsHelp = "Kubernetes annotations converted to Prometheus labels." + +descDeploymentLabelsName = "kube_deployment_labels" + +descDeploymentLabelsHelp = "Kubernetes labels converted to Prometheus labels." + +descDeploymentLabelsDefaultLabels = []string{"namespace", "deployment"} + ) // Reasons copied from kubernetes/pkg/controller/deployment/deployment_utils.go. @@ -59,342 +84,680 @@ var ( ) func deploymentMetricFamilies(allowAnnotationsList, allowLabelsList []string) []generator.FamilyGenerator { - return []generator.FamilyGenerator{ - *generator.NewFamilyGeneratorWithStability( - "kube_deployment_created", - "Unix creation timestamp", - metric.Gauge, - basemetrics.STABLE, - "", - wrapDeploymentFunc(func(d *v1.Deployment) *metric.Family { - ms := []*metric.Metric{} - - if !d.CreationTimestamp.IsZero() { - ms = append(ms, &metric.Metric{ - Value: float64(d.CreationTimestamp.Unix()), - }) - } - - return &metric.Family{ - Metrics: ms, - } - }), - ), - *generator.NewFamilyGeneratorWithStability( - "kube_deployment_status_replicas", - "The number of replicas per deployment.", - metric.Gauge, - basemetrics.STABLE, - "", - wrapDeploymentFunc(func(d *v1.Deployment) *metric.Family { - return &metric.Family{ - Metrics: []*metric.Metric{ - { - Value: float64(d.Status.Replicas), - }, - }, - } - }), - ), - *generator.NewFamilyGeneratorWithStability( - "kube_deployment_status_replicas_ready", - "The number of ready replicas per deployment.", - metric.Gauge, - basemetrics.STABLE, - "", - wrapDeploymentFunc(func(d *v1.Deployment) *metric.Family { - return &metric.Family{ - Metrics: []*metric.Metric{ - { - Value: float64(d.Status.ReadyReplicas), - }, - }, - } - }), - ), - *generator.NewFamilyGeneratorWithStability( - "kube_deployment_status_replicas_available", - "The number of available replicas per deployment.", - metric.Gauge, - basemetrics.STABLE, - "", - wrapDeploymentFunc(func(d *v1.Deployment) *metric.Family { - return &metric.Family{ - Metrics: []*metric.Metric{ - { - Value: float64(d.Status.AvailableReplicas), - }, - }, - } - }), - ), - *generator.NewFamilyGeneratorWithStability( - "kube_deployment_status_replicas_unavailable", - "The number of unavailable replicas per deployment.", - metric.Gauge, - basemetrics.STABLE, - "", - wrapDeploymentFunc(func(d *v1.Deployment) *metric.Family { - return &metric.Family{ - Metrics: []*metric.Metric{ - { - Value: float64(d.Status.UnavailableReplicas), - }, - }, - } - }), - ), - *generator.NewFamilyGeneratorWithStability( - "kube_deployment_status_replicas_updated", - "The number of updated replicas per deployment.", - metric.Gauge, - basemetrics.STABLE, - "", - wrapDeploymentFunc(func(d *v1.Deployment) *metric.Family { - return &metric.Family{ - Metrics: []*metric.Metric{ - { - Value: float64(d.Status.UpdatedReplicas), - }, - }, - } - }), - ), - *generator.NewFamilyGeneratorWithStability( - "kube_deployment_status_observed_generation", - "The generation observed by the deployment controller.", - metric.Gauge, - basemetrics.STABLE, - "", - wrapDeploymentFunc(func(d *v1.Deployment) *metric.Family { - return &metric.Family{ - Metrics: []*metric.Metric{ - { - Value: float64(d.Status.ObservedGeneration), - }, - }, - } - }), - ), - *generator.NewFamilyGeneratorWithStability( - "kube_deployment_status_condition", - "The current status conditions of a deployment.", - metric.Gauge, - basemetrics.STABLE, - "", - wrapDeploymentFunc(func(d *v1.Deployment) *metric.Family { - ms := make([]*metric.Metric, len(d.Status.Conditions)*len(conditionStatuses)) - - for i, c := range d.Status.Conditions { - conditionMetrics := addConditionMetrics(c.Status) - - for j, m := range conditionMetrics { - metric := m - - reason := c.Reason - if _, ok := allowedDeploymentReasons[reason]; !ok { - reason = "unknown" - } - - metric.LabelKeys = []string{"reason", "condition", "status"} - metric.LabelValues = append([]string{reason, string(c.Type)}, metric.LabelValues...) - ms[i*len(conditionStatuses)+j] = metric - } - } - - return &metric.Family{ - Metrics: ms, - } - }), - ), - *generator.NewFamilyGeneratorWithStability( - "kube_deployment_spec_replicas", - "Number of desired pods for a deployment.", - metric.Gauge, - basemetrics.STABLE, - "", - wrapDeploymentFunc(func(d *v1.Deployment) *metric.Family { - return &metric.Family{ - Metrics: []*metric.Metric{ - { - Value: float64(*d.Spec.Replicas), - }, - }, - } - }), - ), - *generator.NewFamilyGeneratorWithStability( - "kube_deployment_spec_paused", - "Whether the deployment is paused and will not be processed by the deployment controller.", - metric.Gauge, - basemetrics.STABLE, - "", - wrapDeploymentFunc(func(d *v1.Deployment) *metric.Family { - return &metric.Family{ - Metrics: []*metric.Metric{ - { - Value: boolFloat64(d.Spec.Paused), - }, - }, - } - }), - ), - *generator.NewFamilyGeneratorWithStability( - "kube_deployment_spec_strategy_rollingupdate_max_unavailable", - "Maximum number of unavailable replicas during a rolling update of a deployment.", - metric.Gauge, - basemetrics.STABLE, - "", - wrapDeploymentFunc(func(d *v1.Deployment) *metric.Family { - if d.Spec.Strategy.RollingUpdate == nil { - return &metric.Family{} - } - - maxUnavailable, err := intstr.GetScaledValueFromIntOrPercent(d.Spec.Strategy.RollingUpdate.MaxUnavailable, int(*d.Spec.Replicas), false) - if err != nil { - panic(err) - } - - return &metric.Family{ - Metrics: []*metric.Metric{ - { - Value: float64(maxUnavailable), - }, - }, - } - }), - ), - *generator.NewFamilyGeneratorWithStability( - "kube_deployment_spec_strategy_rollingupdate_max_surge", - "Maximum number of replicas that can be scheduled above the desired number of replicas during a rolling update of a deployment.", - metric.Gauge, - basemetrics.STABLE, - "", - wrapDeploymentFunc(func(d *v1.Deployment) *metric.Family { - if d.Spec.Strategy.RollingUpdate == nil { - return &metric.Family{} - } - - maxSurge, err := intstr.GetScaledValueFromIntOrPercent(d.Spec.Strategy.RollingUpdate.MaxSurge, int(*d.Spec.Replicas), true) - if err != nil { - panic(err) - } - - return &metric.Family{ - Metrics: []*metric.Metric{ - { - Value: float64(maxSurge), - }, - }, - } - }), - ), - *generator.NewFamilyGeneratorWithStability( - "kube_deployment_metadata_generation", - "Sequence number representing a specific generation of the desired state.", - metric.Gauge, - basemetrics.STABLE, - "", - wrapDeploymentFunc(func(d *v1.Deployment) *metric.Family { - return &metric.Family{ - Metrics: []*metric.Metric{ - { - Value: float64(d.Generation), - }, - }, - } - }), - ), - *generator.NewFamilyGeneratorWithStability( - "kube_deployment_deletion_timestamp", - "Unix deletion timestamp", - metric.Gauge, - basemetrics.ALPHA, - "", - wrapDeploymentFunc(func(d *v1.Deployment) *metric.Family { - ms := []*metric.Metric{} - - if !d.DeletionTimestamp.IsZero() { - ms = append(ms, &metric.Metric{ - Value: float64(d.DeletionTimestamp.Unix()), - }) - } - - return &metric.Family{ - Metrics: ms, - } - }), - ), - *generator.NewFamilyGeneratorWithStability( - descDeploymentAnnotationsName, - descDeploymentAnnotationsHelp, - metric.Gauge, - basemetrics.ALPHA, - "", - wrapDeploymentFunc(func(d *v1.Deployment) *metric.Family { - if len(allowAnnotationsList) == 0 { - return &metric.Family{} - } - annotationKeys, annotationValues := createPrometheusLabelKeysValues("annotation", d.Annotations, allowAnnotationsList) - return &metric.Family{ - Metrics: []*metric.Metric{ - { - LabelKeys: annotationKeys, - LabelValues: annotationValues, - Value: 1, - }, - }, - } - }), - ), - *generator.NewFamilyGeneratorWithStability( - descDeploymentLabelsName, - descDeploymentLabelsHelp, - metric.Gauge, - basemetrics.STABLE, - "", - wrapDeploymentFunc(func(d *v1.Deployment) *metric.Family { - if len(allowLabelsList) == 0 { - return &metric.Family{} - } - labelKeys, labelValues := createPrometheusLabelKeysValues("label", d.Labels, allowLabelsList) - return &metric.Family{ - Metrics: []*metric.Metric{ - { - LabelKeys: labelKeys, - LabelValues: labelValues, - Value: 1, - }, - }, - } - }), - ), + +return []generator.FamilyGenerator{ + +*generator.NewFamilyGeneratorWithStability( + +"kube_deployment_created", + +"Unix creation timestamp", + +metric.Gauge, + +basemetrics.STABLE, + +"", + +wrapDeploymentFunc(func(d *v1.Deployment) *metric.Family { + +ms := []*metric.Metric{} + +if !d.CreationTimestamp.IsZero() { + +ms = append(ms, &metric.Metric{ + +Value: float64(d.CreationTimestamp.Unix()), + +}) + +} + +return &metric.Family{ + +Metrics: ms, + +} + +}), + +), + +*generator.NewFamilyGeneratorWithStability( + +"kube_deployment_status_replicas", + +"The number of replicas per deployment.", + +metric.Gauge, + +basemetrics.STABLE, + +"", + +wrapDeploymentFunc(func(d *v1.Deployment) *metric.Family { + +return &metric.Family{ + +Metrics: []*metric.Metric{ + +{ + +Value: float64(d.Status.Replicas), + +}, + +}, + +} + +}), + +), + +*generator.NewFamilyGeneratorWithStability( + +"kube_deployment_status_replicas_ready", + +"The number of ready replicas per deployment.", + +metric.Gauge, + +basemetrics.STABLE, + +"", + +wrapDeploymentFunc(func(d *v1.Deployment) *metric.Family { + +return &metric.Family{ + +Metrics: []*metric.Metric{ + +{ + +Value: float64(d.Status.ReadyReplicas), + +}, + +}, + +} + +}), + +), + +*generator.NewFamilyGeneratorWithStability( + +"kube_deployment_status_replicas_available", + +"The number of available replicas per deployment.", + +metric.Gauge, + +basemetrics.STABLE, + +"", + +wrapDeploymentFunc(func(d *v1.Deployment) *metric.Family { + +return &metric.Family{ + +Metrics: []*metric.Metric{ + +{ + +Value: float64(d.Status.AvailableReplicas), + +}, + +}, + +} + +}), + +), + +*generator.NewFamilyGeneratorWithStability( + +"kube_deployment_status_replicas_unavailable", + +"The number of unavailable replicas per deployment.", + +metric.Gauge, + +basemetrics.STABLE, + +"", + +wrapDeploymentFunc(func(d *v1.Deployment) *metric.Family { + +return &metric.Family{ + +Metrics: []*metric.Metric{ + +{ + +Value: float64(d.Status.UnavailableReplicas), + +}, + +}, + +} + +}), + +), + +*generator.NewFamilyGeneratorWithStability( + +"kube_deployment_status_replicas_updated", + +"The number of updated replicas per deployment.", + +metric.Gauge, + +basemetrics.STABLE, + +"", + +wrapDeploymentFunc(func(d *v1.Deployment) *metric.Family { + +return &metric.Family{ + +Metrics: []*metric.Metric{ + +{ + +Value: float64(d.Status.UpdatedReplicas), + +}, + +}, + +} + +}), + +), + +*generator.NewFamilyGeneratorWithStability( + +"kube_deployment_status_observed_generation", + +"The generation observed by the deployment controller.", + +metric.Gauge, + +basemetrics.STABLE, + +"", + +wrapDeploymentFunc(func(d *v1.Deployment) *metric.Family { + +return &metric.Family{ + +Metrics: []*metric.Metric{ + +{ + +Value: float64(d.Status.ObservedGeneration), + +}, + +}, + +} + +}), + +), + +*generator.NewFamilyGeneratorWithStability( + +"kube_deployment_status_condition", + +"The current status conditions of a deployment.", + +metric.Gauge, + +basemetrics.STABLE, + +"", + +wrapDeploymentFunc(func(d *v1.Deployment) *metric.Family { + +ms := make([]*metric.Metric, len(d.Status.Conditions)*len(conditionStatuses)) + +for i, c := range d.Status.Conditions { + +conditionMetrics := addConditionMetrics(c.Status) + +for j, m := range conditionMetrics { + +metric := m + +reason := c.Reason +if _, ok := allowedDeploymentReasons[reason]; !ok { + reason = "unknown" +} + +metric.LabelKeys = []string{"reason", "condition", "status"} + +metric.LabelValues = append([]string{reason, string(c.Type)}, metric.LabelValues...) + +ms[i*len(conditionStatuses)+j] = metric + +} + +} + +return &metric.Family{ + +Metrics: ms, + +} + +}), + +), + +*generator.NewFamilyGeneratorWithStability( + +"kube_deployment_spec_replicas", + +"Number of desired pods for a deployment.", + +metric.Gauge, + +basemetrics.STABLE, + +"", + +wrapDeploymentFunc(func(d *v1.Deployment) *metric.Family { + +return &metric.Family{ + +Metrics: []*metric.Metric{ + +{ + +Value: float64(*d.Spec.Replicas), + +}, + +}, + +} + +}), + +), + +*generator.NewFamilyGeneratorWithStability( + +"kube_deployment_spec_paused", + +"Whether the deployment is paused and will not be processed by the deployment controller.", + +metric.Gauge, + +basemetrics.STABLE, + +"", + +wrapDeploymentFunc(func(d *v1.Deployment) *metric.Family { + +return &metric.Family{ + +Metrics: []*metric.Metric{ + +{ + +Value: boolFloat64(d.Spec.Paused), + +}, + +}, + +} + +}), + +), + +*generator.NewFamilyGeneratorWithStability( + +"kube_deployment_spec_strategy_rollingupdate_max_unavailable", + +"Maximum number of unavailable replicas during a rolling update of a deployment.", + +metric.Gauge, + +basemetrics.STABLE, + +"", + +wrapDeploymentFunc(func(d *v1.Deployment) *metric.Family { + +if d.Spec.Strategy.RollingUpdate == nil { + +return &metric.Family{} + +} + +maxUnavailable, err := intstr.GetScaledValueFromIntOrPercent(d.Spec.Strategy.RollingUpdate.MaxUnavailable, int(*d.Spec.Replicas), false) + +if err != nil { + +panic(err) + +} + +return &metric.Family{ + +Metrics: []*metric.Metric{ + +{ + +Value: float64(maxUnavailable), + +}, + +}, + +} + +}), + +), + +*generator.NewFamilyGeneratorWithStability( + +"kube_deployment_spec_strategy_rollingupdate_max_surge", + +"Maximum number of replicas that can be scheduled above the desired number of replicas during a rolling update of a deployment.", + +metric.Gauge, + +basemetrics.STABLE, + +"", + +wrapDeploymentFunc(func(d *v1.Deployment) *metric.Family { + +if d.Spec.Strategy.RollingUpdate == nil { + +return &metric.Family{} + +} + +maxSurge, err := intstr.GetScaledValueFromIntOrPercent(d.Spec.Strategy.RollingUpdate.MaxSurge, int(*d.Spec.Replicas), true) + +if err != nil { + +panic(err) + +} + +return &metric.Family{ + +Metrics: []*metric.Metric{ + +{ + +Value: float64(maxSurge), + +}, + +}, + +} + +}), + +), + +*generator.NewFamilyGeneratorWithStability( + +"kube_deployment_spec_topology_spread_constraint", + +"Explicit details of each topology spread constraint in the deployment's pod template.", + +metric.Gauge, + +basemetrics.ALPHA, + +"", + +wrapDeploymentFunc(func(d *v1.Deployment) *metric.Family { + +ms := []*metric.Metric{} +for _, constraint := range d.Spec.Template.Spec.TopologySpreadConstraints { + labelSelectorStr, err := metav1.LabelSelectorAsSelector(constraint.LabelSelector) + if err != nil { + // Skip invalid label selectors + continue + } + minDomainsStr := "1" + if constraint.MinDomains != nil { + minDomainsStr = strconv.Itoa(int(*constraint.MinDomains)) } + ms = append(ms, &metric.Metric{ + LabelKeys: []string{"topology_key", "max_skew", "when_unsatisfiable", "min_domains", "label_selector"}, + LabelValues: []string{constraint.TopologyKey, strconv.Itoa(int(constraint.MaxSkew)), string(constraint.WhenUnsatisfiable), minDomainsStr, labelSelectorStr.String()}, + Value: 1, + }) +} +return &metric.Family{Metrics: ms} + +}), + +), + +*generator.NewFamilyGeneratorWithStability( + +"kube_deployment_metadata_generation", + +"Sequence number representing a specific generation of the desired state.", + +metric.Gauge, + +basemetrics.STABLE, + +"", + +wrapDeploymentFunc(func(d *v1.Deployment) *metric.Family { + +return &metric.Family{ + +Metrics: []*metric.Metric{ + +{ + +Value: float64(d.Generation), + +}, + +}, + +} + +}), + +), + +*generator.NewFamilyGeneratorWithStability( + +"kube_deployment_deletion_timestamp", + +"Unix deletion timestamp", + +metric.Gauge, + +basemetrics.ALPHA, + +"", + +wrapDeploymentFunc(func(d *v1.Deployment) *metric.Family { + +ms := []*metric.Metric{} + +if !d.DeletionTimestamp.IsZero() { + +ms = append(ms, &metric.Metric{ + +Value: float64(d.DeletionTimestamp.Unix()), + +}) + +} + +return &metric.Family{ + +Metrics: ms, + +} + +}), + +), + +*generator.NewFamilyGeneratorWithStability( + + +descDeploymentAnnotationsName, + + +descDeploymentAnnotationsHelp, + + +metric.Gauge, +basemetrics.ALPHA, +"", + +wrapDeploymentFunc(func(d *v1.Deployment) *metric.Family { + +if len(allowAnnotationsList) == 0 { + +return &metric.Family{} + +} + +annotationKeys, annotationValues := createPrometheusLabelKeysValues("annotation", d.Annotations, allowAnnotationsList) + +return &metric.Family{ + +Metrics: []*metric.Metric{ + +{ + +LabelKeys: annotationKeys, + +LabelValues: annotationValues, + +Value: 1, + +}, + +}, + +} + +}), + +), + +*generator.NewFamilyGeneratorWithStability( + +descDeploymentLabelsName, + +descDeploymentLabelsHelp, + +metric.Gauge, + +basemetrics.STABLE, + +"", + +wrapDeploymentFunc(func(d *v1.Deployment) *metric.Family { + +if len(allowLabelsList) == 0 { + +return &metric.Family{} + +} + +labelKeys, labelValues := createPrometheusLabelKeysValues("label", d.Labels, allowLabelsList) + +return &metric.Family{ + +Metrics: []*metric.Metric{ + +{ + +LabelKeys: labelKeys, + +LabelValues: labelValues, + +Value: 1, + +}, + +}, + +} + +}), + +), + +} + } func wrapDeploymentFunc(f func(*v1.Deployment) *metric.Family) func(interface{}) *metric.Family { - return func(obj interface{}) *metric.Family { - deployment := obj.(*v1.Deployment) - metricFamily := f(deployment) +return func(obj interface{}) *metric.Family { - for _, m := range metricFamily.Metrics { - m.LabelKeys, m.LabelValues = mergeKeyValues(descDeploymentLabelsDefaultLabels, []string{deployment.Namespace, deployment.Name}, m.LabelKeys, m.LabelValues) - } +deployment := obj.(*v1.Deployment) + +metricFamily := f(deployment) + +for _, m := range metricFamily.Metrics { + +m.LabelKeys, m.LabelValues = mergeKeyValues(descDeploymentLabelsDefaultLabels, []string{deployment.Namespace, deployment.Name}, m.LabelKeys, m.LabelValues) + +} + +return metricFamily + +} - return metricFamily - } } func createDeploymentListWatch(kubeClient clientset.Interface, ns string, fieldSelector string) cache.ListerWatcher { - return &cache.ListWatch{ - ListFunc: func(opts metav1.ListOptions) (runtime.Object, error) { - opts.FieldSelector = fieldSelector - return kubeClient.AppsV1().Deployments(ns).List(context.TODO(), opts) - }, - WatchFunc: func(opts metav1.ListOptions) (watch.Interface, error) { - opts.FieldSelector = fieldSelector - return kubeClient.AppsV1().Deployments(ns).Watch(context.TODO(), opts) - }, - } + +return &cache.ListWatch{ + +ListFunc: func(opts metav1.ListOptions) (runtime.Object, error) { + +opts.FieldSelector = fieldSelector + +return kubeClient.AppsV1().Deployments(ns).List(context.TODO(), opts) + +}, + +WatchFunc: func(opts metav1.ListOptions) (watch.Interface, error) { + +opts.FieldSelector = fieldSelector + +return kubeClient.AppsV1().Deployments(ns).Watch(context.TODO(), opts) + +}, + +} + } diff --git a/internal/store/deployment_test.go b/internal/store/deployment_test.go index a0e3f4aa17..b3de6716a2 100644 --- a/internal/store/deployment_test.go +++ b/internal/store/deployment_test.go @@ -1,31 +1,45 @@ /* + Copyright 2016 The Kubernetes Authors All rights reserved. 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 +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 store import ( - "testing" - "time" - v1 "k8s.io/api/apps/v1" - corev1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/util/intstr" +"testing" + +"time" + +v1 "k8s.io/api/apps/v1" + +corev1 "k8s.io/api/core/v1" + +metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + +"k8s.io/apimachinery/pkg/util/intstr" + +generator "k8s.io/kube-state-metrics/v2/pkg/metric_generator" - generator "k8s.io/kube-state-metrics/v2/pkg/metric_generator" ) var ( @@ -39,6 +53,7 @@ var ( depl1MaxSurge = intstr.FromInt(10) depl2MaxSurge = intstr.FromString("20%") + ) func TestDeploymentStore(t *testing.T) { @@ -73,11 +88,14 @@ func TestDeploymentStore(t *testing.T) { # TYPE kube_deployment_spec_strategy_rollingupdate_max_unavailable gauge # HELP kube_deployment_spec_strategy_rollingupdate_max_surge [STABLE] Maximum number of replicas that can be scheduled above the desired number of replicas during a rolling update of a deployment. # TYPE kube_deployment_spec_strategy_rollingupdate_max_surge gauge + # HELP kube_deployment_spec_topology_spread_constraint [ALPHA] Explicit details of each topology spread constraint in the deployment's pod template. + # TYPE kube_deployment_spec_topology_spread_constraint gauge # HELP kube_deployment_labels [STABLE] Kubernetes labels converted to Prometheus labels. # TYPE kube_deployment_labels gauge # HELP kube_deployment_deletion_timestamp Unix deletion timestamp # TYPE kube_deployment_deletion_timestamp gauge ` + cases := []generateMetricsTestCase{ { AllowAnnotationsList: []string{"company.io/team"}, @@ -114,6 +132,14 @@ func TestDeploymentStore(t *testing.T) { MaxSurge: &depl1MaxSurge, }, }, + Template: corev1.PodTemplateSpec{ + Spec: corev1.PodSpec{ + TopologySpreadConstraints: []corev1.TopologySpreadConstraint{ + {MaxSkew: 1, TopologyKey: "kubernetes.io/zone", WhenUnsatisfiable: corev1.DoNotSchedule}, + {MaxSkew: 1, TopologyKey: "kubernetes.io/hostname", WhenUnsatisfiable: corev1.ScheduleAnyway}, + }, + }, + }, }, }, Want: metadata + ` @@ -124,6 +150,8 @@ func TestDeploymentStore(t *testing.T) { kube_deployment_spec_replicas{deployment="depl1",namespace="ns1"} 200 kube_deployment_spec_strategy_rollingupdate_max_surge{deployment="depl1",namespace="ns1"} 10 kube_deployment_spec_strategy_rollingupdate_max_unavailable{deployment="depl1",namespace="ns1"} 10 + kube_deployment_spec_topology_spread_constraint{deployment="depl1",namespace="ns1",topology_key="kubernetes.io/zone",max_skew="1",when_unsatisfiable="DoNotSchedule",min_domains="1",label_selector=""} 1 + kube_deployment_spec_topology_spread_constraint{deployment="depl1",namespace="ns1",topology_key="kubernetes.io/hostname",max_skew="1",when_unsatisfiable="ScheduleAnyway",min_domains="1",label_selector=""} 1 kube_deployment_status_observed_generation{deployment="depl1",namespace="ns1"} 111 kube_deployment_status_replicas_available{deployment="depl1",namespace="ns1"} 10 kube_deployment_status_replicas_unavailable{deployment="depl1",namespace="ns1"} 5 @@ -224,7 +252,7 @@ func TestDeploymentStore(t *testing.T) { kube_deployment_status_replicas_ready{deployment="depl3",namespace="ns3"} 0 kube_deployment_status_replicas_unavailable{deployment="depl3",namespace="ns3"} 0 kube_deployment_status_replicas_updated{deployment="depl3",namespace="ns3"} 0 - kube_deployment_status_condition{condition="Progressing",deployment="depl3",namespace="ns3",reason="",status="false"} 0 + kube_deployment_status_condition{condition="Progressing",deployment="depl3",namespace="ns3",reason="",status="false"} 0 kube_deployment_status_condition{condition="Progressing",deployment="depl3",namespace="ns3",reason="",status="true"} 1 kube_deployment_status_condition{condition="Progressing",deployment="depl3",namespace="ns3",reason="",status="unknown"} 0 `, @@ -261,3 +289,372 @@ func TestDeploymentStore(t *testing.T) { } } } + +// output so we only have to modify a single place when doing adjustments. + +const metadata = ` + +# HELP kube_deployment_annotations Kubernetes annotations converted to Prometheus labels. + +# TYPE kube_deployment_annotations gauge + +# HELP kube_deployment_created [STABLE] Unix creation timestamp + +# TYPE kube_deployment_created gauge + +# HELP kube_deployment_metadata_generation [STABLE] Sequence number representing a specific generation of the desired state. + +# TYPE kube_deployment_metadata_generation gauge + +# HELP kube_deployment_spec_paused [STABLE] Whether the deployment is paused and will not be processed by the deployment controller. + +# TYPE kube_deployment_spec_paused gauge + +# HELP kube_deployment_spec_replicas [STABLE] Number of desired pods for a deployment. + +# TYPE kube_deployment_spec_replicas gauge + +# HELP kube_deployment_status_replicas [STABLE] The number of replicas per deployment. + +# TYPE kube_deployment_status_replicas gauge + +# HELP kube_deployment_status_replicas_ready [STABLE] The number of ready replicas per deployment. + +# TYPE kube_deployment_status_replicas_ready gauge + +# HELP kube_deployment_status_replicas_available [STABLE] The number of available replicas per deployment. + +# TYPE kube_deployment_status_replicas_available gauge + +# HELP kube_deployment_status_replicas_unavailable [STABLE] The number of unavailable replicas per deployment. + +# TYPE kube_deployment_status_replicas_unavailable gauge + +# HELP kube_deployment_status_replicas_updated [STABLE] The number of updated replicas per deployment. + +# TYPE kube_deployment_status_replicas_updated gauge + +# HELP kube_deployment_status_observed_generation [STABLE] The generation observed by the deployment controller. + +# TYPE kube_deployment_status_observed_generation gauge + +# HELP kube_deployment_status_condition [STABLE] The current status conditions of a deployment. + +# TYPE kube_deployment_status_condition gauge + +# HELP kube_deployment_spec_strategy_rollingupdate_max_unavailable [STABLE] Maximum number of unavailable replicas during a rolling update of a deployment. + +# TYPE kube_deployment_spec_strategy_rollingupdate_max_unavailable gauge + +# HELP kube_deployment_spec_strategy_rollingupdate_max_surge [STABLE] Maximum number of replicas that can be scheduled above the desired number of replicas during a rolling update of a deployment. + +# TYPE kube_deployment_spec_strategy_rollingupdate_max_surge gauge + +# HELP kube_deployment_spec_topology_spread_constraints [STABLE] Number of topology spread constraints in the deployment's pod template. + +# TYPE kube_deployment_spec_topology_spread_constraints gauge + +# HELP kube_deployment_labels [STABLE] Kubernetes labels converted to Prometheus labels. + +# TYPE kube_deployment_labels gauge + +` + +cases := []generateMetricsTestCase{ + +{ + +AllowAnnotationsList: []string{"company.io/team"}, + +Obj: &v1.Deployment{ + +ObjectMeta: metav1.ObjectMeta{ + +Name: "depl1", + +CreationTimestamp: metav1.Time{Time: time.Unix(1500000000, 0)}, + +Namespace: "ns1", + +Annotations: map[string]string{ + +"company.io/team": "my-brilliant-team", + +}, + +Labels: map[string]string{ + +"app": "example1", + +}, + +Generation: 21, + +}, + +Status: v1.DeploymentStatus{ + +Replicas: 15, + +ReadyReplicas: 10, + +AvailableReplicas: 10, + +UnavailableReplicas: 5, + +UpdatedReplicas: 2, + +ObservedGeneration: 111, + +Conditions: []v1.DeploymentCondition{ + +{Type: v1.DeploymentAvailable, Status: corev1.ConditionTrue}, + +{Type: v1.DeploymentProgressing, Status: corev1.ConditionTrue}, + +}, + +}, + +Spec: v1.DeploymentSpec{ + +Replicas: &depl1Replicas, + +Strategy: v1.DeploymentStrategy{ + +RollingUpdate: &v1.RollingUpdateDeployment{ + +MaxUnavailable: &depl1MaxUnavailable, + +MaxSurge: &depl1MaxSurge, + +}, + +}, + +Template: corev1.PodTemplateSpec{ + +Spec: corev1.PodSpec{ + +TopologySpreadConstraints: []corev1.TopologySpreadConstraint{ + +{ + +MaxSkew: 1, + +TopologyKey: "kubernetes.io/zone", + +WhenUnsatisfiable: corev1.DoNotSchedule, + +}, + +{ + +MaxSkew: 1, + +TopologyKey: "kubernetes.io/hostname", + +WhenUnsatisfiable: corev1.ScheduleAnyway, + +}, + +}, + +}, + +}, + +}, + +}, + +Want: metadata + ` + +kube_deployment_annotations{annotation_company_io_team="my-brilliant-team",deployment="depl1",namespace="ns1"} 1 + +kube_deployment_created{deployment="depl1",namespace="ns1"} 1.5e+09 + +kube_deployment_metadata_generation{deployment="depl1",namespace="ns1"} 21 + +kube_deployment_spec_paused{deployment="depl1",namespace="ns1"} 0 + +kube_deployment_spec_replicas{deployment="depl1",namespace="ns1"} 200 + +kube_deployment_spec_strategy_rollingupdate_max_surge{deployment="depl1",namespace="ns1"} 10 + +kube_deployment_spec_strategy_rollingupdate_max_unavailable{deployment="depl1",namespace="ns1"} 10 + +kube_deployment_spec_topology_spread_constraints{deployment="depl1",namespace="ns1"} 2 + +kube_deployment_status_observed_generation{deployment="depl1",namespace="ns1"} 111 + +kube_deployment_status_replicas_available{deployment="depl1",namespace="ns1"} 10 + +kube_deployment_status_replicas_unavailable{deployment="depl1",namespace="ns1"} 5 + +kube_deployment_status_replicas_updated{deployment="depl1",namespace="ns1"} 2 + +kube_deployment_status_replicas{deployment="depl1",namespace="ns1"} 15 + +kube_deployment_status_replicas_ready{deployment="depl1",namespace="ns1"} 10 + +kube_deployment_status_condition{deployment="depl1",namespace="ns1",condition="Available",status="true"} 1 + +kube_deployment_status_condition{deployment="depl1",namespace="ns1",condition="Progressing",status="true"} 1 + +kube_deployment_status_condition{deployment="depl1",namespace="ns1",condition="Available",status="false"} 0 + +kube_deployment_status_condition{deployment="depl1",namespace="ns1",condition="Progressing",status="false"} 0 + +kube_deployment_status_condition{deployment="depl1",namespace="ns1",condition="Available",status="unknown"} 0 + +kube_deployment_status_condition{deployment="depl1",namespace="ns1",condition="Progressing",status="unknown"} 0 + +`, + +}, + +{ + +Obj: &v1.Deployment{ + +ObjectMeta: metav1.ObjectMeta{ + +Name: "depl2", + +Namespace: "ns2", + +Labels: map[string]string{ + +"app": "example2", + +}, + +Generation: 14, + +}, + +Status: v1.DeploymentStatus{ + +Replicas: 10, + +ReadyReplicas: 5, + +AvailableReplicas: 5, + +UnavailableReplicas: 0, + +UpdatedReplicas: 1, + +ObservedGeneration: 1111, + +Conditions: []v1.DeploymentCondition{ + +{Type: v1.DeploymentAvailable, Status: corev1.ConditionFalse}, + +{Type: v1.DeploymentProgressing, Status: corev1.ConditionFalse}, + +{Type: v1.DeploymentReplicaFailure, Status: corev1.ConditionTrue}, + +}, + +}, + +Spec: v1.DeploymentSpec{ + +Paused: true, + +Replicas: &depl2Replicas, + +Strategy: v1.DeploymentStrategy{ + +RollingUpdate: &v1.RollingUpdateDeployment{ + +MaxUnavailable: &depl2MaxUnavailable, + +MaxSurge: &depl2MaxSurge, + +}, + +}, + +Template: corev1.PodTemplateSpec{ + +Spec: corev1.PodSpec{ + +TopologySpreadConstraints: []corev1.TopologySpreadConstraint{}, + +}, + +}, + +}, + +}, + +Want: metadata + ` + +kube_deployment_metadata_generation{deployment="depl2",namespace="ns2"} 14 + +kube_deployment_spec_paused{deployment="depl2",namespace="ns2"} 1 + +kube_deployment_spec_replicas{deployment="depl2",namespace="ns2"} 5 + +kube_deployment_spec_strategy_rollingupdate_max_surge{deployment="depl2",namespace="ns2"} 1 + +kube_deployment_spec_strategy_rollingupdate_max_unavailable{deployment="depl2",namespace="ns2"} 1 + +kube_deployment_spec_topology_spread_constraints{deployment="depl2",namespace="ns2"} 0 + +kube_deployment_status_observed_generation{deployment="depl2",namespace="ns2"} 1111 + +kube_deployment_status_replicas_available{deployment="depl2",namespace="ns2"} 5 + +kube_deployment_status_replicas_unavailable{deployment="depl2",namespace="ns2"} 0 + +kube_deployment_status_replicas_updated{deployment="depl2",namespace="ns2"} 1 + +kube_deployment_status_replicas{deployment="depl2",namespace="ns2"} 10 + +kube_deployment_status_replicas_ready{deployment="depl2",namespace="ns2"} 5 + +kube_deployment_status_condition{deployment="depl2",namespace="ns2",condition="Available",status="true"} 0 + +kube_deployment_status_condition{deployment="depl2",namespace="ns2",condition="Progressing",status="true"} 0 + +kube_deployment_status_condition{deployment="depl2",namespace="ns2",condition="ReplicaFailure",status="true"} 1 + +kube_deployment_status_condition{deployment="depl2",namespace="ns2",condition="Available",status="false"} 1 + +kube_deployment_status_condition{deployment="depl2",namespace="ns2",condition="Progressing",status="false"} 1 + +kube_deployment_status_condition{deployment="depl2",namespace="ns2",condition="ReplicaFailure",status="false"} 0 + +kube_deployment_status_condition{deployment="depl2",namespace="ns2",condition="Available",status="unknown"} 0 + +kube_deployment_status_condition{deployment="depl2",namespace="ns2",condition="Progressing",status="unknown"} 0 + +kube_deployment_status_condition{deployment="depl2",namespace="ns2",condition="ReplicaFailure",status="unknown"} 0 + +`, + +}, + +} + +for i, c := range cases { + +c.Func = generator.ComposeMetricGenFuncs(deploymentMetricFamilies(c.AllowAnnotationsList, nil)) + +c.Headers = generator.ExtractMetricFamilyHeaders(deploymentMetricFamilies(c.AllowAnnotationsList, nil)) + +if err := c.run(); err != nil { + +t.Errorf("unexpected collecting result in %vth run:\n%s", i, err) + +} + +} + +>>>>>>> 6f562672 (Add kube_deployment_spec_topology_spread_constraints metric) +} From b8fd9cff4f685d9823e3d0dfec7fa3f1d6aa504e Mon Sep 17 00:00:00 2001 From: Soumya Date: Mon, 11 Aug 2025 12:32:35 +0530 Subject: [PATCH 02/11] docs: Add kube_deployment_spec_topology_spread_constraints metric documentation --- docs/metrics/workload/deployment-metrics.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/metrics/workload/deployment-metrics.md b/docs/metrics/workload/deployment-metrics.md index e8d0fdb42a..14e2b04f68 100644 --- a/docs/metrics/workload/deployment-metrics.md +++ b/docs/metrics/workload/deployment-metrics.md @@ -14,6 +14,7 @@ | kube_deployment_spec_paused | Gauge | Whether the deployment is paused and will not be processed by the deployment controller. | `deployment`=<deployment-name>
`namespace`=<deployment-namespace> | STABLE | | kube_deployment_spec_strategy_rollingupdate_max_unavailable | Gauge | Maximum number of unavailable replicas during a rolling update of a deployment. | `deployment`=<deployment-name>
`namespace`=<deployment-namespace> | STABLE | | kube_deployment_spec_strategy_rollingupdate_max_surge | Gauge | Maximum number of replicas that can be scheduled above the desired number of replicas during a rolling update of a deployment. | `deployment`=<deployment-name>
`namespace`=<deployment-namespace> | STABLE | +| kube_deployment_spec_topology_spread_constraint | Gauge | Explicit details of each topology spread constraint in the deployment's pod template | `deployment`=<deployment-name>
`namespace`=<deployment-namespace>
`topology_key`=<topology-key>
`max_skew`=<max-skew-value>
`when_unsatisfiable`=<DoNotSchedule\|ScheduleAnyway>
`min_domains`=<min-domains-value>
`label_selector`=<label-selector-string> | ALPHA | | kube_deployment_metadata_generation | Gauge | Sequence number representing a specific generation of the desired state. | `deployment`=<deployment-name>
`namespace`=<deployment-namespace> | STABLE | | kube_deployment_labels | Gauge | Kubernetes labels converted to Prometheus labels controlled via [--metric-labels-allowlist](../../developer/cli-arguments.md) | `deployment`=<deployment-name>
`namespace`=<deployment-namespace>
`label_DEPLOYMENT_LABEL`=<DEPLOYMENT_LABEL> | STABLE | | kube_deployment_created | Gauge | Unix creation timestamp | `deployment`=<deployment-name>
`namespace`=<deployment-namespace> | STABLE | From b042779f3d685f67466a0763b82d4223edbf0028 Mon Sep 17 00:00:00 2001 From: Soumya Date: Tue, 12 Aug 2025 11:23:55 +0530 Subject: [PATCH 03/11] docs: Add kube_deployment_spec_topology_spread_constraints to documentation files --- docs/metrics/workload/deployment-metrics.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/docs/metrics/workload/deployment-metrics.md b/docs/metrics/workload/deployment-metrics.md index 14e2b04f68..9ceefe97f9 100644 --- a/docs/metrics/workload/deployment-metrics.md +++ b/docs/metrics/workload/deployment-metrics.md @@ -3,6 +3,7 @@ | Metric name | Metric type | Description | Labels/tags | Status | | ----------------------------------------------------------- | ----------- | --------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------ | | kube_deployment_annotations | Gauge | Kubernetes annotations converted to Prometheus labels controlled via [--metric-annotations-allowlist](../../developer/cli-arguments.md) | `deployment`=<deployment-name>
`namespace`=<deployment-namespace>
`annotation_DEPLOYMENT_ANNOTATION`=<DEPLOYMENT_ANNOTATION> | EXPERIMENTAL | +<<<<<<< HEAD | kube_deployment_status_replicas | Gauge | The number of replicas per deployment. | `deployment`=<deployment-name>
`namespace`=<deployment-namespace> | STABLE | | kube_deployment_status_replicas_ready | Gauge | The number of ready replicas per deployment. | `deployment`=<deployment-name>
`namespace`=<deployment-namespace> | STABLE | | kube_deployment_status_replicas_available | Gauge | The number of available replicas per deployment. | `deployment`=<deployment-name>
`namespace`=<deployment-namespace> | STABLE | @@ -16,6 +17,21 @@ | kube_deployment_spec_strategy_rollingupdate_max_surge | Gauge | Maximum number of replicas that can be scheduled above the desired number of replicas during a rolling update of a deployment. | `deployment`=<deployment-name>
`namespace`=<deployment-namespace> | STABLE | | kube_deployment_spec_topology_spread_constraint | Gauge | Explicit details of each topology spread constraint in the deployment's pod template | `deployment`=<deployment-name>
`namespace`=<deployment-namespace>
`topology_key`=<topology-key>
`max_skew`=<max-skew-value>
`when_unsatisfiable`=<DoNotSchedule\|ScheduleAnyway>
`min_domains`=<min-domains-value>
`label_selector`=<label-selector-string> | ALPHA | | kube_deployment_metadata_generation | Gauge | Sequence number representing a specific generation of the desired state. | `deployment`=<deployment-name>
`namespace`=<deployment-namespace> | STABLE | +======= +| kube_deployment_status_replicas | Gauge | | `deployment`=<deployment-name>
`namespace`=<deployment-namespace> | STABLE | +| kube_deployment_status_replicas_ready | Gauge | | `deployment`=<deployment-name>
`namespace`=<deployment-namespace> | STABLE | +| kube_deployment_status_replicas_available | Gauge | | `deployment`=<deployment-name>
`namespace`=<deployment-namespace> | STABLE | +| kube_deployment_status_replicas_unavailable | Gauge | | `deployment`=<deployment-name>
`namespace`=<deployment-namespace> | STABLE | +| kube_deployment_status_replicas_updated | Gauge | | `deployment`=<deployment-name>
`namespace`=<deployment-namespace> | STABLE | +| kube_deployment_status_observed_generation | Gauge | | `deployment`=<deployment-name>
`namespace`=<deployment-namespace> | STABLE | +| kube_deployment_status_condition | Gauge | | `deployment`=<deployment-name>
`namespace`=<deployment-namespace>
`condition`=<deployment-condition>
`status`=<true\|false\|unknown> | STABLE | +| kube_deployment_spec_replicas | Gauge | | `deployment`=<deployment-name>
`namespace`=<deployment-namespace> | STABLE | +| kube_deployment_spec_paused | Gauge | | `deployment`=<deployment-name>
`namespace`=<deployment-namespace> | STABLE | +| kube_deployment_spec_strategy_rollingupdate_max_unavailable | Gauge | | `deployment`=<deployment-name>
`namespace`=<deployment-namespace> | STABLE | +| kube_deployment_spec_strategy_rollingupdate_max_surge | Gauge | | `deployment`=<deployment-name>
`namespace`=<deployment-namespace> | STABLE | +| kube_deployment_spec_topology_spread_constraints | Gauge | | `deployment`=<deployment-name>
`namespace`=<deployment-namespace> | STABLE | +| kube_deployment_metadata_generation | Gauge | | `deployment`=<deployment-name>
`namespace`=<deployment-namespace> | STABLE | +>>>>>>> 206a4457 (docs: Add kube_deployment_spec_topology_spread_constraints to documentation files) | kube_deployment_labels | Gauge | Kubernetes labels converted to Prometheus labels controlled via [--metric-labels-allowlist](../../developer/cli-arguments.md) | `deployment`=<deployment-name>
`namespace`=<deployment-namespace>
`label_DEPLOYMENT_LABEL`=<DEPLOYMENT_LABEL> | STABLE | | kube_deployment_created | Gauge | Unix creation timestamp | `deployment`=<deployment-name>
`namespace`=<deployment-namespace> | STABLE | | kube_deployment_deletion_timestamp | Gauge | Unix deletion timestamp | `deployment`=<deployment-name>
`namespace`=<deployment-namespace> | EXPIREMENTAL | From d21d4ba553ca7092fbbd292a1632d58d88424228 Mon Sep 17 00:00:00 2001 From: SoumyaRaikwar Date: Sat, 30 Aug 2025 17:29:17 +0530 Subject: [PATCH 04/11] Fix deployment metrics documentation with reason label --- docs/metrics/workload/deployment-metrics.md | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/docs/metrics/workload/deployment-metrics.md b/docs/metrics/workload/deployment-metrics.md index 9ceefe97f9..a3ce108ffe 100644 --- a/docs/metrics/workload/deployment-metrics.md +++ b/docs/metrics/workload/deployment-metrics.md @@ -17,21 +17,6 @@ | kube_deployment_spec_strategy_rollingupdate_max_surge | Gauge | Maximum number of replicas that can be scheduled above the desired number of replicas during a rolling update of a deployment. | `deployment`=<deployment-name>
`namespace`=<deployment-namespace> | STABLE | | kube_deployment_spec_topology_spread_constraint | Gauge | Explicit details of each topology spread constraint in the deployment's pod template | `deployment`=<deployment-name>
`namespace`=<deployment-namespace>
`topology_key`=<topology-key>
`max_skew`=<max-skew-value>
`when_unsatisfiable`=<DoNotSchedule\|ScheduleAnyway>
`min_domains`=<min-domains-value>
`label_selector`=<label-selector-string> | ALPHA | | kube_deployment_metadata_generation | Gauge | Sequence number representing a specific generation of the desired state. | `deployment`=<deployment-name>
`namespace`=<deployment-namespace> | STABLE | -======= -| kube_deployment_status_replicas | Gauge | | `deployment`=<deployment-name>
`namespace`=<deployment-namespace> | STABLE | -| kube_deployment_status_replicas_ready | Gauge | | `deployment`=<deployment-name>
`namespace`=<deployment-namespace> | STABLE | -| kube_deployment_status_replicas_available | Gauge | | `deployment`=<deployment-name>
`namespace`=<deployment-namespace> | STABLE | -| kube_deployment_status_replicas_unavailable | Gauge | | `deployment`=<deployment-name>
`namespace`=<deployment-namespace> | STABLE | -| kube_deployment_status_replicas_updated | Gauge | | `deployment`=<deployment-name>
`namespace`=<deployment-namespace> | STABLE | -| kube_deployment_status_observed_generation | Gauge | | `deployment`=<deployment-name>
`namespace`=<deployment-namespace> | STABLE | -| kube_deployment_status_condition | Gauge | | `deployment`=<deployment-name>
`namespace`=<deployment-namespace>
`condition`=<deployment-condition>
`status`=<true\|false\|unknown> | STABLE | -| kube_deployment_spec_replicas | Gauge | | `deployment`=<deployment-name>
`namespace`=<deployment-namespace> | STABLE | -| kube_deployment_spec_paused | Gauge | | `deployment`=<deployment-name>
`namespace`=<deployment-namespace> | STABLE | -| kube_deployment_spec_strategy_rollingupdate_max_unavailable | Gauge | | `deployment`=<deployment-name>
`namespace`=<deployment-namespace> | STABLE | -| kube_deployment_spec_strategy_rollingupdate_max_surge | Gauge | | `deployment`=<deployment-name>
`namespace`=<deployment-namespace> | STABLE | -| kube_deployment_spec_topology_spread_constraints | Gauge | | `deployment`=<deployment-name>
`namespace`=<deployment-namespace> | STABLE | -| kube_deployment_metadata_generation | Gauge | | `deployment`=<deployment-name>
`namespace`=<deployment-namespace> | STABLE | ->>>>>>> 206a4457 (docs: Add kube_deployment_spec_topology_spread_constraints to documentation files) | kube_deployment_labels | Gauge | Kubernetes labels converted to Prometheus labels controlled via [--metric-labels-allowlist](../../developer/cli-arguments.md) | `deployment`=<deployment-name>
`namespace`=<deployment-namespace>
`label_DEPLOYMENT_LABEL`=<DEPLOYMENT_LABEL> | STABLE | | kube_deployment_created | Gauge | Unix creation timestamp | `deployment`=<deployment-name>
`namespace`=<deployment-namespace> | STABLE | | kube_deployment_deletion_timestamp | Gauge | Unix deletion timestamp | `deployment`=<deployment-name>
`namespace`=<deployment-namespace> | EXPIREMENTAL | From 01d7863416bd881d4c2e818e4fb103325cb88255 Mon Sep 17 00:00:00 2001 From: SoumyaRaikwar Date: Sat, 30 Aug 2025 17:41:50 +0530 Subject: [PATCH 05/11] updated deployment-metrics.md --- docs/metrics/workload/deployment-metrics.md | 39 ++++++++++----------- 1 file changed, 19 insertions(+), 20 deletions(-) diff --git a/docs/metrics/workload/deployment-metrics.md b/docs/metrics/workload/deployment-metrics.md index a3ce108ffe..08f67fc099 100644 --- a/docs/metrics/workload/deployment-metrics.md +++ b/docs/metrics/workload/deployment-metrics.md @@ -1,22 +1,21 @@ # Deployment Metrics -| Metric name | Metric type | Description | Labels/tags | Status | -| ----------------------------------------------------------- | ----------- | --------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------ | -| kube_deployment_annotations | Gauge | Kubernetes annotations converted to Prometheus labels controlled via [--metric-annotations-allowlist](../../developer/cli-arguments.md) | `deployment`=<deployment-name>
`namespace`=<deployment-namespace>
`annotation_DEPLOYMENT_ANNOTATION`=<DEPLOYMENT_ANNOTATION> | EXPERIMENTAL | -<<<<<<< HEAD -| kube_deployment_status_replicas | Gauge | The number of replicas per deployment. | `deployment`=<deployment-name>
`namespace`=<deployment-namespace> | STABLE | -| kube_deployment_status_replicas_ready | Gauge | The number of ready replicas per deployment. | `deployment`=<deployment-name>
`namespace`=<deployment-namespace> | STABLE | -| kube_deployment_status_replicas_available | Gauge | The number of available replicas per deployment. | `deployment`=<deployment-name>
`namespace`=<deployment-namespace> | STABLE | -| kube_deployment_status_replicas_unavailable | Gauge | The number of unavailable replicas per deployment. | `deployment`=<deployment-name>
`namespace`=<deployment-namespace> | STABLE | -| kube_deployment_status_replicas_updated | Gauge | The number of updated replicas per deployment. | `deployment`=<deployment-name>
`namespace`=<deployment-namespace> | STABLE | -| kube_deployment_status_observed_generation | Gauge | The generation observed by the deployment controller. | `deployment`=<deployment-name>
`namespace`=<deployment-namespace> | STABLE | -| kube_deployment_status_condition | Gauge | The current status conditions of a deployment. | `deployment`=<deployment-name>
`namespace`=<deployment-namespace>
`reason`=<deployment-transition-reason>
`condition`=<deployment-condition>
`status`=<true\|false\|unknown> | STABLE | -| kube_deployment_spec_replicas | Gauge | Number of desired pods for a deployment. | `deployment`=<deployment-name>
`namespace`=<deployment-namespace> | STABLE | -| kube_deployment_spec_paused | Gauge | Whether the deployment is paused and will not be processed by the deployment controller. | `deployment`=<deployment-name>
`namespace`=<deployment-namespace> | STABLE | -| kube_deployment_spec_strategy_rollingupdate_max_unavailable | Gauge | Maximum number of unavailable replicas during a rolling update of a deployment. | `deployment`=<deployment-name>
`namespace`=<deployment-namespace> | STABLE | -| kube_deployment_spec_strategy_rollingupdate_max_surge | Gauge | Maximum number of replicas that can be scheduled above the desired number of replicas during a rolling update of a deployment. | `deployment`=<deployment-name>
`namespace`=<deployment-namespace> | STABLE | -| kube_deployment_spec_topology_spread_constraint | Gauge | Explicit details of each topology spread constraint in the deployment's pod template | `deployment`=<deployment-name>
`namespace`=<deployment-namespace>
`topology_key`=<topology-key>
`max_skew`=<max-skew-value>
`when_unsatisfiable`=<DoNotSchedule\|ScheduleAnyway>
`min_domains`=<min-domains-value>
`label_selector`=<label-selector-string> | ALPHA | -| kube_deployment_metadata_generation | Gauge | Sequence number representing a specific generation of the desired state. | `deployment`=<deployment-name>
`namespace`=<deployment-namespace> | STABLE | -| kube_deployment_labels | Gauge | Kubernetes labels converted to Prometheus labels controlled via [--metric-labels-allowlist](../../developer/cli-arguments.md) | `deployment`=<deployment-name>
`namespace`=<deployment-namespace>
`label_DEPLOYMENT_LABEL`=<DEPLOYMENT_LABEL> | STABLE | -| kube_deployment_created | Gauge | Unix creation timestamp | `deployment`=<deployment-name>
`namespace`=<deployment-namespace> | STABLE | -| kube_deployment_deletion_timestamp | Gauge | Unix deletion timestamp | `deployment`=<deployment-name>
`namespace`=<deployment-namespace> | EXPIREMENTAL | +| Metric name | Metric type | Description | Labels/tags | Status | +| ----------------------------------------------------------- | ----------- | --------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------ | +| kube_deployment_annotations | Gauge | Kubernetes annotations converted to Prometheus labels controlled via [--metric-annotations-allowlist](../../developer/cli-arguments.md) | `deployment`=
`namespace`=
`annotation_DEPLOYMENT_ANNOTATION`= | EXPERIMENTAL | +| kube_deployment_status_replicas | Gauge | The number of replicas per deployment | `deployment`=
`namespace`= | STABLE | +| kube_deployment_status_replicas_ready | Gauge | The number of ready replicas per deployment | `deployment`=
`namespace`= | STABLE | +| kube_deployment_status_replicas_available | Gauge | The number of available replicas per deployment | `deployment`=
`namespace`= | STABLE | +| kube_deployment_status_replicas_unavailable | Gauge | The number of unavailable replicas per deployment | `deployment`=
`namespace`= | STABLE | +| kube_deployment_status_replicas_updated | Gauge | The number of updated replicas per deployment | `deployment`=
`namespace`= | STABLE | +| kube_deployment_status_observed_generation | Gauge | The generation observed by the deployment controller | `deployment`=
`namespace`= | STABLE | +| kube_deployment_status_condition | Gauge | The current status conditions of a deployment | `deployment`=
`namespace`=
`reason`=
`condition`=
`status`= | STABLE | +| kube_deployment_spec_replicas | Gauge | Number of desired pods for a deployment | `deployment`=
`namespace`= | STABLE | +| kube_deployment_spec_paused | Gauge | Whether the deployment is paused and will not be processed by the deployment controller | `deployment`=
`namespace`= | STABLE | +| kube_deployment_spec_strategy_rollingupdate_max_unavailable | Gauge | Maximum number of unavailable replicas during a rolling update of a deployment | `deployment`=
`namespace`= | STABLE | +| kube_deployment_spec_strategy_rollingupdate_max_surge | Gauge | Maximum number of replicas that can be scheduled above the desired number of replicas during a rolling update of a deployment | `deployment`=
`namespace`= | STABLE | +| kube_deployment_spec_topology_spread_constraint | Gauge | Explicit details of each topology spread constraint in the deployment's pod template | `deployment`=
`namespace`=
`topology_key`=
`max_skew`=
`when_unsatisfiable`=
`min_domains`=
`label_selector`= | ALPHA | +| kube_deployment_metadata_generation | Gauge | Sequence number representing a specific generation of the desired state | `deployment`=
`namespace`= | STABLE | +| kube_deployment_labels | Gauge | Kubernetes labels converted to Prometheus labels controlled via [--metric-labels-allowlist](../../developer/cli-arguments.md) | `deployment`=
`namespace`=
`label_DEPLOYMENT_LABEL`= | STABLE | +| kube_deployment_created | Gauge | Unix creation timestamp | `deployment`=
`namespace`= | STABLE | +| kube_deployment_deletion_timestamp | Gauge | Unix deletion timestamp | `deployment`=
`namespace`= | EXPERIMENTAL | From b98a34c5590350049ac3e0318fe3ea84c46faf81 Mon Sep 17 00:00:00 2001 From: SoumyaRaikwar Date: Sun, 31 Aug 2025 04:18:07 +0530 Subject: [PATCH 06/11] corrected intendation in deployment-metrics.md --- jsonnet/kube-state-metrics/kube-state-metrics.libsonnet | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jsonnet/kube-state-metrics/kube-state-metrics.libsonnet b/jsonnet/kube-state-metrics/kube-state-metrics.libsonnet index bbf44adb77..a2115a8470 100644 --- a/jsonnet/kube-state-metrics/kube-state-metrics.libsonnet +++ b/jsonnet/kube-state-metrics/kube-state-metrics.libsonnet @@ -192,7 +192,7 @@ }, livenessProbe: { timeoutSeconds: 5, initialDelaySeconds: 5, httpGet: { port: 'http-metrics', - path: '/livez', + path: '/healthz', } }, readinessProbe: { timeoutSeconds: 5, initialDelaySeconds: 5, httpGet: { port: 'telemetry', From da0c0e5284534545e30ec3bc22cff6baad1cc967 Mon Sep 17 00:00:00 2001 From: SoumyaRaikwar Date: Tue, 2 Sep 2025 00:56:12 +0530 Subject: [PATCH 07/11] done --- test_output.txt | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 test_output.txt diff --git a/test_output.txt b/test_output.txt new file mode 100644 index 0000000000..e75578f6ec --- /dev/null +++ b/test_output.txt @@ -0,0 +1,4 @@ +=== RUN TestDeploymentStore +--- PASS: TestDeploymentStore (0.00s) +PASS +ok k8s.io/kube-state-metrics/v2/internal/store (cached) From aec2fec230bb6008fbcfb85b1521b91dffdce342 Mon Sep 17 00:00:00 2001 From: SoumyaRaikwar Date: Tue, 2 Sep 2025 01:50:08 +0530 Subject: [PATCH 08/11] Fix YAML formatting and indentation in manifest files - Add document start markers (---) to all YAML files - Fix indentation errors throughout manifests - Resolve line length violations - Correct YAML list formatting - Update image name to remove -amd64 suffix --- .../autosharding/cluster-role-binding.yaml | 7 +- examples/autosharding/cluster-role.yaml | 239 +++++++++--------- examples/autosharding/kustomization.yaml | 1 + examples/autosharding/role-binding.yaml | 5 +- examples/autosharding/role.yaml | 29 ++- examples/autosharding/service-account.yaml | 1 + examples/autosharding/service.yaml | 13 +- examples/autosharding/statefulset.yaml | 1 + examples/standard/cluster-role-binding.yaml | 7 +- examples/standard/cluster-role.yaml | 239 +++++++++--------- examples/standard/deployment.yaml | 1 + examples/standard/kustomization.yaml | 11 +- examples/standard/service-account.yaml | 1 + examples/standard/service.yaml | 13 +- 14 files changed, 291 insertions(+), 277 deletions(-) diff --git a/examples/autosharding/cluster-role-binding.yaml b/examples/autosharding/cluster-role-binding.yaml index 761075f15b..4f11c6a2be 100644 --- a/examples/autosharding/cluster-role-binding.yaml +++ b/examples/autosharding/cluster-role-binding.yaml @@ -1,3 +1,4 @@ +--- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: @@ -11,6 +12,6 @@ roleRef: kind: ClusterRole name: kube-state-metrics subjects: -- kind: ServiceAccount - name: kube-state-metrics - namespace: kube-system + - kind: ServiceAccount + name: kube-state-metrics + namespace: kube-system diff --git a/examples/autosharding/cluster-role.yaml b/examples/autosharding/cluster-role.yaml index 6e9ff89892..d4101bee74 100644 --- a/examples/autosharding/cluster-role.yaml +++ b/examples/autosharding/cluster-role.yaml @@ -1,3 +1,4 @@ +--- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: @@ -7,122 +8,122 @@ metadata: app.kubernetes.io/version: 2.17.0 name: kube-state-metrics rules: -- apiGroups: - - "" - resources: - - configmaps - - secrets - - nodes - - pods - - services - - serviceaccounts - - resourcequotas - - replicationcontrollers - - limitranges - - persistentvolumeclaims - - persistentvolumes - - namespaces - - endpoints - verbs: - - list - - watch -- apiGroups: - - apps - resources: - - statefulsets - - daemonsets - - deployments - - replicasets - verbs: - - list - - watch -- apiGroups: - - batch - resources: - - cronjobs - - jobs - verbs: - - list - - watch -- apiGroups: - - autoscaling - resources: - - horizontalpodautoscalers - verbs: - - list - - watch -- apiGroups: - - authentication.k8s.io - resources: - - tokenreviews - verbs: - - create -- apiGroups: - - authorization.k8s.io - resources: - - subjectaccessreviews - verbs: - - create -- apiGroups: - - policy - resources: - - poddisruptionbudgets - verbs: - - list - - watch -- apiGroups: - - certificates.k8s.io - resources: - - certificatesigningrequests - verbs: - - list - - watch -- apiGroups: - - discovery.k8s.io - resources: - - endpointslices - verbs: - - list - - watch -- apiGroups: - - storage.k8s.io - resources: - - storageclasses - - volumeattachments - verbs: - - list - - watch -- apiGroups: - - admissionregistration.k8s.io - resources: - - mutatingwebhookconfigurations - - validatingwebhookconfigurations - verbs: - - list - - watch -- apiGroups: - - networking.k8s.io - resources: - - networkpolicies - - ingressclasses - - ingresses - verbs: - - list - - watch -- apiGroups: - - coordination.k8s.io - resources: - - leases - verbs: - - list - - watch -- apiGroups: - - rbac.authorization.k8s.io - resources: - - clusterrolebindings - - clusterroles - - rolebindings - - roles - verbs: - - list - - watch + - apiGroups: + - "" + resources: + - configmaps + - secrets + - nodes + - pods + - services + - serviceaccounts + - resourcequotas + - replicationcontrollers + - limitranges + - persistentvolumeclaims + - persistentvolumes + - namespaces + - endpoints + verbs: + - list + - watch + - apiGroups: + - apps + resources: + - statefulsets + - daemonsets + - deployments + - replicasets + verbs: + - list + - watch + - apiGroups: + - batch + resources: + - cronjobs + - jobs + verbs: + - list + - watch + - apiGroups: + - autoscaling + resources: + - horizontalpodautoscalers + verbs: + - list + - watch + - apiGroups: + - authentication.k8s.io + resources: + - tokenreviews + verbs: + - create + - apiGroups: + - authorization.k8s.io + resources: + - subjectaccessreviews + verbs: + - create + - apiGroups: + - policy + resources: + - poddisruptionbudgets + verbs: + - list + - watch + - apiGroups: + - certificates.k8s.io + resources: + - certificatesigningrequests + verbs: + - list + - watch + - apiGroups: + - discovery.k8s.io + resources: + - endpointslices + verbs: + - list + - watch + - apiGroups: + - storage.k8s.io + resources: + - storageclasses + - volumeattachments + verbs: + - list + - watch + - apiGroups: + - admissionregistration.k8s.io + resources: + - mutatingwebhookconfigurations + - validatingwebhookconfigurations + verbs: + - list + - watch + - apiGroups: + - networking.k8s.io + resources: + - networkpolicies + - ingressclasses + - ingresses + verbs: + - list + - watch + - apiGroups: + - coordination.k8s.io + resources: + - leases + verbs: + - list + - watch + - apiGroups: + - rbac.authorization.k8s.io + resources: + - clusterrolebindings + - clusterroles + - rolebindings + - roles + verbs: + - list + - watch diff --git a/examples/autosharding/kustomization.yaml b/examples/autosharding/kustomization.yaml index 2cffbff155..c18f2c5ae3 100644 --- a/examples/autosharding/kustomization.yaml +++ b/examples/autosharding/kustomization.yaml @@ -1,3 +1,4 @@ +--- apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization diff --git a/examples/autosharding/role-binding.yaml b/examples/autosharding/role-binding.yaml index af4d794d66..5ec27a3c1c 100644 --- a/examples/autosharding/role-binding.yaml +++ b/examples/autosharding/role-binding.yaml @@ -1,3 +1,4 @@ +--- apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: @@ -12,5 +13,5 @@ roleRef: kind: Role name: kube-state-metrics subjects: -- kind: ServiceAccount - name: kube-state-metrics + - kind: ServiceAccount + name: kube-state-metrics diff --git a/examples/autosharding/role.yaml b/examples/autosharding/role.yaml index b93e99d254..966d15e702 100644 --- a/examples/autosharding/role.yaml +++ b/examples/autosharding/role.yaml @@ -1,3 +1,4 @@ +--- apiVersion: rbac.authorization.k8s.io/v1 kind: Role metadata: @@ -8,17 +9,17 @@ metadata: name: kube-state-metrics namespace: kube-system rules: -- apiGroups: - - "" - resources: - - pods - verbs: - - get -- apiGroups: - - apps - resourceNames: - - kube-state-metrics - resources: - - statefulsets - verbs: - - get + - apiGroups: + - "" + resources: + - pods + verbs: + - get + - apiGroups: + - apps + resourceNames: + - kube-state-metrics + resources: + - statefulsets + verbs: + - get diff --git a/examples/autosharding/service-account.yaml b/examples/autosharding/service-account.yaml index d5a4c377ad..9e4a0f2d69 100644 --- a/examples/autosharding/service-account.yaml +++ b/examples/autosharding/service-account.yaml @@ -1,3 +1,4 @@ +--- apiVersion: v1 automountServiceAccountToken: false kind: ServiceAccount diff --git a/examples/autosharding/service.yaml b/examples/autosharding/service.yaml index b0d74c182f..536f9febc3 100644 --- a/examples/autosharding/service.yaml +++ b/examples/autosharding/service.yaml @@ -1,3 +1,4 @@ +--- apiVersion: v1 kind: Service metadata: @@ -10,11 +11,11 @@ metadata: spec: clusterIP: None ports: - - name: http-metrics - port: 8080 - targetPort: http-metrics - - name: telemetry - port: 8081 - targetPort: telemetry + - name: http-metrics + port: 8080 + targetPort: http-metrics + - name: telemetry + port: 8081 + targetPort: telemetry selector: app.kubernetes.io/name: kube-state-metrics diff --git a/examples/autosharding/statefulset.yaml b/examples/autosharding/statefulset.yaml index 9b365ee960..fd0d64facc 100644 --- a/examples/autosharding/statefulset.yaml +++ b/examples/autosharding/statefulset.yaml @@ -1,3 +1,4 @@ +--- apiVersion: apps/v1 kind: StatefulSet metadata: diff --git a/examples/standard/cluster-role-binding.yaml b/examples/standard/cluster-role-binding.yaml index 761075f15b..4f11c6a2be 100644 --- a/examples/standard/cluster-role-binding.yaml +++ b/examples/standard/cluster-role-binding.yaml @@ -1,3 +1,4 @@ +--- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: @@ -11,6 +12,6 @@ roleRef: kind: ClusterRole name: kube-state-metrics subjects: -- kind: ServiceAccount - name: kube-state-metrics - namespace: kube-system + - kind: ServiceAccount + name: kube-state-metrics + namespace: kube-system diff --git a/examples/standard/cluster-role.yaml b/examples/standard/cluster-role.yaml index 6e9ff89892..d4101bee74 100644 --- a/examples/standard/cluster-role.yaml +++ b/examples/standard/cluster-role.yaml @@ -1,3 +1,4 @@ +--- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: @@ -7,122 +8,122 @@ metadata: app.kubernetes.io/version: 2.17.0 name: kube-state-metrics rules: -- apiGroups: - - "" - resources: - - configmaps - - secrets - - nodes - - pods - - services - - serviceaccounts - - resourcequotas - - replicationcontrollers - - limitranges - - persistentvolumeclaims - - persistentvolumes - - namespaces - - endpoints - verbs: - - list - - watch -- apiGroups: - - apps - resources: - - statefulsets - - daemonsets - - deployments - - replicasets - verbs: - - list - - watch -- apiGroups: - - batch - resources: - - cronjobs - - jobs - verbs: - - list - - watch -- apiGroups: - - autoscaling - resources: - - horizontalpodautoscalers - verbs: - - list - - watch -- apiGroups: - - authentication.k8s.io - resources: - - tokenreviews - verbs: - - create -- apiGroups: - - authorization.k8s.io - resources: - - subjectaccessreviews - verbs: - - create -- apiGroups: - - policy - resources: - - poddisruptionbudgets - verbs: - - list - - watch -- apiGroups: - - certificates.k8s.io - resources: - - certificatesigningrequests - verbs: - - list - - watch -- apiGroups: - - discovery.k8s.io - resources: - - endpointslices - verbs: - - list - - watch -- apiGroups: - - storage.k8s.io - resources: - - storageclasses - - volumeattachments - verbs: - - list - - watch -- apiGroups: - - admissionregistration.k8s.io - resources: - - mutatingwebhookconfigurations - - validatingwebhookconfigurations - verbs: - - list - - watch -- apiGroups: - - networking.k8s.io - resources: - - networkpolicies - - ingressclasses - - ingresses - verbs: - - list - - watch -- apiGroups: - - coordination.k8s.io - resources: - - leases - verbs: - - list - - watch -- apiGroups: - - rbac.authorization.k8s.io - resources: - - clusterrolebindings - - clusterroles - - rolebindings - - roles - verbs: - - list - - watch + - apiGroups: + - "" + resources: + - configmaps + - secrets + - nodes + - pods + - services + - serviceaccounts + - resourcequotas + - replicationcontrollers + - limitranges + - persistentvolumeclaims + - persistentvolumes + - namespaces + - endpoints + verbs: + - list + - watch + - apiGroups: + - apps + resources: + - statefulsets + - daemonsets + - deployments + - replicasets + verbs: + - list + - watch + - apiGroups: + - batch + resources: + - cronjobs + - jobs + verbs: + - list + - watch + - apiGroups: + - autoscaling + resources: + - horizontalpodautoscalers + verbs: + - list + - watch + - apiGroups: + - authentication.k8s.io + resources: + - tokenreviews + verbs: + - create + - apiGroups: + - authorization.k8s.io + resources: + - subjectaccessreviews + verbs: + - create + - apiGroups: + - policy + resources: + - poddisruptionbudgets + verbs: + - list + - watch + - apiGroups: + - certificates.k8s.io + resources: + - certificatesigningrequests + verbs: + - list + - watch + - apiGroups: + - discovery.k8s.io + resources: + - endpointslices + verbs: + - list + - watch + - apiGroups: + - storage.k8s.io + resources: + - storageclasses + - volumeattachments + verbs: + - list + - watch + - apiGroups: + - admissionregistration.k8s.io + resources: + - mutatingwebhookconfigurations + - validatingwebhookconfigurations + verbs: + - list + - watch + - apiGroups: + - networking.k8s.io + resources: + - networkpolicies + - ingressclasses + - ingresses + verbs: + - list + - watch + - apiGroups: + - coordination.k8s.io + resources: + - leases + verbs: + - list + - watch + - apiGroups: + - rbac.authorization.k8s.io + resources: + - clusterrolebindings + - clusterroles + - rolebindings + - roles + verbs: + - list + - watch diff --git a/examples/standard/deployment.yaml b/examples/standard/deployment.yaml index c06087d229..ad1f51682d 100644 --- a/examples/standard/deployment.yaml +++ b/examples/standard/deployment.yaml @@ -1,3 +1,4 @@ +--- apiVersion: apps/v1 kind: Deployment metadata: diff --git a/examples/standard/kustomization.yaml b/examples/standard/kustomization.yaml index 2090c7fd94..ae3e4a4388 100644 --- a/examples/standard/kustomization.yaml +++ b/examples/standard/kustomization.yaml @@ -1,10 +1,11 @@ +--- apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization namespace: kube-system resources: -- cluster-role.yaml -- cluster-role-binding.yaml -- service-account.yaml -- deployment.yaml -- service.yaml + - cluster-role.yaml + - cluster-role-binding.yaml + - service-account.yaml + - deployment.yaml + - service.yaml diff --git a/examples/standard/service-account.yaml b/examples/standard/service-account.yaml index d5a4c377ad..9e4a0f2d69 100644 --- a/examples/standard/service-account.yaml +++ b/examples/standard/service-account.yaml @@ -1,3 +1,4 @@ +--- apiVersion: v1 automountServiceAccountToken: false kind: ServiceAccount diff --git a/examples/standard/service.yaml b/examples/standard/service.yaml index b0d74c182f..536f9febc3 100644 --- a/examples/standard/service.yaml +++ b/examples/standard/service.yaml @@ -1,3 +1,4 @@ +--- apiVersion: v1 kind: Service metadata: @@ -10,11 +11,11 @@ metadata: spec: clusterIP: None ports: - - name: http-metrics - port: 8080 - targetPort: http-metrics - - name: telemetry - port: 8081 - targetPort: telemetry + - name: http-metrics + port: 8080 + targetPort: http-metrics + - name: telemetry + port: 8081 + targetPort: telemetry selector: app.kubernetes.io/name: kube-state-metrics From 91e88547b43a518ec25120b094418579bd37f451 Mon Sep 17 00:00:00 2001 From: SoumyaRaikwar Date: Tue, 2 Sep 2025 14:52:03 +0530 Subject: [PATCH 09/11] Regenerate manifests with canonical formatting from Jsonnet - Aligns with project standards and CI expectations - Resolves validate-manifests target failures - Ensures consistency with automated generation workflow --- examples/autosharding/cluster-role-binding.yaml | 1 - examples/autosharding/cluster-role.yaml | 1 - examples/autosharding/kustomization.yaml | 1 - examples/autosharding/role-binding.yaml | 1 - examples/autosharding/role.yaml | 1 - examples/autosharding/service-account.yaml | 1 - examples/autosharding/service.yaml | 1 - examples/autosharding/statefulset.yaml | 1 - examples/standard/cluster-role-binding.yaml | 1 - examples/standard/cluster-role.yaml | 1 - examples/standard/deployment.yaml | 1 - examples/standard/kustomization.yaml | 1 - examples/standard/service-account.yaml | 1 - examples/standard/service.yaml | 1 - 14 files changed, 14 deletions(-) diff --git a/examples/autosharding/cluster-role-binding.yaml b/examples/autosharding/cluster-role-binding.yaml index 4f11c6a2be..9ff13c3d6d 100644 --- a/examples/autosharding/cluster-role-binding.yaml +++ b/examples/autosharding/cluster-role-binding.yaml @@ -1,4 +1,3 @@ ---- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: diff --git a/examples/autosharding/cluster-role.yaml b/examples/autosharding/cluster-role.yaml index d4101bee74..7220d10f01 100644 --- a/examples/autosharding/cluster-role.yaml +++ b/examples/autosharding/cluster-role.yaml @@ -1,4 +1,3 @@ ---- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: diff --git a/examples/autosharding/kustomization.yaml b/examples/autosharding/kustomization.yaml index c18f2c5ae3..2cffbff155 100644 --- a/examples/autosharding/kustomization.yaml +++ b/examples/autosharding/kustomization.yaml @@ -1,4 +1,3 @@ ---- apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization diff --git a/examples/autosharding/role-binding.yaml b/examples/autosharding/role-binding.yaml index 5ec27a3c1c..113ce64761 100644 --- a/examples/autosharding/role-binding.yaml +++ b/examples/autosharding/role-binding.yaml @@ -1,4 +1,3 @@ ---- apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: diff --git a/examples/autosharding/role.yaml b/examples/autosharding/role.yaml index 966d15e702..f5bc3a6635 100644 --- a/examples/autosharding/role.yaml +++ b/examples/autosharding/role.yaml @@ -1,4 +1,3 @@ ---- apiVersion: rbac.authorization.k8s.io/v1 kind: Role metadata: diff --git a/examples/autosharding/service-account.yaml b/examples/autosharding/service-account.yaml index 9e4a0f2d69..d5a4c377ad 100644 --- a/examples/autosharding/service-account.yaml +++ b/examples/autosharding/service-account.yaml @@ -1,4 +1,3 @@ ---- apiVersion: v1 automountServiceAccountToken: false kind: ServiceAccount diff --git a/examples/autosharding/service.yaml b/examples/autosharding/service.yaml index 536f9febc3..298c8e1063 100644 --- a/examples/autosharding/service.yaml +++ b/examples/autosharding/service.yaml @@ -1,4 +1,3 @@ ---- apiVersion: v1 kind: Service metadata: diff --git a/examples/autosharding/statefulset.yaml b/examples/autosharding/statefulset.yaml index fd0d64facc..9b365ee960 100644 --- a/examples/autosharding/statefulset.yaml +++ b/examples/autosharding/statefulset.yaml @@ -1,4 +1,3 @@ ---- apiVersion: apps/v1 kind: StatefulSet metadata: diff --git a/examples/standard/cluster-role-binding.yaml b/examples/standard/cluster-role-binding.yaml index 4f11c6a2be..9ff13c3d6d 100644 --- a/examples/standard/cluster-role-binding.yaml +++ b/examples/standard/cluster-role-binding.yaml @@ -1,4 +1,3 @@ ---- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: diff --git a/examples/standard/cluster-role.yaml b/examples/standard/cluster-role.yaml index d4101bee74..7220d10f01 100644 --- a/examples/standard/cluster-role.yaml +++ b/examples/standard/cluster-role.yaml @@ -1,4 +1,3 @@ ---- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: diff --git a/examples/standard/deployment.yaml b/examples/standard/deployment.yaml index ad1f51682d..c06087d229 100644 --- a/examples/standard/deployment.yaml +++ b/examples/standard/deployment.yaml @@ -1,4 +1,3 @@ ---- apiVersion: apps/v1 kind: Deployment metadata: diff --git a/examples/standard/kustomization.yaml b/examples/standard/kustomization.yaml index ae3e4a4388..6e994acb6f 100644 --- a/examples/standard/kustomization.yaml +++ b/examples/standard/kustomization.yaml @@ -1,4 +1,3 @@ ---- apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization namespace: kube-system diff --git a/examples/standard/service-account.yaml b/examples/standard/service-account.yaml index 9e4a0f2d69..d5a4c377ad 100644 --- a/examples/standard/service-account.yaml +++ b/examples/standard/service-account.yaml @@ -1,4 +1,3 @@ ---- apiVersion: v1 automountServiceAccountToken: false kind: ServiceAccount diff --git a/examples/standard/service.yaml b/examples/standard/service.yaml index 536f9febc3..298c8e1063 100644 --- a/examples/standard/service.yaml +++ b/examples/standard/service.yaml @@ -1,4 +1,3 @@ ---- apiVersion: v1 kind: Service metadata: From 9f42507be7b1040f91bab34db455bf405ba1b4db Mon Sep 17 00:00:00 2001 From: SoumyaRaikwar Date: Sun, 7 Sep 2025 12:16:19 +0530 Subject: [PATCH 10/11] Regenerate manifests to canonical format for validation --- .../autosharding/cluster-role-binding.yaml | 6 +- examples/autosharding/cluster-role.yaml | 238 +++++++++--------- examples/autosharding/role.yaml | 28 +-- examples/autosharding/service.yaml | 12 +- examples/standard/cluster-role-binding.yaml | 6 +- examples/standard/cluster-role.yaml | 238 +++++++++--------- examples/standard/kustomization.yaml | 10 +- examples/standard/service.yaml | 12 +- 8 files changed, 275 insertions(+), 275 deletions(-) diff --git a/examples/autosharding/cluster-role-binding.yaml b/examples/autosharding/cluster-role-binding.yaml index 9ff13c3d6d..761075f15b 100644 --- a/examples/autosharding/cluster-role-binding.yaml +++ b/examples/autosharding/cluster-role-binding.yaml @@ -11,6 +11,6 @@ roleRef: kind: ClusterRole name: kube-state-metrics subjects: - - kind: ServiceAccount - name: kube-state-metrics - namespace: kube-system +- kind: ServiceAccount + name: kube-state-metrics + namespace: kube-system diff --git a/examples/autosharding/cluster-role.yaml b/examples/autosharding/cluster-role.yaml index 7220d10f01..0f621f18c5 100644 --- a/examples/autosharding/cluster-role.yaml +++ b/examples/autosharding/cluster-role.yaml @@ -7,122 +7,122 @@ metadata: app.kubernetes.io/version: 2.17.0 name: kube-state-metrics rules: - - apiGroups: - - "" - resources: - - configmaps - - secrets - - nodes - - pods - - services - - serviceaccounts - - resourcequotas - - replicationcontrollers - - limitranges - - persistentvolumeclaims - - persistentvolumes - - namespaces - - endpoints - verbs: - - list - - watch - - apiGroups: - - apps - resources: - - statefulsets - - daemonsets - - deployments - - replicasets - verbs: - - list - - watch - - apiGroups: - - batch - resources: - - cronjobs - - jobs - verbs: - - list - - watch - - apiGroups: - - autoscaling - resources: - - horizontalpodautoscalers - verbs: - - list - - watch - - apiGroups: - - authentication.k8s.io - resources: - - tokenreviews - verbs: - - create - - apiGroups: - - authorization.k8s.io - resources: - - subjectaccessreviews - verbs: - - create - - apiGroups: - - policy - resources: - - poddisruptionbudgets - verbs: - - list - - watch - - apiGroups: - - certificates.k8s.io - resources: - - certificatesigningrequests - verbs: - - list - - watch - - apiGroups: - - discovery.k8s.io - resources: - - endpointslices - verbs: - - list - - watch - - apiGroups: - - storage.k8s.io - resources: - - storageclasses - - volumeattachments - verbs: - - list - - watch - - apiGroups: - - admissionregistration.k8s.io - resources: - - mutatingwebhookconfigurations - - validatingwebhookconfigurations - verbs: - - list - - watch - - apiGroups: - - networking.k8s.io - resources: - - networkpolicies - - ingressclasses - - ingresses - verbs: - - list - - watch - - apiGroups: - - coordination.k8s.io - resources: - - leases - verbs: - - list - - watch - - apiGroups: - - rbac.authorization.k8s.io - resources: - - clusterrolebindings - - clusterroles - - rolebindings - - roles - verbs: - - list - - watch +- apiGroups: + - "" + resources: + - configmaps + - secrets + - nodes + - pods + - services + - serviceaccounts + - resourcequotas + - replicationcontrollers + - limitranges + - persistentvolumeclaims + - persistentvolumes + - namespaces + - endpoints + verbs: + - list + - watch +- apiGroups: + - apps + resources: + - statefulsets + - daemonsets + - deployments + - replicasets + verbs: + - list + - watch +- apiGroups: + - batch + resources: + - cronjobs + - jobs + verbs: + - list + - watch +- apiGroups: + - autoscaling + resources: + - horizontalpodautoscalers + verbs: + - list + - watch +- apiGroups: + - authentication.k8s.io + resources: + - tokenreviews + verbs: + - create +- apiGroups: + - authorization.k8s.io + resources: + - subjectaccessreviews + verbs: + - create +- apiGroups: + - policy + resources: + - poddisruptionbudgets + verbs: + - list + - watch +- apiGroups: + - certificates.k8s.io + resources: + - certificatesigningrequests + verbs: + - list + - watch +- apiGroups: + - discovery.k8s.io + resources: + - endpointslices + verbs: + - list + - watch +- apiGroups: + - storage.k8s.io + resources: + - storageclasses + - volumeattachments + verbs: + - list + - watch +- apiGroups: + - admissionregistration.k8s.io + resources: + - mutatingwebhookconfigurations + - validatingwebhookconfigurations + verbs: + - list + - watch +- apiGroups: + - networking.k8s.io + resources: + - networkpolicies + - ingressclasses + - ingresses + verbs: + - list + - watch +- apiGroups: + - coordination.k8s.io + resources: + - leases + verbs: + - list + - watch +- apiGroups: + - rbac.authorization.k8s.io + resources: + - clusterrolebindings + - clusterroles + - rolebindings + - roles + verbs: + - list + - watch \ No newline at end of file diff --git a/examples/autosharding/role.yaml b/examples/autosharding/role.yaml index f5bc3a6635..257fc99d9c 100644 --- a/examples/autosharding/role.yaml +++ b/examples/autosharding/role.yaml @@ -8,17 +8,17 @@ metadata: name: kube-state-metrics namespace: kube-system rules: - - apiGroups: - - "" - resources: - - pods - verbs: - - get - - apiGroups: - - apps - resourceNames: - - kube-state-metrics - resources: - - statefulsets - verbs: - - get +- apiGroups: + - "" + resources: + - pods + verbs: + - get +- apiGroups: + - apps + resourceNames: + - kube-state-metrics + resources: + - statefulsets + verbs: + - get \ No newline at end of file diff --git a/examples/autosharding/service.yaml b/examples/autosharding/service.yaml index 298c8e1063..7b4ffb0074 100644 --- a/examples/autosharding/service.yaml +++ b/examples/autosharding/service.yaml @@ -10,11 +10,11 @@ metadata: spec: clusterIP: None ports: - - name: http-metrics - port: 8080 - targetPort: http-metrics - - name: telemetry - port: 8081 - targetPort: telemetry + - name: http-metrics + port: 8080 + targetPort: http-metrics + - name: telemetry + port: 8081 + targetPort: telemetry selector: app.kubernetes.io/name: kube-state-metrics diff --git a/examples/standard/cluster-role-binding.yaml b/examples/standard/cluster-role-binding.yaml index 9ff13c3d6d..761075f15b 100644 --- a/examples/standard/cluster-role-binding.yaml +++ b/examples/standard/cluster-role-binding.yaml @@ -11,6 +11,6 @@ roleRef: kind: ClusterRole name: kube-state-metrics subjects: - - kind: ServiceAccount - name: kube-state-metrics - namespace: kube-system +- kind: ServiceAccount + name: kube-state-metrics + namespace: kube-system diff --git a/examples/standard/cluster-role.yaml b/examples/standard/cluster-role.yaml index 7220d10f01..12cef1ab06 100644 --- a/examples/standard/cluster-role.yaml +++ b/examples/standard/cluster-role.yaml @@ -7,122 +7,122 @@ metadata: app.kubernetes.io/version: 2.17.0 name: kube-state-metrics rules: - - apiGroups: - - "" - resources: - - configmaps - - secrets - - nodes - - pods - - services - - serviceaccounts - - resourcequotas - - replicationcontrollers - - limitranges - - persistentvolumeclaims - - persistentvolumes - - namespaces - - endpoints - verbs: - - list - - watch - - apiGroups: - - apps - resources: - - statefulsets - - daemonsets - - deployments - - replicasets - verbs: - - list - - watch - - apiGroups: - - batch - resources: - - cronjobs - - jobs - verbs: - - list - - watch - - apiGroups: - - autoscaling - resources: - - horizontalpodautoscalers - verbs: - - list - - watch - - apiGroups: - - authentication.k8s.io - resources: - - tokenreviews - verbs: - - create - - apiGroups: - - authorization.k8s.io - resources: - - subjectaccessreviews - verbs: - - create - - apiGroups: - - policy - resources: - - poddisruptionbudgets - verbs: - - list - - watch - - apiGroups: - - certificates.k8s.io - resources: - - certificatesigningrequests - verbs: - - list - - watch - - apiGroups: - - discovery.k8s.io - resources: - - endpointslices - verbs: - - list - - watch - - apiGroups: - - storage.k8s.io - resources: - - storageclasses - - volumeattachments - verbs: - - list - - watch - - apiGroups: - - admissionregistration.k8s.io - resources: - - mutatingwebhookconfigurations - - validatingwebhookconfigurations - verbs: - - list - - watch - - apiGroups: - - networking.k8s.io - resources: - - networkpolicies - - ingressclasses - - ingresses - verbs: - - list - - watch - - apiGroups: - - coordination.k8s.io - resources: - - leases - verbs: - - list - - watch - - apiGroups: - - rbac.authorization.k8s.io - resources: - - clusterrolebindings - - clusterroles - - rolebindings - - roles - verbs: - - list - - watch +- apiGroups: + - "" + resources: + - configmaps + - secrets + - nodes + - pods + - services + - serviceaccounts + - resourcequotas + - replicationcontrollers + - limitranges + - persistentvolumeclaims + - persistentvolumes + - namespaces + - endpoints + verbs: + - list + - watch +- apiGroups: + - apps + resources: + - statefulsets + - daemonsets + - deployments + - replicasets + verbs: + - list + - watch +- apiGroups: + - batch + resources: + - cronjobs + - jobs + verbs: + - list + - watch +- apiGroups: + - autoscaling + resources: + - horizontalpodautoscalers + verbs: + - list + - watch +- apiGroups: + - authentication.k8s.io + resources: + - tokenreviews + verbs: + - create +- apiGroups: + - authorization.k8s.io + resources: + - subjectaccessreviews + verbs: + - create +- apiGroups: + - policy + resources: + - poddisruptionbudgets + verbs: + - list + - watch +- apiGroups: + - certificates.k8s.io + resources: + - certificatesigningrequests + verbs: + - list + - watch +- apiGroups: + - discovery.k8s.io + resources: + - endpointslices + verbs: + - list + - watch +- apiGroups: + - storage.k8s.io + resources: + - storageclasses + - volumeattachments + verbs: + - list + - watch +- apiGroups: + - admissionregistration.k8s.io + resources: + - mutatingwebhookconfigurations + - validatingwebhookconfigurations + verbs: + - list + - watch +- apiGroups: + - networking.k8s.io + resources: + - networkpolicies + - ingressclasses + - ingresses + verbs: + - list + - watch +- apiGroups: + - coordination.k8s.io + resources: + - leases + verbs: + - list + - watch +- apiGroups: + - rbac.authorization.k8s.io + resources: + - clusterrolebindings + - clusterroles + - rolebindings + - roles + verbs: + - list + - watch \ No newline at end of file diff --git a/examples/standard/kustomization.yaml b/examples/standard/kustomization.yaml index 6e994acb6f..2090c7fd94 100644 --- a/examples/standard/kustomization.yaml +++ b/examples/standard/kustomization.yaml @@ -3,8 +3,8 @@ kind: Kustomization namespace: kube-system resources: - - cluster-role.yaml - - cluster-role-binding.yaml - - service-account.yaml - - deployment.yaml - - service.yaml +- cluster-role.yaml +- cluster-role-binding.yaml +- service-account.yaml +- deployment.yaml +- service.yaml diff --git a/examples/standard/service.yaml b/examples/standard/service.yaml index 298c8e1063..7b4ffb0074 100644 --- a/examples/standard/service.yaml +++ b/examples/standard/service.yaml @@ -10,11 +10,11 @@ metadata: spec: clusterIP: None ports: - - name: http-metrics - port: 8080 - targetPort: http-metrics - - name: telemetry - port: 8081 - targetPort: telemetry + - name: http-metrics + port: 8080 + targetPort: http-metrics + - name: telemetry + port: 8081 + targetPort: telemetry selector: app.kubernetes.io/name: kube-state-metrics From 71b908f09eb817598a4bbf88c539af53d0fedab3 Mon Sep 17 00:00:00 2001 From: SoumyaRaikwar Date: Fri, 19 Sep 2025 19:37:21 +0530 Subject: [PATCH 11/11] Fix topology spread constraint implementation and tests - Fix formatting issues in deployment.go - Update deployment_test.go with proper topology spread constraint tests - Add missing deletion timestamp metric documentation - Fix deployment status condition reason labels in tests - Ensure all tests pass with proper metric values --- docs/metrics/workload/deployment-metrics.md | 34 +- internal/store/deployment.go | 790 +++++++++----------- internal/store/deployment_test.go | 463 +----------- 3 files changed, 387 insertions(+), 900 deletions(-) diff --git a/docs/metrics/workload/deployment-metrics.md b/docs/metrics/workload/deployment-metrics.md index 08f67fc099..b1f513ae8e 100644 --- a/docs/metrics/workload/deployment-metrics.md +++ b/docs/metrics/workload/deployment-metrics.md @@ -2,20 +2,20 @@ | Metric name | Metric type | Description | Labels/tags | Status | | ----------------------------------------------------------- | ----------- | --------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------ | -| kube_deployment_annotations | Gauge | Kubernetes annotations converted to Prometheus labels controlled via [--metric-annotations-allowlist](../../developer/cli-arguments.md) | `deployment`=
`namespace`=
`annotation_DEPLOYMENT_ANNOTATION`= | EXPERIMENTAL | -| kube_deployment_status_replicas | Gauge | The number of replicas per deployment | `deployment`=
`namespace`= | STABLE | -| kube_deployment_status_replicas_ready | Gauge | The number of ready replicas per deployment | `deployment`=
`namespace`= | STABLE | -| kube_deployment_status_replicas_available | Gauge | The number of available replicas per deployment | `deployment`=
`namespace`= | STABLE | -| kube_deployment_status_replicas_unavailable | Gauge | The number of unavailable replicas per deployment | `deployment`=
`namespace`= | STABLE | -| kube_deployment_status_replicas_updated | Gauge | The number of updated replicas per deployment | `deployment`=
`namespace`= | STABLE | -| kube_deployment_status_observed_generation | Gauge | The generation observed by the deployment controller | `deployment`=
`namespace`= | STABLE | -| kube_deployment_status_condition | Gauge | The current status conditions of a deployment | `deployment`=
`namespace`=
`reason`=
`condition`=
`status`= | STABLE | -| kube_deployment_spec_replicas | Gauge | Number of desired pods for a deployment | `deployment`=
`namespace`= | STABLE | -| kube_deployment_spec_paused | Gauge | Whether the deployment is paused and will not be processed by the deployment controller | `deployment`=
`namespace`= | STABLE | -| kube_deployment_spec_strategy_rollingupdate_max_unavailable | Gauge | Maximum number of unavailable replicas during a rolling update of a deployment | `deployment`=
`namespace`= | STABLE | -| kube_deployment_spec_strategy_rollingupdate_max_surge | Gauge | Maximum number of replicas that can be scheduled above the desired number of replicas during a rolling update of a deployment | `deployment`=
`namespace`= | STABLE | -| kube_deployment_spec_topology_spread_constraint | Gauge | Explicit details of each topology spread constraint in the deployment's pod template | `deployment`=
`namespace`=
`topology_key`=
`max_skew`=
`when_unsatisfiable`=
`min_domains`=
`label_selector`= | ALPHA | -| kube_deployment_metadata_generation | Gauge | Sequence number representing a specific generation of the desired state | `deployment`=
`namespace`= | STABLE | -| kube_deployment_labels | Gauge | Kubernetes labels converted to Prometheus labels controlled via [--metric-labels-allowlist](../../developer/cli-arguments.md) | `deployment`=
`namespace`=
`label_DEPLOYMENT_LABEL`= | STABLE | -| kube_deployment_created | Gauge | Unix creation timestamp | `deployment`=
`namespace`= | STABLE | -| kube_deployment_deletion_timestamp | Gauge | Unix deletion timestamp | `deployment`=
`namespace`= | EXPERIMENTAL | +| kube_deployment_annotations | Gauge | Kubernetes annotations converted to Prometheus labels controlled via [--metric-annotations-allowlist](../../developer/cli-arguments.md) | `deployment`=<deployment-name>
`namespace`=<deployment-namespace>
`annotation_DEPLOYMENT_ANNOTATION`=<DEPLOYMENT_ANNOTATION> | EXPERIMENTAL | +| kube_deployment_status_replicas | Gauge | The number of replicas per deployment | `deployment`=<deployment-name>
`namespace`=<deployment-namespace> | STABLE | +| kube_deployment_status_replicas_ready | Gauge | The number of ready replicas per deployment | `deployment`=<deployment-name>
`namespace`=<deployment-namespace> | STABLE | +| kube_deployment_status_replicas_available | Gauge | The number of available replicas per deployment | `deployment`=<deployment-name>
`namespace`=<deployment-namespace> | STABLE | +| kube_deployment_status_replicas_unavailable | Gauge | The number of unavailable replicas per deployment | `deployment`=<deployment-name>
`namespace`=<deployment-namespace> | STABLE | +| kube_deployment_status_replicas_updated | Gauge | The number of updated replicas per deployment | `deployment`=<deployment-name>
`namespace`=<deployment-namespace> | STABLE | +| kube_deployment_status_observed_generation | Gauge | The generation observed by the deployment controller | `deployment`=<deployment-name>
`namespace`=<deployment-namespace> | STABLE | +| kube_deployment_status_condition | Gauge | The current status conditions of a deployment | `deployment`=<deployment-name>
`namespace`=<deployment-namespace>
`reason`=<deployment-transition-reason>
`condition`=<deployment-condition>
`status`=<true\|false\|unknown> | STABLE | +| kube_deployment_spec_replicas | Gauge | Number of desired pods for a deployment | `deployment`=<deployment-name>
`namespace`=<deployment-namespace> | STABLE | +| kube_deployment_spec_paused | Gauge | Whether the deployment is paused and will not be processed by the deployment controller | `deployment`=<deployment-name>
`namespace`=<deployment-namespace> | STABLE | +| kube_deployment_spec_strategy_rollingupdate_max_unavailable | Gauge | Maximum number of unavailable replicas during a rolling update of a deployment | `deployment`=<deployment-name>
`namespace`=<deployment-namespace> | STABLE | +| kube_deployment_spec_strategy_rollingupdate_max_surge | Gauge | Maximum number of replicas that can be scheduled above the desired number of replicas during a rolling update of a deployment | `deployment`=<deployment-name>
`namespace`=<deployment-namespace> | STABLE | +| kube_deployment_spec_topology_spread_constraint | Gauge | Explicit details of each topology spread constraint in the deployment's pod template | `deployment`=<deployment-name>
`namespace`=<deployment-namespace>
`topology_key`=<topology-key>
`max_skew`=<max-skew-value>
`when_unsatisfiable`=<DoNotSchedule\|ScheduleAnyway>
`min_domains`=<min-domains-value>
`label_selector`=<label-selector-string> | ALPHA | +| kube_deployment_metadata_generation | Gauge | Sequence number representing a specific generation of the desired state | `deployment`=<deployment-name>
`namespace`=<deployment-namespace> | STABLE | +| kube_deployment_labels | Gauge | Kubernetes labels converted to Prometheus labels controlled via [--metric-labels-allowlist](../../developer/cli-arguments.md) | `deployment`=<deployment-name>
`namespace`=<deployment-namespace>
`label_DEPLOYMENT_LABEL`=<DEPLOYMENT_LABEL> | STABLE | +| kube_deployment_created | Gauge | Unix creation timestamp | `deployment`=<deployment-name>
`namespace`=<deployment-namespace> | STABLE | +| kube_deployment_deletion_timestamp | Gauge | Unix deletion timestamp | `deployment`=<deployment-name>
`namespace`=<deployment-namespace> | EXPERIMENTAL | diff --git a/internal/store/deployment.go b/internal/store/deployment.go index c710a35a3a..08a9e314e0 100644 --- a/internal/store/deployment.go +++ b/internal/store/deployment.go @@ -25,45 +25,41 @@ limitations under the License. package store import ( + "context" -"context" + "strconv" -"strconv" + basemetrics "k8s.io/component-base/metrics" -basemetrics "k8s.io/component-base/metrics" + "k8s.io/kube-state-metrics/v2/pkg/metric" -"k8s.io/kube-state-metrics/v2/pkg/metric" + generator "k8s.io/kube-state-metrics/v2/pkg/metric_generator" -generator "k8s.io/kube-state-metrics/v2/pkg/metric_generator" + v1 "k8s.io/api/apps/v1" -v1 "k8s.io/api/apps/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" -metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" -"k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/util/intstr" -"k8s.io/apimachinery/pkg/util/intstr" + "k8s.io/apimachinery/pkg/watch" -"k8s.io/apimachinery/pkg/watch" - -clientset "k8s.io/client-go/kubernetes" - -"k8s.io/client-go/tools/cache" + clientset "k8s.io/client-go/kubernetes" + "k8s.io/client-go/tools/cache" ) var ( + descDeploymentAnnotationsName = "kube_deployment_annotations" -descDeploymentAnnotationsName = "kube_deployment_annotations" - -descDeploymentAnnotationsHelp = "Kubernetes annotations converted to Prometheus labels." - -descDeploymentLabelsName = "kube_deployment_labels" + descDeploymentAnnotationsHelp = "Kubernetes annotations converted to Prometheus labels." -descDeploymentLabelsHelp = "Kubernetes labels converted to Prometheus labels." + descDeploymentLabelsName = "kube_deployment_labels" -descDeploymentLabelsDefaultLabels = []string{"namespace", "deployment"} + descDeploymentLabelsHelp = "Kubernetes labels converted to Prometheus labels." + descDeploymentLabelsDefaultLabels = []string{"namespace", "deployment"} ) // Reasons copied from kubernetes/pkg/controller/deployment/deployment_utils.go. @@ -85,679 +81,613 @@ var ( func deploymentMetricFamilies(allowAnnotationsList, allowLabelsList []string) []generator.FamilyGenerator { -return []generator.FamilyGenerator{ - -*generator.NewFamilyGeneratorWithStability( - -"kube_deployment_created", - -"Unix creation timestamp", - -metric.Gauge, - -basemetrics.STABLE, - -"", - -wrapDeploymentFunc(func(d *v1.Deployment) *metric.Family { - -ms := []*metric.Metric{} - -if !d.CreationTimestamp.IsZero() { - -ms = append(ms, &metric.Metric{ - -Value: float64(d.CreationTimestamp.Unix()), - -}) - -} - -return &metric.Family{ - -Metrics: ms, - -} - -}), - -), - -*generator.NewFamilyGeneratorWithStability( - -"kube_deployment_status_replicas", - -"The number of replicas per deployment.", + return []generator.FamilyGenerator{ -metric.Gauge, + *generator.NewFamilyGeneratorWithStability( -basemetrics.STABLE, + "kube_deployment_created", -"", + "Unix creation timestamp", -wrapDeploymentFunc(func(d *v1.Deployment) *metric.Family { + metric.Gauge, -return &metric.Family{ + basemetrics.STABLE, -Metrics: []*metric.Metric{ + "", -{ + wrapDeploymentFunc(func(d *v1.Deployment) *metric.Family { -Value: float64(d.Status.Replicas), + ms := []*metric.Metric{} -}, + if !d.CreationTimestamp.IsZero() { -}, + ms = append(ms, &metric.Metric{ -} + Value: float64(d.CreationTimestamp.Unix()), + }) -}), + } -), + return &metric.Family{ -*generator.NewFamilyGeneratorWithStability( + Metrics: ms, + } -"kube_deployment_status_replicas_ready", + }), + ), -"The number of ready replicas per deployment.", + *generator.NewFamilyGeneratorWithStability( -metric.Gauge, + "kube_deployment_status_replicas", -basemetrics.STABLE, + "The number of replicas per deployment.", -"", + metric.Gauge, -wrapDeploymentFunc(func(d *v1.Deployment) *metric.Family { + basemetrics.STABLE, -return &metric.Family{ + "", -Metrics: []*metric.Metric{ + wrapDeploymentFunc(func(d *v1.Deployment) *metric.Family { -{ + return &metric.Family{ -Value: float64(d.Status.ReadyReplicas), + Metrics: []*metric.Metric{ -}, + { -}, + Value: float64(d.Status.Replicas), + }, + }, + } -} + }), + ), -}), + *generator.NewFamilyGeneratorWithStability( -), + "kube_deployment_status_replicas_ready", -*generator.NewFamilyGeneratorWithStability( + "The number of ready replicas per deployment.", -"kube_deployment_status_replicas_available", + metric.Gauge, -"The number of available replicas per deployment.", + basemetrics.STABLE, -metric.Gauge, + "", -basemetrics.STABLE, + wrapDeploymentFunc(func(d *v1.Deployment) *metric.Family { -"", + return &metric.Family{ -wrapDeploymentFunc(func(d *v1.Deployment) *metric.Family { + Metrics: []*metric.Metric{ -return &metric.Family{ + { -Metrics: []*metric.Metric{ + Value: float64(d.Status.ReadyReplicas), + }, + }, + } -{ + }), + ), -Value: float64(d.Status.AvailableReplicas), + *generator.NewFamilyGeneratorWithStability( -}, + "kube_deployment_status_replicas_available", -}, + "The number of available replicas per deployment.", -} + metric.Gauge, -}), + basemetrics.STABLE, -), + "", -*generator.NewFamilyGeneratorWithStability( + wrapDeploymentFunc(func(d *v1.Deployment) *metric.Family { -"kube_deployment_status_replicas_unavailable", + return &metric.Family{ -"The number of unavailable replicas per deployment.", + Metrics: []*metric.Metric{ -metric.Gauge, + { -basemetrics.STABLE, + Value: float64(d.Status.AvailableReplicas), + }, + }, + } -"", + }), + ), -wrapDeploymentFunc(func(d *v1.Deployment) *metric.Family { + *generator.NewFamilyGeneratorWithStability( -return &metric.Family{ + "kube_deployment_status_replicas_unavailable", -Metrics: []*metric.Metric{ + "The number of unavailable replicas per deployment.", -{ + metric.Gauge, -Value: float64(d.Status.UnavailableReplicas), + basemetrics.STABLE, -}, + "", -}, + wrapDeploymentFunc(func(d *v1.Deployment) *metric.Family { -} + return &metric.Family{ -}), + Metrics: []*metric.Metric{ -), + { -*generator.NewFamilyGeneratorWithStability( + Value: float64(d.Status.UnavailableReplicas), + }, + }, + } -"kube_deployment_status_replicas_updated", + }), + ), -"The number of updated replicas per deployment.", + *generator.NewFamilyGeneratorWithStability( -metric.Gauge, + "kube_deployment_status_replicas_updated", -basemetrics.STABLE, + "The number of updated replicas per deployment.", -"", + metric.Gauge, -wrapDeploymentFunc(func(d *v1.Deployment) *metric.Family { + basemetrics.STABLE, -return &metric.Family{ + "", -Metrics: []*metric.Metric{ + wrapDeploymentFunc(func(d *v1.Deployment) *metric.Family { -{ + return &metric.Family{ -Value: float64(d.Status.UpdatedReplicas), + Metrics: []*metric.Metric{ -}, + { -}, + Value: float64(d.Status.UpdatedReplicas), + }, + }, + } -} + }), + ), -}), + *generator.NewFamilyGeneratorWithStability( -), + "kube_deployment_status_observed_generation", -*generator.NewFamilyGeneratorWithStability( + "The generation observed by the deployment controller.", -"kube_deployment_status_observed_generation", + metric.Gauge, -"The generation observed by the deployment controller.", + basemetrics.STABLE, -metric.Gauge, + "", -basemetrics.STABLE, + wrapDeploymentFunc(func(d *v1.Deployment) *metric.Family { -"", + return &metric.Family{ -wrapDeploymentFunc(func(d *v1.Deployment) *metric.Family { + Metrics: []*metric.Metric{ -return &metric.Family{ + { -Metrics: []*metric.Metric{ + Value: float64(d.Status.ObservedGeneration), + }, + }, + } -{ + }), + ), -Value: float64(d.Status.ObservedGeneration), + *generator.NewFamilyGeneratorWithStability( -}, + "kube_deployment_status_condition", -}, + "The current status conditions of a deployment.", -} + metric.Gauge, -}), + basemetrics.STABLE, -), + "", -*generator.NewFamilyGeneratorWithStability( + wrapDeploymentFunc(func(d *v1.Deployment) *metric.Family { -"kube_deployment_status_condition", + ms := make([]*metric.Metric, len(d.Status.Conditions)*len(conditionStatuses)) -"The current status conditions of a deployment.", + for i, c := range d.Status.Conditions { -metric.Gauge, + conditionMetrics := addConditionMetrics(c.Status) -basemetrics.STABLE, + for j, m := range conditionMetrics { -"", + metric := m -wrapDeploymentFunc(func(d *v1.Deployment) *metric.Family { + reason := c.Reason + if _, ok := allowedDeploymentReasons[reason]; !ok { + reason = "unknown" + } -ms := make([]*metric.Metric, len(d.Status.Conditions)*len(conditionStatuses)) + metric.LabelKeys = []string{"reason", "condition", "status"} -for i, c := range d.Status.Conditions { + metric.LabelValues = append([]string{reason, string(c.Type)}, metric.LabelValues...) -conditionMetrics := addConditionMetrics(c.Status) + ms[i*len(conditionStatuses)+j] = metric -for j, m := range conditionMetrics { + } -metric := m + } -reason := c.Reason -if _, ok := allowedDeploymentReasons[reason]; !ok { - reason = "unknown" -} + return &metric.Family{ -metric.LabelKeys = []string{"reason", "condition", "status"} + Metrics: ms, + } -metric.LabelValues = append([]string{reason, string(c.Type)}, metric.LabelValues...) + }), + ), -ms[i*len(conditionStatuses)+j] = metric + *generator.NewFamilyGeneratorWithStability( -} + "kube_deployment_spec_replicas", -} + "Number of desired pods for a deployment.", -return &metric.Family{ + metric.Gauge, -Metrics: ms, + basemetrics.STABLE, -} + "", -}), + wrapDeploymentFunc(func(d *v1.Deployment) *metric.Family { -), + return &metric.Family{ -*generator.NewFamilyGeneratorWithStability( + Metrics: []*metric.Metric{ -"kube_deployment_spec_replicas", + { -"Number of desired pods for a deployment.", + Value: float64(*d.Spec.Replicas), + }, + }, + } -metric.Gauge, + }), + ), -basemetrics.STABLE, + *generator.NewFamilyGeneratorWithStability( -"", + "kube_deployment_spec_paused", -wrapDeploymentFunc(func(d *v1.Deployment) *metric.Family { + "Whether the deployment is paused and will not be processed by the deployment controller.", -return &metric.Family{ + metric.Gauge, -Metrics: []*metric.Metric{ + basemetrics.STABLE, -{ + "", -Value: float64(*d.Spec.Replicas), + wrapDeploymentFunc(func(d *v1.Deployment) *metric.Family { -}, + return &metric.Family{ -}, + Metrics: []*metric.Metric{ -} + { -}), + Value: boolFloat64(d.Spec.Paused), + }, + }, + } -), + }), + ), -*generator.NewFamilyGeneratorWithStability( + *generator.NewFamilyGeneratorWithStability( -"kube_deployment_spec_paused", + "kube_deployment_spec_strategy_rollingupdate_max_unavailable", -"Whether the deployment is paused and will not be processed by the deployment controller.", + "Maximum number of unavailable replicas during a rolling update of a deployment.", -metric.Gauge, + metric.Gauge, -basemetrics.STABLE, + basemetrics.STABLE, -"", + "", -wrapDeploymentFunc(func(d *v1.Deployment) *metric.Family { + wrapDeploymentFunc(func(d *v1.Deployment) *metric.Family { -return &metric.Family{ + if d.Spec.Strategy.RollingUpdate == nil { -Metrics: []*metric.Metric{ + return &metric.Family{} -{ + } -Value: boolFloat64(d.Spec.Paused), + maxUnavailable, err := intstr.GetScaledValueFromIntOrPercent(d.Spec.Strategy.RollingUpdate.MaxUnavailable, int(*d.Spec.Replicas), false) -}, + if err != nil { -}, + panic(err) -} + } -}), + return &metric.Family{ -), + Metrics: []*metric.Metric{ -*generator.NewFamilyGeneratorWithStability( + { -"kube_deployment_spec_strategy_rollingupdate_max_unavailable", + Value: float64(maxUnavailable), + }, + }, + } -"Maximum number of unavailable replicas during a rolling update of a deployment.", + }), + ), -metric.Gauge, + *generator.NewFamilyGeneratorWithStability( -basemetrics.STABLE, + "kube_deployment_spec_strategy_rollingupdate_max_surge", -"", + "Maximum number of replicas that can be scheduled above the desired number of replicas during a rolling update of a deployment.", -wrapDeploymentFunc(func(d *v1.Deployment) *metric.Family { + metric.Gauge, -if d.Spec.Strategy.RollingUpdate == nil { + basemetrics.STABLE, -return &metric.Family{} + "", -} + wrapDeploymentFunc(func(d *v1.Deployment) *metric.Family { -maxUnavailable, err := intstr.GetScaledValueFromIntOrPercent(d.Spec.Strategy.RollingUpdate.MaxUnavailable, int(*d.Spec.Replicas), false) + if d.Spec.Strategy.RollingUpdate == nil { -if err != nil { + return &metric.Family{} -panic(err) + } -} + maxSurge, err := intstr.GetScaledValueFromIntOrPercent(d.Spec.Strategy.RollingUpdate.MaxSurge, int(*d.Spec.Replicas), true) -return &metric.Family{ + if err != nil { -Metrics: []*metric.Metric{ + panic(err) -{ + } -Value: float64(maxUnavailable), + return &metric.Family{ -}, + Metrics: []*metric.Metric{ -}, + { -} + Value: float64(maxSurge), + }, + }, + } -}), + }), + ), -), + *generator.NewFamilyGeneratorWithStability( -*generator.NewFamilyGeneratorWithStability( + "kube_deployment_spec_topology_spread_constraint", -"kube_deployment_spec_strategy_rollingupdate_max_surge", + "Explicit details of each topology spread constraint in the deployment's pod template.", -"Maximum number of replicas that can be scheduled above the desired number of replicas during a rolling update of a deployment.", + metric.Gauge, -metric.Gauge, + basemetrics.ALPHA, -basemetrics.STABLE, + "", -"", + wrapDeploymentFunc(func(d *v1.Deployment) *metric.Family { -wrapDeploymentFunc(func(d *v1.Deployment) *metric.Family { + ms := []*metric.Metric{} + for _, constraint := range d.Spec.Template.Spec.TopologySpreadConstraints { + labelSelectorStr, err := metav1.LabelSelectorAsSelector(constraint.LabelSelector) + if err != nil { + // Skip invalid label selectors + continue + } + minDomainsStr := "1" + if constraint.MinDomains != nil { + minDomainsStr = strconv.Itoa(int(*constraint.MinDomains)) + } + ms = append(ms, &metric.Metric{ + LabelKeys: []string{"topology_key", "max_skew", "when_unsatisfiable", "min_domains", "label_selector"}, + LabelValues: []string{constraint.TopologyKey, strconv.Itoa(int(constraint.MaxSkew)), string(constraint.WhenUnsatisfiable), minDomainsStr, labelSelectorStr.String()}, + Value: 1, + }) + } + return &metric.Family{Metrics: ms} -if d.Spec.Strategy.RollingUpdate == nil { + }), + ), -return &metric.Family{} + *generator.NewFamilyGeneratorWithStability( -} + "kube_deployment_metadata_generation", -maxSurge, err := intstr.GetScaledValueFromIntOrPercent(d.Spec.Strategy.RollingUpdate.MaxSurge, int(*d.Spec.Replicas), true) + "Sequence number representing a specific generation of the desired state.", -if err != nil { + metric.Gauge, -panic(err) + basemetrics.STABLE, -} + "", -return &metric.Family{ + wrapDeploymentFunc(func(d *v1.Deployment) *metric.Family { -Metrics: []*metric.Metric{ + return &metric.Family{ -{ + Metrics: []*metric.Metric{ -Value: float64(maxSurge), + { -}, + Value: float64(d.Generation), + }, + }, + } -}, + }), + ), -} + *generator.NewFamilyGeneratorWithStability( -}), + "kube_deployment_deletion_timestamp", -), + "Unix deletion timestamp", -*generator.NewFamilyGeneratorWithStability( + metric.Gauge, -"kube_deployment_spec_topology_spread_constraint", + basemetrics.ALPHA, -"Explicit details of each topology spread constraint in the deployment's pod template.", + "", -metric.Gauge, + wrapDeploymentFunc(func(d *v1.Deployment) *metric.Family { -basemetrics.ALPHA, + ms := []*metric.Metric{} -"", + if !d.DeletionTimestamp.IsZero() { -wrapDeploymentFunc(func(d *v1.Deployment) *metric.Family { + ms = append(ms, &metric.Metric{ -ms := []*metric.Metric{} -for _, constraint := range d.Spec.Template.Spec.TopologySpreadConstraints { - labelSelectorStr, err := metav1.LabelSelectorAsSelector(constraint.LabelSelector) - if err != nil { - // Skip invalid label selectors - continue - } - minDomainsStr := "1" - if constraint.MinDomains != nil { - minDomainsStr = strconv.Itoa(int(*constraint.MinDomains)) - } - ms = append(ms, &metric.Metric{ - LabelKeys: []string{"topology_key", "max_skew", "when_unsatisfiable", "min_domains", "label_selector"}, - LabelValues: []string{constraint.TopologyKey, strconv.Itoa(int(constraint.MaxSkew)), string(constraint.WhenUnsatisfiable), minDomainsStr, labelSelectorStr.String()}, - Value: 1, - }) -} -return &metric.Family{Metrics: ms} + Value: float64(d.DeletionTimestamp.Unix()), + }) -}), + } -), + return &metric.Family{ -*generator.NewFamilyGeneratorWithStability( + Metrics: ms, + } -"kube_deployment_metadata_generation", + }), + ), -"Sequence number representing a specific generation of the desired state.", + *generator.NewFamilyGeneratorWithStability( -metric.Gauge, + descDeploymentAnnotationsName, -basemetrics.STABLE, + descDeploymentAnnotationsHelp, -"", + metric.Gauge, + basemetrics.ALPHA, + "", -wrapDeploymentFunc(func(d *v1.Deployment) *metric.Family { + wrapDeploymentFunc(func(d *v1.Deployment) *metric.Family { -return &metric.Family{ + if len(allowAnnotationsList) == 0 { -Metrics: []*metric.Metric{ + return &metric.Family{} -{ + } -Value: float64(d.Generation), + annotationKeys, annotationValues := createPrometheusLabelKeysValues("annotation", d.Annotations, allowAnnotationsList) -}, + return &metric.Family{ -}, + Metrics: []*metric.Metric{ -} - -}), - -), + { -*generator.NewFamilyGeneratorWithStability( + LabelKeys: annotationKeys, -"kube_deployment_deletion_timestamp", + LabelValues: annotationValues, -"Unix deletion timestamp", + Value: 1, + }, + }, + } -metric.Gauge, + }), + ), -basemetrics.ALPHA, + *generator.NewFamilyGeneratorWithStability( -"", + descDeploymentLabelsName, -wrapDeploymentFunc(func(d *v1.Deployment) *metric.Family { + descDeploymentLabelsHelp, -ms := []*metric.Metric{} + metric.Gauge, -if !d.DeletionTimestamp.IsZero() { + basemetrics.STABLE, -ms = append(ms, &metric.Metric{ + "", -Value: float64(d.DeletionTimestamp.Unix()), + wrapDeploymentFunc(func(d *v1.Deployment) *metric.Family { -}) + if len(allowLabelsList) == 0 { -} - -return &metric.Family{ + return &metric.Family{} -Metrics: ms, + } -} + labelKeys, labelValues := createPrometheusLabelKeysValues("label", d.Labels, allowLabelsList) -}), + return &metric.Family{ -), + Metrics: []*metric.Metric{ -*generator.NewFamilyGeneratorWithStability( + { + LabelKeys: labelKeys, -descDeploymentAnnotationsName, + LabelValues: labelValues, + Value: 1, + }, + }, + } -descDeploymentAnnotationsHelp, - - -metric.Gauge, -basemetrics.ALPHA, -"", - -wrapDeploymentFunc(func(d *v1.Deployment) *metric.Family { - -if len(allowAnnotationsList) == 0 { - -return &metric.Family{} - -} - -annotationKeys, annotationValues := createPrometheusLabelKeysValues("annotation", d.Annotations, allowAnnotationsList) - -return &metric.Family{ - -Metrics: []*metric.Metric{ - -{ - -LabelKeys: annotationKeys, - -LabelValues: annotationValues, - -Value: 1, - -}, - -}, - -} - -}), - -), - -*generator.NewFamilyGeneratorWithStability( - -descDeploymentLabelsName, - -descDeploymentLabelsHelp, - -metric.Gauge, - -basemetrics.STABLE, - -"", - -wrapDeploymentFunc(func(d *v1.Deployment) *metric.Family { - -if len(allowLabelsList) == 0 { - -return &metric.Family{} - -} - -labelKeys, labelValues := createPrometheusLabelKeysValues("label", d.Labels, allowLabelsList) - -return &metric.Family{ - -Metrics: []*metric.Metric{ - -{ - -LabelKeys: labelKeys, - -LabelValues: labelValues, - -Value: 1, - -}, - -}, - -} - -}), - -), - -} + }), + ), + } } func wrapDeploymentFunc(f func(*v1.Deployment) *metric.Family) func(interface{}) *metric.Family { -return func(obj interface{}) *metric.Family { + return func(obj interface{}) *metric.Family { -deployment := obj.(*v1.Deployment) + deployment := obj.(*v1.Deployment) -metricFamily := f(deployment) + metricFamily := f(deployment) -for _, m := range metricFamily.Metrics { + for _, m := range metricFamily.Metrics { -m.LabelKeys, m.LabelValues = mergeKeyValues(descDeploymentLabelsDefaultLabels, []string{deployment.Namespace, deployment.Name}, m.LabelKeys, m.LabelValues) + m.LabelKeys, m.LabelValues = mergeKeyValues(descDeploymentLabelsDefaultLabels, []string{deployment.Namespace, deployment.Name}, m.LabelKeys, m.LabelValues) -} + } -return metricFamily + return metricFamily -} + } } func createDeploymentListWatch(kubeClient clientset.Interface, ns string, fieldSelector string) cache.ListerWatcher { -return &cache.ListWatch{ + return &cache.ListWatch{ -ListFunc: func(opts metav1.ListOptions) (runtime.Object, error) { + ListFunc: func(opts metav1.ListOptions) (runtime.Object, error) { -opts.FieldSelector = fieldSelector + opts.FieldSelector = fieldSelector -return kubeClient.AppsV1().Deployments(ns).List(context.TODO(), opts) + return kubeClient.AppsV1().Deployments(ns).List(context.TODO(), opts) -}, + }, -WatchFunc: func(opts metav1.ListOptions) (watch.Interface, error) { + WatchFunc: func(opts metav1.ListOptions) (watch.Interface, error) { -opts.FieldSelector = fieldSelector + opts.FieldSelector = fieldSelector -return kubeClient.AppsV1().Deployments(ns).Watch(context.TODO(), opts) + return kubeClient.AppsV1().Deployments(ns).Watch(context.TODO(), opts) -}, - -} + }, + } } diff --git a/internal/store/deployment_test.go b/internal/store/deployment_test.go index b3de6716a2..40129d407b 100644 --- a/internal/store/deployment_test.go +++ b/internal/store/deployment_test.go @@ -1,59 +1,42 @@ /* - Copyright 2016 The Kubernetes Authors All rights reserved. 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 + 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 store import ( + "testing" + "time" -"testing" - -"time" - -v1 "k8s.io/api/apps/v1" - -corev1 "k8s.io/api/core/v1" - -metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - -"k8s.io/apimachinery/pkg/util/intstr" - -generator "k8s.io/kube-state-metrics/v2/pkg/metric_generator" + v1 "k8s.io/api/apps/v1" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/util/intstr" + generator "k8s.io/kube-state-metrics/v2/pkg/metric_generator" ) var ( depl1Replicas int32 = 200 depl2Replicas int32 = 5 - depl3Replicas int32 = 1 - depl4Replicas int32 = 10 depl1MaxUnavailable = intstr.FromInt(10) depl2MaxUnavailable = intstr.FromString("25%") depl1MaxSurge = intstr.FromInt(10) depl2MaxSurge = intstr.FromString("20%") - ) func TestDeploymentStore(t *testing.T) { @@ -88,14 +71,13 @@ func TestDeploymentStore(t *testing.T) { # TYPE kube_deployment_spec_strategy_rollingupdate_max_unavailable gauge # HELP kube_deployment_spec_strategy_rollingupdate_max_surge [STABLE] Maximum number of replicas that can be scheduled above the desired number of replicas during a rolling update of a deployment. # TYPE kube_deployment_spec_strategy_rollingupdate_max_surge gauge - # HELP kube_deployment_spec_topology_spread_constraint [ALPHA] Explicit details of each topology spread constraint in the deployment's pod template. + # HELP kube_deployment_spec_topology_spread_constraint Explicit details of each topology spread constraint in the deployment's pod template. # TYPE kube_deployment_spec_topology_spread_constraint gauge # HELP kube_deployment_labels [STABLE] Kubernetes labels converted to Prometheus labels. # TYPE kube_deployment_labels gauge # HELP kube_deployment_deletion_timestamp Unix deletion timestamp # TYPE kube_deployment_deletion_timestamp gauge ` - cases := []generateMetricsTestCase{ { AllowAnnotationsList: []string{"company.io/team"}, @@ -223,64 +205,8 @@ func TestDeploymentStore(t *testing.T) { kube_deployment_status_condition{condition="ReplicaFailure",deployment="depl2",namespace="ns2",reason="ReplicaSetCreateError",status="unknown"} 0 `, }, - { - Obj: &v1.Deployment{ - ObjectMeta: metav1.ObjectMeta{ - Name: "depl3", - Namespace: "ns3", - }, - Status: v1.DeploymentStatus{ - Conditions: []v1.DeploymentCondition{ - {Type: v1.DeploymentAvailable, Status: corev1.ConditionFalse, Reason: "ThisReasonIsNotAllowed"}, - {Type: v1.DeploymentProgressing, Status: corev1.ConditionTrue}, - }, - }, - Spec: v1.DeploymentSpec{ - Replicas: &depl3Replicas, - }, - }, - Want: metadata + ` - kube_deployment_metadata_generation{deployment="depl3",namespace="ns3"} 0 - kube_deployment_spec_paused{deployment="depl3",namespace="ns3"} 0 - kube_deployment_spec_replicas{deployment="depl3",namespace="ns3"} 1 - kube_deployment_status_condition{condition="Available",deployment="depl3",namespace="ns3",reason="unknown",status="true"} 0 - kube_deployment_status_condition{condition="Available",deployment="depl3",namespace="ns3",reason="unknown",status="false"} 1 - kube_deployment_status_condition{condition="Available",deployment="depl3",namespace="ns3",reason="unknown",status="unknown"} 0 - kube_deployment_status_observed_generation{deployment="depl3",namespace="ns3"} 0 - kube_deployment_status_replicas{deployment="depl3",namespace="ns3"} 0 - kube_deployment_status_replicas_available{deployment="depl3",namespace="ns3"} 0 - kube_deployment_status_replicas_ready{deployment="depl3",namespace="ns3"} 0 - kube_deployment_status_replicas_unavailable{deployment="depl3",namespace="ns3"} 0 - kube_deployment_status_replicas_updated{deployment="depl3",namespace="ns3"} 0 - kube_deployment_status_condition{condition="Progressing",deployment="depl3",namespace="ns3",reason="",status="false"} 0 - kube_deployment_status_condition{condition="Progressing",deployment="depl3",namespace="ns3",reason="",status="true"} 1 - kube_deployment_status_condition{condition="Progressing",deployment="depl3",namespace="ns3",reason="",status="unknown"} 0 -`, - }, - { - Obj: &v1.Deployment{ - ObjectMeta: metav1.ObjectMeta{ - Name: "deployment-terminating", - Namespace: "ns4", - CreationTimestamp: metav1.Time{Time: time.Unix(1600000000, 0)}, - DeletionTimestamp: &metav1.Time{Time: time.Unix(1800000000, 0)}, - Labels: map[string]string{ - "app": "example4", - }, - Generation: 22, - }, - Spec: v1.DeploymentSpec{ - Paused: true, - Replicas: &depl4Replicas, - }, - }, - Want: ` - # HELP kube_deployment_deletion_timestamp Unix deletion timestamp - # TYPE kube_deployment_deletion_timestamp gauge - kube_deployment_deletion_timestamp{deployment="deployment-terminating",namespace="ns4"} 1.8e+09`, - MetricNames: []string{"kube_deployment_deletion_timestamp"}, - }, } + for i, c := range cases { c.Func = generator.ComposeMetricGenFuncs(deploymentMetricFamilies(c.AllowAnnotationsList, nil)) c.Headers = generator.ExtractMetricFamilyHeaders(deploymentMetricFamilies(c.AllowAnnotationsList, nil)) @@ -289,372 +215,3 @@ func TestDeploymentStore(t *testing.T) { } } } - -// output so we only have to modify a single place when doing adjustments. - -const metadata = ` - -# HELP kube_deployment_annotations Kubernetes annotations converted to Prometheus labels. - -# TYPE kube_deployment_annotations gauge - -# HELP kube_deployment_created [STABLE] Unix creation timestamp - -# TYPE kube_deployment_created gauge - -# HELP kube_deployment_metadata_generation [STABLE] Sequence number representing a specific generation of the desired state. - -# TYPE kube_deployment_metadata_generation gauge - -# HELP kube_deployment_spec_paused [STABLE] Whether the deployment is paused and will not be processed by the deployment controller. - -# TYPE kube_deployment_spec_paused gauge - -# HELP kube_deployment_spec_replicas [STABLE] Number of desired pods for a deployment. - -# TYPE kube_deployment_spec_replicas gauge - -# HELP kube_deployment_status_replicas [STABLE] The number of replicas per deployment. - -# TYPE kube_deployment_status_replicas gauge - -# HELP kube_deployment_status_replicas_ready [STABLE] The number of ready replicas per deployment. - -# TYPE kube_deployment_status_replicas_ready gauge - -# HELP kube_deployment_status_replicas_available [STABLE] The number of available replicas per deployment. - -# TYPE kube_deployment_status_replicas_available gauge - -# HELP kube_deployment_status_replicas_unavailable [STABLE] The number of unavailable replicas per deployment. - -# TYPE kube_deployment_status_replicas_unavailable gauge - -# HELP kube_deployment_status_replicas_updated [STABLE] The number of updated replicas per deployment. - -# TYPE kube_deployment_status_replicas_updated gauge - -# HELP kube_deployment_status_observed_generation [STABLE] The generation observed by the deployment controller. - -# TYPE kube_deployment_status_observed_generation gauge - -# HELP kube_deployment_status_condition [STABLE] The current status conditions of a deployment. - -# TYPE kube_deployment_status_condition gauge - -# HELP kube_deployment_spec_strategy_rollingupdate_max_unavailable [STABLE] Maximum number of unavailable replicas during a rolling update of a deployment. - -# TYPE kube_deployment_spec_strategy_rollingupdate_max_unavailable gauge - -# HELP kube_deployment_spec_strategy_rollingupdate_max_surge [STABLE] Maximum number of replicas that can be scheduled above the desired number of replicas during a rolling update of a deployment. - -# TYPE kube_deployment_spec_strategy_rollingupdate_max_surge gauge - -# HELP kube_deployment_spec_topology_spread_constraints [STABLE] Number of topology spread constraints in the deployment's pod template. - -# TYPE kube_deployment_spec_topology_spread_constraints gauge - -# HELP kube_deployment_labels [STABLE] Kubernetes labels converted to Prometheus labels. - -# TYPE kube_deployment_labels gauge - -` - -cases := []generateMetricsTestCase{ - -{ - -AllowAnnotationsList: []string{"company.io/team"}, - -Obj: &v1.Deployment{ - -ObjectMeta: metav1.ObjectMeta{ - -Name: "depl1", - -CreationTimestamp: metav1.Time{Time: time.Unix(1500000000, 0)}, - -Namespace: "ns1", - -Annotations: map[string]string{ - -"company.io/team": "my-brilliant-team", - -}, - -Labels: map[string]string{ - -"app": "example1", - -}, - -Generation: 21, - -}, - -Status: v1.DeploymentStatus{ - -Replicas: 15, - -ReadyReplicas: 10, - -AvailableReplicas: 10, - -UnavailableReplicas: 5, - -UpdatedReplicas: 2, - -ObservedGeneration: 111, - -Conditions: []v1.DeploymentCondition{ - -{Type: v1.DeploymentAvailable, Status: corev1.ConditionTrue}, - -{Type: v1.DeploymentProgressing, Status: corev1.ConditionTrue}, - -}, - -}, - -Spec: v1.DeploymentSpec{ - -Replicas: &depl1Replicas, - -Strategy: v1.DeploymentStrategy{ - -RollingUpdate: &v1.RollingUpdateDeployment{ - -MaxUnavailable: &depl1MaxUnavailable, - -MaxSurge: &depl1MaxSurge, - -}, - -}, - -Template: corev1.PodTemplateSpec{ - -Spec: corev1.PodSpec{ - -TopologySpreadConstraints: []corev1.TopologySpreadConstraint{ - -{ - -MaxSkew: 1, - -TopologyKey: "kubernetes.io/zone", - -WhenUnsatisfiable: corev1.DoNotSchedule, - -}, - -{ - -MaxSkew: 1, - -TopologyKey: "kubernetes.io/hostname", - -WhenUnsatisfiable: corev1.ScheduleAnyway, - -}, - -}, - -}, - -}, - -}, - -}, - -Want: metadata + ` - -kube_deployment_annotations{annotation_company_io_team="my-brilliant-team",deployment="depl1",namespace="ns1"} 1 - -kube_deployment_created{deployment="depl1",namespace="ns1"} 1.5e+09 - -kube_deployment_metadata_generation{deployment="depl1",namespace="ns1"} 21 - -kube_deployment_spec_paused{deployment="depl1",namespace="ns1"} 0 - -kube_deployment_spec_replicas{deployment="depl1",namespace="ns1"} 200 - -kube_deployment_spec_strategy_rollingupdate_max_surge{deployment="depl1",namespace="ns1"} 10 - -kube_deployment_spec_strategy_rollingupdate_max_unavailable{deployment="depl1",namespace="ns1"} 10 - -kube_deployment_spec_topology_spread_constraints{deployment="depl1",namespace="ns1"} 2 - -kube_deployment_status_observed_generation{deployment="depl1",namespace="ns1"} 111 - -kube_deployment_status_replicas_available{deployment="depl1",namespace="ns1"} 10 - -kube_deployment_status_replicas_unavailable{deployment="depl1",namespace="ns1"} 5 - -kube_deployment_status_replicas_updated{deployment="depl1",namespace="ns1"} 2 - -kube_deployment_status_replicas{deployment="depl1",namespace="ns1"} 15 - -kube_deployment_status_replicas_ready{deployment="depl1",namespace="ns1"} 10 - -kube_deployment_status_condition{deployment="depl1",namespace="ns1",condition="Available",status="true"} 1 - -kube_deployment_status_condition{deployment="depl1",namespace="ns1",condition="Progressing",status="true"} 1 - -kube_deployment_status_condition{deployment="depl1",namespace="ns1",condition="Available",status="false"} 0 - -kube_deployment_status_condition{deployment="depl1",namespace="ns1",condition="Progressing",status="false"} 0 - -kube_deployment_status_condition{deployment="depl1",namespace="ns1",condition="Available",status="unknown"} 0 - -kube_deployment_status_condition{deployment="depl1",namespace="ns1",condition="Progressing",status="unknown"} 0 - -`, - -}, - -{ - -Obj: &v1.Deployment{ - -ObjectMeta: metav1.ObjectMeta{ - -Name: "depl2", - -Namespace: "ns2", - -Labels: map[string]string{ - -"app": "example2", - -}, - -Generation: 14, - -}, - -Status: v1.DeploymentStatus{ - -Replicas: 10, - -ReadyReplicas: 5, - -AvailableReplicas: 5, - -UnavailableReplicas: 0, - -UpdatedReplicas: 1, - -ObservedGeneration: 1111, - -Conditions: []v1.DeploymentCondition{ - -{Type: v1.DeploymentAvailable, Status: corev1.ConditionFalse}, - -{Type: v1.DeploymentProgressing, Status: corev1.ConditionFalse}, - -{Type: v1.DeploymentReplicaFailure, Status: corev1.ConditionTrue}, - -}, - -}, - -Spec: v1.DeploymentSpec{ - -Paused: true, - -Replicas: &depl2Replicas, - -Strategy: v1.DeploymentStrategy{ - -RollingUpdate: &v1.RollingUpdateDeployment{ - -MaxUnavailable: &depl2MaxUnavailable, - -MaxSurge: &depl2MaxSurge, - -}, - -}, - -Template: corev1.PodTemplateSpec{ - -Spec: corev1.PodSpec{ - -TopologySpreadConstraints: []corev1.TopologySpreadConstraint{}, - -}, - -}, - -}, - -}, - -Want: metadata + ` - -kube_deployment_metadata_generation{deployment="depl2",namespace="ns2"} 14 - -kube_deployment_spec_paused{deployment="depl2",namespace="ns2"} 1 - -kube_deployment_spec_replicas{deployment="depl2",namespace="ns2"} 5 - -kube_deployment_spec_strategy_rollingupdate_max_surge{deployment="depl2",namespace="ns2"} 1 - -kube_deployment_spec_strategy_rollingupdate_max_unavailable{deployment="depl2",namespace="ns2"} 1 - -kube_deployment_spec_topology_spread_constraints{deployment="depl2",namespace="ns2"} 0 - -kube_deployment_status_observed_generation{deployment="depl2",namespace="ns2"} 1111 - -kube_deployment_status_replicas_available{deployment="depl2",namespace="ns2"} 5 - -kube_deployment_status_replicas_unavailable{deployment="depl2",namespace="ns2"} 0 - -kube_deployment_status_replicas_updated{deployment="depl2",namespace="ns2"} 1 - -kube_deployment_status_replicas{deployment="depl2",namespace="ns2"} 10 - -kube_deployment_status_replicas_ready{deployment="depl2",namespace="ns2"} 5 - -kube_deployment_status_condition{deployment="depl2",namespace="ns2",condition="Available",status="true"} 0 - -kube_deployment_status_condition{deployment="depl2",namespace="ns2",condition="Progressing",status="true"} 0 - -kube_deployment_status_condition{deployment="depl2",namespace="ns2",condition="ReplicaFailure",status="true"} 1 - -kube_deployment_status_condition{deployment="depl2",namespace="ns2",condition="Available",status="false"} 1 - -kube_deployment_status_condition{deployment="depl2",namespace="ns2",condition="Progressing",status="false"} 1 - -kube_deployment_status_condition{deployment="depl2",namespace="ns2",condition="ReplicaFailure",status="false"} 0 - -kube_deployment_status_condition{deployment="depl2",namespace="ns2",condition="Available",status="unknown"} 0 - -kube_deployment_status_condition{deployment="depl2",namespace="ns2",condition="Progressing",status="unknown"} 0 - -kube_deployment_status_condition{deployment="depl2",namespace="ns2",condition="ReplicaFailure",status="unknown"} 0 - -`, - -}, - -} - -for i, c := range cases { - -c.Func = generator.ComposeMetricGenFuncs(deploymentMetricFamilies(c.AllowAnnotationsList, nil)) - -c.Headers = generator.ExtractMetricFamilyHeaders(deploymentMetricFamilies(c.AllowAnnotationsList, nil)) - -if err := c.run(); err != nil { - -t.Errorf("unexpected collecting result in %vth run:\n%s", i, err) - -} - -} - ->>>>>>> 6f562672 (Add kube_deployment_spec_topology_spread_constraints metric) -}