Hosted Mender Getting Started on OSX and Raspberry Pi 3

Having various embedded linux devices around (mostly Raspberry Pi’s), and a few client projects dealing with software updates to remote devices, I’ve become interested in fleet management. More specifically, I wanted:

I’ve heard about resin.io before, and while appealing, the control freak in me wanted something with an open server infrastructrure. I’m also not sold on having docker in production embedded devices (while surely being useful for prototyping and experimentation).

There’s the nerves project, mostly focused around the elixir ecosystem. Something I definitely want to check out in more detail, both to learn more about elixir and for simpler embedded projects.

Then I stumbled onto mender. On first glance, it seems perfect. Let’s take a look, shall we?

Burning the initial image

We’re gonna be roughly following along the mender getting started guide while keeping things OSX compatible.

Instead of running our own server infrastructure (which is nice to have as an option, but not required for initial experimenting), we’ll be using hosted mender. That means we will have to inject our hosted mender token into the initial disk image what we will boot the RPi3 from.

Download the Raspberry Pi 3 disk image from https://docs.mender.io/1.3/getting-started/download-test-images . Decompress and change the file extension to make it palatable for hdiutil.

1
2
3
wget https://d1b0l86ne08fsf.cloudfront.net/1.3.1/raspberrypi3/mender-raspberrypi3_1.3.1.sdimg.gz
gunzip mender-raspberrypi3_1.3.1.sdimg.gz
mv mender-raspberrypi3_1.3.1.sdimg mender-raspberrypi3_1.3.1.img

Verify we have a good image

1
hdiutil imageinfo mender-raspberrypi3_1.3.1.img
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
Backing Store Information:
  URL: file:///Users/erkkieilonen/projects/learning/mender/mender-raspberrypi3_1.3.1.img
  Name: mender-raspberrypi3_1.3.1.img
  Class Name: CBSDBackingStore
Class Name: CRawDiskImage
Checksum Type: none
Size Information:
  Total Bytes: 624951296
  Compressed Ratio: 1
  Sector Count: 1220608
  Total Non-Empty Bytes: 624951296
  Compressed Bytes: 624951296
  Total Empty Bytes: 0
Format: RAW*
Format Description: raw read/write
Checksum Value:
Properties:
  Encrypted: false
 Kernel Compatible: true
 Checksummed: false
 Software License Agreement: false
 Partitioned: false
 Compressed: no
Segments:
  0: /Users/erkkieilonen/projects/learning/mender/mender-raspberrypi3_1.3.1.img
partitions:
  partition-scheme: fdisk
  block-size: 512
  partitions:
      0:
          partition-name: Master Boot Record
          partition-start: 0
          partition-synthesized: true
         partition-length: 1
          partition-hint: MBR
          boot-code: 0xFAB800108ED0BC00B0B800008ED88EC0FBBE007CBF0006B90002F3A4EA21060000BEBE073804750B83C61081FEFE0775F3EB16B402B001BB007CB2808A74018B4C02CD13EA007C0000EBFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000A31D6D430000
      1:
          partition-name:
          partition-start: 1
          partition-synthesized: true
         partition-length: 24575
          partition-hint: Apple_Free
      2:
          partition-start: 24576
          partition-number: 1
          partition-length: 81920
          partition-hint: Windows_FAT_32
          partition-filesystems:
              FAT16: boot
      3:
          partition-start: 106496
          partition-number: 2
          partition-length: 425984
          partition-hint: Linux_Ext2FS
      4:
          partition-start: 532480
          partition-number: 3
          partition-length: 425984
          partition-hint: Linux_Ext2FS
      5:
          partition-start: 958464
          partition-number: 4
          partition-length: 262144
          partition-hint: Linux_Ext2FS
  burnable: false
Resize limits (per hdiutil resize -limits):
 min   cur     max
1220608   1220608 1245311712

We can see the boot partition, the primary and secondary root partitions and the data partition. Since the root partitions are using ext2fs, and we’re on OSX, we need to install FUSE for macOS along with FUSE-Ext2 to be able to mount and write to these partitions.

FUSE for macOS

Download the OSX package from https://osxfuse.github.io and follow the instructions to install it. Make sure to tick the checkbox for MacFUSE Compatibility Layer. That’s required for FUSE-Ext2 support.

FUSE-Ext2

This gets a little more complicated. Instead of compiling everything from source, like described here, we’re using the excellent Homebrew package manager to install the dependencies and just compile FUSE-Ext2 itself. (we should probably create a formula for FUSE-Ext2 too …)

Install the dependencies

1
brew install m4 autoconf automake libtool e2fsprogs

Install FUSE-Ext2 itself

1
2
3
4
5
6
7
8
git clone https://github.com/alperakcan/fuse-ext2.git
cd fuse-ext2
./autogen.sh
./configure
CFLAGS="-I /usr/local/include -I $(brew --prefix e2fsprogs)/include" LDFLAGS="-L/usr/local/lib -L$(brew --prefix e2fsprogs)/lib" ./configure
make
sudo make install
cd ..

Attach the original mender image

1
hdiutil attach mender-raspberrypi3_1.3.1.img
1
2
3
4
5
/dev/disk2           FDisk_partition_scheme
/dev/disk2s1          Windows_FAT_32                  /Volumes/boot
/dev/disk2s2          Linux
/dev/disk2s3          Linux
/dev/disk2s4          Linux

Since /dev/disk1 is our OSX boot disk, and we have nothing else mounted, /dev/disk2 is the .img file we just attached. Pay attention to use the correct device in case you have more disks attached.

We will have to mount both of the root partitions and edit some files in there.

1
2
3
4
mkdir $(e2label /dev/disk2s2)
mkdir $(e2label /dev/disk2s3)
mount -t fuse-ext2 -o rw /dev/disk2s2 $(e2label /dev/disk2s2)
mount -t fuse-ext2 -o rw /dev/disk2s3 $(e2label /dev/disk2s3)

Grab your hosted mender token ( top right menu, under My organization) and inject it to the image. Replace <token from hosted mender> with your token.

1
2
3
4
sed -ibak 's/dummy/<token from hosted mender>/' primary/etc/mender/mender.conf
sed -ibak 's/docker.mender.io/hosted.mender.io/' primary/etc/mender/mender.conf
sed -ibak 's/dummy/<token from hosted mender>/' secondary/etc/mender/mender.conf
sed -ibak 's/docker.mender.io/hosted.mender.io/' secondary/etc/mender/mender.conf

Verify the config file contents

1
cat primary/etc/mender/mender.conf
1
2
3
4
5
6
7
8
9
10
{
    "InventoryPollIntervalSeconds": 5,
    "RetryPollIntervalSeconds": 1,
    "RootfsPartA": "/dev/mmcblk0p2",
    "RootfsPartB": "/dev/mmcblk0p3",
    "ServerCertificate": "/etc/mender/server.crt",
    "ServerURL": "https://hosted.mender.io",
    "TenantToken": "<token from hosted mender>",
    "UpdatePollIntervalSeconds": 5
}

Unmount and burn the image and we’re done. Adjust /dev/disk3 to your sdcard device.

1
2
3
4
5
umount primary
umount secondary
hdiutil detach /dev/disk2
sudo dd if=mender-raspberrypi3_1.3.1.img of=/dev/disk3 bs=1m && sudo sync
hdiutil detach /dev/disk3

Boot the RPi and check mender dashboard, you should see a new authorization request pop up.