Compare commits

...
Sign in to create a new pull request.

2 commits

Author SHA1 Message Date
Robert Hensing
8d5371229d WIP podman-compose 2021-06-03 11:57:12 +02:00
Robert Hensing
3dcadd5e40 WIP podman 2021-06-03 11:54:07 +02:00
15 changed files with 125 additions and 34 deletions

View file

@ -1,4 +1,6 @@
{
deployment.technology = "podman";
services.webserver = { pkgs, lib, ... }: {
nixos.useSystemd = true;
nixos.configuration.boot.tmpOnTmpfs = true;

12
flake.lock generated
View file

@ -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": {

View file

@ -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");

View file

@ -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 {};
}

View file

@ -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 ]} \
;
'';
};

View file

@ -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 ()

View file

@ -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

View file

@ -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
}

View file

@ -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 ""

View file

@ -46,6 +46,7 @@
"-l"
]
}
}
},
"technology": "docker"
}
}

View file

@ -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
]

View file

@ -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;
};
}

View file

@ -0,0 +1,12 @@
{ lib, ... }:
let
inherit (lib) mkOption types;
in
{
options = {
deployment.technology = mkOption {
type = types.enum ["docker"];
default = "docker";
};
};
}

View file

@ -0,0 +1,11 @@
{ lib, ... }:
let
inherit (lib) mkOption types;
in
{
options = {
deployment.technology = mkOption {
type = types.enum ["podman"];
};
};
}

View file

@ -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 {