packages feed

angel-0.5.2: src/Angel/Data.hs

{-# LANGUAGE GeneralizedNewtypeDeriving #-}
module Angel.Data ( GroupConfig(..)
                  , SpecKey
                  , RunKey
                  , ProgramId
                  , FileRequest
                  , Program(..)
                  , RunState(..)
                  , Spec
                  , KillDirective(..)
                  , Verbosity(..)
                  , Options(..)
                  , AngelM(..)
                  , defaultProgram
                  , defaultDelay
                  , defaultStdout
                  , defaultStderr
                  , runAngelM
                  ) where

import qualified Data.Map as M
import System.Process ( ProcessHandle )
import Control.Applicative
import Control.Monad.IO.Class
import Control.Monad.Reader
import Control.Concurrent.STM.TChan (TChan)
import System.IO (Handle)

-- |the whole shared state of the program; spec is what _should_
-- |be running, while running is what actually _is_ running_ currently
data GroupConfig = GroupConfig {
    spec :: SpecKey,
    running :: RunKey,
    fileRequest :: TChan FileRequest
}

-- |map program ids to relevant structure
type SpecKey = M.Map ProgramId Program
type RunKey = M.Map ProgramId RunState

data RunState = RunState {
  rsProgram :: Program,
  rsHandle :: Maybe ProcessHandle,
  rsLogHandle :: Maybe ProcessHandle
}

type ProgramId = String
type FileRequest = (String, TChan (Maybe Handle))

-- |the representation of a program is these 6 values,
-- |read from the config file
data Program = Program {
  name       :: String,
  exec       :: Maybe String,
  user       :: Maybe String,
  delay      :: Maybe Int,
  stdout     :: Maybe String,
  stderr     :: Maybe String,
  workingDir :: Maybe FilePath,
  logExec    :: Maybe String,
  pidFile    :: Maybe FilePath,
  env        :: [(String, String)],
  termGrace  :: Maybe Int -- ^ How long to wait after sending a SIGTERM before SIGKILL. Nothing = never SIGKILL. Default Nothing
} deriving (Show, Eq, Ord)

-- |represents all the data needed to handle terminating a process
data KillDirective = SoftKill String ProcessHandle (Maybe ProcessHandle) |
                     HardKill String ProcessHandle (Maybe ProcessHandle) Int

-- instance Show KillDirective where
--   show (SoftKill _)       = "SoftKill"
--   show (HardKill _ grace) = "HardKill after " ++ show grace ++ "s"

-- |Lower-level atoms in the configuration process
type Spec = [Program]


data Verbosity = V0
               -- ^ Failures only
               | V1
               -- ^ Failures + program starts/stops
               | V2
               -- ^ Max verbosity. Default. Logs all of the above as well as state changes and other debugging info.
               deriving (Show, Eq, Ord)


data Options = Options {
      configFile :: FilePath
    , verbosity  :: Verbosity
    }


newtype AngelM a = AngelM {
      unAngelM :: ReaderT Options IO a
    } deriving (Functor, Applicative, Monad, MonadReader Options, MonadIO)


runAngelM :: Options -> AngelM a -> IO a
runAngelM o (AngelM f) = runReaderT f o

-- |a template for an empty program; the variable set to ""
-- |are required, and must be overridden in the config file
defaultProgram :: Program
defaultProgram = Program "" Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing [] Nothing

defaultDelay :: Int
defaultDelay = 5

defaultStdout :: FilePath
defaultStdout = "/dev/null"

defaultStderr :: FilePath
defaultStderr = "/dev/null"