gitee.com/zhaochuninhefei/gmgo@v0.0.31-0.20240209061119-069254a02979/grpc/xds/internal/resolver/watch_service_test.go (about) 1 /* 2 * 3 * Copyright 2020 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 resolver 20 21 import ( 22 "context" 23 "fmt" 24 "testing" 25 "time" 26 27 "gitee.com/zhaochuninhefei/gmgo/grpc/internal/testutils" 28 "gitee.com/zhaochuninhefei/gmgo/grpc/xds/internal/testutils/fakeclient" 29 "gitee.com/zhaochuninhefei/gmgo/grpc/xds/internal/xdsclient/xdsresource" 30 "github.com/golang/protobuf/proto" 31 "github.com/google/go-cmp/cmp" 32 "github.com/google/go-cmp/cmp/cmpopts" 33 ) 34 35 func (s) TestFindBestMatchingVirtualHost(t *testing.T) { 36 var ( 37 oneExactMatch = &xdsresource.VirtualHost{ 38 Domains: []string{"foo.bar.com"}, 39 } 40 oneSuffixMatch = &xdsresource.VirtualHost{ 41 Domains: []string{"*.bar.com"}, 42 } 43 onePrefixMatch = &xdsresource.VirtualHost{ 44 Domains: []string{"foo.bar.*"}, 45 } 46 oneUniversalMatch = &xdsresource.VirtualHost{ 47 Domains: []string{"*"}, 48 } 49 longExactMatch = &xdsresource.VirtualHost{ 50 Domains: []string{"v2.foo.bar.com"}, 51 } 52 multipleMatch = &xdsresource.VirtualHost{ 53 Domains: []string{"pi.foo.bar.com", "314.*", "*.159"}, 54 } 55 vhs = []*xdsresource.VirtualHost{oneExactMatch, oneSuffixMatch, onePrefixMatch, oneUniversalMatch, longExactMatch, multipleMatch} 56 ) 57 58 tests := []struct { 59 name string 60 host string 61 vHosts []*xdsresource.VirtualHost 62 want *xdsresource.VirtualHost 63 }{ 64 {name: "exact-match", host: "foo.bar.com", vHosts: vhs, want: oneExactMatch}, 65 {name: "suffix-match", host: "123.bar.com", vHosts: vhs, want: oneSuffixMatch}, 66 {name: "prefix-match", host: "foo.bar.org", vHosts: vhs, want: onePrefixMatch}, 67 {name: "universal-match", host: "abc.123", vHosts: vhs, want: oneUniversalMatch}, 68 {name: "long-exact-match", host: "v2.foo.bar.com", vHosts: vhs, want: longExactMatch}, 69 // Matches suffix "*.bar.com" and exact "pi.foo.bar.com". Takes exact. 70 {name: "multiple-match-exact", host: "pi.foo.bar.com", vHosts: vhs, want: multipleMatch}, 71 // Matches suffix "*.159" and prefix "foo.bar.*". Takes suffix. 72 {name: "multiple-match-suffix", host: "foo.bar.159", vHosts: vhs, want: multipleMatch}, 73 // Matches suffix "*.bar.com" and prefix "314.*". Takes suffix. 74 {name: "multiple-match-prefix", host: "314.bar.com", vHosts: vhs, want: oneSuffixMatch}, 75 } 76 for _, tt := range tests { 77 t.Run(tt.name, func(t *testing.T) { 78 if got := xdsresource.FindBestMatchingVirtualHost(tt.host, tt.vHosts); !cmp.Equal(got, tt.want, cmp.Comparer(proto.Equal)) { 79 t.Errorf("findBestMatchingxdsclient.VirtualHost() = %v, want %v", got, tt.want) 80 } 81 }) 82 } 83 } 84 85 type serviceUpdateErr struct { 86 u serviceUpdate 87 err error 88 } 89 90 func verifyServiceUpdate(ctx context.Context, updateCh *testutils.Channel, wantUpdate serviceUpdate) error { 91 u, err := updateCh.Receive(ctx) 92 if err != nil { 93 return fmt.Errorf("timeout when waiting for service update: %v", err) 94 } 95 gotUpdate := u.(serviceUpdateErr) 96 if gotUpdate.err != nil || !cmp.Equal(gotUpdate.u, wantUpdate, cmpopts.EquateEmpty(), cmp.AllowUnexported(serviceUpdate{}, ldsConfig{})) { 97 return fmt.Errorf("unexpected service update: (%v, %v), want: (%v, nil), diff (-want +got):\n%s", gotUpdate.u, gotUpdate.err, wantUpdate, cmp.Diff(gotUpdate.u, wantUpdate, cmpopts.EquateEmpty(), cmp.AllowUnexported(serviceUpdate{}, ldsConfig{}))) 98 } 99 return nil 100 } 101 102 func newStringP(s string) *string { 103 return &s 104 } 105 106 // TestServiceWatch covers the cases: 107 // - an update is received after a watch() 108 // - an update with routes received 109 func (s) TestServiceWatch(t *testing.T) { 110 serviceUpdateCh := testutils.NewChannel() 111 xdsC := fakeclient.NewClient() 112 cancelWatch := watchService(xdsC, targetStr, func(update serviceUpdate, err error) { 113 serviceUpdateCh.Send(serviceUpdateErr{u: update, err: err}) 114 }, nil) 115 defer cancelWatch() 116 117 ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout) 118 defer cancel() 119 waitForWatchListener(ctx, t, xdsC, targetStr) 120 xdsC.InvokeWatchListenerCallback(xdsresource.ListenerUpdate{RouteConfigName: routeStr}, nil) 121 waitForWatchRouteConfig(ctx, t, xdsC, routeStr) 122 123 wantUpdate := serviceUpdate{virtualHost: &xdsresource.VirtualHost{Domains: []string{"target"}, Routes: []*xdsresource.Route{{Prefix: newStringP(""), WeightedClusters: map[string]xdsresource.WeightedCluster{cluster: {Weight: 1}}}}}} 124 xdsC.InvokeWatchRouteConfigCallback("", xdsresource.RouteConfigUpdate{ 125 VirtualHosts: []*xdsresource.VirtualHost{ 126 { 127 Domains: []string{targetStr}, 128 Routes: []*xdsresource.Route{{Prefix: newStringP(""), WeightedClusters: map[string]xdsresource.WeightedCluster{cluster: {Weight: 1}}}}, 129 }, 130 }, 131 }, nil) 132 if err := verifyServiceUpdate(ctx, serviceUpdateCh, wantUpdate); err != nil { 133 t.Fatal(err) 134 } 135 136 wantUpdate2 := serviceUpdate{virtualHost: &xdsresource.VirtualHost{Domains: []string{"target"}, 137 Routes: []*xdsresource.Route{{ 138 Path: newStringP(""), 139 WeightedClusters: map[string]xdsresource.WeightedCluster{cluster: {Weight: 1}}, 140 }}, 141 }} 142 xdsC.InvokeWatchRouteConfigCallback("", xdsresource.RouteConfigUpdate{ 143 VirtualHosts: []*xdsresource.VirtualHost{ 144 { 145 Domains: []string{targetStr}, 146 Routes: []*xdsresource.Route{{Path: newStringP(""), WeightedClusters: map[string]xdsresource.WeightedCluster{cluster: {Weight: 1}}}}, 147 }, 148 { 149 // Another virtual host, with different domains. 150 Domains: []string{"random"}, 151 Routes: []*xdsresource.Route{{Prefix: newStringP(""), WeightedClusters: map[string]xdsresource.WeightedCluster{cluster: {Weight: 1}}}}, 152 }, 153 }, 154 }, nil) 155 if err := verifyServiceUpdate(ctx, serviceUpdateCh, wantUpdate2); err != nil { 156 t.Fatal(err) 157 } 158 } 159 160 // TestServiceWatchLDSUpdate covers the case that after first LDS and first RDS 161 // response, the second LDS response trigger an new RDS watch, and an update of 162 // the old RDS watch doesn't trigger update to service callback. 163 func (s) TestServiceWatchLDSUpdate(t *testing.T) { 164 serviceUpdateCh := testutils.NewChannel() 165 xdsC := fakeclient.NewClient() 166 cancelWatch := watchService(xdsC, targetStr, func(update serviceUpdate, err error) { 167 serviceUpdateCh.Send(serviceUpdateErr{u: update, err: err}) 168 }, nil) 169 defer cancelWatch() 170 171 ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout) 172 defer cancel() 173 waitForWatchListener(ctx, t, xdsC, targetStr) 174 xdsC.InvokeWatchListenerCallback(xdsresource.ListenerUpdate{RouteConfigName: routeStr}, nil) 175 waitForWatchRouteConfig(ctx, t, xdsC, routeStr) 176 177 wantUpdate := serviceUpdate{virtualHost: &xdsresource.VirtualHost{Domains: []string{"target"}, Routes: []*xdsresource.Route{{Prefix: newStringP(""), WeightedClusters: map[string]xdsresource.WeightedCluster{cluster: {Weight: 1}}}}}} 178 xdsC.InvokeWatchRouteConfigCallback("", xdsresource.RouteConfigUpdate{ 179 VirtualHosts: []*xdsresource.VirtualHost{ 180 { 181 Domains: []string{targetStr}, 182 Routes: []*xdsresource.Route{{Prefix: newStringP(""), WeightedClusters: map[string]xdsresource.WeightedCluster{cluster: {Weight: 1}}}}, 183 }, 184 }, 185 }, nil) 186 if err := verifyServiceUpdate(ctx, serviceUpdateCh, wantUpdate); err != nil { 187 t.Fatal(err) 188 } 189 190 // Another LDS update with a different RDS_name. 191 xdsC.InvokeWatchListenerCallback(xdsresource.ListenerUpdate{RouteConfigName: routeStr + "2"}, nil) 192 if _, err := xdsC.WaitForCancelRouteConfigWatch(ctx); err != nil { 193 t.Fatalf("wait for cancel route watch failed: %v, want nil", err) 194 } 195 waitForWatchRouteConfig(ctx, t, xdsC, routeStr+"2") 196 197 // RDS update for the new name. 198 wantUpdate2 := serviceUpdate{virtualHost: &xdsresource.VirtualHost{Domains: []string{"target"}, Routes: []*xdsresource.Route{{Prefix: newStringP(""), WeightedClusters: map[string]xdsresource.WeightedCluster{cluster + "2": {Weight: 1}}}}}} 199 xdsC.InvokeWatchRouteConfigCallback(routeStr+"2", xdsresource.RouteConfigUpdate{ 200 VirtualHosts: []*xdsresource.VirtualHost{ 201 { 202 Domains: []string{targetStr}, 203 Routes: []*xdsresource.Route{{Prefix: newStringP(""), WeightedClusters: map[string]xdsresource.WeightedCluster{cluster + "2": {Weight: 1}}}}, 204 }, 205 }, 206 }, nil) 207 if err := verifyServiceUpdate(ctx, serviceUpdateCh, wantUpdate2); err != nil { 208 t.Fatal(err) 209 } 210 } 211 212 // TestServiceWatchLDSUpdate covers the case that after first LDS and first RDS 213 // response, the second LDS response includes a new MaxStreamDuration. It also 214 // verifies this is reported in subsequent RDS updates. 215 func (s) TestServiceWatchLDSUpdateMaxStreamDuration(t *testing.T) { 216 serviceUpdateCh := testutils.NewChannel() 217 xdsC := fakeclient.NewClient() 218 cancelWatch := watchService(xdsC, targetStr, func(update serviceUpdate, err error) { 219 serviceUpdateCh.Send(serviceUpdateErr{u: update, err: err}) 220 }, nil) 221 defer cancelWatch() 222 223 ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout) 224 defer cancel() 225 waitForWatchListener(ctx, t, xdsC, targetStr) 226 xdsC.InvokeWatchListenerCallback(xdsresource.ListenerUpdate{RouteConfigName: routeStr, MaxStreamDuration: time.Second}, nil) 227 waitForWatchRouteConfig(ctx, t, xdsC, routeStr) 228 229 wantUpdate := serviceUpdate{virtualHost: &xdsresource.VirtualHost{Domains: []string{"target"}, Routes: []*xdsresource.Route{{ 230 Prefix: newStringP(""), 231 WeightedClusters: map[string]xdsresource.WeightedCluster{cluster: {Weight: 1}}}}}, 232 ldsConfig: ldsConfig{maxStreamDuration: time.Second}, 233 } 234 xdsC.InvokeWatchRouteConfigCallback("", xdsresource.RouteConfigUpdate{ 235 VirtualHosts: []*xdsresource.VirtualHost{ 236 { 237 Domains: []string{targetStr}, 238 Routes: []*xdsresource.Route{{Prefix: newStringP(""), WeightedClusters: map[string]xdsresource.WeightedCluster{cluster: {Weight: 1}}}}, 239 }, 240 }, 241 }, nil) 242 if err := verifyServiceUpdate(ctx, serviceUpdateCh, wantUpdate); err != nil { 243 t.Fatal(err) 244 } 245 246 // Another LDS update with the same RDS_name but different MaxStreamDuration (zero in this case). 247 wantUpdate2 := serviceUpdate{virtualHost: &xdsresource.VirtualHost{Domains: []string{"target"}, Routes: []*xdsresource.Route{{Prefix: newStringP(""), WeightedClusters: map[string]xdsresource.WeightedCluster{cluster: {Weight: 1}}}}}} 248 xdsC.InvokeWatchListenerCallback(xdsresource.ListenerUpdate{RouteConfigName: routeStr}, nil) 249 if err := verifyServiceUpdate(ctx, serviceUpdateCh, wantUpdate2); err != nil { 250 t.Fatal(err) 251 } 252 253 // RDS update. 254 wantUpdate3 := serviceUpdate{virtualHost: &xdsresource.VirtualHost{Domains: []string{"target"}, Routes: []*xdsresource.Route{{ 255 Prefix: newStringP(""), 256 WeightedClusters: map[string]xdsresource.WeightedCluster{cluster + "2": {Weight: 1}}}}, 257 }} 258 xdsC.InvokeWatchRouteConfigCallback("", xdsresource.RouteConfigUpdate{ 259 VirtualHosts: []*xdsresource.VirtualHost{ 260 { 261 Domains: []string{targetStr}, 262 Routes: []*xdsresource.Route{{Prefix: newStringP(""), WeightedClusters: map[string]xdsresource.WeightedCluster{cluster + "2": {Weight: 1}}}}, 263 }, 264 }, 265 }, nil) 266 if err := verifyServiceUpdate(ctx, serviceUpdateCh, wantUpdate3); err != nil { 267 t.Fatal(err) 268 } 269 } 270 271 // TestServiceNotCancelRDSOnSameLDSUpdate covers the case that if the second LDS 272 // update contains the same RDS name as the previous, the RDS watch isn't 273 // canceled and restarted. 274 func (s) TestServiceNotCancelRDSOnSameLDSUpdate(t *testing.T) { 275 serviceUpdateCh := testutils.NewChannel() 276 xdsC := fakeclient.NewClient() 277 cancelWatch := watchService(xdsC, targetStr, func(update serviceUpdate, err error) { 278 serviceUpdateCh.Send(serviceUpdateErr{u: update, err: err}) 279 }, nil) 280 defer cancelWatch() 281 282 ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout) 283 defer cancel() 284 waitForWatchListener(ctx, t, xdsC, targetStr) 285 xdsC.InvokeWatchListenerCallback(xdsresource.ListenerUpdate{RouteConfigName: routeStr}, nil) 286 waitForWatchRouteConfig(ctx, t, xdsC, routeStr) 287 288 wantUpdate := serviceUpdate{virtualHost: &xdsresource.VirtualHost{Domains: []string{"target"}, Routes: []*xdsresource.Route{{ 289 Prefix: newStringP(""), 290 WeightedClusters: map[string]xdsresource.WeightedCluster{cluster: {Weight: 1}}}}, 291 }} 292 xdsC.InvokeWatchRouteConfigCallback("", xdsresource.RouteConfigUpdate{ 293 VirtualHosts: []*xdsresource.VirtualHost{ 294 { 295 Domains: []string{targetStr}, 296 Routes: []*xdsresource.Route{{Prefix: newStringP(""), WeightedClusters: map[string]xdsresource.WeightedCluster{cluster: {Weight: 1}}}}, 297 }, 298 }, 299 }, nil) 300 301 if err := verifyServiceUpdate(ctx, serviceUpdateCh, wantUpdate); err != nil { 302 t.Fatal(err) 303 } 304 305 // Another LDS update with a the same RDS_name. 306 xdsC.InvokeWatchListenerCallback(xdsresource.ListenerUpdate{RouteConfigName: routeStr}, nil) 307 sCtx, sCancel := context.WithTimeout(ctx, defaultTestShortTimeout) 308 defer sCancel() 309 if _, err := xdsC.WaitForCancelRouteConfigWatch(sCtx); err != context.DeadlineExceeded { 310 t.Fatalf("wait for cancel route watch failed: %v, want nil", err) 311 } 312 } 313 314 // TestServiceWatchInlineRDS covers the cases switching between: 315 // - LDS update contains RDS name to watch 316 // - LDS update contains inline RDS resource 317 func (s) TestServiceWatchInlineRDS(t *testing.T) { 318 serviceUpdateCh := testutils.NewChannel() 319 xdsC := fakeclient.NewClient() 320 cancelWatch := watchService(xdsC, targetStr, func(update serviceUpdate, err error) { 321 serviceUpdateCh.Send(serviceUpdateErr{u: update, err: err}) 322 }, nil) 323 defer cancelWatch() 324 325 ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout) 326 defer cancel() 327 328 // First LDS update is LDS with RDS name to watch. 329 waitForWatchListener(ctx, t, xdsC, targetStr) 330 xdsC.InvokeWatchListenerCallback(xdsresource.ListenerUpdate{RouteConfigName: routeStr}, nil) 331 waitForWatchRouteConfig(ctx, t, xdsC, routeStr) 332 wantUpdate := serviceUpdate{virtualHost: &xdsresource.VirtualHost{Domains: []string{"target"}, Routes: []*xdsresource.Route{{Prefix: newStringP(""), WeightedClusters: map[string]xdsresource.WeightedCluster{cluster: {Weight: 1}}}}}} 333 xdsC.InvokeWatchRouteConfigCallback("", xdsresource.RouteConfigUpdate{ 334 VirtualHosts: []*xdsresource.VirtualHost{ 335 { 336 Domains: []string{targetStr}, 337 Routes: []*xdsresource.Route{{Prefix: newStringP(""), WeightedClusters: map[string]xdsresource.WeightedCluster{cluster: {Weight: 1}}}}, 338 }, 339 }, 340 }, nil) 341 if err := verifyServiceUpdate(ctx, serviceUpdateCh, wantUpdate); err != nil { 342 t.Fatal(err) 343 } 344 345 // Switch LDS resp to a LDS with inline RDS resource 346 wantVirtualHosts2 := &xdsresource.VirtualHost{Domains: []string{"target"}, 347 Routes: []*xdsresource.Route{{ 348 Path: newStringP(""), 349 WeightedClusters: map[string]xdsresource.WeightedCluster{cluster: {Weight: 1}}, 350 }}, 351 } 352 wantUpdate2 := serviceUpdate{virtualHost: wantVirtualHosts2} 353 xdsC.InvokeWatchListenerCallback(xdsresource.ListenerUpdate{InlineRouteConfig: &xdsresource.RouteConfigUpdate{ 354 VirtualHosts: []*xdsresource.VirtualHost{wantVirtualHosts2}, 355 }}, nil) 356 // This inline RDS resource should cause the RDS watch to be canceled. 357 if _, err := xdsC.WaitForCancelRouteConfigWatch(ctx); err != nil { 358 t.Fatalf("wait for cancel route watch failed: %v, want nil", err) 359 } 360 if err := verifyServiceUpdate(ctx, serviceUpdateCh, wantUpdate2); err != nil { 361 t.Fatal(err) 362 } 363 364 // Switch LDS update back to LDS with RDS name to watch. 365 xdsC.InvokeWatchListenerCallback(xdsresource.ListenerUpdate{RouteConfigName: routeStr}, nil) 366 waitForWatchRouteConfig(ctx, t, xdsC, routeStr) 367 xdsC.InvokeWatchRouteConfigCallback("", xdsresource.RouteConfigUpdate{ 368 VirtualHosts: []*xdsresource.VirtualHost{ 369 { 370 Domains: []string{targetStr}, 371 Routes: []*xdsresource.Route{{Prefix: newStringP(""), WeightedClusters: map[string]xdsresource.WeightedCluster{cluster: {Weight: 1}}}}, 372 }, 373 }, 374 }, nil) 375 if err := verifyServiceUpdate(ctx, serviceUpdateCh, wantUpdate); err != nil { 376 t.Fatal(err) 377 } 378 379 // Switch LDS resp to a LDS with inline RDS resource again. 380 xdsC.InvokeWatchListenerCallback(xdsresource.ListenerUpdate{InlineRouteConfig: &xdsresource.RouteConfigUpdate{ 381 VirtualHosts: []*xdsresource.VirtualHost{wantVirtualHosts2}, 382 }}, nil) 383 // This inline RDS resource should cause the RDS watch to be canceled. 384 if _, err := xdsC.WaitForCancelRouteConfigWatch(ctx); err != nil { 385 t.Fatalf("wait for cancel route watch failed: %v, want nil", err) 386 } 387 if err := verifyServiceUpdate(ctx, serviceUpdateCh, wantUpdate2); err != nil { 388 t.Fatal(err) 389 } 390 }