-- |
-- Module      : Zuul.ZooKeeper
-- Description : Helpers for ZooKeeper
-- Copyright   : (c) Red Hat, 2022
-- License     : Apache-2.0
--
-- Maintainer  : tdecacqu@redhat.com, fboucher@redhat.com
-- Stability   : provisional
-- Portability : portable
--
-- This module contains the logic to load data from ZooKeeper
module Zuul.ZooKeeper
  ( -- * Data acquision fetcher
    ZKConnection (..),
    fetchConfigs,

    -- * File parser
    walkConfigNodes,
    ZKFile (..),
    ConfigError (..),

    -- * Helper to load the tenants configuration
    readTenantsConfig,
    ZKTenantsConfig (..),

    -- * Test helpers
    mkZKFile,
  )
where

import Data.Aeson (eitherDecodeFileStrict)
import Data.Text qualified as Text
import Data.Yaml qualified
import Network.URI.Encode qualified
import Streaming.Prelude qualified as S
import System.Directory qualified
import System.Process.Typed qualified
import ZuulWeeder.Prelude

-- | A config file read from ZooKeeper.
data ZKFile = ZKFile
  { -- | The provider name.
    ZKFile -> Text
provider :: Text,
    -- | The project name.
    ZKFile -> Text
project :: Text,
    -- | The branch name.
    ZKFile -> Text
branch :: Text,
    -- | The configuration file path, .e.g ".zuul.d/jobs.yaml".
    ZKFile -> FilePathT
filePath :: FilePathT,
    -- | The file path on the host file system, for debugging purpose.
    ZKFile -> FilePathT
fullPath :: FilePathT,
    -- | The JSON Value.
    ZKFile -> Value
zkJSONData :: Value
  }
  deriving (Int -> ZKFile -> ShowS
[ZKFile] -> ShowS
ZKFile -> FilePath
(Int -> ZKFile -> ShowS)
-> (ZKFile -> FilePath) -> ([ZKFile] -> ShowS) -> Show ZKFile
forall a.
(Int -> a -> ShowS) -> (a -> FilePath) -> ([a] -> ShowS) -> Show a
showList :: [ZKFile] -> ShowS
$cshowList :: [ZKFile] -> ShowS
show :: ZKFile -> FilePath
$cshow :: ZKFile -> FilePath
showsPrec :: Int -> ZKFile -> ShowS
$cshowsPrec :: Int -> ZKFile -> ShowS
Show, ZKFile -> ZKFile -> Bool
(ZKFile -> ZKFile -> Bool)
-> (ZKFile -> ZKFile -> Bool) -> Eq ZKFile
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: ZKFile -> ZKFile -> Bool
$c/= :: ZKFile -> ZKFile -> Bool
== :: ZKFile -> ZKFile -> Bool
$c== :: ZKFile -> ZKFile -> Bool
Eq)

-- | The list of config error that can happens when loading the configuration.
data ConfigError
  = -- | System level exception, e.g. ENOENT
    ReadError Text
  | -- | YAML decoding error
    YamlError Text
  | -- | The path is missing component, e.g. branch name
    InvalidPath
  | AmbiguousName Text
  | -- | A decoding error
    DecodeError FilePathT Text Value
  deriving (Int -> ConfigError -> ShowS
[ConfigError] -> ShowS
ConfigError -> FilePath
(Int -> ConfigError -> ShowS)
-> (ConfigError -> FilePath)
-> ([ConfigError] -> ShowS)
-> Show ConfigError
forall a.
(Int -> a -> ShowS) -> (a -> FilePath) -> ([a] -> ShowS) -> Show a
showList :: [ConfigError] -> ShowS
$cshowList :: [ConfigError] -> ShowS
show :: ConfigError -> FilePath
$cshow :: ConfigError -> FilePath
showsPrec :: Int -> ConfigError -> ShowS
$cshowsPrec :: Int -> ConfigError -> ShowS
Show, ConfigError -> ConfigError -> Bool
(ConfigError -> ConfigError -> Bool)
-> (ConfigError -> ConfigError -> Bool) -> Eq ConfigError
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: ConfigError -> ConfigError -> Bool
$c/= :: ConfigError -> ConfigError -> Bool
== :: ConfigError -> ConfigError -> Bool
$c== :: ConfigError -> ConfigError -> Bool
Eq, (forall x. ConfigError -> Rep ConfigError x)
-> (forall x. Rep ConfigError x -> ConfigError)
-> Generic ConfigError
forall x. Rep ConfigError x -> ConfigError
forall x. ConfigError -> Rep ConfigError x
forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
$cto :: forall x. Rep ConfigError x -> ConfigError
$cfrom :: forall x. ConfigError -> Rep ConfigError x
Generic, Value -> Parser [ConfigError]
Value -> Parser ConfigError
(Value -> Parser ConfigError)
-> (Value -> Parser [ConfigError]) -> FromJSON ConfigError
forall a.
(Value -> Parser a) -> (Value -> Parser [a]) -> FromJSON a
parseJSONList :: Value -> Parser [ConfigError]
$cparseJSONList :: Value -> Parser [ConfigError]
parseJSON :: Value -> Parser ConfigError
$cparseJSON :: Value -> Parser ConfigError
FromJSON, [ConfigError] -> Encoding
[ConfigError] -> Value
ConfigError -> Encoding
ConfigError -> Value
(ConfigError -> Value)
-> (ConfigError -> Encoding)
-> ([ConfigError] -> Value)
-> ([ConfigError] -> Encoding)
-> ToJSON ConfigError
forall a.
(a -> Value)
-> (a -> Encoding)
-> ([a] -> Value)
-> ([a] -> Encoding)
-> ToJSON a
toEncodingList :: [ConfigError] -> Encoding
$ctoEncodingList :: [ConfigError] -> Encoding
toJSONList :: [ConfigError] -> Value
$ctoJSONList :: [ConfigError] -> Value
toEncoding :: ConfigError -> Encoding
$ctoEncoding :: ConfigError -> Encoding
toJSON :: ConfigError -> Value
$ctoJSON :: ConfigError -> Value
ToJSON)

-- | Read all the configuration found at the given path
walkConfigNodes ::
  -- | The ZooKeeper dump location
  FilePathT ->
  -- | A stream of configuration file
  S.Stream (S.Of (Either ConfigError ZKFile)) IO ()
walkConfigNodes :: FilePathT -> Stream (Of (Either ConfigError ZKFile)) IO ()
walkConfigNodes FilePathT
dumpPath = FilePathT -> Stream (Of (Either ConfigError ZKFile)) IO ()
walkRecursive (FilePathT -> Stream (Of (Either ConfigError ZKFile)) IO ())
-> FilePathT -> Stream (Of (Either ConfigError ZKFile)) IO ()
forall a b. (a -> b) -> a -> b
$ FilePathT
dumpPath FilePathT -> FilePathT -> FilePathT
</> FilePathT
"zuul/config/cache"
  where
    walkRecursive :: FilePathT -> Stream (Of (Either ConfigError ZKFile)) IO ()
walkRecursive FilePathT
curPath = do
      [FilePathT]
tree <- IO [FilePathT]
-> Stream (Of (Either ConfigError ZKFile)) IO [FilePathT]
forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(MonadTrans t, Monad m) =>
m a -> t m a
lift (IO [FilePathT]
 -> Stream (Of (Either ConfigError ZKFile)) IO [FilePathT])
-> IO [FilePathT]
-> Stream (Of (Either ConfigError ZKFile)) IO [FilePathT]
forall a b. (a -> b) -> a -> b
$ FilePathT -> IO [FilePathT]
listDirectory FilePathT
curPath
      [FilePathT]
