hzk 1.0.1 → 2.0.0
raw patch · 4 files changed
+128/−190 lines, 4 files
Files
- hzk.cabal +1/−1
- src/Database/Zookeeper.hs +50/−49
- src/Database/Zookeeper/CApi.hsc +28/−67
- src/test-zookeeper.hs +49/−73
hzk.cabal view
@@ -1,7 +1,7 @@ name: hzk author: DiegoSouza <dsouza@c0d3.xxx> license: BSD3-version: 1.0.1+version: 2.0.0 category: Database homepage: http://github.com/dgvncsz0f/hzk synopsis: Haskell client library for Apache Zookeeper
src/Database/Zookeeper.hs view
@@ -1,4 +1,5 @@-{-# LANGUAGE Safe #-}+{-# LANGUAGE Safe #-}+{-# LANGUAGE TupleSections #-} -- This file is part of zhk --@@ -174,7 +175,7 @@ setDebugLevel :: ZLogLevel -> IO () setDebugLevel = c_zooSetDebugLevel . fromLogLevel --- | Creates a znode (asynchornous)+-- | Creates a znode create :: Zookeeper -- ^ Zookeeper handle -> String@@ -186,17 +187,18 @@ -- ^ The initial ACL of the node. The ACL must not be empty -> [CreateFlag] -- ^ Optional, may be empty- -> (Either ZKError String -> IO ())- -- ^ The callback function- -> IO ()-create (Zookeeper zh) path mvalue acls flags callback =+ -> IO (Either ZKError String)+create (Zookeeper zh) path mvalue acls flags = withCString path $ \pathPtr -> maybeUseAsCStringLen mvalue $ \(valuePtr, valueLen) ->- withAclList acls $ \aclPtr -> do- cStrFn <- wrapStringCompletion callback- rc <- c_zooACreate zh pathPtr valuePtr (fromIntegral valueLen) aclPtr (fromCreateFlags flags) cStrFn nullPtr- unless (isZOK rc) (callback $ Left (toZKError rc))+ withAclList acls $ \aclPtr ->+ allocaBytes pathlen $ \newPathPtr -> do+ rc <- c_zooCreate zh pathPtr valuePtr (fromIntegral valueLen) aclPtr (fromCreateFlags flags) newPathPtr (fromIntegral pathlen)+ if (isZOK rc)+ then fmap Right (peekCString newPathPtr)+ else return (Left $ toZKError rc) where+ pathlen = length path + 11 maybeUseAsCStringLen Nothing f = f (nullPtr, -1) maybeUseAsCStringLen (Just s) f = B.useAsCStringLen s f @@ -232,7 +234,7 @@ cWatcher <- wrapWatcher mwatcher tryZ (c_zooWExists zh pathPtr cWatcher nullPtr statPtr) (toStat statPtr) --- | Lists the children of a znode (asynchronous)+-- | Lists the children of a znode getChildren :: Zookeeper -- ^ Zookeeper handle -> String@@ -241,15 +243,15 @@ -> Maybe Watcher -- ^ The watch to be set at the server to notify the user -- if the node changes- -> (Either ZKError [String] -> IO ())- -- ^ The callback function- -> IO ()-getChildren (Zookeeper zh) path mwatcher callback =- withCString path (\pathPtr -> do- cWatcher <- wrapWatcher mwatcher- cStrFn <- wrapStringsCompletion callback- rc <- c_zooAWGetChildren zh pathPtr cWatcher nullPtr cStrFn nullPtr- unless (isZOK rc) (callback $ Left (toZKError rc)))+ -> IO (Either ZKError [String])+getChildren (Zookeeper zh) path mwatcher =+ withCString path $ \pathPtr ->+ allocaStrVec $ \strVecPtr -> do+ cWatcher <- wrapWatcher mwatcher+ rc <- c_zooWGetChildren zh pathPtr cWatcher nullPtr strVecPtr+ if (isZOK rc)+ then fmap Right $ toStringList strVecPtr+ else return (Left $ toZKError rc) -- | Gets the data associated with a znode (asynchronous) get :: Zookeeper@@ -260,15 +262,22 @@ -> Maybe Watcher -- ^ When provided, a watch will be set at the server to notify -- the client if the node changes- -> (Either ZKError (Maybe B.ByteString, Stat) -> IO ())- -- ^ The callback function- -> IO ()-get (Zookeeper zh) path mwatcher callback =- withCString path $ \pathPtr -> do- cWatcher <- wrapWatcher mwatcher- cDataFn <- wrapDataCompletion callback- rc <- c_zooAWGet zh pathPtr cWatcher nullPtr cDataFn nullPtr- unless (isZOK rc) (callback $ Left (toZKError rc))+ -> IO (Either ZKError (Maybe B.ByteString, Stat))+get (Zookeeper zh) path mwatcher =+ withCString path $ \pathPtr ->+ allocaBytes buffSize $ \dataPtr ->+ allocaStat $ \statPtr ->+ alloca $ \buffSizePtr -> do+ poke buffSizePtr (fromIntegral buffSize)+ cWatcher <- wrapWatcher mwatcher+ rc <- c_zooWGet zh pathPtr cWatcher nullPtr dataPtr buffSizePtr statPtr+ dataSz <- peek buffSizePtr+ case (isZOK rc, fromIntegral dataSz) of+ (True, -1) -> return . Right =<< fmap (Nothing ,) (toStat statPtr)+ (True, sz) -> return . Right =<< liftM2 (,) (fmap Just $ B.packCStringLen (dataPtr, sz)) (toStat statPtr)+ (False, _) -> return (Left $ toZKError rc)+ where+ buffSize = 1024 * 1024 -- | Sets the data associated with a znode (synchronous) set :: Zookeeper@@ -325,14 +334,15 @@ -> String -- ^ The name of the znode expressed as a file name with slashes -- separating ancestors of the znode- -> (Either ZKError (AclList, Stat) -> IO ())- -- ^ The callback function- -> IO ()-getAcl (Zookeeper zh) path callback =- withCString path $ \pathPtr -> do- cAclFn <- wrapAclCompletion callback- rc <- c_zooAGetAcl zh pathPtr cAclFn nullPtr- unless (isZOK rc) (callback $ Left (toZKError rc))+ -> IO (Either ZKError (AclList, Stat))+getAcl (Zookeeper zh) path =+ withCString path $ \pathPtr ->+ allocaAclVec $ \aclVecPtr ->+ allocaStat $ \statPtr -> do+ rc <- c_zooGetAcl zh pathPtr aclVecPtr statPtr+ if (isZOK rc)+ then fmap Right $ liftM2 (,) (toAclList aclVecPtr) (toStat statPtr)+ else return (Left $ toZKError rc) -- | Specify application credentials (asynchronous) --@@ -369,16 +379,7 @@ -- $description ----- This library provides haskell bindings for zookeeper c-library. The--- underlying library exposes two classes of functions: synchronous--- and asynchronous. Whenever possible the synchronous functions are--- used.------ The reason we do not always use the synchronous version is that it--- requires the caller to allocate memory and currently it is--- impossible to know (at least I could not figure it) how much memory--- should be allocated. The asynchronous version has no such problem--- as it manages memory internally.+-- This library provides haskell bindings for zookeeper c-library (mt). -- $example --@@ -397,8 +398,8 @@ -- > takeMVar mvar >>= print -- > where -- > watcher mvar zh _ ConnectedState _ =--- > create zh "/foobar" Nothing OpenAclUnsafe [] $ \_ ->--- > getChildren zh "/" Nothing (putMVar mvar)+-- > void $ create zh "/foobar" Nothing OpenAclUnsafe []+-- > getChildren zh "/" Nothing >>= putMVar mvar -- $notes -- * Watcher callbacks must never block;
src/Database/Zookeeper/CApi.hsc view
@@ -34,20 +34,20 @@ ( -- * C to Haskell toStat , toState+ , toAclList , toZKError , allocaStat , toClientId+ , toStringList+ , allocaAclVec+ , allocaStrVec -- * Haskell to C , withAclList , wrapWatcher , fromLogLevel , fromCreateFlag , fromCreateFlags- , wrapAclCompletion- , wrapDataCompletion , wrapVoidCompletion- , wrapStringCompletion- , wrapStringsCompletion -- * Error handling , tryZ , isZOK@@ -55,31 +55,30 @@ , whenZOK -- * C functions , c_zooSet2- , c_zooAWGet+ , c_zooWGet , c_zooState , c_zooDelete , c_zooSetAcl- , c_zooAGetAcl- , c_zooACreate+ , c_zooCreate+ , c_zooGetAcl , c_zooAddAuth , c_zooWExists , c_zooClientId , c_zookeeperInit- , c_zookeeperClose , c_zooSetWatcher+ , c_zookeeperClose , c_zooRecvTimeout , c_isUnrecoverable- , c_zooAWGetChildren+ , c_zooWGetChildren , c_zooSetDebugLevel ) where #include <zookeeper.h> -import Foreign.C-import Foreign.Safe-import qualified Data.ByteString as B-import Control.Applicative-import Database.Zookeeper.Types+import Foreign.C+import Foreign.Safe+import Control.Applicative+import Database.Zookeeper.Types tryZ :: IO CInt -> IO a -> IO (Either ZKError a) tryZ zkIO nextIO = do@@ -183,12 +182,18 @@ <*> (fmap toPerms ((#peek struct ACL, perms) ptr)) buildList (acl : acc) (n-1) (ptr `plusPtr` (#size struct ACL)) +allocaStrVec :: (Ptr CStrVec -> IO a) -> IO a+allocaStrVec = allocaBytes (#size struct String_vector)++allocaAclVec :: (Ptr CAclVec -> IO a) -> IO a+allocaAclVec = allocaBytes (#size struct ACL_vector)+ withAclList :: AclList -> (Ptr CAclVec -> IO a) -> IO a withAclList CreatorAll cont = cont c_zooCreatorAclAll withAclList OpenAclUnsafe cont = cont c_zooOpenAclUnsafe withAclList ReadAclUnsafe cont = cont c_zooReadAclUnsafe withAclList (List acls) cont =- allocaBytes (#size struct ACL_vector) $ \aclvPtr -> do+ allocaAclVec $ \aclvPtr -> do (#poke struct ACL_vector, count) aclvPtr count allocaBytes (count * (#size struct ACL)) $ \aclPtr -> do (#poke struct ACL_vector, data) aclvPtr aclPtr@@ -258,34 +263,6 @@ else fmap Just (peekCString cpath) fn (Zookeeper zh) event state path -wrapAclCompletion :: AclCompletion -> IO (FunPtr CAclCompletionFn)-wrapAclCompletion fn =- c_aclCompletionFn $ \rc aclPtr statPtr _ ->- fn =<< (onZOK rc $ do- aclList <- toAclList aclPtr- stat <- toStat statPtr- return (aclList, stat))--wrapDataCompletion :: DataCompletion -> IO (FunPtr CDataCompletionFn)-wrapDataCompletion fn =- c_dataCompletionFn $ \rc valPtr valLen statPtr _ ->- fn =<< (onZOK rc $ do- stat <- toStat statPtr- if (valLen == -1)- then return (Nothing, stat)- else fmap (\s -> (Just s, stat)) (B.packCStringLen (valPtr, fromIntegral valLen)))--wrapStringCompletion :: StringCompletion -> IO (FunPtr CStringCompletionFn)-wrapStringCompletion fn =- c_stringCompletionFn $ \rc strPtr _ ->- fn =<< (onZOK rc $ do- peekCString strPtr)--wrapStringsCompletion :: StringsCompletion -> IO (FunPtr CStringsCompletionFn)-wrapStringsCompletion fn =- c_stringsCompletionFn $ \rc strvPtr _ ->- fn =<< (onZOK rc (toStringList strvPtr))- wrapVoidCompletion :: VoidCompletion -> IO (FunPtr CVoidCompletionFn) wrapVoidCompletion fn = c_voidCompletionFn $ \rc _ -> (fn =<< onZOK rc (return ()))@@ -295,22 +272,6 @@ -> IO (FunPtr CWatcherFn) foreign import ccall safe "wrapper"- c_dataCompletionFn :: CDataCompletionFn- -> IO (FunPtr CDataCompletionFn)--foreign import ccall safe "wrapper"- c_stringsCompletionFn :: CStringsCompletionFn- -> IO (FunPtr CStringsCompletionFn)--foreign import ccall safe "wrapper"- c_stringCompletionFn :: CStringCompletionFn- -> IO (FunPtr CStringCompletionFn)--foreign import ccall safe "wrapper"- c_aclCompletionFn :: CAclCompletionFn- -> IO (FunPtr CAclCompletionFn)--foreign import ccall safe "wrapper" c_voidCompletionFn :: CVoidCompletionFn -> IO (FunPtr CVoidCompletionFn) @@ -329,8 +290,8 @@ foreign import ccall safe "zookeeper.h zoo_set_watcher" c_zooSetWatcher :: Ptr CZHandle -> FunPtr CWatcherFn -> IO () -foreign import ccall safe "zookeeper.h zoo_acreate"- c_zooACreate :: Ptr CZHandle -> CString -> CString -> CInt -> Ptr CAclVec -> CInt -> FunPtr CStringCompletionFn -> Ptr () -> IO CInt+foreign import ccall safe "zookeeper.h zoo_create"+ c_zooCreate :: Ptr CZHandle -> CString -> CString -> CInt -> Ptr CAclVec -> CInt -> CString -> CInt -> IO CInt foreign import ccall safe "zookeeper.h zoo_delete" c_zooDelete :: Ptr CZHandle -> CString -> CInt -> IO CInt@@ -356,20 +317,20 @@ foreign import ccall safe "zookeeper.h zoo_set_debug_level" c_zooSetDebugLevel :: CInt -> IO () -foreign import ccall safe "zookeeper.h zoo_aget_acl"- c_zooAGetAcl :: Ptr CZHandle -> CString -> FunPtr CAclCompletionFn -> Ptr () -> IO CInt+foreign import ccall safe "zookeeper.h zoo_get_acl"+ c_zooGetAcl :: Ptr CZHandle -> CString -> Ptr CAclVec -> Ptr CStat -> IO CInt foreign import ccall safe "zookeeper.h zoo_set_acl" c_zooSetAcl :: Ptr CZHandle -> CString -> CInt -> Ptr CAclVec -> IO CInt -foreign import ccall safe "zookeeper.h zoo_awget"- c_zooAWGet :: Ptr CZHandle -> CString -> FunPtr CWatcherFn -> Ptr () -> FunPtr CDataCompletionFn -> Ptr () -> IO CInt+foreign import ccall safe "zookeeper.h zoo_wget"+ c_zooWGet :: Ptr CZHandle -> CString -> FunPtr CWatcherFn -> Ptr () -> CString -> Ptr CInt -> Ptr CStat -> IO CInt foreign import ccall safe "zookeeper.h zoo_set2" c_zooSet2 :: Ptr CZHandle -> CString -> CString -> CInt -> CInt -> Ptr CStat -> IO CInt -foreign import ccall safe "zookeeper.h zoo_awget_children"- c_zooAWGetChildren :: Ptr CZHandle -> CString -> FunPtr CWatcherFn -> Ptr () -> FunPtr CStringsCompletionFn -> Ptr () -> IO CInt+foreign import ccall safe "zookeeper.h zoo_wget_children"+ c_zooWGetChildren :: Ptr CZHandle -> CString -> FunPtr CWatcherFn -> Ptr () -> Ptr CStrVec -> IO CInt foreign import ccall safe "zookeeper.h &ZOO_CREATOR_ALL_ACL" c_zooCreatorAclAll :: Ptr CAclVec
src/test-zookeeper.hs view
@@ -62,35 +62,30 @@ testExists zh = testGroup "exists" [ testCase "exists after create" $ do let path = chroot "/testExists#1"- mvar <- newEmptyMVar- create zh path Nothing OpenAclUnsafe [] (\_ ->- exists zh path Nothing >>= putMVar mvar . either (const False) (const True))- takeMVar mvar @? "== Right _"+ create zh path Nothing OpenAclUnsafe []+ exists zh path Nothing >>= (@? "== Right _") . (either (const False) (const True)) , testCase "exists without znode" $ do let path = chroot "/testExists#2"- mvar <- newEmptyMVar exists zh path Nothing >>= (@?= Left NoNodeError) , testCase "exists(watcher) and create" $ do let path = chroot "/testExists#3" mvar <- newEmptyMVar exists zh path (Just $ watcher mvar) >>= (@?= Left NoNodeError)- create zh path Nothing OpenAclUnsafe [] (const $ return ())+ create zh path Nothing OpenAclUnsafe [] takeMVar mvar >>= (@?= (CreatedEvent, Just path)) , testCase "exists(watcher) and set" $ do let path = chroot "/testExists#4" mvar <- newEmptyMVar- create zh path Nothing OpenAclUnsafe [] (\_ -> do- exists zh path (Just $ watcher mvar)- set zh path Nothing Nothing- return ())+ create zh path Nothing OpenAclUnsafe []+ exists zh path (Just $ watcher mvar)+ set zh path Nothing Nothing takeMVar mvar >>= (@?= (ChangedEvent, Just path)) , testCase "exists(watcher) and delete" $ do let path = chroot "/testExists#5" mvar <- newEmptyMVar- create zh path Nothing OpenAclUnsafe [] (\_ -> do- exists zh path (Just $ watcher mvar)- delete zh path Nothing- return ())+ create zh path Nothing OpenAclUnsafe []+ exists zh path (Just $ watcher mvar)+ delete zh path Nothing takeMVar mvar >>= (@?= (DeletedEvent, Just path)) ] where@@ -99,32 +94,28 @@ testGet zh = testGroup "get" [ testCase "get without znode" $ do let path = chroot "/testGet#1"- mvar <- newEmptyMVar- get zh path Nothing (putMVar mvar)- takeMVar mvar >>= (@?= Left NoNodeError)+ get zh path Nothing >>= (@?= Left NoNodeError) , testCase "create(nodata) and get" $ do let path = chroot "/testGet#2"- mvar <- newEmptyMVar- create zh path Nothing OpenAclUnsafe [] (\_ ->- get zh path Nothing (putMVar mvar . either (const False) (isNothing . fst)))- takeMVar mvar @? "== Right (Nothing, _)"+ create zh path Nothing OpenAclUnsafe []+ get zh path Nothing >>= (@? "== Right (Nothing, _)") . (either (const False) (isNothing . fst)) , testCase "create(data) and get" $ do let path = chroot "/testGet#3"- mvar <- newEmptyMVar- create zh path (Just "foobar") OpenAclUnsafe [] (\_ ->- get zh path Nothing (putMVar mvar . either (const False) ((== Just "foobar") . fst)))- takeMVar mvar @? "== Right (Just \"foobar\", _)"+ create zh path (Just "foobar") OpenAclUnsafe []+ get zh path Nothing >>= (@? "== Right (Just \"foobar\", _)") . (either (const False) ((== Just "foobar") . fst)) , testCase "get(watcher) and set" $ do let path = chroot "/testGet#4" mvar <- newEmptyMVar- create zh path Nothing OpenAclUnsafe [] (\_ ->- get zh path (Just $ watcher mvar) (\_ -> set zh path Nothing Nothing >> return ()))+ create zh path Nothing OpenAclUnsafe []+ get zh path (Just $ watcher mvar)+ set zh path Nothing Nothing takeMVar mvar >>= (@?= (ChangedEvent, Just path)) , testCase "get(watcher) and delete" $ do let path = chroot "/testGet#5" mvar <- newEmptyMVar- create zh path Nothing OpenAclUnsafe [] (\_ ->- get zh path (Just $ watcher mvar) (\_ -> delete zh path Nothing >> return ()))+ create zh path Nothing OpenAclUnsafe []+ get zh path (Just $ watcher mvar)+ delete zh path Nothing takeMVar mvar >>= (@?= (DeletedEvent, Just path)) ] where@@ -133,48 +124,39 @@ testOwnsEphemeral zh = testGroup "ownsEphemeral" [ testCase "get ephemeral" $ do let path = chroot "/testGet#6"- mvar <- newEmptyMVar- create zh path Nothing OpenAclUnsafe [Ephemeral] (\_ ->- get zh path Nothing (\(Right (_, stat)) -> do- myId <- getClientId zh- putMVar mvar =<< ownsEphemeral myId stat))- takeMVar mvar @? "owns ephemeral"+ create zh path Nothing OpenAclUnsafe [Ephemeral]+ (Right (_, stat)) <- get zh path Nothing+ myId <- getClientId zh+ ownsEphemeral myId stat @? "owns ephemeral" ] testGetChildren zh = testGroup "getChildren" [ testCase "getChildren without znode" $ do let path = chroot "/testGetChildren#1"- mvar <- newEmptyMVar- getChildren zh path Nothing (putMVar mvar)- takeMVar mvar >>= (@?= Left NoNodeError)+ getChildren zh path Nothing >>= (@?= Left NoNodeError) , testCase "getChildren after create" $ do let path = chroot "/testGetChildren#2"- mvar <- newEmptyMVar- create zh path Nothing OpenAclUnsafe [] (\_ ->- getChildren zh path Nothing (putMVar mvar))- takeMVar mvar >>= (@?= Right [])+ create zh path Nothing OpenAclUnsafe []+ getChildren zh path Nothing >>= (@?= Right []) , testCase "getChildren with one child" $ do let path = chroot "/testGetChildren#3"- mvar <- newEmptyMVar- create zh path Nothing OpenAclUnsafe [] (\_ ->- create zh (path ++ "/1") Nothing OpenAclUnsafe [] (\_ ->- getChildren zh path Nothing (putMVar mvar)))- takeMVar mvar >>= (@?= Right ["1"])+ create zh path Nothing OpenAclUnsafe []+ create zh (path ++ "/1") Nothing OpenAclUnsafe []+ getChildren zh path Nothing >>= (@?= Right ["1"]) , testCase "getChildren(watcher) and create child" $ do let path = chroot "/testGetChildren#4" mvar <- newEmptyMVar- create zh path Nothing OpenAclUnsafe [] (\_ ->- getChildren zh path (Just $ watcher mvar) (\_ ->- create zh (path ++ "/1") Nothing OpenAclUnsafe [] (const $ return ())))+ create zh path Nothing OpenAclUnsafe []+ getChildren zh path (Just $ watcher mvar)+ create zh (path ++ "/1") Nothing OpenAclUnsafe [] takeMVar mvar >>= (@?= (ChildEvent, Just path)) , testCase "getChildren(watcher) and delete child" $ do let path = chroot "/testGetChildren#5" mvar <- newEmptyMVar- create zh path Nothing OpenAclUnsafe [] (\_ ->- create zh (path ++ "/1") Nothing OpenAclUnsafe [] (\_ ->- getChildren zh path (Just $ watcher mvar) (\_ -> do- delete zh (path ++ "/1") Nothing- return ())))+ create zh path Nothing OpenAclUnsafe []+ create zh (path ++ "/1") Nothing OpenAclUnsafe []+ getChildren zh path (Just $ watcher mvar)+ delete zh (path ++ "/1") Nothing takeMVar mvar >>= (@?= (ChildEvent, Just path)) ] where@@ -183,10 +165,8 @@ testGetAcl zh = testGroup "getAcl" [ testCase "getAcl" $ do let path = chroot "/testGetAcl#1"- mvar <- newEmptyMVar- create zh path Nothing OpenAclUnsafe [] (\_ ->- getAcl zh path (putMVar mvar . either (const 0) (countAcls . fst)))- takeMVar mvar >>= (@?= 1)+ create zh path Nothing OpenAclUnsafe []+ getAcl zh path >>= (@?= 1) . (either (const 0) (countAcls . fst)) , testCase "getAcl flags" $ do let path = chroot "/testGetAcl#2" flags = [ []@@ -197,12 +177,10 @@ , [CanRead, CanAdmin, CanWrite, CanCreate, CanDelete] ] forM_ flags $ \flag -> do- mvar <- newEmptyMVar- create zh path Nothing (List [Acl "world" "anyone" flag]) [] (\_ -> do- getAcl zh path (\rc -> do- delete zh path Nothing- putMVar mvar (either (const []) (getFlags . fst) rc)))- takeMVar mvar >>= (@?= flag)+ create zh path Nothing (List [Acl "world" "anyone" flag]) []+ rc <- getAcl zh path+ delete zh path Nothing+ (either (const []) (getFlags . fst) rc) @?= flag ] where countAcls (List xs) = length xs@@ -214,25 +192,23 @@ rmrf :: Zookeeper -> String -> IO () rmrf zh path = do let childPath name = path ++ "/" ++ name- mvar <- newEmptyMVar- getChildren zh path Nothing (putMVar mvar)- takeMVar mvar >>= either (const $ return ()) (mapM_ (rmrf zh . childPath))- delete zh path Nothing- return ()+ children <- getChildren zh path Nothing+ case children of+ Right xs -> mapM_ (rmrf zh . childPath) xs+ Left _ -> return ()+ void $ delete zh path Nothing main :: IO () main = do disclaimer endpoint <- getEndpoint waitState <- newEmptyMVar- waitCreate <- newEmptyMVar withZookeeper endpoint 5000 (Just $ watcher waitState) Nothing $ \zh -> do state <- takeMVar waitState case state of ConnectedState -> do rmrf zh (chroot "")- create zh (chroot "") Nothing OpenAclUnsafe [] (putMVar waitCreate)- takeMVar waitCreate+ create zh (chroot "") Nothing OpenAclUnsafe [] defaultMain $ testGroup "Zookeeper" [ testGet zh , testExists zh , testGetAcl zh