Merge pull request #25 from hercules-ci/nixos-in-container

NixOS in container
This commit is contained in:
Robert Hensing 2019-03-06 14:55:45 +01:00 committed by GitHub
commit 6fd435c38e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 235 additions and 8 deletions

View file

@ -15,7 +15,7 @@ let
optionsXML =
# builtins.toFile "options.xml" (builtins.toXML optionsList);
pkgs.runCommand "options.xml" {
buildInputs = [pkgs.nix pkgs.fakeroot pkgs.jq];
buildInputs = [pkgs.nix pkgs.jq];
inherit optionsExpr;
} ''
export NIX_LOG_DIR=$PWD

View 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
];
};
}

View file

@ -0,0 +1,2 @@
# Instead of pinning Nixpkgs, we can opt to use the one in NIX_PATH
import <nixpkgs> {}

View 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
];
};
}

View file

@ -0,0 +1,2 @@
# Instead of pinning Nixpkgs, we can opt to use the one in NIX_PATH
import <nixpkgs> {}

View file

@ -12,6 +12,8 @@ let
./modules/service/docker-compose-service.nix
./modules/service/host-store.nix
./modules/service/host.nix
./modules/service/nixos.nix
./modules/service/nixos-init.nix
];
argsModule = {

View 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;
}

View file

@ -36,7 +36,12 @@ in
service.volumes = mkOption {
type = listOf types.unspecified;
default = [];
description = "";
description = dockerComposeRef "volumes";
};
service.tmpfs = mkOption {
type = listOf types.str;
default = [];
description = dockerComposeRef "tmpfs";
};
service.build.context = mkOption {
type = nullOr str;
@ -52,6 +57,11 @@ in
default = null;
description = dockerComposeKitchenSink;
};
service.tty = mkOption {
type = nullOr bool;
default = null;
description = dockerComposeKitchenSink;
};
service.environment = mkOption {
type = attrsOf (either str int);
default = {};
@ -130,6 +140,11 @@ in
default = null;
description = dockerComposeRef "network_mode";
};
service.stop_signal = mkOption {
type = nullOr str;
default = null;
description = dockerComposeRef "stop_signal";
};
};
config.build.service = {
@ -166,6 +181,12 @@ in
inherit (config.service) network_mode;
} // lib.optionalAttrs (config.service.restart != null) {
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) {
inherit (config.service) working_dir;
};

View file

@ -16,6 +16,11 @@ in
default = false;
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 {
service.image = "arion-base";
@ -23,6 +28,6 @@ in
service.volumes = [
"${config.host.nixStorePrefix}/nix/store:/nix/store"
"${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";
};
}

View 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;
};
}

View 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;
};
}

View file

@ -2,8 +2,8 @@
let
# To make some prebuilt derivations available in the vm
preEval = import ../../src/nix/eval-composition.nix {
modules = [ ../../examples/minimal/arion-compose.nix ];
preEval = modules: import ../../src/nix/eval-composition.nix {
inherit modules;
inherit pkgs;
};
in
@ -31,14 +31,35 @@ in
virtualisation.pathsInNixDB = [
# Pre-build the image because we don't want to build the world
# 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
];
};
testScript = ''
$machine->fail("curl localhost:8000");
$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");
};
'';
}