One of our engineers just opened a PR for a FUSE driver for Nanos. What does this mean? It means you can now mount the filesystem natively on MacOS and Linux amongst other things.
Some of you might be scratching your head and asking why is this newsworthy. The reason is that ordinarily with unikernels there are no capabilities to interact with the end filesystem through the use of a shell or ssh - cause they don't exist. This is because there are no users or passwords to begin with. Nor is there the capability of running other programs on the end system other than the one that is running. This is all by design. Thus being able to interact with the system using a normal unix environment is very helpful from a dev/debug viewpoint.
What is FUSE:
FUSE allows us to mount a filesystem in user-space. Typically you need to be root or your /etc/fstab needs a user flag for the specific mount you want. FUSE sidesteps that which is a good thing because we don't want to sudo every single time we want to touch this.
FUSE allows us to mount the directory and then we can run any ordinary commands on the mountpoint before safely unmounting all as a normal user. This makes things like configuration editing super easy. It also opens the door to new ways of provisioning other files on the end disk and easier debugging. OPS traditionally only allowed the capability of specifying an array of Files or Dirs and they would be put on at image build time.
For example:
{
"Files": ["onefile", "twofile"]
}
{
"Dirs": ["mydir", "yourdir"]
}
This works, but again, needs to be done at build-time. Nanos also allows mounting and remounting external volumes, however, they need to be defined up front in the manifest. Some people do this to store on-disk 'secrets', rotating tls certs or other host config that changes periodically.
One of the cooler parts is that this is cross-platform meaning you can interact with the filesystem just as easy on Linux as on Mac.
This also builds on existing work that we had with the 'dump' tool.
Dump had a few different methods to use:
- 1) Literally dump the contents of a filesystem image into a directory.
- 2) Show a tree'd output filesystem from the image.
- 3) Show the contents of a crash log (if there was a crash) which is useful for debugging after the fact, and is precisely how we can currently send crash dumps to Radar after the entire thing has blown up but gets rebooted thanks to the 'RebootOnExit' flag one can enable.
Show Me the Code
If you are following along at home you'll want to have the FUSE kernel driver installed:
On Linux:
sudo apt install fuse libfuse-dev
On MacOS:
brew install macfuse
You might need to allow security settings for FUSE on mac and restart your machine.
Let's try a quick little example. We'll just simply read in a file, print the contents and then exit:
package main
import (
"fmt"
"io/ioutil"
)
func main() {
stuff, err := ioutil.ReadFile("bob.txt")
if err != nil {
fmt.Println(err)
}
fmt.Print(string(stuff))
}
We tell ops to include this txt file when building our image:
{
"Files": ["bob.txt"]
}
what about bob?
Then we run it:
➜ g ops run -c config.json g
booting /Users/eyberg/.ops/images/g.img ...
en1: assigned 10.0.2.15
what about bob?
exit status 1
Simple enough. Let' mount mount it:
mkdir -p /tmp/mnt
~/.ops/0.1.32/tfs-fuse /tmp/mnt ~/.ops/images/g.img
You might be wondering what "TFS" stands for - TFS or the Tuple File System really deserves it's own set of blogposts but is the native fs of choice with Nanos currently. You can read more about its design here.
Let's modify the file:
echo "he's not gone" > /tmp/mnt/bob.txt
We re-run our application but this time we 'skip' the build and wahlah!
➜ g ops run -c config.json -s g
booting /Users/eyberg/.ops/images/g.img ...
en1: assigned 10.0.2.15
he's not gone
exit status 1
This is obviously a trivial example, however, it shows a path forward for a 1000x better development experience.
I'm really excited about this new FUSE support - try it out and let us know what you do with it.