Skip to content

Instantly share code, notes, and snippets.

@fiorix
Last active June 20, 2025 23:23
Show Gist options
  • Save fiorix/cbb682bdf6eb977c599b404d94504005 to your computer and use it in GitHub Desktop.
Save fiorix/cbb682bdf6eb977c599b404d94504005 to your computer and use it in GitHub Desktop.
// Package main implements the mocker program, which is capable of creating
// container images for systemd-nspawn from Dockerfile configuration.
//
// It uses docker to extract the root filesystem of the image, but then
// runs all of the RUN commands from the systemd container.
//
// You have to ensure that the Dockerfile is installing systemd for the
// distro of the image, because these containers will be booted by using
// systemd-nspawn and they fail if there's no systemd inside the image.
//
// Example Dockerfile:
//
// FROM ubuntu
// RUN apt update && apt-get upgrade -y
// RUN apt install -y passwd systemd libpam-systemd
// RUN systemctl enable systemd-networkd
// RUN systemctl enable systemd-resolved
// RUN echo -ne "1\n1\n" | passwd root
//
// Create the container:
// mocker foobar /path/to/Dockerfile
//
// Connect to the container:
// machinectl login foobar
package main
import (
"bytes"
"fmt"
"io"
"os"
"strings"
)
func main() {
var outputCommand, nspawnCommand bytes.Buffer
if len(os.Args) != 3 {
fmt.Fprintf(os.Stderr, "use: %s [container-name] [/path/to/Dockerfile]", os.Args[0])
os.Exit(1)
}
containerName := os.Args[1]
containerDir := fmt.Sprintf("/var/lib/machines/%s", containerName)
dockerfilePath := os.Args[2]
buf, err := os.ReadFile(dockerfilePath)
if err != nil {
fmt.Fprintf(os.Stderr, "cannot read config file %q: %s\n", dockerfilePath, err)
fmt.Fprintf(os.Stderr, "usage: %s <path/to/Dockerfile>\n", os.Args[0])
os.Exit(1)
}
rootDir, err := os.MkdirTemp("", "mocker_")
if err != nil {
fmt.Fprintf(os.Stderr, "cannot create temporary root directory: %s\n", err)
os.Exit(1)
}
args := []string{
"systemd-nspawn",
"--pipe",
"--resolv-conf=copy-static",
"--timezone=copy",
"-D", containerDir,
"<< EOF",
"\nset -ex\n",
}
fmt.Fprintln(&nspawnCommand, strings.Join(args, " "))
var dockerImage string
for _, line := range strings.Split(string(buf), "\n") {
trimmedLine := strings.Trim(line, " ")
if trimmedLine == "" || trimmedLine[0] == '#' {
continue
}
kv := strings.SplitN(trimmedLine, " ", 2)
if len(kv) != 2 {
fmt.Fprintf(os.Stderr, "invalid config line: %q\n", line)
os.Exit(1)
}
switch kv[0] {
case "FROM":
dockerImage = kv[1]
case "RUN":
fmt.Fprintln(&nspawnCommand, kv[1])
default:
fmt.Fprintf(os.Stderr, "invalid command: %q\n", line)
os.Exit(1)
}
}
fmt.Fprintf(&nspawnCommand, "EOF\n")
if dockerImage == "" {
fmt.Fprintf(os.Stderr, "Dockerfile is missing the FROM command. No base image to download.\n")
os.Exit(1)
}
fmt.Fprintf(&outputCommand, "#!/usr/bin/env bash\n\n")
fmt.Fprintf(&outputCommand, "set -ex\n\n")
fmt.Fprintf(&outputCommand, "mkdir -p %s\n\n", rootDir)
fmt.Fprintf(&outputCommand, "MOCKER_ROOT=$(docker create %s)\n", dockerImage)
fmt.Fprintf(&outputCommand, "docker export $MOCKER_ROOT | tar -xC %s\n", rootDir)
fmt.Fprintf(&outputCommand, "docker rm -f $MOCKER_ROOT\n\n")
fmt.Fprintf(&outputCommand, "machinectl stop %s || true\n", containerName)
fmt.Fprintf(&outputCommand, "systemctl disable --now systemd-nspawn@%s.service || true\n", containerName)
fmt.Fprintf(&outputCommand, "rm -f /etc/systemd/nspawn/%s.nspawn\n", containerName)
fmt.Fprintf(&outputCommand, "rm -rf %s && mv %s %s\n", containerDir, rootDir, containerDir)
io.Copy(&outputCommand, &nspawnCommand)
fmt.Fprintf(&outputCommand, "\n")
fmt.Fprintf(&outputCommand, "mkdir -p /etc/systemd/nspawn/\n")
fmt.Fprintf(&outputCommand, "cat << EOF > /etc/systemd/nspawn/%s.nspawn\n", containerName)
fmt.Fprintf(&outputCommand, "[Files]\n")
fmt.Fprintf(&outputCommand, "Root=%s\n\n", containerDir)
fmt.Fprintf(&outputCommand, "[Spawn]\n")
fmt.Fprintf(&outputCommand, "Timezone=copy\n\n")
fmt.Fprintf(&outputCommand, "[Network]\n")
fmt.Fprintf(&outputCommand, "ResolvConf=copy-static\n")
fmt.Fprintf(&outputCommand, "VirtualEthernet=no\n")
fmt.Fprintf(&outputCommand, "EOF\n\n")
fmt.Fprintf(&outputCommand, "systemctl enable --now systemd-nspawn@%s.service\n", containerName)
io.Copy(os.Stdout, &outputCommand)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment