crossplane / crossplane/crossplane
Packages with a name longer than 63 characters can never be installed: revision name is truncated but the pkg.crossplane.io/package label is not
- Dominant language
- Go
- Stars
- 12.1k
- Forks
- 1.3k
- Avg merge
- 1d 6h
- Merged PRs (30d)
- 46
Description
### What happened?
A `Provider`, `Function` or `Configuration` whose `metadata.name` is longer than
63 characters can never be installed. The package object is accepted, but the
package manager fails on every reconcile and no `*Revision` is ever created, so
the package never becomes healthy and anything depending on it never reconciles.
The cause is an inconsistency in how the parent package's name is handled when
creating the revision. The revision's **name** is truncated to fit a DNS label,
but the parent name is then copied **verbatim** into the revision's
`pkg.crossplane.io/package` label, where the 63-character limit on label values
applies and nothing truncates it. The API server rejects the create.
In `internal/controller/pkg/manager/reconciler.go`:
```go
// L363 — parent name truncated to 50 chars + 12 chars of digest
revisionName := xpkg.FriendlyID(p.GetName(), revisionID)
...
// L452 — parent name used untruncated as a label value
pr.SetLabels(map[string]string{v1.LabelParentPackage: p.GetName()})
```
where `FriendlyID` (crossplane-runtime `pkg/xpkg/name.go`) guarantees ≤63:
```go
func FriendlyID(name, hash string) string {
return ToDNSLabel(strings.Join([]string{truncate(name, 50), truncate(hash, 12)}, "-"))
}
```
The same untruncated value is also used as a label selector when listing
existing revisions at L316, so the lookup path has the same latent problem.
Expected: either the package name is validated up front with a clear error
saying the name is too long, or the label value is derived consistently with the
revision name so long names degrade rather than deadlock. Today the failure is
silent at apply time and the eventual error names a label value the user never
wrote, which makes it hard to trace back to "your package name is too long".
Two things make this more than a theoretical limit:
1. The error message doesn't say the package name is too long. It reports an
invalid label value on a `FunctionRevision` whose name has already been
truncated to something the user never typed, so the 63-character budget being
blown by `metadata.name` on the parent isn't obvious.
2. Package names are not always hand-written. Tooling derives them — the Upbound
`up` CLI derives an embedded function's `Function` name from the project's
registry path plus the function directory name, so users with a long registry
path can blow the limit without ever typing a name. That side is filed
separately as https://github.com/upbound/upbound/issues/48; this issue is
about Crossplane accepting a package it can provably never install.
On the fix: naively truncating the label value would make it consistent with the
revision name, but it would also make the label ambiguous for two parents
sharing a 63-character prefix, which matters because L316 uses it as a selector
to find a package's own revisions. Validating the package name length at
admission (with an error that names `metadata.name`) may be the safer option, or
a `kubebuilder` validation on the package types. Happy to send a PR in whichever
direction you prefer.
### How can we reproduce it?
Apply a `Function` (or `Provider`, or `Configuration`) whose name is longer than
63 characters, pointing at any valid package. 72 characters here:
```yaml
apiVersion: pkg.crossplane.io/v1
kind: Function
metadata:
name: svc-docker-local-dev-crossplane-kafkaconnect-aws-xrdcompose-kafkaconnect
spec:
package: xpkg.upbound.io/crossplane-contrib/function-patch-and-transform:v0.8.2
```
The object is created but never installs, and no `FunctionRevision` is created:
```console
$ kubectl get functions.pkg.crossplane.io
NAME INSTALLED HEALTHY PACKAGE
svc-docker-local-dev-crossplane-kafkaconnect-aws-xrdcompose-kafkaconnect xpkg.upbound.io/crossplane-contrib/function-patch-and-transform:v0.8.2
$ kubectl get functionrevisions.pkg.crossplane.io
NAME HEALTHY RUNTIME IMAGE STATE AGE
# empty — no revision was created
$ kubectl get events -A --sort-by=.lastTimestamp | tail -1
default 1s Warning InstallPackageRevision function/svc-docker-local-dev-crossplane-kafkaconnect-aws-xrdcompose-kafkaconnect cannot apply package revision: cannot create object: FunctionRevision.pkg.crossplane.io "svc-docker-local-dev-crossplane-kafkaconnect-aws-x-c05a9add149b" is invalid: metadata.labels: Invalid value: "svc-docker-local-dev-crossplane-kafkaconnect-aws-xrdcompose-kafkaconnect": must be no more than 63 characters
```
Note in that error that the revision *name* was truncated correctly to 63
characters (`...aws-x-c05a9add149b`) while the *label value* was not.
Truncating only the name to 63 characters, with the same package, installs
successfully — so nothing about the package contents is involved, only the length
of `metadata.name`:
```console
$ kubectl get functions.pkg.crossplane.io
NAME INSTALLED HEALTHY PACKAGE
svc-docker-local-dev-crossplane-kafkaconnect-aws-xrdcompose-kaf True True xpkg.upbound.io/crossplane-contrib/function-patch-and-transform:v0.8.2
$ kubectl get functionrevisions.pkg.crossplane.io -o custom-columns='NAME:.metadata.name,PARENT_LABEL:.metadata.labels.pkg\.crossplane\.io/package'
NAME PARENT_LABEL
svc-docker-local-dev-crossplane-kafkaconnect-aws-x-c05a9add149b svc-docker-local-dev-crossplane-kafkaconnect-aws-xrdcompose-kaf
```
That last line shows the mechanism: the revision name is truncated to 63 while
the label holds the parent name in full. At 63 characters the label is legal and
everything works; at 64 the create is rejected and the package is stuck forever.
### What environment did it happen in?
Crossplane version: v2.3.4-up.2 (UXP). The relevant code is unchanged on
`crossplane/crossplane` `main` at the time of writing — see the `reconciler.go`
line references above — so this should not be distribution-specific, but I have
only reproduced it on UXP.
* Kubernetes version: v1.34.0
* Kubernetes distribution: KinD
* OS: macOS 26.6.2 (arm64)
Contributor guide
Research direction
Start in `internal/controller/pkg/manager/reconciler.go`, reading the revision lookup at L316 and revision creation around L363 and L452; compare how the parent name is used in the selector and label with `FriendlyID` in `crossplane-runtime/pkg/xpkg/name.go`. Reproduce with the 72-character `Function` name and verify a chosen fix handles long names without ambiguous revision selection. Done when long names fail clearly at validation or install successfully, with coverage for the relevant behavior.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- go, kubernetes
- Domain
- backend, distributed-systems
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 55/100