vagrant
docker
serverspec
boot2docker
busybox
More than 1 year has passed since last update.

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