Skip to content

Commit 65e074e

Browse files
committed
feat: add show-offline option to provisioners list command
1 parent 2180d17 commit 65e074e

File tree

14 files changed

+165
-11
lines changed

14 files changed

+165
-11
lines changed

cli/provisioners.go

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,8 @@ func (r *RootCmd) provisionerList() *serpent.Command {
3939
cliui.TableFormat([]provisionerDaemonRow{}, []string{"created at", "last seen at", "key name", "name", "version", "status", "tags"}),
4040
cliui.JSONFormat(),
4141
)
42-
limit int64
42+
limit int64
43+
offline bool
4344
)
4445

4546
cmd := &serpent.Command{
@@ -59,7 +60,8 @@ func (r *RootCmd) provisionerList() *serpent.Command {
5960
}
6061

6162
daemons, err := client.OrganizationProvisionerDaemons(ctx, org.ID, &codersdk.OrganizationProvisionerDaemonsOptions{
62-
Limit: int(limit),
63+
Limit: int(limit),
64+
Offline: offline,
6365
})
6466
if err != nil {
6567
return xerrors.Errorf("list provisioner daemons: %w", err)
@@ -98,6 +100,11 @@ func (r *RootCmd) provisionerList() *serpent.Command {
98100
Default: "50",
99101
Value: serpent.Int64Of(&limit),
100102
},
103+
{
104+
Flag: "show-offline",
105+
Description: "Show offline provisioners.",
106+
Value: serpent.BoolOf(&offline),
107+
},
101108
}...)
102109

103110
orgContext.AttachOptions(cmd)

cli/provisioners_test.go

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,23 @@ func TestProvisioners_Golden(t *testing.T) {
198198
clitest.TestGoldenFile(t, t.Name(), got.Bytes(), replace)
199199
})
200200

201+
t.Run("list with offline provisioners", func(t *testing.T) {
202+
t.Parallel()
203+
204+
var got bytes.Buffer
205+
inv, root := clitest.New(t,
206+
"provisioners",
207+
"list",
208+
"--show-offline",
209+
)
210+
inv.Stdout = &got
211+
clitest.SetupConfig(t, templateAdminClient, root)
212+
err := inv.Run()
213+
require.NoError(t, err)
214+
215+
clitest.TestGoldenFile(t, t.Name(), got.Bytes(), replace)
216+
})
217+
201218
// Test jobs list with template admin as members are currently
202219
// unable to access provisioner jobs. In the future (with RBAC
203220
// changes), we may allow them to view _their_ jobs.
Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
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
2-
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
3-
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
4-
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
5-
00000000-0000-0000-aaaa-000000000003 ====[timestamp]===== ====[timestamp]===== provisioner-3 v0.0.0 map[owner: scope:organization] built-in idle <nil> <nil> <nil> <nil> Coder
1+
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
2+
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
3+
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
4+
00000000-0000-0000-aaaa-000000000003 ====[timestamp]===== ====[timestamp]===== provisioner-3 v0.0.0 map[owner: scope:organization] built-in idle <nil> <nil> <nil> <nil> Coder
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
CREATED AT LAST SEEN AT KEY NAME NAME VERSION STATUS TAGS
2+
====[timestamp]===== ====[timestamp]===== built-in default-provisioner v0.0.0-devel idle map[owner: scope:organization]
3+
====[timestamp]===== ====[timestamp]===== built-in provisioner-1 v0.0.0 busy map[foo:bar owner: scope:organization]
4+
====[timestamp]===== ====[timestamp]===== built-in provisioner-2 v0.0.0 offline map[owner: scope:organization]
5+
====[timestamp]===== ====[timestamp]===== built-in provisioner-3 v0.0.0 idle map[owner: scope:organization]
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
CREATED AT LAST SEEN AT KEY NAME NAME VERSION STATUS TAGS
2+
====[timestamp]===== ====[timestamp]===== built-in default-provisioner v0.0.0-devel idle map[owner: scope:organization]
3+
====[timestamp]===== ====[timestamp]===== built-in provisioner-1 v0.0.0 busy map[foo:bar owner: scope:organization]
4+
====[timestamp]===== ====[timestamp]===== built-in provisioner-2 v0.0.0 offline map[owner: scope:organization]
5+
====[timestamp]===== ====[timestamp]===== built-in provisioner-3 v0.0.0 idle map[owner: scope:organization]

