refactor(handlers): widen WorkspaceHandler.provisioner to LocalProvisionerAPI interface (#2369) - #2390
Merged
hongmingwang-moleculeai merged 1 commit intoApr 30, 2026
Conversation
…ionerAPI interface (#2369) Symmetric with the existing CPProvisionerAPI interface. Closes the asymmetry where the SaaS provisioner field was an interface (mockable in tests) but the Docker provisioner field was a concrete pointer (not). ## Changes - New ``provisioner.LocalProvisionerAPI`` interface — the 7 methods WorkspaceHandler / TeamHandler call on h.provisioner today: Start, Stop, IsRunning, ExecRead, RemoveVolume, VolumeHasFile, WriteAuthTokenToVolume. Compile-time assertion confirms *Provisioner satisfies it. Mirror of cp_provisioner.go's CPProvisionerAPI block. - ``WorkspaceHandler.provisioner`` and ``TeamHandler.provisioner`` re-typed from ``*provisioner.Provisioner`` to ``provisioner.LocalProvisionerAPI``. Constructor parameter type is unchanged — the assignment widens to the interface, so the 200+ callers of ``NewWorkspaceHandler`` / ``NewTeamHandler`` are unaffected. - Constructors gain a ``if p != nil`` guard before assigning to the interface field. Without this, ``NewWorkspaceHandler(..., nil, ...)`` (the test fixture pattern across 200+ tests) yields a typed-nil interface value where ``h.provisioner != nil`` evaluates *true*, and the SaaS-vs-Docker fork incorrectly routes nil-fixture tests into the Docker code path. Documented inline with reference to the Go FAQ. - Hardened the 5 Provisioner methods that lacked nil-receiver guards (Start, ExecRead, WriteAuthTokenToVolume, RemoveVolume, VolumeHasFile) — return ErrNoBackend on nil receiver instead of panicking on p.cli dereference. Symmetric with Stop/IsRunning (already hardened in #1813). Defensive cleanup so a future caller that bypasses the constructor's nil-elision still degrades cleanly. - Extended TestZeroValuedBackends_NoPanic with 5 new sub-tests covering the newly-hardened nil-receiver paths. Defense-in-depth: a future refactor that drops one of the nil-checks fails red here before reaching production. ## Why now - Provisioner orchestration has been touched in #2366 / #2368 — the interface symmetry is the natural follow-up captured in #2369. - Future work (CP fleet redeploy endpoint, multi-backend provisioners) wants this in place. Memory note ``project_provisioner_abstraction.md`` calls out pluggable backends as a north-star. - Memory note ``feedback_long_term_robust_automated.md`` — compile-time gates + ErrNoBackend symmetry > runtime panics. ## Verification - ``go build ./...`` clean. - ``go test ./...`` clean — 1300+ tests pass, including the previously-flaky Create-with-nil-provisioner paths that now exercise the constructor's nil-elision correctly. - ``go test ./internal/provisioner/ -run TestZeroValuedBackends_NoPanic -v`` — all 11 nil-receiver subtests green (was 6, +5 for the newly-hardened methods). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
HongmingWang-Rabbit
requested a review
from hongmingwang-moleculeai
as a code owner
April 30, 2026 16:18
HongmingWang-Rabbit
pushed a commit
that referenced
this pull request
Jun 12, 2026
… error + add registry-load-fail test (#2248 follow-up) Researcher review RC 9329 on PR #2390 found two blockers: 1. org_import.go swallowed the new fail-closed config-generation error. In createWorkspaceTree, after the workspace DB row/layout/broadcast are already persisted, cfgErr only logged and continued. That left a silent stuck provisioning workspace during org import. Fixed by calling markProvisionFailed before continuing, so the canvas surfaces the failure card immediately. 2. Registry-unavailable/load-error fail-closed path was not covered. Converted providerRegistry from a named function to a variable function (mirrors the resolveInstanceID testability pattern) so tests can swap in a mock. Added TestDeriveProvider_RegistryLoadErrorFailClosed asserting that a registry load error blocks provisioning. Scope still limited to workspace config/provisioning caller plumbing and tests.
HongmingWang-Rabbit
pushed a commit
that referenced
this pull request
Jun 12, 2026
Empty commit to re-run CI jobs that were absent/pending on the previous infra outage. No code changes.
HongmingWang-Rabbit
pushed a commit
that referenced
this pull request
Jun 12, 2026
Serialized merge by gitea-merge-queue after current-main, genuine approvals, and required CI checks were green.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #2369.
Why
`WorkspaceHandler` typed its CP-side via `provisioner.CPProvisionerAPI` (interface, mockable) but its Docker-side via `*provisioner.Provisioner` (concrete, harder to mock). This PR closes that asymmetry.
What
Drift gate
The compile-time assertion `var _ LocalProvisionerAPI = (*Provisioner)(nil)` blocks any future method-signature drift on `*Provisioner` that would silently break handlers — same protection `CPProvisionerAPI` has had since #1814.
Verification
Test plan
🤖 Generated with Claude Code