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

status: Fix status incompatibility introduced by #6919 and move non-regeneratable proto code into /testdata #7724

Merged
merged 9 commits into from
Oct 22, 2024

Conversation

arjan-bal
Copy link
Contributor

@arjan-bal arjan-bal commented Oct 10, 2024

Fixes: #7679

Problem

#6919 migrated usages of github.com/golang/protobuf to google.golang.org/protobuf/proto within status package. Prior to this, status.Details() called ptypes.UnmarshalAny which called proto.MessageV1(mt.New().Interface()) which called protoimpl.X.ProtoMessageV1Of(m) so the returned type always implemented protoadapt.MessageV1 and was always the same type given to status.WithDetails(). After the change, Status.Details() used anpb.Any.UnmarshallNew() which returns the vanilla type used while creating the anypb.Any which is only guaranteed to implement the MessageV2 API.

Effect

Users of code generated from protoc-gen-go < v1.4 (launched in 2020) would get a wrapped MessageV2 when calling Status.Details() even though they would have provided a type that only implemented MessageV1 to Status.WithDetails().

Fix

This PR brings back the call to protoimpl.X.ProtoMessageV1Of(m) by calling protoadapt.MessageV1Of before returning the Detail messages. A test is also added to catch regressions. Since the test uses generated code from protoc-gen-go v1.3, a dependency on github.com/golang/protobuf is re-introduced in go.mod.

Additional Changes

This PR also moves the generated code that is used for testing and must not be re-generated from the reflections/test module into a common directory under the parent grpc module.

RELEASE NOTES:

Copy link

codecov bot commented Oct 10, 2024

Codecov Report

Attention: Patch coverage is 32.00000% with 17 lines in your changes missing coverage. Please review.

Project coverage is 80.25%. Comparing base (c538c31) to head (ecdbda8).
Report is 2 commits behind head on master.

Files with missing lines Patch % Lines
.../grpc_testing_not_regenerated/simple_message_v1.go 29.16% 17 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master    #7724      +/-   ##
==========================================
+ Coverage   79.70%   80.25%   +0.54%     
==========================================
  Files         365      367       +2     
  Lines       36383    36622     +239     
==========================================
+ Hits        29000    29390     +390     
+ Misses       6183     6040     -143     
+ Partials     1200     1192       -8     
Files with missing lines Coverage Δ
internal/status/status.go 90.52% <100.00%> (ø)
testdata/grpc_testing_not_regenerated/testv3.go 6.86% <ø> (ø)
.../grpc_testing_not_regenerated/simple_message_v1.go 29.16% <29.16%> (ø)

... and 27 files with indirect coverage changes

}

if diff := cmp.Diff(details, gotDetails, protocmp.Transform()); diff != "" {
t.Errorf("(%v).Details got unexpected output, diff (-got +want):\n%s", s, diff)
Copy link
Contributor

Choose a reason for hiding this comment

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

The order in the output, (-got +want), needs to be swapped I guess based on the order of parameters passed to cmp.Diff.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed.

Comment on lines 213 to 218
details := []protoadapt.MessageV1{
&tpb.SimpleMessage{
Data: "abc",
},
&testpb.Empty{},
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Minor nit (optional)

I feel this is more readable and compact:

	details := []protoadapt.MessageV1{
		&tpb.SimpleMessage{Data: "abc"},
		&testpb.Empty{},
	}

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Changed as suggested.

@@ -0,0 +1,27 @@
/*
* Copyright 2022 gRPC authors.
Copy link
Contributor

Choose a reason for hiding this comment

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

Should this be 2024?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed.

Comment on lines +194 to +195
// Since protoc-gen-go generates only code that implements both V1 and
// V2 APIs for backward compatibility, this is not a concern.
Copy link
Member

Choose a reason for hiding this comment

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

Is this guaranteed to always be the case? (Does it matter?)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Is this guaranteed to always be the case?

I don't think so. The V1 API is deprecated and is only present for backward compatibility. There is no guarantee that protoc-gen-go will always support it.

Does it matter?

protoadapt.MessageV1Of will return a wrapped message which implements the V1 API when the argument doesn't implement it already. Callers will need to unwrap it using the protoadapt.MessageV2Of to get the inner type.

This is why I updated the doc comment to say the following:

If the detail can be decoded, the proto message returned is of the same type that was given to WithDetails().

status.WithDetails accepts only V1 messages. To pass it a V2 message, it will need to be wrapped and status.Details() will return the same wrapped type.

@@ -5,12 +5,12 @@ go 1.22.7
replace google.golang.org/grpc => ../../

require (
github.com/golang/protobuf v1.5.4
Copy link
Member

Choose a reason for hiding this comment

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

IIRC, the only reason this file was here is to hide this module dependency, since it's deprecated. Let's just delete this file now. I think we're going to have to accept a huge module dependency list, which is why I created #7690 - to make sure our actual production code dependencies can be tracked.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Removed the go.mod and go.sum files from reflection/test/.

@dfawley dfawley assigned arjan-bal and unassigned easwars and dfawley Oct 14, 2024
@purnesh42H purnesh42H modified the milestones: 1.68 Release, 1.69 Release Oct 16, 2024
option go_package = "google.golang.org/grpc/testdata/grpc_testing_not_regenerate";

// SimpleMessage is used to hold string data.
message SimpleMessage {

Choose a reason for hiding this comment

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

I feel it is better to name the test message as MessageV1Only, and give a short comments describing why it must not be regenerated.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I feel like "implementing the V1 API" is a property of the generated code and not the proto itself. There is a README.md describing the use of each generated file and how it was generated.

Choose a reason for hiding this comment

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

Sorry I missed that.

gotDetails = append(gotDetails, msg)
}

if diff := cmp.Diff(details, gotDetails, protocmp.Transform()); diff != "" {

Choose a reason for hiding this comment

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

I just have glance on cmp.Diff, look like it is used to convert proto.Message into map-like structure, then perform a recursive "value-equal" check.
I think such examination does not satisfy "the proto message returned by Details() is of the same type that was given to WithDetails()". A reflect.Type check is preferred in such situation.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

You're right, added a type comparison using reflect.TypeOf.

@arjan-bal arjan-bal assigned easwars and dfawley and unassigned arjan-bal Oct 17, 2024
func (s) TestStatus_ErrorDetailsMessageV1(t *testing.T) {
details := []protoadapt.MessageV1{
&tpb.SimpleMessage{Data: "abc"},
&testpb.Empty{},

Choose a reason for hiding this comment

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

According to the test comments, should testpb.Empty be removed ?

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, removed. I wanted to keep a test case for ensuring v1 + v2 messages are also handled correctly, added a new test function for that later.

option go_package = "google.golang.org/grpc/testdata/grpc_testing_not_regenerate";

// SimpleMessage is used to hold string data.
message SimpleMessage {

Choose a reason for hiding this comment

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

Sorry I missed that.

Copy link
Member

@dfawley dfawley left a comment

Choose a reason for hiding this comment

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

Super nitpicky, but maybe this is easy to fix?

$ mv testdata/grpc_testing_not_regenerate testdata/grpc_testing_not_regenerated
$ grep -rl not_regenerate | xargs sed -i 's/grpc_testing_not_regenerate/grpc_testing_not_regenerated/g'

Maybe if it's that easy, change it? The awkward use of english always bothered me here. :)

"do_not_regenerate" or "dont_regenerate" would also be fine.

@dfawley dfawley assigned arjan-bal and unassigned dfawley Oct 18, 2024
@arjan-bal
Copy link
Contributor Author

Super nitpicky, but maybe this is easy to fix?

$ mv testdata/grpc_testing_not_regenerate testdata/grpc_testing_not_regenerated
$ grep -rl not_regenerate | xargs sed -i 's/grpc_testing_not_regenerate/grpc_testing_not_regenerated/g'

Maybe if it's that easy, change it? The awkward use of english always bothered me here. :)

"do_not_regenerate" or "dont_regenerate" would also be fine.

Renamed the directory. It required re-generating the message V1 only proto as it started panicking during registration:

panic: mismatching message name: got grpc.testdata.grpc_testing_not_regenerate.SimpleMessage, want grpc.testdata.grpc_testing_not_rege
nerated.SimpleMessage

goroutine 1 [running]:
google.golang.org/protobuf/internal/impl.legacyLoadMessageDesc({0xc54cf0, 0xb38be0}, {0xb91aa2, 0x38})
        /usr/local/google/home/arjansbal/go/pkg/mod/google.golang.org/[email protected]/internal/impl/legacy_message.go:147 +0x4b4
google.golang.org/protobuf/internal/impl.legacyLoadMessageInfo({0xc54cf0, 0xb38be0}, {0xb91aa2, 0x38})
        /usr/local/google/home/arjansbal/go/pkg/mod/google.golang.org/[email protected]/internal/impl/legacy_message.go:56 +0x6c
google.golang.org/protobuf/internal/impl.legacyLoadMessageType({0xc54cf0, 0xb38be0}, {0xb91aa2, 0x38})

@arjan-bal arjan-bal merged commit 4bb0170 into grpc:master Oct 22, 2024
15 checks passed
@arjan-bal arjan-bal deleted the fix-status-incompatability branch October 22, 2024 17:46
purnesh42H pushed a commit to purnesh42H/grpc-go that referenced this pull request Oct 29, 2024
purnesh42H pushed a commit to purnesh42H/grpc-go that referenced this pull request Oct 30, 2024
purnesh42H added a commit that referenced this pull request Oct 30, 2024
misvivek pushed a commit to misvivek/grpc-go that referenced this pull request Nov 7, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

status/Details: unexpected code breaks after upgrading to v1.62.0 and higher
5 participants