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

Commitc0d3650

Browse files
committed
feat: allow the provisioner jobs list command to filter by initiator
1 parentd2c286d commitc0d3650

File tree

14 files changed

+239
-33
lines changed

14 files changed

+239
-33
lines changed

‎cli/provisionerjobs.go‎

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,9 @@ func (r *RootCmd) provisionerJobsList() *serpent.Command {
4343
cliui.TableFormat([]provisionerJobRow{}, []string{"created at","id","type","template display name","status","queue","tags"}),
4444
cliui.JSONFormat(),
4545
)
46-
status []string
47-
limitint64
46+
status []string
47+
limitint64
48+
initiatorIDStrstring
4849
)
4950

5051
cmd:=&serpent.Command{
@@ -65,9 +66,20 @@ func (r *RootCmd) provisionerJobsList() *serpent.Command {
6566
returnxerrors.Errorf("current organization: %w",err)
6667
}
6768

69+
varinitiatorID*uuid.UUID
70+
71+
ifinitiatorIDStr!="" {
72+
user,err:=client.User(ctx,initiatorIDStr)
73+
iferr!=nil {
74+
returnxerrors.Errorf("initiator not found: %s",initiatorIDStr)
75+
}
76+
initiatorID=&user.ID
77+
}
78+
6879
jobs,err:=client.OrganizationProvisionerJobs(ctx,org.ID,&codersdk.OrganizationProvisionerJobsOptions{
69-
Status: slice.StringEnums[codersdk.ProvisionerJobStatus](status),
70-
Limit:int(limit),
80+
Status: slice.StringEnums[codersdk.ProvisionerJobStatus](status),
81+
Limit:int(limit),
82+
InitiatorID:initiatorID,
7183
})
7284
iferr!=nil {
7385
returnxerrors.Errorf("list provisioner jobs: %w",err)
@@ -122,6 +134,12 @@ func (r *RootCmd) provisionerJobsList() *serpent.Command {
122134
Default:"50",
123135
Value:serpent.Int64Of(&limit),
124136
},
137+
{
138+
Flag:"initiator",
139+
Env:"CODER_PROVISIONER_JOB_LIST_INITIATOR",
140+
Description:"Filter by initiator (user ID or username).",
141+
Value:serpent.StringOf(&initiatorIDStr),
142+
},
125143
}...)
126144

127145
orgContext.AttachOptions(cmd)

‎cli/provisionerjobs_test.go‎

Lines changed: 168 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"database/sql"
66
"encoding/json"
77
"fmt"
8+
"strings"
89
"testing"
910
"time"
1011

