arion-compose 0.1.3.0 → 0.2.0.0
raw patch · 15 files changed
+330/−25 lines, 15 filesdep ~aesonPVP ok
version bump matches the API change (PVP)
Dependency ranges changed: aeson
API changes (from Hackage documentation)
Files
- CHANGELOG.md +32/−0
- arion-compose.cabal +8/−3
- src/haskell/exe/Main.hs +1/−1
- src/haskell/lib/Arion/Images.hs +2/−1
- src/haskell/lib/Arion/Services.hs +2/−1
- src/haskell/testdata/Arion/NixSpec/arion-compose.json +8/−3
- src/haskell/testdata/Arion/NixSpec/arion-compose.nix +1/−0
- src/nix/eval-composition.nix +2/−2
- src/nix/lib.nix +16/−0
- src/nix/modules.nix +1/−0
- src/nix/modules/composition/composition.nix +6/−2
- src/nix/modules/composition/networks.nix +53/−0
- src/nix/modules/networks/network.nix +131/−0
- src/nix/modules/service/docker-compose-service.nix +65/−10
- src/nix/modules/service/image.nix +2/−2
CHANGELOG.md view
@@ -1,5 +1,37 @@ # Revision history for Arion +## Next 0.2.0.0++### BREAKING++* The `project.name` option is now mandatory for projects that aren't deployed with the NixOS module.+++* The NixOS module now sets the default network name to the project name (commonly referred to as `<name>` in the option path).+ If this is not desired, for instance if you need the projects to be on the same network, set `networks.default.name` in each of them.++* The NixOS module now sets the default project name. You can still set your own value with the `project.name` option.+ If you did not set one, docker compose heuristically determined the name to be `store`, so you may want to set `project.name = "store"` or prepare to rename the network manually.++### Removed++ - NixOS 20.09 support. Its docker-compose does not support the+ `networks.<name>.name` option, which is important in later versions.+ A newer, bundled docker compose may work there, but for now the decision+ is to drop this legacy version.++### Changed++* Healthcheck-based dependencies in `service.depends_on`.++### Added++* Support `service.healthcheck` for defining custom healthchecks.+* Arion now declares a `networks.default` by default, with `name` set to+ `project.name`. This improves compatibility with container runtimes by+ copying pre-existing behavior. Most users will want to keep using this+ behavior, but it can be disabled with `enableDefaultNetwork`.+ ## 0.1.3.0 -- 2020-05-03 ### Changed
arion-compose.cabal view
@@ -1,7 +1,7 @@ cabal-version: 2.4 name: arion-compose-version: 0.1.3.0+version: 0.2.0.0 synopsis: Run docker-compose with help from Nix/NixOS description: Arion is a tool for building and running applications that consist of multiple docker containers using NixOS modules. It has special support for docker images that are built with Nix, for a smooth development experience and improved performance. homepage: https://github.com/hercules-ci/arion#readme@@ -17,6 +17,7 @@ src/haskell/testdata/**/*.json data-files: nix/*.nix , nix/modules/composition/*.nix+ , nix/modules/networks/*.nix , nix/modules/nixos/*.nix , nix/modules/service/*.nix , nix/modules/lib/*.nix@@ -24,9 +25,13 @@ -- all data is verbatim from some sources data-dir: src +source-repository head+ type: git+ location: https://github.com/hercules-ci/arion+ common common- build-depends: base >=4.12.0.0 && <4.15- , aeson+ build-depends: base >=4.12.0.0 && <4.17+ , aeson >=2 , aeson-pretty , async , bytestring
src/haskell/exe/Main.hs view
@@ -60,7 +60,7 @@ <> help "Use Nix expression EXPR to get the Nixpkgs attrset used for bootstrapping \ \and evaluating the configuration." ) showTrace <- flag False True (long "show-trace"- <> help "Causes Nix to print out a stack trace in case of Nix expression evaluation errors.")+ <> help "Causes Nix to print out a stack trace in case of Nix expression evaluation errors. Specify before command.") -- TODO --option support (https://github.com/pcapriotti/optparse-applicative/issues/284) userNixArgs <- many (T.pack <$> strOption (long "nix-arg" <> metavar "ARG" <> help "Pass an extra argument to nix. Example: --nix-arg --option --nix-arg substitute --nix-arg false")) prebuiltComposeFile <- optional $ strOption
src/haskell/lib/Arion/Images.hs view
@@ -25,7 +25,8 @@ isNew i = -- On docker, the image name is unmodified (imageName i <> ":" <> imageTag i) `notElem` loaded- -- -- On podman, you automatically get a localhost prefix+ -- On podman, you used to automatically get a localhost prefix+ -- however, since NixOS 22.05, this expected to be part of the name instead && ("localhost/" <> imageName i <> ":" <> imageTag i) `notElem` loaded traverse_ loadImage . filter isNew $ requestedImages
src/haskell/lib/Arion/Services.hs view
@@ -9,6 +9,7 @@ import Protolude hiding (to) import qualified Data.Aeson as Aeson+import qualified Data.Aeson.Key as AK import Arion.Aeson (decodeFile) import Control.Lens@@ -20,4 +21,4 @@ v <- decodeFile fp - pure ((v :: Aeson.Value) ^.. key "x-arion" . key "serviceInfo" . key service . key "defaultExec" . _Array . traverse . _String)+ pure ((v :: Aeson.Value) ^.. key "x-arion" . key "serviceInfo" . key (AK.fromText service) . key "defaultExec" . _Array . traverse . _String)
src/haskell/testdata/Arion/NixSpec/arion-compose.json view
@@ -1,4 +1,9 @@ {+ "networks": {+ "default": {+ "name": "unit-test-data"+ }+ }, "services": { "webserver": { "command": [@@ -9,7 +14,7 @@ "PATH": "/usr/bin:/run/current-system/sw/bin/", "container": "docker" },- "image": "webserver:<HASH>",+ "image": "localhost/webserver:<HASH>", "ports": [ "8000:80" ],@@ -32,12 +37,12 @@ "images": [ { "imageExe": "<STOREPATH>",- "imageName": "webserver",+ "imageName": "localhost/webserver", "imageTag": "<HASH>" } ], "project": {- "name": null+ "name": "unit-test-data" }, "serviceInfo": { "webserver": {
src/haskell/testdata/Arion/NixSpec/arion-compose.nix view
@@ -1,4 +1,5 @@ {+ project.name = "unit-test-data"; services.webserver = { pkgs, ... }: { nixos.useSystemd = true; nixos.configuration.boot.tmpOnTmpfs = true;
src/nix/eval-composition.nix view
@@ -12,7 +12,6 @@ inherit (pkgs) lib; composition = lib.evalModules {- check = true; modules = builtinModules ++ modules; }; @@ -24,6 +23,7 @@ _file = ./eval-composition.nix; key = ./eval-composition.nix; config._module.args.pkgs = lib.mkIf (pkgs != null) (lib.mkForce pkgs);+ config._module.args.check = true; config.host.nixStorePrefix = hostNixStorePrefix; config.host.uid = lib.toInt uid; };@@ -33,5 +33,5 @@ composition // { # throw in lib and pkgs for repl convenience inherit lib;- inherit (composition.config._module.args) pkgs;+ inherit (composition._module.args) pkgs; }
+ src/nix/lib.nix view
@@ -0,0 +1,16 @@+{ lib }:+let++ link = url: text:+ ''link:${url}[${text}]'';++ dockerComposeRef = fragment:+ ''See ${link "https://docs.docker.com/compose/compose-file/#${fragment}" "Docker Compose#${fragment}"}'';++in+{+ inherit+ dockerComposeRef+ link+ ;+}
src/nix/modules.nix view
@@ -2,6 +2,7 @@ ./modules/composition/docker-compose.nix ./modules/composition/host-environment.nix ./modules/composition/images.nix+ ./modules/composition/networks.nix ./modules/composition/service-info.nix ./modules/composition/composition.nix ]
src/nix/modules/composition/composition.nix view
@@ -8,14 +8,18 @@ in { options = {+ _module.args = mkOption {+ internal = true;+ }; project.name = mkOption { description = '' Name of the project. See ${link "https://docs.docker.com/compose/reference/envvars/#compose_project_name" "COMPOSE_PROJECT_NAME"}++ This is not optional, because getting the project name from a directory name tends to produce different results for different repo checkout location names. '';- type = types.nullOr types.str;- default = null;+ type = types.str; }; }; config = {
+ src/nix/modules/composition/networks.nix view
@@ -0,0 +1,53 @@+{ config, lib, ... }:++let+ inherit (lib)+ mkOption+ optionalAttrs+ types+ ;+ inherit (import ../../lib.nix { inherit lib; })+ dockerComposeRef+ ;+in+{+ options = {+ networks = mkOption {+ type = types.lazyAttrsOf (types.submoduleWith {+ modules = [+ ../networks/network.nix+ ];+ });+ description = ''+ ${dockerComposeRef "networks-top-level-element"}+ '';+ };+ enableDefaultNetwork = mkOption {+ type = types.bool;+ description = ''+ Whether to define the default network:++ ```nix+ networks.default = {+ name = config.project.name;+ };+ ```+ '';+ default = true;+ };+ };+++ config = {++ networks = optionalAttrs config.enableDefaultNetwork {+ default = {+ name = config.project.name;+ };+ };++ docker-compose.raw.networks =+ lib.mapAttrs (k: v: v.out) config.networks;++ };+}
+ src/nix/modules/networks/network.nix view
@@ -0,0 +1,131 @@+{ config, lib, options, ... }:++let+ inherit (lib)+ mkOption+ optionalAttrs+ types+ ;+ inherit (import ../../lib.nix { inherit lib; })+ dockerComposeRef+ ;+in+{+ options = {+ driver = mkOption {+ description = ''+ `"none"`, `"host"`, or a platform-specific value.+ ${dockerComposeRef "driver"}+ '';+ type = types.str;+ };++ driver_opts = mkOption {+ description = ''+ ${dockerComposeRef "driver_opts"}+ '';+ type = types.lazyAttrsOf types.raw or types.unspecified;+ };++ attachable = mkOption {+ description = ''+ ${dockerComposeRef "attachable"}+ '';+ type = types.bool;+ example = true;+ };++ enable_ipv6 = mkOption {+ description = ''+ Whether we've entered the 21st century yet.+ + ${dockerComposeRef "enable_ipv6"}+ '';+ type = types.bool;+ };++ ipam = mkOption {+ # TODO model sub-options+ description = ''+ Manage IP addresses.++ ${dockerComposeRef "ipam"}+ '';+ type = types.raw or types.unspecified;+ };++ internal = mkOption {+ description = ''+ Achieves "external isolation".++ ${dockerComposeRef "internal"}+ '';+ defaultText = false;+ type = types.bool;+ };++ labels = mkOption {+ description = ''+ Metadata.++ ${dockerComposeRef "labels"}+ '';+ # no list support, because less expressive wrt overriding+ type = types.attrsOf types.str;+ };++ external = mkOption {+ description = ''+ When `true`, don't create or destroy the network, but assume that it+ exists.++ ${dockerComposeRef "external"}+ '';+ type = types.bool;+ };++ name = mkOption {+ description = ''+ Set a custom name for the network.++ It shares a namespace with other projects' networks. `name` is used as-is.++ Note the `default` network's default `name` is set to `project.name` by Arion.++ ${dockerComposeRef "name"}+ '';+ type = types.str;+ };++ out = mkOption {+ internal = true;+ description = ''+ This network's contribution to the docker compose yaml file+ under the `networks.''${name}` key.+ '';+ 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)+ driver+ driver_opts+ attachable+ enable_ipv6+ ipam+ internal+ labels+ external+ name+ ;+ }+ );+ };+}
src/nix/modules/service/docker-compose-service.nix view
@@ -4,16 +4,17 @@ the user-facing options service.image, service.volumes, etc. */-{ pkgs, lib, config, ... }:+{ pkgs, lib, config, options, ... }: let inherit (lib) mkOption types;- inherit (types) listOf nullOr attrsOf str either int bool;+ inherit (types) listOf nullOr attrsOf str either int bool submodule enum; - link = url: text:- ''link:${url}[${text}]'';- dockerComposeRef = fragment:- ''See ${link "https://docs.docker.com/compose/compose-file/#${fragment}" "Docker Compose#${fragment}"}'';+ inherit (import ../../lib.nix { inherit lib; })+ link+ dockerComposeRef+ ;+ dockerComposeKitchenSink = '' Analogous to the `docker run` counterpart. @@ -100,10 +101,62 @@ default = null; description = dockerComposeRef "container_name"; };- service.depends_on = mkOption {- type = listOf str;- default = [];- description = dockerComposeRef "depends_on";+ service.depends_on =+ let conditionsModule = {+ options = {+ condition = mkOption {+ type = enum ["service_started" "service_healthy" "service_completed_successfully"];+ description = dockerComposeRef "depends_on";+ default = "service_started";+ };+ };+ };+ in mkOption {+ type = either (listOf str) (attrsOf (submodule conditionsModule));+ default = [];+ description = dockerComposeRef "depends_on";+ };+ service.healthcheck = mkOption {+ description = dockerComposeRef "healthcheck";+ type = submodule ({ config, options, ...}: {+ options = {+ _out = mkOption {+ internal = true;+ default = lib.optionalAttrs (options.test.highestPrio < 1500) {+ inherit (config) test interval timeout start_period retries;+ };+ };+ test = mkOption {+ type = nullOr (listOf str);+ default = null;+ example = [ "CMD" "pg_isready" ];+ description = dockerComposeRef "healthcheck";+ };+ interval = mkOption {+ type = str;+ default = "30s";+ example = "1m";+ description = dockerComposeRef "healthcheck";+ };+ timeout = mkOption {+ type = str;+ default = "30s";+ example = "10s";+ description = dockerComposeRef "healthcheck";+ };+ start_period = mkOption {+ type = str;+ default = "0s";+ example = "30s";+ description = dockerComposeRef "healthcheck";+ };+ retries = mkOption {+ type = int;+ default = 3;+ description = dockerComposeRef "healthcheck";+ };+ };+ }); }; service.devices = mkOption { type = listOf str;@@ -250,6 +303,8 @@ inherit (config.service) container_name; } // lib.optionalAttrs (config.service.depends_on != []) { inherit (config.service) depends_on;+ } // lib.optionalAttrs (options.service.healthcheck.highestPrio < 1500) {+ healthcheck = config.service.healthcheck._out; } // lib.optionalAttrs (config.service.devices != []) { inherit (config.service) devices; } // lib.optionalAttrs (config.service.entrypoint != null) {
src/nix/modules/service/image.nix view
@@ -104,8 +104,8 @@ }; image.name = mkOption { type = str;- default = config.service.name;- defaultText = lib.literalExample "config.service.name";+ default = "localhost/" + config.service.name;+ defaultText = lib.literalExpression or lib.literalExample ''"localhost/" + config.service.name''; description = '' A human readable name for the docker image.