ansibleでの実行結果のsuccess/failedとignore_errors/failed_whenがよくわからなかったので調べてみた

# 知りたいこと

- ansibleでのsuccessとfailedの判定
- それをハンドリングするための ignore_errors と failed_when の使い方


# 事前実験

## 存在しないコマンドを実行してみる

ansibleでの接続先(リモートサーバー。CentOS6)で `sakaimo --version' コマンドしてみる。ちなみに現時点のsakaimoのversionは39.7くらいのはず。

```
# sakaimo --version
-bash: sakaimo: command not found
```

CentOSさんにはこんなコマンドは存在しないのでnot foundになります。


## ansibleでやってみる

playbook

```
- shell: sakaimo --version

- debug: msg="debug"
```

実行結果

```
PLAY [app] ********************************************************************

TASK: [sakaimo| shell sakaimo --version] *************************************
failed: [qa-lecture01] => {"changed": true, "cmd": "sakaimo --version", "delta": "0:00:00.146319", "end": "2015-05-12 10:52:53.548610", "rc": 127, "start": "2015-05-12 10:52:53.402291", "warnings": []}
stderr: /bin/sh: sakaimo: command not found

FATAL: all hosts have already failed -- aborting

PLAY RECAP ********************************************************************
           to retry, use: --limit @/root/qa-lecture-playbook.retry

qa-lecture01               : ok=0    changed=0    unreachable=0    failed=1
```

一つ目でエラーが出た場合に`- debug: msg="debug"`は実行されていないことがわかります。しれっと「エラーが出た場合」と言っていますが、何がansibleで「エラー」と判定されるのかどうかを知りたくて調べてるんですけどねｗ


## ignore_errors

`ignore_errors`を付けてみる。

playbook

```
- shell: sakaimo --version
  ignore_errors: True #←ここ

- debug: msg="debug"
```

出力結果

```
PLAY [app] ********************************************************************

TASK: [sakaimo | shell sakaimo --version] *************************************
failed: [qa-lecture01] => {"changed": true, "cmd": "sakaimo --version", "delta": "0:00:00.143888", "end": "2015-05-12 10:53:30.886426", "rc": 127, "start": "2015-05-12 10:53:30.742538", "warnings": []}
stderr: /bin/sh: sakaimo: command not found
...ignoring

TASK: [sakaimo | debug msg="debug"] ****************************************
ok: [qa-lecture01] => {
    "msg": "debug"
}

PLAY RECAP ********************************************************************
qa-lecture01               : ok=2    changed=1    unreachable=0    failed=0
```

`ignore_errors: True`では文字通りエラーがあっても無視して先にすすんでくれるようです。(debugが出力されているから) つまり`command not found`はエラーとして認識されるみたいね。


## 実行結果によって分岐させてみる

`when: result|failed`を付けてみた。

playbook

```
- shell: sakaimo --version
  register: result
  ignore_errors: True

- debug: msg="debug"
  when: result|failed #←ここ
```

実行結果

```
PLAY [app] ********************************************************************

TASK: [sakaimo | shell sakaimo --version] *************************************
failed: [qa-lecture01] => {"changed": true, "cmd": "sakaimo --version", "delta": "0:00:00.142611", "end": "2015-05-12 11:00:33.019179", "rc": 127, "start": "2015-05-12 11:00:32.876568", "warnings": []}
stderr: /bin/sh: sakaimo: command not found
...ignoring

TASK: [sakaimo | debug msg="debug"] ****************************************
ok: [qa-lecture01] => {
    "msg": "debug"
}

PLAY RECAP ********************************************************************
qa-lecture01               : ok=2    changed=1    unreachable=0    failed=0
```

failedをsuccessにしてみたplaybook

```
- shell: sakaimo --version
  register: result
  ignore_errors: True

- debug: msg="debug"
  when: result|success #←ここ
```

実行結果

```
PLAY [app] ********************************************************************

TASK: [sakaimo | shell sakaimo --version] *************************************
failed: [qa-lecture01] => {"changed": true, "cmd": "sakaimo --version", "delta": "0:00:00.144219", "end": "2015-05-12 11:01:29.798585", "rc": 127, "start": "2015-05-12 11:01:29.654366", "warnings": []}
stderr: /bin/sh: sakaimo: command not found
...ignoring

TASK: [sakaimo | debug msg="debug"] ****************************************
skipping: [qa-lecture01]

PLAY RECAP ********************************************************************
qa-lecture01               : ok=1    changed=1    unreachable=0    failed=0
```

resultがfailedなので、debugがskipされています。ということは`command not found`は'failed'と判定されるということね。

```
TASK: [sakaimo | debug msg="debug"] ****************************************
skipping: [qa-lecture01]
```

では成功するコマンドだったらどうなるのか。`uname`コマンドで実験。

リモートサーバーでの実行結果は

```
# uname
Linux
```

になってます。

playbook

```
- shell: uname
  register: result
  ignore_errors: True

- debug: msg="debug"
  when: result|success
```

実行結果

```
PLAY [app] ********************************************************************

TASK: [sakaimo | shell uname] *************************************************
changed: [qa-lecture01]

TASK: [sakaimo | debug msg="debug"] *******************************************
ok: [qa-lecture01] => {
    "msg": "debug"
}

