mirror of
https://github.com/nix-community/home-manager.git
synced 2026-06-05 21:02:51 +00:00
podman: fix container config mount on Darwin
On Darwin, `services.podman` mounts `~/.config/containers` into the Fedora CoreOS VM, but this did not work correctly for two reasons: * `xdg.configFile` creates symlinks into `/nix/store`, which are broken inside the guest. * The mount target `~/\.config/containers` is not canonical on Fedora CoreOS, so Podman rejects it. To fix this, we now: * materialize the generated Podman config files as real files with `runCommand` * sync them into `~/.config/containers` during activation, between `linkGeneration` and `podmanMachines` * use the canonical guest path `/var/home/<user>/.config/containers` Because adding the config directory to the volume mounts overrides the defaults, we also restore the default Podman volumes as the defaults for the `machines.<machine>.volumes` attribute while still allowing full overrides. This change does not affect Linux: `xdg.configFile` still produces store symlinks there. Closes #9327.
This commit is contained in:
committed by
Austin Horstman
parent
c53d0150e9
commit
3d64f2875e
@@ -83,15 +83,16 @@ let
|
||||
|
||||
volumes = mkOption {
|
||||
type = types.listOf types.str;
|
||||
default = [ ];
|
||||
example = [
|
||||
default = [
|
||||
"/Users:/Users"
|
||||
"/private:/private"
|
||||
"/var/folders:/var/folders"
|
||||
];
|
||||
example = [
|
||||
"/Users:/Users"
|
||||
];
|
||||
description = ''
|
||||
Volumes to mount in the machine, specified as source:target pairs.
|
||||
If empty, podman will use its default volume mounts.
|
||||
'';
|
||||
};
|
||||
|
||||
@@ -183,7 +184,6 @@ in
|
||||
timezone = "UTC";
|
||||
volumes = [
|
||||
"/Users:/Users"
|
||||
"/private:/private"
|
||||
];
|
||||
autoStart = true;
|
||||
watchdogInterval = 30;
|
||||
@@ -231,7 +231,11 @@ in
|
||||
swap = null;
|
||||
timezone = null;
|
||||
username = null;
|
||||
volumes = [ ];
|
||||
volumes = [
|
||||
"/Users:/Users"
|
||||
"/private:/private"
|
||||
"/var/folders:/var/folders"
|
||||
];
|
||||
autoStart = true;
|
||||
watchdogInterval = 30;
|
||||
};
|
||||
@@ -249,15 +253,18 @@ in
|
||||
];
|
||||
}
|
||||
|
||||
(mkIf pkgs.stdenv.isDarwin {
|
||||
(mkIf pkgs.stdenv.hostPlatform.isDarwin {
|
||||
home.activation.podmanMachines =
|
||||
let
|
||||
mkMachineInitScript =
|
||||
name: machine:
|
||||
let
|
||||
# Automatically mount host's container config into the VM
|
||||
# Automatically mount host's container config into the VM.
|
||||
# The guest target uses /var/home/<user> rather than /home/<user>
|
||||
# because on Fedora CoreOS /home is a filesystem-wide symlink
|
||||
# to /var/home, and podman requires a canonical mount path.
|
||||
username = if isNull machine.username then "core" else machine.username;
|
||||
configVolume = "$HOME/.config/containers:/home/${username}/.config/containers";
|
||||
configVolume = "$HOME/.config/containers:/var/home/${username}/.config/containers";
|
||||
allVolumes = [ configVolume ] ++ machine.volumes;
|
||||
in
|
||||
''
|
||||
|
||||
@@ -7,6 +7,22 @@
|
||||
let
|
||||
cfg = config.services.podman;
|
||||
toml = pkgs.formats.toml { };
|
||||
|
||||
configFiles = {
|
||||
"policy.json" =
|
||||
if cfg.settings.policy != { } then
|
||||
pkgs.writeText "policy.json" (builtins.toJSON cfg.settings.policy)
|
||||
else
|
||||
"${pkgs.skopeo.policy}/default-policy.json";
|
||||
"registries.conf" = toml.generate "registries.conf" {
|
||||
registries = lib.mapAttrs (_n: v: { registries = v; }) cfg.settings.registries;
|
||||
};
|
||||
"storage.conf" = toml.generate "storage.conf" cfg.settings.storage;
|
||||
"containers.conf" = toml.generate "containers.conf" cfg.settings.containers;
|
||||
}
|
||||
// lib.optionalAttrs (cfg.settings.mounts != [ ]) {
|
||||
"mounts.conf" = pkgs.writeText "mounts.conf" (builtins.concatStringsSep "\n" cfg.settings.mounts);
|
||||
};
|
||||
in
|
||||
{
|
||||
meta.maintainers = [
|
||||
@@ -25,6 +41,20 @@ in
|
||||
|
||||
package = lib.mkPackageOption pkgs "podman" { };
|
||||
|
||||
_configFiles = lib.mkOption {
|
||||
type = lib.types.attrsOf lib.types.path;
|
||||
internal = true;
|
||||
visible = false;
|
||||
readOnly = true;
|
||||
default = configFiles;
|
||||
description = ''
|
||||
Attribute set mapping `~/.config/containers/<name>` to the generated
|
||||
source path. Consumed by the Linux module to populate `xdg.configFile`
|
||||
and by the Darwin module to install the same files as real files into
|
||||
`~/.config/containers` for the podman machine bind mount.
|
||||
'';
|
||||
};
|
||||
|
||||
settings = {
|
||||
containers = lib.mkOption {
|
||||
inherit (toml) type;
|
||||
@@ -94,24 +124,28 @@ in
|
||||
|
||||
services.podman.settings.storage.storage.driver = lib.mkDefault "overlay";
|
||||
|
||||
# Configuration files are written to `$XDG_CONFIG_HOME/containers`
|
||||
# On Linux: podman reads them directly from this location
|
||||
# On Darwin: these files are automatically mounted into the podman machine VM
|
||||
# (see darwin.nix for the volume mount configuration)
|
||||
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/registries.conf".source = toml.generate "registries.conf" {
|
||||
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/mounts.conf" = lib.mkIf (cfg.settings.mounts != [ ]) {
|
||||
text = builtins.concatStringsSep "\n" cfg.settings.mounts;
|
||||
};
|
||||
};
|
||||
# On Linux, podman reads its configuration directly from
|
||||
# `$XDG_CONFIG_HOME/containers`. On Darwin the same files are placed there
|
||||
# as real files by an activation script and bind-mounted into the podman
|
||||
# machine VM (see darwin.nix).
|
||||
xdg.configFile = lib.mkIf pkgs.stdenv.hostPlatform.isLinux (
|
||||
lib.mapAttrs' (name: src: lib.nameValuePair "containers/${name}" { source = src; }) cfg._configFiles
|
||||
);
|
||||
home.activation.podmanContainersConfig = lib.mkIf pkgs.stdenv.hostPlatform.isDarwin (
|
||||
lib.hm.dag.entryBetween [ "podmanMachines" ] [ "linkGeneration" ] ''
|
||||
run mkdir -p "$HOME/.config/containers"
|
||||
|
||||
# Remove only files this module manages.
|
||||
for f in ${lib.escapeShellArgs (builtins.attrNames cfg._configFiles)}; do
|
||||
run rm -f "$HOME/.config/containers/$f"
|
||||
done
|
||||
|
||||
${lib.concatStringsSep "\n" (
|
||||
lib.mapAttrsToList (
|
||||
name: src: ''run install -m 0644 ${src} "$HOME/.config/containers/${name}"''
|
||||
) cfg._configFiles
|
||||
)}
|
||||
''
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -39,42 +39,50 @@
|
||||
};
|
||||
};
|
||||
|
||||
nmt.script = ''
|
||||
configPath=home-files/.config/containers
|
||||
containersFile=$configPath/containers.conf
|
||||
policyFile=$configPath/policy.json
|
||||
registriesFile=$configPath/registries.conf
|
||||
storageFile=$configPath/storage.conf
|
||||
mountsFile=$configPath/mounts.conf
|
||||
nmt.script =
|
||||
if pkgs.stdenv.hostPlatform.isDarwin then
|
||||
''
|
||||
# On Darwin, container config files are not part of the home-files
|
||||
# generation — they're installed into ~/.config/containers by an
|
||||
# activation script so they can be bind-mounted into the podman VM.
|
||||
assertFileExists activate
|
||||
assertFileRegex activate 'podmanContainersConfig'
|
||||
assertFileRegex activate 'install -m 0644'
|
||||
assertFileRegex activate 'policy\.json'
|
||||
assertFileRegex activate 'registries\.conf'
|
||||
assertFileRegex activate 'storage\.conf'
|
||||
assertFileRegex activate 'containers\.conf'
|
||||
assertFileRegex activate 'mounts\.conf'
|
||||
|
||||
# Check that config files are generated on both platforms
|
||||
assertFileExists $containersFile
|
||||
assertFileExists $policyFile
|
||||
assertFileExists $registriesFile
|
||||
assertFileExists $storageFile
|
||||
assertFileExists $mountsFile
|
||||
# Verify that config directory is automatically mounted into podman
|
||||
# machines at the canonical /var/home path
|
||||
assertFileRegex activate '\$HOME/\.config/containers:/var/home/core/\.config/containers'
|
||||
''
|
||||
else
|
||||
''
|
||||
configPath=home-files/.config/containers
|
||||
containersFile=$configPath/containers.conf
|
||||
policyFile=$configPath/policy.json
|
||||
registriesFile=$configPath/registries.conf
|
||||
storageFile=$configPath/storage.conf
|
||||
mountsFile=$configPath/mounts.conf
|
||||
|
||||
containersFile=$(normalizeStorePaths $containersFile)
|
||||
policyFile=$(normalizeStorePaths $policyFile)
|
||||
registriesFile=$(normalizeStorePaths $registriesFile)
|
||||
storageFile=$(normalizeStorePaths $storageFile)
|
||||
mountsFile=$(normalizeStorePaths $mountsFile)
|
||||
assertFileExists $containersFile
|
||||
assertFileExists $policyFile
|
||||
assertFileExists $registriesFile
|
||||
assertFileExists $storageFile
|
||||
assertFileExists $mountsFile
|
||||
|
||||
assertFileContent $containersFile ${./configuration-containers-expected.conf}
|
||||
assertFileContent $policyFile ${./configuration-policy-expected.json}
|
||||
assertFileContent $registriesFile ${./configuration-registries-expected.conf}
|
||||
assertFileContent $storageFile ${./configuration-storage-expected.conf}
|
||||
assertFileContent $mountsFile ${./configuration-mounts-expected.conf}
|
||||
containersFile=$(normalizeStorePaths $containersFile)
|
||||
policyFile=$(normalizeStorePaths $policyFile)
|
||||
registriesFile=$(normalizeStorePaths $registriesFile)
|
||||
storageFile=$(normalizeStorePaths $storageFile)
|
||||
mountsFile=$(normalizeStorePaths $mountsFile)
|
||||
|
||||
${
|
||||
if pkgs.stdenv.hostPlatform.isDarwin then
|
||||
''
|
||||
# Darwin-specific: verify that config directory is automatically mounted into podman machines
|
||||
assertFileExists activate
|
||||
assertFileRegex activate '\$HOME/\.config/containers:/home/core/\.config/containers'
|
||||
''
|
||||
else
|
||||
""
|
||||
}
|
||||
'';
|
||||
assertFileContent $containersFile ${./configuration-containers-expected.conf}
|
||||
assertFileContent $policyFile ${./configuration-policy-expected.json}
|
||||
assertFileContent $registriesFile ${./configuration-registries-expected.conf}
|
||||
assertFileContent $storageFile ${./configuration-storage-expected.conf}
|
||||
assertFileContent $mountsFile ${./configuration-mounts-expected.conf}
|
||||
'';
|
||||
}
|
||||
|
||||
@@ -27,9 +27,21 @@
|
||||
assertFileNotRegex activate '[-][-]swap'
|
||||
assertFileNotRegex activate '[-][-]timezone'
|
||||
assertFileNotRegex activate '[-][-]username'
|
||||
assertFileNotRegex activate '[-][-]volumes'
|
||||
assertFileRegex activate '[-][-]volume "$HOME/.config/containers:/var/home/core/.config/containers"'
|
||||
assertFileRegex activate '[-][-]volume "/Users:/Users"'
|
||||
assertFileRegex activate '[-][-]volume "/private:/private"'
|
||||
assertFileRegex activate '[-][-]volume "/var/folders:/var/folders"'
|
||||
|
||||
# Verify that config directory is automatically mounted into the machine
|
||||
assertFileRegex activate '\$HOME/\.config/containers:/home/core/\.config/containers'
|
||||
# at the canonical /var/home path (because /home is a symlink on the guest)
|
||||
assertFileRegex activate '\$HOME/\.config/containers:/var/home/core/\.config/containers'
|
||||
|
||||
# Verify the install-based config materialization is wired in
|
||||
assertFileRegex activate 'podmanContainersConfig'
|
||||
assertFileRegex activate 'install -m 0644'
|
||||
assertFileRegex activate 'policy\.json'
|
||||
assertFileRegex activate 'registries\.conf'
|
||||
assertFileRegex activate 'storage\.conf'
|
||||
assertFileRegex activate 'containers\.conf'
|
||||
'';
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
memory = 4096;
|
||||
diskSize = 50;
|
||||
autoStart = false;
|
||||
volumes = [ ];
|
||||
watchdogInterval = 30;
|
||||
};
|
||||
};
|
||||
@@ -46,6 +47,10 @@
|
||||
assertFileRegex activate '[-][-]memory 8192'
|
||||
assertFileRegex activate '[-][-]disk-size 200'
|
||||
assertFileRegex activate '[-][-]rootful'
|
||||
assertFileRegex activate '[-][-]volume "$HOME/.config/containers:/var/home/core/.config/containers"'
|
||||
assertFileRegex activate '[-][-]volume "/Users:/Users"'
|
||||
assertFileRegex activate '[-][-]volume "/private:/private"'
|
||||
assertFileRegex activate '[-][-]volume "/var/folders:/var/folders"'
|
||||
|
||||
# Check test-machine initialization
|
||||
assertFileRegex activate 'test-machine'
|
||||
@@ -53,11 +58,13 @@
|
||||
assertFileRegex activate '[-][-]cpus 2'
|
||||
assertFileRegex activate '[-][-]memory 4096'
|
||||
assertFileRegex activate '[-][-]disk-size 50'
|
||||
assertFileRegex activate '[-][-]volume "$HOME/.config/containers:/var/home/core/.config/containers"'
|
||||
|
||||
# Verify default machine is NOT created
|
||||
assertFileNotRegex activate 'podman-machine-default'
|
||||
|
||||
# Verify that config directory is automatically mounted into all machines
|
||||
assertFileRegex activate '\$HOME/\.config/containers:/home/core/\.config/containers'
|
||||
# at the canonical /var/home path (because /home is a symlink on the guest)
|
||||
assertFileRegex activate '\$HOME/\.config/containers:/var/home/core/\.config/containers'
|
||||
'';
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user