mirror of
https://github.com/nix-community/home-manager.git
synced 2025-12-01 06:31:04 +01:00
treewide: reformat nixfmt-rfc-style
Reformat repository using new nixfmt-rfc-style.
This commit is contained in:
parent
5df48c4255
commit
cba2f9ce95
1051 changed files with 37028 additions and 26594 deletions
|
|
@ -1,11 +1,17 @@
|
|||
{ config, lib, pkgs, ... }:
|
||||
{
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
with lib;
|
||||
let
|
||||
cfg = config.services.podman;
|
||||
|
||||
podman-lib = import ./podman-lib.nix { inherit pkgs lib config; };
|
||||
|
||||
createQuadletSource = name: buildDef:
|
||||
createQuadletSource =
|
||||
name: buildDef:
|
||||
let
|
||||
quadlet = podman-lib.deepMerge {
|
||||
Build = {
|
||||
|
|
@ -13,7 +19,9 @@ let
|
|||
Environment = buildDef.environment;
|
||||
File = buildDef.file;
|
||||
ImageTag = [ "homemanager/${name}" ] ++ buildDef.tags;
|
||||
Label = buildDef.labels // { "nix.home-manager.managed" = true; };
|
||||
Label = buildDef.labels // {
|
||||
"nix.home-manager.managed" = true;
|
||||
};
|
||||
PodmanArgs = buildDef.extraPodmanArgs;
|
||||
SetWorkingDirectory = buildDef.workingDirectory;
|
||||
TLSVerify = buildDef.tlsVerify;
|
||||
|
|
@ -28,9 +36,12 @@ let
|
|||
TimeoutStartSec = 300;
|
||||
RemainAfterExit = "yes";
|
||||
};
|
||||
Unit = { Description = buildDef.description; };
|
||||
Unit = {
|
||||
Description = buildDef.description;
|
||||
};
|
||||
} buildDef.extraConfig;
|
||||
in {
|
||||
in
|
||||
{
|
||||
attrs = quadlet;
|
||||
text = ''
|
||||
# Automatically generated by home-manager for podman build configuration
|
||||
|
|
@ -41,133 +52,139 @@ let
|
|||
'';
|
||||
};
|
||||
|
||||
toQuadletInternal = name: buildDef:
|
||||
let src = createQuadletSource name buildDef;
|
||||
in {
|
||||
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
|
||||
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 = {
|
||||
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";
|
||||
autoStart = mkOption {
|
||||
type = types.bool;
|
||||
default = true;
|
||||
description = "Whether to start the build on boot. Requires user lingering.";
|
||||
};
|
||||
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.";
|
||||
};
|
||||
authFile = mkOption {
|
||||
type = with types; nullOr path;
|
||||
default = null;
|
||||
description = "Path of the authentication file.";
|
||||
};
|
||||
|
||||
workingDirectory = mkOption {
|
||||
type = with types; nullOr path;
|
||||
default = null;
|
||||
description = "WorkingDirectory of the systemd unit 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 {
|
||||
}
|
||||
);
|
||||
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);
|
||||
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;
|
||||
};
|
||||
xdg.configFile."podman/images.manifest".text = podman-lib.generateManifestText buildQuadlets;
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,9 @@
|
|||
{ config, lib, pkgs, ... }:
|
||||
{
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
|
||||
with lib;
|
||||
|
||||
|
|
@ -7,56 +12,63 @@ let
|
|||
|
||||
podman-lib = import ./podman-lib.nix { inherit pkgs lib config; };
|
||||
|
||||
createQuadletSource = name: containerDef:
|
||||
createQuadletSource =
|
||||
name: containerDef:
|
||||
let
|
||||
extractQuadletReference = type: value:
|
||||
extractQuadletReference =
|
||||
type: value:
|
||||
let
|
||||
regex = "([a-zA-Z0-9_-]+\\." + type + ").*";
|
||||
parts = builtins.match regex value;
|
||||
in if parts == null then value else builtins.elemAt parts 0;
|
||||
in
|
||||
if parts == null then value else builtins.elemAt parts 0;
|
||||
|
||||
dependencyBySuffix = type: value:
|
||||
dependencyBySuffix =
|
||||
type: value:
|
||||
if (hasInfix ".${type}" value) then
|
||||
let name = extractQuadletReference type value;
|
||||
in if (hasAttr name cfg.internal.builtQuadlets) then
|
||||
[ (cfg.internal.builtQuadlets.${name}) ]
|
||||
else
|
||||
[ ]
|
||||
let
|
||||
name = extractQuadletReference type value;
|
||||
in
|
||||
if (hasAttr name cfg.internal.builtQuadlets) then [ (cfg.internal.builtQuadlets.${name}) ] else [ ]
|
||||
else
|
||||
[ ];
|
||||
|
||||
withResolverFor = type: value:
|
||||
let resolve = v: dependencyBySuffix type v;
|
||||
in if builtins.isList value then
|
||||
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)
|
||||
dependencyServices =
|
||||
(withResolverFor "image" containerDef.image)
|
||||
++ (withResolverFor "build" containerDef.image)
|
||||
++ (withResolverFor "network" containerDef.network)
|
||||
++ (withResolverFor "volume" containerDef.volumes);
|
||||
|
||||
checkQuadletReference = types: value:
|
||||
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
|
||||
type = findFirst (t: hasInfix ".${t}" value) null types;
|
||||
in
|
||||
if (type != null) then
|
||||
let
|
||||
quadletName = extractQuadletReference type value;
|
||||
quadletsOfType =
|
||||
filterAttrs (n: v: v.quadletData.resourceType == type)
|
||||
cfg.internal.builtQuadlets;
|
||||
in if (hasAttr quadletName quadletsOfType) then
|
||||
quadletsOfType = filterAttrs (n: v: v.quadletData.resourceType == type) cfg.internal.builtQuadlets;
|
||||
in
|
||||
if (hasAttr quadletName quadletsOfType) then
|
||||
[
|
||||
(replaceStrings [ quadletName ] [ "podman-${quadletName}" ]
|
||||
value)
|
||||
(replaceStrings [ quadletName ] [ "podman-${quadletName}" ] value)
|
||||
]
|
||||
else
|
||||
[ value ]
|
||||
else if ((hasInfix "/nix/store" value) == false
|
||||
&& hasAttr value cfg.internal.builtQuadlets) then
|
||||
else if ((hasInfix "/nix/store" value) == false && hasAttr value cfg.internal.builtQuadlets) then
|
||||
lib.warn ''
|
||||
A value for Podman container '${name}' might use a reference to another quadlet: ${value}.
|
||||
Append the type '.${
|
||||
|
|
@ -66,57 +78,63 @@ let
|
|||
else
|
||||
[ value ];
|
||||
|
||||
quadlet = (podman-lib.deepMerge {
|
||||
Container = {
|
||||
AddCapability = containerDef.addCapabilities;
|
||||
AddDevice = containerDef.devices;
|
||||
AutoUpdate = containerDef.autoUpdate;
|
||||
ContainerName = name;
|
||||
DropCapability = containerDef.dropCapabilities;
|
||||
Entrypoint = containerDef.entrypoint;
|
||||
Environment = containerDef.environment;
|
||||
EnvironmentFile = containerDef.environmentFile;
|
||||
Exec = containerDef.exec;
|
||||
Group = containerDef.group;
|
||||
Image = checkQuadletReference [ "build" "image" ] containerDef.image;
|
||||
IP = containerDef.ip4;
|
||||
IP6 = containerDef.ip6;
|
||||
Label =
|
||||
(containerDef.labels // { "nix.home-manager.managed" = true; });
|
||||
Network = checkQuadletReference [ "network" ] containerDef.network;
|
||||
NetworkAlias = containerDef.networkAlias;
|
||||
PodmanArgs = containerDef.extraPodmanArgs;
|
||||
PublishPort = containerDef.ports;
|
||||
UserNS = containerDef.userNS;
|
||||
User = containerDef.user;
|
||||
Volume = checkQuadletReference [ "volume" ] containerDef.volumes;
|
||||
};
|
||||
Install = {
|
||||
WantedBy = optionals containerDef.autoStart [
|
||||
"default.target"
|
||||
"multi-user.target"
|
||||
];
|
||||
};
|
||||
Service = {
|
||||
Environment = {
|
||||
PATH = (builtins.concatStringsSep ":" [
|
||||
"/run/wrappers/bin"
|
||||
"/run/current-system/sw/bin"
|
||||
"${config.home.homeDirectory}/.nix-profile/bin"
|
||||
"${pkgs.systemd}/bin"
|
||||
]);
|
||||
quadlet = (
|
||||
podman-lib.deepMerge {
|
||||
Container = {
|
||||
AddCapability = containerDef.addCapabilities;
|
||||
AddDevice = containerDef.devices;
|
||||
AutoUpdate = containerDef.autoUpdate;
|
||||
ContainerName = name;
|
||||
DropCapability = containerDef.dropCapabilities;
|
||||
Entrypoint = containerDef.entrypoint;
|
||||
Environment = containerDef.environment;
|
||||
EnvironmentFile = containerDef.environmentFile;
|
||||
Exec = containerDef.exec;
|
||||
Group = containerDef.group;
|
||||
Image = checkQuadletReference [ "build" "image" ] containerDef.image;
|
||||
IP = containerDef.ip4;
|
||||
IP6 = containerDef.ip6;
|
||||
Label = (containerDef.labels // { "nix.home-manager.managed" = true; });
|
||||
Network = checkQuadletReference [ "network" ] containerDef.network;
|
||||
NetworkAlias = containerDef.networkAlias;
|
||||
PodmanArgs = containerDef.extraPodmanArgs;
|
||||
PublishPort = containerDef.ports;
|
||||
UserNS = containerDef.userNS;
|
||||
User = containerDef.user;
|
||||
Volume = checkQuadletReference [ "volume" ] containerDef.volumes;
|
||||
};
|
||||
Restart = "always";
|
||||
TimeoutStopSec = 30;
|
||||
};
|
||||
Unit = {
|
||||
Description = (if (builtins.isString containerDef.description) then
|
||||
containerDef.description
|
||||
else
|
||||
"Service for container ${name}");
|
||||
};
|
||||
} containerDef.extraConfig);
|
||||
in {
|
||||
Install = {
|
||||
WantedBy = optionals containerDef.autoStart [
|
||||
"default.target"
|
||||
"multi-user.target"
|
||||
];
|
||||
};
|
||||
Service = {
|
||||
Environment = {
|
||||
PATH = (
|
||||
builtins.concatStringsSep ":" [
|
||||
"/run/wrappers/bin"
|
||||
"/run/current-system/sw/bin"
|
||||
"${config.home.homeDirectory}/.nix-profile/bin"
|
||||
"${pkgs.systemd}/bin"
|
||||
]
|
||||
);
|
||||
};
|
||||
Restart = "always";
|
||||
TimeoutStopSec = 30;
|
||||
};
|
||||
Unit = {
|
||||
Description = (
|
||||
if (builtins.isString containerDef.description) then
|
||||
containerDef.description
|
||||
else
|
||||
"Service for container ${name}"
|
||||
);
|
||||
};
|
||||
} containerDef.extraConfig
|
||||
);
|
||||
in
|
||||
{
|
||||
dependencies = dependencyServices;
|
||||
attrs = quadlet;
|
||||
text = ''
|
||||
|
|
@ -128,14 +146,16 @@ let
|
|||
'';
|
||||
};
|
||||
|
||||
toQuadletInternal = name: containerDef:
|
||||
let src = createQuadletSource name containerDef;
|
||||
in {
|
||||
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'
|
||||
serviceName = "podman-${src.attrs.Container.ContainerName}"; # generated service name: 'podman-<name>.service'
|
||||
source = podman-lib.removeBlankLines src.text;
|
||||
};
|
||||
|
||||
|
|
@ -146,7 +166,10 @@ let
|
|||
addCapabilities = mkOption {
|
||||
type = with types; listOf str;
|
||||
default = [ ];
|
||||
example = [ "CAP_DAC_OVERRIDE" "CAP_IPC_OWNER" ];
|
||||
example = [
|
||||
"CAP_DAC_OVERRIDE"
|
||||
"CAP_IPC_OWNER"
|
||||
];
|
||||
description = "The capabilities to add to the container.";
|
||||
};
|
||||
|
||||
|
|
@ -159,7 +182,11 @@ let
|
|||
};
|
||||
|
||||
autoUpdate = mkOption {
|
||||
type = types.enum [ null "registry" "local" ];
|
||||
type = types.enum [
|
||||
null
|
||||
"registry"
|
||||
"local"
|
||||
];
|
||||
default = null;
|
||||
example = "registry";
|
||||
description = "The autoupdate policy for the container.";
|
||||
|
|
@ -182,7 +209,10 @@ let
|
|||
dropCapabilities = mkOption {
|
||||
type = with types; listOf str;
|
||||
default = [ ];
|
||||
example = [ "CAP_DAC_OVERRIDE" "CAP_IPC_OWNER" ];
|
||||
example = [
|
||||
"CAP_DAC_OVERRIDE"
|
||||
"CAP_IPC_OWNER"
|
||||
];
|
||||
description = "The capabilities to drop from the container.";
|
||||
};
|
||||
|
||||
|
|
@ -209,7 +239,10 @@ let
|
|||
environmentFile = mkOption {
|
||||
type = with types; listOf str;
|
||||
default = [ ];
|
||||
example = [ "/etc/environment" "/etc/other-env" ];
|
||||
example = [
|
||||
"/etc/environment"
|
||||
"/etc/other-env"
|
||||
];
|
||||
description = ''
|
||||
Paths to files containing container environment variables.
|
||||
'';
|
||||
|
|
@ -304,14 +337,20 @@ let
|
|||
networkAlias = mkOption {
|
||||
type = with types; listOf str;
|
||||
default = [ ];
|
||||
example = [ "mycontainer" "web" ];
|
||||
example = [
|
||||
"mycontainer"
|
||||
"web"
|
||||
];
|
||||
description = "Network aliases for the container.";
|
||||
};
|
||||
|
||||
ports = mkOption {
|
||||
type = with types; listOf str;
|
||||
default = [ ];
|
||||
example = [ "8080:80" "8443:443" ];
|
||||
example = [
|
||||
"8080:80"
|
||||
"8443:443"
|
||||
];
|
||||
description = "A mapping of ports between host and container";
|
||||
};
|
||||
|
||||
|
|
@ -330,14 +369,18 @@ let
|
|||
volumes = mkOption {
|
||||
type = with types; listOf str;
|
||||
default = [ ];
|
||||
example = [ "/tmp:/tmp" "/var/run/test.secret:/etc/secret:ro" ];
|
||||
example = [
|
||||
"/tmp:/tmp"
|
||||
"/var/run/test.secret:/etc/secret:ro"
|
||||
];
|
||||
description = "The volumes to mount into the container.";
|
||||
};
|
||||
|
||||
};
|
||||
};
|
||||
|
||||
in {
|
||||
in
|
||||
{
|
||||
|
||||
imports = [ ./options.nix ];
|
||||
|
||||
|
|
@ -348,11 +391,12 @@ in {
|
|||
};
|
||||
|
||||
config =
|
||||
let containerQuadlets = mapAttrsToList toQuadletInternal cfg.containers;
|
||||
in mkIf cfg.enable {
|
||||
let
|
||||
containerQuadlets = mapAttrsToList toQuadletInternal cfg.containers;
|
||||
in
|
||||
mkIf cfg.enable {
|
||||
services.podman.internal.quadletDefinitions = containerQuadlets;
|
||||
assertions =
|
||||
flatten (map (container: container.assertions) containerQuadlets);
|
||||
assertions = flatten (map (container: container.assertions) containerQuadlets);
|
||||
|
||||
# manifest file
|
||||
xdg.configFile."podman/containers.manifest".text =
|
||||
|
|
|
|||
|
|
@ -1,9 +1,18 @@
|
|||
{ config, pkgs, lib, ... }:
|
||||
{
|
||||
config,
|
||||
pkgs,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
let
|
||||
cfg = config.services.podman;
|
||||
toml = pkgs.formats.toml { };
|
||||
in {
|
||||
meta.maintainers = with lib.hm.maintainers; [ bamhm182 n-hass ];
|
||||
in
|
||||
{
|
||||
meta.maintainers = with lib.hm.maintainers; [
|
||||
bamhm182
|
||||
n-hass
|
||||
];
|
||||
|
||||
imports = [
|
||||
./builds.nix
|
||||
|
|
@ -79,8 +88,7 @@ in {
|
|||
};
|
||||
|
||||
config = lib.mkIf cfg.enable {
|
||||
assertions =
|
||||
[ (lib.hm.assertions.assertPlatform "podman" pkgs lib.platforms.linux) ];
|
||||
assertions = [ (lib.hm.assertions.assertPlatform "podman" pkgs lib.platforms.linux) ];
|
||||
|
||||
home.packages = [ cfg.package ];
|
||||
|
||||
|
|
@ -89,18 +97,16 @@ in {
|
|||
};
|
||||
|
||||
xdg.configFile = {
|
||||
"containers/policy.json".source = if cfg.settings.policy != { } then
|
||||
pkgs.writeText "policy.json" (builtins.toJSON cfg.settings.policy)
|
||||
else
|
||||
"${pkgs.skopeo.policy}/default-policy.json";
|
||||
"containers/policy.json".source =
|
||||
if cfg.settings.policy != { } then
|
||||
pkgs.writeText "policy.json" (builtins.toJSON cfg.settings.policy)
|
||||
else
|
||||
"${pkgs.skopeo.policy}/default-policy.json";
|
||||
"containers/registries.conf".source = toml.generate "registries.conf" {
|
||||
registries =
|
||||
lib.mapAttrs (n: v: { registries = v; }) cfg.settings.registries;
|
||||
registries = lib.mapAttrs (n: v: { registries = v; }) cfg.settings.registries;
|
||||
};
|
||||
"containers/storage.conf".source =
|
||||
toml.generate "storage.conf" cfg.settings.storage;
|
||||
"containers/containers.conf".source =
|
||||
toml.generate "containers.conf" cfg.settings.containers;
|
||||
"containers/storage.conf".source = toml.generate "storage.conf" cfg.settings.storage;
|
||||
"containers/containers.conf".source = toml.generate "containers.conf" cfg.settings.containers;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,11 +1,17 @@
|
|||
{ config, lib, pkgs, ... }:
|
||||
{
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
with lib;
|
||||
let
|
||||
cfg = config.services.podman;
|
||||
|
||||
podman-lib = import ./podman-lib.nix { inherit pkgs lib config; };
|
||||
|
||||
createQuadletSource = name: imageDef:
|
||||
createQuadletSource =
|
||||
name: imageDef:
|
||||
let
|
||||
credsString =
|
||||
(if imageDef.username != null then imageDef.username else "")
|
||||
|
|
@ -33,9 +39,12 @@ let
|
|||
TimeoutStartSec = 300;
|
||||
RemainAfterExit = "yes";
|
||||
};
|
||||
Unit = { Description = imageDef.description; };
|
||||
Unit = {
|
||||
Description = imageDef.description;
|
||||
};
|
||||
} imageDef.extraConfig;
|
||||
in ''
|
||||
in
|
||||
''
|
||||
# Automatically generated by home-manager for podman image configuration
|
||||
# DO NOT EDIT THIS FILE DIRECTLY
|
||||
#
|
||||
|
|
@ -45,118 +54,118 @@ let
|
|||
|
||||
toQuadletInternal = name: imageDef: {
|
||||
assertions = podman-lib.buildConfigAsserts name imageDef.extraConfig;
|
||||
serviceName =
|
||||
"podman-${name}"; # generated service name: 'podman-<name>-image.service
|
||||
serviceName = "podman-${name}"; # generated 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.";
|
||||
};
|
||||
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.";
|
||||
};
|
||||
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.";
|
||||
};
|
||||
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 decryption of images.";
|
||||
};
|
||||
decryptionKeyFile = mkOption {
|
||||
type = with types; nullOr path;
|
||||
default = null;
|
||||
description = "Path to key used for decryption 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.";
|
||||
};
|
||||
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.";
|
||||
};
|
||||
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.";
|
||||
};
|
||||
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.";
|
||||
};
|
||||
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)";
|
||||
};
|
||||
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.";
|
||||
};
|
||||
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.";
|
||||
};
|
||||
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.";
|
||||
};
|
||||
username = mkOption {
|
||||
type = with types; nullOr str;
|
||||
default = null;
|
||||
example = "bob";
|
||||
description = "Username used to connect to registry.";
|
||||
};
|
||||
|
||||
};
|
||||
});
|
||||
in {
|
||||
};
|
||||
}
|
||||
);
|
||||
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);
|
||||
};
|
||||
config =
|
||||
let
|
||||
imageQuadlets = mapAttrsToList toQuadletInternal cfg.images;
|
||||
in
|
||||
mkIf cfg.enable {
|
||||
services.podman.internal.quadletDefinitions = imageQuadlets;
|
||||
assertions = flatten (map (image: image.assertions) imageQuadlets);
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,9 @@
|
|||
{ config, lib, pkgs, ... }:
|
||||
{
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
|
||||
with lib;
|
||||
|
||||
|
|
@ -11,7 +16,8 @@ let
|
|||
activationCleanupScript = activation.cleanup;
|
||||
|
||||
# derivation to build a single Podman quadlet, outputting its systemd unit files
|
||||
buildPodmanQuadlet = quadlet:
|
||||
buildPodmanQuadlet =
|
||||
quadlet:
|
||||
pkgs.stdenv.mkDerivation {
|
||||
name = "home-${quadlet.resourceType}-${quadlet.serviceName}";
|
||||
|
||||
|
|
@ -19,9 +25,12 @@ let
|
|||
|
||||
unpackPhase = ''
|
||||
mkdir -p $out/quadlets
|
||||
${concatStringsSep "\n" (map (v:
|
||||
"echo 'linking ${v.quadletData.serviceName}.${v.quadletData.resourceType}'; ln -s ${v.out}/quadlets/${v.quadletData.serviceName}.${v.quadletData.resourceType} $out/quadlets")
|
||||
quadlet.dependencies)}
|
||||
${concatStringsSep "\n" (
|
||||
map (
|
||||
v:
|
||||
"echo 'linking ${v.quadletData.serviceName}.${v.quadletData.resourceType}'; ln -s ${v.out}/quadlets/${v.quadletData.serviceName}.${v.quadletData.resourceType} $out/quadlets"
|
||||
) quadlet.dependencies
|
||||
)}
|
||||
'';
|
||||
|
||||
installPhase = ''
|
||||
|
|
@ -44,52 +53,68 @@ let
|
|||
|
||||
builtQuadlets = map buildPodmanQuadlet cfg.internal.quadletDefinitions;
|
||||
|
||||
accumulateUnitFiles = prefix: path: quadlet:
|
||||
accumulateUnitFiles =
|
||||
prefix: path: quadlet:
|
||||
let
|
||||
entries = builtins.readDir path;
|
||||
processEntry = name: type:
|
||||
processEntry =
|
||||
name: type:
|
||||
let
|
||||
newPath = "${path}/${name}";
|
||||
newPrefix = prefix + (if prefix == "" then "" else "/") + name;
|
||||
in if type == "directory" then
|
||||
in
|
||||
if type == "directory" then
|
||||
accumulateUnitFiles newPrefix newPath quadlet
|
||||
else [{
|
||||
key = newPrefix;
|
||||
value = {
|
||||
path = newPath;
|
||||
parentQuadlet = quadlet;
|
||||
};
|
||||
}];
|
||||
in flatten
|
||||
(map (name: processEntry name (getAttr name entries)) (attrNames entries));
|
||||
else
|
||||
[
|
||||
{
|
||||
key = newPrefix;
|
||||
value = {
|
||||
path = newPath;
|
||||
parentQuadlet = quadlet;
|
||||
};
|
||||
}
|
||||
];
|
||||
in
|
||||
flatten (map (name: processEntry name (getAttr name entries)) (attrNames entries));
|
||||
|
||||
allUnitFiles = concatMap (builtQuadlet:
|
||||
accumulateUnitFiles "" "${builtQuadlet.outPath}/units"
|
||||
builtQuadlet.quadletData) builtQuadlets;
|
||||
allUnitFiles = concatMap (
|
||||
builtQuadlet: accumulateUnitFiles "" "${builtQuadlet.outPath}/units" builtQuadlet.quadletData
|
||||
) builtQuadlets;
|
||||
|
||||
# we're doing this because the home-manager recursive file linking implementation can't
|
||||
# merge from multiple sources. so we link each file explicitly, which is fine for all unique files
|
||||
generateSystemdFileLinks = files:
|
||||
listToAttrs (map (unitFile: {
|
||||
name = "${config.xdg.configHome}/systemd/user/${unitFile.key}";
|
||||
value = { source = unitFile.value.path; };
|
||||
}) files);
|
||||
generateSystemdFileLinks =
|
||||
files:
|
||||
listToAttrs (
|
||||
map (unitFile: {
|
||||
name = "${config.xdg.configHome}/systemd/user/${unitFile.key}";
|
||||
value = {
|
||||
source = unitFile.value.path;
|
||||
};
|
||||
}) files
|
||||
);
|
||||
|
||||
in {
|
||||
in
|
||||
{
|
||||
imports = [ ./options.nix ];
|
||||
|
||||
config = mkIf cfg.enable {
|
||||
home.file = generateSystemdFileLinks allUnitFiles;
|
||||
|
||||
# if the length of builtQuadlets is 0, then we don't need register the activation script
|
||||
home.activation.podmanQuadletCleanup =
|
||||
lib.mkIf (lib.length builtQuadlets >= 1)
|
||||
(lib.hm.dag.entryAfter [ "reloadSystemd" ] activationCleanupScript);
|
||||
home.activation.podmanQuadletCleanup = 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) + "."
|
||||
+ pkg.passthru.quadletData.resourceType;
|
||||
value = pkg;
|
||||
}) builtQuadlets);
|
||||
services.podman.internal.builtQuadlets = listToAttrs (
|
||||
map (pkg: {
|
||||
name =
|
||||
(removePrefix "podman-" pkg.passthru.quadletData.serviceName)
|
||||
+ "."
|
||||
+ pkg.passthru.quadletData.resourceType;
|
||||
value = pkg;
|
||||
}) builtQuadlets
|
||||
);
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,9 @@
|
|||
{ config, lib, pkgs, ... }:
|
||||
{
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
|
||||
with lib;
|
||||
|
||||
|
|
@ -7,45 +12,62 @@ let
|
|||
|
||||
podman-lib = import ./podman-lib.nix { inherit pkgs lib config; };
|
||||
|
||||
createQuadletSource = name: networkDef:
|
||||
createQuadletSource =
|
||||
name: networkDef:
|
||||
let
|
||||
quadlet = (podman-lib.deepMerge {
|
||||
Install = {
|
||||
WantedBy = (if networkDef.autoStart then [
|
||||
"default.target"
|
||||
"multi-user.target"
|
||||
] else
|
||||
[ ]);
|
||||
};
|
||||
Network = {
|
||||
Driver = networkDef.driver;
|
||||
Gateway = networkDef.gateway;
|
||||
Internal = networkDef.internal;
|
||||
NetworkName = name;
|
||||
Label = networkDef.labels // { "nix.home-manager.managed" = true; };
|
||||
PodmanArgs = networkDef.extraPodmanArgs;
|
||||
Subnet = networkDef.subnet;
|
||||
};
|
||||
Service = {
|
||||
Environment = {
|
||||
PATH = (builtins.concatStringsSep ":" [
|
||||
"${podman-lib.newuidmapPaths}"
|
||||
"${makeBinPath [ pkgs.su pkgs.coreutils ]}"
|
||||
]);
|
||||
quadlet = (
|
||||
podman-lib.deepMerge {
|
||||
Install = {
|
||||
WantedBy = (
|
||||
if networkDef.autoStart then
|
||||
[
|
||||
"default.target"
|
||||
"multi-user.target"
|
||||
]
|
||||
else
|
||||
[ ]
|
||||
);
|
||||
};
|
||||
ExecStartPre = [ "${podman-lib.awaitPodmanUnshare}" ];
|
||||
TimeoutStartSec = 15;
|
||||
RemainAfterExit = "yes";
|
||||
};
|
||||
Unit = {
|
||||
After = [ "network.target" ];
|
||||
Description = (if (builtins.isString networkDef.description) then
|
||||
networkDef.description
|
||||
else
|
||||
"Service for network ${name}");
|
||||
};
|
||||
} networkDef.extraConfig);
|
||||
in ''
|
||||
Network = {
|
||||
Driver = networkDef.driver;
|
||||
Gateway = networkDef.gateway;
|
||||
Internal = networkDef.internal;
|
||||
NetworkName = name;
|
||||
Label = networkDef.labels // {
|
||||
"nix.home-manager.managed" = true;
|
||||
};
|
||||
PodmanArgs = networkDef.extraPodmanArgs;
|
||||
Subnet = networkDef.subnet;
|
||||
};
|
||||
Service = {
|
||||
Environment = {
|
||||
PATH = (
|
||||
builtins.concatStringsSep ":" [
|
||||
"${podman-lib.newuidmapPaths}"
|
||||
"${makeBinPath [
|
||||
pkgs.su
|
||||
pkgs.coreutils
|
||||
]}"
|
||||
]
|
||||
);
|
||||
};
|
||||
ExecStartPre = [ "${podman-lib.awaitPodmanUnshare}" ];
|
||||
TimeoutStartSec = 15;
|
||||
RemainAfterExit = "yes";
|
||||
};
|
||||
Unit = {
|
||||
After = [ "network.target" ];
|
||||
Description = (
|
||||
if (builtins.isString networkDef.description) then
|
||||
networkDef.description
|
||||
else
|
||||
"Service for network ${name}"
|
||||
);
|
||||
};
|
||||
} networkDef.extraConfig
|
||||
);
|
||||
in
|
||||
''
|
||||
# Automatically generated by home-manager for podman network configuration
|
||||
# DO NOT EDIT THIS FILE DIRECTLY
|
||||
#
|
||||
|
|
@ -55,13 +77,13 @@ let
|
|||
|
||||
toQuadletInternal = name: networkDef: {
|
||||
assertions = podman-lib.buildConfigAsserts name networkDef.extraConfig;
|
||||
serviceName =
|
||||
"podman-${name}"; # generated service name: 'podman-<name>-network.service'
|
||||
serviceName = "podman-${name}"; # generated service name: 'podman-<name>-network.service'
|
||||
source = podman-lib.removeBlankLines (createQuadletSource name networkDef);
|
||||
resourceType = "network";
|
||||
};
|
||||
|
||||
in let
|
||||
in
|
||||
let
|
||||
networkDefinitionType = types.submodule {
|
||||
options = {
|
||||
|
||||
|
|
@ -106,7 +128,10 @@ in let
|
|||
extraPodmanArgs = mkOption {
|
||||
type = with types; listOf str;
|
||||
default = [ ];
|
||||
example = [ "--dns=192.168.55.1" "--ipam-driver" ];
|
||||
example = [
|
||||
"--dns=192.168.55.1"
|
||||
"--ipam-driver"
|
||||
];
|
||||
description = ''
|
||||
Extra arguments to pass to the podman network create command.
|
||||
'';
|
||||
|
|
@ -144,19 +169,22 @@ in let
|
|||
|
||||
};
|
||||
};
|
||||
in {
|
||||
in
|
||||
{
|
||||
options.services.podman.networks = mkOption {
|
||||
type = types.attrsOf networkDefinitionType;
|
||||
default = { };
|
||||
description = "Defines Podman network quadlet configurations.";
|
||||
};
|
||||
|
||||
config = let networkQuadlets = mapAttrsToList toQuadletInternal cfg.networks;
|
||||
in mkIf cfg.enable {
|
||||
services.podman.internal.quadletDefinitions = networkQuadlets;
|
||||
assertions = flatten (map (network: network.assertions) networkQuadlets);
|
||||
config =
|
||||
let
|
||||
networkQuadlets = mapAttrsToList toQuadletInternal cfg.networks;
|
||||
in
|
||||
mkIf cfg.enable {
|
||||
services.podman.internal.quadletDefinitions = networkQuadlets;
|
||||
assertions = flatten (map (network: network.assertions) networkQuadlets);
|
||||
|
||||
xdg.configFile."podman/networks.manifest".text =
|
||||
podman-lib.generateManifestText networkQuadlets;
|
||||
};
|
||||
xdg.configFile."podman/networks.manifest".text = podman-lib.generateManifestText networkQuadlets;
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -38,7 +38,8 @@ let
|
|||
};
|
||||
};
|
||||
};
|
||||
in {
|
||||
in
|
||||
{
|
||||
options.services.podman = {
|
||||
internal = {
|
||||
quadletDefinitions = lib.mkOption {
|
||||
|
|
|
|||
|
|
@ -1,21 +1,36 @@
|
|||
{ pkgs, lib, config, ... }:
|
||||
{
|
||||
pkgs,
|
||||
lib,
|
||||
config,
|
||||
...
|
||||
}:
|
||||
with lib;
|
||||
let
|
||||
normalizeKeyValue = k: v:
|
||||
normalizeKeyValue =
|
||||
k: v:
|
||||
let
|
||||
v' = if builtins.isBool v then
|
||||
(if v then "true" else "false")
|
||||
else if builtins.isAttrs v then
|
||||
(concatStringsSep ''
|
||||
v' =
|
||||
if builtins.isBool v then
|
||||
(if v then "true" else "false")
|
||||
else if builtins.isAttrs v then
|
||||
(concatStringsSep ''
|
||||
|
||||
${k}='' (mapAttrsToList normalizeKeyValue v))
|
||||
else
|
||||
builtins.toString v;
|
||||
in if builtins.isNull v then "" else "${k}=${v'}";
|
||||
${k}='' (mapAttrsToList normalizeKeyValue v))
|
||||
else
|
||||
builtins.toString v;
|
||||
in
|
||||
if builtins.isNull v then "" else "${k}=${v'}";
|
||||
|
||||
primitiveAttrs = with types; attrsOf (either primitive (listOf primitive));
|
||||
primitiveList = with types; listOf primitive;
|
||||
primitive = with types; nullOr (oneOf [ bool int str path ]);
|
||||
primitive =
|
||||
with types;
|
||||
nullOr (oneOf [
|
||||
bool
|
||||
int
|
||||
str
|
||||
path
|
||||
]);
|
||||
|
||||
toQuadletIni = generators.toINI {
|
||||
listsAsDuplicateKeys = true;
|
||||
|
|
@ -23,17 +38,22 @@ let
|
|||
};
|
||||
|
||||
# meant for ini. favours b when two values are unmergeable
|
||||
deepMerge = a: b:
|
||||
foldl' (result: key:
|
||||
deepMerge =
|
||||
a: b:
|
||||
foldl' (
|
||||
result: key:
|
||||
let
|
||||
aVal = if builtins.hasAttr key a then a.${key} else null;
|
||||
bVal = if builtins.hasAttr key b then b.${key} else null;
|
||||
|
||||
# check if the types inside a list match the type of a primitive
|
||||
listMatchesType = list: val:
|
||||
isList list && builtins.length list > 0
|
||||
listMatchesType =
|
||||
list: val:
|
||||
isList list
|
||||
&& builtins.length list > 0
|
||||
&& builtins.typeOf (builtins.head list) == builtins.typeOf val;
|
||||
in if isAttrs aVal && isAttrs bVal then
|
||||
in
|
||||
if isAttrs aVal && isAttrs bVal then
|
||||
result // { ${key} = deepMerge aVal bVal; }
|
||||
else if isList aVal && isList bVal then
|
||||
result // { ${key} = aVal ++ bVal; }
|
||||
|
|
@ -50,61 +70,90 @@ let
|
|||
else if builtins.typeOf aVal == builtins.typeOf bVal then
|
||||
result // { ${key} = bVal; }
|
||||
else
|
||||
result // { ${key} = bVal; }) a (builtins.attrNames b);
|
||||
in {
|
||||
result // { ${key} = bVal; }
|
||||
) a (builtins.attrNames b);
|
||||
in
|
||||
{
|
||||
inherit primitiveAttrs;
|
||||
inherit primitiveList;
|
||||
inherit primitive;
|
||||
inherit toQuadletIni;
|
||||
inherit deepMerge;
|
||||
|
||||
buildConfigAsserts = quadletName: extraConfig:
|
||||
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 ]; };
|
||||
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.
|
||||
buildSectionAsserts = section: attrs:
|
||||
buildSectionAsserts =
|
||||
section: attrs:
|
||||
if builtins.hasAttr section configRules then
|
||||
flatten (mapAttrsToList (attrName: attrValue:
|
||||
if builtins.hasAttr attrName configRules.${section} then [{
|
||||
assertion = configRules.${section}.${attrName}.check attrValue;
|
||||
message = "In '${quadletName}' config. ${section}.${attrName}: '${
|
||||
toString attrValue
|
||||
}' does not match expected type: ${
|
||||
configRules.${section}.${attrName}.description
|
||||
}";
|
||||
}] else
|
||||
[ ]) attrs)
|
||||
flatten (
|
||||
mapAttrsToList (
|
||||
attrName: attrValue:
|
||||
if builtins.hasAttr attrName configRules.${section} then
|
||||
[
|
||||
{
|
||||
assertion = configRules.${section}.${attrName}.check attrValue;
|
||||
message = "In '${quadletName}' config. ${section}.${attrName}: '${toString attrValue}' does not match expected type: ${
|
||||
configRules.${section}.${attrName}.description
|
||||
}";
|
||||
}
|
||||
]
|
||||
else
|
||||
[ ]
|
||||
) attrs
|
||||
)
|
||||
else
|
||||
[ ];
|
||||
|
||||
checkImageTag = extraConfig:
|
||||
checkImageTag =
|
||||
extraConfig:
|
||||
let
|
||||
imageTags = (extraConfig.Build or { }).ImageTag or [ ];
|
||||
containsRequiredTag =
|
||||
builtins.elem "homemanager/${quadletName}" imageTags;
|
||||
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}'.'';
|
||||
}];
|
||||
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 (concatLists [
|
||||
in
|
||||
flatten (concatLists [
|
||||
(mapAttrsToList buildSectionAsserts extraConfig)
|
||||
(checkImageTag extraConfig)
|
||||
]);
|
||||
|
||||
extraConfigType = with types;
|
||||
attrsOf (attrsOf (oneOf [ primitiveAttrs primitiveList primitive ]));
|
||||
extraConfigType =
|
||||
with types;
|
||||
attrsOf (
|
||||
attrsOf (oneOf [
|
||||
primitiveAttrs
|
||||
primitiveList
|
||||
primitive
|
||||
])
|
||||
);
|
||||
|
||||
# input expects a list of quadletInternalType with all the same resourceType
|
||||
generateManifestText = quadlets:
|
||||
generateManifestText =
|
||||
quadlets:
|
||||
let
|
||||
# create a list of all unique quadlet.resourceType in quadlets
|
||||
quadletTypes = unique (map (quadlet: quadlet.resourceType) quadlets);
|
||||
|
|
@ -113,22 +162,27 @@ in {
|
|||
|
||||
# ensures the service name is formatted correctly to be easily read
|
||||
# by the activation script and matches `podman <resource> ls` output
|
||||
formatServiceName = quadlet:
|
||||
formatServiceName =
|
||||
quadlet:
|
||||
let
|
||||
# remove the podman- prefix from the service name string
|
||||
strippedName = lib.removePrefix "podman-" quadlet.serviceName;
|
||||
# specific logic for writing the unit name goes here. It should be
|
||||
# identical to what `podman <resource> ls` shows
|
||||
in {
|
||||
in
|
||||
{
|
||||
"build" = "localhost/homemanager/${strippedName}";
|
||||
"container" = strippedName;
|
||||
"network" = strippedName;
|
||||
"volume" = strippedName;
|
||||
}."${quadlet.resourceType}";
|
||||
in if allQuadletsSameType then ''
|
||||
${concatStringsSep "\n"
|
||||
(map (quadlet: formatServiceName quadlet) quadlets)}
|
||||
'' else
|
||||
}
|
||||
."${quadlet.resourceType}";
|
||||
in
|
||||
if allQuadletsSameType then
|
||||
''
|
||||
${concatStringsSep "\n" (map (quadlet: formatServiceName quadlet) quadlets)}
|
||||
''
|
||||
else
|
||||
abort ''
|
||||
All quadlets must be of the same type.
|
||||
Quadlet types in this manifest: ${concatStringsSep ", " quadletTypes}
|
||||
|
|
@ -143,11 +197,13 @@ in {
|
|||
# Tracking for a potential solution: https://github.com/NixOS/nixpkgs/issues/138423
|
||||
newuidmapPaths = "/run/wrappers/bin:/usr/bin:/bin:/usr/sbin:/sbin";
|
||||
|
||||
removeBlankLines = text:
|
||||
removeBlankLines =
|
||||
text:
|
||||
let
|
||||
lines = splitString "\n" text;
|
||||
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
|
||||
|
|
|
|||
|
|
@ -1,9 +1,16 @@
|
|||
{ config, lib, pkgs, ... }:
|
||||
{
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
|
||||
with lib;
|
||||
|
||||
let cfg = config.services.podman;
|
||||
in {
|
||||
let
|
||||
cfg = config.services.podman;
|
||||
in
|
||||
{
|
||||
options.services.podman = {
|
||||
autoUpdate = {
|
||||
enable = mkOption {
|
||||
|
|
@ -36,12 +43,12 @@ in {
|
|||
Service = {
|
||||
Type = "oneshot";
|
||||
Environment = "PATH=${
|
||||
builtins.concatStringsSep ":" [
|
||||
"/run/wrappers/bin"
|
||||
"/run/current-system/sw/bin"
|
||||
"${config.home.homeDirectory}/.nix-profile/bin"
|
||||
]
|
||||
}";
|
||||
builtins.concatStringsSep ":" [
|
||||
"/run/wrappers/bin"
|
||||
"/run/current-system/sw/bin"
|
||||
"${config.home.homeDirectory}/.nix-profile/bin"
|
||||
]
|
||||
}";
|
||||
ExecStart = "${cfg.package}/bin/podman auto-update";
|
||||
ExecStartPost = "${cfg.package}/bin/podman image prune -f";
|
||||
TimeoutStartSec = "300s";
|
||||
|
|
@ -50,7 +57,9 @@ in {
|
|||
};
|
||||
|
||||
systemd.user.timers."podman-auto-update" = {
|
||||
Unit = { Description = "Podman auto-update timer"; };
|
||||
Unit = {
|
||||
Description = "Podman auto-update timer";
|
||||
};
|
||||
|
||||
Timer = {
|
||||
OnCalendar = cfg.autoUpdate.onCalendar;
|
||||
|
|
@ -58,7 +67,9 @@ in {
|
|||
Persistent = true;
|
||||
};
|
||||
|
||||
Install = { WantedBy = [ "timers.target" ]; };
|
||||
Install = {
|
||||
WantedBy = [ "timers.target" ];
|
||||
};
|
||||
};
|
||||
})
|
||||
({
|
||||
|
|
@ -66,7 +77,14 @@ in {
|
|||
''
|
||||
[Service]
|
||||
ExecSearchPath=${
|
||||
makeBinPath (with pkgs; [ bashInteractive systemd coreutils ])
|
||||
makeBinPath (
|
||||
with pkgs;
|
||||
[
|
||||
bashInteractive
|
||||
systemd
|
||||
coreutils
|
||||
]
|
||||
)
|
||||
}:/bin
|
||||
'';
|
||||
})
|
||||
|
|
|
|||
|
|
@ -1,4 +1,9 @@
|
|||
{ config, lib, pkgs, ... }:
|
||||
{
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
|
||||
with lib;
|
||||
|
||||
|
|
@ -7,7 +12,8 @@ let
|
|||
|
||||
podman-lib = import ./podman-lib.nix { inherit pkgs lib config; };
|
||||
|
||||
createQuadletSource = name: volumeDef:
|
||||
createQuadletSource =
|
||||
name: volumeDef:
|
||||
let
|
||||
quadlet = podman-lib.deepMerge {
|
||||
Install = {
|
||||
|
|
@ -18,16 +24,23 @@ let
|
|||
};
|
||||
Service = {
|
||||
Environment = {
|
||||
PATH = (builtins.concatStringsSep ":" [
|
||||
"${podman-lib.newuidmapPaths}"
|
||||
"${makeBinPath [ pkgs.su pkgs.coreutils ]}"
|
||||
]);
|
||||
PATH = (
|
||||
builtins.concatStringsSep ":" [
|
||||
"${podman-lib.newuidmapPaths}"
|
||||
"${makeBinPath [
|
||||
pkgs.su
|
||||
pkgs.coreutils
|
||||
]}"
|
||||
]
|
||||
);
|
||||
};
|
||||
ExecStartPre = [ "${podman-lib.awaitPodmanUnshare}" ];
|
||||
TimeoutStartSec = 15;
|
||||
RemainAfterExit = "yes";
|
||||
};
|
||||
Unit = { Description = volumeDef.description; };
|
||||
Unit = {
|
||||
Description = volumeDef.description;
|
||||
};
|
||||
Volume = {
|
||||
Copy = volumeDef.copy;
|
||||
Device = volumeDef.device;
|
||||
|
|
@ -44,7 +57,8 @@ let
|
|||
VolumeName = name;
|
||||
};
|
||||
} volumeDef.extraConfig;
|
||||
in ''
|
||||
in
|
||||
''
|
||||
# Automatically generated by home-manager for podman volume configuration
|
||||
# DO NOT EDIT THIS FILE DIRECTLY
|
||||
#
|
||||
|
|
@ -54,133 +68,135 @@ let
|
|||
|
||||
toQuadletInternal = name: volumeDef: {
|
||||
assertions = podman-lib.buildConfigAsserts name volumeDef.extraConfig;
|
||||
serviceName =
|
||||
"podman-${name}"; # generated service name: 'podman-<name>-volume.service'
|
||||
serviceName = "podman-${name}"; # generated service name: 'podman-<name>-volume.service'
|
||||
source = podman-lib.removeBlankLines (createQuadletSource name volumeDef);
|
||||
resourceType = "volume";
|
||||
};
|
||||
|
||||
in let
|
||||
volumeDefinitionType = types.submodule ({ name, ... }: {
|
||||
options = {
|
||||
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";
|
||||
autoStart = mkOption {
|
||||
type = types.bool;
|
||||
default = true;
|
||||
description = "Whether to create the volume on boot.";
|
||||
};
|
||||
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
|
||||
'';
|
||||
};
|
||||
copy = mkOption {
|
||||
type = types.bool;
|
||||
default = true;
|
||||
description = "Copy content of the image located at the mountpoint of the volume on first run.";
|
||||
};
|
||||
|
||||
type = mkOption {
|
||||
type = with types; nullOr str;
|
||||
default = null;
|
||||
example = "tmpfs";
|
||||
description =
|
||||
"Filesystem type of Device. (used as -t in mount commands)";
|
||||
};
|
||||
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.";
|
||||
};
|
||||
|
||||
user = mkOption {
|
||||
type = with types; nullOr (either int str);
|
||||
default = null;
|
||||
description = "The user ID owning the volume inside the container.";
|
||||
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 {
|
||||
}
|
||||
);
|
||||
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);
|
||||
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;
|
||||
};
|
||||
xdg.configFile."podman/volumes.manifest".text = podman-lib.generateManifestText volumeQuadlets;
|
||||
};
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue