@@ -17,6 +17,8 @@ import (
17
17
18
18
"github.com/prometheus/client_golang/prometheus"
19
19
20
+ "github.com/coder/coder/v2/coderd/database/dbgen"
21
+
20
22
"github.com/coder/coder/v2/coderd/files"
21
23
agplprebuilds "github.com/coder/coder/v2/coderd/prebuilds"
22
24
"github.com/coder/coder/v2/enterprise/coderd/prebuilds"
@@ -2519,6 +2521,185 @@ func templateWithFailedResponseAndPresetsWithPrebuilds(desiredInstances int32) *
2519
2521
}
2520
2522
}
2521
2523
2524
+ func TestPrebuildUpdateLifecycleParams (t * testing.T ) {
2525
+ t .Parallel ()
2526
+
2527
+ // Set the clock to Monday, January 1st, 2024 at 8:00 AM UTC to keep the test deterministic
2528
+ clock := quartz .NewMock (t )
2529
+ clock .Set (time .Date (2024 , 1 , 1 , 8 , 0 , 0 , 0 , time .UTC ))
2530
+
2531
+ // Autostart schedule configuration set to weekly at 9:30 AM UTC
2532
+ autostartSchedule , err := cron .Weekly ("CRON_TZ=UTC 30 9 * * 1-5" )
2533
+ require .NoError (t , err )
2534
+
2535
+ // TTL configuration set to 8 hours
2536
+ ttlMillis := ptr .Ref ((8 * time .Hour ).Milliseconds ())
2537
+
2538
+ // Deadline configuration set to 10:00 AM UTC
2539
+ deadline := clock .Now ().Add (2 * time .Hour )
2540
+
2541
+ cases := []struct {
2542
+ name string
2543
+ endpoint func (* testing.T , context.Context , * codersdk.Client , uuid.UUID ) error
2544
+ apiErrorMsg string
2545
+ assertUpdate func (* testing.T , * quartz.Mock , * codersdk.Client , uuid.UUID )
2546
+ }{
2547
+ {
2548
+ name : "AutostartUpdatePrebuildAfterClaim" ,
2549
+ endpoint : func (t * testing.T , ctx context.Context , client * codersdk.Client , workspaceID uuid.UUID ) error {
2550
+ err = client .UpdateWorkspaceAutostart (ctx , workspaceID , codersdk.UpdateWorkspaceAutostartRequest {
2551
+ Schedule : ptr .Ref (autostartSchedule .String ()),
2552
+ })
2553
+ return err
2554
+ },
2555
+ apiErrorMsg : "Autostart is not supported for prebuilt workspaces" ,
2556
+ assertUpdate : func (t * testing.T , clock * quartz.Mock , client * codersdk.Client , workspaceID uuid.UUID ) {
2557
+ // The workspace's autostart schedule should be updated to the given schedule,
2558
+ // and its next start time should be set to 2024-01-01 09:30 AM UTC
2559
+ updatedWorkspace := coderdtest .MustWorkspace (t , client , workspaceID )
2560
+ require .Equal (t , autostartSchedule .String (), * updatedWorkspace .AutostartSchedule )
2561
+ require .Equal (t , autostartSchedule .Next (clock .Now ()), updatedWorkspace .NextStartAt .UTC ())
2562
+ expectedNext := time .Date (2024 , 1 , 1 , 9 , 30 , 0 , 0 , time .UTC )
2563
+ require .Equal (t , expectedNext , updatedWorkspace .NextStartAt .UTC ())
2564
+ },
2565
+ },
2566
+ {
2567
+ name : "TTLUpdatePrebuildAfterClaim" ,
2568
+ endpoint : func (t * testing.T , ctx context.Context , client * codersdk.Client , workspaceID uuid.UUID ) error {
2569
+ err := client .UpdateWorkspaceTTL (ctx , workspaceID , codersdk.UpdateWorkspaceTTLRequest {
2570
+ TTLMillis : ttlMillis ,
2571
+ })
2572
+ return err
2573
+ },
2574
+ apiErrorMsg : "TTL updates are not supported for prebuilt workspaces" ,
2575
+ assertUpdate : func (t * testing.T , clock * quartz.Mock , client * codersdk.Client , workspaceID uuid.UUID ) {
2576
+ // The workspace's TTL should be updated accordingly
2577
+ updatedWorkspace := coderdtest .MustWorkspace (t , client , workspaceID )
2578
+ require .Equal (t , ttlMillis , updatedWorkspace .TTLMillis )
2579
+ },
2580
+ },
2581
+ {
2582
+ name : "DormantUpdatePrebuildAfterClaim" ,
2583
+ endpoint : func (t * testing.T , ctx context.Context , client * codersdk.Client , workspaceID uuid.UUID ) error {
2584
+ err := client .UpdateWorkspaceDormancy (ctx , workspaceID , codersdk.UpdateWorkspaceDormancy {
2585
+ Dormant : true ,
2586
+ })
2587
+ return err
2588
+ },
2589
+ apiErrorMsg : "Dormancy updates are not supported for prebuilt workspaces" ,
2590
+ assertUpdate : func (t * testing.T , clock * quartz.Mock , client * codersdk.Client , workspaceID uuid.UUID ) {
2591
+ // The workspace's dormantAt should be updated accordingly
2592
+ updatedWorkspace := coderdtest .MustWorkspace (t , client , workspaceID )
2593
+ require .Equal (t , clock .Now (), updatedWorkspace .DormantAt .UTC ())
2594
+ },
2595
+ },
2596
+ {
2597
+ name : "DeadlineUpdatePrebuildAfterClaim" ,
2598
+ endpoint : func (t * testing.T , ctx context.Context , client * codersdk.Client , workspaceID uuid.UUID ) error {
2599
+ err := client .PutExtendWorkspace (ctx , workspaceID , codersdk.PutExtendWorkspaceRequest {
2600
+ Deadline : deadline ,
2601
+ })
2602
+ return err
2603
+ },
2604
+ apiErrorMsg : "Deadline extension is not supported for prebuilt workspaces" ,
2605
+ assertUpdate : func (t * testing.T , clock * quartz.Mock , client * codersdk.Client , workspaceID uuid.UUID ) {
2606
+ // The workspace build's deadline should be updated accordingly
2607
+ updatedWorkspace := coderdtest .MustWorkspace (t , client , workspaceID )
2608
+ require .Equal (t , deadline , updatedWorkspace .LatestBuild .Deadline .Time .UTC ())
2609
+ },
2610
+ },
2611
+ }
2612
+
2613
+ for _ , tc := range cases {
2614
+ tc := tc
2615
+ t .Run (tc .name , func (t * testing.T ) {
2616
+ t .Parallel ()
2617
+
2618
+ // Setup
2619
+ client , db , owner := coderdenttest .NewWithDatabase (t , & coderdenttest.Options {
2620
+ Options : & coderdtest.Options {
2621
+ IncludeProvisionerDaemon : true ,
2622
+ Clock : clock ,
2623
+ },
2624
+ LicenseOptions : & coderdenttest.LicenseOptions {
2625
+ Features : license.Features {
2626
+ codersdk .FeatureWorkspacePrebuilds : 1 ,
2627
+ },
2628
+ },
2629
+ })
2630
+
2631
+ // Given: a template and a template version with preset and a prebuilt workspace
2632
+ presetID := uuid .New ()
2633
+ version := coderdtest .CreateTemplateVersion (t , client , owner .OrganizationID , nil )
2634
+ _ = coderdtest .AwaitTemplateVersionJobCompleted (t , client , version .ID )
2635
+ template := coderdtest .CreateTemplate (t , client , owner .OrganizationID , version .ID )
2636
+ dbgen .Preset (t , db , database.InsertPresetParams {
2637
+ ID : presetID ,
2638
+ TemplateVersionID : version .ID ,
2639
+ DesiredInstances : sql.NullInt32 {Int32 : 1 , Valid : true },
2640
+ })
2641
+ workspaceBuild := dbfake .WorkspaceBuild (t , db , database.WorkspaceTable {
2642
+ OwnerID : database .PrebuildsSystemUserID ,
2643
+ TemplateID : template .ID ,
2644
+ }).Seed (database.WorkspaceBuild {
2645
+ TemplateVersionID : version .ID ,
2646
+ TemplateVersionPresetID : uuid.NullUUID {
2647
+ UUID : presetID ,
2648
+ Valid : true ,
2649
+ },
2650
+ }).WithAgent (func (agent []* proto.Agent ) []* proto.Agent {
2651
+ return agent
2652
+ }).Do ()
2653
+
2654
+ // Mark the prebuilt workspace's agent as ready so the prebuild can be claimed
2655
+ // nolint:gocritic
2656
+ ctx := dbauthz .AsSystemRestricted (testutil .Context (t , testutil .WaitLong ))
2657
+ agent , err := db .GetWorkspaceAgentAndLatestBuildByAuthToken (ctx , uuid .MustParse (workspaceBuild .AgentToken ))
2658
+ require .NoError (t , err )
2659
+ err = db .UpdateWorkspaceAgentLifecycleStateByID (ctx , database.UpdateWorkspaceAgentLifecycleStateByIDParams {
2660
+ ID : agent .WorkspaceAgent .ID ,
2661
+ LifecycleState : database .WorkspaceAgentLifecycleStateReady ,
2662
+ })
2663
+ require .NoError (t , err )
2664
+
2665
+ // Given: a prebuilt workspace
2666
+ prebuild := coderdtest .MustWorkspace (t , client , workspaceBuild .Workspace .ID )
2667
+
2668
+ // When: the lifecycle-update endpoint is called for the prebuilt workspace
2669
+ err = tc .endpoint (t , ctx , client , prebuild .ID )
2670
+
2671
+ // Then: a 409 Conflict should be returned, with an error message specific to the lifecycle parameter
2672
+ var apiErr * codersdk.Error
2673
+ require .ErrorAs (t , err , & apiErr )
2674
+ require .Equal (t , http .StatusConflict , apiErr .StatusCode ())
2675
+ require .Equal (t , tc .apiErrorMsg , apiErr .Response .Message )
2676
+
2677
+ // Given: the prebuilt workspace is claimed by a user
2678
+ user , err := client .User (ctx , "testUser" )
2679
+ require .NoError (t , err )
2680
+ claimedWorkspace , err := client .CreateUserWorkspace (ctx , user .ID .String (), codersdk.CreateWorkspaceRequest {
2681
+ TemplateVersionID : version .ID ,
2682
+ TemplateVersionPresetID : presetID ,
2683
+ Name : coderdtest .RandomUsername (t ),
2684
+ // The 'extend' endpoint requires the workspace to have an existing deadline.
2685
+ // To ensure this, we set the workspace's TTL to 1 hour.
2686
+ TTLMillis : ptr.Ref [int64 ](time .Hour .Milliseconds ()),
2687
+ })
2688
+ require .NoError (t , err )
2689
+ coderdtest .AwaitWorkspaceBuildJobCompleted (t , client , claimedWorkspace .LatestBuild .ID )
2690
+ workspace := coderdtest .MustWorkspace (t , client , claimedWorkspace .ID )
2691
+ require .Equal (t , prebuild .ID , workspace .ID )
2692
+
2693
+ // When: the same lifecycle-update endpoint is called for the claimed workspace
2694
+ err = tc .endpoint (t , ctx , client , workspace .ID )
2695
+ require .NoError (t , err )
2696
+
2697
+ // Then: the workspace's lifecycle parameter should be updated accordingly
2698
+ tc .assertUpdate (t , clock , client , claimedWorkspace .ID )
2699
+ })
2700
+ }
2701
+ }
2702
+
2522
2703
// TestWorkspaceTemplateParamsChange tests a workspace with a parameter that
2523
2704
// validation changes on apply. The params used in create workspace are invalid
2524
2705
// according to the static params on import.
0 commit comments