gitee.com/ks-custle/core-gm@v0.0.0-20230922171213-b83bdd97b62c/go-control-plane/pkg/cache/v3/delta_test.go (about)

     1  package cache_test
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"reflect"
     7  	"testing"
     8  	"time"
     9  
    10  	"github.com/stretchr/testify/assert"
    11  
    12  	core "gitee.com/ks-custle/core-gm/go-control-plane/envoy/config/core/v3"
    13  	discovery "gitee.com/ks-custle/core-gm/go-control-plane/envoy/service/discovery/v3"
    14  	"gitee.com/ks-custle/core-gm/go-control-plane/pkg/cache/types"
    15  	"gitee.com/ks-custle/core-gm/go-control-plane/pkg/cache/v3"
    16  	rsrc "gitee.com/ks-custle/core-gm/go-control-plane/pkg/resource/v3"
    17  	"gitee.com/ks-custle/core-gm/go-control-plane/pkg/server/stream/v3"
    18  	"gitee.com/ks-custle/core-gm/go-control-plane/pkg/test/resource/v3"
    19  )
    20  
    21  func TestSnapshotCacheDeltaWatch(t *testing.T) {
    22  	c := cache.NewSnapshotCache(false, group{}, logger{t: t})
    23  	watches := make(map[string]chan cache.DeltaResponse)
    24  
    25  	// Make our initial request as a wildcard to get all resources and make sure the wildcard requesting works as intended
    26  	for _, typ := range testTypes {
    27  		watches[typ] = make(chan cache.DeltaResponse, 1)
    28  		c.CreateDeltaWatch(&discovery.DeltaDiscoveryRequest{
    29  			Node: &core.Node{
    30  				Id: "node",
    31  			},
    32  			TypeUrl:                typ,
    33  			ResourceNamesSubscribe: names[typ],
    34  		}, stream.NewStreamState(true, nil), watches[typ])
    35  	}
    36  
    37  	if err := c.SetSnapshot(context.Background(), key, snapshot); err != nil {
    38  		t.Fatal(err)
    39  	}
    40  
    41  	versionMap := make(map[string]map[string]string)
    42  	for _, typ := range testTypes {
    43  		t.Run(typ, func(t *testing.T) {
    44  			select {
    45  			case out := <-watches[typ]:
    46  				if !reflect.DeepEqual(cache.IndexRawResourcesByName(out.(*cache.RawDeltaResponse).Resources), snapshot.GetResources(typ)) {
    47  					t.Errorf("got resources %v, want %v", out.(*cache.RawDeltaResponse).Resources, snapshot.GetResources(typ))
    48  				}
    49  				vMap := out.GetNextVersionMap()
    50  				versionMap[typ] = vMap
    51  			case <-time.After(time.Second):
    52  				t.Fatal("failed to receive snapshot response")
    53  			}
    54  		})
    55  	}
    56  
    57  	// On re-request we want to use non-wildcard so we can verify the logic path of not requesting
    58  	// all resources as well as individual resource removals
    59  	for _, typ := range testTypes {
    60  		watches[typ] = make(chan cache.DeltaResponse, 1)
    61  		c.CreateDeltaWatch(&discovery.DeltaDiscoveryRequest{
    62  			Node: &core.Node{
    63  				Id: "node",
    64  			},
    65  			TypeUrl:                typ,
    66  			ResourceNamesSubscribe: names[typ],
    67  		}, stream.NewStreamState(false, versionMap[typ]), watches[typ])
    68  	}
    69  
    70  	if count := c.GetStatusInfo(key).GetNumDeltaWatches(); count != len(testTypes) {
    71  		t.Errorf("watches should be created for the latest version, saw %d watches expected %d", count, len(testTypes))
    72  	}
    73  
    74  	// set partially-versioned snapshot
    75  	snapshot2 := snapshot
    76  	snapshot2.Resources[types.Endpoint] = cache.NewResources(version2, []types.Resource{resource.MakeEndpoint(clusterName, 9090)})
    77  	if err := c.SetSnapshot(context.Background(), key, snapshot2); err != nil {
    78  		t.Fatal(err)
    79  	}
    80  	if count := c.GetStatusInfo(key).GetNumDeltaWatches(); count != len(testTypes)-1 {
    81  		t.Errorf("watches should be preserved for all but one, got: %d open watches instead of the expected %d open watches", count, len(testTypes)-1)
    82  	}
    83  
    84  	// validate response for endpoints
    85  	select {
    86  	case out := <-watches[testTypes[0]]:
    87  		if !reflect.DeepEqual(cache.IndexRawResourcesByName(out.(*cache.RawDeltaResponse).Resources), snapshot2.GetResources(rsrc.EndpointType)) {
    88  			t.Fatalf("got resources %v, want %v", out.(*cache.RawDeltaResponse).Resources, snapshot2.GetResources(rsrc.EndpointType))
    89  		}
    90  		vMap := out.GetNextVersionMap()
    91  		versionMap[testTypes[0]] = vMap
    92  	case <-time.After(time.Second):
    93  		t.Fatal("failed to receive snapshot response")
    94  	}
    95  }
    96  
    97  func TestDeltaRemoveResources(t *testing.T) {
    98  	c := cache.NewSnapshotCache(false, group{}, logger{t: t})
    99  	watches := make(map[string]chan cache.DeltaResponse)
   100  	streams := make(map[string]*stream.StreamState)
   101  
   102  	for _, typ := range testTypes {
   103  		watches[typ] = make(chan cache.DeltaResponse, 1)
   104  		state := stream.NewStreamState(true, make(map[string]string))
   105  		streams[typ] = &state
   106  		// We don't specify any resource name subscriptions here because we want to make sure we test wildcard
   107  		// functionality. This means we should receive all resources back without requesting a subscription by name.
   108  		c.CreateDeltaWatch(&discovery.DeltaDiscoveryRequest{
   109  			Node: &core.Node{
   110  				Id: "node",
   111  			},
   112  			TypeUrl: typ,
   113  		}, *streams[typ], watches[typ])
   114  	}
   115  
   116  	if err := c.SetSnapshot(context.Background(), key, snapshot); err != nil {
   117  		t.Fatal(err)
   118  	}
   119  
   120  	for _, typ := range testTypes {
   121  		t.Run(typ, func(t *testing.T) {
   122  			select {
   123  			case out := <-watches[typ]:
   124  				if !reflect.DeepEqual(cache.IndexRawResourcesByName(out.(*cache.RawDeltaResponse).Resources), snapshot.GetResources(typ)) {
   125  					t.Errorf("got resources %v, want %v", out.(*cache.RawDeltaResponse).Resources, snapshot.GetResources(typ))
   126  				}
   127  				nextVersionMap := out.GetNextVersionMap()
   128  				streams[typ].SetResourceVersions(nextVersionMap)
   129  			case <-time.After(time.Second):
   130  				t.Fatal("failed to receive a snapshot response")
   131  			}
   132  		})
   133  	}
   134  
   135  	// We want to continue to do wildcard requests here so we can later
   136  	// test the removal of certain resources from a partial snapshot
   137  	for _, typ := range testTypes {
   138  		watches[typ] = make(chan cache.DeltaResponse, 1)
   139  		c.CreateDeltaWatch(&discovery.DeltaDiscoveryRequest{
   140  			Node: &core.Node{
   141  				Id: "node",
   142  			},
   143  			TypeUrl: typ,
   144  		}, *streams[typ], watches[typ])
   145  	}
   146  
   147  	if count := c.GetStatusInfo(key).GetNumDeltaWatches(); count != len(testTypes) {
   148  		t.Errorf("watches should be created for the latest version, saw %d watches expected %d", count, len(testTypes))
   149  	}
   150  
   151  	// set a partially versioned snapshot with no endpoints
   152  	snapshot2 := snapshot
   153  	snapshot2.Resources[types.Endpoint] = cache.NewResources(version2, []types.Resource{})
   154  	if err := c.SetSnapshot(context.Background(), key, snapshot2); err != nil {
   155  		t.Fatal(err)
   156  	}
   157  
   158  	// validate response for endpoints
   159  	select {
   160  	case out := <-watches[testTypes[0]]:
   161  		if !reflect.DeepEqual(cache.IndexRawResourcesByName(out.(*cache.RawDeltaResponse).Resources), snapshot2.GetResources(rsrc.EndpointType)) {
   162  			t.Fatalf("got resources %v, want %v", out.(*cache.RawDeltaResponse).Resources, snapshot2.GetResources(rsrc.EndpointType))
   163  		}
   164  		nextVersionMap := out.GetNextVersionMap()
   165  
   166  		// make sure the version maps are different since we no longer are tracking any endpoint resources
   167  		if reflect.DeepEqual(streams[testTypes[0]].GetResourceVersions(), nextVersionMap) {
   168  			t.Fatalf("versionMap for the endpoint resource type did not change, received: %v, instead of an empty map", nextVersionMap)
   169  		}
   170  	case <-time.After(time.Second):
   171  		t.Fatal("failed to receive snapshot response")
   172  	}
   173  }
   174  
   175  func TestConcurrentSetDeltaWatch(t *testing.T) {
   176  	c := cache.NewSnapshotCache(false, group{}, logger{t: t})
   177  	for i := 0; i < 50; i++ {
   178  		version := fmt.Sprintf("v%d", i)
   179  		func(i int) {
   180  			t.Run(fmt.Sprintf("worker%d", i), func(t *testing.T) {
   181  				t.Parallel()
   182  				id := fmt.Sprintf("%d", i%2)
   183  				responses := make(chan cache.DeltaResponse, 1)
   184  				if i < 25 {
   185  					snap, err := cache.NewSnapshot("", map[rsrc.Type][]types.Resource{})
   186  					if err != nil {
   187  						t.Fatal(err)
   188  					}
   189  					snap.Resources[types.Endpoint] = cache.NewResources(version, []types.Resource{resource.MakeEndpoint(clusterName, uint32(i))})
   190  					if err := c.SetSnapshot(context.Background(), key, snap); err != nil {
   191  						t.Fatalf("snapshot failed: %s", err)
   192  					}
   193  				} else {
   194  					cancel := c.CreateDeltaWatch(&discovery.DeltaDiscoveryRequest{
   195  						Node: &core.Node{
   196  							Id: id,
   197  						},
   198  						TypeUrl:                rsrc.EndpointType,
   199  						ResourceNamesSubscribe: []string{clusterName},
   200  					}, stream.NewStreamState(false, make(map[string]string)), responses)
   201  
   202  					defer cancel()
   203  				}
   204  			})
   205  		}(i)
   206  	}
   207  }
   208  
   209  type testKey struct{}
   210  
   211  func TestSnapshotDeltaCacheWatchTimeout(t *testing.T) {
   212  	c := cache.NewSnapshotCache(true, group{}, logger{t: t})
   213  
   214  	// Create a non-buffered channel that will block sends.
   215  	watchCh := make(chan cache.DeltaResponse)
   216  	c.CreateDeltaWatch(&discovery.DeltaDiscoveryRequest{
   217  		Node: &core.Node{
   218  			Id: key,
   219  		},
   220  		TypeUrl:                rsrc.EndpointType,
   221  		ResourceNamesSubscribe: names[rsrc.EndpointType],
   222  	}, stream.NewStreamState(false, map[string]string{names[rsrc.EndpointType][0]: ""}), watchCh)
   223  
   224  	// The first time we set the snapshot without consuming from the blocking channel, so this should time out.
   225  	ctx, cancel := context.WithTimeout(context.Background(), time.Millisecond)
   226  	defer cancel()
   227  
   228  	err := c.SetSnapshot(ctx, key, snapshot)
   229  	assert.EqualError(t, err, context.Canceled.Error())
   230  
   231  	// Now reset the snapshot with a consuming channel. This verifies that if setting the snapshot fails,
   232  	// we can retry by setting the same snapshot. In other words, we keep the watch open even if we failed
   233  	// to respond to it within the deadline.
   234  	watchTriggeredCh := make(chan cache.DeltaResponse)
   235  	go func() {
   236  		response := <-watchCh
   237  		watchTriggeredCh <- response
   238  		close(watchTriggeredCh)
   239  	}()
   240  
   241  	err = c.SetSnapshot(context.WithValue(context.Background(), testKey{}, "bar"), key, snapshot)
   242  	assert.NoError(t, err)
   243  
   244  	// The channel should get closed due to the watch trigger.
   245  	select {
   246  	case response := <-watchTriggeredCh:
   247  		// Verify that we pass the context through.
   248  		assert.Equal(t, response.GetContext().Value(testKey{}), "bar")
   249  	case <-time.After(time.Second):
   250  		t.Fatalf("timed out")
   251  	}
   252  }
   253  
   254  func TestSnapshotCacheDeltaWatchCancel(t *testing.T) {
   255  	c := cache.NewSnapshotCache(true, group{}, logger{t: t})
   256  	for _, typ := range testTypes {
   257  		responses := make(chan cache.DeltaResponse, 1)
   258  		cancel := c.CreateDeltaWatch(&discovery.DeltaDiscoveryRequest{
   259  			Node: &core.Node{
   260  				Id: key,
   261  			},
   262  			TypeUrl:                typ,
   263  			ResourceNamesSubscribe: names[typ],
   264  		}, stream.NewStreamState(false, make(map[string]string)), responses)
   265  
   266  		// Cancel the watch
   267  		cancel()
   268  	}
   269  	// c.GetStatusKeys() should return at least 1 because we register a node ID with the above watch creations
   270  	if keys := c.GetStatusKeys(); len(keys) == 0 {
   271  		t.Errorf("expected to see a status info registered for watch, saw %d entries", len(keys))
   272  	}
   273  
   274  	for _, typ := range testTypes {
   275  		if count := c.GetStatusInfo(key).GetNumDeltaWatches(); count > 0 {
   276  			t.Errorf("watches should be released for %s", typ)
   277  		}
   278  	}
   279  
   280  	if s := c.GetStatusInfo("missing"); s != nil {
   281  		t.Errorf("should not return a status for unknown key: got %#v", s)
   282  	}
   283  }