From 26a254dd86c0fec34a63c3cd39a520c784c6d961 Mon Sep 17 00:00:00 2001 From: Sam Tay Date: Tue, 13 Dec 2016 00:40:02 -0500 Subject: [PATCH] Implement COP rendering, WIP --- TODO.md | 3 ++- dockmaster.cabal | 1 + src/Dockmaster.hs | 7 ++---- src/Dockmaster/Compose.hs | 47 +++++++++++++++++++++++++++++++++++++++ src/Dockmaster/Types.hs | 11 +++++---- 5 files changed, 59 insertions(+), 10 deletions(-) create mode 100644 src/Dockmaster/Compose.hs diff --git a/TODO.md b/TODO.md index f5bffd4..2aa358f 100644 --- a/TODO.md +++ b/TODO.md @@ -1,9 +1,10 @@ # todo ## mvp -1. Use COP to generate docker-compose.yml and pipe to `docker-compose -f -` +1. Fixup haddock docs and add to /docs directory for github.io. Use `stack build --haddock` to check coverage and get output 2. Parse `DOCKMASTER_HOME` from config.yml PATHS into `get_env "DOCKMASTER_HOME" ~> "~/.dockmaster/home" or whatever ## later +0. --local flag to execute on local machine instead of docker-machine targets 1. Use monad transformer to stack Sh on top of Either (error transformer) 2. Read up on exception handling link in haskell-fun-times resources diff --git a/dockmaster.cabal b/dockmaster.cabal index fe9ca25..17711b5 100644 --- a/dockmaster.cabal +++ b/dockmaster.cabal @@ -24,6 +24,7 @@ library , Dockmaster.Config.Parser other-modules: Dockmaster.Types , Dockmaster.Config.Types + , Dockmaster.Compose , Dockmaster.Utils build-depends: base >= 4.7 && < 5 , bytestring diff --git a/src/Dockmaster.hs b/src/Dockmaster.hs index 0c9d16e..1924adf 100644 --- a/src/Dockmaster.hs +++ b/src/Dockmaster.hs @@ -13,6 +13,7 @@ import Data.Maybe -- Local modules import Dockmaster.Parser import Dockmaster.Config.Parser +import Dockmaster.Compose -- External modules import Shelly @@ -40,11 +41,7 @@ dm path command args = do Right dmYml -> do let targets = map targetName $ dmTargets dmYml -- just grabbing machine name (flip mapM_) targets $ \m -> dockermachine m $ do - hookWrap' dmYml command $ dockercompose $ command : args - --- | Run docker-compose command -dockercompose :: [T.Text] -> Sh () -dockercompose = (print_stdout True) . (run_ "docker-compose") + hookWrap' dmYml command $ dockercompose dmYml $ command : args -- | Takes machine name and Sh action, and wraps Sh action in scope of -- docker-machine env diff --git a/src/Dockmaster/Compose.hs b/src/Dockmaster/Compose.hs new file mode 100644 index 0000000..68e3d8c --- /dev/null +++ b/src/Dockmaster/Compose.hs @@ -0,0 +1,47 @@ +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE ExtendedDefaultRules #-} +{-# OPTIONS_GHC -fno-warn-type-defaults #-} +module Dockmaster.Compose + ( -- * Orchestration for docker-compose.yml + dockercompose + ) where + +-- Local modules +import Dockmaster.Types +import Dockmaster.Utils (parsePath, toText) + +-- External modules +import Control.Monad ((<=<)) +import Shelly +import Prelude hiding (FilePath) +import qualified Data.Text as T +default (T.Text) + +-- | Runs docker-compose but uses compiled docker-compose.yml file +-- if template/vars are specified in dockmaster.yml +dockercompose :: Dockmaster -> [T.Text] -> Sh () +dockercompose cfg optargs = print_stdout True $ maybe + (run_ "docker-compose" optargs) -- default run on docker-compose.yml, as usual + (composeViaTemplate optargs) + (dmFile cfg) + +-- | Uses 'ComposeFile' argument to build templated docker-compose.yml content +-- and pipes it directly to docker-compose +-- +-- TODO make sure the pipe doesnt output garbage +-- If we need shell-level pipe this is possible: +-- https://hackage.haskell.org/package/shelly-1.6.8.1/docs/Shelly.html#v:-45--124--45- +composeViaTemplate :: [T.Text] -> ComposeFile -> Sh () +composeViaTemplate optargs cf = + compileTemplate cf -|- run_ "docker-compose" (optargs ++ ["-f", "-"]) + +-- | Leverages @cop@ to build docker-compose.yml content from template/vars +-- +-- This function returns the stdout wrapped in 'Sh'. It can then be piped to +-- docker-compose via @docker-compose -f -@ -- see documentation for docker-compose. +compileTemplate :: ComposeFile -> Sh T.Text +compileTemplate cf = do + file <- parse (cfPath cf) + vars <- mapM parse (cfVars cf) + run "cop" $ ["--render-template", file] ++ vars + where parse = toText <=< parsePath <=< toText diff --git a/src/Dockmaster/Types.hs b/src/Dockmaster/Types.hs index 7694be7..3cfa86b 100644 --- a/src/Dockmaster/Types.hs +++ b/src/Dockmaster/Types.hs @@ -1,11 +1,14 @@ {-# LANGUAGE OverloadedStrings #-} module Dockmaster.Types where +import Dockmaster.Config.Types + import Data.Yaml import Control.Applicative import Data.HashMap.Lazy (HashMap, lookup, member) import Data.Monoid (mempty) -import Prelude hiding (lookup) +import Shelly +import Prelude hiding (lookup, FilePath) import qualified Data.Text as T -- | Dockmaster configuration (specified by dockmaster.yml) @@ -15,8 +18,8 @@ data Dockmaster = Dockmaster { dmFile :: Maybe ComposeFile } deriving (Show) -- | Configuration for docker-compose.yml file template & vars -data ComposeFile = ComposeFile { cfPath :: Maybe T.Text - , cfConfig :: [T.Text] +data ComposeFile = ComposeFile { cfPath :: FilePath + , cfVars :: [FilePath] } deriving (Show) -- | Targets are used to identify where compositions are run @@ -36,7 +39,7 @@ data CommandConfig = CommandConfig { ccCompose :: Bool instance FromJSON ComposeFile where parseJSON (Object v) = ComposeFile - <$> v .:? "path" + <$> v .: "path" <*> v .:? "config" .!= [] instance FromJSON Target where