ansible_specの導入方法

## 概要
ansible_specの導入方法です。
https://github.com/volanja/ansible_spec

ansible_specとは、ansibleのinventoryファイルを、serverspecからも読み込めるようにするgemです。これにより、ansible, serverspecそれぞれでhostの設定をする必要がなくなります。

既存のansibleに追加する手順を書きます。

## 関連記事
- [Serverspec環境構築手順](https://qiita.com/Esfahan/items/2c80f84a7ea3f71f5037)

## 環境
- CentOS 7.5
- ruby 2.5.1

## 前提
serverspecはruby製のツールなため、rubyが必要になります。
事前にrubyをインストールしておいてください。

参考：[rubyのインストール方法](https://qiita.com/Esfahan/items/1a497fe75405561ef433)

また、serverspecでnginxのインストール、プロセス起動のチェックをするため、事前にansibleでインストール、起動をしておいてください。

## ディレクトリ構造
ansibleが既に存在している場所に対して、以下の用にServerspecが入り込む形になります。
これらのディレクトリは、後述の手順で作成されるので、事前に手動で作成する必要はありません。

```diff
 .
+├── Gemfile
+├── Gemfile.lock
 ├── hosts
 │   └── sample.hosts
+├── Rakefile
+├── .ansiblespec
+├── .rspec
 ├── README.md
 ├── roles
 │   └── nginx
 │       ├── handlers
 │       │   └── main.yml
+│       ├── spec
+│       │   └── nginx_spec.rb
 │       ├── tasks
 │       │   └── main.yml
 │       ├── templates
 │       │   ├── default.conf.j2
 │       │   └── nginx.conf.j2
 │       └── vars
 │           └── main.yml
 ├── site.yml
+├─ spec
+│   └── spec_helper.rb
+└── vendor
+    └── bundle
```

## 初期設定
### ansible_specのインストール
Gemfileを作成します。

```rb:Gemfile
source "https://rubygems.org"

git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }

gem 'rake'
gem 'ansible_spec'
```

ansible_specをインストールします。

```shell-session
$ bundle install --path vendor/bundle
```

### ansible_spec初期設定

```shell-session
$ bundle exec ansiblespec-init
                create  spec
                create  spec/spec_helper.rb
                create  Rakefile
                create  .ansiblespec
                create  .rspec
```

### specファイルを作成
nginxのインストールと、起動しているかのテストを作ります。

roles/nginx/spec/nginx_spec.rbの位置に作成します。

```rb:roles/nginx/spec/nginx_spec.rb
require 'spec_helper'

describe package do
  describe package('nginx') do
    it { should be_installed }
  end

  describe service('nginx') do
    it { should be_enabled }
    it { should be_running }
  end
end
```

### ansibleのinventoryファイルを確認
hosts/sample.hostsとして作成します。
samplehost01.com, samplehost02.comの2つのhostを対象とします。

```ini:hosts/sample.hosts
[servers]
samplehost01.com
samplehost02.com
```

### .ansiblespecの編集
.ansiblespecで、playbookとinventoryのパスを指定します。

```yaml:.ansiblespec
---
-
  playbook: site.yml
  inventory: hosts/sample.hosts
```

### sshd_configの作成
~/.ssh/configなどに、接続先サーバーの接続情報を記載します。
今回は、samplehost01.com, samplehost02.com両方とも同じ接続情報を使用するので、`Host *`としています。

他にも設定方法はありますが、既存のansibleに導入するには、これが一番シンプルな方法かと思います。

```config
Host *
  user username
  port 22
  IdentityFile ~/.ssh/id_rsa
```

環境変数にsshd_configのパスを設定します。

```shell-session
$ export SSH_CONFIG_FILE=~/.ssh/config
```

### Rakefileの編集
このままテストを実行すると、`No backend type is specified. Fall back to :exec type.`というエラーが出ます。
これは、通常serverspecの導入時に初期設定で実行する、`bundle exec serverspec-init`というコマンドで、`Select a backend type:`の設定をしていないことが原因のようです。

なので、以下のようにRakefileを修正します。
(これがベストな対応なのか分かりません)

```diff:Rakefile
namespace :serverspec do
  properties = properties.compact.reject{|e| e["hosts"].length == 0}
  task :all => properties.map {|v| 'serverspec:' + v["name"] }
  properties.each_with_index.map do |property, index|
    property["hosts"].each do |host|
      desc "Run serverspec for #{property["name"]}"
      RSpec::Core::RakeTask.new(property["name"].to_sym) do |t|
        puts "Run serverspec for #{property["name"]} to #{host}"
        ENV['TARGET_HOSTS'] = host["hosts"]
        ENV['TARGET_HOST'] = host["uri"]
        ENV['TARGET_PORT'] = host["port"].to_s
        ENV['TARGET_GROUP_INDEX'] = index.to_s
        ENV['TARGET_PRIVATE_KEY'] = host["private_key"]
        unless host["user"].nil?
          ENV['TARGET_USER'] = host["user"]
        else
          ENV['TARGET_USER'] = property["user"]
        end
        ENV['TARGET_PASSWORD'] = host["pass"]
-       ENV['TARGET_CONNECTION'] = host["connection"]
+       ENV['TARGET_CONNECTION'] = 'ssh'

        roles = property["roles"]
        for role in property["roles"]
          for rolepath in cfg.roles_path
            deps = AnsibleSpec.load_dependencies(role, rolepath)
            if deps != []
              roles += deps
              break
            end
          end
        end
        t.pattern = '{' + cfg.roles_path.join(',') + '}/{' + roles.join(',') + '}/spec/*_spec.rb'
      end
    end
  end
end
```

### site.ymlにnameディレクティブ
ansibleのsite.ymlに、nameディレクティブが無いと、テストを実行したときにエラーが出るので、設定してください。
このnameディレクティブは、rakeコマンドで個別にテストを実行する際に指定するものになります。

```yaml:site.yml
---
- name: servers
  hosts: servers
  become: yes
  roles:
    - { role: nginx }
```

### rakeコマンドで確認
以下のコマンドでエラーがでなければ、初期設定は完了です。

```shell-session
$ bundle exec rake -T
rake all                 # Run serverspec to all test
rake serverspec:servers  # Run serverspec for servers
```

## テスト実行
以下のコマンドで全てのテストケースが実行されます。

```rb
$ bundle exec rake all
Run serverspec for Orchestration for web to {"name"=>"samplehost01.com", "port"=>22, "uri"=>"samplehost01.com"}
/usr/local/bin/ruby -I/code/vendor/bundle/ruby/2.5.0/gems/rspec-core-3.8.0/lib:/code/vendor/bundle/ruby/2.5.0/gems/rspec-support-3.8.0/lib /code/vendor/bundle/ruby/2.5.0/gems/rspec-core-3.8.0/exe/rspec --pattern \{/etc/ansible/roles,/usr/share/ansible/roles,roles\}/\{nginx\}/spec/\*_spec.rb

nginx
  Package "nginx"
    should be installed
  Service "nginx"
    should be enabled
    should be running

Finished in 1.37 seconds (files took 0.55945 seconds to load)
3 examples, 0 failures

Run serverspec for Orchestration for web to {"name"=>"samplehost02.com", "port"=>22, "uri"=>"samplehost02.com"}
/usr/local/bin/ruby -I/code/vendor/bundle/ruby/2.5.0/gems/rspec-core-3.8.0/lib:/code/vendor/bundle/ruby/2.5.0/gems/rspec-support-3.8.0/lib /code/vendor/bundle/ruby/2.5.0/gems/rspec-core-3.8.0/exe/rspec --pattern \{/etc/ansible/roles,/usr/share/ansible/roles,roles\}/\{nginx\}/spec/\*_spec.rb

nginx
  Package "nginx"
    should be installed
  Service "nginx"
    should be enabled
    should be running

Finished in 1.35 seconds (files took 0.50547 seconds to load)
3 examples, 0 failures
```

今はテストケースが1つしかありませんが、個別にserversだけ指定するためには、以下のコマンドを実行します。

```shell-session
$ bundle exec rake serverspec:servers
```

sshd_configのパスの指定は、以下のようにテスト実行と同時に設定することも可能です。

```shell-session
$ SSH_CONFIG_FILE=~/.ssh/config bundle exec rake all
```

## sudoパスワードについて
以下を参照
[ansible_specでsudoパスワードが設定できない](https://qiita.com/Esfahan/items/06c9a18be5b3120f460b)

以上