require 'thor'
require 'debci'
require 'debci/amqp'
require 'debci/job'
module Debci
class Drain
class LifeCycle
def initialize(life_cycle = 10)
@mutex = Mutex.new
@timestamp = Time.now
@life_cycle = life_cycle
@expired = false
end
def retain
@mutex.synchronize do
@timestamp = Time.now
end
end
def expire!
@mutex.synchronize do
@expired = true
end
end
def expired?
@mutex.synchronize do
@expired || (Time.now - @timestamp > @life_cycle)
end
end
end
def run
life_cycle = LifeCycle.new
seen = {}
channel = Debci::AMQP.amqp_channel
queues.each do |queue|
queue.subscribe(manual_ack: true) do |delivery_info, _properties, payload|
job = get_job(payload)
next unless job
if discard_job?(job)
channel.acknowledge(delivery_info.delivery_tag, false)
life_cycle.retain
elsif seen.key?(job.run_id)
life_cycle.expire!
else
channel.reject(delivery_info.delivery_tag, true)
seen[job.run_id] = true
end
end
end
sleep 1 until life_cycle.expired?
end
def queues
combinations = Debci.config.arch_list.map do |arch|
Debci.config.backend_list.map do |backend|
[arch, backend]
end
end.flatten(1)
existing = combinations.select do |arch, backend|
Debci::AMQP.queue_exists?(arch, backend)
end
existing.map do |arch, backend|
Debci::AMQP.get_queue(arch, backend)
end
end
def get_job(payload)
run_id_param = payload.split.find { |part| part =~ /^run-id:/ }
return nil unless run_id_param
run_id = run_id_param.split(":").last.to_i
return nil unless run_id
Debci::Job.includes(:package).find(run_id)
end
def discard_job?(job)
if job.status.nil?
return false
end
Debci.log("DROP ##{job.run_id} (#{job.package.name} #{job.suite}/#{job.arch}/#{job.backend})")
true
end
class CLI < Thor
desc 'run', 'Drains cancelled jobs from the queue'
def start
Drain.new.run
end
default_task :start
end
end
end