Ansibleで書いたplaybookのテストを実行できる方法をまとめています。
環境情報
- python 3.6.7
- ansible 2.7.10
シンタックスチェック
構文エラーを調べてくれるオプション --syntax-check
が用意されているので、これを利用できます。
ヘルプコマンドより。
$ ansible-playbook -help Usage: ansible-playbook [options] playbook.yml [playbook2 ...] Options: --syntax-check perform a syntax check on the playbook, but do not execute it
サンプル。
$ ansible-playbook -i hosts site.yml --syntax-check
playbookのdry run(チェックモード)
作成したplaybookをdry runしてくれるオプション --check
が用意されています。
ヘルプコマンドより。
$ ansible-playbook -help Usage: ansible-playbook [options] playbook.yml [playbook2 ...] Options: -C, --check don't make any changes; instead, try to predict some of the changes that may occur
サンプル。
$ ansible-playbook -i hosts site.yml --check
チェックモードの問題点1:依存関係のあるタスクでの利用
例えば以下の様なタスクの場合。Nginxのyum repoをインストール後、Nginxをinstallする処理ですが、当然repoがなければNginxはインストールできないので、dry runであるチェックモードの処理では、Nginxのインストールするタスクで失敗してしまいます。
- name: nginx用yumリポジトリの用意 yum_repository: name: nginx description: nginx repo baseurl: http://nginx.org/packages/mainline/centos/7/$basearch/ owner: root gpgcheck: no enabled: yes state: present - name: Nginxのインストール yum: name: nginx state: present
この回避策として、 ignore_errors
オプションを利用して、チェックモード時のエラーを回避する方法が公式では紹介されています。以下のように、Nginxインストール用タスクに ignore_errors
オプションを追加することで、チェックモード時のエラーを無視してくれるようなります。
- name: Nginxのインストール yum: name: nginx state: present ignore_errors: "{{ ansible_check_mode }}"
チェックモードの問題点2:チェックモードをスキップするタスクモジュールがいる
command
や shell
モジュールでは、デフォルトではチェックモードをスキップする仕様となっています。( creates
や removes
オプションを付与されている場合、チェックモードで処理される)そのため、以下のようなタスクでは、チェックモードは必ず失敗してしまいます。command
モジュールで情報を取得し register
で変数に代入、後続のタスクにて、変数の値により処理を実行するものです。
- name: システムロケールの確認 command: localectl status register: localectl_result changed_when: false - name: ロケールの日本語化 block: - name: 日本語ロケールのモジュールをインストール yum: name: glibc-common - name: ロケールの変更 command: localectl set-locale LANG=ja_JP.utf8 when: "'LANG=ja_JP.utf8' not in localectl_result.stdout"
command
モジュールは、チェックモードではスキップされてしまうため変数の設定がなされず、後続の処理がエラーとなってしまうのです。この回避策として、 check_mode
オプションを付与して、チェックモード時の動作を制御する方法があります。
check_mode: yes
- Force a task to run in check mode, even when the playbook is called without
--check
.
- Force a task to run in check mode, even when the playbook is called without
check_mode: no
- Force a task to run in normal mode and make changes to the system, even when the playbook is called with
--check
.
- Force a task to run in normal mode and make changes to the system, even when the playbook is called with
check_mode: no
オプションを付与することで、チェックモードであっても必ず処理が実行されるため、下記のように記載することで、 register
の処理が必ず実行されるようできます。
- name: システムロケールの確認 command: localectl status register: localectl_result changed_when: false check_mode: no
ただ全ての依存関係を考慮して各タスクに、こういった回避策を設けていくのは非常にめんどくさく、チェックモードが利用できる範囲は限定的になってしまうのでは、と思っています。
AnsibleのLinter
AnsibleではLinterが用意されいるので、これを利用することができます。
インストール
pipにてインストールできます。
$ sudo pip3 install ansible-lint
$ ansible-lint --version
ansible-lint 4.1.0
デフォルトのルール
デフォルトのルールは、以下のコマンドより確認できます。
$ ansible-lint -L -R 101: Deprecated always_run Instead of ``always_run``, use ``check_mode`` 102: No Jinja2 in when ``when`` lines should not include Jinja2 variables (略)
デフォルトルールのOS上配置パスは、(僕の環境の場合)、以下となっていました。
/usr/local/lib/python3.6/dist-packages/ansiblelint/rules/
利用方法
テストしたいplaybookを指定して、ansible-lintを実行するだけです。
$ ansible-lint --help
Usage: ansible-lint [options] playbook.yml [playbook2 ...]
サンプル。こんな感じで指摘してくれます。
$ ansible-lint site.yml [201] Trailing whitespace /mnt/c/_/ansible/sample/roles/postgresql/tasks/main.yml:43 lineinfile:
独自ルールの作成
lintで利用できる独自のルールを作成することができます。詳しくは、以下の公式資料を見てもらうとして、
Ansible Lint - Creating Custom Rule
以下は timezone
モジュールを利用禁止とする独自ルールの例になります。
ルールを格納しているデフォルトパスに、ルールを定義したpythonプログラムを置いてあげると、
/usr/local/lib/python3.6/dist-packages/ansiblelint/rules/NotUseTimezone.py
import ansiblelint.utils from ansiblelint import AnsibleLintRule class NotUseTimezone(AnsibleLintRule): id = 'CUSTOMRULE001' shortdesc = 'should not use timezone' description = 'should not use timezone' tags = ['prohibition'] modules = ['timezone'] def matchtask(self, file, task): if task['action']['__ansible_module__'] in self.modules: return True return False
linterを実行時に、timezoneを利用している場合に指摘してくるようなります。
$ ansible-lint site.yml [CUSTOMRULE001] should not use timezone /mnt/c/_/ansible/sample/roles/postgresql/tasks/main.yml:23 Task/Handler: タイムゾーンの変更