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

V1: Set request info into error details for error responses #676

Merged
merged 11 commits into from
Nov 21, 2023

Conversation

smaye81
Copy link
Member

@smaye81 smaye81 commented Nov 18, 2023

This updates a testing rule that server implementations should append a RequestInfo message into error details for the following conditions:

  • for unary and client-streaming tests that return an error
  • for server- and bidi-streaming tests that return no responses and only an error (i.e. trailers-only)

If the client specified any error details to be returned, the server should append the RequestInfo to those details.

Comment on lines 419 to 421
// TODO: Should this be more lenient? Are we okay with details getting re-ordered?
// An alternative might be to create a map keyed by type, and for each type
// remove expected messages as they are matched against actual ones.
Copy link
Member Author

Choose a reason for hiding this comment

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

I'm actually wondering whether we care about anything at all other than ConformancePayload? From a testing standpoint, we really only care that the server can support error details and the client can read what the server sent. Setting the ConformancePayload into error details for unary errors should actually cover that case maybe?

Copy link
Member

Choose a reason for hiding this comment

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

We also care about how unprocessable messages in an Any are handled, like if the server gets an Any it doesn't understand from a remote server and then must include it in the error details. This mainly impacts the Connect protocol, since it tries to parse the Any to produce the debug info.

Also, I'm not sure it's ConformancePayload we care about. I think it's only RequestInfo. The ConformancePayload also includes a data field, that is only used with real responses (when the response definition indicates the data it should return).

Copy link
Member Author

Choose a reason for hiding this comment

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

Yeah I actually started out with just RequestInfo, but it was easier to do the ConformancePayload because the checkPayloads function already existed. But, I split that out into a checkRequestInfo function and went with RequestInfo instead.

@@ -103,11 +103,6 @@ testCases:
error:
code: 13
message: "server stream failed"
details:
Copy link
Member Author

Choose a reason for hiding this comment

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

Removed from all but grpc-only.yaml because that file throws the following when details are removed:

could not write request: client closed stdin

Will fix in follow-up PR.

Copy link
Member Author

Choose a reason for hiding this comment

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

Figured out this issue for the follow-up PR, but probably needs a discussion. I'll post it once this one is hashed out.

Copy link
Member

Choose a reason for hiding this comment

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

I'm pretty sure that error means that the client program crashed.

Copy link
Member Author

Choose a reason for hiding this comment

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

See metadata.go. The issue was trying to write grpc-status-details-bin to stdout.

