Skip to content

feat(providerconfig): add namespaced v1beta2 credentials surface - #408

Merged
markussiebert merged 1 commit into
masterfrom
feat/providerconfig-namespaced-secret-scope
Sep 30, 2026
Merged

markussiebert merged 1 commit into
masterfrom
feat/providerconfig-namespaced-secret-scope

Conversation

@markussiebert

Copy link
Copy Markdown
Collaborator

Summary

Adds gitlab.m.crossplane.io/v1beta2 ProviderConfig as the storage version of
the namespaced provider config, with a credentials surface that fits the
namespaced scope:

  • secretRef is a LocalSecretKeySelector, always resolved in the
    ProviderConfig's own namespace (no namespace field);
  • the filesystem and environment sources are not offered — they reference
    provider-global state (the provider pod's filesystem / process environment)
    and cannot be scoped to a single namespace without breaking isolation;
  • the credentials source enum is narrowed to None;Secret.

v1beta1 is retained (served, non-storage) and only gains a version-level
deprecation warning steering users to v1beta2. ClusterProviderConfig and
ProviderConfigUsage are unchanged (v1beta1 only) — they legitimately need a
namespaced secret selector / have no successor.

The provider resolves namespaced credentials via the v1beta2 ProviderConfig and
records the resolved namespace in Config.CredentialsSecretRef, so self-managed
token detection keeps comparing against the managed resource namespace.

Conversion

Strategy None (no webhook). The v1beta1 -> v1beta2 delta is only
dropped/optional fields and a narrowed enum, none of which needs data
transformation. Because v1beta2 is storage, fields it does not define
(secretRef.namespace, fs, env) are pruned when a v1beta1 object is written,
and the provider only ever reads v1beta2 — so namespaced credentials can no
longer be sourced across namespaces regardless of the version applied.

Backward compatibility

  • v1beta1 schema is unchanged except the deprecation marker; existing v1beta1
    manifests and stored objects keep working.
  • ClusterProviderConfig / ProviderConfigUsage CRDs are byte-identical to
    before.
  • No functional regression versus current master: secretRef.namespace was
    already ignored (own-namespace enforced) and fs/env were never
    implemented. The only visible change is that these already-ignored fields no
    longer persist on v1beta1 (pruned at v1beta2 storage), plus the deprecation
    warning.
  • Dropping v1beta1 in the future will require a storage-version migration; it is
    intentionally kept served here so nothing must migrate now.

Testing

  • go build ./..., go vet ./..., go test ./... pass; make generate
    produces no drift.
  • Live end-to-end against a real GitLab instance via a kwok cluster with the
    provider running out-of-cluster:
    • native v1beta2 owner-mode token create -> Ready;
    • v1beta2 self-managed detection (SelfManaged=True) and one forced
      self-rotation (token id changes, then settles, no rotation storm);
    • v1beta1 manifest -> None conversion -> Ready (deprecated namespace pruned
      at storage);
    • the v1beta1 deprecation warning is emitted on kubectl apply.

…credentials surface

Introduce gitlab.m.crossplane.io/v1beta2 ProviderConfig as the storage version
of the namespaced provider config, with a cleaned-up credentials surface:

  - secretRef is a LocalSecretKeySelector, always resolved in the
    ProviderConfig's own namespace (no namespace field);
  - the filesystem and environment sources, which reference provider-global
    state and cannot be scoped to a namespace, are not offered;
  - the credentials source enum is narrowed to None;Secret.

Conversion between v1beta1 and v1beta2 uses strategy None (no webhook): the
delta is only dropped/optional fields and a narrowed enum, none of which needs
data transformation. Because v1beta2 is the storage version, fields it does not
define (secretRef.namespace, fs, env) are pruned when a v1beta1 object is
written, and the provider only ever reads v1beta2 - so namespaced credentials
can no longer be sourced across namespaces regardless of the version applied.

v1beta1 is left unchanged except for a version-level deprecation warning
steering users to v1beta2; it stays served (non-storage). ClusterProviderConfig
and ProviderConfigUsage remain v1beta1 only.

The provider resolves namespaced credentials via the v1beta2 ProviderConfig
(GetTokenValueFromLocalSecret) and records the resolved namespace in
Config.CredentialsSecretRef so self-managed token detection keeps comparing
against the managed resource namespace.

Signed-off-by: Markus Siebert <markus.siebert@deutschebahn.com>
Assisted-by: Kiro:claude-opus-4.8
@markussiebert
markussiebert force-pushed the feat/providerconfig-namespaced-secret-scope branch from 0b2f359 to 1c03ab1 Compare September 4, 2026 14:37
@henrysachs

Copy link
Copy Markdown
Collaborator

Thanks @markussiebert — this addressed everything from the last pass, and in a couple of places more cleanly than I suggested.

LGTM

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

breaking-change Incompatible with previous behaviours

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants