diff --git a/cardano-testnet/.changes/20261006_160204_cardano-testnet_sandy.yml b/cardano-testnet/.changes/20261006_160204_cardano-testnet_sandy.yml new file mode 100644 index 00000000000..bb85308bc77 --- /dev/null +++ b/cardano-testnet/.changes/20261006_160204_cardano-testnet_sandy.yml @@ -0,0 +1,5 @@ +description: Support --prometheus-listen-address and --prometheus-listen-port +kind: +- feature +pr: 6723 +project: cardano-testnet diff --git a/cardano-testnet/src/Parsers/Cardano.hs b/cardano-testnet/src/Parsers/Cardano.hs index 9ef95310566..ce3ae2d2577 100644 --- a/cardano-testnet/src/Parsers/Cardano.hs +++ b/cardano-testnet/src/Parsers/Cardano.hs @@ -23,6 +23,7 @@ import Data.IP (IP) import qualified Data.List as L import Data.List.NonEmpty (NonEmpty ((:|))) import Data.Maybe +import Data.String (fromString) import Data.Word (Word64) import Network.Socket (PortNumber) import Options.Applicative (CommandFields, Mod, Parser) @@ -152,11 +153,28 @@ portReader = OA.eitherReader $ \token -> do pure $ fromIntegral port pEnableTracer :: Parser TraceSupport -pEnableTracer = OA.flag TraceDisabled TraceEnabled - ( OA.long "enable-tracer" - <> OA.help "[EXPERIMENTAL] Enable cardano-tracer support on all of testnet nodes. Looks for cardano-tracer via the environment variable CARDANO_TRACER, falling back to looking into PATH. This will start a Prometheus server at 127.0.0.1, on a port that will be reported at runtime." - <> OA.showDefault - ) +pEnableTracer = OA.asum + [ OA.flag' () + ( OA.long "enable-tracer" + <> OA.help "[EXPERIMENTAL] Enable cardano-tracer support on all of testnet nodes. Looks for cardano-tracer via the environment variable CARDANO_TRACER, falling back to looking into PATH. This will start a Prometheus server at 127.0.0.1 and a random port unless --prometheus-listen-address and --prometheus-listen-port are given." + ) + *> (TraceEnabled + <$> OA.option ipReader + ( OA.long "prometheus-listen-address" + <> OA.metavar "IP" + <> OA.value (fromString "127.0.0.1") + <> OA.showDefault + <> OA.help "IP address the Prometheus server will listen on. Requires --enable-tracer." + ) + <*> OA.optional + (OA.option portReader + ( OA.long "prometheus-listen-port" + <> OA.metavar "PORT" + <> OA.help "Base port for Prometheus. A random free port is used when omitted. Requires --enable-tracer." + ) + )) + , pure TraceDisabled + ] pKesSource :: Parser PraosCredentialsSource pKesSource = OA.flag UseKesKeyFile UseKesSocket diff --git a/cardano-testnet/src/Testnet/CardanoTracer.hs b/cardano-testnet/src/Testnet/CardanoTracer.hs index f34aab0c73e..04fa177bcb8 100644 --- a/cardano-testnet/src/Testnet/CardanoTracer.hs +++ b/cardano-testnet/src/Testnet/CardanoTracer.hs @@ -1,5 +1,5 @@ {-# LANGUAGE DataKinds #-} -{-# LANGUAGE DisambiguateRecordFields #-} +{-# LANGUAGE DuplicateRecordFields #-} {-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE NumericUnderscores #-} {-# LANGUAGE OverloadedStrings #-} @@ -26,6 +26,7 @@ import Control.Monad.Trans.Except (runExceptT) import Control.Monad.Trans.Resource (MonadResource) import Data.Aeson (encodeFile) import Data.List.NonEmpty (NonEmpty(..)) +import Data.IP (IP) import GHC.Stack (HasCallStack) import qualified GHC.Stack as GHC import Network.Socket (PortNumber) @@ -46,18 +47,20 @@ import RIO (runRIO, throwString, unless) -- | Configuration record for invoking 'startCardanoTracer'. data CardanoTracerConf = CardanoTracerConf { tempAbsPath :: FilePath -- ^ Path to the testnet's temp directory. + , prometheusIP :: IP -- ^ Attempt to bind prometheus on this IP. + , prometheusPort :: Maybe PortNumber -- ^ Attempt to bind prometheus to the port, if given. Choose a random port otherwise. , testnetMagic :: Int -- ^ The magic number for the testnet. , logFormat :: LogFormat -- ^ The format for logs produced by cardano-tracer. } deriving (Eq, Show) mkConfig :: CardanoTracerConf -> Int -> FilePath -> Sprocket -> TracerConfig -mkConfig CardanoTracerConf { testnetMagic, logFormat } port logFile sprocket = TracerConfig +mkConfig CardanoTracerConf { testnetMagic, logFormat, prometheusIP } port logFile sprocket = TracerConfig { networkMagic = fromIntegral testnetMagic , network = AcceptAt $ LocalPipe $ sprocketArgumentName sprocket , loRequestNum = Nothing , ekgRequestFreq = Nothing , hasEKG = Nothing - , hasPrometheus = Just $ Endpoint "127.0.0.1" port $ Just False + , hasPrometheus = Just $ Endpoint (show prometheusIP) port $ Just False , hasTimeseries = Nothing , tlsCertificate = Nothing , hasForwarding = Nothing @@ -91,7 +94,7 @@ startCardanoTracer => MonadCatch m => CardanoTracerConf -> m CardanoTracerRuntime -startCardanoTracer conf@CardanoTracerConf{tempAbsPath} = GHC.withFrozenCallStack $ do +startCardanoTracer conf@CardanoTracerConf{tempAbsPath, prometheusPort = mPort} = GHC.withFrozenCallStack $ do let tmpPath = TmpAbsolutePath tempAbsPath logDir = makeLogDir tmpPath tempBaseAbsPath = makeTmpBaseAbsPath tmpPath @@ -108,16 +111,21 @@ startCardanoTracer conf@CardanoTracerConf{tempAbsPath} = GHC.withFrozenCallStack hNodeStdout <- liftIO $ IO.openFile nodeStdoutFile IO.WriteMode hNodeStderr <- liftIO $ IO.openFile nodeStderrFile IO.WriteMode - [prometheusPortNo] <- liftIO $ IO.allocateRandomPorts 1 - let prometheusPort = fromIntegral prometheusPortNo - -- The port number if it is obtained using 'H.randomPort', it is firstly bound to and then closed. The closing - -- and release in the operating system is done asynchronously and can be slow. Here we wait until the port - let portWaitTimeout = 45 - isClosed <- liftIOAnnotated $ Ping.waitForPortClosed portWaitTimeout 0.1 prometheusPort - unless isClosed $ - throwString $ "Port is still in use after " ++ show portWaitTimeout ++ " seconds before starting tracer: " <> show prometheusPortNo - liftIO $ encodeFile configFile $ mkConfig conf prometheusPortNo logDir sprocket + prometheusPort <- + case mPort of + Just port -> pure port + Nothing -> do + [prometheusPortNo] <- liftIO $ IO.allocateRandomPorts 1 + let prometheusPort = fromIntegral prometheusPortNo + -- The port number if it is obtained using 'H.randomPort', it is firstly bound to and then closed. The closing + -- and release in the operating system is done asynchronously and can be slow. Here we wait until the port + isClosed <- liftIOAnnotated $ Ping.waitForPortClosed portWaitTimeout 0.1 prometheusPort + unless isClosed $ + throwString $ "Port is still in use after " ++ show portWaitTimeout ++ " seconds before starting tracer: " <> show prometheusPort + pure prometheusPort + + liftIO $ encodeFile configFile $ mkConfig conf (fromIntegral prometheusPort) logDir sprocket cp <- runRIO () $ procFlex "cardano-tracer" "CARDANO_TRACER" [ "--config", configFile diff --git a/cardano-testnet/src/Testnet/Start/Cardano.hs b/cardano-testnet/src/Testnet/Start/Cardano.hs index f04fce4c4fe..9bfed5d9904 100644 --- a/cardano-testnet/src/Testnet/Start/Cardano.hs +++ b/cardano-testnet/src/Testnet/Start/Cardano.hs @@ -289,10 +289,12 @@ cardanoTestnet -- alongside the nodes (see 'interruptNodesOnSigINT' below). (nodeConfigFile', mTracer) <- case cardanoEnableTracer of TraceDisabled -> pure (nodeConfigFile, Nothing) - TraceEnabled -> do + TraceEnabled ip mport -> do cfgFile' <- liftIOAnnotated $ enableTraceForwarding nodeConfigFile tracerRuntime <- startCardanoTracer $ CardanoTracerConf { tempAbsPath = tmpAbsPath + , prometheusIP = ip + , prometheusPort = mport , testnetMagic = testnetMagic , logFormat = ForMachine } diff --git a/cardano-testnet/src/Testnet/Start/Types.hs b/cardano-testnet/src/Testnet/Start/Types.hs index 093a7b41b1f..71b2f648fe5 100644 --- a/cardano-testnet/src/Testnet/Start/Types.hs +++ b/cardano-testnet/src/Testnet/Start/Types.hs @@ -188,7 +188,12 @@ instance Default RpcHttpOptions where data TraceSupport = TraceDisabled -- ^ Do not enable tracing - | TraceEnabled -- ^ Enable tracing over cardano-tracer + | TraceEnabled + -- ^ Enable tracing over cardano-tracer. + IP + -- ^ Bind prometheus on this IP. + (Maybe PortNumber) + -- ^ Using this port for prometheus, when given. Choose randomly otherwise. deriving (Eq, Show) -- | Options for creating a testnet environment (genesis files, topology, ports). diff --git a/cardano-testnet/test/cardano-testnet-golden/files/golden/help.cli b/cardano-testnet/test/cardano-testnet-golden/files/golden/help.cli index c49db139af6..12a97b9e81b 100644 --- a/cardano-testnet/test/cardano-testnet-golden/files/golden/help.cli +++ b/cardano-testnet/test/cardano-testnet-golden/files/golden/help.cli @@ -16,7 +16,9 @@ Usage: cardano-testnet cardano [ --enable-grpc | --enable-grpc-http [--grpc-listen-address IP] [--grpc-listen-port-base PORT] ] - [--enable-tracer] + [--enable-tracer + [--prometheus-listen-address IP] + [--prometheus-listen-port PORT]] [--use-kes-agent] [--disable-chain-stall-watchdog] diff --git a/cardano-testnet/test/cardano-testnet-golden/files/golden/help/cardano.cli b/cardano-testnet/test/cardano-testnet-golden/files/golden/help/cardano.cli index babfaa22a62..b20abec39fa 100644 --- a/cardano-testnet/test/cardano-testnet-golden/files/golden/help/cardano.cli +++ b/cardano-testnet/test/cardano-testnet-golden/files/golden/help/cardano.cli @@ -14,7 +14,9 @@ Usage: cardano-testnet cardano [ --enable-grpc | --enable-grpc-http [--grpc-listen-address IP] [--grpc-listen-port-base PORT] ] - [--enable-tracer] + [--enable-tracer + [--prometheus-listen-address IP] + [--prometheus-listen-port PORT]] [--use-kes-agent] [--disable-chain-stall-watchdog] @@ -81,8 +83,15 @@ Available options: of testnet nodes. Looks for cardano-tracer via the environment variable CARDANO_TRACER, falling back to looking into PATH. This will start a Prometheus - server at 127.0.0.1, on a port that will be reported - at runtime. + server at 127.0.0.1 and a random port unless + --prometheus-listen-address and + --prometheus-listen-port are given. + --prometheus-listen-address IP + IP address the Prometheus server will listen on. + Requires --enable-tracer. (default: 127.0.0.1) + --prometheus-listen-port PORT + Base port for Prometheus. A random free port is used + when omitted. Requires --enable-tracer. --use-kes-agent Get Praos block forging credentials from kes-agent via the default socket path --disable-chain-stall-watchdog diff --git a/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/EnableTracer.hs b/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/EnableTracer.hs index 96806e6dba2..e7f2f1fab4a 100644 --- a/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/EnableTracer.hs +++ b/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/EnableTracer.hs @@ -34,7 +34,7 @@ hprop_enable_tracer :: H.Property hprop_enable_tracer = integrationRetryWorkspace 2 "enable-tracer" $ \tmpDir -> H.runWithDefaultWatchdog_ $ do let creationOptions = def - runtimeOptions = def { runtimeEnableTracer = TraceEnabled } + runtimeOptions = def { runtimeEnableTracer = TraceEnabled "127.0.0.1" Nothing } conf <- mkConf tmpDir runtime <- createAndRunTestnet creationOptions runtimeOptions conf