diff --git a/pkg/apis/autoscaling/annotations.go b/pkg/apis/autoscaling/annotations.go index 4c377561bb3..c6586ea7060 100644 --- a/pkg/apis/autoscaling/annotations.go +++ b/pkg/apis/autoscaling/annotations.go @@ -24,6 +24,10 @@ const MetricSpecsAnnotation = "autoscaling.alpha.kubernetes.io/metrics" // statuses when converting the `CurrentMetrics` field from autoscaling/v2alpha1 const MetricStatusesAnnotation = "autoscaling.alpha.kubernetes.io/current-metrics" +// HorizontalPodAutoscalerConditionsAnnotation is the annotation which holds the conditions +// of an HPA when converting the `Conditions` field from autoscaling/v2alpha1 +const HorizontalPodAutoscalerConditionsAnnotation = "autoscaling.alpha.kubernetes.io/conditions" + // 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 // logic, and the pseudo-defaulting done in v1 conversion. diff --git a/pkg/apis/autoscaling/types.go b/pkg/apis/autoscaling/types.go index 27d9a6026dd..282c3908cb4 100644 --- a/pkg/apis/autoscaling/types.go +++ b/pkg/apis/autoscaling/types.go @@ -202,6 +202,59 @@ type HorizontalPodAutoscalerStatus struct { // CurrentMetrics is the last read state of the metrics used by this autoscaler. CurrentMetrics []MetricStatus + + // Conditions is the set of conditions required for this autoscaler to scale its target, + // and indicates whether or not those conditions are met. + Conditions []HorizontalPodAutoscalerCondition +} + +// ConditionStatus indicates the status of a condition (true, false, or unknown). +type ConditionStatus string + +// These are valid condition statuses. "ConditionTrue" means a resource is in the condition; +// "ConditionFalse" means a resource is not in the condition; "ConditionUnknown" means kubernetes +// can't decide if a resource is in the condition or not. In the future, we could add other +// intermediate conditions, e.g. ConditionDegraded. +const ( + ConditionTrue ConditionStatus = "True" + ConditionFalse ConditionStatus = "False" + ConditionUnknown ConditionStatus = "Unknown" +) + +// HorizontalPodAutoscalerConditionType are the valid conditions of +// a HorizontalPodAutoscaler. +type HorizontalPodAutoscalerConditionType string + +var ( + // ScalingActive indicates that the HPA controller is able to scale if necessary: + // it's correctly configured, can fetch the desired metrics, and isn't disabled. + ScalingActive HorizontalPodAutoscalerConditionType = "ScalingActive" + // AbleToScale indicates a lack of transient issues which prevent scaling from occuring, + // such as being in a backoff window, or being unable to access/update the target scale. + AbleToScale HorizontalPodAutoscalerConditionType = "AbleToScale" + // ScalingLimited indicates that the calculated scale based on metrics would be above or + // below the range for the HPA, and has thus been capped. + ScalingLimited HorizontalPodAutoscalerConditionType = "ScalingLimited" +) + +// HorizontalPodAutoscalerCondition describes the state of +// a HorizontalPodAutoscaler at a certain point. +type HorizontalPodAutoscalerCondition struct { + // Type describes the current condition + Type HorizontalPodAutoscalerConditionType + // Status is the status of the condition (True, False, Unknown) + Status ConditionStatus + // LastTransitionTime is the last time the condition transitioned from + // one status to another + // +optional + LastTransitionTime metav1.Time + // Reason is the reason for the condition's last transition. + // +optional + Reason string + // Message is a human-readable explanation containing details about + // the transition + // +optional + Message string } // MetricStatus describes the last-read state of a single metric. diff --git a/pkg/apis/autoscaling/v1/conversion.go b/pkg/apis/autoscaling/v1/conversion.go index b8096af807d..0bce7a123b3 100644 --- a/pkg/apis/autoscaling/v1/conversion.go +++ b/pkg/apis/autoscaling/v1/conversion.go @@ -68,9 +68,17 @@ func Convert_autoscaling_HorizontalPodAutoscaler_To_v1_HorizontalPodAutoscaler(i } } - if len(otherMetrics) > 0 || len(in.Status.CurrentMetrics) > 0 { + // store HPA conditions in an annotation + currentConditions := make([]HorizontalPodAutoscalerCondition, len(in.Status.Conditions)) + for i, currentCondition := range in.Status.Conditions { + if err := Convert_autoscaling_HorizontalPodAutoscalerCondition_To_v1_HorizontalPodAutoscalerCondition(¤tCondition, ¤tConditions[i], s); err != nil { + return err + } + } + + if len(otherMetrics) > 0 || len(in.Status.CurrentMetrics) > 0 || len(currentConditions) > 0 { old := out.Annotations - out.Annotations = make(map[string]string, len(old)+2) + out.Annotations = make(map[string]string, len(old)+3) if old != nil { for k, v := range old { out.Annotations[k] = v @@ -94,6 +102,14 @@ func Convert_autoscaling_HorizontalPodAutoscaler_To_v1_HorizontalPodAutoscaler(i out.Annotations[autoscaling.MetricStatusesAnnotation] = string(currentMetricsEnc) } + if len(in.Status.Conditions) > 0 { + currentConditionsEnc, err := json.Marshal(currentConditions) + if err != nil { + return err + } + out.Annotations[autoscaling.HorizontalPodAutoscalerConditionsAnnotation] = string(currentConditionsEnc) + } + return nil } @@ -154,6 +170,21 @@ func Convert_v1_HorizontalPodAutoscaler_To_autoscaling_HorizontalPodAutoscaler(i *out.Spec.Metrics[0].Resource.TargetAverageUtilization = autoscaling.DefaultCPUUtilization } + if currentConditionsEnc, hasCurrentConditions := out.Annotations[autoscaling.HorizontalPodAutoscalerConditionsAnnotation]; hasCurrentConditions { + var currentConditions []HorizontalPodAutoscalerCondition + if err := json.Unmarshal([]byte(currentConditionsEnc), ¤tConditions); err != nil { + return err + } + + out.Status.Conditions = make([]autoscaling.HorizontalPodAutoscalerCondition, len(currentConditions)) + for i, currentCondition := range currentConditions { + if err := Convert_v1_HorizontalPodAutoscalerCondition_To_autoscaling_HorizontalPodAutoscalerCondition(¤tCondition, &out.Status.Conditions[i], s); err != nil { + return err + } + } + delete(out.Annotations, autoscaling.HorizontalPodAutoscalerConditionsAnnotation) + } + return nil } diff --git a/pkg/apis/autoscaling/v1/types.go b/pkg/apis/autoscaling/v1/types.go index 81402bac235..309d214b1c9 100644 --- a/pkg/apis/autoscaling/v1/types.go +++ b/pkg/apis/autoscaling/v1/types.go @@ -251,6 +251,42 @@ type MetricStatus struct { Resource *ResourceMetricStatus `json:"resource,omitempty" protobuf:"bytes,4,opt,name=resource"` } +// HorizontalPodAutoscalerConditionType are the valid conditions of +// a HorizontalPodAutoscaler. +type HorizontalPodAutoscalerConditionType string + +var ( + // ScalingActive indicates that the HPA controller is able to scale if necessary: + // it's correctly configured, can fetch the desired metrics, and isn't disabled. + ScalingActive HorizontalPodAutoscalerConditionType = "ScalingActive" + // AbleToScale indicates a lack of transient issues which prevent scaling from occuring, + // such as being in a backoff window, or being unable to access/update the target scale. + AbleToScale HorizontalPodAutoscalerConditionType = "AbleToScale" + // ScalingLimited indicates that the calculated scale based on metrics would be above or + // below the range for the HPA, and has thus been capped. + ScalingLimited HorizontalPodAutoscalerConditionType = "ScalingLimited" +) + +// HorizontalPodAutoscalerCondition describes the state of +// a HorizontalPodAutoscaler at a certain point. +type HorizontalPodAutoscalerCondition struct { + // type describes the current condition + Type HorizontalPodAutoscalerConditionType `json:"type" protobuf:"bytes,1,name=type"` + // status is the status of the condition (True, False, Unknown) + Status v1.ConditionStatus `json:"status" protobuf:"bytes,2,name=status"` + // lastTransitionTime is the last time the condition transitioned from + // one status to another + // +optional + LastTransitionTime metav1.Time `json:"lastTransitionTime,omitempty" protobuf:"bytes,3,opt,name=lastTransitionTime"` + // reason is the reason for the condition's last transition. + // +optional + Reason string `json:"reason,omitempty" protobuf:"bytes,4,opt,name=reason"` + // message is a human-readable explanation containing details about + // the transition + // +optional + Message string `json:"message,omitempty" protobuf:"bytes,5,opt,name=message"` +} + // ObjectMetricStatus indicates the current value of a metric describing a // kubernetes object (for example, hits-per-second on an Ingress object). type ObjectMetricStatus struct { diff --git a/pkg/apis/autoscaling/v2alpha1/types.go b/pkg/apis/autoscaling/v2alpha1/types.go index c8fc92969d8..2918dca254d 100644 --- a/pkg/apis/autoscaling/v2alpha1/types.go +++ b/pkg/apis/autoscaling/v2alpha1/types.go @@ -166,6 +166,46 @@ type HorizontalPodAutoscalerStatus struct { // currentMetrics is the last read state of the metrics used by this autoscaler. CurrentMetrics []MetricStatus `json:"currentMetrics" protobuf:"bytes,5,rep,name=currentMetrics"` + + // conditions is the set of conditions required for this autoscaler to scale its target, + // and indicates whether or not those conditions are met. + Conditions []HorizontalPodAutoscalerCondition `json:"conditions" protobuf:"bytes,6,rep,name=conditions"` +} + +// HorizontalPodAutoscalerConditionType are the valid conditions of +// a HorizontalPodAutoscaler. +type HorizontalPodAutoscalerConditionType string + +var ( + // ScalingActive indicates that the HPA controller is able to scale if necessary: + // it's correctly configured, can fetch the desired metrics, and isn't disabled. + ScalingActive HorizontalPodAutoscalerConditionType = "ScalingActive" + // AbleToScale indicates a lack of transient issues which prevent scaling from occuring, + // such as being in a backoff window, or being unable to access/update the target scale. + AbleToScale HorizontalPodAutoscalerConditionType = "AbleToScale" + // ScalingLimited indicates that the calculated scale based on metrics would be above or + // below the range for the HPA, and has thus been capped. + ScalingLimited HorizontalPodAutoscalerConditionType = "ScalingLimited" +) + +// HorizontalPodAutoscalerCondition describes the state of +// a HorizontalPodAutoscaler at a certain point. +type HorizontalPodAutoscalerCondition struct { + // type describes the current condition + Type HorizontalPodAutoscalerConditionType `json:"type" protobuf:"bytes,1,name=type"` + // status is the status of the condition (True, False, Unknown) + Status v1.ConditionStatus `json:"status" protobuf:"bytes,2,name=status"` + // lastTransitionTime is the last time the condition transitioned from + // one status to another + // +optional + LastTransitionTime metav1.Time `json:"lastTransitionTime,omitempty" protobuf:"bytes,3,opt,name=lastTransitionTime"` + // reason is the reason for the condition's last transition. + // +optional + Reason string `json:"reason,omitempty" protobuf:"bytes,4,opt,name=reason"` + // message is a human-readable explanation containing details about + // the transition + // +optional + Message string `json:"message,omitempty" protobuf:"bytes,5,opt,name=message"` } // MetricStatus describes the last-read state of a single metric.