Revision 13f2321c

b/htools/Ganeti/HTools/QC.hs
51 51
  ) where
52 52

  
53 53
import Test.QuickCheck
54
import Test.QuickCheck.Monadic (assert, monadicIO, run)
54 55
import Text.Printf (printf)
55 56
import Data.List (intercalate, nub, isPrefixOf)
56 57
import Data.Maybe
......
60 61
import qualified Text.JSON as J
61 62
import qualified Data.Map
62 63
import qualified Data.IntMap as IntMap
64
import Control.Concurrent (forkIO)
65
import Control.Exception (bracket, catchJust)
66
import System.Directory (getTemporaryDirectory, removeFile)
67
import System.IO (hClose, openTempFile)
68
import System.IO.Error (isEOFErrorType, ioeGetErrorType)
63 69

  
64 70
import qualified Ganeti.Confd as Confd
65 71
import qualified Ganeti.Config as Config
......
287 293
    x <- elements (['a'..'z'] ++ ['0'..'9'] ++ "_-")
288 294
    return (DNSChar x)
289 295

  
296
instance Show DNSChar where
297
  show = show . dnsGetChar
298

  
290 299
-- | Generates a single name component.
291 300
getName :: Gen String
292 301
getName = do
......
1820 1829
prop_Luxi_CallEncoding op =
1821 1830
  (Luxi.validateCall (Luxi.buildCall op) >>= Luxi.decodeCall) ==? Types.Ok op
1822 1831

  
1832
-- | Helper to a get a temporary file name.
1833
getTempFileName :: IO FilePath
1834
getTempFileName = do
1835
  tempdir <- getTemporaryDirectory
1836
  (fpath, handle) <- openTempFile tempdir "luxitest"
1837
  _ <- hClose handle
1838
  removeFile fpath
1839
  return fpath
1840

  
1841
-- | Helper to execute recvMsg but return Nothing if we reach EOF.
1842
handleEOF :: (IO a) -> IO (Maybe a)
1843
handleEOF action =
1844
  catchJust
1845
    (\e -> if isEOFErrorType (ioeGetErrorType e) then Just () else Nothing)
1846
    (liftM Just action)
1847
    (\_ -> return Nothing)
1848

  
1849
-- | Server ping-pong helper.
1850
luxiServerPong :: Luxi.Client -> IO ()
1851
luxiServerPong c = do
1852
  msg <- handleEOF (Luxi.recvMsg c)
1853
  case msg of
1854
    Nothing -> return ()
1855
    Just m -> Luxi.sendMsg c m >> luxiServerPong c
1856

  
1857
-- | Client ping-pong helper.
1858
luxiClientPong :: Luxi.Client -> [String] -> IO [String]
1859
luxiClientPong c =
1860
  mapM (\m -> Luxi.sendMsg c m >> Luxi.recvMsg c)
1861

  
1862
-- | Monadic check that, given a server socket, we can connect via a
1863
-- client to it, and that we can send a list of arbitrary messages and
1864
-- get back what we sent.
1865
prop_Luxi_ClientServer :: [[DNSChar]] -> Property
1866
prop_Luxi_ClientServer dnschars = monadicIO $ do
1867
  let msgs = map (map dnsGetChar) dnschars
1868
  fpath <- run $ getTempFileName
1869
  -- we need to create the server first, otherwise (if we do it in the
1870
  -- forked thread) the client could try to connect to it before it's
1871
  -- ready
1872
  server <- run $ Luxi.getServer fpath
1873
  -- fork the server responder
1874
  _ <- run $ forkIO $
1875
    bracket
1876
      (Luxi.acceptClient server)
1877
      (\c -> Luxi.closeClient c >> removeFile fpath)
1878
      luxiServerPong
1879
  replies <- run $
1880
    bracket
1881
      (Luxi.getClient fpath)
1882
      Luxi.closeClient
1883
      (\c -> luxiClientPong c msgs)
1884
  assert $ replies == msgs
1885

  
1823 1886
testSuite "LUXI"
1824 1887
          [ 'prop_Luxi_CallEncoding
1888
          , 'prop_Luxi_ClientServer
1825 1889
          ]
1826 1890

  
1827 1891
-- * Ssconf tests
b/htools/Ganeti/Luxi.hs
34 34
  , JobId
35 35
  , checkRS
36 36
  , getClient
37
  , getServer
38
  , acceptClient
37 39
  , closeClient
38 40
  , callMethod
39 41
  , submitManyJobs
......
41 43
  , buildCall
42 44
  , validateCall
43 45
  , decodeCall
46
  , recvMsg
47
  , sendMsg
44 48
  ) where
45 49

  
46 50
import Data.IORef
......
216 220
  h <- S.socketToHandle s ReadWriteMode
217 221
  return Client { socket=h, rbuf=rf }
218 222

  
223
-- | Creates and returns a server endpoint.
224
getServer :: FilePath -> IO S.Socket
225
getServer path = do
226
  s <- S.socket S.AF_UNIX S.Stream S.defaultProtocol
227
  S.bindSocket s (S.SockAddrUnix path)
228
  S.listen s 5 -- 5 is the max backlog
229
  return s
230

  
231
-- | Accepts a client
232
acceptClient :: S.Socket -> IO Client
233
acceptClient s = do
234
  -- second return is the address of the client, which we ignore here
235
  (client_socket, _) <- S.accept s
236
  new_buffer <- newIORef B.empty
237
  handle <- S.socketToHandle client_socket ReadWriteMode
238
  return Client { socket=handle, rbuf=new_buffer }
239

  
219 240
-- | Closes the client socket.
220 241
closeClient :: Client -> IO ()
221 242
closeClient = hClose . socket

Also available in: Unified diff