2015-09-01

Bash の意外な落とし穴

Bash で if 文の代わりに &&|| を使うと1行になって見た目がすっきりする。

# (A)
if [ -n "$MSG" ]; then
    echo "$MSG"
fi
# (B)

[ -n "$MSG" ] && echo "$MSG"
# (C)

ところがこの2つは完全に等価ではない。前者では (B) の地点で return code ($?) が (A) の地点と変わらないのに対して、後者での (C) 地点での return code は以下のようになる:

  • [ ] 内の条件が成立したとき: && の後の文の return code が return code となる。
  • [ ] 内の条件が成立しないとき: 1 が return code となる。

スクリプトの最後に上記のような条件文を書く場合に && の形で書くと、想定外にスクリプトが 1 を返す(つまり失敗とみなされる)ので注意が必要だ。

2015-01-09

Ubuntu で Vagrant 最新版を使って KVM を起動する

Ubuntu Desktop 14.04 Trusty 64bit で Docker を試していて、Docker を Vagrant 経由で使ったらどうなるかなとふと思ったのだが、Vagrant の Docker プロバイダーは Ubuntu 14.04 で提供されているバージョンの Vagrant では提供されない。そこで最新版の Vagrant をインストールして(Docker を試す前に)KVM 仮想マシンを起動してみようと思ったらハマりどころがたくさんあった、という話。

Vagrant 最新版のインストール

公式サイトからダウンロードしてきてインストールする。依存する Ruby などは自分で抱え込んでいるようなので、事前に準備することは何もない。

$ cd /tmp
$ curl -LO https://dl.bintray.com/mitchellh/vagrant/vagrant_1.7.2_x86_64.deb
$ sudo dpkg -i vagrant_1.7.2_x86_64.deb
$ echo ‘PATH=/opt/vagrant/bin:$PATH' >> .bashrc

KVM のインストール

以前の記事(Septième Sens: UbuntuでKVM仮想マシンを作成する)を参考にして

$ sudo aptitude install kvm virtinst

のようにインストールして進めていると何かがおかしい。KVM がインストールされていないようだ… 上のコマンドを実行したときのメッセージをよくみると、

  "kvm" is a virtual package provided by:
    qemu-kvm:i386 qemu-kvm
  You must choose one to install.

と表示されるだけでインストールされずにスルーされていたことが判明。いろいろ調べた結果、今回の Vagrant 経由で試す場合は以下でよいことがわかった。

$ sudo aptitude install qemu-system-x86 libvirt-bin

ちなみに

$ sudo aptitude install kvm virtinst

での(失敗した)インストールの後で qemu-system-x86 をインストールした場合には libvirt-bin を再起動する必要がある点に注意。これをしないと、libvirtd が KVM を見つけられなくてよくわからないエラーが出る羽目になる。

なお、aptitude でなく apt-get install kvm でインストールした場合には自動で適切なものが選択されるようなので問題は発生しないものと思われる。

vagrant-kvm のインストール

この時点で vagrant-kvm をインストールすると失敗する。

$ vagrant plugin install vagrant-kvm
  ...
  An error occurred while installing ruby-libvirt (0.5.2), and Bundler cannot continue.
  Make sure that `gem install ruby-libvirt -v '0.5.2'` succeeds before bundling.
  ...

この記事を書いた時点の vagrant-kvm プラグインは ruby-libvirt 0.5.2 を要求している。しかし Ubuntu 14.04 の ruby-libvirt は 0.5.1 である。したがって最新版を gem でインストールする必要がある。

$ sudo apt-get install ruby-dev libvirt-dev
$ gem install --user-install ruby-libvirt
$ vagrant plugin install vagrant-kvm

vagrant-mutate のインストール

vagrant のイメージを変換するプラグインが必要である。

$ vagrant plugin install vagrant-mutate

ところで、vagrant install で以下のような非常に奇妙なエラーが出ることがある。

  Net::HTTPNotFound: No gems found matching "vagrant-vmware-fusion" "2.5.0" nil

