Skip to content

Commit 84b5e67

Browse files
add support for docker compose secrets
enables using [docker compose secrets](https://docs.docker.com/compose/use-secrets/) from arion, which includes: - [top-level `secrets` element](https://docs.docker.com/compose/compose-file/09-secrets/) defining the secrets to be used for the below two use-cases, exposing them at `/run/secrets/<secret_name>`. comes in flavors `file` vs `environment`. - run-time: [`services` top-level `secrets` element](https://docs.docker.com/compose/compose-file/05-services/#secrets) - build time: [build secrets](https://docs.docker.com/build/building/secrets/) (to be [mounted](https://docs.docker.com/build/building/secrets/#secret-mounts) in the `Dockerfile` like `RUN --mount=type=secret,id=<secret_name> ...`) unlike hercules-ci#52, i did not so far add support for their [long syntax](https://docs.docker.com/compose/compose-file/05-services/#long-syntax-4), which despite the confusing documentation appears [limited to Docker Swarm](docker/compose#9648 (comment)), in my understanding limiting its use in Arion.
1 parent 236f9dd commit 84b5e67

File tree

9 files changed

+166
-24
lines changed

9 files changed

+166
-24
lines changed

src/haskell/testdata/Arion/NixSpec/arion-compose.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
"name": "unit-test-data"
55
}
66
},
7+
"secrets": {},
78
"services": {
89
"webserver": {
910
"command": [

src/haskell/testdata/Arion/NixSpec/arion-context-compose.json

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,18 @@
44
"name": "unit-test-data"
55
}
66
},
7+
"secrets": {
8+
"foo": {
9+
"environment": "FOO"
10+
}
11+
},
712
"services": {
813
"webserver": {
914
"build": {
10-
"context": "<STOREPATH>"
15+
"context": "<STOREPATH>",
16+
"secrets": [
17+
"foo"
18+
]
1119
},
1220
"environment": {},
1321
"ports": [

src/haskell/testdata/Arion/NixSpec/arion-context-compose.nix

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,10 @@
22
project.name = "unit-test-data";
33
services.webserver.service = {
44
build.context = "${./build-context}";
5+
build.secrets = ["foo"];
56
ports = [
67
"8080:80"
78
];
89
};
10+
secrets.foo.environment = "FOO";
911
}

src/nix/lib.nix

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,15 @@ let
1111
networkRef = fragment:
1212
''See ${link "https://github.com/compose-spec/compose-spec/blob/${composeSpecRev}/06-networks.md#${fragment}" "Compose Spec Networks #${fragment}"}'';
1313

14+
secretRef = fragment:
15+
''See ${link "https://github.com/compose-spec/compose-spec/blob/${composeSpecRev}/09-secrets.md#${fragment}" "Compose Spec Secrets #${fragment}"}'';
16+
1417
in
1518
{
1619
inherit
1720
link
1821
networkRef
1922
serviceRef
23+
secretRef
2024
;
2125
}

src/nix/modules.nix

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
./modules/composition/host-environment.nix
44
./modules/composition/images.nix
55
./modules/composition/networks.nix
6+
./modules/composition/secrets.nix
67
./modules/composition/service-info.nix
78
./modules/composition/composition.nix
8-
]
9+
]

src/nix/modules/composition/docker-compose.nix

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,14 @@ in
6868
description = "A attribute set of volume configurations.";
6969
default = {};
7070
};
71+
docker-compose.secrets = lib.mkOption {
72+
type = lib.types.attrsOf lib.types.unspecified;
73+
description = ''
74+
An attribute set of secret configurations. For more info, see:
75+
https://docs.docker.com/compose/compose-file/09-secrets/
76+
'';
77+
default = {};
78+
};
7179
};
7280
config = {
7381
out.dockerComposeYaml = pkgs.writeText "docker-compose.yaml" config.out.dockerComposeYamlText;
@@ -79,6 +87,7 @@ in
7987
services = lib.mapAttrs (k: c: c.out.service) config.services;
8088
x-arion = config.docker-compose.extended;
8189
volumes = config.docker-compose.volumes;
90+
secrets = config.docker-compose.secrets;
8291
};
8392
};
8493
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
{ config, lib, ... }:
2+
3+
let
4+
inherit (lib)
5+
mkOption
6+
types
7+
;
8+
inherit (import ../../lib.nix { inherit lib; })
9+
link
10+
;
11+
in
12+
{
13+
14+
options = {
15+
secrets = mkOption {
16+
type = types.lazyAttrsOf (types.submoduleWith {
17+
modules = [
18+
../secrets/secret.nix
19+
];
20+
});
21+
description = ''
22+
See ${link "https://docs.docker.com/compose/compose-file/09-secrets/" "Docker Compose Secrets"}
23+
'';
24+
};
25+
};
26+
27+
config = {
28+
29+
secrets = {};
30+
docker-compose.secrets = lib.mapAttrs (k: v: v.out) config.secrets;
31+
32+
};
33+
}

