Skip to content

Commit

Permalink
docker.nix: Add options for named volumes and networks
Browse files Browse the repository at this point in the history
  • Loading branch information
Christian Höppner authored and yorickvP committed Feb 12, 2020
1 parent ae12a80 commit ac6bfb7
Show file tree
Hide file tree
Showing 2 changed files with 184 additions and 2 deletions.
162 changes: 162 additions & 0 deletions nixos/modules/virtualisation/docker.nix
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,67 @@ let
cfg = config.virtualisation.docker;
proxy_env = config.networking.proxy.envVars;

inherit (builtins) attrNames;

mkUncreateMaybe = networks: volumes: ''
set -euo pipefail
nexisting=$(${pkgs.coreutils}/bin/mktemp)
nwanted=$(${pkgs.coreutils}/bin/mktemp)
vexisting=$(${pkgs.coreutils}/bin/mktemp)
vwanted=$(${pkgs.coreutils}/bin/mktemp)
cleanup() {
rm -f "$nexisting" "$nwanted" "$vexisting" "$vwanted"
}
trap cleanup EXIT
${pkgs.docker}/bin/docker network ls --format '{{.Name}}' > "$nexisting"
echo -e "bridge\nhost\nnone\n${concatStringsSep "\n" networks}" > "$nwanted"
${pkgs.docker}/bin/docker volume ls --format '{{.Name}}' > "$vexisting"
echo -e "${concatStringsSep "\n" volumes}" > "$vwanted"
nsuperfluous="$(${pkgs.gnugrep}/bin/grep -vxF -f $nwanted $nexisting || true)"
vsuperfluous="$(${pkgs.gnugrep}/bin/grep -vxF -f $vwanted $vexisting || true)"
while read -r net; do
if [[ ! -z "$net" ]]; then
echo -n "Removed superfluous Docker network: "
${pkgs.docker}/bin/docker network rm "$net" || true
fi
done <<< "$nsuperfluous"
while read -r vol; do
if [[ ! -z "$vol" ]]; then
echo -n "Removed superfluous Docker volume: "
${pkgs.docker}/bin/docker volume rm "$vol" || true
fi
done <<< "$vsuperfluous"
'';

mkNetworkOpts = opts: concatStringsSep " "
([ "--driver=${opts.driver}" ]
++ optional (cfg ? subnet && cfg.subnet != null) "--subnet=${opts.subnet}"
++ optional (cfg ? ip-range && cfg.ip-range != null) "--ip-range=${opts.ip-range}"
++ optional (cfg ? gateway && cfg.gateway != null) "--gateway=${opts.gateway}"
++ optional (cfg ? ipv6 && cfg.ipv6) "--ipv6"
++ optional (cfg ? internal && cfg.internal) "--internal");


mkNetwork = name: opts: ''
if [[ $(${pkgs.docker}/bin/docker network ls --quiet --filter name=${name} | wc -c) -eq 0 ]]; then
echo "*** docker network create ${mkNetworkOpts opts} ${name}"
${pkgs.docker}/bin/docker network create ${mkNetworkOpts opts} ${name}
fi
'';

mkVolume = name: ''
if [[ $(${pkgs.docker}/bin/docker volume ls --quiet --filter name=${name} | wc -c) -eq 0 ]]; then
echo "*** docker volume create ${name}"
${pkgs.docker}/bin/docker volume create ${name}
fi
'';
in

{
Expand Down Expand Up @@ -93,6 +154,16 @@ in
'';
};

logLevel =
mkOption {
type = types.enum ["debug" "info" "warn" "error" "fatal"];
default = "info";
description =
''
This option determines the log level for the Docker daemon.
'';
};

extraOptions =
mkOption {
type = types.separatedString " ";
Expand Down Expand Up @@ -144,6 +215,90 @@ in
Docker package to be used in the module.
'';
};

volumes = mkOption {
default = [];
type = types.listOf types.str;
example = [ "volume_1" "volume_2" ];
description = ''
A list of named volumes that should be created.
'';
};


