LoginSignup
62
60

More than 5 years have passed since last update.

Ansible の shell モジュールでチェックモードも考慮して冪等性を保つ

Last updated at Posted at 2015-03-08

まずは結論から、例えば以下のようなタスクの場合、

  • uname -a コマンドの結果を /tmp/hoge に出力する
  • /tmp/hoge が既にあるなら実行しない
    • コメントの通りファイルの存在確認なら stat モジュールで十分なのですが、より多くのケース(なにかしらのコマンドの結果を元に実行の有無を制御)を想定してあえて shell モジュールを使います

次のようにします。

- hosts: localhost
  tasks:
    - shell: test -e /tmp/hoge
      register: res
      always_run: yes
      failed_when: no
      changed_when: res.rc != 0

    - shell: uname -a > /tmp/hoge
      when: res|changed

これだけなら shell モジュールの creates を使うだけでいいんじゃない?
と思わなくもないですが、良いサンプルが思いつかなかったのでその辺は無視してください。

その1

単に uname -a の結果を /tmp/hoge に書くだけなら、何回実行しても結果は変わらないので次のようにしても大丈夫です。

- hosts: localhost
  tasks:
    - shell: uname -a > /tmp/hoge

その2

がしかし、これだと何回実行しても changed と表示されます。

せっかく Ansible を使うならその辺の表示もそれなりにしたいところです。

そこで、タスクを実行する必要があるかどうかを先行するタスクで確認する方法が考えられます。

- hosts: localhost
  tasks:
    - shell: test -e /tmp/hoge
      register: res
      failed_when: no

    - shell: uname -a > /tmp/hoge
      when: res.rc != 0

最初の test のタスクで /tmp/hoge が存在するかどうか確認し、その結果から次のタスクを実行するかどうかを when で分岐します。

その3

がしかし、これだと test の方のタスクが常に changed になってしまいます。

test のタスクはファイルの存在をチェックしているだけで実際にはなにもしないので、次のように changed_when を追加して changed にならないようにします。

- hosts: localhost
  tasks:
    - shell: test -e /tmp/hoge
      register: res
      failed_when: no
      changed_when: no

    - shell: uname -a > /tmp/hoge
      when: res.rc != 0

これで test のタスクは常に ok となります。

その4

がしかし、このプレイブックをチェックモード (--check|-C) で実行すると失敗します。

チェックモードでは shell はスキップされるため、2番目のタスクの when で参照している res 変数が未定義になるためです。

そこで、次のように test のタスクに always_run を追加してチェックモードでも実行されるようにします。

- hosts: localhost
  tasks:
    - shell: test -e /tmp/hoge
      register: res
      always_run: yes
      failed_when: no
      changed_when: no

    - shell: uname -a > /tmp/hoge
      when: res.rc != 0

これで test のタスクはチェックモードでも実行されるので、2番目のタスクで res 変数が未定義になることはありません。

その5

がしかし、このプレイブックを、/tmp/hoge が存在しない、かつ、チェックモード、で実行すると、changed が 0 件になります。2番目のタスクが when の条件に依らずチェックモードだとスキップされるためです。

プレイブックを実行する前に、なにかしらの変化があるかどうかを予め確認するためにチェックモードで実行することはあると思います。が、これではあたかもプレイブックを実行する必要が無いように感じてしまいます(実際には /tmp/hoge が作成されていないにも関わらず)。

そこで、2番目のタスクを実行する必要があるときには、1番目のタスクが changed になるようにします。

- hosts: localhost
  tasks:
    - shell: test -e /tmp/hoge
      register: res
      always_run: yes
      failed_when: no
      changed_when: res.rc != 0

    - shell: uname -a > /tmp/hoge
      when: res|changed

というわけで、最初に示した形になりました。

まとめ

整理すると・・・

  • 実行の要否を判断するタスク
    • test コマンドなどの実際にはなにも変化をもたらさないことだけやる
    • チェックモードでも実行されるように always_run を指定する
    • 実行結果を register で変数に登録する
    • 終了コードが非ゼロでも失敗にならないように failed_when を指定する
      • 実行するコマンドによっては必要ない
      • 例えば実行の要否を標準出力で判断する場合など
    • 実行の要否を changed_when で指定する
      • 実行が必要な場合に changed にする
  • 実際に処理するタスク
    • 先行するタスクが changed のときだけ実行する

となります。

62
60
3

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
62
60