{-# LANGUAGE TemplateHaskell #-}
import System.Environment (getArgs)
import Data.Binary (encode, decode)
import Control.Monad (forever)
import Control.Monad.IO.Class (liftIO)
import Control.Exception (try, IOException)
import Control.Distributed.Process
( Process
, getSelfPid
, expect
, send
, monitor
, receiveWait
, match
, ProcessMonitorNotification(..)
)
import Control.Distributed.Process.Closure (remotable, mkClosure)
import Control.Distributed.Process.Backend.Azure
import qualified Data.ByteString.Lazy as BSL (readFile, writeFile)
pingServer :: () -> Backend -> Process ()
pingServer () _backend = do
us <- getSelfPid
liftIO $ BSL.writeFile "pingServer.pid" (encode us)
forever $ do
them <- expect
send them ()
pingClientRemote :: () -> Backend -> Process ()
pingClientRemote () _backend = do
mPingServerEnc <- liftIO $ try (BSL.readFile "pingServer.pid")
case mPingServerEnc of
Left err ->
remoteSend $ "Ping server not found: " ++ show (err :: IOException)
Right pingServerEnc -> do
let pingServerPid = decode pingServerEnc
pid <- getSelfPid
_ref <- monitor pingServerPid
send pingServerPid pid
gotReply <- receiveWait
[ match (\() -> return True)
, match (\(ProcessMonitorNotification {}) -> return False)
]
if gotReply
then remoteSend $ "Ping server at " ++ show pingServerPid ++ " ok"
else remoteSend $ "Ping server at " ++ show pingServerPid ++ " failure"
remotable ['pingClientRemote, 'pingServer]
pingClientLocal :: LocalProcess ()
pingClientLocal = localExpect >>= liftIO . putStrLn
main :: IO ()
main = do
args <- getArgs
case args of
"onvm":args' ->
-- Pass execution to 'onVmMain' if we are running on the VM
onVmMain __remoteTable args'
"list":sid:x509:pkey:_ -> do
-- List all available cloud services
-- (useful, but not strictly necessary for the example)
params <- defaultAzureParameters sid x509 pkey
css <- cloudServices (azureSetup params)
mapM_ print css
cmd:sid:x509:pkey:user:cloudService:virtualMachine:port:_ -> do
-- Initialize the backend and find the right VM
params <- defaultAzureParameters sid x509 pkey
let params' = params { azureSshUserName = user }
backend <- initializeBackend params' cloudService
Just vm <- findNamedVM backend virtualMachine
-- The same binary can behave as the client or the server,
-- depending on the command line arguments
case cmd of
"server" -> do
pid <- spawnOnVM backend vm port ($(mkClosure 'pingServer) ())
putStrLn $ "Ping server started at " ++ show pid
"client" ->
callOnVM backend vm port $
ProcessPair ($(mkClosure 'pingClientRemote) ())
pingClientLocal