Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
589f904
feat: add CoderProvisioner CRD types
ThomasK33 Feb 11, 2026
4a58ddf
feat: add coderbootstrap provisioner key management
ThomasK33 Feb 11, 2026
e8d6880
chore: generate deepcopy and CRD manifest for CoderProvisioner
ThomasK33 Feb 11, 2026
713e70d
feat: add coder provisioner reconciler
ThomasK33 Feb 11, 2026
08b086c
test: add coder provisioner controller envtests
ThomasK33 Feb 11, 2026
d7b06d5
chore: add CoderProvisioner sample manifest and API reference docs
ThomasK33 Feb 11, 2026
e6aeadb
chore: fix lint issues in provisioner types and tests
ThomasK33 Feb 11, 2026
59ba90c
chore: regenerate RBAC manifest for provisioner controller
ThomasK33 Feb 11, 2026
d332da7
chore: add provisioner terms to cspell dictionary
ThomasK33 Feb 11, 2026
f1eb5bd
fix: make deletion finalizer best-effort and handle key rotation on s…
ThomasK33 Feb 11, 2026
64281fc
fix: use persisted key name for cleanup and skip deletion on unreacha…
ThomasK33 Feb 11, 2026
266d39e
fix: fully best-effort deletion cleanup and use persisted org identity
ThomasK33 Feb 11, 2026
009884b
fix: fully best-effort delete call and validate secret data key
ThomasK33 Feb 11, 2026
aa09b30
controller: watch owned Role and RoleBinding
ThomasK33 Feb 11, 2026
f2118a3
fix: use org name for key deletion and truncate SA names
ThomasK33 Feb 11, 2026
90a05eb
api: enrich CoderProvisioner status metadata
ThomasK33 Feb 11, 2026
606345e
controller: add provisioner drift rotation and conditions
ThomasK33 Feb 11, 2026
161515b
fix: lint condition constant comments and formatting
ThomasK33 Feb 11, 2026
7de700e
chore: improve coderprovisioner sample secret guidance
ThomasK33 Feb 11, 2026
3f39c79
test: expand coder provisioner reconciler coverage
ThomasK33 Feb 11, 2026
2af668d
fix: preserve key status on skip path and bound secret name
ThomasK33 Feb 11, 2026
ef7bf3c
fix: set ProvisionerKeyReady only after coderd verification
ThomasK33 Feb 11, 2026
2d9f216
fix: use status org for deletion and mark key condition false on failure
ThomasK33 Feb 11, 2026
b568e53
fix: populate status metadata when secret exists but status is empty
ThomasK33 Feb 11, 2026
f648b7c
fix: capture key material during metadata-only ensure
ThomasK33 Feb 11, 2026
a25bf6b
fix: harden provisioner key drift recovery
ThomasK33 Feb 11, 2026
7022044
fix: rotate existing key during metadata backfill
ThomasK33 Feb 11, 2026
bff1409
test: update ExistingSecret rotation expectations
ThomasK33 Feb 11, 2026
4101c4a
fix: fail reconcile on metadata backfill recreate error
ThomasK33 Feb 11, 2026
dd02b9c
fix: guard coderprovisioner status updates
ThomasK33 Feb 11, 2026
2bf3452
fix: harden metadata backfill key reconciliation
ThomasK33 Feb 11, 2026
936bf18
fix: retry metadata backfill key deletion failures
ThomasK33 Feb 11, 2026
33dbfe7
fix: drop RequeueAfter when returning errors in backfill
ThomasK33 Feb 11, 2026
ba890ee
fix: rotate keys on control plane ref drift
ThomasK33 Feb 11, 2026
5dd24ca
fix: persist provisioner control plane URL in status
ThomasK33 Feb 11, 2026
2241f64
fix: track control-plane URL drift for provisioner keys
ThomasK33 Feb 11, 2026
9c5b166
controller: prefer status URL for deletion key cleanup
ThomasK33 Feb 11, 2026
117a6a7
test: update deletion cleanup expectation when control plane is gone
ThomasK33 Feb 11, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .cspell.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,10 @@
"codertemplates",
"coderworkspace",
"coderworkspaces",
"coderprovisioner",
"coderprovisioners",
"controllerapp",
"provisionerd",
"workspaceproxy",
"workspaceproxies",
"derp",
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,4 @@ terraform.rc

# Backend config (local-only, not committed)
terraform/backend.hcl
/coder-k8s
138 changes: 138 additions & 0 deletions api/v1alpha1/coderprovisioner_types.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
package v1alpha1