-> (FilePathT -> Stream (Of (Either ConfigError ZKFile)) IO ())
-> Stream (Of (Either ConfigError ZKFile)) IO ()
forall (t :: * -> *) (m :: * -> *) a b.
(Foldable t, Monad m) =>
t a -> (a -> m b) -> m ()
forM_ [FilePathT]
tree ((FilePathT -> Stream (Of (Either ConfigError ZKFile)) IO ())
 -> Stream (Of (Either ConfigError ZKFile)) IO ())
-> (FilePathT -> Stream (Of (Either ConfigError ZKFile)) IO ())
-> Stream (Of (Either ConfigError ZKFile)) IO ()
forall a b. (a -> b) -> a -> b
$ \FilePathT
path -> do
        let fullPath :: FilePathT
fullPath = FilePathT
curPath FilePathT -> FilePathT -> FilePathT
</> FilePathT
path
        Bool
isDirectory <- IO Bool -> Stream (Of (Either ConfigError ZKFile)) IO Bool
forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(MonadTrans t, Monad m) =>
m a -> t m a
lift (IO Bool -> Stream (Of (Either ConfigError ZKFile)) IO Bool)
-> IO Bool -> Stream (Of (Either ConfigError ZKFile)) IO Bool
forall a b. (a -> b) -> a -> b
$ FilePathT -> IO Bool
doesDirectoryExist FilePathT
fullPath
        if Bool
isDirectory
          then FilePathT -> Stream (Of (Either ConfigError ZKFile)) IO ()
walkRecursive FilePathT
fullPath
          else do
            Bool
-> Stream (Of (Either ConfigError ZKFile)) IO ()
-> Stream (Of (Either ConfigError ZKFile)) IO ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when (Text
"/0000000000/ZKDATA" Text -> Text -> Bool
`Text.isSuffixOf` FilePathT -> Text
forall source target. From source target => source -> target
from FilePathT
fullPath) (Stream (Of (Either ConfigError ZKFile)) IO ()
 -> Stream (Of (Either ConfigError ZKFile)) IO ())
-> Stream (Of (Either ConfigError ZKFile)) IO ()
-> Stream (Of (Either ConfigError ZKFile)) IO ()
forall a b. (a -> b) -> a -> b
$
              IO (Either ConfigError ZKFile)
-> Stream
     (Of (Either ConfigError ZKFile)) IO (Either ConfigError ZKFile)
forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(MonadTrans t, Monad m) =>
m a -> t m a
lift (ExceptT ConfigError IO ZKFile -> IO (Either ConfigError ZKFile)
forall e (m :: * -> *) a. ExceptT e m a -> m (Either e a)
runExceptT (FilePathT -> ExceptT ConfigError IO ZKFile
handleConfig FilePathT
fullPath)) Stream
  (Of (Either ConfigError ZKFile)) IO (Either ConfigError ZKFile)
-> (Either ConfigError ZKFile
    -> Stream (Of (Either ConfigError ZKFile)) IO ())
-> Stream (Of (Either ConfigError ZKFile)) IO ()
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= Either ConfigError ZKFile
-> Stream (Of (Either ConfigError ZKFile)) IO ()
forall (m :: * -> *) a. Monad m => a -> Stream (Of a) m ()
S.yield

    handleConfig :: FilePathT -> ExceptT ConfigError IO ZKFile
    handleConfig :: FilePathT -> ExceptT ConfigError IO ZKFile
handleConfig FilePathT
fullPath = do
      ByteString
zkData <- FilePathT -> ExceptT ConfigError IO ByteString
readZKData FilePathT
fullPath
      Value
zkJSONData <- case ByteString -> Either ParseException Value
forall a. FromJSON a => ByteString -> Either ParseException a
Data.Yaml.decodeEither' ByteString
zkData of
        Left ParseException
err -> ConfigError -> ExceptT ConfigError IO Value
forall e (m :: * -> *) a. MonadError e m => e -> m a
throwError (ConfigError -> ExceptT ConfigError IO Value)
-> ConfigError -> ExceptT ConfigError IO Value
forall a b. (a -> b) -> a -> b
$ Text -> ConfigError
YamlError (FilePath -> Text
forall source target. From source target => source -> target
from (FilePath -> Text) -> FilePath -> Text
forall a b. (a -> b) -> a -> b
$ ParseException -> FilePath
forall a. Show a => a -> FilePath
show ParseException
err)
        Right Value
