Skip to content

Commit 6f550f4

Browse files
committed
🤖 feat: reconcile CoderControlPlane licenses from Secret
Add CoderControlPlane license Secret support and operator-managed license upload reconciliation with readiness/operator-access preconditions, status tracking (`licenseLastApplied`, `licenseLastAppliedHash`), idempotency hashing, Secret rotation handling, and license status conditions. Also wire a codersdk-backed uploader, add referenced Secret field indexing and watches, update controller app wiring, add focused controller tests, and regenerate CRD/deepcopy/docs/sample artifacts. Includes docs reference regeneration to align with CI output formatting. --- _Generated with `mux` • Model: `openai:gpt-5.3-codex` • Thinking: `xhigh` • Cost: `$1.21`_ <!-- mux-attribution: model=openai:gpt-5.3-codex thinking=xhigh costs=1.21 -->
1 parent a84bece commit 6f550f4

9 files changed

Lines changed: 905 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: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
| `extraEnv` | [EnvVar](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#envvar-v1-core) array | ExtraEnv are injected into the Coder control plane container. |
2121
| `imagePullSecrets` | [LocalObjectReference](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#localobjectreference-v1-core) array | ImagePullSecrets are used by the pod to pull private images. |
2222
| `operatorAccess` | [OperatorAccessSpec](#operatoraccessspec) | OperatorAccess configures bootstrap API access to the coderd instance. |
23+
| `licenseSecretRef` | [SecretKeySelector](#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. |
2324

2425
## Status
2526

@@ -30,6 +31,8 @@
3031
| `url` | string | URL is the in-cluster URL for the control plane service. |
3132
| `operatorTokenSecretRef` | [SecretKeySelector](#secretkeyselector) | OperatorTokenSecretRef points to the Secret key containing the `coder-k8s-operator` API token. |
3233
| `operatorAccessReady` | boolean | OperatorAccessReady reports whether operator API access bootstrap succeeded. |
34+
| `licenseLastApplied` | [Time](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#time-v1-meta) | LicenseLastApplied is the timestamp of the most recent successful operator-managed license upload. |
35+
| `licenseLastAppliedHash` | string | LicenseLastAppliedHash is the SHA-256 hex hash of the trimmed license JWT that LicenseLastApplied refers to. |
3336
| `phase` | string | Phase is a high-level readiness indicator. |
3437
| `conditions` | [Condition](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#condition-v1-meta) array | Conditions are Kubernetes-standard conditions for this resource. |
3538

‎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)