keter-2.1.3: src/Keter/Common.hs
{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TypeFamilies #-}
-- | Provides logging, versioning and some type aliases
module Keter.Common where
import qualified Network.Wai as Wai
import Control.Exception (Exception, SomeException)
import Data.Aeson (FromJSON, Object, ToJSON,
Value (Bool), object, withBool,
withObject, (.!=), (.:?), (.=))
import Data.ByteString (ByteString)
import Data.CaseInsensitive (CI, original)
import Data.Map (Map)
import Data.Set (Set)
import qualified Data.Set as Set
import Data.Text (Text, pack, unpack)
import Data.Typeable (Typeable)
import Data.Vector (Vector)
import qualified Data.Vector as V
import qualified Data.Yaml
import Keter.Yaml.FilePath
import qualified Language.Haskell.TH.Syntax as TH
import Network.Socket (AddrInfo, SockAddr)
import System.Exit (ExitCode)
import System.FilePath (FilePath, takeBaseName)
-- | Name of the application. Should just be the basename of the application
-- file.
type Appname = Text
data Plugin = Plugin
{ pluginGetEnv :: Appname -> Object -> IO [(Text, Text)]
}
type Plugins = [Plugin]
-- | Used for versioning data types.
class ToCurrent a where
type Previous a
toCurrent :: Previous a -> a
instance ToCurrent a => ToCurrent (Maybe a) where
type Previous (Maybe a) = Maybe (Previous a)
toCurrent = fmap toCurrent
-- | A port for an individual app to listen on.
type Port = Int
-- | A virtual host we want to serve content from.
type Host = CI Text
type HostBS = CI ByteString
getAppname :: FilePath -> Text
getAppname = pack . takeBaseName
data KeterException = CannotParsePostgres FilePath
| ExitCodeFailure FilePath ExitCode
| NoPortsAvailable
| InvalidConfigFile Data.Yaml.ParseException
| InvalidKeterConfigFile !FilePath !Data.Yaml.ParseException
| CannotReserveHosts !AppId !(Map Host AppId)
| FileNotExecutable !FilePath
| ExecutableNotFound !FilePath
| EnsureAliveShouldBeBiggerThenZero { keterExceptionGot:: !Int }
deriving (Show, Typeable)
instance Exception KeterException
data AppId = AIBuiltin | AINamed !Appname
deriving (Eq, Ord)
instance Show AppId where
show AIBuiltin = "/builtin/"
show (AINamed t) = unpack t
data SSLConfig
= SSLFalse
| SSLTrue
| SSL !FilePath !(Vector FilePath) !FilePath
deriving (Show, Eq, Ord)
instance ParseYamlFile SSLConfig where
parseYamlFile _ v@(Bool _) =
withBool "ssl" ( \b ->
return (if b then SSLTrue else SSLFalse) ) v
parseYamlFile basedir v = withObject "ssl" ( \o -> do
mcert <- lookupBaseMaybe basedir o "certificate"
mkey <- lookupBaseMaybe basedir o "key"
case (mcert, mkey) of
(Just cert, Just key) -> do
chainCerts <- o .:? "chain-certificates"
>>= maybe (return V.empty) (parseYamlFile basedir)
return $ SSL cert chainCerts key
_ -> return SSLFalse
) v
instance ToJSON SSLConfig where
toJSON SSLTrue = Bool True
toJSON SSLFalse = Bool False
toJSON (SSL c cc k) = object [ "certificate" .= c
, "chain-certificates" .= cc
, "key" .= k
]
instance FromJSON SSLConfig where
parseJSON v@(Bool _) = withBool "ssl" ( \b ->
return (if b then SSLTrue else SSLFalse) ) v
parseJSON v = withObject "ssl" ( \o -> do
mcert <- o .:? "certificate"
mkey <- o .:? "key"
case (mcert, mkey) of
(Just cert, Just key) -> do
chainCerts <- o .:? "chain-certificates" .!= V.empty
return $ SSL cert chainCerts key
_ -> return SSLFalse -- fail "Must provide both certificate and key files"
) v