Skip to content

feat: add filtering options to provisioners list #19378

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 our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 13 commits into from
Aug 21, 2025
Merged
Show file tree
Hide file tree
Changes from all 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
33 changes: 31 additions & 2 deletions cli/provisioners.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@ package cli

import (
"fmt"
"time"

"golang.org/x/xerrors"

"github.com/coder/coder/v2/cli/cliui"
"github.com/coder/coder/v2/coderd/util/slice"
"github.com/coder/coder/v2/codersdk"
"github.com/coder/serpent"
)
Expand Down Expand Up @@ -39,7 +41,10 @@ func (r *RootCmd) provisionerList() *serpent.Command {
cliui.TableFormat([]provisionerDaemonRow{}, []string{"created at", "last seen at", "key name", "name", "version", "status", "tags"}),
cliui.JSONFormat(),
)
limit int64
limit int64
offline bool
status []string
maxAge time.Duration
)

cmd := &serpent.Command{
Expand All @@ -59,7 +64,10 @@ func (r *RootCmd) provisionerList() *serpent.Command {
}

daemons, err := client.OrganizationProvisionerDaemons(ctx, org.ID, &codersdk.OrganizationProvisionerDaemonsOptions{
Limit: int(limit),
Limit: int(limit),
Offline: offline,
Status: slice.StringEnums[codersdk.ProvisionerDaemonStatus](status),
MaxAge: maxAge,
})
if err != nil {
return xerrors.Errorf("list provisioner daemons: %w", err)
Expand Down Expand Up @@ -98,6 +106,27 @@ func (r *RootCmd) provisionerList() *serpent.Command {
Default: "50",
Value: serpent.Int64Of(&limit),
},
{
Flag: "show-offline",
FlagShorthand: "f",
Env: "CODER_PROVISIONER_SHOW_OFFLINE",
Description: "Show offline provisioners.",
Value: serpent.BoolOf(&offline),
},
{
Flag: "status",
FlagShorthand: "s",
Env: "CODER_PROVISIONER_LIST_STATUS",
Description: "Filter by provisioner status.",
Value: serpent.EnumArrayOf(&status, slice.ToStrings(codersdk.ProvisionerDaemonStatusEnums())...),
},
{
Flag: "max-age",
FlagShorthand: "m",
Env: "CODER_PROVISIONER_LIST_MAX_AGE",
Description: "Filter provisioners by maximum age.",
Value: serpent.DurationOf(&maxAge),
},
}...)

orgContext.AttachOptions(cmd)
Expand Down
68 changes: 68 additions & 0 deletions cli/provisioners_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,74 @@ func TestProvisioners_Golden(t *testing.T) {
clitest.TestGoldenFile(t, t.Name(), got.Bytes(), replace)
})

t.Run("list with offline provisioner daemons", func(t *testing.T) {
t.Parallel()

var got bytes.Buffer
inv, root := clitest.New(t,
"provisioners",
"list",
"--show-offline",
)
inv.Stdout = &got
clitest.SetupConfig(t, templateAdminClient, root)
err := inv.Run()
require.NoError(t, err)

clitest.TestGoldenFile(t, t.Name(), got.Bytes(), replace)
})

t.Run("list provisioner daemons by status", func(t *testing.T) {
t.Parallel()

var got bytes.Buffer
inv, root := clitest.New(t,
"provisioners",
"list",
"--status=idle,offline,busy",
)
inv.Stdout = &got
clitest.SetupConfig(t, templateAdminClient, root)
err := inv.Run()
require.NoError(t, err)

clitest.TestGoldenFile(t, t.Name(), got.Bytes(), replace)
})

t.Run("list provisioner daemons without offline", func(t *testing.T) {
t.Parallel()

var got bytes.Buffer
inv, root := clitest.New(t,
"provisioners",
"list",
"--status=idle,busy",
)
inv.Stdout = &got
clitest.SetupConfig(t, templateAdminClient, root)
err := inv.Run()
require.NoError(t, err)

clitest.TestGoldenFile(t, t.Name(), got.Bytes(), replace)
})

t.Run("list provisioner daemons by max age", func(t *testing.T) {
t.Parallel()

var got bytes.Buffer
inv, root := clitest.New(t,
"provisioners",
"list",
"--max-age=1h",
)
inv.Stdout = &got
clitest.SetupConfig(t, templateAdminClient, root)
err := inv.Run()
require.NoError(t, err)

clitest.TestGoldenFile(t, t.Name(), got.Bytes(), replace)
})

