Skip to content

Commit

Permalink
feat: [TKC-1948] disable webhooks (#5472)
Browse files Browse the repository at this point in the history
* feat: disable webhooks

* fix: code review comment
  • Loading branch information
vLia authored May 23, 2024
1 parent 3f67575 commit fac4609
Show file tree
Hide file tree
Showing 17 changed files with 184 additions and 9 deletions.
7 changes: 7 additions & 0 deletions api/v1/testkube.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6849,6 +6849,13 @@ components:
example:
env: "prod"
app: "backend"
disabled:
type: boolean
description: whether webhook is disabled
default: false
example:
- true
- false

Event:
description: Event data
Expand Down
20 changes: 20 additions & 0 deletions cmd/kubectl-testkube/commands/webhooks/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,13 @@ func NewCreateWebhookOptionsFromFlags(cmd *cobra.Command) (options apiv1.CreateW
PayloadTemplateReference: payloadTemplateReference,
}

if cmd.Flag("enable").Changed {
options.Disabled = false
}
if cmd.Flag("disable").Changed {
options.Disabled = true
}

return options, nil
}

Expand Down Expand Up @@ -137,5 +144,18 @@ func NewUpdateWebhookOptionsFromFlags(cmd *cobra.Command) (options apiv1.UpdateW
options.Headers = &headers
}

if cmd.Flag("enable").Changed {
options.Disabled = new(bool)
*options.Disabled = false
}
if cmd.Flag("disable").Changed {
options.Disabled = new(bool)
*options.Disabled = true
}

return options, nil
}

func isBothEnabledAndDisabledSet(cmd *cobra.Command) bool {
return cmd.Flag("enable").Changed && cmd.Flag("disable").Changed
}
13 changes: 13 additions & 0 deletions cmd/kubectl-testkube/commands/webhooks/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,13 +38,24 @@ func NewCreateWebhookCmd() *cobra.Command {
ui.Failf("pass valid name (in '--name' flag)")
}

if isBothEnabledAndDisabledSet(cmd) {
ui.Failf("both --enable and --disable flags are set, please use only one")
}

