sigs.k8s.io/external-dns@v0.14.1/source/service_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  	"net"
    22  	"sort"
    23  	"strings"
    24  	"testing"
    25  
    26  	"github.com/stretchr/testify/assert"
    27  	"github.com/stretchr/testify/require"
    28  	"github.com/stretchr/testify/suite"
    29  	v1 "k8s.io/api/core/v1"
    30  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    31  	"k8s.io/apimachinery/pkg/labels"
    32  	"k8s.io/client-go/kubernetes/fake"
    33  
    34  	"sigs.k8s.io/external-dns/endpoint"
    35  )
    36  
    37  type ServiceSuite struct {
    38  	suite.Suite
    39  	sc             Source
    40  	fooWithTargets *v1.Service
    41  }
    42  
    43  func (suite *ServiceSuite) SetupTest() {
    44  	fakeClient := fake.NewSimpleClientset()
    45  
    46  	suite.fooWithTargets = &v1.Service{
    47  		Spec: v1.ServiceSpec{
    48  			Type: v1.ServiceTypeLoadBalancer,
    49  		},
    50  		ObjectMeta: metav1.ObjectMeta{
    51  			Namespace:   "default",
    52  			Name:        "foo-with-targets",
    53  			Annotations: map[string]string{},
    54  		},
    55  		Status: v1.ServiceStatus{
    56  			LoadBalancer: v1.LoadBalancerStatus{
    57  				Ingress: []v1.LoadBalancerIngress{
    58  					{IP: "8.8.8.8"},
    59  					{Hostname: "foo"},
    60  				},
    61  			},
    62  		},
    63  	}
    64  	_, err := fakeClient.CoreV1().Services(suite.fooWithTargets.Namespace).Create(context.Background(), suite.fooWithTargets, metav1.CreateOptions{})
    65  	suite.NoError(err, "should successfully create service")
    66  
    67  	suite.sc, err = NewServiceSource(
    68  		context.TODO(),
    69  		fakeClient,
    70  		"",
    71  		"",
    72  		"{{.Name}}",
    73  		false,
    74  		"",
    75  		false,
    76  		false,
    77  		false,
    78  		[]string{},
    79  		false,
    80  		labels.Everything(),
    81  		false,
    82  	)
    83  	suite.NoError(err, "should initialize service source")
    84  }
    85  
    86  func (suite *ServiceSuite) TestResourceLabelIsSet() {
    87  	endpoints, _ := suite.sc.Endpoints(context.Background())
    88  	for _, ep := range endpoints {
    89  		suite.Equal("service/default/foo-with-targets", ep.Labels[endpoint.ResourceLabelKey], "should set correct resource label")
    90  	}
    91  }
    92  
    93  func TestServiceSource(t *testing.T) {
    94  	t.Parallel()
    95  
    96  	suite.Run(t, new(ServiceSuite))
    97  	t.Run("Interface", testServiceSourceImplementsSource)
    98  	t.Run("NewServiceSource", testServiceSourceNewServiceSource)
    99  	t.Run("Endpoints", testServiceSourceEndpoints)
   100  	t.Run("MultipleServices", testMultipleServicesEndpoints)
   101  }
   102  
   103  // testServiceSourceImplementsSource tests that serviceSource is a valid Source.
   104  func testServiceSourceImplementsSource(t *testing.T) {
   105  	assert.Implements(t, (*Source)(nil), new(serviceSource))
   106  }
   107  
   108  // testServiceSourceNewServiceSource tests that NewServiceSource doesn't return an error.
   109  func testServiceSourceNewServiceSource(t *testing.T) {
   110  	t.Parallel()
   111  
   112  	for _, ti := range []struct {
   113  		title              string
   114  		annotationFilter   string
   115  		fqdnTemplate       string
   116  		serviceTypesFilter []string
   117  		expectError        bool
   118  	}{
   119  		{
   120  			title:        "invalid template",
   121  			expectError:  true,
   122  			fqdnTemplate: "{{.Name",
   123  		},
   124  		{
   125  			title:       "valid empty template",
   126  			expectError: false,
   127  		},
   128  		{
   129  			title:        "valid template",
   130  			expectError:  false,
   131  			fqdnTemplate: "{{.Name}}-{{.Namespace}}.ext-dns.test.com",
   132  		},
   133  		{
   134  			title:            "non-empty annotation filter label",
   135  			expectError:      false,
   136  			annotationFilter: "kubernetes.io/ingress.class=nginx",
   137  		},
   138  		{
   139  			title:              "non-empty service types filter",
   140  			expectError:        false,
   141  			serviceTypesFilter: []string{string(v1.ServiceTypeClusterIP)},
   142  		},
   143  	} {
   144  		ti := ti
   145  		t.Run(ti.title, func(t *testing.T) {
   146  			t.Parallel()
   147  
   148  			_, err := NewServiceSource(
   149  				context.TODO(),
   150  				fake.NewSimpleClientset(),
   151  				"",
   152  				ti.annotationFilter,
   153  				ti.fqdnTemplate,
   154  				false,
   155  				"",
   156  				false,
   157  				false,
   158  				false,
   159  				ti.serviceTypesFilter,
   160  				false,
   161  				labels.Everything(),
   162  				false,
   163  			)
   164  
   165  			if ti.expectError {
   166  				assert.Error(t, err)
   167  			} else {
   168  				assert.NoError(t, err)
   169  			}
   170  		})
   171  	}
   172  }
   173  
   174  // testServiceSourceEndpoints tests that various services generate the correct endpoints.
   175  func testServiceSourceEndpoints(t *testing.T) {
   176  	t.Parallel()
   177  
   178  	for _, tc := range []struct {
   179  		title                       string
   180  		targetNamespace             string
   181  		annotationFilter            string
   182  		svcNamespace                string
   183  		svcName                     string
   184  		svcType                     v1.ServiceType
   185  		compatibility               string
   186  		fqdnTemplate                string
   187  		combineFQDNAndAnnotation    bool
   188  		ignoreHostnameAnnotation    bool
   189  		labels                      map[string]string
   190  		annotations                 map[string]string
   191  		clusterIP                   string
   192  		externalIPs                 []string
   193  		lbs                         []string
   194  		serviceTypesFilter          []string
   195  		expected                    []*endpoint.Endpoint
   196  		expectError                 bool
   197  		serviceLabelSelector        string
   198  		resolveLoadBalancerHostname bool
   199  	}{
   200  		{
   201  			title:              "no annotated services return no endpoints",
   202  			svcNamespace:       "testing",
   203  			svcName:            "foo",
   204  			svcType:            v1.ServiceTypeLoadBalancer,
   205  			labels:             map[string]string{},
   206  			annotations:        map[string]string{},
   207  			externalIPs:        []string{},
   208  			lbs:                []string{"1.2.3.4"},
   209  			serviceTypesFilter: []string{},
   210  			expected:           []*endpoint.Endpoint{},
   211  		},
   212  		{
   213  			title:                    "no annotated services return no endpoints when ignoring annotations",
   214  			svcNamespace:             "testing",
   215  			svcName:                  "foo",
   216  			svcType:                  v1.ServiceTypeLoadBalancer,
   217  			ignoreHostnameAnnotation: true,
   218  			labels:                   map[string]string{},
   219  			annotations:              map[string]string{},
   220  			externalIPs:              []string{},
   221  			lbs:                      []string{"1.2.3.4"},
   222  			serviceTypesFilter:       []string{},
   223  			expected:                 []*endpoint.Endpoint{},
   224  		},
   225  		{
   226  			title:        "annotated services return an endpoint with target IP",
   227  			svcNamespace: "testing",
   228  			svcName:      "foo",
   229  			svcType:      v1.ServiceTypeLoadBalancer,
   230  			labels:       map[string]string{},
   231  			annotations: map[string]string{
   232  				hostnameAnnotationKey: "foo.example.org.",
   233  			},
   234  			externalIPs:        []string{},
   235  			lbs:                []string{"1.2.3.4"},
   236  			serviceTypesFilter: []string{},
   237  			expected: []*endpoint.Endpoint{
   238  				{DNSName: "foo.example.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"1.2.3.4"}},
   239  			},
   240  		},
   241  		{
   242  			title:                    "hostname annotation on services is ignored",
   243  			svcNamespace:             "testing",
   244  			svcName:                  "foo",
   245  			svcType:                  v1.ServiceTypeLoadBalancer,
   246  			ignoreHostnameAnnotation: true,
   247  			labels:                   map[string]string{},
   248  			annotations: map[string]string{
   249  				hostnameAnnotationKey: "foo.example.org.",
   250  			},
   251  			externalIPs:        []string{},
   252  			lbs:                []string{"1.2.3.4"},
   253  			serviceTypesFilter: []string{},
   254  			expected:           []*endpoint.Endpoint{},
   255  		},
   256  		{
   257  			title:        "annotated ClusterIp aren't processed without explicit authorization",
   258  			svcNamespace: "testing",
   259  			svcName:      "foo",
   260  			svcType:      v1.ServiceTypeClusterIP,
   261  			labels:       map[string]string{},
   262  			annotations: map[string]string{
   263  				hostnameAnnotationKey: "foo.example.org.",
   264  			},
   265  			clusterIP:          "1.2.3.4",
   266  			externalIPs:        []string{},
   267  			lbs:                []string{},
   268  			serviceTypesFilter: []string{},
   269  			expected:           []*endpoint.Endpoint{},
   270  		},
   271  		{
   272  			title:              "FQDN template with multiple hostnames return an endpoint with target IP",
   273  			svcNamespace:       "testing",
   274  			svcName:            "foo",
   275  			svcType:            v1.ServiceTypeLoadBalancer,
   276  			fqdnTemplate:       "{{.Name}}.fqdn.org,{{.Name}}.fqdn.com",
   277  			labels:             map[string]string{},
   278  			annotations:        map[string]string{},
   279  			externalIPs:        []string{},
   280  			lbs:                []string{"1.2.3.4"},
   281  			serviceTypesFilter: []string{},
   282  			expected: []*endpoint.Endpoint{
   283  				{DNSName: "foo.fqdn.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"1.2.3.4"}},
   284  				{DNSName: "foo.fqdn.com", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"1.2.3.4"}},
   285  			},
   286  		},
   287  		{
   288  			title:                    "FQDN template with multiple hostnames return an endpoint with target IP when ignoring annotations",
   289  			svcNamespace:             "testing",
   290  			svcName:                  "foo",
   291  			svcType:                  v1.ServiceTypeLoadBalancer,
   292  			fqdnTemplate:             "{{.Name}}.fqdn.org,{{.Name}}.fqdn.com",
   293  			ignoreHostnameAnnotation: true,
   294  			labels:                   map[string]string{},
   295  			annotations:              map[string]string{},
   296  			externalIPs:              []string{},
   297  			lbs:                      []string{"1.2.3.4"},
   298  			serviceTypesFilter:       []string{},
   299  			expected: []*endpoint.Endpoint{
   300  				{DNSName: "foo.fqdn.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"1.2.3.4"}},
   301  				{DNSName: "foo.fqdn.com", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"1.2.3.4"}},
   302  			},
   303  		},
   304  		{
   305  			title:                    "FQDN template and annotation both with multiple hostnames return an endpoint with target IP",
   306  			svcNamespace:             "testing",
   307  			svcName:                  "foo",
   308  			svcType:                  v1.ServiceTypeLoadBalancer,
   309  			fqdnTemplate:             "{{.Name}}.fqdn.org,{{.Name}}.fqdn.com",
   310  			combineFQDNAndAnnotation: true,
   311  			labels:                   map[string]string{},
   312  			annotations: map[string]string{
   313  				hostnameAnnotationKey: "foo.example.org., bar.example.org.",
   314  			},
   315  			externalIPs:        []string{},
   316  			lbs:                []string{"1.2.3.4"},
   317  			serviceTypesFilter: []string{},
   318  			expected: []*endpoint.Endpoint{
   319  				{DNSName: "foo.example.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"1.2.3.4"}},
   320  				{DNSName: "bar.example.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"1.2.3.4"}},
   321  				{DNSName: "foo.fqdn.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"1.2.3.4"}},
   322  				{DNSName: "foo.fqdn.com", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"1.2.3.4"}},
   323  			},
   324  		},
   325  		{
   326  			title:                    "FQDN template and annotation both with multiple hostnames while ignoring annotations will only return FQDN endpoints",
   327  			svcNamespace:             "testing",
   328  			svcName:                  "foo",
   329  			svcType:                  v1.ServiceTypeLoadBalancer,
   330  			fqdnTemplate:             "{{.Name}}.fqdn.org,{{.Name}}.fqdn.com",
   331  			combineFQDNAndAnnotation: true,
   332  			ignoreHostnameAnnotation: true,
   333  			labels:                   map[string]string{},
   334  			annotations: map[string]string{
   335  				hostnameAnnotationKey: "foo.example.org., bar.example.org.",
   336  			},
   337  			externalIPs:        []string{},
   338  			lbs:                []string{"1.2.3.4"},
   339  			serviceTypesFilter: []string{},
   340  			expected: []*endpoint.Endpoint{
   341  				{DNSName: "foo.fqdn.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"1.2.3.4"}},
   342  				{DNSName: "foo.fqdn.com", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"1.2.3.4"}},
   343  			},
   344  		},
   345  		{
   346  			title:        "annotated services with multiple hostnames return an endpoint with target IP",
   347  			svcNamespace: "testing",
   348  			svcName:      "foo",
   349  			svcType:      v1.ServiceTypeLoadBalancer,
   350  			labels:       map[string]string{},
   351  			annotations: map[string]string{
   352  				hostnameAnnotationKey: "foo.example.org., bar.example.org.",
   353  			},
   354  			externalIPs:        []string{},
   355  			lbs:                []string{"1.2.3.4"},
   356  			serviceTypesFilter: []string{},
   357  			expected: []*endpoint.Endpoint{
   358  				{DNSName: "foo.example.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"1.2.3.4"}},
   359  				{DNSName: "bar.example.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"1.2.3.4"}},
   360  			},
   361  		},
   362  		{
   363  			title:        "annotated services with multiple hostnames and without trailing period return an endpoint with target IP",
   364  			svcNamespace: "testing",
   365  			svcName:      "foo",
   366  			svcType:      v1.ServiceTypeLoadBalancer,
   367  			labels:       map[string]string{},
   368  			annotations: map[string]string{
   369  				hostnameAnnotationKey: "foo.example.org, bar.example.org",
   370  			},
   371  			externalIPs:        []string{},
   372  			lbs:                []string{"1.2.3.4"},
   373  			serviceTypesFilter: []string{},
   374  			expected: []*endpoint.Endpoint{
   375  				{DNSName: "foo.example.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"1.2.3.4"}},
   376  				{DNSName: "bar.example.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"1.2.3.4"}},
   377  			},
   378  		},
   379  		{
   380  			title:        "annotated services return an endpoint with target hostname",
   381  			svcNamespace: "testing",
   382  			svcName:      "foo",
   383  			svcType:      v1.ServiceTypeLoadBalancer,
   384  			labels:       map[string]string{},
   385  			annotations: map[string]string{
   386  				hostnameAnnotationKey: "foo.example.org.",
   387  			},
   388  			externalIPs:        []string{},
   389  			lbs:                []string{"lb.example.com"}, // Kubernetes omits the trailing dot
   390  			serviceTypesFilter: []string{},
   391  			expected: []*endpoint.Endpoint{
   392  				{DNSName: "foo.example.org", RecordType: endpoint.RecordTypeCNAME, Targets: endpoint.Targets{"lb.example.com"}},
   393  			},
   394  		},
   395  		{
   396  			title:        "annotated services return an endpoint with hostname then resolve hostname",
   397  			svcNamespace: "testing",
   398  			svcName:      "foo",
   399  			svcType:      v1.ServiceTypeLoadBalancer,
   400  			labels:       map[string]string{},
   401  			annotations: map[string]string{
   402  				hostnameAnnotationKey: "foo.example.org.",
   403  			},
   404  			externalIPs:                 []string{},
   405  			lbs:                         []string{"example.com"}, // Use a resolvable hostname for testing.
   406  			serviceTypesFilter:          []string{},
   407  			resolveLoadBalancerHostname: true,
   408  			expected: []*endpoint.Endpoint{
   409  				{DNSName: "foo.example.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"93.184.216.34"}},
   410  				{DNSName: "foo.example.org", RecordType: endpoint.RecordTypeAAAA, Targets: endpoint.Targets{"2606:2800:220:1:248:1893:25c8:1946"}},
   411  			},
   412  		},
   413  		{
   414  			title:        "annotated services can omit trailing dot",
   415  			svcNamespace: "testing",
   416  			svcName:      "foo",
   417  			svcType:      v1.ServiceTypeLoadBalancer,
   418  			labels:       map[string]string{},
   419  			annotations: map[string]string{
   420  				hostnameAnnotationKey: "foo.example.org", // Trailing dot is omitted
   421  			},
   422  			externalIPs:        []string{},
   423  			lbs:                []string{"1.2.3.4", "lb.example.com"}, // Kubernetes omits the trailing dot
   424  			serviceTypesFilter: []string{},
   425  			expected: []*endpoint.Endpoint{
   426  				{DNSName: "foo.example.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"1.2.3.4"}},
   427  				{DNSName: "foo.example.org", RecordType: endpoint.RecordTypeCNAME, Targets: endpoint.Targets{"lb.example.com"}},
   428  			},
   429  		},
   430  		{
   431  			title:        "our controller type is kops dns controller",
   432  			svcNamespace: "testing",
   433  			svcName:      "foo",
   434  			svcType:      v1.ServiceTypeLoadBalancer,
   435  			labels:       map[string]string{},
   436  			annotations: map[string]string{
   437  				controllerAnnotationKey: controllerAnnotationValue,
   438  				hostnameAnnotationKey:   "foo.example.org.",
   439  			},
   440  			externalIPs:        []string{},
   441  			lbs:                []string{"1.2.3.4"},
   442  			serviceTypesFilter: []string{},
   443  			expected: []*endpoint.Endpoint{
   444  				{DNSName: "foo.example.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"1.2.3.4"}},
   445  			},
   446  		},
   447  		{
   448  			title:        "different controller types are ignored even (with template specified)",
   449  			svcNamespace: "testing",
   450  			svcName:      "foo",
   451  			svcType:      v1.ServiceTypeLoadBalancer,
   452  			fqdnTemplate: "{{.Name}}.ext-dns.test.com",
   453  			labels:       map[string]string{},
   454  			annotations: map[string]string{
   455  				controllerAnnotationKey: "some-other-tool",
   456  				hostnameAnnotationKey:   "foo.example.org.",
   457  			},
   458  			externalIPs:        []string{},
   459  			lbs:                []string{"1.2.3.4"},
   460  			serviceTypesFilter: []string{},
   461  			expected:           []*endpoint.Endpoint{},
   462  		},
   463  		{
   464  			title:           "services are found in target namespace",
   465  			targetNamespace: "testing",
   466  			svcNamespace:    "testing",
   467  			svcName:         "foo",
   468  			svcType:         v1.ServiceTypeLoadBalancer,
   469  			labels:          map[string]string{},
   470  			annotations: map[string]string{
   471  				hostnameAnnotationKey: "foo.example.org.",
   472  			},
   473  			externalIPs:        []string{},
   474  			lbs:                []string{"1.2.3.4"},
   475  			serviceTypesFilter: []string{},
   476  			expected: []*endpoint.Endpoint{
   477  				{DNSName: "foo.example.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"1.2.3.4"}},
   478  			},
   479  		},
   480  		{
   481  			title:           "services that are not in target namespace are ignored",
   482  			targetNamespace: "testing",
   483  			svcNamespace:    "other-testing",
   484  			svcName:         "foo",
   485  			svcType:         v1.ServiceTypeLoadBalancer,
   486  			labels:          map[string]string{},
   487  			annotations: map[string]string{
   488  				hostnameAnnotationKey: "foo.example.org.",
   489  			},
   490  			externalIPs:        []string{},
   491  			lbs:                []string{"1.2.3.4"},
   492  			serviceTypesFilter: []string{},
   493  			expected:           []*endpoint.Endpoint{},
   494  		},
   495  		{
   496  			title:        "services are found in all namespaces",
   497  			svcNamespace: "other-testing",
   498  			svcName:      "foo",
   499  			svcType:      v1.ServiceTypeLoadBalancer,
   500  			labels:       map[string]string{},
   501  			annotations: map[string]string{
   502  				hostnameAnnotationKey: "foo.example.org.",
   503  			},
   504  			externalIPs:        []string{},
   505  			lbs:                []string{"1.2.3.4"},
   506  			serviceTypesFilter: []string{},
   507  			expected: []*endpoint.Endpoint{
   508  				{DNSName: "foo.example.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"1.2.3.4"}},
   509  			},
   510  		},
   511  		{
   512  			title:            "valid matching annotation filter expression",
   513  			annotationFilter: "service.beta.kubernetes.io/external-traffic in (Global, OnlyLocal)",
   514  			svcNamespace:     "testing",
   515  			svcName:          "foo",
   516  			svcType:          v1.ServiceTypeLoadBalancer,
   517  			labels:           map[string]string{},
   518  			annotations: map[string]string{
   519  				hostnameAnnotationKey:                         "foo.example.org.",
   520  				"service.beta.kubernetes.io/external-traffic": "OnlyLocal",
   521  			},
   522  			externalIPs:        []string{},
   523  			lbs:                []string{"1.2.3.4"},
   524  			serviceTypesFilter: []string{},
   525  			expected: []*endpoint.Endpoint{
   526  				{DNSName: "foo.example.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"1.2.3.4"}},
   527  			},
   528  		},
   529  		{
   530  			title:            "valid non-matching annotation filter expression",
   531  			annotationFilter: "service.beta.kubernetes.io/external-traffic in (Global, OnlyLocal)",
   532  			svcNamespace:     "testing",
   533  			svcName:          "foo",
   534  			svcType:          v1.ServiceTypeLoadBalancer,
   535  			labels:           map[string]string{},
   536  			annotations: map[string]string{
   537  				hostnameAnnotationKey:                         "foo.example.org.",
   538  				"service.beta.kubernetes.io/external-traffic": "SomethingElse",
   539  			},
   540  			externalIPs:        []string{},
   541  			lbs:                []string{"1.2.3.4"},
   542  			serviceTypesFilter: []string{},
   543  			expected:           []*endpoint.Endpoint{},
   544  		},
   545  		{
   546  			title:            "invalid annotation filter expression",
   547  			annotationFilter: "service.beta.kubernetes.io/external-traffic in (Global OnlyLocal)",
   548  			svcNamespace:     "testing",
   549  			svcName:          "foo",
   550  			svcType:          v1.ServiceTypeLoadBalancer,
   551  			labels:           map[string]string{},
   552  			annotations: map[string]string{
   553  				hostnameAnnotationKey:                         "foo.example.org.",
   554  				"service.beta.kubernetes.io/external-traffic": "OnlyLocal",
   555  			},
   556  			externalIPs:        []string{},
   557  			lbs:                []string{"1.2.3.4"},
   558  			serviceTypesFilter: []string{},
   559  			expected:           []*endpoint.Endpoint{},
   560  			expectError:        true,
   561  		},
   562  		{
   563  			title:            "valid matching annotation filter label",
   564  			annotationFilter: "service.beta.kubernetes.io/external-traffic=Global",
   565  			svcNamespace:     "testing",
   566  			svcName:          "foo",
   567  			svcType:          v1.ServiceTypeLoadBalancer,
   568  			labels:           map[string]string{},
   569  			annotations: map[string]string{
   570  				hostnameAnnotationKey:                         "foo.example.org.",
   571  				"service.beta.kubernetes.io/external-traffic": "Global",
   572  			},
   573  			externalIPs:        []string{},
   574  			lbs:                []string{"1.2.3.4"},
   575  			serviceTypesFilter: []string{},
   576  			expected: []*endpoint.Endpoint{
   577  				{DNSName: "foo.example.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"1.2.3.4"}},
   578  			},
   579  		},
   580  		{
   581  			title:            "valid non-matching annotation filter label",
   582  			annotationFilter: "service.beta.kubernetes.io/external-traffic=Global",
   583  			svcNamespace:     "testing",
   584  			svcName:          "foo",
   585  			svcType:          v1.ServiceTypeLoadBalancer,
   586  			labels:           map[string]string{},
   587  			annotations: map[string]string{
   588  				hostnameAnnotationKey:                         "foo.example.org.",
   589  				"service.beta.kubernetes.io/external-traffic": "OnlyLocal",
   590  			},
   591  			externalIPs:        []string{},
   592  			lbs:                []string{"1.2.3.4"},
   593  			serviceTypesFilter: []string{},
   594  			expected:           []*endpoint.Endpoint{},
   595  		},
   596  		{
   597  			title:        "no external entrypoints return no endpoints",
   598  			svcNamespace: "testing",
   599  			svcName:      "foo",
   600  			svcType:      v1.ServiceTypeLoadBalancer,
   601  			labels:       map[string]string{},
   602  			annotations: map[string]string{
   603  				hostnameAnnotationKey: "foo.example.org.",
   604  			},
   605  			externalIPs:        []string{},
   606  			lbs:                []string{},
   607  			serviceTypesFilter: []string{},
   608  			expected:           []*endpoint.Endpoint{},
   609  		},
   610  		{
   611  			title:        "annotated service with externalIPs returns a single endpoint with multiple targets",
   612  			svcNamespace: "testing",
   613  			svcName:      "foo",
   614  			svcType:      v1.ServiceTypeLoadBalancer,
   615  			labels:       map[string]string{},
   616  			annotations: map[string]string{
   617  				hostnameAnnotationKey: "foo.example.org.",
   618  			},
   619  			externalIPs:        []string{"10.2.3.4", "11.2.3.4"},
   620  			lbs:                []string{"1.2.3.4"},
   621  			serviceTypesFilter: []string{},
   622  			expected: []*endpoint.Endpoint{
   623  				{DNSName: "foo.example.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"10.2.3.4", "11.2.3.4"}},
   624  			},
   625  		},
   626  		{
   627  			title:        "multiple external entrypoints return a single endpoint with multiple targets",
   628  			svcNamespace: "testing",
   629  			svcName:      "foo",
   630  			svcType:      v1.ServiceTypeLoadBalancer,
   631  			labels:       map[string]string{},
   632  			annotations: map[string]string{
   633  				hostnameAnnotationKey: "foo.example.org.",
   634  			},
   635  			externalIPs:        []string{},
   636  			lbs:                []string{"1.2.3.4", "8.8.8.8"},
   637  			serviceTypesFilter: []string{},
   638  			expected: []*endpoint.Endpoint{
   639  				{DNSName: "foo.example.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"1.2.3.4", "8.8.8.8"}},
   640  			},
   641  		},
   642  		{
   643  			title:        "services annotated with legacy mate annotations are ignored in default mode",
   644  			svcNamespace: "testing",
   645  			svcName:      "foo",
   646  			svcType:      v1.ServiceTypeLoadBalancer,
   647  			labels:       map[string]string{},
   648  			annotations: map[string]string{
   649  				"zalando.org/dnsname": "foo.example.org.",
   650  			},
   651  			externalIPs:        []string{},
   652  			lbs:                []string{"1.2.3.4"},
   653  			serviceTypesFilter: []string{},
   654  			expected:           []*endpoint.Endpoint{},
   655  		},
   656  		{
   657  			title:         "services annotated with legacy mate annotations return an endpoint in compatibility mode",
   658  			svcNamespace:  "testing",
   659  			svcName:       "foo",
   660  			svcType:       v1.ServiceTypeLoadBalancer,
   661  			compatibility: "mate",
   662  			labels:        map[string]string{},
   663  			annotations: map[string]string{
   664  				"zalando.org/dnsname": "foo.example.org.",
   665  			},
   666  			externalIPs:        []string{},
   667  			lbs:                []string{"1.2.3.4"},
   668  			serviceTypesFilter: []string{},
   669  			expected: []*endpoint.Endpoint{
   670  				{DNSName: "foo.example.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"1.2.3.4"}},
   671  			},
   672  		},
   673  		{
   674  			title:         "services annotated with legacy molecule annotations return an endpoint in compatibility mode",
   675  			svcNamespace:  "testing",
   676  			svcName:       "foo",
   677  			svcType:       v1.ServiceTypeLoadBalancer,
   678  			compatibility: "molecule",
   679  			labels: map[string]string{
   680  				"dns": "route53",
   681  			},
   682  			annotations: map[string]string{
   683  				"domainName": "foo.example.org., bar.example.org",
   684  			},
   685  			externalIPs:        []string{},
   686  			lbs:                []string{"1.2.3.4"},
   687  			serviceTypesFilter: []string{},
   688  			expected: []*endpoint.Endpoint{
   689  				{DNSName: "foo.example.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"1.2.3.4"}},
   690  				{DNSName: "bar.example.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"1.2.3.4"}},
   691  			},
   692  		},
   693  		{
   694  			title:         "load balancer services annotated with DNS Controller annotations return an endpoint with A and CNAME targets in compatibility mode",
   695  			svcNamespace:  "testing",
   696  			svcName:       "foo",
   697  			svcType:       v1.ServiceTypeLoadBalancer,
   698  			compatibility: "kops-dns-controller",
   699  			labels:        map[string]string{},
   700  			annotations: map[string]string{
   701  				kopsDNSControllerInternalHostnameAnnotationKey: "internal.foo.example.org",
   702  			},
   703  			externalIPs:        []string{},
   704  			lbs:                []string{"1.2.3.4", "lb.example.com"},
   705  			serviceTypesFilter: []string{},
   706  			expected: []*endpoint.Endpoint{
   707  				{DNSName: "internal.foo.example.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"1.2.3.4"}},
   708  				{DNSName: "internal.foo.example.org", RecordType: endpoint.RecordTypeCNAME, Targets: endpoint.Targets{"lb.example.com"}},
   709  			},
   710  		},
   711  		{
   712  			title:         "load balancer services annotated with DNS Controller annotations return an endpoint with both annotations in compatibility mode",
   713  			svcNamespace:  "testing",
   714  			svcName:       "foo",
   715  			svcType:       v1.ServiceTypeLoadBalancer,
   716  			compatibility: "kops-dns-controller",
   717  			labels:        map[string]string{},
   718  			annotations: map[string]string{
   719  				kopsDNSControllerInternalHostnameAnnotationKey: "internal.foo.example.org., internal.bar.example.org",
   720  				kopsDNSControllerHostnameAnnotationKey:         "foo.example.org., bar.example.org",
   721  			},
   722  			externalIPs:        []string{},
   723  			lbs:                []string{"1.2.3.4"},
   724  			serviceTypesFilter: []string{},
   725  			expected: []*endpoint.Endpoint{
   726  				{DNSName: "foo.example.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"1.2.3.4"}},
   727  				{DNSName: "bar.example.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"1.2.3.4"}},
   728  				{DNSName: "internal.foo.example.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"1.2.3.4"}},
   729  				{DNSName: "internal.bar.example.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"1.2.3.4"}},
   730  			},
   731  		},
   732  		{
   733  			title:              "not annotated services with set fqdnTemplate return an endpoint with target IP",
   734  			svcNamespace:       "testing",
   735  			svcName:            "foo",
   736  			svcType:            v1.ServiceTypeLoadBalancer,
   737  			fqdnTemplate:       "{{.Name}}.bar.example.com",
   738  			labels:             map[string]string{},
   739  			annotations:        map[string]string{},
   740  			externalIPs:        []string{},
   741  			lbs:                []string{"1.2.3.4", "elb.com"},
   742  			serviceTypesFilter: []string{},
   743  			expected: []*endpoint.Endpoint{
   744  				{DNSName: "foo.bar.example.com", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"1.2.3.4"}},
   745  				{DNSName: "foo.bar.example.com", RecordType: endpoint.RecordTypeCNAME, Targets: endpoint.Targets{"elb.com"}},
   746  			},
   747  		},
   748  		{
   749  			title:        "annotated services with set fqdnTemplate annotation takes precedence",
   750  			svcNamespace: "testing",
   751  			svcName:      "foo",
   752  			svcType:      v1.ServiceTypeLoadBalancer,
   753  			fqdnTemplate: "{{.Name}}.bar.example.com",
   754  			labels:       map[string]string{},
   755  			annotations: map[string]string{
   756  				hostnameAnnotationKey: "foo.example.org.",
   757  			},
   758  			externalIPs:        []string{},
   759  			lbs:                []string{"1.2.3.4", "elb.com"},
   760  			serviceTypesFilter: []string{},
   761  			expected: []*endpoint.Endpoint{
   762  				{DNSName: "foo.example.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"1.2.3.4"}},
   763  				{DNSName: "foo.example.org", RecordType: endpoint.RecordTypeCNAME, Targets: endpoint.Targets{"elb.com"}},
   764  			},
   765  		},
   766  		{
   767  			title:         "compatibility annotated services with tmpl. compatibility takes precedence",
   768  			svcNamespace:  "testing",
   769  			svcName:       "foo",
   770  			svcType:       v1.ServiceTypeLoadBalancer,
   771  			compatibility: "mate",
   772  			fqdnTemplate:  "{{.Name}}.bar.example.com",
   773  			labels:        map[string]string{},
   774  			annotations: map[string]string{
   775  				"zalando.org/dnsname": "mate.example.org.",
   776  			},
   777  			externalIPs:        []string{},
   778  			lbs:                []string{"1.2.3.4"},
   779  			serviceTypesFilter: []string{},
   780  			expected: []*endpoint.Endpoint{
   781  				{DNSName: "mate.example.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"1.2.3.4"}},
   782  			},
   783  		},
   784  		{
   785  			title:              "not annotated services with unknown tmpl field should not return anything",
   786  			svcNamespace:       "testing",
   787  			svcName:            "foo",
   788  			svcType:            v1.ServiceTypeLoadBalancer,
   789  			fqdnTemplate:       "{{.Calibre}}.bar.example.com",
   790  			labels:             map[string]string{},
   791  			annotations:        map[string]string{},
   792  			externalIPs:        []string{},
   793  			lbs:                []string{"1.2.3.4"},
   794  			serviceTypesFilter: []string{},
   795  			expected:           []*endpoint.Endpoint{},
   796  			expectError:        true,
   797  		},
   798  		{
   799  			title:        "ttl not annotated should have RecordTTL.IsConfigured set to false",
   800  			svcNamespace: "testing",
   801  			svcName:      "foo",
   802  			svcType:      v1.ServiceTypeLoadBalancer,
   803  			labels:       map[string]string{},
   804  			annotations: map[string]string{
   805  				hostnameAnnotationKey: "foo.example.org.",
   806  			},
   807  			externalIPs:        []string{},
   808  			lbs:                []string{"1.2.3.4"},
   809  			serviceTypesFilter: []string{},
   810  			expected: []*endpoint.Endpoint{
   811  				{DNSName: "foo.example.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"1.2.3.4"}, RecordTTL: endpoint.TTL(0)},
   812  			},
   813  		},
   814  		{
   815  			title:        "ttl annotated but invalid should have RecordTTL.IsConfigured set to false",
   816  			svcNamespace: "testing",
   817  			svcName:      "foo",
   818  			svcType:      v1.ServiceTypeLoadBalancer,
   819  			labels:       map[string]string{},
   820  			annotations: map[string]string{
   821  				hostnameAnnotationKey: "foo.example.org.",
   822  				ttlAnnotationKey:      "foo",
   823  			},
   824  			externalIPs:        []string{},
   825  			lbs:                []string{"1.2.3.4"},
   826  			serviceTypesFilter: []string{},
   827  			expected: []*endpoint.Endpoint{
   828  				{DNSName: "foo.example.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"1.2.3.4"}, RecordTTL: endpoint.TTL(0)},
   829  			},
   830  		},
   831  		{
   832  			title:        "ttl annotated and is valid should set Record.TTL",
   833  			svcNamespace: "testing",
   834  			svcName:      "foo",
   835  			svcType:      v1.ServiceTypeLoadBalancer,
   836  			labels:       map[string]string{},
   837  			annotations: map[string]string{
   838  				hostnameAnnotationKey: "foo.example.org.",
   839  				ttlAnnotationKey:      "10",
   840  			},
   841  			externalIPs:        []string{},
   842  			lbs:                []string{"1.2.3.4"},
   843  			serviceTypesFilter: []string{},
   844  			expected: []*endpoint.Endpoint{
   845  				{DNSName: "foo.example.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"1.2.3.4"}, RecordTTL: endpoint.TTL(10)},
   846  			},
   847  		},
   848  		{
   849  			title:        "ttl annotated (in duration format) and is valid should set Record.TTL",
   850  			svcNamespace: "testing",
   851  			svcName:      "foo",
   852  			svcType:      v1.ServiceTypeLoadBalancer,
   853  			labels:       map[string]string{},
   854  			annotations: map[string]string{
   855  				hostnameAnnotationKey: "foo.example.org.",
   856  				ttlAnnotationKey:      "1m",
   857  			},
   858  			externalIPs:        []string{},
   859  			lbs:                []string{"1.2.3.4"},
   860  			serviceTypesFilter: []string{},
   861  			expected: []*endpoint.Endpoint{
   862  				{DNSName: "foo.example.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"1.2.3.4"}, RecordTTL: endpoint.TTL(60)},
   863  			},
   864  		},
   865  		{
   866  			title:        "Negative ttl is not valid",
   867  			svcNamespace: "testing",
   868  			svcName:      "foo",
   869  			svcType:      v1.ServiceTypeLoadBalancer,
   870  			labels:       map[string]string{},
   871  			annotations: map[string]string{
   872  				hostnameAnnotationKey: "foo.example.org.",
   873  				ttlAnnotationKey:      "-10",
   874  			},
   875  			externalIPs:        []string{},
   876  			lbs:                []string{"1.2.3.4"},
   877  			serviceTypesFilter: []string{},
   878  			expected: []*endpoint.Endpoint{
   879  				{DNSName: "foo.example.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"1.2.3.4"}, RecordTTL: endpoint.TTL(0)},
   880  			},
   881  		},
   882  		{
   883  			title:        "filter on service types should include matching services",
   884  			svcNamespace: "testing",
   885  			svcName:      "foo",
   886  			svcType:      v1.ServiceTypeLoadBalancer,
   887  			labels:       map[string]string{},
   888  			annotations: map[string]string{
   889  				hostnameAnnotationKey: "foo.example.org.",
   890  			},
   891  			externalIPs:        []string{},
   892  			lbs:                []string{"1.2.3.4"},
   893  			serviceTypesFilter: []string{string(v1.ServiceTypeLoadBalancer)},
   894  			expected: []*endpoint.Endpoint{
   895  				{DNSName: "foo.example.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"1.2.3.4"}},
   896  			},
   897  		},
   898  		{
   899  			title:        "filter on service types should exclude non-matching services",
   900  			svcNamespace: "testing",
   901  			svcName:      "foo",
   902  			svcType:      v1.ServiceTypeNodePort,
   903  			labels:       map[string]string{},
   904  			annotations: map[string]string{
   905  				hostnameAnnotationKey: "foo.example.org.",
   906  			},
   907  			externalIPs:        []string{},
   908  			lbs:                []string{"1.2.3.4"},
   909  			serviceTypesFilter: []string{string(v1.ServiceTypeLoadBalancer)},
   910  			expected:           []*endpoint.Endpoint{},
   911  		},
   912  		{
   913  			title:        "internal-host annotated and host annotated clusterip services return an endpoint with Cluster IP",
   914  			svcNamespace: "testing",
   915  			svcName:      "foo",
   916  			svcType:      v1.ServiceTypeClusterIP,
   917  			labels:       map[string]string{},
   918  			annotations: map[string]string{
   919  				hostnameAnnotationKey:         "foo.example.org.",
   920  				internalHostnameAnnotationKey: "foo.internal.example.org.",
   921  			},
   922  			clusterIP:          "1.1.1.1",
   923  			externalIPs:        []string{},
   924  			lbs:                []string{"1.2.3.4"},
   925  			serviceTypesFilter: []string{},
   926  			expected: []*endpoint.Endpoint{
   927  				{DNSName: "foo.internal.example.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"1.1.1.1"}},
   928  			},
   929  		},
   930  		{
   931  			title:        "internal-host annotated loadbalancer services return an endpoint with Cluster IP",
   932  			svcNamespace: "testing",
   933  			svcName:      "foo",
   934  			svcType:      v1.ServiceTypeLoadBalancer,
   935  			labels:       map[string]string{},
   936  			annotations: map[string]string{
   937  				internalHostnameAnnotationKey: "foo.internal.example.org.",
   938  			},
   939  			clusterIP:          "1.1.1.1",
   940  			externalIPs:        []string{},
   941  			lbs:                []string{"1.2.3.4"},
   942  			serviceTypesFilter: []string{},
   943  			expected: []*endpoint.Endpoint{
   944  				{DNSName: "foo.internal.example.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"1.1.1.1"}},
   945  			},
   946  		},
   947  		{
   948  			title:        "internal-host annotated and host annotated loadbalancer services return an endpoint with Cluster IP and an endpoint with lb IP",
   949  			svcNamespace: "testing",
   950  			svcName:      "foo",
   951  			svcType:      v1.ServiceTypeLoadBalancer,
   952  			labels:       map[string]string{},
   953  			annotations: map[string]string{
   954  				hostnameAnnotationKey:         "foo.example.org.",
   955  				internalHostnameAnnotationKey: "foo.internal.example.org.",
   956  			},
   957  			clusterIP:          "1.1.1.1",
   958  			externalIPs:        []string{},
   959  			lbs:                []string{"1.2.3.4"},
   960  			serviceTypesFilter: []string{},
   961  			expected: []*endpoint.Endpoint{
   962  				{DNSName: "foo.internal.example.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"1.1.1.1"}},
   963  				{DNSName: "foo.example.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"1.2.3.4"}},
   964  			},
   965  		},
   966  		{
   967  			title:        "service with matching labels and fqdn filter should be included",
   968  			svcNamespace: "testing",
   969  			svcName:      "fqdn",
   970  			svcType:      v1.ServiceTypeLoadBalancer,
   971  			labels: map[string]string{
   972  				"app": "web-external",
   973  			},
   974  			clusterIP:            "1.1.1.1",
   975  			externalIPs:          []string{},
   976  			lbs:                  []string{"1.2.3.4"},
   977  			serviceTypesFilter:   []string{},
   978  			serviceLabelSelector: "app=web-external",
   979  			fqdnTemplate:         "{{.Name}}.bar.example.com",
   980  			expected: []*endpoint.Endpoint{
   981  				{DNSName: "fqdn.bar.example.com", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"1.2.3.4"}},
   982  			},
   983  		},
   984  		{
   985  			title:        "service with matching labels and hostname annotation should be included",
   986  			svcNamespace: "testing",
   987  			svcName:      "foo",
   988  			svcType:      v1.ServiceTypeLoadBalancer,
   989  			labels: map[string]string{
   990  				"app": "web-external",
   991  			},
   992  			clusterIP:            "1.1.1.1",
   993  			externalIPs:          []string{},
   994  			lbs:                  []string{"1.2.3.4"},
   995  			serviceTypesFilter:   []string{},
   996  			serviceLabelSelector: "app=web-external",
   997  			annotations:          map[string]string{hostnameAnnotationKey: "annotation.bar.example.com"},
   998  			expected: []*endpoint.Endpoint{
   999  				{DNSName: "annotation.bar.example.com", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"1.2.3.4"}},
  1000  			},
  1001  		},
  1002  		{
  1003  			title:        "service without matching labels and fqdn filter should be excluded",
  1004  			svcNamespace: "testing",
  1005  			svcName:      "fqdn",
  1006  			svcType:      v1.ServiceTypeLoadBalancer,
  1007  			labels: map[string]string{
  1008  				"app": "web-internal",
  1009  			},
  1010  			clusterIP:            "1.1.1.1",
  1011  			externalIPs:          []string{},
  1012  			lbs:                  []string{"1.2.3.4"},
  1013  			serviceTypesFilter:   []string{},
  1014  			serviceLabelSelector: "app=web-external",
  1015  			fqdnTemplate:         "{{.Name}}.bar.example.com",
  1016  			expected:             []*endpoint.Endpoint{},
  1017  		},
  1018  		{
  1019  			title:        "service without matching labels and hostname annotation should be excluded",
  1020  			svcNamespace: "testing",
  1021  			svcName:      "foo",
  1022  			svcType:      v1.ServiceTypeLoadBalancer,
  1023  			labels: map[string]string{
  1024  				"app": "web-internal",
  1025  			},
  1026  			clusterIP:            "1.1.1.1",
  1027  			externalIPs:          []string{},
  1028  			lbs:                  []string{"1.2.3.4"},
  1029  			serviceTypesFilter:   []string{},
  1030  			serviceLabelSelector: "app=web-external",
  1031  			annotations:          map[string]string{hostnameAnnotationKey: "annotation.bar.example.com"},
  1032  			expected:             []*endpoint.Endpoint{},
  1033  		},
  1034  		{
  1035  			title:              "dual-stack load-balancer service gets both addresses",
  1036  			svcNamespace:       "testing",
  1037  			svcName:            "foobar",
  1038  			svcType:            v1.ServiceTypeLoadBalancer,
  1039  			labels:             map[string]string{},
  1040  			clusterIP:          "1.1.1.2,2001:db8::2",
  1041  			externalIPs:        []string{},
  1042  			lbs:                []string{"1.1.1.1", "2001:db8::1"},
  1043  			serviceTypesFilter: []string{},
  1044  			annotations:        map[string]string{hostnameAnnotationKey: "foobar.example.org"},
  1045  			expected: []*endpoint.Endpoint{
  1046  				{DNSName: "foobar.example.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"1.1.1.1"}},
  1047  				{DNSName: "foobar.example.org", RecordType: endpoint.RecordTypeAAAA, Targets: endpoint.Targets{"2001:db8::1"}},
  1048  			},
  1049  		},
  1050  		{
  1051  			title:              "IPv6-only load-balancer service gets IPv6 endpoint",
  1052  			svcNamespace:       "testing",
  1053  			svcName:            "foobar-v6",
  1054  			svcType:            v1.ServiceTypeLoadBalancer,
  1055  			labels:             map[string]string{},
  1056  			clusterIP:          "2001:db8::1",
  1057  			externalIPs:        []string{},
  1058  			lbs:                []string{"2001:db8::2"},
  1059  			serviceTypesFilter: []string{},
  1060  			annotations:        map[string]string{hostnameAnnotationKey: "foobar-v6.example.org"},
  1061  			expected: []*endpoint.Endpoint{
  1062  				{DNSName: "foobar-v6.example.org", RecordType: endpoint.RecordTypeAAAA, Targets: endpoint.Targets{"2001:db8::2"}},
  1063  			},
  1064  		},
  1065  	} {
  1066  		tc := tc
  1067  		t.Run(tc.title, func(t *testing.T) {
  1068  			t.Parallel()
  1069  
  1070  			// Create a Kubernetes testing client
  1071  			kubernetes := fake.NewSimpleClientset()
  1072  
  1073  			// Create a service to test against
  1074  			ingresses := []v1.LoadBalancerIngress{}
  1075  			for _, lb := range tc.lbs {
  1076  				if net.ParseIP(lb) != nil {
  1077  					ingresses = append(ingresses, v1.LoadBalancerIngress{IP: lb})
  1078  				} else {
  1079  					ingresses = append(ingresses, v1.LoadBalancerIngress{Hostname: lb})
  1080  				}
  1081  			}
  1082  
  1083  			service := &v1.Service{
  1084  				Spec: v1.ServiceSpec{
  1085  					Type:        tc.svcType,
  1086  					ClusterIP:   tc.clusterIP,
  1087  					ExternalIPs: tc.externalIPs,
  1088  				},
  1089  				ObjectMeta: metav1.ObjectMeta{
  1090  					Namespace:   tc.svcNamespace,
  1091  					Name:        tc.svcName,
  1092  					Labels:      tc.labels,
  1093  					Annotations: tc.annotations,
  1094  				},
  1095  				Status: v1.ServiceStatus{
  1096  					LoadBalancer: v1.LoadBalancerStatus{
  1097  						Ingress: ingresses,
  1098  					},
  1099  				},
  1100  			}
  1101  
  1102  			_, err := kubernetes.CoreV1().Services(service.Namespace).Create(context.Background(), service, metav1.CreateOptions{})
  1103  			require.NoError(t, err)
  1104  
  1105  			var sourceLabel labels.Selector
  1106  			if tc.serviceLabelSelector != "" {
  1107  				sourceLabel, err = labels.Parse(tc.serviceLabelSelector)
  1108  				require.NoError(t, err)
  1109  			} else {
  1110  				sourceLabel = labels.Everything()
  1111  			}
  1112  
  1113  			// Create our object under test and get the endpoints.
  1114  			client, err := NewServiceSource(
  1115  				context.TODO(),
  1116  				kubernetes,
  1117  				tc.targetNamespace,
  1118  				tc.annotationFilter,
  1119  				tc.fqdnTemplate,
  1120  				tc.combineFQDNAndAnnotation,
  1121  				tc.compatibility,
  1122  				false,
  1123  				false,
  1124  				false,
  1125  				tc.serviceTypesFilter,
  1126  				tc.ignoreHostnameAnnotation,
  1127  				sourceLabel,
  1128  				tc.resolveLoadBalancerHostname,
  1129  			)
  1130  
  1131  			require.NoError(t, err)
  1132  
  1133  			res, err := client.Endpoints(context.Background())
  1134  			if tc.expectError {
  1135  				require.Error(t, err)
  1136  			} else {
  1137  				require.NoError(t, err)
  1138  			}
  1139  
  1140  			// Validate returned endpoints against desired endpoints.
  1141  			validateEndpoints(t, res, tc.expected)
  1142  		})
  1143  	}
  1144  }
  1145  
  1146  // testMultipleServicesEndpoints tests that multiple services generate correct merged endpoints
  1147  func testMultipleServicesEndpoints(t *testing.T) {
  1148  	t.Parallel()
  1149  
  1150  	for _, tc := range []struct {
  1151  		title                    string
  1152  		targetNamespace          string
  1153  		annotationFilter         string
  1154  		svcNamespace             string
  1155  		svcName                  string
  1156  		svcType                  v1.ServiceType
  1157  		compatibility            string
  1158  		fqdnTemplate             string
  1159  		combineFQDNAndAnnotation bool
  1160  		ignoreHostnameAnnotation bool
  1161  		labels                   map[string]string
  1162  		clusterIP                string
  1163  		services                 map[string]map[string]string
  1164  		serviceTypesFilter       []string
  1165  		expected                 []*endpoint.Endpoint
  1166  		expectError              bool
  1167  	}{
  1168  		{
  1169  			"test service returns a correct end point",
  1170  			"",
  1171  			"",
  1172  			"testing",
  1173  			"foo",
  1174  			v1.ServiceTypeLoadBalancer,
  1175  			"",
  1176  			"",
  1177  			false,
  1178  			false,
  1179  			map[string]string{},
  1180  			"",
  1181  			map[string]map[string]string{
  1182  				"1.2.3.4": {hostnameAnnotationKey: "foo.example.org"},
  1183  			},
  1184  			[]string{},
  1185  			[]*endpoint.Endpoint{
  1186  				{DNSName: "foo.example.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"1.2.3.4"}, Labels: map[string]string{endpoint.ResourceLabelKey: "service/testing/foo1.2.3.4"}},
  1187  			},
  1188  			false,
  1189  		},
  1190  		{
  1191  			"multiple services that share same DNS should be merged into one endpoint",
  1192  			"",
  1193  			"",
  1194  			"testing",
  1195  			"foo",
  1196  			v1.ServiceTypeLoadBalancer,
  1197  			"",
  1198  			"",
  1199  			false,
  1200  			false,
  1201  			map[string]string{},
  1202  			"",
  1203  			map[string]map[string]string{
  1204  				"1.2.3.4": {hostnameAnnotationKey: "foo.example.org"},
  1205  				"1.2.3.5": {hostnameAnnotationKey: "foo.example.org"},
  1206  				"1.2.3.6": {hostnameAnnotationKey: "foo.example.org"},
  1207  			},
  1208  			[]string{},
  1209  			[]*endpoint.Endpoint{
  1210  				{DNSName: "foo.example.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"1.2.3.4", "1.2.3.5", "1.2.3.6"}, Labels: map[string]string{endpoint.ResourceLabelKey: "service/testing/foo1.2.3.4"}},
  1211  			},
  1212  			false,
  1213  		},
  1214  		{
  1215  			"test that services with different hostnames do not get merged together",
  1216  			"",
  1217  			"",
  1218  			"testing",
  1219  			"foo",
  1220  			v1.ServiceTypeLoadBalancer,
  1221  			"",
  1222  			"",
  1223  			false,
  1224  			false,
  1225  			map[string]string{},
  1226  			"",
  1227  			map[string]map[string]string{
  1228  				"1.2.3.5":  {hostnameAnnotationKey: "foo.example.org"},
  1229  				"10.1.1.3": {hostnameAnnotationKey: "bar.example.org"},
  1230  				"10.1.1.1": {hostnameAnnotationKey: "bar.example.org"},
  1231  				"1.2.3.4":  {hostnameAnnotationKey: "foo.example.org"},
  1232  				"10.1.1.2": {hostnameAnnotationKey: "bar.example.org"},
  1233  				"20.1.1.1": {hostnameAnnotationKey: "foobar.example.org"},
  1234  				"1.2.3.6":  {hostnameAnnotationKey: "foo.example.org"},
  1235  			},
  1236  			[]string{},
  1237  			[]*endpoint.Endpoint{
  1238  				{DNSName: "foo.example.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"1.2.3.4", "1.2.3.5", "1.2.3.6"}, Labels: map[string]string{endpoint.ResourceLabelKey: "service/testing/foo1.2.3.4"}},
  1239  				{DNSName: "bar.example.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"10.1.1.1", "10.1.1.2", "10.1.1.3"}, Labels: map[string]string{endpoint.ResourceLabelKey: "service/testing/foo10.1.1.1"}},
  1240  				{DNSName: "foobar.example.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"20.1.1.1"}, Labels: map[string]string{endpoint.ResourceLabelKey: "service/testing/foo20.1.1.1"}},
  1241  			},
  1242  			false,
  1243  		},
  1244  		{
  1245  			"test that services with different set-identifier do not get merged together",
  1246  			"",
  1247  			"",
  1248  			"testing",
  1249  			"foo",
  1250  			v1.ServiceTypeLoadBalancer,
  1251  			"",
  1252  			"",
  1253  			false,
  1254  			false,
  1255  			map[string]string{},
  1256  			"",
  1257  			map[string]map[string]string{
  1258  				"a.elb.com": {hostnameAnnotationKey: "foo.example.org", SetIdentifierKey: "a"},
  1259  				"b.elb.com": {hostnameAnnotationKey: "foo.example.org", SetIdentifierKey: "b"},
  1260  			},
  1261  			[]string{},
  1262  			[]*endpoint.Endpoint{
  1263  				{DNSName: "foo.example.org", RecordType: endpoint.RecordTypeCNAME, Targets: endpoint.Targets{"a.elb.com"}, Labels: map[string]string{endpoint.ResourceLabelKey: "service/testing/fooa.elb.com"}, SetIdentifier: "a"},
  1264  				{DNSName: "foo.example.org", RecordType: endpoint.RecordTypeCNAME, Targets: endpoint.Targets{"b.elb.com"}, Labels: map[string]string{endpoint.ResourceLabelKey: "service/testing/foob.elb.com"}, SetIdentifier: "b"},
  1265  			},
  1266  			false,
  1267  		},
  1268  	} {
  1269  		tc := tc
  1270  		t.Run(tc.title, func(t *testing.T) {
  1271  			t.Parallel()
  1272  
  1273  			// Create a Kubernetes testing client
  1274  			kubernetes := fake.NewSimpleClientset()
  1275  
  1276  			// Create services to test against
  1277  			for lb, annotations := range tc.services {
  1278  				ingresses := []v1.LoadBalancerIngress{}
  1279  				ingresses = append(ingresses, v1.LoadBalancerIngress{IP: lb})
  1280  
  1281  				service := &v1.Service{
  1282  					Spec: v1.ServiceSpec{
  1283  						Type:      tc.svcType,
  1284  						ClusterIP: tc.clusterIP,
  1285  					},
  1286  					ObjectMeta: metav1.ObjectMeta{
  1287  						Namespace:   tc.svcNamespace,
  1288  						Name:        tc.svcName + lb,
  1289  						Labels:      tc.labels,
  1290  						Annotations: annotations,
  1291  					},
  1292  					Status: v1.ServiceStatus{
  1293  						LoadBalancer: v1.LoadBalancerStatus{
  1294  							Ingress: ingresses,
  1295  						},
  1296  					},
  1297  				}
  1298  
  1299  				_, err := kubernetes.CoreV1().Services(service.Namespace).Create(context.Background(), service, metav1.CreateOptions{})
  1300  				require.NoError(t, err)
  1301  			}
  1302  
  1303  			// Create our object under test and get the endpoints.
  1304  			client, err := NewServiceSource(
  1305  				context.TODO(),
  1306  				kubernetes,
  1307  				tc.targetNamespace,
  1308  				tc.annotationFilter,
  1309  				tc.fqdnTemplate,
  1310  				tc.combineFQDNAndAnnotation,
  1311  				tc.compatibility,
  1312  				false,
  1313  				false,
  1314  				false,
  1315  				tc.serviceTypesFilter,
  1316  				tc.ignoreHostnameAnnotation,
  1317  				labels.Everything(),
  1318  				false,
  1319  			)
  1320  			require.NoError(t, err)
  1321  
  1322  			res, err := client.Endpoints(context.Background())
  1323  			if tc.expectError {
  1324  				require.Error(t, err)
  1325  			} else {
  1326  				require.NoError(t, err)
  1327  			}
  1328  
  1329  			// Validate returned endpoints against desired endpoints.
  1330  			validateEndpoints(t, res, tc.expected)
  1331  			// Test that endpoint resourceLabelKey matches desired endpoint
  1332  			sort.SliceStable(res, func(i, j int) bool {
  1333  				return strings.Compare(res[i].DNSName, res[j].DNSName) < 0
  1334  			})
  1335  			sort.SliceStable(tc.expected, func(i, j int) bool {
  1336  				return strings.Compare(tc.expected[i].DNSName, tc.expected[j].DNSName) < 0
  1337  			})
  1338  
  1339  			for i := range res {
  1340  				if res[i].Labels[endpoint.ResourceLabelKey] != tc.expected[i].Labels[endpoint.ResourceLabelKey] {
  1341  					t.Errorf("expected %s, got %s", tc.expected[i].Labels[endpoint.ResourceLabelKey], res[i].Labels[endpoint.ResourceLabelKey])
  1342  				}
  1343  			}
  1344  		})
  1345  	}
  1346  }
  1347  
  1348  // testServiceSourceEndpoints tests that various services generate the correct endpoints.
  1349  func TestClusterIpServices(t *testing.T) {
  1350  	t.Parallel()
  1351  
  1352  	for _, tc := range []struct {
  1353  		title                    string
  1354  		targetNamespace          string
  1355  		annotationFilter         string
  1356  		svcNamespace             string
  1357  		svcName                  string
  1358  		svcType                  v1.ServiceType
  1359  		compatibility            string
  1360  		fqdnTemplate             string
  1361  		ignoreHostnameAnnotation bool
  1362  		labels                   map[string]string
  1363  		annotations              map[string]string
  1364  		clusterIP                string
  1365  		expected                 []*endpoint.Endpoint
  1366  		expectError              bool
  1367  		labelSelector            string
  1368  	}{
  1369  		{
  1370  			title:        "hostname annotated ClusterIp services return an endpoint with Cluster IP",
  1371  			svcNamespace: "testing",
  1372  			svcName:      "foo",
  1373  			svcType:      v1.ServiceTypeClusterIP,
  1374  			annotations: map[string]string{
  1375  				hostnameAnnotationKey: "foo.example.org.",
  1376  			},
  1377  			clusterIP: "1.2.3.4",
  1378  			expected: []*endpoint.Endpoint{
  1379  				{DNSName: "foo.example.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"1.2.3.4"}},
  1380  			},
  1381  		},
  1382  		{
  1383  			title:        "target annotated ClusterIp services return an endpoint with the specified A",
  1384  			svcNamespace: "testing",
  1385  			svcName:      "foo",
  1386  			svcType:      v1.ServiceTypeClusterIP,
  1387  			annotations: map[string]string{
  1388  				hostnameAnnotationKey: "foo.example.org.",
  1389  				targetAnnotationKey:   "4.3.2.1",
  1390  			},
  1391  			clusterIP: "1.2.3.4",
  1392  			expected: []*endpoint.Endpoint{
  1393  				{DNSName: "foo.example.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"4.3.2.1"}},
  1394  			},
  1395  		},
  1396  		{
  1397  			title:        "target annotated ClusterIp services return an endpoint with the specified CNAME",
  1398  			svcNamespace: "testing",
  1399  			svcName:      "foo",
  1400  			svcType:      v1.ServiceTypeClusterIP,
  1401  			annotations: map[string]string{
  1402  				hostnameAnnotationKey: "foo.example.org.",
  1403  				targetAnnotationKey:   "bar.example.org.",
  1404  			},
  1405  			clusterIP: "1.2.3.4",
  1406  			expected: []*endpoint.Endpoint{
  1407  				{DNSName: "foo.example.org", RecordType: endpoint.RecordTypeCNAME, Targets: endpoint.Targets{"bar.example.org"}},
  1408  			},
  1409  		},
  1410  		{
  1411  			title:        "target annotated ClusterIp services return an endpoint with the specified AAAA",
  1412  			svcNamespace: "testing",
  1413  			svcName:      "foo",
  1414  			svcType:      v1.ServiceTypeClusterIP,
  1415  			annotations: map[string]string{
  1416  				hostnameAnnotationKey: "foo.example.org.",
  1417  				targetAnnotationKey:   "2001:DB8::1",
  1418  			},
  1419  			clusterIP: "1.2.3.4",
  1420  			expected: []*endpoint.Endpoint{
  1421  				{DNSName: "foo.example.org", RecordType: endpoint.RecordTypeAAAA, Targets: endpoint.Targets{"2001:DB8::1"}},
  1422  			},
  1423  		},
  1424  		{
  1425  			title:        "multiple target annotated ClusterIp services return an endpoint with the specified CNAMES",
  1426  			svcNamespace: "testing",
  1427  			svcName:      "foo",
  1428  			svcType:      v1.ServiceTypeClusterIP,
  1429  			annotations: map[string]string{
  1430  				hostnameAnnotationKey: "foo.example.org.",
  1431  				targetAnnotationKey:   "bar.example.org.,baz.example.org.",
  1432  			},
  1433  			clusterIP: "1.2.3.4",
  1434  			expected: []*endpoint.Endpoint{
  1435  				{DNSName: "foo.example.org", RecordType: endpoint.RecordTypeCNAME, Targets: endpoint.Targets{"bar.example.org", "baz.example.org"}},
  1436  			},
  1437  		},
  1438  		{
  1439  			title:        "multiple target annotated ClusterIp services return two endpoints with the specified CNAMES and AAAA",
  1440  			svcNamespace: "testing",
  1441  			svcName:      "foo",
  1442  			svcType:      v1.ServiceTypeClusterIP,
  1443  			annotations: map[string]string{
  1444  				hostnameAnnotationKey: "foo.example.org.",
  1445  				targetAnnotationKey:   "bar.example.org.,baz.example.org.,2001:DB8::1",
  1446  			},
  1447  			clusterIP: "1.2.3.4",
  1448  			expected: []*endpoint.Endpoint{
  1449  				{DNSName: "foo.example.org", RecordType: endpoint.RecordTypeCNAME, Targets: endpoint.Targets{"bar.example.org", "baz.example.org"}},
  1450  				{DNSName: "foo.example.org", RecordType: endpoint.RecordTypeAAAA, Targets: endpoint.Targets{"2001:DB8::1"}},
  1451  			},
  1452  		},
  1453  		{
  1454  			title:                    "hostname annotated ClusterIp services are ignored",
  1455  			svcNamespace:             "testing",
  1456  			svcName:                  "foo",
  1457  			svcType:                  v1.ServiceTypeClusterIP,
  1458  			ignoreHostnameAnnotation: true,
  1459  			annotations: map[string]string{
  1460  				hostnameAnnotationKey: "foo.example.org.",
  1461  			},
  1462  			clusterIP: "1.2.3.4",
  1463  			expected:  []*endpoint.Endpoint{},
  1464  		},
  1465  		{
  1466  			title:                    "hostname and target annotated ClusterIp services are ignored",
  1467  			svcNamespace:             "testing",
  1468  			svcName:                  "foo",
  1469  			svcType:                  v1.ServiceTypeClusterIP,
  1470  			ignoreHostnameAnnotation: true,
  1471  			annotations: map[string]string{
  1472  				hostnameAnnotationKey: "foo.example.org.",
  1473  				targetAnnotationKey:   "bar.example.org.",
  1474  			},
  1475  			clusterIP: "1.2.3.4",
  1476  			expected:  []*endpoint.Endpoint{},
  1477  		},
  1478  		{
  1479  			title:        "hostname and target annotated ClusterIp services return an endpoint with the specified CNAME",
  1480  			svcNamespace: "testing",
  1481  			svcName:      "foo",
  1482  			svcType:      v1.ServiceTypeClusterIP,
  1483  			annotations: map[string]string{
  1484  				hostnameAnnotationKey: "foo.example.org.",
  1485  				targetAnnotationKey:   "bar.example.org.",
  1486  			},
  1487  			clusterIP: "1.2.3.4",
  1488  			expected: []*endpoint.Endpoint{
  1489  				{DNSName: "foo.example.org", RecordType: endpoint.RecordTypeCNAME, Targets: endpoint.Targets{"bar.example.org"}},
  1490  			},
  1491  		},
  1492  		{
  1493  			title:        "non-annotated ClusterIp services with set fqdnTemplate return an endpoint with target IP",
  1494  			svcNamespace: "testing",
  1495  			svcName:      "foo",
  1496  			svcType:      v1.ServiceTypeClusterIP,
  1497  			fqdnTemplate: "{{.Name}}.bar.example.com",
  1498  			clusterIP:    "4.5.6.7",
  1499  			expected: []*endpoint.Endpoint{
  1500  				{DNSName: "foo.bar.example.com", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"4.5.6.7"}},
  1501  			},
  1502  		},
  1503  		{
  1504  			title:        "Headless services do not generate endpoints",
  1505  			svcNamespace: "testing",
  1506  			svcName:      "foo",
  1507  			svcType:      v1.ServiceTypeClusterIP,
  1508  			clusterIP:    v1.ClusterIPNone,
  1509  			expected:     []*endpoint.Endpoint{},
  1510  		},
  1511  		{
  1512  			title:        "Headless services generate endpoints when target is specified",
  1513  			svcNamespace: "testing",
  1514  			svcName:      "foo",
  1515  			svcType:      v1.ServiceTypeClusterIP,
  1516  			annotations: map[string]string{
  1517  				hostnameAnnotationKey: "foo.example.org.",
  1518  				targetAnnotationKey:   "bar.example.org.",
  1519  			},
  1520  			clusterIP: v1.ClusterIPNone,
  1521  			expected: []*endpoint.Endpoint{
  1522  				{DNSName: "foo.example.org", RecordType: endpoint.RecordTypeCNAME, Targets: endpoint.Targets{"bar.example.org"}},
  1523  			},
  1524  		},
  1525  		{
  1526  			title:        "ClusterIP service with matching label generates an endpoint",
  1527  			svcNamespace: "testing",
  1528  			svcName:      "foo",
  1529  			svcType:      v1.ServiceTypeClusterIP,
  1530  			fqdnTemplate: "{{.Name}}.bar.example.com",
  1531  			labels:       map[string]string{"app": "web-internal"},
  1532  			clusterIP:    "4.5.6.7",
  1533  			expected: []*endpoint.Endpoint{
  1534  				{DNSName: "foo.bar.example.com", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"4.5.6.7"}},
  1535  			},
  1536  			labelSelector: "app=web-internal",
  1537  		},
  1538  		{
  1539  			title:        "ClusterIP service with matching label and target generates a CNAME endpoint",
  1540  			svcNamespace: "testing",
  1541  			svcName:      "foo",
  1542  			svcType:      v1.ServiceTypeClusterIP,
  1543  			fqdnTemplate: "{{.Name}}.bar.example.com",
  1544  			labels:       map[string]string{"app": "web-internal"},
  1545  			annotations:  map[string]string{targetAnnotationKey: "bar.example.com."},
  1546  			clusterIP:    "4.5.6.7",
  1547  			expected: []*endpoint.Endpoint{
  1548  				{DNSName: "foo.bar.example.com", RecordType: endpoint.RecordTypeCNAME, Targets: endpoint.Targets{"bar.example.com"}},
  1549  			},
  1550  			labelSelector: "app=web-internal",
  1551  		},
  1552  		{
  1553  			title:         "ClusterIP service without matching label generates an endpoint",
  1554  			svcNamespace:  "testing",
  1555  			svcName:       "foo",
  1556  			svcType:       v1.ServiceTypeClusterIP,
  1557  			fqdnTemplate:  "{{.Name}}.bar.example.com",
  1558  			labels:        map[string]string{"app": "web-internal"},
  1559  			clusterIP:     "4.5.6.7",
  1560  			expected:      []*endpoint.Endpoint{},
  1561  			labelSelector: "app=web-external",
  1562  		},
  1563  		{
  1564  			title:        "invalid hostname does not generate endpoints",
  1565  			svcNamespace: "testing",
  1566  			svcName:      "foo",
  1567  			svcType:      v1.ServiceTypeClusterIP,
  1568  			annotations: map[string]string{
  1569  				hostnameAnnotationKey: "this-is-an-exceedingly-long-label-that-external-dns-should-reject.example.org.",
  1570  			},
  1571  			clusterIP: "1.2.3.4",
  1572  			expected:  []*endpoint.Endpoint{},
  1573  		},
  1574  	} {
  1575  		tc := tc
  1576  		t.Run(tc.title, func(t *testing.T) {
  1577  			t.Parallel()
  1578  
  1579  			// Create a Kubernetes testing client
  1580  			kubernetes := fake.NewSimpleClientset()
  1581  
  1582  			// Create a service to test against
  1583  			service := &v1.Service{
  1584  				Spec: v1.ServiceSpec{
  1585  					Type:      tc.svcType,
  1586  					ClusterIP: tc.clusterIP,
  1587  				},
  1588  				ObjectMeta: metav1.ObjectMeta{
  1589  					Namespace:   tc.svcNamespace,
  1590  					Name:        tc.svcName,
  1591  					Labels:      tc.labels,
  1592  					Annotations: tc.annotations,
  1593  				},
  1594  			}
  1595  
  1596  			_, err := kubernetes.CoreV1().Services(service.Namespace).Create(context.Background(), service, metav1.CreateOptions{})
  1597  			require.NoError(t, err)
  1598  
  1599  			var labelSelector labels.Selector
  1600  			if tc.labelSelector != "" {
  1601  				labelSelector, err = labels.Parse(tc.labelSelector)
  1602  				require.NoError(t, err)
  1603  			} else {
  1604  				labelSelector = labels.Everything()
  1605  			}
  1606  			// Create our object under test and get the endpoints.
  1607  			client, _ := NewServiceSource(
  1608  				context.TODO(),
  1609  				kubernetes,
  1610  				tc.targetNamespace,
  1611  				tc.annotationFilter,
  1612  				tc.fqdnTemplate,
  1613  				false,
  1614  				tc.compatibility,
  1615  				true,
  1616  				false,
  1617  				false,
  1618  				[]string{},
  1619  				tc.ignoreHostnameAnnotation,
  1620  				labelSelector,
  1621  				false,
  1622  			)
  1623  			require.NoError(t, err)
  1624  
  1625  			endpoints, err := client.Endpoints(context.Background())
  1626  			if tc.expectError {
  1627  				require.Error(t, err)
  1628  			} else {
  1629  				require.NoError(t, err)
  1630  			}
  1631  
  1632  			// Validate returned endpoints against desired endpoints.
  1633  			validateEndpoints(t, endpoints, tc.expected)
  1634  		})
  1635  	}
  1636  }
  1637  
  1638  // testNodePortServices tests that various services generate the correct endpoints.
  1639  func TestServiceSourceNodePortServices(t *testing.T) {
  1640  	t.Parallel()
  1641  
  1642  	for _, tc := range []struct {
  1643  		title                    string
  1644  		targetNamespace          string
  1645  		annotationFilter         string
  1646  		svcNamespace             string
  1647  		svcName                  string
  1648  		svcType                  v1.ServiceType
  1649  		svcTrafficPolicy         v1.ServiceExternalTrafficPolicyType
  1650  		compatibility            string
  1651  		fqdnTemplate             string
  1652  		ignoreHostnameAnnotation bool
  1653  		labels                   map[string]string
  1654  		annotations              map[string]string
  1655  		lbs                      []string
  1656  		expected                 []*endpoint.Endpoint
  1657  		expectError              bool
  1658  		nodes                    []*v1.Node
  1659  		podNames                 []string
  1660  		nodeIndex                []int
  1661  		phases                   []v1.PodPhase
  1662  		conditions               []v1.PodCondition
  1663  		labelSelector            labels.Selector
  1664  		deletionTimestamp        []*metav1.Time
  1665  	}{
  1666  		{
  1667  			title:            "annotated NodePort services return an endpoint with IP addresses of the cluster's nodes",
  1668  			svcNamespace:     "testing",
  1669  			svcName:          "foo",
  1670  			svcType:          v1.ServiceTypeNodePort,
  1671  			svcTrafficPolicy: v1.ServiceExternalTrafficPolicyTypeCluster,
  1672  			annotations: map[string]string{
  1673  				hostnameAnnotationKey: "foo.example.org.",
  1674  			},
  1675  			expected: []*endpoint.Endpoint{
  1676  				{DNSName: "_foo._tcp.foo.example.org", Targets: endpoint.Targets{"0 50 30192 foo.example.org"}, RecordType: endpoint.RecordTypeSRV},
  1677  				{DNSName: "foo.example.org", Targets: endpoint.Targets{"54.10.11.1", "54.10.11.2"}, RecordType: endpoint.RecordTypeA},
  1678  				{DNSName: "foo.example.org", Targets: endpoint.Targets{"2001:DB8::1", "2001:DB8::2"}, RecordType: endpoint.RecordTypeAAAA},
  1679  			},
  1680  			nodes: []*v1.Node{{
  1681  				ObjectMeta: metav1.ObjectMeta{
  1682  					Name: "node1",
  1683  				},
  1684  				Status: v1.NodeStatus{
  1685  					Addresses: []v1.NodeAddress{
  1686  						{Type: v1.NodeExternalIP, Address: "54.10.11.1"},
  1687  						{Type: v1.NodeInternalIP, Address: "10.0.1.1"},
  1688  						{Type: v1.NodeInternalIP, Address: "2001:DB8::1"},
  1689  					},
  1690  				},
  1691  			}, {
  1692  				ObjectMeta: metav1.ObjectMeta{
  1693  					Name: "node2",
  1694  				},
  1695  				Status: v1.NodeStatus{
  1696  					Addresses: []v1.NodeAddress{
  1697  						{Type: v1.NodeExternalIP, Address: "54.10.11.2"},
  1698  						{Type: v1.NodeInternalIP, Address: "10.0.1.2"},
  1699  						{Type: v1.NodeInternalIP, Address: "2001:DB8::2"},
  1700  					},
  1701  				},
  1702  			}},
  1703  		},
  1704  		{
  1705  			title:                    "hostname annotated NodePort services are ignored",
  1706  			svcNamespace:             "testing",
  1707  			svcName:                  "foo",
  1708  			svcType:                  v1.ServiceTypeNodePort,
  1709  			svcTrafficPolicy:         v1.ServiceExternalTrafficPolicyTypeCluster,
  1710  			ignoreHostnameAnnotation: true,
  1711  			annotations: map[string]string{
  1712  				hostnameAnnotationKey: "foo.example.org.",
  1713  			},
  1714  			nodes: []*v1.Node{{
  1715  				ObjectMeta: metav1.ObjectMeta{
  1716  					Name: "node1",
  1717  				},
  1718  				Status: v1.NodeStatus{
  1719  					Addresses: []v1.NodeAddress{
  1720  						{Type: v1.NodeExternalIP, Address: "54.10.11.1"},
  1721  						{Type: v1.NodeInternalIP, Address: "10.0.1.1"},
  1722  						{Type: v1.NodeInternalIP, Address: "2001:DB8::1"},
  1723  					},
  1724  				},
  1725  			}, {
  1726  				ObjectMeta: metav1.ObjectMeta{
  1727  					Name: "node2",
  1728  				},
  1729  				Status: v1.NodeStatus{
  1730  					Addresses: []v1.NodeAddress{
  1731  						{Type: v1.NodeExternalIP, Address: "54.10.11.2"},
  1732  						{Type: v1.NodeInternalIP, Address: "10.0.1.2"},
  1733  						{Type: v1.NodeInternalIP, Address: "2001:DB8::2"},
  1734  					},
  1735  				},
  1736  			}},
  1737  			expected: []*endpoint.Endpoint{},
  1738  		},
  1739  		{
  1740  			title:            "non-annotated NodePort services with set fqdnTemplate return an endpoint with target IP",
  1741  			svcNamespace:     "testing",
  1742  			svcName:          "foo",
  1743  			svcType:          v1.ServiceTypeNodePort,
  1744  			svcTrafficPolicy: v1.ServiceExternalTrafficPolicyTypeCluster,
  1745  			fqdnTemplate:     "{{.Name}}.bar.example.com",
  1746  			expected: []*endpoint.Endpoint{
  1747  				{DNSName: "_foo._tcp.foo.bar.example.com", Targets: endpoint.Targets{"0 50 30192 foo.bar.example.com"}, RecordType: endpoint.RecordTypeSRV},
  1748  				{DNSName: "foo.bar.example.com", Targets: endpoint.Targets{"54.10.11.1", "54.10.11.2"}, RecordType: endpoint.RecordTypeA},
  1749  				{DNSName: "foo.bar.example.com", Targets: endpoint.Targets{"2001:DB8::1", "2001:DB8::2"}, RecordType: endpoint.RecordTypeAAAA},
  1750  			},
  1751  			nodes: []*v1.Node{{
  1752  				ObjectMeta: metav1.ObjectMeta{
  1753  					Name: "node1",
  1754  				},
  1755  				Status: v1.NodeStatus{
  1756  					Addresses: []v1.NodeAddress{
  1757  						{Type: v1.NodeExternalIP, Address: "54.10.11.1"},
  1758  						{Type: v1.NodeInternalIP, Address: "10.0.1.1"},
  1759  						{Type: v1.NodeInternalIP, Address: "2001:DB8::1"},
  1760  					},
  1761  				},
  1762  			}, {
  1763  				ObjectMeta: metav1.ObjectMeta{
  1764  					Name: "node2",
  1765  				},
  1766  				Status: v1.NodeStatus{
  1767  					Addresses: []v1.NodeAddress{
  1768  						{Type: v1.NodeExternalIP, Address: "54.10.11.2"},
  1769  						{Type: v1.NodeInternalIP, Address: "10.0.1.2"},
  1770  						{Type: v1.NodeInternalIP, Address: "2001:DB8::2"},
  1771  					},
  1772  				},
  1773  			}},
  1774  		},
  1775  		{
  1776  			title:            "annotated NodePort services return an endpoint with IP addresses of the private cluster's nodes",
  1777  			svcNamespace:     "testing",
  1778  			svcName:          "foo",
  1779  			svcType:          v1.ServiceTypeNodePort,
  1780  			svcTrafficPolicy: v1.ServiceExternalTrafficPolicyTypeCluster,
  1781  			annotations: map[string]string{
  1782  				hostnameAnnotationKey: "foo.example.org.",
  1783  			},
  1784  			expected: []*endpoint.Endpoint{
  1785  				{DNSName: "_foo._tcp.foo.example.org", Targets: endpoint.Targets{"0 50 30192 foo.example.org"}, RecordType: endpoint.RecordTypeSRV},
  1786  				{DNSName: "foo.example.org", Targets: endpoint.Targets{"10.0.1.1", "10.0.1.2"}, RecordType: endpoint.RecordTypeA},
  1787  				{DNSName: "foo.example.org", Targets: endpoint.Targets{"2001:DB8::1", "2001:DB8::2"}, RecordType: endpoint.RecordTypeAAAA},
  1788  			},
  1789  			nodes: []*v1.Node{{
  1790  				ObjectMeta: metav1.ObjectMeta{
  1791  					Name: "node1",
  1792  				},
  1793  				Status: v1.NodeStatus{
  1794  					Addresses: []v1.NodeAddress{
  1795  						{Type: v1.NodeInternalIP, Address: "10.0.1.1"},
  1796  						{Type: v1.NodeInternalIP, Address: "2001:DB8::1"},
  1797  					},
  1798  				},
  1799  			}, {
  1800  				ObjectMeta: metav1.ObjectMeta{
  1801  					Name: "node2",
  1802  				},
  1803  				Status: v1.NodeStatus{
  1804  					Addresses: []v1.NodeAddress{
  1805  						{Type: v1.NodeInternalIP, Address: "10.0.1.2"},
  1806  						{Type: v1.NodeInternalIP, Address: "2001:DB8::2"},
  1807  					},
  1808  				},
  1809  			}},
  1810  		},
  1811  		{
  1812  			title:            "annotated NodePort services with ExternalTrafficPolicy=Local return an endpoint with IP addresses of the cluster's nodes where pods is running only",
  1813  			svcNamespace:     "testing",
  1814  			svcName:          "foo",
  1815  			svcType:          v1.ServiceTypeNodePort,
  1816  			svcTrafficPolicy: v1.ServiceExternalTrafficPolicyTypeLocal,
  1817  			annotations: map[string]string{
  1818  				hostnameAnnotationKey: "foo.example.org.",
  1819  			},
  1820  			expected: []*endpoint.Endpoint{
  1821  				{DNSName: "_foo._tcp.foo.example.org", Targets: endpoint.Targets{"0 50 30192 foo.example.org"}, RecordType: endpoint.RecordTypeSRV},
  1822  				{DNSName: "foo.example.org", Targets: endpoint.Targets{"54.10.11.2"}, RecordType: endpoint.RecordTypeA},
  1823  				{DNSName: "foo.example.org", Targets: endpoint.Targets{"2001:DB8::2"}, RecordType: endpoint.RecordTypeAAAA},
  1824  			},
  1825  			nodes: []*v1.Node{{
  1826  				ObjectMeta: metav1.ObjectMeta{
  1827  					Name: "node1",
  1828  				},
  1829  				Status: v1.NodeStatus{
  1830  					Addresses: []v1.NodeAddress{
  1831  						{Type: v1.NodeExternalIP, Address: "54.10.11.1"},
  1832  						{Type: v1.NodeInternalIP, Address: "10.0.1.1"},
  1833  						{Type: v1.NodeInternalIP, Address: "2001:DB8::1"},
  1834  					},
  1835  				},
  1836  			}, {
  1837  				ObjectMeta: metav1.ObjectMeta{
  1838  					Name: "node2",
  1839  				},
  1840  				Status: v1.NodeStatus{
  1841  					Addresses: []v1.NodeAddress{
  1842  						{Type: v1.NodeExternalIP, Address: "54.10.11.2"},
  1843  						{Type: v1.NodeInternalIP, Address: "10.0.1.2"},
  1844  						{Type: v1.NodeInternalIP, Address: "2001:DB8::2"},
  1845  					},
  1846  				},
  1847  			}},
  1848  			podNames:          []string{"pod-0"},
  1849  			nodeIndex:         []int{1},
  1850  			phases:            []v1.PodPhase{v1.PodRunning},
  1851  			conditions:        []v1.PodCondition{{Type: v1.PodReady, Status: v1.ConditionFalse}},
  1852  			deletionTimestamp: []*metav1.Time{{}},
  1853  		},
  1854  		{
  1855  			title:            "annotated NodePort services with ExternalTrafficPolicy=Local and multiple pods on a single node return an endpoint with unique IP addresses of the cluster's nodes where pods is running only",
  1856  			svcNamespace:     "testing",
  1857  			svcName:          "foo",
  1858  			svcType:          v1.ServiceTypeNodePort,
  1859  			svcTrafficPolicy: v1.ServiceExternalTrafficPolicyTypeLocal,
  1860  			labels:           map[string]string{},
  1861  			annotations: map[string]string{
  1862  				hostnameAnnotationKey: "foo.example.org.",
  1863  			},
  1864  			expected: []*endpoint.Endpoint{
  1865  				{DNSName: "_foo._tcp.foo.example.org", Targets: endpoint.Targets{"0 50 30192 foo.example.org"}, RecordType: endpoint.RecordTypeSRV},
  1866  				{DNSName: "foo.example.org", Targets: endpoint.Targets{"54.10.11.2"}, RecordType: endpoint.RecordTypeA},
  1867  				{DNSName: "foo.example.org", Targets: endpoint.Targets{"2001:DB8::2"}, RecordType: endpoint.RecordTypeAAAA},
  1868  			},
  1869  			nodes: []*v1.Node{{
  1870  				ObjectMeta: metav1.ObjectMeta{
  1871  					Name: "node1",
  1872  				},
  1873  				Status: v1.NodeStatus{
  1874  					Addresses: []v1.NodeAddress{
  1875  						{Type: v1.NodeExternalIP, Address: "54.10.11.1"},
  1876  						{Type: v1.NodeInternalIP, Address: "10.0.1.1"},
  1877  						{Type: v1.NodeInternalIP, Address: "2001:DB8::1"},
  1878  					},
  1879  				},
  1880  			}, {
  1881  				ObjectMeta: metav1.ObjectMeta{
  1882  					Name: "node2",
  1883  				},
  1884  				Status: v1.NodeStatus{
  1885  					Addresses: []v1.NodeAddress{
  1886  						{Type: v1.NodeExternalIP, Address: "54.10.11.2"},
  1887  						{Type: v1.NodeInternalIP, Address: "10.0.1.2"},
  1888  						{Type: v1.NodeInternalIP, Address: "2001:DB8::2"},
  1889  					},
  1890  				},
  1891  			}},
  1892  			podNames:  []string{"pod-0", "pod-1"},
  1893  			nodeIndex: []int{1, 1},
  1894  			phases:    []v1.PodPhase{v1.PodRunning, v1.PodRunning},
  1895  			conditions: []v1.PodCondition{
  1896  				{Type: v1.PodReady, Status: v1.ConditionFalse},
  1897  				{Type: v1.PodReady, Status: v1.ConditionFalse},
  1898  			},
  1899  			deletionTimestamp: []*metav1.Time{{}, {}},
  1900  		},
  1901  		{
  1902  			title:            "annotated NodePort services with ExternalTrafficPolicy=Local return pods in Ready & Running state",
  1903  			svcNamespace:     "testing",
  1904  			svcName:          "foo",
  1905  			svcType:          v1.ServiceTypeNodePort,
  1906  			svcTrafficPolicy: v1.ServiceExternalTrafficPolicyTypeLocal,
  1907  			labels:           map[string]string{},
  1908  			annotations: map[string]string{
  1909  				hostnameAnnotationKey: "foo.example.org.",
  1910  			},
  1911  			expected: []*endpoint.Endpoint{
  1912  				{DNSName: "_foo._tcp.foo.example.org", Targets: endpoint.Targets{"0 50 30192 foo.example.org"}, RecordType: endpoint.RecordTypeSRV},
  1913  				{DNSName: "foo.example.org", Targets: endpoint.Targets{"54.10.11.1"}, RecordType: endpoint.RecordTypeA},
  1914  			},
  1915  			nodes: []*v1.Node{{
  1916  				ObjectMeta: metav1.ObjectMeta{
  1917  					Name: "node1",
  1918  				},
  1919  				Status: v1.NodeStatus{
  1920  					Addresses: []v1.NodeAddress{
  1921  						{Type: v1.NodeExternalIP, Address: "54.10.11.1"},
  1922  						{Type: v1.NodeInternalIP, Address: "10.0.1.1"},
  1923  					},
  1924  				},
  1925  			}, {
  1926  				ObjectMeta: metav1.ObjectMeta{
  1927  					Name: "node2",
  1928  				},
  1929  				Status: v1.NodeStatus{
  1930  					Addresses: []v1.NodeAddress{
  1931  						{Type: v1.NodeExternalIP, Address: "54.10.11.2"},
  1932  						{Type: v1.NodeInternalIP, Address: "10.0.1.2"},
  1933  					},
  1934  				},
  1935  			}},
  1936  			podNames:  []string{"pod-0", "pod-1"},
  1937  			nodeIndex: []int{0, 1},
  1938  			phases:    []v1.PodPhase{v1.PodRunning, v1.PodRunning},
  1939  			conditions: []v1.PodCondition{
  1940  				{Type: v1.PodReady, Status: v1.ConditionTrue},
  1941  				{Type: v1.PodReady, Status: v1.ConditionFalse},
  1942  			},
  1943  			deletionTimestamp: []*metav1.Time{{}, {}},
  1944  		},
  1945  		{
  1946  			title:            "annotated NodePort services with ExternalTrafficPolicy=Local return pods in Ready & Running state & not in Terminating",
  1947  			svcNamespace:     "testing",
  1948  			svcName:          "foo",
  1949  			svcType:          v1.ServiceTypeNodePort,
  1950  			svcTrafficPolicy: v1.ServiceExternalTrafficPolicyTypeLocal,
  1951  			labels:           map[string]string{},
  1952  			annotations: map[string]string{
  1953  				hostnameAnnotationKey: "foo.example.org.",
  1954  			},
  1955  			expected: []*endpoint.Endpoint{
  1956  				{DNSName: "_foo._tcp.foo.example.org", Targets: endpoint.Targets{"0 50 30192 foo.example.org"}, RecordType: endpoint.RecordTypeSRV},
  1957  				{DNSName: "foo.example.org", Targets: endpoint.Targets{"54.10.11.1"}, RecordType: endpoint.RecordTypeA},
  1958  			},
  1959  			nodes: []*v1.Node{{
  1960  				ObjectMeta: metav1.ObjectMeta{
  1961  					Name: "node1",
  1962  				},
  1963  				Status: v1.NodeStatus{
  1964  					Addresses: []v1.NodeAddress{
  1965  						{Type: v1.NodeExternalIP, Address: "54.10.11.1"},
  1966  						{Type: v1.NodeInternalIP, Address: "10.0.1.1"},
  1967  					},
  1968  				},
  1969  			}, {
  1970  				ObjectMeta: metav1.ObjectMeta{
  1971  					Name: "node2",
  1972  				},
  1973  				Status: v1.NodeStatus{
  1974  					Addresses: []v1.NodeAddress{
  1975  						{Type: v1.NodeExternalIP, Address: "54.10.11.2"},
  1976  						{Type: v1.NodeInternalIP, Address: "10.0.1.2"},
  1977  					},
  1978  				},
  1979  			}, {
  1980  				ObjectMeta: metav1.ObjectMeta{
  1981  					Name: "node3",
  1982  				},
  1983  				Status: v1.NodeStatus{
  1984  					Addresses: []v1.NodeAddress{
  1985  						{Type: v1.NodeExternalIP, Address: "54.10.11.3"},
  1986  						{Type: v1.NodeInternalIP, Address: "10.0.1.3"},
  1987  					},
  1988  				},
  1989  			}},
  1990  			podNames:  []string{"pod-0", "pod-1", "pod-2"},
  1991  			nodeIndex: []int{0, 1, 2},
  1992  			phases:    []v1.PodPhase{v1.PodRunning, v1.PodRunning, v1.PodRunning},
  1993  			conditions: []v1.PodCondition{
  1994  				{Type: v1.PodReady, Status: v1.ConditionTrue},
  1995  				{Type: v1.PodReady, Status: v1.ConditionFalse},
  1996  				{Type: v1.PodReady, Status: v1.ConditionTrue},
  1997  			},
  1998  			deletionTimestamp: []*metav1.Time{nil, nil, {}},
  1999  		},
  2000  		{
  2001  			title:            "access=private annotation NodePort services return an endpoint with private IP addresses of the cluster's nodes",
  2002  			svcNamespace:     "testing",
  2003  			svcName:          "foo",
  2004  			svcType:          v1.ServiceTypeNodePort,
  2005  			svcTrafficPolicy: v1.ServiceExternalTrafficPolicyTypeCluster,
  2006  			labels:           map[string]string{},
  2007  			annotations: map[string]string{
  2008  				hostnameAnnotationKey: "foo.example.org.",
  2009  				accessAnnotationKey:   "private",
  2010  			},
  2011  			expected: []*endpoint.Endpoint{
  2012  				{DNSName: "_foo._tcp.foo.example.org", Targets: endpoint.Targets{"0 50 30192 foo.example.org"}, RecordType: endpoint.RecordTypeSRV},
  2013  				{DNSName: "foo.example.org", Targets: endpoint.Targets{"10.0.1.1", "10.0.1.2"}, RecordType: endpoint.RecordTypeA},
  2014  				{DNSName: "foo.example.org", Targets: endpoint.Targets{"2001:DB8::1", "2001:DB8::2"}, RecordType: endpoint.RecordTypeAAAA},
  2015  			},
  2016  			nodes: []*v1.Node{{
  2017  				ObjectMeta: metav1.ObjectMeta{
  2018  					Name: "node1",
  2019  				},
  2020  				Status: v1.NodeStatus{
  2021  					Addresses: []v1.NodeAddress{
  2022  						{Type: v1.NodeExternalIP, Address: "54.10.11.1"},
  2023  						{Type: v1.NodeInternalIP, Address: "10.0.1.1"},
  2024  						{Type: v1.NodeInternalIP, Address: "2001:DB8::1"},
  2025  					},
  2026  				},
  2027  			}, {
  2028  				ObjectMeta: metav1.ObjectMeta{
  2029  					Name: "node2",
  2030  				},
  2031  				Status: v1.NodeStatus{
  2032  					Addresses: []v1.NodeAddress{
  2033  						{Type: v1.NodeExternalIP, Address: "54.10.11.2"},
  2034  						{Type: v1.NodeInternalIP, Address: "10.0.1.2"},
  2035  						{Type: v1.NodeInternalIP, Address: "2001:DB8::2"},
  2036  					},
  2037  				},
  2038  			}},
  2039  		},
  2040  		{
  2041  			title:            "access=public annotation NodePort services return an endpoint with public IP addresses of the cluster's nodes",
  2042  			svcNamespace:     "testing",
  2043  			svcName:          "foo",
  2044  			svcType:          v1.ServiceTypeNodePort,
  2045  			svcTrafficPolicy: v1.ServiceExternalTrafficPolicyTypeCluster,
  2046  			labels:           map[string]string{},
  2047  			annotations: map[string]string{
  2048  				hostnameAnnotationKey: "foo.example.org.",
  2049  				accessAnnotationKey:   "public",
  2050  			},
  2051  			expected: []*endpoint.Endpoint{
  2052  				{DNSName: "_foo._tcp.foo.example.org", Targets: endpoint.Targets{"0 50 30192 foo.example.org"}, RecordType: endpoint.RecordTypeSRV},
  2053  				{DNSName: "foo.example.org", Targets: endpoint.Targets{"54.10.11.1", "54.10.11.2"}, RecordType: endpoint.RecordTypeA},
  2054  				{DNSName: "foo.example.org", Targets: endpoint.Targets{"2001:DB8::1", "2001:DB8::2"}, RecordType: endpoint.RecordTypeAAAA},
  2055  			},
  2056  			nodes: []*v1.Node{{
  2057  				ObjectMeta: metav1.ObjectMeta{
  2058  					Name: "node1",
  2059  				},
  2060  				Status: v1.NodeStatus{
  2061  					Addresses: []v1.NodeAddress{
  2062  						{Type: v1.NodeExternalIP, Address: "54.10.11.1"},
  2063  						{Type: v1.NodeInternalIP, Address: "10.0.1.1"},
  2064  						{Type: v1.NodeInternalIP, Address: "2001:DB8::1"},
  2065  					},
  2066  				},
  2067  			}, {
  2068  				ObjectMeta: metav1.ObjectMeta{
  2069  					Name: "node2",
  2070  				},
  2071  				Status: v1.NodeStatus{
  2072  					Addresses: []v1.NodeAddress{
  2073  						{Type: v1.NodeExternalIP, Address: "54.10.11.2"},
  2074  						{Type: v1.NodeInternalIP, Address: "10.0.1.2"},
  2075  						{Type: v1.NodeInternalIP, Address: "2001:DB8::2"},
  2076  					},
  2077  				},
  2078  			}},
  2079  		},
  2080  		{
  2081  			title:            "node port services annotated DNS Controller annotations return an endpoint where all targets has the node role",
  2082  			svcNamespace:     "testing",
  2083  			svcName:          "foo",
  2084  			svcType:          v1.ServiceTypeNodePort,
  2085  			svcTrafficPolicy: v1.ServiceExternalTrafficPolicyTypeCluster,
  2086  			compatibility:    "kops-dns-controller",
  2087  			labels:           map[string]string{},
  2088  			annotations: map[string]string{
  2089  				kopsDNSControllerInternalHostnameAnnotationKey: "internal.foo.example.org., internal.bar.example.org",
  2090  			},
  2091  			expected: []*endpoint.Endpoint{
  2092  				{DNSName: "internal.foo.example.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"10.0.1.1"}},
  2093  				{DNSName: "internal.foo.example.org", RecordType: endpoint.RecordTypeAAAA, Targets: endpoint.Targets{"2001:DB8::1"}},
  2094  				{DNSName: "internal.bar.example.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"10.0.1.1"}},
  2095  				{DNSName: "internal.bar.example.org", RecordType: endpoint.RecordTypeAAAA, Targets: endpoint.Targets{"2001:DB8::1"}},
  2096  			},
  2097  			nodes: []*v1.Node{{
  2098  				ObjectMeta: metav1.ObjectMeta{
  2099  					Name: "node1",
  2100  					Labels: map[string]string{
  2101  						"node-role.kubernetes.io/node": "",
  2102  					},
  2103  				},
  2104  				Status: v1.NodeStatus{
  2105  					Addresses: []v1.NodeAddress{
  2106  						{Type: v1.NodeExternalIP, Address: "54.10.11.1"},
  2107  						{Type: v1.NodeInternalIP, Address: "10.0.1.1"},
  2108  						{Type: v1.NodeInternalIP, Address: "2001:DB8::1"},
  2109  					},
  2110  				},
  2111  			}, {
  2112  				ObjectMeta: metav1.ObjectMeta{
  2113  					Name: "node2",
  2114  					Labels: map[string]string{
  2115  						"node-role.kubernetes.io/control-plane": "",
  2116  					},
  2117  				},
  2118  				Status: v1.NodeStatus{
  2119  					Addresses: []v1.NodeAddress{
  2120  						{Type: v1.NodeExternalIP, Address: "54.10.11.2"},
  2121  						{Type: v1.NodeInternalIP, Address: "10.0.1.2"},
  2122  						{Type: v1.NodeInternalIP, Address: "2001:DB8::2"},
  2123  					},
  2124  				},
  2125  			}},
  2126  		},
  2127  		{
  2128  			title:            "node port services annotated with internal DNS Controller annotations return an endpoint in compatibility mode",
  2129  			svcNamespace:     "testing",
  2130  			svcName:          "foo",
  2131  			svcType:          v1.ServiceTypeNodePort,
  2132  			svcTrafficPolicy: v1.ServiceExternalTrafficPolicyTypeCluster,
  2133  			compatibility:    "kops-dns-controller",
  2134  			annotations: map[string]string{
  2135  				kopsDNSControllerInternalHostnameAnnotationKey: "internal.foo.example.org., internal.bar.example.org",
  2136  			},
  2137  			expected: []*endpoint.Endpoint{
  2138  				{DNSName: "internal.foo.example.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"10.0.1.1", "10.0.1.2"}},
  2139  				{DNSName: "internal.foo.example.org", RecordType: endpoint.RecordTypeAAAA, Targets: endpoint.Targets{"2001:DB8::1", "2001:DB8::2"}},
  2140  				{DNSName: "internal.bar.example.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"10.0.1.1", "10.0.1.2"}},
  2141  				{DNSName: "internal.bar.example.org", RecordType: endpoint.RecordTypeAAAA, Targets: endpoint.Targets{"2001:DB8::1", "2001:DB8::2"}},
  2142  			},
  2143  			nodes: []*v1.Node{{
  2144  				ObjectMeta: metav1.ObjectMeta{
  2145  					Name: "node1",
  2146  					Labels: map[string]string{
  2147  						"node-role.kubernetes.io/node": "",
  2148  					},
  2149  				},
  2150  				Status: v1.NodeStatus{
  2151  					Addresses: []v1.NodeAddress{
  2152  						{Type: v1.NodeExternalIP, Address: "54.10.11.1"},
  2153  						{Type: v1.NodeInternalIP, Address: "10.0.1.1"},
  2154  						{Type: v1.NodeInternalIP, Address: "2001:DB8::1"},
  2155  					},
  2156  				},
  2157  			}, {
  2158  				ObjectMeta: metav1.ObjectMeta{
  2159  					Name: "node2",
  2160  					Labels: map[string]string{
  2161  						"node-role.kubernetes.io/node": "",
  2162  					},
  2163  				},
  2164  				Status: v1.NodeStatus{
  2165  					Addresses: []v1.NodeAddress{
  2166  						{Type: v1.NodeExternalIP, Address: "54.10.11.2"},
  2167  						{Type: v1.NodeInternalIP, Address: "10.0.1.2"},
  2168  						{Type: v1.NodeInternalIP, Address: "2001:DB8::2"},
  2169  					},
  2170  				},
  2171  			}},
  2172  		},
  2173  		{
  2174  			title:            "node port services annotated with external DNS Controller annotations return an endpoint in compatibility mode",
  2175  			svcNamespace:     "testing",
  2176  			svcName:          "foo",
  2177  			svcType:          v1.ServiceTypeNodePort,
  2178  			svcTrafficPolicy: v1.ServiceExternalTrafficPolicyTypeCluster,
  2179  			compatibility:    "kops-dns-controller",
  2180  			annotations: map[string]string{
  2181  				kopsDNSControllerHostnameAnnotationKey: "foo.example.org., bar.example.org",
  2182  			},
  2183  			expected: []*endpoint.Endpoint{
  2184  				{DNSName: "foo.example.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"54.10.11.1", "54.10.11.2"}},
  2185  				{DNSName: "foo.example.org", RecordType: endpoint.RecordTypeAAAA, Targets: endpoint.Targets{"2001:DB8::1", "2001:DB8::2"}},
  2186  				{DNSName: "bar.example.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"54.10.11.1", "54.10.11.2"}},
  2187  				{DNSName: "bar.example.org", RecordType: endpoint.RecordTypeAAAA, Targets: endpoint.Targets{"2001:DB8::1", "2001:DB8::2"}},
  2188  			},
  2189  			nodes: []*v1.Node{{
  2190  				ObjectMeta: metav1.ObjectMeta{
  2191  					Name: "node1",
  2192  					Labels: map[string]string{
  2193  						"node-role.kubernetes.io/node": "",
  2194  					},
  2195  				},
  2196  				Status: v1.NodeStatus{
  2197  					Addresses: []v1.NodeAddress{
  2198  						{Type: v1.NodeExternalIP, Address: "54.10.11.1"},
  2199  						{Type: v1.NodeInternalIP, Address: "10.0.1.1"},
  2200  						{Type: v1.NodeInternalIP, Address: "2001:DB8::1"},
  2201  					},
  2202  				},
  2203  			}, {
  2204  				ObjectMeta: metav1.ObjectMeta{
  2205  					Name: "node2",
  2206  					Labels: map[string]string{
  2207  						"node-role.kubernetes.io/node": "",
  2208  					},
  2209  				},
  2210  				Status: v1.NodeStatus{
  2211  					Addresses: []v1.NodeAddress{
  2212  						{Type: v1.NodeExternalIP, Address: "54.10.11.2"},
  2213  						{Type: v1.NodeInternalIP, Address: "10.0.1.2"},
  2214  						{Type: v1.NodeInternalIP, Address: "2001:DB8::2"},
  2215  					},
  2216  				},
  2217  			}},
  2218  		},
  2219  		{
  2220  			title:            "node port services annotated with both kops dns controller annotations return an empty set of addons",
  2221  			svcNamespace:     "testing",
  2222  			svcName:          "foo",
  2223  			svcType:          v1.ServiceTypeNodePort,
  2224  			svcTrafficPolicy: v1.ServiceExternalTrafficPolicyTypeCluster,
  2225  			compatibility:    "kops-dns-controller",
  2226  			labels:           map[string]string{},
  2227  			annotations: map[string]string{
  2228  				kopsDNSControllerInternalHostnameAnnotationKey: "internal.foo.example.org., internal.bar.example.org",
  2229  				kopsDNSControllerHostnameAnnotationKey:         "foo.example.org., bar.example.org",
  2230  			},
  2231  			expected: []*endpoint.Endpoint{},
  2232  			nodes: []*v1.Node{{
  2233  				ObjectMeta: metav1.ObjectMeta{
  2234  					Name: "node1",
  2235  					Labels: map[string]string{
  2236  						"node-role.kubernetes.io/node": "",
  2237  					},
  2238  				},
  2239  				Status: v1.NodeStatus{
  2240  					Addresses: []v1.NodeAddress{
  2241  						{Type: v1.NodeExternalIP, Address: "54.10.11.1"},
  2242  						{Type: v1.NodeInternalIP, Address: "10.0.1.1"},
  2243  						{Type: v1.NodeInternalIP, Address: "2001:DB8::1"},
  2244  					},
  2245  				},
  2246  			}, {
  2247  				ObjectMeta: metav1.ObjectMeta{
  2248  					Name: "node2",
  2249  					Labels: map[string]string{
  2250  						"node-role.kubernetes.io/node": "",
  2251  					},
  2252  				},
  2253  				Status: v1.NodeStatus{
  2254  					Addresses: []v1.NodeAddress{
  2255  						{Type: v1.NodeExternalIP, Address: "54.10.11.2"},
  2256  						{Type: v1.NodeInternalIP, Address: "10.0.1.2"},
  2257  						{Type: v1.NodeInternalIP, Address: "2001:DB8::2"},
  2258  					},
  2259  				},
  2260  			}},
  2261  		},
  2262  	} {
  2263  		tc := tc
  2264  		t.Run(tc.title, func(t *testing.T) {
  2265  			t.Parallel()
  2266  
  2267  			// Create a Kubernetes testing client
  2268  			kubernetes := fake.NewSimpleClientset()
  2269  
  2270  			// Create the nodes
  2271  			for _, node := range tc.nodes {
  2272  				if _, err := kubernetes.CoreV1().Nodes().Create(context.Background(), node, metav1.CreateOptions{}); err != nil {
  2273  					t.Fatal(err)
  2274  				}
  2275  			}
  2276  
  2277  			// Create  pods
  2278  			for i, podname := range tc.podNames {
  2279  				pod := &v1.Pod{
  2280  					Spec: v1.PodSpec{
  2281  						Containers: []v1.Container{},
  2282  						Hostname:   podname,
  2283  						NodeName:   tc.nodes[tc.nodeIndex[i]].Name,
  2284  					},
  2285  					ObjectMeta: metav1.ObjectMeta{
  2286  						Namespace:         tc.svcNamespace,
  2287  						Name:              podname,
  2288  						Labels:            tc.labels,
  2289  						Annotations:       tc.annotations,
  2290  						DeletionTimestamp: tc.deletionTimestamp[i],
  2291  					},
  2292  					Status: v1.PodStatus{
  2293  						Phase:      tc.phases[i],
  2294  						Conditions: []v1.PodCondition{tc.conditions[i]},
  2295  					},
  2296  				}
  2297  
  2298  				_, err := kubernetes.CoreV1().Pods(tc.svcNamespace).Create(context.Background(), pod, metav1.CreateOptions{})
  2299  				require.NoError(t, err)
  2300  			}
  2301  
  2302  			// Create a service to test against
  2303  			service := &v1.Service{
  2304  				Spec: v1.ServiceSpec{
  2305  					Type:                  tc.svcType,
  2306  					ExternalTrafficPolicy: tc.svcTrafficPolicy,
  2307  					Ports: []v1.ServicePort{
  2308  						{
  2309  							NodePort: 30192,
  2310  						},
  2311  					},
  2312  				},
  2313  				ObjectMeta: metav1.ObjectMeta{
  2314  					Namespace:   tc.svcNamespace,
  2315  					Name:        tc.svcName,
  2316  					Labels:      tc.labels,
  2317  					Annotations: tc.annotations,
  2318  				},
  2319  			}
  2320  
  2321  			_, err := kubernetes.CoreV1().Services(service.Namespace).Create(context.Background(), service, metav1.CreateOptions{})
  2322  			require.NoError(t, err)
  2323  
  2324  			// Create our object under test and get the endpoints.
  2325  			client, _ := NewServiceSource(
  2326  				context.TODO(),
  2327  				kubernetes,
  2328  				tc.targetNamespace,
  2329  				tc.annotationFilter,
  2330  				tc.fqdnTemplate,
  2331  				false,
  2332  				tc.compatibility,
  2333  				true,
  2334  				false,
  2335  				false,
  2336  				[]string{},
  2337  				tc.ignoreHostnameAnnotation,
  2338  				labels.Everything(),
  2339  				false,
  2340  			)
  2341  			require.NoError(t, err)
  2342  
  2343  			endpoints, err := client.Endpoints(context.Background())
  2344  			if tc.expectError {
  2345  				require.Error(t, err)
  2346  			} else {
  2347  				require.NoError(t, err)
  2348  			}
  2349  
  2350  			// Validate returned endpoints against desired endpoints.
  2351  			validateEndpoints(t, endpoints, tc.expected)
  2352  		})
  2353  	}
  2354  }
  2355  
  2356  // TestHeadlessServices tests that headless services generate the correct endpoints.
  2357  func TestHeadlessServices(t *testing.T) {
  2358  	t.Parallel()
  2359  
  2360  	for _, tc := range []struct {
  2361  		title                    string
  2362  		targetNamespace          string
  2363  		svcNamespace             string
  2364  		svcName                  string
  2365  		svcType                  v1.ServiceType
  2366  		compatibility            string
  2367  		fqdnTemplate             string
  2368  		ignoreHostnameAnnotation bool
  2369  		labels                   map[string]string
  2370  		svcAnnotations           map[string]string
  2371  		podAnnotations           map[string]string
  2372  		clusterIP                string
  2373  		podIPs                   []string
  2374  		hostIPs                  []string
  2375  		selector                 map[string]string
  2376  		lbs                      []string
  2377  		podnames                 []string
  2378  		hostnames                []string
  2379  		podsReady                []bool
  2380  		publishNotReadyAddresses bool
  2381  		nodes                    []v1.Node
  2382  		expected                 []*endpoint.Endpoint
  2383  		expectError              bool
  2384  	}{
  2385  		{
  2386  			"annotated Headless services return IPv4 endpoints for each selected Pod",
  2387  			"",
  2388  			"testing",
  2389  			"foo",
  2390  			v1.ServiceTypeClusterIP,
  2391  			"",
  2392  			"",
  2393  			false,
  2394  			map[string]string{"component": "foo"},
  2395  			map[string]string{
  2396  				hostnameAnnotationKey: "service.example.org",
  2397  			},
  2398  			map[string]string{},
  2399  			v1.ClusterIPNone,
  2400  			[]string{"1.1.1.1", "1.1.1.2"},
  2401  			[]string{"", ""},
  2402  			map[string]string{
  2403  				"component": "foo",
  2404  			},
  2405  			[]string{},
  2406  			[]string{"foo-0", "foo-1"},
  2407  			[]string{"foo-0", "foo-1"},
  2408  			[]bool{true, true},
  2409  			false,
  2410  			[]v1.Node{},
  2411  			[]*endpoint.Endpoint{
  2412  				{DNSName: "foo-0.service.example.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"1.1.1.1"}},
  2413  				{DNSName: "foo-1.service.example.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"1.1.1.2"}},
  2414  				{DNSName: "service.example.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"1.1.1.1", "1.1.1.2"}},
  2415  			},
  2416  			false,
  2417  		},
  2418  		{
  2419  			"annotated Headless services return IPv6 endpoints for each selected Pod",
  2420  			"",
  2421  			"testing",
  2422  			"foo",
  2423  			v1.ServiceTypeClusterIP,
  2424  			"",
  2425  			"",
  2426  			false,
  2427  			map[string]string{"component": "foo"},
  2428  			map[string]string{
  2429  				hostnameAnnotationKey: "service.example.org",
  2430  			},
  2431  			map[string]string{},
  2432  			v1.ClusterIPNone,
  2433  			[]string{"2001:db8::1", "2001:db8::2"},
  2434  			[]string{"", ""},
  2435  			map[string]string{
  2436  				"component": "foo",
  2437  			},
  2438  			[]string{},
  2439  			[]string{"foo-0", "foo-1"},
  2440  			[]string{"foo-0", "foo-1"},
  2441  			[]bool{true, true},
  2442  			false,
  2443  			[]v1.Node{},
  2444  			[]*endpoint.Endpoint{
  2445  				{DNSName: "foo-0.service.example.org", RecordType: endpoint.RecordTypeAAAA, Targets: endpoint.Targets{"2001:db8::1"}},
  2446  				{DNSName: "foo-1.service.example.org", RecordType: endpoint.RecordTypeAAAA, Targets: endpoint.Targets{"2001:db8::2"}},
  2447  				{DNSName: "service.example.org", RecordType: endpoint.RecordTypeAAAA, Targets: endpoint.Targets{"2001:db8::1", "2001:db8::2"}},
  2448  			},
  2449  			false,
  2450  		},
  2451  		{
  2452  			"hostname annotated Headless services are ignored",
  2453  			"",
  2454  			"testing",
  2455  			"foo",
  2456  			v1.ServiceTypeClusterIP,
  2457  			"",
  2458  			"",
  2459  			true,
  2460  			map[string]string{"component": "foo"},
  2461  			map[string]string{
  2462  				hostnameAnnotationKey: "service.example.org",
  2463  			},
  2464  			map[string]string{},
  2465  			v1.ClusterIPNone,
  2466  			[]string{"1.1.1.1", "1.1.1.2"},
  2467  			[]string{"", ""},
  2468  			map[string]string{
  2469  				"component": "foo",
  2470  			},
  2471  			[]string{},
  2472  			[]string{"foo-0", "foo-1"},
  2473  			[]string{"foo-0", "foo-1"},
  2474  			[]bool{true, true},
  2475  			false,
  2476  			[]v1.Node{},
  2477  			[]*endpoint.Endpoint{},
  2478  			false,
  2479  		},
  2480  		{
  2481  			"annotated Headless services return IPv4 endpoints with TTL for each selected Pod",
  2482  			"",
  2483  			"testing",
  2484  			"foo",
  2485  			v1.ServiceTypeClusterIP,
  2486  			"",
  2487  			"",
  2488  			false,
  2489  			map[string]string{"component": "foo"},
  2490  			map[string]string{
  2491  				hostnameAnnotationKey: "service.example.org",
  2492  				ttlAnnotationKey:      "1",
  2493  			},
  2494  			map[string]string{},
  2495  			v1.ClusterIPNone,
  2496  			[]string{"1.1.1.1", "1.1.1.2"},
  2497  			[]string{"", ""},
  2498  			map[string]string{
  2499  				"component": "foo",
  2500  			},
  2501  			[]string{},
  2502  			[]string{"foo-0", "foo-1"},
  2503  			[]string{"foo-0", "foo-1"},
  2504  			[]bool{true, true},
  2505  			false,
  2506  			[]v1.Node{},
  2507  			[]*endpoint.Endpoint{
  2508  				{DNSName: "foo-0.service.example.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"1.1.1.1"}, RecordTTL: endpoint.TTL(1)},
  2509  				{DNSName: "foo-1.service.example.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"1.1.1.2"}, RecordTTL: endpoint.TTL(1)},
  2510  				{DNSName: "service.example.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"1.1.1.1", "1.1.1.2"}, RecordTTL: endpoint.TTL(1)},
  2511  			},
  2512  			false,
  2513  		},
  2514  		{
  2515  			"annotated Headless services return IPv6 endpoints with TTL for each selected Pod",
  2516  			"",
  2517  			"testing",
  2518  			"foo",
  2519  			v1.ServiceTypeClusterIP,
  2520  			"",
  2521  			"",
  2522  			false,
  2523  			map[string]string{"component": "foo"},
  2524  			map[string]string{
  2525  				hostnameAnnotationKey: "service.example.org",
  2526  				ttlAnnotationKey:      "1",
  2527  			},
  2528  			map[string]string{},
  2529  			v1.ClusterIPNone,
  2530  			[]string{"2001:db8::1", "2001:db8::2"},
  2531  			[]string{"", ""},
  2532  			map[string]string{
  2533  				"component": "foo",
  2534  			},
  2535  			[]string{},
  2536  			[]string{"foo-0", "foo-1"},
  2537  			[]string{"foo-0", "foo-1"},
  2538  			[]bool{true, true},
  2539  			false,
  2540  			[]v1.Node{},
  2541  			[]*endpoint.Endpoint{
  2542  				{DNSName: "foo-0.service.example.org", RecordType: endpoint.RecordTypeAAAA, Targets: endpoint.Targets{"2001:db8::1"}, RecordTTL: endpoint.TTL(1)},
  2543  				{DNSName: "foo-1.service.example.org", RecordType: endpoint.RecordTypeAAAA, Targets: endpoint.Targets{"2001:db8::2"}, RecordTTL: endpoint.TTL(1)},
  2544  				{DNSName: "service.example.org", RecordType: endpoint.RecordTypeAAAA, Targets: endpoint.Targets{"2001:db8::1", "2001:db8::2"}, RecordTTL: endpoint.TTL(1)},
  2545  			},
  2546  			false,
  2547  		},
  2548  		{
  2549  			"annotated Headless services return endpoints for each selected Pod, which are in running state",
  2550  			"",
  2551  			"testing",
  2552  			"foo",
  2553  			v1.ServiceTypeClusterIP,
  2554  			"",
  2555  			"",
  2556  			false,
  2557  			map[string]string{"component": "foo"},
  2558  			map[string]string{
  2559  				hostnameAnnotationKey: "service.example.org",
  2560  			},
  2561  			map[string]string{},
  2562  			v1.ClusterIPNone,
  2563  			[]string{"1.1.1.1", "1.1.1.2"},
  2564  			[]string{"", ""},
  2565  			map[string]string{
  2566  				"component": "foo",
  2567  			},
  2568  			[]string{},
  2569  			[]string{"foo-0", "foo-1"},
  2570  			[]string{"foo-0", "foo-1"},
  2571  			[]bool{true, false},
  2572  			false,
  2573  			[]v1.Node{},
  2574  			[]*endpoint.Endpoint{
  2575  				{DNSName: "foo-0.service.example.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"1.1.1.1"}},
  2576  				{DNSName: "service.example.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"1.1.1.1"}},
  2577  			},
  2578  			false,
  2579  		},
  2580  		{
  2581  			"annotated Headless services return endpoints for all Pod if publishNotReadyAddresses is set",
  2582  			"",
  2583  			"testing",
  2584  			"foo",
  2585  			v1.ServiceTypeClusterIP,
  2586  			"",
  2587  			"",
  2588  			false,
  2589  			map[string]string{"component": "foo"},
  2590  			map[string]string{
  2591  				hostnameAnnotationKey: "service.example.org",
  2592  			},
  2593  			map[string]string{},
  2594  			v1.ClusterIPNone,
  2595  			[]string{"1.1.1.1", "1.1.1.2"},
  2596  			[]string{"", ""},
  2597  			map[string]string{
  2598  				"component": "foo",
  2599  			},
  2600  			[]string{},
  2601  			[]string{"foo-0", "foo-1"},
  2602  			[]string{"foo-0", "foo-1"},
  2603  			[]bool{true, false},
  2604  			true,
  2605  			[]v1.Node{},
  2606  			[]*endpoint.Endpoint{
  2607  				{DNSName: "foo-0.service.example.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"1.1.1.1"}},
  2608  				{DNSName: "foo-1.service.example.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"1.1.1.2"}},
  2609  				{DNSName: "service.example.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"1.1.1.1", "1.1.1.2"}},
  2610  			},
  2611  			false,
  2612  		},
  2613  		{
  2614  			"annotated Headless services return endpoints for pods missing hostname",
  2615  			"",
  2616  			"testing",
  2617  			"foo",
  2618  			v1.ServiceTypeClusterIP,
  2619  			"",
  2620  			"",
  2621  			false,
  2622  			map[string]string{"component": "foo"},
  2623  			map[string]string{
  2624  				hostnameAnnotationKey: "service.example.org",
  2625  			},
  2626  			map[string]string{},
  2627  			v1.ClusterIPNone,
  2628  			[]string{"1.1.1.1", "1.1.1.2"},
  2629  			[]string{"", ""},
  2630  			map[string]string{
  2631  				"component": "foo",
  2632  			},
  2633  			[]string{},
  2634  			[]string{"foo-0", "foo-1"},
  2635  			[]string{"", ""},
  2636  			[]bool{true, true},
  2637  			false,
  2638  			[]v1.Node{},
  2639  			[]*endpoint.Endpoint{
  2640  				{DNSName: "service.example.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"1.1.1.1", "1.1.1.2"}},
  2641  			},
  2642  			false,
  2643  		},
  2644  		{
  2645  			"annotated Headless services return only a unique set of IPv4 targets",
  2646  			"",
  2647  			"testing",
  2648  			"foo",
  2649  			v1.ServiceTypeClusterIP,
  2650  			"",
  2651  			"",
  2652  			false,
  2653  			map[string]string{"component": "foo"},
  2654  			map[string]string{
  2655  				hostnameAnnotationKey: "service.example.org",
  2656  			},
  2657  			map[string]string{},
  2658  			v1.ClusterIPNone,
  2659  			[]string{"1.1.1.1", "1.1.1.1", "1.1.1.2"},
  2660  			[]string{"", "", ""},
  2661  			map[string]string{
  2662  				"component": "foo",
  2663  			},
  2664  			[]string{},
  2665  			[]string{"foo-0", "foo-1", "foo-3"},
  2666  			[]string{"", "", ""},
  2667  			[]bool{true, true, true},
  2668  			false,
  2669  			[]v1.Node{},
  2670  			[]*endpoint.Endpoint{
  2671  				{DNSName: "service.example.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"1.1.1.1", "1.1.1.2"}},
  2672  			},
  2673  			false,
  2674  		},
  2675  		{
  2676  			"annotated Headless services return only a unique set of IPv6 targets",
  2677  			"",
  2678  			"testing",
  2679  			"foo",
  2680  			v1.ServiceTypeClusterIP,
  2681  			"",
  2682  			"",
  2683  			false,
  2684  			map[string]string{"component": "foo"},
  2685  			map[string]string{
  2686  				hostnameAnnotationKey: "service.example.org",
  2687  			},
  2688  			map[string]string{},
  2689  			v1.ClusterIPNone,
  2690  			[]string{"2001:db8::1", "2001:db8::1", "2001:db8::2"},
  2691  			[]string{"", "", ""},
  2692  			map[string]string{
  2693  				"component": "foo",
  2694  			},
  2695  			[]string{},
  2696  			[]string{"foo-0", "foo-1", "foo-3"},
  2697  			[]string{"", "", ""},
  2698  			[]bool{true, true, true},
  2699  			false,
  2700  			[]v1.Node{},
  2701  			[]*endpoint.Endpoint{
  2702  				{DNSName: "service.example.org", RecordType: endpoint.RecordTypeAAAA, Targets: endpoint.Targets{"2001:db8::1", "2001:db8::2"}},
  2703  			},
  2704  			false,
  2705  		},
  2706  		{
  2707  			"annotated Headless services return IPv4 targets from pod annotation",
  2708  			"",
  2709  			"testing",
  2710  			"foo",
  2711  			v1.ServiceTypeClusterIP,
  2712  			"",
  2713  			"",
  2714  			false,
  2715  			map[string]string{"component": "foo"},
  2716  			map[string]string{
  2717  				hostnameAnnotationKey: "service.example.org",
  2718  			},
  2719  			map[string]string{
  2720  				targetAnnotationKey: "1.2.3.4",
  2721  			},
  2722  			v1.ClusterIPNone,
  2723  			[]string{"1.1.1.1"},
  2724  			[]string{""},
  2725  			map[string]string{
  2726  				"component": "foo",
  2727  			},
  2728  			[]string{},
  2729  			[]string{"foo"},
  2730  			[]string{"", "", ""},
  2731  			[]bool{true, true, true},
  2732  			false,
  2733  			[]v1.Node{},
  2734  			[]*endpoint.Endpoint{
  2735  				{DNSName: "service.example.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"1.2.3.4"}},
  2736  			},
  2737  			false,
  2738  		},
  2739  		{
  2740  			"annotated Headless services return IPv6 targets from pod annotation",
  2741  			"",
  2742  			"testing",
  2743  			"foo",
  2744  			v1.ServiceTypeClusterIP,
  2745  			"",
  2746  			"",
  2747  			false,
  2748  			map[string]string{"component": "foo"},
  2749  			map[string]string{
  2750  				hostnameAnnotationKey: "service.example.org",
  2751  			},
  2752  			map[string]string{
  2753  				targetAnnotationKey: "2001:db8::4",
  2754  			},
  2755  			v1.ClusterIPNone,
  2756  			[]string{"2001:db8::1"},
  2757  			[]string{""},
  2758  			map[string]string{
  2759  				"component": "foo",
  2760  			},
  2761  			[]string{},
  2762  			[]string{"foo"},
  2763  			[]string{"", "", ""},
  2764  			[]bool{true, true, true},
  2765  			false,
  2766  			[]v1.Node{},
  2767  			[]*endpoint.Endpoint{
  2768  				{DNSName: "service.example.org", RecordType: endpoint.RecordTypeAAAA, Targets: endpoint.Targets{"2001:db8::4"}},
  2769  			},
  2770  			false,
  2771  		},
  2772  		{
  2773  			"annotated Headless services return IPv4 targets from node external IP if endpoints-type annotation is set",
  2774  			"",
  2775  			"testing",
  2776  			"foo",
  2777  			v1.ServiceTypeClusterIP,
  2778  			"",
  2779  			"",
  2780  			false,
  2781  			map[string]string{"component": "foo"},
  2782  			map[string]string{
  2783  				hostnameAnnotationKey:      "service.example.org",
  2784  				endpointsTypeAnnotationKey: EndpointsTypeNodeExternalIP,
  2785  			},
  2786  			map[string]string{},
  2787  			v1.ClusterIPNone,
  2788  			[]string{"1.1.1.1"},
  2789  			[]string{""},
  2790  			map[string]string{
  2791  				"component": "foo",
  2792  			},
  2793  			[]string{},
  2794  			[]string{"foo"},
  2795  			[]string{"", "", ""},
  2796  			[]bool{true, true, true},
  2797  			false,
  2798  			[]v1.Node{
  2799  				{
  2800  					Status: v1.NodeStatus{
  2801  						Addresses: []v1.NodeAddress{
  2802  							{
  2803  								Type:    v1.NodeExternalIP,
  2804  								Address: "1.2.3.4",
  2805  							},
  2806  						},
  2807  					},
  2808  				},
  2809  			},
  2810  			[]*endpoint.Endpoint{
  2811  				{DNSName: "service.example.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"1.2.3.4"}},
  2812  			},
  2813  			false,
  2814  		},
  2815  		{
  2816  			"annotated Headless services return IPv6 targets from node external IP if endpoints-type annotation is set",
  2817  			"",
  2818  			"testing",
  2819  			"foo",
  2820  			v1.ServiceTypeClusterIP,
  2821  			"",
  2822  			"",
  2823  			false,
  2824  			map[string]string{"component": "foo"},
  2825  			map[string]string{
  2826  				hostnameAnnotationKey:      "service.example.org",
  2827  				endpointsTypeAnnotationKey: EndpointsTypeNodeExternalIP,
  2828  			},
  2829  			map[string]string{},
  2830  			v1.ClusterIPNone,
  2831  			[]string{"2001:db8::1"},
  2832  			[]string{""},
  2833  			map[string]string{
  2834  				"component": "foo",
  2835  			},
  2836  			[]string{},
  2837  			[]string{"foo"},
  2838  			[]string{"", "", ""},
  2839  			[]bool{true, true, true},
  2840  			false,
  2841  			[]v1.Node{
  2842  				{
  2843  					Status: v1.NodeStatus{
  2844  						Addresses: []v1.NodeAddress{
  2845  							{
  2846  								Type:    v1.NodeInternalIP,
  2847  								Address: "2001:db8::4",
  2848  							},
  2849  						},
  2850  					},
  2851  				},
  2852  			},
  2853  			[]*endpoint.Endpoint{
  2854  				{DNSName: "service.example.org", RecordType: endpoint.RecordTypeAAAA, Targets: endpoint.Targets{"2001:db8::4"}},
  2855  			},
  2856  			false,
  2857  		},
  2858  		{
  2859  			"annotated Headless services return dual-stack targets from node external IP if endpoints-type annotation is set",
  2860  			"",
  2861  			"testing",
  2862  			"foo",
  2863  			v1.ServiceTypeClusterIP,
  2864  			"",
  2865  			"",
  2866  			false,
  2867  			map[string]string{"component": "foo"},
  2868  			map[string]string{
  2869  				hostnameAnnotationKey:      "service.example.org",
  2870  				endpointsTypeAnnotationKey: EndpointsTypeNodeExternalIP,
  2871  			},
  2872  			map[string]string{},
  2873  			v1.ClusterIPNone,
  2874  			[]string{"1.1.1.1"},
  2875  			[]string{""},
  2876  			map[string]string{
  2877  				"component": "foo",
  2878  			},
  2879  			[]string{},
  2880  			[]string{"foo"},
  2881  			[]string{"", "", ""},
  2882  			[]bool{true, true, true},
  2883  			false,
  2884  			[]v1.Node{
  2885  				{
  2886  					Status: v1.NodeStatus{
  2887  						Addresses: []v1.NodeAddress{
  2888  							{
  2889  								Type:    v1.NodeExternalIP,
  2890  								Address: "1.2.3.4",
  2891  							},
  2892  							{
  2893  								Type:    v1.NodeInternalIP,
  2894  								Address: "2001:db8::4",
  2895  							},
  2896  						},
  2897  					},
  2898  				},
  2899  			},
  2900  			[]*endpoint.Endpoint{
  2901  				{DNSName: "service.example.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"1.2.3.4"}},
  2902  				{DNSName: "service.example.org", RecordType: endpoint.RecordTypeAAAA, Targets: endpoint.Targets{"2001:db8::4"}},
  2903  			},
  2904  			false,
  2905  		},
  2906  		{
  2907  			"annotated Headless services return IPv4 targets from hostIP if endpoints-type annotation is set",
  2908  			"",
  2909  			"testing",
  2910  			"foo",
  2911  			v1.ServiceTypeClusterIP,
  2912  			"",
  2913  			"",
  2914  			false,
  2915  			map[string]string{"component": "foo"},
  2916  			map[string]string{
  2917  				hostnameAnnotationKey:      "service.example.org",
  2918  				endpointsTypeAnnotationKey: EndpointsTypeHostIP,
  2919  			},
  2920  			map[string]string{},
  2921  			v1.ClusterIPNone,
  2922  			[]string{"1.1.1.1"},
  2923  			[]string{"1.2.3.4"},
  2924  			map[string]string{
  2925  				"component": "foo",
  2926  			},
  2927  			[]string{},
  2928  			[]string{"foo"},
  2929  			[]string{"", "", ""},
  2930  			[]bool{true, true, true},
  2931  			false,
  2932  			[]v1.Node{},
  2933  			[]*endpoint.Endpoint{
  2934  				{DNSName: "service.example.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"1.2.3.4"}},
  2935  			},
  2936  			false,
  2937  		},
  2938  		{
  2939  			"annotated Headless services return IPv6 targets from hostIP if endpoints-type annotation is set",
  2940  			"",
  2941  			"testing",
  2942  			"foo",
  2943  			v1.ServiceTypeClusterIP,
  2944  			"",
  2945  			"",
  2946  			false,
  2947  			map[string]string{"component": "foo"},
  2948  			map[string]string{
  2949  				hostnameAnnotationKey:      "service.example.org",
  2950  				endpointsTypeAnnotationKey: EndpointsTypeHostIP,
  2951  			},
  2952  			map[string]string{},
  2953  			v1.ClusterIPNone,
  2954  			[]string{"2001:db8::1"},
  2955  			[]string{"2001:db8::4"},
  2956  			map[string]string{
  2957  				"component": "foo",
  2958  			},
  2959  			[]string{},
  2960  			[]string{"foo"},
  2961  			[]string{"", "", ""},
  2962  			[]bool{true, true, true},
  2963  			false,
  2964  			[]v1.Node{},
  2965  			[]*endpoint.Endpoint{
  2966  				{DNSName: "service.example.org", RecordType: endpoint.RecordTypeAAAA, Targets: endpoint.Targets{"2001:db8::4"}},
  2967  			},
  2968  			false,
  2969  		},
  2970  	} {
  2971  		tc := tc
  2972  		t.Run(tc.title, func(t *testing.T) {
  2973  			t.Parallel()
  2974  
  2975  			// Create a Kubernetes testing client
  2976  			kubernetes := fake.NewSimpleClientset()
  2977  
  2978  			service := &v1.Service{
  2979  				Spec: v1.ServiceSpec{
  2980  					Type:                     tc.svcType,
  2981  					ClusterIP:                tc.clusterIP,
  2982  					Selector:                 tc.selector,
  2983  					PublishNotReadyAddresses: tc.publishNotReadyAddresses,
  2984  				},
  2985  				ObjectMeta: metav1.ObjectMeta{
  2986  					Namespace:   tc.svcNamespace,
  2987  					Name:        tc.svcName,
  2988  					Labels:      tc.labels,
  2989  					Annotations: tc.svcAnnotations,
  2990  				},
  2991  				Status: v1.ServiceStatus{},
  2992  			}
  2993  			_, err := kubernetes.CoreV1().Services(service.Namespace).Create(context.Background(), service, metav1.CreateOptions{})
  2994  			require.NoError(t, err)
  2995  
  2996  			var addresses, notReadyAddresses []v1.EndpointAddress
  2997  			for i, podname := range tc.podnames {
  2998  				pod := &v1.Pod{
  2999  					Spec: v1.PodSpec{
  3000  						Containers: []v1.Container{},
  3001  						Hostname:   tc.hostnames[i],
  3002  					},
  3003  					ObjectMeta: metav1.ObjectMeta{
  3004  						Namespace:   tc.svcNamespace,
  3005  						Name:        podname,
  3006  						Labels:      tc.labels,
  3007  						Annotations: tc.podAnnotations,
  3008  					},
  3009  					Status: v1.PodStatus{
  3010  						PodIP:  tc.podIPs[i],
  3011  						HostIP: tc.hostIPs[i],
  3012  					},
  3013  				}
  3014  
  3015  				_, err = kubernetes.CoreV1().Pods(tc.svcNamespace).Create(context.Background(), pod, metav1.CreateOptions{})
  3016  				require.NoError(t, err)
  3017  
  3018  				address := v1.EndpointAddress{
  3019  					IP: tc.podIPs[i],
  3020  					TargetRef: &v1.ObjectReference{
  3021  						APIVersion: "",
  3022  						Kind:       "Pod",
  3023  						Name:       podname,
  3024  					},
  3025  				}
  3026  				if tc.podsReady[i] {
  3027  					addresses = append(addresses, address)
  3028  				} else {
  3029  					notReadyAddresses = append(notReadyAddresses, address)
  3030  				}
  3031  			}
  3032  			endpointsObject := &v1.Endpoints{
  3033  				ObjectMeta: metav1.ObjectMeta{
  3034  					Namespace: tc.svcNamespace,
  3035  					Name:      tc.svcName,
  3036  					Labels:    tc.labels,
  3037  				},
  3038  				Subsets: []v1.EndpointSubset{
  3039  					{
  3040  						Addresses:         addresses,
  3041  						NotReadyAddresses: notReadyAddresses,
  3042  					},
  3043  				},
  3044  			}
  3045  			_, err = kubernetes.CoreV1().Endpoints(tc.svcNamespace).Create(context.Background(), endpointsObject, metav1.CreateOptions{})
  3046  			require.NoError(t, err)
  3047  			for _, node := range tc.nodes {
  3048  				_, err = kubernetes.CoreV1().Nodes().Create(context.Background(), &node, metav1.CreateOptions{})
  3049  				require.NoError(t, err)
  3050  			}
  3051  
  3052  			// Create our object under test and get the endpoints.
  3053  			client, _ := NewServiceSource(
  3054  				context.TODO(),
  3055  				kubernetes,
  3056  				tc.targetNamespace,
  3057  				"",
  3058  				tc.fqdnTemplate,
  3059  				false,
  3060  				tc.compatibility,
  3061  				true,
  3062  				false,
  3063  				false,
  3064  				[]string{},
  3065  				tc.ignoreHostnameAnnotation,
  3066  				labels.Everything(),
  3067  				false,
  3068  			)
  3069  			require.NoError(t, err)
  3070  
  3071  			endpoints, err := client.Endpoints(context.Background())
  3072  			if tc.expectError {
  3073  				require.Error(t, err)
  3074  			} else {
  3075  				require.NoError(t, err)
  3076  			}
  3077  
  3078  			// Validate returned endpoints against desired endpoints.
  3079  			validateEndpoints(t, endpoints, tc.expected)
  3080  		})
  3081  	}
  3082  }
  3083  
  3084  // TestHeadlessServices tests that headless services generate the correct endpoints.
  3085  func TestHeadlessServicesHostIP(t *testing.T) {
  3086  	t.Parallel()
  3087  
  3088  	for _, tc := range []struct {
  3089  		title                    string
  3090  		targetNamespace          string
  3091  		svcNamespace             string
  3092  		svcName                  string
  3093  		svcType                  v1.ServiceType
  3094  		compatibility            string
  3095  		fqdnTemplate             string
  3096  		ignoreHostnameAnnotation bool
  3097  		labels                   map[string]string
  3098  		annotations              map[string]string
  3099  		clusterIP                string
  3100  		hostIPs                  []string
  3101  		selector                 map[string]string
  3102  		lbs                      []string
  3103  		podnames                 []string
  3104  		hostnames                []string
  3105  		podsReady                []bool
  3106  		targetRefs               []*v1.ObjectReference
  3107  		publishNotReadyAddresses bool
  3108  		expected                 []*endpoint.Endpoint
  3109  		expectError              bool
  3110  	}{
  3111  		{
  3112  			"annotated Headless services return IPv4 endpoints for each selected Pod",
  3113  			"",
  3114  			"testing",
  3115  			"foo",
  3116  			v1.ServiceTypeClusterIP,
  3117  			"",
  3118  			"",
  3119  			false,
  3120  			map[string]string{"component": "foo"},
  3121  			map[string]string{
  3122  				hostnameAnnotationKey: "service.example.org",
  3123  			},
  3124  			v1.ClusterIPNone,
  3125  			[]string{"1.1.1.1", "1.1.1.2"},
  3126  			map[string]string{
  3127  				"component": "foo",
  3128  			},
  3129  			[]string{},
  3130  			[]string{"foo-0", "foo-1"},
  3131  			[]string{"foo-0", "foo-1"},
  3132  			[]bool{true, true},
  3133  			[]*v1.ObjectReference{
  3134  				{APIVersion: "", Kind: "Pod", Name: "foo-0"},
  3135  				{APIVersion: "", Kind: "Pod", Name: "foo-1"},
  3136  			},
  3137  			false,
  3138  			[]*endpoint.Endpoint{
  3139  				{DNSName: "foo-0.service.example.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"1.1.1.1"}},
  3140  				{DNSName: "foo-1.service.example.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"1.1.1.2"}},
  3141  				{DNSName: "service.example.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"1.1.1.1", "1.1.1.2"}},
  3142  			},
  3143  			false,
  3144  		},
  3145  		{
  3146  			"annotated Headless services return IPv6 endpoints for each selected Pod",
  3147  			"",
  3148  			"testing",
  3149  			"foo",
  3150  			v1.ServiceTypeClusterIP,
  3151  			"",
  3152  			"",
  3153  			false,
  3154  			map[string]string{"component": "foo"},
  3155  			map[string]string{
  3156  				hostnameAnnotationKey: "service.example.org",
  3157  			},
  3158  			v1.ClusterIPNone,
  3159  			[]string{"2001:db8::1", "2001:db8::2"},
  3160  			map[string]string{
  3161  				"component": "foo",
  3162  			},
  3163  			[]string{},
  3164  			[]string{"foo-0", "foo-1"},
  3165  			[]string{"foo-0", "foo-1"},
  3166  			[]bool{true, true},
  3167  			[]*v1.ObjectReference{
  3168  				{APIVersion: "", Kind: "Pod", Name: "foo-0"},
  3169  				{APIVersion: "", Kind: "Pod", Name: "foo-1"},
  3170  			},
  3171  			false,
  3172  			[]*endpoint.Endpoint{
  3173  				{DNSName: "foo-0.service.example.org", RecordType: endpoint.RecordTypeAAAA, Targets: endpoint.Targets{"2001:db8::1"}},
  3174  				{DNSName: "foo-1.service.example.org", RecordType: endpoint.RecordTypeAAAA, Targets: endpoint.Targets{"2001:db8::2"}},
  3175  				{DNSName: "service.example.org", RecordType: endpoint.RecordTypeAAAA, Targets: endpoint.Targets{"2001:db8::1", "2001:db8::2"}},
  3176  			},
  3177  			false,
  3178  		},
  3179  		{
  3180  			"hostname annotated Headless services are ignored",
  3181  			"",
  3182  			"testing",
  3183  			"foo",
  3184  			v1.ServiceTypeClusterIP,
  3185  			"",
  3186  			"",
  3187  			true,
  3188  			map[string]string{"component": "foo"},
  3189  			map[string]string{
  3190  				hostnameAnnotationKey: "service.example.org",
  3191  			},
  3192  			v1.ClusterIPNone,
  3193  			[]string{"1.1.1.1", "1.1.1.2"},
  3194  			map[string]string{
  3195  				"component": "foo",
  3196  			},
  3197  			[]string{},
  3198  			[]string{"foo-0", "foo-1"},
  3199  			[]string{"foo-0", "foo-1"},
  3200  			[]bool{true, true},
  3201  			[]*v1.ObjectReference{
  3202  				{APIVersion: "", Kind: "Pod", Name: "foo-0"},
  3203  				{APIVersion: "", Kind: "Pod", Name: "foo-1"},
  3204  			},
  3205  			false,
  3206  			[]*endpoint.Endpoint{},
  3207  			false,
  3208  		},
  3209  		{
  3210  			"annotated Headless services return IPv4 endpoints with TTL for each selected Pod",
  3211  			"",
  3212  			"testing",
  3213  			"foo",
  3214  			v1.ServiceTypeClusterIP,
  3215  			"",
  3216  			"",
  3217  			false,
  3218  			map[string]string{"component": "foo"},
  3219  			map[string]string{
  3220  				hostnameAnnotationKey: "service.example.org",
  3221  				ttlAnnotationKey:      "1",
  3222  			},
  3223  			v1.ClusterIPNone,
  3224  			[]string{"1.1.1.1", "1.1.1.2"},
  3225  			map[string]string{
  3226  				"component": "foo",
  3227  			},
  3228  			[]string{},
  3229  			[]string{"foo-0", "foo-1"},
  3230  			[]string{"foo-0", "foo-1"},
  3231  			[]bool{true, true},
  3232  			[]*v1.ObjectReference{
  3233  				{APIVersion: "", Kind: "Pod", Name: "foo-0"},
  3234  				{APIVersion: "", Kind: "Pod", Name: "foo-1"},
  3235  			},
  3236  			false,
  3237  			[]*endpoint.Endpoint{
  3238  				{DNSName: "foo-0.service.example.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"1.1.1.1"}, RecordTTL: endpoint.TTL(1)},
  3239  				{DNSName: "foo-1.service.example.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"1.1.1.2"}, RecordTTL: endpoint.TTL(1)},
  3240  				{DNSName: "service.example.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"1.1.1.1", "1.1.1.2"}, RecordTTL: endpoint.TTL(1)},
  3241  			},
  3242  			false,
  3243  		},
  3244  		{
  3245  			"annotated Headless services return IPv6 endpoints with TTL for each selected Pod",
  3246  			"",
  3247  			"testing",
  3248  			"foo",
  3249  			v1.ServiceTypeClusterIP,
  3250  			"",
  3251  			"",
  3252  			false,
  3253  			map[string]string{"component": "foo"},
  3254  			map[string]string{
  3255  				hostnameAnnotationKey: "service.example.org",
  3256  				ttlAnnotationKey:      "1",
  3257  			},
  3258  			v1.ClusterIPNone,
  3259  			[]string{"2001:db8::1", "2001:db8::2"},
  3260  			map[string]string{
  3261  				"component": "foo",
  3262  			},
  3263  			[]string{},
  3264  			[]string{"foo-0", "foo-1"},
  3265  			[]string{"foo-0", "foo-1"},
  3266  			[]bool{true, true},
  3267  			[]*v1.ObjectReference{
  3268  				{APIVersion: "", Kind: "Pod", Name: "foo-0"},
  3269  				{APIVersion: "", Kind: "Pod", Name: "foo-1"},
  3270  			},
  3271  			false,
  3272  			[]*endpoint.Endpoint{
  3273  				{DNSName: "foo-0.service.example.org", RecordType: endpoint.RecordTypeAAAA, Targets: endpoint.Targets{"2001:db8::1"}, RecordTTL: endpoint.TTL(1)},
  3274  				{DNSName: "foo-1.service.example.org", RecordType: endpoint.RecordTypeAAAA, Targets: endpoint.Targets{"2001:db8::2"}, RecordTTL: endpoint.TTL(1)},
  3275  				{DNSName: "service.example.org", RecordType: endpoint.RecordTypeAAAA, Targets: endpoint.Targets{"2001:db8::1", "2001:db8::2"}, RecordTTL: endpoint.TTL(1)},
  3276  			},
  3277  			false,
  3278  		},
  3279  		{
  3280  			"annotated Headless services return endpoints for each selected Pod, which are in running state",
  3281  			"",
  3282  			"testing",
  3283  			"foo",
  3284  			v1.ServiceTypeClusterIP,
  3285  			"",
  3286  			"",
  3287  			false,
  3288  			map[string]string{"component": "foo"},
  3289  			map[string]string{
  3290  				hostnameAnnotationKey: "service.example.org",
  3291  			},
  3292  			v1.ClusterIPNone,
  3293  			[]string{"1.1.1.1", "1.1.1.2"},
  3294  			map[string]string{
  3295  				"component": "foo",
  3296  			},
  3297  			[]string{},
  3298  			[]string{"foo-0", "foo-1"},
  3299  			[]string{"foo-0", "foo-1"},
  3300  			[]bool{true, false},
  3301  			[]*v1.ObjectReference{
  3302  				{APIVersion: "", Kind: "Pod", Name: "foo-0"},
  3303  				{APIVersion: "", Kind: "Pod", Name: "foo-1"},
  3304  			},
  3305  			false,
  3306  			[]*endpoint.Endpoint{
  3307  				{DNSName: "foo-0.service.example.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"1.1.1.1"}},
  3308  				{DNSName: "service.example.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"1.1.1.1"}},
  3309  			},
  3310  			false,
  3311  		},
  3312  		{
  3313  			"annotated Headless services return endpoints for all Pod if publishNotReadyAddresses is set",
  3314  			"",
  3315  			"testing",
  3316  			"foo",
  3317  			v1.ServiceTypeClusterIP,
  3318  			"",
  3319  			"",
  3320  			false,
  3321  			map[string]string{"component": "foo"},
  3322  			map[string]string{
  3323  				hostnameAnnotationKey: "service.example.org",
  3324  			},
  3325  			v1.ClusterIPNone,
  3326  			[]string{"1.1.1.1", "1.1.1.2"},
  3327  			map[string]string{
  3328  				"component": "foo",
  3329  			},
  3330  			[]string{},
  3331  			[]string{"foo-0", "foo-1"},
  3332  			[]string{"foo-0", "foo-1"},
  3333  			[]bool{true, false},
  3334  			[]*v1.ObjectReference{
  3335  				{APIVersion: "", Kind: "Pod", Name: "foo-0"},
  3336  				{APIVersion: "", Kind: "Pod", Name: "foo-1"},
  3337  			},
  3338  			true,
  3339  			[]*endpoint.Endpoint{
  3340  				{DNSName: "foo-0.service.example.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"1.1.1.1"}},
  3341  				{DNSName: "foo-1.service.example.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"1.1.1.2"}},
  3342  				{DNSName: "service.example.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"1.1.1.1", "1.1.1.2"}},
  3343  			},
  3344  			false,
  3345  		},
  3346  		{
  3347  			"annotated Headless services return IPv4 endpoints for pods missing hostname",
  3348  			"",
  3349  			"testing",
  3350  			"foo",
  3351  			v1.ServiceTypeClusterIP,
  3352  			"",
  3353  			"",
  3354  			false,
  3355  			map[string]string{"component": "foo"},
  3356  			map[string]string{
  3357  				hostnameAnnotationKey: "service.example.org",
  3358  			},
  3359  			v1.ClusterIPNone,
  3360  			[]string{"1.1.1.1", "1.1.1.2"},
  3361  			map[string]string{
  3362  				"component": "foo",
  3363  			},
  3364  			[]string{},
  3365  			[]string{"foo-0", "foo-1"},
  3366  			[]string{"", ""},
  3367  			[]bool{true, true},
  3368  			[]*v1.ObjectReference{
  3369  				{APIVersion: "", Kind: "Pod", Name: "foo-0"},
  3370  				{APIVersion: "", Kind: "Pod", Name: "foo-1"},
  3371  			},
  3372  			false,
  3373  			[]*endpoint.Endpoint{
  3374  				{DNSName: "service.example.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"1.1.1.1", "1.1.1.2"}},
  3375  			},
  3376  			false,
  3377  		},
  3378  		{
  3379  			"annotated Headless services return IPv6 endpoints for pods missing hostname",
  3380  			"",
  3381  			"testing",
  3382  			"foo",
  3383  			v1.ServiceTypeClusterIP,
  3384  			"",
  3385  			"",
  3386  			false,
  3387  			map[string]string{"component": "foo"},
  3388  			map[string]string{
  3389  				hostnameAnnotationKey: "service.example.org",
  3390  			},
  3391  			v1.ClusterIPNone,
  3392  			[]string{"2001:db8::1", "2001:db8::2"},
  3393  			map[string]string{
  3394  				"component": "foo",
  3395  			},
  3396  			[]string{},
  3397  			[]string{"foo-0", "foo-1"},
  3398  			[]string{"", ""},
  3399  			[]bool{true, true},
  3400  			[]*v1.ObjectReference{
  3401  				{APIVersion: "", Kind: "Pod", Name: "foo-0"},
  3402  				{APIVersion: "", Kind: "Pod", Name: "foo-1"},
  3403  			},
  3404  			false,
  3405  			[]*endpoint.Endpoint{
  3406  				{DNSName: "service.example.org", RecordType: endpoint.RecordTypeAAAA, Targets: endpoint.Targets{"2001:db8::1", "2001:db8::2"}},
  3407  			},
  3408  			false,
  3409  		},
  3410  		{
  3411  			"annotated Headless services without a targetRef has no endpoints",
  3412  			"",
  3413  			"testing",
  3414  			"foo",
  3415  			v1.ServiceTypeClusterIP,
  3416  			"",
  3417  			"",
  3418  			false,
  3419  			map[string]string{"component": "foo"},
  3420  			map[string]string{
  3421  				hostnameAnnotationKey: "service.example.org",
  3422  			},
  3423  			v1.ClusterIPNone,
  3424  			[]string{"1.1.1.1"},
  3425  			map[string]string{
  3426  				"component": "foo",
  3427  			},
  3428  			[]string{},
  3429  			[]string{"foo-0"},
  3430  			[]string{"foo-0"},
  3431  			[]bool{true, true},
  3432  			[]*v1.ObjectReference{nil},
  3433  			false,
  3434  			[]*endpoint.Endpoint{},
  3435  			false,
  3436  		},
  3437  	} {
  3438  		tc := tc
  3439  		t.Run(tc.title, func(t *testing.T) {
  3440  			t.Parallel()
  3441  
  3442  			// Create a Kubernetes testing client
  3443  			kubernetes := fake.NewSimpleClientset()
  3444  
  3445  			service := &v1.Service{
  3446  				Spec: v1.ServiceSpec{
  3447  					Type:                     tc.svcType,
  3448  					ClusterIP:                tc.clusterIP,
  3449  					Selector:                 tc.selector,
  3450  					PublishNotReadyAddresses: tc.publishNotReadyAddresses,
  3451  				},
  3452  				ObjectMeta: metav1.ObjectMeta{
  3453  					Namespace:   tc.svcNamespace,
  3454  					Name:        tc.svcName,
  3455  					Labels:      tc.labels,
  3456  					Annotations: tc.annotations,
  3457  				},
  3458  				Status: v1.ServiceStatus{},
  3459  			}
  3460  			_, err := kubernetes.CoreV1().Services(service.Namespace).Create(context.Background(), service, metav1.CreateOptions{})
  3461  			require.NoError(t, err)
  3462  
  3463  			var addresses []v1.EndpointAddress
  3464  			var notReadyAddresses []v1.EndpointAddress
  3465  			for i, podname := range tc.podnames {
  3466  				pod := &v1.Pod{
  3467  					Spec: v1.PodSpec{
  3468  						Containers: []v1.Container{},
  3469  						Hostname:   tc.hostnames[i],
  3470  					},
  3471  					ObjectMeta: metav1.ObjectMeta{
  3472  						Namespace:   tc.svcNamespace,
  3473  						Name:        podname,
  3474  						Labels:      tc.labels,
  3475  						Annotations: tc.annotations,
  3476  					},
  3477  					Status: v1.PodStatus{
  3478  						HostIP: tc.hostIPs[i],
  3479  					},
  3480  				}
  3481  
  3482  				_, err = kubernetes.CoreV1().Pods(tc.svcNamespace).Create(context.Background(), pod, metav1.CreateOptions{})
  3483  				require.NoError(t, err)
  3484  
  3485  				address := v1.EndpointAddress{
  3486  					IP:        "4.3.2.1",
  3487  					TargetRef: tc.targetRefs[i],
  3488  				}
  3489  				if tc.podsReady[i] {
  3490  					addresses = append(addresses, address)
  3491  				} else {
  3492  					notReadyAddresses = append(notReadyAddresses, address)
  3493  				}
  3494  			}
  3495  			endpointsObject := &v1.Endpoints{
  3496  				ObjectMeta: metav1.ObjectMeta{
  3497  					Namespace: tc.svcNamespace,
  3498  					Name:      tc.svcName,
  3499  					Labels:    tc.labels,
  3500  				},
  3501  				Subsets: []v1.EndpointSubset{
  3502  					{
  3503  						Addresses:         addresses,
  3504  						NotReadyAddresses: notReadyAddresses,
  3505  					},
  3506  				},
  3507  			}
  3508  			_, err = kubernetes.CoreV1().Endpoints(tc.svcNamespace).Create(context.Background(), endpointsObject, metav1.CreateOptions{})
  3509  			require.NoError(t, err)
  3510  
  3511  			// Create our object under test and get the endpoints.
  3512  			client, _ := NewServiceSource(
  3513  				context.TODO(),
  3514  				kubernetes,
  3515  				tc.targetNamespace,
  3516  				"",
  3517  				tc.fqdnTemplate,
  3518  				false,
  3519  				tc.compatibility,
  3520  				true,
  3521  				true,
  3522  				false,
  3523  				[]string{},
  3524  				tc.ignoreHostnameAnnotation,
  3525  				labels.Everything(),
  3526  				false,
  3527  			)
  3528  			require.NoError(t, err)
  3529  
  3530  			endpoints, err := client.Endpoints(context.Background())
  3531  			if tc.expectError {
  3532  				require.Error(t, err)
  3533  			} else {
  3534  				require.NoError(t, err)
  3535  			}
  3536  
  3537  			// Validate returned endpoints against desired endpoints.
  3538  			validateEndpoints(t, endpoints, tc.expected)
  3539  		})
  3540  	}
  3541  }
  3542  
  3543  // TestExternalServices tests that external services generate the correct endpoints.
  3544  func TestExternalServices(t *testing.T) {
  3545  	t.Parallel()
  3546  
  3547  	for _, tc := range []struct {
  3548  		title                    string
  3549  		targetNamespace          string
  3550  		svcNamespace             string
  3551  		svcName                  string
  3552  		svcType                  v1.ServiceType
  3553  		compatibility            string
  3554  		fqdnTemplate             string
  3555  		ignoreHostnameAnnotation bool
  3556  		labels                   map[string]string
  3557  		annotations              map[string]string
  3558  		externalName             string
  3559  		externalIPs              []string
  3560  		expected                 []*endpoint.Endpoint
  3561  		expectError              bool
  3562  	}{
  3563  		{
  3564  			"external services return an A endpoint for the external name that is an IPv4 address",
  3565  			"",
  3566  			"testing",
  3567  			"foo",
  3568  			v1.ServiceTypeExternalName,
  3569  			"",
  3570  			"",
  3571  			false,
  3572  			map[string]string{"component": "foo"},
  3573  			map[string]string{
  3574  				hostnameAnnotationKey: "service.example.org",
  3575  			},
  3576  			"111.111.111.111",
  3577  			[]string{},
  3578  			[]*endpoint.Endpoint{
  3579  				{DNSName: "service.example.org", Targets: endpoint.Targets{"111.111.111.111"}, RecordType: endpoint.RecordTypeA},
  3580  			},
  3581  			false,
  3582  		},
  3583  		{
  3584  			"external services return an AAAA endpoint for the external name that is an IPv6 address",
  3585  			"",
  3586  			"testing",
  3587  			"foo",
  3588  			v1.ServiceTypeExternalName,
  3589  			"",
  3590  			"",
  3591  			false,
  3592  			map[string]string{"component": "foo"},
  3593  			map[string]string{
  3594  				hostnameAnnotationKey: "service.example.org",
  3595  			},
  3596  			"2001:db8::111",
  3597  			[]string{},
  3598  			[]*endpoint.Endpoint{
  3599  				{DNSName: "service.example.org", Targets: endpoint.Targets{"2001:db8::111"}, RecordType: endpoint.RecordTypeAAAA},
  3600  			},
  3601  			false,
  3602  		},
  3603  		{
  3604  			"external services return a CNAME endpoint for the external name that is a domain",
  3605  			"",
  3606  			"testing",
  3607  			"foo",
  3608  			v1.ServiceTypeExternalName,
  3609  			"",
  3610  			"",
  3611  			false,
  3612  			map[string]string{"component": "foo"},
  3613  			map[string]string{
  3614  				hostnameAnnotationKey: "service.example.org",
  3615  			},
  3616  			"remote.example.com",
  3617  			[]string{},
  3618  			[]*endpoint.Endpoint{
  3619  				{DNSName: "service.example.org", Targets: endpoint.Targets{"remote.example.com"}, RecordType: endpoint.RecordTypeCNAME},
  3620  			},
  3621  			false,
  3622  		},
  3623  		{
  3624  			"annotated ExternalName service with externalIPs returns a single endpoint with multiple targets",
  3625  			"",
  3626  			"testing",
  3627  			"foo",
  3628  			v1.ServiceTypeExternalName,
  3629  			"",
  3630  			"",
  3631  			false,
  3632  			map[string]string{"component": "foo"},
  3633  			map[string]string{
  3634  				hostnameAnnotationKey: "service.example.org",
  3635  			},
  3636  			"service.example.org",
  3637  			[]string{"10.2.3.4", "11.2.3.4"},
  3638  			[]*endpoint.Endpoint{
  3639  				{DNSName: "service.example.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"10.2.3.4", "11.2.3.4"}},
  3640  			},
  3641  			false,
  3642  		},
  3643  		{
  3644  			"annotated ExternalName service with externalIPs of dualstack addresses returns 2 endpoints with multiple targets",
  3645  			"",
  3646  			"testing",
  3647  			"foo",
  3648  			v1.ServiceTypeExternalName,
  3649  			"",
  3650  			"",
  3651  			false,
  3652  			map[string]string{"component": "foo"},
  3653  			map[string]string{
  3654  				hostnameAnnotationKey: "service.example.org",
  3655  			},
  3656  			"service.example.org",
  3657  			[]string{"10.2.3.4", "11.2.3.4", "2001:db8::1", "2001:db8::2"},
  3658  			[]*endpoint.Endpoint{
  3659  				{DNSName: "service.example.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"10.2.3.4", "11.2.3.4"}},
  3660  				{DNSName: "service.example.org", RecordType: endpoint.RecordTypeAAAA, Targets: endpoint.Targets{"2001:db8::1", "2001:db8::2"}},
  3661  			},
  3662  			false,
  3663  		},
  3664  	} {
  3665  		tc := tc
  3666  		t.Run(tc.title, func(t *testing.T) {
  3667  			t.Parallel()
  3668  
  3669  			// Create a Kubernetes testing client
  3670  			kubernetes := fake.NewSimpleClientset()
  3671  
  3672  			service := &v1.Service{
  3673  				Spec: v1.ServiceSpec{
  3674  					Type:         tc.svcType,
  3675  					ExternalName: tc.externalName,
  3676  					ExternalIPs:  tc.externalIPs,
  3677  				},
  3678  				ObjectMeta: metav1.ObjectMeta{
  3679  					Namespace:   tc.svcNamespace,
  3680  					Name:        tc.svcName,
  3681  					Labels:      tc.labels,
  3682  					Annotations: tc.annotations,
  3683  				},
  3684  				Status: v1.ServiceStatus{},
  3685  			}
  3686  			_, err := kubernetes.CoreV1().Services(service.Namespace).Create(context.Background(), service, metav1.CreateOptions{})
  3687  			require.NoError(t, err)
  3688  
  3689  			// Create our object under test and get the endpoints.
  3690  			client, _ := NewServiceSource(
  3691  				context.TODO(),
  3692  				kubernetes,
  3693  				tc.targetNamespace,
  3694  				"",
  3695  				tc.fqdnTemplate,
  3696  				false,
  3697  				tc.compatibility,
  3698  				true,
  3699  				false,
  3700  				false,
  3701  				[]string{},
  3702  				tc.ignoreHostnameAnnotation,
  3703  				labels.Everything(),
  3704  				false,
  3705  			)
  3706  			require.NoError(t, err)
  3707  
  3708  			endpoints, err := client.Endpoints(context.Background())
  3709  			if tc.expectError {
  3710  				require.Error(t, err)
  3711  			} else {
  3712  				require.NoError(t, err)
  3713  			}
  3714  
  3715  			// Validate returned endpoints against desired endpoints.
  3716  			validateEndpoints(t, endpoints, tc.expected)
  3717  		})
  3718  	}
  3719  }
  3720  
  3721  func BenchmarkServiceEndpoints(b *testing.B) {
  3722  	kubernetes := fake.NewSimpleClientset()
  3723  
  3724  	service := &v1.Service{
  3725  		ObjectMeta: metav1.ObjectMeta{
  3726  			Namespace: "testing",
  3727  			Name:      "foo",
  3728  			Annotations: map[string]string{
  3729  				hostnameAnnotationKey: "foo.example.org.",
  3730  			},
  3731  		},
  3732  		Status: v1.ServiceStatus{
  3733  			LoadBalancer: v1.LoadBalancerStatus{
  3734  				Ingress: []v1.LoadBalancerIngress{
  3735  					{IP: "1.2.3.4"},
  3736  					{IP: "8.8.8.8"},
  3737  				},
  3738  			},
  3739  		},
  3740  	}
  3741  
  3742  	_, err := kubernetes.CoreV1().Services(service.Namespace).Create(context.Background(), service, metav1.CreateOptions{})
  3743  	require.NoError(b, err)
  3744  
  3745  	client, err := NewServiceSource(
  3746  		context.TODO(),
  3747  		kubernetes,
  3748  		v1.NamespaceAll,
  3749  		"",
  3750  		"",
  3751  		false,
  3752  		"",
  3753  		false,
  3754  		false,
  3755  		false,
  3756  		[]string{},
  3757  		false,
  3758  		labels.Everything(),
  3759  		false,
  3760  	)
  3761  	require.NoError(b, err)
  3762  
  3763  	for i := 0; i < b.N; i++ {
  3764  		_, err := client.Endpoints(context.Background())
  3765  		require.NoError(b, err)
  3766  	}
  3767  }