From 1576da07a4f26a4c605ddbbca50a9788fbe32aee Mon Sep 17 00:00:00 2001 From: ricky Date: Wed, 10 May 2023 15:40:51 +0800 Subject: [PATCH] feat: prevent node movement by label modification Signed-off-by: ricky --- .../yurt-manager-auto-generated.yaml | 20 +++++ pkg/webhook/node/v1/node_handler.go | 57 ++++++++++++++ pkg/webhook/node/v1/node_validation.go | 78 +++++++++++++++++++ pkg/webhook/server.go | 5 ++ .../util/controller/webhook_controller.go | 2 - 5 files changed, 160 insertions(+), 2 deletions(-) create mode 100644 pkg/webhook/node/v1/node_handler.go create mode 100644 pkg/webhook/node/v1/node_validation.go diff --git a/charts/yurt-manager/templates/yurt-manager-auto-generated.yaml b/charts/yurt-manager/templates/yurt-manager-auto-generated.yaml index 70435df84b4..a12797cc04a 100644 --- a/charts/yurt-manager/templates/yurt-manager-auto-generated.yaml +++ b/charts/yurt-manager/templates/yurt-manager-auto-generated.yaml @@ -520,6 +520,26 @@ webhooks: resources: - gateways sideEffects: None +- admissionReviewVersions: + - v1 + - v1beta1 + clientConfig: + service: + name: yurt-manager-webhook-service + namespace: {{ .Release.Namespace }} + path: /validate-core-openyurt-io-v1-node + failurePolicy: Fail + name: validate.core.v1.node.openyurt.io + rules: + - apiGroups: + - "" + apiVersions: + - v1 + operations: + - UPDATE + resources: + - nodes + sideEffects: None - admissionReviewVersions: - v1 clientConfig: diff --git a/pkg/webhook/node/v1/node_handler.go b/pkg/webhook/node/v1/node_handler.go new file mode 100644 index 00000000000..bb107988c50 --- /dev/null +++ b/pkg/webhook/node/v1/node_handler.go @@ -0,0 +1,57 @@ +/* +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 v1 + +import ( + v1 "k8s.io/api/core/v1" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/client/apiutil" + + "github.com/openyurtio/openyurt/pkg/webhook/builder" + "github.com/openyurtio/openyurt/pkg/webhook/util" +) + +const ( + WebhookName = "node" +) + +// SetupWebhookWithManager sets up Cluster webhooks. mutate path, validatepath, error +func (webhook *NodeHandler) SetupWebhookWithManager(mgr ctrl.Manager) (string, string, error) { + // init + webhook.Client = mgr.GetClient() + + gvk, err := apiutil.GVKForObject(&v1.Node{}, mgr.GetScheme()) + if err != nil { + return "", "", err + } + return util.GenerateMutatePath(gvk), + util.GenerateValidatePath(gvk), + builder.WebhookManagedBy(mgr). + For(&v1.Node{}). + WithValidator(webhook). + Complete() +} + +// +kubebuilder:webhook:path=/validate-core-openyurt-io-v1-node,mutating=false,failurePolicy=fail,sideEffects=None,admissionReviewVersions=v1;v1beta1,groups="",resources=nodes,verbs=update,versions=v1,name=validate.core.v1.node.openyurt.io + +// Cluster implements a validating and defaulting webhook for Cluster. +type NodeHandler struct { + Client client.Client +} + +var _ builder.CustomValidator = &NodeHandler{} diff --git a/pkg/webhook/node/v1/node_validation.go b/pkg/webhook/node/v1/node_validation.go new file mode 100644 index 00000000000..cfc6a0cba61 --- /dev/null +++ b/pkg/webhook/node/v1/node_validation.go @@ -0,0 +1,78 @@ +/* +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 v1 + +import ( + "context" + "fmt" + + v1 "k8s.io/api/core/v1" + apierrors "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/util/validation/field" + "sigs.k8s.io/controller-runtime/pkg/webhook/admission" + + "github.com/openyurtio/openyurt/pkg/apis/apps" +) + +// ValidateCreate implements webhook.CustomValidator so a webhook will be registered for the type. +func (webhook *NodeHandler) ValidateCreate(_ context.Context, obj runtime.Object, req admission.Request) error { + return nil +} + +// ValidateUpdate implements webhook.CustomValidator so a webhook will be registered for the type. +func (webhook *NodeHandler) ValidateUpdate(ctx context.Context, oldObj, newObj runtime.Object, req admission.Request) error { + newNode, ok := newObj.(*v1.Node) + if !ok { + return apierrors.NewBadRequest(fmt.Sprintf("expected a Node but got a %T", newObj)) + } + oldNode, ok := oldObj.(*v1.Node) + if !ok { + return apierrors.NewBadRequest(fmt.Sprintf("expected a Node} but got a %T", oldObj)) + } + + if allErrs := validateNodeUpdate(newNode, oldNode, req); len(allErrs) > 0 { + return apierrors.NewInvalid(v1.SchemeGroupVersion.WithKind("Node").GroupKind(), newNode.Name, allErrs) + } + + return nil +} + +// ValidateDelete implements webhook.CustomValidator so a webhook will be registered for the type. +func (webhook *NodeHandler) ValidateDelete(_ context.Context, obj runtime.Object, req admission.Request) error { + return nil +} + +func validateNodeUpdate(newNode, oldNode *v1.Node, req admission.Request) field.ErrorList { + oldNp := oldNode.Labels[apps.LabelDesiredNodePool] + newNp := newNode.Labels[apps.LabelDesiredNodePool] + + if len(oldNp) == 0 { + return nil + } + + // can not change LabelDesiredNodePool if it has been set + if oldNp != newNp { + return field.ErrorList([]*field.Error{ + field.Forbidden( + field.NewPath("metadata").Child("labels").Child(apps.LabelDesiredNodePool), + "apps.openyurt.io/desired-nodepool can not be changed"), + }) + } + + return nil +} diff --git a/pkg/webhook/server.go b/pkg/webhook/server.go index 74a83b8e005..fc1ddc2975c 100644 --- a/pkg/webhook/server.go +++ b/pkg/webhook/server.go @@ -33,6 +33,7 @@ import ( "github.com/openyurtio/openyurt/pkg/controller/yurtappset" "github.com/openyurtio/openyurt/pkg/controller/yurtstaticset" v1alpha1gateway "github.com/openyurtio/openyurt/pkg/webhook/gateway/v1alpha1" + v1node "github.com/openyurtio/openyurt/pkg/webhook/node/v1" v1alpha1nodepool "github.com/openyurtio/openyurt/pkg/webhook/nodepool/v1alpha1" v1beta1nodepool "github.com/openyurtio/openyurt/pkg/webhook/nodepool/v1beta1" v1pod "github.com/openyurtio/openyurt/pkg/webhook/pod/v1" @@ -77,6 +78,7 @@ func init() { addControllerWebhook(yurtappdaemon.ControllerName, &v1alpha1yurtappdaemon.YurtAppDaemonHandler{}) independentWebhooks[v1pod.WebhookName] = &v1pod.PodHandler{} + independentWebhooks[v1node.WebhookName] = &v1node.NodeHandler{} } // Note !!! @kadisi @@ -103,6 +105,9 @@ func SetupWithManager(c *config.CompletedConfig, mgr manager.Manager) error { return nil } + // set up webhook namespace + util.SetNamespace(c.ComponentConfig.Generic.WorkingNamespace) + // set up independent webhooks for name, s := range independentWebhooks { if util.IsWebhookDisabled(name, c.ComponentConfig.Generic.DisabledWebhooks) { diff --git a/pkg/webhook/util/controller/webhook_controller.go b/pkg/webhook/util/controller/webhook_controller.go index 895ddaf7aa3..fddc3264186 100644 --- a/pkg/webhook/util/controller/webhook_controller.go +++ b/pkg/webhook/util/controller/webhook_controller.go @@ -70,8 +70,6 @@ type Controller struct { } func New(handlers map[string]struct{}, cc *config.CompletedConfig) (*Controller, error) { - webhookutil.SetNamespace(cc.ComponentConfig.Generic.WorkingNamespace) - c := &Controller{ kubeClient: extclient.GetGenericClientWithName("webhook-controller").KubeClient, handlers: handlers,