Merge pull request #113351 from andrewsykim/endpointslice-terminating-ga

Promote EndpointSliceTerminatingCondition to GA
This commit is contained in:
Kubernetes Prow Robot 2022-11-04 09:36:39 -07:00 committed by GitHub
commit b20ddbd75a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 286 additions and 968 deletions

View File

@ -9629,11 +9629,11 @@
"type": "boolean"
},
"serving": {
"description": "serving is identical to ready except that it is set regardless of the terminating state of endpoints. This condition should be set to true for a ready endpoint that is terminating. If nil, consumers should defer to the ready condition. This field can be enabled with the EndpointSliceTerminatingCondition feature gate.",
"description": "serving is identical to ready except that it is set regardless of the terminating state of endpoints. This condition should be set to true for a ready endpoint that is terminating. If nil, consumers should defer to the ready condition.",
"type": "boolean"
},
"terminating": {
"description": "terminating indicates that this endpoint is terminating. A nil value indicates an unknown state. Consumers should interpret this unknown state to mean that the endpoint is not terminating. This field can be enabled with the EndpointSliceTerminatingCondition feature gate.",
"description": "terminating indicates that this endpoint is terminating. A nil value indicates an unknown state. Consumers should interpret this unknown state to mean that the endpoint is not terminating.",
"type": "boolean"
}
},

View File

@ -107,11 +107,11 @@
"type": "boolean"
},
"serving": {
"description": "serving is identical to ready except that it is set regardless of the terminating state of endpoints. This condition should be set to true for a ready endpoint that is terminating. If nil, consumers should defer to the ready condition. This field can be enabled with the EndpointSliceTerminatingCondition feature gate.",
"description": "serving is identical to ready except that it is set regardless of the terminating state of endpoints. This condition should be set to true for a ready endpoint that is terminating. If nil, consumers should defer to the ready condition.",
"type": "boolean"
},
"terminating": {
"description": "terminating indicates that this endpoint is terminating. A nil value indicates an unknown state. Consumers should interpret this unknown state to mean that the endpoint is not terminating. This field can be enabled with the EndpointSliceTerminatingCondition feature gate.",
"description": "terminating indicates that this endpoint is terminating. A nil value indicates an unknown state. Consumers should interpret this unknown state to mean that the endpoint is not terminating.",
"type": "boolean"
}
},

View File

@ -118,15 +118,13 @@ type EndpointConditions struct {
// serving is identical to ready except that it is set regardless of the
// terminating state of endpoints. This condition should be set to true for
// a ready endpoint that is terminating. If nil, consumers should defer to
// the ready condition. This field can be enabled with the
// EndpointSliceTerminatingCondition feature gate.
// the ready condition.
// +optional
Serving *bool
// terminating indicates that this endpoint is terminating. A nil value
// indicates an unknown state. Consumers should interpret this unknown state
// to mean that the endpoint is not terminating. This field can be enabled
// with the EndpointSliceTerminatingCondition feature gate.
// to mean that the endpoint is not terminating.
// +optional
Terminating *bool
}

View File

