はじめに
業務でcapistranoを使ってツールを作っています。
今回仕事で要求されている手順で、サーバ1で作業して、サーバ2で作業して、またサーバ3で作業、というものがありました。これをcapistranoで実行するためにはサーバごとにtaskを実行してやらないといけないと思っています。(というのも、ロールごとにわけたとしても、同時に実行されてしまい、順番が保証されない)このいい解決策がないか、ということを探るためにもsshkitを読み込みたい。多分sshkitを単独で使えばいけるんだろうな。
上記のような課題があり、capコマンドをラップする方法を求められています。普通にシェルで書いてもいいんですが、せっかくなのでrubyで統一感を出したいと思い、open3を使って実装してみることにします。
open3
外部コマンドを実施して、標準出力、標準エラー出力、終了ステータスが得られる、とても便利。
kaihar4.com - Rubyのopen3が便利だった話
まず、サンプルのcapistranoのtaskを書きました。
deploy/local.rbにローカル定義。
server 'localhost', user: 'root', roles: %w{web}
deploy.rbにtask定義。
task :ls do run_locally do execute :ls, "-lah" end end task :uptime do run_locally do execute :uptime end end
この時点でcapコマンドを実行するとこんな感じ。
MacBook-Air:capistrano take$ cap local ls INFO [eadb22c2] Running /usr/bin/env ls -lah on DEBUG [eadb22c2] Command: /usr/bin/env ls -lah DEBUG [eadb22c2] total 8 DEBUG [eadb22c2] drwxr-xr-x 6 take staff 204B 4 23 12:32 . DEBUG [eadb22c2] drwxr-xr-x+ 67 take staff 2.2K 4 23 12:46 .. DEBUG [eadb22c2] -rw-r--r-- 1 take staff 837B 4 23 12:23 Capfile DEBUG [eadb22c2] drwxr-xr-x 4 take staff 136B 4 23 12:32 config DEBUG [eadb22c2] drwxr-xr-x 3 take staff 102B 4 23 12:23 lib DEBUG [eadb22c2] drwxr-xr-x 3 take staff 102B 4 23 12:40 wrap INFO [eadb22c2] Finished in 0.020 seconds with exit status 0 (successful). MacBook-Air:capistrano take$ cap local uptime INFO [7639971c] Running /usr/bin/env uptime on DEBUG [7639971c] Command: /usr/bin/env uptime DEBUG [7639971c] 12:48 up 15 days, 16:58, 2 users, load averages: 0.98 1.27 1.36 INFO [7639971c] Finished in 0.012 seconds with exit status 0 (successful).
さてこれらをラップしたり、コマンドを実行できるopen3を使ってみましょう。
require 'open3' result = Open3.capture3('echo "hoge"') p result result = Open3.capture3('eho "hoge"') p result p result[2].class p result[2].exitstatus p result[2].pid result = Open3.capture3('cap local ls') p result
これを実行すると
MacBook-Air:capistrano take$ ruby wrap/wrapper.rb ["hoge\n", "", #<Process::Status: pid 27482 exit 0>] ["", "sh: eho: command not found\n", #<Process::Status: pid 27483 exit 127>] Process::Status 127 27483 [" \e[34mINFO\e[0m [\e[32m2d6060e3\e[0m] Running \e[33m\e[1m/usr/bin/env ls -lah\e[0m\e[0m on \e[34m\e[0m\n\e[30mDEBUG\e[0m [\e[32m2d6060e3\e[0m] Command: \e[34m/usr/bin/env ls -lah\e[0m\n\e[30mDEBUG\e[0m [\e[32m2d6060e3\e[0m] \e[32m\ttotal 8\n\e[0m\e[30mDEBUG\e[0m [\e[32m2d6060e3\e[0m] \e[32m\tdrwxr-xr-x 6 take staff 204B 4 23 12:32 .\n\e[0m\e[30mDEBUG\e[0m [\e[32m2d6060e3\e[0m] \e[32m\tdrwxr-xr-x+ 67 take staff 2.2K 4 23 12:46 ..\n\e[0m\e[30mDEBUG\e[0m [\e[32m2d6060e3\e[0m] \e[32m\t-rw-r--r-- 1 take staff 837B 4 23 12:23 Capfile\n\e[0m\e[30mDEBUG\e[0m [\e[32m2d6060e3\e[0m] \e[32m\tdrwxr-xr-x 4 take staff 136B 4 23 12:32 config\n\e[0m\e[30mDEBUG\e[0m [\e[32m2d6060e3\e[0m] \e[32m\tdrwxr-xr-x 3 take staff 102B 4 23 12:23 lib\n\e[0m\e[30mDEBUG\e[0m [\e[32m2d6060e3\e[0m] \e[32m\tdrwxr-xr-x 3 take staff 102B 4 23 12:40 wrap\n\e[0m \e[34mINFO\e[0m [\e[32m2d6060e3\e[0m] Finished in 0.011 seconds with exit status 0 (\e[1m\e[32msuccessful\e[0m\e[0m).\n", "", #<Process::Status: pid 27484 exit 0>]
capコマンドの実行結果が化けちゃってますね。
出力しているとおり、caputure3の結果は終了ステータスを示すProcess::Statusクラス。はじめて知った。pidや終了コードを取り出せます。
class Process::Status (Ruby 2.2.0)
capコマンドをつなげる場合は、終了ステータスが0以外であれば処理を中断する、というロジックにすればうまくラップできそうですね!
おわりに
open3関連はまだ全然見れてないのでドキュメントを読みたい。
あとは標準出力、画面に出したいなぁと思うのは贅沢でしょうか。標準出力は画面に出しつつ、内部で保持して、終了コードで判定したりとか。実用までもう少し触ってみるかな。