summary refs log tree commit diff
path: root/nixos
diff options
context:
space:
mode:
authorArnold Krille <arnold@arnoldarts.de>2016-04-02 17:03:30 +0200
committerArnold Krille <arnold@arnoldarts.de>2016-04-02 17:07:41 +0200
commit3c819f28f57ee1485f15faf917f2fc6a861a6883 (patch)
treedcc79806b6c27a356c013471de379c97451143e9 /nixos
parent2d6a2b41313464ba82297c4f6e86c7026c7d132e (diff)
downloadnixlib-3c819f28f57ee1485f15faf917f2fc6a861a6883.tar
nixlib-3c819f28f57ee1485f15faf917f2fc6a861a6883.tar.gz
nixlib-3c819f28f57ee1485f15faf917f2fc6a861a6883.tar.bz2
nixlib-3c819f28f57ee1485f15faf917f2fc6a861a6883.tar.lz
nixlib-3c819f28f57ee1485f15faf917f2fc6a861a6883.tar.xz
nixlib-3c819f28f57ee1485f15faf917f2fc6a861a6883.tar.zst
nixlib-3c819f28f57ee1485f15faf917f2fc6a861a6883.zip
containers: Make declarative containers real systemd services
Without the templating (which is still present for imperative containers), it
will be possible to set individual dependencies. Like depending on the network
only if the hostbridge or hardware interfaces are used.

Ported from #3021
Diffstat (limited to 'nixos')
-rw-r--r--nixos/modules/virtualisation/containers.nix307
1 files changed, 147 insertions, 160 deletions
diff --git a/nixos/modules/virtualisation/containers.nix b/nixos/modules/virtualisation/containers.nix
index 4c20ee27de2c..fca21a8610be 100644
--- a/nixos/modules/virtualisation/containers.nix
+++ b/nixos/modules/virtualisation/containers.nix
@@ -278,167 +278,180 @@ in
   };
 
 
