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 }