Skip to content

Commit 4ab7a41

Browse files
authored
chore: executor: add unit test, rename LifecycleTicker (#1420)
* chore: add a unit test to ensure correct behaviour with multiple coderd replicas * nit: rename LifecycleTicker to AutobuildTicker
1 parent 89e44da commit 4ab7a41

File tree

2 files changed

+68
-13
lines changed

2 files changed

+68
-13
lines changed

coderd/autobuild/executor/lifecycle_executor_test.go

+64-9
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package executor_test
33
import (
44
"context"
55
"fmt"
6+
"os"
67
"testing"
78
"time"
89

@@ -25,7 +26,7 @@ func TestExecutorAutostartOK(t *testing.T) {
2526
err error
2627
tickCh = make(chan time.Time)
2728
client = coderdtest.New(t, &coderdtest.Options{
28-
LifecycleTicker: tickCh,
29+
AutobuildTicker: tickCh,
2930
})
3031
// Given: we have a user with a workspace
3132
workspace = mustProvisionWorkspace(t, client)
@@ -65,7 +66,7 @@ func TestExecutorAutostartTemplateUpdated(t *testing.T) {
6566
err error
6667
tickCh = make(chan time.Time)
6768
client = coderdtest.New(t, &coderdtest.Options{
68-
LifecycleTicker: tickCh,
69+
AutobuildTicker: tickCh,
6970
})
7071
// Given: we have a user with a workspace
7172
workspace = mustProvisionWorkspace(t, client)
@@ -117,7 +118,7 @@ func TestExecutorAutostartAlreadyRunning(t *testing.T) {
117118
err error
118119
tickCh = make(chan time.Time)
119120
client = coderdtest.New(t, &coderdtest.Options{
120-
LifecycleTicker: tickCh,
121+
AutobuildTicker: tickCh,
121122
})
122123
// Given: we have a user with a workspace
123124
workspace = mustProvisionWorkspace(t, client)
@@ -155,7 +156,7 @@ func TestExecutorAutostartNotEnabled(t *testing.T) {
155156
var (
156157
tickCh = make(chan time.Time)
157158
client = coderdtest.New(t, &coderdtest.Options{
158-
LifecycleTicker: tickCh,
159+
AutobuildTicker: tickCh,
159160
})
160161
// Given: we have a user with a workspace
161162
workspace = mustProvisionWorkspace(t, client)
@@ -188,7 +189,7 @@ func TestExecutorAutostopOK(t *testing.T) {
188189
err error
189190
tickCh = make(chan time.Time)
190191
client = coderdtest.New(t, &coderdtest.Options{
191-
LifecycleTicker: tickCh,
192+
AutobuildTicker: tickCh,
192193
})
193194
// Given: we have a user with a workspace
194195
workspace = mustProvisionWorkspace(t, client)
@@ -228,7 +229,7 @@ func TestExecutorAutostopAlreadyStopped(t *testing.T) {
228229
err error
229230
tickCh = make(chan time.Time)
230231
client = coderdtest.New(t, &coderdtest.Options{
231-
LifecycleTicker: tickCh,
232+
AutobuildTicker: tickCh,
232233
})
233234
// Given: we have a user with a workspace
234235
workspace = mustProvisionWorkspace(t, client)
@@ -266,7 +267,7 @@ func TestExecutorAutostopNotEnabled(t *testing.T) {
266267
var (
267268
tickCh = make(chan time.Time)
268269
client = coderdtest.New(t, &coderdtest.Options{
269-
LifecycleTicker: tickCh,
270+
AutobuildTicker: tickCh,
270271
})
271272
// Given: we have a user with a workspace
272273
workspace = mustProvisionWorkspace(t, client)
@@ -299,7 +300,7 @@ func TestExecutorWorkspaceDeleted(t *testing.T) {
299300
err error
300301
tickCh = make(chan time.Time)
301302
client = coderdtest.New(t, &coderdtest.Options{
302-
LifecycleTicker: tickCh,
303+
AutobuildTicker: tickCh,
303304
})
304305
// Given: we have a user with a workspace
305306
workspace = mustProvisionWorkspace(t, client)
@@ -339,7 +340,7 @@ func TestExecutorWorkspaceTooEarly(t *testing.T) {
339340
err error
340341
tickCh = make(chan time.Time)
341342
client = coderdtest.New(t, &coderdtest.Options{
342-
LifecycleTicker: tickCh,
343+
AutobuildTicker: tickCh,
343344
})
344345
// Given: we have a user with a workspace
345346
workspace = mustProvisionWorkspace(t, client)
@@ -370,6 +371,60 @@ func TestExecutorWorkspaceTooEarly(t *testing.T) {
370371
require.Equal(t, database.WorkspaceTransitionStart, ws.LatestBuild.Transition, "expected workspace to be running")
371372
}
372373

374+
func TestExecutorAutostartMultipleOK(t *testing.T) {
375+
if os.Getenv("DB") == "" {
376+
t.Skip(`This test only really works when using a "real" database, similar to a HA setup`)
377+
}
378+
379+
t.Parallel()
380+
381+
var (
382+
ctx = context.Background()
383+
err error
384+
tickCh = make(chan time.Time)
385+
tickCh2 = make(chan time.Time)
386+
client = coderdtest.New(t, &coderdtest.Options{
387+
AutobuildTicker: tickCh,
388+
})
389+
_ = coderdtest.New(t, &coderdtest.Options{
390+
AutobuildTicker: tickCh2,
391+
})
392+
// Given: we have a user with a workspace
393+
workspace = mustProvisionWorkspace(t, client)
394+
)
395+
// Given: workspace is stopped
396+
workspace = mustTransitionWorkspace(t, client, workspace.ID, database.WorkspaceTransitionStart, database.WorkspaceTransitionStop)
397+
398+
// Given: the workspace initially has autostart disabled
399+
require.Empty(t, workspace.AutostartSchedule)
400+
401+
// When: we enable workspace autostart
402+
sched, err := schedule.Weekly("* * * * *")
403+
require.NoError(t, err)
404+
require.NoError(t, client.UpdateWorkspaceAutostart(ctx, workspace.ID, codersdk.UpdateWorkspaceAutostartRequest{
405+
Schedule: sched.String(),
406+
}))
407+
408+
// When: the autobuild executor ticks
409+
go func() {
410+
tickCh <- time.Now().UTC().Add(time.Minute)
411+
tickCh2 <- time.Now().UTC().Add(time.Minute)
412+
close(tickCh)
413+
close(tickCh2)
414+
}()
415+
416+
// Then: the workspace should be started
417+
<-time.After(5 * time.Second)
418+
ws := mustWorkspace(t, client, workspace.ID)
419+
require.NotEqual(t, workspace.LatestBuild.ID, ws.LatestBuild.ID, "expected a workspace build to occur")
420+
require.Equal(t, codersdk.ProvisionerJobSucceeded, ws.LatestBuild.Job.Status, "expected provisioner job to have succeeded")
421+
require.Equal(t, database.WorkspaceTransitionStart, ws.LatestBuild.Transition, "expected latest transition to be start")
422+
builds, err := client.WorkspaceBuilds(ctx, ws.ID)
423+
require.NoError(t, err, "fetch list of workspace builds from primary")
424+
// One build to start, one stop transition, and one autostart. No more.
425+
require.Len(t, builds, 3, "unexpected number of builds for workspace from primary")
426+
}
427+
373428
func mustProvisionWorkspace(t *testing.T, client *codersdk.Client) codersdk.Workspace {
374429
t.Helper()
375430
coderdtest.NewProvisionerDaemon(t, client)

coderd/coderdtest/coderdtest.go

+4-4
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ type Options struct {
6161
GoogleTokenValidator *idtoken.Validator
6262
SSHKeygenAlgorithm gitsshkey.Algorithm
6363
APIRateLimit int
64-
LifecycleTicker <-chan time.Time
64+
AutobuildTicker <-chan time.Time
6565
}
6666

6767
// New constructs an in-memory coderd instance and returns
@@ -77,9 +77,9 @@ func New(t *testing.T, options *Options) *codersdk.Client {
7777
options.GoogleTokenValidator, err = idtoken.NewValidator(ctx, option.WithoutAuthentication())
7878
require.NoError(t, err)
7979
}
80-
if options.LifecycleTicker == nil {
80+
if options.AutobuildTicker == nil {
8181
ticker := make(chan time.Time)
82-
options.LifecycleTicker = ticker
82+
options.AutobuildTicker = ticker
8383
t.Cleanup(func() { close(ticker) })
8484
}
8585

@@ -111,7 +111,7 @@ func New(t *testing.T, options *Options) *codersdk.Client {
111111
ctx,
112112
db,
113113
slogtest.Make(t, nil).Named("autobuild.executor").Leveled(slog.LevelDebug),
114-
options.LifecycleTicker,
114+
options.AutobuildTicker,
115115
)
116116
lifecycleExecutor.Run()
117117

0 commit comments

Comments
 (0)