-  config = mkIf (config.boot.enableContainers) {
+  config = mkIf (config.boot.enableContainers) (let
 
-    systemd.services."container@" =
-      { description = "Container '%i'";
+    unit = {
+      description = "Container '%i'";
 
-        unitConfig.RequiresMountsFor = [ "/var/lib/containers/%i" ];
+      unitConfig.RequiresMountsFor = [ "/var/lib/containers/%i" ];
 
-        path = [ pkgs.iproute ];
+      path = [ pkgs.iproute ];
 
-        environment.INSTANCE = "%i";
-        environment.root = "/var/lib/containers/%i";
+      environment.INSTANCE = "%i";
+      environment.root = "/var/lib/containers/%i";
 
-        preStart =
-          ''
-            # Clean up existing machined registration and interfaces.
-            machinectl terminate "$INSTANCE" 2> /dev/null || true
+      preStart =
+        ''
+          # Clean up existing machined registration and interfaces.
+          machinectl terminate "$INSTANCE" 2> /dev/null || true
 
-            if [ "$PRIVATE_NETWORK" = 1 ]; then
-              ip link del dev "ve-$INSTANCE" 2> /dev/null || true
-              ip link del dev "vb-$INSTANCE" 2> /dev/null || true
-            fi
-         '';
+          if [ "$PRIVATE_NETWORK" = 1 ]; then
+            ip link del dev "ve-$INSTANCE" 2> /dev/null || true
+            ip link del dev "vb-$INSTANCE" 2> /dev/null || true
+          fi
+       '';
 
-        script =
-          ''
-            mkdir -p -m 0755 "$root/etc" "$root/var/lib"
-            mkdir -p -m 0700 "$root/var/lib/private" "$root/root" /run/containers
-            if ! [ -e "$root/etc/os-release" ]; then
-              touch "$root/etc/os-release"
+      script =
+        ''
+          mkdir -p -m 0755 "$root/etc" "$root/var/lib"
+          mkdir -p -m 0700 "$root/var/lib/private" "$root/root" /run/containers
+          if ! [ -e "$root/etc/os-release" ]; then
+            touch "$root/etc/os-release"
+          fi
+
+          mkdir -p -m 0755 \
+            "/nix/var/nix/profiles/per-container/$INSTANCE" \
+            "/nix/var/nix/gcroots/per-container/$INSTANCE"
+
+          cp --remove-destination /etc/resolv.conf "$root/etc/resolv.conf"
+
+          if [ "$PRIVATE_NETWORK" = 1 ]; then
+            extraFlags+=" --network-veth"
+            if [ -n "$HOST_BRIDGE" ]; then
+              extraFlags+=" --network-bridge=$HOST_BRIDGE"
             fi
+          fi
 
-            mkdir -p -m 0755 \
-              "/nix/var/nix/profiles/per-container/$INSTANCE" \
-              "/nix/var/nix/gcroots/per-container/$INSTANCE"
+          for iface in $INTERFACES; do
+            extraFlags+=" --network-interface=$iface"
+          done
 
-            cp --remove-destination /etc/resolv.conf "$root/etc/resolv.conf"
+          for iface in $MACVLANS; do
+            extraFlags+=" --network-macvlan=$iface"
+          done
 
-            if [ "$PRIVATE_NETWORK" = 1 ]; then
-              extraFlags+=" --network-veth"
-              if [ -n "$HOST_BRIDGE" ]; then
-                extraFlags+=" --network-bridge=$HOST_BRIDGE"
-              fi
+          # If the host is 64-bit and the container is 32-bit, add a
+          # --personality flag.
+          ${optionalString (config.nixpkgs.system == "x86_64-linux") ''
+            if [ "$(< ''${SYSTEM_PATH:-/nix/var/nix/profiles/per-container/$INSTANCE/system}/system)" = i686-linux ]; then
+              extraFlags+=" --personality=x86"
             fi
+          ''}
+
+
+
+          # Run systemd-nspawn without startup notification (we'll
+          # wait for the container systemd to signal readiness).
+          EXIT_ON_REBOOT=1 NOTIFY_SOCKET= \
+          exec ${config.systemd.package}/bin/systemd-nspawn \
+            --keep-unit \
+            -M "$INSTANCE" -D "$root" $extraFlags \
+            $EXTRA_NSPAWN_FLAGS \
+            --bind-ro=/nix/store \
+            --bind-ro=/nix/var/nix/db \
+            --bind-ro=/nix/var/nix/daemon-socket \
+            --bind=/run/systemd/notify:/var/lib/private/host-notify \
+            --bind="/nix/var/nix/profiles/per-container/$INSTANCE:/nix/var/nix/profiles" \
+            --bind="/nix/var/nix/gcroots/per-container/$INSTANCE:/nix/var/nix/gcroots" \
+            --setenv PRIVATE_NETWORK="$PRIVATE_NETWORK" \
+            --setenv HOST_BRIDGE="$HOST_BRIDGE" \
+            --setenv HOST_ADDRESS="$HOST_ADDRESS" \
+            --setenv LOCAL_ADDRESS="$LOCAL_ADDRESS" \
+            --setenv HOST_ADDRESS6="$HOST_ADDRESS6" \
+            --setenv LOCAL_ADDRESS6="$LOCAL_ADDRESS6" \
+            --setenv PATH="$PATH" \
+            ${containerInit} "''${SYSTEM_PATH:-/nix/var/nix/profiles/system}/init"
+        '';
 
-            for iface in $INTERFACES; do
-              extraFlags+=" --network-interface=$iface"
-            done
-
-            for iface in $MACVLANS; do
-              extraFlags+=" --network-macvlan=$iface"
-            done
-
-            # If the host is 64-bit and the container is 32-bit, add a
-            # --personality flag.
-            ${optionalString (config.nixpkgs.system == "x86_64-linux") ''
-              if [ "$(< ''${SYSTEM_PATH:-/nix/var/nix/profiles/per-container/$INSTANCE/system}/system)" = i686-linux ]; then
-                extraFlags+=" --personality=x86"
+      postStart =
+        ''
+          if [ "$PRIVATE_NETWORK" = 1 ]; then
+            if [ -z "$HOST_BRIDGE" ]; then
+              ifaceHost=ve-$INSTANCE
+              ip link set dev $ifaceHost up
+              if [ -n "$HOST_ADDRESS" ]; then
+                ip addr add $HOST_ADDRESS dev $ifaceHost
               fi
-            ''}
-
-
-
-            # Run systemd-nspawn without startup notification (we'll
-            # wait for the container systemd to signal readiness).
-            EXIT_ON_REBOOT=1 NOTIFY_SOCKET= \
-            exec ${config.systemd.package}/bin/systemd-nspawn \
-              --keep-unit \
-              -M "$INSTANCE" -D "$root" $extraFlags \
-              $EXTRA_NSPAWN_FLAGS \
-              --bind-ro=/nix/store \
-              --bind-ro=/nix/var/nix/db \
-              --bind-ro=/nix/var/nix/daemon-socket \
-              --bind=/run/systemd/notify:/var/lib/private/host-notify \
-              --bind="/nix/var/nix/profiles/per-container/$INSTANCE:/nix/var/nix/profiles" \
-              --bind="/nix/var/nix/gcroots/per-container/$INSTANCE:/nix/var/nix/gcroots" \
-              --setenv PRIVATE_NETWORK="$PRIVATE_NETWORK" \
-              --setenv HOST_BRIDGE="$HOST_BRIDGE" \
-              --setenv HOST_ADDRESS="$HOST_ADDRESS" \
-              --setenv LOCAL_ADDRESS="$LOCAL_ADDRESS" \
-              --setenv HOST_ADDRESS6="$HOST_ADDRESS6" \
-              --setenv LOCAL_ADDRESS6="$LOCAL_ADDRESS6" \
-              --setenv PATH="$PATH" \
-              ${containerInit} "''${SYSTEM_PATH:-/nix/var/nix/profiles/system}/init"
-          '';
-
-        postStart =
-          ''
-            if [ "$PRIVATE_NETWORK" = 1 ]; then
-              if [ -z "$HOST_BRIDGE" ]; then
-                ifaceHost=ve-$INSTANCE
-                ip link set dev $ifaceHost up
-                if [ -n "$HOST_ADDRESS" ]; then
-                  ip addr add $HOST_ADDRESS dev $ifaceHost
-                fi
-                if [ -n "$HOST_ADDRESS6" ]; then
-                  ip -6 addr add $HOST_ADDRESS6 dev $ifaceHost
-                fi
-                if [ -n "$LOCAL_ADDRESS" ]; then
-                  ip route add $LOCAL_ADDRESS dev $ifaceHost
-                fi
-                if [ -n "$LOCAL_ADDRESS6" ]; then
-                  ip -6 route add $LOCAL_ADDRESS6 dev $ifaceHost
-                fi
+              if [ -n "$HOST_ADDRESS6" ]; then
+                ip -6 addr add $HOST_ADDRESS6 dev $ifaceHost
+              fi
+              if [ -n "$LOCAL_ADDRESS" ]; then
+                ip route add $LOCAL_ADDRESS dev $ifaceHost
+              fi
+              if [ -n "$LOCAL_ADDRESS6" ]; then
+                ip -6 route add $LOCAL_ADDRESS6 dev $ifaceHost
               fi
             fi
+          fi
 
-            # Get the leader PID so that we can signal it in
-            # preStop. We can't use machinectl there because D-Bus
-            # might be shutting down. FIXME: in systemd 219 we can
-            # just signal systemd-nspawn to do a clean shutdown.
-            machinectl show "$INSTANCE" | sed 's/Leader=\(.*\)/\1/;t;d' > "/run/containers/$INSTANCE.pid"
-          '';
-
-        preStop =
-          ''
-            pid="$(cat /run/containers/$INSTANCE.pid)"
-            if [ -n "$pid" ]; then
-              kill -RTMIN+4 "$pid"
-            fi
-            rm -f "/run/containers/$INSTANCE.pid"
-          '';
+          # Get the leader PID so that we can signal it in
+          # preStop. We can't use machinectl there because D-Bus
+          # might be shutting down. FIXME: in systemd 219 we can
+          # just signal systemd-nspawn to do a clean shutdown.
+          machinectl show "$INSTANCE" | sed 's/Leader=\(.*\)/\1/;t;d' > "/run/containers/$INSTANCE.pid"
+        '';
 
-        restartIfChanged = false;
-        #reloadIfChanged = true; # FIXME
+      preStop =
+        ''
+          pid="$(cat /run/containers/$INSTANCE.pid)"
+          if [ -n "$pid" ]; then
+            kill -RTMIN+4 "$pid"
+          fi
+          rm -f "/run/containers/$INSTANCE.pid"
+        '';
 
-        wants = [ "netwprk.target" ];
-        after = [ "network.target" ];
+      restartIfChanged = false;
 
-        serviceConfig = {
-          ExecReload = pkgs.writeScript "reload-container"
-            ''
-              #! ${pkgs.stdenv.shell} -e
-              ${nixos-container}/bin/nixos-container run "$INSTANCE" -- \
-                bash --login -c "''${SYSTEM_PATH:-/nix/var/nix/profiles/system}/bin/switch-to-configuration test"
-            '';
+      serviceConfig = {
+        ExecReload = pkgs.writeScript "reload-container"
+          ''
+            #! ${pkgs.stdenv.shell} -e
+            ${nixos-container}/bin/nixos-container run "$INSTANCE" -- \
+              bash --login -c "''${SYSTEM_PATH:-/nix/var/nix/profiles/system}/bin/switch-to-configuration test"
+          '';
 
-          SyslogIdentifier = "container %i";
+        SyslogIdentifier = "container %i";
 
-          EnvironmentFile = "-/etc/containers/%i.conf";
+        EnvironmentFile = "-/etc/containers/%i.conf";
 
-          Type = "notify";
+        Type = "notify";
 
-          NotifyAccess = "all";
+        NotifyAccess = "all";
 
-          # Note that on reboot, systemd-nspawn returns 133, so this
-          # unit will be restarted. On poweroff, it returns 0, so the
-          # unit won't be restarted.
-          RestartForceExitStatus = "133";
-          SuccessExitStatus = "133";
+        # Note that on reboot, systemd-nspawn returns 133, so this
+        # unit will be restarted. On poweroff, it returns 0, so the
+        # unit won't be restarted.
+        RestartForceExitStatus = "133";
+        SuccessExitStatus = "133";
 
-          Restart = "on-failure";
+        Restart = "on-failure";
 
-          # Hack: we don't want to kill systemd-nspawn, since we call
-          # "machinectl poweroff" in preStop to shut down the
-          # container cleanly. But systemd requires sending a signal
-          # (at least if we want remaining processes to be killed
-          # after the timeout). So send an ignored signal.
-          KillMode = "mixed";
-          KillSignal = "WINCH";
-        };
+        # Hack: we don't want to kill systemd-nspawn, since we call
+        # "machinectl poweroff" in preStop to shut down the
+        # container cleanly. But systemd requires sending a signal
+        # (at least if we want remaining processes to be killed
+        # after the timeout). So send an ignored signal.
+        KillMode = "mixed";
+        KillSignal = "WINCH";
       };
+    };
+  in {
+    systemd.services = listToAttrs (filter (x: x.value != null) (
+      # The generic container template used by imperative containers
+      [{ name = "container@"; value = unit; }]
+      # declarative containers
+      ++ (mapAttrsToList (name: cfg: nameValuePair "container@${name}" (
+        if cfg.autoStart then
+          unit // {
+            wantedBy = [ "multi-user.target" ];
+            wants = [ "network.target" ];
+            after = [ "network.target" ];
+            restartTriggers = [ cfg.path ];
+            reloadIfChanged = true;
+          }
+        else null
+      )) config.containers)
+    ));
 
     # Generate a configuration file in /etc/containers for each
     # container so that container@.target can get the container
@@ -482,31 +495,5 @@ in
     networking.dhcpcd.denyInterfaces = [ "ve-*" ];
 
     environment.systemPackages = [ nixos-container ];
-
-    # Start containers at boot time.
-    systemd.services.all-containers =
-      { description = "All Containers";
-
-        wantedBy = [ "multi-user.target" ];
-
-        unitConfig.ConditionDirectoryNotEmpty = "/etc/containers";
-
-        serviceConfig.Type = "oneshot";
-
-        script =
-          ''
-            res=0
-            shopt -s nullglob
-            for i in /etc/containers/*.conf; do
-              AUTO_START=
-              source "$i"
-              if [ "$AUTO_START" = 1 ]; then
-                systemctl start "container@$(basename "$i" .conf).service" || res=1
-              fi
-            done
-            exit $res
-          ''; # */
-      };
-
-  };
+  });
 }