@andrian.yablonskyy/thub-coordinator 1.1.8 → 1.1.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -3
- package/package.json +1 -1
- package/public/js/remove-resource.js +2 -2
- package/src/services/jobs.js +7 -0
- package/src/services/list-prefs.js +1 -1
- package/src/services/registry.js +42 -3
- package/src/web/routes.js +63 -40
- package/test/groups-tab.test.js +74 -9
- package/test/live.test.js +2 -2
- package/test/scheduler.test.js +12 -0
- package/test/users.test.js +2 -2
- package/views/admin/agents.pug +2 -4
- package/views/groups/list.pug +3 -3
- package/views/help/_agent-cli.pug +4 -5
- package/views/help/_agent-setup.pug +1 -1
- package/views/help/_ci.pug +1 -1
- package/views/help/_client-machines.pug +5 -5
- package/views/help/_client-setup.pug +4 -4
- package/views/help/_coordinator.pug +3 -3
- package/views/help/_docker.pug +1 -1
- package/views/help/_env.pug +2 -3
- package/views/help/_overview.pug +3 -3
- package/views/help/_quick-start.pug +1 -1
- package/views/help/_troubleshooting.pug +6 -6
- package/views/index.pug +1 -1
- package/views/jobs/list.pug +3 -3
- package/views/jobs/show.pug +1 -1
- package/views/layout.pug +2 -2
- package/views/mixins/client-config.pug +50 -2
- package/views/mixins/group-select.pug +2 -2
- package/views/mixins/resource.pug +21 -19
- package/views/mixins/updates.pug +1 -1
- package/views/resources/list.pug +11 -8
|
@@ -6,7 +6,7 @@ include client-config
|
|
|
6
6
|
|
|
7
7
|
//- Update isn't here: it sits next to the version (+versionWithUpdate).
|
|
8
8
|
mixin resourceActions(r, returnTo)
|
|
9
|
-
form.d-inline(method="post" action=`/
|
|
9
|
+
form.d-inline(method="post" action=`/runners/${r.id}/maintenance`)
|
|
10
10
|
input(type="hidden" name="enabled" value=r.status === 'MAINTENANCE' ? '0' : '1')
|
|
11
11
|
input(type="hidden" name="returnTo" value=returnTo)
|
|
12
12
|
if r.status === 'MAINTENANCE'
|
|
@@ -14,7 +14,7 @@ mixin resourceActions(r, returnTo)
|
|
|
14
14
|
type="submit"
|
|
15
15
|
aria-label="Enable"
|
|
16
16
|
data-bs-toggle="tooltip"
|
|
17
|
-
data-bs-title="Bring this
|
|
17
|
+
data-bs-title="Bring this runner back into rotation so the scheduler can assign it jobs again"
|
|
18
18
|
)
|
|
19
19
|
i.bi.bi-play-circle
|
|
20
20
|
else
|
|
@@ -22,14 +22,14 @@ mixin resourceActions(r, returnTo)
|
|
|
22
22
|
type="submit"
|
|
23
23
|
aria-label="Disable"
|
|
24
24
|
data-bs-toggle="tooltip"
|
|
25
|
-
data-bs-title="Take this
|
|
25
|
+
data-bs-title="Take this runner out of rotation for maintenance — the scheduler will skip it"
|
|
26
26
|
)
|
|
27
27
|
i.bi.bi-tools
|
|
28
28
|
//- Credentials: admins only (§10.3).
|
|
29
29
|
if can.admin
|
|
30
30
|
form.d-inline.ms-1(
|
|
31
31
|
method="post"
|
|
32
|
-
action=`/
|
|
32
|
+
action=`/runners/${r.id}/rotate-token`
|
|
33
33
|
data-confirm=`Issue a new token for ${r.name}? Its current token stops working at once, and the Client won't reconnect until it has the new one.`
|
|
34
34
|
data-confirm-title="Rotate resource token?"
|
|
35
35
|
data-confirm-ok="Rotate token"
|
|
@@ -58,7 +58,7 @@ mixin removeResourceButton(r, returnTo)
|
|
|
58
58
|
type="button"
|
|
59
59
|
aria-label="Remove"
|
|
60
60
|
data-bs-toggle="tooltip"
|
|
61
|
-
data-bs-title="Remove this
|
|
61
|
+
data-bs-title="Remove this runner from the list — it's running a job"
|
|
62
62
|
data-remove-resource=r.id
|
|
63
63
|
data-remove-name=r.name
|
|
64
64
|
data-remove-job=r.activeJob.id
|
|
@@ -68,7 +68,7 @@ mixin removeResourceButton(r, returnTo)
|
|
|
68
68
|
else
|
|
69
69
|
form.d-inline.ms-1(
|
|
70
70
|
method="post"
|
|
71
|
-
action=`/
|
|
71
|
+
action=`/runners/${r.id}/remove`
|
|
72
72
|
data-confirm=`Remove ${r.name} from the list? Its job history is kept. A Client that's still running shows up again when it restarts and re-registers.`
|
|
73
73
|
data-confirm-title=`Remove ${r.name}?`
|
|
74
74
|
data-confirm-ok="Remove"
|
|
@@ -78,7 +78,7 @@ mixin removeResourceButton(r, returnTo)
|
|
|
78
78
|
type="submit"
|
|
79
79
|
aria-label="Remove"
|
|
80
80
|
data-bs-toggle="tooltip"
|
|
81
|
-
data-bs-title="Remove this
|
|
81
|
+
data-bs-title="Remove this runner from the list"
|
|
82
82
|
)
|
|
83
83
|
i.bi.bi-trash
|
|
84
84
|
|
|
@@ -110,7 +110,7 @@ mixin removeResourceModal()
|
|
|
110
110
|
|
|
|
111
111
|
code CANCELED
|
|
112
112
|
| ), then
|
|
113
|
-
li remove the
|
|
113
|
+
li remove the runner as soon as its Client confirms the job stopped (at most about a minute if the Client doesn't answer).
|
|
114
114
|
p.small.text-body-secondary.mb-0 Its job history is kept. A Client that's still running shows up again when it restarts and re-registers.
|
|
115
115
|
.modal-footer
|
|
116
116
|
button.btn.btn-secondary(type="button" data-bs-dismiss="modal") Keep it
|
|
@@ -231,7 +231,7 @@ mixin rebootScheduleRow(r, returnTo)
|
|
|
231
231
|
if can.operate
|
|
232
232
|
form.d-flex.gap-1.mt-1(
|
|
233
233
|
method="post"
|
|
234
|
-
action=`/
|
|
234
|
+
action=`/runners/${r.id}/reboot-schedule`
|
|
235
235
|
data-confirm=`Reboot the host of ${r.name} on this schedule? It reboots the whole host — every Client instance on it. Running jobs finish first: new ones wait, for up to 2 h, and otherwise that time is skipped.`
|
|
236
236
|
data-confirm-title="Schedule host reboots?"
|
|
237
237
|
data-confirm-ok="Save schedule"
|
|
@@ -248,7 +248,7 @@ mixin rebootScheduleRow(r, returnTo)
|
|
|
248
248
|
)
|
|
249
249
|
button.btn.btn-sm.btn-outline-primary(type="submit" data-bs-toggle="tooltip" data-bs-title="Save this schedule — the Client applies it on its next heartbeat") Save
|
|
250
250
|
if r.reboot_schedule
|
|
251
|
-
form.d-inline(method="post" action=`/
|
|
251
|
+
form.d-inline(method="post" action=`/runners/${r.id}/reboot-schedule`)
|
|
252
252
|
input(type="hidden" name="returnTo" value=returnTo)
|
|
253
253
|
input(type="hidden" name="clear" value="1")
|
|
254
254
|
button.btn.btn-sm.btn-link.p-0.small(type="submit" data-bs-toggle="tooltip" data-bs-title="Remove the schedule — the host isn't rebooted on a schedule any more") Clear schedule
|
|
@@ -270,7 +270,7 @@ mixin cancelActivityButton(r, returnTo)
|
|
|
270
270
|
if what
|
|
271
271
|
form.d-inline.ms-2(
|
|
272
272
|
method="post"
|
|
273
|
-
action=`/
|
|
273
|
+
action=`/runners/${r.id}/cancel-activity`
|
|
274
274
|
data-confirm=`Cancel ${what} on ${r.name}?`
|
|
275
275
|
data-confirm-title=`Cancel ${what}?`
|
|
276
276
|
data-confirm-ok=`Cancel ${what}`
|
|
@@ -338,7 +338,7 @@ mixin usbScanPane(r)
|
|
|
338
338
|
//- Import to config (public/js/usb-import.js): HW Clients that report
|
|
339
339
|
//- their config only; enabled once there's a scan to import from.
|
|
340
340
|
- const importWhy = r.type !== 'hw' ? 'Only HW Clients have USB device lists to import into' : !(r.config_desired || r.client_config) ? noConfigReport[0].toUpperCase() + noConfigReport.slice(1) : null
|
|
341
|
-
div(data-usb-scan=`/
|
|
341
|
+
div(data-usb-scan=`/runners/${r.id}/usb-scan` data-pending=r.usb_scan_request_id ? '1' : null data-requested-at=r.usb_scan_requested_at)
|
|
342
342
|
.d-flex.align-items-center.gap-2.mb-2
|
|
343
343
|
if can.operate
|
|
344
344
|
//- Live (public/js/live.js): a Client that comes back online gets its
|
|
@@ -381,7 +381,7 @@ mixin rebootButton(r, returnTo)
|
|
|
381
381
|
else
|
|
382
382
|
form.d-inline.ms-1(
|
|
383
383
|
method="post"
|
|
384
|
-
action=`/
|
|
384
|
+
action=`/runners/${r.id}/reboot`
|
|
385
385
|
data-confirm=r.activeJob
|
|
386
386
|
? `${r.name} is running job ${r.activeJob.id}. Rebooting cancels that job (reason: user reboot request), then reboots the host on the Client's next heartbeat. Every Client instance on that host goes down with it.`
|
|
387
387
|
: `Reboot the host of ${r.name}? It reboots on the Client's next heartbeat, once nothing on the host is busy. Every Client instance on that host goes down with it.`
|
|
@@ -405,7 +405,7 @@ mixin rebootButton(r, returnTo)
|
|
|
405
405
|
//- a whole file, or an older HW Client's devices only.
|
|
406
406
|
mixin configTransferButtons(r, returnTo)
|
|
407
407
|
a.btn.btn-sm.btn-outline-secondary.ms-1(
|
|
408
|
-
href=`/
|
|
408
|
+
href=`/runners/${r.id}/config/export`
|
|
409
409
|
download
|
|
410
410
|
aria-label=`Export ${r.name}'s config`
|
|
411
411
|
data-bs-toggle="tooltip"
|
|
@@ -419,7 +419,7 @@ mixin configTransferButtons(r, returnTo)
|
|
|
419
419
|
else
|
|
420
420
|
form.d-inline.ms-1(
|
|
421
421
|
method="post"
|
|
422
|
-
action=`/
|
|
422
|
+
action=`/runners/${r.id}/config/import`
|
|
423
423
|
data-config-import
|
|
424
424
|
data-resource-name=r.name
|
|
425
425
|
data-confirm-title=`Import config into ${r.name}?`
|
|
@@ -461,7 +461,7 @@ mixin resourceDetails(r, groupsById, returnTo, local, external)
|
|
|
461
461
|
)
|
|
462
462
|
i.bi.bi-pencil(data-bs-toggle="tooltip" data-bs-title="Rename")
|
|
463
463
|
.collapse(id=`rename-${r.id}`)
|
|
464
|
-
form.d-flex.gap-1.mt-1(method="post" action=`/
|
|
464
|
+
form.d-flex.gap-1.mt-1(method="post" action=`/runners/${r.id}/rename`)
|
|
465
465
|
input(type="hidden" name="returnTo" value=returnTo)
|
|
466
466
|
input.form-control.form-control-sm(
|
|
467
467
|
name="name"
|
|
@@ -479,7 +479,7 @@ mixin resourceDetails(r, groupsById, returnTo, local, external)
|
|
|
479
479
|
dd.col-sm-8: +resourceBadge(r.status, r.remove_requested_at)
|
|
480
480
|
+uptimeRows(r, returnTo)
|
|
481
481
|
dt.col-sm-4 Client version
|
|
482
|
-
dd.col-sm-8: +versionWithUpdate(r.client_version, updates.latest.client, r.update_to, `/
|
|
482
|
+
dd.col-sm-8: +versionWithUpdate(r.client_version, updates.latest.client, r.update_to, `/runners/${r.id}/update`, returnTo, true, r.activity && r.activity.state === 'update-hold')
|
|
483
483
|
dt.col-sm-4 IP addresses
|
|
484
484
|
dd.col-sm-8
|
|
485
485
|
if !local.length && !external
|
|
@@ -502,8 +502,10 @@ mixin resourceDetails(r, groupsById, returnTo, local, external)
|
|
|
502
502
|
dt.col-sm-4 Labels
|
|
503
503
|
dd.col-sm-8
|
|
504
504
|
if r.labels.length
|
|
505
|
-
|
|
506
|
-
|
|
505
|
+
//- One label per line, as on the Labels tab.
|
|
506
|
+
.d-flex.flex-column.align-items-start.gap-1
|
|
507
|
+
each l in r.labels
|
|
508
|
+
span.badge.text-bg-light.border.font-monospace= l
|
|
507
509
|
else
|
|
508
510
|
span.text-body-secondary —
|
|
509
511
|
dt.col-sm-4 Busy source / reason
|
package/views/mixins/updates.pug
CHANGED
|
@@ -48,7 +48,7 @@ mixin updatesToolbar(app, label, returnTo)
|
|
|
48
48
|
| Check for updates
|
|
49
49
|
form.d-inline(
|
|
50
50
|
method="post"
|
|
51
|
-
action=app === 'agent' ? '/admin/agents/update-all' : '/
|
|
51
|
+
action=app === 'agent' ? '/admin/agents/update-all' : '/runners/update-all'
|
|
52
52
|
data-confirm=`Request a self-update of every ${label} to the latest version${updates.latest[app] ? ` (v${updates.latest[app]})` : ''}?`
|
|
53
53
|
data-confirm-title=`Update all ${label}?`
|
|
54
54
|
data-confirm-ok="Request update"
|
package/views/resources/list.pug
CHANGED
|
@@ -5,19 +5,19 @@ include ../mixins/updates
|
|
|
5
5
|
include ../mixins/list-controls
|
|
6
6
|
|
|
7
7
|
block content
|
|
8
|
-
h1.h4.mb-3
|
|
8
|
+
h1.h4.mb-3 Runners
|
|
9
9
|
.d-flex.flex-wrap.align-items-center.gap-2.mb-3
|
|
10
|
-
+updatesToolbar('client', 'clients', '/
|
|
10
|
+
+updatesToolbar('client', 'clients', '/runners')
|
|
11
11
|
p.text-body-secondary.small: i.
|
|
12
|
-
|
|
12
|
+
Runners register themselves — start a Client with the shared join key and it appears here. Click a runner for its details, including IP addresses.
|
|
13
13
|
|
|
14
14
|
.d-flex.flex-wrap.align-items-center.gap-2.mb-3
|
|
15
|
-
+searchForm(list.q, 'Search
|
|
15
|
+
+searchForm(list.q, 'Search runners: name, label, group, status, IP…', list.urlFor({ q: '' }))
|
|
16
16
|
.ms-auto
|
|
17
17
|
+pageSizePicker(list)
|
|
18
18
|
|
|
19
19
|
div(data-live="list-toolbar")
|
|
20
|
-
+listToolbar(list, '
|
|
20
|
+
+listToolbar(list, 'runners')
|
|
21
21
|
|
|
22
22
|
table.table.table-hover.align-middle
|
|
23
23
|
thead
|
|
@@ -37,11 +37,14 @@ block content
|
|
|
37
37
|
tr(role="button" tabindex="0" data-resource-card=`resource-card-${r.id}`)
|
|
38
38
|
td= r.name
|
|
39
39
|
td.text-uppercase= r.type
|
|
40
|
-
td: +versionWithUpdate(r.client_version, updates.latest.client, r.update_to, `/
|
|
40
|
+
td: +versionWithUpdate(r.client_version, updates.latest.client, r.update_to, `/runners/${r.id}/update`, list.pageUrl(), true, r.activity && r.activity.state === 'update-hold')
|
|
41
41
|
td: +resourceBadge(r.status, r.remove_requested_at)
|
|
42
|
+
//- One label per line.
|
|
42
43
|
td
|
|
43
|
-
|
|
44
|
-
|
|
44
|
+
if r.labels.length
|
|
45
|
+
.d-flex.flex-column.align-items-start.gap-1
|
|
46
|
+
each l in r.labels
|
|
47
|
+
span.badge.text-bg-light.border.font-monospace= l
|
|
45
48
|
td
|
|
46
49
|
if r.group_ids.length
|
|
47
50
|
each gid in r.group_ids
|