🤖 fix: wait for the template import before creating a spec.files template - #116
Conversation
…te (#105) CoderTemplate Create with spec.files uploaded the source, created the template version and created the template without waiting for the import, so workspace creation right afterwards failed with "template version is running". Wait for the new version with the existing waitForTemplateVersionBuild (same limits and error mapping as Update) before CreateTemplate. On any wait failure no template is created and no Added event is emitted; the uploaded file and version remain. The non-files Create path is unchanged. Tests (red first on 0a18145): a held pending/running import makes no CreateTemplate call and emits no event until it succeeds, then exactly one of each; failed, canceled, configured timeout, request deadline, request cancellation and polling errors keep the existing mapping and never create a template, including after the timeout. _Generated with [`xum`](https://github.com/coder/xum) • Model: `anthropic:claude-opus-5-5` • Thinking: `xhigh`_ Change-Id: Ibeb11f72cc8679fcc2d60cf957f595875f30542b Signed-off-by: Thomas Kosiewski <tk@coder.com>
_Generated with [`xum`](https://github.com/coder/xum) • Model: `anthropic:claude-opus-5-5` • Thinking: `xhigh`_ Change-Id: Ic31bdb84531f2988d75a5d688c40d13328208b3f Signed-off-by: Thomas Kosiewski <tk@coder.com>
…d apply (#105) The lifecycle driver now creates the CoderTemplate itself with kubectl apply of config/e2e/codertemplate.yaml (long request timeout, since Create waits for the import) and immediately makes one readiness assertion: the active version's import job has succeeded, with no polling. It then creates the workspace with kubectl apply of a canonical manifest and, before any lifecycle mutation, re-applies the identical template and workspace manifests: both must exit 0 and leave the template id, active version and version count, and the workspace UID, latest build, build count and status unchanged. A failing apply's output is recorded in the receipt and stops the run. The separate CI template steps are removed. Offline tests: the old async import (still running right after apply) fails before any workspace create; template/workspace apply errors, a re-apply that adds a template version and one that adds a build each stop before the lifecycle mutations. Each new guard was mutation-checked. _Generated with [`xum`](https://github.com/coder/xum) • Model: `anthropic:claude-opus-5-5` • Thinking: `xhigh`_ Change-Id: I8a5f8cf39ea351f21dc58761a65c9dcfde250cb6 Signed-off-by: Thomas Kosiewski <tk@coder.com>
_Generated with [`xum`](https://github.com/coder/xum) • Model: `anthropic:claude-opus-5-5` • Thinking: `xhigh`_ Change-Id: I8e7c6687e4007dc217129660b426900a0bfd6d9c Signed-off-by: Thomas Kosiewski <tk@coder.com>
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
|
@codex security review |
#105 runtime evidence on
|
🛡️ Codex Security ReviewSecurity review completed. No security issues were found in this pull request. Reviewed commit: Only the user who started this review can view the report in Codex. ℹ️ About Codex security reviews in GitHubThis is an experimental Codex feature. Security reviews are triggered when:
Once complete, Codex will leave suggestions, or a comment if no findings are found. |

Summary
A
CoderTemplatecreated withspec.filesnow waits for Coder to finish importing its first version before the template is created. Previously Create returned while the import was still running, so a workspace created right afterwards failed with400("The provided template version is running"). Template Update already waited this way.Part of #105. The maintained E2E now creates the template with
kubectl applyand requires the import to be ready immediately. It also requires that re-applying identical template and workspace manifests succeeds without new versions or builds.Implementation
internal/aggregated/storage/template.go(spec.filesCreate path only): after creating the template version, wait with the existingwaitForTemplateVersionBuildand map failures withmapTemplateVersionBuildWaitError, beforeCreateTemplate. If the import fails, times out, or the request is cancelled, no template is created and noAddedevent is emitted. The uploaded file and version stay in Coder. Create withoutspec.filesis unchanged.internal/aggregated/storage/template_create_wait_test.go, written red first:CreateTemplateis not called and no event is emitted. After success, exactly one template is created and oneAddedevent is emitted.CreateTemplate, including afterwards.hack/e2e-workspace-lifecycle.sh:kubectl apply, one check with no polling requires the active version's import to have succeeded.kubectl applyof a canonical manifest.docs/how-to/deploy-aggregated-apiserver.md: Create's new behavior, what remains after a failed or timed-out import, and that retries can leave extra versions.Validation
CreateTemplatecall and zero version polls) and pass with the fix.make test,make build,make verify-vendor,make lint,make docs-check(strict),make test-scripts,bash -n,shellcheck -xand actionlint pass onf5bf93b7. Removing each new driver guard makes the offline tests fail.Risks
spec.filesCreate now returns only after the import (seconds to minutes). The server limit isCODER_K8S_TEMPLATE_VERSION_BUILD_WAIT_TIMEOUT(default 25 minutes). Clients and kube-apiserver can impose shorter request deadlines; kube-apiserver's default is 60 seconds for proxied requests. When a deadline cuts the request, the server creates no template, and a retry can create an extra version.kubectl applyon this aggregated API is verified by the E2E here rather than assumed. If it fails, the run records the failing apply, and the defect is split out rather than worked around.400(🤖 fix: enforce canonical organization and owner names #106, 🤖 fix: enforce canonical template and workspace leaf names #111). Quickstart documentation and PR 🤖 feat: ship generated install bundle and CI freshness checks #89 are out of scope.📋 Implementation Plan
#105 plan: template import readiness on Create and repeated-apply convergence
Findings (verified at main 0a18145)
spec.filesuploads, creates the version (template.go:415), and creates the template (:424) without waiting for the import. Workspace creation during the import fails with 400 ("template version is running"). Update already waits (:710) viawaitForTemplateVersionBuild(default limit 25 min, capped by the request deadline; errors mapped bymapTemplateVersionBuildWaitError).precondition failedis not established.Change (one PR)
spec.filespath only): afterCreateTemplateVersion, wait for the version build beforeCreateTemplate, using the existing waiter and error mapping. On any wait failure: noCreateTemplatecall and no Added watch event. The uploaded file and version may remain in Coder; document this. No automatic deletion or cancellation.filesCreate path is unchanged.spec.filestemplates, the residual effects of a failed or timed-out import, and that retries can create additional versions (no exactly-once guarantee).Tests (red first)
CreateTemplate; after success, exactly one template creation and one Added event.CreateTemplatecall follows (including late, after timeout).filesCreate tests stay green.Maintained E2E
kubectl applyof identical canonical template and workspace manifests, before the lifecycle mutations and with desiredspec.runningunchanged: exit 0; template identity, active version and version count unchanged; workspace identity, latest build ID and build count unchanged. If apply does not converge, capture the failing request/status and stop; do not relax resourceVersion checks or add metadata persistence. A distinct convergence defect becomes a separate issue.Out of scope
Quickstart docs and PR #89; template resourceVersion fingerprinting; status conditions.
Acceptance
Red then green unit tests;
make test,make build,make verify-vendor,make lint,make docs-check,make test-scripts, actionlint; one exact-head PR E2E run with receipts; labeled media; bounded reviews; merge queue; post-merge checks.Generated with
xum• Model:anthropic:claude-opus-5-5• Thinking:xhigh• Cost:$1003.96