Ansible Molecule概要

# Moleculeとは 



Ansible Roleのテストツールです。
Ansibleは便利ですが、実際の開発にしっかり使おうとすると作ったPlaybook自体の品質を確保することが重要になってきます。
また、実際に環境に流してみないと分からないエラーもあったりして、結構大変だと思います。
そこで使えるのがこのMoleculeです。Ansibleで作ったPlaybookの部品(Role)単位で、テストを実行することができます。


## 何をしてくれるのか

- Lint … PlaybookがYAMLの文法に従っているか
- Provision … Playbookが実際にAnsibleを使用してちゃんと実行できるかと、2回目の実行で不要な変更が入らないか
- Verify … 別のテストツールを使って作った環境をテスト



### Lint

YAMLの文法に従っているかどうかをチェックします。
例) インデント、リストの書き方、変数の書き方などなど

Lintに何のソフトを使うかは後述の`molecule.yml`で指定できますが、特に要件がなければ普通はYAML Lintでいいと思います。

### Provision

Playbookが実際にAnsibleで読み込めて実行できるかをチェックします。ついでに、Ansible観点でのLintもここでします。
Lintの例) モジュール名が間違ってないか、非推奨の機能を使っていないか、潜在的なバグの原因になるような記載方法がないか

実行には、テスト用のインスタンスを作成して、そこに対してPlaybookを実行します。
どのような方法でテスト用のインスタンスを作成するかをMoleculeでは`Driver`と呼びます。デフォルトではDockerになっています。

Ansibleでは冪等性という考え方があって、同じPlaybookを何度実行しても結果は同じになるべきという考え方があります。
これを確認するために同じインスタンスに2回Playbookが実行されます。2回目で不要な変更が入っていた場合はエラーとして出ます。

### テストツール実行

別のテストツールを起動して、テスト用のインスタンスがちゃんと構築できているかを確認します。
別途テストコードを書く必要があります。
デフォルトではTestInfraが指定されていて、`tests`フォルダにテストコードを配置します。

# 実際に使う

## インストール

python  \>= 3.6, Ansible  \>= 2.8 が必要です。

インストールはpipで行います。

```shell
 python3 -m pip install --user "molecule[docker,lint]"
```



### Dockerコンテナ

Moleculeがインストール済みのDockerコンテナがRedhatから提供されています。
`docker pull quay.io/ansible/molecule`で取得できるので、こちらを使ってもよいです。

Dockerで使う場合は、カレントディレクトリをバインドしたりして、

```
docker run --rm -v ${PWD}:/work/some-role -w /work/some-role quay.io/ansible/molecule [Moleculeのコマンド]
```

などすれば使いやすいでしょう。some-roleは自分のrole名に置き換えてください。
注意として、このように使う場合はカレントディレクトリ名をRoleの名前に合わせておかないとMoleculeの実行が失敗します。
上記の例だと、`/work`に直接Roleを展開して実行するとRole not foundとなってしまいます。

## Role作成

Moleculeを使ったRoleを作成するには、Moleculeのコマンドを使用して、

```shell
molecule init role --role-name some-role --driver-name docker
```

とすると、`--role-name`に指定した名前の空のRoleを作成してくれます。
作成されたRoleには、`molecule`フォルダがあり、ここに各種の定義を書いていきます。
フォルダ構成はこんな感じになってます。

- **molecule**
  - **default**
    - **tests**
    - `Dockerfile.j2`
    - `INSTALL.rst`
    - `molecule.yml`
    - `playbook.yml`

### defaultフォルダ

moleculeフォルダの直下にはdefaultという名前のフォルダがあります。
Moleculeでは複数のシナリオでテストを定義できますので、シナリオを複数作成したい場合はこのフォルダを分けます。

### Dockerfile.j2

ドライバーとしてDockerを使用する場合に使用するDockerfileです。
自動生成なので基本的には触りません。

### molecule.yml

Lintにやテストツールに何を使うかなどを書いています。

