diff --git a/pkg/apis/autoscaling/annotations.go b/pkg/apis/autoscaling/annotations.go index 0acfb04ca8..4c377561bb 100644 --- a/pkg/apis/autoscaling/annotations.go +++ b/pkg/apis/autoscaling/annotations.go @@ -16,13 +16,13 @@ limitations under the License. package autoscaling -// OtherMetricsAnnotation is the annotation which holds non-CPU-utilization HPA metric +// MetricSpecsAnnotation is the annotation which holds non-CPU-utilization HPA metric // specs when converting the `Metrics` field from autoscaling/v2alpha1 -const OtherMetricsAnnotation = "autoscaling.alpha.kubernetes.io/metrics" +const MetricSpecsAnnotation = "autoscaling.alpha.kubernetes.io/metrics" -// CurrentMetricsAnnotation is the annotation which holds non-CPU-utilization HPA metric +// MetricStatusesAnnotation is the annotation which holds non-CPU-utilization HPA metric // statuses when converting the `CurrentMetrics` field from autoscaling/v2alpha1 -const CurrentMetricsAnnotation = "autoscaling.alpha.kubernetes.io/current-metrics" +const MetricStatusesAnnotation = "autoscaling.alpha.kubernetes.io/current-metrics" // DefaultCPUUtilization is the default value for CPU utilization, provided no other // metrics are present. This is here because it's used by both the v2alpha1 defaulting diff --git a/pkg/apis/autoscaling/v1/BUILD b/pkg/apis/autoscaling/v1/BUILD index 2a35aede66..c29d975262 100644 --- a/pkg/apis/autoscaling/v1/BUILD +++ b/pkg/apis/autoscaling/v1/BUILD @@ -26,9 +26,11 @@ go_library( tags = ["automanaged"], deps = [ "//pkg/api:go_default_library", + "//pkg/api/v1:go_default_library", "//pkg/apis/autoscaling:go_default_library", "//vendor:github.com/gogo/protobuf/proto", "//vendor:github.com/ugorji/go/codec", + "//vendor:k8s.io/apimachinery/pkg/api/resource", "//vendor:k8s.io/apimachinery/pkg/apis/meta/v1", "//vendor:k8s.io/apimachinery/pkg/conversion", "//vendor:k8s.io/apimachinery/pkg/runtime", diff --git a/pkg/apis/autoscaling/v1/conversion.go b/pkg/apis/autoscaling/v1/conversion.go index 5aeac55a04..d1723aa1a4 100644 --- a/pkg/apis/autoscaling/v1/conversion.go +++ b/pkg/apis/autoscaling/v1/conversion.go @@ -47,16 +47,27 @@ func Convert_autoscaling_HorizontalPodAutoscaler_To_v1_HorizontalPodAutoscaler(i return err } - otherMetrics := make([]autoscaling.MetricSpec, 0, len(in.Spec.Metrics)) + otherMetrics := make([]MetricSpec, 0, len(in.Spec.Metrics)) for _, metric := range in.Spec.Metrics { if metric.Type == autoscaling.ResourceMetricSourceType && metric.Resource != nil && metric.Resource.Name == api.ResourceCPU && metric.Resource.TargetAverageUtilization != nil { continue } - otherMetrics = append(otherMetrics, metric) + convMetric := MetricSpec{} + if err := Convert_autoscaling_MetricSpec_To_v1_MetricSpec(&metric, &convMetric, s); err != nil { + return err + } + otherMetrics = append(otherMetrics, convMetric) } // NB: we need to save the status even if it maps to a CPU utilization status in order to save the raw value as well + currentMetrics := make([]MetricStatus, len(in.Status.CurrentMetrics)) + for i, currentMetric := range in.Status.CurrentMetrics { + if err := Convert_autoscaling_MetricStatus_To_v1_MetricStatus(¤tMetric, ¤tMetrics[i], s); err != nil { + return err + } + } + if len(otherMetrics) > 0 || len(in.Status.CurrentMetrics) > 0 { old := out.Annotations out.Annotations = make(map[string]string, len(old)+2) @@ -72,15 +83,15 @@ func Convert_autoscaling_HorizontalPodAutoscaler_To_v1_HorizontalPodAutoscaler(i if err != nil { return err } - out.Annotations[autoscaling.OtherMetricsAnnotation] = string(otherMetricsEnc) + out.Annotations[autoscaling.MetricSpecsAnnotation] = string(otherMetricsEnc) } if len(in.Status.CurrentMetrics) > 0 { - currentMetricsEnc, err := json.Marshal(in.Status.CurrentMetrics) + currentMetricsEnc, err := json.Marshal(currentMetrics) if err != nil { return err } - out.Annotations[autoscaling.CurrentMetricsAnnotation] = string(currentMetricsEnc) + out.Annotations[autoscaling.MetricStatusesAnnotation] = string(currentMetricsEnc) } return nil @@ -91,23 +102,35 @@ func Convert_v1_HorizontalPodAutoscaler_To_autoscaling_HorizontalPodAutoscaler(i return err } - if otherMetricsEnc, hasOtherMetrics := out.Annotations[autoscaling.OtherMetricsAnnotation]; hasOtherMetrics { - var otherMetrics []autoscaling.MetricSpec + if otherMetricsEnc, hasOtherMetrics := out.Annotations[autoscaling.MetricSpecsAnnotation]; hasOtherMetrics { + var otherMetrics []MetricSpec if err := json.Unmarshal([]byte(otherMetricsEnc), &otherMetrics); err != nil { return err } - for _, metric := range otherMetrics { - out.Spec.Metrics = append(out.Spec.Metrics, metric) + out.Spec.Metrics = make([]autoscaling.MetricSpec, len(otherMetrics)) + for i, metric := range otherMetrics { + if err := Convert_v1_MetricSpec_To_autoscaling_MetricSpec(&metric, &out.Spec.Metrics[i], s); err != nil { + return err + } } + delete(out.Annotations, autoscaling.MetricSpecsAnnotation) } - if currentMetricsEnc, hasCurrentMetrics := out.Annotations[autoscaling.CurrentMetricsAnnotation]; hasCurrentMetrics { + if currentMetricsEnc, hasCurrentMetrics := out.Annotations[autoscaling.MetricStatusesAnnotation]; hasCurrentMetrics { // ignore any existing status values -- the ones here have more information - out.Status.CurrentMetrics = nil - if err := json.Unmarshal([]byte(currentMetricsEnc), &out.Status.CurrentMetrics); err != nil { + var currentMetrics []MetricStatus + if err := json.Unmarshal([]byte(currentMetricsEnc), ¤tMetrics); err != nil { return err } + + out.Status.CurrentMetrics = make([]autoscaling.MetricStatus, len(currentMetrics)) + for i, currentMetric := range currentMetrics { + if err := Convert_v1_MetricStatus_To_autoscaling_MetricStatus(¤tMetric, &out.Status.CurrentMetrics[i], s); err != nil { + return err + } + } + delete(out.Annotations, autoscaling.MetricStatusesAnnotation) } // autoscaling/v1 formerly had an implicit default applied in the controller. In v2alpha1, we apply it explicitly. diff --git a/pkg/apis/autoscaling/v1/types.go b/pkg/apis/autoscaling/v1/types.go index 2e867c9c06..81402bac23 100644 --- a/pkg/apis/autoscaling/v1/types.go +++ b/pkg/apis/autoscaling/v1/types.go @@ -16,7 +16,11 @@ limitations under the License. package v1 -import metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +import ( + "k8s.io/apimachinery/pkg/api/resource" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/kubernetes/pkg/api/v1" +) // CrossVersionObjectReference contains enough information to let you identify the referred resource. type CrossVersionObjectReference struct { @@ -132,3 +136,161 @@ type ScaleStatus struct { // +optional Selector string `json:"selector,omitempty" protobuf:"bytes,2,opt,name=selector"` } + +// the types below are used in the alpha metrics annotation + +// MetricSourceType indicates the type of metric. +type MetricSourceType string + +var ( + // ObjectMetricSourceType is a metric describing a kubernetes object + // (for example, hits-per-second on an Ingress object). + ObjectMetricSourceType MetricSourceType = "Object" + // PodsMetricSourceType is 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. + PodsMetricSourceType MetricSourceType = "Pods" + // ResourceMetricSourceType is 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 (the "pods" source). + ResourceMetricSourceType MetricSourceType = "Resource" +) + +// MetricSpec specifies how to scale based on a single metric +// (only `type` and one other matching field should be set at once). +type MetricSpec struct { + // type is the type of metric source. It should match one of the fields below. + Type MetricSourceType `json:"type" protobuf:"bytes,1,name=type"` + + // object refers to a metric describing a single kubernetes object + // (for example, hits-per-second on an Ingress object). + // +optional + Object *ObjectMetricSource `json:"object,omitempty" protobuf:"bytes,2,opt,name=object"` + // 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. + // +optional + Pods *PodsMetricSource `json:"pods,omitempty" protobuf:"bytes,3,opt,name=pods"` + // 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. + // +optional + Resource *ResourceMetricSource `json:"resource,omitempty" protobuf:"bytes,4,opt,name=resource"` +} + +// ObjectMetricSource indicates how to scale on a metric describing a +// kubernetes object (for example, hits-per-second on an Ingress object). +type ObjectMetricSource struct { + // target is the described Kubernetes object. + Target CrossVersionObjectReference `json:"target" protobuf:"bytes,1,name=target"` + + // metricName is the name of the metric in question. + MetricName string `json:"metricName" protobuf:"bytes,2,name=metricName"` + // targetValue is the target value of the metric (as a quantity). + TargetValue resource.Quantity `json:"targetValue" protobuf:"bytes,3,name=targetValue"` +} + +// PodsMetricSource indicates how to scale on 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. +type PodsMetricSource struct { + // metricName is the name of the metric in question + MetricName string `json:"metricName" protobuf:"bytes,1,name=metricName"` + // targetAverageValue is the target value of the average of the + // metric across all relevant pods (as a quantity) + TargetAverageValue resource.Quantity `json:"targetAverageValue" protobuf:"bytes,2,name=targetAverageValue"` +} + +// ResourceMetricSource 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 ResourceMetricSource 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 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"` +} + +// MetricStatus describes the last-read state of a single metric. +type MetricStatus struct { + // type is the type of metric source. It will match one of the fields below. + Type MetricSourceType `json:"type" protobuf:"bytes,1,name=type"` + + // object refers to a metric describing a single kubernetes object + // (for example, hits-per-second on an Ingress object). + // +optional + Object *ObjectMetricStatus `json:"object,omitempty" protobuf:"bytes,2,opt,name=object"` + // 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. + // +optional + Pods *PodsMetricStatus `json:"pods,omitempty" protobuf:"bytes,3,opt,name=pods"` + // 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. + // +optional + Resource *ResourceMetricStatus `json:"resource,omitempty" protobuf:"bytes,4,opt,name=resource"` +} + +// ObjectMetricStatus indicates the current value of a metric describing a +// kubernetes object (for example, hits-per-second on an Ingress object). +type ObjectMetricStatus struct { + // target is the described Kubernetes object. + Target CrossVersionObjectReference `json:"target" protobuf:"bytes,1,name=target"` + + // metricName is the name of the metric in question. + MetricName string `json:"metricName" protobuf:"bytes,2,name=metricName"` + // currentValue is the current value of the metric (as a quantity). + CurrentValue resource.Quantity `json:"currentValue" protobuf:"bytes,3,name=currentValue"` +} + +// PodsMetricStatus indicates the current value of a metric describing each pod in +// the current scale target (for example, transactions-processed-per-second). +type PodsMetricStatus struct { + // metricName is the name of the metric in question + MetricName string `json:"metricName" protobuf:"bytes,1,name=metricName"` + // currentAverageValue is the current value of the average of the + // metric across all relevant pods (as a quantity) + CurrentAverageValue resource.Quantity `json:"currentAverageValue" protobuf:"bytes,2,name=currentAverageValue"` +} + +// ResourceMetricStatus 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 ResourceMetricStatus 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 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"` +}