Hi all, Gosuke Miyashita here at ATL.
In a previous post (“libspecinfra Project Overview and Future Plans”), I gave an abbreviated introduction to the libspecinfra project. In this post, I’ll provide a more hands-on explanation to illustrate the project’s actual capabilities.
This tutorial will touch on code written in Rust and mruby. In that sense, it’s also a quick primer on both (well, mruby-libspecinfra involves more C than mruby).
Additionally, as not to affect the extant environment, this guide assumes use of a Docker container. Even when not in a container environment, everything should work on macOS and Ubuntu with little deviation from the tutorial. Please make tweaks to suit your environment when necessary. (Personally, I’m developing directly in macOS.)
If you only want to run the project, it’s as simple as readying a complete Docker image. But that’s no fun. Instead, the goal is to create an environment where you can fiddle with the source code to run and develop the project if you so please.
What can libspecinfra at present?
Before beginning the tutorial, allow me to explain the status of the libspecinfra project.
The Ruby version of specinfra is compatible with many resources and platforms, so there’s still a long way to go. However, an updated status report is as follows:
- Now compatible with direct backends and SSH backends.
- Direct backends are exec backends in the Ruby version of specinfra, for direct target processing on the host.
- SSH backends are a cut-corner implementation, so are only compatible with port number fixing and SSH agent authentication (pull requests are welcome).
- Only compatible with macOS and Ubuntu at the present stage.
- When working in other OS, gets tripped up during OS identification processing.
- In terms of resources, only file has been implemented.
- pocket7878 kindly implemented and sent a pull request. (Thanks.)
- While full functionality can be used for direct backends, only limited functionality can be used with SSH.
For those interested, a more detailed compatibility report can be found in the README matrix on GitHub.
The status of other language bindings and peripheral tools is as follows:
- The mruby binding, mruby-libspecinfra, offers nearly the same functionality as the libspecinfra core.
- The Ruby binding, libspecinfra gem, only offers a portion of functions from the libspecinfra core (could just use the Ruby version of specinfra, so this is a low development priority).
- mruby-serverspec-libspecinfra is being developed as a linbspcinfra-compatible version of Serverspec, a sample of tools that work in libspecinfra. It is only compatible with direct backends, and local file tests are nearly equivalent to the Ruby version of Serverspec.
This tutorial ultimately explains the process to run mruby-serverspec-libspecinfra and conduct a local file test.
Starting the Docker Container and Installing Required Packages
First, start the container that will be used in this tutorial (I won’t cover how to install/use the docker here).
1 2 3 |
docker pull ubuntu:xenial docker run -ti ubuntu:xenial /bin/bash |
The rest is a matter of operating within the container. Install the necessary packages.
1 2 3 |
apt-get update apt-get install -y gcc libssl-dev pkg-config cmake rake bison curl git |
Setting up the Rust Environment
Following the directions provided on the Rust site, execute the curl command and set-up the Rust environment.
1 2 |
curl https://sh.rustup.rs -sSf | sh -s -- -y |
Once installation is complete, execute the below command to set a path to $HOME/.cargo/bin:
1 2 |
source $HOME/.cargo/env |
Preparing the libspecinfra Core
Checkout the Rust version of specinfra (the libspecinfra core) from GitHub.
1 2 3 4 |
cd git clone https://github.com/libspecinfra/specinfra.git cd specinfra |
Once you’ve checkout is complete, it’s time to build. If you just want to build, cargo build is fine. But we might as well go ahead and execute a test as well.
1 2 |
cargo test |
The first time will take a while as the build will run along with download of the dependent crate (“gem” in Ruby).
The result of building, libspecifnfra.so (libspecinfra.dylib on macOS) will require mapping to where it can be referenced by mruby-libspecinfra, so create a symbolic link from /usr/local/lib.
1 2 3 |
ln -s ~/specinfra/target/debug/libspecinfra.so /usr/local/lib ldconfig |
You could also add the build file’s target/debug directory to the environment variable LD_LIBRARY_PATH.
Working in the libspecinfra Core’s Source Code
If you’re interested in the source code for the libspecinfra core, I recommend beginning by peeking at the test code. At the current juncture, only test/file.rs
contains test code, but it still gives you a clear picture of how libspecinfra runs.
It might be worthwhile to play around with the code, execute cargo test, and see what effect it has on the test results.
When implementing cargo test, even if you execute println!() in the test code, standard output won’t be displayed. (You’ll have to add a ––nocapture option.)
1 2 |
cargo test -- --nocapture |
The current test code only uses direct backends. For anyone interested, an example of code using SSH backends is available on GitHub in the libspecinfra/examples repository.
Try Running mruby-libspecinfra
Next, we’ll run mruby-libspecinfra which calls libspecinfra’s core functions from mruby.
First, checkout the source code from GitHub.
1 2 3 4 |
cd git clone https://github.com/libspecinfra/mruby-libspecinfra.git cd mruby-libspecinfra |
Once checkout’s complete, execute the test.
1 2 |
rake test |
Rakefile explains what’s actually going on here, but we’re checking out the mruby source code, cd’ing the directory and executing the below code:
1 2 |
MRUBY_CONFIG=/root/mruby-libspecinfra/build_config.rb rake all test |
This command is building mruby based on build_config.rb bundled in the mruby-libspecinfra repository to execute the mruby test and mruby-libspecinfra test.
mruby facilitates extensions with mrbgems (RubyGems in Ruby), but as mruby isn’t conducive to dynamic loading, we must specify embedded gems with build_config.rb and compile mruby.
build_config.rb, bundled with mruby-libspecinfra, looks like the below. The conf.gem.File.expand_path(File.dirname(__FILE__)) section specifies the checked-out mruby-libspecinfra directory, and specifies embedding as a gem. Furthermore, the linker.libraries = %w(specinfra m) section instructs linkage to 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 |
Play with the mruby-libspecinfra Source Code
As was true with the libspecinfra core, it’s quickest to peek under the hood at the source code. You’ll find concrete code showing how libspecinfra is called from mruby.
By revising the code (it’s all written in C), executing rake test, and seeing how the test results change, you’ll gain a deeper understanding of mruby-libspecinfra.
This test also only uses direct backends, but mruby sample code using SSH backends is available in the libspecinfra/examples repository.
Try Running mruby-serverspec-libspecinfra
Next, we’ll try running mruby-serverspec-libspecinfra, which was developed as a sample of tools using libspecinfra.
First, checkout the source code from GitHub.
1 2 3 4 |
cd git clone https://github.com/libspecinfra/mruby-serverspec-libspecinfra.git cd mruby-serverspec-libspecinfra |
Once checkout is complete, execute the test.
1 2 |
rake test |
As was true for mruby-libspecinfra, you’ll gain a better understanding of the details if you peruse Rakefile. Essentially, as we already saw with mruby-libspecinfra, it’s checking out mruby source code, using build_config.rb to embed mruby-serverspec-libspecinfra into mruby for compilation, then executing tests of mruby and mruby-serverspec-libspecinfra. The test code is being placed within the spec directory, as shown below. This code should be familiar to those of you who use 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 |
Instead of using rake test, it’s possible to execute only a test of mruby-serverspec-libspecinfra:
1 2 3 |
rake compile ./mruby/bin/mruby spec/file_spec.rb |
Play with the mruby-serverspec-libspecinfra
I think you’ll get a general idea of how mruby-serverspec-libspecinfra calls mruby-libspecinfra is you look at mrblib/serverspec/config.rb and mrblib/serverspec/type/file.rb.
Currently, there’s only compatibility with direct backends, so it’s not possible to execute SSH tests, as with Serverspec. (That said, the libspecinfra core and mruby-libspecinfra are compatible with SSH backends, so it’s not too great of an obstacle.) Additionally, only file resources have been implemented for the libspecinfra core, so it’s not possible to run file tests.
If you want to edit your checked-out copy of mruby-libspecinfra and call it from mruby-serverspec-libspecinfra to confirm operation, you’ll need to modify build_config.rb as follows:
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 describes mrbgems dependencies. Left as is, mruby-libspecinfra on BitHub will be used. If you specify mruby-libspecinfra in build_config.rb as shown above, then it will be given precendence.
That concludes today’s tutorial.
Other
As the syucream/mruby-serverspec implementation already exists for the mruby version of Serverspec, mruby-serverspec-libspecinfra is nothing more than a sample of tools using libspecinfra.
Below, I’ll provide links to primary materials consulted in the development of libspecinfra.
Rust provides extensive official documentation which is plenty for learners.
For Japanese speakers, I found this resource on the Engineer Hub to be informative and easy to understand: “Speed Learning! Basic Functions and Memory Management in the Rust Programming Language [Rust as a Second Language].”
For mruby, I consulted the following e-books during development:
- mruby as Told by Developer Yukihiro Matsumoto: Omnibus Edition
- Introduction to mruby: Computing made Fun
- Introduction to mruby 2: mrb_value Explained
When developing bindings for mruby and Ruby, “Objects – The Rust FFI Omnibus” came in handy. It also includes examples of code for Python、Haskell、Node.js、C#, and more.
If you have any questions regarding libspecinfra, please feel free to contact me @gosukenator or elsewhere.