mirror of https://github.com/grpc/grpc-go.git
xds: client test cleanup. (#3284)
This commit is contained in:
parent
c618975385
commit
8a65b8453b
|
|
@ -34,7 +34,7 @@ import (
|
|||
xdsbalancer "google.golang.org/grpc/xds/internal/balancer"
|
||||
xdsclient "google.golang.org/grpc/xds/internal/client"
|
||||
"google.golang.org/grpc/xds/internal/testutils"
|
||||
"google.golang.org/grpc/xds/internal/testutils/fakexds"
|
||||
"google.golang.org/grpc/xds/internal/testutils/fakeclient"
|
||||
)
|
||||
|
||||
const (
|
||||
|
|
@ -55,7 +55,7 @@ type cdsWatchInfo struct {
|
|||
|
||||
// invokeWatchCb invokes the CDS watch callback registered by the cdsBalancer
|
||||
// and waits for appropriate state to be pushed to the provided edsBalancer.
|
||||
func invokeWatchCbAndWait(xdsC *fakexds.Client, cdsW cdsWatchInfo, wantCCS balancer.ClientConnState, edsB *testEDSBalancer) error {
|
||||
func invokeWatchCbAndWait(xdsC *fakeclient.Client, cdsW cdsWatchInfo, wantCCS balancer.ClientConnState, edsB *testEDSBalancer) error {
|
||||
xdsC.InvokeWatchClusterCallback(cdsW.update, cdsW.err)
|
||||
if cdsW.err != nil {
|
||||
return edsB.waitForResolverError(cdsW.err)
|
||||
|
|
@ -224,10 +224,10 @@ func setup() (*cdsBalancer, *testEDSBalancer, func()) {
|
|||
|
||||
// setupWithWatch does everything that setup does, and also pushes a ClientConn
|
||||
// update to the cdsBalancer and waits for a CDS watch call to be registered.
|
||||
func setupWithWatch(t *testing.T) (*fakexds.Client, *cdsBalancer, *testEDSBalancer, func()) {
|
||||
func setupWithWatch(t *testing.T) (*fakeclient.Client, *cdsBalancer, *testEDSBalancer, func()) {
|
||||
t.Helper()
|
||||
|
||||
xdsC := fakexds.NewClient()
|
||||
xdsC := fakeclient.NewClient()
|
||||
cdsB, edsB, cancel := setup()
|
||||
if err := cdsB.UpdateClientConnState(cdsCCS(clusterName, xdsC)); err != nil {
|
||||
t.Fatalf("cdsBalancer.UpdateClientConnState failed with error: %v", err)
|
||||
|
|
@ -246,7 +246,7 @@ func setupWithWatch(t *testing.T) (*fakexds.Client, *cdsBalancer, *testEDSBalanc
|
|||
// cdsBalancer with different inputs and verifies that the CDS watch API on the
|
||||
// provided xdsClient is invoked appropriately.
|
||||
func TestUpdateClientConnState(t *testing.T) {
|
||||
xdsC := fakexds.NewClient()
|
||||
xdsC := fakeclient.NewClient()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
|
|
@ -324,7 +324,7 @@ func TestUpdateClientConnStateAfterClose(t *testing.T) {
|
|||
defer cancel()
|
||||
cdsB.Close()
|
||||
|
||||
if err := cdsB.UpdateClientConnState(cdsCCS(clusterName, fakexds.NewClient())); err != errBalancerClosed {
|
||||
if err := cdsB.UpdateClientConnState(cdsCCS(clusterName, fakeclient.NewClient())); err != errBalancerClosed {
|
||||
t.Fatalf("UpdateClientConnState() after close returned %v, want %v", err, errBalancerClosed)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,7 +32,8 @@ import (
|
|||
xdsinternal "google.golang.org/grpc/xds/internal"
|
||||
xdsclient "google.golang.org/grpc/xds/internal/client"
|
||||
"google.golang.org/grpc/xds/internal/testutils"
|
||||
"google.golang.org/grpc/xds/internal/testutils/fakexds"
|
||||
"google.golang.org/grpc/xds/internal/testutils/fakeclient"
|
||||
"google.golang.org/grpc/xds/internal/testutils/fakeserver"
|
||||
)
|
||||
|
||||
const (
|
||||
|
|
@ -54,7 +55,7 @@ var (
|
|||
// * Sends updates with different edsServiceNames and expects new watches to be
|
||||
// registered.
|
||||
func (s) TestClientWrapperWatchEDS(t *testing.T) {
|
||||
fakeServer, cleanup, err := fakexds.StartServer()
|
||||
fakeServer, cleanup, err := fakeserver.StartServer()
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to start fake xDS server: %v", err)
|
||||
}
|
||||
|
|
@ -101,7 +102,7 @@ func (s) TestClientWrapperWatchEDS(t *testing.T) {
|
|||
if err != nil {
|
||||
t.Fatalf("EDS RPC failed with err: %v", err)
|
||||
}
|
||||
edsReq := req.(*fakexds.Request)
|
||||
edsReq := req.(*fakeserver.Request)
|
||||
if edsReq.Err != nil {
|
||||
t.Fatalf("EDS RPC failed with err: %v", edsReq.Err)
|
||||
}
|
||||
|
|
@ -123,9 +124,9 @@ func (s) TestClientWrapperWatchEDS(t *testing.T) {
|
|||
//
|
||||
// The test does the following:
|
||||
// * Creates a clientWrapper.
|
||||
// * Creates a fakexds.Client and passes it to the clientWrapper in attributes.
|
||||
// * Creates a fakeclient.Client and passes it to the clientWrapper in attributes.
|
||||
// * Verifies the clientWrapper registers an EDS watch.
|
||||
// * Forces the fakexds.Client to invoke the registered EDS watch callback with
|
||||
// * Forces the fakeclient.Client to invoke the registered EDS watch callback with
|
||||
// an error. Verifies that the wrapper does not invoke the top-level
|
||||
// edsBalancer with the received error.
|
||||
func (s) TestClientWrapperHandleUpdateError(t *testing.T) {
|
||||
|
|
@ -138,7 +139,7 @@ func (s) TestClientWrapperHandleUpdateError(t *testing.T) {
|
|||
cw := newXDSClientWrapper(newEDS, nil, balancer.BuildOptions{Target: resolver.Target{Endpoint: testServiceName}}, nil)
|
||||
defer cw.close()
|
||||
|
||||
xdsC := fakexds.NewClient()
|
||||
xdsC := fakeclient.NewClient()
|
||||
cw.handleUpdate(&XDSConfig{EDSServiceName: testEDSClusterName}, attributes.New(xdsinternal.XDSClientID, xdsC))
|
||||
gotCluster, err := xdsC.WaitForWatchEDS()
|
||||
if err != nil {
|
||||
|
|
@ -173,7 +174,7 @@ func (s) TestClientWrapperGetsXDSClientInAttributes(t *testing.T) {
|
|||
defer cw.close()
|
||||
|
||||
// Verify that the eds watch is registered for the expected resource name.
|
||||
xdsC1 := fakexds.NewClient()
|
||||
xdsC1 := fakeclient.NewClient()
|
||||
cw.handleUpdate(&XDSConfig{EDSServiceName: testEDSClusterName}, attributes.New(xdsinternal.XDSClientID, xdsC1))
|
||||
gotCluster, err := xdsC1.WaitForWatchEDS()
|
||||
if err != nil {
|
||||
|
|
@ -187,7 +188,7 @@ func (s) TestClientWrapperGetsXDSClientInAttributes(t *testing.T) {
|
|||
// re-registered on the new client, and that the old client is not closed
|
||||
// (because clientWrapper only closes clients that it creates, it does not
|
||||
// close client that are passed through attributes).
|
||||
xdsC2 := fakexds.NewClient()
|
||||
xdsC2 := fakeclient.NewClient()
|
||||
cw.handleUpdate(&XDSConfig{EDSServiceName: testEDSClusterName}, attributes.New(xdsinternal.XDSClientID, xdsC2))
|
||||
gotCluster, err = xdsC2.WaitForWatchEDS()
|
||||
if err != nil {
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ import (
|
|||
"google.golang.org/grpc/balancer"
|
||||
"google.golang.org/grpc/resolver"
|
||||
xdsinternal "google.golang.org/grpc/xds/internal"
|
||||
"google.golang.org/grpc/xds/internal/testutils/fakexds"
|
||||
"google.golang.org/grpc/xds/internal/testutils/fakeclient"
|
||||
)
|
||||
|
||||
// TestXDSLoadReporting verifies that the edsBalancer starts the loadReport
|
||||
|
|
@ -40,7 +40,7 @@ func (s) TestXDSLoadReporting(t *testing.T) {
|
|||
}
|
||||
defer edsB.Close()
|
||||
|
||||
xdsC := fakexds.NewClient()
|
||||
xdsC := fakeclient.NewClient()
|
||||
edsB.UpdateClientConnState(balancer.ClientConnState{
|
||||
ResolverState: resolver.State{Attributes: attributes.New(xdsinternal.XDSClientID, xdsC)},
|
||||
BalancerConfig: &XDSConfig{LrsLoadReportingServerName: new(string)},
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ import (
|
|||
xdsclient "google.golang.org/grpc/xds/internal/client"
|
||||
"google.golang.org/grpc/xds/internal/client/bootstrap"
|
||||
"google.golang.org/grpc/xds/internal/testutils"
|
||||
"google.golang.org/grpc/xds/internal/testutils/fakexds"
|
||||
"google.golang.org/grpc/xds/internal/testutils/fakeclient"
|
||||
)
|
||||
|
||||
var lbABuilder = &balancerABuilder{}
|
||||
|
|
@ -317,7 +317,7 @@ func (s) TestXDSFallbackResolvedAddrs(t *testing.T) {
|
|||
// waitForNewXDSClientWithEDSWatch makes sure that a new xdsClient is created
|
||||
// with the provided name. It also make sure that the newly created client
|
||||
// registers an eds watcher.
|
||||
func waitForNewXDSClientWithEDSWatch(t *testing.T, ch *testutils.Channel, wantName string) *fakexds.Client {
|
||||
func waitForNewXDSClientWithEDSWatch(t *testing.T, ch *testutils.Channel, wantName string) *fakeclient.Client {
|
||||
t.Helper()
|
||||
|
||||
val, err := ch.Receive()
|
||||
|
|
@ -325,7 +325,7 @@ func waitForNewXDSClientWithEDSWatch(t *testing.T, ch *testutils.Channel, wantNa
|
|||
t.Fatalf("error when waiting for a new xds client: %v", err)
|
||||
return nil
|
||||
}
|
||||
xdsC := val.(*fakexds.Client)
|
||||
xdsC := val.(*fakeclient.Client)
|
||||
if xdsC.Name() != wantName {
|
||||
t.Fatalf("xdsClient created to balancer: %v, want %v", xdsC.Name(), wantName)
|
||||
return nil
|
||||
|
|
@ -365,7 +365,7 @@ func setup(edsLBCh *testutils.Channel, xdsClientCh *testutils.Channel) func() {
|
|||
|
||||
origXdsClientNew := xdsclientNew
|
||||
xdsclientNew = func(opts xdsclient.Options) (xdsClientInterface, error) {
|
||||
xdsC := fakexds.NewClientWithName(opts.Config.BalancerName)
|
||||
xdsC := fakeclient.NewClientWithName(opts.Config.BalancerName)
|
||||
defer func() { xdsClientCh.Send(xdsC) }()
|
||||
return xdsC, nil
|
||||
}
|
||||
|
|
@ -527,7 +527,7 @@ func (s) TestXDSConnfigChildPolicyUpdate(t *testing.T) {
|
|||
// will not kick-in as yet.
|
||||
// * Sends another ClientConn update with fallback addresses. Still fallback
|
||||
// would not have kicked in because the startupTimeout hasn't expired.
|
||||
// * Sends an EDSUpdate through the fakexds.Client object. This will trigger
|
||||
// * Sends an EDSUpdate through the fakeclient.Client object. This will trigger
|
||||
// the creation of an edsLB object. This is verified.
|
||||
// * Trigger fallback by directly calling the loseContact method on the
|
||||
// top-level edsBalancer. This should instantiate the fallbackLB and should
|
||||
|
|
|
|||
|
|
@ -25,11 +25,13 @@ import (
|
|||
"testing"
|
||||
"time"
|
||||
|
||||
discoverypb "github.com/envoyproxy/go-control-plane/envoy/api/v2"
|
||||
xdspb "github.com/envoyproxy/go-control-plane/envoy/api/v2"
|
||||
corepb "github.com/envoyproxy/go-control-plane/envoy/api/v2/core"
|
||||
"github.com/golang/protobuf/proto"
|
||||
anypb "github.com/golang/protobuf/ptypes/any"
|
||||
"google.golang.org/grpc/xds/internal/client/fakexds"
|
||||
"google.golang.org/grpc/xds/internal/testutils"
|
||||
"google.golang.org/grpc/xds/internal/testutils/fakeserver"
|
||||
)
|
||||
|
||||
const (
|
||||
|
|
@ -164,13 +166,10 @@ func TestValidateCluster(t *testing.T) {
|
|||
// and creates a v2Client using it. Then, it registers a CDS watcher and tests
|
||||
// different CDS responses.
|
||||
func TestCDSHandleResponse(t *testing.T) {
|
||||
fakeServer, sCleanup := fakexds.StartServer(t)
|
||||
client, cCleanup := fakeServer.GetClientConn(t)
|
||||
defer func() {
|
||||
cCleanup()
|
||||
sCleanup()
|
||||
}()
|
||||
v2c := newV2Client(client, goodNodeProto, func(int) time.Duration { return 0 })
|
||||
fakeServer, cc, cleanup := startServerAndGetCC(t)
|
||||
defer cleanup()
|
||||
|
||||
v2c := newV2Client(cc, goodNodeProto, func(int) time.Duration { return 0 })
|
||||
defer v2c.close()
|
||||
|
||||
tests := []struct {
|
||||
|
|
@ -230,7 +229,7 @@ func TestCDSHandleResponse(t *testing.T) {
|
|||
wantUpdateErr: test.wantUpdateErr,
|
||||
|
||||
cdsWatch: v2c.watchCDS,
|
||||
watchReqChan: fakeServer.RequestChan,
|
||||
watchReqChan: fakeServer.XDSRequestChan,
|
||||
handleXDSResp: v2c.handleCDSResponse,
|
||||
})
|
||||
})
|
||||
|
|
@ -240,13 +239,10 @@ func TestCDSHandleResponse(t *testing.T) {
|
|||
// TestCDSHandleResponseWithoutWatch tests the case where the v2Client receives
|
||||
// a CDS response without a registered watcher.
|
||||
func TestCDSHandleResponseWithoutWatch(t *testing.T) {
|
||||
fakeServer, sCleanup := fakexds.StartServer(t)
|
||||
client, cCleanup := fakeServer.GetClientConn(t)
|
||||
defer func() {
|
||||
cCleanup()
|
||||
sCleanup()
|
||||
}()
|
||||
v2c := newV2Client(client, goodNodeProto, func(int) time.Duration { return 0 })
|
||||
_, cc, cleanup := startServerAndGetCC(t)
|
||||
defer cleanup()
|
||||
|
||||
v2c := newV2Client(cc, goodNodeProto, func(int) time.Duration { return 0 })
|
||||
defer v2c.close()
|
||||
|
||||
if v2c.handleCDSResponse(goodCDSResponse1) == nil {
|
||||
|
|
@ -260,7 +256,7 @@ type cdsTestOp struct {
|
|||
// target is the resource name to watch for.
|
||||
target string
|
||||
// responseToSend is the xDS response sent to the client
|
||||
responseToSend *fakexds.Response
|
||||
responseToSend *fakeserver.Response
|
||||
// wantOpErr specfies whether the main operation should return an error.
|
||||
wantOpErr bool
|
||||
// wantCDSCache is the expected rdsCache at the end of an operation.
|
||||
|
|
@ -273,16 +269,13 @@ type cdsTestOp struct {
|
|||
// ClientConn to it, creates a v2Client using it. It then reads a bunch of
|
||||
// test operations to be performed from cdsTestOps and returns error, if any,
|
||||
// on the provided error channel. This is executed in a separate goroutine.
|
||||
func testCDSCaching(t *testing.T, cdsTestOps []cdsTestOp, errCh chan error) {
|
||||
func testCDSCaching(t *testing.T, cdsTestOps []cdsTestOp, errCh *testutils.Channel) {
|
||||
t.Helper()
|
||||
|
||||
fakeServer, sCleanup := fakexds.StartServer(t)
|
||||
client, cCleanup := fakeServer.GetClientConn(t)
|
||||
defer func() {
|
||||
cCleanup()
|
||||
sCleanup()
|
||||
}()
|
||||
v2c := newV2Client(client, goodNodeProto, func(int) time.Duration { return 0 })
|
||||
fakeServer, cc, cleanup := startServerAndGetCC(t)
|
||||
defer cleanup()
|
||||
|
||||
v2c := newV2Client(cc, goodNodeProto, func(int) time.Duration { return 0 })
|
||||
defer v2c.close()
|
||||
t.Log("Started xds v2Client...")
|
||||
|
||||
|
|
@ -299,15 +292,19 @@ func testCDSCaching(t *testing.T, cdsTestOps []cdsTestOp, errCh chan error) {
|
|||
|
||||
// Wait till the request makes it to the fakeServer. This ensures that
|
||||
// the watch request has been processed by the v2Client.
|
||||
<-fakeServer.RequestChan
|
||||
if _, err := fakeServer.XDSRequestChan.Receive(); err != nil {
|
||||
errCh.Send(fmt.Errorf("Timeout waiting for CDS request: %v", err))
|
||||
return
|
||||
}
|
||||
t.Log("FakeServer received request...")
|
||||
}
|
||||
|
||||
// Directly push the response through a call to handleCDSResponse,
|
||||
// thereby bypassing the fakeServer.
|
||||
if cdsTestOp.responseToSend != nil {
|
||||
if err := v2c.handleCDSResponse(cdsTestOp.responseToSend.Resp); (err != nil) != cdsTestOp.wantOpErr {
|
||||
errCh <- fmt.Errorf("v2c.handleCDSResponse() returned err: %v", err)
|
||||
resp := cdsTestOp.responseToSend.Resp.(*discoverypb.DiscoveryResponse)
|
||||
if err := v2c.handleCDSResponse(resp); (err != nil) != cdsTestOp.wantOpErr {
|
||||
errCh.Send(fmt.Errorf("v2c.handleRDSResponse(%+v) returned err: %v", resp, err))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
|
@ -320,24 +317,23 @@ func testCDSCaching(t *testing.T, cdsTestOps []cdsTestOp, errCh chan error) {
|
|||
}
|
||||
|
||||
if !reflect.DeepEqual(v2c.cloneCDSCacheForTesting(), cdsTestOp.wantCDSCache) {
|
||||
errCh <- fmt.Errorf("gotCDSCache: %v, wantCDSCache: %v", v2c.rdsCache, cdsTestOp.wantCDSCache)
|
||||
errCh.Send(fmt.Errorf("gotCDSCache: %v, wantCDSCache: %v", v2c.rdsCache, cdsTestOp.wantCDSCache))
|
||||
return
|
||||
}
|
||||
}
|
||||
t.Log("Completed all test ops successfully...")
|
||||
errCh <- nil
|
||||
errCh.Send(nil)
|
||||
}
|
||||
|
||||
// TestCDSCaching tests some end-to-end CDS flows using a fake xDS server, and
|
||||
// verifies the CDS data cached at the v2Client.
|
||||
func TestCDSCaching(t *testing.T) {
|
||||
errCh := make(chan error, 1)
|
||||
ops := []cdsTestOp{
|
||||
// Add an CDS watch for a cluster name (clusterName1), which returns one
|
||||
// matching resource in the response.
|
||||
{
|
||||
target: clusterName1,
|
||||
responseToSend: &fakexds.Response{Resp: goodCDSResponse1},
|
||||
responseToSend: &fakeserver.Response{Resp: goodCDSResponse1},
|
||||
wantCDSCache: map[string]CDSUpdate{
|
||||
clusterName1: {serviceName1, true},
|
||||
},
|
||||
|
|
@ -346,7 +342,7 @@ func TestCDSCaching(t *testing.T) {
|
|||
// Push an CDS response which contains a new resource (apart from the
|
||||
// one received in the previous response). This should be cached.
|
||||
{
|
||||
responseToSend: &fakexds.Response{Resp: cdsResponseWithMultipleResources},
|
||||
responseToSend: &fakeserver.Response{Resp: cdsResponseWithMultipleResources},
|
||||
wantCDSCache: map[string]CDSUpdate{
|
||||
clusterName1: {serviceName1, true},
|
||||
clusterName2: {serviceName2, false},
|
||||
|
|
@ -366,24 +362,15 @@ func TestCDSCaching(t *testing.T) {
|
|||
},
|
||||
// Push an empty CDS response. This should clear the cache.
|
||||
{
|
||||
responseToSend: &fakexds.Response{Resp: &xdspb.DiscoveryResponse{TypeUrl: cdsURL}},
|
||||
responseToSend: &fakeserver.Response{Resp: &xdspb.DiscoveryResponse{TypeUrl: cdsURL}},
|
||||
wantOpErr: false,
|
||||
wantCDSCache: map[string]CDSUpdate{},
|
||||
wantWatchCallback: true,
|
||||
},
|
||||
}
|
||||
errCh := testutils.NewChannel()
|
||||
go testCDSCaching(t, ops, errCh)
|
||||
|
||||
timer := time.NewTimer(defaultTestTimeout)
|
||||
select {
|
||||
case <-timer.C:
|
||||
t.Fatal("Timeout when expecting CDS update")
|
||||
case err := <-errCh:
|
||||
timer.Stop()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
waitForNilErr(t, errCh)
|
||||
}
|
||||
|
||||
// TestCDSWatchExpiryTimer tests the case where the client does not receive an
|
||||
|
|
@ -391,44 +378,36 @@ func TestCDSCaching(t *testing.T) {
|
|||
// to be invoked with an error once the watchExpiryTimer fires.
|
||||
func TestCDSWatchExpiryTimer(t *testing.T) {
|
||||
oldWatchExpiryTimeout := defaultWatchExpiryTimeout
|
||||
defaultWatchExpiryTimeout = 1 * time.Second
|
||||
defaultWatchExpiryTimeout = 500 * time.Millisecond
|
||||
defer func() {
|
||||
defaultWatchExpiryTimeout = oldWatchExpiryTimeout
|
||||
}()
|
||||
|
||||
fakeServer, sCleanup := fakexds.StartServer(t)
|
||||
client, cCleanup := fakeServer.GetClientConn(t)
|
||||
defer func() {
|
||||
cCleanup()
|
||||
sCleanup()
|
||||
}()
|
||||
v2c := newV2Client(client, goodNodeProto, func(int) time.Duration { return 0 })
|
||||
fakeServer, cc, cleanup := startServerAndGetCC(t)
|
||||
defer cleanup()
|
||||
|
||||
v2c := newV2Client(cc, goodNodeProto, func(int) time.Duration { return 0 })
|
||||
defer v2c.close()
|
||||
t.Log("Started xds v2Client...")
|
||||
|
||||
cdsCallbackCh := make(chan error, 1)
|
||||
callbackCh := testutils.NewChannel()
|
||||
v2c.watchCDS(clusterName1, func(u CDSUpdate, err error) {
|
||||
t.Logf("Received callback with CDSUpdate {%+v} and error {%v}", u, err)
|
||||
if u.ServiceName != "" {
|
||||
cdsCallbackCh <- fmt.Errorf("received serviceName %v in cdsCallback, wanted empty string", u.ServiceName)
|
||||
callbackCh.Send(fmt.Errorf("received serviceName %v in cdsCallback, wanted empty string", u.ServiceName))
|
||||
}
|
||||
if err == nil {
|
||||
cdsCallbackCh <- errors.New("received nil error in cdsCallback")
|
||||
callbackCh.Send(errors.New("received nil error in cdsCallback"))
|
||||
}
|
||||
cdsCallbackCh <- nil
|
||||
callbackCh.Send(nil)
|
||||
})
|
||||
<-fakeServer.RequestChan
|
||||
|
||||
timer := time.NewTimer(2 * time.Second)
|
||||
select {
|
||||
case <-timer.C:
|
||||
t.Fatalf("Timeout expired when expecting CDS update")
|
||||
case err := <-cdsCallbackCh:
|
||||
timer.Stop()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
// Wait till the request makes it to the fakeServer. This ensures that
|
||||
// the watch request has been processed by the v2Client.
|
||||
if _, err := fakeServer.XDSRequestChan.Receive(); err != nil {
|
||||
t.Fatalf("Timeout expired when expecting an CDS request")
|
||||
}
|
||||
waitForNilErr(t, callbackCh)
|
||||
}
|
||||
|
||||
var (
|
||||
|
|
|
|||
|
|
@ -26,7 +26,8 @@ import (
|
|||
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/xds/internal/client/bootstrap"
|
||||
"google.golang.org/grpc/xds/internal/client/fakexds"
|
||||
"google.golang.org/grpc/xds/internal/testutils"
|
||||
"google.golang.org/grpc/xds/internal/testutils/fakeserver"
|
||||
|
||||
corepb "github.com/envoyproxy/go-control-plane/envoy/api/v2/core"
|
||||
)
|
||||
|
|
@ -45,7 +46,10 @@ func clientOpts(balancerName string) Options {
|
|||
}
|
||||
|
||||
func TestNew(t *testing.T) {
|
||||
fakeServer, cleanup := fakexds.StartServer(t)
|
||||
fakeServer, cleanup, err := fakeserver.StartServer()
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to start fake xDS server: %v", err)
|
||||
}
|
||||
defer cleanup()
|
||||
|
||||
tests := []struct {
|
||||
|
|
@ -107,7 +111,10 @@ func TestNew(t *testing.T) {
|
|||
// TestWatchService tests the happy case of registering a watcher for
|
||||
// service updates and receiving a good update.
|
||||
func TestWatchService(t *testing.T) {
|
||||
fakeServer, cleanup := fakexds.StartServer(t)
|
||||
fakeServer, cleanup, err := fakeserver.StartServer()
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to start fake xDS server: %v", err)
|
||||
}
|
||||
defer cleanup()
|
||||
|
||||
xdsClient, err := New(clientOpts(fakeServer.Address))
|
||||
|
|
@ -117,37 +124,33 @@ func TestWatchService(t *testing.T) {
|
|||
defer xdsClient.Close()
|
||||
t.Log("Created an xdsClient...")
|
||||
|
||||
callbackCh := make(chan error, 1)
|
||||
callbackCh := testutils.NewChannel()
|
||||
cancelWatch := xdsClient.WatchService(goodLDSTarget1, func(su ServiceUpdate, err error) {
|
||||
if err != nil {
|
||||
callbackCh <- fmt.Errorf("xdsClient.WatchService returned error: %v", err)
|
||||
callbackCh.Send(fmt.Errorf("xdsClient.WatchService returned error: %v", err))
|
||||
return
|
||||
}
|
||||
if su.Cluster != goodClusterName1 {
|
||||
callbackCh <- fmt.Errorf("got clusterName: %+v, want clusterName: %+v", su.Cluster, goodClusterName1)
|
||||
callbackCh.Send(fmt.Errorf("got clusterName: %+v, want clusterName: %+v", su.Cluster, goodClusterName1))
|
||||
return
|
||||
}
|
||||
callbackCh <- nil
|
||||
callbackCh.Send(nil)
|
||||
})
|
||||
defer cancelWatch()
|
||||
t.Log("Registered a watcher for service updates...")
|
||||
|
||||
// Make the fakeServer send LDS and RDS responses.
|
||||
<-fakeServer.RequestChan
|
||||
fakeServer.ResponseChan <- &fakexds.Response{Resp: goodLDSResponse1}
|
||||
<-fakeServer.RequestChan
|
||||
fakeServer.ResponseChan <- &fakexds.Response{Resp: goodRDSResponse1}
|
||||
|
||||
timer := time.NewTimer(defaultTestTimeout)
|
||||
select {
|
||||
case <-timer.C:
|
||||
t.Fatal("Timeout when expecting a service update")
|
||||
case err := <-callbackCh:
|
||||
timer.Stop()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
// Make the fakeServer send LDS response.
|
||||
if _, err := fakeServer.XDSRequestChan.Receive(); err != nil {
|
||||
t.Fatalf("Timeout expired when expecting an LDS request")
|
||||
}
|
||||
fakeServer.XDSResponseChan <- &fakeserver.Response{Resp: goodLDSResponse1}
|
||||
|
||||
// Make the fakeServer send RDS response.
|
||||
if _, err := fakeServer.XDSRequestChan.Receive(); err != nil {
|
||||
t.Fatalf("Timeout expired when expecting an RDS request")
|
||||
}
|
||||
fakeServer.XDSResponseChan <- &fakeserver.Response{Resp: goodRDSResponse1}
|
||||
waitForNilErr(t, callbackCh)
|
||||
}
|
||||
|
||||
// TestWatchServiceWithNoResponseFromServer tests the case where the
|
||||
|
|
@ -155,7 +158,10 @@ func TestWatchService(t *testing.T) {
|
|||
// registering a service update watcher. The underlying v2Client will timeout
|
||||
// and will send us an error.
|
||||
func TestWatchServiceWithNoResponseFromServer(t *testing.T) {
|
||||
fakeServer, cleanup := fakexds.StartServer(t)
|
||||
fakeServer, cleanup, err := fakeserver.StartServer()
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to start fake xDS server: %v", err)
|
||||
}
|
||||
defer cleanup()
|
||||
|
||||
xdsClient, err := New(clientOpts(fakeServer.Address))
|
||||
|
|
@ -166,45 +172,40 @@ func TestWatchServiceWithNoResponseFromServer(t *testing.T) {
|
|||
t.Log("Created an xdsClient...")
|
||||
|
||||
oldWatchExpiryTimeout := defaultWatchExpiryTimeout
|
||||
defaultWatchExpiryTimeout = 1 * time.Second
|
||||
defaultWatchExpiryTimeout = 500 * time.Millisecond
|
||||
defer func() {
|
||||
defaultWatchExpiryTimeout = oldWatchExpiryTimeout
|
||||
}()
|
||||
|
||||
callbackCh := make(chan error, 1)
|
||||
callbackCh := testutils.NewChannel()
|
||||
cancelWatch := xdsClient.WatchService(goodLDSTarget1, func(su ServiceUpdate, err error) {
|
||||
if su.Cluster != "" {
|
||||
callbackCh <- fmt.Errorf("got clusterName: %+v, want empty clusterName", su.Cluster)
|
||||
callbackCh.Send(fmt.Errorf("got clusterName: %+v, want empty clusterName", su.Cluster))
|
||||
return
|
||||
}
|
||||
if err == nil {
|
||||
callbackCh <- errors.New("xdsClient.WatchService returned error non-nil error")
|
||||
callbackCh.Send(errors.New("xdsClient.WatchService returned error non-nil error"))
|
||||
return
|
||||
}
|
||||
callbackCh <- nil
|
||||
callbackCh.Send(nil)
|
||||
})
|
||||
defer cancelWatch()
|
||||
t.Log("Registered a watcher for service updates...")
|
||||
|
||||
// Wait for one request from the client, but send no reponses.
|
||||
<-fakeServer.RequestChan
|
||||
|
||||
timer := time.NewTimer(2 * time.Second)
|
||||
select {
|
||||
case <-timer.C:
|
||||
t.Fatal("Timeout when expecting a service update")
|
||||
case err := <-callbackCh:
|
||||
timer.Stop()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err := fakeServer.XDSRequestChan.Receive(); err != nil {
|
||||
t.Fatalf("Timeout expired when expecting an LDS request")
|
||||
}
|
||||
waitForNilErr(t, callbackCh)
|
||||
}
|
||||
|
||||
// TestWatchServiceEmptyRDS tests the case where the underlying
|
||||
// v2Client receives an empty RDS response.
|
||||
func TestWatchServiceEmptyRDS(t *testing.T) {
|
||||
fakeServer, cleanup := fakexds.StartServer(t)
|
||||
fakeServer, cleanup, err := fakeserver.StartServer()
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to start fake xDS server: %v", err)
|
||||
}
|
||||
defer cleanup()
|
||||
|
||||
xdsClient, err := New(clientOpts(fakeServer.Address))
|
||||
|
|
@ -215,49 +216,48 @@ func TestWatchServiceEmptyRDS(t *testing.T) {
|
|||
t.Log("Created an xdsClient...")
|
||||
|
||||
oldWatchExpiryTimeout := defaultWatchExpiryTimeout
|
||||
defaultWatchExpiryTimeout = 1 * time.Second
|
||||
defaultWatchExpiryTimeout = 500 * time.Millisecond
|
||||
defer func() {
|
||||
defaultWatchExpiryTimeout = oldWatchExpiryTimeout
|
||||
}()
|
||||
|
||||
callbackCh := make(chan error, 1)
|
||||
callbackCh := testutils.NewChannel()
|
||||
cancelWatch := xdsClient.WatchService(goodLDSTarget1, func(su ServiceUpdate, err error) {
|
||||
if su.Cluster != "" {
|
||||
callbackCh <- fmt.Errorf("got clusterName: %+v, want empty clusterName", su.Cluster)
|
||||
callbackCh.Send(fmt.Errorf("got clusterName: %+v, want empty clusterName", su.Cluster))
|
||||
return
|
||||
}
|
||||
if err == nil {
|
||||
callbackCh <- errors.New("xdsClient.WatchService returned error non-nil error")
|
||||
callbackCh.Send(errors.New("xdsClient.WatchService returned error non-nil error"))
|
||||
return
|
||||
}
|
||||
callbackCh <- nil
|
||||
callbackCh.Send(nil)
|
||||
})
|
||||
defer cancelWatch()
|
||||
t.Log("Registered a watcher for service updates...")
|
||||
|
||||
// Send a good LDS response, but send an empty RDS response.
|
||||
<-fakeServer.RequestChan
|
||||
fakeServer.ResponseChan <- &fakexds.Response{Resp: goodLDSResponse1}
|
||||
<-fakeServer.RequestChan
|
||||
fakeServer.ResponseChan <- &fakexds.Response{Resp: noVirtualHostsInRDSResponse}
|
||||
|
||||
timer := time.NewTimer(2 * time.Second)
|
||||
select {
|
||||
case <-timer.C:
|
||||
t.Fatal("Timeout when expecting a service update")
|
||||
case err := <-callbackCh:
|
||||
timer.Stop()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
// Make the fakeServer send LDS response.
|
||||
if _, err := fakeServer.XDSRequestChan.Receive(); err != nil {
|
||||
t.Fatalf("Timeout expired when expecting an LDS request")
|
||||
}
|
||||
fakeServer.XDSResponseChan <- &fakeserver.Response{Resp: goodLDSResponse1}
|
||||
|
||||
// Make the fakeServer send an empty RDS response.
|
||||
if _, err := fakeServer.XDSRequestChan.Receive(); err != nil {
|
||||
t.Fatalf("Timeout expired when expecting an RDS request")
|
||||
}
|
||||
fakeServer.XDSResponseChan <- &fakeserver.Response{Resp: noVirtualHostsInRDSResponse}
|
||||
waitForNilErr(t, callbackCh)
|
||||
}
|
||||
|
||||
// TestWatchServiceWithClientClose tests the case where xDS responses are
|
||||
// received after the client is closed, and we make sure that the registered
|
||||
// watcher callback is not invoked.
|
||||
func TestWatchServiceWithClientClose(t *testing.T) {
|
||||
fakeServer, cleanup := fakexds.StartServer(t)
|
||||
fakeServer, cleanup, err := fakeserver.StartServer()
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to start fake xDS server: %v", err)
|
||||
}
|
||||
defer cleanup()
|
||||
|
||||
xdsClient, err := New(clientOpts(fakeServer.Address))
|
||||
|
|
@ -267,29 +267,26 @@ func TestWatchServiceWithClientClose(t *testing.T) {
|
|||
defer xdsClient.Close()
|
||||
t.Log("Created an xdsClient...")
|
||||
|
||||
callbackCh := make(chan error, 1)
|
||||
callbackCh := testutils.NewChannel()
|
||||
cancelWatch := xdsClient.WatchService(goodLDSTarget1, func(su ServiceUpdate, err error) {
|
||||
callbackCh <- errors.New("watcher callback invoked after client close")
|
||||
callbackCh.Send(errors.New("watcher callback invoked after client close"))
|
||||
})
|
||||
defer cancelWatch()
|
||||
t.Log("Registered a watcher for service updates...")
|
||||
|
||||
// Make the fakeServer send LDS response.
|
||||
<-fakeServer.RequestChan
|
||||
fakeServer.ResponseChan <- &fakexds.Response{Resp: goodLDSResponse1}
|
||||
if _, err := fakeServer.XDSRequestChan.Receive(); err != nil {
|
||||
t.Fatalf("Timeout expired when expecting an LDS request")
|
||||
}
|
||||
fakeServer.XDSResponseChan <- &fakeserver.Response{Resp: goodLDSResponse1}
|
||||
|
||||
xdsClient.Close()
|
||||
t.Log("Closing the xdsClient...")
|
||||
|
||||
// Push an RDS response from the fakeserver
|
||||
fakeServer.ResponseChan <- &fakexds.Response{Resp: goodRDSResponse1}
|
||||
|
||||
timer := time.NewTimer(1 * time.Second)
|
||||
select {
|
||||
case <-timer.C:
|
||||
// Do nothing. Success.
|
||||
case err := <-callbackCh:
|
||||
timer.Stop()
|
||||
t.Fatal(err)
|
||||
fakeServer.XDSResponseChan <- &fakeserver.Response{Resp: goodRDSResponse1}
|
||||
if cbErr, err := callbackCh.Receive(); err != testutils.ErrRecvTimeout {
|
||||
t.Fatal(cbErr)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ import (
|
|||
anypb "github.com/golang/protobuf/ptypes/any"
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"google.golang.org/grpc/xds/internal"
|
||||
"google.golang.org/grpc/xds/internal/client/fakexds"
|
||||
"google.golang.org/grpc/xds/internal/testutils"
|
||||
)
|
||||
|
||||
func TestEDSParseRespProto(t *testing.T) {
|
||||
|
|
@ -162,13 +162,10 @@ var (
|
|||
)
|
||||
|
||||
func TestEDSHandleResponse(t *testing.T) {
|
||||
fakeServer, sCleanup := fakexds.StartServer(t)
|
||||
client, cCleanup := fakeServer.GetClientConn(t)
|
||||
defer func() {
|
||||
cCleanup()
|
||||
sCleanup()
|
||||
}()
|
||||
v2c := newV2Client(client, goodNodeProto, func(int) time.Duration { return 0 })
|
||||
fakeServer, cc, cleanup := startServerAndGetCC(t)
|
||||
defer cleanup()
|
||||
|
||||
v2c := newV2Client(cc, goodNodeProto, func(int) time.Duration { return 0 })
|
||||
defer v2c.close()
|
||||
|
||||
tests := []struct {
|
||||
|
|
@ -235,7 +232,7 @@ func TestEDSHandleResponse(t *testing.T) {
|
|||
wantUpdateErr: test.wantUpdateErr,
|
||||
|
||||
edsWatch: v2c.watchEDS,
|
||||
watchReqChan: fakeServer.RequestChan,
|
||||
watchReqChan: fakeServer.XDSRequestChan,
|
||||
handleXDSResp: v2c.handleEDSResponse,
|
||||
})
|
||||
})
|
||||
|
|
@ -245,13 +242,10 @@ func TestEDSHandleResponse(t *testing.T) {
|
|||
// TestEDSHandleResponseWithoutWatch tests the case where the v2Client
|
||||
// receives an EDS response without a registered EDS watcher.
|
||||
func TestEDSHandleResponseWithoutWatch(t *testing.T) {
|
||||
fakeServer, sCleanup := fakexds.StartServer(t)
|
||||
client, cCleanup := fakeServer.GetClientConn(t)
|
||||
defer func() {
|
||||
cCleanup()
|
||||
sCleanup()
|
||||
}()
|
||||
v2c := newV2Client(client, goodNodeProto, func(int) time.Duration { return 0 })
|
||||
_, cc, cleanup := startServerAndGetCC(t)
|
||||
defer cleanup()
|
||||
|
||||
v2c := newV2Client(cc, goodNodeProto, func(int) time.Duration { return 0 })
|
||||
defer v2c.close()
|
||||
|
||||
if v2c.handleEDSResponse(goodEDSResponse1) == nil {
|
||||
|
|
@ -261,42 +255,34 @@ func TestEDSHandleResponseWithoutWatch(t *testing.T) {
|
|||
|
||||
func TestEDSWatchExpiryTimer(t *testing.T) {
|
||||
oldWatchExpiryTimeout := defaultWatchExpiryTimeout
|
||||
defaultWatchExpiryTimeout = 1 * time.Second
|
||||
defaultWatchExpiryTimeout = 500 * time.Millisecond
|
||||
defer func() {
|
||||
defaultWatchExpiryTimeout = oldWatchExpiryTimeout
|
||||
}()
|
||||
|
||||
fakeServer, sCleanup := fakexds.StartServer(t)
|
||||
client, cCleanup := fakeServer.GetClientConn(t)
|
||||
defer func() {
|
||||
cCleanup()
|
||||
sCleanup()
|
||||
}()
|
||||
v2c := newV2Client(client, goodNodeProto, func(int) time.Duration { return 0 })
|
||||
fakeServer, cc, cleanup := startServerAndGetCC(t)
|
||||
defer cleanup()
|
||||
|
||||
v2c := newV2Client(cc, goodNodeProto, func(int) time.Duration { return 0 })
|
||||
defer v2c.close()
|
||||
t.Log("Started xds v2Client...")
|
||||
|
||||
edsCallbackCh := make(chan error, 1)
|
||||
callbackCh := testutils.NewChannel()
|
||||
v2c.watchEDS(goodRouteName1, func(u *EDSUpdate, err error) {
|
||||
t.Logf("Received callback with edsUpdate {%+v} and error {%v}", u, err)
|
||||
if u != nil {
|
||||
edsCallbackCh <- fmt.Errorf("received EDSUpdate %v in edsCallback, wanted nil", u)
|
||||
callbackCh.Send(fmt.Errorf("received EDSUpdate %v in edsCallback, wanted nil", u))
|
||||
}
|
||||
if err == nil {
|
||||
edsCallbackCh <- errors.New("received nil error in edsCallback")
|
||||
callbackCh.Send(errors.New("received nil error in edsCallback"))
|
||||
}
|
||||
edsCallbackCh <- nil
|
||||
callbackCh.Send(nil)
|
||||
})
|
||||
<-fakeServer.RequestChan
|
||||
|
||||
timer := time.NewTimer(2 * time.Second)
|
||||
select {
|
||||
case <-timer.C:
|
||||
t.Fatalf("Timeout expired when expecting EDS update")
|
||||
case err := <-edsCallbackCh:
|
||||
timer.Stop()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
// Wait till the request makes it to the fakeServer. This ensures that
|
||||
// the watch request has been processed by the v2Client.
|
||||
if _, err := fakeServer.XDSRequestChan.Receive(); err != nil {
|
||||
t.Fatalf("Timeout expired when expecting an CDS request")
|
||||
}
|
||||
waitForNilErr(t, callbackCh)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,164 +0,0 @@
|
|||
/*
|
||||
*
|
||||
* Copyright 2019 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 fakexds provides a very basic fake implementation of the xDS server
|
||||
// for unit testing purposes.
|
||||
package fakexds
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
|
||||
discoverypb "github.com/envoyproxy/go-control-plane/envoy/api/v2"
|
||||
adsgrpc "github.com/envoyproxy/go-control-plane/envoy/service/discovery/v2"
|
||||
lrsgrpc "github.com/envoyproxy/go-control-plane/envoy/service/load_stats/v2"
|
||||
)
|
||||
|
||||
// TODO: Make this a var or a field in the server if there is a need to use a
|
||||
// value other than this default.
|
||||
const defaultChannelBufferSize = 50
|
||||
|
||||
// Request wraps an xDS request and error.
|
||||
type Request struct {
|
||||
Req *discoverypb.DiscoveryRequest
|
||||
Err error
|
||||
}
|
||||
|
||||
// Response wraps an xDS response and error.
|
||||
type Response struct {
|
||||
Resp *discoverypb.DiscoveryResponse
|
||||
Err error
|
||||
}
|
||||
|
||||
// Server is a very basic implementation of a fake xDS server. It provides a
|
||||
// request and response channel for the user to control the requests that are
|
||||
// expected and the responses that needs to be sent out.
|
||||
type Server struct {
|
||||
// RequestChan is a buffered channel on which the fake server writes the
|
||||
// received requests onto.
|
||||
RequestChan chan *Request
|
||||
// ResponseChan is a buffered channel from which the fake server reads the
|
||||
// responses that it must send out to the client.
|
||||
ResponseChan chan *Response
|
||||
// Address is the host:port on which the fake xdsServer is listening on.
|
||||
Address string
|
||||
// LRS is the LRS server installed.
|
||||
LRS *LRSServer
|
||||
}
|
||||
|
||||
// StartServer starts a fakexds.Server. The returned function should be invoked
|
||||
// by the caller once the test is done.
|
||||
func StartServer(t *testing.T) (*Server, func()) {
|
||||
t.Helper()
|
||||
|
||||
lis, err := net.Listen("tcp", "localhost:0")
|
||||
if err != nil {
|
||||
t.Fatalf("net.Listen() failed: %v", err)
|
||||
}
|
||||
server := grpc.NewServer()
|
||||
|
||||
lrss := newLRSServer()
|
||||
lrsgrpc.RegisterLoadReportingServiceServer(server, lrss)
|
||||
|
||||
fs := &Server{
|
||||
RequestChan: make(chan *Request, defaultChannelBufferSize),
|
||||
ResponseChan: make(chan *Response, defaultChannelBufferSize),
|
||||
Address: lis.Addr().String(),
|
||||
LRS: lrss,
|
||||
}
|
||||
adsgrpc.RegisterAggregatedDiscoveryServiceServer(server, fs)
|
||||
|
||||
go server.Serve(lis)
|
||||
t.Logf("Starting fake xDS server at %v...", fs.Address)
|
||||
|
||||
return fs, func() { server.Stop() }
|
||||
}
|
||||
|
||||
// GetClientConn returns a grpc.ClientConn talking to the fake server. The
|
||||
// returned function should be invoked by the caller once the test is done.
|
||||
func (fs *Server) GetClientConn(t *testing.T) (*grpc.ClientConn, func()) {
|
||||
t.Helper()
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||
defer cancel()
|
||||
|
||||
cc, err := grpc.DialContext(ctx, fs.Address, grpc.WithInsecure(), grpc.WithBlock())
|
||||
if err != nil {
|
||||
t.Fatalf("grpc.DialContext(%s) failed: %v", fs.Address, err)
|
||||
}
|
||||
t.Log("Started xDS gRPC client...")
|
||||
|
||||
return cc, func() {
|
||||
cc.Close()
|
||||
}
|
||||
}
|
||||
|
||||
// StreamAggregatedResources is the fake implementation to handle an ADS
|
||||
// stream.
|
||||
func (fs *Server) StreamAggregatedResources(s adsgrpc.AggregatedDiscoveryService_StreamAggregatedResourcesServer) error {
|
||||
errCh := make(chan error, 2)
|
||||
go func() {
|
||||
for {
|
||||
req, err := s.Recv()
|
||||
if err != nil {
|
||||
errCh <- err
|
||||
return
|
||||
}
|
||||
fs.RequestChan <- &Request{req, err}
|
||||
}
|
||||
}()
|
||||
go func() {
|
||||
var retErr error
|
||||
defer func() {
|
||||
errCh <- retErr
|
||||
}()
|
||||
|
||||
for {
|
||||
select {
|
||||
case r := <-fs.ResponseChan:
|
||||
if r.Err != nil {
|
||||
retErr = r.Err
|
||||
return
|
||||
}
|
||||
if err := s.Send(r.Resp); err != nil {
|
||||
retErr = err
|
||||
return
|
||||
}
|
||||
case <-s.Context().Done():
|
||||
retErr = s.Context().Err()
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
if err := <-errCh; err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeltaAggregatedResources helps implement the ADS service.
|
||||
func (fs *Server) DeltaAggregatedResources(adsgrpc.AggregatedDiscoveryService_DeltaAggregatedResourcesServer) error {
|
||||
return status.Error(codes.Unimplemented, "")
|
||||
}
|
||||
|
|
@ -1,104 +0,0 @@
|
|||
/*
|
||||
*
|
||||
* Copyright 2019 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 fakexds
|
||||
|
||||
import (
|
||||
"io"
|
||||
"sync"
|
||||
|
||||
corepb "github.com/envoyproxy/go-control-plane/envoy/api/v2/core"
|
||||
endpointpb "github.com/envoyproxy/go-control-plane/envoy/api/v2/endpoint"
|
||||
lrsgrpc "github.com/envoyproxy/go-control-plane/envoy/service/load_stats/v2"
|
||||
lrspb "github.com/envoyproxy/go-control-plane/envoy/service/load_stats/v2"
|
||||
"github.com/golang/protobuf/proto"
|
||||
durationpb "github.com/golang/protobuf/ptypes/duration"
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
)
|
||||
|
||||
// LRSServer implements the LRS service, and is to be installed on the fakexds
|
||||
// server. It collects load reports, and returned them later for comparison.
|
||||
type LRSServer struct {
|
||||
// ReportingInterval will be sent in the first response to control reporting
|
||||
// interval.
|
||||
ReportingInterval *durationpb.Duration
|
||||
// ExpectedEDSClusterName is checked against the first LRS request. The RPC
|
||||
// is failed if they don't match.
|
||||
ExpectedEDSClusterName string
|
||||
|
||||
mu sync.Mutex
|
||||
dropTotal uint64
|
||||
drops map[string]uint64
|
||||
}
|
||||
|
||||
func newLRSServer() *LRSServer {
|
||||
return &LRSServer{
|
||||
drops: make(map[string]uint64),
|
||||
ReportingInterval: &durationpb.Duration{
|
||||
Seconds: 60 * 60, // 1 hour, each test can override this to a shorter duration.
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// StreamLoadStats implements LRS service.
|
||||
func (lrss *LRSServer) StreamLoadStats(stream lrsgrpc.LoadReportingService_StreamLoadStatsServer) error {
|
||||
req, err := stream.Recv()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
wantReq := &lrspb.LoadStatsRequest{
|
||||
ClusterStats: []*endpointpb.ClusterStats{{
|
||||
ClusterName: lrss.ExpectedEDSClusterName,
|
||||
}},
|
||||
Node: &corepb.Node{},
|
||||
}
|
||||
if !proto.Equal(req, wantReq) {
|
||||
return status.Errorf(codes.FailedPrecondition, "unexpected req: %+v, want %+v, diff: %s", req, wantReq, cmp.Diff(req, wantReq, cmp.Comparer(proto.Equal)))
|
||||
}
|
||||
if err := stream.Send(&lrspb.LoadStatsResponse{
|
||||
Clusters: []string{lrss.ExpectedEDSClusterName},
|
||||
LoadReportingInterval: lrss.ReportingInterval,
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for {
|
||||
req, err := stream.Recv()
|
||||
if err != nil {
|
||||
if err == io.EOF {
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
stats := req.ClusterStats[0]
|
||||
lrss.mu.Lock()
|
||||
lrss.dropTotal += stats.TotalDroppedRequests
|
||||
for _, d := range stats.DroppedRequests {
|
||||
lrss.drops[d.Category] += d.DroppedCount
|
||||
}
|
||||
lrss.mu.Unlock()
|
||||
}
|
||||
}
|
||||
|
||||
// GetDrops returns the drops reported to this server.
|
||||
func (lrss *LRSServer) GetDrops() map[string]uint64 {
|
||||
lrss.mu.Lock()
|
||||
defer lrss.mu.Unlock()
|
||||
return lrss.drops
|
||||
}
|
||||
|
|
@ -25,7 +25,7 @@ import (
|
|||
"time"
|
||||
|
||||
xdspb "github.com/envoyproxy/go-control-plane/envoy/api/v2"
|
||||
"google.golang.org/grpc/xds/internal/client/fakexds"
|
||||
"google.golang.org/grpc/xds/internal/testutils"
|
||||
)
|
||||
|
||||
func TestLDSGetRouteConfig(t *testing.T) {
|
||||
|
|
@ -90,13 +90,10 @@ func TestLDSGetRouteConfig(t *testing.T) {
|
|||
// and creates a v2Client using it. Then, it registers a watchLDS and tests
|
||||
// different LDS responses.
|
||||
func TestLDSHandleResponse(t *testing.T) {
|
||||
fakeServer, sCleanup := fakexds.StartServer(t)
|
||||
client, cCleanup := fakeServer.GetClientConn(t)
|
||||
defer func() {
|
||||
cCleanup()
|
||||
sCleanup()
|
||||
}()
|
||||
v2c := newV2Client(client, goodNodeProto, func(int) time.Duration { return 0 })
|
||||
fakeServer, cc, cleanup := startServerAndGetCC(t)
|
||||
defer cleanup()
|
||||
|
||||
v2c := newV2Client(cc, goodNodeProto, func(int) time.Duration { return 0 })
|
||||
defer v2c.close()
|
||||
|
||||
tests := []struct {
|
||||
|
|
@ -186,7 +183,7 @@ func TestLDSHandleResponse(t *testing.T) {
|
|||
wantUpdateErr: test.wantUpdateErr,
|
||||
|
||||
ldsWatch: v2c.watchLDS,
|
||||
watchReqChan: fakeServer.RequestChan,
|
||||
watchReqChan: fakeServer.XDSRequestChan,
|
||||
handleXDSResp: v2c.handleLDSResponse,
|
||||
})
|
||||
})
|
||||
|
|
@ -196,13 +193,10 @@ func TestLDSHandleResponse(t *testing.T) {
|
|||
// TestLDSHandleResponseWithoutWatch tests the case where the v2Client receives
|
||||
// an LDS response without a registered watcher.
|
||||
func TestLDSHandleResponseWithoutWatch(t *testing.T) {
|
||||
fakeServer, sCleanup := fakexds.StartServer(t)
|
||||
client, cCleanup := fakeServer.GetClientConn(t)
|
||||
defer func() {
|
||||
cCleanup()
|
||||
sCleanup()
|
||||
}()
|
||||
v2c := newV2Client(client, goodNodeProto, func(int) time.Duration { return 0 })
|
||||
_, cc, cleanup := startServerAndGetCC(t)
|
||||
defer cleanup()
|
||||
|
||||
v2c := newV2Client(cc, goodNodeProto, func(int) time.Duration { return 0 })
|
||||
defer v2c.close()
|
||||
|
||||
if v2c.handleLDSResponse(goodLDSResponse1) == nil {
|
||||
|
|
@ -215,43 +209,33 @@ func TestLDSHandleResponseWithoutWatch(t *testing.T) {
|
|||
// to be invoked with an error once the watchExpiryTimer fires.
|
||||
func TestLDSWatchExpiryTimer(t *testing.T) {
|
||||
oldWatchExpiryTimeout := defaultWatchExpiryTimeout
|
||||
defaultWatchExpiryTimeout = 1 * time.Second
|
||||
defaultWatchExpiryTimeout = 500 * time.Millisecond
|
||||
defer func() {
|
||||
defaultWatchExpiryTimeout = oldWatchExpiryTimeout
|
||||
}()
|
||||
|
||||
fakeServer, sCleanup := fakexds.StartServer(t)
|
||||
client, cCleanup := fakeServer.GetClientConn(t)
|
||||
defer func() {
|
||||
cCleanup()
|
||||
sCleanup()
|
||||
}()
|
||||
v2c := newV2Client(client, goodNodeProto, func(int) time.Duration { return 0 })
|
||||
fakeServer, cc, cleanup := startServerAndGetCC(t)
|
||||
defer cleanup()
|
||||
|
||||
v2c := newV2Client(cc, goodNodeProto, func(int) time.Duration { return 0 })
|
||||
defer v2c.close()
|
||||
|
||||
// Wait till the request makes it to the fakeServer. This ensures that
|
||||
// the watch request has been processed by the v2Client.
|
||||
callbackCh := make(chan error, 1)
|
||||
callbackCh := testutils.NewChannel()
|
||||
v2c.watchLDS(goodLDSTarget1, func(u ldsUpdate, err error) {
|
||||
t.Logf("in v2c.watchLDS callback, ldsUpdate: %+v, err: %v", u, err)
|
||||
if u.routeName != "" {
|
||||
callbackCh <- fmt.Errorf("received routeName %v in ldsCallback, wanted empty string", u.routeName)
|
||||
callbackCh.Send(fmt.Errorf("received routeName %v in ldsCallback, wanted empty string", u.routeName))
|
||||
}
|
||||
if err == nil {
|
||||
callbackCh <- errors.New("received nil error in ldsCallback")
|
||||
callbackCh.Send(errors.New("received nil error in ldsCallback"))
|
||||
}
|
||||
callbackCh <- nil
|
||||
callbackCh.Send(nil)
|
||||
})
|
||||
<-fakeServer.RequestChan
|
||||
|
||||
timer := time.NewTimer(2 * time.Second)
|
||||
select {
|
||||
case <-timer.C:
|
||||
t.Fatalf("Timeout expired when expecting LDS update")
|
||||
case err := <-callbackCh:
|
||||
timer.Stop()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
// Wait till the request makes it to the fakeServer. This ensures that
|
||||
// the watch request has been processed by the v2Client.
|
||||
if _, err := fakeServer.XDSRequestChan.Receive(); err != nil {
|
||||
t.Fatalf("Timeout expired when expecting an LDS request")
|
||||
}
|
||||
waitForNilErr(t, callbackCh)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,9 +25,11 @@ import (
|
|||
"testing"
|
||||
"time"
|
||||
|
||||
discoverypb "github.com/envoyproxy/go-control-plane/envoy/api/v2"
|
||||
xdspb "github.com/envoyproxy/go-control-plane/envoy/api/v2"
|
||||
routepb "github.com/envoyproxy/go-control-plane/envoy/api/v2/route"
|
||||
"google.golang.org/grpc/xds/internal/client/fakexds"
|
||||
"google.golang.org/grpc/xds/internal/testutils"
|
||||
"google.golang.org/grpc/xds/internal/testutils/fakeserver"
|
||||
)
|
||||
|
||||
func (v2c *v2Client) cloneRDSCacheForTesting() map[string]string {
|
||||
|
|
@ -165,34 +167,36 @@ func TestRDSGetClusterFromRouteConfiguration(t *testing.T) {
|
|||
// This is called by RDS tests to start LDS first, because LDS is a
|
||||
// pre-requirement for RDS, and RDS handle would fail without an existing LDS
|
||||
// watch.
|
||||
func doLDS(t *testing.T, v2c *v2Client, fakeServer *fakexds.Server) {
|
||||
func doLDS(t *testing.T, v2c *v2Client, fakeServer *fakeserver.Server) {
|
||||
// Register an LDS watcher, and wait till the request is sent out, the
|
||||
// response is received and the callback is invoked.
|
||||
cbCh := make(chan error, 1)
|
||||
cbCh := testutils.NewChannel()
|
||||
v2c.watchLDS(goodLDSTarget1, func(u ldsUpdate, err error) {
|
||||
t.Logf("v2c.watchLDS callback, ldsUpdate: %+v, err: %v", u, err)
|
||||
cbCh <- err
|
||||
cbCh.Send(err)
|
||||
})
|
||||
<-fakeServer.RequestChan
|
||||
fakeServer.ResponseChan <- &fakexds.Response{Resp: goodLDSResponse1}
|
||||
if err := <-cbCh; err != nil {
|
||||
t.Fatalf("v2c.watchLDS returned error in callback: %v", err)
|
||||
|
||||
if _, err := fakeServer.XDSRequestChan.Receive(); err != nil {
|
||||
t.Fatalf("Timeout waiting for LDS request: %v", err)
|
||||
}
|
||||
|
||||
fakeServer.XDSResponseChan <- &fakeserver.Response{Resp: goodLDSResponse1}
|
||||
waitForNilErr(t, cbCh)
|
||||
|
||||
// Read the LDS ack, to clear RequestChan for following tests.
|
||||
<-fakeServer.RequestChan
|
||||
if _, err := fakeServer.XDSRequestChan.Receive(); err != nil {
|
||||
t.Fatalf("Timeout waiting for LDS ACK: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// TestRDSHandleResponse starts a fake xDS server, makes a ClientConn to it,
|
||||
// and creates a v2Client using it. Then, it registers an LDS and RDS watcher
|
||||
// and tests different RDS responses.
|
||||
func TestRDSHandleResponse(t *testing.T) {
|
||||
fakeServer, sCleanup := fakexds.StartServer(t)
|
||||
client, cCleanup := fakeServer.GetClientConn(t)
|
||||
defer func() {
|
||||
cCleanup()
|
||||
sCleanup()
|
||||
}()
|
||||
v2c := newV2Client(client, goodNodeProto, func(int) time.Duration { return 0 })
|
||||
fakeServer, cc, cleanup := startServerAndGetCC(t)
|
||||
defer cleanup()
|
||||
|
||||
v2c := newV2Client(cc, goodNodeProto, func(int) time.Duration { return 0 })
|
||||
defer v2c.close()
|
||||
doLDS(t, v2c, fakeServer)
|
||||
|
||||
|
|
@ -256,7 +260,7 @@ func TestRDSHandleResponse(t *testing.T) {
|
|||
wantUpdateErr: test.wantUpdateErr,
|
||||
|
||||
rdsWatch: v2c.watchRDS,
|
||||
watchReqChan: fakeServer.RequestChan,
|
||||
watchReqChan: fakeServer.XDSRequestChan,
|
||||
handleXDSResp: v2c.handleRDSResponse,
|
||||
})
|
||||
})
|
||||
|
|
@ -266,13 +270,10 @@ func TestRDSHandleResponse(t *testing.T) {
|
|||
// TestRDSHandleResponseWithoutLDSWatch tests the case where the v2Client
|
||||
// receives an RDS response without a registered LDS watcher.
|
||||
func TestRDSHandleResponseWithoutLDSWatch(t *testing.T) {
|
||||
fakeServer, sCleanup := fakexds.StartServer(t)
|
||||
client, cCleanup := fakeServer.GetClientConn(t)
|
||||
defer func() {
|
||||
cCleanup()
|
||||
sCleanup()
|
||||
}()
|
||||
v2c := newV2Client(client, goodNodeProto, func(int) time.Duration { return 0 })
|
||||
_, cc, cleanup := startServerAndGetCC(t)
|
||||
defer cleanup()
|
||||
|
||||
v2c := newV2Client(cc, goodNodeProto, func(int) time.Duration { return 0 })
|
||||
defer v2c.close()
|
||||
|
||||
if v2c.handleRDSResponse(goodRDSResponse1) == nil {
|
||||
|
|
@ -283,13 +284,10 @@ func TestRDSHandleResponseWithoutLDSWatch(t *testing.T) {
|
|||
// TestRDSHandleResponseWithoutRDSWatch tests the case where the v2Client
|
||||
// receives an RDS response without a registered RDS watcher.
|
||||
func TestRDSHandleResponseWithoutRDSWatch(t *testing.T) {
|
||||
fakeServer, sCleanup := fakexds.StartServer(t)
|
||||
client, cCleanup := fakeServer.GetClientConn(t)
|
||||
defer func() {
|
||||
cCleanup()
|
||||
sCleanup()
|
||||
}()
|
||||
v2c := newV2Client(client, goodNodeProto, func(int) time.Duration { return 0 })
|
||||
fakeServer, cc, cleanup := startServerAndGetCC(t)
|
||||
defer cleanup()
|
||||
|
||||
v2c := newV2Client(cc, goodNodeProto, func(int) time.Duration { return 0 })
|
||||
defer v2c.close()
|
||||
doLDS(t, v2c, fakeServer)
|
||||
|
||||
|
|
@ -304,7 +302,7 @@ type rdsTestOp struct {
|
|||
// target is the resource name to watch for.
|
||||
target string
|
||||
// responseToSend is the xDS response sent to the client
|
||||
responseToSend *fakexds.Response
|
||||
responseToSend *fakeserver.Response
|
||||
// wantOpErr specfies whether the main operation should return an error.
|
||||
wantOpErr bool
|
||||
// wantRDSCache is the expected rdsCache at the end of an operation.
|
||||
|
|
@ -318,16 +316,13 @@ type rdsTestOp struct {
|
|||
// pushes a good LDS response. It then reads a bunch of test operations to be
|
||||
// performed from rdsTestOps and returns error, if any, on the provided error
|
||||
// channel. This is executed in a separate goroutine.
|
||||
func testRDSCaching(t *testing.T, rdsTestOps []rdsTestOp, errCh chan error) {
|
||||
func testRDSCaching(t *testing.T, rdsTestOps []rdsTestOp, errCh *testutils.Channel) {
|
||||
t.Helper()
|
||||
|
||||
fakeServer, sCleanup := fakexds.StartServer(t)
|
||||
client, cCleanup := fakeServer.GetClientConn(t)
|
||||
defer func() {
|
||||
cCleanup()
|
||||
sCleanup()
|
||||
}()
|
||||
v2c := newV2Client(client, goodNodeProto, func(int) time.Duration { return 0 })
|
||||
fakeServer, cc, cleanup := startServerAndGetCC(t)
|
||||
defer cleanup()
|
||||
|
||||
v2c := newV2Client(cc, goodNodeProto, func(int) time.Duration { return 0 })
|
||||
defer v2c.close()
|
||||
t.Log("Started xds v2Client...")
|
||||
doLDS(t, v2c, fakeServer)
|
||||
|
|
@ -345,15 +340,18 @@ func testRDSCaching(t *testing.T, rdsTestOps []rdsTestOp, errCh chan error) {
|
|||
|
||||
// Wait till the request makes it to the fakeServer. This ensures that
|
||||
// the watch request has been processed by the v2Client.
|
||||
<-fakeServer.RequestChan
|
||||
if _, err := fakeServer.XDSRequestChan.Receive(); err != nil {
|
||||
errCh.Send(fmt.Errorf("Timeout waiting for RDS request: %v", err))
|
||||
}
|
||||
t.Log("FakeServer received request...")
|
||||
}
|
||||
|
||||
// Directly push the response through a call to handleRDSResponse,
|
||||
// thereby bypassing the fakeServer.
|
||||
if rdsTestOp.responseToSend != nil {
|
||||
if err := v2c.handleRDSResponse(rdsTestOp.responseToSend.Resp); (err != nil) != rdsTestOp.wantOpErr {
|
||||
errCh <- fmt.Errorf("v2c.handleRDSResponse() returned err: %v", err)
|
||||
resp := rdsTestOp.responseToSend.Resp.(*discoverypb.DiscoveryResponse)
|
||||
if err := v2c.handleRDSResponse(resp); (err != nil) != rdsTestOp.wantOpErr {
|
||||
errCh.Send(fmt.Errorf("v2c.handleRDSResponse(%+v) returned err: %v", resp, err))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
|
@ -366,24 +364,23 @@ func testRDSCaching(t *testing.T, rdsTestOps []rdsTestOp, errCh chan error) {
|
|||
}
|
||||
|
||||
if !reflect.DeepEqual(v2c.cloneRDSCacheForTesting(), rdsTestOp.wantRDSCache) {
|
||||
errCh <- fmt.Errorf("gotRDSCache: %v, wantRDSCache: %v", v2c.rdsCache, rdsTestOp.wantRDSCache)
|
||||
errCh.Send(fmt.Errorf("gotRDSCache: %v, wantRDSCache: %v", v2c.rdsCache, rdsTestOp.wantRDSCache))
|
||||
return
|
||||
}
|
||||
}
|
||||
t.Log("Completed all test ops successfully...")
|
||||
errCh <- nil
|
||||
errCh.Send(nil)
|
||||
}
|
||||
|
||||
// TestRDSCaching tests some end-to-end RDS flows using a fake xDS server, and
|
||||
// verifies the RDS data cached at the v2Client.
|
||||
func TestRDSCaching(t *testing.T) {
|
||||
errCh := make(chan error, 1)
|
||||
ops := []rdsTestOp{
|
||||
// Add an RDS watch for a resource name (goodRouteName1), which returns one
|
||||
// matching resource in the response.
|
||||
{
|
||||
target: goodRouteName1,
|
||||
responseToSend: &fakexds.Response{Resp: goodRDSResponse1},
|
||||
responseToSend: &fakeserver.Response{Resp: goodRDSResponse1},
|
||||
wantRDSCache: map[string]string{goodRouteName1: goodClusterName1},
|
||||
wantWatchCallback: true,
|
||||
},
|
||||
|
|
@ -392,7 +389,7 @@ func TestRDSCaching(t *testing.T) {
|
|||
// routeConfigName does not match our RDS watch (so the watch callback will
|
||||
// not be invoked). But this should still be cached.
|
||||
{
|
||||
responseToSend: &fakexds.Response{Resp: goodRDSResponse2},
|
||||
responseToSend: &fakeserver.Response{Resp: goodRDSResponse2},
|
||||
wantRDSCache: map[string]string{
|
||||
goodRouteName1: goodClusterName1,
|
||||
goodRouteName2: goodClusterName2,
|
||||
|
|
@ -402,7 +399,7 @@ func TestRDSCaching(t *testing.T) {
|
|||
// to return an error. But the watch callback should not be invoked, and
|
||||
// the cache should not be updated.
|
||||
{
|
||||
responseToSend: &fakexds.Response{Resp: uninterestingRDSResponse},
|
||||
responseToSend: &fakeserver.Response{Resp: uninterestingRDSResponse},
|
||||
wantOpErr: true,
|
||||
wantRDSCache: map[string]string{
|
||||
goodRouteName1: goodClusterName1,
|
||||
|
|
@ -421,18 +418,9 @@ func TestRDSCaching(t *testing.T) {
|
|||
wantWatchCallback: true,
|
||||
},
|
||||
}
|
||||
errCh := testutils.NewChannel()
|
||||
go testRDSCaching(t, ops, errCh)
|
||||
|
||||
timer := time.NewTimer(defaultTestTimeout)
|
||||
select {
|
||||
case <-timer.C:
|
||||
t.Fatal("Timeout when expecting RDS update")
|
||||
case err := <-errCh:
|
||||
timer.Stop()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
waitForNilErr(t, errCh)
|
||||
}
|
||||
|
||||
// TestRDSWatchExpiryTimer tests the case where the client does not receive an
|
||||
|
|
@ -440,47 +428,37 @@ func TestRDSCaching(t *testing.T) {
|
|||
// to be invoked with an error once the watchExpiryTimer fires.
|
||||
func TestRDSWatchExpiryTimer(t *testing.T) {
|
||||
oldWatchExpiryTimeout := defaultWatchExpiryTimeout
|
||||
defaultWatchExpiryTimeout = 1 * time.Second
|
||||
defaultWatchExpiryTimeout = 500 * time.Millisecond
|
||||
defer func() {
|
||||
defaultWatchExpiryTimeout = oldWatchExpiryTimeout
|
||||
}()
|
||||
|
||||
fakeServer, sCleanup := fakexds.StartServer(t)
|
||||
client, cCleanup := fakeServer.GetClientConn(t)
|
||||
defer func() {
|
||||
cCleanup()
|
||||
sCleanup()
|
||||
}()
|
||||
v2c := newV2Client(client, goodNodeProto, func(int) time.Duration { return 0 })
|
||||
fakeServer, cc, cleanup := startServerAndGetCC(t)
|
||||
defer cleanup()
|
||||
|
||||
v2c := newV2Client(cc, goodNodeProto, func(int) time.Duration { return 0 })
|
||||
defer v2c.close()
|
||||
t.Log("Started xds v2Client...")
|
||||
doLDS(t, v2c, fakeServer)
|
||||
|
||||
rdsCallbackCh := make(chan error, 1)
|
||||
callbackCh := testutils.NewChannel()
|
||||
v2c.watchRDS(goodRouteName1, func(u rdsUpdate, err error) {
|
||||
t.Logf("Received callback with rdsUpdate {%+v} and error {%v}", u, err)
|
||||
if u.clusterName != "" {
|
||||
rdsCallbackCh <- fmt.Errorf("received clusterName %v in rdsCallback, wanted empty string", u.clusterName)
|
||||
callbackCh.Send(fmt.Errorf("received clusterName %v in rdsCallback, wanted empty string", u.clusterName))
|
||||
}
|
||||
if err == nil {
|
||||
rdsCallbackCh <- errors.New("received nil error in rdsCallback")
|
||||
callbackCh.Send(errors.New("received nil error in rdsCallback"))
|
||||
}
|
||||
rdsCallbackCh <- nil
|
||||
callbackCh.Send(nil)
|
||||
})
|
||||
|
||||
// Wait till the request makes it to the fakeServer. This ensures that
|
||||
// the watch request has been processed by the v2Client.
|
||||
<-fakeServer.RequestChan
|
||||
|
||||
timer := time.NewTimer(2 * defaultWatchExpiryTimeout)
|
||||
select {
|
||||
case <-timer.C:
|
||||
t.Fatalf("Timeout expired when expecting RDS update")
|
||||
case err := <-rdsCallbackCh:
|
||||
timer.Stop()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err := fakeServer.XDSRequestChan.Receive(); err != nil {
|
||||
t.Fatalf("Timeout expired when expecting an RDS request")
|
||||
}
|
||||
waitForNilErr(t, callbackCh)
|
||||
}
|
||||
|
||||
func TestHostFromTarget(t *testing.T) {
|
||||
|
|
|
|||
|
|
@ -20,11 +20,12 @@ package client
|
|||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
xdspb "github.com/envoyproxy/go-control-plane/envoy/api/v2"
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"google.golang.org/grpc/xds/internal/client/fakexds"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/xds/internal/testutils"
|
||||
"google.golang.org/grpc/xds/internal/testutils/fakeserver"
|
||||
)
|
||||
|
||||
type watchHandleTestcase struct {
|
||||
|
|
@ -39,7 +40,7 @@ type watchHandleTestcase struct {
|
|||
rdsWatch func(routeName string, rdsCb rdsCallback) (cancel func())
|
||||
cdsWatch func(clusterName string, cdsCb cdsCallback) (cancel func())
|
||||
edsWatch func(clusterName string, edsCb edsCallback) (cancel func())
|
||||
watchReqChan chan *fakexds.Request // The request sent for watch will be sent to this channel.
|
||||
watchReqChan *testutils.Channel // The request sent for watch will be sent to this channel.
|
||||
handleXDSResp func(response *xdspb.DiscoveryResponse) error
|
||||
}
|
||||
|
||||
|
|
@ -50,8 +51,11 @@ type watchHandleTestcase struct {
|
|||
// handleXDSResp with responseToHandle (if it's set). It then compares the
|
||||
// update received by watch callback with the expected results.
|
||||
func testWatchHandle(t *testing.T, test *watchHandleTestcase) {
|
||||
gotUpdateCh := make(chan interface{}, 1)
|
||||
gotUpdateErrCh := make(chan error, 1)
|
||||
type updateErr struct {
|
||||
u interface{}
|
||||
err error
|
||||
}
|
||||
gotUpdateCh := testutils.NewChannel()
|
||||
|
||||
var cancelWatch func()
|
||||
// Register the watcher, this will also trigger the v2Client to send the xDS
|
||||
|
|
@ -60,26 +64,22 @@ func testWatchHandle(t *testing.T, test *watchHandleTestcase) {
|
|||
case test.ldsWatch != nil:
|
||||
cancelWatch = test.ldsWatch(goodLDSTarget1, func(u ldsUpdate, err error) {
|
||||
t.Logf("in v2c.watchLDS callback, ldsUpdate: %+v, err: %v", u, err)
|
||||
gotUpdateCh <- u
|
||||
gotUpdateErrCh <- err
|
||||
gotUpdateCh.Send(updateErr{u, err})
|
||||
})
|
||||
case test.rdsWatch != nil:
|
||||
cancelWatch = test.rdsWatch(goodRouteName1, func(u rdsUpdate, err error) {
|
||||
t.Logf("in v2c.watchRDS callback, rdsUpdate: %+v, err: %v", u, err)
|
||||
gotUpdateCh <- u
|
||||
gotUpdateErrCh <- err
|
||||
gotUpdateCh.Send(updateErr{u, err})
|
||||
})
|
||||
case test.cdsWatch != nil:
|
||||
cancelWatch = test.cdsWatch(clusterName1, func(u CDSUpdate, err error) {
|
||||
t.Logf("in v2c.watchCDS callback, cdsUpdate: %+v, err: %v", u, err)
|
||||
gotUpdateCh <- u
|
||||
gotUpdateErrCh <- err
|
||||
gotUpdateCh.Send(updateErr{u, err})
|
||||
})
|
||||
case test.edsWatch != nil:
|
||||
cancelWatch = test.edsWatch(goodEDSName, func(u *EDSUpdate, err error) {
|
||||
t.Logf("in v2c.watchEDS callback, edsUpdate: %+v, err: %v", u, err)
|
||||
gotUpdateCh <- *u
|
||||
gotUpdateErrCh <- err
|
||||
gotUpdateCh.Send(updateErr{*u, err})
|
||||
})
|
||||
default:
|
||||
t.Fatalf("no watch() is set")
|
||||
|
|
@ -88,9 +88,9 @@ func testWatchHandle(t *testing.T, test *watchHandleTestcase) {
|
|||
|
||||
// Wait till the request makes it to the fakeServer. This ensures that
|
||||
// the watch request has been processed by the v2Client.
|
||||
//
|
||||
// TODO: switch to new fakexds server request channel with timed receive.
|
||||
<-test.watchReqChan
|
||||
if _, err := test.watchReqChan.Receive(); err != nil {
|
||||
t.Fatalf("Timeout waiting for an xDS request: %v", err)
|
||||
}
|
||||
|
||||
// Directly push the response through a call to handleXDSResp. This bypasses
|
||||
// the fakeServer, so it's only testing the handle logic. Client response
|
||||
|
|
@ -108,30 +108,62 @@ func testWatchHandle(t *testing.T, test *watchHandleTestcase) {
|
|||
// Cannot directly compare test.wantUpdate with nil (typed vs non-typed nil:
|
||||
// https://golang.org/doc/faq#nil_error).
|
||||
if c := test.wantUpdate; c == nil || (reflect.ValueOf(c).Kind() == reflect.Ptr && reflect.ValueOf(c).IsNil()) {
|
||||
timer := time.NewTimer(defaultTestTimeout)
|
||||
select {
|
||||
case <-timer.C:
|
||||
update, err := gotUpdateCh.Receive()
|
||||
if err == testutils.ErrRecvTimeout {
|
||||
return
|
||||
case <-gotUpdateCh:
|
||||
t.Fatal("Unexpected update")
|
||||
}
|
||||
t.Fatalf("Unexpected update: +%v", update)
|
||||
}
|
||||
|
||||
wantUpdate := reflect.ValueOf(test.wantUpdate).Elem().Interface()
|
||||
timer := time.NewTimer(defaultTestTimeout)
|
||||
select {
|
||||
case <-timer.C:
|
||||
t.Fatal("Timeout expecting RDS update")
|
||||
case gotUpdate := <-gotUpdateCh:
|
||||
timer.Stop()
|
||||
if diff := cmp.Diff(gotUpdate, wantUpdate, cmp.AllowUnexported(rdsUpdate{}, ldsUpdate{}, CDSUpdate{}, EDSUpdate{})); diff != "" {
|
||||
t.Fatalf("got update : %+v, want %+v, diff: %s", gotUpdate, wantUpdate, diff)
|
||||
}
|
||||
uErr, err := gotUpdateCh.Receive()
|
||||
if err == testutils.ErrRecvTimeout {
|
||||
t.Fatal("Timeout expecting xDS update")
|
||||
}
|
||||
// Since the callback that we registered pushes to both channels at
|
||||
// the same time, this channel read should return immediately.
|
||||
gotUpdateErr := <-gotUpdateErrCh
|
||||
gotUpdate := uErr.(updateErr).u
|
||||
opt := cmp.AllowUnexported(rdsUpdate{}, ldsUpdate{}, CDSUpdate{}, EDSUpdate{})
|
||||
if diff := cmp.Diff(gotUpdate, wantUpdate, opt); diff != "" {
|
||||
t.Fatalf("got update : %+v, want %+v, diff: %s", gotUpdate, wantUpdate, diff)
|
||||
}
|
||||
gotUpdateErr := uErr.(updateErr).err
|
||||
if (gotUpdateErr != nil) != test.wantUpdateErr {
|
||||
t.Fatalf("got RDS update error {%v}, wantErr: %v", gotUpdateErr, test.wantUpdateErr)
|
||||
t.Fatalf("got xDS update error {%v}, wantErr: %v", gotUpdateErr, test.wantUpdateErr)
|
||||
}
|
||||
}
|
||||
|
||||
// startServerAndGetCC starts a fake XDS server and also returns a ClientConn
|
||||
// connected to it.
|
||||
func startServerAndGetCC(t *testing.T) (*fakeserver.Server, *grpc.ClientConn, func()) {
|
||||
t.Helper()
|
||||
|
||||
fs, sCleanup, err := fakeserver.StartServer()
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to start fake xDS server: %v", err)
|
||||
}
|
||||
|
||||
cc, ccCleanup, err := fs.XDSClientConn()
|
||||
if err != nil {
|
||||
sCleanup()
|
||||
t.Fatalf("Failed to get a clientConn to the fake xDS server: %v", err)
|
||||
}
|
||||
return fs, cc, func() {
|
||||
sCleanup()
|
||||
ccCleanup()
|
||||
}
|
||||
}
|
||||
|
||||
// waitForNilErr waits for a nil error value to be received on the
|
||||
// provided channel.
|
||||
func waitForNilErr(t *testing.T, ch *testutils.Channel) {
|
||||
t.Helper()
|
||||
|
||||
val, err := ch.Receive()
|
||||
if err == testutils.ErrRecvTimeout {
|
||||
t.Fatalf("Timeout expired when expecting update")
|
||||
}
|
||||
if val != nil {
|
||||
if cbErr := val.(error); cbErr != nil {
|
||||
t.Fatal(cbErr)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,60 +27,121 @@ import (
|
|||
"github.com/golang/protobuf/proto"
|
||||
anypb "github.com/golang/protobuf/ptypes/any"
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"google.golang.org/grpc/xds/internal/client/fakexds"
|
||||
"google.golang.org/grpc/xds/internal/testutils"
|
||||
"google.golang.org/grpc/xds/internal/testutils/fakeserver"
|
||||
)
|
||||
|
||||
func emptyChanRecvWithTimeout(ch <-chan struct{}, d time.Duration) error {
|
||||
timer := time.NewTimer(d)
|
||||
select {
|
||||
case <-timer.C:
|
||||
return fmt.Errorf("timeout")
|
||||
case <-ch:
|
||||
timer.Stop()
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func requestChanRecvWithTimeout(ch <-chan *fakexds.Request, d time.Duration) (*fakexds.Request, error) {
|
||||
timer := time.NewTimer(d)
|
||||
select {
|
||||
case <-timer.C:
|
||||
return nil, fmt.Errorf("timeout waiting for request")
|
||||
case r := <-ch:
|
||||
timer.Stop()
|
||||
return r, nil
|
||||
}
|
||||
}
|
||||
|
||||
// compareXDSRequest reads requests from channel, compare it with want.
|
||||
func compareXDSRequest(ch <-chan *fakexds.Request, d time.Duration, want *xdspb.DiscoveryRequest, version, nonce string) error {
|
||||
r, err := requestChanRecvWithTimeout(ch, d)
|
||||
func compareXDSRequest(ch *testutils.Channel, want *xdspb.DiscoveryRequest, version, nonce string) error {
|
||||
val, err := ch.Receive()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if r.Err != nil {
|
||||
return fmt.Errorf("unexpected error from request: %v", r.Err)
|
||||
req := val.(*fakeserver.Request)
|
||||
if req.Err != nil {
|
||||
return fmt.Errorf("unexpected error from request: %v", req.Err)
|
||||
}
|
||||
wantClone := proto.Clone(want).(*xdspb.DiscoveryRequest)
|
||||
wantClone.VersionInfo = version
|
||||
wantClone.ResponseNonce = nonce
|
||||
if !cmp.Equal(r.Req, wantClone, cmp.Comparer(proto.Equal)) {
|
||||
return fmt.Errorf("received request different from want, diff: %s", cmp.Diff(r.Req, wantClone))
|
||||
if !cmp.Equal(req.Req, wantClone, cmp.Comparer(proto.Equal)) {
|
||||
return fmt.Errorf("received request different from want, diff: %s", cmp.Diff(req.Req, wantClone))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func sendXDSRespWithVersion(ch chan<- *fakexds.Response, respWithoutVersion *xdspb.DiscoveryResponse, version int) (nonce string) {
|
||||
func sendXDSRespWithVersion(ch chan<- *fakeserver.Response, respWithoutVersion *xdspb.DiscoveryResponse, version int) (nonce string) {
|
||||
respToSend := proto.Clone(respWithoutVersion).(*xdspb.DiscoveryResponse)
|
||||
respToSend.VersionInfo = strconv.Itoa(version)
|
||||
nonce = strconv.Itoa(int(time.Now().UnixNano()))
|
||||
respToSend.Nonce = nonce
|
||||
ch <- &fakexds.Response{Resp: respToSend}
|
||||
ch <- &fakeserver.Response{Resp: respToSend}
|
||||
return
|
||||
}
|
||||
|
||||
// TestV2ClientAck verifies that valid responses are acked, and invalid ones are
|
||||
// nacked.
|
||||
// startXDS calls watch to send the first request. It then sends a good response
|
||||
// and checks for ack.
|
||||
func startXDS(t *testing.T, xdsname string, v2c *v2Client, reqChan *testutils.Channel, req *xdspb.DiscoveryRequest) *testutils.Channel {
|
||||
callbackCh := testutils.NewChannel()
|
||||
switch xdsname {
|
||||
case "LDS":
|
||||
v2c.watchLDS(goodLDSTarget1, func(u ldsUpdate, err error) {
|
||||
t.Logf("Received %s callback with ldsUpdate {%+v} and error {%v}", xdsname, u, err)
|
||||
callbackCh.Send(struct{}{})
|
||||
})
|
||||
case "RDS":
|
||||
v2c.watchRDS(goodRouteName1, func(u rdsUpdate, err error) {
|
||||
t.Logf("Received %s callback with ldsUpdate {%+v} and error {%v}", xdsname, u, err)
|
||||
callbackCh.Send(struct{}{})
|
||||
})
|
||||
case "CDS":
|
||||
v2c.watchCDS(goodClusterName1, func(u CDSUpdate, err error) {
|
||||
t.Logf("Received %s callback with ldsUpdate {%+v} and error {%v}", xdsname, u, err)
|
||||
callbackCh.Send(struct{}{})
|
||||
})
|
||||
case "EDS":
|
||||
v2c.watchEDS(goodEDSName, func(u *EDSUpdate, err error) {
|
||||
t.Logf("Received %s callback with ldsUpdate {%+v} and error {%v}", xdsname, u, err)
|
||||
callbackCh.Send(struct{}{})
|
||||
})
|
||||
}
|
||||
|
||||
if err := compareXDSRequest(reqChan, req, "", ""); err != nil {
|
||||
t.Fatalf("Failed to receive %s request: %v", xdsname, err)
|
||||
}
|
||||
t.Logf("FakeServer received %s request...", xdsname)
|
||||
return callbackCh
|
||||
}
|
||||
|
||||
// sendGoodResp sends the good response, with the given version, and a random
|
||||
// nonce.
|
||||
//
|
||||
// It also waits and checks that the ack request contains the given version, and
|
||||
// the generated nonce.
|
||||
func sendGoodResp(t *testing.T, xdsname string, fakeServer *fakeserver.Server, version int, goodResp *xdspb.DiscoveryResponse, wantReq *xdspb.DiscoveryRequest, callbackCh *testutils.Channel) {
|
||||
nonce := sendXDSRespWithVersion(fakeServer.XDSResponseChan, goodResp, version)
|
||||
t.Logf("Good %s response pushed to fakeServer...", xdsname)
|
||||
|
||||
if err := compareXDSRequest(fakeServer.XDSRequestChan, wantReq, strconv.Itoa(version), nonce); err != nil {
|
||||
t.Errorf("Failed to receive %s request: %v", xdsname, err)
|
||||
}
|
||||
t.Logf("Good %s response acked", xdsname)
|
||||
|
||||
if _, err := callbackCh.Receive(); err != nil {
|
||||
t.Errorf("Timeout when expecting %s update", xdsname)
|
||||
}
|
||||
t.Logf("Good %s response callback executed", xdsname)
|
||||
}
|
||||
|
||||
// sendBadResp sends a bad response with the given version. This response will
|
||||
// be nacked, so we expect a request with the previous version (version-1).
|
||||
//
|
||||
// But the nonce in request should be the new nonce.
|
||||
func sendBadResp(t *testing.T, xdsname string, fakeServer *fakeserver.Server, version int, wantReq *xdspb.DiscoveryRequest) {
|
||||
var typeURL string
|
||||
switch xdsname {
|
||||
case "LDS":
|
||||
typeURL = ldsURL
|
||||
case "RDS":
|
||||
typeURL = rdsURL
|
||||
case "CDS":
|
||||
typeURL = cdsURL
|
||||
case "EDS":
|
||||
typeURL = edsURL
|
||||
}
|
||||
nonce := sendXDSRespWithVersion(fakeServer.XDSResponseChan, &xdspb.DiscoveryResponse{
|
||||
Resources: []*anypb.Any{{}},
|
||||
TypeUrl: typeURL,
|
||||
}, version)
|
||||
t.Logf("Bad %s response pushed to fakeServer...", xdsname)
|
||||
if err := compareXDSRequest(fakeServer.XDSRequestChan, wantReq, strconv.Itoa(version-1), nonce); err != nil {
|
||||
t.Errorf("Failed to receive %s request: %v", xdsname, err)
|
||||
}
|
||||
t.Logf("Bad %s response nacked", xdsname)
|
||||
}
|
||||
|
||||
// TestV2ClientAck verifies that valid responses are acked, and invalid ones
|
||||
// are nacked.
|
||||
//
|
||||
// This test also verifies the version for different types are independent.
|
||||
func TestV2ClientAck(t *testing.T) {
|
||||
|
|
@ -91,27 +152,23 @@ func TestV2ClientAck(t *testing.T) {
|
|||
versionEDS = 4000
|
||||
)
|
||||
|
||||
fakeServer, sCleanup := fakexds.StartServer(t)
|
||||
client, cCleanup := fakeServer.GetClientConn(t)
|
||||
defer func() {
|
||||
cCleanup()
|
||||
sCleanup()
|
||||
}()
|
||||
v2c := newV2Client(client, goodNodeProto, func(int) time.Duration { return 0 })
|
||||
fakeServer, cc, cleanup := startServerAndGetCC(t)
|
||||
defer cleanup()
|
||||
v2c := newV2Client(cc, goodNodeProto, func(int) time.Duration { return 0 })
|
||||
defer v2c.close()
|
||||
t.Log("Started xds v2Client...")
|
||||
|
||||
// Start the watch, send a good response, and check for ack.
|
||||
cbLDS := startXDS(t, "LDS", v2c, fakeServer, goodLDSRequest)
|
||||
cbLDS := startXDS(t, "LDS", v2c, fakeServer.XDSRequestChan, goodLDSRequest)
|
||||
sendGoodResp(t, "LDS", fakeServer, versionLDS, goodLDSResponse1, goodLDSRequest, cbLDS)
|
||||
versionLDS++
|
||||
cbRDS := startXDS(t, "RDS", v2c, fakeServer, goodRDSRequest)
|
||||
cbRDS := startXDS(t, "RDS", v2c, fakeServer.XDSRequestChan, goodRDSRequest)
|
||||
sendGoodResp(t, "RDS", fakeServer, versionRDS, goodRDSResponse1, goodRDSRequest, cbRDS)
|
||||
versionRDS++
|
||||
cbCDS := startXDS(t, "CDS", v2c, fakeServer, goodCDSRequest)
|
||||
cbCDS := startXDS(t, "CDS", v2c, fakeServer.XDSRequestChan, goodCDSRequest)
|
||||
sendGoodResp(t, "CDS", fakeServer, versionCDS, goodCDSResponse1, goodCDSRequest, cbCDS)
|
||||
versionCDS++
|
||||
cbEDS := startXDS(t, "EDS", v2c, fakeServer, goodEDSRequest)
|
||||
cbEDS := startXDS(t, "EDS", v2c, fakeServer.XDSRequestChan, goodEDSRequest)
|
||||
sendGoodResp(t, "EDS", fakeServer, versionEDS, goodEDSResponse1, goodEDSRequest, cbEDS)
|
||||
versionEDS++
|
||||
|
||||
|
|
@ -136,105 +193,21 @@ func TestV2ClientAck(t *testing.T) {
|
|||
versionEDS++
|
||||
}
|
||||
|
||||
// startXDS calls watch to send the first request. It then sends a good response
|
||||
// and checks for ack.
|
||||
func startXDS(t *testing.T, xdsname string, v2c *v2Client, fakeServer *fakexds.Server, goodReq *xdspb.DiscoveryRequest) <-chan struct{} {
|
||||
callbackCh := make(chan struct{}, 1)
|
||||
switch xdsname {
|
||||
case "LDS":
|
||||
v2c.watchLDS(goodLDSTarget1, func(u ldsUpdate, err error) {
|
||||
t.Logf("Received %s callback with ldsUpdate {%+v} and error {%v}", xdsname, u, err)
|
||||
callbackCh <- struct{}{}
|
||||
})
|
||||
case "RDS":
|
||||
v2c.watchRDS(goodRouteName1, func(u rdsUpdate, err error) {
|
||||
t.Logf("Received %s callback with ldsUpdate {%+v} and error {%v}", xdsname, u, err)
|
||||
callbackCh <- struct{}{}
|
||||
})
|
||||
case "CDS":
|
||||
v2c.watchCDS(goodClusterName1, func(u CDSUpdate, err error) {
|
||||
t.Logf("Received %s callback with ldsUpdate {%+v} and error {%v}", xdsname, u, err)
|
||||
callbackCh <- struct{}{}
|
||||
})
|
||||
case "EDS":
|
||||
v2c.watchEDS(goodEDSName, func(u *EDSUpdate, err error) {
|
||||
t.Logf("Received %s callback with ldsUpdate {%+v} and error {%v}", xdsname, u, err)
|
||||
callbackCh <- struct{}{}
|
||||
})
|
||||
}
|
||||
|
||||
if err := compareXDSRequest(fakeServer.RequestChan, defaultTestTimeout, goodReq, "", ""); err != nil {
|
||||
t.Fatalf("Failed to receive %s request: %v", xdsname, err)
|
||||
}
|
||||
t.Logf("FakeServer received %s request...", xdsname)
|
||||
return callbackCh
|
||||
}
|
||||
|
||||
// sendGoodResp sends the good response, with the given version, and a random
|
||||
// nonce.
|
||||
//
|
||||
// It also waits and checks that the ack request contains the given version, and
|
||||
// the generated nonce.
|
||||
func sendGoodResp(t *testing.T, xdsname string, fakeServer *fakexds.Server, version int, goodResp *xdspb.DiscoveryResponse, wantReq *xdspb.DiscoveryRequest, callbackCh <-chan struct{}) {
|
||||
nonce := sendXDSRespWithVersion(fakeServer.ResponseChan, goodResp, version)
|
||||
t.Logf("Good %s response pushed to fakeServer...", xdsname)
|
||||
|
||||
if err := compareXDSRequest(fakeServer.RequestChan, defaultTestTimeout, wantReq, strconv.Itoa(version), nonce); err != nil {
|
||||
t.Errorf("Failed to receive %s request: %v", xdsname, err)
|
||||
}
|
||||
t.Logf("Good %s response acked", xdsname)
|
||||
if err := emptyChanRecvWithTimeout(callbackCh, defaultTestTimeout); err != nil {
|
||||
t.Errorf("Timeout when expecting %s update", xdsname)
|
||||
}
|
||||
t.Logf("Good %s response callback executed", xdsname)
|
||||
}
|
||||
|
||||
// sendBadResp sends a bad response with the given version. This response will
|
||||
// be nacked, so we expect a request with the previous version (version-1).
|
||||
//
|
||||
// But the nonce in request should be the new nonce.
|
||||
func sendBadResp(t *testing.T, xdsname string, fakeServer *fakexds.Server, version int, wantReq *xdspb.DiscoveryRequest) {
|
||||
var typeURL string
|
||||
switch xdsname {
|
||||
case "LDS":
|
||||
typeURL = ldsURL
|
||||
case "RDS":
|
||||
typeURL = rdsURL
|
||||
case "CDS":
|
||||
typeURL = cdsURL
|
||||
case "EDS":
|
||||
typeURL = edsURL
|
||||
}
|
||||
nonce := sendXDSRespWithVersion(fakeServer.ResponseChan, &xdspb.DiscoveryResponse{
|
||||
Resources: []*anypb.Any{{}},
|
||||
TypeUrl: typeURL,
|
||||
}, version)
|
||||
t.Logf("Bad %s response pushed to fakeServer...", xdsname)
|
||||
if err := compareXDSRequest(fakeServer.RequestChan, defaultTestTimeout, wantReq, strconv.Itoa(version-1), nonce); err != nil {
|
||||
t.Errorf("Failed to receive %s request: %v", xdsname, err)
|
||||
}
|
||||
t.Logf("Bad %s response nacked", xdsname)
|
||||
}
|
||||
|
||||
// Test when the first response is invalid, and is nacked, the nack requests
|
||||
// should have an empty version string.
|
||||
func TestV2ClientAckFirstIsNack(t *testing.T) {
|
||||
var versionLDS = 1000
|
||||
|
||||
fakeServer, sCleanup := fakexds.StartServer(t)
|
||||
client, cCleanup := fakeServer.GetClientConn(t)
|
||||
defer func() {
|
||||
cCleanup()
|
||||
sCleanup()
|
||||
}()
|
||||
v2c := newV2Client(client, goodNodeProto, func(int) time.Duration { return 0 })
|
||||
fakeServer, cc, cleanup := startServerAndGetCC(t)
|
||||
defer cleanup()
|
||||
v2c := newV2Client(cc, goodNodeProto, func(int) time.Duration { return 0 })
|
||||
defer v2c.close()
|
||||
t.Log("Started xds v2Client...")
|
||||
|
||||
// Start the watch, send a good response, and check for ack.
|
||||
cbLDS := startXDS(t, "LDS", v2c, fakeServer, goodLDSRequest)
|
||||
cbLDS := startXDS(t, "LDS", v2c, fakeServer.XDSRequestChan, goodLDSRequest)
|
||||
|
||||
nonce := sendXDSRespWithVersion(fakeServer.ResponseChan, &xdspb.DiscoveryResponse{
|
||||
nonce := sendXDSRespWithVersion(fakeServer.XDSResponseChan, &xdspb.DiscoveryResponse{
|
||||
Resources: []*anypb.Any{{}},
|
||||
TypeUrl: ldsURL,
|
||||
}, versionLDS)
|
||||
|
|
@ -242,7 +215,7 @@ func TestV2ClientAckFirstIsNack(t *testing.T) {
|
|||
|
||||
// The expected version string is an empty string, because this is the first
|
||||
// response, and it's nacked (so there's no previous ack version).
|
||||
if err := compareXDSRequest(fakeServer.RequestChan, defaultTestTimeout, goodLDSRequest, "", nonce); err != nil {
|
||||
if err := compareXDSRequest(fakeServer.XDSRequestChan, goodLDSRequest, "", nonce); err != nil {
|
||||
t.Errorf("Failed to receive request: %v", err)
|
||||
}
|
||||
t.Logf("Bad response nacked")
|
||||
|
|
@ -257,33 +230,29 @@ func TestV2ClientAckFirstIsNack(t *testing.T) {
|
|||
func TestV2ClientAckNackAfterNewWatch(t *testing.T) {
|
||||
var versionLDS = 1000
|
||||
|
||||
fakeServer, sCleanup := fakexds.StartServer(t)
|
||||
client, cCleanup := fakeServer.GetClientConn(t)
|
||||
defer func() {
|
||||
cCleanup()
|
||||
sCleanup()
|
||||
}()
|
||||
v2c := newV2Client(client, goodNodeProto, func(int) time.Duration { return 0 })
|
||||
fakeServer, cc, cleanup := startServerAndGetCC(t)
|
||||
defer cleanup()
|
||||
v2c := newV2Client(cc, goodNodeProto, func(int) time.Duration { return 0 })
|
||||
defer v2c.close()
|
||||
t.Log("Started xds v2Client...")
|
||||
|
||||
// Start the watch, send a good response, and check for ack.
|
||||
cbLDS := startXDS(t, "LDS", v2c, fakeServer, goodLDSRequest)
|
||||
cbLDS := startXDS(t, "LDS", v2c, fakeServer.XDSRequestChan, goodLDSRequest)
|
||||
sendGoodResp(t, "LDS", fakeServer, versionLDS, goodLDSResponse1, goodLDSRequest, cbLDS)
|
||||
versionLDS++
|
||||
|
||||
// Start a new watch.
|
||||
cbLDS = startXDS(t, "LDS", v2c, fakeServer, goodLDSRequest)
|
||||
cbLDS = startXDS(t, "LDS", v2c, fakeServer.XDSRequestChan, goodLDSRequest)
|
||||
|
||||
// This is an invalid response after the new watch.
|
||||
nonce := sendXDSRespWithVersion(fakeServer.ResponseChan, &xdspb.DiscoveryResponse{
|
||||
nonce := sendXDSRespWithVersion(fakeServer.XDSResponseChan, &xdspb.DiscoveryResponse{
|
||||
Resources: []*anypb.Any{{}},
|
||||
TypeUrl: ldsURL,
|
||||
}, versionLDS)
|
||||
t.Logf("Bad response pushed to fakeServer...")
|
||||
|
||||
// The expected version string is the previous acked version.
|
||||
if err := compareXDSRequest(fakeServer.RequestChan, defaultTestTimeout, goodLDSRequest, strconv.Itoa(versionLDS-1), nonce); err != nil {
|
||||
if err := compareXDSRequest(fakeServer.XDSRequestChan, goodLDSRequest, strconv.Itoa(versionLDS-1), nonce); err != nil {
|
||||
t.Errorf("Failed to receive request: %v", err)
|
||||
}
|
||||
t.Logf("Bad response nacked")
|
||||
|
|
|
|||
|
|
@ -24,7 +24,8 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/golang/protobuf/proto"
|
||||
"google.golang.org/grpc/xds/internal/client/fakexds"
|
||||
"google.golang.org/grpc/xds/internal/testutils"
|
||||
"google.golang.org/grpc/xds/internal/testutils/fakeserver"
|
||||
|
||||
xdspb "github.com/envoyproxy/go-control-plane/envoy/api/v2"
|
||||
basepb "github.com/envoyproxy/go-control-plane/envoy/api/v2/core"
|
||||
|
|
@ -36,7 +37,7 @@ import (
|
|||
)
|
||||
|
||||
const (
|
||||
defaultTestTimeout = 2 * time.Second
|
||||
defaultTestTimeout = 1 * time.Second
|
||||
goodLDSTarget1 = "lds.target.good:1111"
|
||||
goodLDSTarget2 = "lds.target.good:2222"
|
||||
goodRouteName1 = "GoodRouteConfig1"
|
||||
|
|
@ -390,12 +391,8 @@ var (
|
|||
// TestV2ClientBackoffAfterRecvError verifies if the v2Client backoffs when it
|
||||
// encounters a Recv error while receiving an LDS response.
|
||||
func TestV2ClientBackoffAfterRecvError(t *testing.T) {
|
||||
fakeServer, sCleanup := fakexds.StartServer(t)
|
||||
client, cCleanup := fakeServer.GetClientConn(t)
|
||||
defer func() {
|
||||
cCleanup()
|
||||
sCleanup()
|
||||
}()
|
||||
fakeServer, cc, cleanup := startServerAndGetCC(t)
|
||||
defer cleanup()
|
||||
|
||||
// Override the v2Client backoff function with this, so that we can verify
|
||||
// that a backoff actually was triggerred.
|
||||
|
|
@ -405,7 +402,7 @@ func TestV2ClientBackoffAfterRecvError(t *testing.T) {
|
|||
return 0
|
||||
}
|
||||
|
||||
v2c := newV2Client(client, goodNodeProto, clientBackoff)
|
||||
v2c := newV2Client(cc, goodNodeProto, clientBackoff)
|
||||
defer v2c.close()
|
||||
t.Log("Started xds v2Client...")
|
||||
|
||||
|
|
@ -413,10 +410,12 @@ func TestV2ClientBackoffAfterRecvError(t *testing.T) {
|
|||
v2c.watchLDS(goodLDSTarget1, func(u ldsUpdate, err error) {
|
||||
close(callbackCh)
|
||||
})
|
||||
<-fakeServer.RequestChan
|
||||
if _, err := fakeServer.XDSRequestChan.Receive(); err != nil {
|
||||
t.Fatalf("Timeout expired when expecting an LDS request")
|
||||
}
|
||||
t.Log("FakeServer received request...")
|
||||
|
||||
fakeServer.ResponseChan <- &fakexds.Response{Err: errors.New("RPC error")}
|
||||
fakeServer.XDSResponseChan <- &fakeserver.Response{Err: errors.New("RPC error")}
|
||||
t.Log("Bad LDS response pushed to fakeServer...")
|
||||
|
||||
timer := time.NewTimer(defaultTestTimeout)
|
||||
|
|
@ -435,95 +434,81 @@ func TestV2ClientBackoffAfterRecvError(t *testing.T) {
|
|||
// encountered a Recv() error, and is expected to send out xDS requests for
|
||||
// registered watchers once it comes back up again.
|
||||
func TestV2ClientRetriesAfterBrokenStream(t *testing.T) {
|
||||
fakeServer, sCleanup := fakexds.StartServer(t)
|
||||
client, cCleanup := fakeServer.GetClientConn(t)
|
||||
defer func() {
|
||||
cCleanup()
|
||||
sCleanup()
|
||||
}()
|
||||
v2c := newV2Client(client, goodNodeProto, func(int) time.Duration { return 0 })
|
||||
fakeServer, cc, cleanup := startServerAndGetCC(t)
|
||||
defer cleanup()
|
||||
|
||||
v2c := newV2Client(cc, goodNodeProto, func(int) time.Duration { return 0 })
|
||||
defer v2c.close()
|
||||
t.Log("Started xds v2Client...")
|
||||
|
||||
callbackCh := make(chan struct{}, 1)
|
||||
callbackCh := testutils.NewChannel()
|
||||
v2c.watchLDS(goodLDSTarget1, func(u ldsUpdate, err error) {
|
||||
t.Logf("Received LDS callback with ldsUpdate {%+v} and error {%v}", u, err)
|
||||
callbackCh <- struct{}{}
|
||||
callbackCh.Send(struct{}{})
|
||||
})
|
||||
<-fakeServer.RequestChan
|
||||
if _, err := fakeServer.XDSRequestChan.Receive(); err != nil {
|
||||
t.Fatalf("Timeout expired when expecting an LDS request")
|
||||
}
|
||||
t.Log("FakeServer received request...")
|
||||
|
||||
fakeServer.ResponseChan <- &fakexds.Response{Resp: goodLDSResponse1}
|
||||
fakeServer.XDSResponseChan <- &fakeserver.Response{Resp: goodLDSResponse1}
|
||||
t.Log("Good LDS response pushed to fakeServer...")
|
||||
|
||||
timer := time.NewTimer(defaultTestTimeout)
|
||||
select {
|
||||
case <-timer.C:
|
||||
if _, err := callbackCh.Receive(); err != nil {
|
||||
t.Fatal("Timeout when expecting LDS update")
|
||||
case <-callbackCh:
|
||||
timer.Stop()
|
||||
}
|
||||
// Read the ack, so the next request is sent after stream re-creation.
|
||||
<-fakeServer.RequestChan
|
||||
|
||||
fakeServer.ResponseChan <- &fakexds.Response{Err: errors.New("RPC error")}
|
||||
// Read the ack, so the next request is sent after stream re-creation.
|
||||
if _, err := fakeServer.XDSRequestChan.Receive(); err != nil {
|
||||
t.Fatalf("Timeout expired when expecting an LDS ACK")
|
||||
}
|
||||
|
||||
fakeServer.XDSResponseChan <- &fakeserver.Response{Err: errors.New("RPC error")}
|
||||
t.Log("Bad LDS response pushed to fakeServer...")
|
||||
|
||||
timer = time.NewTimer(defaultTestTimeout)
|
||||
select {
|
||||
case <-timer.C:
|
||||
t.Fatal("Timeout when expecting LDS update")
|
||||
case gotRequest := <-fakeServer.RequestChan:
|
||||
timer.Stop()
|
||||
t.Log("received LDS request after stream re-creation")
|
||||
if !proto.Equal(gotRequest.Req, goodLDSRequest) {
|
||||
t.Fatalf("gotRequest: %+v, wantRequest: %+v", gotRequest.Req, goodLDSRequest)
|
||||
}
|
||||
val, err := fakeServer.XDSRequestChan.Receive()
|
||||
if err == testutils.ErrRecvTimeout {
|
||||
t.Fatalf("Timeout expired when expecting LDS update")
|
||||
}
|
||||
gotRequest := val.(*fakeserver.Request)
|
||||
if !proto.Equal(gotRequest.Req, goodLDSRequest) {
|
||||
t.Fatalf("gotRequest: %+v, wantRequest: %+v", gotRequest.Req, goodLDSRequest)
|
||||
}
|
||||
}
|
||||
|
||||
// TestV2ClientCancelWatch verifies that the registered watch callback is not
|
||||
// invoked if a response is received after the watcher is cancelled.
|
||||
func TestV2ClientCancelWatch(t *testing.T) {
|
||||
fakeServer, sCleanup := fakexds.StartServer(t)
|
||||
client, cCleanup := fakeServer.GetClientConn(t)
|
||||
defer func() {
|
||||
cCleanup()
|
||||
sCleanup()
|
||||
}()
|
||||
v2c := newV2Client(client, goodNodeProto, func(int) time.Duration { return 0 })
|
||||
fakeServer, cc, cleanup := startServerAndGetCC(t)
|
||||
defer cleanup()
|
||||
|
||||
v2c := newV2Client(cc, goodNodeProto, func(int) time.Duration { return 0 })
|
||||
defer v2c.close()
|
||||
t.Log("Started xds v2Client...")
|
||||
|
||||
callbackCh := make(chan struct{}, 1)
|
||||
callbackCh := testutils.NewChannel()
|
||||
cancelFunc := v2c.watchLDS(goodLDSTarget1, func(u ldsUpdate, err error) {
|
||||
t.Logf("Received LDS callback with ldsUpdate {%+v} and error {%v}", u, err)
|
||||
callbackCh <- struct{}{}
|
||||
callbackCh.Send(struct{}{})
|
||||
})
|
||||
<-fakeServer.RequestChan
|
||||
if _, err := fakeServer.XDSRequestChan.Receive(); err != nil {
|
||||
t.Fatalf("Timeout expired when expecting an LDS request")
|
||||
}
|
||||
t.Log("FakeServer received request...")
|
||||
|
||||
fakeServer.ResponseChan <- &fakexds.Response{Resp: goodLDSResponse1}
|
||||
fakeServer.XDSResponseChan <- &fakeserver.Response{Resp: goodLDSResponse1}
|
||||
t.Log("Good LDS response pushed to fakeServer...")
|
||||
|
||||
timer := time.NewTimer(defaultTestTimeout)
|
||||
select {
|
||||
case <-timer.C:
|
||||
if _, err := callbackCh.Receive(); err != nil {
|
||||
t.Fatal("Timeout when expecting LDS update")
|
||||
case <-callbackCh:
|
||||
timer.Stop()
|
||||
}
|
||||
|
||||
cancelFunc()
|
||||
|
||||
fakeServer.ResponseChan <- &fakexds.Response{Resp: goodLDSResponse1}
|
||||
fakeServer.XDSResponseChan <- &fakeserver.Response{Resp: goodLDSResponse1}
|
||||
t.Log("Another good LDS response pushed to fakeServer...")
|
||||
|
||||
timer = time.NewTimer(defaultTestTimeout)
|
||||
select {
|
||||
case <-timer.C:
|
||||
case <-callbackCh:
|
||||
timer.Stop()
|
||||
if _, err := callbackCh.Receive(); err != testutils.ErrRecvTimeout {
|
||||
t.Fatalf("Watch callback invoked after the watcher was cancelled")
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ import (
|
|||
xdsclient "google.golang.org/grpc/xds/internal/client"
|
||||
"google.golang.org/grpc/xds/internal/client/bootstrap"
|
||||
"google.golang.org/grpc/xds/internal/testutils"
|
||||
"google.golang.org/grpc/xds/internal/testutils/fakexds"
|
||||
"google.golang.org/grpc/xds/internal/testutils/fakeclient"
|
||||
|
||||
corepb "github.com/envoyproxy/go-control-plane/envoy/api/v2/core"
|
||||
)
|
||||
|
|
@ -98,7 +98,7 @@ func getXDSClientMakerFunc(wantOpts xdsclient.Options) func(xdsclient.Options) (
|
|||
if len(gotOpts.DialOpts) != len(wantOpts.DialOpts) {
|
||||
return nil, fmt.Errorf("got len(DialOpts): %v, want: %v", len(gotOpts.DialOpts), len(wantOpts.DialOpts))
|
||||
}
|
||||
return fakexds.NewClient(), nil
|
||||
return fakeclient.NewClient(), nil
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -232,7 +232,7 @@ func testSetup(t *testing.T, opts setupOpts) (*xdsResolver, *testClientConn, fun
|
|||
// waitForWatchService waits for the WatchService method to be called on the
|
||||
// xdsClient within a reasonable amount of time, and also verifies that the
|
||||
// watch is called with the expected target.
|
||||
func waitForWatchService(t *testing.T, xdsC *fakexds.Client, wantTarget string) {
|
||||
func waitForWatchService(t *testing.T, xdsC *fakeclient.Client, wantTarget string) {
|
||||
t.Helper()
|
||||
|
||||
gotTarget, err := xdsC.WaitForWatchService()
|
||||
|
|
@ -247,7 +247,7 @@ func waitForWatchService(t *testing.T, xdsC *fakexds.Client, wantTarget string)
|
|||
// TestXDSResolverWatchCallbackAfterClose tests the case where a service update
|
||||
// from the underlying xdsClient is received after the resolver is closed.
|
||||
func TestXDSResolverWatchCallbackAfterClose(t *testing.T) {
|
||||
xdsC := fakexds.NewClient()
|
||||
xdsC := fakeclient.NewClient()
|
||||
xdsR, tcc, cancel := testSetup(t, setupOpts{
|
||||
config: &validConfig,
|
||||
xdsClientFunc: func(_ xdsclient.Options) (xdsClientInterface, error) { return xdsC, nil },
|
||||
|
|
@ -268,7 +268,7 @@ func TestXDSResolverWatchCallbackAfterClose(t *testing.T) {
|
|||
// TestXDSResolverBadServiceUpdate tests the case the xdsClient returns a bad
|
||||
// service update.
|
||||
func TestXDSResolverBadServiceUpdate(t *testing.T) {
|
||||
xdsC := fakexds.NewClient()
|
||||
xdsC := fakeclient.NewClient()
|
||||
xdsR, tcc, cancel := testSetup(t, setupOpts{
|
||||
config: &validConfig,
|
||||
xdsClientFunc: func(_ xdsclient.Options) (xdsClientInterface, error) { return xdsC, nil },
|
||||
|
|
@ -292,7 +292,7 @@ func TestXDSResolverBadServiceUpdate(t *testing.T) {
|
|||
// TestXDSResolverGoodServiceUpdate tests the happy case where the resolver
|
||||
// gets a good service update from the xdsClient.
|
||||
func TestXDSResolverGoodServiceUpdate(t *testing.T) {
|
||||
xdsC := fakexds.NewClient()
|
||||
xdsC := fakeclient.NewClient()
|
||||
xdsR, tcc, cancel := testSetup(t, setupOpts{
|
||||
config: &validConfig,
|
||||
xdsClientFunc: func(_ xdsclient.Options) (xdsClientInterface, error) { return xdsC, nil },
|
||||
|
|
|
|||
|
|
@ -16,9 +16,8 @@
|
|||
*
|
||||
*/
|
||||
|
||||
// Package fakexds provides fake implementation of multiple types, for use in
|
||||
// xds tests.
|
||||
package fakexds
|
||||
// Package fakeclient provides a fake implementation of an xDS client.
|
||||
package fakeclient
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
|
@ -16,12 +16,15 @@
|
|||
*
|
||||
*/
|
||||
|
||||
package fakexds
|
||||
// Package fakeserver provides a fake implementation of an xDS server.
|
||||
package fakeserver
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"github.com/golang/protobuf/proto"
|
||||
"google.golang.org/grpc"
|
||||
|
|
@ -35,9 +38,12 @@ import (
|
|||
lrspb "github.com/envoyproxy/go-control-plane/envoy/service/load_stats/v2"
|
||||
)
|
||||
|
||||
// TODO: Make this a var or a field in the server if there is a need to use a
|
||||
// value other than this default.
|
||||
const defaultChannelBufferSize = 50
|
||||
const (
|
||||
// TODO: Make this a var or a field in the server if there is a need to use a
|
||||
// value other than this default.
|
||||
defaultChannelBufferSize = 50
|
||||
defaultDialTimeout = 5 * time.Second
|
||||
)
|
||||
|
||||
// Request wraps the request protobuf (xds/LRS) and error received by the
|
||||
// Server in a call to stream.Recv().
|
||||
|
|
@ -104,6 +110,18 @@ func StartServer() (*Server, func(), error) {
|
|||
return s, func() { server.Stop() }, nil
|
||||
}
|
||||
|
||||
// XDSClientConn returns a grpc.ClientConn connected to the fakeServer.
|
||||
func (xdsS *Server) XDSClientConn() (*grpc.ClientConn, func(), error) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), defaultDialTimeout)
|
||||
defer cancel()
|
||||
|
||||
cc, err := grpc.DialContext(ctx, xdsS.Address, grpc.WithInsecure(), grpc.WithBlock())
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("grpc.DialContext(%s) failed: %v", xdsS.Address, err)
|
||||
}
|
||||
return cc, func() { cc.Close() }, nil
|
||||
}
|
||||
|
||||
type xdsServer struct {
|
||||
reqChan *testutils.Channel
|
||||
respChan chan *Response
|
||||
Loading…
Reference in New Issue