CLOVER🍀

That was when it all began.

Molecule 3を試す

これは、なにをしたくて書いたもの?

Moleculeが3.0になっていたので、1度試しておこうかなと。

前に試した時は、2.22でした。

Ansible Roleを開発、テストするためのMoleculeを試す - CLOVER🍀

Changelogを見ると、だいぶ変わったみたいです。

Changelog / 3.0

特に大きな変更は、以下あたりです(Changelogの「MAJOR」から抜粋)。

  • Ansibleのバージョンサポート範囲がN/N-1(たとえば2.9と2.8)になる
  • Providerはインストール可能なドライバー、Pythonモジュールになった
  • コアから、Azure、EC2、DigitalOcean、GCE、HetznerCloud、Linode、LXD、OpenStack、Vagrantの各Providerが削除
  • goss verifierを削除
  • playbook.ymlという名前は非推奨になり、converge.ymlに
  • デフォルトのverifierがAnsibleになった
  • Testinfraはデフォルトでインストールされなくなった
  • Scenerio名が設定から削除された
  • Lintingの設定がリファクタリングされた
  • DockerfileのテンプレートがMolecureに組み込み可能になった

Migrating to molecule v3 checklist · Issue #2560 · ansible-community/molecule · GitHub

Providerがだいぶ削除されたり、Testinfraが入らなくなったりしたんですねぇ。

とりあえず、今回は2.22の時に試した内容を、3.0に読み替えてやってみようと思います。

Moleculeとは

少しだけ、振り返っておきます。

Ansible Molecule — Molecule 3.0.3.dev73+gcc0c7634 documentation

Moleculeは、Ansible Roleを作ったりテストするためのプロジェクトです。

Molecule project 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.

環境

今回の環境は、こちらです。

$ 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をインストールします。

Installation — Molecule 3.0.3.dev73+gcc0c7634 documentation

$ pip3 install molecule[lint]

インストールしたバージョン。

$ pip3 freeze | grep -E 'ansible|molecule'
ansible==2.9.6
ansible-lint==4.2.0
molecule==3.0.2

試してみる

Getting Startedを見つつ、Moleculeを試していきます。

Getting Started Guide — Molecule 3.0.3.dev73+gcc0c7634 documentation

お題としては、ApacheをインストールするRoleを作ることにしましょう。

Creating a new role

「molecule init」でRoleを作成。

$ molecule init role apache

できあがったファイル。

$ find apache -type f
apache/handlers/main.yml
apache/README.md
apache/vars/main.yml
apache/.travis.yml
apache/meta/main.yml
apache/defaults/main.yml
apache/tests/inventory
apache/tests/test.yml
apache/tasks/main.yml
apache/molecule/default/verify.yml
apache/molecule/default/converge.yml
apache/molecule/default/molecule.yml
apache/molecule/default/INSTALL.rst
apache/.yamllint

いくつか、見ていきましょう。

定義内容は、Configuration Guideを見つつ。

Configuration — Molecule 3.0.3.dev73+gcc0c7634 documentation

Moleculeの中心となる設定ファイルです。
apache/molecule/default/molecule.yml

---
dependency:
  name: galaxy
driver:
  name: docker
platforms:
  - name: instance
    image: docker.io/pycontribs/centos:7
    pre_build_image: true
provisioner:
  name: ansible
verifier:
  name: ansible

Changelogにもあったとおり、playbook.ymlはconverge.ymlになったんですね。
apache/molecule/default/converge.yml

---
- name: Converge
  hosts: all
  tasks:
    - name: "Include apache"
      include_role:
        name: "apache"

デフォルトのVerifierがAnsibleになったので、verify.ymlというファイルが生成されています。
apache/molecule/default/verify.yml

---
# This is an example playbook to execute Ansible tests.

- name: Verify
  hosts: all
  tasks:
  - name: Example assertion
    assert:
      that: true

assertモジュールで検証します、と。

assert – Asserts given expressions are true — Ansible Documentation

作成されたRoleのディレクトリに移動。

$ cd apache

DockerイメージはpysのCentOSでしたが、今回はUbuntu Linuxを使うことにしましょう。

GitHub - pycontribs/pys: Repository used to pre-build python enabled images for popular operating systems

pys / ubuntu

pysというのは、メジャーなOSでPythonを有効化してビルドされたDockerイメージです。

こんな感じに変更。

driver:
  name: docker
platforms:
  - name: instance
    image: docker.io/pycontribs/ubuntu:latest
    pre_build_image: true
    dns_servers:
      - 8.8.8.8

「dns_servers」を指定しているのは、aptでApacheをインストールする時に名前解決ができなくてエラーになったからです…。

Err:1 http://archive.ubuntu.com/ubuntu bionic InRelease                  
  Temporary failure resolving 'archive.ubuntu.com'
Err:2 http://security.ubuntu.com/ubuntu bionic-security InRelease        
  Temporary failure resolving 'security.ubuntu.com'