content -> Value -> ExceptT ConfigError IO Value
forall (f :: * -> *) a. Applicative f => a -> f a
pure Value
content

      case Value -> FilePathT -> Maybe ZKFile
mkZKFile Value
zkJSONData FilePathT
fullPath of
        Just ZKFile
config -> ZKFile -> ExceptT ConfigError IO ZKFile
forall (f :: * -> *) a. Applicative f => a -> f a
pure ZKFile
config
        Maybe ZKFile
Nothing -> ConfigError -> ExceptT ConfigError IO ZKFile
forall e (m :: * -> *) a. MonadError e m => e -> m a
throwError ConfigError
InvalidPath

    readZKData :: FilePathT -> ExceptT ConfigError IO ByteString
    readZKData :: FilePathT -> ExceptT ConfigError IO ByteString
readZKData FilePathT
path = do
      Either SomeException ByteString
zkDataE <- IO (Either SomeException ByteString)
-> ExceptT ConfigError IO (Either SomeException ByteString)
forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(MonadTrans t, Monad m) =>
m a -> t m a
lift (IO (Either SomeException ByteString)
 -> ExceptT ConfigError IO (Either SomeException ByteString))
-> IO (Either SomeException ByteString)
-> ExceptT ConfigError IO (Either SomeException ByteString)
forall a b. (a -> b) -> a -> b
$ IO ByteString -> IO (Either SomeException ByteString)
forall e a. Exception e => IO a -> IO (Either e a)
try (IO ByteString -> IO (Either SomeException ByteString))
-> IO ByteString -> IO (Either SomeException ByteString)
forall a b. (a -> b) -> a -> b
$ FilePathT -> IO ByteString
readFileBS FilePathT
path
      case Either SomeException ByteString
zkDataE of
        Left SomeException
err -> ConfigError -> ExceptT ConfigError IO ByteString
forall e (m :: * -> *) a. MonadError e m => e -> m a
throwError (ConfigError -> ExceptT ConfigError IO ByteString)
-> ConfigError -> ExceptT ConfigError IO ByteString
forall a b. (a -> b) -> a -> b
$ Text -> ConfigError
ReadError (FilePath -> Text
forall source target. From source target => source -> target
from (FilePath -> Text) -> FilePath -> Text
forall a b. (a -> b) -> a -> b
$ SomeException -> FilePath
forall a. Show a => a -> FilePath
show (SomeException
err :: SomeException))
        Right ByteString
content -> ByteString -> ExceptT ConfigError IO ByteString
forall (f :: * -> *) a. Applicative f => a -> f a
pure ByteString
content

-- | The tenant configuration value read from ZooKeeper. To be decoded by 'Zuul.Tenant.decodeTenantsConfig'
newtype ZKTenantsConfig = ZKTenantsConfig Value deriving (Int -> ZKTenantsConfig -> ShowS
[ZKTenantsConfig] -> ShowS
ZKTenantsConfig -> FilePath
(Int -> ZKTenantsConfig -> ShowS)
-> (ZKTenantsConfig -> FilePath)
-> ([ZKTenantsConfig] -> ShowS)
-> Show ZKTenantsConfig
forall a.
(Int -> a -> ShowS) -> (a -> FilePath) -> ([a] -> ShowS) -> Show a
showList :: [ZKTenantsConfig] -> ShowS
$cshowList :: [ZKTenantsConfig] -> ShowS
show :: ZKTenantsConfig -> FilePath
$cshow :: ZKTenantsConfig -> FilePath
showsPrec :: Int -> ZKTenantsConfig -> ShowS
$cshowsPrec :: Int -> ZKTenantsConfig -> ShowS
Show, ZKTenantsConfig -> ZKTenantsConfig -> Bool
(ZKTenantsConfig -> ZKTenantsConfig -> Bool)
-> (ZKTenantsConfig -> ZKTenantsConfig -> Bool)
-> Eq ZKTenantsConfig
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: ZKTenantsConfig -> ZKTenantsConfig -> Bool
$c/= :: ZKTenantsConfig -> ZKTenantsConfig -> Bool
== :: ZKTenantsConfig -> ZKTenantsConfig -> Bool
$c== :: ZKTenantsConfig -> ZKTenantsConfig -> Bool
Eq)

