From 3dcadd5e4037161f74d1472de3b4ed6640ecc447 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Tue, 18 May 2021 18:46:42 +0200 Subject: [PATCH 1/2] WIP podman --- examples/full-nixos/arion-compose.nix | 2 ++ nix/overlay.nix | 2 ++ src/haskell/exe/Main.hs | 9 +++-- src/haskell/lib/Arion/DockerCompose.hs | 9 ++--- src/haskell/lib/Arion/ExtendedInfo.hs | 13 +++++-- src/haskell/lib/Arion/Images.hs | 35 +++++++++++-------- src/nix/modules.nix | 5 ++- .../modules/composition/docker-compose.nix | 18 ++++++++-- 8 files changed, 67 insertions(+), 26 deletions(-) diff --git a/examples/full-nixos/arion-compose.nix b/examples/full-nixos/arion-compose.nix index 1524e3a..47c2c28 100644 --- a/examples/full-nixos/arion-compose.nix +++ b/examples/full-nixos/arion-compose.nix @@ -1,4 +1,6 @@ { + deployment.technology = "podman"; + services.webserver = { pkgs, lib, ... }: { nixos.useSystemd = true; nixos.configuration.boot.tmpOnTmpfs = true; diff --git a/nix/overlay.nix b/nix/overlay.nix index bbacd30..8067c56 100644 --- a/nix/overlay.nix +++ b/nix/overlay.nix @@ -48,6 +48,8 @@ in haskellPkgs.ghcid haskellPkgs.haskell-language-server super.docker-compose + super.podman + super.podman-compose self.niv self.releaser ]; diff --git a/src/haskell/exe/Main.hs b/src/haskell/exe/Main.hs index eeceda1..f6ffc7c 100644 --- a/src/haskell/exe/Main.hs +++ b/src/haskell/exe/Main.hs @@ -10,7 +10,7 @@ import Arion.Aeson import Arion.Images (loadImages) import qualified Arion.DockerCompose as DockerCompose import Arion.Services (getDefaultExec) -import Arion.ExtendedInfo (loadExtendedInfoFromPath, ExtendedInfo(images, projectName)) +import Arion.ExtendedInfo (loadExtendedInfoFromPath, ExtendedInfo(images, projectName), technology, Technology(..)) import Options.Applicative import Control.Monad.Fail @@ -147,6 +147,7 @@ runDC cmd (DockerComposeArgs args) _opts = do DockerCompose.run DockerCompose.Args { files = [] , otherArgs = [cmd] ++ args + , isPodman = True -- FIXME } runBuildAndDC :: Text -> DockerComposeArgs -> CommonOptions -> IO () @@ -160,11 +161,13 @@ runEvalAndDC cmd dopts opts = do callDC :: Text -> DockerComposeArgs -> CommonOptions -> Bool -> FilePath -> IO () callDC cmd dopts opts shouldLoadImages path = do extendedInfo <- loadExtendedInfoFromPath path - when shouldLoadImages $ loadImages (images extendedInfo) + let is_podman = technology extendedInfo == Podman + when shouldLoadImages $ loadImages is_podman (images extendedInfo) let firstOpts = projectArgs extendedInfo <> commonArgs opts DockerCompose.run DockerCompose.Args { files = [path] , otherArgs = firstOpts ++ [cmd] ++ unDockerComposeArgs dopts + , isPodman = is_podman } projectArgs :: ExtendedInfo -> [Text] @@ -299,6 +302,7 @@ runExec detach privileged user noTTY index envs workDir service commandAndArgs o [] -> ["/bin/sh"] x -> x + let is_podman = technology extendedInfo == Podman let args = concat [ ["exec"] , ("--detach" <$ guard detach :: [Text]) @@ -314,6 +318,7 @@ runExec detach privileged user noTTY index envs workDir service commandAndArgs o DockerCompose.run DockerCompose.Args { files = [path] , otherArgs = projectArgs extendedInfo <> commonArgs opts <> args + , isPodman = is_podman } main :: IO () diff --git a/src/haskell/lib/Arion/DockerCompose.hs b/src/haskell/lib/Arion/DockerCompose.hs index f44d86f..365ae2c 100644 --- a/src/haskell/lib/Arion/DockerCompose.hs +++ b/src/haskell/lib/Arion/DockerCompose.hs @@ -8,6 +8,7 @@ import System.Process data Args = Args { files :: [FilePath] , otherArgs :: [Text] + , isPodman :: Bool } run :: Args -> IO () @@ -15,9 +16,9 @@ run args = do let fileArgs = files args >>= \f -> ["--file", f] allArgs = fileArgs ++ map toS (otherArgs args) - procSpec = proc "docker-compose" allArgs - - -- hPutStrLn stderr ("Running docker-compose with " <> show allArgs :: Text) + exeName = if isPodman args then "podman-compose" else "docker-compose" + procSpec = + proc exeName allArgs withCreateProcess procSpec $ \_in _out _err procHandle -> do @@ -27,4 +28,4 @@ run args = do ExitSuccess -> pass ExitFailure 1 -> exitFailure ExitFailure {} -> do - throwIO $ FatalError $ "docker-compose failed with " <> show exitCode + throwIO $ FatalError $ toS exeName <> " failed with status " <> show exitCode diff --git a/src/haskell/lib/Arion/ExtendedInfo.hs b/src/haskell/lib/Arion/ExtendedInfo.hs index 92a7668..577b5ef 100644 --- a/src/haskell/lib/Arion/ExtendedInfo.hs +++ b/src/haskell/lib/Arion/ExtendedInfo.hs @@ -22,9 +22,13 @@ data Image = Image , imageTag :: Text } deriving (Eq, Show, Generic, Aeson.ToJSON, Aeson.FromJSON) +data Technology = Docker | Podman + deriving (Eq, Show) + data ExtendedInfo = ExtendedInfo { projectName :: Maybe Text, - images :: [Image] + images :: [Image], + technology :: Technology } deriving (Eq, Show) loadExtendedInfoFromPath :: FilePath -> IO ExtendedInfo @@ -33,5 +37,10 @@ loadExtendedInfoFromPath fp = do pure ExtendedInfo { -- TODO: use aeson derived instance? projectName = v ^? key "x-arion" . key "project" . key "name" . _String, - images = (v :: Aeson.Value) ^.. key "x-arion" . key "images" . _Array . traverse . _JSON + images = (v :: Aeson.Value) ^.. key "x-arion" . key "images" . _Array . traverse . _JSON, + technology = + case v ^? key "x-arion" . key "technology" . _String of + Just "podman" -> Podman + Just "docker" -> Docker + _ -> panic "Unknown x-arion.technology" -- Shouldn't happen } diff --git a/src/haskell/lib/Arion/Images.hs b/src/haskell/lib/Arion/Images.hs index da2ad3c..7d43afa 100644 --- a/src/haskell/lib/Arion/Images.hs +++ b/src/haskell/lib/Arion/Images.hs @@ -16,10 +16,10 @@ import Arion.ExtendedInfo (Image(..)) type TaggedImage = Text -- | Subject to change -loadImages :: [Image] -> IO () -loadImages requestedImages = do +loadImages :: Bool -> [Image] -> IO () +loadImages isPodman requestedImages = do - loaded <- getDockerImages + loaded <- getDockerImages isPodman let isNew i = @@ -28,23 +28,28 @@ loadImages requestedImages = do -- -- On podman, you automatically get a localhost prefix && ("localhost/" <> imageName i <> ":" <> imageTag i) `notElem` loaded - traverse_ loadImage . filter isNew $ requestedImages + traverse_ (loadImage isPodman) . filter isNew $ requestedImages -loadImage :: Image -> IO () -loadImage Image { image = Just imgPath, imageName = name } = +exeName :: IsString p => Bool -> p +exeName _isPodman@True = "podman" +exeName _isPodman@False = "docker" + +loadImage :: Bool -> Image -> IO () +loadImage isPodman Image { image = Just imgPath, imageName = name } = withFile (toS imgPath) ReadMode $ \fileHandle -> do - let procSpec = (Process.proc "docker" [ "load" ]) { + let procSpec = (Process.proc (exeName isPodman) [ "load" ]) { Process.std_in = Process.UseHandle fileHandle } + print procSpec Process.withCreateProcess procSpec $ \_in _out _err procHandle -> do 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 + panic $ exeName isPodman <> " load failed with exit code " <> show code <> " for image " <> name <> " from path " <> imgPath -loadImage Image { imageExe = Just imgExe, imageName = name } = do - let loadSpec = (Process.proc "docker" [ "load" ]) { Process.std_in = Process.CreatePipe } +loadImage isPodman Image { imageExe = Just imgExe, imageName = name } = do + let loadSpec = (Process.proc (exeName isPodman) [ "load" ]) { Process.std_in = Process.CreatePipe } Process.withCreateProcess loadSpec $ \(Just inHandle) _out _err loadProcHandle -> do let streamSpec = Process.proc (toS imgExe) [] Process.withCreateProcess streamSpec { Process.std_out = Process.UseHandle inHandle } $ \_ _ _ streamProcHandle -> @@ -57,15 +62,15 @@ loadImage Image { imageExe = Just imgExe, imageName = name } = do Left _ -> pass loadExit <- wait loadExitAsync case loadExit of - ExitFailure code -> panic $ "docker load failed with exit code " <> show code <> " for image " <> name <> " produced by executable " <> imgExe + ExitFailure code -> panic $ exeName isPodman <> " load failed with exit code " <> show code <> " for image " <> name <> " produced by executable " <> imgExe _ -> pass pass -loadImage Image { imageName = name } = do +loadImage _isPodman 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}}" ] +getDockerImages :: Bool -> IO [TaggedImage] +getDockerImages isPodman = do + let procSpec = Process.proc (exeName isPodman) [ "images", "--filter", "dangling=false", "--format", "{{.Repository}}:{{.Tag}}" ] map toS . T.lines . toS <$> Process.readCreateProcess procSpec "" diff --git a/src/nix/modules.nix b/src/nix/modules.nix index ecc73c1..4a8ef27 100644 --- a/src/nix/modules.nix +++ b/src/nix/modules.nix @@ -4,4 +4,7 @@ ./modules/composition/images.nix ./modules/composition/service-info.nix ./modules/composition/composition.nix -] \ No newline at end of file + ./modules/composition/deployment.nix + ./modules/composition/deployment/docker.nix + ./modules/composition/deployment/podman.nix +] diff --git a/src/nix/modules/composition/docker-compose.nix b/src/nix/modules/composition/docker-compose.nix index a74c958..c7652fe 100644 --- a/src/nix/modules/composition/docker-compose.nix +++ b/src/nix/modules/composition/docker-compose.nix @@ -29,6 +29,20 @@ let config.service.name = name; }; + json.type = with lib.types; let + valueType = nullOr (oneOf [ + bool + int + float + str + path # extra + package # extra + (attrsOf valueType) + (listOf valueType) + ]) // { + description = "JSON value"; + }; + in valueType; in { imports = [ @@ -52,11 +66,11 @@ in readOnly = true; }; docker-compose.raw = lib.mkOption { - type = lib.types.attrs; + type = json.type; description = "Attribute set that will be turned into the docker-compose.yaml file, using Nix's toJSON builtin."; }; docker-compose.extended = lib.mkOption { - type = lib.types.attrs; + type = json.type; description = "Attribute set that will be turned into the x-arion section of the docker-compose.yaml file."; }; services = lib.mkOption { From 8d5371229d6e6abbdf5b314ea579128216d0f647 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Tue, 25 May 2021 11:41:03 +0200 Subject: [PATCH 2/2] WIP podman-compose --- flake.lock | 12 +++++++----- flake.nix | 2 ++ nix/overlay.nix | 13 ++++++++++--- nix/upstreamable/default.nix | 2 +- .../testdata/Arion/NixSpec/arion-compose.json | 3 ++- src/nix/modules/composition/deployment.nix | 15 +++++++++++++++ src/nix/modules/composition/deployment/docker.nix | 12 ++++++++++++ src/nix/modules/composition/deployment/podman.nix | 11 +++++++++++ 8 files changed, 60 insertions(+), 10 deletions(-) create mode 100644 src/nix/modules/composition/deployment.nix create mode 100644 src/nix/modules/composition/deployment/docker.nix create mode 100644 src/nix/modules/composition/deployment/podman.nix diff --git a/flake.lock b/flake.lock index 4e92b89..f534b01 100644 --- a/flake.lock +++ b/flake.lock @@ -2,16 +2,18 @@ "nodes": { "nixpkgs": { "locked": { - "lastModified": 1601906239, - "narHash": "sha256-P1jBYbYeFswig/0FKbgh+BpVhh9iurD3m0T2ae4gdx8=", + "lastModified": 1621356929, + "narHash": "sha256-lD43MQ+bDFioz6eTxMmc5/tZ2nGUQ2e26CFRKp8JlF4=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "c2bb4af48d26ed091e5674394bacbf8d488c7939", + "rev": "6be706bbe5d892de78ce2c3b757508701ac592a6", "type": "github" }, "original": { - "id": "nixpkgs", - "type": "indirect" + "owner": "NixOS", + "ref": "master", + "repo": "nixpkgs", + "type": "github" } }, "root": { diff --git a/flake.nix b/flake.nix index d711a13..7a55dac 100644 --- a/flake.nix +++ b/flake.nix @@ -1,6 +1,8 @@ { description = "Arion - use Docker Compose via Nix"; + inputs.nixpkgs.url = "github:NixOS/nixpkgs/master"; + outputs = { self, nixpkgs }: let lib = import (nixpkgs + "/lib"); diff --git a/nix/overlay.nix b/nix/overlay.nix index 8067c56..27f4bff 100644 --- a/nix/overlay.nix +++ b/nix/overlay.nix @@ -47,15 +47,22 @@ in haskellPkgs.cabal-install haskellPkgs.ghcid haskellPkgs.haskell-language-server - super.docker-compose - super.podman - super.podman-compose + self.docker-compose + self.podman + self.podman-compose self.niv self.releaser ]; }; }; + podman-compose = super.podman-compose.overrideAttrs(o: { + src = ~/h/podman-compose; + # patches = (o.patches or []) ++ [ + # ./podman-compose-stop_signal.patch + # ]; + }); + inherit (import (sources.niv) {}) niv; releaser = self.haskellPackages.callCabal2nix "releaser" sources.releaser {}; } diff --git a/nix/upstreamable/default.nix b/nix/upstreamable/default.nix index a06b42d..2a40905 100644 --- a/nix/upstreamable/default.nix +++ b/nix/upstreamable/default.nix @@ -57,7 +57,7 @@ let mv $out/bin/arion $out/libexec makeWrapper $out/libexec/arion $out/bin/arion \ --unset PYTHONPATH \ - --prefix PATH : ${lib.makeBinPath [ pkgs.docker-compose ]} \ + --prefix PATH : ${lib.makeBinPath [ pkgs.docker-compose pkgs.podman pkgs.podman-compose ]} \ ; ''; }; diff --git a/src/haskell/testdata/Arion/NixSpec/arion-compose.json b/src/haskell/testdata/Arion/NixSpec/arion-compose.json index 83d62fc..4d232ca 100644 --- a/src/haskell/testdata/Arion/NixSpec/arion-compose.json +++ b/src/haskell/testdata/Arion/NixSpec/arion-compose.json @@ -46,6 +46,7 @@ "-l" ] } - } + }, + "technology": "docker" } } diff --git a/src/nix/modules/composition/deployment.nix b/src/nix/modules/composition/deployment.nix new file mode 100644 index 0000000..2e484e1 --- /dev/null +++ b/src/nix/modules/composition/deployment.nix @@ -0,0 +1,15 @@ +{ config, lib, ... }: +let + inherit (lib) mkOption types; +in +{ + options = { + deployment.technology = mkOption { + description = "Which container technology to use."; + type = types.enum []; + }; + }; + config = { + docker-compose.raw.x-arion.technology = config.deployment.technology; + }; +} diff --git a/src/nix/modules/composition/deployment/docker.nix b/src/nix/modules/composition/deployment/docker.nix new file mode 100644 index 0000000..78e1ff5 --- /dev/null +++ b/src/nix/modules/composition/deployment/docker.nix @@ -0,0 +1,12 @@ +{ lib, ... }: +let + inherit (lib) mkOption types; +in +{ + options = { + deployment.technology = mkOption { + type = types.enum ["docker"]; + default = "docker"; + }; + }; +} diff --git a/src/nix/modules/composition/deployment/podman.nix b/src/nix/modules/composition/deployment/podman.nix new file mode 100644 index 0000000..4fb77f7 --- /dev/null +++ b/src/nix/modules/composition/deployment/podman.nix @@ -0,0 +1,11 @@ +{ lib, ... }: +let + inherit (lib) mkOption types; +in +{ + options = { + deployment.technology = mkOption { + type = types.enum ["podman"]; + }; + }; +}