v0.15.0
P1 #6: IdentityProvider create/update + discovery test (butleradm)
The butleradm idp group gains four verbs, closing audit P1 #6 so an operator can stand up SSO from the CLI (previously list/get/delete only).
butleradm idp create NAME --from-file f.yaml
butleradm idp update NAME --from-file f.yaml
butleradm idp test --issuer-url URL [-o table|json|yaml]
butleradm idp validate NAME [-o table|json|yaml]
| Verb | Mechanism | What it does |
|---|---|---|
create |
CRD-direct | Create an IdentityProvider from a manifest |
update |
CRD-direct | Update the spec, preserving status conditions and resourceVersion |
test |
butler-server | Probe an issuer's OIDC discovery before creating a provider |
validate |
butler-server | Probe an existing provider's configured issuer |
create and update are CRD-direct
Both read and write the IdentityProvider directly (it is cluster-scoped, so no namespace flag), like the rest of the idp group. update does Get-then-merge-spec-then-Update and applies only the spec, so the controller-managed status conditions and the resourceVersion are preserved. A spec edit never clobbers status.
test and validate are read-only probes
Both go through butler-server, which fetches the issuer's .well-known/openid-configuration and reports whether discovery succeeds and which endpoints it found. Neither mutates anything, so neither needs a confirmation guard. test takes an issuer URL (pre-create); validate uses an existing provider's configured issuer.
Secret handling (v1 choice)
The client secret is supplied by reference: the manifest's spec.oidc.clientSecretRef points at a Secret created separately, consistent with provider create. The console takes the secret inline and creates it server-side; a --client-secret-from-file convenience is queued for later. In exchange, the manifest can set any spec field, including spec.oidc.insecureSkipVerify and spec.oidc.googleWorkspace, which neither client exposed before.
Also fixed
idp get and idp list read the pre-nesting field paths (spec.issuerURL, spec.clientID, spec.scopes, spec.claims), which are always empty on the current CRD because those fields live under spec.oidc. They now read spec.oidc.*, so the get detail view and the list ISSUER column render correctly. Found by the live lifecycle test.
Validation
The full lifecycle was exercised end to end against butler-beta: test (valid and clean-failure paths), create, get, update (status conditions and resourceVersion preserved against the real apiserver, no optimistic-concurrency conflict), validate, delete, and cleanup to pre-state.