Skip to content

🤖 fix: wait for the template import before creating a spec.files template - #116

Merged
ThomasK33 merged 4 commits into
mainfrom
fix/template-create-import-wait
Sep 23, 2026
Merged

ThomasK33 merged 4 commits into
mainfrom
fix/template-create-import-wait

Conversation

@ThomasK33

Copy link
Copy Markdown
Member

Summary

A CoderTemplate created with spec.files now 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 with 400 ("The provided template version is running"). Template Update already waited this way.

Part of #105. The maintained E2E now creates the template with kubectl apply and 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.files Create path only): after creating the template version, wait with the existing waitForTemplateVersionBuild and map failures with mapTemplateVersionBuildWaitError, before CreateTemplate. If the import fails, times out, or the request is cancelled, no template is created and no Added event is emitted. The uploaded file and version stay in Coder. Create without spec.files is unchanged.
  • internal/aggregated/storage/template_create_wait_test.go, written red first:
    • While the import is pending, CreateTemplate is not called and no event is emitted. After success, exactly one template is created and one Added event is emitted.
    • Failed and canceled imports, the configured wait timeout, a request deadline, request cancellation, and a polling error each keep the existing error mapping and never call CreateTemplate, including afterwards.
  • hack/e2e-workspace-lifecycle.sh:
    • The template is now applied by the driver after its setup checks, instead of by a separate CI step. Right after kubectl apply, one check with no polling requires the active version's import to have succeeded.
    • The workspace is created with kubectl apply of a canonical manifest.
    • Before the lifecycle mutations, both identical manifests are applied again. Each apply must succeed, the template's ID, active version and version count must not change, and the workspace's UID, latest build, build count and status must not change.
    • A failing apply is recorded in the receipt and stops the run. The offline tests model the old asynchronous import and require the readiness check to fail before any workspace exists.
  • 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

  • The new unit tests failed on unchanged code (Create returned after one CreateTemplate call 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 -x and actionlint pass on f5bf93b7. Removing each new driver guard makes the offline tests fail.
  • This PR's E2E run on the exact head is the runtime evidence. Results will be added in a comment.

Risks


📋 Implementation Plan

#105 plan: template import readiness on Create and repeated-apply convergence

Findings (verified at main 0a18145)

  • Alias requests now fail with 400 before any mutation (🤖 fix: enforce canonical organization and owner names #106, 🤖 fix: enforce canonical template and workspace leaf names #111). The "returned under a different name" symptom is gone; supported manifests must use canonical names.
  • Template Create with spec.files uploads, 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) via waitForTemplateVersionBuild (default limit 25 min, capped by the request deadline; errors mapped by mapTemplateVersionBuildWaitError).
  • The cause of the original repeated-apply precondition failed is not established.

Change (one PR)

  1. Template Create (spec.files path only): after CreateTemplateVersion, wait for the version build before CreateTemplate, using the existing waiter and error mapping. On any wait failure: no CreateTemplate call and no Added watch event. The uploaded file and version may remain in Coder; document this. No automatic deletion or cancellation.
  2. The non-files Create path is unchanged.
  3. Docs: state that Create returns after a successful import for spec.files templates, the residual effects of a failed or timed-out import, and that retries can create additional versions (no exactly-once guarantee).

Tests (red first)

  • Version held pending/running: Create has not called CreateTemplate; after success, exactly one template creation and one Added event.
  • Failed and canceled imports, configured wait timeout, request cancellation/deadline, polling error: each keeps the existing error mapping, and no CreateTemplate call follows (including late, after timeout).
  • Existing non-files Create tests stay green.

Maintained E2E

  • Create the template inside the driver, after its setup steps, instead of in the preceding CI step. Immediately afterwards make one readiness assertion (active version import succeeded) and create the workspace, with no polling or delay in between.
  • Repeated client-side kubectl apply of identical canonical template and workspace manifests, before the lifecycle mutations and with desired spec.running unchanged: 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

…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>
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 23, 2026

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review Completed 2026-09-23T09:59:50.682737Z f5bf93b PR opened
🔒 Security Review Completed 2026-09-23T10:14:30.485809Z f5bf93b Manual request
ℹ️ 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" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@ThomasK33

Copy link
Copy Markdown
Member Author

@codex security review

@ThomasK33

ThomasK33 commented Sep 23, 2026

Copy link
Copy Markdown
Member Author

#105 runtime evidence on f5bf93b7

The required E2E (Kind + CNPG + Templates) job passed on the exact PR head.

  • Run: 35845782860, attempt 1, job 107132099489, conclusion success; Docs run 35845782807 passed. Source: f5bf93b7227c813c0976fec7fd76a31b9ee1ddbd. Backend: Coder v2.37.2+eb69e27. Image identity: built and serving image IDs are both sha256:beedb26a…4c4c.
  • Template readiness: the driver created the template with kubectl apply, then with no polling read the active version's import job as succeeded and created a workspace from the template right away. The deterministic proof is the unit test that holds the import pending; the E2E check is supporting evidence on a real backend.
  • Repeated apply: before any lifecycle mutation, kubectl apply of the identical template and workspace manifests succeeded. The template kept its ID, active version and a version count of 1, and the workspace kept its UID, latest build, a build count of 1, and status running. The receipt shows apply_failure=none.
  • All cases: 12 of 12 passed, including the existing watch, rename, 🤖 fix: make workspace resource versions reflect backend changes #109 stale-token, delete, and recreate cases.

Media

Both files are a terminal replay of the recorded GitHub Actions job log (run 35845782860, job 107132099489, head f5bf93b7), not a live run. The replay prints the lifecycle step's log lines unchanged under a header with the job log's SHA-256 (02d0e3bc…2292). Playback timing does not reflect the CI run. SHA-256 prefixes: screenshot 56969149…, video 29fb6fd3…. The CI log linked above is the primary evidence.

Replay of the E2E lifecycle step log for PR 116, final screen

pr116-e2e-replay.webm

Generated with xum • Model: anthropic:claude-opus-5-5 • Thinking: xhigh

@chatgpt-codex-connector

Copy link
Copy Markdown

🛡️ Codex Security Review

Security review completed. No security issues were found in this pull request.

Reviewed commit: f5bf93b722

View security finding report

Only the user who started this review can view the report in Codex.

ℹ️ About Codex security reviews in GitHub

This is an experimental Codex feature. Security reviews are triggered when:

  • You comment "@codex security review"
  • A regular code review gets triggered (for example, "@codex review" or when a PR is opened), and you’re opted in so security review runs alongside code review

Once complete, Codex will leave suggestions, or a comment if no findings are found.

@ThomasK33
ThomasK33 added this pull request to the merge queue Sep 23, 2026
Merged via the queue into main with commit c58c851 Sep 23, 2026
13 checks passed
@ThomasK33
ThomasK33 deleted the fix/template-create-import-wait branch September 23, 2026 10:24
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