Skip to content

tbuchi888/serverspec-run-on-ansible-playbook

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

5 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

serverspec-run-on-ansible-playbook

ServerspecをAnsibleのhostvars変数の値を使ってansible-playbook から実行する方法

はじめに

いままで

Ansibleのインベントリホスト(ターゲットホスト)に対してServerspecを実施する場合以下の方法があると思います。

No. 方法 特徴
1 Ansibleのcommandやshellモジュールから、rake specrake spec:hogeroleなどServerspecを直接呼び出す方法 AnsibleとServerspecで別々にinventoryやhostvarsを管理する必要がある
2 ansible_specなどAnsibleとServerspecのパーサーを使う方法 ansible_specはGemでインストール後に、指定の形式(Inventoryファイルやロールなど、ベストプラクティスディレクトリ構成に近い)をとれば、Ansibleのファイルを利用して、容易にAnsibleのロール単位でテスト実行が可能で便利.ただし、Ansible側でロール構成が必要なことやインベントリファイル内のVariablesが未だ対応していない等の制約もある
3 kitchen-ansiblemoleculeなどを利用する こちらは未検証ですが、Vagrantなどと連携して、テスト用のVMまで構築するイメージ

そこで

No.1に近い方法で、以下のAnsibleのtemplateモジュールとServerspecのTipsなど基本機能を利用して 「ServerspecをAnsibleのhostvars変数の値を使ってansible-playbook から実行する方法」 を検討しました。

参考にしたAnsibleとServerspecの基本機能やTips

事前準備

AnsibleとServerspec実行 HOSTマシン

  1. CentOS67 AnsibleとServerspecをインストール済み *1台を用意

サンプルのインベントリホスト(ターゲットホスト)マシン

こちらを参考に作ります

  1. Windows2012R2 IISインストール済み *2台を用意
  2. CentOS67 httpdインストール済み *2台を用意
  3. 上記HOSTマシン *1

Ansible側

  1. インベントリホスト(実行対象ホスト)毎のhostvarsをYAML形式でダンプするjinjya2のtemplateファイルをtemplates/dump_variables.j2として作成
  2. 以下をAnsible実行ホストマシンのlocalで実施するplaybook spec.yml を作成
  3. templateモジュールを利用して、インベントリホストのhostvarsの情報をYAML形式でカレントディレクトリに保存する
  4. shellモジュールで上記ファイルと、serverspecのロール名を引数にしてserverspecを実行する
  5. debugモジュールでserverspec実行結果を表示

dump_variables.j2

{{ hostvars[inventory_hostname] | to_yaml }}
#{{ vars | to_yaml }} 

spec.yml

---
- hosts: all
  gather_facts: no
  vars:
    spec_vars_dir:  "{{playbook_dir}}/spec_vars"
    host_vars_path: "{{spec_vars_dir}}/hostvars_{{inventory_hostname}}.yml"
  tasks:
   - name: create "{{spec_vars_dir}}" directory
     file:
       path: "{{spec_vars_dir}}"
       state: directory 
     delegate_to: localhost
   - name: "dump_variables hostvars to yml"
     template:
       src: templates/dump_variables.j2
       dest: "{{host_vars_path}}"
     delegate_to: localhost
   - name: rake serverspec with hostvars
     shell: HOST_VARS_PATH={{host_vars_path}} rake serverspec:{{spec_role}}
     register: raw_result
     delegate_to: localhost
   - name: stdout of serverspec
     debug: var=raw_result.stdout_lines

サンプルのAnsibleのインベントリファイル 

hosts

[win]
win2012-iis01 spec_role=AAA ansible_host=192.168.33.51
win2012-iis02 spec_role=BBB ansible_host=192.168.33.52

[centos]
centos6-httpd01 spec_role=AAA ansible_host=192.168.33.41
centos6-httpd02 spec_role=BBB ansible_host=192.168.33.42
myansible spec_role=BBB ansible_host=localhost

[win:vars]
ansible_connection=winrm
ansible_port=5985
ansible_user=vagrant
ansible_password=vagrant
#web_service_name="World Wide Web Publishing Service"
web_service_name="W3SVC"

[centos:vars]
ansible_user=vagrant
ansible_private_key_file=~/.ssh/id_rsa
web_service_name=httpd

Serverspec側

  1. Rakefileを以下通りカスタマイズ
  2. spec_helper.rbを以下通りカスタマイズ、合わせてマルチOS(windows/un*x)、マルチコネクション(ssh/local/winrm)対応も実施
  3. テスト用のspecファイルを準備

