Merge pull request #90691 from arjunrn/container-resource-hpa

Add container based scaling to HPA
This commit is contained in:
Kubernetes Prow Robot 2020-10-23 05:51:51 -07:00 committed by GitHub
commit ec453ffb1a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
54 changed files with 5526 additions and 806 deletions

View File

@ -3024,6 +3024,61 @@
],
"type": "object"
},
"io.k8s.api.autoscaling.v2beta1.ContainerResourceMetricSource": {
"description": "ContainerResourceMetricSource indicates how to scale on a resource metric known to Kubernetes, as specified in requests and limits, describing each pod in the current scale target (e.g. CPU or memory). The values will be averaged together before being compared to the target. Such metrics are built in to Kubernetes, and have special scaling options on top of those available to normal per-pod metrics using the \"pods\" source. Only one \"target\" type should be set.",
"properties": {
"container": {
"description": "container is the name of the container in the pods of the scaling target",
"type": "string"
},
"name": {
"description": "name is the name of the resource in question.",
"type": "string"
},
"targetAverageUtilization": {
"description": "targetAverageUtilization is the target value of the average of the resource metric across all relevant pods, represented as a percentage of the requested value of the resource for the pods.",
"format": "int32",
"type": "integer"
},
"targetAverageValue": {
"$ref": "#/definitions/io.k8s.apimachinery.pkg.api.resource.Quantity",
"description": "targetAverageValue is the target value of the average of the resource metric across all relevant pods, as a raw value (instead of as a percentage of the request), similar to the \"pods\" metric source type."
}
},
"required": [
"name",
"container"
],
"type": "object"
},
"io.k8s.api.autoscaling.v2beta1.ContainerResourceMetricStatus": {
"description": "ContainerResourceMetricStatus indicates the current value of a resource metric known to Kubernetes, as specified in requests and limits, describing a single container in each pod in the current scale target (e.g. CPU or memory). Such metrics are built in to Kubernetes, and have special scaling options on top of those available to normal per-pod metrics using the \"pods\" source.",
"properties": {
"container": {
"description": "container is the name of the container in the pods of the scaling target",
"type": "string"
},
"currentAverageUtilization": {
"description": "currentAverageUtilization is the current value of the average of the resource metric across all relevant pods, represented as a percentage of the requested value of the resource for the pods. It will only be present if `targetAverageValue` was set in the corresponding metric specification.",
"format": "int32",
"type": "integer"
},
"currentAverageValue": {
"$ref": "#/definitions/io.k8s.apimachinery.pkg.api.resource.Quantity",
"description": "currentAverageValue is the current value of the average of the resource metric across all relevant pods, as a raw value (instead of as a percentage of the request), similar to the \"pods\" metric source type. It will always be set, regardless of the corresponding metric specification."
},
"name": {
"description": "name is the name of the resource in question.",
"type": "string"
}
},
"required": [
"name",
"currentAverageValue",
"container"
],
"type": "object"
},
"io.k8s.api.autoscaling.v2beta1.CrossVersionObjectReference": {
"description": "CrossVersionObjectReference contains enough information to let you identify the referred resource.",
"properties": {
@ -3273,6 +3328,10 @@
"io.k8s.api.autoscaling.v2beta1.MetricSpec": {
"description": "MetricSpec specifies how to scale based on a single metric (only `type` and one other matching field should be set at once).",
"properties": {
"containerResource": {
"$ref": "#/definitions/io.k8s.api.autoscaling.v2beta1.ContainerResourceMetricSource",
"description": "container resource refers to a resource metric (such as those specified in requests and limits) known to Kubernetes describing a single container in each pod of the current scale target (e.g. CPU or memory). Such metrics are built in to Kubernetes, and have special scaling options on top of those available to normal per-pod metrics using the \"pods\" source. This is an alpha feature and can be enabled by the HPAContainerMetrics feature flag."
},
"external": {
"$ref": "#/definitions/io.k8s.api.autoscaling.v2beta1.ExternalMetricSource",
"description": "external refers to a global metric that is not associated with any Kubernetes object. It allows autoscaling based on information coming from components running outside of cluster (for example length of queue in cloud messaging service, or QPS from loadbalancer running outside of cluster)."
@ -3290,7 +3349,7 @@
"description": "resource refers to a resource metric (such as those specified in requests and limits) known to Kubernetes describing each pod in the current scale target (e.g. CPU or memory). Such metrics are built in to Kubernetes, and have special scaling options on top of those available to normal per-pod metrics using the \"pods\" source."
},
"type": {
"description": "type is the type of metric source. It should be one of \"Object\", \"Pods\", \"Resource\" or \"External\", each mapping to a matching field in the object.",
"description": "type is the type of metric source. It should be one of \"ContainerResource\", \"External\", \"Object\", \"Pods\" or \"Resource\", each mapping to a matching field in the object. Note: \"ContainerResource\" type is available on when the feature-gate HPAContainerMetrics is enabled",
"type": "string"
}
},
@ -3302,6 +3361,10 @@
"io.k8s.api.autoscaling.v2beta1.MetricStatus": {
"description": "MetricStatus describes the last-read state of a single metric.",
"properties": {
"containerResource": {
"$ref": "#/definitions/io.k8s.api.autoscaling.v2beta1.ContainerResourceMetricStatus",
"description": "container resource refers to a resource metric (such as those specified in requests and limits) known to Kubernetes describing a single container in each pod in the current scale target (e.g. CPU or memory). Such metrics are built in to Kubernetes, and have special scaling options on top of those available to normal per-pod metrics using the \"pods\" source."
},
"external": {
"$ref": "#/definitions/io.k8s.api.autoscaling.v2beta1.ExternalMetricStatus",
"description": "external refers to a global metric that is not associated with any Kubernetes object. It allows autoscaling based on information coming from components running outside of cluster (for example length of queue in cloud messaging service, or QPS from loadbalancer running outside of cluster)."
@ -3319,7 +3382,7 @@
"description": "resource refers to a resource metric (such as those specified in requests and limits) known to Kubernetes describing each pod in the current scale target (e.g. CPU or memory). Such metrics are built in to Kubernetes, and have special scaling options on top of those available to normal per-pod metrics using the \"pods\" source."
},
"type": {
"description": "type is the type of metric source. It will be one of \"Object\", \"Pods\" or \"Resource\", each corresponds to a matching field in the object.",
"description": "type is the type of metric source. It will be one of \"ContainerResource\", \"External\", \"Object\", \"Pods\" or \"Resource\", each corresponds to a matching field in the object. Note: \"ContainerResource\" type is available on when the feature-gate HPAContainerMetrics is enabled",
"type": "string"
}
},
@ -3479,6 +3542,52 @@
],
"type": "object"
},
"io.k8s.api.autoscaling.v2beta2.ContainerResourceMetricSource": {
"description": "ContainerResourceMetricSource indicates how to scale on a resource metric known to Kubernetes, as specified in requests and limits, describing each pod in the current scale target (e.g. CPU or memory). The values will be averaged together before being compared to the target. Such metrics are built in to Kubernetes, and have special scaling options on top of those available to normal per-pod metrics using the \"pods\" source. Only one \"target\" type should be set.",
"properties": {
"container": {
"description": "container is the name of the container in the pods of the scaling target",
"type": "string"
},
"name": {
"description": "name is the name of the resource in question.",
"type": "string"
},
"target": {
"$ref": "#/definitions/io.k8s.api.autoscaling.v2beta2.MetricTarget",
"description": "target specifies the target value for the given metric"
}
},
"required": [
"name",
"target",
"container"
],
"type": "object"
},
"io.k8s.api.autoscaling.v2beta2.ContainerResourceMetricStatus": {
"description": "ContainerResourceMetricStatus indicates the current value of a resource metric known to Kubernetes, as specified in requests and limits, describing a single container in each pod in the current scale target (e.g. CPU or memory). Such metrics are built in to Kubernetes, and have special scaling options on top of those available to normal per-pod metrics using the \"pods\" source.",
"properties": {
"container": {
"description": "Container is the name of the container in the pods of the scaling target",
"type": "string"
},
"current": {
"$ref": "#/definitions/io.k8s.api.autoscaling.v2beta2.MetricValueStatus",
"description": "current contains the current value for the given metric"
},
"name": {
"description": "Name is the name of the resource in question.",
"type": "string"
}
},
"required": [
"name",
"current",
"container"
],
"type": "object"
},
"io.k8s.api.autoscaling.v2beta2.CrossVersionObjectReference": {
"description": "CrossVersionObjectReference contains enough information to let you identify the referred resource.",
"properties": {
@ -3795,6 +3904,10 @@
"io.k8s.api.autoscaling.v2beta2.MetricSpec": {
"description": "MetricSpec specifies how to scale based on a single metric (only `type` and one other matching field should be set at once).",
"properties": {
"containerResource": {
"$ref": "#/definitions/io.k8s.api.autoscaling.v2beta2.ContainerResourceMetricSource",
"description": "container resource refers to a resource metric (such as those specified in requests and limits) known to Kubernetes describing a single container in each pod of the current scale target (e.g. CPU or memory). Such metrics are built in to Kubernetes, and have special scaling options on top of those available to normal per-pod metrics using the \"pods\" source. This is an alpha feature and can be enabled by the HPAContainerMetrics feature flag."
},
"external": {
"$ref": "#/definitions/io.k8s.api.autoscaling.v2beta2.ExternalMetricSource",
"description": "external refers to a global metric that is not associated with any Kubernetes object. It allows autoscaling based on information coming from components running outside of cluster (for example length of queue in cloud messaging service, or QPS from loadbalancer running outside of cluster)."
@ -3812,7 +3925,7 @@
"description": "resource refers to a resource metric (such as those specified in requests and limits) known to Kubernetes describing each pod in the current scale target (e.g. CPU or memory). Such metrics are built in to Kubernetes, and have special scaling options on top of those available to normal per-pod metrics using the \"pods\" source."
},
"type": {
"description": "type is the type of metric source. It should be one of \"Object\", \"Pods\", \"Resource\" or \"External\", each mapping to a matching field in the object.",
"description": "type is the type of metric source. It should be one of \"ContainerResource\", \"External\", \"Object\", \"Pods\" or \"Resource\", each mapping to a matching field in the object. Note: \"ContainerResource\" type is available on when the feature-gate HPAContainerMetrics is enabled",
"type": "string"
}
},
@ -3824,6 +3937,10 @@
"io.k8s.api.autoscaling.v2beta2.MetricStatus": {
"description": "MetricStatus describes the last-read state of a single metric.",
"properties": {
"containerResource": {
"$ref": "#/definitions/io.k8s.api.autoscaling.v2beta2.ContainerResourceMetricStatus",
"description": "container resource refers to a resource metric (such as those specified in requests and limits) known to Kubernetes describing a single container in each pod in the current scale target (e.g. CPU or memory). Such metrics are built in to Kubernetes, and have special scaling options on top of those available to normal per-pod metrics using the \"pods\" source."
},
"external": {
"$ref": "#/definitions/io.k8s.api.autoscaling.v2beta2.ExternalMetricStatus",
"description": "external refers to a global metric that is not associated with any Kubernetes object. It allows autoscaling based on information coming from components running outside of cluster (for example length of queue in cloud messaging service, or QPS from loadbalancer running outside of cluster)."
@ -3841,7 +3958,7 @@
"description": "resource refers to a resource metric (such as those specified in requests and limits) known to Kubernetes describing each pod in the current scale target (e.g. CPU or memory). Such metrics are built in to Kubernetes, and have special scaling options on top of those available to normal per-pod metrics using the \"pods\" source."
},
"type": {
"description": "type is the type of metric source. It will be one of \"Object\", \"Pods\" or \"Resource\", each corresponds to a matching field in the object.",
"description": "type is the type of metric source. It will be one of \"ContainerResource\", \"External\", \"Object\", \"Pods\" or \"Resource\", each corresponds to a matching field in the object. Note: \"ContainerResource\" type is available on when the feature-gate HPAContainerMetrics is enabled",
"type": "string"
}
},

View File

@ -204,6 +204,12 @@ const (
// (for example length of queue in cloud messaging service, or
// QPS from loadbalancer running outside of cluster).
ExternalMetricSourceType MetricSourceType = "External"
// ContainerResourceMetricSourceType is a resource metric known to Kubernetes, as
// specified in requests and limits, describing a single container in each pod in the current
// scale target (e.g. CPU or memory). Such metrics are built in to
// Kubernetes, and have special scaling options on top of those available
// to normal per-pod metrics (the "pods" source).
ContainerResourceMetricSourceType MetricSourceType = "ContainerResource"
)
// MetricSpec specifies how to scale based on a single metric
@ -229,6 +235,13 @@ type MetricSpec struct {
// to normal per-pod metrics using the "pods" source.
// +optional
Resource *ResourceMetricSource
// ContainerResource refers to a resource metric (such as those specified in
// requests and limits) known to Kubernetes describing a single container in each pod of the
// current scale target (e.g. CPU or memory). Such metrics are built in to
// Kubernetes, and have special scaling options on top of those available
// to normal per-pod metrics using the "pods" source.
// +optional
ContainerResource *ContainerResourceMetricSource
// External refers to a global metric that is not associated
// with any Kubernetes object. It allows autoscaling based on information
// coming from components running outside of cluster
@ -271,6 +284,22 @@ type ResourceMetricSource struct {
Target MetricTarget
}
// ContainerResourceMetricSource indicates how to scale on a resource metric known to
// Kubernetes, as specified in the requests and limits, describing a single container in
// each of the pods of the current scale target(e.g. CPU or memory). The values will be
// averaged together before being compared to the target. Such metrics are built into
// Kubernetes, and have special scaling options on top of those available to
// normal per-pod metrics using the "pods" source. Only one "target" type
// should be set.
type ContainerResourceMetricSource struct {
// name is the name of the of the resource
Name api.ResourceName
// container is the name of the container in the pods of the scaling target.
Container string
// target specifies the target value for the given metric
Target MetricTarget
}
// ExternalMetricSource indicates how to scale on a metric not associated with
// any Kubernetes object (for example length of queue in cloud
// messaging service, or QPS from loadbalancer running outside of cluster).
@ -420,6 +449,13 @@ type MetricStatus struct {
// to normal per-pod metrics using the "pods" source.
// +optional
Resource *ResourceMetricStatus
// ContainerResource refers to a resource metric (such as those specified in
// requests and limits) known to Kubernetes describing a single container in each pod in the
// current scale target (e.g. CPU or memory). Such metrics are built in to
// Kubernetes, and have special scaling options on top of those available
// to normal per-pod metrics using the "pods" source.
// +optional
ContainerResource *ContainerResourceMetricStatus
// External refers to a global metric that is not associated
// with any Kubernetes object. It allows autoscaling based on information
// coming from components running outside of cluster
@ -456,6 +492,18 @@ type ResourceMetricStatus struct {
Current MetricValueStatus
}
// ContainerResourceMetricStatus indicates the current value of a resource metric known to
// Kubernetes, as specified in requests and limits, describing each pod in the
// current scale target (e.g. CPU or memory). Such metrics are built in to
// Kubernetes, and have special scaling options on top of those available to
// normal per-pod metrics using the "pods" source.
type ContainerResourceMetricStatus struct {
// Name is the name of the resource in question.
Name api.ResourceName
Container string
Current MetricValueStatus
}
// ExternalMetricStatus indicates the current value of a global metric
// not associated with any Kubernetes object.
type ExternalMetricStatus struct {

View File

@ -235,6 +235,28 @@ func Convert_autoscaling_ResourceMetricSource_To_v1_ResourceMetricSource(in *aut
return nil
}
func Convert_v1_ContainerResourceMetricStatus_To_autoscaling_ContainerResourceMetricStatus(in *autoscalingv1.ContainerResourceMetricStatus, out *autoscaling.ContainerResourceMetricStatus, s conversion.Scope) error {
out.Name = core.ResourceName(in.Name)
out.Container = in.Container
utilization := in.CurrentAverageUtilization
averageValue := &in.CurrentAverageValue
out.Current = autoscaling.MetricValueStatus{
AverageValue: averageValue,
AverageUtilization: utilization,
}
return nil
}
func Convert_autoscaling_ContainerResourceMetricStatus_To_v1_ContainerResourceMetricStatus(in *autoscaling.ContainerResourceMetricStatus, out *autoscalingv1.ContainerResourceMetricStatus, s conversion.Scope) error {
out.Name = v1.ResourceName(in.Name)
out.Container = in.Container
out.CurrentAverageUtilization = in.Current.AverageUtilization
if in.Current.AverageValue != nil {
out.CurrentAverageValue = *in.Current.AverageValue
}
return nil
}
func Convert_v1_ResourceMetricStatus_To_autoscaling_ResourceMetricStatus(in *autoscalingv1.ResourceMetricStatus, out *autoscaling.ResourceMetricStatus, s conversion.Scope) error {
out.Name = core.ResourceName(in.Name)
utilization := in.CurrentAverageUtilization
@ -517,3 +539,30 @@ func Convert_v1_HorizontalPodAutoscalerStatus_To_autoscaling_HorizontalPodAutosc
}
return nil
}
func Convert_v1_ContainerResourceMetricSource_To_autoscaling_ContainerResourceMetricSource(in *autoscalingv1.ContainerResourceMetricSource, out *autoscaling.ContainerResourceMetricSource, s conversion.Scope) error {
out.Name = core.ResourceName(in.Name)
out.Container = in.Container
utilization := in.TargetAverageUtilization
averageValue := in.TargetAverageValue
var metricType autoscaling.MetricTargetType
if utilization == nil {
metricType = autoscaling.AverageValueMetricType
} else {
metricType = autoscaling.UtilizationMetricType
}
out.Target = autoscaling.MetricTarget{
Type: metricType,
AverageValue: averageValue,
AverageUtilization: utilization,
}
return nil
}
func Convert_autoscaling_ContainerResourceMetricSource_To_v1_ContainerResourceMetricSource(in *autoscaling.ContainerResourceMetricSource, out *autoscalingv1.ContainerResourceMetricSource, s conversion.Scope) error {
out.Name = v1.ResourceName(in.Name)
out.Container = in.Container
out.TargetAverageUtilization = in.Target.AverageUtilization
out.TargetAverageValue = in.Target.AverageValue
return nil
}

View File

@ -119,6 +119,16 @@ func RegisterConversions(s *runtime.Scheme) error {
}); err != nil {
return err
}
if err := s.AddConversionFunc((*autoscaling.ContainerResourceMetricSource)(nil), (*v1.ContainerResourceMetricSource)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_autoscaling_ContainerResourceMetricSource_To_v1_ContainerResourceMetricSource(a.(*autoscaling.ContainerResourceMetricSource), b.(*v1.ContainerResourceMetricSource), scope)
}); err != nil {
return err
}
if err := s.AddConversionFunc((*autoscaling.ContainerResourceMetricStatus)(nil), (*v1.ContainerResourceMetricStatus)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_autoscaling_ContainerResourceMetricStatus_To_v1_ContainerResourceMetricStatus(a.(*autoscaling.ContainerResourceMetricStatus), b.(*v1.ContainerResourceMetricStatus), scope)
}); err != nil {
return err
}
if err := s.AddConversionFunc((*autoscaling.ExternalMetricSource)(nil), (*v1.ExternalMetricSource)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_autoscaling_ExternalMetricSource_To_v1_ExternalMetricSource(a.(*autoscaling.ExternalMetricSource), b.(*v1.ExternalMetricSource), scope)
}); err != nil {
@ -179,6 +189,16 @@ func RegisterConversions(s *runtime.Scheme) error {
}); err != nil {
return err
}
if err := s.AddConversionFunc((*v1.ContainerResourceMetricSource)(nil), (*autoscaling.ContainerResourceMetricSource)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_v1_ContainerResourceMetricSource_To_autoscaling_ContainerResourceMetricSource(a.(*v1.ContainerResourceMetricSource), b.(*autoscaling.ContainerResourceMetricSource), scope)
}); err != nil {
return err
}
if err := s.AddConversionFunc((*v1.ContainerResourceMetricStatus)(nil), (*autoscaling.ContainerResourceMetricStatus)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_v1_ContainerResourceMetricStatus_To_autoscaling_ContainerResourceMetricStatus(a.(*v1.ContainerResourceMetricStatus), b.(*autoscaling.ContainerResourceMetricStatus), scope)
}); err != nil {
return err
}
if err := s.AddConversionFunc((*v1.CrossVersionObjectReference)(nil), (*autoscaling.MetricTarget)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_v1_CrossVersionObjectReference_To_autoscaling_MetricTarget(a.(*v1.CrossVersionObjectReference), b.(*autoscaling.MetricTarget), scope)
}); err != nil {
@ -242,6 +262,36 @@ func RegisterConversions(s *runtime.Scheme) error {
return nil
}
func autoConvert_v1_ContainerResourceMetricSource_To_autoscaling_ContainerResourceMetricSource(in *v1.ContainerResourceMetricSource, out *autoscaling.ContainerResourceMetricSource, s conversion.Scope) error {
out.Name = core.ResourceName(in.Name)
// WARNING: in.TargetAverageUtilization requires manual conversion: does not exist in peer-type
// WARNING: in.TargetAverageValue requires manual conversion: does not exist in peer-type
out.Container = in.Container
return nil
}
func autoConvert_autoscaling_ContainerResourceMetricSource_To_v1_ContainerResourceMetricSource(in *autoscaling.ContainerResourceMetricSource, out *v1.ContainerResourceMetricSource, s conversion.Scope) error {
out.Name = corev1.ResourceName(in.Name)
out.Container = in.Container
// WARNING: in.Target requires manual conversion: does not exist in peer-type
return nil
}
func autoConvert_v1_ContainerResourceMetricStatus_To_autoscaling_ContainerResourceMetricStatus(in *v1.ContainerResourceMetricStatus, out *autoscaling.ContainerResourceMetricStatus, s conversion.Scope) error {
out.Name = core.ResourceName(in.Name)
// WARNING: in.CurrentAverageUtilization requires manual conversion: does not exist in peer-type
// WARNING: in.CurrentAverageValue requires manual conversion: does not exist in peer-type
out.Container = in.Container
return nil
}
func autoConvert_autoscaling_ContainerResourceMetricStatus_To_v1_ContainerResourceMetricStatus(in *autoscaling.ContainerResourceMetricStatus, out *v1.ContainerResourceMetricStatus, s conversion.Scope) error {
out.Name = corev1.ResourceName(in.Name)
out.Container = in.Container
// WARNING: in.Current requires manual conversion: does not exist in peer-type
return nil
}
func autoConvert_v1_CrossVersionObjectReference_To_autoscaling_CrossVersionObjectReference(in *v1.CrossVersionObjectReference, out *autoscaling.CrossVersionObjectReference, s conversion.Scope) error {
out.Kind = in.Kind
out.Name = in.Name
@ -455,6 +505,15 @@ func autoConvert_v1_MetricSpec_To_autoscaling_MetricSpec(in *v1.MetricSpec, out
} else {
out.Resource = nil
}
if in.ContainerResource != nil {
in, out := &in.ContainerResource, &out.ContainerResource
*out = new(autoscaling.ContainerResourceMetricSource)
if err := Convert_v1_ContainerResourceMetricSource_To_autoscaling_ContainerResourceMetricSource(*in, *out, s); err != nil {
return err
}
} else {
out.ContainerResource = nil
}
if in.External != nil {
in, out := &in.External, &out.External
*out = new(autoscaling.ExternalMetricSource)
@ -501,6 +560,15 @@ func autoConvert_autoscaling_MetricSpec_To_v1_MetricSpec(in *autoscaling.MetricS
} else {
out.Resource = nil
}
if in.ContainerResource != nil {
in, out := &in.ContainerResource, &out.ContainerResource
*out = new(v1.ContainerResourceMetricSource)
if err := Convert_autoscaling_ContainerResourceMetricSource_To_v1_ContainerResourceMetricSource(*in, *out, s); err != nil {
return err
}
} else {
out.ContainerResource = nil
}
if in.External != nil {
in, out := &in.External, &out.External
*out = new(v1.ExternalMetricSource)
@ -547,6 +615,15 @@ func autoConvert_v1_MetricStatus_To_autoscaling_MetricStatus(in *v1.MetricStatus
} else {
out.Resource = nil
}
if in.ContainerResource != nil {
in, out := &in.ContainerResource, &out.ContainerResource
*out = new(autoscaling.ContainerResourceMetricStatus)
if err := Convert_v1_ContainerResourceMetricStatus_To_autoscaling_ContainerResourceMetricStatus(*in, *out, s); err != nil {
return err
}
} else {
out.ContainerResource = nil
}
if in.External != nil {
in, out := &in.External, &out.External
*out = new(autoscaling.ExternalMetricStatus)
@ -593,6 +670,15 @@ func autoConvert_autoscaling_MetricStatus_To_v1_MetricStatus(in *autoscaling.Met
} else {
out.Resource = nil
}
if in.ContainerResource != nil {
in, out := &in.ContainerResource, &out.ContainerResource
*out = new(v1.ContainerResourceMetricStatus)
if err := Convert_autoscaling_ContainerResourceMetricStatus_To_v1_ContainerResourceMetricStatus(*in, *out, s); err != nil {
return err
}
} else {
out.ContainerResource = nil
}
if in.External != nil {
in, out := &in.External, &out.External
*out = new(v1.ExternalMetricStatus)

View File

@ -35,6 +35,28 @@ func Convert_v2beta1_CrossVersionObjectReference_To_autoscaling_MetricTarget(in
return nil
}
func Convert_v2beta1_ContainerResourceMetricStatus_To_autoscaling_ContainerResourceMetricStatus(in *autoscalingv2beta1.ContainerResourceMetricStatus, out *autoscaling.ContainerResourceMetricStatus, s conversion.Scope) error {
out.Name = core.ResourceName(in.Name)
out.Container = in.Container
utilization := in.CurrentAverageUtilization
averageValue := in.CurrentAverageValue
out.Current = autoscaling.MetricValueStatus{
AverageValue: &averageValue,
AverageUtilization: utilization,
}
return nil
}
func Convert_autoscaling_ContainerResourceMetricStatus_To_v2beta1_ContainerResourceMetricStatus(in *autoscaling.ContainerResourceMetricStatus, out *autoscalingv2beta1.ContainerResourceMetricStatus, s conversion.Scope) error {
out.Name = v1.ResourceName(in.Name)
out.Container = in.Container
out.CurrentAverageUtilization = in.Current.AverageUtilization
if in.Current.AverageValue != nil {
out.CurrentAverageValue = *in.Current.AverageValue
}
return nil
}
func Convert_v2beta1_ResourceMetricStatus_To_autoscaling_ResourceMetricStatus(in *autoscalingv2beta1.ResourceMetricStatus, out *autoscaling.ResourceMetricStatus, s conversion.Scope) error {
out.Name = core.ResourceName(in.Name)
utilization := in.CurrentAverageUtilization
@ -309,3 +331,29 @@ func Convert_v2beta1_HorizontalPodAutoscaler_To_autoscaling_HorizontalPodAutosca
func Convert_autoscaling_HorizontalPodAutoscalerSpec_To_v2beta1_HorizontalPodAutoscalerSpec(in *autoscaling.HorizontalPodAutoscalerSpec, out *autoscalingv2beta1.HorizontalPodAutoscalerSpec, s conversion.Scope) error {
return autoConvert_autoscaling_HorizontalPodAutoscalerSpec_To_v2beta1_HorizontalPodAutoscalerSpec(in, out, s)
}
func Convert_v2beta1_ContainerResourceMetricSource_To_autoscaling_ContainerResourceMetricSource(in *autoscalingv2beta1.ContainerResourceMetricSource, out *autoscaling.ContainerResourceMetricSource, s conversion.Scope) error {
out.Name = core.ResourceName(in.Name)
utilization := in.TargetAverageUtilization
averageValue := in.TargetAverageValue
var metricType autoscaling.MetricTargetType
if utilization == nil {
metricType = autoscaling.AverageValueMetricType
} else {
metricType = autoscaling.UtilizationMetricType
}
out.Target = autoscaling.MetricTarget{
Type: metricType,
AverageValue: averageValue,
AverageUtilization: utilization,
}
return nil
}
func Convert_autoscaling_ContainerResourceMetricSource_To_v2beta1_ContainerResourceMetricSource(in *autoscaling.ContainerResourceMetricSource, out *autoscalingv2beta1.ContainerResourceMetricSource, s conversion.Scope) error {
out.Name = v1.ResourceName(in.Name)
out.TargetAverageUtilization = in.Target.AverageUtilization
out.TargetAverageValue = in.Target.AverageValue
return nil
}

View File

@ -104,6 +104,16 @@ func RegisterConversions(s *runtime.Scheme) error {
}); err != nil {
return err
}
if err := s.AddConversionFunc((*autoscaling.ContainerResourceMetricSource)(nil), (*v2beta1.ContainerResourceMetricSource)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_autoscaling_ContainerResourceMetricSource_To_v2beta1_ContainerResourceMetricSource(a.(*autoscaling.ContainerResourceMetricSource), b.(*v2beta1.ContainerResourceMetricSource), scope)
}); err != nil {
return err
}
if err := s.AddConversionFunc((*autoscaling.ContainerResourceMetricStatus)(nil), (*v2beta1.ContainerResourceMetricStatus)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_autoscaling_ContainerResourceMetricStatus_To_v2beta1_ContainerResourceMetricStatus(a.(*autoscaling.ContainerResourceMetricStatus), b.(*v2beta1.ContainerResourceMetricStatus), scope)
}); err != nil {
return err
}
if err := s.AddConversionFunc((*autoscaling.ExternalMetricSource)(nil), (*v2beta1.ExternalMetricSource)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_autoscaling_ExternalMetricSource_To_v2beta1_ExternalMetricSource(a.(*autoscaling.ExternalMetricSource), b.(*v2beta1.ExternalMetricSource), scope)
}); err != nil {
@ -159,6 +169,16 @@ func RegisterConversions(s *runtime.Scheme) error {
}); err != nil {
return err
}
if err := s.AddConversionFunc((*v2beta1.ContainerResourceMetricSource)(nil), (*autoscaling.ContainerResourceMetricSource)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_v2beta1_ContainerResourceMetricSource_To_autoscaling_ContainerResourceMetricSource(a.(*v2beta1.ContainerResourceMetricSource), b.(*autoscaling.ContainerResourceMetricSource), scope)
}); err != nil {
return err
}
if err := s.AddConversionFunc((*v2beta1.ContainerResourceMetricStatus)(nil), (*autoscaling.ContainerResourceMetricStatus)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_v2beta1_ContainerResourceMetricStatus_To_autoscaling_ContainerResourceMetricStatus(a.(*v2beta1.ContainerResourceMetricStatus), b.(*autoscaling.ContainerResourceMetricStatus), scope)
}); err != nil {
return err
}
if err := s.AddConversionFunc((*v2beta1.CrossVersionObjectReference)(nil), (*autoscaling.MetricTarget)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_v2beta1_CrossVersionObjectReference_To_autoscaling_MetricTarget(a.(*v2beta1.CrossVersionObjectReference), b.(*autoscaling.MetricTarget), scope)
}); err != nil {
@ -212,6 +232,36 @@ func RegisterConversions(s *runtime.Scheme) error {
return nil
}
func autoConvert_v2beta1_ContainerResourceMetricSource_To_autoscaling_ContainerResourceMetricSource(in *v2beta1.ContainerResourceMetricSource, out *autoscaling.ContainerResourceMetricSource, s conversion.Scope) error {
out.Name = core.ResourceName(in.Name)
// WARNING: in.TargetAverageUtilization requires manual conversion: does not exist in peer-type
// WARNING: in.TargetAverageValue requires manual conversion: does not exist in peer-type
out.Container = in.Container
return nil
}
func autoConvert_autoscaling_ContainerResourceMetricSource_To_v2beta1_ContainerResourceMetricSource(in *autoscaling.ContainerResourceMetricSource, out *v2beta1.ContainerResourceMetricSource, s conversion.Scope) error {
out.Name = v1.ResourceName(in.Name)
out.Container = in.Container
// WARNING: in.Target requires manual conversion: does not exist in peer-type
return nil
}
func autoConvert_v2beta1_ContainerResourceMetricStatus_To_autoscaling_ContainerResourceMetricStatus(in *v2beta1.ContainerResourceMetricStatus, out *autoscaling.ContainerResourceMetricStatus, s conversion.Scope) error {
out.Name = core.ResourceName(in.Name)
// WARNING: in.CurrentAverageUtilization requires manual conversion: does not exist in peer-type
// WARNING: in.CurrentAverageValue requires manual conversion: does not exist in peer-type
out.Container = in.Container
return nil
}
func autoConvert_autoscaling_ContainerResourceMetricStatus_To_v2beta1_ContainerResourceMetricStatus(in *autoscaling.ContainerResourceMetricStatus, out *v2beta1.ContainerResourceMetricStatus, s conversion.Scope) error {
out.Name = v1.ResourceName(in.Name)
out.Container = in.Container
// WARNING: in.Current requires manual conversion: does not exist in peer-type
return nil
}
func autoConvert_v2beta1_CrossVersionObjectReference_To_autoscaling_CrossVersionObjectReference(in *v2beta1.CrossVersionObjectReference, out *autoscaling.CrossVersionObjectReference, s conversion.Scope) error {
out.Kind = in.Kind
out.Name = in.Name
@ -481,6 +531,15 @@ func autoConvert_v2beta1_MetricSpec_To_autoscaling_MetricSpec(in *v2beta1.Metric
} else {
out.Resource = nil
}
if in.ContainerResource != nil {
in, out := &in.ContainerResource, &out.ContainerResource
*out = new(autoscaling.ContainerResourceMetricSource)
if err := Convert_v2beta1_ContainerResourceMetricSource_To_autoscaling_ContainerResourceMetricSource(*in, *out, s); err != nil {
return err
}
} else {
out.ContainerResource = nil
}
if in.External != nil {
in, out := &in.External, &out.External
*out = new(autoscaling.ExternalMetricSource)
@ -527,6 +586,15 @@ func autoConvert_autoscaling_MetricSpec_To_v2beta1_MetricSpec(in *autoscaling.Me
} else {
out.Resource = nil
}
if in.ContainerResource != nil {
in, out := &in.ContainerResource, &out.ContainerResource
*out = new(v2beta1.ContainerResourceMetricSource)
if err := Convert_autoscaling_ContainerResourceMetricSource_To_v2beta1_ContainerResourceMetricSource(*in, *out, s); err != nil {
return err
}
} else {
out.ContainerResource = nil
}
if in.External != nil {
in, out := &in.External, &out.External
*out = new(v2beta1.ExternalMetricSource)
@ -573,6 +641,15 @@ func autoConvert_v2beta1_MetricStatus_To_autoscaling_MetricStatus(in *v2beta1.Me
} else {
out.Resource = nil
}
if in.ContainerResource != nil {
in, out := &in.ContainerResource, &out.ContainerResource
*out = new(autoscaling.ContainerResourceMetricStatus)
if err := Convert_v2beta1_ContainerResourceMetricStatus_To_autoscaling_ContainerResourceMetricStatus(*in, *out, s); err != nil {
return err
}
} else {
out.ContainerResource = nil
}
if in.External != nil {
in, out := &in.External, &out.External
*out = new(autoscaling.ExternalMetricStatus)
@ -619,6 +696,15 @@ func autoConvert_autoscaling_MetricStatus_To_v2beta1_MetricStatus(in *autoscalin
} else {
out.Resource = nil
}
if in.ContainerResource != nil {
in, out := &in.ContainerResource, &out.ContainerResource
*out = new(v2beta1.ContainerResourceMetricStatus)
if err := Convert_autoscaling_ContainerResourceMetricStatus_To_v2beta1_ContainerResourceMetricStatus(*in, *out, s); err != nil {
return err
}
} else {
out.ContainerResource = nil
}
if in.External != nil {
in, out := &in.External, &out.External
*out = new(v2beta1.ExternalMetricStatus)

View File

@ -40,6 +40,26 @@ func init() {
// RegisterConversions adds conversion functions to the given scheme.
// Public to allow building arbitrary schemes.
func RegisterConversions(s *runtime.Scheme) error {
if err := s.AddGeneratedConversionFunc((*v2beta2.ContainerResourceMetricSource)(nil), (*autoscaling.ContainerResourceMetricSource)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_v2beta2_ContainerResourceMetricSource_To_autoscaling_ContainerResourceMetricSource(a.(*v2beta2.ContainerResourceMetricSource), b.(*autoscaling.ContainerResourceMetricSource), scope)
}); err != nil {
return err
}
if err := s.AddGeneratedConversionFunc((*autoscaling.ContainerResourceMetricSource)(nil), (*v2beta2.ContainerResourceMetricSource)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_autoscaling_ContainerResourceMetricSource_To_v2beta2_ContainerResourceMetricSource(a.(*autoscaling.ContainerResourceMetricSource), b.(*v2beta2.ContainerResourceMetricSource), scope)
}); err != nil {
return err
}
if err := s.AddGeneratedConversionFunc((*v2beta2.ContainerResourceMetricStatus)(nil), (*autoscaling.ContainerResourceMetricStatus)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_v2beta2_ContainerResourceMetricStatus_To_autoscaling_ContainerResourceMetricStatus(a.(*v2beta2.ContainerResourceMetricStatus), b.(*autoscaling.ContainerResourceMetricStatus), scope)
}); err != nil {
return err
}
if err := s.AddGeneratedConversionFunc((*autoscaling.ContainerResourceMetricStatus)(nil), (*v2beta2.ContainerResourceMetricStatus)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_autoscaling_ContainerResourceMetricStatus_To_v2beta2_ContainerResourceMetricStatus(a.(*autoscaling.ContainerResourceMetricStatus), b.(*v2beta2.ContainerResourceMetricStatus), scope)
}); err != nil {
return err
}
if err := s.AddGeneratedConversionFunc((*v2beta2.CrossVersionObjectReference)(nil), (*autoscaling.CrossVersionObjectReference)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_v2beta2_CrossVersionObjectReference_To_autoscaling_CrossVersionObjectReference(a.(*v2beta2.CrossVersionObjectReference), b.(*autoscaling.CrossVersionObjectReference), scope)
}); err != nil {
@ -263,6 +283,62 @@ func RegisterConversions(s *runtime.Scheme) error {
return nil
}
func autoConvert_v2beta2_ContainerResourceMetricSource_To_autoscaling_ContainerResourceMetricSource(in *v2beta2.ContainerResourceMetricSource, out *autoscaling.ContainerResourceMetricSource, s conversion.Scope) error {
out.Name = core.ResourceName(in.Name)
if err := Convert_v2beta2_MetricTarget_To_autoscaling_MetricTarget(&in.Target, &out.Target, s); err != nil {
return err
}
out.Container = in.Container
return nil
}
// Convert_v2beta2_ContainerResourceMetricSource_To_autoscaling_ContainerResourceMetricSource is an autogenerated conversion function.
func Convert_v2beta2_ContainerResourceMetricSource_To_autoscaling_ContainerResourceMetricSource(in *v2beta2.ContainerResourceMetricSource, out *autoscaling.ContainerResourceMetricSource, s conversion.Scope) error {
return autoConvert_v2beta2_ContainerResourceMetricSource_To_autoscaling_ContainerResourceMetricSource(in, out, s)
}
func autoConvert_autoscaling_ContainerResourceMetricSource_To_v2beta2_ContainerResourceMetricSource(in *autoscaling.ContainerResourceMetricSource, out *v2beta2.ContainerResourceMetricSource, s conversion.Scope) error {
out.Name = v1.ResourceName(in.Name)
out.Container = in.Container
if err := Convert_autoscaling_MetricTarget_To_v2beta2_MetricTarget(&in.Target, &out.Target, s); err != nil {
return err
}
return nil
}
// Convert_autoscaling_ContainerResourceMetricSource_To_v2beta2_ContainerResourceMetricSource is an autogenerated conversion function.
func Convert_autoscaling_ContainerResourceMetricSource_To_v2beta2_ContainerResourceMetricSource(in *autoscaling.ContainerResourceMetricSource, out *v2beta2.ContainerResourceMetricSource, s conversion.Scope) error {
return autoConvert_autoscaling_ContainerResourceMetricSource_To_v2beta2_ContainerResourceMetricSource(in, out, s)
}
func autoConvert_v2beta2_ContainerResourceMetricStatus_To_autoscaling_ContainerResourceMetricStatus(in *v2beta2.ContainerResourceMetricStatus, out *autoscaling.ContainerResourceMetricStatus, s conversion.Scope) error {
out.Name = core.ResourceName(in.Name)
if err := Convert_v2beta2_MetricValueStatus_To_autoscaling_MetricValueStatus(&in.Current, &out.Current, s); err != nil {
return err
}
out.Container = in.Container
return nil
}
// Convert_v2beta2_ContainerResourceMetricStatus_To_autoscaling_ContainerResourceMetricStatus is an autogenerated conversion function.
func Convert_v2beta2_ContainerResourceMetricStatus_To_autoscaling_ContainerResourceMetricStatus(in *v2beta2.ContainerResourceMetricStatus, out *autoscaling.ContainerResourceMetricStatus, s conversion.Scope) error {
return autoConvert_v2beta2_ContainerResourceMetricStatus_To_autoscaling_ContainerResourceMetricStatus(in, out, s)
}
func autoConvert_autoscaling_ContainerResourceMetricStatus_To_v2beta2_ContainerResourceMetricStatus(in *autoscaling.ContainerResourceMetricStatus, out *v2beta2.ContainerResourceMetricStatus, s conversion.Scope) error {
out.Name = v1.ResourceName(in.Name)
out.Container = in.Container
if err := Convert_autoscaling_MetricValueStatus_To_v2beta2_MetricValueStatus(&in.Current, &out.Current, s); err != nil {
return err
}
return nil
}
// Convert_autoscaling_ContainerResourceMetricStatus_To_v2beta2_ContainerResourceMetricStatus is an autogenerated conversion function.
func Convert_autoscaling_ContainerResourceMetricStatus_To_v2beta2_ContainerResourceMetricStatus(in *autoscaling.ContainerResourceMetricStatus, out *v2beta2.ContainerResourceMetricStatus, s conversion.Scope) error {
return autoConvert_autoscaling_ContainerResourceMetricStatus_To_v2beta2_ContainerResourceMetricStatus(in, out, s)
}
func autoConvert_v2beta2_CrossVersionObjectReference_To_autoscaling_CrossVersionObjectReference(in *v2beta2.CrossVersionObjectReference, out *autoscaling.CrossVersionObjectReference, s conversion.Scope) error {
out.Kind = in.Kind
out.Name = in.Name
@ -515,7 +591,17 @@ func autoConvert_v2beta2_HorizontalPodAutoscalerSpec_To_autoscaling_HorizontalPo
}
out.MinReplicas = (*int32)(unsafe.Pointer(in.MinReplicas))
out.MaxReplicas = in.MaxReplicas
out.Metrics = *(*[]autoscaling.MetricSpec)(unsafe.Pointer(&in.Metrics))
if in.Metrics != nil {
in, out := &in.Metrics, &out.Metrics
*out = make([]autoscaling.MetricSpec, len(*in))
for i := range *in {
if err := Convert_v2beta2_MetricSpec_To_autoscaling_MetricSpec(&(*in)[i], &(*out)[i], s); err != nil {
return err
}
}
} else {
out.Metrics = nil
}
out.Behavior = (*autoscaling.HorizontalPodAutoscalerBehavior)(unsafe.Pointer(in.Behavior))
return nil
}
@ -531,7 +617,17 @@ func autoConvert_autoscaling_HorizontalPodAutoscalerSpec_To_v2beta2_HorizontalPo
}
out.MinReplicas = (*int32)(unsafe.Pointer(in.MinReplicas))
out.MaxReplicas = in.MaxReplicas
out.Metrics = *(*[]v2beta2.MetricSpec)(unsafe.Pointer(&in.Metrics))
if in.Metrics != nil {
in, out := &in.Metrics, &out.Metrics
*out = make([]v2beta2.MetricSpec, len(*in))
for i := range *in {
if err := Convert_autoscaling_MetricSpec_To_v2beta2_MetricSpec(&(*in)[i], &(*out)[i], s); err != nil {
return err
}
}
} else {
out.Metrics = nil
}
out.Behavior = (*v2beta2.HorizontalPodAutoscalerBehavior)(unsafe.Pointer(in.Behavior))
return nil
}
@ -546,7 +642,17 @@ func autoConvert_v2beta2_HorizontalPodAutoscalerStatus_To_autoscaling_Horizontal
out.LastScaleTime = (*metav1.Time)(unsafe.Pointer(in.LastScaleTime))
out.CurrentReplicas = in.CurrentReplicas
out.DesiredReplicas = in.DesiredReplicas
out.CurrentMetrics = *(*[]autoscaling.MetricStatus)(unsafe.Pointer(&in.CurrentMetrics))
if in.CurrentMetrics != nil {
in, out := &in.CurrentMetrics, &out.CurrentMetrics
*out = make([]autoscaling.MetricStatus, len(*in))
for i := range *in {
if err := Convert_v2beta2_MetricStatus_To_autoscaling_MetricStatus(&(*in)[i], &(*out)[i], s); err != nil {
return err
}
}
} else {
out.CurrentMetrics = nil
}
out.Conditions = *(*[]autoscaling.HorizontalPodAutoscalerCondition)(unsafe.Pointer(&in.Conditions))
return nil
}
@ -561,7 +667,17 @@ func autoConvert_autoscaling_HorizontalPodAutoscalerStatus_To_v2beta2_Horizontal
out.LastScaleTime = (*metav1.Time)(unsafe.Pointer(in.LastScaleTime))
out.CurrentReplicas = in.CurrentReplicas
out.DesiredReplicas = in.DesiredReplicas
out.CurrentMetrics = *(*[]v2beta2.MetricStatus)(unsafe.Pointer(&in.CurrentMetrics))
if in.CurrentMetrics != nil {
in, out := &in.CurrentMetrics, &out.CurrentMetrics
*out = make([]v2beta2.MetricStatus, len(*in))
for i := range *in {
if err := Convert_autoscaling_MetricStatus_To_v2beta2_MetricStatus(&(*in)[i], &(*out)[i], s); err != nil {
return err
}
}
} else {
out.CurrentMetrics = nil
}
out.Conditions = *(*[]v2beta2.HorizontalPodAutoscalerCondition)(unsafe.Pointer(&in.Conditions))
return nil
}
@ -598,6 +714,15 @@ func autoConvert_v2beta2_MetricSpec_To_autoscaling_MetricSpec(in *v2beta2.Metric
out.Object = (*autoscaling.ObjectMetricSource)(unsafe.Pointer(in.Object))
out.Pods = (*autoscaling.PodsMetricSource)(unsafe.Pointer(in.Pods))
out.Resource = (*autoscaling.ResourceMetricSource)(unsafe.Pointer(in.Resource))
if in.ContainerResource != nil {
in, out := &in.ContainerResource, &out.ContainerResource
*out = new(autoscaling.ContainerResourceMetricSource)
if err := Convert_v2beta2_ContainerResourceMetricSource_To_autoscaling_ContainerResourceMetricSource(*in, *out, s); err != nil {
return err
}
} else {
out.ContainerResource = nil
}
out.External = (*autoscaling.ExternalMetricSource)(unsafe.Pointer(in.External))
return nil
}
@ -612,6 +737,15 @@ func autoConvert_autoscaling_MetricSpec_To_v2beta2_MetricSpec(in *autoscaling.Me
out.Object = (*v2beta2.ObjectMetricSource)(unsafe.Pointer(in.Object))
out.Pods = (*v2beta2.PodsMetricSource)(unsafe.Pointer(in.Pods))
out.Resource = (*v2beta2.ResourceMetricSource)(unsafe.Pointer(in.Resource))
if in.ContainerResource != nil {
in, out := &in.ContainerResource, &out.ContainerResource
*out = new(v2beta2.ContainerResourceMetricSource)
if err := Convert_autoscaling_ContainerResourceMetricSource_To_v2beta2_ContainerResourceMetricSource(*in, *out, s); err != nil {
return err
}
} else {
out.ContainerResource = nil
}
out.External = (*v2beta2.ExternalMetricSource)(unsafe.Pointer(in.External))
return nil
}
@ -626,6 +760,15 @@ func autoConvert_v2beta2_MetricStatus_To_autoscaling_MetricStatus(in *v2beta2.Me
out.Object = (*autoscaling.ObjectMetricStatus)(unsafe.Pointer(in.Object))
out.Pods = (*autoscaling.PodsMetricStatus)(unsafe.Pointer(in.Pods))
out.Resource = (*autoscaling.ResourceMetricStatus)(unsafe.Pointer(in.Resource))
if in.ContainerResource != nil {
in, out := &in.ContainerResource, &out.ContainerResource
*out = new(autoscaling.ContainerResourceMetricStatus)
if err := Convert_v2beta2_ContainerResourceMetricStatus_To_autoscaling_ContainerResourceMetricStatus(*in, *out, s); err != nil {
return err
}
} else {
out.ContainerResource = nil
}
out.External = (*autoscaling.ExternalMetricStatus)(unsafe.Pointer(in.External))
return nil
}
@ -640,6 +783,15 @@ func autoConvert_autoscaling_MetricStatus_To_v2beta2_MetricStatus(in *autoscalin
out.Object = (*v2beta2.ObjectMetricStatus)(unsafe.Pointer(in.Object))
out.Pods = (*v2beta2.PodsMetricStatus)(unsafe.Pointer(in.Pods))
out.Resource = (*v2beta2.ResourceMetricStatus)(unsafe.Pointer(in.Resource))
if in.ContainerResource != nil {
in, out := &in.ContainerResource, &out.ContainerResource
*out = new(v2beta2.ContainerResourceMetricStatus)
if err := Convert_autoscaling_ContainerResourceMetricStatus_To_v2beta2_ContainerResourceMetricStatus(*in, *out, s); err != nil {
return err
}
} else {
out.ContainerResource = nil
}
out.External = (*v2beta2.ExternalMetricStatus)(unsafe.Pointer(in.External))
return nil
}

