serverspec で複数ホストを指定する方法が、本家のサンプルにありますが、そのままコピペしてしまうと1つ1つホスト名を書くことになります。本家 → How to share serverspec tests among hosts
こちとら数百台テストしたいときもあるので、こんなことやってらません。ということで、pdsh ライクにレンジを指定して簡単にホストを指定できるようにして見ました。
まず、本家のサンプル通りに Rakefile と spec_helper.rb を書いておきます。
Rakefile と同じ所に、次のような pdhosts.rb を作成します。
def gen_hosts(pattern)
_range = pattern.scan(/\[.*\]/)
# Return the pattern as is because no range specified
return pattern if _range.size == 0
# Multiple geometry of ranges is not supported
if _range.size > 1
raise "Invalid argument: #{pattern}"
end
range = _range.first.gsub(/\[|\]/,'')
hosts = []
# parse each range
range.split(',').each do |range2|
first, second = range2.split('-')
second = first if second.nil?
for num in Range.new(first, second)
hosts << pattern.gsub(/\[#{range}\]/, num)
end
end
return hosts
end
def pdhosts(raw_hosts)
# split to host patterns divided by ","
patterns = raw_hosts.scan(/[^,]*\[[0-9,\-]*\][^,]*|[^,]*/).select{|s| s != ""}
patterns.map{ |s| gen_hosts(s) }.flatten
end
pdhosts は、pdsh の -w オプションで指定するようにレンジを指定できます。ansible と違ってコロンではなく、以下のようにハイフンとカンマを使います。
- proxy[001-008].example.jp
- web[1,2,5-10].example.jp
これを文字列として pdhosts に渡せば全部展開してくれます。
例として、本家の Rakefile にあるホスト指定を書き換えてみました。ホスト指定以外の他の部分はそのまま使うことができますので、残しておく必要があります。
require 'rake'
require 'rspec/core/rake_task'
### ここから ###
require './pdhosts'
roles = [
{
:hosts => 'proxy[001,002].example.jp',
:roles => %w( base proxy ),
},
{
:hosts => 'app[001-010].example.jp',
:roles => %w( base app ),
}
{
:hosts => 'db[001,002].example.jp',
:roles => %w( base db ),
}
]
hosts = roles.map do |role|
pdhosts(role[:hosts]).map do |host|
{
:name => host,
:short_name => host.split('.')[0],
:roles => role[:roles],
}
end
end.flatten
### ここまで ###
desc "Run serverspec to all hosts"
task :spec => 'serverspec:all'
class ServerspecTask < RSpec::Core::RakeTask
attr_accessor :target
def spec_command
cmd = super
"env TARGET_HOST=#{target} #{cmd}"
end
end
namespace :serverspec do
task :all => hosts.map {|h| 'serverspec:' + h[:short_name] }
hosts.each do |host|
desc "Run serverspec to #{host[:name]}"
ServerspecTask.new(host[:short_name].to_sym) do |t|
t.target = host[:name]
t.pattern = 'spec/{' + host[:roles].join(',') + '}/*_spec.rb'
end
end
end
task :default => :spec
flatten しまくってる自分が嫌になりますが。まずは動いてるからいいかなーと。
残念なことに net-ssh を使ってるのでシーケンシャルなんですね。net-ssh-multi を使うように改変したい。。。
訂正:コメントでご指摘いただきました。rake -j もしくは rake -m で同時に複数実行できるんですね。素晴らしい。(rake --help を読み直し中・・・)