インフラ自動テストツール ServerSpec 個人メモ

Ansibleを使用し、Node.jsのインストールを行なった以下記事の続きです。

[[初心者向け] Ansible を体験してみよう](https://qiita.com/Hiro-AGU/items/c6e1c86193f23d7cf026)

ServerSpecはサーバテストを自動化することができるフレームワークです。

今回は、前回の記事でインストールしたNode.jsが正常に入っているかどうかServerSpecを使用し、確認したいと思います。

## 構成の確認

前回に引き続き、MacOSに仮想マシンを二つ立てている構成です。

* Vagrantfile

```
Vagrant.configure(2) do |config|
 config.vm.define "ConfigurationManagementServer" do |node|
    node.vm.box = "bento/ubuntu-18.04"
    node.vm.hostname = "ConfigurationManagementServer"
    node.vm.network :private_network, ip:"192.168.7.7"
    node.vm.provider "virtualbox" do |vb|
      vb.customize ["modifyvm", :id, "--memory", "1024"]
    end
    node.vm.provider "virtualbox" do |vb|
      vb.gui = false
    end
  end
  config.vm.define "AnsibleClient" do |node|
    node.vm.box = "bento/ubuntu-18.04"
    node.vm.hostname = "AnsibleClient"
    node.vm.network :private_network, ip:"192.168.8.8"
    node.vm.provider "virtualbox" do |vb|
      vb.customize ["modifyvm", :id, "--memory", "1024"]
    end
    node.vm.provider "virtualbox" do |vb|
      vb.gui = false
    end
  end
end
```

192.168.8.8が割り当てられている`AnsibleClient`には既にNode.jsがインストールされています。

```
$ vagrant ssh AnsibleClient
vagrant@AnsibleClient:~$ node -v
v10.17.0
```

上記では、手動でコマンドを打ちversionを確認しましたが、もう一つの仮想マシンからServerSpecを使用しテストを行います。

## ServerSpecインストール・ファイルの編集

`AnsibleClient`環境から抜け、`ConfigurationManagementServer`環境に入ります。 

```
$ vagrant ssh ConfigurationManagementServer
vagrant@ConfigurationManagementServer:~$
```

Rubyのインストールを行います。
今回は`rbenv`などは使用せず、aptでインストールします。

```
vagrant@ConfigurationManagementServer:~$ sudo apt-get install ruby
vagrant@ConfigurationManagementServer:~$ ruby --version
ruby 2.5.1p57 (2018-03-29 revision 63029) [x86_64-linux-gnu] 
```

必要なライブラリをインストールします。

```
vagrant@ConfigurationManagementServer:~$ sudo gem install serverspec rake
```

ファイル群を作成します。
`target host name`では、テストを行う対象のIPアドレスを指定しますが、ここで入力された値がそのままディレクトリ名になるみたいです。 

```
vagrant@ConfigurationManagementServer:~$ serverspec-init
Select OS type:

  1) UN*X
  2) Windows

Select number: 1

Select a backend type:

  1) SSH
  2) Exec (local)

Select number: 1

Vagrant instance y/n: n
Input target host name: 192.168.8.8
 + spec/
 + spec/192.168.8.8/
 + spec/192.168.8.8/sample_spec.rb
 + spec/spec_helper.rb
 + Rakefile
 + .rspec
```

テスト項目を記述するファイルは`spec/192.168.8.8/sample_spec.rb` です。
ファイルを以下のように編集します。
`node -v`というコマンドを実行し、特定のバージョンがインストールされているか確認するテスト項目です。 

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

describe command('node -v') do
  its(:stdout) { should match /v10.17.0/ }
end
```

ファイル編集完了後、`Rakefile`があるディレクトリで以下コマンドを実行します。
パスワードを聞かれるので、接続先のパスワードを入力します。今回は`vagrant`です。

```
vagrant@ConfigurationManagementServer:~$ rake

vagrant@192.168.8.8's password: 
Command "node -v"
stdout is expected to match /v10.17.0/

Finished in 48.27 seconds (files took 1.33 seconds to load)
1 example, 0 failures
```

上記結果から、`AnsibleClient`サーバには `Node.js v10.17.0` がインストールされていることがわかりました。
テスト方法は他にも様々存在するため、詳細は以下を参照してください。

https://serverspec.org/

ServerSpecはデフォルトで一つのサーバしか実行できない仕様なので、以下に複数サーバ対応方法を記載します。  

## 複数サーバ対応方法

上記で記述した通り、デフォルトでは一つのサーバしかテストを行えないので、以下方法で複数対応を行います。
ファイルの編集を行う前に、仮想マシンをもう一つ立ち上げ、`Node.js v10.17.0` のインストールを予め行なっています。
今回は`192.168.9.9`を割り当てた`AnsibleClient2`を立ち上げています。

```
$ vagrant ssh AnsibleClient2
vagrant@AnsibleClient2:~$ node -v
v10.17.0
```

次に、ServerSpecを実行するサーバに戻り、以下ディレクトリ構成に変更します。

```
.
├── Rakefile
├── properties.yml       # 新規作成
└── spec
    ├── ansibleclient    # 192.168.8.8というディレクトリ名を変更
    │   └── node_ver_spec.rb  # sample_spec.rbというファイル名を変更
    ├── ansibleclient2   # 新規作成
    │   └── node_ver_spec.rb  # 新規作成
    └── spec_helper.rb
```

spec_helper.rbファイルにライブラリの読み込みを追記します。

```spec/spec_helper.rb

require 'serverspec'
require 'net/ssh'
require 'pathname'       # 追記
require 'rspec/its'      # 追記

set :backend, :ssh
set :disable_sudo, true  # 追記
```

次に、Rakefileを以下のように編集します。 
どこかのサイトから参照しています。 

```Rakefile
require 'rake'
require 'rspec/core/rake_task'
require 'yaml'

properties = YAML.load_file('properties.yml')

task :spec    => 'spec:all'
task :default => :spec
namespace :spec do
  task :all => properties.keys.map {|key| 'spec:' + key.split('.')[0] }
  properties.keys.each do |key|
    desc "Run spec to #{key}"
    RSpec::Core::RakeTask.new(key.split('.')[0].to_sym) do |t|
      ENV['TARGET_HOST'] = key
      puts "\n========================================"
      puts "HOSTNAME: #{key}"
      t.fail_on_error = false
      t.pattern = 'spec/{' + properties[key][:roles].join(',') + '}/*_spec.rb'
    end
  end
end
```

ファイル編集完了後、Rakefileと同じディレクトリに properties.ymlを作成し、本ファイルに接続先の定義を行います。
properties.ymlでは、テストを実行するファイル内で使用できる変数を定義することが可能です。
今回は、`ansibleclient2`のテストで変数 `node_ver`を定義しています。

```properties.yml      
192.168.8.8:          # 接続先を指定
  :roles:             # 実行するroleを指定
    - ansibleclient

192.168.9.9:
  :roles:
    - ansibleclient2
  :node_ver: "v10.17.0"
```

ansibleclient2のテストは以下の通りです。
こちらもnode.jsのversionを確認しているテスト項目です。

```spec/ansibleclient2/node_ver_spec.rb
require 'spec_helper'

describe command('node -v') do
  its(:stdout) { should match /#{property[:node_ver]}/ }
end
```

ファイル編集は上記で完了です。
テストを再度実行します。

```
$ rake
```

1回目のテストが終了後、`ansibleclient2`のテストが開始されたでしょうか。
特に問題がなければ、両方のサーバ共にテストは成功していると思います。

## おわりに

個人的なメモとして残しているため、わかりにくい箇所があるかもしれません。