From 643c9e95d9bbe60311a7c1167c86e335b4f39016 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filip=20K=C5=99epinsk=C3=BD?= Date: Mon, 21 Jul 2025 09:18:21 +0200 Subject: [PATCH] add kube_deployment_status_replicas_terminating and kube_replicaset_status_terminating_replicas metrics --- docs/metrics/workload/deployment-metrics.md | 1 + docs/metrics/workload/replicaset-metrics.md | 1 + internal/store/deployment.go | 20 ++++++++++++++++++++ internal/store/deployment_test.go | 6 ++++++ internal/store/replicaset.go | 20 ++++++++++++++++++++ internal/store/replicaset_test.go | 6 ++++++ 6 files changed, 54 insertions(+) diff --git a/docs/metrics/workload/deployment-metrics.md b/docs/metrics/workload/deployment-metrics.md index e8d0fdb42a..2209d3fdb3 100644 --- a/docs/metrics/workload/deployment-metrics.md +++ b/docs/metrics/workload/deployment-metrics.md @@ -8,6 +8,7 @@ | 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_replicas_terminating | Gauge | The number of terminating replicas per deployment. | `deployment`=<deployment-name>
`namespace`=<deployment-namespace> | ALPHA | | 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 | diff --git a/docs/metrics/workload/replicaset-metrics.md b/docs/metrics/workload/replicaset-metrics.md index cf3f8e6e10..da13603504 100644 --- a/docs/metrics/workload/replicaset-metrics.md +++ b/docs/metrics/workload/replicaset-metrics.md @@ -6,6 +6,7 @@ | kube_replicaset_status_replicas | Gauge | | `replicaset`=<replicaset-name>
`namespace`=<replicaset-namespace> | STABLE | | kube_replicaset_status_fully_labeled_replicas | Gauge | | `replicaset`=<replicaset-name>
`namespace`=<replicaset-namespace> | STABLE | | kube_replicaset_status_ready_replicas | Gauge | | `replicaset`=<replicaset-name>
`namespace`=<replicaset-namespace> | STABLE | +| kube_replicaset_status_terminating_replicas | Gauge | The number of terminating replicas per ReplicaSet. | `replicaset`=<replicaset-name>
`namespace`=<replicaset-namespace> | ALPHA | | kube_replicaset_status_observed_generation | Gauge | | `replicaset`=<replicaset-name>
`namespace`=<replicaset-namespace> | STABLE | | kube_replicaset_spec_replicas | Gauge | | `replicaset`=<replicaset-name>
`namespace`=<replicaset-namespace> | STABLE | | kube_replicaset_metadata_generation | Gauge | | `replicaset`=<replicaset-name>
`namespace`=<replicaset-namespace> | STABLE | diff --git a/internal/store/deployment.go b/internal/store/deployment.go index 31e3b200a6..7fff9eb7c1 100644 --- a/internal/store/deployment.go +++ b/internal/store/deployment.go @@ -160,6 +160,26 @@ func deploymentMetricFamilies(allowAnnotationsList, allowLabelsList []string) [] } }), ), + *generator.NewFamilyGeneratorWithStability( + "kube_deployment_status_replicas_terminating", + "The number of terminating replicas per deployment.", + metric.Gauge, + basemetrics.ALPHA, + "", + wrapDeploymentFunc(func(r *v1.Deployment) *metric.Family { + ms := []*metric.Metric{} + + if r.Status.TerminatingReplicas != nil { + ms = append(ms, &metric.Metric{ + Value: float64(*r.Status.TerminatingReplicas), + }) + } + + return &metric.Family{ + Metrics: ms, + } + }), + ), *generator.NewFamilyGeneratorWithStability( "kube_deployment_status_observed_generation", "The generation observed by the deployment controller.", diff --git a/internal/store/deployment_test.go b/internal/store/deployment_test.go index a0e3f4aa17..508bc6b7e6 100644 --- a/internal/store/deployment_test.go +++ b/internal/store/deployment_test.go @@ -24,6 +24,7 @@ import ( corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/util/intstr" + "k8s.io/utils/ptr" generator "k8s.io/kube-state-metrics/v2/pkg/metric_generator" ) @@ -59,6 +60,8 @@ func TestDeploymentStore(t *testing.T) { # 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_terminating The number of terminating replicas per deployment. + # TYPE kube_deployment_status_replicas_terminating 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. @@ -100,6 +103,7 @@ func TestDeploymentStore(t *testing.T) { AvailableReplicas: 10, UnavailableReplicas: 5, UpdatedReplicas: 2, + TerminatingReplicas: ptr.To[int32](3), ObservedGeneration: 111, Conditions: []v1.DeploymentCondition{ {Type: v1.DeploymentAvailable, Status: corev1.ConditionTrue, Reason: "MinimumReplicasAvailable"}, @@ -130,6 +134,7 @@ func TestDeploymentStore(t *testing.T) { 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_replicas_terminating{deployment="depl1",namespace="ns1"} 3 kube_deployment_status_condition{condition="Available",deployment="depl1",namespace="ns1",reason="MinimumReplicasAvailable",status="true"} 1 kube_deployment_status_condition{condition="Available",deployment="depl1",namespace="ns1",reason="MinimumReplicasAvailable",status="false"} 0 kube_deployment_status_condition{condition="Available",deployment="depl1",namespace="ns1",reason="MinimumReplicasAvailable",status="unknown"} 0 @@ -154,6 +159,7 @@ func TestDeploymentStore(t *testing.T) { AvailableReplicas: 5, UnavailableReplicas: 0, UpdatedReplicas: 1, + TerminatingReplicas: nil, ObservedGeneration: 1111, Conditions: []v1.DeploymentCondition{ {Type: v1.DeploymentAvailable, Status: corev1.ConditionFalse, Reason: "MinimumReplicasUnavailable"}, diff --git a/internal/store/replicaset.go b/internal/store/replicaset.go index 192299ff6d..37f6e459c0 100644 --- a/internal/store/replicaset.go +++ b/internal/store/replicaset.go @@ -112,6 +112,26 @@ func replicaSetMetricFamilies(allowAnnotationsList, allowLabelsList []string) [] } }), ), + *generator.NewFamilyGeneratorWithStability( + "kube_replicaset_status_terminating_replicas", + "The number of terminating replicas per ReplicaSet.", + metric.Gauge, + basemetrics.ALPHA, + "", + wrapReplicaSetFunc(func(r *v1.ReplicaSet) *metric.Family { + ms := []*metric.Metric{} + + if r.Status.TerminatingReplicas != nil { + ms = append(ms, &metric.Metric{ + Value: float64(*r.Status.TerminatingReplicas), + }) + } + + return &metric.Family{ + Metrics: ms, + } + }), + ), *generator.NewFamilyGeneratorWithStability( "kube_replicaset_status_observed_generation", "The generation observed by the ReplicaSet controller.", diff --git a/internal/store/replicaset_test.go b/internal/store/replicaset_test.go index 963a779248..d99d32a936 100644 --- a/internal/store/replicaset_test.go +++ b/internal/store/replicaset_test.go @@ -22,6 +22,7 @@ import ( v1 "k8s.io/api/apps/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/utils/ptr" generator "k8s.io/kube-state-metrics/v2/pkg/metric_generator" ) @@ -43,11 +44,13 @@ func TestReplicaSetStore(t *testing.T) { # HELP kube_replicaset_metadata_generation [STABLE] Sequence number representing a specific generation of the desired state. # TYPE kube_replicaset_metadata_generation gauge # HELP kube_replicaset_status_replicas [STABLE] The number of replicas per ReplicaSet. + # HELP kube_replicaset_status_terminating_replicas The number of terminating replicas per ReplicaSet. # TYPE kube_replicaset_status_replicas gauge # HELP kube_replicaset_status_fully_labeled_replicas [STABLE] The number of fully labeled replicas per ReplicaSet. # TYPE kube_replicaset_status_fully_labeled_replicas gauge # HELP kube_replicaset_status_ready_replicas [STABLE] The number of ready replicas per ReplicaSet. # TYPE kube_replicaset_status_ready_replicas gauge + # TYPE kube_replicaset_status_terminating_replicas gauge # HELP kube_replicaset_status_observed_generation [STABLE] The generation observed by the ReplicaSet controller. # TYPE kube_replicaset_status_observed_generation gauge # HELP kube_replicaset_spec_replicas [STABLE] Number of desired pods for a ReplicaSet. @@ -80,6 +83,7 @@ func TestReplicaSetStore(t *testing.T) { Replicas: 5, FullyLabeledReplicas: 10, ReadyReplicas: 5, + TerminatingReplicas: ptr.To[int32](3), ObservedGeneration: 1, }, Spec: v1.ReplicaSetSpec{ @@ -93,6 +97,7 @@ func TestReplicaSetStore(t *testing.T) { kube_replicaset_status_observed_generation{namespace="ns1",replicaset="rs1"} 1 kube_replicaset_status_fully_labeled_replicas{namespace="ns1",replicaset="rs1"} 10 kube_replicaset_status_ready_replicas{namespace="ns1",replicaset="rs1"} 5 + kube_replicaset_status_terminating_replicas{namespace="ns1",replicaset="rs1"} 3 kube_replicaset_spec_replicas{namespace="ns1",replicaset="rs1"} 5 kube_replicaset_owner{namespace="ns1",owner_is_controller="true",owner_kind="Deployment",owner_name="dp-name",replicaset="rs1"} 1 `, @@ -112,6 +117,7 @@ func TestReplicaSetStore(t *testing.T) { Replicas: 0, FullyLabeledReplicas: 5, ReadyReplicas: 0, + TerminatingReplicas: nil, ObservedGeneration: 5, }, Spec: v1.ReplicaSetSpec{