- はじめに
- 動画
- ロールと変数とチェック
- 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申込時のアンケートでいただいたものも含めています。