{-# LANGUAGE QuasiQuotes, TypeFamilies, TemplateHaskell, MultiParamTypeClasses #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE DeriveDataTypeable #-} {-# LANGUAGE ViewPatterns #-} {-# OPTIONS_GHC -fno-warn-orphans #-} {-# OPTIONS_GHC -fno-warn-type-defaults #-} module Test.Sandbox.Compose.Server where import Test.Sandbox.Compose.Type import Test.Sandbox.Compose.Core import Network.Wai.Handler.Warp (run) import Yesod.Core import qualified Data.Text as T import Data.IORef.Lifted import Test.Sandbox hiding (run) import Test.Sandbox.Internals hiding (Port) import qualified Data.Yaml as Y import qualified Data.Map as M import Control.Concurrent import System.Exit import System.Directory import System.Posix.Process import System.Posix hiding (Kill) import Shelly hiding (command,run,FilePath) import Data.Monoid default (T.Text) instance Yesod App where errorHandler e = liftIO (appendFile ".sandbox/log" (show e)) >> defaultErrorHandler e getUpAllR :: HandlerT App IO RepPlain getUpAllR = do (App serv sand) <- getYesod result <- liftIO $ flip runSandbox sand $ do runServices =<< readIORef serv case result of Right _ -> return $ RepPlain "OK\n" Left err -> return $ RepPlain $ toContent $ err getUpR :: ServiceName -> HandlerT App IO RepPlain getUpR serviceName = do (App serv sand) <- getYesod result <- liftIO $ flip runSandbox sand $ do runService serviceName =<< readIORef serv case result of Right _ -> return $ RepPlain "OK\n" Left err -> return $ RepPlain $ toContent $ err getStatusAllR :: HandlerT App IO RepPlain getStatusAllR = do (App _serv sand) <- getYesod sand' <- liftIO $ readIORef sand return $ RepPlain $ toContent $ Y.encode $ sand' {ssAvailablePorts=[]} getStatusR :: ServiceName -> HandlerT App IO RepPlain getStatusR serviceName = do (App _serv sand) <- getYesod sand' <- liftIO $ readIORef sand return $ RepPlain $ toContent $ Y.encode $ M.lookup serviceName $ ssProcesses sand' getConfR :: HandlerT App IO RepPlain getConfR = do (App serv _sand) <- getYesod sand' <- liftIO $ readIORef serv return $ RepPlain $ toContent $ Y.encode $ sand' getKillAllR :: HandlerT App IO RepPlain getKillAllR = do (App _ sand) <- getYesod result <- liftIO $ flip runSandbox sand $ do killServices case result of Right _ -> return $ RepPlain "OK\n" Left err -> return $ RepPlain $ toContent $ err getKillR :: ServiceName -> HandlerT App IO RepPlain getKillR serviceName = do (App _ sand) <- getYesod result <- liftIO $ flip runSandbox sand $ do killService serviceName case result of Right _ -> return $ RepPlain "OK\n" Left err -> return $ RepPlain $ toContent $ err getDestroyR :: HandlerT App IO RepPlain getDestroyR = do (App _ sand) <- getYesod _ <- liftIO $ flip runSandbox sand $ do killServices _ <- liftIO $ forkIO $ do threadDelay (1*1000*1000) shelly $ do rm ".sandbox/port" exitImmediately ExitSuccess return $ RepPlain "OK\n" getLogsDaemonR :: HandlerT App IO RepPlain getLogsDaemonR = do out <- liftIO $ readFile (".sandbox/" <> ".server" <> "_out.txt") err <- liftIO $ readFile (".sandbox/" <> ".server" <> "_err.txt") return $ RepPlain $ toContent $ out <> err getLogsR :: ServiceName -> HandlerT App IO RepPlain getLogsR serviceName = do out <- liftIO $ readFile (".sandbox/" <> serviceName <> "_out.txt") err <- liftIO $ readFile (".sandbox/" <> serviceName <> "_err.txt") return $ RepPlain $ toContent $ out <> err mkYesod "App" [parseRoutes| /up UpAllR GET /up/#ServiceName UpR GET /status StatusAllR GET /status/#ServiceName StatusR GET /conf ConfR GET /kill KillAllR GET /kill/#ServiceName KillR GET /logs LogsDaemonR GET /logs/#ServiceName LogsR GET /destroy DestroyR GET |] runServer' :: FilePath -> IO (App,Int) runServer' conf = do cdir <- getCurrentDirectory let dir=(cdir <> "/.sandbox") shelly $ do unlessM (test_e ".sandbox") $ mkdir ".sandbox" state <- newSandboxState "compose" dir port <- runSandbox' state $ do getPort "test-sandbox-compose" writeFile (dir <> "/port") $ show port eServ <- Y.decodeFileEither conf :: IO (Either Y.ParseException Services) case eServ of Left err -> do print err exitWith $ ExitFailure 1 Right serv -> do serv' <- runSandbox' state $ do mserv <- setupServices serv case mserv of Just s -> return s Nothing -> liftIO $ do print "setupServices is failed." exitWith $ ExitFailure 1 services <- newIORef serv' return (App services state,port) runServer :: FilePath -> Bool -> IO () runServer conf enbBackGround = do (app,port) <- runServer' conf let prog :: IO () prog = toWaiApp app >>= run port backGround enbBackGround prog backGround :: Bool -> IO () -> IO () backGround exitP prog = do _ <- forkProcess p when exitP $ do exitImmediately ExitSuccess where p = do _ <- createSession _ <- forkProcess p' exitImmediately ExitSuccess p' = do switchDescriptors prog switchDescriptors :: IO () switchDescriptors = do null' <- openFd "/dev/null" ReadWrite Nothing defaultFileFlags out <- openFd ".sandbox/.server_out.txt" ReadWrite (Just 0o644) defaultFileFlags err <- openFd ".sandbox/.server_err.txt" ReadWrite (Just 0o644) defaultFileFlags let sendTo' fd' fd = closeFd fd >> dupTo fd' fd _ <- sendTo' null' stdInput _ <- sendTo' out stdOutput _ <- sendTo' err stdError return ()