diff --git a/Makefile b/Makefile index d4594e1222f..9e79bffce89 100644 --- a/Makefile +++ b/Makefile @@ -138,6 +138,7 @@ openapi-generate-model-testkube: find ./pkg/api/v1/testkube -name "*update*.go" -type f -exec sed -i '' -e "s/ \*PodRequest/ \*\*PodUpdateRequest/g" {} \; find ./pkg/api/v1/testkube -name "*update*.go" -type f -exec sed -i '' -e "s/ \*PodResourcesRequest/ \*\*PodResourcesUpdateRequest/g" {} \; find ./pkg/api/v1/testkube -name "*update*.go" -type f -exec sed -i '' -e "s/ \*ResourceRequest/ \*ResourceUpdateRequest/g" {} \; + find ./pkg/api/v1/testkube -name "*update*.go" -type f -exec sed -i '' -e "s/ \*WebhookTemplateRef/ \*\*WebhookTemplateRef/g" {} \; find ./pkg/api/v1/testkube -type f -exec sed -i '' -e "s/ Deprecated/ \\n\/\/ Deprecated/g" {} \; go fmt pkg/api/v1/testkube/*.go diff --git a/go.mod b/go.mod index 3e91f2b9bcf..fd58f902d1b 100644 --- a/go.mod +++ b/go.mod @@ -42,7 +42,7 @@ require ( github.com/keygen-sh/jsonapi-go v1.2.1 github.com/keygen-sh/keygen-go/v3 v3.2.0 github.com/kubepug/kubepug v1.7.1 - github.com/kubeshop/testkube-operator v1.17.55-0.20241219143413-6a33aef7130f + github.com/kubeshop/testkube-operator v1.17.55-0.20241220172614-fb95745c73c1 github.com/minio/minio-go/v7 v7.0.66 github.com/montanaflynn/stats v0.7.1 github.com/moogar0880/problems v0.1.1 diff --git a/go.sum b/go.sum index 0419aff4e35..56baeafa040 100644 --- a/go.sum +++ b/go.sum @@ -336,8 +336,8 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kubepug/kubepug v1.7.1 h1:LKhfSxS8Y5mXs50v+3Lpyec+cogErDLcV7CMUuiaisw= github.com/kubepug/kubepug v1.7.1/go.mod h1:lv+HxD0oTFL7ZWjj0u6HKhMbbTIId3eG7aWIW0gyF8g= -github.com/kubeshop/testkube-operator v1.17.55-0.20241219143413-6a33aef7130f h1:7wDuGy4MohoFcgz2QPH/02JWCsgBqkWdGgefIH2YSEE= -github.com/kubeshop/testkube-operator v1.17.55-0.20241219143413-6a33aef7130f/go.mod h1:P47tw1nKQFufdsZndyq2HG2MSa0zK/lU0XpRfZtEmIk= +github.com/kubeshop/testkube-operator v1.17.55-0.20241220172614-fb95745c73c1 h1:C1TIQh5zlZG25KlI8RuyaeoehcQE4FKTuw6k5oATbp0= +github.com/kubeshop/testkube-operator v1.17.55-0.20241220172614-fb95745c73c1/go.mod h1:P47tw1nKQFufdsZndyq2HG2MSa0zK/lU0XpRfZtEmIk= github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= github.com/lithammer/fuzzysearch v1.1.8 h1:/HIuJnjHuXS8bKaiTMeeDlW2/AyIWk2brx1V8LFgLN4= diff --git a/pkg/api/v1/testkube/model_webhook_update_request.go b/pkg/api/v1/testkube/model_webhook_update_request.go index 64e5060b33e..a779a3ac969 100644 --- a/pkg/api/v1/testkube/model_webhook_update_request.go +++ b/pkg/api/v1/testkube/model_webhook_update_request.go @@ -33,5 +33,5 @@ type WebhookUpdateRequest struct { Disabled *bool `json:"disabled,omitempty"` Config *map[string]WebhookConfigValue `json:"config,omitempty"` Parameters *map[string]WebhookParameterSchema `json:"parameters,omitempty"` - WebhookTemplateRef *WebhookTemplateRef `json:"webhookTemplateRef,omitempty"` + WebhookTemplateRef **WebhookTemplateRef `json:"webhookTemplateRef,omitempty"` } diff --git a/pkg/crd/crd_test.go b/pkg/crd/crd_test.go index 5db775ba338..a540e80de3d 100644 --- a/pkg/crd/crd_test.go +++ b/pkg/crd/crd_test.go @@ -12,7 +12,7 @@ func TestGenerateYAML(t *testing.T) { t.Run("generate single CRD yaml", func(t *testing.T) { // given - expected := "apiVersion: executor.testkube.io/v1\nkind: Webhook\nmetadata:\n name: name1\n namespace: namespace1\n labels:\n key1: value1\nspec:\n events:\n - start-test\n uri: http://localhost\n selector: app=backend\n payloadObjectField: text\n payloadTemplate: {{ .Id }}\n payloadTemplateReference: ref\n headers:\n Content-Type: appication/xml\n" + expected := "apiVersion: executor.testkube.io/v1\nkind: Webhook\nmetadata:\n name: name1\n namespace: namespace1\n labels:\n key1: value1\n annotations:\n key2: value2 \nspec:\n events:\n - start-test\n uri: http://localhost\n selector: app=backend\n payloadObjectField: text\n payloadTemplate: {{ .Id }}\n payloadTemplateReference: ref\n headers:\n Content-Type: appication/xml\n disabled: true\n config:\n var1:\n public: pb\n var2:\n private:\n namespace: ns\n name: secret\n key: pr\n parameters:\n var3:\n description: descr\n required: true\n example: 12345\n default: 0\n pattern: [0-9]*\n webhookTemplateRef:\n name: tmpl\n" webhooks := []testkube.Webhook{ { Name: "name1", @@ -21,10 +21,29 @@ func TestGenerateYAML(t *testing.T) { Events: []testkube.EventType{*testkube.EventStartTest}, Selector: "app=backend", Labels: map[string]string{"key1": "value1"}, + Annotations: map[string]string{"key2": "value2"}, PayloadObjectField: "text", PayloadTemplate: "{{ .Id }}", Headers: map[string]string{"Content-Type": "appication/xml"}, PayloadTemplateReference: "ref", + Disabled: true, + Config: map[string]testkube.WebhookConfigValue{ + "var1": {Public: &testkube.BoxedString{Value: "pb"}}, + "var2": {Private: &testkube.SecretRef{Namespace: "ns", Name: "secret", Key: "pr"}}}, + Parameters: map[string]testkube.WebhookParameterSchema{ + "var3": { + Description: "descr", + Required: true, + Example: "12345", + Default_: &testkube.BoxedString{ + Value: "0", + }, + Pattern: "[0-9]*", + }, + }, + WebhookTemplateRef: &testkube.WebhookTemplateRef{ + Name: "tmpl", + }, }, } @@ -38,7 +57,7 @@ func TestGenerateYAML(t *testing.T) { t.Run("generate multiple CRDs yaml", func(t *testing.T) { // given - expected := "apiVersion: executor.testkube.io/v1\nkind: Webhook\nmetadata:\n name: name1\n namespace: namespace1\n labels:\n key1: value1\nspec:\n events:\n - start-test\n uri: http://localhost\n selector: app=backend\n payloadObjectField: text\n payloadTemplate: {{ .Id }}\n payloadTemplateReference: ref\n headers:\n Content-Type: appication/xml\n\n---\napiVersion: executor.testkube.io/v1\nkind: Webhook\nmetadata:\n name: name2\n namespace: namespace2\n labels:\n key2: value2\nspec:\n events:\n - end-test-success\n uri: http://localhost\n selector: app=backend\n payloadObjectField: text\n payloadTemplate: {{ .Id }}\n payloadTemplateReference: ref\n headers:\n Content-Type: appication/xml\n" + expected := "apiVersion: executor.testkube.io/v1\nkind: Webhook\nmetadata:\n name: name1\n namespace: namespace1\n labels:\n key1: value1\n annotations:\n key3: value3 \nspec:\n events:\n - start-test\n uri: http://localhost\n selector: app=backend\n payloadObjectField: text\n payloadTemplate: {{ .Id }}\n payloadTemplateReference: ref\n headers:\n Content-Type: appication/xml\n disabled: true\n config:\n var1:\n public: pb\n var2:\n private:\n namespace: ns\n name: secret\n key: pr\n parameters:\n var3:\n description: descr\n required: true\n example: 12345\n default: 0\n pattern: [0-9]*\n webhookTemplateRef:\n name: tmpl\n\n---\napiVersion: executor.testkube.io/v1\nkind: Webhook\nmetadata:\n name: name2\n namespace: namespace2\n labels:\n key2: value2\n annotations:\n key4: value4 \nspec:\n events:\n - end-test-success\n uri: http://localhost\n selector: app=backend\n payloadObjectField: text\n payloadTemplate: {{ .Id }}\n payloadTemplateReference: ref\n headers:\n Content-Type: appication/xml\n disabled: true\n config:\n var1:\n public: pb\n var2:\n private:\n namespace: ns\n name: secret\n key: pr\n parameters:\n var3:\n description: descr\n required: true\n example: 12345\n default: 0\n pattern: [0-9]*\n webhookTemplateRef:\n name: tmpl\n" webhooks := []testkube.Webhook{ { Name: "name1", @@ -47,10 +66,29 @@ func TestGenerateYAML(t *testing.T) { Events: []testkube.EventType{*testkube.EventStartTest}, Selector: "app=backend", Labels: map[string]string{"key1": "value1"}, + Annotations: map[string]string{"key3": "value3"}, PayloadObjectField: "text", PayloadTemplate: "{{ .Id }}", Headers: map[string]string{"Content-Type": "appication/xml"}, PayloadTemplateReference: "ref", + Disabled: true, + Config: map[string]testkube.WebhookConfigValue{ + "var1": {Public: &testkube.BoxedString{Value: "pb"}}, + "var2": {Private: &testkube.SecretRef{Namespace: "ns", Name: "secret", Key: "pr"}}}, + Parameters: map[string]testkube.WebhookParameterSchema{ + "var3": { + Description: "descr", + Required: true, + Example: "12345", + Default_: &testkube.BoxedString{ + Value: "0", + }, + Pattern: "[0-9]*", + }, + }, + WebhookTemplateRef: &testkube.WebhookTemplateRef{ + Name: "tmpl", + }, }, { Name: "name2", @@ -59,10 +97,29 @@ func TestGenerateYAML(t *testing.T) { Events: []testkube.EventType{*testkube.EventEndTestSuccess}, Selector: "app=backend", Labels: map[string]string{"key2": "value2"}, + Annotations: map[string]string{"key4": "value4"}, PayloadObjectField: "text", PayloadTemplate: "{{ .Id }}", Headers: map[string]string{"Content-Type": "appication/xml"}, PayloadTemplateReference: "ref", + Disabled: true, + Config: map[string]testkube.WebhookConfigValue{ + "var1": {Public: &testkube.BoxedString{Value: "pb"}}, + "var2": {Private: &testkube.SecretRef{Namespace: "ns", Name: "secret", Key: "pr"}}}, + Parameters: map[string]testkube.WebhookParameterSchema{ + "var3": { + Description: "descr", + Required: true, + Example: "12345", + Default_: &testkube.BoxedString{ + Value: "0", + }, + Pattern: "[0-9]*", + }, + }, + WebhookTemplateRef: &testkube.WebhookTemplateRef{ + Name: "tmpl", + }, }, } diff --git a/pkg/crd/templates/webhook.tmpl b/pkg/crd/templates/webhook.tmpl index dae15d9bb7f..68f0ecb5a44 100644 --- a/pkg/crd/templates/webhook.tmpl +++ b/pkg/crd/templates/webhook.tmpl @@ -9,6 +9,12 @@ metadata: {{ $key }}: {{ $value }} {{- end }} {{- end }} + {{- if ne (len .Annotations) 0 }} + annotations: + {{- range $key, $value := .Annotations }} + {{ $key }}: {{ $value }} + {{- end }} + {{- end }} spec: {{- if ne (len .Events) 0 }} events: @@ -36,4 +42,47 @@ spec: {{- range $key, $value := .Headers }} {{ $key }}: {{ $value }} {{- end }} + {{- if .Disabled }} + disabled: {{ .Disabled }} + {{- end }} + {{- if ne (len .Config) 0 }} + config: + {{- range $key, $value := .Config }} + {{ $key }}: + {{- if $value.Public }} + public: {{ $value.Public.Value }} + {{- end }} + {{- if $value.Private }} + private: + namespace: {{ $value.Private.Namespace }} + name: {{ $value.Private.Name }} + key: {{ $value.Private.Key }} + {{- end }} + {{- end }} + {{- end }} + {{- if ne (len .Parameters) 0 }} + parameters: + {{- range $key, $value := .Parameters }} + {{ $key }}: + {{- if $value.Description }} + description: {{ $value.Description }} + {{- end }} + {{- if $value.Required }} + required: {{ $value.Required }} + {{- end }} + {{- if $value.Example }} + example: {{ $value.Example }} + {{- end }} + {{- if $value.Default_ }} + default: {{ $value.Default_.Value }} + {{- end }} + {{- if $value.Pattern }} + pattern: {{ $value.Pattern }} + {{- end }} + {{- end }} + {{- end }} + {{- if .WebhookTemplateRef }} + webhookTemplateRef: + name: {{ .WebhookTemplateRef.Name }} + {{- end }} {{- end }} diff --git a/pkg/mapper/webhooks/mapper.go b/pkg/mapper/webhooks/mapper.go index 21f00d88cce..3e961dd074c 100644 --- a/pkg/mapper/webhooks/mapper.go +++ b/pkg/mapper/webhooks/mapper.go @@ -4,6 +4,7 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" executorv1 "github.com/kubeshop/testkube-operator/api/executor/v1" + "github.com/kubeshop/testkube/internal/common" "github.com/kubeshop/testkube/pkg/api/v1/testkube" ) @@ -21,6 +22,52 @@ func MapCRDToAPI(item executorv1.Webhook) testkube.Webhook { PayloadTemplateReference: item.Spec.PayloadTemplateReference, Headers: item.Spec.Headers, Disabled: item.Spec.Disabled, + Config: common.MapMap(item.Spec.Config, MapConfigValueCRDToAPI), + Parameters: common.MapMap(item.Spec.Parameters, MapParameterSchemaCRDToAPI), + WebhookTemplateRef: common.MapPtr(item.Spec.WebhookTemplateRef, MapTemplateRefCRDToAPI), + } +} + +// MapStringToBoxedString maps string to boxed string +func MapStringToBoxedString(v *string) *testkube.BoxedString { + if v == nil { + return nil + } + return &testkube.BoxedString{Value: *v} +} + +// MapSecretRefCRDToAPI maps secret ref to OpenAPI spec +func MapSecretRefCRDToAPI(v executorv1.SecretRef) testkube.SecretRef { + return testkube.SecretRef{ + Namespace: v.Namespace, + Name: v.Name, + Key: v.Key, + } +} + +// MapConigValueCRDToAPI maps config value to OpenAPI spec +func MapConfigValueCRDToAPI(v executorv1.WebhookConfigValue) testkube.WebhookConfigValue { + return testkube.WebhookConfigValue{ + Public: MapStringToBoxedString(v.Public), + Private: common.MapPtr(v.Private, MapSecretRefCRDToAPI), + } +} + +// MapParameterSchemaCRDToAPI maps parameter schema to OpenAPI spec +func MapParameterSchemaCRDToAPI(v executorv1.WebhookParameterSchema) testkube.WebhookParameterSchema { + return testkube.WebhookParameterSchema{ + Description: v.Description, + Required: v.Required, + Example: v.Example, + Default_: MapStringToBoxedString(v.Default_), + Pattern: v.Pattern, + } +} + +// MapTemplateRefCRDToAPI maps template ref to OpenAPI spec +func MapTemplateRefCRDToAPI(v executorv1.WebhookTemplateRef) testkube.WebhookTemplateRef { + return testkube.WebhookTemplateRef{ + Name: v.Name, } } @@ -57,10 +104,56 @@ func MapAPIToCRD(request testkube.WebhookCreateRequest) executorv1.Webhook { PayloadTemplateReference: request.PayloadTemplateReference, Headers: request.Headers, Disabled: request.Disabled, + Config: common.MapMap(request.Config, MapConfigValueAPIToCRD), + Parameters: common.MapMap(request.Parameters, MapParameterSchemaAPIToCRD), + WebhookTemplateRef: common.MapPtr(request.WebhookTemplateRef, MapTemplateRefAPIToCRD), }, } } +// MapBoxedStringToString maps boxed string to string +func MapBoxedStringToString(v *testkube.BoxedString) *string { + if v == nil { + return nil + } + return &v.Value +} + +// MapSecretRefAPIToCRD maps secret ref to CRD spec +func MapSecretRefAPIToCRD(v testkube.SecretRef) executorv1.SecretRef { + return executorv1.SecretRef{ + Namespace: v.Namespace, + Name: v.Name, + Key: v.Key, + } +} + +// MapConigValueAPIToCRD maps config value to CRD spec +func MapConfigValueAPIToCRD(v testkube.WebhookConfigValue) executorv1.WebhookConfigValue { + return executorv1.WebhookConfigValue{ + Public: MapBoxedStringToString(v.Public), + Private: common.MapPtr(v.Private, MapSecretRefAPIToCRD), + } +} + +// MapParameterSchemaAPIToCRD maps parameter schema to CRD spec +func MapParameterSchemaAPIToCRD(v testkube.WebhookParameterSchema) executorv1.WebhookParameterSchema { + return executorv1.WebhookParameterSchema{ + Description: v.Description, + Required: v.Required, + Example: v.Example, + Default_: MapBoxedStringToString(v.Default_), + Pattern: v.Pattern, + } +} + +// MapTemplateRefAPIToCRD maps template ref to CRD spec +func MapTemplateRefAPIToCRD(v testkube.WebhookTemplateRef) executorv1.WebhookTemplateRef { + return executorv1.WebhookTemplateRef{ + Name: v.Name, + } +} + // MapEventTypesToStringArray maps OpenAPI spec list of EventType to string array func MapEventTypesToStringArray(eventTypes []testkube.EventType) (arr []executorv1.EventType) { for _, et := range eventTypes { @@ -131,6 +224,18 @@ func MapUpdateToSpec(request testkube.WebhookUpdateRequest, webhook *executorv1. webhook.Spec.Disabled = *request.Disabled } + if request.Config != nil { + webhook.Spec.Config = common.MapMap(*request.Config, MapConfigValueAPIToCRD) + } + + if request.Parameters != nil { + webhook.Spec.Parameters = common.MapMap(*request.Parameters, MapParameterSchemaAPIToCRD) + } + + if request.WebhookTemplateRef != nil { + webhook.Spec.WebhookTemplateRef = common.MapPtr(*request.WebhookTemplateRef, MapTemplateRefAPIToCRD) + } + return webhook } @@ -181,6 +286,9 @@ func MapSpecToUpdate(webhook *executorv1.Webhook) (request testkube.WebhookUpdat request.Annotations = &webhook.Annotations request.Headers = &webhook.Spec.Headers request.Disabled = &webhook.Spec.Disabled + request.Config = common.Ptr(common.MapMap(webhook.Spec.Config, MapConfigValueCRDToAPI)) + request.Parameters = common.Ptr(common.MapMap(webhook.Spec.Parameters, MapParameterSchemaCRDToAPI)) + request.WebhookTemplateRef = common.Ptr(common.MapPtr(webhook.Spec.WebhookTemplateRef, MapTemplateRefCRDToAPI)) return request }