Merge pull request #20703 from mwielgus/hpa-cm-validation

Auto commit by PR queue bot
pull/6/head
k8s-merge-robot 2016-02-06 21:16:51 -08:00
commit e76caf43aa
4 changed files with 134 additions and 4 deletions

View File

@ -17,6 +17,7 @@ limitations under the License.
package validation package validation
import ( import (
"encoding/json"
"net" "net"
"regexp" "regexp"
"strconv" "strconv"
@ -27,6 +28,7 @@ import (
unversionedvalidation "k8s.io/kubernetes/pkg/api/unversioned/validation" unversionedvalidation "k8s.io/kubernetes/pkg/api/unversioned/validation"
apivalidation "k8s.io/kubernetes/pkg/api/validation" apivalidation "k8s.io/kubernetes/pkg/api/validation"
"k8s.io/kubernetes/pkg/apis/extensions" "k8s.io/kubernetes/pkg/apis/extensions"
"k8s.io/kubernetes/pkg/controller/podautoscaler"
"k8s.io/kubernetes/pkg/labels" "k8s.io/kubernetes/pkg/labels"
"k8s.io/kubernetes/pkg/util/intstr" "k8s.io/kubernetes/pkg/util/intstr"
"k8s.io/kubernetes/pkg/util/sets" "k8s.io/kubernetes/pkg/util/sets"
@ -85,9 +87,34 @@ func ValidateSubresourceReference(ref extensions.SubresourceReference, fldPath *
return allErrs return allErrs
} }
func validateHorizontalPodAutoscalerAnnotations(annotations map[string]string, fldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{}
if annotationValue, found := annotations[podautoscaler.HpaCustomMetricsTargetAnnotationName]; found {
// Try to parse the annotation
var targetList extensions.CustomMetricTargetList
if err := json.Unmarshal([]byte(annotationValue), &targetList); err != nil {
allErrs = append(allErrs, field.Invalid(fldPath.Child("annotations"), annotations, "failed to parse custom metrics target annotation"))
} else {
if len(targetList.Items) == 0 {
allErrs = append(allErrs, field.Required(fldPath.Child("annotations", "items"), "custom metrics target must not be empty"))
}
for _, target := range targetList.Items {
if target.Name == "" {
allErrs = append(allErrs, field.Required(fldPath.Child("annotations", "items", "name"), "missing custom metric target name"))
}
if target.TargetValue.MilliValue() <= 0 {
allErrs = append(allErrs, field.Invalid(fldPath.Child("annotations", "items", "value"), target.TargetValue, "custom metric target value must be greater than 0"))
}
}
}
}
return allErrs
}
func ValidateHorizontalPodAutoscaler(autoscaler *extensions.HorizontalPodAutoscaler) field.ErrorList { func ValidateHorizontalPodAutoscaler(autoscaler *extensions.HorizontalPodAutoscaler) field.ErrorList {
allErrs := apivalidation.ValidateObjectMeta(&autoscaler.ObjectMeta, true, ValidateHorizontalPodAutoscalerName, field.NewPath("metadata")) allErrs := apivalidation.ValidateObjectMeta(&autoscaler.ObjectMeta, true, ValidateHorizontalPodAutoscalerName, field.NewPath("metadata"))
allErrs = append(allErrs, validateHorizontalPodAutoscalerSpec(autoscaler.Spec, field.NewPath("spec"))...) allErrs = append(allErrs, validateHorizontalPodAutoscalerSpec(autoscaler.Spec, field.NewPath("spec"))...)
allErrs = append(allErrs, validateHorizontalPodAutoscalerAnnotations(autoscaler.Annotations, field.NewPath("metadata"))...)
return allErrs return allErrs
} }

View File

@ -24,6 +24,7 @@ import (
"k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/unversioned" "k8s.io/kubernetes/pkg/api/unversioned"
"k8s.io/kubernetes/pkg/apis/extensions" "k8s.io/kubernetes/pkg/apis/extensions"
"k8s.io/kubernetes/pkg/controller/podautoscaler"
"k8s.io/kubernetes/pkg/util/intstr" "k8s.io/kubernetes/pkg/util/intstr"
) )
@ -60,6 +61,24 @@ func TestValidateHorizontalPodAutoscaler(t *testing.T) {
MaxReplicas: 5, MaxReplicas: 5,
}, },
}, },
{
ObjectMeta: api.ObjectMeta{
Name: "myautoscaler",
Namespace: api.NamespaceDefault,
Annotations: map[string]string{
podautoscaler.HpaCustomMetricsTargetAnnotationName: "{\"items\":[{\"name\":\"qps\",\"value\":\"20\"}]}",
},
},
Spec: extensions.HorizontalPodAutoscalerSpec{
ScaleRef: extensions.SubresourceReference{
Kind: "ReplicationController",
Name: "myrc",
Subresource: "scale",
},
MinReplicas: newInt(1),
MaxReplicas: 5,
},
},
} }
for _, successCase := range successCases { for _, successCase := range successCases {
if errs := ValidateHorizontalPodAutoscaler(&successCase); len(errs) != 0 { if errs := ValidateHorizontalPodAutoscaler(&successCase); len(errs) != 0 {
@ -204,6 +223,90 @@ func TestValidateHorizontalPodAutoscaler(t *testing.T) {
}, },
msg: "must be greater than 0", msg: "must be greater than 0",
}, },
{
horizontalPodAutoscaler: extensions.HorizontalPodAutoscaler{
ObjectMeta: api.ObjectMeta{
Name: "myautoscaler",
Namespace: api.NamespaceDefault,
Annotations: map[string]string{
podautoscaler.HpaCustomMetricsTargetAnnotationName: "broken",
},
},
Spec: extensions.HorizontalPodAutoscalerSpec{
ScaleRef: extensions.SubresourceReference{
Kind: "ReplicationController",
Name: "myrc",
Subresource: "scale",
},
MinReplicas: newInt(1),
MaxReplicas: 5,
},
},
msg: "failed to parse custom metrics target annotation",
},
{
horizontalPodAutoscaler: extensions.HorizontalPodAutoscaler{
ObjectMeta: api.ObjectMeta{
Name: "myautoscaler",
Namespace: api.NamespaceDefault,
Annotations: map[string]string{
podautoscaler.HpaCustomMetricsTargetAnnotationName: "{}",
},
},
Spec: extensions.HorizontalPodAutoscalerSpec{
ScaleRef: extensions.SubresourceReference{
Kind: "ReplicationController",
Name: "myrc",
Subresource: "scale",
},
MinReplicas: newInt(1),
MaxReplicas: 5,
},
},
msg: "custom metrics target must not be empty",
},
{
horizontalPodAutoscaler: extensions.HorizontalPodAutoscaler{
ObjectMeta: api.ObjectMeta{
Name: "myautoscaler",
Namespace: api.NamespaceDefault,
Annotations: map[string]string{
podautoscaler.HpaCustomMetricsTargetAnnotationName: "{\"items\":[{\"value\":\"20\"}]}",
},
},
Spec: extensions.HorizontalPodAutoscalerSpec{
ScaleRef: extensions.SubresourceReference{
Kind: "ReplicationController",
Name: "myrc",
Subresource: "scale",
},
MinReplicas: newInt(1),
MaxReplicas: 5,
},
},
msg: "missing custom metric target name",
},
{
horizontalPodAutoscaler: extensions.HorizontalPodAutoscaler{
ObjectMeta: api.ObjectMeta{
Name: "myautoscaler",
Namespace: api.NamespaceDefault,
Annotations: map[string]string{
podautoscaler.HpaCustomMetricsTargetAnnotationName: "{\"items\":[{\"name\":\"qps\",\"value\":\"0\"}]}",
},
},
Spec: extensions.HorizontalPodAutoscalerSpec{
ScaleRef: extensions.SubresourceReference{
Kind: "ReplicationController",
Name: "myrc",
Subresource: "scale",
},
MinReplicas: newInt(1),
MaxReplicas: 5,
},
},
msg: "custom metric target value must be greater than 0",
},
} }
for _, c := range errorCases { for _, c := range errorCases {

View File

@ -39,7 +39,7 @@ const (
// TODO: make it a flag or HPA spec element. // TODO: make it a flag or HPA spec element.
tolerance = 0.1 tolerance = 0.1
HpaCustomMetricsDefinitionAnnotationName = "alpha/definiton.custom-metrics.podautoscaler.kubernetes.io" HpaCustomMetricsTargetAnnotationName = "alpha/target.custom-metrics.podautoscaler.kubernetes.io"
HpaCustomMetricsStatusAnnotationName = "alpha/status.custom-metrics.podautoscaler.kubernetes.io" HpaCustomMetricsStatusAnnotationName = "alpha/status.custom-metrics.podautoscaler.kubernetes.io"
) )
@ -190,7 +190,7 @@ func (a *HorizontalController) reconcileAutoscaler(hpa extensions.HorizontalPodA
} }
} }
if cmAnnotation, cmAnnotationFound := hpa.Annotations[HpaCustomMetricsDefinitionAnnotationName]; cmAnnotationFound { if cmAnnotation, cmAnnotationFound := hpa.Annotations[HpaCustomMetricsTargetAnnotationName]; cmAnnotationFound {
cmDesiredReplicas, cmStatus, cmTimestamp, err = a.computeReplicasForCustomMetrics(hpa, scale, cmAnnotation) cmDesiredReplicas, cmStatus, cmTimestamp, err = a.computeReplicasForCustomMetrics(hpa, scale, cmAnnotation)
if err != nil { if err != nil {
a.eventRecorder.Event(&hpa, api.EventTypeWarning, "FailedComputeCMReplicas", err.Error()) a.eventRecorder.Event(&hpa, api.EventTypeWarning, "FailedComputeCMReplicas", err.Error())

View File

@ -110,7 +110,7 @@ func (tc *testCase) prepareTestClient(t *testing.T) *fake.Clientset {
t.Fatalf("Failed to marshal cm: %v", err) t.Fatalf("Failed to marshal cm: %v", err)
} }
obj.Items[0].Annotations = make(map[string]string) obj.Items[0].Annotations = make(map[string]string)
obj.Items[0].Annotations[HpaCustomMetricsDefinitionAnnotationName] = string(b) obj.Items[0].Annotations[HpaCustomMetricsTargetAnnotationName] = string(b)
} }
return true, obj, nil return true, obj, nil
}) })