一定サイクルで実行するジョブを作成したとき、
ジョブの実行がサイクルの時間内に収まらなかった場合は、
次の実行がビルドキューに追加される。(※)
(※) ジョブの設定で「可能であれば並行してビルド」を「OFF」にしている場合。
「ON」にしていると、Jenkins全体のジョブ並行実行数の制限に達していない限り、
キューには追加されず、即座に実行されてしまう。)
これは、ジョブの実行時間に対して、
指定されたサイクルが短すぎるということなので、
サイクルを長くするなり、ジョブの実行時間をチューニングするなりすべきで、
一般的には良くない状態である。はず。
ではどうすれば、サイクルに収まらず、
次の実行がビルドキューに追加されてしまったジョブを、
プログラマティックに検出できるかという話。
Jenkinsには「リモートアクセスAPI」という便利なものが用意されていて、
以下のURLを呼び出すことで、ビルドキューの状態がjsonで取得できる。
http://[host]:[port]/queue/api/json
json
{
items: [
{
actions: [
{
causes: [
{
shortDescription: "ユーザーkei2100が実行",
userId: "kei2100",
userName: "kei2100"
}
]
}
],
blocked: true,
buildable: false,
id: 564,
params: "",
stuck: false,
task: {
name: "test_job",
url: "http://[host]:[port]/job/test_job/",
color: "blue_anime"
},
why: "ビルド #20 は既に実行中です。 (予定時間:21 秒)",
buildableStartMilliseconds: 1340090312065
}
]
}
このjsonで、「blocked: true」になっているものが「次の実行がビルドキューに追加されている」状態にあたるので、
参考 : http://javadoc.jenkins-ci.org/hudson/model/Queue.Item.html
これを検出してあげれば良い。
例えば以下のような感じ。
#!/usr/bin/env ruby
require 'json'
require 'net/http'
require 'optparse'
def get_job_infos_with_json(host, port)
uri = '/queue/api/json'
http = Net::HTTP.new(host, port)
http.open_timeout = 30
http.read_timeout = 30
begin
http.start do
response = http.get(uri)
if response.instance_of? Net::HTTPOK
return response.body
else
raise "http://#{host}:#{port}#{uri} returns #{response.code}"
end
end
rescue => e
raise "http://#{host}:#{port}#{uri} throws exception.\n caused by #{e.message}"
end
end
def duplicate_job_start?(job_info)
if job_info['blocked']
return true
else
return false
end
end
host = nil
port = nil
# コマンドラインパラメータの取得
opt = OptionParser.new do | opt |
opt.on('-h HOST') {| val | host = val}
opt.on('-p PORT') {| val | port = val}
opt.parse(ARGV)
end
if (!host || !port)
puts opt.help
exit 1
end
# Jenkisのジョブキュー情報を取得
job_infos_json = get_job_infos_with_json(host, port)
job_infos = JSON.parse(job_infos_json)
# 前回の実行が終わっていないジョブを検出
duplicate_jobs = []
job_infos['items'].each do | job_info |
if duplicate_job_start?(job_info)
duplicate_jobs << job_info
end
end
if duplicate_jobs.size > 0
puts "Previous job has not finished yet..."
duplicate_jobs.each do | job_info |
puts "job name : #{job_info['task']['name']}"
end
exit 1
else
exit 0
end