That's one Snappy MOOS

So you're a roboticist, looking at Ubuntu Core and Snappy, trying to decide if they're a good fit for your project. You come across some ROS documentation and realize that the ROS support is first-class, but you're not using ROS. No, you're using the Mission Oriented Operating Suite (MOOS). Why is there no documentation on using MOOS? Because it's too easy to need a document, that's why.

Note that there are two commonly-used projects here, MOOS, and MOOS IvP. The latter uses the former, and adds some more modules on top. It also has a more complicated build system when it comes to packaging, so we're only going to talk about MOOS here (I'll make another post regarding MOOS IvP).

Typical MOOS build process

MOOS is just good-old CMake, so the typical build process looks pretty, well, typical. First we grab and extract the latest release:

$ wget https://github.com/themoos/core-moos/archive/10.0.2.a-release.tar.gz
$ tar xzf 10.0.2.a-release.tar.gz

Now we configure and build it:

$ mkdir build
$ cd build/
$ cmake ../core-moos-10.0.2.a-release/
$ make

There are even sensible install rules in there if you want to run make install. Creating a new module to make use of it is also easy-- just refer to this example.

Packaging MOOS as a .snap

.snaps are built using a tool called Snapcraft. For this example we're going to target Ubuntu Core 15.04, which requires Snapcraft v1 (the latest release in that line as of this writing is v1.0.2). For more information on installing/getting started with Snapcraft, head this direction.

Let's get started. First of all, we're going to borrow this example from MOOS, with one modification to the CMakeLists.txt: an install rule. Just put it at the bottom, like this:

# ...
INSTALL(TARGETS ${EXECNAME}
RUNTIME DESTINATION bin
)

If this isn't clear, the finished code for this post has been pushed here. We need the install rule, or Snapcraft won't know which files to place where when creating the final .snap.

Now we're going to create the following directory structure:

moos-example/
snapcraft.yaml
icon.svg
src/
CMakeLists.txt
ex1010.cpp

The snapcraft.yaml file is the recipe used by Snapcraft to "craft" the .snap. Open it up in your favorite editor.

Let's begin by providing some general metadata about the .snap, so add something like the following:

name: moos-example
version: 1
vendor: me <me@me.com>
summary: MOOS Example
description: Simple ping/pong example for MOOS
icon: icon.svg

In Snapcraft v1 that icon is required, so if you don't have one run this command to make one:

$ echo "<svg />" > icon.svg

Back in the snapcraft.yaml, we need to tell Snapcraft about the parts making up our .snap. In our case, there are two parts: the MOOS core, and our ping/pong example:

parts:
core-moos:
plugin: cmake
source: https://github.com/themoos/core-moos/archive/10.0.2.a-release.tar.gz
configflags:
- -DCMAKE_INSTALL_PREFIX=/

moos-example:
plugin: cmake
source: src/
after: [core-moos] # Build this part after MOOS

The snapcraft.yaml we have so far will result in a valid .snap, but it doesn't export any behavior. Let's make MOOSDB run as a service, at boot:

services:
MOOSDB:
start: MOOSDB
description: MOOS router

Let's also add a few binaries to use our ping/pong example (binaries don't run at boot, they're run by the logged-in user):

binaries:
ping:
exec: ex1010 --ping
pong:
exec: ex1010

So that adds two binaries: ping and pong. Their functionalities should be obvious.

Believe it or not, we're done here. Let's save and close snapcraft.yaml, and create the .snap:

$ cd moos-example/
$ snapcraft

Wait for a minute while MOOS is fetched and built, then the local code is built, and finally while the .snap is being created. Once it's finished, take it for a spin!

Testing the resulting .snap

Note that you need an Ubuntu Core device with the same architecture as the .snap you just created (it can be a real device, or virtualized). Pick your poison. Once you have it up and running, you can scp your .snap to it and install it by running this command on the device:

$ sudo snappy install /path/to/my.snap --allow-unauthenticated

Now, on the device, you can see MOOSDB running:

$ sudo snappy service status moos-example
Snap Service State
moos-example MOOSDB enabled; loaded; active (running)

So now we can run our ping/pong example. In one terminal (logged into the device), run:

$ moos-example.pong

In another terminal (also logged into the device), run:

$ moos-example.ping

And you'll see them communicating, just as you'd expect.

Conclusion

"So what's the big deal? What did I just accomplish?" I hear you asking. You actually accomplished a number of things:

  1. You now have an entire MOOS system prepared for deployment/sharing. All dependencies are included in the .snap-- it will run on any Ubuntu Core 15.04 installation.
  2. You've configured exactly what should be run on boot for the robotic system, no need to fiddle with config files.
  3. By using Ubuntu Core, you now have a transactional deployment/update strategy for free. You can even get automatic updates on your devices if they connect to the internet and you upload your .snap to the store.
  4. Perhaps most importantly, your MOOS system is running completely confined. Even if it has an exploitable vulnerability, the confinement model of Ubuntu Core means an attacker's potential damage is very limited.

If these things are important to you, I definitely suggest you spend some time experimenting with Ubuntu Core, Snappy, and Snapcraft. For your reference, the code used in this post is in GitHub.

Comments