@@ -24,7 +24,9 @@ import (
2424"github.com/coder/coder/v2/coderd/database/dbtestutil"
2525"github.com/coder/coder/v2/coderd/database/dbtime"
2626"github.com/coder/coder/v2/coderd/database/migrations"
27+ "github.com/coder/coder/v2/coderd/httpmw"
2728"github.com/coder/coder/v2/coderd/rbac"
29+ "github.com/coder/coder/v2/coderd/rbac/policy"
2830"github.com/coder/coder/v2/testutil"
2931)
3032
@@ -612,70 +614,98 @@ func TestGetWorkspaceAgentUsageStatsAndLabels(t *testing.T) {
612614})
613615}
614616
615- func TestGetWorkspacesAndAgents (t * testing.T ) {
617+ func TestGetAuthorizedWorkspacesAndAgents (t * testing.T ) {
616618t .Parallel ()
617619if testing .Short () {
618620t .SkipNow ()
619621}
620622
623+ ctx := testutil .Context (t ,testutil .WaitLong )
621624sqlDB := testSQLDB (t )
622625err := migrations .Up (sqlDB )
623626require .NoError (t ,err )
624627db := database .New (sqlDB )
625628
626629org := dbgen .Organization (t ,db , database.Organization {})
630+ owner := dbgen .User (t ,db , database.User {
631+ RBACRoles : []string {rbac .RoleOwner ().String ()},
632+ })
627633user := dbgen .User (t ,db , database.User {})
628634tpl := dbgen .Template (t ,db , database.Template {
629635OrganizationID :org .ID ,
630- CreatedBy :user .ID ,
636+ CreatedBy :owner .ID ,
631637})
632638
633- pending := createTemplateVersion (t ,db ,tpl ,tvArgs {
639+ pendingID := uuid .New ()
640+ createTemplateVersion (t ,db ,tpl ,tvArgs {
634641Status :database .ProvisionerJobStatusPending ,
635642CreateWorkspace :true ,
643+ WorkspaceID :pendingID ,
636644CreateAgent :true ,
637645})
638- failed := createTemplateVersion (t ,db ,tpl ,tvArgs {
646+ failedID := uuid .New ()
647+ createTemplateVersion (t ,db ,tpl ,tvArgs {
639648Status :database .ProvisionerJobStatusFailed ,
640649CreateWorkspace :true ,
641650CreateAgent :true ,
651+ WorkspaceID :failedID ,
642652})
643- succeeded := createTemplateVersion (t ,db ,tpl ,tvArgs {
653+ succeededID := uuid .New ()
654+ createTemplateVersion (t ,db ,tpl ,tvArgs {
644655Status :database .ProvisionerJobStatusSucceeded ,
645656WorkspaceTransition :database .WorkspaceTransitionStart ,
646657CreateWorkspace :true ,
658+ WorkspaceID :succeededID ,
647659CreateAgent :true ,
648660ExtraAgents :1 ,
649661ExtraBuilds :2 ,
650662})
651- deleted := createTemplateVersion (t ,db ,tpl ,tvArgs {
663+ deletedID := uuid .New ()
664+ createTemplateVersion (t ,db ,tpl ,tvArgs {
652665Status :database .ProvisionerJobStatusSucceeded ,
653666WorkspaceTransition :database .WorkspaceTransitionDelete ,
654667CreateWorkspace :true ,
668+ WorkspaceID :deletedID ,
655669CreateAgent :false ,
656670})
657671
658- ctx := testutil .Context (t ,testutil .WaitLong )
659- rows ,err := db .GetWorkspacesAndAgents (ctx )
672+ authorizer := rbac .NewStrictCachingAuthorizer (prometheus .NewRegistry ())
673+
674+ userSubject ,_ ,err := httpmw .UserRBACSubject (ctx ,db ,user .ID ,rbac .ExpandableScope (rbac .ScopeAll ))
675+ require .NoError (t ,err )
676+ preparedUser ,err := authorizer .Prepare (ctx ,userSubject ,policy .ActionRead ,rbac .ResourceWorkspace .Type )
660677require .NoError (t ,err )
678+ userCtx := dbauthz .As (ctx ,userSubject )
679+ userRows ,err := db .GetAuthorizedWorkspacesAndAgents (userCtx ,preparedUser )
680+ require .NoError (t ,err )
681+ require .Len (t ,userRows ,0 )
661682
662- require .Len (t ,rows ,4 )
663- for _ ,row := range rows {
683+ ownerSubject ,_ ,err := httpmw .UserRBACSubject (ctx ,db ,owner .ID ,rbac .ExpandableScope (rbac .ScopeAll ))
684+ require .NoError (t ,err )
685+ preparedOwner ,err := authorizer .Prepare (ctx ,ownerSubject ,policy .ActionRead ,rbac .ResourceWorkspace .Type )
686+ require .NoError (t ,err )
687+ ownerCtx := dbauthz .As (ctx ,ownerSubject )
688+ ownerRows ,err := db .GetAuthorizedWorkspacesAndAgents (ownerCtx ,preparedOwner )
689+ require .NoError (t ,err )
690+ require .Len (t ,ownerRows ,4 )
691+ for _ ,row := range ownerRows {
664692switch row .WorkspaceID {
665- case pending . ID :
693+ case pendingID :
666694require .Len (t ,row .AgentIds ,1 )
667695require .Equal (t ,database .ProvisionerJobStatusPending ,row .JobStatus )
668- case failed . ID :
696+ case failedID :
669697require .Len (t ,row .AgentIds ,1 )
670698require .Equal (t ,database .ProvisionerJobStatusFailed ,row .JobStatus )
671- case succeeded . ID :
699+ case succeededID :
672700require .Len (t ,row .AgentIds ,2 )
673701require .Equal (t ,database .ProvisionerJobStatusSucceeded ,row .JobStatus )
674702require .Equal (t ,database .WorkspaceTransitionStart ,row .Transition )
675- case deleted . ID :
703+ case deletedID :
676704require .Len (t ,row .AgentIds ,0 )
677705require .Equal (t ,database .ProvisionerJobStatusSucceeded ,row .JobStatus )
678706require .Equal (t ,database .WorkspaceTransitionDelete ,row .Transition )
707+ default :
708+ t .Fatalf ("unexpected workspace ID: %s" ,row .WorkspaceID )
679709}
680710}
681711}
@@ -1605,6 +1635,7 @@ type tvArgs struct {
16051635Status database.ProvisionerJobStatus
16061636// CreateWorkspace is true if we should create a workspace for the template version
16071637CreateWorkspace bool
1638+ WorkspaceID uuid.UUID
16081639CreateAgent bool
16091640WorkspaceTransition database.WorkspaceTransition
16101641ExtraAgents int
@@ -1625,49 +1656,18 @@ func createTemplateVersion(t testing.TB, db database.Store, tpl database.Templat
16251656CreatedBy :tpl .CreatedBy ,
16261657})
16271658
1628- earlier := sql.NullTime {
1629- Time :dbtime .Now ().Add (time .Second * - 30 ),
1630- Valid :true ,
1631- }
1632- now := sql.NullTime {
1633- Time :dbtime .Now (),
1634- Valid :true ,
1635- }
1636- j := database.ProvisionerJob {
1659+ latestJob := database.ProvisionerJob {
16371660ID :version .JobID ,
1638- CreatedAt :earlier .Time ,
1639- UpdatedAt :earlier .Time ,
16401661Error : sql.NullString {},
16411662OrganizationID :tpl .OrganizationID ,
16421663InitiatorID :tpl .CreatedBy ,
16431664Type :database .ProvisionerJobTypeTemplateVersionImport ,
16441665}
1645-
1646- switch args .Status {
1647- case database .ProvisionerJobStatusRunning :
1648- j .StartedAt = earlier
1649- case database .ProvisionerJobStatusPending :
1650- case database .ProvisionerJobStatusFailed :
1651- j .StartedAt = earlier
1652- j .CompletedAt = now
1653- j .Error = sql.NullString {
1654- String :"failed" ,
1655- Valid :true ,
1656- }
1657- j .ErrorCode = sql.NullString {
1658- String :"failed" ,
1659- Valid :true ,
1660- }
1661- case database .ProvisionerJobStatusSucceeded :
1662- j .StartedAt = earlier
1663- j .CompletedAt = now
1664- default :
1665- t .Fatalf ("invalid status: %s" ,args .Status )
1666- }
1667-
1668- dbgen .ProvisionerJob (t ,db ,nil ,j )
1666+ setJobStatus (t ,args .Status ,& latestJob )
1667+ dbgen .ProvisionerJob (t ,db ,nil ,latestJob )
16691668if args .CreateWorkspace {
16701669wrk := dbgen .Workspace (t ,db , database.Workspace {
1670+ ID :args .WorkspaceID ,
16711671CreatedAt : time.Time {},
16721672UpdatedAt : time.Time {},
16731673OwnerID :tpl .CreatedBy ,
@@ -1678,13 +1678,13 @@ func createTemplateVersion(t testing.TB, db database.Store, tpl database.Templat
16781678if args .WorkspaceTransition != "" {
16791679trans = args .WorkspaceTransition
16801680}
1681-
1682- latestJob := dbgen .ProvisionerJob (t ,db ,nil , database.ProvisionerJob {
1681+ latestJob = database.ProvisionerJob {
16831682Type :database .ProvisionerJobTypeWorkspaceBuild ,
1684- CompletedAt :now ,
16851683InitiatorID :tpl .CreatedBy ,
16861684OrganizationID :tpl .OrganizationID ,
1687- })
1685+ }
1686+ setJobStatus (t ,args .Status ,& latestJob )
1687+ latestJob = dbgen .ProvisionerJob (t ,db ,nil ,latestJob )
16881688latestResource := dbgen .WorkspaceResource (t ,db , database.WorkspaceResource {
16891689JobID :latestJob .ID ,
16901690})
@@ -1697,12 +1697,13 @@ func createTemplateVersion(t testing.TB, db database.Store, tpl database.Templat
16971697JobID :latestJob .ID ,
16981698})
16991699for i := 0 ;i < args .ExtraBuilds ;i ++ {
1700- latestJob = dbgen . ProvisionerJob ( t , db , nil , database.ProvisionerJob {
1700+ latestJob = database.ProvisionerJob {
17011701Type :database .ProvisionerJobTypeWorkspaceBuild ,
1702- CompletedAt :now ,
17031702InitiatorID :tpl .CreatedBy ,
17041703OrganizationID :tpl .OrganizationID ,
1705- })
1704+ }
1705+ setJobStatus (t ,args .Status ,& latestJob )
1706+ latestJob = dbgen .ProvisionerJob (t ,db ,nil ,latestJob )
17061707latestResource = dbgen .WorkspaceResource (t ,db , database.WorkspaceResource {
17071708JobID :latestJob .ID ,
17081709})
@@ -1730,6 +1731,40 @@ func createTemplateVersion(t testing.TB, db database.Store, tpl database.Templat
17301731return version
17311732}
17321733
1734+ func setJobStatus (t testing.TB ,status database.ProvisionerJobStatus ,j * database.ProvisionerJob ) {
1735+ t .Helper ()
1736+
1737+ earlier := sql.NullTime {
1738+ Time :dbtime .Now ().Add (time .Second * - 30 ),
1739+ Valid :true ,
1740+ }
1741+ now := sql.NullTime {
1742+ Time :dbtime .Now (),
1743+ Valid :true ,
1744+ }
1745+ switch status {
1746+ case database .ProvisionerJobStatusRunning :
1747+ j .StartedAt = earlier
1748+ case database .ProvisionerJobStatusPending :
1749+ case database .ProvisionerJobStatusFailed :
1750+ j .StartedAt = earlier
1751+ j .CompletedAt = now
1752+ j .Error = sql.NullString {
1753+ String :"failed" ,
1754+ Valid :true ,
1755+ }
1756+ j .ErrorCode = sql.NullString {
1757+ String :"failed" ,
1758+ Valid :true ,
1759+ }
1760+ case database .ProvisionerJobStatusSucceeded :
1761+ j .StartedAt = earlier
1762+ j .CompletedAt = now
1763+ default :
1764+ t .Fatalf ("invalid status: %s" ,status )
1765+ }
1766+ }
1767+
17331768func TestArchiveVersions (t * testing.T ) {
17341769t .Parallel ()
17351770if testing .Short () {