packages feed

arion-compose-0.1.2.0: src/nix/modules/service/image.nix

{ pkgs, lib, config, options, ... }:
let
  inherit (lib) types mkOption;
  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))
  ];

  buildOrStreamLayeredImage = args:
    if pkgs.dockerTools?streamLayeredImage
    then pkgs.dockerTools.streamLayeredImage args // { isExe = true; }
    else pkgs.dockerTools.buildLayeredImage args;

  builtImage = buildOrStreamLayeredImage {
    inherit (config.image)
      name
      contents
      ;
    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;
      '';
  };

  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 = config.service.name;
      defaultText = lib.literalExample "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.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 https://github.com/moby/moby/blob/master/image/spec/v1.2.md#image-json-field-descriptions[Docker Image Specification
        v1.2.0].
      '';
    };
    image.command = mkOption {
      type = listOf str;
      default = [];
      description = ''
      '';
    };
  };
  config = {
    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));

    service.image = lib.mkDefault "${config.build.imageName}:${config.build.imageTag}";
    image.rawConfig.Cmd = config.image.command;

    image.nixBuild = lib.mkDefault (priorityIsDefault options.service.image);
  };
}