From f0d0622b150da1e40108797d57c7810d41b75dd8 Mon Sep 17 00:00:00 2001 From: Howard Lau Date: Sun, 1 Nov 2020 07:52:13 +0000 Subject: [PATCH 1/3] Add validation webhook Signed-off-by: Howard Lau --- api/v1/seaweed_webhook.go | 91 +++++++++++++++++++++++++++++++++ api/v1/zz_generated.deepcopy.go | 2 +- config/webhook/manifests.yaml | 52 +++++++++++++++++++ main.go | 4 ++ 4 files changed, 148 insertions(+), 1 deletion(-) create mode 100644 api/v1/seaweed_webhook.go create mode 100644 config/webhook/manifests.yaml diff --git a/api/v1/seaweed_webhook.go b/api/v1/seaweed_webhook.go new file mode 100644 index 0000000..667dca0 --- /dev/null +++ b/api/v1/seaweed_webhook.go @@ -0,0 +1,91 @@ +/* + + +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 v1 + +import ( + "fmt" + + v1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/resource" + "k8s.io/apimachinery/pkg/runtime" + ctrl "sigs.k8s.io/controller-runtime" + logf "sigs.k8s.io/controller-runtime/pkg/log" + "sigs.k8s.io/controller-runtime/pkg/webhook" +) + +// log is for logging in this package. +var seaweedlog = logf.Log.WithName("seaweed-resource") + +func (r *Seaweed) SetupWebhookWithManager(mgr ctrl.Manager) error { + return ctrl.NewWebhookManagedBy(mgr). + For(r). + Complete() +} + +// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN! + +// +kubebuilder:webhook:path=/mutate-seaweed-seaweedfs-com-v1-seaweed,mutating=true,failurePolicy=fail,groups=seaweed.seaweedfs.com,resources=seaweeds,verbs=create;update,versions=v1,name=mseaweed.kb.io + +var _ webhook.Defaulter = &Seaweed{} + +// Default implements webhook.Defaulter so a webhook will be registered for the type +func (r *Seaweed) Default() { + seaweedlog.Info("default", "name", r.Name) + + // TODO(user): fill in your defaulting logic. +} + +// TODO(user): change verbs to "verbs=create;update;delete" if you want to enable deletion validation. +// +kubebuilder:webhook:verbs=create;update,path=/validate-seaweed-seaweedfs-com-v1-seaweed,mutating=false,failurePolicy=fail,groups=seaweed.seaweedfs.com,resources=seaweeds,versions=v1,name=vseaweed.kb.io + +var _ webhook.Validator = &Seaweed{} + +// ValidateCreate implements webhook.Validator so a webhook will be registered for the type +func (r *Seaweed) ValidateCreate() error { + seaweedlog.Info("validate create", "name", r.Name) + + // TODO(user): fill in your validation logic upon object creation. + if r.Spec.Master == nil { + return fmt.Errorf("seaweed[%s/%s] must have master spec", r.Namespace, r.Name) + } + + if r.Spec.Volume == nil { + return fmt.Errorf("seaweed[%s/%s] must have volume spec", r.Namespace, r.Name) + } + + if r.Spec.Volume.Requests[v1.ResourceStorage].Equal(resource.MustParse("0")) { + return fmt.Errorf("seaweed[%s/%s] volume storage request cannot be zero", r.Namespace, r.Name) + } + + return nil +} + +// ValidateUpdate implements webhook.Validator so a webhook will be registered for the type +func (r *Seaweed) ValidateUpdate(old runtime.Object) error { + seaweedlog.Info("validate update", "name", r.Name) + + // TODO(user): fill in your validation logic upon object update. + return nil +} + +// ValidateDelete implements webhook.Validator so a webhook will be registered for the type +func (r *Seaweed) ValidateDelete() error { + seaweedlog.Info("validate delete", "name", r.Name) + + // TODO(user): fill in your validation logic upon object deletion. + return nil +} diff --git a/api/v1/zz_generated.deepcopy.go b/api/v1/zz_generated.deepcopy.go index 5493b98..fb41496 100644 --- a/api/v1/zz_generated.deepcopy.go +++ b/api/v1/zz_generated.deepcopy.go @@ -22,7 +22,7 @@ package v1 import ( corev1 "k8s.io/api/core/v1" - runtime "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime" ) // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. diff --git a/config/webhook/manifests.yaml b/config/webhook/manifests.yaml new file mode 100644 index 0000000..5148573 --- /dev/null +++ b/config/webhook/manifests.yaml @@ -0,0 +1,52 @@ + +--- +apiVersion: admissionregistration.k8s.io/v1beta1 +kind: MutatingWebhookConfiguration +metadata: + creationTimestamp: null + name: mutating-webhook-configuration +webhooks: +- clientConfig: + caBundle: Cg== + service: + name: webhook-service + namespace: system + path: /mutate-seaweed-seaweedfs-com-v1-seaweed + failurePolicy: Fail + name: mseaweed.kb.io + rules: + - apiGroups: + - seaweed.seaweedfs.com + apiVersions: + - v1 + operations: + - CREATE + - UPDATE + resources: + - seaweeds + +--- +apiVersion: admissionregistration.k8s.io/v1beta1 +kind: ValidatingWebhookConfiguration +metadata: + creationTimestamp: null + name: validating-webhook-configuration +webhooks: +- clientConfig: + caBundle: Cg== + service: + name: webhook-service + namespace: system + path: /validate-seaweed-seaweedfs-com-v1-seaweed + failurePolicy: Fail + name: vseaweed.kb.io + rules: + - apiGroups: + - seaweed.seaweedfs.com + apiVersions: + - v1 + operations: + - CREATE + - UPDATE + resources: + - seaweeds diff --git a/main.go b/main.go index ca8885a..c35a9a0 100644 --- a/main.go +++ b/main.go @@ -75,6 +75,10 @@ func main() { setupLog.Error(err, "unable to create controller", "controller", "Seaweed") os.Exit(1) } + if err = (&seaweedv1.Seaweed{}).SetupWebhookWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create webhook", "webhook", "Seaweed") + os.Exit(1) + } // +kubebuilder:scaffold:builder setupLog.Info("starting manager") From 9d134da58269a8bf895b67e12ecd7729b2f7668b Mon Sep 17 00:00:00 2001 From: Howard Lau Date: Sun, 1 Nov 2020 08:21:01 +0000 Subject: [PATCH 2/3] Aggregate errors Signed-off-by: Howard Lau --- api/v1/seaweed_webhook.go | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/api/v1/seaweed_webhook.go b/api/v1/seaweed_webhook.go index 667dca0..9bd9130 100644 --- a/api/v1/seaweed_webhook.go +++ b/api/v1/seaweed_webhook.go @@ -19,9 +19,10 @@ package v1 import ( "fmt" - v1 "k8s.io/api/core/v1" + corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/resource" "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/util/errors" ctrl "sigs.k8s.io/controller-runtime" logf "sigs.k8s.io/controller-runtime/pkg/log" "sigs.k8s.io/controller-runtime/pkg/webhook" @@ -57,21 +58,22 @@ var _ webhook.Validator = &Seaweed{} // ValidateCreate implements webhook.Validator so a webhook will be registered for the type func (r *Seaweed) ValidateCreate() error { seaweedlog.Info("validate create", "name", r.Name) + errs := []error{} // TODO(user): fill in your validation logic upon object creation. if r.Spec.Master == nil { - return fmt.Errorf("seaweed[%s/%s] must have master spec", r.Namespace, r.Name) + errs = append(errs, fmt.Errorf("seaweed[%s/%s] must have master spec", r.Namespace, r.Name)) } if r.Spec.Volume == nil { - return fmt.Errorf("seaweed[%s/%s] must have volume spec", r.Namespace, r.Name) + errs = append(errs, fmt.Errorf("seaweed[%s/%s] must have volume spec", r.Namespace, r.Name)) + } else { + if r.Spec.Volume.Requests[corev1.ResourceStorage].Equal(resource.MustParse("0")) { + errs = append(errs, fmt.Errorf("seaweed[%s/%s] volume storage request cannot be zero", r.Namespace, r.Name)) + } } - if r.Spec.Volume.Requests[v1.ResourceStorage].Equal(resource.MustParse("0")) { - return fmt.Errorf("seaweed[%s/%s] volume storage request cannot be zero", r.Namespace, r.Name) - } - - return nil + return errors.NewAggregate(errs) } // ValidateUpdate implements webhook.Validator so a webhook will be registered for the type From 6437abce16e539a095ceb8bf077b2a7fb2129c69 Mon Sep 17 00:00:00 2001 From: Howard Lau Date: Sun, 1 Nov 2020 08:50:31 +0000 Subject: [PATCH 3/3] less verbose Signed-off-by: Howard Lau --- api/v1/seaweed_webhook.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/api/v1/seaweed_webhook.go b/api/v1/seaweed_webhook.go index 9bd9130..56b6aed 100644 --- a/api/v1/seaweed_webhook.go +++ b/api/v1/seaweed_webhook.go @@ -17,12 +17,12 @@ limitations under the License. package v1 import ( - "fmt" + "errors" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/resource" "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/util/errors" + utilerrors "k8s.io/apimachinery/pkg/util/errors" ctrl "sigs.k8s.io/controller-runtime" logf "sigs.k8s.io/controller-runtime/pkg/log" "sigs.k8s.io/controller-runtime/pkg/webhook" @@ -62,18 +62,18 @@ func (r *Seaweed) ValidateCreate() error { // TODO(user): fill in your validation logic upon object creation. if r.Spec.Master == nil { - errs = append(errs, fmt.Errorf("seaweed[%s/%s] must have master spec", r.Namespace, r.Name)) + errs = append(errs, errors.New("missing master spec")) } if r.Spec.Volume == nil { - errs = append(errs, fmt.Errorf("seaweed[%s/%s] must have volume spec", r.Namespace, r.Name)) + errs = append(errs, errors.New("missing volume spec")) } else { if r.Spec.Volume.Requests[corev1.ResourceStorage].Equal(resource.MustParse("0")) { - errs = append(errs, fmt.Errorf("seaweed[%s/%s] volume storage request cannot be zero", r.Namespace, r.Name)) + errs = append(errs, errors.New("volume storage request cannot be zero")) } } - return errors.NewAggregate(errs) + return utilerrors.NewAggregate(errs) } // ValidateUpdate implements webhook.Validator so a webhook will be registered for the type