arion-compose 0.1.2.0 → 0.1.3.0
raw patch · 14 files changed
+193/−70 lines, 14 filesdep ~basePVP ok
version bump matches the API change (PVP)
Dependency ranges changed: base
API changes (from Hackage documentation)
Files
- CHANGELOG.md +28/−0
- arion-compose.cabal +1/−1
- src/haskell/lib/Arion/Images.hs +11/−7
- src/haskell/testdata/Arion/NixSpec/arion-compose.json +5/−6
- src/nix/modules.nix +0/−1
- src/nix/modules/composition/arion-base-image.nix +0/−41
- src/nix/modules/lib/assert.nix +7/−6
- src/nix/modules/service/all-modules.nix +2/−0
- src/nix/modules/service/check-sys_admin.nix +30/−0
- src/nix/modules/service/docker-compose-service.nix +21/−0
- src/nix/modules/service/host-store.nix +1/−3
- src/nix/modules/service/image-recommended.nix +36/−0
- src/nix/modules/service/image.nix +44/−4
- src/nix/modules/service/nixos-init.nix +7/−1
CHANGELOG.md view
@@ -1,5 +1,32 @@ # Revision history for Arion +## 0.1.3.0 -- 2020-05-03++### Changed++* `useHostStore` now uses an image derived from the `image.*` options. You may+ need to enable `enableRecommendedContents` because with this change, files+ like `/bin/sh` aren't added by default anymore.++* Drop obsolete NixOS 19.03, 19.09 and 20.03 from CI.++### Added++* NixOS-based containers can now run on Podman when it is configured to provide a docker socket. See the [installation docs](https://docs.hercules-ci.com/arion/#_nixos).++* Support `service.dns`, for overriding the DNS servers used by containers.++* Support `service.labels`, which is useful for autodiscovery among other things.++* Add a tested example for Traefik with label-based routing.++* Add a `flake.nix` and an experimental flake example++* Add a warning when systemd `DynamicUser` is used but not available to the+ container.++* CI with NixOS 21.05+ ## 0.1.2.0 -- 2020-03-05 * Support use of prebuilt `docker-compose.yaml`.@@ -26,3 +53,4 @@ ## 0.1.0.0 -- 2019-10-04 * First released version. Released on an unsuspecting world.+
arion-compose.cabal view
@@ -1,7 +1,7 @@ cabal-version: 2.4 name: arion-compose-version: 0.1.2.0+version: 0.1.3.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
src/haskell/lib/Arion/Images.hs view
@@ -1,7 +1,7 @@ {-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE DeriveAnyClass #-} {-# LANGUAGE OverloadedStrings #-}-module Arion.Images +module Arion.Images ( loadImages ) where @@ -22,24 +22,28 @@ loaded <- getDockerImages let- isNew i = (imageName i <> ":" <> imageTag i) `notElem` loaded+ isNew i =+ -- On docker, the image name is unmodified+ (imageName i <> ":" <> imageTag i) `notElem` loaded+ -- -- On podman, you automatically get a localhost prefix+ && ("localhost/" <> imageName i <> ":" <> imageTag i) `notElem` loaded traverse_ loadImage . filter isNew $ requestedImages loadImage :: Image -> IO ()-loadImage (Image { image = Just imgPath, imageName = name }) =+loadImage Image { image = Just imgPath, imageName = name } = withFile (toS imgPath) ReadMode $ \fileHandle -> do let procSpec = (Process.proc "docker" [ "load" ]) { Process.std_in = Process.UseHandle fileHandle } Process.withCreateProcess procSpec $ \_in _out _err procHandle -> do- e <- Process.waitForProcess procHandle + e <- Process.waitForProcess procHandle case e of ExitSuccess -> pass ExitFailure code -> panic $ "docker load failed with exit code " <> show code <> " for image " <> name <> " from path " <> imgPath -loadImage (Image { imageExe = Just imgExe, imageName = name }) = do+loadImage Image { imageExe = Just imgExe, imageName = name } = do let loadSpec = (Process.proc "docker" [ "load" ]) { Process.std_in = Process.CreatePipe } Process.withCreateProcess loadSpec $ \(Just inHandle) _out _err loadProcHandle -> do let streamSpec = Process.proc (toS imgExe) []@@ -57,11 +61,11 @@ _ -> pass pass -loadImage (Image { imageName = name }) = do+loadImage Image { imageName = name } = do panic $ "image " <> name <> " doesn't specify an image file or imageExe executable" getDockerImages :: IO [TaggedImage] getDockerImages = do let procSpec = Process.proc "docker" [ "images", "--filter", "dangling=false", "--format", "{{.Repository}}:{{.Tag}}" ]- (map toS . T.lines . toS) <$> Process.readCreateProcess procSpec ""+ map toS . T.lines . toS <$> Process.readCreateProcess procSpec ""
src/haskell/testdata/Arion/NixSpec/arion-compose.json view
@@ -2,14 +2,14 @@ "services": { "webserver": { "command": [- "/nix/store/b9w61w4g8sqgrm3rid6ca22krslqghb3-nixos-system-unnamed-19.03.173100.e726e8291b2/init"+ "/usr/sbin/init" ], "environment": { "NIX_REMOTE": "", "PATH": "/usr/bin:/run/current-system/sw/bin/", "container": "docker" },- "image": "arion-base:<HASH>",+ "image": "webserver:<HASH>", "ports": [ "8000:80" ],@@ -23,8 +23,7 @@ "tty": true, "volumes": [ "/sys/fs/cgroup:/sys/fs/cgroup:ro",- "/nix/store:/nix/store:ro",- "/nix/store/pssdmhzjnhflawv7rwk1yw39350iv40g-container-system-env:/run/system:ro"+ "/nix/store:/nix/store:ro" ] } },@@ -32,8 +31,8 @@ "x-arion": { "images": [ {- "image": "<STOREPATH>",- "imageName": "arion-base",+ "imageExe": "<STOREPATH>",+ "imageName": "webserver", "imageTag": "<HASH>" } ],
src/nix/modules.nix view
@@ -3,6 +3,5 @@ ./modules/composition/host-environment.nix ./modules/composition/images.nix ./modules/composition/service-info.nix- ./modules/composition/arion-base-image.nix ./modules/composition/composition.nix ]
− src/nix/modules/composition/arion-base-image.nix
@@ -1,41 +0,0 @@---# This module is subject to change.-# In particular, arion-base should use a generic non-service image building system--{ config, lib, pkgs, ... }: --let-- tag = lib.head (lib.strings.splitString "-" (baseNameOf builtImage.outPath));- name = "arion-base";-- builtImage = pkgs.dockerTools.buildImage {- inherit name;- contents = pkgs.runCommand "minimal-contents" {} ''- mkdir -p $out/bin $out/usr/bin- ln -s /run/system/bin/sh $out/bin/sh- ln -s /run/system/usr/bin/env $out/usr/bin/env- '';- config = {};- };--in--{-- options = {- arionBaseImage = lib.mkOption {- type = lib.types.str;- description = "Image to use when using useHostStore. Don't use this option yourself. It's going away.";- internal = true;- };- };-- config = {- arionBaseImage = "${name}:${tag}";- build.imagesToLoad = lib.mkIf (lib.any (s: s.service.useHostStore) (lib.attrValues config.services)) [- { image = builtImage; imageName = name; imageTag = tag; }- ]; - };-}
src/nix/modules/lib/assert.nix view
@@ -3,14 +3,15 @@ # 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 "[1;31mwarning: ${msg}[0m";+ inherit (lib)+ concatStringsSep+ filter+ mkOption+ showWarnings+ types+ ; # Handle assertions and warnings- failedAssertions = map (x: x.message) (filter (x: !x.assertion) config.assertions); assertWarn = if failedAssertions != []
src/nix/modules/service/all-modules.nix view
@@ -5,7 +5,9 @@ ./host-store.nix ./context.nix ./image.nix+ ./image-recommended.nix ./nixos.nix ./nixos-init.nix ../lib/assert.nix+ ./check-sys_admin.nix ]
+ src/nix/modules/service/check-sys_admin.nix view
@@ -0,0 +1,30 @@+{ config, lib, name, ... }:+let+ inherit (lib)+ concatStringsSep+ optional+ ;++ dynamicUserServices = lib.attrNames (+ lib.filterAttrs+ (k: v:+ v.enable &&+ v.serviceConfig.DynamicUser or false)+ config.nixos.evaluatedConfig.systemd.services+ );++ +in+{+ config = {+ warnings = + optional (config.nixos.useSystemd && !(config.service.capabilities.SYS_ADMIN or false) && dynamicUserServices != []) (+ ''In service ${name}, the following units require `SYS_ADMIN` capability+ because of DynamicUser.+ ${concatStringsSep "\n" (map (srv: " - services.${name}.nixos.configuration.systemd.services.${srv}") dynamicUserServices)}+ You can avoid DynamicUser or use+ services.${name}.service.capabilities.SYS_ADMIN = true;+ ''+ );+ };+}
src/nix/modules/service/docker-compose-service.nix view
@@ -115,6 +115,23 @@ ${dockerComposeRef "devices"} ''; };+ service.dns = mkOption {+ type = listOf str;+ default = [];+ example = [ "8.8.8.8" "8.8.4.4" ];+ description = dockerComposeRef "dns";+ };+ service.labels = mkOption {+ type = attrsOf str;+ default = {};+ example = {+ "com.example.foo" = "bar";+ "traefik.enable" = "true";+ "traefik.http.routers.my-service.rule" = "Host(`my-service.localhost`)";+ "traefik.http.routers.my-service.entrypoints" = "web";+ };+ description = dockerComposeRef "labels";+ }; service.links = mkOption { type = listOf str; default = [];@@ -247,6 +264,10 @@ inherit (config.service) extra_hosts; } // lib.optionalAttrs (config.service.hostname != null) { inherit (config.service) hostname;+ } // lib.optionalAttrs (config.service.dns != []) {+ inherit (config.service) dns;+ } // lib.optionalAttrs (config.service.labels != {}) {+ inherit (config.service) labels; } // lib.optionalAttrs (config.service.links != []) { inherit (config.service) links; } // lib.optionalAttrs (config.service.ports != []) {
src/nix/modules/service/host-store.nix view
@@ -29,12 +29,10 @@ }; }; config = mkIf config.service.useHostStore {- image.nixBuild = false; # no need to build and load- service.image = config.composition.arionBaseImage;+ image.includeStorePaths = false; service.environment.NIX_REMOTE = lib.optionalString config.service.useHostNixDaemon "daemon"; service.volumes = [ "${config.host.nixStorePrefix}/nix/store:/nix/store${lib.optionalString config.service.hostStoreAsReadOnly ":ro"}"- "${config.host.nixStorePrefix}${pkgs.buildEnv { name = "container-system-env"; paths = [ pkgs.bashInteractive pkgs.coreutils ]; }}:/run/system${lib.optionalString config.service.hostStoreAsReadOnly ":ro"}" ] ++ lib.optional config.service.useHostNixDaemon "/nix/var/nix/daemon-socket:/nix/var/nix/daemon-socket"; service.command = lib.mkDefault (map escape (config.image.rawConfig.Cmd or [])); };
+ src/nix/modules/service/image-recommended.nix view
@@ -0,0 +1,36 @@+{ config, lib, pkgs, ... }:+let+ inherit (lib)+ mkIf+ mkOption+ types+ ;+ inherit (types)+ bool+ ;++ recommendedContents = { runCommand, bash, coreutils }:+ runCommand "recommended-contents" {} ''+ mkdir -p $out/bin $out/usr/bin $out/var/empty+ ln -s ${bash}/bin/sh $out/bin/sh+ ln -s ${coreutils}/bin/env $out/usr/bin/env+ '';+in+{+ options = {+ image.enableRecommendedContents = mkOption {+ type = bool;+ default = false;+ description = ''+ Add the `/bin/sh` and `/usr/bin/env` symlinks and some lightweight+ files.+ '';+ };+ };++ config = {+ image.contents = mkIf config.image.enableRecommendedContents [+ (pkgs.callPackage recommendedContents {})+ ];+ };+}
src/nix/modules/service/image.nix view
@@ -1,6 +1,15 @@ { pkgs, lib, config, options, ... }: let- inherit (lib) types mkOption;+ 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@@ -9,15 +18,37 @@ (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:- if pkgs.dockerTools?streamLayeredImage- then pkgs.dockerTools.streamLayeredImage args // { isExe = true; }- else pkgs.dockerTools.buildLayeredImage args;+ let+ args_base = builtins.intersectAttrs+ {+ name = null; tag = null; contents = null; config = null;+ created = null; extraCommands = null; maxLayers = 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;@@ -87,6 +118,15 @@ default = []; description = '' Top level paths in the container.+ '';+ };+ 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 {
src/nix/modules/service/nixos-init.nix view
@@ -24,7 +24,13 @@ ../nixos/default-shell.nix (pkgs.path + "/nixos/modules/profiles/minimal.nix") ];- image.command = [ "${config.nixos.build.toplevel}/init" ];+ image.command = [ "/usr/sbin/init" ];+ image.contents = [+ (pkgs.runCommand "root-init" {} ''+ mkdir -p $out/usr/sbin+ ln -s ${config.nixos.build.toplevel}/init $out/usr/sbin/init+ '')+ ]; service.environment.container = "docker"; service.environment.PATH = "/usr/bin:/run/current-system/sw/bin/"; service.volumes = [