diff --git a/conformance/tests/grpcroute-exact-method-matching.go b/conformance/tests/grpcroute-exact-method-matching.go index c0806b9907..35152ed93f 100644 --- a/conformance/tests/grpcroute-exact-method-matching.go +++ b/conformance/tests/grpcroute-exact-method-matching.go @@ -69,7 +69,7 @@ var GRPCExactMethodMatching = suite.ConformanceTest{ tc := testCases[i] t.Run(tc.GetTestCaseName(i), func(t *testing.T) { t.Parallel() - grpc.MakeRequestAndExpectEventuallyConsistentResponse(t, suite.TimeoutConfig, gwAddr, tc) + grpc.MakeRequestAndExpectEventuallyConsistentResponse(t, suite.GRPCClient, suite.TimeoutConfig, gwAddr, tc) }) } }, diff --git a/conformance/tests/grpcroute-header-matching.go b/conformance/tests/grpcroute-header-matching.go index 572d4525be..690efb47e8 100644 --- a/conformance/tests/grpcroute-header-matching.go +++ b/conformance/tests/grpcroute-header-matching.go @@ -130,7 +130,7 @@ var GRPCRouteHeaderMatching = suite.ConformanceTest{ tc := testCases[i] t.Run(tc.GetTestCaseName(i), func(t *testing.T) { t.Parallel() - grpc.MakeRequestAndExpectEventuallyConsistentResponse(t, suite.TimeoutConfig, gwAddr, tc) + grpc.MakeRequestAndExpectEventuallyConsistentResponse(t, suite.GRPCClient, suite.TimeoutConfig, gwAddr, tc) }) } }, diff --git a/conformance/tests/grpcroute-listener-hostname-matching.go b/conformance/tests/grpcroute-listener-hostname-matching.go index b4119c20e4..851c35789d 100644 --- a/conformance/tests/grpcroute-listener-hostname-matching.go +++ b/conformance/tests/grpcroute-listener-hostname-matching.go @@ -126,7 +126,7 @@ var GRPCRouteListenerHostnameMatching = suite.ConformanceTest{ tc := testCases[i] t.Run(tc.GetTestCaseName(i), func(t *testing.T) { t.Parallel() - grpc.MakeRequestAndExpectEventuallyConsistentResponse(t, suite.TimeoutConfig, gwAddr, tc) + grpc.MakeRequestAndExpectEventuallyConsistentResponse(t, suite.GRPCClient, suite.TimeoutConfig, gwAddr, tc) }) } }, diff --git a/conformance/utils/grpc/grpc.go b/conformance/utils/grpc/grpc.go index eea4be4820..a48109df31 100644 --- a/conformance/utils/grpc/grpc.go +++ b/conformance/utils/grpc/grpc.go @@ -42,6 +42,19 @@ const ( echoServerService = "GrpcEcho" ) +// Client is an interface used to make requests within conformance tests for grpc scenarios. +// This can be overridden with custom implementations whenever necessary. +type Client interface { + SendRPC(t *testing.T, address string, expected ExpectedResponse, timeout time.Duration) (*Response, error) + Close() +} + +// DefaultClient is the default implementation of Client. It will +// be used if a custom implementation is not specified. +type DefaultClient struct { + Conn *grpc.ClientConn +} + type Response struct { Code codes.Code Headers *metadata.MD @@ -136,19 +149,14 @@ func (er *ExpectedResponse) GetTestCaseName(i int) string { return fmt.Sprintf("%s should receive a %s (%d)", reqStr, er.Response.Code.String(), er.Response.Code) } -type client struct { - Conn *grpc.ClientConn - RequestMetadata *RequestMetadata -} - -func (c *client) ensureConnection(address string) error { +func (c *DefaultClient) ensureConnection(address string, req *RequestMetadata) error { if c.Conn != nil { return nil } var err error dialOpts := []grpc.DialOption{grpc.WithTransportCredentials(insecure.NewCredentials())} - if c.RequestMetadata != nil && c.RequestMetadata.Authority != "" { - dialOpts = append(dialOpts, grpc.WithAuthority(c.RequestMetadata.Authority)) + if req != nil && req.Authority != "" { + dialOpts = append(dialOpts, grpc.WithAuthority(req.Authority)) } c.Conn, err = grpc.Dial(address, dialOpts...) @@ -159,7 +167,7 @@ func (c *client) ensureConnection(address string) error { return nil } -func (c *client) resetConnection() { +func (c *DefaultClient) resetConnection() { if c.Conn == nil { return } @@ -167,9 +175,12 @@ func (c *client) resetConnection() { c.Conn = nil } -func (c *client) SendRPC(t *testing.T, address string, expected ExpectedResponse, timeout time.Duration) (*Response, error) { +// SendRPC sends a gRPC request to the given address with the expected response. +// An error will be returned if there is an error running the function but not if an HTTP error status code +// is received. +func (c *DefaultClient) SendRPC(t *testing.T, address string, expected ExpectedResponse, timeout time.Duration) (*Response, error) { t.Helper() - if err := c.ensureConnection(address); err != nil { + if err := c.ensureConnection(address, expected.RequestMetadata); err != nil { return &Response{}, err } @@ -179,8 +190,8 @@ func (c *client) SendRPC(t *testing.T, address string, expected ExpectedResponse } ctx, cancel := context.WithTimeout(context.Background(), timeout) - if c.RequestMetadata != nil && len(c.RequestMetadata.Metadata) > 0 { - ctx = metadata.NewOutgoingContext(ctx, metadata.New(c.RequestMetadata.Metadata)) + if expected.RequestMetadata != nil && len(expected.RequestMetadata.Metadata) > 0 { + ctx = metadata.NewOutgoingContext(ctx, metadata.New(expected.RequestMetadata.Metadata)) } defer cancel() @@ -215,7 +226,7 @@ func (c *client) SendRPC(t *testing.T, address string, expected ExpectedResponse return resp, nil } -func (c *client) Close() { +func (c *DefaultClient) Close() { if c.Conn != nil { c.Conn.Close() } @@ -256,13 +267,9 @@ func validateExpectedResponse(t *testing.T, expected ExpectedResponse) { require.Equal(t, 1, requestTypeCount, "expected only one request type to be set, but found %d: %v", requestTypeCount, expected) } -func MakeRequestAndExpectEventuallyConsistentResponse(t *testing.T, timeoutConfig config.TimeoutConfig, gwAddr string, expected ExpectedResponse) { +func MakeRequestAndExpectEventuallyConsistentResponse(t *testing.T, c Client, timeoutConfig config.TimeoutConfig, gwAddr string, expected ExpectedResponse) { t.Helper() validateExpectedResponse(t, expected) - c := &client{ - Conn: nil, - RequestMetadata: expected.RequestMetadata, - } defer c.Close() sendRPC := func(elapsed time.Duration) bool { resp, err := c.SendRPC(t, gwAddr, expected, timeoutConfig.MaxTimeToConsistency-elapsed) diff --git a/conformance/utils/suite/suite.go b/conformance/utils/suite/suite.go index bc945a517c..bd1e1fe08e 100644 --- a/conformance/utils/suite/suite.go +++ b/conformance/utils/suite/suite.go @@ -40,6 +40,7 @@ import ( confv1 "sigs.k8s.io/gateway-api/conformance/apis/v1" "sigs.k8s.io/gateway-api/conformance/utils/config" "sigs.k8s.io/gateway-api/conformance/utils/flags" + "sigs.k8s.io/gateway-api/conformance/utils/grpc" "sigs.k8s.io/gateway-api/conformance/utils/kubernetes" "sigs.k8s.io/gateway-api/conformance/utils/roundtripper" "sigs.k8s.io/gateway-api/conformance/utils/tlog" @@ -59,6 +60,7 @@ type ConformanceTestSuite struct { RESTClient *rest.RESTClient RestConfig *rest.Config RoundTripper roundtripper.RoundTripper + GRPCClient grpc.Client GatewayClassName string ControllerName string Debug bool @@ -125,6 +127,7 @@ type ConformanceOptions struct { GatewayClassName string Debug bool RoundTripper roundtripper.RoundTripper + GRPCClient grpc.Client BaseManifests string MeshManifests string NamespaceLabels map[string]string @@ -189,6 +192,11 @@ func NewConformanceTestSuite(options ConformanceOptions) (*ConformanceTestSuite, roundTripper = &roundtripper.DefaultRoundTripper{Debug: options.Debug, TimeoutConfig: options.TimeoutConfig} } + grpcClient := options.GRPCClient + if grpcClient == nil { + grpcClient = &grpc.DefaultClient{Conn: nil} + } + installedCRDs := &apiextensionsv1.CustomResourceDefinitionList{} err := options.Client.List(context.TODO(), installedCRDs) if err != nil { @@ -229,6 +237,7 @@ func NewConformanceTestSuite(options ConformanceOptions) (*ConformanceTestSuite, Clientset: options.Clientset, RestConfig: options.RestConfig, RoundTripper: roundTripper, + GRPCClient: grpcClient, GatewayClassName: options.GatewayClassName, Debug: options.Debug, Cleanup: options.CleanupBaseResources,