Jenkins Workflow Pluginでリトライとパラメータ付きジョブのテスト

![workflow.jpg](https://qiita-image-store.s3.amazonaws.com/0/63454/4d031dba-476d-600d-8453-8162bb1ca084.jpeg)
#最終的に作成したテストシーケンス

こちらがリトライとパラメーター付きジョブを利用したありそうなフローを想定したコードになります。中身としては以下の3つの処理を実現しています。

1. ネットワークアクセスする可能性のあるジョブ(test-retry)をリトライつきで実行
1. 複数のパラメータを引数として変えながら同一のジョブ(test-pass1)を並行で実行
1. これらのビルドが全て成功し完了した後にだけ実行出来るジョブ（test-pass2)を実行

この処理をWorkflow Pluginで実行する場合には以下のGroovyのコードになります。
※Workflow Plugin導入やflow.groovyをJenkins上で実行する方法は[こちら](http://qiita.com/kawahira/items/7571b43a1b33a941ec78)を参考にしてください。

```groovy:flow.groovy
// flow.groovy よくあるフローのテスト

import hudson.model.* // StringParameterValueの利用に必要

def builds() {
	def platform = new StringParameterValue('Platform', 'SegaElysion')
	def debugArg = [platform,new StringParameterValue('BuildType', 'Debug')]
	def releaseArg = [platform,new StringParameterValue('BuildType', 'Release')]

	parallel(netaccess: {
		// ネットワークアクセスする処理なのでリトライ付き
		retry(3) {
			run 'test-retry'
		}
	}, debug: {
		// デバッグビルド
		run 'test-pass1', debugArg
	}, release: {
		// リリースビルド
		run 'test-pass1', releaseArg
	})
	run 'test-pass2' // 全ての並列処理が終わった後に実行出来るジョブ
}

// 引数付きジョブ実行
def run(name,arg) {
	try {
		echo "job param:${arg}"
		build job: name, parameters: arg
	} catch (e) {
		build 'test-notify'
		throw e
	}
}

// 引数なしジョブ実行
def run(name) {
	try {
		build job: name
	} catch (e) {
		build 'test-notify'
		throw e
	}
}

return this;
```

こちらが上記テスト結果のRunning Stepsです。
意図通りにRetry3回とdebug / releaseブランチのジョブが全て成功してから
最後のtest-pass2が実行されている事が確認出来ます。

![runningsteps-last.png](https://qiita-image-store.s3.amazonaws.com/0/63454/9b91e706-57bc-9341-9861-6a8491c7f8f1.png)

こちらがJenkinsのConsole Outputです。
test-pass1に異なるパラメータが渡されて実行されている事が確認出来ます。

```logos:Jenkins-Console-Output
Running: Loaded script: groovy/flow.groovy
Running: Evaluate a Groovy source file into the workflow script : Body : Start
Running: Evaluate a Groovy source file into the workflow script : Body : End
Running: Execute sub-workflows in parallel : Start
Running: Parallel branch: netaccess
Running: Parallel branch: debug
Running: Parallel branch: release
Running: Retry the body up to N times : Start
Running: Retry the body up to N times : Body : Start
Running: Print Message
job param:[(StringParameterValue) Platform='SegaElysion', (StringParameterValue) BuildType='Debug']
Running: Building test-pass1
Starting building project: test-pass1
Running: Print Message
job param:[(StringParameterValue) Platform='SegaElysion', (StringParameterValue) BuildType='Release']
Running: Building test-pass1
Starting building project: test-pass1
Running: Building test-retry
Starting building project: test-retry
Running: Execute sub-workflows in parallel : Body : End
Running: Building test-notify
Starting building project: test-notify
Running: Execute sub-workflows in parallel : Body : End
Running: Retry the body up to N times : Body : End
Running: Retry the body up to N times : Body : Start
Running: Building test-retry
Starting building project: test-retry
Running: Building test-notify
Starting building project: test-notify
Running: Retry the body up to N times : Body : End
Running: Retry the body up to N times : Body : Start
Running: Building test-retry
Starting building project: test-retry
Running: Retry the body up to N times : Body : End
Running: Retry the body up to N times : End
Running: Execute sub-workflows in parallel : Body : End
Running: Execute sub-workflows in parallel : End
Running: Building test-pass2
Starting building project: test-pass2
Running: Allocate node : Body : End
Running: Allocate node : End
Running: End of Workflow
Finished: SUCCESS
```

以降はリトライだけの簡単なテストとパラメータ付きだけの簡単なテスト内容に分けます。
#パラメータ付きジョブの実行

調査をしようと思っていたら、そのものズバリの答えの投稿があり、とても助かりました。
[Workflow plugin で Parameterized Trigger Build っぽいこと](
http://qiita.com/yasuhiroki/items/9d96af7538975358d175)

と言うわけでコピペさせてもらいつつテストします。

パラメータ付きジョブは以下の書式となります。
`build job: ジョブ名, parameters: [StringParameterValue(パラメータ名, 値),....]`

今回は以下の様に2つのパラメーターを渡すテストをしてみました。

```groovy:parameterized-pass.groovy
// parameterized-pass.groovy パラメーター付き
import hudson.model.* // StringParameterValueの利用に必要

def builds() {
	def arg1 = [new StringParameterValue('Platform', 'SegaElysion'),new StringParameterValue('BuildType', 'Debug')]
	run 'test-pass1',arg1
}

// 引数付きジョブ実行
def run(name,arg) {
	try {
		echo "job param : ${arg}"
		build job: name, parameters: arg
	} catch (e) {
		echo "job error : ${name} status : ${arg}"
		build 'test-notify'
		throw e
	}
}
```

このスクリプト実行後のtest-pass1のパラメーターは以下の通り
![param.png](https://qiita-image-store.s3.amazonaws.com/0/63454/0785b0a8-7514-9f6f-96cc-27f431530251.png)

ちゃんとパラメーターが受け渡されていますね。

```logos:Jenkins-Console-Output-Parameterized
Running: Loaded script: groovy/flow.groovy
Running: Evaluate a Groovy source file into the workflow script : Body : Start
Running: Evaluate a Groovy source file into the workflow script : Body : End
Running: Print Message
job param : [(StringParameterValue) Platform='SegaElysion', (StringParameterValue) BuildType='Debug']
Running: Building test-pass1
Starting building project: test-pass1
Running: Allocate node : Body : End
Running: Allocate node : End
Running: End of Workflow
Finished: SUCCESS
```
ログにも渡されたパラメーターを表示してみましたが正しく表示されています。
これでパラメーターの受け渡しが出来る様になりました

#リトライを検証
ネットサービスへアクセスしているジョブなどは残念ながらジョブが失敗する事が多々あります。
こういうジョブには、もうリトライかましてやる以外（またはジョブの中でリトライ）手がありません。
その様なサービスを監視したり制御するサービスにJenkinsを利用しているとリトライが欲しくなると思います。

こちらのテストの為に成功したり失敗したりするジョブが必要になりましたのでtest-retryと言うジョブを新しく作成しました。

![retry.png](https://qiita-image-store.s3.amazonaws.com/0/63454/273626e6-f466-0b39-c1a7-b6d1bc8dd13b.png)

この様に3回実行すると1回成功するだけのジョブです。

```ps1:test-retry.ps1
exit $env:BUILD_NUMBER % 3
```

中身はこれだけです。

Workflow Pluginでリトライを実現するには以下のキーワードで処理を囲い回数を指定して実現します。

`retry(回数) { 処理 }`

このtest-retryを利用したgroovyでのテストシーケンスがこちら

```groovy:retry-pass.groovy
// retry-pass.groovy リトライ3回して必ず成功する
def builds() {
	retry(3) {
		run 'test-retry'
	}
}
def run(name) {
	try {
		build name
	} catch (e) {
		echo "job error : ${name}"
		build 'test-notify'
		throw e
	}
}
return this;
```

リトライ回数が3回なので必ず成功します。

![retry-runningsteps.png](https://qiita-image-store.s3.amazonaws.com/0/63454/45997ffa-d8b4-a89b-ad34-d8271fcc9ef0.png)

意図した通りtest-retryが2回失敗して3回目で成功していますね。

```logos:Jenkins-Console-Output-Retry
Running: Loaded script: groovy/flow.groovy
Running: Evaluate a Groovy source file into the workflow script : Body : Start
Running: Evaluate a Groovy source file into the workflow script : Body : End
Running: Retry the body up to N times : Start
Running: Retry the body up to N times : Body : Start
Running: Building test-retry
Starting building project: test-retry
Running: Print Message
job error : test-retry
Running: Building test-notify
Starting building project: test-notify
Running: Retry the body up to N times : Body : End
Running: Retry the body up to N times : Body : Start
Running: Building test-retry
Starting building project: test-retry
Running: Print Message
job error : test-retry
Running: Building test-notify
Starting building project: test-notify
Running: Retry the body up to N times : Body : End
Running: Retry the body up to N times : Body : Start
Running: Building test-retry
Starting building project: test-retry
Running: Retry the body up to N times : Body : End
Running: Retry the body up to N times : End
Running: Allocate node : Body : End
Running: Allocate node : End
Running: End of Workflow
Finished: SUCCESS
```
Jenkinsログを見ても**SUCCESS**している事を確認出来ます。

テストコードのリトライ回数を2回にするときちんと失敗する事も確認出来ました。

#まとめ

最終的には今までJenkinsのパラメータ違いで生成しておいたジョブの数だけflow.groovyを作成し個別にバージョン管理しつつ異なるflow.groovyをloadする部分だけをJenkin上で実行する事になるのが移行する際に利用するフローとなりました。

とりあえず影響度が小さく抑えられそうなプロダクトに適用して様子を見てみようと思います。