Test Kitchen で複数の suite をテストする場合の注意点

業務で詰まっていたので展開します

## 環境背景
Test Kitchen ＆ Ansible という構成の、サーバ構築自動化テストの仕組みがある。
具体的に言うと、Test Kitchen で仮想サーバ（vagrant）を作成し、
それに対して Ansible を走らせる、という仕組み。

テストは github の CI/CD で走らせていた。

#### Test Kitchen について

Test Kitchen は、Chef によるサーバ構築などのレシピを
効率よくテストするためのツール。
テスト用の VM 作成、レシピの適用、テスト実施、VM 削除を
一度に実行することができる。
基本、yaml ファイルでテストの定義を記述する。

※[公式サイト](https://kitchen.ci/)
※書き方次第では Ansible のテストにも使えます

#### 環境構成

※業務に関係する情報であるため、本記事に関連する最低限の内容を記載

+ OS(Linux Distribution): **CentOS Linux release 7.7**
+ Test Kitchen: **version 2.1.0**
+ Vagrant: **version 2.2.16**


## やろうとしたこと
以下の様なホストを作り、テストを通そうとした。

+ Instance 名（仮称）
  - ins-centos
  - stg-ins-centos

~~~
* .kitchen.yml テスト部分の抜粋
driver:
  name: vagrant
  pre_create_command: <構築前に実行するコマンド>

～中略～

suites:
  - name: ins-centos
    driver:
      vm_hostname: ins-centos.local
    provisioner:
      extra_vars: <vars file 名>
  - name: stg-ins-centos
    driver:
      vm_hostname: stg-ins-centos.local
    provisioner:
      extra_vars: <vars file 名>
~~~


## 起きたエラー
stg-ins-centos のテストは成功するが、
ins-centos のテストで毎回 fail する。

```
ERROR: Job failed: exit status 1
```

テストで実行しているコマンドは以下。
`bundle exec kitchen test ins`

Ansible の部分や、test spec では特にエラーが出ていなかった。


## 調査
### CI/CD のログを確認
CI/CD の実行結果途中に以下の例外が出ていた。

```
>>>>>> ------Exception-------
>>>>>> Class: Kitchen::UserError
>>>>>> Message: Unknown lifecycle hook target {}
>>>>>> ----------------------
>>>>>> Please see .kitchen/logs/kitchen.log for more details
>>>>>> Also try running `kitchen diagnose --all` for configuration
```

Exception 欄に、
`Unknown lifecycle hook target {}`
という kitchen のエラーが出ている。

### エラー内容を確認
早速、このエラーを検索してみると、
github の test-kitchen の issue に同様の問題の記事を発見した。

+ `https://github.com/test-kitchen/test-kitchen/issues/1498`

どうやら、.kitchen.yml で定義している pre_create_command が原因らしい。
複数の suites（複数インスタンスへのテスト）を同時に実行する場合、
pre_create_command を指定するとエラーになるようだ。

```
* kitchen.yml の設定（再掲）
driver:
  name: vagrant
  pre_create_command: <構築前に実行するコマンド>
　◆↑ここが問題の箇所

～中略～

suites:
  - name: ins-centos
    driver:
      vm_hostname: ins-centos.local
    provisioner:
      extra_vars: <vars file 名>
  - name: stg-ins-centos
    driver:
      vm_hostname: stg-ins-centos.local
    provisioner:
      extra_vars: <vars file 名>
```

ただ、今回の場合、
テストするインスタンス名を個々に指定していたにも関わらず、このエラーが出た。
（インスタンス毎にテストを実施しているので、エラーは出ないはず）

### CI/CD のログを再確認
不思議に思ってログを見てみると、以下の記述を発見した。

```
$ bundle exec kitchen destroy ins
-----> Starting Kitchen (v2.1.0)
-----> Destroying <ins-centos>...
       Finished destroying <ins-centos> (0m0.00s).
-----> Destroying <stg-ins-centos>...　　＝＞指定してないインスタンスがいる
       Finished destroying <stg-ins-centos> (0m0.00s).
-----> Kitchen is finished. (0m2.75s)
```

最後の destroy コマンドにて、
テストをした ins-centos の destroy だけでなく、
stg-ins-centos の destroy まで走っている。

どうやら、kitchen コマンドで指定するインスタンス名は
REGEX（正規表現）として扱われるようで、
ins が stg-ins-centos にも部分一致していたらしい。

その結果、テストが2台のインスタンスに対して実施され、
前述の pre_create_command を2台に適用しようとしてエラーになっていたようだ。


## エラーの原因
エラーの原因は2つ

### 1. 直接の原因
複数の suites（複数インスタンスへのテスト）を同時に実行する場合、
pre_create_command を指定するとテストがエラーになる。
（kitchen create の時点でエラーになるようだ）


### 2. 間接的な原因（今回の真因）
今回、Test Kitchen で実行したコマンドは以下の通り。
`bundle exec kitchen test ins`

意図としては、ins（ins-centos）にだけテストを行う予定だったが、
kitchen コマンドで指定したインスタンス名は部分一致になるので、
このコマンドで実行されるインスタンスは以下の2台になる。

+ ins-centos
+ stg-ins-centos

さらに、本テストには pre_create_command が指定されていたので、
エラー原因1により、テストが失敗していた。


## 対策
### 1. インスタンス名が部分一致で被らないようにする（これが多分正解）
  + 例：インスタンス名を prd-ins-centos, stg-ins-centos の様にする
    - `vagrant destroy prd-ins`
    - `vagrant destroy stg-ins`

◆同じような名前のホストが多い場合は、接頭辞やホスト番号をちゃんとつける。

### 2. kitchen コマンドに渡すインスタンス名を正規表現で完全一致にする
  + 例：`bundle exec kitchen destroy "^ins-centos$"`

◆確実ではあるが、長いインスタンス名を毎回記述するのは面倒でもある

### 3. ~~kitchen コマンドに渡すインスタンス名は省略しないで指定する~~（非推奨）
  + 例：`bundle exec kitchen destroy ins-centos`

◆上記の例の通りに実行しても、stg-ins-centos に後方一致してしまうため、結局解決にならない


## 教訓
ログの最後の部分にエラーが見つからなくても、
よーく途中経過を見てみると、想定外の挙動をしていることがある。
それをログから読み取ることが大事。
（UNIX の文字色でエラーを判断している場合は見逃しに注意）

今回で言うなら、
vagrant destroy でテスト対象外のホストを削除しようとしていた部分。
自分では kitchen コマンドに1台のインスタンスを指定したはずなのに、実は（正規表現で）2台指定されていた。
※気づくのに2時間かかりました；

### 番外：Test Kitchen で複数の suite をテストするメリット
本記事の趣旨とは違う為、深掘りはしませんが、
同じような構成の複数ホストに対してテスト行う際は、便利だと思います。

ただ、本記事の不具合の原因でもある、
pre_create_command での事前準備ができない、という欠点もあるため、
個人的には shell などで wrap し、
Test Kitchen を1 suite（ホスト）ずつ叩いた方が良いのではないか、
と思います。