Merge pull request #25 from hercules-ci/nixos-in-container
NixOS in container
This commit is contained in:
commit
6fd435c38e
12 changed files with 235 additions and 8 deletions
|
@ -15,7 +15,7 @@ let
|
||||||
optionsXML =
|
optionsXML =
|
||||||
# builtins.toFile "options.xml" (builtins.toXML optionsList);
|
# builtins.toFile "options.xml" (builtins.toXML optionsList);
|
||||||
pkgs.runCommand "options.xml" {
|
pkgs.runCommand "options.xml" {
|
||||||
buildInputs = [pkgs.nix pkgs.fakeroot pkgs.jq];
|
buildInputs = [pkgs.nix pkgs.jq];
|
||||||
inherit optionsExpr;
|
inherit optionsExpr;
|
||||||
} ''
|
} ''
|
||||||
export NIX_LOG_DIR=$PWD
|
export NIX_LOG_DIR=$PWD
|
||||||
|
|
11
examples/full-nixos/arion-compose.nix
Normal file
11
examples/full-nixos/arion-compose.nix
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
{
|
||||||
|
docker-compose.services.webserver = { pkgs, ... }: {
|
||||||
|
nixos.useSystemd = true;
|
||||||
|
nixos.configuration.services.nginx.enable = true;
|
||||||
|
nixos.configuration.services.nginx.virtualHosts.localhost.root = "${pkgs.nix.doc}/share/doc/nix/manual";
|
||||||
|
service.useHostStore = true;
|
||||||
|
service.ports = [
|
||||||
|
"8000:80" # host:container
|
||||||
|
];
|
||||||
|
};
|
||||||
|
}
|
2
examples/full-nixos/arion-pkgs.nix
Normal file
2
examples/full-nixos/arion-pkgs.nix
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
# Instead of pinning Nixpkgs, we can opt to use the one in NIX_PATH
|
||||||
|
import <nixpkgs> {}
|
40
examples/nixos-unit/arion-compose.nix
Normal file
40
examples/nixos-unit/arion-compose.nix
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
|
||||||
|
/*
|
||||||
|
|
||||||
|
DISCLAIMER
|
||||||
|
|
||||||
|
This uses a somewhat hidden feature in NixOS, which is the
|
||||||
|
"runner". It's a script that's available on systemd services
|
||||||
|
that lets you run the service independently from systemd.
|
||||||
|
However, it was clearly not intended for public consumption
|
||||||
|
so please use it with care.
|
||||||
|
It does not support all features of systemd so you are on
|
||||||
|
your own if you use it in production.
|
||||||
|
|
||||||
|
One known issue is that the script does not respond to docker's
|
||||||
|
SIGTERM shutdown signal.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
{
|
||||||
|
docker-compose.services.webserver = { config, pkgs, ... }: {
|
||||||
|
|
||||||
|
nixos.configuration = {config, pkgs, ...}: {
|
||||||
|
boot.isContainer = true;
|
||||||
|
services.nginx.enable = true;
|
||||||
|
services.nginx.virtualHosts.localhost.root = "${pkgs.nix.doc}/share/doc/nix/manual";
|
||||||
|
system.build.run-nginx = pkgs.writeScript "run-nginx" ''
|
||||||
|
#!${pkgs.bash}/bin/bash
|
||||||
|
PATH='${config.systemd.services.nginx.environment.PATH}'
|
||||||
|
echo nginx:x:${toString config.users.users.nginx.uid}:${toString config.users.groups.nginx.gid}:nginx web server user:/var/empty:/bin/sh >>/etc/passwd
|
||||||
|
echo nginx:x:${toString config.users.groups.nginx.gid}:nginx >>/etc/group
|
||||||
|
${config.systemd.services.nginx.runner}
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
service.command = [ config.nixos.build.run-nginx ];
|
||||||
|
service.useHostStore = true;
|
||||||
|
service.ports = [
|
||||||
|
"8000:80" # host:container
|
||||||
|
];
|
||||||
|
};
|
||||||
|
}
|
2
examples/nixos-unit/arion-pkgs.nix
Normal file
2
examples/nixos-unit/arion-pkgs.nix
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
# Instead of pinning Nixpkgs, we can opt to use the one in NIX_PATH
|
||||||
|
import <nixpkgs> {}
|
|
@ -12,6 +12,8 @@ let
|
||||||
./modules/service/docker-compose-service.nix
|
./modules/service/docker-compose-service.nix
|
||||||
./modules/service/host-store.nix
|
./modules/service/host-store.nix
|
||||||
./modules/service/host.nix
|
./modules/service/host.nix
|
||||||
|
./modules/service/nixos.nix
|
||||||
|
./modules/service/nixos-init.nix
|
||||||
];
|
];
|
||||||
|
|
||||||
argsModule = {
|
argsModule = {
|
||||||
|
|
24
src/nix/modules/nixos/container-systemd.nix
Normal file
24
src/nix/modules/nixos/container-systemd.nix
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
/*
|
||||||
|
NixOS configuration to for running a mostly normal systemd-based
|
||||||
|
NixOS in Docker.
|
||||||
|
*/
|
||||||
|
{ pkgs, lib, ...}: {
|
||||||
|
|
||||||
|
# imports = [
|
||||||
|
# # This profile doesn't seem to work well.
|
||||||
|
# (pkgs.path + "/nixos/modules/profiles/docker-container.nix")
|
||||||
|
# This one works, but can not be imported here due because imports can not depend on pkgs.
|
||||||
|
# (pkgs.path + "/nixos/modules/profiles/minimal.nix")
|
||||||
|
# ];
|
||||||
|
boot.isContainer = true;
|
||||||
|
boot.specialFileSystems = lib.mkForce {};
|
||||||
|
networking.hostName = "";
|
||||||
|
|
||||||
|
services.journald.console = "/dev/console";
|
||||||
|
|
||||||
|
systemd.services.systemd-logind.enable = false;
|
||||||
|
systemd.services.console-getty.enable = false;
|
||||||
|
|
||||||
|
systemd.sockets.nix-daemon.enable = false;
|
||||||
|
systemd.services.nix-daemon.enable = false;
|
||||||
|
}
|
|
@ -36,7 +36,12 @@ in
|
||||||
service.volumes = mkOption {
|
service.volumes = mkOption {
|
||||||
type = listOf types.unspecified;
|
type = listOf types.unspecified;
|
||||||
default = [];
|
default = [];
|
||||||
description = "";
|
description = dockerComposeRef "volumes";
|
||||||
|
};
|
||||||
|
service.tmpfs = mkOption {
|
||||||
|
type = listOf types.str;
|
||||||
|
default = [];
|
||||||
|
description = dockerComposeRef "tmpfs";
|
||||||
};
|
};
|
||||||
service.build.context = mkOption {
|
service.build.context = mkOption {
|
||||||
type = nullOr str;
|
type = nullOr str;
|
||||||
|
@ -52,6 +57,11 @@ in
|
||||||
default = null;
|
default = null;
|
||||||
description = dockerComposeKitchenSink;
|
description = dockerComposeKitchenSink;
|
||||||
};
|
};
|
||||||
|
service.tty = mkOption {
|
||||||
|
type = nullOr bool;
|
||||||
|
default = null;
|
||||||
|
description = dockerComposeKitchenSink;
|
||||||
|
};
|
||||||
service.environment = mkOption {
|
service.environment = mkOption {
|
||||||
type = attrsOf (either str int);
|
type = attrsOf (either str int);
|
||||||
default = {};
|
default = {};
|
||||||
|
@ -130,6 +140,11 @@ in
|
||||||
default = null;
|
default = null;
|
||||||
description = dockerComposeRef "network_mode";
|
description = dockerComposeRef "network_mode";
|
||||||
};
|
};
|
||||||
|
service.stop_signal = mkOption {
|
||||||
|
type = nullOr str;
|
||||||
|
default = null;
|
||||||
|
description = dockerComposeRef "stop_signal";
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
config.build.service = {
|
config.build.service = {
|
||||||
|
@ -166,6 +181,12 @@ in
|
||||||
inherit (config.service) network_mode;
|
inherit (config.service) network_mode;
|
||||||
} // lib.optionalAttrs (config.service.restart != null) {
|
} // lib.optionalAttrs (config.service.restart != null) {
|
||||||
inherit (config.service) restart;
|
inherit (config.service) restart;
|
||||||
|
} // lib.optionalAttrs (config.service.stop_signal != null) {
|
||||||
|
inherit (config.service) stop_signal;
|
||||||
|
} // lib.optionalAttrs (config.service.tmpfs != []) {
|
||||||
|
inherit (config.service) tmpfs;
|
||||||
|
} // lib.optionalAttrs (config.service.tty != null) {
|
||||||
|
inherit (config.service) tty;
|
||||||
} // lib.optionalAttrs (config.service.working_dir != null) {
|
} // lib.optionalAttrs (config.service.working_dir != null) {
|
||||||
inherit (config.service) working_dir;
|
inherit (config.service) working_dir;
|
||||||
};
|
};
|
||||||
|
|
|
@ -16,6 +16,11 @@ in
|
||||||
default = false;
|
default = false;
|
||||||
description = "Bind mounts the host store if enabled, avoiding copying.";
|
description = "Bind mounts the host store if enabled, avoiding copying.";
|
||||||
};
|
};
|
||||||
|
service.useHostNixDaemon = mkOption {
|
||||||
|
type = types.bool;
|
||||||
|
default = false;
|
||||||
|
description = "Make the host Nix daemon available.";
|
||||||
|
};
|
||||||
};
|
};
|
||||||
config = mkIf config.service.useHostStore {
|
config = mkIf config.service.useHostStore {
|
||||||
service.image = "arion-base";
|
service.image = "arion-base";
|
||||||
|
@ -23,6 +28,6 @@ in
|
||||||
service.volumes = [
|
service.volumes = [
|
||||||
"${config.host.nixStorePrefix}/nix/store:/nix/store"
|
"${config.host.nixStorePrefix}/nix/store:/nix/store"
|
||||||
"${config.host.nixStorePrefix}${pkgs.buildEnv { name = "container-system-env"; paths = [ pkgs.bashInteractive pkgs.coreutils ]; }}:/run/system"
|
"${config.host.nixStorePrefix}${pkgs.buildEnv { name = "container-system-env"; paths = [ pkgs.bashInteractive pkgs.coreutils ]; }}:/run/system"
|
||||||
];
|
] ++ lib.optional config.service.useHostNixDaemon "/nix/var/nix/daemon-socket:/nix/var/nix/daemon-socket";
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
39
src/nix/modules/service/nixos-init.nix
Normal file
39
src/nix/modules/service/nixos-init.nix
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
/*
|
||||||
|
Invokes the NixOS init system in the container.
|
||||||
|
*/
|
||||||
|
{ config, lib, pkgs, ... }:
|
||||||
|
let
|
||||||
|
inherit (lib) types;
|
||||||
|
in
|
||||||
|
{
|
||||||
|
options = {
|
||||||
|
nixos.useSystemd = lib.mkOption {
|
||||||
|
type = types.bool;
|
||||||
|
default = false;
|
||||||
|
description = ''
|
||||||
|
When enabled, call the NixOS systemd-based init system.
|
||||||
|
|
||||||
|
Configure NixOS with <code>nixos.configuration</code>.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
config = lib.mkIf (config.nixos.useSystemd) {
|
||||||
|
nixos.configuration.imports = [
|
||||||
|
../nixos/container-systemd.nix
|
||||||
|
(pkgs.path + "/nixos/modules/profiles/minimal.nix")
|
||||||
|
];
|
||||||
|
service.command = [ "${config.nixos.build.toplevel}/init" ];
|
||||||
|
service.environment.container = "docker";
|
||||||
|
service.volumes = [
|
||||||
|
"/sys/fs/cgroup:/sys/fs/cgroup:ro"
|
||||||
|
];
|
||||||
|
service.tmpfs = [
|
||||||
|
"/tmp"
|
||||||
|
"/run"
|
||||||
|
"/run/wrappers"
|
||||||
|
];
|
||||||
|
service.stop_signal = "SIGRTMIN+3";
|
||||||
|
service.tty = true;
|
||||||
|
};
|
||||||
|
}
|
60
src/nix/modules/service/nixos.nix
Normal file
60
src/nix/modules/service/nixos.nix
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
/*
|
||||||
|
Generic options for adding NixOS modules and evaluating the NixOS
|
||||||
|
configuration. The methods for installing NixOS in the image are
|
||||||
|
defined elsewhere.
|
||||||
|
*/
|
||||||
|
{ pkgs, lib, config, ... }:
|
||||||
|
let
|
||||||
|
inherit (lib) types;
|
||||||
|
inherit (types) attrs;
|
||||||
|
in
|
||||||
|
{
|
||||||
|
options = {
|
||||||
|
nixos.configuration = lib.mkOption {
|
||||||
|
type = with types; coercedTo unspecified (a: [a]) (listOf unspecified);
|
||||||
|
default = {};
|
||||||
|
description = ''
|
||||||
|
Modules to add to the NixOS configuration.
|
||||||
|
|
||||||
|
This option is unused by default, because not all images use NixOS.
|
||||||
|
|
||||||
|
One way to use this is to enable <code>nixos.useSystemd</code>, but the
|
||||||
|
NixOS configuration can be used in other ways.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
nixos.build = lib.mkOption {
|
||||||
|
type = attrs;
|
||||||
|
readOnly = true;
|
||||||
|
description = ''
|
||||||
|
NixOS build products from <code>config.system.build</code>, such as <code>toplevel</code> and <code>etc</code>.
|
||||||
|
|
||||||
|
This option is unused by default, because not all images use NixOS.
|
||||||
|
|
||||||
|
One way to use this is to enable <code>nixos.useSystemd</code>, but the
|
||||||
|
NixOS configuration can be used in other ways.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
nixos.evaluatedConfig = lib.mkOption {
|
||||||
|
type = attrs;
|
||||||
|
readOnly = true;
|
||||||
|
description = ''
|
||||||
|
Evaluated NixOS configuration, to be read by service-level modules.
|
||||||
|
|
||||||
|
This option is unused by default, because not all images use NixOS.
|
||||||
|
|
||||||
|
One way to use this is to enable <code>nixos.useSystemd</code>, but the
|
||||||
|
NixOS configuration can be used in other ways.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
config = {
|
||||||
|
nixos.build = builtins.addErrorContext "while evaluating the service nixos configuration" (
|
||||||
|
pkgs.nixos config.nixos.configuration
|
||||||
|
);
|
||||||
|
nixos.configuration = { config, ... }: { system.build.theConfig = config; };
|
||||||
|
nixos.evaluatedConfig = config.nixos.build.theConfig;
|
||||||
|
};
|
||||||
|
}
|
|
@ -2,8 +2,8 @@
|
||||||
|
|
||||||
let
|
let
|
||||||
# To make some prebuilt derivations available in the vm
|
# To make some prebuilt derivations available in the vm
|
||||||
preEval = import ../../src/nix/eval-composition.nix {
|
preEval = modules: import ../../src/nix/eval-composition.nix {
|
||||||
modules = [ ../../examples/minimal/arion-compose.nix ];
|
inherit modules;
|
||||||
inherit pkgs;
|
inherit pkgs;
|
||||||
};
|
};
|
||||||
in
|
in
|
||||||
|
@ -31,14 +31,35 @@ in
|
||||||
virtualisation.pathsInNixDB = [
|
virtualisation.pathsInNixDB = [
|
||||||
# Pre-build the image because we don't want to build the world
|
# Pre-build the image because we don't want to build the world
|
||||||
# in the vm.
|
# in the vm.
|
||||||
preEval.config.build.dockerComposeYaml
|
(preEval [ ../../examples/minimal/arion-compose.nix ]).config.build.dockerComposeYaml
|
||||||
|
(preEval [ ../../examples/full-nixos/arion-compose.nix ]).config.build.dockerComposeYaml
|
||||||
|
(preEval [ ../../examples/nixos-unit/arion-compose.nix ]).config.build.dockerComposeYaml
|
||||||
pkgs.stdenv
|
pkgs.stdenv
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
testScript = ''
|
testScript = ''
|
||||||
$machine->fail("curl localhost:8000");
|
$machine->fail("curl localhost:8000");
|
||||||
$machine->succeed("docker --version");
|
$machine->succeed("docker --version");
|
||||||
$machine->succeed("cp -r ${../../examples/minimal} work && cd work && NIX_PATH=nixpkgs='${pkgs.path}' arion up -d");
|
|
||||||
$machine->waitUntilSucceeds("curl localhost:8000");
|
subtest "minimal", sub {
|
||||||
|
$machine->succeed("cp -r ${../../examples/minimal} work && cd work && NIX_PATH=nixpkgs='${pkgs.path}' arion up -d");
|
||||||
|
$machine->waitUntilSucceeds("curl localhost:8000");
|
||||||
|
$machine->succeed("cd work && NIX_PATH=nixpkgs='${pkgs.path}' arion down && rm -rf work");
|
||||||
|
$machine->waitUntilFails("curl localhost:8000");
|
||||||
|
};
|
||||||
|
|
||||||
|
subtest "full-nixos", sub {
|
||||||
|
$machine->succeed("cp -r ${../../examples/full-nixos} work && cd work && NIX_PATH=nixpkgs='${pkgs.path}' arion up -d");
|
||||||
|
$machine->waitUntilSucceeds("curl localhost:8000");
|
||||||
|
$machine->succeed("cd work && NIX_PATH=nixpkgs='${pkgs.path}' arion down && rm -rf work");
|
||||||
|
$machine->waitUntilFails("curl localhost:8000");
|
||||||
|
};
|
||||||
|
|
||||||
|
subtest "nixos-unit", sub {
|
||||||
|
$machine->succeed("cp -r ${../../examples/nixos-unit} work && cd work && NIX_PATH=nixpkgs='${pkgs.path}' arion up -d");
|
||||||
|
$machine->waitUntilSucceeds("curl localhost:8000");
|
||||||
|
$machine->succeed("cd work && NIX_PATH=nixpkgs='${pkgs.path}' arion down && rm -rf work");
|
||||||
|
$machine->waitUntilFails("curl localhost:8000");
|
||||||
|
};
|
||||||
'';
|
'';
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue