packages feed

toxiproxy-haskell 0.1.0.0 → 0.2.0.0

raw patch · 3 files changed

+173/−46 lines, 3 files

Files

src/Toxiproxy.hs view
@@ -1,6 +1,8 @@-{-# LANGUAGE DataKinds     #-}-{-# LANGUAGE TypeOperators #-}-{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE DataKinds                  #-}+{-# LANGUAGE TypeOperators              #-}+{-# LANGUAGE DeriveGeneric              #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE OverloadedStrings          #-} module Toxiproxy     ( getVersion     , postReset@@ -18,56 +20,85 @@     , Proxy(..)     , Toxic(..)     , Populate(..)+    , Version(..)+    , Stream(..)+    , ToxicType(..)     , 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.Text (Text, pack, toLower, unpack) import Data.List (stripPrefix)-import Data.Char (toLower)+import qualified Data.Char as Char (toLower) import GHC.Generics import Data.Aeson (FromJSON, parseJSON, fieldLabelModifier, defaultOptions, genericParseJSON,-                   ToJSON, genericToJSON, toJSON)+                   ToJSON, genericToJSON, toJSON, FromJSONKey, Value( String )) import Data.Map.Strict (Map) import Network.HTTP.Client (newManager, defaultManagerSettings) import Control.Exception (bracket) import Control.Monad (void)+import Data.String (IsString)  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 :>+  :<|> "proxies"  :> Get '[JSON] (Map ProxyName Proxy)+  :<|> "proxies"  :> ReqBody '[JSON] Proxy    :> Post '[JSON] Proxy+  :<|> "proxies"  :> Capture "name" ProxyName :> Get '[JSON] Proxy+  :<|> "populate" :> ReqBody '[JSON] [Proxy]  :> Post '[JSON] Populate+  :<|> "proxies"  :> Capture "name" ProxyName :> ReqBody '[JSON] Proxy :> Post '[JSON] Proxy+  :<|> "proxies"  :> Capture "name" ProxyName :> Delete '[] NoContent+  :<|> "proxies"  :> Capture "name" ProxyName :>        "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+  :<|> "proxies"  :> Capture "name" ProxyName :>+       "toxics"   :> ReqBody '[JSON] Toxic    :> Post '[JSON] Toxic+  :<|> "proxies"  :> Capture "name" ProxyName :>+       "toxics"   :> Capture "name" ToxicName :> Get '[JSON] Toxic+  :<|> "proxies"  :> Capture "name" ProxyName :>+       "toxics"   :> Capture "name" ToxicName :> ReqBody '[JSON] Toxic :> Get '[JSON] Toxic+  :<|> "proxies"  :> Capture "name" ProxyName :>+       "toxics"   :> Capture "name" ToxicName :> Delete '[JSON] NoContent -type Version = Text+-- | A unique string for identifying a proxy on the server.+newtype ProxyName = ProxyName Text+  deriving (Show, Eq, IsString, Ord, Generic, ToHttpApiData, FromJSONKey) +instance FromJSON ProxyName+instance ToJSON   ProxyName++-- | A unique string for identifying a toxic on a proxy.+newtype ToxicName = ToxicName Text+  deriving (Show, Eq, IsString, Generic, ToHttpApiData)++instance FromJSON ToxicName+instance ToJSON   ToxicName++-- | The version of the Toxiproxy server. This library is fully supported by any version+--   greater or equal to 2.1.3.+newtype Version = Version Text+  deriving (Show, Eq, MimeUnrender PlainText)++-- | A Toxiproxy proxy. It forwards TCP connections between a listen and upstream host.+--   Toxics can be injected into the proxy to simulate network failure. data Proxy = Proxy-  { proxyName     :: Text-  , proxyListen   :: Text-  , proxyUpstream :: Text+  { proxyName     :: ProxyName+  -- ^ A unique human readable name to identify a proxy.+  , proxyListen   :: Host+  -- ^ The proxy listens on this host:port.+  , proxyUpstream :: Host+  -- ^ The proxy forwards to this upstream host:port.   , proxyEnabled  :: Bool+  -- ^ Whether a proxy is currently listening / accepting connections.   , proxyToxics   :: [Toxic]+  -- ^ The toxics currently applied to the proxy. These should not be specified when+  --   initially creating a proxy. They must be created seperately with 'createToxic'+  --   or 'withToxic'.   } deriving (Show, Eq, Generic)  instance FromJSON Proxy where@@ -80,12 +111,26 @@     defaultOptions       { fieldLabelModifier = stripPrefixJSON "proxy" } +-- | A host:port pair to represent the entrence of a proxy or the upstream the proxy+--   forwards to. For the best experience, provide 127.0.0.1 instead of localhost.+type Host = Text++-- | A toxic is applied to a proxy. It allows the user to simulate a specified kind of+--   network failure on the proxy. data Toxic = Toxic-  { toxicName       :: Text-  , toxicType       :: Text-  , toxicStream     :: Text+  { toxicName       :: ToxicName+  -- ^ A unique human readable name to identify a toxic.+  , toxicType       :: ToxicType+  -- ^ The type of toxic. For example "latency". Please refer to 'ToxicType' or the+  -- Toxiproxy documentation for more information.+  , toxicStream     :: Stream+  -- ^ The direction on which the toxic is applied. Please refer to 'Stream'.   , toxicToxicity   :: Float+  -- ^ The strength that the toxic is applied to the proxy. Please refer to the Toxiproxy+  --   documation.   , toxicAttributes :: Map Text Int+  -- ^ Attributes configure a toxic. They differ based on the 'ToxicType'. Please refer to+  --   the Toxiproxy documentation.   } deriving (Show, Eq, Generic)  instance FromJSON Toxic where@@ -98,6 +143,7 @@     defaultOptions       { fieldLabelModifier = stripPrefixJSON "toxic" } +-- | The return value of the 'populate' endpoint. newtype Populate = Populate { populateProxies :: [Proxy] }   deriving (Show, Eq, Generic) @@ -106,41 +152,108 @@     defaultOptions       { fieldLabelModifier = stripPrefixJSON "populate" } +-- | A toxic can be applied to the upstream or the downstream of a connection. Upstream is+--   the stream traveling from the connecting client to the upstream server. Downstream is+--   the stream traveling from the upstream server to the connecting client.+data Stream = Upstream | Downstream+  deriving (Show, Eq)++instance ToJSON Stream where+  toJSON Upstream   = String "upstream"+  toJSON Downstream = String "downstream"++instance FromJSON Stream where+  parseJSON (String stream) =+    case stream of+      "upstream"   -> return Upstream+      "downstream" -> return Downstream++-- | Different toxic types simulate different kinds of failure. Different toxics require+--   different attribute configuration. Please refer to the Toxiproxy documentation.+data ToxicType =+    Latency+  | Bandwidth+  | SlowClose+  | Timeout+  | Slicer+  | LimitData+  | Other Text+  deriving (Show, Eq)++instance ToJSON ToxicType where+  toJSON Latency       = String "latency"+  toJSON Bandwidth     = String "bandwidth"+  toJSON SlowClose     = String "slow_close"+  toJSON Timeout       = String "timeout"+  toJSON Slicer        = String "slicer"+  toJSON LimitData     = String "limit_data"+  toJSON (Other other) = String other++instance FromJSON ToxicType where+  parseJSON (String toxicType) =+    case toxicType of+      "latency"   -> return Latency+      "bandwidth" -> return Bandwidth+      "slow_clos" -> return SlowClose+      "timeout"   -> return Timeout+      "slicer"    -> return Slicer+      "limit_dat" -> return LimitData+      other       -> return . Other $ other+ stripPrefixJSON :: String -> String -> String stripPrefixJSON prefix str =   case stripPrefix prefix str of     Nothing             -> str-    Just (first : rest) -> toLower first : rest+    Just (first : rest) -> Char.toLower first : rest  toxiproxyAPI :: Proxy.Proxy ToxiproxyAPI toxiproxyAPI = Proxy.Proxy  getVersion   :: ClientM Version postReset    :: ClientM NoContent-getProxies   :: ClientM (Map Text Proxy)+getProxies   :: ClientM (Map ProxyName Proxy) createProxy  :: Proxy -> ClientM Proxy-getProxy     :: Text -> ClientM Proxy+getProxy     :: ProxyName -> 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+updateProxy  :: ProxyName -> Proxy -> ClientM Proxy+deleteProxy  :: ProxyName -> ClientM NoContent+getToxics    :: ProxyName -> ClientM [Toxic]+createToxic  :: ProxyName -> Toxic -> ClientM Toxic+getToxic     :: ProxyName -> ToxicName -> ClientM Toxic+updateToxic  :: ProxyName -> ToxicName -> Toxic -> ClientM Toxic+deleteToxic  :: ProxyName -> ToxicName -> ClientM NoContent  (getVersion :<|> postReset :<|> getProxies :<|> createProxy :<|> getProxy :<|> postPopulate             :<|> updateProxy :<|> deleteProxy :<|> getToxics :<|> createToxic :<|> getToxic             :<|> updateToxic :<|> deleteToxic) = client toxiproxyAPI +-- | The default Toxiproxy service URL.+--   (127.0.0.1:8474) toxiproxyUrl :: BaseUrl toxiproxyUrl = BaseUrl Http "127.0.0.1" 8474 "" +-- | A helper for easily querying the Toxiproxy API. Assumes Toxiproxy is running on+--  'toxiproxyUrl'.+--+-- @+-- proxies <- run getProxies+-- @ run :: ClientM a -> IO (Either ServantError a) run f = do   manager <- newManager defaultManagerSettings   runClientM f (ClientEnv manager toxiproxyUrl) +-- | Given an enabled proxy, disable the proxy, run the given action and then re-enable+--   the proxy.+--+--   This is useful for simulating a crashed server or closed connection.+--+-- @+-- connectToMyProxy       -- This will connect.+-- withDisabled myProxy $+--   connectToMyProxy     -- This will get rejected.+-- connectToMyProxy       -- This will connect again.+-- @ withDisabled :: Proxy -> IO a -> IO a withDisabled proxy f =   bracket disable enable $ const f@@ -149,6 +262,15 @@     disable       = void . run $ updateProxy (proxyName proxy) disabledProxy     disabledProxy = proxy { proxyEnabled = False } +-- | Given a proxy and a toxic, create the toxic on the proxy, run the given action and+--   then delete the toxic.+--+--   This is useful for running some action with a toxic enabled.+--+-- @+-- withToxic myProxy latencyToxic $+--   sendRequestThroughProxy -- This request will have latency applied to it.+-- @ withToxic :: Proxy -> Toxic -> IO a -> IO a withToxic proxy toxic f =   bracket enable disable $ const f@@ -156,6 +278,11 @@     enable = void . run $ createToxic (proxyName proxy) toxic     disable = const . run $ deleteToxic (proxyName proxy) (toxicName toxic) +-- | Given a proxy record, create the proxy on the server, run the given action and then+--   delete the proxy off the server.+--+--   This is useful for wrapping 'withDisabled' and 'withToxic' calls. It enures that your+--   test cleans up the Toxiproxy server so that proxies don't leak into your other tests. withProxy :: Proxy -> (Proxy -> IO a) -> IO a withProxy proxy =   bracket create delete
test/Spec.hs view
@@ -64,8 +64,8 @@         let toxicName = "latency"         let toxic = Toxic               { toxicName       = toxicName-              , toxicType       = toxicName-              , toxicStream     = "upstream"+              , toxicType       = Latency+              , toxicStream     = Upstream               , toxicToxicity   = 1               , toxicAttributes = Map.fromList [("latency", 1000), ("jitter", 0)]               }@@ -113,8 +113,8 @@               }         let toxic = Toxic               { toxicName       = "latency"-              , toxicType       = "latency"-              , toxicStream     = "upstream"+              , toxicType       = Latency+              , toxicStream     = Upstream               , toxicToxicity   = 1               , toxicAttributes = Map.fromList [("latency", 1000), ("jitter", 0)]               }@@ -136,7 +136,7 @@     server = proc "toxiproxy-server" []  version :: Version-version = "git-fe6bf4f"+version = Version "git-fe6bf4f"  proxyUrl :: BaseUrl proxyUrl = BaseUrl Http "127.0.0.1" 4444 ""
toxiproxy-haskell.cabal view
@@ -2,10 +2,10 @@ -- -- see: https://github.com/sol/hpack ----- hash: 5cb73d2ae60bbc8465572da4b0035b5ad27b03f3c50e79f4af4eae94ab5783f3+-- hash: d0e71416ad274526a7e079fcae1e0d802defc1f7a0e5e9e03dcfd872153b1dd2  name:           toxiproxy-haskell-version:        0.1.0.0+version:        0.2.0.0 synopsis:       Client library for Toxiproxy: a TCP failure testing proxy. description:    Please see the README on Github at <https://github.com/jpittis/toxiproxy-haskell#readme> category:       web