Assertions, warnings, renames

This commit is contained in:
Robert Hensing 2019-10-03 21:30:14 +02:00
parent 6abcc26a76
commit c3a5f8c13f
14 changed files with 106 additions and 27 deletions

View file

@ -19,6 +19,7 @@ data-files: nix/*.nix
, nix/modules/composition/*.nix , nix/modules/composition/*.nix
, nix/modules/nixos/*.nix , nix/modules/nixos/*.nix
, nix/modules/service/*.nix , nix/modules/service/*.nix
, nix/modules/lib/*.nix
-- all data is verbatim from some sources -- all data is verbatim from some sources
data-dir: src data-dir: src

View file

@ -11,7 +11,7 @@ let
eval = import (srcDir + "/nix/eval-composition.nix"); eval = import (srcDir + "/nix/eval-composition.nix");
build = args@{...}: build = args@{...}:
let composition = eval args; let composition = eval args;
in composition.config.build.dockerComposeYaml; in composition.config.out.dockerComposeYaml;
in in
justStaticExecutables (overrideCabal arion-compose (o: { justStaticExecutables (overrideCabal arion-compose (o: {

View file

@ -51,7 +51,7 @@ evaluateComposition ea = do
, "--strict" , "--strict"
, "--json" , "--json"
, "--attr" , "--attr"
, "config.build.dockerComposeYamlAttrs" , "config.out.dockerComposeYamlAttrs"
] ]
args = args =
[ evalComposition ] [ evalComposition ]
@ -99,7 +99,7 @@ buildComposition outLink ea = do
evalComposition <- getEvalCompositionFile evalComposition <- getEvalCompositionFile
let commandArgs = let commandArgs =
[ "--attr" [ "--attr"
, "config.build.dockerComposeYaml" , "config.out.dockerComposeYaml"
, "--out-link" , "--out-link"
, outLink , outLink
] ]

View file

@ -34,7 +34,7 @@ let
}; };
in in
# Typically you need composition.config.build.dockerComposeYaml # Typically you need composition.config.out.dockerComposeYaml
composition // { composition // {
# throw in lib and pkgs for repl convenience # throw in lib and pkgs for repl convenience
inherit lib; inherit lib;

View file

@ -34,7 +34,7 @@ in
config = { config = {
arionBaseImage = "${name}:${tag}"; arionBaseImage = "${name}:${tag}";
build.imagesToLoad = lib.mkIf (lib.any (s: s.service.useHostStore) (lib.attrValues config.docker-compose.services)) [ build.imagesToLoad = lib.mkIf (lib.any (s: s.service.useHostStore) (lib.attrValues config.services)) [
{ image = builtImage; imageName = name; imageTag = tag; } { image = builtImage; imageName = name; imageTag = tag; }
]; ];
}; };

View file

@ -3,10 +3,10 @@
This is a composition-level module. This is a composition-level module.
It defines the low-level options that are read by arion, like It defines the low-level options that are read by arion, like
- build.dockerComposeYaml - out.dockerComposeYaml
It declares options like It declares options like
- docker-compose.services - services
*/ */
compositionArgs@{ lib, config, options, pkgs, ... }: compositionArgs@{ lib, config, options, pkgs, ... }:
@ -31,20 +31,24 @@ let
in in
{ {
imports = [
../lib/assert.nix
(lib.mkRenamedOptionModule ["docker-compose" "services"] ["services"])
];
options = { options = {
build.dockerComposeYaml = lib.mkOption { out.dockerComposeYaml = lib.mkOption {
type = lib.types.package; type = lib.types.package;
description = "A derivation that produces a docker-compose.yaml file for this composition."; description = "A derivation that produces a docker-compose.yaml file for this composition.";
readOnly = true; readOnly = true;
}; };
build.dockerComposeYamlText = lib.mkOption { out.dockerComposeYamlText = lib.mkOption {
type = lib.types.str; type = lib.types.str;
description = "The text of build.dockerComposeYaml."; description = "The text of out.dockerComposeYaml.";
readOnly = true; readOnly = true;
}; };
build.dockerComposeYamlAttrs = lib.mkOption { out.dockerComposeYamlAttrs = lib.mkOption {
type = lib.types.attrsOf lib.types.unspecified; type = lib.types.attrsOf lib.types.unspecified;
description = "The text of build.dockerComposeYaml."; description = "The text of out.dockerComposeYaml.";
readOnly = true; readOnly = true;
}; };
docker-compose.raw = lib.mkOption { docker-compose.raw = lib.mkOption {
@ -55,19 +59,19 @@ in
type = lib.types.attrs; type = lib.types.attrs;
description = "Attribute set that will be turned into the x-arion section of the docker-compose.yaml file."; description = "Attribute set that will be turned into the x-arion section of the docker-compose.yaml file.";
}; };
docker-compose.services = lib.mkOption { services = lib.mkOption {
type = lib.types.attrsOf (lib.types.submodule service); type = lib.types.attrsOf (lib.types.submodule service);
description = "An attribute set of service configurations. A service specifies how to run an image as a container."; description = "An attribute set of service configurations. A service specifies how to run an image as a container.";
}; };
}; };
config = { config = {
build.dockerComposeYaml = pkgs.writeText "docker-compose.yaml" config.build.dockerComposeYamlText; out.dockerComposeYaml = pkgs.writeText "docker-compose.yaml" config.out.dockerComposeYamlText;
build.dockerComposeYamlText = builtins.toJSON (config.build.dockerComposeYamlAttrs); out.dockerComposeYamlText = builtins.toJSON (config.out.dockerComposeYamlAttrs);
build.dockerComposeYamlAttrs = config.docker-compose.raw; out.dockerComposeYamlAttrs = config.assertWarn config.docker-compose.raw;
docker-compose.raw = { docker-compose.raw = {
version = "3.4"; version = "3.4";
services = lib.mapAttrs (k: c: c.build.service) config.docker-compose.services; services = lib.mapAttrs (k: c: c.out.service) config.services;
x-arion = config.docker-compose.extended; x-arion = config.docker-compose.extended;
}; };
}; };

View file

@ -4,7 +4,7 @@ let
serviceImages = serviceImages =
lib.mapAttrs addDetails ( lib.mapAttrs addDetails (
lib.filterAttrs filterFunction config.docker-compose.services lib.filterAttrs filterFunction config.services
); );
filterFunction = serviceName: service: filterFunction = serviceName: service:
@ -14,7 +14,7 @@ let
addDetails = serviceName: service: addDetails = serviceName: service:
builtins.addErrorContext "while evaluating the image for service ${serviceName}" builtins.addErrorContext "while evaluating the image for service ${serviceName}"
(let (let
inherit (service.config) build; inherit (service) build;
in { in {
image = build.image.outPath; image = build.image.outPath;
imageName = build.imageName or service.image.name; imageName = build.imageName or service.image.name;

View file

@ -7,7 +7,7 @@
let let
serviceInfo = serviceInfo =
lib.mapAttrs getInfo ( lib.mapAttrs getInfo (
lib.filterAttrs filterFunction config.docker-compose.services lib.filterAttrs filterFunction config.services
); );
filterFunction = _serviceName: service: filterFunction = _serviceName: service:

View file

@ -0,0 +1,2 @@
`assertions.nix`: Nixpkgs
`assert.nix`: extracted from Nixpkgs, adapted

View file

@ -0,0 +1,32 @@
{ config, lib, pkgs, ... }:
# based on nixpkgs/nixos/modules/system/activation/top-level.nix
let
inherit (lib) filter concatStringsSep types mkOption;
# lib.showWarnings since 19.09
showWarnings = warnings: res: lib.fold (w: x: lib.warn w x) res warnings;
warn = msg: builtins.trace "warning: ${msg}";
# Handle assertions and warnings
failedAssertions = map (x: x.message) (filter (x: !x.assertion) config.assertions);
assertWarn = if failedAssertions != []
then throw "\nFailed assertions:\n${concatStringsSep "\n" (map (x: "- ${x}") failedAssertions)}"
else showWarnings config.warnings;
in
{
imports = [ ./assertions.nix ];
options.assertWarn = mkOption {
type = types.unspecified; # a function
# It's for the wrapping program to know about this. User need not care.
internal = true;
readOnly = true;
};
config = { inherit assertWarn; };
}

View file

@ -0,0 +1,34 @@
{ lib, ... }:
with lib;
{
options = {
assertions = mkOption {
type = types.listOf types.unspecified;
internal = true;
default = [];
example = [ { assertion = false; message = "you can't enable this for that reason"; } ];
description = ''
This option allows modules to express conditions that must
hold for the evaluation of the system configuration to
succeed, along with associated error messages for the user.
'';
};
warnings = mkOption {
internal = true;
default = [];
type = types.listOf types.str;
example = [ "The `foo' service is deprecated and will go away soon!" ];
description = ''
This option allows modules to show warnings to users during
the evaluation of the system configuration.
'';
};
};
# impl of assertions is in <nixpkgs/nixos/modules/system/activation/top-level.nix>
}

View file

@ -7,4 +7,5 @@
./image.nix ./image.nix
./nixos.nix ./nixos.nix
./nixos-init.nix ./nixos-init.nix
../lib/assert.nix
] ]

View file

@ -1,6 +1,6 @@
/* /*
This service-level module defines the build.service option, using This service-level module defines the out.service option, using
the user-facing options service.image, service.volumes, etc. the user-facing options service.image, service.volumes, etc.
*/ */
@ -25,8 +25,12 @@ let
in in
{ {
imports = [
(lib.mkRenamedOptionModule ["build" "service"] ["out" "service"])
];
options = { options = {
build.service = mkOption { out.service = mkOption {
type = attrsOf types.unspecified; type = attrsOf types.unspecified;
description = '' description = ''
Raw input for the service in <code>docker-compose.yaml</code>. Raw input for the service in <code>docker-compose.yaml</code>.
@ -37,12 +41,13 @@ in
This option is user accessible because it may serve as an This option is user accessible because it may serve as an
escape hatch for some. escape hatch for some.
''; '';
apply = config.assertWarn;
}; };
service.name = mkOption { service.name = mkOption {
type = str; type = str;
description = '' description = ''
The name of the service - <code>&lt;name></code> in the composition-level <code>docker-compose.services.&lt;name></code> The name of the service - <code>&lt;name></code> in the composition-level <code>services.&lt;name></code>
''; '';
readOnly = true; readOnly = true;
}; };
@ -209,7 +214,7 @@ in
}; };
}; };
config.build.service = { config.out.service = {
inherit (config.service) inherit (config.service)
volumes volumes
environment environment

View file

@ -30,9 +30,9 @@ in
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.out.dockerComposeYaml
(preEval [ ../../examples/full-nixos/arion-compose.nix ]).config.build.dockerComposeYaml (preEval [ ../../examples/full-nixos/arion-compose.nix ]).config.out.dockerComposeYaml
(preEval [ ../../examples/nixos-unit/arion-compose.nix ]).config.build.dockerComposeYaml (preEval [ ../../examples/nixos-unit/arion-compose.nix ]).config.out.dockerComposeYaml
pkgs.stdenv pkgs.stdenv
]; ];
}; };