tenseleyflow/shithub / feb5cd7

Browse files

actions/runners: add pool ops schema and queries (S41j-4)

Authored by mfwolffe <wolffemf@dukes.jmu.edu>
SHA
feb5cd7180fa3fc709d1d0bed0a0e82773abcb79
Parents
fbd1091
Tree
1442e55

22 changed files

StatusFile+-
M internal/actions/queries/workflow_jobs.sql 2 0
M internal/actions/queries/workflow_runners.sql 76 9
M internal/actions/sqlc/models.go 6 0
M internal/actions/sqlc/querier.go 9 5
M internal/actions/sqlc/workflow_jobs.sql.go 2 0
M internal/actions/sqlc/workflow_runners.sql.go 427 19
M internal/admin/sqlc/models.go 6 0
M internal/auth/policy/sqlc/models.go 6 0
M internal/billing/sqlc/models.go 6 0
M internal/checks/sqlc/models.go 6 0
M internal/issues/sqlc/models.go 6 0
M internal/meta/sqlc/models.go 6 0
A internal/migrationsfs/migrations/0067_runner_pool_ops.sql 50 0
M internal/notif/sqlc/models.go 6 0
M internal/orgs/sqlc/models.go 6 0
M internal/pulls/sqlc/models.go 6 0
M internal/ratelimit/sqlc/models.go 6 0
M internal/repos/sqlc/models.go 6 0
M internal/social/sqlc/models.go 6 0
M internal/users/sqlc/models.go 6 0
M internal/webhook/sqlc/models.go 6 0
M internal/worker/sqlc/models.go 6 0
internal/actions/queries/workflow_jobs.sqlmodified
@@ -213,6 +213,8 @@ FROM workflow_jobs j
213
 LEFT JOIN workflow_runners wr
213
 LEFT JOIN workflow_runners wr
214
   ON (j.runs_on = '' OR j.runs_on = ANY(wr.labels))
214
   ON (j.runs_on = '' OR j.runs_on = ANY(wr.labels))
215
  AND wr.status IN ('idle', 'busy')
215
  AND wr.status IN ('idle', 'busy')
216
+ AND wr.draining_at IS NULL
217
+ AND wr.revoked_at IS NULL
216
 WHERE j.status = 'queued'
218
 WHERE j.status = 'queued'
217
   AND j.cancel_requested = false
219
   AND j.cancel_requested = false
218
   AND j.runner_id IS NULL
220
   AND j.runner_id IS NULL
internal/actions/queries/workflow_runners.sqlmodified
@@ -4,28 +4,40 @@
4
 INSERT INTO workflow_runners (name, labels, capacity, registered_by_user_id)
4
 INSERT INTO workflow_runners (name, labels, capacity, registered_by_user_id)
5
 VALUES ($1, $2, $3, $4)
5
 VALUES ($1, $2, $3, $4)
6
 RETURNING id, name, labels, capacity, status, last_heartbeat_at,
6
 RETURNING id, name, labels, capacity, status, last_heartbeat_at,
7
-          registered_by_user_id, created_at, updated_at;
7
+          host_name, version, draining_at, drain_reason, revoked_at,
8
+          revoked_reason, registered_by_user_id, created_at, updated_at;
8
 
9
 
9
 -- name: GetRunnerByID :one
10
 -- name: GetRunnerByID :one
10
 SELECT id, name, labels, capacity, status, last_heartbeat_at,
11
 SELECT id, name, labels, capacity, status, last_heartbeat_at,
11
-       registered_by_user_id, created_at, updated_at
12
+       host_name, version, draining_at, drain_reason, revoked_at,
13
+       revoked_reason, registered_by_user_id, created_at, updated_at
12
 FROM workflow_runners
14
 FROM workflow_runners
13
 WHERE id = $1;
15
 WHERE id = $1;
14
 
16
 
15
 -- name: GetRunnerByName :one
17
 -- name: GetRunnerByName :one
16
 SELECT id, name, labels, capacity, status, last_heartbeat_at,
18
 SELECT id, name, labels, capacity, status, last_heartbeat_at,
17
-       registered_by_user_id, created_at, updated_at
19
+       host_name, version, draining_at, drain_reason, revoked_at,
20
+       revoked_reason, registered_by_user_id, created_at, updated_at
18
 FROM workflow_runners
21
 FROM workflow_runners
19
 WHERE name = $1;
22
 WHERE name = $1;
20
 
23
 
21
 -- name: ListRunners :many
24
 -- name: ListRunners :many
22
-SELECT id, name, labels, capacity, status, last_heartbeat_at, created_at
25
+SELECT r.id, r.name, r.labels, r.capacity, r.status, r.last_heartbeat_at,
23
-FROM workflow_runners
26
+       r.host_name, r.version, r.draining_at, r.drain_reason, r.revoked_at,
24
-ORDER BY name ASC;
27
+       r.revoked_reason, r.created_at, COUNT(j.id)::integer AS active_job_count
28
+FROM workflow_runners r
29
+LEFT JOIN workflow_jobs j
30
+       ON j.runner_id = r.id
31
+      AND j.status = 'running'
32
+GROUP BY r.id, r.name, r.labels, r.capacity, r.status, r.last_heartbeat_at,
33
+         r.host_name, r.version, r.draining_at, r.drain_reason, r.revoked_at,
34
+         r.revoked_reason, r.created_at
35
+ORDER BY r.name ASC;
25
 
36
 
26
 -- name: LockRunnerByID :one
37
 -- name: LockRunnerByID :one
27
 SELECT id, name, labels, capacity, status, last_heartbeat_at,
38
 SELECT id, name, labels, capacity, status, last_heartbeat_at,
28
-       registered_by_user_id, created_at, updated_at
39
+       host_name, version, draining_at, drain_reason, revoked_at,
40
+       revoked_reason, registered_by_user_id, created_at, updated_at
29
 FROM workflow_runners
41
 FROM workflow_runners
30
 WHERE id = $1
42
 WHERE id = $1
31
 FOR UPDATE;
43
 FOR UPDATE;
@@ -36,10 +48,13 @@ SET labels = $2,
36
     capacity = $3,
48
     capacity = $3,
37
     last_heartbeat_at = now(),
49
     last_heartbeat_at = now(),
38
     status = $4,
50
     status = $4,
51
+    host_name = $5,
52
+    version = $6,
39
     updated_at = now()
53
     updated_at = now()
40
 WHERE id = $1
54
 WHERE id = $1
41
 RETURNING id, name, labels, capacity, status, last_heartbeat_at,
55
 RETURNING id, name, labels, capacity, status, last_heartbeat_at,
42
-          registered_by_user_id, created_at, updated_at;
56
+          host_name, version, draining_at, drain_reason, revoked_at,
57
+          revoked_reason, registered_by_user_id, created_at, updated_at;
43
 
58
 
44
 -- name: TouchRunnerHeartbeat :exec
59
 -- name: TouchRunnerHeartbeat :exec
45
 UPDATE workflow_runners
60
 UPDATE workflow_runners
@@ -55,14 +70,66 @@ RETURNING id, runner_id, token_hash, expires_at, revoked_at, created_at;
55
 
70
 
56
 -- name: GetRunnerByTokenHash :one
71
 -- name: GetRunnerByTokenHash :one
57
 SELECT r.id, r.name, r.labels, r.capacity, r.status,
72
 SELECT r.id, r.name, r.labels, r.capacity, r.status,
58
-       r.last_heartbeat_at, r.created_at
73
+       r.last_heartbeat_at, r.host_name, r.version, r.draining_at,
74
+       r.drain_reason, r.revoked_at, r.revoked_reason, r.created_at
59
 FROM workflow_runners r
75
 FROM workflow_runners r
60
 JOIN runner_tokens t ON t.runner_id = r.id
76
 JOIN runner_tokens t ON t.runner_id = r.id
61
 WHERE t.token_hash = $1
77
 WHERE t.token_hash = $1
62
   AND t.revoked_at IS NULL
78
   AND t.revoked_at IS NULL
79
+  AND r.revoked_at IS NULL
63
   AND (t.expires_at IS NULL OR t.expires_at > now());
80
   AND (t.expires_at IS NULL OR t.expires_at > now());
64
 
81
 
82
+-- name: SetRunnerDraining :one
83
+UPDATE workflow_runners
84
+SET draining_at = COALESCE(draining_at, now()),
85
+    drain_reason = $2,
86
+    updated_at = now()
87
+WHERE id = $1
88
+  AND revoked_at IS NULL
89
+RETURNING id, name, labels, capacity, status, last_heartbeat_at,
90
+          host_name, version, draining_at, drain_reason, revoked_at,
91
+          revoked_reason, registered_by_user_id, created_at, updated_at;
92
+
93
+-- name: ClearRunnerDraining :one
94
+UPDATE workflow_runners
95
+SET draining_at = NULL,
96
+    drain_reason = '',
97
+    updated_at = now()
98
+WHERE id = $1
99
+  AND revoked_at IS NULL
100
+RETURNING id, name, labels, capacity, status, last_heartbeat_at,
101
+          host_name, version, draining_at, drain_reason, revoked_at,
102
+          revoked_reason, registered_by_user_id, created_at, updated_at;
103
+
104
+-- name: RevokeRunner :one
105
+UPDATE workflow_runners
106
+SET revoked_at = COALESCE(revoked_at, now()),
107
+    revoked_reason = CASE WHEN revoked_at IS NULL THEN $2 ELSE revoked_reason END,
108
+    draining_at = COALESCE(draining_at, now()),
109
+    drain_reason = CASE
110
+        WHEN draining_at IS NULL THEN $2
111
+        ELSE drain_reason
112
+    END,
113
+    status = 'offline',
114
+    updated_at = now()
115
+WHERE id = $1
116
+RETURNING id, name, labels, capacity, status, last_heartbeat_at,
117
+          host_name, version, draining_at, drain_reason, revoked_at,
118
+          revoked_reason, registered_by_user_id, created_at, updated_at;
119
+
65
 -- name: RevokeAllTokensForRunner :exec
120
 -- name: RevokeAllTokensForRunner :exec
66
 UPDATE runner_tokens
121
 UPDATE runner_tokens
67
 SET revoked_at = now()
122
 SET revoked_at = now()
68
 WHERE runner_id = $1 AND revoked_at IS NULL;
123
 WHERE runner_id = $1 AND revoked_at IS NULL;
124
+
125
+-- name: MarkStaleRunnersOffline :many
126
+UPDATE workflow_runners
127
+SET status = 'offline',
128
+    updated_at = now()
129
+WHERE revoked_at IS NULL
130
+  AND status <> 'offline'
131
+  AND last_heartbeat_at IS NOT NULL
132
+  AND last_heartbeat_at < $1
133
+RETURNING id, name, labels, capacity, status, last_heartbeat_at,
134
+          host_name, version, draining_at, drain_reason, revoked_at,
135
+          revoked_reason, registered_by_user_id, created_at, updated_at;
internal/actions/sqlc/models.gomodified
@@ -2861,6 +2861,12 @@ type WorkflowRunner struct {
2861
 	RegisteredByUserID pgtype.Int8
2861
 	RegisteredByUserID pgtype.Int8
2862
 	CreatedAt          pgtype.Timestamptz
2862
 	CreatedAt          pgtype.Timestamptz
2863
 	UpdatedAt          pgtype.Timestamptz
2863
 	UpdatedAt          pgtype.Timestamptz
2864
+	HostName           string
2865
+	Version            string
2866
+	DrainingAt         pgtype.Timestamptz
2867
+	DrainReason        string
2868
+	RevokedAt          pgtype.Timestamptz
2869
+	RevokedReason      string
2864
 }
2870
 }
2865
 
2871
 
