diff --git a/pkg/controller/endpointslice/reconciler_test.go b/pkg/controller/endpointslice/reconciler_test.go index a4d37d7a2f5..95e88b2fa5f 100644 --- a/pkg/controller/endpointslice/reconciler_test.go +++ b/pkg/controller/endpointslice/reconciler_test.go @@ -917,7 +917,7 @@ func TestReconcileEndpointSlicesReplaceDeprecated(t *testing.T) { namespace := "test" svc, endpointMeta := newServiceAndEndpointMeta("foo", namespace) - endpointMeta.AddressType = discovery.AddressTypeIP + endpointMeta.AddressType = discovery.AddressType("IP") existingSlices := []*discovery.EndpointSlice{} pods := []*corev1.Pod{} diff --git a/pkg/controller/endpointslice/utils.go b/pkg/controller/endpointslice/utils.go index 29b52c127b5..d3738cc7af4 100644 --- a/pkg/controller/endpointslice/utils.go +++ b/pkg/controller/endpointslice/utils.go @@ -87,6 +87,10 @@ func podToEndpoint(pod *corev1.Pod, node *corev1.Node, service *corev1.Service, ep.Conditions.Terminating = &terminating } + if pod.Spec.NodeName != "" && utilfeature.DefaultFeatureGate.Enabled(features.EndpointSliceNodeName) { + ep.NodeName = &pod.Spec.NodeName + } + if endpointutil.ShouldSetHostname(pod, service) { ep.Hostname = &pod.Spec.Hostname } diff --git a/pkg/controller/endpointslice/utils_test.go b/pkg/controller/endpointslice/utils_test.go index 68f654f9fc4..5c61dc3946f 100644 --- a/pkg/controller/endpointslice/utils_test.go +++ b/pkg/controller/endpointslice/utils_test.go @@ -252,6 +252,7 @@ func TestPodToEndpoint(t *testing.T) { expectedEndpoint discovery.Endpoint publishNotReadyAddresses bool terminatingGateEnabled bool + nodeNameGateEnabled bool }{ { name: "Ready pod", @@ -321,6 +322,25 @@ func TestPodToEndpoint(t *testing.T) { }, }, }, + { + name: "Ready pod + node name gate enabled", + pod: readyPod, + svc: &svc, + nodeNameGateEnabled: true, + expectedEndpoint: discovery.Endpoint{ + Addresses: []string{"1.2.3.5"}, + Conditions: discovery.EndpointConditions{Ready: utilpointer.BoolPtr(true)}, + Topology: map[string]string{"kubernetes.io/hostname": "node-1"}, + NodeName: utilpointer.StringPtr("node-1"), + TargetRef: &v1.ObjectReference{ + Kind: "Pod", + Namespace: ns, + Name: readyPod.Name, + UID: readyPod.UID, + ResourceVersion: readyPod.ResourceVersion, + }, + }, + }, { name: "Ready pod + node labels", pod: readyPod, @@ -499,6 +519,7 @@ func TestPodToEndpoint(t *testing.T) { for _, testCase := range testCases { t.Run(testCase.name, func(t *testing.T) { defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.EndpointSliceTerminatingCondition, testCase.terminatingGateEnabled)() + defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.EndpointSliceNodeName, testCase.nodeNameGateEnabled)() endpoint := podToEndpoint(testCase.pod, testCase.node, testCase.svc, discovery.AddressTypeIPv4) if !reflect.DeepEqual(testCase.expectedEndpoint, endpoint) { diff --git a/pkg/controller/endpointslicemirroring/BUILD b/pkg/controller/endpointslicemirroring/BUILD index 41919a6f2d7..ede6890250e 100644 --- a/pkg/controller/endpointslicemirroring/BUILD +++ b/pkg/controller/endpointslicemirroring/BUILD @@ -18,6 +18,7 @@ go_library( "//pkg/controller:go_default_library", "//pkg/controller/endpointslicemirroring/metrics:go_default_library", "//pkg/controller/util/endpoint:go_default_library", + "//pkg/features:go_default_library", "//staging/src/k8s.io/api/core/v1:go_default_library", "//staging/src/k8s.io/api/discovery/v1beta1:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/api/equality:go_default_library", @@ -28,6 +29,7 @@ go_library( "//staging/src/k8s.io/apimachinery/pkg/types:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/util/runtime:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/util/wait:go_default_library", + "//staging/src/k8s.io/apiserver/pkg/util/feature:go_default_library", "//staging/src/k8s.io/client-go/informers/core/v1:go_default_library", "//staging/src/k8s.io/client-go/informers/discovery/v1beta1:go_default_library", "//staging/src/k8s.io/client-go/kubernetes:go_default_library", @@ -58,6 +60,7 @@ go_test( deps = [ "//pkg/controller:go_default_library", "//pkg/controller/endpointslicemirroring/metrics:go_default_library", + "//pkg/features:go_default_library", "//staging/src/k8s.io/api/core/v1:go_default_library", "//staging/src/k8s.io/api/discovery/v1beta1:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", @@ -66,6 +69,7 @@ go_test( "//staging/src/k8s.io/apimachinery/pkg/types:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/util/rand:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/util/wait:go_default_library", + "//staging/src/k8s.io/apiserver/pkg/util/feature:go_default_library", "//staging/src/k8s.io/client-go/informers:go_default_library", "//staging/src/k8s.io/client-go/kubernetes/fake:go_default_library", "//staging/src/k8s.io/client-go/kubernetes/scheme:go_default_library", @@ -73,6 +77,7 @@ go_test( "//staging/src/k8s.io/client-go/tools/cache:go_default_library", "//staging/src/k8s.io/client-go/tools/leaderelection/resourcelock:go_default_library", "//staging/src/k8s.io/client-go/tools/record:go_default_library", + "//staging/src/k8s.io/component-base/featuregate/testing:go_default_library", "//staging/src/k8s.io/component-base/metrics/testutil:go_default_library", "//vendor/github.com/stretchr/testify/assert:go_default_library", "//vendor/k8s.io/utils/pointer:go_default_library", diff --git a/pkg/controller/endpointslicemirroring/utils.go b/pkg/controller/endpointslicemirroring/utils.go index 66ddbf8eee9..3eb555f263d 100644 --- a/pkg/controller/endpointslicemirroring/utils.go +++ b/pkg/controller/endpointslicemirroring/utils.go @@ -27,10 +27,12 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime/schema" utilruntime "k8s.io/apimachinery/pkg/util/runtime" + utilfeature "k8s.io/apiserver/pkg/util/feature" "k8s.io/client-go/tools/cache" "k8s.io/client-go/tools/leaderelection/resourcelock" "k8s.io/kubernetes/pkg/apis/discovery/validation" endpointutil "k8s.io/kubernetes/pkg/controller/util/endpoint" + "k8s.io/kubernetes/pkg/features" ) // addrTypePortMapKey is used to uniquely identify groups of endpoint ports and @@ -138,6 +140,9 @@ func addressToEndpoint(address corev1.EndpointAddress, ready bool) *discovery.En endpoint.Topology = map[string]string{ "kubernetes.io/hostname": *address.NodeName, } + if utilfeature.DefaultFeatureGate.Enabled(features.EndpointSliceNodeName) { + endpoint.NodeName = address.NodeName + } } if address.Hostname != "" { endpoint.Hostname = &address.Hostname diff --git a/pkg/controller/endpointslicemirroring/utils_test.go b/pkg/controller/endpointslicemirroring/utils_test.go index 9944f191edf..3b6bd5cf4be 100644 --- a/pkg/controller/endpointslicemirroring/utils_test.go +++ b/pkg/controller/endpointslicemirroring/utils_test.go @@ -27,8 +27,12 @@ import ( "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/util/rand" + utilfeature "k8s.io/apiserver/pkg/util/feature" "k8s.io/client-go/kubernetes/fake" k8stesting "k8s.io/client-go/testing" + featuregatetesting "k8s.io/component-base/featuregate/testing" + "k8s.io/kubernetes/pkg/features" + utilpointer "k8s.io/utils/pointer" ) func TestNewEndpointSlice(t *testing.T) { @@ -76,6 +80,86 @@ func TestNewEndpointSlice(t *testing.T) { } } +func TestAddressToEndpoint(t *testing.T) { + testCases := []struct { + name string + epAddress v1.EndpointAddress + expectedEndpoint discovery.Endpoint + ready bool + nodeNameGateEnabled bool + }{{ + name: "simple + gate enabled", + epAddress: v1.EndpointAddress{ + IP: "10.1.2.3", + Hostname: "foo", + NodeName: utilpointer.StringPtr("node-abc"), + TargetRef: &v1.ObjectReference{ + APIVersion: "v1", + Kind: "Pod", + Namespace: "default", + Name: "foo", + }, + }, + ready: true, + nodeNameGateEnabled: true, + expectedEndpoint: discovery.Endpoint{ + Addresses: []string{"10.1.2.3"}, + Hostname: utilpointer.StringPtr("foo"), + Conditions: discovery.EndpointConditions{ + Ready: utilpointer.BoolPtr(true), + }, + Topology: map[string]string{ + "kubernetes.io/hostname": "node-abc", + }, + TargetRef: &v1.ObjectReference{ + APIVersion: "v1", + Kind: "Pod", + Namespace: "default", + Name: "foo", + }, + NodeName: utilpointer.StringPtr("node-abc"), + }, + }, { + name: "simple + gate disabled", + epAddress: v1.EndpointAddress{ + IP: "10.1.2.3", + Hostname: "foo", + NodeName: utilpointer.StringPtr("node-abc"), + TargetRef: &v1.ObjectReference{ + APIVersion: "v1", + Kind: "Pod", + Namespace: "default", + Name: "foo", + }, + }, + ready: true, + nodeNameGateEnabled: false, + expectedEndpoint: discovery.Endpoint{ + Addresses: []string{"10.1.2.3"}, + Hostname: utilpointer.StringPtr("foo"), + Conditions: discovery.EndpointConditions{ + Ready: utilpointer.BoolPtr(true), + }, + Topology: map[string]string{ + "kubernetes.io/hostname": "node-abc", + }, + TargetRef: &v1.ObjectReference{ + APIVersion: "v1", + Kind: "Pod", + Namespace: "default", + Name: "foo", + }, + }, + }} + + for _, tc := range testCases { + defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.EndpointSliceNodeName, tc.nodeNameGateEnabled)() + + ep := addressToEndpoint(tc.epAddress, tc.ready) + assert.EqualValues(t, tc.expectedEndpoint, *ep) + } +} + // Test helpers func newClientset() *fake.Clientset {