keter/Keter/Main.hs

136 lines
5.3 KiB
Haskell
Raw Normal View History

2012-05-14 12:18:09 +04:00
{-# LANGUAGE OverloadedStrings #-}
2012-05-15 12:38:54 +04:00
{-# LANGUAGE NoImplicitPrelude #-}
2012-05-17 10:32:11 +04:00
{-# LANGUAGE TemplateHaskell #-}
2012-05-29 06:30:59 +04:00
{-# LANGUAGE RecordWildCards #-}
2012-05-11 12:29:25 +04:00
module Keter.Main
( keter
) where
2012-05-17 11:59:17 +04:00
import Keter.Prelude hiding (getCurrentTime)
2012-05-11 12:29:25 +04:00
import qualified Keter.Nginx as Nginx
import qualified Keter.TempFolder as TempFolder
import qualified Keter.App as App
2012-05-11 12:42:56 +04:00
import qualified Keter.Postgres as Postgres
2012-05-17 08:15:25 +04:00
import qualified Keter.LogFile as LogFile
2012-05-17 10:32:11 +04:00
import qualified Keter.Logger as Logger
2012-05-11 12:29:25 +04:00
import qualified Control.Concurrent.MVar as M
import qualified Data.Map as Map
import qualified System.INotify as I
2012-05-29 06:30:59 +04:00
import Control.Monad (forever, mzero)
2012-05-14 11:15:50 +04:00
import qualified Filesystem.Path.CurrentOS as F
2012-05-29 06:30:59 +04:00
import qualified Filesystem as F
2012-05-14 12:18:09 +04:00
import Control.Exception (throwIO)
2012-05-15 12:38:54 +04:00
import qualified Prelude as P
2012-05-17 08:15:25 +04:00
import Data.Text.Encoding (encodeUtf8)
import Data.Time (getCurrentTime)
import qualified Data.Text as T
2012-05-17 10:32:11 +04:00
import Data.Maybe (fromMaybe)
2012-05-29 06:30:59 +04:00
import Data.Yaml (decodeFile, FromJSON (parseJSON), Value (Object), (.:), (.:?), (.!=))
import Control.Applicative ((<$>), (<*>))
2012-05-11 12:29:25 +04:00
2012-05-29 06:30:59 +04:00
data Config = Config
{ configDir :: F.FilePath
, configNginx :: Nginx.Settings
}
instance FromJSON Config where
parseJSON (Object o) = Config
<$> (F.fromText <$> o .: "root")
<*> o .:? "nginx" .!= def
parseJSON _ = mzero
keter :: P.FilePath -- ^ root directory or config file
2012-05-15 12:38:54 +04:00
-> P.IO ()
2012-05-29 06:30:59 +04:00
keter input' = do
exists <- F.isFile input
Config{..} <-
if exists
then decodeFile input' >>= maybe (P.error "Invalid config file") return
else return $ Config input def
let dir = F.directory input F.</> configDir
nginx <- runThrow $ Nginx.start configNginx
2012-05-15 12:38:54 +04:00
tf <- runThrow $ TempFolder.setup $ dir </> "temp"
postgres <- runThrow $ Postgres.load def $ dir </> "etc" </> "postgres.yaml"
2012-05-17 08:15:25 +04:00
mainlog <- runThrow $ LogFile.start $ dir </> "log" </> "keter"
let runKIO' = runKIO $ \ml -> do
now <- getCurrentTime
let bs = encodeUtf8 $ T.concat
2012-05-17 10:32:11 +04:00
[ T.take 22 $ show now
2012-05-17 08:15:25 +04:00
, ": "
, show ml
, "\n"
]
runKIOPrint $ LogFile.addChunk mainlog bs
runKIOPrint = runKIO P.print
2012-05-11 12:29:25 +04:00
mappMap <- M.newMVar Map.empty
2012-05-15 12:19:03 +04:00
let removeApp appname = Keter.Prelude.modifyMVar_ mappMap $ return . Map.delete appname
2012-05-11 12:29:25 +04:00
addApp bundle = do
let appname = getAppname bundle
2012-05-15 12:38:54 +04:00
rest <- modifyMVar mappMap $ \appMap ->
2012-05-11 12:29:25 +04:00
case Map.lookup appname appMap of
Just app -> do
2012-05-15 12:38:54 +04:00
App.reload app
2012-05-11 12:29:25 +04:00
return (appMap, return ())
Nothing -> do
2012-05-17 10:32:11 +04:00
mlogger <- do
let dirout = dir </> "log" </> fromText ("app-" ++ appname)
direrr = dirout </> "err"
elfout <- LogFile.start dirout
case elfout of
Left e -> do
$logEx e
return Nothing
Right lfout -> do
elferr <- LogFile.start direrr
case elferr of
Left e -> do
$logEx e
LogFile.close lfout
return Nothing
Right lferr -> fmap Just $ Logger.start lfout lferr
let logger = fromMaybe Logger.dummy mlogger
2012-05-15 12:38:54 +04:00
(app, rest) <- App.start
2012-05-14 11:15:50 +04:00
tf
nginx
postgres
2012-05-17 10:32:11 +04:00
logger
2012-05-14 11:15:50 +04:00
appname
2012-05-15 12:38:54 +04:00
bundle
2012-05-14 11:15:50 +04:00
(removeApp appname)
2012-05-11 12:29:25 +04:00
let appMap' = Map.insert appname app appMap
return (appMap', rest)
2012-05-15 12:38:54 +04:00
rest
2012-05-11 12:29:25 +04:00
terminateApp appname = do
appMap <- M.readMVar mappMap
case Map.lookup appname appMap of
Nothing -> return ()
2012-05-15 12:38:54 +04:00
Just app -> runKIO' $ App.terminate app
2012-05-11 12:29:25 +04:00
let incoming = dir </> "incoming"
2012-05-15 12:38:54 +04:00
isKeter fp = hasExtension fp "keter"
isKeter' = isKeter . F.decodeString
2012-05-17 12:47:54 +04:00
createTree incoming
2012-05-15 12:38:54 +04:00
bundles <- fmap (filter isKeter) $ listDirectory incoming
runKIO' $ mapM_ addApp bundles
2012-05-11 12:29:25 +04:00
let events = [I.MoveIn, I.MoveOut, I.Delete, I.CloseWrite]
i <- I.initINotify
2012-05-17 08:15:25 +04:00
_ <- I.addWatch i events (toString incoming) $ \e -> do
2012-05-11 12:29:25 +04:00
case e of
2012-05-15 12:38:54 +04:00
I.Deleted _ fp -> when (isKeter' fp) $ terminateApp $ getAppname' fp
I.MovedOut _ fp _ -> when (isKeter' fp) $ terminateApp $ getAppname' fp
I.Closed _ (Just fp) _ -> when (isKeter' fp) $ runKIO' $ addApp $ incoming </> F.decodeString fp
I.MovedIn _ fp _ -> when (isKeter' fp) $ runKIO' $ addApp $ incoming </> F.decodeString fp
2012-05-17 10:39:16 +04:00
_ -> runKIO' $ log $ ReceivedInotifyEvent $ show e
2012-05-11 12:29:25 +04:00
2012-05-15 12:38:54 +04:00
runKIO' $ forever $ threadDelay $ 60 * 1000 * 1000
2012-05-11 12:29:25 +04:00
where
2012-05-15 12:38:54 +04:00
getAppname = either id id . toText . basename
getAppname' = getAppname . F.decodeString
2012-05-17 08:15:25 +04:00
runThrow f = runKIO P.print f >>= either throwIO return
2012-05-29 06:30:59 +04:00
input = F.decodeString input'