@@ -26,33 +27,32 @@ import (
2627
funcTestProvisionerJobs(t*testing.T) {
2728
t.Parallel()
2829

29-
db,ps:=dbtestutil.NewDB(t)
30-
client,_,coderdAPI:=coderdtest.NewWithAPI(t,&coderdtest.Options{
31-
IncludeProvisionerDaemon:false,
32-
Database:db,
33-
Pubsub:ps,
34-
})
35-
owner:=coderdtest.CreateFirstUser(t,client)
36-
templateAdminClient,templateAdmin:=coderdtest.CreateAnotherUser(t,client,owner.OrganizationID,rbac.ScopedRoleOrgTemplateAdmin(owner.OrganizationID))
37-
memberClient,member:=coderdtest.CreateAnotherUser(t,client,owner.OrganizationID)
38-
39-
// These CLI tests are related to provisioner job CRUD operations and as such
40-
// do not require the overhead of starting a provisioner. Other provisioner job
41-
// functionalities (acquisition etc.) are tested elsewhere.
42-
template:=dbgen.Template(t,db, database.Template{
43-
OrganizationID:owner.OrganizationID,
44-
CreatedBy:owner.UserID,
45-
AllowUserCancelWorkspaceJobs:true,
46-
})
47-
version:=dbgen.TemplateVersion(t,db, database.TemplateVersion{
48-
OrganizationID:owner.OrganizationID,
49-
CreatedBy:owner.UserID,
50-
TemplateID: uuid.NullUUID{UUID:template.ID,Valid:true},
51-
})
52-
5330
t.Run("Cancel",func(t*testing.T) {
5431
t.Parallel()
5532

33+
db,ps:=dbtestutil.NewDB(t)
34+
client,_,coderdAPI:=coderdtest.NewWithAPI(t,&coderdtest.Options{
35+
IncludeProvisionerDaemon:false,
36+
Database:db,
37+
Pubsub:ps,
38+
})
39+
owner:=coderdtest.CreateFirstUser(t,client)
40+
templateAdminClient,templateAdmin:=coderdtest.CreateAnotherUser(t,client,owner.OrganizationID,rbac.ScopedRoleOrgTemplateAdmin(owner.OrganizationID))
41+
memberClient,member:=coderdtest.CreateAnotherUser(t,client,owner.OrganizationID)
42+
43+
// These CLI tests are related to provisioner job CRUD operations and as such
44+
// do not require the overhead of starting a provisioner. Other provisioner job
45+
// functionalities (acquisition etc.) are tested elsewhere.
46+
template:=dbgen.Template(t,db, database.Template{
47+
OrganizationID:owner.OrganizationID,
48+
CreatedBy:owner.UserID,
49+
AllowUserCancelWorkspaceJobs:true,
50+
})
51+
version:=dbgen.TemplateVersion(t,db, database.TemplateVersion{
52+
OrganizationID:owner.OrganizationID,
53+
CreatedBy:owner.UserID,
54+
TemplateID: uuid.NullUUID{UUID:template.ID,Valid:true},
55+
})
5656
// Test helper to create a provisioner job of a given type with a given input.
5757
prepareJob:=func(t*testing.T,jobType database.ProvisionerJobType,input json.RawMessage) database.ProvisionerJob {
5858
t.Helper()
@@ -178,4 +178,148 @@ func TestProvisionerJobs(t *testing.T) {
178178
})
179179
}
180180
})
181+
182+
t.Run("List",func(t*testing.T) {
183+
t.Parallel()
184+
185+
db,ps:=dbtestutil.NewDB(t)
186+
client,_,coderdAPI:=coderdtest.NewWithAPI(t,&coderdtest.Options{
187+
IncludeProvisionerDaemon:false,
188+
Database:db,
189+
Pubsub:ps,
190+
})
191+
owner:=coderdtest.CreateFirstUser(t,client)
192+
_,member:=coderdtest.CreateAnotherUser(t,client,owner.OrganizationID)
193+
194+
// These CLI tests are related to provisioner job CRUD operations and as such
195+
// do not require the overhead of starting a provisioner. Other provisioner job
196+
// functionalities (acquisition etc.) are tested elsewhere.
197+
template:=dbgen.Template(t,db, database.Template{
198+
OrganizationID:owner.OrganizationID,
199+
CreatedBy:owner.UserID,
200+
AllowUserCancelWorkspaceJobs:true,
201+
})
202+
version:=dbgen.TemplateVersion(t,db, database.TemplateVersion{
203+
OrganizationID:owner.OrganizationID,
204+
CreatedBy:owner.UserID,
205+
TemplateID: uuid.NullUUID{UUID:template.ID,Valid:true},
206+
})
207+
// Create some test jobs
208+
job1:=dbgen.ProvisionerJob(t,db,coderdAPI.Pubsub, database.ProvisionerJob{
209+
OrganizationID:owner.OrganizationID,
210+
InitiatorID:owner.UserID,
211+
Type:database.ProvisionerJobTypeTemplateVersionImport,
212+
Input: []byte(`{"template_version_id":"`+version.ID.String()+`"}`),
213+
Tags: database.StringMap{provisionersdk.TagScope:provisionersdk.ScopeOrganization},
214+
})
215+
216+
job2:=dbgen.ProvisionerJob(t,db,coderdAPI.Pubsub, database.ProvisionerJob{
217+
OrganizationID:owner.OrganizationID,
218+
InitiatorID:member.ID,
219+
Type:database.ProvisionerJobTypeWorkspaceBuild,
220+
Input: []byte(`{"workspace_build_id":"`+uuid.New().String()+`"}`),
221+
Tags: database.StringMap{provisionersdk.TagScope:provisionersdk.ScopeOrganization},
222+
})
223+
// Test basic list command
224+
t.Run("Basic",func(t*testing.T) {
225+
t.Parallel()
226+
227+
inv,root:=clitest.New(t,"provisioner","jobs","list")
228+
clitest.SetupConfig(t,client,root)
229+
varbuf bytes.Buffer
230+
inv.Stdout=&buf
231+
err:=inv.Run()
232+
require.NoError(t,err)
233+
234+
// Should contain both jobs
235+
output:=buf.String()
236+
assert.Contains(t,output,job1.ID.String())
237+
assert.Contains(t,output,job2.ID.String())
238+
})
239+
240+
// Test list with JSON output
241+
t.Run("JSON",func(t*testing.T) {
242+
t.Parallel()
243+
244+
inv,root:=clitest.New(t,"provisioner","jobs","list","--output","json")
245+
clitest.SetupConfig(t,client,root)
246+
varbuf bytes.Buffer
247+
inv.Stdout=&buf
248+
err:=inv.Run()
249+
require.NoError(t,err)
250+
251+
// Parse JSON output
252+
varjobs []codersdk.ProvisionerJob
253+
err=json.Unmarshal(buf.Bytes(),&jobs)
254+
require.NoError(t,err)
255+
256+
// Should contain both jobs
257+
jobIDs:=make([]uuid.UUID,len(jobs))
258+
fori,job:=rangejobs {
259+
jobIDs[i]=job.ID
260+
}
261+
assert.Contains(t,jobIDs,job1.ID)
262+
assert.Contains(t,jobIDs,job2.ID)
263+
})
264+
265+
// Test list with limit
266+
t.Run("Limit",func(t*testing.T) {
267+
t.Parallel()
268+
269+
inv,root:=clitest.New(t,"provisioner","jobs","list","--limit","1")
270+
clitest.SetupConfig(t,client,root)
271+
varbuf bytes.Buffer
272+
inv.Stdout=&buf
273+
err:=inv.Run()
274+
require.NoError(t,err)
275+
276+
// Should contain at most 1 job
277+
output:=buf.String()
278+
jobCount:=0
279+
ifstrings.Contains(output,job1.ID.String()) {
280+
jobCount++
281+
}
282+
ifstrings.Contains(output,job2.ID.String()) {
283+
jobCount++
284+
}
285+
assert.LessOrEqual(t,jobCount,1)
286+
})
287+
288+
// Test list with initiator filter
289+
t.Run("InitiatorFilter",func(t*testing.T) {
290+
t.Parallel()
291+
292+
// Get owner user details to access username
293+
ctx:=testutil.Context(t,testutil.WaitShort)
294+
ownerUser,err:=client.User(ctx,owner.UserID.String())
295+
require.NoError(t,err)
296+
297+
// Test filtering by initiator (using username)
298+
inv,root:=clitest.New(t,"provisioner","jobs","list","--initiator",ownerUser.Username)
299+
clitest.SetupConfig(t,client,root)
300+
varbuf bytes.Buffer
301+
inv.Stdout=&buf
302+
err=inv.Run()
303+
require.NoError(t,err)
304+
305+
// Should only contain job1 (initiated by owner)
306+
output:=buf.String()
307+
assert.Contains(t,output,job1.ID.String())
308+
assert.NotContains(t,output,job2.ID.String())
309+
})
310+
311+
// Test list with invalid user
312+
t.Run("InvalidUser",func(t*testing.T) {
313+
t.Parallel()
314+
315+
// Test with non-existent user
316+
inv,root:=clitest.New(t,"provisioner","jobs","list","--initiator","nonexistent-user")
317+
clitest.SetupConfig(t,client,root)
318+
varbuf bytes.Buffer
319+
inv.Stdout=&buf
320+
err:=inv.Run()
321+
require.Error(t,err)
322+
assert.Contains(t,err.Error(),"initiator not found: nonexistent-user")
323+
})
324+
})
181325
}