namespace := cmd.Flag("namespace").Value.String()
var client apiv1.Client
if !crdOnly {
client, namespace, err = common.GetClient(cmd)
ui.ExitOnError("getting client", err)

webhook, _ := client.GetWebhook(name)
if cmd.Flag("enable").Changed {
webhook.Disabled = false
}
if cmd.Flag("disable").Changed {
webhook.Disabled = true
}

if name == webhook.Name {
if cmd.Flag("update").Changed {
if !update {
Expand Down Expand Up @@ -99,6 +110,8 @@ func NewCreateWebhookCmd() *cobra.Command {
cmd.Flags().StringToStringVarP(&headers, "header", "", nil, "webhook header value pair (golang template supported): --header Content-Type=application/xml")
cmd.Flags().StringVar(&payloadTemplateReference, "payload-template-reference", "", "reference to payload template to use for the webhook")
cmd.Flags().BoolVar(&update, "update", false, "update, if webhook already exists")
cmd.Flags().Bool("disable", false, "disable webhook")
cmd.Flags().Bool("enable", false, "enable webhook")

return cmd
}
8 changes: 8 additions & 0 deletions cmd/kubectl-testkube/commands/webhooks/update.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ func UpdateWebhookCmd() *cobra.Command {
payloadTemplate string
headers map[string]string
payloadTemplateReference string
enable bool
disable bool
)

cmd := &cobra.Command{
Expand All @@ -29,6 +31,10 @@ func UpdateWebhookCmd() *cobra.Command {
ui.Failf("pass valid name (in '--name' flag)")
}

if isBothEnabledAndDisabledSet(cmd) {
ui.Failf("both --enable and --disable flags are set, please use only one")
}

client, namespace, err := common.GetClient(cmd)
ui.ExitOnError("getting client", err)

Expand Down Expand Up @@ -56,6 +62,8 @@ func UpdateWebhookCmd() *cobra.Command {
cmd.Flags().StringVarP(&payloadTemplate, "payload-template", "", "", "if webhook needs to send a custom notification, then a path to template file should be provided")
cmd.Flags().StringToStringVarP(&headers, "header", "", nil, "webhook header value pair (golang template supported): --header Content-Type=application/xml")
cmd.Flags().StringVar(&payloadTemplateReference, "payload-template-reference", "", "reference to payload template to use for the webhook")
cmd.Flags().BoolVar(&disable, "disable", false, "disable webhook")
cmd.Flags().BoolVar(&enable, "enable", false, "enable webhook")

return cmd
}
79 changes: 79 additions & 0 deletions docs/docs/articles/webhooks.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -450,3 +450,82 @@ If you are just getting started and want to test your webhook configuration, you
2. [Webhook.Site](https://webhook.site/): Webhook.Site provides an easy way to capture HTTP payloads, review the headers and body for the incoming requests, and automatically respond with a `200 OK` status code.

By using these services, you can quickly set up an HTTP endpoint to receive webhook payloads during your testing/development process.

## Disabling Webhooks

Disabling webhooks can be helpful to make sure your team does not get spammed with notifications during development. Testkube lets you disable them via the CLI, the API or modifying the CRD directly. By default, webhooks are enabled on creation.

<Tabs>
<TabItem value="cli" label="CLI">

The easiest way to operate on webhooks in Testkube is to use the official CLI.

#### Set on Creation

To disable:

```bash
testkube create webhook --disable --name "${WEBHOOK_NAME}" ...
```

To enable it explicitly:

```bash
testkube create webhook --enable --name "${WEBHOOK_NAME}" ...
```

#### Set on Update

To disable:

```bash
testkube update webhook --disable --name "${WEBHOOK_NAME}"
```

To enable:

```bash
testkube update webhook --enable --name "${WEBHOOK_NAME}"
```

</TabItem>

<TabItem value="api" label="API">

When automating your operations on webhooks, you can also use the API. For more details, please consult the "Core OSS OpenAPI Definion" page, respectively the "OpenAPI Specification" for Testkube Pro and Enterprise, under the Reference tab on the left. The field specifying this on the webhooks is called `disabled` and is expecting a value of type `boolean`.

Keep in mind, that enabling the webhook again from the API will require specifying `"disabled":false`, simply deleting the field from the object is interpreted as not setting any value on it.

</TabItem>

<TabItem value="crd" label="Custom Resource">

Using the webhook from the example in creation, you can add the field `disabled` set to `"true"` to the specification:

```yaml title="webhook.yaml"
apiVersion: executor.testkube.io/v1
kind: Webhook
metadata:
name: example-webhook
namespace: testkube
spec:
uri: <YOUR_ENDPOINT_URL>
events:
- start-test
- end-test-success
- end-test-failed
selector: ""
disabled: "true"
```

Apply with:

```sh
kubectl apply -f webhook.yaml
```

To enable a webhook again, you can either set the field `disabled` to `"false"` explicitly, or delete it altogether from the specification.

</TabItem>

</Tabs>
2 changes: 2 additions & 0 deletions docs/docs/cli/testkube_create_webhook.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ testkube create webhook [flags]
### Options

```
--disable disable webhook
--enable enable webhook
-e, --events stringArray event types handled by webhook e.g. start-test|end-test
--header stringToString webhook header value pair (golang template supported): --header Content-Type=application/xml (default [])
-h, --help help for webhook
Expand Down
2 changes: 2 additions & 0 deletions docs/docs/cli/testkube_update_webhook.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ testkube update webhook [flags]
### Options

```
--disable disable webhook
--enable enable webhook
-e, --events stringArray event types handled by webhook e.g. start-test|end-test
--header stringToString webhook header value pair (golang template supported): --header Content-Type=application/xml (default [])
-h, --help help for webhook
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ require (
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51
github.com/kelseyhightower/envconfig v1.4.0
github.com/kubepug/kubepug v1.7.1
github.com/kubeshop/testkube-operator v1.15.2-beta1.0.20240520124846-07a4da84c97e
github.com/kubeshop/testkube-operator v1.15.2-beta1.0.20240521110543-f5f8bc7df098
github.com/minio/minio-go/v7 v7.0.47
github.com/montanaflynn/stats v0.6.6
github.com/moogar0880/problems v0.1.1
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -368,6 +368,8 @@ 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.15.2-beta1.0.20240520124846-07a4da84c97e h1:q6lJqg6BfZlvogXRhC4iuPlQsZ1gk9Ng8JkYALFL6kA=
github.com/kubeshop/testkube-operator v1.15.2-beta1.0.20240520124846-07a4da84c97e/go.mod h1:P47tw1nKQFufdsZndyq2HG2MSa0zK/lU0XpRfZtEmIk=
github.com/kubeshop/testkube-operator v1.15.2-beta1.0.20240521110543-f5f8bc7df098 h1:Dwu0AWX+pPgKYjlDXHf+7TRP3FiPzEcciBIqbUIg3dI=
github.com/kubeshop/testkube-operator v1.15.2-beta1.0.20240521110543-f5f8bc7df098/go.mod h1:P47tw1nKQFufdsZndyq2HG2MSa0zK/lU0XpRfZtEmIk=
github.com/leodido/go-urn v1.2.1 h1:BqpAaACuzVSgi/VLzGZIobT2z4v53pjosyNd9Yv6n/w=
github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY=
github.com/lithammer/fuzzysearch v1.1.8 h1:/HIuJnjHuXS8bKaiTMeeDlW2/AyIWk2brx1V8LFgLN4=
Expand Down
2 changes: 2 additions & 0 deletions pkg/api/v1/testkube/model_webhook.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,6 @@ type Webhook struct {
Headers map[string]string `json:"headers,omitempty"`
// webhook labels
Labels map[string]string `json:"labels,omitempty"`
// whether webhook is disabled
Disabled bool `json:"disabled,omitempty"`
}
2 changes: 2 additions & 0 deletions pkg/api/v1/testkube/model_webhook_create_request.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,6 @@ type WebhookCreateRequest struct {
Headers map[string]string `json:"headers,omitempty"`
// webhook labels
Labels map[string]string `json:"labels,omitempty"`
// whether webhook is disabled
Disabled bool `json:"disabled,omitempty"`
}
3 changes: 2 additions & 1 deletion pkg/api/v1/testkube/model_webhook_extended.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import "fmt"
type Webhooks []Webhook

func (list Webhooks) Table() (header []string, output [][]string) {
header = []string{"Name", "URI", "Events", "Selector", "Labels"}
header = []string{"Name", "URI", "Events", "Selector", "Labels", "Disabled"}

for _, e := range list {
output = append(output, []string{
Expand All @@ -23,6 +23,7 @@ func (list Webhooks) Table() (header []string, output [][]string) {
fmt.Sprintf("%v", e.Events),
e.Selector,
MapToString(e.Labels),
fmt.Sprint(e.Disabled),
})
}

Expand Down
2 changes: 2 additions & 0 deletions pkg/api/v1/testkube/model_webhook_update_request.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,6 @@ type WebhookUpdateRequest struct {
Headers *map[string]string `json:"headers,omitempty"`
// webhook labels
Labels *map[string]string `json:"labels,omitempty"`
// whether webhook is disabled
Disabled *bool `json:"disabled,omitempty"`
}
14 changes: 13 additions & 1 deletion pkg/event/kind/webhook/listener.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import (
var _ common.Listener = (*WebhookListener)(nil)

func NewWebhookListener(name, uri, selector string, events []testkube.EventType,
payloadObjectField, payloadTemplate string, headers map[string]string) *WebhookListener {
payloadObjectField, payloadTemplate string, headers map[string]string, disabled bool) *WebhookListener {
return &WebhookListener{
name: name,
Uri: uri,
Expand All @@ -33,6 +33,7 @@ func NewWebhookListener(name, uri, selector string, events []testkube.EventType,
payloadObjectField: payloadObjectField,
payloadTemplate: payloadTemplate,
headers: headers,
disabled: disabled,
}
}

Expand All @@ -46,6 +47,7 @@ type WebhookListener struct {
payloadObjectField string
payloadTemplate string
headers map[string]string
disabled bool
}

func (l *WebhookListener) Name() string {
Expand All @@ -68,6 +70,7 @@ func (l *WebhookListener) Metadata() map[string]string {
"payloadObjectField": l.payloadObjectField,
"payloadTemplate": l.payloadTemplate,
"headers": fmt.Sprintf("%v", l.headers),
"disabled": fmt.Sprint(l.disabled),
}
}

Expand All @@ -83,7 +86,16 @@ func (l *WebhookListener) Headers() map[string]string {
return l.headers
}

func (l *WebhookListener) Disabled() bool {
return l.disabled
}

func (l *WebhookListener) Notify(event testkube.Event) (result testkube.EventResult) {
if l.disabled {
l.Log.With(event.Log()...).Debug("webhook listener is disabled")
return testkube.NewSuccessEventResult(event.Id, "webhook listener is disabled")
}

body := bytes.NewBuffer([]byte{})
log := l.Log.With(event.Log()...)

Expand Down
26 changes: 21 additions & 5 deletions pkg/event/kind/webhook/listener_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ func TestWebhookListener_Notify(t *testing.T) {
svr := httptest.NewServer(testHandler)
defer svr.Close()

l := NewWebhookListener("l1", svr.URL, "", testEventTypes, "", "", nil)
l := NewWebhookListener("l1", svr.URL, "", testEventTypes, "", "", nil, false)

// when
r := l.Notify(testkube.Event{
Expand All @@ -55,7 +55,7 @@ func TestWebhookListener_Notify(t *testing.T) {
svr := httptest.NewServer(testHandler)
defer svr.Close()

l := NewWebhookListener("l1", svr.URL, "", testEventTypes, "", "", nil)
l := NewWebhookListener("l1", svr.URL, "", testEventTypes, "", "", nil, false)

// when
r := l.Notify(testkube.Event{
Expand All @@ -72,7 +72,7 @@ func TestWebhookListener_Notify(t *testing.T) {
t.Parallel()
// given

s := NewWebhookListener("l1", "http://baduri.badbadbad", "", testEventTypes, "", "", nil)
s := NewWebhookListener("l1", "http://baduri.badbadbad", "", testEventTypes, "", "", nil, false)

// when
r := s.Notify(testkube.Event{
Expand Down Expand Up @@ -105,7 +105,7 @@ func TestWebhookListener_Notify(t *testing.T) {
svr := httptest.NewServer(testHandler)
defer svr.Close()

l := NewWebhookListener("l1", svr.URL, "", testEventTypes, "field", "", nil)
l := NewWebhookListener("l1", svr.URL, "", testEventTypes, "field", "", nil, false)

// when
r := l.Notify(testkube.Event{
Expand All @@ -131,7 +131,7 @@ func TestWebhookListener_Notify(t *testing.T) {
svr := httptest.NewServer(testHandler)
defer svr.Close()

l := NewWebhookListener("l1", svr.URL, "", testEventTypes, "", "{\"id\": \"{{ .Id }}\"}", map[string]string{"Content-Type": "application/json"})
l := NewWebhookListener("l1", svr.URL, "", testEventTypes, "", "{\"id\": \"{{ .Id }}\"}", map[string]string{"Content-Type": "application/json"}, false)

// when
r := l.Notify(testkube.Event{
Expand All @@ -143,6 +143,22 @@ func TestWebhookListener_Notify(t *testing.T) {
assert.Equal(t, "", r.Error())

})

t.Run("send event disabled webhook", func(t *testing.T) {
t.Parallel()
// given

s := NewWebhookListener("l1", "http://baduri.badbadbad", "", testEventTypes, "", "", nil, true)

// when
r := s.Notify(testkube.Event{
Type_: testkube.EventStartTest,
TestExecution: exampleExecution(),
})

// then
assert.Equal(t, "", r.Error())
})
}

func exampleExecution() *testkube.Execution {
Expand Down
2 changes: 1 addition & 1 deletion pkg/event/kind/webhook/loader.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ func (r WebhooksLoader) Load() (listeners common.Listeners, err error) {

types := webhooks.MapEventArrayToCRDEvents(webhook.Spec.Events)
name := fmt.Sprintf("%s.%s", webhook.ObjectMeta.Namespace, webhook.ObjectMeta.Name)
listeners = append(listeners, NewWebhookListener(name, webhook.Spec.Uri, webhook.Spec.Selector, types, webhook.Spec.PayloadObjectField, payloadTemplate, webhook.Spec.Headers))
listeners = append(listeners, NewWebhookListener(name, webhook.Spec.Uri, webhook.Spec.Selector, types, webhook.Spec.PayloadObjectField, payloadTemplate, webhook.Spec.Headers, webhook.Spec.Disabled))
}

return listeners, nil
Expand Down
Loading

0 comments on commit fac4609

Please sign in to comment.