WIP podman
This commit is contained in:
parent
01f359b8f6
commit
3dcadd5e40
8 changed files with 67 additions and 26 deletions
|
@ -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;
|
||||||
|
|
|
@ -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
|
||||||
];
|
];
|
||||||
|
|
|
@ -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 ()
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 ""
|
||||||
|
|
|
@ -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
|
||||||
]
|
]
|
|
@ -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 {
|
||||||
|
|
Loading…
Reference in a new issue