Compare commits
2 commits
Author | SHA1 | Date | |
---|---|---|---|
|
894cb2815b | ||
|
1308b729c1 |
6 changed files with 135 additions and 8 deletions
|
@ -11,8 +11,36 @@
|
||||||
*/
|
*/
|
||||||
{ pkgs, lib, config, ... }:
|
{ pkgs, lib, config, ... }:
|
||||||
let
|
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; };
|
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
|
in
|
||||||
{
|
{
|
||||||
options = {
|
options = {
|
||||||
|
@ -42,6 +70,11 @@ in
|
||||||
description = "Attribute set of evaluated service configurations.";
|
description = "Attribute set of evaluated service configurations.";
|
||||||
readOnly = true;
|
readOnly = true;
|
||||||
};
|
};
|
||||||
|
docker-compose.secrets = lib.mkOption {
|
||||||
|
type = attrsOf secretType;
|
||||||
|
description = dockerComposeRef "secrets";
|
||||||
|
default = {};
|
||||||
|
};
|
||||||
};
|
};
|
||||||
config = {
|
config = {
|
||||||
build.dockerComposeYaml = pkgs.writeText "docker-compose.yaml" config.build.dockerComposeYamlText;
|
build.dockerComposeYaml = pkgs.writeText "docker-compose.yaml" config.build.dockerComposeYamlText;
|
||||||
|
@ -52,6 +85,13 @@ in
|
||||||
version = "3.4";
|
version = "3.4";
|
||||||
services = lib.mapAttrs (k: c: c.config.build.service) config.docker-compose.evaluatedServices;
|
services = lib.mapAttrs (k: c: c.config.build.service) config.docker-compose.evaluatedServices;
|
||||||
x-arion = config.docker-compose.extended;
|
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;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,9 +7,11 @@
|
||||||
{ pkgs, lib, config, ... }:
|
{ pkgs, lib, config, ... }:
|
||||||
|
|
||||||
let
|
let
|
||||||
inherit (lib) mkOption types;
|
inherit (lib) mkOption types mapAttrs mapAttrsToList;
|
||||||
inherit (types) listOf nullOr attrsOf str either int bool;
|
inherit (types) listOf nullOr attrsOf str either int bool;
|
||||||
|
|
||||||
|
cfg = config.service;
|
||||||
|
|
||||||
link = url: text:
|
link = url: text:
|
||||||
''<link xlink:href="${url}">${text}</link>'';
|
''<link xlink:href="${url}">${text}</link>'';
|
||||||
dockerComposeRef = fragment:
|
dockerComposeRef = fragment:
|
||||||
|
@ -23,6 +25,48 @@ let
|
||||||
cap_add = lib.attrNames (lib.filterAttrs (name: value: value == true) config.service.capabilities);
|
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);
|
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
|
in
|
||||||
{
|
{
|
||||||
options = {
|
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 = {
|
config.build.service = {
|
||||||
|
@ -242,6 +299,8 @@ in
|
||||||
inherit (config.service) restart;
|
inherit (config.service) restart;
|
||||||
} // lib.optionalAttrs (config.service.stop_signal != null) {
|
} // lib.optionalAttrs (config.service.stop_signal != null) {
|
||||||
inherit (config.service) stop_signal;
|
inherit (config.service) stop_signal;
|
||||||
|
} // lib.optionalAttrs (secrets != null) {
|
||||||
|
inherit secrets;
|
||||||
} // lib.optionalAttrs (config.service.tmpfs != []) {
|
} // lib.optionalAttrs (config.service.tmpfs != []) {
|
||||||
inherit (config.service) tmpfs;
|
inherit (config.service) tmpfs;
|
||||||
} // lib.optionalAttrs (config.service.tty != null) {
|
} // lib.optionalAttrs (config.service.tty != null) {
|
||||||
|
|
|
@ -12,11 +12,10 @@ in
|
||||||
machine = { pkgs, lib, ... }: {
|
machine = { pkgs, lib, ... }: {
|
||||||
environment.systemPackages = [
|
environment.systemPackages = [
|
||||||
pkgs.arion
|
pkgs.arion
|
||||||
pkgs.docker-compose
|
|
||||||
];
|
];
|
||||||
virtualisation.docker.enable = true;
|
virtualisation.docker.enable = true;
|
||||||
|
|
||||||
# no caches, because no internet
|
# no caches, because the test cannot access the internet
|
||||||
nix.binaryCaches = lib.mkForce [];
|
nix.binaryCaches = lib.mkForce [];
|
||||||
|
|
||||||
# FIXME: Sandbox seems broken with current version of NixOS test
|
# FIXME: Sandbox seems broken with current version of NixOS test
|
||||||
|
@ -29,13 +28,14 @@ in
|
||||||
|
|
||||||
virtualisation.writableStore = true;
|
virtualisation.writableStore = true;
|
||||||
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 [ ../../examples/minimal/arion-compose.nix ]).config.build.dockerComposeYaml
|
(preEval [ ../../examples/minimal/arion-compose.nix ]).config.build.dockerComposeYaml
|
||||||
(preEval [ ../../examples/full-nixos/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 [ ../../examples/nixos-unit/arion-compose.nix ]).config.build.dockerComposeYaml
|
||||||
|
(preEval [ ../testcases/secrets/arion-compose.nix ]).config.build.dockerComposeYaml
|
||||||
pkgs.stdenv
|
pkgs.stdenv
|
||||||
];
|
];
|
||||||
|
virtualisation.memorySize = 4096; #MB
|
||||||
};
|
};
|
||||||
testScript = ''
|
testScript = ''
|
||||||
$machine->fail("curl localhost:8000");
|
$machine->fail("curl localhost:8000");
|
||||||
|
@ -44,7 +44,7 @@ in
|
||||||
subtest "minimal", sub {
|
subtest "minimal", sub {
|
||||||
$machine->succeed("cp -r ${../../examples/minimal} work && cd work && NIX_PATH=nixpkgs='${pkgs.path}' arion up -d");
|
$machine->succeed("cp -r ${../../examples/minimal} work && cd work && NIX_PATH=nixpkgs='${pkgs.path}' arion up -d");
|
||||||
$machine->waitUntilSucceeds("curl localhost:8000");
|
$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");
|
$machine->waitUntilFails("curl localhost:8000");
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -53,14 +53,22 @@ in
|
||||||
$machine->waitUntilSucceeds("curl localhost:8000");
|
$machine->waitUntilSucceeds("curl localhost:8000");
|
||||||
# Also test exec with defaultExec
|
# 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 && 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");
|
$machine->waitUntilFails("curl localhost:8000");
|
||||||
};
|
};
|
||||||
|
|
||||||
subtest "nixos-unit", sub {
|
subtest "nixos-unit", sub {
|
||||||
$machine->succeed("cp -r ${../../examples/nixos-unit} work && cd work && NIX_PATH=nixpkgs='${pkgs.path}' arion up -d");
|
$machine->succeed("cp -r ${../../examples/nixos-unit} work && cd work && NIX_PATH=nixpkgs='${pkgs.path}' arion up -d");
|
||||||
$machine->waitUntilSucceeds("curl localhost:8000");
|
$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");
|
$machine->waitUntilFails("curl localhost:8000");
|
||||||
};
|
};
|
||||||
'';
|
'';
|
||||||
|
|
17
tests/testcases/secrets/arion-compose.nix
Normal file
17
tests/testcases/secrets/arion-compose.nix
Normal 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;
|
||||||
|
}
|
2
tests/testcases/secrets/arion-pkgs.nix
Normal file
2
tests/testcases/secrets/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> {}
|
1
tests/testcases/secrets/foo.key
Normal file
1
tests/testcases/secrets/foo.key
Normal file
|
@ -0,0 +1 @@
|
||||||
|
qux
|
Loading…
Reference in a new issue