mirror of
				https://github.com/kubernetes-sigs/external-dns.git
				synced 2025-10-31 02:31:00 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			331 lines
		
	
	
		
			8.2 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			331 lines
		
	
	
		
			8.2 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| package source
 | |
| 
 | |
| import (
 | |
| 	"testing"
 | |
| 
 | |
| 	"github.com/kubernetes-sigs/external-dns/endpoint"
 | |
| 	"github.com/stretchr/testify/assert"
 | |
| 	"github.com/stretchr/testify/require"
 | |
| 	v1 "k8s.io/api/core/v1"
 | |
| 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 | |
| 	"k8s.io/client-go/kubernetes/fake"
 | |
| )
 | |
| 
 | |
| func TestNodeSource(t *testing.T) {
 | |
| 	t.Run("NewNodeSource", testNodeSourceNewNodeSource)
 | |
| 	t.Run("Endpoints", testNodeSourceEndpoints)
 | |
| }
 | |
| 
 | |
| // testNodeSourceNewNodeSource tests that NewNodeService doesn't return an error.
 | |
| func testNodeSourceNewNodeSource(t *testing.T) {
 | |
| 	for _, ti := range []struct {
 | |
| 		title            string
 | |
| 		annotationFilter string
 | |
| 		fqdnTemplate     string
 | |
| 		expectError      bool
 | |
| 	}{
 | |
| 		{
 | |
| 			title:        "invalid template",
 | |
| 			expectError:  true,
 | |
| 			fqdnTemplate: "{{.Name",
 | |
| 		},
 | |
| 		{
 | |
| 			title:       "valid empty template",
 | |
| 			expectError: false,
 | |
| 		},
 | |
| 		{
 | |
| 			title:        "valid template",
 | |
| 			expectError:  false,
 | |
| 			fqdnTemplate: "{{.Name}}-{{.Namespace}}.ext-dns.test.com",
 | |
| 		},
 | |
| 		{
 | |
| 			title:            "non-empty annotation filter label",
 | |
| 			expectError:      false,
 | |
| 			annotationFilter: "kubernetes.io/ingress.class=nginx",
 | |
| 		},
 | |
| 	} {
 | |
| 		t.Run(ti.title, func(t *testing.T) {
 | |
| 			_, err := NewNodeSource(
 | |
| 				fake.NewSimpleClientset(),
 | |
| 				ti.annotationFilter,
 | |
| 				ti.fqdnTemplate,
 | |
| 			)
 | |
| 
 | |
| 			if ti.expectError {
 | |
| 				assert.Error(t, err)
 | |
| 			} else {
 | |
| 				assert.NoError(t, err)
 | |
| 			}
 | |
| 		})
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // testNodeSourceEndpoints tests that various node generate the correct endpoints.
 | |
| func testNodeSourceEndpoints(t *testing.T) {
 | |
| 	for _, tc := range []struct {
 | |
| 		title            string
 | |
| 		annotationFilter string
 | |
| 		fqdnTemplate     string
 | |
| 		nodeName         string
 | |
| 		nodeAddresses    []v1.NodeAddress
 | |
| 		labels           map[string]string
 | |
| 		annotations      map[string]string
 | |
| 		expected         []*endpoint.Endpoint
 | |
| 		expectError      bool
 | |
| 	}{
 | |
| 		{
 | |
| 			"node with short hostname returns one endpoint",
 | |
| 			"",
 | |
| 			"",
 | |
| 			"node1",
 | |
| 			[]v1.NodeAddress{{v1.NodeExternalIP, "1.2.3.4"}},
 | |
| 			map[string]string{},
 | |
| 			map[string]string{},
 | |
| 			[]*endpoint.Endpoint{
 | |
| 				{RecordType: "A", DNSName: "node1", Targets: endpoint.Targets{"1.2.3.4"}},
 | |
| 			},
 | |
| 			false,
 | |
| 		},
 | |
| 		{
 | |
| 			"node with fqdn returns one endpoint",
 | |
| 			"",
 | |
| 			"",
 | |
| 			"node1.example.org",
 | |
| 			[]v1.NodeAddress{{v1.NodeExternalIP, "1.2.3.4"}},
 | |
| 			map[string]string{},
 | |
| 			map[string]string{},
 | |
| 			[]*endpoint.Endpoint{
 | |
| 				{RecordType: "A", DNSName: "node1.example.org", Targets: endpoint.Targets{"1.2.3.4"}},
 | |
| 			},
 | |
| 			false,
 | |
| 		},
 | |
| 		{
 | |
| 			"node with fqdn template returns endpoint with expanded hostname",
 | |
| 			"",
 | |
| 			"{{.Name}}.example.org",
 | |
| 			"node1",
 | |
| 			[]v1.NodeAddress{{v1.NodeExternalIP, "1.2.3.4"}},
 | |
| 			map[string]string{},
 | |
| 			map[string]string{},
 | |
| 			[]*endpoint.Endpoint{
 | |
| 				{RecordType: "A", DNSName: "node1.example.org", Targets: endpoint.Targets{"1.2.3.4"}},
 | |
| 			},
 | |
| 			false,
 | |
| 		},
 | |
| 		{
 | |
| 			"node with fqdn and fqdn template returns one endpoint",
 | |
| 			"",
 | |
| 			"{{.Name}}.example.org",
 | |
| 			"node1.example.org",
 | |
| 			[]v1.NodeAddress{{v1.NodeExternalIP, "1.2.3.4"}},
 | |
| 			map[string]string{},
 | |
| 			map[string]string{},
 | |
| 			[]*endpoint.Endpoint{
 | |
| 				{RecordType: "A", DNSName: "node1.example.org.example.org", Targets: endpoint.Targets{"1.2.3.4"}},
 | |
| 			},
 | |
| 			false,
 | |
| 		},
 | |
| 		{
 | |
| 			"node with fqdn template returns two endpoints with multiple IP addresses and expanded hostname",
 | |
| 			"",
 | |
| 			"{{.Name}}.example.org",
 | |
| 			"node1",
 | |
| 			[]v1.NodeAddress{{v1.NodeExternalIP, "1.2.3.4"}, {v1.NodeExternalIP, "5.6.7.8"}},
 | |
| 			map[string]string{},
 | |
| 			map[string]string{},
 | |
| 			[]*endpoint.Endpoint{
 | |
| 				{RecordType: "A", DNSName: "node1.example.org", Targets: endpoint.Targets{"1.2.3.4", "5.6.7.8"}},
 | |
| 			},
 | |
| 			false,
 | |
| 		},
 | |
| 		{
 | |
| 			"node with both external and internal IP returns an endpoint with external IP",
 | |
| 			"",
 | |
| 			"",
 | |
| 			"node1",
 | |
| 			[]v1.NodeAddress{{v1.NodeExternalIP, "1.2.3.4"}, {v1.NodeInternalIP, "2.3.4.5"}},
 | |
| 			map[string]string{},
 | |
| 			map[string]string{},
 | |
| 			[]*endpoint.Endpoint{
 | |
| 				{RecordType: "A", DNSName: "node1", Targets: endpoint.Targets{"1.2.3.4"}},
 | |
| 			},
 | |
| 			false,
 | |
| 		},
 | |
| 		{
 | |
| 			"node with only internal IP returns an endpoint with internal IP",
 | |
| 			"",
 | |
| 			"",
 | |
| 			"node1",
 | |
| 			[]v1.NodeAddress{{v1.NodeInternalIP, "2.3.4.5"}},
 | |
| 			map[string]string{},
 | |
| 			map[string]string{},
 | |
| 			[]*endpoint.Endpoint{
 | |
| 				{RecordType: "A", DNSName: "node1", Targets: endpoint.Targets{"2.3.4.5"}},
 | |
| 			},
 | |
| 			false,
 | |
| 		},
 | |
| 		{
 | |
| 			"node with neither external nor internal IP returns no endpoints",
 | |
| 			"",
 | |
| 			"",
 | |
| 			"node1",
 | |
| 			[]v1.NodeAddress{},
 | |
| 			map[string]string{},
 | |
| 			map[string]string{},
 | |
| 			[]*endpoint.Endpoint{},
 | |
| 			true,
 | |
| 		},
 | |
| 		{
 | |
| 			"annotated node without annotation filter returns endpoint",
 | |
| 			"",
 | |
| 			"",
 | |
| 			"node1",
 | |
| 			[]v1.NodeAddress{{v1.NodeExternalIP, "1.2.3.4"}},
 | |
| 			map[string]string{},
 | |
| 			map[string]string{
 | |
| 				"service.beta.kubernetes.io/external-traffic": "OnlyLocal",
 | |
| 			},
 | |
| 			[]*endpoint.Endpoint{
 | |
| 				{RecordType: "A", DNSName: "node1", Targets: endpoint.Targets{"1.2.3.4"}},
 | |
| 			},
 | |
| 			false,
 | |
| 		},
 | |
| 		{
 | |
| 			"annotated node with matching annotation filter returns endpoint",
 | |
| 			"service.beta.kubernetes.io/external-traffic in (Global, OnlyLocal)",
 | |
| 			"",
 | |
| 			"node1",
 | |
| 			[]v1.NodeAddress{{v1.NodeExternalIP, "1.2.3.4"}},
 | |
| 			map[string]string{},
 | |
| 			map[string]string{
 | |
| 				"service.beta.kubernetes.io/external-traffic": "OnlyLocal",
 | |
| 			},
 | |
| 			[]*endpoint.Endpoint{
 | |
| 				{RecordType: "A", DNSName: "node1", Targets: endpoint.Targets{"1.2.3.4"}},
 | |
| 			},
 | |
| 			false,
 | |
| 		},
 | |
| 		{
 | |
| 			"annotated node with non-matching annotation filter returns endpoint",
 | |
| 			"service.beta.kubernetes.io/external-traffic in (Global, OnlyLocal)",
 | |
| 			"",
 | |
| 			"node1",
 | |
| 			[]v1.NodeAddress{{v1.NodeExternalIP, "1.2.3.4"}},
 | |
| 			map[string]string{},
 | |
| 			map[string]string{
 | |
| 				"service.beta.kubernetes.io/external-traffic": "SomethingElse",
 | |
| 			},
 | |
| 			[]*endpoint.Endpoint{},
 | |
| 			false,
 | |
| 		},
 | |
| 		{
 | |
| 			"our controller type is dns-controller",
 | |
| 			"",
 | |
| 			"",
 | |
| 			"node1",
 | |
| 			[]v1.NodeAddress{{v1.NodeExternalIP, "1.2.3.4"}},
 | |
| 			map[string]string{},
 | |
| 			map[string]string{
 | |
| 				controllerAnnotationKey: controllerAnnotationValue,
 | |
| 			},
 | |
| 			[]*endpoint.Endpoint{
 | |
| 				{RecordType: "A", DNSName: "node1", Targets: endpoint.Targets{"1.2.3.4"}},
 | |
| 			},
 | |
| 			false,
 | |
| 		},
 | |
| 		{
 | |
| 			"different controller types are ignored",
 | |
| 			"",
 | |
| 			"",
 | |
| 			"node1",
 | |
| 			[]v1.NodeAddress{{v1.NodeExternalIP, "1.2.3.4"}},
 | |
| 			map[string]string{},
 | |
| 			map[string]string{
 | |
| 				controllerAnnotationKey: "not-dns-controller",
 | |
| 			},
 | |
| 			[]*endpoint.Endpoint{},
 | |
| 			false,
 | |
| 		},
 | |
| 		{
 | |
| 			"ttl not annotated should have RecordTTL.IsConfigured set to false",
 | |
| 			"",
 | |
| 			"",
 | |
| 			"node1",
 | |
| 			[]v1.NodeAddress{{v1.NodeExternalIP, "1.2.3.4"}},
 | |
| 			map[string]string{},
 | |
| 			map[string]string{},
 | |
| 			[]*endpoint.Endpoint{
 | |
| 				{RecordType: "A", DNSName: "node1", Targets: endpoint.Targets{"1.2.3.4"}, RecordTTL: endpoint.TTL(0)},
 | |
| 			},
 | |
| 			false,
 | |
| 		},
 | |
| 		{
 | |
| 			"ttl annotated but invalid should have RecordTTL.IsConfigured set to false",
 | |
| 			"",
 | |
| 			"",
 | |
| 			"node1",
 | |
| 			[]v1.NodeAddress{{v1.NodeExternalIP, "1.2.3.4"}},
 | |
| 			map[string]string{},
 | |
| 			map[string]string{
 | |
| 				ttlAnnotationKey: "foo",
 | |
| 			},
 | |
| 			[]*endpoint.Endpoint{
 | |
| 				{RecordType: "A", DNSName: "node1", Targets: endpoint.Targets{"1.2.3.4"}, RecordTTL: endpoint.TTL(0)},
 | |
| 			},
 | |
| 			false,
 | |
| 		},
 | |
| 		{
 | |
| 			"ttl annotated and is valid should set Record.TTL",
 | |
| 			"",
 | |
| 			"",
 | |
| 			"node1",
 | |
| 			[]v1.NodeAddress{{v1.NodeExternalIP, "1.2.3.4"}},
 | |
| 			map[string]string{},
 | |
| 			map[string]string{
 | |
| 				ttlAnnotationKey: "10",
 | |
| 			},
 | |
| 			[]*endpoint.Endpoint{
 | |
| 				{RecordType: "A", DNSName: "node1", Targets: endpoint.Targets{"1.2.3.4"}, RecordTTL: endpoint.TTL(10)},
 | |
| 			},
 | |
| 			false,
 | |
| 		},
 | |
| 	} {
 | |
| 		t.Run(tc.title, func(t *testing.T) {
 | |
| 			// Create a Kubernetes testing client
 | |
| 			kubernetes := fake.NewSimpleClientset()
 | |
| 
 | |
| 			node := &v1.Node{
 | |
| 				ObjectMeta: metav1.ObjectMeta{
 | |
| 					Name:        tc.nodeName,
 | |
| 					Labels:      tc.labels,
 | |
| 					Annotations: tc.annotations,
 | |
| 				},
 | |
| 				Status: v1.NodeStatus{
 | |
| 					Addresses: tc.nodeAddresses,
 | |
| 				},
 | |
| 			}
 | |
| 
 | |
| 			_, err := kubernetes.CoreV1().Nodes().Create(node)
 | |
| 			require.NoError(t, err)
 | |
| 
 | |
| 			// Create our object under test and get the endpoints.
 | |
| 			client, err := NewNodeSource(
 | |
| 				kubernetes,
 | |
| 				tc.annotationFilter,
 | |
| 				tc.fqdnTemplate,
 | |
| 			)
 | |
| 			require.NoError(t, err)
 | |
| 
 | |
| 			endpoints, err := client.Endpoints()
 | |
| 			if tc.expectError {
 | |
| 				require.Error(t, err)
 | |
| 			} else {
 | |
| 				require.NoError(t, err)
 | |
| 			}
 | |
| 
 | |
| 			// Validate returned endpoints against desired endpoints.
 | |
| 			validateEndpoints(t, endpoints, tc.expected)
 | |
| 		})
 | |
| 	}
 | |
| }
 |