1// Copyright 2018 Istio Authors 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); 4// you may not use this file except in compliance with the License. 5// You may obtain a copy of the License at 6// 7// http://www.apache.org/licenses/LICENSE-2.0 8// 9// Unless required by applicable law or agreed to in writing, software 10// distributed under the License is distributed on an "AS IS" BASIS, 11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12// See the License for the specific language governing permissions and 13// limitations under the License. 14 15package route_test 16 17import ( 18 "os" 19 "reflect" 20 "testing" 21 "time" 22 23 envoyroute "github.com/envoyproxy/go-control-plane/envoy/api/v2/route" 24 "github.com/gogo/protobuf/types" 25 "github.com/golang/protobuf/ptypes" 26 "github.com/onsi/gomega" 27 28 "istio.io/istio/pkg/util/gogo" 29 30 networking "istio.io/api/networking/v1alpha3" 31 32 "istio.io/istio/pilot/pkg/features" 33 "istio.io/istio/pilot/pkg/model" 34 "istio.io/istio/pilot/pkg/networking/core/v1alpha3/route" 35 "istio.io/istio/pkg/config/host" 36 "istio.io/istio/pkg/config/mesh" 37 "istio.io/istio/pkg/config/protocol" 38 "istio.io/istio/pkg/config/schema/collections" 39) 40 41func TestBuildHTTPRoutes(t *testing.T) { 42 serviceRegistry := map[host.Name]*model.Service{ 43 "*.example.org": { 44 Hostname: "*.example.org", 45 Address: "1.1.1.1", 46 ClusterVIPs: make(map[string]string), 47 Ports: model.PortList{ 48 &model.Port{ 49 Name: "default", 50 Port: 8080, 51 Protocol: protocol.HTTP, 52 }, 53 }, 54 }, 55 } 56 57 node := &model.Proxy{ 58 Type: model.SidecarProxy, 59 IPAddresses: []string{"1.1.1.1"}, 60 ID: "someID", 61 DNSDomain: "foo.com", 62 Metadata: &model.NodeMetadata{}, 63 } 64 65 gatewayNames := map[string]bool{"some-gateway": true} 66 67 t.Run("for virtual service", func(t *testing.T) { 68 g := gomega.NewGomegaWithT(t) 69 70 os.Setenv("ISTIO_DEFAULT_REQUEST_TIMEOUT", "0ms") 71 defer os.Unsetenv("ISTIO_DEFAULT_REQUEST_TIMEOUT") 72 73 routes, err := route.BuildHTTPRoutesForVirtualService(node, nil, virtualServicePlain, serviceRegistry, 8080, gatewayNames) 74 75 // Valiate routes. 76 for _, r := range routes { 77 if err := r.Validate(); err != nil { 78 t.Fatalf("Route %s validation failed with error %v", r.Name, err) 79 } 80 } 81 g.Expect(err).NotTo(gomega.HaveOccurred()) 82 g.Expect(len(routes)).To(gomega.Equal(1)) 83 // Validate that when timeout is not specified, we disable it based on default value of flag. 84 g.Expect(routes[0].GetRoute().Timeout.Seconds).To(gomega.Equal(int64(0))) 85 g.Expect(routes[0].GetRoute().MaxGrpcTimeout.Seconds).To(gomega.Equal(int64(0))) 86 }) 87 88 t.Run("for virtual service with changed default timeout", func(t *testing.T) { 89 g := gomega.NewGomegaWithT(t) 90 91 dt := features.DefaultRequestTimeout 92 features.DefaultRequestTimeout = ptypes.DurationProto(1 * time.Second) 93 defer func() { features.DefaultRequestTimeout = dt }() 94 95 routes, err := route.BuildHTTPRoutesForVirtualService(node, nil, virtualServicePlain, serviceRegistry, 8080, gatewayNames) 96 // Valiate routes. 97 for _, r := range routes { 98 if err := r.Validate(); err != nil { 99 t.Fatalf("Route %s validation failed with error %v", r.Name, err) 100 } 101 } 102 103 g.Expect(err).NotTo(gomega.HaveOccurred()) 104 g.Expect(len(routes)).To(gomega.Equal(1)) 105 // Validate that when timeout is not specified, we send what is set in the timeout flag. 106 g.Expect(routes[0].GetRoute().Timeout.Seconds).To(gomega.Equal(int64(1))) 107 g.Expect(routes[0].GetRoute().MaxGrpcTimeout.Seconds).To(gomega.Equal(int64(1))) 108 }) 109 110 t.Run("for virtual service with timeout", func(t *testing.T) { 111 g := gomega.NewGomegaWithT(t) 112 113 routes, err := route.BuildHTTPRoutesForVirtualService(node, nil, virtualServiceWithTimeout, serviceRegistry, 8080, gatewayNames) 114 // Valiate routes. 115 for _, r := range routes { 116 if err := r.Validate(); err != nil { 117 t.Fatalf("Route %s validation failed with error %v", r.Name, err) 118 } 119 } 120 121 g.Expect(err).NotTo(gomega.HaveOccurred()) 122 g.Expect(len(routes)).To(gomega.Equal(1)) 123 // Validate that when timeout specified, we send the configured timeout to Envoys. 124 g.Expect(routes[0].GetRoute().Timeout.Seconds).To(gomega.Equal(int64(10))) 125 g.Expect(routes[0].GetRoute().MaxGrpcTimeout.Seconds).To(gomega.Equal(int64(10))) 126 }) 127 128 t.Run("for virtual service with disabled timeout", func(t *testing.T) { 129 g := gomega.NewGomegaWithT(t) 130 131 routes, err := route.BuildHTTPRoutesForVirtualService(node, nil, virtualServiceWithTimeoutDisabled, serviceRegistry, 8080, gatewayNames) 132 // Valiate routes. 133 for _, r := range routes { 134 if err := r.Validate(); err != nil { 135 t.Fatalf("Route %s validation failed with error %v", r.Name, err) 136 } 137 } 138 139 g.Expect(err).NotTo(gomega.HaveOccurred()) 140 g.Expect(len(routes)).To(gomega.Equal(1)) 141 g.Expect(routes[0].GetRoute().Timeout.Seconds).To(gomega.Equal(int64(0))) 142 g.Expect(routes[0].GetRoute().MaxGrpcTimeout.Seconds).To(gomega.Equal(int64(0))) 143 }) 144 145 t.Run("for virtual service with catch all route", func(t *testing.T) { 146 g := gomega.NewGomegaWithT(t) 147 routes, err := route.BuildHTTPRoutesForVirtualService(node, nil, virtualServiceWithCatchAllRoute, serviceRegistry, 8080, gatewayNames) 148 // Valiate routes. 149 for _, r := range routes { 150 if err := r.Validate(); err != nil { 151 t.Fatalf("Route %s validation failed with error %v", r.Name, err) 152 } 153 } 154 g.Expect(err).NotTo(gomega.HaveOccurred()) 155 g.Expect(len(routes)).To(gomega.Equal(1)) 156 }) 157 158 t.Run("for virtual service with top level catch all route", func(t *testing.T) { 159 g := gomega.NewGomegaWithT(t) 160 161 routes, err := route.BuildHTTPRoutesForVirtualService(node, nil, virtualServiceWithCatchAllRouteWeightedDestination, serviceRegistry, 8080, gatewayNames) 162 // Valiate routes. 163 for _, r := range routes { 164 if err := r.Validate(); err != nil { 165 t.Fatalf("Route %s validation failed with error %v", r.Name, err) 166 } 167 } 168 g.Expect(err).NotTo(gomega.HaveOccurred()) 169 g.Expect(len(routes)).To(gomega.Equal(1)) 170 }) 171 172 t.Run("for virtual service with multi prefix catch all route", func(t *testing.T) { 173 g := gomega.NewGomegaWithT(t) 174 175 routes, err := route.BuildHTTPRoutesForVirtualService(node, nil, virtualServiceWithCatchAllMultiPrefixRoute, serviceRegistry, 8080, gatewayNames) 176 // Valiate routes. 177 for _, r := range routes { 178 if err := r.Validate(); err != nil { 179 t.Fatalf("Route %s validation failed with error %v", r.Name, err) 180 } 181 } 182 g.Expect(err).NotTo(gomega.HaveOccurred()) 183 g.Expect(len(routes)).To(gomega.Equal(1)) 184 }) 185 186 t.Run("for virtual service with regex matching on URI", func(t *testing.T) { 187 g := gomega.NewGomegaWithT(t) 188 189 routes, err := route.BuildHTTPRoutesForVirtualService(node, nil, virtualServiceWithRegexMatchingOnURI, serviceRegistry, 8080, gatewayNames) 190 // Valiate routes. 191 for _, r := range routes { 192 if err := r.Validate(); err != nil { 193 t.Fatalf("Route %s validation failed with error %v", r.Name, err) 194 } 195 } 196 g.Expect(err).NotTo(gomega.HaveOccurred()) 197 g.Expect(len(routes)).To(gomega.Equal(1)) 198 g.Expect(routes[0].GetMatch().GetSafeRegex().GetRegex()).To(gomega.Equal("\\/(.?)\\/status")) 199 g.Expect(routes[0].GetMatch().GetSafeRegex().GetGoogleRe2().GetMaxProgramSize().GetValue()).To(gomega.Equal(uint32(1024))) 200 201 }) 202 203 t.Run("for virtual service with regex matching on header", func(t *testing.T) { 204 g := gomega.NewGomegaWithT(t) 205 206 routes, err := route.BuildHTTPRoutesForVirtualService(node, nil, virtualServiceWithRegexMatchingOnHeader, serviceRegistry, 8080, gatewayNames) 207 // Valiate routes. 208 for _, r := range routes { 209 if err := r.Validate(); err != nil { 210 t.Fatalf("Route %s validation failed with error %v", r.Name, err) 211 } 212 } 213 g.Expect(err).NotTo(gomega.HaveOccurred()) 214 g.Expect(len(routes)).To(gomega.Equal(1)) 215 g.Expect(routes[0].GetMatch().GetHeaders()[0].GetSafeRegexMatch().GetRegex()).To(gomega.Equal("Bearer .+?\\..+?\\..+?")) 216 }) 217 218 t.Run("for virtual service with regex matching on without_header", func(t *testing.T) { 219 g := gomega.NewGomegaWithT(t) 220 221 routes, err := route.BuildHTTPRoutesForVirtualService(node, nil, virtualServiceWithRegexMatchingOnWithoutHeader, serviceRegistry, 8080, gatewayNames) 222 // Valiate routes. 223 for _, r := range routes { 224 if err := r.Validate(); err != nil { 225 t.Fatalf("Route %s validation failed with error %v", r.Name, err) 226 } 227 } 228 g.Expect(err).NotTo(gomega.HaveOccurred()) 229 g.Expect(len(routes)).To(gomega.Equal(1)) 230 g.Expect(routes[0].GetMatch().GetHeaders()[0].GetSafeRegexMatch().GetRegex()).To(gomega.Equal("BAR .+?\\..+?\\..+?")) 231 g.Expect(routes[0].GetMatch().GetHeaders()[0].GetInvertMatch()).To(gomega.Equal(true)) 232 }) 233 234 t.Run("for virtual service with presence matching on header", func(t *testing.T) { 235 g := gomega.NewGomegaWithT(t) 236 237 routes, err := route.BuildHTTPRoutesForVirtualService(node, nil, virtualServiceWithPresentMatchingOnHeader, serviceRegistry, 8080, gatewayNames) 238 // Valiate routes. 239 for _, r := range routes { 240 if err := r.Validate(); err != nil { 241 t.Fatalf("Route %s validation failed with error %v", r.Name, err) 242 } 243 } 244 g.Expect(err).NotTo(gomega.HaveOccurred()) 245 g.Expect(len(routes)).To(gomega.Equal(1)) 246 g.Expect(routes[0].GetMatch().GetHeaders()[0].GetName()).To(gomega.Equal("FOO-HEADER")) 247 g.Expect(routes[0].GetMatch().GetHeaders()[0].GetPresentMatch()).To(gomega.Equal(true)) 248 g.Expect(routes[0].GetMatch().GetHeaders()[0].GetInvertMatch()).To(gomega.Equal(false)) 249 }) 250 251 t.Run("for virtual service with presence matching on header and without_header", func(t *testing.T) { 252 g := gomega.NewGomegaWithT(t) 253 254 routes, err := route.BuildHTTPRoutesForVirtualService(node, nil, virtualServiceWithPresentMatchingOnWithoutHeader, serviceRegistry, 8080, gatewayNames) 255 // Valiate routes. 256 for _, r := range routes { 257 if err := r.Validate(); err != nil { 258 t.Fatalf("Route %s validation failed with error %v", r.Name, err) 259 } 260 } 261 g.Expect(err).NotTo(gomega.HaveOccurred()) 262 g.Expect(len(routes)).To(gomega.Equal(1)) 263 g.Expect(routes[0].GetMatch().GetHeaders()[0].GetName()).To(gomega.Equal("FOO-HEADER")) 264 g.Expect(routes[0].GetMatch().GetHeaders()[0].GetPresentMatch()).To(gomega.Equal(true)) 265 g.Expect(routes[0].GetMatch().GetHeaders()[0].GetInvertMatch()).To(gomega.Equal(true)) 266 }) 267 268 t.Run("for virtual service with regex matching for all cases on header", func(t *testing.T) { 269 270 cset := createVirtualServiceWithRegexMatchingForAllCasesOnHeader() 271 272 for _, c := range cset { 273 g := gomega.NewGomegaWithT(t) 274 routes, err := route.BuildHTTPRoutesForVirtualService(node, nil, *c, serviceRegistry, 8080, gatewayNames) 275 // Valiate routes. 276 for _, r := range routes { 277 if err := r.Validate(); err != nil { 278 t.Fatalf("Route %s validation failed with error %v", r.Name, err) 279 } 280 } 281 g.Expect(err).NotTo(gomega.HaveOccurred()) 282 g.Expect(len(routes)).To(gomega.Equal(1)) 283 g.Expect(routes[0].GetMatch().GetHeaders()[0].GetName()).To(gomega.Equal("FOO-HEADER")) 284 g.Expect(routes[0].GetMatch().GetHeaders()[0].GetPresentMatch()).To(gomega.Equal(true)) 285 g.Expect(routes[0].GetMatch().GetHeaders()[0].GetInvertMatch()).To(gomega.Equal(false)) 286 } 287 }) 288 289 t.Run("for virtual service with source namespace matching", func(t *testing.T) { 290 g := gomega.NewGomegaWithT(t) 291 292 fooNode := *node 293 fooNode.Metadata = &model.NodeMetadata{ 294 Namespace: "foo", 295 } 296 barNode := *node 297 barNode.Metadata = &model.NodeMetadata{ 298 Namespace: "bar", 299 } 300 301 routes, err := route.BuildHTTPRoutesForVirtualService(&fooNode, nil, virtualServiceMatchingOnSourceNamespace, serviceRegistry, 8080, gatewayNames) 302 // Valiate routes. 303 for _, r := range routes { 304 if err := r.Validate(); err != nil { 305 t.Fatalf("Route %s validation failed with error %v", r.Name, err) 306 } 307 } 308 g.Expect(err).NotTo(gomega.HaveOccurred()) 309 g.Expect(len(routes)).To(gomega.Equal(1)) 310 g.Expect(routes[0].GetName()).To(gomega.Equal("foo")) 311 312 routes, err = route.BuildHTTPRoutesForVirtualService(&barNode, nil, virtualServiceMatchingOnSourceNamespace, serviceRegistry, 8080, gatewayNames) 313 g.Expect(err).NotTo(gomega.HaveOccurred()) 314 g.Expect(len(routes)).To(gomega.Equal(1)) 315 g.Expect(routes[0].GetName()).To(gomega.Equal("bar")) 316 }) 317 318 t.Run("for virtual service with ring hash", func(t *testing.T) { 319 g := gomega.NewGomegaWithT(t) 320 321 ttl := types.Duration{Nanos: 100} 322 meshConfig := mesh.DefaultMeshConfig() 323 push := &model.PushContext{ 324 Mesh: &meshConfig, 325 } 326 push.SetDestinationRules([]model.Config{ 327 { 328 ConfigMeta: model.ConfigMeta{ 329 Type: collections.IstioNetworkingV1Alpha3Destinationrules.Resource().Kind(), 330 Version: collections.IstioNetworkingV1Alpha3Destinationrules.Resource().Version(), 331 Name: "acme", 332 }, 333 Spec: &networking.DestinationRule{ 334 Host: "*.example.org", 335 TrafficPolicy: &networking.TrafficPolicy{ 336 LoadBalancer: &networking.LoadBalancerSettings{ 337 LbPolicy: &networking.LoadBalancerSettings_ConsistentHash{ 338 ConsistentHash: &networking.LoadBalancerSettings_ConsistentHashLB{ 339 HashKey: &networking.LoadBalancerSettings_ConsistentHashLB_HttpCookie{ 340 HttpCookie: &networking.LoadBalancerSettings_ConsistentHashLB_HTTPCookie{ 341 Name: "hash-cookie", 342 Ttl: &ttl, 343 }, 344 }, 345 }, 346 }, 347 }, 348 }, 349 }, 350 }, 351 }) 352 353 routes, err := route.BuildHTTPRoutesForVirtualService(node, push, virtualServicePlain, serviceRegistry, 8080, gatewayNames) 354 // Valiate routes. 355 for _, r := range routes { 356 if err := r.Validate(); err != nil { 357 t.Fatalf("Route %s validation failed with error %v", r.Name, err) 358 } 359 } 360 g.Expect(err).NotTo(gomega.HaveOccurred()) 361 g.Expect(len(routes)).To(gomega.Equal(1)) 362 363 hashPolicy := &envoyroute.RouteAction_HashPolicy{ 364 PolicySpecifier: &envoyroute.RouteAction_HashPolicy_Cookie_{ 365 Cookie: &envoyroute.RouteAction_HashPolicy_Cookie{ 366 Name: "hash-cookie", 367 Ttl: gogo.DurationToProtoDuration(&ttl), 368 }, 369 }, 370 } 371 g.Expect(routes[0].GetRoute().GetHashPolicy()).To(gomega.ConsistOf(hashPolicy)) 372 }) 373 374 t.Run("for virtual service with query param based ring hash", func(t *testing.T) { 375 g := gomega.NewGomegaWithT(t) 376 377 meshConfig := mesh.DefaultMeshConfig() 378 push := &model.PushContext{ 379 Mesh: &meshConfig, 380 } 381 push.SetDestinationRules([]model.Config{ 382 { 383 ConfigMeta: model.ConfigMeta{ 384 Type: collections.IstioNetworkingV1Alpha3Destinationrules.Resource().Kind(), 385 Version: collections.IstioNetworkingV1Alpha3Destinationrules.Resource().Version(), 386 Name: "acme", 387 }, 388 Spec: &networking.DestinationRule{ 389 Host: "*.example.org", 390 TrafficPolicy: &networking.TrafficPolicy{ 391 LoadBalancer: &networking.LoadBalancerSettings{ 392 LbPolicy: &networking.LoadBalancerSettings_ConsistentHash{ 393 ConsistentHash: &networking.LoadBalancerSettings_ConsistentHashLB{ 394 HashKey: &networking.LoadBalancerSettings_ConsistentHashLB_HttpQueryParameterName{ 395 HttpQueryParameterName: "query", 396 }, 397 }, 398 }, 399 }, 400 }, 401 }, 402 }, 403 }) 404 405 routes, err := route.BuildHTTPRoutesForVirtualService(node, push, virtualServicePlain, serviceRegistry, 8080, gatewayNames) 406 // Valiate routes. 407 for _, r := range routes { 408 if err := r.Validate(); err != nil { 409 t.Fatalf("Route %s validation failed with error %v", r.Name, err) 410 } 411 } 412 g.Expect(err).NotTo(gomega.HaveOccurred()) 413 g.Expect(len(routes)).To(gomega.Equal(1)) 414 415 hashPolicy := &envoyroute.RouteAction_HashPolicy{ 416 PolicySpecifier: &envoyroute.RouteAction_HashPolicy_QueryParameter_{ 417 QueryParameter: &envoyroute.RouteAction_HashPolicy_QueryParameter{ 418 Name: "query", 419 }, 420 }, 421 } 422 g.Expect(routes[0].GetRoute().GetHashPolicy()).To(gomega.ConsistOf(hashPolicy)) 423 }) 424 425 t.Run("for virtual service with subsets with ring hash", func(t *testing.T) { 426 g := gomega.NewGomegaWithT(t) 427 428 virtualService := model.Config{ 429 ConfigMeta: model.ConfigMeta{ 430 Type: collections.IstioNetworkingV1Alpha3Virtualservices.Resource().Kind(), 431 Version: collections.IstioNetworkingV1Alpha3Virtualservices.Resource().Version(), 432 Name: "acme", 433 }, 434 Spec: virtualServiceWithSubset, 435 } 436 437 meshConfig := mesh.DefaultMeshConfig() 438 push := &model.PushContext{ 439 Mesh: &meshConfig, 440 } 441 push.SetDestinationRules([]model.Config{ 442 { 443 ConfigMeta: model.ConfigMeta{ 444 Type: collections.IstioNetworkingV1Alpha3Destinationrules.Resource().Kind(), 445 Version: collections.IstioNetworkingV1Alpha3Destinationrules.Resource().Version(), 446 Name: "acme", 447 }, 448 Spec: &networking.DestinationRule{ 449 Host: "*.example.org", 450 Subsets: []*networking.Subset{networkingSubset}, 451 }, 452 }, 453 }) 454 455 routes, err := route.BuildHTTPRoutesForVirtualService(node, push, virtualService, serviceRegistry, 8080, gatewayNames) 456 // Valiate routes. 457 for _, r := range routes { 458 if err := r.Validate(); err != nil { 459 t.Fatalf("Route %s validation failed with error %v", r.Name, err) 460 } 461 } 462 g.Expect(err).NotTo(gomega.HaveOccurred()) 463 g.Expect(len(routes)).To(gomega.Equal(1)) 464 465 hashPolicy := &envoyroute.RouteAction_HashPolicy{ 466 PolicySpecifier: &envoyroute.RouteAction_HashPolicy_Cookie_{ 467 Cookie: &envoyroute.RouteAction_HashPolicy_Cookie{ 468 Name: "other-cookie", 469 Ttl: nil, 470 }, 471 }, 472 } 473 g.Expect(routes[0].GetRoute().GetHashPolicy()).To(gomega.ConsistOf(hashPolicy)) 474 }) 475 476 t.Run("for virtual service with subsets with port level settings with ring hash", func(t *testing.T) { 477 g := gomega.NewGomegaWithT(t) 478 479 virtualService := model.Config{ConfigMeta: model.ConfigMeta{Type: collections.IstioNetworkingV1Alpha3Virtualservices.Resource().Kind(), 480 Version: collections.IstioNetworkingV1Alpha3Virtualservices.Resource().Version(), 481 Name: "acme", 482 }, 483 Spec: virtualServiceWithSubsetWithPortLevelSettings, 484 } 485 486 meshConfig := mesh.DefaultMeshConfig() 487 push := &model.PushContext{ 488 Mesh: &meshConfig, 489 } 490 491 push.SetDestinationRules([]model.Config{ 492 { 493 494 ConfigMeta: model.ConfigMeta{ 495 Type: collections.IstioNetworkingV1Alpha3Destinationrules.Resource().Kind(), 496 Version: collections.IstioNetworkingV1Alpha3Destinationrules.Resource().Version(), 497 Name: "acme", 498 }, 499 Spec: portLevelDestinationRuleWithSubsetPolicy, 500 }}) 501 502 routes, err := route.BuildHTTPRoutesForVirtualService(node, push, virtualService, serviceRegistry, 8080, gatewayNames) 503 // Valiate routes. 504 for _, r := range routes { 505 if err := r.Validate(); err != nil { 506 t.Fatalf("Route %s validation failed with error %v", r.Name, err) 507 } 508 } 509 g.Expect(err).NotTo(gomega.HaveOccurred()) 510 g.Expect(len(routes)).To(gomega.Equal(1)) 511 512 hashPolicy := &envoyroute.RouteAction_HashPolicy{ 513 PolicySpecifier: &envoyroute.RouteAction_HashPolicy_Cookie_{ 514 Cookie: &envoyroute.RouteAction_HashPolicy_Cookie{ 515 Name: "port-level-settings-cookie", 516 Ttl: nil, 517 }, 518 }, 519 } 520 g.Expect(routes[0].GetRoute().GetHashPolicy()).To(gomega.ConsistOf(hashPolicy)) 521 }) 522 523 t.Run("for virtual service with subsets and top level traffic policy with ring hash", func(t *testing.T) { 524 g := gomega.NewGomegaWithT(t) 525 526 virtualService := model.Config{ 527 ConfigMeta: model.ConfigMeta{ 528 Type: collections.IstioNetworkingV1Alpha3Virtualservices.Resource().Kind(), 529 Version: collections.IstioNetworkingV1Alpha3Virtualservices.Resource().Version(), 530 Name: "acme", 531 }, 532 Spec: virtualServiceWithSubset, 533 } 534 535 cnfg := model.Config{ 536 ConfigMeta: model.ConfigMeta{ 537 Type: collections.IstioNetworkingV1Alpha3Destinationrules.Resource().Kind(), 538 Version: collections.IstioNetworkingV1Alpha3Destinationrules.Resource().Version(), 539 Name: "acme", 540 }, 541 } 542 rule := networkingDestinationRule 543 rule.Subsets = []*networking.Subset{networkingSubset} 544 cnfg.Spec = networkingDestinationRule 545 546 meshConfig := mesh.DefaultMeshConfig() 547 push := &model.PushContext{ 548 Mesh: &meshConfig, 549 } 550 551 push.SetDestinationRules([]model.Config{ 552 cnfg}) 553 554 routes, err := route.BuildHTTPRoutesForVirtualService(node, push, virtualService, serviceRegistry, 8080, gatewayNames) 555 // Valiate routes. 556 for _, r := range routes { 557 if err := r.Validate(); err != nil { 558 t.Fatalf("Route %s validation failed with error %v", r.Name, err) 559 } 560 } 561 g.Expect(err).NotTo(gomega.HaveOccurred()) 562 g.Expect(len(routes)).To(gomega.Equal(1)) 563 564 hashPolicy := &envoyroute.RouteAction_HashPolicy{ 565 PolicySpecifier: &envoyroute.RouteAction_HashPolicy_Cookie_{ 566 Cookie: &envoyroute.RouteAction_HashPolicy_Cookie{ 567 Name: "other-cookie", 568 Ttl: nil, 569 }, 570 }, 571 } 572 g.Expect(routes[0].GetRoute().GetHashPolicy()).To(gomega.ConsistOf(hashPolicy)) 573 }) 574 575 t.Run("port selector based traffic policy", func(t *testing.T) { 576 g := gomega.NewGomegaWithT(t) 577 578 meshConfig := mesh.DefaultMeshConfig() 579 push := &model.PushContext{ 580 Mesh: &meshConfig, 581 } 582 583 push.SetDestinationRules([]model.Config{ 584 { 585 ConfigMeta: model.ConfigMeta{ 586 Type: collections.IstioNetworkingV1Alpha3Destinationrules.Resource().Kind(), 587 Version: collections.IstioNetworkingV1Alpha3Destinationrules.Resource().Version(), 588 Name: "acme", 589 }, 590 Spec: portLevelDestinationRule, 591 }}) 592 593 gatewayNames := map[string]bool{"some-gateway": true} 594 routes, err := route.BuildHTTPRoutesForVirtualService(node, push, virtualServicePlain, serviceRegistry, 8080, gatewayNames) 595 // Valiate routes. 596 for _, r := range routes { 597 if err := r.Validate(); err != nil { 598 t.Fatalf("Route %s validation failed with error %v", r.Name, err) 599 } 600 } 601 g.Expect(err).NotTo(gomega.HaveOccurred()) 602 g.Expect(len(routes)).To(gomega.Equal(1)) 603 604 hashPolicy := &envoyroute.RouteAction_HashPolicy{ 605 PolicySpecifier: &envoyroute.RouteAction_HashPolicy_Cookie_{ 606 Cookie: &envoyroute.RouteAction_HashPolicy_Cookie{ 607 Name: "hash-cookie", 608 Ttl: nil, 609 }, 610 }, 611 } 612 g.Expect(routes[0].GetRoute().GetHashPolicy()).To(gomega.ConsistOf(hashPolicy)) 613 }) 614 615 t.Run("for redirect code", func(t *testing.T) { 616 g := gomega.NewGomegaWithT(t) 617 618 routes, err := route.BuildHTTPRoutesForVirtualService(node, nil, virtualServiceWithRedirect, 619 serviceRegistry, 8080, gatewayNames) 620 // Valiate routes. 621 for _, r := range routes { 622 if err := r.Validate(); err != nil { 623 t.Fatalf("Route %s validation failed with error %v", r.Name, err) 624 } 625 } 626 g.Expect(err).NotTo(gomega.HaveOccurred()) 627 g.Expect(len(routes)).To(gomega.Equal(1)) 628 629 redirectAction, ok := routes[0].Action.(*envoyroute.Route_Redirect) 630 g.Expect(ok).NotTo(gomega.BeFalse()) 631 g.Expect(redirectAction.Redirect.ResponseCode).To(gomega.Equal(envoyroute.RedirectAction_PERMANENT_REDIRECT)) 632 }) 633 t.Run("for no virtualservice but has destinationrule with consistentHash loadbalancer", func(t *testing.T) { 634 g := gomega.NewGomegaWithT(t) 635 meshConfig := mesh.DefaultMeshConfig() 636 push := &model.PushContext{ 637 Mesh: &meshConfig, 638 } 639 push.SetDestinationRules([]model.Config{ 640 { 641 ConfigMeta: model.ConfigMeta{ 642 Type: collections.IstioNetworkingV1Alpha3Destinationrules.Resource().Kind(), 643 Version: collections.IstioNetworkingV1Alpha3Destinationrules.Resource().Version(), 644 Name: "acme", 645 }, 646 Spec: networkingDestinationRule, 647 }}) 648 vhosts := route.BuildSidecarVirtualHostsFromConfigAndRegistry(node, push, serviceRegistry, []model.Config{}, 8080) 649 g.Expect(vhosts[0].Routes[0].Action.(*envoyroute.Route_Route).Route.HashPolicy).NotTo(gomega.BeNil()) 650 }) 651 t.Run("for no virtualservice but has destinationrule with portLevel consistentHash loadbalancer", func(t *testing.T) { 652 g := gomega.NewGomegaWithT(t) 653 meshConfig := mesh.DefaultMeshConfig() 654 push := &model.PushContext{ 655 Mesh: &meshConfig, 656 } 657 push.SetDestinationRules([]model.Config{ 658 { 659 ConfigMeta: model.ConfigMeta{ 660 Type: collections.IstioNetworkingV1Alpha3Destinationrules.Resource().Kind(), 661 Version: collections.IstioNetworkingV1Alpha3Destinationrules.Resource().Version(), 662 Name: "acme", 663 }, 664 Spec: networkingDestinationRuleWithPortLevelTrafficPolicy, 665 }}) 666 667 vhosts := route.BuildSidecarVirtualHostsFromConfigAndRegistry(node, push, serviceRegistry, []model.Config{}, 8080) 668 669 hashPolicy := &envoyroute.RouteAction_HashPolicy{ 670 PolicySpecifier: &envoyroute.RouteAction_HashPolicy_Cookie_{ 671 Cookie: &envoyroute.RouteAction_HashPolicy_Cookie{ 672 Name: "hash-cookie-1", 673 }, 674 }, 675 } 676 g.Expect(vhosts[0].Routes[0].Action.(*envoyroute.Route_Route).Route.HashPolicy).To(gomega.ConsistOf(hashPolicy)) 677 }) 678} 679 680func loadBalancerPolicy(name string) *networking.LoadBalancerSettings_ConsistentHash { 681 return &networking.LoadBalancerSettings_ConsistentHash{ 682 ConsistentHash: &networking.LoadBalancerSettings_ConsistentHashLB{ 683 HashKey: &networking.LoadBalancerSettings_ConsistentHashLB_HttpCookie{ 684 HttpCookie: &networking.LoadBalancerSettings_ConsistentHashLB_HTTPCookie{ 685 Name: name, 686 }, 687 }, 688 }, 689 } 690} 691 692var virtualServiceWithSubset = &networking.VirtualService{ 693 Hosts: []string{}, 694 Gateways: []string{"some-gateway"}, 695 Http: []*networking.HTTPRoute{ 696 { 697 Route: []*networking.HTTPRouteDestination{ 698 { 699 Destination: &networking.Destination{ 700 Subset: "some-subset", 701 Host: "*.example.org", 702 Port: &networking.PortSelector{ 703 Number: 65000, 704 }, 705 }, 706 Weight: 100, 707 }, 708 }, 709 }, 710 }, 711} 712 713var virtualServiceWithSubsetWithPortLevelSettings = &networking.VirtualService{ 714 Hosts: []string{}, 715 Gateways: []string{"some-gateway"}, 716 Http: []*networking.HTTPRoute{ 717 { 718 Route: []*networking.HTTPRouteDestination{ 719 { 720 Destination: &networking.Destination{ 721 Subset: "port-level-settings-subset", 722 Host: "*.example.org", 723 Port: &networking.PortSelector{ 724 Number: 8484, 725 }, 726 }, 727 Weight: 100, 728 }, 729 }, 730 }, 731 }, 732} 733 734var virtualServicePlain = model.Config{ 735 ConfigMeta: model.ConfigMeta{ 736 Type: collections.IstioNetworkingV1Alpha3Virtualservices.Resource().Kind(), 737 Version: collections.IstioNetworkingV1Alpha3Virtualservices.Resource().Version(), 738 Name: "acme", 739 }, 740 Spec: &networking.VirtualService{ 741 Hosts: []string{}, 742 Gateways: []string{"some-gateway"}, 743 Http: []*networking.HTTPRoute{ 744 { 745 Route: []*networking.HTTPRouteDestination{ 746 { 747 Destination: &networking.Destination{ 748 Host: "*.example.org", 749 Port: &networking.PortSelector{ 750 Number: 8484, 751 }, 752 }, 753 Weight: 100, 754 }, 755 }, 756 }, 757 }, 758 }, 759} 760 761var virtualServiceWithTimeout = model.Config{ 762 ConfigMeta: model.ConfigMeta{ 763 Type: collections.IstioNetworkingV1Alpha3Virtualservices.Resource().Kind(), 764 Version: collections.IstioNetworkingV1Alpha3Virtualservices.Resource().Version(), 765 Name: "acme", 766 }, 767 Spec: &networking.VirtualService{ 768 Hosts: []string{}, 769 Gateways: []string{"some-gateway"}, 770 Http: []*networking.HTTPRoute{ 771 { 772 Route: []*networking.HTTPRouteDestination{ 773 { 774 Destination: &networking.Destination{ 775 Host: "*.example.org", 776 Port: &networking.PortSelector{ 777 Number: 8484, 778 }, 779 }, 780 Weight: 100, 781 }, 782 }, 783 Timeout: &types.Duration{ 784 Seconds: 10, 785 }, 786 }, 787 }, 788 }, 789} 790 791var virtualServiceWithTimeoutDisabled = model.Config{ 792 ConfigMeta: model.ConfigMeta{ 793 Type: collections.IstioNetworkingV1Alpha3Virtualservices.Resource().Kind(), 794 Version: collections.IstioNetworkingV1Alpha3Virtualservices.Resource().Version(), 795 Name: "acme", 796 }, 797 Spec: &networking.VirtualService{ 798 Hosts: []string{}, 799 Gateways: []string{"some-gateway"}, 800 Http: []*networking.HTTPRoute{ 801 { 802 Route: []*networking.HTTPRouteDestination{ 803 { 804 Destination: &networking.Destination{ 805 Host: "*.example.org", 806 Port: &networking.PortSelector{ 807 Number: 8484, 808 }, 809 }, 810 Weight: 100, 811 }, 812 }, 813 Timeout: &types.Duration{ 814 Seconds: 0, 815 }, 816 }, 817 }, 818 }, 819} 820 821var virtualServiceWithCatchAllRoute = model.Config{ 822 ConfigMeta: model.ConfigMeta{ 823 Type: collections.IstioNetworkingV1Alpha3Virtualservices.Resource().Kind(), 824 Version: collections.IstioNetworkingV1Alpha3Virtualservices.Resource().Version(), 825 Name: "acme", 826 }, 827 Spec: &networking.VirtualService{ 828 Hosts: []string{}, 829 Gateways: []string{"some-gateway"}, 830 Http: []*networking.HTTPRoute{ 831 { 832 Match: []*networking.HTTPMatchRequest{ 833 { 834 Name: "non-catch-all", 835 Uri: &networking.StringMatch{ 836 MatchType: &networking.StringMatch_Prefix{ 837 Prefix: "/route/v1", 838 }, 839 }, 840 }, 841 { 842 Name: "catch-all", 843 Uri: &networking.StringMatch{ 844 MatchType: &networking.StringMatch_Prefix{ 845 Prefix: "/", 846 }, 847 }, 848 }, 849 }, 850 Route: []*networking.HTTPRouteDestination{ 851 { 852 Destination: &networking.Destination{ 853 Host: "*.example.org", 854 Port: &networking.PortSelector{ 855 Number: 8484, 856 }, 857 }, 858 Weight: 100, 859 }, 860 }, 861 }, 862 }, 863 }, 864} 865 866var virtualServiceWithCatchAllMultiPrefixRoute = model.Config{ 867 ConfigMeta: model.ConfigMeta{ 868 Type: collections.IstioNetworkingV1Alpha3Virtualservices.Resource().Kind(), 869 Version: collections.IstioNetworkingV1Alpha3Virtualservices.Resource().Version(), 870 Name: "acme", 871 }, 872 Spec: &networking.VirtualService{ 873 Hosts: []string{}, 874 Gateways: []string{"some-gateway"}, 875 Http: []*networking.HTTPRoute{ 876 { 877 Match: []*networking.HTTPMatchRequest{ 878 { 879 Name: "catch-all", 880 Uri: &networking.StringMatch{ 881 MatchType: &networking.StringMatch_Prefix{ 882 Prefix: "/", 883 }, 884 }, 885 SourceLabels: map[string]string{ 886 "matchingNoSrc": "xxx", 887 }, 888 }, 889 { 890 Name: "specific match", 891 Uri: &networking.StringMatch{ 892 MatchType: &networking.StringMatch_Prefix{ 893 Prefix: "/a", 894 }, 895 }, 896 }, 897 }, 898 Route: []*networking.HTTPRouteDestination{ 899 { 900 Destination: &networking.Destination{ 901 Host: "*.example.org", 902 Port: &networking.PortSelector{ 903 Number: 8484, 904 }, 905 }, 906 Weight: 100, 907 }, 908 }, 909 }, 910 }, 911 }, 912} 913 914var virtualServiceWithCatchAllRouteWeightedDestination = model.Config{ 915 ConfigMeta: model.ConfigMeta{ 916 Type: collections.IstioNetworkingV1Alpha3Virtualservices.Resource().Kind(), 917 Version: collections.IstioNetworkingV1Alpha3Virtualservices.Resource().Version(), 918 Name: "acme", 919 }, 920 Spec: &networking.VirtualService{ 921 Hosts: []string{"headers.test.istio.io"}, 922 Gateways: []string{"some-gateway"}, 923 Http: []*networking.HTTPRoute{ 924 { 925 Match: []*networking.HTTPMatchRequest{ 926 { 927 Name: "headers-only", 928 Headers: map[string]*networking.StringMatch{ 929 "version": { 930 MatchType: &networking.StringMatch_Exact{ 931 Exact: "v2", 932 }, 933 }, 934 }, 935 SourceLabels: map[string]string{ 936 "version": "v1", 937 }, 938 }, 939 }, 940 Route: []*networking.HTTPRouteDestination{ 941 { 942 Destination: &networking.Destination{ 943 Host: "c-weighted.extsvc.com", 944 Subset: "v2", 945 }, 946 Weight: 100, 947 }, 948 }, 949 }, 950 { 951 Route: []*networking.HTTPRouteDestination{ 952 { 953 Destination: &networking.Destination{ 954 Host: "c-weighted.extsvc.com", 955 Subset: "v1", 956 }, 957 Weight: 100, 958 }, 959 }, 960 }, 961 }, 962 }, 963} 964 965var virtualServiceWithRedirect = model.Config{ 966 ConfigMeta: model.ConfigMeta{ 967 Type: collections.IstioNetworkingV1Alpha3Virtualservices.Resource().Kind(), 968 Version: collections.IstioNetworkingV1Alpha3Virtualservices.Resource().Version(), 969 Name: "acme", 970 }, 971 Spec: &networking.VirtualService{ 972 Hosts: []string{}, 973 Gateways: []string{"some-gateway"}, 974 Http: []*networking.HTTPRoute{ 975 { 976 Redirect: &networking.HTTPRedirect{ 977 Uri: "example.org", 978 Authority: "some-authority.default.svc.cluster.local", 979 RedirectCode: 308, 980 }, 981 }, 982 }, 983 }, 984} 985 986var virtualServiceWithRegexMatchingOnURI = model.Config{ 987 ConfigMeta: model.ConfigMeta{ 988 Type: collections.IstioNetworkingV1Alpha3Virtualservices.Resource().Kind(), 989 Version: collections.IstioNetworkingV1Alpha3Virtualservices.Resource().Version(), 990 Name: "acme", 991 }, 992 Spec: &networking.VirtualService{ 993 Hosts: []string{}, 994 Gateways: []string{"some-gateway"}, 995 Http: []*networking.HTTPRoute{ 996 { 997 Match: []*networking.HTTPMatchRequest{ 998 { 999 Name: "status", 1000 Uri: &networking.StringMatch{ 1001 MatchType: &networking.StringMatch_Regex{ 1002 Regex: "\\/(.?)\\/status", 1003 }, 1004 }, 1005 }, 1006 }, 1007 Redirect: &networking.HTTPRedirect{ 1008 Uri: "example.org", 1009 Authority: "some-authority.default.svc.cluster.local", 1010 RedirectCode: 308, 1011 }, 1012 }, 1013 }, 1014 }, 1015} 1016 1017var virtualServiceWithRegexMatchingOnHeader = model.Config{ 1018 ConfigMeta: model.ConfigMeta{ 1019 Type: collections.IstioNetworkingV1Alpha3Virtualservices.Resource().Kind(), 1020 Version: collections.IstioNetworkingV1Alpha3Virtualservices.Resource().Version(), 1021 Name: "acme", 1022 }, 1023 Spec: &networking.VirtualService{ 1024 Hosts: []string{}, 1025 Gateways: []string{"some-gateway"}, 1026 Http: []*networking.HTTPRoute{ 1027 { 1028 Match: []*networking.HTTPMatchRequest{ 1029 { 1030 Name: "auth", 1031 Headers: map[string]*networking.StringMatch{ 1032 "Authentication": { 1033 MatchType: &networking.StringMatch_Regex{ 1034 Regex: "Bearer .+?\\..+?\\..+?", 1035 }, 1036 }, 1037 }, 1038 }, 1039 }, 1040 Redirect: &networking.HTTPRedirect{ 1041 Uri: "example.org", 1042 Authority: "some-authority.default.svc.cluster.local", 1043 RedirectCode: 308, 1044 }, 1045 }, 1046 }, 1047 }, 1048} 1049 1050func createVirtualServiceWithRegexMatchingForAllCasesOnHeader() []*model.Config { 1051 ret := []*model.Config{} 1052 regex := "*" 1053 ret = append(ret, &model.Config{ 1054 ConfigMeta: model.ConfigMeta{ 1055 Type: collections.IstioNetworkingV1Alpha3Virtualservices.Resource().Kind(), 1056 Version: collections.IstioNetworkingV1Alpha3Virtualservices.Resource().Version(), 1057 Name: "acme", 1058 }, 1059 Spec: &networking.VirtualService{ 1060 Hosts: []string{}, 1061 Gateways: []string{"some-gateway"}, 1062 Http: []*networking.HTTPRoute{ 1063 { 1064 Match: []*networking.HTTPMatchRequest{ 1065 { 1066 Name: "presence", 1067 Headers: map[string]*networking.StringMatch{ 1068 "FOO-HEADER": { 1069 MatchType: &networking.StringMatch_Regex{ 1070 Regex: regex, 1071 }, 1072 }, 1073 }, 1074 }, 1075 }, 1076 Redirect: &networking.HTTPRedirect{ 1077 Uri: "example.org", 1078 Authority: "some-authority.default.svc.cluster.local", 1079 RedirectCode: 308, 1080 }, 1081 }, 1082 }, 1083 }, 1084 }) 1085 1086 return ret 1087} 1088 1089var virtualServiceWithRegexMatchingOnWithoutHeader = model.Config{ 1090 ConfigMeta: model.ConfigMeta{ 1091 Type: collections.IstioNetworkingV1Alpha3Virtualservices.Resource().Kind(), 1092 Version: collections.IstioNetworkingV1Alpha3Virtualservices.Resource().Version(), 1093 Name: "acme", 1094 }, 1095 Spec: &networking.VirtualService{ 1096 Hosts: []string{}, 1097 Gateways: []string{"some-gateway"}, 1098 Http: []*networking.HTTPRoute{ 1099 { 1100 Match: []*networking.HTTPMatchRequest{ 1101 { 1102 Name: "without-test", 1103 WithoutHeaders: map[string]*networking.StringMatch{ 1104 "FOO-HEADER": { 1105 MatchType: &networking.StringMatch_Regex{ 1106 Regex: "BAR .+?\\..+?\\..+?", 1107 }, 1108 }, 1109 }, 1110 }, 1111 }, 1112 Redirect: &networking.HTTPRedirect{ 1113 Uri: "example.org", 1114 Authority: "some-authority.default.svc.cluster.local", 1115 RedirectCode: 308, 1116 }, 1117 }, 1118 }, 1119 }, 1120} 1121 1122var virtualServiceWithPresentMatchingOnHeader = model.Config{ 1123 ConfigMeta: model.ConfigMeta{ 1124 Type: collections.IstioNetworkingV1Alpha3Virtualservices.Resource().Kind(), 1125 Version: collections.IstioNetworkingV1Alpha3Virtualservices.Resource().Version(), 1126 Name: "acme", 1127 }, 1128 Spec: &networking.VirtualService{ 1129 Hosts: []string{}, 1130 Gateways: []string{"some-gateway"}, 1131 Http: []*networking.HTTPRoute{ 1132 { 1133 Match: []*networking.HTTPMatchRequest{ 1134 { 1135 Name: "presence", 1136 Headers: map[string]*networking.StringMatch{ 1137 "FOO-HEADER": nil, 1138 }, 1139 }, 1140 }, 1141 Redirect: &networking.HTTPRedirect{ 1142 Uri: "example.org", 1143 Authority: "some-authority.default.svc.cluster.local", 1144 RedirectCode: 308, 1145 }, 1146 }, 1147 }, 1148 }, 1149} 1150 1151var virtualServiceWithPresentMatchingOnWithoutHeader = model.Config{ 1152 ConfigMeta: model.ConfigMeta{ 1153 Type: collections.IstioNetworkingV1Alpha3Virtualservices.Resource().Kind(), 1154 Version: collections.IstioNetworkingV1Alpha3Virtualservices.Resource().Version(), 1155 Name: "acme", 1156 }, 1157 Spec: &networking.VirtualService{ 1158 Hosts: []string{}, 1159 Gateways: []string{"some-gateway"}, 1160 Http: []*networking.HTTPRoute{ 1161 { 1162 Match: []*networking.HTTPMatchRequest{ 1163 { 1164 Name: "presence", 1165 WithoutHeaders: map[string]*networking.StringMatch{ 1166 "FOO-HEADER": nil, 1167 }, 1168 }, 1169 }, 1170 Redirect: &networking.HTTPRedirect{ 1171 Uri: "example.org", 1172 Authority: "some-authority.default.svc.cluster.local", 1173 RedirectCode: 308, 1174 }, 1175 }, 1176 }, 1177 }, 1178} 1179 1180var virtualServiceMatchingOnSourceNamespace = model.Config{ 1181 ConfigMeta: model.ConfigMeta{ 1182 Type: collections.IstioNetworkingV1Alpha3Virtualservices.Resource().Kind(), 1183 Version: collections.IstioNetworkingV1Alpha3Virtualservices.Resource().Version(), 1184 Name: "acme", 1185 }, 1186 Spec: &networking.VirtualService{ 1187 Hosts: []string{}, 1188 Http: []*networking.HTTPRoute{ 1189 { 1190 Name: "foo", 1191 Match: []*networking.HTTPMatchRequest{ 1192 { 1193 SourceNamespace: "foo", 1194 }, 1195 }, 1196 Route: []*networking.HTTPRouteDestination{ 1197 { 1198 Destination: &networking.Destination{ 1199 Host: "foo.example.org", 1200 Port: &networking.PortSelector{ 1201 Number: 8484, 1202 }, 1203 }, 1204 Weight: 100, 1205 }, 1206 }, 1207 }, 1208 { 1209 Name: "bar", 1210 Match: []*networking.HTTPMatchRequest{ 1211 { 1212 SourceNamespace: "bar", 1213 }, 1214 }, 1215 Route: []*networking.HTTPRouteDestination{ 1216 { 1217 Destination: &networking.Destination{ 1218 Host: "bar.example.org", 1219 Port: &networking.PortSelector{ 1220 Number: 8484, 1221 }, 1222 }, 1223 Weight: 100, 1224 }, 1225 }, 1226 }, 1227 }, 1228 }, 1229} 1230 1231var portLevelDestinationRule = &networking.DestinationRule{ 1232 Host: "*.example.org", 1233 Subsets: []*networking.Subset{}, 1234 TrafficPolicy: &networking.TrafficPolicy{ 1235 PortLevelSettings: []*networking.TrafficPolicy_PortTrafficPolicy{ 1236 { 1237 LoadBalancer: &networking.LoadBalancerSettings{ 1238 LbPolicy: loadBalancerPolicy("hash-cookie"), 1239 }, 1240 Port: &networking.PortSelector{ 1241 Number: 8484, 1242 }, 1243 }, 1244 }, 1245 }, 1246} 1247 1248var portLevelDestinationRuleWithSubsetPolicy = &networking.DestinationRule{ 1249 Host: "*.example.org", 1250 Subsets: []*networking.Subset{networkingSubsetWithPortLevelSettings}, 1251 TrafficPolicy: &networking.TrafficPolicy{ 1252 PortLevelSettings: []*networking.TrafficPolicy_PortTrafficPolicy{ 1253 { 1254 LoadBalancer: &networking.LoadBalancerSettings{ 1255 LbPolicy: loadBalancerPolicy("hash-cookie"), 1256 }, 1257 Port: &networking.PortSelector{ 1258 Number: 8484, 1259 }, 1260 }, 1261 }, 1262 }, 1263} 1264 1265var networkingDestinationRule = &networking.DestinationRule{ 1266 Host: "*.example.org", 1267 Subsets: []*networking.Subset{}, 1268 TrafficPolicy: &networking.TrafficPolicy{ 1269 LoadBalancer: &networking.LoadBalancerSettings{ 1270 LbPolicy: loadBalancerPolicy("hash-cookie"), 1271 }, 1272 }, 1273} 1274var networkingDestinationRuleWithPortLevelTrafficPolicy = &networking.DestinationRule{ 1275 Host: "*.example.org", 1276 TrafficPolicy: &networking.TrafficPolicy{ 1277 LoadBalancer: &networking.LoadBalancerSettings{ 1278 LbPolicy: loadBalancerPolicy("hash-cookie"), 1279 }, 1280 PortLevelSettings: []*networking.TrafficPolicy_PortTrafficPolicy{ 1281 { 1282 LoadBalancer: &networking.LoadBalancerSettings{ 1283 LbPolicy: loadBalancerPolicy("hash-cookie-1"), 1284 }, 1285 Port: &networking.PortSelector{ 1286 Number: 8080, 1287 }, 1288 }, 1289 }, 1290 }, 1291} 1292var networkingSubset = &networking.Subset{ 1293 Name: "some-subset", 1294 Labels: map[string]string{}, 1295 TrafficPolicy: &networking.TrafficPolicy{ 1296 LoadBalancer: &networking.LoadBalancerSettings{ 1297 LbPolicy: &networking.LoadBalancerSettings_ConsistentHash{ 1298 ConsistentHash: &networking.LoadBalancerSettings_ConsistentHashLB{ 1299 HashKey: &networking.LoadBalancerSettings_ConsistentHashLB_HttpCookie{ 1300 HttpCookie: &networking.LoadBalancerSettings_ConsistentHashLB_HTTPCookie{ 1301 Name: "other-cookie", 1302 }, 1303 }, 1304 }, 1305 }, 1306 }, 1307 }, 1308} 1309 1310var networkingSubsetWithPortLevelSettings = &networking.Subset{ 1311 Name: "port-level-settings-subset", 1312 Labels: map[string]string{}, 1313 TrafficPolicy: &networking.TrafficPolicy{ 1314 PortLevelSettings: []*networking.TrafficPolicy_PortTrafficPolicy{ 1315 { 1316 LoadBalancer: &networking.LoadBalancerSettings{ 1317 LbPolicy: loadBalancerPolicy("port-level-settings-cookie"), 1318 }, 1319 Port: &networking.PortSelector{ 1320 Number: 8484, 1321 }, 1322 }, 1323 }, 1324 }, 1325} 1326 1327func TestCombineVHostRoutes(t *testing.T) { 1328 first := []*envoyroute.Route{ 1329 {Match: &envoyroute.RouteMatch{PathSpecifier: &envoyroute.RouteMatch_Path{Path: "/path1"}}}, 1330 {Match: &envoyroute.RouteMatch{PathSpecifier: &envoyroute.RouteMatch_Prefix{Prefix: "/prefix1"}}}, 1331 {Match: &envoyroute.RouteMatch{PathSpecifier: &envoyroute.RouteMatch_Regex{Regex: ".*?regex1"}}}, 1332 {Match: &envoyroute.RouteMatch{PathSpecifier: &envoyroute.RouteMatch_Prefix{Prefix: "/"}}}, 1333 } 1334 second := []*envoyroute.Route{ 1335 {Match: &envoyroute.RouteMatch{PathSpecifier: &envoyroute.RouteMatch_Path{Path: "/path12"}}}, 1336 {Match: &envoyroute.RouteMatch{PathSpecifier: &envoyroute.RouteMatch_Prefix{Prefix: "/prefix12"}}}, 1337 {Match: &envoyroute.RouteMatch{PathSpecifier: &envoyroute.RouteMatch_Regex{Regex: ".*?regex12"}}}, 1338 {Match: &envoyroute.RouteMatch{ 1339 PathSpecifier: &envoyroute.RouteMatch_Regex{Regex: "*"}, 1340 Headers: []*envoyroute.HeaderMatcher{ 1341 { 1342 Name: "foo", 1343 HeaderMatchSpecifier: &envoyroute.HeaderMatcher_ExactMatch{ExactMatch: "bar"}, 1344 InvertMatch: false, 1345 }, 1346 }, 1347 }}, 1348 } 1349 1350 want := []*envoyroute.Route{ 1351 {Match: &envoyroute.RouteMatch{PathSpecifier: &envoyroute.RouteMatch_Path{Path: "/path1"}}}, 1352 {Match: &envoyroute.RouteMatch{PathSpecifier: &envoyroute.RouteMatch_Prefix{Prefix: "/prefix1"}}}, 1353 {Match: &envoyroute.RouteMatch{PathSpecifier: &envoyroute.RouteMatch_Regex{Regex: ".*?regex1"}}}, 1354 {Match: &envoyroute.RouteMatch{PathSpecifier: &envoyroute.RouteMatch_Path{Path: "/path12"}}}, 1355 {Match: &envoyroute.RouteMatch{PathSpecifier: &envoyroute.RouteMatch_Prefix{Prefix: "/prefix12"}}}, 1356 {Match: &envoyroute.RouteMatch{PathSpecifier: &envoyroute.RouteMatch_Regex{Regex: ".*?regex12"}}}, 1357 {Match: &envoyroute.RouteMatch{ 1358 PathSpecifier: &envoyroute.RouteMatch_Regex{Regex: "*"}, 1359 Headers: []*envoyroute.HeaderMatcher{ 1360 { 1361 Name: "foo", 1362 HeaderMatchSpecifier: &envoyroute.HeaderMatcher_ExactMatch{ExactMatch: "bar"}, 1363 InvertMatch: false, 1364 }, 1365 }, 1366 }}, 1367 {Match: &envoyroute.RouteMatch{PathSpecifier: &envoyroute.RouteMatch_Prefix{Prefix: "/"}}}, 1368 } 1369 1370 got := route.CombineVHostRoutes(first, second) 1371 if !reflect.DeepEqual(want, got) { 1372 t.Errorf("CombineVHostRoutes: \n") 1373 t.Errorf("got: \n") 1374 for _, g := range got { 1375 t.Errorf("%v\n", g.Match.PathSpecifier) 1376 } 1377 t.Errorf("want: \n") 1378 for _, g := range want { 1379 t.Errorf("%v\n", g.Match.PathSpecifier) 1380 } 1381 } 1382} 1383