{-# LANGUAGE DataKinds #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE DeriveGeneric #-}
module Toxiproxy
( getVersion
, postReset
, getProxies
, createProxy
, getProxy
, postPopulate
, updateProxy
, deleteProxy
, getToxics
, createToxic
, getToxic
, updateToxic
, deleteToxic
, Proxy(..)
, Toxic(..)
, Populate(..)
, toxiproxyUrl
, withDisabled
, withToxic
, withProxy
, run
, Version
) where
import Servant.API
import Servant.Client
import qualified Data.Proxy as Proxy
import Data.Text (Text)
import Data.List (stripPrefix)
import Data.Char (toLower)
import GHC.Generics
import Data.Aeson (FromJSON, parseJSON, fieldLabelModifier, defaultOptions, genericParseJSON,
ToJSON, genericToJSON, toJSON)
import Data.Map.Strict (Map)
import Network.HTTP.Client (newManager, defaultManagerSettings)
import Control.Exception (bracket)
import Control.Monad (void)
type ToxiproxyAPI =
"version" :> Get '[PlainText] Version
:<|> "reset" :> Post '[] NoContent
:<|> "proxies" :> Get '[JSON] (Map Text Proxy)
:<|> "proxies" :> ReqBody '[JSON] Proxy :> Post '[JSON] Proxy
:<|> "proxies" :> Capture "name" Text :> Get '[JSON] Proxy
:<|> "populate" :> ReqBody '[JSON] [Proxy] :> Post '[JSON] Populate
:<|> "proxies" :> Capture "name" Text :> ReqBody '[JSON] Proxy :> Post '[JSON] Proxy
:<|> "proxies" :> Capture "name" Text :> Delete '[] NoContent
:<|> "proxies" :> Capture "name" Text :>
"toxics" :> Get '[JSON] [Toxic]
:<|> "proxies" :> Capture "name" Text :>
"toxics" :> ReqBody '[JSON] Toxic :> Post '[JSON] Toxic
:<|> "proxies" :> Capture "name" Text :>
"toxics" :> Capture "name" Text :> Get '[JSON] Toxic
:<|> "proxies" :> Capture "name" Text :>
"toxics" :> Capture "name" Text :> ReqBody '[JSON] Toxic :> Get '[JSON] Toxic
:<|> "proxies" :> Capture "name" Text :>
"toxics" :> Capture "name" Text :> Delete '[JSON] NoContent
type Version = Text
data Proxy = Proxy
{ proxyName :: Text
, proxyListen :: Text
, proxyUpstream :: Text
, proxyEnabled :: Bool
, proxyToxics :: [Toxic]
} deriving (Show, Eq, Generic)
instance FromJSON Proxy where
parseJSON = genericParseJSON $
defaultOptions
{ fieldLabelModifier = stripPrefixJSON "proxy" }
instance ToJSON Proxy where
toJSON = genericToJSON $
defaultOptions
{ fieldLabelModifier = stripPrefixJSON "proxy" }
data Toxic = Toxic
{ toxicName :: Text
, toxicType :: Text
, toxicStream :: Text
, toxicToxicity :: Float
, toxicAttributes :: Map Text Int
} deriving (Show, Eq, Generic)
instance FromJSON Toxic where
parseJSON = genericParseJSON $
defaultOptions
{ fieldLabelModifier = stripPrefixJSON "toxic" }
instance ToJSON Toxic where
toJSON = genericToJSON $
defaultOptions
{ fieldLabelModifier = stripPrefixJSON "toxic" }
newtype Populate = Populate { populateProxies :: [Proxy] }
deriving (Show, Eq, Generic)
instance FromJSON Populate where
parseJSON = genericParseJSON $
defaultOptions
{ fieldLabelModifier = stripPrefixJSON "populate" }
stripPrefixJSON :: String -> String -> String
stripPrefixJSON prefix str =
case stripPrefix prefix str of
Nothing -> str
Just (first : rest) -> toLower first : rest
toxiproxyAPI :: Proxy.Proxy ToxiproxyAPI
toxiproxyAPI = Proxy.Proxy
getVersion :: ClientM Version
postReset :: ClientM NoContent
getProxies :: ClientM (Map Text Proxy)
createProxy :: Proxy -> ClientM Proxy
getProxy :: Text -> ClientM Proxy
postPopulate :: [Proxy] -> ClientM Populate
updateProxy :: Text -> Proxy -> ClientM Proxy
deleteProxy :: Text -> ClientM NoContent
getToxics :: Text -> ClientM [Toxic]
createToxic :: Text -> Toxic -> ClientM Toxic
getToxic :: Text -> Text -> ClientM Toxic
updateToxic :: Text -> Text -> Toxic -> ClientM Toxic
deleteToxic :: Text -> Text -> ClientM NoContent
(getVersion :<|> postReset :<|> getProxies :<|> createProxy :<|> getProxy :<|> postPopulate
:<|> updateProxy :<|> deleteProxy :<|> getToxics :<|> createToxic :<|> getToxic
:<|> updateToxic :<|> deleteToxic) = client toxiproxyAPI
toxiproxyUrl :: BaseUrl
toxiproxyUrl = BaseUrl Http "127.0.0.1" 8474 ""
run :: ClientM a -> IO (Either ServantError a)
run f = do
manager <- newManager defaultManagerSettings
runClientM f (ClientEnv manager toxiproxyUrl)
withDisabled :: Proxy -> IO a -> IO a
withDisabled proxy f =
bracket disable enable $ const f
where
enable = const . run $ updateProxy (proxyName proxy) proxy
disable = void . run $ updateProxy (proxyName proxy) disabledProxy
disabledProxy = proxy { proxyEnabled = False }
withToxic :: Proxy -> Toxic -> IO a -> IO a
withToxic proxy toxic f =
bracket enable disable $ const f
where
enable = void . run $ createToxic (proxyName proxy) toxic
disable = const . run $ deleteToxic (proxyName proxy) (toxicName toxic)
withProxy :: Proxy -> (Proxy -> IO a) -> IO a
withProxy proxy =
bracket create delete
where
create = run (createProxy proxy) >> return proxy
delete = const . run $ deleteProxy (proxyName proxy)