Err:3 http://archive.ubuntu.com/ubuntu bionic-updates InRelease          
  Temporary failure resolving 'archive.ubuntu.com'
Err:4 http://archive.ubuntu.com/ubuntu bionic-backports InRelease
  Temporary failure resolving 'archive.ubuntu.com'

では、プロビジョニングを行うインスタンスを作成します。

Run test sequence commands

「molecule create」でインスタンスの作成…ですが、エラーになります。

$ molecule create
--> Test matrix
    
└── default
    ├── dependency
    ├── create
    └── prepare
    
--> Scenario: 'default'
--> Action: 'dependency'
Skipping, missing the requirements file.
--> Scenario: 'default'
--> Action: 'create'
--> Sanity checks: 'docker'
ERROR: Missing Docker driver dependency. Please install via 'molecule[docker]' or refer to your INSTALL.rst driver documentation file

「Dockerドライバがない」と言われているので、インストール。

$ pip3 install molecule[docker]

バージョン。

$ pip3 freeze | grep -E 'ansible|molecule|docker'
ansible==2.9.6
ansible-lint==4.2.0
docker==4.2.0
molecule==3.0.2

再度、「molecule create」。

$ molecule create

今度は成功します。

$ molecule create
--> Test matrix
    
└── default
    ├── dependency
    ├── create
    └── prepare
    
--> Scenario: 'default'
--> Action: 'dependency'
Skipping, missing the requirements file.
--> Scenario: 'default'
--> Action: 'create'
--> Sanity checks: 'docker'
    
    PLAY [Create] ******************************************************************
    
    TASK [Log into a Docker registry] **********************************************
    skipping: [localhost] => (item=None) 
    
    TASK [Check presence of custom Dockerfiles] ************************************
    ok: [localhost] => (item=None)
    ok: [localhost]
    
    TASK [Create Dockerfiles from image names] *************************************
    skipping: [localhost] => (item=None) 
    
    TASK [Discover local Docker images] ********************************************
    ok: [localhost] => (item=None)
    ok: [localhost]
    
    TASK [Build an Ansible compatible image (new)] *********************************
    skipping: [localhost] => (item=molecule_local/docker.io/pycontribs/ubuntu:latest) 
    
    TASK [Create docker network(s)] ************************************************
    
    TASK [Determine the CMD directives] ********************************************
    ok: [localhost] => (item=None)
    ok: [localhost]
    
    TASK [Create molecule instance(s)] *********************************************
    changed: [localhost] => (item=instance)
    
    TASK [Wait for instance(s) creation to complete] *******************************
    FAILED - RETRYING: Wait for instance(s) creation to complete (300 retries left).
    FAILED - RETRYING: Wait for instance(s) creation to complete (299 retries left).
    FAILED - RETRYING: Wait for instance(s) creation to complete (298 retries left).
    FAILED - RETRYING: Wait for instance(s) creation to complete (297 retries left).
    FAILED - RETRYING: Wait for instance(s) creation to complete (296 retries left).
    FAILED - RETRYING: Wait for instance(s) creation to complete (295 retries left).
    FAILED - RETRYING: Wait for instance(s) creation to complete (294 retries left).
    FAILED - RETRYING: Wait for instance(s) creation to complete (293 retries left).
    FAILED - RETRYING: Wait for instance(s) creation to complete (292 retries left).
    FAILED - RETRYING: Wait for instance(s) creation to complete (291 retries left).
    FAILED - RETRYING: Wait for instance(s) creation to complete (290 retries left).
    FAILED - RETRYING: Wait for instance(s) creation to complete (289 retries left).
    changed: [localhost] => (item=None)
    changed: [localhost]
    
    PLAY RECAP *********************************************************************
    localhost                  : ok=5    changed=2    unreachable=0    failed=0    skipped=4    rescued=0    ignored=0
    
--> Scenario: 'default'
--> Action: 'prepare'
Skipping, prepare playbook not configured.

インスタンスの一覧を確認。

$ molecule list
Instance Name    Driver Name    Provisioner Name    Scenario Name    Created    Converged
---------------  -------------  ------------------  ---------------  ---------  -----------
instance         docker         ansible             default          true       false

当然のことながら、dockerコマンドでも確認できます。

$ docker container ps
CONTAINER ID        IMAGE                      COMMAND                  CREATED             STATUS              PORTS               NAMES
ba6732094f07        pycontribs/ubuntu:latest   "bash -c 'while true…"   56 seconds ago      Up 40 seconds                           instance

起動中のインスタンスにログインするには、「molecule login」で。

$ molecule login
root@instance:/# 

Ubuntu Linux 18.04 LTSですね。

# uname -a
Linux instance 4.18.0-25-generic #26~18.04.1-Ubuntu SMP Thu Jun 27 07:28:31 UTC 2019 x86_64 x86_64 x86_64 GNU/Linux


# lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description:    Ubuntu 18.04.4 LTS
Release:    18.04
Codename:   bionic

インスタンスの破棄。

$ molecule destroy

破棄の様子。

$ molecule destroy
--> Test matrix
    
└── default
    ├── dependency
    ├── cleanup
    └── destroy
    
--> Scenario: 'default'
--> Action: 'dependency'
Skipping, missing the requirements file.
--> Scenario: 'default'
--> Action: 'cleanup'
Skipping, cleanup playbook not configured.
--> Scenario: 'default'
--> Action: 'destroy'
--> Sanity checks: 'docker'
    
    PLAY [Destroy] *****************************************************************
    
    TASK [Destroy molecule instance(s)] ********************************************
    changed: [localhost] => (item=instance)
    
    TASK [Wait for instance(s) deletion to complete] *******************************
    FAILED - RETRYING: Wait for instance(s) deletion to complete (300 retries left).
    changed: [localhost] => (item=None)
    changed: [localhost]
    
    TASK [Delete docker network(s)] ************************************************
    
    PLAY RECAP *********************************************************************
    localhost                  : ok=2    changed=2    unreachable=0    failed=0    skipped=1    rescued=0    ignored=0
    
--> Pruning extra files from scenario ephemeral directory

では、ApacheをインストールするようにAnsible Roleを作成しましょう。tasks/main.ymlに、以下のように記述。
tasks/main.yml

---
- name: install apache2
  apt:
    name: apache2
    state: present
    update_cache: yes

「molecule converge」で、インスタンスの作成とAnsible Playbookの実行を行います。

$ molecule converge

実行されるのは、coverage.ymlの内容ですね。
molecule/default/converge.yml

---
- name: Converge
  hosts: all
  tasks:
    - name: "Include apache"
      include_role:
        name: "apache"

Playbookが実行されている様子。

--> Scenario: 'default'
--> Action: 'prepare'
Skipping, prepare playbook not configured.
--> Scenario: 'default'
--> Action: 'converge'
    
    PLAY [Converge] ****************************************************************
    
    TASK [Gathering Facts] *********************************************************
    ok: [instance]
    
    TASK [Include apache] **********************************************************
    
    TASK [apache : install apache2] ************************************************
    changed: [instance]
    
    PLAY RECAP *********************************************************************
    instance                   : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

次は、テスト(Verify)を行ってみましょう。

Verifier

ところで、MoleculeのデフォルトのVerifierはTestinfraからAnsibleに変わったようですが、それはどうしてかちょっと見てみました。

Make Ansible default verifier · Issue #2013 · ansible-community/molecule · GitHub

使うテクノロジーを、YAMLに統一したかったみたいですね。でも、AnsibleのassertはTestinfraと比べるとどうなのという話もあり…。

デフォルトのverify.ymlを、もう1度見てみます。
molecule/default/verify.yml

---
# This is an example playbook to execute Ansible tests.

- name: Verify
  hosts: all
  tasks:
  - name: Example assertion
    assert:
      that: true

ここからどう変えていいかちょっとわからなくなったので、今回はTestinfraに切り替えることにしました。

Testinfraとpytestをインストールします。

$ pip3 install testinfra pytest

バージョン。

 pip3 freeze | grep -E 'testinfra|pytest'
pytest==5.4.1
testinfra==5.0.0

molecule.ymlのVerifierの設定を、AnsibleからTestinfraへ切り替えます。

verifier:
#  name: ansible
  name: testinfra

テストコードを置くディレクトリを作成。

$ mkdir molecule/default/tests

作成したテストコード。
molecule/default/tests/test_apache.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

インスタンスが起動している状態であれば、「molecule verify」でテストを行うことができます。

$ molecule verify

こんな感じで。

$ molecule verify
--> Test matrix
    
└── default
    └── verify
    
--> Scenario: 'default'
--> Action: 'verify'
--> Executing Testinfra tests found in /path/to/apache/molecule/default/tests/...
    ============================= test session starts ==============================
    platform linux -- Python 3.6.9, pytest-5.4.1, py-1.8.1, pluggy-0.13.1
    rootdir: /path/to/apache/molecule/default
    plugins: testinfra-5.0.0
collected 1 item                                                               
    
    tests/test_default.py .                                                  [100%]
    
    ============================== 1 passed in 5.26s ===============================
Verifier completed successfully.

終わったら、インスタンスを破棄しておきましょう。

$ molecule destroy

ここまでの、インスタンスの作成、Playbookの実行、テスト、インスタンスの破棄まで一気に行う場合は、「molecule test」を
実行します。

Run a full test sequence

$ molecule test

こんな感じで、Moleculeのイントロ的な内容を、ざっくり確認してみました。

Molecule 2から3で、テストまわりが変わっていたり、ドライバがなくなっていたりでなかなかビックリしました。