-- | Read the main.yaml from the ZooKeeper dump
readTenantsConfig ::
  -- | The ZooKeeper dump location
  FilePathT ->
  -- | The object to be decoded by "Zuul.Tenant.decodeTenantsConfig"
  ExceptT Text IO ZKTenantsConfig
readTenantsConfig :: FilePathT -> ExceptT Text IO ZKTenantsConfig
readTenantsConfig FilePathT
dumpPath =
  Value -> ZKTenantsConfig
ZKTenantsConfig (Value -> ZKTenantsConfig)
-> ExceptT Text IO Value -> ExceptT Text IO ZKTenantsConfig
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> ExceptT Text IO Value
readC
  where
    readC :: ExceptT Text IO Value
    readC :: ExceptT Text IO Value
readC = do
      Either FilePath Value
ve <- IO (Either FilePath Value)
-> ExceptT Text IO (Either FilePath Value)
forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(MonadTrans t, Monad m) =>
m a -> t m a
lift (IO (Either FilePath Value)
 -> ExceptT Text IO (Either FilePath Value))
-> IO (Either FilePath Value)
-> ExceptT Text IO (Either FilePath Value)
forall a b. (a -> b) -> a -> b
$ FilePath -> IO (Either FilePath Value)
forall a. FromJSON a => FilePath -> IO (Either FilePath a)
eitherDecodeFileStrict (FilePath -> IO (Either FilePath Value))
-> (FilePathT -> FilePath)
-> FilePathT
-> IO (Either FilePath Value)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. FilePathT -> FilePath
getPath (FilePathT -> IO (Either FilePath Value))
-> FilePathT -> IO (Either FilePath Value)
forall a b. (a -> b) -> a -> b
$ FilePathT
dumpPath FilePathT -> FilePathT -> FilePathT
</> FilePathT
"zuul/system/conf/0000000000/ZKDATA"
      case Either FilePath Value
ve of
        Left FilePath
e -> Text -> ExceptT Text IO Value
forall e (m :: * -> *) a. MonadError e m => e -> m a
throwError (FilePath -> Text
Text.pack FilePath
e)
        Right Value
x -> Value -> ExceptT Text IO Value
forall (f :: * -> *) a. Applicative f => a -> f a
pure Value
x

