moleculeでAnsibleのローカルテスト環境を一瞬で構築する


# moleculeとは
- Ansibleのテスト環境の準備からテスト環境へのPlaybook実行、testinfraやinspec、Ansibleでのテスト実行、テスト環境の破棄を全てやってくれるテストフレームワーク。詳細は[公式サイト](https://molecule.readthedocs.io/en/latest/)にくわしい。
- 2020/2/21時点の最新はv3.1、サポートするAnsibleのバージョンは最新の2メジャーバージョン。(v2.9が最新の場合はv2.8まで)


# 前提条件
- docker daemonが起動している。
- `molecule`はバージョン`3.0.1`を使用。
- `Ansible`はバージョン`２．９．５`を使用。

# インストール 
pipでインストールする。

```
$ pip install 'molecule[docker]'
```


# 既存ロールにテストシナリオを追加
ロールのディレクトリに移動して実行すること

```
$ cd roles/nginx
$ moleculer init scenario
```

- `molecule`ディレクトリが作成される。
- デフォルトのオプションは以下
  - シナリオ名: default
  - driver: docker
  - provisioner: Ansible
  - Verifier: Ansible

```
roles/nginx/molecule
└── default
    ├── converge.yml
    ├── INSTALL.rst
    ├── molecule.yml
    └── verify.yml
```

# テスト実行
- `molecule test`を実行するだけ。デフォルトではシナリオ名`default`が実行される。個別にシナリオ指定することも可能。

```
$ molecule test
```

- `test` では以下全ての工程が実行される。

```
❯ molecule test --help
Usage: molecule test [OPTIONS]

  Test (lint, cleanup, destroy, dependency, syntax, create, prepare,
  converge, idempotence, side_effect, verify, cleanup, destroy).
```

- 今回作成したシナリオでは以下。
  - dockerイメージ取得
  - dockerコンテナ作成、起動
  - ansible-lintでPlaybookのチェック
  - playbook実行
  - ansibleでテスト実行
  - dockerコンテナ破棄


- これらの工程を個別に指定して実行することも可能。
`converge`ではコンテナ作成、Playbook実行、テスト実行、コンテナ破棄までやってくれる。

```
$ molecule converge
```

ローカルでテストするだけなら以上で完了。


# 小ネタ
## 依存関係のあるロールの実行
ロールに依存関係がある場合、たとえばnginxロールで`yum install`しているが、その前にcommonロールでyumのconfig設定している場合はnginxロールだけ実行してもエラーになる。
依存関係にある処理は`prepare.yml`に書いておくことで事前に実行することができる。

``` prepare.yml
- name: prepare.yml
  hosts: all
  tasks:
    - name: "execute common role"
      include_role:
        name: "common"
```

## lintのスキップ
molecule.ymlでオプションをつける

``` molecule.yml
lint: |
  yamllint .
  ansible-lint -x 701,703
  flake8
```

## 環境変数を使用
bashと同じ要領で使える。`${var:-hoge}`とか`${var%/}`とか(名前わからない)も使用可能。

``` molecule.yml
platforms:
  - name: instance
    image: ${DOCKER_IMAGE:-registry.example.com:5000/centos:7}
```

## コンテナ内でsystemd起動させたい

``` molecule.yml
platforms:
# ~snip~
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:rw
      - /sys/fs/cgroup:/sys/fs/cgroup:ro
    command: /sbin/init
    capabilities:
      - SYS_ADMIN
```

## Ansibleの`host_vars`,`group_vars`を使用

``` molecule.yml
provisioner:
# ~snip~
  inventory:
    links:
      #hosts: ../../../inventory/hosts
      group_vars: ../../../..//group_vars/
      host_vars: ../../../../host_vars/
```

## testフラグを立てる

``` molecule.yml
provisioner:
  name: ansible
  ansible_args:
    - "-e molecule_test=true"
```

さらにplaybookの中で`when: not molecule_test`をつければテストでスキップできる。

## verifierをtestinfraに変更する
個人的にはAnsibleでプロビジョニングしたサーバをAsibleでテストすることに疑問を感じる。開発者の間でもかなり議論があった様子。
https://github.com/ansible-community/molecule/issues/2013

testinfraやInspecの学習コストが苦にならないならテストツールは分けるべきというのが個人的な意見なので変更する。

``` molecule.yml
verifier:
  name: testinfra
```

## molecule.ymlカスタマイズ例
``` molecule.yml
---
dependency:
  name: galaxy
driver:
  name: docker
lint: |
  yamllint .
  ansible-lint -x 701,703
  flake8
platforms:
  - name: instance
    image: ${DOCKER_IMAGE:-registry.example.com:5000/centos:7}
    pull: true
    pre_build_image: true
    privileged: true
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:rw
      - /sys/fs/cgroup:/sys/fs/cgroup:ro
    command: /sbin/init
    capabilities:
      - SYS_ADMIN
provisioner:
  name: ansible
  ansible_args:
    - "-e molecule_test=true"
  inventory:
    links:
      #hosts: ../../../inventory/hosts
      group_vars: ../../../..//group_vars/
      host_vars: ../../../../host_vars/
verifier:
  name: ansible
```


#参考
https://molecule.readthedocs.io/en/latest/