Skip to content

Commit dc3ca49

Browse files
committed
πŸ€– feat: apply CoderControlPlane licenses from Secret
Add licenseSecretRef support on CoderControlPlane and reconcile Enterprise license uploads after control-plane/operator readiness. Track successful applies with licenseLastApplied and licenseLastAppliedHash to avoid duplicate uploads, and handle Secret rotation. Also adds Secret field indexing/watch wiring for referenced license Secrets, pluggable SDK-backed license uploader, generated CRD/docs updates, sample manifest updates, and controller tests for pending/apply/idempotency/rotation and NotSupported behavior. --- _Generated with `mux` β€’ Model: `openai:gpt-5.3-codex` β€’ Thinking: `xhigh` β€’ Cost: `.21`_ <!-- mux-attribution: model=openai:gpt-5.3-codex thinking=xhigh costs=1.21 -->
1 parent 968dd47 commit dc3ca49

9 files changed

Lines changed: 907 additions & 1 deletion

File tree

β€Žapi/v1alpha1/codercontrolplane_types.goβ€Ž

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ const (
1010
CoderControlPlanePhasePending = "Pending"
1111
// CoderControlPlanePhaseReady indicates at least one control plane pod is ready.
1212
CoderControlPlanePhaseReady = "Ready"
13+
// CoderControlPlaneConditionLicenseApplied indicates whether the operator uploaded the configured license.
14+
CoderControlPlaneConditionLicenseApplied = "LicenseApplied"
1315
)
1416

1517
// CoderControlPlaneSpec defines the desired state of a CoderControlPlane.
@@ -32,6 +34,11 @@ type CoderControlPlaneSpec struct {
3234
// OperatorAccess configures bootstrap API access to the coderd instance.
3335
// +kubebuilder:default={}
3436
OperatorAccess OperatorAccessSpec `json:"operatorAccess,omitempty"`
37+
// LicenseSecretRef references a Secret key containing a Coder Enterprise
38+
// license JWT. When set, the controller uploads the license after the
39+
// control plane is ready and re-uploads when the Secret value changes.
40+
// +optional
41+
LicenseSecretRef *SecretKeySelector `json:"licenseSecretRef,omitempty"`
3542
}
3643

3744
// OperatorAccessSpec configures the controller-managed coderd operator user.
@@ -56,6 +63,14 @@ type CoderControlPlaneStatus struct {
5663
OperatorTokenSecretRef *SecretKeySelector `json:"operatorTokenSecretRef,omitempty"`
5764
// OperatorAccessReady reports whether operator API access bootstrap succeeded.
5865
OperatorAccessReady bool `json:"operatorAccessReady,omitempty"`
66+
// LicenseLastApplied is the timestamp of the most recent successful
67+
// operator-managed license upload.
68+
// +optional
69+
LicenseLastApplied *metav1.Time `json:"licenseLastApplied,omitempty"`
70+
// LicenseLastAppliedHash is the SHA-256 hex hash of the trimmed license JWT
71+
// that LicenseLastApplied refers to.
72+
// +optional
73+
LicenseLastAppliedHash string `json:"licenseLastAppliedHash,omitempty"`
5974
// Phase is a high-level readiness indicator.
6075
Phase string `json:"phase,omitempty"`
6176
// Conditions are Kubernetes-standard conditions for this resource.

β€Žapi/v1alpha1/types_shared.goβ€Ž

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ import corev1 "k8s.io/api/core/v1"
55
const (
66
// DefaultTokenSecretKey is the default key used for proxy session tokens.
77
DefaultTokenSecretKey = "token"
8+
// DefaultLicenseSecretKey is the default key used for Coder license JWTs.
9+
DefaultLicenseSecretKey = "license"
810
)
911

1012
// ServiceSpec defines the Service configuration reconciled by the operator.

β€Žapi/v1alpha1/zz_generated.deepcopy.goβ€Ž

Lines changed: 9 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

β€Žconfig/crd/bases/coder.com_codercontrolplanes.yamlβ€Ž

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,21 @@ spec:
226226
type: object
227227
x-kubernetes-map-type: atomic
228228
type: array
229+
licenseSecretRef:
230+
description: |-
231+
LicenseSecretRef references a Secret key containing a Coder Enterprise
232+
license JWT. When set, the controller uploads the license after the
233+
control plane is ready and re-uploads when the Secret value changes.
234+
properties:
235+
key:
236+
description: Key is the key inside the Secret data map.
237+
type: string
238+
name:
239+
description: Name is the Kubernetes Secret name.
240+
type: string
241+
required:
242+
- name
243+
type: object
229244
operatorAccess:
230245
default: {}
231246
description: OperatorAccess configures bootstrap API access to the
@@ -330,6 +345,17 @@ spec:
330345
- type
331346
type: object
332347
type: array
348+
licenseLastApplied:
349+
description: |-
350+
LicenseLastApplied is the timestamp of the most recent successful
351+
operator-managed license upload.
352+
format: date-time
353+
type: string
354+
licenseLastAppliedHash:
355+
description: |-
356+
LicenseLastAppliedHash is the SHA-256 hex hash of the trimmed license JWT
357+
that LicenseLastApplied refers to.
358+
type: string
333359
observedGeneration:
334360
description: ObservedGeneration tracks the spec generation this status
335361
reflects.

β€Žconfig/samples/coder_v1alpha1_codercontrolplane.yamlβ€Ž

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,6 @@ metadata:
55
namespace: default
66
spec:
77
image: "ghcr.io/coder/coder-k8s:main"
8+
licenseSecretRef:
9+
name: coder-license
10+
key: license

β€Ždocs/reference/api/codercontrolplane.mdβ€Ž

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,9 @@
2626
| `operatorAccess` | `OperatorAccessSpec` | OperatorAccess configures bootstrap API access to the coderd instance. |
2727
| β”œβ”€ `disabled` | `bool` | Disabled turns off creation and management of the `coder-k8s-operator` user and API token. |
2828
| └─ `generatedTokenSecretName` | `string` | GeneratedTokenSecretName stores the generated operator API token. |
29+
| `licenseSecretRef` | `SecretKeySelector` | LicenseSecretRef references a Secret key containing a Coder Enterprise license JWT. When set, the controller uploads the license after the control plane is ready and re-uploads when the Secret value changes. |
30+
| β”œβ”€ `name` | `string` | Name is the Kubernetes Secret name. |
31+
| └─ `key` | `string` | Key is the key inside the Secret data map. |
2932

3033
## Status
3134

@@ -38,6 +41,8 @@
3841
| β”œβ”€ `name` | `string` | Name is the Kubernetes Secret name. |
3942
| └─ `key` | `string` | Key is the key inside the Secret data map. |
4043
| `operatorAccessReady` | `bool` | OperatorAccessReady reports whether operator API access bootstrap succeeded. |
44+
| `licenseLastApplied` | `metav1.Time` | LicenseLastApplied is the timestamp of the most recent successful operator-managed license upload. |
45+
| `licenseLastAppliedHash` | `string` | LicenseLastAppliedHash is the SHA-256 hex hash of the trimmed license JWT that LicenseLastApplied refers to. |
4146
| `phase` | `string` | Phase is a high-level readiness indicator. |
4247
| `conditions` | `[]metav1.Condition` | Conditions are Kubernetes-standard conditions for this resource. |
4348

β€Žinternal/app/controllerapp/controllerapp.goβ€Ž

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ func SetupControllers(mgr manager.Manager) error {
9090
Client: client,
9191
Scheme: managerScheme,
9292
OperatorAccessProvisioner: coderbootstrap.NewPostgresOperatorAccessProvisioner(),
93+
LicenseUploader: controller.NewSDKLicenseUploader(),
9394
}
9495
if err := reconciler.SetupWithManager(mgr); err != nil {
9596
return fmt.Errorf("unable to create controller: %w", err)

0 commit comments

Comments
Β (0)