-- | Creates a 'ZKFile' for testing purpose.
mkZKFile :: Value -> FilePathT -> Maybe ZKFile
mkZKFile :: Value -> FilePathT -> Maybe ZKFile
mkZKFile Value
zkJSONData FilePathT
path = do
  [Text
_, Text
_, Text
filePath', Text
_, Text
branch', Text -> Text
Network.URI.Encode.decodeText -> Text
providerPath'] <-
    [Text] -> Maybe [Text]
forall (f :: * -> *) a. Applicative f => a -> f a
pure ([Text] -> Maybe [Text]) -> [Text] -> Maybe [Text]
forall a b. (a -> b) -> a -> b
$
      Int -> [Text] -> [Text]
forall a. Int -> [a] -> [a]
take Int
6 ([Text] -> [Text]) -> [Text] -> [Text]
forall a b. (a -> b) -> a -> b
$ [Text] -> [Text]
forall a. [a] -> [a]
reverse ([Text] -> [Text]) -> [Text] -> [Text]
forall a b. (a -> b) -> a -> b
$ (Char -> Bool) -> Text -> [Text]
Text.split (Char -> Char -> Bool
forall a. Eq a => a -> a -> Bool
== Char
'/') (FilePathT -> Text
forall source target. From source target => source -> target
from FilePathT
path)
  (Text
provider, Text
project) <- case Text -> Text -> (Text, Text)
Text.breakOn Text
"/" Text
providerPath' of
    (Text
_, Text
"") -> Maybe (Text, Text)
forall a. Maybe a
Nothing
    (Text
x, Text
xs) -> (Text, Text) -> Maybe (Text, Text)
forall a. a -> Maybe a
Just (Text
x, Int -> Text -> Text
Text.drop Int
1 Text
xs)

  let branch :: Text
branch = Text -> Text
Network.URI.Encode.decodeText Text
branch'
      filePath :: FilePathT
filePath = Text -> FilePathT
FilePathT (Text -> FilePathT) -> Text -> FilePathT
forall a b. (a -> b) -> a -> b
$ Text -> Text
Network.URI.Encode.decodeText Text
filePath'
      fullPath :: FilePathT
fullPath = FilePathT
path

  ZKFile -> Maybe ZKFile
forall a. a -> Maybe a
Just ZKFile {Text
provider :: Text
$sel:provider:ZKFile :: Text
provider, Text
project :: Text
$sel:project:ZKFile :: Text
project, Text
branch :: Text
$sel:branch:ZKFile :: Text
branch, FilePathT
filePath :: FilePathT
$sel:filePath:ZKFile :: FilePathT
filePath, FilePathT
fullPath :: FilePathT
$sel:fullPath:ZKFile :: FilePathT
fullPath, Value
zkJSONData :: Value
$sel:zkJSONData:ZKFile :: Value
zkJSONData}

dumpScript :: System.Process.Typed.StreamSpec 'System.Process.Typed.STInput ()
dumpScript :: StreamSpec 'STInput ()
dumpScript =
  ByteString -> StreamSpec 'STInput ()
System.Process.Typed.byteStringInput
    [s|
import zlib, sys, os, kazoo.client

def getTree(path):
    try:
        data, _ = client.get(path)
        data = zlib.decompress(data)
    except Exception:
        pass
    os.makedirs(root + path)
    with open(root + path + '/ZKDATA', 'wb') as f:
        f.write(data)
    for child in client.get_children(path):
        getTree(path + '/' + child)

[root, host, key, cert, ca] = sys.argv[1:]
client = kazoo.client.KazooClient(host, use_ssl=True, keyfile=key, certfile=cert, ca=ca)
client.start()
getTree("/zuul/config/cache")
getTree("/zuul/system/conf")
|]

-- | The ZooKeeper connection settings: hostname and tls paths.
newtype ZKConnection = ZKConnection [Text] deriving (ZKConnection -> ZKConnection -> Bool
(ZKConnection -> ZKConnection -> Bool)
-> (ZKConnection -> ZKConnection -> Bool) -> Eq ZKConnection
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: ZKConnection -> ZKConnection -> Bool
$c/= :: ZKConnection -> ZKConnection -> Bool
== :: ZKConnection -> ZKConnection -> Bool
$c== :: ZKConnection -> ZKConnection -> Bool
Eq, Int -> ZKConnection -> ShowS
[ZKConnection] -> ShowS
ZKConnection -> FilePath
(Int -> ZKConnection -> ShowS)
-> (ZKConnection -> FilePath)
-> ([ZKConnection] -> ShowS)
-> Show ZKConnection
forall a.
(Int -> a -> ShowS) -> (a -> FilePath) -> ([a] -> ShowS) -> Show a
showList :: [ZKConnection] -> ShowS
$cshowList :: [ZKConnection] -> ShowS
show :: ZKConnection -> FilePath
$cshow :: ZKConnection -> FilePath
showsPrec :: Int -> ZKConnection -> ShowS
$cshowsPrec :: Int -> ZKConnection -> ShowS
Show)

-- | Dump the configuration found in ZooKeeper
fetchConfigs :: Logger -> FilePathT -> ZKConnection -> ExceptT Text IO ()
fetchConfigs :: Logger -> FilePathT -> ZKConnection -> ExceptT Text IO ()
fetchConfigs Logger
logger FilePathT
dataDir (ZKConnection [Text]
zkConf) = do
  ExitCode
exitCode <- IO ExitCode -> ExceptT Text IO ExitCode
forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(MonadTrans t, Monad m) =>
m a -> t m a
lift (IO ExitCode -> ExceptT Text IO ExitCode)
-> IO ExitCode -> ExceptT Text IO ExitCode
forall a b. (a -> b) -> a -> b
$ do
    IO Bool -> IO () -> IO ()
forall (m :: * -> *). Monad m => m Bool -> m () -> m ()
whenM (FilePathT -> IO Bool
doesDirectoryExist FilePathT
dataDir) (IO () -> IO ()) -> IO () -> IO ()
forall a b. (a -> b) -> a -> b
$ do
      Logger -> ByteString -> IO ()
info Logger
logger (ByteString -> IO ()) -> ByteString -> IO ()
forall a b. (a -> b) -> a -> b
$ ByteString
"Removing " ByteString -> ByteString -> ByteString
forall a. Semigroup a => a -> a -> a
<> FilePath -> ByteString
forall source target. From source target => source -> target
from (FilePathT -> FilePath
getPath FilePathT
dataDir)
      FilePath -> IO ()
System.Directory.removeDirectoryRecursive (FilePath -> IO ()) -> FilePath -> IO ()
forall a b. (a -> b) -> a -> b
$ FilePathT -> FilePath
getPath FilePathT
dataDir
    Logger -> ByteString -> IO ()
info Logger
logger (ByteString -> IO ()) -> ByteString -> IO ()
forall a b. (a -> b) -> a -> b
$ ByteString
"Dumping with " ByteString -> ByteString -> ByteString
forall a. Semigroup a => a -> a -> a
<> FilePath -> ByteString
forall source target. From source target => source -> target
from ([Text] -> FilePath
forall a. Show a => a -> FilePath
show [Text]
zkConf)
    ProcessConfig () () () -> IO ExitCode
forall (m :: * -> *) stdin stdout stderr.
MonadIO m =>
ProcessConfig stdin stdout stderr -> m ExitCode
System.Process.Typed.runProcess ProcessConfig () () ()
process
  case ExitCode
exitCode of
    ExitCode
System.Process.Typed.ExitSuccess -> () -> ExceptT Text IO ()
forall (f :: * -> *) a. Applicative f => a -> f a
pure ()
    System.Process.Typed.ExitFailure Int
_ -> Text -> ExceptT Text IO ()
forall e (m :: * -> *) a. MonadError e m => e -> m a
throwError Text
"fetchConfig failed!"
  where
    args :: [FilePath]
args = FilePath
"-" FilePath -> [FilePath] -> [FilePath]
forall a. a -> [a] -> [a]
: FilePathT -> FilePath
getPath FilePathT
dataDir FilePath -> [FilePath] -> [FilePath]
forall a. a -> [a] -> [a]
: (Text -> FilePath) -> [Text] -> [FilePath]
forall a b. (a -> b) -> [a] -> [b]
map Text -> FilePath
Text.unpack [Text]
zkConf
    process :: ProcessConfig () () ()
process = StreamSpec 'STInput ()
-> ProcessConfig () () () -> ProcessConfig () () ()
forall stdin stdin0 stdout stderr.
StreamSpec 'STInput stdin
-> ProcessConfig stdin0 stdout stderr
-> ProcessConfig stdin stdout stderr
System.Process.Typed.setStdin StreamSpec 'STInput ()
dumpScript (ProcessConfig () () () -> ProcessConfig () () ())
-> ProcessConfig () () () -> ProcessConfig () () ()
forall a b. (a -> b) -> a -> b
$ FilePath -> [FilePath] -> ProcessConfig () () ()
System.Process.Typed.proc FilePath
"python" [FilePath]
args