Skip to content

Commit d81d7ee

Browse files
fix: allow agents to be created on dormant workspaces (#20909) (#20912)
We now allow agents to be created on dormant workspaces. I've ran the test with and without the change. I've confirmed that - without the fix - it triggers the "rbac: unauthorized" error. --- Cherry picked from #20909
1 parent ed5785f commit d81d7ee

File tree

2 files changed

+68
-1
lines changed

2 files changed

+68
-1
lines changed

coderd/database/dbauthz/dbauthz.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -217,7 +217,7 @@ var (
217217
rbac.ResourceTemplate.Type: {policy.ActionRead, policy.ActionUpdate},
218218
// Unsure why provisionerd needs update and read personal
219219
rbac.ResourceUser.Type: {policy.ActionRead, policy.ActionReadPersonal, policy.ActionUpdatePersonal},
220-
rbac.ResourceWorkspaceDormant.Type: {policy.ActionDelete, policy.ActionRead, policy.ActionUpdate, policy.ActionWorkspaceStop},
220+
rbac.ResourceWorkspaceDormant.Type: {policy.ActionDelete, policy.ActionRead, policy.ActionUpdate, policy.ActionWorkspaceStop, policy.ActionCreateAgent},
221221
rbac.ResourceWorkspace.Type: {policy.ActionDelete, policy.ActionRead, policy.ActionUpdate, policy.ActionWorkspaceStart, policy.ActionWorkspaceStop, policy.ActionCreateAgent},
222222
// Provisionerd needs to read, update, and delete tasks associated with workspaces.
223223
rbac.ResourceTask.Type: {policy.ActionRead, policy.ActionUpdate, policy.ActionDelete},

enterprise/coderd/workspaces_test.go

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -833,6 +833,73 @@ func TestWorkspaceAutobuild(t *testing.T) {
833833
require.True(t, ws.LastUsedAt.After(dormantLastUsedAt))
834834
})
835835

836+
// This test has been added to ensure we don't introduce a regression
837+
// to this issue https://github.com/coder/coder/issues/20711.
838+
t.Run("DormantAutostop", func(t *testing.T) {
839+
t.Parallel()
840+
841+
var (
842+
ticker = make(chan time.Time)
843+
statCh = make(chan autobuild.Stats)
844+
inactiveTTL = time.Minute
845+
logger = slogtest.Make(t, &slogtest.Options{IgnoreErrors: true}).Leveled(slog.LevelDebug)
846+
)
847+
848+
client, db, user := coderdenttest.NewWithDatabase(t, &coderdenttest.Options{
849+
Options: &coderdtest.Options{
850+
AutobuildTicker: ticker,
851+
AutobuildStats: statCh,
852+
IncludeProvisionerDaemon: true,
853+
TemplateScheduleStore: schedule.NewEnterpriseTemplateScheduleStore(agplUserQuietHoursScheduleStore(), notifications.NewNoopEnqueuer(), logger, nil),
854+
},
855+
LicenseOptions: &coderdenttest.LicenseOptions{
856+
Features: license.Features{codersdk.FeatureAdvancedTemplateScheduling: 1},
857+
},
858+
})
859+
860+
// Create a template version that includes agents on both start AND stop builds.
861+
// This simulates a template without `count = data.coder_workspace.me.start_count`.
862+
authToken := uuid.NewString()
863+
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
864+
Parse: echo.ParseComplete,
865+
ProvisionPlan: echo.PlanComplete,
866+
ProvisionApply: echo.ProvisionApplyWithAgent(authToken),
867+
})
868+
869+
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID, func(ctr *codersdk.CreateTemplateRequest) {
870+
ctr.TimeTilDormantMillis = ptr.Ref[int64](inactiveTTL.Milliseconds())
871+
})
872+
873+
coderdtest.AwaitTemplateVersionJobCompleted(t, client, version.ID)
874+
ws := coderdtest.CreateWorkspace(t, client, template.ID)
875+
build := coderdtest.AwaitWorkspaceBuildJobCompleted(t, client, ws.LatestBuild.ID)
876+
require.Equal(t, codersdk.WorkspaceStatusRunning, build.Status)
877+
878+
// Simulate the workspace becoming inactive and transitioning to dormant.
879+
tickTime := ws.LastUsedAt.Add(inactiveTTL * 2)
880+
881+
p, err := coderdtest.GetProvisionerForTags(db, time.Now(), ws.OrganizationID, nil)
882+
require.NoError(t, err)
883+
coderdtest.UpdateProvisionerLastSeenAt(t, db, p.ID, tickTime)
884+
ticker <- tickTime
885+
stats := <-statCh
886+
887+
// Expect workspace to transition to stopped state.
888+
require.Len(t, stats.Transitions, 1)
889+
require.Equal(t, stats.Transitions[ws.ID], database.WorkspaceTransitionStop)
890+
891+
// The autostop build should succeed even though the template includes
892+
// agents without `count = data.coder_workspace.me.start_count`.
893+
// This verifies that provisionerd has permission to create agents on
894+
// dormant workspaces during stop builds.
895+
ws = coderdtest.MustWorkspace(t, client, ws.ID)
896+
require.NotNil(t, ws.DormantAt, "workspace should be marked as dormant")
897+
require.Equal(t, codersdk.WorkspaceTransitionStop, ws.LatestBuild.Transition)
898+
899+
latestBuild := coderdtest.AwaitWorkspaceBuildJobCompleted(t, client, ws.LatestBuild.ID)
900+
require.Equal(t, codersdk.WorkspaceStatusStopped, latestBuild.Status)
901+
})
902+
836903
// This test serves as a regression prevention for generating
837904
// audit logs in the same transaction the transition workspaces to
838905
// the dormant state. The auditor that is passed to autobuild does

0 commit comments

Comments
 (0)