crossplane / crossplane/cli

xpkg build: GoTemplate function input kind field buried after large block scalar due to JSON key reordering

Open
#344 0 comments 0 reactions 0 assignees View on GitHub
Dominant language
Go
Stars
19
Forks
31
Avg merge
3d 4h
Merged PRs (30d)
52

Description

## Describe the bug

\`crossplane xpkg build\` reorders fields in embedded \`RawExtension\` objects alphabetically (via \`sigs.k8s.io/yaml.YAMLToJSON()\`) during YAML serialization. For a \`function-go-templating\` pipeline step input, this buries \`kind: GoTemplate\` **after** the entire \`inline.template\` block scalar — which can be hundreds or thousands of lines long.

Some YAML parsers and the Crossplane package reader fail to correctly handle a \`kind\` field that appears after a very large literal block scalar, causing installed ConfigurationRevisions to fail with:

```
spec.pipeline[N].input.kind: Required value
```

This became visible with Crossplane v2.2.0, which added strict validation requiring \`kind\` in all pipeline step inputs. See crossplane/crossplane#7819.

## Root cause

The \`encode()\` function in \`crossplane-runtime/pkg/xpkg/build.go\` re-serializes all package objects through the Kubernetes JSON serializer:

1. Source YAML is parsed → stored as \`*v1.Composition\` with \`Input.Raw\` = JSON bytes
2. \`sigs.k8s.io/yaml.YAMLToJSON()\` converts the YAML map to JSON with **alphabetically sorted keys**
3. Original YAML field order: \`apiVersion → kind → source → inline\`
4. JSON (and final YAML) field order: \`apiVersion → inline → kind → source\`

Result in \`package.yaml\`:

\`\`\`yaml
input:
apiVersion: gotemplating.fn.crossplane.io/v1beta1
inline:
template: |
{{- ... hundreds of lines of go template ... }}
kind: GoTemplate # ← buried after the block scalar
source: Inline
\`\`\`

## Steps to reproduce

1. Create a Composition with a \`function-go-templating\` step whose \`inline.template\` is >100 lines
2. Run \`crossplane xpkg build\`
3. Extract \`package.yaml\` from the built \`.xpkg\`:
```
tar xf package.xpkg
tar xzf .tar.gz
```
4. Search for \`kind: GoTemplate\` — it is present but appears **after** the entire template block, not next to \`apiVersion:\`
5. Install the package on a Crossplane v2.2.0+ cluster → ConfigurationRevision fails

## Expected behaviour

\`kind: GoTemplate\` appears adjacent to \`apiVersion: gotemplating.fn.crossplane.io/v1beta1\` in the serialized \`package.yaml\`, matching the source file and ensuring reliable parsing.

## Suggested fix

In \`crossplane-runtime/pkg/xpkg/build.go\`, modify \`encode()\` to re-order keys in \`RawExtension.Raw\` JSON so that TypeMeta fields (\`apiVersion\`, \`kind\`) appear first before emitting. Concretely: after marshaling each object to JSON, walk any embedded \`RawExtension\` fields and produce a new JSON object with \`apiVersion\`/\`kind\` promoted to the front.

An alternative is to add a normalization pass in the \`xpkg build\` command (\`cmd/crossplane/xpkg/build.go\`) after \`c.builder.Build()\` — extract \`package.yaml\` from the built image, re-insert \`kind\`/\`apiVersion\` immediately after each other in embedded resource blocks, and rebuild the layer.

## Workaround

Post-process the built \`.xpkg\` to insert \`kind: GoTemplate\` immediately after each \`apiVersion: gotemplating.fn.crossplane.io/v1beta1\` line. This is what the affected package repo currently does as a stop-gap.

## Related

- crossplane/crossplane#7819 — Crossplane v2.2.0 added the \`kind\` validation requirement that surfaces this bug

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.