// Test jobs list with template admin as members are currently
// unable to access provisioner jobs. In the future (with RBAC
// changes), we may allow them to view _their_ jobs.
Expand Down
9 changes: 4 additions & 5 deletions cli/testdata/TestProvisioners_Golden/list.golden
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
ID CREATED AT LAST SEEN AT NAME VERSION TAGS KEY NAME STATUS CURRENT JOB ID CURRENT JOB STATUS PREVIOUS JOB ID PREVIOUS JOB STATUS ORGANIZATION
00000000-0000-0000-aaaa-000000000000 ====[timestamp]===== ====[timestamp]===== default-provisioner v0.0.0-devel map[owner: scope:organization] built-in idle <nil> <nil> 00000000-0000-0000-bbbb-000000000001 succeeded Coder
00000000-0000-0000-aaaa-000000000001 ====[timestamp]===== ====[timestamp]===== provisioner-1 v0.0.0 map[foo:bar owner: scope:organization] built-in busy 00000000-0000-0000-bbbb-000000000002 running <nil> <nil> Coder
00000000-0000-0000-aaaa-000000000002 ====[timestamp]===== ====[timestamp]===== provisioner-2 v0.0.0 map[owner: scope:organization] built-in offline <nil> <nil> 00000000-0000-0000-bbbb-000000000003 succeeded Coder
00000000-0000-0000-aaaa-000000000003 ====[timestamp]===== ====[timestamp]===== provisioner-3 v0.0.0 map[owner: scope:organization] built-in idle <nil> <nil> <nil> <nil> Coder
ID CREATED AT LAST SEEN AT NAME VERSION TAGS KEY NAME STATUS CURRENT JOB ID CURRENT JOB STATUS PREVIOUS JOB ID PREVIOUS JOB STATUS ORGANIZATION
00000000-0000-0000-aaaa-000000000000 ====[timestamp]===== ====[timestamp]===== default-provisioner v0.0.0-devel map[owner: scope:organization] built-in idle <nil> <nil> 00000000-0000-0000-bbbb-000000000001 succeeded Coder
00000000-0000-0000-aaaa-000000000001 ====[timestamp]===== ====[timestamp]===== provisioner-1 v0.0.0 map[foo:bar owner: scope:organization] built-in busy 00000000-0000-0000-bbbb-000000000002 running <nil> <nil> Coder
00000000-0000-0000-aaaa-000000000003 ====[timestamp]===== ====[timestamp]===== provisioner-3 v0.0.0 map[owner: scope:organization] built-in idle <nil> <nil> <nil> <nil> Coder
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
CREATED AT LAST SEEN AT KEY NAME NAME VERSION STATUS TAGS
====[timestamp]===== ====[timestamp]===== built-in default-provisioner v0.0.0-devel idle map[owner: scope:organization]
====[timestamp]===== ====[timestamp]===== built-in provisioner-1 v0.0.0 busy map[foo:bar owner: scope:organization]
====[timestamp]===== ====[timestamp]===== built-in provisioner-3 v0.0.0 idle map[owner: scope:organization]
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
CREATED AT LAST SEEN AT KEY NAME NAME VERSION STATUS TAGS
====[timestamp]===== ====[timestamp]===== built-in default-provisioner v0.0.0-devel idle map[owner: scope:organization]
====[timestamp]===== ====[timestamp]===== built-in provisioner-1 v0.0.0 busy map[foo:bar owner: scope:organization]
====[timestamp]===== ====[timestamp]===== built-in provisioner-2 v0.0.0 offline map[owner: scope:organization]
====[timestamp]===== ====[timestamp]===== built-in provisioner-3 v0.0.0 idle map[owner: scope:organization]
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
CREATED AT LAST SEEN AT KEY NAME NAME VERSION STATUS TAGS
====[timestamp]===== ====[timestamp]===== built-in default-provisioner v0.0.0-devel idle map[owner: scope:organization]
====[timestamp]===== ====[timestamp]===== built-in provisioner-1 v0.0.0 busy map[foo:bar owner: scope:organization]
====[timestamp]===== ====[timestamp]===== built-in provisioner-3 v0.0.0 idle map[owner: scope:organization]
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
CREATED AT LAST SEEN AT KEY NAME NAME VERSION STATUS TAGS
====[timestamp]===== ====[timestamp]===== built-in default-provisioner v0.0.0-devel idle map[owner: scope:organization]
====[timestamp]===== ====[timestamp]===== built-in provisioner-1 v0.0.0 busy map[foo:bar owner: scope:organization]
====[timestamp]===== ====[timestamp]===== built-in provisioner-2 v0.0.0 offline map[owner: scope:organization]
====[timestamp]===== ====[timestamp]===== built-in provisioner-3 v0.0.0 idle map[owner: scope:organization]
9 changes: 9 additions & 0 deletions cli/testdata/coder_provisioner_list_--help.golden
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,17 @@ OPTIONS:
-l, --limit int, $CODER_PROVISIONER_LIST_LIMIT (default: 50)
Limit the number of provisioners returned.

-m, --max-age duration, $CODER_PROVISIONER_LIST_MAX_AGE
Filter provisioners by maximum age.

-o, --output table|json (default: table)
Output format.

-f, --show-offline bool, $CODER_PROVISIONER_SHOW_OFFLINE
Show offline provisioners.

-s, --status [offline|idle|busy], $CODER_PROVISIONER_LIST_STATUS
Filter by provisioner status.

———
Run `coder --help` for a list of global options.
Loading
Loading
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