Skip to content

🤖 feat: replace WorkspaceProxy with CoderWorkspaceProxy#61

Merged
ThomasK33 merged 3 commits into
mainfrom
workspace-proxy-rt3p
Feb 12, 2026
Merged

🤖 feat: replace WorkspaceProxy with CoderWorkspaceProxy#61
ThomasK33 merged 3 commits into
mainfrom
workspace-proxy-rt3p

Conversation

@ThomasK33
Copy link
Copy Markdown
Member

Summary

Replace the unreleased WorkspaceProxy CRD/controller with CoderWorkspaceProxy as the single workspace-proxy API in coder.com/v1alpha1.

Background

WorkspaceProxy was still under development and had not been released. Keeping both Kinds introduced unnecessary API/controller duplication and naming inconsistency with other Coder* resources.

Implementation

  • Added CoderWorkspaceProxy API roots and registration.
  • Switched controller wiring to CoderWorkspaceProxyReconciler only.
  • Renamed/generated CRD, sample, and API reference docs to CoderWorkspaceProxy.
  • Removed legacy WorkspaceProxy root kind/list, CRD, sample, docs, controller, and controller tests.
  • Updated RBAC and scheme tests to the new Kind.

Validation

  • make verify-vendor
  • make test
  • make build
  • make lint
  • make docs-check (run with generated docs staged)

Risks

Low-to-moderate: this removes an unreleased API surface and consolidates onto one Kind. Main regression risk is controller behavior drift during the rename, mitigated by preserving reconciliation logic and running full local validation.


📋 Implementation Plan

Plan: Rename WorkspaceProxyCoderWorkspaceProxy

Context / Why

Today the operator exposes a namespaced CRD Kind WorkspaceProxy in coder.com/v1alpha1, while most other public CRDs in this repo use a Coder* prefix (e.g. CoderControlPlane, CoderProvisioner, CoderWorkspace, CoderTemplate). This is inconsistent in docs and creates Go naming collisions/confusion with upstream Coder concepts (there is also a codersdk.WorkspaceProxy).

Goal: introduce a CoderWorkspaceProxy CRD and controller as the long-term supported API, with a practical migration path from the existing WorkspaceProxy.

Evidence (repo facts)

  • Go API type + kubebuilder markers live in api/v1alpha1/workspaceproxy_types.go (type WorkspaceProxy struct { ... }, +kubebuilder:resource:scope=Namespaced).
  • Controller lives in internal/controller/workspaceproxy_controller.go and is wired in internal/app/controllerapp/controllerapp.go.
  • Generated artifacts that will change:
    • CRD manifest: config/crd/bases/coder.com_workspaceproxies.yaml
    • Deepcopy: api/v1alpha1/zz_generated.deepcopy.go
    • API reference docs: docs/reference/api/workspaceproxy.md
    • Docs nav: mkdocs.yml
  • Kubernetes does not allow changing a CRD’s spec.names.kind / plural in-place (they are immutable). Therefore a pure “rename” requires either:
    1. destructive delete/recreate of the existing CRD, or
    2. introducing a new CRD (new plural) and migrating users.

Recommended approach (non-destructive): new CRD + migration, then remove old

Phase 1 (Release N): Add CoderWorkspaceProxy alongside WorkspaceProxy

1) Add new API types (new Kind + plural)

  • File(s):
    • Add api/v1alpha1/coderworkspaceproxy_types.go (or extend workspaceproxy_types.go if preferred).
  • New root objects:
    • CoderWorkspaceProxy
    • CoderWorkspaceProxyList
  • Reuse existing spec/status structs (WorkspaceProxySpec, WorkspaceProxyStatus, ProxyBootstrapSpec) to avoid duplicating schema and docs.
  • kubebuilder markers: ensure the new resource gets its own plural (so it can coexist with the old CRD):
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// +kubebuilder:object:root=true
// +kubebuilder:resource:path=coderworkspaceproxies,scope=Namespaced
// +kubebuilder:subresource:status
//
// CoderWorkspaceProxy is the schema for Coder workspace proxy resources.
// (Replacement for WorkspaceProxy.)
type CoderWorkspaceProxy struct {
    metav1.TypeMeta   `json:",inline"`
    metav1.ObjectMeta `json:"metadata,omitempty"`

    Spec   WorkspaceProxySpec   `json:"spec,omitempty"`
    Status WorkspaceProxyStatus `json:"status,omitempty"`
}
  • Register the new types with the scheme (either via a new init() or existing one):
    • SchemeBuilder.Register(&CoderWorkspaceProxy{}, &CoderWorkspaceProxyList{})

2) Generate new CRD + deepcopy

  • Run generators:
    • make codegen
    • make manifests
  • Expected outputs:
    • New CRD file: config/crd/bases/coder.com_coderworkspaceproxies.yaml
    • Existing CRD file remains for workspaceproxies.coder.com.