@smaye81 smaye81 requested a review from jhump November 20, 2023 00:25
internal/app/connectconformance/results.go Outdated Show resolved Hide resolved
internal/app/connectconformance/test_case_library.go Outdated Show resolved Hide resolved
@@ -303,6 +303,18 @@ func parseUnaryResponseDefinition(
if def != nil {
switch respType := def.Response.(type) {
case *v1.UnaryResponseDefinition_Error:
// If error details were not provided to be returned, then the server
// should set the conformance payload as the error details for unary responses
if respType.Error.Details == nil {
Copy link
Member

Choose a reason for hiding this comment

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

It's often an anti-pattern to compare slices to nil. Rarely do you really intend to distinguish between nil and an empty slice. So it's safest to use len(respType.Error.Details) == 0.

Also, why do this only when error details is nil? I think we want to add this to the details, even if non-nil.

proto/connectrpc/conformance/v1/service.proto Outdated Show resolved Hide resolved
@jhump jhump changed the title V2 (V1?): Set conformance payload into error details for unary responses V1: Set conformance payload into error details for unary responses Nov 20, 2023
@@ -28,6 +28,11 @@ func ConvertMetadataToProtoHeader(
) []*v1.Header {
headerInfo := make([]*v1.Header, 0, len(src))
for key, value := range src {
// Omit grpc-status-details-bin from the response trailers reported
Copy link
Member Author

@smaye81 smaye81 Nov 20, 2023

Choose a reason for hiding this comment

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

This fixes the client closed stdin problem because the problem results from trying to write grpc-status-details-bin base64 encoded data to the output (not entirely sure why -- bc of utf-8? buffer length?). But is this the right solution? Doing this removes anything grpc specific from the headers returned to the test. But maybe that's fine bc connect has already tried to parse the grpc stuff by now, so as long as we know the error that grpc returned by now we can discard anything we used to glean that information.

We sort of have to do this here bc it needs to be omitted before we send out the response, but thats probably
ok as long as we know the status and details by now.

Copy link
Member

Choose a reason for hiding this comment

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

This is very confusing. I don't think we should have to do this, and it would be even more confusing if we suggested that other clients-under-test or server-under-test should also do this.

I think we need to identify the real root cause of why this was causing the problem and (hopefully) resolve in a different way.

Copy link
Member

Choose a reason for hiding this comment

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

Having said that, I don't think we need to block this PR. Let's add a TODO to investigate further and remove this when the issue is more clearly understood.

@smaye81
Copy link
Member Author

smaye81 commented Nov 20, 2023

@jhump I was actually going to do a follow-up PR that fixed the client closed stdin error and also update the server-stream responses to set error detail also, but I just moved it all to this PR since it's easier to get a holistic view of the changes. Lmk what you think.

@smaye81 smaye81 requested a review from jhump November 20, 2023 21:13
@smaye81 smaye81 changed the title V1: Set conformance payload into error details for unary responses V1: Set request info into error details for error responses Nov 20, 2023
Copy link
Member

@jhump jhump left a comment

Choose a reason for hiding this comment

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

Let's add a TODO about that grpc-details-bin thing. Otherwise, LGTM

errs = append(errs, fmt.Errorf("request #%d: did not survive round-trip: - wanted, + got\n%s", reqNum, diff))
}
}
errs = append(errs, checkRequestInfo(expectedPayload.GetRequestInfo(), actualPayload.GetRequestInfo(), i)...)
Copy link
Member

Choose a reason for hiding this comment

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

Instead of passing i, I think you can check it here:

if i == 0 {
    // Validate headers, timeout, and query params. We only need to do this once, for first response.
    errs = append(errs, checkRequestInfo(expectedPayload.GetRequestInfo(), actualPayload.GetRequestInfo())...)
}

That way you can omit the hard-coded zero in the other call site.

Copy link
Member Author

Choose a reason for hiding this comment

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

The only problem is that value is also used on line 347 if request lengths don't match:

errs = append(errs, fmt.Errorf("response #%d: expecting %d request messages to be described but instead got %d", respNum+1, len(expected.GetRequests()), len(actual.GetRequests())))

Copy link
Member Author

Choose a reason for hiding this comment

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

I just removed the number from that fmt.Errorf statement. It probably doesn't provide a ton of info when weighed against having to pass the number in as a param.

@@ -28,6 +28,11 @@ func ConvertMetadataToProtoHeader(
) []*v1.Header {
headerInfo := make([]*v1.Header, 0, len(src))
for key, value := range src {
// Omit grpc-status-details-bin from the response trailers reported
Copy link
Member

Choose a reason for hiding this comment

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

Having said that, I don't think we need to block this PR. Let's add a TODO to investigate further and remove this when the issue is more clearly understood.

@@ -379,7 +377,9 @@ func checkPayloads(expected, actual []*conformancev1.ConformancePayload) multiEr
errs = append(errs, fmt.Errorf("response #%d: expecting data %x, got %x", i+1, expectedPayload.Data, actualPayload.Data))
}

errs = append(errs, checkRequestInfo(expectedPayload.GetRequestInfo(), actualPayload.GetRequestInfo(), i)...)
if i == 0 { //nolint:nestif
Copy link
Member

Choose a reason for hiding this comment

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

I just realized that my suggestion changed the functionality here. It's only part of checkRequestInfo that we skip when i != 0 -- the headers, timeout, and query params.

For that, I think checkRequestInfo should have a checkHeaders bool param. This call site can pass i == 0 for that param, and the other call site can always provide true.

@smaye81 smaye81 merged commit b9a2afd into v1 Nov 21, 2023
@smaye81 smaye81 deleted the sayers/error_details branch November 21, 2023 15:43
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.

2 participants