3
3

More than 3 years have passed since last update.

Ansible: shellモジュールに冪等性および正しいchanged結果を持たせる為のロジック例

Last updated at Posted at 2018-12-04

追記

この例では、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にて必要なロジック

イメージとしては以下の様な感じか。

  1. set -e # 途中でエラーとなった際に終了させる
  2. if 変更を伴う処理の実行が必要ならば
    1. 変更を伴う処理を実行
    2. 外部へのchangedの連携
  3. 必要なだけ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使用の方が楽そう。

3
3
0

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
3
3