Make arion cat work

This commit is contained in:
Robert Hensing 2019-06-23 21:27:13 +02:00
parent 77eadf4c41
commit 6882a92e56
11 changed files with 143 additions and 32 deletions

View file

@ -3,3 +3,4 @@
## 0.1.0.0 -- YYYY-mm-dd ## 0.1.0.0 -- YYYY-mm-dd
* First version. Released on an unsuspecting world. * First version. Released on an unsuspecting world.
* *BREAKING:* useHostStore now uses a proper empty base image, like `scratch`.

View file

@ -19,6 +19,7 @@ data-files: nix/*.nix
, nix/modules/composition/*.nix , nix/modules/composition/*.nix
, nix/modules/nixos/*.nix , nix/modules/nixos/*.nix
, nix/modules/service/*.nix , nix/modules/service/*.nix
, arion-image/Dockerfile
-- all data is verbatim from some sources -- all data is verbatim from some sources
data-dir: src data-dir: src
@ -26,16 +27,19 @@ data-dir: src
common deps common deps
build-depends: base ^>=4.12.0.0 build-depends: base ^>=4.12.0.0
, aeson , aeson
, async
, bytestring
, process
, process-extras
, text , text
, protolude , protolude
library library
import: deps import: deps
-- exposed-modules: exposed-modules: Arion.Nix
-- other-modules: other-modules: Paths_arion_compose
-- other-extensions: -- other-extensions:
build-depends: process
hs-source-dirs: src/haskell/lib hs-source-dirs: src/haskell/lib
default-language: Haskell2010 default-language: Haskell2010
@ -45,7 +49,8 @@ executable arion
-- other-modules: -- other-modules:
-- other-extensions: -- other-extensions:
build-depends: optparse-applicative build-depends: optparse-applicative
, process , aeson-pretty
, arion-compose
hs-source-dirs: src/haskell/exe hs-source-dirs: src/haskell/exe
default-language: Haskell2010 default-language: Haskell2010

View file

@ -3,6 +3,7 @@
}: }:
let let
# TODO: Replace by a new expression for the new Haskell main
arion = stdenv.mkDerivation { arion = stdenv.mkDerivation {
name = "arion"; name = "arion";
src = ./src; src = ./src;

View file

@ -1,10 +1,22 @@
self: super: self: super:
let let
inherit (self.arion-project) haskellPkgs; inherit (self.arion-project) haskellPkgs;
srcDir = ../src; # TODO gitignoreSource + whitelist nix and arion-image
eval = import (srcDir + "/nix/eval-composition.nix");
build = args@{...}:
let composition = eval args;
in composition.config.build.dockerComposeYaml;
hlib = super.haskell.lib;
in in
{ {
arion-v0 = super.callPackage ../arion.nix {}; arion-v0 = super.callPackage ../arion.nix {};
arion = super.haskell.lib.justStaticExecutables haskellPkgs.arion-compose; arion = hlib.justStaticExecutables (hlib.overrideCabal haskellPkgs.arion-compose (o: {
passthru = o.passthru // {
inherit eval build;
};
}));
tests = super.callPackage ../tests {}; tests = super.callPackage ../tests {};
doc = super.callPackage ../doc {}; doc = super.callPackage ../doc {};

View file

@ -1,3 +1,5 @@
FROM scratch FROM scratch
COPY passwd /etc/passwd # scratch itself can't be run.
ADD tarball.tar.gz /
# This seems like a no-op:
CMD []

View file

@ -1,2 +0,0 @@
root:x:0:0:System administrator:/root:/bin/sh
nobody:x:65534:65534:Unprivileged account (don't use!):/var/empty:/run/current-system/sw/bin/nologin

View file

@ -1 +0,0 @@
/run/system/bin/sh

View file

@ -1 +0,0 @@
/run/system/usr/bin/env

View file

@ -5,15 +5,21 @@
import Protolude hiding (Down) import Protolude hiding (Down)
import Arion.Nix
import Options.Applicative import Options.Applicative
import Control.Applicative import Control.Applicative
import qualified Data.Aeson.Encode.Pretty
import qualified Data.Text as T import qualified Data.Text as T
import qualified Data.Text.IO as T import qualified Data.Text.IO as T
import qualified Data.Text.Lazy as TL
import qualified Data.Text.Lazy.Builder as TB
import qualified Data.List.NonEmpty as NE import qualified Data.List.NonEmpty as NE
import Data.List.NonEmpty (NonEmpty(..)) import Data.List.NonEmpty (NonEmpty(..))
import Control.Arrow ((>>>)) import Control.Arrow ((>>>))
data CommonOptions = data CommonOptions =
@ -46,7 +52,7 @@ parseOptions = do
<> metavar "EXPR" <> metavar "EXPR"
<> showDefault <> showDefault
<> value "./arion-pkgs.nix" <> value "./arion-pkgs.nix"
<> help "Use EXPR to get the Nixpkgs attrset used for bootstrapping \ <> help "Use Nix expression EXPR to get the Nixpkgs attrset used for bootstrapping \
\and evaluating the configuration." ) \and evaluating the configuration." )
pure CommonOptions{..} pure CommonOptions{..}
@ -111,28 +117,12 @@ commandDC run cmdStr help =
(run cmdStr <$> parseDockerComposeArgs) (run cmdStr <$> parseDockerComposeArgs)
(progDesc (T.unpack help) <> fullDesc <> forwardOptions)) (progDesc (T.unpack help) <> fullDesc <> forwardOptions))
--------------------------------------------------------------------------------
modulesNixExpr :: NonEmpty FilePath -> Text
modulesNixExpr =
NE.toList
>>> fmap pathExpr
>>> T.unwords
>>> wrapList
where
pathExpr path | isAbsolute path = "(/. + \"" <> T.pack path <> "\")"
| otherwise = "(./. + \"" <> T.pack path <> "\")"
isAbsolute ('/':_) = True
isAbsolute _ = False
wrapList s = "[ " <> s <> " ]"
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
runDC :: Text -> DockerComposeArgs -> CommonOptions -> IO () runDC :: Text -> DockerComposeArgs -> CommonOptions -> IO ()
runDC cmd (DockerComposeArgs args) opts = runDC cmd (DockerComposeArgs args) opts =
T.putStrLn $ "TODO: docker-compose " <> cmd <> " " <> T.unwords args panic $ "TODO: docker-compose " <> cmd <> " " <> T.unwords args
runBuildAndDC :: Text -> DockerComposeArgs -> CommonOptions -> IO () runBuildAndDC :: Text -> DockerComposeArgs -> CommonOptions -> IO ()
runBuildAndDC cmd dopts opts = do runBuildAndDC cmd dopts opts = do
@ -146,8 +136,13 @@ runEvalAndDC cmd dopts opts = do
runCat :: CommonOptions -> IO () runCat :: CommonOptions -> IO ()
runCat (CommonOptions files pkgs) = do runCat (CommonOptions files pkgs) = do
T.putStrLn "Running cat ... TODO" v <- Arion.Nix.evaluate EvaluationArgs
T.putStrLn (modulesNixExpr files) { evalUid = 0 -- TODO
, evalModules = files
, evalPkgs = pkgs
, evalWorkDir = Nothing
}
T.hPutStrLn stderr (TL.toStrict $ TB.toLazyText $ Data.Aeson.Encode.Pretty.encodePrettyToTextBuilder v)
runRepl :: CommonOptions -> IO () runRepl :: CommonOptions -> IO ()
runRepl opts = runRepl opts =

View file

@ -0,0 +1,91 @@
{-# LANGUAGE OverloadedStrings #-}
module Arion.Nix where
import Prelude ( )
import Protolude
import Data.Aeson
import qualified Data.String
import System.Process
import qualified Data.ByteString as BS
import qualified Data.ByteString.Lazy as BL
import qualified System.Process.ByteString.Lazy
as PBL
import Paths_arion_compose
import Control.Applicative
import qualified Data.Text as T
import qualified Data.Text.IO as T
import qualified Data.List.NonEmpty as NE
import Data.List.NonEmpty ( NonEmpty(..) )
import Control.Arrow ( (>>>) )
data EvaluationArgs = EvaluationArgs
{ evalUid :: Int
, evalModules :: NonEmpty FilePath
, evalPkgs :: Text
, evalWorkDir :: Maybe FilePath
}
evaluate :: EvaluationArgs -> IO Value
evaluate ea = do
evalComposition <- getDataFileName "nix/eval-composition.nix"
let args =
[ evalComposition
, "--eval"
, "--strict"
, "--read-write-mode"
, "--json"
, "--show-trace"
, "--argstr"
, "uid"
, show $ evalUid ea
, "--arg"
, "modules"
, modulesNixExpr $ evalModules ea
, "--arg"
, "pkgs"
, toS $ evalPkgs ea
, "--attr"
, "config.build.dockerComposeYamlAttrs"
]
stdin = mempty
procSpec = (proc "nix-instantiate" args) { cwd = evalWorkDir ea }
-- TODO: lazy IO is tricky. Let's use conduit/pipes instead?
(exitCode, out, err) <- PBL.readCreateProcessWithExitCode procSpec stdin
-- Stream 'err'
errDone <- async (BL.hPutStr stderr err)
-- Force 'out'
v <- Protolude.evaluate (eitherDecode out)
-- Wait for process exit and 'err' printout
wait errDone
case exitCode of
ExitSuccess -> pass
ExitFailure e -> throwIO $ FatalError "Evaluation failed" -- TODO: don't print this exception in main
case v of
Right r -> pure r
Left e -> throwIO $ FatalError "Couldn't parse nix-instantiate output"
modulesNixExpr :: NonEmpty FilePath -> [Char]
modulesNixExpr =
NE.toList >>> fmap pathExpr >>> Data.String.unwords >>> wrapList
where
pathExpr :: FilePath -> [Char]
pathExpr path | isAbsolute path = "(/. + \"/${" <> toNixStringLiteral path <> "}\")"
| otherwise = "(./. + \"/${" <> toNixStringLiteral path <> "}\")"
isAbsolute ('/' : _) = True
isAbsolute _ = False
wrapList s = "[ " <> s <> " ]"
toNixStringLiteral :: [Char] -> [Char]
toNixStringLiteral = show -- FIXME: custom escaping including '$'

View file

@ -19,10 +19,17 @@ in
build.dockerComposeYaml = lib.mkOption { build.dockerComposeYaml = lib.mkOption {
type = lib.types.package; type = lib.types.package;
description = "A derivation that produces a docker-compose.yaml file for this composition."; description = "A derivation that produces a docker-compose.yaml file for this composition.";
readOnly = true;
}; };
build.dockerComposeYamlText = lib.mkOption { build.dockerComposeYamlText = lib.mkOption {
type = lib.types.string; type = lib.types.string;
description = "The text of build.dockerComposeYaml."; description = "The text of build.dockerComposeYaml.";
readOnly = true;
};
build.dockerComposeYamlAttrs = lib.mkOption {
type = lib.types.attrsOf lib.types.unspecified;
description = "The text of build.dockerComposeYaml.";
readOnly = true;
}; };
docker-compose.raw = lib.mkOption { docker-compose.raw = lib.mkOption {
type = lib.types.attrs; type = lib.types.attrs;
@ -45,7 +52,8 @@ in
}; };
config = { config = {
build.dockerComposeYaml = pkgs.writeText "docker-compose.yaml" config.build.dockerComposeYamlText; build.dockerComposeYaml = pkgs.writeText "docker-compose.yaml" config.build.dockerComposeYamlText;
build.dockerComposeYamlText = builtins.toJSON (config.docker-compose.raw); build.dockerComposeYamlText = builtins.toJSON (config.build.dockerComposeYamlAttrs);
build.dockerComposeYamlAttrs = config.docker-compose.raw;
docker-compose.evaluatedServices = lib.mapAttrs evalService config.docker-compose.services; docker-compose.evaluatedServices = lib.mapAttrs evalService config.docker-compose.services;
docker-compose.raw = { docker-compose.raw = {