View File

@ -12,6 +12,7 @@ go_library(
importpath = "k8s.io/kubernetes/pkg/apis/autoscaling/validation",
deps = [
"//pkg/apis/autoscaling:go_default_library",
"//pkg/apis/core/v1/validation:go_default_library",
"//pkg/apis/core/validation:go_default_library",
"//pkg/features:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/api/validation:go_default_library",

View File

@ -18,14 +18,13 @@ package validation
import (
"fmt"
"strings"
apimachineryvalidation "k8s.io/apimachinery/pkg/api/validation"
pathvalidation "k8s.io/apimachinery/pkg/api/validation/path"
"k8s.io/apimachinery/pkg/util/sets"
"k8s.io/apimachinery/pkg/util/validation/field"
utilfeature "k8s.io/apiserver/pkg/util/feature"
"k8s.io/kubernetes/pkg/apis/autoscaling"
corevalidation "k8s.io/kubernetes/pkg/apis/core/v1/validation"
apivalidation "k8s.io/kubernetes/pkg/apis/core/validation"
"k8s.io/kubernetes/pkg/features"
)
@ -239,7 +238,10 @@ func validateScalingPolicy(policy autoscaling.HPAScalingPolicy, fldPath *field.P
return allErrs
}
var validMetricSourceTypes = sets.NewString(string(autoscaling.ObjectMetricSourceType), string(autoscaling.PodsMetricSourceType), string(autoscaling.ResourceMetricSourceType), string(autoscaling.ExternalMetricSourceType))
var validMetricSourceTypes = sets.NewString(
string(autoscaling.ObjectMetricSourceType), string(autoscaling.PodsMetricSourceType),
string(autoscaling.ResourceMetricSourceType), string(autoscaling.ExternalMetricSourceType),
string(autoscaling.ContainerResourceMetricSourceType))
var validMetricSourceTypesList = validMetricSourceTypes.List()
func validateMetricSpec(spec autoscaling.MetricSpec, fldPath *field.Path) field.ErrorList {
@ -282,10 +284,47 @@ func validateMetricSpec(spec autoscaling.MetricSpec, fldPath *field.Path) field.
}
}
expectedField := strings.ToLower(string(spec.Type))
if spec.ContainerResource != nil {
typesPresent.Insert("containerResource")
if typesPresent.Len() == 1 {
allErrs = append(allErrs, validateContainerResourceSource(spec.ContainerResource, fldPath.Child("containerResource"))...)
}
}
if !typesPresent.Has(expectedField) {
allErrs = append(allErrs, field.Required(fldPath.Child(expectedField), "must populate information for the given metric source"))
var expectedField string
switch spec.Type {
case autoscaling.ObjectMetricSourceType:
if spec.Object == nil {
allErrs = append(allErrs, field.Required(fldPath.Child("object"), "must populate information for the given metric source"))
}
expectedField = "object"
case autoscaling.PodsMetricSourceType:
if spec.Pods == nil {
allErrs = append(allErrs, field.Required(fldPath.Child("pods"), "must populate information for the given metric source"))
}
expectedField = "pods"
case autoscaling.ResourceMetricSourceType:
if spec.Resource == nil {
allErrs = append(allErrs, field.Required(fldPath.Child("resource"), "must populate information for the given metric source"))
}
expectedField = "resource"
case autoscaling.ExternalMetricSourceType:
if spec.External == nil {
allErrs = append(allErrs, field.Required(fldPath.Child("external"), "must populate information for the given metric source"))
}
expectedField = "external"
case autoscaling.ContainerResourceMetricSourceType:
if spec.ContainerResource == nil {
if utilfeature.DefaultFeatureGate.Enabled(features.HPAContainerMetrics) {
allErrs = append(allErrs, field.Required(fldPath.Child("containerResource"), "must populate information for the given metric source"))
} else {
allErrs = append(allErrs, field.Required(fldPath.Child("containerResource"), "must populate information for the given metric source (only allowed when HPAContainerMetrics feature is enabled)"))
}
}
expectedField = "containerResource"
default:
allErrs = append(allErrs, field.NotSupported(fldPath.Child("type"), spec.Type, validMetricSourceTypesList))
}
if typesPresent.Len() != 1 {
@ -342,6 +381,34 @@ func validatePodsSource(src *autoscaling.PodsMetricSource, fldPath *field.Path)
return allErrs
}
func validateContainerResourceSource(src *autoscaling.ContainerResourceMetricSource, fldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{}
if len(src.Name) == 0 {
allErrs = append(allErrs, field.Required(fldPath.Child("name"), "must specify a resource name"))
} else {
allErrs = append(allErrs, corevalidation.ValidateContainerResourceName(string(src.Name), fldPath.Child("name"))...)
}
if len(src.Container) == 0 {
allErrs = append(allErrs, field.Required(fldPath.Child("container"), "must specify a container"))
} else {
allErrs = append(allErrs, apivalidation.ValidateDNS1123Label(src.Container, fldPath.Child("container"))...)
}
allErrs = append(allErrs, validateMetricTarget(src.Target, fldPath.Child("target"))...)
if src.Target.AverageUtilization == nil && src.Target.AverageValue == nil {
allErrs = append(allErrs, field.Required(fldPath.Child("target").Child("averageUtilization"), "must set either a target raw value or a target utilization"))
}
if src.Target.AverageUtilization != nil && src.Target.AverageValue != nil {
allErrs = append(allErrs, field.Forbidden(fldPath.Child("target").Child("averageValue"), "may not set both a target raw value and a target utilization"))
}
return allErrs
}
func validateResourceSource(src *autoscaling.ResourceMetricSource, fldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{}

View File

@ -565,6 +565,60 @@ func TestValidateHorizontalPodAutoscaler(t *testing.T) {
},
},
},
{
ObjectMeta: metav1.ObjectMeta{
Name: "myautoscaler",
Namespace: metav1.NamespaceDefault,
},
Spec: autoscaling.HorizontalPodAutoscalerSpec{
ScaleTargetRef: autoscaling.CrossVersionObjectReference{
Kind: "ReplicationController",
Name: "myrc",
},
MinReplicas: utilpointer.Int32Ptr(1),
MaxReplicas: 5,
Metrics: []autoscaling.MetricSpec{
{
Type: autoscaling.ContainerResourceMetricSourceType,
ContainerResource: &autoscaling.ContainerResourceMetricSource{
Name: api.ResourceCPU,
Container: "test-container",
Target: autoscaling.MetricTarget{
Type: autoscaling.UtilizationMetricType,
AverageUtilization: utilpointer.Int32Ptr(70),
},
},
},
},
},
},
{
ObjectMeta: metav1.ObjectMeta{
Name: "myautoscaler",
Namespace: metav1.NamespaceDefault,
},
Spec: autoscaling.HorizontalPodAutoscalerSpec{
ScaleTargetRef: autoscaling.CrossVersionObjectReference{
Kind: "ReplicationController",
Name: "myrc",
},
MinReplicas: utilpointer.Int32Ptr(1),
MaxReplicas: 5,
Metrics: []autoscaling.MetricSpec{
{
Type: autoscaling.ContainerResourceMetricSourceType,
ContainerResource: &autoscaling.ContainerResourceMetricSource{
Name: api.ResourceCPU,
Container: "test-container",
Target: autoscaling.MetricTarget{
Type: autoscaling.AverageValueMetricType,
AverageValue: resource.NewMilliQuantity(300, resource.DecimalSI),
},
},
},
},
},
},
{
ObjectMeta: metav1.ObjectMeta{
Name: "myautoscaler",
@ -689,6 +743,30 @@ func TestValidateHorizontalPodAutoscaler(t *testing.T) {
},
msg: "scaleTargetRef.kind: Required",
},
{
horizontalPodAutoscaler: autoscaling.HorizontalPodAutoscaler{
ObjectMeta: metav1.ObjectMeta{Name: "myautoscaler", Namespace: metav1.NamespaceDefault},
Spec: autoscaling.HorizontalPodAutoscalerSpec{
ScaleTargetRef: autoscaling.CrossVersionObjectReference{Name: "myrc"},
MinReplicas: utilpointer.Int32Ptr(1),
MaxReplicas: 5,
Metrics: []autoscaling.MetricSpec{
{
Type: autoscaling.ContainerResourceMetricSourceType,
ContainerResource: &autoscaling.ContainerResourceMetricSource{
Name: api.ResourceCPU,
Container: "test-application",
Target: autoscaling.MetricTarget{
Type: autoscaling.UtilizationMetricType,
AverageUtilization: utilpointer.Int32Ptr(70),
},
},
},
},
},
},
msg: "scaleTargetRef.kind: Required",
},
{
horizontalPodAutoscaler: autoscaling.HorizontalPodAutoscaler{
ObjectMeta: metav1.ObjectMeta{Name: "myautoscaler", Namespace: metav1.NamespaceDefault},
@ -712,6 +790,30 @@ func TestValidateHorizontalPodAutoscaler(t *testing.T) {
},
msg: "scaleTargetRef.kind: Invalid",
},
{
horizontalPodAutoscaler: autoscaling.HorizontalPodAutoscaler{
ObjectMeta: metav1.ObjectMeta{Name: "myautoscaler", Namespace: metav1.NamespaceDefault},
Spec: autoscaling.HorizontalPodAutoscalerSpec{
ScaleTargetRef: autoscaling.CrossVersionObjectReference{Kind: "..", Name: "myrc"},
MinReplicas: utilpointer.Int32Ptr(1),
MaxReplicas: 5,
Metrics: []autoscaling.MetricSpec{
{
Type: autoscaling.ContainerResourceMetricSourceType,
ContainerResource: &autoscaling.ContainerResourceMetricSource{
Name: api.ResourceCPU,
Container: "test-application",
Target: autoscaling.MetricTarget{
Type: autoscaling.UtilizationMetricType,
AverageUtilization: utilpointer.Int32Ptr(70),
},
},
},
},
},
},
msg: "scaleTargetRef.kind: Invalid",
},
{
horizontalPodAutoscaler: autoscaling.HorizontalPodAutoscaler{
ObjectMeta: metav1.ObjectMeta{Name: "myautoscaler", Namespace: metav1.NamespaceDefault},
@ -735,6 +837,30 @@ func TestValidateHorizontalPodAutoscaler(t *testing.T) {
},
msg: "scaleTargetRef.name: Required",
},
{
horizontalPodAutoscaler: autoscaling.HorizontalPodAutoscaler{
ObjectMeta: metav1.ObjectMeta{Name: "myautoscaler", Namespace: metav1.NamespaceDefault},
Spec: autoscaling.HorizontalPodAutoscalerSpec{
ScaleTargetRef: autoscaling.CrossVersionObjectReference{Kind: "ReplicationController"},
MinReplicas: utilpointer.Int32Ptr(1),
MaxReplicas: 5,
Metrics: []autoscaling.MetricSpec{
{
Type: autoscaling.ContainerResourceMetricSourceType,
ContainerResource: &autoscaling.ContainerResourceMetricSource{
Name: api.ResourceCPU,
Container: "test-application",
Target: autoscaling.MetricTarget{
Type: autoscaling.UtilizationMetricType,
AverageUtilization: utilpointer.Int32Ptr(70),
},
},
},
},
},
},
msg: "scaleTargetRef.name: Required",
},
{
horizontalPodAutoscaler: autoscaling.HorizontalPodAutoscaler{
ObjectMeta: metav1.ObjectMeta{Name: "myautoscaler", Namespace: metav1.NamespaceDefault},
@ -758,6 +884,30 @@ func TestValidateHorizontalPodAutoscaler(t *testing.T) {
},
msg: "scaleTargetRef.name: Invalid",
},
{
horizontalPodAutoscaler: autoscaling.HorizontalPodAutoscaler{
ObjectMeta: metav1.ObjectMeta{Name: "myautoscaler", Namespace: metav1.NamespaceDefault},
Spec: autoscaling.HorizontalPodAutoscalerSpec{
ScaleTargetRef: autoscaling.CrossVersionObjectReference{Kind: "ReplicationController", Name: ".."},
MinReplicas: utilpointer.Int32Ptr(1),
MaxReplicas: 5,
Metrics: []autoscaling.MetricSpec{
{
Type: autoscaling.ContainerResourceMetricSourceType,
ContainerResource: &autoscaling.ContainerResourceMetricSource{
Name: api.ResourceCPU,
Container: "test-application",
Target: autoscaling.MetricTarget{
Type: autoscaling.UtilizationMetricType,
AverageUtilization: utilpointer.Int32Ptr(70),
},
},
},
},
},
},
msg: "scaleTargetRef.name: Invalid",
},
{
horizontalPodAutoscaler: autoscaling.HorizontalPodAutoscaler{
ObjectMeta: metav1.ObjectMeta{
@ -813,6 +963,34 @@ func TestValidateHorizontalPodAutoscaler(t *testing.T) {
},
msg: "may not set both a target raw value and a target utilization",
},
{
horizontalPodAutoscaler: autoscaling.HorizontalPodAutoscaler{
ObjectMeta: metav1.ObjectMeta{
Name: "myautoscaler",
Namespace: metav1.NamespaceDefault,
},
Spec: autoscaling.HorizontalPodAutoscalerSpec{
ScaleTargetRef: autoscaling.CrossVersionObjectReference{Name: "myrc", Kind: "ReplicationController"},
MinReplicas: utilpointer.Int32Ptr(1),
MaxReplicas: 5,
Metrics: []autoscaling.MetricSpec{
{
Type: autoscaling.ContainerResourceMetricSourceType,
ContainerResource: &autoscaling.ContainerResourceMetricSource{
Name: api.ResourceCPU,
Container: "test-application",
Target: autoscaling.MetricTarget{
Type: autoscaling.UtilizationMetricType,
AverageUtilization: utilpointer.Int32Ptr(70),
AverageValue: resource.NewMilliQuantity(300, resource.DecimalSI),
},
},
},
},
},
},
msg: "may not set both a target raw value and a target utilization",
},
{
horizontalPodAutoscaler: autoscaling.HorizontalPodAutoscaler{
ObjectMeta: metav1.ObjectMeta{Name: "myautoscaler", Namespace: metav1.NamespaceDefault},
@ -835,6 +1013,53 @@ func TestValidateHorizontalPodAutoscaler(t *testing.T) {
},
msg: "must specify a resource name",
},
{
horizontalPodAutoscaler: autoscaling.HorizontalPodAutoscaler{
ObjectMeta: metav1.ObjectMeta{Name: "myautoscaler", Namespace: metav1.NamespaceDefault},
Spec: autoscaling.HorizontalPodAutoscalerSpec{
ScaleTargetRef: autoscaling.CrossVersionObjectReference{Name: "myrc", Kind: "ReplicationController"},
MinReplicas: utilpointer.Int32Ptr(1),
MaxReplicas: 5,
Metrics: []autoscaling.MetricSpec{
{
Type: autoscaling.ContainerResourceMetricSourceType,
ContainerResource: &autoscaling.ContainerResourceMetricSource{
Container: "test-application",
Target: autoscaling.MetricTarget{
Type: autoscaling.UtilizationMetricType,
AverageUtilization: utilpointer.Int32Ptr(70),
},
},
},
},
},
},
msg: "must specify a resource name",
},
{
horizontalPodAutoscaler: autoscaling.HorizontalPodAutoscaler{
ObjectMeta: metav1.ObjectMeta{Name: "myautoscaler", Namespace: metav1.NamespaceDefault},
Spec: autoscaling.HorizontalPodAutoscalerSpec{
ScaleTargetRef: autoscaling.CrossVersionObjectReference{Name: "myrc", Kind: "ReplicationController"},
MinReplicas: utilpointer.Int32Ptr(1),
MaxReplicas: 5,
Metrics: []autoscaling.MetricSpec{
{
Type: autoscaling.ContainerResourceMetricSourceType,
ContainerResource: &autoscaling.ContainerResourceMetricSource{
Name: "InvalidResource",
Container: "test-application",
Target: autoscaling.MetricTarget{
Type: autoscaling.UtilizationMetricType,
AverageUtilization: utilpointer.Int32Ptr(70),
},
},
},
},
},
},
msg: "Invalid value: \"InvalidResource\": must be a standard resource type or fully qualified",
},
{
horizontalPodAutoscaler: autoscaling.HorizontalPodAutoscaler{
ObjectMeta: metav1.ObjectMeta{Name: "myautoscaler", Namespace: metav1.NamespaceDefault},
@ -858,6 +1083,77 @@ func TestValidateHorizontalPodAutoscaler(t *testing.T) {
},
msg: "must be greater than 0",
},
{
horizontalPodAutoscaler: autoscaling.HorizontalPodAutoscaler{
ObjectMeta: metav1.ObjectMeta{Name: "myautoscaler", Namespace: metav1.NamespaceDefault},
Spec: autoscaling.HorizontalPodAutoscalerSpec{
ScaleTargetRef: autoscaling.CrossVersionObjectReference{Name: "myrc", Kind: "ReplicationController"},
MinReplicas: utilpointer.Int32Ptr(1),
MaxReplicas: 5,
Metrics: []autoscaling.MetricSpec{
{
Type: autoscaling.ContainerResourceMetricSourceType,
ContainerResource: &autoscaling.ContainerResourceMetricSource{
Name: api.ResourceCPU,
Container: "test-application",
Target: autoscaling.MetricTarget{
Type: autoscaling.UtilizationMetricType,
AverageUtilization: utilpointer.Int32Ptr(-10),
},
},
},
},
},
},
msg: "must be greater than 0",
},
{
horizontalPodAutoscaler: autoscaling.HorizontalPodAutoscaler{
ObjectMeta: metav1.ObjectMeta{Name: "myautoscaler", Namespace: metav1.NamespaceDefault},
Spec: autoscaling.HorizontalPodAutoscalerSpec{
ScaleTargetRef: autoscaling.CrossVersionObjectReference{Name: "myrc", Kind: "ReplicationController"},
MinReplicas: utilpointer.Int32Ptr(1),
MaxReplicas: 5,
Metrics: []autoscaling.MetricSpec{
{
Type: autoscaling.ContainerResourceMetricSourceType,
ContainerResource: &autoscaling.ContainerResourceMetricSource{
Name: api.ResourceCPU,
Target: autoscaling.MetricTarget{
Type: autoscaling.UtilizationMetricType,
AverageUtilization: utilpointer.Int32Ptr(-10),
},
},
},
},
},
},
msg: "must specify a container",
},
{
horizontalPodAutoscaler: autoscaling.HorizontalPodAutoscaler{
ObjectMeta: metav1.ObjectMeta{Name: "myautoscaler", Namespace: metav1.NamespaceDefault},
Spec: autoscaling.HorizontalPodAutoscalerSpec{
ScaleTargetRef: autoscaling.CrossVersionObjectReference{Name: "myrc", Kind: "ReplicationController"},
MinReplicas: utilpointer.Int32Ptr(1),
MaxReplicas: 5,
Metrics: []autoscaling.MetricSpec{
{
Type: autoscaling.ContainerResourceMetricSourceType,
ContainerResource: &autoscaling.ContainerResourceMetricSource{
Name: api.ResourceCPU,
Container: "---***",
Target: autoscaling.MetricTarget{
Type: autoscaling.UtilizationMetricType,
AverageUtilization: utilpointer.Int32Ptr(-10),
},
},
},
},
},
},
msg: "Invalid value: \"---***\"",
},
{
horizontalPodAutoscaler: autoscaling.HorizontalPodAutoscaler{
ObjectMeta: metav1.ObjectMeta{Name: "myautoscaler", Namespace: metav1.NamespaceDefault},
@ -880,6 +1176,29 @@ func TestValidateHorizontalPodAutoscaler(t *testing.T) {
},
msg: "must set either a target raw value or a target utilization",
},
{
horizontalPodAutoscaler: autoscaling.HorizontalPodAutoscaler{
ObjectMeta: metav1.ObjectMeta{Name: "myautoscaler", Namespace: metav1.NamespaceDefault},
Spec: autoscaling.HorizontalPodAutoscalerSpec{
ScaleTargetRef: autoscaling.CrossVersionObjectReference{Name: "myrc", Kind: "ReplicationController"},
MinReplicas: utilpointer.Int32Ptr(1),
MaxReplicas: 5,
Metrics: []autoscaling.MetricSpec{
{
Type: autoscaling.ContainerResourceMetricSourceType,
ContainerResource: &autoscaling.ContainerResourceMetricSource{
Name: api.ResourceCPU,
Container: "test-application",
Target: autoscaling.MetricTarget{
Type: autoscaling.ValueMetricType,
},
},
},
},
},
},
msg: "must set either a target raw value or a target utilization",
},
{
horizontalPodAutoscaler: autoscaling.HorizontalPodAutoscaler{
ObjectMeta: metav1.ObjectMeta{Name: "myautoscaler", Namespace: metav1.NamespaceDefault},
@ -1321,6 +1640,16 @@ func TestValidateHorizontalPodAutoscaler(t *testing.T) {
},
},
},
autoscaling.ContainerResourceMetricSourceType: {
ContainerResource: &autoscaling.ContainerResourceMetricSource{
Name: api.ResourceCPU,
Container: "test-application",
Target: autoscaling.MetricTarget{
Type: autoscaling.AverageValueMetricType,
AverageValue: resource.NewMilliQuantity(100, resource.DecimalSI),
},
},
},
autoscaling.PodsMetricSourceType: {
Pods: &autoscaling.PodsMetricSource{
Metric: autoscaling.MetricIdentifier{

View File

@ -25,6 +25,40 @@ import (
runtime "k8s.io/apimachinery/pkg/runtime"
)
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ContainerResourceMetricSource) DeepCopyInto(out *ContainerResourceMetricSource) {
*out = *in
in.Target.DeepCopyInto(&out.Target)
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ContainerResourceMetricSource.
func (in *ContainerResourceMetricSource) DeepCopy() *ContainerResourceMetricSource {
if in == nil {
return nil
}
out := new(ContainerResourceMetricSource)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ContainerResourceMetricStatus) DeepCopyInto(out *ContainerResourceMetricStatus) {
*out = *in
in.Current.DeepCopyInto(&out.Current)
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ContainerResourceMetricStatus.
func (in *ContainerResourceMetricStatus) DeepCopy() *ContainerResourceMetricStatus {
if in == nil {
return nil
}
out := new(ContainerResourceMetricStatus)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *CrossVersionObjectReference) DeepCopyInto(out *CrossVersionObjectReference) {
*out = *in
@ -340,6 +374,11 @@ func (in *MetricSpec) DeepCopyInto(out *MetricSpec) {
*out = new(ResourceMetricSource)
(*in).DeepCopyInto(*out)
}
if in.ContainerResource != nil {
in, out := &in.ContainerResource, &out.ContainerResource
*out = new(ContainerResourceMetricSource)
(*in).DeepCopyInto(*out)
}
if in.External != nil {
in, out := &in.External, &out.External
*out = new(ExternalMetricSource)
@ -376,6 +415,11 @@ func (in *MetricStatus) DeepCopyInto(out *MetricStatus) {
*out = new(ResourceMetricStatus)
(*in).DeepCopyInto(*out)
}
if in.ContainerResource != nil {
in, out := &in.ContainerResource, &out.ContainerResource
*out = new(ContainerResourceMetricStatus)
(*in).DeepCopyInto(*out)
}
if in.External != nil {
in, out := &in.External, &out.External
*out = new(ExternalMetricStatus)

View File

@ -42,7 +42,7 @@ func ValidateResourceRequirements(requirements *v1.ResourceRequirements, fldPath
for resourceName, quantity := range requirements.Limits {
fldPath := limPath.Key(string(resourceName))
// Validate resource name.
allErrs = append(allErrs, validateContainerResourceName(string(resourceName), fldPath)...)
allErrs = append(allErrs, ValidateContainerResourceName(string(resourceName), fldPath)...)
// Validate resource quantity.
allErrs = append(allErrs, ValidateResourceQuantityValue(string(resourceName), quantity, fldPath)...)
@ -51,7 +51,7 @@ func ValidateResourceRequirements(requirements *v1.ResourceRequirements, fldPath
for resourceName, quantity := range requirements.Requests {
fldPath := reqPath.Key(string(resourceName))
// Validate resource name.
allErrs = append(allErrs, validateContainerResourceName(string(resourceName), fldPath)...)
allErrs = append(allErrs, ValidateContainerResourceName(string(resourceName), fldPath)...)
// Validate resource quantity.
allErrs = append(allErrs, ValidateResourceQuantityValue(string(resourceName), quantity, fldPath)...)
@ -70,7 +70,8 @@ func ValidateResourceRequirements(requirements *v1.ResourceRequirements, fldPath
return allErrs
}
func validateContainerResourceName(value string, fldPath *field.Path) field.ErrorList {
// ValidateContainerResourceName checks the name of resource specified for a container
func ValidateContainerResourceName(value string, fldPath *field.Path) field.ErrorList {
allErrs := validateResourceName(value, fldPath)
if len(strings.Split(value, "/")) == 1 {
if !helper.IsStandardContainerResourceName(value) {

View File

@ -129,6 +129,62 @@ func TestValidateResourceRequirements(t *testing.T) {
}
}
func TestValidateContainerResourceName(t *testing.T) {
successCase := []struct {
Name string
ResourceName string
}{
{
Name: "CPU resource",
ResourceName: "cpu",
},
{
Name: "Memory resource",
ResourceName: "memory",
},
{
Name: "Hugepages resource",
ResourceName: "hugepages-2Mi",
},
{
Name: "Namespaced resource",
ResourceName: "kubernetes.io/resource-foo",
},
{
Name: "Extended Resource",
ResourceName: "my.org/resource-bar",
},
}
for _, tc := range successCase {
if errs := ValidateContainerResourceName(tc.ResourceName, field.NewPath(tc.ResourceName)); len(errs) != 0 {
t.Errorf("%q unexpected error: %v", tc.Name, errs)
}
}
errorCase := []struct {
Name string
ResourceName string
}{
{
Name: "Invalid standard resource",
ResourceName: "cpu-core",
},
{
Name: "Invalid namespaced resource",
ResourceName: "kubernetes.io/",
},
{
Name: "Invalid extended resource",
ResourceName: "my.org-foo-resource",
},
}
for _, tc := range errorCase {
if errs := ValidateContainerResourceName(tc.ResourceName, field.NewPath(tc.ResourceName)); len(errs) == 0 {
t.Errorf("%q expected error", tc.Name)
}
}
}
func TestValidatePodLogOptions(t *testing.T) {
var (

View File

@ -329,6 +329,11 @@ func (a *HorizontalController) computeReplicasForMetric(hpa *autoscalingv2.Horiz
if err != nil {
return 0, "", time.Time{}, condition, err
}
case autoscalingv2.ContainerResourceMetricSourceType:
replicaCountProposal, timestampProposal, metricNameProposal, condition, err = a.computeStatusForContainerResourceMetric(specReplicas, spec, hpa, selector, status)
if err != nil {
return 0, "", time.Time{}, condition, err
}
case autoscalingv2.ExternalMetricSourceType:
replicaCountProposal, timestampProposal, metricNameProposal, condition, err = a.computeStatusForExternalMetric(specReplicas, statusReplicas, spec, hpa, selector, status)
if err != nil {
@ -435,51 +440,80 @@ func (a *HorizontalController) computeStatusForPodsMetric(currentReplicas int32,
return replicaCountProposal, timestampProposal, fmt.Sprintf("pods metric %s", metricSpec.Pods.Metric.Name), autoscalingv2.HorizontalPodAutoscalerCondition{}, nil
}
// computeStatusForResourceMetric computes the desired number of replicas for the specified metric of type ResourceMetricSourceType.
func (a *HorizontalController) computeStatusForResourceMetric(currentReplicas int32, metricSpec autoscalingv2.MetricSpec, hpa *autoscalingv2.HorizontalPodAutoscaler, selector labels.Selector, status *autoscalingv2.MetricStatus) (replicaCountProposal int32, timestampProposal time.Time, metricNameProposal string, condition autoscalingv2.HorizontalPodAutoscalerCondition, err error) {
if metricSpec.Resource.Target.AverageValue != nil {
func (a *HorizontalController) computeStatusForResourceMetricGeneric(currentReplicas int32, target autoscalingv2.MetricTarget,
resourceName v1.ResourceName, namespace string, container string, selector labels.Selector) (replicaCountProposal int32,
metricStatus *autoscalingv2.MetricValueStatus, timestampProposal time.Time, metricNameProposal string,
condition autoscalingv2.HorizontalPodAutoscalerCondition, err error) {
if target.AverageValue != nil {
var rawProposal int64
replicaCountProposal, rawProposal, timestampProposal, err := a.replicaCalc.GetRawResourceReplicas(currentReplicas, metricSpec.Resource.Target.AverageValue.MilliValue(), metricSpec.Resource.Name, hpa.Namespace, selector)
replicaCountProposal, rawProposal, timestampProposal, err := a.replicaCalc.GetRawResourceReplicas(currentReplicas, target.AverageValue.MilliValue(), resourceName, namespace, selector, container)
if err != nil {
condition = a.getUnableComputeReplicaCountCondition(hpa, "FailedGetResourceMetric", err)
return 0, time.Time{}, "", condition, fmt.Errorf("failed to get %s utilization: %v", metricSpec.Resource.Name, err)
return 0, nil, time.Time{}, "", condition, fmt.Errorf("failed to get %s utilization: %v", resourceName, err)
}
metricNameProposal = fmt.Sprintf("%s resource", metricSpec.Resource.Name)
*status = autoscalingv2.MetricStatus{
Type: autoscalingv2.ResourceMetricSourceType,
Resource: &autoscalingv2.ResourceMetricStatus{
Name: metricSpec.Resource.Name,
Current: autoscalingv2.MetricValueStatus{
AverageValue: resource.NewMilliQuantity(rawProposal, resource.DecimalSI),
},
},
metricNameProposal = fmt.Sprintf("%s resource", resourceName.String())
status := autoscalingv2.MetricValueStatus{
AverageValue: resource.NewMilliQuantity(rawProposal, resource.DecimalSI),
}
return replicaCountProposal, timestampProposal, metricNameProposal, autoscalingv2.HorizontalPodAutoscalerCondition{}, nil
return replicaCountProposal, &status, timestampProposal, metricNameProposal, autoscalingv2.HorizontalPodAutoscalerCondition{}, nil
}
if metricSpec.Resource.Target.AverageUtilization == nil {
if target.AverageUtilization == nil {
errMsg := "invalid resource metric source: neither a utilization target nor a value target was set"
err = fmt.Errorf(errMsg)
condition = a.getUnableComputeReplicaCountCondition(hpa, "FailedGetResourceMetric", err)
return 0, time.Time{}, "", condition, fmt.Errorf(errMsg)
return 0, nil, time.Time{}, "", condition, fmt.Errorf(errMsg)
}
targetUtilization := *metricSpec.Resource.Target.AverageUtilization
replicaCountProposal, percentageProposal, rawProposal, timestampProposal, err := a.replicaCalc.GetResourceReplicas(currentReplicas, targetUtilization, metricSpec.Resource.Name, hpa.Namespace, selector)
targetUtilization := *target.AverageUtilization
replicaCountProposal, percentageProposal, rawProposal, timestampProposal, err := a.replicaCalc.GetResourceReplicas(currentReplicas, targetUtilization, resourceName, namespace, selector, container)
if err != nil {
return 0, nil, time.Time{}, "", condition, fmt.Errorf("failed to get %s utilization: %v", resourceName, err)
}
metricNameProposal = fmt.Sprintf("%s resource utilization (percentage of request)", resourceName)
status := autoscalingv2.MetricValueStatus{
AverageUtilization: &percentageProposal,
AverageValue: resource.NewMilliQuantity(rawProposal, resource.DecimalSI),
}
return replicaCountProposal, &status, timestampProposal, metricNameProposal, autoscalingv2.HorizontalPodAutoscalerCondition{}, nil
}
// computeStatusForResourceMetric computes the desired number of replicas for the specified metric of type ResourceMetricSourceType.
func (a *HorizontalController) computeStatusForResourceMetric(currentReplicas int32, metricSpec autoscalingv2.MetricSpec, hpa *autoscalingv2.HorizontalPodAutoscaler,
selector labels.Selector, status *autoscalingv2.MetricStatus) (replicaCountProposal int32, timestampProposal time.Time,
metricNameProposal string, condition autoscalingv2.HorizontalPodAutoscalerCondition, err error) {
replicaCountProposal, metricValueStatus, timestampProposal, metricNameProposal, condition, err := a.computeStatusForResourceMetricGeneric(currentReplicas, metricSpec.Resource.Target, metricSpec.Resource.Name, hpa.Namespace, "", selector)
if err != nil {
condition = a.getUnableComputeReplicaCountCondition(hpa, "FailedGetResourceMetric", err)
return 0, time.Time{}, "", condition, fmt.Errorf("failed to get %s utilization: %v", metricSpec.Resource.Name, err)
return replicaCountProposal, timestampProposal, metricNameProposal, condition, err
}
metricNameProposal = fmt.Sprintf("%s resource utilization (percentage of request)", metricSpec.Resource.Name)
*status = autoscalingv2.MetricStatus{
Type: autoscalingv2.ResourceMetricSourceType,
Resource: &autoscalingv2.ResourceMetricStatus{
Name: metricSpec.Resource.Name,
Current: autoscalingv2.MetricValueStatus{
AverageUtilization: &percentageProposal,
AverageValue: resource.NewMilliQuantity(rawProposal, resource.DecimalSI),
},
Name: metricSpec.Resource.Name,
Current: *metricValueStatus,
},
}
return replicaCountProposal, timestampProposal, metricNameProposal, autoscalingv2.HorizontalPodAutoscalerCondition{}, nil
return replicaCountProposal, timestampProposal, metricNameProposal, condition, nil
}
// computeStatusForContainerResourceMetric computes the desired number of replicas for the specified metric of type ResourceMetricSourceType.
func (a *HorizontalController) computeStatusForContainerResourceMetric(currentReplicas int32, metricSpec autoscalingv2.MetricSpec, hpa *autoscalingv2.HorizontalPodAutoscaler,
selector labels.Selector, status *autoscalingv2.MetricStatus) (replicaCountProposal int32, timestampProposal time.Time,
metricNameProposal string, condition autoscalingv2.HorizontalPodAutoscalerCondition, err error) {
replicaCountProposal, metricValueStatus, timestampProposal, metricNameProposal, condition, err := a.computeStatusForResourceMetricGeneric(currentReplicas, metricSpec.ContainerResource.Target, metricSpec.ContainerResource.Name, hpa.Namespace, metricSpec.ContainerResource.Container, selector)
if err != nil {
condition = a.getUnableComputeReplicaCountCondition(hpa, "FailedGetContainerResourceMetric", err)
return replicaCountProposal, timestampProposal, metricNameProposal, condition, err
}
*status = autoscalingv2.MetricStatus{
Type: autoscalingv2.ContainerResourceMetricSourceType,
ContainerResource: &autoscalingv2.ContainerResourceMetricStatus{
Name: metricSpec.ContainerResource.Name,
Container: metricSpec.ContainerResource.Container,
Current: *metricValueStatus,
},
}
return replicaCountProposal, timestampProposal, metricNameProposal, condition, nil
}
// computeStatusForExternalMetric computes the desired number of replicas for the specified metric of type ExternalMetricSourceType.
@ -783,7 +817,7 @@ func getReplicasChangePerPeriod(periodSeconds int32, scaleEvents []timestampedSc
}
func (a *HorizontalController) getUnableComputeReplicaCountCondition(hpa *autoscalingv2.HorizontalPodAutoscaler, reason string, err error) (condition autoscalingv2.HorizontalPodAutoscalerCondition) {
func (a *HorizontalController) getUnableComputeReplicaCountCondition(hpa runtime.Object, reason string, err error) (condition autoscalingv2.HorizontalPodAutoscalerCondition) {
a.eventRecorder.Event(hpa, v1.EventTypeWarning, reason, err.Error())
return autoscalingv2.HorizontalPodAutoscalerCondition{
Type: autoscalingv2.ScalingActive,

View File

@ -336,9 +336,18 @@ func (tc *testCase) prepareTestClient(t *testing.T) (*fake.Clientset, *metricsfa
Spec: v1.PodSpec{
Containers: []v1.Container{
{
Name: "container1",
Resources: v1.ResourceRequirements{
Requests: v1.ResourceList{
v1.ResourceCPU: reportedCPURequest,
v1.ResourceCPU: *resource.NewMilliQuantity(reportedCPURequest.MilliValue()/2, resource.DecimalSI),
},
},
},
{
Name: "container2",
Resources: v1.ResourceRequirements{
Requests: v1.ResourceList{
v1.ResourceCPU: *resource.NewMilliQuantity(reportedCPURequest.MilliValue()/2, resource.DecimalSI),
},
},
},
@ -509,13 +518,24 @@ func (tc *testCase) prepareTestClient(t *testing.T) (*fake.Clientset, *metricsfa
Window: metav1.Duration{Duration: time.Minute},
Containers: []metricsapi.ContainerMetrics{
{
Name: "container",
Name: "container1",
Usage: v1.ResourceList{
v1.ResourceCPU: *resource.NewMilliQuantity(
int64(cpu),
int64(cpu/2),
resource.DecimalSI),
v1.ResourceMemory: *resource.NewQuantity(
int64(1024*1024),
int64(1024*1024/2),
resource.BinarySI),
},
},
{
Name: "container2",
Usage: v1.ResourceList{
v1.ResourceCPU: *resource.NewMilliQuantity(
int64(cpu/2),
resource.DecimalSI),
v1.ResourceMemory: *resource.NewQuantity(
int64(1024*1024/2),
resource.BinarySI),
},
},
@ -773,6 +793,31 @@ func TestScaleUp(t *testing.T) {
tc.runTest(t)
}
func TestScaleUpContainer(t *testing.T) {
tc := testCase{
minReplicas: 2,
maxReplicas: 6,
specReplicas: 3,
statusReplicas: 3,
expectedDesiredReplicas: 5,
metricsTarget: []autoscalingv2.MetricSpec{{
Type: autoscalingv2.ContainerResourceMetricSourceType,
ContainerResource: &autoscalingv2.ContainerResourceMetricSource{
Name: v1.ResourceCPU,
Target: autoscalingv2.MetricTarget{
Type: autoscalingv2.UtilizationMetricType,
AverageUtilization: utilpointer.Int32Ptr(30),
},
Container: "container1",
},
}},
reportedLevels: []uint64{300, 500, 700},
reportedCPURequests: []resource.Quantity{resource.MustParse("1.0"), resource.MustParse("1.0"), resource.MustParse("1.0")},
useMetricsAPI: true,
}
tc.runTest(t)
}
func TestScaleUpUnreadyLessScale(t *testing.T) {
tc := testCase{
minReplicas: 2,
@ -1269,6 +1314,32 @@ func TestScaleDown(t *testing.T) {
tc.runTest(t)
}
func TestScaleDownContainerResource(t *testing.T) {
tc := testCase{
minReplicas: 2,
maxReplicas: 6,
specReplicas: 5,
statusReplicas: 5,
expectedDesiredReplicas: 3,
reportedLevels: []uint64{100, 300, 500, 250, 250},
reportedCPURequests: []resource.Quantity{resource.MustParse("1.0"), resource.MustParse("1.0"), resource.MustParse("1.0"), resource.MustParse("1.0"), resource.MustParse("1.0")},
metricsTarget: []autoscalingv2.MetricSpec{{
Type: autoscalingv2.ContainerResourceMetricSourceType,
ContainerResource: &autoscalingv2.ContainerResourceMetricSource{
Container: "container2",
Name: v1.ResourceCPU,
Target: autoscalingv2.MetricTarget{
Type: autoscalingv2.UtilizationMetricType,
AverageUtilization: utilpointer.Int32Ptr(50),
},
},
}},
useMetricsAPI: true,
recommendations: []timestampedRecommendation{},
}
tc.runTest(t)
}
func TestScaleDownWithScalingRules(t *testing.T) {
tc := testCase{
minReplicas: 2,
@ -2809,7 +2880,7 @@ func TestScaleDownRCImmediately(t *testing.T) {
tc.runTest(t)
}
func TestAvoidUncessaryUpdates(t *testing.T) {
func TestAvoidUnnecessaryUpdates(t *testing.T) {
now := metav1.Time{Time: time.Now().Add(-time.Hour)}
tc := testCase{
minReplicas: 2,

View File

@ -126,14 +126,11 @@ func (tc *legacyReplicaCalcTestCase) prepareTestClient(t *testing.T) *fake.Clien
Timestamp: metav1.Time{Time: tc.timestamp},
Containers: make([]metricsapi.ContainerMetrics, numContainersPerPod),
}
for i := 0; i < numContainersPerPod; i++ {
for i, m := range resValue {
podMetric.Containers[i] = metricsapi.ContainerMetrics{
Name: fmt.Sprintf("container%v", i),
Usage: v1.ResourceList{
v1.ResourceName(tc.resource.name): *resource.NewMilliQuantity(
int64(resValue),
resource.DecimalSI),
tc.resource.name: *resource.NewMilliQuantity(m, resource.DecimalSI),
},
}
}
@ -209,7 +206,7 @@ func (tc *legacyReplicaCalcTestCase) runTest(t *testing.T) {
}
if tc.resource != nil {
outReplicas, outUtilization, outRawValue, outTimestamp, err := replicaCalc.GetResourceReplicas(tc.currentReplicas, tc.resource.targetUtilization, tc.resource.name, testNamespace, selector)
outReplicas, outUtilization, outRawValue, outTimestamp, err := replicaCalc.GetResourceReplicas(tc.currentReplicas, tc.resource.targetUtilization, tc.resource.name, testNamespace, selector, "")
if tc.expectedError != nil {
require.Error(t, err, "there should be an error calculating the replica count")
@ -244,7 +241,7 @@ func TestLegacyReplicaCalcDisjointResourcesMetrics(t *testing.T) {
resource: &resourceInfo{
name: v1.ResourceCPU,
requests: []resource.Quantity{resource.MustParse("1.0")},
levels: []int64{100},
levels: makePodMetricLevels(100),
podNames: []string{"an-older-pod-name"},
targetUtilization: 100,
@ -260,7 +257,7 @@ func TestLegacyReplicaCalcScaleUp(t *testing.T) {
resource: &resourceInfo{
name: v1.ResourceCPU,
requests: []resource.Quantity{resource.MustParse("1.0"), resource.MustParse("1.0"), resource.MustParse("1.0")},
levels: []int64{300, 500, 700},
levels: makePodMetricLevels(300, 500, 700),
targetUtilization: 30,
expectedUtilization: 50,
@ -278,7 +275,7 @@ func TestLegacyReplicaCalcScaleUpUnreadyLessScale(t *testing.T) {
resource: &resourceInfo{
name: v1.ResourceCPU,
requests: []resource.Quantity{resource.MustParse("1.0"), resource.MustParse("1.0"), resource.MustParse("1.0")},
levels: []int64{300, 500, 700},
levels: makePodMetricLevels(300, 500, 700),
targetUtilization: 30,
expectedUtilization: 60,
@ -296,7 +293,7 @@ func TestLegacyReplicaCalcScaleUpUnreadyNoScale(t *testing.T) {
resource: &resourceInfo{
name: v1.ResourceCPU,
requests: []resource.Quantity{resource.MustParse("1.0"), resource.MustParse("1.0"), resource.MustParse("1.0")},
levels: []int64{400, 500, 700},
levels: makePodMetricLevels(400, 500, 700),
targetUtilization: 30,
expectedUtilization: 40,
@ -357,7 +354,7 @@ func TestLegacyReplicaCalcScaleDown(t *testing.T) {
resource: &resourceInfo{
name: v1.ResourceCPU,
requests: []resource.Quantity{resource.MustParse("1.0"), resource.MustParse("1.0"), resource.MustParse("1.0"), resource.MustParse("1.0"), resource.MustParse("1.0")},
levels: []int64{100, 300, 500, 250, 250},
levels: makePodMetricLevels(100, 300, 500, 250, 250),
targetUtilization: 50,
expectedUtilization: 28,
@ -389,7 +386,7 @@ func TestLegacyReplicaCalcScaleDownIgnoresUnreadyPods(t *testing.T) {
resource: &resourceInfo{
name: v1.ResourceCPU,
requests: []resource.Quantity{resource.MustParse("1.0"), resource.MustParse("1.0"), resource.MustParse("1.0"), resource.MustParse("1.0"), resource.MustParse("1.0")},
levels: []int64{100, 300, 500, 250, 250},
levels: makePodMetricLevels(100, 300, 500, 250, 250),
targetUtilization: 50,
expectedUtilization: 30,
@ -406,7 +403,7 @@ func TestLegacyReplicaCalcTolerance(t *testing.T) {
resource: &resourceInfo{
name: v1.ResourceCPU,
requests: []resource.Quantity{resource.MustParse("0.9"), resource.MustParse("1.0"), resource.MustParse("1.1")},
levels: []int64{1010, 1030, 1020},
levels: makePodMetricLevels(1010, 1030, 1020),
targetUtilization: 100,
expectedUtilization: 102,
@ -437,7 +434,7 @@ func TestLegacyReplicaCalcSuperfluousMetrics(t *testing.T) {
resource: &resourceInfo{
name: v1.ResourceCPU,
requests: []resource.Quantity{resource.MustParse("1.0"), resource.MustParse("1.0"), resource.MustParse("1.0"), resource.MustParse("1.0")},
levels: []int64{4000, 9500, 3000, 7000, 3200, 2000},
levels: makePodMetricLevels(4000, 9500, 3000, 7000, 3200, 2000),
targetUtilization: 100,
expectedUtilization: 587,
expectedValue: numContainersPerPod * 5875,
@ -453,7 +450,7 @@ func TestLegacyReplicaCalcMissingMetrics(t *testing.T) {
resource: &resourceInfo{
name: v1.ResourceCPU,
requests: []resource.Quantity{resource.MustParse("1.0"), resource.MustParse("1.0"), resource.MustParse("1.0"), resource.MustParse("1.0")},
levels: []int64{400, 95},
levels: makePodMetricLevels(400, 95),
targetUtilization: 100,
expectedUtilization: 24,
@ -470,7 +467,7 @@ func TestLegacyReplicaCalcEmptyMetrics(t *testing.T) {
resource: &resourceInfo{
name: v1.ResourceCPU,
requests: []resource.Quantity{resource.MustParse("1.0"), resource.MustParse("1.0"), resource.MustParse("1.0")},
levels: []int64{},
levels: makePodMetricLevels(),
targetUtilization: 100,
},
@ -485,7 +482,7 @@ func TestLegacyReplicaCalcEmptyCPURequest(t *testing.T) {
resource: &resourceInfo{
name: v1.ResourceCPU,
requests: []resource.Quantity{},
levels: []int64{200},
levels: makePodMetricLevels(200),
targetUtilization: 100,
},
@ -500,7 +497,7 @@ func TestLegacyReplicaCalcMissingMetricsNoChangeEq(t *testing.T) {
resource: &resourceInfo{
name: v1.ResourceCPU,
requests: []resource.Quantity{resource.MustParse("1.0"), resource.MustParse("1.0")},
levels: []int64{1000},
levels: makePodMetricLevels(1000),
targetUtilization: 100,
expectedUtilization: 100,
@ -517,7 +514,7 @@ func TestLegacyReplicaCalcMissingMetricsNoChangeGt(t *testing.T) {
resource: &resourceInfo{
name: v1.ResourceCPU,
requests: []resource.Quantity{resource.MustParse("1.0"), resource.MustParse("1.0")},
levels: []int64{1900},
levels: makePodMetricLevels(1900),
targetUtilization: 100,
expectedUtilization: 190,
@ -534,7 +531,7 @@ func TestLegacyReplicaCalcMissingMetricsNoChangeLt(t *testing.T) {
resource: &resourceInfo{
name: v1.ResourceCPU,
requests: []resource.Quantity{resource.MustParse("1.0"), resource.MustParse("1.0")},
levels: []int64{600},
levels: makePodMetricLevels(600),
targetUtilization: 100,
expectedUtilization: 60,
@ -552,7 +549,7 @@ func TestLegacyReplicaCalcMissingMetricsUnreadyNoChange(t *testing.T) {
resource: &resourceInfo{
name: v1.ResourceCPU,
requests: []resource.Quantity{resource.MustParse("1.0"), resource.MustParse("1.0"), resource.MustParse("1.0")},
levels: []int64{100, 450},
levels: makePodMetricLevels(100, 450),
targetUtilization: 50,
expectedUtilization: 45,
@ -570,7 +567,7 @@ func TestLegacyReplicaCalcMissingMetricsUnreadyScaleUp(t *testing.T) {
resource: &resourceInfo{
name: v1.ResourceCPU,
requests: []resource.Quantity{resource.MustParse("1.0"), resource.MustParse("1.0"), resource.MustParse("1.0")},
levels: []int64{100, 2000},
levels: makePodMetricLevels(100, 2000),
targetUtilization: 50,
expectedUtilization: 200,
@ -588,7 +585,7 @@ func TestLegacyReplicaCalcMissingMetricsUnreadyScaleDown(t *testing.T) {
resource: &resourceInfo{
name: v1.ResourceCPU,
requests: []resource.Quantity{resource.MustParse("1.0"), resource.MustParse("1.0"), resource.MustParse("1.0"), resource.MustParse("1.0")},
levels: []int64{100, 100, 100},
levels: makePodMetricLevels(100, 100, 100),
targetUtilization: 50,
expectedUtilization: 10,
@ -627,18 +624,18 @@ func TestLegacyReplicaCalcComputedToleranceAlgImplementation(t *testing.T) {
expectedReplicas: finalPods,
resource: &resourceInfo{
name: v1.ResourceCPU,
levels: []int64{
totalUsedCPUOfAllPods / 10,
totalUsedCPUOfAllPods / 10,
totalUsedCPUOfAllPods / 10,
totalUsedCPUOfAllPods / 10,
totalUsedCPUOfAllPods / 10,
totalUsedCPUOfAllPods / 10,
totalUsedCPUOfAllPods / 10,
totalUsedCPUOfAllPods / 10,
totalUsedCPUOfAllPods / 10,
totalUsedCPUOfAllPods / 10,
},
levels: makePodMetricLevels(
totalUsedCPUOfAllPods/10,
totalUsedCPUOfAllPods/10,
totalUsedCPUOfAllPods/10,
totalUsedCPUOfAllPods/10,
totalUsedCPUOfAllPods/10,
totalUsedCPUOfAllPods/10,
totalUsedCPUOfAllPods/10,
totalUsedCPUOfAllPods/10,
totalUsedCPUOfAllPods/10,
totalUsedCPUOfAllPods/10,
),
requests: []resource.Quantity{
resource.MustParse(fmt.Sprint(perPodRequested+100) + "m"),
resource.MustParse(fmt.Sprint(perPodRequested-100) + "m"),

View File

@ -20,6 +20,7 @@ go_library(
"//staging/src/k8s.io/client-go/kubernetes/typed/core/v1:go_default_library",
"//staging/src/k8s.io/metrics/pkg/apis/custom_metrics/v1beta2:go_default_library",
"//staging/src/k8s.io/metrics/pkg/apis/metrics/v1alpha1:go_default_library",
"//staging/src/k8s.io/metrics/pkg/apis/metrics/v1beta1:go_default_library",
"//staging/src/k8s.io/metrics/pkg/client/clientset/versioned/typed/metrics/v1beta1:go_default_library",
"//staging/src/k8s.io/metrics/pkg/client/custom_metrics:go_default_library",
"//staging/src/k8s.io/metrics/pkg/client/external_metrics:go_default_library",

View File

@ -38,8 +38,9 @@ type PodMetricsInfo map[string]PodMetric
// resource metrics as well as pod-level arbitrary metrics
type MetricsClient interface {
// GetResourceMetric gets the given resource metric (and an associated oldest timestamp)
// for all pods matching the specified selector in the given namespace
GetResourceMetric(resource v1.ResourceName, namespace string, selector labels.Selector) (PodMetricsInfo, time.Time, error)
// for the specified named container in all pods matching the specified selector in the given namespace and when
// the container is an empty string it returns the sum of all the container metrics.
GetResourceMetric(resource v1.ResourceName, namespace string, selector labels.Selector, container string) (PodMetricsInfo, time.Time, error)
// GetRawMetric gets the given metric (and an associated oldest timestamp)
// for all pods matching the specified selector in the given namespace

View File

@ -63,7 +63,7 @@ func NewHeapsterMetricsClient(client clientset.Interface, namespace, scheme, ser
}
}
func (h *HeapsterMetricsClient) GetResourceMetric(resource v1.ResourceName, namespace string, selector labels.Selector) (PodMetricsInfo, time.Time, error) {
func (h *HeapsterMetricsClient) GetResourceMetric(resource v1.ResourceName, namespace string, selector labels.Selector, container string) (PodMetricsInfo, time.Time, error) {
metricPath := fmt.Sprintf("/apis/metrics/v1alpha1/namespaces/%s/pods", namespace)
params := map[string]string{"labelSelector": selector.String()}
@ -92,13 +92,15 @@ func (h *HeapsterMetricsClient) GetResourceMetric(resource v1.ResourceName, name
podSum := int64(0)
missing := len(m.Containers) == 0
for _, c := range m.Containers {
resValue, found := c.Usage[v1.ResourceName(resource)]
if !found {
missing = true
klog.V(2).Infof("missing resource metric %v for container %s in pod %s/%s", resource, c.Name, namespace, m.Name)
continue
if container == "" || container == c.Name {
resValue, found := c.Usage[v1.ResourceName(resource)]
if !found {
missing = true
klog.V(2).Infof("missing resource metric %v for container %s in pod %s/%s", resource, c.Name, namespace, m.Name)
continue
}
podSum += resValue.MilliValue()
}
podSum += resValue.MilliValue()
}
if !missing {

View File

@ -222,7 +222,7 @@ func (tc *testCase) runTest(t *testing.T) {
metricsClient := NewHeapsterMetricsClient(testClient, DefaultHeapsterNamespace, DefaultHeapsterScheme, DefaultHeapsterService, DefaultHeapsterPort)
isResource := len(tc.resourceName) > 0
if isResource {
info, timestamp, err := metricsClient.GetResourceMetric(tc.resourceName, tc.namespace, tc.selector)
info, timestamp, err := metricsClient.GetResourceMetric(tc.resourceName, tc.namespace, tc.selector, "")
tc.verifyResults(t, info, timestamp, err)
} else {
info, timestamp, err := metricsClient.GetRawMetric(tc.metricName, tc.namespace, tc.selector, tc.metricSelector)

View File

@ -29,6 +29,7 @@ import (
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/runtime/schema"
customapi "k8s.io/metrics/pkg/apis/custom_metrics/v1beta2"
metricsapi "k8s.io/metrics/pkg/apis/metrics/v1beta1"
resourceclient "k8s.io/metrics/pkg/client/clientset/versioned/typed/metrics/v1beta1"
customclient "k8s.io/metrics/pkg/client/custom_metrics"
externalclient "k8s.io/metrics/pkg/client/external_metrics"
@ -63,7 +64,7 @@ type resourceMetricsClient struct {
// GetResourceMetric gets the given resource metric (and an associated oldest timestamp)
// for all pods matching the specified selector in the given namespace
func (c *resourceMetricsClient) GetResourceMetric(resource v1.ResourceName, namespace string, selector labels.Selector) (PodMetricsInfo, time.Time, error) {
func (c *resourceMetricsClient) GetResourceMetric(resource v1.ResourceName, namespace string, selector labels.Selector, container string) (PodMetricsInfo, time.Time, error) {
metrics, err := c.client.PodMetricses(namespace).List(context.TODO(), metav1.ListOptions{LabelSelector: selector.String()})
if err != nil {
return nil, time.Time{}, fmt.Errorf("unable to fetch metrics from resource metrics API: %v", err)
@ -72,34 +73,66 @@ func (c *resourceMetricsClient) GetResourceMetric(resource v1.ResourceName, name
if len(metrics.Items) == 0 {
return nil, time.Time{}, fmt.Errorf("no metrics returned from resource metrics API")
}
var res PodMetricsInfo
if container != "" {
res, err = getContainerMetrics(metrics.Items, resource, container)
if err != nil {
return nil, time.Time{}, fmt.Errorf("failed to get container metrics: %v", err)
}
} else {
res = getPodMetrics(metrics.Items, resource)
}
timestamp := metrics.Items[0].Timestamp.Time
return res, timestamp, nil
}
res := make(PodMetricsInfo, len(metrics.Items))
func getContainerMetrics(rawMetrics []metricsapi.PodMetrics, resource v1.ResourceName, container string) (PodMetricsInfo, error) {
res := make(PodMetricsInfo, len(rawMetrics))
for _, m := range rawMetrics {
containerFound := false
for _, c := range m.Containers {
if c.Name == container {
containerFound = true
if val, resFound := c.Usage[resource]; resFound {
res[m.Name] = PodMetric{
Timestamp: m.Timestamp.Time,
Window: m.Window.Duration,
Value: val.MilliValue(),
}
}
break
}
}
if !containerFound {
return nil, fmt.Errorf("container %s not present in metrics for pod %s/%s", container, m.Namespace, m.Name)
}
}
return res, nil
}
for _, m := range metrics.Items {
func getPodMetrics(rawMetrics []metricsapi.PodMetrics, resource v1.ResourceName) PodMetricsInfo {
res := make(PodMetricsInfo, len(rawMetrics))
for _, m := range rawMetrics {
podSum := int64(0)
missing := len(m.Containers) == 0
for _, c := range m.Containers {
resValue, found := c.Usage[v1.ResourceName(resource)]
resValue, found := c.Usage[resource]
if !found {
missing = true
klog.V(2).Infof("missing resource metric %v for container %s in pod %s/%s", resource, c.Name, namespace, m.Name)
break // containers loop
klog.V(2).Infof("missing resource metric %v for %s/%s", resource, m.Namespace, m.Name)
break
}
podSum += resValue.MilliValue()
}
if !missing {
res[m.Name] = PodMetric{
Timestamp: m.Timestamp.Time,
Window: m.Window.Duration,
Value: int64(podSum),
Value: podSum,
}
}
}
timestamp := metrics.Items[0].Timestamp.Time
return res, timestamp, nil
return res
}
// customMetricsClient implements the custom-metrics-related parts of MetricsClient,

View File

@ -50,12 +50,13 @@ type restClientTestCase struct {
targetTimestamp int
window time.Duration
reportedMetricPoints []metricPoint
reportedPodMetrics [][]int64
reportedPodMetrics []map[string]int64
singleObject *autoscalingapi.CrossVersionObjectReference
namespace string
selector labels.Selector
resourceName v1.ResourceName
container string
metricName string
metricSelector *metav1.LabelSelector
metricLabelSelector labels.Selector
@ -91,9 +92,9 @@ func (tc *restClientTestCase) prepareTestClient(t *testing.T) (*metricsfake.Clie
Window: metav1.Duration{Duration: tc.window},
Containers: []metricsapi.ContainerMetrics{},
}
for j, cpu := range containers {
for containerName, cpu := range containers {
cm := metricsapi.ContainerMetrics{
Name: fmt.Sprintf("%s-%d-container-%d", podNamePrefix, i, j),
Name: containerName,
Usage: v1.ResourceList{
v1.ResourceCPU: *resource.NewMilliQuantity(
cpu,
@ -230,7 +231,7 @@ func (tc *restClientTestCase) runTest(t *testing.T) {
isResource := len(tc.resourceName) > 0
isExternal := tc.metricSelector != nil
if isResource {
info, timestamp, err := metricsClient.GetResourceMetric(v1.ResourceName(tc.resourceName), tc.namespace, tc.selector)
info, timestamp, err := metricsClient.GetResourceMetric(v1.ResourceName(tc.resourceName), tc.namespace, tc.selector, tc.container)
tc.verifyResults(t, info, timestamp, err)
} else if isExternal {
tc.metricLabelSelector, err = metav1.LabelSelectorAsSelector(tc.metricSelector)
@ -253,7 +254,7 @@ func (tc *restClientTestCase) runTest(t *testing.T) {
}
}
func TestRESTClientCPU(t *testing.T) {
func TestRESTClientPodCPU(t *testing.T) {
targetTimestamp := 1
window := 30 * time.Second
tc := restClientTestCase{
@ -265,7 +266,25 @@ func TestRESTClientCPU(t *testing.T) {
resourceName: v1.ResourceCPU,
targetTimestamp: targetTimestamp,
window: window,
reportedPodMetrics: [][]int64{{5000}, {5000}, {5000}},
reportedPodMetrics: []map[string]int64{{"test": 5000}, {"test": 5000}, {"test": 5000}},
}
tc.runTest(t)
}
func TestRESTClientContainerCPU(t *testing.T) {
targetTimestamp := 1
window := 30 * time.Second
tc := restClientTestCase{
desiredMetricValues: PodMetricsInfo{
"test-pod-0": {Value: 5000, Timestamp: offsetTimestampBy(targetTimestamp), Window: window},
"test-pod-1": {Value: 5000, Timestamp: offsetTimestampBy(targetTimestamp), Window: window},
"test-pod-2": {Value: 5000, Timestamp: offsetTimestampBy(targetTimestamp), Window: window},
},
container: "test-1",
resourceName: v1.ResourceCPU,
targetTimestamp: targetTimestamp,
window: window,
reportedPodMetrics: []map[string]int64{{"test-1": 5000, "test-2": 500}, {"test-1": 5000, "test-2": 500}, {"test-1": 5000, "test-2": 500}},
}
tc.runTest(t)
}
@ -362,17 +381,17 @@ func TestRESTClientExternalEmptyMetrics(t *testing.T) {
tc.runTest(t)
}
func TestRESTClientCPUEmptyMetrics(t *testing.T) {
func TestRESTClientPodCPUEmptyMetrics(t *testing.T) {
tc := restClientTestCase{
resourceName: v1.ResourceCPU,
desiredError: fmt.Errorf("no metrics returned from resource metrics API"),
reportedMetricPoints: []metricPoint{},
reportedPodMetrics: [][]int64{},
reportedPodMetrics: []map[string]int64{},
}
tc.runTest(t)
}
func TestRESTClientCPUEmptyMetricsForOnePod(t *testing.T) {
func TestRESTClientPodCPUEmptyMetricsForOnePod(t *testing.T) {
targetTimestamp := 1
window := 30 * time.Second
tc := restClientTestCase{
@ -383,7 +402,25 @@ func TestRESTClientCPUEmptyMetricsForOnePod(t *testing.T) {
},
targetTimestamp: targetTimestamp,
window: window,
reportedPodMetrics: [][]int64{{100}, {300, 400}, {}},
reportedPodMetrics: []map[string]int64{{"test-1": 100}, {"test-1": 300, "test-2": 400}, {}},
}
tc.runTest(t)
}
func TestRESTClientContainerCPUEmptyMetricsForOnePod(t *testing.T) {
targetTimestamp := 1
window := 30 * time.Second
tc := restClientTestCase{
resourceName: v1.ResourceCPU,
desiredMetricValues: PodMetricsInfo{
"test-pod-0": {Value: 100, Timestamp: offsetTimestampBy(targetTimestamp), Window: window},
"test-pod-1": {Value: 300, Timestamp: offsetTimestampBy(targetTimestamp), Window: window},
},
container: "test-1",
targetTimestamp: targetTimestamp,
window: window,
desiredError: fmt.Errorf("failed to get container metrics"),
reportedPodMetrics: []map[string]int64{{"test-1": 100}, {"test-1": 300, "test-2": 400}, {}},
}
tc.runTest(t)
}

View File

@ -61,8 +61,8 @@ func NewReplicaCalculator(metricsClient metricsclient.MetricsClient, podLister c
// GetResourceReplicas calculates the desired replica count based on a target resource utilization percentage
// of the given resource for pods matching the given selector in the given namespace, and the current replica count
func (c *ReplicaCalculator) GetResourceReplicas(currentReplicas int32, targetUtilization int32, resource v1.ResourceName, namespace string, selector labels.Selector) (replicaCount int32, utilization int32, rawUtilization int64, timestamp time.Time, err error) {
metrics, timestamp, err := c.metricsClient.GetResourceMetric(resource, namespace, selector)
func (c *ReplicaCalculator) GetResourceReplicas(currentReplicas int32, targetUtilization int32, resource v1.ResourceName, namespace string, selector labels.Selector, container string) (replicaCount int32, utilization int32, rawUtilization int64, timestamp time.Time, err error) {
metrics, timestamp, err := c.metricsClient.GetResourceMetric(resource, namespace, selector, container)
if err != nil {
return 0, 0, 0, time.Time{}, fmt.Errorf("unable to get metrics for resource %s: %v", resource, err)
}
@ -79,7 +79,7 @@ func (c *ReplicaCalculator) GetResourceReplicas(currentReplicas int32, targetUti
readyPodCount, unreadyPods, missingPods, ignoredPods := groupPods(podList, metrics, resource, c.cpuInitializationPeriod, c.delayOfInitialReadinessStatus)
removeMetricsForPods(metrics, ignoredPods)
removeMetricsForPods(metrics, unreadyPods)
requests, err := calculatePodRequests(podList, resource)
requests, err := calculatePodRequests(podList, container, resource)
if err != nil {
return 0, 0, 0, time.Time{}, err
}
@ -150,8 +150,8 @@ func (c *ReplicaCalculator) GetResourceReplicas(currentReplicas int32, targetUti
// GetRawResourceReplicas calculates the desired replica count based on a target resource utilization (as a raw milli-value)
// for pods matching the given selector in the given namespace, and the current replica count
func (c *ReplicaCalculator) GetRawResourceReplicas(currentReplicas int32, targetUtilization int64, resource v1.ResourceName, namespace string, selector labels.Selector) (replicaCount int32, utilization int64, timestamp time.Time, err error) {
metrics, timestamp, err := c.metricsClient.GetResourceMetric(resource, namespace, selector)
func (c *ReplicaCalculator) GetRawResourceReplicas(currentReplicas int32, targetUtilization int64, resource v1.ResourceName, namespace string, selector labels.Selector, container string) (replicaCount int32, utilization int64, timestamp time.Time, err error) {
metrics, timestamp, err := c.metricsClient.GetResourceMetric(resource, namespace, selector, container)
if err != nil {
return 0, 0, time.Time{}, fmt.Errorf("unable to get metrics for resource %s: %v", resource, err)
}
@ -414,15 +414,17 @@ func groupPods(pods []*v1.Pod, metrics metricsclient.PodMetricsInfo, resource v1
return
}
func calculatePodRequests(pods []*v1.Pod, resource v1.ResourceName) (map[string]int64, error) {
func calculatePodRequests(pods []*v1.Pod, container string, resource v1.ResourceName) (map[string]int64, error) {
requests := make(map[string]int64, len(pods))
for _, pod := range pods {
podSum := int64(0)
for _, container := range pod.Spec.Containers {
if containerRequest, ok := container.Resources.Requests[resource]; ok {
podSum += containerRequest.MilliValue()
} else {
return nil, fmt.Errorf("missing request for %s", resource)
for _, c := range pod.Spec.Containers {
if container == "" || container == c.Name {
if containerRequest, ok := c.Resources.Requests[resource]; ok {
podSum += containerRequest.MilliValue()
} else {
return nil, fmt.Errorf("missing request for %s", resource)
}
}
}
requests[pod.Name] = podSum

View File

@ -52,7 +52,7 @@ import (
type resourceInfo struct {
name v1.ResourceName
requests []resource.Quantity
levels []int64
levels [][]int64
// only applies to pod names returned from "heapster"
podNames []string
@ -93,6 +93,7 @@ type replicaCalcTestCase struct {
resource *resourceInfo
metric *metricInfo
metricLabelSelector labels.Selector
container string
podReadiness []v1.ConditionStatus
podStartTime []metav1.Time
@ -152,7 +153,7 @@ func (tc *replicaCalcTestCase) prepareTestClientSet() *fake.Clientset {
},
},
Spec: v1.PodSpec{
Containers: []v1.Container{{}, {}},
Containers: []v1.Container{{Name: "container1"}, {Name: "container2"}},
},
}
if podDeletionTimestamp {
@ -202,13 +203,11 @@ func (tc *replicaCalcTestCase) prepareTestMetricsClient() *metricsfake.Clientset
Containers: make([]metricsapi.ContainerMetrics, numContainersPerPod),
}
for i := 0; i < numContainersPerPod; i++ {
for i, m := range resValue {
podMetric.Containers[i] = metricsapi.ContainerMetrics{
Name: fmt.Sprintf("container%v", i),
Name: fmt.Sprintf("container%v", i+1),
Usage: v1.ResourceList{
v1.ResourceName(tc.resource.name): *resource.NewMilliQuantity(
int64(resValue),
resource.DecimalSI),
tc.resource.name: *resource.NewMilliQuantity(m, resource.DecimalSI),
},
}
}
@ -362,7 +361,7 @@ func (tc *replicaCalcTestCase) runTest(t *testing.T) {
}
if tc.resource != nil {
outReplicas, outUtilization, outRawValue, outTimestamp, err := replicaCalc.GetResourceReplicas(tc.currentReplicas, tc.resource.targetUtilization, tc.resource.name, testNamespace, selector)
outReplicas, outUtilization, outRawValue, outTimestamp, err := replicaCalc.GetResourceReplicas(tc.currentReplicas, tc.resource.targetUtilization, tc.resource.name, testNamespace, selector, tc.container)
if tc.expectedError != nil {
require.Error(t, err, "there should be an error calculating the replica count")
@ -424,7 +423,16 @@ func (tc *replicaCalcTestCase) runTest(t *testing.T) {
assert.Equal(t, tc.metric.expectedUtilization, outUtilization, "utilization should be as expected")
assert.True(t, tc.timestamp.Equal(outTimestamp), "timestamp should be as expected")
}
func makePodMetricLevels(containerMetric ...int64) [][]int64 {
metrics := make([][]int64, len(containerMetric))
for i := 0; i < len(containerMetric); i++ {
metrics[i] = make([]int64, numContainersPerPod)
for j := 0; j < numContainersPerPod; j++ {
metrics[i][j] = containerMetric[i]
}
}
return metrics
}
func TestReplicaCalcDisjointResourcesMetrics(t *testing.T) {
tc := replicaCalcTestCase{
currentReplicas: 1,
@ -432,7 +440,7 @@ func TestReplicaCalcDisjointResourcesMetrics(t *testing.T) {
resource: &resourceInfo{
name: v1.ResourceCPU,
requests: []resource.Quantity{resource.MustParse("1.0")},
levels: []int64{100},
levels: makePodMetricLevels(100),
podNames: []string{"an-older-pod-name"},
targetUtilization: 100,
@ -441,6 +449,20 @@ func TestReplicaCalcDisjointResourcesMetrics(t *testing.T) {
tc.runTest(t)
}
func TestReplicaCalcMissingContainerMetricError(t *testing.T) {
tc := replicaCalcTestCase{
currentReplicas: 1,
expectedError: fmt.Errorf("container container2 not present in metrics for pod test-namespace/test-pod-0"),
resource: &resourceInfo{
name: v1.ResourceCPU,
requests: []resource.Quantity{resource.MustParse("1.0")},
levels: [][]int64{{0}},
},
container: "container2",
}
tc.runTest(t)
}
func TestReplicaCalcScaleUp(t *testing.T) {
tc := replicaCalcTestCase{
currentReplicas: 3,
@ -448,7 +470,7 @@ func TestReplicaCalcScaleUp(t *testing.T) {
resource: &resourceInfo{
name: v1.ResourceCPU,
requests: []resource.Quantity{resource.MustParse("1.0"), resource.MustParse("1.0"), resource.MustParse("1.0")},
levels: []int64{300, 500, 700},
levels: makePodMetricLevels(300, 500, 700),
targetUtilization: 30,
expectedUtilization: 50,
@ -458,6 +480,24 @@ func TestReplicaCalcScaleUp(t *testing.T) {
tc.runTest(t)
}
func TestReplicaCalcContainerScaleUp(t *testing.T) {
tc := replicaCalcTestCase{
currentReplicas: 3,
expectedReplicas: 5,
resource: &resourceInfo{
name: v1.ResourceCPU,
requests: []resource.Quantity{resource.MustParse("1.0"), resource.MustParse("1.0"), resource.MustParse("1.0")},
levels: [][]int64{{1000, 300}, {1000, 500}, {1000, 700}},
targetUtilization: 30,
expectedUtilization: 50,
expectedValue: 500,
},
container: "container2",
}
tc.runTest(t)
}
func TestReplicaCalcScaleUpUnreadyLessScale(t *testing.T) {
tc := replicaCalcTestCase{
currentReplicas: 3,
@ -466,7 +506,7 @@ func TestReplicaCalcScaleUpUnreadyLessScale(t *testing.T) {
resource: &resourceInfo{
name: v1.ResourceCPU,
requests: []resource.Quantity{resource.MustParse("1.0"), resource.MustParse("1.0"), resource.MustParse("1.0")},
levels: []int64{300, 500, 700},
levels: makePodMetricLevels(300, 500, 700),
targetUtilization: 30,
expectedUtilization: 60,
@ -476,6 +516,25 @@ func TestReplicaCalcScaleUpUnreadyLessScale(t *testing.T) {
tc.runTest(t)
}
func TestReplicaCalcScaleUpContainerHotCpuLessScale(t *testing.T) {
tc := replicaCalcTestCase{
currentReplicas: 3,
expectedReplicas: 4,
podStartTime: []metav1.Time{hotCPUCreationTime(), coolCPUCreationTime(), coolCPUCreationTime()},
resource: &resourceInfo{
name: v1.ResourceCPU,
requests: []resource.Quantity{resource.MustParse("1.0"), resource.MustParse("1.0"), resource.MustParse("1.0")},
levels: [][]int64{{0, 300}, {0, 500}, {0, 700}},
targetUtilization: 30,
expectedUtilization: 60,
expectedValue: 600,
},
container: "container2",
}
tc.runTest(t)
}
func TestReplicaCalcScaleUpHotCpuLessScale(t *testing.T) {
tc := replicaCalcTestCase{
currentReplicas: 3,
@ -484,7 +543,7 @@ func TestReplicaCalcScaleUpHotCpuLessScale(t *testing.T) {
resource: &resourceInfo{
name: v1.ResourceCPU,
requests: []resource.Quantity{resource.MustParse("1.0"), resource.MustParse("1.0"), resource.MustParse("1.0")},
levels: []int64{300, 500, 700},
levels: makePodMetricLevels(300, 500, 700),
targetUtilization: 30,
expectedUtilization: 60,
@ -502,7 +561,7 @@ func TestReplicaCalcScaleUpUnreadyNoScale(t *testing.T) {
resource: &resourceInfo{
name: v1.ResourceCPU,
requests: []resource.Quantity{resource.MustParse("1.0"), resource.MustParse("1.0"), resource.MustParse("1.0")},
levels: []int64{400, 500, 700},
levels: makePodMetricLevels(400, 500, 700),
targetUtilization: 30,
expectedUtilization: 40,
@ -521,7 +580,7 @@ func TestReplicaCalcScaleHotCpuNoScale(t *testing.T) {
resource: &resourceInfo{
name: v1.ResourceCPU,
requests: []resource.Quantity{resource.MustParse("1.0"), resource.MustParse("1.0"), resource.MustParse("1.0")},
levels: []int64{400, 500, 700},
levels: makePodMetricLevels(400, 500, 700),
targetUtilization: 30,
expectedUtilization: 40,
@ -540,7 +599,7 @@ func TestReplicaCalcScaleUpIgnoresFailedPods(t *testing.T) {
resource: &resourceInfo{
name: v1.ResourceCPU,
requests: []resource.Quantity{resource.MustParse("1.0"), resource.MustParse("1.0"), resource.MustParse("1.0"), resource.MustParse("1.0")},
levels: []int64{500, 700},
levels: makePodMetricLevels(500, 700),
targetUtilization: 30,
expectedUtilization: 60,
@ -550,6 +609,26 @@ func TestReplicaCalcScaleUpIgnoresFailedPods(t *testing.T) {
tc.runTest(t)
}
func TestReplicaCalcScaleUpContainerIgnoresFailedPods(t *testing.T) {
tc := replicaCalcTestCase{
currentReplicas: 2,
expectedReplicas: 4,
podReadiness: []v1.ConditionStatus{v1.ConditionTrue, v1.ConditionTrue, v1.ConditionFalse, v1.ConditionFalse},
podPhase: []v1.PodPhase{v1.PodRunning, v1.PodRunning, v1.PodFailed, v1.PodFailed},
resource: &resourceInfo{
name: v1.ResourceCPU,
requests: []resource.Quantity{resource.MustParse("1.0"), resource.MustParse("1.0"), resource.MustParse("1.0"), resource.MustParse("1.0")},
levels: [][]int64{{1000, 500}, {9000, 700}},
targetUtilization: 30,
expectedUtilization: 60,
expectedValue: 600,
},
container: "container2",
}
tc.runTest(t)
}
func TestReplicaCalcScaleUpIgnoresDeletionPods(t *testing.T) {
tc := replicaCalcTestCase{
currentReplicas: 2,
@ -560,7 +639,7 @@ func TestReplicaCalcScaleUpIgnoresDeletionPods(t *testing.T) {
resource: &resourceInfo{
name: v1.ResourceCPU,
requests: []resource.Quantity{resource.MustParse("1.0"), resource.MustParse("1.0"), resource.MustParse("1.0"), resource.MustParse("1.0")},
levels: []int64{500, 700},
levels: makePodMetricLevels(500, 700),
targetUtilization: 30,
expectedUtilization: 60,
@ -570,6 +649,27 @@ func TestReplicaCalcScaleUpIgnoresDeletionPods(t *testing.T) {
tc.runTest(t)
}
func TestReplicaCalcScaleUpContainerIgnoresDeletionPods(t *testing.T) {
tc := replicaCalcTestCase{
currentReplicas: 2,
expectedReplicas: 4,
podReadiness: []v1.ConditionStatus{v1.ConditionTrue, v1.ConditionTrue, v1.ConditionFalse, v1.ConditionFalse},
podPhase: []v1.PodPhase{v1.PodRunning, v1.PodRunning, v1.PodRunning, v1.PodRunning},
podDeletionTimestamp: []bool{false, false, true, true},
resource: &resourceInfo{
name: v1.ResourceCPU,
requests: []resource.Quantity{resource.MustParse("1.0"), resource.MustParse("1.0"), resource.MustParse("1.0"), resource.MustParse("1.0")},
levels: makePodMetricLevels(500, 700), // TODO: This test is broken and works only because of missing metrics
targetUtilization: 30,
expectedUtilization: 60,
expectedValue: 600,
},
container: "container1",
}
tc.runTest(t)
}
func TestReplicaCalcScaleUpCM(t *testing.T) {
tc := replicaCalcTestCase{
currentReplicas: 3,
@ -749,7 +849,7 @@ func TestReplicaCalcScaleDown(t *testing.T) {
resource: &resourceInfo{
name: v1.ResourceCPU,
requests: []resource.Quantity{resource.MustParse("1.0"), resource.MustParse("1.0"), resource.MustParse("1.0"), resource.MustParse("1.0"), resource.MustParse("1.0")},
levels: []int64{100, 300, 500, 250, 250},
levels: makePodMetricLevels(100, 300, 500, 250, 250),
targetUtilization: 50,
expectedUtilization: 28,
@ -759,6 +859,24 @@ func TestReplicaCalcScaleDown(t *testing.T) {
tc.runTest(t)
}
func TestReplicaCalcContainerScaleDown(t *testing.T) {
tc := replicaCalcTestCase{
currentReplicas: 5,
expectedReplicas: 3,
resource: &resourceInfo{
name: v1.ResourceCPU,
requests: []resource.Quantity{resource.MustParse("1.0"), resource.MustParse("1.0"), resource.MustParse("1.0"), resource.MustParse("1.0"), resource.MustParse("1.0")},
levels: [][]int64{{1000, 100}, {1000, 300}, {1000, 500}, {1000, 250}, {1000, 250}},
targetUtilization: 50,
expectedUtilization: 28,
expectedValue: 280,
},
container: "container2",
}
tc.runTest(t)
}
func TestReplicaCalcScaleDownCM(t *testing.T) {
tc := replicaCalcTestCase{
currentReplicas: 5,
@ -845,7 +963,7 @@ func TestReplicaCalcScaleDownPerPodCMExternal(t *testing.T) {
tc.runTest(t)
}
func TestReplicaCalcScaleDownIncludeUnreadyPods(t *testing.T) {
func TestReplicaCalcScaleDownExcludeUnreadyPods(t *testing.T) {
tc := replicaCalcTestCase{
currentReplicas: 5,
expectedReplicas: 2,
@ -853,7 +971,7 @@ func TestReplicaCalcScaleDownIncludeUnreadyPods(t *testing.T) {
resource: &resourceInfo{
name: v1.ResourceCPU,
requests: []resource.Quantity{resource.MustParse("1.0"), resource.MustParse("1.0"), resource.MustParse("1.0"), resource.MustParse("1.0"), resource.MustParse("1.0")},
levels: []int64{100, 300, 500, 250, 250},
levels: makePodMetricLevels(100, 300, 500, 250, 250),
targetUtilization: 50,
expectedUtilization: 30,
@ -863,6 +981,25 @@ func TestReplicaCalcScaleDownIncludeUnreadyPods(t *testing.T) {
tc.runTest(t)
}
func TestReplicaCalcScaleDownContainerExcludeUnreadyPods(t *testing.T) {
tc := replicaCalcTestCase{
currentReplicas: 5,
expectedReplicas: 2,
podReadiness: []v1.ConditionStatus{v1.ConditionTrue, v1.ConditionTrue, v1.ConditionTrue, v1.ConditionFalse, v1.ConditionFalse},
resource: &resourceInfo{
name: v1.ResourceCPU,
requests: []resource.Quantity{resource.MustParse("1.0"), resource.MustParse("1.0"), resource.MustParse("1.0"), resource.MustParse("1.0"), resource.MustParse("1.0")},
levels: [][]int64{{1000, 100}, {1000, 300}, {1000, 500}, {1000, 250}, {1000, 250}},
targetUtilization: 50,
expectedUtilization: 30,
expectedValue: 300,
},
container: "container2",
}
tc.runTest(t)
}
func TestReplicaCalcScaleDownExcludeUnscheduledPods(t *testing.T) {
tc := replicaCalcTestCase{
currentReplicas: 5,
@ -872,7 +1009,7 @@ func TestReplicaCalcScaleDownExcludeUnscheduledPods(t *testing.T) {
resource: &resourceInfo{
name: v1.ResourceCPU,
requests: []resource.Quantity{resource.MustParse("1.0"), resource.MustParse("1.0"), resource.MustParse("1.0"), resource.MustParse("1.0"), resource.MustParse("1.0")},
levels: []int64{100},
levels: makePodMetricLevels(100),
targetUtilization: 50,
expectedUtilization: 10,
@ -882,6 +1019,26 @@ func TestReplicaCalcScaleDownExcludeUnscheduledPods(t *testing.T) {
tc.runTest(t)
}
func TestReplicaCalcScaleDownContainerExcludeUnscheduledPods(t *testing.T) {
tc := replicaCalcTestCase{
currentReplicas: 5,
expectedReplicas: 1,
podReadiness: []v1.ConditionStatus{v1.ConditionTrue, v1.ConditionFalse, v1.ConditionFalse, v1.ConditionFalse, v1.ConditionFalse},
podPhase: []v1.PodPhase{v1.PodRunning, v1.PodPending, v1.PodPending, v1.PodPending, v1.PodPending},
resource: &resourceInfo{
name: v1.ResourceCPU,
requests: []resource.Quantity{resource.MustParse("1.0"), resource.MustParse("1.0"), resource.MustParse("1.0"), resource.MustParse("1.0"), resource.MustParse("1.0")},
levels: [][]int64{{1000, 100}, {1000, 300}, {1000, 500}, {1000, 250}, {1000, 250}},
targetUtilization: 50,
expectedUtilization: 10,
expectedValue: 100,
},
container: "container2",
}
tc.runTest(t)
}
func TestReplicaCalcScaleDownIgnoreHotCpuPods(t *testing.T) {
tc := replicaCalcTestCase{
currentReplicas: 5,
@ -890,7 +1047,7 @@ func TestReplicaCalcScaleDownIgnoreHotCpuPods(t *testing.T) {
resource: &resourceInfo{
name: v1.ResourceCPU,
requests: []resource.Quantity{resource.MustParse("1.0"), resource.MustParse("1.0"), resource.MustParse("1.0"), resource.MustParse("1.0"), resource.MustParse("1.0")},
levels: []int64{100, 300, 500, 250, 250},
levels: makePodMetricLevels(100, 300, 500, 250, 250),
targetUtilization: 50,
expectedUtilization: 30,
@ -900,6 +1057,25 @@ func TestReplicaCalcScaleDownIgnoreHotCpuPods(t *testing.T) {
tc.runTest(t)
}
func TestReplicaCalcScaleDownContainerIgnoreHotCpuPods(t *testing.T) {
tc := replicaCalcTestCase{
currentReplicas: 5,
expectedReplicas: 2,
podStartTime: []metav1.Time{coolCPUCreationTime(), coolCPUCreationTime(), coolCPUCreationTime(), hotCPUCreationTime(), hotCPUCreationTime()},
resource: &resourceInfo{
name: v1.ResourceCPU,
requests: []resource.Quantity{resource.MustParse("1.0"), resource.MustParse("1.0"), resource.MustParse("1.0"), resource.MustParse("1.0"), resource.MustParse("1.0")},
levels: [][]int64{{1000, 100}, {1000, 300}, {1000, 500}, {1000, 1000}, {1000, 1000}},
targetUtilization: 50,
expectedUtilization: 30,
expectedValue: 300,
},
container: "container2",
}
tc.runTest(t)
}
func TestReplicaCalcScaleDownIgnoresFailedPods(t *testing.T) {
tc := replicaCalcTestCase{
currentReplicas: 5,
@ -909,7 +1085,7 @@ func TestReplicaCalcScaleDownIgnoresFailedPods(t *testing.T) {
resource: &resourceInfo{
name: v1.ResourceCPU,
requests: []resource.Quantity{resource.MustParse("1.0"), resource.MustParse("1.0"), resource.MustParse("1.0"), resource.MustParse("1.0"), resource.MustParse("1.0"), resource.MustParse("1.0"), resource.MustParse("1.0")},
levels: []int64{100, 300, 500, 250, 250},
levels: makePodMetricLevels(100, 300, 500, 250, 250),
targetUtilization: 50,
expectedUtilization: 28,
@ -919,6 +1095,26 @@ func TestReplicaCalcScaleDownIgnoresFailedPods(t *testing.T) {
tc.runTest(t)
}
func TestReplicaCalcScaleDownContainerIgnoresFailedPods(t *testing.T) {
tc := replicaCalcTestCase{
currentReplicas: 5,
expectedReplicas: 3,
podReadiness: []v1.ConditionStatus{v1.ConditionTrue, v1.ConditionTrue, v1.ConditionTrue, v1.ConditionTrue, v1.ConditionTrue, v1.ConditionFalse, v1.ConditionFalse},
podPhase: []v1.PodPhase{v1.PodRunning, v1.PodRunning, v1.PodRunning, v1.PodRunning, v1.PodRunning, v1.PodFailed, v1.PodFailed},
resource: &resourceInfo{
name: v1.ResourceCPU,
requests: []resource.Quantity{resource.MustParse("1.0"), resource.MustParse("1.0"), resource.MustParse("1.0"), resource.MustParse("1.0"), resource.MustParse("1.0"), resource.MustParse("1.0"), resource.MustParse("1.0")},
levels: [][]int64{{1000, 100}, {1000, 300}, {1000, 500}, {1000, 250}, {1000, 250}}, //TODO: Test is broken
targetUtilization: 50,
expectedUtilization: 28,
expectedValue: 280,
},
container: "container2",
}
tc.runTest(t)
}
func TestReplicaCalcScaleDownIgnoresDeletionPods(t *testing.T) {
tc := replicaCalcTestCase{
currentReplicas: 5,
@ -929,7 +1125,7 @@ func TestReplicaCalcScaleDownIgnoresDeletionPods(t *testing.T) {
resource: &resourceInfo{
name: v1.ResourceCPU,
requests: []resource.Quantity{resource.MustParse("1.0"), resource.MustParse("1.0"), resource.MustParse("1.0"), resource.MustParse("1.0"), resource.MustParse("1.0"), resource.MustParse("1.0"), resource.MustParse("1.0")},
levels: []int64{100, 300, 500, 250, 250},
levels: makePodMetricLevels(100, 300, 500, 250, 250),
targetUtilization: 50,
expectedUtilization: 28,
@ -950,12 +1146,13 @@ func TestReplicaCalcScaleDownIgnoresDeletionPods_StillRunning(t *testing.T) {
resource: &resourceInfo{
name: v1.ResourceCPU,
requests: []resource.Quantity{resource.MustParse("1.0"), resource.MustParse("1.0"), resource.MustParse("1.0"), resource.MustParse("1.0"), resource.MustParse("1.0"), resource.MustParse("1.0"), resource.MustParse("1.0")},
levels: []int64{100, 300, 500, 250, 250, 0, 0},
levels: [][]int64{{1000, 100}, {1000, 300}, {1000, 500}, {1000, 250}, {1000, 250}},
targetUtilization: 50,
expectedUtilization: 28,
expectedValue: numContainersPerPod * 280,
expectedValue: 280,
},
container: "container2",
}
tc.runTest(t)
}
@ -967,7 +1164,7 @@ func TestReplicaCalcTolerance(t *testing.T) {
resource: &resourceInfo{
name: v1.ResourceCPU,
requests: []resource.Quantity{resource.MustParse("0.9"), resource.MustParse("1.0"), resource.MustParse("1.1")},
levels: []int64{1010, 1030, 1020},
levels: makePodMetricLevels(1010, 1030, 1020),
targetUtilization: 100,
expectedUtilization: 102,
@ -1070,7 +1267,7 @@ func TestReplicaCalcSuperfluousMetrics(t *testing.T) {
resource: &resourceInfo{
name: v1.ResourceCPU,
requests: []resource.Quantity{resource.MustParse("1.0"), resource.MustParse("1.0"), resource.MustParse("1.0"), resource.MustParse("1.0")},
levels: []int64{4000, 9500, 3000, 7000, 3200, 2000},
levels: makePodMetricLevels(4000, 9500, 3000, 7000, 3200, 2000),
targetUtilization: 100,
expectedUtilization: 587,
expectedValue: numContainersPerPod * 5875,
@ -1086,7 +1283,7 @@ func TestReplicaCalcMissingMetrics(t *testing.T) {
resource: &resourceInfo{
name: v1.ResourceCPU,
requests: []resource.Quantity{resource.MustParse("1.0"), resource.MustParse("1.0"), resource.MustParse("1.0"), resource.MustParse("1.0")},
levels: []int64{400, 95},
levels: makePodMetricLevels(400, 95),
targetUtilization: 100,
expectedUtilization: 24,
@ -1103,7 +1300,7 @@ func TestReplicaCalcEmptyMetrics(t *testing.T) {
resource: &resourceInfo{
name: v1.ResourceCPU,
requests: []resource.Quantity{resource.MustParse("1.0"), resource.MustParse("1.0"), resource.MustParse("1.0")},
levels: []int64{},
levels: makePodMetricLevels(),
targetUtilization: 100,
},
@ -1118,7 +1315,7 @@ func TestReplicaCalcEmptyCPURequest(t *testing.T) {
resource: &resourceInfo{
name: v1.ResourceCPU,
requests: []resource.Quantity{},
levels: []int64{200},
levels: makePodMetricLevels(200),
targetUtilization: 100,
},
@ -1133,7 +1330,7 @@ func TestReplicaCalcMissingMetricsNoChangeEq(t *testing.T) {
resource: &resourceInfo{
name: v1.ResourceCPU,
requests: []resource.Quantity{resource.MustParse("1.0"), resource.MustParse("1.0")},
levels: []int64{1000},
levels: makePodMetricLevels(1000),
targetUtilization: 100,
expectedUtilization: 100,
@ -1150,7 +1347,7 @@ func TestReplicaCalcMissingMetricsNoChangeGt(t *testing.T) {
resource: &resourceInfo{
name: v1.ResourceCPU,
requests: []resource.Quantity{resource.MustParse("1.0"), resource.MustParse("1.0")},
levels: []int64{1900},
levels: makePodMetricLevels(1900),
targetUtilization: 100,
expectedUtilization: 190,
@ -1167,7 +1364,7 @@ func TestReplicaCalcMissingMetricsNoChangeLt(t *testing.T) {
resource: &resourceInfo{
name: v1.ResourceCPU,
requests: []resource.Quantity{resource.MustParse("1.0"), resource.MustParse("1.0")},
levels: []int64{600},
levels: makePodMetricLevels(600),
targetUtilization: 100,
expectedUtilization: 60,
@ -1185,7 +1382,7 @@ func TestReplicaCalcMissingMetricsUnreadyChange(t *testing.T) {
resource: &resourceInfo{
name: v1.ResourceCPU,
requests: []resource.Quantity{resource.MustParse("1.0"), resource.MustParse("1.0"), resource.MustParse("1.0")},
levels: []int64{100, 450},
levels: makePodMetricLevels(100, 450),
targetUtilization: 50,
expectedUtilization: 45,
@ -1203,7 +1400,7 @@ func TestReplicaCalcMissingMetricsHotCpuNoChange(t *testing.T) {
resource: &resourceInfo{
name: v1.ResourceCPU,
requests: []resource.Quantity{resource.MustParse("1.0"), resource.MustParse("1.0"), resource.MustParse("1.0")},
levels: []int64{100, 450},
levels: makePodMetricLevels(100, 450),
targetUtilization: 50,
expectedUtilization: 45,
@ -1221,7 +1418,7 @@ func TestReplicaCalcMissingMetricsUnreadyScaleUp(t *testing.T) {
resource: &resourceInfo{
name: v1.ResourceCPU,
requests: []resource.Quantity{resource.MustParse("1.0"), resource.MustParse("1.0"), resource.MustParse("1.0")},
levels: []int64{100, 2000},
levels: makePodMetricLevels(100, 2000),
targetUtilization: 50,
expectedUtilization: 200,
@ -1240,7 +1437,7 @@ func TestReplicaCalcMissingMetricsHotCpuScaleUp(t *testing.T) {
resource: &resourceInfo{
name: v1.ResourceCPU,
requests: []resource.Quantity{resource.MustParse("1.0"), resource.MustParse("1.0"), resource.MustParse("1.0")},
levels: []int64{100, 2000},
levels: makePodMetricLevels(100, 2000),
targetUtilization: 50,
expectedUtilization: 200,
@ -1258,7 +1455,7 @@ func TestReplicaCalcMissingMetricsUnreadyScaleDown(t *testing.T) {
resource: &resourceInfo{
name: v1.ResourceCPU,
requests: []resource.Quantity{resource.MustParse("1.0"), resource.MustParse("1.0"), resource.MustParse("1.0"), resource.MustParse("1.0")},
levels: []int64{100, 100, 100},
levels: makePodMetricLevels(100, 100, 100),
targetUtilization: 50,
expectedUtilization: 10,
@ -1276,7 +1473,7 @@ func TestReplicaCalcDuringRollingUpdateWithMaxSurge(t *testing.T) {
resource: &resourceInfo{
name: v1.ResourceCPU,
requests: []resource.Quantity{resource.MustParse("1.0"), resource.MustParse("1.0"), resource.MustParse("1.0")},
levels: []int64{100, 100},
levels: makePodMetricLevels(100, 100),
targetUtilization: 50,
expectedUtilization: 10,
@ -1315,18 +1512,18 @@ func TestReplicaCalcComputedToleranceAlgImplementation(t *testing.T) {
expectedReplicas: finalPods,
resource: &resourceInfo{
name: v1.ResourceCPU,
levels: []int64{
totalUsedCPUOfAllPods / 10,
totalUsedCPUOfAllPods / 10,
totalUsedCPUOfAllPods / 10,
totalUsedCPUOfAllPods / 10,
totalUsedCPUOfAllPods / 10,
totalUsedCPUOfAllPods / 10,
totalUsedCPUOfAllPods / 10,
totalUsedCPUOfAllPods / 10,
totalUsedCPUOfAllPods / 10,
totalUsedCPUOfAllPods / 10,
},
levels: makePodMetricLevels(
totalUsedCPUOfAllPods/10,
totalUsedCPUOfAllPods/10,
totalUsedCPUOfAllPods/10,
totalUsedCPUOfAllPods/10,
totalUsedCPUOfAllPods/10,
totalUsedCPUOfAllPods/10,
totalUsedCPUOfAllPods/10,
totalUsedCPUOfAllPods/10,
totalUsedCPUOfAllPods/10,
totalUsedCPUOfAllPods/10,
),
requests: []resource.Quantity{
resource.MustParse(fmt.Sprint(perPodRequested+100) + "m"),
resource.MustParse(fmt.Sprint(perPodRequested-100) + "m"),

View File

@ -642,6 +642,13 @@ const (
//
// Disables Accelerator Metrics Collected by Kubelet
DisableAcceleratorUsageMetrics featuregate.Feature = "DisableAcceleratorUsageMetrics"
// owner: @arjunrn @mwielgus @josephburnett
// alpha: v1.20
//
// Add support for the HPA to scale based on metrics from individual containers
// in target pods
HPAContainerMetrics featuregate.Feature = "HPAContainerMetrics"
)
func init() {
@ -739,6 +746,7 @@ var defaultKubernetesFeatureGates = map[featuregate.Feature]featuregate.FeatureS
WinOverlay: {Default: true, PreRelease: featuregate.Beta},
WinDSR: {Default: false, PreRelease: featuregate.Alpha},
DisableAcceleratorUsageMetrics: {Default: true, PreRelease: featuregate.Beta},
HPAContainerMetrics: {Default: false, PreRelease: featuregate.Alpha},
// inherited features from generic apiserver, relisted here to get a conflict if it is changed
// unintentionally on either side:

View File

@ -2042,6 +2042,25 @@ func formatHPAMetrics(specs []autoscaling.MetricSpec, statuses []autoscaling.Met
}
list = append(list, fmt.Sprintf("%s/%s", current, target))
}
case autoscaling.ContainerResourceMetricSourceType:
if spec.ContainerResource.Target.AverageValue != nil {
current := "<unknown>"
if len(statuses) > i && statuses[i].ContainerResource != nil {
current = statuses[i].ContainerResource.Current.AverageValue.String()
}
list = append(list, fmt.Sprintf("%s/%s", current, spec.ContainerResource.Target.AverageValue.String()))
} else {
current := "<unknown>"
if len(statuses) > i && statuses[i].ContainerResource != nil && statuses[i].ContainerResource.Current.AverageUtilization != nil {
current = fmt.Sprintf("%d%%", *statuses[i].ContainerResource.Current.AverageUtilization)
}
target := "<auto>"
if spec.ContainerResource.Target.AverageUtilization != nil {
target = fmt.Sprintf("%d%%", *spec.ContainerResource.Target.AverageUtilization)
}
list = append(list, fmt.Sprintf("%s/%s", current, target))
}
default:
list = append(list, "<unknown type>")
}

View File

@ -2813,6 +2813,161 @@ func TestPrintHPA(t *testing.T) {
// Columns: Name, Reference, Targets, MinPods, MaxPods, Replicas, Age
expected: []metav1.TableRow{{Cells: []interface{}{"some-hpa", "ReplicationController/some-rc", "50%/80%", "2", int64(10), int64(4), "<unknown>"}}},
},
// container resource source type, targetVal (no current)
{
hpa: autoscaling.HorizontalPodAutoscaler{
ObjectMeta: metav1.ObjectMeta{Name: "some-hpa"},
Spec: autoscaling.HorizontalPodAutoscalerSpec{
ScaleTargetRef: autoscaling.CrossVersionObjectReference{
Name: "some-rc",
Kind: "ReplicationController",
},
MinReplicas: &minReplicasVal,
MaxReplicas: 10,
Metrics: []autoscaling.MetricSpec{
{
Type: autoscaling.ContainerResourceMetricSourceType,
ContainerResource: &autoscaling.ContainerResourceMetricSource{
Name: api.ResourceCPU,
Container: "application",
Target: autoscaling.MetricTarget{
Type: autoscaling.AverageValueMetricType,
AverageValue: resource.NewMilliQuantity(100, resource.DecimalSI),
},
},
},
},
},
Status: autoscaling.HorizontalPodAutoscalerStatus{
CurrentReplicas: 4,
DesiredReplicas: 5,
},
},
// Columns: Name, Reference, Targets, MinPods, MaxPods, Replicas, Age
expected: []metav1.TableRow{{Cells: []interface{}{"some-hpa", "ReplicationController/some-rc", "<unknown>/100m", "2", int64(10), int64(4), "<unknown>"}}},
},
// container resource source type, targetVal
{
hpa: autoscaling.HorizontalPodAutoscaler{
ObjectMeta: metav1.ObjectMeta{Name: "some-hpa"},
Spec: autoscaling.HorizontalPodAutoscalerSpec{
ScaleTargetRef: autoscaling.CrossVersionObjectReference{
Name: "some-rc",
Kind: "ReplicationController",
},
MinReplicas: &minReplicasVal,
MaxReplicas: 10,
Metrics: []autoscaling.MetricSpec{
{
Type: autoscaling.ContainerResourceMetricSourceType,
ContainerResource: &autoscaling.ContainerResourceMetricSource{
Name: api.ResourceCPU,
Target: autoscaling.MetricTarget{
Type: autoscaling.AverageValueMetricType,
AverageValue: resource.NewMilliQuantity(100, resource.DecimalSI),
},
},
},
},
},
Status: autoscaling.HorizontalPodAutoscalerStatus{
CurrentReplicas: 4,
DesiredReplicas: 5,
CurrentMetrics: []autoscaling.MetricStatus{
{
Type: autoscaling.ContainerResourceMetricSourceType,
ContainerResource: &autoscaling.ContainerResourceMetricStatus{
Name: api.ResourceCPU,
Container: "application",
Current: autoscaling.MetricValueStatus{
AverageValue: resource.NewMilliQuantity(50, resource.DecimalSI),
},
},
},
},
},
},
// Columns: Name, Reference, Targets, MinPods, MaxPods, Replicas, Age
expected: []metav1.TableRow{{Cells: []interface{}{"some-hpa", "ReplicationController/some-rc", "50m/100m", "2", int64(10), int64(4), "<unknown>"}}},
},
// container resource source type, targetUtil (no current)
{
hpa: autoscaling.HorizontalPodAutoscaler{
ObjectMeta: metav1.ObjectMeta{Name: "some-hpa"},
Spec: autoscaling.HorizontalPodAutoscalerSpec{
ScaleTargetRef: autoscaling.CrossVersionObjectReference{
Name: "some-rc",
Kind: "ReplicationController",
},
MinReplicas: &minReplicasVal,
MaxReplicas: 10,
Metrics: []autoscaling.MetricSpec{
{
Type: autoscaling.ContainerResourceMetricSourceType,
ContainerResource: &autoscaling.ContainerResourceMetricSource{
Name: api.ResourceCPU,
Container: "application",
Target: autoscaling.MetricTarget{
Type: autoscaling.UtilizationMetricType,
AverageUtilization: &targetUtilizationVal,
},
},
},
},
},
Status: autoscaling.HorizontalPodAutoscalerStatus{
CurrentReplicas: 4,
DesiredReplicas: 5,
},
},
// Columns: Name, Reference, Targets, MinPods, MaxPods, Replicas, Age
expected: []metav1.TableRow{{Cells: []interface{}{"some-hpa", "ReplicationController/some-rc", "<unknown>/80%", "2", int64(10), int64(4), "<unknown>"}}},
},
// container resource source type, targetUtil
{
hpa: autoscaling.HorizontalPodAutoscaler{
ObjectMeta: metav1.ObjectMeta{Name: "some-hpa"},
Spec: autoscaling.HorizontalPodAutoscalerSpec{
ScaleTargetRef: autoscaling.CrossVersionObjectReference{
Name: "some-rc",
Kind: "ReplicationController",
},
MinReplicas: &minReplicasVal,
MaxReplicas: 10,
Metrics: []autoscaling.MetricSpec{
{
Type: autoscaling.ContainerResourceMetricSourceType,
ContainerResource: &autoscaling.ContainerResourceMetricSource{
Name: api.ResourceCPU,
Target: autoscaling.MetricTarget{
Type: autoscaling.UtilizationMetricType,
AverageUtilization: &targetUtilizationVal,
},
},
},
},
},
Status: autoscaling.HorizontalPodAutoscalerStatus{
CurrentReplicas: 4,
DesiredReplicas: 5,
CurrentMetrics: []autoscaling.MetricStatus{
{
Type: autoscaling.ContainerResourceMetricSourceType,
ContainerResource: &autoscaling.ContainerResourceMetricStatus{
Name: api.ResourceCPU,
Container: "application",
Current: autoscaling.MetricValueStatus{
AverageUtilization: &currentUtilizationVal,
AverageValue: resource.NewMilliQuantity(40, resource.DecimalSI),
},
},
},
},
},
},
// Columns: Name, Reference, Targets, MinPods, MaxPods, Replicas, Age
expected: []metav1.TableRow{{Cells: []interface{}{"some-hpa", "ReplicationController/some-rc", "50%/80%", "2", int64(10), int64(4), "<unknown>"}}},
},
// multiple specs
{
hpa: autoscaling.HorizontalPodAutoscaler{

View File

@ -3,6 +3,7 @@ package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
"go_test",
)
go_library(
@ -16,9 +17,11 @@ go_library(
"//pkg/api/legacyscheme:go_default_library",
"//pkg/apis/autoscaling:go_default_library",
"//pkg/apis/autoscaling/validation:go_default_library",
"//pkg/features:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/util/validation/field:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/storage/names:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/util/feature:go_default_library",
],
)
@ -37,3 +40,17 @@ filegroup(
],
tags = ["automanaged"],
)
go_test(
name = "go_default_test",
srcs = ["strategy_test.go"],
embed = [":go_default_library"],
deps = [
"//pkg/apis/autoscaling:go_default_library",
"//pkg/apis/core:go_default_library",
"//pkg/features:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/util/feature:go_default_library",
"//staging/src/k8s.io/component-base/featuregate/testing:go_default_library",
"//vendor/k8s.io/utils/pointer:go_default_library",
],
)

View File

@ -22,9 +22,11 @@ import (
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/util/validation/field"
"k8s.io/apiserver/pkg/storage/names"
utilfeature "k8s.io/apiserver/pkg/util/feature"
"k8s.io/kubernetes/pkg/api/legacyscheme"
"k8s.io/kubernetes/pkg/apis/autoscaling"
"k8s.io/kubernetes/pkg/apis/autoscaling/validation"
"k8s.io/kubernetes/pkg/features"
)
// autoscalerStrategy implements behavior for HorizontalPodAutoscalers
@ -48,6 +50,10 @@ func (autoscalerStrategy) PrepareForCreate(ctx context.Context, obj runtime.Obje
// create cannot set status
newHPA.Status = autoscaling.HorizontalPodAutoscalerStatus{}
if !utilfeature.DefaultFeatureGate.Enabled(features.HPAContainerMetrics) {
dropContainerMetricSources(newHPA.Spec.Metrics)
}
}
// Validate validates a new autoscaler.
@ -69,10 +75,30 @@ func (autoscalerStrategy) AllowCreateOnUpdate() bool {
func (autoscalerStrategy) PrepareForUpdate(ctx context.Context, obj, old runtime.Object) {
newHPA := obj.(*autoscaling.HorizontalPodAutoscaler)
oldHPA := old.(*autoscaling.HorizontalPodAutoscaler)
if !utilfeature.DefaultFeatureGate.Enabled(features.HPAContainerMetrics) && !hasContainerMetricSources(oldHPA) {
dropContainerMetricSources(newHPA.Spec.Metrics)
}
// Update is not allowed to set status
newHPA.Status = oldHPA.Status
}
// dropContainerMetricSources ensures all container resource metric sources are nil
func dropContainerMetricSources(metrics []autoscaling.MetricSpec) {
for i := range metrics {
metrics[i].ContainerResource = nil
}
}
// hasContainerMetricSources returns true if the hpa has any container resource metric sources
func hasContainerMetricSources(hpa *autoscaling.HorizontalPodAutoscaler) bool {
for i := range hpa.Spec.Metrics {
if hpa.Spec.Metrics[i].ContainerResource != nil {
return true
}
}
return false
}
// ValidateUpdate is the default update validation for an end user.
func (autoscalerStrategy) ValidateUpdate(ctx context.Context, obj, old runtime.Object) field.ErrorList {
return validation.ValidateHorizontalPodAutoscalerUpdate(obj.(*autoscaling.HorizontalPodAutoscaler), old.(*autoscaling.HorizontalPodAutoscaler))

View File

@ -0,0 +1,110 @@
/*
Copyright 2015 The Kubernetes Authors.
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
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 horizontalpodautoscaler
import (
"context"
"testing"
utilfeature "k8s.io/apiserver/pkg/util/feature"
featuregatetesting "k8s.io/component-base/featuregate/testing"
"k8s.io/kubernetes/pkg/apis/autoscaling"
"k8s.io/kubernetes/pkg/apis/core"
"k8s.io/kubernetes/pkg/features"
"k8s.io/utils/pointer"
)
func makeTestContainerMetricsHPA(hasContainerMetric bool) *autoscaling.HorizontalPodAutoscaler {
testHPA := &autoscaling.HorizontalPodAutoscaler{
Spec: autoscaling.HorizontalPodAutoscalerSpec{
Metrics: []autoscaling.MetricSpec{},
},
}
if hasContainerMetric {
testHPA.Spec.Metrics = append(testHPA.Spec.Metrics, autoscaling.MetricSpec{
Type: autoscaling.ContainerResourceMetricSourceType,
ContainerResource: &autoscaling.ContainerResourceMetricSource{
Name: core.ResourceCPU,
Container: "test-container",
Target: autoscaling.MetricTarget{
Type: autoscaling.UtilizationMetricType,
AverageUtilization: pointer.Int32Ptr(30),
},
},
})
}
return testHPA
}
func TestCreateWithFeatureEnabled(t *testing.T) {
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.HPAContainerMetrics, true)()
testHPA := makeTestContainerMetricsHPA(true)
Strategy.PrepareForCreate(context.Background(), testHPA)
if testHPA.Spec.Metrics[0].ContainerResource == nil {
t.Errorf("container metrics was set to nil")
}
}
func TestCreateWithFeatureDisabled(t *testing.T) {
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.HPAContainerMetrics, false)()
testHPA := makeTestContainerMetricsHPA(true)
Strategy.PrepareForCreate(context.Background(), testHPA)
if testHPA.Spec.Metrics[0].ContainerResource != nil {
t.Errorf("container metrics is not nil")
}
}
func TestAutoscalerStatusStrategy_PrepareForUpdate(t *testing.T) {
for _, tc := range []struct {
name string
featureEnabled bool
old bool
expectedNew bool
}{
{
name: "feature disabled with existing container metrics",
featureEnabled: false,
old: true,
expectedNew: true,
},
{
name: "feature enabled with no container metrics",
featureEnabled: true,
old: false,
expectedNew: true,
},
{
name: "feature enabled with existing container metrics",
featureEnabled: true,
old: true,
expectedNew: true,
},
} {
t.Run(tc.name, func(t *testing.T) {
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.HPAContainerMetrics, tc.featureEnabled)()
oldHPA := makeTestContainerMetricsHPA(tc.old)
newHPA := makeTestContainerMetricsHPA(true)
Strategy.PrepareForUpdate(context.Background(), newHPA, oldHPA)
if tc.expectedNew && newHPA.Spec.Metrics[0].ContainerResource == nil {
t.Errorf("container metric source is nil")
}
if !tc.expectedNew && newHPA.Spec.Metrics[0].ContainerResource != nil {
t.Errorf("container metric source is not nil")
}
})
}
}

File diff suppressed because it is too large Load Diff

View File

@ -30,6 +30,60 @@ import "k8s.io/apimachinery/pkg/runtime/schema/generated.proto";
// Package-wide variables from generator "generated".
option go_package = "v1";
// ContainerResourceMetricSource indicates how to scale on a resource metric known to
// Kubernetes, as specified in the requests and limits, describing a single container in
// each of the pods of the current scale target(e.g. CPU or memory). The values will be
// averaged together before being compared to the target. Such metrics are built into
// Kubernetes, and have special scaling options on top of those available to
// normal per-pod metrics using the "pods" source. Only one "target" type
// should be set.
message ContainerResourceMetricSource {
// name is the name of the resource in question.
optional string name = 1;
// targetAverageUtilization is the target value of the average of the
// resource metric across all relevant pods, represented as a percentage of
// the requested value of the resource for the pods.
// +optional
optional int32 targetAverageUtilization = 2;
// targetAverageValue is the target value of the average of the
// resource metric across all relevant pods, as a raw value (instead of as
// a percentage of the request), similar to the "pods" metric source type.
// +optional
optional k8s.io.apimachinery.pkg.api.resource.Quantity targetAverageValue = 3;
// container is the name of the container in the pods of the scaling target.
optional string container = 5;
}
// ContainerResourceMetricStatus indicates the current value of a resource metric known to
// Kubernetes, as specified in requests and limits, describing a single container in each pod in the
// current scale target (e.g. CPU or memory). Such metrics are built in to
// Kubernetes, and have special scaling options on top of those available to
// normal per-pod metrics using the "pods" source.
message ContainerResourceMetricStatus {
// name is the name of the resource in question.
optional string name = 1;
// currentAverageUtilization is the current value of the average of the
// resource metric across all relevant pods, represented as a percentage of
// the requested value of the resource for the pods. It will only be
// present if `targetAverageValue` was set in the corresponding metric
// specification.
// +optional
optional int32 currentAverageUtilization = 2;
// currentAverageValue is the current value of the average of the
// resource metric across all relevant pods, as a raw value (instead of as
// a percentage of the request), similar to the "pods" metric source type.
// It will always be set, regardless of the corresponding metric specification.
optional k8s.io.apimachinery.pkg.api.resource.Quantity currentAverageValue = 3;
// container is the name of the container in the pods of the scaling taget
optional string container = 4;
}
// CrossVersionObjectReference contains enough information to let you identify the referred resource.
message CrossVersionObjectReference {
// Kind of the referent; More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds"
@ -184,8 +238,10 @@ message HorizontalPodAutoscalerStatus {
// MetricSpec specifies how to scale based on a single metric
// (only `type` and one other matching field should be set at once).
message MetricSpec {
// type is the type of metric source. It should be one of "Object",
// "Pods" or "Resource", each mapping to a matching field in the object.
// type is the type of metric source. It should be one of "ContainerResource",
// "External", "Object", "Pods" or "Resource", each mapping to a matching field in the object.
// Note: "ContainerResource" type is available on when the feature-gate
// HPAContainerMetrics is enabled
optional string type = 1;
// object refers to a metric describing a single kubernetes object
@ -207,6 +263,15 @@ message MetricSpec {
// +optional
optional ResourceMetricSource resource = 4;
// container resource refers to a resource metric (such as those specified in
// requests and limits) known to Kubernetes describing a single container in each pod of the
// current scale target (e.g. CPU or memory). Such metrics are built in to
// Kubernetes, and have special scaling options on top of those available
// to normal per-pod metrics using the "pods" source.
// This is an alpha feature and can be enabled by the HPAContainerMetrics feature flag.
// +optional
optional ContainerResourceMetricSource containerResource = 7;
// external refers to a global metric that is not associated
// with any Kubernetes object. It allows autoscaling based on information
// coming from components running outside of cluster
@ -218,8 +283,10 @@ message MetricSpec {
// MetricStatus describes the last-read state of a single metric.
message MetricStatus {
// type is the type of metric source. It will be one of "Object",
// "Pods" or "Resource", each corresponds to a matching field in the object.
// type is the type of metric source. It will be one of "ContainerResource",
// "External", "Object", "Pods" or "Resource", each corresponds to a matching field in the object.
// Note: "ContainerResource" type is available on when the feature-gate
// HPAContainerMetrics is enabled
optional string type = 1;
// object refers to a metric describing a single kubernetes object
@ -241,6 +308,14 @@ message MetricStatus {
// +optional
optional ResourceMetricStatus resource = 4;
// container resource refers to a resource metric (such as those specified in
// requests and limits) known to Kubernetes describing a single container in each pod in the
// current scale target (e.g. CPU or memory). Such metrics are built in to
// Kubernetes, and have special scaling options on top of those available
// to normal per-pod metrics using the "pods" source.
// +optional
optional ContainerResourceMetricStatus containerResource = 7;
// external refers to a global metric that is not associated
// with any Kubernetes object. It allows autoscaling based on information
// coming from components running outside of cluster

View File

@ -165,6 +165,12 @@ const (
// Kubernetes, and have special scaling options on top of those available
// to normal per-pod metrics (the "pods" source).
ResourceMetricSourceType MetricSourceType = "Resource"
// ContainerResourceMetricSourceType is a resource metric known to Kubernetes, as
// specified in requests and limits, describing a single container in each pod in the current
// scale target (e.g. CPU or memory). Such metrics are built in to
// Kubernetes, and have special scaling options on top of those available
// to normal per-pod metrics (the "pods" source).
ContainerResourceMetricSourceType MetricSourceType = "ContainerResource"
// ExternalMetricSourceType is a global metric that is not associated
// with any Kubernetes object. It allows autoscaling based on information
// coming from components running outside of cluster
@ -176,8 +182,10 @@ const (
// MetricSpec specifies how to scale based on a single metric
// (only `type` and one other matching field should be set at once).
type MetricSpec struct {
// type is the type of metric source. It should be one of "Object",
// "Pods" or "Resource", each mapping to a matching field in the object.
// type is the type of metric source. It should be one of "ContainerResource",
// "External", "Object", "Pods" or "Resource", each mapping to a matching field in the object.
// Note: "ContainerResource" type is available on when the feature-gate
// HPAContainerMetrics is enabled
Type MetricSourceType `json:"type" protobuf:"bytes,1,name=type"`
// object refers to a metric describing a single kubernetes object
@ -196,6 +204,14 @@ type MetricSpec struct {
// to normal per-pod metrics using the "pods" source.
// +optional
Resource *ResourceMetricSource `json:"resource,omitempty" protobuf:"bytes,4,opt,name=resource"`
// container resource refers to a resource metric (such as those specified in
// requests and limits) known to Kubernetes describing a single container in each pod of the
// current scale target (e.g. CPU or memory). Such metrics are built in to
// Kubernetes, and have special scaling options on top of those available
// to normal per-pod metrics using the "pods" source.
// This is an alpha feature and can be enabled by the HPAContainerMetrics feature flag.
// +optional
ContainerResource *ContainerResourceMetricSource `json:"containerResource,omitempty" protobuf:"bytes,7,opt,name=containerResource"`
// external refers to a global metric that is not associated
// with any Kubernetes object. It allows autoscaling based on information
// coming from components running outside of cluster
@ -267,6 +283,30 @@ type ResourceMetricSource struct {
TargetAverageValue *resource.Quantity `json:"targetAverageValue,omitempty" protobuf:"bytes,3,opt,name=targetAverageValue"`
}
// ContainerResourceMetricSource indicates how to scale on a resource metric known to
// Kubernetes, as specified in the requests and limits, describing a single container in
// each of the pods of the current scale target(e.g. CPU or memory). The values will be
// averaged together before being compared to the target. Such metrics are built into
// Kubernetes, and have special scaling options on top of those available to
// normal per-pod metrics using the "pods" source. Only one "target" type
// should be set.
type ContainerResourceMetricSource struct {
// name is the name of the resource in question.
Name v1.ResourceName `json:"name" protobuf:"bytes,1,name=name"`
// targetAverageUtilization is the target value of the average of the
// resource metric across all relevant pods, represented as a percentage of
// the requested value of the resource for the pods.
// +optional
TargetAverageUtilization *int32 `json:"targetAverageUtilization,omitempty" protobuf:"varint,2,opt,name=targetAverageUtilization"`
// targetAverageValue is the target value of the average of the
// resource metric across all relevant pods, as a raw value (instead of as
// a percentage of the request), similar to the "pods" metric source type.
// +optional
TargetAverageValue *resource.Quantity `json:"targetAverageValue,omitempty" protobuf:"bytes,3,opt,name=targetAverageValue"`
// container is the name of the container in the pods of the scaling target.
Container string `json:"container" protobuf:"bytes,5,opt,name=container"`
}
// ExternalMetricSource indicates how to scale on a metric not associated with
// any Kubernetes object (for example length of queue in cloud
// messaging service, or QPS from loadbalancer running outside of cluster).
@ -289,8 +329,10 @@ type ExternalMetricSource struct {
// MetricStatus describes the last-read state of a single metric.
type MetricStatus struct {
// type is the type of metric source. It will be one of "Object",
// "Pods" or "Resource", each corresponds to a matching field in the object.
// type is the type of metric source. It will be one of "ContainerResource",
// "External", "Object", "Pods" or "Resource", each corresponds to a matching field in the object.
// Note: "ContainerResource" type is available on when the feature-gate
// HPAContainerMetrics is enabled
Type MetricSourceType `json:"type" protobuf:"bytes,1,name=type"`
// object refers to a metric describing a single kubernetes object
@ -309,6 +351,13 @@ type MetricStatus struct {
// to normal per-pod metrics using the "pods" source.
// +optional
Resource *ResourceMetricStatus `json:"resource,omitempty" protobuf:"bytes,4,opt,name=resource"`
// container resource refers to a resource metric (such as those specified in
// requests and limits) known to Kubernetes describing a single container in each pod in the
// current scale target (e.g. CPU or memory). Such metrics are built in to
// Kubernetes, and have special scaling options on top of those available
// to normal per-pod metrics using the "pods" source.
// +optional
ContainerResource *ContainerResourceMetricStatus `json:"containerResource,omitempty" protobuf:"bytes,7,opt,name=containerResource"`
// external refers to a global metric that is not associated
// with any Kubernetes object. It allows autoscaling based on information
// coming from components running outside of cluster
@ -414,6 +463,30 @@ type ResourceMetricStatus struct {
CurrentAverageValue resource.Quantity `json:"currentAverageValue" protobuf:"bytes,3,name=currentAverageValue"`
}
// ContainerResourceMetricStatus indicates the current value of a resource metric known to
// Kubernetes, as specified in requests and limits, describing a single container in each pod in the
// current scale target (e.g. CPU or memory). Such metrics are built in to
// Kubernetes, and have special scaling options on top of those available to
// normal per-pod metrics using the "pods" source.
type ContainerResourceMetricStatus struct {
// name is the name of the resource in question.
Name v1.ResourceName `json:"name" protobuf:"bytes,1,name=name"`
// currentAverageUtilization is the current value of the average of the
// resource metric across all relevant pods, represented as a percentage of
// the requested value of the resource for the pods. It will only be
// present if `targetAverageValue` was set in the corresponding metric
// specification.
// +optional
CurrentAverageUtilization *int32 `json:"currentAverageUtilization,omitempty" protobuf:"bytes,2,opt,name=currentAverageUtilization"`
// currentAverageValue is the current value of the average of the
// resource metric across all relevant pods, as a raw value (instead of as
// a percentage of the request), similar to the "pods" metric source type.
// It will always be set, regardless of the corresponding metric specification.
CurrentAverageValue resource.Quantity `json:"currentAverageValue" protobuf:"bytes,3,name=currentAverageValue"`
// container is the name of the container in the pods of the scaling taget
Container string `json:"container" protobuf:"bytes,4,opt,name=container"`
}
// ExternalMetricStatus indicates the current value of a global metric
// not associated with any Kubernetes object.
type ExternalMetricStatus struct {

View File

@ -27,6 +27,30 @@ package v1
// Those methods can be generated by using hack/update-generated-swagger-docs.sh
// AUTO-GENERATED FUNCTIONS START HERE. DO NOT EDIT.
var map_ContainerResourceMetricSource = map[string]string{
"": "ContainerResourceMetricSource indicates how to scale on a resource metric known to Kubernetes, as specified in the requests and limits, describing a single container in each of the pods of the current scale target(e.g. CPU or memory). The values will be averaged together before being compared to the target. Such metrics are built into Kubernetes, and have special scaling options on top of those available to normal per-pod metrics using the \"pods\" source. Only one \"target\" type should be set.",
"name": "name is the name of the resource in question.",
"targetAverageUtilization": "targetAverageUtilization is the target value of the average of the resource metric across all relevant pods, represented as a percentage of the requested value of the resource for the pods.",
"targetAverageValue": "targetAverageValue is the target value of the average of the resource metric across all relevant pods, as a raw value (instead of as a percentage of the request), similar to the \"pods\" metric source type.",
"container": "container is the name of the container in the pods of the scaling target.",
}
func (ContainerResourceMetricSource) SwaggerDoc() map[string]string {
return map_ContainerResourceMetricSource
}
var map_ContainerResourceMetricStatus = map[string]string{
"": "ContainerResourceMetricStatus indicates the current value of a resource metric known to Kubernetes, as specified in requests and limits, describing a single container in each pod in the current scale target (e.g. CPU or memory). Such metrics are built in to Kubernetes, and have special scaling options on top of those available to normal per-pod metrics using the \"pods\" source.",
"name": "name is the name of the resource in question.",
"currentAverageUtilization": "currentAverageUtilization is the current value of the average of the resource metric across all relevant pods, represented as a percentage of the requested value of the resource for the pods. It will only be present if `targetAverageValue` was set in the corresponding metric specification.",
"currentAverageValue": "currentAverageValue is the current value of the average of the resource metric across all relevant pods, as a raw value (instead of as a percentage of the request), similar to the \"pods\" metric source type. It will always be set, regardless of the corresponding metric specification.",
"container": "container is the name of the container in the pods of the scaling taget",
}
func (ContainerResourceMetricStatus) SwaggerDoc() map[string]string {
return map_ContainerResourceMetricStatus
}
var map_CrossVersionObjectReference = map[string]string{
"": "CrossVersionObjectReference contains enough information to let you identify the referred resource.",
"kind": "Kind of the referent; More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds\"",
@ -122,12 +146,13 @@ func (HorizontalPodAutoscalerStatus) SwaggerDoc() map[string]string {
}
var map_MetricSpec = map[string]string{
"": "MetricSpec specifies how to scale based on a single metric (only `type` and one other matching field should be set at once).",
"type": "type is the type of metric source. It should be one of \"Object\", \"Pods\" or \"Resource\", each mapping to a matching field in the object.",
"object": "object refers to a metric describing a single kubernetes object (for example, hits-per-second on an Ingress object).",
"pods": "pods refers to a metric describing each pod in the current scale target (for example, transactions-processed-per-second). The values will be averaged together before being compared to the target value.",
"resource": "resource refers to a resource metric (such as those specified in requests and limits) known to Kubernetes describing each pod in the current scale target (e.g. CPU or memory). Such metrics are built in to Kubernetes, and have special scaling options on top of those available to normal per-pod metrics using the \"pods\" source.",
"external": "external refers to a global metric that is not associated with any Kubernetes object. It allows autoscaling based on information coming from components running outside of cluster (for example length of queue in cloud messaging service, or QPS from loadbalancer running outside of cluster).",
"": "MetricSpec specifies how to scale based on a single metric (only `type` and one other matching field should be set at once).",
"type": "type is the type of metric source. It should be one of \"ContainerResource\", \"External\", \"Object\", \"Pods\" or \"Resource\", each mapping to a matching field in the object. Note: \"ContainerResource\" type is available on when the feature-gate HPAContainerMetrics is enabled",
"object": "object refers to a metric describing a single kubernetes object (for example, hits-per-second on an Ingress object).",
"pods": "pods refers to a metric describing each pod in the current scale target (for example, transactions-processed-per-second). The values will be averaged together before being compared to the target value.",
"resource": "resource refers to a resource metric (such as those specified in requests and limits) known to Kubernetes describing each pod in the current scale target (e.g. CPU or memory). Such metrics are built in to Kubernetes, and have special scaling options on top of those available to normal per-pod metrics using the \"pods\" source.",
"containerResource": "container resource refers to a resource metric (such as those specified in requests and limits) known to Kubernetes describing a single container in each pod of the current scale target (e.g. CPU or memory). Such metrics are built in to Kubernetes, and have special scaling options on top of those available to normal per-pod metrics using the \"pods\" source. This is an alpha feature and can be enabled by the HPAContainerMetrics feature flag.",
"external": "external refers to a global metric that is not associated with any Kubernetes object. It allows autoscaling based on information coming from components running outside of cluster (for example length of queue in cloud messaging service, or QPS from loadbalancer running outside of cluster).",
}
func (MetricSpec) SwaggerDoc() map[string]string {
@ -135,12 +160,13 @@ func (MetricSpec) SwaggerDoc() map[string]string {
}
var map_MetricStatus = map[string]string{
"": "MetricStatus describes the last-read state of a single metric.",
"type": "type is the type of metric source. It will be one of \"Object\", \"Pods\" or \"Resource\", each corresponds to a matching field in the object.",
"object": "object refers to a metric describing a single kubernetes object (for example, hits-per-second on an Ingress object).",
"pods": "pods refers to a metric describing each pod in the current scale target (for example, transactions-processed-per-second). The values will be averaged together before being compared to the target value.",
"resource": "resource refers to a resource metric (such as those specified in requests and limits) known to Kubernetes describing each pod in the current scale target (e.g. CPU or memory). Such metrics are built in to Kubernetes, and have special scaling options on top of those available to normal per-pod metrics using the \"pods\" source.",
"external": "external refers to a global metric that is not associated with any Kubernetes object. It allows autoscaling based on information coming from components running outside of cluster (for example length of queue in cloud messaging service, or QPS from loadbalancer running outside of cluster).",
"": "MetricStatus describes the last-read state of a single metric.",
"type": "type is the type of metric source. It will be one of \"ContainerResource\", \"External\", \"Object\", \"Pods\" or \"Resource\", each corresponds to a matching field in the object. Note: \"ContainerResource\" type is available on when the feature-gate HPAContainerMetrics is enabled",
"object": "object refers to a metric describing a single kubernetes object (for example, hits-per-second on an Ingress object).",
"pods": "pods refers to a metric describing each pod in the current scale target (for example, transactions-processed-per-second). The values will be averaged together before being compared to the target value.",
"resource": "resource refers to a resource metric (such as those specified in requests and limits) known to Kubernetes describing each pod in the current scale target (e.g. CPU or memory). Such metrics are built in to Kubernetes, and have special scaling options on top of those available to normal per-pod metrics using the \"pods\" source.",
"containerResource": "container resource refers to a resource metric (such as those specified in requests and limits) known to Kubernetes describing a single container in each pod in the current scale target (e.g. CPU or memory). Such metrics are built in to Kubernetes, and have special scaling options on top of those available to normal per-pod metrics using the \"pods\" source.",
"external": "external refers to a global metric that is not associated with any Kubernetes object. It allows autoscaling based on information coming from components running outside of cluster (for example length of queue in cloud messaging service, or QPS from loadbalancer running outside of cluster).",
}
func (MetricStatus) SwaggerDoc() map[string]string {

View File

@ -25,6 +25,54 @@ import (
runtime "k8s.io/apimachinery/pkg/runtime"
)
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ContainerResourceMetricSource) DeepCopyInto(out *ContainerResourceMetricSource) {
*out = *in
if in.TargetAverageUtilization != nil {
in, out := &in.TargetAverageUtilization, &out.TargetAverageUtilization
*out = new(int32)
**out = **in
}
if in.TargetAverageValue != nil {
in, out := &in.TargetAverageValue, &out.TargetAverageValue
x := (*in).DeepCopy()
*out = &x
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ContainerResourceMetricSource.
func (in *ContainerResourceMetricSource) DeepCopy() *ContainerResourceMetricSource {
if in == nil {
return nil
}
out := new(ContainerResourceMetricSource)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ContainerResourceMetricStatus) DeepCopyInto(out *ContainerResourceMetricStatus) {
*out = *in
if in.CurrentAverageUtilization != nil {
in, out := &in.CurrentAverageUtilization, &out.CurrentAverageUtilization
*out = new(int32)
**out = **in
}
out.CurrentAverageValue = in.CurrentAverageValue.DeepCopy()
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ContainerResourceMetricStatus.
func (in *ContainerResourceMetricStatus) DeepCopy() *ContainerResourceMetricStatus {
if in == nil {
return nil
}
out := new(ContainerResourceMetricStatus)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *CrossVersionObjectReference) DeepCopyInto(out *CrossVersionObjectReference) {
*out = *in
@ -252,6 +300,11 @@ func (in *MetricSpec) DeepCopyInto(out *MetricSpec) {
*out = new(ResourceMetricSource)
(*in).DeepCopyInto(*out)
}
if in.ContainerResource != nil {
in, out := &in.ContainerResource, &out.ContainerResource
*out = new(ContainerResourceMetricSource)
(*in).DeepCopyInto(*out)
}
if in.External != nil {
in, out := &in.External, &out.External
*out = new(ExternalMetricSource)
@ -288,6 +341,11 @@ func (in *MetricStatus) DeepCopyInto(out *MetricStatus) {
*out = new(ResourceMetricStatus)
(*in).DeepCopyInto(*out)
}
if in.ContainerResource != nil {
in, out := &in.ContainerResource, &out.ContainerResource
*out = new(ContainerResourceMetricStatus)
(*in).DeepCopyInto(*out)
}
if in.External != nil {
in, out := &in.External, &out.External
*out = new(ExternalMetricStatus)

File diff suppressed because it is too large Load Diff

View File

@ -30,6 +30,60 @@ import "k8s.io/apimachinery/pkg/runtime/schema/generated.proto";
// Package-wide variables from generator "generated".
option go_package = "v2beta1";
// ContainerResourceMetricSource indicates how to scale on a resource metric known to
// Kubernetes, as specified in requests and limits, describing each pod in the
// current scale target (e.g. CPU or memory). The values will be averaged
// together before being compared to the target. Such metrics are built in to
// Kubernetes, and have special scaling options on top of those available to
// normal per-pod metrics using the "pods" source. Only one "target" type
// should be set.
message ContainerResourceMetricSource {
// name is the name of the resource in question.
optional string name = 1;
// targetAverageUtilization is the target value of the average of the
// resource metric across all relevant pods, represented as a percentage of
// the requested value of the resource for the pods.
// +optional
optional int32 targetAverageUtilization = 2;
// targetAverageValue is the target value of the average of the
// resource metric across all relevant pods, as a raw value (instead of as
// a percentage of the request), similar to the "pods" metric source type.
// +optional
optional k8s.io.apimachinery.pkg.api.resource.Quantity targetAverageValue = 3;
// container is the name of the container in the pods of the scaling target
optional string container = 4;
}
// ContainerResourceMetricStatus indicates the current value of a resource metric known to
// Kubernetes, as specified in requests and limits, describing a single container in each pod in the
// current scale target (e.g. CPU or memory). Such metrics are built in to
// Kubernetes, and have special scaling options on top of those available to
// normal per-pod metrics using the "pods" source.
message ContainerResourceMetricStatus {
// name is the name of the resource in question.
optional string name = 1;
// currentAverageUtilization is the current value of the average of the
// resource metric across all relevant pods, represented as a percentage of
// the requested value of the resource for the pods. It will only be
// present if `targetAverageValue` was set in the corresponding metric
// specification.
// +optional
optional int32 currentAverageUtilization = 2;
// currentAverageValue is the current value of the average of the
// resource metric across all relevant pods, as a raw value (instead of as
// a percentage of the request), similar to the "pods" metric source type.
// It will always be set, regardless of the corresponding metric specification.
optional k8s.io.apimachinery.pkg.api.resource.Quantity currentAverageValue = 3;
// container is the name of the container in the pods of the scaling target
optional string container = 4;
}
// CrossVersionObjectReference contains enough information to let you identify the referred resource.
message CrossVersionObjectReference {
// Kind of the referent; More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds"
@ -200,8 +254,10 @@ message HorizontalPodAutoscalerStatus {
// MetricSpec specifies how to scale based on a single metric
// (only `type` and one other matching field should be set at once).
message MetricSpec {
// type is the type of metric source. It should be one of "Object",
// "Pods", "Resource" or "External", each mapping to a matching field in the object.
// type is the type of metric source. It should be one of "ContainerResource",
// "External", "Object", "Pods" or "Resource", each mapping to a matching field in the object.
// Note: "ContainerResource" type is available on when the feature-gate
// HPAContainerMetrics is enabled
optional string type = 1;
// object refers to a metric describing a single kubernetes object
@ -223,6 +279,15 @@ message MetricSpec {
// +optional
optional ResourceMetricSource resource = 4;
// container resource refers to a resource metric (such as those specified in
// requests and limits) known to Kubernetes describing a single container in
// each pod of the current scale target (e.g. CPU or memory). Such metrics are
// built in to Kubernetes, and have special scaling options on top of those
// available to normal per-pod metrics using the "pods" source.
// This is an alpha feature and can be enabled by the HPAContainerMetrics feature flag.
// +optional
optional ContainerResourceMetricSource containerResource = 7;
// external refers to a global metric that is not associated
// with any Kubernetes object. It allows autoscaling based on information
// coming from components running outside of cluster
@ -234,8 +299,10 @@ message MetricSpec {
// MetricStatus describes the last-read state of a single metric.
message MetricStatus {
// type is the type of metric source. It will be one of "Object",
// "Pods" or "Resource", each corresponds to a matching field in the object.
// type is the type of metric source. It will be one of "ContainerResource",
// "External", "Object", "Pods" or "Resource", each corresponds to a matching field in the object.
// Note: "ContainerResource" type is available on when the feature-gate
// HPAContainerMetrics is enabled
optional string type = 1;
// object refers to a metric describing a single kubernetes object
@ -257,6 +324,14 @@ message MetricStatus {
// +optional
optional ResourceMetricStatus resource = 4;
// container resource refers to a resource metric (such as those specified in
// requests and limits) known to Kubernetes describing a single container in each pod in the
// current scale target (e.g. CPU or memory). Such metrics are built in to
// Kubernetes, and have special scaling options on top of those available
// to normal per-pod metrics using the "pods" source.
// +optional
optional ContainerResourceMetricStatus containerResource = 7;
// external refers to a global metric that is not associated
// with any Kubernetes object. It allows autoscaling based on information
// coming from components running outside of cluster

View File

@ -76,6 +76,12 @@ const (
// Kubernetes, and have special scaling options on top of those available
// to normal per-pod metrics (the "pods" source).
ResourceMetricSourceType MetricSourceType = "Resource"
// ContainerResourceMetricSourceType is a resource metric known to Kubernetes, as
// specified in requests and limits, describing a single container in each pod in the current
// scale target (e.g. CPU or memory). Such metrics are built in to
// Kubernetes, and have special scaling options on top of those available
// to normal per-pod metrics (the "pods" source).
ContainerResourceMetricSourceType MetricSourceType = "ContainerResource"
// ExternalMetricSourceType is a global metric that is not associated
// with any Kubernetes object. It allows autoscaling based on information
// coming from components running outside of cluster
@ -87,8 +93,10 @@ const (
// MetricSpec specifies how to scale based on a single metric
// (only `type` and one other matching field should be set at once).
type MetricSpec struct {
// type is the type of metric source. It should be one of "Object",
// "Pods", "Resource" or "External", each mapping to a matching field in the object.
// type is the type of metric source. It should be one of "ContainerResource",
// "External", "Object", "Pods" or "Resource", each mapping to a matching field in the object.
// Note: "ContainerResource" type is available on when the feature-gate
// HPAContainerMetrics is enabled
Type MetricSourceType `json:"type" protobuf:"bytes,1,name=type"`
// object refers to a metric describing a single kubernetes object
@ -107,6 +115,14 @@ type MetricSpec struct {
// to normal per-pod metrics using the "pods" source.
// +optional
Resource *ResourceMetricSource `json:"resource,omitempty" protobuf:"bytes,4,opt,name=resource"`
// container resource refers to a resource metric (such as those specified in
// requests and limits) known to Kubernetes describing a single container in
// each pod of the current scale target (e.g. CPU or memory). Such metrics are
// built in to Kubernetes, and have special scaling options on top of those
// available to normal per-pod metrics using the "pods" source.
// This is an alpha feature and can be enabled by the HPAContainerMetrics feature flag.
// +optional
ContainerResource *ContainerResourceMetricSource `json:"containerResource,omitempty" protobuf:"bytes,7,opt,name=containerResource"`
// external refers to a global metric that is not associated
// with any Kubernetes object. It allows autoscaling based on information
// coming from components running outside of cluster
@ -178,6 +194,30 @@ type ResourceMetricSource struct {
TargetAverageValue *resource.Quantity `json:"targetAverageValue,omitempty" protobuf:"bytes,3,opt,name=targetAverageValue"`
}
// ContainerResourceMetricSource indicates how to scale on a resource metric known to
// Kubernetes, as specified in requests and limits, describing each pod in the
// current scale target (e.g. CPU or memory). The values will be averaged
// together before being compared to the target. Such metrics are built in to
// Kubernetes, and have special scaling options on top of those available to
// normal per-pod metrics using the "pods" source. Only one "target" type
// should be set.
type ContainerResourceMetricSource struct {
// name is the name of the resource in question.
Name v1.ResourceName `json:"name" protobuf:"bytes,1,name=name"`
// targetAverageUtilization is the target value of the average of the
// resource metric across all relevant pods, represented as a percentage of
// the requested value of the resource for the pods.
// +optional
TargetAverageUtilization *int32 `json:"targetAverageUtilization,omitempty" protobuf:"varint,2,opt,name=targetAverageUtilization"`
// targetAverageValue is the target value of the average of the
// resource metric across all relevant pods, as a raw value (instead of as
// a percentage of the request), similar to the "pods" metric source type.
// +optional
TargetAverageValue *resource.Quantity `json:"targetAverageValue,omitempty" protobuf:"bytes,3,opt,name=targetAverageValue"`
// container is the name of the container in the pods of the scaling target
Container string `json:"container" protobuf:"bytes,4,opt,name=container"`
}
// ExternalMetricSource indicates how to scale on a metric not associated with
// any Kubernetes object (for example length of queue in cloud
// messaging service, or QPS from loadbalancer running outside of cluster).
@ -265,8 +305,10 @@ type HorizontalPodAutoscalerCondition struct {
// MetricStatus describes the last-read state of a single metric.
type MetricStatus struct {
// type is the type of metric source. It will be one of "Object",
// "Pods" or "Resource", each corresponds to a matching field in the object.
// type is the type of metric source. It will be one of "ContainerResource",
// "External", "Object", "Pods" or "Resource", each corresponds to a matching field in the object.
// Note: "ContainerResource" type is available on when the feature-gate
// HPAContainerMetrics is enabled
Type MetricSourceType `json:"type" protobuf:"bytes,1,name=type"`
// object refers to a metric describing a single kubernetes object
@ -285,6 +327,13 @@ type MetricStatus struct {
// to normal per-pod metrics using the "pods" source.
// +optional
Resource *ResourceMetricStatus `json:"resource,omitempty" protobuf:"bytes,4,opt,name=resource"`
// container resource refers to a resource metric (such as those specified in
// requests and limits) known to Kubernetes describing a single container in each pod in the
// current scale target (e.g. CPU or memory). Such metrics are built in to
// Kubernetes, and have special scaling options on top of those available
// to normal per-pod metrics using the "pods" source.
// +optional
ContainerResource *ContainerResourceMetricStatus `json:"containerResource,omitempty" protobuf:"bytes,7,opt,name=containerResource"`
// external refers to a global metric that is not associated
// with any Kubernetes object. It allows autoscaling based on information
// coming from components running outside of cluster
@ -354,6 +403,30 @@ type ResourceMetricStatus struct {
CurrentAverageValue resource.Quantity `json:"currentAverageValue" protobuf:"bytes,3,name=currentAverageValue"`
}
// ContainerResourceMetricStatus indicates the current value of a resource metric known to
// Kubernetes, as specified in requests and limits, describing a single container in each pod in the
// current scale target (e.g. CPU or memory). Such metrics are built in to
// Kubernetes, and have special scaling options on top of those available to
// normal per-pod metrics using the "pods" source.
type ContainerResourceMetricStatus struct {
// name is the name of the resource in question.
Name v1.ResourceName `json:"name" protobuf:"bytes,1,name=name"`
// currentAverageUtilization is the current value of the average of the
// resource metric across all relevant pods, represented as a percentage of
// the requested value of the resource for the pods. It will only be
// present if `targetAverageValue` was set in the corresponding metric
// specification.
// +optional
CurrentAverageUtilization *int32 `json:"currentAverageUtilization,omitempty" protobuf:"bytes,2,opt,name=currentAverageUtilization"`
// currentAverageValue is the current value of the average of the
// resource metric across all relevant pods, as a raw value (instead of as
// a percentage of the request), similar to the "pods" metric source type.
// It will always be set, regardless of the corresponding metric specification.
CurrentAverageValue resource.Quantity `json:"currentAverageValue" protobuf:"bytes,3,name=currentAverageValue"`
// container is the name of the container in the pods of the scaling target
Container string `json:"container" protobuf:"bytes,4,opt,name=container"`
}
// ExternalMetricStatus indicates the current value of a global metric
// not associated with any Kubernetes object.
type ExternalMetricStatus struct {

View File

@ -27,6 +27,30 @@ package v2beta1
// Those methods can be generated by using hack/update-generated-swagger-docs.sh
// AUTO-GENERATED FUNCTIONS START HERE. DO NOT EDIT.
var map_ContainerResourceMetricSource = map[string]string{
"": "ContainerResourceMetricSource indicates how to scale on a resource metric known to Kubernetes, as specified in requests and limits, describing each pod in the current scale target (e.g. CPU or memory). The values will be averaged together before being compared to the target. Such metrics are built in to Kubernetes, and have special scaling options on top of those available to normal per-pod metrics using the \"pods\" source. Only one \"target\" type should be set.",
"name": "name is the name of the resource in question.",
"targetAverageUtilization": "targetAverageUtilization is the target value of the average of the resource metric across all relevant pods, represented as a percentage of the requested value of the resource for the pods.",
"targetAverageValue": "targetAverageValue is the target value of the average of the resource metric across all relevant pods, as a raw value (instead of as a percentage of the request), similar to the \"pods\" metric source type.",
"container": "container is the name of the container in the pods of the scaling target",
}
func (ContainerResourceMetricSource) SwaggerDoc() map[string]string {
return map_ContainerResourceMetricSource
}
var map_ContainerResourceMetricStatus = map[string]string{
"": "ContainerResourceMetricStatus indicates the current value of a resource metric known to Kubernetes, as specified in requests and limits, describing a single container in each pod in the current scale target (e.g. CPU or memory). Such metrics are built in to Kubernetes, and have special scaling options on top of those available to normal per-pod metrics using the \"pods\" source.",
"name": "name is the name of the resource in question.",
"currentAverageUtilization": "currentAverageUtilization is the current value of the average of the resource metric across all relevant pods, represented as a percentage of the requested value of the resource for the pods. It will only be present if `targetAverageValue` was set in the corresponding metric specification.",
"currentAverageValue": "currentAverageValue is the current value of the average of the resource metric across all relevant pods, as a raw value (instead of as a percentage of the request), similar to the \"pods\" metric source type. It will always be set, regardless of the corresponding metric specification.",
"container": "container is the name of the container in the pods of the scaling target",
}
func (ContainerResourceMetricStatus) SwaggerDoc() map[string]string {
return map_ContainerResourceMetricStatus
}
var map_CrossVersionObjectReference = map[string]string{
"": "CrossVersionObjectReference contains enough information to let you identify the referred resource.",
"kind": "Kind of the referent; More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds\"",
@ -123,12 +147,13 @@ func (HorizontalPodAutoscalerStatus) SwaggerDoc() map[string]string {
}
var map_MetricSpec = map[string]string{
"": "MetricSpec specifies how to scale based on a single metric (only `type` and one other matching field should be set at once).",
"type": "type is the type of metric source. It should be one of \"Object\", \"Pods\", \"Resource\" or \"External\", each mapping to a matching field in the object.",
"object": "object refers to a metric describing a single kubernetes object (for example, hits-per-second on an Ingress object).",
"pods": "pods refers to a metric describing each pod in the current scale target (for example, transactions-processed-per-second). The values will be averaged together before being compared to the target value.",
"resource": "resource refers to a resource metric (such as those specified in requests and limits) known to Kubernetes describing each pod in the current scale target (e.g. CPU or memory). Such metrics are built in to Kubernetes, and have special scaling options on top of those available to normal per-pod metrics using the \"pods\" source.",
"external": "external refers to a global metric that is not associated with any Kubernetes object. It allows autoscaling based on information coming from components running outside of cluster (for example length of queue in cloud messaging service, or QPS from loadbalancer running outside of cluster).",
"": "MetricSpec specifies how to scale based on a single metric (only `type` and one other matching field should be set at once).",
"type": "type is the type of metric source. It should be one of \"ContainerResource\", \"External\", \"Object\", \"Pods\" or \"Resource\", each mapping to a matching field in the object. Note: \"ContainerResource\" type is available on when the feature-gate HPAContainerMetrics is enabled",
"object": "object refers to a metric describing a single kubernetes object (for example, hits-per-second on an Ingress object).",
"pods": "pods refers to a metric describing each pod in the current scale target (for example, transactions-processed-per-second). The values will be averaged together before being compared to the target value.",
"resource": "resource refers to a resource metric (such as those specified in requests and limits) known to Kubernetes describing each pod in the current scale target (e.g. CPU or memory). Such metrics are built in to Kubernetes, and have special scaling options on top of those available to normal per-pod metrics using the \"pods\" source.",
"containerResource": "container resource refers to a resource metric (such as those specified in requests and limits) known to Kubernetes describing a single container in each pod of the current scale target (e.g. CPU or memory). Such metrics are built in to Kubernetes, and have special scaling options on top of those available to normal per-pod metrics using the \"pods\" source. This is an alpha feature and can be enabled by the HPAContainerMetrics feature flag.",
"external": "external refers to a global metric that is not associated with any Kubernetes object. It allows autoscaling based on information coming from components running outside of cluster (for example length of queue in cloud messaging service, or QPS from loadbalancer running outside of cluster).",
}
func (MetricSpec) SwaggerDoc() map[string]string {
@ -136,12 +161,13 @@ func (MetricSpec) SwaggerDoc() map[string]string {
}
var map_MetricStatus = map[string]string{
"": "MetricStatus describes the last-read state of a single metric.",
"type": "type is the type of metric source. It will be one of \"Object\", \"Pods\" or \"Resource\", each corresponds to a matching field in the object.",
"object": "object refers to a metric describing a single kubernetes object (for example, hits-per-second on an Ingress object).",
"pods": "pods refers to a metric describing each pod in the current scale target (for example, transactions-processed-per-second). The values will be averaged together before being compared to the target value.",
"resource": "resource refers to a resource metric (such as those specified in requests and limits) known to Kubernetes describing each pod in the current scale target (e.g. CPU or memory). Such metrics are built in to Kubernetes, and have special scaling options on top of those available to normal per-pod metrics using the \"pods\" source.",
"external": "external refers to a global metric that is not associated with any Kubernetes object. It allows autoscaling based on information coming from components running outside of cluster (for example length of queue in cloud messaging service, or QPS from loadbalancer running outside of cluster).",
"": "MetricStatus describes the last-read state of a single metric.",
"type": "type is the type of metric source. It will be one of \"ContainerResource\", \"External\", \"Object\", \"Pods\" or \"Resource\", each corresponds to a matching field in the object. Note: \"ContainerResource\" type is available on when the feature-gate HPAContainerMetrics is enabled",
"object": "object refers to a metric describing a single kubernetes object (for example, hits-per-second on an Ingress object).",
"pods": "pods refers to a metric describing each pod in the current scale target (for example, transactions-processed-per-second). The values will be averaged together before being compared to the target value.",
"resource": "resource refers to a resource metric (such as those specified in requests and limits) known to Kubernetes describing each pod in the current scale target (e.g. CPU or memory). Such metrics are built in to Kubernetes, and have special scaling options on top of those available to normal per-pod metrics using the \"pods\" source.",
"containerResource": "container resource refers to a resource metric (such as those specified in requests and limits) known to Kubernetes describing a single container in each pod in the current scale target (e.g. CPU or memory). Such metrics are built in to Kubernetes, and have special scaling options on top of those available to normal per-pod metrics using the \"pods\" source.",
"external": "external refers to a global metric that is not associated with any Kubernetes object. It allows autoscaling based on information coming from components running outside of cluster (for example length of queue in cloud messaging service, or QPS from loadbalancer running outside of cluster).",
}
func (MetricStatus) SwaggerDoc() map[string]string {

View File

@ -25,6 +25,54 @@ import (
runtime "k8s.io/apimachinery/pkg/runtime"
)
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ContainerResourceMetricSource) DeepCopyInto(out *ContainerResourceMetricSource) {
*out = *in
if in.TargetAverageUtilization != nil {
in, out := &in.TargetAverageUtilization, &out.TargetAverageUtilization
*out = new(int32)
**out = **in
}
if in.TargetAverageValue != nil {
in, out := &in.TargetAverageValue, &out.TargetAverageValue
x := (*in).DeepCopy()
*out = &x
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ContainerResourceMetricSource.
func (in *ContainerResourceMetricSource) DeepCopy() *ContainerResourceMetricSource {
if in == nil {
return nil
}
out := new(ContainerResourceMetricSource)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ContainerResourceMetricStatus) DeepCopyInto(out *ContainerResourceMetricStatus) {
*out = *in
if in.CurrentAverageUtilization != nil {
in, out := &in.CurrentAverageUtilization, &out.CurrentAverageUtilization
*out = new(int32)
**out = **in
}
out.CurrentAverageValue = in.CurrentAverageValue.DeepCopy()
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ContainerResourceMetricStatus.
func (in *ContainerResourceMetricStatus) DeepCopy() *ContainerResourceMetricStatus {
if in == nil {
return nil
}
out := new(ContainerResourceMetricStatus)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *CrossVersionObjectReference) DeepCopyInto(out *CrossVersionObjectReference) {
*out = *in
@ -263,6 +311,11 @@ func (in *MetricSpec) DeepCopyInto(out *MetricSpec) {
*out = new(ResourceMetricSource)
(*in).DeepCopyInto(*out)
}
if in.ContainerResource != nil {
in, out := &in.ContainerResource, &out.ContainerResource
*out = new(ContainerResourceMetricSource)
(*in).DeepCopyInto(*out)
}
if in.External != nil {
in, out := &in.External, &out.External
*out = new(ExternalMetricSource)
@ -299,6 +352,11 @@ func (in *MetricStatus) DeepCopyInto(out *MetricStatus) {
*out = new(ResourceMetricStatus)
(*in).DeepCopyInto(*out)
}
if in.ContainerResource != nil {
in, out := &in.ContainerResource, &out.ContainerResource
*out = new(ContainerResourceMetricStatus)
(*in).DeepCopyInto(*out)
}
if in.External != nil {
in, out := &in.External, &out.External
*out = new(ExternalMetricStatus)

File diff suppressed because it is too large Load Diff

View File

@ -30,6 +30,40 @@ import "k8s.io/apimachinery/pkg/runtime/schema/generated.proto";
// Package-wide variables from generator "generated".
option go_package = "v2beta2";
// ContainerResourceMetricSource indicates how to scale on a resource metric known to
// Kubernetes, as specified in requests and limits, describing each pod in the
// current scale target (e.g. CPU or memory). The values will be averaged
// together before being compared to the target. Such metrics are built in to
// Kubernetes, and have special scaling options on top of those available to
// normal per-pod metrics using the "pods" source. Only one "target" type
// should be set.
message ContainerResourceMetricSource {
// name is the name of the resource in question.
optional string name = 1;
// target specifies the target value for the given metric
optional MetricTarget target = 2;
// container is the name of the container in the pods of the scaling target
optional string container = 3;
}
// ContainerResourceMetricStatus indicates the current value of a resource metric known to
// Kubernetes, as specified in requests and limits, describing a single container in each pod in the
// current scale target (e.g. CPU or memory). Such metrics are built in to
// Kubernetes, and have special scaling options on top of those available to
// normal per-pod metrics using the "pods" source.
message ContainerResourceMetricStatus {
// Name is the name of the resource in question.
optional string name = 1;
// current contains the current value for the given metric
optional MetricValueStatus current = 2;
// Container is the name of the container in the pods of the scaling target
optional string container = 3;
}
// CrossVersionObjectReference contains enough information to let you identify the referred resource.
message CrossVersionObjectReference {
// Kind of the referent; More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds"
@ -256,8 +290,10 @@ message MetricIdentifier {
// MetricSpec specifies how to scale based on a single metric
// (only `type` and one other matching field should be set at once).
message MetricSpec {
// type is the type of metric source. It should be one of "Object",
// "Pods", "Resource" or "External", each mapping to a matching field in the object.
// type is the type of metric source. It should be one of "ContainerResource", "External",
// "Object", "Pods" or "Resource", each mapping to a matching field in the object.
// Note: "ContainerResource" type is available on when the feature-gate
// HPAContainerMetrics is enabled
optional string type = 1;
// object refers to a metric describing a single kubernetes object
@ -279,6 +315,15 @@ message MetricSpec {
// +optional
optional ResourceMetricSource resource = 4;
// container resource refers to a resource metric (such as those specified in
// requests and limits) known to Kubernetes describing a single container in
// each pod of the current scale target (e.g. CPU or memory). Such metrics are
// built in to Kubernetes, and have special scaling options on top of those
// available to normal per-pod metrics using the "pods" source.
// This is an alpha feature and can be enabled by the HPAContainerMetrics feature flag.
// +optional
optional ContainerResourceMetricSource containerResource = 7;
// external refers to a global metric that is not associated
// with any Kubernetes object. It allows autoscaling based on information
// coming from components running outside of cluster
@ -290,8 +335,10 @@ message MetricSpec {
// MetricStatus describes the last-read state of a single metric.
message MetricStatus {
// type is the type of metric source. It will be one of "Object",
// "Pods" or "Resource", each corresponds to a matching field in the object.
// type is the type of metric source. It will be one of "ContainerResource", "External",
// "Object", "Pods" or "Resource", each corresponds to a matching field in the object.
// Note: "ContainerResource" type is available on when the feature-gate
// HPAContainerMetrics is enabled
optional string type = 1;
// object refers to a metric describing a single kubernetes object
@ -313,6 +360,14 @@ message MetricStatus {
// +optional
optional ResourceMetricStatus resource = 4;
// container resource refers to a resource metric (such as those specified in
// requests and limits) known to Kubernetes describing a single container in each pod in the
// current scale target (e.g. CPU or memory). Such metrics are built in to
// Kubernetes, and have special scaling options on top of those available
// to normal per-pod metrics using the "pods" source.
// +optional
optional ContainerResourceMetricStatus containerResource = 7;
// external refers to a global metric that is not associated
// with any Kubernetes object. It allows autoscaling based on information
// coming from components running outside of cluster

View File

@ -96,8 +96,10 @@ type CrossVersionObjectReference struct {
// MetricSpec specifies how to scale based on a single metric
// (only `type` and one other matching field should be set at once).
type MetricSpec struct {
// type is the type of metric source. It should be one of "Object",
// "Pods", "Resource" or "External", each mapping to a matching field in the object.
// type is the type of metric source. It should be one of "ContainerResource", "External",
// "Object", "Pods" or "Resource", each mapping to a matching field in the object.
// Note: "ContainerResource" type is available on when the feature-gate
// HPAContainerMetrics is enabled
Type MetricSourceType `json:"type" protobuf:"bytes,1,name=type"`
// object refers to a metric describing a single kubernetes object
@ -116,6 +118,14 @@ type MetricSpec struct {
// to normal per-pod metrics using the "pods" source.
// +optional
Resource *ResourceMetricSource `json:"resource,omitempty" protobuf:"bytes,4,opt,name=resource"`
// container resource refers to a resource metric (such as those specified in
// requests and limits) known to Kubernetes describing a single container in
// each pod of the current scale target (e.g. CPU or memory). Such metrics are
// built in to Kubernetes, and have special scaling options on top of those
// available to normal per-pod metrics using the "pods" source.
// This is an alpha feature and can be enabled by the HPAContainerMetrics feature flag.
// +optional
ContainerResource *ContainerResourceMetricSource `json:"containerResource,omitempty" protobuf:"bytes,7,opt,name=containerResource"`
// external refers to a global metric that is not associated
// with any Kubernetes object. It allows autoscaling based on information
// coming from components running outside of cluster
@ -220,6 +230,12 @@ const (
// Kubernetes, and have special scaling options on top of those available
// to normal per-pod metrics (the "pods" source).
ResourceMetricSourceType MetricSourceType = "Resource"
// ContainerResourceMetricSourceType is a resource metric known to Kubernetes, as
// specified in requests and limits, describing a single container in each pod in the current
// scale target (e.g. CPU or memory). Such metrics are built in to
// Kubernetes, and have special scaling options on top of those available
// to normal per-pod metrics (the "pods" source).
ContainerResourceMetricSourceType MetricSourceType = "ContainerResource"
// ExternalMetricSourceType is a global metric that is not associated
// with any Kubernetes object. It allows autoscaling based on information
// coming from components running outside of cluster
@ -263,6 +279,22 @@ type ResourceMetricSource struct {
Target MetricTarget `json:"target" protobuf:"bytes,2,name=target"`
}
// ContainerResourceMetricSource indicates how to scale on a resource metric known to
// Kubernetes, as specified in requests and limits, describing each pod in the
// current scale target (e.g. CPU or memory). The values will be averaged
// together before being compared to the target. Such metrics are built in to
// Kubernetes, and have special scaling options on top of those available to
// normal per-pod metrics using the "pods" source. Only one "target" type
// should be set.
type ContainerResourceMetricSource struct {
// name is the name of the resource in question.
Name v1.ResourceName `json:"name" protobuf:"bytes,1,name=name"`
// target specifies the target value for the given metric
Target MetricTarget `json:"target" protobuf:"bytes,2,name=target"`
// container is the name of the container in the pods of the scaling target
Container string `json:"container" protobuf:"bytes,3,opt,name=container"`
}
// ExternalMetricSource indicates how to scale on a metric not associated with
// any Kubernetes object (for example length of queue in cloud
// messaging service, or QPS from loadbalancer running outside of cluster).
@ -382,8 +414,10 @@ type HorizontalPodAutoscalerCondition struct {
// MetricStatus describes the last-read state of a single metric.
type MetricStatus struct {
// type is the type of metric source. It will be one of "Object",
// "Pods" or "Resource", each corresponds to a matching field in the object.
// type is the type of metric source. It will be one of "ContainerResource", "External",
// "Object", "Pods" or "Resource", each corresponds to a matching field in the object.
// Note: "ContainerResource" type is available on when the feature-gate
// HPAContainerMetrics is enabled
Type MetricSourceType `json:"type" protobuf:"bytes,1,name=type"`
// object refers to a metric describing a single kubernetes object
@ -402,6 +436,13 @@ type MetricStatus struct {
// to normal per-pod metrics using the "pods" source.
// +optional
Resource *ResourceMetricStatus `json:"resource,omitempty" protobuf:"bytes,4,opt,name=resource"`
// container resource refers to a resource metric (such as those specified in
// requests and limits) known to Kubernetes describing a single container in each pod in the
// current scale target (e.g. CPU or memory). Such metrics are built in to
// Kubernetes, and have special scaling options on top of those available
// to normal per-pod metrics using the "pods" source.
// +optional
ContainerResource *ContainerResourceMetricStatus `json:"containerResource,omitempty" protobuf:"bytes,7,opt,name=containerResource"`
// external refers to a global metric that is not associated
// with any Kubernetes object. It allows autoscaling based on information
// coming from components running outside of cluster
@ -443,6 +484,20 @@ type ResourceMetricStatus struct {
Current MetricValueStatus `json:"current" protobuf:"bytes,2,name=current"`
}
// ContainerResourceMetricStatus indicates the current value of a resource metric known to
// Kubernetes, as specified in requests and limits, describing a single container in each pod in the
// current scale target (e.g. CPU or memory). Such metrics are built in to
// Kubernetes, and have special scaling options on top of those available to
// normal per-pod metrics using the "pods" source.
type ContainerResourceMetricStatus struct {
// Name is the name of the resource in question.
Name v1.ResourceName `json:"name" protobuf:"bytes,1,name=name"`
// current contains the current value for the given metric
Current MetricValueStatus `json:"current" protobuf:"bytes,2,name=current"`
// Container is the name of the container in the pods of the scaling target
Container string `json:"container" protobuf:"bytes,3,opt,name=container"`
}
// ExternalMetricStatus indicates the current value of a global metric
// not associated with any Kubernetes object.
type ExternalMetricStatus struct {

View File

@ -27,6 +27,28 @@ package v2beta2
// Those methods can be generated by using hack/update-generated-swagger-docs.sh
// AUTO-GENERATED FUNCTIONS START HERE. DO NOT EDIT.
var map_ContainerResourceMetricSource = map[string]string{
"": "ContainerResourceMetricSource indicates how to scale on a resource metric known to Kubernetes, as specified in requests and limits, describing each pod in the current scale target (e.g. CPU or memory). The values will be averaged together before being compared to the target. Such metrics are built in to Kubernetes, and have special scaling options on top of those available to normal per-pod metrics using the \"pods\" source. Only one \"target\" type should be set.",
"name": "name is the name of the resource in question.",
"target": "target specifies the target value for the given metric",
"container": "container is the name of the container in the pods of the scaling target",
}
func (ContainerResourceMetricSource) SwaggerDoc() map[string]string {
return map_ContainerResourceMetricSource
}
var map_ContainerResourceMetricStatus = map[string]string{
"": "ContainerResourceMetricStatus indicates the current value of a resource metric known to Kubernetes, as specified in requests and limits, describing a single container in each pod in the current scale target (e.g. CPU or memory). Such metrics are built in to Kubernetes, and have special scaling options on top of those available to normal per-pod metrics using the \"pods\" source.",
"name": "Name is the name of the resource in question.",
"current": "current contains the current value for the given metric",
"container": "Container is the name of the container in the pods of the scaling target",
}
func (ContainerResourceMetricStatus) SwaggerDoc() map[string]string {
return map_ContainerResourceMetricStatus
}
var map_CrossVersionObjectReference = map[string]string{
"": "CrossVersionObjectReference contains enough information to let you identify the referred resource.",
"kind": "Kind of the referent; More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds\"",
@ -162,12 +184,13 @@ func (MetricIdentifier) SwaggerDoc() map[string]string {
}
var map_MetricSpec = map[string]string{
"": "MetricSpec specifies how to scale based on a single metric (only `type` and one other matching field should be set at once).",
"type": "type is the type of metric source. It should be one of \"Object\", \"Pods\", \"Resource\" or \"External\", each mapping to a matching field in the object.",
"object": "object refers to a metric describing a single kubernetes object (for example, hits-per-second on an Ingress object).",
"pods": "pods refers to a metric describing each pod in the current scale target (for example, transactions-processed-per-second). The values will be averaged together before being compared to the target value.",
"resource": "resource refers to a resource metric (such as those specified in requests and limits) known to Kubernetes describing each pod in the current scale target (e.g. CPU or memory). Such metrics are built in to Kubernetes, and have special scaling options on top of those available to normal per-pod metrics using the \"pods\" source.",
"external": "external refers to a global metric that is not associated with any Kubernetes object. It allows autoscaling based on information coming from components running outside of cluster (for example length of queue in cloud messaging service, or QPS from loadbalancer running outside of cluster).",
"": "MetricSpec specifies how to scale based on a single metric (only `type` and one other matching field should be set at once).",
"type": "type is the type of metric source. It should be one of \"ContainerResource\", \"External\", \"Object\", \"Pods\" or \"Resource\", each mapping to a matching field in the object. Note: \"ContainerResource\" type is available on when the feature-gate HPAContainerMetrics is enabled",
"object": "object refers to a metric describing a single kubernetes object (for example, hits-per-second on an Ingress object).",
"pods": "pods refers to a metric describing each pod in the current scale target (for example, transactions-processed-per-second). The values will be averaged together before being compared to the target value.",
"resource": "resource refers to a resource metric (such as those specified in requests and limits) known to Kubernetes describing each pod in the current scale target (e.g. CPU or memory). Such metrics are built in to Kubernetes, and have special scaling options on top of those available to normal per-pod metrics using the \"pods\" source.",
"containerResource": "container resource refers to a resource metric (such as those specified in requests and limits) known to Kubernetes describing a single container in each pod of the current scale target (e.g. CPU or memory). Such metrics are built in to Kubernetes, and have special scaling options on top of those available to normal per-pod metrics using the \"pods\" source. This is an alpha feature and can be enabled by the HPAContainerMetrics feature flag.",
"external": "external refers to a global metric that is not associated with any Kubernetes object. It allows autoscaling based on information coming from components running outside of cluster (for example length of queue in cloud messaging service, or QPS from loadbalancer running outside of cluster).",
}
func (MetricSpec) SwaggerDoc() map[string]string {
@ -175,12 +198,13 @@ func (MetricSpec) SwaggerDoc() map[string]string {
}
var map_MetricStatus = map[string]string{
"": "MetricStatus describes the last-read state of a single metric.",
"type": "type is the type of metric source. It will be one of \"Object\", \"Pods\" or \"Resource\", each corresponds to a matching field in the object.",
"object": "object refers to a metric describing a single kubernetes object (for example, hits-per-second on an Ingress object).",
"pods": "pods refers to a metric describing each pod in the current scale target (for example, transactions-processed-per-second). The values will be averaged together before being compared to the target value.",
"resource": "resource refers to a resource metric (such as those specified in requests and limits) known to Kubernetes describing each pod in the current scale target (e.g. CPU or memory). Such metrics are built in to Kubernetes, and have special scaling options on top of those available to normal per-pod metrics using the \"pods\" source.",
"external": "external refers to a global metric that is not associated with any Kubernetes object. It allows autoscaling based on information coming from components running outside of cluster (for example length of queue in cloud messaging service, or QPS from loadbalancer running outside of cluster).",
"": "MetricStatus describes the last-read state of a single metric.",
"type": "type is the type of metric source. It will be one of \"ContainerResource\", \"External\", \"Object\", \"Pods\" or \"Resource\", each corresponds to a matching field in the object. Note: \"ContainerResource\" type is available on when the feature-gate HPAContainerMetrics is enabled",
"object": "object refers to a metric describing a single kubernetes object (for example, hits-per-second on an Ingress object).",
"pods": "pods refers to a metric describing each pod in the current scale target (for example, transactions-processed-per-second). The values will be averaged together before being compared to the target value.",
"resource": "resource refers to a resource metric (such as those specified in requests and limits) known to Kubernetes describing each pod in the current scale target (e.g. CPU or memory). Such metrics are built in to Kubernetes, and have special scaling options on top of those available to normal per-pod metrics using the \"pods\" source.",
"containerResource": "container resource refers to a resource metric (such as those specified in requests and limits) known to Kubernetes describing a single container in each pod in the current scale target (e.g. CPU or memory). Such metrics are built in to Kubernetes, and have special scaling options on top of those available to normal per-pod metrics using the \"pods\" source.",
"external": "external refers to a global metric that is not associated with any Kubernetes object. It allows autoscaling based on information coming from components running outside of cluster (for example length of queue in cloud messaging service, or QPS from loadbalancer running outside of cluster).",
}
func (MetricStatus) SwaggerDoc() map[string]string {

View File

@ -25,6 +25,40 @@ import (
runtime "k8s.io/apimachinery/pkg/runtime"
)
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ContainerResourceMetricSource) DeepCopyInto(out *ContainerResourceMetricSource) {
*out = *in
in.Target.DeepCopyInto(&out.Target)
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ContainerResourceMetricSource.
func (in *ContainerResourceMetricSource) DeepCopy() *ContainerResourceMetricSource {
if in == nil {
return nil
}
out := new(ContainerResourceMetricSource)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ContainerResourceMetricStatus) DeepCopyInto(out *ContainerResourceMetricStatus) {
*out = *in
in.Current.DeepCopyInto(&out.Current)
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ContainerResourceMetricStatus.
func (in *ContainerResourceMetricStatus) DeepCopy() *ContainerResourceMetricStatus {
if in == nil {
return nil
}
out := new(ContainerResourceMetricStatus)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *CrossVersionObjectReference) DeepCopyInto(out *CrossVersionObjectReference) {
*out = *in
@ -340,6 +374,11 @@ func (in *MetricSpec) DeepCopyInto(out *MetricSpec) {
*out = new(ResourceMetricSource)
(*in).DeepCopyInto(*out)
}
if in.ContainerResource != nil {
in, out := &in.ContainerResource, &out.ContainerResource
*out = new(ContainerResourceMetricSource)
(*in).DeepCopyInto(*out)
}
if in.External != nil {
in, out := &in.External, &out.External
*out = new(ExternalMetricSource)
@ -376,6 +415,11 @@ func (in *MetricStatus) DeepCopyInto(out *MetricStatus) {
*out = new(ResourceMetricStatus)
(*in).DeepCopyInto(*out)
}
if in.ContainerResource != nil {
in, out := &in.ContainerResource, &out.ContainerResource
*out = new(ContainerResourceMetricStatus)
(*in).DeepCopyInto(*out)
}
if in.External != nil {
in, out := &in.External, &out.External
*out = new(ExternalMetricStatus)

View File

@ -98,98 +98,122 @@
"targetAverageUtilization": 1253093074,
"targetAverageValue": "8"
},
"containerResource": {
"name": "w垁鷌辪虽U珝Żwʮ馜üNșƶ4ĩ",
"targetAverageUtilization": 1463207240,
"targetAverageValue": "84",
"container": "39"
},
"external": {
"metricName": "39",
"metricName": "40",
"metricSelector": {
"matchLabels": {
"4kh6oqu-or---40--87-1wpl6-2-310e5hz/B._z": "G6t-2.-_-8w6"
"bigm-h8-3q768km-0--03-t-0-035--5b95w------4-n4f-h/u-5.-Z3P__D__6t-2.-_-8wE._._3.-.83_iq_-1": "1-_Y33--.8U.-.5--_zm-.-_RJt2pX_2_28.-.7_8B.HF-U-_ik_--DSX0"
},
"matchExpressions": [
{
"key": "1295at-o7qff7-x--r7v66bm71u-n4f9wk-3--652x01--p--n4-4-t--2gk-0/d.83_iq_-y.-25C.A-j..9dfn3Y8d_0_.---M_4FpF_W-1._-vL_i.-_-a--GI",
"operator": "DoesNotExist"
"key": "680---z-6-t2z-w-fg98-r--v53nyx5u-o-k-md--381d-7.3di292f--90-17-hg1-o-p665--4-j8---t6-r7r/Y-H-Mqpt._.-_..05c.---qy-_5_S.d5a37",
"operator": "NotIn",
"values": [
"dY_um-_8r--684._-_18_...E.-2o_-.5"
]
}
]
},
"targetValue": "570",
"targetAverageValue": "829"
"targetValue": "865",
"targetAverageValue": "379"
}
}
]
},
"status": {
"observedGeneration": -7224326297454280417,
"currentReplicas": 596942561,
"desiredReplicas": -1880980172,
"observedGeneration": 61436896663269868,
"currentReplicas": -1462219068,
"desiredReplicas": -370386363,
"currentMetrics": [
{
"type": " ïì«丯Ƙ枛牐ɺ皚|懥",
"type": "",
"object": {
"target": {
"kind": "46",
"name": "47",
"apiVersion": "48"
"kind": "47",
"name": "48",
"apiVersion": "49"
},
"metricName": "49",
"currentValue": "55",
"metricName": "50",
"currentValue": "856",
"selector": {
"matchLabels": {
"8._Wxpe..J7rs6.0_OHz_.B-.-_w_--.8_r_N-.3n-xu": "o_-Nm-_X8"
"vL7": "L_0N_N.O30-_u._-2hT.-z-._7-5lL..-_--.VEa-_gn.8-c.C3_F._oX-FT"
},
"matchExpressions": [
{
"key": "665--4-j8---t6-r7--9.9dy/XvSA..1",
"operator": "Exists"
"key": "4_.-N_g-.._5",
"operator": "In",
"values": [
"2qz.W..4....-h._.GgT7_7B_D-..-.k4u-zA_--_.-.6GA26C-s.Nj-d-4_t"
]
}
]
},
"averageValue": "14"
"averageValue": "817"
},
"pods": {
"metricName": "56",
"currentAverageValue": "878",
"metricName": "57",
"currentAverageValue": "400",
"selector": {
"matchLabels": {
"6fv--m-8--72-bca4m56au3f-j/0-_1-F.h-__k_K5._..O_.J_-G_--V-42E_--o90G_A4..-L..-__0N_N.O3i": "r_gn.8-c.C3_F._oX-F9_.v"
"mj_9.M.134-5-.q6H_.--_---.M.U_-m.-P.yP9S--858LI__.8U": "KrO-S-P_-...0c.-.p_3_J_SA995IKCR.s--f.-f.-zv._._.5-HT"
},
"matchExpressions": [
{
"key": "g-..__._____K_g1cXfr.4_.B",
"operator": "DoesNotExist"
"key": "26-k8-c2---2etfh41ca-z-5g2wco280.ka-6-31g--z-o-3bz6-8-0-1-z--271s-p9-8--m-cbck561-7n/VC..7o_x3..-.8J",
"operator": "NotIn",
"values": [
"8._Q.6.I--2_9.v.--_.--4QQ.-s.H.Hf"
]
}
]
}
},
"resource": {
"name": "šeSvEȤƏ埮pɵ{WOŭW灬pȭ",
"currentAverageUtilization": -1607821167,
"currentAverageValue": "832"
"name": "ƻ遲njlȘ鹾KƂʼnç",
"currentAverageUtilization": 769521729,
"currentAverageValue": "727"
},
"containerResource": {
"name": "!@@)Zq=",
"currentAverageUtilization": -861915578,
"currentAverageValue": "111",
"container": "64"
},
"external": {
"metricName": "63",
"metricName": "65",
"metricSelector": {
"matchLabels": {
"4_4--.-_Z4.LA3HVG93_._.I3.__-.0-z_z0sn_.hx_-a__0-8-.M-.-.-8vJ": "zET_..3dCv3j._.-_pP__up.2L_s-o779._-k-5___-Qq4"
"2y3-4-3/AO": "r..6W.V0"
},
"matchExpressions": [
{
"key": "FC-rtSY.g._2F7.-_e..OP",
"operator": "Exists"
"key": "v55039780bdw0-1-47rrw8-7/U_--56-.7D.3_KPg___Kp",
"operator": "In",
"values": [
"N7_-Zp_._w"
]
}
]
},
"currentValue": "229",
"currentAverageValue": "606"
"currentValue": "200",
"currentAverageValue": "288"
}
}
],
"conditions": [
{
"type": "鯶縆",
"status": "aTGÒ鵌Ē3",
"lastTransitionTime": "2416-12-01T11:47:49Z",
"reason": "70",
"message": "71"
"type": "",
"status": "",
"lastTransitionTime": "1995-10-31T09:59:43Z",
"reason": "72",
"message": "73"
}
]
}

View File

@ -32,16 +32,23 @@ metadata:
spec:
maxReplicas: -1971381490
metrics:
- external:
metricName: "39"
- containerResource:
container: "39"
name: w垁鷌辪虽U珝Żwʮ馜üNșƶ4ĩ
targetAverageUtilization: 1463207240
targetAverageValue: "84"
external:
metricName: "40"
metricSelector:
matchExpressions:
- key: 1295at-o7qff7-x--r7v66bm71u-n4f9wk-3--652x01--p--n4-4-t--2gk-0/d.83_iq_-y.-25C.A-j..9dfn3Y8d_0_.---M_4FpF_W-1._-vL_i.-_-a--GI
operator: DoesNotExist
- key: 680---z-6-t2z-w-fg98-r--v53nyx5u-o-k-md--381d-7.3di292f--90-17-hg1-o-p665--4-j8---t6-r7r/Y-H-Mqpt._.-_..05c.---qy-_5_S.d5a37
operator: NotIn
values:
- dY_um-_8r--684._-_18_...E.-2o_-.5
matchLabels:
4kh6oqu-or---40--87-1wpl6-2-310e5hz/B._z: G6t-2.-_-8w6
targetAverageValue: "829"
targetValue: "570"
bigm-h8-3q768km-0--03-t-0-035--5b95w------4-n4f-h/u-5.-Z3P__D__6t-2.-_-8wE._._3.-.83_iq_-1: 1-_Y33--.8U.-.5--_zm-.-_RJt2pX_2_28.-.7_8B.HF-U-_ik_--DSX0
targetAverageValue: "379"
targetValue: "865"
object:
averageValue: "954"
metricName: "25"
@ -82,50 +89,61 @@ spec:
name: "20"
status:
conditions:
- lastTransitionTime: "2416-12-01T11:47:49Z"
message: "71"
reason: "70"
status: aTGÒ鵌Ē3
type: 鯶縆
- lastTransitionTime: "1995-10-31T09:59:43Z"
message: "73"
reason: "72"
status: ""
type: ""
currentMetrics:
- external:
currentAverageValue: "606"
currentValue: "229"
metricName: "63"
- containerResource:
container: "64"
currentAverageUtilization: -861915578
currentAverageValue: "111"
name: '!@@)Zq='
external:
currentAverageValue: "288"
currentValue: "200"
metricName: "65"
metricSelector:
matchExpressions:
- key: FC-rtSY.g._2F7.-_e..OP
operator: Exists
- key: v55039780bdw0-1-47rrw8-7/U_--56-.7D.3_KPg___Kp
operator: In
values:
- N7_-Zp_._w
matchLabels:
4_4--.-_Z4.LA3HVG93_._.I3.__-.0-z_z0sn_.hx_-a__0-8-.M-.-.-8vJ: zET_..3dCv3j._.-_pP__up.2L_s-o779._-k-5___-Qq4
2y3-4-3/AO: r..6W.V0
object:
averageValue: "14"
currentValue: "55"
metricName: "49"
averageValue: "817"
currentValue: "856"
metricName: "50"
selector:
matchExpressions:
- key: 665--4-j8---t6-r7--9.9dy/XvSA..1
operator: Exists
- key: 4_.-N_g-.._5
operator: In
values:
- 2qz.W..4....-h._.GgT7_7B_D-..-.k4u-zA_--_.-.6GA26C-s.Nj-d-4_t
matchLabels:
8._Wxpe..J7rs6.0_OHz_.B-.-_w_--.8_r_N-.3n-xu: o_-Nm-_X8
vL7: L_0N_N.O30-_u._-2hT.-z-._7-5lL..-_--.VEa-_gn.8-c.C3_F._oX-FT
target:
apiVersion: "48"
kind: "46"
name: "47"
apiVersion: "49"
kind: "47"
name: "48"
pods:
currentAverageValue: "878"
metricName: "56"
currentAverageValue: "400"
metricName: "57"
selector:
matchExpressions:
- key: g-..__._____K_g1cXfr.4_.B
operator: DoesNotExist
- key: 26-k8-c2---2etfh41ca-z-5g2wco280.ka-6-31g--z-o-3bz6-8-0-1-z--271s-p9-8--m-cbck561-7n/VC..7o_x3..-.8J
operator: NotIn
values:
- 8._Q.6.I--2_9.v.--_.--4QQ.-s.H.Hf
matchLabels:
6fv--m-8--72-bca4m56au3f-j/0-_1-F.h-__k_K5._..O_.J_-G_--V-42E_--o90G_A4..-L..-__0N_N.O3i: r_gn.8-c.C3_F._oX-F9_.v
mj_9.M.134-5-.q6H_.--_---.M.U_-m.-P.yP9S--858LI__.8U: KrO-S-P_-...0c.-.p_3_J_SA995IKCR.s--f.-f.-zv._._.5-HT
resource:
currentAverageUtilization: -1607821167
currentAverageValue: "832"
name: šeSvEȤƏ埮pɵ{WOŭW灬pȭ
type: ' ïì«丯Ƙ枛牐ɺ皚|懥'
currentReplicas: 596942561
desiredReplicas: -1880980172
observedGeneration: -7224326297454280417
currentAverageUtilization: 769521729
currentAverageValue: "727"
name: ƻ遲njlȘ鹾KƂʼnç
type: ""
currentReplicas: -1462219068
desiredReplicas: -370386363
observedGeneration: 61436896663269868

View File

@ -112,159 +112,169 @@
"averageUtilization": 580681683
}
},
"containerResource": {
"name": "Ɋł/擇ɦĽ胚O醔ɍ厶耈 ",
"target": {
"type": "禒Ƙá腿ħ缶.蒅",
"value": "999",
"averageValue": "151",
"averageUtilization": -1105572246
},
"container": "39"
},
"external": {
"metric": {
"name": "39",
"name": "40",
"selector": {
"matchLabels": {
"9s-m---vl80.5-6y-07b-3---g-jdi/z_-tY-R6S17_.8CnK_O.d-._NwcGnP-w-Sf5_Or.i1_7z.WH-.._d": "1_57__JR.N-1zL-4--6o--Bo-F__..XR.7_1-l"
"wcGnP-w-Sf5_O1": "1_70"
},
"matchExpressions": [
{
"key": "n3-8d-0-5qty--4-p---u16-wv-i.84-n4f--139-295at-o7qff7-x--r7v66bm71u-n4f0/2_31.-.-yz-0-_p4mz--.I_f6kjsz-7lwY-Y93-x6bigm_-._q",
"operator": "In",
"values": [
"Q3_Y.5.-..P_pDZ-._._t__2k"
]
"key": "1-1-x1z-j4kh6oqu-or---40--87-1wp6.759s-3------6tv27r-m8w-6-9-35d383-iq-ay1z25-3-vj73d/Y.t--_0..--_6yV07-_.___gO-d.iUaCw",
"operator": "DoesNotExist"
}
]
}
},
"target": {
"type": "3.v-鿧悮坮Ȣ",
"value": "82",
"averageValue": "301",
"averageUtilization": -521487971
"type": "ʣy豎@ɀ羭,铻OŤ",
"value": "830",
"averageValue": "799",
"averageUtilization": 747521320
}
}
}
],
"behavior": {
"scaleUp": {
"stabilizationWindowSeconds": 1761963371,
"selectPolicy": "0矀Kʝ瘴I\\p[ħsĨɆâĺɗŹ倗S",
"stabilizationWindowSeconds": -648954478,
"selectPolicy": "Ƿ裚瓶釆Ɗ+j忊",
"policies": [
{
"type": "嶗U",
"value": -1285424066,
"periodSeconds": -686523310
"type": "ȫ焗捏ĨFħ籘Àǒɿʒ刽",
"value": 427196286,
"periodSeconds": 1048864116
}
]
},
"scaleDown": {
"stabilizationWindowSeconds": 1206365825,
"selectPolicy": "/ɸɎ R§耶FfBls3!",
"stabilizationWindowSeconds": -342705708,
"selectPolicy": "褎weLJèux",
"policies": [
{
"type": "ɾģ毋Ó6dz娝嘚",
"value": 627713162,
"periodSeconds": 1255312175
"type": "VƋZ1Ůđ眊ľǎɳ,ǿ飏",
"value": 2040455355,
"periodSeconds": 1505972335
}
]
}
}
},
"status": {
"observedGeneration": -7477362499801752548,
"currentReplicas": 267768240,
"desiredReplicas": -127849333,
"observedGeneration": -115578794491385044,
"currentReplicas": 474119379,
"desiredReplicas": 1923334396,
"currentMetrics": [
{
"type": "Ǖɳɷ9Ì崟¿瘦ɖ緕",
"type": "0Ƹ[Ęİ榌U",
"object": {
"metric": {
"name": "46",
"name": "47",
"selector": {
"matchLabels": {
"29.-_Z.0_1._hg._o_p665O_4Gj._BXt.O-7___-Y_um-_8r--684._V": "8_...E.-o"
"hA9..9__Y-H-Mqpt._.-_..05c.---qy-_5_S.d5a3Jb": "46g_4....1..jtFe8b_A_..P1s-V.9.4..9..c_uo3Pa__n-Di"
},
"matchExpressions": [
{
"key": "6-d42--clo90---461v-07r--0---8-30i-uo/9DF",
"operator": "In",
"key": "Dp665O_4Gj._BXt.O-7___-Y_um-_8r--684._-_188",
"operator": "NotIn",
"values": [
"2hT.-z-._7-5lL..-_--.VEa-_gn.8-c.C3_F._oX-F9_.5vN5.25aWx.a"
"XK5._..O_.J_-G_--V-42E_--o90G_A4..-L..-__0N_N.O30-_u._-2T"
]
}
]
}
},
"current": {
"value": "526",
"averageValue": "860",
"averageUtilization": -126958936
"value": "124",
"averageValue": "472",
"averageUtilization": -1666319281
},
"describedObject": {
"kind": "53",
"name": "54",
"apiVersion": "55"
"kind": "54",
"name": "55",
"apiVersion": "56"
}
},
"pods": {
"metric": {
"name": "56",
"name": "57",
"selector": {
"matchLabels": {
"d5-g-7-7---g88w2k4usz--mj-8o26--26-hs5-jeds4-4tz9x-4.i-l11q5--uk5mj-94-8134i5k6q6--5tu-tie4-7--gm4p-8y-99/N_g-..__._____K_g1cXfr4": "ET_..3dCv3j._.-_pP__up.2L_s-o779._-k-N"
"5.-_--.VEa-_gn.8-c.3": "F._oX-F9_.5vN5.25aWx.2aM214_.-C"
},
"matchExpressions": [
{
"key": "382m88w-pz9d4i-m7---k8235--8--c83-4b-9-1k.1f-53-x1y-8---3----p-pdn--j2---2--82--cj-1-s--op3w/3--Z1Tvw39F_C-rtSY.g._2F7.-_e..Or_-.3OHgt.U",
"operator": "In",
"values": [
"y.8_8"
]
"key": "4ds4-4tz9x--j.1l11q5--uk5mj-94-8134i5k6q6--5tu-tie4-7--gm4p-8y-9-te5/H._____K_g1cXfr.4_.-_-_-...1py_8-3..s._.x.2K_2qu_0S-CqWD",
"operator": "Exists"
}
]
}
},
"current": {
"value": "671",
"averageValue": "683",
"averageUtilization": 1008425444
"value": "378",
"averageValue": "328",
"averageUtilization": -1050824692
}
},
"resource": {
"name": "Ƈè*鑏='ʨ|ǓÓ敆OɈÏ 瞍髃",
"name": "反-n覦灲閈誹ʅ蕉ɼ搳ǭ濑箨ʨ",
"current": {
"value": "93",
"averageValue": "183",
"averageUtilization": -392406530
"value": "113",
"averageValue": "653",
"averageUtilization": 1190831814
}
},
"containerResource": {
"name": "腂ǂǚŜEuEy",
"current": {
"value": "77",
"averageValue": "394",
"averageUtilization": -1945921250
},
"container": "64"
},
"external": {
"metric": {
"name": "63",
"name": "65",
"selector": {
"matchLabels": {
"627-23---g-----p8-d5-8-m8i--k0j5-gr-y7nlp97v-0-1y7/2....3_t_-l..-.DG7r-3.----._4__XOnf_ZN.-_--r.E__-8": "K.-miJ4x-_0_5-_7"
"Tvw39F_C-rtSY.g._2F7.-_e..Or_-.3OHgt._U.-x_rC9..__-6_k.N-2B_Ve": "fh4.caTz_.g.w-o.8_WT-M.3_-1y_8E"
},
"matchExpressions": [
{
"key": "c-.F5_x.KNC0-.-m_0-m-6Sp_N-S..o",
"operator": "In",
"values": [
"g-_4Q__-v_t_u_.__I_-_-3-3--5X1rh-K5y_AzOBW.9oE9_6.--v7"
]
"key": "n_H-.___._D8.TS-jJ.Ys_Mop34_-2",
"operator": "DoesNotExist"
}
]
}
},
"current": {
"value": "287",
"averageValue": "759",
"averageUtilization": -1175595426
"value": "444",
"averageValue": "797",
"averageUtilization": 1928526133
}
}
}
],
"conditions": [
{
"type": "`翾'ųŎ群E牬庘颮6(|ǖû",
"status": "龢ÄƤUǷ坒ŕF5o儎ĄÇ稕E",
"lastTransitionTime": "2682-06-14T06:09:58Z",
"reason": "70",
"message": "71"
"type": "44矕",
"status": "ƱÁR»淹揀",
"lastTransitionTime": "2026-05-26T09:35:23Z",
"reason": "72",
"message": "73"
}
]
}

View File

@ -33,36 +33,42 @@ spec:
behavior:
scaleDown:
policies:
- periodSeconds: 1255312175
type: ɾģ毋Ó6dz娝嘚
value: 627713162
selectPolicy: /ɸɎ R§耶FfBls3!
stabilizationWindowSeconds: 1206365825
- periodSeconds: 1505972335
type: VƋZ1Ůđ眊ľǎɳ,ǿ飏
value: 2040455355
selectPolicy: 褎weLJèux
stabilizationWindowSeconds: -342705708
scaleUp:
policies:
- periodSeconds: -686523310
type: 嶗U
value: -1285424066
selectPolicy: 0矀Kʝ瘴I\p[ħsĨɆâĺɗŹ倗S
stabilizationWindowSeconds: 1761963371
- periodSeconds: 1048864116
type: ȫ焗捏ĨFħ籘Àǒɿʒ刽
value: 427196286
selectPolicy: Ƿ裚瓶釆Ɗ+j忊
stabilizationWindowSeconds: -648954478
maxReplicas: -1971381490
metrics:
- external:
- containerResource:
container: "39"
name: Ɋł/擇ɦĽ胚O醔ɍ厶耈 
target:
averageUtilization: -1105572246
averageValue: "151"
type: 禒Ƙá腿ħ缶.蒅
value: "999"
external:
metric:
name: "39"
name: "40"
selector:
matchExpressions:
- key: n3-8d-0-5qty--4-p---u16-wv-i.84-n4f--139-295at-o7qff7-x--r7v66bm71u-n4f0/2_31.-.-yz-0-_p4mz--.I_f6kjsz-7lwY-Y93-x6bigm_-._q
operator: In
values:
- Q3_Y.5.-..P_pDZ-._._t__2k
- key: 1-1-x1z-j4kh6oqu-or---40--87-1wp6.759s-3------6tv27r-m8w-6-9-35d383-iq-ay1z25-3-vj73d/Y.t--_0..--_6yV07-_.___gO-d.iUaCw
operator: DoesNotExist
matchLabels:
9s-m---vl80.5-6y-07b-3---g-jdi/z_-tY-R6S17_.8CnK_O.d-._NwcGnP-w-Sf5_Or.i1_7z.WH-.._d: 1_57__JR.N-1zL-4--6o--Bo-F__..XR.7_1-l
wcGnP-w-Sf5_O1: "1_70"
target:
averageUtilization: -521487971
averageValue: "301"
type: 3.v-鿧悮坮Ȣ
value: "82"
averageUtilization: 747521320
averageValue: "799"
type: ʣy豎@ɀ羭,铻OŤ
value: "830"
object:
describedObject:
apiVersion: "24"
@ -112,69 +118,71 @@ spec:
name: "20"
status:
conditions:
- lastTransitionTime: "2682-06-14T06:09:58Z"
message: "71"
reason: "70"
status: 龢ÄƤUǷ坒ŕF5o儎ĄÇ稕E
type: '`翾''ųŎ群E牬庘颮6(|ǖû'
- lastTransitionTime: "2026-05-26T09:35:23Z"
message: "73"
reason: "72"
status: ƱÁR»淹揀
type: 44矕
currentMetrics:
- external:
- containerResource:
container: "64"
current:
averageUtilization: -1175595426
averageValue: "759"
value: "287"
averageUtilization: -1945921250
averageValue: "394"
value: "77"
name: 腂ǂǚŜEuEy
external:
current:
averageUtilization: 1928526133
averageValue: "797"
value: "444"
metric:
name: "63"
name: "65"
selector:
matchExpressions:
- key: c-.F5_x.KNC0-.-m_0-m-6Sp_N-S..o
operator: In
values:
- g-_4Q__-v_t_u_.__I_-_-3-3--5X1rh-K5y_AzOBW.9oE9_6.--v7
- key: n_H-.___._D8.TS-jJ.Ys_Mop34_-2
operator: DoesNotExist
matchLabels:
627-23---g-----p8-d5-8-m8i--k0j5-gr-y7nlp97v-0-1y7/2....3_t_-l..-.DG7r-3.----._4__XOnf_ZN.-_--r.E__-8: K.-miJ4x-_0_5-_7
Tvw39F_C-rtSY.g._2F7.-_e..Or_-.3OHgt._U.-x_rC9..__-6_k.N-2B_Ve: fh4.caTz_.g.w-o.8_WT-M.3_-1y_8E
object:
current:
averageUtilization: -126958936
averageValue: "860"
value: "526"
averageUtilization: -1666319281
averageValue: "472"
value: "124"
describedObject:
apiVersion: "55"
kind: "53"
name: "54"
apiVersion: "56"
kind: "54"
name: "55"
metric:
name: "46"
name: "47"
selector:
matchExpressions:
- key: 6-d42--clo90---461v-07r--0---8-30i-uo/9DF
operator: In
- key: Dp665O_4Gj._BXt.O-7___-Y_um-_8r--684._-_188
operator: NotIn
values:
- 2hT.-z-._7-5lL..-_--.VEa-_gn.8-c.C3_F._oX-F9_.5vN5.25aWx.a
- XK5._..O_.J_-G_--V-42E_--o90G_A4..-L..-__0N_N.O30-_u._-2T
matchLabels:
29.-_Z.0_1._hg._o_p665O_4Gj._BXt.O-7___-Y_um-_8r--684._V: 8_...E.-o
hA9..9__Y-H-Mqpt._.-_..05c.---qy-_5_S.d5a3Jb: 46g_4....1..jtFe8b_A_..P1s-V.9.4..9..c_uo3Pa__n-Di
pods:
current:
averageUtilization: 1008425444
averageValue: "683"
value: "671"
averageUtilization: -1050824692
averageValue: "328"
value: "378"
metric:
name: "56"
name: "57"
selector:
matchExpressions:
- key: 382m88w-pz9d4i-m7---k8235--8--c83-4b-9-1k.1f-53-x1y-8---3----p-pdn--j2---2--82--cj-1-s--op3w/3--Z1Tvw39F_C-rtSY.g._2F7.-_e..Or_-.3OHgt.U
operator: In
values:
- y.8_8
- key: 4ds4-4tz9x--j.1l11q5--uk5mj-94-8134i5k6q6--5tu-tie4-7--gm4p-8y-9-te5/H._____K_g1cXfr.4_.-_-_-...1py_8-3..s._.x.2K_2qu_0S-CqWD
operator: Exists
matchLabels:
? d5-g-7-7---g88w2k4usz--mj-8o26--26-hs5-jeds4-4tz9x-4.i-l11q5--uk5mj-94-8134i5k6q6--5tu-tie4-7--gm4p-8y-99/N_g-..__._____K_g1cXfr4
: ET_..3dCv3j._.-_pP__up.2L_s-o779._-k-N
5.-_--.VEa-_gn.8-c.3: F._oX-F9_.5vN5.25aWx.2aM214_.-C
resource:
current:
averageUtilization: -392406530
averageValue: "183"
value: "93"
name: Ƈè*鑏='ʨ|ǓÓ敆OɈÏ 瞍髃
type: Ǖɳɷ9Ì崟¿瘦ɖ緕
currentReplicas: 267768240
desiredReplicas: -127849333
observedGeneration: -7477362499801752548
averageUtilization: 1190831814
averageValue: "653"
value: "113"
name: 反-n覦灲閈誹ʅ蕉ɼ搳ǭ濑箨ʨ
type: 0Ƹ[Ęİ榌U
currentReplicas: 474119379
desiredReplicas: 1923334396
observedGeneration: -115578794491385044

View File

@ -30,6 +30,8 @@ import (
func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenAPIDefinition {
return map[string]common.OpenAPIDefinition{
"k8s.io/api/autoscaling/v1.ContainerResourceMetricSource": schema_k8sio_api_autoscaling_v1_ContainerResourceMetricSource(ref),
"k8s.io/api/autoscaling/v1.ContainerResourceMetricStatus": schema_k8sio_api_autoscaling_v1_ContainerResourceMetricStatus(ref),
"k8s.io/api/autoscaling/v1.CrossVersionObjectReference": schema_k8sio_api_autoscaling_v1_CrossVersionObjectReference(ref),
"k8s.io/api/autoscaling/v1.ExternalMetricSource": schema_k8sio_api_autoscaling_v1_ExternalMetricSource(ref),
"k8s.io/api/autoscaling/v1.ExternalMetricStatus": schema_k8sio_api_autoscaling_v1_ExternalMetricStatus(ref),
@ -104,6 +106,92 @@ func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenA
}
}
func schema_k8sio_api_autoscaling_v1_ContainerResourceMetricSource(ref common.ReferenceCallback) common.OpenAPIDefinition {
return common.OpenAPIDefinition{
Schema: spec.Schema{
SchemaProps: spec.SchemaProps{
Description: "ContainerResourceMetricSource indicates how to scale on a resource metric known to Kubernetes, as specified in the requests and limits, describing a single container in each of the pods of the current scale target(e.g. CPU or memory). The values will be averaged together before being compared to the target. Such metrics are built into Kubernetes, and have special scaling options on top of those available to normal per-pod metrics using the \"pods\" source. Only one \"target\" type should be set.",
Type: []string{"object"},
Properties: map[string]spec.Schema{
"name": {
SchemaProps: spec.SchemaProps{
Description: "name is the name of the resource in question.",
Type: []string{"string"},
Format: "",
},
},
"targetAverageUtilization": {
SchemaProps: spec.SchemaProps{
Description: "targetAverageUtilization is the target value of the average of the resource metric across all relevant pods, represented as a percentage of the requested value of the resource for the pods.",
Type: []string{"integer"},
Format: "int32",
},
},
"targetAverageValue": {
SchemaProps: spec.SchemaProps{
Description: "targetAverageValue is the target value of the average of the resource metric across all relevant pods, as a raw value (instead of as a percentage of the request), similar to the \"pods\" metric source type.",
Ref: ref("k8s.io/apimachinery/pkg/api/resource.Quantity"),
},
},
"container": {
SchemaProps: spec.SchemaProps{
Description: "container is the name of the container in the pods of the scaling target.",
Type: []string{"string"},
Format: "",
},
},
},
Required: []string{"name", "container"},
},
},
Dependencies: []string{
"k8s.io/apimachinery/pkg/api/resource.Quantity"},
}
}
func schema_k8sio_api_autoscaling_v1_ContainerResourceMetricStatus(ref common.ReferenceCallback) common.OpenAPIDefinition {
return common.OpenAPIDefinition{
Schema: spec.Schema{
SchemaProps: spec.SchemaProps{
Description: "ContainerResourceMetricStatus indicates the current value of a resource metric known to Kubernetes, as specified in requests and limits, describing a single container in each pod in the current scale target (e.g. CPU or memory). Such metrics are built in to Kubernetes, and have special scaling options on top of those available to normal per-pod metrics using the \"pods\" source.",
Type: []string{"object"},
Properties: map[string]spec.Schema{
"name": {
SchemaProps: spec.SchemaProps{
Description: "name is the name of the resource in question.",
Type: []string{"string"},
Format: "",
},
},
"currentAverageUtilization": {
SchemaProps: spec.SchemaProps{
Description: "currentAverageUtilization is the current value of the average of the resource metric across all relevant pods, represented as a percentage of the requested value of the resource for the pods. It will only be present if `targetAverageValue` was set in the corresponding metric specification.",
Type: []string{"integer"},
Format: "int32",
},
},
"currentAverageValue": {
SchemaProps: spec.SchemaProps{
Description: "currentAverageValue is the current value of the average of the resource metric across all relevant pods, as a raw value (instead of as a percentage of the request), similar to the \"pods\" metric source type. It will always be set, regardless of the corresponding metric specification.",
Ref: ref("k8s.io/apimachinery/pkg/api/resource.Quantity"),
},
},
"container": {
SchemaProps: spec.SchemaProps{
Description: "container is the name of the container in the pods of the scaling taget",
Type: []string{"string"},
Format: "",
},
},
},
Required: []string{"name", "currentAverageValue", "container"},
},
},
Dependencies: []string{
"k8s.io/apimachinery/pkg/api/resource.Quantity"},
}
}
func schema_k8sio_api_autoscaling_v1_CrossVersionObjectReference(ref common.ReferenceCallback) common.OpenAPIDefinition {
return common.OpenAPIDefinition{
Schema: spec.Schema{
@ -469,7 +557,7 @@ func schema_k8sio_api_autoscaling_v1_MetricSpec(ref common.ReferenceCallback) co
Properties: map[string]spec.Schema{
"type": {
SchemaProps: spec.SchemaProps{
Description: "type is the type of metric source. It should be one of \"Object\", \"Pods\" or \"Resource\", each mapping to a matching field in the object.",
Description: "type is the type of metric source. It should be one of \"ContainerResource\", \"External\", \"Object\", \"Pods\" or \"Resource\", each mapping to a matching field in the object. Note: \"ContainerResource\" type is available on when the feature-gate HPAContainerMetrics is enabled",
Type: []string{"string"},
Format: "",
},
@ -492,6 +580,12 @@ func schema_k8sio_api_autoscaling_v1_MetricSpec(ref common.ReferenceCallback) co
Ref: ref("k8s.io/api/autoscaling/v1.ResourceMetricSource"),
},
},
"containerResource": {
SchemaProps: spec.SchemaProps{
Description: "container resource refers to a resource metric (such as those specified in requests and limits) known to Kubernetes describing a single container in each pod of the current scale target (e.g. CPU or memory). Such metrics are built in to Kubernetes, and have special scaling options on top of those available to normal per-pod metrics using the \"pods\" source. This is an alpha feature and can be enabled by the HPAContainerMetrics feature flag.",
Ref: ref("k8s.io/api/autoscaling/v1.ContainerResourceMetricSource"),
},
},
"external": {
SchemaProps: spec.SchemaProps{
Description: "external refers to a global metric that is not associated with any Kubernetes object. It allows autoscaling based on information coming from components running outside of cluster (for example length of queue in cloud messaging service, or QPS from loadbalancer running outside of cluster).",
@ -503,7 +597,7 @@ func schema_k8sio_api_autoscaling_v1_MetricSpec(ref common.ReferenceCallback) co
},
},
Dependencies: []string{
"k8s.io/api/autoscaling/v1.ExternalMetricSource", "k8s.io/api/autoscaling/v1.ObjectMetricSource", "k8s.io/api/autoscaling/v1.PodsMetricSource", "k8s.io/api/autoscaling/v1.ResourceMetricSource"},
"k8s.io/api/autoscaling/v1.ContainerResourceMetricSource", "k8s.io/api/autoscaling/v1.ExternalMetricSource", "k8s.io/api/autoscaling/v1.ObjectMetricSource", "k8s.io/api/autoscaling/v1.PodsMetricSource", "k8s.io/api/autoscaling/v1.ResourceMetricSource"},
}
}
@ -516,7 +610,7 @@ func schema_k8sio_api_autoscaling_v1_MetricStatus(ref common.ReferenceCallback)
Properties: map[string]spec.Schema{
"type": {
SchemaProps: spec.SchemaProps{
Description: "type is the type of metric source. It will be one of \"Object\", \"Pods\" or \"Resource\", each corresponds to a matching field in the object.",
Description: "type is the type of metric source. It will be one of \"ContainerResource\", \"External\", \"Object\", \"Pods\" or \"Resource\", each corresponds to a matching field in the object. Note: \"ContainerResource\" type is available on when the feature-gate HPAContainerMetrics is enabled",
Type: []string{"string"},
Format: "",
},
@ -539,6 +633,12 @@ func schema_k8sio_api_autoscaling_v1_MetricStatus(ref common.ReferenceCallback)
Ref: ref("k8s.io/api/autoscaling/v1.ResourceMetricStatus"),
},
},
"containerResource": {
SchemaProps: spec.SchemaProps{
Description: "container resource refers to a resource metric (such as those specified in requests and limits) known to Kubernetes describing a single container in each pod in the current scale target (e.g. CPU or memory). Such metrics are built in to Kubernetes, and have special scaling options on top of those available to normal per-pod metrics using the \"pods\" source.",
Ref: ref("k8s.io/api/autoscaling/v1.ContainerResourceMetricStatus"),
},
},
"external": {
SchemaProps: spec.SchemaProps{
Description: "external refers to a global metric that is not associated with any Kubernetes object. It allows autoscaling based on information coming from components running outside of cluster (for example length of queue in cloud messaging service, or QPS from loadbalancer running outside of cluster).",
@ -550,7 +650,7 @@ func schema_k8sio_api_autoscaling_v1_MetricStatus(ref common.ReferenceCallback)
},
},
Dependencies: []string{
"k8s.io/api/autoscaling/v1.ExternalMetricStatus", "k8s.io/api/autoscaling/v1.ObjectMetricStatus", "k8s.io/api/autoscaling/v1.PodsMetricStatus", "k8s.io/api/autoscaling/v1.ResourceMetricStatus"},
"k8s.io/api/autoscaling/v1.ContainerResourceMetricStatus", "k8s.io/api/autoscaling/v1.ExternalMetricStatus", "k8s.io/api/autoscaling/v1.ObjectMetricStatus", "k8s.io/api/autoscaling/v1.PodsMetricStatus", "k8s.io/api/autoscaling/v1.ResourceMetricStatus"},
}
}

View File

@ -3653,6 +3653,26 @@ func describeHorizontalPodAutoscalerV2beta2(hpa *autoscalingv2beta2.HorizontalPo
}
w.Write(LEVEL_1, "(as a percentage of request):\t%s / %s\n", current, target)
}
case autoscalingv2beta2.ContainerResourceMetricSourceType:
w.Write(LEVEL_1, "resource %s of container \"%s\" on pods", string(metric.ContainerResource.Name), metric.ContainerResource.Container)
if metric.ContainerResource.Target.AverageValue != nil {
current := "<unknown>"
if len(hpa.Status.CurrentMetrics) > i && hpa.Status.CurrentMetrics[i].ContainerResource != nil {
current = hpa.Status.CurrentMetrics[i].ContainerResource.Current.AverageValue.String()
}
w.Write(LEVEL_0, ":\t%s / %s\n", current, metric.ContainerResource.Target.AverageValue.String())
} else {
current := "<unknown>"
if len(hpa.Status.CurrentMetrics) > i && hpa.Status.CurrentMetrics[i].ContainerResource != nil && hpa.Status.CurrentMetrics[i].ContainerResource.Current.AverageUtilization != nil {
current = fmt.Sprintf("%d%% (%s)", *hpa.Status.CurrentMetrics[i].ContainerResource.Current.AverageUtilization, hpa.Status.CurrentMetrics[i].ContainerResource.Current.AverageValue.String())
}
target := "<auto>"
if metric.ContainerResource.Target.AverageUtilization != nil {
target = fmt.Sprintf("%d%%", *metric.ContainerResource.Target.AverageUtilization)
}
w.Write(LEVEL_1, "(as a percentage of request):\t%s / %s\n", current, target)
}
default:
w.Write(LEVEL_1, "<unknown metric type %q>\n", string(metric.Type))
}

View File

@ -2711,6 +2711,152 @@ func TestDescribeHorizontalPodAutoscaler(t *testing.T) {
},
},
},
{
"container resource source type, target average value (no current)",
autoscalingv2beta2.HorizontalPodAutoscaler{
Spec: autoscalingv2beta2.HorizontalPodAutoscalerSpec{
ScaleTargetRef: autoscalingv2beta2.CrossVersionObjectReference{
Name: "some-rc",
Kind: "ReplicationController",
},
MinReplicas: &minReplicasVal,
MaxReplicas: 10,
Metrics: []autoscalingv2beta2.MetricSpec{
{
Type: autoscalingv2beta2.ContainerResourceMetricSourceType,
ContainerResource: &autoscalingv2beta2.ContainerResourceMetricSource{
Name: corev1.ResourceCPU,
Container: "application",
Target: autoscalingv2beta2.MetricTarget{
Type: autoscalingv2beta2.AverageValueMetricType,
AverageValue: resource.NewMilliQuantity(100, resource.DecimalSI),
},
},
},
},
},
Status: autoscalingv2beta2.HorizontalPodAutoscalerStatus{
CurrentReplicas: 4,
DesiredReplicas: 5,
},
},
},
{
"container resource source type, target average value (with current)",
autoscalingv2beta2.HorizontalPodAutoscaler{
Spec: autoscalingv2beta2.HorizontalPodAutoscalerSpec{
ScaleTargetRef: autoscalingv2beta2.CrossVersionObjectReference{
Name: "some-rc",
Kind: "ReplicationController",
},
MinReplicas: &minReplicasVal,
MaxReplicas: 10,
Metrics: []autoscalingv2beta2.MetricSpec{
{
Type: autoscalingv2beta2.ContainerResourceMetricSourceType,
ContainerResource: &autoscalingv2beta2.ContainerResourceMetricSource{
Name: corev1.ResourceCPU,
Container: "application",
Target: autoscalingv2beta2.MetricTarget{
Type: autoscalingv2beta2.AverageValueMetricType,
AverageValue: resource.NewMilliQuantity(100, resource.DecimalSI),
},
},
},
},
},
Status: autoscalingv2beta2.HorizontalPodAutoscalerStatus{
CurrentReplicas: 4,
DesiredReplicas: 5,
CurrentMetrics: []autoscalingv2beta2.MetricStatus{
{
Type: autoscalingv2beta2.ContainerResourceMetricSourceType,
ContainerResource: &autoscalingv2beta2.ContainerResourceMetricStatus{
Name: corev1.ResourceCPU,
Container: "application",
Current: autoscalingv2beta2.MetricValueStatus{
AverageValue: resource.NewMilliQuantity(50, resource.DecimalSI),
},
},
},
},
},
},
},
{
"container resource source type, target utilization (no current)",
autoscalingv2beta2.HorizontalPodAutoscaler{
Spec: autoscalingv2beta2.HorizontalPodAutoscalerSpec{
ScaleTargetRef: autoscalingv2beta2.CrossVersionObjectReference{
Name: "some-rc",
Kind: "ReplicationController",
},
MinReplicas: &minReplicasVal,
MaxReplicas: 10,
Metrics: []autoscalingv2beta2.MetricSpec{
{
Type: autoscalingv2beta2.ContainerResourceMetricSourceType,
ContainerResource: &autoscalingv2beta2.ContainerResourceMetricSource{
Name: corev1.ResourceCPU,
Container: "application",
Target: autoscalingv2beta2.MetricTarget{
Type: autoscalingv2beta2.UtilizationMetricType,
AverageUtilization: &targetUtilizationVal,
},
},
},
},
},
Status: autoscalingv2beta2.HorizontalPodAutoscalerStatus{
CurrentReplicas: 4,
DesiredReplicas: 5,
},
},
},
{
"container resource source type, target utilization (with current)",
autoscalingv2beta2.HorizontalPodAutoscaler{
Spec: autoscalingv2beta2.HorizontalPodAutoscalerSpec{
ScaleTargetRef: autoscalingv2beta2.CrossVersionObjectReference{
Name: "some-rc",
Kind: "ReplicationController",
},
MinReplicas: &minReplicasVal,
MaxReplicas: 10,
Metrics: []autoscalingv2beta2.MetricSpec{
{
Type: autoscalingv2beta2.ContainerResourceMetricSourceType,
ContainerResource: &autoscalingv2beta2.ContainerResourceMetricSource{
Name: corev1.ResourceCPU,
Container: "application",
Target: autoscalingv2beta2.MetricTarget{
Type: autoscalingv2beta2.UtilizationMetricType,
AverageUtilization: &targetUtilizationVal,
},
},
},
},
},
Status: autoscalingv2beta2.HorizontalPodAutoscalerStatus{
CurrentReplicas: 4,
DesiredReplicas: 5,
CurrentMetrics: []autoscalingv2beta2.MetricStatus{
{
Type: autoscalingv2beta2.ContainerResourceMetricSourceType,
ContainerResource: &autoscalingv2beta2.ContainerResourceMetricStatus{
Name: corev1.ResourceCPU,
Container: "application",
Current: autoscalingv2beta2.MetricValueStatus{
AverageUtilization: &currentUtilizationVal,
AverageValue: resource.NewMilliQuantity(40, resource.DecimalSI),
},
},
},
},
},
},
},
{
"multiple metrics",
autoscalingv2beta2.HorizontalPodAutoscaler{