「Serverspec advanced Tips」を見て、以下のような使い方ができないか考えてみました。
- テストケースを複数サーバで使いまわす。
- 全サーバ共通の変数を yaml (spec_all.yml)で定義する。
- サーバ個別の変数を yaml (spec_<IPアドレス>.yml)で定義する。
- ロール単位でテストケースを作成する。
- テスト対象のロールは、yaml で定義し、コマンドラインでファイル名を指定する。
- コマンドラインでテスト対象のIPアドレスを指定する。
- コマンドラインでSSHユーザ/パスワードを指定する。
ディレクトリ・ファイル構成は以下のようになります。
[user01@node01 serverspec]$ tree . ├── Rakefile ├── roles.yml ├── spec │ ├── 00_common │ │ └── os_spec.rb │ ├── 01_private │ │ └── os_spec.rb │ ├── 99_sample │ │ └── sample_spec.rb │ └── spec_helper.rb └── vars ├── spec_192.168.56.11.yml └── spec_all.yml
- Rakefile
対象ロール(roles.yml)のテストケース(*_spec.rb)を実行するように定義します。 - roles.yml
対象ロールを定義します。ファイル名は任意です。 - spec ディレクトリ
この配下にロール単位のディレクトリを作成します。ロール名は任意です。 - spec/<ロール名>ディレクトリ
この配下にテストケースを作成します。ファイル名は *_spec.rb です。(*は任意) - spec_helper.rb
対象サーバへのSSH接続や、変数の読み込みなどを行います。 - vars ディレクトリ
この配下に変数ファイルを定義します。全サーバ共通の変数は、"spec_all.yml" に定義します。サーバ個別の変数は、"spec_<IPアドレス>.yml" に定義します。
テストケースの実行方法は以下のとおりです。
パラメータは環境変数で渡します。
env TARGET_HOST=192.168.56.11 TARGET_USER=centos TARGET_PASSWORD=p@ssw0rd SUDO_PASSWORD=p@ssw0rd TARGET_ROLES=roles.yml rake spec:all
- TARGET_HOST
テスト対象のサーバのIPアドレスを指定する - TARGET_USER
SSH接続に使用するユーザを指定する。このユーザで sudo が使えるようにしておくこと。 - TARGET_PASSWORD
SSH接続ユーザのパスワードを指定する - SUDO_PASSWORD
SUDOのパスワードを指定する - TARGET_ROLES
対象ロールファイル名を指定する
Rakefile は以下のとおり
require 'rake' require 'rspec/core/rake_task' require 'yaml' task :spec => 'spec:all' roles_yaml = YAML.load_file(ENV['TARGET_ROLES']) namespace :spec do short_name = 'all' role = roles_yaml['roles'].join(',') desc "Run serverspec to all" RSpec::Core::RakeTask.new(short_name) do |t| t.pattern = "spec/{#{role}}/*_spec.rb" end end
spec_helper.rb は以下のとおり
require 'serverspec' require 'net/ssh' require 'yaml' set :backend, :ssh if ENV['ASK_SUDO_PASSWORD'] begin require 'highline/import' rescue LoadError fail "highline is not available. Try installing it." end set :sudo_password, ask("Enter sudo password: ") { |q| q.echo = false } else set :sudo_password, ENV['SUDO_PASSWORD'] end host = ENV['TARGET_HOST'] options = Net::SSH::Config.for(host) options[:user] = ENV['TARGET_USER'] options[:password] = ENV['TARGET_PASSWORD'] 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' # sudoersに「Defaults requiretty」が設定されている場合 set :request_pty, true # 全体共通の値を書いたファイルをつかう。 all_vars = YAML.load_file( File.expand_path("../../vars/spec_all.yml", __FILE__) ) # サーバ固有の値を書いたファイルがあればつかう。 host_vars = YAML.load_file( File.expand_path("../../vars/spec_#{host}.yml", __FILE__) ) if File.exists?(File.expand_path("../../vars/spec_#{host}.yml", __FILE__)) spec_property = all_vars spec_property[:host_vars] = host_vars ||= {} #puts spec_property.to_yaml set_property spec_property
roles.yml は以下のとおり
--- roles: - 00_common - 01_private
spec_all.yml は以下のとおり
# os_basic :os_rel: "6.7" :os_lang: "ja_JP.UTF-8" :os_localtime: "JST" :os_zone: "Asia/Tokyo"
spec_192.168.56.11.yml は以下のとおり
# private :system: R&D :env: Develop :role: web :hostname: node01
00_common/os_spec.rb は以下のとおり
require 'rubygems' require 'spec_helper' # OS version describe command('cat /etc/redhat-release') do its(:stdout) { should match /#{property[:os_rel]}/ } end # LANG describe command('grep LANG /etc/sysconfig/i18n') do its(:stdout) { should match /#{property[:os_lang]}/ } end # timezone describe command('date') do its(:stdout) { should match /#{property[:os_localtime]}/ } end describe command('grep ZONE /etc/sysconfig/clock') do its(:stdout) { should match /#{property[:os_zone]}/ } end # selinux describe selinux do it { should be_disabled } end # firewall describe service('iptables') do it { should_not be_enabled } it { should_not be_running } end describe service('ipi6tables') do it { should_not be_enabled } it { should_not be_running } end
00_private/os_spec.rb は以下のとおり
require 'rubygems' require 'spec_helper' # hostname describe file('/etc/sysconfig/network') do it { should be_file } it { should be_mode 644 } it { should be_owned_by 'root' } it { should be_grouped_into 'root' } it { should contain "HOSTNAME=#{property[:host_vars][:hostname]}" } end
実行結果は以下のとおり。
[user01@node01 serverspec]$ env TARGET_HOST=192.168.56.11 TARGET_USER=centos TARGET_PASSWORD=p@ssw0rd SUDO_PASSWORD=p@ssw0rd TARGET_ROLES=roles.yml rake spec:all /usr/bin/ruby -I/usr/lib/ruby/gems/1.8/gems/rspec-support-3.4.0/lib:/usr/lib/ruby/gems/1.8/gems/rspec-core-3.4.0/lib /usr/lib/ruby/gems/1.8/gems/rspec-core-3.4.0/exe/rspec --pattern spec/\{00_common,01_private\}/\*_spec.rb Command "cat /etc/redhat-release" stdout should match /6.7/ Command "grep LANG /etc/sysconfig/i18n" stdout should match /ja_JP.UTF-8/ Command "date" stdout should match /JST/ Command "grep ZONE /etc/sysconfig/clock" stdout should match /Asia\/Tokyo/ SELinux should be disabled Service "iptables" should not be enabled should not be running Service "ipi6tables" should not be enabled should not be running File "/etc/sysconfig/network" should be file should be mode 644 should be owned by "root" should be grouped into "root" should contain "HOSTNAME=node01" Finished in 1.51 seconds (files took 0.43561 seconds to load) 14 examples, 0 failures
これで、一応、想定通りの動きをするようになったと思います。
ただし、上記サンプルでは、"rake -T" でタスク一覧を表示することはできません。