3) Add a new controller for CoderWorkspaceProxy

  • File: internal/controller/coderworkspaceproxy_controller.go
  • Type: CoderWorkspaceProxyReconciler
  • Implementation choices:
    • Prefer factoring shared logic out of the current WorkspaceProxyReconciler into helper functions (naming/labels, secret reads, deployment/service reconcile) so the behavior stays identical and future fixes apply to both during the transition.
  • RBAC markers: add rules for coderworkspaceproxies (+ status/finalizers).

4) Handle adoption of existing child resources (important for smooth migration)

Existing WorkspaceProxy instances may already own a Deployment/Service/Secret via a controller ownerRef. When a user creates a new CoderWorkspaceProxy with the same name, the new controller should “adopt” those children instead of failing on SetControllerReference.

  • Add a small “adopt if owned-by WorkspaceProxy” helper used by all child reconcile paths:
func adoptIfOwnedByWorkspaceProxy(obj metav1.Object, legacy *coderv1alpha1.WorkspaceProxy) bool {
    // Pseudocode:
    // - Inspect obj.GetOwnerReferences() for controller=true entry.
    // - If controllerRef is WorkspaceProxy and matches legacy name/uid: replace it with ownerRef to new CoderWorkspaceProxy.
    // - Return true if adoption happened.
    // - If controllerRef exists but is NOT legacy WorkspaceProxy: return false and let caller error (defensive).
}
  • Defensive programming expectations:
    • If the existing controllerRef is not the legacy WorkspaceProxy, fail fast (this prevents accidental adoption of unrelated resources).

5) Wire controller + keep legacy behavior clear

  • Update internal/app/controllerapp/controllerapp.go to set up the new reconciler.
  • Decide legacy reconciliation policy:
    • Recommended: stop running the old WorkspaceProxyReconciler in the manager (so we don’t have two controllers fighting over the same child resources), but keep the CRD/types temporarily so users can still read/export their legacy objects.
    • Optionally add a minimal “legacy warning” reconciler that only sets a Deprecated Condition on WorkspaceProxy objects (no child management) to guide users.

6) Update/extend tests

  • Update controller tests to cover the new Kind:
    • Either parameterize the existing tests in internal/controller/workspaceproxy_controller_test.go, or add a parallel file (e.g. internal/controller/coderworkspaceproxy_controller_test.go).
  • Add a focused adoption test to prevent regressions:
    1. Create a legacy WorkspaceProxy and reconcile once to create children.
    2. Create a CoderWorkspaceProxy with the same name/namespace.
    3. Reconcile the new controller and assert child resources’ controller ownerRef was moved from WorkspaceProxyCoderWorkspaceProxy.

Phase 2 (same release N): Update samples + docs to prefer CoderWorkspaceProxy

7) Samples / manifests

  • Add a new sample:
    • config/samples/coder_v1alpha1_coderworkspaceproxy.yaml (Kind CoderWorkspaceProxy).
  • Keep the old sample for one release, but mark it deprecated in comments.

8) Docs + docs nav

  • Update:
    • README.md: replace WorkspaceProxy mentions with CoderWorkspaceProxy.
    • mkdocs.yml: generated API nav entry should become CoderWorkspaceProxy: reference/api/coderworkspaceproxy.md.
  • Regenerate API reference docs:
    • make docs-reference
    • (and confirm make docs-check passes)

Phase 3 (Release N+1): Remove legacy WorkspaceProxy

9) Remove legacy API + CRD

  • Delete/stop generating the legacy WorkspaceProxy root types and its CRD.
  • Remove legacy controller code and any remaining references.
  • Regenerate:
    • make codegen
    • make manifests
    • make docs-reference

Migration guide (for Release N)

  1. Upgrade the operator (now serving both CRDs).
  2. For each legacy WorkspaceProxy named X:
    • Create a CoderWorkspaceProxy with the same name/namespace and the same spec.
    • Verify the new controller has adopted the existing Deployment/Service/Secret and the status becomes Ready.
    • Delete the legacy WorkspaceProxy once stable after adoption completes (otherwise Kubernetes GC may delete the child Deployment/Service/Secret).
Alternative (development-only): destructive rename

Because CRD Kind/Plural are immutable, the only way to keep the same REST resource workspaceproxies.coder.com while changing the Kind to CoderWorkspaceProxy is:

  1. export all existing WorkspaceProxy objects,
  2. delete the CRD (which deletes all CRs),
  3. recreate the CRD with the new Kind, and
  4. re-apply the exported objects (updated Kind).

This is not recommended for production clusters.

Validation (what to run when implementing)

  • make test
  • make test-integration (if controller behavior changes materially)
  • make lint
  • make build
  • make verify-vendor (should be a no-op unless deps changed)
  • make manifests && git diff --exit-code config/ (confirm generated output is committed)
  • make docs-reference && make docs-check

Generated with mux • Model: openai:gpt-5.3-codex • Thinking: xhigh • Cost: $0.62

@ThomasK33
Copy link
Copy Markdown
Member Author

@codex review

Please review this PR for correctness and readiness.