Rakefile

require 'rake'
require 'rspec/core/rake_task'
require 'yaml'

hostvars = YAML.load_file(ENV['HOST_VARS_PATH'])

namespace :serverspec do
  desc "Run serverspec for #{hostvars["spec_role"]}"
  RSpec::Core::RakeTask.new(hostvars["spec_role"].to_sym) do |t|
    t.pattern = "spec/#{hostvars["spec_role"]}/*_spec.rb"
  end
end

spec_helper.rb

require 'serverspec'
require 'net/ssh'
require 'winrm'
require 'yaml'

hostvars = YAML.load_file(ENV['HOST_VARS_PATH'])
set_property hostvars

if hostvars["ansible_connection"] == 'winrm'
#
# OS type: Windows / Connetion type: winrm
#
  set :backend, :winrm
  set :os, :family => 'windows'

  host = hostvars["ansible_ssh_host"]  || hostvars["ansible_host"]      || hostvars["inventory_hostname"]
  user = hostvars["ansible_ssh_user"]  || hostvars["ansible_user"]  
  port = hostvars["ansible_ssh_port"]  || hostvars["ansible_port"]    
  pass = hostvars["ansible_ssh_pass"]  || hostvars["ansible_password"]   

  endpoint = "http://#{host}:#{port}/wsman"

  winrm = ::WinRM::WinRMWebService.new(endpoint, :ssl, :user => user, :pass => pass, :basic_auth_only => true)
  winrm.set_timeout 300 # 5 minutes max timeout for any operation
  Specinfra.configuration.winrm = winrm

elsif hostvars["ansible_connection"] == 'local' || hostvars["ansible_ssh_host"] == 'localhost' || hostvars["ansible_host"] == 'localhost' || hostvars["inventory_hostname"] == 'localhost'
#
# OS type: UN*X / Connction type: local exec
#
 set :backend, :exec
else
#
# OS type: UN*X / Connction type: ssh
#
  set :backend, :ssh

  set :sudo_password, hostvars["ansible_ssh_pass"] || hostvars["ansible_password"]

  host = hostvars["ansible_ssh_host"] || hostvars["ansible_host"] || hostvars["inventory_hostname"]

  options = Net::SSH::Config.for(host)

  options[:user] ||= hostvars["ansible_ssh_user"]             || hostvars["ansible_user"]
  options[:port] ||= hostvars["ansible_ssh_port"]             || hostvars["ansible_port"]
  options[:keys] ||= hostvars["ansible_ssh_private_key_file"] || hostvars["ansible_private_key_file"]

  set :host,        options[:host_name] || host
  set :ssh_options, options

  # Disable sudo
  # set :disable_sudo, true

  # Set environment variables
  # set :env, :LANG => 'C', :LC_MESSAGES => 'C'

  # Set PATH
  # set :path, '/sbin:/usr/local/sbin:$PATH'
end

サンプルのServersepcのロールとspecファイル

  • ROLE:AAA
    • spec/AAA/sample_spec.rb
    • Ansibleのhostvarsに設定した変数web_service_nameのサービス状態を確認するサンプル

sample_spec.rb

require 'spec_helper'

describe service( property["web_service_name"] ) do
  it { should be_enabled }
  it { should be_running }
end
  • ROLE:BBB
    • spec/BBB/sample_spec.rb
    • Ansibleのインベントリファイルのinventory_hostnameをホスト名として含んでいるか確認するサンプル

sample_spec.rb

require 'spec_helper'

describe command('hostname') do
  its(:stdout) { should contain( "#{property['inventory_hostname']}" ) }
end

Directory構成

├── README.md
├── Rakefile                # Sererspec:カスタマイズしたもの
├── hosts                   # Ansible:  サンプルのインベントリファイル
├── spec
│   ├── AAA                 # Sererspec:サンプルのロール名
│   │   └── sample_spec.rb  # Sererspec:サンプルのspecファイル
│   ├── BBB                 # Sererspec:サンプルのロール名
│   │   └── sample_spec.rb  # Sererspec:サンプルのspecファイル
│   └── spec_helper.rb      # Sererspec:カスタマイズしたもの
├── spec.yml                # Ansible:  カスタマイズしたPlaybook
├── spec_vars               # Ansible:  playbook実行時に作成
│   ├── hostvars_centos6-httpd01.yml 
│   ├── hostvars_centos6-httpd02.yml
│   ├── hostvars_myansible.yml
│   ├── hostvars_mygitlab.yml
│   ├── hostvars_win2012-iis01.yml
│   └── hostvars_win2012-iis02.yml
└── templates               # Ansible:  hostvarsをダンプjinjya2テンプレート
    └── dump_variables.j2

