| /* |
| Copyright 2016 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 validation |
| |
| import ( |
| "strings" |
| "testing" |
| |
| "k8s.io/apimachinery/pkg/api/resource" |
| metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" |
| utilfeature "k8s.io/apiserver/pkg/util/feature" |
| featuregatetesting "k8s.io/component-base/featuregate/testing" |
| "k8s.io/kubernetes/pkg/apis/autoscaling" |
| api "k8s.io/kubernetes/pkg/apis/core" |
| "k8s.io/kubernetes/pkg/features" |
| utilpointer "k8s.io/utils/pointer" |
| ) |
| |
| func TestValidateScale(t *testing.T) { |
| successCases := []autoscaling.Scale{{ |
| ObjectMeta: metav1.ObjectMeta{ |
| Name: "frontend", |
| Namespace: metav1.NamespaceDefault, |
| }, |
| Spec: autoscaling.ScaleSpec{ |
| Replicas: 1, |
| }, |
| }, { |
| ObjectMeta: metav1.ObjectMeta{ |
| Name: "frontend", |
| Namespace: metav1.NamespaceDefault, |
| }, |
| Spec: autoscaling.ScaleSpec{ |
| Replicas: 10, |
| }, |
| }, { |
| ObjectMeta: metav1.ObjectMeta{ |
| Name: "frontend", |
| Namespace: metav1.NamespaceDefault, |
| }, |
| Spec: autoscaling.ScaleSpec{ |
| Replicas: 0, |
| }, |
| }} |
| |
| for _, successCase := range successCases { |
| if errs := ValidateScale(&successCase); len(errs) != 0 { |
| t.Errorf("expected success: %v", errs) |
| } |
| } |
| |
| errorCases := []struct { |
| scale autoscaling.Scale |
| msg string |
| }{{ |
| scale: autoscaling.Scale{ |
| ObjectMeta: metav1.ObjectMeta{ |
| Name: "frontend", |
| Namespace: metav1.NamespaceDefault, |
| }, |
| Spec: autoscaling.ScaleSpec{ |
| Replicas: -1, |
| }, |
| }, |
| msg: "must be greater than or equal to 0", |
| }} |
| |
| for _, c := range errorCases { |
| if errs := ValidateScale(&c.scale); len(errs) == 0 { |
| t.Errorf("expected failure for %s", c.msg) |
| } else if !strings.Contains(errs[0].Error(), c.msg) { |
| t.Errorf("unexpected error: %v, expected: %s", errs[0], c.msg) |
| } |
| } |
| } |
| |
| func TestValidateBehavior(t *testing.T) { |
| maxPolicy := autoscaling.MaxPolicySelect |
| minPolicy := autoscaling.MinPolicySelect |
| disabledPolicy := autoscaling.DisabledPolicySelect |
| incorrectPolicy := autoscaling.ScalingPolicySelect("incorrect") |
| simplePoliciesList := []autoscaling.HPAScalingPolicy{{ |
| Type: autoscaling.PercentScalingPolicy, |
| Value: 10, |
| PeriodSeconds: 1, |
| }, { |
| Type: autoscaling.PodsScalingPolicy, |
| Value: 1, |
| PeriodSeconds: 1800, |
| }} |
| successCases := []autoscaling.HorizontalPodAutoscalerBehavior{{ |
| ScaleUp: nil, |
| ScaleDown: nil, |
| }, { |
| ScaleUp: &autoscaling.HPAScalingRules{ |
| StabilizationWindowSeconds: utilpointer.Int32(3600), |
| SelectPolicy: &minPolicy, |
| Policies: simplePoliciesList, |
| }, |
| ScaleDown: &autoscaling.HPAScalingRules{ |
| StabilizationWindowSeconds: utilpointer.Int32(0), |
| SelectPolicy: &disabledPolicy, |
| Policies: simplePoliciesList, |
| }, |
| }, { |
| ScaleUp: &autoscaling.HPAScalingRules{ |
| StabilizationWindowSeconds: utilpointer.Int32(120), |
| SelectPolicy: &maxPolicy, |
| Policies: []autoscaling.HPAScalingPolicy{{ |
| Type: autoscaling.PodsScalingPolicy, |
| Value: 1, |
| PeriodSeconds: 2, |
| }, { |
| Type: autoscaling.PercentScalingPolicy, |
| Value: 3, |
| PeriodSeconds: 4, |
| }, { |
| Type: autoscaling.PodsScalingPolicy, |
| Value: 5, |
| PeriodSeconds: 6, |
| }, { |
| Type: autoscaling.PercentScalingPolicy, |
| Value: 7, |
| PeriodSeconds: 8, |
| }}, |
| }, |
| ScaleDown: &autoscaling.HPAScalingRules{ |
| StabilizationWindowSeconds: utilpointer.Int32(120), |
| SelectPolicy: &maxPolicy, |
| Policies: []autoscaling.HPAScalingPolicy{{ |
| Type: autoscaling.PodsScalingPolicy, |
| Value: 1, |
| PeriodSeconds: 2, |
| }, { |
| Type: autoscaling.PercentScalingPolicy, |
| Value: 3, |
| PeriodSeconds: 4, |
| }, { |
| Type: autoscaling.PodsScalingPolicy, |
| Value: 5, |
| PeriodSeconds: 6, |
| }, { |
| Type: autoscaling.PercentScalingPolicy, |
| Value: 7, |
| PeriodSeconds: 8, |
| }}, |
| }, |
| }} |
| for _, behavior := range successCases { |
| hpa := prepareHPAWithBehavior(behavior) |
| if errs := ValidateHorizontalPodAutoscaler(&hpa); len(errs) != 0 { |
| t.Errorf("expected success: %v", errs) |
| } |
| } |
| errorCases := []struct { |
| behavior autoscaling.HorizontalPodAutoscalerBehavior |
| msg string |
| }{{ |
| behavior: autoscaling.HorizontalPodAutoscalerBehavior{ |
| ScaleUp: &autoscaling.HPAScalingRules{ |
| SelectPolicy: &minPolicy, |
| }, |
| }, |
| msg: "spec.behavior.scaleUp.policies: Required value: must specify at least one Policy", |
| }, { |
| behavior: autoscaling.HorizontalPodAutoscalerBehavior{ |
| ScaleUp: &autoscaling.HPAScalingRules{ |
| StabilizationWindowSeconds: utilpointer.Int32(3601), |
| SelectPolicy: &minPolicy, |
| Policies: simplePoliciesList, |
| }, |
| }, |
| msg: "spec.behavior.scaleUp.stabilizationWindowSeconds: Invalid value: 3601: must be less than or equal to 3600", |
| }, { |
| behavior: autoscaling.HorizontalPodAutoscalerBehavior{ |
| ScaleUp: &autoscaling.HPAScalingRules{ |
| Policies: []autoscaling.HPAScalingPolicy{{ |
| Type: autoscaling.PodsScalingPolicy, |
| Value: 7, |
| PeriodSeconds: 1801, |
| }}, |
| }, |
| }, |
| msg: "spec.behavior.scaleUp.policies[0].periodSeconds: Invalid value: 1801: must be less than or equal to 1800", |
| }, { |
| behavior: autoscaling.HorizontalPodAutoscalerBehavior{ |
| ScaleUp: &autoscaling.HPAScalingRules{ |
| SelectPolicy: &incorrectPolicy, |
| Policies: []autoscaling.HPAScalingPolicy{{ |
| Type: autoscaling.PodsScalingPolicy, |
| Value: 7, |
| PeriodSeconds: 8, |
| }}, |
| }, |
| }, |
| msg: `spec.behavior.scaleUp.selectPolicy: Unsupported value: "incorrect": supported values: "Disabled", "Max", "Min"`, |
| }, { |
| behavior: autoscaling.HorizontalPodAutoscalerBehavior{ |
| ScaleUp: &autoscaling.HPAScalingRules{ |
| Policies: []autoscaling.HPAScalingPolicy{{ |
| Type: autoscaling.HPAScalingPolicyType("hm"), |
| Value: 7, |
| PeriodSeconds: 8, |
| }}, |
| }, |
| }, |
| msg: `spec.behavior.scaleUp.policies[0].type: Unsupported value: "hm": supported values: "Percent", "Pods"`, |
| }, { |
| behavior: autoscaling.HorizontalPodAutoscalerBehavior{ |
| ScaleUp: &autoscaling.HPAScalingRules{ |
| Policies: []autoscaling.HPAScalingPolicy{{ |
| Type: autoscaling.PodsScalingPolicy, |
| Value: 8, |
| }}, |
| }, |
| }, |
| msg: "spec.behavior.scaleUp.policies[0].periodSeconds: Invalid value: 0: must be greater than zero", |
| }, { |
| behavior: autoscaling.HorizontalPodAutoscalerBehavior{ |
| ScaleUp: &autoscaling.HPAScalingRules{ |
| Policies: []autoscaling.HPAScalingPolicy{{ |
| Type: autoscaling.PodsScalingPolicy, |
| PeriodSeconds: 8, |
| }}, |
| }, |
| }, |
| msg: "spec.behavior.scaleUp.policies[0].value: Invalid value: 0: must be greater than zero", |
| }, { |
| behavior: autoscaling.HorizontalPodAutoscalerBehavior{ |
| ScaleUp: &autoscaling.HPAScalingRules{ |
| Policies: []autoscaling.HPAScalingPolicy{{ |
| Type: autoscaling.PodsScalingPolicy, |
| PeriodSeconds: -1, |
| Value: 1, |
| }}, |
| }, |
| }, |
| msg: "spec.behavior.scaleUp.policies[0].periodSeconds: Invalid value: -1: must be greater than zero", |
| }, { |
| behavior: autoscaling.HorizontalPodAutoscalerBehavior{ |
| ScaleUp: &autoscaling.HPAScalingRules{ |
| Policies: []autoscaling.HPAScalingPolicy{{ |
| Type: autoscaling.PodsScalingPolicy, |
| PeriodSeconds: 1, |
| Value: -1, |
| }}, |
| }, |
| }, |
| msg: "spec.behavior.scaleUp.policies[0].value: Invalid value: -1: must be greater than zero", |
| }, { |
| behavior: autoscaling.HorizontalPodAutoscalerBehavior{ |
| ScaleDown: &autoscaling.HPAScalingRules{ |
| SelectPolicy: &minPolicy, |
| }, |
| }, |
| msg: "spec.behavior.scaleDown.policies: Required value: must specify at least one Policy", |
| }, { |
| behavior: autoscaling.HorizontalPodAutoscalerBehavior{ |
| ScaleDown: &autoscaling.HPAScalingRules{ |
| StabilizationWindowSeconds: utilpointer.Int32(3601), |
| SelectPolicy: &minPolicy, |
| Policies: simplePoliciesList, |
| }, |
| }, |
| msg: "spec.behavior.scaleDown.stabilizationWindowSeconds: Invalid value: 3601: must be less than or equal to 3600", |
| }, { |
| behavior: autoscaling.HorizontalPodAutoscalerBehavior{ |
| ScaleDown: &autoscaling.HPAScalingRules{ |
| Policies: []autoscaling.HPAScalingPolicy{{ |
| Type: autoscaling.PercentScalingPolicy, |
| Value: 7, |
| PeriodSeconds: 1801, |
| }}, |
| }, |
| }, |
| msg: "spec.behavior.scaleDown.policies[0].periodSeconds: Invalid value: 1801: must be less than or equal to 1800", |
| }, { |
| behavior: autoscaling.HorizontalPodAutoscalerBehavior{ |
| ScaleDown: &autoscaling.HPAScalingRules{ |
| SelectPolicy: &incorrectPolicy, |
| Policies: []autoscaling.HPAScalingPolicy{{ |
| Type: autoscaling.PodsScalingPolicy, |
| Value: 7, |
| PeriodSeconds: 8, |
| }}, |
| }, |
| }, |
| msg: `spec.behavior.scaleDown.selectPolicy: Unsupported value: "incorrect": supported values: "Disabled", "Max", "Min"`, |
| }, { |
| behavior: autoscaling.HorizontalPodAutoscalerBehavior{ |
| ScaleDown: &autoscaling.HPAScalingRules{ |
| Policies: []autoscaling.HPAScalingPolicy{{ |
| Type: autoscaling.HPAScalingPolicyType("hm"), |
| Value: 7, |
| PeriodSeconds: 8, |
| }}, |
| }, |
| }, |
| msg: `spec.behavior.scaleDown.policies[0].type: Unsupported value: "hm": supported values: "Percent", "Pods"`, |
| }, { |
| behavior: autoscaling.HorizontalPodAutoscalerBehavior{ |
| ScaleDown: &autoscaling.HPAScalingRules{ |
| Policies: []autoscaling.HPAScalingPolicy{{ |
| Type: autoscaling.PodsScalingPolicy, |
| Value: 8, |
| }}, |
| }, |
| }, |
| msg: "spec.behavior.scaleDown.policies[0].periodSeconds: Invalid value: 0: must be greater than zero", |
| }, { |
| behavior: autoscaling.HorizontalPodAutoscalerBehavior{ |
| ScaleDown: &autoscaling.HPAScalingRules{ |
| Policies: []autoscaling.HPAScalingPolicy{{ |
| Type: autoscaling.PodsScalingPolicy, |
| PeriodSeconds: 8, |
| }}, |
| }, |
| }, |
| msg: "spec.behavior.scaleDown.policies[0].value: Invalid value: 0: must be greater than zero", |
| }, { |
| behavior: autoscaling.HorizontalPodAutoscalerBehavior{ |
| ScaleDown: &autoscaling.HPAScalingRules{ |
| Policies: []autoscaling.HPAScalingPolicy{{ |
| Type: autoscaling.PodsScalingPolicy, |
| PeriodSeconds: -1, |
| Value: 1, |
| }}, |
| }, |
| }, |
| msg: "spec.behavior.scaleDown.policies[0].periodSeconds: Invalid value: -1: must be greater than zero", |
| }, { |
| behavior: autoscaling.HorizontalPodAutoscalerBehavior{ |
| ScaleDown: &autoscaling.HPAScalingRules{ |
| Policies: []autoscaling.HPAScalingPolicy{{ |
| Type: autoscaling.PodsScalingPolicy, |
| PeriodSeconds: 1, |
| Value: -1, |
| }}, |
| }, |
| }, |
| msg: "spec.behavior.scaleDown.policies[0].value: Invalid value: -1: must be greater than zero", |
| }} |
| for _, c := range errorCases { |
| hpa := prepareHPAWithBehavior(c.behavior) |
| if errs := ValidateHorizontalPodAutoscaler(&hpa); len(errs) == 0 { |
| t.Errorf("expected failure for %s", c.msg) |
| } else if !strings.Contains(errs[0].Error(), c.msg) { |
| t.Errorf("unexpected error: %v, expected: %s", errs[0], c.msg) |
| } |
| } |
| } |
| |
| func prepareHPAWithBehavior(b autoscaling.HorizontalPodAutoscalerBehavior) autoscaling.HorizontalPodAutoscaler { |
| return autoscaling.HorizontalPodAutoscaler{ |
| ObjectMeta: metav1.ObjectMeta{ |
| Name: "myautoscaler", |
| Namespace: metav1.NamespaceDefault, |
| }, |
| Spec: autoscaling.HorizontalPodAutoscalerSpec{ |
| ScaleTargetRef: autoscaling.CrossVersionObjectReference{ |
| Kind: "ReplicationController", |
| Name: "myrc", |
| }, |
| MinReplicas: utilpointer.Int32(1), |
| MaxReplicas: 5, |
| Metrics: []autoscaling.MetricSpec{{ |
| Type: autoscaling.ResourceMetricSourceType, |
| Resource: &autoscaling.ResourceMetricSource{ |
| Name: api.ResourceCPU, |
| Target: autoscaling.MetricTarget{ |
| Type: autoscaling.UtilizationMetricType, |
| AverageUtilization: utilpointer.Int32(70), |
| }, |
| }, |
| }}, |
| Behavior: &b, |
| }, |
| } |
| } |
| |
| func TestValidateHorizontalPodAutoscaler(t *testing.T) { |
| metricLabelSelector, err := metav1.ParseToLabelSelector("label=value") |
| if err != nil { |
| t.Errorf("unable to parse label selector: %v", err) |
| } |
| |
| successCases := []autoscaling.HorizontalPodAutoscaler{{ |
| ObjectMeta: metav1.ObjectMeta{ |
| Name: "myautoscaler", |
| Namespace: metav1.NamespaceDefault, |
| }, |
| Spec: autoscaling.HorizontalPodAutoscalerSpec{ |
| ScaleTargetRef: autoscaling.CrossVersionObjectReference{ |
| Kind: "ReplicationController", |
| Name: "myrc", |
| }, |
| MinReplicas: utilpointer.Int32(1), |
| MaxReplicas: 5, |
| Metrics: []autoscaling.MetricSpec{{ |
| Type: autoscaling.ResourceMetricSourceType, |
| Resource: &autoscaling.ResourceMetricSource{ |
| Name: api.ResourceCPU, |
| Target: autoscaling.MetricTarget{ |
| Type: autoscaling.UtilizationMetricType, |
| AverageUtilization: utilpointer.Int32(70), |
| }, |
| }, |
| }}, |
| }, |
| }, { |
| ObjectMeta: metav1.ObjectMeta{ |
| Name: "myautoscaler", |
| Namespace: metav1.NamespaceDefault, |
| }, |
| Spec: autoscaling.HorizontalPodAutoscalerSpec{ |
| ScaleTargetRef: autoscaling.CrossVersionObjectReference{ |
| Kind: "ReplicationController", |
| Name: "myrc", |
| }, |
| MinReplicas: utilpointer.Int32(1), |
| MaxReplicas: 5, |
| }, |
| }, { |
| ObjectMeta: metav1.ObjectMeta{ |
| Name: "myautoscaler", |
| Namespace: metav1.NamespaceDefault, |
| }, |
| Spec: autoscaling.HorizontalPodAutoscalerSpec{ |
| ScaleTargetRef: autoscaling.CrossVersionObjectReference{ |
| Kind: "ReplicationController", |
| Name: "myrc", |
| }, |
| MinReplicas: utilpointer.Int32(1), |
| MaxReplicas: 5, |
| Metrics: []autoscaling.MetricSpec{{ |
| Type: autoscaling.ResourceMetricSourceType, |
| Resource: &autoscaling.ResourceMetricSource{ |
| Name: api.ResourceCPU, |
| Target: autoscaling.MetricTarget{ |
| Type: autoscaling.AverageValueMetricType, |
| AverageValue: resource.NewMilliQuantity(300, resource.DecimalSI), |
| }, |
| }, |
| }}, |
| }, |
| }, { |
| ObjectMeta: metav1.ObjectMeta{ |
| Name: "myautoscaler", |
| Namespace: metav1.NamespaceDefault, |
| }, |
| Spec: autoscaling.HorizontalPodAutoscalerSpec{ |
| ScaleTargetRef: autoscaling.CrossVersionObjectReference{ |
| Kind: "ReplicationController", |
| Name: "myrc", |
| }, |
| MinReplicas: utilpointer.Int32(1), |
| MaxReplicas: 5, |
| Metrics: []autoscaling.MetricSpec{{ |
| Type: autoscaling.PodsMetricSourceType, |
| Pods: &autoscaling.PodsMetricSource{ |
| Metric: autoscaling.MetricIdentifier{ |
| Name: "somemetric", |
| }, |
| Target: autoscaling.MetricTarget{ |
| Type: autoscaling.AverageValueMetricType, |
| AverageValue: resource.NewMilliQuantity(300, resource.DecimalSI), |
| }, |
| }, |
| }}, |
| }, |
| }, { |
| ObjectMeta: metav1.ObjectMeta{ |
| Name: "myautoscaler", |
| Namespace: metav1.NamespaceDefault, |
| }, |
| Spec: autoscaling.HorizontalPodAutoscalerSpec{ |
| ScaleTargetRef: autoscaling.CrossVersionObjectReference{ |
| Kind: "ReplicationController", |
| Name: "myrc", |
| }, |
| MinReplicas: utilpointer.Int32(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.Int32(70), |
| }, |
| }, |
| }}, |
| }, |
| }, { |
| ObjectMeta: metav1.ObjectMeta{ |
| Name: "myautoscaler", |
| Namespace: metav1.NamespaceDefault, |
| }, |
| Spec: autoscaling.HorizontalPodAutoscalerSpec{ |
| ScaleTargetRef: autoscaling.CrossVersionObjectReference{ |
| Kind: "ReplicationController", |
| Name: "myrc", |
| }, |
| MinReplicas: utilpointer.Int32(1), |
| MaxReplicas: 5, |
| Metrics: []autoscaling.MetricSpec{{ |
| Type: autoscaling.ContainerResourceMetricSourceType, |
| ContainerResource: &autoscaling.ContainerResourceMetricSource{ |
| Name: api.ResourceCPU, |
| Container: "test-container", |
| Target: autoscaling.MetricTarget{ |
| Type: autoscaling.AverageValueMetricType, |
| AverageValue: resource.NewMilliQuantity(300, resource.DecimalSI), |
| }, |
| }, |
| }}, |
| }, |
| }, { |
| ObjectMeta: metav1.ObjectMeta{ |
| Name: "myautoscaler", |
| Namespace: metav1.NamespaceDefault, |
| }, |
| Spec: autoscaling.HorizontalPodAutoscalerSpec{ |
| ScaleTargetRef: autoscaling.CrossVersionObjectReference{ |
| Kind: "ReplicationController", |
| Name: "myrc", |
| }, |
| MinReplicas: utilpointer.Int32(1), |
| MaxReplicas: 5, |
| Metrics: []autoscaling.MetricSpec{{ |
| Type: autoscaling.ObjectMetricSourceType, |
| Object: &autoscaling.ObjectMetricSource{ |
| DescribedObject: autoscaling.CrossVersionObjectReference{ |
| Kind: "ReplicationController", |
| Name: "myrc", |
| }, |
| Metric: autoscaling.MetricIdentifier{ |
| Name: "somemetric", |
| }, |
| Target: autoscaling.MetricTarget{ |
| Type: autoscaling.ValueMetricType, |
| Value: resource.NewMilliQuantity(300, resource.DecimalSI), |
| }, |
| }, |
| }}, |
| }, |
| }, { |
| ObjectMeta: metav1.ObjectMeta{ |
| Name: "myautoscaler", |
| Namespace: metav1.NamespaceDefault, |
| }, |
| Spec: autoscaling.HorizontalPodAutoscalerSpec{ |
| ScaleTargetRef: autoscaling.CrossVersionObjectReference{ |
| Kind: "ReplicationController", |
| Name: "myrc", |
| }, |
| MinReplicas: utilpointer.Int32(1), |
| MaxReplicas: 5, |
| Metrics: []autoscaling.MetricSpec{{ |
| Type: autoscaling.ExternalMetricSourceType, |
| External: &autoscaling.ExternalMetricSource{ |
| Metric: autoscaling.MetricIdentifier{ |
| Name: "somemetric", |
| Selector: metricLabelSelector, |
| }, |
| Target: autoscaling.MetricTarget{ |
| Type: autoscaling.ValueMetricType, |
| Value: resource.NewMilliQuantity(300, resource.DecimalSI), |
| }, |
| }, |
| }}, |
| }, |
| }, { |
| ObjectMeta: metav1.ObjectMeta{ |
| Name: "myautoscaler", |
| Namespace: metav1.NamespaceDefault, |
| }, |
| Spec: autoscaling.HorizontalPodAutoscalerSpec{ |
| ScaleTargetRef: autoscaling.CrossVersionObjectReference{ |
| Kind: "ReplicationController", |
| Name: "myrc", |
| }, |
| MinReplicas: utilpointer.Int32(1), |
| MaxReplicas: 5, |
| Metrics: []autoscaling.MetricSpec{{ |
| Type: autoscaling.ExternalMetricSourceType, |
| External: &autoscaling.ExternalMetricSource{ |
| Metric: autoscaling.MetricIdentifier{ |
| Name: "somemetric", |
| Selector: metricLabelSelector, |
| }, |
| Target: autoscaling.MetricTarget{ |
| Type: autoscaling.AverageValueMetricType, |
| AverageValue: resource.NewMilliQuantity(300, resource.DecimalSI), |
| }, |
| }, |
| }}, |
| }, |
| }} |
| for _, successCase := range successCases { |
| if errs := ValidateHorizontalPodAutoscaler(&successCase); len(errs) != 0 { |
| t.Errorf("expected success: %v", errs) |
| } |
| } |
| |
| errorCases := []struct { |
| horizontalPodAutoscaler autoscaling.HorizontalPodAutoscaler |
| msg string |
| }{{ |
| horizontalPodAutoscaler: autoscaling.HorizontalPodAutoscaler{ |
| ObjectMeta: metav1.ObjectMeta{Name: "myautoscaler", Namespace: metav1.NamespaceDefault}, |
| Spec: autoscaling.HorizontalPodAutoscalerSpec{ |
| ScaleTargetRef: autoscaling.CrossVersionObjectReference{Name: "myrc"}, |
| MinReplicas: utilpointer.Int32(1), |
| MaxReplicas: 5, |
| Metrics: []autoscaling.MetricSpec{{ |
| Type: autoscaling.ResourceMetricSourceType, |
| Resource: &autoscaling.ResourceMetricSource{ |
| Name: api.ResourceCPU, |
| Target: autoscaling.MetricTarget{ |
| Type: autoscaling.UtilizationMetricType, |
| AverageUtilization: utilpointer.Int32(70), |
| }, |
| }, |
| }}, |
| }, |
| }, |
| 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.Int32(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.Int32(70), |
| }, |
| }, |
| }}, |
| }, |
| }, |
| msg: "scaleTargetRef.kind: Required", |
| }, { |
| horizontalPodAutoscaler: autoscaling.HorizontalPodAutoscaler{ |
| ObjectMeta: metav1.ObjectMeta{Name: "myautoscaler", Namespace: metav1.NamespaceDefault}, |
| Spec: autoscaling.HorizontalPodAutoscalerSpec{ |
| ScaleTargetRef: autoscaling.CrossVersionObjectReference{Kind: "..", Name: "myrc"}, |
| MinReplicas: utilpointer.Int32(1), |
| MaxReplicas: 5, |
| Metrics: []autoscaling.MetricSpec{{ |
| Type: autoscaling.ResourceMetricSourceType, |
| Resource: &autoscaling.ResourceMetricSource{ |
| Name: api.ResourceCPU, |
| Target: autoscaling.MetricTarget{ |
| Type: autoscaling.UtilizationMetricType, |
| AverageUtilization: utilpointer.Int32(70), |
| }, |
| }, |
| }}, |
| }, |
| }, |
| 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.Int32(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.Int32(70), |
| }, |
| }, |
| }}, |
| }, |
| }, |
| msg: "scaleTargetRef.kind: Invalid", |
| }, { |
| horizontalPodAutoscaler: autoscaling.HorizontalPodAutoscaler{ |
| ObjectMeta: metav1.ObjectMeta{Name: "myautoscaler", Namespace: metav1.NamespaceDefault}, |
| Spec: autoscaling.HorizontalPodAutoscalerSpec{ |
| ScaleTargetRef: autoscaling.CrossVersionObjectReference{Kind: "ReplicationController"}, |
| MinReplicas: utilpointer.Int32(1), |
| MaxReplicas: 5, |
| Metrics: []autoscaling.MetricSpec{{ |
| Type: autoscaling.ResourceMetricSourceType, |
| Resource: &autoscaling.ResourceMetricSource{ |
| Name: api.ResourceCPU, |
| Target: autoscaling.MetricTarget{ |
| Type: autoscaling.UtilizationMetricType, |
| AverageUtilization: utilpointer.Int32(70), |
| }, |
| }, |
| }}, |
| }, |
| }, |
| 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.Int32(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.Int32(70), |
| }, |
| }, |
| }}, |
| }, |
| }, |
| msg: "scaleTargetRef.name: Required", |
| }, { |
| horizontalPodAutoscaler: autoscaling.HorizontalPodAutoscaler{ |
| ObjectMeta: metav1.ObjectMeta{Name: "myautoscaler", Namespace: metav1.NamespaceDefault}, |
| Spec: autoscaling.HorizontalPodAutoscalerSpec{ |
| ScaleTargetRef: autoscaling.CrossVersionObjectReference{Kind: "ReplicationController", Name: ".."}, |
| MinReplicas: utilpointer.Int32(1), |
| MaxReplicas: 5, |
| Metrics: []autoscaling.MetricSpec{{ |
| Type: autoscaling.ResourceMetricSourceType, |
| Resource: &autoscaling.ResourceMetricSource{ |
| Name: api.ResourceCPU, |
| Target: autoscaling.MetricTarget{ |
| Type: autoscaling.UtilizationMetricType, |
| AverageUtilization: utilpointer.Int32(70), |
| }, |
| }, |
| }}, |
| }, |
| }, |
| 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.Int32(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.Int32(70), |
| }, |
| }, |
| }}, |
| }, |
| }, |
| msg: "scaleTargetRef.name: Invalid", |
| }, { |
| horizontalPodAutoscaler: autoscaling.HorizontalPodAutoscaler{ |
| ObjectMeta: metav1.ObjectMeta{ |
| Name: "myautoscaler", |
| Namespace: metav1.NamespaceDefault, |
| }, |
| Spec: autoscaling.HorizontalPodAutoscalerSpec{ |
| ScaleTargetRef: autoscaling.CrossVersionObjectReference{}, |
| MinReplicas: utilpointer.Int32(-1), |
| MaxReplicas: 5, |
| }, |
| }, |
| msg: "must be greater than or equal to 1", |
| }, { |
| horizontalPodAutoscaler: autoscaling.HorizontalPodAutoscaler{ |
| ObjectMeta: metav1.ObjectMeta{ |
| Name: "myautoscaler", |
| Namespace: metav1.NamespaceDefault, |
| }, |
| Spec: autoscaling.HorizontalPodAutoscalerSpec{ |
| ScaleTargetRef: autoscaling.CrossVersionObjectReference{}, |
| MinReplicas: utilpointer.Int32(7), |
| MaxReplicas: 5, |
| }, |
| }, |
| msg: "must be greater than or equal to `minReplicas`", |
| }, { |
| horizontalPodAutoscaler: autoscaling.HorizontalPodAutoscaler{ |
| ObjectMeta: metav1.ObjectMeta{ |
| Name: "myautoscaler", |
| Namespace: metav1.NamespaceDefault, |
| }, |
| Spec: autoscaling.HorizontalPodAutoscalerSpec{ |
| ScaleTargetRef: autoscaling.CrossVersionObjectReference{Name: "myrc", Kind: "ReplicationController"}, |
| MinReplicas: utilpointer.Int32(1), |
| MaxReplicas: 5, |
| Metrics: []autoscaling.MetricSpec{{ |
| Type: autoscaling.ResourceMetricSourceType, |
| Resource: &autoscaling.ResourceMetricSource{ |
| Name: api.ResourceCPU, |
| Target: autoscaling.MetricTarget{ |
| Type: autoscaling.UtilizationMetricType, |
| AverageUtilization: utilpointer.Int32(70), |
| AverageValue: resource.NewMilliQuantity(300, resource.DecimalSI), |
| }, |
| }, |
| }}, |
| }, |
| }, |
| msg: "may not set both a target raw value and a target utilization", |
| }, { |
| horizontalPodAutoscaler: autoscaling.HorizontalPodAutoscaler{ |
| ObjectMeta: metav1.ObjectMeta{ |
| Name: "myautoscaler", |
| Namespace: metav1.NamespaceDefault, |
| }, |
| Spec: autoscaling.HorizontalPodAutoscalerSpec{ |
| ScaleTargetRef: autoscaling.CrossVersionObjectReference{Name: "myrc", Kind: "ReplicationController"}, |
| MinReplicas: utilpointer.Int32(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.Int32(70), |
| AverageValue: resource.NewMilliQuantity(300, resource.DecimalSI), |
| }, |
| }, |
| }}, |
| }, |
| }, |
| msg: "may not set both a target raw value and a target utilization", |
| }, { |
| horizontalPodAutoscaler: autoscaling.HorizontalPodAutoscaler{ |
| ObjectMeta: metav1.ObjectMeta{Name: "myautoscaler", Namespace: metav1.NamespaceDefault}, |
| Spec: autoscaling.HorizontalPodAutoscalerSpec{ |
| ScaleTargetRef: autoscaling.CrossVersionObjectReference{Name: "myrc", Kind: "ReplicationController"}, |
| MinReplicas: utilpointer.Int32(1), |
| MaxReplicas: 5, |
| Metrics: []autoscaling.MetricSpec{{ |
| Type: autoscaling.ResourceMetricSourceType, |
| Resource: &autoscaling.ResourceMetricSource{ |
| Target: autoscaling.MetricTarget{ |
| Type: autoscaling.UtilizationMetricType, |
| AverageUtilization: utilpointer.Int32(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.Int32(1), |
| MaxReplicas: 5, |
| Metrics: []autoscaling.MetricSpec{{ |
| Type: autoscaling.ContainerResourceMetricSourceType, |
| ContainerResource: &autoscaling.ContainerResourceMetricSource{ |
| Container: "test-application", |
| Target: autoscaling.MetricTarget{ |
| Type: autoscaling.UtilizationMetricType, |
| AverageUtilization: utilpointer.Int32(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.Int32(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.Int32(70), |
| }, |
| }, |
| }}, |
| }, |
| }, |
| msg: "Invalid value: InvalidResource: must be a standard resource type or fully qualified", |
| }, { |
| horizontalPodAutoscaler: autoscaling.HorizontalPodAutoscaler{ |
| ObjectMeta: metav1.ObjectMeta{Name: "myautoscaler", Namespace: metav1.NamespaceDefault}, |
| Spec: autoscaling.HorizontalPodAutoscalerSpec{ |
| ScaleTargetRef: autoscaling.CrossVersionObjectReference{Name: "myrc", Kind: "ReplicationController"}, |
| MinReplicas: utilpointer.Int32(1), |
| MaxReplicas: 5, |
| Metrics: []autoscaling.MetricSpec{{ |
| Type: autoscaling.ResourceMetricSourceType, |
| Resource: &autoscaling.ResourceMetricSource{ |
| Name: api.ResourceCPU, |
| Target: autoscaling.MetricTarget{ |
| Type: autoscaling.UtilizationMetricType, |
| AverageUtilization: utilpointer.Int32(-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.Int32(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.Int32(-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.Int32(1), |
| MaxReplicas: 5, |
| Metrics: []autoscaling.MetricSpec{{ |
| Type: autoscaling.ContainerResourceMetricSourceType, |
| ContainerResource: &autoscaling.ContainerResourceMetricSource{ |
| Name: api.ResourceCPU, |
| Target: autoscaling.MetricTarget{ |
| Type: autoscaling.UtilizationMetricType, |
| AverageUtilization: utilpointer.Int32(-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.Int32(1), |
| MaxReplicas: 5, |
| Metrics: []autoscaling.MetricSpec{{ |
| Type: autoscaling.ContainerResourceMetricSourceType, |
| ContainerResource: &autoscaling.ContainerResourceMetricSource{ |
| Name: api.ResourceCPU, |
| Container: "---***", |
| Target: autoscaling.MetricTarget{ |
| Type: autoscaling.UtilizationMetricType, |
| AverageUtilization: utilpointer.Int32(-10), |
| }, |
| }, |
| }}, |
| }, |
| }, |
| msg: "Invalid value: \"---***\"", |
| }, { |
| horizontalPodAutoscaler: autoscaling.HorizontalPodAutoscaler{ |
| ObjectMeta: metav1.ObjectMeta{Name: "myautoscaler", Namespace: metav1.NamespaceDefault}, |
| Spec: autoscaling.HorizontalPodAutoscalerSpec{ |
| ScaleTargetRef: autoscaling.CrossVersionObjectReference{Name: "myrc", Kind: "ReplicationController"}, |
| MinReplicas: utilpointer.Int32(1), |
| MaxReplicas: 5, |
| Metrics: []autoscaling.MetricSpec{{ |
| Type: autoscaling.ResourceMetricSourceType, |
| Resource: &autoscaling.ResourceMetricSource{ |
| Name: api.ResourceCPU, |
| Target: autoscaling.MetricTarget{ |
| Type: autoscaling.ValueMetricType, |
| }, |
| }, |
| }}, |
| }, |
| }, |
| msg: "must set either a target raw value or a target utilization", |
| }, { |
| horizontalPodAutoscaler: autoscaling.HorizontalPodAutoscaler{ |
| ObjectMeta: metav1.ObjectMeta{Name: "myautoscaler", Namespace: metav1.NamespaceDefault}, |
| Spec: autoscaling.HorizontalPodAutoscalerSpec{ |
| ScaleTargetRef: autoscaling.CrossVersionObjectReference{Name: "myrc", Kind: "ReplicationController"}, |
| MinReplicas: utilpointer.Int32(1), |
| MaxReplicas: 5, |
| Metrics: []autoscaling.MetricSpec{{ |
| Type: autoscaling.ContainerResourceMetricSourceType, |
| ContainerResource: &autoscaling.ContainerResourceMetricSource{ |
| Name: api.ResourceCPU, |
| Container: "test-application", |
| Target: autoscaling.MetricTarget{ |
| Type: autoscaling.ValueMetricType, |
| }, |
| }, |
| }}, |
| }, |
| }, |
| msg: "must set either a target raw value or a target utilization", |
| }, { |
| horizontalPodAutoscaler: autoscaling.HorizontalPodAutoscaler{ |
| ObjectMeta: metav1.ObjectMeta{Name: "myautoscaler", Namespace: metav1.NamespaceDefault}, |
| Spec: autoscaling.HorizontalPodAutoscalerSpec{ |
| ScaleTargetRef: autoscaling.CrossVersionObjectReference{Name: "myrc", Kind: "ReplicationController"}, |
| MinReplicas: utilpointer.Int32(1), |
| MaxReplicas: 5, |
| Metrics: []autoscaling.MetricSpec{{ |
| Type: autoscaling.PodsMetricSourceType, |
| Pods: &autoscaling.PodsMetricSource{ |
| Metric: autoscaling.MetricIdentifier{}, |
| Target: autoscaling.MetricTarget{ |
| Type: autoscaling.ValueMetricType, |
| AverageValue: resource.NewMilliQuantity(100, resource.DecimalSI), |
| }, |
| }, |
| }}, |
| }, |
| }, |
| msg: "must specify a metric name", |
| }, { |
| horizontalPodAutoscaler: autoscaling.HorizontalPodAutoscaler{ |
| ObjectMeta: metav1.ObjectMeta{Name: "myautoscaler", Namespace: metav1.NamespaceDefault}, |
| Spec: autoscaling.HorizontalPodAutoscalerSpec{ |
| ScaleTargetRef: autoscaling.CrossVersionObjectReference{Name: "myrc", Kind: "ReplicationController"}, |
| MinReplicas: utilpointer.Int32(1), |
| MaxReplicas: 5, |
| Metrics: []autoscaling.MetricSpec{{ |
| Type: autoscaling.PodsMetricSourceType, |
| Pods: &autoscaling.PodsMetricSource{ |
| Metric: autoscaling.MetricIdentifier{ |
| Name: "somemetric", |
| }, |
| Target: autoscaling.MetricTarget{ |
| Type: autoscaling.ValueMetricType, |
| }, |
| }, |
| }}, |
| }, |
| }, |
| msg: "must specify a positive target averageValue", |
| }, { |
| horizontalPodAutoscaler: autoscaling.HorizontalPodAutoscaler{ |
| ObjectMeta: metav1.ObjectMeta{Name: "myautoscaler", Namespace: metav1.NamespaceDefault}, |
| Spec: autoscaling.HorizontalPodAutoscalerSpec{ |
| ScaleTargetRef: autoscaling.CrossVersionObjectReference{Name: "myrc", Kind: "ReplicationController"}, |
| MinReplicas: utilpointer.Int32(1), |
| MaxReplicas: 5, |
| Metrics: []autoscaling.MetricSpec{{ |
| Type: autoscaling.ObjectMetricSourceType, |
| Object: &autoscaling.ObjectMetricSource{ |
| DescribedObject: autoscaling.CrossVersionObjectReference{ |
| Kind: "ReplicationController", |
| Name: "myrc", |
| }, |
| Metric: autoscaling.MetricIdentifier{ |
| Name: "somemetric", |
| }, |
| Target: autoscaling.MetricTarget{ |
| Type: autoscaling.ValueMetricType, |
| }, |
| }, |
| }}, |
| }, |
| }, |
| msg: "must set either a target value or averageValue", |
| }, { |
| horizontalPodAutoscaler: autoscaling.HorizontalPodAutoscaler{ |
| ObjectMeta: metav1.ObjectMeta{Name: "myautoscaler", Namespace: metav1.NamespaceDefault}, |
| Spec: autoscaling.HorizontalPodAutoscalerSpec{ |
| ScaleTargetRef: autoscaling.CrossVersionObjectReference{Name: "myrc", Kind: "ReplicationController"}, |
| MinReplicas: utilpointer.Int32(1), |
| MaxReplicas: 5, |
| Metrics: []autoscaling.MetricSpec{{ |
| Type: autoscaling.ObjectMetricSourceType, |
| Object: &autoscaling.ObjectMetricSource{ |
| DescribedObject: autoscaling.CrossVersionObjectReference{ |
| Name: "myrc", |
| }, |
| Metric: autoscaling.MetricIdentifier{ |
| Name: "somemetric", |
| }, |
| Target: autoscaling.MetricTarget{ |
| Type: autoscaling.ValueMetricType, |
| Value: resource.NewMilliQuantity(100, resource.DecimalSI), |
| }, |
| }, |
| }}, |
| }, |
| }, |
| msg: "object.describedObject.kind: Required", |
| }, { |
| horizontalPodAutoscaler: autoscaling.HorizontalPodAutoscaler{ |
| ObjectMeta: metav1.ObjectMeta{Name: "myautoscaler", Namespace: metav1.NamespaceDefault}, |
| Spec: autoscaling.HorizontalPodAutoscalerSpec{ |
| ScaleTargetRef: autoscaling.CrossVersionObjectReference{Name: "myrc", Kind: "ReplicationController"}, |
| MinReplicas: utilpointer.Int32(1), |
| MaxReplicas: 5, |
| Metrics: []autoscaling.MetricSpec{{ |
| Type: autoscaling.ObjectMetricSourceType, |
| Object: &autoscaling.ObjectMetricSource{ |
| DescribedObject: autoscaling.CrossVersionObjectReference{ |
| Kind: "ReplicationController", |
| Name: "myrc", |
| }, |
| Target: autoscaling.MetricTarget{ |
| Type: autoscaling.ValueMetricType, |
| Value: resource.NewMilliQuantity(100, resource.DecimalSI), |
| }, |
| }, |
| }}, |
| }, |
| }, |
| msg: "must specify a metric name", |
| }, { |
| horizontalPodAutoscaler: autoscaling.HorizontalPodAutoscaler{ |
| ObjectMeta: metav1.ObjectMeta{Name: "myautoscaler", Namespace: metav1.NamespaceDefault}, |
| Spec: autoscaling.HorizontalPodAutoscalerSpec{ |
| ScaleTargetRef: autoscaling.CrossVersionObjectReference{Name: "myrc", Kind: "ReplicationController"}, |
| MinReplicas: utilpointer.Int32(1), |
| MaxReplicas: 5, |
| Metrics: []autoscaling.MetricSpec{{ |
| Type: autoscaling.ExternalMetricSourceType, |
| External: &autoscaling.ExternalMetricSource{ |
| Metric: autoscaling.MetricIdentifier{ |
| Selector: metricLabelSelector, |
| }, |
| Target: autoscaling.MetricTarget{ |
| Type: autoscaling.ValueMetricType, |
| Value: resource.NewMilliQuantity(300, resource.DecimalSI), |
| }, |
| }, |
| }}, |
| }, |
| }, |
| msg: "must specify a metric name", |
| }, { |
| horizontalPodAutoscaler: autoscaling.HorizontalPodAutoscaler{ |
| ObjectMeta: metav1.ObjectMeta{Name: "myautoscaler", Namespace: metav1.NamespaceDefault}, |
| Spec: autoscaling.HorizontalPodAutoscalerSpec{ |
| ScaleTargetRef: autoscaling.CrossVersionObjectReference{Name: "myrc", Kind: "ReplicationController"}, |
| MinReplicas: utilpointer.Int32(1), |
| MaxReplicas: 5, |
| Metrics: []autoscaling.MetricSpec{{ |
| Type: autoscaling.ExternalMetricSourceType, |
| External: &autoscaling.ExternalMetricSource{ |
| Metric: autoscaling.MetricIdentifier{ |
| Name: "foo/../", |
| Selector: metricLabelSelector, |
| }, |
| Target: autoscaling.MetricTarget{ |
| Type: autoscaling.ValueMetricType, |
| Value: resource.NewMilliQuantity(300, resource.DecimalSI), |
| }, |
| }, |
| }}, |
| }, |
| }, |
| msg: "'/'", |
| }, |
| |
| { |
| horizontalPodAutoscaler: autoscaling.HorizontalPodAutoscaler{ |
| ObjectMeta: metav1.ObjectMeta{Name: "myautoscaler", Namespace: metav1.NamespaceDefault}, |
| Spec: autoscaling.HorizontalPodAutoscalerSpec{ |
| ScaleTargetRef: autoscaling.CrossVersionObjectReference{Name: "myrc", Kind: "ReplicationController"}, |
| MinReplicas: utilpointer.Int32(1), |
| MaxReplicas: 5, |
| Metrics: []autoscaling.MetricSpec{{ |
| Type: autoscaling.ExternalMetricSourceType, |
| External: &autoscaling.ExternalMetricSource{ |
| Metric: autoscaling.MetricIdentifier{ |
| Name: "somemetric", |
| Selector: metricLabelSelector, |
| }, |
| Target: autoscaling.MetricTarget{ |
| Type: autoscaling.ValueMetricType, |
| }, |
| }, |
| }}, |
| }, |
| }, |
| msg: "must set either a target value for metric or a per-pod target", |
| }, { |
| horizontalPodAutoscaler: autoscaling.HorizontalPodAutoscaler{ |
| ObjectMeta: metav1.ObjectMeta{Name: "myautoscaler", Namespace: metav1.NamespaceDefault}, |
| Spec: autoscaling.HorizontalPodAutoscalerSpec{ |
| ScaleTargetRef: autoscaling.CrossVersionObjectReference{Name: "myrc", Kind: "ReplicationController"}, |
| MinReplicas: utilpointer.Int32(1), |
| MaxReplicas: 5, |
| Metrics: []autoscaling.MetricSpec{{ |
| Type: autoscaling.ExternalMetricSourceType, |
| External: &autoscaling.ExternalMetricSource{ |
| Metric: autoscaling.MetricIdentifier{ |
| Name: "somemetric", |
| Selector: metricLabelSelector, |
| }, |
| Target: autoscaling.MetricTarget{ |
| Type: autoscaling.ValueMetricType, |
| Value: resource.NewMilliQuantity(-300, resource.DecimalSI), |
| }, |
| }, |
| }}, |
| }, |
| }, |
| msg: "must be positive", |
| }, { |
| horizontalPodAutoscaler: autoscaling.HorizontalPodAutoscaler{ |
| ObjectMeta: metav1.ObjectMeta{Name: "myautoscaler", Namespace: metav1.NamespaceDefault}, |
| Spec: autoscaling.HorizontalPodAutoscalerSpec{ |
| ScaleTargetRef: autoscaling.CrossVersionObjectReference{Name: "myrc", Kind: "ReplicationController"}, |
| MinReplicas: utilpointer.Int32(1), |
| MaxReplicas: 5, |
| Metrics: []autoscaling.MetricSpec{{ |
| Type: autoscaling.ExternalMetricSourceType, |
| External: &autoscaling.ExternalMetricSource{ |
| Metric: autoscaling.MetricIdentifier{ |
| Name: "somemetric", |
| Selector: metricLabelSelector, |
| }, |
| Target: autoscaling.MetricTarget{ |
| Type: autoscaling.ValueMetricType, |
| AverageValue: resource.NewMilliQuantity(-300, resource.DecimalSI), |
| }, |
| }, |
| }}, |
| }, |
| }, |
| msg: "must be positive", |
| }, { |
| horizontalPodAutoscaler: autoscaling.HorizontalPodAutoscaler{ |
| ObjectMeta: metav1.ObjectMeta{Name: "myautoscaler", Namespace: metav1.NamespaceDefault}, |
| Spec: autoscaling.HorizontalPodAutoscalerSpec{ |
| ScaleTargetRef: autoscaling.CrossVersionObjectReference{Name: "myrc", Kind: "ReplicationController"}, |
| MinReplicas: utilpointer.Int32(1), |
| MaxReplicas: 5, |
| Metrics: []autoscaling.MetricSpec{{ |
| Type: autoscaling.ExternalMetricSourceType, |
| External: &autoscaling.ExternalMetricSource{ |
| Metric: autoscaling.MetricIdentifier{ |
| Name: "somemetric", |
| Selector: metricLabelSelector, |
| }, |
| Target: autoscaling.MetricTarget{ |
| Type: autoscaling.ValueMetricType, |
| Value: resource.NewMilliQuantity(300, resource.DecimalSI), |
| AverageValue: resource.NewMilliQuantity(300, resource.DecimalSI), |
| }, |
| }, |
| }}, |
| }, |
| }, |
| msg: "may not set both a target value for metric and a per-pod target", |
| }, { |
| horizontalPodAutoscaler: autoscaling.HorizontalPodAutoscaler{ |
| ObjectMeta: metav1.ObjectMeta{Name: "myautoscaler", Namespace: metav1.NamespaceDefault}, |
| Spec: autoscaling.HorizontalPodAutoscalerSpec{ |
| ScaleTargetRef: autoscaling.CrossVersionObjectReference{Name: "myrc", Kind: "ReplicationController"}, |
| MinReplicas: utilpointer.Int32(1), |
| MaxReplicas: 5, |
| Metrics: []autoscaling.MetricSpec{{ |
| Type: autoscaling.ExternalMetricSourceType, |
| External: &autoscaling.ExternalMetricSource{ |
| Metric: autoscaling.MetricIdentifier{ |
| Name: "somemetric", |
| Selector: metricLabelSelector, |
| }, |
| Target: autoscaling.MetricTarget{ |
| Type: "boogity", |
| Value: resource.NewMilliQuantity(300, resource.DecimalSI), |
| }, |
| }, |
| }}, |
| }, |
| }, |
| msg: "must be either Utilization, Value, or AverageValue", |
| }, { |
| horizontalPodAutoscaler: autoscaling.HorizontalPodAutoscaler{ |
| ObjectMeta: metav1.ObjectMeta{Name: "myautoscaler", Namespace: metav1.NamespaceDefault}, |
| Spec: autoscaling.HorizontalPodAutoscalerSpec{ |
| ScaleTargetRef: autoscaling.CrossVersionObjectReference{Name: "myrc", Kind: "ReplicationController"}, |
| MinReplicas: utilpointer.Int32(1), |
| MaxReplicas: 5, |
| Metrics: []autoscaling.MetricSpec{{ |
| Type: autoscaling.ExternalMetricSourceType, |
| External: &autoscaling.ExternalMetricSource{ |
| Metric: autoscaling.MetricIdentifier{ |
| Name: "somemetric", |
| Selector: metricLabelSelector, |
| }, |
| Target: autoscaling.MetricTarget{ |
| Value: resource.NewMilliQuantity(300, resource.DecimalSI), |
| }, |
| }, |
| }}, |
| }, |
| }, |
| msg: "must specify a metric target type", |
| }, { |
| horizontalPodAutoscaler: autoscaling.HorizontalPodAutoscaler{ |
| ObjectMeta: metav1.ObjectMeta{Name: "myautoscaler", Namespace: metav1.NamespaceDefault}, |
| Spec: autoscaling.HorizontalPodAutoscalerSpec{ |
| ScaleTargetRef: autoscaling.CrossVersionObjectReference{Name: "myrc", Kind: "ReplicationController"}, |
| MinReplicas: utilpointer.Int32(1), |
| MaxReplicas: 5, |
| Metrics: []autoscaling.MetricSpec{{ |
| Type: autoscaling.ExternalMetricSourceType, |
| External: &autoscaling.ExternalMetricSource{ |
| Metric: autoscaling.MetricIdentifier{ |
| Name: "somemetric", |
| Selector: metricLabelSelector, |
| }, |
| }, |
| }}, |
| }, |
| }, |
| msg: "must specify a metric target", |
| }, { |
| horizontalPodAutoscaler: autoscaling.HorizontalPodAutoscaler{ |
| ObjectMeta: metav1.ObjectMeta{Name: "myautoscaler", Namespace: metav1.NamespaceDefault}, |
| Spec: autoscaling.HorizontalPodAutoscalerSpec{ |
| ScaleTargetRef: autoscaling.CrossVersionObjectReference{Name: "myrc", Kind: "ReplicationController"}, |
| MinReplicas: utilpointer.Int32(1), |
| MaxReplicas: 5, |
| Metrics: []autoscaling.MetricSpec{ |
| {}, |
| }, |
| }, |
| }, |
| msg: "must specify a metric source type", |
| }, { |
| horizontalPodAutoscaler: autoscaling.HorizontalPodAutoscaler{ |
| ObjectMeta: metav1.ObjectMeta{Name: "myautoscaler", Namespace: metav1.NamespaceDefault}, |
| Spec: autoscaling.HorizontalPodAutoscalerSpec{ |
| ScaleTargetRef: autoscaling.CrossVersionObjectReference{Name: "myrc", Kind: "ReplicationController"}, |
| MinReplicas: utilpointer.Int32(1), |
| MaxReplicas: 5, |
| Metrics: []autoscaling.MetricSpec{{ |
| Type: autoscaling.MetricSourceType("InvalidType"), |
| }}, |
| }, |
| }, |
| msg: "type: Unsupported value", |
| }, { |
| horizontalPodAutoscaler: autoscaling.HorizontalPodAutoscaler{ |
| ObjectMeta: metav1.ObjectMeta{Name: "myautoscaler", Namespace: metav1.NamespaceDefault}, |
| Spec: autoscaling.HorizontalPodAutoscalerSpec{ |
| ScaleTargetRef: autoscaling.CrossVersionObjectReference{Name: "myrc", Kind: "ReplicationController"}, |
| MinReplicas: utilpointer.Int32(1), |
| MaxReplicas: 5, |
| Metrics: []autoscaling.MetricSpec{{ |
| Type: autoscaling.ResourceMetricSourceType, |
| Resource: &autoscaling.ResourceMetricSource{ |
| Name: api.ResourceCPU, |
| Target: autoscaling.MetricTarget{ |
| Type: autoscaling.AverageValueMetricType, |
| AverageValue: resource.NewMilliQuantity(100, resource.DecimalSI), |
| }, |
| }, |
| Pods: &autoscaling.PodsMetricSource{ |
| Metric: autoscaling.MetricIdentifier{ |
| Name: "somemetric", |
| }, |
| Target: autoscaling.MetricTarget{ |
| Type: autoscaling.AverageValueMetricType, |
| AverageValue: resource.NewMilliQuantity(100, resource.DecimalSI), |
| }, |
| }, |
| }}, |
| }, |
| }, |
| msg: "must populate the given metric source only", |
| }, |
| } |
| |
| for _, c := range errorCases { |
| errs := ValidateHorizontalPodAutoscaler(&c.horizontalPodAutoscaler) |
| if len(errs) == 0 { |
| t.Errorf("expected failure for %q", c.msg) |
| } else if !strings.Contains(errs[0].Error(), c.msg) { |
| t.Errorf("unexpected error: %q, expected: %q", errs[0], c.msg) |
| } |
| } |
| |
| sourceTypes := map[autoscaling.MetricSourceType]autoscaling.MetricSpec{ |
| autoscaling.ResourceMetricSourceType: { |
| Resource: &autoscaling.ResourceMetricSource{ |
| Name: api.ResourceCPU, |
| Target: autoscaling.MetricTarget{ |
| Type: autoscaling.AverageValueMetricType, |
| AverageValue: resource.NewMilliQuantity(100, resource.DecimalSI), |
| }, |
| }, |
| }, |
| autoscaling.ContainerResourceMetricSourceType: { |
| ContainerResource: &autoscaling.ContainerResourceMetricSource{ |
| Name: api.ResourceCPU, |
| Container: "test-application", |
| Target: autoscaling.MetricTarget{ |
| Type: autoscaling.AverageValueMetricType, |
| AverageValue: resource.NewMilliQuantity(100, resource.DecimalSI), |
| }, |
| }, |
| }, |
| autoscaling.PodsMetricSourceType: { |
| Pods: &autoscaling.PodsMetricSource{ |
| Metric: autoscaling.MetricIdentifier{ |
| Name: "somemetric", |
| }, |
| Target: autoscaling.MetricTarget{ |
| Type: autoscaling.AverageValueMetricType, |
| AverageValue: resource.NewMilliQuantity(100, resource.DecimalSI), |
| }, |
| }, |
| }, |
| autoscaling.ObjectMetricSourceType: { |
| Object: &autoscaling.ObjectMetricSource{ |
| DescribedObject: autoscaling.CrossVersionObjectReference{ |
| Kind: "ReplicationController", |
| Name: "myrc", |
| }, |
| Metric: autoscaling.MetricIdentifier{ |
| Name: "somemetric", |
| }, |
| Target: autoscaling.MetricTarget{ |
| Type: autoscaling.ValueMetricType, |
| Value: resource.NewMilliQuantity(100, resource.DecimalSI), |
| }, |
| }, |
| }, |
| } |
| |
| for correctType, spec := range sourceTypes { |
| for incorrectType := range sourceTypes { |
| if correctType == incorrectType { |
| continue |
| } |
| |
| spec.Type = incorrectType |
| |
| errs := ValidateHorizontalPodAutoscaler(&autoscaling.HorizontalPodAutoscaler{ |
| ObjectMeta: metav1.ObjectMeta{Name: "myautoscaler", Namespace: metav1.NamespaceDefault}, |
| Spec: autoscaling.HorizontalPodAutoscalerSpec{ |
| ScaleTargetRef: autoscaling.CrossVersionObjectReference{Name: "myrc", Kind: "ReplicationController"}, |
| MinReplicas: utilpointer.Int32(1), |
| MaxReplicas: 5, Metrics: []autoscaling.MetricSpec{spec}, |
| }, |
| }) |
| |
| expectedMsg := "must populate information for the given metric source" |
| |
| if len(errs) == 0 { |
| t.Errorf("expected failure with type of %v and spec for %v", incorrectType, correctType) |
| } else if !strings.Contains(errs[0].Error(), expectedMsg) { |
| t.Errorf("unexpected error: %q, expected %q", errs[0], expectedMsg) |
| } |
| } |
| } |
| } |
| |
| func prepareMinReplicasCases(t *testing.T, minReplicas int32) []autoscaling.HorizontalPodAutoscaler { |
| metricLabelSelector, err := metav1.ParseToLabelSelector("label=value") |
| if err != nil { |
| t.Errorf("unable to parse label selector: %v", err) |
| } |
| minReplicasCases := []autoscaling.HorizontalPodAutoscaler{{ |
| ObjectMeta: metav1.ObjectMeta{ |
| Name: "myautoscaler", |
| Namespace: metav1.NamespaceDefault, |
| ResourceVersion: "theversion", |
| }, |
| Spec: autoscaling.HorizontalPodAutoscalerSpec{ |
| ScaleTargetRef: autoscaling.CrossVersionObjectReference{ |
| Kind: "ReplicationController", |
| Name: "myrc", |
| }, |
| MinReplicas: utilpointer.Int32(minReplicas), |
| MaxReplicas: 5, |
| Metrics: []autoscaling.MetricSpec{{ |
| Type: autoscaling.ObjectMetricSourceType, |
| Object: &autoscaling.ObjectMetricSource{ |
| DescribedObject: autoscaling.CrossVersionObjectReference{ |
| Kind: "ReplicationController", |
| Name: "myrc", |
| }, |
| Metric: autoscaling.MetricIdentifier{ |
| Name: "somemetric", |
| }, |
| Target: autoscaling.MetricTarget{ |
| Type: autoscaling.ValueMetricType, |
| Value: resource.NewMilliQuantity(300, resource.DecimalSI), |
| }, |
| }, |
| }}, |
| }, |
| }, { |
| ObjectMeta: metav1.ObjectMeta{ |
| Name: "myautoscaler", |
| Namespace: metav1.NamespaceDefault, |
| ResourceVersion: "theversion", |
| }, |
| Spec: autoscaling.HorizontalPodAutoscalerSpec{ |
| ScaleTargetRef: autoscaling.CrossVersionObjectReference{ |
| Kind: "ReplicationController", |
| Name: "myrc", |
| }, |
| MinReplicas: utilpointer.Int32(minReplicas), |
| MaxReplicas: 5, |
| Metrics: []autoscaling.MetricSpec{{ |
| Type: autoscaling.ExternalMetricSourceType, |
| External: &autoscaling.ExternalMetricSource{ |
| Metric: autoscaling.MetricIdentifier{ |
| Name: "somemetric", |
| Selector: metricLabelSelector, |
| }, |
| Target: autoscaling.MetricTarget{ |
| Type: autoscaling.AverageValueMetricType, |
| AverageValue: resource.NewMilliQuantity(300, resource.DecimalSI), |
| }, |
| }, |
| }}, |
| }, |
| }} |
| return minReplicasCases |
| } |
| |
| func TestValidateHorizontalPodAutoscalerScaleToZeroEnabled(t *testing.T) { |
| // Enable HPAScaleToZero feature gate. |
| defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.HPAScaleToZero, true)() |
| |
| zeroMinReplicasCases := prepareMinReplicasCases(t, 0) |
| for _, successCase := range zeroMinReplicasCases { |
| if errs := ValidateHorizontalPodAutoscaler(&successCase); len(errs) != 0 { |
| t.Errorf("expected success: %v", errs) |
| } |
| } |
| } |
| |
| func TestValidateHorizontalPodAutoscalerScaleToZeroDisabled(t *testing.T) { |
| // Disable HPAScaleToZero feature gate. |
| defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.HPAScaleToZero, false)() |
| |
| zeroMinReplicasCases := prepareMinReplicasCases(t, 0) |
| errorMsg := "must be greater than or equal to 1" |
| |
| for _, errorCase := range zeroMinReplicasCases { |
| errs := ValidateHorizontalPodAutoscaler(&errorCase) |
| if len(errs) == 0 { |
| t.Errorf("expected failure for %q", errorMsg) |
| } else if !strings.Contains(errs[0].Error(), errorMsg) { |
| t.Errorf("unexpected error: %q, expected: %q", errs[0], errorMsg) |
| } |
| } |
| |
| nonZeroMinReplicasCases := prepareMinReplicasCases(t, 1) |
| |
| for _, successCase := range nonZeroMinReplicasCases { |
| successCase.Spec.MinReplicas = utilpointer.Int32(1) |
| if errs := ValidateHorizontalPodAutoscaler(&successCase); len(errs) != 0 { |
| t.Errorf("expected success: %v", errs) |
| } |
| } |
| } |
| |
| func TestValidateHorizontalPodAutoscalerUpdateScaleToZeroEnabled(t *testing.T) { |
| // Enable HPAScaleToZero feature gate. |
| defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.HPAScaleToZero, true)() |
| |
| zeroMinReplicasCases := prepareMinReplicasCases(t, 0) |
| nonZeroMinReplicasCases := prepareMinReplicasCases(t, 1) |
| |
| for i, zeroCase := range zeroMinReplicasCases { |
| nonZeroCase := nonZeroMinReplicasCases[i] |
| |
| if errs := ValidateHorizontalPodAutoscalerUpdate(&nonZeroCase, &zeroCase); len(errs) != 0 { |
| t.Errorf("expected success: %v", errs) |
| } |
| |
| if errs := ValidateHorizontalPodAutoscalerUpdate(&zeroCase, &nonZeroCase); len(errs) != 0 { |
| t.Errorf("expected success: %v", errs) |
| } |
| } |
| } |
| |
| func TestValidateHorizontalPodAutoscalerScaleToZeroUpdateDisabled(t *testing.T) { |
| // Disable HPAScaleToZero feature gate. |
| defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.HPAScaleToZero, false)() |
| |
| zeroMinReplicasCases := prepareMinReplicasCases(t, 0) |
| nonZeroMinReplicasCases := prepareMinReplicasCases(t, 1) |
| errorMsg := "must be greater than or equal to 1" |
| |
| for i, zeroCase := range zeroMinReplicasCases { |
| nonZeroCase := nonZeroMinReplicasCases[i] |
| errs := ValidateHorizontalPodAutoscalerUpdate(&zeroCase, &nonZeroCase) |
| |
| if len(errs) == 0 { |
| t.Errorf("expected failure for %q", errorMsg) |
| } else if !strings.Contains(errs[0].Error(), errorMsg) { |
| t.Errorf("unexpected error: %q, expected: %q", errs[0], errorMsg) |
| } |
| |
| if errs := ValidateHorizontalPodAutoscalerUpdate(&zeroCase, &zeroCase); len(errs) != 0 { |
| t.Errorf("expected success: %v", errs) |
| } |
| |
| if errs := ValidateHorizontalPodAutoscalerUpdate(&nonZeroCase, &zeroCase); len(errs) != 0 { |
| t.Errorf("expected success: %v", errs) |
| } |
| } |
| } |