From bebc599f1f4ddc84662a58cbd46de29c7f65a5ff Mon Sep 17 00:00:00 2001 From: vie-serendipity <2733147505@qq.com> Date: Thu, 24 Aug 2023 09:21:27 +0800 Subject: [PATCH 01/20] adjust CRD and add wildcard --- .../apps.openyurt.io_yurtappoverriders..yaml | 142 +++++++++ .../yurt-manager-auto-generated.yaml | 54 ++++ cmd/yurt-manager/app/options/options.go | 35 ++- .../app/options/yurtappoverridercontroller.go | 60 ++++ pkg/apis/apps/v1alpha1/default.go | 9 + .../v1alpha1/yurtappoverrider_conversion.go | 45 +++ .../apps/v1alpha1/yurtappoverrider_types.go | 110 +++++++ .../apps/v1alpha1/zz_generated.deepcopy.go | 171 +++++++++++ .../controller/apis/config/types.go | 4 + .../yurtappoverrider/config/types.go | 21 ++ .../yurtappoverrider_controller.go | 203 ++++++++++++ .../yurtappoverrider_controller_test.go | 16 + .../webhook/deploymentrender/utils/util.go | 58 ++++ .../deploymentrender/utils/util_test.go | 69 +++++ .../v1alpha1/deploymentrender_default.go | 177 +++++++++++ .../v1alpha1/deploymentrender_handler.go | 58 ++++ .../v1alpha1/deploymentrender_webhook_test.go | 215 +++++++++++++ .../deploymentrender/v1alpha1/item_control.go | 39 +++ .../v1alpha1/item_control_test.go | 94 ++++++ .../v1alpha1/patch_control.go | 78 +++++ .../v1alpha1/patch_control_test.go | 105 +++++++ .../v1alpha1/yurtappconfigrender_default.go | 39 +++ .../v1alpha1/yurtappconfigrender_handler.go | 56 ++++ .../yurtappconfigrender_validation.go | 109 +++++++ test/e2e/yurt/yurtappoverrider.go | 288 ++++++++++++++++++ 25 files changed, 2241 insertions(+), 14 deletions(-) create mode 100644 charts/yurt-manager/crds/apps.openyurt.io_yurtappoverriders..yaml create mode 100644 cmd/yurt-manager/app/options/yurtappoverridercontroller.go create mode 100644 pkg/apis/apps/v1alpha1/yurtappoverrider_conversion.go create mode 100644 pkg/apis/apps/v1alpha1/yurtappoverrider_types.go create mode 100644 pkg/yurtmanager/controller/yurtappoverrider/config/types.go create mode 100644 pkg/yurtmanager/controller/yurtappoverrider/yurtappoverrider_controller.go create mode 100644 pkg/yurtmanager/controller/yurtappoverrider/yurtappoverrider_controller_test.go create mode 100644 pkg/yurtmanager/webhook/deploymentrender/utils/util.go create mode 100644 pkg/yurtmanager/webhook/deploymentrender/utils/util_test.go create mode 100644 pkg/yurtmanager/webhook/deploymentrender/v1alpha1/deploymentrender_default.go create mode 100644 pkg/yurtmanager/webhook/deploymentrender/v1alpha1/deploymentrender_handler.go create mode 100644 pkg/yurtmanager/webhook/deploymentrender/v1alpha1/deploymentrender_webhook_test.go create mode 100644 pkg/yurtmanager/webhook/deploymentrender/v1alpha1/item_control.go create mode 100644 pkg/yurtmanager/webhook/deploymentrender/v1alpha1/item_control_test.go create mode 100644 pkg/yurtmanager/webhook/deploymentrender/v1alpha1/patch_control.go create mode 100644 pkg/yurtmanager/webhook/deploymentrender/v1alpha1/patch_control_test.go create mode 100644 pkg/yurtmanager/webhook/yurtappoverrider/v1alpha1/yurtappconfigrender_default.go create mode 100644 pkg/yurtmanager/webhook/yurtappoverrider/v1alpha1/yurtappconfigrender_handler.go create mode 100644 pkg/yurtmanager/webhook/yurtappoverrider/v1alpha1/yurtappconfigrender_validation.go create mode 100644 test/e2e/yurt/yurtappoverrider.go diff --git a/charts/yurt-manager/crds/apps.openyurt.io_yurtappoverriders..yaml b/charts/yurt-manager/crds/apps.openyurt.io_yurtappoverriders..yaml new file mode 100644 index 00000000000..dcaa084ebc5 --- /dev/null +++ b/charts/yurt-manager/crds/apps.openyurt.io_yurtappoverriders..yaml @@ -0,0 +1,142 @@ +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.7.0 + creationTimestamp: null + name: yurtappoverriders.apps.openyurt.io +spec: + group: apps.openyurt.io + names: + kind: YurtAppOverrider + listKind: YurtAppOverriderList + plural: yurtappoverriders + shortNames: + - yacr + singular: yurtappoverrider + scope: Namespaced + versions: + - additionalPrinterColumns: + - description: The subject kind of this overrider. + jsonPath: .subject.kind + name: Subject + type: string + - description: CreationTimestamp is a timestamp representing the server time when + this object was created. It is not guaranteed to be set in happens-before + order across separate operations. Clients may not set this value. It is represented + in RFC3339 form and is in UTC. + jsonPath: .metadata.creationTimestamp + name: AGE + type: date + name: v1alpha1 + schema: + openAPIV3Schema: + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + entries: + items: + description: Describe detailed multi-region configuration of the subject + Entry describe a set of nodepools and their shared or identical configurations + properties: + items: + items: + description: Item represents configuration to be injected. Only + one of its members may be specified. + properties: + image: + description: ImageItem specifies the corresponding container + and the claimed image + properties: + containerName: + description: ContainerName represents name of the container + in which the Image will be replaced + type: string + imageClaim: + description: ImageClaim represents the claimed image name + which is injected into the container above + type: string + required: + - containerName + - imageClaim + type: object + replicas: + format: int32 + type: integer + type: object + type: array + patches: + description: Convert Patch struct into json patch operation + items: + properties: + operation: + description: type represents the operation default is strategic + merge patch + enum: + - add + - remove + - replace + type: string + path: + description: Path represents the path in the json patch + type: string + value: + description: Indicates the patch for the template + x-kubernetes-preserve-unknown-fields: true + required: + - operation + - path + type: object + type: array + pools: + items: + type: string + type: array + required: + - pools + type: object + type: array + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + subject: + description: Describe the object Entries belongs + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource + this object represents. Servers may infer this from the endpoint + the client submits requests to. Cannot be updated. In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + name: + description: Name is the name of YurtAppSet or YurtAppDaemon + type: string + required: + - name + type: object + required: + - entries + - subject + type: object + served: true + storage: true + subresources: + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] diff --git a/charts/yurt-manager/templates/yurt-manager-auto-generated.yaml b/charts/yurt-manager/templates/yurt-manager-auto-generated.yaml index 46800849e86..76afe100ea6 100644 --- a/charts/yurt-manager/templates/yurt-manager-auto-generated.yaml +++ b/charts/yurt-manager/templates/yurt-manager-auto-generated.yaml @@ -159,6 +159,18 @@ rules: - get - patch - update +- apiGroups: + - apps.openyurt.io + resources: + - yurtappoverriders + verbs: + - create + - delete + - get + - list + - patch + - update + - watch - apiGroups: - apps.openyurt.io resources: @@ -659,6 +671,27 @@ webhooks: resources: - yurtstaticsets sideEffects: None +- admissionReviewVersions: + - v1 + - v1beta1 + clientConfig: + service: + name: yurt-manager-webhook-service + namespace: {{ .Release.Namespace }} + path: /mutate-apps-openyurt-io-v1alpha1-yurtappoverrider + failurePolicy: Fail + name: mutate.apps.v1alpha1.yurtappoverrider.openyurt.io + rules: + - apiGroups: + - apps.openyurt.io + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - yurtappoverriders + sideEffects: None --- apiVersion: admissionregistration.k8s.io/v1 kind: ValidatingWebhookConfiguration @@ -830,3 +863,24 @@ webhooks: resources: - yurtstaticsets sideEffects: None +- admissionReviewVersions: + - v1 + - v1beta1 + clientConfig: + service: + name: yurt-manager-webhook-service + namespace: {{ .Release.Namespace }} + path: /validate-apps-openyurt-io-v1alpha1-yurtappoverrider + failurePolicy: Fail + name: validate.apps.v1alpha1.yurtappoverrider.openyurt.io + rules: + - apiGroups: + - apps.openyurt.io + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - yurtappoverriders + sideEffects: None diff --git a/cmd/yurt-manager/app/options/options.go b/cmd/yurt-manager/app/options/options.go index 369c5ff944f..bb448bf0dec 100644 --- a/cmd/yurt-manager/app/options/options.go +++ b/cmd/yurt-manager/app/options/options.go @@ -25,26 +25,28 @@ import ( // YurtManagerOptions is the main context object for the yurt-manager. type YurtManagerOptions struct { - Generic *GenericOptions - NodePoolController *NodePoolControllerOptions - GatewayPickupController *GatewayPickupControllerOptions - YurtStaticSetController *YurtStaticSetControllerOptions - YurtAppSetController *YurtAppSetControllerOptions - YurtAppDaemonController *YurtAppDaemonControllerOptions - PlatformAdminController *PlatformAdminControllerOptions + Generic *GenericOptions + NodePoolController *NodePoolControllerOptions + GatewayPickupController *GatewayPickupControllerOptions + YurtStaticSetController *YurtStaticSetControllerOptions + YurtAppSetController *YurtAppSetControllerOptions + YurtAppDaemonController *YurtAppDaemonControllerOptions + PlatformAdminController *PlatformAdminControllerOptions + YurtAppOverriderController *YurtAppOverriderControllerOptions } // NewYurtManagerOptions creates a new YurtManagerOptions with a default config. func NewYurtManagerOptions() (*YurtManagerOptions, error) { s := YurtManagerOptions{ - Generic: NewGenericOptions(), - NodePoolController: NewNodePoolControllerOptions(), - GatewayPickupController: NewGatewayPickupControllerOptions(), - YurtStaticSetController: NewYurtStaticSetControllerOptions(), - YurtAppSetController: NewYurtAppSetControllerOptions(), - YurtAppDaemonController: NewYurtAppDaemonControllerOptions(), - PlatformAdminController: NewPlatformAdminControllerOptions(), + Generic: NewGenericOptions(), + NodePoolController: NewNodePoolControllerOptions(), + GatewayPickupController: NewGatewayPickupControllerOptions(), + YurtStaticSetController: NewYurtStaticSetControllerOptions(), + YurtAppSetController: NewYurtAppSetControllerOptions(), + YurtAppDaemonController: NewYurtAppDaemonControllerOptions(), + PlatformAdminController: NewPlatformAdminControllerOptions(), + YurtAppOverriderController: NewYurtAppOverriderControllerOptions(), } return &s, nil @@ -58,6 +60,7 @@ func (y *YurtManagerOptions) Flags() cliflag.NamedFlagSets { y.YurtStaticSetController.AddFlags(fss.FlagSet("yurtstaticset controller")) y.YurtAppDaemonController.AddFlags(fss.FlagSet("yurtappdaemon controller")) y.PlatformAdminController.AddFlags(fss.FlagSet("iot controller")) + y.YurtAppOverriderController.AddFlags(fss.FlagSet("yurtappoverrider controller")) // Please Add Other controller flags @kadisi return fss @@ -72,6 +75,7 @@ func (y *YurtManagerOptions) Validate() error { errs = append(errs, y.YurtStaticSetController.Validate()...) errs = append(errs, y.YurtAppDaemonController.Validate()...) errs = append(errs, y.PlatformAdminController.Validate()...) + errs = append(errs, y.YurtAppOverriderController.Validate()...) return utilerrors.NewAggregate(errs) } @@ -92,6 +96,9 @@ func (y *YurtManagerOptions) ApplyTo(c *config.Config) error { if err := y.PlatformAdminController.ApplyTo(&c.ComponentConfig.PlatformAdminController); err != nil { return err } + if err := y.YurtAppOverriderController.ApplyTo(&c.ComponentConfig.YurtAppOverriderController); err != nil { + return err + } return nil } diff --git a/cmd/yurt-manager/app/options/yurtappoverridercontroller.go b/cmd/yurt-manager/app/options/yurtappoverridercontroller.go new file mode 100644 index 00000000000..da9fd8f60ee --- /dev/null +++ b/cmd/yurt-manager/app/options/yurtappoverridercontroller.go @@ -0,0 +1,60 @@ +/* +Copyright 2023 The OpenYurt Authors. + +Licensed under the Apache License, Version 2.0 (the License); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an AS IS BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package options + +import ( + "github.com/spf13/pflag" + + "github.com/openyurtio/openyurt/pkg/yurtmanager/controller/yurtappoverrider/config" +) + +type YurtAppOverriderControllerOptions struct { + *config.YurtAppOverriderControllerConfiguration +} + +func NewYurtAppOverriderControllerOptions() *YurtAppOverriderControllerOptions { + return &YurtAppOverriderControllerOptions{ + &config.YurtAppOverriderControllerConfiguration{}, + } +} + +// AddFlags adds flags related to nodepool for yurt-manager to the specified FlagSet. +func (n *YurtAppOverriderControllerOptions) AddFlags(fs *pflag.FlagSet) { + if n == nil { + return + } + + //fs.BoolVar(&n.CreateDefaultPool, "create-default-pool", n.CreateDefaultPool, "Create default cloud/edge pools if indicated.") +} + +// ApplyTo fills up nodepool config with options. +func (o *YurtAppOverriderControllerOptions) ApplyTo(cfg *config.YurtAppOverriderControllerConfiguration) error { + if o == nil { + return nil + } + + return nil +} + +// Validate checks validation of YurtAppOverriderControllerOptions. +func (o *YurtAppOverriderControllerOptions) Validate() []error { + if o == nil { + return nil + } + errs := []error{} + return errs +} diff --git a/pkg/apis/apps/v1alpha1/default.go b/pkg/apis/apps/v1alpha1/default.go index 8069f55e412..f18d1c87420 100644 --- a/pkg/apis/apps/v1alpha1/default.go +++ b/pkg/apis/apps/v1alpha1/default.go @@ -254,3 +254,12 @@ func SetDefaultsYurtAppDaemon(obj *YurtAppDaemon) { SetDefaultPodSpec(&obj.Spec.WorkloadTemplate.DeploymentTemplate.Spec.Template.Spec) } } + +// SetDefaultsYurtAppOverrider set default values for YurtAppOverrider. +func SetDefaultsYurtAppOverrider(obj *YurtAppOverrider) { + // example for set default value for YurtAppOverrider + + //if len(obj.Subject.Default) == 0 { + // obj.Spec.Default = "set-default-value-0" + //} +} diff --git a/pkg/apis/apps/v1alpha1/yurtappoverrider_conversion.go b/pkg/apis/apps/v1alpha1/yurtappoverrider_conversion.go new file mode 100644 index 00000000000..0ef677e1595 --- /dev/null +++ b/pkg/apis/apps/v1alpha1/yurtappoverrider_conversion.go @@ -0,0 +1,45 @@ +/* +Copyright 2023 The OpenYurt Authors. + +Licensed under the Apache License, Version 2.0 (the License); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an AS IS BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1alpha1 + +/* +Implementing the hub method is pretty easy -- we just have to add an empty +method called Hub() to serve as a +[marker](https://pkg.go.dev/sigs.k8s.io/controller-runtime/pkg/conversion?tab=doc#Hub). +*/ + +// NOTE !!!!!! @kadisi +// If this version is storageversion, you only need to uncommand this method + +// Hub marks this type as a conversion hub. +//func (*YurtAppOverrider) Hub() {} + +// NOTE !!!!!!! @kadisi +// If this version is not storageversion, you need to implement the ConvertTo and ConvertFrom methods + +// need import "sigs.k8s.io/controller-runtime/pkg/conversion" +//func (src *YurtAppOverrider) ConvertTo(dstRaw conversion.Hub) error { +// return nil +//} + +// NOTE !!!!!!! @kadisi +// If this version is not storageversion, you need to implement the ConvertTo and ConvertFrom methods + +// need import "sigs.k8s.io/controller-runtime/pkg/conversion" +//func (dst *YurtAppOverrider) ConvertFrom(srcRaw conversion.Hub) error { +// return nil +//} diff --git a/pkg/apis/apps/v1alpha1/yurtappoverrider_types.go b/pkg/apis/apps/v1alpha1/yurtappoverrider_types.go new file mode 100644 index 00000000000..19936822eef --- /dev/null +++ b/pkg/apis/apps/v1alpha1/yurtappoverrider_types.go @@ -0,0 +1,110 @@ +/* +Copyright 2023 The OpenYurt Authors. + +Licensed under the Apache License, Version 2.0 (the License); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an AS IS BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1alpha1 + +import ( + apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN! +// NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized. + +// ImageItem specifies the corresponding container and the claimed image +type ImageItem struct { + // ContainerName represents name of the container + // in which the Image will be replaced + ContainerName string `json:"containerName"` + // ImageClaim represents the claimed image name + //which is injected into the container above + ImageClaim string `json:"imageClaim"` +} + +// Item represents configuration to be injected. +// Only one of its members may be specified. +type Item struct { + // +optional + Image *ImageItem `json:"image,omitempty"` + // +optional + Replicas *int32 `json:"replicas,omitempty"` +} + +type Operation string + +const ( + Default Operation = "default" // strategic merge patch + ADD Operation = "add" // json patch + REMOVE Operation = "remove" // json patch + REPLACE Operation = "replace" // json patch +) + +type Patch struct { + // Path represents the path in the json patch + Path string `json:"path"` + // type represents the operation + // default is strategic merge patch + // +kubebuilder:validation:Enum=add;remove;replace + Operation Operation `json:"operation"` + // Indicates the patch for the template + // +optional + Value apiextensionsv1.JSON `json:"value,omitempty"` +} + +// Describe detailed multi-region configuration of the subject +// Entry describe a set of nodepools and their shared or identical configurations +type Entry struct { + Pools []string `json:"pools"` + // +optional + Items []Item `json:"items,omitempty"` + // Convert Patch struct into json patch operation + // +optional + Patches []Patch `json:"patches,omitempty"` +} + +// Describe the object Entries belongs +type Subject struct { + metav1.TypeMeta `json:",inline"` + // Name is the name of YurtAppSet or YurtAppDaemon + Name string `json:"name"` +} + +// +genclient +// +kubebuilder:object:root=true +// +kubebuilder:subresource:status +// +kubebuilder:resource:shortName=yacr +// +kubebuilder:printcolumn:name="Subject",type="string",JSONPath=".subject.kind",description="The subject kind of this overrider." +// +kubebuilder:printcolumn:name="AGE",type="date",JSONPath=".metadata.creationTimestamp",description="CreationTimestamp is a timestamp representing the server time when this object was created. It is not guaranteed to be set in happens-before order across separate operations. Clients may not set this value. It is represented in RFC3339 form and is in UTC." + +type YurtAppOverrider struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + Subject Subject `json:"subject"` + Entries []Entry `json:"entries"` +} + +//+kubebuilder:object:root=true + +// YurtAppOverriderList contains a list of YurtAppOverrider +type YurtAppOverriderList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []YurtAppOverrider `json:"items"` +} + +func init() { + SchemeBuilder.Register(&YurtAppOverrider{}, &YurtAppOverriderList{}) +} diff --git a/pkg/apis/apps/v1alpha1/zz_generated.deepcopy.go b/pkg/apis/apps/v1alpha1/zz_generated.deepcopy.go index f732bdcaa9b..bbd03f00dfb 100644 --- a/pkg/apis/apps/v1alpha1/zz_generated.deepcopy.go +++ b/pkg/apis/apps/v1alpha1/zz_generated.deepcopy.go @@ -45,6 +45,80 @@ func (in *DeploymentTemplateSpec) DeepCopy() *DeploymentTemplateSpec { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Entry) DeepCopyInto(out *Entry) { + *out = *in + if in.Pools != nil { + in, out := &in.Pools, &out.Pools + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]Item, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.Patches != nil { + in, out := &in.Patches, &out.Patches + *out = make([]Patch, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Entry. +func (in *Entry) DeepCopy() *Entry { + if in == nil { + return nil + } + out := new(Entry) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ImageItem) DeepCopyInto(out *ImageItem) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ImageItem. +func (in *ImageItem) DeepCopy() *ImageItem { + if in == nil { + return nil + } + out := new(ImageItem) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Item) DeepCopyInto(out *Item) { + *out = *in + if in.Image != nil { + in, out := &in.Image, &out.Image + *out = new(ImageItem) + **out = **in + } + if in.Replicas != nil { + in, out := &in.Replicas, &out.Replicas + *out = new(int32) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Item. +func (in *Item) DeepCopy() *Item { + if in == nil { + return nil + } + out := new(Item) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *NodePool) DeepCopyInto(out *NodePool) { *out = *in @@ -165,6 +239,22 @@ func (in *NodePoolStatus) DeepCopy() *NodePoolStatus { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Patch) DeepCopyInto(out *Patch) { + *out = *in + in.Value.DeepCopyInto(&out.Value) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Patch. +func (in *Patch) DeepCopy() *Patch { + if in == nil { + return nil + } + out := new(Patch) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *Pool) DeepCopyInto(out *Pool) { *out = *in @@ -215,6 +305,22 @@ func (in *StatefulSetTemplateSpec) DeepCopy() *StatefulSetTemplateSpec { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Subject) DeepCopyInto(out *Subject) { + *out = *in + out.TypeMeta = in.TypeMeta +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Subject. +func (in *Subject) DeepCopy() *Subject { + if in == nil { + return nil + } + out := new(Subject) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *Topology) DeepCopyInto(out *Topology) { *out = *in @@ -400,6 +506,71 @@ func (in *YurtAppDaemonStatus) DeepCopy() *YurtAppDaemonStatus { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *YurtAppOverrider) DeepCopyInto(out *YurtAppOverrider) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + out.Subject = in.Subject + if in.Entries != nil { + in, out := &in.Entries, &out.Entries + *out = make([]Entry, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new YurtAppOverrider. +func (in *YurtAppOverrider) DeepCopy() *YurtAppOverrider { + if in == nil { + return nil + } + out := new(YurtAppOverrider) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *YurtAppOverrider) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *YurtAppOverriderList) DeepCopyInto(out *YurtAppOverriderList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]YurtAppOverrider, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new YurtAppOverriderList. +func (in *YurtAppOverriderList) DeepCopy() *YurtAppOverriderList { + if in == nil { + return nil + } + out := new(YurtAppOverriderList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *YurtAppOverriderList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *YurtAppSet) DeepCopyInto(out *YurtAppSet) { *out = *in diff --git a/pkg/yurtmanager/controller/apis/config/types.go b/pkg/yurtmanager/controller/apis/config/types.go index 3be8f661277..aaeb974bf98 100644 --- a/pkg/yurtmanager/controller/apis/config/types.go +++ b/pkg/yurtmanager/controller/apis/config/types.go @@ -23,6 +23,7 @@ import ( platformadminconfig "github.com/openyurtio/openyurt/pkg/yurtmanager/controller/platformadmin/config" gatewaypickupconfig "github.com/openyurtio/openyurt/pkg/yurtmanager/controller/raven/gatewaypickup/config" yurtappdaemonconfig "github.com/openyurtio/openyurt/pkg/yurtmanager/controller/yurtappdaemon/config" + yurtappoverriderconfig "github.com/openyurtio/openyurt/pkg/yurtmanager/controller/yurtappoverrider/config" yurtappsetconfig "github.com/openyurtio/openyurt/pkg/yurtmanager/controller/yurtappset/config" yurtstaticsetconfig "github.com/openyurtio/openyurt/pkg/yurtmanager/controller/yurtstaticset/config" ) @@ -48,6 +49,9 @@ type YurtManagerConfiguration struct { // PlatformAdminControllerConfiguration holds configuration for PlatformAdminController related features. PlatformAdminController platformadminconfig.PlatformAdminControllerConfiguration + + // YurtAppOverriderControllerConfiguration holds configuration for YurtAppOverriderController related features. + YurtAppOverriderController yurtappoverriderconfig.YurtAppOverriderControllerConfiguration } type GenericConfiguration struct { diff --git a/pkg/yurtmanager/controller/yurtappoverrider/config/types.go b/pkg/yurtmanager/controller/yurtappoverrider/config/types.go new file mode 100644 index 00000000000..e11801f07f3 --- /dev/null +++ b/pkg/yurtmanager/controller/yurtappoverrider/config/types.go @@ -0,0 +1,21 @@ +/* +Copyright 2023 The OpenYurt Authors. + +Licensed under the Apache License, Version 2.0 (the License); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an AS IS BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package config + +// YurtAppOverriderControllerConfiguration contains elements describing YurtAppOverriderController. +type YurtAppOverriderControllerConfiguration struct { +} diff --git a/pkg/yurtmanager/controller/yurtappoverrider/yurtappoverrider_controller.go b/pkg/yurtmanager/controller/yurtappoverrider/yurtappoverrider_controller.go new file mode 100644 index 00000000000..da04296d9a8 --- /dev/null +++ b/pkg/yurtmanager/controller/yurtappoverrider/yurtappoverrider_controller.go @@ -0,0 +1,203 @@ +/* +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 yurtappoverrider + +import ( + "context" + "flag" + "fmt" + + v1 "k8s.io/api/apps/v1" + "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/client-go/tools/record" + "k8s.io/klog/v2" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/controller" + "sigs.k8s.io/controller-runtime/pkg/event" + "sigs.k8s.io/controller-runtime/pkg/handler" + "sigs.k8s.io/controller-runtime/pkg/manager" + "sigs.k8s.io/controller-runtime/pkg/predicate" + "sigs.k8s.io/controller-runtime/pkg/reconcile" + "sigs.k8s.io/controller-runtime/pkg/source" + + appconfig "github.com/openyurtio/openyurt/cmd/yurt-manager/app/config" + appsv1alpha1 "github.com/openyurtio/openyurt/pkg/apis/apps/v1alpha1" + "github.com/openyurtio/openyurt/pkg/yurtmanager/controller/yurtappoverrider/config" +) + +func init() { + flag.IntVar(&concurrentReconciles, "yurtappoverrider-workers", concurrentReconciles, "Max concurrent workers for YurtAppOverrider controller.") +} + +var ( + concurrentReconciles = 3 + controllerResource = appsv1alpha1.SchemeGroupVersion.WithResource("yurtappoverriders") +) + +const ( + ControllerName = "YurtAppOverrider-controller" +) + +func Format(format string, args ...interface{}) string { + s := fmt.Sprintf(format, args...) + return fmt.Sprintf("%s: %s", ControllerName, s) +} + +// Add creates a new YurtAppOverrider 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(c *appconfig.CompletedConfig, mgr manager.Manager) error { + if _, err := mgr.GetRESTMapper().KindFor(controllerResource); err != nil { + klog.Infof("resource %s doesn't exist", controllerResource.String()) + return err + } + + return add(mgr, newReconciler(c, mgr)) +} + +var _ reconcile.Reconciler = &ReconcileYurtAppOverrider{} + +// ReconcileYurtAppOverrider reconciles a YurtAppOverrider object +type ReconcileYurtAppOverrider struct { + client.Client + scheme *runtime.Scheme + recorder record.EventRecorder + Configration config.YurtAppOverriderControllerConfiguration +} + +// newReconciler returns a new reconcile.Reconciler +func newReconciler(c *appconfig.CompletedConfig, mgr manager.Manager) reconcile.Reconciler { + return &ReconcileYurtAppOverrider{ + Client: mgr.GetClient(), + scheme: mgr.GetScheme(), + recorder: mgr.GetEventRecorderFor(ControllerName), + Configration: c.ComponentConfig.YurtAppOverriderController, + } +} + +// add adds a new Controller to mgr with r as the reconcile.Reconciler +func add(mgr manager.Manager, r reconcile.Reconciler) error { + // Create a new controller + c, err := controller.New(ControllerName, mgr, controller.Options{ + Reconciler: r, MaxConcurrentReconciles: concurrentReconciles, + }) + if err != nil { + return err + } + + yurtappoverriderPredicates := predicate.Funcs{ + CreateFunc: func(evt event.CreateEvent) bool { + obj, ok := evt.Object.(*appsv1alpha1.YurtAppOverrider) + if !ok { + return ok + } + if err := r.(*ReconcileYurtAppOverrider).updatePools(obj); err != nil { + klog.Errorf("fail to update deployments belonging to obj: %v", err) + } + return true + }, + DeleteFunc: func(evt event.DeleteEvent) bool { + obj, ok := evt.Object.(*appsv1alpha1.YurtAppOverrider) + if !ok { + return ok + } + if err := r.(*ReconcileYurtAppOverrider).updatePools(obj); err != nil { + klog.Errorf("fail to update deployments belonging to obj: %v", err) + } + return true + }, + UpdateFunc: func(evt event.UpdateEvent) bool { + obj, ok := evt.ObjectOld.(*appsv1alpha1.YurtAppOverrider) + if !ok { + return ok + } + if err := r.(*ReconcileYurtAppOverrider).updatePools(obj); err != nil { + klog.Errorf("fail to update deployments belonging to obj: %v", err) + } + return true + }, + GenericFunc: func(evt event.GenericEvent) bool { + return false + }, + } + + // Watch for changes to YurtAppOverrider + err = c.Watch(&source.Kind{Type: &appsv1alpha1.YurtAppOverrider{}}, &handler.EnqueueRequestForObject{}, yurtappoverriderPredicates) + if err != nil { + return err + } + + return nil +} + +// +kubebuilder:rbac:groups=apps.openyurt.io,resources=yurtappoverriders,verbs=get;list;watch;create;update;patch;delete +// +kubebuilder:rbac:groups=apps,resources=deployments,verbs=get;list;watch;create;update;patch;delete +// +kubebuilder:rbac:groups=apps,resources=deployments/status,verbs=get;update;patch +// +kubebuilder:rbac:groups=core,resources=events,verbs=get;list;watch;create;update;patch;delete +// +kubebuilder:rbac:groups=apps,resources=controllerrevisions,verbs=get;list;watch;create;update;patch;delete + +// Reconcile reads that state of the cluster for a YurtAppOverrider object and makes changes based on the state read +// and what is in the YurtAppOverrider.Spec +func (r *ReconcileYurtAppOverrider) Reconcile(_ context.Context, request reconcile.Request) (reconcile.Result, error) { + + // Note !!!!!!!!!! + // We strongly recommend use Format() to encapsulation because Format() can print logs by module + // @kadisi + klog.Infof(Format("Reconcile YurtAppOverrider %s/%s", request.Namespace, request.Name)) + + // Fetch the YurtAppOverrider instance + instance := &appsv1alpha1.YurtAppOverrider{} + err := r.Get(context.TODO(), request.NamespacedName, instance) + if err != nil { + if errors.IsNotFound(err) { + return reconcile.Result{}, nil + } + return reconcile.Result{}, err + } + + if instance.DeletionTimestamp != nil { + return reconcile.Result{}, nil + } + + return reconcile.Result{}, nil +} + +func (r *ReconcileYurtAppOverrider) updatePools(yacr *appsv1alpha1.YurtAppOverrider) error { + + pools := []string(nil) + for _, entry := range yacr.Entries { + pools = append(pools, entry.Pools...) + } + + for _, pool := range pools { + deployments := v1.DeploymentList{} + listOptions := client.MatchingLabels{"apps.openyurt.io/pool-name": pool} + err := r.List(context.TODO(), &deployments, listOptions) + if err != nil { + return err + } + for _, deployment := range deployments.Items { + //deployment.Annotations["apps.openyurt.io/resourceVersion"] = deployment.ResourceVersion + //deployment.Annotations["deployment-webhook"] = "pending" + err = r.Update(context.TODO(), &deployment) + if err != nil { + return err + } + } + } + return nil +} diff --git a/pkg/yurtmanager/controller/yurtappoverrider/yurtappoverrider_controller_test.go b/pkg/yurtmanager/controller/yurtappoverrider/yurtappoverrider_controller_test.go new file mode 100644 index 00000000000..ea8cbeae1ba --- /dev/null +++ b/pkg/yurtmanager/controller/yurtappoverrider/yurtappoverrider_controller_test.go @@ -0,0 +1,16 @@ +/* +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 yurtappoverrider diff --git a/pkg/yurtmanager/webhook/deploymentrender/utils/util.go b/pkg/yurtmanager/webhook/deploymentrender/utils/util.go new file mode 100644 index 00000000000..463ac2ffaaf --- /dev/null +++ b/pkg/yurtmanager/webhook/deploymentrender/utils/util.go @@ -0,0 +1,58 @@ +/* +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 utils + +import ( + "strconv" + "strings" +) + +func FlattenYAML(data map[string]interface{}, parentKey string, sep string) (map[string]interface{}, error) { + flattenedData := make(map[string]interface{}) + for key, value := range data { + newKey := strings.Join([]string{parentKey, key}, sep) + switch value.(type) { + case map[string]interface{}: + flattenedMap, err := FlattenYAML(value.(map[string]interface{}), newKey, sep) + if err != nil { + return flattenedData, err + } + for k, v := range flattenedMap { + flattenedData[k] = v + } + case []interface{}: + for i, item := range value.([]interface{}) { + itemKey := strings.Join([]string{newKey, strconv.Itoa(i)}, sep) + if nestedMap, ok := item.(map[string]interface{}); ok { + nestedData, err := FlattenYAML(nestedMap, itemKey, sep) + if err != nil { + return flattenedData, err + } + for nestedKey, nestedValue := range nestedData { + flattenedData[nestedKey] = nestedValue + } + } else { + flattenedData[itemKey] = item + } + } + default: + flattenedData[newKey] = value + } + + } + return flattenedData, nil +} diff --git a/pkg/yurtmanager/webhook/deploymentrender/utils/util_test.go b/pkg/yurtmanager/webhook/deploymentrender/utils/util_test.go new file mode 100644 index 00000000000..5b25805216d --- /dev/null +++ b/pkg/yurtmanager/webhook/deploymentrender/utils/util_test.go @@ -0,0 +1,69 @@ +/* +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 utils + +import ( + "encoding/json" + "testing" + + "sigs.k8s.io/yaml" +) + +func TestFlattenYAML(t *testing.T) { + tests := []struct { + name string + patch string + }{ + { + name: "test1", + patch: ` +spec: + template: + spec: + containers: + - name: nginx + image: nginx + - hello + - world`, + }, + { + name: "test2", + patch: ` +spec: + template: + spec: + containers: + - name: nginx + image: nginx + - name: tomcat + image: tomcat`}} + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + patchMap := make(map[string]interface{}) + data, err := yaml.YAMLToJSON([]byte(tt.patch)) + if err != nil { + t.Fatalf("fail to convert patch from yaml to json: %v", err) + } + if err := json.Unmarshal(data, &patchMap); err != nil { + t.Fatalf("fail to call function Unmarshal: %v", err) + } + if _, err := FlattenYAML(patchMap, "", "/"); err != nil { + t.Fatalf("fail to call function FlattenYAML: %v", err) + } + }) + } +} diff --git a/pkg/yurtmanager/webhook/deploymentrender/v1alpha1/deploymentrender_default.go b/pkg/yurtmanager/webhook/deploymentrender/v1alpha1/deploymentrender_default.go new file mode 100644 index 00000000000..9829b7cdf2a --- /dev/null +++ b/pkg/yurtmanager/webhook/deploymentrender/v1alpha1/deploymentrender_default.go @@ -0,0 +1,177 @@ +/* +Copyright 2023 The OpenYurt Authors. + +Licensed under the Apache License, Version 2.0 (the License); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an AS IS BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1alpha1 + +import ( + "context" + "fmt" + "strings" + + v1 "k8s.io/api/apps/v1" + apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" + apierrors "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/klog/v2" + "sigs.k8s.io/controller-runtime/pkg/client" + + "github.com/openyurtio/openyurt/pkg/apis/apps/v1alpha1" + "github.com/openyurtio/openyurt/pkg/yurtmanager/controller/yurtappset/adapter" +) + +const ( + DeploymentMutatingWebhook = "deployment-webhook" +) + +var ( + resources = []string{"YurtAppSet", "YurtAppDaemon"} +) + +func contain(kind string, resources []string) bool { + for _, v := range resources { + if kind == v { + return true + } + } + return false +} + +// Default satisfies the defaulting webhook interface. +func (webhook *DeploymentRenderHandler) Default(ctx context.Context, obj runtime.Object) error { + + deployment, ok := obj.(*v1.Deployment) + if !ok { + return apierrors.NewBadRequest(fmt.Sprintf("expected a YurtAppOverrider but got a %T", obj)) + } + if v, ok := deployment.Annotations[DeploymentMutatingWebhook]; ok && v == "mutated" { + delete(deployment.Annotations, DeploymentMutatingWebhook) + return nil + } + if deployment.OwnerReferences == nil { + return nil + } + if !contain(deployment.OwnerReferences[0].Kind, resources) { + return nil + } + klog.Info("start to validate deployment") + // Get YurtAppSet/YurtAppDaemon resource of this deployment + app := deployment.OwnerReferences[0] + var instance client.Object + switch app.Kind { + case "YurtAppSet": + instance = &v1alpha1.YurtAppSet{} + + case "YurtAppDaemon": + instance = &v1alpha1.YurtAppDaemon{} + default: + return nil + } + if err := webhook.Client.Get(ctx, client.ObjectKey{ + Namespace: deployment.Namespace, + Name: app.Name, + }, instance); err != nil { + return err + } + klog.Info("Successfully get the owner of deployment") + // Get nodepool of deployment + nodepool := deployment.Labels["apps.openyurt.io/pool-name"] + + // resume deployment + switch app.Kind { + case "YurtAppSet": + var replicas int32 + yas := instance.(*v1alpha1.YurtAppSet) + revision := yas.Status.CurrentRevision + deploymentAdapter := adapter.DeploymentAdapter{ + Client: webhook.Client, + Scheme: webhook.Scheme, + } + for _, pool := range yas.Spec.Topology.Pools { + if pool.Name == nodepool { + replicas = *pool.Replicas + } + } + if err := deploymentAdapter.ApplyPoolTemplate(yas, nodepool, revision, replicas, deployment); err != nil { + return err + } + case "YurtAppDaemon": + yad := instance.(*v1alpha1.YurtAppDaemon) + yad.Spec.WorkloadTemplate.DeploymentTemplate.Spec.DeepCopyInto(&deployment.Spec) + } + + // Get YurtAppOverrider resource of app(1 to 1) + var allConfigRenderList v1alpha1.YurtAppOverriderList + //listOptions := client.MatchingFields{"spec.subject.kind": app.Kind, "spec.subject.name": app.Name, "spec.subject.APIVersion": app.APIVersion} + if err := webhook.Client.List(ctx, &allConfigRenderList, client.InNamespace(deployment.Namespace)); err != nil { + klog.Info("error in listing YurtAppOverrider") + return err + } + var overriderList = v1alpha1.YurtAppOverriderList{} + for _, configRender := range allConfigRenderList.Items { + if configRender.Subject.Kind == app.Kind && configRender.Subject.Name == app.Name && configRender.Subject.APIVersion == app.APIVersion { + overriderList.Items = append(overriderList.Items, configRender) + } + } + klog.Info("Successfully list YurtAppOverrider") + + if len(overriderList.Items) == 0 { + return nil + } + render := overriderList.Items[0] + + klog.Info("start to render deployment") + for _, entry := range render.Entries { + pools := entry.Pools + for _, pool := range pools { + if pool == nodepool || pool == "*" { + items := entry.Items + // Replace items + if err := replaceItems(deployment, items); err != nil { + return err + } + klog.Info("Successfully replace items for deployment") + // json patch and strategic merge + patches := entry.Patches + for i, patch := range patches { + if strings.Contains(string(patch.Value.Raw), "{{nodepool}}") { + newPatchString := strings.ReplaceAll(string(patch.Value.Raw), "{{nodepool}}", nodepool) + patches[i].Value = apiextensionsv1.JSON{Raw: []byte(newPatchString)} + } + } + + // Implement injection + dataStruct := v1.Deployment{} + pc := PatchControl{ + patches: patches, + patchObject: deployment, + dataStruct: dataStruct, + } + if err := pc.updatePatches(); err != nil { + return err + } + klog.Info("Successfully update patches for deployment") + klog.Infof("name of deployment: %v", deployment.Name) + } + } + } + deployment.Annotations[DeploymentMutatingWebhook] = "mutated" + if err := webhook.Client.Update(ctx, deployment); err != nil { + klog.Infof("error to update deployment: %v", err) + return err + } + klog.Info("Successfully render deployment") + return nil +} diff --git a/pkg/yurtmanager/webhook/deploymentrender/v1alpha1/deploymentrender_handler.go b/pkg/yurtmanager/webhook/deploymentrender/v1alpha1/deploymentrender_handler.go new file mode 100644 index 00000000000..bd15999a1a5 --- /dev/null +++ b/pkg/yurtmanager/webhook/deploymentrender/v1alpha1/deploymentrender_handler.go @@ -0,0 +1,58 @@ +/* +Copyright 2023 The OpenYurt Authors. + +Licensed under the Apache License, Version 2.0 (the License); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an AS IS BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1alpha1 + +import ( + v1 "k8s.io/api/apps/v1" + "k8s.io/apimachinery/pkg/runtime" + 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/yurtmanager/webhook/util" +) + +const ( + WebhookName = "deploymentrender" +) + +// SetupWebhookWithManager sets up Cluster webhooks. mutate path, validatepath, error +func (webhook *DeploymentRenderHandler) SetupWebhookWithManager(mgr ctrl.Manager) (string, string, error) { + // init + webhook.Client = mgr.GetClient() + webhook.Scheme = mgr.GetScheme() + + gvk, err := apiutil.GVKForObject(&v1.Deployment{}, mgr.GetScheme()) + if err != nil { + return "", "", err + } + return util.GenerateMutatePath(gvk), + util.GenerateValidatePath(gvk), + ctrl.NewWebhookManagedBy(mgr). + For(&v1.Deployment{}). + WithDefaulter(webhook). + Complete() +} + +// Cluster implements a validating and defaulting webhook for Cluster. +type DeploymentRenderHandler struct { + Client client.Client + Scheme *runtime.Scheme +} + +var _ webhook.CustomDefaulter = &DeploymentRenderHandler{} diff --git a/pkg/yurtmanager/webhook/deploymentrender/v1alpha1/deploymentrender_webhook_test.go b/pkg/yurtmanager/webhook/deploymentrender/v1alpha1/deploymentrender_webhook_test.go new file mode 100644 index 00000000000..1c7dcd24b04 --- /dev/null +++ b/pkg/yurtmanager/webhook/deploymentrender/v1alpha1/deploymentrender_webhook_test.go @@ -0,0 +1,215 @@ +/* +Copyright 2023 The OpenYurt Authors. + +Licensed under the Apache License, Version 2.0 (the License); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an AS IS BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +package v1alpha1 + +import ( + "context" + "testing" + + appsv1 "k8s.io/api/apps/v1" + corev1 "k8s.io/api/core/v1" + apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + clientgoscheme "k8s.io/client-go/kubernetes/scheme" + fakeclient "sigs.k8s.io/controller-runtime/pkg/client/fake" + + "github.com/openyurtio/openyurt/pkg/apis/apps/v1alpha1" +) + +var ( + replica int32 = 3 +) + +var defaultAppSet = &v1alpha1.YurtAppSet{ + ObjectMeta: metav1.ObjectMeta{ + Name: "yurtappset-patch", + Namespace: "default", + }, + Spec: v1alpha1.YurtAppSetSpec{ + Topology: v1alpha1.Topology{ + Pools: []v1alpha1.Pool{{ + Name: "nodepool-test", + Replicas: &replica}}, + }, + Selector: &metav1.LabelSelector{MatchLabels: map[string]string{"app": "test"}}, + WorkloadTemplate: v1alpha1.WorkloadTemplate{ + DeploymentTemplate: &v1alpha1.DeploymentTemplateSpec{ + ObjectMeta: metav1.ObjectMeta{ + Labels: map[string]string{"app": "test"}, + }, + Spec: appsv1.DeploymentSpec{ + Selector: &metav1.LabelSelector{MatchLabels: map[string]string{"app": "test"}}, + Template: corev1.PodTemplateSpec{ + ObjectMeta: metav1.ObjectMeta{ + Labels: map[string]string{"app": "test"}, + }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + {Name: "nginx", Image: "nginx"}, + }, + Volumes: []corev1.Volume{ + { + Name: "config", + VolumeSource: corev1.VolumeSource{ + ConfigMap: &corev1.ConfigMapVolumeSource{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "configMapSource-nodepool-test", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, +} + +var defaultDeployment = &appsv1.Deployment{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + Namespace: "default", + OwnerReferences: []metav1.OwnerReference{{ + APIVersion: "apps.openyurt.io/v1alpha1", + Kind: "YurtAppSet", + Name: "yurtappset-patch", + }}, + Labels: map[string]string{ + "apps.openyurt.io/pool-name": "nodepool-test", + }, + }, + Status: appsv1.DeploymentStatus{}, + Spec: appsv1.DeploymentSpec{ + Replicas: &replica, + Selector: &metav1.LabelSelector{ + MatchLabels: map[string]string{ + "app": "test", + }, + }, + Template: corev1.PodTemplateSpec{ + ObjectMeta: metav1.ObjectMeta{ + Labels: map[string]string{ + "app": "test", + }, + }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "nginx", + Image: "nginx", + }, + }, + }, + }, + }, +} + +var overrider1 = &v1alpha1.YurtAppOverrider{ + ObjectMeta: metav1.ObjectMeta{ + Name: "demo", + Namespace: "default", + }, + Subject: v1alpha1.Subject{ + Name: "yurtappset-patch", + TypeMeta: metav1.TypeMeta{ + Kind: "YurtAppSet", + APIVersion: "apps.openyurt.io/v1alpha1", + }, + }, + Entries: []v1alpha1.Entry{ + { + Pools: []string{"nodepool-test"}, + Items: []v1alpha1.Item{ + { + Image: &v1alpha1.ImageItem{ + ContainerName: "nginx", + ImageClaim: "nginx:1.18", + }, + }, + }, + Patches: []v1alpha1.Patch{ + { + Operation: v1alpha1.REPLACE, + Path: "/spec/replicas", + Value: apiextensionsv1.JSON{ + Raw: []byte("3"), + }, + }, + }, + }, + }, +} + +var overrider2 = &v1alpha1.YurtAppOverrider{ + ObjectMeta: metav1.ObjectMeta{ + Name: "demo", + Namespace: "default", + }, + Subject: v1alpha1.Subject{ + Name: "yurtappset-patch", + TypeMeta: metav1.TypeMeta{ + Kind: "YurtAppSet", + APIVersion: "apps.openyurt.io/v1alpha1", + }, + }, + Entries: []v1alpha1.Entry{ + { + Pools: []string{"*"}, + Patches: []v1alpha1.Patch{ + { + Operation: v1alpha1.ADD, + Path: "/spec/template/spec/volumes/-", + Value: apiextensionsv1.JSON{ + Raw: []byte(`{"name":"configmap-{{nodepool}}","configMap":{"name":"demo","items":[{"key": "game.properities","path": "game.properities"}]}}`), + }, + }, + }, + }, + }, +} + +func TestDeploymentRenderHandler_Default(t *testing.T) { + tcases := []struct { + overrider *v1alpha1.YurtAppOverrider + }{ + {overrider1}, + {overrider2}, + } + scheme := runtime.NewScheme() + if err := v1alpha1.AddToScheme(scheme); err != nil { + t.Logf("failed to add yurt custom resource") + return + } + if err := clientgoscheme.AddToScheme(scheme); err != nil { + t.Logf("failed to add kubernetes clint-go custom resource") + return + } + for _, tcase := range tcases { + t.Run("", func(t *testing.T) { + webhook := &DeploymentRenderHandler{ + Client: fakeclient.NewClientBuilder().WithScheme(scheme).WithObjects(defaultAppSet, defaultDeployment, tcase.overrider).Build(), + Scheme: scheme, + } + if err := webhook.Default(context.TODO(), defaultDeployment); err != nil { + t.Fatal(err) + } + }) + } + +} diff --git a/pkg/yurtmanager/webhook/deploymentrender/v1alpha1/item_control.go b/pkg/yurtmanager/webhook/deploymentrender/v1alpha1/item_control.go new file mode 100644 index 00000000000..1d68a944bbc --- /dev/null +++ b/pkg/yurtmanager/webhook/deploymentrender/v1alpha1/item_control.go @@ -0,0 +1,39 @@ +/* +Copyright 2023 The OpenYurt Authors. + +Licensed under the Apache License, Version 2.0 (the License); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an AS IS BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1alpha1 + +import ( + v1 "k8s.io/api/apps/v1" + + "github.com/openyurtio/openyurt/pkg/apis/apps/v1alpha1" +) + +func replaceItems(deployment *v1.Deployment, items []v1alpha1.Item) error { + for _, item := range items { + switch { + case item.Replicas != nil: + deployment.Spec.Replicas = item.Replicas + case item.Image != nil: + for i := range deployment.Spec.Template.Spec.Containers { + if deployment.Spec.Template.Spec.Containers[i].Name == item.Image.ContainerName { + deployment.Spec.Template.Spec.Containers[i].Image = item.Image.ImageClaim + } + } + } + } + return nil +} diff --git a/pkg/yurtmanager/webhook/deploymentrender/v1alpha1/item_control_test.go b/pkg/yurtmanager/webhook/deploymentrender/v1alpha1/item_control_test.go new file mode 100644 index 00000000000..5acd4f835a1 --- /dev/null +++ b/pkg/yurtmanager/webhook/deploymentrender/v1alpha1/item_control_test.go @@ -0,0 +1,94 @@ +/* +Copyright 2023 The OpenYurt Authors. + +Licensed under the Apache License, Version 2.0 (the License); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an AS IS BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1alpha1 + +import ( + "testing" + + appsv1 "k8s.io/api/apps/v1" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + "github.com/openyurtio/openyurt/pkg/apis/apps/v1alpha1" +) + +var ( + itemReplicas int32 = 3 +) + +var testItemDeployment = &appsv1.Deployment{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + Namespace: "default", + }, + Status: appsv1.DeploymentStatus{}, + Spec: appsv1.DeploymentSpec{ + Replicas: &itemReplicas, + Selector: &metav1.LabelSelector{ + MatchLabels: map[string]string{ + "app": "test", + }, + }, + Strategy: appsv1.DeploymentStrategy{ + Type: appsv1.RollingUpdateDeploymentStrategyType, + }, + Template: corev1.PodTemplateSpec{ + ObjectMeta: metav1.ObjectMeta{ + Labels: map[string]string{ + "app": "test", + }, + }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "nginx", + Image: "nginx", + }, + }, + Volumes: []corev1.Volume{ + { + Name: "config", + VolumeSource: corev1.VolumeSource{ + ConfigMap: &corev1.ConfigMapVolumeSource{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "configMapSource", + }, + }, + }, + }, + }, + }, + }, + }, +} + +func TestReplaceItems(t *testing.T) { + items := []v1alpha1.Item{ + { + Image: &v1alpha1.ImageItem{ + ContainerName: "nginx", + ImageClaim: "nginx", + }, + }, + { + Replicas: &itemReplicas, + }, + } + if err := replaceItems(testItemDeployment, items); err != nil { + t.Fatalf("Error: %v", err) + } +} diff --git a/pkg/yurtmanager/webhook/deploymentrender/v1alpha1/patch_control.go b/pkg/yurtmanager/webhook/deploymentrender/v1alpha1/patch_control.go new file mode 100644 index 00000000000..17ed2fb0912 --- /dev/null +++ b/pkg/yurtmanager/webhook/deploymentrender/v1alpha1/patch_control.go @@ -0,0 +1,78 @@ +/* +Copyright 2023 The OpenYurt Authors. + +Licensed under the Apache License, Version 2.0 (the License); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an AS IS BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1alpha1 + +import ( + "encoding/json" + + jsonpatch "github.com/evanphx/json-patch" + appsv1 "k8s.io/api/apps/v1" + + "github.com/openyurtio/openyurt/pkg/apis/apps/v1alpha1" +) + +type PatchControl struct { + patches []v1alpha1.Patch + patchObject interface{} + // data structure + dataStruct interface{} +} + +type overrider struct { + Op string `json:"op"` + Path string `json:"path"` + Value interface{} `json:"value,omitempty"` +} + +// implement json patch +func (pc *PatchControl) jsonMergePatch(patches []v1alpha1.Patch) error { + // convert into json patch format + var patchOperations []overrider + for _, patch := range patches { + single := overrider{ + Op: string(patch.Operation), + Path: patch.Path, + Value: patch.Value, + } + patchOperations = append(patchOperations, single) + } + patchBytes, err := json.Marshal(patchOperations) + if err != nil { + return err + } + patchedData, err := json.Marshal(pc.patchObject.(*appsv1.Deployment)) + if err != nil { + return err + } + // conduct json patch + patchObj, err := jsonpatch.DecodePatch(patchBytes) + if err != nil { + return err + } + patchedData, err = patchObj.Apply(patchedData) + if err != nil { + return err + } + return json.Unmarshal(patchedData, &pc.patchObject) +} + +func (pc *PatchControl) updatePatches() error { + if err := pc.jsonMergePatch(pc.patches); err != nil { + return err + } + return nil +} diff --git a/pkg/yurtmanager/webhook/deploymentrender/v1alpha1/patch_control_test.go b/pkg/yurtmanager/webhook/deploymentrender/v1alpha1/patch_control_test.go new file mode 100644 index 00000000000..67742a6a68f --- /dev/null +++ b/pkg/yurtmanager/webhook/deploymentrender/v1alpha1/patch_control_test.go @@ -0,0 +1,105 @@ +/* +Copyright 2023 The OpenYurt Authors. + +Licensed under the Apache License, Version 2.0 (the License); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an AS IS BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1alpha1 + +import ( + "testing" + + appsv1 "k8s.io/api/apps/v1" + corev1 "k8s.io/api/core/v1" + apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + "github.com/openyurtio/openyurt/pkg/apis/apps/v1alpha1" +) + +var initialReplicas int32 = 2 + +var testPatchDeployment = &appsv1.Deployment{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + Namespace: "default", + OwnerReferences: []metav1.OwnerReference{{ + APIVersion: "apps.openyurt.io/v1alpha1", + Kind: "YurtAppSet", + Name: "yurtappset-patch", + }}, + }, + Status: appsv1.DeploymentStatus{}, + Spec: appsv1.DeploymentSpec{ + Replicas: &initialReplicas, + Selector: &metav1.LabelSelector{ + MatchLabels: map[string]string{ + "app": "test", + }, + }, + Template: corev1.PodTemplateSpec{ + ObjectMeta: metav1.ObjectMeta{ + Labels: map[string]string{ + "app": "test", + }, + }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "nginx", + Image: "nginx", + }, + }, + }, + }, + }, +} + +var patchControl = PatchControl{ + patches: []v1alpha1.Patch{ + { + Operation: v1alpha1.REPLACE, + Path: "/spec/template/spec/containers/0/image", + Value: apiextensionsv1.JSON{ + Raw: []byte(`"tomcat:1.18"`), + }, + }, + { + Operation: v1alpha1.ADD, + Path: "/spec/replicas", + Value: apiextensionsv1.JSON{ + Raw: []byte("5"), + }, + }, + }, + patchObject: testPatchDeployment, + dataStruct: appsv1.Deployment{}, +} + +func TestJsonMergePatch(t *testing.T) { + sample := v1alpha1.Patch{ + Operation: v1alpha1.ADD, + Path: "/spec/template/spec/containers/0/image", + Value: apiextensionsv1.JSON{Raw: []byte(`"tomcat"`)}, + } + if err := patchControl.jsonMergePatch([]v1alpha1.Patch{sample}); err != nil { + t.Fatalf("fail to call jsonMergePatch") + } + t.Logf("image:%v", testPatchDeployment.Spec.Template.Spec.Containers[0].Name) +} + +func TestUpdatePatches(t *testing.T) { + if err := patchControl.updatePatches(); err != nil { + t.Fatalf("fail to call updatePatches: %v", err) + } +} diff --git a/pkg/yurtmanager/webhook/yurtappoverrider/v1alpha1/yurtappconfigrender_default.go b/pkg/yurtmanager/webhook/yurtappoverrider/v1alpha1/yurtappconfigrender_default.go new file mode 100644 index 00000000000..107a80a86cc --- /dev/null +++ b/pkg/yurtmanager/webhook/yurtappoverrider/v1alpha1/yurtappconfigrender_default.go @@ -0,0 +1,39 @@ +/* +Copyright 2023 The OpenYurt Authors. + +Licensed under the Apache License, Version 2.0 (the License); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an AS IS BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1alpha1 + +import ( + "context" + "fmt" + + apierrors "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/runtime" + + "github.com/openyurtio/openyurt/pkg/apis/apps/v1alpha1" +) + +// Default satisfies the defaulting webhook interface. +func (webhook *YurtAppOverriderHandler) Default(ctx context.Context, obj runtime.Object) error { + np, ok := obj.(*v1alpha1.YurtAppOverrider) + if !ok { + return apierrors.NewBadRequest(fmt.Sprintf("expected a YurtAppOverrider but got a %T", obj)) + } + + v1alpha1.SetDefaultsYurtAppOverrider(np) + + return nil +} diff --git a/pkg/yurtmanager/webhook/yurtappoverrider/v1alpha1/yurtappconfigrender_handler.go b/pkg/yurtmanager/webhook/yurtappoverrider/v1alpha1/yurtappconfigrender_handler.go new file mode 100644 index 00000000000..453410a26f7 --- /dev/null +++ b/pkg/yurtmanager/webhook/yurtappoverrider/v1alpha1/yurtappconfigrender_handler.go @@ -0,0 +1,56 @@ +/* +Copyright 2023 The OpenYurt Authors. + +Licensed under the Apache License, Version 2.0 (the License); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an AS IS BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1alpha1 + +import ( + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/client/apiutil" + "sigs.k8s.io/controller-runtime/pkg/webhook" + + "github.com/openyurtio/openyurt/pkg/apis/apps/v1alpha1" + "github.com/openyurtio/openyurt/pkg/yurtmanager/webhook/util" +) + +// SetupWebhookWithManager sets up Cluster webhooks. mutate path, validatepath, error +func (webhook *YurtAppOverriderHandler) SetupWebhookWithManager(mgr ctrl.Manager) (string, string, error) { + // init + webhook.Client = mgr.GetClient() + + gvk, err := apiutil.GVKForObject(&v1alpha1.YurtAppOverrider{}, mgr.GetScheme()) + if err != nil { + return "", "", err + } + return util.GenerateMutatePath(gvk), + util.GenerateValidatePath(gvk), + ctrl.NewWebhookManagedBy(mgr). + For(&v1alpha1.YurtAppOverrider{}). + WithDefaulter(webhook). + WithValidator(webhook). + Complete() +} + +// +kubebuilder:webhook:path=/validate-apps-openyurt-io-v1alpha1-yurtappoverrider,mutating=false,failurePolicy=fail,sideEffects=None,admissionReviewVersions=v1;v1beta1,groups=apps.openyurt.io,resources=yurtappoverriders,verbs=create;update,versions=v1alpha1,name=validate.apps.v1alpha1.yurtappoverrider.openyurt.io +// +kubebuilder:webhook:path=/mutate-apps-openyurt-io-v1alpha1-yurtappoverrider,mutating=true,failurePolicy=fail,sideEffects=None,admissionReviewVersions=v1;v1beta1,groups=apps.openyurt.io,resources=yurtappoverriders,verbs=create;update,versions=v1alpha1,name=mutate.apps.v1alpha1.yurtappoverrider.openyurt.io + +// Cluster implements a validating and defaulting webhook for Cluster. +type YurtAppOverriderHandler struct { + Client client.Client +} + +var _ webhook.CustomDefaulter = &YurtAppOverriderHandler{} +var _ webhook.CustomValidator = &YurtAppOverriderHandler{} diff --git a/pkg/yurtmanager/webhook/yurtappoverrider/v1alpha1/yurtappconfigrender_validation.go b/pkg/yurtmanager/webhook/yurtappoverrider/v1alpha1/yurtappconfigrender_validation.go new file mode 100644 index 00000000000..d67ccf5a569 --- /dev/null +++ b/pkg/yurtmanager/webhook/yurtappoverrider/v1alpha1/yurtappconfigrender_validation.go @@ -0,0 +1,109 @@ +/* +Copyright 2023 The OpenYurt Authors. + +Licensed under the Apache License, Version 2.0 (the License); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an AS IS BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1alpha1 + +import ( + "context" + "fmt" + + apierrors "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/klog/v2" + "sigs.k8s.io/controller-runtime/pkg/client" + + "github.com/openyurtio/openyurt/pkg/apis/apps/v1alpha1" +) + +// ValidateCreate implements webhook.CustomValidator so a webhook will be registered for the type. +func (webhook *YurtAppOverriderHandler) ValidateCreate(ctx context.Context, obj runtime.Object) error { + configRender, ok := obj.(*v1alpha1.YurtAppOverrider) + if !ok { + return apierrors.NewBadRequest(fmt.Sprintf("expected a YurtAppOverrider but got a %T", obj)) + } + + // validate + if err := webhook.validateOneToOne(ctx, configRender); err != nil { + return err + } + if err := webhook.validateStar(configRender); err != nil { + return err + } + return nil +} + +// ValidateUpdate implements webhook.CustomValidator so a webhook will be registered for the type. +func (webhook *YurtAppOverriderHandler) ValidateUpdate(ctx context.Context, oldObj, newObj runtime.Object) error { + _, ok := newObj.(*v1alpha1.YurtAppOverrider) + if !ok { + return apierrors.NewBadRequest(fmt.Sprintf("expected a YurtAppOverrider but got a %T", newObj)) + } + newConfigRender, ok := oldObj.(*v1alpha1.YurtAppOverrider) + if !ok { + return apierrors.NewBadRequest(fmt.Sprintf("expected a YurtAppOverrider} but got a %T", oldObj)) + } + + // validate + if err := webhook.validateOneToOne(ctx, newConfigRender); err != nil { + return err + } + if err := webhook.validateStar(newConfigRender); err != nil { + return err + } + return nil +} + +// ValidateDelete implements webhook.CustomValidator so a webhook will be registered for the type. +func (webhook *YurtAppOverriderHandler) ValidateDelete(_ context.Context, obj runtime.Object) error { + _, ok := obj.(*v1alpha1.YurtAppOverrider) + if !ok { + return apierrors.NewBadRequest(fmt.Sprintf("expected a YurtAppOverrider but got a %T", obj)) + } + // validate + return nil +} + +// YurtConfigRender and YurtAppSet are one-to-one relationship +func (webhook *YurtAppOverriderHandler) validateOneToOne(ctx context.Context, configRender *v1alpha1.YurtAppOverrider) error { + app := configRender.Subject + var allConfigRenderList v1alpha1.YurtAppOverriderList + if err := webhook.Client.List(ctx, &allConfigRenderList, client.InNamespace(configRender.Namespace)); err != nil { + klog.Info("error in listing YurtAppOverrider") + return err + } + var configRenderList = v1alpha1.YurtAppOverriderList{} + for _, configRender := range allConfigRenderList.Items { + if configRender.Subject.Kind == app.Kind && configRender.Name == app.Name && configRender.APIVersion == app.APIVersion { + configRenderList.Items = append(configRenderList.Items, configRender) + } + } + if len(configRenderList.Items) > 0 { + return fmt.Errorf("only one YurtAppOverrider can be bound into one YurtAppSet") + } + return nil +} + +// Verify that * and other pools are not set at the same time +func (webhook *YurtAppOverriderHandler) validateStar(configRender *v1alpha1.YurtAppOverrider) error { + for _, entry := range configRender.Entries { + for _, pool := range entry.Pools { + if pool == "*" && len(entry.Pools) > 1 { + return fmt.Errorf("pool can't be '*' when other pools are set") + } + } + } + return nil +} diff --git a/test/e2e/yurt/yurtappoverrider.go b/test/e2e/yurt/yurtappoverrider.go new file mode 100644 index 00000000000..fe7b2cfcb41 --- /dev/null +++ b/test/e2e/yurt/yurtappoverrider.go @@ -0,0 +1,288 @@ +/* +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 yurt + +import ( + "context" + "fmt" + "time" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + v1 "k8s.io/api/apps/v1" + corev1 "k8s.io/api/core/v1" + apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" + "k8s.io/apimachinery/pkg/util/rand" + "sigs.k8s.io/controller-runtime/pkg/client" + + "github.com/openyurtio/openyurt/pkg/apis/apps/v1alpha1" + "github.com/openyurtio/openyurt/test/e2e/util" + ycfg "github.com/openyurtio/openyurt/test/e2e/yurtconfig" +) + +var _ = Describe("YurtAppOverrider Test", func() { + ctx := context.Background() + k8sClient := ycfg.YurtE2eCfg.RuntimeClient + var namespaceName string + timeout := 60 * time.Second + nodePoolName := "nodepool-test" + yurtAppSetName := "yurtappset-test" + yurtAppOverriderName := "yurtappoverrider-test" + var testReplicasOld int32 = 3 + var testReplicasNew int32 = 5 + createNameSpace := func() { + ns := corev1.Namespace{ + ObjectMeta: metav1.ObjectMeta{ + Name: namespaceName, + }, + } + Eventually(func() error { + return k8sClient.Delete(ctx, &ns, client.PropagationPolicy(metav1.DeletePropagationBackground)) + }).WithTimeout(timeout).WithPolling(500 * time.Millisecond).Should(SatisfyAny(BeNil(), &util.NotFoundMatcher{})) + By("make sure namespace are removed") + + res := &corev1.Namespace{} + Eventually(func() error { + return k8sClient.Get(ctx, client.ObjectKey{Name: namespaceName}, res) + }).WithTimeout(timeout).WithPolling(500 * time.Millisecond).Should(&util.NotFoundMatcher{}) + Eventually(func() error { + return k8sClient.Create(ctx, &ns) + }).WithTimeout(timeout).WithPolling(500 * time.Millisecond).Should(SatisfyAny(BeNil(), &util.AlreadyExistMatcher{})) + } + createNodePool := func() { + Eventually(func() error { + return k8sClient.Delete(ctx, &v1alpha1.NodePool{ + ObjectMeta: metav1.ObjectMeta{ + Name: nodePoolName, + Namespace: namespaceName, + }, + }) + }).WithTimeout(timeout).WithPolling(500 * time.Millisecond).Should(SatisfyAny(BeNil(), &util.NotFoundMatcher{})) + testNodePool := v1alpha1.NodePool{ + ObjectMeta: metav1.ObjectMeta{ + Name: nodePoolName, + Namespace: namespaceName, + }, + } + Eventually(func() error { + return k8sClient.Create(ctx, &testNodePool) + }).WithTimeout(timeout).WithPolling(500 * time.Millisecond).Should(SatisfyAny(BeNil(), &util.AlreadyExistMatcher{})) + } + createYurtAppSet := func() { + Eventually(func() error { + return k8sClient.Delete(ctx, &v1alpha1.YurtAppSet{ + ObjectMeta: metav1.ObjectMeta{ + Name: yurtAppSetName, + Namespace: namespaceName, + }, + }) + }).WithTimeout(timeout).WithPolling(500 * time.Millisecond).Should(SatisfyAny(BeNil(), &util.NotFoundMatcher{})) + testYurtAppSet := v1alpha1.YurtAppSet{ + ObjectMeta: metav1.ObjectMeta{ + Name: yurtAppSetName, + Namespace: namespaceName, + }, + Spec: v1alpha1.YurtAppSetSpec{ + Selector: &metav1.LabelSelector{ + MatchLabels: map[string]string{"app": "test"}, + }, + WorkloadTemplate: v1alpha1.WorkloadTemplate{ + DeploymentTemplate: &v1alpha1.DeploymentTemplateSpec{ + Spec: v1.DeploymentSpec{ + Template: corev1.PodTemplateSpec{ + ObjectMeta: metav1.ObjectMeta{ + Labels: map[string]string{"app": "test"}, + }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{{ + Image: "nginx-old", + Name: "nginx", + }}, + }, + }, + }, + }, + }, + Topology: v1alpha1.Topology{ + Pools: []v1alpha1.Pool{ + { + Name: nodePoolName, + Replicas: &testReplicasOld, + }, + }, + }, + }, + } + Eventually(func() error { + return k8sClient.Create(ctx, &testYurtAppSet) + }).WithTimeout(timeout).WithPolling(500 * time.Millisecond).Should(SatisfyAny(BeNil(), &util.AlreadyExistMatcher{})) + } + createYurtAppOverrider := func() { + Eventually(func() error { + return k8sClient.Delete(ctx, &v1alpha1.YurtAppOverrider{ + ObjectMeta: metav1.ObjectMeta{ + Name: yurtAppOverriderName, + Namespace: namespaceName, + }, + }) + }).WithTimeout(timeout).WithPolling(500 * time.Millisecond).Should(SatisfyAny(BeNil(), &util.NotFoundMatcher{})) + testYurtAppOverrider := v1alpha1.YurtAppOverrider{ + ObjectMeta: metav1.ObjectMeta{ + Name: yurtAppOverriderName, + Namespace: namespaceName, + }, + Subject: v1alpha1.Subject{ + Name: yurtAppSetName, + TypeMeta: metav1.TypeMeta{ + Kind: "YurtAppSet", + APIVersion: "apps.openyurt.io/v1alpha1", + }, + }, + Entries: []v1alpha1.Entry{ + { + Pools: []string{"nodepool-test"}, + Items: []v1alpha1.Item{ + { + Image: &v1alpha1.ImageItem{ + ContainerName: "nginx", + ImageClaim: "nginx-item", + }, + }, + { + Replicas: &testReplicasNew, + }, + }, + Patches: []v1alpha1.Patch{ + { + Operation: v1alpha1.Default, + Path: "/spec/template/spec/containers/0/image", + Value: apiextensionsv1.JSON{ + Raw: []byte("nginx-patch"), + }, + }, + }, + }, + }, + } + Eventually(func() error { + return k8sClient.Create(ctx, &testYurtAppOverrider) + }).WithTimeout(timeout).WithPolling(500 * time.Millisecond).Should(SatisfyAny(BeNil(), &util.AlreadyExistMatcher{})) + } + deleteNodePool := func() { + Eventually(func() error { + return k8sClient.Delete(ctx, &v1alpha1.NodePool{ + ObjectMeta: metav1.ObjectMeta{ + Name: nodePoolName, + Namespace: namespaceName, + }, + }) + }).WithTimeout(timeout).WithPolling(500 * time.Millisecond).Should(SatisfyAny(BeNil(), &util.NotFoundMatcher{})) + } + deleteYurtAppSet := func() { + Eventually(func() error { + return k8sClient.Delete(ctx, &v1alpha1.YurtAppSet{ + ObjectMeta: metav1.ObjectMeta{ + Name: yurtAppSetName, + Namespace: namespaceName, + }, + }) + }).WithTimeout(timeout).WithPolling(500 * time.Millisecond).Should(SatisfyAny(BeNil(), &util.NotFoundMatcher{})) + } + deleteYurtAppOverrider := func() { + Eventually(func() error { + return k8sClient.Delete(ctx, &v1alpha1.YurtAppOverrider{ + ObjectMeta: metav1.ObjectMeta{ + Name: yurtAppOverriderName, + Namespace: namespaceName, + }, + }) + }).WithTimeout(timeout).WithPolling(500 * time.Millisecond).Should(SatisfyAny(BeNil(), &util.NotFoundMatcher{})) + } + + BeforeEach(func() { + By("Start to run YurtAppOverrider test, prepare resources") + namespaceName = "yurtappoverrider-e2e-test" + "-" + rand.String(4) + createNameSpace() + + }) + AfterEach(func() { + By("Cleanup resources after test") + Expect(k8sClient.Delete(ctx, &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: namespaceName}}, client.PropagationPolicy(metav1.DeletePropagationBackground))).Should(Succeed()) + }) + + Describe("Test function of YurtAppOverrider", func() { + It("YurtAppOverrider should work after it is created", func() { + By("validate replicas and image of deployment") + Eventually(func() error { + deploymentList := &v1.DeploymentList{} + if err := k8sClient.List(ctx, deploymentList, client.InNamespace(namespaceName)); err != nil { + return err + } + for _, deployment := range deploymentList.Items { + if deployment.Labels["apps.openyurt.io/pool-name"] == nodePoolName { + if deployment.Spec.Template.Spec.Containers[0].Image != "nginx-patch" { + return fmt.Errorf("the image of nginx is not nginx-patch but %s", deployment.Spec.Template.Spec.Containers[0].Image) + } + if *deployment.Spec.Replicas != 5 { + return fmt.Errorf("the replicas of nginx is not 3 but %d", *deployment.Spec.Replicas) + } + } + } + return nil + }).WithTimeout(timeout).WithPolling(500 * time.Millisecond).Should(Succeed()) + }) + It("YurtAppOverrider should refresh template after it is updated", func() { + By("Deployment will be returned to former when the YurtAppOverrider is deleted") + yurtAppOverrider := &v1alpha1.YurtAppOverrider{} + Eventually(func() error { + return k8sClient.Get(ctx, types.NamespacedName{Name: yurtAppOverriderName, Namespace: namespaceName}, yurtAppOverrider) + }).WithTimeout(timeout).WithPolling(500 * time.Millisecond).Should(Succeed()) + for _, entry := range yurtAppOverrider.Entries { + entry.Pools = []string{} + } + Expect(k8sClient.Update(ctx, yurtAppOverrider)).Should(Succeed()) + Eventually(func() error { + deploymentList := &v1.DeploymentList{} + if err := k8sClient.List(ctx, deploymentList, client.MatchingLabels{ + "apps.openyurt.io/pool-name": nodePoolName, + }); err != nil { + return err + } + for _, deployment := range deploymentList.Items { + if deployment.Spec.Template.Spec.Containers[0].Image != "nginx-old" { + return fmt.Errorf("the image of nginx is not nginx but %s", deployment.Spec.Template.Spec.Containers[0].Image) + } + if *deployment.Spec.Replicas != 3 { + return fmt.Errorf("the replicas of nginx is not 3 but %d", *deployment.Spec.Replicas) + } + } + return nil + }).WithTimeout(timeout).WithPolling(500 * time.Millisecond).Should(Succeed()) + }) + BeforeEach(func() { + createNodePool() + createYurtAppSet() + createYurtAppOverrider() + }) + AfterEach(func() { + deleteNodePool() + deleteYurtAppSet() + deleteYurtAppOverrider() + }) + }) +}) From a968b4a374f809283883240e133cf2fe7ee2f8d6 Mon Sep 17 00:00:00 2001 From: vie-serendipity <2733147505@qq.com> Date: Fri, 25 Aug 2023 09:47:45 +0800 Subject: [PATCH 02/20] add configuration of deployment webhook --- .../util/configuration/configuration.go | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/pkg/yurtmanager/webhook/util/configuration/configuration.go b/pkg/yurtmanager/webhook/util/configuration/configuration.go index 9332939adfc..32b8744d9de 100644 --- a/pkg/yurtmanager/webhook/util/configuration/configuration.go +++ b/pkg/yurtmanager/webhook/util/configuration/configuration.go @@ -31,6 +31,11 @@ import ( webhookutil "github.com/openyurtio/openyurt/pkg/yurtmanager/webhook/util" ) +var ( + deploymentMutatingPath = "/mutate-apps-v1-deployment" + deploymentSideEffect = admissionregistrationv1.SideEffectClassNone +) + func Ensure(kubeClient clientset.Interface, handlers map[string]struct{}, caBundle []byte, webhookPort int) error { mutatingConfig, err := kubeClient.AdmissionregistrationV1().MutatingWebhookConfigurations().Get(context.TODO(), webhookutil.MutatingWebhookConfigurationName, metav1.GetOptions{}) if err != nil { @@ -75,6 +80,39 @@ func Ensure(kubeClient clientset.Interface, handlers map[string]struct{}, caBund mutatingWHs = append(mutatingWHs, *wh) } + + // add additional deployment mutating webhook + wh := &admissionregistrationv1.MutatingWebhook{} + wh.AdmissionReviewVersions = []string{"v1"} + svc := &admissionregistrationv1.ServiceReference{ + Namespace: webhookutil.GetNamespace(), + Name: webhookutil.GetServiceName(), + Path: &deploymentMutatingPath, + } + wh.ClientConfig = admissionregistrationv1.WebhookClientConfig{ + Service: svc, + CABundle: caBundle, + } + path, err := getPath(&wh.ClientConfig) + if err != nil { + return err + } + if _, ok := handlers[path]; !ok { + klog.V(4).Infof("Ignore webhook for %s in configuration", path) + } + if host := webhookutil.GetHost(); len(host) > 0 { + convertClientConfig(&wh.ClientConfig, host, webhookPort) + } + wh.Rules = []admissionregistrationv1.RuleWithOperations{{ + Operations: []admissionregistrationv1.OperationType{ + admissionregistrationv1.Create, admissionregistrationv1.Update}, + Rule: admissionregistrationv1.Rule{ + APIGroups: []string{"apps"}, + APIVersions: []string{"v1"}, + Resources: []string{"deployments"}}}} + wh.SideEffects = &deploymentSideEffect + wh.Name = "mutate.apps.v1.deployment" + mutatingWHs = append(mutatingWHs, *wh) mutatingConfig.Webhooks = mutatingWHs var validatingWHs []admissionregistrationv1.ValidatingWebhook From 4544f782ce79d753602abf4c29d5352f8fa01b18 Mon Sep 17 00:00:00 2001 From: vie-serendipity <2733147505@qq.com> Date: Fri, 25 Aug 2023 10:28:11 +0800 Subject: [PATCH 03/20] add register of webhook and controller --- pkg/yurtmanager/controller/controller.go | 2 ++ pkg/yurtmanager/webhook/server.go | 2 ++ 2 files changed, 4 insertions(+) diff --git a/pkg/yurtmanager/controller/controller.go b/pkg/yurtmanager/controller/controller.go index 2df60ea13c1..0ef50415112 100644 --- a/pkg/yurtmanager/controller/controller.go +++ b/pkg/yurtmanager/controller/controller.go @@ -33,6 +33,7 @@ import ( servicetopologyendpointslice "github.com/openyurtio/openyurt/pkg/yurtmanager/controller/servicetopology/endpointslice" "github.com/openyurtio/openyurt/pkg/yurtmanager/controller/util" "github.com/openyurtio/openyurt/pkg/yurtmanager/controller/yurtappdaemon" + "github.com/openyurtio/openyurt/pkg/yurtmanager/controller/yurtappoverrider" "github.com/openyurtio/openyurt/pkg/yurtmanager/controller/yurtappset" yurtcoordinatorcert "github.com/openyurtio/openyurt/pkg/yurtmanager/controller/yurtcoordinator/cert" "github.com/openyurtio/openyurt/pkg/yurtmanager/controller/yurtcoordinator/delegatelease" @@ -63,6 +64,7 @@ func init() { controllerAddFuncs[yurtappset.ControllerName] = []AddControllerFn{yurtappset.Add} controllerAddFuncs[yurtappdaemon.ControllerName] = []AddControllerFn{yurtappdaemon.Add} controllerAddFuncs[platformadmin.ControllerName] = []AddControllerFn{platformadmin.Add} + controllerAddFuncs[yurtappoverrider.ControllerName] = []AddControllerFn{yurtappoverrider.Add} } // If you want to add additional RBAC, enter it here !!! @kadisi diff --git a/pkg/yurtmanager/webhook/server.go b/pkg/yurtmanager/webhook/server.go index a57ed20f149..253e4f25480 100644 --- a/pkg/yurtmanager/webhook/server.go +++ b/pkg/yurtmanager/webhook/server.go @@ -34,6 +34,7 @@ import ( "github.com/openyurtio/openyurt/pkg/yurtmanager/controller/yurtappdaemon" "github.com/openyurtio/openyurt/pkg/yurtmanager/controller/yurtappset" "github.com/openyurtio/openyurt/pkg/yurtmanager/controller/yurtstaticset" + v1alpha1deploymentrender "github.com/openyurtio/openyurt/pkg/yurtmanager/webhook/deploymentrender/v1alpha1" v1beta1gateway "github.com/openyurtio/openyurt/pkg/yurtmanager/webhook/gateway/v1beta1" v1node "github.com/openyurtio/openyurt/pkg/yurtmanager/webhook/node/v1" v1beta1nodepool "github.com/openyurtio/openyurt/pkg/yurtmanager/webhook/nodepool/v1beta1" @@ -83,6 +84,7 @@ func init() { independentWebhooks[v1pod.WebhookName] = &v1pod.PodHandler{} independentWebhooks[v1node.WebhookName] = &v1node.NodeHandler{} + independentWebhooks[v1alpha1deploymentrender.WebhookName] = &v1alpha1deploymentrender.DeploymentRenderHandler{} } // Note !!! @kadisi From 4e98dafba7071bf504aae44032defe1c002f368a Mon Sep 17 00:00:00 2001 From: vie-serendipity <2733147505@qq.com> Date: Mon, 28 Aug 2023 11:10:55 +0800 Subject: [PATCH 04/20] call deployment webhook directly --- .../yurtappoverrider/yurtappoverrider_controller.go | 11 ++++++----- .../v1alpha1/deploymentrender_default.go | 1 - 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/pkg/yurtmanager/controller/yurtappoverrider/yurtappoverrider_controller.go b/pkg/yurtmanager/controller/yurtappoverrider/yurtappoverrider_controller.go index da04296d9a8..b6d0e869ab0 100644 --- a/pkg/yurtmanager/controller/yurtappoverrider/yurtappoverrider_controller.go +++ b/pkg/yurtmanager/controller/yurtappoverrider/yurtappoverrider_controller.go @@ -38,6 +38,7 @@ import ( appconfig "github.com/openyurtio/openyurt/cmd/yurt-manager/app/config" appsv1alpha1 "github.com/openyurtio/openyurt/pkg/apis/apps/v1alpha1" "github.com/openyurtio/openyurt/pkg/yurtmanager/controller/yurtappoverrider/config" + "github.com/openyurtio/openyurt/pkg/yurtmanager/webhook/deploymentrender/v1alpha1" ) func init() { @@ -182,7 +183,10 @@ func (r *ReconcileYurtAppOverrider) updatePools(yacr *appsv1alpha1.YurtAppOverri for _, entry := range yacr.Entries { pools = append(pools, entry.Pools...) } - + deploymentRenderHandler := v1alpha1.DeploymentRenderHandler{ + Client: r.Client, + Scheme: r.scheme, + } for _, pool := range pools { deployments := v1.DeploymentList{} listOptions := client.MatchingLabels{"apps.openyurt.io/pool-name": pool} @@ -191,10 +195,7 @@ func (r *ReconcileYurtAppOverrider) updatePools(yacr *appsv1alpha1.YurtAppOverri return err } for _, deployment := range deployments.Items { - //deployment.Annotations["apps.openyurt.io/resourceVersion"] = deployment.ResourceVersion - //deployment.Annotations["deployment-webhook"] = "pending" - err = r.Update(context.TODO(), &deployment) - if err != nil { + if err := deploymentRenderHandler.Default(context.TODO(), &deployment); err != nil { return err } } diff --git a/pkg/yurtmanager/webhook/deploymentrender/v1alpha1/deploymentrender_default.go b/pkg/yurtmanager/webhook/deploymentrender/v1alpha1/deploymentrender_default.go index 9829b7cdf2a..1391aa3b9de 100644 --- a/pkg/yurtmanager/webhook/deploymentrender/v1alpha1/deploymentrender_default.go +++ b/pkg/yurtmanager/webhook/deploymentrender/v1alpha1/deploymentrender_default.go @@ -163,7 +163,6 @@ func (webhook *DeploymentRenderHandler) Default(ctx context.Context, obj runtime return err } klog.Info("Successfully update patches for deployment") - klog.Infof("name of deployment: %v", deployment.Name) } } } From c69eb7e55f358d7f87f379a7c1b119f601e634bc Mon Sep 17 00:00:00 2001 From: vie-serendipity <2733147505@qq.com> Date: Mon, 28 Aug 2023 11:30:00 +0800 Subject: [PATCH 05/20] remove flatternYaml and add more info --- ...> apps.openyurt.io_yurtappoverriders.yaml} | 4 ++ .../apps/v1alpha1/yurtappoverrider_types.go | 1 + .../webhook/deploymentrender/utils/util.go | 58 ---------------- .../deploymentrender/utils/util_test.go | 69 ------------------- .../v1alpha1/deploymentrender_default.go | 11 ++- 5 files changed, 10 insertions(+), 133 deletions(-) rename charts/yurt-manager/crds/{apps.openyurt.io_yurtappoverriders..yaml => apps.openyurt.io_yurtappoverriders.yaml} (97%) delete mode 100644 pkg/yurtmanager/webhook/deploymentrender/utils/util.go delete mode 100644 pkg/yurtmanager/webhook/deploymentrender/utils/util_test.go diff --git a/charts/yurt-manager/crds/apps.openyurt.io_yurtappoverriders..yaml b/charts/yurt-manager/crds/apps.openyurt.io_yurtappoverriders.yaml similarity index 97% rename from charts/yurt-manager/crds/apps.openyurt.io_yurtappoverriders..yaml rename to charts/yurt-manager/crds/apps.openyurt.io_yurtappoverriders.yaml index dcaa084ebc5..acfd024baf9 100644 --- a/charts/yurt-manager/crds/apps.openyurt.io_yurtappoverriders..yaml +++ b/charts/yurt-manager/crds/apps.openyurt.io_yurtappoverriders.yaml @@ -21,6 +21,10 @@ spec: jsonPath: .subject.kind name: Subject type: string + - description: The subject name of this overrider. + jsonPath: .subject.name + name: Name + type: string - description: CreationTimestamp is a timestamp representing the server time when this object was created. It is not guaranteed to be set in happens-before order across separate operations. Clients may not set this value. It is represented diff --git a/pkg/apis/apps/v1alpha1/yurtappoverrider_types.go b/pkg/apis/apps/v1alpha1/yurtappoverrider_types.go index 19936822eef..a305ea536d2 100644 --- a/pkg/apis/apps/v1alpha1/yurtappoverrider_types.go +++ b/pkg/apis/apps/v1alpha1/yurtappoverrider_types.go @@ -87,6 +87,7 @@ type Subject struct { // +kubebuilder:subresource:status // +kubebuilder:resource:shortName=yacr // +kubebuilder:printcolumn:name="Subject",type="string",JSONPath=".subject.kind",description="The subject kind of this overrider." +// +kubebuilder:printcolumn:name="Name",type="string",JSONPath=".subject.name",description="The subject name of this overrider." // +kubebuilder:printcolumn:name="AGE",type="date",JSONPath=".metadata.creationTimestamp",description="CreationTimestamp is a timestamp representing the server time when this object was created. It is not guaranteed to be set in happens-before order across separate operations. Clients may not set this value. It is represented in RFC3339 form and is in UTC." type YurtAppOverrider struct { diff --git a/pkg/yurtmanager/webhook/deploymentrender/utils/util.go b/pkg/yurtmanager/webhook/deploymentrender/utils/util.go deleted file mode 100644 index 463ac2ffaaf..00000000000 --- a/pkg/yurtmanager/webhook/deploymentrender/utils/util.go +++ /dev/null @@ -1,58 +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 utils - -import ( - "strconv" - "strings" -) - -func FlattenYAML(data map[string]interface{}, parentKey string, sep string) (map[string]interface{}, error) { - flattenedData := make(map[string]interface{}) - for key, value := range data { - newKey := strings.Join([]string{parentKey, key}, sep) - switch value.(type) { - case map[string]interface{}: - flattenedMap, err := FlattenYAML(value.(map[string]interface{}), newKey, sep) - if err != nil { - return flattenedData, err - } - for k, v := range flattenedMap { - flattenedData[k] = v - } - case []interface{}: - for i, item := range value.([]interface{}) { - itemKey := strings.Join([]string{newKey, strconv.Itoa(i)}, sep) - if nestedMap, ok := item.(map[string]interface{}); ok { - nestedData, err := FlattenYAML(nestedMap, itemKey, sep) - if err != nil { - return flattenedData, err - } - for nestedKey, nestedValue := range nestedData { - flattenedData[nestedKey] = nestedValue - } - } else { - flattenedData[itemKey] = item - } - } - default: - flattenedData[newKey] = value - } - - } - return flattenedData, nil -} diff --git a/pkg/yurtmanager/webhook/deploymentrender/utils/util_test.go b/pkg/yurtmanager/webhook/deploymentrender/utils/util_test.go deleted file mode 100644 index 5b25805216d..00000000000 --- a/pkg/yurtmanager/webhook/deploymentrender/utils/util_test.go +++ /dev/null @@ -1,69 +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 utils - -import ( - "encoding/json" - "testing" - - "sigs.k8s.io/yaml" -) - -func TestFlattenYAML(t *testing.T) { - tests := []struct { - name string - patch string - }{ - { - name: "test1", - patch: ` -spec: - template: - spec: - containers: - - name: nginx - image: nginx - - hello - - world`, - }, - { - name: "test2", - patch: ` -spec: - template: - spec: - containers: - - name: nginx - image: nginx - - name: tomcat - image: tomcat`}} - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - patchMap := make(map[string]interface{}) - data, err := yaml.YAMLToJSON([]byte(tt.patch)) - if err != nil { - t.Fatalf("fail to convert patch from yaml to json: %v", err) - } - if err := json.Unmarshal(data, &patchMap); err != nil { - t.Fatalf("fail to call function Unmarshal: %v", err) - } - if _, err := FlattenYAML(patchMap, "", "/"); err != nil { - t.Fatalf("fail to call function FlattenYAML: %v", err) - } - }) - } -} diff --git a/pkg/yurtmanager/webhook/deploymentrender/v1alpha1/deploymentrender_default.go b/pkg/yurtmanager/webhook/deploymentrender/v1alpha1/deploymentrender_default.go index 1391aa3b9de..e0d958d638e 100644 --- a/pkg/yurtmanager/webhook/deploymentrender/v1alpha1/deploymentrender_default.go +++ b/pkg/yurtmanager/webhook/deploymentrender/v1alpha1/deploymentrender_default.go @@ -73,7 +73,6 @@ func (webhook *DeploymentRenderHandler) Default(ctx context.Context, obj runtime switch app.Kind { case "YurtAppSet": instance = &v1alpha1.YurtAppSet{} - case "YurtAppDaemon": instance = &v1alpha1.YurtAppDaemon{} default: @@ -113,16 +112,16 @@ func (webhook *DeploymentRenderHandler) Default(ctx context.Context, obj runtime } // Get YurtAppOverrider resource of app(1 to 1) - var allConfigRenderList v1alpha1.YurtAppOverriderList + var allOverriderList v1alpha1.YurtAppOverriderList //listOptions := client.MatchingFields{"spec.subject.kind": app.Kind, "spec.subject.name": app.Name, "spec.subject.APIVersion": app.APIVersion} - if err := webhook.Client.List(ctx, &allConfigRenderList, client.InNamespace(deployment.Namespace)); err != nil { + if err := webhook.Client.List(ctx, &allOverriderList, client.InNamespace(deployment.Namespace)); err != nil { klog.Info("error in listing YurtAppOverrider") return err } var overriderList = v1alpha1.YurtAppOverriderList{} - for _, configRender := range allConfigRenderList.Items { - if configRender.Subject.Kind == app.Kind && configRender.Subject.Name == app.Name && configRender.Subject.APIVersion == app.APIVersion { - overriderList.Items = append(overriderList.Items, configRender) + for _, overrider := range allOverriderList.Items { + if overrider.Subject.Kind == app.Kind && overrider.Subject.Name == app.Name && overrider.Subject.APIVersion == app.APIVersion { + overriderList.Items = append(overriderList.Items, overrider) } } klog.Info("Successfully list YurtAppOverrider") From 15a5d0929e924dc8b42419b5932f5fd4661606e6 Mon Sep 17 00:00:00 2001 From: vie-serendipity <2733147505@qq.com> Date: Wed, 30 Aug 2023 11:17:03 +0800 Subject: [PATCH 06/20] modify deployment webhook failurePolicy --- pkg/yurtmanager/webhook/util/configuration/configuration.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/pkg/yurtmanager/webhook/util/configuration/configuration.go b/pkg/yurtmanager/webhook/util/configuration/configuration.go index 32b8744d9de..5ee809b5e1e 100644 --- a/pkg/yurtmanager/webhook/util/configuration/configuration.go +++ b/pkg/yurtmanager/webhook/util/configuration/configuration.go @@ -32,8 +32,9 @@ import ( ) var ( - deploymentMutatingPath = "/mutate-apps-v1-deployment" - deploymentSideEffect = admissionregistrationv1.SideEffectClassNone + deploymentMutatingPath = "/mutate-apps-v1-deployment" + deploymentSideEffect = admissionregistrationv1.SideEffectClassNone + deploymentFailurePolicy = admissionregistrationv1.Ignore ) func Ensure(kubeClient clientset.Interface, handlers map[string]struct{}, caBundle []byte, webhookPort int) error { @@ -89,6 +90,7 @@ func Ensure(kubeClient clientset.Interface, handlers map[string]struct{}, caBund Name: webhookutil.GetServiceName(), Path: &deploymentMutatingPath, } + wh.FailurePolicy = &deploymentFailurePolicy wh.ClientConfig = admissionregistrationv1.WebhookClientConfig{ Service: svc, CABundle: caBundle, From dd9c1f1cadc54584526368ad99de37a4e708b0a2 Mon Sep 17 00:00:00 2001 From: vie-serendipity <2733147505@qq.com> Date: Wed, 30 Aug 2023 14:49:11 +0800 Subject: [PATCH 07/20] do some modifies --- .../apps.openyurt.io_yurtappoverriders.yaml | 224 +++++++++--------- .../apps/v1alpha1/yurtappoverrider_types.go | 12 +- .../yurtappoverrider_controller.go | 55 +++-- .../v1alpha1/deploymentrender_default.go | 13 +- .../yurtappconfigrender_validation.go | 36 +-- 5 files changed, 171 insertions(+), 169 deletions(-) diff --git a/charts/yurt-manager/crds/apps.openyurt.io_yurtappoverriders.yaml b/charts/yurt-manager/crds/apps.openyurt.io_yurtappoverriders.yaml index acfd024baf9..d2b08cbd0a0 100644 --- a/charts/yurt-manager/crds/apps.openyurt.io_yurtappoverriders.yaml +++ b/charts/yurt-manager/crds/apps.openyurt.io_yurtappoverriders.yaml @@ -12,132 +12,130 @@ spec: listKind: YurtAppOverriderList plural: yurtappoverriders shortNames: - - yacr + - yao singular: yurtappoverrider scope: Namespaced versions: - - additionalPrinterColumns: - - description: The subject kind of this overrider. - jsonPath: .subject.kind - name: Subject - type: string - - description: The subject name of this overrider. - jsonPath: .subject.name - name: Name - type: string - - description: CreationTimestamp is a timestamp representing the server time when - this object was created. It is not guaranteed to be set in happens-before - order across separate operations. Clients may not set this value. It is represented - in RFC3339 form and is in UTC. - jsonPath: .metadata.creationTimestamp - name: AGE - type: date - name: v1alpha1 - schema: - openAPIV3Schema: - properties: - apiVersion: - description: 'APIVersion defines the versioned schema of this representation + - additionalPrinterColumns: + - description: The subject kind of this overrider. + jsonPath: .subject.kind + name: Subject + type: string + - description: The subject name of this overrider. + jsonPath: .subject.name + name: Name + type: string + - description: CreationTimestamp is a timestamp representing the server time when + this object was created. It is not guaranteed to be set in happens-before + order across separate operations. Clients may not set this value. It is represented + in RFC3339 form and is in UTC. + jsonPath: .metadata.creationTimestamp + name: AGE + type: date + name: v1alpha1 + schema: + openAPIV3Schema: + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' - type: string - entries: - items: - description: Describe detailed multi-region configuration of the subject - Entry describe a set of nodepools and their shared or identical configurations - properties: - items: - items: - description: Item represents configuration to be injected. Only - one of its members may be specified. - properties: - image: - description: ImageItem specifies the corresponding container - and the claimed image - properties: - containerName: - description: ContainerName represents name of the container - in which the Image will be replaced - type: string - imageClaim: - description: ImageClaim represents the claimed image name - which is injected into the container above - type: string - required: - - containerName - - imageClaim - type: object - replicas: - format: int32 - type: integer - type: object - type: array - patches: - description: Convert Patch struct into json patch operation - items: - properties: - operation: - description: type represents the operation default is strategic - merge patch - enum: - - add - - remove - - replace - type: string - path: - description: Path represents the path in the json patch - type: string - value: - description: Indicates the patch for the template - x-kubernetes-preserve-unknown-fields: true - required: - - operation - - path - type: object - type: array - pools: + type: string + entries: + items: + description: Describe detailed multi-region configuration of the subject + Entry describe a set of nodepools and their shared or identical configurations + properties: items: - type: string - type: array - required: - - pools - type: object - type: array - kind: - description: 'Kind is a string value representing the REST resource this + items: + description: Item represents configuration to be injected. Only + one of its members may be specified. + properties: + image: + description: ImageItem specifies the corresponding container + and the claimed image + properties: + containerName: + description: ContainerName represents name of the container + in which the Image will be replaced + type: string + imageClaim: + description: ImageClaim represents the claimed image name + which is injected into the container above + type: string + required: + - containerName + - imageClaim + type: object + replicas: + format: int32 + type: integer + type: object + type: array + patches: + description: Convert Patch struct into json patch operation + items: + properties: + operation: + description: Operation represents the operation + enum: + - add + - remove + - replace + type: string + path: + description: Path represents the path in the json patch + type: string + value: + description: Indicates the value of json patch + x-kubernetes-preserve-unknown-fields: true + required: + - operation + - path + type: object + type: array + pools: + items: + type: string + type: array + required: + - pools + type: object + type: array + kind: + description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' - type: string - metadata: - type: object - subject: - description: Describe the object Entries belongs - properties: - apiVersion: - description: 'APIVersion defines the versioned schema of this representation + type: string + metadata: + type: object + subject: + description: Describe the object Entries belongs + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' - type: string - kind: - description: 'Kind is a string value representing the REST resource + type: string + kind: + description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' - type: string - name: - description: Name is the name of YurtAppSet or YurtAppDaemon - type: string - required: - - name - type: object - required: - - entries - - subject - type: object - served: true - storage: true - subresources: - status: {} + type: string + name: + description: Name is the name of YurtAppSet or YurtAppDaemon + type: string + required: + - name + type: object + required: + - entries + - subject + type: object + served: true + storage: true + subresources: {} status: acceptedNames: kind: "" diff --git a/pkg/apis/apps/v1alpha1/yurtappoverrider_types.go b/pkg/apis/apps/v1alpha1/yurtappoverrider_types.go index a305ea536d2..c7d658b7bb8 100644 --- a/pkg/apis/apps/v1alpha1/yurtappoverrider_types.go +++ b/pkg/apis/apps/v1alpha1/yurtappoverrider_types.go @@ -46,7 +46,6 @@ type Item struct { type Operation string const ( - Default Operation = "default" // strategic merge patch ADD Operation = "add" // json patch REMOVE Operation = "remove" // json patch REPLACE Operation = "replace" // json patch @@ -55,11 +54,10 @@ const ( type Patch struct { // Path represents the path in the json patch Path string `json:"path"` - // type represents the operation - // default is strategic merge patch + // Operation represents the operation // +kubebuilder:validation:Enum=add;remove;replace Operation Operation `json:"operation"` - // Indicates the patch for the template + // Indicates the value of json patch // +optional Value apiextensionsv1.JSON `json:"value,omitempty"` } @@ -84,8 +82,7 @@ type Subject struct { // +genclient // +kubebuilder:object:root=true -// +kubebuilder:subresource:status -// +kubebuilder:resource:shortName=yacr +// +kubebuilder:resource:shortName=yao // +kubebuilder:printcolumn:name="Subject",type="string",JSONPath=".subject.kind",description="The subject kind of this overrider." // +kubebuilder:printcolumn:name="Name",type="string",JSONPath=".subject.name",description="The subject name of this overrider." // +kubebuilder:printcolumn:name="AGE",type="date",JSONPath=".metadata.creationTimestamp",description="CreationTimestamp is a timestamp representing the server time when this object was created. It is not guaranteed to be set in happens-before order across separate operations. Clients may not set this value. It is represented in RFC3339 form and is in UTC." @@ -97,9 +94,8 @@ type YurtAppOverrider struct { Entries []Entry `json:"entries"` } -//+kubebuilder:object:root=true - // YurtAppOverriderList contains a list of YurtAppOverrider +// +kubebuilder:object:root=true type YurtAppOverriderList struct { metav1.TypeMeta `json:",inline"` metav1.ListMeta `json:"metadata,omitempty"` diff --git a/pkg/yurtmanager/controller/yurtappoverrider/yurtappoverrider_controller.go b/pkg/yurtmanager/controller/yurtappoverrider/yurtappoverrider_controller.go index b6d0e869ab0..8bbb41cd48f 100644 --- a/pkg/yurtmanager/controller/yurtappoverrider/yurtappoverrider_controller.go +++ b/pkg/yurtmanager/controller/yurtappoverrider/yurtappoverrider_controller.go @@ -24,6 +24,7 @@ import ( v1 "k8s.io/api/apps/v1" "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/types" "k8s.io/client-go/tools/record" "k8s.io/klog/v2" "sigs.k8s.io/controller-runtime/pkg/client" @@ -38,7 +39,6 @@ import ( appconfig "github.com/openyurtio/openyurt/cmd/yurt-manager/app/config" appsv1alpha1 "github.com/openyurtio/openyurt/pkg/apis/apps/v1alpha1" "github.com/openyurtio/openyurt/pkg/yurtmanager/controller/yurtappoverrider/config" - "github.com/openyurtio/openyurt/pkg/yurtmanager/webhook/deploymentrender/v1alpha1" ) func init() { @@ -75,18 +75,18 @@ var _ reconcile.Reconciler = &ReconcileYurtAppOverrider{} // ReconcileYurtAppOverrider reconciles a YurtAppOverrider object type ReconcileYurtAppOverrider struct { client.Client - scheme *runtime.Scheme - recorder record.EventRecorder - Configration config.YurtAppOverriderControllerConfiguration + scheme *runtime.Scheme + recorder record.EventRecorder + Configuration config.YurtAppOverriderControllerConfiguration } // newReconciler returns a new reconcile.Reconciler func newReconciler(c *appconfig.CompletedConfig, mgr manager.Manager) reconcile.Reconciler { return &ReconcileYurtAppOverrider{ - Client: mgr.GetClient(), - scheme: mgr.GetScheme(), - recorder: mgr.GetEventRecorderFor(ControllerName), - Configration: c.ComponentConfig.YurtAppOverriderController, + Client: mgr.GetClient(), + scheme: mgr.GetScheme(), + recorder: mgr.GetEventRecorderFor(ControllerName), + Configuration: c.ComponentConfig.YurtAppOverriderController, } } @@ -177,17 +177,36 @@ func (r *ReconcileYurtAppOverrider) Reconcile(_ context.Context, request reconci return reconcile.Result{}, nil } -func (r *ReconcileYurtAppOverrider) updatePools(yacr *appsv1alpha1.YurtAppOverrider) error { - - pools := []string(nil) - for _, entry := range yacr.Entries { - pools = append(pools, entry.Pools...) +func (r *ReconcileYurtAppOverrider) updatePools(yao *appsv1alpha1.YurtAppOverrider) error { + pools := make(map[string]struct{}) + for _, entry := range yao.Entries { + for _, pool := range entry.Pools { + pools[pool] = struct{}{} + } } - deploymentRenderHandler := v1alpha1.DeploymentRenderHandler{ - Client: r.Client, - Scheme: r.scheme, + if _, ok := pools["*"]; ok { + switch yao.Subject.Kind { + case "YurtAppSet": + instance := &appsv1alpha1.YurtAppSet{} + if err := r.Get(context.TODO(), types.NamespacedName{Namespace: yao.Namespace, Name: yao.Subject.Name}, instance); err != nil { + return err + } + for _, pool := range instance.Spec.Topology.Pools { + pools[pool.Name] = struct{}{} + } + case "YurtAppDaemon": + instance := &appsv1alpha1.YurtAppDaemon{} + if err := r.Get(context.TODO(), types.NamespacedName{Namespace: yao.Namespace, Name: yao.Subject.Name}, instance); err != nil { + return err + } + for _, pool := range instance.Status.NodePools { + pools[pool] = struct{}{} + } + default: + return errors.NewBadRequest("unknown subject kind") + } } - for _, pool := range pools { + for pool := range pools { deployments := v1.DeploymentList{} listOptions := client.MatchingLabels{"apps.openyurt.io/pool-name": pool} err := r.List(context.TODO(), &deployments, listOptions) @@ -195,7 +214,7 @@ func (r *ReconcileYurtAppOverrider) updatePools(yacr *appsv1alpha1.YurtAppOverri return err } for _, deployment := range deployments.Items { - if err := deploymentRenderHandler.Default(context.TODO(), &deployment); err != nil { + if err := r.Client.Update(context.TODO(), &deployment); err != nil { return err } } diff --git a/pkg/yurtmanager/webhook/deploymentrender/v1alpha1/deploymentrender_default.go b/pkg/yurtmanager/webhook/deploymentrender/v1alpha1/deploymentrender_default.go index e0d958d638e..3b29d5ac572 100644 --- a/pkg/yurtmanager/webhook/deploymentrender/v1alpha1/deploymentrender_default.go +++ b/pkg/yurtmanager/webhook/deploymentrender/v1alpha1/deploymentrender_default.go @@ -51,15 +51,10 @@ func contain(kind string, resources []string) bool { // Default satisfies the defaulting webhook interface. func (webhook *DeploymentRenderHandler) Default(ctx context.Context, obj runtime.Object) error { - deployment, ok := obj.(*v1.Deployment) if !ok { return apierrors.NewBadRequest(fmt.Sprintf("expected a YurtAppOverrider but got a %T", obj)) } - if v, ok := deployment.Annotations[DeploymentMutatingWebhook]; ok && v == "mutated" { - delete(deployment.Annotations, DeploymentMutatingWebhook) - return nil - } if deployment.OwnerReferences == nil { return nil } @@ -135,7 +130,7 @@ func (webhook *DeploymentRenderHandler) Default(ctx context.Context, obj runtime for _, entry := range render.Entries { pools := entry.Pools for _, pool := range pools { - if pool == nodepool || pool == "*" { + if pool == nodepool || pool == `"*"` { items := entry.Items // Replace items if err := replaceItems(deployment, items); err != nil { @@ -150,7 +145,6 @@ func (webhook *DeploymentRenderHandler) Default(ctx context.Context, obj runtime patches[i].Value = apiextensionsv1.JSON{Raw: []byte(newPatchString)} } } - // Implement injection dataStruct := v1.Deployment{} pc := PatchControl{ @@ -165,11 +159,6 @@ func (webhook *DeploymentRenderHandler) Default(ctx context.Context, obj runtime } } } - deployment.Annotations[DeploymentMutatingWebhook] = "mutated" - if err := webhook.Client.Update(ctx, deployment); err != nil { - klog.Infof("error to update deployment: %v", err) - return err - } klog.Info("Successfully render deployment") return nil } diff --git a/pkg/yurtmanager/webhook/yurtappoverrider/v1alpha1/yurtappconfigrender_validation.go b/pkg/yurtmanager/webhook/yurtappoverrider/v1alpha1/yurtappconfigrender_validation.go index d67ccf5a569..a19f4678658 100644 --- a/pkg/yurtmanager/webhook/yurtappoverrider/v1alpha1/yurtappconfigrender_validation.go +++ b/pkg/yurtmanager/webhook/yurtappoverrider/v1alpha1/yurtappconfigrender_validation.go @@ -30,16 +30,16 @@ import ( // ValidateCreate implements webhook.CustomValidator so a webhook will be registered for the type. func (webhook *YurtAppOverriderHandler) ValidateCreate(ctx context.Context, obj runtime.Object) error { - configRender, ok := obj.(*v1alpha1.YurtAppOverrider) + overrider, ok := obj.(*v1alpha1.YurtAppOverrider) if !ok { return apierrors.NewBadRequest(fmt.Sprintf("expected a YurtAppOverrider but got a %T", obj)) } // validate - if err := webhook.validateOneToOne(ctx, configRender); err != nil { + if err := webhook.validateOneToOne(ctx, overrider); err != nil { return err } - if err := webhook.validateStar(configRender); err != nil { + if err := webhook.validateStar(overrider); err != nil { return err } return nil @@ -51,16 +51,16 @@ func (webhook *YurtAppOverriderHandler) ValidateUpdate(ctx context.Context, oldO if !ok { return apierrors.NewBadRequest(fmt.Sprintf("expected a YurtAppOverrider but got a %T", newObj)) } - newConfigRender, ok := oldObj.(*v1alpha1.YurtAppOverrider) + newOverrider, ok := oldObj.(*v1alpha1.YurtAppOverrider) if !ok { return apierrors.NewBadRequest(fmt.Sprintf("expected a YurtAppOverrider} but got a %T", oldObj)) } // validate - if err := webhook.validateOneToOne(ctx, newConfigRender); err != nil { + if err := webhook.validateOneToOne(ctx, newOverrider); err != nil { return err } - if err := webhook.validateStar(newConfigRender); err != nil { + if err := webhook.validateStar(newOverrider); err != nil { return err } return nil @@ -77,30 +77,30 @@ func (webhook *YurtAppOverriderHandler) ValidateDelete(_ context.Context, obj ru } // YurtConfigRender and YurtAppSet are one-to-one relationship -func (webhook *YurtAppOverriderHandler) validateOneToOne(ctx context.Context, configRender *v1alpha1.YurtAppOverrider) error { - app := configRender.Subject - var allConfigRenderList v1alpha1.YurtAppOverriderList - if err := webhook.Client.List(ctx, &allConfigRenderList, client.InNamespace(configRender.Namespace)); err != nil { +func (webhook *YurtAppOverriderHandler) validateOneToOne(ctx context.Context, yurtAppOverrider *v1alpha1.YurtAppOverrider) error { + app := yurtAppOverrider.Subject + var allOverriderList v1alpha1.YurtAppOverriderList + if err := webhook.Client.List(ctx, &allOverriderList, client.InNamespace(yurtAppOverrider.Namespace)); err != nil { klog.Info("error in listing YurtAppOverrider") return err } - var configRenderList = v1alpha1.YurtAppOverriderList{} - for _, configRender := range allConfigRenderList.Items { - if configRender.Subject.Kind == app.Kind && configRender.Name == app.Name && configRender.APIVersion == app.APIVersion { - configRenderList.Items = append(configRenderList.Items, configRender) + var overriderList = v1alpha1.YurtAppOverriderList{} + for _, overrider := range overriderList.Items { + if overrider.Subject.Kind == app.Kind && overrider.Name == app.Name && overrider.APIVersion == app.APIVersion { + overriderList.Items = append(overriderList.Items, overrider) } } - if len(configRenderList.Items) > 0 { + if len(overriderList.Items) > 0 { return fmt.Errorf("only one YurtAppOverrider can be bound into one YurtAppSet") } return nil } // Verify that * and other pools are not set at the same time -func (webhook *YurtAppOverriderHandler) validateStar(configRender *v1alpha1.YurtAppOverrider) error { - for _, entry := range configRender.Entries { +func (webhook *YurtAppOverriderHandler) validateStar(yurtAppOverrider *v1alpha1.YurtAppOverrider) error { + for _, entry := range yurtAppOverrider.Entries { for _, pool := range entry.Pools { - if pool == "*" && len(entry.Pools) > 1 { + if pool == `"*"` && len(entry.Pools) > 1 { return fmt.Errorf("pool can't be '*' when other pools are set") } } From 286a0ffb05cb25cf6b92e60a896ae486a13ac464 Mon Sep 17 00:00:00 2001 From: vie-serendipity <2733147505@qq.com> Date: Wed, 30 Aug 2023 14:57:35 +0800 Subject: [PATCH 08/20] rename missing configrender to overrider --- .../v1alpha1/deploymentrender_webhook_test.go | 2 +- .../webhook/deploymentrender/v1alpha1/patch_control_test.go | 2 +- ...appconfigrender_default.go => yurtappoverrider_default.go} | 0 ...appconfigrender_handler.go => yurtappoverrider_handler.go} | 0 ...figrender_validation.go => yurtappoverrider_validation.go} | 2 +- test/e2e/yurt/yurtappoverrider.go | 4 ++-- 6 files changed, 5 insertions(+), 5 deletions(-) rename pkg/yurtmanager/webhook/yurtappoverrider/v1alpha1/{yurtappconfigrender_default.go => yurtappoverrider_default.go} (100%) rename pkg/yurtmanager/webhook/yurtappoverrider/v1alpha1/{yurtappconfigrender_handler.go => yurtappoverrider_handler.go} (100%) rename pkg/yurtmanager/webhook/yurtappoverrider/v1alpha1/{yurtappconfigrender_validation.go => yurtappoverrider_validation.go} (98%) diff --git a/pkg/yurtmanager/webhook/deploymentrender/v1alpha1/deploymentrender_webhook_test.go b/pkg/yurtmanager/webhook/deploymentrender/v1alpha1/deploymentrender_webhook_test.go index 1c7dcd24b04..17373423818 100644 --- a/pkg/yurtmanager/webhook/deploymentrender/v1alpha1/deploymentrender_webhook_test.go +++ b/pkg/yurtmanager/webhook/deploymentrender/v1alpha1/deploymentrender_webhook_test.go @@ -148,7 +148,7 @@ var overrider1 = &v1alpha1.YurtAppOverrider{ Operation: v1alpha1.REPLACE, Path: "/spec/replicas", Value: apiextensionsv1.JSON{ - Raw: []byte("3"), + Raw: []byte(`"3"`), }, }, }, diff --git a/pkg/yurtmanager/webhook/deploymentrender/v1alpha1/patch_control_test.go b/pkg/yurtmanager/webhook/deploymentrender/v1alpha1/patch_control_test.go index 67742a6a68f..55a564a09ff 100644 --- a/pkg/yurtmanager/webhook/deploymentrender/v1alpha1/patch_control_test.go +++ b/pkg/yurtmanager/webhook/deploymentrender/v1alpha1/patch_control_test.go @@ -78,7 +78,7 @@ var patchControl = PatchControl{ Operation: v1alpha1.ADD, Path: "/spec/replicas", Value: apiextensionsv1.JSON{ - Raw: []byte("5"), + Raw: []byte(`"5"`), }, }, }, diff --git a/pkg/yurtmanager/webhook/yurtappoverrider/v1alpha1/yurtappconfigrender_default.go b/pkg/yurtmanager/webhook/yurtappoverrider/v1alpha1/yurtappoverrider_default.go similarity index 100% rename from pkg/yurtmanager/webhook/yurtappoverrider/v1alpha1/yurtappconfigrender_default.go rename to pkg/yurtmanager/webhook/yurtappoverrider/v1alpha1/yurtappoverrider_default.go diff --git a/pkg/yurtmanager/webhook/yurtappoverrider/v1alpha1/yurtappconfigrender_handler.go b/pkg/yurtmanager/webhook/yurtappoverrider/v1alpha1/yurtappoverrider_handler.go similarity index 100% rename from pkg/yurtmanager/webhook/yurtappoverrider/v1alpha1/yurtappconfigrender_handler.go rename to pkg/yurtmanager/webhook/yurtappoverrider/v1alpha1/yurtappoverrider_handler.go diff --git a/pkg/yurtmanager/webhook/yurtappoverrider/v1alpha1/yurtappconfigrender_validation.go b/pkg/yurtmanager/webhook/yurtappoverrider/v1alpha1/yurtappoverrider_validation.go similarity index 98% rename from pkg/yurtmanager/webhook/yurtappoverrider/v1alpha1/yurtappconfigrender_validation.go rename to pkg/yurtmanager/webhook/yurtappoverrider/v1alpha1/yurtappoverrider_validation.go index a19f4678658..7b6df71f9e2 100644 --- a/pkg/yurtmanager/webhook/yurtappoverrider/v1alpha1/yurtappconfigrender_validation.go +++ b/pkg/yurtmanager/webhook/yurtappoverrider/v1alpha1/yurtappoverrider_validation.go @@ -76,7 +76,7 @@ func (webhook *YurtAppOverriderHandler) ValidateDelete(_ context.Context, obj ru return nil } -// YurtConfigRender and YurtAppSet are one-to-one relationship +// YurtAppOverrider and YurtAppSet are one-to-one relationship func (webhook *YurtAppOverriderHandler) validateOneToOne(ctx context.Context, yurtAppOverrider *v1alpha1.YurtAppOverrider) error { app := yurtAppOverrider.Subject var allOverriderList v1alpha1.YurtAppOverriderList diff --git a/test/e2e/yurt/yurtappoverrider.go b/test/e2e/yurt/yurtappoverrider.go index fe7b2cfcb41..17981f6bf32 100644 --- a/test/e2e/yurt/yurtappoverrider.go +++ b/test/e2e/yurt/yurtappoverrider.go @@ -169,10 +169,10 @@ var _ = Describe("YurtAppOverrider Test", func() { }, Patches: []v1alpha1.Patch{ { - Operation: v1alpha1.Default, + Operation: v1alpha1.REPLACE, Path: "/spec/template/spec/containers/0/image", Value: apiextensionsv1.JSON{ - Raw: []byte("nginx-patch"), + Raw: []byte(`"nginx-patch"`), }, }, }, From 1f80a99feb39b416437315d9d6c1713a6b1711f0 Mon Sep 17 00:00:00 2001 From: vie-serendipity <2733147505@qq.com> Date: Wed, 30 Aug 2023 15:17:47 +0800 Subject: [PATCH 09/20] remove the outer of * --- .../deploymentrender/v1alpha1/deploymentrender_default.go | 2 +- .../yurtappoverrider/v1alpha1/yurtappoverrider_validation.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/yurtmanager/webhook/deploymentrender/v1alpha1/deploymentrender_default.go b/pkg/yurtmanager/webhook/deploymentrender/v1alpha1/deploymentrender_default.go index 3b29d5ac572..ef14a688fa8 100644 --- a/pkg/yurtmanager/webhook/deploymentrender/v1alpha1/deploymentrender_default.go +++ b/pkg/yurtmanager/webhook/deploymentrender/v1alpha1/deploymentrender_default.go @@ -130,7 +130,7 @@ func (webhook *DeploymentRenderHandler) Default(ctx context.Context, obj runtime for _, entry := range render.Entries { pools := entry.Pools for _, pool := range pools { - if pool == nodepool || pool == `"*"` { + if pool == nodepool || pool == "*" { items := entry.Items // Replace items if err := replaceItems(deployment, items); err != nil { diff --git a/pkg/yurtmanager/webhook/yurtappoverrider/v1alpha1/yurtappoverrider_validation.go b/pkg/yurtmanager/webhook/yurtappoverrider/v1alpha1/yurtappoverrider_validation.go index 7b6df71f9e2..a01ec21cd8f 100644 --- a/pkg/yurtmanager/webhook/yurtappoverrider/v1alpha1/yurtappoverrider_validation.go +++ b/pkg/yurtmanager/webhook/yurtappoverrider/v1alpha1/yurtappoverrider_validation.go @@ -100,7 +100,7 @@ func (webhook *YurtAppOverriderHandler) validateOneToOne(ctx context.Context, yu func (webhook *YurtAppOverriderHandler) validateStar(yurtAppOverrider *v1alpha1.YurtAppOverrider) error { for _, entry := range yurtAppOverrider.Entries { for _, pool := range entry.Pools { - if pool == `"*"` && len(entry.Pools) > 1 { + if pool == "*" && len(entry.Pools) > 1 { return fmt.Errorf("pool can't be '*' when other pools are set") } } From a75de1f15c5f13c261d123a64421dfe633f1948c Mon Sep 17 00:00:00 2001 From: vie-serendipity <2733147505@qq.com> Date: Mon, 4 Sep 2023 16:33:41 +0800 Subject: [PATCH 10/20] fix problems in the comments --- .../yurt-manager-auto-generated.yaml | 44 ++++--- go.mod | 2 +- .../yurtappoverrider_controller.go | 107 +++--------------- .../v1alpha1/deploymentrender_default.go | 12 +- .../v1alpha1/deploymentrender_webhook_test.go | 2 +- .../deploymentrender/v1alpha1/item_control.go | 5 + .../v1alpha1/patch_control_test.go | 2 +- pkg/yurtmanager/webhook/server.go | 3 + .../util/configuration/configuration.go | 5 +- .../v1alpha1/yurtappoverrider_validation.go | 41 ++----- test/e2e/yurt/yurtappoverrider.go | 7 +- 11 files changed, 68 insertions(+), 162 deletions(-) diff --git a/charts/yurt-manager/templates/yurt-manager-auto-generated.yaml b/charts/yurt-manager/templates/yurt-manager-auto-generated.yaml index 76afe100ea6..ac047530d86 100644 --- a/charts/yurt-manager/templates/yurt-manager-auto-generated.yaml +++ b/charts/yurt-manager/templates/yurt-manager-auto-generated.yaml @@ -164,12 +164,8 @@ rules: resources: - yurtappoverriders verbs: - - create - - delete - get - list - - patch - - update - watch - apiGroups: - apps.openyurt.io @@ -632,13 +628,14 @@ webhooks: sideEffects: None - admissionReviewVersions: - v1 + - v1beta1 clientConfig: service: name: yurt-manager-webhook-service namespace: {{ .Release.Namespace }} - path: /mutate-apps-openyurt-io-v1alpha1-yurtappset + path: /mutate-apps-openyurt-io-v1alpha1-yurtappoverrider failurePolicy: Fail - name: myurtappset.kb.io + name: mutate.apps.v1alpha1.yurtappoverrider.openyurt.io rules: - apiGroups: - apps.openyurt.io @@ -648,18 +645,17 @@ webhooks: - CREATE - UPDATE resources: - - yurtappsets + - yurtappoverriders sideEffects: None - admissionReviewVersions: - v1 - - v1beta1 clientConfig: service: name: yurt-manager-webhook-service namespace: {{ .Release.Namespace }} - path: /mutate-apps-openyurt-io-v1alpha1-yurtstaticset + path: /mutate-apps-openyurt-io-v1alpha1-yurtappset failurePolicy: Fail - name: mutate.apps.v1alpha1.yurtstaticset.openyurt.io + name: myurtappset.kb.io rules: - apiGroups: - apps.openyurt.io @@ -669,7 +665,7 @@ webhooks: - CREATE - UPDATE resources: - - yurtstaticsets + - yurtappsets sideEffects: None - admissionReviewVersions: - v1 @@ -678,9 +674,9 @@ webhooks: service: name: yurt-manager-webhook-service namespace: {{ .Release.Namespace }} - path: /mutate-apps-openyurt-io-v1alpha1-yurtappoverrider + path: /mutate-apps-openyurt-io-v1alpha1-yurtstaticset failurePolicy: Fail - name: mutate.apps.v1alpha1.yurtappoverrider.openyurt.io + name: mutate.apps.v1alpha1.yurtstaticset.openyurt.io rules: - apiGroups: - apps.openyurt.io @@ -690,7 +686,7 @@ webhooks: - CREATE - UPDATE resources: - - yurtappoverriders + - yurtstaticsets sideEffects: None --- apiVersion: admissionregistration.k8s.io/v1 @@ -824,13 +820,14 @@ webhooks: sideEffects: None - admissionReviewVersions: - v1 + - v1beta1 clientConfig: service: name: yurt-manager-webhook-service namespace: {{ .Release.Namespace }} - path: /validate-apps-openyurt-io-v1alpha1-yurtappset + path: /validate-apps-openyurt-io-v1alpha1-yurtappoverrider failurePolicy: Fail - name: vyurtappset.kb.io + name: validate.apps.v1alpha1.yurtappoverrider.openyurt.io rules: - apiGroups: - apps.openyurt.io @@ -840,18 +837,17 @@ webhooks: - CREATE - UPDATE resources: - - yurtappsets + - yurtappoverriders sideEffects: None - admissionReviewVersions: - v1 - - v1beta1 clientConfig: service: name: yurt-manager-webhook-service namespace: {{ .Release.Namespace }} - path: /validate-apps-openyurt-io-v1alpha1-yurtstaticset + path: /validate-apps-openyurt-io-v1alpha1-yurtappset failurePolicy: Fail - name: validate.apps.v1alpha1.yurtstaticset.openyurt.io + name: vyurtappset.kb.io rules: - apiGroups: - apps.openyurt.io @@ -861,7 +857,7 @@ webhooks: - CREATE - UPDATE resources: - - yurtstaticsets + - yurtappsets sideEffects: None - admissionReviewVersions: - v1 @@ -870,9 +866,9 @@ webhooks: service: name: yurt-manager-webhook-service namespace: {{ .Release.Namespace }} - path: /validate-apps-openyurt-io-v1alpha1-yurtappoverrider + path: /validate-apps-openyurt-io-v1alpha1-yurtstaticset failurePolicy: Fail - name: validate.apps.v1alpha1.yurtappoverrider.openyurt.io + name: validate.apps.v1alpha1.yurtstaticset.openyurt.io rules: - apiGroups: - apps.openyurt.io @@ -882,5 +878,5 @@ webhooks: - CREATE - UPDATE resources: - - yurtappoverriders + - yurtstaticsets sideEffects: None diff --git a/go.mod b/go.mod index 6bdad04c789..b260becf00f 100644 --- a/go.mod +++ b/go.mod @@ -7,6 +7,7 @@ require ( github.com/davecgh/go-spew v1.1.1 github.com/edgexfoundry/go-mod-core-contracts/v2 v2.3.0 github.com/edgexfoundry/go-mod-core-contracts/v3 v3.0.0 + github.com/evanphx/json-patch v5.6.0+incompatible github.com/go-resty/resty/v2 v2.7.0 github.com/golang-jwt/jwt v3.2.2+incompatible github.com/google/uuid v1.3.0 @@ -86,7 +87,6 @@ require ( github.com/cyphar/filepath-securejoin v0.2.3 // indirect github.com/docker/distribution v2.7.1+incompatible // indirect github.com/emicklei/go-restful v2.16.0+incompatible // indirect - github.com/evanphx/json-patch v5.6.0+incompatible // indirect github.com/fatih/camelcase v1.0.0 // indirect github.com/felixge/httpsnoop v1.0.1 // indirect github.com/fsnotify/fsnotify v1.6.0 // indirect diff --git a/pkg/yurtmanager/controller/yurtappoverrider/yurtappoverrider_controller.go b/pkg/yurtmanager/controller/yurtappoverrider/yurtappoverrider_controller.go index 8bbb41cd48f..dc624631636 100644 --- a/pkg/yurtmanager/controller/yurtappoverrider/yurtappoverrider_controller.go +++ b/pkg/yurtmanager/controller/yurtappoverrider/yurtappoverrider_controller.go @@ -20,19 +20,15 @@ import ( "context" "flag" "fmt" + "time" v1 "k8s.io/api/apps/v1" "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/types" - "k8s.io/client-go/tools/record" "k8s.io/klog/v2" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/controller" - "sigs.k8s.io/controller-runtime/pkg/event" "sigs.k8s.io/controller-runtime/pkg/handler" "sigs.k8s.io/controller-runtime/pkg/manager" - "sigs.k8s.io/controller-runtime/pkg/predicate" "sigs.k8s.io/controller-runtime/pkg/reconcile" "sigs.k8s.io/controller-runtime/pkg/source" @@ -51,7 +47,7 @@ var ( ) const ( - ControllerName = "YurtAppOverrider-controller" + ControllerName = "yurtappoverrider" ) func Format(format string, args ...interface{}) string { @@ -75,8 +71,6 @@ var _ reconcile.Reconciler = &ReconcileYurtAppOverrider{} // ReconcileYurtAppOverrider reconciles a YurtAppOverrider object type ReconcileYurtAppOverrider struct { client.Client - scheme *runtime.Scheme - recorder record.EventRecorder Configuration config.YurtAppOverriderControllerConfiguration } @@ -84,8 +78,6 @@ type ReconcileYurtAppOverrider struct { func newReconciler(c *appconfig.CompletedConfig, mgr manager.Manager) reconcile.Reconciler { return &ReconcileYurtAppOverrider{ Client: mgr.GetClient(), - scheme: mgr.GetScheme(), - recorder: mgr.GetEventRecorderFor(ControllerName), Configuration: c.ComponentConfig.YurtAppOverriderController, } } @@ -100,44 +92,8 @@ func add(mgr manager.Manager, r reconcile.Reconciler) error { return err } - yurtappoverriderPredicates := predicate.Funcs{ - CreateFunc: func(evt event.CreateEvent) bool { - obj, ok := evt.Object.(*appsv1alpha1.YurtAppOverrider) - if !ok { - return ok - } - if err := r.(*ReconcileYurtAppOverrider).updatePools(obj); err != nil { - klog.Errorf("fail to update deployments belonging to obj: %v", err) - } - return true - }, - DeleteFunc: func(evt event.DeleteEvent) bool { - obj, ok := evt.Object.(*appsv1alpha1.YurtAppOverrider) - if !ok { - return ok - } - if err := r.(*ReconcileYurtAppOverrider).updatePools(obj); err != nil { - klog.Errorf("fail to update deployments belonging to obj: %v", err) - } - return true - }, - UpdateFunc: func(evt event.UpdateEvent) bool { - obj, ok := evt.ObjectOld.(*appsv1alpha1.YurtAppOverrider) - if !ok { - return ok - } - if err := r.(*ReconcileYurtAppOverrider).updatePools(obj); err != nil { - klog.Errorf("fail to update deployments belonging to obj: %v", err) - } - return true - }, - GenericFunc: func(evt event.GenericEvent) bool { - return false - }, - } - // Watch for changes to YurtAppOverrider - err = c.Watch(&source.Kind{Type: &appsv1alpha1.YurtAppOverrider{}}, &handler.EnqueueRequestForObject{}, yurtappoverriderPredicates) + err = c.Watch(&source.Kind{Type: &appsv1alpha1.YurtAppOverrider{}}, &handler.EnqueueRequestForObject{}) if err != nil { return err } @@ -145,11 +101,8 @@ func add(mgr manager.Manager, r reconcile.Reconciler) error { return nil } -// +kubebuilder:rbac:groups=apps.openyurt.io,resources=yurtappoverriders,verbs=get;list;watch;create;update;patch;delete -// +kubebuilder:rbac:groups=apps,resources=deployments,verbs=get;list;watch;create;update;patch;delete -// +kubebuilder:rbac:groups=apps,resources=deployments/status,verbs=get;update;patch -// +kubebuilder:rbac:groups=core,resources=events,verbs=get;list;watch;create;update;patch;delete -// +kubebuilder:rbac:groups=apps,resources=controllerrevisions,verbs=get;list;watch;create;update;patch;delete +// +kubebuilder:rbac:groups=apps.openyurt.io,resources=yurtappoverriders,verbs=get;list;watch +// +kubebuilder:rbac:groups=apps,resources=deployments,verbs=list;watch;update // Reconcile reads that state of the cluster for a YurtAppOverrider object and makes changes based on the state read // and what is in the YurtAppOverrider.Spec @@ -174,50 +127,20 @@ func (r *ReconcileYurtAppOverrider) Reconcile(_ context.Context, request reconci return reconcile.Result{}, nil } - return reconcile.Result{}, nil -} + deployments := v1.DeploymentList{} -func (r *ReconcileYurtAppOverrider) updatePools(yao *appsv1alpha1.YurtAppOverrider) error { - pools := make(map[string]struct{}) - for _, entry := range yao.Entries { - for _, pool := range entry.Pools { - pools[pool] = struct{}{} - } - } - if _, ok := pools["*"]; ok { - switch yao.Subject.Kind { - case "YurtAppSet": - instance := &appsv1alpha1.YurtAppSet{} - if err := r.Get(context.TODO(), types.NamespacedName{Namespace: yao.Namespace, Name: yao.Subject.Name}, instance); err != nil { - return err - } - for _, pool := range instance.Spec.Topology.Pools { - pools[pool.Name] = struct{}{} - } - case "YurtAppDaemon": - instance := &appsv1alpha1.YurtAppDaemon{} - if err := r.Get(context.TODO(), types.NamespacedName{Namespace: yao.Namespace, Name: yao.Subject.Name}, instance); err != nil { - return err - } - for _, pool := range instance.Status.NodePools { - pools[pool] = struct{}{} - } - default: - return errors.NewBadRequest("unknown subject kind") - } + if err := r.List(context.TODO(), &deployments); err != nil { + return reconcile.Result{}, err } - for pool := range pools { - deployments := v1.DeploymentList{} - listOptions := client.MatchingLabels{"apps.openyurt.io/pool-name": pool} - err := r.List(context.TODO(), &deployments, listOptions) - if err != nil { - return err - } - for _, deployment := range deployments.Items { + + for _, deployment := range deployments.Items { + if deployment.OwnerReferences[0].Kind == instance.Subject.Kind && deployment.OwnerReferences[0].Name == instance.Subject.Name { + deployment.Annotations["LastOverrideTime"] = time.Now().String() if err := r.Client.Update(context.TODO(), &deployment); err != nil { - return err + return reconcile.Result{}, err } } } - return nil + + return reconcile.Result{}, nil } diff --git a/pkg/yurtmanager/webhook/deploymentrender/v1alpha1/deploymentrender_default.go b/pkg/yurtmanager/webhook/deploymentrender/v1alpha1/deploymentrender_default.go index ef14a688fa8..efbb98bcb1f 100644 --- a/pkg/yurtmanager/webhook/deploymentrender/v1alpha1/deploymentrender_default.go +++ b/pkg/yurtmanager/webhook/deploymentrender/v1alpha1/deploymentrender_default.go @@ -53,7 +53,7 @@ func contain(kind string, resources []string) bool { func (webhook *DeploymentRenderHandler) Default(ctx context.Context, obj runtime.Object) error { deployment, ok := obj.(*v1.Deployment) if !ok { - return apierrors.NewBadRequest(fmt.Sprintf("expected a YurtAppOverrider but got a %T", obj)) + return apierrors.NewBadRequest(fmt.Sprintf("expected a Deployment but got a %T", obj)) } if deployment.OwnerReferences == nil { return nil @@ -79,7 +79,6 @@ func (webhook *DeploymentRenderHandler) Default(ctx context.Context, obj runtime }, instance); err != nil { return err } - klog.Info("Successfully get the owner of deployment") // Get nodepool of deployment nodepool := deployment.Labels["apps.openyurt.io/pool-name"] @@ -119,24 +118,25 @@ func (webhook *DeploymentRenderHandler) Default(ctx context.Context, obj runtime overriderList.Items = append(overriderList.Items, overrider) } } - klog.Info("Successfully list YurtAppOverrider") if len(overriderList.Items) == 0 { return nil } render := overriderList.Items[0] - klog.Info("start to render deployment") for _, entry := range render.Entries { pools := entry.Pools for _, pool := range pools { + if pool[0] == '-' && pool[1:] == nodepool { + continue + } if pool == nodepool || pool == "*" { items := entry.Items // Replace items if err := replaceItems(deployment, items); err != nil { + klog.Info("fail to replace items for deployment: %v", err) return err } - klog.Info("Successfully replace items for deployment") // json patch and strategic merge patches := entry.Patches for i, patch := range patches { @@ -153,9 +153,9 @@ func (webhook *DeploymentRenderHandler) Default(ctx context.Context, obj runtime dataStruct: dataStruct, } if err := pc.updatePatches(); err != nil { + klog.Infof("fail to update patches for deployment: %v", err) return err } - klog.Info("Successfully update patches for deployment") } } } diff --git a/pkg/yurtmanager/webhook/deploymentrender/v1alpha1/deploymentrender_webhook_test.go b/pkg/yurtmanager/webhook/deploymentrender/v1alpha1/deploymentrender_webhook_test.go index 17373423818..1c7dcd24b04 100644 --- a/pkg/yurtmanager/webhook/deploymentrender/v1alpha1/deploymentrender_webhook_test.go +++ b/pkg/yurtmanager/webhook/deploymentrender/v1alpha1/deploymentrender_webhook_test.go @@ -148,7 +148,7 @@ var overrider1 = &v1alpha1.YurtAppOverrider{ Operation: v1alpha1.REPLACE, Path: "/spec/replicas", Value: apiextensionsv1.JSON{ - Raw: []byte(`"3"`), + Raw: []byte("3"), }, }, }, diff --git a/pkg/yurtmanager/webhook/deploymentrender/v1alpha1/item_control.go b/pkg/yurtmanager/webhook/deploymentrender/v1alpha1/item_control.go index 1d68a944bbc..fc3036910e9 100644 --- a/pkg/yurtmanager/webhook/deploymentrender/v1alpha1/item_control.go +++ b/pkg/yurtmanager/webhook/deploymentrender/v1alpha1/item_control.go @@ -33,6 +33,11 @@ func replaceItems(deployment *v1.Deployment, items []v1alpha1.Item) error { deployment.Spec.Template.Spec.Containers[i].Image = item.Image.ImageClaim } } + for i := range deployment.Spec.Template.Spec.InitContainers { + if deployment.Spec.Template.Spec.InitContainers[i].Name == item.Image.ContainerName { + deployment.Spec.Template.Spec.InitContainers[i].Image = item.Image.ImageClaim + } + } } } return nil diff --git a/pkg/yurtmanager/webhook/deploymentrender/v1alpha1/patch_control_test.go b/pkg/yurtmanager/webhook/deploymentrender/v1alpha1/patch_control_test.go index 55a564a09ff..67742a6a68f 100644 --- a/pkg/yurtmanager/webhook/deploymentrender/v1alpha1/patch_control_test.go +++ b/pkg/yurtmanager/webhook/deploymentrender/v1alpha1/patch_control_test.go @@ -78,7 +78,7 @@ var patchControl = PatchControl{ Operation: v1alpha1.ADD, Path: "/spec/replicas", Value: apiextensionsv1.JSON{ - Raw: []byte(`"5"`), + Raw: []byte("5"), }, }, }, diff --git a/pkg/yurtmanager/webhook/server.go b/pkg/yurtmanager/webhook/server.go index 253e4f25480..fbb9edc27ea 100644 --- a/pkg/yurtmanager/webhook/server.go +++ b/pkg/yurtmanager/webhook/server.go @@ -32,6 +32,7 @@ import ( "github.com/openyurtio/openyurt/pkg/yurtmanager/controller/raven" ctrlutil "github.com/openyurtio/openyurt/pkg/yurtmanager/controller/util" "github.com/openyurtio/openyurt/pkg/yurtmanager/controller/yurtappdaemon" + "github.com/openyurtio/openyurt/pkg/yurtmanager/controller/yurtappoverrider" "github.com/openyurtio/openyurt/pkg/yurtmanager/controller/yurtappset" "github.com/openyurtio/openyurt/pkg/yurtmanager/controller/yurtstaticset" v1alpha1deploymentrender "github.com/openyurtio/openyurt/pkg/yurtmanager/webhook/deploymentrender/v1alpha1" @@ -44,6 +45,7 @@ import ( "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" + v1alpha1yurtappoverrider "github.com/openyurtio/openyurt/pkg/yurtmanager/webhook/yurtappoverrider/v1alpha1" v1alpha1yurtappset "github.com/openyurtio/openyurt/pkg/yurtmanager/webhook/yurtappset/v1alpha1" v1alpha1yurtstaticset "github.com/openyurtio/openyurt/pkg/yurtmanager/webhook/yurtstaticset/v1alpha1" ) @@ -81,6 +83,7 @@ func init() { addControllerWebhook(yurtappdaemon.ControllerName, &v1alpha1yurtappdaemon.YurtAppDaemonHandler{}) addControllerWebhook(platformadmin.ControllerName, &v1alpha1platformadmin.PlatformAdminHandler{}) addControllerWebhook(platformadmin.ControllerName, &v1alpha2platformadmin.PlatformAdminHandler{}) + addControllerWebhook(yurtappoverrider.ControllerName, &v1alpha1yurtappoverrider.YurtAppOverriderHandler{}) independentWebhooks[v1pod.WebhookName] = &v1pod.PodHandler{} independentWebhooks[v1node.WebhookName] = &v1node.NodeHandler{} diff --git a/pkg/yurtmanager/webhook/util/configuration/configuration.go b/pkg/yurtmanager/webhook/util/configuration/configuration.go index 5ee809b5e1e..83577bb595f 100644 --- a/pkg/yurtmanager/webhook/util/configuration/configuration.go +++ b/pkg/yurtmanager/webhook/util/configuration/configuration.go @@ -83,14 +83,15 @@ func Ensure(kubeClient clientset.Interface, handlers map[string]struct{}, caBund } // add additional deployment mutating webhook - wh := &admissionregistrationv1.MutatingWebhook{} + wh := &admissionregistrationv1.MutatingWebhook{ + FailurePolicy: &deploymentFailurePolicy, + } wh.AdmissionReviewVersions = []string{"v1"} svc := &admissionregistrationv1.ServiceReference{ Namespace: webhookutil.GetNamespace(), Name: webhookutil.GetServiceName(), Path: &deploymentMutatingPath, } - wh.FailurePolicy = &deploymentFailurePolicy wh.ClientConfig = admissionregistrationv1.WebhookClientConfig{ Service: svc, CABundle: caBundle, diff --git a/pkg/yurtmanager/webhook/yurtappoverrider/v1alpha1/yurtappoverrider_validation.go b/pkg/yurtmanager/webhook/yurtappoverrider/v1alpha1/yurtappoverrider_validation.go index a01ec21cd8f..b4f58bf833b 100644 --- a/pkg/yurtmanager/webhook/yurtappoverrider/v1alpha1/yurtappoverrider_validation.go +++ b/pkg/yurtmanager/webhook/yurtappoverrider/v1alpha1/yurtappoverrider_validation.go @@ -36,10 +36,7 @@ func (webhook *YurtAppOverriderHandler) ValidateCreate(ctx context.Context, obj } // validate - if err := webhook.validateOneToOne(ctx, overrider); err != nil { - return err - } - if err := webhook.validateStar(overrider); err != nil { + if err := webhook.validateOneToOneBinding(ctx, overrider); err != nil { return err } return nil @@ -57,10 +54,7 @@ func (webhook *YurtAppOverriderHandler) ValidateUpdate(ctx context.Context, oldO } // validate - if err := webhook.validateOneToOne(ctx, newOverrider); err != nil { - return err - } - if err := webhook.validateStar(newOverrider); err != nil { + if err := webhook.validateOneToOneBinding(ctx, newOverrider); err != nil { return err } return nil @@ -68,42 +62,25 @@ func (webhook *YurtAppOverriderHandler) ValidateUpdate(ctx context.Context, oldO // ValidateDelete implements webhook.CustomValidator so a webhook will be registered for the type. func (webhook *YurtAppOverriderHandler) ValidateDelete(_ context.Context, obj runtime.Object) error { - _, ok := obj.(*v1alpha1.YurtAppOverrider) - if !ok { - return apierrors.NewBadRequest(fmt.Sprintf("expected a YurtAppOverrider but got a %T", obj)) - } - // validate return nil } // YurtAppOverrider and YurtAppSet are one-to-one relationship -func (webhook *YurtAppOverriderHandler) validateOneToOne(ctx context.Context, yurtAppOverrider *v1alpha1.YurtAppOverrider) error { +func (webhook *YurtAppOverriderHandler) validateOneToOneBinding(ctx context.Context, yurtAppOverrider *v1alpha1.YurtAppOverrider) error { app := yurtAppOverrider.Subject var allOverriderList v1alpha1.YurtAppOverriderList if err := webhook.Client.List(ctx, &allOverriderList, client.InNamespace(yurtAppOverrider.Namespace)); err != nil { - klog.Info("error in listing YurtAppOverrider") + klog.Infof("could not list YurtAppOverrider, %v", err) return err } - var overriderList = v1alpha1.YurtAppOverriderList{} - for _, overrider := range overriderList.Items { - if overrider.Subject.Kind == app.Kind && overrider.Name == app.Name && overrider.APIVersion == app.APIVersion { - overriderList.Items = append(overriderList.Items, overrider) + overriderList := make([]v1alpha1.YurtAppOverrider, 0) + for _, overrider := range allOverriderList.Items { + if overrider.Subject.Kind == app.Kind && overrider.Subject.Name == app.Name { + overriderList = append(overriderList, overrider) } } - if len(overriderList.Items) > 0 { + if len(overriderList) > 0 { return fmt.Errorf("only one YurtAppOverrider can be bound into one YurtAppSet") } return nil } - -// Verify that * and other pools are not set at the same time -func (webhook *YurtAppOverriderHandler) validateStar(yurtAppOverrider *v1alpha1.YurtAppOverrider) error { - for _, entry := range yurtAppOverrider.Entries { - for _, pool := range entry.Pools { - if pool == "*" && len(entry.Pools) > 1 { - return fmt.Errorf("pool can't be '*' when other pools are set") - } - } - } - return nil -} diff --git a/test/e2e/yurt/yurtappoverrider.go b/test/e2e/yurt/yurtappoverrider.go index 17981f6bf32..839268f4543 100644 --- a/test/e2e/yurt/yurtappoverrider.go +++ b/test/e2e/yurt/yurtappoverrider.go @@ -51,9 +51,10 @@ var _ = Describe("YurtAppOverrider Test", func() { Name: namespaceName, }, } - Eventually(func() error { - return k8sClient.Delete(ctx, &ns, client.PropagationPolicy(metav1.DeletePropagationBackground)) - }).WithTimeout(timeout).WithPolling(500 * time.Millisecond).Should(SatisfyAny(BeNil(), &util.NotFoundMatcher{})) + Eventually( + func() error { + return k8sClient.Delete(ctx, &ns, client.PropagationPolicy(metav1.DeletePropagationForeground)) + }).WithTimeout(timeout).WithPolling(500 * time.Millisecond).Should(SatisfyAny(BeNil(), &util.NotFoundMatcher{})) By("make sure namespace are removed") res := &corev1.Namespace{} From c3f41561aae23099177ea5246352315dff07338a Mon Sep 17 00:00:00 2001 From: vie-serendipity <2733147505@qq.com> Date: Wed, 6 Sep 2023 11:00:59 +0800 Subject: [PATCH 11/20] fix error in e2e of yurtappoverrider --- test/e2e/yurt/yurtappoverrider.go | 1 + 1 file changed, 1 insertion(+) diff --git a/test/e2e/yurt/yurtappoverrider.go b/test/e2e/yurt/yurtappoverrider.go index 839268f4543..9f414bfdd1c 100644 --- a/test/e2e/yurt/yurtappoverrider.go +++ b/test/e2e/yurt/yurtappoverrider.go @@ -218,6 +218,7 @@ var _ = Describe("YurtAppOverrider Test", func() { BeforeEach(func() { By("Start to run YurtAppOverrider test, prepare resources") namespaceName = "yurtappoverrider-e2e-test" + "-" + rand.String(4) + k8sClient = ycfg.YurtE2eCfg.RuntimeClient createNameSpace() }) From 4ab54bbd22a258e64aaca6076e88512dc59cba76 Mon Sep 17 00:00:00 2001 From: vie-serendipity <2733147505@qq.com> Date: Wed, 6 Sep 2023 11:50:40 +0800 Subject: [PATCH 12/20] fix error in e2e of yurtappoverrider --- .../v1alpha1/deploymentrender_webhook_test.go | 20 +++++++++++++++++++ .../v1alpha1/item_control_test.go | 12 +++++++++++ test/e2e/yurt/yurtappoverrider.go | 3 +++ 3 files changed, 35 insertions(+) diff --git a/pkg/yurtmanager/webhook/deploymentrender/v1alpha1/deploymentrender_webhook_test.go b/pkg/yurtmanager/webhook/deploymentrender/v1alpha1/deploymentrender_webhook_test.go index 1c7dcd24b04..c47479e230f 100644 --- a/pkg/yurtmanager/webhook/deploymentrender/v1alpha1/deploymentrender_webhook_test.go +++ b/pkg/yurtmanager/webhook/deploymentrender/v1alpha1/deploymentrender_webhook_test.go @@ -184,12 +184,32 @@ var overrider2 = &v1alpha1.YurtAppOverrider{ }, } +var overrider3 = &v1alpha1.YurtAppOverrider{ + ObjectMeta: metav1.ObjectMeta{ + Name: "demo", + Namespace: "default", + }, + Subject: v1alpha1.Subject{ + Name: "demo", + TypeMeta: metav1.TypeMeta{ + Kind: "test", + APIVersion: "apps.openyurt.io/v1alpha1", + }, + }, + Entries: []v1alpha1.Entry{ + { + Pools: []string{"*"}, + }, + }, +} + func TestDeploymentRenderHandler_Default(t *testing.T) { tcases := []struct { overrider *v1alpha1.YurtAppOverrider }{ {overrider1}, {overrider2}, + {overrider3}, } scheme := runtime.NewScheme() if err := v1alpha1.AddToScheme(scheme); err != nil { diff --git a/pkg/yurtmanager/webhook/deploymentrender/v1alpha1/item_control_test.go b/pkg/yurtmanager/webhook/deploymentrender/v1alpha1/item_control_test.go index 5acd4f835a1..ce93f68eb56 100644 --- a/pkg/yurtmanager/webhook/deploymentrender/v1alpha1/item_control_test.go +++ b/pkg/yurtmanager/webhook/deploymentrender/v1alpha1/item_control_test.go @@ -53,6 +53,12 @@ var testItemDeployment = &appsv1.Deployment{ }, }, Spec: corev1.PodSpec{ + InitContainers: []corev1.Container{ + { + Name: "initContainer", + Image: "initOld", + }, + }, Containers: []corev1.Container{ { Name: "nginx", @@ -84,6 +90,12 @@ func TestReplaceItems(t *testing.T) { ImageClaim: "nginx", }, }, + { + Image: &v1alpha1.ImageItem{ + ContainerName: "initOld", + ImageClaim: "initNew", + }, + }, { Replicas: &itemReplicas, }, diff --git a/test/e2e/yurt/yurtappoverrider.go b/test/e2e/yurt/yurtappoverrider.go index 9f414bfdd1c..acccfd7ce2b 100644 --- a/test/e2e/yurt/yurtappoverrider.go +++ b/test/e2e/yurt/yurtappoverrider.go @@ -104,6 +104,9 @@ var _ = Describe("YurtAppOverrider Test", func() { }, WorkloadTemplate: v1alpha1.WorkloadTemplate{ DeploymentTemplate: &v1alpha1.DeploymentTemplateSpec{ + ObjectMeta: metav1.ObjectMeta{ + Labels: map[string]string{"app": "test"}, + }, Spec: v1.DeploymentSpec{ Template: corev1.PodTemplateSpec{ ObjectMeta: metav1.ObjectMeta{ From 75e0f2725c3bfb746f95a4e95f62b725a7727f9e Mon Sep 17 00:00:00 2001 From: vie-serendipity <2733147505@qq.com> Date: Wed, 6 Sep 2023 14:20:22 +0800 Subject: [PATCH 13/20] fix error in validating webhook of yurtappoverrider --- .../v1alpha1/deploymentrender_default.go | 6 ++---- .../v1alpha1/yurtappoverrider_validation.go | 11 +++++++---- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/pkg/yurtmanager/webhook/deploymentrender/v1alpha1/deploymentrender_default.go b/pkg/yurtmanager/webhook/deploymentrender/v1alpha1/deploymentrender_default.go index efbb98bcb1f..89c41f84d64 100644 --- a/pkg/yurtmanager/webhook/deploymentrender/v1alpha1/deploymentrender_default.go +++ b/pkg/yurtmanager/webhook/deploymentrender/v1alpha1/deploymentrender_default.go @@ -61,7 +61,6 @@ func (webhook *DeploymentRenderHandler) Default(ctx context.Context, obj runtime if !contain(deployment.OwnerReferences[0].Kind, resources) { return nil } - klog.Info("start to validate deployment") // Get YurtAppSet/YurtAppDaemon resource of this deployment app := deployment.OwnerReferences[0] var instance client.Object @@ -109,7 +108,7 @@ func (webhook *DeploymentRenderHandler) Default(ctx context.Context, obj runtime var allOverriderList v1alpha1.YurtAppOverriderList //listOptions := client.MatchingFields{"spec.subject.kind": app.Kind, "spec.subject.name": app.Name, "spec.subject.APIVersion": app.APIVersion} if err := webhook.Client.List(ctx, &allOverriderList, client.InNamespace(deployment.Namespace)); err != nil { - klog.Info("error in listing YurtAppOverrider") + klog.Infof("error in listing YurtAppOverrider: %v", err) return err } var overriderList = v1alpha1.YurtAppOverriderList{} @@ -134,7 +133,7 @@ func (webhook *DeploymentRenderHandler) Default(ctx context.Context, obj runtime items := entry.Items // Replace items if err := replaceItems(deployment, items); err != nil { - klog.Info("fail to replace items for deployment: %v", err) + klog.Infof("fail to replace items for deployment: %v", err) return err } // json patch and strategic merge @@ -159,6 +158,5 @@ func (webhook *DeploymentRenderHandler) Default(ctx context.Context, obj runtime } } } - klog.Info("Successfully render deployment") return nil } diff --git a/pkg/yurtmanager/webhook/yurtappoverrider/v1alpha1/yurtappoverrider_validation.go b/pkg/yurtmanager/webhook/yurtappoverrider/v1alpha1/yurtappoverrider_validation.go index b4f58bf833b..fb76943a199 100644 --- a/pkg/yurtmanager/webhook/yurtappoverrider/v1alpha1/yurtappoverrider_validation.go +++ b/pkg/yurtmanager/webhook/yurtappoverrider/v1alpha1/yurtappoverrider_validation.go @@ -44,11 +44,11 @@ func (webhook *YurtAppOverriderHandler) ValidateCreate(ctx context.Context, obj // ValidateUpdate implements webhook.CustomValidator so a webhook will be registered for the type. func (webhook *YurtAppOverriderHandler) ValidateUpdate(ctx context.Context, oldObj, newObj runtime.Object) error { - _, ok := newObj.(*v1alpha1.YurtAppOverrider) + _, ok := oldObj.(*v1alpha1.YurtAppOverrider) if !ok { return apierrors.NewBadRequest(fmt.Sprintf("expected a YurtAppOverrider but got a %T", newObj)) } - newOverrider, ok := oldObj.(*v1alpha1.YurtAppOverrider) + newOverrider, ok := newObj.(*v1alpha1.YurtAppOverrider) if !ok { return apierrors.NewBadRequest(fmt.Sprintf("expected a YurtAppOverrider} but got a %T", oldObj)) } @@ -67,7 +67,7 @@ func (webhook *YurtAppOverriderHandler) ValidateDelete(_ context.Context, obj ru // YurtAppOverrider and YurtAppSet are one-to-one relationship func (webhook *YurtAppOverriderHandler) validateOneToOneBinding(ctx context.Context, yurtAppOverrider *v1alpha1.YurtAppOverrider) error { - app := yurtAppOverrider.Subject + app := yurtAppOverrider var allOverriderList v1alpha1.YurtAppOverriderList if err := webhook.Client.List(ctx, &allOverriderList, client.InNamespace(yurtAppOverrider.Namespace)); err != nil { klog.Infof("could not list YurtAppOverrider, %v", err) @@ -75,7 +75,10 @@ func (webhook *YurtAppOverriderHandler) validateOneToOneBinding(ctx context.Cont } overriderList := make([]v1alpha1.YurtAppOverrider, 0) for _, overrider := range allOverriderList.Items { - if overrider.Subject.Kind == app.Kind && overrider.Subject.Name == app.Name { + if overrider.Name == app.Name && overrider.Kind == app.Kind { + continue + } + if overrider.Subject.Kind == app.Subject.Kind && overrider.Subject.Name == app.Subject.Name { overriderList = append(overriderList, overrider) } } From a2b58fa46c229af11292de99e88eb89ebe01379c Mon Sep 17 00:00:00 2001 From: vie-serendipity <2733147505@qq.com> Date: Wed, 6 Sep 2023 15:12:29 +0800 Subject: [PATCH 14/20] add more UT --- .../yurtappoverrider_controller_test.go | 58 +++++++++++++++++++ .../v1alpha1/deploymentrender_default.go | 2 +- .../v1alpha1/deploymentrender_webhook_test.go | 10 ++++ .../v1alpha1/patch_control.go | 11 +--- .../v1alpha1/patch_control_test.go | 13 +---- 5 files changed, 72 insertions(+), 22 deletions(-) diff --git a/pkg/yurtmanager/controller/yurtappoverrider/yurtappoverrider_controller_test.go b/pkg/yurtmanager/controller/yurtappoverrider/yurtappoverrider_controller_test.go index ea8cbeae1ba..a2bb4a019d9 100644 --- a/pkg/yurtmanager/controller/yurtappoverrider/yurtappoverrider_controller_test.go +++ b/pkg/yurtmanager/controller/yurtappoverrider/yurtappoverrider_controller_test.go @@ -14,3 +14,61 @@ See the License for the specific language governing permissions and limitations under the License. */ package yurtappoverrider + +import ( + "context" + "testing" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + "github.com/openyurtio/openyurt/pkg/apis/apps/v1alpha1" + "k8s.io/apimachinery/pkg/runtime" + clientgoscheme "k8s.io/client-go/kubernetes/scheme" + + "k8s.io/apimachinery/pkg/types" + fakeclient "sigs.k8s.io/controller-runtime/pkg/client/fake" + "sigs.k8s.io/controller-runtime/pkg/reconcile" +) + +var overrider = &v1alpha1.YurtAppOverrider{ + ObjectMeta: metav1.ObjectMeta{ + Name: "demo", + Namespace: "default", + }, + Subject: v1alpha1.Subject{ + Name: "demo", + TypeMeta: metav1.TypeMeta{ + Kind: "test", + APIVersion: "apps.openyurt.io/v1alpha1", + }, + }, + Entries: []v1alpha1.Entry{ + { + Pools: []string{"*"}, + }, + }, +} + +func TestReconcile(t *testing.T) { + scheme := runtime.NewScheme() + if err := v1alpha1.AddToScheme(scheme); err != nil { + t.Logf("failed to add yurt custom resource") + return + } + if err := clientgoscheme.AddToScheme(scheme); err != nil { + t.Logf("failed to add kubernetes clint-go custom resource") + return + } + reconciler := ReconcileYurtAppOverrider{ + Client: fakeclient.NewClientBuilder().WithScheme(scheme).WithObjects(overrider).Build(), + } + _, err := reconciler.Reconcile(context.Background(), reconcile.Request{ + NamespacedName: types.NamespacedName{ + Namespace: "default", + Name: "demo", + }, + }) + if err != nil { + t.Logf("fail to call Reconcile: %v", err) + } +} diff --git a/pkg/yurtmanager/webhook/deploymentrender/v1alpha1/deploymentrender_default.go b/pkg/yurtmanager/webhook/deploymentrender/v1alpha1/deploymentrender_default.go index 89c41f84d64..4864b8c1ae7 100644 --- a/pkg/yurtmanager/webhook/deploymentrender/v1alpha1/deploymentrender_default.go +++ b/pkg/yurtmanager/webhook/deploymentrender/v1alpha1/deploymentrender_default.go @@ -151,7 +151,7 @@ func (webhook *DeploymentRenderHandler) Default(ctx context.Context, obj runtime patchObject: deployment, dataStruct: dataStruct, } - if err := pc.updatePatches(); err != nil { + if err := pc.jsonMergePatch(); err != nil { klog.Infof("fail to update patches for deployment: %v", err) return err } diff --git a/pkg/yurtmanager/webhook/deploymentrender/v1alpha1/deploymentrender_webhook_test.go b/pkg/yurtmanager/webhook/deploymentrender/v1alpha1/deploymentrender_webhook_test.go index c47479e230f..fdc9354f626 100644 --- a/pkg/yurtmanager/webhook/deploymentrender/v1alpha1/deploymentrender_webhook_test.go +++ b/pkg/yurtmanager/webhook/deploymentrender/v1alpha1/deploymentrender_webhook_test.go @@ -25,7 +25,9 @@ import ( 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/config" fakeclient "sigs.k8s.io/controller-runtime/pkg/client/fake" + "sigs.k8s.io/controller-runtime/pkg/manager" "github.com/openyurtio/openyurt/pkg/apis/apps/v1alpha1" ) @@ -231,5 +233,13 @@ func TestDeploymentRenderHandler_Default(t *testing.T) { } }) } +} +func TestSetupWebhookWithManager(t *testing.T) { + handler := DeploymentRenderHandler{} + cfg, _ := config.GetConfig() + mgr, _ := manager.New(cfg, manager.Options{}) + if _, _, err := handler.SetupWebhookWithManager(mgr); err != nil { + t.Fatalf("setup webhook with manager failed %s", err) + } } diff --git a/pkg/yurtmanager/webhook/deploymentrender/v1alpha1/patch_control.go b/pkg/yurtmanager/webhook/deploymentrender/v1alpha1/patch_control.go index 17ed2fb0912..9be0cee032a 100644 --- a/pkg/yurtmanager/webhook/deploymentrender/v1alpha1/patch_control.go +++ b/pkg/yurtmanager/webhook/deploymentrender/v1alpha1/patch_control.go @@ -39,10 +39,10 @@ type overrider struct { } // implement json patch -func (pc *PatchControl) jsonMergePatch(patches []v1alpha1.Patch) error { +func (pc *PatchControl) jsonMergePatch() error { // convert into json patch format var patchOperations []overrider - for _, patch := range patches { + for _, patch := range pc.patches { single := overrider{ Op: string(patch.Operation), Path: patch.Path, @@ -69,10 +69,3 @@ func (pc *PatchControl) jsonMergePatch(patches []v1alpha1.Patch) error { } return json.Unmarshal(patchedData, &pc.patchObject) } - -func (pc *PatchControl) updatePatches() error { - if err := pc.jsonMergePatch(pc.patches); err != nil { - return err - } - return nil -} diff --git a/pkg/yurtmanager/webhook/deploymentrender/v1alpha1/patch_control_test.go b/pkg/yurtmanager/webhook/deploymentrender/v1alpha1/patch_control_test.go index 67742a6a68f..845369ed137 100644 --- a/pkg/yurtmanager/webhook/deploymentrender/v1alpha1/patch_control_test.go +++ b/pkg/yurtmanager/webhook/deploymentrender/v1alpha1/patch_control_test.go @@ -87,19 +87,8 @@ var patchControl = PatchControl{ } func TestJsonMergePatch(t *testing.T) { - sample := v1alpha1.Patch{ - Operation: v1alpha1.ADD, - Path: "/spec/template/spec/containers/0/image", - Value: apiextensionsv1.JSON{Raw: []byte(`"tomcat"`)}, - } - if err := patchControl.jsonMergePatch([]v1alpha1.Patch{sample}); err != nil { + if err := patchControl.jsonMergePatch(); err != nil { t.Fatalf("fail to call jsonMergePatch") } t.Logf("image:%v", testPatchDeployment.Spec.Template.Spec.Containers[0].Name) } - -func TestUpdatePatches(t *testing.T) { - if err := patchControl.updatePatches(); err != nil { - t.Fatalf("fail to call updatePatches: %v", err) - } -} From bbb72d2b38edaf564487e27ad6ded9d5e4832497 Mon Sep 17 00:00:00 2001 From: vie-serendipity <2733147505@qq.com> Date: Wed, 6 Sep 2023 15:13:45 +0800 Subject: [PATCH 15/20] gci writting ut --- .../yurtappoverrider/yurtappoverrider_controller_test.go | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/pkg/yurtmanager/controller/yurtappoverrider/yurtappoverrider_controller_test.go b/pkg/yurtmanager/controller/yurtappoverrider/yurtappoverrider_controller_test.go index a2bb4a019d9..412b76482ab 100644 --- a/pkg/yurtmanager/controller/yurtappoverrider/yurtappoverrider_controller_test.go +++ b/pkg/yurtmanager/controller/yurtappoverrider/yurtappoverrider_controller_test.go @@ -20,14 +20,13 @@ import ( "testing" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - - "github.com/openyurtio/openyurt/pkg/apis/apps/v1alpha1" "k8s.io/apimachinery/pkg/runtime" - clientgoscheme "k8s.io/client-go/kubernetes/scheme" - "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/apps/v1alpha1" ) var overrider = &v1alpha1.YurtAppOverrider{ From c1d8dbc0b421fc59ca61d5cce1ef94965390e3bb Mon Sep 17 00:00:00 2001 From: vie-serendipity <2733147505@qq.com> Date: Wed, 6 Sep 2023 16:05:23 +0800 Subject: [PATCH 16/20] fix error in ut --- .../v1alpha1/deploymentrender_webhook_test.go | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/pkg/yurtmanager/webhook/deploymentrender/v1alpha1/deploymentrender_webhook_test.go b/pkg/yurtmanager/webhook/deploymentrender/v1alpha1/deploymentrender_webhook_test.go index fdc9354f626..2aeeee99993 100644 --- a/pkg/yurtmanager/webhook/deploymentrender/v1alpha1/deploymentrender_webhook_test.go +++ b/pkg/yurtmanager/webhook/deploymentrender/v1alpha1/deploymentrender_webhook_test.go @@ -25,9 +25,7 @@ import ( 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/config" fakeclient "sigs.k8s.io/controller-runtime/pkg/client/fake" - "sigs.k8s.io/controller-runtime/pkg/manager" "github.com/openyurtio/openyurt/pkg/apis/apps/v1alpha1" ) @@ -234,12 +232,3 @@ func TestDeploymentRenderHandler_Default(t *testing.T) { }) } } - -func TestSetupWebhookWithManager(t *testing.T) { - handler := DeploymentRenderHandler{} - cfg, _ := config.GetConfig() - mgr, _ := manager.New(cfg, manager.Options{}) - if _, _, err := handler.SetupWebhookWithManager(mgr); err != nil { - t.Fatalf("setup webhook with manager failed %s", err) - } -} From eb6b971643d3187e9119dfe5d646ac3297c7442a Mon Sep 17 00:00:00 2001 From: vie-serendipity <2733147505@qq.com> Date: Wed, 6 Sep 2023 16:43:53 +0800 Subject: [PATCH 17/20] add more UT --- .../yurtappoverrider_controller.go | 3 + .../yurtappoverrider_controller_test.go | 51 +++++++++- .../v1alpha1/deploymentrender_default.go | 5 +- .../v1alpha1/deploymentrender_webhook_test.go | 96 ++++++++++++++++++- .../deploymentrender/v1alpha1/item_control.go | 3 +- .../v1alpha1/item_control_test.go | 4 +- 6 files changed, 148 insertions(+), 14 deletions(-) diff --git a/pkg/yurtmanager/controller/yurtappoverrider/yurtappoverrider_controller.go b/pkg/yurtmanager/controller/yurtappoverrider/yurtappoverrider_controller.go index dc624631636..d9bf81b0eb2 100644 --- a/pkg/yurtmanager/controller/yurtappoverrider/yurtappoverrider_controller.go +++ b/pkg/yurtmanager/controller/yurtappoverrider/yurtappoverrider_controller.go @@ -135,6 +135,9 @@ func (r *ReconcileYurtAppOverrider) Reconcile(_ context.Context, request reconci for _, deployment := range deployments.Items { if deployment.OwnerReferences[0].Kind == instance.Subject.Kind && deployment.OwnerReferences[0].Name == instance.Subject.Name { + if deployment.Annotations == nil { + deployment.Annotations = make(map[string]string) + } deployment.Annotations["LastOverrideTime"] = time.Now().String() if err := r.Client.Update(context.TODO(), &deployment); err != nil { return reconcile.Result{}, err diff --git a/pkg/yurtmanager/controller/yurtappoverrider/yurtappoverrider_controller_test.go b/pkg/yurtmanager/controller/yurtappoverrider/yurtappoverrider_controller_test.go index 412b76482ab..646ce4f5d89 100644 --- a/pkg/yurtmanager/controller/yurtappoverrider/yurtappoverrider_controller_test.go +++ b/pkg/yurtmanager/controller/yurtappoverrider/yurtappoverrider_controller_test.go @@ -19,6 +19,8 @@ import ( "context" "testing" + appsv1 "k8s.io/api/apps/v1" + corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/types" @@ -29,15 +31,58 @@ import ( "github.com/openyurtio/openyurt/pkg/apis/apps/v1alpha1" ) +var ( + replica int32 = 3 +) + +var daemonDeployment = &appsv1.Deployment{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + Namespace: "default", + OwnerReferences: []metav1.OwnerReference{{ + APIVersion: "apps.openyurt.io/v1alpha1", + Kind: "YurtAppDaemon", + Name: "yurtappdaemon", + }}, + Labels: map[string]string{ + "apps.openyurt.io/pool-name": "nodepool-test", + }, + }, + Status: appsv1.DeploymentStatus{}, + Spec: appsv1.DeploymentSpec{ + Replicas: &replica, + Selector: &metav1.LabelSelector{ + MatchLabels: map[string]string{ + "app": "test", + }, + }, + Template: corev1.PodTemplateSpec{ + ObjectMeta: metav1.ObjectMeta{ + Labels: map[string]string{ + "app": "test", + }, + }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "nginx", + Image: "nginx", + }, + }, + }, + }, + }, +} + var overrider = &v1alpha1.YurtAppOverrider{ ObjectMeta: metav1.ObjectMeta{ Name: "demo", Namespace: "default", }, Subject: v1alpha1.Subject{ - Name: "demo", + Name: "yurtappdaemon", TypeMeta: metav1.TypeMeta{ - Kind: "test", + Kind: "YurtAppDaemon", APIVersion: "apps.openyurt.io/v1alpha1", }, }, @@ -59,7 +104,7 @@ func TestReconcile(t *testing.T) { return } reconciler := ReconcileYurtAppOverrider{ - Client: fakeclient.NewClientBuilder().WithScheme(scheme).WithObjects(overrider).Build(), + Client: fakeclient.NewClientBuilder().WithScheme(scheme).WithObjects(daemonDeployment, overrider).Build(), } _, err := reconciler.Reconcile(context.Background(), reconcile.Request{ NamespacedName: types.NamespacedName{ diff --git a/pkg/yurtmanager/webhook/deploymentrender/v1alpha1/deploymentrender_default.go b/pkg/yurtmanager/webhook/deploymentrender/v1alpha1/deploymentrender_default.go index 4864b8c1ae7..5a86e0c9c21 100644 --- a/pkg/yurtmanager/webhook/deploymentrender/v1alpha1/deploymentrender_default.go +++ b/pkg/yurtmanager/webhook/deploymentrender/v1alpha1/deploymentrender_default.go @@ -132,10 +132,7 @@ func (webhook *DeploymentRenderHandler) Default(ctx context.Context, obj runtime if pool == nodepool || pool == "*" { items := entry.Items // Replace items - if err := replaceItems(deployment, items); err != nil { - klog.Infof("fail to replace items for deployment: %v", err) - return err - } + replaceItems(deployment, items) // json patch and strategic merge patches := entry.Patches for i, patch := range patches { diff --git a/pkg/yurtmanager/webhook/deploymentrender/v1alpha1/deploymentrender_webhook_test.go b/pkg/yurtmanager/webhook/deploymentrender/v1alpha1/deploymentrender_webhook_test.go index 2aeeee99993..1bd80a09cd8 100644 --- a/pkg/yurtmanager/webhook/deploymentrender/v1alpha1/deploymentrender_webhook_test.go +++ b/pkg/yurtmanager/webhook/deploymentrender/v1alpha1/deploymentrender_webhook_test.go @@ -81,9 +81,39 @@ var defaultAppSet = &v1alpha1.YurtAppSet{ }, } +var defaultAppDaemon = &v1alpha1.YurtAppDaemon{ + ObjectMeta: metav1.ObjectMeta{ + Name: "yurtappdaemon", + Namespace: "default", + }, + Spec: v1alpha1.YurtAppDaemonSpec{ + Selector: &metav1.LabelSelector{MatchLabels: map[string]string{"app": "test"}}, + WorkloadTemplate: v1alpha1.WorkloadTemplate{ + DeploymentTemplate: &v1alpha1.DeploymentTemplateSpec{ + ObjectMeta: metav1.ObjectMeta{ + Labels: map[string]string{"app": "test"}, + }, + Spec: appsv1.DeploymentSpec{ + Selector: &metav1.LabelSelector{MatchLabels: map[string]string{"app": "test"}}, + Template: corev1.PodTemplateSpec{ + ObjectMeta: metav1.ObjectMeta{ + Labels: map[string]string{"app": "test"}, + }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + {Name: "nginx", Image: "nginx"}, + }, + }, + }, + }, + }, + }, + }, +} + var defaultDeployment = &appsv1.Deployment{ ObjectMeta: metav1.ObjectMeta{ - Name: "test", + Name: "test1", Namespace: "default", OwnerReferences: []metav1.OwnerReference{{ APIVersion: "apps.openyurt.io/v1alpha1", @@ -120,6 +150,45 @@ var defaultDeployment = &appsv1.Deployment{ }, } +var daemonDeployment = &appsv1.Deployment{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test2", + Namespace: "default", + OwnerReferences: []metav1.OwnerReference{{ + APIVersion: "apps.openyurt.io/v1alpha1", + Kind: "YurtAppDaemon", + Name: "yurtappdaemon", + }}, + Labels: map[string]string{ + "apps.openyurt.io/pool-name": "nodepool-test", + }, + }, + Status: appsv1.DeploymentStatus{}, + Spec: appsv1.DeploymentSpec{ + Replicas: &replica, + Selector: &metav1.LabelSelector{ + MatchLabels: map[string]string{ + "app": "test", + }, + }, + Template: corev1.PodTemplateSpec{ + ObjectMeta: metav1.ObjectMeta{ + Labels: map[string]string{ + "app": "test", + }, + }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "nginx", + Image: "nginx", + }, + }, + }, + }, + }, +} + var overrider1 = &v1alpha1.YurtAppOverrider{ ObjectMeta: metav1.ObjectMeta{ Name: "demo", @@ -203,6 +272,25 @@ var overrider3 = &v1alpha1.YurtAppOverrider{ }, } +var overrider4 = &v1alpha1.YurtAppOverrider{ + ObjectMeta: metav1.ObjectMeta{ + Name: "demo", + Namespace: "default", + }, + Subject: v1alpha1.Subject{ + Name: "yurtappdaemon", + TypeMeta: metav1.TypeMeta{ + Kind: "YurtAppDaemon", + APIVersion: "apps.openyurt.io/v1alpha1", + }, + }, + Entries: []v1alpha1.Entry{ + { + Pools: []string{"*", "-nodepool-test"}, + }, + }, +} + func TestDeploymentRenderHandler_Default(t *testing.T) { tcases := []struct { overrider *v1alpha1.YurtAppOverrider @@ -210,6 +298,7 @@ func TestDeploymentRenderHandler_Default(t *testing.T) { {overrider1}, {overrider2}, {overrider3}, + {overrider4}, } scheme := runtime.NewScheme() if err := v1alpha1.AddToScheme(scheme); err != nil { @@ -223,12 +312,15 @@ func TestDeploymentRenderHandler_Default(t *testing.T) { for _, tcase := range tcases { t.Run("", func(t *testing.T) { webhook := &DeploymentRenderHandler{ - Client: fakeclient.NewClientBuilder().WithScheme(scheme).WithObjects(defaultAppSet, defaultDeployment, tcase.overrider).Build(), + Client: fakeclient.NewClientBuilder().WithScheme(scheme).WithObjects(defaultAppSet, daemonDeployment, defaultDeployment, defaultAppDaemon, tcase.overrider).Build(), Scheme: scheme, } if err := webhook.Default(context.TODO(), defaultDeployment); err != nil { t.Fatal(err) } + if err := webhook.Default(context.TODO(), daemonDeployment); err != nil { + t.Fatal(err) + } }) } } diff --git a/pkg/yurtmanager/webhook/deploymentrender/v1alpha1/item_control.go b/pkg/yurtmanager/webhook/deploymentrender/v1alpha1/item_control.go index fc3036910e9..83e4fcaa862 100644 --- a/pkg/yurtmanager/webhook/deploymentrender/v1alpha1/item_control.go +++ b/pkg/yurtmanager/webhook/deploymentrender/v1alpha1/item_control.go @@ -22,7 +22,7 @@ import ( "github.com/openyurtio/openyurt/pkg/apis/apps/v1alpha1" ) -func replaceItems(deployment *v1.Deployment, items []v1alpha1.Item) error { +func replaceItems(deployment *v1.Deployment, items []v1alpha1.Item) { for _, item := range items { switch { case item.Replicas != nil: @@ -40,5 +40,4 @@ func replaceItems(deployment *v1.Deployment, items []v1alpha1.Item) error { } } } - return nil } diff --git a/pkg/yurtmanager/webhook/deploymentrender/v1alpha1/item_control_test.go b/pkg/yurtmanager/webhook/deploymentrender/v1alpha1/item_control_test.go index ce93f68eb56..7fb5982876f 100644 --- a/pkg/yurtmanager/webhook/deploymentrender/v1alpha1/item_control_test.go +++ b/pkg/yurtmanager/webhook/deploymentrender/v1alpha1/item_control_test.go @@ -100,7 +100,5 @@ func TestReplaceItems(t *testing.T) { Replicas: &itemReplicas, }, } - if err := replaceItems(testItemDeployment, items); err != nil { - t.Fatalf("Error: %v", err) - } + replaceItems(testItemDeployment, items) } From 8360700ba34d426a33a93ebc5e87740effb08a8e Mon Sep 17 00:00:00 2001 From: vie-serendipity <2733147505@qq.com> Date: Tue, 12 Sep 2023 10:11:59 +0800 Subject: [PATCH 18/20] fix problems in the comments --- .../yurt-manager-auto-generated.yaml | 20 +++++++++ pkg/apis/apps/v1alpha1/default.go | 9 ---- .../v1alpha1/yurtappoverrider_conversion.go | 45 ------------------- .../yurtappoverrider_controller.go | 1 + .../v1alpha1/deploymentrender_default.go | 31 ++++++------- .../v1alpha1/deploymentrender_handler.go | 6 +-- pkg/yurtmanager/webhook/server.go | 2 +- .../util/configuration/configuration.go | 41 ----------------- .../v1alpha1/yurtappoverrider_default.go | 5 +-- .../v1alpha1/yurtappoverrider_validation.go | 44 +++++++++++++----- 10 files changed, 72 insertions(+), 132 deletions(-) delete mode 100644 pkg/apis/apps/v1alpha1/yurtappoverrider_conversion.go diff --git a/charts/yurt-manager/templates/yurt-manager-auto-generated.yaml b/charts/yurt-manager/templates/yurt-manager-auto-generated.yaml index ac047530d86..3e2ffa52208 100644 --- a/charts/yurt-manager/templates/yurt-manager-auto-generated.yaml +++ b/charts/yurt-manager/templates/yurt-manager-auto-generated.yaml @@ -524,6 +524,26 @@ metadata: creationTimestamp: null name: yurt-manager-mutating-webhook-configuration webhooks: +- admissionReviewVersions: + - v1 + clientConfig: + service: + name: yurt-manager-webhook-service + namespace: {{ .Release.Namespace }} + path: /mutate-apps-v1-deployment + failurePolicy: Ignore + name: mutate.apps.v1.deployment + rules: + - apiGroups: + - apps + apiVersions: + - v1 + operations: + - CREATE + - UPDATE + resources: + - deployments + sideEffects: None - admissionReviewVersions: - v1 - v1beta1 diff --git a/pkg/apis/apps/v1alpha1/default.go b/pkg/apis/apps/v1alpha1/default.go index f18d1c87420..8069f55e412 100644 --- a/pkg/apis/apps/v1alpha1/default.go +++ b/pkg/apis/apps/v1alpha1/default.go @@ -254,12 +254,3 @@ func SetDefaultsYurtAppDaemon(obj *YurtAppDaemon) { SetDefaultPodSpec(&obj.Spec.WorkloadTemplate.DeploymentTemplate.Spec.Template.Spec) } } - -// SetDefaultsYurtAppOverrider set default values for YurtAppOverrider. -func SetDefaultsYurtAppOverrider(obj *YurtAppOverrider) { - // example for set default value for YurtAppOverrider - - //if len(obj.Subject.Default) == 0 { - // obj.Spec.Default = "set-default-value-0" - //} -} diff --git a/pkg/apis/apps/v1alpha1/yurtappoverrider_conversion.go b/pkg/apis/apps/v1alpha1/yurtappoverrider_conversion.go deleted file mode 100644 index 0ef677e1595..00000000000 --- a/pkg/apis/apps/v1alpha1/yurtappoverrider_conversion.go +++ /dev/null @@ -1,45 +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 v1alpha1 - -/* -Implementing the hub method is pretty easy -- we just have to add an empty -method called Hub() to serve as a -[marker](https://pkg.go.dev/sigs.k8s.io/controller-runtime/pkg/conversion?tab=doc#Hub). -*/ - -// NOTE !!!!!! @kadisi -// If this version is storageversion, you only need to uncommand this method - -// Hub marks this type as a conversion hub. -//func (*YurtAppOverrider) Hub() {} - -// NOTE !!!!!!! @kadisi -// If this version is not storageversion, you need to implement the ConvertTo and ConvertFrom methods - -// need import "sigs.k8s.io/controller-runtime/pkg/conversion" -//func (src *YurtAppOverrider) ConvertTo(dstRaw conversion.Hub) error { -// return nil -//} - -// NOTE !!!!!!! @kadisi -// If this version is not storageversion, you need to implement the ConvertTo and ConvertFrom methods - -// need import "sigs.k8s.io/controller-runtime/pkg/conversion" -//func (dst *YurtAppOverrider) ConvertFrom(srcRaw conversion.Hub) error { -// return nil -//} diff --git a/pkg/yurtmanager/controller/yurtappoverrider/yurtappoverrider_controller.go b/pkg/yurtmanager/controller/yurtappoverrider/yurtappoverrider_controller.go index d9bf81b0eb2..0fd375a3ce8 100644 --- a/pkg/yurtmanager/controller/yurtappoverrider/yurtappoverrider_controller.go +++ b/pkg/yurtmanager/controller/yurtappoverrider/yurtappoverrider_controller.go @@ -103,6 +103,7 @@ func add(mgr manager.Manager, r reconcile.Reconciler) error { // +kubebuilder:rbac:groups=apps.openyurt.io,resources=yurtappoverriders,verbs=get;list;watch // +kubebuilder:rbac:groups=apps,resources=deployments,verbs=list;watch;update +// +kubebuilder:rbac:groups=core,resources=events,verbs=get;list;watch;create;update;patch;delete // Reconcile reads that state of the cluster for a YurtAppOverrider object and makes changes based on the state read // and what is in the YurtAppOverrider.Spec diff --git a/pkg/yurtmanager/webhook/deploymentrender/v1alpha1/deploymentrender_default.go b/pkg/yurtmanager/webhook/deploymentrender/v1alpha1/deploymentrender_default.go index 5a86e0c9c21..e1433cf75ce 100644 --- a/pkg/yurtmanager/webhook/deploymentrender/v1alpha1/deploymentrender_default.go +++ b/pkg/yurtmanager/webhook/deploymentrender/v1alpha1/deploymentrender_default.go @@ -32,10 +32,6 @@ import ( "github.com/openyurtio/openyurt/pkg/yurtmanager/controller/yurtappset/adapter" ) -const ( - DeploymentMutatingWebhook = "deployment-webhook" -) - var ( resources = []string{"YurtAppSet", "YurtAppDaemon"} ) @@ -87,6 +83,9 @@ func (webhook *DeploymentRenderHandler) Default(ctx context.Context, obj runtime var replicas int32 yas := instance.(*v1alpha1.YurtAppSet) revision := yas.Status.CurrentRevision + if yas.Spec.WorkloadTemplate.DeploymentTemplate != nil && yas.Spec.WorkloadTemplate.DeploymentTemplate.Spec.Replicas != nil { + replicas = *yas.Spec.WorkloadTemplate.DeploymentTemplate.Spec.Replicas + } deploymentAdapter := adapter.DeploymentAdapter{ Client: webhook.Client, Scheme: webhook.Scheme, @@ -111,40 +110,37 @@ func (webhook *DeploymentRenderHandler) Default(ctx context.Context, obj runtime klog.Infof("error in listing YurtAppOverrider: %v", err) return err } - var overriderList = v1alpha1.YurtAppOverriderList{} + var overriders = make([]v1alpha1.YurtAppOverrider, 0) for _, overrider := range allOverriderList.Items { if overrider.Subject.Kind == app.Kind && overrider.Subject.Name == app.Name && overrider.Subject.APIVersion == app.APIVersion { - overriderList.Items = append(overriderList.Items, overrider) + overriders = append(overriders, overrider) } } - if len(overriderList.Items) == 0 { + if len(overriders) == 0 { return nil } - render := overriderList.Items[0] + render := overriders[0] for _, entry := range render.Entries { - pools := entry.Pools - for _, pool := range pools { + for _, pool := range entry.Pools { if pool[0] == '-' && pool[1:] == nodepool { continue } if pool == nodepool || pool == "*" { - items := entry.Items // Replace items - replaceItems(deployment, items) - // json patch and strategic merge - patches := entry.Patches - for i, patch := range patches { + replaceItems(deployment, entry.Items) + // json patch + for i, patch := range entry.Patches { if strings.Contains(string(patch.Value.Raw), "{{nodepool}}") { newPatchString := strings.ReplaceAll(string(patch.Value.Raw), "{{nodepool}}", nodepool) - patches[i].Value = apiextensionsv1.JSON{Raw: []byte(newPatchString)} + entry.Patches[i].Value = apiextensionsv1.JSON{Raw: []byte(newPatchString)} } } // Implement injection dataStruct := v1.Deployment{} pc := PatchControl{ - patches: patches, + patches: entry.Patches, patchObject: deployment, dataStruct: dataStruct, } @@ -152,6 +148,7 @@ func (webhook *DeploymentRenderHandler) Default(ctx context.Context, obj runtime klog.Infof("fail to update patches for deployment: %v", err) return err } + break } } } diff --git a/pkg/yurtmanager/webhook/deploymentrender/v1alpha1/deploymentrender_handler.go b/pkg/yurtmanager/webhook/deploymentrender/v1alpha1/deploymentrender_handler.go index bd15999a1a5..ffb4fad7d0c 100644 --- a/pkg/yurtmanager/webhook/deploymentrender/v1alpha1/deploymentrender_handler.go +++ b/pkg/yurtmanager/webhook/deploymentrender/v1alpha1/deploymentrender_handler.go @@ -27,10 +27,6 @@ import ( "github.com/openyurtio/openyurt/pkg/yurtmanager/webhook/util" ) -const ( - WebhookName = "deploymentrender" -) - // SetupWebhookWithManager sets up Cluster webhooks. mutate path, validatepath, error func (webhook *DeploymentRenderHandler) SetupWebhookWithManager(mgr ctrl.Manager) (string, string, error) { // init @@ -49,6 +45,8 @@ func (webhook *DeploymentRenderHandler) SetupWebhookWithManager(mgr ctrl.Manager Complete() } +// +kubebuilder:webhook:path=/mutate-apps-v1-deployment,mutating=true,failurePolicy=ignore,groups=apps,resources=deployments,verbs=create;update,versions=v1,name=mutate.apps.v1.deployment,sideEffects=None,admissionReviewVersions=v1 + // Cluster implements a validating and defaulting webhook for Cluster. type DeploymentRenderHandler struct { Client client.Client diff --git a/pkg/yurtmanager/webhook/server.go b/pkg/yurtmanager/webhook/server.go index e47c1a05f8e..7b43a79c414 100644 --- a/pkg/yurtmanager/webhook/server.go +++ b/pkg/yurtmanager/webhook/server.go @@ -79,10 +79,10 @@ func init() { addControllerWebhook(names.PlatformAdminController, &v1alpha1platformadmin.PlatformAdminHandler{}) addControllerWebhook(names.PlatformAdminController, &v1alpha2platformadmin.PlatformAdminHandler{}) addControllerWebhook(names.YurtAppOverriderController, &v1alpha1yurtappoverrider.YurtAppOverriderHandler{}) + addControllerWebhook(names.YurtAppOverriderController, &v1alpha1deploymentrender.DeploymentRenderHandler{}) independentWebhooks[v1pod.WebhookName] = &v1pod.PodHandler{} independentWebhooks[v1node.WebhookName] = &v1node.NodeHandler{} - independentWebhooks[v1alpha1deploymentrender.WebhookName] = &v1alpha1deploymentrender.DeploymentRenderHandler{} } // Note !!! @kadisi diff --git a/pkg/yurtmanager/webhook/util/configuration/configuration.go b/pkg/yurtmanager/webhook/util/configuration/configuration.go index 83577bb595f..9332939adfc 100644 --- a/pkg/yurtmanager/webhook/util/configuration/configuration.go +++ b/pkg/yurtmanager/webhook/util/configuration/configuration.go @@ -31,12 +31,6 @@ import ( webhookutil "github.com/openyurtio/openyurt/pkg/yurtmanager/webhook/util" ) -var ( - deploymentMutatingPath = "/mutate-apps-v1-deployment" - deploymentSideEffect = admissionregistrationv1.SideEffectClassNone - deploymentFailurePolicy = admissionregistrationv1.Ignore -) - func Ensure(kubeClient clientset.Interface, handlers map[string]struct{}, caBundle []byte, webhookPort int) error { mutatingConfig, err := kubeClient.AdmissionregistrationV1().MutatingWebhookConfigurations().Get(context.TODO(), webhookutil.MutatingWebhookConfigurationName, metav1.GetOptions{}) if err != nil { @@ -81,41 +75,6 @@ func Ensure(kubeClient clientset.Interface, handlers map[string]struct{}, caBund mutatingWHs = append(mutatingWHs, *wh) } - - // add additional deployment mutating webhook - wh := &admissionregistrationv1.MutatingWebhook{ - FailurePolicy: &deploymentFailurePolicy, - } - wh.AdmissionReviewVersions = []string{"v1"} - svc := &admissionregistrationv1.ServiceReference{ - Namespace: webhookutil.GetNamespace(), - Name: webhookutil.GetServiceName(), - Path: &deploymentMutatingPath, - } - wh.ClientConfig = admissionregistrationv1.WebhookClientConfig{ - Service: svc, - CABundle: caBundle, - } - path, err := getPath(&wh.ClientConfig) - if err != nil { - return err - } - if _, ok := handlers[path]; !ok { - klog.V(4).Infof("Ignore webhook for %s in configuration", path) - } - if host := webhookutil.GetHost(); len(host) > 0 { - convertClientConfig(&wh.ClientConfig, host, webhookPort) - } - wh.Rules = []admissionregistrationv1.RuleWithOperations{{ - Operations: []admissionregistrationv1.OperationType{ - admissionregistrationv1.Create, admissionregistrationv1.Update}, - Rule: admissionregistrationv1.Rule{ - APIGroups: []string{"apps"}, - APIVersions: []string{"v1"}, - Resources: []string{"deployments"}}}} - wh.SideEffects = &deploymentSideEffect - wh.Name = "mutate.apps.v1.deployment" - mutatingWHs = append(mutatingWHs, *wh) mutatingConfig.Webhooks = mutatingWHs var validatingWHs []admissionregistrationv1.ValidatingWebhook diff --git a/pkg/yurtmanager/webhook/yurtappoverrider/v1alpha1/yurtappoverrider_default.go b/pkg/yurtmanager/webhook/yurtappoverrider/v1alpha1/yurtappoverrider_default.go index 107a80a86cc..3424b412670 100644 --- a/pkg/yurtmanager/webhook/yurtappoverrider/v1alpha1/yurtappoverrider_default.go +++ b/pkg/yurtmanager/webhook/yurtappoverrider/v1alpha1/yurtappoverrider_default.go @@ -28,12 +28,9 @@ import ( // Default satisfies the defaulting webhook interface. func (webhook *YurtAppOverriderHandler) Default(ctx context.Context, obj runtime.Object) error { - np, ok := obj.(*v1alpha1.YurtAppOverrider) + _, ok := obj.(*v1alpha1.YurtAppOverrider) if !ok { return apierrors.NewBadRequest(fmt.Sprintf("expected a YurtAppOverrider but got a %T", obj)) } - - v1alpha1.SetDefaultsYurtAppOverrider(np) - return nil } diff --git a/pkg/yurtmanager/webhook/yurtappoverrider/v1alpha1/yurtappoverrider_validation.go b/pkg/yurtmanager/webhook/yurtappoverrider/v1alpha1/yurtappoverrider_validation.go index fb76943a199..b319bf68fcb 100644 --- a/pkg/yurtmanager/webhook/yurtappoverrider/v1alpha1/yurtappoverrider_validation.go +++ b/pkg/yurtmanager/webhook/yurtappoverrider/v1alpha1/yurtappoverrider_validation.go @@ -44,7 +44,7 @@ func (webhook *YurtAppOverriderHandler) ValidateCreate(ctx context.Context, obj // ValidateUpdate implements webhook.CustomValidator so a webhook will be registered for the type. func (webhook *YurtAppOverriderHandler) ValidateUpdate(ctx context.Context, oldObj, newObj runtime.Object) error { - _, ok := oldObj.(*v1alpha1.YurtAppOverrider) + oldOverrider, ok := oldObj.(*v1alpha1.YurtAppOverrider) if !ok { return apierrors.NewBadRequest(fmt.Sprintf("expected a YurtAppOverrider but got a %T", newObj)) } @@ -52,7 +52,12 @@ func (webhook *YurtAppOverriderHandler) ValidateUpdate(ctx context.Context, oldO if !ok { return apierrors.NewBadRequest(fmt.Sprintf("expected a YurtAppOverrider} but got a %T", oldObj)) } - + if oldOverrider.Namespace != newOverrider.Namespace || newOverrider.Name != oldOverrider.Name { + return fmt.Errorf("unable to change metadata after %s is created", oldOverrider.Name) + } + if newOverrider.Subject.Kind != oldOverrider.Subject.Kind || newOverrider.Subject.Name != oldOverrider.Subject.Name { + return fmt.Errorf("unable to modify subject after %s is created", oldOverrider.Name) + } // validate if err := webhook.validateOneToOneBinding(ctx, newOverrider); err != nil { return err @@ -61,29 +66,46 @@ func (webhook *YurtAppOverriderHandler) ValidateUpdate(ctx context.Context, oldO } // ValidateDelete implements webhook.CustomValidator so a webhook will be registered for the type. -func (webhook *YurtAppOverriderHandler) ValidateDelete(_ context.Context, obj runtime.Object) error { +func (webhook *YurtAppOverriderHandler) ValidateDelete(ctx context.Context, obj runtime.Object) error { + overrider, ok := obj.(*v1alpha1.YurtAppOverrider) + if !ok { + return apierrors.NewBadRequest(fmt.Sprintf("expected a YurtAppOverrider but got a %T", obj)) + } + switch overrider.Subject.Kind { + case "YurtAppSet": + appSet := &v1alpha1.YurtAppSet{} + err := webhook.Client.Get(ctx, client.ObjectKey{Name: overrider.Subject.Name, Namespace: overrider.Namespace}, appSet) + if err == nil { + return fmt.Errorf("namespace: %s, unable to delete YurtAppOverrider when subject resource exists: %s", overrider.Namespace, appSet.Name) + } + case "YurtAppDaemon": + appDaemon := &v1alpha1.YurtAppDaemon{} + err := webhook.Client.Get(ctx, client.ObjectKey{Name: overrider.Subject.Name, Namespace: overrider.Namespace}, appDaemon) + if err == nil { + return fmt.Errorf("namespace: %s, unable to delete YurtAppOverrider when subject resource exists: %s", overrider.Namespace, appDaemon.Name) + } + } return nil } // YurtAppOverrider and YurtAppSet are one-to-one relationship -func (webhook *YurtAppOverriderHandler) validateOneToOneBinding(ctx context.Context, yurtAppOverrider *v1alpha1.YurtAppOverrider) error { - app := yurtAppOverrider +func (webhook *YurtAppOverriderHandler) validateOneToOneBinding(ctx context.Context, app *v1alpha1.YurtAppOverrider) error { var allOverriderList v1alpha1.YurtAppOverriderList - if err := webhook.Client.List(ctx, &allOverriderList, client.InNamespace(yurtAppOverrider.Namespace)); err != nil { + if err := webhook.Client.List(ctx, &allOverriderList, client.InNamespace(app.Namespace)); err != nil { klog.Infof("could not list YurtAppOverrider, %v", err) return err } - overriderList := make([]v1alpha1.YurtAppOverrider, 0) + duplicatedOverriders := make([]v1alpha1.YurtAppOverrider, 0) for _, overrider := range allOverriderList.Items { - if overrider.Name == app.Name && overrider.Kind == app.Kind { + if overrider.Name == app.Name { continue } if overrider.Subject.Kind == app.Subject.Kind && overrider.Subject.Name == app.Subject.Name { - overriderList = append(overriderList, overrider) + duplicatedOverriders = append(duplicatedOverriders, overrider) } } - if len(overriderList) > 0 { - return fmt.Errorf("only one YurtAppOverrider can be bound into one YurtAppSet") + if len(duplicatedOverriders) > 0 { + return fmt.Errorf("unable to bind multiple yurtappoverriders to one subject resource %s in namespace %s, %s already exists", app.Subject.Name, app.Namespace, app.Name) } return nil } From b195856170f18ac356d06519f173c9fe7e5e6c79 Mon Sep 17 00:00:00 2001 From: vie-serendipity <2733147505@qq.com> Date: Tue, 12 Sep 2023 11:53:53 +0800 Subject: [PATCH 19/20] fix problems in the comments --- charts/yurt-manager/templates/yurt-manager-auto-generated.yaml | 1 + .../deploymentrender/v1alpha1/deploymentrender_handler.go | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/charts/yurt-manager/templates/yurt-manager-auto-generated.yaml b/charts/yurt-manager/templates/yurt-manager-auto-generated.yaml index 3e2ffa52208..9bce6bc1486 100644 --- a/charts/yurt-manager/templates/yurt-manager-auto-generated.yaml +++ b/charts/yurt-manager/templates/yurt-manager-auto-generated.yaml @@ -541,6 +541,7 @@ webhooks: operations: - CREATE - UPDATE + - DELETE resources: - deployments sideEffects: None diff --git a/pkg/yurtmanager/webhook/deploymentrender/v1alpha1/deploymentrender_handler.go b/pkg/yurtmanager/webhook/deploymentrender/v1alpha1/deploymentrender_handler.go index ffb4fad7d0c..02b88e4ff75 100644 --- a/pkg/yurtmanager/webhook/deploymentrender/v1alpha1/deploymentrender_handler.go +++ b/pkg/yurtmanager/webhook/deploymentrender/v1alpha1/deploymentrender_handler.go @@ -45,7 +45,7 @@ func (webhook *DeploymentRenderHandler) SetupWebhookWithManager(mgr ctrl.Manager Complete() } -// +kubebuilder:webhook:path=/mutate-apps-v1-deployment,mutating=true,failurePolicy=ignore,groups=apps,resources=deployments,verbs=create;update,versions=v1,name=mutate.apps.v1.deployment,sideEffects=None,admissionReviewVersions=v1 +// +kubebuilder:webhook:path=/mutate-apps-v1-deployment,mutating=true,failurePolicy=ignore,groups=apps,resources=deployments,verbs=create;update;delete,versions=v1,name=mutate.apps.v1.deployment,sideEffects=None,admissionReviewVersions=v1 // Cluster implements a validating and defaulting webhook for Cluster. type DeploymentRenderHandler struct { From ce66db0afb729beb1f98fab45e5e00b44971439a Mon Sep 17 00:00:00 2001 From: vie-serendipity <2733147505@qq.com> Date: Tue, 12 Sep 2023 13:57:32 +0800 Subject: [PATCH 20/20] fix problems in the comments --- charts/yurt-manager/templates/yurt-manager-auto-generated.yaml | 2 +- .../deploymentrender/v1alpha1/deploymentrender_handler.go | 2 +- .../yurtappoverrider/v1alpha1/yurtappoverrider_handler.go | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/charts/yurt-manager/templates/yurt-manager-auto-generated.yaml b/charts/yurt-manager/templates/yurt-manager-auto-generated.yaml index 9bce6bc1486..1b452b66bcb 100644 --- a/charts/yurt-manager/templates/yurt-manager-auto-generated.yaml +++ b/charts/yurt-manager/templates/yurt-manager-auto-generated.yaml @@ -541,7 +541,6 @@ webhooks: operations: - CREATE - UPDATE - - DELETE resources: - deployments sideEffects: None @@ -857,6 +856,7 @@ webhooks: operations: - CREATE - UPDATE + - DELETE resources: - yurtappoverriders sideEffects: None diff --git a/pkg/yurtmanager/webhook/deploymentrender/v1alpha1/deploymentrender_handler.go b/pkg/yurtmanager/webhook/deploymentrender/v1alpha1/deploymentrender_handler.go index 02b88e4ff75..ffb4fad7d0c 100644 --- a/pkg/yurtmanager/webhook/deploymentrender/v1alpha1/deploymentrender_handler.go +++ b/pkg/yurtmanager/webhook/deploymentrender/v1alpha1/deploymentrender_handler.go @@ -45,7 +45,7 @@ func (webhook *DeploymentRenderHandler) SetupWebhookWithManager(mgr ctrl.Manager Complete() } -// +kubebuilder:webhook:path=/mutate-apps-v1-deployment,mutating=true,failurePolicy=ignore,groups=apps,resources=deployments,verbs=create;update;delete,versions=v1,name=mutate.apps.v1.deployment,sideEffects=None,admissionReviewVersions=v1 +// +kubebuilder:webhook:path=/mutate-apps-v1-deployment,mutating=true,failurePolicy=ignore,groups=apps,resources=deployments,verbs=create;update,versions=v1,name=mutate.apps.v1.deployment,sideEffects=None,admissionReviewVersions=v1 // Cluster implements a validating and defaulting webhook for Cluster. type DeploymentRenderHandler struct { diff --git a/pkg/yurtmanager/webhook/yurtappoverrider/v1alpha1/yurtappoverrider_handler.go b/pkg/yurtmanager/webhook/yurtappoverrider/v1alpha1/yurtappoverrider_handler.go index 453410a26f7..53df018c966 100644 --- a/pkg/yurtmanager/webhook/yurtappoverrider/v1alpha1/yurtappoverrider_handler.go +++ b/pkg/yurtmanager/webhook/yurtappoverrider/v1alpha1/yurtappoverrider_handler.go @@ -44,7 +44,7 @@ func (webhook *YurtAppOverriderHandler) SetupWebhookWithManager(mgr ctrl.Manager Complete() } -// +kubebuilder:webhook:path=/validate-apps-openyurt-io-v1alpha1-yurtappoverrider,mutating=false,failurePolicy=fail,sideEffects=None,admissionReviewVersions=v1;v1beta1,groups=apps.openyurt.io,resources=yurtappoverriders,verbs=create;update,versions=v1alpha1,name=validate.apps.v1alpha1.yurtappoverrider.openyurt.io +// +kubebuilder:webhook:path=/validate-apps-openyurt-io-v1alpha1-yurtappoverrider,mutating=false,failurePolicy=fail,sideEffects=None,admissionReviewVersions=v1;v1beta1,groups=apps.openyurt.io,resources=yurtappoverriders,verbs=create;update;delete,versions=v1alpha1,name=validate.apps.v1alpha1.yurtappoverrider.openyurt.io // +kubebuilder:webhook:path=/mutate-apps-openyurt-io-v1alpha1-yurtappoverrider,mutating=true,failurePolicy=fail,sideEffects=None,admissionReviewVersions=v1;v1beta1,groups=apps.openyurt.io,resources=yurtappoverriders,verbs=create;update,versions=v1alpha1,name=mutate.apps.v1alpha1.yurtappoverrider.openyurt.io // Cluster implements a validating and defaulting webhook for Cluster.