‎cli/testdata/coder_provisioner_jobs_list_--help.golden‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@ OPTIONS:
1414
-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)
1515
Columns to display in table output.
1616

17+
--initiator string, $CODER_PROVISIONER_JOB_LIST_INITIATOR
18+
Filter by initiator (user ID or username).
19+
1720
-l, --limit int, $CODER_PROVISIONER_JOB_LIST_LIMIT (default: 50)
1821
Limit the number of jobs returned.
1922

‎coderd/apidoc/docs.go‎

Lines changed: 7 additions & 0 deletions
Some generated files are not rendered by default. Learn more aboutcustomizing how changed files appear on GitHub.

‎coderd/apidoc/swagger.json‎

Lines changed: 7 additions & 0 deletions
Some generated files are not rendered by default. Learn more aboutcustomizing how changed files appear on GitHub.

‎coderd/database/dbauthz/dbauthz_test.go‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2484,10 +2484,12 @@ func (s *MethodTestSuite) TestExtraMethods() {
24842484

24852485
ds,err:=db.GetProvisionerJobsByOrganizationAndStatusWithQueuePositionAndProvisioner(context.Background(), database.GetProvisionerJobsByOrganizationAndStatusWithQueuePositionAndProvisionerParams{
24862486
OrganizationID:org.ID,
2487+
InitiatorID:uuid.Nil,
24872488
})
24882489
s.NoError(err,"get provisioner jobs by org")
24892490
check.Args(database.GetProvisionerJobsByOrganizationAndStatusWithQueuePositionAndProvisionerParams{
24902491
OrganizationID:org.ID,
2492+
InitiatorID:uuid.Nil,
24912493
}).Asserts(j1,policy.ActionRead,j2,policy.ActionRead).Returns(ds)
24922494
}))
24932495
}

