cloudpack の DJ かっぱ (@inokara) です。
はじめに
Cron の結果をメール通知ぢゃなくて Jenkins に飛ばす試みは以前にもやったことがありましたが、同じことを Go で実装したら面白そうだなって思ったので勉強を兼ねて作ってみました。Jenkins のドキュメントには Java で作られた jar を利用していますが Java をインストールしたくない場合に Go であれば、その辺りのハードルを超えられれないかなと考えています。
作り始めたら一時間位で出来てしまったの(ソースの美しさとか置いといて)で Go の敷居の低さとか先人の方々の努力の結晶には本当に頭が下がります。ありがとうございます。
Cron の結果を Jenkins にポストするには
外部ジョブの監視
Jenkins は以下のようなジョブを作成することが出来ます。
- フリースタイル・プロジェクトのビルド
- Mavenプロジェクトのビルド
- External Job(外部ジョブ)
- マルチ構成プロジェクトのビルド
その中から External Job
を利用します。
XML をポストする
外部ジョブの監視では以下のような XML
を Jenkins ジョブの postBuildResult
にポストすることで結果を Jenkins に登録します。
...コンソール出力を16進数のバイナリエンコードしたもの... ... エラーコード(整数)0は成功でそれ以外は失敗 ... 実行時間(ms) ... ... ビルドナンバーの代わりとして表示させたい名前 ... ... ビルドの詳細 ...
例えば、curl を利用する場合には以下のように実行することでも結果を登録することが出来ます。
$ curl -X POST -d '' http://user:pass@myhost/jenkins/job/_jobName_/postBuildResult 4142430A 0 2000
実際に実行すると Jenkins 上には以下のように登録されています。
<log encoding='hexBinary'> ~ </log>
の間には上記の通りコンソール出力をバイナリエンコードした内容を記録することで Jenkins にも同じ内容を出力することが可能です(上図では ABC
という文字列を登録しています)。また、<result> ~ </result>
には cron で実行したスクリプトのステータスコードを 0
やそれ以外の数値を設定します。0
は成功、それ以外は失敗として Jenkins は取り扱います(上図では 0
を登録しているの成功のビルドとなっています)。
メリット、デメリット
cron の結果を「外部ジョブの監視」を利用して Jenkins に登録することで以下のようなメリットがあると考えています。
- 結果の継続的な可視化(
Duration
の時間等で cron で動作するスクリプトの実行時間を把握) - cron ジョブの一括管理
- 一覧化されることで結果の見落としを防ぐことが出来る
デメリット(注意点)としては…
- http リクエストでの登録なので Jenkins への接続が失敗すると cron ジョブの結果を把握することが出来ない
- Jenkins 見ないと結果判らない
- ジョブ単位での通知が出来ない(?)
というそもそものお話となります、上記のようにデメリット(注意点)はあるものの、結果の継続的な可視化や複数のホストで稼働している cron ジョブを一覧で管理出来るというのは嬉しいかな…。同様の事が出来る Rundeck というサービスもあるようですので試してみたいなと思っています。
ということで go2jenkins
という名前にしました
ということで cron の実行結果を Jenkins の外部ジョブに登録するスクリプトを go で作ってみました。
使い方
以下のようにソースコードを取得してビルドします。
git clone https://github.com/inokappa/go2jenkins.git cd go2jenkins go build go2jenkins.go
ビルドが終わったら以下のように実行してオプションを確認してみます。
$ ./go2jenkins -h Usage of ./go2jenkins: -host="http://localhost:8080": Set Jenkins hostname -job="hoge": Set Jenkins job name -script="/path/to/foo.sh": Set script
次に以下のように Jenkins で外部ジョブ監視(External Job
)を作成します。
次にリポジトリに同梱している example.sh
を利用して以下のように go2jenkins
を実行してみます。(※Jenkins はローカルホストで起動している状態です。)
$ ./go2jenkins -script=/path/to/example.sh -host=http://localhost:8080 -job=hogehoge status: 200 OK
また、crontab への登録は以下のように登録します。
* * * * * /path/to/go2jenkins -script=/path/to/example.sh -host=http://localhost:8080 -job=hogehoge
尚、example.sh
をそのまま実行すると以下のように出力されます。
$ /path/to/example.sh Sun Jan 18 13:56:13 UTC 2015 Hello Jenkins. Sun Jan 18 13:56:23 UTC 2015
上記の内容が Jenkins 上で結果の判定を含めて見れるのはちょっと嬉しいかも。
結果を見てみる
Jenkins を見ると以下のように結果が登録されています。
ちゃんとコンソールに出力されている内容が Jenkins にも登録されています。また、example.sh
が構文エラーになっている場合には以下のようにビルドは失敗している状態として Jenkins には登録されています。
さらに cron で定期実行させた結果の一覧は以下のように出力されて継続的な結果の可視化が可能かと思います。
go2jenkins を作るにあたって得た知見
hex バイナリエンコード
encoding/hex パッケージを利用します。
package main import ( "fmt" "encoding/hex" ) func main() { Log := "hogehoge" sEnc := hex.EncodeToString([]byte(Log)) fmt.Println(sEnc) }
以下のように出力されます。
$ go run example.go 686f6765686f6765
HTTP Post
bytes パッケージと net/http パッケージを利用します。
package main import ( "fmt" "bytes" "net/http" "os" ) func main() { body := bytes.NewBufferString("") res, err := http.Post("http://localhost:8080/job/test/postBuildResult", "text/xml; charset=utf-8", body) if err != nil { fmt.Println(err) os.Exit(1) } fmt.Println("status: ", res.Status) } 4142430A 0 2000
bytes.NewBufferString
を利用してリクエストボディを生成しています(なぜ bytes.NewBufferString
しているのかは要調査…)。http.post
は以下のように実行しています。
res, err := http.Post("http://localhost:8080/job/test/postBuildResult", "text/xml; charset=utf-8", body)
test/xml
のようなボディタイプを指定する点に注意する必要があるようです。
上記のサンプルを実行すると以下のように出力されます。
$ go run example.go status: 200 OK
外部コマンドの実行
Go スクリプト内から外部のコマンドを実行する場合には os/exec を利用します。実行した外部コマンドの標準出力及び標準エラーを得るには以下のように書くようです。こちら「golang で外部コマンドを実行して標準出力を取得する」を参考にさせて頂きました。ありがとうございます。
package main import ( "fmt" "bytes" "os/exec" ) func main() { // var stdout bytes.Buffer var stderr bytes.Buffer var Log string // cmd := exec.Command("date") cmd.Stdout = &stdout cmd.Stderr = &stderr err := cmd.Run() if err != nil { Log = string(stderr.String()) }else{ Log = string(stdout.String()) } fmt.Println(Log) }
実行すると以下のように date
コマンドの実行結果が表示されます。
$ go run example2.go Sun Jan 18 15:00:10 UTC 2015
ということで…
今回作ったスクリプトはまだまだ修正しなければいけない点がいくつかありますが、プログラムとはほぼ無縁な自分でも色々な「型」に気をつける必要はありつつもシェルスクリプト感覚で思った物が作れる Go って面白いなと思います。また、Jenkins を利用した cron ジョブの監視スクリプトはツッコミどころはいくつかありますが、Go で作ったことによりビルドしたバイナリ一個を設置するだけで結果の見落とし防止やジョブの管理として利用することは出来る点は嬉しいと思っています。
ということで…おやすみなさい。
元記事はこちらです。
「cron の結果を Jenkins にポストするハンドラっぽいのを Go で作ってみた」