1
0
Fork 0
mirror of https://github.com/nix-community/home-manager synced 2025-01-24 01:49:55 +01:00
home-manager/modules/virtualisation/podman/podman.nix
Michael Vogel faa4b16358
podman: add module
This module is a continuation of #2630 by MaeIsBad.

It also adds a module `virtualisation.oci-containers` that is
equivalent to the one in NixOS. Basically it allows a simple toggle to
activate oci-container services and commands.

We also support Podman on mac. Note, Podman requires a VM on mac,
which has to be started before any Podman commands can be executed.
Users might sometimes require VMs that use different architectures
than the default VM started by Podman. Thus, they get the option to
define the VM(s) that will be initialized and started by podman.

Since Podman has to start a machine, it's best to do it using launchd.
The configuration of the machines requires a JSON, generated from an
attrset in Home Manager, which is where Python script comes into play
to take care of diff-ing the `podman machine list` to CRUD them.

PR #4331

Co-authored-by: MaeIsBad <26093674+MaeIsBad@users.noreply.github.com>
2023-12-23 17:27:37 +01:00

274 lines
7.8 KiB
Nix

{ config, lib, pkgs, ... }:
let
cfg = config.virtualisation.podman;
toml = pkgs.formats.toml { };
json = pkgs.formats.json { };
inherit (lib) mkDefault mkIf mkMerge mkOption types;
podmanPackage = (pkgs.podman.override { inherit (cfg) extraPackages; });
# Provides a fake "docker" binary mapping to podman
dockerAlias = pkgs.runCommandNoCC
"${podmanPackage.pname}-docker-alias-${podmanPackage.version}" {
outputs = [ "out" "man" ];
inherit (podmanPackage) meta;
} ''
mkdir -p $out/bin
ln -s ${podmanPackage}/bin/podman $out/bin/docker
mkdir -p $man/share/man/man1
for f in ${podmanPackage.man}/share/man/man1/*; do
basename=$(basename $f | sed s/podman/docker/g)
ln -s $f $man/share/man/man1/$basename
done
'';
podmactl = pkgs.callPackage ./podmactl { };
machineOpts = {
# Options here are loaded into python. For simplicity, please use
# snake_case.
options = {
active = mkOption {
type = types.bool;
default = false;
description = ''
This machine should be started. Only one machine can be active at a time
'';
};
qemu_binary = mkOption {
type = types.nullOr types.str;
default = null;
example = "''${pkgs.qemu}/bin/qemu-system-x86_64";
description = ''
Use this to start VM with the qemu appropriate for your architecture.
'';
};
# Options passed to Podman machine.
# See https://docs.podman.io/en/latest/markdown/podman-machine.1.html
cpus = mkOption {
type = types.ints.positive;
default = 1;
description = "The number of CPUs to assign to the VM.";
};
disk_size = mkOption {
type = types.ints.positive;
default = 100;
description = "Size of disk in gigabytes. Can only be increased";
};
image_path = mkOption {
type = types.nullOr types.str;
default = null;
example = lib.literalExpression ''
builtins.fetchurl "https://builds.coreos.fedoraproject.org/prod/streams/stable/builds/38.20230819.3.0/x86_64/fedora-coreos-38.20230819.3.0-qemu.x86_64.qcow2.xz"'';
description = ''
Image to be used when starting the VM
Can be a local path or a URL to an image.
Alternatives can be found at <https://fedoraproject.org/en/coreos/download>.
'';
};
memory = mkOption {
type = types.ints.positive;
default = 2048;
description = "RAM in MB to be assigned to the machine";
};
};
};
in {
meta.maintainers = [ pkgs.lib.maintainers.michaelCTS ];
options.virtualisation.podman = {
enable = mkOption {
type = types.bool;
default = false;
description = ''
This option enables Podman, a daemonless container engine for
developing, managing, and running OCI Containers on your Linux System.
It is a drop-in replacement for the {command}`docker` command.
'';
};
enableDockerSocket = mkOption {
type = types.bool;
default = false;
description = ''
Make the Podman socket available in place of the Docker socket, so
Docker tools can find the Podman socket.
Podman implements the Docker API.
'';
};
enableDockerAlias = mkOption {
type = types.bool;
default = false;
description = ''
Create an alias mapping {command}`docker` to {command}`podman`.
'';
};
extraPackages = mkOption {
type = with types; listOf package;
default = [ ];
example = lib.literalExpression "[ pkgs.gvisor ]";
description = ''
Extra packages to be installed in the Podman wrapper.
'';
};
finalPackage = lib.mkOption {
type = types.package;
internal = true;
readOnly = true;
default = podmanPackage;
description = ''
The final Podman package (including extra packages).
'';
};
defaultNetwork.extraPlugins = lib.mkOption {
type = types.listOf json.type;
default = [ ];
description = ''
Extra CNI plugin configurations to add to Podman's default network.
'';
};
machines = lib.mkOption {
type = types.attrsOf (types.submodule machineOpts);
# One and only one machine may be active at any given time
apply = machines:
assert ((lib.lists.count (machine: machine.active)
(lib.attrsets.attrValues machines)) == 1);
machines;
default = {
podman-machine-default = {
active = true;
cpus = 2;
disk_size = 100;
memory = 2048;
};
};
example = lib.literalExpression ''
{
intel-x86 = {
cpus = 2;
disk_size = 200;
memory = 4096;
image_path = "fedora-coreos-38.20230806.3.0-qemu.x86_64.qcow2.xz";
qemu_binary = "${pkgs.qemu}/bin/qemu-system-x86_64";
};
}
'';
description = ''
Virtual machine descriptions when Podman is run in on non-Linux systems.
'';
};
};
config = mkIf cfg.enable (mkMerge [
{
home.packages = [ cfg.finalPackage ]
++ lib.optional cfg.enableDockerAlias dockerAlias;
virtualisation.containers = {
enable = true; # Enable common /etc/containers configuration
};
}
(mkIf pkgs.stdenv.hostPlatform.isLinux (mkMerge [
{
systemd.user = {
services.podman = {
Unit = {
Description = "Podman API Service";
Requires = "podman.socket";
After = "podman.socket";
Documentation = "man:podman-system-service(1)";
StartLimitIntervalSec = 0;
};
Service = {
Type = "exec";
KillMode = "process";
Environment = ''LOGGING=" --log-level=info"'';
ExecStart = [
"${cfg.finalPackage}/bin/podman"
"$LOGGING"
"system"
"service"
];
};
Install = { WantedBy = [ "default.target" ]; };
};
sockets.podman = {
Unit = {
Description = "Podman API Socket";
Documentation = "man:podman-system-service(1)";
};
Socket = {
ListenStream = "%t/podman/podman.sock";
SocketMode = 660;
};
Install.WantedBy = [ "sockets.target" ];
};
};
}
(mkIf cfg.enableDockerSocket {
home.sessionVariables."DOCKER_HOST" =
"unix:///$XDG_RUNTIME_DIR/podman/podman.sock";
})
]))
(mkIf pkgs.stdenv.isDarwin (mkMerge [
{
home.packages = [
pkgs.qemu # To manage machines
pkgs.openssh # To ssh into the machines
];
}
{
home.extraActivationPath = [
pkgs.qemu # To manage machines.
pkgs.openssh # To ssh into the machines.
];
# CRUD the requested podman machines when activating the profile
home.activation.podman-machine =
lib.hm.dag.entryAfter [ "writeBoundary" ]
(lib.strings.concatStringsSep " " [
"$DRY_RUN_CMD"
"${podmactl}/bin/podmactl"
"--podman"
"${cfg.finalPackage}/bin/podman"
"$VERBOSE_ARG"
"${json.generate "podman-machines.json" cfg.machines}"
]);
}
# Socket is actually only available after the launchd agent has
# successfully completed and the machine has been started.
(mkIf cfg.enableDockerSocket {
home.sessionVariables."DOCKER_HOST" =
"unix:///Users/$USER/.local/share/containers/podman/machine/qemu/podman.sock";
})
]))
]);
}