1
0
Fork 0
mirror of https://github.com/nix-community/home-manager synced 2025-01-30 21:05:02 +01:00

podman: added volume, image, and build quadlets

This commit is contained in:
Dylan Wilson 2024-11-23 12:46:13 -05:00
parent f8ef4541bb
commit cc9881c1fb
No known key found for this signature in database
21 changed files with 996 additions and 32 deletions

View file

@ -18,6 +18,7 @@
local manifestFile="${config.xdg.configHome}/podman/$2"
local extraListCommands="''${3:-}"
[[ $resourceType = "container" ]] && extraListCommands+=" -a"
[[ $resourceType = "volume" ]] && extraListCommands+=" --filter label=nix.home-manager.preserve=false"
[ ! -f "$manifestFile" ] && VERBOSE_ENABLED && echo "Manifest does not exist: $manifestFile" && return 0
@ -69,19 +70,20 @@
commands=()
case "$resourceType" in
"container")
commands+="${config.services.podman.package}/bin/podman $resourceType stop $resource"
commands+="${config.services.podman.package}/bin/podman $resourceType rm -f $resource"
commands+=("${config.services.podman.package}/bin/podman $resourceType stop $resource")
commands+=("${config.services.podman.package}/bin/podman $resourceType rm -f $resource")
;;
"network")
commands+="${config.services.podman.package}/bin/podman $resourceType rm $resource"
"image" | "network" | "volume")
commands+=("${config.services.podman.package}/bin/podman $resourceType rm $resource")
;;
esac
for command in "''${commands[@]}"; do
command=$(echo $command | tr -d ';&|`')
DRYRUN_ENABLED && echo "Would run: $command" && continue || true
VERBOSE_ENABLED && echo "Running: $command" || true
if [[ "$(eval "$command")" != "$resource" ]]; then
if [[ "$(eval "$command")" != *"$resource" ]]; then
echo -e "\tCommand failed: ''${command}"
[ "$resourceType" == "image" ] && resourceType="ancestor"
usedByContainers=$(${config.services.podman.package}/bin/podman container ls -a --filter "$resourceType=$resource" --format "{{.Names}}")
echo -e "\t$resource in use by containers: $usedByContainers"
fi
@ -92,7 +94,7 @@
[[ "$@" == *"--verbose"* ]] && VERBOSE="true"
[[ "$@" == *"--dry-run"* ]] && DRY_RUN="true"
for type in "container" "network"; do
for type in "container" "image" "network" "volume"; do
cleanup "$type" "''${type}s.manifest"
done
'';

View file

@ -0,0 +1,168 @@
{ config, lib, pkgs, ... }:
with lib;
let
cfg = config.services.podman;
podman-lib = import ./podman-lib.nix { inherit lib config; };
createQuadletSource = name: buildDef:
let
buildConfig = podman-lib.deepMerge {
Build = {
AuthFile = buildDef.authFile;
Environment = buildDef.environment;
File = buildDef.file;
ImageTag = [ "homemanager/${name}" ] ++ buildDef.tags;
Label = buildDef.labels // { "nix.home-manager.managed" = true; };
PodmanArgs = buildDef.extraPodmanArgs;
SetWorkingDirectory = buildDef.workingDirectory;
TLSVerify = buildDef.tlsVerify;
};
Install = {
WantedBy = optionals buildDef.autoStart [
"default.target"
"multi-user.target"
];
};
Service = {
TimeoutStartSec = 300;
RemainAfterExit = "yes";
};
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}
'';
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";
};
in let
buildDefinitionType = types.submodule ({ name, ... }: {
options = {
autoStart = mkOption {
type = types.bool;
default = true;
description =
"Whether to start the build on boot. Requires user lingering.";
};
authFile = mkOption {
type = with types; nullOr path;
default = null;
description = "Path of the authentication file.";
};
description = mkOption {
type = with types; nullOr str;
default = "Service for build ${name}";
defaultText = "Service for build \${name}";
example = "My Build";
description = "The description of the build.";
};
environment = mkOption {
type = podman-lib.primitiveAttrs;
default = { };
example = literalExpression ''
{
VAR1 = "0:100";
VAR2 = true;
VAR3 = 5;
}
'';
description = "Environment variables to set in the build.";
};
extraConfig = mkOption {
type = podman-lib.extraConfigType;
default = { };
example = literalExpression ''
{
Build = {
Arch = "aarch64";
};
Service = {
TimeoutStartSec = 15;
};
}
'';
description = "INI sections and values to populate the Build Quadlet.";
};
extraPodmanArgs = mkOption {
type = types.listOf types.str;
default = [ ];
example = [ "--retries 5" ];
description = "Extra arguments to pass to the podman build command.";
};
file = mkOption {
type = types.str;
example = literalExpression ''
`"xdg.configFile."containerfiles/my-img/Containerfile"`
or
`"https://github.com/.../my-img/Containerfile"`
'';
description =
"Path to a Containerfile which contains instructions to build the image.";
};
tags = mkOption {
type = with types; listOf str;
default = [ ];
description = ''
Name associated with the build.
First tag will always be "homemanager/<name>".
'';
};
labels = mkOption {
type = with types; attrsOf str;
default = { };
example = {
app = "myapp";
some-label = "somelabel";
};
description = "The labels to apply to the build.";
};
tlsVerify = mkOption {
type = types.bool;
default = true;
description =
"Require HTTPS and verification of certificates when contacting registries.";
};
workingDirectory = mkOption {
type = with types; nullOr path;
default = null;
description = "WorkingDirectory of the systemd unit file.";
};
};
});
in {
options.services.podman.builds = mkOption {
type = types.attrsOf buildDefinitionType;
default = { };
description = "Defines Podman build quadlet configurations.";
};
config = let buildQuadlets = mapAttrsToList toQuadletInternal cfg.builds;
in mkIf cfg.enable {
services.podman.internal.quadletDefinitions = buildQuadlets;
assertions = flatten (map (build: build.assertions) buildQuadlets);
xdg.configFile."/podman/images.manifest".text =
podman-lib.generateManifestText buildQuadlets;
};
}

