使用Ansible的hostvars变量值来执行ansible-playbook中的Serverspec的方法。(使用Ansible的hostvars变量值通过ansible-playbook来执行Serverspec的方法。)

由ansible-playbook运行的Serverspec,使用Ansible的hostvars变量。

这是2016年Ansible Advent Calendar第25篇文章。

0.首先

本文将介绍如何使用Ansible的hostvars变量值来执行ansible-playbook中的Serverspec的方法。

    • こちらは以前書いた記事を元にアップデートしたものです。

 

    コード一式は https://github.com/tbuchi888/serverspec-run-by-ansible-playbook.git へ上げています。すぐに試したい場合は以下手順を参照してください。
#Step1
git clone https://github.com/tbuchi888/serverspec-run-by-ansible-playbook.git
cd serverspec-run-by-ansible-playbook

#Step2
#インベントリファイル(hosts.yml)やspec/ロール名/配下等を自環境へ合わせて修正。

#Step3
ansible-plyabook -i hosts.yml spec.yml

请查看以下链接,了解Ansible官方推荐的测试方法,供参考:
http://docs.ansible.com/ansible/test_strategies.html

执行Serverspec到库存主机

如果对Ansible的主机清单(inventory)中的主机执行Serverspec测试,我认为有以下几种方法。

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

这次的方法

这次,我们考虑使用类似前面提到的第一种方法,通过使用Ansible的模板模块和Serverspec的技巧来运用以下基本功能,即通过使用Ansible的hostvars变量的值从ansible-playbook执行Serverspec的方法。

    • Ansible

FAQ: In a template, get all the IPs of all machines in a group

Serverspec

advanced tips: How to use host specific properties

具体来说,尽管有些费力,但在执行 ansible-playbook 时。

    • 各ターゲットホストのhostvarsの情報をAnsibleのtemplateモジュールを利用してYAML形式のファイルとしてdumpします。

 

    • 続けてServerspec呼出し時に先ほどdumpしたファイルのパスと

 

    テストしたいロール名(spec_role)を渡してあげる

通过这种方式实现。

image01.png

3. 环境

用Ansible和Serverspec在主机上执行。

    • OSX Yosemite

Ansible: 2.3.0 (devel 6c3de3c299) last updated 2016/12/22
Serverspec: 2.37.2

参考:样本的库存主机(目标主机)机器。

    • Windows2012R2 * 1台

IISインストール済

CentOS6.8 * 2台

apacheインストール済

另外,我们参考了这个样板机来制作本次的机器。另外,我们会将OSX用户的SSH公钥复制到vagrant用户的CentOS中。

请把以下句子用中文进行同义转述,只需要一个选项:
例)
Please paraphrase the following sentence in Chinese.
(Note: The given sentence might not be visible to the model.)

ssh-copy-id vagrant@192.168.33.41
ssh-copy-id vagrant@192.168.33.41

4. 提前准备

目录结构如下所示。

├── README.md
├── Rakefile                # Sererspec:カスタマイズしたもの
├── hosts.yml               # 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_win2012-iis01.yml
└── templates               
    └── dump_variables.j2      # Ansible:  hostvarsをダンプするjinjya2テンプレート

4.1. Ansible的准备工作

    インベントリホスト(実行対象ホスト)毎のhostvarsをYAML形式でダンプするjinjya2のtemplateファイルをtemplates/dump_variables.j2として作成します。
{{ hostvars[inventory_hostname] | to_yaml }}
    • Ansible実行ホストマシンのlocalで以下を実施するplaybookをspec.ymlとして作成します。

templateモジュールを利用して、インベントリホストのhostvarsの情報をYAML形式でカレントディレクトリ上のspec_vars/[inventory_hostname].ymlとして保存する
shellモジュールより、上記ファイルと、serverspecのロール名(spec_role)を引数にしてserverspecを実行する
debugモジュールでserverspec実行結果を表示する

