libspecinfra 開発者向けチュートリアル
宮下 剛輔
ATL 宮下 です。
先日 libspecinfra プロジェクトの概要と今後について というエントリにて、 libspecinfra プロジェクト の概略について説明しました。今回は libspecinfra プロジェクトが実際にどんなものか、触って試すための手順を解説します。
このチュートリアルでは、Rust と mruby で書かれたコードに触れるため、Rust と mruby の簡易的なチュートリアルにもなっています(mruby-libspecinfra は、mruby といいつつもほとんどがCのコードですが)。
また、既存環境に影響を与えないよう、Docker コンテナの利用を前提とした手順で解説しますが、コンテナ環境ではなくても、macOS や Ubuntu であればほぼ同じ手順で動くはずですので、適宜ご自身の環境に合わせて修正してください(筆者は macOS 上で直接開発を行っています)。
ただ動かすだけであれば、全部入りのDockerイメージを用意するだけで済みますが、それだと面白みがないので、ソースコードを見たり修正したりしながら動かせる、すなわち、開発しようと思えばできる環境をつくる、ということを目標にします。
libspecinfra では現状何ができるのか
チュートリアルに入る前に、現在の libspecinfra プロジェクトの状況について説明します。
Ruby 版 specinfra が 対応しているリソースやプラットフォームがたくさんある ため、完成にはまだほど遠い状態ですが、現在以下のようなステータスになっています。
- Direct バックエンドと SSH バックエンドは対応済み。
- Direct バックエンドは Ruby 版 specinfra の Exec バックエンドにあたるもので、直接対象ホスト上で処理を行うためのバックエンドです。
- SSH バックエンドは手抜き実装なのでポート番号固定だったり、SSHエージェントでの認証にしか対応していません(プルリクエスト大歓迎です)。
- OSは macOS と Ubuntu のみ対応で、他はまだ未対応。
- 他の OS だと、OS を識別する処理でコケます。
- リソースは File のみ実装済み。
- pocket7878 さんが 実装して プルリク を送ってくれました。ありがとうございます。
- ただし、Direct バックエンドはフル機能が使えるが、SSH バックエンドは一部機能しか使えない。
この辺りの対応状況の詳細については、GitHub 上の README に対応マトリクスを載せている ので、ご参照ください。
また、各種言語バインディングや周辺ツールの対応状況は以下の通りです。
- mruby バインディングである mruby-libspecinfra は libspecinfra コアとほぼ同等の機能が使える。
- Ruby バインディングである libspecinfra gem は libspecinfra コアの一部機能しか使えない(Ruby であれば Ruby 版 specinfra を使えばいいので、こちらの開発は優先度が低い)。
- libspecinfra 上で動くツールのサンプルとして開発している、lbispecinfra 対応版 Serverspec である mruby-serverspec-libspecinfra は、Direct バックエンドのみ対応で、ローカルファイルに対するテストは Ruby 版Serverspec とほぼ同等のことができる。
このチュートリアルでは、最終的には mruby-serverspec-libspecinfra を動かしてローカルファイルのテストを行うまでの手順を解説します。
Dockerコンテナの起動と必要なパッケージのインストール
最初に、チュートリアルで使うためのコンテナを起動します(Docker のインストール手順や使い方等は割愛)。
1 2 3 |
docker pull ubuntu:xenial docker run -ti ubuntu:xenial /bin/bash |
以降はコンテナ内での操作です。必要なパッケージをインストールしておきます。
1 2 3 |
apt-get update apt-get install -y gcc libssl-dev pkg-config cmake rake bison curl git |
Rust 環境のセットアップ
Rust オフィシャルサイト にある手順の通り、curl
コマンドを実行して Rust 環境のセットアップを行います。
1 2 |
curl https://sh.rustup.rs -sSf | sh -s -- -y |
インストール完了後、$HOME/.cargo/bin
へパスを通すため以下のコマンドを実行します。
1 2 |
source $HOME/.cargo/env |
libspecinfra コアの準備
libspecinfra のコアである Rust 版 specinfra を GitHub からチェックアウトします。
1 2 3 4 |
cd git clone https://github.com/libspecinfra/specinfra.git cd specinfra |
チェックアウトしたらビルドします。ビルドするだけなら cargo build
でいいのですが、せっかくなのでテストも一緒に実行してみましょう。
1 2 |
cargo test |
初回は依存する crate (Ruby でいう gem)のダウンロードとビルドも一緒に走るので、少し時間がかかります。
ビルドしてできた libspecifnfra.so
(macOS の場合は libspecinfra.dylib)は mruby-libspecinfra から参照できるところに配置する必要があるので、/usr/local/lib
配下からシンボリックリンクを張ります。
1 2 3 |
ln -s ~/specinfra/target/debug/libspecinfra.so /usr/local/lib ldconfig |
ビルドしたファイルができる target/debug
ディレクトリを環境変数 LD_LIBRARY_PATH に追加する、というやり方などでも構いません。
libspecinfra コアのソースコードに触れてみる
libspecinfra コアのソースコードに興味ある人は、とっかかりとしてテストコードから覗いてみることをお勧めします。現在テストコードが書かれているファイルは test/file.rs
だけですが、実際にどういうコードを書いて libspecinfra を動かすのか、端的に知ることができます。
テストコードや本体のコードを適当に書き換え、cargo test
を実行して、テスト結果がどのように変わるか見てみると良いでしょう。
cargo test
実行時は、テストコード中で println!()
を実行しても、標準出力には表示されません。表示したい場合には、--nocapture
オプションをつける必要があります。
1 2 |
cargo test -- --nocapture |
現在のテストコードは Direct バックエンドしか使用していません。SSH バックエンドを利用したコード例は GitHub 上の libspecinfra/examples リポジトリ にあるので、興味ある方はこちらも覗いてみてください。
mruby-libspecinfra を動かしてみる
次に、libspeinfra コアの機能を mruby から呼び出すための、mruby-libspecinfra を動かしてみます。
まずはソースコードを GitHub からチェックアウトします。
1 2 3 4 |
cd git clone https://github.com/libspecinfra/mruby-libspecinfra.git cd mruby-libspecinfra |
チェックアウトしたら、テストを実行してみます。
1 2 |
rake test |
実際に何をやってるかは Rakefile の中身を見てもらうとわかるのですが、mruby 本体のソースコードをチェックアウトし、そのディレクトリに cd
して以下のコマンドを実行しています。
1 2 |
MRUBY_CONFIG=/root/mruby-libspecinfra/build_config.rb rake all test |
このコマンドでは、mruby-libspecinfra リポジトリに同梱してある build_config.rb
を元に mruby をビルドし、mruby 本体のテストと mruby-libspecinfra のテストを実行しています。
mruby には mrbgems という、Ruby の RubyGems にあたる機能拡張の仕組みがありますが、mruby には動的にロードする仕組みがないため、build_config.rb
で組み込む gem を指定して mruby をコンパイルする必要があります。
mruby-libspecinfra に同梱されている build_config.rb
は以下のようになっていて、conf.gem File.expand_path(File.dirname(__FILE__))
の部分で、チェックアウト済みの mruby-libspecinfra ディレクトリを指定し、gem として組み込むよう指定しています。また、linker.libraries = %w(specinfra m)
の部分では libspecinfra.so
をリンクするように指示しています。
1 2 3 4 5 6 7 8 9 10 |
MRuby::Build.new do |conf| toolchain :gcc conf.gembox 'default' conf.gem File.expand_path(File.dirname(__FILE__)) conf.enable_test conf.linker do |linker| linker.libraries = %w(specinfra m) end end |
mruby-libspecinfra のソースコードに触れてみる
こちらも libspecinfra コア同様、テストコード から覗いてみるのがとっかかりとして良いでしょう。どのように mruby から libspecinfra を呼び出すのかが具体的なコードで示されています。
テストコードや 本体のコード (すべてCで書かれています)を修正して rake test
を実行し、テスト結果がどのように変わるか見ることで、mruby-libspecinfra への理解が深まると思います。
こちらのテストコードも Direct バックエンドしか利用していませんが、SSH バックエンドを利用した mruby のサンプルコードも libspecinfra/examples リポジトリ にあります。
mruby-serverspec-libspecinfra を動かしてみる
次に、libspecinfra を利用したツールのサンプルとして開発している、mruby-serverspec-libspecinfra を動かしてみます。
まずはソースコードを GitHub からチェックアウトします。
1 2 3 4 |
cd git clone https://github.com/libspecinfra/mruby-serverspec-libspecinfra.git cd mruby-serverspec-libspecinfra |
チェックアウトしたら、テストを実行してみます。
1 2 |
rake test |
mruby-libspecinfra と同様、何をしているかの詳細は Rakefile を見ればわかりますが、基本的にやってることは mruby-libspecinfra と同様、mruby のソースコードをチェックアウト、build_config.rb
で mruby に mruby-serverspec-libspecinfra を組み込んでコンパイルし、mruby 本体のテストと mruby-serverspec-libspecinfra のテストを実行しています。テストコードは spec ディレクトリ以下に置いてあり、以下のようなコードになっています。Serverspec を使っている方にはお馴染みのコードですね。
1 2 3 4 5 6 7 8 |
describe file('/etc/passwd') do its(:mode) { should eq 0o644 } end describe file('/etc/passwd') do it { should be_file } end |
rake test
の代わりに以下のようすることで、mruby-serverspec-libspecinfra のテストのみを実行することもできます。
1 2 3 |
rake compile ./mruby/bin/mruby spec/file_spec.rb |
mruby-serverspec-libspecinfra のソースコードに触れてみる
mruby-serverspec-libspecinfra のソースコードについては、mrblib/serverspec/config.rb と mrblib/serverspec/type/file.rb を見てもらうと、どのように mruby-libspecinfra を呼び出しているのか、雰囲気が掴めるかと思います。
現在は Direct バックエンドのみしか対応していないので、本家 Serverspec のように、 SSH 越しにテストを実行する、といったことはできません(が、libspecinfra コアと mruby-libspecinfra は SSH バックエンドに対応してるので、対応はそれほど難しくありません)。また、libspecinfra コアが File リソースしか実装していないので、ファイルに関するテストしかできません。
手元にチェックアウトした mruby-libspecinfra を 修正し、mruby-serverspec-libspecinfra から呼び出して動作を確認したい場合、以下のように build_config.rb
を修正する必要があります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
git diff diff --git a/build_config.rb b/build_config.rb index f9ebc6d..0e598c1 100644 --- a/build_config.rb +++ b/build_config.rb @@ -2,6 +2,7 @@ MRuby::Build.new do |conf| toolchain :gcc conf.gembox 'default' conf.gem File.expand_path(File.dirname(__FILE__)) + conf.gem '../mruby-libspecinfra' conf.gem :github => 'iij/mruby-regexp-pcre' conf.enable_test conf.linker do |linker| |
mrbgem.rake
には mrbgems の依存関係が記述されているのですが、そのままですと GitHub 上の mruby-libspecinfra を利用します。上記のように build_config.rb
に mruby-libspecinfra のローカルディレクトリを指定すると、こちらの方が優先して利用されるようになります。
チュートリアルは以上です。
その他
mruby 版の Serverspec には既に syucream/mruby-serverspec という実装があるため、mruby-serverspec-libspecinfra はあくまでも libspecinfra を利用したツールのサンプルという位置づけです。
libspecinfra 開発にあたって、私が主に参考にしたリンクをあげておきます。
Rust は公式ドキュメントが非常に充実しているので、 ここ から辿れるリンクだけで十分に学習できます。
日本語の記事ですと、 最速で知る! プログラミング言語Rustの基本機能とメモリ管理【第二言語としてのRust】 – エンジニアHub|若手Webエンジニアのキャリアを考える! がわかりやすかったです。
mruby については私は以下の電子書籍を参考にしながら開発を進めました。
mruby や Ruby バインディングの開発には Objects – The Rust FFI Omnibus が参考になりました。Python、Haskell、Node.js、C# などのコード例も載っています。
libspecinfra についてご質問等ありましたら、@gosukenator 等まで、お気軽にご連絡ください。