Skip to content

Generated controllers never set WithDeterministicExternalName, blocking recovery from external-create-pending #681

Description

@polds

What happened?

Generated controllers build the reconciler options in pkg/pipeline/templates/controller.go.tmpl without managed.WithDeterministicExternalName, so deterministicExternalName is false for every upjet resource.

When a create is interrupted after crossplane.io/external-create-pending is set but before -succeeded/-failed is recorded (for example the provider pod restarts during an async create), crossplane-runtime treats the create as incomplete. With deterministicExternalName false it refuses to retry, and the MR is stuck indefinitely on:

cannot determine creation result - remove the crossplane.io/external-create-pending annotation if it is safe to proceed

crossplane-runtime already handles the safe case: when deterministicExternalName is true it logs "proceeding due to deterministic external name" and continues. Resources with a user-supplied external name (everything except config.IdentifierFromProvider) are deterministic, so the retry is safe and would not leak a resource. Because upjet never opts in, the only recovery is deleting the annotation by hand, which is not viable for GitOps-managed clusters.

How can we reproduce it?

  1. Pick a resource with a user-supplied external name (config.NameAsIdentifier or config.TemplatedStringAsIdentifier).
  2. Create an instance, then restart the provider pod while the async create is in flight, so external-create-pending ends up as the newest of the three external-create-* annotations.
  3. The MR stays Synced=False with "cannot determine creation result" and never retries until the annotation is removed manually.

Suggested fix

Add managed.WithDeterministicExternalName(deterministic) to the opts in controller.go.tmpl, with deterministic derived from the resource config: false for config.IdentifierFromProvider (provider-assigned id), true for user-supplied external names.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions