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

feat: add filtering by initiator to provisioner job listing in the CLI#20137

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to ourterms of service andprivacy statement. We’ll occasionally send you account related emails.

Already on GitHub?Sign in to your account

Merged
SasSwart merged 8 commits intomainfromjjs/internal-934
Oct 6, 2025
Merged
Show file tree
Hide file tree
Changes fromall commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 23 additions & 4 deletionscli/provisionerjobs.go
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -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{
Expand All@@ -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)
Expand DownExpand Up@@ -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)
Expand Down
192 changes: 168 additions & 24 deletionscli/provisionerjobs_test.go
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -5,6 +5,7 @@ import (
"database/sql"
"encoding/json"
"fmt"
"strings"
"testing"
"time"

Expand All@@ -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)
Copy link
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

Moving this inside the subtest to properly isolate these tests from the list subcommand tests

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()
Expand DownExpand Up@@ -178,4 +178,148 @@ func TestProvisionerJobs(t *testing.T) {
})
}
})

t.Run("List", func(t *testing.T) {
Copy link
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

There were no tests for the list subcommand. I've added some basic test cases as a scouting effort, but I focus on the tests to prove my own work right now.

ssncferreira reacted with thumbs up emoji
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")
})
})
}
1 change: 1 addition & 0 deletionscli/testdata/coder_list_--output_json.golden
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -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]========"
},
Expand Down
5 changes: 4 additions & 1 deletioncli/testdata/coder_provisioner_jobs_list_--help.golden
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -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.

Expand Down
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -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]============"
},
Expand DownExpand Up@@ -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]========"
},
Expand Down
11 changes: 11 additions & 0 deletionscoderd/apidoc/docs.go
View file
Open in desktop

Some generated files are not rendered by default. Learn more abouthow customized files appear on GitHub.

11 changes: 11 additions & 0 deletionscoderd/apidoc/swagger.json
View file
Open in desktop

Some generated files are not rendered by default. Learn more abouthow customized files appear on GitHub.

2 changes: 2 additions & 0 deletionscoderd/database/dbauthz/dbauthz_test.go
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -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)
}))
}
Expand Down
5 changes: 4 additions & 1 deletioncoderd/database/queries.sql.go
View file
Open in desktop

Some generated files are not rendered by default. Learn more abouthow customized files appear on GitHub.

Loading
Loading

[8]ページ先頭

©2009-2025 Movatter.jp