Avoid duplicate runner names

This commit is contained in:
Chocobozzz 2023-07-12 10:53:46 +02:00
parent 88cde4392a
commit d959b763f0
No known key found for this signature in database
GPG Key ID: 583A612D890159BE
6 changed files with 57 additions and 2 deletions

View File

@ -27,7 +27,7 @@ import { CONFIG, registerConfigChangedHandler } from './config'
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
const LAST_MIGRATION_VERSION = 790 const LAST_MIGRATION_VERSION = 795
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------

View File

@ -0,0 +1,24 @@
import * as Sequelize from 'sequelize'
async function up (utils: {
transaction: Sequelize.Transaction
queryInterface: Sequelize.QueryInterface
sequelize: Sequelize.Sequelize
}): Promise<void> {
const { transaction } = utils
const query = 'DELETE FROM "runner" r1 ' +
'USING (SELECT MIN(id) as id, "name" FROM "runner" GROUP BY "name" HAVING COUNT(*) > 1) r2 ' +
'WHERE r1."name" = r2."name" AND r1.id <> r2.id'
await utils.sequelize.query(query, { transaction })
}
function down (options) {
throw new Error('Not implemented.')
}
export {
up,
down
}

View File

@ -35,6 +35,15 @@ const registerRunnerValidator = [
}) })
} }
const existing = await RunnerModel.loadByName(body.name)
if (existing) {
return res.fail({
status: HttpStatusCode.BAD_REQUEST_400,
message: 'This runner name already exists on this instance',
tags
})
}
res.locals.runnerRegistrationToken = runnerRegistrationToken res.locals.runnerRegistrationToken = runnerRegistrationToken
return next() return next()

View File

@ -16,6 +16,10 @@ import { CONSTRAINTS_FIELDS } from '@server/initializers/constants'
}, },
{ {
fields: [ 'runnerRegistrationTokenId' ] fields: [ 'runnerRegistrationTokenId' ]
},
{
fields: [ 'name' ],
unique: true
} }
] ]
}) })
@ -74,6 +78,14 @@ export class RunnerModel extends Model<Partial<AttributesOnly<RunnerModel>>> {
return RunnerModel.findOne(query) return RunnerModel.findOne(query)
} }
static loadByName (name: string) {
const query = {
where: { name }
}
return RunnerModel.findOne(query)
}
static listForApi (options: { static listForApi (options: {
start: number start: number
count: number count: number

View File

@ -177,6 +177,15 @@ describe('Test managing runners', function () {
toDeleteId = id toDeleteId = id
}) })
it('Should fail with the same runner name', async function () {
await server.runners.register({
name,
description: 'super description',
registrationToken,
expectedStatus: HttpStatusCode.BAD_REQUEST_400
})
})
}) })
describe('Delete', function () { describe('Delete', function () {

View File

@ -1,4 +1,5 @@
import { pick } from '@shared/core-utils' import { pick } from '@shared/core-utils'
import { buildUUID } from '@shared/extra-utils'
import { HttpStatusCode, RegisterRunnerBody, RegisterRunnerResult, ResultList, Runner, UnregisterRunnerBody } from '@shared/models' import { HttpStatusCode, RegisterRunnerBody, RegisterRunnerResult, ResultList, Runner, UnregisterRunnerBody } from '@shared/models'
import { unwrapBody } from '../requests' import { unwrapBody } from '../requests'
import { AbstractCommand, OverrideCommandOptions } from '../shared' import { AbstractCommand, OverrideCommandOptions } from '../shared'
@ -68,7 +69,7 @@ export class RunnersCommand extends AbstractCommand {
const { data } = await this.server.runnerRegistrationTokens.list({ sort: 'createdAt' }) const { data } = await this.server.runnerRegistrationTokens.list({ sort: 'createdAt' })
const { runnerToken } = await this.register({ const { runnerToken } = await this.register({
name: 'runner', name: 'runner ' + buildUUID(),
registrationToken: data[0].registrationToken registrationToken: data[0].registrationToken
}) })