Merge pull request #92968 from andrewsykim/endpointslice-termination

Add Terminating Condition to EndpointSlice
This commit is contained in:
Kubernetes Prow Robot 2020-11-05 19:32:50 -08:00 committed by GitHub
commit cccd77bd3a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
27 changed files with 1807 additions and 219 deletions

View File

@ -10914,7 +10914,15 @@
"description": "EndpointConditions represents the current condition of an endpoint.",
"properties": {
"ready": {
"description": "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.",
"description": "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.",
"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.",
"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.",
"type": "boolean"
}
},

View File

@ -114,8 +114,21 @@ type EndpointConditions struct {
// 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.
// unknown state as ready. For compatibility reasons, ready should never be
// "true" for terminating endpoints.
Ready *bool
// 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.
// +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.
// +optional
Terminating *bool
}
// EndpointPort represents a Port used by an EndpointSlice.

View File

@ -125,6 +125,8 @@ func Convert_discovery_Endpoint_To_v1alpha1_Endpoint(in *discovery.Endpoint, out
func autoConvert_v1alpha1_EndpointConditions_To_discovery_EndpointConditions(in *v1alpha1.EndpointConditions, out *discovery.EndpointConditions, s conversion.Scope) error {
out.Ready = (*bool)(unsafe.Pointer(in.Ready))
out.Serving = (*bool)(unsafe.Pointer(in.Serving))
out.Terminating = (*bool)(unsafe.Pointer(in.Terminating))
return nil
}
@ -135,6 +137,8 @@ func Convert_v1alpha1_EndpointConditions_To_discovery_EndpointConditions(in *v1a
func autoConvert_discovery_EndpointConditions_To_v1alpha1_EndpointConditions(in *discovery.EndpointConditions, out *v1alpha1.EndpointConditions, s conversion.Scope) error {
out.Ready = (*bool)(unsafe.Pointer(in.Ready))
out.Serving = (*bool)(unsafe.Pointer(in.Serving))
out.Terminating = (*bool)(unsafe.Pointer(in.Terminating))
return nil
}

View File

@ -125,6 +125,8 @@ func Convert_discovery_Endpoint_To_v1beta1_Endpoint(in *discovery.Endpoint, out
func autoConvert_v1beta1_EndpointConditions_To_discovery_EndpointConditions(in *v1beta1.EndpointConditions, out *discovery.EndpointConditions, s conversion.Scope) error {
out.Ready = (*bool)(unsafe.Pointer(in.Ready))
out.Serving = (*bool)(unsafe.Pointer(in.Serving))
out.Terminating = (*bool)(unsafe.Pointer(in.Terminating))
return nil
}
@ -135,6 +137,8 @@ func Convert_v1beta1_EndpointConditions_To_discovery_EndpointConditions(in *v1be
func autoConvert_discovery_EndpointConditions_To_v1beta1_EndpointConditions(in *discovery.EndpointConditions, out *v1beta1.EndpointConditions, s conversion.Scope) error {
out.Ready = (*bool)(unsafe.Pointer(in.Ready))
out.Serving = (*bool)(unsafe.Pointer(in.Serving))
out.Terminating = (*bool)(unsafe.Pointer(in.Terminating))
return nil
}

View File

@ -72,6 +72,16 @@ func (in *EndpointConditions) DeepCopyInto(out *EndpointConditions) {
*out = new(bool)
**out = **in
}
if in.Serving != nil {
in, out := &in.Serving, &out.Serving
*out = new(bool)
**out = **in
}
if in.Terminating != nil {
in, out := &in.Terminating, &out.Terminating
*out = new(bool)
**out = **in
}
return
}

View File

@ -19,6 +19,7 @@ go_library(
"//pkg/controller:go_default_library",
"//pkg/controller/endpointslice/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",
@ -31,6 +32,7 @@ go_library(
"//staging/src/k8s.io/apimachinery/pkg/util/runtime:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/util/sets: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",
@ -61,6 +63,7 @@ go_test(
"//pkg/controller:go_default_library",
"//pkg/controller/endpointslice/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",
@ -71,11 +74,13 @@ go_test(
"//staging/src/k8s.io/apimachinery/pkg/util/intstr: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/listers/core/v1:go_default_library",
"//staging/src/k8s.io/client-go/testing:go_default_library",
"//staging/src/k8s.io/client-go/tools/cache: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",

View File

@ -33,12 +33,15 @@ import (
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/intstr"
"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"
endpointutil "k8s.io/kubernetes/pkg/controller/util/endpoint"
"k8s.io/kubernetes/pkg/features"
utilpointer "k8s.io/utils/pointer"
)
@ -186,11 +189,11 @@ func TestSyncServicePodSelection(t *testing.T) {
client, esController := newController([]string{"node-1"}, time.Duration(0))
ns := metav1.NamespaceDefault
pod1 := newPod(1, ns, true, 0)
pod1 := newPod(1, ns, true, 0, false)
esController.podStore.Add(pod1)
// ensure this pod will not match the selector
pod2 := newPod(2, ns, true, 0)
pod2 := newPod(2, ns, true, 0, false)
pod2.Labels["foo"] = "boo"
esController.podStore.Add(pod2)
@ -335,76 +338,708 @@ func TestOnEndpointSliceUpdate(t *testing.T) {
assert.Equal(t, 1, esController.queue.Len())
}
// Ensure SyncService handles a variety of protocols and IPs appropriately.
func TestSyncServiceFull(t *testing.T) {
client, esController := newController([]string{"node-1"}, time.Duration(0))
namespace := metav1.NamespaceDefault
serviceName := "all-the-protocols"
func TestSyncService(t *testing.T) {
creationTimestamp := metav1.Now()
deletionTimestamp := metav1.Now()
pod1 := newPod(1, namespace, true, 0)
pod1.Status.PodIPs = []v1.PodIP{{IP: "1.2.3.4"}}
esController.podStore.Add(pod1)
pod2 := newPod(2, namespace, true, 0)
pod2.Status.PodIPs = []v1.PodIP{{IP: "1.2.3.5"}, {IP: "1234::5678:0000:0000:9abc:def0"}}
esController.podStore.Add(pod2)
// create service with all protocols and multiple ports
serviceCreateTime := time.Now()
service := &v1.Service{
ObjectMeta: metav1.ObjectMeta{
Name: serviceName,
Namespace: namespace,
CreationTimestamp: metav1.NewTime(serviceCreateTime),
},
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},
testcases := []struct {
name string
service *v1.Service
pods []*v1.Pod
expectedEndpointPorts []discovery.EndpointPort
expectedEndpoints []discovery.Endpoint
terminatingGateEnabled bool
}{
{
name: "pods with multiple IPs and Service with ipFamilies=ipv4",
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},
},
},
Selector: map[string]string{"foo": "bar"},
IPFamilies: []v1.IPFamily{v1.IPv6Protocol},
pods: []*v1.Pod{
{
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: nil,
},
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",
},
{
IP: "fd08::5678:0000:0000:9abc:def0",
},
},
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"},
Topology: map[string]string{"kubernetes.io/hostname": "node-1"},
},
{
Conditions: discovery.EndpointConditions{
Ready: utilpointer.BoolPtr(true),
},
Addresses: []string{"10.0.0.2"},
TargetRef: &v1.ObjectReference{Kind: "Pod", Namespace: "default", Name: "pod1"},
Topology: map[string]string{"kubernetes.io/hostname": "node-1"},
},
},
},
{
name: "pods with multiple IPs and Service with ipFamilies=ipv6",
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.IPv6Protocol},
},
},
pods: []*v1.Pod{
{
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: nil,
},
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",
},
{
IP: "fd08::5678:0000:0000:9abc:def0",
},
},
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{"fd08::5678:0000:0000:9abc:def0"},
TargetRef: &v1.ObjectReference{Kind: "Pod", Namespace: "default", Name: "pod1"},
Topology: map[string]string{"kubernetes.io/hostname": "node-1"},
},
},
},
{
name: "Terminating pods with EndpointSliceTerminatingCondition enabled",
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),
Serving: utilpointer.BoolPtr(true),
Terminating: utilpointer.BoolPtr(false),
},
Addresses: []string{"10.0.0.1"},
TargetRef: &v1.ObjectReference{Kind: "Pod", Namespace: "default", Name: "pod0"},
Topology: map[string]string{"kubernetes.io/hostname": "node-1"},
},
{
Conditions: discovery.EndpointConditions{
Ready: utilpointer.BoolPtr(false),
Serving: utilpointer.BoolPtr(true),
Terminating: utilpointer.BoolPtr(true),
},
Addresses: []string{"10.0.0.2"},
TargetRef: &v1.ObjectReference{Kind: "Pod", Namespace: "default", Name: "pod1"},
Topology: map[string]string{"kubernetes.io/hostname": "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"},
Topology: map[string]string{"kubernetes.io/hostname": "node-1"},
},
},
terminatingGateEnabled: false,
},
{
name: "Not ready terminating pods with EndpointSliceTerminatingCondition enabled",
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),
Serving: utilpointer.BoolPtr(true),
Terminating: utilpointer.BoolPtr(false),
},
Addresses: []string{"10.0.0.1"},
TargetRef: &v1.ObjectReference{Kind: "Pod", Namespace: "default", Name: "pod0"},
Topology: map[string]string{"kubernetes.io/hostname": "node-1"},
},
{
Conditions: discovery.EndpointConditions{
Ready: utilpointer.BoolPtr(false),
Serving: utilpointer.BoolPtr(false),
Terminating: utilpointer.BoolPtr(true),
},
Addresses: []string{"10.0.0.2"},
TargetRef: &v1.ObjectReference{Kind: "Pod", Namespace: "default", Name: "pod1"},
Topology: map[string]string{"kubernetes.io/hostname": "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"},
Topology: map[string]string{"kubernetes.io/hostname": "node-1"},
},
},
terminatingGateEnabled: false,
},
}
esController.serviceStore.Add(service)
_, err := esController.client.CoreV1().Services(namespace).Create(context.TODO(), service, metav1.CreateOptions{})
assert.Nil(t, err, "Expected no error creating service")
// run through full sync service loop
err = esController.syncService(fmt.Sprintf("%s/%s", namespace, serviceName))
assert.NoError(t, err)
for _, testcase := range testcases {
t.Run(testcase.name, func(t *testing.T) {
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.EndpointSliceTerminatingCondition, testcase.terminatingGateEnabled)()
// last action should be to create endpoint slice
expectActions(t, client.Actions(), 1, "create", "endpointslices")
sliceList, err := client.DiscoveryV1beta1().EndpointSlices(namespace).List(context.TODO(), metav1.ListOptions{})
assert.Nil(t, err, "Expected no error fetching endpoint slices")
assert.Len(t, sliceList.Items, 1, "Expected 1 endpoint slices")
client, esController := newController([]string{"node-1"}, time.Duration(0))
// ensure all attributes of endpoint slice match expected state
slice := sliceList.Items[0]
assert.Len(t, slice.Endpoints, 1, "Expected 1 endpoints in first slice")
assert.Equal(t, slice.Annotations["endpoints.kubernetes.io/last-change-trigger-time"], serviceCreateTime.Format(time.RFC3339Nano))
assert.EqualValues(t, []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)),
}}, slice.Ports)
for _, pod := range testcase.pods {
esController.podStore.Add(pod)
}
esController.serviceStore.Add(testcase.service)
assert.ElementsMatch(t, []discovery.Endpoint{{
Conditions: discovery.EndpointConditions{Ready: utilpointer.BoolPtr(true)},
Addresses: []string{"1234::5678:0000:0000:9abc:def0"},
TargetRef: &v1.ObjectReference{Kind: "Pod", Namespace: namespace, Name: pod2.Name},
Topology: map[string]string{"kubernetes.io/hostname": "node-1"},
}}, slice.Endpoints)
_, err := esController.client.CoreV1().Services(testcase.service.Namespace).Create(context.TODO(), testcase.service, metav1.CreateOptions{})
assert.Nil(t, err, "Expected no error creating service")
err = esController.syncService(fmt.Sprintf("%s/%s", testcase.service.Namespace, testcase.service.Name))
assert.Nil(t, err)
// last action should be to create endpoint slice
expectActions(t, client.Actions(), 1, "create", "endpointslices")
sliceList, err := client.DiscoveryV1beta1().EndpointSlices(testcase.service.Namespace).List(context.TODO(), metav1.ListOptions{})
assert.Nil(t, err, "Expected no error fetching endpoint slices")
assert.Len(t, sliceList.Items, 1, "Expected 1 endpoint slices")
// ensure all attributes of endpoint slice match expected state
slice := sliceList.Items[0]
assert.Equal(t, slice.Annotations["endpoints.kubernetes.io/last-change-trigger-time"], creationTimestamp.Format(time.RFC3339Nano))
assert.EqualValues(t, testcase.expectedEndpointPorts, slice.Ports)
assert.ElementsMatch(t, testcase.expectedEndpoints, slice.Endpoints)
})
}
}
// TestPodAddsBatching verifies that endpoint updates caused by pod addition are batched together.
@ -499,7 +1134,7 @@ func TestPodAddsBatching(t *testing.T) {
for i, add := range tc.adds {
time.Sleep(add.delay)
p := newPod(i, ns, true, 0)
p := newPod(i, ns, true, 0, false)
esController.podStore.Add(p)
esController.addPod(p)
}
@ -789,7 +1424,7 @@ func TestPodDeleteBatching(t *testing.T) {
func addPods(t *testing.T, esController *endpointSliceController, namespace string, podsCount int) {
t.Helper()
for i := 0; i < podsCount; i++ {
pod := newPod(i, namespace, true, 0)
pod := newPod(i, namespace, true, 0, false)
esController.podStore.Add(pod)
}
}

View File

@ -29,10 +29,12 @@ 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/kubernetes/pkg/controller/endpointslice/metrics"
endpointutil "k8s.io/kubernetes/pkg/controller/util/endpoint"
"k8s.io/kubernetes/pkg/features"
)
// reconciler is responsible for transforming current EndpointSlice state into
@ -136,7 +138,8 @@ func (r *reconciler) reconcileByAddressType(service *corev1.Service, pods []*cor
numDesiredEndpoints := 0
for _, pod := range pods {
if !endpointutil.ShouldPodBeInEndpoints(pod, service.Spec.PublishNotReadyAddresses) {
includeTerminating := service.Spec.PublishNotReadyAddresses || utilfeature.DefaultFeatureGate.Enabled(features.EndpointSliceTerminatingCondition)
if !endpointutil.ShouldPodBeInEndpointSlice(pod, includeTerminating) {
continue
}

View File

@ -32,13 +32,16 @@ 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/features"
utilpointer "k8s.io/utils/pointer"
)
@ -95,7 +98,7 @@ func TestReconcile1Pod(t *testing.T) {
dualStackSvc.Spec.ClusterIP = "10.0.0.10"
dualStackSvc.Spec.ClusterIPs = []string{"10.0.0.10", "2000::1"}
pod1 := newPod(1, namespace, true, 1)
pod1 := newPod(1, namespace, true, 1, false)
pod1.Status.PodIPs = []corev1.PodIP{{IP: "1.2.3.4"}, {IP: "1234::5678:0000:0000:9abc:def0"}}
pod1.Spec.Hostname = "example-hostname"
node1 := &corev1.Node{
@ -114,6 +117,7 @@ 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,
@ -140,7 +144,6 @@ func TestReconcile1Pod(t *testing.T) {
discovery.LabelServiceName: "foo",
},
},
"ipv4": {
service: svcv4,
expectedEndpointPerSlice: map[discovery.AddressType][]discovery.Endpoint{
@ -167,6 +170,37 @@ func TestReconcile1Pod(t *testing.T) {
corev1.IsHeadlessService: "",
},
},
"ipv4-with-terminating-gate-enabled": {
service: svcv4,
expectedEndpointPerSlice: map[discovery.AddressType][]discovery.Endpoint{
discovery.AddressTypeIPv4: {
{
Addresses: []string{"1.2.3.4"},
Conditions: discovery.EndpointConditions{
Ready: utilpointer.BoolPtr(true),
Serving: utilpointer.BoolPtr(true),
Terminating: utilpointer.BoolPtr(false),
},
Topology: map[string]string{
"kubernetes.io/hostname": "node-1",
"topology.kubernetes.io/zone": "us-central1-a",
"topology.kubernetes.io/region": "us-central1",
},
TargetRef: &corev1.ObjectReference{
Kind: "Pod",
Namespace: namespace,
Name: "pod1",
},
},
},
},
expectedLabels: map[string]string{
discovery.LabelManagedBy: controllerName,
discovery.LabelServiceName: "foo",
corev1.IsHeadlessService: "",
},
terminatingGateEnabled: true,
},
"ipv4-clusterip": {
service: svcv4ClusterIP,
expectedEndpointPerSlice: map[discovery.AddressType][]discovery.Endpoint{
@ -389,6 +423,8 @@ 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()
@ -496,7 +532,7 @@ func TestReconcile1EndpointSlicePublishNotReadyAddresses(t *testing.T) {
pods := []*corev1.Pod{}
for i := 0; i < 50; i++ {
ready := !(i%3 == 0)
pods = append(pods, newPod(i, namespace, ready, 1))
pods = append(pods, newPod(i, namespace, ready, 1, false))
}
r := newReconciler(client, []*corev1.Node{{ObjectMeta: metav1.ObjectMeta{Name: "node-1"}}}, defaultMaxEndpointsPerSlice)
@ -530,7 +566,7 @@ func TestReconcileManyPods(t *testing.T) {
pods := []*corev1.Pod{}
for i := 0; i < 250; i++ {
ready := !(i%3 == 0)
pods = append(pods, newPod(i, namespace, ready, 1))
pods = append(pods, newPod(i, namespace, ready, 1, false))
}
r := newReconciler(client, []*corev1.Node{{ObjectMeta: metav1.ObjectMeta{Name: "node-1"}}}, defaultMaxEndpointsPerSlice)
@ -563,7 +599,7 @@ func TestReconcileEndpointSlicesSomePreexisting(t *testing.T) {
pods := []*corev1.Pod{}
for i := 0; i < 250; i++ {
ready := !(i%3 == 0)
pods = append(pods, newPod(i, namespace, ready, 1))
pods = append(pods, newPod(i, namespace, ready, 1, false))
}
// have approximately 1/4 in first slice
@ -619,7 +655,7 @@ func TestReconcileEndpointSlicesSomePreexistingWorseAllocation(t *testing.T) {
pods := []*corev1.Pod{}
for i := 0; i < 300; i++ {
ready := !(i%3 == 0)
pods = append(pods, newPod(i, namespace, ready, 1))
pods = append(pods, newPod(i, namespace, ready, 1, false))
}
// have approximately 1/4 in first slice
@ -665,7 +701,7 @@ func TestReconcileEndpointSlicesUpdating(t *testing.T) {
pods := []*corev1.Pod{}
for i := 0; i < 250; i++ {
ready := !(i%3 == 0)
pods = append(pods, newPod(i, namespace, ready, 1))
pods = append(pods, newPod(i, namespace, ready, 1, false))
}
r := newReconciler(client, []*corev1.Node{{ObjectMeta: metav1.ObjectMeta{Name: "node-1"}}}, defaultMaxEndpointsPerSlice)
@ -698,7 +734,7 @@ func TestReconcileEndpointSlicesServicesLabelsUpdating(t *testing.T) {
pods := []*corev1.Pod{}
for i := 0; i < 250; i++ {
ready := !(i%3 == 0)
pods = append(pods, newPod(i, namespace, ready, 1))
pods = append(pods, newPod(i, namespace, ready, 1, false))
}
r := newReconciler(client, []*corev1.Node{{ObjectMeta: metav1.ObjectMeta{Name: "node-1"}}}, defaultMaxEndpointsPerSlice)
@ -742,7 +778,7 @@ func TestReconcileEndpointSlicesServicesReservedLabels(t *testing.T) {
pods := []*corev1.Pod{}
for i := 0; i < 250; i++ {
ready := !(i%3 == 0)
pods = append(pods, newPod(i, namespace, ready, 1))
pods = append(pods, newPod(i, namespace, ready, 1, false))
}
r := newReconciler(client, []*corev1.Node{{ObjectMeta: metav1.ObjectMeta{Name: "node-1"}}}, defaultMaxEndpointsPerSlice)
@ -776,7 +812,7 @@ func TestReconcileEndpointSlicesRecycling(t *testing.T) {
pods := []*corev1.Pod{}
for i := 0; i < 300; i++ {
ready := !(i%3 == 0)
pods = append(pods, newPod(i, namespace, ready, 1))
pods = append(pods, newPod(i, namespace, ready, 1, false))
}
// generate 10 existing slices with 30 pods/endpoints each
@ -827,7 +863,7 @@ func TestReconcileEndpointSlicesUpdatePacking(t *testing.T) {
slice1 := newEmptyEndpointSlice(1, namespace, endpointMeta, svc)
for i := 0; i < 80; i++ {
pod := newPod(i, namespace, true, 1)
pod := newPod(i, namespace, true, 1, false)
slice1.Endpoints = append(slice1.Endpoints, podToEndpoint(pod, &corev1.Node{}, &svc, discovery.AddressTypeIPv4))
pods = append(pods, pod)
}
@ -835,7 +871,7 @@ func TestReconcileEndpointSlicesUpdatePacking(t *testing.T) {
slice2 := newEmptyEndpointSlice(2, namespace, endpointMeta, svc)
for i := 100; i < 120; i++ {
pod := newPod(i, namespace, true, 1)
pod := newPod(i, namespace, true, 1, false)
slice2.Endpoints = append(slice2.Endpoints, podToEndpoint(pod, &corev1.Node{}, &svc, discovery.AddressTypeIPv4))
pods = append(pods, pod)
}
@ -856,7 +892,7 @@ func TestReconcileEndpointSlicesUpdatePacking(t *testing.T) {
// add a few additional endpoints - no more than could fit in either slice.
for i := 200; i < 215; i++ {
pods = append(pods, newPod(i, namespace, true, 1))
pods = append(pods, newPod(i, namespace, true, 1, false))
}
r := newReconciler(client, []*corev1.Node{{ObjectMeta: metav1.ObjectMeta{Name: "node-1"}}}, defaultMaxEndpointsPerSlice)
@ -888,7 +924,7 @@ func TestReconcileEndpointSlicesReplaceDeprecated(t *testing.T) {
slice1 := newEmptyEndpointSlice(1, namespace, endpointMeta, svc)
for i := 0; i < 80; i++ {
pod := newPod(i, namespace, true, 1)
pod := newPod(i, namespace, true, 1, false)
slice1.Endpoints = append(slice1.Endpoints, podToEndpoint(pod, &corev1.Node{}, &corev1.Service{Spec: corev1.ServiceSpec{}}, discovery.AddressTypeIPv4))
pods = append(pods, pod)
}
@ -896,7 +932,7 @@ func TestReconcileEndpointSlicesReplaceDeprecated(t *testing.T) {
slice2 := newEmptyEndpointSlice(2, namespace, endpointMeta, svc)
for i := 100; i < 150; i++ {
pod := newPod(i, namespace, true, 1)
pod := newPod(i, namespace, true, 1, false)
slice2.Endpoints = append(slice2.Endpoints, podToEndpoint(pod, &corev1.Node{}, &corev1.Service{Spec: corev1.ServiceSpec{}}, discovery.AddressTypeIPv4))
pods = append(pods, pod)
}
@ -955,7 +991,7 @@ func TestReconcileEndpointSlicesRecreation(t *testing.T) {
svc, endpointMeta := newServiceAndEndpointMeta("foo", namespace)
slice := newEmptyEndpointSlice(1, namespace, endpointMeta, svc)
pod := newPod(1, namespace, true, 1)
pod := newPod(1, namespace, true, 1, false)
slice.Endpoints = append(slice.Endpoints, podToEndpoint(pod, &corev1.Node{}, &corev1.Service{Spec: corev1.ServiceSpec{}}, discovery.AddressTypeIPv4))
if !tc.ownedByService {
@ -1023,7 +1059,7 @@ func TestReconcileEndpointSlicesNamedPorts(t *testing.T) {
for i := 0; i < 300; i++ {
ready := !(i%3 == 0)
portOffset := i % 5
pod := newPod(i, namespace, ready, 1)
pod := newPod(i, namespace, ready, 1, false)
pod.Spec.Containers[0].Ports = []corev1.ContainerPort{{
Name: portNameIntStr.StrVal,
ContainerPort: int32(8080 + portOffset),
@ -1073,7 +1109,7 @@ func TestReconcileMaxEndpointsPerSlice(t *testing.T) {
pods := []*corev1.Pod{}
for i := 0; i < 250; i++ {
ready := !(i%3 == 0)
pods = append(pods, newPod(i, namespace, ready, 1))
pods = append(pods, newPod(i, namespace, ready, 1, false))
}
testCases := []struct {
@ -1125,7 +1161,7 @@ func TestReconcileEndpointSlicesMetrics(t *testing.T) {
// start with 20 pods
pods := []*corev1.Pod{}
for i := 0; i < 20; i++ {
pods = append(pods, newPod(i, namespace, true, 1))
pods = append(pods, newPod(i, namespace, true, 1, false))
}
r := newReconciler(client, []*corev1.Node{{ObjectMeta: metav1.ObjectMeta{Name: "node-1"}}}, defaultMaxEndpointsPerSlice)

View File

@ -27,6 +27,7 @@ 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/klog/v2"
podutil "k8s.io/kubernetes/pkg/api/v1/pod"
@ -34,6 +35,7 @@ import (
helper "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"
)
@ -60,7 +62,11 @@ func podToEndpoint(pod *corev1.Pod, node *corev1.Node, service *corev1.Service,
}
}
ready := service.Spec.PublishNotReadyAddresses || podutil.IsPodReady(pod)
serving := podutil.IsPodReady(pod)
terminating := pod.DeletionTimestamp != nil
// For compatibility reasons, "ready" should never be "true" if a pod is terminatng, unless
// publishNotReadyAddresses was set.
ready := service.Spec.PublishNotReadyAddresses || (serving && !terminating)
ep := discovery.Endpoint{
Addresses: getEndpointAddresses(pod.Status, service, addressType),
Conditions: discovery.EndpointConditions{
@ -76,6 +82,11 @@ func podToEndpoint(pod *corev1.Pod, node *corev1.Node, service *corev1.Service,
},
}
if utilfeature.DefaultFeatureGate.Enabled(features.EndpointSliceTerminatingCondition) {
ep.Conditions.Serving = &serving
ep.Conditions.Terminating = &terminating
}
if endpointutil.ShouldSetHostname(pod, service) {
ep.Hostname = &pod.Spec.Hostname
}

View File

@ -20,6 +20,7 @@ import (
"fmt"
"reflect"
"testing"
"time"
"github.com/stretchr/testify/assert"
v1 "k8s.io/api/core/v1"
@ -30,8 +31,11 @@ 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"
)
@ -219,13 +223,15 @@ func TestPodToEndpoint(t *testing.T) {
svcPublishNotReady, _ := newServiceAndEndpointMeta("publishnotready", ns)
svcPublishNotReady.Spec.PublishNotReadyAddresses = true
readyPod := newPod(1, ns, true, 1)
readyPodHostname := newPod(1, ns, true, 1)
readyPod := newPod(1, ns, true, 1, false)
readyTerminatingPod := newPod(1, ns, true, 1, true)
readyPodHostname := newPod(1, ns, true, 1, false)
readyPodHostname.Spec.Subdomain = svc.Name
readyPodHostname.Spec.Hostname = "example-hostname"
unreadyPod := newPod(1, ns, false, 1)
multiIPPod := newPod(1, ns, true, 1)
unreadyPod := newPod(1, ns, false, 1, false)
unreadyTerminatingPod := newPod(1, ns, false, 1, true)
multiIPPod := newPod(1, ns, true, 1, false)
multiIPPod.Status.PodIPs = []v1.PodIP{{IP: "1.2.3.4"}, {IP: "1234::5678:0000:0000:9abc:def0"}}
node1 := &v1.Node{
@ -245,6 +251,7 @@ func TestPodToEndpoint(t *testing.T) {
svc *v1.Service
expectedEndpoint discovery.Endpoint
publishNotReadyAddresses bool
terminatingGateEnabled bool
}{
{
name: "Ready pod",
@ -381,13 +388,121 @@ func TestPodToEndpoint(t *testing.T) {
},
},
},
{
name: "Ready pod, terminating gate enabled",
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),
},
Topology: map[string]string{"kubernetes.io/hostname": "node-1"},
TargetRef: &v1.ObjectReference{
Kind: "Pod",
Namespace: ns,
Name: readyPod.Name,
UID: readyPod.UID,
ResourceVersion: readyPod.ResourceVersion,
},
},
terminatingGateEnabled: true,
},
{
name: "Ready terminating pod, terminating gate disabled",
pod: readyTerminatingPod,
svc: &svc,
expectedEndpoint: discovery.Endpoint{
Addresses: []string{"1.2.3.5"},
Conditions: discovery.EndpointConditions{
Ready: utilpointer.BoolPtr(false),
},
Topology: map[string]string{"kubernetes.io/hostname": "node-1"},
TargetRef: &v1.ObjectReference{
Kind: "Pod",
Namespace: ns,
Name: readyPod.Name,
UID: readyPod.UID,
ResourceVersion: readyPod.ResourceVersion,
},
},
terminatingGateEnabled: false,
},
{
name: "Ready terminating pod, terminating gate enabled",
pod: readyTerminatingPod,
svc: &svc,
expectedEndpoint: discovery.Endpoint{
Addresses: []string{"1.2.3.5"},
Conditions: discovery.EndpointConditions{
Ready: utilpointer.BoolPtr(false),
Serving: utilpointer.BoolPtr(true),
Terminating: utilpointer.BoolPtr(true),
},
Topology: map[string]string{"kubernetes.io/hostname": "node-1"},
TargetRef: &v1.ObjectReference{
Kind: "Pod",
Namespace: ns,
Name: readyPod.Name,
UID: readyPod.UID,
ResourceVersion: readyPod.ResourceVersion,
},
},
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),
},
Topology: map[string]string{"kubernetes.io/hostname": "node-1"},
TargetRef: &v1.ObjectReference{
Kind: "Pod",
Namespace: ns,
Name: readyPod.Name,
UID: readyPod.UID,
ResourceVersion: readyPod.ResourceVersion,
},
},
terminatingGateEnabled: false,
},
{
name: "Not ready terminating pod, terminating gate enabled",
pod: unreadyTerminatingPod,
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(true),
},
Topology: map[string]string{"kubernetes.io/hostname": "node-1"},
TargetRef: &v1.ObjectReference{
Kind: "Pod",
Namespace: ns,
Name: readyPod.Name,
UID: readyPod.UID,
ResourceVersion: readyPod.ResourceVersion,
},
},
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)
t.Errorf("Expected endpoint: %+v, got: %+v", testCase.expectedEndpoint, endpoint)
}
})
}
@ -811,18 +926,26 @@ func TestSetEndpointSliceLabels(t *testing.T) {
// Test helpers
func newPod(n int, namespace string, ready bool, nPorts int) *v1.Pod {
func newPod(n int, namespace string, ready bool, nPorts int, terminating bool) *v1.Pod {
status := v1.ConditionTrue
if !ready {
status = v1.ConditionFalse
}
var deletionTimestamp *metav1.Time
if terminating {
deletionTimestamp = &metav1.Time{
Time: time.Now(),
}
}
p := &v1.Pod{
TypeMeta: metav1.TypeMeta{APIVersion: "v1"},
ObjectMeta: metav1.ObjectMeta{
Namespace: namespace,
Name: fmt.Sprintf("pod%d", n),
Labels: map[string]string{"foo": "bar"},
Namespace: namespace,
Name: fmt.Sprintf("pod%d", n),
Labels: map[string]string{"foo": "bar"},
DeletionTimestamp: deletionTimestamp,
},
Spec: v1.PodSpec{
Containers: []v1.Container{{

View File

@ -122,14 +122,14 @@ func DeepHashObjectToString(objectToWrite interface{}) string {
return hex.EncodeToString(hasher.Sum(nil)[0:])
}
// ShouldPodBeInEndpoints returns true if a specified pod should be in an
// endpoints object. Terminating pods are only included if publishNotReady is true.
func ShouldPodBeInEndpoints(pod *v1.Pod, publishNotReady bool) bool {
// ShouldPodBeInEndpointSlice returns true if a specified pod should be in an EndpointSlice object.
// Terminating pods are only included if includeTerminating is true
func ShouldPodBeInEndpointSlice(pod *v1.Pod, includeTerminating bool) bool {
if len(pod.Status.PodIP) == 0 && len(pod.Status.PodIPs) == 0 {
return false
}
if !publishNotReady && pod.DeletionTimestamp != nil {
if !includeTerminating && pod.DeletionTimestamp != nil {
return false
}

View File

@ -100,12 +100,12 @@ func TestDetermineNeededServiceUpdates(t *testing.T) {
// There are 3*5 possibilities(3 types of RestartPolicy by 5 types of PodPhase).
// Not listing them all here. Just listing all of the 3 false cases and 3 of the
// 12 true cases.
func TestShouldPodBeInEndpoints(t *testing.T) {
func TestShouldPodBeInEndpointSlice(t *testing.T) {
testCases := []struct {
name string
pod *v1.Pod
publishNotReady bool
expected bool
name string
pod *v1.Pod
expected bool
includeTerminating bool
}{
// Pod should not be in endpoints:
{
@ -162,7 +162,7 @@ func TestShouldPodBeInEndpoints(t *testing.T) {
expected: false,
},
{
name: "Terminating Pod",
name: "Terminating Pod with includeTerminating=false",
pod: &v1.Pod{
ObjectMeta: metav1.ObjectMeta{
DeletionTimestamp: &metav1.Time{
@ -175,8 +175,7 @@ func TestShouldPodBeInEndpoints(t *testing.T) {
PodIP: "1.2.3.4",
},
},
publishNotReady: false,
expected: false,
expected: false,
},
// Pod should be in endpoints:
{
@ -245,7 +244,7 @@ func TestShouldPodBeInEndpoints(t *testing.T) {
expected: true,
},
{
name: "Terminating Pod with publish not ready",
name: "Terminating Pod with includeTerminating=true",
pod: &v1.Pod{
ObjectMeta: metav1.ObjectMeta{
DeletionTimestamp: &metav1.Time{
@ -258,14 +257,14 @@ func TestShouldPodBeInEndpoints(t *testing.T) {
PodIP: "1.2.3.4",
},
},
publishNotReady: true,
expected: true,
expected: true,
includeTerminating: true,
},
}
for _, test := range testCases {
t.Run(test.name, func(t *testing.T) {
result := ShouldPodBeInEndpoints(test.pod, test.publishNotReady)
result := ShouldPodBeInEndpointSlice(test.pod, test.includeTerminating)
if result != test.expected {
t.Errorf("expected: %t, got: %t", test.expected, result)
}

View File

@ -651,6 +651,12 @@ const (
// Allows kube-controller-manager to publish kube-root-ca.crt configmap to
// every namespace. This feature is a prerequisite of BoundServiceAccountTokenVolume.
RootCAConfigMap featuregate.Feature = "RootCAConfigMap"
// owner: @andrewsykim
// alpha: v1.20
//
// Enable Terminating condition in Endpoint Slices.
EndpointSliceTerminatingCondition featuregate.Feature = "EndpointSliceTerminatingCondition"
)
func init() {
@ -731,6 +737,7 @@ var defaultKubernetesFeatureGates = map[featuregate.Feature]featuregate.FeatureS
IPv6DualStack: {Default: false, PreRelease: featuregate.Alpha},
EndpointSlice: {Default: true, PreRelease: featuregate.Beta},
EndpointSliceProxying: {Default: true, PreRelease: featuregate.Beta},
EndpointSliceTerminatingCondition: {Default: false, PreRelease: featuregate.Alpha},
WindowsEndpointSliceProxying: {Default: false, PreRelease: featuregate.Alpha},
EvenPodsSpread: {Default: true, PreRelease: featuregate.GA, LockToDefault: true}, // remove in 1.21
StartupProbe: {Default: true, PreRelease: featuregate.GA, LockToDefault: true}, // remove in 1.21

View File

@ -1,4 +1,4 @@
load("@io_bazel_rules_go//go:def.bzl", "go_library")
load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
go_library(
name = "go_default_library",
@ -12,11 +12,13 @@ go_library(
"//pkg/api/legacyscheme:go_default_library",
"//pkg/apis/discovery:go_default_library",
"//pkg/apis/discovery/validation:go_default_library",
"//pkg/features:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/api/equality:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/util/validation/field:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/storage/names:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/util/feature:go_default_library",
],
)
@ -36,3 +38,17 @@ filegroup(
tags = ["automanaged"],
visibility = ["//visibility:public"],
)
go_test(
name = "go_default_test",
srcs = ["strategy_test.go"],
embed = [":go_default_library"],
deps = [
"//pkg/apis/discovery:go_default_library",
"//pkg/features:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/api/equality:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/util/feature:go_default_library",
"//staging/src/k8s.io/component-base/featuregate/testing:go_default_library",
"//vendor/k8s.io/utils/pointer:go_default_library",
],
)

View File

@ -24,9 +24,11 @@ import (
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/util/validation/field"
"k8s.io/apiserver/pkg/storage/names"
utilfeature "k8s.io/apiserver/pkg/util/feature"
"k8s.io/kubernetes/pkg/api/legacyscheme"
"k8s.io/kubernetes/pkg/apis/discovery"
"k8s.io/kubernetes/pkg/apis/discovery/validation"
"k8s.io/kubernetes/pkg/features"
)
// endpointSliceStrategy implements verification logic for Replication.
@ -47,6 +49,8 @@ func (endpointSliceStrategy) NamespaceScoped() bool {
func (endpointSliceStrategy) PrepareForCreate(ctx context.Context, obj runtime.Object) {
endpointSlice := obj.(*discovery.EndpointSlice)
endpointSlice.Generation = 1
dropDisabledConditionsOnCreate(endpointSlice)
}
// PrepareForUpdate clears fields that are not allowed to be set by end users on update.
@ -67,6 +71,8 @@ func (endpointSliceStrategy) PrepareForUpdate(ctx context.Context, obj, old runt
newEPS.ObjectMeta = ogNewMeta
oldEPS.ObjectMeta = ogOldMeta
dropDisabledConditionsOnUpdate(oldEPS, newEPS)
}
// Validate validates a new EndpointSlice.
@ -96,3 +102,42 @@ func (endpointSliceStrategy) ValidateUpdate(ctx context.Context, new, old runtim
func (endpointSliceStrategy) AllowUnconditionalUpdate() bool {
return true
}
// dropDisabledConditionsOnCreate will drop the terminating condition if the
// EndpointSliceTerminatingCondition is disabled. Otherwise the field is left untouched.
func dropDisabledConditionsOnCreate(endpointSlice *discovery.EndpointSlice) {
if utilfeature.DefaultFeatureGate.Enabled(features.EndpointSliceTerminatingCondition) {
return
}
// Always drop the serving/terminating conditions on create when feature gate is disabled.
for i := range endpointSlice.Endpoints {
endpointSlice.Endpoints[i].Conditions.Serving = nil
endpointSlice.Endpoints[i].Conditions.Terminating = nil
}
}
// dropDisabledConditionsOnUpdate will drop the terminating condition field if the EndpointSliceTerminatingCondition
// feature gate is disabled unless an existing EndpointSlice object has the field already set. This ensures
// the field is not dropped on rollback.
func dropDisabledConditionsOnUpdate(oldEPS, newEPS *discovery.EndpointSlice) {
if utilfeature.DefaultFeatureGate.Enabled(features.EndpointSliceTerminatingCondition) {
return
}
// Only drop the serving/terminating condition if the existing EndpointSlice doesn't have it set.
dropConditions := true
for _, ep := range oldEPS.Endpoints {
if ep.Conditions.Serving != nil || ep.Conditions.Terminating != nil {
dropConditions = false
break
}
}
if dropConditions {
for i := range newEPS.Endpoints {
newEPS.Endpoints[i].Conditions.Serving = nil
newEPS.Endpoints[i].Conditions.Terminating = nil
}
}
}

View File

@ -0,0 +1,448 @@
/*
Copyright 2020 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package endpointslice
import (
"testing"
apiequality "k8s.io/apimachinery/pkg/api/equality"
utilfeature "k8s.io/apiserver/pkg/util/feature"
featuregatetesting "k8s.io/component-base/featuregate/testing"
"k8s.io/kubernetes/pkg/apis/discovery"
"k8s.io/kubernetes/pkg/features"
utilpointer "k8s.io/utils/pointer"
)
func Test_dropConditionsOnCreate(t *testing.T) {
testcases := []struct {
name string
terminatingGateEnabled bool
eps *discovery.EndpointSlice
expectedEPS *discovery.EndpointSlice
}{
{
name: "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: "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,
},
},
},
},
},
}
for _, testcase := range testcases {
t.Run(testcase.name, func(t *testing.T) {
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.EndpointSliceTerminatingCondition, testcase.terminatingGateEnabled)()
dropDisabledConditionsOnCreate(testcase.eps)
if !apiequality.Semantic.DeepEqual(testcase.eps, testcase.expectedEPS) {
t.Logf("actual endpointslice: %v", testcase.eps)
t.Logf("expected endpointslice: %v", testcase.expectedEPS)
t.Errorf("unexpected EndpointSlice on create API strategy")
}
})
}
}
func Test_dropTerminatingConditionOnUpdate(t *testing.T) {
testcases := []struct {
name string
terminatingGateEnabled bool
oldEPS *discovery.EndpointSlice
newEPS *discovery.EndpointSlice
expectedEPS *discovery.EndpointSlice
}{
{
name: "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: "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: "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: "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),
},
},
},
},
},
}
for _, testcase := range testcases {
t.Run(testcase.name, func(t *testing.T) {
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.EndpointSliceTerminatingCondition, testcase.terminatingGateEnabled)()
dropDisabledConditionsOnUpdate(testcase.oldEPS, testcase.newEPS)
if !apiequality.Semantic.DeepEqual(testcase.newEPS, testcase.expectedEPS) {
t.Logf("actual endpointslice: %v", testcase.newEPS)
t.Logf("expected endpointslice: %v", testcase.expectedEPS)
t.Errorf("unexpected EndpointSlice from update API strategy")
}
})
}
}

View File

@ -200,54 +200,57 @@ func init() {
}
var fileDescriptor_772f83c5b34e07a5 = []byte{
// 746 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x53, 0x4b, 0x6f, 0xd3, 0x4a,
0x14, 0x8e, 0x9b, 0x5a, 0xb2, 0x27, 0x8d, 0x6e, 0x3b, 0xba, 0x8b, 0x28, 0xf7, 0x5e, 0x3b, 0xca,
0x5d, 0x10, 0xa9, 0x30, 0x26, 0x15, 0x45, 0x15, 0xac, 0x6a, 0x28, 0x0f, 0x89, 0x47, 0x18, 0xba,
0x40, 0x88, 0x05, 0x13, 0x7b, 0xea, 0x98, 0x24, 0x1e, 0xcb, 0x9e, 0x44, 0xca, 0x8e, 0x9f, 0xc0,
0x0f, 0x62, 0x89, 0x50, 0x97, 0x5d, 0x76, 0x65, 0x51, 0xf7, 0x5f, 0x74, 0x85, 0x66, 0xfc, 0x4a,
0x09, 0x8f, 0xec, 0x66, 0xbe, 0x39, 0xdf, 0x77, 0xce, 0xf9, 0xe6, 0x1c, 0xf0, 0x68, 0x7c, 0x10,
0x23, 0x9f, 0x59, 0xe3, 0xd9, 0x90, 0x46, 0x01, 0xe5, 0x34, 0xb6, 0xe6, 0x34, 0x70, 0x59, 0x64,
0xe5, 0x0f, 0x24, 0xf4, 0x2d, 0xd7, 0x8f, 0x1d, 0x36, 0xa7, 0xd1, 0xc2, 0x9a, 0xf7, 0xc9, 0x24,
0x1c, 0x91, 0xbe, 0xe5, 0xd1, 0x80, 0x46, 0x84, 0x53, 0x17, 0x85, 0x11, 0xe3, 0x0c, 0xfe, 0x97,
0x85, 0x23, 0x12, 0xfa, 0xa8, 0x0c, 0x47, 0x45, 0x78, 0xfb, 0x96, 0xe7, 0xf3, 0xd1, 0x6c, 0x88,
0x1c, 0x36, 0xb5, 0x3c, 0xe6, 0x31, 0x4b, 0xb2, 0x86, 0xb3, 0x13, 0x79, 0x93, 0x17, 0x79, 0xca,
0xd4, 0xda, 0xdd, 0xa5, 0xe4, 0x0e, 0x8b, 0xa8, 0x35, 0x5f, 0xc9, 0xd8, 0xbe, 0x53, 0xc5, 0x4c,
0x89, 0x33, 0xf2, 0x03, 0x51, 0x5f, 0x38, 0xf6, 0x04, 0x10, 0x5b, 0x53, 0xca, 0xc9, 0xcf, 0x58,
0xd6, 0xaf, 0x58, 0xd1, 0x2c, 0xe0, 0xfe, 0x94, 0xae, 0x10, 0xee, 0xfe, 0x89, 0x10, 0x3b, 0x23,
0x3a, 0x25, 0x3f, 0xf2, 0xba, 0x9f, 0xeb, 0x40, 0x3b, 0x0a, 0xdc, 0x90, 0xf9, 0x01, 0x87, 0xbb,
0x40, 0x27, 0xae, 0x1b, 0xd1, 0x38, 0xa6, 0x71, 0x4b, 0xe9, 0xd4, 0x7b, 0xba, 0xdd, 0x4c, 0x13,
0x53, 0x3f, 0x2c, 0x40, 0x5c, 0xbd, 0x43, 0x0a, 0x80, 0xc3, 0x02, 0xd7, 0xe7, 0x3e, 0x0b, 0xe2,
0xd6, 0x46, 0x47, 0xe9, 0x35, 0xf6, 0xfa, 0xe8, 0xb7, 0xfe, 0xa2, 0x22, 0xd3, 0x83, 0x92, 0x68,
0xc3, 0xd3, 0xc4, 0xac, 0xa5, 0x89, 0x09, 0x2a, 0x0c, 0x2f, 0x09, 0xc3, 0x1e, 0xd0, 0x46, 0x2c,
0xe6, 0x01, 0x99, 0xd2, 0x56, 0xbd, 0xa3, 0xf4, 0x74, 0x7b, 0x2b, 0x4d, 0x4c, 0xed, 0x49, 0x8e,
0xe1, 0xf2, 0x15, 0x0e, 0x80, 0xce, 0x49, 0xe4, 0x51, 0x8e, 0xe9, 0x49, 0x6b, 0x53, 0xd6, 0xf3,
0xff, 0x72, 0x3d, 0xe2, 0x87, 0xd0, 0xbc, 0x8f, 0x5e, 0x0e, 0x3f, 0x50, 0x47, 0x04, 0xd1, 0x88,
0x06, 0x0e, 0xcd, 0x5a, 0x3c, 0x2e, 0x98, 0xb8, 0x12, 0x81, 0x0e, 0xd0, 0x38, 0x0b, 0xd9, 0x84,
0x79, 0x8b, 0x96, 0xda, 0xa9, 0xf7, 0x1a, 0x7b, 0xfb, 0x6b, 0x36, 0x88, 0x8e, 0x73, 0xde, 0x51,
0xc0, 0xa3, 0x85, 0xbd, 0x9d, 0x37, 0xa9, 0x15, 0x30, 0x2e, 0x85, 0xdb, 0xf7, 0x41, 0xf3, 0x5a,
0x30, 0xdc, 0x06, 0xf5, 0x31, 0x5d, 0xb4, 0x14, 0xd1, 0x2c, 0x16, 0x47, 0xf8, 0x37, 0x50, 0xe7,
0x64, 0x32, 0xa3, 0xd2, 0x65, 0x1d, 0x67, 0x97, 0x7b, 0x1b, 0x07, 0x4a, 0x77, 0x1f, 0xc0, 0x55,
0x4f, 0xa1, 0x09, 0xd4, 0x88, 0x12, 0x37, 0xd3, 0xd0, 0x6c, 0x3d, 0x4d, 0x4c, 0x15, 0x0b, 0x00,
0x67, 0x78, 0xf7, 0xab, 0x02, 0xb6, 0x0a, 0xde, 0x80, 0x45, 0x1c, 0xfe, 0x0b, 0x36, 0xa5, 0xc3,
0x32, 0xa9, 0xad, 0xa5, 0x89, 0xb9, 0xf9, 0x42, 0xb8, 0x2b, 0x51, 0xf8, 0x18, 0x68, 0x72, 0x5a,
0x1c, 0x36, 0xc9, 0x4a, 0xb0, 0x77, 0x45, 0x33, 0x83, 0x1c, 0xbb, 0x4a, 0xcc, 0x7f, 0x56, 0x37,
0x01, 0x15, 0xcf, 0xb8, 0x24, 0x8b, 0x34, 0x21, 0x8b, 0xb8, 0xfc, 0x48, 0x35, 0x4b, 0x23, 0xd2,
0x63, 0x89, 0xc2, 0x3e, 0x68, 0x90, 0x30, 0x2c, 0x68, 0xf2, 0x0b, 0x75, 0xfb, 0xaf, 0x34, 0x31,
0x1b, 0x87, 0x15, 0x8c, 0x97, 0x63, 0xba, 0x97, 0x1b, 0xa0, 0x59, 0x34, 0xf2, 0x7a, 0xe2, 0x3b,
0x14, 0xbe, 0x07, 0x9a, 0x58, 0x2a, 0x97, 0x70, 0x22, 0xbb, 0x69, 0xec, 0xdd, 0x5e, 0xfa, 0xb3,
0x72, 0x37, 0x50, 0x38, 0xf6, 0x04, 0x10, 0x23, 0x11, 0x5d, 0x8d, 0xc5, 0x73, 0xca, 0x49, 0x35,
0x93, 0x15, 0x86, 0x4b, 0x55, 0xf8, 0x10, 0x34, 0xf2, 0x2d, 0x38, 0x5e, 0x84, 0x34, 0x2f, 0xb3,
0x9b, 0x53, 0x1a, 0x87, 0xd5, 0xd3, 0xd5, 0xf5, 0x2b, 0x5e, 0xa6, 0xc1, 0x37, 0x40, 0xa7, 0x79,
0xe1, 0x62, 0x7b, 0xc4, 0x70, 0xdd, 0x58, 0x73, 0xb8, 0xec, 0x9d, 0x3c, 0x99, 0x5e, 0x20, 0x31,
0xae, 0xc4, 0xe0, 0x00, 0xa8, 0xc2, 0xce, 0xb8, 0x55, 0x97, 0xaa, 0xbb, 0x6b, 0xaa, 0x8a, 0x8f,
0xb0, 0x9b, 0xb9, 0xb2, 0x2a, 0x6e, 0x31, 0xce, 0x84, 0xba, 0x5f, 0x14, 0xb0, 0x73, 0xcd, 0xe5,
0x67, 0x7e, 0xcc, 0xe1, 0xbb, 0x15, 0xa7, 0xd1, 0x7a, 0x4e, 0x0b, 0xb6, 0xf4, 0xb9, 0x5c, 0x8b,
0x02, 0x59, 0x72, 0xf9, 0x15, 0x50, 0x7d, 0x4e, 0xa7, 0x85, 0x37, 0x37, 0xd7, 0xec, 0x42, 0x96,
0x57, 0xb5, 0xf1, 0x54, 0x48, 0xe0, 0x4c, 0xc9, 0x46, 0xa7, 0x17, 0x46, 0xed, 0xec, 0xc2, 0xa8,
0x9d, 0x5f, 0x18, 0xb5, 0x8f, 0xa9, 0xa1, 0x9c, 0xa6, 0x86, 0x72, 0x96, 0x1a, 0xca, 0x79, 0x6a,
0x28, 0xdf, 0x52, 0x43, 0xf9, 0x74, 0x69, 0xd4, 0xde, 0x6a, 0x85, 0xe6, 0xf7, 0x00, 0x00, 0x00,
0xff, 0xff, 0x65, 0x85, 0x5a, 0x9b, 0x75, 0x06, 0x00, 0x00,
// 787 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x53, 0x4d, 0x8f, 0xe3, 0x44,
0x10, 0x8d, 0x27, 0x13, 0xad, 0xdd, 0xd9, 0x11, 0xbb, 0x2d, 0x0e, 0xd1, 0x00, 0xf6, 0x28, 0x08,
0x11, 0x69, 0xa0, 0x4d, 0x46, 0x80, 0x56, 0x70, 0x1a, 0xc3, 0xf2, 0x21, 0xf1, 0x31, 0xf4, 0xce,
0x01, 0x21, 0x0e, 0xf4, 0xd8, 0xb5, 0x8e, 0x49, 0xec, 0xb6, 0xba, 0x3b, 0x91, 0x72, 0xe3, 0x1f,
0xc0, 0x0f, 0xe2, 0x88, 0xd0, 0x1c, 0xf7, 0xb8, 0x27, 0x8b, 0xf1, 0xfe, 0x8b, 0x39, 0xa1, 0x6e,
0x7f, 0x0e, 0x01, 0x36, 0x37, 0xf7, 0xab, 0x7a, 0xaf, 0xea, 0x95, 0xab, 0xd0, 0x67, 0xcb, 0x47,
0x92, 0x24, 0xdc, 0x5f, 0xae, 0xaf, 0x40, 0x64, 0xa0, 0x40, 0xfa, 0x1b, 0xc8, 0x22, 0x2e, 0xfc,
0x3a, 0xc0, 0xf2, 0xc4, 0x8f, 0x12, 0x19, 0xf2, 0x0d, 0x88, 0xad, 0xbf, 0x99, 0xb3, 0x55, 0xbe,
0x60, 0x73, 0x3f, 0x86, 0x0c, 0x04, 0x53, 0x10, 0x91, 0x5c, 0x70, 0xc5, 0xf1, 0x1b, 0x55, 0x3a,
0x61, 0x79, 0x42, 0xda, 0x74, 0xd2, 0xa4, 0x1f, 0xbf, 0x1b, 0x27, 0x6a, 0xb1, 0xbe, 0x22, 0x21,
0x4f, 0xfd, 0x98, 0xc7, 0xdc, 0x37, 0xac, 0xab, 0xf5, 0x53, 0xf3, 0x32, 0x0f, 0xf3, 0x55, 0xa9,
0x1d, 0x4f, 0x7b, 0xc5, 0x43, 0x2e, 0xc0, 0xdf, 0xec, 0x54, 0x3c, 0x7e, 0xbf, 0xcb, 0x49, 0x59,
0xb8, 0x48, 0x32, 0xdd, 0x5f, 0xbe, 0x8c, 0x35, 0x20, 0xfd, 0x14, 0x14, 0xfb, 0x37, 0x96, 0xff,
0x5f, 0x2c, 0xb1, 0xce, 0x54, 0x92, 0xc2, 0x0e, 0xe1, 0xc3, 0x97, 0x11, 0x64, 0xb8, 0x80, 0x94,
0xfd, 0x93, 0x37, 0xfd, 0x7d, 0x88, 0xec, 0xc7, 0x59, 0x94, 0xf3, 0x24, 0x53, 0xf8, 0x14, 0x39,
0x2c, 0x8a, 0x04, 0x48, 0x09, 0x72, 0x62, 0x9d, 0x0c, 0x67, 0x4e, 0x70, 0x54, 0x16, 0x9e, 0x73,
0xde, 0x80, 0xb4, 0x8b, 0x63, 0x40, 0x28, 0xe4, 0x59, 0x94, 0xa8, 0x84, 0x67, 0x72, 0x72, 0x70,
0x62, 0xcd, 0xc6, 0x67, 0x73, 0xf2, 0xbf, 0xf3, 0x25, 0x4d, 0xa5, 0x4f, 0x5a, 0x62, 0x80, 0xaf,
0x0b, 0x6f, 0x50, 0x16, 0x1e, 0xea, 0x30, 0xda, 0x13, 0xc6, 0x33, 0x64, 0x2f, 0xb8, 0x54, 0x19,
0x4b, 0x61, 0x32, 0x3c, 0xb1, 0x66, 0x4e, 0x70, 0xbf, 0x2c, 0x3c, 0xfb, 0x8b, 0x1a, 0xa3, 0x6d,
0x14, 0x5f, 0x20, 0x47, 0x31, 0x11, 0x83, 0xa2, 0xf0, 0x74, 0x72, 0x68, 0xfa, 0x79, 0xb3, 0xdf,
0x8f, 0xfe, 0x43, 0x64, 0x33, 0x27, 0xdf, 0x5e, 0xfd, 0x0c, 0xa1, 0x4e, 0x02, 0x01, 0x59, 0x08,
0x95, 0xc5, 0xcb, 0x86, 0x49, 0x3b, 0x11, 0x1c, 0x22, 0x5b, 0xf1, 0x9c, 0xaf, 0x78, 0xbc, 0x9d,
0x8c, 0x4e, 0x86, 0xb3, 0xf1, 0xd9, 0x07, 0x7b, 0x1a, 0x24, 0x97, 0x35, 0xef, 0x71, 0xa6, 0xc4,
0x36, 0x78, 0x50, 0x9b, 0xb4, 0x1b, 0x98, 0xb6, 0xc2, 0xc7, 0x1f, 0xa3, 0xa3, 0x3b, 0xc9, 0xf8,
0x01, 0x1a, 0x2e, 0x61, 0x3b, 0xb1, 0xb4, 0x59, 0xaa, 0x3f, 0xf1, 0xab, 0x68, 0xb4, 0x61, 0xab,
0x35, 0x98, 0x29, 0x3b, 0xb4, 0x7a, 0x7c, 0x74, 0xf0, 0xc8, 0x9a, 0xfe, 0x6a, 0x21, 0xbc, 0x3b,
0x54, 0xec, 0xa1, 0x91, 0x00, 0x16, 0x55, 0x22, 0x76, 0xe0, 0x94, 0x85, 0x37, 0xa2, 0x1a, 0xa0,
0x15, 0x8e, 0xdf, 0x42, 0xf7, 0x24, 0x88, 0x4d, 0x92, 0xc5, 0x46, 0xd3, 0x0e, 0xc6, 0x65, 0xe1,
0xdd, 0x7b, 0x52, 0x41, 0xb4, 0x89, 0xe1, 0x39, 0x1a, 0x2b, 0x10, 0x69, 0x92, 0x31, 0xa5, 0x53,
0x87, 0x26, 0xf5, 0x95, 0xb2, 0xf0, 0xc6, 0x97, 0x1d, 0x4c, 0xfb, 0x39, 0xd3, 0x3f, 0x2d, 0x74,
0xbf, 0xe9, 0xe8, 0x82, 0x0b, 0x85, 0x5f, 0x47, 0x87, 0xe6, 0xe7, 0x19, 0x3f, 0x81, 0x5d, 0x16,
0xde, 0xe1, 0x37, 0xfa, 0xc7, 0x19, 0x14, 0x7f, 0x8e, 0x6c, 0xb3, 0x88, 0x21, 0x5f, 0x55, 0xee,
0x82, 0x53, 0x3d, 0xa7, 0x8b, 0x1a, 0xbb, 0x2d, 0xbc, 0xd7, 0x76, 0x8f, 0x8c, 0x34, 0x61, 0xda,
0x92, 0x75, 0x99, 0x9c, 0x0b, 0x65, 0x7a, 0x1c, 0x55, 0x65, 0x74, 0x79, 0x6a, 0x50, 0x6d, 0x84,
0xe5, 0x79, 0x43, 0x33, 0xdb, 0xe1, 0x54, 0x46, 0xce, 0x3b, 0x98, 0xf6, 0x73, 0xa6, 0x2f, 0x0e,
0xd0, 0x51, 0x63, 0xe4, 0xc9, 0x2a, 0x09, 0x01, 0xff, 0x84, 0x6c, 0x7d, 0xaf, 0x11, 0x53, 0xcc,
0xb8, 0x19, 0x9f, 0xbd, 0xd7, 0x5b, 0x87, 0xf6, 0xec, 0x48, 0xbe, 0x8c, 0x35, 0x20, 0x89, 0xce,
0xee, 0x36, 0xee, 0x6b, 0x50, 0xac, 0x5b, 0xf7, 0x0e, 0xa3, 0xad, 0x2a, 0xfe, 0x14, 0x8d, 0xeb,
0x03, 0xbb, 0xdc, 0xe6, 0x50, 0xb7, 0x39, 0xad, 0x29, 0xe3, 0xf3, 0x2e, 0x74, 0x7b, 0xf7, 0x49,
0xfb, 0x34, 0xfc, 0x3d, 0x72, 0xa0, 0x6e, 0x5c, 0x1f, 0xa6, 0xde, 0xdb, 0xb7, 0xf7, 0xdc, 0xdb,
0xe0, 0x61, 0x5d, 0xcc, 0x69, 0x10, 0x49, 0x3b, 0x31, 0x7c, 0x81, 0x46, 0x7a, 0x9c, 0x72, 0x32,
0x34, 0xaa, 0xa7, 0x7b, 0xaa, 0xea, 0x1f, 0x11, 0x1c, 0xd5, 0xca, 0x23, 0xfd, 0x92, 0xb4, 0x12,
0x9a, 0xfe, 0x61, 0xa1, 0x87, 0x77, 0xa6, 0xfc, 0x55, 0x22, 0x15, 0xfe, 0x71, 0x67, 0xd2, 0x64,
0xbf, 0x49, 0x6b, 0xb6, 0x99, 0x73, 0x7b, 0x71, 0x0d, 0xd2, 0x9b, 0xf2, 0x77, 0x68, 0x94, 0x28,
0x48, 0x9b, 0xd9, 0xbc, 0xb3, 0xa7, 0x0b, 0xd3, 0x5e, 0x67, 0xe3, 0x4b, 0x2d, 0x41, 0x2b, 0xa5,
0x80, 0x5c, 0xdf, 0xb8, 0x83, 0x67, 0x37, 0xee, 0xe0, 0xf9, 0x8d, 0x3b, 0xf8, 0xa5, 0x74, 0xad,
0xeb, 0xd2, 0xb5, 0x9e, 0x95, 0xae, 0xf5, 0xbc, 0x74, 0xad, 0xbf, 0x4a, 0xd7, 0xfa, 0xed, 0x85,
0x3b, 0xf8, 0xc1, 0x6e, 0x34, 0xff, 0x0e, 0x00, 0x00, 0xff, 0xff, 0xa7, 0x11, 0xa7, 0x0f, 0xd0,
0x06, 0x00, 0x00,
}
func (m *Endpoint) Marshal() (dAtA []byte, err error) {
@ -355,6 +358,26 @@ func (m *EndpointConditions) MarshalToSizedBuffer(dAtA []byte) (int, error) {
_ = i
var l int
_ = l
if m.Terminating != nil {
i--
if *m.Terminating {
dAtA[i] = 1
} else {
dAtA[i] = 0
}
i--
dAtA[i] = 0x18
}
if m.Serving != nil {
i--
if *m.Serving {
dAtA[i] = 1
} else {
dAtA[i] = 0
}
i--
dAtA[i] = 0x10
}
if m.Ready != nil {
i--
if *m.Ready {
@ -583,6 +606,12 @@ func (m *EndpointConditions) Size() (n int) {
if m.Ready != nil {
n += 2
}
if m.Serving != nil {
n += 2
}
if m.Terminating != nil {
n += 2
}
return n
}
@ -688,6 +717,8 @@ func (this *EndpointConditions) String() string {
}
s := strings.Join([]string{`&EndpointConditions{`,
`Ready:` + valueToStringGenerated(this.Ready) + `,`,
`Serving:` + valueToStringGenerated(this.Serving) + `,`,
`Terminating:` + valueToStringGenerated(this.Terminating) + `,`,
`}`,
}, "")
return s
@ -1116,6 +1147,48 @@ func (m *EndpointConditions) Unmarshal(dAtA []byte) error {
}
b := bool(v != 0)
m.Ready = &b
case 2:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field Serving", wireType)
}
var v int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowGenerated
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
v |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
b := bool(v != 0)
m.Serving = &b
case 3:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field Terminating", wireType)
}
var v int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowGenerated
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
v |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
b := bool(v != 0)
m.Terminating = &b
default:
iNdEx = preIndex
skippy, err := skipGenerated(dAtA[iNdEx:])

View File

@ -76,9 +76,22 @@ message EndpointConditions {
// 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.
// unknown state as ready. For compatibility reasons, ready should never be
// "true" for terminating endpoints.
// +optional
optional bool ready = 1;
// 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.
// +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.
// +optional
optional bool terminating = 3;
}
// EndpointPort represents a Port used by an EndpointSlice

View File

@ -115,9 +115,22 @@ type EndpointConditions struct {
// 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.
// unknown state as ready. For compatibility reasons, ready should never be
// "true" for terminating endpoints.
// +optional
Ready *bool `json:"ready,omitempty" protobuf:"bytes,1,name=ready"`
// 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.
// +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.
// +optional
Terminating *bool `json:"terminating,omitempty" protobuf:"bytes,3,name=terminating"`
}
// EndpointPort represents a Port used by an EndpointSlice

View File

@ -41,8 +41,10 @@ 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.",
"": "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.",
"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

@ -72,6 +72,16 @@ func (in *EndpointConditions) DeepCopyInto(out *EndpointConditions) {
*out = new(bool)
**out = **in
}
if in.Serving != nil {
in, out := &in.Serving, &out.Serving
*out = new(bool)
**out = **in
}
if in.Terminating != nil {
in, out := &in.Terminating, &out.Terminating
*out = new(bool)
**out = **in
}
return
}

View File

@ -200,54 +200,56 @@ func init() {
}
var fileDescriptor_ece80bbc872d519b = []byte{
// 745 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x53, 0xcf, 0x6b, 0xdb, 0x48,
0x14, 0xb6, 0xe2, 0x88, 0x95, 0xc6, 0x31, 0x9b, 0x0c, 0x7b, 0x30, 0xde, 0x20, 0x19, 0x2f, 0x2c,
0x66, 0x43, 0xa4, 0x75, 0xc8, 0x2e, 0x61, 0xf7, 0x14, 0xed, 0x86, 0xb6, 0xd0, 0x36, 0x66, 0x1a,
0x28, 0x94, 0x1e, 0x3a, 0x96, 0x26, 0xb2, 0x6a, 0x5b, 0x23, 0x34, 0x63, 0x83, 0x6f, 0xfd, 0x13,
0xfa, 0xf7, 0xf4, 0x5a, 0x28, 0x39, 0xe6, 0x98, 0x93, 0xa8, 0xd5, 0xff, 0x22, 0xa7, 0x32, 0xa3,
0x5f, 0x76, 0xdd, 0x1f, 0xbe, 0xcd, 0x7c, 0xf3, 0xbe, 0xef, 0xbd, 0xf7, 0xcd, 0x7b, 0xe0, 0x62,
0x7c, 0xc6, 0xac, 0x80, 0xda, 0xe3, 0xd9, 0x90, 0xc4, 0x21, 0xe1, 0x84, 0xd9, 0x73, 0x12, 0x7a,
0x34, 0xb6, 0xf3, 0x07, 0x1c, 0x05, 0xb6, 0x17, 0x30, 0x97, 0xce, 0x49, 0xbc, 0xb0, 0xe7, 0xfd,
0x21, 0xe1, 0xb8, 0x6f, 0xfb, 0x24, 0x24, 0x31, 0xe6, 0xc4, 0xb3, 0xa2, 0x98, 0x72, 0x0a, 0x0f,
0xb3, 0x68, 0x0b, 0x47, 0x81, 0x55, 0x46, 0x5b, 0x79, 0x74, 0xfb, 0xd8, 0x0f, 0xf8, 0x68, 0x36,
0xb4, 0x5c, 0x3a, 0xb5, 0x7d, 0xea, 0x53, 0x5b, 0x92, 0x86, 0xb3, 0x6b, 0x79, 0x93, 0x17, 0x79,
0xca, 0xc4, 0xda, 0xdd, 0x95, 0xd4, 0x2e, 0x8d, 0x89, 0x3d, 0xdf, 0x48, 0xd8, 0x3e, 0xad, 0x62,
0xa6, 0xd8, 0x1d, 0x05, 0xa1, 0xa8, 0x2e, 0x1a, 0xfb, 0x02, 0x60, 0xf6, 0x94, 0x70, 0xfc, 0x35,
0x96, 0xfd, 0x2d, 0x56, 0x3c, 0x0b, 0x79, 0x30, 0x25, 0x1b, 0x84, 0xbf, 0x7f, 0x44, 0x60, 0xee,
0x88, 0x4c, 0xf1, 0x97, 0xbc, 0xee, 0xbb, 0x3a, 0xd0, 0x2e, 0x42, 0x2f, 0xa2, 0x41, 0xc8, 0xe1,
0x11, 0xd0, 0xb1, 0xe7, 0xc5, 0x84, 0x31, 0xc2, 0x5a, 0x4a, 0xa7, 0xde, 0xd3, 0x9d, 0x66, 0x9a,
0x98, 0xfa, 0x79, 0x01, 0xa2, 0xea, 0x1d, 0x7a, 0x00, 0xb8, 0x34, 0xf4, 0x02, 0x1e, 0xd0, 0x90,
0xb5, 0x76, 0x3a, 0x4a, 0xaf, 0x71, 0xf2, 0xa7, 0xf5, 0x3d, 0x7b, 0xad, 0x22, 0xd1, 0x7f, 0x25,
0xcf, 0x81, 0x37, 0x89, 0x59, 0x4b, 0x13, 0x13, 0x54, 0x18, 0x5a, 0xd1, 0x85, 0x3d, 0xa0, 0x8d,
0x28, 0xe3, 0x21, 0x9e, 0x92, 0x56, 0xbd, 0xa3, 0xf4, 0x74, 0x67, 0x2f, 0x4d, 0x4c, 0xed, 0x61,
0x8e, 0xa1, 0xf2, 0x15, 0x0e, 0x80, 0xce, 0x71, 0xec, 0x13, 0x8e, 0xc8, 0x75, 0x6b, 0x57, 0x96,
0xf3, 0xdb, 0x6a, 0x39, 0xe2, 0x83, 0xac, 0x79, 0xdf, 0xba, 0x1c, 0xbe, 0x26, 0xae, 0x08, 0x22,
0x31, 0x09, 0x5d, 0x92, 0x75, 0x78, 0x55, 0x30, 0x51, 0x25, 0x02, 0x87, 0x40, 0xe3, 0x34, 0xa2,
0x13, 0xea, 0x2f, 0x5a, 0x6a, 0xa7, 0xde, 0x6b, 0x9c, 0x9c, 0x6e, 0xd7, 0x9f, 0x75, 0x95, 0xd3,
0x2e, 0x42, 0x1e, 0x2f, 0x9c, 0xfd, 0xbc, 0x47, 0xad, 0x80, 0x51, 0xa9, 0xdb, 0xfe, 0x17, 0x34,
0xd7, 0x82, 0xe1, 0x3e, 0xa8, 0x8f, 0xc9, 0xa2, 0xa5, 0x88, 0x5e, 0x91, 0x38, 0xc2, 0x5f, 0x80,
0x3a, 0xc7, 0x93, 0x19, 0x91, 0x1e, 0xeb, 0x28, 0xbb, 0xfc, 0xb3, 0x73, 0xa6, 0x74, 0xff, 0x02,
0x70, 0xd3, 0x52, 0x68, 0x02, 0x35, 0x26, 0xd8, 0xcb, 0x34, 0x34, 0x47, 0x4f, 0x13, 0x53, 0x45,
0x02, 0x40, 0x19, 0xde, 0xfd, 0xa0, 0x80, 0xbd, 0x82, 0x37, 0xa0, 0x31, 0x87, 0x87, 0x60, 0x57,
0x1a, 0x2c, 0x93, 0x3a, 0x5a, 0x9a, 0x98, 0xbb, 0x4f, 0x85, 0xb9, 0x12, 0x85, 0x0f, 0x80, 0x26,
0x67, 0xc5, 0xa5, 0x93, 0xac, 0x04, 0xe7, 0x48, 0x34, 0x33, 0xc8, 0xb1, 0xfb, 0xc4, 0xfc, 0x75,
0x73, 0x0f, 0xac, 0xe2, 0x19, 0x95, 0x64, 0x91, 0x26, 0xa2, 0x31, 0x97, 0xff, 0xa8, 0x66, 0x69,
0x44, 0x7a, 0x24, 0x51, 0xd8, 0x07, 0x0d, 0x1c, 0x45, 0x05, 0x4d, 0xfe, 0xa0, 0xee, 0xfc, 0x9c,
0x26, 0x66, 0xe3, 0xbc, 0x82, 0xd1, 0x6a, 0x4c, 0x77, 0xb9, 0x03, 0x9a, 0x45, 0x23, 0xcf, 0x26,
0x81, 0x4b, 0xe0, 0x2b, 0xa0, 0x89, 0x95, 0xf2, 0x30, 0xc7, 0xb2, 0x9b, 0xf5, 0x91, 0x2c, 0x37,
0xc3, 0x8a, 0xc6, 0xbe, 0x00, 0x98, 0x25, 0xa2, 0xab, 0xa9, 0x78, 0x42, 0x38, 0xae, 0x46, 0xb2,
0xc2, 0x50, 0xa9, 0x0a, 0xff, 0x07, 0x8d, 0x7c, 0x07, 0xae, 0x16, 0x11, 0xc9, 0xcb, 0xec, 0xe6,
0x94, 0xc6, 0x79, 0xf5, 0x74, 0xbf, 0x7e, 0x45, 0xab, 0x34, 0xf8, 0x1c, 0xe8, 0x24, 0x2f, 0x5c,
0xec, 0x8e, 0x98, 0xad, 0xdf, 0xb7, 0x9b, 0x2d, 0xe7, 0x20, 0xcf, 0xa5, 0x17, 0x08, 0x43, 0x95,
0x16, 0xbc, 0x04, 0xaa, 0x70, 0x93, 0xb5, 0xea, 0x52, 0xf4, 0x8f, 0xed, 0x44, 0xc5, 0x37, 0x38,
0xcd, 0x5c, 0x58, 0x15, 0x37, 0x86, 0x32, 0x9d, 0xee, 0x7b, 0x05, 0x1c, 0xac, 0x79, 0xfc, 0x38,
0x60, 0x1c, 0xbe, 0xdc, 0xf0, 0xd9, 0xda, 0xce, 0x67, 0xc1, 0x96, 0x2e, 0x97, 0x4b, 0x51, 0x20,
0x2b, 0x1e, 0x0f, 0x80, 0x1a, 0x70, 0x32, 0x2d, 0x9c, 0x39, 0xda, 0xae, 0x09, 0x59, 0x5d, 0xd5,
0xc5, 0x23, 0xa1, 0x80, 0x32, 0x21, 0xe7, 0xf8, 0x66, 0x69, 0xd4, 0x6e, 0x97, 0x46, 0xed, 0x6e,
0x69, 0xd4, 0xde, 0xa4, 0x86, 0x72, 0x93, 0x1a, 0xca, 0x6d, 0x6a, 0x28, 0x77, 0xa9, 0xa1, 0x7c,
0x4c, 0x0d, 0xe5, 0xed, 0x27, 0xa3, 0xf6, 0xe2, 0xa7, 0x5c, 0xf2, 0x73, 0x00, 0x00, 0x00, 0xff,
0xff, 0x29, 0x1a, 0xa2, 0x6f, 0x6d, 0x06, 0x00, 0x00,
// 784 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x53, 0x4d, 0x8f, 0xe3, 0x44,
0x10, 0x8d, 0x27, 0x63, 0x8d, 0xdd, 0xd9, 0x11, 0xbb, 0x2d, 0x0e, 0xd1, 0xb0, 0xb2, 0x47, 0x41,
0xa0, 0x88, 0xd1, 0xda, 0xcc, 0x6a, 0x85, 0x56, 0x70, 0x1a, 0xc3, 0x08, 0x90, 0x80, 0x8d, 0x7a,
0x23, 0x21, 0x21, 0x0e, 0x74, 0xec, 0x5a, 0xc7, 0x24, 0x76, 0x5b, 0xdd, 0x9d, 0x48, 0xb9, 0xf1,
0x0f, 0xe0, 0xf7, 0x70, 0x45, 0x42, 0x73, 0xdc, 0xe3, 0x9e, 0x2c, 0x62, 0xfe, 0xc5, 0x9c, 0x50,
0xb7, 0xbf, 0x12, 0xc2, 0x47, 0x6e, 0xee, 0x57, 0xf5, 0x5e, 0xd5, 0x2b, 0x57, 0xa1, 0xdb, 0xc5,
0x73, 0xe1, 0x25, 0xcc, 0x5f, 0xac, 0x66, 0xc0, 0x33, 0x90, 0x20, 0xfc, 0x35, 0x64, 0x11, 0xe3,
0x7e, 0x1d, 0xa0, 0x79, 0xe2, 0x47, 0x89, 0x08, 0xd9, 0x1a, 0xf8, 0xc6, 0x5f, 0x5f, 0xcf, 0x40,
0xd2, 0x6b, 0x3f, 0x86, 0x0c, 0x38, 0x95, 0x10, 0x79, 0x39, 0x67, 0x92, 0xe1, 0xc7, 0x55, 0xb6,
0x47, 0xf3, 0xc4, 0x6b, 0xb3, 0xbd, 0x3a, 0xfb, 0xe2, 0x49, 0x9c, 0xc8, 0xf9, 0x6a, 0xe6, 0x85,
0x2c, 0xf5, 0x63, 0x16, 0x33, 0x5f, 0x93, 0x66, 0xab, 0x57, 0xfa, 0xa5, 0x1f, 0xfa, 0xab, 0x12,
0xbb, 0x18, 0xed, 0x94, 0x0e, 0x19, 0x07, 0x7f, 0x7d, 0x50, 0xf0, 0xe2, 0x59, 0x97, 0x93, 0xd2,
0x70, 0x9e, 0x64, 0xaa, 0xbb, 0x7c, 0x11, 0x2b, 0x40, 0xf8, 0x29, 0x48, 0xfa, 0x4f, 0x2c, 0xff,
0xdf, 0x58, 0x7c, 0x95, 0xc9, 0x24, 0x85, 0x03, 0xc2, 0x47, 0xff, 0x47, 0x10, 0xe1, 0x1c, 0x52,
0xfa, 0x77, 0xde, 0xe8, 0xd7, 0x3e, 0xb2, 0x6e, 0xb3, 0x28, 0x67, 0x49, 0x26, 0xf1, 0x15, 0xb2,
0x69, 0x14, 0x71, 0x10, 0x02, 0xc4, 0xd0, 0xb8, 0xec, 0x8f, 0xed, 0xe0, 0xbc, 0x2c, 0x5c, 0xfb,
0xa6, 0x01, 0x49, 0x17, 0xc7, 0x11, 0x42, 0x21, 0xcb, 0xa2, 0x44, 0x26, 0x2c, 0x13, 0xc3, 0x93,
0x4b, 0x63, 0x3c, 0x78, 0xfa, 0xa1, 0xf7, 0x5f, 0xe3, 0xf5, 0x9a, 0x42, 0x9f, 0xb6, 0xbc, 0x00,
0xdf, 0x15, 0x6e, 0xaf, 0x2c, 0x5c, 0xd4, 0x61, 0x64, 0x47, 0x17, 0x8f, 0x91, 0x35, 0x67, 0x42,
0x66, 0x34, 0x85, 0x61, 0xff, 0xd2, 0x18, 0xdb, 0xc1, 0x83, 0xb2, 0x70, 0xad, 0x2f, 0x6a, 0x8c,
0xb4, 0x51, 0x3c, 0x41, 0xb6, 0xa4, 0x3c, 0x06, 0x49, 0xe0, 0xd5, 0xf0, 0x54, 0xb7, 0xf3, 0xee,
0x6e, 0x3b, 0xea, 0x07, 0x79, 0xeb, 0x6b, 0xef, 0xc5, 0xec, 0x47, 0x08, 0x55, 0x12, 0x70, 0xc8,
0x42, 0xa8, 0x1c, 0x4e, 0x1b, 0x26, 0xe9, 0x44, 0xf0, 0x0c, 0x59, 0x92, 0xe5, 0x6c, 0xc9, 0xe2,
0xcd, 0xd0, 0xbc, 0xec, 0x8f, 0x07, 0x4f, 0x9f, 0x1d, 0xe7, 0xcf, 0x9b, 0xd6, 0xb4, 0xdb, 0x4c,
0xf2, 0x4d, 0xf0, 0xb0, 0xf6, 0x68, 0x35, 0x30, 0x69, 0x75, 0x2f, 0x3e, 0x41, 0xe7, 0x7b, 0xc9,
0xf8, 0x21, 0xea, 0x2f, 0x60, 0x33, 0x34, 0x94, 0x57, 0xa2, 0x3e, 0xf1, 0xdb, 0xc8, 0x5c, 0xd3,
0xe5, 0x0a, 0xf4, 0x8c, 0x6d, 0x52, 0x3d, 0x3e, 0x3e, 0x79, 0x6e, 0x8c, 0x7e, 0x36, 0x10, 0x3e,
0x9c, 0x29, 0x76, 0x91, 0xc9, 0x81, 0x46, 0x95, 0x88, 0x15, 0xd8, 0x65, 0xe1, 0x9a, 0x44, 0x01,
0xa4, 0xc2, 0xf1, 0x7b, 0xe8, 0x4c, 0x00, 0x5f, 0x27, 0x59, 0xac, 0x35, 0xad, 0x60, 0x50, 0x16,
0xee, 0xd9, 0xcb, 0x0a, 0x22, 0x4d, 0x0c, 0x5f, 0xa3, 0x81, 0x04, 0x9e, 0x26, 0x19, 0x95, 0x2a,
0xb5, 0xaf, 0x53, 0xdf, 0x2a, 0x0b, 0x77, 0x30, 0xed, 0x60, 0xb2, 0x9b, 0x33, 0xfa, 0xdd, 0x40,
0x0f, 0x9a, 0x8e, 0x26, 0x8c, 0x4b, 0xfc, 0x18, 0x9d, 0xea, 0x7f, 0xa7, 0xfd, 0x04, 0x56, 0x59,
0xb8, 0xa7, 0xdf, 0xa8, 0xff, 0xa6, 0x51, 0xfc, 0x39, 0xb2, 0xf4, 0x1a, 0x86, 0x6c, 0x59, 0xb9,
0x0b, 0xae, 0xd4, 0x9c, 0x26, 0x35, 0x76, 0x5f, 0xb8, 0xef, 0x1c, 0x9e, 0x98, 0xd7, 0x84, 0x49,
0x4b, 0x56, 0x65, 0x72, 0xc6, 0xa5, 0xee, 0xd1, 0xac, 0xca, 0xa8, 0xf2, 0x44, 0xa3, 0xca, 0x08,
0xcd, 0xf3, 0x86, 0xa6, 0x97, 0xc3, 0xae, 0x8c, 0xdc, 0x74, 0x30, 0xd9, 0xcd, 0x19, 0x6d, 0x4f,
0xd0, 0x79, 0x63, 0xe4, 0xe5, 0x32, 0x09, 0x01, 0xff, 0x80, 0x2c, 0x75, 0xad, 0x11, 0x95, 0x54,
0xbb, 0xd9, 0xdf, 0xf6, 0xf6, 0xe8, 0xbc, 0x7c, 0x11, 0x2b, 0x40, 0x78, 0x2a, 0xbb, 0x5b, 0xb8,
0xaf, 0x41, 0xd2, 0x6e, 0xdb, 0x3b, 0x8c, 0xb4, 0xaa, 0xf8, 0x33, 0x34, 0xa8, 0xcf, 0x6b, 0xba,
0xc9, 0xa1, 0x6e, 0x73, 0x54, 0x53, 0x06, 0x37, 0x5d, 0xe8, 0x7e, 0xff, 0x49, 0x76, 0x69, 0xf8,
0x5b, 0x64, 0x43, 0xdd, 0xb8, 0x3a, 0x4b, 0xb5, 0xb6, 0xef, 0x1f, 0xb7, 0xb6, 0xc1, 0xa3, 0xba,
0x96, 0xdd, 0x20, 0x82, 0x74, 0x5a, 0xf8, 0x05, 0x32, 0xd5, 0x34, 0xc5, 0xb0, 0xaf, 0x45, 0x3f,
0x38, 0x4e, 0x54, 0xfd, 0x86, 0xe0, 0xbc, 0x16, 0x36, 0xd5, 0x4b, 0x90, 0x4a, 0x67, 0xf4, 0x9b,
0x81, 0x1e, 0xed, 0xcd, 0xf8, 0xab, 0x44, 0x48, 0xfc, 0xfd, 0xc1, 0x9c, 0xbd, 0xe3, 0xe6, 0xac,
0xd8, 0x7a, 0xca, 0xed, 0xbd, 0x35, 0xc8, 0xce, 0x8c, 0x27, 0xc8, 0x4c, 0x24, 0xa4, 0xcd, 0x64,
0xae, 0x8e, 0x33, 0xa1, 0xbb, 0xeb, 0x5c, 0x7c, 0xa9, 0x14, 0x48, 0x25, 0x14, 0x3c, 0xb9, 0xdb,
0x3a, 0xbd, 0xd7, 0x5b, 0xa7, 0xf7, 0x66, 0xeb, 0xf4, 0x7e, 0x2a, 0x1d, 0xe3, 0xae, 0x74, 0x8c,
0xd7, 0xa5, 0x63, 0xbc, 0x29, 0x1d, 0xe3, 0x8f, 0xd2, 0x31, 0x7e, 0xf9, 0xd3, 0xe9, 0x7d, 0x77,
0x56, 0x4b, 0xfe, 0x15, 0x00, 0x00, 0xff, 0xff, 0x95, 0x55, 0x4b, 0x65, 0xc8, 0x06, 0x00, 0x00,
}
func (m *Endpoint) Marshal() (dAtA []byte, err error) {
@ -355,6 +357,26 @@ func (m *EndpointConditions) MarshalToSizedBuffer(dAtA []byte) (int, error) {
_ = i
var l int
_ = l
if m.Terminating != nil {
i--
if *m.Terminating {
dAtA[i] = 1
} else {
dAtA[i] = 0
}
i--
dAtA[i] = 0x18
}
if m.Serving != nil {
i--
if *m.Serving {
dAtA[i] = 1
} else {
dAtA[i] = 0
}
i--
dAtA[i] = 0x10
}
if m.Ready != nil {
i--
if *m.Ready {
@ -583,6 +605,12 @@ func (m *EndpointConditions) Size() (n int) {
if m.Ready != nil {
n += 2
}
if m.Serving != nil {
n += 2
}
if m.Terminating != nil {
n += 2
}
return n
}
@ -688,6 +716,8 @@ func (this *EndpointConditions) String() string {
}
s := strings.Join([]string{`&EndpointConditions{`,
`Ready:` + valueToStringGenerated(this.Ready) + `,`,
`Serving:` + valueToStringGenerated(this.Serving) + `,`,
`Terminating:` + valueToStringGenerated(this.Terminating) + `,`,
`}`,
}, "")
return s
@ -1116,6 +1146,48 @@ func (m *EndpointConditions) Unmarshal(dAtA []byte) error {
}
b := bool(v != 0)
m.Ready = &b
case 2:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field Serving", wireType)
}
var v int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowGenerated
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
v |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
b := bool(v != 0)
m.Serving = &b
case 3:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field Terminating", wireType)
}
var v int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowGenerated
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
v |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
b := bool(v != 0)
m.Terminating = &b
default:
iNdEx = preIndex
skippy, err := skipGenerated(dAtA[iNdEx:])

View File

@ -76,9 +76,22 @@ message EndpointConditions {
// 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.
// unknown state as ready. For compatibility reasons, ready should never be
// "true" for terminating endpoints.
// +optional
optional bool ready = 1;
// 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.
// +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.
// +optional
optional bool terminating = 3;
}
// EndpointPort represents a Port used by an EndpointSlice

View File

@ -117,9 +117,22 @@ type EndpointConditions struct {
// 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.
// unknown state as ready. For compatibility reasons, ready should never be
// "true" for terminating endpoints.
// +optional
Ready *bool `json:"ready,omitempty" protobuf:"bytes,1,name=ready"`
// 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.
// +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.
// +optional
Terminating *bool `json:"terminating,omitempty" protobuf:"bytes,3,name=terminating"`
}
// EndpointPort represents a Port used by an EndpointSlice

View File

@ -41,8 +41,10 @@ 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.",
"": "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.",
"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

@ -72,6 +72,16 @@ func (in *EndpointConditions) DeepCopyInto(out *EndpointConditions) {
*out = new(bool)
**out = **in
}
if in.Serving != nil {
in, out := &in.Serving, &out.Serving
*out = new(bool)
**out = **in
}
if in.Terminating != nil {
in, out := &in.Terminating, &out.Terminating
*out = new(bool)
**out = **in
}
return
}