networks = mkOption {
default = {};
type = types.attrsOf (types.submodule {
options = {
driver = mkOption {
default = "bridge";
type = types.str;
example = "overlay";
description = ''
Driver to manage the network. One of bridge, or overlay.
'';
};

subnet = mkOption {
default = null;
type = types.nullOr types.str;
example = "172.28.0.0/16";
description = ''
Subnet in CIDR format that represents a network segment.
'';
};

ip-range = mkOption {
default = null;
type = types.nullOr types.str;
example = "172.28.5.0/24";
description = ''
Allocate container ip from a sub-range.
'';
};

gateway = mkOption {
default = null;
type = types.nullOr types.str;
example = "172.28.5.254";
description = ''
IPv4 or IPv6 Gateway for the master subnet.
'';
};

ipv6 = mkOption {
default = false;
type = types.bool;
example = true;
description = ''
Enable IPv6 networking.
'';
};

internal = mkOption {
default = false;
type = types.bool;
example = true;
description = ''
Restrict external access to the network.
'';
};
};
});

example = {
my-network = {
driver = "bridge";
subnet = "172.28.0.0/16";
ip-range = "172.28.5.0/24";
gateway = "172.28.5.254";
};
};

description = ''
A list of named networks to be created.
'';
};
};

###### implementation
Expand All @@ -157,6 +312,11 @@ in
systemd.services.docker = {
wantedBy = optional cfg.enableOnBoot "multi-user.target";
environment = proxy_env;

postStart = mkUncreateMaybe (attrNames cfg.networks) cfg.volumes
+ concatStrings (mapAttrsToList mkNetwork cfg.networks)
+ concatStrings (map mkVolume cfg.volumes);

serviceConfig = {
ExecStart = [
""
Expand All @@ -165,11 +325,13 @@ in
--group=docker \
--host=fd:// \
--log-driver=${cfg.logDriver} \
--log-level=${cfg.logLevel} \
${optionalString (cfg.storageDriver != null) "--storage-driver=${cfg.storageDriver}"} \
${optionalString cfg.liveRestore "--live-restore" } \
${optionalString cfg.enableNvidia "--add-runtime nvidia=${pkgs.nvidia-docker}/bin/nvidia-container-runtime" } \
${cfg.extraOptions}
''];

ExecReload=[
""
"${pkgs.procps}/bin/kill -s HUP $MAINPID"
Expand Down
24 changes: 22 additions & 2 deletions nixos/tests/docker.nix
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,19 @@ import ./make-test-python.nix ({ pkgs, ...} : {
docker =
{ pkgs, ... }:
{
virtualisation.docker.enable = true;
virtualisation.docker.package = pkgs.docker;
virtualisation.docker = {
enable = true;
package = pkgs.docker;
volumes = [ "thevolume" ];
networks.thenetwork = {
driver = "bridge";
subnet = "172.28.0.0/16";
ip-range = "172.28.5.0/24";
gateway = "172.28.5.254";
};

logLevel = "warn";
};

users.users = {
noprivs = {
Expand Down Expand Up @@ -43,6 +54,15 @@ import ./make-test-python.nix ({ pkgs, ...} : {
docker.fail("sudo -u noprivs docker ps")
docker.succeed("docker stop sleeping")
$docker->succeed("docker volume ls | grep thevolume");
$docker->succeed("docker network ls | grep thenetwork");
$docker->succeed("docker volume create superfluousvolume");
$docker->succeed("docker network create superfluousnetwork");
$docker->systemctl("restart docker");
$docker->waitForUnit("docker.service");
$docker->fail("docker volume ls | grep superfluous");
# Must match version twice to ensure client and server versions are correct
docker.succeed('[ $(docker version | grep ${pkgs.docker.version} | wc -l) = "2" ]')
'';
Expand Down

0 comments on commit ac6bfb7

Please sign in to comment.