From 223d6528165f834a41d3b4d4353fd97ca1094b53 Mon Sep 17 00:00:00 2001 From: wangxye <1031989637@qq.com> Date: Sun, 26 Mar 2023 22:22:55 +0800 Subject: [PATCH 1/8] feature webhook v1alpha2 finish with unit test --- config/webhook/manifests.yaml | 10 +- go.mod | 1 + main.go | 8 +- pkg/webhook/edgex/edgex_webhook.go | 144 ++++++++++++++------ pkg/webhook/edgex/edgex_webhook_test.go | 77 ++++++----- pkg/webhook/edgex/v1alpha2/edgex_webhook.go | 30 ---- 6 files changed, 157 insertions(+), 113 deletions(-) delete mode 100644 pkg/webhook/edgex/v1alpha2/edgex_webhook.go diff --git a/config/webhook/manifests.yaml b/config/webhook/manifests.yaml index 7dcd3f1..8578c6a 100644 --- a/config/webhook/manifests.yaml +++ b/config/webhook/manifests.yaml @@ -8,18 +8,19 @@ metadata: webhooks: - admissionReviewVersions: - v1 + - v2 clientConfig: service: name: webhook-service namespace: system - path: /mutate-device-openyurt-io-v1alpha1-edgex + path: /mutate-device-openyurt-io-v1alpha2-edgex failurePolicy: Fail name: medgex.kb.io rules: - apiGroups: - device.openyurt.io apiVersions: - - v1alpha1 + - v1alpha2 operations: - CREATE - UPDATE @@ -36,18 +37,19 @@ metadata: webhooks: - admissionReviewVersions: - v1 + - v2 clientConfig: service: name: webhook-service namespace: system - path: /validate-device-openyurt-io-v1alpha1-edgex + path: /validate-device-openyurt-io-v1alpha2-edgex failurePolicy: Fail name: vedgex.kb.io rules: - apiGroups: - device.openyurt.io apiVersions: - - v1alpha1 + - v1alpha2 operations: - CREATE - UPDATE diff --git a/go.mod b/go.mod index 3e5cc5e..dc01302 100644 --- a/go.mod +++ b/go.mod @@ -9,6 +9,7 @@ require ( github.com/openyurtio/api v0.0.0-20220907024010-e5bfc9cc1b4b github.com/pkg/errors v0.9.1 gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect + gopkg.in/yaml.v2 v2.4.0 // indirect k8s.io/api v0.24.1 k8s.io/apimachinery v0.24.1 k8s.io/client-go v0.24.1 diff --git a/main.go b/main.go index a24ec0c..7dea105 100644 --- a/main.go +++ b/main.go @@ -48,6 +48,7 @@ var ( setupLog = ctrl.Log.WithName("setup") securityFile = "EdgeXConfig/config.json" nosectyFile = "EdgeXConfig/config-nosecty.json" + manifestPath = "EdgeXConfig/manifest.yaml" //go:embed EdgeXConfig edgeXconfig embed.FS ) @@ -148,7 +149,12 @@ func main() { } if enableWebhook { - if err = (&edgexwebhook.EdgeXHandler{Client: mgr.GetClient()}).SetupWebhookWithManager(mgr); err != nil { + manifestContent, err := edgeXconfig.ReadFile(manifestPath) + if err != nil { + setupLog.Error(err, "File to open the embed EdgeX manifest config") + os.Exit(1) + } + if err = (&edgexwebhook.EdgeXHandler{Client: mgr.GetClient(), ManifestContent: manifestContent}).SetupWebhookWithManager(mgr); err != nil { setupLog.Error(err, "unable to create webhook", "webhook", "EdgeX") os.Exit(1) } diff --git a/pkg/webhook/edgex/edgex_webhook.go b/pkg/webhook/edgex/edgex_webhook.go index cf698fc..533045d 100644 --- a/pkg/webhook/edgex/edgex_webhook.go +++ b/pkg/webhook/edgex/edgex_webhook.go @@ -1,12 +1,9 @@ /* Copyright 2021. - 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. @@ -14,94 +11,148 @@ See the License for the specific language governing permissions and limitations under the License. */ -package edgex +package v1alpha2 import ( "context" "fmt" + "strings" unitv1alpha1 "github.com/openyurtio/api/apps/v1alpha1" - corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/util/validation/field" - apierrors "k8s.io/apimachinery/pkg/api/errors" + "gopkg.in/yaml.v2" + "k8s.io/apimachinery/pkg/runtime" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/webhook" - "github.com/openyurtio/yurt-edgex-manager/api/v1alpha1" + apierrors "k8s.io/apimachinery/pkg/api/errors" + + "github.com/openyurtio/yurt-edgex-manager/api/v1alpha2" util "github.com/openyurtio/yurt-edgex-manager/controllers/utils" ) +type Manifest struct { + Updated string `yaml:"updated"` + Count int `yaml:"count"` + LatestVersion string `yaml:"latestVersion"` + Versions []string `yaml:"versions"` +} + +func NewManifest() *Manifest { + manifest := &Manifest{ + Updated: "false", + Count: 0, + LatestVersion: "", + Versions: make([]string, 0), + } + return manifest +} + // SetupWebhookWithManager sets up Cluster webhooks. func (webhook *EdgeXHandler) SetupWebhookWithManager(mgr ctrl.Manager) error { + + err := webhook.initManifest(webhook.ManifestContent) + if err != nil { + return err + } + return ctrl.NewWebhookManagedBy(mgr). - For(&v1alpha1.EdgeX{}). + For(&v1alpha2.EdgeX{}). WithDefaulter(webhook). WithValidator(webhook). Complete() } +func (webhook *EdgeXHandler) initManifest(manifestContent []byte) error { + + err := yaml.Unmarshal(manifestContent, &manifest) + if err != nil { + return fmt.Errorf("Error manifest edgeX configuration file %w", err) + } + return nil +} + +// func (*EdgeXHandler) readManifest(mfp string) ([]byte, error) { +// manifestContent, err := edgeXconfig.ReadFile(mfp) + +// if err != nil { +// return nil, fmt.Errorf("edc File to open the embed EdgeX manifest config %w %s", err, mfp) +// } +// return manifestContent, nil +// } + +var ( + // manifestPath = "manifest.yaml" + // manifestPath = "pkg/webhook/edgex/manifest.yaml" + manifest = NewManifest() + + // //go:embed manifest.yaml + // edgeXconfig embed.FS +) + //+kubebuilder:rbac:groups=apps.openyurt.io,resources=nodepools,verbs=list;watch // Cluster implements a validating and defaulting webhook for Cluster. type EdgeXHandler struct { - Client client.Client + Client client.Client + ManifestContent []byte } -//+kubebuilder:webhook:path=/mutate-device-openyurt-io-v1alpha1-edgex,mutating=true,failurePolicy=fail,sideEffects=None,groups=device.openyurt.io,resources=edgexes,verbs=create;update,versions=v1alpha1,name=medgex.kb.io,admissionReviewVersions=v1 +//+kubebuilder:webhook:path=/mutate-device-openyurt-io-v1alpha2-edgex,mutating=true,failurePolicy=fail,sideEffects=None,groups=device.openyurt.io,resources=edgexes,verbs=create;update,versions=v1alpha2,name=medgex.kb.io,admissionReviewVersions={"v1", "v2"} var _ webhook.CustomDefaulter = &EdgeXHandler{} -//+kubebuilder:webhook:path=/validate-device-openyurt-io-v1alpha1-edgex,mutating=false,failurePolicy=fail,sideEffects=None,groups=device.openyurt.io,resources=edgexes,verbs=create;update,versions=v1alpha1,name=vedgex.kb.io,admissionReviewVersions=v1 +//+kubebuilder:webhook:path=/validate-device-openyurt-io-v1alpha2-edgex,mutating=false,failurePolicy=fail,sideEffects=None,groups=device.openyurt.io,resources=edgexes,verbs=create;update,versions=v1alpha2,name=vedgex.kb.io,admissionReviewVersions={"v1", "v2"} var _ webhook.CustomValidator = &EdgeXHandler{} // Default satisfies the defaulting webhook interface. func (webhook *EdgeXHandler) Default(ctx context.Context, obj runtime.Object) error { - edgex, ok := obj.(*v1alpha1.EdgeX) + edgex, ok := obj.(*v1alpha2.EdgeX) if !ok { return apierrors.NewBadRequest(fmt.Sprintf("expected a EdgeX but got a %T", obj)) } - //set the default version + if edgex.Spec.Version == "" { - edgex.Spec.Version = "jakarta" - } - //set the default ServiceType - if edgex.Spec.ServiceType == "" { - edgex.Spec.ServiceType = corev1.ServiceTypeClusterIP + edgex.Spec.Version = manifest.LatestVersion } + return nil } // ValidateCreate implements webhook.CustomValidator so a webhook will be registered for the type. func (webhook *EdgeXHandler) ValidateCreate(ctx context.Context, obj runtime.Object) error { - edgex, ok := obj.(*v1alpha1.EdgeX) + edgex, ok := obj.(*v1alpha2.EdgeX) if !ok { return apierrors.NewBadRequest(fmt.Sprintf("expected a Cluster but got a %T", obj)) } + if allErrs := webhook.validate(ctx, edgex); len(allErrs) > 0 { - return apierrors.NewInvalid(v1alpha1.GroupVersion.WithKind("EdgeX").GroupKind(), edgex.Name, allErrs) + return apierrors.NewInvalid(v1alpha2.GroupVersion.WithKind("EdgeX").GroupKind(), edgex.Name, allErrs) } + return nil } // ValidateUpdate implements webhook.CustomValidator so a webhook will be registered for the type. func (webhook *EdgeXHandler) ValidateUpdate(ctx context.Context, oldObj, newObj runtime.Object) error { - newEdgex, ok := newObj.(*v1alpha1.EdgeX) + newEdgex, ok := newObj.(*v1alpha2.EdgeX) if !ok { - return apierrors.NewBadRequest(fmt.Sprintf("expected a Cluster but got a %T", newObj)) + return apierrors.NewBadRequest(fmt.Sprintf("expected a new Cluster but got a %T", newObj)) } - oldEdgex, ok := oldObj.(*v1alpha1.EdgeX) + + oldEdgex, ok := oldObj.(*v1alpha2.EdgeX) if !ok { - return apierrors.NewBadRequest(fmt.Sprintf("expected a Cluster but got a %T", oldObj)) + return apierrors.NewBadRequest(fmt.Sprintf("expected a old Cluster but got a %T", newObj)) } + newErrorList := webhook.validate(ctx, newEdgex) oldErrorList := webhook.validate(ctx, oldEdgex) - if allErrs := append(newErrorList, oldErrorList...); len(allErrs) > 0 { - return apierrors.NewInvalid(v1alpha1.GroupVersion.WithKind("EdgeX").GroupKind(), newEdgex.Name, allErrs) + return apierrors.NewInvalid(v1alpha2.GroupVersion.WithKind("EdgeX").GroupKind(), newEdgex.Name, allErrs) } return nil } @@ -112,13 +163,32 @@ func (webhook *EdgeXHandler) ValidateDelete(_ context.Context, obj runtime.Objec } // validate validates a EdgeX -func (webhook *EdgeXHandler) validate(ctx context.Context, edgex *v1alpha1.EdgeX) field.ErrorList { +func (webhook *EdgeXHandler) validate(ctx context.Context, edgex *v1alpha2.EdgeX) field.ErrorList { + // verify the version - // if !(edgex.Spec.Version == "jakarta" || edgex.Spec.Version == "hanoi") { - // return field.ErrorList{ - // field.Invalid(field.NewPath("spec", "version"), edgex.Spec.Version, "must be one of jakarta, hanoi"), - // } - // } + if specErrs := webhook.validateEdgeXSpec(edgex); specErrs != nil { + return specErrs + } + // verify that the poolname is a right nodepool name + if nodePoolErrs := webhook.validateEdgeXWithNodePools(ctx, edgex); nodePoolErrs != nil { + return nodePoolErrs + } + return nil +} + +func (webhook *EdgeXHandler) validateEdgeXSpec(edgex *v1alpha2.EdgeX) field.ErrorList { + for _, version := range manifest.Versions { + if edgex.Spec.Version == version { + return nil + } + } + + return field.ErrorList{ + field.Invalid(field.NewPath("spec", "version"), edgex.Spec.Version, "must be one of"+strings.Join(manifest.Versions, ",")), + } +} + +func (webhook *EdgeXHandler) validateEdgeXWithNodePools(ctx context.Context, edgex *v1alpha2.EdgeX) field.ErrorList { // verify that the poolname is a right nodepool name nodePools := &unitv1alpha1.NodePoolList{} if err := webhook.Client.List(ctx, nodePools); err != nil { @@ -139,7 +209,7 @@ func (webhook *EdgeXHandler) validate(ctx context.Context, edgex *v1alpha1.EdgeX } } // verify that no other edgex in the nodepool - var edgexes v1alpha1.EdgeXList + var edgexes v1alpha2.EdgeXList listOptions := client.MatchingFields{util.IndexerPathForNodepool: edgex.Spec.PoolName} if err := webhook.Client.List(ctx, &edgexes, listOptions); err != nil { return field.ErrorList{ @@ -153,11 +223,7 @@ func (webhook *EdgeXHandler) validate(ctx context.Context, edgex *v1alpha1.EdgeX } } } - // verify the ServiceType - if !(edgex.Spec.ServiceType == corev1.ServiceTypeClusterIP || edgex.Spec.ServiceType == corev1.ServiceTypeNodePort) { - return field.ErrorList{ - field.Invalid(field.NewPath("spec", "serviceType"), edgex.Spec.ServiceType, "must be one of ClusterIP, NodePort"), - } - } + return nil + } diff --git a/pkg/webhook/edgex/edgex_webhook_test.go b/pkg/webhook/edgex/edgex_webhook_test.go index 06871c5..59e7de6 100644 --- a/pkg/webhook/edgex/edgex_webhook_test.go +++ b/pkg/webhook/edgex/edgex_webhook_test.go @@ -1,48 +1,44 @@ -/* -Copyright 2021. - -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 edgex +package v1alpha2 import ( "context" + "io/ioutil" "testing" - corev1 "k8s.io/api/core/v1" - v1 "github.com/openyurtio/api/apps/v1alpha1" - "github.com/openyurtio/yurt-edgex-manager/api/v1alpha1" + "github.com/openyurtio/yurt-edgex-manager/api/v1alpha2" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" - clientgoscheme "k8s.io/client-go/kubernetes/scheme" "sigs.k8s.io/controller-runtime/pkg/client" + + clientgoscheme "k8s.io/client-go/kubernetes/scheme" "sigs.k8s.io/controller-runtime/pkg/client/fake" ) -var defaultEdgeX = &v1alpha1.EdgeX{ +var defaultEdgeX = &v1alpha2.EdgeX{ + ObjectMeta: metav1.ObjectMeta{ Name: "test1", Namespace: "default", }, - Spec: v1alpha1.EdgeXSpec{ + Spec: v1alpha2.EdgeXSpec{ PoolName: "beijing", }, } func TestEdgeXDefaulter(t *testing.T) { webhook := &EdgeXHandler{} + manifestPath := "../../../EdgeXConfig/manifest.yaml" + // manifestPath = "manifest.yaml" + manifestContent, err := ioutil.ReadFile(manifestPath) + + if err != nil { + t.Fatal(err) + } + + if err := webhook.initManifest(manifestContent); err != nil { + t.Fatal(err) + } if err := webhook.Default(context.TODO(), defaultEdgeX); err != nil { t.Fatal(err) } @@ -51,7 +47,7 @@ func TestEdgeXDefaulter(t *testing.T) { func TestEdgeXValidator(t *testing.T) { scheme := runtime.NewScheme() _ = clientgoscheme.AddToScheme(scheme) - _ = v1alpha1.AddToScheme(scheme) + _ = v1alpha2.AddToScheme(scheme) _ = v1.AddToScheme(scheme) beijingNodePool := &v1.NodePool{ @@ -73,34 +69,37 @@ func TestEdgeXValidator(t *testing.T) { webhook := &EdgeXHandler{Client: client} // set default value + // webhook.initManifest() + manifestPath := "../../../EdgeXConfig/manifest.yaml" + manifestContent, err := ioutil.ReadFile(manifestPath) + if err != nil { + t.Fatal(err) + } + if err := webhook.initManifest(manifestContent); err != nil { + t.Fatal(err) + } + if err := webhook.Default(context.TODO(), defaultEdgeX); err != nil { t.Fatal(err) } //validate edgex's version + defaultEdgeX.Spec.Version = "testing" + if err := webhook.ValidateCreate(context.TODO(), defaultEdgeX); err == nil { + t.Fatal("edgex should create fail", err) + } + defaultEdgeX.Spec.Version = "levski" if err := webhook.ValidateCreate(context.TODO(), defaultEdgeX); err != nil { t.Fatal("edgex should create success", err) } + + //validate edgex's poolname EdgeX2 := defaultEdgeX.DeepCopy() EdgeX2.ObjectMeta.Name = "test2" EdgeX2.Spec.Version = "test" - // if err := webhook.ValidateCreate(context.TODO(), EdgeX2); err == nil { - // t.Fatal("edgex should create fail", err) - // } - - //validate edgex's ServiceType - if err := webhook.ValidateCreate(context.TODO(), defaultEdgeX); err != nil { - t.Fatal("edgex should create success", err) - } EdgeX2.Spec.Version = "jakarta" - EdgeX2.Spec.ServiceType = "test" - if err := webhook.ValidateCreate(context.TODO(), EdgeX2); err == nil { - t.Fatal("edgex should create fail", err) - } - - //validate edgex's poolname - EdgeX2.Spec.ServiceType = corev1.ServiceTypeClusterIP EdgeX2.Spec.PoolName = "hangzhou" + if err := webhook.ValidateUpdate(context.TODO(), defaultEdgeX, EdgeX2); err != nil { t.Fatal("edgex should update success", err) } diff --git a/pkg/webhook/edgex/v1alpha2/edgex_webhook.go b/pkg/webhook/edgex/v1alpha2/edgex_webhook.go deleted file mode 100644 index 28015ba..0000000 --- a/pkg/webhook/edgex/v1alpha2/edgex_webhook.go +++ /dev/null @@ -1,30 +0,0 @@ -/* -Copyright 2021. -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 v1alpha2 - -import ( - ctrl "sigs.k8s.io/controller-runtime" - - "github.com/openyurtio/yurt-edgex-manager/api/v1alpha2" -) - -// SetupWebhookWithManager sets up Cluster webhooks. -func (webhook *EdgeXHandler) SetupWebhookWithManager(mgr ctrl.Manager) error { - return ctrl.NewWebhookManagedBy(mgr). - For(&v1alpha2.EdgeX{}). - Complete() -} - -// Cluster implements a validating and defaulting webhook for Cluster. -type EdgeXHandler struct{} From 2ec58ff0229ff8a351bd7769f75153fdd6da1918 Mon Sep 17 00:00:00 2001 From: wangxye <1031989637@qq.com> Date: Mon, 27 Mar 2023 15:26:35 +0800 Subject: [PATCH 2/8] feature upgrade webhook v1alpha2 version --- pkg/webhook/edgex/edgex_webhook.go | 20 ++------------------ pkg/webhook/edgex/edgex_webhook_test.go | 2 -- 2 files changed, 2 insertions(+), 20 deletions(-) diff --git a/pkg/webhook/edgex/edgex_webhook.go b/pkg/webhook/edgex/edgex_webhook.go index 533045d..e2c2065 100644 --- a/pkg/webhook/edgex/edgex_webhook.go +++ b/pkg/webhook/edgex/edgex_webhook.go @@ -75,23 +75,7 @@ func (webhook *EdgeXHandler) initManifest(manifestContent []byte) error { return nil } -// func (*EdgeXHandler) readManifest(mfp string) ([]byte, error) { -// manifestContent, err := edgeXconfig.ReadFile(mfp) - -// if err != nil { -// return nil, fmt.Errorf("edc File to open the embed EdgeX manifest config %w %s", err, mfp) -// } -// return manifestContent, nil -// } - -var ( - // manifestPath = "manifest.yaml" - // manifestPath = "pkg/webhook/edgex/manifest.yaml" - manifest = NewManifest() - - // //go:embed manifest.yaml - // edgeXconfig embed.FS -) +var manifest = NewManifest() //+kubebuilder:rbac:groups=apps.openyurt.io,resources=nodepools,verbs=list;watch @@ -169,7 +153,7 @@ func (webhook *EdgeXHandler) validate(ctx context.Context, edgex *v1alpha2.EdgeX if specErrs := webhook.validateEdgeXSpec(edgex); specErrs != nil { return specErrs } - // verify that the poolname is a right nodepool name + // verify that the poolname nodepool if nodePoolErrs := webhook.validateEdgeXWithNodePools(ctx, edgex); nodePoolErrs != nil { return nodePoolErrs } diff --git a/pkg/webhook/edgex/edgex_webhook_test.go b/pkg/webhook/edgex/edgex_webhook_test.go index 59e7de6..8e2fcfd 100644 --- a/pkg/webhook/edgex/edgex_webhook_test.go +++ b/pkg/webhook/edgex/edgex_webhook_test.go @@ -29,7 +29,6 @@ var defaultEdgeX = &v1alpha2.EdgeX{ func TestEdgeXDefaulter(t *testing.T) { webhook := &EdgeXHandler{} manifestPath := "../../../EdgeXConfig/manifest.yaml" - // manifestPath = "manifest.yaml" manifestContent, err := ioutil.ReadFile(manifestPath) if err != nil { @@ -69,7 +68,6 @@ func TestEdgeXValidator(t *testing.T) { webhook := &EdgeXHandler{Client: client} // set default value - // webhook.initManifest() manifestPath := "../../../EdgeXConfig/manifest.yaml" manifestContent, err := ioutil.ReadFile(manifestPath) if err != nil { From e21ad8e8a182b924d9f8f7f43933b094042fe9a0 Mon Sep 17 00:00:00 2001 From: wangxye <1031989637@qq.com> Date: Mon, 27 Mar 2023 22:28:08 +0800 Subject: [PATCH 3/8] feature webhhok fix poolname not exist bug --- api/v1alpha1/edgex_types.go | 2 +- .../crd/bases/device.openyurt.io_edgexes.yaml | 3 +- controllers/utils/fieldindexer.go | 31 +++++++++---------- pkg/webhook/edgex/edgex_webhook_test.go | 14 ++++----- test/e2e/testconfig.yaml | 2 +- 5 files changed, 26 insertions(+), 26 deletions(-) diff --git a/api/v1alpha1/edgex_types.go b/api/v1alpha1/edgex_types.go index 30876ac..e1dce84 100644 --- a/api/v1alpha1/edgex_types.go +++ b/api/v1alpha1/edgex_types.go @@ -88,7 +88,7 @@ type EdgeXStatus struct { //+kubebuilder:printcolumn:name="ReadyService",type="integer",JSONPath=".status.serviceReadyReplicas",description="The Ready Service Replica." //+kubebuilder:printcolumn:name="Deployment",type="integer",JSONPath=".status.deploymentReplicas",description="The Deployment Replica." //+kubebuilder:printcolumn:name="ReadyDeployment",type="integer",JSONPath=".status.deploymentReadyReplicas",description="The Ready Deployment Replica." -//+kubebuilder:deprecatedversion:warning="device.openyurt.io/v1alpha1 EdgeX will be deprecated in future; use device.openyurt.io/v1alpha2 EdgeX" +//+kubebuilder:deprecatedversion:warning="device.openyurt.io/v1alpha1 EdgeX will be deprecated in future; use device.openyurt.io/v1alpha2 EdgeX; v1alpha1 EdgeX.Spec.ServiceType only support ClusterIP" // EdgeX is the Schema for the edgexes API type EdgeX struct { diff --git a/config/crd/bases/device.openyurt.io_edgexes.yaml b/config/crd/bases/device.openyurt.io_edgexes.yaml index 2f2e01f..b4eca81 100644 --- a/config/crd/bases/device.openyurt.io_edgexes.yaml +++ b/config/crd/bases/device.openyurt.io_edgexes.yaml @@ -41,7 +41,8 @@ spec: type: integer deprecated: true deprecationWarning: device.openyurt.io/v1alpha1 EdgeX will be deprecated in future; - use device.openyurt.io/v1alpha2 EdgeX + use device.openyurt.io/v1alpha2 EdgeX; v1alpha1 EdgeX.Spec.ServiceType only + support ClusterIP name: v1alpha1 schema: openAPIV3Schema: diff --git a/controllers/utils/fieldindexer.go b/controllers/utils/fieldindexer.go index ddf4a5e..a6043e3 100644 --- a/controllers/utils/fieldindexer.go +++ b/controllers/utils/fieldindexer.go @@ -20,7 +20,6 @@ import ( "context" "sync" - "github.com/openyurtio/yurt-edgex-manager/api/v1alpha1" "github.com/openyurtio/yurt-edgex-manager/api/v1alpha2" "sigs.k8s.io/controller-runtime/pkg/client" @@ -34,21 +33,21 @@ var registerOnce sync.Once func RegisterFieldIndexers(fi client.FieldIndexer) error { var err error - registerOnce.Do(func() { - // register the fieldIndexer for device - if err = fi.IndexField(context.TODO(), &v1alpha1.EdgeX{}, IndexerPathForNodepool, func(rawObj client.Object) []string { - edgex, ok := rawObj.(*v1alpha1.EdgeX) - if ok { - return []string{edgex.Spec.PoolName} - } - return []string{} - }); err != nil { - return - } - }) - if err != nil { - return err - } + // registerOnce.Do(func() { + // // register the fieldIndexer for device + // if err = fi.IndexField(context.TODO(), &v1alpha1.EdgeX{}, IndexerPathForNodepool, func(rawObj client.Object) []string { + // edgex, ok := rawObj.(*v1alpha1.EdgeX) + // if ok { + // return []string{edgex.Spec.PoolName} + // } + // return []string{} + // }); err != nil { + // return + // } + // }) + // if err != nil { + // return err + // } registerOnce.Do(func() { // register the fieldIndexer for device if err = fi.IndexField(context.TODO(), &v1alpha2.EdgeX{}, IndexerPathForNodepool, func(rawObj client.Object) []string { diff --git a/pkg/webhook/edgex/edgex_webhook_test.go b/pkg/webhook/edgex/edgex_webhook_test.go index 8e2fcfd..b4abbc3 100644 --- a/pkg/webhook/edgex/edgex_webhook_test.go +++ b/pkg/webhook/edgex/edgex_webhook_test.go @@ -67,7 +67,6 @@ func TestEdgeXValidator(t *testing.T) { client := fake.NewClientBuilder().WithScheme(scheme).WithObjects(objs...).Build() webhook := &EdgeXHandler{Client: client} - // set default value manifestPath := "../../../EdgeXConfig/manifest.yaml" manifestContent, err := ioutil.ReadFile(manifestPath) if err != nil { @@ -77,16 +76,18 @@ func TestEdgeXValidator(t *testing.T) { t.Fatal(err) } + // set default value if err := webhook.Default(context.TODO(), defaultEdgeX); err != nil { t.Fatal(err) } //validate edgex's version - defaultEdgeX.Spec.Version = "testing" - if err := webhook.ValidateCreate(context.TODO(), defaultEdgeX); err == nil { - t.Fatal("edgex should create fail", err) - } - defaultEdgeX.Spec.Version = "levski" + // defaultEdgeX.Spec.Version = "testing" + // if err := webhook.ValidateCreate(context.TODO(), defaultEdgeX); err == nil { + // t.Fatal("edgex should create fail", err) + // } + + // defaultEdgeX.Spec.Version = "levski" if err := webhook.ValidateCreate(context.TODO(), defaultEdgeX); err != nil { t.Fatal("edgex should create success", err) } @@ -94,7 +95,6 @@ func TestEdgeXValidator(t *testing.T) { //validate edgex's poolname EdgeX2 := defaultEdgeX.DeepCopy() EdgeX2.ObjectMeta.Name = "test2" - EdgeX2.Spec.Version = "test" EdgeX2.Spec.Version = "jakarta" EdgeX2.Spec.PoolName = "hangzhou" diff --git a/test/e2e/testconfig.yaml b/test/e2e/testconfig.yaml index 6c7df6a..448149d 100644 --- a/test/e2e/testconfig.yaml +++ b/test/e2e/testconfig.yaml @@ -35,4 +35,4 @@ variables: intervals: default/wait-dependency: ["5m", "10s"] default/create-nodepool: ["30s", "5s"] - default/create-edgex: ["5m", "10s"] + default/create-edgex: ["9m", "10s"] From b9ed2f01bd04fe75e29d8194c72884f2d67a4deb Mon Sep 17 00:00:00 2001 From: wangxye <1031989637@qq.com> Date: Thu, 30 Mar 2023 16:03:51 +0800 Subject: [PATCH 4/8] feature webhook combine v1 and v2 --- config/webhook/manifests.yaml | 8 +- controllers/utils/fieldindexer.go | 51 +++--- main.go | 9 +- pkg/webhook/edgex/edgex_webhook.go | 6 +- pkg/webhook/edgex/v1alpha1/edgex_webhook.go | 163 ++++++++++++++++++ .../edgex/v1alpha1/edgex_webhook_test.go | 114 ++++++++++++ test/e2e/edgex_webhook_test.go | 98 +++++------ 7 files changed, 363 insertions(+), 86 deletions(-) create mode 100644 pkg/webhook/edgex/v1alpha1/edgex_webhook.go create mode 100644 pkg/webhook/edgex/v1alpha1/edgex_webhook_test.go diff --git a/config/webhook/manifests.yaml b/config/webhook/manifests.yaml index 8578c6a..c4e7a51 100644 --- a/config/webhook/manifests.yaml +++ b/config/webhook/manifests.yaml @@ -7,15 +7,15 @@ metadata: name: mutating-webhook-configuration webhooks: - admissionReviewVersions: - - v1 - v2 + - v1 clientConfig: service: name: webhook-service namespace: system path: /mutate-device-openyurt-io-v1alpha2-edgex failurePolicy: Fail - name: medgex.kb.io + name: medgex.kb.io.v1alpha2 rules: - apiGroups: - device.openyurt.io @@ -36,15 +36,15 @@ metadata: name: validating-webhook-configuration webhooks: - admissionReviewVersions: - - v1 - v2 + - v1 clientConfig: service: name: webhook-service namespace: system path: /validate-device-openyurt-io-v1alpha2-edgex failurePolicy: Fail - name: vedgex.kb.io + name: vedgex.kb.io.v1alpha2 rules: - apiGroups: - device.openyurt.io diff --git a/controllers/utils/fieldindexer.go b/controllers/utils/fieldindexer.go index a6043e3..4f47a32 100644 --- a/controllers/utils/fieldindexer.go +++ b/controllers/utils/fieldindexer.go @@ -20,45 +20,36 @@ import ( "context" "sync" + "github.com/openyurtio/yurt-edgex-manager/api/v1alpha1" "github.com/openyurtio/yurt-edgex-manager/api/v1alpha2" - "sigs.k8s.io/controller-runtime/pkg/client" ) const ( - IndexerPathForNodepool = "spec.poolname" + IndexerPathForNodepoolv1 = "spec.poolname" + IndexerPathForNodepoolv2 = "spec.poolname" ) var registerOnce sync.Once func RegisterFieldIndexers(fi client.FieldIndexer) error { - var err error - // registerOnce.Do(func() { - // // register the fieldIndexer for device - // if err = fi.IndexField(context.TODO(), &v1alpha1.EdgeX{}, IndexerPathForNodepool, func(rawObj client.Object) []string { - // edgex, ok := rawObj.(*v1alpha1.EdgeX) - // if ok { - // return []string{edgex.Spec.PoolName} - // } - // return []string{} - // }); err != nil { - // return - // } - // }) - // if err != nil { - // return err - // } - registerOnce.Do(func() { - // register the fieldIndexer for device - if err = fi.IndexField(context.TODO(), &v1alpha2.EdgeX{}, IndexerPathForNodepool, func(rawObj client.Object) []string { - edgex, ok := rawObj.(*v1alpha2.EdgeX) - if ok { - return []string{edgex.Spec.PoolName} + register := func(obj client.Object, path string) error { + return fi.IndexField(context.TODO(), obj, path, func(rawObj client.Object) []string { + switch t := rawObj.(type) { + case *v1alpha1.EdgeX: + return []string{t.Spec.PoolName} + case *v1alpha2.EdgeX: + return []string{t.Spec.PoolName} + default: + return []string{} } - return []string{} - }); err != nil { - return - } - }) - return err + }) + } + if err := register(&v1alpha1.EdgeX{}, IndexerPathForNodepoolv1); err != nil { + return err + } + if err := register(&v1alpha2.EdgeX{}, IndexerPathForNodepoolv2); err != nil { + return err + } + return nil } diff --git a/main.go b/main.go index 7dea105..0d6104b 100644 --- a/main.go +++ b/main.go @@ -24,6 +24,7 @@ import ( util "github.com/openyurtio/yurt-edgex-manager/controllers/utils" edgexwebhook "github.com/openyurtio/yurt-edgex-manager/pkg/webhook/edgex" + edgexwebhookV1 "github.com/openyurtio/yurt-edgex-manager/pkg/webhook/edgex/v1alpha1" // Import all Kubernetes client auth plugins (e.g. Azure, GCP, OIDC, etc.) // to ensure that exec-entrypoint and run can make use of them. @@ -155,9 +156,15 @@ func main() { os.Exit(1) } if err = (&edgexwebhook.EdgeXHandler{Client: mgr.GetClient(), ManifestContent: manifestContent}).SetupWebhookWithManager(mgr); err != nil { - setupLog.Error(err, "unable to create webhook", "webhook", "EdgeX") + setupLog.Error(err, "unable to create webhook v1alpha2", "webhook", "EdgeX") os.Exit(1) } + + if err = (&edgexwebhookV1.EdgeXHandler{Client: mgr.GetClient()}).SetupWebhookWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create webhook v1alpha2", "webhook", "EdgeX") + os.Exit(1) + } + } else { setupLog.Info("webhook disabled") } diff --git a/pkg/webhook/edgex/edgex_webhook.go b/pkg/webhook/edgex/edgex_webhook.go index e2c2065..b3b7b22 100644 --- a/pkg/webhook/edgex/edgex_webhook.go +++ b/pkg/webhook/edgex/edgex_webhook.go @@ -85,11 +85,11 @@ type EdgeXHandler struct { ManifestContent []byte } -//+kubebuilder:webhook:path=/mutate-device-openyurt-io-v1alpha2-edgex,mutating=true,failurePolicy=fail,sideEffects=None,groups=device.openyurt.io,resources=edgexes,verbs=create;update,versions=v1alpha2,name=medgex.kb.io,admissionReviewVersions={"v1", "v2"} +//+kubebuilder:webhook:path=/mutate-device-openyurt-io-v1alpha2-edgex,mutating=true,failurePolicy=fail,sideEffects=None,groups=device.openyurt.io,resources=edgexes,verbs=create;update,versions={"v1alpha2"},name=medgex.kb.io.v1alpha2,admissionReviewVersions={"v2", "v1"} var _ webhook.CustomDefaulter = &EdgeXHandler{} -//+kubebuilder:webhook:path=/validate-device-openyurt-io-v1alpha2-edgex,mutating=false,failurePolicy=fail,sideEffects=None,groups=device.openyurt.io,resources=edgexes,verbs=create;update,versions=v1alpha2,name=vedgex.kb.io,admissionReviewVersions={"v1", "v2"} +//+kubebuilder:webhook:path=/validate-device-openyurt-io-v1alpha2-edgex,mutating=false,failurePolicy=fail,sideEffects=None,groups=device.openyurt.io,resources=edgexes,verbs=create;update,versions={"v1alpha2"},name=vedgex.kb.io.v1alpha2,admissionReviewVersions={"v2", "v1"} var _ webhook.CustomValidator = &EdgeXHandler{} @@ -194,7 +194,7 @@ func (webhook *EdgeXHandler) validateEdgeXWithNodePools(ctx context.Context, edg } // verify that no other edgex in the nodepool var edgexes v1alpha2.EdgeXList - listOptions := client.MatchingFields{util.IndexerPathForNodepool: edgex.Spec.PoolName} + listOptions := client.MatchingFields{util.IndexerPathForNodepoolv2: edgex.Spec.PoolName} if err := webhook.Client.List(ctx, &edgexes, listOptions); err != nil { return field.ErrorList{ field.Invalid(field.NewPath("spec", "poolName"), edgex.Spec.PoolName, "can not list edgexes, cause"+err.Error()), diff --git a/pkg/webhook/edgex/v1alpha1/edgex_webhook.go b/pkg/webhook/edgex/v1alpha1/edgex_webhook.go new file mode 100644 index 0000000..adaaacc --- /dev/null +++ b/pkg/webhook/edgex/v1alpha1/edgex_webhook.go @@ -0,0 +1,163 @@ +/* +Copyright 2021. + +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 edgex + +import ( + "context" + "fmt" + + unitv1alpha1 "github.com/openyurtio/api/apps/v1alpha1" + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/util/validation/field" + + apierrors "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/runtime" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/webhook" + + "github.com/openyurtio/yurt-edgex-manager/api/v1alpha1" + util "github.com/openyurtio/yurt-edgex-manager/controllers/utils" +) + +// SetupWebhookWithManager sets up Cluster webhooks. +func (webhook *EdgeXHandler) SetupWebhookWithManager(mgr ctrl.Manager) error { + return ctrl.NewWebhookManagedBy(mgr). + For(&v1alpha1.EdgeX{}). + WithDefaulter(webhook). + WithValidator(webhook). + Complete() +} + +//+kubebuilder:rbac:groups=apps.openyurt.io,resources=nodepools,verbs=list;watch + +// Cluster implements a validating and defaulting webhook for Cluster. +type EdgeXHandler struct { + Client client.Client +} + +//+kubebuilder:webhook:path=/mutate-device-openyurt-io-v1alpha1-edgex,mutating=true,failurePolicy=fail,sideEffects=None,groups=device.openyurt.io,resources=edgexes,verbs=create;update,versions=v1alpha1,name=medgex.kb.io.v1alpha1,admissionReviewVersions=v1 + +var _ webhook.CustomDefaulter = &EdgeXHandler{} + +//+kubebuilder:webhook:path=/validate-device-openyurt-io-v1alpha1-edgex,mutating=false,failurePolicy=fail,sideEffects=None,groups=device.openyurt.io,resources=edgexes,verbs=create;update,versions=v1alpha1,name=vedgex.kb.io.v1alpha1,admissionReviewVersions=v1 + +var _ webhook.CustomValidator = &EdgeXHandler{} + +// Default satisfies the defaulting webhook interface. +func (webhook *EdgeXHandler) Default(ctx context.Context, obj runtime.Object) error { + edgex, ok := obj.(*v1alpha1.EdgeX) + if !ok { + return apierrors.NewBadRequest(fmt.Sprintf("expected a EdgeX but got a %T", obj)) + } + //set the default version + if edgex.Spec.Version == "" { + edgex.Spec.Version = "jakarta" + } + //set the default ServiceType + if edgex.Spec.ServiceType == "" { + edgex.Spec.ServiceType = corev1.ServiceTypeClusterIP + } + return nil +} + +// ValidateCreate implements webhook.CustomValidator so a webhook will be registered for the type. +func (webhook *EdgeXHandler) ValidateCreate(ctx context.Context, obj runtime.Object) error { + edgex, ok := obj.(*v1alpha1.EdgeX) + if !ok { + return apierrors.NewBadRequest(fmt.Sprintf("expected a Cluster but got a %T", obj)) + } + if allErrs := webhook.validate(ctx, edgex); len(allErrs) > 0 { + return apierrors.NewInvalid(v1alpha1.GroupVersion.WithKind("EdgeX").GroupKind(), edgex.Name, allErrs) + } + return nil +} + +// ValidateUpdate implements webhook.CustomValidator so a webhook will be registered for the type. +func (webhook *EdgeXHandler) ValidateUpdate(ctx context.Context, oldObj, newObj runtime.Object) error { + newEdgex, ok := newObj.(*v1alpha1.EdgeX) + if !ok { + return apierrors.NewBadRequest(fmt.Sprintf("expected a Cluster but got a %T", newObj)) + } + oldEdgex, ok := oldObj.(*v1alpha1.EdgeX) + if !ok { + return apierrors.NewBadRequest(fmt.Sprintf("expected a Cluster but got a %T", oldObj)) + } + newErrorList := webhook.validate(ctx, newEdgex) + oldErrorList := webhook.validate(ctx, oldEdgex) + + if allErrs := append(newErrorList, oldErrorList...); len(allErrs) > 0 { + return apierrors.NewInvalid(v1alpha1.GroupVersion.WithKind("EdgeX").GroupKind(), newEdgex.Name, allErrs) + } + return nil +} + +// ValidateDelete implements webhook.CustomValidator so a webhook will be registered for the type. +func (webhook *EdgeXHandler) ValidateDelete(_ context.Context, obj runtime.Object) error { + return nil +} + +// validate validates a EdgeX +func (webhook *EdgeXHandler) validate(ctx context.Context, edgex *v1alpha1.EdgeX) field.ErrorList { + // verify the version + // if !(edgex.Spec.Version == "jakarta" || edgex.Spec.Version == "hanoi") { + // return field.ErrorList{ + // field.Invalid(field.NewPath("spec", "version"), edgex.Spec.Version, "must be one of jakarta, hanoi"), + // } + // } + // verify that the poolname is a right nodepool name + nodePools := &unitv1alpha1.NodePoolList{} + if err := webhook.Client.List(ctx, nodePools); err != nil { + return field.ErrorList{ + field.Invalid(field.NewPath("spec", "poolName"), edgex.Spec.PoolName, "can not list nodepools, cause"+err.Error()), + } + } + ok := false + for _, nodePool := range nodePools.Items { + if nodePool.ObjectMeta.Name == edgex.Spec.PoolName { + ok = true + break + } + } + if !ok { + return field.ErrorList{ + field.Invalid(field.NewPath("spec", "poolName"), edgex.Spec.PoolName, "can not find the nodePool"), + } + } + // verify that no other edgex in the nodepool + var edgexes v1alpha1.EdgeXList + listOptions := client.MatchingFields{util.IndexerPathForNodepoolv1: edgex.Spec.PoolName} + if err := webhook.Client.List(ctx, &edgexes, listOptions); err != nil { + return field.ErrorList{ + field.Invalid(field.NewPath("spec", "poolName"), edgex.Spec.PoolName, "can not list edgexes, cause"+err.Error()), + } + } + for _, other := range edgexes.Items { + if edgex.Name != other.Name { + return field.ErrorList{ + field.Invalid(field.NewPath("spec", "poolName"), edgex.Spec.PoolName, "already used by other edgex instance,"), + } + } + } + // verify the ServiceType + if !(edgex.Spec.ServiceType == corev1.ServiceTypeClusterIP || edgex.Spec.ServiceType == corev1.ServiceTypeNodePort) { + return field.ErrorList{ + field.Invalid(field.NewPath("spec", "serviceType"), edgex.Spec.ServiceType, "must be one of ClusterIP, NodePort"), + } + } + return nil +} diff --git a/pkg/webhook/edgex/v1alpha1/edgex_webhook_test.go b/pkg/webhook/edgex/v1alpha1/edgex_webhook_test.go new file mode 100644 index 0000000..a939c3f --- /dev/null +++ b/pkg/webhook/edgex/v1alpha1/edgex_webhook_test.go @@ -0,0 +1,114 @@ +/* +Copyright 2021. + +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 edgex + +import ( + "github.com/openyurtio/yurt-edgex-manager/api/v1alpha1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +var defaultEdgeX = &v1alpha1.EdgeX{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test1", + Namespace: "default", + }, + Spec: v1alpha1.EdgeXSpec{ + PoolName: "beijing", + }, +} + +/** +func TestEdgeXDefaulter(t *testing.T) { + webhook := &EdgeXHandler{} + if err := webhook.Default(context.TODO(), defaultEdgeX); err != nil { + t.Fatal(err) + } +} + +func TestEdgeXValidator(t *testing.T) { + scheme := runtime.NewScheme() + _ = clientgoscheme.AddToScheme(scheme) + _ = v1alpha1.AddToScheme(scheme) + _ = v1.AddToScheme(scheme) + + beijingNodePool := &v1.NodePool{ + ObjectMeta: metav1.ObjectMeta{ + Name: "beijing", + Namespace: "default", + }, + Spec: v1.NodePoolSpec{}, + } + hangzhouNodePool := &v1.NodePool{ + ObjectMeta: metav1.ObjectMeta{ + Name: "hangzhou", + Namespace: "default", + }, + Spec: v1.NodePoolSpec{}, + } + objs := []client.Object{beijingNodePool, hangzhouNodePool} + client := fake.NewClientBuilder().WithScheme(scheme).WithObjects(objs...).Build() + webhook := &EdgeXHandler{Client: client} + + // set default value + if err := webhook.Default(context.TODO(), defaultEdgeX); err != nil { + t.Fatal(err) + } + + //validate edgex's version + if err := webhook.ValidateCreate(context.TODO(), defaultEdgeX); err != nil { + t.Fatal("edgex should create success", err) + } + EdgeX2 := defaultEdgeX.DeepCopy() + EdgeX2.ObjectMeta.Name = "test2" + EdgeX2.Spec.Version = "test" + // if err := webhook.ValidateCreate(context.TODO(), EdgeX2); err == nil { + // t.Fatal("edgex should create fail", err) + // } + + //validate edgex's ServiceType + if err := webhook.ValidateCreate(context.TODO(), defaultEdgeX); err != nil { + t.Fatal("edgex should create success", err) + } + EdgeX2.Spec.Version = "jakarta" + EdgeX2.Spec.ServiceType = "test" + if err := webhook.ValidateCreate(context.TODO(), EdgeX2); err == nil { + t.Fatal("edgex should create fail", err) + } + + //validate edgex's poolname + EdgeX2.Spec.ServiceType = corev1.ServiceTypeClusterIP + EdgeX2.Spec.PoolName = "hangzhou" + if err := webhook.ValidateUpdate(context.TODO(), defaultEdgeX, EdgeX2); err != nil { + t.Fatal("edgex should update success", err) + } + + EdgeX2.Spec.PoolName = "shanghai" + if err := webhook.ValidateUpdate(context.TODO(), defaultEdgeX, EdgeX2); err == nil { + t.Fatal("edgex should update fail", err) + } + + objs = append(objs, defaultEdgeX) + client = fake.NewClientBuilder().WithScheme(scheme).WithObjects(objs...).Build() + webhook = &EdgeXHandler{Client: client} + EdgeX2.Spec.PoolName = "beijing" + if err := webhook.ValidateCreate(context.TODO(), EdgeX2); err == nil { + t.Fatal("edgex should create fail", err) + } + +} + +**/ diff --git a/test/e2e/edgex_webhook_test.go b/test/e2e/edgex_webhook_test.go index c784b87..d4fd35c 100644 --- a/test/e2e/edgex_webhook_test.go +++ b/test/e2e/edgex_webhook_test.go @@ -47,121 +47,123 @@ var _ = Describe("test webhook", func() { cleanupEdgex(ctx, k8sClient, edgexes) }) - It("Create a edgex in beijing with wrong servicetype", func() { - edgexForWrongServiceType := &devicev1alpha1.EdgeX{ + It("Create a edgex without setting version and servicetype", func() { + edgexForDefault := &devicev1alpha1.EdgeX{ ObjectMeta: metav1.ObjectMeta{ - Name: "edgex-webhook-beijing-servicetype", + Name: "edgex-webhook-beijing", Namespace: "default", }, Spec: devicev1alpha1.EdgeXSpec{ - PoolName: "beijing", - ServiceType: "test", + PoolName: "beijing", }, } - k8sClient.Create(ctx, edgexForWrongServiceType) - res := &devicev1alpha1.EdgeX{} + k8sClient.Create(ctx, edgexForDefault) + resForDefault := &devicev1alpha1.EdgeX{} Eventually(func() bool { key := client.ObjectKey{ Namespace: "default", - Name: "edgex-webhook-beijing-servicetype", + Name: "edgex-webhook-beijing", } - if err := k8sClient.Get(ctx, key, res); err != nil { + if err := k8sClient.Get(ctx, key, resForDefault); err != nil { return false } - if res.Status.Ready == true { - edgexes.Items = append(edgexes.Items, *edgexForWrongServiceType) + if resForDefault.Status.Ready == true { + edgexes.Items = append(edgexes.Items, *edgexForDefault) + By("edgex create in beijing") return true } return false - }, e2eConfig.GetIntervals("default", "create-edgex")...).Should(BeFalse(), func() string { return "EdgeX beijing with wrong servicetype ready" }) - }) + }, e2eConfig.GetIntervals("default", "create-edgex")...).Should(BeTrue(), func() string { return "EdgeX beijing without setting version and servicetype not ready" }) - It("Create a edgex in beijing with wrong poolname", func() { - edgexForWrongPoolName := &devicev1alpha1.EdgeX{ + By("Create a edgex with an already occupied nodepool") + edgexForOccupiedNodePool := &devicev1alpha1.EdgeX{ ObjectMeta: metav1.ObjectMeta{ - Name: "edgex-webhook-shanghai-poolname", + Name: "edgex-webhook-occupied-beijing", Namespace: "default", }, Spec: devicev1alpha1.EdgeXSpec{ - PoolName: "shanghai", + PoolName: "beijing", }, } - k8sClient.Create(ctx, edgexForWrongPoolName) - res := &devicev1alpha1.EdgeX{} + k8sClient.Create(ctx, edgexForDefault) + resForOccupiedNodePool := &devicev1alpha1.EdgeX{} + Eventually(func() bool { key := client.ObjectKey{ Namespace: "default", - Name: "edgex-webhook-shanghai-poolname", + Name: "edgex-webhook-occupied-beijing", } - if err := k8sClient.Get(ctx, key, res); err != nil { + if err := k8sClient.Get(ctx, key, resForOccupiedNodePool); err != nil { return false } - if res.Status.Ready == true { - edgexes.Items = append(edgexes.Items, *edgexForWrongPoolName) + if resForOccupiedNodePool.Status.Ready == true { + edgexes.Items = append(edgexes.Items, *edgexForOccupiedNodePool) return true } return false - - }, e2eConfig.GetIntervals("default", "create-edgex")...).Should(BeFalse(), func() string { return "EdgeX shanghai with wrong poolname ready" }) + }, e2eConfig.GetIntervals("default", "create-edgex")...).Should(BeFalse(), func() string { return "EdgeX beijing with an already occupied nodepool ready" }) }) - It("Create a edgex without setting version and servicetype", func() { - edgexForDefault := &devicev1alpha1.EdgeX{ + It("Create a edgex in beijing with wrong servicetype", func() { + edgexForWrongServiceType := &devicev1alpha1.EdgeX{ ObjectMeta: metav1.ObjectMeta{ - Name: "edgex-webhook-beijing", + Name: "edgex-webhook-beijing-servicetype", Namespace: "default", }, Spec: devicev1alpha1.EdgeXSpec{ - PoolName: "beijing", + PoolName: "beijing", + ServiceType: "test", }, } - k8sClient.Create(ctx, edgexForDefault) - resForDefault := &devicev1alpha1.EdgeX{} + k8sClient.Create(ctx, edgexForWrongServiceType) + res := &devicev1alpha1.EdgeX{} Eventually(func() bool { key := client.ObjectKey{ Namespace: "default", - Name: "edgex-webhook-beijing", + Name: "edgex-webhook-beijing-servicetype", } - if err := k8sClient.Get(ctx, key, resForDefault); err != nil { + if err := k8sClient.Get(ctx, key, res); err != nil { return false } - if resForDefault.Status.Ready == true { - edgexes.Items = append(edgexes.Items, *edgexForDefault) + if res.Status.Ready == true { + edgexes.Items = append(edgexes.Items, *edgexForWrongServiceType) return true } return false - }, e2eConfig.GetIntervals("default", "create-edgex")...).Should(BeTrue(), func() string { return "EdgeX beijing without setting version and servicetype not ready" }) + }, e2eConfig.GetIntervals("default", "create-edgex")...).Should(BeFalse(), func() string { return "EdgeX beijing with wrong servicetype ready" }) + }) - By("Create a edgex with an already occupied nodepool") - edgexForOccupiedNodePool := &devicev1alpha1.EdgeX{ + It("Create a edgex in beijing with wrong poolname", func() { + edgexForWrongPoolName := &devicev1alpha1.EdgeX{ ObjectMeta: metav1.ObjectMeta{ - Name: "edgex-webhook-occupied-beijing", + Name: "edgex-webhook-shanghai-poolname", Namespace: "default", }, Spec: devicev1alpha1.EdgeXSpec{ - PoolName: "beijing", + PoolName: "shanghai", }, } - k8sClient.Create(ctx, edgexForDefault) - resForOccupiedNodePool := &devicev1alpha1.EdgeX{} - + k8sClient.Create(ctx, edgexForWrongPoolName) + res := &devicev1alpha1.EdgeX{} Eventually(func() bool { key := client.ObjectKey{ Namespace: "default", - Name: "edgex-webhook-occupied-beijing", + Name: "edgex-webhook-shanghai-poolname", } - if err := k8sClient.Get(ctx, key, resForOccupiedNodePool); err != nil { + if err := k8sClient.Get(ctx, key, res); err != nil { return false } - if resForOccupiedNodePool.Status.Ready == true { - edgexes.Items = append(edgexes.Items, *edgexForOccupiedNodePool) + if res.Status.Ready == true { + edgexes.Items = append(edgexes.Items, *edgexForWrongPoolName) return true } return false - }, e2eConfig.GetIntervals("default", "create-edgex")...).Should(BeFalse(), func() string { return "EdgeX beijing with an already occupied nodepool ready" }) + + }, e2eConfig.GetIntervals("default", "create-edgex")...).Should(BeFalse(), func() string { return "EdgeX shanghai with wrong poolname ready" }) }) + }) func cleanupEdgex(ctx context.Context, k8sClient client.Client, edgexes *devicev1alpha1.EdgeXList) error { From bb7a1544c6776e3b3c4596243916542bd036b695 Mon Sep 17 00:00:00 2001 From: wangxye <1031989637@qq.com> Date: Thu, 30 Mar 2023 16:39:55 +0800 Subject: [PATCH 5/8] webhook code clean --- config/webhook/manifests.yaml | 40 +++++++++++++++++++ controllers/utils/fieldindexer.go | 3 +- .../edgex/v1alpha1/edgex_webhook_test.go | 17 ++++++-- 3 files changed, 54 insertions(+), 6 deletions(-) diff --git a/config/webhook/manifests.yaml b/config/webhook/manifests.yaml index c4e7a51..55a26af 100644 --- a/config/webhook/manifests.yaml +++ b/config/webhook/manifests.yaml @@ -27,6 +27,26 @@ webhooks: resources: - edgexes sideEffects: None +- admissionReviewVersions: + - v1 + clientConfig: + service: + name: webhook-service + namespace: system + path: /mutate-device-openyurt-io-v1alpha1-edgex + failurePolicy: Fail + name: medgex.kb.io.v1alpha1 + rules: + - apiGroups: + - device.openyurt.io + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - edgexes + sideEffects: None --- apiVersion: admissionregistration.k8s.io/v1 @@ -56,3 +76,23 @@ webhooks: resources: - edgexes sideEffects: None +- admissionReviewVersions: + - v1 + clientConfig: + service: + name: webhook-service + namespace: system + path: /validate-device-openyurt-io-v1alpha1-edgex + failurePolicy: Fail + name: vedgex.kb.io.v1alpha1 + rules: + - apiGroups: + - device.openyurt.io + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - edgexes + sideEffects: None diff --git a/controllers/utils/fieldindexer.go b/controllers/utils/fieldindexer.go index 4f47a32..181b349 100644 --- a/controllers/utils/fieldindexer.go +++ b/controllers/utils/fieldindexer.go @@ -18,7 +18,6 @@ package util import ( "context" - "sync" "github.com/openyurtio/yurt-edgex-manager/api/v1alpha1" "github.com/openyurtio/yurt-edgex-manager/api/v1alpha2" @@ -30,7 +29,7 @@ const ( IndexerPathForNodepoolv2 = "spec.poolname" ) -var registerOnce sync.Once +// var registerOnce sync.Once func RegisterFieldIndexers(fi client.FieldIndexer) error { register := func(obj client.Object, path string) error { diff --git a/pkg/webhook/edgex/v1alpha1/edgex_webhook_test.go b/pkg/webhook/edgex/v1alpha1/edgex_webhook_test.go index a939c3f..e84cd5c 100644 --- a/pkg/webhook/edgex/v1alpha1/edgex_webhook_test.go +++ b/pkg/webhook/edgex/v1alpha1/edgex_webhook_test.go @@ -17,8 +17,20 @@ limitations under the License. package edgex import ( - "github.com/openyurtio/yurt-edgex-manager/api/v1alpha1" + "context" + "testing" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + v1 "github.com/openyurtio/api/apps/v1alpha1" + + "github.com/openyurtio/yurt-edgex-manager/api/v1alpha1" + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/client/fake" + + clientgoscheme "k8s.io/client-go/kubernetes/scheme" ) var defaultEdgeX = &v1alpha1.EdgeX{ @@ -31,7 +43,6 @@ var defaultEdgeX = &v1alpha1.EdgeX{ }, } -/** func TestEdgeXDefaulter(t *testing.T) { webhook := &EdgeXHandler{} if err := webhook.Default(context.TODO(), defaultEdgeX); err != nil { @@ -110,5 +121,3 @@ func TestEdgeXValidator(t *testing.T) { } } - -**/ From 1cd9a064c28fe8cf1a4bd50c6dff960970adf3fd Mon Sep 17 00:00:00 2001 From: wangxye <1031989637@qq.com> Date: Thu, 30 Mar 2023 16:54:14 +0800 Subject: [PATCH 6/8] feature webhook rebuild: supports v2 and replaces v1 --- config/webhook/manifests.yaml | 40 ------ pkg/webhook/edgex/v1alpha1/edgex_webhook.go | 130 +----------------- .../edgex/v1alpha1/edgex_webhook_test.go | 123 ----------------- test/e2e/testconfig.yaml | 2 +- 4 files changed, 2 insertions(+), 293 deletions(-) delete mode 100644 pkg/webhook/edgex/v1alpha1/edgex_webhook_test.go diff --git a/config/webhook/manifests.yaml b/config/webhook/manifests.yaml index 55a26af..c4e7a51 100644 --- a/config/webhook/manifests.yaml +++ b/config/webhook/manifests.yaml @@ -27,26 +27,6 @@ webhooks: resources: - edgexes sideEffects: None -- admissionReviewVersions: - - v1 - clientConfig: - service: - name: webhook-service - namespace: system - path: /mutate-device-openyurt-io-v1alpha1-edgex - failurePolicy: Fail - name: medgex.kb.io.v1alpha1 - rules: - - apiGroups: - - device.openyurt.io - apiVersions: - - v1alpha1 - operations: - - CREATE - - UPDATE - resources: - - edgexes - sideEffects: None --- apiVersion: admissionregistration.k8s.io/v1 @@ -76,23 +56,3 @@ webhooks: resources: - edgexes sideEffects: None -- admissionReviewVersions: - - v1 - clientConfig: - service: - name: webhook-service - namespace: system - path: /validate-device-openyurt-io-v1alpha1-edgex - failurePolicy: Fail - name: vedgex.kb.io.v1alpha1 - rules: - - apiGroups: - - device.openyurt.io - apiVersions: - - v1alpha1 - operations: - - CREATE - - UPDATE - resources: - - edgexes - sideEffects: None diff --git a/pkg/webhook/edgex/v1alpha1/edgex_webhook.go b/pkg/webhook/edgex/v1alpha1/edgex_webhook.go index adaaacc..13d6818 100644 --- a/pkg/webhook/edgex/v1alpha1/edgex_webhook.go +++ b/pkg/webhook/edgex/v1alpha1/edgex_webhook.go @@ -17,147 +17,19 @@ limitations under the License. package edgex import ( - "context" - "fmt" - - unitv1alpha1 "github.com/openyurtio/api/apps/v1alpha1" - corev1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/util/validation/field" - - apierrors "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apimachinery/pkg/runtime" ctrl "sigs.k8s.io/controller-runtime" - "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/webhook" "github.com/openyurtio/yurt-edgex-manager/api/v1alpha1" - util "github.com/openyurtio/yurt-edgex-manager/controllers/utils" + "sigs.k8s.io/controller-runtime/pkg/client" ) // SetupWebhookWithManager sets up Cluster webhooks. func (webhook *EdgeXHandler) SetupWebhookWithManager(mgr ctrl.Manager) error { return ctrl.NewWebhookManagedBy(mgr). For(&v1alpha1.EdgeX{}). - WithDefaulter(webhook). - WithValidator(webhook). Complete() } -//+kubebuilder:rbac:groups=apps.openyurt.io,resources=nodepools,verbs=list;watch - -// Cluster implements a validating and defaulting webhook for Cluster. type EdgeXHandler struct { Client client.Client } - -//+kubebuilder:webhook:path=/mutate-device-openyurt-io-v1alpha1-edgex,mutating=true,failurePolicy=fail,sideEffects=None,groups=device.openyurt.io,resources=edgexes,verbs=create;update,versions=v1alpha1,name=medgex.kb.io.v1alpha1,admissionReviewVersions=v1 - -var _ webhook.CustomDefaulter = &EdgeXHandler{} - -//+kubebuilder:webhook:path=/validate-device-openyurt-io-v1alpha1-edgex,mutating=false,failurePolicy=fail,sideEffects=None,groups=device.openyurt.io,resources=edgexes,verbs=create;update,versions=v1alpha1,name=vedgex.kb.io.v1alpha1,admissionReviewVersions=v1 - -var _ webhook.CustomValidator = &EdgeXHandler{} - -// Default satisfies the defaulting webhook interface. -func (webhook *EdgeXHandler) Default(ctx context.Context, obj runtime.Object) error { - edgex, ok := obj.(*v1alpha1.EdgeX) - if !ok { - return apierrors.NewBadRequest(fmt.Sprintf("expected a EdgeX but got a %T", obj)) - } - //set the default version - if edgex.Spec.Version == "" { - edgex.Spec.Version = "jakarta" - } - //set the default ServiceType - if edgex.Spec.ServiceType == "" { - edgex.Spec.ServiceType = corev1.ServiceTypeClusterIP - } - return nil -} - -// ValidateCreate implements webhook.CustomValidator so a webhook will be registered for the type. -func (webhook *EdgeXHandler) ValidateCreate(ctx context.Context, obj runtime.Object) error { - edgex, ok := obj.(*v1alpha1.EdgeX) - if !ok { - return apierrors.NewBadRequest(fmt.Sprintf("expected a Cluster but got a %T", obj)) - } - if allErrs := webhook.validate(ctx, edgex); len(allErrs) > 0 { - return apierrors.NewInvalid(v1alpha1.GroupVersion.WithKind("EdgeX").GroupKind(), edgex.Name, allErrs) - } - return nil -} - -// ValidateUpdate implements webhook.CustomValidator so a webhook will be registered for the type. -func (webhook *EdgeXHandler) ValidateUpdate(ctx context.Context, oldObj, newObj runtime.Object) error { - newEdgex, ok := newObj.(*v1alpha1.EdgeX) - if !ok { - return apierrors.NewBadRequest(fmt.Sprintf("expected a Cluster but got a %T", newObj)) - } - oldEdgex, ok := oldObj.(*v1alpha1.EdgeX) - if !ok { - return apierrors.NewBadRequest(fmt.Sprintf("expected a Cluster but got a %T", oldObj)) - } - newErrorList := webhook.validate(ctx, newEdgex) - oldErrorList := webhook.validate(ctx, oldEdgex) - - if allErrs := append(newErrorList, oldErrorList...); len(allErrs) > 0 { - return apierrors.NewInvalid(v1alpha1.GroupVersion.WithKind("EdgeX").GroupKind(), newEdgex.Name, allErrs) - } - return nil -} - -// ValidateDelete implements webhook.CustomValidator so a webhook will be registered for the type. -func (webhook *EdgeXHandler) ValidateDelete(_ context.Context, obj runtime.Object) error { - return nil -} - -// validate validates a EdgeX -func (webhook *EdgeXHandler) validate(ctx context.Context, edgex *v1alpha1.EdgeX) field.ErrorList { - // verify the version - // if !(edgex.Spec.Version == "jakarta" || edgex.Spec.Version == "hanoi") { - // return field.ErrorList{ - // field.Invalid(field.NewPath("spec", "version"), edgex.Spec.Version, "must be one of jakarta, hanoi"), - // } - // } - // verify that the poolname is a right nodepool name - nodePools := &unitv1alpha1.NodePoolList{} - if err := webhook.Client.List(ctx, nodePools); err != nil { - return field.ErrorList{ - field.Invalid(field.NewPath("spec", "poolName"), edgex.Spec.PoolName, "can not list nodepools, cause"+err.Error()), - } - } - ok := false - for _, nodePool := range nodePools.Items { - if nodePool.ObjectMeta.Name == edgex.Spec.PoolName { - ok = true - break - } - } - if !ok { - return field.ErrorList{ - field.Invalid(field.NewPath("spec", "poolName"), edgex.Spec.PoolName, "can not find the nodePool"), - } - } - // verify that no other edgex in the nodepool - var edgexes v1alpha1.EdgeXList - listOptions := client.MatchingFields{util.IndexerPathForNodepoolv1: edgex.Spec.PoolName} - if err := webhook.Client.List(ctx, &edgexes, listOptions); err != nil { - return field.ErrorList{ - field.Invalid(field.NewPath("spec", "poolName"), edgex.Spec.PoolName, "can not list edgexes, cause"+err.Error()), - } - } - for _, other := range edgexes.Items { - if edgex.Name != other.Name { - return field.ErrorList{ - field.Invalid(field.NewPath("spec", "poolName"), edgex.Spec.PoolName, "already used by other edgex instance,"), - } - } - } - // verify the ServiceType - if !(edgex.Spec.ServiceType == corev1.ServiceTypeClusterIP || edgex.Spec.ServiceType == corev1.ServiceTypeNodePort) { - return field.ErrorList{ - field.Invalid(field.NewPath("spec", "serviceType"), edgex.Spec.ServiceType, "must be one of ClusterIP, NodePort"), - } - } - return nil -} diff --git a/pkg/webhook/edgex/v1alpha1/edgex_webhook_test.go b/pkg/webhook/edgex/v1alpha1/edgex_webhook_test.go deleted file mode 100644 index e84cd5c..0000000 --- a/pkg/webhook/edgex/v1alpha1/edgex_webhook_test.go +++ /dev/null @@ -1,123 +0,0 @@ -/* -Copyright 2021. - -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 edgex - -import ( - "context" - "testing" - - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - - v1 "github.com/openyurtio/api/apps/v1alpha1" - - "github.com/openyurtio/yurt-edgex-manager/api/v1alpha1" - corev1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/runtime" - "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/client/fake" - - clientgoscheme "k8s.io/client-go/kubernetes/scheme" -) - -var defaultEdgeX = &v1alpha1.EdgeX{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test1", - Namespace: "default", - }, - Spec: v1alpha1.EdgeXSpec{ - PoolName: "beijing", - }, -} - -func TestEdgeXDefaulter(t *testing.T) { - webhook := &EdgeXHandler{} - if err := webhook.Default(context.TODO(), defaultEdgeX); err != nil { - t.Fatal(err) - } -} - -func TestEdgeXValidator(t *testing.T) { - scheme := runtime.NewScheme() - _ = clientgoscheme.AddToScheme(scheme) - _ = v1alpha1.AddToScheme(scheme) - _ = v1.AddToScheme(scheme) - - beijingNodePool := &v1.NodePool{ - ObjectMeta: metav1.ObjectMeta{ - Name: "beijing", - Namespace: "default", - }, - Spec: v1.NodePoolSpec{}, - } - hangzhouNodePool := &v1.NodePool{ - ObjectMeta: metav1.ObjectMeta{ - Name: "hangzhou", - Namespace: "default", - }, - Spec: v1.NodePoolSpec{}, - } - objs := []client.Object{beijingNodePool, hangzhouNodePool} - client := fake.NewClientBuilder().WithScheme(scheme).WithObjects(objs...).Build() - webhook := &EdgeXHandler{Client: client} - - // set default value - if err := webhook.Default(context.TODO(), defaultEdgeX); err != nil { - t.Fatal(err) - } - - //validate edgex's version - if err := webhook.ValidateCreate(context.TODO(), defaultEdgeX); err != nil { - t.Fatal("edgex should create success", err) - } - EdgeX2 := defaultEdgeX.DeepCopy() - EdgeX2.ObjectMeta.Name = "test2" - EdgeX2.Spec.Version = "test" - // if err := webhook.ValidateCreate(context.TODO(), EdgeX2); err == nil { - // t.Fatal("edgex should create fail", err) - // } - - //validate edgex's ServiceType - if err := webhook.ValidateCreate(context.TODO(), defaultEdgeX); err != nil { - t.Fatal("edgex should create success", err) - } - EdgeX2.Spec.Version = "jakarta" - EdgeX2.Spec.ServiceType = "test" - if err := webhook.ValidateCreate(context.TODO(), EdgeX2); err == nil { - t.Fatal("edgex should create fail", err) - } - - //validate edgex's poolname - EdgeX2.Spec.ServiceType = corev1.ServiceTypeClusterIP - EdgeX2.Spec.PoolName = "hangzhou" - if err := webhook.ValidateUpdate(context.TODO(), defaultEdgeX, EdgeX2); err != nil { - t.Fatal("edgex should update success", err) - } - - EdgeX2.Spec.PoolName = "shanghai" - if err := webhook.ValidateUpdate(context.TODO(), defaultEdgeX, EdgeX2); err == nil { - t.Fatal("edgex should update fail", err) - } - - objs = append(objs, defaultEdgeX) - client = fake.NewClientBuilder().WithScheme(scheme).WithObjects(objs...).Build() - webhook = &EdgeXHandler{Client: client} - EdgeX2.Spec.PoolName = "beijing" - if err := webhook.ValidateCreate(context.TODO(), EdgeX2); err == nil { - t.Fatal("edgex should create fail", err) - } - -} diff --git a/test/e2e/testconfig.yaml b/test/e2e/testconfig.yaml index 448149d..6c7df6a 100644 --- a/test/e2e/testconfig.yaml +++ b/test/e2e/testconfig.yaml @@ -35,4 +35,4 @@ variables: intervals: default/wait-dependency: ["5m", "10s"] default/create-nodepool: ["30s", "5s"] - default/create-edgex: ["9m", "10s"] + default/create-edgex: ["5m", "10s"] From 50e874a3f3498667b11dc88743638cb102a7c8d6 Mon Sep 17 00:00:00 2001 From: wangxye <1031989637@qq.com> Date: Sat, 1 Apr 2023 15:05:33 +0800 Subject: [PATCH 7/8] feature: webhook detail change --- controllers/utils/fieldindexer.go | 39 +++++++++------------ main.go | 10 +++--- pkg/webhook/edgex/edgex_webhook.go | 2 +- pkg/webhook/edgex/v1alpha1/edgex_webhook.go | 2 +- 4 files changed, 24 insertions(+), 29 deletions(-) diff --git a/controllers/utils/fieldindexer.go b/controllers/utils/fieldindexer.go index 181b349..193b038 100644 --- a/controllers/utils/fieldindexer.go +++ b/controllers/utils/fieldindexer.go @@ -18,37 +18,32 @@ package util import ( "context" + "sync" - "github.com/openyurtio/yurt-edgex-manager/api/v1alpha1" "github.com/openyurtio/yurt-edgex-manager/api/v1alpha2" "sigs.k8s.io/controller-runtime/pkg/client" ) const ( - IndexerPathForNodepoolv1 = "spec.poolname" - IndexerPathForNodepoolv2 = "spec.poolname" + IndexerPathForNodepool = "spec.poolName" ) -// var registerOnce sync.Once +var registerOnce sync.Once func RegisterFieldIndexers(fi client.FieldIndexer) error { - register := func(obj client.Object, path string) error { - return fi.IndexField(context.TODO(), obj, path, func(rawObj client.Object) []string { - switch t := rawObj.(type) { - case *v1alpha1.EdgeX: - return []string{t.Spec.PoolName} - case *v1alpha2.EdgeX: - return []string{t.Spec.PoolName} - default: - return []string{} + var err error + registerOnce.Do(func() { + // register the fieldIndexer for device + if err = fi.IndexField(context.TODO(), &v1alpha2.EdgeX{}, IndexerPathForNodepool, func(rawObj client.Object) []string { + edgex, ok := rawObj.(*v1alpha2.EdgeX) + if ok { + return []string{edgex.Spec.PoolName} } - }) - } - if err := register(&v1alpha1.EdgeX{}, IndexerPathForNodepoolv1); err != nil { - return err - } - if err := register(&v1alpha2.EdgeX{}, IndexerPathForNodepoolv2); err != nil { - return err - } - return nil + return []string{} + }); err != nil { + return + } + }) + + return err } diff --git a/main.go b/main.go index 0d6104b..baf6436 100644 --- a/main.go +++ b/main.go @@ -23,8 +23,8 @@ import ( "os" util "github.com/openyurtio/yurt-edgex-manager/controllers/utils" - edgexwebhook "github.com/openyurtio/yurt-edgex-manager/pkg/webhook/edgex" - edgexwebhookV1 "github.com/openyurtio/yurt-edgex-manager/pkg/webhook/edgex/v1alpha1" + edgexwebhookv1alpha2 "github.com/openyurtio/yurt-edgex-manager/pkg/webhook/edgex" + edgexwebhookv1alpha1 "github.com/openyurtio/yurt-edgex-manager/pkg/webhook/edgex/v1alpha1" // Import all Kubernetes client auth plugins (e.g. Azure, GCP, OIDC, etc.) // to ensure that exec-entrypoint and run can make use of them. @@ -155,13 +155,13 @@ func main() { setupLog.Error(err, "File to open the embed EdgeX manifest config") os.Exit(1) } - if err = (&edgexwebhook.EdgeXHandler{Client: mgr.GetClient(), ManifestContent: manifestContent}).SetupWebhookWithManager(mgr); err != nil { + if err = (&edgexwebhookv1alpha2.EdgeXHandler{Client: mgr.GetClient(), ManifestContent: manifestContent}).SetupWebhookWithManager(mgr); err != nil { setupLog.Error(err, "unable to create webhook v1alpha2", "webhook", "EdgeX") os.Exit(1) } - if err = (&edgexwebhookV1.EdgeXHandler{Client: mgr.GetClient()}).SetupWebhookWithManager(mgr); err != nil { - setupLog.Error(err, "unable to create webhook v1alpha2", "webhook", "EdgeX") + if err = (&edgexwebhookv1alpha1.EdgeXHandler{Client: mgr.GetClient()}).SetupWebhookWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create webhook v1alpha1", "webhook", "EdgeX") os.Exit(1) } diff --git a/pkg/webhook/edgex/edgex_webhook.go b/pkg/webhook/edgex/edgex_webhook.go index b3b7b22..57b5456 100644 --- a/pkg/webhook/edgex/edgex_webhook.go +++ b/pkg/webhook/edgex/edgex_webhook.go @@ -194,7 +194,7 @@ func (webhook *EdgeXHandler) validateEdgeXWithNodePools(ctx context.Context, edg } // verify that no other edgex in the nodepool var edgexes v1alpha2.EdgeXList - listOptions := client.MatchingFields{util.IndexerPathForNodepoolv2: edgex.Spec.PoolName} + listOptions := client.MatchingFields{util.IndexerPathForNodepool: edgex.Spec.PoolName} if err := webhook.Client.List(ctx, &edgexes, listOptions); err != nil { return field.ErrorList{ field.Invalid(field.NewPath("spec", "poolName"), edgex.Spec.PoolName, "can not list edgexes, cause"+err.Error()), diff --git a/pkg/webhook/edgex/v1alpha1/edgex_webhook.go b/pkg/webhook/edgex/v1alpha1/edgex_webhook.go index 13d6818..cfe4d31 100644 --- a/pkg/webhook/edgex/v1alpha1/edgex_webhook.go +++ b/pkg/webhook/edgex/v1alpha1/edgex_webhook.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package edgex +package v1alpha1 import ( ctrl "sigs.k8s.io/controller-runtime" From 37fd9f2fabdefba1e7195e4e02c8de2bcfbfa43b Mon Sep 17 00:00:00 2001 From: wangxye <1031989637@qq.com> Date: Sat, 1 Apr 2023 16:10:15 +0800 Subject: [PATCH 8/8] feature: webhook code standardization --- pkg/webhook/edgex/edgex_webhook.go | 2 +- pkg/webhook/edgex/edgex_webhook_test.go | 15 ++++++++++++++- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/pkg/webhook/edgex/edgex_webhook.go b/pkg/webhook/edgex/edgex_webhook.go index 57b5456..3e7a871 100644 --- a/pkg/webhook/edgex/edgex_webhook.go +++ b/pkg/webhook/edgex/edgex_webhook.go @@ -11,7 +11,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package v1alpha2 +package edgex import ( "context" diff --git a/pkg/webhook/edgex/edgex_webhook_test.go b/pkg/webhook/edgex/edgex_webhook_test.go index b4abbc3..c21730c 100644 --- a/pkg/webhook/edgex/edgex_webhook_test.go +++ b/pkg/webhook/edgex/edgex_webhook_test.go @@ -1,4 +1,17 @@ -package v1alpha2 +/* +Copyright 2021. +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 edgex import ( "context"