From 7fac440aa56eb970c4e9197692e626ce9f2344d0 Mon Sep 17 00:00:00 2001 From: Vladislav Sukhin Date: Mon, 20 Jan 2025 17:36:37 +0300 Subject: [PATCH] feat: [TKC-2933] add webhook config (#310) * feat: webhook config Signed-off-by: Vladislav Sukhin * fix: tune conig Signed-off-by: Vladislav Sukhin * fix: update crd Signed-off-by: Vladislav Sukhin * fix: add isTemplate field Signed-off-by: Vladislav Sukhin * fix: rename fields Signed-off-by: Vladislav Sukhin * feat: WebhookTemplate CRD Signed-off-by: Vladislav Sukhin * fix: remove deprecated field Signed-off-by: Vladislav Sukhin * fix: use list for parameters Signed-off-by: Vladislav Sukhin * fix: change description Signed-off-by: Vladislav Sukhin * fix: add backend team Signed-off-by: Vladislav Sukhin --------- Signed-off-by: Vladislav Sukhin --- PROJECT | 9 + api/executor/v1/groupversion_info.go | 6 + api/executor/v1/webhook_types.go | 101 +++++++++ api/executor/v1/zz_generated.deepcopy.go | 209 ++++++++++++++++++ cmd/main.go | 7 + .../bases/executor.testkube.io_webhooks.yaml | 63 ++++++ ...executor.testkube.io_webhooktemplates.yaml | 167 ++++++++++++++ config/crd/kustomization.yaml | 2 + .../patches/webhook_in_webhooktemplates.yaml | 17 ++ config/rbac/role.yaml | 26 +++ config/rbac/webhooktemplate_editor_role.yaml | 24 ++ config/rbac/webhooktemplate_viewer_role.yaml | 20 ++ .../samples/executor_v1_webhooktemplate.yaml | 9 + .../executor/webhooktemplate_controller.go | 62 ++++++ .../executors/v1/mock_webhooktemplates.go | 138 ++++++++++++ pkg/client/executors/v1/webhooktemplates.go | 136 ++++++++++++ .../typed/executor/v1/executor_client.go | 5 + .../executor/v1/fake/fake_tests_client.go | 4 + .../executor/v1/fake/fake_webhooktemplate.go | 72 ++++++ .../typed/executor/v1/generated_expansion.go | 2 + .../typed/executor/v1/webhooktemplate.go | 88 ++++++++ .../externalversions/executor/v1/interface.go | 7 + .../executor/v1/webhooktemplate.go | 94 ++++++++ pkg/informers/externalversions/generic.go | 6 + .../executor/v1/expansion_generated.go | 7 + pkg/listers/executor/v1/webhooktemplates.go | 101 +++++++++ 26 files changed, 1382 insertions(+) create mode 100644 config/crd/bases/executor.testkube.io_webhooktemplates.yaml create mode 100644 config/crd/patches/webhook_in_webhooktemplates.yaml create mode 100644 config/rbac/webhooktemplate_editor_role.yaml create mode 100644 config/rbac/webhooktemplate_viewer_role.yaml create mode 100644 config/samples/executor_v1_webhooktemplate.yaml create mode 100644 internal/controller/executor/webhooktemplate_controller.go create mode 100644 pkg/client/executors/v1/mock_webhooktemplates.go create mode 100644 pkg/client/executors/v1/webhooktemplates.go create mode 100644 pkg/clientset/versioned/typed/executor/v1/fake/fake_webhooktemplate.go create mode 100644 pkg/clientset/versioned/typed/executor/v1/webhooktemplate.go create mode 100644 pkg/informers/externalversions/executor/v1/webhooktemplate.go create mode 100644 pkg/listers/executor/v1/webhooktemplates.go diff --git a/PROJECT b/PROJECT index 5ffd1163..5de01415 100644 --- a/PROJECT +++ b/PROJECT @@ -54,6 +54,15 @@ resources: kind: Webhook path: github.com/kubeshop/testkube-operator/api/executor/v1 version: v1 +- api: + crdVersion: v1 + namespaced: true + controller: true + domain: testkube.io + group: executor + kind: WebhookTemplate + path: github.com/kubeshop/testkube-operator/api/executor/v1 + version: v1 - api: crdVersion: v1 namespaced: true diff --git a/api/executor/v1/groupversion_info.go b/api/executor/v1/groupversion_info.go index 92d8edc7..e3b28646 100644 --- a/api/executor/v1/groupversion_info.go +++ b/api/executor/v1/groupversion_info.go @@ -37,6 +37,9 @@ var ( // WebhookResource corresponds to the CRD Kind WebhookResource = "Webhook" + // WebhookTemplateResource corresponds to the CRD Kind + WebhookTemplateResource = "WebhookTemplate" + // GroupVersion is group version used to register these objects GroupVersion = schema.GroupVersion{Group: Group, Version: Version} @@ -46,6 +49,9 @@ var ( // WebhookGroupVersionResource is group, version and resource used to register these objects WebhookGroupVersionResource = schema.GroupVersionResource{Group: Group, Version: Version, Resource: WebhookResource} + // WebhookTemplateGroupVersionResource is group, version and resource used to register these objects + WebhookTemplateGroupVersionResource = schema.GroupVersionResource{Group: Group, Version: Version, Resource: WebhookTemplateResource} + // SchemeBuilder is used to add go types to the GroupVersionKind scheme SchemeBuilder = &scheme.Builder{GroupVersion: GroupVersion} diff --git a/api/executor/v1/webhook_types.go b/api/executor/v1/webhook_types.go index 70613e09..ca7a1676 100644 --- a/api/executor/v1/webhook_types.go +++ b/api/executor/v1/webhook_types.go @@ -47,6 +47,79 @@ type WebhookSpec struct { // OnStateChange will trigger the webhook only when the result of the current execution differs from the previous result of the same test/test suite/workflow // Deprecated: field is not used OnStateChange bool `json:"onStateChange,omitempty"` + // webhook configuration + Config map[string]WebhookConfigValue `json:"config,omitempty"` + // webhook parameters + Parameters []WebhookParameterSchema `json:"parameters,omitempty"` + // webhook template reference + WebhookTemplateRef *WebhookTemplateRef `json:"webhookTemplateRef,omitempty"` +} + +// WebhookTemplateSpec defines the desired state of Webhook Template +type WebhookTemplateSpec struct { + // INSERT ADDITIONAL SPEC FIELDS - desired state of cluster + // Important: Run "make" to regenerate code after modifying this file + + // Uri is address where webhook should be made (golang template supported) + Uri string `json:"uri,omitempty"` + // Events declare list if events on which webhook should be called + Events []EventType `json:"events,omitempty"` + // Labels to filter for tests and test suites + Selector string `json:"selector,omitempty"` + // will load the generated payload for notification inside the object + PayloadObjectField string `json:"payloadObjectField,omitempty"` + // golang based template for notification payload + PayloadTemplate string `json:"payloadTemplate,omitempty"` + // name of the template resource + PayloadTemplateReference string `json:"payloadTemplateReference,omitempty"` + // webhook headers (golang template supported) + Headers map[string]string `json:"headers,omitempty"` + // Disabled will disable the webhook + Disabled bool `json:"disabled,omitempty"` + // webhook configuration + Config map[string]WebhookConfigValue `json:"config,omitempty"` + // webhook parameters + Parameters []WebhookParameterSchema `json:"parameters,omitempty"` +} + +// webhook parameter schema +type WebhookParameterSchema struct { + // unique parameter name + Name string `json:"name"` + // description for the parameter + Description string `json:"description,omitempty"` + // whether parameter is required + Required bool `json:"required,omitempty"` + // example value for the parameter + Example string `json:"example,omitempty"` + // default parameter value + Default_ *string `json:"default,omitempty"` + // regular expression to match + Pattern string `json:"pattern,omitempty"` +} + +// webhook template reference +type WebhookTemplateRef struct { + // webhook template name to include + Name string `json:"name"` +} + +// webhook configuration value +type WebhookConfigValue struct { + // public value to use in webhook template + Value *string `json:"value,omitempty"` + // private value stored in secret to use in webhook template + Secret *SecretRef `json:"secret,omitempty"` +} + +// Testkube internal reference for secret storage in Kubernetes secrets +type SecretRef struct { + // object kubernetes namespace + Namespace string `json:"namespace,omitempty"` + // object name + Name string `json:"name"` + // object key + Key string `json:"key"` } // +kubebuilder:validation:Enum=start-test;end-test-success;end-test-failed;end-test-aborted;end-test-timeout;become-test-up;become-test-down;become-test-failed;become-test-aborted;become-test-timeout;start-testsuite;end-testsuite-success;end-testsuite-failed;end-testsuite-aborted;end-testsuite-timeout;become-testsuite-up;become-testsuite-down;become-testsuite-failed;become-testsuite-aborted;become-testsuite-timeout;start-testworkflow;queue-testworkflow;end-testworkflow-success;end-testworkflow-failed;end-testworkflow-aborted;become-testworkflow-up;become-testworkflow-down;become-testworkflow-failed;become-testworkflow-aborted @@ -91,6 +164,12 @@ type WebhookStatus struct { // Important: Run "make" to regenerate code after modifying this file } +// WebhookTemplateStatus defines the observed state of Webhook Template +type WebhookTemplateStatus struct { + // INSERT ADDITIONAL STATUS FIELD - define observed state of cluster + // Important: Run "make" to regenerate code after modifying this file +} + //+kubebuilder:object:root=true //+kubebuilder:subresource:status @@ -103,6 +182,18 @@ type Webhook struct { Status WebhookStatus `json:"status,omitempty"` } +//+kubebuilder:object:root=true +//+kubebuilder:subresource:status + +// WebhookTemplate is the Schema for the webhook templates API +type WebhookTemplate struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec WebhookTemplateSpec `json:"spec,omitempty"` + Status WebhookTemplateStatus `json:"status,omitempty"` +} + //+kubebuilder:object:root=true // WebhookList contains a list of Webhook @@ -112,6 +203,16 @@ type WebhookList struct { Items []Webhook `json:"items"` } +//+kubebuilder:object:root=true + +// WebhookTemplateList contains a list of Webhook Template +type WebhookTemplateList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []WebhookTemplate `json:"items"` +} + func init() { SchemeBuilder.Register(&Webhook{}, &WebhookList{}) + SchemeBuilder.Register(&WebhookTemplate{}, &WebhookTemplateList{}) } diff --git a/api/executor/v1/zz_generated.deepcopy.go b/api/executor/v1/zz_generated.deepcopy.go index dda0d45a..2397a7c8 100644 --- a/api/executor/v1/zz_generated.deepcopy.go +++ b/api/executor/v1/zz_generated.deepcopy.go @@ -191,6 +191,21 @@ func (in *Runner) DeepCopy() *Runner { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *SecretRef) DeepCopyInto(out *SecretRef) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SecretRef. +func (in *SecretRef) DeepCopy() *SecretRef { + if in == nil { + return nil + } + out := new(SecretRef) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *SlavesMeta) DeepCopyInto(out *SlavesMeta) { *out = *in @@ -233,6 +248,31 @@ func (in *Webhook) DeepCopyObject() runtime.Object { return nil } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *WebhookConfigValue) DeepCopyInto(out *WebhookConfigValue) { + *out = *in + if in.Value != nil { + in, out := &in.Value, &out.Value + *out = new(string) + **out = **in + } + if in.Secret != nil { + in, out := &in.Secret, &out.Secret + *out = new(SecretRef) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WebhookConfigValue. +func (in *WebhookConfigValue) DeepCopy() *WebhookConfigValue { + if in == nil { + return nil + } + out := new(WebhookConfigValue) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *WebhookList) DeepCopyInto(out *WebhookList) { *out = *in @@ -265,6 +305,26 @@ func (in *WebhookList) DeepCopyObject() runtime.Object { return nil } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *WebhookParameterSchema) DeepCopyInto(out *WebhookParameterSchema) { + *out = *in + if in.Default_ != nil { + in, out := &in.Default_, &out.Default_ + *out = new(string) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WebhookParameterSchema. +func (in *WebhookParameterSchema) DeepCopy() *WebhookParameterSchema { + if in == nil { + return nil + } + out := new(WebhookParameterSchema) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *WebhookSpec) DeepCopyInto(out *WebhookSpec) { *out = *in @@ -280,6 +340,25 @@ func (in *WebhookSpec) DeepCopyInto(out *WebhookSpec) { (*out)[key] = val } } + if in.Config != nil { + in, out := &in.Config, &out.Config + *out = make(map[string]WebhookConfigValue, len(*in)) + for key, val := range *in { + (*out)[key] = *val.DeepCopy() + } + } + if in.Parameters != nil { + in, out := &in.Parameters, &out.Parameters + *out = make([]WebhookParameterSchema, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.WebhookTemplateRef != nil { + in, out := &in.WebhookTemplateRef, &out.WebhookTemplateRef + *out = new(WebhookTemplateRef) + **out = **in + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WebhookSpec. @@ -306,3 +385,133 @@ func (in *WebhookStatus) DeepCopy() *WebhookStatus { in.DeepCopyInto(out) return out } + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *WebhookTemplate) DeepCopyInto(out *WebhookTemplate) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + out.Status = in.Status +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WebhookTemplate. +func (in *WebhookTemplate) DeepCopy() *WebhookTemplate { + if in == nil { + return nil + } + out := new(WebhookTemplate) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *WebhookTemplate) 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 *WebhookTemplateList) DeepCopyInto(out *WebhookTemplateList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]WebhookTemplate, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WebhookTemplateList. +func (in *WebhookTemplateList) DeepCopy() *WebhookTemplateList { + if in == nil { + return nil + } + out := new(WebhookTemplateList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *WebhookTemplateList) 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 *WebhookTemplateRef) DeepCopyInto(out *WebhookTemplateRef) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WebhookTemplateRef. +func (in *WebhookTemplateRef) DeepCopy() *WebhookTemplateRef { + if in == nil { + return nil + } + out := new(WebhookTemplateRef) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *WebhookTemplateSpec) DeepCopyInto(out *WebhookTemplateSpec) { + *out = *in + if in.Events != nil { + in, out := &in.Events, &out.Events + *out = make([]EventType, len(*in)) + copy(*out, *in) + } + if in.Headers != nil { + in, out := &in.Headers, &out.Headers + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + if in.Config != nil { + in, out := &in.Config, &out.Config + *out = make(map[string]WebhookConfigValue, len(*in)) + for key, val := range *in { + (*out)[key] = *val.DeepCopy() + } + } + if in.Parameters != nil { + in, out := &in.Parameters, &out.Parameters + *out = make([]WebhookParameterSchema, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WebhookTemplateSpec. +func (in *WebhookTemplateSpec) DeepCopy() *WebhookTemplateSpec { + if in == nil { + return nil + } + out := new(WebhookTemplateSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *WebhookTemplateStatus) DeepCopyInto(out *WebhookTemplateStatus) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WebhookTemplateStatus. +func (in *WebhookTemplateStatus) DeepCopy() *WebhookTemplateStatus { + if in == nil { + return nil + } + out := new(WebhookTemplateStatus) + in.DeepCopyInto(out) + return out +} diff --git a/cmd/main.go b/cmd/main.go index b7fa3b73..a28dba1b 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -193,6 +193,13 @@ func main() { setupLog.Error(err, "unable to create controller", "controller", "Webhook") os.Exit(1) } + if err = (&executorcontrollers.WebhookTemplateReconciler{ + Client: mgr.GetClient(), + Scheme: mgr.GetScheme(), + }).SetupWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create controller", "controller", "WebhookTemplate") + os.Exit(1) + } if err = (&testsourcecontrollers.TestSourceReconciler{ Client: mgr.GetClient(), Scheme: mgr.GetScheme(), diff --git a/config/crd/bases/executor.testkube.io_webhooks.yaml b/config/crd/bases/executor.testkube.io_webhooks.yaml index b5803d26..cb8a9e7d 100644 --- a/config/crd/bases/executor.testkube.io_webhooks.yaml +++ b/config/crd/bases/executor.testkube.io_webhooks.yaml @@ -39,6 +39,33 @@ spec: spec: description: WebhookSpec defines the desired state of Webhook properties: + config: + additionalProperties: + description: webhook configuration value + properties: + secret: + description: private value stored in secret to use in webhook + template + properties: + key: + description: object key + type: string + name: + description: object name + type: string + namespace: + description: object kubernetes namespace + type: string + required: + - key + - name + type: object + value: + description: public value to use in webhook template + type: string + type: object + description: webhook configuration + type: object disabled: description: Disabled will disable the webhook type: boolean @@ -88,6 +115,33 @@ spec: OnStateChange will trigger the webhook only when the result of the current execution differs from the previous result of the same test/test suite/workflow Deprecated: field is not used type: boolean + parameters: + description: webhook parameters + items: + description: webhook parameter schema + properties: + default: + description: default parameter value + type: string + description: + description: description for the parameter + type: string + example: + description: example value for the parameter + type: string + name: + description: unique parameter name + type: string + pattern: + description: regular expression to match + type: string + required: + description: whether parameter is required + type: boolean + required: + - name + type: object + type: array payloadObjectField: description: will load the generated payload for notification inside the object @@ -105,6 +159,15 @@ spec: description: Uri is address where webhook should be made (golang template supported) type: string + webhookTemplateRef: + description: webhook template reference + properties: + name: + description: webhook template name to include + type: string + required: + - name + type: object type: object status: description: WebhookStatus defines the observed state of Webhook diff --git a/config/crd/bases/executor.testkube.io_webhooktemplates.yaml b/config/crd/bases/executor.testkube.io_webhooktemplates.yaml new file mode 100644 index 00000000..69cbb0fb --- /dev/null +++ b/config/crd/bases/executor.testkube.io_webhooktemplates.yaml @@ -0,0 +1,167 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.15.0 + name: webhooktemplates.executor.testkube.io +spec: + group: executor.testkube.io + names: + kind: WebhookTemplate + listKind: WebhookTemplateList + plural: webhooktemplates + singular: webhooktemplate + scope: Namespaced + versions: + - name: v1 + schema: + openAPIV3Schema: + description: WebhookTemplate is the Schema for the webhook templates API + 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 + metadata: + type: object + spec: + description: WebhookTemplateSpec defines the desired state of Webhook + Template + properties: + config: + additionalProperties: + description: webhook configuration value + properties: + secret: + description: private value stored in secret to use in webhook + template + properties: + key: + description: object key + type: string + name: + description: object name + type: string + namespace: + description: object kubernetes namespace + type: string + required: + - key + - name + type: object + value: + description: public value to use in webhook template + type: string + type: object + description: webhook configuration + type: object + disabled: + description: Disabled will disable the webhook + type: boolean + events: + description: Events declare list if events on which webhook should + be called + items: + enum: + - start-test + - end-test-success + - end-test-failed + - end-test-aborted + - end-test-timeout + - become-test-up + - become-test-down + - become-test-failed + - become-test-aborted + - become-test-timeout + - start-testsuite + - end-testsuite-success + - end-testsuite-failed + - end-testsuite-aborted + - end-testsuite-timeout + - become-testsuite-up + - become-testsuite-down + - become-testsuite-failed + - become-testsuite-aborted + - become-testsuite-timeout + - start-testworkflow + - queue-testworkflow + - end-testworkflow-success + - end-testworkflow-failed + - end-testworkflow-aborted + - become-testworkflow-up + - become-testworkflow-down + - become-testworkflow-failed + - become-testworkflow-aborted + type: string + type: array + headers: + additionalProperties: + type: string + description: webhook headers (golang template supported) + type: object + parameters: + description: webhook parameters + items: + description: webhook parameter schema + properties: + default: + description: default parameter value + type: string + description: + description: description for the parameter + type: string + example: + description: example value for the parameter + type: string + name: + description: unique parameter name + type: string + pattern: + description: regular expression to match + type: string + required: + description: whether parameter is required + type: boolean + required: + - name + type: object + type: array + payloadObjectField: + description: will load the generated payload for notification inside + the object + type: string + payloadTemplate: + description: golang based template for notification payload + type: string + payloadTemplateReference: + description: name of the template resource + type: string + selector: + description: Labels to filter for tests and test suites + type: string + uri: + description: Uri is address where webhook should be made (golang template + supported) + type: string + type: object + status: + description: WebhookTemplateStatus defines the observed state of Webhook + Template + type: object + type: object + served: true + storage: true + subresources: + status: {} diff --git a/config/crd/kustomization.yaml b/config/crd/kustomization.yaml index 6c500a21..9ac8404f 100644 --- a/config/crd/kustomization.yaml +++ b/config/crd/kustomization.yaml @@ -7,6 +7,7 @@ resources: - bases/tests.testkube.io_tests.yaml - bases/tests.testkube.io_testsuites.yaml - bases/executor.testkube.io_webhooks.yaml +- bases/executor.testkube.io_webhooktemplates.yaml - bases/tests.testkube.io_testtriggers.yaml - bases/tests.testkube.io_testsources.yaml - bases/tests.testkube.io_testexecutions.yaml @@ -25,6 +26,7 @@ patchesStrategicMerge: #- patches/webhook_in_tests.yaml #- patches/webhook_in_testsuites.yaml #- patches/webhook_in_webhooks.yaml +#- patches/webhook_in_webhooktemplates.yaml - patches/webhook_in_testtriggers.yaml #- patches/webhook_in_testsources.yaml #- patches/webhook_in_testexecutions.yaml diff --git a/config/crd/patches/webhook_in_webhooktemplates.yaml b/config/crd/patches/webhook_in_webhooktemplates.yaml new file mode 100644 index 00000000..4b027331 --- /dev/null +++ b/config/crd/patches/webhook_in_webhooktemplates.yaml @@ -0,0 +1,17 @@ +# The following patch enables a conversion webhook for the CRD +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + name: webhooktemplates.executor.testkube.io +spec: + conversion: + strategy: Webhook + webhook: + clientConfig: + service: + namespace: system + name: webhook-service + path: /convert + conversionReviewVersions: + - v1 + - v1beta1 diff --git a/config/rbac/role.yaml b/config/rbac/role.yaml index be876a7e..e48d57e3 100644 --- a/config/rbac/role.yaml +++ b/config/rbac/role.yaml @@ -56,6 +56,32 @@ rules: - get - patch - update +- apiGroups: + - executor.testkube.io + resources: + - webhooktemplates + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - executor.testkube.io + resources: + - webhooktemplates/finalizers + verbs: + - update +- apiGroups: + - executor.testkube.io + resources: + - webhooktemplates/status + verbs: + - get + - patch + - update - apiGroups: - tests.testkube.io resources: diff --git a/config/rbac/webhooktemplate_editor_role.yaml b/config/rbac/webhooktemplate_editor_role.yaml new file mode 100644 index 00000000..1dbb1b4b --- /dev/null +++ b/config/rbac/webhooktemplate_editor_role.yaml @@ -0,0 +1,24 @@ +# permissions for end users to edit webhook templates. +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: webhooktemplate-editor-role +rules: +- apiGroups: + - executor.testkube.io + resources: + - webhooktemplates + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - executor.testkube.io + resources: + - webhooktemplates/status + verbs: + - get diff --git a/config/rbac/webhooktemplate_viewer_role.yaml b/config/rbac/webhooktemplate_viewer_role.yaml new file mode 100644 index 00000000..6880c7cd --- /dev/null +++ b/config/rbac/webhooktemplate_viewer_role.yaml @@ -0,0 +1,20 @@ +# permissions for end users to view webhook templates. +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: webhooktemplate-viewer-role +rules: +- apiGroups: + - executor.testkube.io + resources: + - webhooktemplates + verbs: + - get + - list + - watch +- apiGroups: + - executor.testkube.io + resources: + - webhooktemplates/status + verbs: + - get diff --git a/config/samples/executor_v1_webhooktemplate.yaml b/config/samples/executor_v1_webhooktemplate.yaml new file mode 100644 index 00000000..cdba052e --- /dev/null +++ b/config/samples/executor_v1_webhooktemplate.yaml @@ -0,0 +1,9 @@ +apiVersion: executor.testkube.io/v1 +kind: WebhookTemplate +metadata: + name: webhooktemplate-sample +spec: + uri: http://localhost:8080/events + events: + - start-test + - end-test diff --git a/internal/controller/executor/webhooktemplate_controller.go b/internal/controller/executor/webhooktemplate_controller.go new file mode 100644 index 00000000..996fc12a --- /dev/null +++ b/internal/controller/executor/webhooktemplate_controller.go @@ -0,0 +1,62 @@ +/* +Copyright 2021. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package executor + +import ( + "context" + + "k8s.io/apimachinery/pkg/runtime" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/log" + + executorv1 "github.com/kubeshop/testkube-operator/api/executor/v1" +) + +// WebhooTemplateReconciler reconciles a Webhook template object +type WebhookTemplateReconciler struct { + client.Client + Scheme *runtime.Scheme +} + +//+kubebuilder:rbac:groups=executor.testkube.io,resources=webhooktemplates,verbs=get;list;watch;create;update;patch;delete +//+kubebuilder:rbac:groups=executor.testkube.io,resources=webhooktemplates/status,verbs=get;update;patch +//+kubebuilder:rbac:groups=executor.testkube.io,resources=webhooktemplates/finalizers,verbs=update + +// Reconcile is part of the main kubernetes reconciliation loop which aims to +// move the current state of the cluster closer to the desired state. +// TODO(user): Modify the Reconcile function to compare the state specified by +// the Webhook template object against the actual cluster state, and then +// perform operations to make the cluster state reflect the state specified by +// the user. +// +// For more details, check Reconcile and its Result here: +// - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.11.0/pkg/reconcile +func (r *WebhookTemplateReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { + _ = log.FromContext(ctx) + + // TODO(user): your logic here + + return ctrl.Result{}, nil +} + +// SetupWithManager sets up the controller with the Manager. +func (r *WebhookTemplateReconciler) SetupWithManager(mgr ctrl.Manager) error { + return ctrl.NewControllerManagedBy(mgr). + For(&executorv1.WebhookTemplate{}). + Complete(r) +} diff --git a/pkg/client/executors/v1/mock_webhooktemplates.go b/pkg/client/executors/v1/mock_webhooktemplates.go new file mode 100644 index 00000000..672896b6 --- /dev/null +++ b/pkg/client/executors/v1/mock_webhooktemplates.go @@ -0,0 +1,138 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: github.com/kubeshop/testkube-operator/pkg/client/executors/v1 (interfaces: WebhookTemplatesInterface) + +// Package executors is a generated GoMock package. +package executors + +import ( + reflect "reflect" + + gomock "github.com/golang/mock/gomock" + v1 "github.com/kubeshop/testkube-operator/api/executor/v1" +) + +// MockWebhookTemplatesInterface is a mock of WebhookTemplatesInterface interface. +type MockWebhookTemplatesInterface struct { + ctrl *gomock.Controller + recorder *MockWebhookTemplatesInterfaceMockRecorder +} + +// MockWebhookTemplatesInterfaceMockRecorder is the mock recorder for MockWebhookTemplatesInterface. +type MockWebhookTemplatesInterfaceMockRecorder struct { + mock *MockWebhookTemplatesInterface +} + +// NewMockWebhookTemplatesInterface creates a new mock instance. +func NewMockWebhookTemplatesInterface(ctrl *gomock.Controller) *MockWebhookTemplatesInterface { + mock := &MockWebhookTemplatesInterface{ctrl: ctrl} + mock.recorder = &MockWebhookTemplatesInterfaceMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockWebhookTemplatesInterface) EXPECT() *MockWebhookTemplatesInterfaceMockRecorder { + return m.recorder +} + +// Create mocks base method. +func (m *MockWebhookTemplatesInterface) Create(arg0 *v1.WebhookTemplate) (*v1.WebhookTemplate, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Create", arg0) + ret0, _ := ret[0].(*v1.WebhookTemplate) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Create indicates an expected call of Create. +func (mr *MockWebhookTemplatesInterfaceMockRecorder) Create(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Create", reflect.TypeOf((*MockWebhookTemplatesInterface)(nil).Create), arg0) +} + +// Delete mocks base method. +func (m *MockWebhookTemplatesInterface) Delete(arg0 string) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Delete", arg0) + ret0, _ := ret[0].(error) + return ret0 +} + +// Delete indicates an expected call of Delete. +func (mr *MockWebhookTemplatesInterfaceMockRecorder) Delete(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Delete", reflect.TypeOf((*MockWebhookTemplatesInterface)(nil).Delete), arg0) +} + +// DeleteByLabels mocks base method. +func (m *MockWebhookTemplatesInterface) DeleteByLabels(arg0 string) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "DeleteByLabels", arg0) + ret0, _ := ret[0].(error) + return ret0 +} + +// DeleteByLabels indicates an expected call of DeleteByLabels. +func (mr *MockWebhookTemplatesInterfaceMockRecorder) DeleteByLabels(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteByLabels", reflect.TypeOf((*MockWebhookTemplatesInterface)(nil).DeleteByLabels), arg0) +} + +// Get mocks base method. +func (m *MockWebhookTemplatesInterface) Get(arg0 string) (*v1.WebhookTemplate, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Get", arg0) + ret0, _ := ret[0].(*v1.WebhookTemplate) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Get indicates an expected call of Get. +func (mr *MockWebhookTemplatesInterfaceMockRecorder) Get(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Get", reflect.TypeOf((*MockWebhookTemplatesInterface)(nil).Get), arg0) +} + +// GetByEvent mocks base method. +func (m *MockWebhookTemplatesInterface) GetByEvent(arg0 v1.EventType) (*v1.WebhookTemplateList, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetByEvent", arg0) + ret0, _ := ret[0].(*v1.WebhookTemplateList) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetByEvent indicates an expected call of GetByEvent. +func (mr *MockWebhookTemplatesInterfaceMockRecorder) GetByEvent(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetByEvent", reflect.TypeOf((*MockWebhookTemplatesInterface)(nil).GetByEvent), arg0) +} + +// List mocks base method. +func (m *MockWebhookTemplatesInterface) List(arg0 string) (*v1.WebhookTemplateList, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "List", arg0) + ret0, _ := ret[0].(*v1.WebhookTemplateList) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// List indicates an expected call of List. +func (mr *MockWebhookTemplatesInterfaceMockRecorder) List(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "List", reflect.TypeOf((*MockWebhookTemplatesInterface)(nil).List), arg0) +} + +// Update mocks base method. +func (m *MockWebhookTemplatesInterface) Update(arg0 *v1.WebhookTemplate) (*v1.WebhookTemplate, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Update", arg0) + ret0, _ := ret[0].(*v1.WebhookTemplate) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Update indicates an expected call of Update. +func (mr *MockWebhookTemplatesInterfaceMockRecorder) Update(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Update", reflect.TypeOf((*MockWebhookTemplatesInterface)(nil).Update), arg0) +} diff --git a/pkg/client/executors/v1/webhooktemplates.go b/pkg/client/executors/v1/webhooktemplates.go new file mode 100644 index 00000000..fff7400f --- /dev/null +++ b/pkg/client/executors/v1/webhooktemplates.go @@ -0,0 +1,136 @@ +package executors + +import ( + "context" + "fmt" + + executorsv1 "github.com/kubeshop/testkube-operator/api/executor/v1" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/labels" + "sigs.k8s.io/controller-runtime/pkg/client" +) + +//go:generate mockgen -destination=./mock_webhooktemplates.go -package=executors "github.com/kubeshop/testkube-operator/pkg/client/executors/v1" WebhookTemplatesInterface +type WebhookTemplatesInterface interface { + List(selector string) (*executorsv1.WebhookTemplateList, error) + Get(name string) (*executorsv1.WebhookTemplate, error) + GetByEvent(event executorsv1.EventType) (*executorsv1.WebhookTemplateList, error) + Create(webhookTemplate *executorsv1.WebhookTemplate) (*executorsv1.WebhookTemplate, error) + Update(webhookTemplate *executorsv1.WebhookTemplate) (*executorsv1.WebhookTemplate, error) + Delete(name string) error + DeleteByLabels(selector string) error +} + +// NewWebhookTemplatesClient returns new client instance, needs kubernetes client to be passed as dependecy +func NewWebhookTemplatesClient(client client.Client, namespace string) *WebhookTemplatesClient { + return &WebhookTemplatesClient{ + Client: client, + Namespace: namespace, + } +} + +// WebhookTemplatesClient client for getting webhook templates CRs +type WebhookTemplatesClient struct { + Client client.Client + Namespace string +} + +// List shows list of available webhook templates +func (s WebhookTemplatesClient) List(selector string) (*executorsv1.WebhookTemplateList, error) { + list := &executorsv1.WebhookTemplateList{} + reqs, err := labels.ParseToRequirements(selector) + if err != nil { + return list, err + } + + options := &client.ListOptions{ + Namespace: s.Namespace, + LabelSelector: labels.NewSelector().Add(reqs...), + } + + err = s.Client.List(context.Background(), list, options) + return list, err +} + +// Get gets webhook template by name in given namespace +func (s WebhookTemplatesClient) Get(name string) (*executorsv1.WebhookTemplate, error) { + item := &executorsv1.WebhookTemplate{} + err := s.Client.Get(context.Background(), client.ObjectKey{Namespace: s.Namespace, Name: name}, item) + return item, err +} + +// GetByEvent gets all webhook templates with given event +func (s WebhookTemplatesClient) GetByEvent(event executorsv1.EventType) (*executorsv1.WebhookTemplateList, error) { + list := &executorsv1.WebhookTemplateList{} + err := s.Client.List(context.Background(), list, &client.ListOptions{Namespace: s.Namespace}) + if err != nil { + return nil, err + } + + for i := len(list.Items) - 1; i >= 0; i-- { + exec := list.Items[i] + hasEvent := false + for _, t := range exec.Spec.Events { + if t == event { + hasEvent = true + } + } + + if !hasEvent { + list.Items = append(list.Items[:i], list.Items[i+1:]...) + } + } + + return list, nil +} + +// Create creates new Webhook Template CR +func (s WebhookTemplatesClient) Create(webhookTemplate *executorsv1.WebhookTemplate) (*executorsv1.WebhookTemplate, error) { + if webhookTemplate.Namespace != s.Namespace { + return nil, fmt.Errorf("wrong namespace, expected: %s, got: %s", s.Namespace, webhookTemplate.Namespace) + } + err := s.Client.Create(context.Background(), webhookTemplate) + if err != nil { + return nil, fmt.Errorf("could not create webhook template: %w", err) + } + res, err := s.Get(webhookTemplate.Name) + if err != nil { + return nil, fmt.Errorf("could not get created webhook template: %w", err) + } + return res, nil +} + +// Delete deletes Webhook Template by name +func (s WebhookTemplatesClient) Delete(name string) error { + webhookTemplate := &executorsv1.WebhookTemplate{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: s.Namespace, + }, + } + err := s.Client.Delete(context.Background(), webhookTemplate) + return err +} + +// Update updates Webhook Template +func (s WebhookTemplatesClient) Update(webhookTemplate *executorsv1.WebhookTemplate) (*executorsv1.WebhookTemplate, error) { + err := s.Client.Update(context.Background(), webhookTemplate) + return webhookTemplate, err +} + +// DeleteByLabels deletes webhook templates by labels +func (s WebhookTemplatesClient) DeleteByLabels(selector string) error { + reqs, err := labels.ParseToRequirements(selector) + if err != nil { + return err + } + + u := &unstructured.Unstructured{} + u.SetKind("WebhookTemplate") + u.SetAPIVersion("executor.testkube.io/v1") + err = s.Client.DeleteAllOf(context.Background(), u, client.InNamespace(s.Namespace), + client.MatchingLabelsSelector{Selector: labels.NewSelector().Add(reqs...)}) + return err +} diff --git a/pkg/clientset/versioned/typed/executor/v1/executor_client.go b/pkg/clientset/versioned/typed/executor/v1/executor_client.go index 0f798848..40e250fd 100644 --- a/pkg/clientset/versioned/typed/executor/v1/executor_client.go +++ b/pkg/clientset/versioned/typed/executor/v1/executor_client.go @@ -29,6 +29,7 @@ type ExecutorV1Interface interface { RESTClient() rest.Interface ExecutorGetter WebhookGetter + WebhookTemplateGetter } // ExecutorV1Client is used to interact with features provided by the executor.testkube.io group. @@ -44,6 +45,10 @@ func (c *ExecutorV1Client) Webhook(namespace string) WebhookInterface { return newWebhook(c, namespace) } +func (c *ExecutorV1Client) WebhookTemplate(namespace string) WebhookTemplateInterface { + return newWebhookTemplate(c, namespace) +} + // NewForConfig creates a new ExecutorV1Client for the given config. // NewForConfig is equivalent to NewForConfigAndClient(c, httpClient), // where httpClient was generated with rest.HTTPClientFor(c). diff --git a/pkg/clientset/versioned/typed/executor/v1/fake/fake_tests_client.go b/pkg/clientset/versioned/typed/executor/v1/fake/fake_tests_client.go index a560f7fb..715f8cd0 100644 --- a/pkg/clientset/versioned/typed/executor/v1/fake/fake_tests_client.go +++ b/pkg/clientset/versioned/typed/executor/v1/fake/fake_tests_client.go @@ -34,6 +34,10 @@ func (c *FakeExecutorV1) Webhook(namespace string) v1.WebhookInterface { return &FakeWebhook{c, namespace} } +func (c *FakeExecutorV1) WebhookTemplate(namespace string) v1.WebhookTemplateInterface { + return &FakeWebhookTemplate{c, namespace} +} + // RESTClient returns a RESTClient that is used to communicate // with API server by this client implementation. func (c *FakeExecutorV1) RESTClient() rest.Interface { diff --git a/pkg/clientset/versioned/typed/executor/v1/fake/fake_webhooktemplate.go b/pkg/clientset/versioned/typed/executor/v1/fake/fake_webhooktemplate.go new file mode 100644 index 00000000..bf880183 --- /dev/null +++ b/pkg/clientset/versioned/typed/executor/v1/fake/fake_webhooktemplate.go @@ -0,0 +1,72 @@ +/* +Copyright 2021. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package fake + +import ( + "context" + "fmt" + + executorv1 "github.com/kubeshop/testkube-operator/api/executor/v1" + + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/watch" + "k8s.io/client-go/testing" +) + +// FakeWebhookTemplate implements WebhookTemplateInterface +type FakeWebhookTemplate struct { + Fake *FakeExecutorV1 + ns string +} + +var webhookTemplateResource = schema.GroupVersionResource{Group: "executor.testkube.io", Version: "v1", Resource: "WebhookTemplate"} + +var webhookTemplateKind = schema.GroupVersionKind{Group: "executor.testkube.io", Version: "v1", Kind: "WebhookTemplate"} + +// List takes label and field selectors, and returns the list of WebhookTemplate that match those selectors. +func (c *FakeWebhookTemplate) List(ctx context.Context, opts v1.ListOptions) (result *executorv1.WebhookTemplateList, err error) { + obj, err := c.Fake. + Invokes(testing.NewListAction(webhookTemplateResource, webhookTemplateKind, c.ns, opts), &executorv1.WebhookTemplateList{}) + + if err != nil { + return nil, err + } + + if obj == nil { + return nil, fmt.Errorf("empty object") + } + + label, _, _ := testing.ExtractFromListOptions(opts) + if label == nil { + label = labels.Everything() + } + list := &executorv1.WebhookTemplateList{ListMeta: obj.(*executorv1.WebhookTemplateList).ListMeta} + for _, item := range obj.(*executorv1.WebhookTemplateList).Items { + if label.Matches(labels.Set(item.Labels)) { + list.Items = append(list.Items, item) + } + } + return list, err +} + +// Watch returns a watch.Interface that watches the requested WebhookTemplates. +func (c *FakeWebhookTemplate) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { + return c.Fake. + InvokesWatch(testing.NewWatchAction(webhookTemplateResource, c.ns, opts)) +} diff --git a/pkg/clientset/versioned/typed/executor/v1/generated_expansion.go b/pkg/clientset/versioned/typed/executor/v1/generated_expansion.go index 8081a33f..bec41030 100644 --- a/pkg/clientset/versioned/typed/executor/v1/generated_expansion.go +++ b/pkg/clientset/versioned/typed/executor/v1/generated_expansion.go @@ -19,3 +19,5 @@ package v1 type ExecutorExpansion interface{} type WebhookExpansion interface{} + +type WebhookTemplateExpansion interface{} diff --git a/pkg/clientset/versioned/typed/executor/v1/webhooktemplate.go b/pkg/clientset/versioned/typed/executor/v1/webhooktemplate.go new file mode 100644 index 00000000..16fdd8ab --- /dev/null +++ b/pkg/clientset/versioned/typed/executor/v1/webhooktemplate.go @@ -0,0 +1,88 @@ +/* +Copyright 2021. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1 + +import ( + "context" + "time" + + executorv1 "github.com/kubeshop/testkube-operator/api/executor/v1" + "github.com/kubeshop/testkube-operator/pkg/clientset/versioned/scheme" + + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/watch" + "k8s.io/client-go/rest" +) + +// WebhookTemplateGetter has a method to return a WebhookTemplateInterface. +// A group's client should implement this interface. +type WebhookTemplateGetter interface { + WebhookTemplate(namespace string) WebhookTemplateInterface +} + +// WebhookTemplateInterface has methods to work with Webhook Template resources. +type WebhookTemplateInterface interface { + List(ctx context.Context, opts v1.ListOptions) (*executorv1.WebhookTemplateList, error) + Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) + WebhookTemplateExpansion +} + +// webhookTemplates implements WebhookTemplateInterface +type webhookTemplates struct { + client rest.Interface + ns string +} + +// newWebhookTemplate returns a Webhook Template +func newWebhookTemplate(c *ExecutorV1Client, namespace string) *webhookTemplates { + return &webhookTemplates{ + client: c.RESTClient(), + ns: namespace, + } +} + +// List takes label and field selectors, and returns the list of Webhook Template that match those selectors. +func (c *webhookTemplates) List(ctx context.Context, opts v1.ListOptions) (result *executorv1.WebhookTemplateList, err error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + result = &executorv1.WebhookTemplateList{} + err = c.client.Get(). + Namespace(c.ns). + Resource("webhookTemplates"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Do(ctx). + Into(result) + return +} + +// Watch returns a watch.Interface that watches the requested webhook templates. +func (c *webhookTemplates) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + opts.Watch = true + return c.client.Get(). + Namespace(c.ns). + Resource("webhookTemplates"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Watch(ctx) +} diff --git a/pkg/informers/externalversions/executor/v1/interface.go b/pkg/informers/externalversions/executor/v1/interface.go index 71e12744..f41aee7e 100644 --- a/pkg/informers/externalversions/executor/v1/interface.go +++ b/pkg/informers/externalversions/executor/v1/interface.go @@ -24,6 +24,8 @@ type Interface interface { Executor() ExecutorInformer // Webhook returns a WebhookInformer. Webhook() WebhookInformer + // WebhookTemplate returns a WebhookTemplateInformer. + WebhookTemplate() WebhookTemplateInformer } type version struct { @@ -50,3 +52,8 @@ func (v *version) Executor() ExecutorInformer { func (v *version) Webhook() WebhookInformer { return &webhookInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions} } + +// WebhookTemplate returns a WebhookTemplateInformer. +func (v *version) WebhookTemplate() WebhookTemplateInformer { + return &webhookTemplateInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions} +} diff --git a/pkg/informers/externalversions/executor/v1/webhooktemplate.go b/pkg/informers/externalversions/executor/v1/webhooktemplate.go new file mode 100644 index 00000000..c681c202 --- /dev/null +++ b/pkg/informers/externalversions/executor/v1/webhooktemplate.go @@ -0,0 +1,94 @@ +/* +Copyright 2021. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1 + +import ( + "context" + "time" + + executorv1 "github.com/kubeshop/testkube-operator/api/executor/v1" + "github.com/kubeshop/testkube-operator/pkg/clientset/versioned" + "github.com/kubeshop/testkube-operator/pkg/informers/externalversions/internalinterfaces" + executorlisterv1 "github.com/kubeshop/testkube-operator/pkg/listers/executor/v1" + + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/watch" + "k8s.io/client-go/tools/cache" +) + +// WebhookTemplateInformer provides access to a shared informer and lister for WebhookTemplate. +type WebhookTemplateInformer interface { + Informer() cache.SharedIndexInformer + Lister() executorlisterv1.WebhookTemplateLister +} + +type webhookTemplateInformer struct { + factory internalinterfaces.SharedInformerFactory + tweakListOptions internalinterfaces.TweakListOptionsFunc + namespace string +} + +// NewWebhookTemplateInformer constructs a new informer for WebhookTemplate type. +// Always prefer using an informer factory to get a shared informer instead of getting an independent +// one. This reduces memory print and number of connections to the server. +func NewWebhookTemplateInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer { + return NewFilteredWebhookTemplateInformer(client, namespace, resyncPeriod, indexers, nil) +} + +// NewFilteredWebhookTemplateInformer constructs a new informer for WebhookTemplate type. +// Always prefer using an informer factory to get a shared informer instead of getting an independent +// one. This reduces memory print and number of connections to the server. +func NewFilteredWebhookTemplateInformer( + client versioned.Interface, + namespace string, + resyncPeriod time.Duration, + indexers cache.Indexers, + tweakListOptions internalinterfaces.TweakListOptionsFunc, +) cache.SharedIndexInformer { + return cache.NewSharedIndexInformer( + &cache.ListWatch{ + ListFunc: func(options v1.ListOptions) (runtime.Object, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.ExecutorV1().WebhookTemplate(namespace).List(context.TODO(), options) + }, + WatchFunc: func(options v1.ListOptions) (watch.Interface, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.ExecutorV1().WebhookTemplate(namespace).Watch(context.TODO(), options) + }, + }, + &executorv1.WebhookTemplate{}, + resyncPeriod, + indexers, + ) +} + +func (f *webhookTemplateInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer { + return NewFilteredWebhookTemplateInformer(client, f.namespace, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions) +} + +func (f *webhookTemplateInformer) Informer() cache.SharedIndexInformer { + return f.factory.InformerFor(&executorv1.WebhookTemplate{}, f.defaultInformer) +} + +func (f *webhookTemplateInformer) Lister() executorlisterv1.WebhookTemplateLister { + return executorlisterv1.NewWebhookTemplateLister(f.Informer().GetIndexer()) +} diff --git a/pkg/informers/externalversions/generic.go b/pkg/informers/externalversions/generic.go index eb6f2e2a..fbc25ba3 100644 --- a/pkg/informers/externalversions/generic.go +++ b/pkg/informers/externalversions/generic.go @@ -85,6 +85,12 @@ func (f *sharedInformerFactory) ForResource(resource schema.GroupVersionResource resource: resource.GroupResource(), informer: f.Executor().V1().Webhook().Informer(), }, nil + // Group=executor.testkube.io, Version=v1 + case executorv1.WebhookTemplateGroupVersionResource: + return &genericInformer{ + resource: resource.GroupResource(), + informer: f.Executor().V1().WebhookTemplate().Informer(), + }, nil // Group=tests.testkube.io, Version=v1 case testsourcev1.GroupVersionResource: return &genericInformer{ diff --git a/pkg/listers/executor/v1/expansion_generated.go b/pkg/listers/executor/v1/expansion_generated.go index 30a233f6..a8ccbbde 100644 --- a/pkg/listers/executor/v1/expansion_generated.go +++ b/pkg/listers/executor/v1/expansion_generated.go @@ -29,3 +29,10 @@ type WebhookListerExpansion interface{} // WebhookNamespaceListerExpansion allows custom methods to be added to // WebhookNamespaceLister. type WebhookNamespaceListerExpansion interface{} + +// WebhookTemplateListerExpansion allows custom methods to be added to WebhookTemplateLister. +type WebhookTemplateListerExpansion interface{} + +// WebhookTemplateNamespaceListerExpansion allows custom methods to be added to +// WebhookTemplateNamespaceLister. +type WebhookTemplateNamespaceListerExpansion interface{} diff --git a/pkg/listers/executor/v1/webhooktemplates.go b/pkg/listers/executor/v1/webhooktemplates.go new file mode 100644 index 00000000..c84d5626 --- /dev/null +++ b/pkg/listers/executor/v1/webhooktemplates.go @@ -0,0 +1,101 @@ +/* +Copyright 2021. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1 + +import ( + executorv1 "github.com/kubeshop/testkube-operator/api/executor/v1" + "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/client-go/tools/cache" +) + +// WebhookTemplateLister helps list WebhookTemplates. +// All objects returned here must be treated as read-only. +type WebhookTemplateLister interface { + // List lists all WebhookTemplates in the indexer. + // Objects returned here must be treated as read-only. + List(selector labels.Selector) (ret []*executorv1.WebhookTemplate, err error) + // WebhookTemplates returns an object that can list and get WebhookTemplates. + WebhookTemplates(namespace string) WebhookTemplateNamespaceLister + WebhookTemplateListerExpansion +} + +// webhookTemplateLister implements the WebhookTemplateLister interface. +type webhookTemplateLister struct { + indexer cache.Indexer +} + +// NewWebhookTemplateLister returns a new WebhookTemplateLister. +func NewWebhookTemplateLister(indexer cache.Indexer) WebhookTemplateLister { + return &webhookTemplateLister{indexer: indexer} +} + +// List lists all WebhookTemplates in the indexer. +func (s *webhookTemplateLister) List(selector labels.Selector) (ret []*executorv1.WebhookTemplate, err error) { + err = cache.ListAll(s.indexer, selector, func(m interface{}) { + ret = append(ret, m.(*executorv1.WebhookTemplate)) + }) + return ret, err +} + +// WebhookTemplates returns an object that can list and get WebhookTemplates. +func (s *webhookTemplateLister) WebhookTemplates(namespace string) WebhookTemplateNamespaceLister { + return webhookTemplateNamespaceLister{indexer: s.indexer, namespace: namespace} +} + +// WebhookTemplateNamespaceLister helps list and get WebhookTemplates. +// All objects returned here must be treated as read-only. +type WebhookTemplateNamespaceLister interface { + // List lists all WebhookTemplates in the indexer for a given namespace. + // Objects returned here must be treated as read-only. + List(selector labels.Selector) (ret []*executorv1.WebhookTemplate, err error) + // Get retrieves the WebhookTemplate from the indexer for a given namespace and name. + // Objects returned here must be treated as read-only. + Get(name string) (*executorv1.WebhookTemplate, error) + WebhookTemplateNamespaceListerExpansion +} + +// webhookTemplateNamespaceLister implements the WebhookTemplateNamespaceLister +// interface. +type webhookTemplateNamespaceLister struct { + indexer cache.Indexer + namespace string +} + +// List lists all WebhookTemplates in the indexer for a given namespace. +func (s webhookTemplateNamespaceLister) List(selector labels.Selector) (ret []*executorv1.WebhookTemplate, err error) { + err = cache.ListAllByNamespace(s.indexer, s.namespace, selector, func(m interface{}) { + ret = append(ret, m.(*executorv1.WebhookTemplate)) + }) + return ret, err +} + +// Get retrieves the WebhookTemplate from the indexer for a given namespace and name. +func (s webhookTemplateNamespaceLister) Get(name string) (*executorv1.WebhookTemplate, error) { + obj, exists, err := s.indexer.GetByKey(s.namespace + "/" + name) + if err != nil { + return nil, err + } + if !exists { + return nil, errors.NewNotFound( + schema.GroupResource{Group: executorv1.GroupVersion.Group, Resource: executorv1.WebhookTemplateResource}, + name, + ) + } + return obj.(*executorv1.WebhookTemplate), nil +}