てくなべ (tekunabe)

ansible / network / automation

[Ansible] YAML のキー重複時の挙動を DUPLICATE_YAML_DICT_KEY で設定する


これは Ansible 2 Advent Calendar 2019 の7日目の記事です。

はじめに

Playbook は YAML というフォーマットで記述します。ときどき、ミスによって YAML のディクショナリのキーの重複して書いてしまうこともあるかと思います。 これまではキーの重複時は、警告が表示された上で処理が継続するという仕様で固定でした。そいのため、あとから定義した値が優先されて実行されるなどの想定外の結果を引き起こしてしまう恐れがありました。個人的には、いっそのことエラーにしてほしいなと思っていました。

Ansible 2.9 で追加された、DUPLICATE_YAML_DICT_KEY という設定で選べるようになりました。

この記事では、簡単な例をもとに説明します。

  • 動作確認環境
    • Ansible 2.9.1


■ 選べる警告類の種類

DUPLICATE_YAML_DICT_KEYでは、キーの重複時の警告類を3種類の中から選べます

設定値 動作
error エラーを表示して、Playbook の実行を中断。
warn 警告(WARNING)表示して、Playbook の実行を続行。最後に定義した値を利用。デフォルトの設定。
False 特に何も表示せずに、Playbook の実行を続行。最後に定義した値を利用。


■ 検証

errorwarnFalse をそれぞれ検証します。

検証用 Playbook

以下の 2 種類の Playbook を検証に利用します。

Playbook1: モジュールオプションの重複

debug モジュールの msg オプションを意図的に重複させています。

- hosts: localhost
  gather_facts: no

  tasks:
    - name: debug test
      debug:
        msg: ryukin
        msg: demekin

Playbook2: 変数定義の重複

変数 debug の定義を意図的に重複させています。

- hosts: localhost
  gather_facts: no

  vars:
    kind: ryukin
    kind: demekin

  tasks:
    - name: debug test
      debug:
        msg: "{{ kind }}"

検証1: error の場合

DUPLICATE_YAML_DICT_KEYerror に指定する場合です。

キーの重複を検出した場合はエラーで処理が止まる設定です。

ansible.cfg で以下の指定します。

[defaults]
duplicate_dict_key = error

以下、各 Playbook の実行結果です。

  • 「Playbook1: モジュールオプションの重複」の実行
$ ansible-playbook -i localhost, duplicate.yml
ERROR! Unexpected Exception, this is probably a bug: 'NoneType' object has no attribute 'line'

ちょっとピンとこないメッセージですが、ERROR といえば ERROR です。

  • 「Playbook2: 変数定義の重複」の実行
$ ansible-playbook -i localhost, duplicate.yml
ERROR! Unexpected Exception, this is probably a bug: 'NoneType' object has no attribute 'line'

こちらも Playbook 1 と同じでした。

設定を判断してるのはこのあたりのコードようです

ここまでが、 error の検証でした。

検証2: warn の場合 (デフォルト)

DUPLICATE_YAML_DICT_KEYwarn に指定する場合です。デフォルトがこれなので、特に指定しなくても同じです。

キーの重複を検出した場合は WARNING が表示され、処理は継続する設定です。最後に定義した値が利用されます。

今回は明示的に ansible.cfg で以下の指定します。

[defaults]
duplicate_dict_key = warn
  • 「Playbook1: モジュールオプションの重複」の実行
$ ansible-playbook -i localhost, key_dup_option.yml
[WARNING]: While constructing a mapping from /vagrant/a29/key_dup_option.yml, line 7, column 9, found a duplicate dict key (msg). Using last defined value only.


PLAY [localhost] **********************************************************************************

TASK [debug test] *********************************************************************************
ok: [localhost] => {
    "msg": "demekin"     ← 最後に定義した msg の値
}

PLAY RECAP ****************************************************************************************
localhost                  : ok=1    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

msg が重複していたので、最後に定義した値が使われたという WARNING が表示され、処理自体は実行されました。

  • 「Playbook2: 変数定義の重複」の実行
]$ ansible-playbook -i localhost, key_dup_var.yml
[WARNING]: While constructing a mapping from /vagrant/a29/key_dup_var.yml, line 6, column 5, found a duplicate dict key (kind). Using last defined value only.


PLAY [localhost] **********************************************************************************

TASK [debug test] *********************************************************************************
ok: [localhost] => {
    "msg": "demekin"    ← 最後に定義した kind 変数の値
}

PLAY RECAP ****************************************************************************************
localhost                  : ok=1    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

こちらも Playbook 1 と同じでした。

ここまでが、 warn (デフォルト) の検証でした。

検証3:False の場合

最後に、DUPLICATE_YAML_DICT_KEYFalse に指定する場合です。

キーの重複を検出しても、エラーにもならず WARNING も表示されず、何事もなかったように処理をする設定です。最後に定義した値が利用されます。

ansible.cfg で以下の指定します。

[defaults]
duplicate_dict_key = False
  • 「Playbook1: モジュールオプションの重複」の実行
$ ansible-playbook -i localhost, key_dup_option.yml

PLAY [localhost] **********************************************************************************

TASK [debug test] *********************************************************************************
ok: [localhost] => {
    "msg": "demekin"    ← 最後に定義した msg の値
}

PLAY RECAP ****************************************************************************************
localhost                  : ok=1    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

何事もなかったように実行されました。最後に定義した msg: demekin が表示されました。

  • 「Playbook2: 変数定義の重複」の実行
$ ansible-playbook -i localhost, key_dup_var.yml

PLAY [localhost] **********************************************************************************

TASK [debug test] *********************************************************************************
ok: [localhost] => {
    "msg": "demekin"    ← 最後に定義した kind 変数の値
}

PLAY RECAP ****************************************************************************************
localhost                  : ok=1    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

こちらも Playbook 1 と同じでした。

なお、コードを見た感じは、実際は False だけでなく。「error でも warn でもない値」が設定された場合に、何も表示しないといった仕様のようです。ドキュメント上

These warnings can be silenced by adjusting this setting to False. と記載があったので False で検証しました。

ここまでが、 False の検証でした。


■ まとめ

YAML のキーが重複している場合の、挙動を設定できる DUPLICATE_YAML_DICT_KEY について検証しました。

-(再掲)

設定値 動作
error エラーを表示して、Playbook の実行を中断。
warn 警告(WARNING)表示して、Playbook の実行を続行。最後に定義した値を利用。デフォルトの設定。
False 特に何も表示せずに、Playbook の実行を続行。最後に定義した値を利用。

補足

できることなら、この設定に頼らず、YAML のキーの重複は実行前に気づいて修正しておきたいものです。 たとえば VSCode など多機能なエディタを利用すれば、以下のように気づかせてくれます。

f:id:akira6592:20191129175238p:plain
VSCode 上で 重複キーを検出