From b3e26e5ffe726144c8853f381dd076c7c0ba68e3 Mon Sep 17 00:00:00 2001 From: Chewing_Bever Date: Tue, 13 Dec 2022 22:03:04 +0100 Subject: [PATCH] feat: queue one-time builds from CLI --- src/build/queue.v | 40 +++++++++++++++---------- src/client/jobs.v | 11 +++++++ src/console/targets/targets.v | 29 +++++++++++++++++- src/server/{api_builds.v => api_jobs.v} | 8 ++++- 4 files changed, 70 insertions(+), 18 deletions(-) rename src/server/{api_builds.v => api_jobs.v} (83%) diff --git a/src/build/queue.v b/src/build/queue.v index 1395a0b..5d50f34 100644 --- a/src/build/queue.v +++ b/src/build/queue.v @@ -7,7 +7,7 @@ import datatypes { MinHeap } import util struct BuildJob { -pub: +pub mut: // Time at which this build job was created/queued created time.Time // Next timestamp from which point this job is allowed to be executed @@ -64,6 +64,8 @@ pub struct InsertConfig { target Target [required] arch string [required] single bool + force bool + now bool } // 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{} } - 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 - } - - timestamp := ce.next_from_now()! - - job := BuildJob{ + mut job := BuildJob{ created: time.now() - timestamp: timestamp - ce: ce single: input.single config: BuildConfig{ target_id: input.target.id @@ -98,9 +88,25 @@ pub fn (mut q BuildJobQueue) insert(input InsertConfig) ! { repo: input.target.repo // TODO make this configurable 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) } } @@ -198,8 +204,10 @@ pub fn (mut q BuildJobQueue) pop_n(arch string, n int) []BuildJob { if job.timestamp < time.now() { job = q.queues[arch].pop() or { break } - // TODO idem - q.reschedule(job, arch) or {} + if !job.single { + // TODO idem + q.reschedule(job, arch) or {} + } out << job } else { diff --git a/src/client/jobs.v b/src/client/jobs.v index 7fee94f..2d8e99b 100644 --- a/src/client/jobs.v +++ b/src/client/jobs.v @@ -1,6 +1,7 @@ module client import build { BuildConfig } +import web.response { Response } // poll_jobs requests a list of new build jobs from the server. 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 } + +pub fn (c &Client) queue_job(target_id int, arch string, force bool) !Response { + data := c.send_request(.post, '/api/v1/jobs/queue', { + 'target': target_id.str() + 'arch': arch + 'force': force.str() + })! + + return data +} diff --git a/src/console/targets/targets.v b/src/console/targets/targets.v index ffcd36c..bf2051f 100644 --- a/src/console/targets/targets.v +++ b/src/console/targets/targets.v @@ -188,12 +188,39 @@ pub fn cmd() cli.Command { description: 'Build the target without checking whether it needs to be renewed.' 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) ! { config_file := cmd.flags.get_string('config-file')! conf := vconf.load(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 == '' { + println('When scheduling the build remotely, you have to specify an architecture.') + exit(1) + } + + 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)! + } } }, ] diff --git a/src/server/api_builds.v b/src/server/api_jobs.v similarity index 83% rename from src/server/api_builds.v rename to src/server/api_jobs.v index bc841ce..b75e70e 100644 --- a/src/server/api_builds.v +++ b/src/server/api_jobs.v @@ -30,11 +30,17 @@ fn (mut app App) v1_queue_job() web.Result { 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 { 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) }