zaki work log

作業ログやら生活ログやらなんやら

[Ansible] creates/removesを指定してcommand使用時のファイルの有無事前チェックを行う

commandshellモジュール使用時の冪等性の処理に「事前チェックtaskを実行、結果をregisterで保持、その内容を使ってwhenでガード」ってよくするけど、ファイルの有無程度であれば標準機能でカバーできる。
(commandの結果ファイルを生成するような処理の場合、そのファイルが作成済みであればスルーする、など)

docs.ansible.com

以下すべてcommandの例だけど、shellも同じ動作。
確認した環境は以下の通り。

[zaki@cloud-dev ansible-work]$ cat /etc/redhat-release 
CentOS Linux release 7.8.2003 (Core)
[zaki@cloud-dev ansible-work]$ ansible-playbook --version
ansible-playbook 2.9.10
  config file = None
  configured module search path = ['/home/zaki/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
  ansible python module location = /home/zaki/.local/lib/python3.6/site-packages/ansible
  executable location = /usr/local/bin/ansible-playbook
  python version = 3.6.8 (default, Apr  2 2020, 13:34:55) [GCC 4.8.5 20150623 (Red Hat 4.8.5-39)]

書式

command:の行に実行するコマンドを書かずに、cmd:をパラメタとして渡すように書く。そこにcreatesremovesも追加する。

  - name: mv file
    command:
      cmd: mv /tmp/zzz-src /tmp/zzz-dest
      removes: /tmp/zzz-src

以下のように書くとエラーになる。

  - name: mv file
    command: mv /tmp/zzz-src /tmp/zzz-dest
      removes: /tmp/zzz-src

というかそもそもこれはYAML的に書式がおかしい。
commandというキーにmv ~~~という値をセットいている以上、そのサブキー(なんて言うのが正しい?)は書けない。

あとドキュメントにも書かれている通り、以下のようにワイルドカードも使用可能。
shellの方には書かれてないけど、shellでもワイルドカードは使えた。

  - name: mv file
    shell: 
      cmd: mv /tmp/zzz-src /tmp/zzz-dest
      removes: /tmp/zzz-sr*

A filename or (since 2.0) glob pattern

creates

  - name: mv file
    command:
      cmd: mv /tmp/zzz-src /tmp/zzz-dest
      creates: /tmp/zzz-dest

これは、/tmp/zzz-destが作成済みであれば実行されない(okになる)

ok: [localhost] => changed=false
  cmd:
  - mv
  - /tmp/zzz-src
  - /tmp/zzz-dest
  rc: 0
  stdout: skipped, since /tmp/zzz-dest exists
  stdout_lines: <omitted>

removes

  - name: mv file
    command:
      cmd: mv /tmp/zzz-src /tmp/zzz-dest
      removes: /tmp/zzz-src

removesを使うと逆で、/tmp/zzz-srcが無い場合は実行されない

ok: [localhost] => changed=false
  cmd:
  - mv
  - /tmp/zzz-src
  - /tmp/zzz-dest
  rc: 0
  stdout: skipped, since /tmp/zzz-src does not exist
  stdout_lines: <omitted>

同時指定

両方していするとどうなるか。

  - name: mv file
    command:
      cmd: mv /tmp/zzz-src /tmp/zzz-dest
      creates: /tmp/zzz-dest
      removes: /tmp/zzz-src
# src dest 動作
1 ない ない removes: /tmp/zzz-srcの条件によりskip (1)
2 ある ない creates/removesの条件を満たさず、task動作
3 ない ある creates: /tmp/zzz-destの条件によりskip (2)
4 ある ある creates: /tmp/zzz-destの条件によりskip (2)
  1. stdout: skipped, since /tmp/zzz-src does not exist
  2. stdout: skipped, since /tmp/zzz-dest exists

#4 の動作を見る限り、内部処理でcreatesremovesに評価していると思われる。

というかソース見るとそうなってる。

github.com

Ansible的には「どちらかでも条件を満たせばskip (評価順はcreates->removes)」となる。(ANDではなくOR)

例: 「ファイルを移動する」をこれ使って簡易的にやりたいとき

srcファイルが無い場合

通常は「エラー」じゃなくて「何もしない」を期待すると思うので、基本的に removes: /tmp/zzz-src は大前提と考える。

これは #3 の場合でcreates未指定であっても、removes: /tmp/zzz-src を満たすので、「何もしない」動作になる。

destファイルが既にある場合

上記の表の#3/#4だが、#4の「srcファイルが存在する場合」の実行は要件によって以下の2パターンが考えられる。

  • destがあるなら無視
  • srcがあるならdestを上書き

srcがあっても無視

これは上記表の#4の動作の通り。
よって、creates/removes両方を指定する。

      cmd: mv /tmp/zzz-src /tmp/zzz-dest
      removes: /tmp/zzz-src
      creates: /tmp/zzz-dest

srcがあるならdestを上書き

上記表#4の条件時に、skipでなく実行させたいというパターン。
これはcreatesの定義を外せば要件を満たす。 createsを条件に使用している#3については、前述のsrcファイルが無い場合の通り、removesの条件で同じ要件を満たすので問題ない。
よって、removesのみ指定する。

      cmd: mv /tmp/zzz-src /tmp/zzz-dest
      removes: /tmp/zzz-src

この場合の動作は

# src dest 動作
1 ない ない removes: /tmp/zzz-srcの条件によりskip
2 ある ない removesの条件を満たさず、task実行
3 ない ある removes: /tmp/zzz-srcの条件によりskip
4 ある ある removesの条件を満たさず、task実行

(余談)srcとdestに差異がある場合は上書き

cmd: mvcreates/removesの組み合わせじゃ無理なのでcopyモジュールとか使うしかないかな?
この場合、「コピー」になるのでdestを上書きしてもsrcは残ったままなので消したい場合は工夫(changedの場合のみ消すとか)が必要だけど。


もし「createsremovesどっちがどの条件のときに動くのか動かないのかどっち?」と思った人は、Ansibleらしく「creates/reomovesの状態にする(その状態になってたら何もしない)」と覚えればOK