LoginSignup
2
3

More than 5 years have passed since last update.

Serverspecでロジックとテスト内容を分離してテスト内容をYAMLで管理するようにしてみた。

Last updated at Posted at 2016-01-07

はじめに

前に書いた記事Serverspecでロジックとテストケースの分離を検討してみたの続きです。

テストケース

YAMLファイルにチェックしたい内容を記載します。
今回はapacheを例に取り上げました。

$TEST_HOME\sample\env\apache_conf.yml
# チェック内容
CONF:
  # ファイル内容の確認
  FILE:
    httpd.conf:
      PATH: C:\APP\Apache\etc\httpd\conf\httpd.conf
      STATUS: true
      MATCH:
        Server:
          - ServerRoot "C:/APP/Apache/etc/httpd"
          - ServerTokens ProductOnly
          - ServerSignature Off
        PidFile:
          - PidFile run/httpd.pid
        Timeout:
          - Timeout 120
        KeepAlive:
          - KeepAlive Off
        Include:
          - Include conf.d/*.conf
        LogFormat:
          - LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\" %D" combined_time
        LoadModule:
          - LoadModule authz_host_module modules/mod_authz_host.so
          - LoadModule log_config_module modules/mod_log_config.so
          - LoadModule deflate_module modules/mod_deflate.so
          - LoadModule headers_module modules/mod_headers.so
      UNMATCH:
        LoadModule:
          - LoadModule auth_digest_module modules/mod_auth_digest.so
          - LoadModule authn_alias_module modules/mod_authn_alias.so
  # レジストリ登録内容の確認
  REGISTRY:
    service:
      PATH: HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\services\Apache2.2
      STATUS: true
      MATCH:
        type_string:
          DisplayName: Apache2.2
        type_dword:
          Type: 16
        type_expandstring:
          ImagePath: \"C:\APP\Apache\bin\httpd.exe\" -k runservice
# ホスト毎のチェック項目マッピング
STG-WEB01:
  FILE:
    - httpd.conf
  REGISTRY:
    - service
STG-WEB02:
  FILE:
    - httpd.conf
  REGISTRY:
    - service

ファイル内容をチェックするspecファイル

specファイルからyamlファイルを読み込み、現在のサーバのテスト内容を判別して評価します。

$TEST_HOME\sample\spec\apache\apache_conf_spec.rb
require 'spec_helper'
require 'yaml'
require 'CmnClsHelper'

objHelper = CmnClsHelper.new
this_file_name = File.basename( __FILE__, ".rb" )
this_file_dir_path = File.dirname( File.expand_path( __FILE__ ) )
this_parent_name = File.basename( this_file_dir_path )
target_host      = "#{ENV['TARGET_HOST']}".upcase
this_yaml_name   = this_file_name.gsub("_spec", "")
path_f_yml       = "env/#{this_yaml_name}.yml"
strHR            = "=" * 60
path_d_parser    = "C:\\Tool\\Infra\\serverspec\\parser"
path_d_cmd       = "C:\\Tool\\Infra\\serverspec\\cmd"

puts "PATH_F_YAML = #{path_f_yml}"
h_all   = YAML.load_file(path_f_yml)

if h_all.key?(target_host) && h_all["CONF"] && h_all["CONF"].instance_of?( Hash )
  check_file_list   = []
  h_replace_list    = {}
  target_category   = "FILE"
  if h_all["#{target_host}"].instance_of?( Hash ) && h_all["#{target_host}"]["#{target_category}"]
    if h_all["#{target_host}"]["#{target_category}"].instance_of?( Array )
      h_all["#{target_host}"]["#{target_category}"].each do |filename|
        check_file_list   << filename
      end
    end
  end
  if h_all["#{target_host}"].instance_of?( Hash ) && h_all["#{target_host}"]["REPLACE"]
    if h_all["#{target_host}"]["REPLACE"].instance_of?( Hash )
      h_all["#{target_host}"]["REPLACE"].each do |target_var_name, h_val|
        h_replace_list["#{target_var_name}"]  = {}  if ! h_replace_list.key?("#{target_var_name}")
        h_val.each do |replace_key, replace_val|
          h_replace_list["#{target_var_name}"]["#{replace_key}"]  = "#{replace_val}"
        end
      end
    end
  end
  if h_all["CONF"]["#{target_category}"] && h_all["CONF"]["#{target_category}"].instance_of?( Hash )
    check_file_list.each do |target_name|
      context "#{strHR}\n[#{target_host}] #{this_parent_name}/#{this_file_name} : #{target_name}\n#{strHR}\n" do
        if h_all["CONF"]["#{target_category}"]["#{target_name}"] && h_all["CONF"]["#{target_category}"]["#{target_name}"].instance_of?( Hash )
          h_cur_test_case   = h_all["CONF"]["#{target_category}"]["#{target_name}"]
          ary_ignore_spaces = []
          h_check_list    = {}
          blnIsPipeParse    = false
          cmd_parser      = ""
          path_f_taget  = "NOT_DEFINED"
          path_f_taget  = h_cur_test_case["PATH"]               if h_cur_test_case["PATH"]
          if h_replace_list["PATH"] && h_replace_list["PATH"].instance_of?( Hash )
            h_replace_list["PATH"].each do |replace_key, replace_val|
              path_f_taget  = path_f_taget.gsub("#{replace_key}","#{replace_val}")
            end
          end
          if h_cur_test_case.key?("PIPE-PARSE") && objHelper.isNotNull(h_cur_test_case["PIPE-PARSE"])
            blnIsPipeParse    = true
            case File.extname(h_cur_test_case["PIPE-PARSE"])
              when ".rb" then
                cmd_parser  = "ruby #{path_d_parser}\\#{h_cur_test_case['PIPE-PARSE'].strip}"
              else
                cmd_parser  = "#{path_d_parser}\\#{h_cur_test_case['PIPE-PARSE'].strip}"
            end
          end
          if h_cur_test_case.key?("STATUS")
            stat_f_taget  = h_cur_test_case["STATUS"]
            describe file("#{path_f_taget}") do
              if stat_f_taget
                it { should be_file }
              else
                it { should_not be_file }
              end
            end
          end
          if h_cur_test_case.key?("IGNORE_MULTI_SPACES") && h_cur_test_case["IGNORE_MULTI_SPACES"].instance_of?( Array )
            ary_ignore_spaces = h_cur_test_case["IGNORE_MULTI_SPACES"]
          end
          ["MATCH", "UNMATCH","MATCH-REGEX", "UNMATCH-REGEX", "FILE-MATCH", "FILE-UNMATCH", "FILE-MATCH-REGEX", "FILE-UNMATCH-REGEX"].each do |check_action|
            blnIsCmd    = true
            blnIsMatch    = true
            blnIsUseRegex = false
            action      = "MATCH"
            if h_cur_test_case[check_action] && h_cur_test_case[check_action].instance_of?( Hash )
              case "#{check_action}"
                when "FILE-MATCH"
                  blnIsCmd    = false
                when "FILE-MATCH-REGEX"
                  blnIsCmd    = false
                  blnIsUseRegex = true
                when "FILE-UNMATCH"
                  blnIsCmd    = false
                  blnIsMatch    = false
                  action      = "UNMATCH"
                when "FILE-UNMATCH-REGEX"
                  blnIsCmd    = false
                  blnIsMatch    = false
                  blnIsUseRegex = true
                  action      = "UNMATCH"
                when "MATCH-REGEX"
                  blnIsUseRegex = true
                when "UNMATCH-REGEX"
                  blnIsMatch    = false
                  blnIsUseRegex = true
                  action      = "UNMATCH"
                when "UNMATCH"
                  blnIsMatch    = false
                  action      = "UNMATCH"
              end
              h_cur_test_case[check_action].each do |filter_name, ary_case|
                # 器の用意
                h_check_list["#{filter_name}"]        ||= {}
                h_check_list["#{filter_name}"]["MATCH"]   ||= []
                h_check_list["#{filter_name}"]["UNMATCH"] ||= []
                h_check_list["#{filter_name}"]["CMD"]   = blnIsCmd
                # 
                ary_case.each do |test_case|
                  blnIsIgnoreSpaces = ary_ignore_spaces.include?(filter_name) ? true : false
                  tmp         = objHelper.convCheckStr({"str" => "#{test_case}".strip, "blnIsUseRegex" => blnIsUseRegex, "blnIsIgnoreSpaces" => blnIsIgnoreSpaces})
                  tmp         = "#{tmp}".encode("Windows-31J")
                  h_check_list["#{filter_name}"]["#{action}"]   << "#{tmp}"   if "#{tmp}" != ""
                end
              end
            end
          end
          h_check_list.each do |filter_name, h_case|
            if h_case["CMD"]
              # 「|」で日本語が化けるため、バッチを用意(バッチ内の「|」だと日本語が化けない)
              exec_cmd        = "#{path_d_cmd}\\cat.cmd #{path_f_taget}"
              if blnIsPipeParse
                if "#{filter_name}".upcase != "NO_GREP"
                  exec_cmd    = "#{path_d_cmd}\\cat_parser_grep.cmd #{path_f_taget} #{filter_name} #{cmd_parser}"
                else
                  exec_cmd    = "#{path_d_cmd}\\cat_parser.cmd #{path_f_taget} #{cmd_parser}"
                end
              else
                if "#{filter_name}".upcase != "NO_GREP"
                  exec_cmd    = "#{path_d_cmd}\\cat_grep.cmd #{path_f_taget} #{filter_name}"
                end
              end
              # ファイル内容出力コマンド
              describe command("#{exec_cmd}") do
                h_case["MATCH"].each do |test_case|
                  its(:stdout) { should match /#{test_case}/ }
                end
                h_case["UNMATCH"].each do |test_case|
                  its(:stdout) { should_not match /#{test_case}/ }
                end
              end
            else
              describe file("#{path_f_taget}") do
                h_case["MATCH"].each do |test_case|
                  its(:content) { should match /#{test_case}/ }
                end
                h_case["UNMATCH"].each do |test_case|
                  its(:content) { should_not match /#{test_case}/ }
                end
              end
            end
          end
        end
      end
    end
  end
end

レジストリ設定内容をチェックするspecファイル

specファイルからyamlファイルを読み込み、現在のサーバのテスト内容を判別して評価します。

$TEST_HOME\sample\spec\apache\apache_registry_spec.rb
require 'spec_helper'
require 'yaml'
require 'CmnClsHelper'

objHelper = CmnClsHelper.new
this_file_name = File.basename( __FILE__, ".rb" )
this_file_dir_path = File.dirname( File.expand_path( __FILE__ ) )
this_parent_name = File.basename( this_file_dir_path )
target_host = "#{ENV['TARGET_HOST']}".upcase
path_f_yml = "env/apache_conf.yml"
strHR = "=" * 60
h_all = YAML.load_file(path_f_yml)
if h_all.key?(target_host) && h_all["CONF"] && h_all["CONF"].instance_of?( Hash )
  check_target_list = []
  target_category   = "REGISTRY"
  if h_all["#{target_host}"].instance_of?( Hash ) && h_all["#{target_host}"]["#{target_category}"]
    if h_all["#{target_host}"]["#{target_category}"].instance_of?( Array )
      h_all["#{target_host}"]["#{target_category}"].each do |target_name|
        check_target_list   << target_name
      end
    end
  end
  if h_all["CONF"]["#{target_category}"] && h_all["CONF"]["#{target_category}"].instance_of?( Hash )
    check_target_list.each do |target_name|
      context "#{strHR}\n[#{target_host}] #{this_parent_name}/#{this_file_name} : #{target_name}\n#{strHR}\n" do
        if h_all["CONF"]["#{target_category}"]["#{target_name}"] && h_all["CONF"]["#{target_category}"]["#{target_name}"].instance_of?( Hash )
          h_cur_test_case = h_all["CONF"]["#{target_category}"]["#{target_name}"]
          path_f_taget  = "NOT_DEFINED"
          path_f_taget  = h_cur_test_case["PATH"]               if h_cur_test_case["PATH"]
          if h_cur_test_case.key?("STATUS")
            stat_f_taget  = h_cur_test_case["STATUS"]
            describe windows_registry_key("#{path_f_taget}") do
              if stat_f_taget
                it { should exist }
              else
                it { should_not exist }
              end
            end
          end
          ["MATCH", "UNMATCH"].each do |check_action|
            if h_cur_test_case[check_action] && h_cur_test_case[check_action].instance_of?( Hash )
              blnIsMatch  = true
              blnIsMatch  = false     if check_action == "UNMATCH"
              describe windows_registry_key("#{path_f_taget}") do
                h_cur_test_case[check_action].each do |reg_type, h_list|
                  h_list.each do |key,val|
                    case reg_type
                      when "type_dword"
                      else
                        val = "#{val}".gsub("\\\"","_DQ_").gsub("_DQ_","\"")
                    end
                    case reg_type
                      when "type_string"
                        if blnIsMatch
                          it { should have_property("#{key}", :type_string) }
                          it { should have_property_value("#{key}", :type_string, "#{val}") }
                        else
                          it { should_not have_property("#{key}", :type_string) }
                          it { should_not have_property_value("#{key}", :type_string, "#{val}") }
                        end
                      when "type_dword"
                        if blnIsMatch
                          it { should have_property("#{key}", :type_dword) }
                          it { should have_property_value("#{key}", :type_dword, "#{val.to_s(16)}") }
                        else
                          it { should_not have_property("#{key}", :type_dword) }
                          it { should_not have_property_value("#{key}", :type_dword, "#{val.to_s(16)}") }
                        end
                      when "type_binary"
                        if blnIsMatch
                          it { should have_property("#{key}", :type_binary) }
                          it { should have_property_value("#{key}", :type_binary, "#{val}") }
                        else
                          it { should_not have_property("#{key}", :type_binary) }
                          it { should_not have_property_value("#{key}", :type_binary, "#{val}") }
                        end
                      when "type_qword"
                        if blnIsMatch
                          it { should have_property("#{key}", :type_qword) }
                          it { should have_property_value("#{key}", :type_qword, "#{val}") }
                        else
                          it { should_not have_property("#{key}", :type_qword) }
                          it { should_not have_property_value("#{key}", :type_qword, "#{val}") }
                        end
                      when "type_multistring"
                        if blnIsMatch
                          it { should have_property("#{key}", :type_multistring) }
                          it { should have_property_value("#{key}", :type_multistring, "#{val}") }
                        else
                          it { should_not have_property("#{key}", :type_multistring) }
                          it { should_not have_property_value("#{key}", :type_multistring, "#{val}") }
                        end
                      when "type_expandstring"
                        if blnIsMatch
                          it { should have_property("#{key}", :type_expandstring) }
                          it { should have_property_value("#{key}", :type_expandstring, "#{val}") }
                        else
                          it { should_not have_property("#{key}", :type_expandstring) }
                          it { should_not have_property_value("#{key}", :type_expandstring, "#{val}") }
                        end
                    end
                  end
                end
              end
            end
          end
        end
      end
    end
  end
end

実行

実行してみます。

PS C:\serverspec\sample> rake

##################################################
HOST: REMOTEHOST : development
##################################################
PATH_F_YAML = env/apache_conf.yml

============================================================
[REMOTESERVER] apache/apache_conf_spec : httpd.conf
============================================================
  File "C:\APP\Apache\etc\httpd\conf\httpd.conf"
    should be file
  Command "C:\Tool\Infra\serverspec\cmd\cat_grep.cmd C:\APP\Apache\etc\httpd\conf\httpd.conf Server"
    stdout
      should match /ServerRoot \"C:\/APP\/Apache\/etc\/httpd\"/
    stdout
      should match /ServerTokens ProductOnly/
    stdout
      should match /ServerSignature Off/
  Command "C:\Tool\Infra\serverspec\cmd\cat_grep.cmd C:\APP\Apache\etc\httpd\conf\httpd.conf PidFile"
    stdout
      should match /PidFile run\/httpd\.pid/
  Command "C:\Tool\Infra\serverspec\cmd\cat_grep.cmd C:\APP\Apache\etc\httpd\conf\httpd.conf Timeout"
    stdout
      should match /Timeout 120/
  Command "C:\Tool\Infra\serverspec\cmd\cat_grep.cmd C:\APP\Apache\etc\httpd\conf\httpd.conf KeepAlive"
    stdout
      should match /KeepAlive Off/
  Command "C:\Tool\Infra\serverspec\cmd\cat_grep.cmd C:\APP\Apache\etc\httpd\conf\httpd.conf Include"
    stdout
      should match /Include conf\.d\/\*\.conf/
  Command "C:\Tool\Infra\serverspec\cmd\cat_grep.cmd C:\APP\Apache\etc\httpd\conf\httpd.conf LogFormat"
    stdout
      should match /LogFormat \"%h %l %u %t \\\"%r\\\" %>s %b \\\"%\{Referer\}i\\\" \\\"%\{User\-Agent\}i\\\" %D\" combined_time/
  Command "C:\Tool\Infra\serverspec\cmd\cat_grep.cmd C:\APP\Apache\etc\httpd\conf\httpd.conf LoadModule"
    stdout
      should match /LoadModule authz_host_module modules\/mod_authz_host\.so/
    stdout
      should match /LoadModule log_config_module modules\/mod_log_config\.so/
    stdout
      should match /LoadModule deflate_module modules\/mod_deflate\.so/
    stdout
      should match /LoadModule headers_module modules\/mod_headers\.so/
    stdout
      should not match /LoadModule auth_digest_module modules\/mod_auth_digest\.so/
    stdout
      should not match /LoadModule authn_alias_module modules\/mod_authn_alias\.so/

============================================================
[REMOTESERVER] apache/apache_registry_spec : service
============================================================
  Windows registry key "HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\services\Apache2.2"
    should exist
  Windows registry key "HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\services\Apache2.2"
    should have property "DisplayName", :type_string
    should have property value "DisplayName", :type_string, "Apache2.2"
    should have property "Type", :type_dword
    should have property value "Type", :type_dword, "16"
    should have property "ImagePath", :type_expandstring
    should have property value "ImagePath", :type_expandstring, "\"C:\\APP\\Apache\\bin\\httpd.exe\" -k runservice"

Finished in 49.38 seconds (files took 1.82 seconds to load)
132 examples, 0 failures ← テストケースは記事作成に際して一部削除してます。

##################################################

PS C:\serverspec\sample>
2
3
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
2
3