v0.7.13 — External protocols, serve layer, new condition operators, two breaking changes

2 min read

validation.external and mutation.external

External calls can now fire at admission time, not only reconcile time:

validation:
  external:
    - name: healthCheck
      url: "{{ .spec.serviceUrl }}/health"
      fires:
        reconcile: false   # admission-only
  rules:
    - field: "{{ .external.healthCheck.status }}"
      equals: "200"
      action: deny
      message: "health check failed"

Results propagate into all template contexts, including status fields and resource templates.

External protocol clients

external: blocks support native protocol clients alongside HTTP:

Protocolprotocol: value
Prometheusprometheus
Redisredis
PostgreSQLpostgres
MongoDBmongo
Kafkakafka

Results available at external.<name>.* in all template contexts.

New condition operators

gte, lte, between, notBetween, notIn, notContains, regex, notPrefix, notSuffix — available in when:, or:, and validation.rules.

Fixes: gt/lt in validation rules were accidentally inclusive; operator: in was never evaluated in validation rules (silently always passed).

operator: unique

Check that a field value is unique across all CRs of this CRD. Authoritative at reconcile time; best-effort early rejection at admission time.

Breaking: labels/annotations move to native map syntax

# before
labels:
  - key: app
    value: "{{ .metadata.name }}"

# after
labels:
  app: "{{ .metadata.name }}"

Applies to selector:, labelSelector:, and fieldSelector: too.

Breaking: envFrom.secretRef/configMapRef move to struct

# before
envFrom:
  secretRef: [myapp-creds]

# after
envFrom:
  secretRef:
    - name: myapp-creds
      prefix: "DB_"

serve labels/annotations — expose metadata as serve fields

serve:
  labels:
    team:
      label: "Team"
      required: true

Written to metadata.labels/metadata.annotations on apply. required: true synthesizes a server-side exists validation rule; type: enum synthesizes an in rule.

serve.fields.path — nested spec paths

path: app.resources.cpu maps a flat serve field to a nested spec location.

Names the serve field a validation rule concerns, so clients can highlight the offending form field by name:

validation:
  rules:
    - field: '{{ getLabel . "team" }}'
      link: team
      operator: exists

kubectl.apply — admission rejection tests

kubectl:
  apply:
    - file: ./cr-invalid.yaml
      exitCode: 1
      outputContains: "spec.domain must be unique"

Assert that an apply should be rejected, and what the message contains.