Webhook

Let the source ask for an immediate discovery run

A TargetSource polls its source on interval. When the source can call out on change, for example a NetBox webhook, it can ask the operator to run now instead of waiting.

The webhook is notify-only. The request body carries no target data and is not parsed; it is only an input to signature verification. A successful call annotates the TargetSource, and the controller performs an ordinary full run against the source. That keeps one desired set, always read from the source, so a pushed device can never be undone by the next poll.

Configuration

apiVersion: operator.gnmic.dev/v1alpha1
kind: TargetSource
metadata:
  name: netbox
  namespace: gnmic-system
spec:
  source:
    type: HTTP
    http:
      url: https://netbox.example.com/api/dcim/devices/
      auth:
        token:
          scheme: Token
          secretRef: { name: netbox-api-token, key: token }
      mapping:
        items: self.results
        address: item.primary_ip4.address.split('/')[0]
      pagination:
        nextField: self.next
  target:
    profile: netbox-device
  webhook:
    enabled: true
    debounce: 5s
    auth:
      bearer:
        secretRef: { name: gnmic-api-auth, key: bearer-token }
      signature:
        secretRef: { name: gnmic-signature, key: signature }
        header: X-Hook-Signature
        algorithm: sha256
FieldTypeDefaultDescription
enabledboolfalseTurn the endpoint on for this TargetSource.
authobjectrequired when enabledAt least one of bearer, signature. When both are set, both must pass.
auth.bearer.secretRefSecret keyCompared in constant time against Authorization: Bearer <token>.
auth.signature.secretRefSecret keyHMAC key.
auth.signature.headerstringX-Hook-SignatureHeader carrying the hex signature, optionally prefixed with sha256= or sha512=.
auth.signature.algorithmstringsha256sha256 or sha512.
debounceduration5sCalls within this window of the previous one are acknowledged without scheduling another run.

Authentication is mandatory: the API server rejects enabled: true without auth. An unauthenticated endpoint would let anyone who can reach the API port drive fetches against the source.

Endpoint

The operator API listens on the address given by --api-bind-address (Helm: api.port, default 8082). It runs on every replica, so a Service in front of several replicas works.

POST /api/v1/namespaces/{namespace}/targetsources/{name}/refresh
ResponseMeaning
202 AcceptedA run was requested. Body: {"requestedAt": "...", "debounced": false}.
200 OKA run was requested less than debounce ago; nothing new was scheduled. Body: {"requestedAt": "...", "debounced": true}.
401 UnauthorizedAuthentication failed.
404 Not FoundThe TargetSource does not exist or has no webhook enabled. The two are indistinguishable on purpose.

Example with both methods, signing an arbitrary body:

BODY='{"event":"device.updated"}'
SIG=$(printf '%s' "$BODY" | openssl dgst -sha256 -hmac "$SIGNATURE_SECRET" | awk '{print $NF}')
curl -X POST "http://gnmic-controller-manager-api.gnmic-system.svc:8082/api/v1/namespaces/gnmic-system/targetsources/netbox/refresh" \
  -H "Authorization: Bearer $BEARER_TOKEN" \
  -H "X-Hook-Signature: sha256=$SIG" \
  -d "$BODY"

For a NetBox walkthrough see NetBox webhook.

What changed from push mode

Earlier versions accepted target data on a POST .../applyTargets route and applied it directly. That route is gone. A pushed device was applied and then deleted by the next poll, because the poll’s result did not contain it, and the endpoint only worked on the leader replica. A source that has nothing to poll should write a ConfigMap or serve the native list over HTTP.