diff --git a/docs/horizontalpodautoscaler-metrics.md b/docs/horizontalpodautoscaler-metrics.md
index 0b4c1cc773..9531864edb 100644
--- a/docs/horizontalpodautoscaler-metrics.md
+++ b/docs/horizontalpodautoscaler-metrics.md
@@ -2,10 +2,12 @@
| Metric name | Metric type | Labels/tags | Status |
| -------------------------------- | ----------- | ------------------------------------------------------------- | ------ |
+| kube_hpa_labels | Gauge | `hpa`=<hpa-name>
`namespace`=<hpa-namespace> | STABLE |
| kube_hpa_metadata_generation | Gauge | `hpa`=<hpa-name>
`namespace`=<hpa-namespace> | STABLE |
| kube_hpa_spec_max_replicas | Gauge | `hpa`=<hpa-name>
`namespace`=<hpa-namespace> | STABLE |
| kube_hpa_spec_min_replicas | Gauge | `hpa`=<hpa-name>
`namespace`=<hpa-namespace> | STABLE |
+| kube_hpa_status_condition | Gauge | `hpa`=<hpa-name>
`namespace`=<hpa-namespace>
`condition`=<hpa-condition>
`status`=<true\|false\|unknown> | STABLE |
| kube_hpa_status_current_replicas | Gauge | `hpa`=<hpa-name>
`namespace`=<hpa-namespace> | STABLE |
+| kube_hpa_status_currentmetrics_average_utilization | Gauge | `hpa`=<hpa-name>
`namespace`=<hpa-namespace> | EXPERIMENTAL |
+| kube_hpa_status_currentmetrics_average_value | Gauge | `hpa`=<hpa-name>
`namespace`=<hpa-namespace> | EXPERIMENTAL |
| kube_hpa_status_desired_replicas | Gauge | `hpa`=<hpa-name>
`namespace`=<hpa-namespace> | STABLE |
-| kube_hpa_status_condition | Gauge | `hpa`=<hpa-name>
`namespace`=<hpa-namespace>
`condition`=<hpa-condition>
`status`=<true\|false\|unknown> | STABLE |
-| kube_hpa_labels | Gauge | `hpa`=<hpa-name>
`namespace`=<hpa-namespace> | STABLE |
diff --git a/internal/store/hpa.go b/internal/store/hpa.go
index 08a949c29b..251debc6ab 100644
--- a/internal/store/hpa.go
+++ b/internal/store/hpa.go
@@ -20,6 +20,7 @@ import (
"k8s.io/kube-state-metrics/pkg/metric"
autoscaling "k8s.io/api/autoscaling/v2beta1"
+ "k8s.io/apimachinery/pkg/api/resource"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/watch"
@@ -143,6 +144,56 @@ var (
}
}),
},
+ {
+ Name: "kube_hpa_status_currentmetrics_average_value",
+ Type: metric.Gauge,
+ Help: "Average metric value observed by the autoscaler.",
+ GenerateFunc: wrapHPAFunc(func(a *autoscaling.HorizontalPodAutoscaler) *metric.Family {
+ ms := make([]*metric.Metric, len(a.Status.CurrentMetrics))
+ for i, c := range a.Status.CurrentMetrics {
+ var value *resource.Quantity
+ switch c.Type {
+ case autoscaling.ResourceMetricSourceType:
+ value = &c.Resource.CurrentAverageValue
+ case autoscaling.PodsMetricSourceType:
+ value = &c.Pods.CurrentAverageValue
+ case autoscaling.ObjectMetricSourceType:
+ value = c.Object.AverageValue
+ case autoscaling.ExternalMetricSourceType:
+ value = c.External.CurrentAverageValue
+ default:
+ // Skip unsupported metric type
+ continue
+ }
+ if intVal, canFastConvert := value.AsInt64(); canFastConvert {
+ ms[i] = &metric.Metric{
+ Value: float64(intVal),
+ }
+ }
+ }
+ return &metric.Family{
+ Metrics: ms,
+ }
+ }),
+ },
+ {
+ Name: "kube_hpa_status_currentmetrics_average_utilization",
+ Type: metric.Gauge,
+ Help: "Average metric utilization observed by the autoscaler.",
+ GenerateFunc: wrapHPAFunc(func(a *autoscaling.HorizontalPodAutoscaler) *metric.Family {
+ ms := make([]*metric.Metric, len(a.Status.CurrentMetrics))
+ for i, c := range a.Status.CurrentMetrics {
+ if c.Type == autoscaling.ResourceMetricSourceType {
+ ms[i] = &metric.Metric{
+ Value: float64(*c.Resource.CurrentAverageUtilization),
+ }
+ }
+ }
+ return &metric.Family{
+ Metrics: ms,
+ }
+ }),
+ },
}
)
diff --git a/internal/store/hpa_test.go b/internal/store/hpa_test.go
index fcea8f65c4..22d242539f 100644
--- a/internal/store/hpa_test.go
+++ b/internal/store/hpa_test.go
@@ -21,6 +21,7 @@ import (
autoscaling "k8s.io/api/autoscaling/v2beta1"
v1 "k8s.io/api/core/v1"
+ "k8s.io/apimachinery/pkg/api/resource"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/kube-state-metrics/pkg/metric"
@@ -44,10 +45,14 @@ func TestHPAStore(t *testing.T) {
# TYPE kube_hpa_status_current_replicas gauge
# HELP kube_hpa_status_desired_replicas Desired number of replicas of pods managed by this autoscaler.
# TYPE kube_hpa_status_desired_replicas gauge
- # HELP kube_hpa_status_condition The condition of this autoscaler.
- # TYPE kube_hpa_status_condition gauge
- # HELP kube_hpa_labels Kubernetes labels converted to Prometheus labels.
- # TYPE kube_hpa_labels gauge
+ # HELP kube_hpa_status_condition The condition of this autoscaler.
+ # TYPE kube_hpa_status_condition gauge
+ # HELP kube_hpa_labels Kubernetes labels converted to Prometheus labels.
+ # TYPE kube_hpa_labels gauge
+ # HELP kube_hpa_status_currentmetrics_average_value Average metric value observed by the autoscaler.
+ # TYPE kube_hpa_status_currentmetrics_average_value gauge
+ # HELP kube_hpa_status_currentmetrics_average_utilization Average metric utilization observed by the autoscaler.
+ # TYPE kube_hpa_status_currentmetrics_average_utilization gauge
`
cases := []generateMetricsTestCase{
{
@@ -77,20 +82,33 @@ func TestHPAStore(t *testing.T) {
{
Type: autoscaling.AbleToScale,
Status: v1.ConditionTrue,
+ Reason: "reason",
+ },
+ },
+ CurrentMetrics: []autoscaling.MetricStatus{
+ {
+ Type: "Resource",
+ Resource: &autoscaling.ResourceMetricStatus{
+ Name: "cpu",
+ CurrentAverageUtilization: new(int32),
+ CurrentAverageValue: resource.MustParse("10"),
+ },
},
},
},
},
Want: metadata + `
- kube_hpa_labels{hpa="hpa1",label_app="foobar",namespace="ns1"} 1
- kube_hpa_metadata_generation{hpa="hpa1",namespace="ns1"} 2
- kube_hpa_spec_max_replicas{hpa="hpa1",namespace="ns1"} 4
- kube_hpa_spec_min_replicas{hpa="hpa1",namespace="ns1"} 2
- kube_hpa_status_condition{condition="AbleToScale",hpa="hpa1",namespace="ns1",status="false"} 0
- kube_hpa_status_condition{condition="AbleToScale",hpa="hpa1",namespace="ns1",status="true"} 1
- kube_hpa_status_condition{condition="AbleToScale",hpa="hpa1",namespace="ns1",status="unknown"} 0
- kube_hpa_status_current_replicas{hpa="hpa1",namespace="ns1"} 2
- kube_hpa_status_desired_replicas{hpa="hpa1",namespace="ns1"} 2
+ kube_hpa_labels{hpa="hpa1",label_app="foobar",namespace="ns1"} 1
+ kube_hpa_metadata_generation{hpa="hpa1",namespace="ns1"} 2
+ kube_hpa_spec_max_replicas{hpa="hpa1",namespace="ns1"} 4
+ kube_hpa_spec_min_replicas{hpa="hpa1",namespace="ns1"} 2
+ kube_hpa_status_condition{condition="AbleToScale",hpa="hpa1",namespace="ns1",status="false"} 0
+ kube_hpa_status_condition{condition="AbleToScale",hpa="hpa1",namespace="ns1",status="true"} 1
+ kube_hpa_status_condition{condition="AbleToScale",hpa="hpa1",namespace="ns1",status="unknown"} 0
+ kube_hpa_status_current_replicas{hpa="hpa1",namespace="ns1"} 2
+ kube_hpa_status_desired_replicas{hpa="hpa1",namespace="ns1"} 2
+ kube_hpa_status_currentmetrics_average_value{hpa="hpa1",namespace="ns1"} 10
+ kube_hpa_status_currentmetrics_average_utilization{hpa="hpa1",namespace="ns1"} 0
`,
MetricNames: []string{
"kube_hpa_metadata_generation",
@@ -100,6 +118,8 @@ func TestHPAStore(t *testing.T) {
"kube_hpa_status_desired_replicas",
"kube_hpa_status_condition",
"kube_hpa_labels",
+ "kube_hpa_status_currentmetrics_average_value",
+ "kube_hpa_status_currentmetrics_average_utilization",
},
},
}