Serverspec 最初の一歩 @ AWS EC2

## Serverspecとは
O'REILLY『Serverspec』P.5 によると以下の目的に使うサーバテストツールです。

1. テスト駆動によるインフラコード開発
2. サーバ構築後の確認作業の自動化
3. 稼働しているサーバの監視
4. サーバの再起動後の状態確認
5. サーバのあるべき状態の抽象化

Chef, Puppet, Ansibleと合わせて使うと、TDD（テスト駆動）なインフラコード開発が可能になります。


## 対象読者
AWS EC2上で*Serverspec*を体感してみたい初心者を対象としています。

+ 『Serverspec』を読んで、本に書いてある通りにコマンドを打っても動作しなかった人。
+ あらゆること（SSHでサーバー間の通信をする、Linuxのsudoはこう使うべき論、Rubyのgemはこう管理すべき論）を横に置いておいて、とにかくAWS EC2上で*Serverspec*を走らせてみたい人。
+ サーバの管理を任されてしまった新人（Rubyの知識、RSpecの知識はあまりなし）。

が対象です。

今回は、ローカルサーバの状態をテストします。
利用するコマンドは、以下の３つです。
　　・ <font color="#0000ff">**gem install**</font>
　　・ <font color="#0000ff">**serverspec-init**</font>
　　・ <font color="#0000ff">**rake spec**</font>


## 環境の準備
+ **EC2のインスタンスを作成**  
    選択したインスタンスは以下です。  
    「Amazon Linux AMI 2016.03.2 (HVM), SSD Volume Type - ami-6154bb00」  
    　  
+ **Serverspecインストール**
    以下の流れで*Serverspec*をインストールします。
    (1) ec2-userでEC2インスタンスにログイン
    (2) Amazon Linuxにrubyとgemがインストールされていることを確認
    (3) rootでRakeとServerspecをインストール
    　　インストールは、 <font color="#0000ff">**gem install**</font> コマンドで行います。

    ```Bash
    login as: ec2-user
    Authenticating with public key "imported-openssh-key"
    
           __|  __|_  )
           _|  (     /   Amazon Linux AMI
          ___|\___|___|
    
    https://aws.amazon.com/amazon-linux-ami/2016.03-release-notes/
    No packages needed for security; 2 packages available
    Run "sudo yum update" to apply all updates.
    [ec2-user@ip-172-31-24-21 ~]$ ruby -v
    ruby 2.0.0p648 (2015-12-16) [x86_64-linux]
    [ec2-user@ip-172-31-24-21 ~]$ gem -v
    2.0.14.1
    [ec2-user@ip-172-31-24-21 ~]$ sudo su -
    [root@ip-172-31-24-21 ~]# gem install rake
    Fetching: rake-11.2.2.gem (100%)
    Successfully installed rake-11.2.2
    Parsing documentation for rake-11.2.2
    Installing ri documentation for rake-11.2.2
    Done installing documentation for rake after 1 seconds
    1 gem installed
    [root@ip-172-31-24-21 ~]# gem install serverspec
    Fetching: rspec-support-3.4.1.gem (100%)
    Successfully installed rspec-support-3.4.1
    Fetching: rspec-core-3.4.4.gem (100%)
    Successfully installed rspec-core-3.4.4
    Fetching: diff-lcs-1.2.5.gem (100%)
    Successfully installed diff-lcs-1.2.5
    Fetching: rspec-expectations-3.4.0.gem (100%)
    Successfully installed rspec-expectations-3.4.0
    Fetching: rspec-mocks-3.4.1.gem (100%)
    Successfully installed rspec-mocks-3.4.1
    Fetching: rspec-3.4.0.gem (100%)
    Successfully installed rspec-3.4.0
    Fetching: rspec-its-1.2.0.gem (100%)
    Successfully installed rspec-its-1.2.0
    Fetching: multi_json-1.12.1.gem (100%)
    Successfully installed multi_json-1.12.1
    Fetching: net-ssh-3.1.1.gem (100%)
    Successfully installed net-ssh-3.1.1
    Fetching: net-scp-1.2.1.gem (100%)
    Successfully installed net-scp-1.2.1
    Fetching: net-telnet-0.1.1.gem (100%)
    Successfully installed net-telnet-0.1.1
    Fetching: sfl-2.2.gem (100%)
    Successfully installed sfl-2.2
    Fetching: specinfra-2.59.0.gem (100%)
    Successfully installed specinfra-2.59.0
    Fetching: serverspec-2.36.0.gem (100%)
    Successfully installed serverspec-2.36.0
    Parsing documentation for rspec-support-3.4.1
    Installing ri documentation for rspec-support-3.4.1
    Parsing documentation for rspec-core-3.4.4
    Installing ri documentation for rspec-core-3.4.4
    Parsing documentation for diff-lcs-1.2.5
    Installing ri documentation for diff-lcs-1.2.5
    Parsing documentation for rspec-expectations-3.4.0
    Installing ri documentation for rspec-expectations-3.4.0
    Parsing documentation for rspec-mocks-3.4.1
    Installing ri documentation for rspec-mocks-3.4.1
    Parsing documentation for rspec-3.4.0
    Installing ri documentation for rspec-3.4.0
    Parsing documentation for rspec-its-1.2.0
    Installing ri documentation for rspec-its-1.2.0
    Parsing documentation for multi_json-1.12.1
    Installing ri documentation for multi_json-1.12.1
    Parsing documentation for net-ssh-3.1.1
    Installing ri documentation for net-ssh-3.1.1
    Parsing documentation for net-scp-1.2.1
    Installing ri documentation for net-scp-1.2.1
    Parsing documentation for net-telnet-0.1.1
    Installing ri documentation for net-telnet-0.1.1
    Parsing documentation for sfl-2.2
    Installing ri documentation for sfl-2.2
    Parsing documentation for specinfra-2.59.0
    Installing ri documentation for specinfra-2.59.0
    Parsing documentation for serverspec-2.36.0
    Installing ri documentation for serverspec-2.36.0
    Done installing documentation for rspec-support, rspec-core, diff-lcs, rspec-expectations, rspec-mocks, rspec, rspec-its, multi_json, net-ssh, net-scp, net-telnet, sfl, specinfra, serverspec after 15 seconds
    14 gems installed
    ```

