Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

feat(cli): prevent coder schedule command on prebuilt workspaces#19259

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to ourterms of service andprivacy statement. We’ll occasionally send you account related emails.

Already on GitHub?Sign in to your account

Merged
ssncferreira merged 10 commits intomainfromssncferreira/feat-cli-schedule-prebuild
Aug 13, 2025
Merged
Show file tree
Hide file tree
Changes fromall commits
Commits
Show all changes
10 commits
Select commitHold shift + click to select a range
61778e7
feat(cli): prevent coder schedule command on prebuilt workspaces
ssncferreiraAug 8, 2025
90f04e8
Merge remote-tracking branch 'origin/main' into ssncferreira/feat-cli…
ssncferreiraAug 12, 2025
c5853a0
feat: update 'schedule extend' command to error in case of prebuild
ssncferreiraAug 12, 2025
ff6cc7b
test: add tests for schedule command with prebuilds
ssncferreiraAug 12, 2025
7236b9e
Merge remote-tracking branch 'origin/main' into ssncferreira/feat-cli…
ssncferreiraAug 12, 2025
cc872b9
test: improve test cases names
ssncferreiraAug 12, 2025
50133fc
fix: prebuilds schedule tests
ssncferreiraAug 13, 2025
ae6e17f
Merge remote-tracking branch 'origin/main' into ssncferreira/feat-cli…
ssncferreiraAug 13, 2025
acb1bc2
Merge remote-tracking branch 'origin/main' into ssncferreira/feat-cli…
ssncferreiraAug 13, 2025
4d7af66
fix: tests after merge of #19252
ssncferreiraAug 13, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 22 additions & 1 deletioncli/schedule.go
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -46,7 +46,7 @@ When enabling scheduled stop, enter a duration in one of the following formats:
* 2m (2 minutes)
* 2 (2 minutes)
`
scheduleExtendDescriptionLong = `
scheduleExtendDescriptionLong = `Extends the workspace deadline.
* The new stop time is calculated from *now*.
* The new stop time must be at least 30 minutes in the future.
* The workspace template may restrict the maximum workspace runtime.
Expand DownExpand Up@@ -157,6 +157,13 @@ func (r *RootCmd) scheduleStart() *serpent.Command {
return err
}

// Autostart configuration is not supported for prebuilt workspaces.
// Prebuild lifecycle is managed by the reconciliation loop, with scheduling behavior
// defined per preset at the template level, not per workspace.
if workspace.IsPrebuild {
return xerrors.Errorf("autostart configuration is not supported for prebuilt workspaces")
}

var schedStr *string
if inv.Args[1] != "manual" {
sched, err := parseCLISchedule(inv.Args[1:]...)
Expand DownExpand Up@@ -205,6 +212,13 @@ func (r *RootCmd) scheduleStop() *serpent.Command {
return err
}

// Autostop configuration is not supported for prebuilt workspaces.
// Prebuild lifecycle is managed by the reconciliation loop, with scheduling behavior
// defined per preset at the template level, not per workspace.
if workspace.IsPrebuild {
return xerrors.Errorf("autostop configuration is not supported for prebuilt workspaces")
}

var durMillis *int64
if inv.Args[1] != "manual" {
dur, err := parseDuration(inv.Args[1])
Expand DownExpand Up@@ -255,6 +269,13 @@ func (r *RootCmd) scheduleExtend() *serpent.Command {
return xerrors.Errorf("get workspace: %w", err)
}

// Deadline extensions are not supported for prebuilt workspaces.
// Prebuild lifecycle is managed by the reconciliation loop, with TTL behavior
// defined per preset at the template level, not per workspace.
if workspace.IsPrebuild {
return xerrors.Errorf("extend configuration is not supported for prebuilt workspaces")
}

loc, err := tz.TimezoneIANA()
if err != nil {
loc = time.UTC // best effort
Expand Down
3 changes: 2 additions & 1 deletioncli/testdata/coder_schedule_extend_--help.golden
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -7,7 +7,8 @@ USAGE:

Aliases: override-stop

* The new stop time is calculated from *now*.
Extends the workspace deadline.
* The new stop time is calculated from *now*.
* The new stop time must be at least 30 minutes in the future.
* The workspace template may restrict the maximum workspace runtime.

Expand Down
2 changes: 1 addition & 1 deletiondocs/reference/cli/schedule_extend.md
View file
Open in desktop

Some generated files are not rendered by default. Learn more abouthow customized files appear on GitHub.

149 changes: 149 additions & 0 deletionsenterprise/cli/prebuilds_test.go
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -2,17 +2,30 @@ package cli_test

import (
"bytes"
"database/sql"
"net/http"
"testing"
"time"

"github.com/google/uuid"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"

"github.com/coder/coder/v2/cli/clitest"
"github.com/coder/coder/v2/coderd/coderdtest"
"github.com/coder/coder/v2/coderd/database"
"github.com/coder/coder/v2/coderd/database/dbauthz"
"github.com/coder/coder/v2/coderd/database/dbfake"
"github.com/coder/coder/v2/coderd/database/dbgen"
"github.com/coder/coder/v2/coderd/database/dbtime"
"github.com/coder/coder/v2/coderd/util/ptr"
"github.com/coder/coder/v2/codersdk"
"github.com/coder/coder/v2/enterprise/coderd/coderdenttest"
"github.com/coder/coder/v2/enterprise/coderd/license"
"github.com/coder/coder/v2/provisionersdk/proto"
"github.com/coder/coder/v2/pty/ptytest"
"github.com/coder/coder/v2/testutil"
"github.com/coder/quartz"
)

func TestPrebuildsPause(t *testing.T) {
Expand DownExpand Up@@ -341,3 +354,139 @@ func TestPrebuildsSettingsAPI(t *testing.T) {
assert.False(t, settings.ReconciliationPaused)
})
}

// TestSchedulePrebuilds verifies the CLI schedule command when used with prebuilds.
// Running the command on an unclaimed prebuild fails, but after the prebuild is
// claimed (becoming a regular workspace) it succeeds as expected.
func TestSchedulePrebuilds(t *testing.T) {
t.Parallel()

cases := []struct {
name string
cliErrorMsg string
cmdArgs func(string) []string
}{
{
name: "AutostartPrebuildError",
cliErrorMsg: "autostart configuration is not supported for prebuilt workspaces",
cmdArgs: func(workspaceName string) []string {
return []string{"schedule", "start", workspaceName, "7:30AM", "Mon-Fri", "Europe/Lisbon"}
},
},
{
name: "AutostopPrebuildError",
cliErrorMsg: "autostop configuration is not supported for prebuilt workspaces",
cmdArgs: func(workspaceName string) []string {
return []string{"schedule", "stop", workspaceName, "8h30m"}
},
},
{
name: "ExtendPrebuildError",
cliErrorMsg: "extend configuration is not supported for prebuilt workspaces",
cmdArgs: func(workspaceName string) []string {
return []string{"schedule", "extend", workspaceName, "90m"}
},
},
}

for _, tc := range cases {
tc := tc
t.Run(tc.name, func(t *testing.T) {
t.Parallel()

clock := quartz.NewMock(t)
clock.Set(dbtime.Now())

// Setup
client, db, owner := coderdenttest.NewWithDatabase(t, &coderdenttest.Options{
Options: &coderdtest.Options{
IncludeProvisionerDaemon: true,
Clock: clock,
},
LicenseOptions: &coderdenttest.LicenseOptions{
Features: license.Features{
codersdk.FeatureWorkspacePrebuilds: 1,
},
},
})

// Given: a template and a template version with preset and a prebuilt workspace
presetID := uuid.New()
version := coderdtest.CreateTemplateVersion(t, client, owner.OrganizationID, nil)
_ = coderdtest.AwaitTemplateVersionJobCompleted(t, client, version.ID)
template := coderdtest.CreateTemplate(t, client, owner.OrganizationID, version.ID)
dbgen.Preset(t, db, database.InsertPresetParams{
ID: presetID,
TemplateVersionID: version.ID,
DesiredInstances: sql.NullInt32{Int32: 1, Valid: true},
})
workspaceBuild := dbfake.WorkspaceBuild(t, db, database.WorkspaceTable{
OwnerID: database.PrebuildsSystemUserID,
TemplateID: template.ID,
}).Seed(database.WorkspaceBuild{
TemplateVersionID: version.ID,
TemplateVersionPresetID: uuid.NullUUID{
UUID: presetID,
Valid: true,
},
}).WithAgent(func(agent []*proto.Agent) []*proto.Agent {
return agent
}).Do()

// Mark the prebuilt workspace's agent as ready so the prebuild can be claimed
// nolint:gocritic
ctx := dbauthz.AsSystemRestricted(testutil.Context(t, testutil.WaitLong))
agent, err := db.GetWorkspaceAgentAndLatestBuildByAuthToken(ctx, uuid.MustParse(workspaceBuild.AgentToken))
require.NoError(t, err)
err = db.UpdateWorkspaceAgentLifecycleStateByID(ctx, database.UpdateWorkspaceAgentLifecycleStateByIDParams{
ID: agent.WorkspaceAgent.ID,
LifecycleState: database.WorkspaceAgentLifecycleStateReady,
})
require.NoError(t, err)

// Given: a prebuilt workspace
prebuild := coderdtest.MustWorkspace(t, client, workspaceBuild.Workspace.ID)

// When: running the schedule command over a prebuilt workspace
inv, root := clitest.New(t, tc.cmdArgs(prebuild.OwnerName+"/"+prebuild.Name)...)
clitest.SetupConfig(t, client, root)
ptytest.New(t).Attach(inv)
doneChan := make(chan struct{})
var runErr error
go func() {
defer close(doneChan)
runErr = inv.Run()
}()
<-doneChan

// Then: an error should be returned, with an error message specific to the lifecycle parameter
require.Error(t, runErr)
require.Contains(t, runErr.Error(), tc.cliErrorMsg)

// Given: the prebuilt workspace is claimed by a user
user, err := client.User(ctx, "testUser")
require.NoError(t, err)
claimedWorkspace, err := client.CreateUserWorkspace(ctx, user.ID.String(), codersdk.CreateWorkspaceRequest{
TemplateVersionID: version.ID,
TemplateVersionPresetID: presetID,
Name: coderdtest.RandomUsername(t),
// The 'extend' command requires the workspace to have an existing deadline.
// To ensure this, we set the workspace's TTL to 1 hour.
TTLMillis: ptr.Ref[int64](time.Hour.Milliseconds()),
})
require.NoError(t, err)
coderdtest.AwaitWorkspaceBuildJobCompleted(t, client, claimedWorkspace.LatestBuild.ID)
workspace := coderdtest.MustWorkspace(t, client, claimedWorkspace.ID)
require.Equal(t, prebuild.ID, workspace.ID)

// When: running the schedule command over the claimed workspace
inv, root = clitest.New(t, tc.cmdArgs(workspace.OwnerName+"/"+workspace.Name)...)
clitest.SetupConfig(t, client, root)
pty := ptytest.New(t).Attach(inv)
require.NoError(t, inv.Run())

// Then: the updated schedule should be shown
pty.ExpectMatch(workspace.OwnerName + "/" + workspace.Name)
})
}
}
Loading

[8]ページ先頭

©2009-2025 Movatter.jp