2012/08/10

過去の番組チケットと録画ジョブの後処理

jenkinsとrest-clientとjsonとhudson-remote-apiの組み合わせ問題

日々過去のものとなる番組チケットクローズと録画ジョブ削除を行う処理の中での話。
結構前から構築して動かしていたけれど、色々試行錯誤していて落ち着かなかった。すっきりはしてないけれど妥協点として一度書き記しておこうかと。

チケットのクローズ
これはシンプルに、開始日時が今日以前の番組チケットをクローズするっていうだけの処理。削除することもやってみたけれど、削除は負荷が高く、データベース的にもよろしくないらしいのでクローズフラグだけにしている。

close.rb

#!/usr/bin/ruby
# -*- coding: utf-8 -*-
#
require 'active_record'
require 'active_support/core_ext'

ActiveRecord::Base.establish_connection(
        :adapter => 'mysql2',
        :host => 'localhost',
        :username => 'redmine',
        :password => 'redmine',
        :database => 'redmine'
)

class Issue < ActiveRecord::Base
end

# 昨日までの開いてる'番組'チケットをクローズ(終了)する
Issue.find(:all, :conditions => ["tracker_id=1 and start_date<?", Date.today]).each {|i|
  i.status_id = 3
  i.save
}


Jenkinsジョブの削除
録画ジョブはワンショットなので、実行済みのジョブは不要だ。実行ログ確認のため数日間は残して、それ以降の古い録画ジョブを削除したい。これを行うのに結構試行錯誤してしまった。

過去の録画ジョブは成功失敗にかかわらず、一度はビルドされているはず。そのビルド日時を調べて数日前に行われたならば削除するようにしたかった。

hudson-remote-apiでビルドタイムスタンプへアクセス出来ない
これ便利なので全部これでやりたかったんだけれど、ビルド番号は取得できるけれど、ビルドのタイムスタンプへのアクセスができそうにない。

じゃあ使うのを諦めて、rest_clientで全部処理しようかと考えたが・・

/job/ジョブ名/doDeleteでエラーが
JenkinsのRemote APIでJenkins-path/job/ジョブ名/doDeleteが消すコマンド。これをRestClient.postで実行すればいいやと。だけど
/var/lib/gems/1.9.1/gems/rest-client-1.6.7/lib/restclient/abstract_response.rb:39:in `return!': 302 Found (RestClient::Found)
なんてエラーが出てアボートしてしまう。だけど削除はされる。うーん。色々手順踏まないとpostは正常に処理できないようだ。

'record'View内の録画ジョブだけをリストアップしたい
Hudson::Job.listとかだと全部列挙されてしまう。
Hudson::Job.find("recod_.*")とかやりたい。
Hudson::Job.listInView("record")とかview内のジョブリストを得たい。
ってのが出来ない。

結局、RestClientで取得することにした。

RestClient.get "http://jenkins/view/record/api/json"

って感じで今回はJSONで受けてみた。XMLでも大した違いはないし、hudson-remote-apiがREXML/documentをrequireしてるから、XMLでやった方がスマートだと思うけど。

気持ちは、hudson-remote-apiで全部やるか、rest-clientで全部やるか、だったけど上記のように、どっちも鎮座した。すぐには解決しそうにないんで、結果を優先して

  • ジョブ情報取得はrest-clientを使用
  • 削除はhudson-remote-apiを使用
  • 情報パースは気まぐれにJSONを使用

ということになった。

/job/ジョブ名/1/buildTimestamp をTime.parse 出来ない
ビルド#1のタイムスタンプを得るJenkinsのリモートコマンドから得られる日時文字列をそのままTime.parseするとエラーになってしまう。受け付けてくれないフォーマットなのだろう。
これだけのAPI使ってるのに自力てパースなんてやりたくないぞ。

/job/ジョブ名/1/api/json
で取得した中の'timestamp'を直接頂いて、Time.atに食わせるようにした。

こんな風に、どれもこれもが中途半端な感じですっきりしないんで公開するのをためらっていたが、ひとまずの結果として残しておこうと思う。

そんなこんなのJenkinsジョブ削除スクリプトがこちら。
close2.rb

#!/usr/bin/ruby
# -*- coding: utf-8 -*-
#
require 'hudson-remote-api'
require 'rest_client'
require 'json'

Hudson.settings = {:url => 'http://localhost/jenkins', :crumb => false }

# 5日前
some_days_ago = 60 * 60 * 24 * 5

if true
  # REST-APIでtimestampアクセス
  jobs = JSON.parse RestClient.get "http://localhost/jenkins/view/record/api/json"
  jobs['jobs'].each {|job|
    if job['color']=='blue'

      #timestamp = RestClient.get Hudson[:url] + "/job/#{job['name']}/1/buildTimestamp"
      #datetime = Time.parse(timestamp)
      # パースがうまく出来ないんで

      build = JSON.parse RestClient.get Hudson[:url] + "/job/#{job['name']}/1/api/json"

      datetime = Time.at( build["timestamp"].to_i / 1000 )

      if datetime + some_days_ago < Time.now

        #RestClient.post Hudson[:url] + "/job/#{job['name']}/doDelete",nil
        #/var/lib/gems/1.9.1/gems/rest-client-1.6.7/lib/restclient/abstract_response.rb:39:in `return!': 302 Found (RestClient::Found)
        #このエラーを解消できないんで

        jjob = Hudson::Job.get job['name']
        if !jjob.nil?
          jjob.delete
          puts job['name']
        end
      end
    end
  }

