summary refs log tree commit diff
path: root/pkgs/build-support/portable-service/default.nix
diff options
context:
space:
mode:
authorДамјан Георгиевски <gdamjan@gmail.com>2022-08-25 23:14:41 +0200
committerДамјан Георгиевски <gdamjan@gmail.com>2022-09-22 20:11:25 +0200
commit499aebcf340f48ef02211700b39512c429bf88bb (patch)
tree8900d10000369c8f5bbfbc01d97b04551b68471a /pkgs/build-support/portable-service/default.nix
parenta22f7345336c7bbb785d3525c6760c5391c3171f (diff)
downloadnixpkgs-499aebcf340f48ef02211700b39512c429bf88bb.tar
nixpkgs-499aebcf340f48ef02211700b39512c429bf88bb.tar.gz
nixpkgs-499aebcf340f48ef02211700b39512c429bf88bb.tar.bz2
nixpkgs-499aebcf340f48ef02211700b39512c429bf88bb.tar.lz
nixpkgs-499aebcf340f48ef02211700b39512c429bf88bb.tar.xz
nixpkgs-499aebcf340f48ef02211700b39512c429bf88bb.tar.zst
nixpkgs-499aebcf340f48ef02211700b39512c429bf88bb.zip
portableService: tooling to create portable service images
see https://systemd.io/PORTABLE_SERVICES/ about the definition of
portable services. this tooling is analogous to the `pkgs.dockerTools.buildImage`
tooling and is called `pkgs.portableService`.

systemd (since version 239) supports a concept of “Portable Services”.
“Portable Services” are a delivery method for system services that uses
two specific features of container management:

* Applications are bundled. I.e. multiple services, their binaries and all
  their dependencies are packaged in an image, and are run directly from it.
* Stricter default security policies, i.e. sandboxing of applications.

The primary tool for interacting with Portable Services is portablectl,
and they are managed by the systemd-portabled service.

This function will create a squashfs raw image in `result/$pname_$version.raw`
that has the required files by the portable services spec, and all the
dependencies for the running program in the nix store.
Diffstat (limited to 'pkgs/build-support/portable-service/default.nix')
-rw-r--r--pkgs/build-support/portable-service/default.nix111
1 files changed, 111 insertions, 0 deletions
diff --git a/pkgs/build-support/portable-service/default.nix b/pkgs/build-support/portable-service/default.nix
new file mode 100644
index 00000000000..6389e8d66fb
--- /dev/null
+++ b/pkgs/build-support/portable-service/default.nix
@@ -0,0 +1,111 @@
+{ pkgs, lib, stdenv }:
+/*
+  Create a systemd portable service image
+  https://systemd.io/PORTABLE_SERVICES/
+
+  Example:
+  pkgs.portableService {
+    pname = "demo";
+    version = "1.0";
+    units = [ demo-service demo-socket ];
+  }
+*/
+{
+  # The name and version of the portable service. The resulting image will be
+  # created in result/$pname_$version.raw
+  pname
+, version
+
+  # Units is a list of derivations for systemd unit files. Those files will be
+  # copied to /etc/systemd/system in the resulting image. Note that the unit
+  # names must be prefixed with the name of the portable service.
+, units
+
+  # Basic info about the portable service image, used for the generated
+  # /etc/os-release
+, description ? null
+, homepage ? null
+
+  # A list of attribute sets {object, symlink}. Symlinks will be created
+  # in the root filesystem of the image to objects in the nix store.
+, symlinks ? [ ]
+
+  # A list of additional derivations to be included in the image as-is.
+, contents ? [ ]
+
+  # mksquashfs options
+, squashfsTools ? pkgs.squashfsTools
+, squash-compression ? "xz -Xdict-size 100%"
+, squash-block-size ? "1M"
+}:
+
+let
+  filterNull = lib.filterAttrs (_: v: v != null);
+  envFileGenerator = lib.generators.toKeyValue { };
+
+  rootFsScaffold =
+    let
+      os-release-params = {
+        PORTABLE_ID = pname;
+        PORTABLE_PRETTY_NAME = description;
+        HOME_URL = homepage;
+        ID = "nixos";
+        PRETTY_NAME = "NixOS";
+        BUILD_ID = "rolling";
+      };
+      os-release = pkgs.writeText "os-release"
+        (envFileGenerator (filterNull os-release-params));
+
+    in
+    stdenv.mkDerivation {
+      pname = "root-fs-scaffold";
+      inherit version;
+
+      buildCommand = ''
+        # scaffold a file system layout
+        mkdir -p $out/etc/systemd/system $out/proc $out/sys $out/dev $out/run \
+                 $out/tmp $out/var/tmp $out/var/lib $out/var/cache $out/var/log
+
+        # empty files to mount over with host's version
+        touch $out/etc/resolv.conf $out/etc/machine-id
+
+        # required for portable services
+        cp ${os-release} $out/etc/os-release
+      ''
+      # units **must** be copied to /etc/systemd/system/
+      + (lib.concatMapStringsSep "\n" (u: "cp ${u} $out/etc/systemd/system/${u.name};") units)
+      + (lib.concatMapStringsSep "\n"
+        ({ object, symlink }: ''
+          mkdir -p $(dirname $out/${symlink});
+          ln -s ${object} $out/${symlink};
+        '')
+        symlinks)
+      ;
+    };
+in
+
+assert lib.assertMsg (lib.all (u: lib.hasPrefix pname u.name) units) "Unit names must be prefixed with the service name";
+
+stdenv.mkDerivation {
+  pname = "${pname}-img";
+  inherit version;
+
+  nativeBuildInputs = [ squashfsTools ];
+  closureInfo = pkgs.closureInfo { rootPaths = [ rootFsScaffold ] ++ contents; };
+
+  buildCommand = ''
+    mkdir -p nix/store
+    for i in $(< $closureInfo/store-paths); do
+      cp -a "$i" "''${i:1}"
+    done
+
+    mkdir -p $out
+    # the '.raw' suffix is mandatory by the portable service spec
+    mksquashfs nix ${rootFsScaffold}/* $out/"${pname}_${version}.raw" \
+      -quiet -noappend \
+      -exit-on-error \
+      -keep-as-directory \
+      -all-root -root-mode 755 \
+      -b ${squash-block-size} -comp ${squash-compression}
+  '';
+}