これは既知のバグ(例えばココを見よ)であるが修正されていないようだ。対処法はもう一度 install を実行すること。たいていの場合うまくいく。

Vagrant 用イメージのダウンロード

ここで Vagrant の Getting Started を参考にしてイメージをダウンロードするとハマる。

$ vagrant box add hashicorp/precise64
$ vagrant box list
  hashicorp/precise64 (virtualbox, 1.1.0)
$ vagrant mutate hashicorp/precise64 kvm
  Vagrant-mutate can not download from Vagrant Cloud for you.

これは vagrant-mutate のバグで、比較的最近 Vagrant に導入された「/ を含む名前」の扱いがうまくいっていないためである(→ Support boxes with slash in name · Issue #52 · sciurus/vagrant-mutate)。

そこで URL を直接指定してダウンロードする必要があるが、前出の hashicorp/precise64 のイメージの URL を調べて、それをダウンロードしよう。

$ vagrant box add precise64 https://atlas.hashicorp.com/hashicorp/boxes/precise64/versions/1.1.0/providers/virtualbox.box
$ vagrant mutate precise64 kvm

Vagrant から KVM を起動

先程変換したイメージを元に KVM を起動する。

$ mkdir /tmp/vag
$ cd /tmp/vag
$ vagrant init precise64
$ vagrant up --provider=kvm
  ...
  /home/kimura/.vagrant.d/boxes/test/0/kvm/box-disk1.img': Permission denied (Libvirt::Error)
  ...

再び謎のエラーが発生してしまった。これはカーネルの AppArmor 関連のバグだそうだ(→ https://github.com/adrahon/vagrant-kvm#ubuntu)。回避するには以下を実行する。

$ sudo apt-get install apparmor-utils
$ sudo aa-complain /usr/lib/libvirt/virt-aa-helper

これでうまくいくはず。

$ vagrant up --provider=kvm
$ vagrant ssh

vagrant-libvirt との衝突

vagrant-kvm と同様の機能を持つプラグインに vagrant-libvirt がある。この2つのプラグインを同時にインストールすると衝突して非常に謎なエラーが発生してハマることになるので注意。

$ vagrant plugin install vagrant-libvirt
$ vagrant up --provider=kvm
  ...
  /home/kimura/.vagrant.d/gems/gems/vagrant-libvirt-0.0.24/lib/vagrant-libvirt/cap/mount_p9.rb:9:in `mount_p9_shared_folder': wrong number of arguments (3 for 2) (ArgumentError)
  ...

vagrant-kvm を呼び出したはずなのになぜか vagrant-libvirt のコードで例外が発生してしまっている!vagrant-kvm のサイトでは vagrant-libvirt との衝突は修正されたと書いてあったが、私の所では残念ながら両立していない。

まとめ

$ cd /tmp
$ curl -LO https://dl.bintray.com/mitchellh/vagrant/vagrant_1.7.2_x86_64.deb
$ sudo dpkg -i vagrant_1.7.2_x86_64.deb
$ echo ‘PATH=/opt/vagrant/bin:$PATH' >> .bashrc

Bash 終了後、再開。

$ sudo apt-get install qemu-system-x86 libvirt-bin
$ sudo apt-get install ruby-dev libvirt-dev
$ gem install --user-install ruby-libvirt
$ vagrant plugin install vagrant-kvm
$ vagrant plugin install vagrant-mutate
$ sudo apt-get install apparmor-utils
$ sudo aa-complain /usr/lib/libvirt/virt-aa-helper
$ vagrant box add precise64 https://atlas.hashicorp.com/hashicorp/boxes/precise64/versions/1.1.0/providers/virtualbox.box
$ vagrant mutate precise64 kvm
$ mkdir /tmp/vag
$ cd /tmp/vag
$ vagrant init precise64
$ vagrant up --provider=kvm
$ vagrant ssh