Molecule, Serverspec による Ansible Role の開発環境構築

## はじめに

Molecule3系を使って、 Ansible Role を開発する環境を構築する手順について模索したので、結果を記録として残します。
なお、Molecule3系では Serverspec をサポートしなくなりましたが、手に馴染んでいるので、どうやったら使えるようになるのかも検討しました。


## 前提

* MacOS
* Python3系がインストールされている

## 必要なパッケージ等のインストール

Molecule は pip から、Serverspec は gem からインストールします。

### Molecule

```
pip3 install molecule
pip3 install docker
pip3 install 'molecule[docker]'
```

### Serverspec

Serverspec, Serverspec-runner をインストールします。
Serverspec-runner は、Serverspec の wrapper で、テスト結果をCSVや表形式で出力してくれるので、テスト完了後のまとめに便利なので、好んで使っています。

```
gem install serverspec
gem install serverspec-runner
gem install docker-api
```

## Role の雛形の作成

## Ansible Role 雛形の作成

`molecule init` を使います。

```
ROLE={{ ROLE名 }}
molecule init role ${ROLE}
cd ${ROLE}
vi molecule/default/molecule.yml
```

```molecule/default/molecule.yml
---
dependency:
  name: galaxy
driver:
  name: docker
platforms:
  - name: instance
    image: docker.io/centos:8
    pre_build_image: true
    privileged: True
    command: /sbin/init
provisioner:
  name: ansible
verifier:
  name: ansible
```

## Serverspec 雛形の作成

`serverspec-runner -r` を使います。

```
serverspec-runner -r .
vi spec/spec_helper.rb
```

```
--- spec_helper.rb.default	2020-08-27 08:14:59.000000000 +0900
+++ spec_helper.rb	2020-08-27 08:14:51.000000000 +0900
@@ -63,8 +63,9 @@
     set :ssh_options, options
     set :backend, :ssh
     set :request_pty, true
-  #else
-  #  set :backend, :exec
+  else
+     set :backend, :docker
+     set :docker_container, 'instance'
   end

   prev_desc_hierarchy = nil
```

host がデフォルトの 127.0.0.1 のときは instance という名前の Docker コンテナを対象にするようにしました。


## 開発の例

### Roleのサンプル

httpdをインストールするタスクとテストを書いてみます。

```tasks/main.yml
---
- name: install httpd package
  yum:
    name: httpd
    state: latest

- name: start httpd
  systemd:
    name: httpd
    state: started
    enabled: yes
```

```molecule/default/verify.yml
---
- name: Verify
  hosts: all
  tasks:
  - ignore_errors: yes
    block:
    - name: httpd package
      shell: yum list installed | grep -e '^httpd\.'
      register: result_rpm

    - name: httpd proccess
      shell: ps -ef | grep http[d]
      register: result_proc

    - name: httpd service
      shell: systemctl is-enabled httpd
      register: result_enabled

  - name: result
    assert:
      that: "{{ result.failed == false }}"
    loop:
      - "{{ result_rpm }}"
      - "{{ result_proc }}"
      - "{{ result_enabled }}"
    loop_control:
      loop_var: result
```

### Molecule の実行

```
# Dockerコンテナの作成、Roleの実行、テスト、コンテナの破棄まで全て行う
molecule test

# テストまで行う(コンテナは破棄しない)
molecule converge

# Dockerコンテナの破棄
molecule destroy
```

### Serverspecのサンプル

```spec/httpd/sample_spec.rb
require 'spec_helper'

describe package('httpd'), :if => os[:family] == 'redhat' do
  it { should be_installed }
end
describe service('httpd'), :if => os[:family] == 'redhat' do
  it { should be_enabled }
  it { should be_running }
end
describe port(80) do
  it { should be_listening }
end
```
```scenario.yml
httpd:
  - anyhost-01
---
anyhost-01:
  host:        127.0.0.1
```

### Serverspecの実行

```
serverspec-runner
```

## driver に vagrant を使う

上記の手順で `molecule test` を実行すると、以下のような `firewalld` のタスクでエラーになってしまいました。

```tasks/main.yml
- name: "open firewalld ntp port"
  firewalld:
    zone: public
    service: ntp
    permanent: yes
    state: enabled
    immediate: yes
```

driver に docker ではなく、 vagrant を指定するとエラーは解消されたので、手順を以下に示します。

### Molecule

`molecule-vagrant` をインストールします。

```
pip3 install molecule-vagrant
```

`molecule.yml` は以下のように編集します。

```molecule/default/molecule.yml
dependency:
  name: galaxy
driver:
  name: vagrant
  provider:
    name: virtualbox
platforms:
  - name: instance
    box: bento/centos-8
    interfaces:
      - network_name: private_network
        ip: 192.168.33.11
provisioner:
  name: ansible
verifier:
  name: ansible
```

なお、 `molecule init` を実行するときに `--driver-name vagrant` を指定すると driver の指定ができます。

```
molecule init role ${ROLE} --driver-name vagrant
```

### Serverspec

driver を vagrant にした場合は、 scenario.yml を以下のようにします。

```scenario.yml
example:
  - anyhost-01
---
anyhost-01:
  host: 192.168.33.11
  ssh_opts:
    user: vagrant
    password: vagrant
```

## 参考

* [Ansible Molecule (公式サイト)](https://molecule.readthedocs.io/en/latest/)
* [Software Design 2020年6月号, Ansible問題解決マップ](https://gihyo.jp/magazine/SD/archive/2020/202006)
* [GitLabとMoleculeでPlaybookのテストをしてみる - 腐女子エンジニアの日記](https://dcn-f.hatenablog.com/entry/2020/02/10/140230)
* [起動中のdockerコンテナにserverspecを実行する](https://blog.1mg.org/20190725_01/)