aboutsummaryrefslogtreecommitdiffhomepage
path: root/server/models/runner
diff options
context:
space:
mode:
Diffstat (limited to 'server/models/runner')
-rw-r--r--server/models/runner/runner-job.ts357
-rw-r--r--server/models/runner/runner-registration-token.ts103
-rw-r--r--server/models/runner/runner.ts124
3 files changed, 0 insertions, 584 deletions
diff --git a/server/models/runner/runner-job.ts b/server/models/runner/runner-job.ts
deleted file mode 100644
index f2ffd6a84..000000000
--- a/server/models/runner/runner-job.ts
+++ /dev/null
@@ -1,357 +0,0 @@
1import { Op, Transaction } from 'sequelize'
2import {
3 AllowNull,
4 BelongsTo,
5 Column,
6 CreatedAt,
7 DataType,
8 Default,
9 ForeignKey,
10 IsUUID,
11 Model,
12 Scopes,
13 Table,
14 UpdatedAt
15} from 'sequelize-typescript'
16import { isArray, isUUIDValid } from '@server/helpers/custom-validators/misc'
17import { CONSTRAINTS_FIELDS, RUNNER_JOB_STATES } from '@server/initializers/constants'
18import { MRunnerJob, MRunnerJobRunner, MRunnerJobRunnerParent } from '@server/types/models/runners'
19import { RunnerJob, RunnerJobAdmin, RunnerJobPayload, RunnerJobPrivatePayload, RunnerJobState, RunnerJobType } from '@shared/models'
20import { AttributesOnly } from '@shared/typescript-utils'
21import { getSort, searchAttribute } from '../shared'
22import { RunnerModel } from './runner'
23
24enum ScopeNames {
25 WITH_RUNNER = 'WITH_RUNNER',
26 WITH_PARENT = 'WITH_PARENT'
27}
28
29@Scopes(() => ({
30 [ScopeNames.WITH_RUNNER]: {
31 include: [
32 {
33 model: RunnerModel.unscoped(),
34 required: false
35 }
36 ]
37 },
38 [ScopeNames.WITH_PARENT]: {
39 include: [
40 {
41 model: RunnerJobModel.unscoped(),
42 required: false
43 }
44 ]
45 }
46}))
47@Table({
48 tableName: 'runnerJob',
49 indexes: [
50 {
51 fields: [ 'uuid' ],
52 unique: true
53 },
54 {
55 fields: [ 'processingJobToken' ],
56 unique: true
57 },
58 {
59 fields: [ 'runnerId' ]
60 }
61 ]
62})
63export class RunnerJobModel extends Model<Partial<AttributesOnly<RunnerJobModel>>> {
64
65 @AllowNull(false)
66 @IsUUID(4)
67 @Column(DataType.UUID)
68 uuid: string
69
70 @AllowNull(false)
71 @Column
72 type: RunnerJobType
73
74 @AllowNull(false)
75 @Column(DataType.JSONB)
76 payload: RunnerJobPayload
77
78 @AllowNull(false)
79 @Column(DataType.JSONB)
80 privatePayload: RunnerJobPrivatePayload
81
82 @AllowNull(false)
83 @Column
84 state: RunnerJobState
85
86 @AllowNull(false)
87 @Default(0)
88 @Column
89 failures: number
90
91 @AllowNull(true)
92 @Column(DataType.STRING(CONSTRAINTS_FIELDS.RUNNER_JOBS.ERROR_MESSAGE.max))
93 error: string
94
95 // Less has priority
96 @AllowNull(false)
97 @Column
98 priority: number
99
100 // Used to fetch the appropriate job when the runner wants to post the result
101 @AllowNull(true)
102 @Column
103 processingJobToken: string
104
105 @AllowNull(true)
106 @Column
107 progress: number
108
109 @AllowNull(true)
110 @Column
111 startedAt: Date
112
113 @AllowNull(true)
114 @Column
115 finishedAt: Date
116
117 @CreatedAt
118 createdAt: Date
119
120 @UpdatedAt
121 updatedAt: Date
122
123 @ForeignKey(() => RunnerJobModel)
124 @Column
125 dependsOnRunnerJobId: number
126
127 @BelongsTo(() => RunnerJobModel, {
128 foreignKey: {
129 name: 'dependsOnRunnerJobId',
130 allowNull: true
131 },
132 onDelete: 'cascade'
133 })
134 DependsOnRunnerJob: RunnerJobModel
135
136 @ForeignKey(() => RunnerModel)
137 @Column
138 runnerId: number
139
140 @BelongsTo(() => RunnerModel, {
141 foreignKey: {
142 name: 'runnerId',
143 allowNull: true
144 },
145 onDelete: 'SET NULL'
146 })
147 Runner: RunnerModel
148
149 // ---------------------------------------------------------------------------
150
151 static loadWithRunner (uuid: string) {
152 const query = {
153 where: { uuid }
154 }
155
156 return RunnerJobModel.scope(ScopeNames.WITH_RUNNER).findOne<MRunnerJobRunner>(query)
157 }
158
159 static loadByRunnerAndJobTokensWithRunner (options: {
160 uuid: string
161 runnerToken: string
162 jobToken: string
163 }) {
164 const { uuid, runnerToken, jobToken } = options
165
166 const query = {
167 where: {
168 uuid,
169 processingJobToken: jobToken
170 },
171 include: {
172 model: RunnerModel.unscoped(),
173 required: true,
174 where: {
175 runnerToken
176 }
177 }
178 }
179
180 return RunnerJobModel.findOne<MRunnerJobRunner>(query)
181 }
182
183 static listAvailableJobs () {
184 const query = {
185 limit: 10,
186 order: getSort('priority'),
187 where: {
188 state: RunnerJobState.PENDING
189 }
190 }
191
192 return RunnerJobModel.findAll<MRunnerJob>(query)
193 }
194
195 static listStalledJobs (options: {
196 staleTimeMS: number
197 types: RunnerJobType[]
198 }) {
199 const before = new Date(Date.now() - options.staleTimeMS)
200
201 return RunnerJobModel.findAll<MRunnerJob>({
202 where: {
203 type: {
204 [Op.in]: options.types
205 },
206 state: RunnerJobState.PROCESSING,
207 updatedAt: {
208 [Op.lt]: before
209 }
210 }
211 })
212 }
213
214 static listChildrenOf (job: MRunnerJob, transaction?: Transaction) {
215 const query = {
216 where: {
217 dependsOnRunnerJobId: job.id
218 },
219 transaction
220 }
221
222 return RunnerJobModel.findAll<MRunnerJob>(query)
223 }
224
225 static listForApi (options: {
226 start: number
227 count: number
228 sort: string
229 search?: string
230 stateOneOf?: RunnerJobState[]
231 }) {
232 const { start, count, sort, search, stateOneOf } = options
233
234 const query = {
235 offset: start,
236 limit: count,
237 order: getSort(sort),
238 where: []
239 }
240
241 if (search) {
242 if (isUUIDValid(search)) {
243 query.where.push({ uuid: search })
244 } else {
245 query.where.push({
246 [Op.or]: [
247 searchAttribute(search, 'type'),
248 searchAttribute(search, '$Runner.name$')
249 ]
250 })
251 }
252 }
253
254 if (isArray(stateOneOf) && stateOneOf.length !== 0) {
255 query.where.push({
256 state: {
257 [Op.in]: stateOneOf
258 }
259 })
260 }
261
262 return Promise.all([
263 RunnerJobModel.scope([ ScopeNames.WITH_RUNNER ]).count(query),
264 RunnerJobModel.scope([ ScopeNames.WITH_RUNNER, ScopeNames.WITH_PARENT ]).findAll<MRunnerJobRunnerParent>(query)
265 ]).then(([ total, data ]) => ({ total, data }))
266 }
267
268 static updateDependantJobsOf (runnerJob: MRunnerJob) {
269 const where = {
270 dependsOnRunnerJobId: runnerJob.id
271 }
272
273 return RunnerJobModel.update({ state: RunnerJobState.PENDING }, { where })
274 }
275
276 static cancelAllJobs (options: { type: RunnerJobType }) {
277 const where = {
278 type: options.type
279 }
280
281 return RunnerJobModel.update({ state: RunnerJobState.CANCELLED }, { where })
282 }
283
284 // ---------------------------------------------------------------------------
285
286 resetToPending () {
287 this.state = RunnerJobState.PENDING
288 this.processingJobToken = null
289 this.progress = null
290 this.startedAt = null
291 this.runnerId = null
292 }
293
294 setToErrorOrCancel (
295 state: RunnerJobState.PARENT_ERRORED | RunnerJobState.ERRORED | RunnerJobState.CANCELLED | RunnerJobState.PARENT_CANCELLED
296 ) {
297 this.state = state
298 this.processingJobToken = null
299 this.finishedAt = new Date()
300 }
301
302 toFormattedJSON (this: MRunnerJobRunnerParent): RunnerJob {
303 const runner = this.Runner
304 ? {
305 id: this.Runner.id,
306 name: this.Runner.name,
307 description: this.Runner.description
308 }
309 : null
310
311 const parent = this.DependsOnRunnerJob
312 ? {
313 id: this.DependsOnRunnerJob.id,
314 uuid: this.DependsOnRunnerJob.uuid,
315 type: this.DependsOnRunnerJob.type,
316 state: {
317 id: this.DependsOnRunnerJob.state,
318 label: RUNNER_JOB_STATES[this.DependsOnRunnerJob.state]
319 }
320 }
321 : undefined
322
323 return {
324 uuid: this.uuid,
325 type: this.type,
326
327 state: {
328 id: this.state,
329 label: RUNNER_JOB_STATES[this.state]
330 },
331
332 progress: this.progress,
333 priority: this.priority,
334 failures: this.failures,
335 error: this.error,
336
337 payload: this.payload,
338
339 startedAt: this.startedAt?.toISOString(),
340 finishedAt: this.finishedAt?.toISOString(),
341
342 createdAt: this.createdAt.toISOString(),
343 updatedAt: this.updatedAt.toISOString(),
344
345 parent,
346 runner
347 }
348 }
349
350 toFormattedAdminJSON (this: MRunnerJobRunnerParent): RunnerJobAdmin {
351 return {
352 ...this.toFormattedJSON(),
353
354 privatePayload: this.privatePayload
355 }
356 }
357}
diff --git a/server/models/runner/runner-registration-token.ts b/server/models/runner/runner-registration-token.ts
deleted file mode 100644
index b2ae6c9eb..000000000
--- a/server/models/runner/runner-registration-token.ts
+++ /dev/null
@@ -1,103 +0,0 @@
1import { FindOptions, literal } from 'sequelize'
2import { AllowNull, Column, CreatedAt, HasMany, Model, Table, UpdatedAt } from 'sequelize-typescript'
3import { MRunnerRegistrationToken } from '@server/types/models/runners'
4import { RunnerRegistrationToken } from '@shared/models'
5import { AttributesOnly } from '@shared/typescript-utils'
6import { getSort } from '../shared'
7import { RunnerModel } from './runner'
8
9/**
10 *
11 * Tokens used by PeerTube runners to register themselves to the PeerTube instance
12 *
13 */
14
15@Table({
16 tableName: 'runnerRegistrationToken',
17 indexes: [
18 {
19 fields: [ 'registrationToken' ],
20 unique: true
21 }
22 ]
23})
24export class RunnerRegistrationTokenModel extends Model<Partial<AttributesOnly<RunnerRegistrationTokenModel>>> {
25
26 @AllowNull(false)
27 @Column
28 registrationToken: string
29
30 @CreatedAt
31 createdAt: Date
32
33 @UpdatedAt
34 updatedAt: Date
35
36 @HasMany(() => RunnerModel, {
37 foreignKey: {
38 allowNull: true
39 },
40 onDelete: 'cascade'
41 })
42 Runners: RunnerModel[]
43
44 static load (id: number) {
45 return RunnerRegistrationTokenModel.findByPk(id)
46 }
47
48 static loadByRegistrationToken (registrationToken: string) {
49 const query = {
50 where: { registrationToken }
51 }
52
53 return RunnerRegistrationTokenModel.findOne(query)
54 }
55
56 static countTotal () {
57 return RunnerRegistrationTokenModel.unscoped().count()
58 }
59
60 static listForApi (options: {
61 start: number
62 count: number
63 sort: string
64 }) {
65 const { start, count, sort } = options
66
67 const query: FindOptions = {
68 attributes: {
69 include: [
70 [
71 literal('(SELECT COUNT(*) FROM "runner" WHERE "runner"."runnerRegistrationTokenId" = "RunnerRegistrationTokenModel"."id")'),
72 'registeredRunnersCount'
73 ]
74 ]
75 },
76 offset: start,
77 limit: count,
78 order: getSort(sort)
79 }
80
81 return Promise.all([
82 RunnerRegistrationTokenModel.count(query),
83 RunnerRegistrationTokenModel.findAll<MRunnerRegistrationToken>(query)
84 ]).then(([ total, data ]) => ({ total, data }))
85 }
86
87 // ---------------------------------------------------------------------------
88
89 toFormattedJSON (this: MRunnerRegistrationToken): RunnerRegistrationToken {
90 const registeredRunnersCount = this.get('registeredRunnersCount') as number
91
92 return {
93 id: this.id,
94
95 registrationToken: this.registrationToken,
96
97 createdAt: this.createdAt,
98 updatedAt: this.updatedAt,
99
100 registeredRunnersCount
101 }
102 }
103}
diff --git a/server/models/runner/runner.ts b/server/models/runner/runner.ts
deleted file mode 100644
index 4d07707d8..000000000
--- a/server/models/runner/runner.ts
+++ /dev/null
@@ -1,124 +0,0 @@
1import { FindOptions } from 'sequelize'
2import { AllowNull, BelongsTo, Column, CreatedAt, DataType, ForeignKey, Model, Table, UpdatedAt } from 'sequelize-typescript'
3import { MRunner } from '@server/types/models/runners'
4import { Runner } from '@shared/models'
5import { AttributesOnly } from '@shared/typescript-utils'
6import { getSort } from '../shared'
7import { RunnerRegistrationTokenModel } from './runner-registration-token'
8import { CONSTRAINTS_FIELDS } from '@server/initializers/constants'
9
10@Table({
11 tableName: 'runner',
12 indexes: [
13 {
14 fields: [ 'runnerToken' ],
15 unique: true
16 },
17 {
18 fields: [ 'runnerRegistrationTokenId' ]
19 },
20 {
21 fields: [ 'name' ],
22 unique: true
23 }
24 ]
25})
26export class RunnerModel extends Model<Partial<AttributesOnly<RunnerModel>>> {
27
28 // Used to identify the appropriate runner when it uses the runner REST API
29 @AllowNull(false)
30 @Column
31 runnerToken: string
32
33 @AllowNull(false)
34 @Column
35 name: string
36
37 @AllowNull(true)
38 @Column(DataType.STRING(CONSTRAINTS_FIELDS.RUNNERS.DESCRIPTION.max))
39 description: string
40
41 @AllowNull(false)
42 @Column
43 lastContact: Date
44
45 @AllowNull(false)
46 @Column
47 ip: string
48
49 @CreatedAt
50 createdAt: Date
51
52 @UpdatedAt
53 updatedAt: Date
54
55 @ForeignKey(() => RunnerRegistrationTokenModel)
56 @Column
57 runnerRegistrationTokenId: number
58
59 @BelongsTo(() => RunnerRegistrationTokenModel, {
60 foreignKey: {
61 allowNull: false
62 },
63 onDelete: 'cascade'
64 })
65 RunnerRegistrationToken: RunnerRegistrationTokenModel
66
67 // ---------------------------------------------------------------------------
68
69 static load (id: number) {
70 return RunnerModel.findByPk(id)
71 }
72
73 static loadByToken (runnerToken: string) {
74 const query = {
75 where: { runnerToken }
76 }
77
78 return RunnerModel.findOne(query)
79 }
80
81 static loadByName (name: string) {
82 const query = {
83 where: { name }
84 }
85
86 return RunnerModel.findOne(query)
87 }
88
89 static listForApi (options: {
90 start: number
91 count: number
92 sort: string
93 }) {
94 const { start, count, sort } = options
95
96 const query: FindOptions = {
97 offset: start,
98 limit: count,
99 order: getSort(sort)
100 }
101
102 return Promise.all([
103 RunnerModel.count(query),
104 RunnerModel.findAll<MRunner>(query)
105 ]).then(([ total, data ]) => ({ total, data }))
106 }
107
108 // ---------------------------------------------------------------------------
109
110 toFormattedJSON (this: MRunner): Runner {
111 return {
112 id: this.id,
113
114 name: this.name,
115 description: this.description,
116
117 ip: this.ip,
118 lastContact: this.lastContact,
119
120 createdAt: this.createdAt,
121 updatedAt: this.updatedAt
122 }
123 }
124}