#追記
この例では、dry runに未対応
dry runに対応させるには、分離方式が必要と考えられる。つまり、冪等性確認の為の先行shellモジュールを{ check_mode: false, changed_when: false }で実行し、本処理はwhenなどでskipするかどうかを決める。
参考
https://dev.classmethod.jp/server-side/ansible/using_check_mode/
#問題
Ansibleのshellモジュールにて冪等性を担保するには、以下の方法が考えられる
- creates, removes バリヤーの使用
- 使用できるケースが限定される。
- 事前に別モジュールで入手した結果をregister:にて連携し、shellモジュールのwhen:にてバリヤーとして使用
- Chefではnot_ifなどにおいて対象システムの状態を入手/使用できるが、Ansibleでは(ファイル有無を除いて)事前に別モジュールで情報を入手する必要があり、冗長で美しく無い
- スクリプトロジックを再入可能とする
また、バリヤーが弱い事から、変更が必要な状態だから実行した(i.e.変更が試みられた筈)という推測が困難であり、正しいchanged結果出力が望まれる。
ここでは、冪等性の担保、および正しいchanged結果出力を得る、スクリプトロジックを考える。
#目標
- 事前モジュール実行による情報取得無し
- 変更無しならばok, 変更有りならばchanged状態を返す
#shellにて必要なロジック
イメージとしては以下の様な感じか。
- set -e # 途中でエラーとなった際に終了させる
- if 変更を伴う処理の実行が必要ならば
- 変更を伴う処理を実行
- 外部へのchangedの連携
- 必要なだけ2を繰り返し
一度も外部へのchangedの連携が試みられなかった場合、okとする。
外部へのchangedの連携だが、stderrなどを用いる方法, rcを用いる方法 などが考えられる。
##stderrを用いる方法例
- shell: |
set -e
# ...
if grep -q 'foo' /tmp/test.txt; then # 変更を伴う処理の実行が必要ならば
sed -ie 's/foo/bar/g' /tmp/test.txt # 変更を伴う処理を実行
echo _changed_ >&2 # 変更があった事を外部に連携
fi
# ...
register: result
changed_when: '"_changed_" in result.stderr_lines'
ここでは外部に連携する方法として、stderrに、"_changed_"という行が1行でも出力されたかどうかを使用。
stdoutを使用するなら、echo _changed_およびchanged_when: '"_changed_" in result.stdout_lines'とする。
考慮点
exec 2>>/tmp/logなどstderrを横取りしてしまうと動作できない。
##rcを用いる方法例
- shell: |
set -e
changed=false
# ...
if grep -q 'foo' /tmp/test.txt; then # test.txtにfooが含まれるならば
sed -ie 's/foo/bar/g' /tmp/test.txt # fooをbarに置き換え
changed=true # 変更があった事を外部に連携
fi
# ...
if $changed ; then
exit 99
fi
register: result
changed_when: result.rc == 99
failed_when: result.rc not in [ 0, 99 ]
rcに、ok, failed, に加えて、changedの意味を持たせる為、ここでは99をchengedを示すrcとしている。
- rc == 0 ならば ok
- rc == 99 ならば changed
- rc not in [0,99] ならば failed
if $changed ... は、[[ $changed == false ]] || exit 99 などでも。
##出力例
初回
TASK [shell] *******************************************************************************************************************
changed: [ansiblepush-centos-7]
2回目以降
TASK [shell] *******************************************************************************************************************
ok: [ansiblepush-centos-7]
##比較
rcの体系を考えなくても良いstderr使用の方が楽そう。