From 1e6929cab6733b40baa5032aca1130d1490eba27 Mon Sep 17 00:00:00 2001 From: Kevin McDermott Date: Sun, 13 Oct 2024 15:27:57 +0100 Subject: [PATCH] Add request.body in CEL notification filtering This moves the JSON body to request.body from request to allow for future expansion with the headers. Add documentation Signed-off-by: Kevin McDermott --- docs/spec/v1/receivers.md | 49 ++++++++++++++++++++++++ internal/server/cel.go | 5 ++- internal/server/receiver_handler_test.go | 4 +- 3 files changed, 54 insertions(+), 4 deletions(-) diff --git a/docs/spec/v1/receivers.md b/docs/spec/v1/receivers.md index 08217738c..b685a2633 100644 --- a/docs/spec/v1/receivers.md +++ b/docs/spec/v1/receivers.md @@ -700,6 +700,55 @@ resources: **Note:** Cross-namespace references [can be disabled for security reasons](#disabling-cross-namespace-selectors). +#### Filtering reconciled objects with CEL + +To filter the resources that are reconciled you can use [Common Expression Language (CEL)](https://cel.dev/). + +For example to trigger `ImageRepositories` on notifications from [Google Artifact Regisry](https://cloud.google.com/artifact-registry/docs/configure-notifications#examples) you can define a receiver. + +```yaml +apiVersion: notification.toolkit.fluxcd.io/v1 +kind: Receiver +metadata: + name: gar-receiver + namespace: apps +spec: + type: gcr + secretRef: + name: flux-gar-token + resources: + - apiVersion: image.toolkit.fluxcd.io/v1beta2 + kind: ImageRepository + name: "*" + matchLabels: + registry: gar +``` + +This will trigger the reconciliation of all `ImageRepositories` with matching labels `registry: gar`, but if you want to only notify `ImageRepository` resources that are referenced from the incoming hook you can use CEL to filter the resources. + +```yaml +apiVersion: notification.toolkit.fluxcd.io/v1 +kind: Receiver +metadata: + name: gar-receiver + namespace: apps +spec: + type: gcr + secretRef: + name: flux-gar-token + resources: + - apiVersion: image.toolkit.fluxcd.io/v1beta2 + kind: ImageRepository + name: "*" + matchLabels: + registry: gar + resourceFilter: 'request.tag.contains(resource.metadata.name)' +``` + +To reduce the number of `ImageRepositories` that are reconciled, you can filter them with a CEL expression. + +``` + ### Secret reference `.spec.secretRef.name` is a required field to specify a name reference to a diff --git a/internal/server/cel.go b/internal/server/cel.go index f3ee26215..09494ea05 100644 --- a/internal/server/cel.go +++ b/internal/server/cel.go @@ -53,7 +53,9 @@ func newCELEvaluator(expr string, req *http.Request) (resourcePredicate, error) out, _, err := prg.Eval(map[string]any{ "resource": data, - "request": body, + "request": map[string]any{ + "body": body, + }, }) if err != nil { return nil, fmt.Errorf("expression %v failed to evaluate: %w", expr, err) @@ -74,7 +76,6 @@ func makeCELEnv() (*cel.Env, error) { mapStrDyn := decls.NewMapType(decls.String, decls.Dyn) return cel.NewEnv( celext.Strings(), - celext.Encoders(), notifications(), cel.Declarations( decls.NewVar("resource", mapStrDyn), diff --git a/internal/server/receiver_handler_test.go b/internal/server/receiver_handler_test.go index 8908749b6..82664bbf4 100644 --- a/internal/server/receiver_handler_test.go +++ b/internal/server/receiver_handler_test.go @@ -789,7 +789,7 @@ func Test_handlePayload(t *testing.T) { }, }, }, - ResourceFilter: `has(resource.metadata.annotations) && request.tag.split('/').last().split(":").first() == resource.metadata.annotations['update-image']`, + ResourceFilter: `has(resource.metadata.annotations) && request.body.tag.split('/').last().split(":").first() == resource.metadata.annotations['update-image']`, }, Status: apiv1.ReceiverStatus{ WebhookPath: apiv1.ReceiverWebhookPath, @@ -878,7 +878,7 @@ func Test_handlePayload(t *testing.T) { Name: "test-resource", }, }, - ResourceFilter: `has(resource.metadata.annotations) && request.tag.split('/').last().split(":").first() == resource.metadata.annotations['update-image']`, + ResourceFilter: `has(resource.metadata.annotations) && request.body.tag.split('/').last().split(":").first() == resource.metadata.annotations['update-image']`, }, Status: apiv1.ReceiverStatus{ WebhookPath: apiv1.ReceiverWebhookPath,