else
  # Hudson-remote-apiだけで出来ないかなぁ

  jobs = REXML::Document.new Hudson::HudsonObject.get_xml "http://localhost/jenkins/view/record/api/xml"

  jobs.elements.each('listView/job') {|j|
    job = Hudson::Job.get j.elements['name'].text
    if !job.nil? && job.color == 'blue'
      #p job.last_build
      # うーんと、ビルドタイムスタンプへ辿り着けないぞ
    end
  }
end


うまく出来なかった部分のコメントやら、いずれ、hudson-remote-apiだけで出きるようにならないかな?という期待を込めてのコードも残してあるんで、ちょっと読みにくいよ。

この2つのスクリプトを1日1回実行するJenkinsジョブを作って回している。が・・

Jenkinsで動かすと以下の様なエラーが
+ /opt/task/close2.rb
/usr/lib/ruby/1.9.1/json/common.rb:148:in `encode': "\xE9" on US-ASCII (Encoding::InvalidByteSequenceError)
 from /usr/lib/ruby/1.9.1/json/common.rb:148:in `initialize'
 from /usr/lib/ruby/1.9.1/json/common.rb:148:in `new'
 from /usr/lib/ruby/1.9.1/json/common.rb:148:in `parse'
 from /opt/task/close2.rb:15:in `
' Build step 'Execute shell' marked build as failure
普通に自分の環境でコマンド実行する場合は問題ないけど、Jenkinsジョブとして動かすとこんなエラーが出てしまう。気まぐれに使ったJSONが仇になったか。ちょうど'record'ビューの説明文に日本語を使っていたので、引っかかってしまった。

JSON自体はunicodeにもちろん対応しているけど、encodeの動作はLANG環境変数に依存しているようだ。解決方法は2つ。
  • シェルコマンドに "LANG=ja_JP.UTF-8 /opt/task/close2.rb" って個別に書くか
  • Jenkinsのシステム設定のグローバルプロパティで環境変数を設定するか
どっちでもいいと思うけど、また同じようなことで対応するのは面倒なのでJenkinsの設定でLANG環境変数を設定することにした。



オリンピック、女子サッカーと女子バレー、
自分がライブで観る試合はなぜか負ける・・・

0 件のコメント:

コメントを投稿