src/nix/modules/secrets/secret.nix

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
{ config, lib, options, ... }:
2+
3+
let
4+
inherit (lib)
5+
mkOption
6+
optionalAttrs
7+
types
8+
;
9+
inherit (import ../../lib.nix { inherit lib; })
10+
secretRef
11+
;
12+
in
13+
{
14+
options = {
15+
file = mkOption {
16+
description = ''
17+
The secret is created with the contents of the file at the specified path.
18+
${secretRef "file"}
19+
'';
20+
type = types.nullOr types.str;
21+
};
22+
23+
environment = mkOption {
24+
description = ''
25+
The secret is created with the value of an environment variable.
26+
${secretRef "environment"}
27+
'';
28+
type = types.nullOr types.str;
29+
};
30+
31+
out = mkOption {
32+
internal = true;
33+
description = ''
34+
Defines sensitive data that is granted to the services in your Compose application.
35+
The source of the secret is either `file` or `environment`.
36+
'';
37+
type = lib.types.attrsOf lib.types.raw or lib.types.unspecified;
38+
};
39+
};
40+
41+
config = {
42+
out =
43+
lib.mapAttrs
44+
(k: opt: opt.value)
45+
(lib.filterAttrs
46+
(k: opt: opt.isDefined)
47+
{
48+
inherit (options)
49+
file
50+
environment
51+
;
52+
}
53+
);
54+
};
55+
}

src/nix/modules/service/docker-compose-service.nix

Lines changed: 51 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -57,29 +57,56 @@ in
5757
default = [];
5858
description = serviceRef "tmpfs";
5959
};
60-
service.build.context = mkOption {
61-
type = nullOr str;
62-
default = null;
63-
description = ''
64-
Locates a Dockerfile to use for creating an image to use in this service.
60+
service.build = mkOption {
61+
default = {};
62+
description = serviceRef "build";
63+
type = submodule ({ options, ...}: {
64+
options = {
65+
_out = mkOption {
66+
internal = true;
67+
readOnly = true;
68+
default = lib.mapAttrs (k: opt: opt.value) (lib.filterAttrs (_: opt: opt.value != null) { inherit (options) context dockerfile target secrets; });
69+
};
70+
context = mkOption {
71+
type = nullOr str;
72+
default = null;
73+
description = ''
74+
Locates a Dockerfile to use for creating an image to use in this service.
6575
66-
https://docs.docker.com/compose/compose-file/build/#context
67-
'';
68-
};
69-
service.build.dockerfile = mkOption {
70-
type = nullOr str;
71-
default = null;
72-
description = ''
73-
Sets an alternate Dockerfile. A relative path is resolved from the build context.
74-
https://docs.docker.com/compose/compose-file/build/#dockerfile
75-
'';
76+
https://docs.docker.com/compose/compose-file/build/#context
77+
'';
78+
};
79+
dockerfile = mkOption {
80+
type = nullOr str;
81+
default = null;
82+
description = ''
83+
Sets an alternate Dockerfile. A relative path is resolved from the build context.
84+
https://docs.docker.com/compose/compose-file/build/#dockerfile
85+
'';
86+
};
87+
target = mkOption {
88+
type = nullOr str;
89+
default = null;
90+
description = ''
91+
Defines the stage to build as defined inside a multi-stage Dockerfile.
92+
https://docs.docker.com/compose/compose-file/build/#target
93+
'';
94+
};
95+
secrets = mkOption {
96+
type = nullOr (listOf str);
97+
default = null;
98+
description = ''
99+
Build-time secrets exposed to the service.
100+
'';
101+
};
102+
};
103+
});
76104
};
77-
service.build.target = mkOption {
78-
type = nullOr str;
79-
default = null;
105+
service.secrets = mkOption {
106+
type = listOf str;
107+
default = [];
80108
description = ''
81-
Defines the stage to build as defined inside a multi-stage Dockerfile.
82-
https://docs.docker.com/compose/compose-file/build/#target
109+
Run-time secrets exposed to the service.
83110
'';
84111
};
85112
service.hostname = mkOption {
@@ -353,8 +380,8 @@ in
353380
;
354381
} // lib.optionalAttrs (config.service.image != null) {
355382
inherit (config.service) image;
356-
} // lib.optionalAttrs (config.service.build.context != null ) {
357-
build = lib.filterAttrs (n: v: v != null) config.service.build;
383+
} // lib.optionalAttrs (config.service.build._out != {}) {
384+
build = config.service.build._out;
358385
} // lib.optionalAttrs (cap_add != []) {
359386
inherit cap_add;
360387
} // lib.optionalAttrs (cap_drop != []) {
@@ -379,6 +406,8 @@ in
379406
inherit (config.service) external_links;
380407
} // lib.optionalAttrs (config.service.extra_hosts != []) {
381408
inherit (config.service) extra_hosts;
409+
} // lib.optionalAttrs (config.service.secrets != []) {
410+
inherit (config.service) secrets;
382411
} // lib.optionalAttrs (config.service.hostname != null) {
383412
inherit (config.service) hostname;
384413
} // lib.optionalAttrs (config.service.dns != []) {

0 commit comments

Comments
 (0)