arion-compose-0.2.2.0: src/nix/modules/service/image.nix
{ pkgs, lib, config, options, ... }:
let
inherit (lib)
functionArgs
mkOption
optionalAttrs
types
warn
;
inherit (pkgs)
dockerTools
;
inherit (types) attrsOf listOf nullOr package str unspecified bool;
# TODO: dummy-config is a useless layer. Nix 2.3 will let us inspect
# the string context instead, so we can avoid this.
contentsList = config.image.contents ++ [
(pkgs.writeText "dummy-config.json" (builtins.toJSON config.image.rawConfig))
];
includeStorePathsWarningAndDefault = lib.warn ''
You're using a version of Nixpkgs that doesn't support the includeStorePaths
parameter in dockerTools.streamLayeredImage. Without this, Arion's
useHostStore does not achieve the intended speedup.
'' {};
buildOrStreamLayeredImage = args:
let
args_base = builtins.intersectAttrs
{
name = null; tag = null; contents = null; config = null;
created = null; extraCommands = null; maxLayers = null;
fakeRootCommands = null;
}
args;
acceptedArgs = functionArgs dockerTools.streamLayeredImage;
args_no_store = lib.optionalAttrs (!(args.includeStorePaths or true)) (
if acceptedArgs ? includeStorePaths
then { inherit (args) includeStorePaths; }
else includeStorePathsWarningAndDefault
);
args_streamLayered = args_base // args_no_store;
in
if dockerTools?streamLayeredImage
then dockerTools.streamLayeredImage args_streamLayered // { isExe = true; }
else dockerTools.buildLayeredImage args_base;
builtImage = buildOrStreamLayeredImage {
inherit (config.image)
name
contents
includeStorePaths
;
config = config.image.rawConfig;
maxLayers = 100;
# TODO: allow use of image's Nix package instead
# TODO: option to disable db generation
extraCommands = ''
echo "Generating the nix database..."
echo "Warning: only the database of the deepest Nix layer is loaded."
echo " If you want to use nix commands in the container, it would"
echo " be better to only have one layer that contains a nix store."
export NIX_REMOTE=local?root=$PWD
${pkgs.nix}/bin/nix-store --load-db < ${pkgs.closureInfo {rootPaths = contentsList;}}/registration
mkdir -p nix/var/nix/gcroots/docker/
for i in ${lib.concatStringsSep " " contentsList}; do
ln -s $i nix/var/nix/gcroots/docker/$(basename $i)
done;
'';
fakeRootCommands = config.image.fakeRootCommands;
};
priorityIsDefault = option: option.highestPrio >= (lib.mkDefault true).priority;
in
{
options = {
build.image = mkOption {
type = nullOr package;
description = ''
Docker image derivation to be `docker load`-ed.
'';
internal = true;
};
build.imageName = mkOption {
type = str;
description = "Derived from `build.image`";
internal = true;
};
build.imageTag = mkOption {
type = str;
description = "Derived from `build.image`";
internal = true;
};
image.nixBuild = mkOption {
type = bool;
description = ''
Whether to build this image with Nixpkgs'
`dockerTools.buildLayeredImage`
and then load it with `docker load`.
By default, an image will be built with Nix unless `service.image`
is set. See also `image.name`, which defaults to
the service name.
'';
};
image.name = mkOption {
type = str;
default = "localhost/" + config.service.name;
defaultText = lib.literalExpression or lib.literalExample ''"localhost/" + config.service.name'';
description = ''
A human readable name for the docker image.
Shows up in the `docker ps` output in the
`IMAGE` column, among other places.
'';
};
image.contents = mkOption {
type = listOf package;
default = [];
description = ''
Top level paths in the container.
'';
};
image.fakeRootCommands = mkOption {
type = types.lines;
default = "";
description = ''
Commands that build the root of the container in the current working directory.
See [`dockerTools.buildLayeredImage`](https://nixos.org/manual/nixpkgs/stable/#ssec-pkgs-dockerTools-buildLayeredImage).
'';
};
image.includeStorePaths = mkOption {
type = bool;
default = true;
internal = true;
description = ''
Include all referenced store paths. You generally want this in your
image, unless you load store paths via some other means, like `useHostStore = true`;
'';
};
image.rawConfig = mkOption {
type = attrsOf unspecified;
default = {};
description = ''
This is a low-level fallback for when a container option has not
been modeled in the Arion module system.
This attribute set does not have an appropriate merge function.
Please use the specific `image` options instead.
Run-time configuration of the container. A full list of the
options is available in the [Docker Image Specification
v1.2.0](https://github.com/moby/moby/blob/master/image/spec/v1.2.md#image-json-field-descriptions).
'';
};
image.command = mkOption {
type = listOf str;
default = [];
description = ''
'';
};
};
config = lib.mkMerge [{
build.image = builtImage;
build.imageName = config.build.image.imageName;
build.imageTag =
if config.build.image.imageTag != ""
then config.build.image.imageTag
else lib.head (lib.strings.splitString "-" (baseNameOf config.build.image.outPath));
image.rawConfig.Cmd = config.image.command;
image.nixBuild = lib.mkDefault (priorityIsDefault options.service.image);
}
( lib.mkIf (config.service.build.context == null)
{
service.image = lib.mkDefault "${config.build.imageName}:${config.build.imageTag}";
})
];
}