docker-compose で Goss 試してみた

# はじめに

これまで業務でサーバ構築後のテストツールとして、[Serverspec](https://serverspec.org/) を使用していました。

Ansible と組み合わせて、Serverspec のテストケースの生成、実行などの自動化を行いましたが、手動でやるよりは確実に速いものの **実行にそこそこ時間がかかってしまう** という欠点を感じていました。

> 人間、どんどん欲深くなるものですね、恐ろしや恐ろしや

そんなある日、[Goss](https://github.com/aelsabbahy/goss) というテストツールがあることを知ったので試してみました。



# Serverspec と比較した Goss の特徴

|                  | Serverspec                                                | Goss                                 |
| ---------------- | --------------------------------------------------------- | ------------------------------------ |
| 環境構築         | そこそこ簡単、テスト対象に接続可能な 1 ホストにあればいい | テスト対象全てにシングルバイナリ配布 |
| 実行速度*         | 速い (100テストケース:10秒くらい)                         | 爆速(500テストケース:1秒くらい)     |
| テストケース形式 | Ruby 形式                                                 | YAML 形式                            |
| テストケース生成 | 手動                                                      | 一部自動生成可能                     |

> 実行速度に関しては、テスト対象が物理マシンか VM か、などで変わると思うがあくまで参考

# 環境構築

テストの趣旨として、Serverspec をそのまま Goss に置き換え、これまで通り Ansible と組み合わせてテストの自動実行を行います。

## 環境

docker-compose で **ansible-server** と **test-target** の2つのコンテナを立ち上げ、テストを行います。

**ansible-server** に Goss をインストールして、そのバイナリをテスト対象に Ansible でばら撒いてテストを行うといった流れです。

```
┌────────────────┐          ┌─────────────┐ 
│ ansible-server │ =[SSH]=> │ test-target │
└────────────────┘          └─────────────┘
```



ディレクトリ構成

```shell
.
├── ansible-server
│   ├── Dockerfile
│   ├── config
│   └── test-target-key
├── docker-compose.yml
├── test-target
│   ├── Dockerfile
│   └── test-target-key.pub
└── work
    ├── inventory.yml
    ├── roles
    │   └── goss
    │       ├── files
    │       │   ├── goss
    │       │   └── goss.yaml
    │       ├── tasks
    │       │   └── main.yml
    │       ├── templates
    │       └── vars
    │           └── main.yml
    └── site.yml
```



**Dockerfile [ansible-server]**

```dockerfile
FROM ubuntu:18.04

WORKDIR /root

# SSH Settings
COPY ./test-target-key /root/.ssh/
COPY ./config /root/.ssh/
# Install common tools
RUN apt update && apt install --yes curl vim
# Install ansible
RUN apt install --yes software-properties-common && \
    apt-add-repository --yes --update ppa:ansible/ansible && \
    apt install --yes ansible
# Install Goss
RUN curl -L https://github.com/aelsabbahy/goss/releases/latest/download/goss-linux-amd64 -o /usr/local/bin/goss && \
    chmod +rx /usr/local/bin/goss && \
    cp -i $(which goss) .

CMD ["tail", "-f", "/dev/null"]
```



**Dockerfile [test-target]**

```dockerfile
FROM ubuntu:18.04

WORKDIR /root

RUN apt update && apt install --yes openssh-server
RUN mkdir /var/run/sshd
RUN echo 'root:P@ssw0rd' | chpasswd
COPY ./test-target-key.pub /root/.ssh/authorized_keys
EXPOSE 22

# For Test.
RUN echo "this is test." > test.txt
RUN apt install --yes nginx
EXPOSE 80

CMD ["/usr/sbin/sshd", "-D"]
```



**docker-compose.yml**

```yaml
---
  version: "2"
  services:
    ansible-server:
      build: ./ansible-server
      volumes:
        - ./work:/root/work

    test-target:
      build: ./test-target
      ports:
        - 8080:80
```



## 起動

```shell
$ docker-compose build
$ docker-compose up -d
```



# テスト

**test-target** の Dockerfile 内の記述、 `# For Test.` 以下のテストを行います。

- `test.txt` に `this is test.` と記述されていること
- Nginx がインストールされていること
- Nginx が起動していること
- 80 ポートで Listen していること



テストケースは、YAML 形式で記述します。

**goss.yaml**

```yaml
file:
  /root/test.txt:
    exists: true
    contains: ["this is test."]

port:
  tcp:80:
    listening: true
    ip:
    - 0.0.0.0
  tcp6:80:
    listening: true
    ip:
    - '::'
service:
  nginx:
    enabled: true
    running: false
process:
  nginx:
    running: true
```



Ansible のプレイブックは割愛



## テスト実行

```shell
$ docker-compose exec ansible-server /bin/bash
root@38b380ec3969:~# cd work/
root@38b380ec3969:~/work# ansible-playbook -i inventory.yml site.yml
```

```shell
ok: [test-target] => {
    "msg": {
        "changed": true,
        "cmd": [
            "./goss",
            "validate",
            "--format",
            "documentation"
---
        "stdout_lines": [
            "File: /root/test.txt: exists: matches expectation: [true]",
            "File: /root/test.txt: contains: matches expectation: [this is test.]",
            "Port: tcp:80: listening: matches expectation: [true]",
            "Port: tcp:80: ip: matches expectation: [[\"0.0.0.0\"]]",
            "Port: tcp6:80: listening: matches expectation: [true]",
            "Port: tcp6:80: ip: matches expectation: [[\"::\"]]",
            "Process: nginx: running: matches expectation: [true]",
            "Service: nginx: enabled: matches expectation: [true]",
            "Service: nginx: running: matches expectation: [false]",
            "",
            "",
            "Total Duration: 0.006s",
            "Count: 9, Failed: 0, Skipped: 0"
        ]
    }
}
```



**9 のテストケースを 0.006 秒でクリア** してますね、爆速。



# まとめ

- Goss はやはり爆速
- Ansible との相性も良さそう
- テストケース作るのも簡単 (紹介してないが、今回のテストケースはほとんど自動生成した)

他にも、環境に合わせてテストケースを切り替えたりする機能や、for ループでテストを回す機能などもある


# さいごに

今回作成したソースコードは [GItHub](https://github.com/guromityan/docker_goss_handson) で公開しています。

Goss で凝ったテストを作ろうとすると、やはり Golang の知識が必要そう、時代はやはり Golang か