cli/testdata/coder_provisioner_list_--help.golden

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,5 +20,8 @@ OPTIONS:
2020
-o, --output table|json (default: table)
2121
Output format.
2222

23+
--show-offline bool
24+
Show offline provisioners.
25+
2326
———
2427
Run `coder --help` for a list of global options.

coderd/database/querier_test.go

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -397,6 +397,7 @@ func TestGetProvisionerDaemonsWithStatusByOrganization(t *testing.T) {
397397
daemons, err := db.GetProvisionerDaemonsWithStatusByOrganization(context.Background(), database.GetProvisionerDaemonsWithStatusByOrganizationParams{
398398
OrganizationID: org.ID,
399399
IDs: []uuid.UUID{matchingDaemon0.ID, matchingDaemon1.ID},
400+
Offline: sql.NullBool{Bool: true, Valid: true},
400401
})
401402
require.NoError(t, err)
402403
require.Len(t, daemons, 2)
@@ -430,6 +431,7 @@ func TestGetProvisionerDaemonsWithStatusByOrganization(t *testing.T) {
430431
daemons, err := db.GetProvisionerDaemonsWithStatusByOrganization(context.Background(), database.GetProvisionerDaemonsWithStatusByOrganizationParams{
431432
OrganizationID: org.ID,
432433
Tags: database.StringMap{"foo": "bar"},
434+
Offline: sql.NullBool{Bool: true, Valid: true},
433435
})
434436
require.NoError(t, err)
435437
require.Len(t, daemons, 1)
@@ -463,6 +465,7 @@ func TestGetProvisionerDaemonsWithStatusByOrganization(t *testing.T) {
463465
daemons, err := db.GetProvisionerDaemonsWithStatusByOrganization(context.Background(), database.GetProvisionerDaemonsWithStatusByOrganizationParams{
464466
OrganizationID: org.ID,
465467
StaleIntervalMS: 45 * time.Minute.Milliseconds(),
468+
Offline: sql.NullBool{Bool: true, Valid: true},
466469
})
467470
require.NoError(t, err)
468471
require.Len(t, daemons, 2)
@@ -475,6 +478,89 @@ func TestGetProvisionerDaemonsWithStatusByOrganization(t *testing.T) {
475478
require.Equal(t, database.ProvisionerDaemonStatusOffline, daemons[0].Status)
476479
require.Equal(t, database.ProvisionerDaemonStatusIdle, daemons[1].Status)
477480
})
481+
482+
t.Run("Excludes offline daemons", func(t *testing.T) {
483+
t.Parallel()
484+
db, _ := dbtestutil.NewDB(t)
485+
org := dbgen.Organization(t, db, database.Organization{})
486+
487+
_ = dbgen.ProvisionerDaemon(t, db, database.ProvisionerDaemon{
488+
Name: "offline-daemon",
489+
OrganizationID: org.ID,
490+
CreatedAt: dbtime.Now().Add(-time.Hour),
491+
LastSeenAt: sql.NullTime{
492+
Valid: true,
493+
Time: dbtime.Now().Add(-time.Hour),
494+
},
495+
})
496+
fooDaemon := dbgen.ProvisionerDaemon(t, db, database.ProvisionerDaemon{
497+
Name: "foo-daemon",
498+
OrganizationID: org.ID,
499+
CreatedAt: dbtime.Now().Add(-(30 * time.Minute)),
500+
LastSeenAt: sql.NullTime{
501+
Valid: true,
502+
Time: dbtime.Now().Add(-(30 * time.Minute)),
503+
},
504+
})
505+
506+
daemons, err := db.GetProvisionerDaemonsWithStatusByOrganization(context.Background(), database.GetProvisionerDaemonsWithStatusByOrganizationParams{
507+
OrganizationID: org.ID,
508+
StaleIntervalMS: 45 * time.Minute.Milliseconds(),
509+
})
510+
require.NoError(t, err)
511+
require.Len(t, daemons, 1)
512+
513+
require.Equal(t, fooDaemon.ID, daemons[0].ProvisionerDaemon.ID)
514+
require.Equal(t, database.ProvisionerDaemonStatusIdle, daemons[0].Status)
515+
})
516+
517+
t.Run("Includes offline daemons", func(t *testing.T) {
518+
t.Parallel()
519+
db, _ := dbtestutil.NewDB(t)
520+
org := dbgen.Organization(t, db, database.Organization{})
521+
522+
_ = dbgen.ProvisionerDaemon(t, db, database.ProvisionerDaemon{
523+
Name: "offline-daemon",
524+
OrganizationID: org.ID,
525+
CreatedAt: dbtime.Now().Add(-time.Hour),
526+
LastSeenAt: sql.NullTime{
527+
Valid: true,
528+
Time: dbtime.Now().Add(-time.Hour),
529+
},
530+
})
531+
_ = dbgen.ProvisionerDaemon(t, db, database.ProvisionerDaemon{
532+
Name: "foo-daemon",
533+
OrganizationID: org.ID,
534+
Tags: database.StringMap{
535+
"foo": "bar",
536+
},
537+
})
538+
_ = dbgen.ProvisionerDaemon(t, db, database.ProvisionerDaemon{
539+
Name: "bar-daemon",
540+
OrganizationID: org.ID,
541+
CreatedAt: dbtime.Now().Add(-(30 * time.Minute)),
542+
LastSeenAt: sql.NullTime{
543+
Valid: true,
544+
Time: dbtime.Now().Add(-(30 * time.Minute)),
545+
},
546+
})
547+
548+
daemons, err := db.GetProvisionerDaemonsWithStatusByOrganization(context.Background(), database.GetProvisionerDaemonsWithStatusByOrganizationParams{
549+
OrganizationID: org.ID,
550+
StaleIntervalMS: 45 * time.Minute.Milliseconds(),
551+
Offline: sql.NullBool{Bool: true, Valid: true},
552+
})
553+
require.NoError(t, err)
554+
require.Len(t, daemons, 3)
555+
556+
statusCounts := make(map[database.ProvisionerDaemonStatus]int)
557+
for _, daemon := range daemons {
558+
statusCounts[daemon.Status]++
559+
}
560+
561+
require.Equal(t, 2, statusCounts[database.ProvisionerDaemonStatusIdle])
562+
require.Equal(t, 1, statusCounts[database.ProvisionerDaemonStatusOffline])
563+
})
478564
}
479565

480566
func TestGetWorkspaceAgentUsageStats(t *testing.T) {

coderd/database/queries.sql.go

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

coderd/database/queries/provisionerdaemons.sql

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,12 @@ WHERE
110110
pd.organization_id = @organization_id::uuid
111111
AND (COALESCE(array_length(@ids::uuid[], 1), 0) = 0 OR pd.id = ANY(@ids::uuid[]))
112112
AND (@tags::tagset = 'null'::tagset OR provisioner_tagset_contains(pd.tags::tagset, @tags::tagset))
113+
-- Include offline daemons only if offline is set to true
114+
AND (
115+
COALESCE(sqlc.narg('offline')::bool, false) = true
116+
OR
117+
(pd.last_seen_at IS NOT NULL AND pd.last_seen_at >= (NOW() - (@stale_interval_ms::bigint || ' ms')::interval))
118+
)
113119
ORDER BY
114120
pd.created_at DESC
115121
LIMIT

coderd/provisionerdaemons.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ func (api *API) provisionerDaemons(rw http.ResponseWriter, r *http.Request) {
4545
limit := p.PositiveInt32(qp, 50, "limit")
4646
ids := p.UUIDs(qp, nil, "ids")
4747
tags := p.JSONStringMap(qp, database.StringMap{}, "tags")
48+
includeOffline := p.NullableBoolean(qp, sql.NullBool{}, "offline")
4849
p.ErrorExcessParams(qp)
4950
if len(p.Errors) > 0 {
5051
httpapi.Write(ctx, rw, http.StatusBadRequest, codersdk.Response{
@@ -60,6 +61,7 @@ func (api *API) provisionerDaemons(rw http.ResponseWriter, r *http.Request) {
6061
OrganizationID: org.ID,
6162
StaleIntervalMS: provisionerdserver.StaleInterval.Milliseconds(),
6263
Limit: sql.NullInt32{Int32: limit, Valid: limit > 0},
64+
Offline: includeOffline,
6365
IDs: ids,
6466
Tags: tags,
6567
},

0 commit comments

Comments
 (0)