github:eで管理しているChefのクックブックを、Berkshelf APIサーバーを立てていい感じに依存解決する方法

ちょっと前にリリースされた Berkshelf 3から、Berkshelf APIというインデックスサーバーからクックブックの情報を得るようになりました。

Berkshelf APIを使うと、外部に公開していないクックブックをBerksfileで指定する際の記述が簡潔になります。

まだ日が浅いせいか、Berkshelf APIgithub:e の情報が少ないので、備忘録も兼ねて残しておきます。

環境は以下のとおりです。

  • berkshelf (3.1.3)
  • berkshelf-api (2.0.0)

Berkshelf APIが必要な理由

Berksfile で cookbook キーワードに git: を添えることで、github:e からクックブックを持ってくることができます。

source 'https://api.berkshelf.com'

cookbook 'oreno-apache', git: 'git@github.oreno:cookbooks/oreno-apache.git'

さて、クックブック oreno-apache が oreno-iptables に依存している(oreno-apache/metadata.rbで depends 'oreno-iptables' している)とします。

この状態で berks を実行すると、oreno-iptables が見つけられなくてエラーになります。

$ berks vendor ./cookbooks
Resolving cookbook dependencies...
Fetching 'oreno-apache' from git@github.oreno:cookbooks/oreno-apache.git (at master)
Fetching cookbook index from https://api.berkshelf.com...
Unable to satisfy constraints on package oreno-iptables, which does not exist, due to solution constraint (oreno-apache = 0.1.0). Solution constraints that may result in a constraint on oreno-iptables: [(oreno-apache = 0.1.0) -> (oreno-iptables >= 0.0.0)]
Missing artifacts: oreno-iptables
Demand that cannot be met: (oreno-apache = 0.1.0)
Unable to find a solution for demands: oreno-apache (0.1.0)

原因は、内部の github:e にしかない oreno-iptables の情報をBerkshelf API (https://api.berkshelf.com) から得たインデックス情報から探そうとして、当然そこには情報がないためです。

なので、以下のようにBerksfileに oreno-iptables の情報を書けば、うまくいきます。

source 'https://api.berkshelf.com'

cookbook 'oreno-apache', git: 'git@github.oreno:cookbooks/oreno-apache.git'
cookbook 'oreno-iptables, git: 'git@github.oreno:cookbooks/oreno-iptables.git'

しかしこれでは使いたいクックブックが依存しているクックブックまでいちいち意識してトップレベルのBerksfileに書かないといけません。ひとつやふたつならまだいいですが、増えてくると面倒このうえないでしょう。

そこで内部のクックブックの情報を返すBerkshelf APIを立てることで、このようなBerksfileを書けばよいようになります。

source 'http://berks-api.oreno:26200'
source 'https://api.berkshelf.com'

cookbook 'oreno-apache'

簡潔ですよね?

最初のsourceを参照してクックブックが見つからなかった場合は、次のsourceを参照するので、これで内部クックブックとコミュニティクックブックの両方の情報を取得できるようになるわけです。

github:e の構成

Berkshelf APIサーバーから参照する場合、github:e 上のクックブックの管理は以下のような構成になっている必要があります。

  • organizationの下にクックブックのレポジトリを置く
    • ユーザーの下ではダメです (少なくとも berkshelf-api 2.0の実装では)
    • クックブック以外のレポジトリがあっても無視されますが、クックブック専用のorganizationを用意した方がいいでしょう
  • metadata.rbのversionをgit tagする
    • 「version '1.2.3'」なら「v1.2.3」というタグをつける必要があります
    • タグがついていないとクックブックと認識されなくてハマります

metadata.rb の書き方

Berkshelf APIサーバーは、github:e から当該クックブックの metadata.rb の「内容」を得て、それをもって instance_eval で評価します。

したがって、metadata.rb に外部ファイルを参照するようなコードを書いていると、例外が発生してスキップされクックブックとみなされなくなってしまいます。

例えば knife cookbook create で作ったクックブックの metadata.rb には、

long_description IO.read(File.join(File.dirname(__FILE__), 'README.md'))

と書かれているので修正する必要がります。(berks cookbookで作ったものは大丈夫です)

自分は ../recipes/*.rb を開いて「include_recipe」しているクックブックを全部 depends するRubyコードを書いていたのでハマりました。

Berkshelf APIサーバーを上げる

berkshelf-api のインストール方法は https://github.com/berkshelf/berkshelf-api を参照してください。

github:e にあるクックブックの情報を返すために、次のような設定ファイルを作って、

{
  "endpoints": [
    {
      "type": "github",
      "options": {
        "organization": "cookbooks",
        "access_token": "YOUR_ACCESS_TOKEN",
        "api_endpoint": "https://github.oreno/api/v3",
        "web_endpoint": "https://github.oreno",
        "ssl_verify": true
      }
    }
  ]
}

起動します。

berks-api -c /path/to/config.json

うまくいけば、26200番ポートの /universe にアクセスすればクックブックの情報が返ってきます。

$ curl http://127.0.0.1:26200/universe | jq .
{
  "oreno-apache": {
    "0.1.2": {
      "location_path": "https://github.oreno/cookbooks/oreno-apache",
      "location_type": "github",
      "dependencies": {
        "oreno-iptables": ">= 0.0.0"
      },
...

berks-api はかなり短い周期で github:e にアクセスしているようで、push された情報はほぼすぐに反映されるようです。

berks の設定をする

これで適切な URL からクックブックをダウンロードできる情報は揃ったのですが、github:e から実際にダウンロードするには berks の設定が必要です。

~/.berkshelf/config.json に次のように書きます。

{
  "github": [
    {
      "access_token": "YOUR_ACCESS_TOKEN",
      "api_endpoint": "https://github.oreno/api/v3",
      "web_endpoint": "https://github.oreno",
      "ssl_verify": true
    }
  ]
}

これで berks vendor で自家製とコミュニティの両方のクックブックが取得できるようになったはずです!