Ansible v2: Dynamic Include Problemsを検証する①

# **Dynamic Include Problems**

以前このような翻訳記事を書きました。

**[Ansible 2.0リリース！](http://qiita.com/t_nakayama0714/items/0d602a4fae60aa78b6ef)**

2016年1月に正式リリースされたAnsible v2のリリース記事ですが、その中で **Dynamic Include Problems** というものが言及されていました。

> **Dynamic Include Problems**
> Due to the fact that include tasks are now evaluated at run time, Ansible no longer knows ahead of time about any tasks contained within files which have yet to be included. This presents problems in several situations (which we intend to correct in future versions):
>
> Tags on tasks are not seen until the include is processed, so tags should now be specified on the include task rather than on individual tasks within the include, otherwise they will not be seen. Likewise, the --list-tags option will not show tags contained only in the include files.

この部分に対しては、下記のように訳しています。

> **Dynamic Include Problems**
> 今回のリファクタリングを通じ、includeされるタスクは実行時に評価されることになり、Ansibleはincludeして実行されるタスクを事前に知ることができなくなりました。
このことはいくつかの状況において問題(将来のバージョンでは修正される予定です)を引き起こしてきます。
>
> タスクに設定されたタグはincludeが実行されるまで見られることがないため、includeされる個々のタスクではなくincludeに対してタグを設定しないと評価されません。--list-tags オプションについても同様です。

とのことなので、

「 **include内部のタグが評価されない** 」

という問題があるものと解釈していました。

# **それってホント?**

先日、このような記事を見つけました。

**[実サービスで利用中のAnsible 1系をAnsible 2.0にアップグレードすると、何が起こるか？](http://recruit.gmo.jp/engineer/jisedai/blog/upgrade-to-ansible-v2/)**

Ansible v1からv2への移行について述べた記事なのですが、v2について触れる際に上の **Dynamic Include Problems** に注目しています。

> このなかで個人的に特に気になったのは “Dynamic Include Problems” の部分でした。
> (略)
> この部分については原文を読んでも、include されたファイルに書かれているタグやハンドラは実行時に評価されるのか、実行時にも無視されてしまうのか、いまいちよく分かりませんでした。

で、こちらの方は実際に検証をすることで、

> 結論としては、**include されるファイル内の各タスクに tags を付与しても、実用上は問題ない** ようです。その一方で、**include されるファイル内で定義されたハンドラはエラーも出さずに無視** されました。こちらは、移行前の書き換えが必須のようです。

という結論に達しています。
あのAnsible 2.0リリース翻訳の記事は意外と多くの方に読んでいただいているようなので、これはいかんと思い僕のほうでも検証してみようと思います。

# **検証準備**

検証にあたり、いろいろ準備します。

## **検証環境**

```
[root@controller ~]# cat /etc/centos-release
CentOS release 6.7 (Final)

[root@controller ~]# python --version
Python 2.6.6
```

Ansibleはいくつものバージョンを切り替えたいのでgithubからオリジナルを持ってきます。

```
[root@controller ~]# cd /opt
[root@controller opt]# git clone https://github.com/ansible/ansible.git
Initialized empty Git repository in /opt/ansible/.git/
remote: Counting objects: 113219, done.
remote: Compressing objects: 100% (72/72), done.
remote: Total 113219 (delta 34), reused 6 (delta 6), pack-reused 113135
Receiving objects: 100% (113219/113219), 39.52 MiB | 600 KiB/s, done.
Resolving deltas: 100% (67362/67362), done.
```

## **検証対象**

検証対象はalpha, betaを除くv2のものとします。

```
[root@controller ansible]# git tag -l | grep v2 | grep -Ev "alpha|beta"
v2.0.0-0.6.rc1
v2.0.0-0.7.rc2
v2.0.0-0.8.rc3
v2.0.0-0.9.rc4
v2.0.0.0-1
v2.0.0.1-1
v2.0.0.2-1
v2.0.1.0-0.1.rc1
v2.0.1.0-0.2.rc2
v2.0.1.0-1
```

バイナリの切り替えはタグを指定した **git checkout** と同梱されている **hacking/env-setup** を組み合わせて使います。

```bash:change_ver.sh
#!/bin/bash

VERSION=${1}

cd /opt/ansible
git checkout refs/tags/${VERSION}
source ./hacking/env-setup > /dev/null
ansible --version
```

## **検証項目**

下記のplaybookを使用します。

```main.yml
- hosts: targets
  user: root
  tasks:
    - debug: msg="main always play"
      tags:
        - always

    - debug: msg="main no play"

    - debug: msg="main play"
      tags:
        - main

    - include: include.yml
      tags:
        - include
```

```include.yml
- debug: msg="sub always play"
  tags:
    - always

- debug: msg="sub no play"

- debug: msg="sub play"
  tags:
    - sub
```

検証項目は下記です。

- **--list-tagsで出力されるタグ**
- **--tags mainで実行されるタスク**
- **--tags noneで実行されるタスク**

# **検証結果**

## **--list-tags**

シェル芸っぽくざっくり回します。
main.ymlとinclude.ymlでは [always, main, sub, include] の4種類が明示的に指定されているので、**4種類出れば正常** です。

```
[root@controller ansible]# for ver in $(cat target-ver.list) ; do echo "# ${ver}" ; ./change_ver.sh "${ver}" 2&>1  > /dev/null ; ansible --version | grep -E "^ansible" ; ansible-playbook -i inventory/hosts main.yml --list-tags | grep "TASK TAGS" ; echo "" ; done
# v2.0.0-0.6.rc1
ansible 2.0.0 (detached HEAD f2225395f9) last updated 2016/03/17 17:27:36 (GMT +100)

# v2.0.0-0.7.rc2
ansible 2.0.0 (detached HEAD cc98528ecb) last updated 2016/03/17 17:27:37 (GMT +100)

# v2.0.0-0.8.rc3
ansible 2.0.0 (detached HEAD a2120a3d63) last updated 2016/03/17 17:27:38 (GMT +100)
      TASK TAGS: [always, include, main]

# v2.0.0-0.9.rc4
ansible 2.0.0 (detached HEAD 502ad88506) last updated 2016/03/17 17:27:38 (GMT +100)
      TASK TAGS: [always, include, main]

# v2.0.0.0-1
ansible 2.0.0.0 (detached HEAD 06ec219982) last updated 2016/03/17 17:27:39 (GMT +100)
      TASK TAGS: [always, include, main]

# v2.0.0.1-1
ansible 2.0.0.1 (detached HEAD a50d1ea756) last updated 2016/03/17 17:27:40 (GMT +100)
      TASK TAGS: [always, include, main]

# v2.0.0.2-1
ansible 2.0.0.2 (detached HEAD 7de237c5a1) last updated 2016/03/17 17:27:41 (GMT +100)
      TASK TAGS: [always, include, main]

# v2.0.1.0-0.1.rc1
ansible 2.0.1.0 (detached HEAD 870a4b8dc1) last updated 2016/03/17 17:27:41 (GMT +100)
      TASK TAGS: [always, include, main]

# v2.0.1.0-0.2.rc2
ansible 2.0.1.0 (detached HEAD bec698052f) last updated 2016/03/17 17:27:42 (GMT +100)
      TASK TAGS: [always, include, main]

# v2.0.1.0-1
ansible 2.0.1.0 (detached HEAD bb6cadefa2) last updated 2016/03/17 17:27:43 (GMT +100)
      TASK TAGS: [always, include, main]
```

全体的に見ると、TASK TAGSとして出ているのが **[always, include, main]** なので、**sub** タグが足りません。
subタグは確かにincludeされるタスクに書いてあるタグであるため、リリースノートの通り、反応していないことが分かります。

一方で、そもそも `v2.0.0-0.6.rc1`, `v2.0.0-0.6.rc2` では出力がうまくされていません。
`grep "TASK TAGS"` を外して出力を見てみます。

```
[root@controller ansible]# for ver in $(cat tmp.list) ; do echo "# ${ver}" ; ./change_ver.sh "${ver}" 2&>1  > /dev/null ; ansible --version | grep -E "^ansible" ; ansible-playbook -i inventory/hosts main.yml --list-tags ; echo "" ; done
# v2.0.0-0.6.rc1
ansible 2.0.0 (detached HEAD f2225395f9) last updated 2016/03/17 17:33:26 (GMT +100)

playbook: main.yml

  PLAY: #1
    tasks:
      TASK: meta
      TASK: debug msg=main always play    TAGS: [always]
      TASK: debug msg=main no play
      TASK: debug msg=main play    TAGS: [main]
      TASK: include     TAGS: [include]
      TASK: meta
      TASK: meta

# v2.0.0-0.7.rc2
ansible 2.0.0 (detached HEAD cc98528ecb) last updated 2016/03/17 17:33:26 (GMT +100)

playbook: main.yml

  PLAY: #1
    tasks:
      TASK: meta (flush_handlers)
      TASK: debug msg=main always play    TAGS: [always]
      TASK: debug msg=main no play
      TASK: debug msg=main play    TAGS: [main]
      TASK: include     TAGS: [include]
      TASK: meta (flush_handlers)
      TASK: meta (flush_handlers)
```

どうやら `v2.0.0-0.7.rc3` から `--list-tags` による出力フォーマットが変わったんですね。
とはいえ、TAGSとして反応しているのはやはりsub以外の3つなので挙動的には同じなようです。

## **--tags main**

**main** タグを指定する場合、リリースノートの通りであれば、include.yml内のタグが評価されず、バッサリ実行されなくなるはずです。

```
[root@controller ansible]# for ver in $(cat target-ver.list) ; do echo "# ${ver}" ; ./change_ver.sh "${ver}" 2&>1  > /dev/null ; ansible --version | grep -E "^ansible" ; ansible-playbook -i inventory/hosts main.yml --tags main | grep msg ; echo "" ; done
# v2.0.0-0.6.rc1
ansible 2.0.0 (detached HEAD f2225395f9) last updated 2016/03/17 17:39:13 (GMT +100)
TASK [debug msg=main always play] **********************************************
    "msg": "main always play"
TASK [debug msg=main play] *****************************************************
    "msg": "main play"
TASK [debug msg=sub always play] ***********************************************
    "msg": "sub always play"

# v2.0.0-0.7.rc2
ansible 2.0.0 (detached HEAD cc98528ecb) last updated 2016/03/17 17:39:14 (GMT +100)
TASK [debug msg=main always play] **********************************************
    "msg": "main always play"
TASK [debug msg=main play] *****************************************************
    "msg": "main play"
TASK [debug msg=sub always play] ***********************************************
    "msg": "sub always play"

# v2.0.0-0.8.rc3
ansible 2.0.0 (detached HEAD a2120a3d63) last updated 2016/03/17 17:39:15 (GMT +100)
    "msg": "main always play"
    "msg": "main play"
    "msg": "sub always play"

# v2.0.0-0.9.rc4
ansible 2.0.0 (detached HEAD 502ad88506) last updated 2016/03/17 17:39:16 (GMT +100)
    "msg": "main always play"
    "msg": "main play"
    "msg": "sub always play"

# v2.0.0.0-1
ansible 2.0.0.0 (detached HEAD 06ec219982) last updated 2016/03/17 17:39:17 (GMT +100)
    "msg": "main always play"
    "msg": "main play"
    "msg": "sub always play"

# v2.0.0.1-1
ansible 2.0.0.1 (detached HEAD a50d1ea756) last updated 2016/03/17 17:39:19 (GMT +100)
    "msg": "main always play"
    "msg": "main play"
    "msg": "sub always play"

# v2.0.0.2-1
ansible 2.0.0.2 (detached HEAD 7de237c5a1) last updated 2016/03/17 17:39:20 (GMT +100)
    "msg": "main always play"
    "msg": "main play"
    "msg": "sub always play"

# v2.0.1.0-0.1.rc1
ansible 2.0.1.0 (detached HEAD 870a4b8dc1) last updated 2016/03/17 17:39:21 (GMT +100)
    "msg": "main always play"
    "msg": "main play"
    "msg": "sub always play"

# v2.0.1.0-0.2.rc2
ansible 2.0.1.0 (detached HEAD bec698052f) last updated 2016/03/17 17:39:22 (GMT +100)
    "msg": "main always play"
    "msg": "main play"
    "msg": "sub always play"

# v2.0.1.0-1
ansible 2.0.1.0 (detached HEAD bb6cadefa2) last updated 2016/03/17 17:39:23 (GMT +100)
    "msg": "main always play"
    "msg": "main play"
    "msg": "sub always play"
```

こちらもやはり `v2.0.0-0.6.rc1`, `v2.0.0-0.6.rc2` の出力が他と違いますが、内容は全て同じです。
想定では `"msg": "sub always play"` が実行されないはずだったのですが、**ちゃんと include されるタスクにおいてもタグを適切に評価されている** ようです。

## **--tags none**

では最後です。存在しないタグ、noneを指定して動かしてみます。
こちらでもリリースノートの通りであれば、include自体が働かないため、**include内部タスクが全て実行されないはず** です。

```
[root@controller ansible]# for ver in $(cat target-ver.list) ; do echo "# ${ver}" ; ./change_ver.sh "${ver}" 2&>1  > /dev/null ; ansible --version | grep -E "^ansible" ; ansible-playbook -i inventory/hosts main.yml --tags none | grep msg ; echo "" ; done
# v2.0.0-0.6.rc1
ansible 2.0.0 (detached HEAD f2225395f9) last updated 2016/03/17 17:46:57 (GMT +100)
TASK [debug msg=main always play] **********************************************
    "msg": "main always play"
TASK [debug msg=sub always play] ***********************************************
    "msg": "sub always play"

# v2.0.0-0.7.rc2
ansible 2.0.0 (detached HEAD cc98528ecb) last updated 2016/03/17 17:46:58 (GMT +100)
TASK [debug msg=main always play] **********************************************
    "msg": "main always play"
TASK [debug msg=sub always play] ***********************************************
    "msg": "sub always play"

# v2.0.0-0.8.rc3
ansible 2.0.0 (detached HEAD a2120a3d63) last updated 2016/03/17 17:46:59 (GMT +100)
    "msg": "main always play"
    "msg": "sub always play"

# v2.0.0-0.9.rc4
ansible 2.0.0 (detached HEAD 502ad88506) last updated 2016/03/17 17:47:01 (GMT +100)
    "msg": "main always play"
    "msg": "sub always play"

# v2.0.0.0-1
ansible 2.0.0.0 (detached HEAD 06ec219982) last updated 2016/03/17 17:47:02 (GMT +100)
    "msg": "main always play"
    "msg": "sub always play"

# v2.0.0.1-1
ansible 2.0.0.1 (detached HEAD a50d1ea756) last updated 2016/03/17 17:47:03 (GMT +100)
    "msg": "main always play"
    "msg": "sub always play"

# v2.0.0.2-1
ansible 2.0.0.2 (detached HEAD 7de237c5a1) last updated 2016/03/17 17:47:04 (GMT +100)
    "msg": "main always play"
    "msg": "sub always play"

# v2.0.1.0-0.1.rc1
ansible 2.0.1.0 (detached HEAD 870a4b8dc1) last updated 2016/03/17 17:47:05 (GMT +100)
    "msg": "main always play"
    "msg": "sub always play"

# v2.0.1.0-0.2.rc2
ansible 2.0.1.0 (detached HEAD bec698052f) last updated 2016/03/17 17:47:06 (GMT +100)
    "msg": "main always play"
    "msg": "sub always play"

# v2.0.1.0-1
ansible 2.0.1.0 (detached HEAD bb6cadefa2) last updated 2016/03/17 17:47:08 (GMT +100)
    "msg": "main always play"
    "msg": "sub always play"
```

こちらもincludeされるタスクの中でも、実行されるべきものは実行されているため **タグ指定通りの挙動** をしているようです。

# **まとめ**

タグ機能については下記のことがわかりました。

- **include内部に付けたタグでも、実行には問題がない**
- **include内部に付ける場合、--list-tagsでタグが拾えなくなる**

ハンドラのほうについても後日検証するのと、

「 **じゃあ原文では一体何を言ってるの?** 」

という部分はもう少し調べてみます。