きっかけ
今回は、時々つまづく Ansible の変数の仕様に関するメモです。
最近、Amazon Linux 1(そろそろ完全に廃止しないといけない……)と Amazon Linux 2 で処理を分岐させたいことがあり、同僚にこういう書き方でできるよと教えてもらいました。
- name: Task for Amazon Linux 2 debug: msg="Task for Amazon Linux 2" when: (ansible_distribution_file_variety == "Amazon" and ansible_distribution_major_version == "2")
以前は ansible_distribution_major_version
がいずれも NA
になっていたようなのですが、現在は Amazon Linux 2 なら "2" が返されるようです。
ただ、毎回この長い when を書くのは面倒なので、以下のように playbook の冒頭で定義するようにしてみました。
- hosts: hogehoge vars: amzn2: (ansible_distribution_file_variety == "Amazon" and ansible_distribution_major_version == "2") tasks: - name: Task for Amazon Linux 2 debug: msg="Task for Amazon Linux 2" when: amzn2 - name: Task NOT for Amazon Linux 2 debug: msg="Task NOT for Amazon Linux 2" when: not amzn2
しかし、この書き方をすると、以下の DEPRECATION WARNING が出てしまいます。
TASK [Task for Amazon Linux 2] *************************************************************************************** [DEPRECATION WARNING]: evaluating (ansible_distribution_file_variety == "Amazon" and ansible_distribution_major_version == "2") as a bare variable, this behaviour will go away and you might need to add |bool to the expression in the future. Also see CONDITIONAL_BARE_VARS configuration toggle.. This feature will be removed in version 2.12. Deprecation warnings can be disabled by setting deprecation_warnings=False in ansible.cfg. ok: [hogehoge] => { "msg": "Task for Amazon Linux 2" } TASK [Task NOT for Amazon Linux 2] *********************************************************************************** skipping: [hogehoge]
なるほど。じゃあ、面倒ですけど、警告文にあるように |bool
を書き足します。
- hosts: hogehoge vars: amzn2: (ansible_distribution_file_variety == "Amazon" and ansible_distribution_major_version == "2") tasks: - name: Task for Amazon Linux 2 debug: msg="Task for Amazon Linux 2" when: amzn2|bool - name: Task NOT for Amazon Linux 2 debug: msg="Task NOT for Amazon Linux 2" when: not amzn2|bool
すると、DEPRECATION WARNING は出なくなったのですが、実行結果が期待と真逆になってしまいました……なんで?
TASK [Task for Amazon Linux 2] *************************************************************************************** skipping: [hogehoge] TASK [Task NOT for Amazon Linux 2] *********************************************************************************** ok: [hogehoge] => { "msg": "Task NOT for Amazon Linux 2" }
結論
文字列比較の結果を代入する際に "{{ }}"
で囲むと、文字列ではなく boolean として代入されます。DEPRECATION WARNING にある |bool
は不要です。
上の例で言うと、
vars: amzn2: "{{ ansible_distribution_file_variety == \"Amazon\" and ansible_distribution_major_version == \"2\" }}"
と書くと、以下のように DEPRECATION WARNING が出ず、実行結果も期待通りになりました。
TASK [Task for Amazon Linux 2] *************************************************************************************** ok: [hogehoge] => { "msg": "Task for Amazon Linux 2" } TASK [Task NOT for Amazon Linux 2] *********************************************************************************** skipping: [hogehoge]
実験
文字列比較の内容は何でもいいので、("1" == "1")
という単純な条件式で実験します。以下の実験は、Ansible 2.9 の最新版(2.9.6)で行いました。
いろいろな方法での代入
まず、代入した値をそのまま debug モジュールで出力してみます。
playbook:
- hosts: localhost connection: local vars: var1: True var2: False var3: "True" var4: "False" var5: ("1" == "1") var6: ("1" == "1")|bool var7: "{{ ('1' == '1') }}" var8: "{{ ('1' == '1')|bool }}" tasks: - name: Print vars debug: msg="{{ item }}" with_items: - "{{ var1 }}" - "{{ var2 }}" - "{{ var3 }}" - "{{ var4 }}" - "{{ var5 }}" - "{{ var6 }}" - "{{ var7 }}" - "{{ var8 }}"
結果:
% ansible-playbook test.yml [WARNING]: No inventory was parsed, only implicit localhost is available [WARNING]: provided hosts list is empty, only localhost is available. Note that the implicit localhost does not match 'all' PLAY [localhost] ***************************************************************************************************** TASK [Gathering Facts] *********************************************************************************************** ok: [localhost] TASK [Print vars] **************************************************************************************************** ok: [localhost] => (item=True) => { "msg": true } ok: [localhost] => (item=False) => { "msg": false } ok: [localhost] => (item=True) => { "msg": true } ok: [localhost] => (item=False) => { "msg": false } ok: [localhost] => (item=("1" == "1")) => { "msg": "(\"1\" == \"1\")" } ok: [localhost] => (item=("1" == "1")|bool) => { "msg": "(\"1\" == \"1\")|bool" } ok: [localhost] => (item=True) => { "msg": true } ok: [localhost] => (item=True) => { "msg": true } PLAY RECAP *********************************************************************************************************** localhost : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
変数名 | 代入方法 | 結果 |
---|---|---|
var1 | True を代入 | boolean true |
var2 | False を代入 | boolean false |
var3 | "True" という文字列を代入 | boolean true |
var4 | "False" という文字列を代入 | boolean false |
var5 | 条件式を代入 | 条件式の文字列 |
var6 | 条件式を |bool 付きで代入 |
条件式の文字列 |
var7 | 条件式を "{{ }}" で囲んで代入 |
boolean true |
var8 | 条件式に |bool を付けた上で "{{ }}" で囲んで代入 |
boolean true |
条件式を "{{ }}"
で囲めば、その結果が boolean として代入されるし、囲まなければ文字列として代入されることがわかります。いずれにせよ、代入の時点で |bool
の有無は全く関係ないようです。
when 節への文字列の渡し方
代入時に boolean になっている場合は当然成功するので、ここからは代入時に文字列になってしまった場合のみを考えます。
|bool
の有無と、結果が True になるかどうかの4通りで実験しました。Playbook が長くなるので、以下には var1 のタスクだけ記載しています。
--- - hosts: localhost connection: local vars: var1: ("1" == "1") var2: ("1" == "1")|bool var3: ("1" == "2") var4: ("1" == "2")|bool tasks: - name: 1. var1 should be printed debug: msg="{{ var1 }}" when: var1 - name: 2. var1 should NOT be printed debug: msg="{{ var1 }}" when: not var1 - name: 3. var1 should be printed debug: msg="{{ var1 }}" when: var1|bool - name: 4. var1 should NOT be printed debug: msg="{{ var1 }}" when: not var1|bool - name: 5. var1 should NOT be printed debug: msg="{{ var1 }}" when: not (var1|bool) - name: 6. var1 should be printed debug: msg="{{ var1 }}" when: "{{ var1 }}" - name: 7. var1 should NOT be printed debug: msg="{{ var1 }}" when: "{{ not var1 }}" # Template error # - name: 8. var1 should NOT be printed # debug: msg="{{ var1 }}" # when: not "{{ var1 }}" - name: 9. var1 should be printed debug: msg="{{ var1 }}" when: "{{ var1|bool }}" - name: 10. var1 should NOT be printed debug: msg="{{ var1 }}" when: "{{ not var1|bool }}" - name: 11. var1 should NOT be printed debug: msg="{{ var1 }}" when: not "{{ var1|bool }}"
結果はこうなりました。
% ansible-playbook test.yml [WARNING]: No inventory was parsed, only implicit localhost is available [WARNING]: provided hosts list is empty, only localhost is available. Note that the implicit localhost does not match 'all' PLAY [localhost] ***************************************************************************************************** TASK [Gathering Facts] *********************************************************************************************** ok: [localhost] TASK [1. var1 should be printed] ************************************************************************************* [DEPRECATION WARNING]: evaluating u'var1' as a bare variable, this behaviour will go away and you might need to add |bool to the expression in the future. Also see CONDITIONAL_BARE_VARS configuration toggle. This feature will be removed in version 2.12. Deprecation warnings can be disabled by setting deprecation_warnings=False in ansible.cfg. ok: [localhost] => { "msg": "(\"1\" == \"1\")" } TASK [2. var1 should NOT be printed] ********************************************************************************* skipping: [localhost] TASK [3. var1 should be printed] ************************************************************************************* skipping: [localhost] TASK [4. var1 should NOT be printed] ********************************************************************************* ok: [localhost] => { "msg": "(\"1\" == \"1\")" } TASK [5. var1 should NOT be printed] ********************************************************************************* ok: [localhost] => { "msg": "(\"1\" == \"1\")" } TASK [6. var1 should be printed] ************************************************************************************* [WARNING]: conditional statements should not include jinja2 templating delimiters such as {{ }} or {% %}. Found: {{ var1 }} ok: [localhost] => { "msg": "(\"1\" == \"1\")" } TASK [7. var1 should NOT be printed] ********************************************************************************* [WARNING]: conditional statements should not include jinja2 templating delimiters such as {{ }} or {% %}. Found: {{ not var1 }} skipping: [localhost] TASK [9. var1 should be printed] ************************************************************************************* [WARNING]: conditional statements should not include jinja2 templating delimiters such as {{ }} or {% %}. Found: {{ var1|bool }} skipping: [localhost] TASK [10. var1 should NOT be printed] ******************************************************************************** [WARNING]: conditional statements should not include jinja2 templating delimiters such as {{ }} or {% %}. Found: {{ not var1|bool }} ok: [localhost] => { "msg": "(\"1\" == \"1\")" } TASK [11. var1 should NOT be printed] ******************************************************************************** [WARNING]: conditional statements should not include jinja2 templating delimiters such as {{ }} or {% %}. Found: not "{{ var1|bool }}" skipping: [localhost]
まとめると、1番の when: varX
または 6番の when: {{ varX }}
の書き方をすると、DEPRECATION WARNING は出るが、想定通りの結果になりました。それ以外は、文字列の中身に関わらず同じ結果になりました。
when の書き方 | var1 | var2 | var3 | var4 |
---|---|---|---|---|
1. varX |
ok | ok | skipping | skipping |
2. not varX |
skipping | skipping | skipping | skipping |
3. varX|bool |
skipping | skipping | skipping | skipping |
4. not varX|bool |
ok | ok | ok | ok |
5. not (varX|bool) |
ok | ok | ok | ok |
6. {{ varX }} |
ok | ok | skipping | skipping |
7. {{ not varX }} |
skipping | skipping | skipping | skipping |
9. {{ varX|bool }} |
skipping | skipping | skipping | skipping |
10. {{ not varX|bool }} |
ok | ok | ok | ok |
11. not {{ varX|bool }} |
skipping | skipping | skipping | skipping |
結局、この DEPRECATION WARNING で |bool
を付けろ、と言っている意味はわからずじまいでした。誰か詳しい人いたら教えてください……。
[DEPRECATION WARNING]: evaluating (ansible_distribution_file_variety == "Amazon" and ansible_distribution_major_version == "2") as a bare variable, this behaviour will go away and you might need to add |bool to the expression in the future. Also see CONDITIONAL_BARE_VARS configuration toggle.. This feature will be removed in version 2.12. Deprecation warnings can be disabled by setting deprecation_warnings=False in ansible.cfg.