Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add support for docker compose secrets #255

Open
wants to merge 7 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions arion-compose.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ data-files: nix/*.nix
, nix/modules/composition/*.nix
, nix/modules/networks/*.nix
, nix/modules/nixos/*.nix
, nix/modules/secrets/*.nix
, nix/modules/service/*.nix
, nix/modules/lib/*.nix

Expand Down
1 change: 1 addition & 0 deletions src/haskell/testdata/Arion/NixSpec/arion-compose.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
"name": "unit-test-data"
}
},
"secrets": {},
"services": {
"webserver": {
"command": [
Expand Down
20 changes: 19 additions & 1 deletion src/haskell/testdata/Arion/NixSpec/arion-context-compose.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,33 @@
"name": "unit-test-data"
}
},
"secrets": {
"foo": {
"environment": "FOO",
"external": false
}
},
"services": {
"webserver": {
"build": {
"context": "<STOREPATH>"
"context": "<STOREPATH>",
"secrets": [
"foo"
]
},
"environment": {},
"ports": [
"8080:80"
],
"secrets": [
{
"gid": "123",
"mode": "0440",
"source": "foo",
"target": "/run/secrets/foo",
"uid": "123"
}
],
"sysctls": {},
"volumes": []
}
Expand Down
10 changes: 10 additions & 0 deletions src/haskell/testdata/Arion/NixSpec/arion-context-compose.nix
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,18 @@
project.name = "unit-test-data";
services.webserver.service = {
build.context = "${./build-context}";
build.secrets = ["foo"];
ports = [
"8080:80"
];
secrets = {
"foo" = {
target = "/run/secrets/foo";
uid = "123";
gid = "123";
mode = "0440";
};
};
};
secrets.foo.environment = "FOO";
}
4 changes: 4 additions & 0 deletions src/nix/lib.nix
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,15 @@ let
networkRef = fragment:
''See ${link "https://github.com/compose-spec/compose-spec/blob/${composeSpecRev}/06-networks.md#${fragment}" "Compose Spec Networks #${fragment}"}'';

secretRef = fragment:
''See ${link "https://github.com/compose-spec/compose-spec/blob/${composeSpecRev}/09-secrets.md#${fragment}" "Compose Spec Secrets #${fragment}"}'';

in
{
inherit
link
networkRef
serviceRef
secretRef
;
}
3 changes: 2 additions & 1 deletion src/nix/modules.nix
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
./modules/composition/host-environment.nix
./modules/composition/images.nix
./modules/composition/networks.nix
./modules/composition/secrets.nix
./modules/composition/service-info.nix
./modules/composition/composition.nix
]
]
9 changes: 9 additions & 0 deletions src/nix/modules/composition/docker-compose.nix
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,14 @@ in
description = "A attribute set of volume configurations.";
default = {};
};
docker-compose.secrets = lib.mkOption {
type = lib.types.attrsOf lib.types.unspecified;
description = ''
An attribute set of secret configurations. For more info, see:
https://docs.docker.com/compose/compose-file/09-secrets/
'';
default = {};
};
};
config = {
out.dockerComposeYaml = pkgs.writeText "docker-compose.yaml" config.out.dockerComposeYamlText;
Expand All @@ -79,6 +87,7 @@ in
services = lib.mapAttrs (k: c: c.out.service) config.services;
x-arion = config.docker-compose.extended;
volumes = config.docker-compose.volumes;
secrets = config.docker-compose.secrets;
};
};
}
33 changes: 33 additions & 0 deletions src/nix/modules/composition/secrets.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
{ config, lib, ... }:

let
inherit (lib)
mkOption
types
;
inherit (import ../../lib.nix { inherit lib; })
link
;
in
{

options = {
secrets = mkOption {
type = types.lazyAttrsOf (types.submoduleWith {
modules = [
../secrets/secret.nix
];
});
description = ''
See ${link "https://docs.docker.com/compose/compose-file/09-secrets/" "Docker Compose Secrets"}
'';
};
};

config = {

secrets = {};
docker-compose.secrets = lib.mapAttrs (k: v: v.out) config.secrets;

};
}
65 changes: 65 additions & 0 deletions src/nix/modules/secrets/secret.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
{ config, lib, options, ... }:

let
inherit (lib)
mkOption
optionalAttrs
types
;
inherit (import ../../lib.nix { inherit lib; })
secretRef
;
in
{
options = {
file = mkOption {
description = ''
The secret is created with the contents of the file at the specified path.
${secretRef "file"}
'';
type = types.nullOr (types.either types.path types.str);
};

environment = mkOption {
description = ''
The secret is created with the value of an environment variable.
${secretRef "environment"}
'';
type = types.nullOr types.str;
};

external = lib.mkOption {
type = lib.types.bool;
default = false;
description = ''
Whether the value of this secret is set via other means.
${secretRef "secrets"}
'';
};

out = mkOption {
internal = true;
description = ''
Defines sensitive data that is granted to the services in your Compose application.
The source of the secret is either `file` or `environment`.
'';
type = lib.types.attrsOf lib.types.raw or lib.types.unspecified;
};
};

config = {
out =
lib.mapAttrs
(k: opt: opt.value)
(lib.filterAttrs
(k: opt: opt.isDefined)
{
inherit (options)
file
environment
external
;
}
);
};
}
130 changes: 108 additions & 22 deletions src/nix/modules/service/docker-compose-service.nix
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,41 @@ 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 = serviceRef "secrets";
};
target = mkOption {
type = nullOr str;
default = null;
description = serviceRef "secrets";
};
uid = mkOption {
type = nullOr str;
default = null;
description = serviceRef "secrets";
};
gid = mkOption {
type = nullOr str;
default = null;
description = serviceRef "secrets";
};
mode = mkOption {
type = nullOr str;
default = null;
example = "0444";
description = ''
The default value of is usually 0444. This option may not be supported
when not deploying to a Swarm.
${serviceRef "secrets"}
'';
};
};
};

in
{
imports = [
Expand Down Expand Up @@ -57,30 +92,66 @@ in
default = [];
description = serviceRef "tmpfs";
};
service.build.context = mkOption {
type = nullOr str;
default = null;
description = ''
Locates a Dockerfile to use for creating an image to use in this service.
service.build = mkOption {
default = {};
description = serviceRef "build";
type = submodule ({ options, ...}: {
options = {
_out = mkOption {
internal = true;
readOnly = true;
default = lib.mapAttrs (k: opt: opt.value) (lib.filterAttrs (_: opt: opt.value != null) { inherit (options) context dockerfile target secrets; });
};
context = mkOption {
type = nullOr str;
default = null;
description = ''
Locates a Dockerfile to use for creating an image to use in this service.

https://docs.docker.com/compose/compose-file/build/#context
'';
};
service.build.dockerfile = mkOption {
type = nullOr str;
default = null;
description = ''
Sets an alternate Dockerfile. A relative path is resolved from the build context.
https://docs.docker.com/compose/compose-file/build/#dockerfile
'';
https://docs.docker.com/compose/compose-file/build/#context
'';
};
dockerfile = mkOption {
type = nullOr str;
default = null;
description = ''
Sets an alternate Dockerfile. A relative path is resolved from the build context.
https://docs.docker.com/compose/compose-file/build/#dockerfile
'';
};
target = mkOption {
type = nullOr str;
default = null;
description = ''
Defines the stage to build as defined inside a multi-stage Dockerfile.
https://docs.docker.com/compose/compose-file/build/#target
'';
};
secrets = mkOption {
type = nullOr (either (listOf str) (attrsOf serviceSecretType));
default = null;
description = ''
Build-time secrets exposed to the service.
'';
};
};
});
};
service.build.target = mkOption {
type = nullOr str;
default = null;
service.secrets = mkOption {
type = nullOr (either (listOf str) (attrsOf serviceSecretType));
default = [];
description = ''
Defines the stage to build as defined inside a multi-stage Dockerfile.
https://docs.docker.com/compose/compose-file/build/#target
Run-time secrets exposed to the service.
'';
example = {
redis_secret = {
source = "web_cache_redis_secret";
target = "/run/secrets/web_cache_redis_secret";
uid = 123;
gid = 123;
mode = "0440";
};
};
};
service.hostname = mkOption {
type = nullOr str;
Expand Down Expand Up @@ -353,8 +424,8 @@ in
;
} // lib.optionalAttrs (config.service.image != null) {
inherit (config.service) image;
} // lib.optionalAttrs (config.service.build.context != null ) {
build = lib.filterAttrs (n: v: v != null) config.service.build;
} // lib.optionalAttrs (config.service.build._out != {}) {
build = config.service.build._out;
} // lib.optionalAttrs (cap_add != []) {
inherit cap_add;
} // lib.optionalAttrs (cap_drop != []) {
Expand All @@ -379,6 +450,21 @@ in
inherit (config.service) external_links;
} // lib.optionalAttrs (config.service.extra_hosts != []) {
inherit (config.service) extra_hosts;
} // lib.optionalAttrs (config.service.secrets != []) {
secrets = lib.mapAttrsToList (k: s: {
source = k;
target = k;
} // lib.optionalAttrs (s.source != null) {
inherit (s) source;
} // lib.optionalAttrs (s.target != null) {
inherit (s) target;
} // lib.optionalAttrs (s.uid != null) {
inherit (s) uid;
} // lib.optionalAttrs (s.gid != null) {
inherit (s) gid;
} // lib.optionalAttrs (s.mode != null) {
inherit (s) mode;
}) config.service.secrets;
} // lib.optionalAttrs (config.service.hostname != null) {
inherit (config.service) hostname;
} // lib.optionalAttrs (config.service.dns != []) {
Expand Down