```yaml
---
dependency:
  name: galaxy
driver:
  name: docker    # ドライバーはデフォルトでDocker
lint:
  name: yamllint    # LintツールはデフォルトでYamllint
platforms:
  - name: instance
    image: centos:7    # Dockerドライバーを使用する場合、テスト用のインスタンスに何を使うか
provisioner:
  name: ansible    # プロビジョニングに何を使うか。デフォルトはAnsibleです
  lint:
    name: ansible-lint
verifier:
  name: testinfra    # テストに何を使うか。デフォルトはAnsibleです
  lint:
    name: flake8
```

### playbook.yml

テスト用のインスタンスにRoleを適用する際に使うダミーPlaybookです。
基本的には変更しません。

```yaml
---
- name: Converge
  hosts: all
  roles:
    - role: ansible-role-test
```

## テストの実行

いざ実行してみましょう。

## 実行コマンド

Roleのフォルダで、以下のように実行します。

```shell
molecule test --all
```

インスタンスの作成などを個別に実行することもできますが、ここでは割愛します。

## 実行結果

こんな感じで実行結果が出ます。
この場合はYAMLの文法にエラーが出ています。

```
$ molecule test --all
--> Validating schema /var/jenkins_home/workspace/Ansible-Molecule/molecule/default/molecule.yml.
Validation completed successfully.
--> Test matrix
    
└── default
    ├── lint
    ├── cleanup
    ├── destroy
    ├── dependency
    ├── syntax
    ├── create
    ├── prepare
    ├── converge
    ├── idempotence
    ├── side_effect
    ├── verify
    ├── cleanup
    └── destroy
    
--> Scenario: 'default'
--> Action: 'lint'
--> Executing Yamllint on files found in /var/jenkins_home/workspace/Ansible-Molecule/...
    /var/jenkins_home/workspace/Ansible-Molecule/tasks/main.yml
      8:1       warning  comment not indented like content  (comments-indentation)
      8:16      error    no new line character at the end of file  (new-line-at-end-of-file)
    
An error occurred during the test sequence action: 'lint'. Cleaning up.
--> Scenario: 'default'
--> Action: 'destroy'
    
    PLAY [Destroy] *****************************************************************
    
    TASK [Destroy molecule instance(s)] ********************************************
    changed: [localhost] => (item=None)
    changed: [localhost]
    
    TASK [Wait for instance(s) deletion to complete] *******************************
    FAILED - RETRYING: Wait for instance(s) deletion to complete (300 retries left).
    ok: [localhost] => (item=None)
    ok: [localhost]
    
    TASK [Delete docker network(s)] ************************************************
    
    PLAY RECAP *********************************************************************
    localhost                  : ok=2    changed=1    unreachable=0    failed=0
```

どこがエラー内容かわかりますか？

```
    /var/jenkins_home/workspace/Ansible-Molecule/tasks/main.yml
      8:1       warning  comment not indented like content  (comments-indentation)
      8:16      error    no new line character at the end of file  (new-line-at-end-of-file)
```

この2行ですね。
コメントのインデントが不正(エラーではなく警告)なのと、ファイル末尾に改行がないことでエラーになっています。
こんな感じで実行結果を見ながら一つ一つ直していきましょう。

# CIツールとの連携

毎回毎回手でコマンドを実行してるのはやってられないので、CIツールと連携させて実行するのが良いと思います。

## Jenkinsfileの例

以下はJenkinsで実行するための例(Jenkinsfile)です。

```groovy
pipeline {
  agent {
    docker {
      image 'quay.io/ansible/molecule'
      args '-v /var/run/docker.sock:/var/run/docker.sock'
    }
  }

  stages {
    stage ('バージョンの表示') {
      steps {
        sh '''
          docker -v
          python -V
          ansible --version
          molecule --version
        '''
      }
    }

    stage ('Moleculeテスト実行') {
      steps {
        sh 'molecule test --all'
      }
    }
    
  }
}
```