## テストの作成
+ **テスト作成コマンドを実行**
    - <font color="#0000ff">**serverspec-init**</font> コマンドで*Serverspec*の実行に最低限必要なファイルを作成します。
    - このコマンドを実行すると対話的に選択肢がでてきます。
    - 「1) UN*X」「2) Exec (local)」を選択します。

    ```Bash
    [root@ip-172-31-24-21 ~]# mkdir /etc/severspec
    [root@ip-172-31-24-21 ~]# cd /etc/severspec
    [root@ip-172-31-24-21 severspec]# serverspec-init
    Select OS type:
    
      1) UN*X
      2) Windows
    
    Select number: 1
    
    Select a backend type:
    
      1) SSH
      2) Exec (local)
    
    Select number: 2
    
     + spec/
     + spec/localhost/
     + spec/localhost/sample_spec.rb
     + spec/spec_helper.rb
     + Rakefile
     + .rspec
    
    ```
    
+ **スペック ファイル**
    「spec/localhost/sample_spec.rb」にサーバのテストが記載されています。
    内容は以下のようになっています。

    ```sample_spec.rb
    require 'spec_helper'
    
    describe package('httpd'), :if => os[:family] == 'redhat' do
      it { should be_installed }
    end
    
    describe package('apache2'), :if => os[:family] == 'ubuntu' do
      it { should be_installed }
    end
    
    describe service('httpd'), :if => os[:family] == 'redhat' do
      it { should be_enabled }
      it { should be_running }
    end
    
    describe service('apache2'), :if => os[:family] == 'ubuntu' do
      it { should be_enabled }
      it { should be_running }
    end
    
    describe service('org.apache.httpd'), :if => os[:family] == 'darwin' do
      it { should be_enabled }
      it { should be_running }
    end
    
    describe port(80) do
      it { should be_listening }
    end
    
    ```
    　<font color="#0000ff">**serverspec-init**</font> コマンドで作成したスペック ファイル（spec/localhost/sample_spec.rb）には様々なテストが記載されています。
    　ただ、Amazon Linuxのos familyは、

    ```Ruby
    os[:family] == 'amazon'
    ```
    です。
    　AWS EC2でServerspecのテストを実行すると、最後の３行だけが実行されます。

