@andrian.yablonskyy/thub-coordinator 1.0.17 → 1.0.19
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/package.json +1 -1
- package/public/js/remove-resource.js +45 -0
- package/src/api/admin.js +15 -0
- package/src/api/agent.js +9 -2
- package/src/api/resource.js +6 -0
- package/src/db/migrations/013_add_job_resource_name.sql +4 -0
- package/src/db/migrations/014_add_remove_requested_at.sql +5 -0
- package/src/db/migrations/015_add_job_duration.sql +9 -0
- package/src/db/migrations/016_add_list_prefs.sql +5 -0
- package/src/services/admin-users.js +27 -2
- package/src/services/heartbeat.js +23 -2
- package/src/services/jobs.js +108 -8
- package/src/services/list-prefs.js +92 -0
- package/src/services/registry.js +40 -2
- package/src/web/routes.js +101 -14
- package/test/list-prefs.test.js +89 -0
- package/test/scheduler.test.js +106 -0
- package/views/index.pug +5 -1
- package/views/jobs/list.pug +77 -13
- package/views/jobs/show.pug +5 -2
- package/views/mixins/confirm-modal.pug +26 -0
- package/views/mixins/list-controls.pug +61 -0
- package/views/mixins/resource.pug +77 -1
- package/views/mixins/status-badge.pug +8 -1
- package/views/resources/list.pug +18 -9
|
@@ -32,6 +32,82 @@ mixin resourceActions(r, returnTo)
|
|
|
32
32
|
)
|
|
33
33
|
i.bi.bi-arrow-repeat
|
|
34
34
|
+updateButton(`/resources/${r.id}/update`, r.update_to, returnTo)
|
|
35
|
+
+removeResourceButton(r, returnTo)
|
|
36
|
+
|
|
37
|
+
//- Remove: a plain confirm when the resource is idle; with a running job,
|
|
38
|
+
//- the shared warning modal (+removeResourceModal) instead, which stops the
|
|
39
|
+
//- job first (stopJob=1). Disabled once a removal is already pending.
|
|
40
|
+
mixin removeResourceButton(r, returnTo)
|
|
41
|
+
if r.remove_requested_at
|
|
42
|
+
span.d-inline-block.ms-1(tabindex="0" data-bs-toggle="tooltip" data-bs-title="Removal pending — waiting for the Client to confirm its job stopped")
|
|
43
|
+
button.btn.btn-sm.btn-outline-danger(type="button" disabled style="pointer-events: none;" aria-label="Remove")
|
|
44
|
+
i.bi.bi-hourglass-split
|
|
45
|
+
else if r.activeJob
|
|
46
|
+
button.btn.btn-sm.btn-outline-danger.ms-1(
|
|
47
|
+
type="button"
|
|
48
|
+
aria-label="Remove"
|
|
49
|
+
data-bs-toggle="tooltip"
|
|
50
|
+
data-bs-title="Remove this resource from the list — it's running a job"
|
|
51
|
+
data-remove-resource=r.id
|
|
52
|
+
data-remove-name=r.name
|
|
53
|
+
data-remove-job=r.activeJob.id
|
|
54
|
+
data-remove-return=returnTo
|
|
55
|
+
)
|
|
56
|
+
i.bi.bi-trash
|
|
57
|
+
else
|
|
58
|
+
form.d-inline.ms-1(
|
|
59
|
+
method="post"
|
|
60
|
+
action=`/resources/${r.id}/remove`
|
|
61
|
+
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.`
|
|
62
|
+
onsubmit="return confirm(this.dataset.confirm)"
|
|
63
|
+
)
|
|
64
|
+
input(type="hidden" name="returnTo" value=returnTo)
|
|
65
|
+
button.btn.btn-sm.btn-outline-danger(
|
|
66
|
+
type="submit"
|
|
67
|
+
aria-label="Remove"
|
|
68
|
+
data-bs-toggle="tooltip"
|
|
69
|
+
data-bs-title="Remove this resource from the list"
|
|
70
|
+
)
|
|
71
|
+
i.bi.bi-trash
|
|
72
|
+
|
|
73
|
+
//- One per page (admins): filled in by public/js/remove-resource.js from
|
|
74
|
+
//- the clicked Remove button's data-remove-* attributes.
|
|
75
|
+
mixin removeResourceModal()
|
|
76
|
+
.modal.fade#remove-resource-modal(tabindex="-1" aria-labelledby="remove-resource-title" aria-hidden="true")
|
|
77
|
+
.modal-dialog.modal-dialog-centered
|
|
78
|
+
.modal-content
|
|
79
|
+
.modal-header.text-bg-warning
|
|
80
|
+
h5.modal-title#remove-resource-title
|
|
81
|
+
i.bi.bi-exclamation-triangle-fill.me-2
|
|
82
|
+
| Client is running a job
|
|
83
|
+
button.btn-close(type="button" data-bs-dismiss="modal" aria-label="Close")
|
|
84
|
+
.modal-body
|
|
85
|
+
p
|
|
86
|
+
strong(data-field="name")
|
|
87
|
+
| is running job
|
|
88
|
+
|
|
|
89
|
+
a(data-field="job")
|
|
90
|
+
| .
|
|
91
|
+
p.mb-2 Removing it will:
|
|
92
|
+
ul.mb-2
|
|
93
|
+
li
|
|
94
|
+
| stop job
|
|
95
|
+
|
|
|
96
|
+
strong(data-field="job-text")
|
|
97
|
+
| immediately (it ends as
|
|
98
|
+
|
|
|
99
|
+
code CANCELED
|
|
100
|
+
| ), then
|
|
101
|
+
li remove the resource as soon as its Client confirms the job stopped (at most about a minute if the Client doesn't answer).
|
|
102
|
+
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.
|
|
103
|
+
.modal-footer
|
|
104
|
+
button.btn.btn-secondary(type="button" data-bs-dismiss="modal") Keep it
|
|
105
|
+
form(method="post" data-field="form")
|
|
106
|
+
input(type="hidden" name="stopJob" value="1")
|
|
107
|
+
input(type="hidden" name="returnTo" data-field="return")
|
|
108
|
+
button.btn.btn-danger(type="submit")
|
|
109
|
+
i.bi.bi-stop-circle.me-1
|
|
110
|
+
| Stop job and remove
|
|
35
111
|
|
|
36
112
|
//- One udev device line: path (or udev index / ST-Link serial), extra
|
|
37
113
|
//- detail, and whether the node was present when the Client registered.
|
|
@@ -156,7 +232,7 @@ mixin resourceCard(r, groupsById, returnTo)
|
|
|
156
232
|
dt.col-sm-4 Type
|
|
157
233
|
dd.col-sm-8.text-uppercase= r.type
|
|
158
234
|
dt.col-sm-4 Status
|
|
159
|
-
dd.col-sm-8: +resourceBadge(r.status)
|
|
235
|
+
dd.col-sm-8: +resourceBadge(r.status, r.remove_requested_at)
|
|
160
236
|
+uptimeRows(r)
|
|
161
237
|
dt.col-sm-4 Client version
|
|
162
238
|
dd.col-sm-8: +versionCell(r.client_version, updates.latest.client, r.update_to)
|
|
@@ -1,6 +1,13 @@
|
|
|
1
|
-
|
|
1
|
+
//- `removing`: the resource's remove_requested_at — removal is pending
|
|
2
|
+
//- until its Client confirms its stopped job ended (jobs.removeResource).
|
|
3
|
+
mixin resourceBadge(status, removing)
|
|
2
4
|
- const map = { IDLE: 'success', BUSY: 'primary', OUT_OF_SERVICE: 'danger', MAINTENANCE: 'secondary', REGISTERED: 'secondary' }
|
|
3
5
|
span.badge(class=`text-bg-${map[status] || 'secondary'}`)= status
|
|
6
|
+
if removing
|
|
7
|
+
span.badge.text-bg-warning.ms-1(
|
|
8
|
+
data-bs-toggle="tooltip"
|
|
9
|
+
data-bs-title="Its job was stopped; removed as soon as the Client confirms"
|
|
10
|
+
) REMOVING
|
|
4
11
|
|
|
5
12
|
mixin jobBadge(state)
|
|
6
13
|
- const map = { QUEUED: 'secondary', ASSIGNED: 'info', PREPARING: 'info', RUNNING: 'primary', PASSED: 'success', FAILED: 'danger', ERROR: 'danger', TIMEOUT: 'warning', CANCELED: 'secondary', LOST: 'danger' }
|
package/views/resources/list.pug
CHANGED
|
@@ -2,26 +2,31 @@ extends ../layout
|
|
|
2
2
|
include ../mixins/status-badge
|
|
3
3
|
include ../mixins/resource
|
|
4
4
|
include ../mixins/updates
|
|
5
|
+
include ../mixins/list-controls
|
|
5
6
|
|
|
6
7
|
block content
|
|
7
8
|
h1.h4.mb-3 Resources
|
|
8
9
|
p.text-body-secondary.small.
|
|
9
10
|
Resources register themselves — start a Client with the shared join key and it appears here. Click a resource for its details, including IP addresses.
|
|
10
11
|
|
|
11
|
-
.mb-3
|
|
12
|
+
.d-flex.flex-wrap.align-items-center.gap-2.mb-3
|
|
12
13
|
+updatesToolbar('client', 'clients', '/resources')
|
|
14
|
+
.ms-auto
|
|
15
|
+
+pageSizePicker(list)
|
|
16
|
+
|
|
17
|
+
+listToolbar(list, 'resources')
|
|
13
18
|
|
|
14
19
|
table.table.table-hover.align-middle
|
|
15
20
|
thead
|
|
16
21
|
tr
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
22
|
+
+sortHeader(list, 'name', 'Name')
|
|
23
|
+
+sortHeader(list, 'type', 'Type')
|
|
24
|
+
+sortHeader(list, 'version', 'Version', 'desc')
|
|
25
|
+
+sortHeader(list, 'status', 'Status')
|
|
21
26
|
th Labels
|
|
22
27
|
th Groups
|
|
23
28
|
th Busy source / reason
|
|
24
|
-
|
|
29
|
+
+sortHeader(list, 'heartbeat', 'Last heartbeat', 'desc')
|
|
25
30
|
if user.role === 'admin'
|
|
26
31
|
th Actions
|
|
27
32
|
tbody
|
|
@@ -30,7 +35,7 @@ block content
|
|
|
30
35
|
td= r.name
|
|
31
36
|
td.text-uppercase= r.type
|
|
32
37
|
td: +versionCell(r.client_version, updates.latest.client, r.update_to)
|
|
33
|
-
td: +resourceBadge(r.status)
|
|
38
|
+
td: +resourceBadge(r.status, r.remove_requested_at)
|
|
34
39
|
td
|
|
35
40
|
each l in r.labels
|
|
36
41
|
span.badge.text-bg-light.border.me-1= l
|
|
@@ -44,10 +49,14 @@ block content
|
|
|
44
49
|
td= fmtDate(r.last_heartbeat_at, 'never')
|
|
45
50
|
if user.role === 'admin'
|
|
46
51
|
td
|
|
47
|
-
+resourceActions(r,
|
|
52
|
+
+resourceActions(r, list.pageUrl())
|
|
48
53
|
|
|
49
54
|
each r in resources
|
|
50
|
-
+resourceCard(r, groupsById,
|
|
55
|
+
+resourceCard(r, groupsById, list.pageUrl())
|
|
56
|
+
|
|
57
|
+
if user && user.role === 'admin'
|
|
58
|
+
+removeResourceModal()
|
|
51
59
|
|
|
52
60
|
block scripts
|
|
53
61
|
script(src="/js/resource-card.js")
|
|
62
|
+
script(src="/js/remove-resource.js")
|