sigs.k8s.io/external-dns@v0.14.1/source/istio_virtualservice_test.go (about)

     1  /*
     2  Copyright 2020 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package source
    18  
    19  import (
    20  	"context"
    21  	"fmt"
    22  	"testing"
    23  
    24  	"github.com/pkg/errors"
    25  	"github.com/stretchr/testify/assert"
    26  	"github.com/stretchr/testify/require"
    27  	"github.com/stretchr/testify/suite"
    28  	"istio.io/api/meta/v1alpha1"
    29  	istionetworking "istio.io/api/networking/v1alpha3"
    30  	networkingv1alpha3 "istio.io/client-go/pkg/apis/networking/v1alpha3"
    31  	istiofake "istio.io/client-go/pkg/clientset/versioned/fake"
    32  	fakenetworking3 "istio.io/client-go/pkg/clientset/versioned/typed/networking/v1alpha3/fake"
    33  	v1 "k8s.io/api/core/v1"
    34  	networkv1 "k8s.io/api/networking/v1"
    35  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    36  	"k8s.io/apimachinery/pkg/runtime"
    37  	"k8s.io/client-go/kubernetes/fake"
    38  	k8sclienttesting "k8s.io/client-go/testing"
    39  
    40  	"sigs.k8s.io/external-dns/endpoint"
    41  )
    42  
    43  // This is a compile-time validation that istioVirtualServiceSource is a Source.
    44  var _ Source = &virtualServiceSource{}
    45  
    46  type VirtualServiceSuite struct {
    47  	suite.Suite
    48  	source     Source
    49  	lbServices []*v1.Service
    50  	ingresses  []*networkv1.Ingress
    51  	gwconfig   *networkingv1alpha3.Gateway
    52  	vsconfig   *networkingv1alpha3.VirtualService
    53  }
    54  
    55  func (suite *VirtualServiceSuite) SetupTest() {
    56  	fakeKubernetesClient := fake.NewSimpleClientset()
    57  	fakeIstioClient := istiofake.NewSimpleClientset()
    58  	var err error
    59  
    60  	suite.lbServices = []*v1.Service{
    61  		(fakeIngressGatewayService{
    62  			ips:       []string{"8.8.8.8"},
    63  			hostnames: []string{"v1"},
    64  			namespace: "istio-system",
    65  			name:      "istio-gateway1",
    66  		}).Service(),
    67  		(fakeIngressGatewayService{
    68  			ips:       []string{"1.1.1.1"},
    69  			hostnames: []string{"v42"},
    70  			namespace: "istio-system",
    71  			name:      "istio-gateway2",
    72  		}).Service(),
    73  	}
    74  
    75  	for _, service := range suite.lbServices {
    76  		_, err = fakeKubernetesClient.CoreV1().Services(service.Namespace).Create(context.Background(), service, metav1.CreateOptions{})
    77  		suite.NoError(err, "should succeed")
    78  	}
    79  
    80  	suite.ingresses = []*networkv1.Ingress{
    81  		(fakeIngress{
    82  			ips:       []string{"2.2.2.2"},
    83  			hostnames: []string{"v2"},
    84  			namespace: "istio-system",
    85  			name:      "istio-ingress",
    86  		}).Ingress(),
    87  		(fakeIngress{
    88  			ips:       []string{"3.3.3.3"},
    89  			hostnames: []string{"v62"},
    90  			namespace: "istio-system",
    91  			name:      "istio-ingress2",
    92  		}).Ingress(),
    93  	}
    94  
    95  	for _, ingress := range suite.ingresses {
    96  		_, err = fakeKubernetesClient.NetworkingV1().Ingresses(ingress.Namespace).Create(context.Background(), ingress, metav1.CreateOptions{})
    97  		suite.NoError(err, "should succeed")
    98  	}
    99  
   100  	suite.gwconfig = (fakeGatewayConfig{
   101  		name:      "foo-gateway-with-targets",
   102  		namespace: "istio-system",
   103  		dnsnames:  [][]string{{"*"}},
   104  	}).Config()
   105  	_, err = fakeIstioClient.NetworkingV1alpha3().Gateways(suite.gwconfig.Namespace).Create(context.Background(), suite.gwconfig, metav1.CreateOptions{})
   106  	suite.NoError(err, "should succeed")
   107  
   108  	suite.vsconfig = (fakeVirtualServiceConfig{
   109  		name:      "foo-virtualservice",
   110  		namespace: "istio-other",
   111  		gateways:  []string{"istio-system/foo-gateway-with-targets"},
   112  		dnsnames:  []string{"foo"},
   113  	}).Config()
   114  	_, err = fakeIstioClient.NetworkingV1alpha3().VirtualServices(suite.vsconfig.Namespace).Create(context.Background(), suite.vsconfig, metav1.CreateOptions{})
   115  	suite.NoError(err, "should succeed")
   116  
   117  	suite.source, err = NewIstioVirtualServiceSource(
   118  		context.TODO(),
   119  		fakeKubernetesClient,
   120  		fakeIstioClient,
   121  		"",
   122  		"",
   123  		"{{.Name}}",
   124  		false,
   125  		false,
   126  	)
   127  	suite.NoError(err, "should initialize virtualservice source")
   128  }
   129  
   130  func (suite *VirtualServiceSuite) TestResourceLabelIsSet() {
   131  	endpoints, err := suite.source.Endpoints(context.Background())
   132  	suite.NoError(err, "should succeed")
   133  	suite.Equal(len(endpoints), 2, "should return the correct number of endpoints")
   134  	for _, ep := range endpoints {
   135  		suite.Equal("virtualservice/istio-other/foo-virtualservice", ep.Labels[endpoint.ResourceLabelKey], "should set correct resource label")
   136  	}
   137  }
   138  
   139  func TestVirtualService(t *testing.T) {
   140  	t.Parallel()
   141  
   142  	suite.Run(t, new(VirtualServiceSuite))
   143  	t.Run("virtualServiceBindsToGateway", testVirtualServiceBindsToGateway)
   144  	t.Run("endpointsFromVirtualServiceConfig", testEndpointsFromVirtualServiceConfig)
   145  	t.Run("Endpoints", testVirtualServiceEndpoints)
   146  	t.Run("gatewaySelectorMatchesService", testGatewaySelectorMatchesService)
   147  }
   148  
   149  func TestNewIstioVirtualServiceSource(t *testing.T) {
   150  	t.Parallel()
   151  
   152  	for _, ti := range []struct {
   153  		title                    string
   154  		annotationFilter         string
   155  		fqdnTemplate             string
   156  		combineFQDNAndAnnotation bool
   157  		expectError              bool
   158  	}{
   159  		{
   160  			title:        "invalid template",
   161  			expectError:  true,
   162  			fqdnTemplate: "{{.Name",
   163  		},
   164  		{
   165  			title:       "valid empty template",
   166  			expectError: false,
   167  		},
   168  		{
   169  			title:        "valid template",
   170  			expectError:  false,
   171  			fqdnTemplate: "{{.Name}}-{{.Namespace}}.ext-dns.test.com",
   172  		},
   173  		{
   174  			title:        "valid template",
   175  			expectError:  false,
   176  			fqdnTemplate: "{{.Name}}-{{.Namespace}}.ext-dns.test.com, {{.Name}}-{{.Namespace}}.ext-dna.test.com",
   177  		},
   178  		{
   179  			title:                    "valid template",
   180  			expectError:              false,
   181  			fqdnTemplate:             "{{.Name}}-{{.Namespace}}.ext-dns.test.com, {{.Name}}-{{.Namespace}}.ext-dna.test.com",
   182  			combineFQDNAndAnnotation: true,
   183  		},
   184  		{
   185  			title:            "non-empty annotation filter label",
   186  			expectError:      false,
   187  			annotationFilter: "kubernetes.io/gateway.class=nginx",
   188  		},
   189  	} {
   190  		ti := ti
   191  		t.Run(ti.title, func(t *testing.T) {
   192  			t.Parallel()
   193  
   194  			_, err := NewIstioVirtualServiceSource(
   195  				context.TODO(),
   196  				fake.NewSimpleClientset(),
   197  				istiofake.NewSimpleClientset(),
   198  				"",
   199  				ti.annotationFilter,
   200  				ti.fqdnTemplate,
   201  				ti.combineFQDNAndAnnotation,
   202  				false,
   203  			)
   204  			if ti.expectError {
   205  				assert.Error(t, err)
   206  			} else {
   207  				assert.NoError(t, err)
   208  			}
   209  		})
   210  	}
   211  }
   212  
   213  func testVirtualServiceBindsToGateway(t *testing.T) {
   214  	for _, ti := range []struct {
   215  		title    string
   216  		gwconfig fakeGatewayConfig
   217  		vsconfig fakeVirtualServiceConfig
   218  		vsHost   string
   219  		expected bool
   220  	}{
   221  		{
   222  			title: "matching host *",
   223  			gwconfig: fakeGatewayConfig{
   224  				dnsnames: [][]string{{"*"}},
   225  			},
   226  			vsconfig: fakeVirtualServiceConfig{},
   227  			vsHost:   "foo.bar",
   228  			expected: true,
   229  		},
   230  		{
   231  			title: "matching host *.<domain>",
   232  			gwconfig: fakeGatewayConfig{
   233  				dnsnames: [][]string{{"*.foo.bar"}},
   234  			},
   235  			vsconfig: fakeVirtualServiceConfig{},
   236  			vsHost:   "baz.foo.bar",
   237  			expected: true,
   238  		},
   239  		{
   240  			title: "not matching host *.<domain>",
   241  			gwconfig: fakeGatewayConfig{
   242  				dnsnames: [][]string{{"*.foo.bar"}},
   243  			},
   244  			vsconfig: fakeVirtualServiceConfig{},
   245  			vsHost:   "foo.bar",
   246  			expected: false,
   247  		},
   248  		{
   249  			title: "not matching host *.<domain>",
   250  			gwconfig: fakeGatewayConfig{
   251  				dnsnames: [][]string{{"*.foo.bar"}},
   252  			},
   253  			vsconfig: fakeVirtualServiceConfig{},
   254  			vsHost:   "bazfoo.bar",
   255  			expected: false,
   256  		},
   257  		{
   258  			title: "not matching host *.<domain>",
   259  			gwconfig: fakeGatewayConfig{
   260  				dnsnames: [][]string{{"*.foo.bar"}},
   261  			},
   262  			vsconfig: fakeVirtualServiceConfig{},
   263  			vsHost:   "*foo.bar",
   264  			expected: false,
   265  		},
   266  		{
   267  			title: "matching host */*",
   268  			gwconfig: fakeGatewayConfig{
   269  				dnsnames: [][]string{{"*/*"}},
   270  			},
   271  			vsconfig: fakeVirtualServiceConfig{},
   272  			vsHost:   "foo.bar",
   273  			expected: true,
   274  		},
   275  		{
   276  			title: "matching host <namespace>/*",
   277  			gwconfig: fakeGatewayConfig{
   278  				namespace: "istio-system",
   279  				dnsnames:  [][]string{{"myns/*"}},
   280  			},
   281  			vsconfig: fakeVirtualServiceConfig{
   282  				namespace: "myns",
   283  			},
   284  			vsHost:   "foo.bar",
   285  			expected: true,
   286  		},
   287  		{
   288  			title: "matching host ./*",
   289  			gwconfig: fakeGatewayConfig{
   290  				namespace: "istio-system",
   291  				dnsnames:  [][]string{{"./*"}},
   292  			},
   293  			vsconfig: fakeVirtualServiceConfig{
   294  				namespace: "istio-system",
   295  			},
   296  			vsHost:   "foo.bar",
   297  			expected: true,
   298  		},
   299  		{
   300  			title: "not matching host ./*",
   301  			gwconfig: fakeGatewayConfig{
   302  				namespace: "istio-system",
   303  				dnsnames:  [][]string{{"./*"}},
   304  			},
   305  			vsconfig: fakeVirtualServiceConfig{
   306  				namespace: "myns",
   307  			},
   308  			vsHost:   "foo.bar",
   309  			expected: false,
   310  		},
   311  		{
   312  			title: "not matching host <namespace>/*",
   313  			gwconfig: fakeGatewayConfig{
   314  				namespace: "istio-system",
   315  				dnsnames:  [][]string{{"myns/*"}},
   316  			},
   317  			vsconfig: fakeVirtualServiceConfig{
   318  				namespace: "otherns",
   319  			},
   320  			vsHost:   "foo.bar",
   321  			expected: false,
   322  		},
   323  		{
   324  			title: "not matching host <namespace>/*",
   325  			gwconfig: fakeGatewayConfig{
   326  				namespace: "istio-system",
   327  				dnsnames:  [][]string{{"myns/*"}},
   328  			},
   329  			vsconfig: fakeVirtualServiceConfig{
   330  				namespace: "otherns",
   331  			},
   332  			vsHost:   "foo.bar",
   333  			expected: false,
   334  		},
   335  		{
   336  			title: "matching exportTo *",
   337  			gwconfig: fakeGatewayConfig{
   338  				namespace: "istio-system",
   339  				dnsnames:  [][]string{{"*"}},
   340  			},
   341  			vsconfig: fakeVirtualServiceConfig{
   342  				namespace: "otherns",
   343  				exportTo:  "*",
   344  			},
   345  			vsHost:   "foo.bar",
   346  			expected: true,
   347  		},
   348  		{
   349  			title: "matching exportTo <namespace>",
   350  			gwconfig: fakeGatewayConfig{
   351  				namespace: "istio-system",
   352  				dnsnames:  [][]string{{"*"}},
   353  			},
   354  			vsconfig: fakeVirtualServiceConfig{
   355  				namespace: "otherns",
   356  				exportTo:  "istio-system",
   357  			},
   358  			vsHost:   "foo.bar",
   359  			expected: true,
   360  		},
   361  		{
   362  			title: "not matching exportTo <namespace>",
   363  			gwconfig: fakeGatewayConfig{
   364  				namespace: "istio-system",
   365  				dnsnames:  [][]string{{"*"}},
   366  			},
   367  			vsconfig: fakeVirtualServiceConfig{
   368  				namespace: "otherns",
   369  				exportTo:  "myns",
   370  			},
   371  			vsHost:   "foo.bar",
   372  			expected: false,
   373  		},
   374  		{
   375  			title: "not matching exportTo .",
   376  			gwconfig: fakeGatewayConfig{
   377  				namespace: "istio-system",
   378  				dnsnames:  [][]string{{"*"}},
   379  			},
   380  			vsconfig: fakeVirtualServiceConfig{
   381  				namespace: "otherns",
   382  				exportTo:  ".",
   383  			},
   384  			vsHost:   "foo.bar",
   385  			expected: false,
   386  		},
   387  	} {
   388  		t.Run(ti.title, func(t *testing.T) {
   389  			vsconfig := ti.vsconfig.Config()
   390  			gwconfig := ti.gwconfig.Config()
   391  			require.Equal(t, ti.expected, virtualServiceBindsToGateway(vsconfig, gwconfig, ti.vsHost))
   392  		})
   393  	}
   394  }
   395  
   396  func testEndpointsFromVirtualServiceConfig(t *testing.T) {
   397  	t.Parallel()
   398  
   399  	for _, ti := range []struct {
   400  		title      string
   401  		lbServices []fakeIngressGatewayService
   402  		ingresses  []fakeIngress
   403  		gwconfig   fakeGatewayConfig
   404  		vsconfig   fakeVirtualServiceConfig
   405  		expected   []*endpoint.Endpoint
   406  	}{
   407  		{
   408  			title: "one rule.host one lb.hostname",
   409  			lbServices: []fakeIngressGatewayService{
   410  				{
   411  					hostnames: []string{"lb.com"}, // Kubernetes omits the trailing dot
   412  				},
   413  			},
   414  			gwconfig: fakeGatewayConfig{
   415  				name:     "mygw",
   416  				dnsnames: [][]string{{"*"}},
   417  			},
   418  			vsconfig: fakeVirtualServiceConfig{
   419  				gateways: []string{"mygw"},
   420  				dnsnames: []string{"foo.bar"},
   421  			},
   422  			expected: []*endpoint.Endpoint{
   423  				{
   424  					DNSName:    "foo.bar",
   425  					RecordType: endpoint.RecordTypeCNAME,
   426  					Targets:    endpoint.Targets{"lb.com"},
   427  				},
   428  			},
   429  		},
   430  		{
   431  			title: "one rule.host one lb.IP",
   432  			lbServices: []fakeIngressGatewayService{
   433  				{
   434  					ips: []string{"8.8.8.8"},
   435  				},
   436  			},
   437  			gwconfig: fakeGatewayConfig{
   438  				name:     "mygw",
   439  				dnsnames: [][]string{{"*"}},
   440  			},
   441  			vsconfig: fakeVirtualServiceConfig{
   442  				gateways: []string{"mygw"},
   443  				dnsnames: []string{"foo.bar"},
   444  			},
   445  			expected: []*endpoint.Endpoint{
   446  				{
   447  					DNSName:    "foo.bar",
   448  					RecordType: endpoint.RecordTypeA,
   449  					Targets:    endpoint.Targets{"8.8.8.8"},
   450  				},
   451  			},
   452  		},
   453  		{
   454  			title: "one rule.host one lb.externalIPs",
   455  			lbServices: []fakeIngressGatewayService{
   456  				{
   457  					externalIPs: []string{"8.8.8.8"},
   458  				},
   459  			},
   460  			gwconfig: fakeGatewayConfig{
   461  				name:     "mygw",
   462  				dnsnames: [][]string{{"*"}},
   463  			},
   464  			vsconfig: fakeVirtualServiceConfig{
   465  				gateways: []string{"mygw"},
   466  				dnsnames: []string{"foo.bar"},
   467  			},
   468  			expected: []*endpoint.Endpoint{
   469  				{
   470  					DNSName:    "foo.bar",
   471  					RecordType: endpoint.RecordTypeA,
   472  					Targets:    endpoint.Targets{"8.8.8.8"},
   473  				},
   474  			},
   475  		},
   476  		{
   477  			title: "one rule.host two lb.IP and two lb.Hostname",
   478  			lbServices: []fakeIngressGatewayService{
   479  				{
   480  					ips:       []string{"8.8.8.8", "127.0.0.1"},
   481  					hostnames: []string{"elb.com", "alb.com"},
   482  				},
   483  			},
   484  			gwconfig: fakeGatewayConfig{
   485  				name:     "mygw",
   486  				dnsnames: [][]string{{"*"}},
   487  			},
   488  			vsconfig: fakeVirtualServiceConfig{
   489  				gateways: []string{"mygw"},
   490  				dnsnames: []string{"foo.bar"},
   491  			},
   492  			expected: []*endpoint.Endpoint{
   493  				{
   494  					DNSName:    "foo.bar",
   495  					RecordType: endpoint.RecordTypeA,
   496  					Targets:    endpoint.Targets{"8.8.8.8", "127.0.0.1"},
   497  				},
   498  				{
   499  					DNSName:    "foo.bar",
   500  					RecordType: endpoint.RecordTypeCNAME,
   501  					Targets:    endpoint.Targets{"elb.com", "alb.com"},
   502  				},
   503  			},
   504  		},
   505  		{
   506  			title: "one rule.host two lb.IP and two lb.Hostname and two lb.externalIPs",
   507  			lbServices: []fakeIngressGatewayService{
   508  				{
   509  					ips:         []string{"8.8.8.8", "127.0.0.1"},
   510  					hostnames:   []string{"elb.com", "alb.com"},
   511  					externalIPs: []string{"1.1.1.1", "2.2.2.2"},
   512  				},
   513  			},
   514  			gwconfig: fakeGatewayConfig{
   515  				name:     "mygw",
   516  				dnsnames: [][]string{{"*"}},
   517  			},
   518  			vsconfig: fakeVirtualServiceConfig{
   519  				gateways: []string{"mygw"},
   520  				dnsnames: []string{"foo.bar"},
   521  			},
   522  			expected: []*endpoint.Endpoint{
   523  				{
   524  					DNSName:    "foo.bar",
   525  					RecordType: endpoint.RecordTypeA,
   526  					Targets:    endpoint.Targets{"1.1.1.1", "2.2.2.2"},
   527  				},
   528  			},
   529  		},
   530  		{
   531  			title: "no rule.host",
   532  			lbServices: []fakeIngressGatewayService{
   533  				{
   534  					ips:         []string{"8.8.8.8", "127.0.0.1"},
   535  					hostnames:   []string{"elb.com", "alb.com"},
   536  					externalIPs: []string{"1.1.1.1", "2.2.2.2"},
   537  				},
   538  			},
   539  			gwconfig: fakeGatewayConfig{
   540  				name:     "mygw",
   541  				dnsnames: [][]string{{"*"}},
   542  			},
   543  			vsconfig: fakeVirtualServiceConfig{
   544  				gateways: []string{"mygw"},
   545  				dnsnames: []string{},
   546  			},
   547  			expected: []*endpoint.Endpoint{},
   548  		},
   549  		{
   550  			title: "no rule.gateway",
   551  			lbServices: []fakeIngressGatewayService{
   552  				{
   553  					ips:         []string{"8.8.8.8", "127.0.0.1"},
   554  					hostnames:   []string{"elb.com", "alb.com"},
   555  					externalIPs: []string{"1.1.1.1", "2.2.2.2"},
   556  				},
   557  			},
   558  			gwconfig: fakeGatewayConfig{
   559  				name:     "mygw",
   560  				dnsnames: [][]string{{"*"}},
   561  			},
   562  			vsconfig: fakeVirtualServiceConfig{
   563  				gateways: []string{},
   564  				dnsnames: []string{"foo.bar"},
   565  			},
   566  			expected: []*endpoint.Endpoint{},
   567  		},
   568  		{
   569  			title: "one empty rule.host",
   570  			lbServices: []fakeIngressGatewayService{
   571  				{
   572  					ips:         []string{"8.8.8.8", "127.0.0.1"},
   573  					hostnames:   []string{"elb.com", "alb.com"},
   574  					externalIPs: []string{"1.1.1.1", "2.2.2.2"},
   575  				},
   576  			},
   577  			gwconfig: fakeGatewayConfig{
   578  				dnsnames: [][]string{
   579  					{""},
   580  				},
   581  			},
   582  			expected: []*endpoint.Endpoint{},
   583  		},
   584  		{
   585  			title:      "no targets",
   586  			lbServices: []fakeIngressGatewayService{{}},
   587  			gwconfig: fakeGatewayConfig{
   588  				name:     "mygw",
   589  				dnsnames: [][]string{{"*"}},
   590  			},
   591  			vsconfig: fakeVirtualServiceConfig{
   592  				gateways: []string{},
   593  				dnsnames: []string{"foo.bar"},
   594  			},
   595  			expected: []*endpoint.Endpoint{},
   596  		},
   597  		{
   598  			title: "matching selectors for service and gateway",
   599  			lbServices: []fakeIngressGatewayService{
   600  				{
   601  					name: "service1",
   602  					selector: map[string]string{
   603  						"app": "myservice",
   604  					},
   605  					hostnames: []string{"elb.com", "alb.com"},
   606  				},
   607  				{
   608  					name: "service2",
   609  					selector: map[string]string{
   610  						"app": "otherservice",
   611  					},
   612  					ips: []string{"8.8.8.8", "127.0.0.1"},
   613  				},
   614  			},
   615  			gwconfig: fakeGatewayConfig{
   616  				name:     "mygw",
   617  				dnsnames: [][]string{{"*"}},
   618  				selector: map[string]string{
   619  					"app": "myservice",
   620  				},
   621  			},
   622  			vsconfig: fakeVirtualServiceConfig{
   623  				gateways: []string{"mygw"},
   624  				dnsnames: []string{"foo.bar"},
   625  			},
   626  			expected: []*endpoint.Endpoint{
   627  				{
   628  					DNSName:    "foo.bar",
   629  					RecordType: endpoint.RecordTypeCNAME,
   630  					Targets:    endpoint.Targets{"elb.com", "alb.com"},
   631  				},
   632  			},
   633  		},
   634  		{
   635  			title: "ingress gateway annotation same namespace",
   636  			ingresses: []fakeIngress{
   637  				{
   638  					name:      "ingress1",
   639  					hostnames: []string{"alb.com", "elb.com"},
   640  				},
   641  				{
   642  					name: "ingress2",
   643  					ips:  []string{"127.0.0.1", "8.8.8.8"},
   644  				},
   645  			},
   646  			gwconfig: fakeGatewayConfig{
   647  				name:     "mygw",
   648  				dnsnames: [][]string{{"*"}},
   649  				annotations: map[string]string{
   650  					IstioGatewayIngressSource: "ingress1",
   651  				},
   652  			},
   653  			vsconfig: fakeVirtualServiceConfig{
   654  				gateways: []string{"mygw"},
   655  				dnsnames: []string{"foo.bar"},
   656  			},
   657  			expected: []*endpoint.Endpoint{
   658  				{
   659  					DNSName:    "foo.bar",
   660  					RecordType: endpoint.RecordTypeCNAME,
   661  					Targets:    endpoint.Targets{"alb.com", "elb.com"},
   662  				},
   663  			},
   664  		},
   665  		{
   666  			title: "ingress gateway annotation separate namespace",
   667  			ingresses: []fakeIngress{
   668  				{
   669  					name:      "ingress1",
   670  					namespace: "ingress",
   671  					hostnames: []string{"alb.com", "elb.com"},
   672  				},
   673  				{
   674  					name:      "ingress2",
   675  					namespace: "ingress",
   676  					ips:       []string{"127.0.0.1", "8.8.8.8"},
   677  				},
   678  			},
   679  			gwconfig: fakeGatewayConfig{
   680  				name:     "mygw",
   681  				dnsnames: [][]string{{"*"}},
   682  				annotations: map[string]string{
   683  					IstioGatewayIngressSource: "ingress/ingress2",
   684  				},
   685  			},
   686  			vsconfig: fakeVirtualServiceConfig{
   687  				gateways: []string{"mygw"},
   688  				dnsnames: []string{"foo.bar"},
   689  			},
   690  			expected: []*endpoint.Endpoint{
   691  				{
   692  					DNSName:    "foo.bar",
   693  					RecordType: endpoint.RecordTypeA,
   694  					Targets:    endpoint.Targets{"127.0.0.1", "8.8.8.8"},
   695  				},
   696  			},
   697  		},
   698  	} {
   699  		ti := ti
   700  		t.Run(ti.title, func(t *testing.T) {
   701  			t.Parallel()
   702  
   703  			if source, err := newTestVirtualServiceSource(ti.lbServices, ti.ingresses, []fakeGatewayConfig{ti.gwconfig}); err != nil {
   704  				require.NoError(t, err)
   705  			} else if endpoints, err := source.endpointsFromVirtualService(context.Background(), ti.vsconfig.Config()); err != nil {
   706  				require.NoError(t, err)
   707  			} else {
   708  				validateEndpoints(t, endpoints, ti.expected)
   709  			}
   710  		})
   711  	}
   712  }
   713  
   714  func testVirtualServiceEndpoints(t *testing.T) {
   715  	t.Parallel()
   716  
   717  	namespace := "testing"
   718  	for _, ti := range []struct {
   719  		title                    string
   720  		targetNamespace          string
   721  		annotationFilter         string
   722  		lbServices               []fakeIngressGatewayService
   723  		ingresses                []fakeIngress
   724  		gwConfigs                []fakeGatewayConfig
   725  		vsConfigs                []fakeVirtualServiceConfig
   726  		expected                 []*endpoint.Endpoint
   727  		expectError              bool
   728  		fqdnTemplate             string
   729  		combineFQDNAndAnnotation bool
   730  		ignoreHostnameAnnotation bool
   731  	}{
   732  		{
   733  			title: "two simple virtualservices with one gateway each, one ingressgateway loadbalancer service",
   734  			lbServices: []fakeIngressGatewayService{
   735  				{
   736  					namespace: namespace,
   737  					ips:       []string{"8.8.8.8"},
   738  					hostnames: []string{"lb.com"},
   739  				},
   740  			},
   741  			gwConfigs: []fakeGatewayConfig{
   742  				{
   743  					name:      "fake1",
   744  					namespace: namespace,
   745  					dnsnames:  [][]string{{"example.org"}},
   746  				},
   747  				{
   748  					name:      "fake2",
   749  					namespace: namespace,
   750  					dnsnames:  [][]string{{"new.org"}},
   751  				},
   752  			},
   753  			vsConfigs: []fakeVirtualServiceConfig{
   754  				{
   755  					name:      "vs1",
   756  					namespace: namespace,
   757  					gateways:  []string{"fake1"},
   758  					dnsnames:  []string{"example.org"},
   759  				},
   760  				{
   761  					name:      "vs2",
   762  					namespace: namespace,
   763  					gateways:  []string{"fake2"},
   764  					dnsnames:  []string{"new.org"},
   765  				},
   766  			},
   767  			expected: []*endpoint.Endpoint{
   768  				{
   769  					DNSName:    "example.org",
   770  					RecordType: endpoint.RecordTypeA,
   771  					Targets:    endpoint.Targets{"8.8.8.8"},
   772  				},
   773  				{
   774  					DNSName:    "example.org",
   775  					RecordType: endpoint.RecordTypeCNAME,
   776  					Targets:    endpoint.Targets{"lb.com"},
   777  				},
   778  				{
   779  					DNSName:    "new.org",
   780  					RecordType: endpoint.RecordTypeA,
   781  					Targets:    endpoint.Targets{"8.8.8.8"},
   782  				},
   783  				{
   784  					DNSName:    "new.org",
   785  					RecordType: endpoint.RecordTypeCNAME,
   786  					Targets:    endpoint.Targets{"lb.com"},
   787  				},
   788  			},
   789  		},
   790  		{
   791  			title: "two simple virtualservices with one gateway each, one ingress",
   792  			lbServices: []fakeIngressGatewayService{
   793  				{
   794  					namespace: namespace,
   795  					ips:       []string{"8.8.8.8"},
   796  					hostnames: []string{"lb.com"},
   797  				},
   798  			},
   799  			ingresses: []fakeIngress{
   800  				{
   801  					name:      "ingress1",
   802  					namespace: namespace,
   803  					ips:       []string{"8.8.8.8"},
   804  					hostnames: []string{"lb.com"},
   805  				},
   806  			},
   807  			gwConfigs: []fakeGatewayConfig{
   808  				{
   809  					name:      "fake1",
   810  					namespace: namespace,
   811  					dnsnames:  [][]string{{"example.org"}},
   812  					annotations: map[string]string{
   813  						IstioGatewayIngressSource: "ingress1",
   814  					},
   815  				},
   816  				{
   817  					name:      "fake2",
   818  					namespace: namespace,
   819  					dnsnames:  [][]string{{"new.org"}},
   820  					annotations: map[string]string{
   821  						IstioGatewayIngressSource: "ingress1",
   822  					},
   823  				},
   824  			},
   825  			vsConfigs: []fakeVirtualServiceConfig{
   826  				{
   827  					name:      "vs1",
   828  					namespace: namespace,
   829  					gateways:  []string{"fake1"},
   830  					dnsnames:  []string{"example.org"},
   831  				},
   832  				{
   833  					name:      "vs2",
   834  					namespace: namespace,
   835  					gateways:  []string{"fake2"},
   836  					dnsnames:  []string{"new.org"},
   837  				},
   838  			},
   839  			expected: []*endpoint.Endpoint{
   840  				{
   841  					DNSName:    "example.org",
   842  					RecordType: endpoint.RecordTypeA,
   843  					Targets:    endpoint.Targets{"8.8.8.8"},
   844  				},
   845  				{
   846  					DNSName:    "example.org",
   847  					RecordType: endpoint.RecordTypeCNAME,
   848  					Targets:    endpoint.Targets{"lb.com"},
   849  				},
   850  				{
   851  					DNSName:    "new.org",
   852  					RecordType: endpoint.RecordTypeA,
   853  					Targets:    endpoint.Targets{"8.8.8.8"},
   854  				},
   855  				{
   856  					DNSName:    "new.org",
   857  					RecordType: endpoint.RecordTypeCNAME,
   858  					Targets:    endpoint.Targets{"lb.com"},
   859  				},
   860  			},
   861  		},
   862  		{
   863  			title: "one virtualservice with two gateways, one ingressgateway loadbalancer service",
   864  			lbServices: []fakeIngressGatewayService{
   865  				{
   866  					namespace: namespace,
   867  					ips:       []string{"8.8.8.8"},
   868  				},
   869  			},
   870  			gwConfigs: []fakeGatewayConfig{
   871  				{
   872  					name:      "gw1",
   873  					namespace: namespace,
   874  					dnsnames:  [][]string{{"*"}},
   875  				},
   876  				{
   877  					name:      "gw2",
   878  					namespace: namespace,
   879  					dnsnames:  [][]string{{"*"}},
   880  				},
   881  			},
   882  			vsConfigs: []fakeVirtualServiceConfig{
   883  				{
   884  					name:      "vs",
   885  					namespace: namespace,
   886  					gateways:  []string{"gw1", "gw2"},
   887  					dnsnames:  []string{"example.org"},
   888  				},
   889  			},
   890  			expected: []*endpoint.Endpoint{
   891  				{
   892  					DNSName:    "example.org",
   893  					RecordType: endpoint.RecordTypeA,
   894  					Targets:    endpoint.Targets{"8.8.8.8"},
   895  				},
   896  			},
   897  		},
   898  		{
   899  			title: "one virtualservice with two gateways, one ingressgateway loadbalancer service with externalIPs",
   900  			lbServices: []fakeIngressGatewayService{
   901  				{
   902  					namespace:   namespace,
   903  					externalIPs: []string{"8.8.8.8"},
   904  				},
   905  			},
   906  			gwConfigs: []fakeGatewayConfig{
   907  				{
   908  					name:      "gw1",
   909  					namespace: namespace,
   910  					dnsnames:  [][]string{{"*"}},
   911  				},
   912  				{
   913  					name:      "gw2",
   914  					namespace: namespace,
   915  					dnsnames:  [][]string{{"*"}},
   916  				},
   917  			},
   918  			vsConfigs: []fakeVirtualServiceConfig{
   919  				{
   920  					name:      "vs",
   921  					namespace: namespace,
   922  					gateways:  []string{"gw1", "gw2"},
   923  					dnsnames:  []string{"example.org"},
   924  				},
   925  			},
   926  			expected: []*endpoint.Endpoint{
   927  				{
   928  					DNSName:    "example.org",
   929  					RecordType: endpoint.RecordTypeA,
   930  					Targets:    endpoint.Targets{"8.8.8.8"},
   931  				},
   932  			},
   933  		},
   934  		{
   935  			title: "two simple virtualservices on different namespaces with the same target gateway, one ingressgateway loadbalancer service",
   936  			lbServices: []fakeIngressGatewayService{
   937  				{
   938  					ips:       []string{"8.8.8.8"},
   939  					hostnames: []string{"lb.com"},
   940  					namespace: "istio-system",
   941  				},
   942  			},
   943  			gwConfigs: []fakeGatewayConfig{
   944  				{
   945  					name:      "fake1",
   946  					namespace: "istio-system",
   947  					dnsnames:  [][]string{{"*"}},
   948  				},
   949  			},
   950  			vsConfigs: []fakeVirtualServiceConfig{
   951  				{
   952  					name:      "vs1",
   953  					namespace: "testing1",
   954  					gateways:  []string{"istio-system/fake1"},
   955  					dnsnames:  []string{"example.org"},
   956  				},
   957  				{
   958  					name:      "vs2",
   959  					namespace: "testing2",
   960  					gateways:  []string{"istio-system/fake1"},
   961  					dnsnames:  []string{"new.org"},
   962  				},
   963  			},
   964  			expected: []*endpoint.Endpoint{
   965  				{
   966  					DNSName:    "example.org",
   967  					RecordType: endpoint.RecordTypeA,
   968  					Targets:    endpoint.Targets{"8.8.8.8"},
   969  				},
   970  				{
   971  					DNSName:    "example.org",
   972  					RecordType: endpoint.RecordTypeCNAME,
   973  					Targets:    endpoint.Targets{"lb.com"},
   974  				},
   975  				{
   976  					DNSName:    "new.org",
   977  					RecordType: endpoint.RecordTypeA,
   978  					Targets:    endpoint.Targets{"8.8.8.8"},
   979  				},
   980  				{
   981  					DNSName:    "new.org",
   982  					RecordType: endpoint.RecordTypeCNAME,
   983  					Targets:    endpoint.Targets{"lb.com"},
   984  				},
   985  			},
   986  		},
   987  		{
   988  			title:           "two simple virtualservices with one gateway on different namespaces and a target namespace, one ingressgateway loadbalancer service",
   989  			targetNamespace: "testing1",
   990  			lbServices: []fakeIngressGatewayService{
   991  				{
   992  					ips:       []string{"8.8.8.8"},
   993  					hostnames: []string{"lb.com"},
   994  					namespace: "testing1",
   995  				},
   996  			},
   997  			gwConfigs: []fakeGatewayConfig{
   998  				{
   999  					name:      "fake1",
  1000  					namespace: "testing1",
  1001  					dnsnames:  [][]string{{"*"}},
  1002  				},
  1003  			},
  1004  			vsConfigs: []fakeVirtualServiceConfig{
  1005  				{
  1006  					name:      "vs1",
  1007  					namespace: "testing1",
  1008  					gateways:  []string{"testing1/fake1"},
  1009  					dnsnames:  []string{"example.org"},
  1010  				},
  1011  				{
  1012  					name:      "vs2",
  1013  					namespace: "testing2",
  1014  					gateways:  []string{"testing1/fake1"},
  1015  					dnsnames:  []string{"new.org"},
  1016  				},
  1017  			},
  1018  			expected: []*endpoint.Endpoint{
  1019  				{
  1020  					DNSName:    "example.org",
  1021  					RecordType: endpoint.RecordTypeA,
  1022  					Targets:    endpoint.Targets{"8.8.8.8"},
  1023  				},
  1024  				{
  1025  					DNSName:    "example.org",
  1026  					RecordType: endpoint.RecordTypeCNAME,
  1027  					Targets:    endpoint.Targets{"lb.com"},
  1028  				},
  1029  			},
  1030  		},
  1031  		{
  1032  			title:           "two simple virtualservices with one gateway on different namespaces and a target namespace, one ingressgateway loadbalancer service with externalIPs",
  1033  			targetNamespace: "testing1",
  1034  			lbServices: []fakeIngressGatewayService{
  1035  				{
  1036  					externalIPs: []string{"8.8.8.8"},
  1037  					namespace:   "testing1",
  1038  				},
  1039  			},
  1040  			gwConfigs: []fakeGatewayConfig{
  1041  				{
  1042  					name:      "fake1",
  1043  					namespace: "testing1",
  1044  					dnsnames:  [][]string{{"*"}},
  1045  				},
  1046  			},
  1047  			vsConfigs: []fakeVirtualServiceConfig{
  1048  				{
  1049  					name:      "vs1",
  1050  					namespace: "testing1",
  1051  					gateways:  []string{"testing1/fake1"},
  1052  					dnsnames:  []string{"example.org"},
  1053  				},
  1054  				{
  1055  					name:      "vs2",
  1056  					namespace: "testing2",
  1057  					gateways:  []string{"testing1/fake1"},
  1058  					dnsnames:  []string{"new.org"},
  1059  				},
  1060  			},
  1061  			expected: []*endpoint.Endpoint{
  1062  				{
  1063  					DNSName:    "example.org",
  1064  					RecordType: endpoint.RecordTypeA,
  1065  					Targets:    endpoint.Targets{"8.8.8.8"},
  1066  				},
  1067  			},
  1068  		},
  1069  		{
  1070  			title:           "two simple virtualservices with one gateway on different namespaces and a target namespace, one ingress",
  1071  			targetNamespace: "testing1",
  1072  			ingresses: []fakeIngress{
  1073  				{
  1074  					name:      "ingress1",
  1075  					ips:       []string{"8.8.8.8"},
  1076  					hostnames: []string{"lb.com"},
  1077  					namespace: "testing1",
  1078  				},
  1079  			},
  1080  			gwConfigs: []fakeGatewayConfig{
  1081  				{
  1082  					name:      "fake1",
  1083  					namespace: "testing1",
  1084  					dnsnames:  [][]string{{"*"}},
  1085  					annotations: map[string]string{
  1086  						IstioGatewayIngressSource: "ingress1",
  1087  					},
  1088  				},
  1089  			},
  1090  			vsConfigs: []fakeVirtualServiceConfig{
  1091  				{
  1092  					name:      "vs1",
  1093  					namespace: "testing1",
  1094  					gateways:  []string{"testing1/fake1"},
  1095  					dnsnames:  []string{"example.org"},
  1096  				},
  1097  				{
  1098  					name:      "vs2",
  1099  					namespace: "testing2",
  1100  					gateways:  []string{"testing1/fake1"},
  1101  					dnsnames:  []string{"new.org"},
  1102  				},
  1103  			},
  1104  			expected: []*endpoint.Endpoint{
  1105  				{
  1106  					DNSName:    "example.org",
  1107  					RecordType: endpoint.RecordTypeA,
  1108  					Targets:    endpoint.Targets{"8.8.8.8"},
  1109  				},
  1110  				{
  1111  					DNSName:    "example.org",
  1112  					RecordType: endpoint.RecordTypeCNAME,
  1113  					Targets:    endpoint.Targets{"lb.com"},
  1114  				},
  1115  			},
  1116  		},
  1117  		{
  1118  			title:            "valid matching annotation filter expression",
  1119  			annotationFilter: "kubernetes.io/virtualservice.class in (alb, nginx)",
  1120  			lbServices: []fakeIngressGatewayService{
  1121  				{
  1122  					ips:       []string{"8.8.8.8"},
  1123  					namespace: namespace,
  1124  				},
  1125  			},
  1126  			gwConfigs: []fakeGatewayConfig{
  1127  				{
  1128  					name:      "fake1",
  1129  					namespace: namespace,
  1130  					dnsnames:  [][]string{{"*"}},
  1131  				},
  1132  			},
  1133  			vsConfigs: []fakeVirtualServiceConfig{
  1134  				{
  1135  					name:      "vs1",
  1136  					namespace: namespace,
  1137  					annotations: map[string]string{
  1138  						"kubernetes.io/virtualservice.class": "nginx",
  1139  					},
  1140  					gateways: []string{"fake1"},
  1141  					dnsnames: []string{"example.org"},
  1142  				},
  1143  			},
  1144  			expected: []*endpoint.Endpoint{
  1145  				{
  1146  					DNSName:    "example.org",
  1147  					RecordType: endpoint.RecordTypeA,
  1148  					Targets:    endpoint.Targets{"8.8.8.8"},
  1149  				},
  1150  			},
  1151  		},
  1152  		{
  1153  			title:            "valid non-matching annotation filter expression",
  1154  			annotationFilter: "kubernetes.io/gateway.class in (alb, nginx)",
  1155  			lbServices: []fakeIngressGatewayService{
  1156  				{
  1157  					ips:       []string{"8.8.8.8"},
  1158  					namespace: namespace,
  1159  				},
  1160  			},
  1161  			gwConfigs: []fakeGatewayConfig{
  1162  				{
  1163  					name:      "fake1",
  1164  					namespace: namespace,
  1165  					dnsnames:  [][]string{{"*"}},
  1166  				},
  1167  			},
  1168  			vsConfigs: []fakeVirtualServiceConfig{
  1169  				{
  1170  					name:      "vs1",
  1171  					namespace: namespace,
  1172  					annotations: map[string]string{
  1173  						"kubernetes.io/virtualservice.class": "tectonic",
  1174  					},
  1175  					gateways: []string{"fake1"},
  1176  					dnsnames: []string{"example.org"},
  1177  				},
  1178  			},
  1179  			expected: []*endpoint.Endpoint{},
  1180  		},
  1181  		{
  1182  			title:            "invalid annotation filter expression",
  1183  			annotationFilter: "kubernetes.io/gateway.name in (a b)",
  1184  			expected:         []*endpoint.Endpoint{},
  1185  			expectError:      true,
  1186  		},
  1187  		{
  1188  			title: "gateway ingress annotation; ingress not found",
  1189  			ingresses: []fakeIngress{
  1190  				{
  1191  					name:      "ingress1",
  1192  					namespace: namespace,
  1193  					ips:       []string{"8.8.8.8"},
  1194  				},
  1195  			},
  1196  			gwConfigs: []fakeGatewayConfig{
  1197  				{
  1198  					name:      "fake1",
  1199  					namespace: namespace,
  1200  					dnsnames:  [][]string{{"*"}},
  1201  					annotations: map[string]string{
  1202  						IstioGatewayIngressSource: "ingress2",
  1203  					},
  1204  				},
  1205  			},
  1206  			vsConfigs: []fakeVirtualServiceConfig{
  1207  				{
  1208  					name:      "vs1",
  1209  					namespace: namespace,
  1210  					gateways:  []string{"fake1"},
  1211  					dnsnames:  []string{"example.org"},
  1212  				},
  1213  			},
  1214  			expected:    []*endpoint.Endpoint{},
  1215  			expectError: true,
  1216  		},
  1217  		{
  1218  			title: "our controller type is dns-controller",
  1219  			lbServices: []fakeIngressGatewayService{
  1220  				{
  1221  					ips:       []string{"8.8.8.8"},
  1222  					namespace: namespace,
  1223  				},
  1224  			},
  1225  			gwConfigs: []fakeGatewayConfig{
  1226  				{
  1227  					name:      "fake1",
  1228  					namespace: namespace,
  1229  					dnsnames:  [][]string{{"*"}},
  1230  				},
  1231  			},
  1232  			vsConfigs: []fakeVirtualServiceConfig{
  1233  				{
  1234  					name:      "vs1",
  1235  					namespace: namespace,
  1236  					annotations: map[string]string{
  1237  						controllerAnnotationKey: controllerAnnotationValue,
  1238  					},
  1239  					gateways: []string{"fake1"},
  1240  					dnsnames: []string{"example.org"},
  1241  				},
  1242  			},
  1243  			expected: []*endpoint.Endpoint{
  1244  				{
  1245  					DNSName:    "example.org",
  1246  					RecordType: endpoint.RecordTypeA,
  1247  					Targets:    endpoint.Targets{"8.8.8.8"},
  1248  				},
  1249  			},
  1250  		},
  1251  		{
  1252  			title: "different controller types are ignored",
  1253  			lbServices: []fakeIngressGatewayService{
  1254  				{
  1255  					ips:       []string{"8.8.8.8"},
  1256  					namespace: namespace,
  1257  				},
  1258  			},
  1259  			gwConfigs: []fakeGatewayConfig{
  1260  				{
  1261  					name:      "fake1",
  1262  					namespace: namespace,
  1263  					dnsnames:  [][]string{{"*"}},
  1264  				},
  1265  			},
  1266  			vsConfigs: []fakeVirtualServiceConfig{
  1267  				{
  1268  					name:      "vs1",
  1269  					namespace: namespace,
  1270  					annotations: map[string]string{
  1271  						controllerAnnotationKey: "some-other-tool",
  1272  					},
  1273  					gateways: []string{"fake1"},
  1274  					dnsnames: []string{"example.org"},
  1275  				},
  1276  			},
  1277  			expected: []*endpoint.Endpoint{},
  1278  		},
  1279  		{
  1280  			title: "template for virtualservice if host is missing",
  1281  			lbServices: []fakeIngressGatewayService{
  1282  				{
  1283  					ips:       []string{"8.8.8.8"},
  1284  					hostnames: []string{"elb.com"},
  1285  					namespace: namespace,
  1286  				},
  1287  			},
  1288  			gwConfigs: []fakeGatewayConfig{
  1289  				{
  1290  					name:      "fake1",
  1291  					namespace: namespace,
  1292  					dnsnames:  [][]string{{"*"}},
  1293  				},
  1294  			},
  1295  			vsConfigs: []fakeVirtualServiceConfig{
  1296  				{
  1297  					name:      "vs1",
  1298  					namespace: namespace,
  1299  					gateways:  []string{"fake1"},
  1300  					dnsnames:  []string{""},
  1301  				},
  1302  			},
  1303  			expected: []*endpoint.Endpoint{
  1304  				{
  1305  					DNSName:    "vs1.ext-dns.test.com",
  1306  					RecordType: endpoint.RecordTypeA,
  1307  					Targets:    endpoint.Targets{"8.8.8.8"},
  1308  				},
  1309  				{
  1310  					DNSName:    "vs1.ext-dns.test.com",
  1311  					RecordType: endpoint.RecordTypeCNAME,
  1312  					Targets:    endpoint.Targets{"elb.com"},
  1313  				},
  1314  			},
  1315  			fqdnTemplate: "{{.Name}}.ext-dns.test.com",
  1316  		},
  1317  		{
  1318  			title: "multiple FQDN template hostnames",
  1319  			lbServices: []fakeIngressGatewayService{
  1320  				{
  1321  					ips:       []string{"8.8.8.8"},
  1322  					namespace: namespace,
  1323  				},
  1324  			},
  1325  			gwConfigs: []fakeGatewayConfig{
  1326  				{
  1327  					name:      "fake1",
  1328  					namespace: namespace,
  1329  					dnsnames:  [][]string{{"*"}},
  1330  				},
  1331  			},
  1332  			vsConfigs: []fakeVirtualServiceConfig{
  1333  				{
  1334  					name:      "vs1",
  1335  					namespace: namespace,
  1336  					gateways:  []string{"fake1"},
  1337  					dnsnames:  []string{""},
  1338  				},
  1339  			},
  1340  			expected: []*endpoint.Endpoint{
  1341  				{
  1342  					DNSName:    "vs1.ext-dns.test.com",
  1343  					Targets:    endpoint.Targets{"8.8.8.8"},
  1344  					RecordType: endpoint.RecordTypeA,
  1345  				},
  1346  				{
  1347  					DNSName:    "vs1.ext-dna.test.com",
  1348  					Targets:    endpoint.Targets{"8.8.8.8"},
  1349  					RecordType: endpoint.RecordTypeA,
  1350  				},
  1351  			},
  1352  			fqdnTemplate: "{{.Name}}.ext-dns.test.com, {{.Name}}.ext-dna.test.com",
  1353  		},
  1354  		{
  1355  			title: "multiple FQDN template hostnames with restricted gw.hosts",
  1356  			gwConfigs: []fakeGatewayConfig{
  1357  				{
  1358  					name:      "fake1",
  1359  					namespace: namespace,
  1360  					annotations: map[string]string{
  1361  						targetAnnotationKey: "gateway-target.com",
  1362  					},
  1363  					dnsnames: [][]string{{"*.org", "*.ext-dns.test.com"}},
  1364  				},
  1365  			},
  1366  			vsConfigs: []fakeVirtualServiceConfig{
  1367  				{
  1368  					name:      "vs1",
  1369  					namespace: namespace,
  1370  					gateways:  []string{"fake1"},
  1371  					dnsnames:  []string{"example.org"},
  1372  				},
  1373  				{
  1374  					name:      "vs2",
  1375  					namespace: namespace,
  1376  					gateways:  []string{"fake1"},
  1377  					dnsnames:  []string{},
  1378  				},
  1379  			},
  1380  			expected: []*endpoint.Endpoint{
  1381  				{
  1382  					DNSName:    "vs1.ext-dns.test.com",
  1383  					Targets:    endpoint.Targets{"gateway-target.com"},
  1384  					RecordType: endpoint.RecordTypeCNAME,
  1385  				},
  1386  				{
  1387  					DNSName:    "vs2.ext-dns.test.com",
  1388  					Targets:    endpoint.Targets{"gateway-target.com"},
  1389  					RecordType: endpoint.RecordTypeCNAME,
  1390  				},
  1391  				{
  1392  					DNSName:    "example.org",
  1393  					Targets:    endpoint.Targets{"gateway-target.com"},
  1394  					RecordType: endpoint.RecordTypeCNAME,
  1395  				},
  1396  			},
  1397  			fqdnTemplate:             "{{.Name}}.ext-dns.test.com, {{.Name}}.ext-dna.test.com",
  1398  			combineFQDNAndAnnotation: true,
  1399  		},
  1400  		{
  1401  			title: "virtualservice with target annotation",
  1402  			lbServices: []fakeIngressGatewayService{
  1403  				{
  1404  					ips:       []string{"8.8.8.8"},
  1405  					namespace: namespace,
  1406  				},
  1407  			},
  1408  			vsConfigs: []fakeVirtualServiceConfig{
  1409  				{
  1410  					name:      "vs1",
  1411  					namespace: namespace,
  1412  					gateways:  []string{"fake1"},
  1413  					annotations: map[string]string{
  1414  						targetAnnotationKey: "virtualservice-target.com",
  1415  					},
  1416  					dnsnames: []string{"example.org"},
  1417  				},
  1418  				{
  1419  					name:      "vs2",
  1420  					namespace: namespace,
  1421  					gateways:  []string{"fake1"},
  1422  					annotations: map[string]string{
  1423  						targetAnnotationKey: "virtualservice-target.com",
  1424  					},
  1425  					dnsnames: []string{"example2.org"},
  1426  				},
  1427  			},
  1428  			expected: []*endpoint.Endpoint{
  1429  				{
  1430  					DNSName:    "example.org",
  1431  					Targets:    endpoint.Targets{"virtualservice-target.com"},
  1432  					RecordType: endpoint.RecordTypeCNAME,
  1433  				},
  1434  				{
  1435  					DNSName:    "example2.org",
  1436  					Targets:    endpoint.Targets{"virtualservice-target.com"},
  1437  					RecordType: endpoint.RecordTypeCNAME,
  1438  				},
  1439  			},
  1440  		},
  1441  		{
  1442  			title: "virtualservice; gateway with target annotation",
  1443  			lbServices: []fakeIngressGatewayService{
  1444  				{
  1445  					ips:       []string{"8.8.8.8"},
  1446  					namespace: namespace,
  1447  				},
  1448  			},
  1449  			gwConfigs: []fakeGatewayConfig{
  1450  				{
  1451  					name:      "fake1",
  1452  					namespace: namespace,
  1453  					dnsnames:  [][]string{{"*"}},
  1454  					annotations: map[string]string{
  1455  						targetAnnotationKey: "gateway-target.com",
  1456  					},
  1457  				},
  1458  			},
  1459  			vsConfigs: []fakeVirtualServiceConfig{
  1460  				{
  1461  					name:      "vs1",
  1462  					namespace: namespace,
  1463  					gateways:  []string{"fake1"},
  1464  					dnsnames:  []string{"example.org"},
  1465  				},
  1466  				{
  1467  					name:      "vs2",
  1468  					namespace: namespace,
  1469  					gateways:  []string{"fake1"},
  1470  					dnsnames:  []string{"example2.org"},
  1471  				},
  1472  			},
  1473  			expected: []*endpoint.Endpoint{
  1474  				{
  1475  					DNSName:    "example.org",
  1476  					Targets:    endpoint.Targets{"gateway-target.com"},
  1477  					RecordType: endpoint.RecordTypeCNAME,
  1478  				},
  1479  				{
  1480  					DNSName:    "example2.org",
  1481  					Targets:    endpoint.Targets{"gateway-target.com"},
  1482  					RecordType: endpoint.RecordTypeCNAME,
  1483  				},
  1484  			},
  1485  		},
  1486  		{
  1487  			title: "virtualservice; gateway with target and ingress annotation",
  1488  			lbServices: []fakeIngressGatewayService{
  1489  				{
  1490  					ips:       []string{"8.8.8.8"},
  1491  					namespace: namespace,
  1492  				},
  1493  			},
  1494  			ingresses: []fakeIngress{
  1495  				{
  1496  					name:      "ingress1",
  1497  					ips:       []string{"1.1.1.1"},
  1498  					namespace: namespace,
  1499  				},
  1500  			},
  1501  			gwConfigs: []fakeGatewayConfig{
  1502  				{
  1503  					name:      "fake1",
  1504  					namespace: namespace,
  1505  					dnsnames:  [][]string{{"*"}},
  1506  					annotations: map[string]string{
  1507  						targetAnnotationKey:       "gateway-target.com",
  1508  						IstioGatewayIngressSource: "ingress1",
  1509  					},
  1510  				},
  1511  			},
  1512  			vsConfigs: []fakeVirtualServiceConfig{
  1513  				{
  1514  					name:      "vs1",
  1515  					namespace: namespace,
  1516  					gateways:  []string{"fake1"},
  1517  					dnsnames:  []string{"example.org"},
  1518  				},
  1519  				{
  1520  					name:      "vs2",
  1521  					namespace: namespace,
  1522  					gateways:  []string{"fake1"},
  1523  					dnsnames:  []string{"example2.org"},
  1524  				},
  1525  			},
  1526  			expected: []*endpoint.Endpoint{
  1527  				{
  1528  					DNSName:    "example.org",
  1529  					Targets:    endpoint.Targets{"gateway-target.com"},
  1530  					RecordType: endpoint.RecordTypeCNAME,
  1531  				},
  1532  				{
  1533  					DNSName:    "example2.org",
  1534  					Targets:    endpoint.Targets{"gateway-target.com"},
  1535  					RecordType: endpoint.RecordTypeCNAME,
  1536  				},
  1537  			},
  1538  		},
  1539  		{
  1540  			title: "virtualservice with hostname annotation",
  1541  			lbServices: []fakeIngressGatewayService{
  1542  				{
  1543  					ips:       []string{"1.2.3.4"},
  1544  					namespace: namespace,
  1545  				},
  1546  			},
  1547  			gwConfigs: []fakeGatewayConfig{
  1548  				{
  1549  					name:      "fake1",
  1550  					namespace: namespace,
  1551  					dnsnames:  [][]string{{"*"}},
  1552  				},
  1553  			},
  1554  			vsConfigs: []fakeVirtualServiceConfig{
  1555  				{
  1556  					name:      "vs1",
  1557  					namespace: namespace,
  1558  					gateways:  []string{"fake1"},
  1559  					annotations: map[string]string{
  1560  						hostnameAnnotationKey: "dns-through-hostname.com",
  1561  					},
  1562  					dnsnames: []string{"example.org"},
  1563  				},
  1564  			},
  1565  			expected: []*endpoint.Endpoint{
  1566  				{
  1567  					DNSName:    "example.org",
  1568  					Targets:    endpoint.Targets{"1.2.3.4"},
  1569  					RecordType: endpoint.RecordTypeA,
  1570  				},
  1571  				{
  1572  					DNSName:    "dns-through-hostname.com",
  1573  					Targets:    endpoint.Targets{"1.2.3.4"},
  1574  					RecordType: endpoint.RecordTypeA,
  1575  				},
  1576  			},
  1577  		},
  1578  		{
  1579  			title: "virtualservice with hostname annotation having multiple hostnames, restricted by gw.hosts",
  1580  			lbServices: []fakeIngressGatewayService{
  1581  				{
  1582  					ips:       []string{"1.2.3.4"},
  1583  					namespace: namespace,
  1584  				},
  1585  			},
  1586  			gwConfigs: []fakeGatewayConfig{
  1587  				{
  1588  					name:      "fake1",
  1589  					namespace: namespace,
  1590  					dnsnames:  [][]string{{"*.bar.com"}},
  1591  				},
  1592  			},
  1593  			vsConfigs: []fakeVirtualServiceConfig{
  1594  				{
  1595  					name:      "vs1",
  1596  					namespace: namespace,
  1597  					gateways:  []string{"fake1"},
  1598  					annotations: map[string]string{
  1599  						hostnameAnnotationKey: "foo.bar.com, another-dns-through-hostname.com",
  1600  					},
  1601  					dnsnames: []string{"baz.bar.org"},
  1602  				},
  1603  			},
  1604  			expected: []*endpoint.Endpoint{
  1605  				{
  1606  					DNSName:    "foo.bar.com",
  1607  					Targets:    endpoint.Targets{"1.2.3.4"},
  1608  					RecordType: endpoint.RecordTypeA,
  1609  				},
  1610  			},
  1611  		},
  1612  		{
  1613  			title: "virtualservices with annotation and custom TTL",
  1614  			lbServices: []fakeIngressGatewayService{
  1615  				{
  1616  					ips:       []string{"8.8.8.8"},
  1617  					namespace: namespace,
  1618  				},
  1619  			},
  1620  			gwConfigs: []fakeGatewayConfig{
  1621  				{
  1622  					name:      "fake1",
  1623  					namespace: namespace,
  1624  					dnsnames:  [][]string{{"*"}},
  1625  				},
  1626  			},
  1627  			vsConfigs: []fakeVirtualServiceConfig{
  1628  				{
  1629  					name:      "vs1",
  1630  					namespace: namespace,
  1631  					gateways:  []string{"fake1"},
  1632  					annotations: map[string]string{
  1633  						ttlAnnotationKey: "6",
  1634  					},
  1635  					dnsnames: []string{"example.org"},
  1636  				},
  1637  				{
  1638  					name:      "vs2",
  1639  					namespace: namespace,
  1640  					gateways:  []string{"fake1"},
  1641  					annotations: map[string]string{
  1642  						ttlAnnotationKey: "1",
  1643  					},
  1644  					dnsnames: []string{"example2.org"},
  1645  				},
  1646  			},
  1647  			expected: []*endpoint.Endpoint{
  1648  				{
  1649  					DNSName:    "example.org",
  1650  					RecordType: endpoint.RecordTypeA,
  1651  					Targets:    endpoint.Targets{"8.8.8.8"},
  1652  					RecordTTL:  endpoint.TTL(6),
  1653  				},
  1654  				{
  1655  					DNSName:    "example2.org",
  1656  					RecordType: endpoint.RecordTypeA,
  1657  					Targets:    endpoint.Targets{"8.8.8.8"},
  1658  					RecordTTL:  endpoint.TTL(1),
  1659  				},
  1660  			},
  1661  		},
  1662  		{
  1663  			title: "template for virtualservice; gateway with target annotation",
  1664  			gwConfigs: []fakeGatewayConfig{
  1665  				{
  1666  					name:      "fake1",
  1667  					namespace: namespace,
  1668  					annotations: map[string]string{
  1669  						targetAnnotationKey: "gateway-target.com",
  1670  					},
  1671  					dnsnames: [][]string{{"*"}},
  1672  				},
  1673  				{
  1674  					name:      "fake2",
  1675  					namespace: namespace,
  1676  					annotations: map[string]string{
  1677  						targetAnnotationKey: "gateway-target.com",
  1678  					},
  1679  					dnsnames: [][]string{{"*"}},
  1680  				},
  1681  				{
  1682  					name:      "fake3",
  1683  					namespace: namespace,
  1684  					annotations: map[string]string{
  1685  						targetAnnotationKey: "1.2.3.4",
  1686  					},
  1687  					dnsnames: [][]string{{"*"}},
  1688  				},
  1689  			},
  1690  			vsConfigs: []fakeVirtualServiceConfig{
  1691  				{
  1692  					name:      "vs1",
  1693  					namespace: namespace,
  1694  					gateways:  []string{"fake1"},
  1695  					dnsnames:  []string{},
  1696  				},
  1697  				{
  1698  					name:      "vs2",
  1699  					namespace: namespace,
  1700  					gateways:  []string{"fake2"},
  1701  					dnsnames:  []string{},
  1702  				},
  1703  				{
  1704  					name:      "vs3",
  1705  					namespace: namespace,
  1706  					gateways:  []string{"fake3"},
  1707  					dnsnames:  []string{},
  1708  				},
  1709  			},
  1710  			expected: []*endpoint.Endpoint{
  1711  				{
  1712  					DNSName:    "vs1.ext-dns.test.com",
  1713  					Targets:    endpoint.Targets{"gateway-target.com"},
  1714  					RecordType: endpoint.RecordTypeCNAME,
  1715  				},
  1716  				{
  1717  					DNSName:    "vs2.ext-dns.test.com",
  1718  					Targets:    endpoint.Targets{"gateway-target.com"},
  1719  					RecordType: endpoint.RecordTypeCNAME,
  1720  				},
  1721  				{
  1722  					DNSName:    "vs3.ext-dns.test.com",
  1723  					Targets:    endpoint.Targets{"1.2.3.4"},
  1724  					RecordType: endpoint.RecordTypeA,
  1725  				},
  1726  			},
  1727  			fqdnTemplate: "{{.Name}}.ext-dns.test.com",
  1728  		},
  1729  		{
  1730  			title: "ignore hostname annotations",
  1731  			lbServices: []fakeIngressGatewayService{
  1732  				{
  1733  					ips:       []string{"8.8.8.8"},
  1734  					hostnames: []string{"lb.com"},
  1735  					namespace: namespace,
  1736  				},
  1737  			},
  1738  			gwConfigs: []fakeGatewayConfig{
  1739  				{
  1740  					name:      "fake1",
  1741  					namespace: namespace,
  1742  					dnsnames:  [][]string{{"*"}},
  1743  				},
  1744  			},
  1745  			vsConfigs: []fakeVirtualServiceConfig{
  1746  				{
  1747  					name:      "vs1",
  1748  					namespace: namespace,
  1749  					gateways:  []string{"fake1"},
  1750  					annotations: map[string]string{
  1751  						hostnameAnnotationKey: "ignore.me",
  1752  					},
  1753  					dnsnames: []string{"example.org"},
  1754  				},
  1755  				{
  1756  					name:      "vs2",
  1757  					namespace: namespace,
  1758  					gateways:  []string{"fake1"},
  1759  					annotations: map[string]string{
  1760  						hostnameAnnotationKey: "ignore.me.too",
  1761  					},
  1762  					dnsnames: []string{"new.org"},
  1763  				},
  1764  			},
  1765  			expected: []*endpoint.Endpoint{
  1766  				{
  1767  					DNSName:    "example.org",
  1768  					RecordType: endpoint.RecordTypeA,
  1769  					Targets:    endpoint.Targets{"8.8.8.8"},
  1770  				},
  1771  				{
  1772  					DNSName:    "example.org",
  1773  					RecordType: endpoint.RecordTypeCNAME,
  1774  					Targets:    endpoint.Targets{"lb.com"},
  1775  				},
  1776  				{
  1777  					DNSName:    "new.org",
  1778  					RecordType: endpoint.RecordTypeA,
  1779  					Targets:    endpoint.Targets{"8.8.8.8"},
  1780  				},
  1781  				{
  1782  					DNSName:    "new.org",
  1783  					RecordType: endpoint.RecordTypeCNAME,
  1784  					Targets:    endpoint.Targets{"lb.com"},
  1785  				},
  1786  			},
  1787  			ignoreHostnameAnnotation: true,
  1788  		},
  1789  		{
  1790  			title: "complex setup with multiple gateways and multiple vs.hosts only matching some of the gateway",
  1791  			lbServices: []fakeIngressGatewayService{
  1792  				{
  1793  					name: "svc1",
  1794  					selector: map[string]string{
  1795  						"app": "igw1",
  1796  					},
  1797  					hostnames: []string{"target1.com"},
  1798  					namespace: "istio-system",
  1799  				},
  1800  				{
  1801  					name: "svc2",
  1802  					selector: map[string]string{
  1803  						"app": "igw2",
  1804  					},
  1805  					hostnames: []string{"target2.com"},
  1806  					namespace: "testing1",
  1807  				},
  1808  				{
  1809  					name: "svc3",
  1810  					selector: map[string]string{
  1811  						"app": "igw3",
  1812  					},
  1813  					hostnames: []string{"target3.com"},
  1814  					namespace: "testing2",
  1815  				},
  1816  			},
  1817  			ingresses: []fakeIngress{
  1818  				{
  1819  					name:      "ingress1",
  1820  					namespace: "testing1",
  1821  					hostnames: []string{"target4.com"},
  1822  				},
  1823  				{
  1824  					name:      "ingress3",
  1825  					namespace: "testing3",
  1826  					hostnames: []string{"target5.com"},
  1827  				},
  1828  			},
  1829  			gwConfigs: []fakeGatewayConfig{
  1830  				{
  1831  					name:      "fake1",
  1832  					namespace: "istio-system",
  1833  					dnsnames:  [][]string{{"*"}},
  1834  					selector: map[string]string{
  1835  						"app": "igw1",
  1836  					},
  1837  				},
  1838  				{
  1839  					name:      "fake2",
  1840  					namespace: "testing1",
  1841  					dnsnames:  [][]string{{"*.baz.com"}, {"*.bar.com"}},
  1842  					selector: map[string]string{
  1843  						"app": "igw2",
  1844  					},
  1845  				},
  1846  				{
  1847  					name:      "fake3",
  1848  					namespace: "testing2",
  1849  					dnsnames:  [][]string{{"*.bax.com", "*.bar.com"}},
  1850  					selector: map[string]string{
  1851  						"app": "igw3",
  1852  					},
  1853  				},
  1854  				{
  1855  					name:      "fake4",
  1856  					namespace: "testing3",
  1857  					dnsnames:  [][]string{{"*.bax.com", "*.bar.com"}},
  1858  					selector: map[string]string{
  1859  						"app": "igw4",
  1860  					},
  1861  					annotations: map[string]string{
  1862  						IstioGatewayIngressSource: "testing1/ingress1",
  1863  					},
  1864  				},
  1865  			},
  1866  			vsConfigs: []fakeVirtualServiceConfig{
  1867  				{
  1868  					name:      "vs1",
  1869  					namespace: "testing3",
  1870  					gateways:  []string{"istio-system/fake1", "testing1/fake2"},
  1871  					dnsnames:  []string{"somedomain.com", "foo.bar.com"},
  1872  				},
  1873  				{
  1874  					name:      "vs2",
  1875  					namespace: "testing2",
  1876  					gateways:  []string{"testing1/fake2", "fake3"},
  1877  					dnsnames:  []string{"hello.bar.com", "hello.bax.com", "hello.bak.com"},
  1878  				},
  1879  				{
  1880  					name:      "vs3",
  1881  					namespace: "testing1",
  1882  					gateways:  []string{"istio-system/fake1", "testing2/fake3"},
  1883  					dnsnames:  []string{"world.bax.com", "world.bak.com"},
  1884  				},
  1885  				{
  1886  					name:      "vs4",
  1887  					namespace: "testing1",
  1888  					gateways:  []string{"istio-system/fake1", "testing3/fake4"},
  1889  					dnsnames:  []string{"foo.bax.com", "foo.bak.com"},
  1890  				},
  1891  			},
  1892  			expected: []*endpoint.Endpoint{
  1893  				{
  1894  					DNSName:    "somedomain.com",
  1895  					Targets:    endpoint.Targets{"target1.com"},
  1896  					RecordType: endpoint.RecordTypeCNAME,
  1897  				},
  1898  				{
  1899  					DNSName:    "foo.bar.com",
  1900  					Targets:    endpoint.Targets{"target1.com", "target2.com"},
  1901  					RecordType: endpoint.RecordTypeCNAME,
  1902  				},
  1903  				{
  1904  					DNSName:    "hello.bar.com",
  1905  					Targets:    endpoint.Targets{"target2.com", "target3.com"},
  1906  					RecordType: endpoint.RecordTypeCNAME,
  1907  				},
  1908  				{
  1909  					DNSName:    "hello.bax.com",
  1910  					Targets:    endpoint.Targets{"target3.com"},
  1911  					RecordType: endpoint.RecordTypeCNAME,
  1912  				},
  1913  				{
  1914  					DNSName:    "world.bak.com",
  1915  					Targets:    endpoint.Targets{"target1.com"},
  1916  					RecordType: endpoint.RecordTypeCNAME,
  1917  				},
  1918  				{
  1919  					DNSName:    "world.bax.com",
  1920  					Targets:    endpoint.Targets{"target1.com", "target3.com"},
  1921  					RecordType: endpoint.RecordTypeCNAME,
  1922  				},
  1923  				{
  1924  					DNSName:    "foo.bak.com",
  1925  					Targets:    endpoint.Targets{"target1.com"},
  1926  					RecordType: endpoint.RecordTypeCNAME,
  1927  				},
  1928  				{
  1929  					DNSName:    "foo.bax.com",
  1930  					Targets:    endpoint.Targets{"target1.com", "target4.com"},
  1931  					RecordType: endpoint.RecordTypeCNAME,
  1932  				},
  1933  			},
  1934  			fqdnTemplate: "{{.Name}}.ext-dns.test.com",
  1935  		},
  1936  	} {
  1937  		ti := ti
  1938  		t.Run(ti.title, func(t *testing.T) {
  1939  			t.Parallel()
  1940  
  1941  			var gateways []*networkingv1alpha3.Gateway
  1942  			var virtualservices []*networkingv1alpha3.VirtualService
  1943  
  1944  			for _, gwItem := range ti.gwConfigs {
  1945  				gateways = append(gateways, gwItem.Config())
  1946  			}
  1947  			for _, vsItem := range ti.vsConfigs {
  1948  				virtualservices = append(virtualservices, vsItem.Config())
  1949  			}
  1950  
  1951  			fakeKubernetesClient := fake.NewSimpleClientset()
  1952  
  1953  			for _, lb := range ti.lbServices {
  1954  				service := lb.Service()
  1955  				_, err := fakeKubernetesClient.CoreV1().Services(service.Namespace).Create(context.Background(), service, metav1.CreateOptions{})
  1956  				require.NoError(t, err)
  1957  			}
  1958  
  1959  			for _, ing := range ti.ingresses {
  1960  				ingress := ing.Ingress()
  1961  				_, err := fakeKubernetesClient.NetworkingV1().Ingresses(ingress.Namespace).Create(context.Background(), ingress, metav1.CreateOptions{})
  1962  				require.NoError(t, err)
  1963  			}
  1964  
  1965  			fakeIstioClient := istiofake.NewSimpleClientset()
  1966  
  1967  			for _, gateway := range gateways {
  1968  				_, err := fakeIstioClient.NetworkingV1alpha3().Gateways(gateway.Namespace).Create(context.Background(), gateway, metav1.CreateOptions{})
  1969  				require.NoError(t, err)
  1970  			}
  1971  
  1972  			for _, virtualservice := range virtualservices {
  1973  				_, err := fakeIstioClient.NetworkingV1alpha3().VirtualServices(virtualservice.Namespace).Create(context.Background(), virtualservice, metav1.CreateOptions{})
  1974  				require.NoError(t, err)
  1975  			}
  1976  
  1977  			virtualServiceSource, err := NewIstioVirtualServiceSource(
  1978  				context.TODO(),
  1979  				fakeKubernetesClient,
  1980  				fakeIstioClient,
  1981  				ti.targetNamespace,
  1982  				ti.annotationFilter,
  1983  				ti.fqdnTemplate,
  1984  				ti.combineFQDNAndAnnotation,
  1985  				ti.ignoreHostnameAnnotation,
  1986  			)
  1987  			require.NoError(t, err)
  1988  
  1989  			res, err := virtualServiceSource.Endpoints(context.Background())
  1990  			if ti.expectError {
  1991  				assert.Error(t, err)
  1992  			} else {
  1993  				assert.NoError(t, err)
  1994  			}
  1995  
  1996  			validateEndpoints(t, res, ti.expected)
  1997  		})
  1998  	}
  1999  }
  2000  
  2001  func testGatewaySelectorMatchesService(t *testing.T) {
  2002  	for _, ti := range []struct {
  2003  		title      string
  2004  		gwSelector map[string]string
  2005  		lbSelector map[string]string
  2006  		expected   bool
  2007  	}{
  2008  		{
  2009  			title:      "gw selector matches lb selector",
  2010  			gwSelector: map[string]string{"istio": "ingressgateway"},
  2011  			lbSelector: map[string]string{"istio": "ingressgateway"},
  2012  			expected:   true,
  2013  		},
  2014  		{
  2015  			title:      "gw selector matches lb selector partially",
  2016  			gwSelector: map[string]string{"istio": "ingressgateway"},
  2017  			lbSelector: map[string]string{"release": "istio", "istio": "ingressgateway"},
  2018  			expected:   true,
  2019  		},
  2020  		{
  2021  			title:      "gw selector does not match lb selector",
  2022  			gwSelector: map[string]string{"app": "mytest"},
  2023  			lbSelector: map[string]string{"istio": "ingressgateway"},
  2024  			expected:   false,
  2025  		},
  2026  	} {
  2027  		t.Run(ti.title, func(t *testing.T) {
  2028  			require.Equal(t, ti.expected, gatewaySelectorMatchesServiceSelector(ti.gwSelector, ti.lbSelector))
  2029  		})
  2030  	}
  2031  }
  2032  
  2033  func newTestVirtualServiceSource(loadBalancerList []fakeIngressGatewayService, ingressList []fakeIngress, gwList []fakeGatewayConfig) (*virtualServiceSource, error) {
  2034  	fakeKubernetesClient := fake.NewSimpleClientset()
  2035  	fakeIstioClient := istiofake.NewSimpleClientset()
  2036  
  2037  	for _, lb := range loadBalancerList {
  2038  		service := lb.Service()
  2039  		_, err := fakeKubernetesClient.CoreV1().Services(service.Namespace).Create(context.Background(), service, metav1.CreateOptions{})
  2040  		if err != nil {
  2041  			return nil, err
  2042  		}
  2043  	}
  2044  
  2045  	for _, ing := range ingressList {
  2046  		ingress := ing.Ingress()
  2047  		_, err := fakeKubernetesClient.NetworkingV1().Ingresses(ingress.Namespace).Create(context.Background(), ingress, metav1.CreateOptions{})
  2048  		if err != nil {
  2049  			return nil, err
  2050  		}
  2051  	}
  2052  
  2053  	for _, gw := range gwList {
  2054  		gwObj := gw.Config()
  2055  		// use create instead of add
  2056  		// https://github.com/kubernetes/client-go/blob/92512ee2b8cf6696e9909245624175b7f0c971d9/testing/fixture.go#LL336C3-L336C52
  2057  		_, err := fakeIstioClient.NetworkingV1alpha3().Gateways(gw.namespace).Create(context.Background(), gwObj, metav1.CreateOptions{})
  2058  		if err != nil {
  2059  			return nil, err
  2060  		}
  2061  	}
  2062  
  2063  	src, err := NewIstioVirtualServiceSource(
  2064  		context.TODO(),
  2065  		fakeKubernetesClient,
  2066  		fakeIstioClient,
  2067  		"",
  2068  		"",
  2069  		"{{.Name}}",
  2070  		false,
  2071  		false,
  2072  	)
  2073  	if err != nil {
  2074  		return nil, err
  2075  	}
  2076  
  2077  	vssrc, ok := src.(*virtualServiceSource)
  2078  	if !ok {
  2079  		return nil, errors.New("underlying source type was not virtualservice")
  2080  	}
  2081  
  2082  	return vssrc, nil
  2083  }
  2084  
  2085  type fakeVirtualServiceConfig struct {
  2086  	namespace   string
  2087  	name        string
  2088  	gateways    []string
  2089  	annotations map[string]string
  2090  	dnsnames    []string
  2091  	exportTo    string
  2092  }
  2093  
  2094  func (c fakeVirtualServiceConfig) Config() *networkingv1alpha3.VirtualService {
  2095  	vs := istionetworking.VirtualService{
  2096  		Gateways: c.gateways,
  2097  		Hosts:    c.dnsnames,
  2098  	}
  2099  	if c.exportTo != "" {
  2100  		vs.ExportTo = []string{c.exportTo}
  2101  	}
  2102  
  2103  	return &networkingv1alpha3.VirtualService{
  2104  		ObjectMeta: metav1.ObjectMeta{
  2105  			Name:        c.name,
  2106  			Namespace:   c.namespace,
  2107  			Annotations: c.annotations,
  2108  		},
  2109  		Spec: *vs.DeepCopy(),
  2110  	}
  2111  }
  2112  
  2113  func TestVirtualServiceSourceGetGateway(t *testing.T) {
  2114  	type fields struct {
  2115  		virtualServiceSource *virtualServiceSource
  2116  	}
  2117  	type args struct {
  2118  		ctx            context.Context
  2119  		gatewayStr     string
  2120  		virtualService *networkingv1alpha3.VirtualService
  2121  	}
  2122  	tests := []struct {
  2123  		name           string
  2124  		fields         fields
  2125  		args           args
  2126  		want           *networkingv1alpha3.Gateway
  2127  		expectedErrStr string
  2128  	}{
  2129  		{name: "EmptyGateway", fields: fields{
  2130  			virtualServiceSource: func() *virtualServiceSource { vs, _ := newTestVirtualServiceSource(nil, nil, nil); return vs }(),
  2131  		}, args: args{
  2132  			ctx:            context.TODO(),
  2133  			gatewayStr:     "",
  2134  			virtualService: nil,
  2135  		}, want: nil, expectedErrStr: ""},
  2136  		{name: "MeshGateway", fields: fields{
  2137  			virtualServiceSource: func() *virtualServiceSource { vs, _ := newTestVirtualServiceSource(nil, nil, nil); return vs }(),
  2138  		}, args: args{
  2139  			ctx:            context.TODO(),
  2140  			gatewayStr:     IstioMeshGateway,
  2141  			virtualService: nil,
  2142  		}, want: nil, expectedErrStr: ""},
  2143  		{name: "MissingGateway", fields: fields{
  2144  			virtualServiceSource: func() *virtualServiceSource { vs, _ := newTestVirtualServiceSource(nil, nil, nil); return vs }(),
  2145  		}, args: args{
  2146  			ctx:        context.TODO(),
  2147  			gatewayStr: "doesnt/exist",
  2148  			virtualService: &networkingv1alpha3.VirtualService{
  2149  				TypeMeta:   metav1.TypeMeta{},
  2150  				ObjectMeta: metav1.ObjectMeta{Name: "exist", Namespace: "doesnt"},
  2151  				Spec:       istionetworking.VirtualService{},
  2152  				Status:     v1alpha1.IstioStatus{},
  2153  			},
  2154  		}, want: nil, expectedErrStr: ""},
  2155  		{name: "InvalidGatewayStr", fields: fields{
  2156  			virtualServiceSource: func() *virtualServiceSource { vs, _ := newTestVirtualServiceSource(nil, nil, nil); return vs }(),
  2157  		}, args: args{
  2158  			ctx:            context.TODO(),
  2159  			gatewayStr:     "1/2/3/",
  2160  			virtualService: &networkingv1alpha3.VirtualService{},
  2161  		}, want: nil, expectedErrStr: "invalid gateway name (name or namespace/name) found '1/2/3/'"},
  2162  		{name: "ExistingGateway", fields: fields{
  2163  			virtualServiceSource: func() *virtualServiceSource {
  2164  				vs, _ := newTestVirtualServiceSource(nil, nil, []fakeGatewayConfig{{
  2165  					namespace: "bar",
  2166  					name:      "foo",
  2167  				}})
  2168  				return vs
  2169  			}(),
  2170  		}, args: args{
  2171  			ctx:        context.TODO(),
  2172  			gatewayStr: "bar/foo",
  2173  			virtualService: &networkingv1alpha3.VirtualService{
  2174  				TypeMeta:   metav1.TypeMeta{},
  2175  				ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "bar"},
  2176  				Spec:       istionetworking.VirtualService{},
  2177  				Status:     v1alpha1.IstioStatus{},
  2178  			},
  2179  		}, want: &networkingv1alpha3.Gateway{
  2180  			TypeMeta:   metav1.TypeMeta{},
  2181  			ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "bar"},
  2182  			Spec:       istionetworking.Gateway{},
  2183  			Status:     v1alpha1.IstioStatus{},
  2184  		}, expectedErrStr: ""},
  2185  		{name: "ErrorGettingGateway", fields: fields{
  2186  			virtualServiceSource: func() *virtualServiceSource {
  2187  				istioFake := istiofake.NewSimpleClientset()
  2188  				istioFake.NetworkingV1alpha3().(*fakenetworking3.FakeNetworkingV1alpha3).PrependReactor("get", "gateways", func(action k8sclienttesting.Action) (handled bool, ret runtime.Object, err error) {
  2189  					return true, &networkingv1alpha3.Gateway{}, fmt.Errorf("error getting gateway")
  2190  				})
  2191  				vs, _ := NewIstioVirtualServiceSource(
  2192  					context.TODO(),
  2193  					fake.NewSimpleClientset(),
  2194  					istioFake,
  2195  					"",
  2196  					"",
  2197  					"{{.Name}}",
  2198  					false,
  2199  					false,
  2200  				)
  2201  				return vs.(*virtualServiceSource)
  2202  			}(),
  2203  		}, args: args{
  2204  			ctx:        context.TODO(),
  2205  			gatewayStr: "foo/bar",
  2206  			virtualService: &networkingv1alpha3.VirtualService{
  2207  				TypeMeta:   metav1.TypeMeta{},
  2208  				ObjectMeta: metav1.ObjectMeta{Name: "gateway", Namespace: "error"},
  2209  				Spec:       istionetworking.VirtualService{},
  2210  				Status:     v1alpha1.IstioStatus{},
  2211  			},
  2212  		}, want: nil, expectedErrStr: "error getting gateway"},
  2213  	}
  2214  	for _, tt := range tests {
  2215  		t.Run(tt.name, func(t *testing.T) {
  2216  			got, err := tt.fields.virtualServiceSource.getGateway(tt.args.ctx, tt.args.gatewayStr, tt.args.virtualService)
  2217  			if tt.expectedErrStr != "" {
  2218  				assert.EqualError(t, err, tt.expectedErrStr, fmt.Sprintf("getGateway(%v, %v, %v)", tt.args.ctx, tt.args.gatewayStr, tt.args.virtualService))
  2219  				return
  2220  			} else {
  2221  				require.NoError(t, err)
  2222  			}
  2223  			if tt.want != nil && got != nil {
  2224  				tt.want.Spec.ProtoReflect()
  2225  				tt.want.Status.ProtoReflect()
  2226  				assert.Equalf(t, tt.want, got, "getGateway(%v, %v, %v)", tt.args.ctx, tt.args.gatewayStr, tt.args.virtualService)
  2227  			} else {
  2228  				assert.Equalf(t, tt.want, got, "getGateway(%v, %v, %v)", tt.args.ctx, tt.args.gatewayStr, tt.args.virtualService)
  2229  			}
  2230  		})
  2231  	}
  2232  }