instana-haskell-trace-sdk 0.10.1.0 → 0.10.2.0
raw patch · 8 files changed
+229/−53 lines, 8 filesdep +safePVP ok
version bump matches the API change (PVP)
Dependencies added: safe
API changes (from Hackage documentation)
Files
- CHANGELOG.md +3/−0
- README.md +1/−1
- instana-haskell-trace-sdk.cabal +6/−2
- src/Instana/SDK/Internal/AgentConnection/AgentHostLookup.hs +39/−31
- src/Instana/SDK/Internal/AgentConnection/DefaultGatewayIp.hs +71/−0
- test/integration/Instana/SDK/IntegrationTest/HttpHelper.hs +5/−5
- test/unit/Instana/SDK/Internal/AgentConnection/DefaultGatewayIpTest.hs +88/−0
- test/unit/Main.hs +16/−14
CHANGELOG.md view
@@ -1,5 +1,8 @@ # Changelog for instana-haskell-trace-sdk +## 0.10.2.0+- Remove dependency on sbin and awk in containers.+ ## 0.10.1.0 - Fix: Remove obsolete check for Server header when connecting to the Instana host agent.
README.md view
@@ -15,7 +15,7 @@ ``` extra-deps:-- instana-haskell-trace-sdk-0.10.1.0+- instana-haskell-trace-sdk-0.10.2.0 ``` Depending on the stack resolver you use, you might also need to add `aeson-extra` to your extra-deps:
instana-haskell-trace-sdk.cabal view
@@ -1,5 +1,5 @@ name: instana-haskell-trace-sdk-version: 0.10.1.0+version: 0.10.2.0 synopsis: SDK for adding custom Instana tracing support to Haskell applications. description: Please also see the README on Github at <https://github.com/instana/haskell-trace-sdk#readme> homepage: https://www.instana.com/@@ -47,13 +47,13 @@ , http-client-tls , http-types , network- , process , random , regex-base , regex-compat , regex-pcre , regex-tdfa , retry+ , safe , scientific , stm , sysinfo@@ -87,6 +87,7 @@ Instana.SDK.Internal.AgentConnection.AgentReady Instana.SDK.Internal.AgentConnection.Announce Instana.SDK.Internal.AgentConnection.ConnectLoop+ Instana.SDK.Internal.AgentConnection.DefaultGatewayIp Instana.SDK.Internal.AgentConnection.Json.AnnounceResponse Instana.SDK.Internal.AgentConnection.Json.Util Instana.SDK.Internal.AgentConnection.Paths@@ -168,6 +169,7 @@ , regex-compat , regex-pcre , regex-tdfa+ , safe , scientific , text , unordered-containers@@ -175,6 +177,7 @@ , wai other-modules: -- the actual tests+ Instana.SDK.Internal.AgentConnection.DefaultGatewayIpTest Instana.SDK.Internal.AgentConnection.SchedFileTest Instana.SDK.Internal.ConfigTest Instana.SDK.Internal.IdTest@@ -190,6 +193,7 @@ Instana.SDK.TracingHeadersTest -- modules under test Instana.SDK.Internal.Config+ Instana.SDK.Internal.AgentConnection.DefaultGatewayIp Instana.SDK.Internal.AgentConnection.SchedFile Instana.SDK.Internal.Id Instana.SDK.Internal.Logging
src/Instana/SDK/Internal/AgentConnection/AgentHostLookup.hs view
@@ -10,26 +10,23 @@ ) where -import qualified Control.Concurrent.STM as STM-import Control.Exception (SomeException,- catch)-import Data.Char (isSpace)-import qualified Data.List as List-import qualified Network.HTTP.Client as HTTP-import qualified Network.HTTP.Types.Status as Status-import System.Exit (ExitCode (ExitSuccess))-import System.Log.Logger (debugM)-import System.Process as Process+import qualified Control.Concurrent.STM as STM+import Control.Exception (SomeException,+ catch)+import qualified Network.HTTP.Client as HTTP+import qualified Network.HTTP.Types.Status as Status+import System.Log.Logger (debugM) -import qualified Instana.SDK.Internal.AgentConnection.Announce as Announce-import Instana.SDK.Internal.AgentConnection.ProcessInfo (ProcessInfo)-import qualified Instana.SDK.Internal.Config as InternalConfig-import Instana.SDK.Internal.Context (ConnectionState (..),- InternalContext)-import qualified Instana.SDK.Internal.Context as InternalContext-import Instana.SDK.Internal.Logging (instanaLogger)-import qualified Instana.SDK.Internal.Retry as Retry-import qualified Instana.SDK.Internal.URL as URL+import qualified Instana.SDK.Internal.AgentConnection.Announce as Announce+import Instana.SDK.Internal.AgentConnection.DefaultGatewayIp (extractDefaultGatewayIp)+import Instana.SDK.Internal.AgentConnection.ProcessInfo (ProcessInfo)+import qualified Instana.SDK.Internal.Config as InternalConfig+import Instana.SDK.Internal.Context (ConnectionState (..),+ InternalContext)+import qualified Instana.SDK.Internal.Context as InternalContext+import Instana.SDK.Internal.Logging (instanaLogger)+import qualified Instana.SDK.Internal.Retry as Retry+import qualified Instana.SDK.Internal.URL as URL type SuccessfullHost = (String, Int)@@ -143,15 +140,26 @@ readDefaultGateway :: IO (Either String String) readDefaultGateway = do let- cmd = "/sbin/ip route | awk '/default/ { print $3 }'"- stdIn = ""- (exitCode, stdOut, stdErr) <-- Process.readCreateProcessWithExitCode (Process.shell cmd) stdIn- if exitCode /= ExitSuccess || stdErr /= ""- then- return $ Left $ "Failed to retrieve default gateway: " ++ stdErr- else- return $ Right $ trim stdOut- where- trim = List.dropWhileEnd isSpace . dropWhile isSpace-+ routeFilePath = "/proc/self/net/route"+ catch+ ( do+ routeFileContent <- readFile routeFilePath+ let+ defaultGatewayIpM = extractDefaultGatewayIp routeFileContent+ case defaultGatewayIpM of+ Just defaultGatewayIp -> do+ debugM instanaLogger $+ "Determined default gateway IP: " ++ defaultGatewayIp+ return $ Right defaultGatewayIp+ Nothing ->+ return $+ Left $+ "Failed to parse default gateway IP from " ++ routeFilePath ++ "."+ )+ (\(e :: SomeException) -> do+ return $+ Left $+ "Failed to read " ++ routeFilePath +++ " to determine the default gateway IP: " +++ show e+ )
+ src/Instana/SDK/Internal/AgentConnection/DefaultGatewayIp.hs view
@@ -0,0 +1,71 @@+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-|+Module : Instana.SDK.Internal.AgentConnection.DefaultGatewayIp+Description : Extracts the default gateway IP from the content of /proc/self/net/route.+-}+module Instana.SDK.Internal.AgentConnection.DefaultGatewayIp+ ( extractDefaultGatewayIp+ ) where++import Control.Monad (join)+import qualified Data.List as List+import Data.Text (Text)+import qualified Data.Text as T+import Numeric (readHex)+import Safe (atMay)+++-- |Parses the content of /proc/self/net/route to retrieve the IP of the default+-- gateway.+extractDefaultGatewayIp :: String -> Maybe String+extractDefaultGatewayIp routeFileContent =+ let+ allLines = lines routeFileContent+ linesAsFields = map (\l -> T.splitOn "\t" $ T.pack l) allLines+ lineWithDefaultGateway = List.find isDefaultGatewayLine linesAsFields+ ip = join $ fmap extractIpFromFields lineWithDefaultGateway+ in+ fmap T.unpack ip+++isDefaultGatewayLine :: [Text] -> Bool+isDefaultGatewayLine fields =+ let+ field2 = atMay fields 1+ lenField3 = fmap T.length $ atMay fields 2+ in+ length fields >= 3 &&+ field2 == Just "00000000" &&+ lenField3 == Just 8+++extractIpFromFields :: [Text] -> Maybe Text+extractIpFromFields fields =+ fmap convertHexStringToIp $ atMay fields 2+++convertHexStringToIp :: Text -> Text+convertHexStringToIp hexstring =+ let+ o1 = substringLength2 6 hexstring+ o2 = substringLength2 4 hexstring+ o3 = substringLength2 2 hexstring+ o4 = T.take 2 hexstring+ in+ T.intercalate "." $ map hexstringToDecimal [o1, o2, o3, o4]+++substringLength2 :: Int -> Text -> Text+substringLength2 start =+ T.take 2 . T.drop start+++hexstringToDecimal :: Text -> Text+hexstringToDecimal hexstring =+ let+ result = readHex $ T.unpack hexstring+ in+ case result of+ (x,_):_ -> T.pack $ show (x :: Int)+ _ -> "0"
test/integration/Instana/SDK/IntegrationTest/HttpHelper.hs view
@@ -45,15 +45,15 @@ -- agent to connect to with fibonacci backoff). -- 3) When this happens, the first attempt to talk to 127.0.0.1:1302 fails. -- 4) Next, the SDK attempts to talk to an agent over the default gateway.--- 5) This never happens locally (at least not on MacOS), as there is no--- /sbin/ip executable present--- 6) On Travis (or more generally on most Linux systems), the SDK actually--- tries the default gateway because that executable _is_ present. It will+-- 5) This never happens locally (at least not on MacOS), since the file+-- /proc/self/net/route does not exist.+-- 6) On CircleCI (or more generally on most Linux systems), the SDK actually+-- tries the default gateway because that file _is_ present. It will -- also find a default gateway (let's say, for example, 10.20.0.1). -- 7) That host is reachable so an HTTP request to 10.20.0.1:1302 is attempted -- (1302 is the configured agent port in the integration tests). -- 8) If something exists at 10.20.0.1:1302 it never sends a response. Or there--- is nothing there but somehow the request does not fail immediately.+-- is nothing there but the request only fails after the connection timeout. -- Either way, trying this HTTP request eats up around 5 seconds. Why 5 -- seconds? 5 seconds is: -- a) the timeout the SDK sets itself (see Instana/SDK/SDK.hs, value for
+ test/unit/Instana/SDK/Internal/AgentConnection/DefaultGatewayIpTest.hs view
@@ -0,0 +1,88 @@+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE OverloadedStrings #-}+module Instana.SDK.Internal.AgentConnection.DefaultGatewayIpTest (allTests) where+++import Test.HUnit++import Instana.SDK.Internal.AgentConnection.DefaultGatewayIp (extractDefaultGatewayIp)+++allTests :: Test+allTests =+ TestList+ [ TestLabel "shouldExtractDefaultGatewayIpFromFile" shouldExtractDefaultGatewayIpFromFile+ , TestLabel "shouldNotExtractFromEmptyFile" shouldNotExtractFromEmptyFile+ , TestLabel "shouldNotExtractFromLinesWithTooFewFields" shouldNotExtractFromLinesWithTooFewFields+ , TestLabel "shouldNotExtractIfNoLineHas8Zeroes" shouldNotExtractIfNoLineHas8Zeroes+ , TestLabel "shouldNotExtractIfThirdFieldHasWrongLength" shouldNotExtractIfThirdFieldHasWrongLength+ , TestLabel "shouldCopeWithThirdFieldNotAHexString" shouldCopeWithThirdFieldNotAHexString+ ]+++shouldExtractDefaultGatewayIpFromFile :: Test+shouldExtractDefaultGatewayIpFromFile =+ let+ defaultGatewayIp =+ extractDefaultGatewayIp $+ "Iface\tDestination\tGateway\tFlags\tRefCnt\tUse\tMetricMask\tMTU\tWindow\tIRTT\n" +++ "eth0\t0000F1AC\t00000000\t0001\t0\t0\t0\t0000FFFF0\t0\t0\n" +++ "eth0\t00000000\t010011AC\t0003\t0\t0\t0\t000000000\t0\t0\n" +++ "eth0\t000011AC\t00000000\t0001\t0\t0\t0\t0000FFFF0\t0\t0"+ in+ TestCase $+ assertEqual "default gateway IP" (Just "172.17.0.1") defaultGatewayIp+++shouldNotExtractFromEmptyFile :: Test+shouldNotExtractFromEmptyFile =+ let+ defaultGatewayIp = extractDefaultGatewayIp ""+ in+ TestCase $+ assertEqual "default gateway IP" Nothing defaultGatewayIp+++shouldNotExtractFromLinesWithTooFewFields :: Test+shouldNotExtractFromLinesWithTooFewFields =+ let+ defaultGatewayIp =+ extractDefaultGatewayIp $+ "Iface\tDestination\n" +++ "eth0\t00000000"+ in+ TestCase $+ assertEqual "default gateway IP" Nothing defaultGatewayIp+++shouldNotExtractIfNoLineHas8Zeroes :: Test+shouldNotExtractIfNoLineHas8Zeroes =+ let+ defaultGatewayIp =+ extractDefaultGatewayIp $+ "eth0\t00009000\t010011AC\t0003\t0\t0\t0\t000000000\t0\t0"+ in+ TestCase $+ assertEqual "default gateway IP" Nothing defaultGatewayIp+++shouldNotExtractIfThirdFieldHasWrongLength :: Test+shouldNotExtractIfThirdFieldHasWrongLength =+ let+ defaultGatewayIp =+ extractDefaultGatewayIp $+ "eth0\t00000000\t010011A\t0003\t0\t0\t0\t000000000\t0\t0"+ in+ TestCase $+ assertEqual "default gateway IP" Nothing defaultGatewayIp+++shouldCopeWithThirdFieldNotAHexString :: Test+shouldCopeWithThirdFieldNotAHexString =+ let+ defaultGatewayIp =+ extractDefaultGatewayIp $+ "eth0\t00000000\tghijklmn\t0003\t0\t0\t0\t000000000\t0\t0"+ in+ TestCase $+ assertEqual "default gateway IP" (Just "0.0.0.0") defaultGatewayIp
test/unit/Main.hs view
@@ -1,19 +1,20 @@ module Main where -import qualified Instana.SDK.Internal.AgentConnection.SchedFileTest as SchedFileTest-import qualified Instana.SDK.Internal.ConfigTest as ConfigTest-import qualified Instana.SDK.Internal.IdTest as IdTest-import qualified Instana.SDK.Internal.LoggingTest as LoggingTest-import qualified Instana.SDK.Internal.Metrics.CompressionTest as MetricsCompressionTest-import qualified Instana.SDK.Internal.Metrics.DeltasTest as MetricsDeltasTest-import qualified Instana.SDK.Internal.SecretsTest as SecretsTest-import qualified Instana.SDK.Internal.ServerTimingTest as ServerTimingTest-import qualified Instana.SDK.Internal.SpanStackTest as SpanStackTest-import qualified Instana.SDK.Internal.W3CTraceContextTest as W3CTraceContextTest-import qualified Instana.SDK.SpanDataTest as SpanDataTest-import qualified Instana.SDK.SpanTest as SpanTest-import qualified Instana.SDK.TracingHeadersTest as TracingHeadersTest+import qualified Instana.SDK.Internal.AgentConnection.DefaultGatewayIpTest as DefaultGatewayIpTest+import qualified Instana.SDK.Internal.AgentConnection.SchedFileTest as SchedFileTest+import qualified Instana.SDK.Internal.ConfigTest as ConfigTest+import qualified Instana.SDK.Internal.IdTest as IdTest+import qualified Instana.SDK.Internal.LoggingTest as LoggingTest+import qualified Instana.SDK.Internal.Metrics.CompressionTest as MetricsCompressionTest+import qualified Instana.SDK.Internal.Metrics.DeltasTest as MetricsDeltasTest+import qualified Instana.SDK.Internal.SecretsTest as SecretsTest+import qualified Instana.SDK.Internal.ServerTimingTest as ServerTimingTest+import qualified Instana.SDK.Internal.SpanStackTest as SpanStackTest+import qualified Instana.SDK.Internal.W3CTraceContextTest as W3CTraceContextTest+import qualified Instana.SDK.SpanDataTest as SpanDataTest+import qualified Instana.SDK.SpanTest as SpanTest+import qualified Instana.SDK.TracingHeadersTest as TracingHeadersTest import Test.HUnit @@ -27,6 +28,7 @@ allTests = TestList [ ConfigTest.allTests+ , DefaultGatewayIpTest.allTests , IdTest.allTests , LoggingTest.allTests , MetricsCompressionTest.allTests@@ -34,9 +36,9 @@ , SchedFileTest.allTests , SecretsTest.allTests , ServerTimingTest.allTests+ , SpanDataTest.allTests , SpanStackTest.allTests , SpanTest.allTests- , SpanDataTest.allTests , TracingHeadersTest.allTests , W3CTraceContextTest.allTests ]