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

Commit92d505c

Browse files
authored
feat(cli): prevent coder schedule command on prebuilt workspaces (#19259)
## DescriptionThis PR adds CLI-side validation to prevent the use of the `coderschedule` command (including both `start` and `stop` subcommands) onprebuilt workspaces.Prebuilt workspaces are scheduled independently by the reconciliationloop, based on template and preset-level configuration. They do notparticipate in the regular user workspace lifecycle, and cannot beconfigured via the `coder schedule` CLI command. This change ensuresthat attempting to configure scheduling on a prebuilt workspace resultsin a clear CLI error.## Changes- `coder schedule start` — now returns an error if the target workspaceis a prebuild- `coder schedule stop` — now returns an error if the target workspaceis a prebuildRelated with: * Issue:#18898* **Depends on PR**:#19252
1 parente10f29c commit92d505c

File tree

4 files changed

+174
-3
lines changed

4 files changed

+174
-3
lines changed

‎cli/schedule.go‎

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ When enabling scheduled stop, enter a duration in one of the following formats:
4646
* 2m (2 minutes)
4747
* 2 (2 minutes)
4848
`
49-
scheduleExtendDescriptionLong=`
49+
scheduleExtendDescriptionLong=`Extends the workspace deadline.
5050
* The new stop time is calculated from *now*.
5151
* The new stop time must be at least 30 minutes in the future.
5252
* The workspace template may restrict the maximum workspace runtime.
@@ -157,6 +157,13 @@ func (r *RootCmd) scheduleStart() *serpent.Command {
157157
returnerr
158158
}
159159

160+
// Autostart configuration is not supported for prebuilt workspaces.
161+
// Prebuild lifecycle is managed by the reconciliation loop, with scheduling behavior
162+
// defined per preset at the template level, not per workspace.
163+
ifworkspace.IsPrebuild {
164+
returnxerrors.Errorf("autostart configuration is not supported for prebuilt workspaces")
165+
}
166+
160167
varschedStr*string
161168
ifinv.Args[1]!="manual" {
162169
sched,err:=parseCLISchedule(inv.Args[1:]...)
@@ -205,6 +212,13 @@ func (r *RootCmd) scheduleStop() *serpent.Command {
205212
returnerr
206213
}
207214

215+
// Autostop configuration is not supported for prebuilt workspaces.
216+
// Prebuild lifecycle is managed by the reconciliation loop, with scheduling behavior
217+
// defined per preset at the template level, not per workspace.
218+
ifworkspace.IsPrebuild {
219+
returnxerrors.Errorf("autostop configuration is not supported for prebuilt workspaces")
220+
}
221+
208222
vardurMillis*int64
209223
ifinv.Args[1]!="manual" {
210224
dur,err:=parseDuration(inv.Args[1])
@@ -255,6 +269,13 @@ func (r *RootCmd) scheduleExtend() *serpent.Command {
255269
returnxerrors.Errorf("get workspace: %w",err)
256270
}
257271

272+
// Deadline extensions are not supported for prebuilt workspaces.
273+
// Prebuild lifecycle is managed by the reconciliation loop, with TTL behavior
274+
// defined per preset at the template level, not per workspace.
275+
ifworkspace.IsPrebuild {
276+
returnxerrors.Errorf("extend configuration is not supported for prebuilt workspaces")
277+
}
278+
258279
loc,err:=tz.TimezoneIANA()
259280
iferr!=nil {
260281
loc=time.UTC// best effort

‎cli/testdata/coder_schedule_extend_--help.golden‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@ USAGE:
77

88
Aliases: override-stop
99

10-
* The new stop time is calculated from *now*.
10+
Extends the workspace deadline.
11+
* The new stop time is calculated from *now*.
1112
* The new stop time must be at least 30 minutes in the future.
1213
* The workspace template may restrict the maximum workspace runtime.
1314

‎docs/reference/cli/schedule_extend.md‎

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more aboutcustomizing how changed files appear on GitHub.

‎enterprise/cli/prebuilds_test.go‎

Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,30 @@ package cli_test
22

33
import (
44
"bytes"
5+
"database/sql"
56
"net/http"
67
"testing"
8+
"time"
79

10+
"github.com/google/uuid"
811
"github.com/stretchr/testify/assert"
912
"github.com/stretchr/testify/require"
1013

1114
"github.com/coder/coder/v2/cli/clitest"
1215
"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"
1322
"github.com/coder/coder/v2/codersdk"
1423
"github.com/coder/coder/v2/enterprise/coderd/coderdenttest"
1524
"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"
1629
)
1730

1831
funcTestPrebuildsPause(t*testing.T) {
@@ -341,3 +354,139 @@ func TestPrebuildsSettingsAPI(t *testing.T) {
341354
assert.False(t,settings.ReconciliationPaused)
342355
})
343356
}
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+
funcTestSchedulePrebuilds(t*testing.T) {
362+
t.Parallel()
363+
364+
cases:= []struct {
365+
namestring
366+
cliErrorMsgstring
367+
cmdArgsfunc(string) []string
368+
}{
369+
{
370+
name:"AutostartPrebuildError",
371+
cliErrorMsg:"autostart configuration is not supported for prebuilt workspaces",
372+
cmdArgs:func(workspaceNamestring) []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(workspaceNamestring) []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(workspaceNamestring) []string {
387+
return []string{"schedule","extend",workspaceName,"90m"}
388+
},
389+
},
390+
}
391+
392+
for_,tc:=rangecases {
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+
returnagent
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(chanstruct{})
455+
varrunErrerror
456+
gofunc() {
457+
deferclose(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

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp