- はじめに
- 動画
- ロールと変数とチェック
- Role argument validation
- validate_argument_spec モジュール
- チェックモードでもvalidateされる
- ansible-docによるargument情報の表示
- まとめ
- Part36にむけて
はじめに
2022/03/05 に、YouTube Live で「つまずき Ansible 【Part35】ロールの実行に必要な変数をチェック」という配信をしました。
今回は、ロールの実行に必要な変数のバリデーションができる「Role argument validation」を試します。
ansible-core 2.11 から利用できる機能です。
動画
ロールと変数とチェック
ロール内で変数を利用する場合は、あらじめ必要な変数が定義されているかなどをチェックしたいところです。
古くから使える方法としては、assert モジュールがあり、柔軟なチェックができます。
一方 ansible-core 2.11 からは Role argument validation という機能が利用できます(正確な機能名はわからないですが、ドキュメントの見出しからとっています)。
assert モジュールのよいな汎用的な機能ではなく、ロールの argument (内部で利用する変数と解釈しています)専用のチェック機能です。
Role argument validation
Role argument validation では、チェックするルールをロール内の meta/argument_specs.yml 内に定義します。
必須チェック
エラーになるケース
必要な変数だと定義しているのに、定義してない場合です。
meta/argument_specs.yml を以下のようにします。myapp_int も myapp_str も required: true で必須としています。
--- argument_specs: main: short_description: The main entry point for the myapp role options: myapp_int: type: "int" required: true description: "The integer value" myapp_str: type: "str" required: true description: "The string value"
以下の Playbook を実行することにします。myapp_int も myapp_str も未定義です。
--- - hosts: localhost gather_facts: false tasks: - name: import test ansible.builtin.import_role: name: test
ロールの処理の中身も変数を利用するだけです。
--- - name: debug test 1 debug: msg: "{{ myapp_int }}" - name: debug test 2 debug: msg: "{{ myapp_str }}"
この状態で、Playbook を実行します。(エラーを改行表示するために -vvv を付けてます。)
$ ansible-playbook -i localhost, site.yml -vvv
...(略)...
PLAYBOOK: site.yml ******************************************************************************************************************************************************************************
1 plays in site.yml
PLAY [localhost] ********************************************************************************************************************************************************************************
META: ran handlers
TASK [test : Validating arguments against arg spec 'main' - The main entry point for the myapp role] ********************************************************************************************
task path: /home/admin/ansible-ee/site.yml:2
fatal: [localhost]: FAILED! => {
"argument_errors": [
"missing required arguments: myapp_int, myapp_str" # ここがポイント
],
"argument_spec_data": {
"myapp_int": {
"description": "The integer value",
"required": true,
"type": "int"
},
"myapp_str": {
"description": "The string value",
"required": true,
"type": "str"
}
},
"changed": false,
"msg": "Validation of arguments failed:\nmissing required arguments: myapp_int, myapp_str",
"validate_args_context": {
"argument_spec_name": "main",
"name": "test",
"path": "/home/admin/ansible-ee/roles/test",
"type": "role"
}
}
PLAY RECAP **************************************************************************************************************************************************************************************
localhost : ok=0 changed=0 unreachable=0 failed=1 skipped=0 rescued=0 ignored=0
ポイントは "missing required arguments: myapp_int, myapp_str" です。2つの変数の分がいっぺんに表示してくれるのが嬉しいです。
validate が1つのタスクとてログに表示される点も特徴かなと思います。
正常なケース
今度は正常なケースです。myapp_int と myapp_str をきちんと定義します。
どこで定義されててもいいのですが、今回は import_role モジュールのタスクの vars で定義します。
--- - hosts: localhost gather_facts: false tasks: - name: import test ansible.builtin.import_role: name: test vars: myapp_int: 123 myapp_str: hello
これで実行すると成功します。validate のタスクの状態も ok です。
TASK [test : Validating arguments against arg spec 'main' - The main entry point for the myapp role] ********************************************************************************************
task path: /home/admin/ansible-ee/site.yml:2
ok: [localhost] => {
"changed": false,
"msg": "The arg spec validation passed", # 成功
"validate_args_context": {
"argument_spec_name": "main",
"name": "test",
"path": "/home/admin/ansible-ee/roles/test",
"type": "role"
}
}
TASK [test : debug test 1] **********************************************************************************************************************************************************************
task path: /home/admin/ansible-ee/roles/test/tasks/main.yml:2
ok: [localhost] => {
"msg": 123
}
TASK [test : debug test 2] **********************************************************************************************************************************************************************
task path: /home/admin/ansible-ee/roles/test/tasks/main.yml:6
ok: [localhost] => {
"msg": "hello"
}
チェックの無効化
ロールに meta/argument_specs.yml があると validate 処理がされますが、無効化もできます。
import_role モジュールで呼び出す場合は rolespec_validate オプションで false を指定します。デフォルトは true です。
# ...(略)... - name: import test ansible.builtin.import_role: name: test rolespec_validate: false
この場合は、validate 処理はされず、ログにも出てきません。
型チェック
次は型チェックです。meta/argument_specs.yml 内では、 myapp_int は type: "int" という指定をしています。
なので、意図的に 文字に指定してエラーになるようにしてみます。
# ...(略)... - name: import test ansible.builtin.import_role: name: test vars: myapp_int: abc # 文字列 myapp_str: hello
こんなエラーになりました。
fatal: [localhost]: FAILED! => {
"argument_errors": [
"argument 'myapp_int' is of type <class 'ansible.parsing.yaml.objects.AnsibleUnicode'> and we were unable to convert to int: <class 'ansible.parsing.yaml.objects.AnsibleUnicode'> cannot be converted to an int"
],
unable to convert to int とあるので、変換しようとしてエラー、ということのようです。 なお、myapp_int: "123" という指定はエラーになりませんでした。
選択肢チェック
AかBであること、のような選択肢のチェックです。meta/argument_specs.yml に以下を追加します。
# ...(略)... kind: type: "str" choices: - kingyo - ugui required: true description: "kind of sakana"
この場合は、kind の値は kingyo、ugui のいずれかであること、という指定です。
Playbook 側の vars では、意図的にエラーになるように、kind: buri を指定します。
# ...(略)... - name: import test ansible.builtin.import_role: name: test vars: myapp_int: 123 myapp_str: hello kind: buri
こんなエラーになりました。"value of kind must be one of: kingyo, ugui, got: buri" と表示されています。
...(略)...
TASK [test : Validating arguments against arg spec 'main' - The main entry point for the myapp role] ***
task path: /home/admin/ansible-ee/site.yml:2
fatal: [localhost]: FAILED! => {
"argument_errors": [
"value of kind must be one of: kingyo, ugui, got: buri" # ポイント
],
"argument_spec_data": {
"kind": {
"choices": [
"kingyo",
"ugui"
],
"description": "kind of sakana",
"required": true,
"type": "str"
},
"myapp_int": {
"description": "The integer value",
"required": true,
"type": "int"
},
"myapp_str": {
"description": "The string value",
"required": true,
"type": "str"
}
},
"changed": false,
"msg": "Validation of arguments failed:\nvalue of kind must be one of: kingyo, ugui, got: buri",
"validate_args_context": {
"argument_spec_name": "main",
"name": "test",
"path": "/home/admin/ansible-ee/roles/test",
"type": "role"
}
}
validate_argument_spec モジュール
Role argument validation は、ロール読み込み時に meta/argument_specs.yml があるとチェックするという動作でした。
似たようなものとして、ansible.builtin.validate_argument_specモジュールがあります。
これは、任意のタイミングで、 meta/argument_specs.yml のような定義(厳密には meta/argument_specs.yml の options 配下のみ) を利用してチェックするモジュールです。
タイミングが任意の代わりに、ファイルを明示的に指定する必要があります。
[2024/01/13 追記] 別の記事で書きました。
チェックモードでもvalidateされる
Role argument validation は ansible-playbook コマンドの -C または --check によるチェックモードによる実行でもvalidateしてくれます。
ansible-docによるargument情報の表示
meta/argument_specs.yml に定義した情報は、ansible-doc コマンドで表示できあます。
ロール一覧
ロール一覧の表示では、short_description に定義した情報が表示されまます。
$ ansible-doc -t role -r roles -l ...(略)... test main The main entry point for the myapp role!!!!
ロール個別表示
ロールと指定すると書く argument のタイプ、説明なども表示されます。
$ ansible-doc -t role -r roles test
> TEST (/home/admin/ansible-ee/roles/test)
ENTRY POINT: main - The main entry point for the myapp role
OPTIONS (= is mandatory):
= kind
kind of sakana
(Choices: kingyo, ugui)
type: str
= myapp_int
The integer value
type: int
= myapp_str
The string value
type: str
まとめ
ロールの実行に必要な変数をチェックする Role argument validation 機能をためしてみました。
柔軟さでは assert や ansible.utils.validate`モジュールのほうが勝ると思いますが、meta/argument_specs.yml はドキュメンテーションも兼ねる点は便利かなと思います。
Part36にむけて
以下のネタを検討中です。気が向いたものをやります。 connpass申込時のアンケートでいただいたものも含めています。