-
Notifications
You must be signed in to change notification settings - Fork 4.5k
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
csds: implement CSDS service handler #4243
Conversation
xds/csds/csds.go
Outdated
ret := &clientStatusServer{} | ||
ret.xdsClient, ret.xdsClientErr = newXDSClient() | ||
if ret.xdsClientErr != nil { | ||
logger.Errorf("failed to create xds client: %v", ret.xdsClientErr) |
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.
Why not return an error
as a second return value instead? What good is it to have a CSDS server without an xdsClient?
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.
It's harder to use if this function returns an error, because they cannot register this in one line.
If this fails, the server will fail all RPCs.
And also, I think the error returned here is either useless, or should have been returned by other functions.
Before this happens, the gRPC client/server that uses xDS would also fail, and the user will get the returned error from there.
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 not convinced by the argument that returning an error makes the function harder to use. And that if the client creation runs into an error here, it would have elsewhere as well.
Given that this is a public facing API, I feel it would be better to have it stand on its own rather than have these unsaid dependencies.
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 changed this to return an error.
Also the returned struct is a concrete struct, instead of the interface. So we can also get rid of the finalizer.
xds/csds/csds.go
Outdated
if err != nil { | ||
return err | ||
} | ||
resp, errResp := s.buildClientStatusRespForReq(req) |
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.
Nit: s/errResp/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.
Done
xds/csds/csds.go
Outdated
// If it returns an error, the error is a status error. | ||
func (s *clientStatusServer) buildClientStatusRespForReq(req *v3statuspb.ClientStatusRequest) (*v3statuspb.ClientStatusResponse, error) { | ||
if len(req.NodeMatchers) != 0 { | ||
return nil, status.Errorf(codes.InvalidArgument, "NodeMatchers are not supported, request contains NodeMatchers: %v", req.NodeMatchers) |
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.
Nit: s/NodeMatchers/node_matchers/ in the error message.
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.
Done
xds/csds/csds.go
Outdated
return nil, status.Errorf(codes.InvalidArgument, "NodeMatchers are not supported, request contains NodeMatchers: %v", req.NodeMatchers) | ||
} | ||
if s.xdsClient == nil { | ||
return nil, status.Errorf(codes.FailedPrecondition, "xds client creation failed with error: %v", s.xdsClientErr) |
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.
Same question here? What purpose does it serve to have a CSDS server without an xdsClient?
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.
Replied in the previous comment.
DumpCDS() (string, map[string]client.UpdateWithMD) | ||
DumpEDS() (string, map[string]client.UpdateWithMD) | ||
BootstrapConfig() *bootstrap.Config | ||
Close() |
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.
Same for Close()
.
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 a good question actually. The client used by CSDS is never closed. We should close it, but there's no easy way.
What's I'm thinking is to set a Finalizer: https://golang.org/pkg/runtime/#SetFinalizer
But not sure if it's a good idea
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.
Added a finalizer. Does seems too bad. Let me know what you think.
Also, cc @dfawley What do you think of a finalizer?
xdsC.WatchEndpoints(target, func(client.EndpointsUpdate, error) {}) | ||
} | ||
|
||
for i := 0; i < retryCount; i++ { |
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.
Is there something better than retrying in a loop and sleeping in between that we can do 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.
I didn't find any better way. There are real xDS RPCs involved, and we don't have control of the timing.
if err := stream.Send(&v3statuspb.ClientStatusRequest{Node: nil}); err != nil { | ||
return fmt.Errorf("failed to send: %v", err) | ||
} | ||
r, err := stream.Recv() |
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.
We are using the streaming method in all tests. For completeness sake, should one of the checkForXxx
use the unary method?
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 it would be OK, because they both simply call the same function.
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.
But isn't that an implementation detail. Hmm ... maybe its ok.
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 the only sticking point is the API of NewClientStatusDiscoveryServer
. Otherwise looks good.
xds/csds/csds.go
Outdated
ret := &clientStatusServer{} | ||
ret.xdsClient, ret.xdsClientErr = newXDSClient() | ||
if ret.xdsClientErr != nil { | ||
logger.Errorf("failed to create xds client: %v", ret.xdsClientErr) |
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 not convinced by the argument that returning an error makes the function harder to use. And that if the client creation runs into an error here, it would have elsewhere as well.
Given that this is a public facing API, I feel it would be better to have it stand on its own rather than have these unsaid dependencies.
xds/csds/csds.go
Outdated
// | ||
// Besides, if client cannot be created, the users should have received | ||
// the error, because their client/server cannot work, either. | ||
logger.Errorf("failed to create xds client: %v", ret.xdsClientErr) |
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.
Also, return
from inside the if
, so that the code under else
need not be indented.
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.
Done
xds/csds/csds.go
Outdated
case client.ServiceStatusNACKed: | ||
return v3adminpb.ClientResourceStatus_NACKED | ||
} | ||
return v3adminpb.ClientResourceStatus_UNKNOWN |
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.
Nit: move the last return
into a default
case?
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.
Done
if err := stream.Send(&v3statuspb.ClientStatusRequest{Node: nil}); err != nil { | ||
return fmt.Errorf("failed to send: %v", err) | ||
} | ||
r, err := stream.Recv() |
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.
But isn't that an implementation detail. Hmm ... maybe its ok.
No more finalizer
xds/csds/csds.go
Outdated
if s.xdsClient == nil { | ||
// This should never happen. | ||
return nil, status.Errorf(codes.FailedPrecondition, "xds client is nil") | ||
} |
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.
We don't need this check anymore, right?
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.
Removed. Should be safe now.
No description provided.