From afa41061c816e2801df9b4a35a1e946544110e8b Mon Sep 17 00:00:00 2001 From: justinsb Date: Fri, 26 Apr 2024 11:30:56 -0400 Subject: [PATCH] tests: record grpc requests in our golden tests We aim to construct a readable version of the GRPC requests. --- config/tests/samples/create/harness.go | 42 +++++++++++++++++++++++++ mockgcp/common/operations/operations.go | 5 +++ mockgcp/mock_http_roundtrip.go | 13 ++++++++ 3 files changed, 60 insertions(+) diff --git a/config/tests/samples/create/harness.go b/config/tests/samples/create/harness.go index 0b765aa82bd..7c1ce6bd3a9 100644 --- a/config/tests/samples/create/harness.go +++ b/config/tests/samples/create/harness.go @@ -31,6 +31,9 @@ import ( "golang.org/x/oauth2/google" cloudresourcemanagerv1 "google.golang.org/api/cloudresourcemanager/v1" "google.golang.org/api/option" + "google.golang.org/grpc" + "google.golang.org/protobuf/encoding/protojson" + "google.golang.org/protobuf/proto" "gopkg.in/dnaeon/go-vcr.v3/recorder" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime/schema" @@ -264,11 +267,14 @@ func NewHarnessWithOptions(ctx context.Context, t *testing.T, opts *HarnessOptio } } + var mockCloudGRPCClientConnection *grpc.ClientConn if targetGCP := os.Getenv("E2E_GCP_TARGET"); targetGCP == "mock" { t.Logf("creating mock gcp") mockCloud := mockgcp.NewMockRoundTripper(t, h.client, storage.NewInMemoryStorage()) + mockCloudGRPCClientConnection = mockCloud.NewGRPCConnection(ctx) + roundTripper := http.RoundTripper(mockCloud) ctx = context.WithValue(ctx, httpRoundTripperKey, roundTripper) @@ -448,6 +454,42 @@ func NewHarnessWithOptions(ctx context.Context, t *testing.T, opts *HarnessOptio return ret } } else { + // Intercept (and log) GRPC requests + grpcUnaryInterceptor := func(ctx context.Context, method string, req, reply any, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error { + entry := &test.LogEntry{} + + entry.Request.URL = method + entry.Request.Method = "GRPC" + + if req != nil { + requestBytes, _ := protojson.Marshal(req.(proto.Message)) + entry.Request.Body = string(requestBytes) + } + + if mockCloudGRPCClientConnection != nil { + cc = mockCloudGRPCClientConnection + } + err := invoker(ctx, method, req, reply, cc, opts...) + + if reply != nil { + replyBytes, _ := protojson.Marshal(reply.(proto.Message)) + entry.Response.Body = string(replyBytes) + } + + if err != nil { + entry.Response.Status = fmt.Sprintf("error: %v", err) + } else { + entry.Response.Status = "OK" + } + + for _, eventSink := range eventSinks { + eventSink.AddHTTPEvent(ctx, entry) + } + return err + } + + transport_tpg.GRPCUnaryClientInterceptor = grpcUnaryInterceptor + // Intercept (and log) DCL requests if len(eventSinks) != 0 { if kccConfig.HTTPClient == nil { diff --git a/mockgcp/common/operations/operations.go b/mockgcp/common/operations/operations.go index 1bb815609f6..8039b940a0c 100644 --- a/mockgcp/common/operations/operations.go +++ b/mockgcp/common/operations/operations.go @@ -22,6 +22,7 @@ import ( pb "google.golang.org/genproto/googleapis/longrunning" rpcstatus "google.golang.org/genproto/googleapis/rpc/status" + "google.golang.org/grpc" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" "google.golang.org/protobuf/proto" @@ -44,6 +45,10 @@ func NewOperationsService(storage storage.Storage) *Operations { } } +func (s *Operations) RegisterGRPCServices(grpcServer *grpc.Server) { + pb.RegisterOperationsServer(grpcServer, s) +} + func (s *Operations) NewLRO(ctx context.Context) (*pb.Operation, error) { now := time.Now() millis := now.UnixMilli() diff --git a/mockgcp/mock_http_roundtrip.go b/mockgcp/mock_http_roundtrip.go index 59263d508e1..88b8575407b 100644 --- a/mockgcp/mock_http_roundtrip.go +++ b/mockgcp/mock_http_roundtrip.go @@ -28,6 +28,7 @@ import ( "google.golang.org/grpc" "google.golang.org/grpc/credentials/insecure" + "k8s.io/klog/v2" "sigs.k8s.io/controller-runtime/pkg/client" "github.com/GoogleCloudPlatform/k8s-config-connector/mockgcp/common" @@ -187,6 +188,18 @@ func NewMockRoundTripper(t *testing.T, k8sClient client.Client, storage storage. return mockRoundTripper } +func (m *mockRoundTripper) NewGRPCConnection(ctx context.Context) *grpc.ClientConn { + endpoint := m.grpcListener.Addr().String() + + var opts []grpc.DialOption + opts = append(opts, grpc.WithTransportCredentials(insecure.NewCredentials())) + conn, err := grpc.DialContext(ctx, endpoint, opts...) + if err != nil { + klog.Fatalf("error dialing grpc endpoint %q: %v", endpoint, err) + } + return conn +} + func (m *mockRoundTripper) prefilterRequest(req *http.Request) error { if req.Body != nil { var requestBody bytes.Buffer