istio.io/istio@v0.0.0-20240520182934-d79c90f27776/pilot/pkg/networking/core/route/route_test.go (about) 1 // Copyright 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 15 package route_test 16 17 import ( 18 "log" 19 "reflect" 20 "testing" 21 22 envoycore "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" 23 envoyroute "github.com/envoyproxy/go-control-plane/envoy/config/route/v3" 24 matcher "github.com/envoyproxy/go-control-plane/envoy/type/matcher/v3" 25 . "github.com/onsi/gomega" 26 "google.golang.org/protobuf/types/known/durationpb" 27 "k8s.io/apimachinery/pkg/types" 28 29 networking "istio.io/api/networking/v1alpha3" 30 "istio.io/istio/pilot/pkg/model" 31 "istio.io/istio/pilot/pkg/networking/core" 32 "istio.io/istio/pilot/pkg/networking/core/route" 33 "istio.io/istio/pilot/pkg/networking/util" 34 "istio.io/istio/pilot/pkg/xds/filters" 35 "istio.io/istio/pilot/test/xdstest" 36 "istio.io/istio/pkg/config" 37 "istio.io/istio/pkg/config/constants" 38 "istio.io/istio/pkg/config/host" 39 "istio.io/istio/pkg/config/protocol" 40 "istio.io/istio/pkg/config/schema/gvk" 41 "istio.io/istio/pkg/util/sets" 42 ) 43 44 func TestBuildHTTPRoutes(t *testing.T) { 45 serviceRegistry := map[host.Name]*model.Service{ 46 "*.example.org": { 47 Hostname: "*.example.org", 48 DefaultAddress: "1.1.1.1", 49 Ports: model.PortList{ 50 &model.Port{ 51 Name: "default", 52 Port: 8080, 53 Protocol: protocol.HTTP, 54 }, 55 }, 56 }, 57 } 58 59 node := func(cg *core.ConfigGenTest) *model.Proxy { 60 return cg.SetupProxy(&model.Proxy{ 61 Type: model.SidecarProxy, 62 IPAddresses: []string{"1.1.1.1"}, 63 ID: "someID", 64 DNSDomain: "foo.com", 65 IstioVersion: &model.IstioVersion{ 66 Major: 1, 67 Minor: 20, 68 }, 69 }) 70 } 71 72 nodeWithExtended := func(cg *core.ConfigGenTest) *model.Proxy { 73 out := node(cg) 74 out.IstioVersion.Minor = 21 75 return out 76 } 77 78 gatewayNames := sets.New("some-gateway") 79 80 t.Run("for virtual service", func(t *testing.T) { 81 g := NewWithT(t) 82 cg := core.NewConfigGenTest(t, core.TestOptions{}) 83 84 t.Setenv("ISTIO_DEFAULT_REQUEST_TIMEOUT", "0ms") 85 86 routes, err := route.BuildHTTPRoutesForVirtualService(node(cg), virtualServicePlain, serviceRegistry, nil, 8080, gatewayNames, route.RouteOptions{}) 87 xdstest.ValidateRoutes(t, routes) 88 89 g.Expect(err).NotTo(HaveOccurred()) 90 g.Expect(len(routes)).To(Equal(1)) 91 // Validate that when timeout is not specified, we disable it based on default value of flag. 92 g.Expect(routes[0].GetRoute().Timeout.Seconds).To(Equal(int64(0))) 93 // nolint: staticcheck 94 g.Expect(routes[0].GetRoute().MaxGrpcTimeout.Seconds).To(Equal(int64(0))) 95 }) 96 97 t.Run("for virtual service with HTTP/3 discovery enabled", func(t *testing.T) { 98 g := NewWithT(t) 99 cg := core.NewConfigGenTest(t, core.TestOptions{}) 100 101 routes, err := route.BuildHTTPRoutesForVirtualService(node(cg), virtualServicePlain, serviceRegistry, 102 nil, 8080, gatewayNames, route.RouteOptions{IsHTTP3AltSvcHeaderNeeded: true}) 103 xdstest.ValidateRoutes(t, routes) 104 g.Expect(err).NotTo(HaveOccurred()) 105 g.Expect(routes[0].GetResponseHeadersToAdd()).To(Equal([]*envoycore.HeaderValueOption{ 106 { 107 Header: &envoycore.HeaderValue{ 108 Key: util.AltSvcHeader, 109 Value: `h3=":8080"; ma=86400`, 110 }, 111 AppendAction: envoycore.HeaderValueOption_APPEND_IF_EXISTS_OR_ADD, 112 }, 113 })) 114 }) 115 116 t.Run("for virtual service with timeout", func(t *testing.T) { 117 g := NewWithT(t) 118 cg := core.NewConfigGenTest(t, core.TestOptions{}) 119 120 routes, err := route.BuildHTTPRoutesForVirtualService(node(cg), virtualServiceWithTimeout, serviceRegistry, 121 nil, 8080, gatewayNames, route.RouteOptions{}) 122 xdstest.ValidateRoutes(t, routes) 123 124 g.Expect(err).NotTo(HaveOccurred()) 125 g.Expect(len(routes)).To(Equal(1)) 126 // Validate that when timeout specified, we send the configured timeout to Envoys. 127 g.Expect(routes[0].GetRoute().Timeout.Seconds).To(Equal(int64(10))) 128 // nolint: staticcheck 129 g.Expect(routes[0].GetRoute().MaxGrpcTimeout.Seconds).To(Equal(int64(10))) 130 }) 131 132 t.Run("for virtual service with disabled timeout", func(t *testing.T) { 133 g := NewWithT(t) 134 cg := core.NewConfigGenTest(t, core.TestOptions{}) 135 136 routes, err := route.BuildHTTPRoutesForVirtualService(node(cg), virtualServiceWithTimeoutDisabled, serviceRegistry, 137 nil, 8080, gatewayNames, route.RouteOptions{}) 138 xdstest.ValidateRoutes(t, routes) 139 140 g.Expect(err).NotTo(HaveOccurred()) 141 g.Expect(len(routes)).To(Equal(1)) 142 g.Expect(routes[0].GetRoute().Timeout.Seconds).To(Equal(int64(0))) 143 // nolint: staticcheck 144 g.Expect(routes[0].GetRoute().MaxGrpcTimeout.Seconds).To(Equal(int64(0))) 145 }) 146 147 t.Run("for virtual service with catch all route", func(t *testing.T) { 148 g := NewWithT(t) 149 cg := core.NewConfigGenTest(t, core.TestOptions{}) 150 routes, err := route.BuildHTTPRoutesForVirtualService(node(cg), virtualServiceWithCatchAllRoute, 151 serviceRegistry, nil, 8080, gatewayNames, route.RouteOptions{}) 152 xdstest.ValidateRoutes(t, routes) 153 154 g.Expect(err).NotTo(HaveOccurred()) 155 g.Expect(len(routes)).To(Equal(2)) 156 g.Expect(routes[0].Name).To(Equal("route.non-catch-all")) 157 g.Expect(routes[1].Name).To(Equal("route.catch-all")) 158 }) 159 160 t.Run("for virtual service with catch all routeļ¼port match", func(t *testing.T) { 161 g := NewWithT(t) 162 cg := core.NewConfigGenTest(t, core.TestOptions{}) 163 routes, err := route.BuildHTTPRoutesForVirtualService(node(cg), virtualServiceWithCatchAllPort, 164 serviceRegistry, nil, 8080, gatewayNames, route.RouteOptions{}) 165 xdstest.ValidateRoutes(t, routes) 166 167 g.Expect(err).NotTo(HaveOccurred()) 168 g.Expect(len(routes)).To(Equal(1)) 169 g.Expect(routes[0].Name).To(Equal("route 1.catch-all for 8080")) 170 }) 171 172 t.Run("for internally generated virtual service with ingress semantics", func(t *testing.T) { 173 g := NewWithT(t) 174 cg := core.NewConfigGenTest(t, core.TestOptions{}) 175 176 vs := virtualServiceWithCatchAllRoute 177 if vs.Annotations == nil { 178 vs.Annotations = make(map[string]string) 179 } 180 vs.Annotations[constants.InternalRouteSemantics] = constants.RouteSemanticsIngress 181 182 routes, err := route.BuildHTTPRoutesForVirtualService(node(cg), vs, 183 serviceRegistry, nil, 8080, gatewayNames, route.RouteOptions{}) 184 xdstest.ValidateRoutes(t, routes) 185 186 g.Expect(err).NotTo(HaveOccurred()) 187 g.Expect(routes[0].Match.PathSpecifier).To(Equal(&envoyroute.RouteMatch_PathSeparatedPrefix{ 188 PathSeparatedPrefix: "/route/v1", 189 })) 190 g.Expect(routes[1].Match.PathSpecifier).To(Equal(&envoyroute.RouteMatch_Prefix{ 191 Prefix: "/", 192 })) 193 }) 194 195 t.Run("for internally generated virtual service with gateway semantics", func(t *testing.T) { 196 g := NewWithT(t) 197 cg := core.NewConfigGenTest(t, core.TestOptions{}) 198 199 vs := virtualServiceWithCatchAllRoute 200 if vs.Annotations == nil { 201 vs.Annotations = make(map[string]string) 202 } 203 vs.Annotations[constants.InternalRouteSemantics] = constants.RouteSemanticsGateway 204 205 routes, err := route.BuildHTTPRoutesForVirtualService(node(cg), vs, 206 serviceRegistry, nil, 8080, gatewayNames, route.RouteOptions{}) 207 xdstest.ValidateRoutes(t, routes) 208 209 g.Expect(err).NotTo(HaveOccurred()) 210 g.Expect(routes[0].Match.PathSpecifier).To(Equal(&envoyroute.RouteMatch_PathSeparatedPrefix{ 211 PathSeparatedPrefix: "/route/v1", 212 })) 213 g.Expect(routes[0].Action.(*envoyroute.Route_Route).Route.ClusterNotFoundResponseCode). 214 To(Equal(envoyroute.RouteAction_INTERNAL_SERVER_ERROR)) 215 g.Expect(routes[1].Match.PathSpecifier).To(Equal(&envoyroute.RouteMatch_Prefix{ 216 Prefix: "/", 217 })) 218 g.Expect(routes[1].Action.(*envoyroute.Route_Route).Route.ClusterNotFoundResponseCode). 219 To(Equal(envoyroute.RouteAction_INTERNAL_SERVER_ERROR)) 220 }) 221 222 t.Run("for virtual service with top level catch all route", func(t *testing.T) { 223 g := NewWithT(t) 224 cg := core.NewConfigGenTest(t, core.TestOptions{}) 225 226 routes, err := route.BuildHTTPRoutesForVirtualService(node(cg), virtualServiceWithCatchAllRouteWeightedDestination, 227 serviceRegistry, nil, 8080, gatewayNames, route.RouteOptions{}) 228 xdstest.ValidateRoutes(t, routes) 229 230 g.Expect(err).NotTo(HaveOccurred()) 231 g.Expect(len(routes)).To(Equal(1)) 232 }) 233 234 t.Run("for virtual service with multi prefix catch all route", func(t *testing.T) { 235 g := NewWithT(t) 236 cg := core.NewConfigGenTest(t, core.TestOptions{}) 237 238 routes, err := route.BuildHTTPRoutesForVirtualService(node(cg), virtualServiceWithCatchAllMultiPrefixRoute, 239 serviceRegistry, nil, 8080, gatewayNames, route.RouteOptions{}) 240 xdstest.ValidateRoutes(t, routes) 241 242 g.Expect(err).NotTo(HaveOccurred()) 243 g.Expect(len(routes)).To(Equal(1)) 244 }) 245 246 t.Run("for virtual service with regex matching on URI", func(t *testing.T) { 247 g := NewWithT(t) 248 cg := core.NewConfigGenTest(t, core.TestOptions{}) 249 250 routes, err := route.BuildHTTPRoutesForVirtualService(node(cg), virtualServiceWithRegexMatchingOnURI, 251 serviceRegistry, nil, 8080, gatewayNames, route.RouteOptions{}) 252 xdstest.ValidateRoutes(t, routes) 253 g.Expect(err).NotTo(HaveOccurred()) 254 g.Expect(len(routes)).To(Equal(1)) 255 g.Expect(routes[0].GetMatch().GetSafeRegex().GetRegex()).To(Equal("\\/(.?)\\/status")) 256 }) 257 258 t.Run("for virtual service with stat_prefix set for a match on URI", func(t *testing.T) { 259 g := NewWithT(t) 260 cg := core.NewConfigGenTest(t, core.TestOptions{}) 261 262 routes, err := route.BuildHTTPRoutesForVirtualService(node(cg), virtualServiceWithStatPrefix, 263 serviceRegistry, nil, 8080, gatewayNames, route.RouteOptions{}) 264 xdstest.ValidateRoutes(t, routes) 265 g.Expect(err).NotTo(HaveOccurred()) 266 g.Expect(len(routes)).To(Equal(3)) 267 g.Expect(routes[0].GetMatch().GetPrefix()).To(Equal("/foo")) 268 g.Expect(routes[0].StatPrefix).To(Equal("foo")) 269 g.Expect(routes[1].GetMatch().GetPrefix()).To(Equal("/baz")) 270 g.Expect(routes[1].StatPrefix).To(Equal("")) 271 g.Expect(routes[2].GetMatch().GetPrefix()).To(Equal("/bar")) 272 g.Expect(routes[2].StatPrefix).To(Equal("")) 273 g.Expect(len(routes[0].GetRoute().GetRetryPolicy().RetryHostPredicate)).To(Equal(1)) 274 }) 275 276 t.Run("for virtual service with exact matching on JWT claims", func(t *testing.T) { 277 g := NewWithT(t) 278 cg := core.NewConfigGenTest(t, core.TestOptions{}) 279 280 routes, err := route.BuildHTTPRoutesForVirtualService(node(cg), virtualServiceWithExactMatchingOnHeaderForJWTClaims, 281 serviceRegistry, nil, 8080, gatewayNames, route.RouteOptions{}) 282 xdstest.ValidateRoutes(t, routes) 283 g.Expect(err).NotTo(HaveOccurred()) 284 g.Expect(len(routes)).To(Equal(1)) 285 g.Expect(len(routes[0].GetMatch().GetHeaders())).To(Equal(0)) 286 g.Expect(routes[0].GetMatch().GetDynamicMetadata()[0].GetFilter()).To(Equal("istio_authn")) 287 g.Expect(routes[0].GetMatch().GetDynamicMetadata()[0].GetInvert()).To(BeFalse()) 288 g.Expect(routes[0].GetMatch().GetDynamicMetadata()[1].GetFilter()).To(Equal("istio_authn")) 289 g.Expect(routes[0].GetMatch().GetDynamicMetadata()[1].GetInvert()).To(BeTrue()) 290 }) 291 292 t.Run("for virtual service with exact matching on JWT claims with extended", func(t *testing.T) { 293 g := NewWithT(t) 294 cg := core.NewConfigGenTest(t, core.TestOptions{}) 295 296 routes, err := route.BuildHTTPRoutesForVirtualService(nodeWithExtended(cg), virtualServiceWithExactMatchingOnHeaderForJWTClaims, 297 serviceRegistry, nil, 8080, gatewayNames, route.RouteOptions{}) 298 xdstest.ValidateRoutes(t, routes) 299 g.Expect(err).NotTo(HaveOccurred()) 300 g.Expect(len(routes)).To(Equal(1)) 301 g.Expect(len(routes[0].GetMatch().GetHeaders())).To(Equal(0)) 302 g.Expect(routes[0].GetMatch().GetDynamicMetadata()[0].GetFilter()).To(Equal(filters.EnvoyJwtFilterName)) 303 g.Expect(routes[0].GetMatch().GetDynamicMetadata()[0].GetInvert()).To(BeFalse()) 304 g.Expect(routes[0].GetMatch().GetDynamicMetadata()[1].GetFilter()).To(Equal(filters.EnvoyJwtFilterName)) 305 g.Expect(routes[0].GetMatch().GetDynamicMetadata()[1].GetInvert()).To(BeTrue()) 306 }) 307 308 t.Run("for virtual service with regex matching on header", func(t *testing.T) { 309 g := NewWithT(t) 310 cg := core.NewConfigGenTest(t, core.TestOptions{}) 311 312 routes, err := route.BuildHTTPRoutesForVirtualService(node(cg), virtualServiceWithRegexMatchingOnHeader, 313 serviceRegistry, nil, 8080, gatewayNames, route.RouteOptions{}) 314 xdstest.ValidateRoutes(t, routes) 315 g.Expect(err).NotTo(HaveOccurred()) 316 g.Expect(len(routes)).To(Equal(1)) 317 g.Expect(routes[0].GetMatch().GetHeaders()[0].GetStringMatch().GetSafeRegex().GetRegex()).To(Equal("Bearer .+?\\..+?\\..+?")) 318 }) 319 320 t.Run("for virtual service with regex matching on without_header", func(t *testing.T) { 321 g := NewWithT(t) 322 cg := core.NewConfigGenTest(t, core.TestOptions{}) 323 324 routes, err := route.BuildHTTPRoutesForVirtualService(node(cg), virtualServiceWithRegexMatchingOnWithoutHeader, 325 serviceRegistry, nil, 8080, gatewayNames, route.RouteOptions{}) 326 xdstest.ValidateRoutes(t, routes) 327 g.Expect(err).NotTo(HaveOccurred()) 328 g.Expect(len(routes)).To(Equal(1)) 329 g.Expect(routes[0].GetMatch().GetHeaders()[0].GetStringMatch().GetSafeRegex().GetRegex()).To(Equal("BAR .+?\\..+?\\..+?")) 330 g.Expect(routes[0].GetMatch().GetHeaders()[0].GetInvertMatch()).To(Equal(true)) 331 g.Expect(routes[0].GetMatch().GetHeaders()[0].GetTreatMissingHeaderAsEmpty()).To(Equal(true)) 332 }) 333 334 t.Run("for virtual service with presence matching on header", func(t *testing.T) { 335 g := NewWithT(t) 336 cg := core.NewConfigGenTest(t, core.TestOptions{}) 337 338 routes, err := route.BuildHTTPRoutesForVirtualService(node(cg), virtualServiceWithPresentMatchingOnHeader, 339 serviceRegistry, nil, 8080, gatewayNames, route.RouteOptions{}) 340 g.Expect(err).NotTo(HaveOccurred()) 341 xdstest.ValidateRoutes(t, routes) 342 g.Expect(len(routes)).To(Equal(1)) 343 g.Expect(routes[0].GetMatch().GetHeaders()[0].GetName()).To(Equal("FOO-HEADER")) 344 g.Expect(routes[0].GetMatch().GetHeaders()[0].GetPresentMatch()).To(Equal(true)) 345 g.Expect(routes[0].GetMatch().GetHeaders()[0].GetInvertMatch()).To(Equal(false)) 346 347 routes, err = route.BuildHTTPRoutesForVirtualService(node(cg), virtualServiceWithPresentMatchingOnHeader2, 348 serviceRegistry, nil, 8080, gatewayNames, route.RouteOptions{}) 349 g.Expect(err).NotTo(HaveOccurred()) 350 xdstest.ValidateRoutes(t, routes) 351 g.Expect(len(routes)).To(Equal(1)) 352 g.Expect(routes[0].GetMatch().GetHeaders()[0].GetName()).To(Equal("FOO-HEADER")) 353 g.Expect(routes[0].GetMatch().GetHeaders()[0].GetPresentMatch()).To(Equal(true)) 354 g.Expect(routes[0].GetMatch().GetHeaders()[0].GetInvertMatch()).To(Equal(false)) 355 }) 356 357 t.Run("for virtual service with presence matching on header and without_header", func(t *testing.T) { 358 g := NewWithT(t) 359 cg := core.NewConfigGenTest(t, core.TestOptions{}) 360 361 routes, err := route.BuildHTTPRoutesForVirtualService(node(cg), virtualServiceWithPresentMatchingOnWithoutHeader, 362 serviceRegistry, nil, 8080, gatewayNames, route.RouteOptions{}) 363 g.Expect(err).NotTo(HaveOccurred()) 364 xdstest.ValidateRoutes(t, routes) 365 g.Expect(len(routes)).To(Equal(1)) 366 g.Expect(routes[0].GetMatch().GetHeaders()[0].GetName()).To(Equal("FOO-HEADER")) 367 g.Expect(routes[0].GetMatch().GetHeaders()[0].GetPresentMatch()).To(Equal(true)) 368 g.Expect(routes[0].GetMatch().GetHeaders()[0].GetInvertMatch()).To(Equal(true)) 369 }) 370 371 t.Run("for virtual service with regex matching for all cases on header", func(t *testing.T) { 372 cset := createVirtualServiceWithRegexMatchingForAllCasesOnHeader() 373 374 for _, c := range cset { 375 g := NewWithT(t) 376 cg := core.NewConfigGenTest(t, core.TestOptions{}) 377 routes, err := route.BuildHTTPRoutesForVirtualService(node(cg), *c, serviceRegistry, nil, 378 8080, gatewayNames, route.RouteOptions{}) 379 xdstest.ValidateRoutes(t, routes) 380 g.Expect(err).NotTo(HaveOccurred()) 381 g.Expect(len(routes)).To(Equal(1)) 382 g.Expect(routes[0].GetMatch().GetHeaders()[0].GetName()).To(Equal("FOO-HEADER")) 383 g.Expect(routes[0].GetMatch().GetHeaders()[0].GetPresentMatch()).To(Equal(true)) 384 g.Expect(routes[0].GetMatch().GetHeaders()[0].GetInvertMatch()).To(Equal(false)) 385 g.Expect(routes[0].GetMatch().GetHeaders()[0].GetTreatMissingHeaderAsEmpty()).To(Equal(false)) 386 } 387 }) 388 389 t.Run("for virtual service with exact matching on query parameter", func(t *testing.T) { 390 g := NewWithT(t) 391 cg := core.NewConfigGenTest(t, core.TestOptions{}) 392 393 routes, err := route.BuildHTTPRoutesForVirtualService(node(cg), virtualServiceWithExactMatchingOnQueryParameter, 394 serviceRegistry, nil, 8080, gatewayNames, route.RouteOptions{}) 395 xdstest.ValidateRoutes(t, routes) 396 g.Expect(err).NotTo(HaveOccurred()) 397 g.Expect(len(routes)).To(Equal(1)) 398 g.Expect(routes[0].GetMatch().GetQueryParameters()[0].GetStringMatch().GetExact()).To(Equal("foo")) 399 }) 400 401 t.Run("for virtual service with prefix matching on query parameter", func(t *testing.T) { 402 g := NewWithT(t) 403 cg := core.NewConfigGenTest(t, core.TestOptions{}) 404 405 routes, err := route.BuildHTTPRoutesForVirtualService(node(cg), virtualServiceWithPrefixMatchingOnQueryParameter, 406 serviceRegistry, nil, 8080, gatewayNames, route.RouteOptions{}) 407 xdstest.ValidateRoutes(t, routes) 408 g.Expect(err).NotTo(HaveOccurred()) 409 g.Expect(len(routes)).To(Equal(1)) 410 g.Expect(routes[0].GetMatch().GetQueryParameters()[0].GetStringMatch().GetPrefix()).To(Equal("foo-")) 411 }) 412 413 t.Run("for virtual service with regex matching on query parameter", func(t *testing.T) { 414 g := NewWithT(t) 415 cg := core.NewConfigGenTest(t, core.TestOptions{}) 416 417 routes, err := route.BuildHTTPRoutesForVirtualService(node(cg), virtualServiceWithRegexMatchingOnQueryParameter, 418 serviceRegistry, nil, 8080, gatewayNames, route.RouteOptions{}) 419 xdstest.ValidateRoutes(t, routes) 420 g.Expect(err).NotTo(HaveOccurred()) 421 g.Expect(len(routes)).To(Equal(1)) 422 g.Expect(routes[0].GetMatch().GetQueryParameters()[0].GetStringMatch().GetSafeRegex().GetRegex()).To(Equal("BAR .+?\\..+?\\..+?")) 423 }) 424 425 t.Run("for virtual service with regex matching for all cases on query parameter", func(t *testing.T) { 426 cset := createVirtualServiceWithRegexMatchingForAllCasesOnQueryParameter() 427 428 for _, c := range cset { 429 g := NewWithT(t) 430 cg := core.NewConfigGenTest(t, core.TestOptions{}) 431 routes, err := route.BuildHTTPRoutesForVirtualService(node(cg), *c, serviceRegistry, nil, 432 8080, gatewayNames, route.RouteOptions{}) 433 xdstest.ValidateRoutes(t, routes) 434 g.Expect(err).NotTo(HaveOccurred()) 435 g.Expect(len(routes)).To(Equal(1)) 436 g.Expect(routes[0].GetMatch().GetQueryParameters()[0].GetName()).To(Equal("token")) 437 g.Expect(routes[0].GetMatch().GetQueryParameters()[0].GetPresentMatch()).To(Equal(true)) 438 } 439 }) 440 441 t.Run("for virtual service with source namespace matching", func(t *testing.T) { 442 g := NewWithT(t) 443 cg := core.NewConfigGenTest(t, core.TestOptions{}) 444 445 fooNode := cg.SetupProxy(&model.Proxy{ 446 ConfigNamespace: "foo", 447 }) 448 449 routes, err := route.BuildHTTPRoutesForVirtualService(fooNode, virtualServiceMatchingOnSourceNamespace, 450 serviceRegistry, nil, 8080, gatewayNames, route.RouteOptions{}) 451 xdstest.ValidateRoutes(t, routes) 452 g.Expect(err).NotTo(HaveOccurred()) 453 g.Expect(len(routes)).To(Equal(1)) 454 g.Expect(routes[0].GetName()).To(Equal("foo")) 455 456 barNode := cg.SetupProxy(&model.Proxy{ 457 ConfigNamespace: "bar", 458 }) 459 460 routes, err = route.BuildHTTPRoutesForVirtualService(barNode, virtualServiceMatchingOnSourceNamespace, 461 serviceRegistry, nil, 8080, gatewayNames, route.RouteOptions{}) 462 g.Expect(err).NotTo(HaveOccurred()) 463 g.Expect(len(routes)).To(Equal(1)) 464 g.Expect(routes[0].GetName()).To(Equal("bar")) 465 }) 466 467 t.Run("for virtual service with ring hash", func(t *testing.T) { 468 g := NewWithT(t) 469 ttl := durationpb.Duration{Nanos: 100} 470 cg := core.NewConfigGenTest(t, core.TestOptions{ 471 Services: exampleService, 472 Configs: []config.Config{ 473 { 474 Meta: config.Meta{ 475 GroupVersionKind: gvk.DestinationRule, 476 Name: "acme", 477 Namespace: "istio-system", 478 }, 479 Spec: &networking.DestinationRule{ 480 Host: "*.example.org", 481 TrafficPolicy: &networking.TrafficPolicy{ 482 LoadBalancer: &networking.LoadBalancerSettings{ 483 LbPolicy: &networking.LoadBalancerSettings_ConsistentHash{ 484 ConsistentHash: &networking.LoadBalancerSettings_ConsistentHashLB{ 485 HashKey: &networking.LoadBalancerSettings_ConsistentHashLB_HttpCookie{ 486 HttpCookie: &networking.LoadBalancerSettings_ConsistentHashLB_HTTPCookie{ 487 Name: "hash-cookie", 488 Ttl: &ttl, 489 }, 490 }, 491 }, 492 }, 493 }, 494 }, 495 }, 496 }, 497 }, 498 }) 499 500 proxy := node(cg) 501 hashByDestination := route.GetConsistentHashForVirtualService(cg.PushContext(), proxy, virtualServicePlain) 502 routes, err := route.BuildHTTPRoutesForVirtualService(proxy, virtualServicePlain, serviceRegistry, 503 hashByDestination, 8080, gatewayNames, route.RouteOptions{}) 504 xdstest.ValidateRoutes(t, routes) 505 g.Expect(err).NotTo(HaveOccurred()) 506 g.Expect(len(routes)).To(Equal(1)) 507 508 hashPolicy := &envoyroute.RouteAction_HashPolicy{ 509 PolicySpecifier: &envoyroute.RouteAction_HashPolicy_Cookie_{ 510 Cookie: &envoyroute.RouteAction_HashPolicy_Cookie{ 511 Name: "hash-cookie", 512 Ttl: &ttl, 513 }, 514 }, 515 } 516 g.Expect(routes[0].GetRoute().GetHashPolicy()).To(ConsistOf(hashPolicy)) 517 g.Expect(len(routes[0].GetRoute().GetRetryPolicy().RetryHostPredicate)).To(Equal(0)) 518 }) 519 520 t.Run("for virtual service with query param based ring hash", func(t *testing.T) { 521 g := NewWithT(t) 522 cg := core.NewConfigGenTest(t, core.TestOptions{ 523 Services: exampleService, 524 Configs: []config.Config{ 525 { 526 Meta: config.Meta{ 527 GroupVersionKind: gvk.DestinationRule, 528 Name: "acme", 529 Namespace: "istio-system", 530 }, 531 Spec: &networking.DestinationRule{ 532 Host: "*.example.org", 533 TrafficPolicy: &networking.TrafficPolicy{ 534 LoadBalancer: &networking.LoadBalancerSettings{ 535 LbPolicy: &networking.LoadBalancerSettings_ConsistentHash{ 536 ConsistentHash: &networking.LoadBalancerSettings_ConsistentHashLB{ 537 HashKey: &networking.LoadBalancerSettings_ConsistentHashLB_HttpQueryParameterName{ 538 HttpQueryParameterName: "query", 539 }, 540 }, 541 }, 542 }, 543 }, 544 }, 545 }, 546 }, 547 }) 548 549 proxy := node(cg) 550 hashByDestination := route.GetConsistentHashForVirtualService(cg.PushContext(), proxy, virtualServicePlain) 551 routes, err := route.BuildHTTPRoutesForVirtualService(proxy, virtualServicePlain, serviceRegistry, 552 hashByDestination, 8080, gatewayNames, route.RouteOptions{}) 553 xdstest.ValidateRoutes(t, routes) 554 g.Expect(err).NotTo(HaveOccurred()) 555 g.Expect(len(routes)).To(Equal(1)) 556 557 hashPolicy := &envoyroute.RouteAction_HashPolicy{ 558 PolicySpecifier: &envoyroute.RouteAction_HashPolicy_QueryParameter_{ 559 QueryParameter: &envoyroute.RouteAction_HashPolicy_QueryParameter{ 560 Name: "query", 561 }, 562 }, 563 } 564 g.Expect(routes[0].GetRoute().GetHashPolicy()).To(ConsistOf(hashPolicy)) 565 g.Expect(len(routes[0].GetRoute().GetRetryPolicy().RetryHostPredicate)).To(Equal(0)) 566 }) 567 568 t.Run("for virtual service with subsets with ring hash", func(t *testing.T) { 569 g := NewWithT(t) 570 virtualService := config.Config{ 571 Meta: config.Meta{ 572 GroupVersionKind: gvk.VirtualService, 573 Name: "acme", 574 }, 575 Spec: virtualServiceWithSubset, 576 } 577 cg := core.NewConfigGenTest(t, core.TestOptions{ 578 Services: exampleService, 579 Configs: []config.Config{ 580 virtualService, 581 { 582 Meta: config.Meta{ 583 GroupVersionKind: gvk.DestinationRule, 584 Name: "acme", 585 Namespace: "istio-system", 586 }, 587 Spec: &networking.DestinationRule{ 588 Host: "*.example.org", 589 Subsets: []*networking.Subset{networkingSubset}, 590 }, 591 }, 592 }, 593 }) 594 595 proxy := node(cg) 596 hashByDestination := route.GetConsistentHashForVirtualService(cg.PushContext(), proxy, virtualService) 597 routes, err := route.BuildHTTPRoutesForVirtualService(proxy, virtualService, serviceRegistry, 598 hashByDestination, 8080, gatewayNames, route.RouteOptions{}) 599 xdstest.ValidateRoutes(t, routes) 600 g.Expect(err).NotTo(HaveOccurred()) 601 g.Expect(len(routes)).To(Equal(1)) 602 603 hashPolicy := &envoyroute.RouteAction_HashPolicy{ 604 PolicySpecifier: &envoyroute.RouteAction_HashPolicy_Cookie_{ 605 Cookie: &envoyroute.RouteAction_HashPolicy_Cookie{ 606 Name: "other-cookie", 607 Ttl: nil, 608 }, 609 }, 610 } 611 g.Expect(routes[0].GetRoute().GetHashPolicy()).To(ConsistOf(hashPolicy)) 612 }) 613 614 t.Run("for virtual service with subsets with port level settings with ring hash", func(t *testing.T) { 615 g := NewWithT(t) 616 virtualService := config.Config{ 617 Meta: config.Meta{ 618 GroupVersionKind: gvk.VirtualService, 619 Name: "acme", 620 }, 621 Spec: virtualServiceWithSubsetWithPortLevelSettings, 622 } 623 cg := core.NewConfigGenTest(t, core.TestOptions{ 624 Services: exampleService, 625 Configs: []config.Config{ 626 virtualService, 627 { 628 Meta: config.Meta{ 629 GroupVersionKind: gvk.DestinationRule, 630 Name: "acme", 631 Namespace: "istio-system", 632 }, 633 Spec: portLevelDestinationRuleWithSubsetPolicy, 634 }, 635 }, 636 }) 637 638 proxy := node(cg) 639 hashByDestination := route.GetConsistentHashForVirtualService(cg.PushContext(), proxy, virtualService) 640 routes, err := route.BuildHTTPRoutesForVirtualService(proxy, virtualService, serviceRegistry, 641 hashByDestination, 8080, gatewayNames, route.RouteOptions{}) 642 xdstest.ValidateRoutes(t, routes) 643 g.Expect(err).NotTo(HaveOccurred()) 644 g.Expect(len(routes)).To(Equal(1)) 645 646 hashPolicy := &envoyroute.RouteAction_HashPolicy{ 647 PolicySpecifier: &envoyroute.RouteAction_HashPolicy_Cookie_{ 648 Cookie: &envoyroute.RouteAction_HashPolicy_Cookie{ 649 Name: "port-level-settings-cookie", 650 Ttl: nil, 651 }, 652 }, 653 } 654 g.Expect(routes[0].GetRoute().GetHashPolicy()).To(ConsistOf(hashPolicy)) 655 }) 656 657 t.Run("for virtual service with subsets and top level traffic policy with ring hash", func(t *testing.T) { 658 g := NewWithT(t) 659 660 virtualService := config.Config{ 661 Meta: config.Meta{ 662 GroupVersionKind: gvk.VirtualService, 663 Name: "acme", 664 }, 665 Spec: virtualServiceWithSubset, 666 } 667 668 cnfg := config.Config{ 669 Meta: config.Meta{ 670 GroupVersionKind: gvk.DestinationRule, 671 Name: "acme", 672 Namespace: "istio-system", 673 }, 674 } 675 rule := networkingDestinationRule 676 rule.Subsets = []*networking.Subset{networkingSubset} 677 cnfg.Spec = networkingDestinationRule 678 679 cg := core.NewConfigGenTest(t, core.TestOptions{ 680 Services: exampleService, 681 Configs: []config.Config{cnfg, virtualService}, 682 }) 683 684 proxy := node(cg) 685 hashByDestination := route.GetConsistentHashForVirtualService(cg.PushContext(), proxy, virtualService) 686 routes, err := route.BuildHTTPRoutesForVirtualService(proxy, virtualService, serviceRegistry, 687 hashByDestination, 8080, gatewayNames, route.RouteOptions{}) 688 xdstest.ValidateRoutes(t, routes) 689 g.Expect(err).NotTo(HaveOccurred()) 690 g.Expect(len(routes)).To(Equal(1)) 691 692 hashPolicy := &envoyroute.RouteAction_HashPolicy{ 693 PolicySpecifier: &envoyroute.RouteAction_HashPolicy_Cookie_{ 694 Cookie: &envoyroute.RouteAction_HashPolicy_Cookie{ 695 Name: "other-cookie", 696 Ttl: nil, 697 }, 698 }, 699 } 700 g.Expect(routes[0].GetRoute().GetHashPolicy()).To(ConsistOf(hashPolicy)) 701 }) 702 703 t.Run("port selector based traffic policy", func(t *testing.T) { 704 g := NewWithT(t) 705 706 cg := core.NewConfigGenTest(t, core.TestOptions{ 707 Services: exampleService, 708 Configs: []config.Config{{ 709 Meta: config.Meta{ 710 GroupVersionKind: gvk.DestinationRule, 711 Name: "acme", 712 Namespace: "istio-system", 713 }, 714 Spec: portLevelDestinationRule, 715 }}, 716 }) 717 718 proxy := node(cg) 719 gatewayNames := sets.New("some-gateway") 720 hashByDestination := route.GetConsistentHashForVirtualService(cg.PushContext(), proxy, virtualServicePlain) 721 routes, err := route.BuildHTTPRoutesForVirtualService(proxy, virtualServicePlain, serviceRegistry, 722 hashByDestination, 8080, gatewayNames, route.RouteOptions{}) 723 xdstest.ValidateRoutes(t, routes) 724 g.Expect(err).NotTo(HaveOccurred()) 725 g.Expect(len(routes)).To(Equal(1)) 726 727 hashPolicy := &envoyroute.RouteAction_HashPolicy{ 728 PolicySpecifier: &envoyroute.RouteAction_HashPolicy_Cookie_{ 729 Cookie: &envoyroute.RouteAction_HashPolicy_Cookie{ 730 Name: "hash-cookie", 731 Ttl: nil, 732 }, 733 }, 734 } 735 g.Expect(routes[0].GetRoute().GetHashPolicy()).To(ConsistOf(hashPolicy)) 736 }) 737 738 t.Run("for header operations for single cluster", func(t *testing.T) { 739 g := NewWithT(t) 740 cg := core.NewConfigGenTest(t, core.TestOptions{}) 741 742 routes, err := route.BuildHTTPRoutesForVirtualService(node(cg), virtualServiceWithHeaderOperationsForSingleCluster, 743 serviceRegistry, nil, 8080, gatewayNames, route.RouteOptions{}) 744 xdstest.ValidateRoutes(t, routes) 745 g.Expect(err).NotTo(HaveOccurred()) 746 g.Expect(len(routes)).To(Equal(1)) 747 748 r := routes[0] 749 g.Expect(len(r.RequestHeadersToAdd)).To(Equal(4)) 750 g.Expect(len(r.ResponseHeadersToAdd)).To(Equal(4)) 751 g.Expect(len(r.RequestHeadersToRemove)).To(Equal(2)) 752 g.Expect(len(r.ResponseHeadersToRemove)).To(Equal(2)) 753 754 g.Expect(r.RequestHeadersToAdd).To(Equal([]*envoycore.HeaderValueOption{ 755 { 756 Header: &envoycore.HeaderValue{ 757 Key: "x-req-set", 758 Value: "v1", 759 }, 760 AppendAction: envoycore.HeaderValueOption_OVERWRITE_IF_EXISTS_OR_ADD, 761 }, 762 { 763 Header: &envoycore.HeaderValue{ 764 Key: "x-req-add", 765 Value: "v2", 766 }, 767 AppendAction: envoycore.HeaderValueOption_APPEND_IF_EXISTS_OR_ADD, 768 }, 769 { 770 Header: &envoycore.HeaderValue{ 771 Key: "x-route-req-set", 772 Value: "v1", 773 }, 774 AppendAction: envoycore.HeaderValueOption_OVERWRITE_IF_EXISTS_OR_ADD, 775 }, 776 { 777 Header: &envoycore.HeaderValue{ 778 Key: "x-route-req-add", 779 Value: "v2", 780 }, 781 AppendAction: envoycore.HeaderValueOption_APPEND_IF_EXISTS_OR_ADD, 782 }, 783 })) 784 g.Expect(r.RequestHeadersToRemove).To(Equal([]string{"x-req-remove", "x-route-req-remove"})) 785 786 g.Expect(r.ResponseHeadersToAdd).To(Equal([]*envoycore.HeaderValueOption{ 787 { 788 Header: &envoycore.HeaderValue{ 789 Key: "x-resp-set", 790 Value: "v1", 791 }, 792 AppendAction: envoycore.HeaderValueOption_OVERWRITE_IF_EXISTS_OR_ADD, 793 }, 794 { 795 Header: &envoycore.HeaderValue{ 796 Key: "x-resp-add", 797 Value: "v2", 798 }, 799 AppendAction: envoycore.HeaderValueOption_APPEND_IF_EXISTS_OR_ADD, 800 }, 801 { 802 Header: &envoycore.HeaderValue{ 803 Key: "x-route-resp-set", 804 Value: "v1", 805 }, 806 AppendAction: envoycore.HeaderValueOption_OVERWRITE_IF_EXISTS_OR_ADD, 807 }, 808 { 809 Header: &envoycore.HeaderValue{ 810 Key: "x-route-resp-add", 811 Value: "v2", 812 }, 813 AppendAction: envoycore.HeaderValueOption_APPEND_IF_EXISTS_OR_ADD, 814 }, 815 })) 816 g.Expect(r.ResponseHeadersToRemove).To(Equal([]string{"x-resp-remove", "x-route-resp-remove"})) 817 818 routeAction, ok := r.GetAction().(*envoyroute.Route_Route) 819 g.Expect(ok).NotTo(BeFalse()) 820 g.Expect(routeAction.Route.GetHostRewriteLiteral()).To(Equal("foo.extsvc.com")) 821 }) 822 823 t.Run("for header operations for weighted cluster", func(t *testing.T) { 824 g := NewWithT(t) 825 cg := core.NewConfigGenTest(t, core.TestOptions{}) 826 827 routes, err := route.BuildHTTPRoutesForVirtualService(node(cg), virtualServiceWithHeaderOperationsForWeightedCluster, 828 serviceRegistry, nil, 8080, gatewayNames, route.RouteOptions{}) 829 xdstest.ValidateRoutes(t, routes) 830 g.Expect(err).NotTo(HaveOccurred()) 831 g.Expect(len(routes)).To(Equal(1)) 832 833 r := routes[0] 834 routeAction, ok := r.GetAction().(*envoyroute.Route_Route) 835 g.Expect(ok).NotTo(BeFalse()) 836 837 weightedCluster := routeAction.Route.GetWeightedClusters() 838 g.Expect(weightedCluster).NotTo(BeNil()) 839 g.Expect(len(weightedCluster.GetClusters())).To(Equal(2)) 840 841 expectResults := []struct { 842 reqAdd []*envoycore.HeaderValueOption 843 reqRemove []string 844 respAdd []*envoycore.HeaderValueOption 845 respRemove []string 846 authority string 847 }{ 848 { 849 reqAdd: []*envoycore.HeaderValueOption{ 850 { 851 Header: &envoycore.HeaderValue{ 852 Key: "x-route-req-set-blue", 853 Value: "v1", 854 }, 855 AppendAction: envoycore.HeaderValueOption_OVERWRITE_IF_EXISTS_OR_ADD, 856 }, 857 { 858 Header: &envoycore.HeaderValue{ 859 Key: "x-route-req-add-blue", 860 Value: "v2", 861 }, 862 AppendAction: envoycore.HeaderValueOption_APPEND_IF_EXISTS_OR_ADD, 863 }, 864 }, 865 reqRemove: []string{"x-route-req-remove-blue"}, 866 respAdd: []*envoycore.HeaderValueOption{ 867 { 868 Header: &envoycore.HeaderValue{ 869 Key: "x-route-resp-set-blue", 870 Value: "v1", 871 }, 872 AppendAction: envoycore.HeaderValueOption_OVERWRITE_IF_EXISTS_OR_ADD, 873 }, 874 { 875 Header: &envoycore.HeaderValue{ 876 Key: "x-route-resp-add-blue", 877 Value: "v2", 878 }, 879 AppendAction: envoycore.HeaderValueOption_APPEND_IF_EXISTS_OR_ADD, 880 }, 881 }, 882 respRemove: []string{"x-route-resp-remove-blue"}, 883 authority: "blue.foo.extsvc.com", 884 }, 885 { 886 reqAdd: []*envoycore.HeaderValueOption{ 887 { 888 Header: &envoycore.HeaderValue{ 889 Key: "x-route-req-set-green", 890 Value: "v1", 891 }, 892 AppendAction: envoycore.HeaderValueOption_OVERWRITE_IF_EXISTS_OR_ADD, 893 }, 894 { 895 Header: &envoycore.HeaderValue{ 896 Key: "x-route-req-add-green", 897 Value: "v2", 898 }, 899 AppendAction: envoycore.HeaderValueOption_APPEND_IF_EXISTS_OR_ADD, 900 }, 901 }, 902 reqRemove: []string{"x-route-req-remove-green"}, 903 respAdd: []*envoycore.HeaderValueOption{ 904 { 905 Header: &envoycore.HeaderValue{ 906 Key: "x-route-resp-set-green", 907 Value: "v1", 908 }, 909 AppendAction: envoycore.HeaderValueOption_OVERWRITE_IF_EXISTS_OR_ADD, 910 }, 911 { 912 Header: &envoycore.HeaderValue{ 913 Key: "x-route-resp-add-green", 914 Value: "v2", 915 }, 916 AppendAction: envoycore.HeaderValueOption_APPEND_IF_EXISTS_OR_ADD, 917 }, 918 }, 919 respRemove: []string{"x-route-resp-remove-green"}, 920 authority: "green.foo.extsvc.com", 921 }, 922 } 923 924 for i, expectResult := range expectResults { 925 cluster := weightedCluster.GetClusters()[i] 926 g.Expect(cluster.RequestHeadersToAdd).To(Equal(expectResult.reqAdd)) 927 g.Expect(cluster.RequestHeadersToRemove).To(Equal(expectResult.reqRemove)) 928 g.Expect(cluster.ResponseHeadersToAdd).To(Equal(expectResult.respAdd)) 929 g.Expect(cluster.RequestHeadersToRemove).To(Equal(expectResult.reqRemove)) 930 g.Expect(cluster.GetHostRewriteLiteral()).To(Equal(expectResult.authority)) 931 } 932 }) 933 934 t.Run("for redirect code", func(t *testing.T) { 935 g := NewWithT(t) 936 cg := core.NewConfigGenTest(t, core.TestOptions{}) 937 938 routes, err := route.BuildHTTPRoutesForVirtualService(node(cg), virtualServiceWithRedirect, serviceRegistry, 939 nil, 8080, gatewayNames, route.RouteOptions{}) 940 xdstest.ValidateRoutes(t, routes) 941 g.Expect(err).NotTo(HaveOccurred()) 942 g.Expect(len(routes)).To(Equal(1)) 943 944 redirectAction, ok := routes[0].Action.(*envoyroute.Route_Redirect) 945 g.Expect(ok).NotTo(BeFalse()) 946 g.Expect(redirectAction.Redirect.ResponseCode).To(Equal(envoyroute.RedirectAction_PERMANENT_REDIRECT)) 947 }) 948 949 t.Run("for path prefix redirect", func(t *testing.T) { 950 g := NewWithT(t) 951 cg := core.NewConfigGenTest(t, core.TestOptions{}) 952 953 routes, err := route.BuildHTTPRoutesForVirtualService(node(cg), virtualServiceWithRedirectPathPrefix, serviceRegistry, 954 nil, 8080, gatewayNames, route.RouteOptions{}) 955 xdstest.ValidateRoutes(t, routes) 956 g.Expect(err).NotTo(HaveOccurred()) 957 g.Expect(len(routes)).To(Equal(1)) 958 959 redirectAction, ok := routes[0].Action.(*envoyroute.Route_Redirect) 960 g.Expect(ok).NotTo(BeFalse()) 961 g.Expect(redirectAction.Redirect.PathRewriteSpecifier).To(Equal(&envoyroute.RedirectAction_PrefixRewrite{ 962 PrefixRewrite: "/replace-prefix", 963 })) 964 }) 965 966 t.Run("for host rewrite", func(t *testing.T) { 967 g := NewWithT(t) 968 cg := core.NewConfigGenTest(t, core.TestOptions{}) 969 970 routes, err := route.BuildHTTPRoutesForVirtualService(node(cg), virtualServiceWithRewriteHost, serviceRegistry, 971 nil, 8080, gatewayNames, route.RouteOptions{}) 972 xdstest.ValidateRoutes(t, routes) 973 g.Expect(err).NotTo(HaveOccurred()) 974 g.Expect(len(routes)).To(Equal(1)) 975 976 routeAction, ok := routes[0].Action.(*envoyroute.Route_Route) 977 g.Expect(ok).NotTo(BeFalse()) 978 g.Expect(routeAction.Route.HostRewriteSpecifier).To(Equal(&envoyroute.RouteAction_HostRewriteLiteral{ 979 HostRewriteLiteral: "bar.example.org", 980 })) 981 }) 982 983 t.Run("for full path rewrite", func(t *testing.T) { 984 g := NewWithT(t) 985 cg := core.NewConfigGenTest(t, core.TestOptions{}) 986 987 routes, err := route.BuildHTTPRoutesForVirtualService(node(cg), virtualServiceWithRewriteFullPath, serviceRegistry, 988 nil, 8080, gatewayNames, route.RouteOptions{}) 989 xdstest.ValidateRoutes(t, routes) 990 g.Expect(err).NotTo(HaveOccurred()) 991 g.Expect(len(routes)).To(Equal(1)) 992 993 routeAction, ok := routes[0].Action.(*envoyroute.Route_Route) 994 g.Expect(ok).NotTo(BeFalse()) 995 996 g.Expect(routeAction.Route.RegexRewrite).To(Equal(&matcher.RegexMatchAndSubstitute{ 997 Pattern: &matcher.RegexMatcher{ 998 Regex: "/.*", 999 }, 1000 Substitution: "/replace-full", 1001 })) 1002 }) 1003 1004 t.Run("for prefix path rewrite", func(t *testing.T) { 1005 g := NewWithT(t) 1006 cg := core.NewConfigGenTest(t, core.TestOptions{}) 1007 1008 routes, err := route.BuildHTTPRoutesForVirtualService(node(cg), virtualServiceWithRewritePrefixPath, serviceRegistry, 1009 nil, 8080, gatewayNames, route.RouteOptions{}) 1010 xdstest.ValidateRoutes(t, routes) 1011 g.Expect(err).NotTo(HaveOccurred()) 1012 g.Expect(len(routes)).To(Equal(1)) 1013 1014 routeAction, ok := routes[0].Action.(*envoyroute.Route_Route) 1015 g.Expect(ok).NotTo(BeFalse()) 1016 g.Expect(routeAction.Route.PrefixRewrite).To(Equal("/replace-prefix")) 1017 }) 1018 1019 t.Run("for empty prefix path rewrite", func(t *testing.T) { 1020 g := NewWithT(t) 1021 cg := core.NewConfigGenTest(t, core.TestOptions{}) 1022 1023 routes, err := route.BuildHTTPRoutesForVirtualService(node(cg), virtualServiceWithEmptyRewritePrefixPath, serviceRegistry, 1024 nil, 8080, gatewayNames, route.RouteOptions{}) 1025 xdstest.ValidateRoutes(t, routes) 1026 g.Expect(err).NotTo(HaveOccurred()) 1027 g.Expect(len(routes)).To(Equal(1)) 1028 1029 routeAction, ok := routes[0].Action.(*envoyroute.Route_Route) 1030 g.Expect(ok).NotTo(BeFalse()) 1031 g.Expect(routeAction.Route.RegexRewrite).To(Equal(&matcher.RegexMatchAndSubstitute{ 1032 Pattern: &matcher.RegexMatcher{ 1033 Regex: `^/prefix-to-be-removed(/?)(.*)`, 1034 }, 1035 Substitution: `/\2`, 1036 })) 1037 }) 1038 1039 t.Run("for path and host rewrite", func(t *testing.T) { 1040 g := NewWithT(t) 1041 cg := core.NewConfigGenTest(t, core.TestOptions{}) 1042 1043 routes, err := route.BuildHTTPRoutesForVirtualService(node(cg), 1044 virtualServiceWithRewriteFullPathAndHost, 1045 serviceRegistry, nil, 8080, gatewayNames, route.RouteOptions{}) 1046 xdstest.ValidateRoutes(t, routes) 1047 g.Expect(err).NotTo(HaveOccurred()) 1048 g.Expect(len(routes)).To(Equal(1)) 1049 1050 routeAction, ok := routes[0].Action.(*envoyroute.Route_Route) 1051 g.Expect(ok).NotTo(BeFalse()) 1052 g.Expect(routeAction.Route.HostRewriteSpecifier).To(Equal(&envoyroute.RouteAction_HostRewriteLiteral{ 1053 HostRewriteLiteral: "bar.example.org", 1054 })) 1055 g.Expect(routeAction.Route.RegexRewrite).To(Equal(&matcher.RegexMatchAndSubstitute{ 1056 Pattern: &matcher.RegexMatcher{ 1057 Regex: "/.*", 1058 }, 1059 Substitution: "/replace-full", 1060 })) 1061 }) 1062 1063 t.Run("for path regex match with regex rewrite", func(t *testing.T) { 1064 g := NewWithT(t) 1065 cg := core.NewConfigGenTest(t, core.TestOptions{}) 1066 1067 routes, err := route.BuildHTTPRoutesForVirtualService(node(cg), 1068 virtualServiceWithPathRegexMatchRegexRewrite, 1069 serviceRegistry, nil, 8080, gatewayNames, route.RouteOptions{}) 1070 xdstest.ValidateRoutes(t, routes) 1071 g.Expect(err).NotTo(HaveOccurred()) 1072 g.Expect(len(routes)).To(Equal(1)) 1073 1074 routeAction, ok := routes[0].Action.(*envoyroute.Route_Route) 1075 g.Expect(ok).NotTo(BeFalse()) 1076 g.Expect(routeAction.Route.RegexRewrite).To(Equal(&matcher.RegexMatchAndSubstitute{ 1077 Pattern: &matcher.RegexMatcher{ 1078 Regex: "^/service/([^/]+)(/.*)$", 1079 }, 1080 Substitution: "\\2/instance/\\1", 1081 })) 1082 }) 1083 1084 t.Run("for redirect uri prefix '%PREFIX()%' that is without gateway semantics", func(t *testing.T) { 1085 g := NewWithT(t) 1086 cg := core.NewConfigGenTest(t, core.TestOptions{}) 1087 1088 routes, err := route.BuildHTTPRoutesForVirtualService(node(cg), 1089 virtualServiceWithRedirectPathPrefixNoGatewaySematics, 1090 serviceRegistry, nil, 8080, gatewayNames, route.RouteOptions{}) 1091 xdstest.ValidateRoutes(t, routes) 1092 g.Expect(err).NotTo(HaveOccurred()) 1093 g.Expect(len(routes)).To(Equal(1)) 1094 1095 redirectAction, ok := routes[0].Action.(*envoyroute.Route_Redirect) 1096 g.Expect(ok).NotTo(BeFalse()) 1097 g.Expect(redirectAction.Redirect.PathRewriteSpecifier).To(Equal(&envoyroute.RedirectAction_PathRedirect{ 1098 PathRedirect: "%PREFIX()%/replace-full", 1099 })) 1100 }) 1101 1102 t.Run("for full path redirect", func(t *testing.T) { 1103 g := NewWithT(t) 1104 cg := core.NewConfigGenTest(t, core.TestOptions{}) 1105 1106 routes, err := route.BuildHTTPRoutesForVirtualService(node(cg), virtualServiceWithRedirectFullPath, serviceRegistry, 1107 nil, 8080, gatewayNames, route.RouteOptions{}) 1108 xdstest.ValidateRoutes(t, routes) 1109 g.Expect(err).NotTo(HaveOccurred()) 1110 g.Expect(len(routes)).To(Equal(1)) 1111 1112 redirectAction, ok := routes[0].Action.(*envoyroute.Route_Redirect) 1113 g.Expect(ok).NotTo(BeFalse()) 1114 g.Expect(redirectAction.Redirect.PathRewriteSpecifier).To(Equal(&envoyroute.RedirectAction_PathRedirect{ 1115 PathRedirect: "/replace-full-path", 1116 })) 1117 }) 1118 1119 t.Run("for redirect and header manipulation", func(t *testing.T) { 1120 g := NewWithT(t) 1121 cg := core.NewConfigGenTest(t, core.TestOptions{}) 1122 1123 routes, err := route.BuildHTTPRoutesForVirtualService(node(cg), virtualServiceWithRedirectAndSetHeader, serviceRegistry, 1124 nil, 8080, gatewayNames, route.RouteOptions{}) 1125 xdstest.ValidateRoutes(t, routes) 1126 g.Expect(err).NotTo(HaveOccurred()) 1127 g.Expect(len(routes)).To(Equal(1)) 1128 1129 redirectAction, ok := routes[0].Action.(*envoyroute.Route_Redirect) 1130 g.Expect(ok).NotTo(BeFalse()) 1131 g.Expect(redirectAction.Redirect.ResponseCode).To(Equal(envoyroute.RedirectAction_PERMANENT_REDIRECT)) 1132 g.Expect(len(routes[0].ResponseHeadersToAdd)).To(Equal(1)) 1133 g.Expect(routes[0].ResponseHeadersToAdd[0].AppendAction).To(Equal(envoycore.HeaderValueOption_OVERWRITE_IF_EXISTS_OR_ADD)) 1134 g.Expect(routes[0].ResponseHeadersToAdd[0].Header.Key).To(Equal("Strict-Transport-Security")) 1135 g.Expect(routes[0].ResponseHeadersToAdd[0].Header.Value).To(Equal("max-age=31536000; includeSubDomains; preload")) 1136 }) 1137 1138 t.Run("for direct response code", func(t *testing.T) { 1139 g := NewWithT(t) 1140 cg := core.NewConfigGenTest(t, core.TestOptions{}) 1141 1142 routes, err := route.BuildHTTPRoutesForVirtualService(node(cg), virtualServiceWithDirectResponse, serviceRegistry, 1143 nil, 8080, gatewayNames, route.RouteOptions{}) 1144 xdstest.ValidateRoutes(t, routes) 1145 g.Expect(err).NotTo(HaveOccurred()) 1146 g.Expect(len(routes)).To(Equal(1)) 1147 1148 directResponseAction, ok := routes[0].Action.(*envoyroute.Route_DirectResponse) 1149 g.Expect(ok).NotTo(BeFalse()) 1150 g.Expect(directResponseAction.DirectResponse.Status).To(Equal(uint32(200))) 1151 g.Expect(directResponseAction.DirectResponse.Body.Specifier.(*envoycore.DataSource_InlineString).InlineString).To(Equal("hello")) 1152 }) 1153 1154 t.Run("for direct response code and header manipulation", func(t *testing.T) { 1155 g := NewWithT(t) 1156 cg := core.NewConfigGenTest(t, core.TestOptions{}) 1157 1158 routes, err := route.BuildHTTPRoutesForVirtualService(node(cg), virtualServiceWithDirectResponseAndSetHeader, serviceRegistry, 1159 nil, 8080, gatewayNames, route.RouteOptions{}) 1160 xdstest.ValidateRoutes(t, routes) 1161 g.Expect(err).NotTo(HaveOccurred()) 1162 g.Expect(len(routes)).To(Equal(1)) 1163 1164 directResponseAction, ok := routes[0].Action.(*envoyroute.Route_DirectResponse) 1165 g.Expect(ok).NotTo(BeFalse()) 1166 g.Expect(directResponseAction.DirectResponse.Status).To(Equal(uint32(200))) 1167 g.Expect(directResponseAction.DirectResponse.Body.Specifier.(*envoycore.DataSource_InlineString).InlineString).To(Equal("hello")) 1168 g.Expect(len(routes[0].ResponseHeadersToAdd)).To(Equal(1)) 1169 g.Expect(routes[0].ResponseHeadersToAdd[0].AppendAction).To(Equal(envoycore.HeaderValueOption_OVERWRITE_IF_EXISTS_OR_ADD)) 1170 g.Expect(routes[0].ResponseHeadersToAdd[0].Header.Key).To(Equal("Strict-Transport-Security")) 1171 g.Expect(routes[0].ResponseHeadersToAdd[0].Header.Value).To(Equal("max-age=31536000; includeSubDomains; preload")) 1172 }) 1173 1174 t.Run("for no virtualservice but has destinationrule with consistentHash loadbalancer", func(t *testing.T) { 1175 g := NewWithT(t) 1176 cg := core.NewConfigGenTest(t, core.TestOptions{ 1177 Configs: []config.Config{ 1178 { 1179 Meta: config.Meta{ 1180 GroupVersionKind: gvk.DestinationRule, 1181 Name: "acme", 1182 Namespace: "istio-system", 1183 }, 1184 Spec: networkingDestinationRule, 1185 }, 1186 }, 1187 Services: exampleService, 1188 }) 1189 vhosts := route.BuildSidecarVirtualHostWrapper(nil, node(cg), cg.PushContext(), serviceRegistry, 1190 []config.Config{}, 8080, map[host.Name]types.NamespacedName{}, 1191 ) 1192 g.Expect(vhosts[0].Routes[0].Action.(*envoyroute.Route_Route).Route.HashPolicy).NotTo(BeNil()) 1193 }) 1194 t.Run("for no virtualservice but has destinationrule with portLevel consistentHash loadbalancer", func(t *testing.T) { 1195 g := NewWithT(t) 1196 cg := core.NewConfigGenTest(t, core.TestOptions{ 1197 Configs: []config.Config{ 1198 { 1199 Meta: config.Meta{ 1200 GroupVersionKind: gvk.DestinationRule, 1201 Name: "acme", 1202 Namespace: "istio-system", 1203 }, 1204 Spec: networkingDestinationRuleWithPortLevelTrafficPolicy, 1205 }, 1206 }, 1207 Services: exampleService, 1208 }) 1209 vhosts := route.BuildSidecarVirtualHostWrapper(nil, node(cg), cg.PushContext(), serviceRegistry, 1210 []config.Config{}, 8080, map[host.Name]types.NamespacedName{}, 1211 ) 1212 1213 hashPolicy := &envoyroute.RouteAction_HashPolicy{ 1214 PolicySpecifier: &envoyroute.RouteAction_HashPolicy_Cookie_{ 1215 Cookie: &envoyroute.RouteAction_HashPolicy_Cookie{ 1216 Name: "hash-cookie-1", 1217 }, 1218 }, 1219 } 1220 g.Expect(vhosts[0].Routes[0].Action.(*envoyroute.Route_Route).Route.HashPolicy).To(ConsistOf(hashPolicy)) 1221 }) 1222 1223 t.Run("for virtualservices and services with overlapping wildcard hosts", func(t *testing.T) { 1224 g := NewWithT(t) 1225 cg := core.NewConfigGenTest(t, core.TestOptions{ 1226 Configs: []config.Config{ 1227 virtualServiceWithWildcardHost, 1228 virtualServiceWithNestedWildcardHost, 1229 virtualServiceWithGoogleWildcardHost, 1230 }, 1231 Services: []*model.Service{exampleWildcardService, exampleNestedWildcardService}, 1232 }) 1233 1234 // Redefine the service registry for this test 1235 serviceRegistry := map[host.Name]*model.Service{ 1236 "*.example.org": exampleWildcardService, 1237 "goodbye.hello.example.org": exampleNestedWildcardService, 1238 } 1239 1240 wildcardIndex := map[host.Name]types.NamespacedName{ 1241 "*.example.org": virtualServiceWithWildcardHost.NamespacedName(), 1242 "*.hello.example.org": virtualServiceWithNestedWildcardHost.NamespacedName(), 1243 } 1244 1245 vhosts := route.BuildSidecarVirtualHostWrapper(nil, node(cg), cg.PushContext(), serviceRegistry, 1246 []config.Config{ 1247 virtualServiceWithWildcardHost, 1248 virtualServiceWithNestedWildcardHost, 1249 virtualServiceWithGoogleWildcardHost, 1250 }, 8080, 1251 wildcardIndex, 1252 ) 1253 log.Printf("%#v", vhosts) 1254 // *.example.org, *.hello.example.org. The *.google.com VS is missing from virtualHosts because 1255 // it is not attached to a service 1256 g.Expect(vhosts).To(HaveLen(2)) 1257 for _, vhost := range vhosts { 1258 g.Expect(vhost.Services).To(HaveLen(1)) 1259 g.Expect(vhost.Routes).To(HaveLen(1)) 1260 } 1261 }) 1262 1263 t.Run("for virtualservices with with wildcard hosts outside of the serviceregistry (on port 80)", func(t *testing.T) { 1264 g := NewWithT(t) 1265 cg := core.NewConfigGenTest(t, core.TestOptions{ 1266 Configs: []config.Config{ 1267 virtualServiceWithWildcardHost, 1268 virtualServiceWithNestedWildcardHost, 1269 virtualServiceWithGoogleWildcardHost, 1270 }, 1271 Services: []*model.Service{exampleWildcardService, exampleNestedWildcardService}, 1272 }) 1273 1274 // Redefine the service registry for this test 1275 serviceRegistry := map[host.Name]*model.Service{ 1276 "*.example.org": exampleWildcardService, 1277 "goodbye.hello.example.org": exampleNestedWildcardService, 1278 } 1279 1280 // note that the VS containing *.google.com doesn't have an entry in the wildcard index 1281 wildcardIndex := map[host.Name]types.NamespacedName{ 1282 "*.example.org": virtualServiceWithWildcardHost.NamespacedName(), 1283 "*.hello.example.org": virtualServiceWithNestedWildcardHost.NamespacedName(), 1284 } 1285 1286 vhosts := route.BuildSidecarVirtualHostWrapper(nil, node(cg), cg.PushContext(), serviceRegistry, 1287 []config.Config{virtualServiceWithGoogleWildcardHost}, 80, wildcardIndex, 1288 ) 1289 // The service hosts (*.example.org and goodbye.hello.example.org) and the unattached VS host (*.google.com) 1290 g.Expect(vhosts).To(HaveLen(3)) 1291 for _, vhost := range vhosts { 1292 if len(vhost.VirtualServiceHosts) > 0 && vhost.VirtualServiceHosts[0] == "*.google.com" { 1293 // The *.google.com VS shouldn't have any services 1294 g.Expect(vhost.Services).To(HaveLen(0)) 1295 } else { 1296 // The other two VSs should have one service each 1297 g.Expect(vhost.Services).To(HaveLen(1)) 1298 } 1299 // All VSs should have one route 1300 g.Expect(vhost.Routes).To(HaveLen(1)) 1301 } 1302 }) 1303 } 1304 1305 func loadBalancerPolicy(name string) *networking.LoadBalancerSettings_ConsistentHash { 1306 return &networking.LoadBalancerSettings_ConsistentHash{ 1307 ConsistentHash: &networking.LoadBalancerSettings_ConsistentHashLB{ 1308 HashKey: &networking.LoadBalancerSettings_ConsistentHashLB_HttpCookie{ 1309 HttpCookie: &networking.LoadBalancerSettings_ConsistentHashLB_HTTPCookie{ 1310 Name: name, 1311 }, 1312 }, 1313 }, 1314 } 1315 } 1316 1317 var virtualServiceWithSubset = &networking.VirtualService{ 1318 Hosts: []string{}, 1319 Gateways: []string{"some-gateway"}, 1320 Http: []*networking.HTTPRoute{ 1321 { 1322 Route: []*networking.HTTPRouteDestination{ 1323 { 1324 Destination: &networking.Destination{ 1325 Subset: "some-subset", 1326 Host: "*.example.org", 1327 Port: &networking.PortSelector{ 1328 Number: 65000, 1329 }, 1330 }, 1331 Weight: 100, 1332 }, 1333 }, 1334 }, 1335 }, 1336 } 1337 1338 var virtualServiceWithSubsetWithPortLevelSettings = &networking.VirtualService{ 1339 Hosts: []string{}, 1340 Gateways: []string{"some-gateway"}, 1341 Http: []*networking.HTTPRoute{ 1342 { 1343 Route: []*networking.HTTPRouteDestination{ 1344 { 1345 Destination: &networking.Destination{ 1346 Subset: "port-level-settings-subset", 1347 Host: "*.example.org", 1348 Port: &networking.PortSelector{ 1349 Number: 8484, 1350 }, 1351 }, 1352 Weight: 100, 1353 }, 1354 }, 1355 }, 1356 }, 1357 } 1358 1359 var virtualServicePlain = config.Config{ 1360 Meta: config.Meta{ 1361 GroupVersionKind: gvk.VirtualService, 1362 Name: "acme", 1363 }, 1364 Spec: &networking.VirtualService{ 1365 Hosts: []string{}, 1366 Gateways: []string{"some-gateway"}, 1367 Http: []*networking.HTTPRoute{ 1368 { 1369 Route: []*networking.HTTPRouteDestination{ 1370 { 1371 Destination: &networking.Destination{ 1372 Host: "*.example.org", 1373 Port: &networking.PortSelector{ 1374 Number: 8484, 1375 }, 1376 }, 1377 Weight: 100, 1378 }, 1379 }, 1380 }, 1381 }, 1382 }, 1383 } 1384 1385 var virtualServiceWithTimeout = config.Config{ 1386 Meta: config.Meta{ 1387 GroupVersionKind: gvk.VirtualService, 1388 Name: "acme", 1389 }, 1390 Spec: &networking.VirtualService{ 1391 Hosts: []string{}, 1392 Gateways: []string{"some-gateway"}, 1393 Http: []*networking.HTTPRoute{ 1394 { 1395 Route: []*networking.HTTPRouteDestination{ 1396 { 1397 Destination: &networking.Destination{ 1398 Host: "*.example.org", 1399 Port: &networking.PortSelector{ 1400 Number: 8484, 1401 }, 1402 }, 1403 Weight: 100, 1404 }, 1405 }, 1406 Timeout: &durationpb.Duration{ 1407 Seconds: 10, 1408 }, 1409 }, 1410 }, 1411 }, 1412 } 1413 1414 var virtualServiceWithTimeoutDisabled = config.Config{ 1415 Meta: config.Meta{ 1416 GroupVersionKind: gvk.VirtualService, 1417 Name: "acme", 1418 }, 1419 Spec: &networking.VirtualService{ 1420 Hosts: []string{}, 1421 Gateways: []string{"some-gateway"}, 1422 Http: []*networking.HTTPRoute{ 1423 { 1424 Route: []*networking.HTTPRouteDestination{ 1425 { 1426 Destination: &networking.Destination{ 1427 Host: "*.example.org", 1428 Port: &networking.PortSelector{ 1429 Number: 8484, 1430 }, 1431 }, 1432 Weight: 100, 1433 }, 1434 }, 1435 Timeout: &durationpb.Duration{ 1436 Seconds: 0, 1437 }, 1438 }, 1439 }, 1440 }, 1441 } 1442 1443 var virtualServiceWithCatchAllRoute = config.Config{ 1444 Meta: config.Meta{ 1445 GroupVersionKind: gvk.VirtualService, 1446 Name: "acme", 1447 }, 1448 Spec: &networking.VirtualService{ 1449 Hosts: []string{}, 1450 Gateways: []string{"some-gateway"}, 1451 Http: []*networking.HTTPRoute{ 1452 { 1453 Name: "route", 1454 Match: []*networking.HTTPMatchRequest{ 1455 { 1456 Name: "non-catch-all", 1457 Uri: &networking.StringMatch{ 1458 MatchType: &networking.StringMatch_Prefix{ 1459 Prefix: "/route/v1", 1460 }, 1461 }, 1462 }, 1463 { 1464 Name: "catch-all", 1465 Uri: &networking.StringMatch{ 1466 MatchType: &networking.StringMatch_Prefix{ 1467 Prefix: "/", 1468 }, 1469 }, 1470 }, 1471 }, 1472 Route: []*networking.HTTPRouteDestination{ 1473 { 1474 Destination: &networking.Destination{ 1475 Host: "*.example.org", 1476 Port: &networking.PortSelector{ 1477 Number: 8484, 1478 }, 1479 }, 1480 Weight: 100, 1481 }, 1482 }, 1483 }, 1484 }, 1485 }, 1486 } 1487 1488 var virtualServiceWithCatchAllPort = config.Config{ 1489 Meta: config.Meta{ 1490 GroupVersionKind: gvk.VirtualService, 1491 Name: "acme", 1492 }, 1493 Spec: &networking.VirtualService{ 1494 Hosts: []string{}, 1495 Gateways: []string{"some-gateway"}, 1496 Http: []*networking.HTTPRoute{ 1497 { 1498 Name: "route 1", 1499 Match: []*networking.HTTPMatchRequest{ 1500 { 1501 Name: "catch-all for 8080", 1502 Port: 8080, 1503 }, 1504 }, 1505 Route: []*networking.HTTPRouteDestination{ 1506 { 1507 Destination: &networking.Destination{ 1508 Host: "example1.default.svc.cluster.local", 1509 Port: &networking.PortSelector{ 1510 Number: 8484, 1511 }, 1512 }, 1513 }, 1514 }, 1515 }, 1516 { 1517 Name: "route 2", 1518 Match: []*networking.HTTPMatchRequest{ 1519 { 1520 Name: "header match", 1521 Headers: map[string]*networking.StringMatch{ 1522 "cookie": { 1523 MatchType: &networking.StringMatch_Exact{Exact: "canary"}, 1524 }, 1525 }, 1526 }, 1527 }, 1528 Route: []*networking.HTTPRouteDestination{ 1529 { 1530 Destination: &networking.Destination{ 1531 Host: "example2.default.svc.cluster.local", 1532 Port: &networking.PortSelector{ 1533 Number: 8484, 1534 }, 1535 }, 1536 }, 1537 }, 1538 }, 1539 { 1540 Name: "route 3", 1541 Route: []*networking.HTTPRouteDestination{ 1542 { 1543 Destination: &networking.Destination{ 1544 Host: "example1.default.svc.cluster.local", 1545 Port: &networking.PortSelector{ 1546 Number: 8484, 1547 }, 1548 }, 1549 }, 1550 }, 1551 }, 1552 }, 1553 }, 1554 } 1555 1556 var virtualServiceWithWildcardHost = config.Config{ 1557 Meta: config.Meta{ 1558 GroupVersionKind: gvk.VirtualService, 1559 Name: "wildcard", 1560 }, 1561 Spec: &networking.VirtualService{ 1562 Hosts: []string{"*.example.org"}, 1563 Http: []*networking.HTTPRoute{ 1564 { 1565 Match: []*networking.HTTPMatchRequest{ 1566 { 1567 Name: "https", 1568 Port: uint32(8080), 1569 }, 1570 }, 1571 Route: []*networking.HTTPRouteDestination{ 1572 { 1573 Destination: &networking.Destination{ 1574 Host: "*.example.org", 1575 Port: &networking.PortSelector{ 1576 Number: 8080, 1577 }, 1578 }, 1579 }, 1580 }, 1581 }, 1582 }, 1583 }, 1584 } 1585 1586 var virtualServiceWithNestedWildcardHost = config.Config{ 1587 Meta: config.Meta{ 1588 GroupVersionKind: gvk.VirtualService, 1589 Name: "nested-wildcard", 1590 }, 1591 Spec: &networking.VirtualService{ 1592 Hosts: []string{"*.hello.example.org"}, 1593 Http: []*networking.HTTPRoute{ 1594 { 1595 Match: []*networking.HTTPMatchRequest{ 1596 { 1597 Name: "https", 1598 Port: uint32(8080), 1599 }, 1600 }, 1601 Route: []*networking.HTTPRouteDestination{ 1602 { 1603 Destination: &networking.Destination{ 1604 Host: "*.hello.example.org", 1605 Port: &networking.PortSelector{ 1606 Number: 8080, 1607 }, 1608 }, 1609 }, 1610 }, 1611 }, 1612 }, 1613 }, 1614 } 1615 1616 var virtualServiceWithGoogleWildcardHost = config.Config{ 1617 Meta: config.Meta{ 1618 GroupVersionKind: gvk.VirtualService, 1619 Name: "google-wildcard", 1620 }, 1621 Spec: &networking.VirtualService{ 1622 Hosts: []string{"*.google.com"}, 1623 Http: []*networking.HTTPRoute{ 1624 { 1625 Match: []*networking.HTTPMatchRequest{ 1626 { 1627 Uri: &networking.StringMatch{ 1628 MatchType: &networking.StringMatch_Prefix{ 1629 Prefix: "/", 1630 }, 1631 }, 1632 }, 1633 }, 1634 Route: []*networking.HTTPRouteDestination{ 1635 { 1636 Destination: &networking.Destination{ 1637 Host: "internal-google.default.svc.cluster.local", 1638 }, 1639 }, 1640 }, 1641 }, 1642 }, 1643 }, 1644 } 1645 1646 var virtualServiceWithCatchAllMultiPrefixRoute = config.Config{ 1647 Meta: config.Meta{ 1648 GroupVersionKind: gvk.VirtualService, 1649 Name: "acme", 1650 }, 1651 Spec: &networking.VirtualService{ 1652 Hosts: []string{}, 1653 Gateways: []string{"some-gateway"}, 1654 Http: []*networking.HTTPRoute{ 1655 { 1656 Match: []*networking.HTTPMatchRequest{ 1657 { 1658 Name: "catch-all", 1659 Uri: &networking.StringMatch{ 1660 MatchType: &networking.StringMatch_Prefix{ 1661 Prefix: "/", 1662 }, 1663 }, 1664 SourceLabels: map[string]string{ 1665 "matchingNoSrc": "xxx", 1666 }, 1667 }, 1668 { 1669 Name: "specific match", 1670 Uri: &networking.StringMatch{ 1671 MatchType: &networking.StringMatch_Prefix{ 1672 Prefix: "/a", 1673 }, 1674 }, 1675 }, 1676 }, 1677 Route: []*networking.HTTPRouteDestination{ 1678 { 1679 Destination: &networking.Destination{ 1680 Host: "*.example.org", 1681 Port: &networking.PortSelector{ 1682 Number: 8484, 1683 }, 1684 }, 1685 Weight: 100, 1686 }, 1687 }, 1688 }, 1689 }, 1690 }, 1691 } 1692 1693 var virtualServiceWithCatchAllRouteWeightedDestination = config.Config{ 1694 Meta: config.Meta{ 1695 GroupVersionKind: gvk.VirtualService, 1696 Name: "acme", 1697 }, 1698 Spec: &networking.VirtualService{ 1699 Hosts: []string{"headers.test.istio.io"}, 1700 Gateways: []string{"some-gateway"}, 1701 Http: []*networking.HTTPRoute{ 1702 { 1703 Match: []*networking.HTTPMatchRequest{ 1704 { 1705 Name: "headers-only", 1706 Headers: map[string]*networking.StringMatch{ 1707 "version": { 1708 MatchType: &networking.StringMatch_Exact{ 1709 Exact: "v2", 1710 }, 1711 }, 1712 }, 1713 SourceLabels: map[string]string{ 1714 "version": "v1", 1715 }, 1716 }, 1717 }, 1718 Route: []*networking.HTTPRouteDestination{ 1719 { 1720 Destination: &networking.Destination{ 1721 Host: "c-weighted.extsvc.com", 1722 Subset: "v2", 1723 }, 1724 Weight: 100, 1725 }, 1726 }, 1727 }, 1728 { 1729 Route: []*networking.HTTPRouteDestination{ 1730 { 1731 Destination: &networking.Destination{ 1732 Host: "c-weighted.extsvc.com", 1733 Subset: "v1", 1734 }, 1735 Weight: 100, 1736 }, 1737 }, 1738 }, 1739 }, 1740 }, 1741 } 1742 1743 var virtualServiceWithHeaderOperationsForSingleCluster = config.Config{ 1744 Meta: config.Meta{ 1745 GroupVersionKind: gvk.VirtualService, 1746 Name: "acme", 1747 }, 1748 Spec: &networking.VirtualService{ 1749 Hosts: []string{"headers.test.istio.io"}, 1750 Gateways: []string{"some-gateway"}, 1751 Http: []*networking.HTTPRoute{ 1752 { 1753 Route: []*networking.HTTPRouteDestination{ 1754 { 1755 Destination: &networking.Destination{ 1756 Host: "c-weighted.extsvc.com", 1757 Subset: "v1", 1758 }, 1759 Headers: &networking.Headers{ 1760 Request: &networking.Headers_HeaderOperations{ 1761 Set: map[string]string{"x-route-req-set": "v1", ":authority": "internal.foo.extsvc.com"}, 1762 Add: map[string]string{"x-route-req-add": "v2", ":authority": "internal.bar.extsvc.com"}, 1763 Remove: []string{"x-route-req-remove"}, 1764 }, 1765 Response: &networking.Headers_HeaderOperations{ 1766 Set: map[string]string{"x-route-resp-set": "v1"}, 1767 Add: map[string]string{"x-route-resp-add": "v2"}, 1768 Remove: []string{"x-route-resp-remove"}, 1769 }, 1770 }, 1771 Weight: 100, 1772 }, 1773 }, 1774 Headers: &networking.Headers{ 1775 Request: &networking.Headers_HeaderOperations{ 1776 Set: map[string]string{"x-req-set": "v1", ":authority": "foo.extsvc.com"}, 1777 Add: map[string]string{"x-req-add": "v2", ":authority": "bar.extsvc.com"}, 1778 Remove: []string{"x-req-remove"}, 1779 }, 1780 Response: &networking.Headers_HeaderOperations{ 1781 Set: map[string]string{"x-resp-set": "v1"}, 1782 Add: map[string]string{"x-resp-add": "v2"}, 1783 Remove: []string{"x-resp-remove"}, 1784 }, 1785 }, 1786 }, 1787 }, 1788 }, 1789 } 1790 1791 var virtualServiceWithHeaderOperationsForWeightedCluster = config.Config{ 1792 Meta: config.Meta{ 1793 GroupVersionKind: gvk.VirtualService, 1794 Name: "acme", 1795 }, 1796 Spec: &networking.VirtualService{ 1797 Hosts: []string{"headers.test.istio.io"}, 1798 Gateways: []string{"some-gateway"}, 1799 Http: []*networking.HTTPRoute{ 1800 { 1801 Route: []*networking.HTTPRouteDestination{ 1802 { 1803 Destination: &networking.Destination{ 1804 Host: "c-weighted.extsvc.com", 1805 Subset: "blue", 1806 }, 1807 Headers: &networking.Headers{ 1808 Request: &networking.Headers_HeaderOperations{ 1809 Set: map[string]string{"x-route-req-set-blue": "v1", ":authority": "blue.foo.extsvc.com"}, 1810 Add: map[string]string{"x-route-req-add-blue": "v2", ":authority": "blue.bar.extsvc.com"}, 1811 Remove: []string{"x-route-req-remove-blue"}, 1812 }, 1813 Response: &networking.Headers_HeaderOperations{ 1814 Set: map[string]string{"x-route-resp-set-blue": "v1"}, 1815 Add: map[string]string{"x-route-resp-add-blue": "v2"}, 1816 Remove: []string{"x-route-resp-remove-blue"}, 1817 }, 1818 }, 1819 Weight: 9, 1820 }, 1821 { 1822 Destination: &networking.Destination{ 1823 Host: "c-weighted.extsvc.com", 1824 Subset: "green", 1825 }, 1826 Headers: &networking.Headers{ 1827 Request: &networking.Headers_HeaderOperations{ 1828 Set: map[string]string{"x-route-req-set-green": "v1", ":authority": "green.foo.extsvc.com"}, 1829 Add: map[string]string{"x-route-req-add-green": "v2", ":authority": "green.bar.extsvc.com"}, 1830 Remove: []string{"x-route-req-remove-green"}, 1831 }, 1832 Response: &networking.Headers_HeaderOperations{ 1833 Set: map[string]string{"x-route-resp-set-green": "v1"}, 1834 Add: map[string]string{"x-route-resp-add-green": "v2"}, 1835 Remove: []string{"x-route-resp-remove-green"}, 1836 }, 1837 }, 1838 Weight: 1, 1839 }, 1840 }, 1841 Headers: &networking.Headers{ 1842 Request: &networking.Headers_HeaderOperations{ 1843 Set: map[string]string{"x-req-set": "v1", ":authority": "foo.extsvc.com"}, 1844 Add: map[string]string{"x-req-add": "v2", ":authority": "bar.extsvc.com"}, 1845 Remove: []string{"x-req-remove"}, 1846 }, 1847 Response: &networking.Headers_HeaderOperations{ 1848 Set: map[string]string{"x-resp-set": "v1"}, 1849 Add: map[string]string{"x-resp-add": "v2"}, 1850 Remove: []string{"x-resp-remove"}, 1851 }, 1852 }, 1853 }, 1854 }, 1855 }, 1856 } 1857 1858 var virtualServiceWithRedirect = config.Config{ 1859 Meta: config.Meta{ 1860 GroupVersionKind: gvk.VirtualService, 1861 Name: "acme", 1862 }, 1863 Spec: &networking.VirtualService{ 1864 Hosts: []string{}, 1865 Gateways: []string{"some-gateway"}, 1866 Http: []*networking.HTTPRoute{ 1867 { 1868 Redirect: &networking.HTTPRedirect{ 1869 Uri: "example.org", 1870 Authority: "some-authority.default.svc.cluster.local", 1871 RedirectCode: 308, 1872 }, 1873 }, 1874 }, 1875 }, 1876 } 1877 1878 var virtualServiceWithRedirectPathPrefix = config.Config{ 1879 Meta: config.Meta{ 1880 GroupVersionKind: gvk.VirtualService, 1881 Name: "acme", 1882 Annotations: map[string]string{ 1883 "internal.istio.io/route-semantics": "gateway", 1884 }, 1885 }, 1886 Spec: &networking.VirtualService{ 1887 Hosts: []string{}, 1888 Gateways: []string{"some-gateway"}, 1889 Http: []*networking.HTTPRoute{ 1890 { 1891 Redirect: &networking.HTTPRedirect{ 1892 Uri: "%PREFIX()%/replace-prefix", 1893 Authority: "some-authority.default.svc.cluster.local", 1894 RedirectCode: 308, 1895 }, 1896 }, 1897 }, 1898 }, 1899 } 1900 1901 var virtualServiceWithRedirectPathPrefixNoGatewaySematics = config.Config{ 1902 Meta: config.Meta{ 1903 GroupVersionKind: gvk.VirtualService, 1904 Name: "acme", 1905 }, 1906 Spec: &networking.VirtualService{ 1907 Hosts: []string{}, 1908 Gateways: []string{"some-gateway"}, 1909 Http: []*networking.HTTPRoute{ 1910 { 1911 Redirect: &networking.HTTPRedirect{ 1912 Uri: "%PREFIX()%/replace-full", 1913 Authority: "some-authority.default.svc.cluster.local", 1914 RedirectCode: 308, 1915 }, 1916 }, 1917 }, 1918 }, 1919 } 1920 1921 var virtualServiceWithRedirectFullPath = config.Config{ 1922 Meta: config.Meta{ 1923 GroupVersionKind: gvk.VirtualService, 1924 Name: "acme", 1925 }, 1926 Spec: &networking.VirtualService{ 1927 Hosts: []string{}, 1928 Gateways: []string{"some-gateway"}, 1929 Http: []*networking.HTTPRoute{ 1930 { 1931 Redirect: &networking.HTTPRedirect{ 1932 Uri: "/replace-full-path", 1933 Authority: "some-authority.default.svc.cluster.local", 1934 RedirectCode: 308, 1935 }, 1936 }, 1937 }, 1938 }, 1939 } 1940 1941 var virtualServiceWithRewriteHost = config.Config{ 1942 Meta: config.Meta{ 1943 GroupVersionKind: gvk.VirtualService, 1944 Name: "acme", 1945 Annotations: map[string]string{ 1946 "internal.istio.io/route-semantics": "gateway", 1947 }, 1948 }, 1949 Spec: &networking.VirtualService{ 1950 Hosts: []string{}, 1951 Gateways: []string{"some-gateway"}, 1952 Http: []*networking.HTTPRoute{ 1953 { 1954 Match: []*networking.HTTPMatchRequest{ 1955 { 1956 Name: "host-rewrite", 1957 }, 1958 }, 1959 Rewrite: &networking.HTTPRewrite{ 1960 Authority: "bar.example.org", 1961 }, 1962 Route: []*networking.HTTPRouteDestination{ 1963 { 1964 Destination: &networking.Destination{ 1965 Host: "foo.example.org", 1966 }, 1967 Weight: 100, 1968 }, 1969 }, 1970 }, 1971 }, 1972 }, 1973 } 1974 1975 var virtualServiceWithRewritePrefixPath = config.Config{ 1976 Meta: config.Meta{ 1977 GroupVersionKind: gvk.VirtualService, 1978 Name: "acme", 1979 Annotations: map[string]string{ 1980 "internal.istio.io/route-semantics": "gateway", 1981 }, 1982 }, 1983 Spec: &networking.VirtualService{ 1984 Hosts: []string{}, 1985 Gateways: []string{"some-gateway"}, 1986 Http: []*networking.HTTPRoute{ 1987 { 1988 Match: []*networking.HTTPMatchRequest{ 1989 { 1990 Name: "prefix-path-rewrite", 1991 }, 1992 }, 1993 Rewrite: &networking.HTTPRewrite{ 1994 Uri: "/replace-prefix", 1995 }, 1996 Route: []*networking.HTTPRouteDestination{ 1997 { 1998 Destination: &networking.Destination{ 1999 Host: "foo.example.org", 2000 }, 2001 Weight: 100, 2002 }, 2003 }, 2004 }, 2005 }, 2006 }, 2007 } 2008 2009 var virtualServiceWithEmptyRewritePrefixPath = config.Config{ 2010 Meta: config.Meta{ 2011 GroupVersionKind: gvk.VirtualService, 2012 Name: "acme", 2013 Annotations: map[string]string{ 2014 "internal.istio.io/route-semantics": "gateway", 2015 }, 2016 }, 2017 Spec: &networking.VirtualService{ 2018 Hosts: []string{}, 2019 Gateways: []string{"some-gateway"}, 2020 Http: []*networking.HTTPRoute{ 2021 { 2022 Match: []*networking.HTTPMatchRequest{ 2023 { 2024 Name: "prefix-path-rewrite", 2025 Uri: &networking.StringMatch{ 2026 MatchType: &networking.StringMatch_Prefix{Prefix: "/prefix-to-be-removed"}, 2027 }, 2028 }, 2029 }, 2030 Rewrite: &networking.HTTPRewrite{ 2031 Uri: "/", 2032 }, 2033 Route: []*networking.HTTPRouteDestination{ 2034 { 2035 Destination: &networking.Destination{ 2036 Host: "foo.example.org", 2037 }, 2038 Weight: 100, 2039 }, 2040 }, 2041 }, 2042 }, 2043 }, 2044 } 2045 2046 var virtualServiceWithRewriteFullPath = config.Config{ 2047 Meta: config.Meta{ 2048 GroupVersionKind: gvk.VirtualService, 2049 Name: "acme", 2050 Annotations: map[string]string{ 2051 "internal.istio.io/route-semantics": "gateway", 2052 }, 2053 }, 2054 Spec: &networking.VirtualService{ 2055 Hosts: []string{}, 2056 Gateways: []string{"some-gateway"}, 2057 Http: []*networking.HTTPRoute{ 2058 { 2059 Match: []*networking.HTTPMatchRequest{ 2060 { 2061 Name: "full-path-rewrite", 2062 }, 2063 }, 2064 Rewrite: &networking.HTTPRewrite{ 2065 UriRegexRewrite: &networking.RegexRewrite{ 2066 Match: "/.*", 2067 Rewrite: "/replace-full", 2068 }, 2069 }, 2070 Route: []*networking.HTTPRouteDestination{ 2071 { 2072 Destination: &networking.Destination{ 2073 Host: "foo.example.org", 2074 }, 2075 Weight: 100, 2076 }, 2077 }, 2078 }, 2079 }, 2080 }, 2081 } 2082 2083 var virtualServiceWithRewriteFullPathAndHost = config.Config{ 2084 Meta: config.Meta{ 2085 GroupVersionKind: gvk.VirtualService, 2086 Name: "acme", 2087 Annotations: map[string]string{ 2088 "internal.istio.io/route-semantics": "gateway", 2089 }, 2090 }, 2091 Spec: &networking.VirtualService{ 2092 Hosts: []string{}, 2093 Gateways: []string{"some-gateway"}, 2094 Http: []*networking.HTTPRoute{ 2095 { 2096 Match: []*networking.HTTPMatchRequest{ 2097 { 2098 Name: "full-path-and-host-rewrite", 2099 }, 2100 }, 2101 Rewrite: &networking.HTTPRewrite{ 2102 UriRegexRewrite: &networking.RegexRewrite{ 2103 Match: "/.*", 2104 Rewrite: "/replace-full", 2105 }, 2106 Authority: "bar.example.org", 2107 }, 2108 Route: []*networking.HTTPRouteDestination{ 2109 { 2110 Destination: &networking.Destination{ 2111 Host: "foo.example.org", 2112 }, 2113 Weight: 100, 2114 }, 2115 }, 2116 }, 2117 }, 2118 }, 2119 } 2120 2121 var virtualServiceWithPathRegexMatchRegexRewrite = config.Config{ 2122 Meta: config.Meta{ 2123 GroupVersionKind: gvk.VirtualService, 2124 Name: "acme", 2125 Annotations: map[string]string{ 2126 "internal.istio.io/route-semantics": "gateway", 2127 }, 2128 }, 2129 Spec: &networking.VirtualService{ 2130 Hosts: []string{}, 2131 Gateways: []string{"some-gateway"}, 2132 Http: []*networking.HTTPRoute{ 2133 { 2134 Match: []*networking.HTTPMatchRequest{ 2135 { 2136 Name: "full-path-and-host-rewrite", 2137 Uri: &networking.StringMatch{ 2138 MatchType: &networking.StringMatch_Regex{ 2139 Regex: "^/service/[^/]+/.*$", 2140 }, 2141 }, 2142 }, 2143 }, 2144 Rewrite: &networking.HTTPRewrite{ 2145 UriRegexRewrite: &networking.RegexRewrite{ 2146 Match: "^/service/([^/]+)(/.*)$", 2147 Rewrite: "\\2/instance/\\1", 2148 }, 2149 }, 2150 Route: []*networking.HTTPRouteDestination{ 2151 { 2152 Destination: &networking.Destination{ 2153 Host: "foo.example.org", 2154 }, 2155 Weight: 100, 2156 }, 2157 }, 2158 }, 2159 }, 2160 }, 2161 } 2162 2163 var virtualServiceWithRedirectAndSetHeader = config.Config{ 2164 Meta: config.Meta{ 2165 GroupVersionKind: gvk.VirtualService, 2166 Name: "acme", 2167 }, 2168 Spec: &networking.VirtualService{ 2169 Hosts: []string{}, 2170 Gateways: []string{"some-gateway"}, 2171 Http: []*networking.HTTPRoute{ 2172 { 2173 Redirect: &networking.HTTPRedirect{ 2174 Uri: "example.org", 2175 Authority: "some-authority.default.svc.cluster.local", 2176 RedirectCode: 308, 2177 }, 2178 Headers: &networking.Headers{ 2179 Response: &networking.Headers_HeaderOperations{ 2180 Set: map[string]string{ 2181 "Strict-Transport-Security": "max-age=31536000; includeSubDomains; preload", 2182 }, 2183 }, 2184 }, 2185 }, 2186 }, 2187 }, 2188 } 2189 2190 var virtualServiceWithDirectResponse = config.Config{ 2191 Meta: config.Meta{ 2192 GroupVersionKind: gvk.VirtualService, 2193 Name: "acme", 2194 }, 2195 Spec: &networking.VirtualService{ 2196 Hosts: []string{}, 2197 Gateways: []string{"some-gateway"}, 2198 Http: []*networking.HTTPRoute{ 2199 { 2200 DirectResponse: &networking.HTTPDirectResponse{ 2201 Status: 200, 2202 Body: &networking.HTTPBody{ 2203 Specifier: &networking.HTTPBody_String_{String_: "hello"}, 2204 }, 2205 }, 2206 }, 2207 }, 2208 }, 2209 } 2210 2211 var virtualServiceWithDirectResponseAndSetHeader = config.Config{ 2212 Meta: config.Meta{ 2213 GroupVersionKind: gvk.VirtualService, 2214 Name: "acme", 2215 }, 2216 Spec: &networking.VirtualService{ 2217 Hosts: []string{}, 2218 Gateways: []string{"some-gateway"}, 2219 Http: []*networking.HTTPRoute{ 2220 { 2221 DirectResponse: &networking.HTTPDirectResponse{ 2222 Status: 200, 2223 Body: &networking.HTTPBody{ 2224 Specifier: &networking.HTTPBody_String_{String_: "hello"}, 2225 }, 2226 }, 2227 Headers: &networking.Headers{ 2228 Response: &networking.Headers_HeaderOperations{ 2229 Set: map[string]string{ 2230 "Strict-Transport-Security": "max-age=31536000; includeSubDomains; preload", 2231 }, 2232 }, 2233 }, 2234 }, 2235 }, 2236 }, 2237 } 2238 2239 var virtualServiceWithRegexMatchingOnURI = config.Config{ 2240 Meta: config.Meta{ 2241 GroupVersionKind: gvk.VirtualService, 2242 Name: "acme", 2243 }, 2244 Spec: &networking.VirtualService{ 2245 Hosts: []string{}, 2246 Gateways: []string{"some-gateway"}, 2247 Http: []*networking.HTTPRoute{ 2248 { 2249 Match: []*networking.HTTPMatchRequest{ 2250 { 2251 Name: "status", 2252 Uri: &networking.StringMatch{ 2253 MatchType: &networking.StringMatch_Regex{ 2254 Regex: "\\/(.?)\\/status", 2255 }, 2256 }, 2257 }, 2258 }, 2259 Redirect: &networking.HTTPRedirect{ 2260 Uri: "example.org", 2261 Authority: "some-authority.default.svc.cluster.local", 2262 RedirectCode: 308, 2263 }, 2264 }, 2265 }, 2266 }, 2267 } 2268 2269 var virtualServiceWithExactMatchingOnHeaderForJWTClaims = config.Config{ 2270 Meta: config.Meta{ 2271 GroupVersionKind: gvk.VirtualService, 2272 Name: "acme", 2273 }, 2274 Spec: &networking.VirtualService{ 2275 Hosts: []string{}, 2276 Gateways: []string{"some-gateway"}, 2277 Http: []*networking.HTTPRoute{ 2278 { 2279 Match: []*networking.HTTPMatchRequest{ 2280 { 2281 Name: "auth", 2282 Headers: map[string]*networking.StringMatch{ 2283 "@request.auth.claims.Foo": { 2284 MatchType: &networking.StringMatch_Exact{ 2285 Exact: "Bar", 2286 }, 2287 }, 2288 }, 2289 WithoutHeaders: map[string]*networking.StringMatch{ 2290 "@request.auth.claims.Bla": { 2291 MatchType: &networking.StringMatch_Exact{ 2292 Exact: "Bar", 2293 }, 2294 }, 2295 }, 2296 }, 2297 }, 2298 Redirect: &networking.HTTPRedirect{ 2299 Uri: "example.org", 2300 Authority: "some-authority.default.svc.cluster.local", 2301 RedirectCode: 308, 2302 }, 2303 }, 2304 }, 2305 }, 2306 } 2307 2308 var virtualServiceWithRegexMatchingOnHeader = config.Config{ 2309 Meta: config.Meta{ 2310 GroupVersionKind: gvk.VirtualService, 2311 Name: "acme", 2312 }, 2313 Spec: &networking.VirtualService{ 2314 Hosts: []string{}, 2315 Gateways: []string{"some-gateway"}, 2316 Http: []*networking.HTTPRoute{ 2317 { 2318 Match: []*networking.HTTPMatchRequest{ 2319 { 2320 Name: "auth", 2321 Headers: map[string]*networking.StringMatch{ 2322 "Authentication": { 2323 MatchType: &networking.StringMatch_Regex{ 2324 Regex: "Bearer .+?\\..+?\\..+?", 2325 }, 2326 }, 2327 }, 2328 }, 2329 }, 2330 Redirect: &networking.HTTPRedirect{ 2331 Uri: "example.org", 2332 Authority: "some-authority.default.svc.cluster.local", 2333 RedirectCode: 308, 2334 }, 2335 }, 2336 }, 2337 }, 2338 } 2339 2340 func createVirtualServiceWithRegexMatchingForAllCasesOnHeader() []*config.Config { 2341 ret := []*config.Config{} 2342 regex := "*" 2343 ret = append(ret, &config.Config{ 2344 Meta: config.Meta{ 2345 GroupVersionKind: gvk.VirtualService, 2346 Name: "acme", 2347 }, 2348 Spec: &networking.VirtualService{ 2349 Hosts: []string{}, 2350 Gateways: []string{"some-gateway"}, 2351 Http: []*networking.HTTPRoute{ 2352 { 2353 Match: []*networking.HTTPMatchRequest{ 2354 { 2355 Name: "presence", 2356 Headers: map[string]*networking.StringMatch{ 2357 "FOO-HEADER": { 2358 MatchType: &networking.StringMatch_Regex{ 2359 Regex: regex, 2360 }, 2361 }, 2362 }, 2363 }, 2364 }, 2365 Redirect: &networking.HTTPRedirect{ 2366 Uri: "example.org", 2367 Authority: "some-authority.default.svc.cluster.local", 2368 RedirectCode: 308, 2369 }, 2370 }, 2371 }, 2372 }, 2373 }) 2374 2375 return ret 2376 } 2377 2378 var virtualServiceWithRegexMatchingOnWithoutHeader = config.Config{ 2379 Meta: config.Meta{ 2380 GroupVersionKind: gvk.VirtualService, 2381 Name: "acme", 2382 }, 2383 Spec: &networking.VirtualService{ 2384 Hosts: []string{}, 2385 Gateways: []string{"some-gateway"}, 2386 Http: []*networking.HTTPRoute{ 2387 { 2388 Match: []*networking.HTTPMatchRequest{ 2389 { 2390 Name: "without-test", 2391 WithoutHeaders: map[string]*networking.StringMatch{ 2392 "FOO-HEADER": { 2393 MatchType: &networking.StringMatch_Regex{ 2394 Regex: "BAR .+?\\..+?\\..+?", 2395 }, 2396 }, 2397 }, 2398 }, 2399 }, 2400 Redirect: &networking.HTTPRedirect{ 2401 Uri: "example.org", 2402 Authority: "some-authority.default.svc.cluster.local", 2403 RedirectCode: 308, 2404 }, 2405 }, 2406 }, 2407 }, 2408 } 2409 2410 var virtualServiceWithPresentMatchingOnHeader = config.Config{ 2411 Meta: config.Meta{ 2412 GroupVersionKind: gvk.VirtualService, 2413 Name: "acme", 2414 }, 2415 Spec: &networking.VirtualService{ 2416 Hosts: []string{}, 2417 Gateways: []string{"some-gateway"}, 2418 Http: []*networking.HTTPRoute{ 2419 { 2420 Match: []*networking.HTTPMatchRequest{ 2421 { 2422 Name: "presence", 2423 Headers: map[string]*networking.StringMatch{ 2424 "FOO-HEADER": nil, 2425 }, 2426 }, 2427 }, 2428 Redirect: &networking.HTTPRedirect{ 2429 Uri: "example.org", 2430 Authority: "some-authority.default.svc.cluster.local", 2431 RedirectCode: 308, 2432 }, 2433 }, 2434 }, 2435 }, 2436 } 2437 2438 var virtualServiceWithPresentMatchingOnHeader2 = config.Config{ 2439 Meta: config.Meta{ 2440 GroupVersionKind: gvk.VirtualService, 2441 Name: "acme", 2442 }, 2443 Spec: &networking.VirtualService{ 2444 Hosts: []string{}, 2445 Gateways: []string{"some-gateway"}, 2446 Http: []*networking.HTTPRoute{ 2447 { 2448 Match: []*networking.HTTPMatchRequest{ 2449 { 2450 Name: "presence", 2451 Headers: map[string]*networking.StringMatch{ 2452 "FOO-HEADER": {}, 2453 }, 2454 }, 2455 }, 2456 Redirect: &networking.HTTPRedirect{ 2457 Uri: "example.org", 2458 Authority: "some-authority.default.svc.cluster.local", 2459 RedirectCode: 308, 2460 }, 2461 }, 2462 }, 2463 }, 2464 } 2465 2466 var virtualServiceWithPresentMatchingOnWithoutHeader = config.Config{ 2467 Meta: config.Meta{ 2468 GroupVersionKind: gvk.VirtualService, 2469 Name: "acme", 2470 }, 2471 Spec: &networking.VirtualService{ 2472 Hosts: []string{}, 2473 Gateways: []string{"some-gateway"}, 2474 Http: []*networking.HTTPRoute{ 2475 { 2476 Match: []*networking.HTTPMatchRequest{ 2477 { 2478 Name: "presence", 2479 WithoutHeaders: map[string]*networking.StringMatch{ 2480 "FOO-HEADER": nil, 2481 }, 2482 }, 2483 }, 2484 Redirect: &networking.HTTPRedirect{ 2485 Uri: "example.org", 2486 Authority: "some-authority.default.svc.cluster.local", 2487 RedirectCode: 308, 2488 }, 2489 }, 2490 }, 2491 }, 2492 } 2493 2494 var virtualServiceWithExactMatchingOnQueryParameter = config.Config{ 2495 Meta: config.Meta{ 2496 GroupVersionKind: gvk.VirtualService, 2497 Name: "acme", 2498 }, 2499 Spec: &networking.VirtualService{ 2500 Hosts: []string{}, 2501 Gateways: []string{"some-gateway"}, 2502 Http: []*networking.HTTPRoute{ 2503 { 2504 Match: []*networking.HTTPMatchRequest{ 2505 { 2506 Name: "auth", 2507 QueryParams: map[string]*networking.StringMatch{ 2508 "token": { 2509 MatchType: &networking.StringMatch_Exact{ 2510 Exact: "foo", 2511 }, 2512 }, 2513 }, 2514 }, 2515 }, 2516 Redirect: &networking.HTTPRedirect{ 2517 Uri: "example.org", 2518 Authority: "some-authority.default.svc.cluster.local", 2519 RedirectCode: 308, 2520 }, 2521 }, 2522 }, 2523 }, 2524 } 2525 2526 var virtualServiceWithPrefixMatchingOnQueryParameter = config.Config{ 2527 Meta: config.Meta{ 2528 GroupVersionKind: gvk.VirtualService, 2529 Name: "acme", 2530 }, 2531 Spec: &networking.VirtualService{ 2532 Hosts: []string{}, 2533 Gateways: []string{"some-gateway"}, 2534 Http: []*networking.HTTPRoute{ 2535 { 2536 Match: []*networking.HTTPMatchRequest{ 2537 { 2538 Name: "auth", 2539 QueryParams: map[string]*networking.StringMatch{ 2540 "token": { 2541 MatchType: &networking.StringMatch_Prefix{ 2542 Prefix: "foo-", 2543 }, 2544 }, 2545 }, 2546 }, 2547 }, 2548 Redirect: &networking.HTTPRedirect{ 2549 Uri: "example.org", 2550 Authority: "some-authority.default.svc.cluster.local", 2551 RedirectCode: 308, 2552 }, 2553 }, 2554 }, 2555 }, 2556 } 2557 2558 var virtualServiceWithRegexMatchingOnQueryParameter = config.Config{ 2559 Meta: config.Meta{ 2560 GroupVersionKind: gvk.VirtualService, 2561 Name: "acme", 2562 }, 2563 Spec: &networking.VirtualService{ 2564 Hosts: []string{}, 2565 Gateways: []string{"some-gateway"}, 2566 Http: []*networking.HTTPRoute{ 2567 { 2568 Match: []*networking.HTTPMatchRequest{ 2569 { 2570 Name: "auth", 2571 QueryParams: map[string]*networking.StringMatch{ 2572 "token": { 2573 MatchType: &networking.StringMatch_Regex{ 2574 Regex: "BAR .+?\\..+?\\..+?", 2575 }, 2576 }, 2577 }, 2578 }, 2579 }, 2580 Redirect: &networking.HTTPRedirect{ 2581 Uri: "example.org", 2582 Authority: "some-authority.default.svc.cluster.local", 2583 RedirectCode: 308, 2584 }, 2585 }, 2586 }, 2587 }, 2588 } 2589 2590 func createVirtualServiceWithRegexMatchingForAllCasesOnQueryParameter() []*config.Config { 2591 ret := []*config.Config{} 2592 regex := "*" 2593 ret = append(ret, &config.Config{ 2594 Meta: config.Meta{ 2595 GroupVersionKind: gvk.VirtualService, 2596 Name: "acme", 2597 }, 2598 Spec: &networking.VirtualService{ 2599 Hosts: []string{}, 2600 Gateways: []string{"some-gateway"}, 2601 Http: []*networking.HTTPRoute{ 2602 { 2603 Match: []*networking.HTTPMatchRequest{ 2604 { 2605 Name: "presence", 2606 QueryParams: map[string]*networking.StringMatch{ 2607 "token": { 2608 MatchType: &networking.StringMatch_Regex{ 2609 Regex: regex, 2610 }, 2611 }, 2612 }, 2613 }, 2614 }, 2615 Redirect: &networking.HTTPRedirect{ 2616 Uri: "example.org", 2617 Authority: "some-authority.default.svc.cluster.local", 2618 RedirectCode: 308, 2619 }, 2620 }, 2621 }, 2622 }, 2623 }) 2624 2625 return ret 2626 } 2627 2628 var virtualServiceMatchingOnSourceNamespace = config.Config{ 2629 Meta: config.Meta{ 2630 GroupVersionKind: gvk.VirtualService, 2631 Name: "acme", 2632 }, 2633 Spec: &networking.VirtualService{ 2634 Hosts: []string{}, 2635 Http: []*networking.HTTPRoute{ 2636 { 2637 Name: "foo", 2638 Match: []*networking.HTTPMatchRequest{ 2639 { 2640 SourceNamespace: "foo", 2641 }, 2642 }, 2643 Route: []*networking.HTTPRouteDestination{ 2644 { 2645 Destination: &networking.Destination{ 2646 Host: "foo.example.org", 2647 Port: &networking.PortSelector{ 2648 Number: 8484, 2649 }, 2650 }, 2651 Weight: 100, 2652 }, 2653 }, 2654 }, 2655 { 2656 Name: "bar", 2657 Match: []*networking.HTTPMatchRequest{ 2658 { 2659 SourceNamespace: "bar", 2660 }, 2661 }, 2662 Route: []*networking.HTTPRouteDestination{ 2663 { 2664 Destination: &networking.Destination{ 2665 Host: "bar.example.org", 2666 Port: &networking.PortSelector{ 2667 Number: 8484, 2668 }, 2669 }, 2670 Weight: 100, 2671 }, 2672 }, 2673 }, 2674 }, 2675 }, 2676 } 2677 2678 var virtualServiceWithStatPrefix = config.Config{ 2679 Meta: config.Meta{ 2680 GroupVersionKind: gvk.VirtualService, 2681 Name: "acme", 2682 }, 2683 Spec: &networking.VirtualService{ 2684 Hosts: []string{}, 2685 Http: []*networking.HTTPRoute{ 2686 { 2687 Name: "foo", 2688 Match: []*networking.HTTPMatchRequest{ 2689 { 2690 Name: "foo", 2691 Uri: &networking.StringMatch{ 2692 MatchType: &networking.StringMatch_Prefix{ 2693 Prefix: "/foo", 2694 }, 2695 }, 2696 StatPrefix: "foo", 2697 }, 2698 { 2699 Name: "baz", 2700 Uri: &networking.StringMatch{ 2701 MatchType: &networking.StringMatch_Prefix{ 2702 Prefix: "/baz", 2703 }, 2704 }, 2705 }, 2706 }, 2707 Route: []*networking.HTTPRouteDestination{ 2708 { 2709 Destination: &networking.Destination{ 2710 Host: "foo.example.org", 2711 Port: &networking.PortSelector{ 2712 Number: 8484, 2713 }, 2714 }, 2715 Weight: 100, 2716 }, 2717 }, 2718 }, 2719 { 2720 Name: "bar", 2721 Match: []*networking.HTTPMatchRequest{ 2722 { 2723 Name: "bar", 2724 Uri: &networking.StringMatch{ 2725 MatchType: &networking.StringMatch_Prefix{ 2726 Prefix: "/bar", 2727 }, 2728 }, 2729 }, 2730 }, 2731 Route: []*networking.HTTPRouteDestination{ 2732 { 2733 Destination: &networking.Destination{ 2734 Host: "bar.example.org", 2735 Port: &networking.PortSelector{ 2736 Number: 8484, 2737 }, 2738 }, 2739 Weight: 100, 2740 }, 2741 }, 2742 }, 2743 }, 2744 }, 2745 } 2746 2747 var portLevelDestinationRule = &networking.DestinationRule{ 2748 Host: "*.example.org", 2749 Subsets: []*networking.Subset{}, 2750 TrafficPolicy: &networking.TrafficPolicy{ 2751 PortLevelSettings: []*networking.TrafficPolicy_PortTrafficPolicy{ 2752 { 2753 LoadBalancer: &networking.LoadBalancerSettings{ 2754 LbPolicy: loadBalancerPolicy("hash-cookie"), 2755 }, 2756 Port: &networking.PortSelector{ 2757 Number: 8484, 2758 }, 2759 }, 2760 }, 2761 }, 2762 } 2763 2764 var portLevelDestinationRuleWithSubsetPolicy = &networking.DestinationRule{ 2765 Host: "*.example.org", 2766 Subsets: []*networking.Subset{networkingSubsetWithPortLevelSettings}, 2767 TrafficPolicy: &networking.TrafficPolicy{ 2768 PortLevelSettings: []*networking.TrafficPolicy_PortTrafficPolicy{ 2769 { 2770 LoadBalancer: &networking.LoadBalancerSettings{ 2771 LbPolicy: loadBalancerPolicy("hash-cookie"), 2772 }, 2773 Port: &networking.PortSelector{ 2774 Number: 8484, 2775 }, 2776 }, 2777 }, 2778 }, 2779 } 2780 2781 var networkingDestinationRule = &networking.DestinationRule{ 2782 Host: "*.example.org", 2783 Subsets: []*networking.Subset{}, 2784 TrafficPolicy: &networking.TrafficPolicy{ 2785 LoadBalancer: &networking.LoadBalancerSettings{ 2786 LbPolicy: loadBalancerPolicy("hash-cookie"), 2787 }, 2788 }, 2789 } 2790 2791 var ( 2792 exampleService = []*model.Service{{Hostname: "*.example.org", Attributes: model.ServiceAttributes{Namespace: "istio-system"}}} 2793 exampleWildcardService = &model.Service{ 2794 Hostname: "*.example.org", 2795 Attributes: model.ServiceAttributes{Namespace: "istio-system"}, 2796 Ports: []*model.Port{{Port: 8080, Protocol: "HTTP"}}, 2797 } 2798 exampleNestedWildcardService = &model.Service{ 2799 Hostname: "goodbye.hello.example.org", 2800 Attributes: model.ServiceAttributes{Namespace: "istio-system"}, 2801 Ports: []*model.Port{{Port: 8080, Protocol: "HTTP"}}, 2802 } 2803 ) 2804 2805 var networkingDestinationRuleWithPortLevelTrafficPolicy = &networking.DestinationRule{ 2806 Host: "*.example.org", 2807 TrafficPolicy: &networking.TrafficPolicy{ 2808 LoadBalancer: &networking.LoadBalancerSettings{ 2809 LbPolicy: loadBalancerPolicy("hash-cookie"), 2810 }, 2811 PortLevelSettings: []*networking.TrafficPolicy_PortTrafficPolicy{ 2812 { 2813 LoadBalancer: &networking.LoadBalancerSettings{ 2814 LbPolicy: loadBalancerPolicy("hash-cookie-1"), 2815 }, 2816 Port: &networking.PortSelector{ 2817 Number: 8080, 2818 }, 2819 }, 2820 }, 2821 }, 2822 } 2823 2824 var networkingSubset = &networking.Subset{ 2825 Name: "some-subset", 2826 Labels: map[string]string{}, 2827 TrafficPolicy: &networking.TrafficPolicy{ 2828 LoadBalancer: &networking.LoadBalancerSettings{ 2829 LbPolicy: &networking.LoadBalancerSettings_ConsistentHash{ 2830 ConsistentHash: &networking.LoadBalancerSettings_ConsistentHashLB{ 2831 HashKey: &networking.LoadBalancerSettings_ConsistentHashLB_HttpCookie{ 2832 HttpCookie: &networking.LoadBalancerSettings_ConsistentHashLB_HTTPCookie{ 2833 Name: "other-cookie", 2834 }, 2835 }, 2836 }, 2837 }, 2838 }, 2839 }, 2840 } 2841 2842 var networkingSubsetWithPortLevelSettings = &networking.Subset{ 2843 Name: "port-level-settings-subset", 2844 Labels: map[string]string{}, 2845 TrafficPolicy: &networking.TrafficPolicy{ 2846 PortLevelSettings: []*networking.TrafficPolicy_PortTrafficPolicy{ 2847 { 2848 LoadBalancer: &networking.LoadBalancerSettings{ 2849 LbPolicy: loadBalancerPolicy("port-level-settings-cookie"), 2850 }, 2851 Port: &networking.PortSelector{ 2852 Number: 8484, 2853 }, 2854 }, 2855 }, 2856 }, 2857 } 2858 2859 func TestSortVHostRoutes(t *testing.T) { 2860 first := []*envoyroute.Route{ 2861 {Match: &envoyroute.RouteMatch{PathSpecifier: &envoyroute.RouteMatch_Prefix{Prefix: "/"}}}, 2862 {Match: &envoyroute.RouteMatch{PathSpecifier: &envoyroute.RouteMatch_Path{Path: "/path1"}}}, 2863 {Match: &envoyroute.RouteMatch{PathSpecifier: &envoyroute.RouteMatch_Prefix{Prefix: "/prefix1"}}}, 2864 {Match: &envoyroute.RouteMatch{PathSpecifier: &envoyroute.RouteMatch_SafeRegex{ 2865 SafeRegex: &matcher.RegexMatcher{ 2866 Regex: ".*?regex1", 2867 }, 2868 }}}, 2869 } 2870 wantFirst := []*envoyroute.Route{ 2871 {Match: &envoyroute.RouteMatch{PathSpecifier: &envoyroute.RouteMatch_Path{Path: "/path1"}}}, 2872 {Match: &envoyroute.RouteMatch{PathSpecifier: &envoyroute.RouteMatch_Prefix{Prefix: "/prefix1"}}}, 2873 {Match: &envoyroute.RouteMatch{PathSpecifier: &envoyroute.RouteMatch_SafeRegex{ 2874 SafeRegex: &matcher.RegexMatcher{ 2875 Regex: ".*?regex1", 2876 }, 2877 }}}, 2878 {Match: &envoyroute.RouteMatch{PathSpecifier: &envoyroute.RouteMatch_Prefix{Prefix: "/"}}}, 2879 } 2880 second := []*envoyroute.Route{ 2881 {Match: &envoyroute.RouteMatch{PathSpecifier: &envoyroute.RouteMatch_Path{Path: "/path12"}}}, 2882 {Match: &envoyroute.RouteMatch{PathSpecifier: &envoyroute.RouteMatch_Prefix{Prefix: "/prefix12"}}}, 2883 {Match: &envoyroute.RouteMatch{PathSpecifier: &envoyroute.RouteMatch_SafeRegex{ 2884 SafeRegex: &matcher.RegexMatcher{ 2885 Regex: ".*?regex12", 2886 }, 2887 }}}, 2888 {Match: &envoyroute.RouteMatch{ 2889 PathSpecifier: &envoyroute.RouteMatch_SafeRegex{ 2890 SafeRegex: &matcher.RegexMatcher{ 2891 Regex: "*", 2892 }, 2893 }, 2894 Headers: []*envoyroute.HeaderMatcher{ 2895 { 2896 Name: "foo", 2897 HeaderMatchSpecifier: &envoyroute.HeaderMatcher_StringMatch{ 2898 StringMatch: &matcher.StringMatcher{MatchPattern: &matcher.StringMatcher_Exact{Exact: "bar"}}, 2899 }, 2900 InvertMatch: false, 2901 }, 2902 }, 2903 }}, 2904 } 2905 2906 wantSecond := []*envoyroute.Route{ 2907 {Match: &envoyroute.RouteMatch{PathSpecifier: &envoyroute.RouteMatch_Path{Path: "/path12"}}}, 2908 {Match: &envoyroute.RouteMatch{PathSpecifier: &envoyroute.RouteMatch_Prefix{Prefix: "/prefix12"}}}, 2909 {Match: &envoyroute.RouteMatch{PathSpecifier: &envoyroute.RouteMatch_SafeRegex{ 2910 SafeRegex: &matcher.RegexMatcher{ 2911 Regex: ".*?regex12", 2912 }, 2913 }}}, 2914 {Match: &envoyroute.RouteMatch{ 2915 PathSpecifier: &envoyroute.RouteMatch_SafeRegex{ 2916 SafeRegex: &matcher.RegexMatcher{ 2917 Regex: "*", 2918 }, 2919 }, 2920 Headers: []*envoyroute.HeaderMatcher{ 2921 { 2922 Name: "foo", 2923 HeaderMatchSpecifier: &envoyroute.HeaderMatcher_StringMatch{ 2924 StringMatch: &matcher.StringMatcher{MatchPattern: &matcher.StringMatcher_Exact{Exact: "bar"}}, 2925 }, 2926 InvertMatch: false, 2927 }, 2928 }, 2929 }}, 2930 } 2931 2932 testCases := []struct { 2933 name string 2934 in []*envoyroute.Route 2935 expected []*envoyroute.Route 2936 }{ 2937 { 2938 name: "routes with catchall match", 2939 in: first, 2940 expected: wantFirst, 2941 }, 2942 { 2943 name: "routes without catchall match", 2944 in: second, 2945 expected: wantSecond, 2946 }, 2947 } 2948 2949 for _, tc := range testCases { 2950 t.Run(tc.name, func(t *testing.T) { 2951 got := route.SortVHostRoutes(tc.in) 2952 if !reflect.DeepEqual(tc.expected, got) { 2953 t.Errorf("SortVHostRoutes: \n") 2954 t.Errorf("got: \n") 2955 for _, g := range got { 2956 t.Errorf("%v\n", g.Match.PathSpecifier) 2957 } 2958 t.Errorf("want: \n") 2959 for _, g := range tc.expected { 2960 t.Errorf("%v\n", g.Match.PathSpecifier) 2961 } 2962 } 2963 }) 2964 } 2965 }