diff --git a/charts/openyurt/templates/yurt-manager-auto-generated.yaml b/charts/openyurt/templates/yurt-manager-auto-generated.yaml index bc3fcd3c644..ad0505d6b21 100644 --- a/charts/openyurt/templates/yurt-manager-auto-generated.yaml +++ b/charts/openyurt/templates/yurt-manager-auto-generated.yaml @@ -936,7 +936,7 @@ webhooks: service: name: webhook-service namespace: kube-system - path: /mutate-apps-openyurt-io-v1alpha1-staticpod + path: /mutate-apps-openyurt-io-staticpod failurePolicy: Fail name: mutate.apps.v1alpha1.staticpod.openyurt.io rules: @@ -1006,7 +1006,7 @@ webhooks: service: name: webhook-service namespace: kube-system - path: /validate-apps-openyurt-io-v1alpha1-staticpod + path: /validate-apps-openyurt-io-staticpod failurePolicy: Fail name: validate.apps.v1alpha1.staticpod.openyurt.io rules: diff --git a/cmd/yurt-manager/app/options/options.go b/cmd/yurt-manager/app/options/options.go index 3c343ed6645..6d195c34de5 100644 --- a/cmd/yurt-manager/app/options/options.go +++ b/cmd/yurt-manager/app/options/options.go @@ -24,18 +24,20 @@ import ( // YurtManagerOptions is the main context object for the yurt-manager. type YurtManagerOptions struct { - Generic *GenericOptions - NodePoolController *NodePoolControllerOptions - GatewayController *GatewayControllerOptions + Generic *GenericOptions + NodePoolController *NodePoolControllerOptions + GatewayController *GatewayControllerOptions + StaticPodController *StaticPodControllerOptions } // NewYurtManagerOptions creates a new YurtManagerOptions with a default config. func NewYurtManagerOptions() (*YurtManagerOptions, error) { s := YurtManagerOptions{ - Generic: NewGenericOptions(), - NodePoolController: NewNodePoolControllerOptions(), - GatewayController: NewGatewayControllerOptions(), + Generic: NewGenericOptions(), + NodePoolController: NewNodePoolControllerOptions(), + GatewayController: NewGatewayControllerOptions(), + StaticPodController: NewStaticPodControllerOptions(), } return &s, nil @@ -46,7 +48,7 @@ func (y *YurtManagerOptions) Flags() cliflag.NamedFlagSets { y.Generic.AddFlags(fss.FlagSet("generic")) y.NodePoolController.AddFlags(fss.FlagSet("nodepool controller")) y.GatewayController.AddFlags(fss.FlagSet("gateway controller")) - + y.StaticPodController.AddFlags(fss.FlagSet("staticpod controller")) // Please Add Other controller flags @kadisi return fss @@ -58,6 +60,7 @@ func (y *YurtManagerOptions) Validate() error { errs = append(errs, y.Generic.Validate()...) errs = append(errs, y.NodePoolController.Validate()...) errs = append(errs, y.GatewayController.Validate()...) + errs = append(errs, y.StaticPodController.Validate()...) return utilerrors.NewAggregate(errs) } diff --git a/cmd/yurt-manager/app/options/staticpodcontroller.go b/cmd/yurt-manager/app/options/staticpodcontroller.go new file mode 100644 index 00000000000..820095ed0ab --- /dev/null +++ b/cmd/yurt-manager/app/options/staticpodcontroller.go @@ -0,0 +1,59 @@ +/* +Copyright 2023 The OpenYurt 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 options + +import ( + "github.com/openyurtio/openyurt/pkg/controller/staticpod/config" + "github.com/spf13/pflag" +) + +type StaticPodControllerOptions struct { + *config.StaticPodControllerConfiguration +} + +func NewStaticPodControllerOptions() *StaticPodControllerOptions { + return &StaticPodControllerOptions{ + &config.StaticPodControllerConfiguration{}, + } +} + +// AddFlags adds flags related to nodepool for yurt-manager to the specified FlagSet. +func (n *StaticPodControllerOptions) AddFlags(fs *pflag.FlagSet) { + if n == nil { + return + } + + //fs.BoolVar(&n.CreateDefaultPool, "create-default-pool", n.CreateDefaultPool, "Create default cloud/edge pools if indicated.") +} + +// ApplyTo fills up nodepool config with options. +func (o *StaticPodControllerOptions) ApplyTo(cfg *config.StaticPodControllerConfiguration) error { + if o == nil { + return nil + } + + return nil +} + +// Validate checks validation of StaticPodControllerOptions. +func (o *StaticPodControllerOptions) Validate() []error { + if o == nil { + return nil + } + errs := []error{} + return errs +} diff --git a/go.mod b/go.mod index 10862d74350..6c6dc1d717d 100644 --- a/go.mod +++ b/go.mod @@ -59,7 +59,6 @@ require ( github.com/russross/blackfriday v1.5.2 // indirect github.com/xlab/treeprint v0.0.0-20181112141820-a009c3971eca // indirect go.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5 // indirect - k8s.io/cli-runtime v0.22.3 sigs.k8s.io/kustomize/api v0.8.11 // indirect sigs.k8s.io/kustomize/kyaml v0.11.0 // indirect ) @@ -100,7 +99,6 @@ require ( github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af // indirect github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect - github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de // indirect github.com/mailru/easyjson v0.7.6 // indirect github.com/mattn/go-isatty v0.0.14 // indirect github.com/mattn/go-runewidth v0.0.7 // indirect diff --git a/pkg/apis/apps/v1alpha1/default.go b/pkg/apis/apps/v1alpha1/default.go index be1a486b028..35cdcf6c539 100644 --- a/pkg/apis/apps/v1alpha1/default.go +++ b/pkg/apis/apps/v1alpha1/default.go @@ -16,6 +16,8 @@ limitations under the License. package v1alpha1 +import "k8s.io/apimachinery/pkg/util/intstr" + // SetDefaultsNodePool set default values for NodePool. func SetDefaultsNodePool(obj *NodePool) { // example for set default value for NodePool @@ -24,3 +26,19 @@ func SetDefaultsNodePool(obj *NodePool) { } } + +// SetDefaultsStaticPod set default values for StaticPod. +func SetDefaultsStaticPod(obj *StaticPod) { + // Set default max-unavailable to "10%" in auto mode if it's not set + strategy := obj.Spec.UpgradeStrategy.DeepCopy() + if strategy != nil && strategy.Type == AutoStaticPodUpgradeStrategyType && strategy.MaxUnavailable == nil { + v := intstr.FromString("10%") + obj.Spec.UpgradeStrategy.MaxUnavailable = &v + } + + // Set default RevisionHistoryLimit to 10 + if obj.Spec.RevisionHistoryLimit == nil { + obj.Spec.RevisionHistoryLimit = new(int32) + *obj.Spec.RevisionHistoryLimit = 10 + } +} diff --git a/pkg/controller/apis/config/types.go b/pkg/controller/apis/config/types.go index d01b44427d3..cd150a6bfd5 100644 --- a/pkg/controller/apis/config/types.go +++ b/pkg/controller/apis/config/types.go @@ -23,6 +23,7 @@ import ( gatewayconfig "github.com/openyurtio/openyurt/pkg/controller/gateway/config" nodepoolconfig "github.com/openyurtio/openyurt/pkg/controller/nodepool/config" + staticpodconfig "github.com/openyurtio/openyurt/pkg/controller/staticpod/config" ) // YurtControllerManagerConfiguration contains elements describing yurt-controller manager. @@ -46,6 +47,9 @@ type YurtManagerConfiguration struct { // GatewayControllerConfiguration holds configuration for GatewayController related features. GatewayController gatewayconfig.GatewayControllerConfiguration + + // StaticPodControllerConfiguration holds configuration for StaticPodController related features. + StaticPodController staticpodconfig.StaticPodControllerConfiguration } type GenericConfiguration struct { diff --git a/pkg/controller/staticpod/config/types.go b/pkg/controller/staticpod/config/types.go new file mode 100644 index 00000000000..aeebd2c565b --- /dev/null +++ b/pkg/controller/staticpod/config/types.go @@ -0,0 +1,21 @@ +/* +Copyright 2023 The OpenYurt 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 config + +// StaticPodControllerConfiguration contains elements describing GatewayController. +type StaticPodControllerConfiguration struct { +} diff --git a/pkg/controller/staticpod/staticpod_controller.go b/pkg/controller/staticpod/staticpod_controller.go index a15379a4b55..9e184998323 100644 --- a/pkg/controller/staticpod/staticpod_controller.go +++ b/pkg/controller/staticpod/staticpod_controller.go @@ -40,7 +40,9 @@ import ( "sigs.k8s.io/controller-runtime/pkg/reconcile" "sigs.k8s.io/controller-runtime/pkg/source" + appconfig "github.com/openyurtio/openyurt/cmd/yurt-manager/app/config" appsv1alpha1 "github.com/openyurtio/openyurt/pkg/apis/apps/v1alpha1" + "github.com/openyurtio/openyurt/pkg/controller/staticpod/config" "github.com/openyurtio/openyurt/pkg/controller/staticpod/upgradeinfo" "github.com/openyurtio/openyurt/pkg/controller/staticpod/util" utilclient "github.com/openyurtio/openyurt/pkg/util/client" @@ -129,11 +131,11 @@ func Format(format string, args ...interface{}) string { // Add creates a new StaticPod Controller and adds it to the Manager with default RBAC. The Manager will set fields on the Controller // and Start it when the Manager is Started. -func Add(mgr manager.Manager) error { +func Add(c *appconfig.CompletedConfig, mgr manager.Manager) error { if !utildiscovery.DiscoverGVK(controllerKind) { return nil } - return add(mgr, newReconciler(mgr)) + return add(mgr, newReconciler(c, mgr)) } var _ reconcile.Reconciler = &ReconcileStaticPod{} @@ -141,16 +143,18 @@ var _ reconcile.Reconciler = &ReconcileStaticPod{} // ReconcileStaticPod reconciles a StaticPod object type ReconcileStaticPod struct { client.Client - scheme *runtime.Scheme - recorder record.EventRecorder + scheme *runtime.Scheme + recorder record.EventRecorder + Configration config.StaticPodControllerConfiguration } // newReconciler returns a new reconcile.Reconciler -func newReconciler(mgr manager.Manager) reconcile.Reconciler { +func newReconciler(c *appconfig.CompletedConfig, mgr manager.Manager) reconcile.Reconciler { return &ReconcileStaticPod{ - Client: utilclient.NewClientFromManager(mgr, controllerName), - scheme: mgr.GetScheme(), - recorder: mgr.GetEventRecorderFor(controllerName), + Client: utilclient.NewClientFromManager(mgr, controllerName), + scheme: mgr.GetScheme(), + recorder: mgr.GetEventRecorderFor(controllerName), + Configration: c.ComponentConfig.StaticPodController, } } diff --git a/pkg/controller/staticpod/staticpod_controller_test.go b/pkg/controller/staticpod/staticpod_controller_test.go index 874a7b670c7..d411a15ac14 100644 --- a/pkg/controller/staticpod/staticpod_controller_test.go +++ b/pkg/controller/staticpod/staticpod_controller_test.go @@ -94,13 +94,12 @@ func TestReconcile(t *testing.T) { nodes := prepareNodes() instance := &appsv1alpha1.StaticPod{ ObjectMeta: metav1.ObjectMeta{ - Name: "foo", + Name: TestStaticPodName, + Namespace: metav1.NamespaceDefault, }, Spec: appsv1alpha1.StaticPodSpec{ - StaticPodNamespace: metav1.NamespaceDefault, - StaticPodName: "nginx", - StaticPodManifest: "nginx", - Template: corev1.PodTemplateSpec{}, + StaticPodManifest: "nginx", + Template: corev1.PodTemplateSpec{}, }, } @@ -116,7 +115,7 @@ func TestReconcile(t *testing.T) { instance.Spec.UpgradeStrategy = s c := fakeclint.NewClientBuilder().WithScheme(scheme).WithRuntimeObjects(instance).WithObjects(staticPods...).WithObjects(nodes...).Build() - var req = reconcile.Request{NamespacedName: types.NamespacedName{Name: "foo"}} + var req = reconcile.Request{NamespacedName: types.NamespacedName{Namespace: metav1.NamespaceDefault, Name: TestStaticPodName}} rsp := ReconcileStaticPod{ Client: c, scheme: scheme, diff --git a/pkg/controller/staticpod/upgradeinfo/upgrade_info_test.go b/pkg/controller/staticpod/upgradeinfo/upgrade_info_test.go index 6aadf201e70..733de03def4 100644 --- a/pkg/controller/staticpod/upgradeinfo/upgrade_info_test.go +++ b/pkg/controller/staticpod/upgradeinfo/upgrade_info_test.go @@ -66,18 +66,18 @@ func newPods(nodes []string, namespace string, isStaticPod bool) []client.Object func newStaticPod() *appsv1alpha1.StaticPod { return &appsv1alpha1.StaticPod{ - TypeMeta: metav1.TypeMeta{}, - ObjectMeta: metav1.ObjectMeta{}, - Spec: appsv1alpha1.StaticPodSpec{ - StaticPodName: fakeStaticPodName, - }, + TypeMeta: metav1.TypeMeta{}, + ObjectMeta: metav1.ObjectMeta{ + Name: fakeStaticPodName, + Namespace: metav1.NamespaceDefault}, + Spec: appsv1alpha1.StaticPodSpec{}, Status: appsv1alpha1.StaticPodStatus{}, } } func Test_ConstructStaticPodsUpgradeInfoList(t *testing.T) { staticPods := newPods(fakeStaticPodNodes, metav1.NamespaceDefault, true) - workerPods := newPods(fakeWorkerPodNodes, metav1.NamespaceSystem, false) + workerPods := newPods(fakeWorkerPodNodes, metav1.NamespaceDefault, false) expect := map[string]*UpgradeInfo{ "node1": { StaticPod: staticPods[0].(*corev1.Pod), @@ -85,7 +85,6 @@ func Test_ConstructStaticPodsUpgradeInfoList(t *testing.T) { }, "node2": { StaticPod: staticPods[1].(*corev1.Pod), - WorkerPod: workerPods[1].(*corev1.Pod), }, "node3": { diff --git a/pkg/webhook/add_staticpod.go b/pkg/webhook/add_v1alpha1_staticpod.go similarity index 75% rename from pkg/webhook/add_staticpod.go rename to pkg/webhook/add_v1alpha1_staticpod.go index 4b8ceb4be83..1b707150a9c 100644 --- a/pkg/webhook/add_staticpod.go +++ b/pkg/webhook/add_v1alpha1_staticpod.go @@ -17,11 +17,9 @@ limitations under the License. package webhook import ( - "github.com/openyurtio/openyurt/pkg/webhook/staticpod/mutating" - "github.com/openyurtio/openyurt/pkg/webhook/staticpod/validating" + "github.com/openyurtio/openyurt/pkg/webhook/staticpod/v1alpha1" ) func init() { - addHandlers(mutating.HandlerMap) - addHandlers(validating.HandlerMap) + addWebhook(&v1alpha1.StaticPodHandler{}) } diff --git a/pkg/webhook/staticpod/mutating/staticpod_handler.go b/pkg/webhook/staticpod/mutating/staticpod_handler.go deleted file mode 100644 index 1612d680c52..00000000000 --- a/pkg/webhook/staticpod/mutating/staticpod_handler.go +++ /dev/null @@ -1,89 +0,0 @@ -/* -Copyright 2023 The OpenYurt 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 mutating - -import ( - "context" - "encoding/json" - "fmt" - "net/http" - "reflect" - - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/klog/v2" - "sigs.k8s.io/controller-runtime/pkg/webhook/admission" - - appsv1alpha1 "github.com/openyurtio/openyurt/pkg/apis/apps/v1alpha1" - "github.com/openyurtio/openyurt/pkg/util" -) - -const ( - webhookName = "StaticPod-mutate-webhook" -) - -func Format(format string, args ...interface{}) string { - s := fmt.Sprintf(format, args...) - return fmt.Sprintf("%s: %s", webhookName, s) -} - -// StaticPodCreateUpdateHandler handles StaticPod -type StaticPodCreateUpdateHandler struct { - // Decoder decodes objects - Decoder *admission.Decoder -} - -var _ admission.Handler = &StaticPodCreateUpdateHandler{} - -// Handle handles admission requests. -func (h *StaticPodCreateUpdateHandler) Handle(ctx context.Context, req admission.Request) admission.Response { - - // Note !!!!!!!!!! - // We strongly recommend use Format() to encapsulation because Format() can print logs by module - // @kadisi - klog.Infof(Format("Handle StaticPod %s/%s", req.Namespace, req.Name)) - - obj := &appsv1alpha1.StaticPod{} - err := h.Decoder.Decode(req, obj) - if err != nil { - return admission.Errored(http.StatusBadRequest, err) - } - var copy runtime.Object = obj.DeepCopy() - // Set defaults - appsv1alpha1.SetDefaultsStaticPod(obj) - - if reflect.DeepEqual(obj, copy) { - return admission.Allowed("") - } - marshalled, err := json.Marshal(obj) - if err != nil { - return admission.Errored(http.StatusInternalServerError, err) - } - resp := admission.PatchResponseFromRaw(req.AdmissionRequest.Object.Raw, marshalled) - if len(resp.Patches) > 0 { - klog.Infof(Format("Admit StaticPod %s patches: %v", obj.Name, util.DumpJSON(resp.Patches))) - } - - return resp -} - -var _ admission.DecoderInjector = &StaticPodCreateUpdateHandler{} - -// InjectDecoder injects the decoder into the StaticPodCreateUpdateHandler -func (h *StaticPodCreateUpdateHandler) InjectDecoder(d *admission.Decoder) error { - h.Decoder = d - return nil -} diff --git a/pkg/webhook/staticpod/mutating/webhooks.go b/pkg/webhook/staticpod/v1alpha1/staticpod_default.go similarity index 51% rename from pkg/webhook/staticpod/mutating/webhooks.go rename to pkg/webhook/staticpod/v1alpha1/staticpod_default.go index 5cf3f2a8cb7..78af8464059 100644 --- a/pkg/webhook/staticpod/mutating/webhooks.go +++ b/pkg/webhook/staticpod/v1alpha1/staticpod_default.go @@ -14,17 +14,26 @@ See the License for the specific language governing permissions and limitations under the License. */ -package mutating +package v1alpha1 import ( - "sigs.k8s.io/controller-runtime/pkg/webhook/admission" -) + "context" + "fmt" -// +kubebuilder:webhook:path=/mutate-apps-openyurt-io-v1alpha1-staticpod,mutating=true,failurePolicy=fail,sideEffects=None,admissionReviewVersions=v1;v1beta1,groups=apps.openyurt.io,resources=staticpods,verbs=create;update,versions=v1alpha1,name=mutate.apps.v1alpha1.staticpod.openyurt.io + apierrors "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/runtime" -var ( - // HandlerMap contains admission webhook handlers - HandlerMap = map[string]admission.Handler{ - "mutate-apps-openyurt-io-v1alpha1-staticpod": &StaticPodCreateUpdateHandler{}, - } + "github.com/openyurtio/openyurt/pkg/apis/apps/v1alpha1" ) + +// Default satisfies the defaulting webhook interface. +func (webhook *StaticPodHandler) Default(ctx context.Context, obj runtime.Object) error { + sp, ok := obj.(*v1alpha1.StaticPod) + if !ok { + return apierrors.NewBadRequest(fmt.Sprintf("expected a StaticPod but got a %T", obj)) + } + + v1alpha1.SetDefaultsStaticPod(sp) + + return nil +} diff --git a/pkg/webhook/staticpod/v1alpha1/staticpod_handler.go b/pkg/webhook/staticpod/v1alpha1/staticpod_handler.go new file mode 100644 index 00000000000..268bc556046 --- /dev/null +++ b/pkg/webhook/staticpod/v1alpha1/staticpod_handler.go @@ -0,0 +1,56 @@ +/* +Copyright 2023 The OpenYurt 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 v1alpha1 + +import ( + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/client/apiutil" + "sigs.k8s.io/controller-runtime/pkg/webhook" + + "github.com/openyurtio/openyurt/pkg/apis/apps/v1alpha1" + "github.com/openyurtio/openyurt/pkg/webhook/util" +) + +// SetupWebhookWithManager sets up Cluster webhooks. mutate path, validatepath, error +func (webhook *StaticPodHandler) SetupWebhookWithManager(mgr ctrl.Manager) (string, string, error) { + // init + webhook.Client = mgr.GetClient() + + gvk, err := apiutil.GVKForObject(&v1alpha1.StaticPod{}, mgr.GetScheme()) + if err != nil { + return "", "", err + } + return util.GenerateMutatePath(gvk), + util.GenerateValidatePath(gvk), + ctrl.NewWebhookManagedBy(mgr). + For(&v1alpha1.StaticPod{}). + WithDefaulter(webhook). + WithValidator(webhook). + Complete() +} + +// +kubebuilder:webhook:path=/validate-apps-openyurt-io-staticpod,mutating=false,failurePolicy=fail,sideEffects=None,admissionReviewVersions=v1;v1beta1,groups=apps.openyurt.io,resources=staticpods,verbs=create;update,versions=v1alpha1,name=validate.apps.v1alpha1.staticpod.openyurt.io +// +kubebuilder:webhook:path=/mutate-apps-openyurt-io-staticpod,mutating=true,failurePolicy=fail,sideEffects=None,admissionReviewVersions=v1;v1beta1,groups=apps.openyurt.io,resources=staticpods,verbs=create;update,versions=v1alpha1,name=mutate.apps.v1alpha1.staticpod.openyurt.io + +// Cluster implements a validating and defaulting webhook for Cluster. +type StaticPodHandler struct { + Client client.Client +} + +var _ webhook.CustomDefaulter = &StaticPodHandler{} +var _ webhook.CustomValidator = &StaticPodHandler{} diff --git a/pkg/webhook/staticpod/v1alpha1/staticpod_validation.go b/pkg/webhook/staticpod/v1alpha1/staticpod_validation.go new file mode 100644 index 00000000000..f567da81467 --- /dev/null +++ b/pkg/webhook/staticpod/v1alpha1/staticpod_validation.go @@ -0,0 +1,108 @@ +/* +Copyright 2023 The OpenYurt 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 v1alpha1 + +import ( + "context" + "fmt" + + apierrors "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/util/validation/field" + "k8s.io/klog/v2" + + "github.com/openyurtio/openyurt/pkg/apis/apps/v1alpha1" +) + +// ValidateCreate implements webhook.CustomValidator so a webhook will be registered for the type. +func (webhook *StaticPodHandler) ValidateCreate(ctx context.Context, obj runtime.Object) error { + sp, ok := obj.(*v1alpha1.StaticPod) + if !ok { + return apierrors.NewBadRequest(fmt.Sprintf("expected a StaticPod but got a %T", obj)) + } + + return validate(sp) +} + +// ValidateUpdate implements webhook.CustomValidator so a webhook will be registered for the type. +func (webhook *StaticPodHandler) ValidateUpdate(ctx context.Context, oldObj, newObj runtime.Object) error { + newSP, ok := newObj.(*v1alpha1.StaticPod) + if !ok { + return apierrors.NewBadRequest(fmt.Sprintf("expected a StaticPod but got a %T", newObj)) + } + oldSP, ok := oldObj.(*v1alpha1.StaticPod) + if !ok { + return apierrors.NewBadRequest(fmt.Sprintf("expected a StaticPod but got a %T", oldObj)) + } + + if err := validate(newSP); err != nil { + return err + } + + if err := validate(oldSP); err != nil { + return err + } + + return nil +} + +// ValidateDelete implements webhook.CustomValidator so a webhook will be registered for the type. +func (webhook *StaticPodHandler) ValidateDelete(_ context.Context, obj runtime.Object) error { + return nil +} + +func validate(obj *v1alpha1.StaticPod) error { + if allErrs := validateStaticPodSpec(&obj.Spec); len(allErrs) > 0 { + return apierrors.NewInvalid(v1alpha1.GroupVersion.WithKind("StaticPod").GroupKind(), obj.Name, allErrs) + } + + klog.Infof("Validate StaticPod %s successfully ...", klog.KObj(obj)) + + return nil +} + +// validateStaticPodSpec validates the staticpod spec. +func validateStaticPodSpec(spec *v1alpha1.StaticPodSpec) field.ErrorList { + var allErrs field.ErrorList + + if spec.StaticPodManifest == "" { + allErrs = append(allErrs, field.Required(field.NewPath("spec").Child("StaticPodManifest"), + "StaticPodManifest is required")) + } + + strategy := &spec.UpgradeStrategy + if strategy == nil { + allErrs = append(allErrs, field.Required(field.NewPath("spec").Child("upgradeStrategy"), + "upgrade strategy is required")) + } + + if strategy.Type != v1alpha1.AutoStaticPodUpgradeStrategyType && strategy.Type != v1alpha1.OTAStaticPodUpgradeStrategyType { + allErrs = append(allErrs, field.NotSupported(field.NewPath("spec").Child("upgradeStrategy"), + strategy, []string{"auto", "ota"})) + } + + if strategy.Type == v1alpha1.AutoStaticPodUpgradeStrategyType && strategy.MaxUnavailable == nil { + allErrs = append(allErrs, field.Required(field.NewPath("spec").Child("upgradeStrategy"), + "max-unavailable is required in auto mode")) + } + + if allErrs != nil { + return allErrs + } + + return nil +} diff --git a/pkg/webhook/staticpod/validating/staticpod_handler.go b/pkg/webhook/staticpod/validating/staticpod_handler.go deleted file mode 100644 index 1167b381a09..00000000000 --- a/pkg/webhook/staticpod/validating/staticpod_handler.go +++ /dev/null @@ -1,128 +0,0 @@ -/* -Copyright 2023 The OpenYurt 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 validating - -import ( - "context" - "fmt" - "net/http" - - apierrors "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apimachinery/pkg/util/validation/field" - "k8s.io/klog/v2" - "sigs.k8s.io/controller-runtime/pkg/webhook/admission" - - appsv1alpha1 "github.com/openyurtio/openyurt/pkg/apis/apps/v1alpha1" -) - -// StaticPodCreateUpdateHandler handles StaticPod -type StaticPodCreateUpdateHandler struct { - // Decoder decodes objects - Decoder *admission.Decoder -} - -const ( - webhookName = "StaticPod-validate-webhook" -) - -func Format(format string, args ...interface{}) string { - s := fmt.Sprintf(format, args...) - return fmt.Sprintf("%s: %s", webhookName, s) -} - -var _ admission.Handler = &StaticPodCreateUpdateHandler{} - -// Handle handles admission requests. -func (h *StaticPodCreateUpdateHandler) Handle(ctx context.Context, req admission.Request) admission.Response { - // Note !!!!!!!!!! - // We strongly recommend use Format() to encapsulation because Format() can print logs by module - // @kadisi - klog.Infof(Format("Handle StaticPod %s/%s", req.Namespace, req.Name)) - - obj := &appsv1alpha1.StaticPod{} - - if err := h.Decoder.Decode(req, obj); err != nil { - return admission.Errored(http.StatusBadRequest, err) - } - - if err := validate(obj); err != nil { - klog.Warningf("Error validate StaticPod %s: %v", obj.Name, err) - return admission.Errored(http.StatusBadRequest, err) - } - - return admission.ValidationResponse(true, "allowed") -} - -func validate(obj *appsv1alpha1.StaticPod) error { - if allErrs := validateStaticPodSpec(&obj.Spec); len(allErrs) > 0 { - return apierrors.NewInvalid(appsv1alpha1.GroupVersion.WithKind("StaticPod").GroupKind(), obj.Name, allErrs) - } - - klog.Infof(Format("Validate StaticPod %s successfully ...", klog.KObj(obj))) - - return nil -} - -var _ admission.DecoderInjector = &StaticPodCreateUpdateHandler{} - -// InjectDecoder injects the decoder into the StaticPodCreateUpdateHandler -func (h *StaticPodCreateUpdateHandler) InjectDecoder(d *admission.Decoder) error { - h.Decoder = d - return nil -} - -// validateStaticPodSpec validates the staticpod spec. -func validateStaticPodSpec(spec *appsv1alpha1.StaticPodSpec) field.ErrorList { - var allErrs field.ErrorList - - if spec.StaticPodName == "" { - allErrs = append(allErrs, field.Required(field.NewPath("spec").Child("StaticPodName"), - "StaticPodName is required")) - } - - if spec.StaticPodManifest == "" { - allErrs = append(allErrs, field.Required(field.NewPath("spec").Child("StaticPodManifest"), - "StaticPodManifest is required")) - } - - if spec.StaticPodNamespace == "" { - allErrs = append(allErrs, field.Required(field.NewPath("spec").Child("Namespace"), - "Namespace is required")) - } - - strategy := &spec.UpgradeStrategy - if strategy == nil { - allErrs = append(allErrs, field.Required(field.NewPath("spec").Child("upgradeStrategy"), - "upgrade strategy is required")) - } - - if strategy.Type != appsv1alpha1.AutoStaticPodUpgradeStrategyType && strategy.Type != appsv1alpha1.OTAStaticPodUpgradeStrategyType { - allErrs = append(allErrs, field.NotSupported(field.NewPath("spec").Child("upgradeStrategy"), - strategy, []string{"auto", "ota"})) - } - - if strategy.Type == appsv1alpha1.AutoStaticPodUpgradeStrategyType && strategy.MaxUnavailable == nil { - allErrs = append(allErrs, field.Required(field.NewPath("spec").Child("upgradeStrategy"), - "max-unavailable is required in auto mode")) - } - - if allErrs != nil { - return allErrs - } - - return nil -} diff --git a/pkg/webhook/staticpod/validating/staticpod_handler_test.go b/pkg/webhook/staticpod/validating/staticpod_handler_test.go deleted file mode 100644 index 88aa7e97231..00000000000 --- a/pkg/webhook/staticpod/validating/staticpod_handler_test.go +++ /dev/null @@ -1,103 +0,0 @@ -/* -Copyright 2023 The OpenYurt 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 validating - -import ( - "reflect" - "testing" - - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/util/validation/field" - - appsv1alpha1 "github.com/openyurtio/openyurt/pkg/apis/apps/v1alpha1" -) - -func Test_validateStaticPodSpec(t *testing.T) { - - tests := []struct { - name string - spec *appsv1alpha1.StaticPodSpec - want field.ErrorList - }{ - { - "validate success", - &appsv1alpha1.StaticPodSpec{ - StaticPodNamespace: metav1.NamespaceDefault, - StaticPodName: "nginx", - StaticPodManifest: "nginx", - UpgradeStrategy: appsv1alpha1.StaticPodUpgradeStrategy{ - Type: appsv1alpha1.OTAStaticPodUpgradeStrategyType, - }, - }, - nil, - }, - { - "miss namespace", - &appsv1alpha1.StaticPodSpec{ - StaticPodName: "nginx", - StaticPodManifest: "nginx", - UpgradeStrategy: appsv1alpha1.StaticPodUpgradeStrategy{ - Type: appsv1alpha1.OTAStaticPodUpgradeStrategyType, - }, - }, - field.ErrorList{field.Required(field.NewPath("spec").Child("Namespace"), - "Namespace is required")}, - }, - { - "miss name", - &appsv1alpha1.StaticPodSpec{ - StaticPodNamespace: metav1.NamespaceDefault, - StaticPodManifest: "nginx", - UpgradeStrategy: appsv1alpha1.StaticPodUpgradeStrategy{ - Type: appsv1alpha1.OTAStaticPodUpgradeStrategyType, - }, - }, - field.ErrorList{field.Required(field.NewPath("spec").Child("StaticPodName"), - "StaticPodName is required")}, - }, - { - "miss manifest", - &appsv1alpha1.StaticPodSpec{ - StaticPodName: "nginx", - StaticPodNamespace: metav1.NamespaceDefault, - UpgradeStrategy: appsv1alpha1.StaticPodUpgradeStrategy{ - Type: appsv1alpha1.OTAStaticPodUpgradeStrategyType, - }, - }, - field.ErrorList{field.Required(field.NewPath("spec").Child("StaticPodManifest"), - "StaticPodManifest is required")}, - }, - { - "miss upgrade strategy unavailable", - &appsv1alpha1.StaticPodSpec{ - StaticPodName: "nginx", - StaticPodNamespace: metav1.NamespaceDefault, - StaticPodManifest: "nginx", - UpgradeStrategy: appsv1alpha1.StaticPodUpgradeStrategy{Type: appsv1alpha1.AutoStaticPodUpgradeStrategyType}, - }, - field.ErrorList{field.Required(field.NewPath("spec").Child("upgradeStrategy"), - "max-unavailable is required in auto mode")}, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - if got := validateStaticPodSpec(tt.spec); !reflect.DeepEqual(got, tt.want) { - t.Errorf("validateStaticPodSpec() = %v, want %v", got, tt.want) - } - }) - } -} diff --git a/pkg/webhook/staticpod/validating/webhooks.go b/pkg/webhook/staticpod/validating/webhooks.go deleted file mode 100644 index e42f717893e..00000000000 --- a/pkg/webhook/staticpod/validating/webhooks.go +++ /dev/null @@ -1,30 +0,0 @@ -/* -Copyright 2023 The OpenYurt 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 validating - -import ( - "sigs.k8s.io/controller-runtime/pkg/webhook/admission" -) - -// +kubebuilder:webhook:path=/validate-apps-openyurt-io-v1alpha1-staticpod,mutating=false,failurePolicy=fail,sideEffects=None,admissionReviewVersions=v1;v1beta1,groups=apps.openyurt.io,resources=staticpods,verbs=create;update,versions=v1alpha1,name=validate.apps.v1alpha1.staticpod.openyurt.io - -var ( - // HandlerMap contains admission webhook handlers - HandlerMap = map[string]admission.Handler{ - "validate-apps-openyurt-io-v1alpha1-staticpod": &StaticPodCreateUpdateHandler{}, - } -)