現在、Embulkは次の安定版であるv0.11.0に向けた開発版としてv0.10がリリースされています。
メンテナであるdmikurubeさんのアナウンスに依ると、0.11.0以降はJRubyがデフォルトでembulkに組込まれなくなるため、プラグインは基本的にJavaで作ることが推奨される様になります。 また、JRubyがデフォルトで入らなくなるため、基本となるプラグインの配布プラットフォームはMavenリポジトリになる予定です。
JavaのプラグインのAPIもいくつか変更されており、新しいバージョンに対応するためには多少の修正が必要になります。
基本的な開発ガイドについては、以下の記事を参考にすると良いでしょう。
ある程度embulkのプラグイン開発に慣れていれば、上記の記事で実装とビルドまでは何とかなるんですが、当分の間0.9系が生き続けることは間違いないため、プラグイン開発者としては出来るなら0.9系と0.11系で両対応できる様にしたいと考えるのは自然なことでしょう。
そのため、複数のembulkバージョンを利用してCIを実行できる様にしたいと思い、実際に構築したのですが、その過程で結構ハマる箇所があったので、その辺りについて解説します。
先に、実際に作業したリポジトリを紹介しておきます。
CI環境での実行確認は各バージョンのembulkをインストールした後、シンプルなコンフィグを実際に実行してみてエラーが無ければOKというシンプルなものです。(別途JUnitで書いたテストはある)
また、ここで紹介する方法はバリバリのbeta版という感じの内容なので、今後のembulkの開発次第で大きく変更になる可能性があります。ご注意ください。
Embulk System Propertiesについて
v0.9系であれば普通にビルドした時の生成物のパスをCLIオプションでLOAD PATHに追加すれば、rubygemsの仕組みを経由してembulkに読み込むことができ、それで実行できました。 circleciの設定を抜粋するとこんな感じ。
# 省略 - run: ./gradlew gem - run: name: run-embulk command: ~/repo/embulk run -I ../../../build/gemContents/lib config_acceptance.yml working_directory: src/test/resources
しかし、v0.10以降だとJRubyがデフォルトで存在しないため、jrubyを追加するかmavenプラグインとしてインストールするかのどちらかをやる必要があります。
この時に利用するのがEmbulk System Propertiesです。
詳しくは、以下の記事に書かれているのですが、.embulk/embulk.propertiesというファイルを作っておくと、embulkの基本設定をjavaのpropertyファイル形式で記述できます。
このembulk.propertiesにjrubyのjarファイルの所在を指定することで、embulkでJRubyが利用可能になります。そうすれば、以前と同じ様にrubygems経由でプラグインの読み込みが出来る様になります。
一方で、gemプラグインは今後プラグイン開発の本流では無くなりそうなので、このタイミングでmavenプラグインの活用方法について学んでおきたいと思い、embulk-output-kafkaではmavenプラグインとしてプラグインを導入する方法を調査しました。
で、めちゃくちゃハマったのが、そもそもドキュメントが現時点では全く無いということですw まだ開発版なのでプラグインインストールのための機構そのものが未整備であり、正にこれから作られている最中です。 一般ユーザーならここで諦めても別に良いのですが、プラグインをちょくちょく開発していて、出来れば新しいバージョンにも速やかに対応したいと考えている開発者としては、ここでキャッチアップしておきたいので、頑張って探し当てた結果や質問して得られた回答を共有していきます。
CI環境におけるMavenプラグインの導入方法
1. プラグイン情報の記述
まず、Embulk System Propertiesかyamlのconfigファイルに利用したいプラグインの情報を記述します。
embulk-output-kafkaの例だと以下の様になります。
embulk.propertiesの例
plugins.output.kafka=maven:io.github.joker1007:kafka:0.3.0
重要なのがgroupIdの後のブロックです。ここはembulk-output-kafkaにはなりません。plugins.output
で始まっているキーの場合、kafka
と入れておくと自動でartifactIdがembulk-output-kafka
にマッピングされてMavenリポジトリの探索が行われます。ここでembulk-output-kafka
と書いてしまうとembulk-output-embulk-output-kafka
が探索されることになるので注意しましょう。
config fileの例
out: type: { source: maven, group: io.github.joker1007, type: kafka, version: 0.3.0 } # other config parameters
ちなみにこのフォーマットについてですが、現在ガイドが全然無くて、embulkのソースコードを漁ってたらMavenPluginTypeというクラス内でそれを見つけることができました。
2. プラグインの配置
現在、プラグインを実際にMaven Centralからダウンロードしてくる仕組みがそもそも存在しません。ローカルでビルドしたものも、どうやってそこに読み込ませるかはパッと分かる仕組みやドキュメントはありません。
実際のところ、現時点ではどうやれば良いかというと、以下の様になります。
開発中のプラグインであれば、gradleのmaven-publishプラグインを利用してpublishToMavenLocal
を実行すると、ローカルのMavenリポジトリキャッシュにビルド済みのプラグインがpublishされます。
デフォルトでは~/.m2/repository
になるはずです。
そして、上記のユーザー向けv0.11ガイドに依ると、embulk.propertiesにはm2_repoというものを指定できます。 m2_repoにそのローカルMavenリポジトリキャッシュを指定すれば、読み込みのためのパスが通ります。
しかし、これだけでは十分ではありません。
publishToMavenLocal
だけではプラグインが依存しているライブラリのjarがローカルのMavenリポジトリに書き込まれないため、現時点では自力でそれらをダウンロードしてくる必要があります。
publishToMavenLocal
を実施すると、build/publications/<task name>/pom-default.xml
というファイルが生成されます。そのpomファイルをコピーしてきて、mvnコマンドを使って依存関係を解決します。pom.xmlのあるディレクトリでmvn install
を実行することで、依存ライブラリも全てローカルリポジトリにインストールされます。
その上でembulk run
を実行すると、無事にmavenプラグイン形式でプラグインを読み込み、実行することができます。
開発中のプラグインで無い場合は、ダミーのpom.xmlを用意して、そこに利用したいプラグインのmaven dependencyを記述しmvn install
した上で、embulk.propertiesを設定すれば利用できる様になるはずです。
3. CI環境用のヘルパー
embulk.propertiesにgroupIdやバージョン指定が必要になるため、gradleに現在のプロジェクト定義情報からembulk.propertiesを生成するヘルパータスクを追加しました。
task generateEmbulkProperties { doLast { mkdir ".embulk" def f = file(".embulk/embulk.properties") f.write("m2_repo=${System.properties["user.home"]}/.m2/repository\nplugins.output.kafka=maven:${project.group}:kafka:${project.version}") } }
4. circleciの設定例
kafkaの起動等も含むため、余計なものが入っていますがembulk-output-kafkaでは以下の様な設定でCircleCIを動かすことにしました。
embulk-0.10: docker: - image: circleci/openjdk:8-jdk - image: wurstmeister/zookeeper - image: wurstmeister/kafka:2.13-2.7.0 environment: KAFKA_ADVERTISED_HOST_NAME: localhost KAFKA_ADVERTISED_PORT: 9092 KAFKA_PORT: 9092 KAFKA_ZOOKEEPER_CONNECT: localhost:2181 KAFKA_DELETE_TOPIC_ENABLE: true KAFKA_CREATE_TOPICS: "json-topic:1:1" working_directory: ~/repo environment: # Customize the JVM maximum heap limit JVM_OPTS: -Xmx3200m TERM: dumb SKIP_SIGNING: true steps: - checkout - run: curl -o ./embulk -L https://github.com/embulk/embulk/releases/download/v0.10.31/embulk-0.10.31.jar - run: chmod +x ./embulk # Download and cache dependencies - restore_cache: keys: - v1-dependencies-{{ checksum "build.gradle" }} # fallback to using the latest cache if no exact match is found - v1-dependencies- - run: ./gradlew dependencies - save_cache: paths: - ~/.gradle key: v1-dependencies-{{ checksum "build.gradle" }} - run: ./gradlew publishToMavenLocal - run: cp build/publications/embulkPluginMaven/pom-default.xml pom.xml - restore_cache: keys: - m2-v1-{{ checksum "pom.xml" }} - m2-v1- - run: mvn install - save_cache: paths: - ~/.m2 key: m2-v1-{{ checksum "pom.xml" }} - run: ./gradlew generateEmbulkProperties - run: name: run-embulk command: ~/repo/embulk run config_acceptance.yml working_directory: src/test/resources
5. その他の注意点
CI環境でpublishToMavenLocalを行う時に、生成されるpomファイルの定義にちゃんとgroupIdやartifactIdが設定される様に、以下の公式ドキュメントを参照して必要な情報を定義しておきましょう。
必要な情報が足りないと、配置されるファイル名やリポジトリ内のディレクトリ名が環境依存で変化する場合があるので、embulkのプラグインローダーが正しくプラグインを見つけられなくなります。自分は、そもそもプラグインの読み込みの挙動が良く分かっていなかったこともあって、しょうもないことで1時間ぐらい無駄にしました。(artifactIdが抜けていて、jarが見つからなくなっていた)
CI環境で引っかかるもう一つのポイントが署名でした。
実際にMaven Centralにpublishするためにはgpg鍵による署名が必要になるんですが、それをgradleで実施するsigningというプラグインを使うと、publishToMavenLocal実行時にも自動的に署名処理が走る様になります。 しかし、別にCI環境で鍵署名とか必要無いし、そのためにgpgの秘密鍵をどうこうするのは危ないので、CI環境ではスキップさせる様にしました。
tasks.withType(Sign) { onlyIf { System.getenv().get("SKIP_SIGNING") == null } }
CI環境ではSKIP_SIGNING環境変数を設定してスキップさせます。
Gradleに慣れてればサッと書けると思うんですが、Gradleにあんまり詳しくないので自分はちょっと悩みました。
最後に
これでv0.9系とv0.11系のMavenプラグインの両方を実際に動かして検証する準備が出来ました。
現時点ではMavenプラグインの導入方法は、ほとんどドキュメント化されておらず、またエコシステムやインストール支援機能の構築もまだまだこれからといった状況なので、ここで紹介した方法はどこかのタイミングで不要になることでしょう。(少なくとも一般ユーザーにとっては)
しかし、早い内から次期バージョンのAPIに対応したembulkプラグインを作っておきたい、利用しているプラグインを新しいembulkに対応させるパッチを書きたいといった開発者にとっては、しばらくは使えるノウハウになると思います。
自分は大体現状の動作については理解したので、自分がメンテしているプラグインは暇を見て対応していこうと思っています。
その他の参考資料: GitHub - embulk/embulk-input-s3: S3 file input plugin for Embulk