Capistrano3のSSHKitでエラーハンドリングする方法
今取り組んでいる仕事でCapistrano3を使ったデプロイを実装していました。
SSHコマンド実行先ホストでエラーが発生した場合の対処方法に
相当悩まされ、なんとか解決できたので、せっかくなので共有します。
サマリ
sshkitでコマンド実行先のエラーをハンドリングしたい場合、
raise_on_non_zero_exit:false
を設定するとともに、
captureメソッドで戻り値を取得し、実行元サーバで判定するとよい。
実現したいこと
- Jenkinsでアプリをビルドしたい
- JenkinsからCapistranoでS3に最新資材を配置したい
- JenkinsからCapistranoでAPサーバ上の資材取得スクリプトを実行したい
- APサーバ上の資材取得スクリプトで、S3から最新資材を取得したい
今回悩んだのは、3,4の資材取得スクリプト実行で失敗した場合、
S3上の最新資材をロールバックしたいという点でした。
困ったこと
sshkitは、(特に考慮しない場合、)
コマンドの戻り値が0以外だとその時点で異常終了してしまいます。
(それがたとえdiffの結果であっても、、!)
調査を進めたところ、 raise_on_non_zero_exit
パラメータで、
エラーを無視することができそうだということがわかりました。
ということはsshkitのデフォルト機能でエラーハンドリングできるのかも?
と調査を進めたところ、下記のような実装が見つかりました。
def exit_status=(new_exit_status) @finished_at = Time.now @exit_status = new_exit_status if options[:raise_on_non_zero_exit] && exit_status > 0 message = "" message += "#{command} exit status: " + exit_status.to_s + "\n" message += "#{command} stdout: " + (full_stdout.strip.empty? ? "Nothing written" : full_stdout.strip) + "\n" message += "#{command} stderr: " + (full_stderr.strip.empty? ? 'Nothing written' : full_stderr.strip) + "\n" raise Failed, message end end
sshkit/command.rb at master · capistrano/sshkit · GitHub
なんで !raise_on_non_zero_exit && exit_status > 0
をフックさせてくれないの、、
ということで、デフォルトにエラーハンドリングの機能はないと判断しました。
※sshkitではなくcapistrano3にはデフォルトでrollbackタスクがありますが、
今回はカスタムで作りこんでいたため利用しませんでした。
結論
raise_on_non_zero_exit
でエラーを無視でき、
デフォルトにハンドリング機能はないことがわかったので、
愚直に戻り値を元にハンドリングすることにしました。
具体的には下記のような実装です。
ret = capture :bash /xxxx/xxxx/apply_package.sh > /dev/null;echo $?", raise_on_non_zero_exit: false # 資材取得スクリプトの戻り値が0以外の場合にS3上の資産をロールバック invoke "aplfront:rollback_s3" if !ret.to_i.zero?
SSHコマンド実行先のサーバで、 /xxxx/xxxx/apply_package.sh
を実行し、
戻り値をcaptureメソッドによって ret
変数に格納しています。
その後、 ret
変数の値が0でない場合、 aplfront:rollback_s3
を実行しています。
また、rollback処理が複数起動しないよう、該当のタスクは同時実行数が1となるように設定しています。
ちなみに
Capistrano3以前はon_rollbackを定義できたご様子。なくなっちゃったの?