diff --git a/docs/metrics/workload/deployment-metrics.md b/docs/metrics/workload/deployment-metrics.md
index e8d0fdb42..cab9ff154 100644
--- a/docs/metrics/workload/deployment-metrics.md
+++ b/docs/metrics/workload/deployment-metrics.md
@@ -18,3 +18,4 @@
| 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 |
+| kube_deployment_owner | Gauge | Information about the Deployment's owner. | `deployment`=<deployment-name>
`namespace`=<deployment-namespace>
`owner_kind`=<owner-kind>
`owner_name`=<owner-name>
`owner_is_controller`=<true\|false> | ALPHA |
diff --git a/internal/store/deployment.go b/internal/store/deployment.go
index 31e3b200a..b610a387c 100644
--- a/internal/store/deployment.go
+++ b/internal/store/deployment.go
@@ -18,6 +18,7 @@ package store
import (
"context"
+ "strconv"
basemetrics "k8s.io/component-base/metrics"
@@ -60,6 +61,51 @@ var (
func deploymentMetricFamilies(allowAnnotationsList, allowLabelsList []string) []generator.FamilyGenerator {
return []generator.FamilyGenerator{
+ *generator.NewFamilyGeneratorWithStability(
+ "kube_deployment_owner",
+ "Information about the Deployment's owner.",
+ metric.Gauge,
+ basemetrics.ALPHA,
+ "",
+ wrapDeploymentFunc(func(d *v1.Deployment) *metric.Family {
+ labelKeys := []string{"owner_kind", "owner_name", "owner_is_controller"}
+
+ owners := d.GetOwnerReferences()
+ if len(owners) == 0 {
+ return &metric.Family{
+ Metrics: []*metric.Metric{
+ {
+ LabelKeys: labelKeys,
+ LabelValues: []string{"", "", ""},
+ Value: 1,
+ },
+ },
+ }
+ }
+
+ ms := make([]*metric.Metric, len(owners))
+
+ for i, owner := range owners {
+ if owner.Controller != nil {
+ ms[i] = &metric.Metric{
+ LabelKeys: labelKeys,
+ LabelValues: []string{owner.Kind, owner.Name, strconv.FormatBool(*owner.Controller)},
+ Value: 1,
+ }
+ } else {
+ ms[i] = &metric.Metric{
+ LabelKeys: labelKeys,
+ LabelValues: []string{owner.Kind, owner.Name, "false"},
+ Value: 1,
+ }
+ }
+ }
+
+ return &metric.Family{
+ Metrics: ms,
+ }
+ }),
+ ),
*generator.NewFamilyGeneratorWithStability(
"kube_deployment_created",
"Unix creation timestamp",
diff --git a/internal/store/deployment_test.go b/internal/store/deployment_test.go
index a0e3f4aa1..d8f0bd1d5 100644
--- a/internal/store/deployment_test.go
+++ b/internal/store/deployment_test.go
@@ -45,6 +45,8 @@ func TestDeploymentStore(t *testing.T) {
// Fixed metadata on type and help text. We prepend this to every expected
// output so we only have to modify a single place when doing adjustments.
const metadata = `
+ # HELP kube_deployment_owner Information about the Deployment's owner.
+ # TYPE kube_deployment_owner gauge
# HELP kube_deployment_annotations Kubernetes annotations converted to Prometheus labels.
# TYPE kube_deployment_annotations gauge
# HELP kube_deployment_created [STABLE] Unix creation timestamp
@@ -119,6 +121,7 @@ func TestDeploymentStore(t *testing.T) {
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_owner{deployment="depl1",namespace="ns1",owner_kind="",owner_name="",owner_is_controller=""} 1
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
@@ -174,6 +177,7 @@ func TestDeploymentStore(t *testing.T) {
},
Want: metadata + `
kube_deployment_metadata_generation{deployment="depl2",namespace="ns2"} 14
+ kube_deployment_owner{deployment="depl2",namespace="ns2",owner_kind="",owner_name="",owner_is_controller=""} 1
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
@@ -213,6 +217,7 @@ func TestDeploymentStore(t *testing.T) {
},
Want: metadata + `
kube_deployment_metadata_generation{deployment="depl3",namespace="ns3"} 0
+ kube_deployment_owner{deployment="depl3",namespace="ns3",owner_kind="",owner_name="",owner_is_controller=""} 1
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
@@ -252,6 +257,59 @@ func TestDeploymentStore(t *testing.T) {
kube_deployment_deletion_timestamp{deployment="deployment-terminating",namespace="ns4"} 1.8e+09`,
MetricNames: []string{"kube_deployment_deletion_timestamp"},
},
+ {
+ Obj: &v1.Deployment{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "deployment-with-owner",
+ Namespace: "ns5",
+ OwnerReferences: []metav1.OwnerReference{
+ {
+ Kind: "Application",
+ Name: "my-app",
+ Controller: &[]bool{true}[0],
+ },
+ },
+ },
+ Spec: v1.DeploymentSpec{
+ Replicas: &depl1Replicas,
+ },
+ },
+ Want: metadata + `
+ kube_deployment_owner{deployment="deployment-with-owner",namespace="ns5",owner_kind="Application",owner_name="my-app",owner_is_controller="true"} 1
+ kube_deployment_metadata_generation{deployment="deployment-with-owner",namespace="ns5"} 0
+ kube_deployment_spec_paused{deployment="deployment-with-owner",namespace="ns5"} 0
+ kube_deployment_spec_replicas{deployment="deployment-with-owner",namespace="ns5"} 200
+ kube_deployment_status_observed_generation{deployment="deployment-with-owner",namespace="ns5"} 0
+ kube_deployment_status_replicas{deployment="deployment-with-owner",namespace="ns5"} 0
+ kube_deployment_status_replicas_available{deployment="deployment-with-owner",namespace="ns5"} 0
+ kube_deployment_status_replicas_ready{deployment="deployment-with-owner",namespace="ns5"} 0
+ kube_deployment_status_replicas_unavailable{deployment="deployment-with-owner",namespace="ns5"} 0
+ kube_deployment_status_replicas_updated{deployment="deployment-with-owner",namespace="ns5"} 0
+ `,
+ },
+ {
+ Obj: &v1.Deployment{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "deployment-without-owner",
+ Namespace: "ns5",
+ },
+ Spec: v1.DeploymentSpec{
+ Replicas: &depl1Replicas,
+ },
+ },
+ Want: metadata + `
+ kube_deployment_owner{deployment="deployment-without-owner",namespace="ns5",owner_kind="",owner_name="",owner_is_controller=""} 1
+ kube_deployment_metadata_generation{deployment="deployment-without-owner",namespace="ns5"} 0
+ kube_deployment_spec_paused{deployment="deployment-without-owner",namespace="ns5"} 0
+ kube_deployment_spec_replicas{deployment="deployment-without-owner",namespace="ns5"} 200
+ kube_deployment_status_observed_generation{deployment="deployment-without-owner",namespace="ns5"} 0
+ kube_deployment_status_replicas{deployment="deployment-without-owner",namespace="ns5"} 0
+ kube_deployment_status_replicas_available{deployment="deployment-without-owner",namespace="ns5"} 0
+ kube_deployment_status_replicas_ready{deployment="deployment-without-owner",namespace="ns5"} 0
+ kube_deployment_status_replicas_unavailable{deployment="deployment-without-owner",namespace="ns5"} 0
+ kube_deployment_status_replicas_updated{deployment="deployment-without-owner",namespace="ns5"} 0
+ `,
+ },
}
for i, c := range cases {
c.Func = generator.ComposeMetricGenFuncs(deploymentMetricFamilies(c.AllowAnnotationsList, nil))