Compare commits

...

2 commits

Author SHA1 Message Date
Robert Hensing
894cb2815b Small test improvements 2019-06-02 12:50:30 +02:00
Robert Hensing
1308b729c1 Add secrets options to service and composition 2019-06-02 12:44:23 +02:00
6 changed files with 135 additions and 8 deletions

View file

@ -11,8 +11,36 @@
*/
{ pkgs, lib, config, ... }:
let
cfg = config.docker-compose;
inherit (lib) mkOption optionalAttrs mapAttrs;
inherit (lib.types) submodule attrsOf nullOr either str path bool;
evalService = name: modules: pkgs.callPackage ../../eval-service.nix {} { inherit name modules; inherit (config) host; };
dockerComposeRef = fragment:
''See <link xlink:href="https://docs.docker.com/compose/compose-file/#${fragment}">Docker Compose#${fragment}</link>'';
secretType = submodule {
options = {
file = mkOption {
type = either path str;
description = ''
Sets the secret's value to this file.
${dockerComposeRef "secrets"}
'';
};
external = mkOption {
type = bool;
default = false;
description = ''
Whether the value of this secret is set via other means.
${dockerComposeRef "secrets"}
'';
};
};
};
in
{
options = {
@ -42,6 +70,11 @@ in
description = "Attribute set of evaluated service configurations.";
readOnly = true;
};
docker-compose.secrets = lib.mkOption {
type = attrsOf secretType;
description = dockerComposeRef "secrets";
default = {};
};
};
config = {
build.dockerComposeYaml = pkgs.writeText "docker-compose.yaml" config.build.dockerComposeYamlText;
@ -52,6 +85,13 @@ in
version = "3.4";
services = lib.mapAttrs (k: c: c.config.build.service) config.docker-compose.evaluatedServices;
x-arion = config.docker-compose.extended;
} // optionalAttrs (cfg.secrets != {}) {
secrets = mapAttrs (_k: s: optionalAttrs (s.external != false) {
inherit (s) external;
} // optionalAttrs (s.file != null) {
file = toString s.file;
}
) cfg.secrets;
};
};
}

View file

@ -7,9 +7,11 @@
{ pkgs, lib, config, ... }:
let
inherit (lib) mkOption types;
inherit (lib) mkOption types mapAttrs mapAttrsToList;
inherit (types) listOf nullOr attrsOf str either int bool;
cfg = config.service;
link = url: text:
''<link xlink:href="${url}">${text}</link>'';
dockerComposeRef = fragment:
@ -23,6 +25,48 @@ let
cap_add = lib.attrNames (lib.filterAttrs (name: value: value == true) config.service.capabilities);
cap_drop = lib.attrNames (lib.filterAttrs (name: value: value == false) config.service.capabilities);
serviceSecretType = types.submodule {
options = {
source = mkOption {
type = nullOr str;
default = null;
description = dockerComposeRef "secrets";
};
uid = mkOption {
type = nullOr (either str int);
default = null;
description = dockerComposeRef "secrets";
};
gid = mkOption {
type = nullOr (either str int);
default = null;
description = dockerComposeRef "secrets";
};
mode = mkOption {
type = nullOr str;
# default = "0444";
default = null;
description = ''
The default value of is usually 0444. This option may not be supported
when not deploying to a Swarm.
${dockerComposeRef "secrets"}
'';
};
};
};
secrets = mapAttrsToList (k: s: {
target = k;
} //lib.optionalAttrs (s.source != null) {
inherit (s) source;
} // lib.optionalAttrs (s.uid != null) {
inherit (s) uid;
} // lib.optionalAttrs (s.gid != null) {
inherit (s) gid;
} // lib.optionalAttrs (s.mode != null) {
inherit (s) mode;
}) cfg.secrets;
in
{
options = {
@ -197,6 +241,19 @@ in
}
'';
};
service.secrets = mkOption {
type = attrsOf serviceSecretType;
default = {};
description = dockerComposeRef "secrets";
example = {
redis_secret = {
source = "web_cache_redis_secret";
uid = 123;
gid = 123;
mode = "0440";
};
};
};
};
config.build.service = {
@ -242,6 +299,8 @@ in
inherit (config.service) restart;
} // lib.optionalAttrs (config.service.stop_signal != null) {
inherit (config.service) stop_signal;
} // lib.optionalAttrs (secrets != null) {
inherit secrets;
} // lib.optionalAttrs (config.service.tmpfs != []) {
inherit (config.service) tmpfs;
} // lib.optionalAttrs (config.service.tty != null) {

View file

@ -12,11 +12,10 @@ in
machine = { pkgs, lib, ... }: {
environment.systemPackages = [
pkgs.arion
pkgs.docker-compose
];
virtualisation.docker.enable = true;
# no caches, because no internet
# no caches, because the test cannot access the internet
nix.binaryCaches = lib.mkForce [];
# FIXME: Sandbox seems broken with current version of NixOS test
@ -29,13 +28,14 @@ in
virtualisation.writableStore = true;
virtualisation.pathsInNixDB = [
# Pre-build the image because we don't want to build the world
# in the vm.
# Pre-build the image because we don't want to build the world in the vm.
(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
(preEval [ ../testcases/secrets/arion-compose.nix ]).config.build.dockerComposeYaml
pkgs.stdenv
];
virtualisation.memorySize = 4096; #MB
};
testScript = ''
$machine->fail("curl localhost:8000");
@ -44,7 +44,7 @@ in
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->succeed("(cd work && NIX_PATH=nixpkgs='${pkgs.path}' arion down) && rm -rf work");
$machine->waitUntilFails("curl localhost:8000");
};
@ -53,14 +53,22 @@ in
$machine->waitUntilSucceeds("curl localhost:8000");
# Also test exec with defaultExec
$machine->succeed("cd work && export NIX_PATH=nixpkgs='${pkgs.path}' && (echo 'nix run -f ~/h/arion arion -c arion exec webserver'; echo 'target=world; echo Hello \$target'; echo exit) | script /dev/null | grep 'Hello world'");
$machine->succeed("cd work && NIX_PATH=nixpkgs='${pkgs.path}' arion down && rm -rf work");
$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->succeed("(cd work && NIX_PATH=nixpkgs='${pkgs.path}' arion down) && rm -rf work");
$machine->waitUntilFails("curl localhost:8000");
};
subtest "secrets", sub {
$machine->succeed("cp -r ${../testcases/secrets} work && cd work && NIX_PATH=nixpkgs='${pkgs.path}' arion up -d");
$machine->waitUntilSucceeds("curl localhost:8000");
$machine->succeed("test qux = \"\$(curl localhost:8000/foo.txt)\"");
$machine->succeed("(cd work && NIX_PATH=nixpkgs='${pkgs.path}' arion down) && rm -rf work");
$machine->waitUntilFails("curl localhost:8000");
};
'';

View file

@ -0,0 +1,17 @@
{
docker-compose.services.webserver = { pkgs, ... }: {
nixos.useSystemd = true;
nixos.configuration.boot.tmpOnTmpfs = true;
nixos.configuration.services.nginx.enable = true;
# Please don't do this
nixos.configuration.services.nginx.virtualHosts.localhost.root = "/run/secrets";
service.useHostStore = true;
service.ports = [
"8000:80" # host:container
];
service.secrets."foo.txt".source = "foo";
};
docker-compose.secrets.foo.file = ./foo.key;
}

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 @@
qux