1
0
Fork 0
mirror of https://github.com/nix-community/home-manager.git synced 2025-03-31 04:04:32 +00:00

podman: link dependent quadlets during build

podman's systemd generator can automatically resolve unit dependencies, so instead of us guessing these links to create them, we provide the sources during generation
This commit is contained in:
Nicholas Hassan 2025-02-13 14:17:16 +10:30 committed by Austin Horstman
parent 4108ec3aa8
commit 81bf639da7
10 changed files with 96 additions and 91 deletions

View file

@ -7,7 +7,7 @@ let
createQuadletSource = name: buildDef:
let
buildConfig = podman-lib.deepMerge {
quadlet = podman-lib.deepMerge {
Build = {
AuthFile = buildDef.authFile;
Environment = buildDef.environment;
@ -30,21 +30,26 @@ let
};
Unit = { Description = buildDef.description; };
} buildDef.extraConfig;
in ''
# Automatically generated by home-manager for podman build configuration
# DO NOT EDIT THIS FILE DIRECTLY
#
# ${name}.build
${podman-lib.toQuadletIni buildConfig}
'';
in {
attrs = quadlet;
text = ''
# Automatically generated by home-manager for podman build configuration
# DO NOT EDIT THIS FILE DIRECTLY
#
# ${name}.build
${podman-lib.toQuadletIni quadlet}
'';
};
toQuadletInternal = name: buildDef: {
assertions = podman-lib.buildConfigAsserts name buildDef.extraConfig;
serviceName =
"podman-${name}"; # quadlet service name: 'podman-<name>-build.service
source = podman-lib.removeBlankLines (createQuadletSource name buildDef);
resourceType = "build";
};
toQuadletInternal = name: buildDef:
let src = createQuadletSource name buildDef;
in {
assertions = podman-lib.buildConfigAsserts name buildDef.extraConfig;
serviceName =
"podman-${name}"; # generated service name: 'podman-<name>-build.service
source = podman-lib.removeBlankLines src.text;
resourceType = "build";
};
in let
buildDefinitionType = types.submodule ({ name, ... }: {
options = {

View file

@ -9,20 +9,10 @@ let
createQuadletSource = name: containerDef:
let
# formatServiceNameForType = type: name:
# {
# image = "${name}-image.service";
# build = "${name}-build.service";
# network = "${name}-network.service";
# volume = "${name}-volume.service";
# }."${type}";
dependencyBySuffix = type: name:
if (hasInfix ".${type}" name) then
let
baseName = elemAt (splitString ".${type}" name) 0;
in
if (hasAttr (builtins.trace (baseName) baseName) cfg.internal.builtQuadlets) then
let baseName = elemAt (splitString ".${type}" name) 0;
in if (hasAttr baseName cfg.internal.builtQuadlets) then
[ (cfg.internal.builtQuadlets.${baseName}) ]
else
[ ]
@ -30,25 +20,34 @@ let
[ ];
withResolverFor = type: value:
let
resolve = v: dependencyBySuffix type v;
in
if builtins.isList value
then builtins.concatLists (map resolve value) # Flatten list of lists
else resolve value;
dependencyServices = (withResolverFor "image" containerDef.image) ++
(withResolverFor "build" containerDef.image) ++
(withResolverFor "network" containerDef.network) ++
(withResolverFor "volume" containerDef.volumes);
getActualImage =
if (builtins.hasAttr containerDef.image cfg.images) then
cfg.images."${containerDef.image}".image
else if (builtins.hasAttr containerDef.image cfg.builds) then
"localhost/homemanager/${containerDef.image}"
let resolve = v: dependencyBySuffix type v;
in if builtins.isList value then
builtins.concatLists (map resolve value) # Flatten list of lists
else
containerDef.image;
resolve value;
dependencyServices = (withResolverFor "image" containerDef.image)
++ (withResolverFor "build" containerDef.image)
++ (withResolverFor "network" containerDef.network)
++ (withResolverFor "volume" containerDef.volumes);
checkQuadletReference = types: value:
if builtins.isList value then
builtins.concatLists (map (checkQuadletReference types) value)
else
let type = findFirst (t: hasInfix ".${t}" value) null types;
in if (type != null) then
let
baseName = elemAt (splitString ".${type}" value) 0;
quadletsOfType =
filterAttrs (n: v: v.quadletData.resourceType == type)
cfg.internal.builtQuadlets;
in if (hasAttr baseName quadletsOfType) then
[ (replaceStrings [ baseName ] [ "podman-${baseName}" ] value) ]
else
[ value ]
else
[ value ];
quadlet = (podman-lib.deepMerge {
Container = {
@ -62,18 +61,18 @@ let
EnvironmentFile = containerDef.environmentFile;
Exec = containerDef.exec;
Group = containerDef.group;
Image = resolvedImage;
Image = checkQuadletReference [ "build" "image" ] containerDef.image;
IP = containerDef.ip4;
IP6 = containerDef.ip6;
Label =
(containerDef.labels // { "nix.home-manager.managed" = true; });
Network = containerDef.network;
Network = checkQuadletReference [ "network" ] containerDef.network;
NetworkAlias = containerDef.networkAlias;
PodmanArgs = containerDef.extraPodmanArgs;
PublishPort = containerDef.ports;
UserNS = containerDef.userNS;
User = containerDef.user;
Volume = containerDef.volumes;
Volume = checkQuadletReference [ "volume" ] containerDef.volumes;
};
Install = {
WantedBy = optionals containerDef.autoStart [
@ -99,9 +98,9 @@ let
"Service for container ${name}");
};
} containerDef.extraConfig);
in
{
in {
dependencies = dependencyServices;
attrs = quadlet;
text = ''
# Automatically generated by home-manager podman container configuration
# DO NOT EDIT THIS FILE DIRECTLY
@ -111,17 +110,16 @@ let
'';
};
toQuadletInternal = name: containerDef: let
quadletSrc = createQuadletSource name containerDef;
in {
assertions = podman-lib.buildConfigAsserts name containerDef.extraConfig;
dependencies = quadletSrc.dependencies;
resourceType = "container";
serviceName =
"podman-${name}"; # quadlet service name: 'podman-<name>.service'
source =
podman-lib.removeBlankLines quadletSrc.text;
};
toQuadletInternal = name: containerDef:
let src = createQuadletSource name containerDef;
in {
assertions = podman-lib.buildConfigAsserts name containerDef.extraConfig;
dependencies = src.dependencies;
resourceType = "container";
serviceName =
"podman-${src.attrs.Container.ContainerName}"; # generated service name: 'podman-<name>.service'
source = podman-lib.removeBlankLines src.text;
};
# Define the container user type as the user interface
containerDefinitionType = types.submodule {

View file

@ -11,7 +11,7 @@ let
(if imageDef.username != null then imageDef.username else "")
+ (if imageDef.password != null then ":${imageDef.password}" else "");
imageConfig = podman-lib.deepMerge {
quadlet = podman-lib.deepMerge {
Image = {
AuthFile = imageDef.authFile;
CertDir = imageDef.certDir;
@ -40,13 +40,13 @@ let
# DO NOT EDIT THIS FILE DIRECTLY
#
# ${name}.image
${podman-lib.toQuadletIni imageConfig}
${podman-lib.toQuadletIni quadlet}
'';
toQuadletInternal = name: imageDef: {
assertions = podman-lib.buildConfigAsserts name imageDef.extraConfig;
serviceName =
"podman-${name}"; # quadlet service name: 'podman-<name>-image.service
"podman-${name}"; # generated service name: 'podman-<name>-image.service
source = podman-lib.removeBlankLines (createQuadletSource name imageDef);
resourceType = "image";
};

View file

@ -15,13 +15,13 @@ let
pkgs.stdenv.mkDerivation {
name = "home-${quadlet.resourceType}-${quadlet.serviceName}";
buildInputs = [ cfg.package ];
# dontUnpack = true;
buildInputs = [ cfg.package ] ++ quadlet.dependencies;
unpackPhase = ''
mkdir -p $out/quadlets
${concatStringsSep "\n" (map (v: "cp ${v.out}/quadlets/${v.quadletData.serviceName}.${v.quadletData.resourceType} $out/quadlets") quadlet.dependencies)}
${concatStringsSep "\n" (map (v:
"ln -s ${v.out}/quadlets/${v.quadletData.serviceName}.${v.quadletData.resourceType} $out/quadlets")
quadlet.dependencies)}
'';
installPhase = ''
@ -42,7 +42,6 @@ let
};
};
# Create a derivation for each quadlet spec
builtQuadlets = map buildPodmanQuadlet cfg.internal.quadletDefinitions;
accumulateUnitFiles = prefix: path: quadlet:
@ -87,6 +86,9 @@ in {
lib.mkIf (lib.length builtQuadlets >= 1)
(lib.hm.dag.entryAfter [ "reloadSystemd" ] activationCleanupScript);
services.podman.internal.builtQuadlets = listToAttrs (map (pkg: { name = removePrefix "podman-" pkg.passthru.quadletData.serviceName; value = pkg; }) builtQuadlets);
services.podman.internal.builtQuadlets = listToAttrs (map (pkg: {
name = removePrefix "podman-" pkg.passthru.quadletData.serviceName;
value = pkg;
}) builtQuadlets);
};
}

View file

@ -9,7 +9,7 @@ let
createQuadletSource = name: networkDef:
let
cfg = (podman-lib.deepMerge {
quadlet = (podman-lib.deepMerge {
Install = {
WantedBy = (if networkDef.autoStart then [
"default.target"
@ -50,13 +50,13 @@ let
# DO NOT EDIT THIS FILE DIRECTLY
#
# ${name}.network
${podman-lib.toQuadletIni cfg}
${podman-lib.toQuadletIni quadlet}
'';
toQuadletInternal = name: networkDef: {
assertions = podman-lib.buildConfigAsserts name networkDef.extraConfig;
serviceName =
"podman-${name}"; # quadlet service name: 'podman-<name>-network.service'
"podman-${name}"; # generated service name: 'podman-<name>-network.service'
source = podman-lib.removeBlankLines (createQuadletSource name networkDef);
resourceType = "network";
};

View file

@ -9,7 +9,7 @@ let
createQuadletSource = name: volumeDef:
let
volumeConfig = podman-lib.deepMerge {
quadlet = podman-lib.deepMerge {
Install = {
WantedBy = optionals volumeDef.autoStart [
"default.target"
@ -49,13 +49,13 @@ let
# DO NOT EDIT THIS FILE DIRECTLY
#
# ${name}.volume
${podman-lib.toQuadletIni volumeConfig}
${podman-lib.toQuadletIni quadlet}
'';
toQuadletInternal = name: volumeDef: {
assertions = podman-lib.buildConfigAsserts name volumeDef.extraConfig;
serviceName =
"podman-${name}"; # quadlet service name: 'podman-<name>-volume.service'
"podman-${name}"; # generated service name: 'podman-<name>-volume.service'
source = podman-lib.removeBlankLines (createQuadletSource name volumeDef);
resourceType = "volume";
};

View file

@ -27,4 +27,4 @@ Wants=podman-user-wait-network-online.service
After=podman-user-wait-network-online.service
Description=Service for build my-bld
RequiresMountsFor=%t/containers
SourcePath=/nix/store/00000000000000000000000000000000-home-build-podman-my-bld/quadlets/podman-my-bld.build
SourcePath=/nix/store/00000000000000000000000000000000-home-container-podman-my-container-bld/quadlets/podman-my-bld.build

View file

@ -7,7 +7,7 @@
[X-Container]
ContainerName=my-container-bld
Environment=
Image=localhost/homemanager/my-bld
Image=podman-my-bld.build
Label=nix.home-manager.managed=true
[Install]
@ -26,13 +26,13 @@ Delegate=yes
Type=notify
NotifyAccess=all
SyslogIdentifier=%N
ExecStart=/nix/store/00000000000000000000000000000000-podman/bin/podman run --name my-container-bld --cidfile=%t/%N.cid --replace --rm --cgroups=split --sdnotify=conmon -d --label nix.home-manager.managed=true localhost/homemanager/my-bld
ExecStart=/nix/store/00000000000000000000000000000000-podman/bin/podman run --name my-container-bld --cidfile=%t/%N.cid --replace --rm --cgroups=split --sdnotify=conmon -d --label nix.home-manager.managed=true homemanager/my-bld
[Unit]
Wants=podman-user-wait-network-online.service
After=podman-user-wait-network-online.service
After=podman-my-bld-build.service
Description=Service for container my-container-bld
Requires=podman-my-bld-build.service
SourcePath=/nix/store/00000000000000000000000000000000-home-container-podman-my-container-bld/quadlets/podman-my-container-bld.container
Requires=podman-my-bld-build.service
After=podman-my-bld-build.service
RequiresMountsFor=%t/containers

View file

@ -7,11 +7,11 @@
[X-Container]
ContainerName=my-container
Environment=
Image=docker.io/alpine:latest
Image=podman-my-img.image
Label=nix.home-manager.managed=true
Network=my-net
Network=podman-my-net.network
Network=externalnet
Volume=my-vol:/data
Volume=podman-my-vol.volume:/data
[Install]
WantedBy=default.target
@ -34,12 +34,12 @@ ExecStart=/nix/store/00000000000000000000000000000000-podman/bin/podman run --na
[Unit]
Wants=podman-user-wait-network-online.service
After=podman-user-wait-network-online.service
After=podman-my-img-image.service
After=podman-my-net-network.service
After=podman-my-vol-volume.service
Description=Service for container my-container
Requires=podman-my-img-image.service
Requires=podman-my-net-network.service
Requires=podman-my-vol-volume.service
SourcePath=/nix/store/00000000000000000000000000000000-home-container-podman-my-container/quadlets/podman-my-container.container
Requires=podman-my-img-image.service
After=podman-my-img-image.service
RequiresMountsFor=%t/containers
Requires=podman-my-net-network.service
After=podman-my-net-network.service
Requires=podman-my-vol-volume.service
After=podman-my-vol-volume.service

View file

@ -21,7 +21,7 @@
network = [ "my-net.network" "externalnet" ];
volumes = [ "my-vol.volume:/data" ];
};
"my-container-bld" = { image = "my-bld"; };
"my-container-bld" = { image = "my-bld.build"; };
};
images."my-img" = { image = "docker.io/alpine:latest"; };
networks."my-net" = {