実行結果

[vagrant@myansible work2]$ ansible-playbook -i hosts spec.yml

PLAY [all] *********************************************************************

TASK [create "/opt/work2/spec_vars" directory] *********************************
ok: [win2012-iis01 -> localhost]
ok: [win2012-iis02 -> localhost]
ok: [centos6-httpd01 -> localhost]
ok: [centos6-httpd02 -> localhost]
ok: [myansible -> localhost]

TASK [dump_variables hostvars to yml] ******************************************
changed: [win2012-iis02 -> localhost]
changed: [win2012-iis01 -> localhost]
changed: [centos6-httpd02 -> localhost]
changed: [centos6-httpd01 -> localhost]
changed: [myansible -> localhost]

TASK [rake serverspec with hostvars] *******************************************
changed: [myansible -> localhost]
changed: [centos6-httpd01 -> localhost]
changed: [centos6-httpd02 -> localhost]
changed: [win2012-iis02 -> localhost]
changed: [win2012-iis01 -> localhost]

TASK [stdout of serverspec] ****************************************************
ok: [win2012-iis01] => {
    "raw_result.stdout_lines": [
        "/home/vagrant/.rbenv/versions/2.2.0/bin/ruby -I/home/vagrant/.rbenv/versions/2.2.0/lib/ruby/gems/2.2.0/gems/rspec-support-3.4.1/lib:/home/vagrant/.rbenv/versions/2.2.0/lib/ruby/gems/2.2.0/gems/rspec-core-3.4.3/lib /home/vagrant/.rbenv/versions/2.2.0/lib/ruby/gems/2.2.0/gems/rspec-core-3.4.3/exe/rspec --pattern spec/AAA/\\*_spec.rb", 
        "", 
        "Service \"W3SVC\"", 
        " WARN  WinRM::WinRMWebService : WinRM::WinRMWebService#run_powershell_script is deprecated. Use WinRM::CommandExecutor#run_powershell_script instead", 
        "  should be enabled", 
        " WARN  WinRM::WinRMWebService : WinRM::WinRMWebService#run_powershell_script is deprecated. Use WinRM::CommandExecutor#run_powershell_script instead", 
        "  should be running", 
        "", 
        "Finished in 8.53 seconds (files took 5.67 seconds to load)", 
        "2 examples, 0 failures"
    ]
}
ok: [centos6-httpd01] => {
    "raw_result.stdout_lines": [
        "/home/vagrant/.rbenv/versions/2.2.0/bin/ruby -I/home/vagrant/.rbenv/versions/2.2.0/lib/ruby/gems/2.2.0/gems/rspec-support-3.4.1/lib:/home/vagrant/.rbenv/versions/2.2.0/lib/ruby/gems/2.2.0/gems/rspec-core-3.4.3/lib /home/vagrant/.rbenv/versions/2.2.0/lib/ruby/gems/2.2.0/gems/rspec-core-3.4.3/exe/rspec --pattern spec/AAA/\\*_spec.rb", 
        "", 
        "Service \"httpd\"", 
        "  should be enabled", 
        "  should be running", 
        "", 
        "Finished in 2.99 seconds (files took 3.3 seconds to load)", 
        "2 examples, 0 failures"
    ]
}
ok: [win2012-iis02] => {
    "raw_result.stdout_lines": [
        "/home/vagrant/.rbenv/versions/2.2.0/bin/ruby -I/home/vagrant/.rbenv/versions/2.2.0/lib/ruby/gems/2.2.0/gems/rspec-support-3.4.1/lib:/home/vagrant/.rbenv/versions/2.2.0/lib/ruby/gems/2.2.0/gems/rspec-core-3.4.3/lib /home/vagrant/.rbenv/versions/2.2.0/lib/ruby/gems/2.2.0/gems/rspec-core-3.4.3/exe/rspec --pattern spec/BBB/\\*_spec.rb", 
        "", 
        "Command \"hostname\"", 
        "  stdout", 
        " WARN  WinRM::WinRMWebService : WinRM::WinRMWebService#run_powershell_script is deprecated. Use WinRM::CommandExecutor#run_powershell_script instead", 
        "    should contain \"win2012-iis02\"", 
        "", 
        "Finished in 5.32 seconds (files took 5.65 seconds to load)", 
        "1 example, 0 failures"
    ]
}
ok: [centos6-httpd02] => {
    "raw_result.stdout_lines": [
        "/home/vagrant/.rbenv/versions/2.2.0/bin/ruby -I/home/vagrant/.rbenv/versions/2.2.0/lib/ruby/gems/2.2.0/gems/rspec-support-3.4.1/lib:/home/vagrant/.rbenv/versions/2.2.0/lib/ruby/gems/2.2.0/gems/rspec-core-3.4.3/lib /home/vagrant/.rbenv/versions/2.2.0/lib/ruby/gems/2.2.0/gems/rspec-core-3.4.3/exe/rspec --pattern spec/BBB/\\*_spec.rb", 
        "", 
        "Command \"hostname\"", 
        "  stdout", 
        "    should contain \"centos6-httpd02\"", 
        "", 
        "Finished in 3.74 seconds (files took 4.73 seconds to load)", 
        "1 example, 0 failures"
    ]
}
ok: [myansible] => {
    "raw_result.stdout_lines": [
        "/home/vagrant/.rbenv/versions/2.2.0/bin/ruby -I/home/vagrant/.rbenv/versions/2.2.0/lib/ruby/gems/2.2.0/gems/rspec-support-3.4.1/lib:/home/vagrant/.rbenv/versions/2.2.0/lib/ruby/gems/2.2.0/gems/rspec-core-3.4.3/lib /home/vagrant/.rbenv/versions/2.2.0/lib/ruby/gems/2.2.0/gems/rspec-core-3.4.3/exe/rspec --pattern spec/BBB/\\*_spec.rb", 
        "", 
        "Command \"hostname\"", 
        "  stdout", 
        "    should contain \"myansible\"", 
        "", 
        "Finished in 1.9 seconds (files took 3.59 seconds to load)", 
        "1 example, 0 failures"
    ]
}