‎coderd/database/queries.sql.go‎

Lines changed: 4 additions & 1 deletion
Some generated files are not rendered by default. Learn more aboutcustomizing how changed files appear on GitHub.

‎coderd/database/queries/provisionerjobs.sql‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,7 @@ WHERE
224224
AND (COALESCE(array_length(@ids::uuid[],1),0)=0ORpj.id= ANY(@ids::uuid[]))
225225
AND (COALESCE(array_length(@status::provisioner_job_status[],1),0)=0ORpj.job_status= ANY(@status::provisioner_job_status[]))
226226
AND (@tags::tagset='null'::tagsetOR provisioner_tagset_contains(pj.tags::tagset, @tags::tagset))
227+
AND (@initiator_id::uuid='00000000-0000-0000-0000-000000000000'::uuidORpj.initiator_id= @initiator_id::uuid)
227228
GROUP BY
228229
pj.id,
229230
qp.queue_position,

‎coderd/provisionerjobs.go‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ func (api *API) provisionerJob(rw http.ResponseWriter, r *http.Request) {
7676
// @Param ids query []string false "Filter results by job IDs" format(uuid)
7777
// @Param status query codersdk.ProvisionerJobStatus false "Filter results by status" enums(pending,running,succeeded,canceling,canceled,failed)
7878
// @Param tags query object false "Provisioner tags to filter by (JSON of the form {'tag1':'value1','tag2':'value2'})"
79+
// @Param initiator_id query string false "Filter results by initiator ID" format(uuid)
7980
// @Success 200 {array} codersdk.ProvisionerJob
8081
// @Router /organizations/{organization}/provisionerjobs [get]
8182
func (api*API)provisionerJobs(rw http.ResponseWriter,r*http.Request) {
@@ -110,6 +111,7 @@ func (api *API) handleAuthAndFetchProvisionerJobs(rw http.ResponseWriter, r *htt
110111
ids=p.UUIDs(qp,nil,"ids")
111112
}
112113
tags:=p.JSONStringMap(qp, database.StringMap{},"tags")
114+
initiatorID:=p.UUID(qp,uuid.Nil,"initiator_id")
113115
p.ErrorExcessParams(qp)
114116
iflen(p.Errors)>0 {
115117
httpapi.Write(ctx,rw,http.StatusBadRequest, codersdk.Response{
@@ -125,6 +127,7 @@ func (api *API) handleAuthAndFetchProvisionerJobs(rw http.ResponseWriter, r *htt
125127
Limit: sql.NullInt32{Int32:limit,Valid:limit>0},
126128
IDs:ids,
127129
Tags:tags,
130+
InitiatorID:initiatorID,
128131
})
129132
iferr!=nil {
130133
ifhttpapi.Is404Error(err) {

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp