-
Notifications
You must be signed in to change notification settings - Fork 1.1k
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
BasicHost changes for introspection #774
BasicHost changes for introspection #774
Conversation
004f0e7
to
694477a
Compare
1c61da3
to
b5a2ce9
Compare
p2p/host/basic/basic_host.go
Outdated
@@ -191,12 +194,54 @@ func NewHost(ctx context.Context, net network.Network, opts *HostOpts) (*BasicHo | |||
h.pings = ping.NewPingService(h) | |||
} | |||
|
|||
if h.introspector == nil { | |||
h.introspector = introspection.NewDefaultIntrospector() | |||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This duplicates the code in config.NewNode() here.
We can safely assume here that an introspector exists.
However, do we want to allow users to be able to disable introspection ? That decision will affect how we've currently configured introspection on the host.
p2p/host/basic/basic_host.go
Outdated
return h, nil | ||
} | ||
|
||
func (h *BasicHost) runtimeDataProvider() *coreit.RuntimeProviders { | ||
// TODO What is the version here ? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What does version mean ?
p2p/host/basic/basic_host.go
Outdated
@@ -783,6 +828,13 @@ func (h *BasicHost) AllAddrs() []ma.Multiaddr { | |||
return dedupAddrs(finalAddrs) | |||
} | |||
|
|||
// TODO Do this for routed & relay hosts as well |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Need to give this a thought.
TODO
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some initial feedback. This is shaping up good!
config/config.go
Outdated
@@ -77,6 +79,9 @@ type Config struct { | |||
|
|||
EnableAutoRelay bool | |||
StaticRelays []peer.AddrInfo | |||
|
|||
Introspector coreit.Introspector | |||
IntrospectionServerAddr string |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think we should allow the user to pass in an address. For UX, I would recommend the following:
- We add an
ListenAddress() string
method to theIntrospector
interface, so we can query it later. - The
Introspector
implementation has aDefaultAddress
, and may use fallback logic if the default listen address is taken. For example, the default address could be:ws://localhost:8096
, and if the port is occupied, we can fall back to trying 8096 + [1,100], trying one by one until we find an available port, or fail entirely. See https://github.com/libp2p/go-libp2p-daemon/blob/b95e77dbfcd186ccf817f51e95f73f9fd5982600/p2pd/main.go#L30-L60 for an example of this logic. - We expose the final listen address via the
ListenAddress() string
method. - Pushing this concern to the implementation is better, because alternate implementations may use different protocols (e.g. Unix sockets) and heuristics.
For a user that wants to set a concrete address, they would construct the concrete implementation via its constructor (which would take a configuration object, that includes the listen addr), and inject that via the Introspector
option.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am not sure I agree. Why shouldn't we allow to user to pass in a address while creating a libp2p node ?
The address is a required by the introspection WS server which is owned by the Host.
The Introspector
is only responsible for proving a registry for sub-systems & resolving the state when it is asked to. It is not/shouldn't be responsible for serving that state over the wire & hence dosen't need to be bothered with finding a free port which is anyways not a trivial operation & muddles the construction logic of the Introspector
.
I'd like to make ListenAddress() string
a part of the IntrospectableHost
interface that you've described here. The go-libp2p-introspection.StartWsServer
method returns the address it was able to bind to(using the same logic as the code that you've linked) which the host then captures & returns via ListenAddress() string
.
Wdyt ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think I was unclear. We should totally allow passing in a listen address, but not as a dedicated host option. For two reasons:
- Creating such granular field-level options muddies the namespace.
- Listen addresses, along with all potential customisations of the introspection endpoint, are implementation-dependent.
Users wanting to customise the listen addr of an introspection server, should be able to do that through the endpoint's constructor:
ep, err := introspection.Endpoint(&introspection.EndpointOpts{
ListenAddress: "...",
}
h, err := host.New(host.Introspector(ep))
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm gonna be refactoring things a little bit myself; no need for you to do anything here, @aarshkshah1992. Just wanting to provide feedback for future PRs.
package libp2p | ||
|
||
import ( | ||
"context" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please group imports in groups, in this order:
- one group for stdlib
- one group for go-libp2p-core
- other go-libp2p imports
- 3rd party libs
}) | ||
s1, err := h1.NewStream(ctx, h3.ID(), p1) | ||
require.NoError(t, err) | ||
wg.Add(1) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is racy. It's possible for wg.Done()
to get called before the the wg.Add(1)
. Move the Add to before opening the stream to remove the race.
_, err = s1.Write(msg1) | ||
require.NoError(t, err) | ||
bz1 := make([]byte, len(msg2)) | ||
wg.Wait() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think you need this wait at all. You can use io.ReadFull
instead.
msg3 := []byte("111") | ||
msg4 := []byte("0000") | ||
|
||
iaddr := "0.0.0.0:9999" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Tests should not listen on 0.0.0.0 (that's insecure) nor on a fixed port (it might be occupied). Instead, we should listen on 127.0.0.1:0, and allow the listener to allocate a port. ListenAddrs()
should then return the actual addresses, not the user-supplied ones.
bz1 := make([]byte, len(msg2)) | ||
wg.Wait() | ||
_, err = s1.Read(bz1) | ||
require.NoError(t, err) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can do require := require.New(t)
at the top, and omit the first t
parameter from all assertions.
// host2 -> OPENS Stream 2 -> host1 , writes a message & reads the response | ||
p2 := protocol.ID("h2h1") | ||
h1.SetStreamHandler(p2, func(s network.Stream) { | ||
bz := make([]byte, len(msg3)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's use buf
. It shadows previous usages, and it's idiomatic.
_, err = s2.Write(msg3) | ||
require.NoError(t, err) | ||
bz2 := make([]byte, len(msg4)) | ||
wg.Wait() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No need for waitgroups. This code is racy, and we can use io.ReadFull
instead.
// wait till connection is established | ||
i := 0 | ||
var connection *websocket.Conn | ||
for { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this loop declaration can be made more elegant, by moving the init, test, increment up here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Turns out I didn't push the changes after basing them on the new core changes. :/ |
For #775
Allows the BasicHost to be "introspectable".