external

3 min read

The external: list under onReconcile: declares HTTP calls to make before any resource group is processed. Results are injected into the template resolver under .external.<name>.* and are available in every subsequent template expression, when: condition, and status field.

Calls run sequentially in declaration order. The resolver is updated after each call — later calls can reference earlier results via template expressions in their url:, token:, or body: fields.

Wire format

operatorBox:
  onReconcile:
    external:
      - name: healthCheck
        url: "{{ .spec.serviceUrl }}/health"
        method: GET
        body: ""
        token: "$API_TOKEN"
        headers:
          X-Request-Source: orkestra
        timeout: 5s
        expectedStatus: 200
        continueOnError: false
        when:
          - field: status.phase
            notEquals: "Ready"
        sleep: ""

Fields

FieldRequiredDefaultDescription
nameyesResult identifier. Must be a valid Go identifier (camelCase). Used as {{ .external.<name>.status }}.
urlyesEndpoint URL. Template expressions are resolved against the current CR.
methodnoGETHTTP method: GET, POST, PUT, PATCH, DELETE.
bodyno""Request body. Template expressions supported. Sets Content-Type: application/json when non-empty.
tokenno""Bearer token. Use $ENV_VAR syntax — expanded via os.ExpandEnv. Never put raw secrets here.
headersno{}Additional HTTP headers as map[string]string.
timeoutno10sPer-call deadline. Go duration: "5s", "1m", "500ms".
expectedStatusno0When set: any other status code is a failure. When 0: 4xx/5xx is a failure, 2xx succeeds.
continueOnErrornofalsefalse: failure halts reconcile, writes Ready=False. true: failure logged, reconcile continues.
whenno[]AND gate conditions. If any fail, the call is skipped and .called = "false".
anyOfno[]OR gate conditions. At least one must pass. Combined with when: using AND semantics.
sleepno""Delay before this call. Go duration. For development and sequencing async side-effects — not for production rate limiting.

Result context

FieldDescription
.external.<name>.statusHTTP status code string ("200", "503"). Empty on pre-response failure.
.external.<name>.bodyFirst 4096 bytes of response body.
.external.<name>.errorError message on failure; "" on success.
.external.<name>.called"true" when the call ran; "false" when skipped by when:/anyOf:.

Access pattern

# Gate a resource on a successful call
deployments:
  - name: "{{ .metadata.name }}"
    when:
      - field: external.healthCheck.status
        equals: "200"

# Embed the body in a ConfigMap
configMaps:
  - name: "{{ .metadata.name }}-config"
    data:
      config.json: "{{ .external.appConfig.body }}"

# Use a previous call's result in the next call's token
external:
  - name: tokenFetch
    url: "{{ .spec.authUrl }}/token"
    method: POST

  - name: protectedCall
    url: "{{ .spec.serviceUrl }}/resource"
    token: "{{ .external.tokenFetch.body }}"

Constraints

  • Body cap: 4096 bytes — larger responses are truncated silently.
  • Sequential: no parallel execution; declaration order is execution order.
  • camelCase names: hyphens in name break template access.
  • Every reconcile: calls run on every reconcile unless gated by when:.
  • Token expansion: $VAR and ${VAR} only — template expressions in token: resolve first, then env expansion applies.

Example

operatorBox:
  onReconcile:
    external:
      - name: healthCheck
        url: "{{ .spec.serviceUrl }}/health"
        expectedStatus: 200
        continueOnError: true
        timeout: 5s

    deployments:
      - name: "{{ .metadata.name }}"
        image: "{{ .spec.image }}"
        when:
          - field: external.healthCheck.status
            equals: "200"

  status:
    fields:
      - path: phase
        value: "Degraded"
        when:
          - field: external.healthCheck.status
            notEquals: "200"
      - path: phase
        value: "Ready"
        when:
          - field: external.healthCheck.status
            equals: "200"
          - field: "{{ allReplicasReady .children.deployment }}"
            equals: "true"
      - path: lastHealthCheck
        value: "{{ .external.healthCheck.status }}"

See the External concept doc for patterns and best practices.