v0.7.13 — External calls at admission and reconcile time, protocol clients [UNRELEASED]
validation.external and mutation.external
External HTTP calls can now be declared under validation: and mutation: in addition to onReconcile:. Calls declared here fire before the corresponding rule loop — at admission webhook time and, by default, at every reconcile.
validation:
external:
- name: healthCheck
url: "{{ .spec.healthCheckUrl }}/health"
expectedStatus: 200
continueOnError: true
fires:
reconcile: false # admission-only
rules:
- field: "{{ .external.healthCheck.status }}"
equals: "200"
action: deny
message: "health check failed — CR rejected"
fires.reconcile: false marks a call as admission-only. The reconciler skips it on resyncs; the webhook always runs it. When omitted, the call fires at both sites.
Results from validation.external and mutation.external are now propagated back into the main reconcile resolver — status fields, resource templates, and subsequent steps can reference .external.<name>.* just like onReconcile.external results. This holds even on the denial path: when validation denies, status is patched with the enriched resolver, so phase and error fields reflect the external call outcome.
The external call runner is now in pkg/external — shared by the reconciler and the gateway webhook.
include: in external call lists
All external: lists now support per-item include: entries. An item with include: set is replaced in-place by the calls: list from the referenced file. Works in onReconcile.external, onCreate.external, hooks.external, validation.external, and mutation.external.
onReconcile:
external:
- include: ./shared/auth-calls.yaml
- name: healthCheck
url: "{{ .spec.serviceUrl }}/health"
./shared/auth-calls.yaml:
calls:
- name: tokenFetch
url: "{{ .spec.authUrl }}/token"
method: POST
Protocol clients
external: blocks now support a protocol: field selecting a native client instead of HTTP.
| Protocol | protocol: value | url: | query: |
|---|---|---|---|
| HTTP (default) | http or omit | any URL | not used |
| Prometheus | prometheus | Prometheus base URL | PromQL expression |
| Redis | redis | redis://host:port | Redis command (PING, LLEN jobs, …) |
| PostgreSQL | postgres | connection string | SQL query |
| MongoDB | mongo | mongodb://host:port | db.collection [filter] |
| Kafka | kafka | kafka://broker:9092 | group/topic for lag, @topic for metadata |
All protocols resolve before deployment evaluation and return results at external.<name>.*. continueOnError: true is supported on all of them.
Prometheus notes
Seven template notes for working with Prometheus external results:
promValue— extract the first scalar value from a Prometheus instant query resultpromSum/promMax— aggregate across multiple seriespromAboveThreshold/promBelowThreshold— boolean threshold checks forwhen:conditionspromSeriesCount— number of series returnedpromLabelValues— extract a label value from the first series
exists and notExists e2e assertions
- name: field is populated
kubectl:
get:
- kind: MyApp
name: my-app
namespace: default
field: .status.connectionCount
exists: true
exists: true fails when the field is empty or missing. notExists: true fails when the field is present. Both work in all e2e assertion blocks (kubectl, exec, http, expect).
Runtime RBAC — automatic secrets get for auth.secretRef
When any external: block in a Katalog uses auth.secretRef, the generated runtime RBAC now automatically includes a secrets get rule. No manual RBAC annotation required.
GET /api/v1/query endpoint
The runtime now exposes GET /api/v1/query?expr=<promql> — a passthrough Prometheus query endpoint backed by the operator’s own metrics. Useful for dashboards and autoscaler signals without a separate Prometheus instance.