Skip to content

Commit

Permalink
Remove GetAnomaProcess from the Anoma effect (#3179)
Browse files Browse the repository at this point in the history
This PR removes `GetAnomaProcess` from the Anoma effect.

Use the `launchAnoma` function to start a persistent Anoma client /
server (used by `juvix dev anoma node`).

Other changes:

* It's no longer necessary to pass the protobuf files to `grpcurl`
because the Anoma client now supports gRPC reflection.
* We pass the elixir start command to `mix` via `-e` argument instead of
using a temporary file.

The purpose for this change is that we I want to add an interpreter for
Anoma that makes gRPC calls to an exisitng Anoma client.
`GetAnomaProcess` has no meaning for this interpreter.
  • Loading branch information
paulcadman authored Nov 19, 2024
1 parent dc2d268 commit eab02a7
Show file tree
Hide file tree
Showing 5 changed files with 54 additions and 73 deletions.
4 changes: 1 addition & 3 deletions app/Commands/Dev/Anoma/Node.hs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,4 @@ runCommand opts = runAppError @SimpleError
. runProcess
$ do
anomaDir :: AnomaPath <- AnomaPath <$> fromAppPathDir (opts ^. nodeAnomaPath)
runAnoma anomaDir $ do
p <- getAnomaProcess
void (waitForProcess (p ^. anomaProcessHandle))
launchAnoma anomaDir >>= void . waitForProcess
2 changes: 1 addition & 1 deletion app/Commands/Dev/Nockma/Run.hs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ runCommand opts = do
inputFile = opts ^. nockmaRunFile

runInAnoma :: (Members AppEffects r) => AnomaPath -> Term Natural -> [Term Natural] -> Sem r ()
runInAnoma anoma t args = runAppError @SimpleError . runAnoma anoma $ do
runInAnoma anoma t args = runAppError @SimpleError . runAnomaEphemeral anoma $ do
res <-
runNockma
RunNockmaInput
Expand Down
107 changes: 51 additions & 56 deletions src/Anoma/Effect/Base.hs
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,24 @@
-- 2. grpcurl
module Anoma.Effect.Base
( Anoma,
getAnomaProcess,
anomaRpc,
AnomaPath (..),
AnomaProcess (..),
AnomaGrpcClientInfo (..),
anomaGrpcClientInfoPort,
anomaGrpcClientInfoUrl,
anomaProcessHandle,
anomaPath,
runAnoma,
runAnomaEphemeral,
launchAnoma,
module Anoma.Rpc.Base,
module Juvix.Compiler.Nockma.Translation.FromTree,
)
where

import Anoma.Effect.Paths
import Anoma.Rpc.Base
import Data.ByteString qualified as B
import Data.Text qualified as T
import Juvix.Compiler.Nockma.Translation.FromTree (AnomaResult)
import Juvix.Data.CodeAnn
import Juvix.Extra.Paths (anomaStartExs)
Expand All @@ -29,9 +32,12 @@ newtype AnomaProcess = AnomaProcess
{ _anomaProcessHandle :: ProcessHandle
}

data AnomaGrpcClientInfo = AnomaGrpcClientInfo
{ _anomaGrpcClientInfoPort :: Int,
_anomaGrpcClientInfoUrl :: String
}

data Anoma :: Effect where
-- | Keep the node and client running
GetAnomaProcess :: Anoma m AnomaProcess
-- | grpc call
AnomaRpc :: GrpcMethodUrl -> Value -> Anoma m Value

Expand All @@ -40,44 +46,34 @@ makeLenses ''AnomaProcess

newtype AnomaPath = AnomaPath {_anomaPath :: Path Abs Dir}

newtype ClientGrpcPort = ClientGrpcPort {_clientGrpcPort :: Int}

makeLenses ''AnomaPath
makeLenses ''ClientGrpcPort
makeLenses ''AnomaGrpcClientInfo

relativeToAnomaDir :: (Members '[Reader AnomaPath] r) => Path Rel x -> Sem r (Path Abs x)
relativeToAnomaDir p = do
anoma <- asks (^. anomaPath)
return (anoma <//> p)
anomaCreateProcess :: (Members '[Reader AnomaPath] r') => Sem r' CreateProcess
anomaCreateProcess = do
anomapath <- asks (^. anomaPath)
return
(proc "mix" ["run", "--no-halt", "-e", unpack (T.strip (decodeUtf8 anomaStartExs))])
{ std_out = CreatePipe,
cwd = Just (toFilePath anomapath)
}

withSpawnAnomaNodeAndClient ::
forall r a.
(Members '[EmbedIO, Logger, Error SimpleError, Process, Reader AnomaPath] r) =>
(ClientGrpcPort -> ProcessHandle -> Sem r a) ->
Sem r a
withSpawnAnomaNodeAndClient body = withSystemTempFile "start.exs" $ \fp tmpHandle -> do
liftIO (B.hPutStr tmpHandle anomaStartExs) >> hClose tmpHandle
cproc <- cprocess (toFilePath fp)
withCreateProcess cproc $ \_stdin mstdout _stderr procHandle -> do
let nodeOut = fromJust mstdout
ln <- hGetLine nodeOut
let parseError = throw (SimpleError (mkAnsiText ("Failed to parse the client grpc port when starting the anoma node and client.\nExpected a number but got " <> ln)))
grpcPort :: Int <- either (const parseError) return . readEither . unpack $ ln
logInfo "Anoma node and client successfully started"
logInfo (mkAnsiText ("Listening on port " <> annotate AnnImportant (pretty grpcPort)))
body (ClientGrpcPort grpcPort) procHandle
where
cprocess :: (Members '[Reader AnomaPath] r') => FilePath -> Sem r' CreateProcess
cprocess exs = do
anomapath <- asks (^. anomaPath)
return
(proc "mix" ["run", "--no-halt", exs])
{ std_out = CreatePipe,
cwd = Just (toFilePath anomapath)
}
setupAnomaProcess :: (Members '[EmbedIO, Logger, Error SimpleError] r) => Handle -> Sem r AnomaGrpcClientInfo
setupAnomaProcess nodeOut = do
ln <- hGetLine nodeOut
let parseError = throw (SimpleError (mkAnsiText ("Failed to parse the client grpc port when starting the anoma node and client.\nExpected a number but got " <> ln)))
grpcPort :: Int <- either (const parseError) return . readEither . unpack $ ln
logInfo "Anoma node and client successfully started"
logInfo (mkAnsiText ("Listening on port " <> annotate AnnImportant (pretty grpcPort)))
return
( AnomaGrpcClientInfo
{ _anomaGrpcClientInfoPort = grpcPort,
_anomaGrpcClientInfoUrl = "localhost"
}
)

anomaRpc' ::
(Members '[Reader ClientGrpcPort, Reader AnomaPath, Process, EmbedIO, Error SimpleError] r) =>
(Members '[Reader AnomaGrpcClientInfo, Process, EmbedIO, Error SimpleError] r) =>
GrpcMethodUrl ->
Value ->
Sem r Value
Expand All @@ -94,36 +90,35 @@ anomaRpc' method payload = do
Right r -> return r
Left err -> throw (SimpleError (mkAnsiText err))

grpcCliProcess :: (Members '[Reader ClientGrpcPort, Reader AnomaPath] r) => GrpcMethodUrl -> Sem r CreateProcess
grpcCliProcess :: (Members '[Reader AnomaGrpcClientInfo] r) => GrpcMethodUrl -> Sem r CreateProcess
grpcCliProcess method = do
importPath <- relativeToAnomaDir relProtoDir
grpcPort <- asks (^. clientGrpcPort)
grpcPort <- asks (^. anomaGrpcClientInfoPort)
grpcUrl <- asks (^. anomaGrpcClientInfoUrl)
return
( proc
"grpcurl"
[ "-import-path",
toFilePath importPath,
"-proto",
toFilePath relProtoFile,
"-d",
[ "-d",
"@",
"-plaintext",
"localhost:" <> show grpcPort,
grpcUrl <> ":" <> show grpcPort,
show method
]
)
{ std_in = CreatePipe,
std_out = CreatePipe
}

runAnoma :: forall r a. (Members '[Logger, EmbedIO, Error SimpleError] r) => AnomaPath -> Sem (Anoma ': r) a -> Sem r a
runAnoma anomapath body = runReader anomapath . runProcess $
withSpawnAnomaNodeAndClient $ \grpcport anomaH ->
runReader grpcport $ do
runAnomaEphemeral :: forall r a. (Members '[Logger, EmbedIO, Error SimpleError] r) => AnomaPath -> Sem (Anoma ': r) a -> Sem r a
runAnomaEphemeral anomapath body = runReader anomapath . runProcess $ do
cproc <- anomaCreateProcess
withCreateProcess cproc $ \_stdin mstdout _stderr _procHandle -> do
grpcServer <- setupAnomaProcess (fromJust mstdout)
runReader grpcServer $ do
(`interpret` inject body) $ \case
GetAnomaProcess ->
return
AnomaProcess
{ _anomaProcessHandle = anomaH
}
AnomaRpc method i -> anomaRpc' method i

launchAnoma :: (Members '[Logger, EmbedIO, Error SimpleError] r) => AnomaPath -> Sem r ProcessHandle
launchAnoma anomapath = runReader anomapath . runProcess $ do
cproc <- anomaCreateProcess
(_stdin, mstdout, _stderr, procHandle) <- createProcess cproc
setupAnomaProcess (fromJust mstdout) >> return procHandle
12 changes: 0 additions & 12 deletions src/Anoma/Effect/Paths.hs

This file was deleted.

2 changes: 1 addition & 1 deletion test/Anoma/Compilation/Positive.hs
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ mkAnomaNodeTest a@AnomaTest {..} =
runM
. ignoreLogger
. runSimpleErrorHUnit
. runAnoma testAnomaPath
. runAnomaEphemeral testAnomaPath
$ do
let rinput =
RunNockmaInput
Expand Down

0 comments on commit eab02a7

Please sign in to comment.