Skip to content

Commit 42f06c8

Browse files
fix: allow agents to be created on dormant workspaces (#20909) (#20911)
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 5ed27e7 commit 42f06c8

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
rbac.ResourceApiKey.Type: {policy.WildcardSymbol},
223223
// When org scoped provisioner credentials are implemented,

enterprise/coderd/workspaces_test.go

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

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

0 commit comments

Comments
 (0)