From 1bd1f9655b724571a04797bdf85919e3dd70fa1b Mon Sep 17 00:00:00 2001 From: "hexi.ghx" Date: Tue, 3 Dec 2019 23:29:17 +0800 Subject: [PATCH] add excludeDates type validator --- Gopkg.lock | 4 +- .../cronhorizontalpodautoscaler_types.go | 6 +- pkg/apis/autoscaling/v1beta1/validator.go | 90 +++++++++++++++++++ .../cronhorizontalpodautoscaler_controller.go | 14 ++- 4 files changed, 108 insertions(+), 6 deletions(-) create mode 100644 pkg/apis/autoscaling/v1beta1/validator.go diff --git a/Gopkg.lock b/Gopkg.lock index 5805c3f6e..998df9961 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -768,8 +768,6 @@ "discovery/cached/memory", "discovery/fake", "dynamic", - "dynamic/dynamicinformer", - "dynamic/dynamiclister", "informers", "informers/admissionregistration", "informers/admissionregistration/v1beta1", @@ -1055,6 +1053,7 @@ "pkg/client/apiutil", "pkg/client/config", "pkg/controller", + "pkg/controller/controllerutil", "pkg/envtest", "pkg/envtest/printer", "pkg/event", @@ -1175,6 +1174,7 @@ "sigs.k8s.io/controller-runtime/pkg/client", "sigs.k8s.io/controller-runtime/pkg/client/config", "sigs.k8s.io/controller-runtime/pkg/controller", + "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil", "sigs.k8s.io/controller-runtime/pkg/envtest", "sigs.k8s.io/controller-runtime/pkg/handler", "sigs.k8s.io/controller-runtime/pkg/manager", diff --git a/pkg/apis/autoscaling/v1beta1/cronhorizontalpodautoscaler_types.go b/pkg/apis/autoscaling/v1beta1/cronhorizontalpodautoscaler_types.go index 4c48ec3b1..c247f95b5 100644 --- a/pkg/apis/autoscaling/v1beta1/cronhorizontalpodautoscaler_types.go +++ b/pkg/apis/autoscaling/v1beta1/cronhorizontalpodautoscaler_types.go @@ -27,9 +27,9 @@ import ( type CronHorizontalPodAutoscalerSpec struct { // INSERT ADDITIONAL SPEC FIELDS - desired state of cluster // Important: Run "make" to regenerate code after modifying this file - ExcludeDates []string `json:"excludeDates"` - ScaleTargetRef ScaleTargetRef `json:"scaleTargetRef"` - Jobs []Job `json:"jobs"` + ExcludeDates []string `json:"excludeDates" validate:"excludeDatesValidator"` + ScaleTargetRef ScaleTargetRef `json:"scaleTargetRef"` + Jobs []Job `json:"jobs"` } type Job struct { diff --git a/pkg/apis/autoscaling/v1beta1/validator.go b/pkg/apis/autoscaling/v1beta1/validator.go new file mode 100644 index 000000000..504673af7 --- /dev/null +++ b/pkg/apis/autoscaling/v1beta1/validator.go @@ -0,0 +1,90 @@ +package v1beta1 + +import ( + "fmt" + "github.com/ringtail/go-cron" + "reflect" + "strings" +) + +const tagName = "validate" + +type Validator interface { + Validate(interface{}) (bool, error) +} + +type DefaultValidator struct { +} + +type NumberValidator struct { + Min int + Max int +} + +type StringValidator struct { + Min int + Max int +} + +func (v StringValidator) Validate(val interface{}) (bool, error) { + return true, nil +} + +type ExcludeDatesValidator struct { +} + +func (v ExcludeDatesValidator) Validate(val interface{}) (bool, error) { + switch i := val.(type) { + case []string: + parser := cron.NewParser(cron.Second | cron.Minute | cron.Hour | cron.Dom | cron.Month | cron.Dow) + for _, item := range i { + _, err := parser.Parse(item) + if err != nil { + return false, err + } + } + } + return true, nil +} + +func (v DefaultValidator) Validate(val interface{}) (bool,error) { + return true, nil +} + +// get Validator struct corresponding to validate tag +func getValidatorFromTag(tag string) Validator { + args := strings.Split(tag, ",") + switch args[0] { + case "excludeDatesValidator": + validator := ExcludeDatesValidator{} + return validator + } + return DefaultValidator{} +} + +func Validate(s interface{}) (bool, []error){ + errs := []error{} + pass := true + v := reflect.ValueOf(s) + for i := 0; i < v.NumField(); i++ { + // Get the field tag value + tag := strings.TrimSpace(v.Type().Field(i).Tag.Get(tagName)) + + if tag == "" || tag=="-" { + continue + } + // Get a validator + validator := getValidatorFromTag(tag) + + // Perform validation + valid, err := validator.Validate(v.Field(i).Interface()) + + if !valid && err != nil { + pass = false + errs = append(errs, fmt.Errorf("%s %s", v.Type().Field(i).Name, err.Error())) + } + } + return pass, errs +} + + diff --git a/pkg/controller/cronhorizontalpodautoscaler/cronhorizontalpodautoscaler_controller.go b/pkg/controller/cronhorizontalpodautoscaler/cronhorizontalpodautoscaler_controller.go index 2e9e57dd7..b23c91737 100644 --- a/pkg/controller/cronhorizontalpodautoscaler/cronhorizontalpodautoscaler_controller.go +++ b/pkg/controller/cronhorizontalpodautoscaler/cronhorizontalpodautoscaler_controller.go @@ -111,7 +111,7 @@ func (r *ReconcileCronHorizontalPodAutoscaler) Reconcile(request reconcile.Reque leftConditions := make([]v1beta1.Condition, 0) // check scaleTargetRef and excludeDates - if checkGlobalParamsChanges(instance.Status, instance.Spec) { + if !validateParams(instance) || checkGlobalParamsChanges(instance.Status, instance.Spec) { for _, cJob := range conditions { r.CronManager.delete(cJob.JobId) } @@ -237,6 +237,18 @@ func updateConditions(conditions []v1beta1.Condition, condition v1beta1.Conditio return r } +func validateParams(instance *v1beta1.CronHorizontalPodAutoscaler) (bool) { + pass := true + var errs []error + // verify spec params - ExcludeDates + validRes,errs:= autoscalingv1beta1.Validate(instance.Spec.ExcludeDates) + if !validRes || errs != nil { + log.Errorf("Failed to validate spec.ExcludeDates: %s Errors are: %s ", instance.Spec.ExcludeDates, errs) + pass = false + } + return pass +} + // if global params changed then all jobs need to be recreated. func checkGlobalParamsChanges(status v1beta1.CronHorizontalPodAutoscalerStatus, spec v1beta1.CronHorizontalPodAutoscalerSpec) bool { if &status.ScaleTargetRef != nil && (status.ScaleTargetRef.Kind != spec.ScaleTargetRef.Kind || status.ScaleTargetRef.ApiVersion != spec.ScaleTargetRef.ApiVersion ||