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

Adapt NodeIPC module from cardano-sl #388

Merged
merged 12 commits into from
Jun 14, 2019

Conversation

rvl
Copy link
Contributor

@rvl rvl commented Jun 10, 2019

Relates to issue #144

Overview

  • Takes the cardano-sl/cardano-shell NodeIPC code, cleans it up, and splits the general NodeJS child_process IPC protocol out from the Daedalus/Cardano specific protocol, improves the exception handling, changes the logging, and just makes it simpler.
  • Integrates DaedalusIPC into the server and launcher.
  • Adds an integration test which starts the launcher from nodejs, sending QueryPort, waiting for ReplyPort, as Daedalus does.
  • Adds a test for launcher --state-dir in the same Spec.

@KtorZ
Copy link
Member

KtorZ commented Jun 10, 2019

~~@rvl I am afraid you've done some double-work done on this PR :s ... ~~

Have you looked into cardano-shell (as mentioned in #144) -> https://github.com/input-output-hk/cardano-shell/tree/master/src/Cardano/Shell/NodeIPC ?

Nevermind, just saw your comment on your development plan:

it's not suitable for re-use because testing code is mixed up with message protocol code which is mixed up with the ipc code.

@rvl rvl marked this pull request as ready for review June 11, 2019 07:08
@rvl rvl force-pushed the rvl/144/daedalus-ipc branch 3 times, most recently from 9695881 to f7e12ed Compare June 13, 2019 05:54
Copy link
Member

@KtorZ KtorZ left a comment

Choose a reason for hiding this comment

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

LGTM overall, but haven't really got time to play with it.

Also, some modification regarding ports have already been included in #407

@@ -76,6 +78,7 @@ library
Cardano.Wallet.Api
Cardano.Wallet.Api.Server
Cardano.Wallet.Api.Types
Cardano.Wallet.DaedalusIPC
Copy link
Member

Choose a reason for hiding this comment

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

Not a big fan of referring to Daedalus here. The IPC stuff can be seen as a low-level protocol to allow a client to perform some process management task of the wallet server. Of course, the primary user is Daedalus, but I don't want us to fall in the trap of "making stuff just for the frontend" as a Daedalus*** module would suggest. This already caused us quite some pain with the old API that has some non-sensical stuff into it.

In short: let's build more generic interfaces that serve a well-defined purpose.

Copy link
Member

Choose a reason for hiding this comment

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

I agree that NodeIPC was rather confusing, so maybe have something like NodeJSIPC or JSClientIPC

Copy link
Contributor Author

Choose a reason for hiding this comment

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

One of our main requirements is working with the Daedalus frontend. This ipc protocol is unique to Daedalus. I'm not aware of any requirements for integrating with some imaginary software that doesn't exist yet, but we can change the name if that happens.

It would be better to document the ways of starting cardano-wallet server on a port, so that prospective users can assess whether any of the schemes would work for them.

sayErr "[INFO] Daedalus IPC is not enabled"
sleep
Left (NodeChannelBadFD err) ->
sayErr $ "[ERROR] Starting Daedalus IPC: " <> err
Copy link
Member

Choose a reason for hiding this comment

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

Now that we've the logging infrastructure setup, I'd suggest to switch to using the trace object instead of any other outputting method 👍

Copy link
Contributor Author

Choose a reason for hiding this comment

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

OK

});
}
});
}
Copy link
Member

Choose a reason for hiding this comment

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

Would be worth adding a negative test case with Daedalus sending some gibberish at first, to see that the ParseFailure message gets triggered correctly 👍

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Added a test. You might have noticed the tests are a bit thin. That's because

  1. cardano-shell has more tests, and we will probably move the code there
  2. I think it's worth considering inheriting the socket from the parent process, rather than implementing some hokey message protocol.

-- run launcher in the tests. But that dependency can't be expressed in the
-- cabal file, because otherwise there would be a cycle.
--
-- So one hacky way to work around it is by running programs under "stack exec".
Copy link
Member

Choose a reason for hiding this comment

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

👍

-> IO ()
ipcListener handle onMsg chan = do
hSetNewlineMode handle noNewlineTranslation
catch (race_ replyLoop sendLoop) onIOError
Copy link
Member

Choose a reason for hiding this comment

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

Nice pattern. I'd have a slight preference for concurrently instead of race_ since we aren't actually expecting any of the two sides to eventually terminate (which race_ conveys). We still get the cancellation behavior with concurrently if I am correct.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It really doesn't matter which one. Both of them imply the wrong thing. But if concurrently makes you happy, then I'll change it.

onIOError :: IOError -> IO ()
onIOError err = do
sayErrString $ "[ERROR] Exception caught in DaedalusIPC: " <> show err
when (isEOFError err) $ sayErr "[DEBUG] it's an eof"
Copy link
Member

Choose a reason for hiding this comment

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

An EOF here would mean that Daedalus / the client has closed the connection on their hand, right ?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Usually the exception is usually broken pipe. I think this was debug code that got left in cardano-sl from the DEVOPS-1234 investigation. I have removed the log anyway.

act
Left NodeChannelDisabled -> do
sayErr "[INFO] Daedalus IPC is not enabled"
sleep
Copy link
Member

Choose a reason for hiding this comment

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

Why sleeping here? What would be the consequence of returning () ? Do we expect daedalusIPC to never return? If so, I'd rather have it mentioned in a function comment to explain this behavior.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, it should have been commented. This is the error handling I want:

  1. If set up is unsuccessful, return immediately, causing the cardano-wallet process to exit. Daedalus does not want zombie cardano-wallet processes hanging around that it can't communicate with.
  2. If set up is successful, never return until Daedalus exits.
  3. If not running within a nodejs channel, never return (simplifies calling code)

Copy link
Member

Choose a reason for hiding this comment

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

Okay, so we really assume that any termination is an error, hence the sleep when there's no node.js channel (not an error so to speak).

Server.start logStartup walletPort wallet
Server.withListeningSocket walletPort $ \(port, socket) -> do
let settings = Server.mkWarpSettings logStartup port
race_ (daedalusIPC port) (Server.startOnSocket settings socket wallet)
Copy link
Member

Choose a reason for hiding this comment

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

Ah! I supposed this is why we make the daedalusIPC hangs forever. I believe that switching for concurrently instead of race_ would remove that limitation since the action could return without cancelling the startOnSocket.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This needs to be race so that the program exits when there is an error from daedalusIPC.

Copy link
Member

@KtorZ KtorZ Jun 14, 2019

Choose a reason for hiding this comment

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

Got you, this comment of yours clarifies the choice of race indeed. I'd suggest to have the behavior documented either on the call-site or, somewhere in the DaedalusIPC. Maybe in both places.

edit: I see it's now the case

@rvl rvl force-pushed the rvl/144/daedalus-ipc branch 3 times, most recently from e6cf806 to 3d63b7b Compare June 14, 2019 03:11
@KtorZ
Copy link
Member

KtorZ commented Jun 14, 2019

Merging into #414 to group PRs and avoid multiple merges because of CI issues due to stack major bump today ....

@KtorZ KtorZ changed the base branch from master to KtorZ/368/merge-cardano-launcher June 14, 2019 18:00
@KtorZ KtorZ merged commit d9b3553 into KtorZ/368/merge-cardano-launcher Jun 14, 2019
@KtorZ KtorZ deleted the rvl/144/daedalus-ipc branch June 14, 2019 18:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants