nix/modules/virtualisation/podman-pod.nix
2024-08-27 09:46:44 +02:00

173 lines
5 KiB
Nix

# Save this as podman-pod.nix
{
config,
lib,
pkgs,
...
}:
with lib;
let
cfg = config.services.podmanPods;
# Get the options from the original oci-containers module
containerOptions = (
filterAttrs (
n: v: n != "definition"
) config.virtualisation.oci-containers.containers.type.getSubOptions
);
# Add our enable option
extendedContainerOptions = containerOptions // {
enable = mkEnableOption "Enable this container";
};
podOptions =
{ name, config, ... }:
{
options = {
name = mkOption {
type = types.str;
description = "Name of the pod";
};
ports = mkOption {
type = types.listOf types.str;
default = [ ];
description = "List of port mappings (e.g. ['8080:80'])";
};
containers = mkOption {
type = types.attrsOf (types.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
);
in
{
options.services.podmanPods = mkOption {
type = types.attrsOf (types.submodule podOptions);
default = { };
description = "Podman pods to create";
};
config = mkIf (cfg != { }) {
virtualisation.podman.enable = true;
virtualisation.oci-containers.backend = "podman";
environment.systemPackages = [ pkgs.jq ];
virtualisation.oci-containers.containers = enabledContainers;
# 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.virtualisation.oci-containers.containers;
in
podServices // containerServices;
};
}