Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cluster fixes #155

Closed
wants to merge 6 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 8 additions & 8 deletions plutip-server/Api/Handlers.hs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import Cardano.Api (serialiseToCBOR)
import Cardano.Launcher.Node (nodeSocketFile)
import Test.Plutip.Tools.CardanoApi qualified as Tools

import Control.Concurrent.MVar (isEmptyMVar, putMVar, takeMVar)
import Control.Concurrent.MVar (isEmptyMVar, putMVar, tryTakeMVar)
import Control.Monad (unless)
import Control.Monad.Except (runExceptT, throwError)
import Control.Monad.Extra (unlessM)
Expand Down Expand Up @@ -83,15 +83,15 @@ startClusterHandler
let extraConf = ExtraConfig slotLength epochSize
cfg = def {relayNodeLogs = nodeLogs, chainIndexMode = NotNeeded, extraConfig = extraConf}

(statusTVar, res@(clusterEnv, _)) <- liftIO $ startCluster cfg setup
(statusTVar, (clusterEnv, wallets)) <- liftIO $ startCluster cfg setup
liftIO $ putMVar statusMVar statusTVar
let nodeConfigPath = getNodeConfigFile clusterEnv
-- safeguard against directory tree structure changes
unlessM (liftIO $ doesFileExist nodeConfigPath) $ throwError NodeConfigNotFound
pure $
ClusterStartupSuccess $
ClusterStartupParameters
{ privateKeys = getWalletPrivateKey <$> snd res
{ privateKeys = getWalletPrivateKey <$> wallets
, nodeSocketPath = getNodeSocketFile clusterEnv
, nodeConfigPath = nodeConfigPath
, keysDirectory = keysDir clusterEnv
Expand All @@ -110,6 +110,7 @@ startClusterHandler
getNodeConfigFile =
-- assumption is that node.config lies in the same directory as node.socket
flip replaceFileName "node.config" . getNodeSocketFile

getWalletPrivateKey :: BpiWallet -> PrivateKey
getWalletPrivateKey = Text.decodeUtf8 . Base16.encode . serialiseToCBOR . signKey
interpret = fmap (either ClusterStartupFailure id) . runExceptT
Expand All @@ -123,10 +124,9 @@ startClusterHandler
stopClusterHandler :: StopClusterRequest -> AppM StopClusterResponse
stopClusterHandler StopClusterRequest = do
statusMVar <- asks status
isClusterDown <- liftIO $ isEmptyMVar statusMVar
if isClusterDown
then pure $ StopClusterFailure "Cluster is not running"
else do
statusTVar <- liftIO $ takeMVar statusMVar
maybeClusterStatus <- liftIO $ tryTakeMVar statusMVar
case maybeClusterStatus of
Nothing -> pure $ StopClusterFailure "Cluster is not running"
Just statusTVar -> do
liftIO $ stopCluster statusTVar
pure StopClusterSuccess
4 changes: 1 addition & 3 deletions plutip-server/Types.hs
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,7 @@ import Data.Time (NominalDiffTime)
import GHC.Generics (Generic)
import Network.Wai.Handler.Warp (Port)
import Test.Plutip.Internal.BotPlutusInterface.Wallet (BpiWallet)
import Test.Plutip.Internal.LocalCluster (
ClusterStatus,
)
import Test.Plutip.Internal.LocalCluster (ClusterStatus)
import Test.Plutip.Internal.Types (ClusterEnv)
import UnliftIO.STM (TVar)

Expand Down
13 changes: 6 additions & 7 deletions src/Test/Plutip/Internal/ChainIndex.hs
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,7 @@ import Cardano.BM.Configuration.Model qualified as CM
import Cardano.BM.Data.Severity qualified as Severity
import Cardano.Launcher.Node (nodeSocketFile)

import Control.Concurrent.Async (async)
import Control.Monad (void)
import Control.Concurrent.Async (Async, async)
import Control.Retry (constantDelay, limitRetries, recoverAll)
import Plutus.ChainIndex.App qualified as ChainIndex
import Plutus.ChainIndex.Config qualified as ChainIndex
Expand Down Expand Up @@ -48,7 +47,7 @@ handleChainIndexLaunch ::
ChainIndexMode ->
RunningNode ->
FilePath ->
IO (Maybe ChainIndexPort)
IO (Maybe (ChainIndexPort, Async ()))
handleChainIndexLaunch mode rn dir = do
maybePort <-
case mode of
Expand All @@ -57,15 +56,15 @@ handleChainIndexLaunch mode rn dir = do
CustomPort port' -> do
Just <$> launchChainIndex (fromEnum port') rn dir
NotNeeded -> pure Nothing
reportLaunch maybePort
reportLaunch $ fst <$> maybePort
pure maybePort
where
reportLaunch = \case
Just p -> putStrLn $ "Chain index started at port " <> show p
_ -> pure ()

-- | Launch the chain index in a separate thread.
launchChainIndex :: Int -> RunningNode -> FilePath -> IO Int
launchChainIndex :: Int -> RunningNode -> FilePath -> IO (Int, Async ())
launchChainIndex port (RunningNode sp _block0 (netParams, _vData) _) dir = do
let (NetworkParameters _ (SlottingParameters (SlotLength slotLen) _ _ _) _) = netParams

Expand All @@ -80,9 +79,9 @@ launchChainIndex port (RunningNode sp _block0 (netParams, _vData) _) dir = do
& CIC.port .~ port
& CIC.slotConfig .~ (def {scSlotLength = toMilliseconds slotLen})

void $ async $ void $ ChainIndex.runMainWithLog (const $ return ()) config chainIndexConfig
running <- async $ ChainIndex.runMainWithLog (const $ return ()) config chainIndexConfig
waitForChainIndex
return $ chainIndexConfig ^. CIC.port
return (chainIndexConfig ^. CIC.port, running)
where
toMilliseconds = floor . (1e3 *) . nominalDiffTimeToSeconds

Expand Down
35 changes: 25 additions & 10 deletions src/Test/Plutip/Internal/LocalCluster.hs
Original file line number Diff line number Diff line change
Expand Up @@ -15,18 +15,20 @@ import Cardano.Api qualified as CAPI
import Cardano.BM.Data.Severity qualified as Severity
import Cardano.BM.Data.Tracer (HasPrivacyAnnotation, HasSeverityAnnotation (getSeverityAnnotation))
import Cardano.CLI (LogOutput (LogToFile), withLoggingNamed)
import Cardano.Launcher (ProcessHasExited (ProcessHasExited))
import Cardano.Startup (installSignalHandlers, setDefaultFilePermissions, withUtf8Encoding)
import Cardano.Wallet.Logging (stdoutTextTracer, trMessageText)
import Cardano.Wallet.Shelley.Launch (TempDirLog, withSystemTempDir)

-- import Cardano.Wallet.Shelley.Launch.Cluster (ClusterLog, localClusterConfigFromEnv, testMinSeverityFromEnv, walletMinSeverityFromEnv, withCluster)

import Control.Concurrent.Async (cancel)
import Control.Monad (unless, void, when)
import Control.Monad.IO.Class (liftIO)
import Control.Monad.IO.Unlift (MonadUnliftIO)
import Control.Monad.Reader (ReaderT (runReaderT))
import Control.Retry (constantDelay, limitRetries, recoverAll)
import Control.Retry (constantDelay, limitRetries, logRetries, recoverAll, recovering)
import Control.Tracer (Tracer, contramap, traceWith)
import Data.ByteString.Char8 qualified as B
import Data.Foldable (for_)
import Data.Kind (Type)
import Data.Maybe (catMaybes, fromMaybe, isJust)
Expand All @@ -47,7 +49,7 @@ import System.Directory (
import System.Environment (setEnv)
import System.Exit (die)
import System.FilePath ((</>))
import System.IO (IOMode (WriteMode), hClose, openFile, stdout)
import System.IO (IOMode (WriteMode), hClose, openFile, stderr, stdout)
import Test.Plutip.Config (
PlutipConfig (
chainIndexMode,
Expand Down Expand Up @@ -128,22 +130,25 @@ withPlutusInterface :: forall (a :: Type). PlutipConfig -> (ClusterEnv -> IO a)
withPlutusInterface conf action = do
-- current setup requires `cardano-node` and `cardano-cli` as external processes
checkProcessesAvailable ["cardano-node", "cardano-cli"]

withLocalClusterSetup conf $ \dir clusterLogs _walletLogs nodeConfigLogHdl -> do
result <- withLoggingNamed "cluster" clusterLogs $ \(_, (_, trCluster)) -> do
let tr' = contramap MsgCluster $ trMessageText trCluster
clusterCfg <- localClusterConfigWithExtraConf (extraConfig conf)
withRedirectedStdoutHdl nodeConfigLogHdl $ \restoreStdout ->
withCluster tr' dir clusterCfg mempty $ \rn -> do
restoreStdout $ runActionWthSetup rn dir trCluster action
retryClusterFailedStartup $
withCluster tr' dir clusterCfg mempty $ \rn -> do
restoreStdout $ runActionWthSetup rn dir trCluster action
handleLogs dir conf
return result
where
runActionWthSetup rn dir trCluster userActon = do
runActionWthSetup rn dir trCluster userAction = do
let tracer' = trMessageText trCluster
waitForRelayNode tracer' rn
maybePort <- handleChainIndexLaunch (chainIndexMode conf) rn dir
let cEnv =
mChainStarted <- handleChainIndexLaunch (chainIndexMode conf) rn dir
Copy link
Collaborator

@mikekeke mikekeke Nov 28, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

An IMHO, but this stuff starting to get a bit involved. I think because of the way handleChainIndexLaunch is done.

Maybe do something like

getChIndexPort :: ChainIndexMode -> Maybe ChainIndexPort

and then clearly branch on it like

mPort = getChIndexPort (chainIndexMode conf)
case mPort of
  Just port -> bracket (startIndex port rn dir) stopIndex (<make cEnv and run setup and actions>)
  -- or
  -- Just port -> withChainIndex port rn dir (<make cEnv and run setup and actions>)
  Nothing ->  (<make cEnv and run setup and actions>)

or something like that, and trash handleChainIndexLaunch

let maybePort = fst <$> mChainStarted
maybeRunning = snd <$> mChainStarted
maybeCancelChainIndex = maybe id (\chain io -> io `finally` cancel chain) maybeRunning
cEnv =
ClusterEnv
{ runningNode = rn
, chainIndexUrl = (\p -> BaseUrl Http "localhost" p mempty) <$> maybePort
Expand All @@ -154,7 +159,17 @@ withPlutusInterface conf action = do
}

BotSetup.runSetup cEnv -- run preparations to use `bot-plutus-interface`
userActon cEnv -- executing user action on cluster
maybeCancelChainIndex $ userAction cEnv -- executing user action on cluster
retryClusterFailedStartup =
let msg err = B.pack $ "Retrying cluster startup due to: " <> show err <> "\n"
shouldRetry =
pure . \case
ProcessHasExited _ _ -> True
_ -> False
in recovering
(limitRetries 5)
[logRetries shouldRetry (\_ y _ -> B.hPutStr stderr $ msg y)]
. const

-- Redirect stdout to a provided handle providing mask to temporarily revert back to initial stdout.
withRedirectedStdoutHdl :: Handle -> ((forall b. IO b -> IO b) -> IO a) -> IO a
Expand Down