{ config, lib, pkgs, ... }: with lib; let cfg = config.my.virtualisation.podmanPods; # Get the options from the original oci-containers module # containerOptions = ( # filterAttrs ( # n: v: n != "definition" # ) config.virtualisation.oci-containers.containers.type.getSubOptions # ); # containerDefinition = mkOption { # type = types.attrs; # description = "The container definition, derived from virtualisation.oci-containers.containers."; # example = { # image = "nginx:latest"; # ports = [ "8080:80" ]; # }; # default = { }; # }; # containerDefinition = mkOption { # type = types.attrs; # description = "The container definition, derived from virtualisation.oci-containers.containers."; # example = { # image = "nginx:latest"; # ports = [ "8080:80" ]; # }; # default = {}; # }; # # config.virtualisation.oci-containers.containers.type.getSubOptions; containerDefinition = config.virtualisation.oci-containers.containers.type.getSubOptions; # Add our enable option extendedContainerOptions = containerDefinition // { enable = mkEnableOption "Enable this container"; }; podOptions = { options = with types; { name = mkOption { type = str; description = "Name of the pod"; }; ports = mkOption { type = listOf str; default = [ ]; description = "List of port mappings (e.g. ['8080:80'])"; }; containers = mkOption { type = attrsOf (submodule { options = extendedContainerOptions; }); default = { }; description = "Attribute set of OCI container configurations for this set"; }; }; }; createPodScript = name: podDef: let podDefinitionString = builtins.toJSON { inherit (podDef) ports; }; in pkgs.writeScript "manage-pod-${name}.sh" '' #! /bin/sh set -e POD_NAME="${name}" POD_DEFINITION="${podDefinitionString}" create_pod() { podman pod create --name "$POD_NAME" \ ${concatStringsSep " " (map (port: "--publish ${port}") podDef.ports)} } if podman pod exists "$POD_NAME"; then CURRENT_CONFIG=$(podman pod inspect "$POD_NAME" | jq -c '{ports: .[0].InfraConfig.PortBindings | to_entries | map("\(.value[0].HostPort):\(.key | split("/")[0])") | sort'}) echo "POD_DEFINITION: $POD_DEFINITION" echo "CURRENT_CONFIG: $CURRENT_CONFIG" if [ "$CURRENT_CONFIG" != "$POD_DEFINITION" ]; then echo "Pod configuration has changed. Recreating pod..." podman pod rm -f "$POD_NAME" create_pod else echo "Pod configuration unchanged." fi else echo "Pod does not exist. Creating..." create_pod fi ''; # enabledContainers = lib.flatten ( # mapAttrs ( # podName: podConfig: filterAttrs (name: value: value.enable or true) podConfig.containers # ) cfg # ); # flattenPodContainers = # pods: # mapAttrs' ( # podName: pod: # mapAttrs' ( # containerName: container: # nameValuePair "${podName}-${containerName}" ( # container # # // { # # inherit (pod) name; # # extraOptions = (container.extraOptions or [ ]) ++ [ "--pod=${pod.name}" ]; # # } # ) # ) (filterAttrs (n: v: v.enable or true) pod.containers) # ) pods; in { options.my.virtualisation.podmanPods = mkOption { type = types.attrsOf (types.submodule podOptions); default = { }; description = "Podman pods to create"; }; config = mkIf (cfg != { }) { my.virtualisation.podman.enable = true; environment.systemPackages = [ pkgs.jq ]; # virtualisation.oci-containers.containers = enabledContainers; # virtualisation.oci-containers.containers = flattenPodContainers cfg; # virtualisation.oci-containers.containers = lib.mkMerge ( # map (entry: entry.container) (filter (entry: entry.enable) cfg.containers) # ); # virtualisation.oci-containers.containers = listToAttrs (flatten (mapAttrsToList # (podName: podDef: # mapAttrs # (containerName: containerDef: # filterAttrs (attrName: attrValue: attrName != "enable") containerDef # # nameValuePair "${podName}-${containerName}" ({ # # inherit (containerDef) image; # # extraOptions = [ "--pod=${podName}" ] ++ containerDef.extraOptions; # # }) # ) # podDef.containers # ) # cfg)); # virtualisation.oci-containers.containers = lib.flatten # (mapAttrs # (podName: podConfig: # mapAttrs # (containerName: containerConfig: # filterAttrs (attrName: attrValue: attrName != "enable") containerConfig # ) # (filterAttrs (name: value: value.enable) podConfig.containers) # ) # (filterAttrs (podName: podConfig: podConfig.enable) cfg) # ); # mapAttrs # (name: value: # filterAttrs (n: v: n != "enable") value # ) # (filterAttrs (name: value: value.enable) cfg.containers); networking.firewall.allowedTCPPorts = flatten ( mapAttrsToList ( name: podDef: map (portMapping: lib.toInt (lib.head (lib.splitString ":" portMapping))) podDef.ports ) cfg ); systemd.services = let podServices = mapAttrs' ( name: podDef: nameValuePair "podman-pod-${name}" { description = "Manage Podman pod: ${name}"; serviceConfig = { Type = "oneshot"; ExecStart = "${createPodScript name podDef}"; }; path = [ pkgs.jq pkgs.podman ]; after = [ "network.target" ]; wantedBy = [ "multi-user.target" ]; } ) cfg; containerServices = mapAttrs' ( name: container: nameValuePair "podman-${name}" { after = [ "podman-pod-${lib.head (lib.splitString "-" name)}.service" ]; requires = [ "podman-pod-${lib.head (lib.splitString "-" name)}.service" ]; partOf = [ "podman-pod-${lib.head (lib.splitString "-" name)}.service" ]; } ) config.containers; in podServices // containerServices; }; }