github.com/hxx258456/ccgo@v0.0.5-0.20230213014102-48b35f46f66f/grpc/xds/internal/xdsclient/xdsresource/unmarshal_eds_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  package xdsresource
    19  
    20  import (
    21  	"fmt"
    22  	"net"
    23  	"strconv"
    24  	"testing"
    25  
    26  	anypb "github.com/golang/protobuf/ptypes/any"
    27  	wrapperspb "github.com/golang/protobuf/ptypes/wrappers"
    28  	"github.com/google/go-cmp/cmp"
    29  	"github.com/google/go-cmp/cmp/cmpopts"
    30  	v3corepb "github.com/hxx258456/ccgo/go-control-plane/envoy/config/core/v3"
    31  	v3endpointpb "github.com/hxx258456/ccgo/go-control-plane/envoy/config/endpoint/v3"
    32  	v3typepb "github.com/hxx258456/ccgo/go-control-plane/envoy/type/v3"
    33  	"github.com/hxx258456/ccgo/grpc/internal/testutils"
    34  	"github.com/hxx258456/ccgo/grpc/xds/internal"
    35  	"github.com/hxx258456/ccgo/grpc/xds/internal/xdsclient/xdsresource/version"
    36  )
    37  
    38  func (s) TestEDSParseRespProto(t *testing.T) {
    39  	tests := []struct {
    40  		name    string
    41  		m       *v3endpointpb.ClusterLoadAssignment
    42  		want    EndpointsUpdate
    43  		wantErr bool
    44  	}{
    45  		{
    46  			name: "missing-priority",
    47  			m: func() *v3endpointpb.ClusterLoadAssignment {
    48  				clab0 := newClaBuilder("test", nil)
    49  				clab0.addLocality("locality-1", 1, 0, []string{"addr1:314"}, nil)
    50  				clab0.addLocality("locality-2", 1, 2, []string{"addr2:159"}, nil)
    51  				return clab0.Build()
    52  			}(),
    53  			want:    EndpointsUpdate{},
    54  			wantErr: true,
    55  		},
    56  		{
    57  			name: "missing-locality-ID",
    58  			m: func() *v3endpointpb.ClusterLoadAssignment {
    59  				clab0 := newClaBuilder("test", nil)
    60  				clab0.addLocality("", 1, 0, []string{"addr1:314"}, nil)
    61  				return clab0.Build()
    62  			}(),
    63  			want:    EndpointsUpdate{},
    64  			wantErr: true,
    65  		},
    66  		{
    67  			name: "good",
    68  			m: func() *v3endpointpb.ClusterLoadAssignment {
    69  				clab0 := newClaBuilder("test", nil)
    70  				clab0.addLocality("locality-1", 1, 1, []string{"addr1:314"}, &addLocalityOptions{
    71  					Health: []v3corepb.HealthStatus{v3corepb.HealthStatus_UNHEALTHY},
    72  					Weight: []uint32{271},
    73  				})
    74  				clab0.addLocality("locality-2", 1, 0, []string{"addr2:159"}, &addLocalityOptions{
    75  					Health: []v3corepb.HealthStatus{v3corepb.HealthStatus_DRAINING},
    76  					Weight: []uint32{828},
    77  				})
    78  				return clab0.Build()
    79  			}(),
    80  			want: EndpointsUpdate{
    81  				Drops: nil,
    82  				Localities: []Locality{
    83  					{
    84  						Endpoints: []Endpoint{{
    85  							Address:      "addr1:314",
    86  							HealthStatus: EndpointHealthStatusUnhealthy,
    87  							Weight:       271,
    88  						}},
    89  						ID:       internal.LocalityID{SubZone: "locality-1"},
    90  						Priority: 1,
    91  						Weight:   1,
    92  					},
    93  					{
    94  						Endpoints: []Endpoint{{
    95  							Address:      "addr2:159",
    96  							HealthStatus: EndpointHealthStatusDraining,
    97  							Weight:       828,
    98  						}},
    99  						ID:       internal.LocalityID{SubZone: "locality-2"},
   100  						Priority: 0,
   101  						Weight:   1,
   102  					},
   103  				},
   104  			},
   105  			wantErr: false,
   106  		},
   107  	}
   108  	for _, tt := range tests {
   109  		t.Run(tt.name, func(t *testing.T) {
   110  			got, err := parseEDSRespProto(tt.m)
   111  			if (err != nil) != tt.wantErr {
   112  				t.Errorf("parseEDSRespProto() error = %v, wantErr %v", err, tt.wantErr)
   113  				return
   114  			}
   115  			if d := cmp.Diff(got, tt.want); d != "" {
   116  				t.Errorf("parseEDSRespProto() got = %v, want %v, diff: %v", got, tt.want, d)
   117  			}
   118  		})
   119  	}
   120  }
   121  
   122  func (s) TestUnmarshalEndpoints(t *testing.T) {
   123  	var v3EndpointsAny = testutils.MarshalAny(func() *v3endpointpb.ClusterLoadAssignment {
   124  		clab0 := newClaBuilder("test", nil)
   125  		clab0.addLocality("locality-1", 1, 1, []string{"addr1:314"}, &addLocalityOptions{
   126  			Health: []v3corepb.HealthStatus{v3corepb.HealthStatus_UNHEALTHY},
   127  			Weight: []uint32{271},
   128  		})
   129  		clab0.addLocality("locality-2", 1, 0, []string{"addr2:159"}, &addLocalityOptions{
   130  			Health: []v3corepb.HealthStatus{v3corepb.HealthStatus_DRAINING},
   131  			Weight: []uint32{828},
   132  		})
   133  		return clab0.Build()
   134  	}())
   135  	const testVersion = "test-version-eds"
   136  
   137  	tests := []struct {
   138  		name       string
   139  		resources  []*anypb.Any
   140  		wantUpdate map[string]EndpointsUpdateErrTuple
   141  		wantMD     UpdateMetadata
   142  		wantErr    bool
   143  	}{
   144  		{
   145  			name:      "non-clusterLoadAssignment resource type",
   146  			resources: []*anypb.Any{{TypeUrl: version.V3HTTPConnManagerURL}},
   147  			wantMD: UpdateMetadata{
   148  				Status:  ServiceStatusNACKed,
   149  				Version: testVersion,
   150  				ErrState: &UpdateErrorMetadata{
   151  					Version: testVersion,
   152  					Err:     cmpopts.AnyError,
   153  				},
   154  			},
   155  			wantErr: true,
   156  		},
   157  		{
   158  			name: "badly marshaled clusterLoadAssignment resource",
   159  			resources: []*anypb.Any{
   160  				{
   161  					TypeUrl: version.V3EndpointsURL,
   162  					Value:   []byte{1, 2, 3, 4},
   163  				},
   164  			},
   165  			wantMD: UpdateMetadata{
   166  				Status:  ServiceStatusNACKed,
   167  				Version: testVersion,
   168  				ErrState: &UpdateErrorMetadata{
   169  					Version: testVersion,
   170  					Err:     cmpopts.AnyError,
   171  				},
   172  			},
   173  			wantErr: true,
   174  		},
   175  		{
   176  			name: "bad endpoints resource",
   177  			resources: []*anypb.Any{testutils.MarshalAny(func() *v3endpointpb.ClusterLoadAssignment {
   178  				clab0 := newClaBuilder("test", nil)
   179  				clab0.addLocality("locality-1", 1, 0, []string{"addr1:314"}, nil)
   180  				clab0.addLocality("locality-2", 1, 2, []string{"addr2:159"}, nil)
   181  				return clab0.Build()
   182  			}())},
   183  			wantUpdate: map[string]EndpointsUpdateErrTuple{"test": {Err: cmpopts.AnyError}},
   184  			wantMD: UpdateMetadata{
   185  				Status:  ServiceStatusNACKed,
   186  				Version: testVersion,
   187  				ErrState: &UpdateErrorMetadata{
   188  					Version: testVersion,
   189  					Err:     cmpopts.AnyError,
   190  				},
   191  			},
   192  			wantErr: true,
   193  		},
   194  		{
   195  			name:      "v3 endpoints",
   196  			resources: []*anypb.Any{v3EndpointsAny},
   197  			wantUpdate: map[string]EndpointsUpdateErrTuple{
   198  				"test": {Update: EndpointsUpdate{
   199  					Drops: nil,
   200  					Localities: []Locality{
   201  						{
   202  							Endpoints: []Endpoint{{
   203  								Address:      "addr1:314",
   204  								HealthStatus: EndpointHealthStatusUnhealthy,
   205  								Weight:       271,
   206  							}},
   207  							ID:       internal.LocalityID{SubZone: "locality-1"},
   208  							Priority: 1,
   209  							Weight:   1,
   210  						},
   211  						{
   212  							Endpoints: []Endpoint{{
   213  								Address:      "addr2:159",
   214  								HealthStatus: EndpointHealthStatusDraining,
   215  								Weight:       828,
   216  							}},
   217  							ID:       internal.LocalityID{SubZone: "locality-2"},
   218  							Priority: 0,
   219  							Weight:   1,
   220  						},
   221  					},
   222  					Raw: v3EndpointsAny,
   223  				}},
   224  			},
   225  			wantMD: UpdateMetadata{
   226  				Status:  ServiceStatusACKed,
   227  				Version: testVersion,
   228  			},
   229  		},
   230  		{
   231  			// To test that unmarshal keeps processing on errors.
   232  			name: "good and bad endpoints",
   233  			resources: []*anypb.Any{
   234  				v3EndpointsAny,
   235  				testutils.MarshalAny(func() *v3endpointpb.ClusterLoadAssignment {
   236  					clab0 := newClaBuilder("bad", nil)
   237  					clab0.addLocality("locality-1", 1, 0, []string{"addr1:314"}, nil)
   238  					clab0.addLocality("locality-2", 1, 2, []string{"addr2:159"}, nil)
   239  					return clab0.Build()
   240  				}()),
   241  			},
   242  			wantUpdate: map[string]EndpointsUpdateErrTuple{
   243  				"test": {Update: EndpointsUpdate{
   244  					Drops: nil,
   245  					Localities: []Locality{
   246  						{
   247  							Endpoints: []Endpoint{{
   248  								Address:      "addr1:314",
   249  								HealthStatus: EndpointHealthStatusUnhealthy,
   250  								Weight:       271,
   251  							}},
   252  							ID:       internal.LocalityID{SubZone: "locality-1"},
   253  							Priority: 1,
   254  							Weight:   1,
   255  						},
   256  						{
   257  							Endpoints: []Endpoint{{
   258  								Address:      "addr2:159",
   259  								HealthStatus: EndpointHealthStatusDraining,
   260  								Weight:       828,
   261  							}},
   262  							ID:       internal.LocalityID{SubZone: "locality-2"},
   263  							Priority: 0,
   264  							Weight:   1,
   265  						},
   266  					},
   267  					Raw: v3EndpointsAny,
   268  				}},
   269  				"bad": {Err: cmpopts.AnyError},
   270  			},
   271  			wantMD: UpdateMetadata{
   272  				Status:  ServiceStatusNACKed,
   273  				Version: testVersion,
   274  				ErrState: &UpdateErrorMetadata{
   275  					Version: testVersion,
   276  					Err:     cmpopts.AnyError,
   277  				},
   278  			},
   279  			wantErr: true,
   280  		},
   281  	}
   282  	for _, test := range tests {
   283  		t.Run(test.name, func(t *testing.T) {
   284  			opts := &UnmarshalOptions{
   285  				Version:   testVersion,
   286  				Resources: test.resources,
   287  			}
   288  			update, md, err := UnmarshalEndpoints(opts)
   289  			if (err != nil) != test.wantErr {
   290  				t.Fatalf("UnmarshalEndpoints(%+v), got err: %v, wantErr: %v", opts, err, test.wantErr)
   291  			}
   292  			if diff := cmp.Diff(update, test.wantUpdate, cmpOpts); diff != "" {
   293  				t.Errorf("got unexpected update, diff (-got +want): %v", diff)
   294  			}
   295  			if diff := cmp.Diff(md, test.wantMD, cmpOptsIgnoreDetails); diff != "" {
   296  				t.Errorf("got unexpected metadata, diff (-got +want): %v", diff)
   297  			}
   298  		})
   299  	}
   300  }
   301  
   302  // claBuilder builds a ClusterLoadAssignment, aka EDS
   303  // response.
   304  type claBuilder struct {
   305  	v *v3endpointpb.ClusterLoadAssignment
   306  }
   307  
   308  // newClaBuilder creates a claBuilder.
   309  func newClaBuilder(clusterName string, dropPercents []uint32) *claBuilder {
   310  	var drops []*v3endpointpb.ClusterLoadAssignment_Policy_DropOverload
   311  	for i, d := range dropPercents {
   312  		drops = append(drops, &v3endpointpb.ClusterLoadAssignment_Policy_DropOverload{
   313  			Category: fmt.Sprintf("test-drop-%d", i),
   314  			DropPercentage: &v3typepb.FractionalPercent{
   315  				Numerator:   d,
   316  				Denominator: v3typepb.FractionalPercent_HUNDRED,
   317  			},
   318  		})
   319  	}
   320  
   321  	return &claBuilder{
   322  		v: &v3endpointpb.ClusterLoadAssignment{
   323  			ClusterName: clusterName,
   324  			Policy: &v3endpointpb.ClusterLoadAssignment_Policy{
   325  				DropOverloads: drops,
   326  			},
   327  		},
   328  	}
   329  }
   330  
   331  // addLocalityOptions contains options when adding locality to the builder.
   332  type addLocalityOptions struct {
   333  	Health []v3corepb.HealthStatus
   334  	Weight []uint32
   335  }
   336  
   337  // addLocality adds a locality to the builder.
   338  func (clab *claBuilder) addLocality(subzone string, weight uint32, priority uint32, addrsWithPort []string, opts *addLocalityOptions) {
   339  	var lbEndPoints []*v3endpointpb.LbEndpoint
   340  	for i, a := range addrsWithPort {
   341  		host, portStr, err := net.SplitHostPort(a)
   342  		if err != nil {
   343  			panic("failed to split " + a)
   344  		}
   345  		port, err := strconv.Atoi(portStr)
   346  		if err != nil {
   347  			panic("failed to atoi " + portStr)
   348  		}
   349  
   350  		lbe := &v3endpointpb.LbEndpoint{
   351  			HostIdentifier: &v3endpointpb.LbEndpoint_Endpoint{
   352  				Endpoint: &v3endpointpb.Endpoint{
   353  					Address: &v3corepb.Address{
   354  						Address: &v3corepb.Address_SocketAddress{
   355  							SocketAddress: &v3corepb.SocketAddress{
   356  								Protocol: v3corepb.SocketAddress_TCP,
   357  								Address:  host,
   358  								PortSpecifier: &v3corepb.SocketAddress_PortValue{
   359  									PortValue: uint32(port)}}}}}},
   360  		}
   361  		if opts != nil {
   362  			if i < len(opts.Health) {
   363  				lbe.HealthStatus = opts.Health[i]
   364  			}
   365  			if i < len(opts.Weight) {
   366  				lbe.LoadBalancingWeight = &wrapperspb.UInt32Value{Value: opts.Weight[i]}
   367  			}
   368  		}
   369  		lbEndPoints = append(lbEndPoints, lbe)
   370  	}
   371  
   372  	var localityID *v3corepb.Locality
   373  	if subzone != "" {
   374  		localityID = &v3corepb.Locality{
   375  			Region:  "",
   376  			Zone:    "",
   377  			SubZone: subzone,
   378  		}
   379  	}
   380  
   381  	clab.v.Endpoints = append(clab.v.Endpoints, &v3endpointpb.LocalityLbEndpoints{
   382  		Locality:            localityID,
   383  		LbEndpoints:         lbEndPoints,
   384  		LoadBalancingWeight: &wrapperspb.UInt32Value{Value: weight},
   385  		Priority:            priority,
   386  	})
   387  }
   388  
   389  // Build builds ClusterLoadAssignment.
   390  func (clab *claBuilder) Build() *v3endpointpb.ClusterLoadAssignment {
   391  	return clab.v
   392  }