LoginSignup
16
18

More than 5 years have passed since last update.

Serverspec でコマンド実行のエビデンスを取得

Posted at

Serverspecでインフラテストを自動化したのは良いんだけど、 RSpecのテスト結果でOKと出ただけで本当に信用して良いのか……? という心配性の人のために、Serverspecが内部でどんなコマンドを打って、どんな出力を見て結果を判定したかを記録に残してみます。

SSHバックエンドかつcommand,file,serviceリソースでしか試していません。SSHバックエンドなら大体大丈夫だと思いますが……

手順

普通に

serverspec-init

でSSHを選んでサンプルを作成した後、以下のファイルを作成し、

./formatters/serverspec_audit_formatter.rb
require 'rspec/core/formatters/documentation_formatter'
require 'specinfra'
require 'serverspec/version'
require 'serverspec/type/base'
require 'serverspec/type/command'
require 'fileutils'

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

  def initialize(output)
    super
    @seq = 0
  end

  def example_group_started(notification)
    @seq = 0 if @group_level == 0
    super
  end

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

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

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

  def save_evidence(example, exception=nil)
    # 以下のように、トップレベルに結果ディレクトリ名として使う名称を指定する前提
    # describe "テストケース1" do
    #   describe command('hostname') do
    #     its(:stdout) { should ... }
    #   end
    # end
    name = example.example_group.top_level_description

    # 出力先ディレクトリを {ホスト名}/{テスト名}/{連番} で作成
    host = ENV['TARGET_HOST'] || Specinfra.configuration.host
    @seq += 1
    d = "audit/#{host}/#{name}/#{@seq}"
    FileUtils.mkdir_p(d)

    # 実行内容をファイル出力
    Dir.chdir(d) do
      # 実行コマンド
      File.write('command.txt', example.metadata[:command])
      # 標準出力
      File.write('stdout.txt', example.metadata[:stdout]) if example.metadata[:stdout]
      # RSpecのエラーメッセージ
      File.open('exception.txt', 'w') {|f| f.puts exception.message } if exception
      # 標準エラー出力と終了ステータス(commandリソースのみ)
      resource = example.metadata[:described_class]
      if resource.kind_of? Serverspec::Type::Command
        File.write('stderr.txt', resource.stderr) unless resource.stderr.to_s.empty?
        File.write('exit_status.txt', resource.exit_status)
      end
    end
  end
end

.rspec ファイルも以下のように変更します。

.rspec
--color
--format ServerspecAuditFormatter
--require ./formatters/serverspec_audit_formatter.rb

出力結果

この状態で以下のようなテストを流すと……

./spec/host1/sample_spec.rb
require 'spec_helper'

describe 'テストケース1' do
  describe command('hostname') do
    its(:stdout) { should match /^host1$/ }
  end
end

./audit/host1/テストケース1/1/ ディレクトリに、

command.txt
sudo -p 'Password: ' /bin/sh -c hostname
stdout.txt
host1<改行>

のような結果が保存されます。

16
18
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
16
18