istio.io/istio@v0.0.0-20240520182934-d79c90f27776/pilot/pkg/networking/core/gateway_simulation_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 core_test
    16  
    17  import (
    18  	"testing"
    19  
    20  	"istio.io/istio/pilot/pkg/model"
    21  	"istio.io/istio/pilot/pkg/simulation"
    22  	"istio.io/istio/pilot/test/xds"
    23  	"istio.io/istio/pilot/test/xdstest"
    24  	"istio.io/istio/pkg/env"
    25  	"istio.io/istio/pkg/test/util/tmpl"
    26  )
    27  
    28  func TestDisablePortTranslation(t *testing.T) {
    29  	virtualServices := `
    30  apiVersion: networking.istio.io/v1alpha3
    31  kind: VirtualService
    32  metadata:
    33    name: a
    34  spec:
    35    hosts:
    36    - "example.com"
    37    gateways:
    38    - gateway80
    39    - gateway8081
    40    http:
    41    - match:
    42      - uri:
    43          prefix: /
    44      route:
    45      - destination:
    46          host: a
    47          port:
    48            number: 80
    49  ---
    50  apiVersion: networking.istio.io/v1alpha3
    51  kind: VirtualService
    52  metadata:
    53    name: b
    54  spec:
    55    hosts:
    56    - "example.com"
    57    gateways:
    58    - gateway80
    59    - gateway8081
    60    http:
    61    - match:
    62      - uri:
    63          prefix: /
    64      route:
    65      - destination:
    66          host: b
    67          port:
    68            number: 8081`
    69  	runGatewayTest(t,
    70  		simulationTest{
    71  			name: "multiple target port, target port first",
    72  			config: createGateway("gateway80", "", `
    73  port:
    74    number: 80
    75    name: http
    76    protocol: HTTP
    77  hosts:
    78  - "example.com"
    79  `) + createGateway("gateway8081", "", `
    80  port:
    81    number: 8081
    82    name: http
    83    protocol: HTTP
    84  hosts:
    85  - "example.com"
    86  `) + `
    87  ---
    88  apiVersion: networking.istio.io/v1alpha3
    89  kind: ServiceEntry
    90  metadata:
    91    name: service-instance
    92    namespace: istio-system
    93    labels:
    94      experimental.istio.io/disable-gateway-port-translation: "true"
    95  spec:
    96    hosts: ["a.example.com"]
    97    ports:
    98    - number: 80
    99      targetPort: 8081
   100      name: http
   101      protocol: HTTP
   102    resolution: STATIC
   103    location: MESH_INTERNAL
   104    endpoints:
   105    - address: 1.1.1.1
   106      labels:
   107        istio: ingressgateway
   108  ---
   109  apiVersion: networking.istio.io/v1alpha3
   110  kind: ServiceEntry
   111  metadata:
   112    name: service-instance-2
   113    namespace: istio-system
   114  spec:
   115    hosts: ["b.example.com"]
   116    ports:
   117    - number: 80
   118      targetPort: 8080
   119      name: http
   120      protocol: HTTP
   121    resolution: STATIC
   122    location: MESH_INTERNAL
   123    endpoints:
   124    - address: 1.1.1.1
   125      labels:
   126        istio: ingressgateway
   127  ---
   128  ` + virtualServices,
   129  			calls: []simulation.Expect{
   130  				{
   131  					Name: "target port 1",
   132  					Call: simulation.Call{
   133  						Port:       8080,
   134  						HostHeader: "example.com",
   135  						Protocol:   simulation.HTTP,
   136  					},
   137  					Result: simulation.Result{
   138  						ListenerMatched:    "0.0.0.0_8080",
   139  						ClusterMatched:     "outbound|80||a.default",
   140  						RouteConfigMatched: "http.8080",
   141  						VirtualHostMatched: "example.com:80",
   142  					},
   143  				},
   144  				{
   145  					Name: "target port 2",
   146  					Call: simulation.Call{
   147  						Port:       8081,
   148  						HostHeader: "example.com",
   149  						Protocol:   simulation.HTTP,
   150  					},
   151  					Result: simulation.Result{
   152  						ListenerMatched:    "0.0.0.0_8081",
   153  						ClusterMatched:     "outbound|80||a.default",
   154  						RouteConfigMatched: "http.8081",
   155  						VirtualHostMatched: "example.com:8081",
   156  					},
   157  				},
   158  			},
   159  		},
   160  		simulationTest{
   161  			name: "multiple target port, service port first",
   162  			config: createGateway("gateway80", "", `
   163  port:
   164    number: 80
   165    name: http
   166    protocol: HTTP
   167  hosts:
   168  - "example.com"
   169  `) + createGateway("gateway8081", "", `
   170  port:
   171    number: 8081
   172    name: http
   173    protocol: HTTP
   174  hosts:
   175  - "example.com"
   176  `) + `
   177  ---
   178  apiVersion: networking.istio.io/v1alpha3
   179  kind: ServiceEntry
   180  metadata:
   181    name: service-instance
   182    namespace: istio-system
   183    labels:
   184      experimental.istio.io/disable-gateway-port-translation: "true"
   185  spec:
   186    hosts: ["b.example.com"]
   187    ports:
   188    - number: 80
   189      targetPort: 8081
   190      name: http
   191      protocol: HTTP
   192    resolution: STATIC
   193    location: MESH_INTERNAL
   194    endpoints:
   195    - address: 1.1.1.1
   196      labels:
   197        istio: ingressgateway
   198  ---
   199  apiVersion: networking.istio.io/v1alpha3
   200  kind: ServiceEntry
   201  metadata:
   202    name: service-instance-2
   203    namespace: istio-system
   204  spec:
   205    hosts: ["a.example.com"]
   206    ports:
   207    - number: 80
   208      targetPort: 8080
   209      name: http
   210      protocol: HTTP
   211    resolution: STATIC
   212    location: MESH_INTERNAL
   213    endpoints:
   214    - address: 1.1.1.1
   215      labels:
   216        istio: ingressgateway
   217  ---
   218  ` + virtualServices,
   219  			calls: []simulation.Expect{
   220  				{
   221  					Name: "target port 1",
   222  					Call: simulation.Call{
   223  						Port:       8080,
   224  						HostHeader: "example.com",
   225  						Protocol:   simulation.HTTP,
   226  					},
   227  					Result: simulation.Result{
   228  						ListenerMatched:    "0.0.0.0_8080",
   229  						ClusterMatched:     "outbound|80||a.default",
   230  						RouteConfigMatched: "http.8080",
   231  						VirtualHostMatched: "example.com:80",
   232  					},
   233  				},
   234  				{
   235  					Name: "target port 2",
   236  					Call: simulation.Call{
   237  						Port:       8081,
   238  						HostHeader: "example.com",
   239  						Protocol:   simulation.HTTP,
   240  					},
   241  					Result: simulation.Result{
   242  						ListenerMatched:    "0.0.0.0_8081",
   243  						ClusterMatched:     "outbound|80||a.default",
   244  						RouteConfigMatched: "http.8081",
   245  						VirtualHostMatched: "example.com:8081",
   246  					},
   247  				},
   248  			},
   249  		})
   250  }
   251  
   252  func TestHTTPGateway(t *testing.T) {
   253  	httpServer := `port:
   254    number: 80
   255    name: http
   256    protocol: HTTP
   257  hosts:
   258  - "foo.bar"`
   259  	simpleRoute := `apiVersion: networking.istio.io/v1alpha3
   260  kind: VirtualService
   261  metadata:
   262    name: vs
   263  spec:
   264    hosts:
   265    - "example.com"
   266    gateways:
   267    - gateway
   268    http:
   269    - match:
   270      - uri:
   271          prefix: /
   272      route:
   273      - destination:
   274          host: b
   275  ---
   276  `
   277  	runGatewayTest(t,
   278  		simulationTest{
   279  			name:   "no virtual services",
   280  			config: createGateway("", "", httpServer),
   281  			calls: []simulation.Expect{
   282  				{
   283  					// Expect listener, but no routing
   284  					Name: "defined port",
   285  					Call: simulation.Call{
   286  						Port:       80,
   287  						HostHeader: "foo.bar",
   288  						Protocol:   simulation.HTTP,
   289  					},
   290  					Result: simulation.Result{
   291  						Error:              simulation.ErrNoRoute,
   292  						ListenerMatched:    "0.0.0.0_80",
   293  						RouteConfigMatched: "http.80",
   294  						VirtualHostMatched: "blackhole:80",
   295  					},
   296  				},
   297  				{
   298  					// There will be no listener
   299  					Name: "undefined port",
   300  					Call: simulation.Call{
   301  						Port:       81,
   302  						HostHeader: "foo.bar",
   303  						Protocol:   simulation.HTTP,
   304  					},
   305  					Result: simulation.Result{
   306  						Error: simulation.ErrNoListener,
   307  					},
   308  				},
   309  			},
   310  		},
   311  		simulationTest{
   312  			name: "simple http and virtual service",
   313  			config: createGateway("gateway", "", httpServer) + `
   314  apiVersion: networking.istio.io/v1alpha3
   315  kind: VirtualService
   316  metadata:
   317    name: bookinfo
   318  spec:
   319    hosts:
   320    - "*"
   321    gateways:
   322    - gateway
   323    http:
   324    - match:
   325      - uri:
   326          exact: /productpage
   327      route:
   328      - destination:
   329          host: productpage
   330          port:
   331            number: 9080
   332  `,
   333  			calls: []simulation.Expect{
   334  				{
   335  					Name: "uri mismatch",
   336  					Call: simulation.Call{
   337  						Port:       80,
   338  						HostHeader: "foo.bar",
   339  						Protocol:   simulation.HTTP,
   340  					},
   341  					Result: simulation.Result{
   342  						// We didn't match the URI
   343  						Error:              simulation.ErrNoRoute,
   344  						ListenerMatched:    "0.0.0.0_80",
   345  						RouteConfigMatched: "http.80",
   346  						VirtualHostMatched: "foo.bar:80",
   347  					},
   348  				},
   349  				{
   350  					Name: "host mismatch",
   351  					Call: simulation.Call{
   352  						Port:       80,
   353  						HostHeader: "bad.bar",
   354  						Protocol:   simulation.HTTP,
   355  					},
   356  					Result: simulation.Result{
   357  						// We didn't match the host
   358  						Error:              simulation.ErrNoVirtualHost,
   359  						ListenerMatched:    "0.0.0.0_80",
   360  						RouteConfigMatched: "http.80",
   361  					},
   362  				},
   363  				{
   364  					Name: "match",
   365  					Call: simulation.Call{
   366  						Port:       80,
   367  						HostHeader: "foo.bar",
   368  						Path:       "/productpage",
   369  						Protocol:   simulation.HTTP,
   370  					},
   371  					Result: simulation.Result{
   372  						ListenerMatched:    "0.0.0.0_80",
   373  						VirtualHostMatched: "foo.bar:80",
   374  						ClusterMatched:     "outbound|9080||productpage.default",
   375  					},
   376  				},
   377  			},
   378  		},
   379  		simulationTest{
   380  			name: "virtual service merging",
   381  			config: createGateway("gateway", "", `port:
   382    number: 80
   383    name: http
   384    protocol: HTTP
   385  hosts:
   386  - "*.example.com"`) + `
   387  apiVersion: networking.istio.io/v1alpha3
   388  kind: VirtualService
   389  metadata:
   390    name: a
   391  spec:
   392    hosts:
   393    - "a.example.com"
   394    gateways:
   395    - gateway
   396    http:
   397    - match:
   398      - uri:
   399          prefix: /
   400      route:
   401      - destination:
   402          host: a
   403          port:
   404            number: 80
   405  ---
   406  apiVersion: networking.istio.io/v1alpha3
   407  kind: VirtualService
   408  metadata:
   409    name: b
   410  spec:
   411    hosts:
   412    - "b.example.com"
   413    gateways:
   414    - gateway
   415    http:
   416    - match:
   417      - uri:
   418          prefix: /
   419      route:
   420      - destination:
   421          host: b
   422          port:
   423            number: 80
   424  `,
   425  			calls: []simulation.Expect{
   426  				{
   427  					Name: "a",
   428  					Call: simulation.Call{
   429  						Port:       80,
   430  						HostHeader: "a.example.com",
   431  						Protocol:   simulation.HTTP,
   432  					},
   433  					Result: simulation.Result{ClusterMatched: "outbound|80||a.default"},
   434  				},
   435  				{
   436  					Name: "b",
   437  					Call: simulation.Call{
   438  						Port:       80,
   439  						HostHeader: "b.example.com",
   440  						Protocol:   simulation.HTTP,
   441  					},
   442  					Result: simulation.Result{ClusterMatched: "outbound|80||b.default"},
   443  				},
   444  				{
   445  					Name: "undefined hostname",
   446  					Call: simulation.Call{
   447  						Port:       80,
   448  						HostHeader: "c.example.com",
   449  						Protocol:   simulation.HTTP,
   450  					},
   451  					Result: simulation.Result{Error: simulation.ErrNoVirtualHost},
   452  				},
   453  			},
   454  		},
   455  		simulationTest{
   456  			name: "httpsRedirect without routes",
   457  			config: createGateway("gateway", "", `port:
   458    number: 80
   459    name: http
   460    protocol: HTTP
   461  hosts:
   462  - "example.com"
   463  tls:
   464    httpsRedirect: true`),
   465  			calls: []simulation.Expect{
   466  				{
   467  					Name: "request",
   468  					Call: simulation.Call{
   469  						Port:       80,
   470  						HostHeader: "example.com",
   471  						Protocol:   simulation.HTTP,
   472  					},
   473  					Result: simulation.Result{
   474  						Error:              simulation.ErrTLSRedirect,
   475  						ListenerMatched:    "0.0.0.0_80",
   476  						VirtualHostMatched: "example.com:80",
   477  						RouteConfigMatched: "http.80",
   478  						StrictMatch:        true,
   479  					},
   480  				},
   481  			},
   482  		},
   483  		simulationTest{
   484  			// This should be the same as without, we elide the routes anyways since they always
   485  			// redirect
   486  			name: "httpsRedirect with routes",
   487  			config: createGateway("gateway", "", `port:
   488    number: 80
   489    name: http
   490    protocol: HTTP
   491  hosts:
   492  - "example.com"
   493  tls:
   494    httpsRedirect: true`) + simpleRoute,
   495  			calls: []simulation.Expect{
   496  				{
   497  					Name: "request",
   498  					Call: simulation.Call{
   499  						Port:       80,
   500  						HostHeader: "example.com",
   501  						Protocol:   simulation.HTTP,
   502  					},
   503  					Result: simulation.Result{
   504  						Error:              simulation.ErrTLSRedirect,
   505  						ListenerMatched:    "0.0.0.0_80",
   506  						VirtualHostMatched: "example.com:80",
   507  						RouteConfigMatched: "http.80",
   508  						StrictMatch:        true,
   509  					},
   510  				},
   511  			},
   512  		},
   513  		simulationTest{
   514  			// A common example of when this would occur is when a user defines their Gateway with
   515  			// httpsRedirect, then cert-manager creates an Ingress which requires sending HTTP
   516  			// traffic Unfortunately, Envoy doesn't have a good way to do HTTPS redirect per-route.
   517  			name: "mixed httpsRedirect with routes",
   518  			config: createGateway("gateway", "", `port:
   519    number: 80
   520    name: http
   521    protocol: HTTP
   522  hosts:
   523  - "example.com"
   524  tls:
   525    httpsRedirect: true`) + createGateway("gateway2", "", `port:
   526    number: 80
   527    name: http
   528    protocol: HTTP
   529  hosts:
   530  - "example.com"`) + simpleRoute,
   531  			calls: []simulation.Expect{
   532  				{
   533  					Name: "request",
   534  					Call: simulation.Call{
   535  						Port:       80,
   536  						HostHeader: "example.com",
   537  						Protocol:   simulation.HTTP,
   538  					},
   539  					Result: simulation.Result{
   540  						Error:              simulation.ErrTLSRedirect,
   541  						ListenerMatched:    "0.0.0.0_80",
   542  						VirtualHostMatched: "example.com:80",
   543  						RouteConfigMatched: "http.80",
   544  						StrictMatch:        true,
   545  					},
   546  				},
   547  			},
   548  		},
   549  		simulationTest{
   550  			name: "httpsRedirect on https",
   551  			config: createGateway("gateway", "", `port:
   552    number: 443
   553    name: https
   554    protocol: HTTPS
   555  hosts:
   556  - "example.com"
   557  tls:
   558    httpsRedirect: true
   559    mode: SIMPLE
   560    credentialName: test`) + simpleRoute,
   561  			calls: []simulation.Expect{
   562  				{
   563  					Name: "request",
   564  					Call: simulation.Call{
   565  						Port:       443,
   566  						HostHeader: "example.com",
   567  						Protocol:   simulation.HTTP,
   568  						TLS:        simulation.TLS,
   569  					},
   570  					Result: simulation.Result{
   571  						ListenerMatched:    "0.0.0.0_443",
   572  						VirtualHostMatched: "example.com:443",
   573  						RouteConfigMatched: "https.443.https.gateway.default",
   574  						ClusterMatched:     "outbound|443||b.default",
   575  						StrictMatch:        true,
   576  					},
   577  				},
   578  			},
   579  		},
   580  	)
   581  }
   582  
   583  func TestGatewayConflicts(t *testing.T) {
   584  	tcpServer := `port:
   585    number: 80
   586    name: tcp
   587    protocol: TCP
   588  hosts:
   589  - "foo.bar"`
   590  	httpServer := `port:
   591    number: 80
   592    name: http
   593    protocol: HTTP
   594  hosts:
   595  - "foo.bar"`
   596  	tlsServer := `hosts:
   597    - ./*
   598  port:
   599    name: https-ingress
   600    number: 443
   601    protocol: HTTPS
   602  tls:
   603    credentialName: sds-credential
   604    mode: SIMPLE`
   605  	gatewayCollision := `
   606  apiVersion: networking.istio.io/v1alpha3
   607  kind: VirtualService
   608  metadata:
   609    name: bookinfo
   610  spec:
   611    hosts:
   612    - "*"
   613    gateways:
   614    - istio-system/gateway
   615    tcp:
   616    - route:
   617      - destination:
   618          host: productpage
   619          port:
   620            number: 9080
   621  `
   622  	runGatewayTest(t,
   623  		simulationTest{
   624  			name: "duplicate cross namespace gateway collision",
   625  			config: createGateway("gateway", "istio-system", tcpServer) +
   626  				createGateway("gateway", "alpha", tcpServer) + // namespace comes before istio-system
   627  
   628  				gatewayCollision,
   629  			calls: []simulation.Expect{
   630  				{
   631  					Name: "call",
   632  					Call: simulation.Call{Port: 80, Protocol: simulation.TCP},
   633  					// TODO(https://github.com/istio/istio/issues/21394) This is a bug!
   634  					// Should have identical result to the test below
   635  					Result: simulation.Result{Error: simulation.ErrNoListener},
   636  				},
   637  			},
   638  		},
   639  		simulationTest{
   640  			name: "duplicate cross namespace gateway collision - selected first",
   641  			config: createGateway("gateway", "istio-system", tcpServer) +
   642  				createGateway("gateway", "zeta", tcpServer) + // namespace comes after istio-system
   643  				gatewayCollision,
   644  			calls: []simulation.Expect{
   645  				{
   646  					Name:   "call",
   647  					Call:   simulation.Call{Port: 80, Protocol: simulation.TCP},
   648  					Result: simulation.Result{ListenerMatched: "0.0.0.0_80", ClusterMatched: "outbound|9080||productpage.default"},
   649  				},
   650  			},
   651  		},
   652  		simulationTest{
   653  			name:           "duplicate tls gateway",
   654  			skipValidation: true,
   655  			// Create the same gateway in two namespaces
   656  			config: createGateway("", "istio-system", tlsServer) +
   657  				createGateway("", "default", tlsServer),
   658  			calls: []simulation.Expect{
   659  				{
   660  					// TODO(https://github.com/istio/istio/issues/24638) This is a bug!
   661  					// We should not have multiple matches, envoy will NACK this
   662  					Name:   "call",
   663  					Call:   simulation.Call{Port: 443, Protocol: simulation.HTTP, TLS: simulation.TLS, HostHeader: "foo.bar"},
   664  					Result: simulation.Result{Error: simulation.ErrMultipleFilterChain},
   665  				},
   666  			},
   667  		},
   668  		simulationTest{
   669  			name: "duplicate tls virtual service",
   670  			// Create the same virtual service in two namespaces
   671  			config: `
   672  apiVersion: networking.istio.io/v1alpha3
   673  kind: Gateway
   674  metadata:
   675    name: ingressgateway
   676    namespace: istio-system
   677  spec:
   678    selector:
   679      istio: ingressgateway
   680    servers:
   681    - hosts:
   682      - '*.example.com'
   683      port:
   684        name: https
   685        number: 443
   686        protocol: HTTPS
   687      tls:
   688        mode: PASSTHROUGH
   689  ---
   690  apiVersion: networking.istio.io/v1alpha3
   691  kind: VirtualService
   692  metadata:
   693    name: vs1
   694    namespace: default
   695  spec:
   696    gateways:
   697    - istio-system/ingressgateway
   698    hosts:
   699    - mysite.example.com
   700    tls:
   701    - match:
   702      - port: 443
   703        sniHosts:
   704        - mysite.example.com
   705      route:
   706      - destination:
   707          host: mysite.default.svc.cluster.local
   708          port:
   709            number: 443
   710  ---
   711  apiVersion: networking.istio.io/v1alpha3
   712  kind: VirtualService
   713  metadata:
   714    name: vs2
   715    namespace: default
   716  spec:
   717    gateways:
   718    - istio-system/ingressgateway
   719    hosts:
   720    - mysite.example.com
   721    tls:
   722    - match:
   723      - port: 443
   724        sniHosts:
   725        - mysite.example.com
   726      route:
   727      - destination:
   728          host: mysite.default.svc.cluster.local
   729          port:
   730            number: 443
   731  `,
   732  			calls: []simulation.Expect{
   733  				{
   734  					Name: "call",
   735  					Call: simulation.Call{Port: 443, Protocol: simulation.HTTP, TLS: simulation.TLS, HostHeader: "mysite.example.com"},
   736  					Result: simulation.Result{
   737  						ListenerMatched: "0.0.0.0_443",
   738  						ClusterMatched:  "outbound|443||mysite.default.svc.cluster.local",
   739  					},
   740  				},
   741  			},
   742  		},
   743  		simulationTest{
   744  			// TODO(https://github.com/istio/istio/issues/27481) this may be a bug. At very least, this should have indication to user
   745  			name: "multiple protocols on a port - tcp first",
   746  			config: createGateway("alpha", "", tcpServer) +
   747  				createGateway("beta", "", httpServer),
   748  			calls: []simulation.Expect{
   749  				{
   750  					Name: "call tcp",
   751  					// TCP takes precedence. Since we have no tcp routes, this will result in no listeners
   752  					Call: simulation.Call{
   753  						Port:     80,
   754  						Protocol: simulation.TCP,
   755  					},
   756  					Result: simulation.Result{
   757  						Error: simulation.ErrNoListener,
   758  					},
   759  				},
   760  			},
   761  		},
   762  		simulationTest{
   763  			// TODO(https://github.com/istio/istio/issues/27481) this may be a bug. At very least, this should have indication to user
   764  			name: "multiple protocols on a port - http first",
   765  			config: createGateway("beta", "", tcpServer) +
   766  				createGateway("alpha", "", httpServer),
   767  			calls: []simulation.Expect{
   768  				{
   769  					// Port define in gateway, but no virtual services
   770  					// Expect a 404
   771  					// HTTP protocol takes precedence
   772  					Name: "call http",
   773  					Call: simulation.Call{
   774  						Port:       80,
   775  						HostHeader: "foo.bar",
   776  						Protocol:   simulation.HTTP,
   777  					},
   778  					Result: simulation.Result{
   779  						Error:              simulation.ErrNoRoute,
   780  						ListenerMatched:    "0.0.0.0_80",
   781  						RouteConfigMatched: "http.80",
   782  						VirtualHostMatched: "blackhole:80",
   783  					},
   784  				},
   785  			},
   786  		},
   787  		simulationTest{
   788  			name: "multiple wildcards with virtual service disambiguator",
   789  			config: createGateway("alpha", "", `
   790  hosts:
   791    - ns-1/*.example.com
   792  port:
   793    name: http
   794    number: 80
   795    protocol: HTTP`) +
   796  				createGateway("beta", "", `
   797  hosts:
   798    - ns-2/*.example.com
   799  port:
   800    name: http
   801    number: 80
   802    protocol: HTTP`) + `
   803  apiVersion: networking.istio.io/v1alpha3
   804  kind: VirtualService
   805  metadata:
   806    name: default
   807    namespace: ns-1
   808  spec:
   809    hosts:
   810    - "ns-1.example.com"
   811    gateways:
   812    - default/alpha
   813    http:
   814    - route:
   815      - destination:
   816          host: echo
   817  ---
   818  apiVersion: networking.istio.io/v1alpha3
   819  kind: VirtualService
   820  metadata:
   821    name: default
   822    namespace: ns-2
   823  spec:
   824    hosts:
   825    - "ns-2.example.com"
   826    gateways:
   827    - default/beta
   828    http:
   829    - route:
   830      - destination:
   831          host: echo
   832  `,
   833  			calls: []simulation.Expect{
   834  				{
   835  					Name: "ns-1",
   836  					Call: simulation.Call{
   837  						Port:       80,
   838  						HostHeader: "ns-1.example.com",
   839  						Protocol:   simulation.HTTP,
   840  					},
   841  					Result: simulation.Result{
   842  						ListenerMatched:    "0.0.0.0_80",
   843  						RouteConfigMatched: "http.80",
   844  						ClusterMatched:     "outbound|80||echo.ns-1",
   845  					},
   846  				},
   847  				{
   848  					Name: "ns-2",
   849  					Call: simulation.Call{
   850  						Port:       80,
   851  						HostHeader: "ns-2.example.com",
   852  						Protocol:   simulation.HTTP,
   853  					},
   854  					Result: simulation.Result{
   855  						ListenerMatched:    "0.0.0.0_80",
   856  						RouteConfigMatched: "http.80",
   857  						ClusterMatched:     "outbound|80||echo.ns-2",
   858  					},
   859  				},
   860  			},
   861  		},
   862  		simulationTest{
   863  			name: "multiple virtual services that should become separate vhosts",
   864  			config: `
   865  apiVersion: networking.istio.io/v1beta1
   866  kind: Gateway
   867  metadata:
   868    name: public-gw
   869    namespace: istio-system
   870  spec:
   871    selector:
   872      istio: ingressgateway
   873    servers:
   874      - port:
   875          number: 80
   876          name: http
   877          protocol: HTTP
   878        hosts:
   879          - foo.example.com
   880          - bar.example.com
   881  ---
   882  apiVersion: networking.istio.io/v1alpha3
   883  kind: VirtualService
   884  metadata:
   885    name: foobar-vs
   886    namespace: default
   887    creationTimestamp: 2010-01-01T00:00:00Z
   888  spec:
   889    hosts:
   890      - foo.example.com
   891      - bar.example.com
   892    gateways:
   893      - istio-system/public-gw
   894    http:
   895      - name: foobar
   896        match:
   897          - uri:
   898              prefix: /1
   899          - uri:
   900              prefix: /2
   901          - uri:
   902              prefix: /3
   903        route:
   904          - destination:
   905              host: echo-foobar
   906  ---
   907  apiVersion: networking.istio.io/v1beta1
   908  kind: VirtualService
   909  metadata:
   910    name: foo-vs
   911    namespace: default
   912    creationTimestamp: 2015-01-01T00:00:00Z
   913  spec:
   914    gateways:
   915      - istio-system/public-gw
   916    hosts:
   917      - foo.example.com
   918    http:
   919      - name: foo
   920        match:
   921          - uri:
   922              prefix: /foo
   923        route:
   924          - destination:
   925              host: echo-foo
   926              port:
   927                number: 80
   928  ---
   929  apiVersion: networking.istio.io/v1beta1
   930  kind: VirtualService
   931  metadata:
   932    name: bar-vs
   933    namespace: default
   934    creationTimestamp: 2020-01-01T00:00:00Z
   935  spec:
   936    gateways:
   937      - istio-system/public-gw
   938    hosts:
   939      - bar.example.com
   940    http:
   941      - name: bar
   942        match:
   943          - uri:
   944              prefix: /bar
   945        route:
   946          - destination:
   947              host: echo-bar
   948              port:
   949                number: 80
   950  `,
   951  			calls: []simulation.Expect{
   952  				{
   953  					Name: "foobar",
   954  					Call: simulation.Call{
   955  						Port:       80,
   956  						HostHeader: "foo.example.com",
   957  						Protocol:   simulation.HTTP,
   958  						Path:       "/1",
   959  					},
   960  					Result: simulation.Result{
   961  						ListenerMatched:    "0.0.0.0_80",
   962  						RouteConfigMatched: "http.80",
   963  						ClusterMatched:     "outbound|80||echo-foobar.default",
   964  					},
   965  				},
   966  				{
   967  					Name: "foo",
   968  					Call: simulation.Call{
   969  						Port:       80,
   970  						HostHeader: "foo.example.com",
   971  						Protocol:   simulation.HTTP,
   972  						Path:       "/foo",
   973  					},
   974  					Result: simulation.Result{
   975  						ListenerMatched:    "0.0.0.0_80",
   976  						RouteConfigMatched: "http.80",
   977  						ClusterMatched:     "outbound|80||echo-foo.default",
   978  					},
   979  				},
   980  				{
   981  					Name: "bar",
   982  					Call: simulation.Call{
   983  						Port:       80,
   984  						HostHeader: "bar.example.com",
   985  						Protocol:   simulation.HTTP,
   986  						Path:       "/bar",
   987  					},
   988  					Result: simulation.Result{
   989  						ListenerMatched:    "0.0.0.0_80",
   990  						RouteConfigMatched: "http.80",
   991  						ClusterMatched:     "outbound|80||echo-bar.default",
   992  					},
   993  				},
   994  			},
   995  		},
   996  	)
   997  }
   998  
   999  func TestIngress(t *testing.T) {
  1000  	cfg := `
  1001  apiVersion: networking.k8s.io/v1
  1002  kind: Ingress
  1003  metadata:
  1004    name: {{.Name}}
  1005    namespace: default
  1006    creationTimestamp: "{{.Time}}"
  1007    annotations:
  1008      kubernetes.io/ingress.class: istio
  1009  spec:
  1010    rules:
  1011    - host: example.com
  1012      http:
  1013        paths:
  1014        - pathType: Prefix
  1015          backend:
  1016            service:
  1017              name: {{.Name}}
  1018              port:
  1019                number: 80
  1020          path: /{{.Name}}
  1021    tls:
  1022    - hosts:
  1023      - example.com
  1024      secretName: ingressgateway-certs
  1025  ---`
  1026  	runGatewayTest(t, simulationTest{
  1027  		name: "ingress shared TLS cert conflict - beta first",
  1028  		// TODO(https://github.com/istio/istio/issues/24385) this is a bug
  1029  		// "alpha" is created after "beta". This triggers a mismatch in the conflict resolution logic in Ingress and VirtualService, leading to unexpected results
  1030  		kubeConfig: tmpl.MustEvaluate(cfg, map[string]string{"Name": "alpha", "Time": "2020-01-01T00:00:00Z"}) +
  1031  			tmpl.MustEvaluate(cfg, map[string]string{"Name": "beta", "Time": "2010-01-01T00:00:00Z"}),
  1032  		calls: []simulation.Expect{
  1033  			{
  1034  				Name: "http alpha",
  1035  				Call: simulation.Call{
  1036  					Port:       80,
  1037  					HostHeader: "example.com",
  1038  					Path:       "/alpha",
  1039  					Protocol:   simulation.HTTP,
  1040  				},
  1041  				Result: simulation.Result{
  1042  					ListenerMatched:    "0.0.0.0_80",
  1043  					RouteConfigMatched: "http.80",
  1044  					ClusterMatched:     "outbound|80||alpha.default.svc.cluster.local",
  1045  				},
  1046  			},
  1047  			{
  1048  				Name: "http beta",
  1049  				Call: simulation.Call{
  1050  					Port:       80,
  1051  					HostHeader: "example.com",
  1052  					Path:       "/beta",
  1053  					Protocol:   simulation.HTTP,
  1054  				},
  1055  				Result: simulation.Result{
  1056  					ListenerMatched:    "0.0.0.0_80",
  1057  					RouteConfigMatched: "http.80",
  1058  					ClusterMatched:     "outbound|80||beta.default.svc.cluster.local",
  1059  				},
  1060  			},
  1061  			{
  1062  				Name: "https alpha",
  1063  				Call: simulation.Call{
  1064  					Port:       443,
  1065  					HostHeader: "example.com",
  1066  					Path:       "/alpha",
  1067  					Protocol:   simulation.HTTP,
  1068  					TLS:        simulation.TLS,
  1069  				},
  1070  				Result: simulation.Result{
  1071  					Error:              simulation.ErrNoRoute,
  1072  					ListenerMatched:    "0.0.0.0_443",
  1073  					RouteConfigMatched: "https.443.https-443-ingress-alpha-default-0.alpha-istio-autogenerated-k8s-ingress-default.istio-system",
  1074  					VirtualHostMatched: "blackhole:443",
  1075  				},
  1076  			},
  1077  			{
  1078  				Name: "https beta",
  1079  				Call: simulation.Call{
  1080  					Port:       443,
  1081  					HostHeader: "example.com",
  1082  					Path:       "/beta",
  1083  					Protocol:   simulation.HTTP,
  1084  					TLS:        simulation.TLS,
  1085  				},
  1086  				Result: simulation.Result{
  1087  					Error:              simulation.ErrNoRoute,
  1088  					ListenerMatched:    "0.0.0.0_443",
  1089  					RouteConfigMatched: "https.443.https-443-ingress-alpha-default-0.alpha-istio-autogenerated-k8s-ingress-default.istio-system",
  1090  					VirtualHostMatched: "blackhole:443",
  1091  				},
  1092  			},
  1093  		},
  1094  	}, simulationTest{
  1095  		name: "ingress shared TLS cert conflict - alpha first",
  1096  		// "alpha" is created before "beta". This avoids the bug in the previous test
  1097  		kubeConfig: tmpl.MustEvaluate(cfg, map[string]string{"Name": "alpha", "Time": "2010-01-01T00:00:00Z"}) +
  1098  			tmpl.MustEvaluate(cfg, map[string]string{"Name": "beta", "Time": "2020-01-01T00:00:00Z"}),
  1099  		calls: []simulation.Expect{
  1100  			{
  1101  				Name: "http alpha",
  1102  				Call: simulation.Call{
  1103  					Port:       80,
  1104  					HostHeader: "example.com",
  1105  					Path:       "/alpha",
  1106  					Protocol:   simulation.HTTP,
  1107  				},
  1108  				Result: simulation.Result{
  1109  					ListenerMatched:    "0.0.0.0_80",
  1110  					RouteConfigMatched: "http.80",
  1111  					ClusterMatched:     "outbound|80||alpha.default.svc.cluster.local",
  1112  				},
  1113  			},
  1114  			{
  1115  				Name: "http beta",
  1116  				Call: simulation.Call{
  1117  					Port:       80,
  1118  					HostHeader: "example.com",
  1119  					Path:       "/beta",
  1120  					Protocol:   simulation.HTTP,
  1121  				},
  1122  				Result: simulation.Result{
  1123  					ListenerMatched:    "0.0.0.0_80",
  1124  					RouteConfigMatched: "http.80",
  1125  					ClusterMatched:     "outbound|80||beta.default.svc.cluster.local",
  1126  				},
  1127  			},
  1128  			{
  1129  				Name: "https alpha",
  1130  				Call: simulation.Call{
  1131  					Port:       443,
  1132  					HostHeader: "example.com",
  1133  					Path:       "/alpha",
  1134  					Protocol:   simulation.HTTP,
  1135  					TLS:        simulation.TLS,
  1136  				},
  1137  				Result: simulation.Result{
  1138  					ListenerMatched:    "0.0.0.0_443",
  1139  					RouteConfigMatched: "https.443.https-443-ingress-alpha-default-0.alpha-istio-autogenerated-k8s-ingress-default.istio-system",
  1140  					ClusterMatched:     "outbound|80||alpha.default.svc.cluster.local",
  1141  				},
  1142  			},
  1143  			{
  1144  				Name: "https beta",
  1145  				Call: simulation.Call{
  1146  					Port:       443,
  1147  					HostHeader: "example.com",
  1148  					Path:       "/beta",
  1149  					Protocol:   simulation.HTTP,
  1150  					TLS:        simulation.TLS,
  1151  				},
  1152  				Result: simulation.Result{
  1153  					ListenerMatched:    "0.0.0.0_443",
  1154  					RouteConfigMatched: "https.443.https-443-ingress-alpha-default-0.alpha-istio-autogenerated-k8s-ingress-default.istio-system",
  1155  					ClusterMatched:     "outbound|80||beta.default.svc.cluster.local",
  1156  				},
  1157  			},
  1158  		},
  1159  	})
  1160  }
  1161  
  1162  type simulationTest struct {
  1163  	name       string
  1164  	config     string
  1165  	kubeConfig string
  1166  	// skipValidation disables validation of XDS resources. Should be used only when we expect a failure (regression catching)
  1167  	skipValidation bool
  1168  	calls          []simulation.Expect
  1169  }
  1170  
  1171  var debugMode = env.Register("SIMULATION_DEBUG", true, "if enabled, will dump verbose output").Get()
  1172  
  1173  func runGatewayTest(t *testing.T, cases ...simulationTest) {
  1174  	for _, tt := range cases {
  1175  		proxy := &model.Proxy{
  1176  			Labels: map[string]string{"istio": "ingressgateway"},
  1177  			Metadata: &model.NodeMetadata{
  1178  				Labels:    map[string]string{"istio": "ingressgateway"},
  1179  				Namespace: "istio-system",
  1180  			},
  1181  			Type: model.Router,
  1182  		}
  1183  		runSimulationTest(t, proxy, xds.FakeOptions{}, tt)
  1184  	}
  1185  }
  1186  
  1187  func runSimulationTest(t *testing.T, proxy *model.Proxy, o xds.FakeOptions, tt simulationTest) {
  1188  	runTest := func(t *testing.T) {
  1189  		o.ConfigString = tt.config
  1190  		o.KubernetesObjectString = tt.kubeConfig
  1191  		s := xds.NewFakeDiscoveryServer(t, o)
  1192  		sim := simulation.NewSimulation(t, s, s.SetupProxy(proxy))
  1193  		sim.RunExpectations(tt.calls)
  1194  		if t.Failed() && debugMode {
  1195  			t.Log(xdstest.MapKeys(xdstest.ExtractClusters(sim.Clusters)))
  1196  			t.Log(xdstest.ExtractListenerNames(sim.Listeners))
  1197  			t.Log(xdstest.DumpList(t, sim.Listeners))
  1198  			t.Log(xdstest.DumpList(t, sim.Routes))
  1199  			t.Log(tt.config)
  1200  			t.Log(tt.kubeConfig)
  1201  		}
  1202  		t.Run("validate configs", func(t *testing.T) {
  1203  			if tt.skipValidation {
  1204  				t.Skip()
  1205  			}
  1206  			xdstest.ValidateClusters(t, sim.Clusters)
  1207  			xdstest.ValidateListeners(t, sim.Listeners)
  1208  			xdstest.ValidateRouteConfigurations(t, sim.Routes)
  1209  		})
  1210  	}
  1211  	if tt.name != "" {
  1212  		t.Run(tt.name, runTest)
  1213  	} else {
  1214  		runTest(t)
  1215  	}
  1216  }
  1217  
  1218  func createGateway(name, namespace string, servers ...string) string {
  1219  	if name == "" {
  1220  		name = "default"
  1221  	}
  1222  	if namespace == "" {
  1223  		namespace = "default"
  1224  	}
  1225  	return tmpl.MustEvaluate(`apiVersion: networking.istio.io/v1alpha3
  1226  kind: Gateway
  1227  metadata:
  1228    name: "{{.Name}}"
  1229    namespace: "{{.Namespace}}"
  1230  spec:
  1231    selector:
  1232      istio: ingressgateway
  1233    servers:
  1234  {{- range $i, $p := $.Servers }}
  1235    -
  1236  {{$p | trim | indent 4}}
  1237  {{- end }}
  1238  ---
  1239  `, struct {
  1240  		Name      string
  1241  		Namespace string
  1242  		Servers   []string
  1243  	}{name, namespace, servers})
  1244  }
  1245  
  1246  func createGatewayWithServiceSelector(name, service string, servers ...string) string {
  1247  	if name == "" {
  1248  		name = "default"
  1249  	}
  1250  	return tmpl.MustEvaluate(`apiVersion: networking.istio.io/v1alpha3
  1251  kind: Gateway
  1252  metadata:
  1253    name: "{{.Name}}"
  1254    namespace: "istio-system"
  1255    annotations:
  1256      internal.istio.io/gateway-service: "{{.Service}}"
  1257  spec:
  1258    servers:
  1259  {{- range $i, $p := $.Servers }}
  1260    -
  1261  {{$p | trim | indent 4}}
  1262  {{- end }}
  1263  ---
  1264  `, struct {
  1265  		Name    string
  1266  		Service string
  1267  		Servers []string
  1268  	}{name, service, servers})
  1269  }
  1270  
  1271  func TestTargetPort(t *testing.T) {
  1272  	runGatewayTest(t,
  1273  		simulationTest{
  1274  			name: "basic",
  1275  			config: createGatewayWithServiceSelector("gateway80", "istio-ingressgateway.istio-system.svc.cluster.local", `
  1276  port:
  1277   number: 80
  1278   name: http
  1279   protocol: HTTP
  1280  hosts:
  1281  - "example.com"
  1282  `) + createGatewayWithServiceSelector("gateway81", "istio-ingressgateway.istio-system.svc.cluster.local", `
  1283  port:
  1284   number: 8080
  1285   name: http
  1286   protocol: HTTP
  1287  hosts:
  1288  - "example.com"
  1289  `) + `
  1290  ---
  1291  apiVersion: networking.istio.io/v1alpha3
  1292  kind: ServiceEntry
  1293  metadata:
  1294   name: service-instance
  1295   namespace: istio-system
  1296  spec:
  1297   hosts: ["istio-ingressgateway.istio-system.svc.cluster.local"]
  1298   ports:
  1299   - number: 80
  1300     targetPort: 8080
  1301     name: http
  1302     protocol: HTTP
  1303   resolution: STATIC
  1304   location: MESH_INTERNAL
  1305   endpoints:
  1306   - address: 1.1.1.1
  1307  ---
  1308  apiVersion: networking.istio.io/v1alpha3
  1309  kind: VirtualService
  1310  metadata:
  1311   name: a
  1312  spec:
  1313   hosts:
  1314   - "example.com"
  1315   gateways:
  1316   - istio-system/gateway80
  1317   - istio-system/gateway81
  1318   http:
  1319   - match:
  1320     - uri:
  1321         prefix: /
  1322     route:
  1323     - destination:
  1324         host: a
  1325         port:
  1326           number: 80`,
  1327  			calls: []simulation.Expect{
  1328  				{
  1329  					Call: simulation.Call{
  1330  						Port:       8080,
  1331  						HostHeader: "example.com",
  1332  						Protocol:   simulation.HTTP,
  1333  					},
  1334  					Result: simulation.Result{
  1335  						ListenerMatched:    "0.0.0.0_8080",
  1336  						ClusterMatched:     "outbound|80||a.default",
  1337  						RouteConfigMatched: "http.8080",
  1338  						VirtualHostMatched: "example.com:80",
  1339  					},
  1340  				},
  1341  			},
  1342  		},
  1343  		simulationTest{
  1344  			name: "multiple target port",
  1345  			config: createGatewayWithServiceSelector("gateway80", "ingress1.istio-system.svc.cluster.local", `
  1346  port:
  1347    number: 80
  1348    name: http
  1349    protocol: HTTP
  1350  hosts:
  1351  - "example.com"
  1352  `) + createGatewayWithServiceSelector("gateway81", "ingress2.istio-system.svc.cluster.local", `
  1353  port:
  1354    number: 80
  1355    name: http
  1356    protocol: HTTP
  1357  hosts:
  1358  - "example.com"
  1359  `) + `
  1360  ---
  1361  apiVersion: networking.istio.io/v1alpha3
  1362  kind: ServiceEntry
  1363  metadata:
  1364    name: service-instance
  1365    namespace: istio-system
  1366  spec:
  1367    hosts: ["ingress1.istio-system.svc.cluster.local"]
  1368    ports:
  1369    - number: 80
  1370      targetPort: 8080
  1371      name: http
  1372      protocol: HTTP
  1373    resolution: STATIC
  1374    location: MESH_INTERNAL
  1375    endpoints:
  1376    - address: 1.1.1.1
  1377  ---
  1378  apiVersion: networking.istio.io/v1alpha3
  1379  kind: ServiceEntry
  1380  metadata:
  1381    name: service-instance-2
  1382    namespace: istio-system
  1383  spec:
  1384    hosts: ["ingress2.istio-system.svc.cluster.local"]
  1385    ports:
  1386    - number: 80
  1387      targetPort: 8081
  1388      name: http
  1389      protocol: HTTP
  1390    resolution: STATIC
  1391    location: MESH_INTERNAL
  1392    endpoints:
  1393    - address: 1.1.1.1
  1394  ---
  1395  apiVersion: networking.istio.io/v1alpha3
  1396  kind: VirtualService
  1397  metadata:
  1398    name: a
  1399  spec:
  1400    hosts:
  1401    - "example.com"
  1402    gateways:
  1403    - istio-system/gateway80
  1404    - istio-system/gateway81
  1405    http:
  1406    - match:
  1407      - uri:
  1408          prefix: /
  1409      route:
  1410      - destination:
  1411          host: a
  1412          port:
  1413            number: 80
  1414  ---
  1415  apiVersion: networking.istio.io/v1alpha3
  1416  kind: VirtualService
  1417  metadata:
  1418    name: b
  1419  spec:
  1420    hosts:
  1421    - "example.com"
  1422    gateways:
  1423    - istio-system/gateway80
  1424    - istio-system/gateway81
  1425    http:
  1426    - match:
  1427      - uri:
  1428          prefix: /
  1429      route:
  1430      - destination:
  1431          host: b
  1432          port:
  1433            number: 8081`,
  1434  			calls: []simulation.Expect{
  1435  				{
  1436  					Name: "target port 1",
  1437  					Call: simulation.Call{
  1438  						Port:       8080,
  1439  						HostHeader: "example.com",
  1440  						Protocol:   simulation.HTTP,
  1441  					},
  1442  					Result: simulation.Result{
  1443  						ListenerMatched:    "0.0.0.0_8080",
  1444  						ClusterMatched:     "outbound|80||a.default",
  1445  						RouteConfigMatched: "http.8080",
  1446  						VirtualHostMatched: "example.com:80",
  1447  					},
  1448  				},
  1449  				{
  1450  					Name: "target port 2",
  1451  					Call: simulation.Call{
  1452  						Port:       8081,
  1453  						HostHeader: "example.com",
  1454  						Protocol:   simulation.HTTP,
  1455  					},
  1456  					Result: simulation.Result{
  1457  						ListenerMatched:    "0.0.0.0_8081",
  1458  						ClusterMatched:     "outbound|80||a.default",
  1459  						RouteConfigMatched: "http.8081",
  1460  						VirtualHostMatched: "example.com:80",
  1461  					},
  1462  				},
  1463  			},
  1464  		})
  1465  }
  1466  
  1467  func TestGatewayServices(t *testing.T) {
  1468  	runGatewayTest(t,
  1469  		simulationTest{
  1470  			name: "single service",
  1471  			config: createGatewayWithServiceSelector("gateway", "istio-ingressgateway.istio-system.svc.cluster.local", `
  1472  port:
  1473    number: 80
  1474    name: http-80
  1475    protocol: HTTP
  1476  hosts:
  1477  - "80.example.com"
  1478  `, `
  1479  port:
  1480    number: 82
  1481    name: http-82
  1482    protocol: HTTP
  1483  hosts:
  1484  - "82.example.com"
  1485  `) + `
  1486  ---
  1487  apiVersion: networking.istio.io/v1alpha3
  1488  kind: ServiceEntry
  1489  metadata:
  1490    name: service-instance
  1491    namespace: istio-system
  1492  spec:
  1493    hosts: ["istio-ingressgateway.istio-system.svc.cluster.local"]
  1494    ports:
  1495    - number: 80
  1496      targetPort: 8080
  1497      name: http
  1498      protocol: HTTP
  1499    resolution: STATIC
  1500    location: MESH_INTERNAL
  1501    endpoints:
  1502    - address: 1.1.1.1
  1503  ---
  1504  apiVersion: networking.istio.io/v1alpha3
  1505  kind: VirtualService
  1506  metadata:
  1507    name: a
  1508  spec:
  1509    hosts:
  1510    - "*.example.com"
  1511    gateways:
  1512    - istio-system/gateway
  1513    http:
  1514    - match:
  1515      - uri:
  1516          prefix: /
  1517      route:
  1518      - destination:
  1519          host: a
  1520          port:
  1521            number: 80`,
  1522  			calls: []simulation.Expect{
  1523  				{
  1524  					Name: "port 8080",
  1525  					Call: simulation.Call{
  1526  						Port:       8080,
  1527  						HostHeader: "80.example.com",
  1528  						Protocol:   simulation.HTTP,
  1529  					},
  1530  					Result: simulation.Result{
  1531  						ListenerMatched:    "0.0.0.0_8080",
  1532  						ClusterMatched:     "outbound|80||a.default",
  1533  						RouteConfigMatched: "http.8080",
  1534  						VirtualHostMatched: "80.example.com:80",
  1535  					},
  1536  				},
  1537  				{
  1538  					Name: "no service match",
  1539  					Call: simulation.Call{
  1540  						Port:       82,
  1541  						HostHeader: "81.example.com",
  1542  						Protocol:   simulation.HTTP,
  1543  					},
  1544  					// For gateway service reference, if there is no matching port we do not setup a listener
  1545  					Result: simulation.Result{
  1546  						Error: simulation.ErrNoListener,
  1547  					},
  1548  				},
  1549  			},
  1550  		},
  1551  		simulationTest{
  1552  			name: "wrong namespace",
  1553  			config: createGatewayWithServiceSelector("gateway", "istio-ingressgateway.not-istio-system.svc.cluster.local", `
  1554  port:
  1555    number: 80
  1556    name: http-80
  1557    protocol: HTTP
  1558  hosts:
  1559  - "80.example.com"
  1560  `) + `
  1561  ---
  1562  apiVersion: networking.istio.io/v1alpha3
  1563  kind: ServiceEntry
  1564  metadata:
  1565    name: service-instance
  1566    namespace: not-istio-system
  1567  spec:
  1568    hosts: ["istio-ingressgateway.not-istio-system.svc.cluster.local"]
  1569    ports:
  1570    - number: 80
  1571      name: http
  1572      protocol: HTTP
  1573    resolution: STATIC
  1574    location: MESH_INTERNAL
  1575    endpoints:
  1576    - address: 1.1.1.1
  1577  ---`,
  1578  			calls: []simulation.Expect{
  1579  				{
  1580  					Name: "port 80",
  1581  					Call: simulation.Call{
  1582  						Port:       80,
  1583  						HostHeader: "80.example.com",
  1584  						Protocol:   simulation.HTTP,
  1585  					},
  1586  					Result: simulation.Result{
  1587  						// Service selected is in another namespace, we cannot cross namespace boundary
  1588  						Error: simulation.ErrNoListener,
  1589  					},
  1590  				},
  1591  			},
  1592  		},
  1593  		simulationTest{
  1594  			name: "multiple services",
  1595  			config: createGatewayWithServiceSelector("gateway", "istio-ingressgateway.istio-system.svc.cluster.local,ingress.com", `
  1596  port:
  1597    number: 80
  1598    name: http-80
  1599    protocol: HTTP
  1600  hosts:
  1601  - "80.example.com"
  1602  `, `
  1603  port:
  1604    number: 81
  1605    name: http-81
  1606    protocol: HTTP
  1607  hosts:
  1608  - "81.example.com"
  1609  `, `
  1610  port:
  1611    number: 82
  1612    name: http-82
  1613    protocol: HTTP
  1614  hosts:
  1615  - "82.example.com"
  1616  `) + `
  1617  ---
  1618  apiVersion: networking.istio.io/v1alpha3
  1619  kind: ServiceEntry
  1620  metadata:
  1621    name: service-instance
  1622    namespace: istio-system
  1623  spec:
  1624    hosts: ["istio-ingressgateway.istio-system.svc.cluster.local"]
  1625    ports:
  1626    - number: 80
  1627      targetPort: 8080
  1628      name: http
  1629      protocol: HTTP
  1630    resolution: STATIC
  1631    location: MESH_INTERNAL
  1632    endpoints:
  1633    - address: 1.1.1.1
  1634  ---
  1635  apiVersion: networking.istio.io/v1alpha3
  1636  kind: ServiceEntry
  1637  metadata:
  1638    name: service-instance2
  1639    namespace: istio-system
  1640  spec:
  1641    hosts: ["ingress.com"]
  1642    ports:
  1643    - number: 81
  1644      targetPort: 8081
  1645      name: http
  1646      protocol: HTTP
  1647    resolution: STATIC
  1648    location: MESH_INTERNAL
  1649    endpoints:
  1650    - address: 1.1.1.1
  1651  ---
  1652  apiVersion: networking.istio.io/v1alpha3
  1653  kind: VirtualService
  1654  metadata:
  1655    name: a
  1656  spec:
  1657    hosts:
  1658    - "*.example.com"
  1659    gateways:
  1660    - istio-system/gateway
  1661    http:
  1662    - match:
  1663      - uri:
  1664          prefix: /
  1665      route:
  1666      - destination:
  1667          host: a
  1668          port:
  1669            number: 80`,
  1670  			calls: []simulation.Expect{
  1671  				{
  1672  					Name: "port 8080",
  1673  					Call: simulation.Call{
  1674  						Port:       8080,
  1675  						HostHeader: "80.example.com",
  1676  						Protocol:   simulation.HTTP,
  1677  					},
  1678  					Result: simulation.Result{
  1679  						ListenerMatched:    "0.0.0.0_8080",
  1680  						ClusterMatched:     "outbound|80||a.default",
  1681  						RouteConfigMatched: "http.8080",
  1682  						VirtualHostMatched: "80.example.com:80",
  1683  					},
  1684  				},
  1685  				{
  1686  					Name: "port 8081",
  1687  					Call: simulation.Call{
  1688  						Port:       8081,
  1689  						HostHeader: "81.example.com",
  1690  						Protocol:   simulation.HTTP,
  1691  					},
  1692  					Result: simulation.Result{
  1693  						ListenerMatched:    "0.0.0.0_8081",
  1694  						ClusterMatched:     "outbound|80||a.default",
  1695  						RouteConfigMatched: "http.8081",
  1696  						VirtualHostMatched: "81.example.com:81",
  1697  					},
  1698  				},
  1699  				{
  1700  					// For gateway service reference, if there is no matching port we do not setup a listener
  1701  					Name: "no service match",
  1702  					Call: simulation.Call{
  1703  						Port:       82,
  1704  						HostHeader: "81.example.com",
  1705  						Protocol:   simulation.HTTP,
  1706  					},
  1707  					Result: simulation.Result{
  1708  						Error: simulation.ErrNoListener,
  1709  					},
  1710  				},
  1711  			},
  1712  		},
  1713  		simulationTest{
  1714  			name: "multiple overlapping services",
  1715  			config: createGatewayWithServiceSelector("gateway", "istio-ingressgateway.istio-system.svc.cluster.local,ingress.com", `
  1716  port:
  1717   number: 80
  1718   name: http-80
  1719   protocol: HTTP
  1720  hosts:
  1721  - "80.example.com"
  1722  `, `
  1723  port:
  1724   number: 81
  1725   name: http-81
  1726   protocol: HTTP
  1727  hosts:
  1728  - "81.example.com"
  1729  `) + `
  1730  ---
  1731  apiVersion: networking.istio.io/v1alpha3
  1732  kind: ServiceEntry
  1733  metadata:
  1734   name: service-instance
  1735   namespace: istio-system
  1736  spec:
  1737   hosts: ["istio-ingressgateway.istio-system.svc.cluster.local"]
  1738   ports:
  1739   - number: 80
  1740     targetPort: 8080
  1741     name: http
  1742     protocol: HTTP
  1743   resolution: STATIC
  1744   location: MESH_INTERNAL
  1745   endpoints:
  1746   - address: 1.1.1.1
  1747  ---
  1748  apiVersion: networking.istio.io/v1alpha3
  1749  kind: ServiceEntry
  1750  metadata:
  1751   name: service-instance2
  1752   namespace: istio-system
  1753  spec:
  1754   hosts: ["ingress.com"]
  1755   ports:
  1756   - number: 81
  1757     targetPort: 8080
  1758     name: http
  1759     protocol: HTTP
  1760   resolution: STATIC
  1761   location: MESH_INTERNAL
  1762   endpoints:
  1763   - address: 1.1.1.1
  1764  ---
  1765  apiVersion: networking.istio.io/v1alpha3
  1766  kind: VirtualService
  1767  metadata:
  1768   name: a-80
  1769  spec:
  1770   hosts:
  1771   - "80.example.com"
  1772   gateways:
  1773   - istio-system/gateway
  1774   http:
  1775   - match:
  1776     - uri:
  1777         prefix: /
  1778     route:
  1779     - destination:
  1780         host: a
  1781         port:
  1782           number: 80
  1783  ---
  1784  apiVersion: networking.istio.io/v1alpha3
  1785  kind: VirtualService
  1786  metadata:
  1787   name: a-81
  1788  spec:
  1789   hosts:
  1790   - "81.example.com"
  1791   gateways:
  1792   - istio-system/gateway
  1793   http:
  1794   - match:
  1795     - uri:
  1796         prefix: /
  1797     route:
  1798     - destination:
  1799         host: a
  1800         port:
  1801           number: 80`,
  1802  			calls: []simulation.Expect{
  1803  				{
  1804  					Name: "port 8080 from 80",
  1805  					Call: simulation.Call{
  1806  						Port:       8080,
  1807  						HostHeader: "80.example.com",
  1808  						Protocol:   simulation.HTTP,
  1809  					},
  1810  					Result: simulation.Result{
  1811  						ListenerMatched:    "0.0.0.0_8080",
  1812  						ClusterMatched:     "outbound|80||a.default",
  1813  						RouteConfigMatched: "http.8080",
  1814  						VirtualHostMatched: "80.example.com:80",
  1815  					},
  1816  				},
  1817  				{
  1818  					Name: "port 8080 from 81",
  1819  					Call: simulation.Call{
  1820  						Port:       8080,
  1821  						HostHeader: "81.example.com",
  1822  						Protocol:   simulation.HTTP,
  1823  					},
  1824  					Result: simulation.Result{
  1825  						ListenerMatched:    "0.0.0.0_8080",
  1826  						ClusterMatched:     "outbound|80||a.default",
  1827  						RouteConfigMatched: "http.8080",
  1828  						VirtualHostMatched: "81.example.com:81",
  1829  					},
  1830  				},
  1831  			},
  1832  		},
  1833  		simulationTest{
  1834  			name: "multiple overlapping services wildcard",
  1835  			config: createGatewayWithServiceSelector("gateway", "istio-ingressgateway.istio-system.svc.cluster.local,ingress.com", `
  1836  port:
  1837    number: 80
  1838    name: http-80
  1839    protocol: HTTP
  1840  hosts:
  1841  - "80.example.com"
  1842  `, `
  1843  port:
  1844    number: 81
  1845    name: http-81
  1846    protocol: HTTP
  1847  hosts:
  1848  - "81.example.com"
  1849  `) + `
  1850  ---
  1851  apiVersion: networking.istio.io/v1alpha3
  1852  kind: ServiceEntry
  1853  metadata:
  1854    name: service-instance
  1855    namespace: istio-system
  1856  spec:
  1857    hosts: ["istio-ingressgateway.istio-system.svc.cluster.local"]
  1858    ports:
  1859    - number: 80
  1860      targetPort: 8080
  1861      name: http
  1862      protocol: HTTP
  1863    resolution: STATIC
  1864    location: MESH_INTERNAL
  1865    endpoints:
  1866    - address: 1.1.1.1
  1867  ---
  1868  apiVersion: networking.istio.io/v1alpha3
  1869  kind: ServiceEntry
  1870  metadata:
  1871    name: service-instance2
  1872    namespace: istio-system
  1873  spec:
  1874    hosts: ["ingress.com"]
  1875    ports:
  1876    - number: 81
  1877      targetPort: 8080
  1878      name: http
  1879      protocol: HTTP
  1880    resolution: STATIC
  1881    location: MESH_INTERNAL
  1882    endpoints:
  1883    - address: 1.1.1.1
  1884  ---
  1885  apiVersion: networking.istio.io/v1alpha3
  1886  kind: VirtualService
  1887  metadata:
  1888   name: a-80
  1889  spec:
  1890   hosts:
  1891   - "80.example.com"
  1892   gateways:
  1893   - istio-system/gateway
  1894   http:
  1895   - match:
  1896     - uri:
  1897         prefix: /
  1898     route:
  1899     - destination:
  1900         host: a
  1901         port:
  1902           number: 80
  1903  ---
  1904  apiVersion: networking.istio.io/v1alpha3
  1905  kind: VirtualService
  1906  metadata:
  1907   name: a-81
  1908  spec:
  1909   hosts:
  1910   - "81.example.com"
  1911   gateways:
  1912   - istio-system/gateway
  1913   http:
  1914   - match:
  1915     - uri:
  1916         prefix: /
  1917     route:
  1918     - destination:
  1919         host: a
  1920         port:
  1921           number: 80`,
  1922  			calls: []simulation.Expect{
  1923  				{
  1924  					Name: "port 8080 from 80",
  1925  					Call: simulation.Call{
  1926  						Port:       8080,
  1927  						HostHeader: "80.example.com",
  1928  						Protocol:   simulation.HTTP,
  1929  					},
  1930  					Result: simulation.Result{
  1931  						ListenerMatched:    "0.0.0.0_8080",
  1932  						ClusterMatched:     "outbound|80||a.default",
  1933  						RouteConfigMatched: "http.8080",
  1934  						VirtualHostMatched: "80.example.com:80",
  1935  					},
  1936  				},
  1937  				{
  1938  					Name: "port 8080 from 81",
  1939  					Call: simulation.Call{
  1940  						Port:       8080,
  1941  						HostHeader: "81.example.com",
  1942  						Protocol:   simulation.HTTP,
  1943  					},
  1944  					Result: simulation.Result{
  1945  						ListenerMatched:    "0.0.0.0_8080",
  1946  						ClusterMatched:     "outbound|80||a.default",
  1947  						RouteConfigMatched: "http.8080",
  1948  						VirtualHostMatched: "81.example.com:81",
  1949  					},
  1950  				},
  1951  			},
  1952  		},
  1953  		simulationTest{
  1954  			name: "no match selector",
  1955  			config: createGateway("gateway", "istio-system", `
  1956  port:
  1957    number: 80
  1958    name: http-80
  1959    protocol: HTTP
  1960  hosts:
  1961  - "80.example.com"
  1962  `, `
  1963  port:
  1964    number: 82
  1965    name: http-82
  1966    protocol: HTTP
  1967  hosts:
  1968  - "82.example.com"
  1969  `) + `
  1970  ---
  1971  apiVersion: networking.istio.io/v1alpha3
  1972  kind: ServiceEntry
  1973  metadata:
  1974    name: service-instance
  1975    namespace: istio-system
  1976  spec:
  1977    hosts: ["istio-ingressgateway.istio-system.svc.cluster.local"]
  1978    ports:
  1979    - number: 80
  1980      targetPort: 8080
  1981      name: http
  1982      protocol: HTTP
  1983    resolution: STATIC
  1984    location: MESH_INTERNAL
  1985    endpoints:
  1986    - address: 1.1.1.1
  1987  ---
  1988  apiVersion: networking.istio.io/v1alpha3
  1989  kind: VirtualService
  1990  metadata:
  1991    name: a
  1992  spec:
  1993    hosts:
  1994    - "*.example.com"
  1995    gateways:
  1996    - istio-system/gateway
  1997    http:
  1998    - match:
  1999      - uri:
  2000          prefix: /
  2001      route:
  2002      - destination:
  2003          host: a
  2004          port:
  2005            number: 80`,
  2006  			calls: []simulation.Expect{
  2007  				{
  2008  					Name: "port 8080",
  2009  					Call: simulation.Call{
  2010  						Port:       8080,
  2011  						HostHeader: "80.example.com",
  2012  						Protocol:   simulation.HTTP,
  2013  					},
  2014  					Result: simulation.Result{
  2015  						ListenerMatched:    "0.0.0.0_8080",
  2016  						ClusterMatched:     "outbound|80||a.default",
  2017  						RouteConfigMatched: "http.8080",
  2018  						VirtualHostMatched: "80.example.com:80",
  2019  					},
  2020  				},
  2021  				{
  2022  					// For gateway selector more, if there is no matching service we use the port as is
  2023  					Name: "no service match",
  2024  					Call: simulation.Call{
  2025  						Port:       82,
  2026  						HostHeader: "82.example.com",
  2027  						Protocol:   simulation.HTTP,
  2028  					},
  2029  					Result: simulation.Result{
  2030  						ListenerMatched:    "0.0.0.0_82",
  2031  						ClusterMatched:     "outbound|80||a.default",
  2032  						RouteConfigMatched: "http.82",
  2033  						VirtualHostMatched: "82.example.com:82",
  2034  					},
  2035  				},
  2036  			},
  2037  		},
  2038  		simulationTest{
  2039  			name:           "overlapping SNI match",
  2040  			skipValidation: true, // TODO: https://github.com/istio/istio/issues/39921
  2041  			config: `apiVersion: networking.istio.io/v1beta1
  2042  kind: Gateway
  2043  metadata:
  2044    name: gw
  2045    namespace: istio-system
  2046  spec:
  2047    selector:
  2048      istio: ingressgateway
  2049    servers:
  2050    - hosts:
  2051      - example.com
  2052      port:
  2053        name: example
  2054        number: 443
  2055        protocol: TLS
  2056      tls:
  2057        mode: PASSTHROUGH
  2058    - hosts:
  2059      - '*'
  2060      port:
  2061        name: wildcard-tls
  2062        number: 443
  2063        protocol: HTTPS
  2064      tls:
  2065        mode: PASSTHROUGH
  2066  ---
  2067  apiVersion: networking.istio.io/v1beta1
  2068  kind: VirtualService
  2069  metadata:
  2070    name: example
  2071    namespace: istio-system
  2072  spec:
  2073    gateways:
  2074    - gw
  2075    hosts:
  2076    - example.com
  2077    tls:
  2078    - match:
  2079      - sniHosts:
  2080        - example.com
  2081      route:
  2082      - destination:
  2083          host: example
  2084  ` + `
  2085  ---
  2086  apiVersion: networking.istio.io/v1alpha3
  2087  kind: ServiceEntry
  2088  metadata:
  2089    name: service-instance
  2090    namespace: istio-system
  2091  spec:
  2092    hosts: ["istio-ingressgateway.istio-system.svc.cluster.local"]
  2093    ports:
  2094    - number: 443
  2095      targetPort: 443
  2096      name: https
  2097      protocol: HTTPS
  2098    resolution: STATIC
  2099    location: MESH_INTERNAL
  2100    endpoints:
  2101    - address: 1.1.1.1`,
  2102  			calls: []simulation.Expect{},
  2103  		},
  2104  	)
  2105  }