2866
 type WorkflowSecret struct {
2872
 type WorkflowSecret struct {
internal/actions/sqlc/querier.gomodified
@@ -16,6 +16,7 @@ type Querier interface {
16
 	ApproveWorkflowRun(ctx context.Context, db DBTX, arg ApproveWorkflowRunParams) (ApproveWorkflowRunRow, error)
16
 	ApproveWorkflowRun(ctx context.Context, db DBTX, arg ApproveWorkflowRunParams) (ApproveWorkflowRunRow, error)
17
 	CancelOpenWorkflowStepsForJob(ctx context.Context, db DBTX, jobID int64) ([]WorkflowStep, error)
17
 	CancelOpenWorkflowStepsForJob(ctx context.Context, db DBTX, jobID int64) ([]WorkflowStep, error)
18
 	ClaimQueuedWorkflowJob(ctx context.Context, db DBTX, arg ClaimQueuedWorkflowJobParams) (ClaimQueuedWorkflowJobRow, error)
18
 	ClaimQueuedWorkflowJob(ctx context.Context, db DBTX, arg ClaimQueuedWorkflowJobParams) (ClaimQueuedWorkflowJobRow, error)
19
+	ClearRunnerDraining(ctx context.Context, db DBTX, id int64) (ClearRunnerDrainingRow, error)
19
 	CompleteWorkflowRun(ctx context.Context, db DBTX, arg CompleteWorkflowRunParams) (WorkflowRun, error)
20
 	CompleteWorkflowRun(ctx context.Context, db DBTX, arg CompleteWorkflowRunParams) (WorkflowRun, error)
20
 	CountQueuedWorkflowRunsForRepo(ctx context.Context, db DBTX, repoID int64) (int64, error)
21
 	CountQueuedWorkflowRunsForRepo(ctx context.Context, db DBTX, repoID int64) (int64, error)
21
 	CountRecentWorkflowRunsForActor(ctx context.Context, db DBTX, arg CountRecentWorkflowRunsForActorParams) (int64, error)
22
 	CountRecentWorkflowRunsForActor(ctx context.Context, db DBTX, arg CountRecentWorkflowRunsForActorParams) (int64, error)
@@ -69,8 +70,8 @@ type Querier interface {
69
 	GetOrgVariable(ctx context.Context, db DBTX, arg GetOrgVariableParams) (GetOrgVariableRow, error)
70
 	GetOrgVariable(ctx context.Context, db DBTX, arg GetOrgVariableParams) (GetOrgVariableRow, error)
70
 	GetRepoSecret(ctx context.Context, db DBTX, arg GetRepoSecretParams) (GetRepoSecretRow, error)
71
 	GetRepoSecret(ctx context.Context, db DBTX, arg GetRepoSecretParams) (GetRepoSecretRow, error)
71
 	GetRepoVariable(ctx context.Context, db DBTX, arg GetRepoVariableParams) (GetRepoVariableRow, error)
72
 	GetRepoVariable(ctx context.Context, db DBTX, arg GetRepoVariableParams) (GetRepoVariableRow, error)
72
-	GetRunnerByID(ctx context.Context, db DBTX, id int64) (WorkflowRunner, error)
73
+	GetRunnerByID(ctx context.Context, db DBTX, id int64) (GetRunnerByIDRow, error)
73
-	GetRunnerByName(ctx context.Context, db DBTX, name string) (WorkflowRunner, error)
74
+	GetRunnerByName(ctx context.Context, db DBTX, name string) (GetRunnerByNameRow, error)
74
 	GetRunnerByTokenHash(ctx context.Context, db DBTX, tokenHash []byte) (GetRunnerByTokenHashRow, error)
75
 	GetRunnerByTokenHash(ctx context.Context, db DBTX, tokenHash []byte) (GetRunnerByTokenHashRow, error)
75
 	GetStepLogChunkBefore(ctx context.Context, db DBTX, arg GetStepLogChunkBeforeParams) (WorkflowStepLogChunk, error)
76
 	GetStepLogChunkBefore(ctx context.Context, db DBTX, arg GetStepLogChunkBeforeParams) (WorkflowStepLogChunk, error)
76
 	GetStepLogChunkByStepSeq(ctx context.Context, db DBTX, arg GetStepLogChunkByStepSeqParams) (WorkflowStepLogChunk, error)
77
 	GetStepLogChunkByStepSeq(ctx context.Context, db DBTX, arg GetStepLogChunkByStepSeqParams) (WorkflowStepLogChunk, error)
@@ -81,11 +82,11 @@ type Querier interface {
81
 	GetWorkflowRunByID(ctx context.Context, db DBTX, id int64) (WorkflowRun, error)
82
 	GetWorkflowRunByID(ctx context.Context, db DBTX, id int64) (WorkflowRun, error)
82
 	GetWorkflowRunForRepoByIndex(ctx context.Context, db DBTX, arg GetWorkflowRunForRepoByIndexParams) (GetWorkflowRunForRepoByIndexRow, error)
83
 	GetWorkflowRunForRepoByIndex(ctx context.Context, db DBTX, arg GetWorkflowRunForRepoByIndexParams) (GetWorkflowRunForRepoByIndexRow, error)
83
 	GetWorkflowStepByID(ctx context.Context, db DBTX, id int64) (WorkflowStep, error)
84
 	GetWorkflowStepByID(ctx context.Context, db DBTX, id int64) (WorkflowStep, error)
84
-	HeartbeatRunner(ctx context.Context, db DBTX, arg HeartbeatRunnerParams) (WorkflowRunner, error)
85
+	HeartbeatRunner(ctx context.Context, db DBTX, arg HeartbeatRunnerParams) (HeartbeatRunnerRow, error)
85
 	// SPDX-License-Identifier: AGPL-3.0-or-later
86
 	// SPDX-License-Identifier: AGPL-3.0-or-later
86
 	InsertArtifact(ctx context.Context, db DBTX, arg InsertArtifactParams) (WorkflowArtifact, error)
87
 	InsertArtifact(ctx context.Context, db DBTX, arg InsertArtifactParams) (WorkflowArtifact, error)
87
 	// SPDX-License-Identifier: AGPL-3.0-or-later
88
 	// SPDX-License-Identifier: AGPL-3.0-or-later
88
-	InsertRunner(ctx context.Context, db DBTX, arg InsertRunnerParams) (WorkflowRunner, error)
89
+	InsertRunner(ctx context.Context, db DBTX, arg InsertRunnerParams) (InsertRunnerRow, error)
89
 	InsertRunnerToken(ctx context.Context, db DBTX, arg InsertRunnerTokenParams) (RunnerToken, error)
90
 	InsertRunnerToken(ctx context.Context, db DBTX, arg InsertRunnerTokenParams) (RunnerToken, error)
90
 	// SPDX-License-Identifier: AGPL-3.0-or-later
91
 	// SPDX-License-Identifier: AGPL-3.0-or-later
91
 	// Called by the future runner-side upload handler when an
92
 	// Called by the future runner-side upload handler when an
@@ -136,7 +137,7 @@ type Querier interface {
136
 	ListWorkflowCachesForRepo(ctx context.Context, db DBTX, arg ListWorkflowCachesForRepoParams) ([]WorkflowCache, error)
137
 	ListWorkflowCachesForRepo(ctx context.Context, db DBTX, arg ListWorkflowCachesForRepoParams) ([]WorkflowCache, error)
137
 	ListWorkflowRunWorkflowsForRepo(ctx context.Context, db DBTX, repoID int64) ([]ListWorkflowRunWorkflowsForRepoRow, error)
138
 	ListWorkflowRunWorkflowsForRepo(ctx context.Context, db DBTX, repoID int64) ([]ListWorkflowRunWorkflowsForRepoRow, error)
138
 	ListWorkflowRunsForRepo(ctx context.Context, db DBTX, arg ListWorkflowRunsForRepoParams) ([]ListWorkflowRunsForRepoRow, error)
139
 	ListWorkflowRunsForRepo(ctx context.Context, db DBTX, arg ListWorkflowRunsForRepoParams) ([]ListWorkflowRunsForRepoRow, error)
139
-	LockRunnerByID(ctx context.Context, db DBTX, id int64) (WorkflowRunner, error)
140
+	LockRunnerByID(ctx context.Context, db DBTX, id int64) (LockRunnerByIDRow, error)
140
 	// Companion to EnqueueWorkflowRun for the conflict path: when an
141
 	// Companion to EnqueueWorkflowRun for the conflict path: when an
141
 	// INSERT ... ON CONFLICT DO NOTHING returns no rows, the trigger
142
 	// INSERT ... ON CONFLICT DO NOTHING returns no rows, the trigger
142
 	// handler uses this to find the existing row so it can surface a
143
 	// handler uses this to find the existing row so it can surface a
@@ -144,6 +145,7 @@ type Querier interface {
144
 	LookupWorkflowRunByTriggerEvent(ctx context.Context, db DBTX, arg LookupWorkflowRunByTriggerEventParams) (WorkflowRun, error)
145
 	LookupWorkflowRunByTriggerEvent(ctx context.Context, db DBTX, arg LookupWorkflowRunByTriggerEventParams) (WorkflowRun, error)
145
 	// SPDX-License-Identifier: AGPL-3.0-or-later
146
 	// SPDX-License-Identifier: AGPL-3.0-or-later
146
 	MarkRunnerJWTUsed(ctx context.Context, db DBTX, arg MarkRunnerJWTUsedParams) (RunnerJwtUsed, error)
147
 	MarkRunnerJWTUsed(ctx context.Context, db DBTX, arg MarkRunnerJWTUsedParams) (RunnerJwtUsed, error)
148
+	MarkStaleRunnersOffline(ctx context.Context, db DBTX, lastHeartbeatAt pgtype.Timestamptz) ([]MarkStaleRunnersOfflineRow, error)
147
 	MarkWorkflowJobsRejected(ctx context.Context, db DBTX, runID int64) ([]WorkflowJob, error)
149
 	MarkWorkflowJobsRejected(ctx context.Context, db DBTX, runID int64) ([]WorkflowJob, error)
148
 	MarkWorkflowRunRejected(ctx context.Context, db DBTX, id int64) (WorkflowRun, error)
150
 	MarkWorkflowRunRejected(ctx context.Context, db DBTX, id int64) (WorkflowRun, error)
149
 	MarkWorkflowRunRunning(ctx context.Context, db DBTX, id int64) error
151
 	MarkWorkflowRunRunning(ctx context.Context, db DBTX, id int64) error
@@ -157,6 +159,8 @@ type Querier interface {
157
 	RequestWorkflowJobCancel(ctx context.Context, db DBTX, id int64) (WorkflowJob, error)
159
 	RequestWorkflowJobCancel(ctx context.Context, db DBTX, id int64) (WorkflowJob, error)
158
 	RequestWorkflowRunCancel(ctx context.Context, db DBTX, runID int64) ([]WorkflowJob, error)
160
 	RequestWorkflowRunCancel(ctx context.Context, db DBTX, runID int64) ([]WorkflowJob, error)
159
 	RevokeAllTokensForRunner(ctx context.Context, db DBTX, runnerID int64) error
161
 	RevokeAllTokensForRunner(ctx context.Context, db DBTX, runnerID int64) error
162
+	RevokeRunner(ctx context.Context, db DBTX, arg RevokeRunnerParams) (RevokeRunnerRow, error)
163
+	SetRunnerDraining(ctx context.Context, db DBTX, arg SetRunnerDrainingParams) (SetRunnerDrainingRow, error)
160
 	StartWorkflowRun(ctx context.Context, db DBTX, id int64) (WorkflowRun, error)
164
 	StartWorkflowRun(ctx context.Context, db DBTX, id int64) (WorkflowRun, error)
161
 	TouchRunnerHeartbeat(ctx context.Context, db DBTX, arg TouchRunnerHeartbeatParams) error
165
 	TouchRunnerHeartbeat(ctx context.Context, db DBTX, arg TouchRunnerHeartbeatParams) error
162
 	// Bumps last_accessed_at on cache hit. Called by the future
166
 	// Bumps last_accessed_at on cache hit. Called by the future
internal/actions/sqlc/workflow_jobs.sql.gomodified
@@ -374,6 +374,8 @@ FROM workflow_jobs j
374
 LEFT JOIN workflow_runners wr
374
 LEFT JOIN workflow_runners wr
375
   ON (j.runs_on = '' OR j.runs_on = ANY(wr.labels))
375
   ON (j.runs_on = '' OR j.runs_on = ANY(wr.labels))
376
  AND wr.status IN ('idle', 'busy')
376
  AND wr.status IN ('idle', 'busy')
377
+ AND wr.draining_at IS NULL
378
+ AND wr.revoked_at IS NULL
377
 WHERE j.status = 'queued'
379
 WHERE j.status = 'queued'
378
   AND j.cancel_requested = false
380
   AND j.cancel_requested = false
379
   AND j.runner_id IS NULL
381
   AND j.runner_id IS NULL
internal/actions/sqlc/workflow_runners.sql.gomodified
@@ -11,16 +11,88 @@ import (
11
 	"github.com/jackc/pgx/v5/pgtype"
11
 	"github.com/jackc/pgx/v5/pgtype"
12
 )
12
 )
13
 
13
 
14
+const clearRunnerDraining = `-- name: ClearRunnerDraining :one
15
+UPDATE workflow_runners
16
+SET draining_at = NULL,
17
+    drain_reason = '',
18
+    updated_at = now()
19
+WHERE id = $1
20
+  AND revoked_at IS NULL
21
+RETURNING id, name, labels, capacity, status, last_heartbeat_at,
22
+          host_name, version, draining_at, drain_reason, revoked_at,
23
+          revoked_reason, registered_by_user_id, created_at, updated_at
24
+`
25
+
26
+type ClearRunnerDrainingRow struct {
27
+	ID                 int64
28
+	Name               string
29
+	Labels             []string
30
+	Capacity           int32
31
+	Status             WorkflowRunnerStatus
32
+	LastHeartbeatAt    pgtype.Timestamptz
33
+	HostName           string
34
+	Version            string
35
+	DrainingAt         pgtype.Timestamptz
36
+	DrainReason        string
37
+	RevokedAt          pgtype.Timestamptz
38
+	RevokedReason      string
39
+	RegisteredByUserID pgtype.Int8
40
+	CreatedAt          pgtype.Timestamptz
41
+	UpdatedAt          pgtype.Timestamptz
42
+}
43
+
44
+func (q *Queries) ClearRunnerDraining(ctx context.Context, db DBTX, id int64) (ClearRunnerDrainingRow, error) {
45
+	row := db.QueryRow(ctx, clearRunnerDraining, id)
46
+	var i ClearRunnerDrainingRow
47
+	err := row.Scan(
48
+		&i.ID,
49
+		&i.Name,
50
+		&i.Labels,
51
+		&i.Capacity,
52
+		&i.Status,
53
+		&i.LastHeartbeatAt,
54
+		&i.HostName,
55
+		&i.Version,
56
+		&i.DrainingAt,
57
+		&i.DrainReason,
58
+		&i.RevokedAt,
59
+		&i.RevokedReason,
60
+		&i.RegisteredByUserID,
61
+		&i.CreatedAt,
62
+		&i.UpdatedAt,
63
+	)
64
+	return i, err
65
+}
66
+
14
 const getRunnerByID = `-- name: GetRunnerByID :one
67
 const getRunnerByID = `-- name: GetRunnerByID :one
15
 SELECT id, name, labels, capacity, status, last_heartbeat_at,
68
 SELECT id, name, labels, capacity, status, last_heartbeat_at,
16
-       registered_by_user_id, created_at, updated_at
69
+       host_name, version, draining_at, drain_reason, revoked_at,
70
+       revoked_reason, registered_by_user_id, created_at, updated_at
17
 FROM workflow_runners
71
 FROM workflow_runners
18
 WHERE id = $1
72
 WHERE id = $1
19
 `
73
 `
20
 
74
 
21
-func (q *Queries) GetRunnerByID(ctx context.Context, db DBTX, id int64) (WorkflowRunner, error) {
75
+type GetRunnerByIDRow struct {
76
+	ID                 int64
77
+	Name               string
78
+	Labels             []string
79
+	Capacity           int32
80
+	Status             WorkflowRunnerStatus
81
+	LastHeartbeatAt    pgtype.Timestamptz
82
+	HostName           string
83
+	Version            string
84
+	DrainingAt         pgtype.Timestamptz
85
+	DrainReason        string
86
+	RevokedAt          pgtype.Timestamptz
87
+	RevokedReason      string
88
+	RegisteredByUserID pgtype.Int8
89
+	CreatedAt          pgtype.Timestamptz
90
+	UpdatedAt          pgtype.Timestamptz
91
+}
92
+
93
+func (q *Queries) GetRunnerByID(ctx context.Context, db DBTX, id int64) (GetRunnerByIDRow, error) {
22
 	row := db.QueryRow(ctx, getRunnerByID, id)
94
 	row := db.QueryRow(ctx, getRunnerByID, id)
23
-	var i WorkflowRunner
95
+	var i GetRunnerByIDRow
24
 	err := row.Scan(
96
 	err := row.Scan(
25
 		&i.ID,
97
 		&i.ID,
26
 		&i.Name,
98
 		&i.Name,
@@ -28,6 +100,12 @@ func (q *Queries) GetRunnerByID(ctx context.Context, db DBTX, id int64) (Workflo
28
 		&i.Capacity,
100
 		&i.Capacity,
29
 		&i.Status,
101
 		&i.Status,
30
 		&i.LastHeartbeatAt,
102
 		&i.LastHeartbeatAt,
103
+		&i.HostName,
104
+		&i.Version,
105
+		&i.DrainingAt,
106
+		&i.DrainReason,
107
+		&i.RevokedAt,
108
+		&i.RevokedReason,
31
 		&i.RegisteredByUserID,
109
 		&i.RegisteredByUserID,
32
 		&i.CreatedAt,
110
 		&i.CreatedAt,
33
 		&i.UpdatedAt,
111
 		&i.UpdatedAt,
@@ -37,14 +115,33 @@ func (q *Queries) GetRunnerByID(ctx context.Context, db DBTX, id int64) (Workflo
37
 
115
 
38
 const getRunnerByName = `-- name: GetRunnerByName :one
116
 const getRunnerByName = `-- name: GetRunnerByName :one
39
 SELECT id, name, labels, capacity, status, last_heartbeat_at,
117
 SELECT id, name, labels, capacity, status, last_heartbeat_at,
40
-       registered_by_user_id, created_at, updated_at
118
+       host_name, version, draining_at, drain_reason, revoked_at,
119
+       revoked_reason, registered_by_user_id, created_at, updated_at
41
 FROM workflow_runners
120
 FROM workflow_runners
42
 WHERE name = $1
121
 WHERE name = $1
43
 `
122
 `
44
 
123
 
45
-func (q *Queries) GetRunnerByName(ctx context.Context, db DBTX, name string) (WorkflowRunner, error) {
124
+type GetRunnerByNameRow struct {
125
+	ID                 int64
126
+	Name               string
127
+	Labels             []string
128
+	Capacity           int32
129
+	Status             WorkflowRunnerStatus
130
+	LastHeartbeatAt    pgtype.Timestamptz
131
+	HostName           string
132
+	Version            string
133
+	DrainingAt         pgtype.Timestamptz
134
+	DrainReason        string
135
+	RevokedAt          pgtype.Timestamptz
136
+	RevokedReason      string
137
+	RegisteredByUserID pgtype.Int8
138
+	CreatedAt          pgtype.Timestamptz
139
+	UpdatedAt          pgtype.Timestamptz
140
+}
141
+
142
+func (q *Queries) GetRunnerByName(ctx context.Context, db DBTX, name string) (GetRunnerByNameRow, error) {
46
 	row := db.QueryRow(ctx, getRunnerByName, name)
143
 	row := db.QueryRow(ctx, getRunnerByName, name)
47
-	var i WorkflowRunner
144
+	var i GetRunnerByNameRow
48
 	err := row.Scan(
145
 	err := row.Scan(
49
 		&i.ID,
146
 		&i.ID,
50
 		&i.Name,
147
 		&i.Name,
@@ -52,6 +149,12 @@ func (q *Queries) GetRunnerByName(ctx context.Context, db DBTX, name string) (Wo
52
 		&i.Capacity,
149
 		&i.Capacity,
53
 		&i.Status,
150
 		&i.Status,
54
 		&i.LastHeartbeatAt,
151
 		&i.LastHeartbeatAt,
152
+		&i.HostName,
153
+		&i.Version,
154
+		&i.DrainingAt,
155
+		&i.DrainReason,
156
+		&i.RevokedAt,
157
+		&i.RevokedReason,
55
 		&i.RegisteredByUserID,
158
 		&i.RegisteredByUserID,
56
 		&i.CreatedAt,
159
 		&i.CreatedAt,
57
 		&i.UpdatedAt,
160
 		&i.UpdatedAt,
@@ -61,11 +164,13 @@ func (q *Queries) GetRunnerByName(ctx context.Context, db DBTX, name string) (Wo
61
 
164
 
62
 const getRunnerByTokenHash = `-- name: GetRunnerByTokenHash :one
165
 const getRunnerByTokenHash = `-- name: GetRunnerByTokenHash :one
63
 SELECT r.id, r.name, r.labels, r.capacity, r.status,
166
 SELECT r.id, r.name, r.labels, r.capacity, r.status,
64
-       r.last_heartbeat_at, r.created_at
167
+       r.last_heartbeat_at, r.host_name, r.version, r.draining_at,
168
+       r.drain_reason, r.revoked_at, r.revoked_reason, r.created_at
65
 FROM workflow_runners r
169
 FROM workflow_runners r
66
 JOIN runner_tokens t ON t.runner_id = r.id
170
 JOIN runner_tokens t ON t.runner_id = r.id
67
 WHERE t.token_hash = $1
171
 WHERE t.token_hash = $1
68
   AND t.revoked_at IS NULL
172
   AND t.revoked_at IS NULL
173
+  AND r.revoked_at IS NULL
69
   AND (t.expires_at IS NULL OR t.expires_at > now())
174
   AND (t.expires_at IS NULL OR t.expires_at > now())
70
 `
175
 `
71
 
176
 
@@ -76,6 +181,12 @@ type GetRunnerByTokenHashRow struct {
76
 	Capacity        int32
181
 	Capacity        int32
77
 	Status          WorkflowRunnerStatus
182
 	Status          WorkflowRunnerStatus
78
 	LastHeartbeatAt pgtype.Timestamptz
183
 	LastHeartbeatAt pgtype.Timestamptz
184
+	HostName        string
185
+	Version         string
186
+	DrainingAt      pgtype.Timestamptz
187
+	DrainReason     string
188
+	RevokedAt       pgtype.Timestamptz
189
+	RevokedReason   string
79
 	CreatedAt       pgtype.Timestamptz
190
 	CreatedAt       pgtype.Timestamptz
80
 }
191
 }
81
 
192
 
@@ -89,6 +200,12 @@ func (q *Queries) GetRunnerByTokenHash(ctx context.Context, db DBTX, tokenHash [
89
 		&i.Capacity,
200
 		&i.Capacity,
90
 		&i.Status,
201
 		&i.Status,
91
 		&i.LastHeartbeatAt,
202
 		&i.LastHeartbeatAt,
203
+		&i.HostName,
204
+		&i.Version,
205
+		&i.DrainingAt,
206
+		&i.DrainReason,
207
+		&i.RevokedAt,
208
+		&i.RevokedReason,
92
 		&i.CreatedAt,
209
 		&i.CreatedAt,
93
 	)
210
 	)
94
 	return i, err
211
 	return i, err
@@ -100,10 +217,13 @@ SET labels = $2,
100
     capacity = $3,
217
     capacity = $3,
101
     last_heartbeat_at = now(),
218
     last_heartbeat_at = now(),
102
     status = $4,
219
     status = $4,
220
+    host_name = $5,
221
+    version = $6,
103
     updated_at = now()
222
     updated_at = now()
104
 WHERE id = $1
223
 WHERE id = $1
105
 RETURNING id, name, labels, capacity, status, last_heartbeat_at,
224
 RETURNING id, name, labels, capacity, status, last_heartbeat_at,
106
-          registered_by_user_id, created_at, updated_at
225
+          host_name, version, draining_at, drain_reason, revoked_at,
226
+          revoked_reason, registered_by_user_id, created_at, updated_at
107
 `
227
 `
108
 
228
 
109
 type HeartbeatRunnerParams struct {
229
 type HeartbeatRunnerParams struct {
@@ -111,16 +231,38 @@ type HeartbeatRunnerParams struct {
111
 	Labels   []string
231
 	Labels   []string
112
 	Capacity int32
232
 	Capacity int32
113
 	Status   WorkflowRunnerStatus
233
 	Status   WorkflowRunnerStatus
234
+	HostName string
235
+	Version  string
236
+}
237
+
238
+type HeartbeatRunnerRow struct {
239
+	ID                 int64
240
+	Name               string
241
+	Labels             []string
242
+	Capacity           int32
243
+	Status             WorkflowRunnerStatus
244
+	LastHeartbeatAt    pgtype.Timestamptz
245
+	HostName           string
246
+	Version            string
247
+	DrainingAt         pgtype.Timestamptz
248
+	DrainReason        string
249
+	RevokedAt          pgtype.Timestamptz
250
+	RevokedReason      string
251
+	RegisteredByUserID pgtype.Int8
252
+	CreatedAt          pgtype.Timestamptz
253
+	UpdatedAt          pgtype.Timestamptz
114
 }
254
 }
115
 
255
 
116
-func (q *Queries) HeartbeatRunner(ctx context.Context, db DBTX, arg HeartbeatRunnerParams) (WorkflowRunner, error) {
256
+func (q *Queries) HeartbeatRunner(ctx context.Context, db DBTX, arg HeartbeatRunnerParams) (HeartbeatRunnerRow, error) {
117
 	row := db.QueryRow(ctx, heartbeatRunner,
257
 	row := db.QueryRow(ctx, heartbeatRunner,
118
 		arg.ID,
258
 		arg.ID,
119
 		arg.Labels,
259
 		arg.Labels,
120
 		arg.Capacity,
260
 		arg.Capacity,
121
 		arg.Status,
261
 		arg.Status,
262
+		arg.HostName,
263
+		arg.Version,
122
 	)
264
 	)
123
-	var i WorkflowRunner
265
+	var i HeartbeatRunnerRow
124
 	err := row.Scan(
266
 	err := row.Scan(
125
 		&i.ID,
267
 		&i.ID,
126
 		&i.Name,
268
 		&i.Name,
@@ -128,6 +270,12 @@ func (q *Queries) HeartbeatRunner(ctx context.Context, db DBTX, arg HeartbeatRun
128
 		&i.Capacity,
270
 		&i.Capacity,
129
 		&i.Status,
271
 		&i.Status,
130
 		&i.LastHeartbeatAt,
272
 		&i.LastHeartbeatAt,
273
+		&i.HostName,
274
+		&i.Version,
275
+		&i.DrainingAt,
276
+		&i.DrainReason,
277
+		&i.RevokedAt,
278
+		&i.RevokedReason,
131
 		&i.RegisteredByUserID,
279
 		&i.RegisteredByUserID,
132
 		&i.CreatedAt,
280
 		&i.CreatedAt,
133
 		&i.UpdatedAt,
281
 		&i.UpdatedAt,
@@ -140,7 +288,8 @@ const insertRunner = `-- name: InsertRunner :one
140
 INSERT INTO workflow_runners (name, labels, capacity, registered_by_user_id)
288
 INSERT INTO workflow_runners (name, labels, capacity, registered_by_user_id)
141
 VALUES ($1, $2, $3, $4)
289
 VALUES ($1, $2, $3, $4)
142
 RETURNING id, name, labels, capacity, status, last_heartbeat_at,
290
 RETURNING id, name, labels, capacity, status, last_heartbeat_at,
143
-          registered_by_user_id, created_at, updated_at
291
+          host_name, version, draining_at, drain_reason, revoked_at,
292
+          revoked_reason, registered_by_user_id, created_at, updated_at
144
 `
293
 `
145
 
294
 
146
 type InsertRunnerParams struct {
295
 type InsertRunnerParams struct {
@@ -150,15 +299,33 @@ type InsertRunnerParams struct {
150
 	RegisteredByUserID pgtype.Int8
299
 	RegisteredByUserID pgtype.Int8
151
 }
300
 }
152
 
301
 
302
+type InsertRunnerRow struct {
303
+	ID                 int64
304
+	Name               string
305
+	Labels             []string
306
+	Capacity           int32
307
+	Status             WorkflowRunnerStatus
308
+	LastHeartbeatAt    pgtype.Timestamptz
309
+	HostName           string
310
+	Version            string
311
+	DrainingAt         pgtype.Timestamptz
312
+	DrainReason        string
313
+	RevokedAt          pgtype.Timestamptz
314
+	RevokedReason      string
315
+	RegisteredByUserID pgtype.Int8
316
+	CreatedAt          pgtype.Timestamptz
317
+	UpdatedAt          pgtype.Timestamptz
318
+}
319
+
153
 // SPDX-License-Identifier: AGPL-3.0-or-later
320
 // SPDX-License-Identifier: AGPL-3.0-or-later
154
-func (q *Queries) InsertRunner(ctx context.Context, db DBTX, arg InsertRunnerParams) (WorkflowRunner, error) {
321
+func (q *Queries) InsertRunner(ctx context.Context, db DBTX, arg InsertRunnerParams) (InsertRunnerRow, error) {
155
 	row := db.QueryRow(ctx, insertRunner,
322
 	row := db.QueryRow(ctx, insertRunner,
156
 		arg.Name,
323
 		arg.Name,
157
 		arg.Labels,
324
 		arg.Labels,
158
 		arg.Capacity,
325
 		arg.Capacity,
159
 		arg.RegisteredByUserID,
326
 		arg.RegisteredByUserID,
160
 	)
327
 	)
161
-	var i WorkflowRunner
328
+	var i InsertRunnerRow
162
 	err := row.Scan(
329
 	err := row.Scan(
163
 		&i.ID,
330
 		&i.ID,
164
 		&i.Name,
331
 		&i.Name,
@@ -166,6 +333,12 @@ func (q *Queries) InsertRunner(ctx context.Context, db DBTX, arg InsertRunnerPar
166
 		&i.Capacity,
333
 		&i.Capacity,
167
 		&i.Status,
334
 		&i.Status,
168
 		&i.LastHeartbeatAt,
335
 		&i.LastHeartbeatAt,
336
+		&i.HostName,
337
+		&i.Version,
338
+		&i.DrainingAt,
339
+		&i.DrainReason,
340
+		&i.RevokedAt,
341
+		&i.RevokedReason,
169
 		&i.RegisteredByUserID,
342
 		&i.RegisteredByUserID,
170
 		&i.CreatedAt,
343
 		&i.CreatedAt,
171
 		&i.UpdatedAt,
344
 		&i.UpdatedAt,
@@ -200,9 +373,17 @@ func (q *Queries) InsertRunnerToken(ctx context.Context, db DBTX, arg InsertRunn
200
 }
373
 }
201
 
374
 
202
 const listRunners = `-- name: ListRunners :many
375
 const listRunners = `-- name: ListRunners :many
203
-SELECT id, name, labels, capacity, status, last_heartbeat_at, created_at
376
+SELECT r.id, r.name, r.labels, r.capacity, r.status, r.last_heartbeat_at,
204
-FROM workflow_runners
377
+       r.host_name, r.version, r.draining_at, r.drain_reason, r.revoked_at,
205
-ORDER BY name ASC
378
+       r.revoked_reason, r.created_at, COUNT(j.id)::integer AS active_job_count
379
+FROM workflow_runners r
380
+LEFT JOIN workflow_jobs j
381
+       ON j.runner_id = r.id
382
+      AND j.status = 'running'
383
+GROUP BY r.id, r.name, r.labels, r.capacity, r.status, r.last_heartbeat_at,
384
+         r.host_name, r.version, r.draining_at, r.drain_reason, r.revoked_at,
385
+         r.revoked_reason, r.created_at
386
+ORDER BY r.name ASC
206
 `
387
 `
207
 
388
 
208
 type ListRunnersRow struct {
389
 type ListRunnersRow struct {
@@ -212,7 +393,14 @@ type ListRunnersRow struct {
212
 	Capacity        int32
393
 	Capacity        int32
213
 	Status          WorkflowRunnerStatus
394
 	Status          WorkflowRunnerStatus
214
 	LastHeartbeatAt pgtype.Timestamptz
395
 	LastHeartbeatAt pgtype.Timestamptz
396
+	HostName        string
397
+	Version         string
398
+	DrainingAt      pgtype.Timestamptz
399
+	DrainReason     string
400
+	RevokedAt       pgtype.Timestamptz
401
+	RevokedReason   string
215
 	CreatedAt       pgtype.Timestamptz
402
 	CreatedAt       pgtype.Timestamptz
403
+	ActiveJobCount  int32
216
 }
404
 }
217
 
405
 
218
 func (q *Queries) ListRunners(ctx context.Context, db DBTX) ([]ListRunnersRow, error) {
406
 func (q *Queries) ListRunners(ctx context.Context, db DBTX) ([]ListRunnersRow, error) {
@@ -231,7 +419,14 @@ func (q *Queries) ListRunners(ctx context.Context, db DBTX) ([]ListRunnersRow, e
231
 			&i.Capacity,
419
 			&i.Capacity,
232
 			&i.Status,
420
 			&i.Status,
233
 			&i.LastHeartbeatAt,
421
 			&i.LastHeartbeatAt,
422
+			&i.HostName,
423
+			&i.Version,
424
+			&i.DrainingAt,
425
+			&i.DrainReason,
426
+			&i.RevokedAt,
427
+			&i.RevokedReason,
234
 			&i.CreatedAt,
428
 			&i.CreatedAt,
429
+			&i.ActiveJobCount,
235
 		); err != nil {
430
 		); err != nil {
236
 			return nil, err
431
 			return nil, err
237
 		}
432
 		}
@@ -245,15 +440,34 @@ func (q *Queries) ListRunners(ctx context.Context, db DBTX) ([]ListRunnersRow, e
245
 
440
 
246
 const lockRunnerByID = `-- name: LockRunnerByID :one
441
 const lockRunnerByID = `-- name: LockRunnerByID :one
247
 SELECT id, name, labels, capacity, status, last_heartbeat_at,
442
 SELECT id, name, labels, capacity, status, last_heartbeat_at,
248
-       registered_by_user_id, created_at, updated_at
443
+       host_name, version, draining_at, drain_reason, revoked_at,
444
+       revoked_reason, registered_by_user_id, created_at, updated_at
249
 FROM workflow_runners
445
 FROM workflow_runners
250
 WHERE id = $1
446
 WHERE id = $1
251
 FOR UPDATE
447
 FOR UPDATE
252
 `
448
 `
253
 
449
 
254
-func (q *Queries) LockRunnerByID(ctx context.Context, db DBTX, id int64) (WorkflowRunner, error) {
450
+type LockRunnerByIDRow struct {
451
+	ID                 int64
452
+	Name               string
453
+	Labels             []string
454
+	Capacity           int32
455
+	Status             WorkflowRunnerStatus
456
+	LastHeartbeatAt    pgtype.Timestamptz
457
+	HostName           string
458
+	Version            string
459
+	DrainingAt         pgtype.Timestamptz
460
+	DrainReason        string
461
+	RevokedAt          pgtype.Timestamptz
462
+	RevokedReason      string
463
+	RegisteredByUserID pgtype.Int8
464
+	CreatedAt          pgtype.Timestamptz
465
+	UpdatedAt          pgtype.Timestamptz
466
+}
467
+
468
+func (q *Queries) LockRunnerByID(ctx context.Context, db DBTX, id int64) (LockRunnerByIDRow, error) {
255
 	row := db.QueryRow(ctx, lockRunnerByID, id)
469
 	row := db.QueryRow(ctx, lockRunnerByID, id)
256
-	var i WorkflowRunner
470
+	var i LockRunnerByIDRow
257
 	err := row.Scan(
471
 	err := row.Scan(
258
 		&i.ID,
472
 		&i.ID,
259
 		&i.Name,
473
 		&i.Name,
@@ -261,6 +475,12 @@ func (q *Queries) LockRunnerByID(ctx context.Context, db DBTX, id int64) (Workfl
261
 		&i.Capacity,
475
 		&i.Capacity,
262
 		&i.Status,
476
 		&i.Status,
263
 		&i.LastHeartbeatAt,
477
 		&i.LastHeartbeatAt,
478
+		&i.HostName,
479
+		&i.Version,
480
+		&i.DrainingAt,
481
+		&i.DrainReason,
482
+		&i.RevokedAt,
483
+		&i.RevokedReason,
264
 		&i.RegisteredByUserID,
484
 		&i.RegisteredByUserID,
265
 		&i.CreatedAt,
485
 		&i.CreatedAt,
266
 		&i.UpdatedAt,
486
 		&i.UpdatedAt,
@@ -268,6 +488,73 @@ func (q *Queries) LockRunnerByID(ctx context.Context, db DBTX, id int64) (Workfl
268
 	return i, err
488
 	return i, err
269
 }
489
 }
270
 
490
 
491
+const markStaleRunnersOffline = `-- name: MarkStaleRunnersOffline :many
492
+UPDATE workflow_runners
493
+SET status = 'offline',
494
+    updated_at = now()
495
+WHERE revoked_at IS NULL
496
+  AND status <> 'offline'
497
+  AND last_heartbeat_at IS NOT NULL
498
+  AND last_heartbeat_at < $1
499
+RETURNING id, name, labels, capacity, status, last_heartbeat_at,
500
+          host_name, version, draining_at, drain_reason, revoked_at,
501
+          revoked_reason, registered_by_user_id, created_at, updated_at
502
+`
503
+
504
+type MarkStaleRunnersOfflineRow struct {
505
+	ID                 int64
506
+	Name               string
507
+	Labels             []string
508
+	Capacity           int32
509
+	Status             WorkflowRunnerStatus
510
+	LastHeartbeatAt    pgtype.Timestamptz
511
+	HostName           string
512
+	Version            string
513
+	DrainingAt         pgtype.Timestamptz
514
+	DrainReason        string
515
+	RevokedAt          pgtype.Timestamptz
516
+	RevokedReason      string
517
+	RegisteredByUserID pgtype.Int8
518
+	CreatedAt          pgtype.Timestamptz
519
+	UpdatedAt          pgtype.Timestamptz
520
+}
521
+
522
+func (q *Queries) MarkStaleRunnersOffline(ctx context.Context, db DBTX, lastHeartbeatAt pgtype.Timestamptz) ([]MarkStaleRunnersOfflineRow, error) {
523
+	rows, err := db.Query(ctx, markStaleRunnersOffline, lastHeartbeatAt)
524
+	if err != nil {
525
+		return nil, err
526
+	}
527
+	defer rows.Close()
528
+	items := []MarkStaleRunnersOfflineRow{}
529
+	for rows.Next() {
530
+		var i MarkStaleRunnersOfflineRow
531
+		if err := rows.Scan(
532
+			&i.ID,
533
+			&i.Name,
534
+			&i.Labels,
535
+			&i.Capacity,
536
+			&i.Status,
537
+			&i.LastHeartbeatAt,
538
+			&i.HostName,
539
+			&i.Version,
540
+			&i.DrainingAt,
541
+			&i.DrainReason,
542
+			&i.RevokedAt,
543
+			&i.RevokedReason,
544
+			&i.RegisteredByUserID,
545
+			&i.CreatedAt,
546
+			&i.UpdatedAt,
547
+		); err != nil {
548
+			return nil, err
549
+		}
550
+		items = append(items, i)
551
+	}
552
+	if err := rows.Err(); err != nil {
553
+		return nil, err
554
+	}
555
+	return items, nil
556
+}
557
+
271
 const revokeAllTokensForRunner = `-- name: RevokeAllTokensForRunner :exec
558
 const revokeAllTokensForRunner = `-- name: RevokeAllTokensForRunner :exec
272
 UPDATE runner_tokens
559
 UPDATE runner_tokens
273
 SET revoked_at = now()
560
 SET revoked_at = now()
@@ -279,6 +566,127 @@ func (q *Queries) RevokeAllTokensForRunner(ctx context.Context, db DBTX, runnerI
279
 	return err
566
 	return err
280
 }
567
 }
281
 
568
 
569
+const revokeRunner = `-- name: RevokeRunner :one
570
+UPDATE workflow_runners
571
+SET revoked_at = COALESCE(revoked_at, now()),
572
+    revoked_reason = CASE WHEN revoked_at IS NULL THEN $2 ELSE revoked_reason END,
573
+    draining_at = COALESCE(draining_at, now()),
574
+    drain_reason = CASE
575
+        WHEN draining_at IS NULL THEN $2
576
+        ELSE drain_reason
577
+    END,
578
+    status = 'offline',
579
+    updated_at = now()
580
+WHERE id = $1
581
+RETURNING id, name, labels, capacity, status, last_heartbeat_at,
582
+          host_name, version, draining_at, drain_reason, revoked_at,
583
+          revoked_reason, registered_by_user_id, created_at, updated_at
584
+`
585
+
586
+type RevokeRunnerParams struct {
587
+	ID            int64
588
+	RevokedReason string
589
+}
590
+
591
+type RevokeRunnerRow struct {
592
+	ID                 int64
593
+	Name               string
594
+	Labels             []string
595
+	Capacity           int32
596
+	Status             WorkflowRunnerStatus
597
+	LastHeartbeatAt    pgtype.Timestamptz
598
+	HostName           string
599
+	Version            string
600
+	DrainingAt         pgtype.Timestamptz
601
+	DrainReason        string
602
+	RevokedAt          pgtype.Timestamptz
603
+	RevokedReason      string
604
+	RegisteredByUserID pgtype.Int8
605
+	CreatedAt          pgtype.Timestamptz
606
+	UpdatedAt          pgtype.Timestamptz
607
+}
608
+
609
+func (q *Queries) RevokeRunner(ctx context.Context, db DBTX, arg RevokeRunnerParams) (RevokeRunnerRow, error) {
610
+	row := db.QueryRow(ctx, revokeRunner, arg.ID, arg.RevokedReason)
611
+	var i RevokeRunnerRow
612
+	err := row.Scan(
613
+		&i.ID,
614
+		&i.Name,
615
+		&i.Labels,
616
+		&i.Capacity,
617
+		&i.Status,
618
+		&i.LastHeartbeatAt,
619
+		&i.HostName,
620
+		&i.Version,
621
+		&i.DrainingAt,
622
+		&i.DrainReason,
623
+		&i.RevokedAt,
624
+		&i.RevokedReason,
625
+		&i.RegisteredByUserID,
626
+		&i.CreatedAt,
627
+		&i.UpdatedAt,
628
+	)
629
+	return i, err
630
+}
631
+
632
+const setRunnerDraining = `-- name: SetRunnerDraining :one
633
+UPDATE workflow_runners
634
+SET draining_at = COALESCE(draining_at, now()),
635
+    drain_reason = $2,
636
+    updated_at = now()
637
+WHERE id = $1
638
+  AND revoked_at IS NULL
639
+RETURNING id, name, labels, capacity, status, last_heartbeat_at,
640
+          host_name, version, draining_at, drain_reason, revoked_at,
641
+          revoked_reason, registered_by_user_id, created_at, updated_at
642
+`
643
+
644
+type SetRunnerDrainingParams struct {
645
+	ID          int64
646
+	DrainReason string
647
+}
648
+
649
+type SetRunnerDrainingRow struct {
650
+	ID                 int64
651
+	Name               string
652
+	Labels             []string
653
+	Capacity           int32
654
+	Status             WorkflowRunnerStatus
655
+	LastHeartbeatAt    pgtype.Timestamptz
656
+	HostName           string
657
+	Version            string
658
+	DrainingAt         pgtype.Timestamptz
659
+	DrainReason        string
660
+	RevokedAt          pgtype.Timestamptz
661
+	RevokedReason      string
662
+	RegisteredByUserID pgtype.Int8
663
+	CreatedAt          pgtype.Timestamptz
664
+	UpdatedAt          pgtype.Timestamptz
665
+}
666
+
667
+func (q *Queries) SetRunnerDraining(ctx context.Context, db DBTX, arg SetRunnerDrainingParams) (SetRunnerDrainingRow, error) {
668
+	row := db.QueryRow(ctx, setRunnerDraining, arg.ID, arg.DrainReason)
669
+	var i SetRunnerDrainingRow
670
+	err := row.Scan(
671
+		&i.ID,
672
+		&i.Name,
673
+		&i.Labels,
674
+		&i.Capacity,
675
+		&i.Status,
676
+		&i.LastHeartbeatAt,
677
+		&i.HostName,
678
+		&i.Version,
679
+		&i.DrainingAt,
680
+		&i.DrainReason,
681
+		&i.RevokedAt,
682
+		&i.RevokedReason,
683
+		&i.RegisteredByUserID,
684
+		&i.CreatedAt,
685
+		&i.UpdatedAt,
686
+	)
687
+	return i, err
688
+}
689
+
282
 const touchRunnerHeartbeat = `-- name: TouchRunnerHeartbeat :exec
690
 const touchRunnerHeartbeat = `-- name: TouchRunnerHeartbeat :exec
283
 UPDATE workflow_runners
691
 UPDATE workflow_runners
284
 SET last_heartbeat_at = now(),
692
 SET last_heartbeat_at = now(),
internal/admin/sqlc/models.gomodified
@@ -2861,6 +2861,12 @@ type WorkflowRunner struct {
2861
 	RegisteredByUserID pgtype.Int8
2861
 	RegisteredByUserID pgtype.Int8
2862
 	CreatedAt          pgtype.Timestamptz
2862
 	CreatedAt          pgtype.Timestamptz
2863
 	UpdatedAt          pgtype.Timestamptz
2863
 	UpdatedAt          pgtype.Timestamptz
2864
+	HostName           string
2865
+	Version            string
2866
+	DrainingAt         pgtype.Timestamptz
2867
+	DrainReason        string
2868
+	RevokedAt          pgtype.Timestamptz
2869
+	RevokedReason      string
2864
 }
2870
 }
2865
 
2871
 
2866
 type WorkflowSecret struct {
2872
 type WorkflowSecret struct {
internal/auth/policy/sqlc/models.gomodified
@@ -2861,6 +2861,12 @@ type WorkflowRunner struct {
2861
 	RegisteredByUserID pgtype.Int8
2861
 	RegisteredByUserID pgtype.Int8
2862
 	CreatedAt          pgtype.Timestamptz
2862
 	CreatedAt          pgtype.Timestamptz
2863
 	UpdatedAt          pgtype.Timestamptz
2863
 	UpdatedAt          pgtype.Timestamptz
2864
+	HostName           string
2865
+	Version            string
2866
+	DrainingAt         pgtype.Timestamptz
2867
+	DrainReason        string
2868
+	RevokedAt          pgtype.Timestamptz
2869
+	RevokedReason      string
2864
 }
2870
 }
2865
 
2871
 
2866
 type WorkflowSecret struct {
2872
 type WorkflowSecret struct {
internal/billing/sqlc/models.gomodified
@@ -2861,6 +2861,12 @@ type WorkflowRunner struct {
2861
 	RegisteredByUserID pgtype.Int8
2861
 	RegisteredByUserID pgtype.Int8
2862
 	CreatedAt          pgtype.Timestamptz
2862
 	CreatedAt          pgtype.Timestamptz
2863
 	UpdatedAt          pgtype.Timestamptz
2863
 	UpdatedAt          pgtype.Timestamptz
2864
+	HostName           string
2865
+	Version            string
2866
+	DrainingAt         pgtype.Timestamptz
2867
+	DrainReason        string
2868
+	RevokedAt          pgtype.Timestamptz
2869
+	RevokedReason      string
2864
 }
2870
 }
2865
 
2871
 
2866
 type WorkflowSecret struct {
2872
 type WorkflowSecret struct {
internal/checks/sqlc/models.gomodified
@@ -2861,6 +2861,12 @@ type WorkflowRunner struct {
2861
 	RegisteredByUserID pgtype.Int8
2861
 	RegisteredByUserID pgtype.Int8
2862
 	CreatedAt          pgtype.Timestamptz
2862
 	CreatedAt          pgtype.Timestamptz
2863
 	UpdatedAt          pgtype.Timestamptz
2863
 	UpdatedAt          pgtype.Timestamptz
2864
+	HostName           string
2865
+	Version            string
2866
+	DrainingAt         pgtype.Timestamptz
2867
+	DrainReason        string
2868
+	RevokedAt          pgtype.Timestamptz
2869
+	RevokedReason      string
2864
 }
2870
 }
2865
 
2871
 
2866
 type WorkflowSecret struct {
2872
 type WorkflowSecret struct {
internal/issues/sqlc/models.gomodified
@@ -2861,6 +2861,12 @@ type WorkflowRunner struct {
2861
 	RegisteredByUserID pgtype.Int8
2861
 	RegisteredByUserID pgtype.Int8
2862
 	CreatedAt          pgtype.Timestamptz
2862
 	CreatedAt          pgtype.Timestamptz
2863
 	UpdatedAt          pgtype.Timestamptz
2863
 	UpdatedAt          pgtype.Timestamptz
2864
+	HostName           string
2865
+	Version            string
2866
+	DrainingAt         pgtype.Timestamptz
2867
+	DrainReason        string
2868
+	RevokedAt          pgtype.Timestamptz
2869
+	RevokedReason      string
2864
 }
2870
 }
2865
 
2871
 
2866
 type WorkflowSecret struct {
2872
 type WorkflowSecret struct {
internal/meta/sqlc/models.gomodified
@@ -2861,6 +2861,12 @@ type WorkflowRunner struct {
2861
 	RegisteredByUserID pgtype.Int8
2861
 	RegisteredByUserID pgtype.Int8
2862
 	CreatedAt          pgtype.Timestamptz
2862
 	CreatedAt          pgtype.Timestamptz
2863
 	UpdatedAt          pgtype.Timestamptz
2863
 	UpdatedAt          pgtype.Timestamptz
2864
+	HostName           string
2865
+	Version            string
2866
+	DrainingAt         pgtype.Timestamptz
2867
+	DrainReason        string
2868
+	RevokedAt          pgtype.Timestamptz
2869
+	RevokedReason      string
2864
 }
2870
 }
2865
 
2871
 
2866
 type WorkflowSecret struct {
2872
 type WorkflowSecret struct {
internal/migrationsfs/migrations/0067_runner_pool_ops.sqladded
@@ -0,0 +1,50 @@
1
+-- SPDX-License-Identifier: AGPL-3.0-or-later
2
+--
3
+-- S41j runner pool operations: runner metadata plus explicit drain/revoke
4
+-- state. Draining prevents new claims while allowing in-flight jobs to finish.
5
+-- Revocation is a hard operator control: registration tokens no longer
6
+-- authenticate and job API JWTs minted for that runner are rejected.
7
+
8
+-- +goose Up
9
+
10
+ALTER TABLE workflow_runners
11
+    ADD COLUMN host_name text NOT NULL DEFAULT '',
12
+    ADD COLUMN version text NOT NULL DEFAULT '',
13
+    ADD COLUMN draining_at timestamptz,
14
+    ADD COLUMN drain_reason text NOT NULL DEFAULT '',
15
+    ADD COLUMN revoked_at timestamptz,
16
+    ADD COLUMN revoked_reason text NOT NULL DEFAULT '',
17
+    ADD CONSTRAINT workflow_runners_host_name_length CHECK (char_length(host_name) <= 255),
18
+    ADD CONSTRAINT workflow_runners_version_length CHECK (char_length(version) <= 255),
19
+    ADD CONSTRAINT workflow_runners_drain_reason_length CHECK (char_length(drain_reason) <= 1000),
20
+    ADD CONSTRAINT workflow_runners_revoked_reason_length CHECK (char_length(revoked_reason) <= 1000);
21
+
22
+CREATE INDEX workflow_runners_draining_idx
23
+    ON workflow_runners (draining_at)
24
+    WHERE draining_at IS NOT NULL AND revoked_at IS NULL;
25
+
26
+CREATE INDEX workflow_runners_revoked_idx
27
+    ON workflow_runners (revoked_at)
28
+    WHERE revoked_at IS NOT NULL;
29
+
30
+CREATE INDEX workflow_runners_claimable_idx
31
+    ON workflow_runners (status)
32
+    WHERE revoked_at IS NULL AND draining_at IS NULL;
33
+
34
+-- +goose Down
35
+
36
+DROP INDEX IF EXISTS workflow_runners_claimable_idx;
37
+DROP INDEX IF EXISTS workflow_runners_revoked_idx;
38
+DROP INDEX IF EXISTS workflow_runners_draining_idx;
39
+
40
+ALTER TABLE workflow_runners
41
+    DROP CONSTRAINT IF EXISTS workflow_runners_revoked_reason_length,
42
+    DROP CONSTRAINT IF EXISTS workflow_runners_drain_reason_length,
43
+    DROP CONSTRAINT IF EXISTS workflow_runners_version_length,
44
+    DROP CONSTRAINT IF EXISTS workflow_runners_host_name_length,
45
+    DROP COLUMN IF EXISTS revoked_reason,
46
+    DROP COLUMN IF EXISTS revoked_at,
47
+    DROP COLUMN IF EXISTS drain_reason,
48
+    DROP COLUMN IF EXISTS draining_at,
49
+    DROP COLUMN IF EXISTS version,
50
+    DROP COLUMN IF EXISTS host_name;
internal/notif/sqlc/models.gomodified
@@ -2861,6 +2861,12 @@ type WorkflowRunner struct {
2861
 	RegisteredByUserID pgtype.Int8
2861
 	RegisteredByUserID pgtype.Int8
2862
 	CreatedAt          pgtype.Timestamptz
2862
 	CreatedAt          pgtype.Timestamptz
2863
 	UpdatedAt          pgtype.Timestamptz
2863
 	UpdatedAt          pgtype.Timestamptz
2864
+	HostName           string
2865
+	Version            string
2866
+	DrainingAt         pgtype.Timestamptz
2867
+	DrainReason        string
2868
+	RevokedAt          pgtype.Timestamptz
2869
+	RevokedReason      string
2864
 }
2870
 }
2865
 
2871
 
2866
 type WorkflowSecret struct {
2872
 type WorkflowSecret struct {
internal/orgs/sqlc/models.gomodified
@@ -2861,6 +2861,12 @@ type WorkflowRunner struct {
2861
 	RegisteredByUserID pgtype.Int8
2861
 	RegisteredByUserID pgtype.Int8
2862
 	CreatedAt          pgtype.Timestamptz
2862
 	CreatedAt          pgtype.Timestamptz
2863
 	UpdatedAt          pgtype.Timestamptz
2863
 	UpdatedAt          pgtype.Timestamptz
2864
+	HostName           string
2865
+	Version            string
2866
+	DrainingAt         pgtype.Timestamptz
2867
+	DrainReason        string
2868
+	RevokedAt          pgtype.Timestamptz
2869
+	RevokedReason      string
2864
 }
2870
 }
2865
 
2871
 
2866
 type WorkflowSecret struct {
2872
 type WorkflowSecret struct {
internal/pulls/sqlc/models.gomodified
@@ -2861,6 +2861,12 @@ type WorkflowRunner struct {
2861
 	RegisteredByUserID pgtype.Int8
2861
 	RegisteredByUserID pgtype.Int8
2862
 	CreatedAt          pgtype.Timestamptz
2862
 	CreatedAt          pgtype.Timestamptz
2863
 	UpdatedAt          pgtype.Timestamptz
2863
 	UpdatedAt          pgtype.Timestamptz
2864
+	HostName           string
2865
+	Version            string
2866
+	DrainingAt         pgtype.Timestamptz
2867
+	DrainReason        string
2868
+	RevokedAt          pgtype.Timestamptz
2869
+	RevokedReason      string
2864
 }
2870
 }
2865
 
2871
 
2866
 type WorkflowSecret struct {
2872
 type WorkflowSecret struct {
internal/ratelimit/sqlc/models.gomodified
@@ -2861,6 +2861,12 @@ type WorkflowRunner struct {
2861
 	RegisteredByUserID pgtype.Int8
2861
 	RegisteredByUserID pgtype.Int8
2862
 	CreatedAt          pgtype.Timestamptz
2862
 	CreatedAt          pgtype.Timestamptz
2863
 	UpdatedAt          pgtype.Timestamptz
2863
 	UpdatedAt          pgtype.Timestamptz
2864
+	HostName           string
2865
+	Version            string
2866
+	DrainingAt         pgtype.Timestamptz
2867
+	DrainReason        string
2868
+	RevokedAt          pgtype.Timestamptz
2869
+	RevokedReason      string
2864
 }
2870
 }
2865
 
2871
 
2866
 type WorkflowSecret struct {
2872
 type WorkflowSecret struct {
internal/repos/sqlc/models.gomodified
@@ -2861,6 +2861,12 @@ type WorkflowRunner struct {
2861
 	RegisteredByUserID pgtype.Int8
2861
 	RegisteredByUserID pgtype.Int8
2862
 	CreatedAt          pgtype.Timestamptz
2862
 	CreatedAt          pgtype.Timestamptz
2863
 	UpdatedAt          pgtype.Timestamptz
2863
 	UpdatedAt          pgtype.Timestamptz
2864
+	HostName           string
2865
+	Version            string
2866
+	DrainingAt         pgtype.Timestamptz
2867
+	DrainReason        string
2868
+	RevokedAt          pgtype.Timestamptz
2869
+	RevokedReason      string
2864
 }
2870
 }
2865
 
2871
 
2866
 type WorkflowSecret struct {
2872
 type WorkflowSecret struct {
internal/social/sqlc/models.gomodified
@@ -2861,6 +2861,12 @@ type WorkflowRunner struct {
2861
 	RegisteredByUserID pgtype.Int8
2861
 	RegisteredByUserID pgtype.Int8
2862
 	CreatedAt          pgtype.Timestamptz
2862
 	CreatedAt          pgtype.Timestamptz
2863
 	UpdatedAt          pgtype.Timestamptz
2863
 	UpdatedAt          pgtype.Timestamptz
2864
+	HostName           string
2865
+	Version            string
2866
+	DrainingAt         pgtype.Timestamptz
2867
+	DrainReason        string
2868
+	RevokedAt          pgtype.Timestamptz
2869
+	RevokedReason      string
2864
 }
2870
 }
2865
 
2871
 
2866
 type WorkflowSecret struct {
2872
 type WorkflowSecret struct {
internal/users/sqlc/models.gomodified
@@ -2861,6 +2861,12 @@ type WorkflowRunner struct {
2861
 	RegisteredByUserID pgtype.Int8
2861
 	RegisteredByUserID pgtype.Int8
2862
 	CreatedAt          pgtype.Timestamptz
2862
 	CreatedAt          pgtype.Timestamptz
2863
 	UpdatedAt          pgtype.Timestamptz
2863
 	UpdatedAt          pgtype.Timestamptz
2864
+	HostName           string
2865
+	Version            string
2866
+	DrainingAt         pgtype.Timestamptz
2867
+	DrainReason        string
2868
+	RevokedAt          pgtype.Timestamptz
2869
+	RevokedReason      string
2864
 }
2870
 }
2865
 
2871
 
2866
 type WorkflowSecret struct {
2872
 type WorkflowSecret struct {
internal/webhook/sqlc/models.gomodified
@@ -2861,6 +2861,12 @@ type WorkflowRunner struct {
2861
 	RegisteredByUserID pgtype.Int8
2861
 	RegisteredByUserID pgtype.Int8
2862
 	CreatedAt          pgtype.Timestamptz
2862
 	CreatedAt          pgtype.Timestamptz
2863
 	UpdatedAt          pgtype.Timestamptz
2863
 	UpdatedAt          pgtype.Timestamptz
2864
+	HostName           string
2865
+	Version            string
2866
+	DrainingAt         pgtype.Timestamptz
2867
+	DrainReason        string
2868
+	RevokedAt          pgtype.Timestamptz
2869
+	RevokedReason      string
2864
 }
2870
 }
2865
 
2871
 
2866
 type WorkflowSecret struct {
2872
 type WorkflowSecret struct {
internal/worker/sqlc/models.gomodified
@@ -2861,6 +2861,12 @@ type WorkflowRunner struct {
2861
 	RegisteredByUserID pgtype.Int8
2861
 	RegisteredByUserID pgtype.Int8
2862
 	CreatedAt          pgtype.Timestamptz
2862
 	CreatedAt          pgtype.Timestamptz
2863
 	UpdatedAt          pgtype.Timestamptz
2863
 	UpdatedAt          pgtype.Timestamptz
2864
+	HostName           string
2865
+	Version            string
2866
+	DrainingAt         pgtype.Timestamptz
2867
+	DrainReason        string
2868
+	RevokedAt          pgtype.Timestamptz
2869
+	RevokedReason      string
2864
 }
2870
 }
2865
 
2871
 
2866
 type WorkflowSecret struct {
2872
 type WorkflowSecret struct {