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" "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": { "io.k8s.api.autoscaling.v2beta1.CrossVersionObjectReference": {
"description": "CrossVersionObjectReference contains enough information to let you identify the referred resource.", "description": "CrossVersionObjectReference contains enough information to let you identify the referred resource.",
"properties": { "properties": {
@ -3273,6 +3328,10 @@
"io.k8s.api.autoscaling.v2beta1.MetricSpec": { "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).", "description": "MetricSpec specifies how to scale based on a single metric (only `type` and one other matching field should be set at once).",
"properties": { "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": { "external": {
"$ref": "#/definitions/io.k8s.api.autoscaling.v2beta1.ExternalMetricSource", "$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)." "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." "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": { "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" "type": "string"
} }
}, },
@ -3302,6 +3361,10 @@
"io.k8s.api.autoscaling.v2beta1.MetricStatus": { "io.k8s.api.autoscaling.v2beta1.MetricStatus": {
"description": "MetricStatus describes the last-read state of a single metric.", "description": "MetricStatus describes the last-read state of a single metric.",
"properties": { "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": { "external": {
"$ref": "#/definitions/io.k8s.api.autoscaling.v2beta1.ExternalMetricStatus", "$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)." "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." "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": { "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" "type": "string"
} }
}, },
@ -3479,6 +3542,52 @@
], ],
"type": "object" "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": { "io.k8s.api.autoscaling.v2beta2.CrossVersionObjectReference": {
"description": "CrossVersionObjectReference contains enough information to let you identify the referred resource.", "description": "CrossVersionObjectReference contains enough information to let you identify the referred resource.",
"properties": { "properties": {
@ -3795,6 +3904,10 @@
"io.k8s.api.autoscaling.v2beta2.MetricSpec": { "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).", "description": "MetricSpec specifies how to scale based on a single metric (only `type` and one other matching field should be set at once).",
"properties": { "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": { "external": {
"$ref": "#/definitions/io.k8s.api.autoscaling.v2beta2.ExternalMetricSource", "$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)." "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." "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": { "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" "type": "string"
} }
}, },
@ -3824,6 +3937,10 @@
"io.k8s.api.autoscaling.v2beta2.MetricStatus": { "io.k8s.api.autoscaling.v2beta2.MetricStatus": {
"description": "MetricStatus describes the last-read state of a single metric.", "description": "MetricStatus describes the last-read state of a single metric.",
"properties": { "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": { "external": {
"$ref": "#/definitions/io.k8s.api.autoscaling.v2beta2.ExternalMetricStatus", "$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)." "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." "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": { "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" "type": "string"
} }
}, },

View File

@ -204,6 +204,12 @@ const (
// (for example length of queue in cloud messaging service, or // (for example length of queue in cloud messaging service, or
// QPS from loadbalancer running outside of cluster). // QPS from loadbalancer running outside of cluster).
ExternalMetricSourceType MetricSourceType = "External" 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 // 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. // to normal per-pod metrics using the "pods" source.
// +optional // +optional
Resource *ResourceMetricSource 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 // External refers to a global metric that is not associated
// with any Kubernetes object. It allows autoscaling based on information // with any Kubernetes object. It allows autoscaling based on information
// coming from components running outside of cluster // coming from components running outside of cluster
@ -271,6 +284,22 @@ type ResourceMetricSource struct {
Target MetricTarget 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 // ExternalMetricSource indicates how to scale on a metric not associated with
// any Kubernetes object (for example length of queue in cloud // any Kubernetes object (for example length of queue in cloud
// messaging service, or QPS from loadbalancer running outside of cluster). // 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. // to normal per-pod metrics using the "pods" source.
// +optional // +optional
Resource *ResourceMetricStatus 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 // External refers to a global metric that is not associated
// with any Kubernetes object. It allows autoscaling based on information // with any Kubernetes object. It allows autoscaling based on information
// coming from components running outside of cluster // coming from components running outside of cluster
@ -456,6 +492,18 @@ type ResourceMetricStatus struct {
Current MetricValueStatus 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 // ExternalMetricStatus indicates the current value of a global metric
// not associated with any Kubernetes object. // not associated with any Kubernetes object.
type ExternalMetricStatus struct { type ExternalMetricStatus struct {

View File

@ -235,6 +235,28 @@ func Convert_autoscaling_ResourceMetricSource_To_v1_ResourceMetricSource(in *aut
return nil 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 { func Convert_v1_ResourceMetricStatus_To_autoscaling_ResourceMetricStatus(in *autoscalingv1.ResourceMetricStatus, out *autoscaling.ResourceMetricStatus, s conversion.Scope) error {
out.Name = core.ResourceName(in.Name) out.Name = core.ResourceName(in.Name)
utilization := in.CurrentAverageUtilization utilization := in.CurrentAverageUtilization
@ -517,3 +539,30 @@ func Convert_v1_HorizontalPodAutoscalerStatus_To_autoscaling_HorizontalPodAutosc
} }
return nil 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 { }); err != nil {
return err 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 { 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) return Convert_autoscaling_ExternalMetricSource_To_v1_ExternalMetricSource(a.(*autoscaling.ExternalMetricSource), b.(*v1.ExternalMetricSource), scope)
}); err != nil { }); err != nil {
@ -179,6 +189,16 @@ func RegisterConversions(s *runtime.Scheme) error {
}); err != nil { }); err != nil {
return err 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 { 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) return Convert_v1_CrossVersionObjectReference_To_autoscaling_MetricTarget(a.(*v1.CrossVersionObjectReference), b.(*autoscaling.MetricTarget), scope)
}); err != nil { }); err != nil {
@ -242,6 +262,36 @@ func RegisterConversions(s *runtime.Scheme) error {
return nil 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 { func autoConvert_v1_CrossVersionObjectReference_To_autoscaling_CrossVersionObjectReference(in *v1.CrossVersionObjectReference, out *autoscaling.CrossVersionObjectReference, s conversion.Scope) error {
out.Kind = in.Kind out.Kind = in.Kind
out.Name = in.Name out.Name = in.Name
@ -455,6 +505,15 @@ func autoConvert_v1_MetricSpec_To_autoscaling_MetricSpec(in *v1.MetricSpec, out
} else { } else {
out.Resource = nil 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 { if in.External != nil {
in, out := &in.External, &out.External in, out := &in.External, &out.External
*out = new(autoscaling.ExternalMetricSource) *out = new(autoscaling.ExternalMetricSource)
@ -501,6 +560,15 @@ func autoConvert_autoscaling_MetricSpec_To_v1_MetricSpec(in *autoscaling.MetricS
} else { } else {
out.Resource = nil 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 { if in.External != nil {
in, out := &in.External, &out.External in, out := &in.External, &out.External
*out = new(v1.ExternalMetricSource) *out = new(v1.ExternalMetricSource)
@ -547,6 +615,15 @@ func autoConvert_v1_MetricStatus_To_autoscaling_MetricStatus(in *v1.MetricStatus
} else { } else {
out.Resource = nil 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 { if in.External != nil {
in, out := &in.External, &out.External in, out := &in.External, &out.External
*out = new(autoscaling.ExternalMetricStatus) *out = new(autoscaling.ExternalMetricStatus)
@ -593,6 +670,15 @@ func autoConvert_autoscaling_MetricStatus_To_v1_MetricStatus(in *autoscaling.Met
} else { } else {
out.Resource = nil 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 { if in.External != nil {
in, out := &in.External, &out.External in, out := &in.External, &out.External
*out = new(v1.ExternalMetricStatus) *out = new(v1.ExternalMetricStatus)

View File

@ -35,6 +35,28 @@ func Convert_v2beta1_CrossVersionObjectReference_To_autoscaling_MetricTarget(in
return nil 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 { func Convert_v2beta1_ResourceMetricStatus_To_autoscaling_ResourceMetricStatus(in *autoscalingv2beta1.ResourceMetricStatus, out *autoscaling.ResourceMetricStatus, s conversion.Scope) error {
out.Name = core.ResourceName(in.Name) out.Name = core.ResourceName(in.Name)
utilization := in.CurrentAverageUtilization 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 { 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) 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 { }); err != nil {
return err 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 { 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) return Convert_autoscaling_ExternalMetricSource_To_v2beta1_ExternalMetricSource(a.(*autoscaling.ExternalMetricSource), b.(*v2beta1.ExternalMetricSource), scope)
}); err != nil { }); err != nil {
@ -159,6 +169,16 @@ func RegisterConversions(s *runtime.Scheme) error {
}); err != nil { }); err != nil {
return err 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 { 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) return Convert_v2beta1_CrossVersionObjectReference_To_autoscaling_MetricTarget(a.(*v2beta1.CrossVersionObjectReference), b.(*autoscaling.MetricTarget), scope)
}); err != nil { }); err != nil {
@ -212,6 +232,36 @@ func RegisterConversions(s *runtime.Scheme) error {
return nil 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 { func autoConvert_v2beta1_CrossVersionObjectReference_To_autoscaling_CrossVersionObjectReference(in *v2beta1.CrossVersionObjectReference, out *autoscaling.CrossVersionObjectReference, s conversion.Scope) error {
out.Kind = in.Kind out.Kind = in.Kind
out.Name = in.Name out.Name = in.Name
@ -481,6 +531,15 @@ func autoConvert_v2beta1_MetricSpec_To_autoscaling_MetricSpec(in *v2beta1.Metric
} else { } else {
out.Resource = nil 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 { if in.External != nil {
in, out := &in.External, &out.External in, out := &in.External, &out.External
*out = new(autoscaling.ExternalMetricSource) *out = new(autoscaling.ExternalMetricSource)
@ -527,6 +586,15 @@ func autoConvert_autoscaling_MetricSpec_To_v2beta1_MetricSpec(in *autoscaling.Me
} else { } else {
out.Resource = nil 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 { if in.External != nil {
in, out := &in.External, &out.External in, out := &in.External, &out.External
*out = new(v2beta1.ExternalMetricSource) *out = new(v2beta1.ExternalMetricSource)
@ -573,6 +641,15 @@ func autoConvert_v2beta1_MetricStatus_To_autoscaling_MetricStatus(in *v2beta1.Me
} else { } else {
out.Resource = nil 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 { if in.External != nil {
in, out := &in.External, &out.External in, out := &in.External, &out.External
*out = new(autoscaling.ExternalMetricStatus) *out = new(autoscaling.ExternalMetricStatus)
@ -619,6 +696,15 @@ func autoConvert_autoscaling_MetricStatus_To_v2beta1_MetricStatus(in *autoscalin
} else { } else {
out.Resource = nil 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 { if in.External != nil {
in, out := &in.External, &out.External in, out := &in.External, &out.External
*out = new(v2beta1.ExternalMetricStatus) *out = new(v2beta1.ExternalMetricStatus)

View File

@ -40,6 +40,26 @@ func init() {
// RegisterConversions adds conversion functions to the given scheme. // RegisterConversions adds conversion functions to the given scheme.
// Public to allow building arbitrary schemes. // Public to allow building arbitrary schemes.
func RegisterConversions(s *runtime.Scheme) error { 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 { 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) return Convert_v2beta2_CrossVersionObjectReference_To_autoscaling_CrossVersionObjectReference(a.(*v2beta2.CrossVersionObjectReference), b.(*autoscaling.CrossVersionObjectReference), scope)
}); err != nil { }); err != nil {
@ -263,6 +283,62 @@ func RegisterConversions(s *runtime.Scheme) error {
return nil 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 { func autoConvert_v2beta2_CrossVersionObjectReference_To_autoscaling_CrossVersionObjectReference(in *v2beta2.CrossVersionObjectReference, out *autoscaling.CrossVersionObjectReference, s conversion.Scope) error {
out.Kind = in.Kind out.Kind = in.Kind
out.Name = in.Name out.Name = in.Name
@ -515,7 +591,17 @@ func autoConvert_v2beta2_HorizontalPodAutoscalerSpec_To_autoscaling_HorizontalPo
} }
out.MinReplicas = (*int32)(unsafe.Pointer(in.MinReplicas)) out.MinReplicas = (*int32)(unsafe.Pointer(in.MinReplicas))
out.MaxReplicas = in.MaxReplicas 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)) out.Behavior = (*autoscaling.HorizontalPodAutoscalerBehavior)(unsafe.Pointer(in.Behavior))
return nil return nil
} }
@ -531,7 +617,17 @@ func autoConvert_autoscaling_HorizontalPodAutoscalerSpec_To_v2beta2_HorizontalPo
} }
out.MinReplicas = (*int32)(unsafe.Pointer(in.MinReplicas)) out.MinReplicas = (*int32)(unsafe.Pointer(in.MinReplicas))
out.MaxReplicas = in.MaxReplicas 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)) out.Behavior = (*v2beta2.HorizontalPodAutoscalerBehavior)(unsafe.Pointer(in.Behavior))
return nil return nil
} }
@ -546,7 +642,17 @@ func autoConvert_v2beta2_HorizontalPodAutoscalerStatus_To_autoscaling_Horizontal
out.LastScaleTime = (*metav1.Time)(unsafe.Pointer(in.LastScaleTime)) out.LastScaleTime = (*metav1.Time)(unsafe.Pointer(in.LastScaleTime))
out.CurrentReplicas = in.CurrentReplicas out.CurrentReplicas = in.CurrentReplicas
out.DesiredReplicas = in.DesiredReplicas 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)) out.Conditions = *(*[]autoscaling.HorizontalPodAutoscalerCondition)(unsafe.Pointer(&in.Conditions))
return nil return nil
} }
@ -561,7 +667,17 @@ func autoConvert_autoscaling_HorizontalPodAutoscalerStatus_To_v2beta2_Horizontal
out.LastScaleTime = (*metav1.Time)(unsafe.Pointer(in.LastScaleTime)) out.LastScaleTime = (*metav1.Time)(unsafe.Pointer(in.LastScaleTime))
out.CurrentReplicas = in.CurrentReplicas out.CurrentReplicas = in.CurrentReplicas
out.DesiredReplicas = in.DesiredReplicas 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)) out.Conditions = *(*[]v2beta2.HorizontalPodAutoscalerCondition)(unsafe.Pointer(&in.Conditions))
return nil 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.Object = (*autoscaling.ObjectMetricSource)(unsafe.Pointer(in.Object))
out.Pods = (*autoscaling.PodsMetricSource)(unsafe.Pointer(in.Pods)) out.Pods = (*autoscaling.PodsMetricSource)(unsafe.Pointer(in.Pods))
out.Resource = (*autoscaling.ResourceMetricSource)(unsafe.Pointer(in.Resource)) 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)) out.External = (*autoscaling.ExternalMetricSource)(unsafe.Pointer(in.External))
return nil 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.Object = (*v2beta2.ObjectMetricSource)(unsafe.Pointer(in.Object))
out.Pods = (*v2beta2.PodsMetricSource)(unsafe.Pointer(in.Pods)) out.Pods = (*v2beta2.PodsMetricSource)(unsafe.Pointer(in.Pods))
out.Resource = (*v2beta2.ResourceMetricSource)(unsafe.Pointer(in.Resource)) 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)) out.External = (*v2beta2.ExternalMetricSource)(unsafe.Pointer(in.External))
return nil 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.Object = (*autoscaling.ObjectMetricStatus)(unsafe.Pointer(in.Object))
out.Pods = (*autoscaling.PodsMetricStatus)(unsafe.Pointer(in.Pods)) out.Pods = (*autoscaling.PodsMetricStatus)(unsafe.Pointer(in.Pods))
out.Resource = (*autoscaling.ResourceMetricStatus)(unsafe.Pointer(in.Resource)) 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)) out.External = (*autoscaling.ExternalMetricStatus)(unsafe.Pointer(in.External))
return nil return nil
} }
@ -640,6 +783,15 @@ func autoConvert_autoscaling_MetricStatus_To_v2beta2_MetricStatus(in *autoscalin
out.Object = (*v2beta2.ObjectMetricStatus)(unsafe.Pointer(in.Object)) out.Object = (*v2beta2.ObjectMetricStatus)(unsafe.Pointer(in.Object))
out.Pods = (*v2beta2.PodsMetricStatus)(unsafe.Pointer(in.Pods)) out.Pods = (*v2beta2.PodsMetricStatus)(unsafe.Pointer(in.Pods))
out.Resource = (*v2beta2.ResourceMetricStatus)(unsafe.Pointer(in.Resource)) 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)) out.External = (*v2beta2.ExternalMetricStatus)(unsafe.Pointer(in.External))
return nil return nil
} }

View File

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

View File

@ -18,14 +18,13 @@ package validation
import ( import (
"fmt" "fmt"
"strings"
apimachineryvalidation "k8s.io/apimachinery/pkg/api/validation" apimachineryvalidation "k8s.io/apimachinery/pkg/api/validation"
pathvalidation "k8s.io/apimachinery/pkg/api/validation/path" pathvalidation "k8s.io/apimachinery/pkg/api/validation/path"
"k8s.io/apimachinery/pkg/util/sets" "k8s.io/apimachinery/pkg/util/sets"
"k8s.io/apimachinery/pkg/util/validation/field" "k8s.io/apimachinery/pkg/util/validation/field"
utilfeature "k8s.io/apiserver/pkg/util/feature" utilfeature "k8s.io/apiserver/pkg/util/feature"
"k8s.io/kubernetes/pkg/apis/autoscaling" "k8s.io/kubernetes/pkg/apis/autoscaling"
corevalidation "k8s.io/kubernetes/pkg/apis/core/v1/validation"
apivalidation "k8s.io/kubernetes/pkg/apis/core/validation" apivalidation "k8s.io/kubernetes/pkg/apis/core/validation"
"k8s.io/kubernetes/pkg/features" "k8s.io/kubernetes/pkg/features"
) )
@ -239,7 +238,10 @@ func validateScalingPolicy(policy autoscaling.HPAScalingPolicy, fldPath *field.P
return allErrs 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() var validMetricSourceTypesList = validMetricSourceTypes.List()
func validateMetricSpec(spec autoscaling.MetricSpec, fldPath *field.Path) field.ErrorList { 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) { var expectedField string
allErrs = append(allErrs, field.Required(fldPath.Child(expectedField), "must populate information for the given metric source")) 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 { if typesPresent.Len() != 1 {
@ -342,6 +381,34 @@ func validatePodsSource(src *autoscaling.PodsMetricSource, fldPath *field.Path)
return allErrs 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 { func validateResourceSource(src *autoscaling.ResourceMetricSource, fldPath *field.Path) field.ErrorList {
allErrs := 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{ ObjectMeta: metav1.ObjectMeta{
Name: "myautoscaler", Name: "myautoscaler",
@ -689,6 +743,30 @@ func TestValidateHorizontalPodAutoscaler(t *testing.T) {
}, },
msg: "scaleTargetRef.kind: Required", 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{ horizontalPodAutoscaler: autoscaling.HorizontalPodAutoscaler{
ObjectMeta: metav1.ObjectMeta{Name: "myautoscaler", Namespace: metav1.NamespaceDefault}, ObjectMeta: metav1.ObjectMeta{Name: "myautoscaler", Namespace: metav1.NamespaceDefault},
@ -712,6 +790,30 @@ func TestValidateHorizontalPodAutoscaler(t *testing.T) {
}, },
msg: "scaleTargetRef.kind: Invalid", 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{ horizontalPodAutoscaler: autoscaling.HorizontalPodAutoscaler{
ObjectMeta: metav1.ObjectMeta{Name: "myautoscaler", Namespace: metav1.NamespaceDefault}, ObjectMeta: metav1.ObjectMeta{Name: "myautoscaler", Namespace: metav1.NamespaceDefault},
@ -735,6 +837,30 @@ func TestValidateHorizontalPodAutoscaler(t *testing.T) {
}, },
msg: "scaleTargetRef.name: Required", 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{ horizontalPodAutoscaler: autoscaling.HorizontalPodAutoscaler{
ObjectMeta: metav1.ObjectMeta{Name: "myautoscaler", Namespace: metav1.NamespaceDefault}, ObjectMeta: metav1.ObjectMeta{Name: "myautoscaler", Namespace: metav1.NamespaceDefault},
@ -758,6 +884,30 @@ func TestValidateHorizontalPodAutoscaler(t *testing.T) {
}, },
msg: "scaleTargetRef.name: Invalid", 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{ horizontalPodAutoscaler: autoscaling.HorizontalPodAutoscaler{
ObjectMeta: metav1.ObjectMeta{ 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", 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{ horizontalPodAutoscaler: autoscaling.HorizontalPodAutoscaler{
ObjectMeta: metav1.ObjectMeta{Name: "myautoscaler", Namespace: metav1.NamespaceDefault}, ObjectMeta: metav1.ObjectMeta{Name: "myautoscaler", Namespace: metav1.NamespaceDefault},
@ -835,6 +1013,53 @@ func TestValidateHorizontalPodAutoscaler(t *testing.T) {
}, },
msg: "must specify a resource name", 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{ horizontalPodAutoscaler: autoscaling.HorizontalPodAutoscaler{
ObjectMeta: metav1.ObjectMeta{Name: "myautoscaler", Namespace: metav1.NamespaceDefault}, ObjectMeta: metav1.ObjectMeta{Name: "myautoscaler", Namespace: metav1.NamespaceDefault},
@ -858,6 +1083,77 @@ func TestValidateHorizontalPodAutoscaler(t *testing.T) {
}, },
msg: "must be greater than 0", 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{ horizontalPodAutoscaler: autoscaling.HorizontalPodAutoscaler{
ObjectMeta: metav1.ObjectMeta{Name: "myautoscaler", Namespace: metav1.NamespaceDefault}, 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", 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{ horizontalPodAutoscaler: autoscaling.HorizontalPodAutoscaler{
ObjectMeta: metav1.ObjectMeta{Name: "myautoscaler", Namespace: metav1.NamespaceDefault}, 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: { autoscaling.PodsMetricSourceType: {
Pods: &autoscaling.PodsMetricSource{ Pods: &autoscaling.PodsMetricSource{
Metric: autoscaling.MetricIdentifier{ Metric: autoscaling.MetricIdentifier{

View File

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

View File

@ -42,7 +42,7 @@ func ValidateResourceRequirements(requirements *v1.ResourceRequirements, fldPath
for resourceName, quantity := range requirements.Limits { for resourceName, quantity := range requirements.Limits {
fldPath := limPath.Key(string(resourceName)) fldPath := limPath.Key(string(resourceName))
// Validate resource name. // Validate resource name.
allErrs = append(allErrs, validateContainerResourceName(string(resourceName), fldPath)...) allErrs = append(allErrs, ValidateContainerResourceName(string(resourceName), fldPath)...)
// Validate resource quantity. // Validate resource quantity.
allErrs = append(allErrs, ValidateResourceQuantityValue(string(resourceName), quantity, fldPath)...) allErrs = append(allErrs, ValidateResourceQuantityValue(string(resourceName), quantity, fldPath)...)
@ -51,7 +51,7 @@ func ValidateResourceRequirements(requirements *v1.ResourceRequirements, fldPath
for resourceName, quantity := range requirements.Requests { for resourceName, quantity := range requirements.Requests {
fldPath := reqPath.Key(string(resourceName)) fldPath := reqPath.Key(string(resourceName))
// Validate resource name. // Validate resource name.
allErrs = append(allErrs, validateContainerResourceName(string(resourceName), fldPath)...) allErrs = append(allErrs, ValidateContainerResourceName(string(resourceName), fldPath)...)
// Validate resource quantity. // Validate resource quantity.
allErrs = append(allErrs, ValidateResourceQuantityValue(string(resourceName), quantity, fldPath)...) allErrs = append(allErrs, ValidateResourceQuantityValue(string(resourceName), quantity, fldPath)...)
@ -70,7 +70,8 @@ func ValidateResourceRequirements(requirements *v1.ResourceRequirements, fldPath
return allErrs 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) allErrs := validateResourceName(value, fldPath)
if len(strings.Split(value, "/")) == 1 { if len(strings.Split(value, "/")) == 1 {
if !helper.IsStandardContainerResourceName(value) { 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) { func TestValidatePodLogOptions(t *testing.T) {
var ( var (

View File

@ -329,6 +329,11 @@ func (a *HorizontalController) computeReplicasForMetric(hpa *autoscalingv2.Horiz
if err != nil { if err != nil {
return 0, "", time.Time{}, condition, err 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: case autoscalingv2.ExternalMetricSourceType:
replicaCountProposal, timestampProposal, metricNameProposal, condition, err = a.computeStatusForExternalMetric(specReplicas, statusReplicas, spec, hpa, selector, status) replicaCountProposal, timestampProposal, metricNameProposal, condition, err = a.computeStatusForExternalMetric(specReplicas, statusReplicas, spec, hpa, selector, status)
if err != nil { 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 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) computeStatusForResourceMetricGeneric(currentReplicas int32, target autoscalingv2.MetricTarget,
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) { resourceName v1.ResourceName, namespace string, container string, selector labels.Selector) (replicaCountProposal int32,
if metricSpec.Resource.Target.AverageValue != nil { metricStatus *autoscalingv2.MetricValueStatus, timestampProposal time.Time, metricNameProposal string,
condition autoscalingv2.HorizontalPodAutoscalerCondition, err error) {
if target.AverageValue != nil {
var rawProposal int64 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 { if err != nil {
condition = a.getUnableComputeReplicaCountCondition(hpa, "FailedGetResourceMetric", err) return 0, nil, time.Time{}, "", condition, fmt.Errorf("failed to get %s utilization: %v", resourceName, err)
return 0, time.Time{}, "", condition, fmt.Errorf("failed to get %s utilization: %v", metricSpec.Resource.Name, err)
} }
metricNameProposal = fmt.Sprintf("%s resource", metricSpec.Resource.Name) metricNameProposal = fmt.Sprintf("%s resource", resourceName.String())
*status = autoscalingv2.MetricStatus{ status := autoscalingv2.MetricValueStatus{
Type: autoscalingv2.ResourceMetricSourceType, AverageValue: resource.NewMilliQuantity(rawProposal, resource.DecimalSI),
Resource: &autoscalingv2.ResourceMetricStatus{
Name: metricSpec.Resource.Name,
Current: 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" errMsg := "invalid resource metric source: neither a utilization target nor a value target was set"
err = fmt.Errorf(errMsg) err = fmt.Errorf(errMsg)
condition = a.getUnableComputeReplicaCountCondition(hpa, "FailedGetResourceMetric", err) return 0, nil, time.Time{}, "", condition, fmt.Errorf(errMsg)
return 0, 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 { if err != nil {
condition = a.getUnableComputeReplicaCountCondition(hpa, "FailedGetResourceMetric", err) 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{ *status = autoscalingv2.MetricStatus{
Type: autoscalingv2.ResourceMetricSourceType, Type: autoscalingv2.ResourceMetricSourceType,
Resource: &autoscalingv2.ResourceMetricStatus{ Resource: &autoscalingv2.ResourceMetricStatus{
Name: metricSpec.Resource.Name, Name: metricSpec.Resource.Name,
Current: autoscalingv2.MetricValueStatus{ Current: *metricValueStatus,
AverageUtilization: &percentageProposal,
AverageValue: resource.NewMilliQuantity(rawProposal, resource.DecimalSI),
},
}, },
} }
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. // 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()) a.eventRecorder.Event(hpa, v1.EventTypeWarning, reason, err.Error())
return autoscalingv2.HorizontalPodAutoscalerCondition{ return autoscalingv2.HorizontalPodAutoscalerCondition{
Type: autoscalingv2.ScalingActive, Type: autoscalingv2.ScalingActive,

View File

@ -336,9 +336,18 @@ func (tc *testCase) prepareTestClient(t *testing.T) (*fake.Clientset, *metricsfa
Spec: v1.PodSpec{ Spec: v1.PodSpec{
Containers: []v1.Container{ Containers: []v1.Container{
{ {
Name: "container1",
Resources: v1.ResourceRequirements{ Resources: v1.ResourceRequirements{
Requests: v1.ResourceList{ 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}, Window: metav1.Duration{Duration: time.Minute},
Containers: []metricsapi.ContainerMetrics{ Containers: []metricsapi.ContainerMetrics{
{ {
Name: "container", Name: "container1",
Usage: v1.ResourceList{ Usage: v1.ResourceList{
v1.ResourceCPU: *resource.NewMilliQuantity( v1.ResourceCPU: *resource.NewMilliQuantity(
int64(cpu), int64(cpu/2),
resource.DecimalSI), resource.DecimalSI),
v1.ResourceMemory: *resource.NewQuantity( 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), resource.BinarySI),
}, },
}, },
@ -773,6 +793,31 @@ func TestScaleUp(t *testing.T) {
tc.runTest(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) { func TestScaleUpUnreadyLessScale(t *testing.T) {
tc := testCase{ tc := testCase{
minReplicas: 2, minReplicas: 2,
@ -1269,6 +1314,32 @@ func TestScaleDown(t *testing.T) {
tc.runTest(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) { func TestScaleDownWithScalingRules(t *testing.T) {
tc := testCase{ tc := testCase{
minReplicas: 2, minReplicas: 2,
@ -2809,7 +2880,7 @@ func TestScaleDownRCImmediately(t *testing.T) {
tc.runTest(t) tc.runTest(t)
} }
func TestAvoidUncessaryUpdates(t *testing.T) { func TestAvoidUnnecessaryUpdates(t *testing.T) {
now := metav1.Time{Time: time.Now().Add(-time.Hour)} now := metav1.Time{Time: time.Now().Add(-time.Hour)}
tc := testCase{ tc := testCase{
minReplicas: 2, minReplicas: 2,

View File

@ -126,14 +126,11 @@ func (tc *legacyReplicaCalcTestCase) prepareTestClient(t *testing.T) *fake.Clien
Timestamp: metav1.Time{Time: tc.timestamp}, Timestamp: metav1.Time{Time: tc.timestamp},
Containers: make([]metricsapi.ContainerMetrics, numContainersPerPod), Containers: make([]metricsapi.ContainerMetrics, numContainersPerPod),
} }
for i, m := range resValue {
for i := 0; i < numContainersPerPod; i++ {
podMetric.Containers[i] = metricsapi.ContainerMetrics{ podMetric.Containers[i] = metricsapi.ContainerMetrics{
Name: fmt.Sprintf("container%v", i), Name: fmt.Sprintf("container%v", i),
Usage: v1.ResourceList{ Usage: v1.ResourceList{
v1.ResourceName(tc.resource.name): *resource.NewMilliQuantity( tc.resource.name: *resource.NewMilliQuantity(m, resource.DecimalSI),
int64(resValue),
resource.DecimalSI),
}, },
} }
} }
@ -209,7 +206,7 @@ func (tc *legacyReplicaCalcTestCase) runTest(t *testing.T) {
} }
if tc.resource != nil { 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 { if tc.expectedError != nil {
require.Error(t, err, "there should be an error calculating the replica count") require.Error(t, err, "there should be an error calculating the replica count")
@ -244,7 +241,7 @@ func TestLegacyReplicaCalcDisjointResourcesMetrics(t *testing.T) {
resource: &resourceInfo{ resource: &resourceInfo{
name: v1.ResourceCPU, name: v1.ResourceCPU,
requests: []resource.Quantity{resource.MustParse("1.0")}, requests: []resource.Quantity{resource.MustParse("1.0")},
levels: []int64{100}, levels: makePodMetricLevels(100),
podNames: []string{"an-older-pod-name"}, podNames: []string{"an-older-pod-name"},
targetUtilization: 100, targetUtilization: 100,
@ -260,7 +257,7 @@ func TestLegacyReplicaCalcScaleUp(t *testing.T) {
resource: &resourceInfo{ resource: &resourceInfo{
name: v1.ResourceCPU, name: v1.ResourceCPU,
requests: []resource.Quantity{resource.MustParse("1.0"), resource.MustParse("1.0"), resource.MustParse("1.0")}, 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, targetUtilization: 30,
expectedUtilization: 50, expectedUtilization: 50,
@ -278,7 +275,7 @@ func TestLegacyReplicaCalcScaleUpUnreadyLessScale(t *testing.T) {
resource: &resourceInfo{ resource: &resourceInfo{
name: v1.ResourceCPU, name: v1.ResourceCPU,
requests: []resource.Quantity{resource.MustParse("1.0"), resource.MustParse("1.0"), resource.MustParse("1.0")}, 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, targetUtilization: 30,
expectedUtilization: 60, expectedUtilization: 60,
@ -296,7 +293,7 @@ func TestLegacyReplicaCalcScaleUpUnreadyNoScale(t *testing.T) {
resource: &resourceInfo{ resource: &resourceInfo{
name: v1.ResourceCPU, name: v1.ResourceCPU,
requests: []resource.Quantity{resource.MustParse("1.0"), resource.MustParse("1.0"), resource.MustParse("1.0")}, 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, targetUtilization: 30,
expectedUtilization: 40, expectedUtilization: 40,
@ -357,7 +354,7 @@ func TestLegacyReplicaCalcScaleDown(t *testing.T) {
resource: &resourceInfo{ resource: &resourceInfo{
name: v1.ResourceCPU, 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")}, 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, targetUtilization: 50,
expectedUtilization: 28, expectedUtilization: 28,
@ -389,7 +386,7 @@ func TestLegacyReplicaCalcScaleDownIgnoresUnreadyPods(t *testing.T) {
resource: &resourceInfo{ resource: &resourceInfo{
name: v1.ResourceCPU, 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")}, 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, targetUtilization: 50,
expectedUtilization: 30, expectedUtilization: 30,
@ -406,7 +403,7 @@ func TestLegacyReplicaCalcTolerance(t *testing.T) {
resource: &resourceInfo{ resource: &resourceInfo{
name: v1.ResourceCPU, name: v1.ResourceCPU,
requests: []resource.Quantity{resource.MustParse("0.9"), resource.MustParse("1.0"), resource.MustParse("1.1")}, 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, targetUtilization: 100,
expectedUtilization: 102, expectedUtilization: 102,
@ -437,7 +434,7 @@ func TestLegacyReplicaCalcSuperfluousMetrics(t *testing.T) {
resource: &resourceInfo{ resource: &resourceInfo{
name: v1.ResourceCPU, name: v1.ResourceCPU,
requests: []resource.Quantity{resource.MustParse("1.0"), resource.MustParse("1.0"), resource.MustParse("1.0"), resource.MustParse("1.0")}, 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, targetUtilization: 100,
expectedUtilization: 587, expectedUtilization: 587,
expectedValue: numContainersPerPod * 5875, expectedValue: numContainersPerPod * 5875,
@ -453,7 +450,7 @@ func TestLegacyReplicaCalcMissingMetrics(t *testing.T) {
resource: &resourceInfo{ resource: &resourceInfo{
name: v1.ResourceCPU, name: v1.ResourceCPU,
requests: []resource.Quantity{resource.MustParse("1.0"), resource.MustParse("1.0"), resource.MustParse("1.0"), resource.MustParse("1.0")}, 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, targetUtilization: 100,
expectedUtilization: 24, expectedUtilization: 24,
@ -470,7 +467,7 @@ func TestLegacyReplicaCalcEmptyMetrics(t *testing.T) {
resource: &resourceInfo{ resource: &resourceInfo{
name: v1.ResourceCPU, name: v1.ResourceCPU,
requests: []resource.Quantity{resource.MustParse("1.0"), resource.MustParse("1.0"), resource.MustParse("1.0")}, requests: []resource.Quantity{resource.MustParse("1.0"), resource.MustParse("1.0"), resource.MustParse("1.0")},
levels: []int64{}, levels: makePodMetricLevels(),
targetUtilization: 100, targetUtilization: 100,
}, },
@ -485,7 +482,7 @@ func TestLegacyReplicaCalcEmptyCPURequest(t *testing.T) {
resource: &resourceInfo{ resource: &resourceInfo{
name: v1.ResourceCPU, name: v1.ResourceCPU,
requests: []resource.Quantity{}, requests: []resource.Quantity{},
levels: []int64{200}, levels: makePodMetricLevels(200),
targetUtilization: 100, targetUtilization: 100,
}, },
@ -500,7 +497,7 @@ func TestLegacyReplicaCalcMissingMetricsNoChangeEq(t *testing.T) {
resource: &resourceInfo{ resource: &resourceInfo{
name: v1.ResourceCPU, name: v1.ResourceCPU,
requests: []resource.Quantity{resource.MustParse("1.0"), resource.MustParse("1.0")}, requests: []resource.Quantity{resource.MustParse("1.0"), resource.MustParse("1.0")},
levels: []int64{1000}, levels: makePodMetricLevels(1000),
targetUtilization: 100, targetUtilization: 100,
expectedUtilization: 100, expectedUtilization: 100,
@ -517,7 +514,7 @@ func TestLegacyReplicaCalcMissingMetricsNoChangeGt(t *testing.T) {
resource: &resourceInfo{ resource: &resourceInfo{
name: v1.ResourceCPU, name: v1.ResourceCPU,
requests: []resource.Quantity{resource.MustParse("1.0"), resource.MustParse("1.0")}, requests: []resource.Quantity{resource.MustParse("1.0"), resource.MustParse("1.0")},
levels: []int64{1900}, levels: makePodMetricLevels(1900),
targetUtilization: 100, targetUtilization: 100,
expectedUtilization: 190, expectedUtilization: 190,
@ -534,7 +531,7 @@ func TestLegacyReplicaCalcMissingMetricsNoChangeLt(t *testing.T) {
resource: &resourceInfo{ resource: &resourceInfo{
name: v1.ResourceCPU, name: v1.ResourceCPU,
requests: []resource.Quantity{resource.MustParse("1.0"), resource.MustParse("1.0")}, requests: []resource.Quantity{resource.MustParse("1.0"), resource.MustParse("1.0")},
levels: []int64{600}, levels: makePodMetricLevels(600),
targetUtilization: 100, targetUtilization: 100,
expectedUtilization: 60, expectedUtilization: 60,
@ -552,7 +549,7 @@ func TestLegacyReplicaCalcMissingMetricsUnreadyNoChange(t *testing.T) {
resource: &resourceInfo{ resource: &resourceInfo{
name: v1.ResourceCPU, name: v1.ResourceCPU,
requests: []resource.Quantity{resource.MustParse("1.0"), resource.MustParse("1.0"), resource.MustParse("1.0")}, 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, targetUtilization: 50,
expectedUtilization: 45, expectedUtilization: 45,
@ -570,7 +567,7 @@ func TestLegacyReplicaCalcMissingMetricsUnreadyScaleUp(t *testing.T) {
resource: &resourceInfo{ resource: &resourceInfo{
name: v1.ResourceCPU, name: v1.ResourceCPU,
requests: []resource.Quantity{resource.MustParse("1.0"), resource.MustParse("1.0"), resource.MustParse("1.0")}, 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, targetUtilization: 50,
expectedUtilization: 200, expectedUtilization: 200,
@ -588,7 +585,7 @@ func TestLegacyReplicaCalcMissingMetricsUnreadyScaleDown(t *testing.T) {
resource: &resourceInfo{ resource: &resourceInfo{
name: v1.ResourceCPU, name: v1.ResourceCPU,
requests: []resource.Quantity{resource.MustParse("1.0"), resource.MustParse("1.0"), resource.MustParse("1.0"), resource.MustParse("1.0")}, 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, targetUtilization: 50,
expectedUtilization: 10, expectedUtilization: 10,
@ -627,18 +624,18 @@ func TestLegacyReplicaCalcComputedToleranceAlgImplementation(t *testing.T) {
expectedReplicas: finalPods, expectedReplicas: finalPods,
resource: &resourceInfo{ resource: &resourceInfo{
name: v1.ResourceCPU, name: v1.ResourceCPU,
levels: []int64{ levels: makePodMetricLevels(
totalUsedCPUOfAllPods / 10, totalUsedCPUOfAllPods/10,
totalUsedCPUOfAllPods / 10, totalUsedCPUOfAllPods/10,
totalUsedCPUOfAllPods / 10, totalUsedCPUOfAllPods/10,
totalUsedCPUOfAllPods / 10, totalUsedCPUOfAllPods/10,
totalUsedCPUOfAllPods / 10, totalUsedCPUOfAllPods/10,
totalUsedCPUOfAllPods / 10, totalUsedCPUOfAllPods/10,
totalUsedCPUOfAllPods / 10, totalUsedCPUOfAllPods/10,
totalUsedCPUOfAllPods / 10, totalUsedCPUOfAllPods/10,
totalUsedCPUOfAllPods / 10, totalUsedCPUOfAllPods/10,
totalUsedCPUOfAllPods / 10, totalUsedCPUOfAllPods/10,
}, ),
requests: []resource.Quantity{ requests: []resource.Quantity{
resource.MustParse(fmt.Sprint(perPodRequested+100) + "m"), resource.MustParse(fmt.Sprint(perPodRequested+100) + "m"),
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/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/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/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/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/custom_metrics:go_default_library",
"//staging/src/k8s.io/metrics/pkg/client/external_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 // resource metrics as well as pod-level arbitrary metrics
type MetricsClient interface { type MetricsClient interface {
// GetResourceMetric gets the given resource metric (and an associated oldest timestamp) // GetResourceMetric gets the given resource metric (and an associated oldest timestamp)
// for all pods matching the specified selector in the given namespace // for the specified named container in all pods matching the specified selector in the given namespace and when
GetResourceMetric(resource v1.ResourceName, namespace string, selector labels.Selector) (PodMetricsInfo, time.Time, error) // 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) // GetRawMetric gets the given metric (and an associated oldest timestamp)
// for all pods matching the specified selector in the given namespace // 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) metricPath := fmt.Sprintf("/apis/metrics/v1alpha1/namespaces/%s/pods", namespace)
params := map[string]string{"labelSelector": selector.String()} params := map[string]string{"labelSelector": selector.String()}
@ -92,13 +92,15 @@ func (h *HeapsterMetricsClient) GetResourceMetric(resource v1.ResourceName, name
podSum := int64(0) podSum := int64(0)
missing := len(m.Containers) == 0 missing := len(m.Containers) == 0
for _, c := range m.Containers { for _, c := range m.Containers {
resValue, found := c.Usage[v1.ResourceName(resource)] if container == "" || container == c.Name {
if !found { resValue, found := c.Usage[v1.ResourceName(resource)]
missing = true if !found {
klog.V(2).Infof("missing resource metric %v for container %s in pod %s/%s", resource, c.Name, namespace, m.Name) missing = true
continue 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 { if !missing {

View File

@ -222,7 +222,7 @@ func (tc *testCase) runTest(t *testing.T) {
metricsClient := NewHeapsterMetricsClient(testClient, DefaultHeapsterNamespace, DefaultHeapsterScheme, DefaultHeapsterService, DefaultHeapsterPort) metricsClient := NewHeapsterMetricsClient(testClient, DefaultHeapsterNamespace, DefaultHeapsterScheme, DefaultHeapsterService, DefaultHeapsterPort)
isResource := len(tc.resourceName) > 0 isResource := len(tc.resourceName) > 0
if isResource { 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) tc.verifyResults(t, info, timestamp, err)
} else { } else {
info, timestamp, err := metricsClient.GetRawMetric(tc.metricName, tc.namespace, tc.selector, tc.metricSelector) 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/labels"
"k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/runtime/schema"
customapi "k8s.io/metrics/pkg/apis/custom_metrics/v1beta2" 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" resourceclient "k8s.io/metrics/pkg/client/clientset/versioned/typed/metrics/v1beta1"
customclient "k8s.io/metrics/pkg/client/custom_metrics" customclient "k8s.io/metrics/pkg/client/custom_metrics"
externalclient "k8s.io/metrics/pkg/client/external_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) // GetResourceMetric gets the given resource metric (and an associated oldest timestamp)
// for all pods matching the specified selector in the given namespace // 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()}) metrics, err := c.client.PodMetricses(namespace).List(context.TODO(), metav1.ListOptions{LabelSelector: selector.String()})
if err != nil { if err != nil {
return nil, time.Time{}, fmt.Errorf("unable to fetch metrics from resource metrics API: %v", err) 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 { if len(metrics.Items) == 0 {
return nil, time.Time{}, fmt.Errorf("no metrics returned from resource metrics API") 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) podSum := int64(0)
missing := len(m.Containers) == 0 missing := len(m.Containers) == 0
for _, c := range m.Containers { for _, c := range m.Containers {
resValue, found := c.Usage[v1.ResourceName(resource)] resValue, found := c.Usage[resource]
if !found { if !found {
missing = true missing = true
klog.V(2).Infof("missing resource metric %v for container %s in pod %s/%s", resource, c.Name, namespace, m.Name) klog.V(2).Infof("missing resource metric %v for %s/%s", resource, m.Namespace, m.Name)
break // containers loop break
} }
podSum += resValue.MilliValue() podSum += resValue.MilliValue()
} }
if !missing { if !missing {
res[m.Name] = PodMetric{ res[m.Name] = PodMetric{
Timestamp: m.Timestamp.Time, Timestamp: m.Timestamp.Time,
Window: m.Window.Duration, Window: m.Window.Duration,
Value: int64(podSum), Value: podSum,
} }
} }
} }
return res
timestamp := metrics.Items[0].Timestamp.Time
return res, timestamp, nil
} }
// customMetricsClient implements the custom-metrics-related parts of MetricsClient, // customMetricsClient implements the custom-metrics-related parts of MetricsClient,

View File

@ -50,12 +50,13 @@ type restClientTestCase struct {
targetTimestamp int targetTimestamp int
window time.Duration window time.Duration
reportedMetricPoints []metricPoint reportedMetricPoints []metricPoint
reportedPodMetrics [][]int64 reportedPodMetrics []map[string]int64
singleObject *autoscalingapi.CrossVersionObjectReference singleObject *autoscalingapi.CrossVersionObjectReference
namespace string namespace string
selector labels.Selector selector labels.Selector
resourceName v1.ResourceName resourceName v1.ResourceName
container string
metricName string metricName string
metricSelector *metav1.LabelSelector metricSelector *metav1.LabelSelector
metricLabelSelector labels.Selector metricLabelSelector labels.Selector
@ -91,9 +92,9 @@ func (tc *restClientTestCase) prepareTestClient(t *testing.T) (*metricsfake.Clie
Window: metav1.Duration{Duration: tc.window}, Window: metav1.Duration{Duration: tc.window},
Containers: []metricsapi.ContainerMetrics{}, Containers: []metricsapi.ContainerMetrics{},
} }
for j, cpu := range containers { for containerName, cpu := range containers {
cm := metricsapi.ContainerMetrics{ cm := metricsapi.ContainerMetrics{
Name: fmt.Sprintf("%s-%d-container-%d", podNamePrefix, i, j), Name: containerName,
Usage: v1.ResourceList{ Usage: v1.ResourceList{
v1.ResourceCPU: *resource.NewMilliQuantity( v1.ResourceCPU: *resource.NewMilliQuantity(
cpu, cpu,
@ -230,7 +231,7 @@ func (tc *restClientTestCase) runTest(t *testing.T) {
isResource := len(tc.resourceName) > 0 isResource := len(tc.resourceName) > 0
isExternal := tc.metricSelector != nil isExternal := tc.metricSelector != nil
if isResource { 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) tc.verifyResults(t, info, timestamp, err)
} else if isExternal { } else if isExternal {
tc.metricLabelSelector, err = metav1.LabelSelectorAsSelector(tc.metricSelector) 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 targetTimestamp := 1
window := 30 * time.Second window := 30 * time.Second
tc := restClientTestCase{ tc := restClientTestCase{
@ -265,7 +266,25 @@ func TestRESTClientCPU(t *testing.T) {
resourceName: v1.ResourceCPU, resourceName: v1.ResourceCPU,
targetTimestamp: targetTimestamp, targetTimestamp: targetTimestamp,
window: window, 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) tc.runTest(t)
} }
@ -362,17 +381,17 @@ func TestRESTClientExternalEmptyMetrics(t *testing.T) {
tc.runTest(t) tc.runTest(t)
} }
func TestRESTClientCPUEmptyMetrics(t *testing.T) { func TestRESTClientPodCPUEmptyMetrics(t *testing.T) {
tc := restClientTestCase{ tc := restClientTestCase{
resourceName: v1.ResourceCPU, resourceName: v1.ResourceCPU,
desiredError: fmt.Errorf("no metrics returned from resource metrics API"), desiredError: fmt.Errorf("no metrics returned from resource metrics API"),
reportedMetricPoints: []metricPoint{}, reportedMetricPoints: []metricPoint{},
reportedPodMetrics: [][]int64{}, reportedPodMetrics: []map[string]int64{},
} }
tc.runTest(t) tc.runTest(t)
} }
func TestRESTClientCPUEmptyMetricsForOnePod(t *testing.T) { func TestRESTClientPodCPUEmptyMetricsForOnePod(t *testing.T) {
targetTimestamp := 1 targetTimestamp := 1
window := 30 * time.Second window := 30 * time.Second
tc := restClientTestCase{ tc := restClientTestCase{
@ -383,7 +402,25 @@ func TestRESTClientCPUEmptyMetricsForOnePod(t *testing.T) {
}, },
targetTimestamp: targetTimestamp, targetTimestamp: targetTimestamp,
window: window, 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) 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 // 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 // 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) { 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) metrics, timestamp, err := c.metricsClient.GetResourceMetric(resource, namespace, selector, container)
if err != nil { if err != nil {
return 0, 0, 0, time.Time{}, fmt.Errorf("unable to get metrics for resource %s: %v", resource, err) 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) readyPodCount, unreadyPods, missingPods, ignoredPods := groupPods(podList, metrics, resource, c.cpuInitializationPeriod, c.delayOfInitialReadinessStatus)
removeMetricsForPods(metrics, ignoredPods) removeMetricsForPods(metrics, ignoredPods)
removeMetricsForPods(metrics, unreadyPods) removeMetricsForPods(metrics, unreadyPods)
requests, err := calculatePodRequests(podList, resource) requests, err := calculatePodRequests(podList, container, resource)
if err != nil { if err != nil {
return 0, 0, 0, time.Time{}, err 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) // 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 // 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) { 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) metrics, timestamp, err := c.metricsClient.GetResourceMetric(resource, namespace, selector, container)
if err != nil { if err != nil {
return 0, 0, time.Time{}, fmt.Errorf("unable to get metrics for resource %s: %v", resource, err) 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 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)) requests := make(map[string]int64, len(pods))
for _, pod := range pods { for _, pod := range pods {
podSum := int64(0) podSum := int64(0)
for _, container := range pod.Spec.Containers { for _, c := range pod.Spec.Containers {
if containerRequest, ok := container.Resources.Requests[resource]; ok { if container == "" || container == c.Name {
podSum += containerRequest.MilliValue() if containerRequest, ok := c.Resources.Requests[resource]; ok {
} else { podSum += containerRequest.MilliValue()
return nil, fmt.Errorf("missing request for %s", resource) } else {
return nil, fmt.Errorf("missing request for %s", resource)
}
} }
} }
requests[pod.Name] = podSum requests[pod.Name] = podSum

View File

@ -52,7 +52,7 @@ import (
type resourceInfo struct { type resourceInfo struct {
name v1.ResourceName name v1.ResourceName
requests []resource.Quantity requests []resource.Quantity
levels []int64 levels [][]int64
// only applies to pod names returned from "heapster" // only applies to pod names returned from "heapster"
podNames []string podNames []string
@ -93,6 +93,7 @@ type replicaCalcTestCase struct {
resource *resourceInfo resource *resourceInfo
metric *metricInfo metric *metricInfo
metricLabelSelector labels.Selector metricLabelSelector labels.Selector
container string
podReadiness []v1.ConditionStatus podReadiness []v1.ConditionStatus
podStartTime []metav1.Time podStartTime []metav1.Time
@ -152,7 +153,7 @@ func (tc *replicaCalcTestCase) prepareTestClientSet() *fake.Clientset {
}, },
}, },
Spec: v1.PodSpec{ Spec: v1.PodSpec{
Containers: []v1.Container{{}, {}}, Containers: []v1.Container{{Name: "container1"}, {Name: "container2"}},
}, },
} }
if podDeletionTimestamp { if podDeletionTimestamp {
@ -202,13 +203,11 @@ func (tc *replicaCalcTestCase) prepareTestMetricsClient() *metricsfake.Clientset
Containers: make([]metricsapi.ContainerMetrics, numContainersPerPod), Containers: make([]metricsapi.ContainerMetrics, numContainersPerPod),
} }
for i := 0; i < numContainersPerPod; i++ { for i, m := range resValue {
podMetric.Containers[i] = metricsapi.ContainerMetrics{ podMetric.Containers[i] = metricsapi.ContainerMetrics{
Name: fmt.Sprintf("container%v", i), Name: fmt.Sprintf("container%v", i+1),
Usage: v1.ResourceList{ Usage: v1.ResourceList{
v1.ResourceName(tc.resource.name): *resource.NewMilliQuantity( tc.resource.name: *resource.NewMilliQuantity(m, resource.DecimalSI),
int64(resValue),
resource.DecimalSI),
}, },
} }
} }
@ -362,7 +361,7 @@ func (tc *replicaCalcTestCase) runTest(t *testing.T) {
} }
if tc.resource != nil { 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 { if tc.expectedError != nil {
require.Error(t, err, "there should be an error calculating the replica count") 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.Equal(t, tc.metric.expectedUtilization, outUtilization, "utilization should be as expected")
assert.True(t, tc.timestamp.Equal(outTimestamp), "timestamp 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) { func TestReplicaCalcDisjointResourcesMetrics(t *testing.T) {
tc := replicaCalcTestCase{ tc := replicaCalcTestCase{
currentReplicas: 1, currentReplicas: 1,
@ -432,7 +440,7 @@ func TestReplicaCalcDisjointResourcesMetrics(t *testing.T) {
resource: &resourceInfo{ resource: &resourceInfo{
name: v1.ResourceCPU, name: v1.ResourceCPU,
requests: []resource.Quantity{resource.MustParse("1.0")}, requests: []resource.Quantity{resource.MustParse("1.0")},
levels: []int64{100}, levels: makePodMetricLevels(100),
podNames: []string{"an-older-pod-name"}, podNames: []string{"an-older-pod-name"},
targetUtilization: 100, targetUtilization: 100,
@ -441,6 +449,20 @@ func TestReplicaCalcDisjointResourcesMetrics(t *testing.T) {
tc.runTest(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) { func TestReplicaCalcScaleUp(t *testing.T) {
tc := replicaCalcTestCase{ tc := replicaCalcTestCase{
currentReplicas: 3, currentReplicas: 3,
@ -448,7 +470,7 @@ func TestReplicaCalcScaleUp(t *testing.T) {
resource: &resourceInfo{ resource: &resourceInfo{
name: v1.ResourceCPU, name: v1.ResourceCPU,
requests: []resource.Quantity{resource.MustParse("1.0"), resource.MustParse("1.0"), resource.MustParse("1.0")}, 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, targetUtilization: 30,
expectedUtilization: 50, expectedUtilization: 50,
@ -458,6 +480,24 @@ func TestReplicaCalcScaleUp(t *testing.T) {
tc.runTest(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) { func TestReplicaCalcScaleUpUnreadyLessScale(t *testing.T) {
tc := replicaCalcTestCase{ tc := replicaCalcTestCase{
currentReplicas: 3, currentReplicas: 3,
@ -466,7 +506,7 @@ func TestReplicaCalcScaleUpUnreadyLessScale(t *testing.T) {
resource: &resourceInfo{ resource: &resourceInfo{
name: v1.ResourceCPU, name: v1.ResourceCPU,
requests: []resource.Quantity{resource.MustParse("1.0"), resource.MustParse("1.0"), resource.MustParse("1.0")}, 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, targetUtilization: 30,
expectedUtilization: 60, expectedUtilization: 60,
@ -476,6 +516,25 @@ func TestReplicaCalcScaleUpUnreadyLessScale(t *testing.T) {
tc.runTest(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) { func TestReplicaCalcScaleUpHotCpuLessScale(t *testing.T) {
tc := replicaCalcTestCase{ tc := replicaCalcTestCase{
currentReplicas: 3, currentReplicas: 3,
@ -484,7 +543,7 @@ func TestReplicaCalcScaleUpHotCpuLessScale(t *testing.T) {
resource: &resourceInfo{ resource: &resourceInfo{
name: v1.ResourceCPU, name: v1.ResourceCPU,
requests: []resource.Quantity{resource.MustParse("1.0"), resource.MustParse("1.0"), resource.MustParse("1.0")}, 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, targetUtilization: 30,
expectedUtilization: 60, expectedUtilization: 60,
@ -502,7 +561,7 @@ func TestReplicaCalcScaleUpUnreadyNoScale(t *testing.T) {
resource: &resourceInfo{ resource: &resourceInfo{
name: v1.ResourceCPU, name: v1.ResourceCPU,
requests: []resource.Quantity{resource.MustParse("1.0"), resource.MustParse("1.0"), resource.MustParse("1.0")}, 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, targetUtilization: 30,
expectedUtilization: 40, expectedUtilization: 40,
@ -521,7 +580,7 @@ func TestReplicaCalcScaleHotCpuNoScale(t *testing.T) {
resource: &resourceInfo{ resource: &resourceInfo{
name: v1.ResourceCPU, name: v1.ResourceCPU,
requests: []resource.Quantity{resource.MustParse("1.0"), resource.MustParse("1.0"), resource.MustParse("1.0")}, 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, targetUtilization: 30,
expectedUtilization: 40, expectedUtilization: 40,
@ -540,7 +599,7 @@ func TestReplicaCalcScaleUpIgnoresFailedPods(t *testing.T) {
resource: &resourceInfo{ resource: &resourceInfo{
name: v1.ResourceCPU, name: v1.ResourceCPU,
requests: []resource.Quantity{resource.MustParse("1.0"), resource.MustParse("1.0"), resource.MustParse("1.0"), resource.MustParse("1.0")}, 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, targetUtilization: 30,
expectedUtilization: 60, expectedUtilization: 60,
@ -550,6 +609,26 @@ func TestReplicaCalcScaleUpIgnoresFailedPods(t *testing.T) {
tc.runTest(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) { func TestReplicaCalcScaleUpIgnoresDeletionPods(t *testing.T) {
tc := replicaCalcTestCase{ tc := replicaCalcTestCase{
currentReplicas: 2, currentReplicas: 2,
@ -560,7 +639,7 @@ func TestReplicaCalcScaleUpIgnoresDeletionPods(t *testing.T) {
resource: &resourceInfo{ resource: &resourceInfo{
name: v1.ResourceCPU, name: v1.ResourceCPU,
requests: []resource.Quantity{resource.MustParse("1.0"), resource.MustParse("1.0"), resource.MustParse("1.0"), resource.MustParse("1.0")}, 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, targetUtilization: 30,
expectedUtilization: 60, expectedUtilization: 60,
@ -570,6 +649,27 @@ func TestReplicaCalcScaleUpIgnoresDeletionPods(t *testing.T) {
tc.runTest(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) { func TestReplicaCalcScaleUpCM(t *testing.T) {
tc := replicaCalcTestCase{ tc := replicaCalcTestCase{
currentReplicas: 3, currentReplicas: 3,
@ -749,7 +849,7 @@ func TestReplicaCalcScaleDown(t *testing.T) {
resource: &resourceInfo{ resource: &resourceInfo{
name: v1.ResourceCPU, 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")}, 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, targetUtilization: 50,
expectedUtilization: 28, expectedUtilization: 28,
@ -759,6 +859,24 @@ func TestReplicaCalcScaleDown(t *testing.T) {
tc.runTest(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) { func TestReplicaCalcScaleDownCM(t *testing.T) {
tc := replicaCalcTestCase{ tc := replicaCalcTestCase{
currentReplicas: 5, currentReplicas: 5,
@ -845,7 +963,7 @@ func TestReplicaCalcScaleDownPerPodCMExternal(t *testing.T) {
tc.runTest(t) tc.runTest(t)
} }
func TestReplicaCalcScaleDownIncludeUnreadyPods(t *testing.T) { func TestReplicaCalcScaleDownExcludeUnreadyPods(t *testing.T) {
tc := replicaCalcTestCase{ tc := replicaCalcTestCase{
currentReplicas: 5, currentReplicas: 5,
expectedReplicas: 2, expectedReplicas: 2,
@ -853,7 +971,7 @@ func TestReplicaCalcScaleDownIncludeUnreadyPods(t *testing.T) {
resource: &resourceInfo{ resource: &resourceInfo{
name: v1.ResourceCPU, 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")}, 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, targetUtilization: 50,
expectedUtilization: 30, expectedUtilization: 30,
@ -863,6 +981,25 @@ func TestReplicaCalcScaleDownIncludeUnreadyPods(t *testing.T) {
tc.runTest(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) { func TestReplicaCalcScaleDownExcludeUnscheduledPods(t *testing.T) {
tc := replicaCalcTestCase{ tc := replicaCalcTestCase{
currentReplicas: 5, currentReplicas: 5,
@ -872,7 +1009,7 @@ func TestReplicaCalcScaleDownExcludeUnscheduledPods(t *testing.T) {
resource: &resourceInfo{ resource: &resourceInfo{
name: v1.ResourceCPU, 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")}, 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, targetUtilization: 50,
expectedUtilization: 10, expectedUtilization: 10,
@ -882,6 +1019,26 @@ func TestReplicaCalcScaleDownExcludeUnscheduledPods(t *testing.T) {
tc.runTest(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) { func TestReplicaCalcScaleDownIgnoreHotCpuPods(t *testing.T) {
tc := replicaCalcTestCase{ tc := replicaCalcTestCase{
currentReplicas: 5, currentReplicas: 5,
@ -890,7 +1047,7 @@ func TestReplicaCalcScaleDownIgnoreHotCpuPods(t *testing.T) {
resource: &resourceInfo{ resource: &resourceInfo{
name: v1.ResourceCPU, 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")}, 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, targetUtilization: 50,
expectedUtilization: 30, expectedUtilization: 30,
@ -900,6 +1057,25 @@ func TestReplicaCalcScaleDownIgnoreHotCpuPods(t *testing.T) {
tc.runTest(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) { func TestReplicaCalcScaleDownIgnoresFailedPods(t *testing.T) {
tc := replicaCalcTestCase{ tc := replicaCalcTestCase{
currentReplicas: 5, currentReplicas: 5,
@ -909,7 +1085,7 @@ func TestReplicaCalcScaleDownIgnoresFailedPods(t *testing.T) {
resource: &resourceInfo{ resource: &resourceInfo{
name: v1.ResourceCPU, 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")}, 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, targetUtilization: 50,
expectedUtilization: 28, expectedUtilization: 28,
@ -919,6 +1095,26 @@ func TestReplicaCalcScaleDownIgnoresFailedPods(t *testing.T) {
tc.runTest(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) { func TestReplicaCalcScaleDownIgnoresDeletionPods(t *testing.T) {
tc := replicaCalcTestCase{ tc := replicaCalcTestCase{
currentReplicas: 5, currentReplicas: 5,
@ -929,7 +1125,7 @@ func TestReplicaCalcScaleDownIgnoresDeletionPods(t *testing.T) {
resource: &resourceInfo{ resource: &resourceInfo{
name: v1.ResourceCPU, 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")}, 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, targetUtilization: 50,
expectedUtilization: 28, expectedUtilization: 28,
@ -950,12 +1146,13 @@ func TestReplicaCalcScaleDownIgnoresDeletionPods_StillRunning(t *testing.T) {
resource: &resourceInfo{ resource: &resourceInfo{
name: v1.ResourceCPU, 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")}, 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, targetUtilization: 50,
expectedUtilization: 28, expectedUtilization: 28,
expectedValue: numContainersPerPod * 280, expectedValue: 280,
}, },
container: "container2",
} }
tc.runTest(t) tc.runTest(t)
} }
@ -967,7 +1164,7 @@ func TestReplicaCalcTolerance(t *testing.T) {
resource: &resourceInfo{ resource: &resourceInfo{
name: v1.ResourceCPU, name: v1.ResourceCPU,
requests: []resource.Quantity{resource.MustParse("0.9"), resource.MustParse("1.0"), resource.MustParse("1.1")}, 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, targetUtilization: 100,
expectedUtilization: 102, expectedUtilization: 102,
@ -1070,7 +1267,7 @@ func TestReplicaCalcSuperfluousMetrics(t *testing.T) {
resource: &resourceInfo{ resource: &resourceInfo{
name: v1.ResourceCPU, name: v1.ResourceCPU,
requests: []resource.Quantity{resource.MustParse("1.0"), resource.MustParse("1.0"), resource.MustParse("1.0"), resource.MustParse("1.0")}, 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, targetUtilization: 100,
expectedUtilization: 587, expectedUtilization: 587,
expectedValue: numContainersPerPod * 5875, expectedValue: numContainersPerPod * 5875,
@ -1086,7 +1283,7 @@ func TestReplicaCalcMissingMetrics(t *testing.T) {
resource: &resourceInfo{ resource: &resourceInfo{
name: v1.ResourceCPU, name: v1.ResourceCPU,
requests: []resource.Quantity{resource.MustParse("1.0"), resource.MustParse("1.0"), resource.MustParse("1.0"), resource.MustParse("1.0")}, 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, targetUtilization: 100,
expectedUtilization: 24, expectedUtilization: 24,
@ -1103,7 +1300,7 @@ func TestReplicaCalcEmptyMetrics(t *testing.T) {
resource: &resourceInfo{ resource: &resourceInfo{
name: v1.ResourceCPU, name: v1.ResourceCPU,
requests: []resource.Quantity{resource.MustParse("1.0"), resource.MustParse("1.0"), resource.MustParse("1.0")}, requests: []resource.Quantity{resource.MustParse("1.0"), resource.MustParse("1.0"), resource.MustParse("1.0")},
levels: []int64{}, levels: makePodMetricLevels(),
targetUtilization: 100, targetUtilization: 100,
}, },
@ -1118,7 +1315,7 @@ func TestReplicaCalcEmptyCPURequest(t *testing.T) {
resource: &resourceInfo{ resource: &resourceInfo{
name: v1.ResourceCPU, name: v1.ResourceCPU,
requests: []resource.Quantity{}, requests: []resource.Quantity{},
levels: []int64{200}, levels: makePodMetricLevels(200),
targetUtilization: 100, targetUtilization: 100,
}, },
@ -1133,7 +1330,7 @@ func TestReplicaCalcMissingMetricsNoChangeEq(t *testing.T) {
resource: &resourceInfo{ resource: &resourceInfo{
name: v1.ResourceCPU, name: v1.ResourceCPU,
requests: []resource.Quantity{resource.MustParse("1.0"), resource.MustParse("1.0")}, requests: []resource.Quantity{resource.MustParse("1.0"), resource.MustParse("1.0")},
levels: []int64{1000}, levels: makePodMetricLevels(1000),
targetUtilization: 100, targetUtilization: 100,
expectedUtilization: 100, expectedUtilization: 100,
@ -1150,7 +1347,7 @@ func TestReplicaCalcMissingMetricsNoChangeGt(t *testing.T) {
resource: &resourceInfo{ resource: &resourceInfo{
name: v1.ResourceCPU, name: v1.ResourceCPU,
requests: []resource.Quantity{resource.MustParse("1.0"), resource.MustParse("1.0")}, requests: []resource.Quantity{resource.MustParse("1.0"), resource.MustParse("1.0")},
levels: []int64{1900}, levels: makePodMetricLevels(1900),
targetUtilization: 100, targetUtilization: 100,
expectedUtilization: 190, expectedUtilization: 190,
@ -1167,7 +1364,7 @@ func TestReplicaCalcMissingMetricsNoChangeLt(t *testing.T) {
resource: &resourceInfo{ resource: &resourceInfo{
name: v1.ResourceCPU, name: v1.ResourceCPU,
requests: []resource.Quantity{resource.MustParse("1.0"), resource.MustParse("1.0")}, requests: []resource.Quantity{resource.MustParse("1.0"), resource.MustParse("1.0")},
levels: []int64{600}, levels: makePodMetricLevels(600),
targetUtilization: 100, targetUtilization: 100,
expectedUtilization: 60, expectedUtilization: 60,
@ -1185,7 +1382,7 @@ func TestReplicaCalcMissingMetricsUnreadyChange(t *testing.T) {
resource: &resourceInfo{ resource: &resourceInfo{
name: v1.ResourceCPU, name: v1.ResourceCPU,
requests: []resource.Quantity{resource.MustParse("1.0"), resource.MustParse("1.0"), resource.MustParse("1.0")}, 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, targetUtilization: 50,
expectedUtilization: 45, expectedUtilization: 45,
@ -1203,7 +1400,7 @@ func TestReplicaCalcMissingMetricsHotCpuNoChange(t *testing.T) {
resource: &resourceInfo{ resource: &resourceInfo{
name: v1.ResourceCPU, name: v1.ResourceCPU,
requests: []resource.Quantity{resource.MustParse("1.0"), resource.MustParse("1.0"), resource.MustParse("1.0")}, 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, targetUtilization: 50,
expectedUtilization: 45, expectedUtilization: 45,
@ -1221,7 +1418,7 @@ func TestReplicaCalcMissingMetricsUnreadyScaleUp(t *testing.T) {
resource: &resourceInfo{ resource: &resourceInfo{
name: v1.ResourceCPU, name: v1.ResourceCPU,
requests: []resource.Quantity{resource.MustParse("1.0"), resource.MustParse("1.0"), resource.MustParse("1.0")}, 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, targetUtilization: 50,
expectedUtilization: 200, expectedUtilization: 200,
@ -1240,7 +1437,7 @@ func TestReplicaCalcMissingMetricsHotCpuScaleUp(t *testing.T) {
resource: &resourceInfo{ resource: &resourceInfo{
name: v1.ResourceCPU, name: v1.ResourceCPU,
requests: []resource.Quantity{resource.MustParse("1.0"), resource.MustParse("1.0"), resource.MustParse("1.0")}, 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, targetUtilization: 50,
expectedUtilization: 200, expectedUtilization: 200,
@ -1258,7 +1455,7 @@ func TestReplicaCalcMissingMetricsUnreadyScaleDown(t *testing.T) {
resource: &resourceInfo{ resource: &resourceInfo{
name: v1.ResourceCPU, name: v1.ResourceCPU,
requests: []resource.Quantity{resource.MustParse("1.0"), resource.MustParse("1.0"), resource.MustParse("1.0"), resource.MustParse("1.0")}, 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, targetUtilization: 50,
expectedUtilization: 10, expectedUtilization: 10,
@ -1276,7 +1473,7 @@ func TestReplicaCalcDuringRollingUpdateWithMaxSurge(t *testing.T) {
resource: &resourceInfo{ resource: &resourceInfo{
name: v1.ResourceCPU, name: v1.ResourceCPU,
requests: []resource.Quantity{resource.MustParse("1.0"), resource.MustParse("1.0"), resource.MustParse("1.0")}, 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, targetUtilization: 50,
expectedUtilization: 10, expectedUtilization: 10,
@ -1315,18 +1512,18 @@ func TestReplicaCalcComputedToleranceAlgImplementation(t *testing.T) {
expectedReplicas: finalPods, expectedReplicas: finalPods,
resource: &resourceInfo{ resource: &resourceInfo{
name: v1.ResourceCPU, name: v1.ResourceCPU,
levels: []int64{ levels: makePodMetricLevels(
totalUsedCPUOfAllPods / 10, totalUsedCPUOfAllPods/10,
totalUsedCPUOfAllPods / 10, totalUsedCPUOfAllPods/10,
totalUsedCPUOfAllPods / 10, totalUsedCPUOfAllPods/10,
totalUsedCPUOfAllPods / 10, totalUsedCPUOfAllPods/10,
totalUsedCPUOfAllPods / 10, totalUsedCPUOfAllPods/10,
totalUsedCPUOfAllPods / 10, totalUsedCPUOfAllPods/10,
totalUsedCPUOfAllPods / 10, totalUsedCPUOfAllPods/10,
totalUsedCPUOfAllPods / 10, totalUsedCPUOfAllPods/10,
totalUsedCPUOfAllPods / 10, totalUsedCPUOfAllPods/10,
totalUsedCPUOfAllPods / 10, totalUsedCPUOfAllPods/10,
}, ),
requests: []resource.Quantity{ requests: []resource.Quantity{
resource.MustParse(fmt.Sprint(perPodRequested+100) + "m"), resource.MustParse(fmt.Sprint(perPodRequested+100) + "m"),
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 // Disables Accelerator Metrics Collected by Kubelet
DisableAcceleratorUsageMetrics featuregate.Feature = "DisableAcceleratorUsageMetrics" 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() { func init() {
@ -739,6 +746,7 @@ var defaultKubernetesFeatureGates = map[featuregate.Feature]featuregate.FeatureS
WinOverlay: {Default: true, PreRelease: featuregate.Beta}, WinOverlay: {Default: true, PreRelease: featuregate.Beta},
WinDSR: {Default: false, PreRelease: featuregate.Alpha}, WinDSR: {Default: false, PreRelease: featuregate.Alpha},
DisableAcceleratorUsageMetrics: {Default: true, PreRelease: featuregate.Beta}, 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 // inherited features from generic apiserver, relisted here to get a conflict if it is changed
// unintentionally on either side: // 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)) 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: default:
list = append(list, "<unknown type>") list = append(list, "<unknown type>")
} }

View File

@ -2813,6 +2813,161 @@ func TestPrintHPA(t *testing.T) {
// Columns: Name, Reference, Targets, MinPods, MaxPods, Replicas, Age // 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>"}}}, 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 // multiple specs
{ {
hpa: autoscaling.HorizontalPodAutoscaler{ hpa: autoscaling.HorizontalPodAutoscaler{

View File

@ -3,6 +3,7 @@ package(default_visibility = ["//visibility:public"])
load( load(
"@io_bazel_rules_go//go:def.bzl", "@io_bazel_rules_go//go:def.bzl",
"go_library", "go_library",
"go_test",
) )
go_library( go_library(
@ -16,9 +17,11 @@ go_library(
"//pkg/api/legacyscheme:go_default_library", "//pkg/api/legacyscheme:go_default_library",
"//pkg/apis/autoscaling:go_default_library", "//pkg/apis/autoscaling:go_default_library",
"//pkg/apis/autoscaling/validation: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/runtime:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/util/validation/field: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/storage/names:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/util/feature:go_default_library",
], ],
) )
@ -37,3 +40,17 @@ filegroup(
], ],
tags = ["automanaged"], 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/runtime"
"k8s.io/apimachinery/pkg/util/validation/field" "k8s.io/apimachinery/pkg/util/validation/field"
"k8s.io/apiserver/pkg/storage/names" "k8s.io/apiserver/pkg/storage/names"
utilfeature "k8s.io/apiserver/pkg/util/feature"
"k8s.io/kubernetes/pkg/api/legacyscheme" "k8s.io/kubernetes/pkg/api/legacyscheme"
"k8s.io/kubernetes/pkg/apis/autoscaling" "k8s.io/kubernetes/pkg/apis/autoscaling"
"k8s.io/kubernetes/pkg/apis/autoscaling/validation" "k8s.io/kubernetes/pkg/apis/autoscaling/validation"
"k8s.io/kubernetes/pkg/features"
) )
// autoscalerStrategy implements behavior for HorizontalPodAutoscalers // autoscalerStrategy implements behavior for HorizontalPodAutoscalers
@ -48,6 +50,10 @@ func (autoscalerStrategy) PrepareForCreate(ctx context.Context, obj runtime.Obje
// create cannot set status // create cannot set status
newHPA.Status = autoscaling.HorizontalPodAutoscalerStatus{} newHPA.Status = autoscaling.HorizontalPodAutoscalerStatus{}
if !utilfeature.DefaultFeatureGate.Enabled(features.HPAContainerMetrics) {
dropContainerMetricSources(newHPA.Spec.Metrics)
}
} }
// Validate validates a new autoscaler. // Validate validates a new autoscaler.
@ -69,10 +75,30 @@ func (autoscalerStrategy) AllowCreateOnUpdate() bool {
func (autoscalerStrategy) PrepareForUpdate(ctx context.Context, obj, old runtime.Object) { func (autoscalerStrategy) PrepareForUpdate(ctx context.Context, obj, old runtime.Object) {
newHPA := obj.(*autoscaling.HorizontalPodAutoscaler) newHPA := obj.(*autoscaling.HorizontalPodAutoscaler)
oldHPA := old.(*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 // Update is not allowed to set status
newHPA.Status = oldHPA.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. // ValidateUpdate is the default update validation for an end user.
func (autoscalerStrategy) ValidateUpdate(ctx context.Context, obj, old runtime.Object) field.ErrorList { func (autoscalerStrategy) ValidateUpdate(ctx context.Context, obj, old runtime.Object) field.ErrorList {
return validation.ValidateHorizontalPodAutoscalerUpdate(obj.(*autoscaling.HorizontalPodAutoscaler), old.(*autoscaling.HorizontalPodAutoscaler)) 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". // Package-wide variables from generator "generated".
option go_package = "v1"; 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. // CrossVersionObjectReference contains enough information to let you identify the referred resource.
message CrossVersionObjectReference { message CrossVersionObjectReference {
// Kind of the referent; More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds" // 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 // MetricSpec specifies how to scale based on a single metric
// (only `type` and one other matching field should be set at once). // (only `type` and one other matching field should be set at once).
message MetricSpec { message MetricSpec {
// type is the type of metric source. It should be one of "Object", // type is the type of metric source. It should be one of "ContainerResource",
// "Pods" or "Resource", each mapping to a matching field in the object. // "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; optional string type = 1;
// object refers to a metric describing a single kubernetes object // object refers to a metric describing a single kubernetes object
@ -207,6 +263,15 @@ message MetricSpec {
// +optional // +optional
optional ResourceMetricSource resource = 4; 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 // external refers to a global metric that is not associated
// with any Kubernetes object. It allows autoscaling based on information // with any Kubernetes object. It allows autoscaling based on information
// coming from components running outside of cluster // coming from components running outside of cluster
@ -218,8 +283,10 @@ message MetricSpec {
// MetricStatus describes the last-read state of a single metric. // MetricStatus describes the last-read state of a single metric.
message MetricStatus { message MetricStatus {
// type is the type of metric source. It will be one of "Object", // type is the type of metric source. It will be one of "ContainerResource",
// "Pods" or "Resource", each corresponds to a matching field in the object. // "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; optional string type = 1;
// object refers to a metric describing a single kubernetes object // object refers to a metric describing a single kubernetes object
@ -241,6 +308,14 @@ message MetricStatus {
// +optional // +optional
optional ResourceMetricStatus resource = 4; 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 // external refers to a global metric that is not associated
// with any Kubernetes object. It allows autoscaling based on information // with any Kubernetes object. It allows autoscaling based on information
// coming from components running outside of cluster // 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 // Kubernetes, and have special scaling options on top of those available
// to normal per-pod metrics (the "pods" source). // to normal per-pod metrics (the "pods" source).
ResourceMetricSourceType MetricSourceType = "Resource" 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 // ExternalMetricSourceType is a global metric that is not associated
// with any Kubernetes object. It allows autoscaling based on information // with any Kubernetes object. It allows autoscaling based on information
// coming from components running outside of cluster // coming from components running outside of cluster
@ -176,8 +182,10 @@ const (
// MetricSpec specifies how to scale based on a single metric // MetricSpec specifies how to scale based on a single metric
// (only `type` and one other matching field should be set at once). // (only `type` and one other matching field should be set at once).
type MetricSpec struct { type MetricSpec struct {
// type is the type of metric source. It should be one of "Object", // type is the type of metric source. It should be one of "ContainerResource",
// "Pods" or "Resource", each mapping to a matching field in the object. // "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"` Type MetricSourceType `json:"type" protobuf:"bytes,1,name=type"`
// object refers to a metric describing a single kubernetes object // 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. // to normal per-pod metrics using the "pods" source.
// +optional // +optional
Resource *ResourceMetricSource `json:"resource,omitempty" protobuf:"bytes,4,opt,name=resource"` 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 // external refers to a global metric that is not associated
// with any Kubernetes object. It allows autoscaling based on information // with any Kubernetes object. It allows autoscaling based on information
// coming from components running outside of cluster // 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"` 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 // ExternalMetricSource indicates how to scale on a metric not associated with
// any Kubernetes object (for example length of queue in cloud // any Kubernetes object (for example length of queue in cloud
// messaging service, or QPS from loadbalancer running outside of cluster). // 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. // MetricStatus describes the last-read state of a single metric.
type MetricStatus struct { type MetricStatus struct {
// type is the type of metric source. It will be one of "Object", // type is the type of metric source. It will be one of "ContainerResource",
// "Pods" or "Resource", each corresponds to a matching field in the object. // "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"` Type MetricSourceType `json:"type" protobuf:"bytes,1,name=type"`
// object refers to a metric describing a single kubernetes object // 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. // to normal per-pod metrics using the "pods" source.
// +optional // +optional
Resource *ResourceMetricStatus `json:"resource,omitempty" protobuf:"bytes,4,opt,name=resource"` 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 // external refers to a global metric that is not associated
// with any Kubernetes object. It allows autoscaling based on information // with any Kubernetes object. It allows autoscaling based on information
// coming from components running outside of cluster // coming from components running outside of cluster
@ -414,6 +463,30 @@ type ResourceMetricStatus struct {
CurrentAverageValue resource.Quantity `json:"currentAverageValue" protobuf:"bytes,3,name=currentAverageValue"` 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 // ExternalMetricStatus indicates the current value of a global metric
// not associated with any Kubernetes object. // not associated with any Kubernetes object.
type ExternalMetricStatus struct { type ExternalMetricStatus struct {

View File

@ -27,6 +27,30 @@ package v1
// Those methods can be generated by using hack/update-generated-swagger-docs.sh // Those methods can be generated by using hack/update-generated-swagger-docs.sh
// AUTO-GENERATED FUNCTIONS START HERE. DO NOT EDIT. // 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{ var map_CrossVersionObjectReference = map[string]string{
"": "CrossVersionObjectReference contains enough information to let you identify the referred resource.", "": "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\"", "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{ 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).", "": "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.", "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).", "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.", "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.", "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).", "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 { func (MetricSpec) SwaggerDoc() map[string]string {
@ -135,12 +160,13 @@ func (MetricSpec) SwaggerDoc() map[string]string {
} }
var map_MetricStatus = map[string]string{ var map_MetricStatus = map[string]string{
"": "MetricStatus describes the last-read state of a single metric.", "": "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.", "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).", "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.", "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.", "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).", "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 { func (MetricStatus) SwaggerDoc() map[string]string {

View File

@ -25,6 +25,54 @@ import (
runtime "k8s.io/apimachinery/pkg/runtime" 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. // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *CrossVersionObjectReference) DeepCopyInto(out *CrossVersionObjectReference) { func (in *CrossVersionObjectReference) DeepCopyInto(out *CrossVersionObjectReference) {
*out = *in *out = *in
@ -252,6 +300,11 @@ func (in *MetricSpec) DeepCopyInto(out *MetricSpec) {
*out = new(ResourceMetricSource) *out = new(ResourceMetricSource)
(*in).DeepCopyInto(*out) (*in).DeepCopyInto(*out)
} }
if in.ContainerResource != nil {
in, out := &in.ContainerResource, &out.ContainerResource
*out = new(ContainerResourceMetricSource)
(*in).DeepCopyInto(*out)
}
if in.External != nil { if in.External != nil {
in, out := &in.External, &out.External in, out := &in.External, &out.External
*out = new(ExternalMetricSource) *out = new(ExternalMetricSource)
@ -288,6 +341,11 @@ func (in *MetricStatus) DeepCopyInto(out *MetricStatus) {
*out = new(ResourceMetricStatus) *out = new(ResourceMetricStatus)
(*in).DeepCopyInto(*out) (*in).DeepCopyInto(*out)
} }
if in.ContainerResource != nil {
in, out := &in.ContainerResource, &out.ContainerResource
*out = new(ContainerResourceMetricStatus)
(*in).DeepCopyInto(*out)
}
if in.External != nil { if in.External != nil {
in, out := &in.External, &out.External in, out := &in.External, &out.External
*out = new(ExternalMetricStatus) *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". // Package-wide variables from generator "generated".
option go_package = "v2beta1"; 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. // CrossVersionObjectReference contains enough information to let you identify the referred resource.
message CrossVersionObjectReference { message CrossVersionObjectReference {
// Kind of the referent; More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds" // 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 // MetricSpec specifies how to scale based on a single metric
// (only `type` and one other matching field should be set at once). // (only `type` and one other matching field should be set at once).
message MetricSpec { message MetricSpec {
// type is the type of metric source. It should be one of "Object", // type is the type of metric source. It should be one of "ContainerResource",
// "Pods", "Resource" or "External", each mapping to a matching field in the object. // "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; optional string type = 1;
// object refers to a metric describing a single kubernetes object // object refers to a metric describing a single kubernetes object
@ -223,6 +279,15 @@ message MetricSpec {
// +optional // +optional
optional ResourceMetricSource resource = 4; 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 // external refers to a global metric that is not associated
// with any Kubernetes object. It allows autoscaling based on information // with any Kubernetes object. It allows autoscaling based on information
// coming from components running outside of cluster // coming from components running outside of cluster
@ -234,8 +299,10 @@ message MetricSpec {
// MetricStatus describes the last-read state of a single metric. // MetricStatus describes the last-read state of a single metric.
message MetricStatus { message MetricStatus {
// type is the type of metric source. It will be one of "Object", // type is the type of metric source. It will be one of "ContainerResource",
// "Pods" or "Resource", each corresponds to a matching field in the object. // "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; optional string type = 1;
// object refers to a metric describing a single kubernetes object // object refers to a metric describing a single kubernetes object
@ -257,6 +324,14 @@ message MetricStatus {
// +optional // +optional
optional ResourceMetricStatus resource = 4; 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 // external refers to a global metric that is not associated
// with any Kubernetes object. It allows autoscaling based on information // with any Kubernetes object. It allows autoscaling based on information
// coming from components running outside of cluster // 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 // Kubernetes, and have special scaling options on top of those available
// to normal per-pod metrics (the "pods" source). // to normal per-pod metrics (the "pods" source).
ResourceMetricSourceType MetricSourceType = "Resource" 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 // ExternalMetricSourceType is a global metric that is not associated
// with any Kubernetes object. It allows autoscaling based on information // with any Kubernetes object. It allows autoscaling based on information
// coming from components running outside of cluster // coming from components running outside of cluster
@ -87,8 +93,10 @@ const (
// MetricSpec specifies how to scale based on a single metric // MetricSpec specifies how to scale based on a single metric
// (only `type` and one other matching field should be set at once). // (only `type` and one other matching field should be set at once).
type MetricSpec struct { type MetricSpec struct {
// type is the type of metric source. It should be one of "Object", // type is the type of metric source. It should be one of "ContainerResource",
// "Pods", "Resource" or "External", each mapping to a matching field in the object. // "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"` Type MetricSourceType `json:"type" protobuf:"bytes,1,name=type"`
// object refers to a metric describing a single kubernetes object // 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. // to normal per-pod metrics using the "pods" source.
// +optional // +optional
Resource *ResourceMetricSource `json:"resource,omitempty" protobuf:"bytes,4,opt,name=resource"` 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 // external refers to a global metric that is not associated
// with any Kubernetes object. It allows autoscaling based on information // with any Kubernetes object. It allows autoscaling based on information
// coming from components running outside of cluster // 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"` 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 // ExternalMetricSource indicates how to scale on a metric not associated with
// any Kubernetes object (for example length of queue in cloud // any Kubernetes object (for example length of queue in cloud
// messaging service, or QPS from loadbalancer running outside of cluster). // 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. // MetricStatus describes the last-read state of a single metric.
type MetricStatus struct { type MetricStatus struct {
// type is the type of metric source. It will be one of "Object", // type is the type of metric source. It will be one of "ContainerResource",
// "Pods" or "Resource", each corresponds to a matching field in the object. // "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"` Type MetricSourceType `json:"type" protobuf:"bytes,1,name=type"`
// object refers to a metric describing a single kubernetes object // 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. // to normal per-pod metrics using the "pods" source.
// +optional // +optional
Resource *ResourceMetricStatus `json:"resource,omitempty" protobuf:"bytes,4,opt,name=resource"` 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 // external refers to a global metric that is not associated
// with any Kubernetes object. It allows autoscaling based on information // with any Kubernetes object. It allows autoscaling based on information
// coming from components running outside of cluster // coming from components running outside of cluster
@ -354,6 +403,30 @@ type ResourceMetricStatus struct {
CurrentAverageValue resource.Quantity `json:"currentAverageValue" protobuf:"bytes,3,name=currentAverageValue"` 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 // ExternalMetricStatus indicates the current value of a global metric
// not associated with any Kubernetes object. // not associated with any Kubernetes object.
type ExternalMetricStatus struct { type ExternalMetricStatus struct {

View File

@ -27,6 +27,30 @@ package v2beta1
// Those methods can be generated by using hack/update-generated-swagger-docs.sh // Those methods can be generated by using hack/update-generated-swagger-docs.sh
// AUTO-GENERATED FUNCTIONS START HERE. DO NOT EDIT. // 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{ var map_CrossVersionObjectReference = map[string]string{
"": "CrossVersionObjectReference contains enough information to let you identify the referred resource.", "": "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\"", "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{ 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).", "": "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.", "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).", "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.", "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.", "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).", "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 { func (MetricSpec) SwaggerDoc() map[string]string {
@ -136,12 +161,13 @@ func (MetricSpec) SwaggerDoc() map[string]string {
} }
var map_MetricStatus = map[string]string{ var map_MetricStatus = map[string]string{
"": "MetricStatus describes the last-read state of a single metric.", "": "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.", "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).", "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.", "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.", "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).", "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 { func (MetricStatus) SwaggerDoc() map[string]string {

View File

@ -25,6 +25,54 @@ import (
runtime "k8s.io/apimachinery/pkg/runtime" 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. // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *CrossVersionObjectReference) DeepCopyInto(out *CrossVersionObjectReference) { func (in *CrossVersionObjectReference) DeepCopyInto(out *CrossVersionObjectReference) {
*out = *in *out = *in
@ -263,6 +311,11 @@ func (in *MetricSpec) DeepCopyInto(out *MetricSpec) {
*out = new(ResourceMetricSource) *out = new(ResourceMetricSource)
(*in).DeepCopyInto(*out) (*in).DeepCopyInto(*out)
} }
if in.ContainerResource != nil {
in, out := &in.ContainerResource, &out.ContainerResource
*out = new(ContainerResourceMetricSource)
(*in).DeepCopyInto(*out)
}
if in.External != nil { if in.External != nil {
in, out := &in.External, &out.External in, out := &in.External, &out.External
*out = new(ExternalMetricSource) *out = new(ExternalMetricSource)
@ -299,6 +352,11 @@ func (in *MetricStatus) DeepCopyInto(out *MetricStatus) {
*out = new(ResourceMetricStatus) *out = new(ResourceMetricStatus)
(*in).DeepCopyInto(*out) (*in).DeepCopyInto(*out)
} }
if in.ContainerResource != nil {
in, out := &in.ContainerResource, &out.ContainerResource
*out = new(ContainerResourceMetricStatus)
(*in).DeepCopyInto(*out)
}
if in.External != nil { if in.External != nil {
in, out := &in.External, &out.External in, out := &in.External, &out.External
*out = new(ExternalMetricStatus) *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". // Package-wide variables from generator "generated".
option go_package = "v2beta2"; 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. // CrossVersionObjectReference contains enough information to let you identify the referred resource.
message CrossVersionObjectReference { message CrossVersionObjectReference {
// Kind of the referent; More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds" // 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 // MetricSpec specifies how to scale based on a single metric
// (only `type` and one other matching field should be set at once). // (only `type` and one other matching field should be set at once).
message MetricSpec { message MetricSpec {
// type is the type of metric source. It should be one of "Object", // type is the type of metric source. It should be one of "ContainerResource", "External",
// "Pods", "Resource" or "External", each mapping to a matching field in the object. // "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; optional string type = 1;
// object refers to a metric describing a single kubernetes object // object refers to a metric describing a single kubernetes object
@ -279,6 +315,15 @@ message MetricSpec {
// +optional // +optional
optional ResourceMetricSource resource = 4; 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 // external refers to a global metric that is not associated
// with any Kubernetes object. It allows autoscaling based on information // with any Kubernetes object. It allows autoscaling based on information
// coming from components running outside of cluster // coming from components running outside of cluster
@ -290,8 +335,10 @@ message MetricSpec {
// MetricStatus describes the last-read state of a single metric. // MetricStatus describes the last-read state of a single metric.
message MetricStatus { message MetricStatus {
// type is the type of metric source. It will be one of "Object", // type is the type of metric source. It will be one of "ContainerResource", "External",
// "Pods" or "Resource", each corresponds to a matching field in the object. // "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; optional string type = 1;
// object refers to a metric describing a single kubernetes object // object refers to a metric describing a single kubernetes object
@ -313,6 +360,14 @@ message MetricStatus {
// +optional // +optional
optional ResourceMetricStatus resource = 4; 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 // external refers to a global metric that is not associated
// with any Kubernetes object. It allows autoscaling based on information // with any Kubernetes object. It allows autoscaling based on information
// coming from components running outside of cluster // 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 // MetricSpec specifies how to scale based on a single metric
// (only `type` and one other matching field should be set at once). // (only `type` and one other matching field should be set at once).
type MetricSpec struct { type MetricSpec struct {
// type is the type of metric source. It should be one of "Object", // type is the type of metric source. It should be one of "ContainerResource", "External",
// "Pods", "Resource" or "External", each mapping to a matching field in the object. // "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"` Type MetricSourceType `json:"type" protobuf:"bytes,1,name=type"`
// object refers to a metric describing a single kubernetes object // 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. // to normal per-pod metrics using the "pods" source.
// +optional // +optional
Resource *ResourceMetricSource `json:"resource,omitempty" protobuf:"bytes,4,opt,name=resource"` 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 // external refers to a global metric that is not associated
// with any Kubernetes object. It allows autoscaling based on information // with any Kubernetes object. It allows autoscaling based on information
// coming from components running outside of cluster // coming from components running outside of cluster
@ -220,6 +230,12 @@ const (
// Kubernetes, and have special scaling options on top of those available // Kubernetes, and have special scaling options on top of those available
// to normal per-pod metrics (the "pods" source). // to normal per-pod metrics (the "pods" source).
ResourceMetricSourceType MetricSourceType = "Resource" 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 // ExternalMetricSourceType is a global metric that is not associated
// with any Kubernetes object. It allows autoscaling based on information // with any Kubernetes object. It allows autoscaling based on information
// coming from components running outside of cluster // coming from components running outside of cluster
@ -263,6 +279,22 @@ type ResourceMetricSource struct {
Target MetricTarget `json:"target" protobuf:"bytes,2,name=target"` 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 // ExternalMetricSource indicates how to scale on a metric not associated with
// any Kubernetes object (for example length of queue in cloud // any Kubernetes object (for example length of queue in cloud
// messaging service, or QPS from loadbalancer running outside of cluster). // 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. // MetricStatus describes the last-read state of a single metric.
type MetricStatus struct { type MetricStatus struct {
// type is the type of metric source. It will be one of "Object", // type is the type of metric source. It will be one of "ContainerResource", "External",
// "Pods" or "Resource", each corresponds to a matching field in the object. // "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"` Type MetricSourceType `json:"type" protobuf:"bytes,1,name=type"`
// object refers to a metric describing a single kubernetes object // 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. // to normal per-pod metrics using the "pods" source.
// +optional // +optional
Resource *ResourceMetricStatus `json:"resource,omitempty" protobuf:"bytes,4,opt,name=resource"` 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 // external refers to a global metric that is not associated
// with any Kubernetes object. It allows autoscaling based on information // with any Kubernetes object. It allows autoscaling based on information
// coming from components running outside of cluster // coming from components running outside of cluster
@ -443,6 +484,20 @@ type ResourceMetricStatus struct {
Current MetricValueStatus `json:"current" protobuf:"bytes,2,name=current"` 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 // ExternalMetricStatus indicates the current value of a global metric
// not associated with any Kubernetes object. // not associated with any Kubernetes object.
type ExternalMetricStatus struct { type ExternalMetricStatus struct {

View File

@ -27,6 +27,28 @@ package v2beta2
// Those methods can be generated by using hack/update-generated-swagger-docs.sh // Those methods can be generated by using hack/update-generated-swagger-docs.sh
// AUTO-GENERATED FUNCTIONS START HERE. DO NOT EDIT. // 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{ var map_CrossVersionObjectReference = map[string]string{
"": "CrossVersionObjectReference contains enough information to let you identify the referred resource.", "": "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\"", "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{ 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).", "": "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.", "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).", "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.", "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.", "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).", "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 { func (MetricSpec) SwaggerDoc() map[string]string {
@ -175,12 +198,13 @@ func (MetricSpec) SwaggerDoc() map[string]string {
} }
var map_MetricStatus = map[string]string{ var map_MetricStatus = map[string]string{
"": "MetricStatus describes the last-read state of a single metric.", "": "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.", "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).", "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.", "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.", "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).", "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 { func (MetricStatus) SwaggerDoc() map[string]string {

View File

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

View File

@ -98,98 +98,122 @@
"targetAverageUtilization": 1253093074, "targetAverageUtilization": 1253093074,
"targetAverageValue": "8" "targetAverageValue": "8"
}, },
"containerResource": {
"name": "w垁鷌辪虽U珝Żwʮ馜üNșƶ4ĩ",
"targetAverageUtilization": 1463207240,
"targetAverageValue": "84",
"container": "39"
},
"external": { "external": {
"metricName": "39", "metricName": "40",
"metricSelector": { "metricSelector": {
"matchLabels": { "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": [ "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", "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": "DoesNotExist" "operator": "NotIn",
"values": [
"dY_um-_8r--684._-_18_...E.-2o_-.5"
]
} }
] ]
}, },
"targetValue": "570", "targetValue": "865",
"targetAverageValue": "829" "targetAverageValue": "379"
} }
} }
] ]
}, },
"status": { "status": {
"observedGeneration": -7224326297454280417, "observedGeneration": 61436896663269868,
"currentReplicas": 596942561, "currentReplicas": -1462219068,
"desiredReplicas": -1880980172, "desiredReplicas": -370386363,
"currentMetrics": [ "currentMetrics": [
{ {
"type": " ïì«丯Ƙ枛牐ɺ皚|懥", "type": "",
"object": { "object": {
"target": { "target": {
"kind": "46", "kind": "47",
"name": "47", "name": "48",
"apiVersion": "48" "apiVersion": "49"
}, },
"metricName": "49", "metricName": "50",
"currentValue": "55", "currentValue": "856",
"selector": { "selector": {
"matchLabels": { "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": [ "matchExpressions": [
{ {
"key": "665--4-j8---t6-r7--9.9dy/XvSA..1", "key": "4_.-N_g-.._5",
"operator": "Exists" "operator": "In",
"values": [
"2qz.W..4....-h._.GgT7_7B_D-..-.k4u-zA_--_.-.6GA26C-s.Nj-d-4_t"
]
} }
] ]
}, },
"averageValue": "14" "averageValue": "817"
}, },
"pods": { "pods": {
"metricName": "56", "metricName": "57",
"currentAverageValue": "878", "currentAverageValue": "400",
"selector": { "selector": {
"matchLabels": { "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": [ "matchExpressions": [
{ {
"key": "g-..__._____K_g1cXfr.4_.B", "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": "DoesNotExist" "operator": "NotIn",
"values": [
"8._Q.6.I--2_9.v.--_.--4QQ.-s.H.Hf"
]
} }
] ]
} }
}, },
"resource": { "resource": {
"name": "šeSvEȤƏ埮pɵ{WOŭW灬pȭ", "name": "ƻ遲njlȘ鹾KƂʼnç",
"currentAverageUtilization": -1607821167, "currentAverageUtilization": 769521729,
"currentAverageValue": "832" "currentAverageValue": "727"
},
"containerResource": {
"name": "!@@)Zq=",
"currentAverageUtilization": -861915578,
"currentAverageValue": "111",
"container": "64"
}, },
"external": { "external": {
"metricName": "63", "metricName": "65",
"metricSelector": { "metricSelector": {
"matchLabels": { "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": [ "matchExpressions": [
{ {
"key": "FC-rtSY.g._2F7.-_e..OP", "key": "v55039780bdw0-1-47rrw8-7/U_--56-.7D.3_KPg___Kp",
"operator": "Exists" "operator": "In",
"values": [
"N7_-Zp_._w"
]
} }
] ]
}, },
"currentValue": "229", "currentValue": "200",
"currentAverageValue": "606" "currentAverageValue": "288"
} }
} }
], ],
"conditions": [ "conditions": [
{ {
"type": "鯶縆", "type": "",
"status": "aTGÒ鵌Ē3", "status": "",
"lastTransitionTime": "2416-12-01T11:47:49Z", "lastTransitionTime": "1995-10-31T09:59:43Z",
"reason": "70", "reason": "72",
"message": "71" "message": "73"
} }
] ]
} }

View File

@ -32,16 +32,23 @@ metadata:
spec: spec:
maxReplicas: -1971381490 maxReplicas: -1971381490
metrics: metrics:
- external: - containerResource:
metricName: "39" container: "39"
name: w垁鷌辪虽U珝Żwʮ馜üNșƶ4ĩ
targetAverageUtilization: 1463207240
targetAverageValue: "84"
external:
metricName: "40"
metricSelector: metricSelector:
matchExpressions: 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 - 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: DoesNotExist operator: NotIn
values:
- dY_um-_8r--684._-_18_...E.-2o_-.5
matchLabels: 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
targetAverageValue: "829" targetAverageValue: "379"
targetValue: "570" targetValue: "865"
object: object:
averageValue: "954" averageValue: "954"
metricName: "25" metricName: "25"
@ -82,50 +89,61 @@ spec:
name: "20" name: "20"
status: status:
conditions: conditions:
- lastTransitionTime: "2416-12-01T11:47:49Z" - lastTransitionTime: "1995-10-31T09:59:43Z"
message: "71" message: "73"
reason: "70" reason: "72"
status: aTGÒ鵌Ē3 status: ""
type: 鯶縆 type: ""
currentMetrics: currentMetrics:
- external: - containerResource:
currentAverageValue: "606" container: "64"
currentValue: "229" currentAverageUtilization: -861915578
metricName: "63" currentAverageValue: "111"
name: '!@@)Zq='
external:
currentAverageValue: "288"
currentValue: "200"
metricName: "65"
metricSelector: metricSelector:
matchExpressions: matchExpressions:
- key: FC-rtSY.g._2F7.-_e..OP - key: v55039780bdw0-1-47rrw8-7/U_--56-.7D.3_KPg___Kp
operator: Exists operator: In
values:
- N7_-Zp_._w
matchLabels: 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: object:
averageValue: "14" averageValue: "817"
currentValue: "55" currentValue: "856"
metricName: "49" metricName: "50"
selector: selector:
matchExpressions: matchExpressions:
- key: 665--4-j8---t6-r7--9.9dy/XvSA..1 - key: 4_.-N_g-.._5
operator: Exists operator: In
values:
- 2qz.W..4....-h._.GgT7_7B_D-..-.k4u-zA_--_.-.6GA26C-s.Nj-d-4_t
matchLabels: 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: target:
apiVersion: "48" apiVersion: "49"
kind: "46" kind: "47"
name: "47" name: "48"
pods: pods:
currentAverageValue: "878" currentAverageValue: "400"
metricName: "56" metricName: "57"
selector: selector:
matchExpressions: matchExpressions:
- key: g-..__._____K_g1cXfr.4_.B - 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: DoesNotExist operator: NotIn
values:
- 8._Q.6.I--2_9.v.--_.--4QQ.-s.H.Hf
matchLabels: 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: resource:
currentAverageUtilization: -1607821167 currentAverageUtilization: 769521729
currentAverageValue: "832" currentAverageValue: "727"
name: šeSvEȤƏ埮pɵ{WOŭW灬pȭ name: ƻ遲njlȘ鹾KƂʼnç
type: ' ïì«丯Ƙ枛牐ɺ皚|懥' type: ""
currentReplicas: 596942561 currentReplicas: -1462219068
desiredReplicas: -1880980172 desiredReplicas: -370386363
observedGeneration: -7224326297454280417 observedGeneration: 61436896663269868

View File

@ -112,159 +112,169 @@
"averageUtilization": 580681683 "averageUtilization": 580681683
} }
}, },
"containerResource": {
"name": "Ɋł/擇ɦĽ胚O醔ɍ厶耈 ",
"target": {
"type": "禒Ƙá腿ħ缶.蒅",
"value": "999",
"averageValue": "151",
"averageUtilization": -1105572246
},
"container": "39"
},
"external": { "external": {
"metric": { "metric": {
"name": "39", "name": "40",
"selector": { "selector": {
"matchLabels": { "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": [ "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", "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": "In", "operator": "DoesNotExist"
"values": [
"Q3_Y.5.-..P_pDZ-._._t__2k"
]
} }
] ]
} }
}, },
"target": { "target": {
"type": "3.v-鿧悮坮Ȣ", "type": "ʣy豎@ɀ羭,铻OŤ",
"value": "82", "value": "830",
"averageValue": "301", "averageValue": "799",
"averageUtilization": -521487971 "averageUtilization": 747521320
} }
} }
} }
], ],
"behavior": { "behavior": {
"scaleUp": { "scaleUp": {
"stabilizationWindowSeconds": 1761963371, "stabilizationWindowSeconds": -648954478,
"selectPolicy": "0矀Kʝ瘴I\\p[ħsĨɆâĺɗŹ倗S", "selectPolicy": "Ƿ裚瓶釆Ɗ+j忊",
"policies": [ "policies": [
{ {
"type": "嶗U", "type": "ȫ焗捏ĨFħ籘Àǒɿʒ刽",
"value": -1285424066, "value": 427196286,
"periodSeconds": -686523310 "periodSeconds": 1048864116
} }
] ]
}, },
"scaleDown": { "scaleDown": {
"stabilizationWindowSeconds": 1206365825, "stabilizationWindowSeconds": -342705708,
"selectPolicy": "/ɸɎ R§耶FfBls3!", "selectPolicy": "褎weLJèux",
"policies": [ "policies": [
{ {
"type": "ɾģ毋Ó6dz娝嘚", "type": "VƋZ1Ůđ眊ľǎɳ,ǿ飏",
"value": 627713162, "value": 2040455355,
"periodSeconds": 1255312175 "periodSeconds": 1505972335
} }
] ]
} }
} }
}, },
"status": { "status": {
"observedGeneration": -7477362499801752548, "observedGeneration": -115578794491385044,
"currentReplicas": 267768240, "currentReplicas": 474119379,
"desiredReplicas": -127849333, "desiredReplicas": 1923334396,
"currentMetrics": [ "currentMetrics": [
{ {
"type": "Ǖɳɷ9Ì崟¿瘦ɖ緕", "type": "0Ƹ[Ęİ榌U",
"object": { "object": {
"metric": { "metric": {
"name": "46", "name": "47",
"selector": { "selector": {
"matchLabels": { "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": [ "matchExpressions": [
{ {
"key": "6-d42--clo90---461v-07r--0---8-30i-uo/9DF", "key": "Dp665O_4Gj._BXt.O-7___-Y_um-_8r--684._-_188",
"operator": "In", "operator": "NotIn",
"values": [ "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": { "current": {
"value": "526", "value": "124",
"averageValue": "860", "averageValue": "472",
"averageUtilization": -126958936 "averageUtilization": -1666319281
}, },
"describedObject": { "describedObject": {
"kind": "53", "kind": "54",
"name": "54", "name": "55",
"apiVersion": "55" "apiVersion": "56"
} }
}, },
"pods": { "pods": {
"metric": { "metric": {
"name": "56", "name": "57",
"selector": { "selector": {
"matchLabels": { "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": [ "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", "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": "In", "operator": "Exists"
"values": [
"y.8_8"
]
} }
] ]
} }
}, },
"current": { "current": {
"value": "671", "value": "378",
"averageValue": "683", "averageValue": "328",
"averageUtilization": 1008425444 "averageUtilization": -1050824692
} }
}, },
"resource": { "resource": {
"name": "Ƈè*鑏='ʨ|ǓÓ敆OɈÏ 瞍髃", "name": "反-n覦灲閈誹ʅ蕉ɼ搳ǭ濑箨ʨ",
"current": { "current": {
"value": "93", "value": "113",
"averageValue": "183", "averageValue": "653",
"averageUtilization": -392406530 "averageUtilization": 1190831814
} }
}, },
"containerResource": {
"name": "腂ǂǚŜEuEy",
"current": {
"value": "77",
"averageValue": "394",
"averageUtilization": -1945921250
},
"container": "64"
},
"external": { "external": {
"metric": { "metric": {
"name": "63", "name": "65",
"selector": { "selector": {
"matchLabels": { "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": [ "matchExpressions": [
{ {
"key": "c-.F5_x.KNC0-.-m_0-m-6Sp_N-S..o", "key": "n_H-.___._D8.TS-jJ.Ys_Mop34_-2",
"operator": "In", "operator": "DoesNotExist"
"values": [
"g-_4Q__-v_t_u_.__I_-_-3-3--5X1rh-K5y_AzOBW.9oE9_6.--v7"
]
} }
] ]
} }
}, },
"current": { "current": {
"value": "287", "value": "444",
"averageValue": "759", "averageValue": "797",
"averageUtilization": -1175595426 "averageUtilization": 1928526133
} }
} }
} }
], ],
"conditions": [ "conditions": [
{ {
"type": "`翾'ųŎ群E牬庘颮6(|ǖû", "type": "44矕",
"status": "龢ÄƤUǷ坒ŕF5o儎ĄÇ稕E", "status": "ƱÁR»淹揀",
"lastTransitionTime": "2682-06-14T06:09:58Z", "lastTransitionTime": "2026-05-26T09:35:23Z",
"reason": "70", "reason": "72",
"message": "71" "message": "73"
} }
] ]
} }

View File

@ -33,36 +33,42 @@ spec:
behavior: behavior:
scaleDown: scaleDown:
policies: policies:
- periodSeconds: 1255312175 - periodSeconds: 1505972335
type: ɾģ毋Ó6dz娝嘚 type: VƋZ1Ůđ眊ľǎɳ,ǿ飏
value: 627713162 value: 2040455355
selectPolicy: /ɸɎ R§耶FfBls3! selectPolicy: 褎weLJèux
stabilizationWindowSeconds: 1206365825 stabilizationWindowSeconds: -342705708
scaleUp: scaleUp:
policies: policies:
- periodSeconds: -686523310 - periodSeconds: 1048864116
type: 嶗U type: ȫ焗捏ĨFħ籘Àǒɿʒ刽
value: -1285424066 value: 427196286
selectPolicy: 0矀Kʝ瘴I\p[ħsĨɆâĺɗŹ倗S selectPolicy: Ƿ裚瓶釆Ɗ+j忊
stabilizationWindowSeconds: 1761963371 stabilizationWindowSeconds: -648954478
maxReplicas: -1971381490 maxReplicas: -1971381490
metrics: metrics:
- external: - containerResource:
container: "39"
name: Ɋł/擇ɦĽ胚O醔ɍ厶耈 
target:
averageUtilization: -1105572246
averageValue: "151"
type: 禒Ƙá腿ħ缶.蒅
value: "999"
external:
metric: metric:
name: "39" name: "40"
selector: selector:
matchExpressions: 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 - 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: In operator: DoesNotExist
values:
- Q3_Y.5.-..P_pDZ-._._t__2k
matchLabels: 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: target:
averageUtilization: -521487971 averageUtilization: 747521320
averageValue: "301" averageValue: "799"
type: 3.v-鿧悮坮Ȣ type: ʣy豎@ɀ羭,铻OŤ
value: "82" value: "830"
object: object:
describedObject: describedObject:
apiVersion: "24" apiVersion: "24"
@ -112,69 +118,71 @@ spec:
name: "20" name: "20"
status: status:
conditions: conditions:
- lastTransitionTime: "2682-06-14T06:09:58Z" - lastTransitionTime: "2026-05-26T09:35:23Z"
message: "71" message: "73"
reason: "70" reason: "72"
status: 龢ÄƤUǷ坒ŕF5o儎ĄÇ稕E status: ƱÁR»淹揀
type: '`翾''ųŎ群E牬庘颮6(|ǖû' type: 44矕
currentMetrics: currentMetrics:
- external: - containerResource:
container: "64"
current: current:
averageUtilization: -1175595426 averageUtilization: -1945921250
averageValue: "759" averageValue: "394"
value: "287" value: "77"
name: 腂ǂǚŜEuEy
external:
current:
averageUtilization: 1928526133
averageValue: "797"
value: "444"
metric: metric:
name: "63" name: "65"
selector: selector:
matchExpressions: matchExpressions:
- key: c-.F5_x.KNC0-.-m_0-m-6Sp_N-S..o - key: n_H-.___._D8.TS-jJ.Ys_Mop34_-2
operator: In operator: DoesNotExist
values:
- g-_4Q__-v_t_u_.__I_-_-3-3--5X1rh-K5y_AzOBW.9oE9_6.--v7
matchLabels: 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: object:
current: current:
averageUtilization: -126958936 averageUtilization: -1666319281
averageValue: "860" averageValue: "472"
value: "526" value: "124"
describedObject: describedObject:
apiVersion: "55" apiVersion: "56"
kind: "53" kind: "54"
name: "54" name: "55"
metric: metric:
name: "46" name: "47"
selector: selector:
matchExpressions: matchExpressions:
- key: 6-d42--clo90---461v-07r--0---8-30i-uo/9DF - key: Dp665O_4Gj._BXt.O-7___-Y_um-_8r--684._-_188
operator: In operator: NotIn
values: 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: 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: pods:
current: current:
averageUtilization: 1008425444 averageUtilization: -1050824692
averageValue: "683" averageValue: "328"
value: "671" value: "378"
metric: metric:
name: "56" name: "57"
selector: selector:
matchExpressions: 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 - 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: In operator: Exists
values:
- y.8_8
matchLabels: 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 5.-_--.VEa-_gn.8-c.3: F._oX-F9_.5vN5.25aWx.2aM214_.-C
: ET_..3dCv3j._.-_pP__up.2L_s-o779._-k-N
resource: resource:
current: current:
averageUtilization: -392406530 averageUtilization: 1190831814
averageValue: "183" averageValue: "653"
value: "93" value: "113"
name: Ƈè*鑏='ʨ|ǓÓ敆OɈÏ 瞍髃 name: 反-n覦灲閈誹ʅ蕉ɼ搳ǭ濑箨ʨ
type: Ǖɳɷ9Ì崟¿瘦ɖ緕 type: 0Ƹ[Ęİ榌U
currentReplicas: 267768240 currentReplicas: 474119379
desiredReplicas: -127849333 desiredReplicas: 1923334396
observedGeneration: -7477362499801752548 observedGeneration: -115578794491385044

View File

@ -30,6 +30,8 @@ import (
func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenAPIDefinition { func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenAPIDefinition {
return 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.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.ExternalMetricSource": schema_k8sio_api_autoscaling_v1_ExternalMetricSource(ref),
"k8s.io/api/autoscaling/v1.ExternalMetricStatus": schema_k8sio_api_autoscaling_v1_ExternalMetricStatus(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 { func schema_k8sio_api_autoscaling_v1_CrossVersionObjectReference(ref common.ReferenceCallback) common.OpenAPIDefinition {
return common.OpenAPIDefinition{ return common.OpenAPIDefinition{
Schema: spec.Schema{ Schema: spec.Schema{
@ -469,7 +557,7 @@ func schema_k8sio_api_autoscaling_v1_MetricSpec(ref common.ReferenceCallback) co
Properties: map[string]spec.Schema{ Properties: map[string]spec.Schema{
"type": { "type": {
SchemaProps: spec.SchemaProps{ 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"}, Type: []string{"string"},
Format: "", Format: "",
}, },
@ -492,6 +580,12 @@ func schema_k8sio_api_autoscaling_v1_MetricSpec(ref common.ReferenceCallback) co
Ref: ref("k8s.io/api/autoscaling/v1.ResourceMetricSource"), 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": { "external": {
SchemaProps: spec.SchemaProps{ 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).", 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{ 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{ Properties: map[string]spec.Schema{
"type": { "type": {
SchemaProps: spec.SchemaProps{ 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"}, Type: []string{"string"},
Format: "", Format: "",
}, },
@ -539,6 +633,12 @@ func schema_k8sio_api_autoscaling_v1_MetricStatus(ref common.ReferenceCallback)
Ref: ref("k8s.io/api/autoscaling/v1.ResourceMetricStatus"), 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": { "external": {
SchemaProps: spec.SchemaProps{ 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).", 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{ 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) 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: default:
w.Write(LEVEL_1, "<unknown metric type %q>\n", string(metric.Type)) 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", "multiple metrics",
autoscalingv2beta2.HorizontalPodAutoscaler{ autoscalingv2beta2.HorizontalPodAutoscaler{