From e5ef9e1406a12811f08139d88a7552e8c9e52e79 Mon Sep 17 00:00:00 2001 From: Solly Ross Date: Mon, 2 Nov 2015 13:50:43 -0500 Subject: [PATCH] Add Validators for Scale Objects This commit introduces a validator for use with Scale updates. The validator checks that we have > 0 replica count, as well as the normal ObjectMeta checks (some of which have to be faked since they don't exist on the Scale object). --- pkg/apis/extensions/validation/validation.go | 11 ++++ .../extensions/validation/validation_test.go | 64 +++++++++++++++++++ pkg/registry/deployment/etcd/etcd.go | 7 ++ .../experimental/controller/etcd/etcd.go | 7 ++ 4 files changed, 89 insertions(+) diff --git a/pkg/apis/extensions/validation/validation.go b/pkg/apis/extensions/validation/validation.go index 7c1210ae8ed..d36c262f2b8 100644 --- a/pkg/apis/extensions/validation/validation.go +++ b/pkg/apis/extensions/validation/validation.go @@ -564,3 +564,14 @@ func ValidatePodSelectorRequirement(sr extensions.PodSelectorRequirement) errs.V allErrs = append(allErrs, apivalidation.ValidateLabelName(sr.Key, "key")...) return allErrs } + +func ValidateScale(scale *extensions.Scale) errs.ValidationErrorList { + allErrs := errs.ValidationErrorList{} + allErrs = append(allErrs, apivalidation.ValidateObjectMeta(&scale.ObjectMeta, true, apivalidation.NameIsDNSSubdomain).Prefix("metadata")...) + + if scale.Spec.Replicas < 0 { + allErrs = append(allErrs, errs.NewFieldInvalid("spec.replicas", scale.Spec.Replicas, "must be non-negative")) + } + + return allErrs +} diff --git a/pkg/apis/extensions/validation/validation_test.go b/pkg/apis/extensions/validation/validation_test.go index f0fbad786fd..2b40fda1b23 100644 --- a/pkg/apis/extensions/validation/validation_test.go +++ b/pkg/apis/extensions/validation/validation_test.go @@ -1145,6 +1145,70 @@ func TestValidateClusterAutoscaler(t *testing.T) { } } +func TestValidateScale(t *testing.T) { + successCases := []extensions.Scale{ + { + ObjectMeta: api.ObjectMeta{ + Name: "frontend", + Namespace: api.NamespaceDefault, + }, + Spec: extensions.ScaleSpec{ + Replicas: 1, + }, + }, + { + ObjectMeta: api.ObjectMeta{ + Name: "frontend", + Namespace: api.NamespaceDefault, + }, + Spec: extensions.ScaleSpec{ + Replicas: 10, + }, + }, + { + ObjectMeta: api.ObjectMeta{ + Name: "frontend", + Namespace: api.NamespaceDefault, + }, + Spec: extensions.ScaleSpec{ + Replicas: 0, + }, + }, + } + + for _, successCase := range successCases { + if errs := ValidateScale(&successCase); len(errs) != 0 { + t.Errorf("expected success: %v", errs) + } + } + + errorCases := []struct { + scale extensions.Scale + msg string + }{ + { + scale: extensions.Scale{ + ObjectMeta: api.ObjectMeta{ + Name: "frontend", + Namespace: api.NamespaceDefault, + }, + Spec: extensions.ScaleSpec{ + Replicas: -1, + }, + }, + msg: "must be non-negative", + }, + } + + 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 newInt(val int) *int { p := new(int) *p = val diff --git a/pkg/registry/deployment/etcd/etcd.go b/pkg/registry/deployment/etcd/etcd.go index 265588d33b0..175750601fa 100644 --- a/pkg/registry/deployment/etcd/etcd.go +++ b/pkg/registry/deployment/etcd/etcd.go @@ -30,6 +30,8 @@ import ( etcdgeneric "k8s.io/kubernetes/pkg/registry/generic/etcd" "k8s.io/kubernetes/pkg/runtime" "k8s.io/kubernetes/pkg/storage" + + extvalidation "k8s.io/kubernetes/pkg/apis/extensions/validation" ) // DeploymentStorage includes dummy storage for Deployments and for Scale subresource. @@ -149,6 +151,11 @@ func (r *ScaleREST) Update(ctx api.Context, obj runtime.Object) (runtime.Object, if !ok { return nil, false, errors.NewBadRequest(fmt.Sprintf("wrong object passed to Scale update: %v", obj)) } + + if errs := extvalidation.ValidateScale(scale); len(errs) > 0 { + return nil, false, errors.NewInvalid("scale", scale.Name, errs) + } + deployment, err := (*r.registry).GetDeployment(ctx, scale.Name) if err != nil { return nil, false, errors.NewNotFound("scale", scale.Name) diff --git a/pkg/registry/experimental/controller/etcd/etcd.go b/pkg/registry/experimental/controller/etcd/etcd.go index fc4cfe8161d..095f414bd8f 100644 --- a/pkg/registry/experimental/controller/etcd/etcd.go +++ b/pkg/registry/experimental/controller/etcd/etcd.go @@ -29,6 +29,8 @@ import ( "k8s.io/kubernetes/pkg/registry/controller/etcd" "k8s.io/kubernetes/pkg/apis/extensions" + + extvalidation "k8s.io/kubernetes/pkg/apis/extensions/validation" ) // Container includes dummy storage for RC pods and experimental storage for Scale. @@ -89,6 +91,11 @@ func (r *ScaleREST) Update(ctx api.Context, obj runtime.Object) (runtime.Object, if !ok { return nil, false, errors.NewBadRequest(fmt.Sprintf("wrong object passed to Scale update: %v", obj)) } + + if errs := extvalidation.ValidateScale(scale); len(errs) > 0 { + return nil, false, errors.NewInvalid("scale", scale.Name, errs) + } + rc, err := (*r.registry).GetController(ctx, scale.Name) if err != nil { return nil, false, errors.NewNotFound("scale", scale.Name)