scramble cadenza

技術ネタのガラクタ置き場

Docker と Vagrant で dotfiles をテストする

イントロ

mgi.hatenablog.com

で書いたとおり、最近は自分の dotfilesMItamae で管理している。

運用していく中で、ちょっと複雑な receipe を書いた時、「これ試したいけど、いきなりぶっこむのはちょっとなぁ…」と思うようになった。

そこで MItamae を試すだけ試して、満足したら廃棄できる環境を作ってみた。 ついでに、その sandbox 的環境をテストする serverspec も一緒に書いたので、まとめてみた。

準備

Dockerfile.serverspec

ローカルに serverspec をインストールするのは、ruby に依存してしまうのでナシ。

そこで serverspec を実行する Docker コンテナを用意した。

よく docker-api を使って serverspec でテストするやり方を見かけるけど、Docker コンテナの中で docker-api を使えないので、ssh 接続でテストする。

FROM ruby:2.4.1-alpine

RUN apk update && apk add openssh-client && echo -e 'Host *\nUseRoaming no' >> /etc/ssh/ssh_config

ENV SERVERSPEC_VERSION 2.39.1
RUN gem install serverspec -v ${SERVERSPEC_VERSION}

WORKDIR /serverspec

Vagrant

cookbook を試す環境は Vagrant で用意した。
最初はこれも Docker でコンテナを用意してやっていたけど、serverspec からアクセスするのはコンテナではなくイメージなので、テストが失敗する
(状況を見るとそのような挙動に見える)

# 以下は Docker で環境を作って、serverspec でテストするパターンを試したときのログ

# `ubuntu`(レシピ実行場所) で bash を起動
$ docker-compose run --rm ubuntu bash

# MItamae を install
root@a16bec43ae35:/home/root# bin/setup.sh

# レシピを実行する
root@a16bec43ae35:/home/root# bin/mitamae local cookbooks/git/default.rb
 INFO : Starting MItamae...
 INFO : Recipe: /home/root/cookbooks/git/default.rb
 INFO :   package[git] installed will change from 'false' to 'true'

# git が install された
root@a16bec43ae35:/home/root# which git
/usr/bin/git

# `serverspec` (serverspec 実行用コンテナ)で sh を起動
$ docker-compose run --rm serverspec sh

# spec を実行する。`docker-compose` を使うことで `ubuntu` で名前解決している
/serverspec # TARGET_HOST=ubuntu rspec spec/git_spec.rb

Package "git"
  should be installed (FAILED - 1)

Failures:

  1) Package "git" should be installed
     On host `ubuntu'
     Failure/Error: it { should be_installed }
       expected Package "git" to be installed
       /bin/sh -c dpkg-query\ -f\ \'\$\{Status\}\'\ -W\ git\ \|\ grep\ -E\ \'\^\(install\|hold\)\ ok\ installed\$\'

     # ./spec/git_spec.rb:4:in `block (2 levels) in <top (required)>'

Finished in 0.26349 seconds (files took 0.62787 seconds to load)
1 example, 1 failure

Failed examples:

# テストを実行しても、ssh で接続した世界には git が入っていない
rspec ./spec/git_spec.rb:4 # Package "git" should be installed

レシピを実行した後に docker commit して、もう一度 image を build して serverspec を走らせてもできたが、やってみてあまりに面倒に感じたので、スパッとやめてしまった。

ということで一周戻って Vagrant で環境を作ることになった。 現在のディレクトリを自動的に sync してくれるのも地味に助かった。

Vagrant.configure("2") do |config|
  config.vm.box = "ubuntu/xenial64"
  config.vm.network "private_network", ip: "192.168.33.10"
end

レシピ実行

昔なつかし、vagrant ssh からのレシピ実行

$ vagrant up
$ vagrant ssh
ubuntu@ubuntu-xenial:~$ cd /vagrant
ubuntu@ubuntu-xenial:/vagrant$ bin/setup.sh
+ mitamae_version=1.3.2
+ mitamae_cache=mitamae-1.3.2
+ [ -f bin/mitamae-1.3.2 ]
+ uname
+ mitamae_bin=mitamae-x86_64-linux
+ wget -O bin/mitamae-x86_64-linux.tar.gz --max-redirect 3 -q https://github.com/k0kubun/mitamae/releases/download/v1.3.2/mitamae-x86_64-linux.tar.gz
+ tar xvzf bin/mitamae-x86_64-linux.tar.gz
mitamae-x86_64-linux
+ rm bin/mitamae-x86_64-linux.tar.gz
+ mv mitamae-x86_64-linux bin/mitamae-1.3.2
+ chmod +x bin/mitamae-1.3.2
+ ln -sf mitamae-1.3.2 bin/mitamae

ubuntu@ubuntu-xenial:/vagrant$ bin/mitamae local cookbooks/git/default.rb
 INFO : Starting MItamae...
 INFO : Recipe: /vagrant/cookbooks/git/default.rb

テスト

  • spec_helper.rbvagrantssh 接続する情報を書く
    • host は Vagrantfile に書いた private な ipaddress を指定
    • userkeyvagrant ssh-config の内容をそのまま使う
  • コンテナから serverspec を実行する
    • -v で現在のディレクトリをコンテナ内に mount する
    • --net=host で、ホストのネットワークを使う
      • serverspec 用コンテナの中から、Vagrant で立ち上げた VM を見つけるために、この指定
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

options = Net::SSH::Config.for(host)

options[:user] = 'ubuntu'
options[:keys] = '.vagrant/machines/default/virtualbox/private_key'

set :host,        ENV['TARGET_HOST'] || "192.168.33.10"
set :ssh_options, options

# Disable sudo
set :disable_sudo, true

Dir['./spec/shared/*'].each { |f| require f }
$ docker run --rm -it -v ${PWD}:/serverspec --net=host dotfiles_serverspec rspec spec/ubuntu_spec.rb

ubuntu
  behaves like git
    Package "git"
      should be installed

Finished in 1.15 seconds (files took 0.60525 seconds to load)
1 example, 0 failures

まとめ

ここまですると逆に面倒くさくなってきて、もはや趣味の領域に入りつつあります。

それと Mac 環境でのテストになってなくて、当初目標にしていた事が実現できたとは言い難い。

しかしながら、いつでも OS X を捨てられる準備をしておかないと、もし Apple がとんでもない改悪をしても、
それに耐え続けなければならなくなるし、私はそれを我慢し続けられるほど大人では無いという自覚がある。

ということで目的とはズレてしまったが、 Mac を捨てる準備を整えることができた、という副産物が得られたので良しとしたい。

それと、少しづつテストやレシピを補完していきたい。