Skip to content

Commit 21eb14d

Browse files
authored
🤖 fix: wait for successful template builds before promotion (#70)
## Summary Fix aggregated CoderTemplate update behavior so new template versions are promoted to active only after the build succeeds, and export a reusable aggregated template example for smoke/regression testing. ## Background Template updates were creating new Coder template versions, but promotion could race the provisioner build and fail with a "running build" error. That left active version unchanged, which matched the issue observed during testing. ## Implementation - Added template build wait logic in aggregated template storage: - waits for template version job to reach "succeeded" - fails on "failed" / "canceled" - uses backoff polling with configurable env-based settings - Added/expanded tests in internal/aggregated/storage/storage_test.go: - coverage for pending-build wait before promotion - env configuration default/validation coverage - mock API behavior for build-state gated promotion - Added docs for tuning knobs in docs/how-to/deploy-aggregated-apiserver.md - Added reusable aggregated template example: - examples/aggregated/codertemplate-smoke-scratch.yaml - examples/aggregated/README.md - linked from root README.md ## Validation - make verify-vendor - make test - make build - make lint - make docs-check - kubectl apply --dry-run=client -f examples/aggregated/codertemplate-smoke-scratch.yaml ## Risks - Template update requests now block until build completes or timeout; this increases latency for update calls by design. - Misconfigured wait env vars now fail fast with explicit assertion errors (intentional defensive behavior). --- _Generated with `mux` • Model: `openai:gpt-5.3-codex` • Thinking: `xhigh` • Cost: `$0.00`_ <!-- mux-attribution: model=openai:gpt-5.3-codex thinking=xhigh costs=0.00 -->
1 parent 1261148 commit 21eb14d

8 files changed

Lines changed: 966 additions & 6 deletions

File tree

‎README.md‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,8 @@ make docs-serve
7575

7676
- [`examples/cloudnativepg/`](examples/cloudnativepg/) — Deploy a `CoderControlPlane` with a CloudNativePG-managed PostgreSQL backend.
7777

78+
- [`examples/aggregated/`](examples/aggregated/) - Reusable `CoderTemplate` manifests for aggregated API testing.
79+
7880
## KIND development cluster (k9s demos)
7981

8082
Bootstrap a KIND cluster and install CRDs/RBAC (**this switches current kubectl context**):

‎docs/how-to/deploy-aggregated-apiserver.md‎

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,30 @@ kubectl get codertemplates.aggregation.coder.com -A
9090
kubectl logs -n coder-system deploy/coder-k8s
9191
```
9292

93+
## Template build wait tuning
94+
95+
When updating `CoderTemplate.spec.files`, the aggregated API server now waits for
96+
Coder to finish building the new template version before promoting it active.
97+
98+
The wait behavior is configurable via environment variables on the
99+
`coder-k8s` deployment:
100+
101+
- `CODER_K8S_TEMPLATE_BUILD_WAIT_TIMEOUT` (default: `25m`)
102+
- `CODER_K8S_TEMPLATE_BUILD_BACKOFF_AFTER` (default: `2m`)
103+
- `CODER_K8S_TEMPLATE_BUILD_INITIAL_POLL_INTERVAL` (default: `2s`)
104+
- `CODER_K8S_TEMPLATE_BUILD_MAX_POLL_INTERVAL` (default: `10s`)
105+
106+
- Aggregated API request timeout defaults to `30m`; keep it at or above the build wait timeout.
107+
108+
- `CODER_K8S_TEMPLATE_BUILD_WAIT_TIMEOUT` values above `30m` are rejected, because they cannot exceed the API request timeout.
109+
110+
Behavior:
111+
112+
- Polls at the initial interval for the first 2 minutes.
113+
- After that, poll interval doubles up to the max poll interval.
114+
- Fails if the version build ends in `failed`/`canceled` or the total wait
115+
timeout is exceeded.
116+
93117
## TLS note
94118

95119
`deploy/apiserver-apiservice.yaml` uses `insecureSkipTLSVerify: true` for development convenience.
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# Aggregated API examples
2+
3+
This directory contains example `CoderTemplate` resources for exercising the
4+
aggregated API server (`aggregation.coder.com/v1alpha1`).
5+
6+
## Smoke template export
7+
8+
- [`codertemplate-smoke-scratch.yaml`](./codertemplate-smoke-scratch.yaml)
9+
is an exported template manifest captured from a live smoke-test cluster.
10+
- It is intended as a reusable baseline for template create/update API testing.
11+
12+
Apply it with:
13+
14+
```bash
15+
kubectl apply -f examples/aggregated/codertemplate-smoke-scratch.yaml
16+
```
17+
18+
If you are updating an already-existing object with `kubectl replace`, include
19+
the latest `metadata.resourceVersion` from `kubectl get -o yaml`.
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
apiVersion: aggregation.coder.com/v1alpha1
2+
kind: CoderTemplate
3+
metadata:
4+
name: coder.smoke-scratch
5+
namespace: coder
6+
spec:
7+
organization: coder
8+
# Leave blank for create-from-files workflows; the API creates a new template
9+
# version and promotes it active after build succeeds.
10+
versionID: ""
11+
files:
12+
README.md: |
13+
---
14+
display_name: Scratch
15+
description: A minimal starter template for Coder
16+
icon: ../../../site/static/emojis/1f4e6.png
17+
maintainer_github: coder
18+
verified: true
19+
tags: []
20+
---
21+
22+
# A minimal Scaffolding for a Coder Template
23+
24+
Use this starter template as a basis to create your own unique template from scratch.
25+
main.tf: |
26+
terraform {
27+
required_providers {
28+
coder = {
29+
source = "coder/coder"
30+
}
31+
}
32+
}
33+
34+
data "coder_provisioner" "me" {}
35+
36+
data "coder_workspace" "me" {}
37+
38+
resource "coder_agent" "main" {
39+
arch = data.coder_provisioner.me.arch
40+
os = data.coder_provisioner.me.os
41+
42+
metadata {
43+
display_name = "CPU Usage"
44+
key = "0_cpu_usage"
45+
script = "coder stat cpu"
46+
interval = 10
47+
timeout = 1
48+
}
49+
50+
metadata {
51+
display_name = "RAM Usage"
52+
key = "1_ram_usage"
53+
script = "coder stat mem"
54+
interval = 10
55+
timeout = 1
56+
}
57+
}
58+
59+
# Use this to set environment variables in your workspace
60+
# details: https://registry.terraform.io/providers/coder/coder/latest/docs/resources/env
61+
resource "coder_env" "welcome_message" {
62+
agent_id = coder_agent.main.id
63+
name = "WELCOME_MESSAGE"
64+
value = "Welcome to your Coder workspace!"
65+
}
66+
67+
# Adds code-server
68+
# See all available modules at https://registry.coder.com/modules
69+
module "code-server" {
70+
count = data.coder_workspace.me.start_count
71+
source = "registry.coder.com/coder/code-server/coder"
72+
73+
# This ensures that the latest non-breaking version of the module gets downloaded, you can also pin the module version to prevent breaking changes in production.
74+
version = "~> 1.0"
75+
76+
agent_id = coder_agent.main.id
77+
}
78+
79+
# Runs a script at workspace start/stop or on a cron schedule
80+
# details: https://registry.terraform.io/providers/coder/coder/latest/docs/resources/script
81+
resource "coder_script" "startup_script" {
82+
agent_id = coder_agent.main.id
83+
display_name = "Startup Script"
84+
script = <<-EOF
85+
#!/bin/sh
86+
set -e
87+
# Run programs at workspace startup
88+
EOF
89+
run_on_start = true
90+
start_blocks_login = true
91+
}

0 commit comments

Comments
 (0)