先日 fluent-plugin-out_chatwork を作ったのですが、fluentdの公式サイト だとtestunit使用を前提にしててrspec厨には厳しい世の中だったので自分用にrspecでfluentdプラグインを作るまでの手順をまとめてます。
コマンドを叩いた後のファイル出力は適宜コミットのURLをつけているので参照してください
0. 前提条件
rbenv や rvm のようなrubyのバージョン管理ツールをインストールしてること。
このエントリではrbenvを前提に書きます。
1. ruby 1.9.3をインストールする
td-agent ver 1.1.20 時点でruby 1.9.3なので合わせておくのが無難。
rbenv install 1.9.3-p547
公式だと 1.9.3-p194 ってあるけどローカルで開発する分にはパッチバージョンまでは気にしなくていいと思う。
とは言えrubyの最新が2.1なので追随したいという欲求はあると思うので、並列ビルドするための手順は後述。
2. bundlerをインストールする
gem install bundler
ruby使っててbundler使ってないことはないはずだけど、新規でruby入れた時にはbundlerは入ってないので忘れがち
3. bundle gemでgemのスケルトンを作る
$ bundle gem fluent-plugin-out_chatwork create fluent-plugin-out_chatwork/Gemfile create fluent-plugin-out_chatwork/Rakefile create fluent-plugin-out_chatwork/LICENSE.txt create fluent-plugin-out_chatwork/README.md create fluent-plugin-out_chatwork/.gitignore create fluent-plugin-out_chatwork/fluent-plugin-out_chatwork.gemspec create fluent-plugin-out_chatwork/lib/fluent/plugin/out_chatwork.rb create fluent-plugin-out_chatwork/lib/fluent/plugin/out_chatwork/version.rb
https://github.com/sue445/fluent-plugin-out_chatwork/commit/f462b5a5d3d912fe860dad2a6f0b1c496d7bf5b4
fluentdのためのプラグインをイチから書く手順(bundler版) - tagomorisのメモ置き場 だと手動でディレクトリ構成変えてますが、今のbundlerだとハイフン区切りをディレクトリ階層の区切りにしてくれてる模様。*1
fluentdプラグインの命名規則
fluent-plugin-
で始めて後ろに好きな名前をつける- プラグインを書く | Fluentd には
<TYPE>_<NAME>
にしろってあったのでchatworkプラグインでも fluent-plugin-out_chatwork にしたのですが、 gemの名前に<TYPE>
*2 をつけてるのはあんまり見ない(後悔)
- プラグインを書く | Fluentd には
bundle gem する時のハイフン区切りとアンスコ区切りの違いはこんな感じ
ハイフン区切り
ハイフンでディレクトリ作られるのでネームスペースの区切りとして使われる
$ bundle gem happiness-charge-precure create happiness-charge-precure/Gemfile create happiness-charge-precure/Rakefile create happiness-charge-precure/LICENSE.txt create happiness-charge-precure/README.md create happiness-charge-precure/.gitignore create happiness-charge-precure/happiness-charge-precure.gemspec create happiness-charge-precure/lib/happiness/charge/precure.rb create happiness-charge-precure/lib/happiness/charge/precure/version.rb
アンスコ区切り(単語区切り)
ディレクトリは作られないので単語区切り
$ bundle gem happiness_charge_precure create happiness_charge_precure/Gemfile create happiness_charge_precure/Rakefile create happiness_charge_precure/LICENSE.txt create happiness_charge_precure/README.md create happiness_charge_precure/.gitignore create happiness_charge_precure/happiness_charge_precure.gemspec create happiness_charge_precure/lib/happiness_charge_precure.rb create happiness_charge_precure/lib/happiness_charge_precure/version.rb
4. version.rbを消してgemspecにバージョンを直接書く
これは理由はよく分かってないのですが、fluentdのプラグインだとgemspecに直接バージョンを書いてることが多いのでfluentdの文化にあわせておくのが無難な気がする。(ファイルをtd-agentのpluginフォルダに置いて検証しやすくするため?)*3
5. 公式のサンプルコードをコピペする
作るプラグインの種類(インプットプラグインとかアウトプットプラグインとか)に応じてサンプルコードをコピペする
https://github.com/sue445/fluent-plugin-out_chatwork/commit/32f20ee6165c58a34ded4d374d1f166fa8a4617f
ファイル名が <TYPE>_<NAME>.rb
になってないならこの時に合わせる
6. rspecのセットアップをする
gemspecに
spec.add_development_dependency "rspec", "~> 3.0.0"
を追加して
$ bundle install $ bundle exec rspec --init create .rspec create spec/spec_helper.rb
spec_helperにオプションがコメントアウトされていろいろ書いてあるので必要に応じて追加する。
config.order = 'random'
があるとテストの実行順を毎回ランダムにしてくれるのでテストの順番に依存するテストがなくなるのでおすすめ
https://github.com/sue445/fluent-plugin-out_chatwork/commit/c711a9b424ed30c067ac9c6fc3a4499cfbb0af84
bundle gem 〜 -t
すればspec_helper.rbと.rspecも作られるんですが、bundlerで作られるやつが書式が古いので後から rspec --init した方がいいと思うw
7. fluentdプラグインのためのrspecの設定を追加する
gemspec
spec.add_dependency "fluentd"
spec_helper.rb
require 'fluent/load' require 'fluent/test' require 'fluent/plugin/out_chatwork' # ←自分のプラグインの名前に適宜変える
https://github.com/sue445/fluent-plugin-out_chatwork/commit/bb64ad36f4b205cbb13a8624ce8f362f96f6e8da
8. プラグインを作り始める
いきなりプラグインの処理を実装するよりも、設定が読めるかどうかのテストを一応書いた方がハードル低いと思う
サンプルspecはこんな感じ
describe Fluent::ChatworkOutput do let(:driver) { Fluent::Test::OutputTestDriver.new(Fluent::ChatworkOutput, 'test.metrics').configure(config) } let(:instance) { driver.instance } describe "config" do let(:config) do %[ api_token xxxxxxxxxxxxxxxxxxxx room_id 1234567890 body some message ] end it "should get api_token" do expect( instance.api_token ).to eq "xxxxxxxxxxxxxxxxxxxx" end it "should get room_id" do expect( instance.room_id ).to eq "1234567890" end it "should get body" do expect( instance.body ).to eq "some message" end end end
let(:config)
の中が
<match **> type chatwork api_token xxxxxxxxxxxxxxxxxxxx room_id 1234567890 message some message </match>
のtype以降の行相当(typeだけはfluentd側で設定される)
https://github.com/sue445/fluent-plugin-out_chatwork/commit/6f8c82bf787c2896d12c93c92f49edceee5cceae
9. 思い思いにプラグインの処理を実装したりリファクタリングしたり
https://github.com/sue445/fluent-plugin-out_chatwork/commit/a109acb7849641942b3a5fdc77d758bd9c97e5c1
10. Travis CIで複数バージョンのrubyのテストを出来るようにする
Travis CIの設定はこちらを参照
GithubにあるリポジトリをTravis CI連携する手順 #junitbook - くりにっき
最小限の .travis.yml
language: ruby rvm: - 1.9.3 - 2.0.0 - 2.1.2 script: bundle exec rspec
rvmの後にrubyのバージョン複数書いているのがポイント(こうするだけでtravisだと同時に複数バージョンテストできる)
https://travis-ci.org/sue445/fluent-plugin-out_chatwork
謝辞
id:bash0c7 さんの fluent-plugin-idobata を参考にさせてもらってます m( )m