---
- 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のインベントリファイルとなります。

 

    • 自環境に合わせて適宜変更してください。

 

    • なお、spec_roleに合致するserverspecを実施します。

 

    spec_roleを定義しない場合はserverspecのテスト対象外となります。
[win]
win2012-iis01 spec_role=AAA ansible_host=192.168.33.51

[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="W3SVC"

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

4.2. 准备Serverspec方面

Rakefileを以下の通りカスタマイズします。

hostvarsのYAML形式ファイルのパスを指定して、変数として取込ます。

spec_role名をロールとして指定します。

Rakefile -> Rake文件

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を以下通りカスタマイズします。

hostvarsのYAML形式ファイルのパスを指定して、変数として取込ます。
マルチOS(windows/un*x)、マルチコネクション(ssh/local/winrm)対応も実施します。

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
    • テスト用のspecファイルを準備します。

 

    こちらは自環境に合わせて適宜変更してください。

以下是Serversepc角色和spec文件的示例。

    • ROLE:AAA

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

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をホスト名として含んでいるか確認するサンプル

require 'spec_helper'

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

参考:运行结果

以下是在以下示例环境中运行playbook的结果。
对Windows服务器和CentOS服务器的每个角色都执行了Serverspec。

$ ansible-playbook -i hosts.yml spec.yml

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

TASK [create spec_vars directory] *******************************************************************************************************************************
changed: [win2012-iis01 -> localhost]
ok: [centos6-httpd02 -> localhost]
ok: [centos6-httpd01 -> localhost]

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

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

TASK [stdout of serverspec] *************************************************************************************************************************************
ok: [centos6-httpd02] => {
    "raw_result.stdout_lines": [
        "/System/Library/Frameworks/Ruby.framework/Versions/2.0/usr/bin/ruby -I/Library/Ruby/Gems/2.0.0/gems/rspec-support-3.5.0/lib:/Library/Ruby/Gems/2.0.0/gems/rspec-core-3.5.4/lib /Library/Ruby/Gems/2.0.0/gems/rspec-core-3.5.4/exe/rspec --pattern spec/BBB/\\*_spec.rb", 
        "", 
        "Command \"hostname\"", 
        "  stdout", 
        "    should contain \"centos6-httpd02\"", 
        "", 
        "Finished in 5.33 seconds (files took 2.5 seconds to load)", 
        "1 example, 0 failures"
    ]
}
ok: [centos6-httpd01] => {
    "raw_result.stdout_lines": [
        "/System/Library/Frameworks/Ruby.framework/Versions/2.0/usr/bin/ruby -I/Library/Ruby/Gems/2.0.0/gems/rspec-support-3.5.0/lib:/Library/Ruby/Gems/2.0.0/gems/rspec-core-3.5.4/lib /Library/Ruby/Gems/2.0.0/gems/rspec-core-3.5.4/exe/rspec --pattern spec/AAA/\\*_spec.rb", 
        "", 
        "Service \"httpd\"", 
        "  should be enabled", 
        "  should be running", 
        "", 
        "Finished in 5.62 seconds (files took 2.68 seconds to load)", 
        "2 examples, 0 failures"
    ]
}
ok: [win2012-iis01] => {
    "raw_result.stdout_lines": [
        "/System/Library/Frameworks/Ruby.framework/Versions/2.0/usr/bin/ruby -I/Library/Ruby/Gems/2.0.0/gems/rspec-support-3.5.0/lib:/Library/Ruby/Gems/2.0.0/gems/rspec-core-3.5.4/lib /Library/Ruby/Gems/2.0.0/gems/rspec-core-3.5.4/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 9.7 seconds (files took 2.48 seconds to load)", 
        "2 examples, 0 failures"
    ]
}

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

6. 其他

以下是一些关于在Windows服务器上使用Serverspec时的资源类型和设置的参考:
https://github.com/mizzy/serverspec/blob/master/WINDOWS_SUPPORT.md

广告
将在 10 秒后关闭
bannerAds