はじめに
Ansible 2.11で出ていたargument_specs
を今更ながら知ったのでどのようなものか試してみました。
Ansible Roleの変数チェックの問題点
AnsibleでRoleを使う場合、基本的にはパラメータを渡してその値をもとに何らかの処理を実行します。
しかし、Roleとそれを利用するPlaybook開発者は別の場合が多いので、Playbook開発者側はパラメータをよく理解しないまま実行してRole実行途中にエラーが発生するということがよくありました。
筆者もよく悩まされていて、Role実行時の最初のタスクで以下コードのようなassertを実行してパラメータチェックすることで対応していました。
- ansible.builtin.assert:
that:
- var1 is defined
- var1 is string
- var1 | length > 0
しかし、冗長なコードとなりログにassertのタスクが大量に出てくるのが課題となっていました。
Role引数の検証定義
Ansible 2.11より、Roleのメタ情報としてRole実行時のパラメータ定義ができるようになっています。
ロールディレクトリのmeta/argument_specs.yml
にパラメータ定義を記載しておくことで、Role実行時にPlaybook実行変数がパラメータ定義の条件を満たしているか検証してくれます。
検証に失敗すると、Roleを実行せずにエラーを出してくれるため何が理由でエラーが起こったのか分かりやすいです。
以下のようにargument_specs.main.options
配下に各パラメータの定義を行います。
---
argument_specs:
main:
options:
myapp_int:
type: "int"
required: false
default: 42
description:
- "The integer value, defaulting to 42."
- "This is a second paragraph."
typeは以下型を識別することができ、list, dictの場合はさらにその配下の型も定義できます。
- str
- list
- dict
- bool
- int
- float
- path
- raw
- jsonarg
- json
- bytes
- bits
注意点として
default
は、ドキュメント用のものであるため、このファイルで定義していても自動で作られるものでないため注意が必要です。
個別にdefaults/main.yml
などで定義が必要となります。
また、調べた限りだとmapのkey名をワイルドカードとしての定義はできませんでした。
例えば以下のように、os_familyの種類名をmap keyにしてその配下がlistであることのチェックはできません。
※ほかにやり方あればコメントいただきたいです
# チェック定義可能
os_family:
- { name: rhel, distro: [rhel, centos, oraclelinux] }
- { name: debian, distro: [debian, ubuntu] }
# チェック定義不可
os_family:
rhel: [rhel, centos, oraclelinux]
debian: [debian, ubuntu]
Playbook実行時の結果
ファイル構成は以下の通り
./
├── ansible.cfg
├── playbook.yml
└── roles
└── sukina_ranking_happyou
├── meta
│ └── argument_specs.yml
└── tasks
└── main.yml
ファイルの中身は、以下の通り。
Playbook
---
- name: Playbook
hosts: localhost
gather_facts: false
vars:
doragon_name: 好きな惣菜発表ドラゴン
happyou_category: ネットワーク機器
# happyou_category: サーバ機器
category_rankings:
- category: 惣菜
ranking:
- からあげ
- ハンバーグ
- とんかつ
- 肉を甘辛く炒めたやつ
- category: ネットワーク機器
ranking:
- PaloAlto
- Juniper
- F5
- ASA以外のCisco機全般
roles:
- sukina_ranking_happyou
Roleタスク
---
- name: "{{ doragon_name }}が好きな{{ happyou_category }}を発表します"
ansible.builtin.debug:
msg: |-
┌─────────────────────────────┐
│ {{ item }}
└─────────────────────────────┘
Oo。.
<<
___( ^ )
___ )____
| | _ ] _ \\
| |_| )
| |_| \\
""( ) _ _ ^~
_ ( _)
loop: "{{ category_rankings | json_query(query) }}"
vars:
query: "[?category==`{{ happyou_category }}`].ranking[]"
argument_specs
---
argument_specs:
main:
short_description: Sukina_ranking_happyou
description:
- 好きな何かのランキングを発表します
author:
- miyuk
options:
doragon_name:
type: str
required: true
description: 何かを発表するドラゴンの名前
happyou_category:
type: str
choices:
- 惣菜
- ネットワーク機器
required: true
description: 発表する好きなカテゴリ名
category_rankings:
type: list
elements: dict
required: true
description: 好きなカテゴリ
options:
category:
type: str
choices:
- 惣菜
- ネットワーク機器
required: true
description: カテゴリ名
ranking:
type: list
elements: str
required: true
description: ランキング
検証成功・失敗時のPlaybook結果は以下の通り。
実行結果(検証成功)
happyou_category: ネットワーク機器
で実行
PLAY [Playbook] ***************************************************************************************************
TASK [sukina_ranking_happyou : Validating arguments against arg spec 'main' - Sukina_ranking_happyou] *************
ok: [localhost] => changed=false
msg: The arg spec validation passed
validate_args_context:
argument_spec_name: main
name: sukina_ranking_happyou
path: /root/work/test/roles/sukina_ranking_happyou
type: role
TASK [sukina_ranking_happyou : 好きな惣菜発表ドラゴンが好きなネットワーク機器を発表します] ************************
ok: [localhost] => (item=PaloAlto) =>
msg: |-
┌─────────────────────────────┐
│ PaloAlto
└─────────────────────────────┘
Oo。.
<<
___( ^ )
___ )____
| | _ ] _ \\
| |_| )
| |_| \\
""( ) _ _ ^~
_ ( _)
ok: [localhost] => (item=Juniper) =>
msg: |-
┌─────────────────────────────┐
│ Juniper
└─────────────────────────────┘
Oo。.
<<
___( ^ )
___ )____
| | _ ] _ \\
| |_| )
| |_| \\
""( ) _ _ ^~
_ ( _)
ok: [localhost] => (item=F5) =>
msg: |-
┌─────────────────────────────┐
│ F5
└─────────────────────────────┘
Oo。.
<<
___( ^ )
___ )____
| | _ ] _ \\
| |_| )
| |_| \\
""( ) _ _ ^~
_ ( _)
ok: [localhost] => (item=ASA以外のCisco機全般) =>
msg: |-
┌─────────────────────────────┐
│ ASA以外のCisco機全般
└─────────────────────────────┘
Oo。.
<<
___( ^ )
___ )____
| | _ ] _ \\
| |_| )
| |_| \\
""( ) _ _ ^~
_ ( _)
PLAY RECAP ********************************************************************************************************
localhost : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
実行結果(検証失敗)
happyou_category: ネットワーク機器
で実行
PLAY [Playbook] ***************************************************************************************************
TASK [sukina_ranking_happyou : Validating arguments against arg spec 'main' - Sukina_ranking_happyou] *************
fatal: [localhost]: FAILED! => changed=false
argument_errors:
- 'value of happyou_category must be one of: 惣菜, ネットワーク機器, got: サーバ機器'
argument_spec_data:
category_rankings:
description: 好きなカテゴリ
elements: dict
options:
category:
choices:
- 惣菜
- ネットワーク機器
description: カテゴリ名
required: true
type: str
ranking:
description: ランキング
elements: str
required: true
type: list
required: true
type: list
doragon_name:
description: 何かを発表するドラゴンの名前
required: true
type: str
happyou_category:
choices:
- 惣菜
- ネットワーク機器
description: 発表する好きなカテゴリ名
required: true
type: str
msg: |-
Validation of arguments failed:
value of happyou_category must be one of: 惣菜, ネットワーク機器, got: サーバ機器
validate_args_context:
argument_spec_name: main
name: sukina_ranking_happyou
path: /root/work/test/roles/sukina_ranking_happyou
type: role
PLAY RECAP ********************************************************************************************************
localhost : ok=0 changed=0 unreachable=0 failed=1 skipped=0 rescued=0 ignored=0
ドラゴンAAはこちら参考にさせていただきました。
Role実行前にValidating arguments against arg spec 'main'
というタスクが自動で実行されており、ここで検証してくれていることがわかります。
おそらく、validate_argument_specモジュールが実行されているかと思います。
さいごに
argument_specs
を使うことで、大体のパラメータチェックは可能そうなので今後使っていきたい。
今回の記事を書くにあたり同じネタがないか調べてみたところ日本語でヒットしたのはてくなべブログのみでした。さすがです。。。
参考リンク
- https://docs.ansible.com/ansible/latest/playbook_guide/playbooks_reuse_roles.html#role-argument-validation
- https://docs.ansible.com/ansible/latest/collections/ansible/builtin/validate_argument_spec_module.html
- https://docs.ansible.com/ansible/latest/collections/ansible/builtin/import_role_module.html
- https://tekunabe.hatenablog.jp/entry/2022/03/05/ansible_stumble_35