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

     1  package server_test
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"fmt"
     7  	"testing"
     8  	"time"
     9  
    10  	"gitee.com/ks-custle/core-gm/grpc"
    11  
    12  	"github.com/stretchr/testify/assert"
    13  
    14  	discovery "gitee.com/ks-custle/core-gm/go-control-plane/envoy/service/discovery/v3"
    15  	"gitee.com/ks-custle/core-gm/go-control-plane/pkg/cache/types"
    16  	"gitee.com/ks-custle/core-gm/go-control-plane/pkg/cache/v3"
    17  	rsrc "gitee.com/ks-custle/core-gm/go-control-plane/pkg/resource/v3"
    18  	"gitee.com/ks-custle/core-gm/go-control-plane/pkg/server/stream/v3"
    19  	"gitee.com/ks-custle/core-gm/go-control-plane/pkg/server/v3"
    20  )
    21  
    22  func (config *mockConfigWatcher) CreateDeltaWatch(req *discovery.DeltaDiscoveryRequest, state stream.StreamState, out chan cache.DeltaResponse) func() {
    23  	config.deltaCounts[req.TypeUrl] = config.deltaCounts[req.TypeUrl] + 1
    24  
    25  	if len(config.deltaResponses[req.TypeUrl]) > 0 {
    26  		res := config.deltaResponses[req.TypeUrl][0]
    27  		// In subscribed, we only want to send back what's changed if we detect changes
    28  		var subscribed []types.Resource
    29  		r, _ := res.GetDeltaDiscoveryResponse()
    30  
    31  		switch {
    32  		case state.IsWildcard():
    33  			for _, resource := range r.Resources {
    34  				name := resource.GetName()
    35  				res, _ := cache.MarshalResource(resource)
    36  
    37  				nextVersion := cache.HashResource(res)
    38  				prevVersion, found := state.GetResourceVersions()[name]
    39  				if !found || (prevVersion != nextVersion) {
    40  					state.GetResourceVersions()[name] = nextVersion
    41  					subscribed = append(subscribed, resource)
    42  				}
    43  			}
    44  		default:
    45  			for _, resource := range r.Resources {
    46  				res, _ := cache.MarshalResource(resource)
    47  				nextVersion := cache.HashResource(res)
    48  				for _, prevVersion := range state.GetResourceVersions() {
    49  					if prevVersion != nextVersion {
    50  						subscribed = append(subscribed, resource)
    51  					}
    52  					state.GetResourceVersions()[resource.GetName()] = nextVersion
    53  				}
    54  			}
    55  		}
    56  
    57  		out <- &cache.RawDeltaResponse{
    58  			DeltaRequest:      req,
    59  			Resources:         subscribed,
    60  			SystemVersionInfo: "",
    61  			NextVersionMap:    state.GetResourceVersions(),
    62  		}
    63  	} else {
    64  		config.deltaWatches++
    65  		return func() {
    66  			config.deltaWatches--
    67  		}
    68  	}
    69  
    70  	return nil
    71  }
    72  
    73  type mockDeltaStream struct {
    74  	t         *testing.T
    75  	ctx       context.Context
    76  	recv      chan *discovery.DeltaDiscoveryRequest
    77  	sent      chan *discovery.DeltaDiscoveryResponse
    78  	nonce     int
    79  	sendError bool
    80  	grpc.ServerStream
    81  }
    82  
    83  func (stream *mockDeltaStream) Context() context.Context {
    84  	return stream.ctx
    85  }
    86  
    87  func (stream *mockDeltaStream) Send(resp *discovery.DeltaDiscoveryResponse) error {
    88  	// Check that nonce is incremented by one
    89  	stream.nonce = stream.nonce + 1
    90  	if resp.Nonce != fmt.Sprintf("%d", stream.nonce) {
    91  		stream.t.Errorf("Nonce => got %q, want %d", resp.Nonce, stream.nonce)
    92  	}
    93  	// Check that resources are non-empty
    94  	if len(resp.Resources) == 0 {
    95  		stream.t.Error("Resources => got none, want non-empty")
    96  	}
    97  	if resp.TypeUrl == "" {
    98  		stream.t.Error("TypeUrl => got none, want non-empty")
    99  	}
   100  
   101  	// Check that the per resource TypeURL is correctly set.
   102  	for _, res := range resp.Resources {
   103  		if res.Resource.TypeUrl != resp.TypeUrl {
   104  			stream.t.Errorf("TypeUrl => got %q, want %q", res.Resource.TypeUrl, resp.TypeUrl)
   105  		}
   106  	}
   107  
   108  	stream.sent <- resp
   109  	if stream.sendError {
   110  		return errors.New("send error")
   111  	}
   112  	return nil
   113  }
   114  
   115  func (stream *mockDeltaStream) Recv() (*discovery.DeltaDiscoveryRequest, error) {
   116  	req, more := <-stream.recv
   117  	if !more {
   118  		return nil, errors.New("empty")
   119  	}
   120  	return req, nil
   121  }
   122  
   123  func makeMockDeltaStream(t *testing.T) *mockDeltaStream {
   124  	return &mockDeltaStream{
   125  		t:    t,
   126  		ctx:  context.Background(),
   127  		sent: make(chan *discovery.DeltaDiscoveryResponse, 10),
   128  		recv: make(chan *discovery.DeltaDiscoveryRequest, 10),
   129  	}
   130  }
   131  
   132  func makeDeltaResponses() map[string][]cache.DeltaResponse {
   133  	return map[string][]cache.DeltaResponse{
   134  		rsrc.EndpointType: {
   135  			&cache.RawDeltaResponse{
   136  				Resources:         []types.Resource{endpoint},
   137  				DeltaRequest:      &discovery.DeltaDiscoveryRequest{TypeUrl: rsrc.EndpointType},
   138  				SystemVersionInfo: "1",
   139  			},
   140  		},
   141  		rsrc.ClusterType: {
   142  			&cache.RawDeltaResponse{
   143  				Resources:         []types.Resource{cluster},
   144  				DeltaRequest:      &discovery.DeltaDiscoveryRequest{TypeUrl: rsrc.ClusterType},
   145  				SystemVersionInfo: "2",
   146  			},
   147  		},
   148  		rsrc.RouteType: {
   149  			&cache.RawDeltaResponse{
   150  				Resources:         []types.Resource{route},
   151  				DeltaRequest:      &discovery.DeltaDiscoveryRequest{TypeUrl: rsrc.RouteType},
   152  				SystemVersionInfo: "3",
   153  			},
   154  		},
   155  		rsrc.ListenerType: {
   156  			&cache.RawDeltaResponse{
   157  				Resources:         []types.Resource{listener},
   158  				DeltaRequest:      &discovery.DeltaDiscoveryRequest{TypeUrl: rsrc.ListenerType},
   159  				SystemVersionInfo: "4",
   160  			},
   161  		},
   162  		rsrc.SecretType: {
   163  			&cache.RawDeltaResponse{
   164  				SystemVersionInfo: "5",
   165  				Resources:         []types.Resource{secret},
   166  				DeltaRequest:      &discovery.DeltaDiscoveryRequest{TypeUrl: rsrc.SecretType},
   167  			},
   168  		},
   169  		rsrc.RuntimeType: {
   170  			&cache.RawDeltaResponse{
   171  				SystemVersionInfo: "6",
   172  				Resources:         []types.Resource{runtime},
   173  				DeltaRequest:      &discovery.DeltaDiscoveryRequest{TypeUrl: rsrc.RuntimeType},
   174  			},
   175  		},
   176  		rsrc.ExtensionConfigType: {
   177  			&cache.RawDeltaResponse{
   178  				SystemVersionInfo: "7",
   179  				Resources:         []types.Resource{extensionConfig},
   180  				DeltaRequest:      &discovery.DeltaDiscoveryRequest{TypeUrl: rsrc.ExtensionConfigType},
   181  			},
   182  		},
   183  		// Pass-through type (types without explicit handling)
   184  		opaqueType: {
   185  			&cache.RawDeltaResponse{
   186  				SystemVersionInfo: "8",
   187  				Resources:         []types.Resource{opaque},
   188  				DeltaRequest:      &discovery.DeltaDiscoveryRequest{TypeUrl: opaqueType},
   189  			},
   190  		},
   191  	}
   192  }
   193  
   194  func process(typ string, resp *mockDeltaStream, s server.Server) error {
   195  	var err error
   196  	switch typ {
   197  	case rsrc.EndpointType:
   198  		err = s.DeltaEndpoints(resp)
   199  	case rsrc.ClusterType:
   200  		err = s.DeltaClusters(resp)
   201  	case rsrc.RouteType:
   202  		err = s.DeltaRoutes(resp)
   203  	case rsrc.ListenerType:
   204  		err = s.DeltaListeners(resp)
   205  	case rsrc.SecretType:
   206  		err = s.DeltaSecrets(resp)
   207  	case rsrc.RuntimeType:
   208  		err = s.DeltaRuntime(resp)
   209  	case rsrc.ExtensionConfigType:
   210  		err = s.DeltaExtensionConfigs(resp)
   211  	case opaqueType:
   212  		err = s.DeltaAggregatedResources(resp)
   213  	}
   214  
   215  	return err
   216  }
   217  
   218  func TestDeltaResponseHandlersWildcard(t *testing.T) {
   219  	for _, typ := range testTypes {
   220  		t.Run(typ, func(t *testing.T) {
   221  			config := makeMockConfigWatcher()
   222  			config.deltaResponses = makeDeltaResponses()
   223  			s := server.NewServer(context.Background(), config, server.CallbackFuncs{})
   224  
   225  			resp := makeMockDeltaStream(t)
   226  			// This is a wildcard request since we don't specify a list of resource subscriptions
   227  			resp.recv <- &discovery.DeltaDiscoveryRequest{Node: node, TypeUrl: typ}
   228  
   229  			go func() {
   230  				err := process(typ, resp, s)
   231  				assert.NoError(t, err)
   232  			}()
   233  
   234  			select {
   235  			case res := <-resp.sent:
   236  				close(resp.recv)
   237  
   238  				assert.Equal(t, 1, config.deltaCounts[typ])
   239  				assert.Empty(t, res.GetSystemVersionInfo())
   240  			case <-time.After(1 * time.Second):
   241  				t.Fatalf("got no response")
   242  			}
   243  		})
   244  	}
   245  }
   246  
   247  func TestDeltaResponseHandlers(t *testing.T) {
   248  	for _, typ := range testTypes {
   249  		t.Run(typ, func(t *testing.T) {
   250  			config := makeMockConfigWatcher()
   251  			config.deltaResponses = makeDeltaResponses()
   252  			s := server.NewServer(context.Background(), config, server.CallbackFuncs{})
   253  
   254  			resp := makeMockDeltaStream(t)
   255  			// This is a wildcard request since we don't specify a list of resource subscriptions
   256  			res, err := config.deltaResponses[typ][0].GetDeltaDiscoveryResponse()
   257  			if err != nil {
   258  				t.Error(err)
   259  			}
   260  			// We only subscribe to one resource to see if we get the appropriate number of resources back
   261  			resp.recv <- &discovery.DeltaDiscoveryRequest{Node: node, TypeUrl: typ, ResourceNamesSubscribe: []string{res.Resources[0].Name}}
   262  
   263  			go func() {
   264  				err := process(typ, resp, s)
   265  				assert.NoError(t, err)
   266  			}()
   267  
   268  			select {
   269  			case res := <-resp.sent:
   270  				close(resp.recv)
   271  
   272  				assert.Equal(t, 1, config.deltaCounts[typ])
   273  				assert.Empty(t, res.GetSystemVersionInfo())
   274  			case <-time.After(1 * time.Second):
   275  				t.Fatalf("got no response")
   276  			}
   277  		})
   278  	}
   279  }
   280  
   281  func TestSendDeltaError(t *testing.T) {
   282  	for _, typ := range testTypes {
   283  		t.Run(typ, func(t *testing.T) {
   284  			config := makeMockConfigWatcher()
   285  			config.deltaResponses = makeDeltaResponses()
   286  			s := server.NewServer(context.Background(), config, server.CallbackFuncs{})
   287  
   288  			// make a request with an error
   289  			resp := makeMockDeltaStream(t)
   290  			resp.sendError = true
   291  			resp.recv <- &discovery.DeltaDiscoveryRequest{
   292  				Node:    node,
   293  				TypeUrl: typ,
   294  			}
   295  
   296  			// check that response fails since we expect an error to come through
   297  			err := s.DeltaAggregatedResources(resp)
   298  			assert.Error(t, err)
   299  
   300  			close(resp.recv)
   301  		})
   302  	}
   303  }
   304  
   305  func TestDeltaAggregatedHandlers(t *testing.T) {
   306  	config := makeMockConfigWatcher()
   307  	config.deltaResponses = makeDeltaResponses()
   308  	resp := makeMockDeltaStream(t)
   309  
   310  	reqs := []*discovery.DeltaDiscoveryRequest{
   311  		{
   312  			Node:    node,
   313  			TypeUrl: rsrc.ListenerType,
   314  		},
   315  		{
   316  			Node:    node,
   317  			TypeUrl: rsrc.ClusterType,
   318  		},
   319  		{
   320  			Node:                   node,
   321  			TypeUrl:                rsrc.EndpointType,
   322  			ResourceNamesSubscribe: []string{clusterName},
   323  		},
   324  		{
   325  			TypeUrl:                rsrc.RouteType,
   326  			ResourceNamesSubscribe: []string{routeName},
   327  		},
   328  		{
   329  			TypeUrl:                rsrc.SecretType,
   330  			ResourceNamesSubscribe: []string{secretName},
   331  		},
   332  	}
   333  
   334  	for _, r := range reqs {
   335  		resp.recv <- r
   336  	}
   337  
   338  	s := server.NewServer(context.Background(), config, server.CallbackFuncs{})
   339  	go func() {
   340  		err := s.DeltaAggregatedResources(resp)
   341  		assert.NoError(t, err)
   342  	}()
   343  
   344  	count := 0
   345  	for {
   346  		select {
   347  		case <-resp.sent:
   348  			count++
   349  			if count >= len(reqs) {
   350  				close(resp.recv)
   351  				assert.Equal(
   352  					t,
   353  					map[string]int{rsrc.EndpointType: 1, rsrc.ClusterType: 1, rsrc.RouteType: 1, rsrc.ListenerType: 1, rsrc.SecretType: 1},
   354  					config.deltaCounts,
   355  				)
   356  				return
   357  			}
   358  		case <-time.After(1 * time.Second):
   359  			t.Fatalf("got %d messages on the stream, not 5", count)
   360  		}
   361  	}
   362  }
   363  
   364  func TestDeltaAggregateRequestType(t *testing.T) {
   365  	config := makeMockConfigWatcher()
   366  	s := server.NewServer(context.Background(), config, server.CallbackFuncs{})
   367  	resp := makeMockDeltaStream(t)
   368  	resp.recv <- &discovery.DeltaDiscoveryRequest{Node: node}
   369  	if err := s.DeltaAggregatedResources(resp); err == nil {
   370  		t.Error("DeltaAggregatedResources() => got nil, want an error")
   371  	}
   372  }
   373  
   374  func TestDeltaCancellations(t *testing.T) {
   375  	config := makeMockConfigWatcher()
   376  	resp := makeMockDeltaStream(t)
   377  	for _, typ := range testTypes {
   378  		resp.recv <- &discovery.DeltaDiscoveryRequest{
   379  			Node:    node,
   380  			TypeUrl: typ,
   381  		}
   382  	}
   383  	close(resp.recv)
   384  	s := server.NewServer(context.Background(), config, server.CallbackFuncs{})
   385  	if err := s.DeltaAggregatedResources(resp); err != nil {
   386  		t.Errorf("DeltaAggregatedResources() => got %v, want no error", err)
   387  	}
   388  	if config.watches != 0 {
   389  		t.Errorf("Expect all watches canceled, got %q", config.watches)
   390  	}
   391  }
   392  
   393  func TestDeltaOpaqueRequestsChannelMuxing(t *testing.T) {
   394  	config := makeMockConfigWatcher()
   395  	resp := makeMockDeltaStream(t)
   396  	for i := 0; i < 10; i++ {
   397  		resp.recv <- &discovery.DeltaDiscoveryRequest{
   398  			Node:                   node,
   399  			TypeUrl:                fmt.Sprintf("%s%d", opaqueType, i%2),
   400  			ResourceNamesSubscribe: []string{fmt.Sprintf("%d", i)},
   401  		}
   402  	}
   403  	close(resp.recv)
   404  	s := server.NewServer(context.Background(), config, server.CallbackFuncs{})
   405  	if err := s.DeltaAggregatedResources(resp); err != nil {
   406  		t.Errorf("DeltaAggregatedResources() => got %v, want no error", err)
   407  	}
   408  	if config.watches != 0 {
   409  		t.Errorf("Expect all watches canceled, got %q", config.watches)
   410  	}
   411  }
   412  
   413  func TestDeltaCallbackError(t *testing.T) {
   414  	for _, typ := range testTypes {
   415  		t.Run(typ, func(t *testing.T) {
   416  			config := makeMockConfigWatcher()
   417  			config.deltaResponses = makeDeltaResponses()
   418  
   419  			s := server.NewServer(context.Background(), config, server.CallbackFuncs{
   420  				DeltaStreamOpenFunc: func(ctx context.Context, i int64, s string) error {
   421  					return errors.New("stream open error")
   422  				},
   423  			})
   424  
   425  			// make a request
   426  			resp := makeMockDeltaStream(t)
   427  			resp.recv <- &discovery.DeltaDiscoveryRequest{
   428  				Node:    node,
   429  				TypeUrl: typ,
   430  			}
   431  
   432  			// check that response fails since stream open returns error
   433  			if err := s.DeltaAggregatedResources(resp); err == nil {
   434  				t.Error("Stream() => got no error, want error")
   435  			}
   436  
   437  			close(resp.recv)
   438  		})
   439  	}
   440  }