a73x

da27bc64

build: adopt Go 1.26 idioms

a73x   2026-07-26 10:00

Commit message
build: adopt Go 1.26 idioms

golang.org/x/tools/gopls modernize -fix across the module: for-int loops
become range-over-int, hand-rolled atomics become atomic.Int64, errors.As
narrows to errors.AsType, plus maps.Copy, slices.Contains, strings.CutPrefix,
strings.SplitSeq, the max builtin, and reflect.TypeFor. Generated code
(internal/pb) is excluded.

internal/agent/hostinfo/hostinfo.go
Old New
@@ -89,7 +89,7 @@ func memAvailableMB(si *syscall.Sysinfo_t) int64 {
89 // parseOSRelease extracts ID, PRETTY_NAME, VERSION_ID from /etc/os-release 89 // parseOSRelease extracts ID, PRETTY_NAME, VERSION_ID from /etc/os-release
90 // (KEY=VALUE lines, values optionally double-quoted). 90 // (KEY=VALUE lines, values optionally double-quoted).
91 func parseOSRelease(s string) (id, pretty, version string) { 91 func parseOSRelease(s string) (id, pretty, version string) {
92 for _, line := range strings.Split(s, "\n") { 92 for line := range strings.SplitSeq(s, "\n") {
93 k, v, ok := strings.Cut(strings.TrimSpace(line), "=") 93 k, v, ok := strings.Cut(strings.TrimSpace(line), "=")
94 if !ok { 94 if !ok {
95 continue 95 continue
@@ -110,7 +110,7 @@ func parseOSRelease(s string) (id, pretty, version string) {
110 // parseCPUModel returns the first "model name" value from /proc/cpuinfo, or "" 110 // parseCPUModel returns the first "model name" value from /proc/cpuinfo, or ""
111 // (e.g. on arm, which uses different fields). 111 // (e.g. on arm, which uses different fields).
112 func parseCPUModel(s string) string { 112 func parseCPUModel(s string) string {
113 for _, line := range strings.Split(s, "\n") { 113 for line := range strings.SplitSeq(s, "\n") {
114 if k, v, ok := strings.Cut(line, ":"); ok && strings.TrimSpace(k) == "model name" { 114 if k, v, ok := strings.Cut(line, ":"); ok && strings.TrimSpace(k) == "model name" {
115 return strings.TrimSpace(v) 115 return strings.TrimSpace(v)
116 } 116 }
@@ -121,7 +121,7 @@ func parseCPUModel(s string) string {
121 // parseMemAvailableKB returns the MemAvailable value in kB and whether it was 121 // parseMemAvailableKB returns the MemAvailable value in kB and whether it was
122 // present. 122 // present.
123 func parseMemAvailableKB(s string) (int64, bool) { 123 func parseMemAvailableKB(s string) (int64, bool) {
124 for _, line := range strings.Split(s, "\n") { 124 for line := range strings.SplitSeq(s, "\n") {
125 if !strings.HasPrefix(line, "MemAvailable:") { 125 if !strings.HasPrefix(line, "MemAvailable:") {
126 continue 126 continue
127 } 127 }
internal/agent/imagecache/imagecache_test.go
Old New
@@ -239,10 +239,10 @@ func TestConcurrentEnsureFetchesOnce(t *testing.T) {
239 sum := sha256.Sum256(body) 239 sum := sha256.Sum256(body)
240 sha := hex.EncodeToString(sum[:]) 240 sha := hex.EncodeToString(sum[:])
241 241
242 var downloads int32 242 var downloads atomic.Int32
243 release := make(chan struct{}) 243 release := make(chan struct{})
244 srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 244 srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
245 atomic.AddInt32(&downloads, 1) 245 downloads.Add(1)
246 <-release // hold every request open so the callers genuinely overlap 246 <-release // hold every request open so the callers genuinely overlap
247 w.Write(body) 247 w.Write(body)
248 })) 248 }))
@@ -253,7 +253,7 @@ func TestConcurrentEnsureFetchesOnce(t *testing.T) {
253 const callers = 4 253 const callers = 4
254 paths := make(chan string, callers) 254 paths := make(chan string, callers)
255 errs := make(chan error, callers) 255 errs := make(chan error, callers)
256 for i := 0; i < callers; i++ { 256 for range callers {
257 go func() { 257 go func() {
258 p, err := c.Ensure(context.Background(), srv.URL, sha) 258 p, err := c.Ensure(context.Background(), srv.URL, sha)
259 if err != nil { 259 if err != nil {
@@ -265,13 +265,13 @@ func TestConcurrentEnsureFetchesOnce(t *testing.T) {
265 } 265 }
266 266
267 // Let them all arrive at the singleflight, then let the one download finish. 267 // Let them all arrive at the singleflight, then let the one download finish.
268 require.Eventually(t, func() bool { return atomic.LoadInt32(&downloads) >= 1 }, 268 require.Eventually(t, func() bool { return downloads.Load() >= 1 },
269 2*time.Second, 10*time.Millisecond, "no caller reached the server") 269 2*time.Second, 10*time.Millisecond, "no caller reached the server")
270 time.Sleep(100 * time.Millisecond) // any un-deduplicated caller would arrive by now 270 time.Sleep(100 * time.Millisecond) // any un-deduplicated caller would arrive by now
271 close(release) 271 close(release)
272 272
273 want := filepath.Join(c.dir, sha+".raw") 273 want := filepath.Join(c.dir, sha+".raw")
274 for i := 0; i < callers; i++ { 274 for range callers {
275 select { 275 select {
276 case err := <-errs: 276 case err := <-errs:
277 t.Fatalf("Ensure failed: %v", err) 277 t.Fatalf("Ensure failed: %v", err)
@@ -281,7 +281,7 @@ func TestConcurrentEnsureFetchesOnce(t *testing.T) {
281 t.Fatal("Ensure never returned") 281 t.Fatal("Ensure never returned")
282 } 282 }
283 } 283 }
284 assert.Equal(t, int32(1), atomic.LoadInt32(&downloads), 284 assert.Equal(t, int32(1), downloads.Load(),
285 "concurrent Ensure calls for one image must share a single download") 285 "concurrent Ensure calls for one image must share a single download")
286 286
287 data, err := os.ReadFile(want) 287 data, err := os.ReadFile(want)
internal/agent/reconcile/quota_test.go
Old New
@@ -64,7 +64,7 @@ func TestQuotaRefusalIsNonTerminal(t *testing.T) {
64 64
65 // vm2 (2 vcpus) would push the host to 4 > 2 — blocked. Repeat well past 65 // vm2 (2 vcpus) would push the host to 4 > 2 — blocked. Repeat well past
66 // MaxCreateAttempts (3): quota refusal must NOT consume the retry budget. 66 // MaxCreateAttempts (3): quota refusal must NOT consume the retry budget.
67 for i := 0; i < 5; i++ { 67 for range 5 {
68 f.step(snap(2, vm("vm1", withRes(2, 512, 5)), vm("vm2", withRes(2, 512, 5)))) 68 f.step(snap(2, vm("vm1", withRes(2, 512, 5)), vm("vm2", withRes(2, 512, 5))))
69 } 69 }
70 assert.Equal(t, []string{"vm1"}, f.prov.booted, "vm2 still blocked, vm1 untouched") 70 assert.Equal(t, []string{"vm1"}, f.prov.booted, "vm2 still blocked, vm1 untouched")
@@ -94,7 +94,7 @@ func TestQuotaFreedByQuarantineEventuallyBootsTheWaitingVM(t *testing.T) {
94 // vm1 and releases its compute, so tick 2 always admits vm2. The third is 94 // vm1 and releases its compute, so tick 2 always admits vm2. The third is
95 // slack, not a flake bound. 95 // slack, not a flake bound.
96 var rep *pb.ActualStateReport 96 var rep *pb.ActualStateReport
97 for i := 0; i < 3; i++ { 97 for range 3 {
98 rep = f.step(snap(2, 98 rep = f.step(snap(2,
99 tombstoned(vm("vm1", withRes(2, 512, 5))), 99 tombstoned(vm("vm1", withRes(2, 512, 5))),
100 vm("vm2", withRes(2, 512, 5)))) 100 vm("vm2", withRes(2, 512, 5))))
internal/agent/reconcile/reconcile_test.go
Old New
@@ -338,7 +338,7 @@ func TestFenceRefusesLowerEpochWithoutActing(t *testing.T) {
338 func TestCreateRetryIsBoundedThenTerminalFailed(t *testing.T) { 338 func TestCreateRetryIsBoundedThenTerminalFailed(t *testing.T) {
339 f := setup(t) 339 f := setup(t)
340 f.prov.prepErr = assert.AnError 340 f.prov.prepErr = assert.AnError
341 for i := 0; i < 3; i++ { 341 for range 3 {
342 f.step(snap(1, vm("vm1"))) 342 f.step(snap(1, vm("vm1")))
343 } 343 }
344 f.prov.prepErr = nil // even if the cause clears... 344 f.prov.prepErr = nil // even if the cause clears...
@@ -460,7 +460,7 @@ func TestUndeleteClearsQuarantineSoNextDeleteGetsFullGrace(t *testing.T) {
460 func TestEditedSpecResetsCreateAttempts(t *testing.T) { 460 func TestEditedSpecResetsCreateAttempts(t *testing.T) {
461 f := setup(t) 461 f := setup(t)
462 f.prov.prepErr = assert.AnError 462 f.prov.prepErr = assert.AnError
463 for i := 0; i < 3; i++ { 463 for range 3 {
464 f.step(snap(1, vm("vm1"))) 464 f.step(snap(1, vm("vm1")))
465 } 465 }
466 f.prov.prepErr = nil 466 f.prov.prepErr = nil
internal/agent/reconcile/worker_test.go
Old New
@@ -86,7 +86,7 @@ func TestVMsReconcileConcurrently(t *testing.T) {
86 86
87 stepNoWait(t, f, snap(1, vm("vm1"), vm("vm2"))) 87 stepNoWait(t, f, snap(1, vm("vm1"), vm("vm2")))
88 88
89 for i := 0; i < 2; i++ { 89 for range 2 {
90 select { 90 select {
91 case <-arrived: 91 case <-arrived:
92 case <-time.After(2 * time.Second): 92 case <-time.After(2 * time.Second):
@@ -179,7 +179,7 @@ func TestCreateConcurrencyIsCapped(t *testing.T) {
179 f.eng.Step(context.Background(), snap(1, vm("vm1"), vm("vm2"), vm("vm3"))) 179 f.eng.Step(context.Background(), snap(1, vm("vm1"), vm("vm2"), vm("vm3")))
180 180
181 // Two VMs take the available slots. 181 // Two VMs take the available slots.
182 for i := 0; i < 2; i++ { 182 for range 2 {
183 select { 183 select {
184 case <-entered: 184 case <-entered:
185 case <-time.After(2 * time.Second): 185 case <-time.After(2 * time.Second):
@@ -207,7 +207,7 @@ func TestCreateConcurrencyZeroIsUnlimited(t *testing.T) {
207 f.eng.Images = wedge(arrived, release) 207 f.eng.Images = wedge(arrived, release)
208 208
209 f.eng.Step(context.Background(), snap(1, vm("vm1"), vm("vm2"), vm("vm3"))) 209 f.eng.Step(context.Background(), snap(1, vm("vm1"), vm("vm2"), vm("vm3")))
210 for i := 0; i < 3; i++ { 210 for range 3 {
211 select { 211 select {
212 case <-arrived: 212 case <-arrived:
213 case <-time.After(2 * time.Second): 213 case <-time.After(2 * time.Second):
internal/agent/seed/seed.go
Old New
@@ -136,7 +136,7 @@ func vendorDataDoc(p Params) string {
136 // cloud-init ssh_keys map (ed25519_private / ed25519_certificate). 136 // cloud-init ssh_keys map (ed25519_private / ed25519_certificate).
137 func writeBlockScalar(b *strings.Builder, indent, key, content string) { 137 func writeBlockScalar(b *strings.Builder, indent, key, content string) {
138 b.WriteString(indent + key + ": |\n") 138 b.WriteString(indent + key + ": |\n")
139 for _, line := range strings.Split(strings.TrimRight(content, "\n"), "\n") { 139 for line := range strings.SplitSeq(strings.TrimRight(content, "\n"), "\n") {
140 b.WriteString(indent + " " + line + "\n") 140 b.WriteString(indent + " " + line + "\n")
141 } 141 }
142 } 142 }
@@ -149,7 +149,7 @@ func writeFileBlock(b *strings.Builder, path, content string) {
149 b.WriteString(" - path: " + path + "\n") 149 b.WriteString(" - path: " + path + "\n")
150 b.WriteString(" permissions: '0644'\n") 150 b.WriteString(" permissions: '0644'\n")
151 b.WriteString(" content: |\n") 151 b.WriteString(" content: |\n")
152 for _, line := range strings.Split(strings.TrimRight(content, "\n"), "\n") { 152 for line := range strings.SplitSeq(strings.TrimRight(content, "\n"), "\n") {
153 b.WriteString(" " + line + "\n") 153 b.WriteString(" " + line + "\n")
154 } 154 }
155 } 155 }
internal/agent/serialpump/serialpump_test.go
Old New
@@ -114,8 +114,7 @@ func TestInputForwardedToSocket(t *testing.T) {
114 guest := ch.conn(t) 114 guest := ch.conn(t)
115 115
116 viewer, _, in := pipeViewer() 116 viewer, _, in := pipeViewer()
117 ctx, cancel := context.WithCancel(context.Background()) 117 ctx := t.Context()
118 defer cancel()
119 go m.Attach(ctx, "vm1", viewer, nil) //nolint:errcheck 118 go m.Attach(ctx, "vm1", viewer, nil) //nolint:errcheck
120 119
121 _, err := in.Write([]byte("ls\r")) 120 _, err := in.Write([]byte("ls\r"))
@@ -141,8 +140,7 @@ func TestOnReadyFiresBeforeBacklog(t *testing.T) {
141 140
142 viewer, out, _ := pipeViewer() 141 viewer, out, _ := pipeViewer()
143 ready := make(chan struct{}) 142 ready := make(chan struct{})
144 ctx, cancel := context.WithCancel(context.Background()) 143 ctx := t.Context()
145 defer cancel()
146 go m.Attach(ctx, "vm1", viewer, func() error { close(ready); return nil }) //nolint:errcheck 144 go m.Attach(ctx, "vm1", viewer, func() error { close(ready); return nil }) //nolint:errcheck
147 <-ready // onReady before any viewer write (protocol: reply frame precedes raw bytes) 145 <-ready // onReady before any viewer write (protocol: reply frame precedes raw bytes)
148 assert.Equal(t, "X", string(readN(t, out, 1))) 146 assert.Equal(t, "X", string(readN(t, out, 1)))
@@ -166,8 +164,7 @@ func TestRingIsBounded(t *testing.T) {
166 }, 5*time.Second, 10*time.Millisecond) 164 }, 5*time.Second, 10*time.Millisecond)
167 165
168 viewer, out, _ := pipeViewer() 166 viewer, out, _ := pipeViewer()
169 ctx, cancel := context.WithCancel(context.Background()) 167 ctx := t.Context()
170 defer cancel()
171 go m.Attach(ctx, "vm1", viewer, nil) //nolint:errcheck 168 go m.Attach(ctx, "vm1", viewer, nil) //nolint:errcheck
172 // Backlog is only the LAST 16 bytes. 169 // Backlog is only the LAST 16 bytes.
173 assert.Equal(t, "456789ABCDEFGHIJ", string(readN(t, out, 16))) 170 assert.Equal(t, "456789ABCDEFGHIJ", string(readN(t, out, 16)))
@@ -223,7 +220,7 @@ func TestSlowViewerIsDroppedNotBlocking(t *testing.T) {
223 // channel overflows, the pump drops the viewer, and Attach's next channel 220 // channel overflows, the pump drops the viewer, and Attach's next channel
224 // receive observes the close and errors out. 221 // receive observes the close and errors out.
225 junk := make([]byte, 4096) 222 junk := make([]byte, 4096)
226 for i := 0; i < 256; i++ { 223 for range 256 {
227 _, err := guest.Write(junk) 224 _, err := guest.Write(junk)
228 require.NoError(t, err) 225 require.NoError(t, err)
229 } 226 }
@@ -255,8 +252,7 @@ func TestFanOutToTwoViewers(t *testing.T) {
255 v2, out2, _ := pipeViewer() 252 v2, out2, _ := pipeViewer()
256 ctx1, cancel1 := context.WithCancel(context.Background()) 253 ctx1, cancel1 := context.WithCancel(context.Background())
257 defer cancel1() 254 defer cancel1()
258 ctx2, cancel2 := context.WithCancel(context.Background()) 255 ctx2 := t.Context()
259 defer cancel2()
260 go m.Attach(ctx1, "vm1", v1, nil) //nolint:errcheck 256 go m.Attach(ctx1, "vm1", v1, nil) //nolint:errcheck
261 go m.Attach(ctx2, "vm1", v2, nil) //nolint:errcheck 257 go m.Attach(ctx2, "vm1", v2, nil) //nolint:errcheck
262 258
internal/agent/syncclient/client_test.go
Old New
@@ -37,7 +37,7 @@ func (noopProv) Running(string) bool { retur
37 type noopNet struct{} 37 type noopNet struct{}
38 38
39 func (noopNet) CreateTap(context.Context, string, string) error { return nil } 39 func (noopNet) CreateTap(context.Context, string, string) error { return nil }
40 func (noopNet) DeleteTap(context.Context, string) error { return nil } 40 func (noopNet) DeleteTap(context.Context, string) error { return nil }
41 func (noopNet) ReserveIP(string) (string, error) { 41 func (noopNet) ReserveIP(string) (string, error) {
42 return "10.77.1.2", nil 42 return "10.77.1.2", nil
43 } 43 }
@@ -58,7 +58,7 @@ func TestAdvertisedCapacityComputesOnce(t *testing.T) {
58 return &pb.Capacity{Vcpus: 8, MemMb: 16384, DiskGb: 500} 58 return &pb.Capacity{Vcpus: 8, MemMb: 16384, DiskGb: 500}
59 } 59 }
60 c := &Client{MaxVCPUs: 2} // cap vCPUs to prove the clamp runs each call 60 c := &Client{MaxVCPUs: 2} // cap vCPUs to prove the clamp runs each call
61 for i := 0; i < 5; i++ { 61 for range 5 {
62 got := c.advertisedCapacity("/state") 62 got := c.advertisedCapacity("/state")
63 require.Equal(t, int64(2), got.GetVcpus()) 63 require.Equal(t, int64(2), got.GetVcpus())
64 require.Equal(t, int64(16384), got.GetMemMb()) 64 require.Equal(t, int64(16384), got.GetMemMb())
@@ -220,8 +220,7 @@ func TestReconnectAfterDrop(t *testing.T) {
220 require.NoError(t, h.st.CreateVM(store.VM{ID: "vm1", HostID: hostID, Name: "a", 220 require.NoError(t, h.st.CreateVM(store.VM{ID: "vm1", HostID: hostID, Name: "a",
221 ImageURL: "u", ImageSHA256: "s", VCPUs: 1, MemMB: 512, DiskGB: 5, PowerState: "stopped"})) 221 ImageURL: "u", ImageSHA256: "s", VCPUs: 1, MemMB: 512, DiskGB: 5, PowerState: "stopped"}))
222 222
223 ctx, cancel := context.WithCancel(context.Background()) 223 ctx := t.Context()
224 defer cancel()
225 go c.Run(ctx) 224 go c.Run(ctx)
226 225
227 // First connection: wait for the host to register a report. 226 // First connection: wait for the host to register a report.
internal/agent/syncclient/leak_test.go
Old New
@@ -88,8 +88,7 @@ func TestSessionNoWorkerLeakAcrossReconnects(t *testing.T) {
88 defer stop() 88 defer stop()
89 c := newClient(t, addr, fp, "host-x", "host-x.deadbeef") 89 c := newClient(t, addr, fp, "host-x", "host-x.deadbeef")
90 90
91 ctx, cancel := context.WithCancel(context.Background()) 91 ctx := t.Context()
92 defer cancel()
93 92
94 // Each session connects, gets one snapshot, then the server drops it, so 93 // Each session connects, gets one snapshot, then the server drops it, so
95 // session() returns promptly. The parent ctx is never cancelled — precisely 94 // session() returns promptly. The parent ctx is never cancelled — precisely
@@ -110,7 +109,7 @@ func TestSessionNoWorkerLeakAcrossReconnects(t *testing.T) {
110 base := settleGoroutines(0, 3*time.Second) // target 0 => returns settled count 109 base := settleGoroutines(0, 3*time.Second) // target 0 => returns settled count
111 110
112 const cycles = 10 111 const cycles = 10
113 for i := 0; i < cycles; i++ { 112 for range cycles {
114 runOne() 113 runOne()
115 } 114 }
116 115
internal/arch/execwalk_test.go
Old New
@@ -1,6 +1,7 @@
1 package arch 1 package arch
2 2
3 import ( 3 import (
4 "slices"
4 "sort" 5 "sort"
5 "strings" 6 "strings"
6 "testing" 7 "testing"
@@ -66,12 +67,7 @@ func TestExecViolationsDetectsWrappers(t *testing.T) {
66 // package that wraps os/exec and is imported by the guarded plane. 67 // package that wraps os/exec and is imported by the guarded plane.
67 func execViolations(graph map[string][]string, module, prefix string, allowed map[string]bool) map[string][]string { 68 func execViolations(graph map[string][]string, module, prefix string, allowed map[string]bool) map[string][]string {
68 importsExec := func(pkg string) bool { 69 importsExec := func(pkg string) bool {
69 for _, d := range graph[pkg] { 70 return slices.Contains(graph[pkg], "os/exec")
70 if d == "os/exec" {
71 return true
72 }
73 }
74 return false
75 } 71 }
76 72
77 out := map[string][]string{} 73 out := map[string][]string{}
internal/cloudinit/multipart.go
Old New
@@ -251,7 +251,7 @@ func appendToMultipart(userData, key string) (string, error) {
251 251
252 // firstNonBlankLine returns the first line with non-whitespace content, trimmed. 252 // firstNonBlankLine returns the first line with non-whitespace content, trimmed.
253 func firstNonBlankLine(s string) string { 253 func firstNonBlankLine(s string) string {
254 for _, line := range strings.Split(s, "\n") { 254 for line := range strings.SplitSeq(s, "\n") {
255 if t := strings.TrimSpace(line); t != "" { 255 if t := strings.TrimSpace(line); t != "" {
256 return t 256 return t
257 } 257 }
internal/mcpserver/sshrun.go
Old New
@@ -83,8 +83,7 @@ func (r *Runner) Exec(ctx context.Context, vmName, cmd string, timeout time.Dura
83 case err = <-done: 83 case err = <-done:
84 } 84 }
85 res := ExecResult{Stdout: stdout.String(), Stderr: stderr.String(), Truncated: stdout.truncated || stderr.truncated} 85 res := ExecResult{Stdout: stdout.String(), Stderr: stderr.String(), Truncated: stdout.truncated || stderr.truncated}
86 var exitErr *ssh.ExitError 86 if exitErr, ok := errors.AsType[*ssh.ExitError](err); ok {
87 if errors.As(err, &exitErr) {
88 res.ExitCode = exitErr.ExitStatus() 87 res.ExitCode = exitErr.ExitStatus()
89 return res, nil 88 return res, nil
90 } 89 }
internal/mcpserver/tools.go
Old New
@@ -182,10 +182,7 @@ func (t *Tools) VMCreate(ctx context.Context, in VMCreateIn) (VMCreateOut, error
182 // cloud-init is transparent as long as the new key is a CA-signed cert. 182 // cloud-init is transparent as long as the new key is a CA-signed cert.
183 // Give the command the time left in the shared budget (floored so a 183 // Give the command the time left in the shared budget (floored so a
184 // nearly-exhausted budget still gets a real chance). 184 // nearly-exhausted budget still gets a real chance).
185 remaining := time.Until(deadline) 185 remaining := max(time.Until(deadline), 30*time.Second)
186 if remaining < 30*time.Second {
187 remaining = 30 * time.Second
188 }
189 res, execErr := t.Runner.Exec(ctx, created.Name, "cloud-init status --wait", remaining) 186 res, execErr := t.Runner.Exec(ctx, created.Name, "cloud-init status --wait", remaining)
190 if execErr == nil { 187 if execErr == nil {
191 if res.ExitCode != 0 { 188 if res.ExitCode != 0 {
internal/mcpserver/tools_test.go
Old New
@@ -254,7 +254,6 @@ func TestExecUnknownVM(t *testing.T) {
254 254
255 func TestExecNotReadyVMRejectedWithoutCallingRunner(t *testing.T) { 255 func TestExecNotReadyVMRejectedWithoutCallingRunner(t *testing.T) {
256 for _, lifecycle := range []string{"creating", "stopped", "failed", "deleting", ""} { 256 for _, lifecycle := range []string{"creating", "stopped", "failed", "deleting", ""} {
257 lifecycle := lifecycle
258 t.Run(lifecycle, func(t *testing.T) { 257 t.Run(lifecycle, func(t *testing.T) {
259 api := &fakeToolsAPI{vms: []client.VM{{ID: "abc123", Name: "web-1", Lifecycle: lifecycle}}} 258 api := &fakeToolsAPI{vms: []client.VM{{ID: "abc123", Name: "web-1", Lifecycle: lifecycle}}}
260 run := &fakeRunner{} 259 run := &fakeRunner{}
internal/server/api/api_test.go
Old New
@@ -3,6 +3,7 @@ package api
3 import ( 3 import (
4 "bytes" 4 "bytes"
5 "encoding/json" 5 "encoding/json"
6 "maps"
6 "net/http" 7 "net/http"
7 "net/http/httptest" 8 "net/http/httptest"
8 "strings" 9 "strings"
@@ -388,9 +389,7 @@ func TestCreateVMResourceValidation(t *testing.T) {
388 for _, tc := range tests { 389 for _, tc := range tests {
389 t.Run(tc.name, func(t *testing.T) { 390 t.Run(tc.name, func(t *testing.T) {
390 body := map[string]any{"host_id": out["host_id"]} 391 body := map[string]any{"host_id": out["host_id"]}
391 for k, v := range tc.body { 392 maps.Copy(body, tc.body)
392 body[k] = v
393 }
394 resp := do(t, "POST", ts.URL+"/api/v1/vms", "admintok", body) 393 resp := do(t, "POST", ts.URL+"/api/v1/vms", "admintok", body)
395 assert.Equal(t, tc.wantStatus, resp.StatusCode) 394 assert.Equal(t, tc.wantStatus, resp.StatusCode)
396 }) 395 })
@@ -548,7 +547,7 @@ func TestEnrollRateLimited(t *testing.T) {
548 ts, _, _ := testServer(t) 547 ts, _, _ := testServer(t)
549 548
550 var got429 bool 549 var got429 bool
551 for i := 0; i < enrollBurst+3; i++ { 550 for range enrollBurst + 3 {
552 resp := do(t, "POST", ts.URL+"/api/v1/enroll", "", map[string]string{ 551 resp := do(t, "POST", ts.URL+"/api/v1/enroll", "", map[string]string{
553 "token": "bogus", "name": "x", "os": "linux", "arch": "amd64", "provisioner": "cloudhv"}) 552 "token": "bogus", "name": "x", "os": "linux", "arch": "amd64", "provisioner": "cloudhv"})
554 if resp.StatusCode == http.StatusTooManyRequests { 553 if resp.StatusCode == http.StatusTooManyRequests {
internal/server/api/ratelimit_test.go
Old New
@@ -14,7 +14,7 @@ func TestLimiterRefillAndCap(t *testing.T) {
14 now := time.Unix(1_750_000_000, 0) 14 now := time.Unix(1_750_000_000, 0)
15 l := newIPLimiter(func() time.Time { return now }) 15 l := newIPLimiter(func() time.Time { return now })
16 16
17 for i := 0; i < enrollBurst; i++ { 17 for i := range enrollBurst {
18 assert.True(t, l.allow("10.0.0.1"), "burst request %d", i) 18 assert.True(t, l.allow("10.0.0.1"), "burst request %d", i)
19 } 19 }
20 assert.False(t, l.allow("10.0.0.1"), "burst exhausted") 20 assert.False(t, l.allow("10.0.0.1"), "burst exhausted")
@@ -25,7 +25,7 @@ func TestLimiterRefillAndCap(t *testing.T) {
25 25
26 // A long idle period refills to the cap, not beyond. 26 // A long idle period refills to the cap, not beyond.
27 now = now.Add(24 * time.Hour) 27 now = now.Add(24 * time.Hour)
28 for i := 0; i < enrollBurst; i++ { 28 for i := range enrollBurst {
29 assert.True(t, l.allow("10.0.0.1"), "cap request %d", i) 29 assert.True(t, l.allow("10.0.0.1"), "cap request %d", i)
30 } 30 }
31 assert.False(t, l.allow("10.0.0.1"), "cap enforced") 31 assert.False(t, l.allow("10.0.0.1"), "cap enforced")
@@ -38,7 +38,7 @@ func TestLimiterPruneAndFailOpen(t *testing.T) {
38 now := time.Unix(1_750_000_000, 0) 38 now := time.Unix(1_750_000_000, 0)
39 l := newIPLimiter(func() time.Time { return now }) 39 l := newIPLimiter(func() time.Time { return now })
40 40
41 for i := 0; i < maxLimiterEntries; i++ { 41 for i := range maxLimiterEntries {
42 l.allow(fmt.Sprintf("10.%d.%d.%d", i>>16&0xff, i>>8&0xff, i&0xff)) 42 l.allow(fmt.Sprintf("10.%d.%d.%d", i>>16&0xff, i>>8&0xff, i&0xff))
43 } 43 }
44 assert.Len(t, l.buckets, maxLimiterEntries) 44 assert.Len(t, l.buckets, maxLimiterEntries)
@@ -73,7 +73,7 @@ func TestBucketKeyGroupsIPv6BySlash64(t *testing.T) {
73 func TestLimiterSharesBucketAcrossSameSlash64(t *testing.T) { 73 func TestLimiterSharesBucketAcrossSameSlash64(t *testing.T) {
74 now := time.Unix(1_750_000_000, 0) 74 now := time.Unix(1_750_000_000, 0)
75 l := newIPLimiter(func() time.Time { return now }) 75 l := newIPLimiter(func() time.Time { return now })
76 for i := 0; i < enrollBurst; i++ { 76 for range enrollBurst {
77 assert.True(t, l.allow(bucketKey("2001:db8::1"))) 77 assert.True(t, l.allow(bucketKey("2001:db8::1")))
78 } 78 }
79 assert.False(t, l.allow(bucketKey("2001:db8::2")), "same /64: bucket shared") 79 assert.False(t, l.allow(bucketKey("2001:db8::2")), "same /64: bucket shared")
internal/server/api/snapshot_hub_test.go
Old New
@@ -38,14 +38,14 @@ func TestSnapshotHubCloseIdempotent(t *testing.T) {
38 // and a wake drives exactly one recompute regardless of how many clients are 38 // and a wake drives exactly one recompute regardless of how many clients are
39 // attached (M clients must NOT cause M builds). 39 // attached (M clients must NOT cause M builds).
40 func TestSnapshotHubSingleBuildFanout(t *testing.T) { 40 func TestSnapshotHubSingleBuildFanout(t *testing.T) {
41 var builds int64 41 var builds atomic.Int64
42 build := func() ([]byte, error) { 42 build := func() ([]byte, error) {
43 n := atomic.AddInt64(&builds, 1) 43 n := builds.Add(1)
44 return []byte(fmt.Sprintf("snap-%d", n)), nil 44 return []byte(fmt.Sprintf("snap-%d", n)), nil
45 } 45 }
46 notif := newNotifier() 46 notif := newNotifier()
47 h := newSnapshotHub(build, notif) // computes the initial snapshot (build #1) 47 h := newSnapshotHub(build, notif) // computes the initial snapshot (build #1)
48 require.Equal(t, int64(1), atomic.LoadInt64(&builds)) 48 require.Equal(t, int64(1), builds.Load())
49 go h.run() 49 go h.run()
50 defer h.Close() 50 defer h.Close()
51 51
@@ -53,7 +53,7 @@ func TestSnapshotHubSingleBuildFanout(t *testing.T) {
53 // triggering their own build. 53 // triggering their own build.
54 const n = 3 54 const n = 3
55 chans := make([]<-chan []byte, n) 55 chans := make([]<-chan []byte, n)
56 for i := 0; i < n; i++ { 56 for i := range n {
57 ch, unsub := h.subscribe() 57 ch, unsub := h.subscribe()
58 defer unsub() 58 defer unsub()
59 chans[i] = ch 59 chans[i] = ch
@@ -67,13 +67,13 @@ func TestSnapshotHubSingleBuildFanout(t *testing.T) {
67 } 67 }
68 } 68 }
69 // Still exactly one build despite three subscribers. 69 // Still exactly one build despite three subscribers.
70 assert.Equal(t, int64(1), atomic.LoadInt64(&builds), "subscribing must not build") 70 assert.Equal(t, int64(1), builds.Load(), "subscribing must not build")
71 71
72 // One wake ⇒ exactly one recompute, fanned identically to all three. 72 // One wake ⇒ exactly one recompute, fanned identically to all three.
73 // (The 1s ticker cannot fire within this sub-second window, so builds is 73 // (The 1s ticker cannot fire within this sub-second window, so builds is
74 // driven solely by the wake here.) 74 // driven solely by the wake here.)
75 notif.notify() 75 notif.notify()
76 waitFor(t, time.Second, func() bool { return atomic.LoadInt64(&builds) == 2 }) 76 waitFor(t, time.Second, func() bool { return builds.Load() == 2 })
77 for i, ch := range chans { 77 for i, ch := range chans {
78 select { 78 select {
79 case b := <-ch: 79 case b := <-ch:
@@ -82,16 +82,16 @@ func TestSnapshotHubSingleBuildFanout(t *testing.T) {
82 t.Fatalf("subscriber %d got no wake snapshot", i) 82 t.Fatalf("subscriber %d got no wake snapshot", i)
83 } 83 }
84 } 84 }
85 assert.Equal(t, int64(2), atomic.LoadInt64(&builds), 85 assert.Equal(t, int64(2), builds.Load(),
86 "one wake must drive exactly one build regardless of client count") 86 "one wake must drive exactly one build regardless of client count")
87 } 87 }
88 88
89 // TestSnapshotHubSuppressesUnchanged pins change-suppression: identical bytes 89 // TestSnapshotHubSuppressesUnchanged pins change-suppression: identical bytes
90 // on recompute produce no push to subscribers. 90 // on recompute produce no push to subscribers.
91 func TestSnapshotHubSuppressesUnchanged(t *testing.T) { 91 func TestSnapshotHubSuppressesUnchanged(t *testing.T) {
92 var builds int64 92 var builds atomic.Int64
93 build := func() ([]byte, error) { 93 build := func() ([]byte, error) {
94 atomic.AddInt64(&builds, 1) 94 builds.Add(1)
95 return []byte("constant"), nil // never changes 95 return []byte("constant"), nil // never changes
96 } 96 }
97 notif := newNotifier() 97 notif := newNotifier()
@@ -110,7 +110,7 @@ func TestSnapshotHubSuppressesUnchanged(t *testing.T) {
110 } 110 }
111 111
112 notif.notify() 112 notif.notify()
113 waitFor(t, time.Second, func() bool { return atomic.LoadInt64(&builds) >= 2 }) 113 waitFor(t, time.Second, func() bool { return builds.Load() >= 2 })
114 // Bytes are unchanged, so nothing new must be delivered. 114 // Bytes are unchanged, so nothing new must be delivered.
115 select { 115 select {
116 case b := <-ch: 116 case b := <-ch:
@@ -123,9 +123,9 @@ func TestSnapshotHubSuppressesUnchanged(t *testing.T) {
123 // slow (never-draining) subscriber's channel holds only the newest snapshot and 123 // slow (never-draining) subscriber's channel holds only the newest snapshot and
124 // the central loop never blocks on it. 124 // the central loop never blocks on it.
125 func TestSnapshotHubLatestWins(t *testing.T) { 125 func TestSnapshotHubLatestWins(t *testing.T) {
126 var n int64 126 var n atomic.Int64
127 build := func() ([]byte, error) { 127 build := func() ([]byte, error) {
128 return []byte(fmt.Sprintf("v%d", atomic.AddInt64(&n, 1))), nil 128 return []byte(fmt.Sprintf("v%d", n.Add(1))), nil
129 } 129 }
130 notif := newNotifier() 130 notif := newNotifier()
131 h := newSnapshotHub(build, notif) 131 h := newSnapshotHub(build, notif)
@@ -138,12 +138,12 @@ func TestSnapshotHubLatestWins(t *testing.T) {
138 ch, unsub := h.subscribe() 138 ch, unsub := h.subscribe()
139 defer unsub() 139 defer unsub()
140 140
141 for i := 0; i < 5; i++ { 141 for i := range 5 {
142 notif.notify() 142 notif.notify()
143 waitFor(t, time.Second, func() bool { return atomic.LoadInt64(&n) >= int64(i+2) }) 143 waitFor(t, time.Second, func() bool { return n.Load() >= int64(i+2) })
144 } 144 }
145 // The lone buffered value must be the LATEST, not a stale early one. 145 // The lone buffered value must be the LATEST, not a stale early one.
146 last := fmt.Sprintf("v%d", atomic.LoadInt64(&n)) 146 last := fmt.Sprintf("v%d", n.Load())
147 select { 147 select {
148 case b := <-ch: 148 case b := <-ch:
149 assert.Equal(t, last, string(b), "slow subscriber must hold the newest snapshot") 149 assert.Equal(t, last, string(b), "slow subscriber must hold the newest snapshot")
internal/server/store/store.go
Old New
@@ -537,8 +537,7 @@ func (s *Store) CreateVM(vm VM) error {
537 // vms_tenant_name — a name collision within the tenant. Matched by 537 // vms_tenant_name — a name collision within the tenant. Matched by
538 // errno, not message text: the message embeds the index's column list 538 // errno, not message text: the message embeds the index's column list
539 // and silently breaks on the next index change (review finding). 539 // and silently breaks on the next index change (review finding).
540 var serr *sqlite.Error 540 if serr, ok := errors.AsType[*sqlite.Error](err); ok {
541 if errors.As(err, &serr) {
542 switch serr.Code() { 541 switch serr.Code() {
543 case 2067: // SQLITE_CONSTRAINT_UNIQUE 542 case 2067: // SQLITE_CONSTRAINT_UNIQUE
544 return ErrNameTaken 543 return ErrNameTaken
internal/server/store/store_test.go
Old New
@@ -440,7 +440,7 @@ func TestSnapshotIsAtomicUnderConcurrentWrites(t *testing.T) {
440 done := make(chan struct{}) 440 done := make(chan struct{})
441 go func() { 441 go func() {
442 defer close(done) 442 defer close(done)
443 for i := 0; i < 300; i++ { 443 for range 300 {
444 id := random.Hex(8) 444 id := random.Hex(8)
445 _ = s.CreateVM(VM{ID: id, HostID: h.ID, Name: "vm-" + id, 445 _ = s.CreateVM(VM{ID: id, HostID: h.ID, Name: "vm-" + id,
446 ImageURL: "http://x/i", ImageSHA256: "abc", 446 ImageURL: "http://x/i", ImageSHA256: "abc",
@@ -449,7 +449,7 @@ func TestSnapshotIsAtomicUnderConcurrentWrites(t *testing.T) {
449 } 449 }
450 }() 450 }()
451 451
452 for i := 0; i < 100; i++ { 452 for i := range 100 {
453 _, alloc, vms, err := s.Snapshot() 453 _, alloc, vms, err := s.Snapshot()
454 require.NoError(t, err) 454 require.NoError(t, err)
455 derived := map[string]Alloc{} 455 derived := map[string]Alloc{}
internal/server/syncsvc/syncsvc_test.go
Old New
@@ -310,7 +310,7 @@ func TestNoGoroutineLeakOnDisconnect(t *testing.T) {
310 310
311 // Warm to steady state: open several connections, read a snapshot, close. 311 // Warm to steady state: open several connections, read a snapshot, close.
312 const warm = 5 312 const warm = 5
313 for i := 0; i < warm; i++ { 313 for range warm {
314 ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) 314 ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
315 conn, err := quic.DialAddr(ctx, f.addr, transport.ClientTLS(f.fp), 315 conn, err := quic.DialAddr(ctx, f.addr, transport.ClientTLS(f.fp),
316 &quic.Config{MaxIdleTimeout: 5 * time.Second}) 316 &quic.Config{MaxIdleTimeout: 5 * time.Second})
@@ -331,7 +331,7 @@ func TestNoGoroutineLeakOnDisconnect(t *testing.T) {
331 before := runtime.NumGoroutine() 331 before := runtime.NumGoroutine()
332 332
333 const n = 5 333 const n = 5
334 for i := 0; i < n; i++ { 334 for range n {
335 ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) 335 ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
336 conn, err := quic.DialAddr(ctx, f.addr, transport.ClientTLS(f.fp), 336 conn, err := quic.DialAddr(ctx, f.addr, transport.ClientTLS(f.fp),
337 &quic.Config{MaxIdleTimeout: 5 * time.Second}) 337 &quic.Config{MaxIdleTimeout: 5 * time.Second})
@@ -391,7 +391,7 @@ func TestStalledReaderDoesNotPinServer(t *testing.T) {
391 // the server keeps trying to write snapshots until the window fills and the 391 // the server keeps trying to write snapshots until the window fills and the
392 // next write hits the 300ms deadline. 392 // next write hits the 300ms deadline.
393 go func() { 393 go func() {
394 for i := 0; i < 100; i++ { 394 for range 100 {
395 f.hub.Poke(f.host.ID) 395 f.hub.Poke(f.host.ID)
396 time.Sleep(20 * time.Millisecond) 396 time.Sleep(20 * time.Millisecond)
397 } 397 }
internal/server/syncsvc/tracker_test.go
Old New
@@ -110,7 +110,7 @@ func TestStatusTrackerWriteThroughAtomic(t *testing.T) {
110 } 110 }
111 111
112 var wg sync.WaitGroup 112 var wg sync.WaitGroup
113 for i := 0; i < 300; i++ { 113 for range 300 {
114 wg.Add(2) 114 wg.Add(2)
115 go func() { defer wg.Done(); writer("ready") }() 115 go func() { defer wg.Done(); writer("ready") }()
116 go func() { defer wg.Done(); writer("failed") }() 116 go func() { defer wg.Done(); writer("failed") }()
internal/shape/build.go
Old New
@@ -17,8 +17,8 @@ func Build(raw []rawPackage) Model {
17 // null — the viewer's JS iterates this field and would crash on null. 17 // null — the viewer's JS iterates this field and would crash on null.
18 imps := make([]string, 0, len(r.Imports)) 18 imps := make([]string, 0, len(r.Imports))
19 for _, imp := range r.Imports { 19 for _, imp := range r.Imports {
20 if strings.HasPrefix(imp, module+"/") { 20 if after, ok := strings.CutPrefix(imp, module+"/"); ok {
21 imps = append(imps, strings.TrimPrefix(imp, module+"/")) 21 imps = append(imps, after)
22 } 22 }
23 } 23 }
24 sort.Strings(imps) 24 sort.Strings(imps)
internal/transport/apicheck_test.go
Old New
@@ -49,8 +49,7 @@ func TestQUICAPISurface(t *testing.T) {
49 var _ func(context.Context) (quic.Connection, error) = lis.Accept 49 var _ func(context.Context) (quic.Connection, error) = lis.Accept
50 } 50 }
51 // Client-side: reading the peer's application close code. 51 // Client-side: reading the peer's application close code.
52 var appErr *quic.ApplicationError 52 if appErr, ok := errors.AsType[*quic.ApplicationError](error(nil)); ok {
53 if errors.As(error(nil), &appErr) {
54 var _ quic.ApplicationErrorCode = appErr.ErrorCode 53 var _ quic.ApplicationErrorCode = appErr.ErrorCode
55 } 54 }
56 } 55 }