Serverspecで実行されるコマンド結果を標準出力


# やりたいこと
Serverspecでテストを書いたとき、内部のコマンド結果がみれないので正しくテストできているか不安になったりします。コマンドと結果をファイルに書き出す方法は、別の方がすでに投稿されているのでそれを標準出力に出すようにしてみました。

参考：[Serverspec でコマンド実行のエビデンスを取得](http://qiita.com/ohtsuka1317/items/984041a1b94131547a21)

この投稿でファイルに書いている部分を、標準出力に出しただけです。

# 通常の結果表示
Serverspecを普通に実行するとこんな感じに、内部で実行しているコマンドの結果は表示されないです。

```
[root@console serverspec]# rake spec
/usr/bin/ruby -I/usr/local/share/gems/gems/rspec-core-3.4.2/lib:/usr/local/share/gems/gems/rspec-support-3.4.1/lib /usr/local/share/gems/gems/rspec-core-3.4.2/exe/rspec --pattern spec/localhost/\*_spec.rb

Package "httpd"
  should be installed

Service "httpd"
  should be enabled
  should be running

Port "80"
  should be listening

Command "ls -al /"
  stdout
    should match /bin/

Finished in 0.10108 seconds (files took 0.41086 seconds to load)
5 examples, 0 failures
```

# フォーマット変更後の結果表示
結果表示用のフォーマットをスクリプトで書いて、実行時に指定した場合、コマンドとその結果が標準出力に出るようになります。スクリプトは後述。

参考元だと.rspecファイルでフォーマット等を指定していますが、今回はデバック時だけ詳細表示したかったので、実行時のSPEC_OPTSでフォーマット等を指定するようにしています。

```
[root@console serverspec]# rake spec SPEC_OPTS="--format ServerspecDebugFormatter --require ./serverspec_debug_formatter.rb"                                                                  
/usr/bin/ruby -I/usr/local/share/gems/gems/rspec-core-3.4.2/lib:/usr/local/share/gems/gems/rspec-support-3.4.1/lib /usr/local/share/gems/gems/rspec-core-3.4.2/exe/rspec --pattern spec/localhost/\*_spec.rb

Package "httpd"
  should be installed
    -- command
    /bin/sh -c rpm\ -q\ httpd
    -- stdout
    httpd-2.4.6-40.el7.centos.x86_64

Service "httpd"
  should be enabled
    -- command
    /bin/sh -c systemctl\ --quiet\ is-enabled\ httpd
    -- stdout
  should be running
    -- command
    /bin/sh -c systemctl\ is-active\ httpd
    -- stdout
    active

Port "80"
  should be listening
    -- command
    /bin/sh -c ss\ -tunl\ \|\ grep\ --\ :80\\\ 
    -- stdout
    tcp    LISTEN     0      128      :::80                   :::*                  

Command "ls -al /"
  stdout
    should match /bin/
      -- command
      /bin/sh -c ls\ -al\ /
      -- stdout
      total 68
      dr-xr-xr-x  71 root root 4096 Feb 13 14:51 .
      dr-xr-xr-x  71 root root 4096 Feb 13 14:51 ..
      -rw-r--r--   1 root root    0 Feb  6 01:18 .autorelabel
      -rwxr-xr-x   1 root root    0 Feb  6 01:18 .dockerenv
      -rwxr-xr-x   1 root root    0 Feb  6 01:18 .dockerinit
      -rw-r--r--   1 root root   31 Feb 13 14:32 .rspec
      -rw-r--r--   1 root root  685 Feb 13 12:20 Rakefile
      lrwxrwxrwx   1 root root    7 Feb  6 01:22 bin -> usr/bin
      dr-xr-xr-x   3 root root 4096 Feb 13 12:32 boot
      drwxr-xr-x  15 root root 4380 Feb 11 01:02 dev
      drwxr-xr-x 133 root root 4096 Feb 13 12:32 etc
      drwxr-xr-x   2 root root 4096 Aug 12  2015 home
      lrwxrwxrwx   1 root root    7 Feb  6 01:22 lib -> usr/lib
      lrwxrwxrwx   1 root root    9 Feb  6 01:22 lib64 -> usr/lib64
      drwx------   2 root root 4096 Aug 14  2015 lost+found
      drwxr-xr-x   2 root root 4096 Aug 12  2015 media
      drwxr-xr-x   2 root root 4096 Aug 12  2015 mnt
      drwxr-xr-x   3 root root 4096 Feb 10 14:17 opt
      dr-xr-xr-x 196 root root    0 Feb 11 01:02 proc
      dr-xr-x---   8 root root 4096 Feb 13 14:31 root
      drwxr-xr-x  16 root root  360 Feb 13 15:59 run
      lrwxrwxrwx   1 root root    8 Feb  6 01:22 sbin -> usr/sbin
      drwxr-xr-x   1 1000 ftp   272 Feb  5 17:17 share
      drwxr-xr-x   5 root root 4096 Feb 13 12:29 spec
      drwxr-xr-x   2 root root 4096 Aug 12  2015 srv
      dr-xr-xr-x  13 root root    0 Feb 10 13:37 sys
      drwxrwxrwt  15 root root 4096 Feb 13 15:59 tmp
      drwxr-xr-x  46 root root 4096 Feb  6 01:22 usr
      drwxr-xr-x  53 root root 4096 Feb 13 12:32 var
      -- exit status
      0

Finished in 0.09723 seconds (files took 0.39293 seconds to load)
5 examples, 0 failures
```

# フォーマット変更用のスクリプト
デバック時に指定するフォーマット用のrubyスクリプトです。インデントや表示をごにょごにょするようにしていますが、処理的には参考元のスクリプトでファイル出力していたのをputsに変えただけです。

```rb:serverspec_debug_formatter.rb
require 'rspec/core/formatters/documentation_formatter'
require 'specinfra'
require 'serverspec/version'
require 'serverspec/type/base'
require 'serverspec/type/command'

class ServerspecDebugFormatter < RSpec::Core::Formatters::DocumentationFormatter
  RSpec::Core::Formatters.register self, :example_group_started,
                                   :example_passed, :example_pending, :example_failed

  def initialize(output)
    super
  end

  def example_group_started(notification)
    @indent = 2 * (@group_level + 2)
    super
  end

  def example_passed(notification)
    super
    puts_evidence(notification.example)
  end

  def example_pending(notification)
    super
    puts_evidence(notification.example)
  end

  def example_failed(notification)
    super
    puts_evidence(notification.example, notification.exception)
  end

  def puts_evidence(example, exception=nil)
    # 実行コマンド
    puts ' ' * @indent + '-- command'
    puts ' ' * @indent + example.metadata[:command]

    # 標準出力
    if example.metadata[:stdout]
      puts ' ' * @indent + '-- stdout'
      example.metadata[:stdout].lines do |line|
        puts ' ' * @indent + line
      end
    end

    # RSpecのエラーメッセージ
    if exception
      puts ' ' * @indent + '-- exception'
      exception.message.lines do |line|
        puts ' ' * @indent + line
      end
    end

    # 標準エラー出力と終了ステータス（commandリソースのみ）
    resource = example.metadata[:described_class]
    if resource.kind_of? Serverspec::Type::Command
      unless resource.stderr.to_s.empty?
        puts ' ' * @indent + '-- stderr'
        resource.stderr.lines do |line|
          puts ' ' * @indent + line
        end
      end
      puts ' ' * @indent + '-- exit status'
      puts ' ' * @indent + resource.exit_status.to_s
    end
  end
end
```

このスクリプトをこんな風に配置して、

```
[root@console serverspec]# tree .
.           
├── Rakefile
├── serverspec_debug_formatter.rb
└── spec
    ├── localhost
    │   └── sample_spec.rb
    └── spec_helper.rb
```

Serverspec実行時のSPEC_OPTSで指定してやれば、実行コマンド結果を標準出力することができます。

```
# rake spec SPEC_OPTS="--format ServerspecDebugFormatter --require ./serverspec_debug_formatter.rb"
```

普段はオプションなしのrake specで実行して、テストを追加したときのデバックや、きちんと証跡を残す時はオプション付きでフォーマットを変えてコマンド結果を表示するとかです。

# エラー出力がうまくとれない？
コマンドの標準エラー出力も表示できるはずだったんですが、なぜか標準エラー出力が表示されなかったです。そもそも標準エラー出力をテストする定義Serverspecで書いても、結果がブランクになったりするので環境が悪いのかも。原因がわかったら追記します。