PLAY RECAP ********************************************************************
qa-lecture01               : ok=2    changed=1    unreachable=0    failed=0
```

successをfailedにしてみる

playbook

```
- shell: uname
  register: result
  ignore_errors: True

- debug: msg="debug"
  when: result|failed #←ここ
```

実行結果

```
PLAY [app] ********************************************************************

TASK: [sakaimo | shell uname] *************************************************
changed: [qa-lecture01]

TASK: [sakaimo | debug msg="debug"] *******************************************
skipping: [qa-lecture01]

PLAY RECAP ********************************************************************
qa-lecture01               : ok=1    changed=1    unreachable=0    failed=0
```

これにより、unameでLinuxって返ってきてることはsuccessと判断されているってことね。


## failed_when

どうやら`failed_when`は「こういう時はfailedにしてね」って感じのコマンドのようです。
[ansible公式サイト](https://docs.ansible.com/playbooks_error_handling.html#controlling-what-defines-failure)より

例としてこんなんが載ってるので、

```
- name: this command prints FAILED when it fails
  command: /usr/bin/example-command -x -y -z
  register: command_result
  failed_when: "'FAILED' in command_result.stderr"
```

私の例で言うと

```
- shell: sakaimo --version
  register: result
  failed_when: "'command not found' in result.stderr"

- debug: msg="debug"
```

標準エラー(stderr)に「command not found」っていう文字列が含まれていたらfailedとして判定してね、って指定しました。

実行結果

```
PLAY [app] ********************************************************************

TASK: [sakaimo | shell sakaimo --version] *************************************
failed: [qa-lecture01] => {"changed": true, "cmd": "sakaimo --version", "delta": "0:00:00.139589", "end": "2015-05-12 12:07:03.049756", "failed": true, "failed_when_result": true, "rc": 127, "start": "2015-05-12 12:07:02.910167", "stdout_lines": [], "warnings": []}
stderr: /bin/sh: sakaimo : command not found

FATAL: all hosts have already failed -- aborting

PLAY RECAP ********************************************************************
           to retry, use: --limit @/root/qa-lecture-playbook.retry

qa-lecture01               : ok=0    changed=0    unreachable=0    failed=1
```

うん、想定通りエラーになりました。そしてエラーになって処理が止まっているので、debugは出力されていません。

では今度は実行結果に`errorrrrr`っていう文字列があったらfailedにするようにしてみます。

```
- shell: sakaimo --version
  register: result
  failed_when: "'errorrrrr'in result.stderr"

- debug: msg="debug"
```

にした場合、

```
PLAY [app] ********************************************************************

TASK: [sakaimo | shell sakaimo --version] *************************************
changed: [qa-lecture01]

TASK: [sakaimo | debug msg="debug"] *******************************************
ok: [qa-lecture01] => {
    "msg": "debug"
}

PLAY RECAP ********************************************************************
qa-lecture01               : ok=2    changed=1    unreachable=0    failed=0
```

となりました。`errorrrrr`は(リモートサーバーから)出力されていないので　 ~~`success`とみなされ~~　`failed`ではないとみなされ、次の処理に進んでdebugも表示されています。


では`when: result|success`を追加したらどうでしょうか。

```
- shell: sakaimo --version
  register: result
  failed_when: "'errorrrrr'in result.stderr"

- debug: msg="debug"
  when: result|success #←ここ
```


```
PLAY [app] ********************************************************************

TASK: [sakaimo | shell sakaimo --version] *************************************
changed: [qa-lecture01]

TASK: [sakaimo | debug msg="debug"] *******************************************
skipping: [qa-lecture01]

PLAY RECAP ********************************************************************
qa-lecture01               : ok=1    changed=1    unreachable=0    failed=0
```

`debug`がskipされています。つまり`result`は`failed`だったってことですね。

ということは、`failed_when`を使って「こういう時はfailedにしてね」という指定はできるけれども、これは「それ以外のときは`succsess`だよ」という指定ではないってことですよねあたりまえかｗ

ためしに

```
- shell: sakaimo --version
  register: result
  failed_when: "'errorrrrr'in result.stderr"

- debug: msg="debug"
  when: result|failed #←ここを変えた

```

にして実行したら

```
PLAY [app] ********************************************************************

TASK: [sakaimo | shell sakaimo --version] *************************************
changed: [qa-lecture01]

TASK: [sakaimo | debug msg="debug"] *******************************************
ok: [qa-lecture01] => {
    "msg": "debug"
}

PLAY RECAP ********************************************************************
qa-lecture01               : ok=2    changed=1    unreachable=0    failed=0
```

になりました。

## ということは

```
- shell: sakaimo --version
  register: result
  failed_when: "'errorrrrr'in result.stderr"

- debug: msg="debug"
  when: result|failed #←ここを変えた
```

において、

- `sakaimo --version`のリモートサーバーでの実行結果は`-bash: sakaimo: command not found`
- だから`result`には`failed`が入る (`when: result|failed` がtrue判定されてdebugが出力されているから)
- なんだけど`failed_when: "'errorrrrr'in result.stderr"`でfailedの条件を指定してしまっている
- だからこの3行全体での処理結果としては「failedじゃない」(successとはいえない)けど、`result`は`failed`なのでdebugは実行される

...あれ、頭がこんがらがってきた。