WIP podman

This commit is contained in:
Robert Hensing 2021-05-18 18:46:42 +02:00
parent 01f359b8f6
commit 3dcadd5e40
8 changed files with 67 additions and 26 deletions

View file

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

View file

@ -48,6 +48,8 @@ in
haskellPkgs.ghcid haskellPkgs.ghcid
haskellPkgs.haskell-language-server haskellPkgs.haskell-language-server
super.docker-compose super.docker-compose
super.podman
super.podman-compose
self.niv self.niv
self.releaser self.releaser
]; ];

View file

@ -10,7 +10,7 @@ import Arion.Aeson
import Arion.Images (loadImages) import Arion.Images (loadImages)
import qualified Arion.DockerCompose as DockerCompose import qualified Arion.DockerCompose as DockerCompose
import Arion.Services (getDefaultExec) import Arion.Services (getDefaultExec)
import Arion.ExtendedInfo (loadExtendedInfoFromPath, ExtendedInfo(images, projectName)) import Arion.ExtendedInfo (loadExtendedInfoFromPath, ExtendedInfo(images, projectName), technology, Technology(..))
import Options.Applicative import Options.Applicative
import Control.Monad.Fail import Control.Monad.Fail
@ -147,6 +147,7 @@ runDC cmd (DockerComposeArgs args) _opts = do
DockerCompose.run DockerCompose.Args DockerCompose.run DockerCompose.Args
{ files = [] { files = []
, otherArgs = [cmd] ++ args , otherArgs = [cmd] ++ args
, isPodman = True -- FIXME
} }
runBuildAndDC :: Text -> DockerComposeArgs -> CommonOptions -> IO () runBuildAndDC :: Text -> DockerComposeArgs -> CommonOptions -> IO ()
@ -160,11 +161,13 @@ runEvalAndDC cmd dopts opts = do
callDC :: Text -> DockerComposeArgs -> CommonOptions -> Bool -> FilePath -> IO () callDC :: Text -> DockerComposeArgs -> CommonOptions -> Bool -> FilePath -> IO ()
callDC cmd dopts opts shouldLoadImages path = do callDC cmd dopts opts shouldLoadImages path = do
extendedInfo <- loadExtendedInfoFromPath path 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 let firstOpts = projectArgs extendedInfo <> commonArgs opts
DockerCompose.run DockerCompose.Args DockerCompose.run DockerCompose.Args
{ files = [path] { files = [path]
, otherArgs = firstOpts ++ [cmd] ++ unDockerComposeArgs dopts , otherArgs = firstOpts ++ [cmd] ++ unDockerComposeArgs dopts
, isPodman = is_podman
} }
projectArgs :: ExtendedInfo -> [Text] projectArgs :: ExtendedInfo -> [Text]
@ -299,6 +302,7 @@ runExec detach privileged user noTTY index envs workDir service commandAndArgs o
[] -> ["/bin/sh"] [] -> ["/bin/sh"]
x -> x x -> x
let is_podman = technology extendedInfo == Podman
let args = concat let args = concat
[ ["exec"] [ ["exec"]
, ("--detach" <$ guard detach :: [Text]) , ("--detach" <$ guard detach :: [Text])
@ -314,6 +318,7 @@ runExec detach privileged user noTTY index envs workDir service commandAndArgs o
DockerCompose.run DockerCompose.Args DockerCompose.run DockerCompose.Args
{ files = [path] { files = [path]
, otherArgs = projectArgs extendedInfo <> commonArgs opts <> args , otherArgs = projectArgs extendedInfo <> commonArgs opts <> args
, isPodman = is_podman
} }
main :: IO () main :: IO ()

View file

@ -8,6 +8,7 @@ import System.Process
data Args = Args data Args = Args
{ files :: [FilePath] { files :: [FilePath]
, otherArgs :: [Text] , otherArgs :: [Text]
, isPodman :: Bool
} }
run :: Args -> IO () run :: Args -> IO ()
@ -15,9 +16,9 @@ run args = do
let fileArgs = files args >>= \f -> ["--file", f] let fileArgs = files args >>= \f -> ["--file", f]
allArgs = fileArgs ++ map toS (otherArgs args) allArgs = fileArgs ++ map toS (otherArgs args)
procSpec = proc "docker-compose" allArgs exeName = if isPodman args then "podman-compose" else "docker-compose"
procSpec =
-- hPutStrLn stderr ("Running docker-compose with " <> show allArgs :: Text) proc exeName allArgs
withCreateProcess procSpec $ \_in _out _err procHandle -> do withCreateProcess procSpec $ \_in _out _err procHandle -> do
@ -27,4 +28,4 @@ run args = do
ExitSuccess -> pass ExitSuccess -> pass
ExitFailure 1 -> exitFailure ExitFailure 1 -> exitFailure
ExitFailure {} -> do 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 , imageTag :: Text
} deriving (Eq, Show, Generic, Aeson.ToJSON, Aeson.FromJSON) } deriving (Eq, Show, Generic, Aeson.ToJSON, Aeson.FromJSON)
data Technology = Docker | Podman
deriving (Eq, Show)
data ExtendedInfo = ExtendedInfo { data ExtendedInfo = ExtendedInfo {
projectName :: Maybe Text, projectName :: Maybe Text,
images :: [Image] images :: [Image],
technology :: Technology
} deriving (Eq, Show) } deriving (Eq, Show)
loadExtendedInfoFromPath :: FilePath -> IO ExtendedInfo loadExtendedInfoFromPath :: FilePath -> IO ExtendedInfo
@ -33,5 +37,10 @@ loadExtendedInfoFromPath fp = do
pure ExtendedInfo { pure ExtendedInfo {
-- TODO: use aeson derived instance? -- TODO: use aeson derived instance?
projectName = v ^? key "x-arion" . key "project" . key "name" . _String, 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 type TaggedImage = Text
-- | Subject to change -- | Subject to change
loadImages :: [Image] -> IO () loadImages :: Bool -> [Image] -> IO ()
loadImages requestedImages = do loadImages isPodman requestedImages = do
loaded <- getDockerImages loaded <- getDockerImages isPodman
let let
isNew i = isNew i =
@ -28,23 +28,28 @@ loadImages requestedImages = do
-- -- On podman, you automatically get a localhost prefix -- -- On podman, you automatically get a localhost prefix
&& ("localhost/" <> imageName i <> ":" <> imageTag i) `notElem` loaded && ("localhost/" <> imageName i <> ":" <> imageTag i) `notElem` loaded
traverse_ loadImage . filter isNew $ requestedImages traverse_ (loadImage isPodman) . filter isNew $ requestedImages
loadImage :: Image -> IO () exeName :: IsString p => Bool -> p
loadImage Image { image = Just imgPath, imageName = name } = 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 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 Process.std_in = Process.UseHandle fileHandle
} }
print procSpec
Process.withCreateProcess procSpec $ \_in _out _err procHandle -> do Process.withCreateProcess procSpec $ \_in _out _err procHandle -> do
e <- Process.waitForProcess procHandle e <- Process.waitForProcess procHandle
case e of case e of
ExitSuccess -> pass ExitSuccess -> pass
ExitFailure code -> 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 loadImage isPodman Image { imageExe = Just imgExe, imageName = name } = do
let loadSpec = (Process.proc "docker" [ "load" ]) { Process.std_in = Process.CreatePipe } let loadSpec = (Process.proc (exeName isPodman) [ "load" ]) { Process.std_in = Process.CreatePipe }
Process.withCreateProcess loadSpec $ \(Just inHandle) _out _err loadProcHandle -> do Process.withCreateProcess loadSpec $ \(Just inHandle) _out _err loadProcHandle -> do
let streamSpec = Process.proc (toS imgExe) [] let streamSpec = Process.proc (toS imgExe) []
Process.withCreateProcess streamSpec { Process.std_out = Process.UseHandle inHandle } $ \_ _ _ streamProcHandle -> Process.withCreateProcess streamSpec { Process.std_out = Process.UseHandle inHandle } $ \_ _ _ streamProcHandle ->
@ -57,15 +62,15 @@ loadImage Image { imageExe = Just imgExe, imageName = name } = do
Left _ -> pass Left _ -> pass
loadExit <- wait loadExitAsync loadExit <- wait loadExitAsync
case loadExit of 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
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" panic $ "image " <> name <> " doesn't specify an image file or imageExe executable"
getDockerImages :: IO [TaggedImage] getDockerImages :: Bool -> IO [TaggedImage]
getDockerImages = do getDockerImages isPodman = do
let procSpec = Process.proc "docker" [ "images", "--filter", "dangling=false", "--format", "{{.Repository}}:{{.Tag}}" ] let procSpec = Process.proc (exeName isPodman) [ "images", "--filter", "dangling=false", "--format", "{{.Repository}}:{{.Tag}}" ]
map toS . T.lines . toS <$> Process.readCreateProcess procSpec "" map toS . T.lines . toS <$> Process.readCreateProcess procSpec ""

View file

@ -4,4 +4,7 @@
./modules/composition/images.nix ./modules/composition/images.nix
./modules/composition/service-info.nix ./modules/composition/service-info.nix
./modules/composition/composition.nix ./modules/composition/composition.nix
] ./modules/composition/deployment.nix
./modules/composition/deployment/docker.nix
./modules/composition/deployment/podman.nix
]

View file

@ -29,6 +29,20 @@ let
config.service.name = name; 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 in
{ {
imports = [ imports = [
@ -52,11 +66,11 @@ in
readOnly = true; readOnly = true;
}; };
docker-compose.raw = lib.mkOption { 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."; description = "Attribute set that will be turned into the docker-compose.yaml file, using Nix's toJSON builtin.";
}; };
docker-compose.extended = lib.mkOption { 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."; description = "Attribute set that will be turned into the x-arion section of the docker-compose.yaml file.";
}; };
services = lib.mkOption { services = lib.mkOption {