## テストの実行

+ **テストの実行**
    　/etc/severspec ディレクトリでテストを実行します。テストを実行するコマンドは、 <font color="#0000ff">**rake spec**</font> です。

+ **失敗テスト**
+ 
    ```sample_spec.rb
    describe port(80) do
      it { should be_listening }
    end
    ```
    　特に設定を変更していなければ、EC2のインスタンスは、22番ポートのみ開いてる状態です。80番ポートは開いていません。
    　この状態でテストを実行（<font color="#0000ff">**rake spec**</font>）させてみましょう。テストが失敗します。
    
    ```Bash
    [root@ip-172-31-24-21 severspec]# rake spec
    /usr/bin/ruby2.0 -I/usr/local/share/ruby/gems/2.0/gems/rspec-support-3.4.1/lib:/usr/local/share/ruby/gems/2.0/gems/rspec-core-3.4.4/lib /usr/local/share/ruby/gems/2.0/gems/rspec-core-3.4.4/exe/rspec --pattern spec/localhost/\*_spec.rb
    
    Port "80"
      should be listening (FAILED - 1)
    
    Failures:
    
      1) Port "80" should be listening
         On host `localhost'
         Failure/Error: it { should be_listening }
           expected Port "80" to be listening
           /bin/sh -c netstat\ -tunl\ \|\ grep\ --\ :80\\\
    
         # ./spec/localhost/sample_spec.rb:27:in `block (2 levels) in <top (required)>'
    
    Finished in 0.022 seconds (files took 0.34902 seconds to load)
    1 example, 1 failure
    
    Failed examples:
    
    rspec ./spec/localhost/sample_spec.rb:27 # Port "80" should be listening
    
    /usr/bin/ruby2.0 -I/usr/local/share/ruby/gems/2.0/gems/rspec-support-3.4.1/lib:/usr/local/share/ruby/gems/2.0/gems/rspec-core-3.4.4/lib /usr/local/share/ruby/gems/2.0/gems/rspec-core-3.4.4/exe/rspec --pattern spec/localhost/\*_spec.rb failed
    ```
    
+ **成功テスト**
    　それでは、テスト スペック ファイルを編集して、今度は成功するテストに変更してみましょう。
    　「spec/localhost/sample_spec.rb」の最後の３行を次のように変更します。
    
    ```sample_spec.rb
    describe port(22) do
      it { should be_listening }
    end
    
    describe service('ntpd') do
      it { should be_enabled }
      it { should be_running }
    end
    ```
    
    EC2のAmazon Linuxは、インスタンス生成直後は、以下の状態にあります。

    - 22番ポートが開いている
    - 「ntpd」サービスが利用可能
    - 「ntpd」サービスが稼働している

    この状態でテストを実行（<font color="#0000ff">**rake spec**</font>）すると、３つのテストが成功します。
    
    ```Bash
    [root@ip-172-31-24-21 localhost]# rake spec
    (in /etc/severspec)
    /usr/bin/ruby2.0 -I/usr/local/share/ruby/gems/2.0/gems/rspec-support-3.4.1/lib:/usr/local/share/ruby/gems/2.0/gems/rspec-core-3.4.4/lib /usr/local/share/ruby/gems/2.0/gems/rspec-core-3.4.4/exe/rspec --pattern spec/localhost/\*_spec.rb
    
    Port "22"
      should be listening
    
    Service "ntpd"
      should be enabled
      should be running
    
    Finished in 0.03241 seconds (files took 0.33734 seconds to load)
    3 examples, 0 failures
    ```

## Nextステップ
次は、リモートマシンのテストをSSH経由で行います。
　・[Serverspec で AWS EC2 のリモートテスト](http://qiita.com/olympic2020/items/c76fcf088daff31069ff)