Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

podman-compose support #118

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions examples/full-nixos/arion-compose.nix
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
{
deployment.technology = "podman";

services.webserver = { pkgs, lib, ... }: {
nixos.useSystemd = true;
nixos.configuration.boot.tmpOnTmpfs = true;
Expand Down
12 changes: 7 additions & 5 deletions flake.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions flake.nix
Original file line number Diff line number Diff line change
@@ -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");
Expand Down
11 changes: 10 additions & 1 deletion nix/overlay.nix
Original file line number Diff line number Diff line change
Expand Up @@ -47,13 +47,22 @@ in
haskellPkgs.cabal-install
haskellPkgs.ghcid
haskellPkgs.haskell-language-server
super.docker-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 {};
}
2 changes: 1 addition & 1 deletion nix/upstreamable/default.nix
Original file line number Diff line number Diff line change
Expand Up @@ -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 ]} \
;
'';
};
Expand Down
9 changes: 7 additions & 2 deletions src/haskell/exe/Main.hs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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 ()
Expand All @@ -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]
Expand Down Expand Up @@ -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])
Expand All @@ -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 ()
Expand Down
9 changes: 5 additions & 4 deletions src/haskell/lib/Arion/DockerCompose.hs
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,17 @@ import System.Process
data Args = Args
{ files :: [FilePath]
, otherArgs :: [Text]
, isPodman :: Bool
}

run :: Args -> IO ()
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

Expand All @@ -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
13 changes: 11 additions & 2 deletions src/haskell/lib/Arion/ExtendedInfo.hs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
}
35 changes: 20 additions & 15 deletions src/haskell/lib/Arion/Images.hs
Original file line number Diff line number Diff line change
Expand Up @@ -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 =
Expand All @@ -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 ->
Expand All @@ -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 ""
3 changes: 2 additions & 1 deletion src/haskell/testdata/Arion/NixSpec/arion-compose.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
"-l"
]
}
}
},
"technology": "docker"
}
}
5 changes: 4 additions & 1 deletion src/nix/modules.nix
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,7 @@
./modules/composition/images.nix
./modules/composition/service-info.nix
./modules/composition/composition.nix
]
./modules/composition/deployment.nix
./modules/composition/deployment/docker.nix
./modules/composition/deployment/podman.nix
]
15 changes: 15 additions & 0 deletions src/nix/modules/composition/deployment.nix
Original file line number Diff line number Diff line change
@@ -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;
};
}
12 changes: 12 additions & 0 deletions src/nix/modules/composition/deployment/docker.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{ lib, ... }:
let
inherit (lib) mkOption types;
in
{
options = {
deployment.technology = mkOption {
type = types.enum ["docker"];
default = "docker";
};
};
}
11 changes: 11 additions & 0 deletions src/nix/modules/composition/deployment/podman.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{ lib, ... }:
let
inherit (lib) mkOption types;
in
{
options = {
deployment.technology = mkOption {
type = types.enum ["podman"];
};
};
}
18 changes: 16 additions & 2 deletions src/nix/modules/composition/docker-compose.nix
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,20 @@ let
config.service.name = name;
};

json.type = with lib.types; let
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we can use (pkgs.formats.json {}).json by now.

valueType = nullOr (oneOf [
bool
int
float
str
path # extra
package # extra
(attrsOf valueType)
(listOf valueType)
]) // {
description = "JSON value";
};
in valueType;
in
{
imports = [
Expand All @@ -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 {
Expand Down