使用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测试,我认为有以下几种方法。
rake spec
やrake 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)を渡してあげる
通过这种方式实现。
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