PageRenderTime 25ms CodeModel.GetById 0ms RepoModel.GetById 0ms app.codeStats 0ms

/Angel/Config.hs

http://github.com/jamwt/Angel
Haskell | 99 lines | 74 code | 18 blank | 7 comment | 6 complexity | bb3d46a73e1db97160fd41c67b7c3937 MD5 | raw file
Possible License(s): BSD-3-Clause
  1. module Angel.Config where
  2. import Control.Exception (try, SomeException)
  3. import qualified Data.Map as M
  4. import Control.Monad (when, mapM_)
  5. import Control.Concurrent.STM
  6. import Control.Concurrent.STM.TVar (readTVar, writeTVar)
  7. import Data.Configurator (load, getMap, Worth(..))
  8. import Data.Configurator.Types (Config, Value(..), Name)
  9. import qualified Data.HashMap.Lazy as HM
  10. import Data.String.Utils (split)
  11. import Data.List (foldl')
  12. import qualified Data.Text as T
  13. import Angel.Job (syncSupervisors)
  14. import Angel.Data
  15. import Angel.Log (logger)
  16. import Angel.Util (waitForWake)
  17. import Debug.Trace (trace)
  18. void :: Monad m => m a -> m ()
  19. void m = m >> return ()
  20. -- |produce a mapping of name -> program for every program
  21. buildConfigMap :: HM.HashMap Name Value -> IO SpecKey
  22. buildConfigMap cfg =
  23. return $! HM.foldlWithKey' addToMap M.empty $ cfg
  24. where
  25. addToMap :: SpecKey -> Name -> Value -> SpecKey
  26. addToMap m
  27. (split "." . T.unpack -> [basekey, localkey])
  28. value =
  29. let !newprog = case M.lookup basekey m of
  30. Just prog -> modifyProg prog localkey value
  31. Nothing -> modifyProg defaultProgram{name=basekey} localkey value
  32. in
  33. M.insert basekey newprog m
  34. addToMap m _ _ = m
  35. checkConfigValues :: SpecKey -> IO SpecKey
  36. checkConfigValues progs = (mapM_ checkProgram $ M.elems progs) >> (return progs)
  37. where
  38. checkProgram p = void $ when (exec p == Nothing) $ error $ name p ++ " does not have an 'exec' specification"
  39. modifyProg :: Program -> String -> Value -> Program
  40. modifyProg prog "exec" (String s) = prog{exec = Just (T.unpack s)}
  41. modifyProg prog "exec" _ = error "wrong type for field 'exec'; string required"
  42. modifyProg prog "delay" (Number n) | n < 0 = error "delay value must be >= 0"
  43. | otherwise = prog{delay = Just $ round n}
  44. modifyProg prog "delay" _ = error "wrong type for field 'delay'; integer"
  45. modifyProg prog "stdout" (String s) = prog{stdout = Just (T.unpack s)}
  46. modifyProg prog "stdout" _ = error "wrong type for field 'stdout'; string required"
  47. modifyProg prog "stderr" (String s) = prog{stderr = Just (T.unpack s)}
  48. modifyProg prog "stderr" _ = error "wrong type for field 'stderr'; string required"
  49. modifyProg prog "directory" (String s) = prog{workingDir = (Just $ T.unpack s)}
  50. modifyProg prog "directory" _ = error "wrong type for field 'directory'; string required"
  51. modifyProg prog n _ = prog
  52. -- |invoke the parser to process the file at configPath
  53. -- |produce a SpecKey
  54. processConfig :: String -> IO (Either String SpecKey)
  55. processConfig configPath = do
  56. mconf <- try $ load [Required configPath] >>= getMap >>= buildConfigMap >>= checkConfigValues
  57. case mconf of
  58. Right config -> return $ Right config
  59. Left (e :: SomeException) -> return $ Left $ show e
  60. -- |given a new SpecKey just parsed from the file, update the
  61. -- |shared state TVar
  62. updateSpecConfig :: TVar GroupConfig -> SpecKey -> STM ()
  63. updateSpecConfig sharedGroupConfig spec = do
  64. cfg <- readTVar sharedGroupConfig
  65. writeTVar sharedGroupConfig cfg{spec=spec}
  66. -- |read the config file, update shared state with current spec,
  67. -- |re-sync running supervisors, wait for the HUP TVar, then repeat!
  68. monitorConfig :: String -> TVar GroupConfig -> TVar (Maybe Int) -> IO ()
  69. monitorConfig configPath sharedGroupConfig wakeSig = do
  70. let log = logger "config-monitor"
  71. mspec <- processConfig configPath
  72. case mspec of
  73. Left e -> do
  74. log $ " <<<< Config Error >>>>\n" ++ e
  75. log " <<<< Config Error: Skipping reload >>>>"
  76. Right spec -> do
  77. print spec
  78. atomically $ updateSpecConfig sharedGroupConfig spec
  79. syncSupervisors sharedGroupConfig
  80. waitForWake wakeSig
  81. log "HUP caught, reloading config"