これは、なにをしたくて書いたもの?
AnsibleのRoleに対して、テストが書きたいなぁと思いまして。
調べてみたら、Moleculeというのが良さそうな感じだったので、ちょっと試してみることにしました。
Molecule — Molecule 2.22 documentation
Molecule?
Ansible Roleを開発したり、テストするためのものみたいです。
Molecule — Molecule 2.22 documentation
Molecule is designed to aid in the development and testing of Ansible roles.
複数のインスタンス、OS、仮想化プロバイダー、テストに関するサポートを提供します、と。
Molecule provides support for testing with multiple instances, operating systems and distributions, virtualization providers, test frameworks and testing scenarios.
AnsibleのOrganizationにあるのもポイントですね。
GitHub - ansible/molecule: Molecule aids in the development and testing of Ansible roles.
現在のバージョンは、2.22です。
とりあえず、インストールしてGetting Startedに沿って試してみましょう。
環境
インストールに必要な情報は、こちら。
Installation — Molecule 2.22 documentation
OSは、CentOS 7かUbuntu 16.xらしいですが…今回はこんな感じでいきます。
$ lsb_release -a No LSB modules are available. Distributor ID: Ubuntu Description: Ubuntu 18.04.3 LTS Release: 18.04 Codename: bionic $ docker version Client: Docker Engine - Community Version: 19.03.5 API version: 1.40 Go version: go1.12.12 Git commit: 633a0ea838 Built: Wed Nov 13 07:29:52 2019 OS/Arch: linux/amd64 Experimental: false Server: Docker Engine - Community Engine: Version: 19.03.5 API version: 1.40 (minimum version 1.12) Go version: go1.12.12 Git commit: 633a0ea838 Built: Wed Nov 13 07:28:22 2019 OS/Arch: linux/amd64 Experimental: false containerd: Version: 1.2.10 GitCommit: b34a5c8af56e510852c35414db4c1f4fa6172339 runc: Version: 1.0.0-rc8+dev GitCommit: 3e425f80a8c931f88e6d94a8c831b9d5aa481657 docker-init: Version: 0.18.0 GitCommit: fec3683 $ python3 -V Python 3.6.9 $ pip3 -V pip 9.0.1 from /path/to/venv/lib/python3.6/site-packages (python 3.6)
Moleculeのインストール。
$ pip3 install molecule
Driverとして、デフォルトでDockerが使用されるので、こちらもインストール。
$ pip3 install molecule[docker]
molecule[docker]のインストールについては書いてないな?と思ったら、Getting Startedに書いてありました…。
Getting Started Guide — Molecule 2.22 documentation
バージョン。Ansibleも一緒にインストールされます。
$ pip3 freeze | grep -E 'ansible|molecule|docker|testinfra' ansible==2.9.3 ansible-lint==4.2.0 docker==4.1.0 molecule==2.22 testinfra==3.4.0
また、テストフレームワークとしては、Testinfraが使われます。
Testinfra test your infrastructure — testinfra 3.4.1.dev0+gd7a7512.d20200105 documentation
Driverには、Docker以外にもEC2、Azure、GCEなど、いろいろあるようです。
お題と確認
今回は、Ubuntu LinuxにApacheをインストールするRoleを作って、テストを動かすことをゴールにしてみます。
基本は、Getting Startedに沿っていく形で。
Getting Started Guide — Molecule 2.22 documentation
まずは、新しいRoleを作成します。
「molecule init role」で作成します。Role名は、「apache」にしました。
$ molecule init role -r apache
なお、すでに存在するRoleにMoleculeを追加する場合は、以下のコマンドになるようです。
molecule init scenario -r my-role-name
作成されたファイルは、こんな感じです。
$ find apache -type f apache/handlers/main.yml apache/README.md apache/vars/main.yml apache/meta/main.yml apache/defaults/main.yml apache/tasks/main.yml apache/molecule/default/playbook.yml apache/molecule/default/tests/__pycache__/test_default.cpython-36.pyc apache/molecule/default/tests/test_default.py apache/molecule/default/Dockerfile.j2 apache/molecule/default/molecule.yml apache/molecule/default/INSTALL.rst apache/.yamllint
Ansible Roleと、Moleculeに関連するファイルがそれぞれできた感じですね。Moleculeに関連するファイルは、Role内に納まっています。
Molecule側のファイルを、ちょっと見てみましょう。
moleculeディレクトリ配下の、「default」というのはシナリオの名前です。
Dockerfileのテンプレート。
apache/molecule/default/Dockerfile.j2
# Molecule managed {% if item.registry is defined %} FROM {{ item.registry.url }}/{{ item.image }} {% else %} FROM {{ item.image }} {% endif %} {% if item.env is defined %} {% for var, value in item.env.items() %} {% if value %} ENV {{ var }} {{ value }} {% endif %} {% endfor %} {% endif %} RUN if [ $(command -v apt-get) ]; then apt-get update && apt-get install -y python sudo bash ca-certificates iproute2 && apt-get clean; \ elif [ $(command -v dnf) ]; then dnf makecache && dnf --assumeyes install python sudo python-devel python*-dnf bash iproute && dnf clean all; \ elif [ $(command -v yum) ]; then yum makecache fast && yum install -y python sudo yum-plugin-ovl bash iproute && sed -i 's/plugins=0/plugins=1/g' /etc/yum.conf && yum clean all; \ elif [ $(command -v zypper) ]; then zypper refresh && zypper install -y python sudo bash python-xml iproute2 && zypper clean -a; \ elif [ $(command -v apk) ]; then apk update && apk add --no-cache python sudo bash ca-certificates; \ elif [ $(command -v xbps-install) ]; then xbps-install -Syu && xbps-install -y python sudo bash ca-certificates iproute2 && xbps-remove -O; fi
Molecureの設定ファイル。
apache/molecule/default/molecule.yml
--- dependency: name: galaxy driver: name: docker lint: name: yamllint platforms: - name: instance image: centos:7 provisioner: name: ansible lint: name: ansible-lint verifier: name: testinfra lint: name: flake8
Playbook。
apache/molecule/default/playbook.yml
--- - name: Converge hosts: all roles: - role: apache
テストコード。
apache/molecule/default/tests/test_default.py
import os import testinfra.utils.ansible_runner testinfra_hosts = testinfra.utils.ansible_runner.AnsibleRunner( os.environ['MOLECULE_INVENTORY_FILE'] ).get_hosts('all') def test_hosts_file(host): f = host.file('/etc/hosts') assert f.exists assert f.user == 'root' assert f.group == 'root'
ざっと、こんな感じです。
とりあえず、Role内に移動。
$ cd apache
DriverはDockerとなっていますが、OSのイメージは今回はUbuntu Linux 18.04に変更します。
molecule.yml
driver: name: docker lint: name: yamllint platforms: - name: instance image: ubuntu:18.04
「molecule create」で、インスタンスを作成。今回は、DriverにDockerを使用しているので、Dockerコンテナが立ち上がります。
$ molecule create
インスタンスは、「molecule list」で確認することができます。
$ molecule list --> Validating schema /path/to/apache/molecule/default/molecule.yml. Validation completed successfully. Instance Name Driver Name Provisioner Name Scenario Name Created Converged --------------- ------------- ------------------ --------------- --------- ----------- instance docker ansible default true false
Dockerコンテナなので、dockerコマンドでも確認できます。
$ docker container ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 3bf5b879b166 molecule_local/ubuntu:18.04 "bash -c 'while true…" 14 seconds ago Up 10 seconds instance
イメージ名は、こんな感じに。
$ docker image ls REPOSITORY TAG IMAGE ID CREATED SIZE molecule_local/ubuntu 18.04 304dca78c3df 3 minutes ago 132MB ...
このインスタンスにログインする場合は、「molecule login」を使います。
$ molecule login
コンテナの中に入れます。
root@instance:/# uname -a Linux instance 4.15.0-74-generic #84-Ubuntu SMP Thu Dec 19 08:06:28 UTC 2019 x86_64 x86_64 x86_64 GNU/Linux
このインスタンスを破棄するには、「molecule destroy」で。
$ molecule destroy
ここで本来のRoleの方に、Apacheをインストールするタスクを書いてみましょう。
tasks/main.yml
--- - name: install apache2 apt: name: apache2 state: present
「molecule converge」を実行すると、「molecule create」に加えてPlaybookの実行まで行ってくれます。
$ molecule converge
こんな感じですね。
--> Scenario: 'default' --> Action: 'converge' PLAY [Converge] **************************************************************** TASK [Gathering Facts] ********************************************************* [DEPRECATION WARNING]: Distribution Ubuntu 18.04 on host instance should use /usr/bin/python3, but is using /usr/bin/python for backward compatibility with prior Ansible releases. A future Ansible release will default to using the discovered platform python for this host. See https://docs.ansible.com/ansible/ 2.9/reference_appendices/interpreter_discovery.html for more information. This feature will be removed in version 2.12. Deprecation warnings can be disabled by setting deprecation_warnings=False in ansible.cfg. ok: [instance] TASK [apache : install apache2] ************************************************ [WARNING]: Updating cache and auto-installing missing dependency: python-apt changed: [instance] PLAY RECAP ********************************************************************* instance : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
これでコンテナにログインすると、Apacheがインストールされた状態を確認することができます。
次に、テストをしてみましょう。
「molecule init」時に作成されたテストコードを、こんな感じに修正。
molecule/default/tests/test_default.py
import os import testinfra.utils.ansible_runner testinfra_hosts = testinfra.utils.ansible_runner.AnsibleRunner( os.environ['MOLECULE_INVENTORY_FILE'] ).get_hosts('all') def test_apache_is_installed(host): apache2 = host.package('apache2') assert apache2.is_installed
Apacheがインストールされたことを確認するテストにしました。
テストは、「molecule verify」で実行します。
$ molecule verify
ログ。
collected 1 item tests/test_default.py . [100%] ============================== 1 passed in 3.37s =============================== Verifier completed successfully.
ちゃんとテストが動作しているか確認するために、失敗するテストにしてみましょう。
def test_apache_is_installed(host): apache2 = host.package('apache2') assert apache2.is_installed == False
確認。
collected 1 item tests/test_default.py F [100%] =================================== FAILURES =================================== _________________ test_apache_is_installed[ansible://instance] _________________ host = <testinfra.host.Host object at 0x7fde1da8af28> def test_apache_is_installed(host): apache2 = host.package('apache2') > assert apache2.is_installed == False E assert True == False E + where True = <package apache2>.is_installed tests/test_default.py:11: AssertionError ============================== 1 failed in 3.08s ===============================
テストを実行してくれてそうですね。
使い終わったインスタンスの破棄は、やっぱり「molecule destroy」で。
$ molecule destroy
最後にここまで一気に実行するには、「molecule test」を実行します。
$ molecule test
デフォルトで作成したRoleのままだと、metaがlintでエラーになってまうので、エラーメッセージを読んで修正しておきましょう。
--> Scenario: 'default' --> Action: 'lint' --> Executing Yamllint on files found in /path/to/apache/... Lint completed successfully. --> Executing Flake8 on files found in /path/to/apache/molecule/default/tests/... Lint completed successfully. --> Executing Ansible Lint on /path/to/apache/molecule/default/playbook.yml... [701] Role info should contain platforms meta/main.yml:1 {'meta/main.yml': {'galaxy_info': {'author': 'your name', 'description': 'your description', 'company': 'your company (optional)', 'license': 'license (GPLv2, CC-BY, etc)', 'min_ansible_version': 1.2, 'galaxy_tags': [], '__line__': 2, '__file__': '/path/to/apache/meta/main.yml'}, 'dependencies': [], '__line__': 1, '__file__': '/path/to/apache/meta/main.yml', 'skipped_rules': []}} [703] Should change default metadata: author meta/main.yml:1 {'meta/main.yml': {'galaxy_info': {'author': 'your name', 'description': 'your description', 'company': 'your company (optional)', 'license': 'license (GPLv2, CC-BY, etc)', 'min_ansible_version': 1.2, 'galaxy_tags': [], '__line__': 2, '__file__': '/path/to/apache/meta/main.yml'}, 'dependencies': [], '__line__': 1, '__file__': '/path/to/apache/meta/main.yml', 'skipped_rules': []}} [703] Should change default metadata: description meta/main.yml:1 {'meta/main.yml': {'galaxy_info': {'author': 'your name', 'description': 'your description', 'company': 'your company (optional)', 'license': 'license (GPLv2, CC-BY, etc)', 'min_ansible_version': 1.2, 'galaxy_tags': [], '__line__': 2, '__file__': '/path/to/apache/meta/main.yml'}, 'dependencies': [], '__line__': 1, '__file__': '/path/to/apache/meta/main.yml', 'skipped_rules': []}} [703] Should change default metadata: company meta/main.yml:1 {'meta/main.yml': {'galaxy_info': {'author': 'your name', 'description': 'your description', 'company': 'your company (optional)', 'license': 'license (GPLv2, CC-BY, etc)', 'min_ansible_version': 1.2, 'galaxy_tags': [], '__line__': 2, '__file__': '/path/to/apache/meta/main.yml'}, 'dependencies': [], '__line__': 1, '__file__': '/path/to/apache/meta/main.yml', 'skipped_rules': []}} [703] Should change default metadata: license meta/main.yml:1 {'meta/main.yml': {'galaxy_info': {'author': 'your name', 'description': 'your description', 'company': 'your company (optional)', 'license': 'license (GPLv2, CC-BY, etc)', 'min_ansible_version': 1.2, 'galaxy_tags': [], '__line__': 2, '__file__': '/path/to/apache/meta/main.yml'}, 'dependencies': [], '__line__': 1, '__file__': '/path/to/apache/meta/main.yml', 'skipped_rules': []}}
各コマンドでどんなことが実行されるのかは、実行時にmatrixが出力されるので、こちらで確認できます。
「molecule test」だと、こうなります。
--> Test matrix └── default ├── lint ├── dependency ├── cleanup ├── destroy ├── syntax ├── create ├── prepare ├── converge ├── idempotence ├── side_effect ├── verify ├── cleanup └── destroy
コマンドのリストは、こちら。
Usage — Molecule 2.22 documentation
デバッグログを出力したかったら、「--debug」オプションを使います。
$ molecule --debug test
とりあえず、簡単な使い方は押さえられた感じでしょうか?