feat: queue one-time builds from CLI

Jef Roosens 2022-12-13 22:03:04 +01:00
parent 6a208dbe6c
commit 2cc3e8404e
Signed by untrusted user: Jef Roosens
GPG Key ID: B75D4F293C7052DB
4 changed files with 69 additions and 18 deletions

View File

@ -7,7 +7,7 @@ import datatypes { MinHeap }
import util import util
struct BuildJob { struct BuildJob {
pub: pub mut:
// Time at which this build job was created/queued // Time at which this build job was created/queued
created time.Time created time.Time
// Next timestamp from which point this job is allowed to be executed // Next timestamp from which point this job is allowed to be executed
@ -64,6 +64,8 @@ pub struct InsertConfig {
target Target [required] target Target [required]
arch string [required] arch string [required]
single bool single bool
force bool
now bool
} }
// insert a new target's job into the queue for the given architecture. This // insert a new target's job into the queue for the given architecture. This
@ -75,20 +77,8 @@ pub fn (mut q BuildJobQueue) insert(input InsertConfig) ! {
q.queues[input.arch] = MinHeap<BuildJob>{} q.queues[input.arch] = MinHeap<BuildJob>{}
} }
ce := if input.target.schedule != '' { mut job := BuildJob{
parse_expression(input.target.schedule) or {
return error("Error while parsing cron expression '$input.target.schedule' (id $input.target.id): $err.msg()")
}
} else {
q.default_schedule
}
timestamp := ce.next_from_now()!
job := BuildJob{
created: time.now() created: time.now()
timestamp: timestamp
ce: ce
single: input.single single: input.single
config: BuildConfig{ config: BuildConfig{
target_id: input.target.id target_id: input.target.id
@ -98,9 +88,25 @@ pub fn (mut q BuildJobQueue) insert(input InsertConfig) ! {
repo: input.target.repo repo: input.target.repo
// TODO make this configurable // TODO make this configurable
base_image: q.default_base_image base_image: q.default_base_image
force: input.force
} }
} }
if !input.now {
ce := if input.target.schedule != '' {
parse_expression(input.target.schedule) or {
return error("Error while parsing cron expression '$input.target.schedule' (id $input.target.id): $err.msg()")
}
} else {
q.default_schedule
}
job.timestamp = ce.next_from_now()!
job.ce = ce
} else {
job.timestamp = time.now()
}
q.queues[input.arch].insert(job) q.queues[input.arch].insert(job)
} }
} }
@ -198,8 +204,10 @@ pub fn (mut q BuildJobQueue) pop_n(arch string, n int) []BuildJob {
if job.timestamp < time.now() { if job.timestamp < time.now() {
job = q.queues[arch].pop() or { break } job = q.queues[arch].pop() or { break }
if !job.single {
// TODO idem // TODO idem
q.reschedule(job, arch) or {} q.reschedule(job, arch) or {}
}
out << job out << job
} else { } else {

View File

@ -1,6 +1,7 @@
module client module client
import build { BuildConfig } import build { BuildConfig }
import web.response { Response }
// poll_jobs requests a list of new build jobs from the server. // poll_jobs requests a list of new build jobs from the server.
pub fn (c &Client) poll_jobs(arch string, max int) ![]BuildConfig { pub fn (c &Client) poll_jobs(arch string, max int) ![]BuildConfig {
@ -11,3 +12,13 @@ pub fn (c &Client) poll_jobs(arch string, max int) ![]BuildConfig {
return data.data return data.data
} }
pub fn (c &Client) queue_job(target_id int, arch string, force bool) !Response<string> {
data := c.send_request<string>(.post, '/api/v1/jobs/queue', {
'target': target_id.str()
'arch': arch
'force': force.str()
})!
return data
}

View File

@ -188,12 +188,38 @@ pub fn cmd() cli.Command {
description: 'Build the target without checking whether it needs to be renewed.' description: 'Build the target without checking whether it needs to be renewed.'
flag: cli.FlagType.bool flag: cli.FlagType.bool
}, },
cli.Flag{
name: 'remote'
description: 'Schedule the build on the server instead of running it locally.'
flag: cli.FlagType.bool
},
cli.Flag{
name: 'arch'
description: 'Architecture to schedule build for. Required when using -remote.'
flag: cli.FlagType.string
},
] ]
execute: fn (cmd cli.Command) ! { execute: fn (cmd cli.Command) ! {
config_file := cmd.flags.get_string('config-file')! config_file := cmd.flags.get_string('config-file')!
conf := vconf.load<Config>(prefix: 'VIETER_', default_path: config_file)! conf := vconf.load<Config>(prefix: 'VIETER_', default_path: config_file)!
build(conf, cmd.args[0].int(), cmd.flags.get_bool('force')!)! remote := cmd.flags.get_bool('remote')!
force := cmd.flags.get_bool('force')!
target_id := cmd.args[0].int()
if remote {
arch := cmd.flags.get_string('arch')!
if arch == '' {
return error('When scheduling the build remotely, you have to specify an architecture.')
}
c := client.new(conf.address, conf.api_key)
res := c.queue_job(target_id, arch, force)!
println(res.message)
} else {
build(conf, target_id, force)!
}
} }
}, },
] ]

View File

@ -30,11 +30,17 @@ fn (mut app App) v1_queue_job() web.Result {
return app.json(.bad_request, new_response('Missing arch query arg.')) return app.json(.bad_request, new_response('Missing arch query arg.'))
} }
if arch == '' {
app.json(.bad_request, new_response('Empty arch query arg.'))
}
force := 'force' in app.query
target := app.db.get_target(target_id) or { target := app.db.get_target(target_id) or {
return app.json(.bad_request, new_response('Unknown target id.')) return app.json(.bad_request, new_response('Unknown target id.'))
} }
app.job_queue.insert(target: target, arch: arch, single: true) or { app.job_queue.insert(target: target, arch: arch, single: true, now: true, force: force) or {
return app.status(.internal_server_error) return app.status(.internal_server_error)
} }