@ThomasK33 ThomasK33 force-pushed the workspace-proxy-rt3p branch from 5994604 to b4c5ec5 Compare February 12, 2026 11:09
@ThomasK33
Copy link
Copy Markdown
Member Author

@codex review

Rebased on latest main to resolve merge conflicts. Please re-review.

@ThomasK33 ThomasK33 force-pushed the workspace-proxy-rt3p branch from b4c5ec5 to 0a6f43d Compare February 12, 2026 11:11
@ThomasK33
Copy link
Copy Markdown
Member Author

@codex review

Resolved merge conflict after rebasing onto latest main and revalidated locally.

@ThomasK33
Copy link
Copy Markdown
Member Author

@codex review

Added cspell entries for CoderWorkspaceProxy terms to fix docs-quality CI. Please re-review.

@chatgpt-codex-connector
Copy link
Copy Markdown

Codex Review: Didn't find any major issues. 🚀

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@ThomasK33
Copy link
Copy Markdown
Member Author

@codex review

Moved remaining workspace proxy types/constants into coderworkspaceproxy_types.go and removed workspaceproxy_types.go.

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

if err := controllerutil.SetControllerReference(workspaceProxy, deployment, r.Scheme); err != nil {
return fmt.Errorf("set controller reference: %w", err)

P2 Badge Adopt existing proxy children before setting controller owner

Upgrades from builds that created WorkspaceProxy objects can fail reconciliation because child resource names are unchanged (wsproxy-...), so existing Deployments/Services/Secrets may already be present with an ownerRef to the old kind; in that case SetControllerReference returns an AlreadyOwned error and the new CoderWorkspaceProxy never converges. This makes migration to the renamed CRD brittle in clusters with pre-existing proxy resources.

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Remove the unreleased WorkspaceProxy legacy kind/controller path and keep
CoderWorkspaceProxy as the single workspace-proxy API surface.

- add CoderWorkspaceProxy API type, CRD, docs, sample, and controller/tests
- remove WorkspaceProxy API roots, CRD, docs, sample, and controller/tests
- update controller wiring, RBAC, and scheme tests to reference only the new kind

---

_Generated with [`mux`](https://github.com/coder/mux) • Model: `openai:gpt-5.3-codex` • Thinking: `xhigh` • Cost: `$0.62`_

<!-- mux-attribution: model=openai:gpt-5.3-codex thinking=xhigh costs=0.62 -->
The docs-quality check failed on new CoderWorkspaceProxy resource names.
Add the new singular/plural words to `.cspell.json` so docs CI passes.

---

_Generated with [`mux`](https://github.com/coder/mux) • Model: `openai:gpt-5.3-codex` • Thinking: `xhigh` • Cost: `$0.62`_

<!-- mux-attribution: model=openai:gpt-5.3-codex thinking=xhigh costs=0.62 -->
Move the remaining shared workspace proxy constants/spec/status types from
`workspaceproxy_types.go` into `coderworkspaceproxy_types.go` and delete the
old file.

---

_Generated with [`mux`](https://github.com/coder/mux) • Model: `openai:gpt-5.3-codex` • Thinking: `xhigh` • Cost: `$0.62`_

<!-- mux-attribution: model=openai:gpt-5.3-codex thinking=xhigh costs=0.62 -->
@ThomasK33
Copy link
Copy Markdown
Member Author

@codex review

Rebased again on latest main to clear merge conflicts; retained the workspaceproxy_types.go consolidation.

@ThomasK33 ThomasK33 force-pushed the workspace-proxy-rt3p branch from 5404ec2 to fe92f51 Compare February 12, 2026 11:31
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

if err := controllerutil.SetControllerReference(workspaceProxy, deployment, r.Scheme); err != nil {
return fmt.Errorf("set controller reference: %w", err)

P1 Badge Adopt legacy-owned children before setting controller refs

If a cluster already has WorkspaceProxy objects from the prior API, their Deployment/Service/token Secret keep controller ownerRefs to that legacy kind. This reconciler still computes the same child names (wsproxy-<name>), so upgrade-time reconciles will hit existing children and SetControllerReference will return an AlreadyOwned error instead of adopting them, leaving CoderWorkspaceProxy stuck and blocking migration for existing proxies.

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@ThomasK33
Copy link
Copy Markdown
Member Author

@codex review

Context on the migration concern: this API rename is still pre-release/internal development and WorkspaceProxy was never shipped as a supported surface.

For this PR we intentionally removed the legacy path (CRD/controller/docs/sample) and are not supporting in-place migration/adoption from legacy-owned children. Existing dev clusters are expected to recreate these resources if they still have old WorkspaceProxy objects.

Given that scope, please re-review for correctness of the no-legacy implementation.

@chatgpt-codex-connector
Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Bravo.

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@ThomasK33 ThomasK33 added this pull request to the merge queue Feb 12, 2026
@ThomasK33
Copy link
Copy Markdown
Member Author

Merged via the queue into main with commit 968dd47 Feb 12, 2026
11 checks passed
@ThomasK33 ThomasK33 deleted the workspace-proxy-rt3p branch February 12, 2026 11:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant