From 963c2475c7d49b94149c6891dec592116f1006ee Mon Sep 17 00:00:00 2001 From: wangxye Date: Sat, 4 May 2024 16:39:23 +0800 Subject: [PATCH] feat: add sync poolservice and webhook Signed-off-by: wangxye --- .../yurt-manager-auto-generated.yaml | 21 ++ cmd/yurt-manager/app/options/options.go | 7 + .../app/options/vipbalancercontroller.go | 64 ++++ pkg/yurtmanager/controller/controller.go | 2 + .../viploadbalancer/config/types.go | 1 + .../viploadbalancer/predicate.go | 10 +- .../viploadbalancer/predicate_test.go | 72 +++++ .../loadbalancerset/viploadbalancer/util.go | 24 +- .../viploadbalancer/util_test.go | 75 +++++ .../viploadbalancer/version.go | 35 -- .../viploadbalancer_controller.go | 151 +++++++-- .../viploadbalancer_controller_test.go | 306 ++++++++++++++++++ .../v1alpha1/poolservice_default.go | 46 +++ .../v1alpha1/poolservice_default_test.go | 93 ++++++ .../v1alpha1/poolservice_handler.go | 63 ++++ pkg/yurtmanager/webhook/server.go | 2 + 16 files changed, 889 insertions(+), 83 deletions(-) create mode 100644 cmd/yurt-manager/app/options/vipbalancercontroller.go create mode 100644 pkg/yurtmanager/controller/loadbalancerset/viploadbalancer/predicate_test.go create mode 100644 pkg/yurtmanager/controller/loadbalancerset/viploadbalancer/util_test.go delete mode 100644 pkg/yurtmanager/controller/loadbalancerset/viploadbalancer/version.go create mode 100644 pkg/yurtmanager/controller/loadbalancerset/viploadbalancer/viploadbalancer_controller_test.go create mode 100644 pkg/yurtmanager/webhook/poolservice/v1alpha1/poolservice_default.go create mode 100644 pkg/yurtmanager/webhook/poolservice/v1alpha1/poolservice_default_test.go create mode 100644 pkg/yurtmanager/webhook/poolservice/v1alpha1/poolservice_handler.go diff --git a/charts/yurt-manager/templates/yurt-manager-auto-generated.yaml b/charts/yurt-manager/templates/yurt-manager-auto-generated.yaml index 3fe9f17374d..d474dc84416 100644 --- a/charts/yurt-manager/templates/yurt-manager-auto-generated.yaml +++ b/charts/yurt-manager/templates/yurt-manager-auto-generated.yaml @@ -672,6 +672,27 @@ webhooks: resources: - pods sideEffects: None +- admissionReviewVersions: + - v1 + - v1beta1 + clientConfig: + service: + name: yurt-manager-webhook-service + namespace: {{ .Release.Namespace }} + path: /mutate-network-openyurt-io-poolservice + failurePolicy: Fail + name: mutate.network.v1alpha1.poolservice.openyurt.io + rules: + - apiGroups: + - network.openyurt.io + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - poolservices + sideEffects: None - admissionReviewVersions: - v1 - v1beta1 diff --git a/cmd/yurt-manager/app/options/options.go b/cmd/yurt-manager/app/options/options.go index 932fe76e030..545949fbe16 100644 --- a/cmd/yurt-manager/app/options/options.go +++ b/cmd/yurt-manager/app/options/options.go @@ -40,6 +40,7 @@ type YurtManagerOptions struct { NodeLifeCycleController *NodeLifecycleControllerOptions NodeBucketController *NodeBucketControllerOptions LoadBalancerSetController *LoadBalancerSetControllerOptions + VipLoadBalancerController *VipLoadBalancerControllerOptions } // NewYurtManagerOptions creates a new YurtManagerOptions with a default config. @@ -61,6 +62,7 @@ func NewYurtManagerOptions() (*YurtManagerOptions, error) { NodeLifeCycleController: NewNodeLifecycleControllerOptions(), NodeBucketController: NewNodeBucketControllerOptions(), LoadBalancerSetController: NewLoadBalancerSetControllerOptions(), + VipLoadBalancerController: NewVipLoadBalancerControllerOptions(), } return &s, nil @@ -82,6 +84,7 @@ func (y *YurtManagerOptions) Flags(allControllers, disabledByDefaultControllers y.NodeLifeCycleController.AddFlags(fss.FlagSet("nodelifecycle controller")) y.NodeBucketController.AddFlags(fss.FlagSet("nodebucket controller")) y.LoadBalancerSetController.AddFlags(fss.FlagSet("loadbalancerset controller")) + y.VipLoadBalancerController.AddFlags(fss.FlagSet("viploadbalancer controller")) return fss } @@ -102,6 +105,7 @@ func (y *YurtManagerOptions) Validate(allControllers []string, controllerAliases errs = append(errs, y.NodeLifeCycleController.Validate()...) errs = append(errs, y.NodeBucketController.Validate()...) errs = append(errs, y.LoadBalancerSetController.Validate()...) + errs = append(errs, y.VipLoadBalancerController.Validate()...) return utilerrors.NewAggregate(errs) } @@ -149,6 +153,9 @@ func (y *YurtManagerOptions) ApplyTo(c *config.Config, controllerAliases map[str if err := y.LoadBalancerSetController.ApplyTo(&c.ComponentConfig.LoadBalancerSetController); err != nil { return err } + if err := y.VipLoadBalancerController.ApplyTo(&c.ComponentConfig.VipLoadBalancerController); err != nil { + return err + } return nil } diff --git a/cmd/yurt-manager/app/options/vipbalancercontroller.go b/cmd/yurt-manager/app/options/vipbalancercontroller.go new file mode 100644 index 00000000000..3ee78be8ab4 --- /dev/null +++ b/cmd/yurt-manager/app/options/vipbalancercontroller.go @@ -0,0 +1,64 @@ +/* +Copyright 2024 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/spf13/pflag" + + "github.com/openyurtio/openyurt/pkg/yurtmanager/controller/loadbalancerset/viploadbalancer/config" +) + +type VipLoadBalancerControllerOptions struct { + *config.VipLoadBalancerControllerConfiguration +} + +func NewVipLoadBalancerControllerOptions() *VipLoadBalancerControllerOptions { + return &VipLoadBalancerControllerOptions{ + &config.VipLoadBalancerControllerConfiguration{ + ConcurrentLoadBalancerSetWorkers: 3, + }, + } +} + +// AddFlags adds flags related to poolservice for yurt-manager to the specified FlagSet. +func (n *VipLoadBalancerControllerOptions) AddFlags(fs *pflag.FlagSet) { + if n == nil { + return + } + + fs.Int32Var(&n.ConcurrentLoadBalancerSetWorkers, "concurrent-load-balancer-set-workers", n.ConcurrentLoadBalancerSetWorkers, "The number of load-balancer-set service objects that are allowed to reconcile concurrently. Larger number = more responsive load-balancer-set services, but more CPU (and network) load") +} + +// ApplyTo fills up poolservice config with options. +func (o *VipLoadBalancerControllerOptions) ApplyTo(cfg *config.VipLoadBalancerControllerConfiguration) error { + if o == nil { + return nil + } + + cfg.ConcurrentLoadBalancerSetWorkers = o.ConcurrentLoadBalancerSetWorkers + + return nil +} + +// Validate checks validation of VipLoadBalancerControllerOptions. +func (o *VipLoadBalancerControllerOptions) Validate() []error { + if o == nil { + return nil + } + errs := []error{} + return errs +} diff --git a/pkg/yurtmanager/controller/controller.go b/pkg/yurtmanager/controller/controller.go index a6cb06f999b..9fd09b5931e 100644 --- a/pkg/yurtmanager/controller/controller.go +++ b/pkg/yurtmanager/controller/controller.go @@ -33,6 +33,7 @@ import ( "github.com/openyurtio/openyurt/pkg/yurtmanager/controller/csrapprover" "github.com/openyurtio/openyurt/pkg/yurtmanager/controller/daemonpodupdater" "github.com/openyurtio/openyurt/pkg/yurtmanager/controller/loadbalancerset/loadbalancerset" + "github.com/openyurtio/openyurt/pkg/yurtmanager/controller/loadbalancerset/viploadbalancer" "github.com/openyurtio/openyurt/pkg/yurtmanager/controller/nodebucket" "github.com/openyurtio/openyurt/pkg/yurtmanager/controller/nodelifecycle" "github.com/openyurtio/openyurt/pkg/yurtmanager/controller/nodepool" @@ -99,6 +100,7 @@ func NewControllerInitializers() map[string]InitFunc { register(names.NodeLifeCycleController, nodelifecycle.Add) register(names.NodeBucketController, nodebucket.Add) register(names.LoadBalancerSetController, loadbalancerset.Add) + register(names.VipLoadBalancerController, viploadbalancer.Add) return controllers } diff --git a/pkg/yurtmanager/controller/loadbalancerset/viploadbalancer/config/types.go b/pkg/yurtmanager/controller/loadbalancerset/viploadbalancer/config/types.go index 73f4cc25516..06e62af3758 100644 --- a/pkg/yurtmanager/controller/loadbalancerset/viploadbalancer/config/types.go +++ b/pkg/yurtmanager/controller/loadbalancerset/viploadbalancer/config/types.go @@ -18,4 +18,5 @@ package config // VipLoadBalancerControllerConfiguration contains elements describing EdgeLoadBalanceController. type VipLoadBalancerControllerConfiguration struct { + ConcurrentLoadBalancerSetWorkers int32 } diff --git a/pkg/yurtmanager/controller/loadbalancerset/viploadbalancer/predicate.go b/pkg/yurtmanager/controller/loadbalancerset/viploadbalancer/predicate.go index 606940efeea..892b3254010 100644 --- a/pkg/yurtmanager/controller/loadbalancerset/viploadbalancer/predicate.go +++ b/pkg/yurtmanager/controller/loadbalancerset/viploadbalancer/predicate.go @@ -63,10 +63,6 @@ func NewPoolServicePredicated() predicate.Predicate { } func predicatedPoolService(ps *v1alpha1.PoolService) bool { - if ps.Labels == nil { - return false - } - return matchLoadBalancerClass(ps) } @@ -75,9 +71,9 @@ func matchLoadBalancerClass(ps *v1alpha1.PoolService) bool { return false } - if *ps.Spec.LoadBalancerClass == VipLoadBalancerClass { - return true + if *ps.Spec.LoadBalancerClass != VipLoadBalancerClass { + return false } - return false + return true } diff --git a/pkg/yurtmanager/controller/loadbalancerset/viploadbalancer/predicate_test.go b/pkg/yurtmanager/controller/loadbalancerset/viploadbalancer/predicate_test.go new file mode 100644 index 00000000000..d4f2833a290 --- /dev/null +++ b/pkg/yurtmanager/controller/loadbalancerset/viploadbalancer/predicate_test.go @@ -0,0 +1,72 @@ +/* +Copyright 2024 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 viploadbalancer_test + +import ( + "testing" + + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "sigs.k8s.io/controller-runtime/pkg/event" + + vip "github.com/openyurtio/openyurt/pkg/yurtmanager/controller/loadbalancerset/viploadbalancer" +) + +var ( + elbClass = "elb" +) + +func TestPoolServicePredicated(t *testing.T) { + f := vip.NewPoolServicePredicated() + + t.Run("create/delete/update/generic pool service has not LoadBalancerClass", func(t *testing.T) { + ps := newPoolService(v1.NamespaceDefault, "np123", nil, nil) + ps.Spec.LoadBalancerClass = nil + + assertBool(t, false, f.Create(event.CreateEvent{Object: ps})) + assertBool(t, false, f.Update(event.UpdateEvent{ObjectOld: ps, ObjectNew: ps})) + assertBool(t, false, f.Delete(event.DeleteEvent{Object: ps})) + assertBool(t, false, f.Generic(event.GenericEvent{Object: ps})) + }) + + t.Run("create/delete/update/generic pool service LoadBalancerClass is not valid", func(t *testing.T) { + ps := newPoolService(v1.NamespaceDefault, "np123", nil, nil) + ps.Spec.LoadBalancerClass = &elbClass + + assertBool(t, false, f.Create(event.CreateEvent{Object: ps})) + assertBool(t, false, f.Update(event.UpdateEvent{ObjectOld: ps, ObjectNew: ps})) + assertBool(t, false, f.Delete(event.DeleteEvent{Object: ps})) + assertBool(t, false, f.Generic(event.GenericEvent{Object: ps})) + }) + + t.Run("create/delete/update/generic pool service", func(t *testing.T) { + ps := newPoolService(v1.NamespaceDefault, "np123", nil, nil) + + assertBool(t, true, f.Create(event.CreateEvent{Object: ps})) + assertBool(t, true, f.Update(event.UpdateEvent{ObjectOld: ps, ObjectNew: ps})) + assertBool(t, true, f.Delete(event.DeleteEvent{Object: ps})) + assertBool(t, true, f.Generic(event.GenericEvent{Object: ps})) + + }) +} + +func assertBool(t testing.TB, expected, got bool) { + t.Helper() + + if expected != got { + t.Errorf("expected %v, but got %v", expected, got) + } +} diff --git a/pkg/yurtmanager/controller/loadbalancerset/viploadbalancer/util.go b/pkg/yurtmanager/controller/loadbalancerset/viploadbalancer/util.go index 6e9e0b9015a..c7e91a096cc 100644 --- a/pkg/yurtmanager/controller/loadbalancerset/viploadbalancer/util.go +++ b/pkg/yurtmanager/controller/loadbalancerset/viploadbalancer/util.go @@ -18,6 +18,7 @@ package viploadbalancer import "sync" const ( + MINVRIDLIMIT = 0 MAXVRIDLIMIT = 255 EVICTED = -1 ) @@ -43,7 +44,7 @@ func (vm *VRIDManager) GetVRID(key string) int { vm.vridMap[key] = make(map[int]bool) } - for vrid := 1; vrid <= vm.maxVRID; vrid++ { + for vrid := 0; vrid < vm.maxVRID; vrid++ { if !vm.vridMap[key][vrid] { vm.vridMap[key][vrid] = true return vrid @@ -53,12 +54,6 @@ func (vm *VRIDManager) GetVRID(key string) int { return EVICTED } -func (vm *VRIDManager) isEmpty(key string) bool { - vm.mutex.Lock() - defer vm.mutex.Unlock() - return len(vm.vridMap[key]) == 0 -} - func (vm *VRIDManager) ReleaseVRID(key string, vrid int) { vm.mutex.Lock() defer vm.mutex.Unlock() @@ -70,7 +65,7 @@ func (vm *VRIDManager) ReleaseVRID(key string, vrid int) { delete(vm.vridMap[key], vrid) } -func (vm *VRIDManager) isValid(key string, vrid int) bool { +func (vm *VRIDManager) IsValid(key string, vrid int) bool { vm.mutex.Lock() defer vm.mutex.Unlock() @@ -84,3 +79,16 @@ func (vm *VRIDManager) isValid(key string, vrid int) bool { return true } + +func (vm *VRIDManager) SyncVRID(key string, vird []int) { + vm.mutex.Lock() + defer vm.mutex.Unlock() + + if _, ok := vm.vridMap[key]; !ok { + vm.vridMap[key] = make(map[int]bool) + } + + for _, v := range vird { + vm.vridMap[key][v] = true + } +} diff --git a/pkg/yurtmanager/controller/loadbalancerset/viploadbalancer/util_test.go b/pkg/yurtmanager/controller/loadbalancerset/viploadbalancer/util_test.go new file mode 100644 index 00000000000..9a8250f14f9 --- /dev/null +++ b/pkg/yurtmanager/controller/loadbalancerset/viploadbalancer/util_test.go @@ -0,0 +1,75 @@ +/* +Copyright 2024 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 viploadbalancer_test + +import ( + "testing" + + vip "github.com/openyurtio/openyurt/pkg/yurtmanager/controller/loadbalancerset/viploadbalancer" +) + +var ( + nodepool = "np123" +) + +func TestVRIDManage(t *testing.T) { + vm := vip.NewVRIDManager() + + t.Run("get vrid", func(t *testing.T) { + // Test getting VRID + vrid := vm.GetVRID(nodepool) + if vrid != 0 { + t.Errorf("Expected VRID 0, but got %d", vrid) + } + }) + + t.Run("release vrid", func(t *testing.T) { + // Test releasing VRID + target := vm.GetVRID(nodepool) + vm.ReleaseVRID(nodepool, target) + vrid := vm.GetVRID(nodepool) + if vrid != target { + t.Errorf("Expected VRID %d after release, but got %d", target, vrid) + } + }) + + t.Run("vrid is valid", func(t *testing.T) { + // Test isValid function + isValid := vm.IsValid(nodepool, 0) + if !isValid { + t.Errorf("Expected VRID 0 to be valid, but it's not") + } + }) + + t.Run("vrid is not valid", func(t *testing.T) { + // Test isValid function + isValid := vm.IsValid(nodepool, -1) + if isValid { + t.Errorf("Expected VRID -1 to be invalid, but it's not") + } + }) + + t.Run("sync vrid", func(t *testing.T) { + // Test SyncVRID function + vm.SyncVRID(nodepool, []int{1, 2, 3}) + target := 4 + vrid := vm.GetVRID(nodepool) + if vrid != target { + t.Errorf("Expected VRID %d after sync, but got %d", target, vrid) + } + }) +} diff --git a/pkg/yurtmanager/controller/loadbalancerset/viploadbalancer/version.go b/pkg/yurtmanager/controller/loadbalancerset/viploadbalancer/version.go deleted file mode 100644 index c9775cc29c1..00000000000 --- a/pkg/yurtmanager/controller/loadbalancerset/viploadbalancer/version.go +++ /dev/null @@ -1,35 +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 viploadbalancer - -import ( - netv1alpha1 "github.com/openyurtio/openyurt/pkg/apis/network/v1alpha1" -) - -const VipAgentName = "yurt-lb-agent" -const VipAgentImage = "openyurt/yurt-lb-agent" -const VipAgentControlPlane = "vip-loadbalance-controller" - -func DefaultVersion(poolService netv1alpha1.PoolService) (string, string, error) { - var ( - ver string - ns string - ) - ver = "latest" - ns = poolService.Namespace - return ver, ns, nil -} diff --git a/pkg/yurtmanager/controller/loadbalancerset/viploadbalancer/viploadbalancer_controller.go b/pkg/yurtmanager/controller/loadbalancerset/viploadbalancer/viploadbalancer_controller.go index 1716bf8a632..15dbf38071e 100644 --- a/pkg/yurtmanager/controller/loadbalancerset/viploadbalancer/viploadbalancer_controller.go +++ b/pkg/yurtmanager/controller/loadbalancerset/viploadbalancer/viploadbalancer_controller.go @@ -18,11 +18,13 @@ package viploadbalancer import ( "context" - "flag" "fmt" "strconv" "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/api/meta" + "k8s.io/apimachinery/pkg/fields" + "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/runtime" "k8s.io/client-go/tools/record" "k8s.io/klog/v2" @@ -40,15 +42,16 @@ import ( "github.com/openyurtio/openyurt/pkg/yurtmanager/controller/loadbalancerset/viploadbalancer/config" ) -func init() { - flag.IntVar(&concurrentReconciles, "edgeloadbalace-workers", concurrentReconciles, "Max concurrent workers for edgeloadbalace controller.") -} - var ( - concurrentReconciles = 3 - controllerKind = netv1alpha1.SchemeGroupVersion.WithKind("VipLoadBalancer") - VipLoadBalancerVRIDLabel = "service.openyurt.io/vrid" - VipLoadBalancerClass = "service.openyurt.io/viplb" + poolServicesControllerResource = netv1alpha1.SchemeGroupVersion.WithResource("poolservices") +) + +const ( + AnnotationVipLoadBalancerVRID = "service.openyurt.io/vrid" + VipLoadBalancerClass = "service.openyurt.io/viplb" + + AnnotationServiceTopologyKey = "openyurt.io/topologyKeys" + AnnotationServiceTopologyValueNodePool = "openyurt.io/nodepool" ) func Format(format string, args ...interface{}) string { @@ -59,8 +62,14 @@ func Format(format string, args ...interface{}) string { // Add creates a new EdgeLoadBalace 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(ctx context.Context, c *appconfig.CompletedConfig, mgr manager.Manager) error { - klog.Infof(Format("viploadbalacer-controller add controller %s", controllerKind.String())) - return add(mgr, newReconciler(c, mgr)) + klog.Infof(Format("viploadbalacer-controller add controller %s", poolServicesControllerResource.String())) + r := newReconciler(c, mgr) + + if _, err := r.mapper.KindFor(poolServicesControllerResource); err != nil { + return fmt.Errorf("resource %s isn't exist", poolServicesControllerResource.String()) + } + + return add(mgr, c, r) } var _ reconcile.Reconciler = &ReconcileVipLoadBalancer{} @@ -68,17 +77,20 @@ var _ reconcile.Reconciler = &ReconcileVipLoadBalancer{} // ReconcileVipLoadBalancer reconciles service, endpointslice and PoolService object type ReconcileVipLoadBalancer struct { client.Client - scheme *runtime.Scheme - recorder record.EventRecorder + scheme *runtime.Scheme + recorder record.EventRecorder + mapper meta.RESTMapper + Configration config.VipLoadBalancerControllerConfiguration VRIDManager *VRIDManager } // newReconciler returns a new reconcile.Reconciler -func newReconciler(c *appconfig.CompletedConfig, mgr manager.Manager) reconcile.Reconciler { +func newReconciler(c *appconfig.CompletedConfig, mgr manager.Manager) *ReconcileVipLoadBalancer { return &ReconcileVipLoadBalancer{ Client: mgr.GetClient(), scheme: mgr.GetScheme(), + mapper: mgr.GetRESTMapper(), recorder: mgr.GetEventRecorderFor(names.VipLoadBalancerController), Configration: c.ComponentConfig.VipLoadBalancerController, VRIDManager: NewVRIDManager(), @@ -86,10 +98,10 @@ func newReconciler(c *appconfig.CompletedConfig, mgr manager.Manager) reconcile. } // add adds a new Controller to mgr with r as the reconcile.Reconciler -func add(mgr manager.Manager, r reconcile.Reconciler) error { +func add(mgr manager.Manager, cfg *appconfig.CompletedConfig, r reconcile.Reconciler) error { // Create a new controller c, err := controller.New(names.VipLoadBalancerController, mgr, controller.Options{ - Reconciler: r, MaxConcurrentReconciles: concurrentReconciles, + Reconciler: r, MaxConcurrentReconciles: int(cfg.ComponentConfig.VipLoadBalancerController.ConcurrentLoadBalancerSetWorkers), }) if err != nil { return err @@ -104,9 +116,7 @@ func add(mgr manager.Manager, r reconcile.Reconciler) error { return nil } -// +kubebuilder:rbac:groups=net.openyurt.io,resources=poolservices,verbs=get;list;watch;create;update;patch;delete -// +kubebuilder:rbac:groups=core,resources=services,verbs=get;list;watch -// +kubebuilder:rbac:groups=discovery.k8s.io,resources=endpointslices,verbs=get;list;watch;patch +// +kubebuilder:rbac:groups=network.openyurt.io,resources=poolservices,verbs=get;list;watch;create;update;patch;delete // Reconcile reads that state of the cluster for a PoolService object and makes changes based on the state read func (r *ReconcileVipLoadBalancer) Reconcile(ctx context.Context, request reconcile.Request) (reconcile.Result, error) { @@ -117,7 +127,7 @@ func (r *ReconcileVipLoadBalancer) Reconcile(ctx context.Context, request reconc err := r.Get(context.TODO(), request.NamespacedName, poolService) if err != nil { if errors.IsNotFound(err) { - return reconcile.Result{}, err + return reconcile.Result{}, nil } return reconcile.Result{}, err } @@ -136,42 +146,109 @@ func (r *ReconcileVipLoadBalancer) Reconcile(ctx context.Context, request reconc } func (r *ReconcileVipLoadBalancer) syncPoolServices(ctx context.Context, poolService *netv1alpha1.PoolService) error { - // Create a new PoolService if not exist - klog.V(4).Infof(Format("ReconcilCreate VipLoadBalancer %s/%s", poolService.Namespace, poolService.Name)) - + klog.V(4).Infof(Format("SyncPoolServices VipLoadBalancer %s/%s", poolService.Namespace, poolService.Name)) // if not exist, create a new VRID - if err := r.handleVRID(ctx, poolService); err != nil { + if err := r.handleVRID(poolService); err != nil { return err } - // Create a new PoolService + // Update the PoolService if err := r.Update(ctx, poolService); err != nil { klog.Errorf(Format("Failed to create PoolService %s/%s: %v", poolService.Namespace, poolService.Name, err)) return err } + // sync VRID from the poolservice + if err := r.syncVRID(ctx, poolService); err != nil { + klog.Errorf(Format("Failed to sync VRID on Pool Service %s/%s: %v", poolService.Namespace, poolService.Name, err)) + return err + } + return nil } +func (r *ReconcileVipLoadBalancer) syncVRID(ctx context.Context, poolService *netv1alpha1.PoolService) error { + currentVRIDs, err := r.getCurrentVRID(ctx, poolService) + if err != nil { + return fmt.Errorf("failed to get current PoolServices: %v", err) + } + + r.VRIDManager.SyncVRID(poolService.Labels[network.LabelNodePoolName], currentVRIDs) + return nil +} + +func (r *ReconcileVipLoadBalancer) getCurrentVRID(ctx context.Context, poolService *netv1alpha1.PoolService) ([]int, error) { + // Get the poolservice list + listSelector := &client.ListOptions{ + LabelSelector: labels.SelectorFromSet(map[string]string{ + network.LabelNodePoolName: poolService.Labels[network.LabelNodePoolName], + }), + FieldSelector: fields.SelectorFromSet(map[string]string{ + "spec.loadBalancerClass": VipLoadBalancerClass, + }), + } + poolServiceList := &netv1alpha1.PoolServiceList{} + if err := r.List(ctx, poolServiceList, listSelector); err != nil { + return nil, err + } + + return filterInvalidPoolService(poolServiceList.Items), nil +} + +func filterInvalidPoolService(poolServices []netv1alpha1.PoolService) []int { + poolVrids := []int{} + for _, poolService := range poolServices { + if poolService.Labels == nil { + continue + } + + if _, ok := poolService.Annotations[AnnotationVipLoadBalancerVRID]; !ok { + continue + } + + vrid, err := strconv.Atoi(poolService.Annotations[AnnotationVipLoadBalancerVRID]) + if err != nil { + klog.Errorf(Format("Failed to convert VRID to int: %v", err)) + continue + } + + if vrid < MINVRIDLIMIT || vrid > MAXVRIDLIMIT { + klog.Errorf(Format("Invalid VRID %d", vrid)) + continue + } + + poolVrids = append(poolVrids, vrid) + } + + return poolVrids +} + func (r *ReconcileVipLoadBalancer) hasValidVRID(poolService netv1alpha1.PoolService) bool { - if poolService.Labels == nil { + if poolService.Annotations == nil { return false } - if _, ok := poolService.Labels[VipLoadBalancerVRIDLabel]; !ok { + if _, ok := poolService.Annotations[AnnotationVipLoadBalancerVRID]; !ok { return false } return true } -func (r *ReconcileVipLoadBalancer) handleVRID(_ context.Context, poolService *netv1alpha1.PoolService) error { +func (r *ReconcileVipLoadBalancer) handleVRID(poolService *netv1alpha1.PoolService) error { if r.hasValidVRID(*poolService) { return nil } - // TODO: sync VRID from the nodepool + // Assign a new VRID to the PoolService + if err := r.assignVRID(poolService); err != nil { + return err + } + + return nil +} +func (r *ReconcileVipLoadBalancer) assignVRID(poolService *netv1alpha1.PoolService) error { // Get the poolName from the PoolService poolName := poolService.Labels[network.LabelNodePoolName] // Get a new VRID @@ -181,7 +258,11 @@ func (r *ReconcileVipLoadBalancer) handleVRID(_ context.Context, poolService *ne } // Set the VRID to the PoolService - poolService.Labels[VipLoadBalancerVRIDLabel] = strconv.Itoa(vrid) + if poolService.Annotations == nil { + poolService.Annotations = make(map[string]string) + } + + poolService.Annotations[AnnotationVipLoadBalancerVRID] = strconv.Itoa(vrid) return nil } @@ -189,9 +270,13 @@ func (r *ReconcileVipLoadBalancer) reconcileDelete(ctx context.Context, poolServ klog.V(4).Infof(Format("ReconcilDelete VipLoadBalancer %s/%s", poolService.Namespace, poolService.Name)) poolName := poolService.Labels[network.LabelNodePoolName] - vrid, err := strconv.Atoi(poolService.Labels[VipLoadBalancerVRIDLabel]) - if err != nil || !r.VRIDManager.isValid(poolName, vrid) { - return reconcile.Result{}, fmt.Errorf("invalid VRID %d", vrid) + vrid, err := strconv.Atoi(poolService.Annotations[AnnotationVipLoadBalancerVRID]) + if err != nil { + return reconcile.Result{}, fmt.Errorf("invalid VRID in poolservice: %v", err) + } + + if !r.VRIDManager.IsValid(poolName, vrid) { + return reconcile.Result{}, fmt.Errorf("VRID: %d is not in valid range", vrid) } r.VRIDManager.ReleaseVRID(poolName, vrid) // Delete the PoolService diff --git a/pkg/yurtmanager/controller/loadbalancerset/viploadbalancer/viploadbalancer_controller_test.go b/pkg/yurtmanager/controller/loadbalancerset/viploadbalancer/viploadbalancer_controller_test.go new file mode 100644 index 00000000000..5d8946df482 --- /dev/null +++ b/pkg/yurtmanager/controller/loadbalancerset/viploadbalancer/viploadbalancer_controller_test.go @@ -0,0 +1,306 @@ +/* +Copyright 2024 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 viploadbalancer_test + +import ( + "context" + "reflect" + "sort" + "strconv" + "strings" + "testing" + "time" + + corev1 "k8s.io/api/core/v1" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/types" + clientgoscheme "k8s.io/client-go/kubernetes/scheme" + fakeclient "sigs.k8s.io/controller-runtime/pkg/client/fake" + "sigs.k8s.io/controller-runtime/pkg/reconcile" + + "github.com/openyurtio/openyurt/pkg/apis" + "github.com/openyurtio/openyurt/pkg/apis/apps/v1beta1" + network "github.com/openyurtio/openyurt/pkg/apis/network" + "github.com/openyurtio/openyurt/pkg/apis/network/v1alpha1" + "github.com/openyurtio/openyurt/pkg/yurtmanager/controller/loadbalancerset/viploadbalancer" +) + +const ( + mockServiceName = "test" + mockPoolServiceName = "test-np123" + mockNodePoolLabel = "app=deploy" + mockServiceUid = "c0af506a-7096-4ef9-b39a-eac2feb5c07g" + mockNodePoolUid = "f47dd9db-d3bc-40f3-8d03-7409930b6289" + vridLable = "service.openyurt.io/vrid" +) + +var ( + viplbClass = "service.openyurt.io/viplb" +) + +func TestReconcileVipLoadBalancer_Reconcile(t *testing.T) { + scheme := initScheme(t) + + t.Run("test get poolservice not found", func(t *testing.T) { + rc := viploadbalancer.ReconcileVipLoadBalancer{ + Client: fakeclient.NewClientBuilder().WithScheme(scheme).WithObjects().Build(), + } + + _, err := rc.Reconcile(context.Background(), newReconcileRequest(v1.NamespaceDefault, mockServiceName)) + + assertErrNil(t, err) + }) + + t.Run("test update pool services", func(t *testing.T) { + svc := newService(v1.NamespaceDefault, mockServiceName) + poolsvc := newPoolService(v1.NamespaceDefault, "np123", nil, nil) + np1 := newNodepool("np123", "name=np123,app=deploy") + c := fakeclient.NewClientBuilder().WithScheme(scheme).WithObjects(svc).WithObjects(np1).WithObjects(poolsvc).Build() + + vridManage := viploadbalancer.NewVRIDManager() + rc := viploadbalancer.ReconcileVipLoadBalancer{ + Client: c, + VRIDManager: vridManage, + } + + _, err := rc.Reconcile(context.Background(), newReconcileRequest(v1.NamespaceDefault, mockPoolServiceName)) + + psl := &v1alpha1.PoolServiceList{} + c.List(context.Background(), psl) + + assertErrNil(t, err) + assertPoolServicesNameList(t, psl, []string{"test-np123"}) + assertPoolServicesLBClass(t, psl, svc.Spec.LoadBalancerClass) + assertPoolServiceLabels(t, psl, svc.Name) + assertPoolServiceVRIDLabels(t, psl, "0") + }) + + t.Run("test delete pool services with delete time not zero", func(t *testing.T) { + svc := newService(v1.NamespaceDefault, mockServiceName) + np1 := newNodepool("np123", "name=np123,app=deploy") + ps1 := newPoolService(v1.NamespaceDefault, "np123", nil, nil) + + vridManage := viploadbalancer.NewVRIDManager() + vrid := vridManage.GetVRID("np123") + + ps1.Annotations = map[string]string{viploadbalancer.AnnotationVipLoadBalancerVRID: strconv.Itoa(vrid)} + ps1.DeletionTimestamp = &v1.Time{Time: time.Now()} + + c := fakeclient.NewClientBuilder().WithScheme(scheme).WithObjects(svc).WithObjects(np1).WithObjects(ps1).Build() + + rc := viploadbalancer.ReconcileVipLoadBalancer{ + Client: c, + VRIDManager: vridManage, + } + + _, err := rc.Reconcile(context.Background(), newReconcileRequest(v1.NamespaceDefault, mockPoolServiceName)) + + psl := &v1alpha1.PoolServiceList{} + c.List(context.Background(), psl) + + assertErrNil(t, err) + assertPoolServiceNil(t, psl) + }) + +} + +func initScheme(t *testing.T) *runtime.Scheme { + scheme := runtime.NewScheme() + if err := clientgoscheme.AddToScheme(scheme); err != nil { + t.Fatal("Fail to add kubernetes clint-go custom resource") + } + apis.AddToScheme(scheme) + + return scheme +} + +func newReconcileRequest(namespace string, name string) reconcile.Request { + return reconcile.Request{ + NamespacedName: types.NamespacedName{ + Namespace: namespace, + Name: name, + }} +} + +func assertErrNil(t testing.TB, err error) { + t.Helper() + + if err != nil { + t.Errorf("expected err is nil, but got %v", err) + } +} + +func newService(namespace string, name string) *corev1.Service { + return &corev1.Service{ + TypeMeta: v1.TypeMeta{ + Kind: "Service", + APIVersion: "v1", + }, + ObjectMeta: v1.ObjectMeta{ + Annotations: map[string]string{ + network.AnnotationNodePoolSelector: mockNodePoolLabel, + }, + Namespace: namespace, + Name: name, + UID: mockServiceUid, + }, + Spec: corev1.ServiceSpec{ + Type: corev1.ServiceTypeLoadBalancer, + LoadBalancerClass: &viplbClass, + }, + } +} + +func newNodepool(name string, labelStr string) *v1beta1.NodePool { + var splitLabels []string + if labelStr != "" { + splitLabels = strings.Split(labelStr, ",") + } + + labels := make(map[string]string) + for _, s := range splitLabels { + kv := strings.Split(s, "=") + labels[kv[0]] = kv[1] + } + + return &v1beta1.NodePool{ + TypeMeta: v1.TypeMeta{ + Kind: "NodePool", + APIVersion: "apps.openyurt.io/v1beta1", + }, + ObjectMeta: v1.ObjectMeta{ + Labels: labels, + Name: name, + UID: mockNodePoolUid, + }, + } +} + +func assertPoolServiceNil(t testing.TB, psl *v1alpha1.PoolServiceList) { + t.Helper() + + if len(psl.Items) != 0 { + t.Errorf("expected pool service list is nil") + } +} + +func assertPoolServicesNameList(t testing.TB, psl *v1alpha1.PoolServiceList, expectedNameList []string) { + t.Helper() + + sort.Strings(expectedNameList) + + gotNameList := getPoolServicesNameList(t, psl) + + if !reflect.DeepEqual(expectedNameList, gotNameList) { + t.Errorf("expected name list %v, but got name list %v", expectedNameList, gotNameList) + } +} + +func getPoolServicesNameList(t testing.TB, psl *v1alpha1.PoolServiceList) []string { + t.Helper() + + var gotPoolServiceNameList []string + for _, item := range psl.Items { + gotPoolServiceNameList = append(gotPoolServiceNameList, item.Name) + } + sort.Strings(gotPoolServiceNameList) + + return gotPoolServiceNameList +} + +func assertPoolServicesLBClass(t testing.TB, psl *v1alpha1.PoolServiceList, lbClass *string) { + t.Helper() + + for _, ps := range psl.Items { + if *ps.Spec.LoadBalancerClass != *lbClass { + t.Errorf("expected loadbalancer class %s, but got %s", *lbClass, *ps.Spec.LoadBalancerClass) + } + } +} + +func assertPoolServiceLabels(t testing.TB, psl *v1alpha1.PoolServiceList, serviceName string) { + t.Helper() + + for _, ps := range psl.Items { + if ps.Labels == nil { + t.Errorf("expected labels not nil") + return + } + + if ps.Labels[network.LabelServiceName] != serviceName { + t.Errorf("expected pool service name is %s, but got %s", serviceName, ps.Labels[network.LabelServiceName]) + } + } +} + +func assertPoolServiceVRIDLabels(t testing.TB, psl *v1alpha1.PoolServiceList, vrid string) { + t.Helper() + + for _, ps := range psl.Items { + if ps.Annotations == nil { + t.Errorf("expected labels not nil") + return + } + + if ps.Annotations[vridLable] != vrid { + t.Errorf("expected pool service name is %s, but got %s", vrid, ps.Labels[vridLable]) + } + } +} + +func newPoolService(namespace string, poolName string, annotations map[string]string, lbIngress []corev1.LoadBalancerIngress) *v1alpha1.PoolService { + blockOwnerDeletion := true + controller := true + return &v1alpha1.PoolService{ + TypeMeta: v1.TypeMeta{ + Kind: "PoolService", + APIVersion: v1alpha1.GroupVersion.String(), + }, + ObjectMeta: v1.ObjectMeta{ + Namespace: namespace, + Name: mockServiceName + "-" + poolName, + Labels: map[string]string{network.LabelServiceName: mockServiceName, network.LabelNodePoolName: poolName}, + Annotations: annotations, + OwnerReferences: []v1.OwnerReference{ + { + APIVersion: "v1", + BlockOwnerDeletion: &blockOwnerDeletion, + Controller: &controller, + Kind: "Service", + Name: mockServiceName, + UID: mockServiceUid, + }, { + APIVersion: "apps.openyurt.io/v1alpha1", + BlockOwnerDeletion: &blockOwnerDeletion, + Controller: &controller, + Kind: "NodePool", + Name: poolName, + UID: mockNodePoolUid, + }, + }, + }, + Spec: v1alpha1.PoolServiceSpec{ + LoadBalancerClass: &viplbClass, + }, + Status: v1alpha1.PoolServiceStatus{ + LoadBalancer: corev1.LoadBalancerStatus{ + Ingress: lbIngress, + }, + }, + } +} diff --git a/pkg/yurtmanager/webhook/poolservice/v1alpha1/poolservice_default.go b/pkg/yurtmanager/webhook/poolservice/v1alpha1/poolservice_default.go new file mode 100644 index 00000000000..7d179943bdc --- /dev/null +++ b/pkg/yurtmanager/webhook/poolservice/v1alpha1/poolservice_default.go @@ -0,0 +1,46 @@ +/* +Copyright 2024 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" + + "github.com/openyurtio/openyurt/pkg/apis/network/v1alpha1" + viplb "github.com/openyurtio/openyurt/pkg/yurtmanager/controller/loadbalancerset/viploadbalancer" +) + +// Default satisfies the defaulting webhook interface. +func (webhook *PoolServiceHandler) Default(ctx context.Context, obj runtime.Object) error { + ps, ok := obj.(*v1alpha1.PoolService) + if !ok { + return apierrors.NewBadRequest(fmt.Sprintf("expected a PoolService but got a %T", obj)) + } + + v1alpha1.SetDefaultsPoolService(ps) + + if ps.Spec.LoadBalancerClass != nil { + if *ps.Spec.LoadBalancerClass == viplb.VipLoadBalancerClass { + if ps.Annotations == nil { + ps.Annotations = make(map[string]string) + } + ps.Annotations[viplb.AnnotationServiceTopologyKey] = viplb.AnnotationServiceTopologyValueNodePool + } + } + + return nil +} diff --git a/pkg/yurtmanager/webhook/poolservice/v1alpha1/poolservice_default_test.go b/pkg/yurtmanager/webhook/poolservice/v1alpha1/poolservice_default_test.go new file mode 100644 index 00000000000..6ec20ce0c1d --- /dev/null +++ b/pkg/yurtmanager/webhook/poolservice/v1alpha1/poolservice_default_test.go @@ -0,0 +1,93 @@ +/* +Copyright 2024 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" + "reflect" + "testing" + + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + + "github.com/openyurtio/openyurt/pkg/apis/network/v1alpha1" + viplb "github.com/openyurtio/openyurt/pkg/yurtmanager/controller/loadbalancerset/viploadbalancer" +) + +func TestDefault(t *testing.T) { + var ( + elbClass = "service.openyurt.io/elb" + vipClass = "service.openyurt.io/viplb" + ) + + testcases := map[string]struct { + obj runtime.Object + errHappened bool + wantedPoolService *v1alpha1.PoolService + }{ + "it is not a v1alpha1": { + obj: &corev1.Node{}, + errHappened: true, + }, + "pod with specified loadBalancerClass but not viplb": { + obj: &v1alpha1.PoolService{ + Spec: v1alpha1.PoolServiceSpec{ + LoadBalancerClass: &elbClass, + }, + }, + wantedPoolService: &v1alpha1.PoolService{ + Spec: v1alpha1.PoolServiceSpec{ + LoadBalancerClass: &elbClass, + }, + }, + }, + "pod with specified loadBalancerClass: vrip": { + obj: &v1alpha1.PoolService{ + Spec: v1alpha1.PoolServiceSpec{ + LoadBalancerClass: &vipClass, + }, + }, + wantedPoolService: &v1alpha1.PoolService{ + Spec: v1alpha1.PoolServiceSpec{ + LoadBalancerClass: &vipClass, + }, + ObjectMeta: metav1.ObjectMeta{ + Annotations: map[string]string{ + viplb.AnnotationServiceTopologyKey: viplb.AnnotationServiceTopologyValueNodePool, + }, + }, + }, + }, + } + + for k, tc := range testcases { + t.Run(k, func(t *testing.T) { + h := PoolServiceHandler{} + err := h.Default(context.TODO(), tc.obj) + if tc.errHappened { + if err == nil { + t.Errorf("expect error, got nil") + } + } else if err != nil { + t.Errorf("expect no error, but got %v", err) + } else { + currentPoolServices := tc.obj.(*v1alpha1.PoolService) + if !reflect.DeepEqual(currentPoolServices, tc.wantedPoolService) { + t.Errorf("expect %#+v, got %#+v", tc.wantedPoolService, currentPoolServices) + } + } + }) + } +} diff --git a/pkg/yurtmanager/webhook/poolservice/v1alpha1/poolservice_handler.go b/pkg/yurtmanager/webhook/poolservice/v1alpha1/poolservice_handler.go new file mode 100644 index 00000000000..b97dc9a6649 --- /dev/null +++ b/pkg/yurtmanager/webhook/poolservice/v1alpha1/poolservice_handler.go @@ -0,0 +1,63 @@ +/* +Copyright 2024 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. +*/ + +/* +Copyright 2024 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/network/v1alpha1" + "github.com/openyurtio/openyurt/pkg/yurtmanager/webhook/util" +) + +// SetupWebhookWithManager sets up Cluster webhooks. mutate path, validatepath, error +func (webhook *PoolServiceHandler) SetupWebhookWithManager(mgr ctrl.Manager) (string, string, error) { + // init + webhook.Client = mgr.GetClient() + + gvk, err := apiutil.GVKForObject(&v1alpha1.PoolService{}, mgr.GetScheme()) + if err != nil { + return "", "", err + } + return util.GenerateMutatePath(gvk), + util.GenerateValidatePath(gvk), + ctrl.NewWebhookManagedBy(mgr). + For(&v1alpha1.PoolService{}). + WithDefaulter(webhook). + Complete() +} + +// +kubebuilder:webhook:path=/mutate-network-openyurt-io-poolservice,mutating=true,failurePolicy=fail,sideEffects=None,admissionReviewVersions=v1;v1beta1,groups=network.openyurt.io,resources=poolservices,verbs=create;update,versions=v1alpha1,name=mutate.network.v1alpha1.poolservice.openyurt.io + +// Cluster implements a validating and defaulting webhook for Cluster. +type PoolServiceHandler struct { + Client client.Client +} + +var _ webhook.CustomDefaulter = &PoolServiceHandler{} diff --git a/pkg/yurtmanager/webhook/server.go b/pkg/yurtmanager/webhook/server.go index be6b725ef9f..675f2d09a36 100644 --- a/pkg/yurtmanager/webhook/server.go +++ b/pkg/yurtmanager/webhook/server.go @@ -37,6 +37,7 @@ import ( v1alpha1platformadmin "github.com/openyurtio/openyurt/pkg/yurtmanager/webhook/platformadmin/v1alpha1" v1alpha2platformadmin "github.com/openyurtio/openyurt/pkg/yurtmanager/webhook/platformadmin/v1alpha2" v1alpha1pod "github.com/openyurtio/openyurt/pkg/yurtmanager/webhook/pod/v1alpha1" + v1alpha1poolservice "github.com/openyurtio/openyurt/pkg/yurtmanager/webhook/poolservice/v1alpha1" "github.com/openyurtio/openyurt/pkg/yurtmanager/webhook/util" webhookcontroller "github.com/openyurtio/openyurt/pkg/yurtmanager/webhook/util/controller" v1alpha1yurtappdaemon "github.com/openyurtio/openyurt/pkg/yurtmanager/webhook/yurtappdaemon/v1alpha1" @@ -80,6 +81,7 @@ func init() { addControllerWebhook(names.PlatformAdminController, &v1alpha2platformadmin.PlatformAdminHandler{}) addControllerWebhook(names.YurtAppOverriderController, &v1alpha1yurtappoverrider.YurtAppOverriderHandler{}) addControllerWebhook(names.YurtAppOverriderController, &v1alpha1deploymentrender.DeploymentRenderHandler{}) + addControllerWebhook(names.VipLoadBalancerController, &v1alpha1poolservice.PoolServiceHandler{}) independentWebhooks[v1node.WebhookName] = &v1node.NodeHandler{} independentWebhooks[v1alpha1pod.WebhookName] = &v1alpha1pod.PodHandler{}