はじめに
Ansible の実行時に --check
を指定すると check mode (dry run) として (リモートに変更を加えることなく) 実行され,--diff
を指定すると変更内容の差分が表示されます.
ただしモジュール側がこれらのオプションをサポートしている場合に限ります*1.
check mode や差分表示は事前確認が容易になるという点で利便性が高いため,可能な限りモジュールを対応させたくなります.
これらの対応方法について調べてみると Check Mode (“Dry Run”) で若干触れられていますが,実際には対応済みモジュールのコードを読み解く必要があります*2.
そこで今回はこの対応方法についてまとめておきたいと考えました.
Ansible 環境
ansible 2.6.0 (devel ad69ef88e7) last updated 2018/05/20 18:09:04 (GMT +900) config file = None configured module search path = [u'/Users/krdlab/.ansible/plugins/modules', u'/usr/share/ansible/plugins/modules'] ansible python module location = /Users/krdlab/.provisioning/dev/projects/misc/krdlab/ansible/lib/ansible executable location = /Users/krdlab/.provisioning/dev/projects/misc/krdlab/ansible/bin/ansible python version = 2.7.11 (default, Oct 20 2016, 16:27:36) [GCC 4.2.1 Compatible Apple LLVM 7.0.2 (clang-700.1.81)]
ベースとなるサンプルモジュール
path
で指定したファイルを作成/削除するだけのモジュールを用意しました.
https://github.com/krdlab/examples/blob/master/ansible-module-check-and-diff/version1/simplefile.py
以下のように実行します.
$ ansible all -i ',localhost' -c local --module-path version1 -m simplefile -a 'path=/tmp/testfile state=present' localhost | SUCCESS => { "changed": true, "simplefile": { "path": "/tmp/testfile", "state": "present" } }
まだ check mode には対応していないコードであるため,当然 check mode では期待通りに動きません.
$ ansible all -i ',localhost' -c local --module-path version1 -m simplefile -a 'path=/tmp/testfile state=present' --check localhost | SKIPPED # ← スキップされる
check mode のサポート
check mode のサポートを追加したコード全体は,以下のリンク先の通りです.
https://github.com/krdlab/examples/blob/master/ansible-module-check-and-diff/version2/simplefile.py
AnsibleModule
に supports_check_mode = True
を指定すると check mode でもスキップされることなくモジュールが実行されます.
module = AnsibleModule( argument_spec=dict( ... ), supports_check_mode=True # ← これ )
module.check_mode
で check mode かどうかの判定が可能です.
check mode 時は実際の変更をリモートに加えることなく,現在のリモートの状態と指定されたパラメータを比較して「変更が発生するか?」だけを判定させます. 例えば AWS 系のモジュールだと,describe API を呼び出して得られた情報と指定されたパラメータの内容とを比較して "changed" か "ok" かを決定しています.
今回の simplefile
モジュールも (リモートではありませんが) 現在の状態と指定されたパラメータに基づいて,変更が発生する場合に changed = True
を返すように実装しています (以下のリンク先参照).
また戻り値*3をきちんと返すようにすると,register
を使っていても後続のタスクが失敗しなくなるためおすすめです*4.
差分表示のサポート
差分表示のサポートを追加したコード全体は,以下のリンク先の通りです.
https://github.com/krdlab/examples/blob/master/ansible-module-check-and-diff/version3/simplefile.py
module.exit_json
に diff
パラメータを渡すと,--diff
を指定して実行した場合に変更差分が表示されるようになります.
...
module.exit_json(changed=changed, diff=diff, simplefile=params)
# ↑これ
diff の構造は以下のものが期待されます.
diff = dict( before=dict( 変更前の状態 ), after=dict( 変更後の状態 ) )
before
と after
の中身は比較可能となるようにモジュール作成者が決定します.
user
や file
モジュールなどを参考に実装すると良さそうです.
今回の simplefile
モジュールだと以下のような情報を返しています.
def __create_diff(self): path = self.expected['path'] act_state, exp_state = self.__file_state(), self.expected['state'] diff = dict(before=dict(path=path), after=dict(path=path)) # path は変化しないので必ず入れる if act_state != exp_state: # state は変化する場合だけ diff に取り込む diff['before']['state'] = act_state diff['after']['state'] = exp_state return diff
上記のようなデータを返すと,--diff
を付けて実行したときに以下のような表示が得られます.ちなみにこれは,既に存在している /tmp/testfile
を absent
にする場合のものです.
--- before +++ after @@ -1,4 +1,4 @@ { "path": "/tmp/testfile", - "state": "present" + "state": "absent" }
おわりに
Ansible のモジュールを check mode に対応させることで,実際に変更を適用する前に変更が発生するのかどうかを知ることができます.
差分表示にまで対応させれば「あれ?なんの変更だっけ?」といったときに役立ちます.
また可能であれば check mode でも戻り値を返すようにしておくと,regsiter
を使ってい他場合に後続タスクが失敗しなくなります.
やはり適用前に確認はしたいですから,check mode に対応することは結構重要なことだと思います*5.
というわけで今回はその対応方法の紹介でした.
*1:実際に Ansible の標準モジュールの中にも未対応なものがちらほらみつかります.コアモジュール以外は "not required" とされているようです → Check Mode (“Dry Run”)
*2:もしかしてどこかにまとまっているんだろうか?
*3:ドキュメントで Return Value として定義されているデータ
*4:どうしても返せない場合もあるのですが……
*5:なのでこれが取り込まれると嬉しいなぁ…… → https://github.com/ansible/ansible/pull/39846