[Vagrant]BOXファイルのメタデータを作成したらNAS上のBOXを取り込めるようになった

  • 投稿日:
  • by
  • Category:

Vagrant_PrimaryLogo_FullColor_RGB.png

前回VagrantでNAS上のBOXファイルが取り込めない問題を取り上げた。

Vagrantfile内で box_url="\\192.168.0.1\日本語フォルダ\日本語ファイル.box" を指定したがcurlのバージョン&2バイト文字が原因で取り込めなかったため、一時的にネットワークドライブを割り当てることで解決した、という内容だ。

ややトリッキーではあるものの実用に問題はなかったのでしばらくはこの方法で運用していたが、遂に根本的に解決することができたのでご紹介する。

(古いバージョンでも問題はないと思うが、今回は現時点で最新版のVagrant 2.2.10を使用する)

 

 

BOXメタデータが鍵になる

BOXメタデータはJSON形式で書かれたヘッダファイルのようなもの。詳細は公式ドキュメントを参照してほしい。

というかこれを見ればすぐ理解できると思う。

{
  "name": "hashicorp/bionic64",
  "description": "This box contains Ubuntu 18.04 LTS 64-bit.",
  "versions": [
    {
      "version": "0.1.0",
      "providers": [
        {
          "name": "virtualbox",
          "url": "http://example.com/bionic64_010_virtualbox.box",
          "checksum_type": "sha1",
          "checksum": "foo"
        }
      ]
    }
  ]
}

例えばBOXをプロバイダ(VirualBox、VMware等)毎に作成すると、プロバイダの数だけVagrantfileを作成してその中でそれぞれのbox_urlを指定する......となると管理が面倒になってしまう。

そこでVagrantfileのbox_urlにメタデータを指定し、メタデータの中でプロバイダ毎にBOXファイルのURLを記載しておけば、一つのVagrantfile(というかbox_url)で複数のプロバイダに対応することができる。

その他にもBOXファイルのバージョンを記載することで複数バージョンを管理することができたり、BOXファイルの説明や作成者情報を記載することもできる。

 

メタデータを作成する

ベースは上の公式サンプルを使用する。

"name"はVagrantfileで指定するBOX名。"description""version"は好きな内容を記載する。

ポイントはurlで、下記のように記載する。

 "url": "\\\\192.168.0.1\\日本語フォルダ\\日本語ファイル名.box", 

これはJSON形式に従ったフォーマットで、"\"をエスケープする為に2個重ねることを忘れてはいけない。2バイト文字のエンコードは不要。

今回 "checksum_type""checksum"は使用しないため削除した。

 

Vagrantfileにも手を加える

まずbox_urlの指定をメタデータのパスに変更する。

config.vm.box_url = "メタデータのパス.json"

そして、もう一行追加する。

config.vm.box_download_options = {globoff: ""}

これはbox_urlからファイルをダウンロードする際のオプション

 

box_download_optionsにgloboffを指定する意味

ここに指定された内容は、実際にファイルをダウンロードするcurlにそのままオプション(引数)として渡される。

本来は {key: "<path/to/key>"}のkey-value形式で指定する(とcurl --key <path/to/key>と渡される)が、--globoffオプションは単体で使うためvaue部分は空欄のままにする。

そしてcurlの--globoffオプションとはこういうもの

URLに[]{}&?*などの特殊記号が含まれると本来は不正URLと判断されてエラーになるが、このオプションを付けるとチェックされなくなる。詳細はcurl公式

もしこれを追加しないと、

curl: (3) [globbing] illegal character in range specification at pos 29

このようにエラーが出てしまいダウンロードに失敗してしまうのである。

 

globoff指定してもbox_url直指定はできなかった

globoff指定すればいいなら、Vagrantfileのbox_urlに直接書けるんじゃない?

config.vm.box_url="\\192.168.0.1\日本語フォルダ\日本語ファイル.box"
config.vm.box_download_options = {globoff: ""}

と思ったが、Windows特有の2バイト文字が使えない問題に直面してしまいvagrant内でのエラーが出てしまう。

リンク先には対策方法も書いてあるが、前回の記事にも書いた通りソースを直接いじる方法は避けたい(一連の構築手順に手作業を加えると全マシンでその手間が増えてしまうし、バージョンアップ時にトラブルの元になるので)

Windows以外ではbox_urlでは直接記載できるのだろうか。

 

どうしてメタデータを介するとうまくいくのか

じゃあメタデータに記載した場合はなぜうまくいくのかというと、Vagrantfileのbox_urlとメタデータのurlではパース方法が違うのではないかと推測できる。

box_urlはRuby(というかRFC3986)の仕様でURLとしてバリデーションされるため非ASCII文字が使えなかったが、じゃあエンコードすればどうなるのかというと、curl側がエラーになってしまいダメだった。

一方でメタデータのurlはおそらくJSONの仕様に沿って文字列としてバリデーションされるため、"\"さえきちんとエスケープしていれば2バイト文字が混ざっていても問題なく受け入れられたため、無事にcurlにURLを渡すことができたのだろう。

Ruby JSON curl
2バイト文字を含むURL ×
エンコード済のURL 未検証 ×

やや一貫性の無いつくりであることは否めないが、昨今のソフトウェアは多数のOSSを組み合わせて作られていることが多く、こういう問題が出てしまうのは仕方がないのかもしれない。

今思いついたが、Vagrantfileのbox_download_optionsに「curlがエンコード済みURLを受け付けるような引数」を指定することができれば(表のcurlの×になり)、直接box_urlにエンコードしたURLを指定しても動くんじゃないだろうか。

というかcurlがエンコード済みURLを受け付けてくれないのはなんでだったっけな。普通そのまま受け付けてくれそうなんだが。UNCか?でもUNCがダメだったのは特定のバージョンだけの問題で、今は解決されているはずなんだが...。

コメントする