diff --git a/modules/virtualisation/oci-containers/default.nix b/modules/virtualisation/oci-containers/default.nix deleted file mode 100644 index ea26ce8..0000000 --- a/modules/virtualisation/oci-containers/default.nix +++ /dev/null @@ -1,122 +0,0 @@ -# ```admonish note title="To do" -# Run podman containers run as a non-root user. -# ``` -# -# An abstraction over running containers as systemd units, enforcing some good -# practices: -# -# - Container DNS behaves the same under docker and podman. -# - Ports are exposed on `127.0.0.1`, rather than `0.0.0.0`. -# - Volumes are backed up by bind-mounts to the host filesystem. -# -# Switching between using docker or podman for the container runtime should be -# totally transparent. -# -# **Erase your darlings:** overrides the `volumeBaseDir`. -{ config, lib, pkgs, ... }: - -with lib; -let - mkPortDef = { host, inner }: "127.0.0.1:${toString host}:${toString inner}"; - - mkVolumeDef = container: { name, host, inner }: - if host != null - then "${host}:${inner}" - else "${cfg.volumeBaseDir}/${container.volumeSubDir}/${name}:${inner}"; - - shouldPreStart = _name: container: container.pullOnStart; - mkPreStart = name: container: nameValuePair "${cfg.backend}-${name}" { - preStart = if container.pullOnStart then "${cfg.backend} pull ${container.image}" else ""; - }; - - shouldNetworkService = _name: container: container.network != null; - mkNetworkService = _name: container: - let package = if cfg.backend == "docker" then pkgs.docker else pkgs.podman; - in nameValuePair "${cfg.backend}-net-${container.network}" { - description = "Manage the ${container.network} network for ${cfg.backend}"; - preStart = "${package}/bin/${cfg.backend} network rm ${container.network} || true"; - serviceConfig = { - Type = "oneshot"; - ExecStart = "${package}/bin/${cfg.backend} network create -d bridge ${container.network}"; - ExecStop = "${package}/bin/${cfg.backend} network rm ${container.network}"; - RemainAfterExit = "yes"; - }; - }; - - mkPodService = name: pod: - let - package = if cfg.backend == "podman" then pkgs.podman else throw "mkPodService only supports podman"; - aliases = map (cn: "${name}-${cn}") (attrNames pod.containers); - ports = concatLists (catAttrs "ports" (attrValues pod.containers)); - in - nameValuePair "${cfg.backend}-pod-${name}" { - description = "Manage the ${name} pod for ${cfg.backend}"; - preStart = "${package}/bin/${cfg.backend} pod rm --force --ignore ${name} || true"; - serviceConfig = { - Type = "oneshot"; - ExecStart = - let args = map (n: "--network-alias=${n}") aliases ++ map (pd: "-p ${mkPortDef pd}") ports; - in "${package}/bin/${cfg.backend} pod create ${concatStringsSep " " args} ${name}"; - ExecStop = "${package}/bin/${cfg.backend} pod rm ${name}"; - RemainAfterExit = "yes"; - }; - }; - - mkContainer = _name: container: with container; - let - hasNetwork = container.network != null; - hasPod = container.pod != null; - in - { - inherit autoStart cmd environment environmentFiles image login; - dependsOn = - container.dependsOn ++ - (if hasNetwork then [ "net-${container.network}" ] else [ ]) ++ - (if hasPod then [ "pod-${container.pod}" ] else [ ]); - extraOptions = - container.extraOptions ++ - (if hasNetwork then [ "--network=${container.network}" ] else [ ]) ++ - (if hasPod then [ "--pod=${container.pod}" ] else [ ]); - /* ports are defined at the pod level */ - ports = if hasPod then [ ] else map mkPortDef ports; - volumes = map (mkVolumeDef container) volumes; - }; - - cfg = config.nixfiles.oci-containers; - - allContainers = - let - mkPodContainer = podName: pod: containerName: container: nameValuePair "${podName}-${containerName}" ( - container // - { - network = if cfg.backend == "docker" then podName else null; - pod = if cfg.backend == "docker" then null else podName; - volumeSubDir = pod.volumeSubDir; - } - ); - in - concatMapAttrs (podName: pod: mapAttrs' (mkPodContainer podName pod) pod.containers) cfg.pods; -in -{ - imports = [ - ./options.nix - ]; - - config = { - virtualisation.${cfg.backend} = { - enable = true; - autoPrune.enable = true; - }; - - virtualisation.oci-containers = { - backend = cfg.backend; - containers = mapAttrs mkContainer allContainers; - }; - - systemd.services = mkMerge [ - (mapAttrs' mkPreStart (filterAttrs shouldPreStart allContainers)) - (mapAttrs' mkNetworkService (filterAttrs shouldNetworkService allContainers)) - (if cfg.backend == "podman" then mapAttrs' mkPodService cfg.pods else { }) - ]; - }; -} diff --git a/modules/virtualisation/oci-containers/options.nix b/modules/virtualisation/oci-containers/options.nix deleted file mode 100644 index 38ef737..0000000 --- a/modules/virtualisation/oci-containers/options.nix +++ /dev/null @@ -1,207 +0,0 @@ -{ lib, ... }: - -with lib; -let - portOptions = { - host = mkOption { - type = types.int; - description = mdDoc '' - Host port (on 127.0.0.1) to expose the container port on. - ''; - }; - - inner = mkOption { - type = types.int; - description = mdDoc '' - The container port to expose to the hosti. - ''; - }; - }; - - volumeOptions = { - name = mkOption { - type = types.nullOr types.str; - default = null; - description = mdDoc '' - Name of the volume. This is equivalent to: - - ```nix - host = "''${volumeBaseDir}/''${volumeSubDir}/''${name}"; - ``` - - This option c.logonflicts with `''${host}`. - ''; - }; - - host = mkOption { - type = types.nullOr types.str; - default = null; - description = mdDoc '' - Directory on the host to bind-mount into the container. - - This option conflicts with `''${name}`. - ''; - }; - - inner = mkOption { - type = types.str; - description = mdDoc '' - Directory in the container to mount the volume to. - ''; - }; - }; - - containerOptions = { - /* regular oci-containers */ - autoStart = mkOption { - type = types.bool; - default = true; - description = mdDoc '' - Start the container automatically on boot. - ''; - }; - - cmd = mkOption { - type = types.listOf types.str; - default = [ ]; - description = mdDoc '' - Command-line arguments to pass to the container image's entrypoint. - ''; - }; - - dependsOn = mkOption { - type = types.listOf types.str; - default = [ ]; - example = [ "concourse-db" ]; - description = mdDoc '' - Other containers that this one depends on, in `''${pod}-''${name}` - format. - ''; - }; - - environment = mkOption { - type = types.attrsOf types.str; - default = { }; - description = mdDoc '' - Environment variables to set for this container. - ''; - }; - - environmentFiles = mkOption { - type = types.listOf types.path; - default = [ ]; - description = mdDoc '' - List of environment files for this container. - ''; - }; - - extraOptions = mkOption { - type = types.listOf types.str; - default = [ ]; - description = mdDoc '' - Extra options to pass to `docker run` / `podman run`. - ''; - }; - - image = mkOption { - type = types.str; - description = mdDoc '' - Container image to run. - ''; - }; - - login = { - username = mkOption { - type = types.nullOr types.str; - default = null; - description = mdDoc '' - Username for the container registry. - ''; - }; - passwordFile = mkOption { - type = types.nullOr types.str; - default = null; - description = mdDoc '' - File containing the password for the container registry. - ''; - }; - registry = mkOption { - type = types.nullOr types.str; - default = null; - description = mdDoc '' - Container registry to authenticate with. - ''; - }; - }; - - /* changed */ - ports = mkOption { - type = types.listOf (types.submodule { options = portOptions; }); - default = [ ]; - description = mdDoc '' - List of ports to expose. - ''; - }; - - volumes = mkOption { - type = types.listOf (types.submodule { options = volumeOptions; }); - default = [ ]; - description = mdDoc '' - List of volume definitions. - ''; - }; - - /* new options */ - pullOnStart = mkOption { - type = types.bool; - default = true; - description = mdDoc '' - Pull the container image when starting (useful for `:latest` images). - ''; - }; - }; -in -{ - options.nixfiles.oci-containers = { - backend = mkOption { - type = types.enum [ "docker" "podman" ]; - default = "docker"; - description = mdDoc '' - The container runtime. - ''; - }; - - pods = mkOption { - type = types.attrsOf (types.submodule ({ name, ... }: { - options = { - containers = mkOption { - type = types.attrsOf (types.submodule { options = containerOptions; }); - default = { }; - description = mdDoc '' - Attrset of container definitions. - ''; - }; - volumeSubDir = mkOption { - type = types.str; - default = name; - description = mdDoc '' - Subdirectory of the `''${volumeBaseDir}` to store bind-mounts - under. - ''; - }; - }; - })); - default = { }; - description = mdDoc '' - Attrset of pod definitions. - ''; - }; - - volumeBaseDir = mkOption { - type = types.str; - description = mdDoc '' - Directory to store volume bind-mounts under. - ''; - }; - }; -}