github.com/hxx258456/ccgo@v0.0.5-0.20230213014102-48b35f46f66f/grpc/xds/internal/balancer/clusterresolver/resource_resolver_test.go (about)

     1  /*
     2   *
     3   * Copyright 2021 gRPC authors.
     4   *
     5   * Licensed under the Apache License, Version 2.0 (the "License");
     6   * you may not use this file except in compliance with the License.
     7   * You may obtain a copy of the License at
     8   *
     9   *     http://www.apache.org/licenses/LICENSE-2.0
    10   *
    11   * Unless required by applicable law or agreed to in writing, software
    12   * distributed under the License is distributed on an "AS IS" BASIS,
    13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    14   * See the License for the specific language governing permissions and
    15   * limitations under the License.
    16   *
    17   */
    18  
    19  package clusterresolver
    20  
    21  import (
    22  	"context"
    23  	"fmt"
    24  	"testing"
    25  
    26  	"github.com/google/go-cmp/cmp"
    27  	"github.com/hxx258456/ccgo/grpc/resolver"
    28  	"github.com/hxx258456/ccgo/grpc/resolver/manual"
    29  	"github.com/hxx258456/ccgo/grpc/xds/internal/testutils"
    30  	"github.com/hxx258456/ccgo/grpc/xds/internal/testutils/fakeclient"
    31  	"github.com/hxx258456/ccgo/grpc/xds/internal/xdsclient/xdsresource"
    32  )
    33  
    34  const (
    35  	testDNSTarget = "dns.com"
    36  )
    37  
    38  var (
    39  	testEDSUpdates []xdsresource.EndpointsUpdate
    40  )
    41  
    42  func init() {
    43  	clab1 := testutils.NewClusterLoadAssignmentBuilder(testClusterNames[0], nil)
    44  	clab1.AddLocality(testSubZones[0], 1, 0, testEndpointAddrs[:1], nil)
    45  	testEDSUpdates = append(testEDSUpdates, parseEDSRespProtoForTesting(clab1.Build()))
    46  	clab2 := testutils.NewClusterLoadAssignmentBuilder(testClusterNames[0], nil)
    47  	clab2.AddLocality(testSubZones[1], 1, 0, testEndpointAddrs[1:2], nil)
    48  	testEDSUpdates = append(testEDSUpdates, parseEDSRespProtoForTesting(clab2.Build()))
    49  }
    50  
    51  // Test the simple case with one EDS resource to watch.
    52  func (s) TestResourceResolverOneEDSResource(t *testing.T) {
    53  	for _, test := range []struct {
    54  		name                 string
    55  		clusterName, edsName string
    56  		wantName             string
    57  		edsUpdate            xdsresource.EndpointsUpdate
    58  		want                 []priorityConfig
    59  	}{
    60  		{name: "watch EDS",
    61  			clusterName: testClusterName,
    62  			edsName:     testEDSServcie,
    63  			wantName:    testEDSServcie,
    64  			edsUpdate:   testEDSUpdates[0],
    65  			want: []priorityConfig{{
    66  				mechanism: DiscoveryMechanism{
    67  					Type:           DiscoveryMechanismTypeEDS,
    68  					Cluster:        testClusterName,
    69  					EDSServiceName: testEDSServcie,
    70  				},
    71  				edsResp: testEDSUpdates[0],
    72  			}},
    73  		},
    74  		{
    75  			name:        "watch EDS no EDS name", // Will watch for cluster name.
    76  			clusterName: testClusterName,
    77  			wantName:    testClusterName,
    78  			edsUpdate:   testEDSUpdates[1],
    79  			want: []priorityConfig{{
    80  				mechanism: DiscoveryMechanism{
    81  					Type:    DiscoveryMechanismTypeEDS,
    82  					Cluster: testClusterName,
    83  				},
    84  				edsResp: testEDSUpdates[1],
    85  			}},
    86  		},
    87  	} {
    88  		t.Run(test.name, func(t *testing.T) {
    89  			fakeClient := fakeclient.NewClient()
    90  			rr := newResourceResolver(&clusterResolverBalancer{xdsClient: fakeClient})
    91  			rr.updateMechanisms([]DiscoveryMechanism{{
    92  				Type:           DiscoveryMechanismTypeEDS,
    93  				Cluster:        test.clusterName,
    94  				EDSServiceName: test.edsName,
    95  			}})
    96  			ctx, ctxCancel := context.WithTimeout(context.Background(), defaultTestTimeout)
    97  			defer ctxCancel()
    98  			gotEDSName, err := fakeClient.WaitForWatchEDS(ctx)
    99  			if err != nil {
   100  				t.Fatalf("xdsClient.WatchCDS failed with error: %v", err)
   101  			}
   102  			if gotEDSName != test.wantName {
   103  				t.Fatalf("xdsClient.WatchEDS called for cluster: %v, want: %v", gotEDSName, test.wantName)
   104  			}
   105  
   106  			// Invoke callback, should get an update.
   107  			fakeClient.InvokeWatchEDSCallback("", test.edsUpdate, nil)
   108  			select {
   109  			case u := <-rr.updateChannel:
   110  				if diff := cmp.Diff(u.priorities, test.want, cmp.AllowUnexported(priorityConfig{})); diff != "" {
   111  					t.Fatalf("got unexpected resource update, diff (-got, +want): %v", diff)
   112  				}
   113  			case <-ctx.Done():
   114  				t.Fatal("Timed out waiting for update from update channel.")
   115  			}
   116  			// Close the resource resolver. Should stop EDS watch.
   117  			rr.stop()
   118  			edsNameCanceled, err := fakeClient.WaitForCancelEDSWatch(ctx)
   119  			if err != nil {
   120  				t.Fatalf("xdsClient.CancelCDS failed with error: %v", err)
   121  			}
   122  			if edsNameCanceled != test.wantName {
   123  				t.Fatalf("xdsClient.CancelEDS called for %v, want: %v", edsNameCanceled, testEDSServcie)
   124  			}
   125  		})
   126  	}
   127  }
   128  
   129  func setupDNS() (chan resolver.Target, chan struct{}, chan resolver.ResolveNowOptions, *manual.Resolver, func()) {
   130  	dnsTargetCh := make(chan resolver.Target, 1)
   131  	dnsCloseCh := make(chan struct{}, 1)
   132  	resolveNowCh := make(chan resolver.ResolveNowOptions, 1)
   133  
   134  	mr := manual.NewBuilderWithScheme("dns")
   135  	mr.BuildCallback = func(target resolver.Target, _ resolver.ClientConn, _ resolver.BuildOptions) { dnsTargetCh <- target }
   136  	mr.CloseCallback = func() { dnsCloseCh <- struct{}{} }
   137  	mr.ResolveNowCallback = func(opts resolver.ResolveNowOptions) { resolveNowCh <- opts }
   138  	oldNewDNS := newDNS
   139  	newDNS = func(target resolver.Target, cc resolver.ClientConn, opts resolver.BuildOptions) (resolver.Resolver, error) {
   140  		return mr.Build(target, cc, opts)
   141  	}
   142  	return dnsTargetCh, dnsCloseCh, resolveNowCh, mr, func() { newDNS = oldNewDNS }
   143  }
   144  
   145  // Test the simple case of one DNS resolver.
   146  func (s) TestResourceResolverOneDNSResource(t *testing.T) {
   147  	for _, test := range []struct {
   148  		name       string
   149  		target     string
   150  		wantTarget resolver.Target
   151  		addrs      []resolver.Address
   152  		want       []priorityConfig
   153  	}{
   154  		{
   155  			name:       "watch DNS",
   156  			target:     testDNSTarget,
   157  			wantTarget: resolver.Target{Scheme: "dns", Endpoint: testDNSTarget},
   158  			addrs:      []resolver.Address{{Addr: "1.1.1.1"}, {Addr: "2.2.2.2"}},
   159  			want: []priorityConfig{{
   160  				mechanism: DiscoveryMechanism{
   161  					Type:        DiscoveryMechanismTypeLogicalDNS,
   162  					DNSHostname: testDNSTarget,
   163  				},
   164  				addresses: []string{"1.1.1.1", "2.2.2.2"},
   165  			}},
   166  		},
   167  	} {
   168  		t.Run(test.name, func(t *testing.T) {
   169  			dnsTargetCh, dnsCloseCh, _, dnsR, cleanup := setupDNS()
   170  			defer cleanup()
   171  			fakeClient := fakeclient.NewClient()
   172  			rr := newResourceResolver(&clusterResolverBalancer{xdsClient: fakeClient})
   173  			rr.updateMechanisms([]DiscoveryMechanism{{
   174  				Type:        DiscoveryMechanismTypeLogicalDNS,
   175  				DNSHostname: test.target,
   176  			}})
   177  			ctx, ctxCancel := context.WithTimeout(context.Background(), defaultTestTimeout)
   178  			defer ctxCancel()
   179  			select {
   180  			case target := <-dnsTargetCh:
   181  				if diff := cmp.Diff(target, test.wantTarget); diff != "" {
   182  					t.Fatalf("got unexpected DNS target to watch, diff (-got, +want): %v", diff)
   183  				}
   184  			case <-ctx.Done():
   185  				t.Fatal("Timed out waiting for building DNS resolver")
   186  			}
   187  
   188  			// Invoke callback, should get an update.
   189  			dnsR.UpdateState(resolver.State{Addresses: test.addrs})
   190  			select {
   191  			case u := <-rr.updateChannel:
   192  				if diff := cmp.Diff(u.priorities, test.want, cmp.AllowUnexported(priorityConfig{})); diff != "" {
   193  					t.Fatalf("got unexpected resource update, diff (-got, +want): %v", diff)
   194  				}
   195  			case <-ctx.Done():
   196  				t.Fatal("Timed out waiting for update from update channel.")
   197  			}
   198  			// Close the resource resolver. Should close the underlying resolver.
   199  			rr.stop()
   200  			select {
   201  			case <-dnsCloseCh:
   202  			case <-ctx.Done():
   203  				t.Fatal("Timed out waiting for closing DNS resolver")
   204  			}
   205  		})
   206  	}
   207  }
   208  
   209  // Test that changing EDS name would cause a cancel and a new watch.
   210  //
   211  // Also, changes that don't actually change EDS names (e.g. changing cluster
   212  // name but not service name, or change circuit breaking count) doesn't do
   213  // anything.
   214  //
   215  // - update DiscoveryMechanism
   216  // - same EDS name to watch, but different MaxCurrentCount: no new watch
   217  // - different cluster name, but same EDS name: no new watch
   218  func (s) TestResourceResolverChangeEDSName(t *testing.T) {
   219  	fakeClient := fakeclient.NewClient()
   220  	rr := newResourceResolver(&clusterResolverBalancer{xdsClient: fakeClient})
   221  	rr.updateMechanisms([]DiscoveryMechanism{{
   222  		Type:           DiscoveryMechanismTypeEDS,
   223  		Cluster:        testClusterName,
   224  		EDSServiceName: testEDSServcie,
   225  	}})
   226  	ctx, ctxCancel := context.WithTimeout(context.Background(), defaultTestTimeout)
   227  	defer ctxCancel()
   228  	gotEDSName1, err := fakeClient.WaitForWatchEDS(ctx)
   229  	if err != nil {
   230  		t.Fatalf("xdsClient.WatchCDS failed with error: %v", err)
   231  	}
   232  	if gotEDSName1 != testEDSServcie {
   233  		t.Fatalf("xdsClient.WatchEDS called for cluster: %v, want: %v", gotEDSName1, testEDSServcie)
   234  	}
   235  
   236  	// Invoke callback, should get an update.
   237  	fakeClient.InvokeWatchEDSCallback(gotEDSName1, testEDSUpdates[0], nil)
   238  	select {
   239  	case u := <-rr.updateChannel:
   240  		if diff := cmp.Diff(u.priorities, []priorityConfig{{
   241  			mechanism: DiscoveryMechanism{
   242  				Type:           DiscoveryMechanismTypeEDS,
   243  				Cluster:        testClusterName,
   244  				EDSServiceName: testEDSServcie,
   245  			},
   246  			edsResp: testEDSUpdates[0],
   247  		}}, cmp.AllowUnexported(priorityConfig{})); diff != "" {
   248  			t.Fatalf("got unexpected resource update, diff (-got, +want): %v", diff)
   249  		}
   250  	case <-ctx.Done():
   251  		t.Fatal("Timed out waiting for update from update channel.")
   252  	}
   253  
   254  	// Change name to watch.
   255  	rr.updateMechanisms([]DiscoveryMechanism{{
   256  		Type:    DiscoveryMechanismTypeEDS,
   257  		Cluster: testClusterName,
   258  	}})
   259  	edsNameCanceled1, err := fakeClient.WaitForCancelEDSWatch(ctx)
   260  	if err != nil {
   261  		t.Fatalf("xdsClient.CancelCDS failed with error: %v", err)
   262  	}
   263  	if edsNameCanceled1 != gotEDSName1 {
   264  		t.Fatalf("xdsClient.CancelEDS called for %v, want: %v", edsNameCanceled1, testEDSServcie)
   265  	}
   266  	gotEDSName2, err := fakeClient.WaitForWatchEDS(ctx)
   267  	if err != nil {
   268  		t.Fatalf("xdsClient.WatchCDS failed with error: %v", err)
   269  	}
   270  	if gotEDSName2 != testClusterName {
   271  		t.Fatalf("xdsClient.WatchEDS called for cluster: %v, want: %v", gotEDSName2, testClusterName)
   272  	}
   273  	// Shouldn't get any update, because the new resource hasn't received any
   274  	// update.
   275  	shortCtx, shortCancel := context.WithTimeout(context.Background(), defaultTestShortTimeout)
   276  	defer shortCancel()
   277  	select {
   278  	case u := <-rr.updateChannel:
   279  		t.Fatalf("get unexpected update %+v", u)
   280  	case <-shortCtx.Done():
   281  	}
   282  
   283  	// Invoke callback, should get an update.
   284  	fakeClient.InvokeWatchEDSCallback(gotEDSName2, testEDSUpdates[1], nil)
   285  	select {
   286  	case u := <-rr.updateChannel:
   287  		if diff := cmp.Diff(u.priorities, []priorityConfig{{
   288  			mechanism: DiscoveryMechanism{
   289  				Type:    DiscoveryMechanismTypeEDS,
   290  				Cluster: testClusterName,
   291  			},
   292  			edsResp: testEDSUpdates[1],
   293  		}}, cmp.AllowUnexported(priorityConfig{})); diff != "" {
   294  			t.Fatalf("got unexpected resource update, diff (-got, +want): %v", diff)
   295  		}
   296  	case <-ctx.Done():
   297  		t.Fatal("Timed out waiting for update from update channel.")
   298  	}
   299  
   300  	// Change circuit breaking count, should get an update with new circuit
   301  	// breaking count, but shouldn't trigger new watch.
   302  	rr.updateMechanisms([]DiscoveryMechanism{{
   303  		Type:                  DiscoveryMechanismTypeEDS,
   304  		Cluster:               testClusterName,
   305  		MaxConcurrentRequests: newUint32(123),
   306  	}})
   307  	shortCtx, shortCancel = context.WithTimeout(context.Background(), defaultTestShortTimeout)
   308  	defer shortCancel()
   309  	if n, err := fakeClient.WaitForWatchEDS(shortCtx); err == nil {
   310  		t.Fatalf("unexpected watch started for EDS: %v", n)
   311  	}
   312  	select {
   313  	case u := <-rr.updateChannel:
   314  		if diff := cmp.Diff(u.priorities, []priorityConfig{{
   315  			mechanism: DiscoveryMechanism{
   316  				Type:                  DiscoveryMechanismTypeEDS,
   317  				Cluster:               testClusterName,
   318  				MaxConcurrentRequests: newUint32(123),
   319  			},
   320  			edsResp: testEDSUpdates[1],
   321  		}}, cmp.AllowUnexported(priorityConfig{})); diff != "" {
   322  			t.Fatalf("got unexpected resource update, diff (-got, +want): %v", diff)
   323  		}
   324  	case <-ctx.Done():
   325  		t.Fatal("Timed out waiting for update from update channel.")
   326  	}
   327  
   328  	// Close the resource resolver. Should stop EDS watch.
   329  	rr.stop()
   330  	edsNameCanceled, err := fakeClient.WaitForCancelEDSWatch(ctx)
   331  	if err != nil {
   332  		t.Fatalf("xdsClient.CancelCDS failed with error: %v", err)
   333  	}
   334  	if edsNameCanceled != gotEDSName2 {
   335  		t.Fatalf("xdsClient.CancelEDS called for %v, want: %v", edsNameCanceled, gotEDSName2)
   336  	}
   337  }
   338  
   339  // Test the case that same resources with the same priority should not add new
   340  // EDS watch, and also should not trigger an update.
   341  func (s) TestResourceResolverNoChangeNoUpdate(t *testing.T) {
   342  	fakeClient := fakeclient.NewClient()
   343  	rr := newResourceResolver(&clusterResolverBalancer{xdsClient: fakeClient})
   344  	rr.updateMechanisms([]DiscoveryMechanism{
   345  		{
   346  			Type:    DiscoveryMechanismTypeEDS,
   347  			Cluster: testClusterNames[0],
   348  		},
   349  		{
   350  			Type:                  DiscoveryMechanismTypeEDS,
   351  			Cluster:               testClusterNames[1],
   352  			MaxConcurrentRequests: newUint32(100),
   353  		},
   354  	})
   355  	ctx, ctxCancel := context.WithTimeout(context.Background(), defaultTestTimeout)
   356  	defer ctxCancel()
   357  	gotEDSName1, err := fakeClient.WaitForWatchEDS(ctx)
   358  	if err != nil {
   359  		t.Fatalf("xdsClient.WatchCDS failed with error: %v", err)
   360  	}
   361  	if gotEDSName1 != testClusterNames[0] {
   362  		t.Fatalf("xdsClient.WatchEDS called for cluster: %v, want: %v", gotEDSName1, testClusterNames[0])
   363  	}
   364  	gotEDSName2, err := fakeClient.WaitForWatchEDS(ctx)
   365  	if err != nil {
   366  		t.Fatalf("xdsClient.WatchCDS failed with error: %v", err)
   367  	}
   368  	if gotEDSName2 != testClusterNames[1] {
   369  		t.Fatalf("xdsClient.WatchEDS called for cluster: %v, want: %v", gotEDSName2, testClusterNames[1])
   370  	}
   371  
   372  	// Invoke callback, should get an update.
   373  	fakeClient.InvokeWatchEDSCallback(gotEDSName1, testEDSUpdates[0], nil)
   374  	// Shouldn't send update, because only one resource received an update.
   375  	shortCtx, shortCancel := context.WithTimeout(context.Background(), defaultTestShortTimeout)
   376  	defer shortCancel()
   377  	select {
   378  	case u := <-rr.updateChannel:
   379  		t.Fatalf("get unexpected update %+v", u)
   380  	case <-shortCtx.Done():
   381  	}
   382  	fakeClient.InvokeWatchEDSCallback(gotEDSName2, testEDSUpdates[1], nil)
   383  	select {
   384  	case u := <-rr.updateChannel:
   385  		if diff := cmp.Diff(u.priorities, []priorityConfig{
   386  			{
   387  				mechanism: DiscoveryMechanism{
   388  					Type:    DiscoveryMechanismTypeEDS,
   389  					Cluster: testClusterNames[0],
   390  				},
   391  				edsResp: testEDSUpdates[0],
   392  			},
   393  			{
   394  				mechanism: DiscoveryMechanism{
   395  					Type:                  DiscoveryMechanismTypeEDS,
   396  					Cluster:               testClusterNames[1],
   397  					MaxConcurrentRequests: newUint32(100),
   398  				},
   399  				edsResp: testEDSUpdates[1],
   400  			},
   401  		}, cmp.AllowUnexported(priorityConfig{})); diff != "" {
   402  			t.Fatalf("got unexpected resource update, diff (-got, +want): %v", diff)
   403  		}
   404  	case <-ctx.Done():
   405  		t.Fatal("Timed out waiting for update from update channel.")
   406  	}
   407  
   408  	// Send the same resources with the same priorities, shouldn't any change.
   409  	rr.updateMechanisms([]DiscoveryMechanism{
   410  		{
   411  			Type:    DiscoveryMechanismTypeEDS,
   412  			Cluster: testClusterNames[0],
   413  		},
   414  		{
   415  			Type:                  DiscoveryMechanismTypeEDS,
   416  			Cluster:               testClusterNames[1],
   417  			MaxConcurrentRequests: newUint32(100),
   418  		},
   419  	})
   420  	shortCtx, shortCancel = context.WithTimeout(context.Background(), defaultTestShortTimeout)
   421  	defer shortCancel()
   422  	if n, err := fakeClient.WaitForWatchEDS(shortCtx); err == nil {
   423  		t.Fatalf("unexpected watch started for EDS: %v", n)
   424  	}
   425  	shortCtx, shortCancel = context.WithTimeout(context.Background(), defaultTestShortTimeout)
   426  	defer shortCancel()
   427  	select {
   428  	case u := <-rr.updateChannel:
   429  		t.Fatalf("unexpected update: %+v", u)
   430  	case <-shortCtx.Done():
   431  	}
   432  
   433  	// Close the resource resolver. Should stop EDS watch.
   434  	rr.stop()
   435  	edsNameCanceled1, err := fakeClient.WaitForCancelEDSWatch(ctx)
   436  	if err != nil {
   437  		t.Fatalf("xdsClient.CancelCDS failed with error: %v", err)
   438  	}
   439  	if edsNameCanceled1 != gotEDSName1 && edsNameCanceled1 != gotEDSName2 {
   440  		t.Fatalf("xdsClient.CancelEDS called for %v, want: %v or %v", edsNameCanceled1, gotEDSName1, gotEDSName2)
   441  	}
   442  	edsNameCanceled2, err := fakeClient.WaitForCancelEDSWatch(ctx)
   443  	if err != nil {
   444  		t.Fatalf("xdsClient.CancelCDS failed with error: %v", err)
   445  	}
   446  	if edsNameCanceled2 != gotEDSName2 && edsNameCanceled2 != gotEDSName1 {
   447  		t.Fatalf("xdsClient.CancelEDS called for %v, want: %v or %v", edsNameCanceled2, gotEDSName1, gotEDSName2)
   448  	}
   449  }
   450  
   451  // Test the case that same resources are watched, but with different priority.
   452  // Should not add new EDS watch, but should trigger an update with the new
   453  // priorities.
   454  func (s) TestResourceResolverChangePriority(t *testing.T) {
   455  	fakeClient := fakeclient.NewClient()
   456  	rr := newResourceResolver(&clusterResolverBalancer{xdsClient: fakeClient})
   457  	rr.updateMechanisms([]DiscoveryMechanism{
   458  		{
   459  			Type:    DiscoveryMechanismTypeEDS,
   460  			Cluster: testClusterNames[0],
   461  		},
   462  		{
   463  			Type:    DiscoveryMechanismTypeEDS,
   464  			Cluster: testClusterNames[1],
   465  		},
   466  	})
   467  	ctx, ctxCancel := context.WithTimeout(context.Background(), defaultTestTimeout)
   468  	defer ctxCancel()
   469  	gotEDSName1, err := fakeClient.WaitForWatchEDS(ctx)
   470  	if err != nil {
   471  		t.Fatalf("xdsClient.WatchCDS failed with error: %v", err)
   472  	}
   473  	if gotEDSName1 != testClusterNames[0] {
   474  		t.Fatalf("xdsClient.WatchEDS called for cluster: %v, want: %v", gotEDSName1, testClusterNames[0])
   475  	}
   476  	gotEDSName2, err := fakeClient.WaitForWatchEDS(ctx)
   477  	if err != nil {
   478  		t.Fatalf("xdsClient.WatchCDS failed with error: %v", err)
   479  	}
   480  	if gotEDSName2 != testClusterNames[1] {
   481  		t.Fatalf("xdsClient.WatchEDS called for cluster: %v, want: %v", gotEDSName2, testClusterNames[1])
   482  	}
   483  
   484  	// Invoke callback, should get an update.
   485  	fakeClient.InvokeWatchEDSCallback(gotEDSName1, testEDSUpdates[0], nil)
   486  	// Shouldn't send update, because only one resource received an update.
   487  	shortCtx, shortCancel := context.WithTimeout(context.Background(), defaultTestShortTimeout)
   488  	defer shortCancel()
   489  	select {
   490  	case u := <-rr.updateChannel:
   491  		t.Fatalf("get unexpected update %+v", u)
   492  	case <-shortCtx.Done():
   493  	}
   494  	fakeClient.InvokeWatchEDSCallback(gotEDSName2, testEDSUpdates[1], nil)
   495  	select {
   496  	case u := <-rr.updateChannel:
   497  		if diff := cmp.Diff(u.priorities, []priorityConfig{
   498  			{
   499  				mechanism: DiscoveryMechanism{
   500  					Type:    DiscoveryMechanismTypeEDS,
   501  					Cluster: testClusterNames[0],
   502  				},
   503  				edsResp: testEDSUpdates[0],
   504  			},
   505  			{
   506  				mechanism: DiscoveryMechanism{
   507  					Type:    DiscoveryMechanismTypeEDS,
   508  					Cluster: testClusterNames[1],
   509  				},
   510  				edsResp: testEDSUpdates[1],
   511  			},
   512  		}, cmp.AllowUnexported(priorityConfig{})); diff != "" {
   513  			t.Fatalf("got unexpected resource update, diff (-got, +want): %v", diff)
   514  		}
   515  	case <-ctx.Done():
   516  		t.Fatal("Timed out waiting for update from update channel.")
   517  	}
   518  
   519  	// Send the same resources with different priorities, shouldn't trigger
   520  	// watch, but should trigger an update with the new priorities.
   521  	rr.updateMechanisms([]DiscoveryMechanism{
   522  		{
   523  			Type:    DiscoveryMechanismTypeEDS,
   524  			Cluster: testClusterNames[1],
   525  		},
   526  		{
   527  			Type:    DiscoveryMechanismTypeEDS,
   528  			Cluster: testClusterNames[0],
   529  		},
   530  	})
   531  	shortCtx, shortCancel = context.WithTimeout(context.Background(), defaultTestShortTimeout)
   532  	defer shortCancel()
   533  	if n, err := fakeClient.WaitForWatchEDS(shortCtx); err == nil {
   534  		t.Fatalf("unexpected watch started for EDS: %v", n)
   535  	}
   536  	select {
   537  	case u := <-rr.updateChannel:
   538  		if diff := cmp.Diff(u.priorities, []priorityConfig{
   539  			{
   540  				mechanism: DiscoveryMechanism{
   541  					Type:    DiscoveryMechanismTypeEDS,
   542  					Cluster: testClusterNames[1],
   543  				},
   544  				edsResp: testEDSUpdates[1],
   545  			},
   546  			{
   547  				mechanism: DiscoveryMechanism{
   548  					Type:    DiscoveryMechanismTypeEDS,
   549  					Cluster: testClusterNames[0],
   550  				},
   551  				edsResp: testEDSUpdates[0],
   552  			},
   553  		}, cmp.AllowUnexported(priorityConfig{})); diff != "" {
   554  			t.Fatalf("got unexpected resource update, diff (-got, +want): %v", diff)
   555  		}
   556  	case <-ctx.Done():
   557  		t.Fatal("Timed out waiting for update from update channel.")
   558  	}
   559  
   560  	// Close the resource resolver. Should stop EDS watch.
   561  	rr.stop()
   562  	edsNameCanceled1, err := fakeClient.WaitForCancelEDSWatch(ctx)
   563  	if err != nil {
   564  		t.Fatalf("xdsClient.CancelCDS failed with error: %v", err)
   565  	}
   566  	if edsNameCanceled1 != gotEDSName1 && edsNameCanceled1 != gotEDSName2 {
   567  		t.Fatalf("xdsClient.CancelEDS called for %v, want: %v or %v", edsNameCanceled1, gotEDSName1, gotEDSName2)
   568  	}
   569  	edsNameCanceled2, err := fakeClient.WaitForCancelEDSWatch(ctx)
   570  	if err != nil {
   571  		t.Fatalf("xdsClient.CancelCDS failed with error: %v", err)
   572  	}
   573  	if edsNameCanceled2 != gotEDSName2 && edsNameCanceled2 != gotEDSName1 {
   574  		t.Fatalf("xdsClient.CancelEDS called for %v, want: %v or %v", edsNameCanceled2, gotEDSName1, gotEDSName2)
   575  	}
   576  }
   577  
   578  // Test the case that covers resource for both EDS and DNS.
   579  func (s) TestResourceResolverEDSAndDNS(t *testing.T) {
   580  	dnsTargetCh, dnsCloseCh, _, dnsR, cleanup := setupDNS()
   581  	defer cleanup()
   582  	fakeClient := fakeclient.NewClient()
   583  	rr := newResourceResolver(&clusterResolverBalancer{xdsClient: fakeClient})
   584  	rr.updateMechanisms([]DiscoveryMechanism{
   585  		{
   586  			Type:    DiscoveryMechanismTypeEDS,
   587  			Cluster: testClusterName,
   588  		},
   589  		{
   590  			Type:        DiscoveryMechanismTypeLogicalDNS,
   591  			DNSHostname: testDNSTarget,
   592  		},
   593  	})
   594  	ctx, ctxCancel := context.WithTimeout(context.Background(), defaultTestTimeout)
   595  	defer ctxCancel()
   596  	gotEDSName1, err := fakeClient.WaitForWatchEDS(ctx)
   597  	if err != nil {
   598  		t.Fatalf("xdsClient.WatchCDS failed with error: %v", err)
   599  	}
   600  	if gotEDSName1 != testClusterName {
   601  		t.Fatalf("xdsClient.WatchEDS called for cluster: %v, want: %v", gotEDSName1, testClusterName)
   602  	}
   603  	select {
   604  	case target := <-dnsTargetCh:
   605  		if diff := cmp.Diff(target, resolver.Target{Scheme: "dns", Endpoint: testDNSTarget}); diff != "" {
   606  			t.Fatalf("got unexpected DNS target to watch, diff (-got, +want): %v", diff)
   607  		}
   608  	case <-ctx.Done():
   609  		t.Fatal("Timed out waiting for building DNS resolver")
   610  	}
   611  
   612  	fakeClient.InvokeWatchEDSCallback(gotEDSName1, testEDSUpdates[0], nil)
   613  	// Shouldn't send update, because only one resource received an update.
   614  	shortCtx, shortCancel := context.WithTimeout(context.Background(), defaultTestShortTimeout)
   615  	defer shortCancel()
   616  	select {
   617  	case u := <-rr.updateChannel:
   618  		t.Fatalf("get unexpected update %+v", u)
   619  	case <-shortCtx.Done():
   620  	}
   621  	// Invoke DNS, should get an update.
   622  	dnsR.UpdateState(resolver.State{Addresses: []resolver.Address{{Addr: "1.1.1.1"}, {Addr: "2.2.2.2"}}})
   623  	select {
   624  	case u := <-rr.updateChannel:
   625  		if diff := cmp.Diff(u.priorities, []priorityConfig{
   626  			{
   627  				mechanism: DiscoveryMechanism{
   628  					Type:    DiscoveryMechanismTypeEDS,
   629  					Cluster: testClusterName,
   630  				},
   631  				edsResp: testEDSUpdates[0],
   632  			},
   633  			{
   634  				mechanism: DiscoveryMechanism{
   635  					Type:        DiscoveryMechanismTypeLogicalDNS,
   636  					DNSHostname: testDNSTarget,
   637  				},
   638  				addresses: []string{"1.1.1.1", "2.2.2.2"},
   639  			},
   640  		}, cmp.AllowUnexported(priorityConfig{})); diff != "" {
   641  			t.Fatalf("got unexpected resource update, diff (-got, +want): %v", diff)
   642  		}
   643  	case <-ctx.Done():
   644  		t.Fatal("Timed out waiting for update from update channel.")
   645  	}
   646  
   647  	// Close the resource resolver. Should stop EDS watch.
   648  	rr.stop()
   649  	edsNameCanceled1, err := fakeClient.WaitForCancelEDSWatch(ctx)
   650  	if err != nil {
   651  		t.Fatalf("xdsClient.CancelCDS failed with error: %v", err)
   652  	}
   653  	if edsNameCanceled1 != gotEDSName1 {
   654  		t.Fatalf("xdsClient.CancelEDS called for %v, want: %v", edsNameCanceled1, gotEDSName1)
   655  	}
   656  	select {
   657  	case <-dnsCloseCh:
   658  	case <-ctx.Done():
   659  		t.Fatal("Timed out waiting for closing DNS resolver")
   660  	}
   661  }
   662  
   663  // Test the case that covers resource changing between EDS and DNS.
   664  func (s) TestResourceResolverChangeFromEDSToDNS(t *testing.T) {
   665  	dnsTargetCh, dnsCloseCh, _, dnsR, cleanup := setupDNS()
   666  	defer cleanup()
   667  	fakeClient := fakeclient.NewClient()
   668  	rr := newResourceResolver(&clusterResolverBalancer{xdsClient: fakeClient})
   669  	rr.updateMechanisms([]DiscoveryMechanism{{
   670  		Type:    DiscoveryMechanismTypeEDS,
   671  		Cluster: testClusterName,
   672  	}})
   673  	ctx, ctxCancel := context.WithTimeout(context.Background(), defaultTestTimeout)
   674  	defer ctxCancel()
   675  	gotEDSName1, err := fakeClient.WaitForWatchEDS(ctx)
   676  	if err != nil {
   677  		t.Fatalf("xdsClient.WatchCDS failed with error: %v", err)
   678  	}
   679  	if gotEDSName1 != testClusterName {
   680  		t.Fatalf("xdsClient.WatchEDS called for cluster: %v, want: %v", gotEDSName1, testClusterName)
   681  	}
   682  
   683  	// Invoke callback, should get an update.
   684  	fakeClient.InvokeWatchEDSCallback(gotEDSName1, testEDSUpdates[0], nil)
   685  	select {
   686  	case u := <-rr.updateChannel:
   687  		if diff := cmp.Diff(u.priorities, []priorityConfig{{
   688  			mechanism: DiscoveryMechanism{
   689  				Type:    DiscoveryMechanismTypeEDS,
   690  				Cluster: testClusterName,
   691  			},
   692  			edsResp: testEDSUpdates[0],
   693  		}}, cmp.AllowUnexported(priorityConfig{})); diff != "" {
   694  			t.Fatalf("got unexpected resource update, diff (-got, +want): %v", diff)
   695  		}
   696  	case <-ctx.Done():
   697  		t.Fatal("Timed out waiting for update from update channel.")
   698  	}
   699  
   700  	// Update to watch DNS instead. Should cancel EDS, and start DNS.
   701  	rr.updateMechanisms([]DiscoveryMechanism{{
   702  		Type:        DiscoveryMechanismTypeLogicalDNS,
   703  		DNSHostname: testDNSTarget,
   704  	}})
   705  	select {
   706  	case target := <-dnsTargetCh:
   707  		if diff := cmp.Diff(target, resolver.Target{Scheme: "dns", Endpoint: testDNSTarget}); diff != "" {
   708  			t.Fatalf("got unexpected DNS target to watch, diff (-got, +want): %v", diff)
   709  		}
   710  	case <-ctx.Done():
   711  		t.Fatal("Timed out waiting for building DNS resolver")
   712  	}
   713  	edsNameCanceled1, err := fakeClient.WaitForCancelEDSWatch(ctx)
   714  	if err != nil {
   715  		t.Fatalf("xdsClient.CancelCDS failed with error: %v", err)
   716  	}
   717  	if edsNameCanceled1 != gotEDSName1 {
   718  		t.Fatalf("xdsClient.CancelEDS called for %v, want: %v", edsNameCanceled1, gotEDSName1)
   719  	}
   720  
   721  	dnsR.UpdateState(resolver.State{Addresses: []resolver.Address{{Addr: "1.1.1.1"}, {Addr: "2.2.2.2"}}})
   722  	select {
   723  	case u := <-rr.updateChannel:
   724  		if diff := cmp.Diff(u.priorities, []priorityConfig{{
   725  			mechanism: DiscoveryMechanism{
   726  				Type:        DiscoveryMechanismTypeLogicalDNS,
   727  				DNSHostname: testDNSTarget,
   728  			},
   729  			addresses: []string{"1.1.1.1", "2.2.2.2"},
   730  		}}, cmp.AllowUnexported(priorityConfig{})); diff != "" {
   731  			t.Fatalf("got unexpected resource update, diff (-got, +want): %v", diff)
   732  		}
   733  	case <-ctx.Done():
   734  		t.Fatal("Timed out waiting for update from update channel.")
   735  	}
   736  
   737  	// Close the resource resolver. Should stop DNS.
   738  	rr.stop()
   739  	select {
   740  	case <-dnsCloseCh:
   741  	case <-ctx.Done():
   742  		t.Fatal("Timed out waiting for closing DNS resolver")
   743  	}
   744  }
   745  
   746  // Test the case that covers errors for both EDS and DNS.
   747  func (s) TestResourceResolverError(t *testing.T) {
   748  	dnsTargetCh, dnsCloseCh, _, dnsR, cleanup := setupDNS()
   749  	defer cleanup()
   750  	fakeClient := fakeclient.NewClient()
   751  	rr := newResourceResolver(&clusterResolverBalancer{xdsClient: fakeClient})
   752  	rr.updateMechanisms([]DiscoveryMechanism{
   753  		{
   754  			Type:    DiscoveryMechanismTypeEDS,
   755  			Cluster: testClusterName,
   756  		},
   757  		{
   758  			Type:        DiscoveryMechanismTypeLogicalDNS,
   759  			DNSHostname: testDNSTarget,
   760  		},
   761  	})
   762  	ctx, ctxCancel := context.WithTimeout(context.Background(), defaultTestTimeout)
   763  	defer ctxCancel()
   764  	gotEDSName1, err := fakeClient.WaitForWatchEDS(ctx)
   765  	if err != nil {
   766  		t.Fatalf("xdsClient.WatchCDS failed with error: %v", err)
   767  	}
   768  	if gotEDSName1 != testClusterName {
   769  		t.Fatalf("xdsClient.WatchEDS called for cluster: %v, want: %v", gotEDSName1, testClusterName)
   770  	}
   771  	select {
   772  	case target := <-dnsTargetCh:
   773  		if diff := cmp.Diff(target, resolver.Target{Scheme: "dns", Endpoint: testDNSTarget}); diff != "" {
   774  			t.Fatalf("got unexpected DNS target to watch, diff (-got, +want): %v", diff)
   775  		}
   776  	case <-ctx.Done():
   777  		t.Fatal("Timed out waiting for building DNS resolver")
   778  	}
   779  
   780  	// Invoke callback with an error, should get an update.
   781  	edsErr := fmt.Errorf("EDS error")
   782  	fakeClient.InvokeWatchEDSCallback(gotEDSName1, xdsresource.EndpointsUpdate{}, edsErr)
   783  	select {
   784  	case u := <-rr.updateChannel:
   785  		if u.err != edsErr {
   786  			t.Fatalf("got unexpected error from update, want %v, got %v", edsErr, u.err)
   787  		}
   788  	case <-ctx.Done():
   789  		t.Fatal("Timed out waiting for update from update channel.")
   790  	}
   791  
   792  	// Invoke DNS with an error, should get an update.
   793  	dnsErr := fmt.Errorf("DNS error")
   794  	dnsR.ReportError(dnsErr)
   795  	select {
   796  	case u := <-rr.updateChannel:
   797  		if u.err != dnsErr {
   798  			t.Fatalf("got unexpected error from update, want %v, got %v", dnsErr, u.err)
   799  		}
   800  	case <-ctx.Done():
   801  		t.Fatal("Timed out waiting for update from update channel.")
   802  	}
   803  
   804  	// Close the resource resolver. Should stop EDS watch.
   805  	rr.stop()
   806  	edsNameCanceled1, err := fakeClient.WaitForCancelEDSWatch(ctx)
   807  	if err != nil {
   808  		t.Fatalf("xdsClient.CancelCDS failed with error: %v", err)
   809  	}
   810  	if edsNameCanceled1 != gotEDSName1 {
   811  		t.Fatalf("xdsClient.CancelEDS called for %v, want: %v", edsNameCanceled1, gotEDSName1)
   812  	}
   813  	select {
   814  	case <-dnsCloseCh:
   815  	case <-ctx.Done():
   816  		t.Fatal("Timed out waiting for closing DNS resolver")
   817  	}
   818  }
   819  
   820  // Test re-resolve of the DNS resolver.
   821  func (s) TestResourceResolverDNSResolveNow(t *testing.T) {
   822  	dnsTargetCh, dnsCloseCh, resolveNowCh, dnsR, cleanup := setupDNS()
   823  	defer cleanup()
   824  	fakeClient := fakeclient.NewClient()
   825  	rr := newResourceResolver(&clusterResolverBalancer{xdsClient: fakeClient})
   826  	rr.updateMechanisms([]DiscoveryMechanism{{
   827  		Type:        DiscoveryMechanismTypeLogicalDNS,
   828  		DNSHostname: testDNSTarget,
   829  	}})
   830  	ctx, ctxCancel := context.WithTimeout(context.Background(), defaultTestTimeout)
   831  	defer ctxCancel()
   832  	select {
   833  	case target := <-dnsTargetCh:
   834  		if diff := cmp.Diff(target, resolver.Target{Scheme: "dns", Endpoint: testDNSTarget}); diff != "" {
   835  			t.Fatalf("got unexpected DNS target to watch, diff (-got, +want): %v", diff)
   836  		}
   837  	case <-ctx.Done():
   838  		t.Fatal("Timed out waiting for building DNS resolver")
   839  	}
   840  
   841  	// Invoke callback, should get an update.
   842  	dnsR.UpdateState(resolver.State{Addresses: []resolver.Address{{Addr: "1.1.1.1"}, {Addr: "2.2.2.2"}}})
   843  	select {
   844  	case u := <-rr.updateChannel:
   845  		if diff := cmp.Diff(u.priorities, []priorityConfig{{
   846  			mechanism: DiscoveryMechanism{
   847  				Type:        DiscoveryMechanismTypeLogicalDNS,
   848  				DNSHostname: testDNSTarget,
   849  			},
   850  			addresses: []string{"1.1.1.1", "2.2.2.2"},
   851  		}}, cmp.AllowUnexported(priorityConfig{})); diff != "" {
   852  			t.Fatalf("got unexpected resource update, diff (-got, +want): %v", diff)
   853  		}
   854  	case <-ctx.Done():
   855  		t.Fatal("Timed out waiting for update from update channel.")
   856  	}
   857  	rr.resolveNow()
   858  	select {
   859  	case <-resolveNowCh:
   860  	case <-ctx.Done():
   861  		t.Fatal("Timed out waiting for re-resolve")
   862  	}
   863  	// Close the resource resolver. Should close the underlying resolver.
   864  	rr.stop()
   865  	select {
   866  	case <-dnsCloseCh:
   867  	case <-ctx.Done():
   868  		t.Fatal("Timed out waiting for closing DNS resolver")
   869  	}
   870  }