View file

@ -9,19 +9,66 @@ let
createQuadletSource = name: containerDef:
let
mapHmNetworks = network:
if builtins.hasAttr network cfg.networks then
"podman-${network}-network.service"
else
null;
makeServiceName = name: type:
let
typeName = (if lib.strings.hasSuffix "s" type then
(lib.strings.substring 0 (builtins.stringLength type - 1) type)
else
type);
in "podman-${name}-${typeName}.service";
extractVolumeName = resource: builtins.head (builtins.split ":" resource);
standardizeResource = res:
let
lst = if lib.isList res then
res
else if res != null then
[ res ]
else
[ ];
in map extractVolumeName lst;
filterManagedResources = resource: definedResources:
builtins.filter (x: builtins.elem x definedResources)
(standardizeResource resource);
getDefinedResourceNames = type:
let
typeName =
if lib.strings.hasSuffix "s" type then type else "${type}s";
in builtins.attrNames cfg."${typeName}";
findDefinedImageType = if (builtins.elem containerDef.image
(getDefinedResourceNames "builds")) then
"builds"
else
"images";
getManagedResourceNames = type:
let
resourceType = if type == "image" then findDefinedImageType else type;
in (filterManagedResources (builtins.getAttr type containerDef)
(getDefinedResourceNames resourceType));
getServiceNames = type:
let
resourceType = if type == "image" then findDefinedImageType else type;
in map (name: makeServiceName name resourceType)
(getManagedResourceNames type);
finalConfig = let
managedNetworks = if lib.isList containerDef.network then
map mapHmNetworks containerDef.network
else if containerDef.network != null then
map mapHmNetworks [ containerDef.network ]
else
[ ];
managedServices = builtins.concatLists
(map (type: getServiceNames type) [ "image" "network" "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}"
else
containerDef.image;
in (podman-lib.deepMerge {
Container = {
AddCapability = containerDef.addCapabilities;
@ -34,7 +81,7 @@ let
EnvironmentFile = containerDef.environmentFile;
Exec = containerDef.exec;
Group = containerDef.group;
Image = containerDef.image;
Image = getActualImage;
IP = containerDef.ip4;
IP6 = containerDef.ip6;
Label =
@ -66,8 +113,8 @@ let
TimeoutStopSec = 30;
};
Unit = {
After = [ "network.target" ] ++ managedNetworks;
Requires = managedNetworks;
After = [ "network.target" ] ++ managedServices;
Requires = managedServices;
Description = (if (builtins.isString containerDef.description) then
containerDef.description
else

View file

@ -5,8 +5,15 @@ let
in {
meta.maintainers = with lib.hm.maintainers; [ bamhm182 n-hass ];
imports =
[ ./containers.nix ./install-quadlet.nix ./networks.nix ./services.nix ];
imports = [
./builds.nix
./containers.nix
./images.nix
./install-quadlet.nix
./networks.nix
./services.nix
./volumes.nix
];
options.services.podman = {
enable = lib.mkEnableOption "Podman, a daemonless container engine";

View file

@ -0,0 +1,168 @@
{ config, lib, pkgs, ... }:
with lib;
let
cfg = config.services.podman;
podman-lib = import ./podman-lib.nix { inherit lib config; };
awaitPodmanUnshare = pkgs.writeShellScript "await-podman-unshare" ''
until ${cfg.package}/bin/podman unshare ${pkgs.coreutils}/bin/true; do
${pkgs.coreutils}/bin/sleep 1
done
'';
createQuadletSource = name: imageDef:
let
credsString =
(if imageDef.username != null then imageDef.username else "")
+ (if imageDef.password != null then ":${imageDef.password}" else "");
imageConfig = podman-lib.deepMerge {
Image = {
AuthFile = imageDef.authFile;
CertDir = imageDef.certDir;
Creds = (if credsString != "" then credsString else null);
DecryptionKey = imageDef.decryptionKeyFile;
Image = imageDef.image;
ImageTag = imageDef.tag;
PodmanArgs = imageDef.extraPodmanArgs;
TLSVerify = imageDef.tlsVerify;
};
Install = {
WantedBy = optionals imageDef.autoStart [
"default.target"
"multi-user.target"
];
};
Service = {
ExecStartPre = [ "${awaitPodmanUnshare}" ];
TimeoutStartSec = 300;
RemainAfterExit = "yes";
};
Unit = { Description = imageDef.description; };
} imageDef.extraConfig;
in ''
# Automatically generated by home-manager for podman image configuration
# DO NOT EDIT THIS FILE DIRECTLY
#
# ${name}.image
${podman-lib.toQuadletIni imageConfig}
'';
toQuadletInternal = name: imageDef: {
assertions = podman-lib.buildConfigAsserts name imageDef.extraConfig;
serviceName =
"podman-${name}"; # quadlet service name: 'podman-<name>-image.service
source = podman-lib.removeBlankLines (createQuadletSource name imageDef);
resourceType = "image";
};
in let
imageDefinitionType = types.submodule ({ name, ... }: {
options = {
autoStart = mkOption {
type = types.bool;
default = true;
description =
"Whether to pull the image on boot. Requires user lingering.";
};
authFile = mkOption {
type = with types; nullOr path;
default = null;
description =
"Path of the authentication file used to connect to registry.";
};
certDir = mkOption {
type = with types; nullOr path;
default = null;
description =
"Path of certificates (*.{crt,cert,key}) used to connect to registry.";
};
decryptionKeyFile = mkOption {
type = with types; nullOr path;
default = null;
description = "Path to key used for decrpytion of images.";
};
description = mkOption {
type = with types; nullOr str;
default = "Service for image ${name}";
defaultText = "Service for image \${name}";
example = "My Image";
description = "The description of the image.";
};
extraConfig = mkOption {
type = podman-lib.extraConfigType;
default = { };
example = literalExpression ''
{
Image = {
ContainersConfModule = "/etc/nvd.conf";
};
}
'';
description = "INI sections and values to populate the Image Quadlet.";
};
extraPodmanArgs = mkOption {
type = with types; listOf str;
default = [ ];
example = [ "--os=linux" ];
description =
"Extra arguments to pass to the podman image pull command.";
};
image = mkOption {
type = types.str;
example = "quay.io/centos/centos:latest";
description = "Image to pull.";
};
password = mkOption {
type = with types; nullOr str;
default = null;
example = "P@ssw0rd";
description =
"Password used to connect to registry. (Will be visible in nix store)";
};
tag = mkOption {
type = with types; nullOr str;
default = null;
example = "quay.io/centos/centos:latest";
description =
"FQIN of referenced Image when source is a file or directory archive.";
};
tlsVerify = mkOption {
type = types.bool;
default = true;
description =
"Require HTTPS and verification of certificates when contacting registries.";
};
username = mkOption {
type = with types; nullOr str;
default = null;
example = "bob";
description = "Username used to connect to registry.";
};
};
});
in {
options.services.podman.images = mkOption {
type = types.attrsOf imageDefinitionType;
default = { };
description = "Defines Podman image quadlet configurations.";
};
config = let imageQuadlets = mapAttrsToList toQuadletInternal cfg.images;
in mkIf cfg.enable {
services.podman.internal.quadletDefinitions = imageQuadlets;
assertions = flatten (map (image: image.assertions) imageQuadlets);
};
}

View file

@ -63,8 +63,10 @@ in {
buildConfigAsserts = quadletName: extraConfig:
let
configRules = {
Build = { ImageTag = (with types; listOf str); };
Container = { ContainerName = types.enum [ quadletName ]; };
Network = { NetworkName = types.enum [ quadletName ]; };
Volume = { VolumeName = types.enum [ quadletName ]; };
};
# Function to build assertions for a specific section and its attributes.
@ -83,8 +85,23 @@ in {
else
[ ];
checkImageTag = extraConfig:
let
imageTags = (extraConfig.Build or { }).ImageTag or [ ];
containsRequiredTag =
builtins.elem "homemanager/${quadletName}" imageTags;
imageTagsStr = concatMapStringsSep ''" "'' toString imageTags;
in [{
assertion = imageTags == [ ] || containsRequiredTag;
message = ''
In '${quadletName}' config. Build.ImageTag: '[ "${imageTagsStr}" ]' does not contain 'homemanager/${quadletName}'.'';
}];
# Flatten assertions from all sections in `extraConfig`.
in flatten (mapAttrsToList buildSectionAsserts extraConfig);
in flatten (concatLists [
(mapAttrsToList buildSectionAsserts extraConfig)
(checkImageTag extraConfig)
]);
extraConfigType = with types;
attrsOf (attrsOf (oneOf [ primitiveAttrs primitiveList primitive ]));
@ -107,8 +124,11 @@ in {
# specific logic for writing the unit name goes here. It should be
# identical to what `podman <resource> ls` shows
in {
"build" = strippedName;
"container" = strippedName;
"image" = strippedName;
"network" = strippedName;
"volume" = strippedName;
}."${quadlet.resourceType}";
in if allQuadletsSameType then ''
${concatStringsSep "\n"

View file

@ -42,8 +42,8 @@ in {
"${config.home.homeDirectory}/.nix-profile/bin"
]
}";
ExecStart = "${pkgs.podman}/bin/podman auto-update";
ExecStartPost = "${pkgs.podman}/bin/podman image prune -f";
ExecStart = "${cfg.package}/bin/podman auto-update";
ExecStartPost = "${cfg.package}/bin/podman image prune -f";
TimeoutStartSec = "300s";
TimeoutStopSec = "10s";
};

View file

@ -0,0 +1,192 @@
{ config, lib, pkgs, ... }:
with lib;
let
cfg = config.services.podman;
podman-lib = import ./podman-lib.nix { inherit lib config; };
awaitPodmanUnshare = pkgs.writeShellScript "await-podman-unshare" ''
until ${cfg.package}/bin/podman unshare ${pkgs.coreutils}/bin/true; do
${pkgs.coreutils}/bin/sleep 1
done
'';
createQuadletSource = name: volumeDef:
let
volumeConfig = podman-lib.deepMerge {
Install = {
WantedBy = optionals volumeDef.autoStart [
"default.target"
"multi-user.target"
];
};
Service = {
Environment = {
PATH = (builtins.concatStringsSep ":" [
"${podman-lib.newuidmapPaths}"
"${makeBinPath [ pkgs.su pkgs.coreutils ]}"
]);
};
ExecStartPre = [ "${awaitPodmanUnshare}" ];
TimeoutStartSec = 15;
RemainAfterExit = "yes";
};
Unit = { Description = volumeDef.description; };
Volume = {
Copy = volumeDef.copy;
Device = volumeDef.device;
Driver = volumeDef.driver;
Group = volumeDef.group;
Image = volumeDef.image;
Label = volumeDef.labels // {
"nix.home-manager.managed" = true;
"nix.home-manager.preserve" = volumeDef.preserve;
};
PodmanArgs = volumeDef.extraPodmanArgs;
Type = volumeDef.type;
User = volumeDef.user;
VolumeName = name;
};
} volumeDef.extraConfig;
in ''
# Automatically generated by home-manager for podman volume configuration
# DO NOT EDIT THIS FILE DIRECTLY
#
# ${name}.volume
${podman-lib.toQuadletIni volumeConfig}
'';
toQuadletInternal = name: volumeDef: {
assertions = podman-lib.buildConfigAsserts name volumeDef.extraConfig;
serviceName =
"podman-${name}"; # quadlet service name: 'podman-<name>-volume.service'
source = podman-lib.removeBlankLines (createQuadletSource name volumeDef);
resourceType = "volume";
};
in let
volumeDefinitionType = types.submodule ({ name, ... }: {
options = {
autoStart = mkOption {
type = types.bool;
default = true;
description = "Whether to create the volume on boot.";
};
copy = mkOption {
type = types.bool;
default = true;
description =
"Copy content of the image located at the mountpoint of the volume on first run.";
};
description = mkOption {
type = with types; nullOr str;
default = "Service for volume ${name}";
defaultText = "Service for volume \${name}";
example = "My Volume";
description = "The description of the volume.";
};
device = mkOption {
type = with types; nullOr str;
default = null;
example = "tmpfs";
description = "The path of a device which is mounted for the volume.";
};
driver = mkOption {
type = with types; nullOr str;
default = null;
example = "image";
description = "The volume driver to use.";
};
extraConfig = mkOption {
type = podman-lib.extraConfigType;
default = { };
example = literalExpression ''
{
Volume = {
ContainerConfModule = "/etc/nvd.conf";
};
}
'';
description = "INI sections and values to populate the Volume Quadlet.";
};
extraPodmanArgs = mkOption {
type = with types; listOf str;
default = [ ];
example = [ "--opt copy" ];
description =
"Extra arguments to pass to the podman volume create command.";
};
group = mkOption {
type = with types; nullOr (either int str);
default = null;
description = "The group ID owning the volume inside the container.";
};
image = mkOption {
type = with types; nullOr str;
default = null;
example = "quay.io/centos/centos:latest";
description =
"Specifies the image the volume is based on when Driver is set to the image.";
};
labels = mkOption {
type = with types; attrsOf str;
default = { };
example = {
app = "myapp";
some-label = "somelabel";
};
description = "The labels to apply to the volume.";
};
preserve = mkOption {
type = types.bool;
default = true;
description = ''
Whether the volume should be preserved if it is removed from the configuration.
Setting this to false will cause the volume to be deleted if the volume is removed from the configuration
'';
};
type = mkOption {
type = with types; nullOr str;
default = null;
example = "tmpfs";
description =
"Filesystem type of Device. (used as -t in mount commands)";
};
user = mkOption {
type = with types; nullOr (either int str);
default = null;
description = "The user ID owning the volume inside the container.";
};
};
});
in {
options.services.podman.volumes = mkOption {
type = types.attrsOf volumeDefinitionType;
default = { };
description = "Defines Podman volume quadlet configurations.";
};
config = let volumeQuadlets = mapAttrsToList toQuadletInternal cfg.volumes;
in mkIf cfg.enable {
services.podman.internal.quadletDefinitions = volumeQuadlets;
assertions = flatten (map (volume: volume.assertions) volumeQuadlets);
xdg.configFile."/podman/volumes.manifest".text =
podman-lib.generateManifestText volumeQuadlets;
};
}

View file

@ -0,0 +1,30 @@
# Automatically generated by /nix/store/00000000000000000000000000000000-podman/lib/systemd/user-generators/podman-user-generator
#
# Automatically generated by home-manager for podman build configuration
# DO NOT EDIT THIS FILE DIRECTLY
#
# my-bld.build
[X-Build]
Environment=
File=/nix/store/00000000000000000000000000000000-Containerfile
ImageTag=homemanager/my-bld
Label=nix.home-manager.managed=true
TLSVerify=true
[Install]
WantedBy=default.target
WantedBy=multi-user.target
[Service]
RemainAfterExit=yes
TimeoutStartSec=300
ExecStart=/nix/store/00000000000000000000000000000000-podman/bin/podman build --tls-verify --tag homemanager/my-bld --label nix.home-manager.managed=true --file /nix/store/00000000000000000000000000000000-Containerfile
SyslogIdentifier=%N
Type=oneshot
[Unit]
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

View file

@ -0,0 +1,40 @@
{ pkgs, ... }:
let
containerFile = pkgs.writeTextFile {
name = "Containerfile";
text = ''
FROM docker.io/alpine:latest
'';
};
in {
services.podman = {
enable = true;
builds = {
"my-bld" = { file = "${containerFile}"; };
"my-bld-2" = {
file = "https://www.github.com/././Containerfile";
extraConfig = {
Build.ImageTag = [ "locahost/somethingelse" "localhost/anothertag" ];
};
};
};
};
test.asserts.assertions.expected = [
''
In 'my-bld-2' config. Build.ImageTag: '[ "locahost/somethingelse" "localhost/anothertag" ]' does not contain 'homemanager/my-bld-2'.''
];
nmt.script = ''
configPath=home-files/.config/systemd/user
buildFile=$configPath/podman-my-bld-build.service
assertFileExists $buildFile
buildFile=$(normalizeStorePaths $buildFile)
assertFileContent $buildFile ${./build-expected.service}
'';
}

View file

@ -1,7 +1,10 @@
{
podman-configuration = ./configuration.nix;
podman-container = ./container.nix;
podman-build = ./build.nix;
podman-image = ./image.nix;
podman-integration = ./integration.nix;
podman-manifest = ./manifest.nix;
podman-network = ./network.nix;
podman-volume = ./volume.nix;
}

View file

@ -0,0 +1,28 @@
# Automatically generated by /nix/store/00000000000000000000000000000000-podman/lib/systemd/user-generators/podman-user-generator
#
# Automatically generated by home-manager for podman image configuration
# DO NOT EDIT THIS FILE DIRECTLY
#
# my-img.image
[X-Image]
Image=docker.io/alpine:latest
TLSVerify=true
[Install]
WantedBy=default.target
WantedBy=multi-user.target
[Service]
ExecStartPre=/nix/store/00000000000000000000000000000000-await-podman-unshare
RemainAfterExit=yes
TimeoutStartSec=300
ExecStart=/nix/store/00000000000000000000000000000000-podman/bin/podman image pull --tls-verify docker.io/alpine:latest
SyslogIdentifier=%N
Type=oneshot
[Unit]
Wants=podman-user-wait-network-online.service
After=podman-user-wait-network-online.service
Description=Service for image my-img
SourcePath=/nix/store/00000000000000000000000000000000-home-image-podman-my-img/quadlets/podman-my-img.image
RequiresMountsFor=%t/containers

View file

@ -0,0 +1,18 @@
{ ... }:
{
services.podman = {
enable = true;
images = { "my-img" = { image = "docker.io/alpine:latest"; }; };
};
nmt.script = ''
configPath=home-files/.config/systemd/user
imageFile=$configPath/podman-my-img-image.service
assertFileExists $imageFile
imageFile=$(normalizeStorePaths $imageFile)
assertFileContent $imageFile ${./image-expected.service}
'';
}

View file

@ -0,0 +1,30 @@
# Automatically generated by /nix/store/00000000000000000000000000000000-podman/lib/systemd/user-generators/podman-user-generator
#
# Automatically generated by home-manager for podman build configuration
# DO NOT EDIT THIS FILE DIRECTLY
#
# my-bld.build
[X-Build]
Environment=
File=/nix/store/00000000000000000000000000000000-Containerfile
ImageTag=homemanager/my-bld
Label=nix.home-manager.managed=true
TLSVerify=true
[Install]
WantedBy=default.target
WantedBy=multi-user.target
[Service]
RemainAfterExit=yes
TimeoutStartSec=300
ExecStart=/nix/store/00000000000000000000000000000000-podman/bin/podman build --tls-verify --tag homemanager/my-bld --label nix.home-manager.managed=true --file /nix/store/00000000000000000000000000000000-Containerfile
SyslogIdentifier=%N
Type=oneshot
[Unit]
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

View file

@ -0,0 +1,39 @@
# Automatically generated by /nix/store/00000000000000000000000000000000-podman/lib/systemd/user-generators/podman-user-generator
#
# Automatically generated by home-manager podman container configuration
# DO NOT EDIT THIS FILE DIRECTLY
#
# my-container-bld.container
[X-Container]
ContainerName=my-container-bld
Environment=
Image=localhost/homemanager/my-bld
Label=nix.home-manager.managed=true
[Install]
WantedBy=default.target
WantedBy=multi-user.target
[Service]
Environment=PATH=/run/wrappers/bin:/run/current-system/sw/bin:/home/hm-user/.nix-profile/bin
Restart=always
TimeoutStopSec=30
Environment=PODMAN_SYSTEMD_UNIT=%n
KillMode=mixed
ExecStop=/nix/store/00000000000000000000000000000000-podman/bin/podman rm -v -f -i --cidfile=%t/%N.cid
ExecStopPost=-/nix/store/00000000000000000000000000000000-podman/bin/podman rm -v -f -i --cidfile=%t/%N.cid
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
[Unit]
Wants=podman-user-wait-network-online.service
After=podman-user-wait-network-online.service
After=network.target
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
RequiresMountsFor=%t/containers

View file

@ -11,6 +11,7 @@ Image=docker.io/alpine:latest
Label=nix.home-manager.managed=true
Network=my-net
Network=externalnet
Volume=my-vol:/data
[Install]
WantedBy=default.target
@ -28,14 +29,18 @@ Delegate=yes
Type=notify
NotifyAccess=all
SyslogIdentifier=%N
ExecStart=/nix/store/00000000000000000000000000000000-podman/bin/podman run --name my-container --cidfile=%t/%N.cid --replace --rm --cgroups=split --network my-net --network externalnet --sdnotify=conmon -d --label nix.home-manager.managed=true docker.io/alpine:latest
ExecStart=/nix/store/00000000000000000000000000000000-podman/bin/podman run --name my-container --cidfile=%t/%N.cid --replace --rm --cgroups=split --network my-net --network externalnet --sdnotify=conmon -d -v my-vol:/data --label nix.home-manager.managed=true docker.io/alpine:latest
[Unit]
Wants=podman-user-wait-network-online.service
After=podman-user-wait-network-online.service
After=network.target
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
RequiresMountsFor=%t/containers

View file

@ -0,0 +1,28 @@
# Automatically generated by /nix/store/00000000000000000000000000000000-podman/lib/systemd/user-generators/podman-user-generator
#
# Automatically generated by home-manager for podman image configuration
# DO NOT EDIT THIS FILE DIRECTLY
#
# my-img.image
[X-Image]
Image=docker.io/alpine:latest
TLSVerify=true
[Install]
WantedBy=default.target
WantedBy=multi-user.target
[Service]
ExecStartPre=/nix/store/00000000000000000000000000000000-await-podman-unshare
RemainAfterExit=yes
TimeoutStartSec=300
ExecStart=/nix/store/00000000000000000000000000000000-podman/bin/podman image pull --tls-verify docker.io/alpine:latest
SyslogIdentifier=%N
Type=oneshot
[Unit]
Wants=podman-user-wait-network-online.service
After=podman-user-wait-network-online.service
Description=Service for image my-img
SourcePath=/nix/store/00000000000000000000000000000000-home-image-podman-my-img/quadlets/podman-my-img.image
RequiresMountsFor=%t/containers

View file

@ -0,0 +1,33 @@
# Automatically generated by /nix/store/00000000000000000000000000000000-podman/lib/systemd/user-generators/podman-user-generator
#
# Automatically generated by home-manager for podman volume configuration
# DO NOT EDIT THIS FILE DIRECTLY
#
# my-vol.volume
[Install]
WantedBy=default.target
WantedBy=multi-user.target
[Service]
Environment=PATH=/run/wrappers/bin:/usr/bin:/bin:/usr/sbin:/sbin:/nix/store/00000000000000000000000000000000-shadow/bin:/nix/store/00000000000000000000000000000000-coreutils/bin
ExecStartPre=/nix/store/00000000000000000000000000000000-await-podman-unshare
RemainAfterExit=yes
TimeoutStartSec=15
ExecStart=/nix/store/00000000000000000000000000000000-podman/bin/podman volume create --ignore --opt copy --opt device=tmpfs --opt type=tmpfs --label nix.home-manager.managed=true --label nix.home-manager.preserve=false my-vol
SyslogIdentifier=%N
Type=oneshot
[Unit]
Wants=podman-user-wait-network-online.service
After=podman-user-wait-network-online.service
Description=Service for volume my-vol
SourcePath=/nix/store/00000000000000000000000000000000-home-volume-podman-my-vol/quadlets/podman-my-vol.volume
RequiresMountsFor=%t/containers
[X-Volume]
Copy=true
Device=tmpfs
Label=nix.home-manager.managed=true
Label=nix.home-manager.preserve=false
Type=tmpfs
VolumeName=my-vol

View file

@ -1,29 +1,64 @@
{ ... }:
{
{ pkgs, ... }:
let
containerFile = pkgs.writeTextFile {
name = "Containerfile";
text = ''
FROM docker.io/alpine:latest
'';
};
in {
services.podman = {
enable = true;
containers."my-container" = {
image = "docker.io/alpine:latest";
network = [ "my-net" "externalnet" ];
builds."my-bld" = { file = "${containerFile}"; };
containers = {
"my-container" = {
image = "my-img";
network = [ "my-net" "externalnet" ];
volumes = [ "my-vol:/data" ];
};
"my-container-bld" = { image = "my-bld"; };
};
images."my-img" = { image = "docker.io/alpine:latest"; };
networks."my-net" = {
gateway = "192.168.123.1";
subnet = "192.168.123.0/24";
};
volumes."my-vol" = {
device = "tmpfs";
preserve = false;
type = "tmpfs";
};
};
nmt.script = ''
configPath=home-files/.config/systemd/user
buildFile=$configPath/podman-my-bld-build.service
containerFile=$configPath/podman-my-container.service
containerBldFile=$configPath/podman-my-container-bld.service
imageFile=$configPath/podman-my-img-image.service
networkFile=$configPath/podman-my-net-network.service
volumeFile=$configPath/podman-my-vol-volume.service
assertFileExists $buildFile
assertFileExists $containerFile
assertFileExists $containerBldFile
assertFileExists $imageFile
assertFileExists $networkFile
assertFileExists $volumeFile
buildFile=$(normalizeStorePaths $buildFile)
containerFile=$(normalizeStorePaths $containerFile)
containerBldFile=$(normalizeStorePaths $containerBldFile)
imageFile=$(normalizeStorePaths $imageFile)
networkFile=$(normalizeStorePaths $networkFile)
volumeFile=$(normalizeStorePaths $volumeFile)
assertFileContent $buildFile ${./integration-build-expected.service}
assertFileContent $containerFile ${./integration-container-expected.service}
assertFileContent $containerBldFile ${
./integration-container-bld-expected.service
}
assertFileContent $imageFile ${./integration-image-expected.service}
assertFileContent $networkFile ${./integration-network-expected.service}
assertFileContent $volumeFile ${./integration-volume-expected.service}
'';
}

View file

@ -0,0 +1,36 @@
# Automatically generated by /nix/store/00000000000000000000000000000000-podman/lib/systemd/user-generators/podman-user-generator
#
# Automatically generated by home-manager for podman volume configuration
# DO NOT EDIT THIS FILE DIRECTLY
#
# my-vol.volume
[Install]
WantedBy=default.target
WantedBy=multi-user.target
[Service]
Environment=PATH=/run/wrappers/bin:/usr/bin:/bin:/usr/sbin:/sbin:/nix/store/00000000000000000000000000000000-shadow/bin:/nix/store/00000000000000000000000000000000-coreutils/bin
ExecStartPre=/nix/store/00000000000000000000000000000000-await-podman-unshare
RemainAfterExit=yes
TimeoutStartSec=15
ExecStart=/nix/store/00000000000000000000000000000000-podman/bin/podman volume create --ignore --opt copy --opt device=tmpfs --opt type=tmpfs --opt o=uid=1000,gid=1000 --label nix.home-manager.managed=true --label nix.home-manager.preserve=true --module=/etc/nvd.conf my-vol
SyslogIdentifier=%N
Type=oneshot
[Unit]
Wants=podman-user-wait-network-online.service
After=podman-user-wait-network-online.service
Description=Service for volume my-vol
SourcePath=/nix/store/00000000000000000000000000000000-home-volume-podman-my-vol/quadlets/podman-my-vol.volume
RequiresMountsFor=%t/containers
[X-Volume]
Copy=true
Device=tmpfs
Group=1000
Label=nix.home-manager.managed=true
Label=nix.home-manager.preserve=true
PodmanArgs=--module=/etc/nvd.conf
Type=tmpfs
User=1000
VolumeName=my-vol

View file

@ -0,0 +1,35 @@
{ ... }:
{
services.podman = {
enable = true;
volumes = {
"my-vol" = {
device = "tmpfs";
extraConfig = { Volume = { User = 1000; }; };
extraPodmanArgs = [ "--module=/etc/nvd.conf" ];
group = 1000;
type = "tmpfs";
};
"my-vol-2" = {
extraConfig = { Volume = { VolumeName = "some-other-volume-name"; }; };
};
};
};
test.asserts.assertions.expected = [
''
In 'my-vol-2' config. Volume.VolumeName: 'some-other-volume-name' does not match expected type: value "my-vol-2" (singular enum)''
];
nmt.script = ''
configPath=home-files/.config/systemd/user
volumeFile=$configPath/podman-my-vol-volume.service
assertFileExists $volumeFile
volumeFile=$(normalizeStorePaths $volumeFile)
assertFileContent $volumeFile ${./volume-expected.service}
'';
}