mirror of
https://github.com/nix-community/home-manager.git
synced 2025-11-08 19:46:05 +01:00
podman: added volume, image, and build quadlets (#6137)
Added support for build, image, and volume quadlets Resolved test failures due to podman 5.3.0 upgrade Replaced several instances of pkgs.podman with services.podman.package
This commit is contained in:
parent
f8bb0ba6de
commit
ce9cb2496c
24 changed files with 1000 additions and 54 deletions
|
|
@ -18,6 +18,7 @@
|
||||||
local manifestFile="${config.xdg.configHome}/podman/$2"
|
local manifestFile="${config.xdg.configHome}/podman/$2"
|
||||||
local extraListCommands="''${3:-}"
|
local extraListCommands="''${3:-}"
|
||||||
[[ $resourceType = "container" ]] && extraListCommands+=" -a"
|
[[ $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
|
[ ! -f "$manifestFile" ] && VERBOSE_ENABLED && echo "Manifest does not exist: $manifestFile" && return 0
|
||||||
|
|
||||||
|
|
@ -27,6 +28,7 @@
|
||||||
|
|
||||||
formatString="{{.Name}}"
|
formatString="{{.Name}}"
|
||||||
[[ $resourceType = "container" ]] && formatString="{{.Names}}"
|
[[ $resourceType = "container" ]] && formatString="{{.Names}}"
|
||||||
|
[[ $resourceType = "image" ]] && formatString="{{.Repository}}"
|
||||||
|
|
||||||
local listOutput=$(${config.services.podman.package}/bin/podman $resourceType ls $extraListCommands --filter 'label=nix.home-manager.managed=true' --format "$formatString")
|
local listOutput=$(${config.services.podman.package}/bin/podman $resourceType ls $extraListCommands --filter 'label=nix.home-manager.managed=true' --format "$formatString")
|
||||||
|
|
||||||
|
|
@ -36,12 +38,12 @@
|
||||||
VERBOSE_ENABLED && echo "No ''${resourceType}s available to process." || true
|
VERBOSE_ENABLED && echo "No ''${resourceType}s available to process." || true
|
||||||
else
|
else
|
||||||
for resource in "''${podmanResources[@]}"; do
|
for resource in "''${podmanResources[@]}"; do
|
||||||
if ! isResourceInManifest "$resource"; then
|
if ! isResourceInManifest "$resource"; then
|
||||||
removeResource "$resourceType" "$resource"
|
removeResource "$resourceType" "$resource"
|
||||||
else
|
else
|
||||||
VERBOSE_ENABLED && echo "Keeping managed $resourceType: $resource" || true
|
VERBOSE_ENABLED && echo "Keeping managed $resourceType: $resource" || true
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -69,19 +71,20 @@
|
||||||
commands=()
|
commands=()
|
||||||
case "$resourceType" in
|
case "$resourceType" in
|
||||||
"container")
|
"container")
|
||||||
commands+="${config.services.podman.package}/bin/podman $resourceType stop $resource"
|
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 rm -f $resource")
|
||||||
;;
|
;;
|
||||||
"network")
|
"image" | "network" | "volume")
|
||||||
commands+="${config.services.podman.package}/bin/podman $resourceType rm $resource"
|
commands+=("${config.services.podman.package}/bin/podman $resourceType rm $resource")
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
for command in "''${commands[@]}"; do
|
for command in "''${commands[@]}"; do
|
||||||
command=$(echo $command | tr -d ';&|`')
|
command=$(echo $command | tr -d ';&|`')
|
||||||
DRYRUN_ENABLED && echo "Would run: $command" && continue || true
|
DRYRUN_ENABLED && echo "Would run: $command" && continue || true
|
||||||
VERBOSE_ENABLED && echo "Running: $command" || true
|
VERBOSE_ENABLED && echo "Running: $command" || true
|
||||||
if [[ "$(eval "$command")" != "$resource" ]]; then
|
if [[ "$(eval "$command")" != *"$resource" ]]; then
|
||||||
echo -e "\tCommand failed: ''${command}"
|
echo -e "\tCommand failed: ''${command}"
|
||||||
|
[ "$resourceType" == "image" ] && resourceType="ancestor"
|
||||||
usedByContainers=$(${config.services.podman.package}/bin/podman container ls -a --filter "$resourceType=$resource" --format "{{.Names}}")
|
usedByContainers=$(${config.services.podman.package}/bin/podman container ls -a --filter "$resourceType=$resource" --format "{{.Names}}")
|
||||||
echo -e "\t$resource in use by containers: $usedByContainers"
|
echo -e "\t$resource in use by containers: $usedByContainers"
|
||||||
fi
|
fi
|
||||||
|
|
@ -92,7 +95,7 @@
|
||||||
[[ "$@" == *"--verbose"* ]] && VERBOSE="true"
|
[[ "$@" == *"--verbose"* ]] && VERBOSE="true"
|
||||||
[[ "$@" == *"--dry-run"* ]] && DRY_RUN="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"
|
cleanup "$type" "''${type}s.manifest"
|
||||||
done
|
done
|
||||||
'';
|
'';
|
||||||
|
|
|
||||||
168
modules/services/podman-linux/builds.nix
Normal file
168
modules/services/podman-linux/builds.nix
Normal file
|
|
@ -0,0 +1,168 @@
|
||||||
|
{ config, lib, pkgs, ... }:
|
||||||
|
with lib;
|
||||||
|
let
|
||||||
|
cfg = config.services.podman;
|
||||||
|
|
||||||
|
podman-lib = import ./podman-lib.nix { inherit pkgs 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;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
@ -5,24 +5,60 @@ with lib;
|
||||||
let
|
let
|
||||||
cfg = config.services.podman;
|
cfg = config.services.podman;
|
||||||
|
|
||||||
podman-lib = import ./podman-lib.nix { inherit lib config; };
|
podman-lib = import ./podman-lib.nix { inherit pkgs lib config; };
|
||||||
|
|
||||||
createQuadletSource = name: containerDef:
|
createQuadletSource = name: containerDef:
|
||||||
let
|
let
|
||||||
mapHmNetworks = network:
|
formatServiceNameForType = type: name:
|
||||||
if builtins.hasAttr network cfg.networks then
|
{
|
||||||
"podman-${network}-network.service"
|
image = "podman-${name}-image.service";
|
||||||
else
|
build = "podman-${name}-build.service";
|
||||||
null;
|
network = "podman-${name}-network.service";
|
||||||
|
volume = "podman-${name}-volume.service";
|
||||||
|
}."${type}";
|
||||||
|
|
||||||
finalConfig = let
|
dependencyByHomeManagerQuadlet = type: name:
|
||||||
managedNetworks = if lib.isList containerDef.network then
|
let
|
||||||
map mapHmNetworks containerDef.network
|
definitionsOfType =
|
||||||
else if containerDef.network != null then
|
filter (q: q.resourceType == type) cfg.internal.quadletDefinitions;
|
||||||
map mapHmNetworks [ containerDef.network ]
|
matchingName =
|
||||||
|
filter (q: q.serviceName == "podman-${name}") definitionsOfType;
|
||||||
|
in if ((length matchingName) == 1) then
|
||||||
|
[ (formatServiceNameForType type name) ]
|
||||||
else
|
else
|
||||||
[ ];
|
[ ];
|
||||||
in (podman-lib.deepMerge {
|
|
||||||
|
forEachValue = type: value:
|
||||||
|
let resolve = v: dependencyByHomeManagerQuadlet type v;
|
||||||
|
in if isList value then
|
||||||
|
concatLists (map resolve value)
|
||||||
|
else
|
||||||
|
resolve value;
|
||||||
|
|
||||||
|
withResolverFor = type: value:
|
||||||
|
{
|
||||||
|
"image" = forEachValue "image" value;
|
||||||
|
"build" = forEachValue "build" value;
|
||||||
|
"network" = forEachValue "network" value;
|
||||||
|
"volume" = let
|
||||||
|
a = if isList value then value else [ value ];
|
||||||
|
volumes = map (v: elemAt (splitString ":" v) 0) a;
|
||||||
|
in forEachValue "volume" volumes;
|
||||||
|
}.${type};
|
||||||
|
|
||||||
|
dependencyServices = (withResolverFor "image" containerDef.image)
|
||||||
|
++ (withResolverFor "build" containerDef.image)
|
||||||
|
++ (withResolverFor "network" containerDef.network)
|
||||||
|
++ (withResolverFor "volume" containerDef.volumes);
|
||||||
|
|
||||||
|
resolvedImage = 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;
|
||||||
|
|
||||||
|
quadlet = (podman-lib.deepMerge {
|
||||||
Container = {
|
Container = {
|
||||||
AddCapability = containerDef.addCapabilities;
|
AddCapability = containerDef.addCapabilities;
|
||||||
AddDevice = containerDef.devices;
|
AddDevice = containerDef.devices;
|
||||||
|
|
@ -34,7 +70,7 @@ let
|
||||||
EnvironmentFile = containerDef.environmentFile;
|
EnvironmentFile = containerDef.environmentFile;
|
||||||
Exec = containerDef.exec;
|
Exec = containerDef.exec;
|
||||||
Group = containerDef.group;
|
Group = containerDef.group;
|
||||||
Image = containerDef.image;
|
Image = resolvedImage;
|
||||||
IP = containerDef.ip4;
|
IP = containerDef.ip4;
|
||||||
IP6 = containerDef.ip6;
|
IP6 = containerDef.ip6;
|
||||||
Label =
|
Label =
|
||||||
|
|
@ -48,11 +84,10 @@ let
|
||||||
Volume = containerDef.volumes;
|
Volume = containerDef.volumes;
|
||||||
};
|
};
|
||||||
Install = {
|
Install = {
|
||||||
WantedBy = (if containerDef.autoStart then [
|
WantedBy = optionals containerDef.autoStart [
|
||||||
"default.target"
|
"default.target"
|
||||||
"multi-user.target"
|
"multi-user.target"
|
||||||
] else
|
];
|
||||||
[ ]);
|
|
||||||
};
|
};
|
||||||
Service = {
|
Service = {
|
||||||
Environment = {
|
Environment = {
|
||||||
|
|
@ -66,8 +101,8 @@ let
|
||||||
TimeoutStopSec = 30;
|
TimeoutStopSec = 30;
|
||||||
};
|
};
|
||||||
Unit = {
|
Unit = {
|
||||||
After = [ "network.target" ] ++ managedNetworks;
|
After = dependencyServices;
|
||||||
Requires = managedNetworks;
|
Requires = dependencyServices;
|
||||||
Description = (if (builtins.isString containerDef.description) then
|
Description = (if (builtins.isString containerDef.description) then
|
||||||
containerDef.description
|
containerDef.description
|
||||||
else
|
else
|
||||||
|
|
@ -79,7 +114,7 @@ let
|
||||||
# DO NOT EDIT THIS FILE DIRECTLY
|
# DO NOT EDIT THIS FILE DIRECTLY
|
||||||
#
|
#
|
||||||
# ${name}.container
|
# ${name}.container
|
||||||
${podman-lib.toQuadletIni finalConfig}
|
${podman-lib.toQuadletIni quadlet}
|
||||||
'';
|
'';
|
||||||
|
|
||||||
toQuadletInternal = name: containerDef: {
|
toQuadletInternal = name: containerDef: {
|
||||||
|
|
@ -307,7 +342,7 @@ in {
|
||||||
flatten (map (container: container.assertions) containerQuadlets);
|
flatten (map (container: container.assertions) containerQuadlets);
|
||||||
|
|
||||||
# manifest file
|
# manifest file
|
||||||
home.file."${config.xdg.configHome}/podman/containers.manifest".text =
|
xdg.configFile."podman/containers.manifest".text =
|
||||||
podman-lib.generateManifestText containerQuadlets;
|
podman-lib.generateManifestText containerQuadlets;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,8 +5,15 @@ let
|
||||||
in {
|
in {
|
||||||
meta.maintainers = with lib.hm.maintainers; [ bamhm182 n-hass ];
|
meta.maintainers = with lib.hm.maintainers; [ bamhm182 n-hass ];
|
||||||
|
|
||||||
imports =
|
imports = [
|
||||||
[ ./containers.nix ./install-quadlet.nix ./networks.nix ./services.nix ];
|
./builds.nix
|
||||||
|
./containers.nix
|
||||||
|
./images.nix
|
||||||
|
./install-quadlet.nix
|
||||||
|
./networks.nix
|
||||||
|
./services.nix
|
||||||
|
./volumes.nix
|
||||||
|
];
|
||||||
|
|
||||||
options.services.podman = {
|
options.services.podman = {
|
||||||
enable = lib.mkEnableOption "Podman, a daemonless container engine";
|
enable = lib.mkEnableOption "Podman, a daemonless container engine";
|
||||||
|
|
|
||||||
162
modules/services/podman-linux/images.nix
Normal file
162
modules/services/podman-linux/images.nix
Normal file
|
|
@ -0,0 +1,162 @@
|
||||||
|
{ config, lib, pkgs, ... }:
|
||||||
|
with lib;
|
||||||
|
let
|
||||||
|
cfg = config.services.podman;
|
||||||
|
|
||||||
|
podman-lib = import ./podman-lib.nix { inherit pkgs lib config; };
|
||||||
|
|
||||||
|
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 = [ "${podman-lib.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);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
@ -5,7 +5,7 @@ with lib;
|
||||||
let
|
let
|
||||||
cfg = config.services.podman;
|
cfg = config.services.podman;
|
||||||
|
|
||||||
podman-lib = import ./podman-lib.nix { inherit lib config; };
|
podman-lib = import ./podman-lib.nix { inherit pkgs lib config; };
|
||||||
activation = import ./activation.nix { inherit config podman-lib; };
|
activation = import ./activation.nix { inherit config podman-lib; };
|
||||||
|
|
||||||
activationCleanupScript = activation.cleanup;
|
activationCleanupScript = activation.cleanup;
|
||||||
|
|
|
||||||
|
|
@ -5,13 +5,7 @@ with lib;
|
||||||
let
|
let
|
||||||
cfg = config.services.podman;
|
cfg = config.services.podman;
|
||||||
|
|
||||||
podman-lib = import ./podman-lib.nix { inherit lib config; };
|
podman-lib = import ./podman-lib.nix { inherit pkgs lib config; };
|
||||||
|
|
||||||
awaitPodmanUnshare = pkgs.writeShellScript "await-podman-unshare" ''
|
|
||||||
until ${cfg.package}/bin/podman unshare ${pkgs.coreutils}/bin/true; do
|
|
||||||
sleep 1;
|
|
||||||
done
|
|
||||||
'';
|
|
||||||
|
|
||||||
createQuadletSource = name: networkDef:
|
createQuadletSource = name: networkDef:
|
||||||
let
|
let
|
||||||
|
|
@ -39,7 +33,7 @@ let
|
||||||
"${makeBinPath [ pkgs.su pkgs.coreutils ]}"
|
"${makeBinPath [ pkgs.su pkgs.coreutils ]}"
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
ExecStartPre = [ "${awaitPodmanUnshare}" ];
|
ExecStartPre = [ "${podman-lib.awaitPodmanUnshare}" ];
|
||||||
TimeoutStartSec = 15;
|
TimeoutStartSec = 15;
|
||||||
RemainAfterExit = "yes";
|
RemainAfterExit = "yes";
|
||||||
};
|
};
|
||||||
|
|
@ -162,7 +156,7 @@ in {
|
||||||
services.podman.internal.quadletDefinitions = networkQuadlets;
|
services.podman.internal.quadletDefinitions = networkQuadlets;
|
||||||
assertions = flatten (map (network: network.assertions) networkQuadlets);
|
assertions = flatten (map (network: network.assertions) networkQuadlets);
|
||||||
|
|
||||||
home.file."${config.xdg.configHome}/podman/networks.manifest".text =
|
xdg.configFile."podman/networks.manifest".text =
|
||||||
podman-lib.generateManifestText networkQuadlets;
|
podman-lib.generateManifestText networkQuadlets;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
{ lib, config, ... }:
|
{ pkgs, lib, config, ... }:
|
||||||
|
|
||||||
with lib;
|
with lib;
|
||||||
|
|
||||||
|
|
@ -63,8 +63,10 @@ in {
|
||||||
buildConfigAsserts = quadletName: extraConfig:
|
buildConfigAsserts = quadletName: extraConfig:
|
||||||
let
|
let
|
||||||
configRules = {
|
configRules = {
|
||||||
|
Build = { ImageTag = (with types; listOf str); };
|
||||||
Container = { ContainerName = types.enum [ quadletName ]; };
|
Container = { ContainerName = types.enum [ quadletName ]; };
|
||||||
Network = { NetworkName = types.enum [ quadletName ]; };
|
Network = { NetworkName = types.enum [ quadletName ]; };
|
||||||
|
Volume = { VolumeName = types.enum [ quadletName ]; };
|
||||||
};
|
};
|
||||||
|
|
||||||
# Function to build assertions for a specific section and its attributes.
|
# Function to build assertions for a specific section and its attributes.
|
||||||
|
|
@ -83,8 +85,23 @@ in {
|
||||||
else
|
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`.
|
# Flatten assertions from all sections in `extraConfig`.
|
||||||
in flatten (mapAttrsToList buildSectionAsserts extraConfig);
|
in flatten (concatLists [
|
||||||
|
(mapAttrsToList buildSectionAsserts extraConfig)
|
||||||
|
(checkImageTag extraConfig)
|
||||||
|
]);
|
||||||
|
|
||||||
extraConfigType = with types;
|
extraConfigType = with types;
|
||||||
attrsOf (attrsOf (oneOf [ primitiveAttrs primitiveList primitive ]));
|
attrsOf (attrsOf (oneOf [ primitiveAttrs primitiveList primitive ]));
|
||||||
|
|
@ -107,8 +124,10 @@ in {
|
||||||
# specific logic for writing the unit name goes here. It should be
|
# specific logic for writing the unit name goes here. It should be
|
||||||
# identical to what `podman <resource> ls` shows
|
# identical to what `podman <resource> ls` shows
|
||||||
in {
|
in {
|
||||||
|
"build" = "localhost/homemanager/${strippedName}";
|
||||||
"container" = strippedName;
|
"container" = strippedName;
|
||||||
"network" = strippedName;
|
"network" = strippedName;
|
||||||
|
"volume" = strippedName;
|
||||||
}."${quadlet.resourceType}";
|
}."${quadlet.resourceType}";
|
||||||
in if allQuadletsSameType then ''
|
in if allQuadletsSameType then ''
|
||||||
${concatStringsSep "\n"
|
${concatStringsSep "\n"
|
||||||
|
|
@ -133,4 +152,10 @@ in {
|
||||||
lines = splitString "\n" text;
|
lines = splitString "\n" text;
|
||||||
nonEmptyLines = filter (line: line != "") lines;
|
nonEmptyLines = filter (line: line != "") lines;
|
||||||
in concatStringsSep "\n" nonEmptyLines;
|
in concatStringsSep "\n" nonEmptyLines;
|
||||||
|
|
||||||
|
awaitPodmanUnshare = pkgs.writeShellScript "await-podman-unshare" ''
|
||||||
|
until ${config.services.podman.package}/bin/podman unshare ${pkgs.coreutils}/bin/true; do
|
||||||
|
${pkgs.coreutils}/bin/sleep 1
|
||||||
|
done
|
||||||
|
'';
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -42,8 +42,8 @@ in {
|
||||||
"${config.home.homeDirectory}/.nix-profile/bin"
|
"${config.home.homeDirectory}/.nix-profile/bin"
|
||||||
]
|
]
|
||||||
}";
|
}";
|
||||||
ExecStart = "${pkgs.podman}/bin/podman auto-update";
|
ExecStart = "${cfg.package}/bin/podman auto-update";
|
||||||
ExecStartPost = "${pkgs.podman}/bin/podman image prune -f";
|
ExecStartPost = "${cfg.package}/bin/podman image prune -f";
|
||||||
TimeoutStartSec = "300s";
|
TimeoutStartSec = "300s";
|
||||||
TimeoutStopSec = "10s";
|
TimeoutStopSec = "10s";
|
||||||
};
|
};
|
||||||
|
|
|
||||||
186
modules/services/podman-linux/volumes.nix
Normal file
186
modules/services/podman-linux/volumes.nix
Normal file
|
|
@ -0,0 +1,186 @@
|
||||||
|
{ config, lib, pkgs, ... }:
|
||||||
|
|
||||||
|
with lib;
|
||||||
|
|
||||||
|
let
|
||||||
|
cfg = config.services.podman;
|
||||||
|
|
||||||
|
podman-lib = import ./podman-lib.nix { inherit pkgs lib config; };
|
||||||
|
|
||||||
|
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 = [ "${podman-lib.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;
|
||||||
|
};
|
||||||
|
}
|
||||||
30
tests/modules/services/podman-linux/build-expected.service
Normal file
30
tests/modules/services/podman-linux/build-expected.service
Normal 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
|
||||||
44
tests/modules/services/podman-linux/build.nix
Normal file
44
tests/modules/services/podman-linux/build.nix
Normal file
|
|
@ -0,0 +1,44 @@
|
||||||
|
{ pkgs, ... }:
|
||||||
|
|
||||||
|
{
|
||||||
|
imports = [ ./podman-stubs.nix ];
|
||||||
|
|
||||||
|
services.podman = {
|
||||||
|
enable = true;
|
||||||
|
builds = {
|
||||||
|
"my-bld" = {
|
||||||
|
file = let
|
||||||
|
containerFile = pkgs.writeTextFile {
|
||||||
|
name = "Containerfile";
|
||||||
|
text = ''
|
||||||
|
FROM docker.io/alpine:latest
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
in "${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}
|
||||||
|
'';
|
||||||
|
}
|
||||||
|
|
@ -43,7 +43,6 @@ ExecStart=/nix/store/00000000000000000000000000000000-podman/bin/podman run --na
|
||||||
[Unit]
|
[Unit]
|
||||||
Wants=podman-user-wait-network-online.service
|
Wants=podman-user-wait-network-online.service
|
||||||
After=podman-user-wait-network-online.service
|
After=podman-user-wait-network-online.service
|
||||||
After=network.target
|
|
||||||
Before=fake.target
|
Before=fake.target
|
||||||
Description=home-manager test
|
Description=home-manager test
|
||||||
SourcePath=/nix/store/00000000000000000000000000000000-home-container-podman-my-container/quadlets/podman-my-container.container
|
SourcePath=/nix/store/00000000000000000000000000000000-home-container-podman-my-container/quadlets/podman-my-container.container
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,10 @@
|
||||||
{
|
{
|
||||||
podman-configuration = ./configuration.nix;
|
podman-configuration = ./configuration.nix;
|
||||||
podman-container = ./container.nix;
|
podman-container = ./container.nix;
|
||||||
|
podman-build = ./build.nix;
|
||||||
|
podman-image = ./image.nix;
|
||||||
podman-integration = ./integration.nix;
|
podman-integration = ./integration.nix;
|
||||||
podman-manifest = ./manifest.nix;
|
podman-manifest = ./manifest.nix;
|
||||||
podman-network = ./network.nix;
|
podman-network = ./network.nix;
|
||||||
|
podman-volume = ./volume.nix;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
28
tests/modules/services/podman-linux/image-expected.service
Normal file
28
tests/modules/services/podman-linux/image-expected.service
Normal 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
|
||||||
18
tests/modules/services/podman-linux/image.nix
Normal file
18
tests/modules/services/podman-linux/image.nix
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
{
|
||||||
|
imports = [ ./podman-stubs.nix ];
|
||||||
|
|
||||||
|
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}
|
||||||
|
'';
|
||||||
|
}
|
||||||
|
|
@ -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
|
||||||
|
|
@ -0,0 +1,38 @@
|
||||||
|
# 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=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
|
||||||
|
|
@ -11,6 +11,7 @@ Image=docker.io/alpine:latest
|
||||||
Label=nix.home-manager.managed=true
|
Label=nix.home-manager.managed=true
|
||||||
Network=my-net
|
Network=my-net
|
||||||
Network=externalnet
|
Network=externalnet
|
||||||
|
Volume=my-vol:/data
|
||||||
|
|
||||||
[Install]
|
[Install]
|
||||||
WantedBy=default.target
|
WantedBy=default.target
|
||||||
|
|
@ -28,14 +29,17 @@ Delegate=yes
|
||||||
Type=notify
|
Type=notify
|
||||||
NotifyAccess=all
|
NotifyAccess=all
|
||||||
SyslogIdentifier=%N
|
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]
|
[Unit]
|
||||||
Wants=podman-user-wait-network-online.service
|
Wants=podman-user-wait-network-online.service
|
||||||
After=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-net-network.service
|
||||||
|
After=podman-my-vol-volume.service
|
||||||
Description=Service for container my-container
|
Description=Service for container my-container
|
||||||
|
Requires=podman-my-img-image.service
|
||||||
Requires=podman-my-net-network.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
|
SourcePath=/nix/store/00000000000000000000000000000000-home-container-podman-my-container/quadlets/podman-my-container.container
|
||||||
RequiresMountsFor=%t/containers
|
RequiresMountsFor=%t/containers
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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:@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
|
||||||
|
|
@ -1,29 +1,69 @@
|
||||||
|
{ pkgs, ... }:
|
||||||
|
|
||||||
{
|
{
|
||||||
imports = [ ./podman-stubs.nix ];
|
imports = [ ./podman-stubs.nix ];
|
||||||
|
|
||||||
services.podman = {
|
services.podman = {
|
||||||
enable = true;
|
enable = true;
|
||||||
containers."my-container" = {
|
builds."my-bld" = {
|
||||||
image = "docker.io/alpine:latest";
|
file = let
|
||||||
network = [ "my-net" "externalnet" ];
|
containerFile = pkgs.writeTextFile {
|
||||||
|
name = "Containerfile";
|
||||||
|
text = ''
|
||||||
|
FROM docker.io/alpine:latest
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
in "${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" = {
|
networks."my-net" = {
|
||||||
gateway = "192.168.123.1";
|
gateway = "192.168.123.1";
|
||||||
subnet = "192.168.123.0/24";
|
subnet = "192.168.123.0/24";
|
||||||
};
|
};
|
||||||
|
volumes."my-vol" = {
|
||||||
|
device = "tmpfs";
|
||||||
|
preserve = false;
|
||||||
|
type = "tmpfs";
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
nmt.script = ''
|
nmt.script = ''
|
||||||
configPath=home-files/.config/systemd/user
|
configPath=home-files/.config/systemd/user
|
||||||
|
buildFile=$configPath/podman-my-bld-build.service
|
||||||
containerFile=$configPath/podman-my-container.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
|
networkFile=$configPath/podman-my-net-network.service
|
||||||
|
volumeFile=$configPath/podman-my-vol-volume.service
|
||||||
|
assertFileExists $buildFile
|
||||||
assertFileExists $containerFile
|
assertFileExists $containerFile
|
||||||
|
assertFileExists $containerBldFile
|
||||||
|
assertFileExists $imageFile
|
||||||
assertFileExists $networkFile
|
assertFileExists $networkFile
|
||||||
|
assertFileExists $volumeFile
|
||||||
|
|
||||||
|
buildFile=$(normalizeStorePaths $buildFile)
|
||||||
containerFile=$(normalizeStorePaths $containerFile)
|
containerFile=$(normalizeStorePaths $containerFile)
|
||||||
|
containerBldFile=$(normalizeStorePaths $containerBldFile)
|
||||||
|
imageFile=$(normalizeStorePaths $imageFile)
|
||||||
networkFile=$(normalizeStorePaths $networkFile)
|
networkFile=$(normalizeStorePaths $networkFile)
|
||||||
|
volumeFile=$(normalizeStorePaths $volumeFile)
|
||||||
|
|
||||||
|
assertFileContent $buildFile ${./integration-build-expected.service}
|
||||||
assertFileContent $containerFile ${./integration-container-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 $networkFile ${./integration-network-expected.service}
|
||||||
|
assertFileContent $volumeFile ${./integration-volume-expected.service}
|
||||||
'';
|
'';
|
||||||
}
|
}
|
||||||
|
|
|
||||||
36
tests/modules/services/podman-linux/volume-expected.service
Normal file
36
tests/modules/services/podman-linux/volume-expected.service
Normal 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:@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
|
||||||
35
tests/modules/services/podman-linux/volume.nix
Normal file
35
tests/modules/services/podman-linux/volume.nix
Normal file
|
|
@ -0,0 +1,35 @@
|
||||||
|
{
|
||||||
|
imports = [ ./podman-stubs.nix ];
|
||||||
|
|
||||||
|
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}
|
||||||
|
'';
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue