Skip to content

Commit 1b54658

Browse files
committed
Fix status.Details()'s MessageV1 hadling change due to grpc#6919
1 parent 5f178a8 commit 1b54658

File tree

7 files changed

+177
-1
lines changed

7 files changed

+177
-1
lines changed

go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ require (
77
github.com/cncf/xds/go v0.0.0-20240905190251-b4127c9b8d78
88
github.com/envoyproxy/go-control-plane v0.13.0
99
github.com/golang/glog v1.2.2
10+
github.com/golang/protobuf v1.5.3
1011
github.com/google/go-cmp v0.6.0
1112
github.com/google/uuid v1.6.0
1213
golang.org/x/net v0.29.0

go.sum

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,10 @@ github.com/envoyproxy/protoc-gen-validate v1.1.0 h1:tntQDh69XqOCOZsDz0lVJQez/2L6
1616
github.com/envoyproxy/protoc-gen-validate v1.1.0/go.mod h1:sXRDRVmzEbkM7CVcM06s9shE/m23dg3wzjl0UWqJ2q4=
1717
github.com/golang/glog v1.2.2 h1:1+mZ9upx1Dh6FmUTFR1naJ77miKiXgALjWOZ3NVFPmY=
1818
github.com/golang/glog v1.2.2/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w=
19+
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
20+
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
21+
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
22+
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
1923
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
2024
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
2125
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
@@ -38,10 +42,13 @@ golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34=
3842
golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
3943
golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224=
4044
golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
45+
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
4146
google.golang.org/genproto/googleapis/api v0.0.0-20240903143218-8af14fe29dc1 h1:hjSy6tcFQZ171igDaN5QHOw2n6vx40juYbC/x67CEhc=
4247
google.golang.org/genproto/googleapis/api v0.0.0-20240903143218-8af14fe29dc1/go.mod h1:qpvKtACPCQhAdu3PyQgV4l3LMXZEtft7y8QcarRsp9I=
4348
google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1 h1:pPJltXNxVzT4pK9yD8vR9X75DaWYYmLGMsEvBfFQZzQ=
4449
google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU=
50+
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
51+
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
4552
google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
4653
google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
4754
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=

internal/status/status.go

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,8 @@ func (s *Status) WithDetails(details ...protoadapt.MessageV1) (*Status, error) {
149149

150150
// Details returns a slice of details messages attached to the status.
151151
// If a detail cannot be decoded, the error is returned in place of the detail.
152+
// If the detail can be decoded, the proto message returned is of the same
153+
// type that was given to WithDetails().
152154
func (s *Status) Details() []any {
153155
if s == nil || s.s == nil {
154156
return nil
@@ -160,7 +162,31 @@ func (s *Status) Details() []any {
160162
details = append(details, err)
161163
continue
162164
}
163-
details = append(details, detail)
165+
// The call to MessageV1Of is required to unwrap the proto message if
166+
// it implemented only the MessageV1 API. The proto message would have
167+
// been wrapped in a V2 wrapper in Status.WithDetails. V2 messages are
168+
// added to a global registry used by any.UnmarshalNew().
169+
// MessageV1Of has the following behaviour:
170+
// 1. If the given message is a wrapped MessageV1, it returns the
171+
// unwrapped value.
172+
// 2. If the given message already implements MessageV1, it returns it
173+
// as is.
174+
// 3. Else, it wraps the MessageV2 in a MessageV1 wrapper.
175+
//
176+
// Since the Status.WithDetails() API only accepts MessageV1, calling
177+
// MessageV1Of ensures we return the same type that was given to
178+
// WithDetails:
179+
// * If the give type implemented only MessageV1, the unwrapping from
180+
// point 1 above will restore the type.
181+
// * If the given type implemented both MessageV1 and MessageV2, point 2
182+
// above will ensure no wrapping is performed.
183+
// * If the given type implemented only MessageV2 and was wrapped using
184+
// MessageV1Of before passing to WithDetails(), it would be unwrapped
185+
// in WithDetails by calling MessageV2Of(). Point 3 above will ensure
186+
// that the type is wrapped in a MessageV1 wrapper again before
187+
// returning. Note that protoc-gen-go doesn't generate code which
188+
// implements ONLY MessageV2 at the time of writing.
189+
details = append(details, protoadapt.MessageV1Of(detail))
164190
}
165191
return details
166192
}

status/status_test.go

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,12 +31,14 @@ import (
3131
"google.golang.org/protobuf/proto"
3232
"google.golang.org/protobuf/protoadapt"
3333
"google.golang.org/protobuf/reflect/protoreflect"
34+
"google.golang.org/protobuf/testing/protocmp"
3435
"google.golang.org/protobuf/types/known/anypb"
3536
"google.golang.org/protobuf/types/known/durationpb"
3637

3738
"google.golang.org/grpc/codes"
3839
"google.golang.org/grpc/internal/grpctest"
3940
"google.golang.org/grpc/internal/status"
41+
tpb "google.golang.org/grpc/status/test/grpc_testing_not_regenerate"
4042
)
4143

4244
type s struct {
@@ -362,6 +364,34 @@ func (s) TestStatus_ErrorDetails(t *testing.T) {
362364
}
363365
}
364366

367+
// TestStatus_ErrorDetailsMessageV1 verifies backward compatibility of the
368+
// status.Details() method when using protobuf code generated with only the
369+
// MessageV1 API implementation.
370+
func (s) TestStatus_ErrorDetailsMessageV1(t *testing.T) {
371+
details := []protoadapt.MessageV1{
372+
&tpb.SimpleMessage{
373+
Data: "abc",
374+
},
375+
}
376+
s, err := New(codes.Aborted, "").WithDetails(details...)
377+
if err != nil {
378+
t.Fatalf("(%v).WithDetails(%+v) failed: %v", str(s), details, err)
379+
}
380+
got := s.Details()
381+
gotDetails := []protoadapt.MessageV1{}
382+
for i := range got {
383+
msg, ok := got[i].(protoadapt.MessageV1)
384+
if !ok {
385+
t.Fatalf("(%v).Details() returned message that doesn't implement protoadapt.MessageV1: %v", s, got[i])
386+
}
387+
gotDetails = append(gotDetails, msg)
388+
}
389+
390+
if diff := cmp.Diff(details, gotDetails, protocmp.Transform()); diff != "" {
391+
t.Errorf("(%v).Details got unexpected output, diff (-got +want):\n%s", s, diff)
392+
}
393+
}
394+
365395
func (s) TestStatus_WithDetails_Fail(t *testing.T) {
366396
tests := []*Status{
367397
nil,
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
`simple_message_v1.go` was generated using protoc-gen-go v1.3.5 which doesn't
2+
support the MesssageV2 API. As a result the generated code implements only the
3+
old MessageV1 API.
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
/*
2+
* Copyright 2022 gRPC authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*
16+
*/
17+
18+
syntax = "proto3";
19+
20+
package grpc.testing;
21+
22+
option go_package = "google.golang.org/grpc/status/test/grpc_testing_not_regenerate";
23+
24+
// SimpleMessage is used to hold string data.
25+
message SimpleMessage {
26+
string data = 1;
27+
}

status/test/grpc_testing_not_regenerate/simple_message_v1.go

Lines changed: 82 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)