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