182 lines
5.3 KiB
Go
182 lines
5.3 KiB
Go
/*
|
|
*
|
|
* Copyright 2020 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.
|
|
*
|
|
*/
|
|
|
|
package rls
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/golang/protobuf/proto"
|
|
"github.com/google/go-cmp/cmp"
|
|
"google.golang.org/grpc"
|
|
rlspb "google.golang.org/grpc/balancer/rls/internal/proto/grpc_lookup_v1"
|
|
"google.golang.org/grpc/balancer/rls/internal/testutils/fakeserver"
|
|
"google.golang.org/grpc/codes"
|
|
"google.golang.org/grpc/internal/testutils"
|
|
"google.golang.org/grpc/status"
|
|
)
|
|
|
|
const (
|
|
defaultDialTarget = "dummy"
|
|
defaultRPCTimeout = 5 * time.Second
|
|
)
|
|
|
|
func setup(t *testing.T) (*fakeserver.Server, *grpc.ClientConn, func()) {
|
|
t.Helper()
|
|
|
|
server, sCleanup, err := fakeserver.Start(nil)
|
|
if err != nil {
|
|
t.Fatalf("Failed to start fake RLS server: %v", err)
|
|
}
|
|
|
|
cc, cCleanup, err := server.ClientConn()
|
|
if err != nil {
|
|
sCleanup()
|
|
t.Fatalf("Failed to get a ClientConn to the RLS server: %v", err)
|
|
}
|
|
|
|
return server, cc, func() {
|
|
sCleanup()
|
|
cCleanup()
|
|
}
|
|
}
|
|
|
|
// TestLookupFailure verifies the case where the RLS server returns an error.
|
|
func (s) TestLookupFailure(t *testing.T) {
|
|
server, cc, cleanup := setup(t)
|
|
defer cleanup()
|
|
|
|
// We setup the fake server to return an error.
|
|
server.ResponseChan <- fakeserver.Response{Err: errors.New("rls failure")}
|
|
|
|
rlsClient := newRLSClient(cc, defaultDialTarget, defaultRPCTimeout)
|
|
|
|
errCh := testutils.NewChannel()
|
|
rlsClient.lookup("", nil, func(targets []string, headerData string, err error) {
|
|
if err == nil {
|
|
errCh.Send(errors.New("rlsClient.lookup() succeeded, should have failed"))
|
|
return
|
|
}
|
|
if len(targets) != 0 || headerData != "" {
|
|
errCh.Send(fmt.Errorf("rlsClient.lookup() = (%v, %s), want (nil, \"\")", targets, headerData))
|
|
return
|
|
}
|
|
errCh.Send(nil)
|
|
})
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
|
|
defer cancel()
|
|
if e, err := errCh.Receive(ctx); err != nil || e != nil {
|
|
t.Fatalf("lookup error: %v, error receiving from channel: %v", e, err)
|
|
}
|
|
}
|
|
|
|
// TestLookupDeadlineExceeded tests the case where the RPC deadline associated
|
|
// with the lookup expires.
|
|
func (s) TestLookupDeadlineExceeded(t *testing.T) {
|
|
_, cc, cleanup := setup(t)
|
|
defer cleanup()
|
|
|
|
// Give the Lookup RPC a small deadline, but don't setup the fake server to
|
|
// return anything. So the Lookup call will block and eventually expire.
|
|
rlsClient := newRLSClient(cc, defaultDialTarget, 100*time.Millisecond)
|
|
|
|
errCh := testutils.NewChannel()
|
|
rlsClient.lookup("", nil, func(_ []string, _ string, err error) {
|
|
if st, ok := status.FromError(err); !ok || st.Code() != codes.DeadlineExceeded {
|
|
errCh.Send(fmt.Errorf("rlsClient.lookup() returned error: %v, want %v", err, codes.DeadlineExceeded))
|
|
return
|
|
}
|
|
errCh.Send(nil)
|
|
})
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
|
|
defer cancel()
|
|
if e, err := errCh.Receive(ctx); err != nil || e != nil {
|
|
t.Fatalf("lookup error: %v, error receiving from channel: %v", e, err)
|
|
}
|
|
}
|
|
|
|
// TestLookupSuccess verifies the successful Lookup API case.
|
|
func (s) TestLookupSuccess(t *testing.T) {
|
|
server, cc, cleanup := setup(t)
|
|
defer cleanup()
|
|
|
|
const (
|
|
rlsReqPath = "/service/method"
|
|
wantHeaderData = "headerData"
|
|
)
|
|
|
|
rlsReqKeyMap := map[string]string{
|
|
"k1": "v1",
|
|
"k2": "v2",
|
|
}
|
|
wantLookupRequest := &rlspb.RouteLookupRequest{
|
|
Server: defaultDialTarget,
|
|
Path: rlsReqPath,
|
|
TargetType: "grpc",
|
|
KeyMap: rlsReqKeyMap,
|
|
}
|
|
wantRespTargets := []string{"us_east_1.firestore.googleapis.com"}
|
|
|
|
rlsClient := newRLSClient(cc, defaultDialTarget, defaultRPCTimeout)
|
|
|
|
errCh := testutils.NewChannel()
|
|
rlsClient.lookup(rlsReqPath, rlsReqKeyMap, func(targets []string, hd string, err error) {
|
|
if err != nil {
|
|
errCh.Send(fmt.Errorf("rlsClient.Lookup() failed: %v", err))
|
|
return
|
|
}
|
|
if !cmp.Equal(targets, wantRespTargets) || hd != wantHeaderData {
|
|
errCh.Send(fmt.Errorf("rlsClient.lookup() = (%v, %s), want (%v, %s)", targets, hd, wantRespTargets, wantHeaderData))
|
|
return
|
|
}
|
|
errCh.Send(nil)
|
|
})
|
|
|
|
// Make sure that the fake server received the expected RouteLookupRequest
|
|
// proto.
|
|
ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
|
|
defer cancel()
|
|
req, err := server.RequestChan.Receive(ctx)
|
|
if err != nil {
|
|
t.Fatalf("Timed out wile waiting for a RouteLookupRequest")
|
|
}
|
|
gotLookupRequest := req.(*rlspb.RouteLookupRequest)
|
|
if diff := cmp.Diff(wantLookupRequest, gotLookupRequest, cmp.Comparer(proto.Equal)); diff != "" {
|
|
t.Fatalf("RouteLookupRequest diff (-want, +got):\n%s", diff)
|
|
}
|
|
|
|
// We setup the fake server to return this response when it receives a
|
|
// request.
|
|
server.ResponseChan <- fakeserver.Response{
|
|
Resp: &rlspb.RouteLookupResponse{
|
|
Targets: wantRespTargets,
|
|
HeaderData: wantHeaderData,
|
|
},
|
|
}
|
|
|
|
if e, err := errCh.Receive(ctx); err != nil || e != nil {
|
|
t.Fatalf("lookup error: %v, error receiving from channel: %v", e, err)
|
|
}
|
|
}
|