Skip to content

Commit

Permalink
status: Fix status incompatibility introduced by #6919 and move non-r…
Browse files Browse the repository at this point in the history
…egeneratable proto code into /testdata (#7724) (#7793)
  • Loading branch information
purnesh42H authored Oct 30, 2024
1 parent b79fb61 commit 056dc64
Show file tree
Hide file tree
Showing 21 changed files with 236 additions and 52 deletions.
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ require (
github.com/cncf/xds/go v0.0.0-20240905190251-b4127c9b8d78
github.com/envoyproxy/go-control-plane v0.13.0
github.com/golang/glog v1.2.2
github.com/golang/protobuf v1.5.4
github.com/google/go-cmp v0.6.0
github.com/google/uuid v1.6.0
golang.org/x/net v0.29.0
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ github.com/envoyproxy/protoc-gen-validate v1.1.0 h1:tntQDh69XqOCOZsDz0lVJQez/2L6
github.com/envoyproxy/protoc-gen-validate v1.1.0/go.mod h1:sXRDRVmzEbkM7CVcM06s9shE/m23dg3wzjl0UWqJ2q4=
github.com/golang/glog v1.2.2 h1:1+mZ9upx1Dh6FmUTFR1naJ77miKiXgALjWOZ3NVFPmY=
github.com/golang/glog v1.2.2/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w=
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
Expand Down
35 changes: 34 additions & 1 deletion internal/status/status.go
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,8 @@ func (s *Status) WithDetails(details ...protoadapt.MessageV1) (*Status, error) {

// Details returns a slice of details messages attached to the status.
// If a detail cannot be decoded, the error is returned in place of the detail.
// If the detail can be decoded, the proto message returned is of the same
// type that was given to WithDetails().
func (s *Status) Details() []any {
if s == nil || s.s == nil {
return nil
Expand All @@ -160,7 +162,38 @@ func (s *Status) Details() []any {
details = append(details, err)
continue
}
details = append(details, detail)
// The call to MessageV1Of is required to unwrap the proto message if
// it implemented only the MessageV1 API. The proto message would have
// been wrapped in a V2 wrapper in Status.WithDetails. V2 messages are
// added to a global registry used by any.UnmarshalNew().
// MessageV1Of has the following behaviour:
// 1. If the given message is a wrapped MessageV1, it returns the
// unwrapped value.
// 2. If the given message already implements MessageV1, it returns it
// as is.
// 3. Else, it wraps the MessageV2 in a MessageV1 wrapper.
//
// Since the Status.WithDetails() API only accepts MessageV1, calling
// MessageV1Of ensures we return the same type that was given to
// WithDetails:
// * If the give type implemented only MessageV1, the unwrapping from
// point 1 above will restore the type.
// * If the given type implemented both MessageV1 and MessageV2, point 2
// above will ensure no wrapping is performed.
// * If the given type implemented only MessageV2 and was wrapped using
// MessageV1Of before passing to WithDetails(), it would be unwrapped
// in WithDetails by calling MessageV2Of(). Point 3 above will ensure
// that the type is wrapped in a MessageV1 wrapper again before
// returning. Note that protoc-gen-go doesn't generate code which
// implements ONLY MessageV2 at the time of writing.
//
// NOTE: Status details can also be added using the FromProto method.
// This could theoretically allow passing a Detail message that only
// implements the V2 API. In such a case the message will be wrapped in
// a MessageV1 wrapper when fetched using Details().
// Since protoc-gen-go generates only code that implements both V1 and
// V2 APIs for backward compatibility, this is not a concern.
details = append(details, protoadapt.MessageV1Of(detail))
}
return details
}
Expand Down
2 changes: 2 additions & 0 deletions interop/xds/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
Expand Down
18 changes: 0 additions & 18 deletions reflection/test/go.mod

This file was deleted.

14 changes: 0 additions & 14 deletions reflection/test/go.sum

This file was deleted.

2 changes: 1 addition & 1 deletion reflection/test/serverreflection_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ import (
v1reflectionpb "google.golang.org/grpc/reflection/grpc_reflection_v1"
v1alphareflectiongrpc "google.golang.org/grpc/reflection/grpc_reflection_v1alpha"
pb "google.golang.org/grpc/reflection/grpc_testing"
pbv3 "google.golang.org/grpc/reflection/test/grpc_testing_not_regenerate"
pbv3 "google.golang.org/grpc/testdata/grpc_testing_not_regenerated"
)

var (
Expand Down
12 changes: 6 additions & 6 deletions scripts/regenerate.sh
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,9 @@ export PATH="${GOBIN}:${PATH}"
mkdir -p "${GOBIN}"

echo "removing existing generated files..."
# grpc_testing_not_regenerate/*.pb.go is not re-generated,
# see grpc_testing_not_regenerate/README.md for details.
find . -name '*.pb.go' | grep -v 'grpc_testing_not_regenerate' | xargs rm -f || true
# grpc_testing_not_regenerated/*.pb.go is not re-generated,
# see grpc_testing_not_regenerated/README.md for details.
find . -name '*.pb.go' | grep -v 'grpc_testing_not_regenerated' | xargs rm -f || true

echo "Executing: go install google.golang.org/protobuf/cmd/protoc-gen-go..."
(cd test/tools && go install google.golang.org/protobuf/cmd/protoc-gen-go)
Expand Down Expand Up @@ -124,8 +124,8 @@ done
mkdir -p "${WORKDIR}/out/google.golang.org/grpc/internal/proto/grpc_lookup_v1"
mv "${WORKDIR}"/out/google.golang.org/grpc/lookup/grpc_lookup_v1/* "${WORKDIR}/out/google.golang.org/grpc/internal/proto/grpc_lookup_v1"

# grpc_testing_not_regenerate/*.pb.go are not re-generated,
# see grpc_testing_not_regenerate/README.md for details.
rm "${WORKDIR}"/out/google.golang.org/grpc/reflection/test/grpc_testing_not_regenerate/*.pb.go
# grpc_testing_not_regenerated/*.pb.go are not re-generated,
# see grpc_testing_not_regenerated/README.md for details.
rm "${WORKDIR}"/out/google.golang.org/grpc/testdata/grpc_testing_not_regenerated/*.pb.go

cp -R "${WORKDIR}"/out/google.golang.org/grpc/* .
10 changes: 5 additions & 5 deletions scripts/vet.sh
Original file line number Diff line number Diff line change
Expand Up @@ -56,13 +56,13 @@ git grep 'func [A-Z]' -- "*_test.go" | not grep -v 'func Test\|Benchmark\|Exampl
git grep -l 'time.After(' -- "*.go" | not grep -v '_test.go\|test_utils\|testutils'

# - Do not use "interface{}"; use "any" instead.
git grep -l 'interface{}' -- "*.go" 2>&1 | not grep -v '\.pb\.go\|protoc-gen-go-grpc\|grpc_testing_not_regenerate'
git grep -l 'interface{}' -- "*.go" 2>&1 | not grep -v '\.pb\.go\|protoc-gen-go-grpc\|grpc_testing_not_regenerated'

# - Do not call grpclog directly. Use grpclog.Component instead.
git grep -l -e 'grpclog.I' --or -e 'grpclog.W' --or -e 'grpclog.E' --or -e 'grpclog.F' --or -e 'grpclog.V' -- "*.go" | not grep -v '^grpclog/component.go\|^internal/grpctest/tlogger_test.go\|^internal/grpclog/prefix_logger.go'

# - Ensure that the deprecated protobuf dependency is not used.
not git grep "\"github.com/golang/protobuf/*" -- "*.go" ':(exclude)reflection/test/grpc_testing_not_regenerate/*'
not git grep "\"github.com/golang/protobuf/*" -- "*.go" ':(exclude)testdata/grpc_testing_not_regenerated/*'

# - Ensure all usages of grpc_testing package are renamed when importing.
not git grep "\(import \|^\s*\)\"google.golang.org/grpc/interop/grpc_testing" -- "*.go"
Expand Down Expand Up @@ -109,7 +109,7 @@ for MOD_FILE in $(find . -name 'go.mod'); do
noret_grep -v "(ST1000)" "${SC_OUT}" | noret_grep -v "(SA1019)" | noret_grep -v "(ST1003)" | noret_grep -v "(ST1019)\|\(other import of\)" | not grep -v "(SA4000)"

# Exclude underscore checks for generated code.
noret_grep "(ST1003)" "${SC_OUT}" | not grep -v '\(.pb.go:\)\|\(code_string_test.go:\)\|\(grpc_testing_not_regenerate\)'
noret_grep "(ST1003)" "${SC_OUT}" | not grep -v '\(.pb.go:\)\|\(code_string_test.go:\)\|\(grpc_testing_not_regenerated\)'

# Error for duplicate imports not including grpc protos.
noret_grep "(ST1019)\|\(other import of\)" "${SC_OUT}" | not grep -Fv 'XXXXX PleaseIgnoreUnused
Expand Down Expand Up @@ -146,7 +146,7 @@ XXXXX PleaseIgnoreUnused'
XXXXX Protobuf related deprecation errors:
"github.com/golang/protobuf
.pb.go:
grpc_testing_not_regenerate
grpc_testing_not_regenerated
: ptypes.
proto.RegisterType
XXXXX gRPC internal usage deprecation errors:
Expand Down Expand Up @@ -188,7 +188,7 @@ done
# Error for violation of enabled lint rules in config excluding generated code.
revive \
-set_exit_status=1 \
-exclude "reflection/test/grpc_testing_not_regenerate/" \
-exclude "testdata/grpc_testing_not_regenerated/" \
-exclude "**/*.pb.go" \
-formatter plain \
-config "$(dirname "$0")/revive.toml" \
Expand Down
2 changes: 2 additions & 0 deletions security/advancedtls/examples/go.sum
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
golang.org/x/crypto v0.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A=
Expand Down
2 changes: 2 additions & 0 deletions security/advancedtls/go.sum
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
golang.org/x/crypto v0.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A=
Expand Down
2 changes: 2 additions & 0 deletions stats/opencensus/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -859,6 +859,8 @@ github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaS
github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM=
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
Expand Down
2 changes: 2 additions & 0 deletions stats/opentelemetry/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
Expand Down
56 changes: 56 additions & 0 deletions status/status_ext_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ package status_test
import (
"context"
"errors"
"reflect"
"strings"
"testing"
"time"
Expand All @@ -35,8 +36,10 @@ import (
"google.golang.org/grpc/status"
"google.golang.org/protobuf/proto"
"google.golang.org/protobuf/protoadapt"
"google.golang.org/protobuf/testing/protocmp"

testpb "google.golang.org/grpc/interop/grpc_testing"
tpb "google.golang.org/grpc/testdata/grpc_testing_not_regenerated"
)

const defaultTestTimeout = 10 * time.Second
Expand Down Expand Up @@ -203,3 +206,56 @@ func (s) TestStatusDetails(t *testing.T) {
})
}
}

// TestStatus_ErrorDetailsMessageV1 verifies backward compatibility of the
// status.Details() method when using protobuf code generated with only the
// MessageV1 API implementation.
func (s) TestStatus_ErrorDetailsMessageV1(t *testing.T) {
details := []protoadapt.MessageV1{
&tpb.SimpleMessage{Data: "abc"},
}
s, err := status.New(codes.Aborted, "").WithDetails(details...)
if err != nil {
t.Fatalf("(%v).WithDetails(%+v) failed: %v", s, details, err)
}
gotDetails := s.Details()
for i, msg := range gotDetails {
if got, want := reflect.TypeOf(msg), reflect.TypeOf(details[i]); got != want {
t.Errorf("reflect.Typeof(%v) = %v, want = %v", msg, got, want)
}
if _, ok := msg.(protoadapt.MessageV1); !ok {
t.Errorf("(%v).Details() returned message that doesn't implement protoadapt.MessageV1: %v", s, msg)
}
if diff := cmp.Diff(msg, details[i], protocmp.Transform()); diff != "" {
t.Errorf("(%v).Details got unexpected output, diff (-got +want):\n%s", s, diff)
}
}
}

// TestStatus_ErrorDetailsMessageV1AndV2 verifies that status.Details() method
// returns the same message types when using protobuf code generated with both the
// MessageV1 and MessageV2 API implementations.
func (s) TestStatus_ErrorDetailsMessageV1AndV2(t *testing.T) {
details := []protoadapt.MessageV1{
&testpb.Empty{},
}
s, err := status.New(codes.Aborted, "").WithDetails(details...)
if err != nil {
t.Fatalf("(%v).WithDetails(%+v) failed: %v", s, details, err)
}
gotDetails := s.Details()
for i, msg := range gotDetails {
if got, want := reflect.TypeOf(msg), reflect.TypeOf(details[i]); got != want {
t.Errorf("reflect.Typeof(%v) = %v, want = %v", msg, got, want)
}
if _, ok := msg.(protoadapt.MessageV1); !ok {
t.Errorf("(%v).Details() returned message that doesn't implement protoadapt.MessageV1: %v", s, msg)
}
if _, ok := msg.(protoadapt.MessageV2); !ok {
t.Errorf("(%v).Details() returned message that doesn't implement protoadapt.MessageV2: %v", s, msg)
}
if diff := cmp.Diff(msg, details[i], protocmp.Transform()); diff != "" {
t.Errorf("(%v).Details got unexpected output, diff (-got +want):\n%s", s, diff)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,7 @@ with `"context"`.

`dynamic.go` was generated with a newer protoc and manually edited to remove
everything except the descriptor bytes var, which is renamed and exported.

`simple_message_v1.go` was generated using protoc-gen-go v1.3.5 which doesn't
support the MesssageV2 API. As a result the generated code implements only the
old MessageV1 API.
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
*
*/

package grpc_testing_not_regenerate
package grpc_testing_not_regenerated

// FileDynamicProtoRawDesc is the descriptor for dynamic.proto, see README.md.
var FileDynamicProtoRawDesc = []byte{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,10 @@

syntax = "proto3";

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

package grpc.testing;

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

message DynamicRes {}

message DynamicReq {}
Expand Down
27 changes: 27 additions & 0 deletions testdata/grpc_testing_not_regenerated/simple.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/*
* Copyright 2024 gRPC authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/

syntax = "proto3";

package grpc.testdata.grpc_testing_not_regenerated;

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

// SimpleMessage is used to hold string data.
message SimpleMessage {
string data = 1;
}
Loading

0 comments on commit 056dc64

Please sign in to comment.