diff --git a/source/service.go b/source/service.go index 636ac1d21a..000fd628a1 100644 --- a/source/service.go +++ b/source/service.go @@ -82,6 +82,7 @@ type serviceSource struct { nodeInformer coreinformers.NodeInformer serviceTypeFilter *serviceTypes exposeInternalIPv6 bool + excludeUnschedulable bool // process Services with legacy annotations compatibility string @@ -98,7 +99,7 @@ func NewServiceSource( ignoreHostnameAnnotation bool, labelSelector labels.Selector, resolveLoadBalancerHostname, - listenEndpointEvents, exposeInternalIPv6 bool, + listenEndpointEvents, exposeInternalIPv6, excludeUnschedulable bool, ) (Source, error) { tmpl, err := fqdn.ParseTemplate(fqdnTemplate) if err != nil { @@ -224,6 +225,7 @@ func NewServiceSource( resolveLoadBalancerHostname: resolveLoadBalancerHostname, listenEndpointEvents: listenEndpointEvents, exposeInternalIPv6: exposeInternalIPv6, + excludeUnschedulable: excludeUnschedulable, }, nil } @@ -812,6 +814,10 @@ func (sc *serviceSource) extractNodePortTargets(svc *v1.Service) (endpoint.Targe } for _, node := range nodes { + if node.Spec.Unschedulable && sc.excludeUnschedulable { + log.Debugf("Skipping node %s - unschedulable", node.Name) + continue + } for _, address := range node.Status.Addresses { switch address.Type { case v1.NodeExternalIP: diff --git a/source/service_fqdn_test.go b/source/service_fqdn_test.go index 459cd42bbc..eb900f7144 100644 --- a/source/service_fqdn_test.go +++ b/source/service_fqdn_test.go @@ -809,6 +809,7 @@ func TestServiceSourceFqdnTemplatingExamples(t *testing.T) { false, false, true, + true, ) require.NoError(t, err) diff --git a/source/service_test.go b/source/service_test.go index 5fe1e8d5da..bec4309bc2 100644 --- a/source/service_test.go +++ b/source/service_test.go @@ -98,6 +98,7 @@ func (suite *ServiceSuite) SetupTest() { false, false, false, + false, ) suite.NoError(err, "should initialize service source") } @@ -181,6 +182,7 @@ func testServiceSourceNewServiceSource(t *testing.T) { false, false, false, + false, ) if ti.expectError { @@ -1165,6 +1167,7 @@ func testServiceSourceEndpoints(t *testing.T) { tc.resolveLoadBalancerHostname, false, false, + false, ) require.NoError(t, err) @@ -1381,6 +1384,7 @@ func testMultipleServicesEndpoints(t *testing.T) { false, false, false, + false, ) require.NoError(t, err) @@ -1686,6 +1690,7 @@ func TestClusterIpServices(t *testing.T) { false, false, false, + false, ) require.NoError(t, err) @@ -1718,6 +1723,7 @@ func TestServiceSourceNodePortServices(t *testing.T) { fqdnTemplate string ignoreHostnameAnnotation bool exposeInternalIPv6 bool + ignoreUnscheduledNodes bool labels map[string]string annotations map[string]string lbs []string @@ -2383,6 +2389,55 @@ func TestServiceSourceNodePortServices(t *testing.T) { }, }}, }, + { + title: "NodePort services ignore unschedulable node", + ignoreUnscheduledNodes: true, + svcNamespace: "testing", + svcName: "foo", + svcType: v1.ServiceTypeNodePort, + svcTrafficPolicy: v1.ServiceExternalTrafficPolicyTypeCluster, + labels: map[string]string{}, + annotations: map[string]string{ + annotations.HostnameKey: "foo.example.org.", + annotations.AccessKey: "public", + }, + expected: []*endpoint.Endpoint{ + {DNSName: "_foo._tcp.foo.example.org", Targets: endpoint.Targets{"0 50 30192 foo.example.org."}, RecordType: endpoint.RecordTypeSRV}, + {DNSName: "foo.example.org", Targets: endpoint.Targets{"54.10.11.2"}, RecordType: endpoint.RecordTypeA}, + {DNSName: "foo.example.org", Targets: endpoint.Targets{"2001:DB8::3"}, RecordType: endpoint.RecordTypeAAAA}, + }, + nodes: []*v1.Node{{ + ObjectMeta: metav1.ObjectMeta{ + Name: "node1", + }, + Spec: v1.NodeSpec{ + Unschedulable: true, + }, + Status: v1.NodeStatus{ + Addresses: []v1.NodeAddress{ + {Type: v1.NodeExternalIP, Address: "54.10.11.1"}, + {Type: v1.NodeInternalIP, Address: "10.0.1.1"}, + {Type: v1.NodeExternalIP, Address: "2001:DB8::1"}, + {Type: v1.NodeInternalIP, Address: "2001:DB8::2"}, + }, + }, + }, { + ObjectMeta: metav1.ObjectMeta{ + Name: "node2", + }, + Spec: v1.NodeSpec{ + Unschedulable: false, + }, + Status: v1.NodeStatus{ + Addresses: []v1.NodeAddress{ + {Type: v1.NodeExternalIP, Address: "54.10.11.2"}, + {Type: v1.NodeInternalIP, Address: "10.0.1.2"}, + {Type: v1.NodeExternalIP, Address: "2001:DB8::3"}, + {Type: v1.NodeInternalIP, Address: "2001:DB8::4"}, + }, + }, + }}, + }, } { t.Run(tc.title, func(t *testing.T) { @@ -2463,6 +2518,7 @@ func TestServiceSourceNodePortServices(t *testing.T) { false, false, tc.exposeInternalIPv6, + tc.ignoreUnscheduledNodes, ) require.NoError(t, err) @@ -3371,6 +3427,7 @@ func TestHeadlessServices(t *testing.T) { false, false, tc.exposeInternalIPv6, + false, ) require.NoError(t, err) @@ -3507,6 +3564,7 @@ func TestMultipleServicesPointingToSameLoadBalancer(t *testing.T) { false, false, false, + true, ) require.NoError(t, err) assert.NotNil(t, src) @@ -3873,6 +3931,7 @@ func TestMultipleHeadlessServicesPointingToPodsOnTheSameNode(t *testing.T) { false, false, false, + true, ) require.NoError(t, err) assert.NotNil(t, src) @@ -4331,6 +4390,7 @@ func TestHeadlessServicesHostIP(t *testing.T) { false, false, false, + true, ) require.NoError(t, err) @@ -4541,6 +4601,7 @@ func TestExternalServices(t *testing.T) { false, false, false, + true, ) require.NoError(t, err) @@ -4603,6 +4664,7 @@ func BenchmarkServiceEndpoints(b *testing.B) { false, false, false, + true, ) require.NoError(b, err) @@ -4702,6 +4764,7 @@ func TestNewServiceSourceInformersEnabled(t *testing.T) { false, false, false, + false, ) require.NoError(t, err) svcSrc, ok := svc.(*serviceSource) @@ -4733,6 +4796,7 @@ func TestNewServiceSourceWithServiceTypeFilters_Unsupported(t *testing.T) { false, false, false, + false, ) require.Errorf(t, err, "unsupported service type filter: \"UnknownType\". Supported types are: [\"ClusterIP\" \"NodePort\" \"LoadBalancer\" \"ExternalName\"]") require.Nil(t, svc, "ServiceSource should be nil when an unsupported service type is provided") @@ -4912,6 +4976,7 @@ func TestEndpointSlicesIndexer(t *testing.T) { false, false, false, + true, ) require.NoError(t, err) ss, ok := src.(*serviceSource) @@ -4999,6 +5064,7 @@ func TestPodTransformerInServiceSource(t *testing.T) { false, false, false, + false, ) require.NoError(t, err) ss, ok := src.(*serviceSource) diff --git a/source/store.go b/source/store.go index efa08aba48..81f3b135be 100644 --- a/source/store.go +++ b/source/store.go @@ -429,7 +429,12 @@ func buildServiceSource(ctx context.Context, p ClientGenerator, cfg *Config) (So if err != nil { return nil, err } - return NewServiceSource(ctx, client, cfg.Namespace, cfg.AnnotationFilter, cfg.FQDNTemplate, cfg.CombineFQDNAndAnnotation, cfg.Compatibility, cfg.PublishInternal, cfg.PublishHostIP, cfg.AlwaysPublishNotReadyAddresses, cfg.ServiceTypeFilter, cfg.IgnoreHostnameAnnotation, cfg.LabelFilter, cfg.ResolveLoadBalancerHostname, cfg.ListenEndpointEvents, cfg.ExposeInternalIPv6) + return NewServiceSource(ctx, client, cfg.Namespace, + cfg.AnnotationFilter, cfg.FQDNTemplate, cfg.CombineFQDNAndAnnotation, + cfg.Compatibility, cfg.PublishInternal, cfg.PublishHostIP, + cfg.AlwaysPublishNotReadyAddresses, cfg.ServiceTypeFilter, cfg.IgnoreHostnameAnnotation, + cfg.LabelFilter, cfg.ResolveLoadBalancerHostname, cfg.ListenEndpointEvents, + cfg.ExposeInternalIPv6, cfg.ExcludeUnschedulable) } // buildIngressSource creates an Ingress source for exposing Kubernetes ingresses as DNS records.