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

     1  /*
     2  Copyright 2017 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  	"testing"
    22  
    23  	"github.com/pkg/errors"
    24  	"github.com/stretchr/testify/assert"
    25  	"github.com/stretchr/testify/require"
    26  	"github.com/stretchr/testify/suite"
    27  	networkingv1alpha3api "istio.io/api/networking/v1alpha3"
    28  	networkingv1alpha3 "istio.io/client-go/pkg/apis/networking/v1alpha3"
    29  	istiofake "istio.io/client-go/pkg/clientset/versioned/fake"
    30  	v1 "k8s.io/api/core/v1"
    31  	networkv1 "k8s.io/api/networking/v1"
    32  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    33  	"k8s.io/client-go/kubernetes/fake"
    34  
    35  	"sigs.k8s.io/external-dns/endpoint"
    36  )
    37  
    38  // This is a compile-time validation that gatewaySource is a Source.
    39  var _ Source = &gatewaySource{}
    40  
    41  type GatewaySuite struct {
    42  	suite.Suite
    43  	source     Source
    44  	lbServices []*v1.Service
    45  	ingresses  []*networkv1.Ingress
    46  }
    47  
    48  func (suite *GatewaySuite) SetupTest() {
    49  	fakeKubernetesClient := fake.NewSimpleClientset()
    50  	fakeIstioClient := istiofake.NewSimpleClientset()
    51  	var err error
    52  
    53  	suite.lbServices = []*v1.Service{
    54  		(fakeIngressGatewayService{
    55  			ips:       []string{"8.8.8.8"},
    56  			hostnames: []string{"v1"},
    57  			namespace: "istio-system",
    58  			name:      "istio-gateway1",
    59  		}).Service(),
    60  		(fakeIngressGatewayService{
    61  			ips:       []string{"1.1.1.1"},
    62  			hostnames: []string{"v42"},
    63  			namespace: "istio-other",
    64  			name:      "istio-gateway2",
    65  		}).Service(),
    66  	}
    67  
    68  	for _, service := range suite.lbServices {
    69  		_, err = fakeKubernetesClient.CoreV1().Services(service.Namespace).Create(context.Background(), service, metav1.CreateOptions{})
    70  		suite.NoError(err, "should succeed")
    71  	}
    72  
    73  	suite.ingresses = []*networkv1.Ingress{
    74  		(fakeIngress{
    75  			ips:       []string{"2.2.2.2"},
    76  			hostnames: []string{"v2"},
    77  			namespace: "istio-system",
    78  			name:      "istio-ingress",
    79  		}).Ingress(),
    80  		(fakeIngress{
    81  			ips:       []string{"3.3.3.3"},
    82  			hostnames: []string{"v62"},
    83  			namespace: "istio-system",
    84  			name:      "istio-ingress2",
    85  		}).Ingress(),
    86  	}
    87  
    88  	for _, ingress := range suite.ingresses {
    89  		_, err = fakeKubernetesClient.NetworkingV1().Ingresses(ingress.Namespace).Create(context.Background(), ingress, metav1.CreateOptions{})
    90  		suite.NoError(err, "should succeed")
    91  	}
    92  
    93  	suite.source, err = NewIstioGatewaySource(
    94  		context.TODO(),
    95  		fakeKubernetesClient,
    96  		fakeIstioClient,
    97  		"",
    98  		"",
    99  		"{{.Name}}",
   100  		false,
   101  		false,
   102  	)
   103  	suite.NoError(err, "should initialize gateway source")
   104  	suite.NoError(err, "should succeed")
   105  }
   106  
   107  func (suite *GatewaySuite) TestResourceLabelIsSet() {
   108  	endpoints, _ := suite.source.Endpoints(context.Background())
   109  	for _, ep := range endpoints {
   110  		suite.Equal("gateway/default/foo-gateway-with-targets", ep.Labels[endpoint.ResourceLabelKey], "should set correct resource label")
   111  	}
   112  }
   113  
   114  func TestGateway(t *testing.T) {
   115  	t.Parallel()
   116  
   117  	suite.Run(t, new(GatewaySuite))
   118  	t.Run("endpointsFromGatewayConfig", testEndpointsFromGatewayConfig)
   119  	t.Run("Endpoints", testGatewayEndpoints)
   120  }
   121  
   122  func TestNewIstioGatewaySource(t *testing.T) {
   123  	t.Parallel()
   124  
   125  	for _, ti := range []struct {
   126  		title                    string
   127  		annotationFilter         string
   128  		fqdnTemplate             string
   129  		combineFQDNAndAnnotation bool
   130  		expectError              bool
   131  	}{
   132  		{
   133  			title:        "invalid template",
   134  			expectError:  true,
   135  			fqdnTemplate: "{{.Name",
   136  		},
   137  		{
   138  			title:       "valid empty template",
   139  			expectError: false,
   140  		},
   141  		{
   142  			title:        "valid template",
   143  			expectError:  false,
   144  			fqdnTemplate: "{{.Name}}-{{.Namespace}}.ext-dns.test.com",
   145  		},
   146  		{
   147  			title:        "valid template",
   148  			expectError:  false,
   149  			fqdnTemplate: "{{.Name}}-{{.Namespace}}.ext-dns.test.com, {{.Name}}-{{.Namespace}}.ext-dna.test.com",
   150  		},
   151  		{
   152  			title:                    "valid template",
   153  			expectError:              false,
   154  			fqdnTemplate:             "{{.Name}}-{{.Namespace}}.ext-dns.test.com, {{.Name}}-{{.Namespace}}.ext-dna.test.com",
   155  			combineFQDNAndAnnotation: true,
   156  		},
   157  		{
   158  			title:            "non-empty annotation filter label",
   159  			expectError:      false,
   160  			annotationFilter: "kubernetes.io/gateway.class=nginx",
   161  		},
   162  	} {
   163  		ti := ti
   164  		t.Run(ti.title, func(t *testing.T) {
   165  			t.Parallel()
   166  
   167  			_, err := NewIstioGatewaySource(
   168  				context.TODO(),
   169  				fake.NewSimpleClientset(),
   170  				istiofake.NewSimpleClientset(),
   171  				"",
   172  				ti.annotationFilter,
   173  				ti.fqdnTemplate,
   174  				ti.combineFQDNAndAnnotation,
   175  				false,
   176  			)
   177  			if ti.expectError {
   178  				assert.Error(t, err)
   179  			} else {
   180  				assert.NoError(t, err)
   181  			}
   182  		})
   183  	}
   184  }
   185  
   186  func testEndpointsFromGatewayConfig(t *testing.T) {
   187  	t.Parallel()
   188  
   189  	for _, ti := range []struct {
   190  		title      string
   191  		lbServices []fakeIngressGatewayService
   192  		ingresses  []fakeIngress
   193  		config     fakeGatewayConfig
   194  		expected   []*endpoint.Endpoint
   195  	}{
   196  		{
   197  			title: "one rule.host one lb.hostname",
   198  			lbServices: []fakeIngressGatewayService{
   199  				{
   200  					hostnames: []string{"lb.com"}, // Kubernetes omits the trailing dot
   201  				},
   202  			},
   203  			config: fakeGatewayConfig{
   204  				dnsnames: [][]string{
   205  					{"foo.bar"}, // Kubernetes requires removal of trailing dot
   206  				},
   207  			},
   208  			expected: []*endpoint.Endpoint{
   209  				{
   210  					DNSName:    "foo.bar",
   211  					RecordType: endpoint.RecordTypeCNAME,
   212  					Targets:    endpoint.Targets{"lb.com"},
   213  				},
   214  			},
   215  		},
   216  		{
   217  			title: "one namespaced rule.host one lb.hostname",
   218  			lbServices: []fakeIngressGatewayService{
   219  				{
   220  					hostnames: []string{"lb.com"}, // Kubernetes omits the trailing dot
   221  				},
   222  			},
   223  			config: fakeGatewayConfig{
   224  				dnsnames: [][]string{
   225  					{"my-namespace/foo.bar"}, // Kubernetes requires removal of trailing dot
   226  				},
   227  			},
   228  			expected: []*endpoint.Endpoint{
   229  				{
   230  					DNSName:    "foo.bar",
   231  					RecordType: endpoint.RecordTypeCNAME,
   232  					Targets:    endpoint.Targets{"lb.com"},
   233  				},
   234  			},
   235  		},
   236  		{
   237  			title: "one rule.host one lb.IP",
   238  			lbServices: []fakeIngressGatewayService{
   239  				{
   240  					ips: []string{"8.8.8.8"},
   241  				},
   242  			},
   243  			config: fakeGatewayConfig{
   244  				dnsnames: [][]string{
   245  					{"foo.bar"},
   246  				},
   247  			},
   248  			expected: []*endpoint.Endpoint{
   249  				{
   250  					DNSName:    "foo.bar",
   251  					RecordType: endpoint.RecordTypeA,
   252  					Targets:    endpoint.Targets{"8.8.8.8"},
   253  				},
   254  			},
   255  		},
   256  		{
   257  			title: "one rule.host one ingress.IP",
   258  			ingresses: []fakeIngress{
   259  				{
   260  					name: "ingress1",
   261  					ips:  []string{"8.8.8.8"},
   262  				},
   263  			},
   264  			config: fakeGatewayConfig{
   265  				annotations: map[string]string{
   266  					IstioGatewayIngressSource: "ingress1",
   267  				},
   268  				dnsnames: [][]string{
   269  					{"foo.bar"},
   270  				},
   271  			},
   272  			expected: []*endpoint.Endpoint{
   273  				{
   274  					DNSName:    "foo.bar",
   275  					RecordType: endpoint.RecordTypeA,
   276  					Targets:    endpoint.Targets{"8.8.8.8"},
   277  				},
   278  			},
   279  		},
   280  		{
   281  			title: "one rule.host two lb.IP and two lb.Hostname",
   282  			lbServices: []fakeIngressGatewayService{
   283  				{
   284  					ips:       []string{"8.8.8.8", "127.0.0.1"},
   285  					hostnames: []string{"elb.com", "alb.com"},
   286  				},
   287  			},
   288  			config: fakeGatewayConfig{
   289  				dnsnames: [][]string{
   290  					{"foo.bar"},
   291  				},
   292  			},
   293  			expected: []*endpoint.Endpoint{
   294  				{
   295  					DNSName:    "foo.bar",
   296  					RecordType: endpoint.RecordTypeA,
   297  					Targets:    endpoint.Targets{"8.8.8.8", "127.0.0.1"},
   298  				},
   299  				{
   300  					DNSName:    "foo.bar",
   301  					RecordType: endpoint.RecordTypeCNAME,
   302  					Targets:    endpoint.Targets{"elb.com", "alb.com"},
   303  				},
   304  			},
   305  		},
   306  		{
   307  			title: "one rule.host two ingress.IP and two ingress.Hostname",
   308  			ingresses: []fakeIngress{
   309  				{
   310  					name:      "ingress1",
   311  					ips:       []string{"8.8.8.8", "127.0.0.1"},
   312  					hostnames: []string{"elb.com", "alb.com"},
   313  				},
   314  			},
   315  			config: fakeGatewayConfig{
   316  				annotations: map[string]string{
   317  					IstioGatewayIngressSource: "ingress1",
   318  				},
   319  				dnsnames: [][]string{
   320  					{"foo.bar"},
   321  				},
   322  			},
   323  			expected: []*endpoint.Endpoint{
   324  				{
   325  					DNSName:    "foo.bar",
   326  					RecordType: endpoint.RecordTypeA,
   327  					Targets:    endpoint.Targets{"8.8.8.8", "127.0.0.1"},
   328  				},
   329  				{
   330  					DNSName:    "foo.bar",
   331  					RecordType: endpoint.RecordTypeCNAME,
   332  					Targets:    endpoint.Targets{"elb.com", "alb.com"},
   333  				},
   334  			},
   335  		},
   336  		{
   337  			title: "no rule.host",
   338  			lbServices: []fakeIngressGatewayService{
   339  				{
   340  					ips:         []string{"8.8.8.8", "127.0.0.1"},
   341  					hostnames:   []string{"elb.com", "alb.com"},
   342  					externalIPs: []string{"1.1.1.1", "2.2.2.2"},
   343  				},
   344  			},
   345  			config: fakeGatewayConfig{
   346  				dnsnames: [][]string{},
   347  			},
   348  			expected: []*endpoint.Endpoint{},
   349  		},
   350  		{
   351  			title: "one empty rule.host",
   352  			lbServices: []fakeIngressGatewayService{
   353  				{
   354  					ips:         []string{"8.8.8.8", "127.0.0.1"},
   355  					hostnames:   []string{"elb.com", "alb.com"},
   356  					externalIPs: []string{"1.1.1.1", "2.2.2.2"},
   357  				},
   358  			},
   359  			config: fakeGatewayConfig{
   360  				dnsnames: [][]string{
   361  					{""},
   362  				},
   363  			},
   364  			expected: []*endpoint.Endpoint{},
   365  		},
   366  		{
   367  			title: "one empty rule.host with gateway ingress annotation",
   368  			ingresses: []fakeIngress{
   369  				{
   370  					name:      "ingress1",
   371  					ips:       []string{"8.8.8.8", "127.0.0.1"},
   372  					hostnames: []string{"elb.com", "alb.com"},
   373  				},
   374  			},
   375  			config: fakeGatewayConfig{
   376  				annotations: map[string]string{
   377  					IstioGatewayIngressSource: "ingress1",
   378  				},
   379  				dnsnames: [][]string{
   380  					{""},
   381  				},
   382  			},
   383  			expected: []*endpoint.Endpoint{},
   384  		},
   385  		{
   386  			title:      "no targets",
   387  			lbServices: []fakeIngressGatewayService{{}},
   388  			config: fakeGatewayConfig{
   389  				dnsnames: [][]string{
   390  					{""},
   391  				},
   392  			},
   393  			expected: []*endpoint.Endpoint{},
   394  		},
   395  		{
   396  			title: "one gateway, two ingressgateway loadbalancer hostnames",
   397  			lbServices: []fakeIngressGatewayService{
   398  				{
   399  					hostnames: []string{"lb.com"},
   400  					namespace: "istio-other",
   401  					name:      "gateway1",
   402  				},
   403  				{
   404  					hostnames: []string{"lb2.com"},
   405  					namespace: "istio-other",
   406  					name:      "gateway2",
   407  				},
   408  			},
   409  			config: fakeGatewayConfig{
   410  				dnsnames: [][]string{
   411  					{"foo.bar"}, // Kubernetes requires removal of trailing dot
   412  				},
   413  			},
   414  			expected: []*endpoint.Endpoint{
   415  				{
   416  					DNSName:    "foo.bar",
   417  					RecordType: endpoint.RecordTypeCNAME,
   418  					Targets:    endpoint.Targets{"lb.com", "lb2.com"},
   419  				},
   420  			},
   421  		},
   422  		{
   423  			title: "one gateway, ingress in seperate namespace",
   424  			ingresses: []fakeIngress{
   425  				{
   426  					hostnames: []string{"lb.com"},
   427  					namespace: "istio-other2",
   428  					name:      "ingress1",
   429  				},
   430  				{
   431  					hostnames: []string{"lb2.com"},
   432  					namespace: "istio-other",
   433  					name:      "ingress2",
   434  				},
   435  			},
   436  			config: fakeGatewayConfig{
   437  				annotations: map[string]string{
   438  					IstioGatewayIngressSource: "istio-other2/ingress1",
   439  				},
   440  				dnsnames: [][]string{
   441  					{"foo.bar"}, // Kubernetes requires removal of trailing dot
   442  				},
   443  			},
   444  			expected: []*endpoint.Endpoint{
   445  				{
   446  					DNSName:    "foo.bar",
   447  					RecordType: endpoint.RecordTypeCNAME,
   448  					Targets:    endpoint.Targets{"lb.com"},
   449  				},
   450  			},
   451  		},
   452  		{
   453  			title: "one rule.host one lb.externalIP",
   454  			lbServices: []fakeIngressGatewayService{
   455  				{
   456  					externalIPs: []string{"8.8.8.8"},
   457  				},
   458  			},
   459  			config: fakeGatewayConfig{
   460  				dnsnames: [][]string{
   461  					{"foo.bar"},
   462  				},
   463  			},
   464  			expected: []*endpoint.Endpoint{
   465  				{
   466  					DNSName:    "foo.bar",
   467  					RecordType: endpoint.RecordTypeA,
   468  					Targets:    endpoint.Targets{"8.8.8.8"},
   469  				},
   470  			},
   471  		},
   472  		{
   473  			title: "one rule.host two lb.IP, two lb.Hostname and two lb.externalIP",
   474  			lbServices: []fakeIngressGatewayService{
   475  				{
   476  					ips:         []string{"8.8.8.8", "127.0.0.1"},
   477  					hostnames:   []string{"elb.com", "alb.com"},
   478  					externalIPs: []string{"1.1.1.1", "2.2.2.2"},
   479  				},
   480  			},
   481  			config: fakeGatewayConfig{
   482  				dnsnames: [][]string{
   483  					{"foo.bar"},
   484  				},
   485  			},
   486  			expected: []*endpoint.Endpoint{
   487  				{
   488  					DNSName:    "foo.bar",
   489  					RecordType: endpoint.RecordTypeA,
   490  					Targets:    endpoint.Targets{"1.1.1.1", "2.2.2.2"},
   491  				},
   492  			},
   493  		},
   494  	} {
   495  		ti := ti
   496  		t.Run(ti.title, func(t *testing.T) {
   497  			t.Parallel()
   498  
   499  			gatewayCfg := ti.config.Config()
   500  			if source, err := newTestGatewaySource(ti.lbServices, ti.ingresses); err != nil {
   501  				require.NoError(t, err)
   502  			} else if hostnames, err := source.hostNamesFromGateway(gatewayCfg); err != nil {
   503  				require.NoError(t, err)
   504  			} else if endpoints, err := source.endpointsFromGateway(context.Background(), hostnames, gatewayCfg); err != nil {
   505  				require.NoError(t, err)
   506  			} else {
   507  				validateEndpoints(t, endpoints, ti.expected)
   508  			}
   509  		})
   510  	}
   511  }
   512  
   513  func testGatewayEndpoints(t *testing.T) {
   514  	t.Parallel()
   515  
   516  	for _, ti := range []struct {
   517  		title                    string
   518  		targetNamespace          string
   519  		annotationFilter         string
   520  		lbServices               []fakeIngressGatewayService
   521  		ingresses                []fakeIngress
   522  		configItems              []fakeGatewayConfig
   523  		expected                 []*endpoint.Endpoint
   524  		expectError              bool
   525  		fqdnTemplate             string
   526  		combineFQDNAndAnnotation bool
   527  		ignoreHostnameAnnotation bool
   528  	}{
   529  		{
   530  			title:           "no gateway",
   531  			targetNamespace: "",
   532  		},
   533  		{
   534  			title:           "two simple gateways, one ingressgateway loadbalancer service",
   535  			targetNamespace: "",
   536  			lbServices: []fakeIngressGatewayService{
   537  				{
   538  					ips:       []string{"8.8.8.8"},
   539  					hostnames: []string{"lb.com"},
   540  				},
   541  			},
   542  			configItems: []fakeGatewayConfig{
   543  				{
   544  					name:      "fake1",
   545  					namespace: "",
   546  					dnsnames:  [][]string{{"example.org"}},
   547  				},
   548  				{
   549  					name:      "fake2",
   550  					namespace: "",
   551  					dnsnames:  [][]string{{"new.org"}},
   552  				},
   553  			},
   554  			expected: []*endpoint.Endpoint{
   555  				{
   556  					DNSName:    "example.org",
   557  					RecordType: endpoint.RecordTypeA,
   558  					Targets:    endpoint.Targets{"8.8.8.8"},
   559  				},
   560  				{
   561  					DNSName:    "example.org",
   562  					RecordType: endpoint.RecordTypeCNAME,
   563  					Targets:    endpoint.Targets{"lb.com"},
   564  				},
   565  				{
   566  					DNSName:    "new.org",
   567  					RecordType: endpoint.RecordTypeA,
   568  					Targets:    endpoint.Targets{"8.8.8.8"},
   569  				},
   570  				{
   571  					DNSName:    "new.org",
   572  					RecordType: endpoint.RecordTypeCNAME,
   573  					Targets:    endpoint.Targets{"lb.com"},
   574  				},
   575  			},
   576  		},
   577  		{
   578  			title:           "two simple gateways on different namespaces, one ingressgateway loadbalancer service",
   579  			targetNamespace: "",
   580  			lbServices: []fakeIngressGatewayService{
   581  				{
   582  					ips:       []string{"8.8.8.8"},
   583  					hostnames: []string{"lb.com"},
   584  				},
   585  			},
   586  			configItems: []fakeGatewayConfig{
   587  				{
   588  					name:      "fake1",
   589  					namespace: "",
   590  					dnsnames:  [][]string{{"example.org"}},
   591  				},
   592  				{
   593  					name:      "fake2",
   594  					namespace: "",
   595  					dnsnames:  [][]string{{"new.org"}},
   596  				},
   597  			},
   598  			expected: []*endpoint.Endpoint{
   599  				{
   600  					DNSName:    "example.org",
   601  					RecordType: endpoint.RecordTypeA,
   602  					Targets:    endpoint.Targets{"8.8.8.8"},
   603  				},
   604  				{
   605  					DNSName:    "example.org",
   606  					RecordType: endpoint.RecordTypeCNAME,
   607  					Targets:    endpoint.Targets{"lb.com"},
   608  				},
   609  				{
   610  					DNSName:    "new.org",
   611  					RecordType: endpoint.RecordTypeA,
   612  					Targets:    endpoint.Targets{"8.8.8.8"},
   613  				},
   614  				{
   615  					DNSName:    "new.org",
   616  					RecordType: endpoint.RecordTypeCNAME,
   617  					Targets:    endpoint.Targets{"lb.com"},
   618  				},
   619  			},
   620  		},
   621  		{
   622  			title:           "two simple gateways on different namespaces and a target namespace, one ingressgateway loadbalancer service",
   623  			targetNamespace: "testing1",
   624  			lbServices: []fakeIngressGatewayService{
   625  				{
   626  					ips:       []string{"8.8.8.8"},
   627  					hostnames: []string{"lb.com"},
   628  					namespace: "testing1",
   629  				},
   630  			},
   631  			configItems: []fakeGatewayConfig{
   632  				{
   633  					name:      "fake1",
   634  					namespace: "testing1",
   635  					dnsnames:  [][]string{{"example.org"}},
   636  				},
   637  			},
   638  			expected: []*endpoint.Endpoint{
   639  				{
   640  					DNSName:    "example.org",
   641  					RecordType: endpoint.RecordTypeA,
   642  					Targets:    endpoint.Targets{"8.8.8.8"},
   643  				},
   644  				{
   645  					DNSName:    "example.org",
   646  					RecordType: endpoint.RecordTypeCNAME,
   647  					Targets:    endpoint.Targets{"lb.com"},
   648  				},
   649  			},
   650  		},
   651  		{
   652  			title:           "one simple gateways on different namespace and a target namespace, one ingress service",
   653  			targetNamespace: "testing1",
   654  			ingresses: []fakeIngress{
   655  				{
   656  					name:      "ingress1",
   657  					ips:       []string{"8.8.8.8"},
   658  					hostnames: []string{"lb.com"},
   659  					namespace: "testing2",
   660  				},
   661  			},
   662  			configItems: []fakeGatewayConfig{
   663  				{
   664  					name:      "fake1",
   665  					namespace: "testing1",
   666  					dnsnames:  [][]string{{"example.org"}},
   667  					annotations: map[string]string{
   668  						IstioGatewayIngressSource: "testing2/ingress1",
   669  					},
   670  				},
   671  			},
   672  			expected: []*endpoint.Endpoint{
   673  				{
   674  					DNSName:    "example.org",
   675  					RecordType: endpoint.RecordTypeA,
   676  					Targets:    endpoint.Targets{"8.8.8.8"},
   677  				},
   678  				{
   679  					DNSName:    "example.org",
   680  					RecordType: endpoint.RecordTypeCNAME,
   681  					Targets:    endpoint.Targets{"lb.com"},
   682  				},
   683  			},
   684  		},
   685  		{
   686  			title:            "valid matching annotation filter expression",
   687  			targetNamespace:  "",
   688  			annotationFilter: "kubernetes.io/gateway.class in (alb, nginx)",
   689  			lbServices: []fakeIngressGatewayService{
   690  				{
   691  					ips: []string{"8.8.8.8"},
   692  				},
   693  			},
   694  			configItems: []fakeGatewayConfig{
   695  				{
   696  					name:      "fake1",
   697  					namespace: "",
   698  					annotations: map[string]string{
   699  						"kubernetes.io/gateway.class": "nginx",
   700  					},
   701  					dnsnames: [][]string{{"example.org"}},
   702  				},
   703  			},
   704  			expected: []*endpoint.Endpoint{
   705  				{
   706  					DNSName:    "example.org",
   707  					RecordType: endpoint.RecordTypeA,
   708  					Targets:    endpoint.Targets{"8.8.8.8"},
   709  				},
   710  			},
   711  		},
   712  		{
   713  			title:            "valid non-matching annotation filter expression",
   714  			targetNamespace:  "",
   715  			annotationFilter: "kubernetes.io/gateway.class in (alb, nginx)",
   716  			lbServices: []fakeIngressGatewayService{
   717  				{
   718  					ips: []string{"8.8.8.8"},
   719  				},
   720  			},
   721  			configItems: []fakeGatewayConfig{
   722  				{
   723  					name:      "fake1",
   724  					namespace: "",
   725  					annotations: map[string]string{
   726  						"kubernetes.io/gateway.class": "tectonic",
   727  					},
   728  					dnsnames: [][]string{{"example.org"}},
   729  				},
   730  			},
   731  			expected: []*endpoint.Endpoint{},
   732  		},
   733  		{
   734  			title:            "invalid annotation filter expression",
   735  			targetNamespace:  "",
   736  			annotationFilter: "kubernetes.io/gateway.name in (a b)",
   737  			lbServices: []fakeIngressGatewayService{
   738  				{
   739  					ips: []string{"8.8.8.8"},
   740  				},
   741  			},
   742  			configItems: []fakeGatewayConfig{
   743  				{
   744  					name:      "fake1",
   745  					namespace: "",
   746  					annotations: map[string]string{
   747  						"kubernetes.io/gateway.class": "alb",
   748  					},
   749  					dnsnames: [][]string{{"example.org"}},
   750  				},
   751  			},
   752  			expected:    []*endpoint.Endpoint{},
   753  			expectError: true,
   754  		},
   755  		{
   756  			title:            "valid matching annotation filter label",
   757  			targetNamespace:  "",
   758  			annotationFilter: "kubernetes.io/gateway.class=nginx",
   759  			lbServices: []fakeIngressGatewayService{
   760  				{
   761  					ips: []string{"8.8.8.8"},
   762  				},
   763  			},
   764  			configItems: []fakeGatewayConfig{
   765  				{
   766  					name:      "fake1",
   767  					namespace: "",
   768  					annotations: map[string]string{
   769  						"kubernetes.io/gateway.class": "nginx",
   770  					},
   771  					dnsnames: [][]string{{"example.org"}},
   772  				},
   773  			},
   774  			expected: []*endpoint.Endpoint{
   775  				{
   776  					DNSName:    "example.org",
   777  					RecordType: endpoint.RecordTypeA,
   778  					Targets:    endpoint.Targets{"8.8.8.8"},
   779  				},
   780  			},
   781  		},
   782  		{
   783  			title:            "valid non-matching annotation filter label",
   784  			targetNamespace:  "",
   785  			annotationFilter: "kubernetes.io/gateway.class=nginx",
   786  			lbServices: []fakeIngressGatewayService{
   787  				{
   788  					ips: []string{"8.8.8.8"},
   789  				},
   790  			},
   791  			configItems: []fakeGatewayConfig{
   792  				{
   793  					name:      "fake1",
   794  					namespace: "",
   795  					annotations: map[string]string{
   796  						"kubernetes.io/gateway.class": "alb",
   797  					},
   798  					dnsnames: [][]string{{"example.org"}},
   799  				},
   800  			},
   801  			expected: []*endpoint.Endpoint{},
   802  		},
   803  		{
   804  			title:           "our controller type is dns-controller",
   805  			targetNamespace: "",
   806  			lbServices: []fakeIngressGatewayService{
   807  				{
   808  					ips: []string{"8.8.8.8"},
   809  				},
   810  			},
   811  			configItems: []fakeGatewayConfig{
   812  				{
   813  					name:      "fake1",
   814  					namespace: "",
   815  					annotations: map[string]string{
   816  						controllerAnnotationKey: controllerAnnotationValue,
   817  					},
   818  					dnsnames: [][]string{{"example.org"}},
   819  				},
   820  			},
   821  			expected: []*endpoint.Endpoint{
   822  				{
   823  					DNSName:    "example.org",
   824  					RecordType: endpoint.RecordTypeA,
   825  					Targets:    endpoint.Targets{"8.8.8.8"},
   826  				},
   827  			},
   828  		},
   829  		{
   830  			title:           "different controller types are ignored",
   831  			targetNamespace: "",
   832  			lbServices: []fakeIngressGatewayService{
   833  				{
   834  					ips: []string{"8.8.8.8"},
   835  				},
   836  			},
   837  			configItems: []fakeGatewayConfig{
   838  				{
   839  					name:      "fake1",
   840  					namespace: "",
   841  					annotations: map[string]string{
   842  						controllerAnnotationKey: "some-other-tool",
   843  					},
   844  					dnsnames: [][]string{{"example.org"}},
   845  				},
   846  			},
   847  			expected: []*endpoint.Endpoint{},
   848  		},
   849  		{
   850  			title:           "template for gateway if host is missing",
   851  			targetNamespace: "",
   852  			lbServices: []fakeIngressGatewayService{
   853  				{
   854  					ips:       []string{"8.8.8.8"},
   855  					hostnames: []string{"elb.com"},
   856  				},
   857  			},
   858  			configItems: []fakeGatewayConfig{
   859  				{
   860  					name:      "fake1",
   861  					namespace: "",
   862  					annotations: map[string]string{
   863  						controllerAnnotationKey: controllerAnnotationValue,
   864  					},
   865  					dnsnames: [][]string{},
   866  				},
   867  			},
   868  			expected: []*endpoint.Endpoint{
   869  				{
   870  					DNSName:    "fake1.ext-dns.test.com",
   871  					RecordType: endpoint.RecordTypeA,
   872  					Targets:    endpoint.Targets{"8.8.8.8"},
   873  				},
   874  				{
   875  					DNSName:    "fake1.ext-dns.test.com",
   876  					RecordType: endpoint.RecordTypeCNAME,
   877  					Targets:    endpoint.Targets{"elb.com"},
   878  				},
   879  			},
   880  			fqdnTemplate: "{{.Name}}.ext-dns.test.com",
   881  		},
   882  		{
   883  			title:           "another controller annotation skipped even with template",
   884  			targetNamespace: "",
   885  			lbServices: []fakeIngressGatewayService{
   886  				{
   887  					ips: []string{"8.8.8.8"},
   888  				},
   889  			},
   890  			configItems: []fakeGatewayConfig{
   891  				{
   892  					name:      "fake1",
   893  					namespace: "",
   894  					annotations: map[string]string{
   895  						controllerAnnotationKey: "other-controller",
   896  					},
   897  					dnsnames: [][]string{},
   898  				},
   899  			},
   900  			expected:     []*endpoint.Endpoint{},
   901  			fqdnTemplate: "{{.Name}}.ext-dns.test.com",
   902  		},
   903  		{
   904  			title:           "multiple FQDN template hostnames",
   905  			targetNamespace: "",
   906  			lbServices: []fakeIngressGatewayService{
   907  				{
   908  					ips: []string{"8.8.8.8"},
   909  				},
   910  			},
   911  			configItems: []fakeGatewayConfig{
   912  				{
   913  					name:        "fake1",
   914  					namespace:   "",
   915  					annotations: map[string]string{},
   916  					dnsnames:    [][]string{},
   917  				},
   918  			},
   919  			expected: []*endpoint.Endpoint{
   920  				{
   921  					DNSName:    "fake1.ext-dns.test.com",
   922  					Targets:    endpoint.Targets{"8.8.8.8"},
   923  					RecordType: endpoint.RecordTypeA,
   924  				},
   925  				{
   926  					DNSName:    "fake1.ext-dna.test.com",
   927  					Targets:    endpoint.Targets{"8.8.8.8"},
   928  					RecordType: endpoint.RecordTypeA,
   929  				},
   930  			},
   931  			fqdnTemplate: "{{.Name}}.ext-dns.test.com, {{.Name}}.ext-dna.test.com",
   932  		},
   933  		{
   934  			title:           "multiple FQDN template hostnames",
   935  			targetNamespace: "",
   936  			lbServices: []fakeIngressGatewayService{
   937  				{
   938  					ips: []string{"8.8.8.8"},
   939  				},
   940  			},
   941  			configItems: []fakeGatewayConfig{
   942  				{
   943  					name:        "fake1",
   944  					namespace:   "",
   945  					annotations: map[string]string{},
   946  					dnsnames:    [][]string{},
   947  				},
   948  				{
   949  					name:      "fake2",
   950  					namespace: "",
   951  					annotations: map[string]string{
   952  						targetAnnotationKey: "gateway-target.com",
   953  					},
   954  					dnsnames: [][]string{{"example.org"}},
   955  				},
   956  			},
   957  			expected: []*endpoint.Endpoint{
   958  				{
   959  					DNSName:    "fake1.ext-dns.test.com",
   960  					Targets:    endpoint.Targets{"8.8.8.8"},
   961  					RecordType: endpoint.RecordTypeA,
   962  				},
   963  				{
   964  					DNSName:    "fake1.ext-dna.test.com",
   965  					Targets:    endpoint.Targets{"8.8.8.8"},
   966  					RecordType: endpoint.RecordTypeA,
   967  				},
   968  				{
   969  					DNSName:    "example.org",
   970  					Targets:    endpoint.Targets{"gateway-target.com"},
   971  					RecordType: endpoint.RecordTypeCNAME,
   972  				},
   973  				{
   974  					DNSName:    "fake2.ext-dns.test.com",
   975  					Targets:    endpoint.Targets{"gateway-target.com"},
   976  					RecordType: endpoint.RecordTypeCNAME,
   977  				},
   978  				{
   979  					DNSName:    "fake2.ext-dna.test.com",
   980  					Targets:    endpoint.Targets{"gateway-target.com"},
   981  					RecordType: endpoint.RecordTypeCNAME,
   982  				},
   983  			},
   984  			fqdnTemplate:             "{{.Name}}.ext-dns.test.com, {{.Name}}.ext-dna.test.com",
   985  			combineFQDNAndAnnotation: true,
   986  		},
   987  		{
   988  			title:           "gateway rules with annotation",
   989  			targetNamespace: "",
   990  			lbServices: []fakeIngressGatewayService{
   991  				{
   992  					ips: []string{"8.8.8.8"},
   993  				},
   994  			},
   995  			configItems: []fakeGatewayConfig{
   996  				{
   997  					name:      "fake1",
   998  					namespace: "",
   999  					annotations: map[string]string{
  1000  						targetAnnotationKey: "gateway-target.com",
  1001  					},
  1002  					dnsnames: [][]string{{"example.org"}},
  1003  				},
  1004  				{
  1005  					name:      "fake2",
  1006  					namespace: "",
  1007  					annotations: map[string]string{
  1008  						targetAnnotationKey: "gateway-target.com",
  1009  					},
  1010  					dnsnames: [][]string{{"example2.org"}},
  1011  				},
  1012  				{
  1013  					name:      "fake3",
  1014  					namespace: "",
  1015  					annotations: map[string]string{
  1016  						IstioGatewayIngressSource: "not-real/ingress1",
  1017  						targetAnnotationKey:       "1.2.3.4",
  1018  					},
  1019  					dnsnames: [][]string{{"example3.org"}},
  1020  				},
  1021  			},
  1022  			expected: []*endpoint.Endpoint{
  1023  				{
  1024  					DNSName:    "example.org",
  1025  					Targets:    endpoint.Targets{"gateway-target.com"},
  1026  					RecordType: endpoint.RecordTypeCNAME,
  1027  				},
  1028  				{
  1029  					DNSName:    "example2.org",
  1030  					Targets:    endpoint.Targets{"gateway-target.com"},
  1031  					RecordType: endpoint.RecordTypeCNAME,
  1032  				},
  1033  				{
  1034  					DNSName:    "example3.org",
  1035  					Targets:    endpoint.Targets{"1.2.3.4"},
  1036  					RecordType: endpoint.RecordTypeA,
  1037  				},
  1038  			},
  1039  		},
  1040  		{
  1041  			title:           "gateway rules with hostname annotation",
  1042  			targetNamespace: "",
  1043  			lbServices: []fakeIngressGatewayService{
  1044  				{
  1045  					ips: []string{"1.2.3.4"},
  1046  				},
  1047  			},
  1048  			configItems: []fakeGatewayConfig{
  1049  				{
  1050  					name:      "fake1",
  1051  					namespace: "",
  1052  					annotations: map[string]string{
  1053  						hostnameAnnotationKey: "dns-through-hostname.com",
  1054  					},
  1055  					dnsnames: [][]string{{"example.org"}},
  1056  				},
  1057  			},
  1058  			expected: []*endpoint.Endpoint{
  1059  				{
  1060  					DNSName:    "example.org",
  1061  					Targets:    endpoint.Targets{"1.2.3.4"},
  1062  					RecordType: endpoint.RecordTypeA,
  1063  				},
  1064  				{
  1065  					DNSName:    "dns-through-hostname.com",
  1066  					Targets:    endpoint.Targets{"1.2.3.4"},
  1067  					RecordType: endpoint.RecordTypeA,
  1068  				},
  1069  			},
  1070  		},
  1071  		{
  1072  			title:           "gateway rules with hostname annotation having multiple hostnames",
  1073  			targetNamespace: "",
  1074  			lbServices: []fakeIngressGatewayService{
  1075  				{
  1076  					ips: []string{"1.2.3.4"},
  1077  				},
  1078  			},
  1079  			configItems: []fakeGatewayConfig{
  1080  				{
  1081  					name:      "fake1",
  1082  					namespace: "",
  1083  					annotations: map[string]string{
  1084  						hostnameAnnotationKey: "dns-through-hostname.com, another-dns-through-hostname.com",
  1085  					},
  1086  					dnsnames: [][]string{{"example.org"}},
  1087  				},
  1088  			},
  1089  			expected: []*endpoint.Endpoint{
  1090  				{
  1091  					DNSName:    "example.org",
  1092  					Targets:    endpoint.Targets{"1.2.3.4"},
  1093  					RecordType: endpoint.RecordTypeA,
  1094  				},
  1095  				{
  1096  					DNSName:    "dns-through-hostname.com",
  1097  					Targets:    endpoint.Targets{"1.2.3.4"},
  1098  					RecordType: endpoint.RecordTypeA,
  1099  				},
  1100  				{
  1101  					DNSName:    "another-dns-through-hostname.com",
  1102  					Targets:    endpoint.Targets{"1.2.3.4"},
  1103  					RecordType: endpoint.RecordTypeA,
  1104  				},
  1105  			},
  1106  		},
  1107  		{
  1108  			title:           "gateway rules with hostname and target annotation",
  1109  			targetNamespace: "",
  1110  			lbServices: []fakeIngressGatewayService{
  1111  				{
  1112  					ips: []string{},
  1113  				},
  1114  			},
  1115  			configItems: []fakeGatewayConfig{
  1116  				{
  1117  					name:      "fake1",
  1118  					namespace: "",
  1119  					annotations: map[string]string{
  1120  						hostnameAnnotationKey: "dns-through-hostname.com",
  1121  						targetAnnotationKey:   "gateway-target.com",
  1122  					},
  1123  					dnsnames: [][]string{{"example.org"}},
  1124  				},
  1125  			},
  1126  			expected: []*endpoint.Endpoint{
  1127  				{
  1128  					DNSName:    "example.org",
  1129  					Targets:    endpoint.Targets{"gateway-target.com"},
  1130  					RecordType: endpoint.RecordTypeCNAME,
  1131  				},
  1132  				{
  1133  					DNSName:    "dns-through-hostname.com",
  1134  					Targets:    endpoint.Targets{"gateway-target.com"},
  1135  					RecordType: endpoint.RecordTypeCNAME,
  1136  				},
  1137  			},
  1138  		},
  1139  		{
  1140  			title:           "gateway rules with hostname, target and ingress annotation",
  1141  			targetNamespace: "",
  1142  			lbServices: []fakeIngressGatewayService{
  1143  				{
  1144  					ips: []string{},
  1145  				},
  1146  			},
  1147  			ingresses: []fakeIngress{
  1148  				{
  1149  					name: "ingress1",
  1150  					ips:  []string{},
  1151  				},
  1152  			},
  1153  			configItems: []fakeGatewayConfig{
  1154  				{
  1155  					name:      "fake1",
  1156  					namespace: "",
  1157  					annotations: map[string]string{
  1158  						IstioGatewayIngressSource: "ingress1",
  1159  						hostnameAnnotationKey:     "dns-through-hostname.com",
  1160  						targetAnnotationKey:       "gateway-target.com",
  1161  					},
  1162  					dnsnames: [][]string{{"example.org"}},
  1163  				},
  1164  			},
  1165  			expected: []*endpoint.Endpoint{
  1166  				{
  1167  					DNSName:    "example.org",
  1168  					Targets:    endpoint.Targets{"gateway-target.com"},
  1169  					RecordType: endpoint.RecordTypeCNAME,
  1170  				},
  1171  				{
  1172  					DNSName:    "dns-through-hostname.com",
  1173  					Targets:    endpoint.Targets{"gateway-target.com"},
  1174  					RecordType: endpoint.RecordTypeCNAME,
  1175  				},
  1176  			},
  1177  		},
  1178  		{
  1179  			title:           "gateway rules with annotation and custom TTL",
  1180  			targetNamespace: "",
  1181  			lbServices: []fakeIngressGatewayService{
  1182  				{
  1183  					ips: []string{"8.8.8.8"},
  1184  				},
  1185  			},
  1186  			configItems: []fakeGatewayConfig{
  1187  				{
  1188  					name:      "fake1",
  1189  					namespace: "",
  1190  					annotations: map[string]string{
  1191  						targetAnnotationKey: "gateway-target.com",
  1192  						ttlAnnotationKey:    "6",
  1193  					},
  1194  					dnsnames: [][]string{{"example.org"}},
  1195  				},
  1196  				{
  1197  					name:      "fake2",
  1198  					namespace: "",
  1199  					annotations: map[string]string{
  1200  						targetAnnotationKey: "gateway-target.com",
  1201  						ttlAnnotationKey:    "1",
  1202  					},
  1203  					dnsnames: [][]string{{"example2.org"}},
  1204  				},
  1205  				{
  1206  					name:      "fake3",
  1207  					namespace: "",
  1208  					annotations: map[string]string{
  1209  						targetAnnotationKey: "gateway-target.com",
  1210  						ttlAnnotationKey:    "10s",
  1211  					},
  1212  					dnsnames: [][]string{{"example3.org"}},
  1213  				},
  1214  			},
  1215  			expected: []*endpoint.Endpoint{
  1216  				{
  1217  					DNSName:    "example.org",
  1218  					RecordType: endpoint.RecordTypeCNAME,
  1219  					Targets:    endpoint.Targets{"gateway-target.com"},
  1220  					RecordTTL:  endpoint.TTL(6),
  1221  				},
  1222  				{
  1223  					DNSName:    "example2.org",
  1224  					RecordType: endpoint.RecordTypeCNAME,
  1225  					Targets:    endpoint.Targets{"gateway-target.com"},
  1226  					RecordTTL:  endpoint.TTL(1),
  1227  				},
  1228  				{
  1229  					DNSName:    "example3.org",
  1230  					RecordType: endpoint.RecordTypeCNAME,
  1231  					Targets:    endpoint.Targets{"gateway-target.com"},
  1232  					RecordTTL:  endpoint.TTL(10),
  1233  				},
  1234  			},
  1235  		},
  1236  		{
  1237  			title:           "template for gateway with annotation",
  1238  			targetNamespace: "",
  1239  			lbServices: []fakeIngressGatewayService{
  1240  				{
  1241  					ips:       []string{},
  1242  					hostnames: []string{},
  1243  				},
  1244  			},
  1245  			configItems: []fakeGatewayConfig{
  1246  				{
  1247  					name:      "fake1",
  1248  					namespace: "",
  1249  					annotations: map[string]string{
  1250  						targetAnnotationKey: "gateway-target.com",
  1251  					},
  1252  					dnsnames: [][]string{},
  1253  				},
  1254  				{
  1255  					name:      "fake2",
  1256  					namespace: "",
  1257  					annotations: map[string]string{
  1258  						targetAnnotationKey: "gateway-target.com",
  1259  					},
  1260  					dnsnames: [][]string{},
  1261  				},
  1262  				{
  1263  					name:      "fake3",
  1264  					namespace: "",
  1265  					annotations: map[string]string{
  1266  						targetAnnotationKey: "1.2.3.4",
  1267  					},
  1268  					dnsnames: [][]string{},
  1269  				},
  1270  			},
  1271  			expected: []*endpoint.Endpoint{
  1272  				{
  1273  					DNSName:    "fake1.ext-dns.test.com",
  1274  					Targets:    endpoint.Targets{"gateway-target.com"},
  1275  					RecordType: endpoint.RecordTypeCNAME,
  1276  				},
  1277  				{
  1278  					DNSName:    "fake2.ext-dns.test.com",
  1279  					Targets:    endpoint.Targets{"gateway-target.com"},
  1280  					RecordType: endpoint.RecordTypeCNAME,
  1281  				},
  1282  				{
  1283  					DNSName:    "fake3.ext-dns.test.com",
  1284  					Targets:    endpoint.Targets{"1.2.3.4"},
  1285  					RecordType: endpoint.RecordTypeA,
  1286  				},
  1287  			},
  1288  			fqdnTemplate: "{{.Name}}.ext-dns.test.com",
  1289  		},
  1290  		{
  1291  			title:           "Ingress with empty annotation",
  1292  			targetNamespace: "",
  1293  			lbServices: []fakeIngressGatewayService{
  1294  				{
  1295  					ips:       []string{},
  1296  					hostnames: []string{},
  1297  				},
  1298  			},
  1299  			configItems: []fakeGatewayConfig{
  1300  				{
  1301  					name:      "fake1",
  1302  					namespace: "",
  1303  					annotations: map[string]string{
  1304  						targetAnnotationKey: "",
  1305  					},
  1306  					dnsnames: [][]string{},
  1307  				},
  1308  			},
  1309  			expected:     []*endpoint.Endpoint{},
  1310  			fqdnTemplate: "{{.Name}}.ext-dns.test.com",
  1311  		},
  1312  		{
  1313  			title:           "Gateway with empty ingress annotation",
  1314  			targetNamespace: "",
  1315  			lbServices: []fakeIngressGatewayService{
  1316  				{
  1317  					ips:       []string{},
  1318  					hostnames: []string{},
  1319  				},
  1320  			},
  1321  			ingresses: []fakeIngress{
  1322  				{
  1323  					name:      "ingress1",
  1324  					ips:       []string{},
  1325  					hostnames: []string{},
  1326  				},
  1327  			},
  1328  			configItems: []fakeGatewayConfig{
  1329  				{
  1330  					name:      "fake1",
  1331  					namespace: "",
  1332  					annotations: map[string]string{
  1333  						IstioGatewayIngressSource: "",
  1334  					},
  1335  					dnsnames: [][]string{},
  1336  				},
  1337  			},
  1338  			expected:     []*endpoint.Endpoint{},
  1339  			fqdnTemplate: "{{.Name}}.ext-dns.test.com",
  1340  		},
  1341  		{
  1342  			title:           "ignore hostname annotations",
  1343  			targetNamespace: "",
  1344  			lbServices: []fakeIngressGatewayService{
  1345  				{
  1346  					ips:       []string{"8.8.8.8"},
  1347  					hostnames: []string{"lb.com"},
  1348  				},
  1349  			},
  1350  			configItems: []fakeGatewayConfig{
  1351  				{
  1352  					name:      "fake1",
  1353  					namespace: "",
  1354  					annotations: map[string]string{
  1355  						hostnameAnnotationKey: "ignore.me",
  1356  					},
  1357  					dnsnames: [][]string{{"example.org"}},
  1358  				},
  1359  				{
  1360  					name:      "fake2",
  1361  					namespace: "",
  1362  					annotations: map[string]string{
  1363  						hostnameAnnotationKey: "ignore.me.too",
  1364  					},
  1365  					dnsnames: [][]string{{"new.org"}},
  1366  				},
  1367  			},
  1368  			expected: []*endpoint.Endpoint{
  1369  				{
  1370  					DNSName:    "example.org",
  1371  					RecordType: endpoint.RecordTypeA,
  1372  					Targets:    endpoint.Targets{"8.8.8.8"},
  1373  				},
  1374  				{
  1375  					DNSName:    "example.org",
  1376  					RecordType: endpoint.RecordTypeCNAME,
  1377  					Targets:    endpoint.Targets{"lb.com"},
  1378  				},
  1379  				{
  1380  					DNSName:    "new.org",
  1381  					RecordType: endpoint.RecordTypeA,
  1382  					Targets:    endpoint.Targets{"8.8.8.8"},
  1383  				},
  1384  				{
  1385  					DNSName:    "new.org",
  1386  					RecordType: endpoint.RecordTypeCNAME,
  1387  					Targets:    endpoint.Targets{"lb.com"},
  1388  				},
  1389  			},
  1390  			ignoreHostnameAnnotation: true,
  1391  		},
  1392  		{
  1393  			title:           "gateways with wildcard host",
  1394  			targetNamespace: "",
  1395  			lbServices: []fakeIngressGatewayService{
  1396  				{
  1397  					ips: []string{"1.2.3.4"},
  1398  				},
  1399  			},
  1400  			configItems: []fakeGatewayConfig{
  1401  				{
  1402  					name:      "fake1",
  1403  					namespace: "",
  1404  					dnsnames:  [][]string{{"*"}},
  1405  				},
  1406  				{
  1407  					name:      "fake2",
  1408  					namespace: "",
  1409  					dnsnames:  [][]string{{"some-namespace/*"}},
  1410  				},
  1411  			},
  1412  			expected: []*endpoint.Endpoint{},
  1413  		},
  1414  		{
  1415  			title:           "gateways with wildcard host and hostname annotation",
  1416  			targetNamespace: "",
  1417  			lbServices: []fakeIngressGatewayService{
  1418  				{
  1419  					ips: []string{"1.2.3.4"},
  1420  				},
  1421  			},
  1422  			configItems: []fakeGatewayConfig{
  1423  				{
  1424  					name:      "fake1",
  1425  					namespace: "",
  1426  					annotations: map[string]string{
  1427  						hostnameAnnotationKey: "fake1.dns-through-hostname.com",
  1428  					},
  1429  					dnsnames: [][]string{{"*"}},
  1430  				},
  1431  				{
  1432  					name:      "fake2",
  1433  					namespace: "",
  1434  					annotations: map[string]string{
  1435  						hostnameAnnotationKey: "fake2.dns-through-hostname.com",
  1436  					},
  1437  					dnsnames: [][]string{{"some-namespace/*"}},
  1438  				},
  1439  			},
  1440  			expected: []*endpoint.Endpoint{
  1441  				{
  1442  					DNSName:    "fake1.dns-through-hostname.com",
  1443  					RecordType: endpoint.RecordTypeA,
  1444  					Targets:    endpoint.Targets{"1.2.3.4"},
  1445  				},
  1446  				{
  1447  					DNSName:    "fake2.dns-through-hostname.com",
  1448  					RecordType: endpoint.RecordTypeA,
  1449  					Targets:    endpoint.Targets{"1.2.3.4"},
  1450  				},
  1451  			},
  1452  		},
  1453  		{
  1454  			title:           "gateways with ingress annotation; ingress not found",
  1455  			targetNamespace: "",
  1456  			ingresses: []fakeIngress{
  1457  				{
  1458  					name: "ingress1",
  1459  					ips:  []string{"8.8.8.8"},
  1460  				},
  1461  			},
  1462  			configItems: []fakeGatewayConfig{
  1463  				{
  1464  					name:      "fake1",
  1465  					namespace: "",
  1466  					annotations: map[string]string{
  1467  						IstioGatewayIngressSource: "ingress2",
  1468  					},
  1469  					dnsnames: [][]string{{"new.org"}},
  1470  				},
  1471  			},
  1472  			expected:    []*endpoint.Endpoint{},
  1473  			expectError: true,
  1474  		},
  1475  	} {
  1476  		ti := ti
  1477  		t.Run(ti.title, func(t *testing.T) {
  1478  			t.Parallel()
  1479  
  1480  			fakeKubernetesClient := fake.NewSimpleClientset()
  1481  
  1482  			for _, lb := range ti.lbServices {
  1483  				service := lb.Service()
  1484  				_, err := fakeKubernetesClient.CoreV1().Services(service.Namespace).Create(context.Background(), service, metav1.CreateOptions{})
  1485  				require.NoError(t, err)
  1486  			}
  1487  
  1488  			for _, ing := range ti.ingresses {
  1489  				ingress := ing.Ingress()
  1490  				_, err := fakeKubernetesClient.NetworkingV1().Ingresses(ingress.Namespace).Create(context.Background(), ingress, metav1.CreateOptions{})
  1491  				require.NoError(t, err)
  1492  			}
  1493  
  1494  			fakeIstioClient := istiofake.NewSimpleClientset()
  1495  			for _, config := range ti.configItems {
  1496  				gatewayCfg := config.Config()
  1497  				_, err := fakeIstioClient.NetworkingV1alpha3().Gateways(ti.targetNamespace).Create(context.Background(), gatewayCfg, metav1.CreateOptions{})
  1498  				require.NoError(t, err)
  1499  			}
  1500  
  1501  			gatewaySource, err := NewIstioGatewaySource(
  1502  				context.TODO(),
  1503  				fakeKubernetesClient,
  1504  				fakeIstioClient,
  1505  				ti.targetNamespace,
  1506  				ti.annotationFilter,
  1507  				ti.fqdnTemplate,
  1508  				ti.combineFQDNAndAnnotation,
  1509  				ti.ignoreHostnameAnnotation,
  1510  			)
  1511  			require.NoError(t, err)
  1512  
  1513  			res, err := gatewaySource.Endpoints(context.Background())
  1514  			if ti.expectError {
  1515  				assert.Error(t, err)
  1516  			} else {
  1517  				assert.NoError(t, err)
  1518  			}
  1519  
  1520  			validateEndpoints(t, res, ti.expected)
  1521  		})
  1522  	}
  1523  }
  1524  
  1525  // gateway specific helper functions
  1526  func newTestGatewaySource(loadBalancerList []fakeIngressGatewayService, ingressList []fakeIngress) (*gatewaySource, error) {
  1527  	fakeKubernetesClient := fake.NewSimpleClientset()
  1528  	fakeIstioClient := istiofake.NewSimpleClientset()
  1529  
  1530  	for _, lb := range loadBalancerList {
  1531  		service := lb.Service()
  1532  		_, err := fakeKubernetesClient.CoreV1().Services(service.Namespace).Create(context.Background(), service, metav1.CreateOptions{})
  1533  		if err != nil {
  1534  			return nil, err
  1535  		}
  1536  	}
  1537  	for _, ing := range ingressList {
  1538  		ingress := ing.Ingress()
  1539  		_, err := fakeKubernetesClient.NetworkingV1().Ingresses(ingress.Namespace).Create(context.Background(), ingress, metav1.CreateOptions{})
  1540  		if err != nil {
  1541  			return nil, err
  1542  		}
  1543  	}
  1544  
  1545  	src, err := NewIstioGatewaySource(
  1546  		context.TODO(),
  1547  		fakeKubernetesClient,
  1548  		fakeIstioClient,
  1549  		"",
  1550  		"",
  1551  		"{{.Name}}",
  1552  		false,
  1553  		false,
  1554  	)
  1555  	if err != nil {
  1556  		return nil, err
  1557  	}
  1558  
  1559  	gwsrc, ok := src.(*gatewaySource)
  1560  	if !ok {
  1561  		return nil, errors.New("underlying source type was not gateway")
  1562  	}
  1563  
  1564  	return gwsrc, nil
  1565  }
  1566  
  1567  type fakeIngressGatewayService struct {
  1568  	ips         []string
  1569  	hostnames   []string
  1570  	namespace   string
  1571  	name        string
  1572  	selector    map[string]string
  1573  	externalIPs []string
  1574  }
  1575  
  1576  func (ig fakeIngressGatewayService) Service() *v1.Service {
  1577  	svc := &v1.Service{
  1578  		ObjectMeta: metav1.ObjectMeta{
  1579  			Namespace: ig.namespace,
  1580  			Name:      ig.name,
  1581  		},
  1582  		Status: v1.ServiceStatus{
  1583  			LoadBalancer: v1.LoadBalancerStatus{
  1584  				Ingress: []v1.LoadBalancerIngress{},
  1585  			},
  1586  		},
  1587  		Spec: v1.ServiceSpec{
  1588  			Selector:    ig.selector,
  1589  			ExternalIPs: ig.externalIPs,
  1590  		},
  1591  	}
  1592  
  1593  	for _, ip := range ig.ips {
  1594  		svc.Status.LoadBalancer.Ingress = append(svc.Status.LoadBalancer.Ingress, v1.LoadBalancerIngress{
  1595  			IP: ip,
  1596  		})
  1597  	}
  1598  	for _, hostname := range ig.hostnames {
  1599  		svc.Status.LoadBalancer.Ingress = append(svc.Status.LoadBalancer.Ingress, v1.LoadBalancerIngress{
  1600  			Hostname: hostname,
  1601  		})
  1602  	}
  1603  
  1604  	return svc
  1605  }
  1606  
  1607  type fakeGatewayConfig struct {
  1608  	namespace   string
  1609  	name        string
  1610  	annotations map[string]string
  1611  	dnsnames    [][]string
  1612  	selector    map[string]string
  1613  }
  1614  
  1615  func (c fakeGatewayConfig) Config() *networkingv1alpha3.Gateway {
  1616  	gw := &networkingv1alpha3.Gateway{
  1617  		ObjectMeta: metav1.ObjectMeta{
  1618  			Name:        c.name,
  1619  			Namespace:   c.namespace,
  1620  			Annotations: c.annotations,
  1621  		},
  1622  		Spec: networkingv1alpha3api.Gateway{
  1623  			Servers:  nil,
  1624  			Selector: c.selector,
  1625  		},
  1626  	}
  1627  
  1628  	var servers []*networkingv1alpha3api.Server
  1629  	for _, dnsnames := range c.dnsnames {
  1630  		servers = append(servers, &networkingv1alpha3api.Server{
  1631  			Hosts: dnsnames,
  1632  		})
  1633  	}
  1634  
  1635  	gw.Spec.Servers = servers
  1636  
  1637  	return gw
  1638  }