Serverspecでrubyのバージョン確認する
出来たサーバに監視周りとかいろいろ周辺のパッケージとか入れて遊んでいこうかなぁと思ったけど、その前にせっかくChefでパッケージのインストールとかも自動化したのだから、流れ的にテストも自動化したいよねぇとか思ってServerspec使い始めた。
試しにまずはServerspecでサーバにインストールされてるrubyのバージョンを確認するというのをやってみた。やりたいことをもう少し具体化するとruby -vコマンド実行した標準出力の文字列をチェックして想定通りのバージョンかをテストすればよい。
Serverspecは現時点の最新版の2.8.2を使いました。
最初に準備として、Gemfileにserverspecを追加する。
source 'https://rubygems.org' gem "chef", '~> 12.0.3' gem "knife-solo", '~> 0.4.2' gem "berkshelf", '~> 3.2.3' gem "serverspec", '~> 2.8.2'
で、bundle installする。
$ bundle install --path vendor/bundle
次にserverspec-initコマンドでひな形ファイルを生成する。途中で何個か質問があるけど、
$ bundle exec serverspec-init Select OS type: 1) UN*X 2) Windows Select number: 1
OSの種類を選ぶ。UN*Xなので1。
Select a backend type: 1) SSH 2) Exec (local) Select number: 1
Vagrant instance y/n: n
Vagrantのインスタンスか。開発環境はVagrantだけど、本番環境はEC2なのでnで素のままで使う。
Input target host name: commitm-dev
テスト対象のホスト名入力する。ホスト名はssh接続先として名前解決できるものと揃えておく。とりあえずvagrant上の開発用の仮想サーバのホスト名を指定した。
+ spec/ + spec/commitm-dev/ + spec/commitm-dev/sample_spec.rb + spec/spec_helper.rb + Rakefile + .rspec
以上でひな形ファイルが生成された。
ちなみに参考までに手元の~/.ssh/configはこんなかんじです。各自環境に合わせてssh鍵認証でパスワードなしでログインできるようにしておけばよい。
Host commitm-dev HostName 127.0.0.1 User vagrant Port 2222 UserKnownHostsFile /dev/null StrictHostKeyChecking no PasswordAuthentication no IdentityFile xxxxx IdentitiesOnly yes LogLevel FATAL
自動生成されたRakefileの中身を眺めてみる。
require 'rake' require 'rspec/core/rake_task' task :spec => 'spec:all' task :default => :spec namespace :spec do targets = [] Dir.glob('./spec/*').each do |dir| next unless File.directory?(dir) targets << File.basename(dir) end task :all => targets task :default => :all targets.each do |target| desc "Run serverspec tests to #{target}" RSpec::Core::RakeTask.new(target.to_sym) do |t| ENV['TARGET_HOST'] = target t.pattern = "spec/#{target}/*_spec.rb" end end end
specディレクトリ配下に#{target}でホスト名ごとのフォルダがあって、その中の*_spec.rbをテストするrakeタスクが定義されているようです。
自動生成されたspec/spec_helper.rbも眺めてみる。
require 'serverspec' require 'net/ssh' 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] ||= Etc.getlogin 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'
sshのconfig読んでホスト名とかオプションとかセットしてるよう。あと、sudoのパスワード設定したい場合は環境変数にセットすればよさそうです。特に今は変更は必要ないのでこのままで。
あと自動生成されたテスト用のspecファイルspec/(ホスト名)/sample_spec.rbを眺める。
require 'spec_helper' describe package('httpd'), :if => os[:family] == 'redhat' do it { should be_installed } end describe package('apache2'), :if => os[:family] == 'ubuntu' do it { should be_installed } end describe service('httpd'), :if => os[:family] == 'redhat' do it { should be_enabled } it { should be_running } end describe service('apache2'), :if => os[:family] == 'ubuntu' do it { should be_enabled } it { should be_running } end describe service('org.apache.httpd'), :if => os[:family] == 'darwin' do it { should be_enabled } it { should be_running } end describe port(80) do it { should be_listening } end
RSpecの書き方でこんなかんじで書いていけば良さそう。これを参考にテストを書いてみる。
とりあえずsample_spec.rbをruby_spec.rbとリネームして、インストールされてるrubyのバージョンをテストするコードを書いてみる。
require 'spec_helper' # ruby versions describe command('ruby -v') do let(:disable_sudo) { true } its(:stdout) { should match /ruby 2\.2\.0p0.+/ } end
予めいろいろなリソースタイプが定義されているけど、単純に任意のコマンド実行するにはcommandというリソースタイプを使えばよい。
標準出力はits(:stdout)で取れるので、should matchで正規表現で期待する文字列を書けばよい。
ちなみに古いブログ記事見るとreturn_stdoutというのを使っているサンプルコードがあるようなのだけど、既に廃止されてるようで、試したらNoMethodErrorになった。
あとハマりどころで、Serverspecのテスト実行はデフォルトでsudoされるのだけど、今回チェックしたいのはログインした一般ユーザのrubyバージョンなので、明示的にdisable_sudoをtrueにする。このようにletで設定すると、このテストケースだけ一時的に設定が変更できるよう。
準備出来たのでrakeコマンドでテスト実行。
$ bundle exec rake (略) Command "ruby -v" stdout should match /ruby 2\.2\.0p0.+/ Finished in 1.33 seconds (files took 0.33412 seconds to load) 1 example, 0 failures
うん。ちゃんと動いたっぽい。
こんな感じでテストコード書いてけばよいのね。