instana-haskell-trace-sdk-0.1.0.0: test/agent-stub/Instana/SDK/AgentStub/Server.hs
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE OverloadedStrings #-}
module Instana.SDK.AgentStub.Server (mainServer) where
import Control.Monad.IO.Class (liftIO)
import Data.List (isPrefixOf)
import qualified Data.List as List
import Data.Maybe (fromMaybe)
import Data.STRef (modifySTRef,
readSTRef)
import Data.Time.Clock.POSIX (getPOSIXTime)
import Servant ((:<|>) (..), Header,
Headers,
NoContent (NoContent),
err404, err503)
import qualified Servant
import System.Log.Logger (debugM, warningM)
import Text.Read (readMaybe)
import Instana.SDK.AgentStub.API (API)
import Instana.SDK.AgentStub.Config (AgentStubConfig)
import qualified Instana.SDK.AgentStub.Config as Config
import Instana.SDK.AgentStub.DiscoveryRequest (DiscoveryRequest)
import qualified Instana.SDK.AgentStub.DiscoveryRequest as DiscoveryRequest
import Instana.SDK.AgentStub.DiscoveryResponse (DiscoveryResponse (DiscoveryResponse),
SecretsConfig (SecretsConfig))
import qualified Instana.SDK.AgentStub.DiscoveryResponse as DiscoveryResponse
import Instana.SDK.AgentStub.EntityDataRequest (EntityDataRequest)
import Instana.SDK.AgentStub.Logging (agentStubLogger)
import Instana.SDK.AgentStub.Recorders (Recorders)
import qualified Instana.SDK.AgentStub.Recorders as Recorders
import qualified Instana.SDK.AgentStub.StubServer as StubServer
import Instana.SDK.AgentStub.TraceRequest (TraceRequest)
import Instana.SDK.AgentStub.Util (shouldSimulateConnectionLoss,
stToServant)
mainServer ::
AgentStubConfig
-> Int
-> Recorders
-> Servant.Server API
mainServer config startupTime recorders =
getRoot config
:<|> putDiscovery config startupTime recorders
:<|> headAgentReady config startupTime recorders
:<|> postEntityData config startupTime recorders
:<|> postTrace config startupTime recorders
:<|> StubServer.stubServer recorders
getRoot ::
AgentStubConfig
-> Servant.Handler (Headers '[Header "Server" String] NoContent)
getRoot config =
return $ Servant.addHeader (Config.agentName config) NoContent
putDiscovery ::
AgentStubConfig
-> Int
-> Recorders
-> DiscoveryRequest
-> Servant.Handler DiscoveryResponse
putDiscovery config startupTime recorders discoveryRequest = do
now <- liftIO $ round . (* 1000) <$> getPOSIXTime
if shouldSimulateConnectionLoss config startupTime now
then do
liftIO $ debugM agentStubLogger $
"Rejecting PUT discovery to simulate connection loss."
Servant.throwError err503
else do
liftIO $
debugM agentStubLogger $ "PUT discovery " ++ show discoveryRequest
let
pidStr = DiscoveryRequest.pid discoveryRequest
pid = fromMaybe 0 $ readMaybe pidStr
translatedPid =
if Config.simulatPidTranslation config
then pid + 1
else pid
translatedDiscoveryRequest =
discoveryRequest { DiscoveryRequest.pid = show translatedPid }
stToServant $
modifySTRef
(Recorders.discoveryRecorder recorders)
((++) [translatedDiscoveryRequest])
return $
DiscoveryResponse
{ DiscoveryResponse.pid = translatedPid
, DiscoveryResponse.agentUuid = "agent-stub-id"
, DiscoveryResponse.extraHeaders = Nothing
, DiscoveryResponse.secrets =
SecretsConfig
{ DiscoveryResponse.matcher = "contains-ignore-case"
, DiscoveryResponse.list = ["key", "pass", "secret"]
}
}
headAgentReady ::
AgentStubConfig
-> Int
-> Recorders
-> String
-> Servant.Handler NoContent
headAgentReady
config
startupTime
recorders
pidString
= do
now <- liftIO $ round . (* 1000) <$> getPOSIXTime
if shouldSimulateConnectionLoss config startupTime now
then do
liftIO $ debugM agentStubLogger $
"Rejecting HEAD agent ready to simulate connection loss."
Servant.throwError err503
else do
liftIO $ debugM agentStubLogger $ "HEAD " ++ pidString
recordedDiscoveries <-
stToServant $ readSTRef $ Recorders.discoveryRecorder recorders
let
pidStrWithoutPrefix =
if isPrefixOf "com.instana.plugin.haskell." pidString
then drop 27 pidString else ""
knownPids = List.map DiscoveryRequest.pid recordedDiscoveries
pidHasBeenAnnounced = elem pidStrWithoutPrefix knownPids
if pidHasBeenAnnounced
then do
stToServant $
modifySTRef
(Recorders.agentReadyRecorder recorders)
((++) [pidStrWithoutPrefix])
return NoContent
else do
liftIO $ warningM agentStubLogger $
"Rejecting agent ready request for unannounced PID " ++
pidStrWithoutPrefix
Servant.throwError err404
postEntityData ::
AgentStubConfig
-> Int
-> Recorders
-> String
-> EntityDataRequest
-> Servant.Handler NoContent
postEntityData
config
startupTime
recorders
pidString
entityData
= do
now <- liftIO $ round . (* 1000) <$> getPOSIXTime
if shouldSimulateConnectionLoss config startupTime now
then do
liftIO $ debugM agentStubLogger $
"Rejecting POST entity data to simulate connection loss."
Servant.throwError err503
else do
liftIO $ debugM agentStubLogger $ "POST entity data " ++ pidString
recordedDiscoveries <-
stToServant $ readSTRef $ Recorders.discoveryRecorder recorders
let
pidStrWithoutPrefix =
if isPrefixOf "com.instana.plugin.haskell." pidString
then drop 27 pidString else ""
knownPids = List.map DiscoveryRequest.pid recordedDiscoveries
pidHasBeenAnnounced = elem pidStrWithoutPrefix knownPids
if pidHasBeenAnnounced
then do
liftIO $
debugM agentStubLogger $
"accepting entity data " ++ show entityData
stToServant $
modifySTRef
(Recorders.entityDataRecorder recorders)
((++) [entityData])
return NoContent
else do
liftIO $ warningM agentStubLogger $
"Rejecting entity data request for unannounced PID " ++
pidStrWithoutPrefix
Servant.throwError err404
postTrace ::
AgentStubConfig
-> Int
-> Recorders
-> String
-> TraceRequest
-> Servant.Handler NoContent
postTrace
config
startupTime
recorders
pidString
traceRequest = do
now <- liftIO $ round . (* 1000) <$> getPOSIXTime
if shouldSimulateConnectionLoss config startupTime now
then do
liftIO $ debugM agentStubLogger $
"Rejecting POST trace to simulate connection loss."
Servant.throwError err503
else do
liftIO $ debugM agentStubLogger $ "POST " ++ pidString ++
show traceRequest
recordedDiscoveries <-
stToServant $ readSTRef $ Recorders.discoveryRecorder recorders
let
pidStrWithoutTracesPrefix =
if isPrefixOf "traces." pidString then drop 7 pidString else ""
knownPids = List.map DiscoveryRequest.pid recordedDiscoveries
pidHasBeenAnnounced = elem pidStrWithoutTracesPrefix knownPids
if pidHasBeenAnnounced
then do
stToServant $
modifySTRef (Recorders.spanRecorder recorders) ((++) traceRequest)
return NoContent
else do
liftIO $ warningM agentStubLogger $
"Rejecting trace for unannounced PID " ++ pidStrWithoutTracesPrefix
Servant.throwError err404