Bug Description
When using observed.composite.resource.spec in a Go template, fields that are present in the XR may not be available on the first reconcile cycle. This causes {{- with .fieldName }} blocks to silently skip, resulting in composed resources never being created.
Root Cause
On the first reconcile after XR creation, observed.composite.resource.spec may not contain all user-specified fields. The desired.composite.resource.spec always has the full spec. When combined with resources that fail quickly (triggering the circuit breaker), the template never gets a second chance to execute with the now-populated observed state.
Environment
- function-go-templating: v0.11.0
- Crossplane: v1.19.x (latest stable)
- Cluster: Kind (local development)
Steps to Reproduce
- Create a Composition with an inline template that uses
{{- with .observed.composite.resource.spec -}} and accesses multiple sub-objects via {{- with .service }}, {{- with .autoscaling }}, etc.
- Include a resource that will fail immediately (e.g., AppAutoscaling Policy that depends on a Target that doesn't exist yet)
- Create an XR with all fields populated
Expected Behavior
All with blocks should execute on every reconcile since the XR spec is fully populated.
Actual Behavior
- First reconcile: some
with blocks don't execute because observed.composite.resource.spec is missing those fields
- The failing resource triggers the circuit breaker
- Subsequent reconciles are throttled — the template never re-executes with the complete observed state
- Result: some composed resources are never created
Evidence
- Decoder test:
yaml.NewYAMLOrJSONDecoder correctly decodes 6+ documents from well-formed YAML
convertToMap uses protojson.Marshal(req) with default options — proto3 omits zero/empty fields
- When the same template uses only 3 resources (no failing ones), all fields are available on the second reconcile (circuit breaker doesn't activate)
- Switching from
observed to desired composite spec resolves the issue completely
Workaround
Use desired with fallback to observed:
{{- $spec := (.desired.composite.resource.spec | default .observed.composite.resource.spec) -}}
{{- with $spec -}}
...
{{- end -}}
This ensures the full user-specified spec is always available regardless of reconcile timing.
Recommendation
Consider documenting that desired.composite.resource.spec should be preferred over observed.composite.resource.spec for resource creation templates, since desired always reflects the user's intent while observed may lag behind. Alternatively, the function could pre-merge desired and observed composite state before template execution.
Bug Description
When using
observed.composite.resource.specin a Go template, fields that are present in the XR may not be available on the first reconcile cycle. This causes{{- with .fieldName }}blocks to silently skip, resulting in composed resources never being created.Root Cause
On the first reconcile after XR creation,
observed.composite.resource.specmay not contain all user-specified fields. Thedesired.composite.resource.specalways has the full spec. When combined with resources that fail quickly (triggering the circuit breaker), the template never gets a second chance to execute with the now-populated observed state.Environment
Steps to Reproduce
{{- with .observed.composite.resource.spec -}}and accesses multiple sub-objects via{{- with .service }},{{- with .autoscaling }}, etc.Expected Behavior
All
withblocks should execute on every reconcile since the XR spec is fully populated.Actual Behavior
withblocks don't execute becauseobserved.composite.resource.specis missing those fieldsEvidence
yaml.NewYAMLOrJSONDecodercorrectly decodes 6+ documents from well-formed YAMLconvertToMapusesprotojson.Marshal(req)with default options — proto3 omits zero/empty fieldsobservedtodesiredcomposite spec resolves the issue completelyWorkaround
Use
desiredwith fallback toobserved:{{- $spec := (.desired.composite.resource.spec | default .observed.composite.resource.spec) -}} {{- with $spec -}} ... {{- end -}}This ensures the full user-specified spec is always available regardless of reconcile timing.
Recommendation
Consider documenting that
desired.composite.resource.specshould be preferred overobserved.composite.resource.specfor resource creation templates, sincedesiredalways reflects the user's intent whileobservedmay lag behind. Alternatively, the function could pre-merge desired and observed composite state before template execution.