Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Filtering for incoming webhooks #491

Open
tun0 opened this issue Mar 9, 2023 · 6 comments · May be fixed by #948
Open

Filtering for incoming webhooks #491

tun0 opened this issue Mar 9, 2023 · 6 comments · May be fixed by #948
Labels
area/receiver Webhook receiver related issues and PRs enhancement New feature or request

Comments

@tun0
Copy link

tun0 commented Mar 9, 2023

A more generic approach similar to the support for filtering on Events for GitHub, etc.

For example GCR/GAR sends a pretty simple, but also useful payload:

{
  "action":"INSERT",
  "digest":"us-east1-docker.pkg.dev/my-project/my-repo/hello-world@sha256:6ec128e26cd5...",
  "tag":"us-east1-docker.pkg.dev/my-project/my-repo/hello-world:1.1"
}

Ref: https://cloud.google.com/artifact-registry/docs/configure-notifications#examples

It would be nice to be able to map the digest to a ImageRepository to notify (either automatically or explicitly configured). This could potentially reduce the load within and caused by Flux drastically.

The current alternative (to reduce the load) is to configure separate incoming webhooks on both GCR/GAR's and Flux' side. Which is rather cumbersome.

@makkes
Copy link
Member

makkes commented Mar 13, 2023

I'm not sure I'm following what you're requesting. Would you mind describing the envisioned API changes and/or workflow you're aiming at, please?

@tun0
Copy link
Author

tun0 commented Mar 13, 2023

Currently we have a single receiver to consume notifications from our GCR/GAR. In this receiver spec.resources lists 34 ImageRepository references. Whenever a notification arrives from GCR or GAR, all 34 image repositories get annotated and scanned for new images. What I'm proposing is instead of triggering a scan for all 34, only trigger those image repositories that match against the digestpart of the notification payload (without the @... part).

@lucioveloso
Copy link

I will go even deep. The primary concern isn't just that all images are annotated for reconciliation. The core issue is the reconciliation process itself. I have thousands of images, and with each new webhook received, there's a scan of all image tags in Harbor. This results in significant resource consumption in Harbor instance.

@stefanprodan
Copy link
Member

Each provider has a unique payload schema, we'll need to get all those schemas as dependencies (does al of them have Go SDKs?). After that we'll need to write a custom parser for each one, to be able to extract some string that may or may not match the Flux resource name listed under .spec.resources. The Receiver API is common to all current and future Flux resource types which makes this issue about container images so much larger is scope.

@bigkevmcd
Copy link
Contributor

Could something like CEL be used to process the incoming request body (assuming JSON).

Taking the example above...

{
  "action":"INSERT",
  "digest":"us-east1-docker.pkg.dev/my-project/my-repo/hello-world@sha256:6ec128e26cd5...",
  "tag":"us-east1-docker.pkg.dev/my-project/my-repo/hello-world:1.1"
}

To extract the image from this, and trigger an update of a GitRepository:

---
apiVersion: notification.toolkit.fluxcd.io/v1
kind: Receiver
metadata:
  name: generic-receiver
  namespace: default
spec:
  type: generic
  secretRef:
    name: webhook-token
  resourceExpressions:
    - '{"name": body.tag.split('/').last().split(":").first(), "kind": "GitRepository", "apiVersion": "source.toolkit.fluxcd.io/v1"}'

The expression there extracts the tag field from the JSON body, and then splits it apart to extract the hello-world part of the image reference, as with other receivers, it will default to the same namespace so it would trigger an update of GitRepository default/hello-world.

Multiple resourceExpressions could be provided, with the result being that all the resources are notified.

I have a working version of this and the tests include these expressions...

(assuming a request body with this JSON)

{
    "image": "test-resource:1.2.1",
    "resources": [
        "test-resource-3",
        "test-resource-4"
    ]
}

The test uses these expressions:

{
    // A single CEL value with static keys
    `{"name": "test-resource-1", "kind": "Receiver", "apiVersion": "notification.toolkit.fluxcd.io/v1"}`,
    // An array of CEL values with a dynamic key for the name
    `[{"name": body.image.split(':',2)[0] + '-2', "namespace": "tested", "kind": "Receiver", "apiVersion": "notification.toolkit.fluxcd.io/v1"}]`,
    // A dynamically created array of CEL values with the
    // map macro and generating the name dynamically
    `body.resources.map(r, {"name": r, "kind": "Receiver", "apiVersion": "notification.toolkit.fluxcd.io/v1"})`,
},

@stefanprodan
Copy link
Member

stefanprodan commented Oct 10, 2024

After talking to @bigkevmcd we came up with the following specification: we'll add an optional field .spec.resourceFilter which takes a CEL expression which is evaluated against the list of Flux object specified in .spec.resources. The CEL expression has access to the request JSON payload and to the metadata name, namespace, annotations and labels of the resources.

Example

We define a Receiver that selects all the Flux ImageRepositories matching a label:

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)'

In the apps namespace we have the following ImageRepositories all labeled with registry: gar:

  • app1
  • app2
  • app3

When GAR sends the following payload:

{
  "action":"INSERT",
  "digest":"us-east1-docker.pkg.dev/my-project/my-repo/app1@sha256:6ec128e26cd5...",
  "tag":"us-east1-docker.pkg.dev/my-project/my-repo/app1:v1.2.3"
}

The controller selects all the ImageRepositories that match the label, then it applies the CEL filter, and finally will trigger the reconciliation only for app1.

Besides the Flux resource name, users can decide which objects to be reconciled using annotations, for example:

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.annotations["image"])'

@stefanprodan stefanprodan added enhancement New feature or request area/receiver Webhook receiver related issues and PRs labels Oct 11, 2024
@stefanprodan stefanprodan linked a pull request Oct 11, 2024 that will close this issue
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area/receiver Webhook receiver related issues and PRs enhancement New feature or request
Projects
None yet
Development

Successfully merging a pull request may close this issue.

5 participants