Chefのノード情報取得ライブラリOhaiの自作プラグイン作成メモ

## 概要

- Chefでノード情報を取得する機能を提供するOhaiの自作プラグインの作り方について
- 特にOhaiを単独（組み込み）で使う方法についてメモ
- 主に公式ドキュメントに載っていない部分についてメモ
- 公式のマニュアルは[Ohai Custom Plugins](
https://docs.chef.io/ohai_custom.html)

## インストール

```bash
$ gem install ohai
```

## コマンドラインツール

```bash
$ ohai -h
Usage: /Users/masayuki/.gem/ruby/2.2.0/bin/ohai (options)
    -d, --directory DIRECTORY        A directory to add to the Ohai search path
    -l, --log_level LEVEL            Set the log level (debug, info, warn, error, fatal)
    -L, --logfile LOGLOCATION        Set the log file location, defaults to STDOUT - recommended for daemonizing
    -v, --version                    Show Ohai version
    -h, --help                       Show this message
```

## 組み込みOhaiの基本

ディレクトリ構成は何でも良いがとりあえず下記のとおりとする。`sample.rb`から`plugins/sample.rb`を読み込みたい。

```bash
$ tree 
.
├── plugins
│   └── sample.rb
└── sample.rb
```

スクリプトの基本は下記のとおり。

```sample.rb
require 'ohai'
# 自作プラグインの保管場所をプラグインパスに追加する
plugin_path = File.expand_path(File.join(File.dirname(__FILE__), 'plugins'))
if !Ohai::Config[:plugin_path].include?(plugin_path)
  Ohai::Config[:plugin_path] << plugin_path
end
ohai = Ohai::System.new
# プラグインを読み込む。バージョン7系から記法がこっちに変わったらしい。
ohai.all_plugins(%w(sample cpu network))
# データがMashで返る
p ohai.data
```

- プラグインの書き方は[Ohai Custom Plugins](
https://docs.chef.io/ohai_custom.html)が詳しい。

```plugins/sample.rb
Ohai.plugin(:Sample) do

  provides 'sample'

  collect_data do
  	sample = Mash.new
  	sample[:hello] = 'Hello'
  end

end
```

- 公式のプラグイン一覧: https://github.com/chef/ohai/tree/master/lib/ohai/plugins

## はまりポイント

### 自作したプラグインのパスの追加

#### NG: 相対パスで指定

```ruby
Ohai::Config[:plugin_path] << 'plugins'
```

プロジェクトのルートディレクトリで`bundle exec rake spec`している時は気づきにくいですが、他のディレクトリから実行する場合にロードできなくなります。また、gem化して配布した場合でも同じ問題が起こる。

#### OK: 絶対パスに変換して指定

`__FILE__`の場所から相対パスを作成して絶対パスに変換するのが常套手段のようです。

```ruby
plugin_path = File.expand_path(File.join(File.dirname(__FILE__), 'plugins'))
Ohai::Config[:plugin_path] << plugin_path
```

### 同一パスの多重追加

#### NG: 同じパスを2回以上追加

```ruby
plugin_path = File.expand_path(File.join(File.dirname(__FILE__), 'plugins'))
Ohai::Config[:plugin_path] << plugin_path
Ohai::Config[:plugin_path] << plugin_path
```


同じプラグインが2回ロードされ下記のような警告が出力されます。

```
[2015-05-25T12:34:44+09:00] WARN: Plugin Definition Error: </path/to/plugins/sample.rb>: collect_data already defined on platform default
```

#### OK: 重複しないようにする

```ruby
plugin_path = File.expand_path(File.join(File.dirname(__FILE__), 'plugins'))
if !Ohai::Config[:plugin_path].include?(plugin_path)
  Ohai::Config[:plugin_path] << plugin_path
end
```

`uniq!`でもよいかも。

```ruby
plugin_path = File.expand_path(File.join(File.dirname(__FILE__), 'plugins'))
Ohai::Config[:plugin_path] << plugin_path
Ohai::Config[:plugin_path].uniq!
```

### CPUプラグイン

`cpu`プラグインで返ってくるデータ構造がプラットフォームによって異なる。

- `windows`や`linux`など: Mash（Hashのようなもの）にN個の情報がネストされている
- `darwin`: 1個だけ
- つらい
- ソース
    - https://github.com/chef/ohai/blob/master/lib/ohai/plugins/linux/cpu.rb
    - https://github.com/chef/ohai/blob/master/lib/ohai/plugins/windows/cpu.rb
    - https://github.com/chef/ohai/blob/master/lib/ohai/plugins/darwin/cpu.rb

（謎: OS XはデュアルCPU構成でも同じCPUの型番が採用されるのが前提だから？）

### テスト

プラグインで取得するデータはプラットフォームによって取れたり取れなかったりする。（つらい。）

- 対象プラットフォームごとにテストが必要かと。
- CIツールを活用すべし
- 対応プラットフォームを明記するDSLが必要ではないかと思う。