@@ -2,17 +2,30 @@ package cli_test
2
2
3
3
import (
4
4
"bytes"
5
+ "database/sql"
5
6
"net/http"
6
7
"testing"
8
+ "time"
7
9
10
+ "github.com/google/uuid"
8
11
"github.com/stretchr/testify/assert"
9
12
"github.com/stretchr/testify/require"
10
13
11
14
"github.com/coder/coder/v2/cli/clitest"
12
15
"github.com/coder/coder/v2/coderd/coderdtest"
16
+ "github.com/coder/coder/v2/coderd/database"
17
+ "github.com/coder/coder/v2/coderd/database/dbauthz"
18
+ "github.com/coder/coder/v2/coderd/database/dbfake"
19
+ "github.com/coder/coder/v2/coderd/database/dbgen"
20
+ "github.com/coder/coder/v2/coderd/database/dbtime"
21
+ "github.com/coder/coder/v2/coderd/util/ptr"
13
22
"github.com/coder/coder/v2/codersdk"
14
23
"github.com/coder/coder/v2/enterprise/coderd/coderdenttest"
15
24
"github.com/coder/coder/v2/enterprise/coderd/license"
25
+ "github.com/coder/coder/v2/provisionersdk/proto"
26
+ "github.com/coder/coder/v2/pty/ptytest"
27
+ "github.com/coder/coder/v2/testutil"
28
+ "github.com/coder/quartz"
16
29
)
17
30
18
31
func TestPrebuildsPause (t * testing.T ) {
@@ -341,3 +354,139 @@ func TestPrebuildsSettingsAPI(t *testing.T) {
341
354
assert .False (t , settings .ReconciliationPaused )
342
355
})
343
356
}
357
+
358
+ // TestSchedulePrebuilds verifies the CLI schedule command when used with prebuilds.
359
+ // Running the command on an unclaimed prebuild fails, but after the prebuild is
360
+ // claimed (becoming a regular workspace) it succeeds as expected.
361
+ func TestSchedulePrebuilds (t * testing.T ) {
362
+ t .Parallel ()
363
+
364
+ cases := []struct {
365
+ name string
366
+ cliErrorMsg string
367
+ cmdArgs func (string ) []string
368
+ }{
369
+ {
370
+ name : "AutostartPrebuildError" ,
371
+ cliErrorMsg : "autostart configuration is not supported for prebuilt workspaces" ,
372
+ cmdArgs : func (workspaceName string ) []string {
373
+ return []string {"schedule" , "start" , workspaceName , "7:30AM" , "Mon-Fri" , "Europe/Lisbon" }
374
+ },
375
+ },
376
+ {
377
+ name : "AutostopPrebuildError" ,
378
+ cliErrorMsg : "autostop configuration is not supported for prebuilt workspaces" ,
379
+ cmdArgs : func (workspaceName string ) []string {
380
+ return []string {"schedule" , "stop" , workspaceName , "8h30m" }
381
+ },
382
+ },
383
+ {
384
+ name : "ExtendPrebuildError" ,
385
+ cliErrorMsg : "extend configuration is not supported for prebuilt workspaces" ,
386
+ cmdArgs : func (workspaceName string ) []string {
387
+ return []string {"schedule" , "extend" , workspaceName , "90m" }
388
+ },
389
+ },
390
+ }
391
+
392
+ for _ , tc := range cases {
393
+ tc := tc
394
+ t .Run (tc .name , func (t * testing.T ) {
395
+ t .Parallel ()
396
+
397
+ clock := quartz .NewMock (t )
398
+ clock .Set (dbtime .Now ())
399
+
400
+ // Setup
401
+ client , db , owner := coderdenttest .NewWithDatabase (t , & coderdenttest.Options {
402
+ Options : & coderdtest.Options {
403
+ IncludeProvisionerDaemon : true ,
404
+ Clock : clock ,
405
+ },
406
+ LicenseOptions : & coderdenttest.LicenseOptions {
407
+ Features : license.Features {
408
+ codersdk .FeatureWorkspacePrebuilds : 1 ,
409
+ },
410
+ },
411
+ })
412
+
413
+ // Given: a template and a template version with preset and a prebuilt workspace
414
+ presetID := uuid .New ()
415
+ version := coderdtest .CreateTemplateVersion (t , client , owner .OrganizationID , nil )
416
+ _ = coderdtest .AwaitTemplateVersionJobCompleted (t , client , version .ID )
417
+ template := coderdtest .CreateTemplate (t , client , owner .OrganizationID , version .ID )
418
+ dbgen .Preset (t , db , database.InsertPresetParams {
419
+ ID : presetID ,
420
+ TemplateVersionID : version .ID ,
421
+ DesiredInstances : sql.NullInt32 {Int32 : 1 , Valid : true },
422
+ })
423
+ workspaceBuild := dbfake .WorkspaceBuild (t , db , database.WorkspaceTable {
424
+ OwnerID : database .PrebuildsSystemUserID ,
425
+ TemplateID : template .ID ,
426
+ }).Seed (database.WorkspaceBuild {
427
+ TemplateVersionID : version .ID ,
428
+ TemplateVersionPresetID : uuid.NullUUID {
429
+ UUID : presetID ,
430
+ Valid : true ,
431
+ },
432
+ }).WithAgent (func (agent []* proto.Agent ) []* proto.Agent {
433
+ return agent
434
+ }).Do ()
435
+
436
+ // Mark the prebuilt workspace's agent as ready so the prebuild can be claimed
437
+ // nolint:gocritic
438
+ ctx := dbauthz .AsSystemRestricted (testutil .Context (t , testutil .WaitLong ))
439
+ agent , err := db .GetWorkspaceAgentAndLatestBuildByAuthToken (ctx , uuid .MustParse (workspaceBuild .AgentToken ))
440
+ require .NoError (t , err )
441
+ err = db .UpdateWorkspaceAgentLifecycleStateByID (ctx , database.UpdateWorkspaceAgentLifecycleStateByIDParams {
442
+ ID : agent .WorkspaceAgent .ID ,
443
+ LifecycleState : database .WorkspaceAgentLifecycleStateReady ,
444
+ })
445
+ require .NoError (t , err )
446
+
447
+ // Given: a prebuilt workspace
448
+ prebuild := coderdtest .MustWorkspace (t , client , workspaceBuild .Workspace .ID )
449
+
450
+ // When: running the schedule command over a prebuilt workspace
451
+ inv , root := clitest .New (t , tc .cmdArgs (prebuild .OwnerName + "/" + prebuild .Name )... )
452
+ clitest .SetupConfig (t , client , root )
453
+ ptytest .New (t ).Attach (inv )
454
+ doneChan := make (chan struct {})
455
+ var runErr error
456
+ go func () {
457
+ defer close (doneChan )
458
+ runErr = inv .Run ()
459
+ }()
460
+ <- doneChan
461
+
462
+ // Then: an error should be returned, with an error message specific to the lifecycle parameter
463
+ require .Error (t , runErr )
464
+ require .Contains (t , runErr .Error (), tc .cliErrorMsg )
465
+
466
+ // Given: the prebuilt workspace is claimed by a user
467
+ user , err := client .User (ctx , "testUser" )
468
+ require .NoError (t , err )
469
+ claimedWorkspace , err := client .CreateUserWorkspace (ctx , user .ID .String (), codersdk.CreateWorkspaceRequest {
470
+ TemplateVersionID : version .ID ,
471
+ TemplateVersionPresetID : presetID ,
472
+ Name : coderdtest .RandomUsername (t ),
473
+ // The 'extend' command requires the workspace to have an existing deadline.
474
+ // To ensure this, we set the workspace's TTL to 1 hour.
475
+ TTLMillis : ptr.Ref [int64 ](time .Hour .Milliseconds ()),
476
+ })
477
+ require .NoError (t , err )
478
+ coderdtest .AwaitWorkspaceBuildJobCompleted (t , client , claimedWorkspace .LatestBuild .ID )
479
+ workspace := coderdtest .MustWorkspace (t , client , claimedWorkspace .ID )
480
+ require .Equal (t , prebuild .ID , workspace .ID )
481
+
482
+ // When: running the schedule command over the claimed workspace
483
+ inv , root = clitest .New (t , tc .cmdArgs (workspace .OwnerName + "/" + workspace .Name )... )
484
+ clitest .SetupConfig (t , client , root )
485
+ pty := ptytest .New (t ).Attach (inv )
486
+ require .NoError (t , inv .Run ())
487
+
488
+ // Then: the updated schedule should be shown
489
+ pty .ExpectMatch (workspace .OwnerName + "/" + workspace .Name )
490
+ })
491
+ }
492
+ }
0 commit comments