Serverspec では様々な種類のテストを記述することが出来るのですが、内部では Specinfra というクラスでそのテストの定義と実装をしています。
実際にテストする際に使用するツールは OS によって変わってきてしまうので、その差異を吸収する部分も Specinfra に書かれています。
Vagrant が様々なホストとゲスト OS でプロビジョン出来る仕掛けと同じです。
さて、その Specinfra ですが、boot2docker や拙作の DockerRoot のように Busybox がベースの OS 用の設定がなく、テストがうまくいきません。
そこで、簡易な(実際に使ってみてうまく動かなかった部分のみ) DockerRoot/Busybox 用拡張を書いてみました。
ここでは、単純に spec/spec_help.rb
に追加して、拡張することにしました。
(本格的なのは面倒なのでw)
たぶん、他のタイプのテストでも動かないのがあるでしょう。そのうち対応するかも知れませんが、需要が無ければこのままですw
spec/spec_help.rb
class Specinfra::Command::Busybox
class Base < Specinfra::Command::Linux::Base
class Group < Specinfra::Command::Base::Group
class << self
def check_exists(group)
"grep #{escape(group)} /etc/group"
end
def check_has_gid(group, gid)
"grep #{escape(group)} /etc/group | cut -f 3 -d ':' | grep -w -- #{escape(gid)}"
end
end
end
class RoutingTable < Specinfra::Command::Base::RoutingTable
class << self
def check_has_entry(destination)
%Q{ip route show #{destination} | awk '{print $1, "via", $5, "dev", $3, " "}'}
end
alias :get_entry :check_has_entry
end
end
class User < Specinfra::Command::Base::User
class << self
def check_has_home_directory(user, path_to_home)
"grep #{escape(user)} /etc/passwd | cut -f 6 -d ':' | grep -w -- #{escape(path_to_home)}"
end
def check_has_login_shell(user, path_to_shell)
"grep #{escape(user)} /etc/passwd | cut -f 7 -d ':' | grep -w -- #{escape(path_to_shell)}"
end
end
end
end
end
class Specinfra::Helper::DetectOs::DockerRoot < Specinfra::Helper::DetectOs
def detect
if ( uname = run_command('uname -r').stdout ) && uname =~ /docker-root/i
family = nil
release = nil
os_release = run_command("cat /etc/os-release")
if os_release.success?
os_release.stdout.each_line do |line|
family = line.split('=').last.strip if line =~ /^ID_LIKE=/
release = line.split('=').last.strip if line =~ /^VERSION=/
end
end
family ||= 'linux'
{ :family => family, :release => release }
end
end
end
以下はこれを使ったサンプルです。
spec/docker-root/docker_spec.rb
require 'spec_helper'
describe user('docker') do
it { should exist }
it { should belong_to_group 'docker' }
it { should belong_to_primary_group 'docker' }
it { should have_uid 1000 }
it { should have_home_directory '/home/docker' }
it { should have_login_shell '/bin/bash' }
end
describe group('docker') do
it { should exist }
it { should have_gid 1000 }
end
describe 'Docker Daemon' do
context command('/etc/init.d/docker status') do
its(:stdout) { should match /^Docker .* is running.$/ }
end
context file('/var/run/docker.sock') do
it { should be_socket }
end
context interface('docker0') do
it { should exist }
it { should be_up }
end
context routing_table do
it do
should have_entry(
:destination => '172.17.0.0/16',
:interface => 'docker0',
:gateway => '172.17.0.1',
)
end
end
end
describe 'Linux kernel parameters' do
context linux_kernel_parameter('net.ipv4.ip_forward') do
its(:value) { should eq 1 }
end
context linux_kernel_parameter('net.ipv6.conf.all.forwarding') do
its(:value) { should eq 1 }
end
end
Vagrantfile
# A dummy plugin for DockerRoot to set hostname and network correctly at the very first `vagrant up`
module VagrantPlugins
module GuestLinux
class Plugin < Vagrant.plugin("2")
guest_capability("linux", "change_host_name") { Cap::ChangeHostName }
guest_capability("linux", "configure_networks") { Cap::ConfigureNetworks }
end
end
end
Vagrant.configure(2) do |config|
config.vm.define "docker-root"
config.vm.box = "ailispaw/docker-root"
if Vagrant.has_plugin?("vagrant-triggers") then
config.trigger.after [:up, :resume] do
info "Adjusting datetime after suspend and resume."
run_remote "sudo sntp -4sSc pool.ntp.org; date"
end
end
# Adjusting datetime before provisioning.
config.vm.provision :shell, run: "always" do |sh|
sh.inline = "sntp -4sSc pool.ntp.org; date"
end
config.vm.provision :docker do |docker|
docker.pull_images "busybox"
docker.run "simple-echo",
image: "busybox",
args: "-p 8080:8080 -v /usr/bin/dumb-init:/dumb-init:ro --entrypoint=/dumb-init",
cmd: "nc -p 8080 -l -l -e echo hello world!"
end
config.vm.network :forwarded_port, guest: 8080, host: 8080
if Vagrant.has_plugin?("vagrant-serverspec") then
config.vm.provision :serverspec do |spec|
spec.pattern = "spec/docker-root/*_spec.rb"
end
end
end
他にもサンプルとしてこちらで実際に利用しています。
https://github.com/ailispaw/docker-root-packer/tree/serverspec/spec