From d5bb36253231bd2533e7f00235b49eff68db2929 Mon Sep 17 00:00:00 2001 From: Igor Ignatev Date: Tue, 3 Jun 2025 19:10:07 +0300 Subject: [PATCH] feat: deletion timestamp metric for multiple resources --- .../policy/poddisruptionbudget-metrics.md | 17 +++++----- docs/metrics/service/service-metrics.md | 19 ++++++------ docs/metrics/workload/daemonset-metrics.md | 25 ++++++++------- docs/metrics/workload/deployment-metrics.md | 31 ++++++++++--------- docs/metrics/workload/statefulset-metrics.md | 31 ++++++++++--------- internal/store/daemonset.go | 20 ++++++++++++ internal/store/daemonset_test.go | 28 +++++++++++++++++ internal/store/deployment.go | 20 ++++++++++++ internal/store/deployment_test.go | 27 +++++++++++++++- internal/store/poddisruptionbudget.go | 20 ++++++++++++ internal/store/poddisruptionbudget_test.go | 24 ++++++++++++++ internal/store/service.go | 19 ++++++++++++ internal/store/service_test.go | 28 +++++++++++++++++ internal/store/statefulset.go | 20 ++++++++++++ internal/store/statefulset_test.go | 30 ++++++++++++++++++ 15 files changed, 299 insertions(+), 60 deletions(-) diff --git a/docs/metrics/policy/poddisruptionbudget-metrics.md b/docs/metrics/policy/poddisruptionbudget-metrics.md index 2e8fb1179a..df9fe3679d 100644 --- a/docs/metrics/policy/poddisruptionbudget-metrics.md +++ b/docs/metrics/policy/poddisruptionbudget-metrics.md @@ -1,12 +1,13 @@ # PodDisruptionBudget Metrics -| Metric name | Metric type | Description | Labels/tags | Status | -| ------------------------------------------------------- | ----------- | ------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------ | +| Metric name | Metric type | Description | Labels/tags | Status | +| ------------------------------------------------------- | ----------- | --------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------ | | kube_poddisruptionbudget_annotations | Gauge | Kubernetes annotations converted to Prometheus labels controlled via [--metric-annotations-allowlist](../../developer/cli-arguments.md) | `poddisruptionbudget`=<poddisruptionbudget-name>
`namespace`=<poddisruptionbudget-namespace>
`annotation_PODDISRUPTIONBUDGET_ANNOTATION`=<PODDISRUPTIONBUDGET_ANNOATION> | EXPERIMENTAL | | kube_poddisruptionbudget_labels | Gauge | Kubernetes labels converted to Prometheus labels controlled via [--metric-labels-allowlist](../../developer/cli-arguments.md) | `poddisruptionbudget`=<poddisruptionbudget-name>
`namespace`=<poddisruptionbudget-namespace>
`label_PODDISRUPTIONBUDGET_LABEL`=<PODDISRUPTIONBUDGET_ANNOATION> | EXPERIMENTAL | -| kube_poddisruptionbudget_created | Gauge | | `poddisruptionbudget`=<pdb-name>
`namespace`=<pdb-namespace> | STABLE | -| kube_poddisruptionbudget_status_current_healthy | Gauge | | `poddisruptionbudget`=<pdb-name>
`namespace`=<pdb-namespace> | STABLE | -| kube_poddisruptionbudget_status_desired_healthy | Gauge | | `poddisruptionbudget`=<pdb-name>
`namespace`=<pdb-namespace> | STABLE | -| kube_poddisruptionbudget_status_pod_disruptions_allowed | Gauge | | `poddisruptionbudget`=<pdb-name>
`namespace`=<pdb-namespace> | STABLE | -| kube_poddisruptionbudget_status_expected_pods | Gauge | | `poddisruptionbudget`=<pdb-name>
`namespace`=<pdb-namespace> | STABLE | -| kube_poddisruptionbudget_status_observed_generation | Gauge | | `poddisruptionbudget`=<pdb-name>
`namespace`=<pdb-namespace> | STABLE | +| kube_poddisruptionbudget_created | Gauge | | `poddisruptionbudget`=<pdb-name>
`namespace`=<pdb-namespace> | STABLE | +| kube_poddisruptionbudget_status_current_healthy | Gauge | | `poddisruptionbudget`=<pdb-name>
`namespace`=<pdb-namespace> | STABLE | +| kube_poddisruptionbudget_status_desired_healthy | Gauge | | `poddisruptionbudget`=<pdb-name>
`namespace`=<pdb-namespace> | STABLE | +| kube_poddisruptionbudget_status_pod_disruptions_allowed | Gauge | | `poddisruptionbudget`=<pdb-name>
`namespace`=<pdb-namespace> | STABLE | +| kube_poddisruptionbudget_status_expected_pods | Gauge | | `poddisruptionbudget`=<pdb-name>
`namespace`=<pdb-namespace> | STABLE | +| kube_poddisruptionbudget_status_observed_generation | Gauge | | `poddisruptionbudget`=<pdb-name>
`namespace`=<pdb-namespace> | STABLE | +| kube_poddisruptionbudget_deletion_timestamp | Gauge | | `poddisruptionbudget`=<pdb-name>
`namespace`=<pdb-namespace> | EXPERIMENTAL | diff --git a/docs/metrics/service/service-metrics.md b/docs/metrics/service/service-metrics.md index 981b0c83ef..8586a9df53 100644 --- a/docs/metrics/service/service-metrics.md +++ b/docs/metrics/service/service-metrics.md @@ -1,11 +1,12 @@ # Service Metrics -| Metric name | Metric type | Description | Unit (where applicable) | Labels/tags | Status | -| ----------------------------------------- | ----------- | ------------------------------------------------------------------------------------------------------------------------- | ----------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------ | -| kube_service_annotations | Gauge | Kubernetes annotations converted to Prometheus labels controlled via [--metric-annotations-allowlist](../../developer/cli-arguments.md) | | `service`=<service-name>
`namespace`=<service-namespace>
`uid`=<service-uid>
`annotation_SERVICE_ANNOTATION`=<SERVICE_ANNOTATION> | EXPERIMENTAL | -| kube_service_info | Gauge | Information about service | | `service`=<service-name>
`namespace`=<service-namespace>
`uid`=<service-uid>
`cluster_ip`=<service cluster ip>
`external_name`=<service external name>
`external_traffic_policy`=<service external traffic policy>
`load_balancer_ip`=<service load balancer ip> | STABLE | -| kube_service_labels | Gauge | Kubernetes labels converted to Prometheus labels controlled via [--metric-labels-allowlist](../../developer/cli-arguments.md) | | `service`=<service-name>
`namespace`=<service-namespace>
`uid`=<service-uid>
`label_SERVICE_LABEL`=<SERVICE_LABEL> | STABLE | -| kube_service_created | Gauge | Unix creation timestamp | seconds | `service`=<service-name>
`namespace`=<service-namespace>
`uid`=<service-uid> | STABLE | -| kube_service_spec_type | Gauge | Type about service | | `service`=<service-name>
`namespace`=<service-namespace>
`uid`=<service-uid>
`type`=<ClusterIP\|NodePort\|LoadBalancer\|ExternalName> | STABLE | -| kube_service_spec_external_ip | Gauge | Service external ips. One series for each ip | | `service`=<service-name>
`namespace`=<service-namespace>
`uid`=<service-uid>
`external_ip`=<external-ip> | STABLE | -| kube_service_status_load_balancer_ingress | Gauge | Service load balancer ingress status | | `service`=<service-name>
`namespace`=<service-namespace>
`uid`=<service-uid>
`ip`=<load-balancer-ingress-ip>
`hostname`=<load-balancer-ingress-hostname> | STABLE | +| Metric name | Metric type | Description | Unit (where applicable) | Labels/tags | Status | +| ----------------------------------------- | ----------- | --------------------------------------------------------------------------------------------------------------------------------------- | ----------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------ | +| kube_service_annotations | Gauge | Kubernetes annotations converted to Prometheus labels controlled via [--metric-annotations-allowlist](../../developer/cli-arguments.md) | | `service`=<service-name>
`namespace`=<service-namespace>
`uid`=<service-uid>
`annotation_SERVICE_ANNOTATION`=<SERVICE_ANNOTATION> | EXPERIMENTAL | +| kube_service_info | Gauge | Information about service | | `service`=<service-name>
`namespace`=<service-namespace>
`uid`=<service-uid>
`cluster_ip`=<service cluster ip>
`external_name`=<service external name>
`external_traffic_policy`=<service external traffic policy>
`load_balancer_ip`=<service load balancer ip> | STABLE | +| kube_service_labels | Gauge | Kubernetes labels converted to Prometheus labels controlled via [--metric-labels-allowlist](../../developer/cli-arguments.md) | | `service`=<service-name>
`namespace`=<service-namespace>
`uid`=<service-uid>
`label_SERVICE_LABEL`=<SERVICE_LABEL> | STABLE | +| kube_service_created | Gauge | Unix creation timestamp | seconds | `service`=<service-name>
`namespace`=<service-namespace>
`uid`=<service-uid> | STABLE | +| kube_service_spec_type | Gauge | Type about service | | `service`=<service-name>
`namespace`=<service-namespace>
`uid`=<service-uid>
`type`=<ClusterIP\|NodePort\|LoadBalancer\|ExternalName> | STABLE | +| kube_service_spec_external_ip | Gauge | Service external ips. One series for each ip | | `service`=<service-name>
`namespace`=<service-namespace>
`uid`=<service-uid>
`external_ip`=<external-ip> | STABLE | +| kube_service_status_load_balancer_ingress | Gauge | Service load balancer ingress status | | `service`=<service-name>
`namespace`=<service-namespace>
`uid`=<service-uid>
`ip`=<load-balancer-ingress-ip>
`hostname`=<load-balancer-ingress-hostname> | STABLE | +| kube_service_deletion_timestamp | Gauge | Unix deletion timestamp | seconds | `service`=<service-name>
`namespace`=<service-namespace>
`uid`=<service-uid> | EXPERIMENTAL | diff --git a/docs/metrics/workload/daemonset-metrics.md b/docs/metrics/workload/daemonset-metrics.md index 04c5edf4e0..e77394993e 100644 --- a/docs/metrics/workload/daemonset-metrics.md +++ b/docs/metrics/workload/daemonset-metrics.md @@ -1,16 +1,17 @@ # DaemonSet Metrics -| Metric name | Metric type | Description | Labels/tags | Status | -| ---------------------------------------------- | ----------- | ------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------- | ------------ | +| Metric name | Metric type | Description | Labels/tags | Status | +| ---------------------------------------------- | ----------- | --------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------- | ------------ | | kube_daemonset_annotations | Gauge | Kubernetes annotations converted to Prometheus labels controlled via [--metric-annotations-allowlist](../../developer/cli-arguments.md) | `daemonset`=<daemonset-name>
`namespace`=<daemonset-namespace>
`annotation_DAEMONSET_ANNOTATION`=<DAEMONSET_ANNOTATION> | EXPERIMENTAL | -| kube_daemonset_created | Gauge | | `daemonset`=<daemonset-name>
`namespace`=<daemonset-namespace> | STABLE | -| kube_daemonset_status_current_number_scheduled | Gauge | | `daemonset`=<daemonset-name>
`namespace`=<daemonset-namespace> | STABLE | -| kube_daemonset_status_desired_number_scheduled | Gauge | | `daemonset`=<daemonset-name>
`namespace`=<daemonset-namespace> | STABLE | -| kube_daemonset_status_number_available | Gauge | | `daemonset`=<daemonset-name>
`namespace`=<daemonset-namespace> | STABLE | -| kube_daemonset_status_number_misscheduled | Gauge | | `daemonset`=<daemonset-name>
`namespace`=<daemonset-namespace> | STABLE | -| kube_daemonset_status_number_ready | Gauge | | `daemonset`=<daemonset-name>
`namespace`=<daemonset-namespace> | STABLE | -| kube_daemonset_status_number_unavailable | Gauge | | `daemonset`=<daemonset-name>
`namespace`=<daemonset-namespace> | STABLE | -| kube_daemonset_status_observed_generation | Gauge | | `daemonset`=<daemonset-name>
`namespace`=<daemonset-namespace> | STABLE | -| kube_daemonset_status_updated_number_scheduled | Gauge | | `daemonset`=<daemonset-name>
`namespace`=<daemonset-namespace> | STABLE | -| kube_daemonset_metadata_generation | Gauge | | `daemonset`=<daemonset-name>
`namespace`=<daemonset-namespace> | STABLE | +| kube_daemonset_created | Gauge | | `daemonset`=<daemonset-name>
`namespace`=<daemonset-namespace> | STABLE | +| kube_daemonset_status_current_number_scheduled | Gauge | | `daemonset`=<daemonset-name>
`namespace`=<daemonset-namespace> | STABLE | +| kube_daemonset_status_desired_number_scheduled | Gauge | | `daemonset`=<daemonset-name>
`namespace`=<daemonset-namespace> | STABLE | +| kube_daemonset_status_number_available | Gauge | | `daemonset`=<daemonset-name>
`namespace`=<daemonset-namespace> | STABLE | +| kube_daemonset_status_number_misscheduled | Gauge | | `daemonset`=<daemonset-name>
`namespace`=<daemonset-namespace> | STABLE | +| kube_daemonset_status_number_ready | Gauge | | `daemonset`=<daemonset-name>
`namespace`=<daemonset-namespace> | STABLE | +| kube_daemonset_status_number_unavailable | Gauge | | `daemonset`=<daemonset-name>
`namespace`=<daemonset-namespace> | STABLE | +| kube_daemonset_status_observed_generation | Gauge | | `daemonset`=<daemonset-name>
`namespace`=<daemonset-namespace> | STABLE | +| kube_daemonset_status_updated_number_scheduled | Gauge | | `daemonset`=<daemonset-name>
`namespace`=<daemonset-namespace> | STABLE | +| kube_daemonset_metadata_generation | Gauge | | `daemonset`=<daemonset-name>
`namespace`=<daemonset-namespace> | STABLE | | kube_daemonset_labels | Gauge | Kubernetes labels converted to Prometheus labels controlled via [--metric-labels-allowlist](../../developer/cli-arguments.md) | `daemonset`=<daemonset-name>
`namespace`=<daemonset-namespace>
`label_DAEMONSET_LABEL`=<DAEMONSET_LABEL> | STABLE | +| kube_daemonset_deletion_timestamp | Gauge | Unix deletion timestamp | `daemonset`=<daemonset-name>
`namespace`=<daemonset-namespace> | EXPERIMENTAL | diff --git a/docs/metrics/workload/deployment-metrics.md b/docs/metrics/workload/deployment-metrics.md index 94f5159015..d0156ff8de 100644 --- a/docs/metrics/workload/deployment-metrics.md +++ b/docs/metrics/workload/deployment-metrics.md @@ -1,19 +1,20 @@ # Deployment Metrics -| Metric name | Metric type | Description | Labels/tags | Status | -| ----------------------------------------------------------- | ----------- | ------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------ | +| 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 | -| 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_metadata_generation | Gauge | | `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_metadata_generation | Gauge | | `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 | | `deployment`=<deployment-name>
`namespace`=<deployment-namespace> | STABLE | +| kube_deployment_created | Gauge | | `deployment`=<deployment-name>
`namespace`=<deployment-namespace> | STABLE | +| kube_deployment_deletion_timestamp | Gauge | Unix deletion timestamp | `deployment`=<deployment-name>
`namespace`=<deployment-namespace> | EXPIREMENTAL | diff --git a/docs/metrics/workload/statefulset-metrics.md b/docs/metrics/workload/statefulset-metrics.md index 08fd7c75c0..42ac8cac00 100644 --- a/docs/metrics/workload/statefulset-metrics.md +++ b/docs/metrics/workload/statefulset-metrics.md @@ -1,19 +1,20 @@ # Stateful Set Metrics -| Metric name | Metric type | Description | Labels/tags | Status | -| ------------------------------------------------------- | ----------- | ------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------ | +| Metric name | Metric type | Description | Labels/tags | Status | +| ------------------------------------------------------- | ----------- | --------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------ | | kube_statefulset_annotations | Gauge | Kubernetes annotations converted to Prometheus labels controlled via [--metric-annotations-allowlist](../../developer/cli-arguments.md) | `statefulset`=<statefulset-name>
`namespace`=<statefulset-namespace>
`annotation_STATEFULSET_ANNOTATION`=<STATEFULSET_ANNOTATION> | EXPERIMENTAL | -| kube_statefulset_status_replicas | Gauge | | `statefulset`=<statefulset-name>
`namespace`=<statefulset-namespace> | STABLE | -| kube_statefulset_status_replicas_current | Gauge | | `statefulset`=<statefulset-name>
`namespace`=<statefulset-namespace> | STABLE | -| kube_statefulset_status_replicas_ready | Gauge | | `statefulset`=<statefulset-name>
`namespace`=<statefulset-namespace> | STABLE | -| kube_statefulset_status_replicas_available | Gauge | | `statefulset`=<statefulset-name>
`namespace`=<statefulset-namespace> | EXPERIMENTAL | -| kube_statefulset_status_replicas_updated | Gauge | | `statefulset`=<statefulset-name>
`namespace`=<statefulset-namespace> | STABLE | -| kube_statefulset_status_observed_generation | Gauge | | `statefulset`=<statefulset-name>
`namespace`=<statefulset-namespace> | STABLE | -| kube_statefulset_replicas | Gauge | | `statefulset`=<statefulset-name>
`namespace`=<statefulset-namespace> | STABLE | -| kube_statefulset_ordinals_start | Gauge | | `statefulset`=<statefulset-name>
`namespace`=<statefulset-namespace> | STABLE | -| kube_statefulset_metadata_generation | Gauge | | `statefulset`=<statefulset-name>
`namespace`=<statefulset-namespace> | STABLE | -| kube_statefulset_persistentvolumeclaim_retention_policy | Gauge | | `statefulset`=<statefulset-name>
`namespace`=<statefulset-namespace>
`when_deleted`=<statefulset-when-deleted-pvc-policy>
`when_scaled`=<statefulset-when-scaled-pvc-policy> | EXPERIMENTAL | -| kube_statefulset_created | Gauge | | `statefulset`=<statefulset-name>
`namespace`=<statefulset-namespace> | STABLE | +| kube_statefulset_status_replicas | Gauge | | `statefulset`=<statefulset-name>
`namespace`=<statefulset-namespace> | STABLE | +| kube_statefulset_status_replicas_current | Gauge | | `statefulset`=<statefulset-name>
`namespace`=<statefulset-namespace> | STABLE | +| kube_statefulset_status_replicas_ready | Gauge | | `statefulset`=<statefulset-name>
`namespace`=<statefulset-namespace> | STABLE | +| kube_statefulset_status_replicas_available | Gauge | | `statefulset`=<statefulset-name>
`namespace`=<statefulset-namespace> | EXPERIMENTAL | +| kube_statefulset_status_replicas_updated | Gauge | | `statefulset`=<statefulset-name>
`namespace`=<statefulset-namespace> | STABLE | +| kube_statefulset_status_observed_generation | Gauge | | `statefulset`=<statefulset-name>
`namespace`=<statefulset-namespace> | STABLE | +| kube_statefulset_replicas | Gauge | | `statefulset`=<statefulset-name>
`namespace`=<statefulset-namespace> | STABLE | +| kube_statefulset_ordinals_start | Gauge | | `statefulset`=<statefulset-name>
`namespace`=<statefulset-namespace> | STABLE | +| kube_statefulset_metadata_generation | Gauge | | `statefulset`=<statefulset-name>
`namespace`=<statefulset-namespace> | STABLE | +| kube_statefulset_persistentvolumeclaim_retention_policy | Gauge | | `statefulset`=<statefulset-name>
`namespace`=<statefulset-namespace>
`when_deleted`=<statefulset-when-deleted-pvc-policy>
`when_scaled`=<statefulset-when-scaled-pvc-policy> | EXPERIMENTAL | +| kube_statefulset_created | Gauge | | `statefulset`=<statefulset-name>
`namespace`=<statefulset-namespace> | STABLE | | kube_statefulset_labels | Gauge | Kubernetes labels converted to Prometheus labels controlled via [--metric-labels-allowlist](../../developer/cli-arguments.md) | `statefulset`=<statefulset-name>
`namespace`=<statefulset-namespace>
`label_STATEFULSET_LABEL`=<STATEFULSET_LABEL> | STABLE | -| kube_statefulset_status_current_revision | Gauge | | `statefulset`=<statefulset-name>
`namespace`=<statefulset-namespace>
`revision`=<statefulset-current-revision> | STABLE | -| kube_statefulset_status_update_revision | Gauge | | `statefulset`=<statefulset-name>
`namespace`=<statefulset-namespace>
`revision`=<statefulset-update-revision> | STABLE | +| kube_statefulset_status_current_revision | Gauge | | `statefulset`=<statefulset-name>
`namespace`=<statefulset-namespace>
`revision`=<statefulset-current-revision> | STABLE | +| kube_statefulset_status_update_revision | Gauge | | `statefulset`=<statefulset-name>
`namespace`=<statefulset-namespace>
`revision`=<statefulset-update-revision> | STABLE | +| kube_statefulset_deletion_timestamp | Gauge | Unix deletion timestamp | `statefulset`=<statefulset-name>
`namespace`=<statefulset-namespace> | EXPERIMENTAL | diff --git a/internal/store/daemonset.go b/internal/store/daemonset.go index 2ef2bb0c4b..fb9f9f8e87 100644 --- a/internal/store/daemonset.go +++ b/internal/store/daemonset.go @@ -223,6 +223,26 @@ func daemonSetMetricFamilies(allowAnnotationsList, allowLabelsList []string) []g } }), ), + *generator.NewFamilyGeneratorWithStability( + "kube_daemonset_deletion_timestamp", + "Unix deletion timestamp", + metric.Gauge, + basemetrics.ALPHA, + "", + wrapDaemonSetFunc(func(d *v1.DaemonSet) *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( descDaemonSetAnnotationsName, descDaemonSetAnnotationsHelp, diff --git a/internal/store/daemonset_test.go b/internal/store/daemonset_test.go index 729e75cb47..2de6f579fb 100644 --- a/internal/store/daemonset_test.go +++ b/internal/store/daemonset_test.go @@ -228,6 +228,34 @@ func TestDaemonSetStore(t *testing.T) { "kube_daemonset_status_updated_number_scheduled", }, }, + { + Obj: &v1.DaemonSet{ + ObjectMeta: metav1.ObjectMeta{ + Name: "ds4", + CreationTimestamp: metav1.Time{Time: time.Unix(1500000000, 0)}, + DeletionTimestamp: &metav1.Time{Time: time.Unix(1800000000, 0)}, + Namespace: "ns4", + Labels: map[string]string{ + "app": "example4", + }, + Generation: 14, + }, + Status: v1.DaemonSetStatus{ + CurrentNumberScheduled: 10, + NumberMisscheduled: 5, + DesiredNumberScheduled: 0, + NumberReady: 0, + }, + }, + Want: ` + # HELP kube_daemonset_deletion_timestamp Unix deletion timestamp + # TYPE kube_daemonset_deletion_timestamp gauge + kube_daemonset_deletion_timestamp{daemonset="ds4",namespace="ns4"} 1.8e+09 +`, + MetricNames: []string{ + "kube_daemonset_deletion_timestamp", + }, + }, } for i, c := range cases { c.Func = generator.ComposeMetricGenFuncs(daemonSetMetricFamilies(c.AllowAnnotationsList, c.AllowLabelsList)) diff --git a/internal/store/deployment.go b/internal/store/deployment.go index 7f902b0c16..af6dff7406 100644 --- a/internal/store/deployment.go +++ b/internal/store/deployment.go @@ -283,6 +283,26 @@ func deploymentMetricFamilies(allowAnnotationsList, allowLabelsList []string) [] } }), ), + *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, diff --git a/internal/store/deployment_test.go b/internal/store/deployment_test.go index 31be4a17d6..283d73df14 100644 --- a/internal/store/deployment_test.go +++ b/internal/store/deployment_test.go @@ -31,6 +31,7 @@ import ( var ( depl1Replicas int32 = 200 depl2Replicas int32 = 5 + depl3Replicas int32 = 10 depl1MaxUnavailable = intstr.FromInt(10) depl2MaxUnavailable = intstr.FromString("25%") @@ -73,6 +74,8 @@ func TestDeploymentStore(t *testing.T) { # TYPE kube_deployment_spec_strategy_rollingupdate_max_surge 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{ { @@ -191,8 +194,30 @@ func TestDeploymentStore(t *testing.T) { kube_deployment_status_condition{deployment="depl2",namespace="ns2",condition="ReplicaFailure",status="unknown"} 0 `, }, + { + Obj: &v1.Deployment{ + ObjectMeta: metav1.ObjectMeta{ + Name: "deployment-terminating", + Namespace: "ns3", + CreationTimestamp: metav1.Time{Time: time.Unix(1600000000, 0)}, + DeletionTimestamp: &metav1.Time{Time: time.Unix(1800000000, 0)}, + Labels: map[string]string{ + "app": "example3", + }, + Generation: 22, + }, + Spec: v1.DeploymentSpec{ + Paused: true, + Replicas: &depl3Replicas, + }, + }, + Want: ` + # HELP kube_deployment_deletion_timestamp Unix deletion timestamp + # TYPE kube_deployment_deletion_timestamp gauge + kube_deployment_deletion_timestamp{deployment="deployment-terminating",namespace="ns3"} 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)) diff --git a/internal/store/poddisruptionbudget.go b/internal/store/poddisruptionbudget.go index c4fa0cb5b0..2f7ab286d1 100644 --- a/internal/store/poddisruptionbudget.go +++ b/internal/store/poddisruptionbudget.go @@ -185,6 +185,26 @@ func podDisruptionBudgetMetricFamilies(allowAnnotationsList, allowLabelsList []s } }), ), + *generator.NewFamilyGeneratorWithStability( + "kube_poddisruptionbudget_deletion_timestamp", + "Unix deletion timestamp", + metric.Gauge, + basemetrics.ALPHA, + "", + wrapPodDisruptionBudgetFunc(func(p *policyv1.PodDisruptionBudget) *metric.Family { + ms := []*metric.Metric{} + + if !p.DeletionTimestamp.IsZero() { + ms = append(ms, &metric.Metric{ + Value: float64(p.DeletionTimestamp.Unix()), + }) + } + + return &metric.Family{ + Metrics: ms, + } + }), + ), } } diff --git a/internal/store/poddisruptionbudget_test.go b/internal/store/poddisruptionbudget_test.go index f974412f6d..3f4442329b 100644 --- a/internal/store/poddisruptionbudget_test.go +++ b/internal/store/poddisruptionbudget_test.go @@ -48,6 +48,8 @@ func TestPodDisruptionBudgetStore(t *testing.T) { # TYPE kube_poddisruptionbudget_status_expected_pods gauge # HELP kube_poddisruptionbudget_status_observed_generation [STABLE] Most recent generation observed when updating this PDB status # TYPE kube_poddisruptionbudget_status_observed_generation gauge + # HELP kube_poddisruptionbudget_deletion_timestamp Unix deletion timestamp + # TYPE kube_poddisruptionbudget_deletion_timestamp gauge ` cases := []generateMetricsTestCase{ { @@ -128,6 +130,28 @@ func TestPodDisruptionBudgetStore(t *testing.T) { "kube_poddisruptionbudget_labels", }, }, + { + Obj: &policyv1.PodDisruptionBudget{ + ObjectMeta: metav1.ObjectMeta{ + Name: "pdb3", + Namespace: "ns3", + DeletionTimestamp: &metav1.Time{Time: time.Unix(1800000000, 0)}, + Generation: 14, + }, + Status: policyv1.PodDisruptionBudgetStatus{ + CurrentHealthy: 8, + DesiredHealthy: 9, + }, + }, + Want: ` + # HELP kube_poddisruptionbudget_deletion_timestamp Unix deletion timestamp + # TYPE kube_poddisruptionbudget_deletion_timestamp gauge + kube_poddisruptionbudget_deletion_timestamp{namespace="ns3",poddisruptionbudget="pdb3"} 1.8e+09 + `, + MetricNames: []string{ + "kube_poddisruptionbudget_deletion_timestamp", + }, + }, } for i, c := range cases { c.Func = generator.ComposeMetricGenFuncs(podDisruptionBudgetMetricFamilies(c.AllowAnnotationsList, c.AllowLabelsList)) diff --git a/internal/store/service.go b/internal/store/service.go index e4a8282e58..ac80a63108 100644 --- a/internal/store/service.go +++ b/internal/store/service.go @@ -184,6 +184,25 @@ func serviceMetricFamilies(allowAnnotationsList, allowLabelsList []string) []gen } }), ), + *generator.NewFamilyGeneratorWithStability( + "kube_service_deletion_timestamp", + "Unix deletion timestamp", + metric.Gauge, + basemetrics.ALPHA, + "", + wrapSvcFunc(func(s *v1.Service) *metric.Family { + ms := []*metric.Metric{} + + if !s.DeletionTimestamp.IsZero() { + ms = append(ms, &metric.Metric{ + Value: float64(s.DeletionTimestamp.Unix()), + }) + } + return &metric.Family{ + Metrics: ms, + } + }), + ), } } diff --git a/internal/store/service_test.go b/internal/store/service_test.go index 20be8ea83c..57f6d62edb 100644 --- a/internal/store/service_test.go +++ b/internal/store/service_test.go @@ -44,6 +44,8 @@ func TestServiceStore(t *testing.T) { # TYPE kube_service_spec_external_ip gauge # HELP kube_service_status_load_balancer_ingress [STABLE] Service load balancer ingress status # TYPE kube_service_status_load_balancer_ingress gauge + # HELP kube_service_deletion_timestamp Unix deletion timestamp + # TYPE kube_service_deletion_timestamp gauge ` cases := []generateMetricsTestCase{ { @@ -259,6 +261,32 @@ func TestServiceStore(t *testing.T) { kube_service_spec_type{namespace="default",service="test-service8",uid="uid8",type="LoadBalancer"} 1 `, }, + { + Obj: &v1.Service{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-service9", + CreationTimestamp: metav1.Time{Time: time.Unix(1500000000, 0)}, + DeletionTimestamp: &metav1.Time{Time: time.Unix(1800000000, 0)}, + Namespace: "default", + UID: "uid9", + Labels: map[string]string{ + "app": "example9", + }, + }, + Spec: v1.ServiceSpec{ + ClusterIP: "1.2.3.4", + Type: v1.ServiceTypeClusterIP, + }, + }, + Want: ` + # HELP kube_service_deletion_timestamp Unix deletion timestamp + # TYPE kube_service_deletion_timestamp gauge + kube_service_deletion_timestamp{namespace="default",service="test-service9",uid="uid9"} 1.8e+09 + `, + MetricNames: []string{ + "kube_service_deletion_timestamp", + }, + }, } for i, c := range cases { c.Func = generator.ComposeMetricGenFuncs(serviceMetricFamilies(nil, nil)) diff --git a/internal/store/statefulset.go b/internal/store/statefulset.go index 2cb6329d7c..77049e1a54 100644 --- a/internal/store/statefulset.go +++ b/internal/store/statefulset.go @@ -321,6 +321,26 @@ func statefulSetMetricFamilies(allowAnnotationsList, allowLabelsList []string) [ } }), ), + *generator.NewFamilyGeneratorWithStability( + "kube_statefulset_deletion_timestamp", + "Unix deletion timestamp", + metric.Gauge, + basemetrics.ALPHA, + "", + wrapStatefulSetFunc(func(s *v1.StatefulSet) *metric.Family { + ms := []*metric.Metric{} + + if !s.DeletionTimestamp.IsZero() { + ms = append(ms, &metric.Metric{ + Value: float64(s.DeletionTimestamp.Unix()), + }) + } + + return &metric.Family{ + Metrics: ms, + } + }), + ), } } diff --git a/internal/store/statefulset_test.go b/internal/store/statefulset_test.go index 7b57e8e933..45d810fb2e 100644 --- a/internal/store/statefulset_test.go +++ b/internal/store/statefulset_test.go @@ -30,6 +30,7 @@ var ( statefulSet1Replicas int32 = 3 statefulSet2Replicas int32 = 6 statefulSet3Replicas int32 = 9 + statefulSet6Replicas int32 = 1 statefulSet1ObservedGeneration int64 = 1 statefulSet2ObservedGeneration int64 = 2 @@ -410,6 +411,35 @@ func TestStatefulSetStore(t *testing.T) { "kube_statefulset_persistentvolumeclaim_retention_policy", }, }, + { + Obj: &v1.StatefulSet{ + ObjectMeta: metav1.ObjectMeta{ + Name: "statefulset6", + Namespace: "ns6", + DeletionTimestamp: &metav1.Time{Time: time.Unix(1800000000, 0)}, + Labels: map[string]string{ + "app": "example6", + }, + Generation: 1, + }, + Spec: v1.StatefulSetSpec{ + Replicas: &statefulSet6Replicas, + ServiceName: "statefulset6service", + }, + Status: v1.StatefulSetStatus{ + ObservedGeneration: 0, + Replicas: 1, + }, + }, + Want: ` + # HELP kube_statefulset_deletion_timestamp Unix deletion timestamp + # TYPE kube_statefulset_deletion_timestamp gauge + kube_statefulset_deletion_timestamp{statefulset="statefulset6",namespace="ns6"} 1.8e+09 + `, + MetricNames: []string{ + "kube_statefulset_deletion_timestamp", + }, + }, } for i, c := range cases { c.Func = generator.ComposeMetricGenFuncs(statefulSetMetricFamilies(nil, nil))