Skip to content

Commit

Permalink
Add secrets options to service and composition
Browse files Browse the repository at this point in the history
  • Loading branch information
roberth committed May 22, 2019
1 parent 221bccd commit a6878c0
Show file tree
Hide file tree
Showing 6 changed files with 128 additions and 1 deletion.
40 changes: 40 additions & 0 deletions src/nix/modules/composition/docker-compose.nix
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,36 @@
*/
{ pkgs, lib, config, ... }:
let
cfg = config.docker-compose;
inherit (lib) mkOption optionalAttrs mapAttrs;
inherit (lib.types) submodule attrsOf nullOr 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 = {
Expand Down Expand Up @@ -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;
Expand All @@ -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;
};
};
}
61 changes: 60 additions & 1 deletion src/nix/modules/service/docker-compose-service.nix
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -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 = {
Expand Down Expand Up @@ -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 = {
Expand Down Expand Up @@ -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) {
Expand Down
8 changes: 8 additions & 0 deletions tests/arion-test/default.nix
Original file line number Diff line number Diff line change
Expand Up @@ -63,5 +63,13 @@ in
$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)\"");
$machine->succeed("cd work && NIX_PATH=nixpkgs='${pkgs.path}' arion down && rm -rf work");
$machine->waitUntilFails("curl localhost:8000");
};
'';
}
17 changes: 17 additions & 0 deletions tests/testcases/secrets/arion-compose.nix
Original file line number Diff line number Diff line change
@@ -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 changes: 2 additions & 0 deletions tests/testcases/secrets/arion-pkgs.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# Instead of pinning Nixpkgs, we can opt to use the one in NIX_PATH
import <nixpkgs> {}
1 change: 1 addition & 0 deletions tests/testcases/secrets/foo.key
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
qux

0 comments on commit a6878c0

Please sign in to comment.