@@ -15,6 +15,7 @@ import (
15
15
"github.com/coder/coder/coderd/autobuild"
16
16
"github.com/coder/coder/coderd/coderdtest"
17
17
"github.com/coder/coder/coderd/database"
18
+ "github.com/coder/coder/coderd/schedule"
18
19
"github.com/coder/coder/coderd/util/ptr"
19
20
"github.com/coder/coder/codersdk"
20
21
"github.com/coder/coder/enterprise/coderd"
@@ -235,6 +236,7 @@ func TestWorkspaceAutobuild(t *testing.T) {
235
236
t .Parallel ()
236
237
237
238
var (
239
+ ctx = testutil .Context (t , testutil .WaitMedium )
238
240
ticker = make (chan time.Time )
239
241
statCh = make (chan autobuild.Stats )
240
242
inactiveTTL = time .Millisecond
@@ -277,6 +279,15 @@ func TestWorkspaceAutobuild(t *testing.T) {
277
279
ws = coderdtest .MustWorkspace (t , client , ws .ID )
278
280
// The workspace should be locked.
279
281
require .NotNil (t , ws .LockedAt )
282
+ lastUsedAt := ws .LastUsedAt
283
+
284
+ err := client .UpdateWorkspaceLock (ctx , ws .ID , codersdk.UpdateWorkspaceLock {Lock : false })
285
+ require .NoError (t , err )
286
+
287
+ // Assert that we updated our last_used_at so that we don't immediately
288
+ // retrigger another lock action.
289
+ ws = coderdtest .MustWorkspace (t , client , ws .ID )
290
+ require .True (t , ws .LastUsedAt .After (lastUsedAt ))
280
291
})
281
292
282
293
t .Run ("InactiveTTLTooEarly" , func (t * testing.T ) {
@@ -496,6 +507,88 @@ func TestWorkspaceAutobuild(t *testing.T) {
496
507
require .True (t , ok )
497
508
require .Equal (t , http .StatusGone , cerr .StatusCode ())
498
509
})
510
+
511
+ t .Run ("LockedNoAutostart" , func (t * testing.T ) {
512
+ t .Parallel ()
513
+
514
+ var (
515
+ ctx = testutil .Context (t , testutil .WaitMedium )
516
+ tickCh = make (chan time.Time )
517
+ statsCh = make (chan autobuild.Stats )
518
+ client = coderdenttest .New (t , & coderdenttest.Options {
519
+ Options : & coderdtest.Options {
520
+ AutobuildTicker : tickCh ,
521
+ IncludeProvisionerDaemon : true ,
522
+ AutobuildStats : statsCh ,
523
+ TemplateScheduleStore : & coderd.EnterpriseTemplateScheduleStore {},
524
+ },
525
+ })
526
+ inactiveTTL = time .Millisecond
527
+ )
528
+
529
+ user := coderdtest .CreateFirstUser (t , client )
530
+ _ = coderdenttest .AddFullLicense (t , client )
531
+
532
+ version := coderdtest .CreateTemplateVersion (t , client , user .OrganizationID , & echo.Responses {
533
+ Parse : echo .ParseComplete ,
534
+ ProvisionPlan : echo .ProvisionComplete ,
535
+ ProvisionApply : echo .ProvisionComplete ,
536
+ })
537
+ coderdtest .AwaitTemplateVersionJob (t , client , version .ID )
538
+
539
+ template := coderdtest .CreateTemplate (t , client , user .OrganizationID , version .ID )
540
+
541
+ sched , err := schedule .Weekly ("CRON_TZ=UTC 0 * * * *" )
542
+ require .NoError (t , err )
543
+
544
+ ws := coderdtest .CreateWorkspace (t , client , user .OrganizationID , template .ID , func (cwr * codersdk.CreateWorkspaceRequest ) {
545
+ cwr .AutostartSchedule = ptr .Ref (sched .String ())
546
+ })
547
+ coderdtest .AwaitWorkspaceBuildJob (t , client , ws .LatestBuild .ID )
548
+ coderdtest .MustTransitionWorkspace (t , client , ws .ID , database .WorkspaceTransitionStart , database .WorkspaceTransitionStop )
549
+
550
+ // Assert that autostart works when the workspace isn't locked..
551
+ tickCh <- sched .Next (ws .LatestBuild .CreatedAt )
552
+ // Then: the workspace should eventually be started
553
+ stats := <- statsCh
554
+ require .NoError (t , stats .Error )
555
+ require .Len (t , stats .Transitions , 1 )
556
+ require .Contains (t , stats .Transitions , ws .ID )
557
+ require .Equal (t , database .WorkspaceTransitionStart , stats .Transitions [ws .ID ])
558
+ ws = coderdtest .MustWorkspace (t , client , ws .ID )
559
+ coderdtest .AwaitWorkspaceBuildJob (t , client , ws .LatestBuild .ID )
560
+
561
+ // Now that we've validated that the workspace is eligible for autostart
562
+ // lets cause it to become locked.
563
+ _ , err = client .UpdateTemplateMeta (ctx , template .ID , codersdk.UpdateTemplateMeta {
564
+ InactivityTTLMillis : inactiveTTL .Milliseconds (),
565
+ })
566
+ require .NoError (t , err )
567
+ // Wait for the workspace to breach the inactivity threshold.
568
+ require .Eventually (t ,
569
+ func () bool {
570
+ return database .Now ().Sub (ws .LastUsedAt ) > inactiveTTL
571
+ },
572
+ testutil .IntervalMedium , testutil .IntervalFast )
573
+
574
+ tickCh <- time .Now ()
575
+ // Then: the workspace should eventually be started
576
+ stats = <- statsCh
577
+ require .NoError (t , stats .Error )
578
+ require .Len (t , stats .Transitions , 1 )
579
+ require .Contains (t , stats .Transitions , ws .ID )
580
+ require .Equal (t , database .WorkspaceTransitionStop , stats .Transitions [ws .ID ])
581
+ ws = coderdtest .MustWorkspace (t , client , ws .ID )
582
+ coderdtest .AwaitWorkspaceBuildJob (t , client , ws .LatestBuild .ID )
583
+ // The workspace should be locked now.
584
+ require .NotNil (t , ws .LockedAt )
585
+
586
+ // Assert that autostart is no longer triggered since workspace is locked.
587
+ tickCh <- sched .Next (ws .LatestBuild .CreatedAt )
588
+ // Then: the workspace should eventually be started
589
+ stats = <- statsCh
590
+ require .Len (t , stats .Transitions , 0 )
591
+ })
499
592
}
500
593
501
594
func TestWorkspacesFiltering (t * testing.T ) {
0 commit comments