PLAY RECAP *********************************************************************
centos6-httpd01            : ok=4    changed=2    unreachable=0    failed=0   
centos6-httpd02            : ok=4    changed=2    unreachable=0    failed=0   
myansible                  : ok=4    changed=2    unreachable=0    failed=0   
win2012-iis01              : ok=4    changed=2    unreachable=0    failed=0   
win2012-iis02              : ok=4    changed=2    unreachable=0    failed=0   

[vagrant@myansible work2]$ 

参考:AnsibleとServerspec実行環境

[vagrant@myansible work2]$ ansible --version
ansible 2.1.0 (devel f99ed97c40) last updated 2016/02/28 15:58:58 (GMT +100)
  lib/ansible/modules/core: (detached HEAD 45367c3d09) last updated 2016/02/28 15:59:27 (GMT +100)
  lib/ansible/modules/extras: (detached HEAD 479f99678b) last updated 2016/02/28 15:59:53 (GMT +100)
  config file = /etc/ansible/ansible.cfg
  configured module search path = Default w/o overrides
[vagrant@myansible work2]$ ruby -v
ruby 2.2.0p0 (2014-12-25 revision 49005) [x86_64-linux]
[vagrant@myansible work2]$ rbenv -v
rbenv 1.0.0-19-g29b4da7
[vagrant@myansible work2]$ gem list

*** LOCAL GEMS ***

ansible_spec (0.2.9)
bigdecimal (1.2.6)
builder (3.2.2)
bundler (1.11.2)
diff-lcs (1.2.5)
docile (1.1.5)
ffi (1.9.10)
gssapi (1.2.0)
gyoku (1.3.1)
hostlist_expression (0.2.1)
httpclient (2.7.1)
io-console (0.4.3)
json (1.8.3, 1.8.1)
little-plugger (1.1.4)
logging (2.0.0)
minitest (5.4.3)
multi_json (1.11.2)
net-scp (1.2.1)
net-ssh (3.0.2)
net-telnet (0.1.1)
nori (2.6.0)
oj (2.14.6)
power_assert (0.2.2)
psych (2.0.8)
rake (10.5.0, 10.4.2)
rdoc (4.2.0)
rspec (3.4.0)
rspec-core (3.4.3)
rspec-expectations (3.4.0)
rspec-its (1.2.0)
rspec-mocks (3.4.1)
rspec-support (3.4.1)
rubyntlm (0.6.0)
serverspec (2.30.0)
sfl (2.2)
simplecov (0.11.2)
simplecov-html (0.10.0)
specinfra (2.52.0)
test-unit (3.0.8)
winrm (1.7.2)
[vagrant@myansible work2]$ 

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages