ROS production: create an Ubuntu Core image with our snap preinstalled [5/5]

This is the fifth (and final) blog post in this series about ROS production. In the previous post we created a gadget snap to allow confined access to the Turtlebot. In this post, we're going to put all the pieces from this series together and create an Ubuntu Core image with our ROS snap preinstalled, ready to send to the factory for production.

If you download one of the generic Ubuntu Core images for a reference device and run snap list on it immediately, you'll see three snaps installed: a core snap, a kernel snap, and a gadget snap.

Every Ubuntu Core image consists of at least these three snaps. In my case, since I'm using an Intel NUC, I see core, pc-kernel and pc-amd64-gadget. You may recognize that last one from part 3, because it's the gadget we started with when creating our custom one.

We want to create an Ubuntu Core image that uses our custom gadget, and includes our ROS snap so that as soon as the device boots for the first time, the robot starts moving.

Alright, let's get started! Remember that this is also a video series: feel free to watch the video version of this post:

Step 1: Request interface auto-connection

As soon as the device boots for the first time, we want the Turtlebot to begin moving. In order for this to happen, the ROS snap needs access to the Turtlebot at boot. We added the correct slot to our gadget and plug to our app, but this interface isn't automatically connected, which means at boot, the ROS snap doesn't actually have the appropriate permission to access the Turtlebot. This works okay for testing purposes, but to actually have a production image, we need this interface automatically connected. Eventually this will be automated, but currently it's a manual process. You have to:

  1. Put the ROS snap in the store (which we did in part 3).
  2. Put the gadget snap in the store (which we did in part 4).
  3. Create a new topic in the Snapcraft forum requesting that the kobuki interface (or whatever you called it) be automatically connected between these two snaps. This is done via an assertion on the gadget in the store.

Step 2: Install required tools

Ubuntu Core will verify that the image it's booting actually came from you. In order for that to happen, you need to create a signing key and register it with the store. You need Snapcraft and Snapd to generate, register, and use the signing key, and you need Ubuntu Image to actually create the image. You already have most of this from following the rest of the series, but for completeness:

$ sudo apt install snapcraft snapd ubuntu-image

Step 3: Create and register your signing key

Now let's create a signing key:

$ snapcraft create-key my-key-name

Check that everything worked and your key is ready:

$ snapcraft list-keys
Name SHA3-384 fingerprint
* my-key-name Qjdfpj0EW<snip>iZ41H4CROy (not registered)

Now register your new key with the store to associate it with your account:

$ snapcraft register-key
Registering key ...
Done. The key "my-key-name" (Qjdfp<snip>H4CROy) may be used to sign your assertions.

Step 4: Define the model

In order to create an Ubuntu Core image, we need to create what's called the "model definition." The "model" in question is your device. The definition is simply a JSON document that answers the questions "what is this device, who created it, and what software is running on it?" The model definition and assertion I created are available for reference.

Create a file named turtlebot-model.json and make it look something like this:

{
"type": "model",
"series": "16",
"model": "turtlebot-demo",
"architecture": "amd64",
"kernel": "pc-kernel",
"gadget": "<my gadget snap>",
"authority-id": "<my account ID>",
"brand-id": "<my account ID>",
"timestamp": "<timestamp>",
"required-snaps": ["<my turtlebot snap>"]
}

We specify that this is targeting Ubuntu Core series 16, name the model "turtlebot-demo" (you can use whatever you wish), specify the architecture for this model as well as the kernel snap to use (these would change if your arch wasn't amd64). Note that we're using the kernel maintained and supported by Canonical here. The gadget field needs to contain the name of your gadget snap. The authority-id and brand-id need to be the account ID from your store account. The timestamp needs to be the output of

$ date -Iseconds --utc

Finally, the required-snaps is a list of snaps to be preinstalled on this image. In our case, that's the name of the ROS snap we created in part 3.

Step 5: Create the model assertion

Now that we have a model definition, we need to use our key to sign it, thereby turning it into what's called a "model assertion":

$ cat turtlebot-model.json | snap sign -k my-key-name > turtlebot.model
You need a passphrase to unlock the secret key for
user: "my-key-name"
4096-bit RSA key, ID 0B79B865, created 2016-01-01
...

After giving your passphrase, a turtlebot.model file is created containing the assertion.

Step 6: Create the image

The assertion we just created is all we need for ubuntu-image to actually put the image together (note that the image I created is available for download, for both amd64 and the DragonBoard):

$ sudo ubuntu-image -c stable -o turtlebot.img turtlebot.model

This will pull down the various components of the image (gadget, kernel, core, and any additional snaps from required-snaps) from the stable channel, and create an Ubuntu Core image out of them. Well, what if the gadget (or other) snap is your own, and isn't available in the store yet? The assertion doesn't change, but you can provide local snaps to satisfy the snap names specified in the assertion with the --extra-snaps parameter. For example, say your gadget snap isn't available in the store; you could use the local one with:

$ sudo ubuntu-image -c stable -o turtlebot.img \
--extra-snaps /path/to/gadget.snap \
turtlebot.model

We already mentioned the downsides to this, but it's worth mentioning again: installing local snaps like this will prevent those snaps from updating, and will also prevent the serial-port interface from auto-connecting.

Once ubuntu-image completes successfully, you'll have an Ubuntu Core image ready to flash to your device!

Step 7: Test the image

You can take the image you just created for a test drive by following the flashing directions for the device you used, or if you use amd64 you can even test it out in VirtualBox or KVM.

Once the image boots (assuming the Turtlebot is connected and powered on), the Turtlebot will begin moving. Congratulations, you're done!

Step 7a: Connect interfaces if necessary

If you were unable to follow Step 1, then the Turtlebot probably didn't begin moving for you once you powered the device on. This is due to the lack of interface auto-connection. To work around that, go through the first boot wizard as discussed on the flashing directions, SSH into the device, and run:

$ snap interfaces
Slot Plug
[...]
my-gadget-snap:kobuki -
- my-turtlebot-snap:kobuki

As you can see, this interface is not connected. So connect them:

$ sudo snap connect my-turtlebot-snap:kobuki my-gadget-snap:kobuki
$ snap interfaces
Slot Plug
[...]
my-gadget-snap:kobuki my-turtlebot-snap:kobuki

The ROS snap now has permission to access the Turtlebot, but the service has probably errored out by now. So let's start it again by disabling and re-enabling the snap:

$ sudo snap disable my-turtlebot-snap
$ sudo snap enable my-turtlebot-snap

Now you should see the Turtlebot start moving as expected. This is persistent across reboots.

Conclusion

At this point, we've taken a ROS prototype running on classic Ubuntu all the way to a production-ready Ubuntu Core image with the ROS prototype preinstalled and running on boot. Every component of this image will automatically update, including your ROS snap. Admittedly there are a few rough edges, but I hope this shows you how powerful snaps and Ubuntu Core can be.

Hopefully you managed to follow along successfully, but if you ran into issues, here's the reference material for this series:

If you need any help with this, or anything to do with ROS on snaps and/or Ubuntu Core, please feel free to comment on this article, or ask in the Snapcraft forum.

Comments

Hi Kyle, I rebuilt the kernel with the following addition in snapcraft.yaml and other required changes,

slots:
ttyS1:
interface: serial-port
path: /dev/ttyS1

however the snap interfaces command does not indicate any change.
The snap list command indicates the new components,

Name Version Rev Developer Notes
classic 16.04 19 canonical devmode
core 16-2 1691 canonical -
dragonboard-gateway-abes 16.04-0.18 x1 -
dragonboard-kernel 4.4.0-1054.58 23 canonical -

Is it possible that the default dragonboard gadget device configuration is overwriting my modified gadget ogra mentions in the thread, https://lists.ubuntu.com/archives/snapcraft/2017-January/002418.html as follows:
"note that all our gadgets offer a console on serial by default, if you
drive some external device via serial you might want to drop this
console tty option.
all our official gadgets are under https://github.com/snapcore/ ..
namely the pi2-gadget, pi3-gadget, pc-gadget and dragonboard-gadget
sub-trees if you want to take a look ..."

Please advise,

Thanks,
Attila

By abes

Reply