@ -35,17 +35,14 @@ import (
"k8s.io/apimachinery/pkg/util/intstr"
"k8s.io/apimachinery/pkg/util/rand"
"k8s.io/apimachinery/pkg/util/wait"
utilfeature "k8s.io/apiserver/pkg/util/feature"
"k8s.io/client-go/informers"
"k8s.io/client-go/kubernetes/fake"
k8stesting "k8s.io/client-go/testing"
"k8s.io/client-go/tools/cache"
featuregatetesting "k8s.io/component-base/featuregate/testing"
"k8s.io/kubernetes/pkg/controller"
"k8s.io/kubernetes/pkg/controller/endpointslice/topologycache"
endpointutil "k8s.io/kubernetes/pkg/controller/util/endpoint"
endpointsliceutil "k8s.io/kubernetes/pkg/controller/util/endpointslice"
"k8s.io/kubernetes/pkg/features"
utilpointer "k8s.io/utils/pointer"
)
@ -418,12 +415,11 @@ func TestSyncService(t *testing.T) {
deletionTimestamp := metav1.Now()
testcases := []struct {
name string
service *v1.Service
pods []*v1.Pod
expectedEndpointPorts []discovery.EndpointPort
expectedEndpoints []discovery.Endpoint
terminatingGateEnabled bool
name string
service *v1.Service
pods []*v1.Pod
expectedEndpointPorts []discovery.EndpointPort
expectedEndpoints []discovery.Endpoint
}{
{
name: "pods with multiple IPs and Service with ipFamilies=ipv4",
@ -522,7 +518,9 @@ func TestSyncService(t *testing.T) {
expectedEndpoints: []discovery.Endpoint{
{
Conditions: discovery.EndpointConditions{
Ready: utilpointer.BoolPtr(true),
Ready: utilpointer.BoolPtr(true),
Serving: utilpointer.BoolPtr(true),
Terminating: utilpointer.BoolPtr(false),
},
Addresses: []string{"10.0.0.1"},
TargetRef: &v1.ObjectReference{Kind: "Pod", Namespace: "default", Name: "pod0"},
@ -530,7 +528,9 @@ func TestSyncService(t *testing.T) {
},
{
Conditions: discovery.EndpointConditions{
Ready: utilpointer.BoolPtr(true),
Ready: utilpointer.BoolPtr(true),
Serving: utilpointer.BoolPtr(true),
Terminating: utilpointer.BoolPtr(false),
},
Addresses: []string{"10.0.0.2"},
TargetRef: &v1.ObjectReference{Kind: "Pod", Namespace: "default", Name: "pod1"},
@ -635,7 +635,9 @@ func TestSyncService(t *testing.T) {
expectedEndpoints: []discovery.Endpoint{
{
Conditions: discovery.EndpointConditions{
Ready: utilpointer.BoolPtr(true),
Ready: utilpointer.BoolPtr(true),
Serving: utilpointer.BoolPtr(true),
Terminating: utilpointer.BoolPtr(false),
},
Addresses: []string{"fd08::5678:0000:0000:9abc:def0"},
TargetRef: &v1.ObjectReference{Kind: "Pod", Namespace: "default", Name: "pod1"},
@ -644,7 +646,7 @@ func TestSyncService(t *testing.T) {
},
},
{
name: "Terminating pods with EndpointSliceTerminatingCondition enabled",
name: "Terminating pods",
service: &v1.Service{
ObjectMeta: metav1.ObjectMeta{
Name: "foobar",
@ -757,114 +759,9 @@ func TestSyncService(t *testing.T) {
NodeName: utilpointer.StringPtr("node-1"),
},
},
terminatingGateEnabled: true,
},
{
name: "Terminating pods with EndpointSliceTerminatingCondition disabled",
service: &v1.Service{
ObjectMeta: metav1.ObjectMeta{
Name: "foobar",
Namespace: "default",
CreationTimestamp: creationTimestamp,
},
Spec: v1.ServiceSpec{
Ports: []v1.ServicePort{
{Name: "tcp-example", TargetPort: intstr.FromInt(80), Protocol: v1.ProtocolTCP},
{Name: "udp-example", TargetPort: intstr.FromInt(161), Protocol: v1.ProtocolUDP},
{Name: "sctp-example", TargetPort: intstr.FromInt(3456), Protocol: v1.ProtocolSCTP},
},
Selector: map[string]string{"foo": "bar"},
IPFamilies: []v1.IPFamily{v1.IPv4Protocol},
},
},
pods: []*v1.Pod{
{
// one ready pod for comparison
ObjectMeta: metav1.ObjectMeta{
Namespace: "default",
Name: "pod0",
Labels: map[string]string{"foo": "bar"},
DeletionTimestamp: nil,
},
Spec: v1.PodSpec{
Containers: []v1.Container{{
Name: "container-1",
}},
NodeName: "node-1",
},
Status: v1.PodStatus{
PodIP: "10.0.0.1",
PodIPs: []v1.PodIP{{
IP: "10.0.0.1",
}},
Conditions: []v1.PodCondition{
{
Type: v1.PodReady,
Status: v1.ConditionTrue,
},
},
},
},
{
ObjectMeta: metav1.ObjectMeta{
Namespace: "default",
Name: "pod1",
Labels: map[string]string{"foo": "bar"},
DeletionTimestamp: &deletionTimestamp,
},
Spec: v1.PodSpec{
Containers: []v1.Container{{
Name: "container-1",
}},
NodeName: "node-1",
},
Status: v1.PodStatus{
PodIP: "10.0.0.2",
PodIPs: []v1.PodIP{
{
IP: "10.0.0.2",
},
},
Conditions: []v1.PodCondition{
{
Type: v1.PodReady,
Status: v1.ConditionTrue,
},
},
},
},
},
expectedEndpointPorts: []discovery.EndpointPort{
{
Name: utilpointer.StringPtr("sctp-example"),
Protocol: protoPtr(v1.ProtocolSCTP),
Port: utilpointer.Int32Ptr(int32(3456)),
},
{
Name: utilpointer.StringPtr("udp-example"),
Protocol: protoPtr(v1.ProtocolUDP),
Port: utilpointer.Int32Ptr(int32(161)),
},
{
Name: utilpointer.StringPtr("tcp-example"),
Protocol: protoPtr(v1.ProtocolTCP),
Port: utilpointer.Int32Ptr(int32(80)),
},
},
expectedEndpoints: []discovery.Endpoint{
{
Conditions: discovery.EndpointConditions{
Ready: utilpointer.BoolPtr(true),
},
Addresses: []string{"10.0.0.1"},
TargetRef: &v1.ObjectReference{Kind: "Pod", Namespace: "default", Name: "pod0"},
NodeName: utilpointer.StringPtr("node-1"),
},
},
terminatingGateEnabled: false,
},
{
name: "Not ready terminating pods with EndpointSliceTerminatingCondition enabled",
name: "Not ready terminating pods",
service: &v1.Service{
ObjectMeta: metav1.ObjectMeta{
Name: "foobar",
@ -977,118 +874,11 @@ func TestSyncService(t *testing.T) {
NodeName: utilpointer.StringPtr("node-1"),
},
},
terminatingGateEnabled: true,
},
{
name: "Not ready terminating pods with EndpointSliceTerminatingCondition disabled",
service: &v1.Service{
ObjectMeta: metav1.ObjectMeta{
Name: "foobar",
Namespace: "default",
CreationTimestamp: creationTimestamp,
},
Spec: v1.ServiceSpec{
Ports: []v1.ServicePort{
{Name: "tcp-example", TargetPort: intstr.FromInt(80), Protocol: v1.ProtocolTCP},
{Name: "udp-example", TargetPort: intstr.FromInt(161), Protocol: v1.ProtocolUDP},
{Name: "sctp-example", TargetPort: intstr.FromInt(3456), Protocol: v1.ProtocolSCTP},
},
Selector: map[string]string{"foo": "bar"},
IPFamilies: []v1.IPFamily{v1.IPv4Protocol},
},
},
pods: []*v1.Pod{
{
// one ready pod for comparison
ObjectMeta: metav1.ObjectMeta{
Namespace: "default",
Name: "pod0",
Labels: map[string]string{"foo": "bar"},
DeletionTimestamp: nil,
},
Spec: v1.PodSpec{
Containers: []v1.Container{{
Name: "container-1",
}},
NodeName: "node-1",
},
Status: v1.PodStatus{
PodIP: "10.0.0.1",
PodIPs: []v1.PodIP{{
IP: "10.0.0.1",
}},
Conditions: []v1.PodCondition{
{
Type: v1.PodReady,
Status: v1.ConditionTrue,
},
},
},
},
{
ObjectMeta: metav1.ObjectMeta{
Namespace: "default",
Name: "pod1",
Labels: map[string]string{"foo": "bar"},
DeletionTimestamp: &deletionTimestamp,
},
Spec: v1.PodSpec{
Containers: []v1.Container{{
Name: "container-1",
}},
NodeName: "node-1",
},
Status: v1.PodStatus{
PodIP: "10.0.0.2",
PodIPs: []v1.PodIP{
{
IP: "10.0.0.2",
},
},
Conditions: []v1.PodCondition{
{
Type: v1.PodReady,
Status: v1.ConditionFalse,
},
},
},
},
},
expectedEndpointPorts: []discovery.EndpointPort{
{
Name: utilpointer.StringPtr("sctp-example"),
Protocol: protoPtr(v1.ProtocolSCTP),
Port: utilpointer.Int32Ptr(int32(3456)),
},
{
Name: utilpointer.StringPtr("udp-example"),
Protocol: protoPtr(v1.ProtocolUDP),
Port: utilpointer.Int32Ptr(int32(161)),
},
{
Name: utilpointer.StringPtr("tcp-example"),
Protocol: protoPtr(v1.ProtocolTCP),
Port: utilpointer.Int32Ptr(int32(80)),
},
},
expectedEndpoints: []discovery.Endpoint{
{
Conditions: discovery.EndpointConditions{
Ready: utilpointer.BoolPtr(true),
},
Addresses: []string{"10.0.0.1"},
TargetRef: &v1.ObjectReference{Kind: "Pod", Namespace: "default", Name: "pod0"},
NodeName: utilpointer.StringPtr("node-1"),
},
},
terminatingGateEnabled: false,
},
}
for _, testcase := range testcases {
t.Run(testcase.name, func(t *testing.T) {
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.EndpointSliceTerminatingCondition, testcase.terminatingGateEnabled)()
client, esController := newController([]string{"node-1"}, time.Duration(0))
for _, pod := range testcase.pods {

View File

@ -30,7 +30,6 @@ import (
"k8s.io/apimachinery/pkg/types"
utilerrors "k8s.io/apimachinery/pkg/util/errors"
"k8s.io/apimachinery/pkg/util/sets"
utilfeature "k8s.io/apiserver/pkg/util/feature"
clientset "k8s.io/client-go/kubernetes"
corelisters "k8s.io/client-go/listers/core/v1"
"k8s.io/klog/v2"
@ -38,7 +37,6 @@ import (
"k8s.io/kubernetes/pkg/controller/endpointslice/topologycache"
endpointutil "k8s.io/kubernetes/pkg/controller/util/endpoint"
endpointsliceutil "k8s.io/kubernetes/pkg/controller/util/endpointslice"
"k8s.io/kubernetes/pkg/features"
)
// reconciler is responsible for transforming current EndpointSlice state into
@ -154,8 +152,7 @@ func (r *reconciler) reconcileByAddressType(service *corev1.Service, pods []*cor
desiredEndpointsByPortMap := map[endpointutil.PortMapKey]endpointsliceutil.EndpointSet{}
for _, pod := range pods {
includeTerminating := service.Spec.PublishNotReadyAddresses || utilfeature.DefaultFeatureGate.Enabled(features.EndpointSliceTerminatingCondition)
if !endpointutil.ShouldPodBeInEndpoints(pod, includeTerminating) {
if !endpointutil.ShouldPodBeInEndpoints(pod, true) {
continue
}

View File

@ -32,18 +32,15 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/util/intstr"
utilfeature "k8s.io/apiserver/pkg/util/feature"
"k8s.io/client-go/informers"
"k8s.io/client-go/kubernetes/fake"
corelisters "k8s.io/client-go/listers/core/v1"
k8stesting "k8s.io/client-go/testing"
featuregatetesting "k8s.io/component-base/featuregate/testing"
"k8s.io/component-base/metrics/testutil"
"k8s.io/kubernetes/pkg/controller"
"k8s.io/kubernetes/pkg/controller/endpointslice/metrics"
"k8s.io/kubernetes/pkg/controller/endpointslice/topologycache"
endpointsliceutil "k8s.io/kubernetes/pkg/controller/util/endpointslice"
"k8s.io/kubernetes/pkg/features"
utilpointer "k8s.io/utils/pointer"
)
@ -119,17 +116,20 @@ func TestReconcile1Pod(t *testing.T) {
expectedEndpoint discovery.Endpoint
expectedLabels map[string]string
expectedEndpointPerSlice map[discovery.AddressType][]discovery.Endpoint
terminatingGateEnabled bool
}{
"no-family-service": {
service: noFamilyService,
expectedEndpointPerSlice: map[discovery.AddressType][]discovery.Endpoint{
discovery.AddressTypeIPv4: {
{
Addresses: []string{"1.2.3.4"},
Conditions: discovery.EndpointConditions{Ready: utilpointer.BoolPtr(true)},
Zone: utilpointer.StringPtr("us-central1-a"),
NodeName: utilpointer.StringPtr("node-1"),
Addresses: []string{"1.2.3.4"},
Conditions: discovery.EndpointConditions{
Ready: utilpointer.BoolPtr(true),
Serving: utilpointer.BoolPtr(true),
Terminating: utilpointer.BoolPtr(false),
},
Zone: utilpointer.StringPtr("us-central1-a"),
NodeName: utilpointer.StringPtr("node-1"),
TargetRef: &corev1.ObjectReference{
Kind: "Pod",
Namespace: namespace,
@ -144,29 +144,6 @@ func TestReconcile1Pod(t *testing.T) {
},
},
"ipv4": {
service: svcv4,
expectedEndpointPerSlice: map[discovery.AddressType][]discovery.Endpoint{
discovery.AddressTypeIPv4: {
{
Addresses: []string{"1.2.3.4"},
Conditions: discovery.EndpointConditions{Ready: utilpointer.BoolPtr(true)},
Zone: utilpointer.StringPtr("us-central1-a"),
NodeName: utilpointer.StringPtr("node-1"),
TargetRef: &corev1.ObjectReference{
Kind: "Pod",
Namespace: namespace,
Name: "pod1",
},
},
},
},
expectedLabels: map[string]string{
discovery.LabelManagedBy: controllerName,
discovery.LabelServiceName: "foo",
corev1.IsHeadlessService: "",
},
},
"ipv4-with-terminating-gate-enabled": {
service: svcv4,
expectedEndpointPerSlice: map[discovery.AddressType][]discovery.Endpoint{
discovery.AddressTypeIPv4: {
@ -192,17 +169,20 @@ func TestReconcile1Pod(t *testing.T) {
discovery.LabelServiceName: "foo",
corev1.IsHeadlessService: "",
},
terminatingGateEnabled: true,
},
"ipv4-clusterip": {
service: svcv4ClusterIP,
expectedEndpointPerSlice: map[discovery.AddressType][]discovery.Endpoint{
discovery.AddressTypeIPv4: {
{
Addresses: []string{"1.2.3.4"},
Conditions: discovery.EndpointConditions{Ready: utilpointer.BoolPtr(true)},
Zone: utilpointer.StringPtr("us-central1-a"),
NodeName: utilpointer.StringPtr("node-1"),
Addresses: []string{"1.2.3.4"},
Conditions: discovery.EndpointConditions{
Ready: utilpointer.BoolPtr(true),
Serving: utilpointer.BoolPtr(true),
Terminating: utilpointer.BoolPtr(false),
},
Zone: utilpointer.StringPtr("us-central1-a"),
NodeName: utilpointer.StringPtr("node-1"),
TargetRef: &corev1.ObjectReference{
Kind: "Pod",
Namespace: namespace,
@ -213,10 +193,14 @@ func TestReconcile1Pod(t *testing.T) {
},
expectedAddressType: discovery.AddressTypeIPv4,
expectedEndpoint: discovery.Endpoint{
Addresses: []string{"1.2.3.4"},
Conditions: discovery.EndpointConditions{Ready: utilpointer.BoolPtr(true)},
Zone: utilpointer.StringPtr("us-central1-a"),
NodeName: utilpointer.StringPtr("node-1"),
Addresses: []string{"1.2.3.4"},
Conditions: discovery.EndpointConditions{
Ready: utilpointer.BoolPtr(true),
Serving: utilpointer.BoolPtr(true),
Terminating: utilpointer.BoolPtr(false),
},
Zone: utilpointer.StringPtr("us-central1-a"),
NodeName: utilpointer.StringPtr("node-1"),
TargetRef: &corev1.ObjectReference{
Kind: "Pod",
Namespace: namespace,
@ -233,10 +217,14 @@ func TestReconcile1Pod(t *testing.T) {
expectedEndpointPerSlice: map[discovery.AddressType][]discovery.Endpoint{
discovery.AddressTypeIPv4: {
{
Addresses: []string{"1.2.3.4"},
Conditions: discovery.EndpointConditions{Ready: utilpointer.BoolPtr(true)},
Zone: utilpointer.StringPtr("us-central1-a"),
NodeName: utilpointer.StringPtr("node-1"),
Addresses: []string{"1.2.3.4"},
Conditions: discovery.EndpointConditions{
Ready: utilpointer.BoolPtr(true),
Serving: utilpointer.BoolPtr(true),
Terminating: utilpointer.BoolPtr(false),
},
Zone: utilpointer.StringPtr("us-central1-a"),
NodeName: utilpointer.StringPtr("node-1"),
TargetRef: &corev1.ObjectReference{
Kind: "Pod",
Namespace: namespace,
@ -247,10 +235,14 @@ func TestReconcile1Pod(t *testing.T) {
},
expectedAddressType: discovery.AddressTypeIPv4,
expectedEndpoint: discovery.Endpoint{
Addresses: []string{"1.2.3.4"},
Conditions: discovery.EndpointConditions{Ready: utilpointer.BoolPtr(true)},
Zone: utilpointer.StringPtr("us-central1-a"),
NodeName: utilpointer.StringPtr("node-1"),
Addresses: []string{"1.2.3.4"},
Conditions: discovery.EndpointConditions{
Ready: utilpointer.BoolPtr(true),
Serving: utilpointer.BoolPtr(true),
Terminating: utilpointer.BoolPtr(false),
},
Zone: utilpointer.StringPtr("us-central1-a"),
NodeName: utilpointer.StringPtr("node-1"),
TargetRef: &corev1.ObjectReference{
Kind: "Pod",
Namespace: namespace,
@ -269,10 +261,14 @@ func TestReconcile1Pod(t *testing.T) {
expectedEndpointPerSlice: map[discovery.AddressType][]discovery.Endpoint{
discovery.AddressTypeIPv4: {
{
Addresses: []string{"1.2.3.4"},
Conditions: discovery.EndpointConditions{Ready: utilpointer.BoolPtr(true)},
Zone: utilpointer.StringPtr("us-central1-a"),
NodeName: utilpointer.StringPtr("node-1"),
Addresses: []string{"1.2.3.4"},
Conditions: discovery.EndpointConditions{
Ready: utilpointer.BoolPtr(true),
Serving: utilpointer.BoolPtr(true),
Terminating: utilpointer.BoolPtr(false),
},
Zone: utilpointer.StringPtr("us-central1-a"),
NodeName: utilpointer.StringPtr("node-1"),
TargetRef: &corev1.ObjectReference{
Kind: "Pod",
Namespace: namespace,
@ -283,10 +279,14 @@ func TestReconcile1Pod(t *testing.T) {
},
expectedAddressType: discovery.AddressTypeIPv4,
expectedEndpoint: discovery.Endpoint{
Addresses: []string{"1.2.3.4"},
Conditions: discovery.EndpointConditions{Ready: utilpointer.BoolPtr(true)},
Zone: utilpointer.StringPtr("us-central1-a"),
NodeName: utilpointer.StringPtr("node-1"),
Addresses: []string{"1.2.3.4"},
Conditions: discovery.EndpointConditions{
Ready: utilpointer.BoolPtr(true),
Serving: utilpointer.BoolPtr(true),
Terminating: utilpointer.BoolPtr(false),
},
Zone: utilpointer.StringPtr("us-central1-a"),
NodeName: utilpointer.StringPtr("node-1"),
TargetRef: &corev1.ObjectReference{
Kind: "Pod",
Namespace: namespace,
@ -305,10 +305,14 @@ func TestReconcile1Pod(t *testing.T) {
expectedEndpointPerSlice: map[discovery.AddressType][]discovery.Endpoint{
discovery.AddressTypeIPv6: {
{
Addresses: []string{"1234::5678:0000:0000:9abc:def0"},
Conditions: discovery.EndpointConditions{Ready: utilpointer.BoolPtr(true)},
Zone: utilpointer.StringPtr("us-central1-a"),
NodeName: utilpointer.StringPtr("node-1"),
Addresses: []string{"1234::5678:0000:0000:9abc:def0"},
Conditions: discovery.EndpointConditions{
Ready: utilpointer.BoolPtr(true),
Serving: utilpointer.BoolPtr(true),
Terminating: utilpointer.BoolPtr(false),
},
Zone: utilpointer.StringPtr("us-central1-a"),
NodeName: utilpointer.StringPtr("node-1"),
TargetRef: &corev1.ObjectReference{
Kind: "Pod",
Namespace: namespace,
@ -329,10 +333,14 @@ func TestReconcile1Pod(t *testing.T) {
expectedEndpointPerSlice: map[discovery.AddressType][]discovery.Endpoint{
discovery.AddressTypeIPv6: {
{
Addresses: []string{"1234::5678:0000:0000:9abc:def0"},
Conditions: discovery.EndpointConditions{Ready: utilpointer.BoolPtr(true)},
Zone: utilpointer.StringPtr("us-central1-a"),
NodeName: utilpointer.StringPtr("node-1"),
Addresses: []string{"1234::5678:0000:0000:9abc:def0"},
Conditions: discovery.EndpointConditions{
Ready: utilpointer.BoolPtr(true),
Serving: utilpointer.BoolPtr(true),
Terminating: utilpointer.BoolPtr(false),
},
Zone: utilpointer.StringPtr("us-central1-a"),
NodeName: utilpointer.StringPtr("node-1"),
TargetRef: &corev1.ObjectReference{
Kind: "Pod",
Namespace: namespace,
@ -352,10 +360,14 @@ func TestReconcile1Pod(t *testing.T) {
expectedEndpointPerSlice: map[discovery.AddressType][]discovery.Endpoint{
discovery.AddressTypeIPv6: {
{
Addresses: []string{"1234::5678:0000:0000:9abc:def0"},
Conditions: discovery.EndpointConditions{Ready: utilpointer.BoolPtr(true)},
Zone: utilpointer.StringPtr("us-central1-a"),
NodeName: utilpointer.StringPtr("node-1"),
Addresses: []string{"1234::5678:0000:0000:9abc:def0"},
Conditions: discovery.EndpointConditions{
Ready: utilpointer.BoolPtr(true),
Serving: utilpointer.BoolPtr(true),
Terminating: utilpointer.BoolPtr(false),
},
Zone: utilpointer.StringPtr("us-central1-a"),
NodeName: utilpointer.StringPtr("node-1"),
TargetRef: &corev1.ObjectReference{
Kind: "Pod",
Namespace: namespace,
@ -365,10 +377,14 @@ func TestReconcile1Pod(t *testing.T) {
},
discovery.AddressTypeIPv4: {
{
Addresses: []string{"1.2.3.4"},
Conditions: discovery.EndpointConditions{Ready: utilpointer.BoolPtr(true)},
Zone: utilpointer.StringPtr("us-central1-a"),
NodeName: utilpointer.StringPtr("node-1"),
Addresses: []string{"1.2.3.4"},
Conditions: discovery.EndpointConditions{
Ready: utilpointer.BoolPtr(true),
Serving: utilpointer.BoolPtr(true),
Terminating: utilpointer.BoolPtr(false),
},
Zone: utilpointer.StringPtr("us-central1-a"),
NodeName: utilpointer.StringPtr("node-1"),
TargetRef: &corev1.ObjectReference{
Kind: "Pod",
Namespace: namespace,
@ -386,8 +402,6 @@ func TestReconcile1Pod(t *testing.T) {
for name, testCase := range testCases {
t.Run(name, func(t *testing.T) {
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.EndpointSliceTerminatingCondition, testCase.terminatingGateEnabled)()
client := newClientset()
setupMetrics()
triggerTime := time.Now().UTC()

View File

@ -20,14 +20,13 @@ import (
"fmt"
"time"
"k8s.io/api/core/v1"
v1 "k8s.io/api/core/v1"
discovery "k8s.io/api/discovery/v1"
apiequality "k8s.io/apimachinery/pkg/api/equality"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime/schema"
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
"k8s.io/apimachinery/pkg/util/sets"
utilfeature "k8s.io/apiserver/pkg/util/feature"
"k8s.io/client-go/tools/cache"
"k8s.io/klog/v2"
podutil "k8s.io/kubernetes/pkg/api/v1/pod"
@ -35,7 +34,6 @@ import (
"k8s.io/kubernetes/pkg/apis/core/v1/helper"
"k8s.io/kubernetes/pkg/apis/discovery/validation"
endpointutil "k8s.io/kubernetes/pkg/controller/util/endpoint"
"k8s.io/kubernetes/pkg/features"
utilnet "k8s.io/utils/net"
)
@ -49,7 +47,9 @@ func podToEndpoint(pod *v1.Pod, node *v1.Node, service *v1.Service, addressType
ep := discovery.Endpoint{
Addresses: getEndpointAddresses(pod.Status, service, addressType),
Conditions: discovery.EndpointConditions{
Ready: &ready,
Ready: &ready,
Serving: &serving,
Terminating: &terminating,
},
TargetRef: &v1.ObjectReference{
Kind: "Pod",
@ -59,11 +59,6 @@ func podToEndpoint(pod *v1.Pod, node *v1.Node, service *v1.Service, addressType
},
}
if utilfeature.DefaultFeatureGate.Enabled(features.EndpointSliceTerminatingCondition) {
ep.Conditions.Serving = &serving
ep.Conditions.Terminating = &terminating
}
if pod.Spec.NodeName != "" {
ep.NodeName = &pod.Spec.NodeName
}

View File

@ -31,11 +31,8 @@ import (
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/intstr"
"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"
)
@ -251,131 +248,11 @@ func TestPodToEndpoint(t *testing.T) {
svc *v1.Service
expectedEndpoint discovery.Endpoint
publishNotReadyAddresses bool
terminatingGateEnabled bool
}{
{
name: "Ready pod",
pod: readyPod,
svc: &svc,
expectedEndpoint: discovery.Endpoint{
Addresses: []string{"1.2.3.5"},
Conditions: discovery.EndpointConditions{Ready: utilpointer.BoolPtr(true)},
NodeName: utilpointer.StringPtr("node-1"),
TargetRef: &v1.ObjectReference{
Kind: "Pod",
Namespace: ns,
Name: readyPod.Name,
UID: readyPod.UID,
},
},
},
{
name: "Ready pod + publishNotReadyAddresses",
pod: readyPod,
svc: &svcPublishNotReady,
expectedEndpoint: discovery.Endpoint{
Addresses: []string{"1.2.3.5"},
Conditions: discovery.EndpointConditions{Ready: utilpointer.BoolPtr(true)},
NodeName: utilpointer.StringPtr("node-1"),
TargetRef: &v1.ObjectReference{
Kind: "Pod",
Namespace: ns,
Name: readyPod.Name,
UID: readyPod.UID,
},
},
},
{
name: "Unready pod",
pod: unreadyPod,
svc: &svc,
expectedEndpoint: discovery.Endpoint{
Addresses: []string{"1.2.3.5"},
Conditions: discovery.EndpointConditions{Ready: utilpointer.BoolPtr(false)},
NodeName: utilpointer.StringPtr("node-1"),
TargetRef: &v1.ObjectReference{
Kind: "Pod",
Namespace: ns,
Name: readyPod.Name,
UID: readyPod.UID,
},
},
},
{
name: "Unready pod + publishNotReadyAddresses",
pod: unreadyPod,
svc: &svcPublishNotReady,
expectedEndpoint: discovery.Endpoint{
Addresses: []string{"1.2.3.5"},
Conditions: discovery.EndpointConditions{Ready: utilpointer.BoolPtr(true)},
NodeName: utilpointer.StringPtr("node-1"),
TargetRef: &v1.ObjectReference{
Kind: "Pod",
Namespace: ns,
Name: readyPod.Name,
UID: readyPod.UID,
},
},
},
{
name: "Ready pod + node labels",
pod: readyPod,
node: node1,
svc: &svc,
expectedEndpoint: discovery.Endpoint{
Addresses: []string{"1.2.3.5"},
Conditions: discovery.EndpointConditions{Ready: utilpointer.BoolPtr(true)},
Zone: utilpointer.StringPtr("us-central1-a"),
NodeName: utilpointer.StringPtr("node-1"),
TargetRef: &v1.ObjectReference{
Kind: "Pod",
Namespace: ns,
Name: readyPod.Name,
UID: readyPod.UID,
},
},
},
{
name: "Multi IP Ready pod + node labels",
pod: multiIPPod,
node: node1,
svc: &svc,
expectedEndpoint: discovery.Endpoint{
Addresses: []string{"1.2.3.4"},
Conditions: discovery.EndpointConditions{Ready: utilpointer.BoolPtr(true)},
Zone: utilpointer.StringPtr("us-central1-a"),
NodeName: utilpointer.StringPtr("node-1"),
TargetRef: &v1.ObjectReference{
Kind: "Pod",
Namespace: ns,
Name: readyPod.Name,
UID: readyPod.UID,
},
},
},
{
name: "Ready pod + hostname",
pod: readyPodHostname,
node: node1,
svc: &svc,
expectedEndpoint: discovery.Endpoint{
Addresses: []string{"1.2.3.5"},
Conditions: discovery.EndpointConditions{Ready: utilpointer.BoolPtr(true)},
Hostname: &readyPodHostname.Spec.Hostname,
Zone: utilpointer.StringPtr("us-central1-a"),
NodeName: utilpointer.StringPtr("node-1"),
TargetRef: &v1.ObjectReference{
Kind: "Pod",
Namespace: ns,
Name: readyPodHostname.Name,
UID: readyPodHostname.UID,
},
},
},
{
name: "Ready pod, terminating gate enabled",
pod: readyPod,
svc: &svc,
expectedEndpoint: discovery.Endpoint{
Addresses: []string{"1.2.3.5"},
Conditions: discovery.EndpointConditions{
@ -391,16 +268,17 @@ func TestPodToEndpoint(t *testing.T) {
UID: readyPod.UID,
},
},
terminatingGateEnabled: true,
},
{
name: "Ready terminating pod, terminating gate disabled",
pod: readyTerminatingPod,
svc: &svc,
name: "Ready pod + publishNotReadyAddresses",
pod: readyPod,
svc: &svcPublishNotReady,
expectedEndpoint: discovery.Endpoint{
Addresses: []string{"1.2.3.5"},
Conditions: discovery.EndpointConditions{
Ready: utilpointer.BoolPtr(false),
Ready: utilpointer.BoolPtr(true),
Serving: utilpointer.BoolPtr(true),
Terminating: utilpointer.BoolPtr(false),
},
NodeName: utilpointer.StringPtr("node-1"),
TargetRef: &v1.ObjectReference{
@ -410,10 +288,136 @@ func TestPodToEndpoint(t *testing.T) {
UID: readyPod.UID,
},
},
terminatingGateEnabled: false,
},
{
name: "Ready terminating pod, terminating gate enabled",
name: "Unready pod",
pod: unreadyPod,
svc: &svc,
expectedEndpoint: discovery.Endpoint{
Addresses: []string{"1.2.3.5"},
Conditions: discovery.EndpointConditions{
Ready: utilpointer.BoolPtr(false),
Serving: utilpointer.BoolPtr(false),
Terminating: utilpointer.BoolPtr(false),
},
NodeName: utilpointer.StringPtr("node-1"),
TargetRef: &v1.ObjectReference{
Kind: "Pod",
Namespace: ns,
Name: readyPod.Name,
UID: readyPod.UID,
},
},
},
{
name: "Unready pod + publishNotReadyAddresses",
pod: unreadyPod,
svc: &svcPublishNotReady,
expectedEndpoint: discovery.Endpoint{
Addresses: []string{"1.2.3.5"},
Conditions: discovery.EndpointConditions{
Ready: utilpointer.BoolPtr(true),
Serving: utilpointer.BoolPtr(false),
Terminating: utilpointer.BoolPtr(false),
},
NodeName: utilpointer.StringPtr("node-1"),
TargetRef: &v1.ObjectReference{
Kind: "Pod",
Namespace: ns,
Name: readyPod.Name,
UID: readyPod.UID,
},
},
},
{
name: "Ready pod + node labels",
pod: readyPod,
node: node1,
svc: &svc,
expectedEndpoint: discovery.Endpoint{
Addresses: []string{"1.2.3.5"},
Conditions: discovery.EndpointConditions{
Ready: utilpointer.BoolPtr(true),
Serving: utilpointer.BoolPtr(true),
Terminating: utilpointer.BoolPtr(false),
},
Zone: utilpointer.StringPtr("us-central1-a"),
NodeName: utilpointer.StringPtr("node-1"),
TargetRef: &v1.ObjectReference{
Kind: "Pod",
Namespace: ns,
Name: readyPod.Name,
UID: readyPod.UID,
},
},
},
{
name: "Multi IP Ready pod + node labels",
pod: multiIPPod,
node: node1,
svc: &svc,
expectedEndpoint: discovery.Endpoint{
Addresses: []string{"1.2.3.4"},
Conditions: discovery.EndpointConditions{
Ready: utilpointer.BoolPtr(true),
Serving: utilpointer.BoolPtr(true),
Terminating: utilpointer.BoolPtr(false),
},
Zone: utilpointer.StringPtr("us-central1-a"),
NodeName: utilpointer.StringPtr("node-1"),
TargetRef: &v1.ObjectReference{
Kind: "Pod",
Namespace: ns,
Name: readyPod.Name,
UID: readyPod.UID,
},
},
},
{
name: "Ready pod + hostname",
pod: readyPodHostname,
node: node1,
svc: &svc,
expectedEndpoint: discovery.Endpoint{
Addresses: []string{"1.2.3.5"},
Conditions: discovery.EndpointConditions{
Ready: utilpointer.BoolPtr(true),
Serving: utilpointer.BoolPtr(true),
Terminating: utilpointer.BoolPtr(false),
},
Hostname: &readyPodHostname.Spec.Hostname,
Zone: utilpointer.StringPtr("us-central1-a"),
NodeName: utilpointer.StringPtr("node-1"),
TargetRef: &v1.ObjectReference{
Kind: "Pod",
Namespace: ns,
Name: readyPodHostname.Name,
UID: readyPodHostname.UID,
},
},
},
{
name: "Ready pod",
pod: readyPod,
svc: &svc,
expectedEndpoint: discovery.Endpoint{
Addresses: []string{"1.2.3.5"},
Conditions: discovery.EndpointConditions{
Ready: utilpointer.BoolPtr(true),
Serving: utilpointer.BoolPtr(true),
Terminating: utilpointer.BoolPtr(false),
},
NodeName: utilpointer.StringPtr("node-1"),
TargetRef: &v1.ObjectReference{
Kind: "Pod",
Namespace: ns,
Name: readyPod.Name,
UID: readyPod.UID,
},
},
},
{
name: "Ready terminating pod",
pod: readyTerminatingPod,
svc: &svc,
expectedEndpoint: discovery.Endpoint{
@ -431,29 +435,9 @@ func TestPodToEndpoint(t *testing.T) {
UID: readyPod.UID,
},
},
terminatingGateEnabled: true,
},
{
name: "Not ready terminating pod, terminating gate disabled",
pod: unreadyTerminatingPod,
svc: &svc,
expectedEndpoint: discovery.Endpoint{
Addresses: []string{"1.2.3.5"},
Conditions: discovery.EndpointConditions{
Ready: utilpointer.BoolPtr(false),
},
NodeName: utilpointer.StringPtr("node-1"),
TargetRef: &v1.ObjectReference{
Kind: "Pod",
Namespace: ns,
Name: readyPod.Name,
UID: readyPod.UID,
},
},
terminatingGateEnabled: false,
},
{
name: "Not ready terminating pod, terminating gate enabled",
name: "Not ready terminating pod",
pod: unreadyTerminatingPod,
svc: &svc,
expectedEndpoint: discovery.Endpoint{
@ -471,14 +455,11 @@ func TestPodToEndpoint(t *testing.T) {
UID: readyPod.UID,
},
},
terminatingGateEnabled: true,
},
}
for _, testCase := range testCases {
t.Run(testCase.name, func(t *testing.T) {
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.EndpointSliceTerminatingCondition, testCase.terminatingGateEnabled)()
endpoint := podToEndpoint(testCase.pod, testCase.node, testCase.svc, discovery.AddressTypeIPv4)
if !reflect.DeepEqual(testCase.expectedEndpoint, endpoint) {
t.Errorf("Expected endpoint: %+v, got: %+v", testCase.expectedEndpoint, endpoint)

View File

@ -306,16 +306,11 @@ func EndpointsEqualBeyondHash(ep1, ep2 *discovery.Endpoint) bool {
return false
}
// Serving and Terminating will only be set when the EndpointSliceTerminatingCondition feature is on.
// Ignore their difference if the expected or actual value is nil, which means the feature enablement is changed.
// Otherwise all EndpointSlices in the system would be updated on the first controller-manager restart even without
// actual changes, leading to delay in processing legitimate updates.
// Its value will be set to the expected one when there is an actual change triggering update of this EndpointSlice.
if ep1.Conditions.Serving != nil && ep2.Conditions.Serving != nil && *ep1.Conditions.Serving != *ep2.Conditions.Serving {
if boolPtrChanged(ep1.Conditions.Serving, ep2.Conditions.Serving) {
return false
}
if ep1.Conditions.Terminating != nil && ep2.Conditions.Terminating != nil && *ep1.Conditions.Terminating != *ep2.Conditions.Terminating {
if boolPtrChanged(ep1.Conditions.Terminating, ep2.Conditions.Terminating) {
return false
}

View File

@ -776,7 +776,7 @@ func TestEndpointsEqualBeyondHash(t *testing.T) {
Zone: utilpointer.StringPtr("zone-1"),
NodeName: utilpointer.StringPtr("node-1"),
},
expected: true,
expected: false,
},
{
name: "Serving condition changed from false to true",

View File

@ -269,6 +269,7 @@ const (
// kep: https://kep.k8s.io/1672
// alpha: v1.20
// beta: v1.22
// GA: v1.26
//
// Enable Terminating condition in Endpoint Slices.
EndpointSliceTerminatingCondition featuregate.Feature = "EndpointSliceTerminatingCondition"
@ -899,7 +900,7 @@ var defaultKubernetesFeatureGates = map[featuregate.Feature]featuregate.FeatureS
DownwardAPIHugePages: {Default: true, PreRelease: featuregate.Beta}, // on by default in 1.22
EndpointSliceTerminatingCondition: {Default: true, PreRelease: featuregate.Beta},
EndpointSliceTerminatingCondition: {Default: true, PreRelease: featuregate.GA, LockToDefault: true}, // remove in v1.28
EphemeralContainers: {Default: true, PreRelease: featuregate.GA, LockToDefault: true}, // remove in 1.27

View File

@ -27012,14 +27012,14 @@ func schema_k8sio_api_discovery_v1_EndpointConditions(ref common.ReferenceCallba
},
"serving": {
SchemaProps: spec.SchemaProps{
Description: "serving is identical to ready except that it is set regardless of the terminating state of endpoints. This condition should be set to true for a ready endpoint that is terminating. If nil, consumers should defer to the ready condition. This field can be enabled with the EndpointSliceTerminatingCondition feature gate.",
Description: "serving is identical to ready except that it is set regardless of the terminating state of endpoints. This condition should be set to true for a ready endpoint that is terminating. If nil, consumers should defer to the ready condition.",
Type: []string{"boolean"},
Format: "",
},
},
"terminating": {
SchemaProps: spec.SchemaProps{
Description: "terminating indicates that this endpoint is terminating. A nil value indicates an unknown state. Consumers should interpret this unknown state to mean that the endpoint is not terminating. This field can be enabled with the EndpointSliceTerminatingCondition feature gate.",
Description: "terminating indicates that this endpoint is terminating. A nil value indicates an unknown state. Consumers should interpret this unknown state to mean that the endpoint is not terminating.",
Type: []string{"boolean"},
Format: "",
},
@ -27367,14 +27367,14 @@ func schema_k8sio_api_discovery_v1beta1_EndpointConditions(ref common.ReferenceC
},
"serving": {
SchemaProps: spec.SchemaProps{
Description: "serving is identical to ready except that it is set regardless of the terminating state of endpoints. This condition should be set to true for a ready endpoint that is terminating. If nil, consumers should defer to the ready condition. This field can be enabled with the EndpointSliceTerminatingCondition feature gate.",
Description: "serving is identical to ready except that it is set regardless of the terminating state of endpoints. This condition should be set to true for a ready endpoint that is terminating. If nil, consumers should defer to the ready condition.",
Type: []string{"boolean"},
Format: "",
},
},
"terminating": {
SchemaProps: spec.SchemaProps{
Description: "terminating indicates that this endpoint is terminating. A nil value indicates an unknown state. Consumers should interpret this unknown state to mean that the endpoint is not terminating. This field can be enabled with the EndpointSliceTerminatingCondition feature gate.",
Description: "terminating indicates that this endpoint is terminating. A nil value indicates an unknown state. Consumers should interpret this unknown state to mean that the endpoint is not terminating.",
Type: []string{"boolean"},
Format: "",
},

View File

@ -123,15 +123,10 @@ func (endpointSliceStrategy) AllowUnconditionalUpdate() bool {
// dropDisabledConditionsOnCreate will drop any fields that are disabled.
func dropDisabledFieldsOnCreate(endpointSlice *discovery.EndpointSlice) {
dropTerminating := !utilfeature.DefaultFeatureGate.Enabled(features.EndpointSliceTerminatingCondition)
dropHints := !utilfeature.DefaultFeatureGate.Enabled(features.TopologyAwareHints)
if dropHints || dropTerminating {
if dropHints {
for i := range endpointSlice.Endpoints {
if dropTerminating {
endpointSlice.Endpoints[i].Conditions.Serving = nil
endpointSlice.Endpoints[i].Conditions.Terminating = nil
}
if dropHints {
endpointSlice.Endpoints[i].Hints = nil
}
@ -142,16 +137,6 @@ func dropDisabledFieldsOnCreate(endpointSlice *discovery.EndpointSlice) {
// dropDisabledFieldsOnUpdate will drop any disable fields that have not already
// been set on the EndpointSlice.
func dropDisabledFieldsOnUpdate(oldEPS, newEPS *discovery.EndpointSlice) {
dropTerminating := !utilfeature.DefaultFeatureGate.Enabled(features.EndpointSliceTerminatingCondition)
if dropTerminating {
for _, ep := range oldEPS.Endpoints {
if ep.Conditions.Serving != nil || ep.Conditions.Terminating != nil {
dropTerminating = false
break
}
}
}
dropHints := !utilfeature.DefaultFeatureGate.Enabled(features.TopologyAwareHints)
if dropHints {
for _, ep := range oldEPS.Endpoints {
@ -162,12 +147,8 @@ func dropDisabledFieldsOnUpdate(oldEPS, newEPS *discovery.EndpointSlice) {
}
}
if dropHints || dropTerminating {
if dropHints {
for i := range newEPS.Endpoints {
if dropTerminating {
newEPS.Endpoints[i].Conditions.Serving = nil
newEPS.Endpoints[i].Conditions.Terminating = nil
}
if dropHints {
newEPS.Endpoints[i].Hints = nil
}

View File

@ -34,108 +34,11 @@ import (
func Test_dropDisabledFieldsOnCreate(t *testing.T) {
testcases := []struct {
name string
terminatingGateEnabled bool
hintsGateEnabled bool
eps *discovery.EndpointSlice
expectedEPS *discovery.EndpointSlice
name string
hintsGateEnabled bool
eps *discovery.EndpointSlice
expectedEPS *discovery.EndpointSlice
}{
{
name: "terminating gate enabled, field should be allowed",
terminatingGateEnabled: true,
eps: &discovery.EndpointSlice{
Endpoints: []discovery.Endpoint{
{
Conditions: discovery.EndpointConditions{
Serving: utilpointer.BoolPtr(true),
Terminating: utilpointer.BoolPtr(false),
},
},
{
Conditions: discovery.EndpointConditions{
Serving: utilpointer.BoolPtr(true),
Terminating: utilpointer.BoolPtr(true),
},
},
{
Conditions: discovery.EndpointConditions{
Serving: nil,
Terminating: nil,
},
},
},
},
expectedEPS: &discovery.EndpointSlice{
Endpoints: []discovery.Endpoint{
{
Conditions: discovery.EndpointConditions{
Serving: utilpointer.BoolPtr(true),
Terminating: utilpointer.BoolPtr(false),
},
},
{
Conditions: discovery.EndpointConditions{
Serving: utilpointer.BoolPtr(true),
Terminating: utilpointer.BoolPtr(true),
},
},
{
Conditions: discovery.EndpointConditions{
Serving: nil,
Terminating: nil,
},
},
},
},
},
{
name: "terminating gate disabled, field should be set to nil",
terminatingGateEnabled: false,
eps: &discovery.EndpointSlice{
Endpoints: []discovery.Endpoint{
{
Conditions: discovery.EndpointConditions{
Serving: utilpointer.BoolPtr(true),
Terminating: utilpointer.BoolPtr(false),
},
},
{
Conditions: discovery.EndpointConditions{
Serving: utilpointer.BoolPtr(true),
Terminating: utilpointer.BoolPtr(true),
},
},
{
Conditions: discovery.EndpointConditions{
Serving: nil,
Terminating: nil,
},
},
},
},
expectedEPS: &discovery.EndpointSlice{
Endpoints: []discovery.Endpoint{
{
Conditions: discovery.EndpointConditions{
Serving: nil,
Terminating: nil,
},
},
{
Conditions: discovery.EndpointConditions{
Serving: nil,
Terminating: nil,
},
},
{
Conditions: discovery.EndpointConditions{
Serving: nil,
Terminating: nil,
},
},
},
},
},
{
name: "node name gate enabled, field should be allowed",
eps: &discovery.EndpointSlice{
@ -163,7 +66,6 @@ func Test_dropDisabledFieldsOnCreate(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.TopologyAwareHints, testcase.hintsGateEnabled)()
dropDisabledFieldsOnCreate(testcase.eps)
@ -178,290 +80,12 @@ func Test_dropDisabledFieldsOnCreate(t *testing.T) {
func Test_dropDisabledFieldsOnUpdate(t *testing.T) {
testcases := []struct {
name string
terminatingGateEnabled bool
hintsGateEnabled bool
oldEPS *discovery.EndpointSlice
newEPS *discovery.EndpointSlice
expectedEPS *discovery.EndpointSlice
name string
hintsGateEnabled bool
oldEPS *discovery.EndpointSlice
newEPS *discovery.EndpointSlice
expectedEPS *discovery.EndpointSlice
}{
{
name: "terminating gate enabled, field should be allowed",
terminatingGateEnabled: true,
oldEPS: &discovery.EndpointSlice{
Endpoints: []discovery.Endpoint{
{
Conditions: discovery.EndpointConditions{
Serving: utilpointer.BoolPtr(true),
Terminating: utilpointer.BoolPtr(false),
},
},
{
Conditions: discovery.EndpointConditions{
Serving: utilpointer.BoolPtr(true),
Terminating: utilpointer.BoolPtr(true),
},
},
{
Conditions: discovery.EndpointConditions{
Serving: nil,
Terminating: nil,
},
},
},
},
newEPS: &discovery.EndpointSlice{
Endpoints: []discovery.Endpoint{
{
Conditions: discovery.EndpointConditions{
Serving: utilpointer.BoolPtr(true),
Terminating: utilpointer.BoolPtr(false),
},
},
{
Conditions: discovery.EndpointConditions{
Serving: utilpointer.BoolPtr(true),
Terminating: utilpointer.BoolPtr(true),
},
},
{
Conditions: discovery.EndpointConditions{
Serving: nil,
Terminating: nil,
},
},
},
},
expectedEPS: &discovery.EndpointSlice{
Endpoints: []discovery.Endpoint{
{
Conditions: discovery.EndpointConditions{
Serving: utilpointer.BoolPtr(true),
Terminating: utilpointer.BoolPtr(false),
},
},
{
Conditions: discovery.EndpointConditions{
Serving: utilpointer.BoolPtr(true),
Terminating: utilpointer.BoolPtr(true),
},
},
{
Conditions: discovery.EndpointConditions{
Serving: nil,
Terminating: nil,
},
},
},
},
},
{
name: "terminating gate disabled, and not set on existing EPS",
terminatingGateEnabled: false,
oldEPS: &discovery.EndpointSlice{
Endpoints: []discovery.Endpoint{
{
Conditions: discovery.EndpointConditions{
Serving: nil,
Terminating: nil,
},
},
{
Conditions: discovery.EndpointConditions{
Serving: nil,
Terminating: nil,
},
},
{
Conditions: discovery.EndpointConditions{
Serving: nil,
Terminating: nil,
},
},
},
},
newEPS: &discovery.EndpointSlice{
Endpoints: []discovery.Endpoint{
{
Conditions: discovery.EndpointConditions{
Serving: utilpointer.BoolPtr(true),
Terminating: utilpointer.BoolPtr(false),
},
},
{
Conditions: discovery.EndpointConditions{
Serving: utilpointer.BoolPtr(true),
Terminating: utilpointer.BoolPtr(true),
},
},
{
Conditions: discovery.EndpointConditions{
Serving: nil,
Terminating: nil,
},
},
},
},
expectedEPS: &discovery.EndpointSlice{
Endpoints: []discovery.Endpoint{
{
Conditions: discovery.EndpointConditions{
Serving: nil,
Terminating: nil,
},
},
{
Conditions: discovery.EndpointConditions{
Serving: nil,
Terminating: nil,
},
},
{
Conditions: discovery.EndpointConditions{
Serving: nil,
Terminating: nil,
},
},
},
},
},
{
name: "terminating gate disabled, and set on existing EPS",
terminatingGateEnabled: false,
oldEPS: &discovery.EndpointSlice{
Endpoints: []discovery.Endpoint{
{
Conditions: discovery.EndpointConditions{
Serving: utilpointer.BoolPtr(true),
Terminating: utilpointer.BoolPtr(false),
},
},
{
Conditions: discovery.EndpointConditions{
Serving: utilpointer.BoolPtr(true),
Terminating: utilpointer.BoolPtr(true),
},
},
{
Conditions: discovery.EndpointConditions{
Serving: nil,
Terminating: nil,
},
},
},
},
newEPS: &discovery.EndpointSlice{
Endpoints: []discovery.Endpoint{
{
Conditions: discovery.EndpointConditions{
Serving: utilpointer.BoolPtr(true),
Terminating: utilpointer.BoolPtr(false),
},
},
{
Conditions: discovery.EndpointConditions{
Serving: utilpointer.BoolPtr(true),
Terminating: utilpointer.BoolPtr(true),
},
},
{
Conditions: discovery.EndpointConditions{
Serving: nil,
Terminating: nil,
},
},
},
},
expectedEPS: &discovery.EndpointSlice{
Endpoints: []discovery.Endpoint{
{
Conditions: discovery.EndpointConditions{
Serving: utilpointer.BoolPtr(true),
Terminating: utilpointer.BoolPtr(false),
},
},
{
Conditions: discovery.EndpointConditions{
Serving: utilpointer.BoolPtr(true),
Terminating: utilpointer.BoolPtr(true),
},
},
{
Conditions: discovery.EndpointConditions{
Serving: nil,
Terminating: nil,
},
},
},
},
},
{
name: "terminating gate disabled, and set on existing EPS with new values",
terminatingGateEnabled: false,
oldEPS: &discovery.EndpointSlice{
Endpoints: []discovery.Endpoint{
{
Conditions: discovery.EndpointConditions{
Serving: utilpointer.BoolPtr(false),
Terminating: utilpointer.BoolPtr(false),
},
},
{
Conditions: discovery.EndpointConditions{
Serving: utilpointer.BoolPtr(true),
Terminating: utilpointer.BoolPtr(true),
},
},
{
Conditions: discovery.EndpointConditions{
Terminating: nil,
},
},
},
},
newEPS: &discovery.EndpointSlice{
Endpoints: []discovery.Endpoint{
{
Conditions: discovery.EndpointConditions{
Serving: utilpointer.BoolPtr(true),
Terminating: utilpointer.BoolPtr(true),
},
},
{
Conditions: discovery.EndpointConditions{
Serving: utilpointer.BoolPtr(false),
Terminating: utilpointer.BoolPtr(false),
},
},
{
Conditions: discovery.EndpointConditions{
Terminating: utilpointer.BoolPtr(false),
},
},
},
},
expectedEPS: &discovery.EndpointSlice{
Endpoints: []discovery.Endpoint{
{
Conditions: discovery.EndpointConditions{
Serving: utilpointer.BoolPtr(true),
Terminating: utilpointer.BoolPtr(true),
},
},
{
Conditions: discovery.EndpointConditions{
Serving: utilpointer.BoolPtr(false),
Terminating: utilpointer.BoolPtr(false),
},
},
{
Conditions: discovery.EndpointConditions{
Terminating: utilpointer.BoolPtr(false),
},
},
},
},
},
{
name: "node name gate enabled, set on new EPS",
oldEPS: &discovery.EndpointSlice{
@ -658,7 +282,6 @@ func Test_dropDisabledFieldsOnUpdate(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.TopologyAwareHints, testcase.hintsGateEnabled)()
dropDisabledFieldsOnUpdate(testcase.oldEPS, testcase.newEPS)

View File

@ -93,15 +93,13 @@ message EndpointConditions {
// serving is identical to ready except that it is set regardless of the
// terminating state of endpoints. This condition should be set to true for
// a ready endpoint that is terminating. If nil, consumers should defer to
// the ready condition. This field can be enabled with the
// EndpointSliceTerminatingCondition feature gate.
// the ready condition.
// +optional
optional bool serving = 2;
// terminating indicates that this endpoint is terminating. A nil value
// indicates an unknown state. Consumers should interpret this unknown state
// to mean that the endpoint is not terminating. This field can be enabled
// with the EndpointSliceTerminatingCondition feature gate.
// to mean that the endpoint is not terminating.
// +optional
optional bool terminating = 3;
}

View File

@ -126,15 +126,13 @@ type EndpointConditions struct {
// serving is identical to ready except that it is set regardless of the
// terminating state of endpoints. This condition should be set to true for
// a ready endpoint that is terminating. If nil, consumers should defer to
// the ready condition. This field can be enabled with the
// EndpointSliceTerminatingCondition feature gate.
// the ready condition.
// +optional
Serving *bool `json:"serving,omitempty" protobuf:"bytes,2,name=serving"`
// terminating indicates that this endpoint is terminating. A nil value
// indicates an unknown state. Consumers should interpret this unknown state
// to mean that the endpoint is not terminating. This field can be enabled
// with the EndpointSliceTerminatingCondition feature gate.
// to mean that the endpoint is not terminating.
// +optional
Terminating *bool `json:"terminating,omitempty" protobuf:"bytes,3,name=terminating"`
}

View File

@ -46,8 +46,8 @@ func (Endpoint) SwaggerDoc() map[string]string {
var map_EndpointConditions = map[string]string{
"": "EndpointConditions represents the current condition of an endpoint.",
"ready": "ready indicates that this endpoint is prepared to receive traffic, according to whatever system is managing the endpoint. A nil value indicates an unknown state. In most cases consumers should interpret this unknown state as ready. For compatibility reasons, ready should never be \"true\" for terminating endpoints.",
"serving": "serving is identical to ready except that it is set regardless of the terminating state of endpoints. This condition should be set to true for a ready endpoint that is terminating. If nil, consumers should defer to the ready condition. This field can be enabled with the EndpointSliceTerminatingCondition feature gate.",
"terminating": "terminating indicates that this endpoint is terminating. A nil value indicates an unknown state. Consumers should interpret this unknown state to mean that the endpoint is not terminating. This field can be enabled with the EndpointSliceTerminatingCondition feature gate.",
"serving": "serving is identical to ready except that it is set regardless of the terminating state of endpoints. This condition should be set to true for a ready endpoint that is terminating. If nil, consumers should defer to the ready condition.",
"terminating": "terminating indicates that this endpoint is terminating. A nil value indicates an unknown state. Consumers should interpret this unknown state to mean that the endpoint is not terminating.",
}
func (EndpointConditions) SwaggerDoc() map[string]string {

View File

@ -97,15 +97,13 @@ message EndpointConditions {
// serving is identical to ready except that it is set regardless of the
// terminating state of endpoints. This condition should be set to true for
// a ready endpoint that is terminating. If nil, consumers should defer to
// the ready condition. This field can be enabled with the
// EndpointSliceTerminatingCondition feature gate.
// the ready condition.
// +optional
optional bool serving = 2;
// terminating indicates that this endpoint is terminating. A nil value
// indicates an unknown state. Consumers should interpret this unknown state
// to mean that the endpoint is not terminating. This field can be enabled
// with the EndpointSliceTerminatingCondition feature gate.
// to mean that the endpoint is not terminating.
// +optional
optional bool terminating = 3;
}

View File

@ -132,15 +132,13 @@ type EndpointConditions struct {
// serving is identical to ready except that it is set regardless of the
// terminating state of endpoints. This condition should be set to true for
// a ready endpoint that is terminating. If nil, consumers should defer to
// the ready condition. This field can be enabled with the
// EndpointSliceTerminatingCondition feature gate.
// the ready condition.
// +optional
Serving *bool `json:"serving,omitempty" protobuf:"bytes,2,name=serving"`
// terminating indicates that this endpoint is terminating. A nil value
// indicates an unknown state. Consumers should interpret this unknown state
// to mean that the endpoint is not terminating. This field can be enabled
// with the EndpointSliceTerminatingCondition feature gate.
// to mean that the endpoint is not terminating.
// +optional
Terminating *bool `json:"terminating,omitempty" protobuf:"bytes,3,name=terminating"`
}

View File

@ -45,8 +45,8 @@ func (Endpoint) SwaggerDoc() map[string]string {
var map_EndpointConditions = map[string]string{
"": "EndpointConditions represents the current condition of an endpoint.",
"ready": "ready indicates that this endpoint is prepared to receive traffic, according to whatever system is managing the endpoint. A nil value indicates an unknown state. In most cases consumers should interpret this unknown state as ready. For compatibility reasons, ready should never be \"true\" for terminating endpoints.",
"serving": "serving is identical to ready except that it is set regardless of the terminating state of endpoints. This condition should be set to true for a ready endpoint that is terminating. If nil, consumers should defer to the ready condition. This field can be enabled with the EndpointSliceTerminatingCondition feature gate.",
"terminating": "terminating indicates that this endpoint is terminating. A nil value indicates an unknown state. Consumers should interpret this unknown state to mean that the endpoint is not terminating. This field can be enabled with the EndpointSliceTerminatingCondition feature gate.",
"serving": "serving is identical to ready except that it is set regardless of the terminating state of endpoints. This condition should be set to true for a ready endpoint that is terminating. If nil, consumers should defer to the ready condition.",
"terminating": "terminating indicates that this endpoint is terminating. A nil value indicates an unknown state. Consumers should interpret this unknown state to mean that the endpoint is not terminating.",
}
func (EndpointConditions) SwaggerDoc() map[string]string {

View File

@ -27,69 +27,24 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/intstr"
"k8s.io/apimachinery/pkg/util/wait"
utilfeature "k8s.io/apiserver/pkg/util/feature"
"k8s.io/client-go/informers"
clientset "k8s.io/client-go/kubernetes"
featuregatetesting "k8s.io/component-base/featuregate/testing"
kubeapiservertesting "k8s.io/kubernetes/cmd/kube-apiserver/app/testing"
"k8s.io/kubernetes/pkg/controller/endpointslice"
"k8s.io/kubernetes/pkg/features"
"k8s.io/kubernetes/test/integration/framework"
utilpointer "k8s.io/utils/pointer"
)
// TestEndpointSliceTerminating tests that terminating pods are NOT included in EndpointSlice when
// the feature gate EndpointSliceTerminatingCondition is off. If the gate is on, it tests that
// terminating endpoints are included but with the correct conditions set for ready, serving and terminating.
// TestEndpointSliceTerminating tests that terminating endpoints are included with the
// correct conditions set for ready, serving and terminating.
func TestEndpointSliceTerminating(t *testing.T) {
testcases := []struct {
name string
podStatus corev1.PodStatus
expectedEndpoints []discovery.Endpoint
terminatingGate bool
}{
{
name: "ready terminating pods not included, terminating gate off",
podStatus: corev1.PodStatus{
Phase: corev1.PodRunning,
Conditions: []corev1.PodCondition{
{
Type: corev1.PodReady,
Status: corev1.ConditionTrue,
},
},
PodIP: "10.0.0.1",
PodIPs: []corev1.PodIP{
{
IP: "10.0.0.1",
},
},
},
expectedEndpoints: []discovery.Endpoint{},
terminatingGate: false,
},
{
name: "not ready terminating pods not included, terminating gate off",
podStatus: corev1.PodStatus{
Phase: corev1.PodRunning,
Conditions: []corev1.PodCondition{
{
Type: corev1.PodReady,
Status: corev1.ConditionFalse,
},
},
PodIP: "10.0.0.1",
PodIPs: []corev1.PodIP{
{
IP: "10.0.0.1",
},
},
},
expectedEndpoints: []discovery.Endpoint{},
terminatingGate: false,
},
{
name: "ready terminating pods included, terminating gate on",
name: "ready terminating pods",
podStatus: corev1.PodStatus{
Phase: corev1.PodRunning,
Conditions: []corev1.PodCondition{
@ -115,10 +70,9 @@ func TestEndpointSliceTerminating(t *testing.T) {
},
},
},
terminatingGate: true,
},
{
name: "not ready terminating pods included, terminating gate on",
name: "not ready terminating pods",
podStatus: corev1.PodStatus{
Phase: corev1.PodRunning,
Conditions: []corev1.PodCondition{
@ -144,14 +98,11 @@ func TestEndpointSliceTerminating(t *testing.T) {
},
},
},
terminatingGate: true,
},
}
for _, testcase := range testcases {
t.Run(testcase.name, func(t *testing.T) {
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.EndpointSliceTerminatingCondition, testcase.terminatingGate)()
// Disable ServiceAccount admission plugin as we don't have serviceaccount controller running.
server := kubeapiservertesting.StartTestServerOrDie(t, nil, []string{"--disable-admission-plugins=ServiceAccount"}, framework.SharedEtcd())
defer server.TearDownFn()