Skip to content

Commit e38bd27

Browse files
authored
feat(coderd): add support for provisioner job id and tag filter (#16556)
This change adds to new filters to the provisionerjobs endpoint, id (array) and tags (map). Updates #15084 Updates #15192 Related #16532
1 parent ade0a53 commit e38bd27

File tree

10 files changed

+110
-7
lines changed

10 files changed

+110
-7
lines changed

coderd/apidoc/docs.go

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

coderd/apidoc/swagger.json

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

coderd/database/dbmem/dbmem.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4170,6 +4170,9 @@ func (q *FakeQuerier) GetProvisionerJobsByOrganizationAndStatusWithQueuePosition
41704170
if len(arg.IDs) > 0 && !slices.Contains(arg.IDs, job.ID) {
41714171
continue
41724172
}
4173+
if len(arg.Tags) > 0 && !tagsSubset(job.Tags, arg.Tags) {
4174+
continue
4175+
}
41734176

41744177
row := database.GetProvisionerJobsByOrganizationAndStatusWithQueuePositionAndProvisionerRow{
41754178
ProvisionerJob: rowQP.ProvisionerJob,

coderd/database/queries.sql.go

Lines changed: 4 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing 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
@@ -158,6 +158,7 @@ WHERE
158158
(sqlc.narg('organization_id')::uuid IS NULL OR pj.organization_id = @organization_id)
159159
AND (COALESCE(array_length(@ids::uuid[], 1), 0) = 0 OR pj.id = ANY(@ids::uuid[]))
160160
AND (COALESCE(array_length(@status::provisioner_job_status[], 1), 0) = 0 OR pj.job_status = ANY(@status::provisioner_job_status[]))
161+
AND (@tags::tagset = 'null'::tagset OR provisioner_tagset_contains(pj.tags::tagset, @tags::tagset))
161162
GROUP BY
162163
pj.id,
163164
qp.queue_position,

coderd/provisionerjobs.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,9 @@ func (api *API) provisionerJob(rw http.ResponseWriter, r *http.Request) {
7272
// @Tags Organizations
7373
// @Param organization path string true "Organization ID" format(uuid)
7474
// @Param limit query int false "Page limit"
75+
// @Param ids query []string false "Filter results by job IDs" format(uuid)
7576
// @Param status query codersdk.ProvisionerJobStatus false "Filter results by status" enums(pending,running,succeeded,canceling,canceled,failed)
77+
// @Param tags query object false "Provisioner tags to filter by (JSON of the form {'tag1':'value1','tag2':'value2'})"
7678
// @Success 200 {array} codersdk.ProvisionerJob
7779
// @Router /organizations/{organization}/provisionerjobs [get]
7880
func (api *API) provisionerJobs(rw http.ResponseWriter, r *http.Request) {
@@ -103,6 +105,10 @@ func (api *API) handleAuthAndFetchProvisionerJobs(rw http.ResponseWriter, r *htt
103105
p := httpapi.NewQueryParamParser()
104106
limit := p.PositiveInt32(qp, 50, "limit")
105107
status := p.Strings(qp, nil, "status")
108+
if ids == nil {
109+
ids = p.UUIDs(qp, nil, "ids")
110+
}
111+
tagsRaw := p.String(qp, "", "tags")
106112
p.ErrorExcessParams(qp)
107113
if len(p.Errors) > 0 {
108114
httpapi.Write(ctx, rw, http.StatusBadRequest, codersdk.Response{
@@ -112,11 +118,23 @@ func (api *API) handleAuthAndFetchProvisionerJobs(rw http.ResponseWriter, r *htt
112118
return nil, false
113119
}
114120

121+
tags := database.StringMap{}
122+
if tagsRaw != "" {
123+
if err := tags.Scan([]byte(tagsRaw)); err != nil {
124+
httpapi.Write(ctx, rw, http.StatusBadRequest, codersdk.Response{
125+
Message: "Invalid tags query parameter",
126+
Detail: err.Error(),
127+
})
128+
return nil, false
129+
}
130+
}
131+
115132
jobs, err := api.Database.GetProvisionerJobsByOrganizationAndStatusWithQueuePositionAndProvisioner(ctx, database.GetProvisionerJobsByOrganizationAndStatusWithQueuePositionAndProvisionerParams{
116133
OrganizationID: uuid.NullUUID{UUID: org.ID, Valid: true},
117134
Status: slice.StringEnums[database.ProvisionerJobStatus](status),
118135
Limit: sql.NullInt32{Int32: limit, Valid: limit > 0},
119136
IDs: ids,
137+
Tags: tags,
120138
})
121139
if err != nil {
122140
if httpapi.Is404Error(err) {

coderd/provisionerjobs_test.go

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"context"
55
"database/sql"
66
"encoding/json"
7+
"strconv"
78
"testing"
89
"time"
910

@@ -65,9 +66,10 @@ func TestProvisionerJobs(t *testing.T) {
6566
})
6667

6768
// Add more jobs than the default limit.
68-
for range 60 {
69+
for i := range 60 {
6970
dbgen.ProvisionerJob(t, db, nil, database.ProvisionerJob{
7071
OrganizationID: owner.OrganizationID,
72+
Tags: database.StringMap{"count": strconv.Itoa(i)},
7173
})
7274
}
7375

@@ -132,6 +134,16 @@ func TestProvisionerJobs(t *testing.T) {
132134
require.Len(t, jobs, 50)
133135
})
134136

137+
t.Run("IDs", func(t *testing.T) {
138+
t.Parallel()
139+
ctx := testutil.Context(t, testutil.WaitMedium)
140+
jobs, err := templateAdminClient.OrganizationProvisionerJobs(ctx, owner.OrganizationID, &codersdk.OrganizationProvisionerJobsOptions{
141+
IDs: []uuid.UUID{workspace.LatestBuild.Job.ID, version.Job.ID},
142+
})
143+
require.NoError(t, err)
144+
require.Len(t, jobs, 2)
145+
})
146+
135147
t.Run("Status", func(t *testing.T) {
136148
t.Parallel()
137149
ctx := testutil.Context(t, testutil.WaitMedium)
@@ -142,6 +154,16 @@ func TestProvisionerJobs(t *testing.T) {
142154
require.Len(t, jobs, 1)
143155
})
144156

157+
t.Run("Tags", func(t *testing.T) {
158+
t.Parallel()
159+
ctx := testutil.Context(t, testutil.WaitMedium)
160+
jobs, err := templateAdminClient.OrganizationProvisionerJobs(ctx, owner.OrganizationID, &codersdk.OrganizationProvisionerJobsOptions{
161+
Tags: map[string]string{"count": "1"},
162+
})
163+
require.NoError(t, err)
164+
require.Len(t, jobs, 1)
165+
})
166+
145167
t.Run("Limit", func(t *testing.T) {
146168
t.Parallel()
147169
ctx := testutil.Context(t, testutil.WaitMedium)

codersdk/organizations.go

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -346,7 +346,9 @@ func (c *Client) OrganizationProvisionerDaemons(ctx context.Context, organizatio
346346

347347
type OrganizationProvisionerJobsOptions struct {
348348
Limit int
349+
IDs []uuid.UUID
349350
Status []ProvisionerJobStatus
351+
Tags map[string]string
350352
}
351353

352354
func (c *Client) OrganizationProvisionerJobs(ctx context.Context, organizationID uuid.UUID, opts *OrganizationProvisionerJobsOptions) ([]ProvisionerJob, error) {
@@ -355,9 +357,19 @@ func (c *Client) OrganizationProvisionerJobs(ctx context.Context, organizationID
355357
if opts.Limit > 0 {
356358
qp.Add("limit", strconv.Itoa(opts.Limit))
357359
}
360+
if len(opts.IDs) > 0 {
361+
qp.Add("ids", joinSliceStringer(opts.IDs))
362+
}
358363
if len(opts.Status) > 0 {
359364
qp.Add("status", joinSlice(opts.Status))
360365
}
366+
if len(opts.Tags) > 0 {
367+
tagsRaw, err := json.Marshal(opts.Tags)
368+
if err != nil {
369+
return nil, xerrors.Errorf("marshal tags: %w", err)
370+
}
371+
qp.Add("tags", string(tagsRaw))
372+
}
361373
}
362374

363375
res, err := c.Request(ctx, http.MethodGet,
@@ -401,6 +413,14 @@ func joinSlice[T ~string](s []T) string {
401413
return strings.Join(ss, ",")
402414
}
403415

416+
func joinSliceStringer[T fmt.Stringer](s []T) string {
417+
var ss []string
418+
for _, v := range s {
419+
ss = append(ss, v.String())
420+
}
421+
return strings.Join(ss, ",")
422+
}
423+
404424
// CreateTemplateVersion processes source-code and optionally associates the version with a template.
405425
// Executing without a template is useful for validating source-code.
406426
func (c *Client) CreateTemplateVersion(ctx context.Context, organizationID uuid.UUID, req CreateTemplateVersionRequest) (TemplateVersion, error) {

docs/reference/api/organizations.md

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

site/src/api/typesGenerated.ts

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

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy