diff --git a/cli/provisionerjobs.go b/cli/provisionerjobs.go index 3ce7da20b7..3f441a1758 100644 --- a/cli/provisionerjobs.go +++ b/cli/provisionerjobs.go @@ -43,8 +43,9 @@ func (r *RootCmd) provisionerJobsList() *serpent.Command { cliui.TableFormat([]provisionerJobRow{}, []string{"created at", "id", "type", "template display name", "status", "queue", "tags"}), cliui.JSONFormat(), ) - status []string - limit int64 + status []string + limit int64 + initiator string ) cmd := &serpent.Command{ @@ -65,9 +66,20 @@ func (r *RootCmd) provisionerJobsList() *serpent.Command { return xerrors.Errorf("current organization: %w", err) } + var initiatorID *uuid.UUID + + if initiator != "" { + user, err := client.User(ctx, initiator) + if err != nil { + return xerrors.Errorf("initiator not found: %s", initiator) + } + initiatorID = &user.ID + } + jobs, err := client.OrganizationProvisionerJobs(ctx, org.ID, &codersdk.OrganizationProvisionerJobsOptions{ - Status: slice.StringEnums[codersdk.ProvisionerJobStatus](status), - Limit: int(limit), + Status: slice.StringEnums[codersdk.ProvisionerJobStatus](status), + Limit: int(limit), + InitiatorID: initiatorID, }) if err != nil { return xerrors.Errorf("list provisioner jobs: %w", err) @@ -122,6 +134,13 @@ func (r *RootCmd) provisionerJobsList() *serpent.Command { Default: "50", Value: serpent.Int64Of(&limit), }, + { + Flag: "initiator", + FlagShorthand: "i", + Env: "CODER_PROVISIONER_JOB_LIST_INITIATOR", + Description: "Filter by initiator (user ID or username).", + Value: serpent.StringOf(&initiator), + }, }...) orgContext.AttachOptions(cmd) diff --git a/cli/provisionerjobs_test.go b/cli/provisionerjobs_test.go index 4db42e8e3c..57072a6156 100644 --- a/cli/provisionerjobs_test.go +++ b/cli/provisionerjobs_test.go @@ -5,6 +5,7 @@ import ( "database/sql" "encoding/json" "fmt" + "strings" "testing" "time" @@ -26,33 +27,32 @@ import ( func TestProvisionerJobs(t *testing.T) { t.Parallel() - db, ps := dbtestutil.NewDB(t) - client, _, coderdAPI := coderdtest.NewWithAPI(t, &coderdtest.Options{ - IncludeProvisionerDaemon: false, - Database: db, - Pubsub: ps, - }) - owner := coderdtest.CreateFirstUser(t, client) - templateAdminClient, templateAdmin := coderdtest.CreateAnotherUser(t, client, owner.OrganizationID, rbac.ScopedRoleOrgTemplateAdmin(owner.OrganizationID)) - memberClient, member := coderdtest.CreateAnotherUser(t, client, owner.OrganizationID) - - // These CLI tests are related to provisioner job CRUD operations and as such - // do not require the overhead of starting a provisioner. Other provisioner job - // functionalities (acquisition etc.) are tested elsewhere. - template := dbgen.Template(t, db, database.Template{ - OrganizationID: owner.OrganizationID, - CreatedBy: owner.UserID, - AllowUserCancelWorkspaceJobs: true, - }) - version := dbgen.TemplateVersion(t, db, database.TemplateVersion{ - OrganizationID: owner.OrganizationID, - CreatedBy: owner.UserID, - TemplateID: uuid.NullUUID{UUID: template.ID, Valid: true}, - }) - t.Run("Cancel", func(t *testing.T) { t.Parallel() + db, ps := dbtestutil.NewDB(t) + client, _, coderdAPI := coderdtest.NewWithAPI(t, &coderdtest.Options{ + IncludeProvisionerDaemon: false, + Database: db, + Pubsub: ps, + }) + owner := coderdtest.CreateFirstUser(t, client) + templateAdminClient, templateAdmin := coderdtest.CreateAnotherUser(t, client, owner.OrganizationID, rbac.ScopedRoleOrgTemplateAdmin(owner.OrganizationID)) + memberClient, member := coderdtest.CreateAnotherUser(t, client, owner.OrganizationID) + + // These CLI tests are related to provisioner job CRUD operations and as such + // do not require the overhead of starting a provisioner. Other provisioner job + // functionalities (acquisition etc.) are tested elsewhere. + template := dbgen.Template(t, db, database.Template{ + OrganizationID: owner.OrganizationID, + CreatedBy: owner.UserID, + AllowUserCancelWorkspaceJobs: true, + }) + version := dbgen.TemplateVersion(t, db, database.TemplateVersion{ + OrganizationID: owner.OrganizationID, + CreatedBy: owner.UserID, + TemplateID: uuid.NullUUID{UUID: template.ID, Valid: true}, + }) // Test helper to create a provisioner job of a given type with a given input. prepareJob := func(t *testing.T, jobType database.ProvisionerJobType, input json.RawMessage) database.ProvisionerJob { t.Helper() @@ -178,4 +178,148 @@ func TestProvisionerJobs(t *testing.T) { }) } }) + + t.Run("List", func(t *testing.T) { + t.Parallel() + + db, ps := dbtestutil.NewDB(t) + client, _, coderdAPI := coderdtest.NewWithAPI(t, &coderdtest.Options{ + IncludeProvisionerDaemon: false, + Database: db, + Pubsub: ps, + }) + owner := coderdtest.CreateFirstUser(t, client) + _, member := coderdtest.CreateAnotherUser(t, client, owner.OrganizationID) + + // These CLI tests are related to provisioner job CRUD operations and as such + // do not require the overhead of starting a provisioner. Other provisioner job + // functionalities (acquisition etc.) are tested elsewhere. + template := dbgen.Template(t, db, database.Template{ + OrganizationID: owner.OrganizationID, + CreatedBy: owner.UserID, + AllowUserCancelWorkspaceJobs: true, + }) + version := dbgen.TemplateVersion(t, db, database.TemplateVersion{ + OrganizationID: owner.OrganizationID, + CreatedBy: owner.UserID, + TemplateID: uuid.NullUUID{UUID: template.ID, Valid: true}, + }) + // Create some test jobs + job1 := dbgen.ProvisionerJob(t, db, coderdAPI.Pubsub, database.ProvisionerJob{ + OrganizationID: owner.OrganizationID, + InitiatorID: owner.UserID, + Type: database.ProvisionerJobTypeTemplateVersionImport, + Input: []byte(`{"template_version_id":"` + version.ID.String() + `"}`), + Tags: database.StringMap{provisionersdk.TagScope: provisionersdk.ScopeOrganization}, + }) + + job2 := dbgen.ProvisionerJob(t, db, coderdAPI.Pubsub, database.ProvisionerJob{ + OrganizationID: owner.OrganizationID, + InitiatorID: member.ID, + Type: database.ProvisionerJobTypeWorkspaceBuild, + Input: []byte(`{"workspace_build_id":"` + uuid.New().String() + `"}`), + Tags: database.StringMap{provisionersdk.TagScope: provisionersdk.ScopeOrganization}, + }) + // Test basic list command + t.Run("Basic", func(t *testing.T) { + t.Parallel() + + inv, root := clitest.New(t, "provisioner", "jobs", "list") + clitest.SetupConfig(t, client, root) + var buf bytes.Buffer + inv.Stdout = &buf + err := inv.Run() + require.NoError(t, err) + + // Should contain both jobs + output := buf.String() + assert.Contains(t, output, job1.ID.String()) + assert.Contains(t, output, job2.ID.String()) + }) + + // Test list with JSON output + t.Run("JSON", func(t *testing.T) { + t.Parallel() + + inv, root := clitest.New(t, "provisioner", "jobs", "list", "--output", "json") + clitest.SetupConfig(t, client, root) + var buf bytes.Buffer + inv.Stdout = &buf + err := inv.Run() + require.NoError(t, err) + + // Parse JSON output + var jobs []codersdk.ProvisionerJob + err = json.Unmarshal(buf.Bytes(), &jobs) + require.NoError(t, err) + + // Should contain both jobs + jobIDs := make([]uuid.UUID, len(jobs)) + for i, job := range jobs { + jobIDs[i] = job.ID + } + assert.Contains(t, jobIDs, job1.ID) + assert.Contains(t, jobIDs, job2.ID) + }) + + // Test list with limit + t.Run("Limit", func(t *testing.T) { + t.Parallel() + + inv, root := clitest.New(t, "provisioner", "jobs", "list", "--limit", "1") + clitest.SetupConfig(t, client, root) + var buf bytes.Buffer + inv.Stdout = &buf + err := inv.Run() + require.NoError(t, err) + + // Should contain at most 1 job + output := buf.String() + jobCount := 0 + if strings.Contains(output, job1.ID.String()) { + jobCount++ + } + if strings.Contains(output, job2.ID.String()) { + jobCount++ + } + assert.LessOrEqual(t, jobCount, 1) + }) + + // Test list with initiator filter + t.Run("InitiatorFilter", func(t *testing.T) { + t.Parallel() + + // Get owner user details to access username + ctx := testutil.Context(t, testutil.WaitShort) + ownerUser, err := client.User(ctx, owner.UserID.String()) + require.NoError(t, err) + + // Test filtering by initiator (using username) + inv, root := clitest.New(t, "provisioner", "jobs", "list", "--initiator", ownerUser.Username) + clitest.SetupConfig(t, client, root) + var buf bytes.Buffer + inv.Stdout = &buf + err = inv.Run() + require.NoError(t, err) + + // Should only contain job1 (initiated by owner) + output := buf.String() + assert.Contains(t, output, job1.ID.String()) + assert.NotContains(t, output, job2.ID.String()) + }) + + // Test list with invalid user + t.Run("InvalidUser", func(t *testing.T) { + t.Parallel() + + // Test with non-existent user + inv, root := clitest.New(t, "provisioner", "jobs", "list", "--initiator", "nonexistent-user") + clitest.SetupConfig(t, client, root) + var buf bytes.Buffer + inv.Stdout = &buf + err := inv.Run() + require.Error(t, err) + assert.Contains(t, err.Error(), "initiator not found: nonexistent-user") + }) + }) } diff --git a/cli/testdata/coder_list_--output_json.golden b/cli/testdata/coder_list_--output_json.golden index 82b73f7b24..66afcf563d 100644 --- a/cli/testdata/coder_list_--output_json.golden +++ b/cli/testdata/coder_list_--output_json.golden @@ -45,6 +45,7 @@ "queue_position": 0, "queue_size": 0, "organization_id": "===========[first org ID]===========", + "initiator_id": "==========[first user ID]===========", "input": { "workspace_build_id": "========[workspace build ID]========" }, diff --git a/cli/testdata/coder_provisioner_jobs_list_--help.golden b/cli/testdata/coder_provisioner_jobs_list_--help.golden index 8e22f78e97..3a581bd880 100644 --- a/cli/testdata/coder_provisioner_jobs_list_--help.golden +++ b/cli/testdata/coder_provisioner_jobs_list_--help.golden @@ -11,9 +11,12 @@ OPTIONS: -O, --org string, $CODER_ORGANIZATION Select which organization (uuid or name) to use. - -c, --column [id|created at|started at|completed at|canceled at|error|error code|status|worker id|worker name|file id|tags|queue position|queue size|organization id|template version id|workspace build id|type|available workers|template version name|template id|template name|template display name|template icon|workspace id|workspace name|logs overflowed|organization|queue] (default: created at,id,type,template display name,status,queue,tags) + -c, --column [id|created at|started at|completed at|canceled at|error|error code|status|worker id|worker name|file id|tags|queue position|queue size|organization id|initiator id|template version id|workspace build id|type|available workers|template version name|template id|template name|template display name|template icon|workspace id|workspace name|logs overflowed|organization|queue] (default: created at,id,type,template display name,status,queue,tags) Columns to display in table output. + -i, --initiator string, $CODER_PROVISIONER_JOB_LIST_INITIATOR + Filter by initiator (user ID or username). + -l, --limit int, $CODER_PROVISIONER_JOB_LIST_LIMIT (default: 50) Limit the number of jobs returned. diff --git a/cli/testdata/coder_provisioner_jobs_list_--output_json.golden b/cli/testdata/coder_provisioner_jobs_list_--output_json.golden index 6ccf672360..3ee6c25e34 100644 --- a/cli/testdata/coder_provisioner_jobs_list_--output_json.golden +++ b/cli/testdata/coder_provisioner_jobs_list_--output_json.golden @@ -15,6 +15,7 @@ "queue_position": 0, "queue_size": 0, "organization_id": "===========[first org ID]===========", + "initiator_id": "==========[first user ID]===========", "input": { "template_version_id": "============[version ID]============" }, @@ -45,6 +46,7 @@ "queue_position": 0, "queue_size": 0, "organization_id": "===========[first org ID]===========", + "initiator_id": "==========[first user ID]===========", "input": { "workspace_build_id": "========[workspace build ID]========" }, diff --git a/coderd/apidoc/docs.go b/coderd/apidoc/docs.go index b93c647dad..289e4c9a3f 100644 --- a/coderd/apidoc/docs.go +++ b/coderd/apidoc/docs.go @@ -3744,6 +3744,13 @@ const docTemplate = `{ "description": "Provisioner tags to filter by (JSON of the form {'tag1':'value1','tag2':'value2'})", "name": "tags", "in": "query" + }, + { + "type": "string", + "format": "uuid", + "description": "Filter results by initiator", + "name": "initiator", + "in": "query" } ], "responses": { @@ -15974,6 +15981,10 @@ const docTemplate = `{ "type": "string", "format": "uuid" }, + "initiator_id": { + "type": "string", + "format": "uuid" + }, "input": { "$ref": "#/definitions/codersdk.ProvisionerJobInput" }, diff --git a/coderd/apidoc/swagger.json b/coderd/apidoc/swagger.json index 731e1720c0..4b3d4c86aa 100644 --- a/coderd/apidoc/swagger.json +++ b/coderd/apidoc/swagger.json @@ -3299,6 +3299,13 @@ "description": "Provisioner tags to filter by (JSON of the form {'tag1':'value1','tag2':'value2'})", "name": "tags", "in": "query" + }, + { + "type": "string", + "format": "uuid", + "description": "Filter results by initiator", + "name": "initiator", + "in": "query" } ], "responses": { @@ -14532,6 +14539,10 @@ "type": "string", "format": "uuid" }, + "initiator_id": { + "type": "string", + "format": "uuid" + }, "input": { "$ref": "#/definitions/codersdk.ProvisionerJobInput" }, diff --git a/coderd/database/dbauthz/dbauthz_test.go b/coderd/database/dbauthz/dbauthz_test.go index 730d5f3198..39350ad948 100644 --- a/coderd/database/dbauthz/dbauthz_test.go +++ b/coderd/database/dbauthz/dbauthz_test.go @@ -2484,10 +2484,12 @@ func (s *MethodTestSuite) TestExtraMethods() { ds, err := db.GetProvisionerJobsByOrganizationAndStatusWithQueuePositionAndProvisioner(context.Background(), database.GetProvisionerJobsByOrganizationAndStatusWithQueuePositionAndProvisionerParams{ OrganizationID: org.ID, + InitiatorID: uuid.Nil, }) s.NoError(err, "get provisioner jobs by org") check.Args(database.GetProvisionerJobsByOrganizationAndStatusWithQueuePositionAndProvisionerParams{ OrganizationID: org.ID, + InitiatorID: uuid.Nil, }).Asserts(j1, policy.ActionRead, j2, policy.ActionRead).Returns(ds) })) } diff --git a/coderd/database/queries.sql.go b/coderd/database/queries.sql.go index 6bf1a3e25d..3612375b8b 100644 --- a/coderd/database/queries.sql.go +++ b/coderd/database/queries.sql.go @@ -9834,6 +9834,7 @@ WHERE AND (COALESCE(array_length($2::uuid[], 1), 0) = 0 OR pj.id = ANY($2::uuid[])) AND (COALESCE(array_length($3::provisioner_job_status[], 1), 0) = 0 OR pj.job_status = ANY($3::provisioner_job_status[])) AND ($4::tagset = 'null'::tagset OR provisioner_tagset_contains(pj.tags::tagset, $4::tagset)) + AND ($5::uuid = '00000000-0000-0000-0000-000000000000'::uuid OR pj.initiator_id = $5::uuid) GROUP BY pj.id, qp.queue_position, @@ -9849,7 +9850,7 @@ GROUP BY ORDER BY pj.created_at DESC LIMIT - $5::int + $6::int ` type GetProvisionerJobsByOrganizationAndStatusWithQueuePositionAndProvisionerParams struct { @@ -9857,6 +9858,7 @@ type GetProvisionerJobsByOrganizationAndStatusWithQueuePositionAndProvisionerPar IDs []uuid.UUID `db:"ids" json:"ids"` Status []ProvisionerJobStatus `db:"status" json:"status"` Tags StringMap `db:"tags" json:"tags"` + InitiatorID uuid.UUID `db:"initiator_id" json:"initiator_id"` Limit sql.NullInt32 `db:"limit" json:"limit"` } @@ -9881,6 +9883,7 @@ func (q *sqlQuerier) GetProvisionerJobsByOrganizationAndStatusWithQueuePositionA pq.Array(arg.IDs), pq.Array(arg.Status), arg.Tags, + arg.InitiatorID, arg.Limit, ) if err != nil { diff --git a/coderd/database/queries/provisionerjobs.sql b/coderd/database/queries/provisionerjobs.sql index dfc95a0bb4..02d67d628a 100644 --- a/coderd/database/queries/provisionerjobs.sql +++ b/coderd/database/queries/provisionerjobs.sql @@ -224,6 +224,7 @@ WHERE AND (COALESCE(array_length(@ids::uuid[], 1), 0) = 0 OR pj.id = ANY(@ids::uuid[])) AND (COALESCE(array_length(@status::provisioner_job_status[], 1), 0) = 0 OR pj.job_status = ANY(@status::provisioner_job_status[])) AND (@tags::tagset = 'null'::tagset OR provisioner_tagset_contains(pj.tags::tagset, @tags::tagset)) + AND (@initiator_id::uuid = '00000000-0000-0000-0000-000000000000'::uuid OR pj.initiator_id = @initiator_id::uuid) GROUP BY pj.id, qp.queue_position, diff --git a/coderd/provisionerjobs.go b/coderd/provisionerjobs.go index e9ab526098..4ba923dae2 100644 --- a/coderd/provisionerjobs.go +++ b/coderd/provisionerjobs.go @@ -76,6 +76,7 @@ func (api *API) provisionerJob(rw http.ResponseWriter, r *http.Request) { // @Param ids query []string false "Filter results by job IDs" format(uuid) // @Param status query codersdk.ProvisionerJobStatus false "Filter results by status" enums(pending,running,succeeded,canceling,canceled,failed) // @Param tags query object false "Provisioner tags to filter by (JSON of the form {'tag1':'value1','tag2':'value2'})" +// @Param initiator query string false "Filter results by initiator" format(uuid) // @Success 200 {array} codersdk.ProvisionerJob // @Router /organizations/{organization}/provisionerjobs [get] func (api *API) provisionerJobs(rw http.ResponseWriter, r *http.Request) { @@ -110,6 +111,7 @@ func (api *API) handleAuthAndFetchProvisionerJobs(rw http.ResponseWriter, r *htt ids = p.UUIDs(qp, nil, "ids") } tags := p.JSONStringMap(qp, database.StringMap{}, "tags") + initiatorID := p.UUID(qp, uuid.Nil, "initiator_id") p.ErrorExcessParams(qp) if len(p.Errors) > 0 { httpapi.Write(ctx, rw, http.StatusBadRequest, codersdk.Response{ @@ -125,6 +127,7 @@ func (api *API) handleAuthAndFetchProvisionerJobs(rw http.ResponseWriter, r *htt Limit: sql.NullInt32{Int32: limit, Valid: limit > 0}, IDs: ids, Tags: tags, + InitiatorID: initiatorID, }) if err != nil { if httpapi.Is404Error(err) { @@ -355,6 +358,7 @@ func convertProvisionerJob(pj database.GetProvisionerJobsByIDsWithQueuePositionR job := codersdk.ProvisionerJob{ ID: provisionerJob.ID, OrganizationID: provisionerJob.OrganizationID, + InitiatorID: provisionerJob.InitiatorID, CreatedAt: provisionerJob.CreatedAt, Type: codersdk.ProvisionerJobType(provisionerJob.Type), Error: provisionerJob.Error.String, diff --git a/coderd/provisionerjobs_test.go b/coderd/provisionerjobs_test.go index 98da3ae558..829c72aa4d 100644 --- a/coderd/provisionerjobs_test.go +++ b/coderd/provisionerjobs_test.go @@ -58,6 +58,8 @@ func TestProvisionerJobs(t *testing.T) { StartedAt: sql.NullTime{Time: dbtime.Now(), Valid: true}, Type: database.ProvisionerJobTypeWorkspaceBuild, Input: json.RawMessage(`{"workspace_build_id":"` + wbID.String() + `"}`), + InitiatorID: member.ID, + Tags: database.StringMap{"initiatorTest": "true"}, }) dbgen.WorkspaceBuild(t, db, database.WorkspaceBuild{ ID: wbID, @@ -71,6 +73,7 @@ func TestProvisionerJobs(t *testing.T) { dbgen.ProvisionerJob(t, db, nil, database.ProvisionerJob{ OrganizationID: owner.OrganizationID, Tags: database.StringMap{"count": strconv.Itoa(i)}, + InitiatorID: owner.UserID, }) } @@ -165,6 +168,94 @@ func TestProvisionerJobs(t *testing.T) { require.Len(t, jobs, 1) }) + t.Run("Initiator", func(t *testing.T) { + t.Parallel() + ctx := testutil.Context(t, testutil.WaitMedium) + + jobs, err := templateAdminClient.OrganizationProvisionerJobs(ctx, owner.OrganizationID, &codersdk.OrganizationProvisionerJobsOptions{ + InitiatorID: &member.ID, + }) + require.NoError(t, err) + require.GreaterOrEqual(t, len(jobs), 1) + require.Equal(t, member.ID, jobs[0].InitiatorID) + }) + + t.Run("InitiatorWithOtherFilters", func(t *testing.T) { + t.Parallel() + ctx := testutil.Context(t, testutil.WaitMedium) + + // Test filtering by initiator ID combined with status filter + jobs, err := templateAdminClient.OrganizationProvisionerJobs(ctx, owner.OrganizationID, &codersdk.OrganizationProvisionerJobsOptions{ + InitiatorID: &owner.UserID, + Status: []codersdk.ProvisionerJobStatus{codersdk.ProvisionerJobSucceeded}, + }) + require.NoError(t, err) + + // Verify all returned jobs have the correct initiator and status + for _, job := range jobs { + require.Equal(t, owner.UserID, job.InitiatorID) + require.Equal(t, codersdk.ProvisionerJobSucceeded, job.Status) + } + }) + + t.Run("InitiatorWithLimit", func(t *testing.T) { + t.Parallel() + ctx := testutil.Context(t, testutil.WaitMedium) + + // Test filtering by initiator ID with limit + jobs, err := templateAdminClient.OrganizationProvisionerJobs(ctx, owner.OrganizationID, &codersdk.OrganizationProvisionerJobsOptions{ + InitiatorID: &owner.UserID, + Limit: 1, + }) + require.NoError(t, err) + require.Len(t, jobs, 1) + + // Verify the returned job has the correct initiator + require.Equal(t, owner.UserID, jobs[0].InitiatorID) + }) + + t.Run("InitiatorWithTags", func(t *testing.T) { + t.Parallel() + ctx := testutil.Context(t, testutil.WaitMedium) + + // Test filtering by initiator ID combined with tags + jobs, err := templateAdminClient.OrganizationProvisionerJobs(ctx, owner.OrganizationID, &codersdk.OrganizationProvisionerJobsOptions{ + InitiatorID: &member.ID, + Tags: map[string]string{"initiatorTest": "true"}, + }) + require.NoError(t, err) + require.Len(t, jobs, 1) + + // Verify the returned job has the correct initiator and tags + require.Equal(t, member.ID, jobs[0].InitiatorID) + require.Equal(t, "true", jobs[0].Tags["initiatorTest"]) + }) + + t.Run("InitiatorNotFound", func(t *testing.T) { + t.Parallel() + ctx := testutil.Context(t, testutil.WaitMedium) + + // Test with non-existent initiator ID + nonExistentID := uuid.New() + jobs, err := templateAdminClient.OrganizationProvisionerJobs(ctx, owner.OrganizationID, &codersdk.OrganizationProvisionerJobsOptions{ + InitiatorID: &nonExistentID, + }) + require.NoError(t, err) + require.Len(t, jobs, 0) + }) + + t.Run("InitiatorNil", func(t *testing.T) { + t.Parallel() + ctx := testutil.Context(t, testutil.WaitMedium) + + // Test with nil initiator ID (should return all jobs) + jobs, err := templateAdminClient.OrganizationProvisionerJobs(ctx, owner.OrganizationID, &codersdk.OrganizationProvisionerJobsOptions{ + InitiatorID: nil, + }) + require.NoError(t, err) + require.GreaterOrEqual(t, len(jobs), 50) // Should return all jobs (up to default limit) + }) + t.Run("Limit", func(t *testing.T) { t.Parallel() ctx := testutil.Context(t, testutil.WaitMedium) @@ -185,6 +276,17 @@ func TestProvisionerJobs(t *testing.T) { require.Error(t, err) require.Len(t, jobs, 0) }) + + t.Run("MemberDeniedWithInitiator", func(t *testing.T) { + t.Parallel() + ctx := testutil.Context(t, testutil.WaitMedium) + // Member should not be able to access jobs even with initiator filter + jobs, err := memberClient.OrganizationProvisionerJobs(ctx, owner.OrganizationID, &codersdk.OrganizationProvisionerJobsOptions{ + InitiatorID: &member.ID, + }) + require.Error(t, err) + require.Len(t, jobs, 0) + }) }) // Ensures that when a provisioner job is in the succeeded state, diff --git a/codersdk/organizations.go b/codersdk/organizations.go index bca87c7bd4..291bb9ac1b 100644 --- a/codersdk/organizations.go +++ b/codersdk/organizations.go @@ -397,10 +397,11 @@ func (c *Client) OrganizationProvisionerDaemons(ctx context.Context, organizatio } type OrganizationProvisionerJobsOptions struct { - Limit int - IDs []uuid.UUID - Status []ProvisionerJobStatus - Tags map[string]string + Limit int + IDs []uuid.UUID + Status []ProvisionerJobStatus + Tags map[string]string + InitiatorID *uuid.UUID } func (c *Client) OrganizationProvisionerJobs(ctx context.Context, organizationID uuid.UUID, opts *OrganizationProvisionerJobsOptions) ([]ProvisionerJob, error) { @@ -422,6 +423,9 @@ func (c *Client) OrganizationProvisionerJobs(ctx context.Context, organizationID } qp.Add("tags", string(tagsRaw)) } + if opts.InitiatorID != nil { + qp.Add("initiator_id", opts.InitiatorID.String()) + } } res, err := c.Request(ctx, http.MethodGet, diff --git a/codersdk/provisionerdaemons.go b/codersdk/provisionerdaemons.go index b3cefa0911..19f8cae546 100644 --- a/codersdk/provisionerdaemons.go +++ b/codersdk/provisionerdaemons.go @@ -198,6 +198,7 @@ type ProvisionerJob struct { QueuePosition int `json:"queue_position" table:"queue position"` QueueSize int `json:"queue_size" table:"queue size"` OrganizationID uuid.UUID `json:"organization_id" format:"uuid" table:"organization id"` + InitiatorID uuid.UUID `json:"initiator_id" format:"uuid" table:"initiator id"` Input ProvisionerJobInput `json:"input" table:"input,recursive_inline"` Type ProvisionerJobType `json:"type" table:"type"` AvailableWorkers []uuid.UUID `json:"available_workers,omitempty" format:"uuid" table:"available workers"` diff --git a/docs/reference/api/builds.md b/docs/reference/api/builds.md index 232509052b..41df0b9efa 100644 --- a/docs/reference/api/builds.md +++ b/docs/reference/api/builds.md @@ -48,6 +48,7 @@ curl -X GET http://coder-server:8080/api/v2/users/{user}/workspace/{workspacenam "error_code": "REQUIRED_TEMPLATE_VARIABLES", "file_id": "8a0cfb4f-ddc9-436d-91bb-75133c583767", "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "initiator_id": "06588898-9a84-4b35-ba8f-f9cbd64946f3", "input": { "error": "string", "template_version_id": "0ba39c92-1f1b-4c32-aa3e-9925d7713eb1", @@ -288,6 +289,7 @@ curl -X GET http://coder-server:8080/api/v2/workspacebuilds/{workspacebuild} \ "error_code": "REQUIRED_TEMPLATE_VARIABLES", "file_id": "8a0cfb4f-ddc9-436d-91bb-75133c583767", "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "initiator_id": "06588898-9a84-4b35-ba8f-f9cbd64946f3", "input": { "error": "string", "template_version_id": "0ba39c92-1f1b-4c32-aa3e-9925d7713eb1", @@ -1019,6 +1021,7 @@ curl -X GET http://coder-server:8080/api/v2/workspacebuilds/{workspacebuild}/sta "error_code": "REQUIRED_TEMPLATE_VARIABLES", "file_id": "8a0cfb4f-ddc9-436d-91bb-75133c583767", "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "initiator_id": "06588898-9a84-4b35-ba8f-f9cbd64946f3", "input": { "error": "string", "template_version_id": "0ba39c92-1f1b-4c32-aa3e-9925d7713eb1", @@ -1332,6 +1335,7 @@ curl -X GET http://coder-server:8080/api/v2/workspaces/{workspace}/builds \ "error_code": "REQUIRED_TEMPLATE_VARIABLES", "file_id": "8a0cfb4f-ddc9-436d-91bb-75133c583767", "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "initiator_id": "06588898-9a84-4b35-ba8f-f9cbd64946f3", "input": { "error": "string", "template_version_id": "0ba39c92-1f1b-4c32-aa3e-9925d7713eb1", @@ -1551,6 +1555,7 @@ Status Code **200** | `»» error_code` | [codersdk.JobErrorCode](schemas.md#codersdkjoberrorcode) | false | | | | `»» file_id` | string(uuid) | false | | | | `»» id` | string(uuid) | false | | | +| `»» initiator_id` | string(uuid) | false | | | | `»» input` | [codersdk.ProvisionerJobInput](schemas.md#codersdkprovisionerjobinput) | false | | | | `»»» error` | string | false | | | | `»»» template_version_id` | string(uuid) | false | | | @@ -1829,6 +1834,7 @@ curl -X POST http://coder-server:8080/api/v2/workspaces/{workspace}/builds \ "error_code": "REQUIRED_TEMPLATE_VARIABLES", "file_id": "8a0cfb4f-ddc9-436d-91bb-75133c583767", "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "initiator_id": "06588898-9a84-4b35-ba8f-f9cbd64946f3", "input": { "error": "string", "template_version_id": "0ba39c92-1f1b-4c32-aa3e-9925d7713eb1", diff --git a/docs/reference/api/organizations.md b/docs/reference/api/organizations.md index d418a1fcba..ffd6f78405 100644 --- a/docs/reference/api/organizations.md +++ b/docs/reference/api/organizations.md @@ -366,6 +366,7 @@ curl -X GET http://coder-server:8080/api/v2/organizations/{organization}/provisi | `ids` | query | array(uuid) | false | Filter results by job IDs | | `status` | query | string | false | Filter results by status | | `tags` | query | object | false | Provisioner tags to filter by (JSON of the form {'tag1':'value1','tag2':'value2'}) | +| `initiator` | query | string(uuid) | false | Filter results by initiator | #### Enumerated Values @@ -402,6 +403,7 @@ curl -X GET http://coder-server:8080/api/v2/organizations/{organization}/provisi "error_code": "REQUIRED_TEMPLATE_VARIABLES", "file_id": "8a0cfb4f-ddc9-436d-91bb-75133c583767", "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "initiator_id": "06588898-9a84-4b35-ba8f-f9cbd64946f3", "input": { "error": "string", "template_version_id": "0ba39c92-1f1b-4c32-aa3e-9925d7713eb1", @@ -454,6 +456,7 @@ Status Code **200** | `» error_code` | [codersdk.JobErrorCode](schemas.md#codersdkjoberrorcode) | false | | | | `» file_id` | string(uuid) | false | | | | `» id` | string(uuid) | false | | | +| `» initiator_id` | string(uuid) | false | | | | `» input` | [codersdk.ProvisionerJobInput](schemas.md#codersdkprovisionerjobinput) | false | | | | `»» error` | string | false | | | | `»» template_version_id` | string(uuid) | false | | | @@ -531,6 +534,7 @@ curl -X GET http://coder-server:8080/api/v2/organizations/{organization}/provisi "error_code": "REQUIRED_TEMPLATE_VARIABLES", "file_id": "8a0cfb4f-ddc9-436d-91bb-75133c583767", "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "initiator_id": "06588898-9a84-4b35-ba8f-f9cbd64946f3", "input": { "error": "string", "template_version_id": "0ba39c92-1f1b-4c32-aa3e-9925d7713eb1", diff --git a/docs/reference/api/schemas.md b/docs/reference/api/schemas.md index 98324941a1..33cb280ae1 100644 --- a/docs/reference/api/schemas.md +++ b/docs/reference/api/schemas.md @@ -6390,6 +6390,7 @@ Only certain features set these fields: - FeatureManagedAgentLimit| "error_code": "REQUIRED_TEMPLATE_VARIABLES", "file_id": "8a0cfb4f-ddc9-436d-91bb-75133c583767", "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "initiator_id": "06588898-9a84-4b35-ba8f-f9cbd64946f3", "input": { "error": "string", "template_version_id": "0ba39c92-1f1b-4c32-aa3e-9925d7713eb1", @@ -6432,6 +6433,7 @@ Only certain features set these fields: - FeatureManagedAgentLimit| | `error_code` | [codersdk.JobErrorCode](#codersdkjoberrorcode) | false | | | | `file_id` | string | false | | | | `id` | string | false | | | +| `initiator_id` | string | false | | | | `input` | [codersdk.ProvisionerJobInput](#codersdkprovisionerjobinput) | false | | | | `logs_overflowed` | boolean | false | | | | `metadata` | [codersdk.ProvisionerJobMetadata](#codersdkprovisionerjobmetadata) | false | | | @@ -8118,6 +8120,7 @@ Restarts will only happen on weekdays in this list on weeks which line up with W "error_code": "REQUIRED_TEMPLATE_VARIABLES", "file_id": "8a0cfb4f-ddc9-436d-91bb-75133c583767", "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "initiator_id": "06588898-9a84-4b35-ba8f-f9cbd64946f3", "input": { "error": "string", "template_version_id": "0ba39c92-1f1b-4c32-aa3e-9925d7713eb1", @@ -9386,6 +9389,7 @@ If the schedule is empty, the user will be updated to use the default schedule.| "error_code": "REQUIRED_TEMPLATE_VARIABLES", "file_id": "8a0cfb4f-ddc9-436d-91bb-75133c583767", "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "initiator_id": "06588898-9a84-4b35-ba8f-f9cbd64946f3", "input": { "error": "string", "template_version_id": "0ba39c92-1f1b-4c32-aa3e-9925d7713eb1", @@ -10553,6 +10557,7 @@ If the schedule is empty, the user will be updated to use the default schedule.| "error_code": "REQUIRED_TEMPLATE_VARIABLES", "file_id": "8a0cfb4f-ddc9-436d-91bb-75133c583767", "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "initiator_id": "06588898-9a84-4b35-ba8f-f9cbd64946f3", "input": { "error": "string", "template_version_id": "0ba39c92-1f1b-4c32-aa3e-9925d7713eb1", @@ -11389,6 +11394,7 @@ If the schedule is empty, the user will be updated to use the default schedule.| "error_code": "REQUIRED_TEMPLATE_VARIABLES", "file_id": "8a0cfb4f-ddc9-436d-91bb-75133c583767", "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "initiator_id": "06588898-9a84-4b35-ba8f-f9cbd64946f3", "input": { "error": "string", "template_version_id": "0ba39c92-1f1b-4c32-aa3e-9925d7713eb1", diff --git a/docs/reference/api/templates.md b/docs/reference/api/templates.md index efc59cf7b5..9d69949ac7 100644 --- a/docs/reference/api/templates.md +++ b/docs/reference/api/templates.md @@ -475,6 +475,7 @@ curl -X GET http://coder-server:8080/api/v2/organizations/{organization}/templat "error_code": "REQUIRED_TEMPLATE_VARIABLES", "file_id": "8a0cfb4f-ddc9-436d-91bb-75133c583767", "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "initiator_id": "06588898-9a84-4b35-ba8f-f9cbd64946f3", "input": { "error": "string", "template_version_id": "0ba39c92-1f1b-4c32-aa3e-9925d7713eb1", @@ -575,6 +576,7 @@ curl -X GET http://coder-server:8080/api/v2/organizations/{organization}/templat "error_code": "REQUIRED_TEMPLATE_VARIABLES", "file_id": "8a0cfb4f-ddc9-436d-91bb-75133c583767", "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "initiator_id": "06588898-9a84-4b35-ba8f-f9cbd64946f3", "input": { "error": "string", "template_version_id": "0ba39c92-1f1b-4c32-aa3e-9925d7713eb1", @@ -699,6 +701,7 @@ curl -X POST http://coder-server:8080/api/v2/organizations/{organization}/templa "error_code": "REQUIRED_TEMPLATE_VARIABLES", "file_id": "8a0cfb4f-ddc9-436d-91bb-75133c583767", "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "initiator_id": "06588898-9a84-4b35-ba8f-f9cbd64946f3", "input": { "error": "string", "template_version_id": "0ba39c92-1f1b-4c32-aa3e-9925d7713eb1", @@ -1306,6 +1309,7 @@ curl -X GET http://coder-server:8080/api/v2/templates/{template}/versions \ "error_code": "REQUIRED_TEMPLATE_VARIABLES", "file_id": "8a0cfb4f-ddc9-436d-91bb-75133c583767", "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "initiator_id": "06588898-9a84-4b35-ba8f-f9cbd64946f3", "input": { "error": "string", "template_version_id": "0ba39c92-1f1b-4c32-aa3e-9925d7713eb1", @@ -1382,6 +1386,7 @@ Status Code **200** | `»» error_code` | [codersdk.JobErrorCode](schemas.md#codersdkjoberrorcode) | false | | | | `»» file_id` | string(uuid) | false | | | | `»» id` | string(uuid) | false | | | +| `»» initiator_id` | string(uuid) | false | | | | `»» input` | [codersdk.ProvisionerJobInput](schemas.md#codersdkprovisionerjobinput) | false | | | | `»»» error` | string | false | | | | `»»» template_version_id` | string(uuid) | false | | | @@ -1589,6 +1594,7 @@ curl -X GET http://coder-server:8080/api/v2/templates/{template}/versions/{templ "error_code": "REQUIRED_TEMPLATE_VARIABLES", "file_id": "8a0cfb4f-ddc9-436d-91bb-75133c583767", "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "initiator_id": "06588898-9a84-4b35-ba8f-f9cbd64946f3", "input": { "error": "string", "template_version_id": "0ba39c92-1f1b-4c32-aa3e-9925d7713eb1", @@ -1665,6 +1671,7 @@ Status Code **200** | `»» error_code` | [codersdk.JobErrorCode](schemas.md#codersdkjoberrorcode) | false | | | | `»» file_id` | string(uuid) | false | | | | `»» id` | string(uuid) | false | | | +| `»» initiator_id` | string(uuid) | false | | | | `»» input` | [codersdk.ProvisionerJobInput](schemas.md#codersdkprovisionerjobinput) | false | | | | `»»» error` | string | false | | | | `»»» template_version_id` | string(uuid) | false | | | @@ -1762,6 +1769,7 @@ curl -X GET http://coder-server:8080/api/v2/templateversions/{templateversion} \ "error_code": "REQUIRED_TEMPLATE_VARIABLES", "file_id": "8a0cfb4f-ddc9-436d-91bb-75133c583767", "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "initiator_id": "06588898-9a84-4b35-ba8f-f9cbd64946f3", "input": { "error": "string", "template_version_id": "0ba39c92-1f1b-4c32-aa3e-9925d7713eb1", @@ -1871,6 +1879,7 @@ curl -X PATCH http://coder-server:8080/api/v2/templateversions/{templateversion} "error_code": "REQUIRED_TEMPLATE_VARIABLES", "file_id": "8a0cfb4f-ddc9-436d-91bb-75133c583767", "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "initiator_id": "06588898-9a84-4b35-ba8f-f9cbd64946f3", "input": { "error": "string", "template_version_id": "0ba39c92-1f1b-4c32-aa3e-9925d7713eb1", @@ -2069,6 +2078,7 @@ curl -X POST http://coder-server:8080/api/v2/templateversions/{templateversion}/ "error_code": "REQUIRED_TEMPLATE_VARIABLES", "file_id": "8a0cfb4f-ddc9-436d-91bb-75133c583767", "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "initiator_id": "06588898-9a84-4b35-ba8f-f9cbd64946f3", "input": { "error": "string", "template_version_id": "0ba39c92-1f1b-4c32-aa3e-9925d7713eb1", @@ -2143,6 +2153,7 @@ curl -X GET http://coder-server:8080/api/v2/templateversions/{templateversion}/d "error_code": "REQUIRED_TEMPLATE_VARIABLES", "file_id": "8a0cfb4f-ddc9-436d-91bb-75133c583767", "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "initiator_id": "06588898-9a84-4b35-ba8f-f9cbd64946f3", "input": { "error": "string", "template_version_id": "0ba39c92-1f1b-4c32-aa3e-9925d7713eb1", diff --git a/docs/reference/api/workspaces.md b/docs/reference/api/workspaces.md index 455fefcb57..e2a95613ed 100644 --- a/docs/reference/api/workspaces.md +++ b/docs/reference/api/workspaces.md @@ -103,6 +103,7 @@ of the template will be used. "error_code": "REQUIRED_TEMPLATE_VARIABLES", "file_id": "8a0cfb4f-ddc9-436d-91bb-75133c583767", "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "initiator_id": "06588898-9a84-4b35-ba8f-f9cbd64946f3", "input": { "error": "string", "template_version_id": "0ba39c92-1f1b-4c32-aa3e-9925d7713eb1", @@ -393,6 +394,7 @@ curl -X GET http://coder-server:8080/api/v2/users/{user}/workspace/{workspacenam "error_code": "REQUIRED_TEMPLATE_VARIABLES", "file_id": "8a0cfb4f-ddc9-436d-91bb-75133c583767", "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "initiator_id": "06588898-9a84-4b35-ba8f-f9cbd64946f3", "input": { "error": "string", "template_version_id": "0ba39c92-1f1b-4c32-aa3e-9925d7713eb1", @@ -708,6 +710,7 @@ of the template will be used. "error_code": "REQUIRED_TEMPLATE_VARIABLES", "file_id": "8a0cfb4f-ddc9-436d-91bb-75133c583767", "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "initiator_id": "06588898-9a84-4b35-ba8f-f9cbd64946f3", "input": { "error": "string", "template_version_id": "0ba39c92-1f1b-4c32-aa3e-9925d7713eb1", @@ -1001,6 +1004,7 @@ curl -X GET http://coder-server:8080/api/v2/workspaces \ "error_code": "REQUIRED_TEMPLATE_VARIABLES", "file_id": "8a0cfb4f-ddc9-436d-91bb-75133c583767", "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "initiator_id": "06588898-9a84-4b35-ba8f-f9cbd64946f3", "input": { "error": "string", "template_version_id": "0ba39c92-1f1b-4c32-aa3e-9925d7713eb1", @@ -1275,6 +1279,7 @@ curl -X GET http://coder-server:8080/api/v2/workspaces/{workspace} \ "error_code": "REQUIRED_TEMPLATE_VARIABLES", "file_id": "8a0cfb4f-ddc9-436d-91bb-75133c583767", "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "initiator_id": "06588898-9a84-4b35-ba8f-f9cbd64946f3", "input": { "error": "string", "template_version_id": "0ba39c92-1f1b-4c32-aa3e-9925d7713eb1", @@ -1824,6 +1829,7 @@ curl -X PUT http://coder-server:8080/api/v2/workspaces/{workspace}/dormant \ "error_code": "REQUIRED_TEMPLATE_VARIABLES", "file_id": "8a0cfb4f-ddc9-436d-91bb-75133c583767", "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "initiator_id": "06588898-9a84-4b35-ba8f-f9cbd64946f3", "input": { "error": "string", "template_version_id": "0ba39c92-1f1b-4c32-aa3e-9925d7713eb1", diff --git a/docs/reference/cli/provisioner_jobs_list.md b/docs/reference/cli/provisioner_jobs_list.md index a0bff8554d..0167dd467d 100644 --- a/docs/reference/cli/provisioner_jobs_list.md +++ b/docs/reference/cli/provisioner_jobs_list.md @@ -34,6 +34,15 @@ Filter by job status. Limit the number of jobs returned. +### -i, --initiator + +| | | +|-------------|----------------------------------------------------| +| Type | string | +| Environment | $CODER_PROVISIONER_JOB_LIST_INITIATOR | + +Filter by initiator (user ID or username). + ### -O, --org | | | @@ -45,10 +54,10 @@ Select which organization (uuid or name) to use. ### -c, --column -| | | -|---------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| Type | [id\|created at\|started at\|completed at\|canceled at\|error\|error code\|status\|worker id\|worker name\|file id\|tags\|queue position\|queue size\|organization id\|template version id\|workspace build id\|type\|available workers\|template version name\|template id\|template name\|template display name\|template icon\|workspace id\|workspace name\|logs overflowed\|organization\|queue] | -| Default | created at,id,type,template display name,status,queue,tags | +| | | +|---------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| Type | [id\|created at\|started at\|completed at\|canceled at\|error\|error code\|status\|worker id\|worker name\|file id\|tags\|queue position\|queue size\|organization id\|initiator id\|template version id\|workspace build id\|type\|available workers\|template version name\|template id\|template name\|template display name\|template icon\|workspace id\|workspace name\|logs overflowed\|organization\|queue] | +| Default | created at,id,type,template display name,status,queue,tags | Columns to display in table output. diff --git a/enterprise/cli/testdata/coder_provisioner_jobs_list_--help.golden b/enterprise/cli/testdata/coder_provisioner_jobs_list_--help.golden index 8e22f78e97..3a581bd880 100644 --- a/enterprise/cli/testdata/coder_provisioner_jobs_list_--help.golden +++ b/enterprise/cli/testdata/coder_provisioner_jobs_list_--help.golden @@ -11,9 +11,12 @@ OPTIONS: -O, --org string, $CODER_ORGANIZATION Select which organization (uuid or name) to use. - -c, --column [id|created at|started at|completed at|canceled at|error|error code|status|worker id|worker name|file id|tags|queue position|queue size|organization id|template version id|workspace build id|type|available workers|template version name|template id|template name|template display name|template icon|workspace id|workspace name|logs overflowed|organization|queue] (default: created at,id,type,template display name,status,queue,tags) + -c, --column [id|created at|started at|completed at|canceled at|error|error code|status|worker id|worker name|file id|tags|queue position|queue size|organization id|initiator id|template version id|workspace build id|type|available workers|template version name|template id|template name|template display name|template icon|workspace id|workspace name|logs overflowed|organization|queue] (default: created at,id,type,template display name,status,queue,tags) Columns to display in table output. + -i, --initiator string, $CODER_PROVISIONER_JOB_LIST_INITIATOR + Filter by initiator (user ID or username). + -l, --limit int, $CODER_PROVISIONER_JOB_LIST_LIMIT (default: 50) Limit the number of jobs returned. diff --git a/site/src/api/typesGenerated.ts b/site/src/api/typesGenerated.ts index 0519c9c136..e7c612078b 100644 --- a/site/src/api/typesGenerated.ts +++ b/site/src/api/typesGenerated.ts @@ -2060,6 +2060,7 @@ export interface OrganizationProvisionerJobsOptions { readonly IDs: readonly string[]; readonly Status: readonly ProvisionerJobStatus[]; readonly Tags: Record; + readonly InitiatorID: string | null; } // From codersdk/idpsync.go @@ -2379,6 +2380,7 @@ export interface ProvisionerJob { readonly queue_position: number; readonly queue_size: number; readonly organization_id: string; + readonly initiator_id: string; readonly input: ProvisionerJobInput; readonly type: ProvisionerJobType; readonly available_workers?: readonly string[]; diff --git a/site/src/testHelpers/entities.ts b/site/src/testHelpers/entities.ts index 19c414bdd3..0b8d87f811 100644 --- a/site/src/testHelpers/entities.ts +++ b/site/src/testHelpers/entities.ts @@ -679,6 +679,7 @@ export const MockProvisionerJob: TypesGen.ProvisionerJob = { status: "succeeded", file_id: MockOrganization.id, completed_at: "2022-05-17T17:39:01.382927298Z", + initiator_id: MockUserMember.id, tags: { scope: "organization", owner: "",