import (
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

const (
// CoderProvisionerPhasePending indicates the provisioner deployment is not ready.
CoderProvisionerPhasePending = "Pending"
// CoderProvisionerPhaseReady indicates at least one provisioner pod is ready.
CoderProvisionerPhaseReady = "Ready"

// CoderProvisionerConditionControlPlaneReady indicates whether the referenced control plane is reachable.
CoderProvisionerConditionControlPlaneReady = "ControlPlaneReady"
// CoderProvisionerConditionBootstrapSecretReady indicates whether the bootstrap credentials secret is available.
CoderProvisionerConditionBootstrapSecretReady = "BootstrapSecretReady"
// CoderProvisionerConditionProvisionerKeyReady indicates whether the provisioner key exists in coderd.
CoderProvisionerConditionProvisionerKeyReady = "ProvisionerKeyReady"
// CoderProvisionerConditionProvisionerKeySecretReady indicates whether the provisioner key secret is populated.
CoderProvisionerConditionProvisionerKeySecretReady = "ProvisionerKeySecretReady"
// CoderProvisionerConditionDeploymentReady indicates whether the provisioner deployment has ready replicas.
CoderProvisionerConditionDeploymentReady = "DeploymentReady"

// DefaultProvisionerKeySecretKey is the default data key for provisioner key secrets.
DefaultProvisionerKeySecretKey = "key"

// ProvisionerKeyCleanupFinalizer is applied to ensure coderd key cleanup on deletion.
ProvisionerKeyCleanupFinalizer = "coder.com/provisioner-key-cleanup"
)

// CoderProvisionerBootstrapSpec configures credentials for provisioner key management.
type CoderProvisionerBootstrapSpec struct {
// CredentialsSecretRef points to a Secret containing a Coder session token
// with permission to manage provisioner keys.
CredentialsSecretRef SecretKeySelector `json:"credentialsSecretRef"`
}

// CoderProvisionerKeySpec configures provisioner key naming and storage.
type CoderProvisionerKeySpec struct {
// Name is the provisioner key name in coderd. Defaults to the CR name.
// +kubebuilder:validation:MaxLength=128
Name string `json:"name,omitempty"`
// SecretName is the Kubernetes Secret to store the key. Defaults to "{crName}-provisioner-key".
// +kubebuilder:validation:MaxLength=253
SecretName string `json:"secretName,omitempty"`
// SecretKey is the data key in the Secret. Defaults to "key".
// +kubebuilder:validation:MaxLength=253
SecretKey string `json:"secretKey,omitempty"`
}

// CoderProvisionerSpec defines the desired state of a CoderProvisioner.
type CoderProvisionerSpec struct {
// ControlPlaneRef identifies which CoderControlPlane instance to join.
ControlPlaneRef corev1.LocalObjectReference `json:"controlPlaneRef"`
// OrganizationName is the Coder organization. Defaults to "default".
// +kubebuilder:validation:MaxLength=128
OrganizationName string `json:"organizationName,omitempty"`
// Bootstrap configures credentials for provisioner key management.
Bootstrap CoderProvisionerBootstrapSpec `json:"bootstrap"`
// Key configures provisioner key naming and secret storage.
Key CoderProvisionerKeySpec `json:"key,omitempty"`
// Replicas is the desired number of provisioner pods.
Replicas *int32 `json:"replicas,omitempty"`
// Tags are attached to the provisioner key for job routing.
Tags map[string]string `json:"tags,omitempty"`
// Image is the container image. Defaults to the control plane image.
Image string `json:"image,omitempty"`
// ExtraArgs are appended after "provisionerd start".
ExtraArgs []string `json:"extraArgs,omitempty"`
// ExtraEnv are injected into the provisioner container.
ExtraEnv []corev1.EnvVar `json:"extraEnv,omitempty"`
// Resources for the provisioner container.
Resources corev1.ResourceRequirements `json:"resources,omitempty"`
// ImagePullSecrets are used by the pod to pull private images.
ImagePullSecrets []corev1.LocalObjectReference `json:"imagePullSecrets,omitempty"`
// TerminationGracePeriodSeconds for the provisioner pods.
TerminationGracePeriodSeconds *int64 `json:"terminationGracePeriodSeconds,omitempty"`
}

// CoderProvisionerStatus defines the observed state of a CoderProvisioner.
type CoderProvisionerStatus struct {
// ObservedGeneration tracks the spec generation this status reflects.
ObservedGeneration int64 `json:"observedGeneration,omitempty"`
// ReadyReplicas is the number of ready pods observed in the deployment.
ReadyReplicas int32 `json:"readyReplicas,omitempty"`
// Phase is a high-level readiness indicator.
Phase string `json:"phase,omitempty"`
// Conditions are Kubernetes-standard conditions for this resource.
Conditions []metav1.Condition `json:"conditions,omitempty"`
// OrganizationID is the organization ID last applied to the provisioner key.
OrganizationID string `json:"organizationID,omitempty"`
// OrganizationName is the organization name last applied to the provisioner key.
OrganizationName string `json:"organizationName,omitempty"`
// ProvisionerKeyID is the provisioner key ID last applied in coderd.
ProvisionerKeyID string `json:"provisionerKeyID,omitempty"`
// ProvisionerKeyName is the provisioner key name last applied in coderd.
ProvisionerKeyName string `json:"provisionerKeyName,omitempty"`
// TagsHash is a deterministic hash of spec.tags last applied to the provisioner key.
TagsHash string `json:"tagsHash,omitempty"`
// ControlPlaneRefName is the control plane ref name last applied to the provisioner key.
ControlPlaneRefName string `json:"controlPlaneRefName,omitempty"`
// ControlPlaneURL is the control plane URL last applied to the provisioner key.
ControlPlaneURL string `json:"controlPlaneURL,omitempty"`
// SecretRef references the provisioner key secret data currently in use.
SecretRef *SecretKeySelector `json:"secretRef,omitempty"`
}

// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// +kubebuilder:object:root=true
// +kubebuilder:resource:scope=Namespaced
// +kubebuilder:subresource:status
// +kubebuilder:printcolumn:name="Phase",type=string,JSONPath=`.status.phase`
// +kubebuilder:printcolumn:name="Replicas",type=integer,JSONPath=`.status.readyReplicas`
// +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp"

// CoderProvisioner is the schema for Coder external provisioner daemon resources.
type CoderProvisioner struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`

Spec CoderProvisionerSpec `json:"spec,omitempty"`
Status CoderProvisionerStatus `json:"status,omitempty"`
}

// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// +kubebuilder:object:root=true

// CoderProvisionerList contains a list of CoderProvisioner objects.
type CoderProvisionerList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitempty"`
Items []CoderProvisioner `json:"items"`
}

func init() {
SchemeBuilder.Register(&CoderProvisioner{}, &CoderProvisionerList{})
}
176 changes: 176 additions & 0 deletions api/v1alpha1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading