From 9613f79adde3e0080fe25576cb37a74f25262848 Mon Sep 17 00:00:00 2001 From: Rob Scott Date: Tue, 10 Nov 2020 17:47:20 -0800 Subject: [PATCH 1/8] Adding NodeName to EndpointSlice API, deprecation updates In addition to adding NodeName, this notes that the topology field will be deprecated soon. It also removes the IP address type that was deprecated in Kubernetes 1.17 and intended to be removed in 1.20. --- api/openapi-spec/swagger.json | 6 +- pkg/apis/discovery/types.go | 11 +- .../v1alpha1/zz_generated.conversion.go | 2 + .../v1beta1/zz_generated.conversion.go | 2 + pkg/apis/discovery/validation/BUILD | 5 + pkg/apis/discovery/validation/validation.go | 53 ++++-- .../discovery/validation/validation_test.go | 172 ++++++++++++++++-- pkg/apis/discovery/zz_generated.deepcopy.go | 5 + .../api/discovery/v1alpha1/generated.pb.go | 146 ++++++++++----- .../api/discovery/v1alpha1/generated.proto | 6 + .../k8s.io/api/discovery/v1alpha1/types.go | 5 + .../v1alpha1/types_swagger_doc_generated.go | 3 +- .../v1alpha1/zz_generated.deepcopy.go | 5 + .../api/discovery/v1beta1/generated.pb.go | 144 ++++++++++----- .../api/discovery/v1beta1/generated.proto | 6 + .../src/k8s.io/api/discovery/v1beta1/types.go | 11 +- .../v1beta1/types_swagger_doc_generated.go | 3 +- .../v1beta1/zz_generated.deepcopy.go | 5 + 18 files changed, 443 insertions(+), 147 deletions(-) diff --git a/api/openapi-spec/swagger.json b/api/openapi-spec/swagger.json index 61805b66b09..7366e8bc361 100644 --- a/api/openapi-spec/swagger.json +++ b/api/openapi-spec/swagger.json @@ -10897,6 +10897,10 @@ "description": "hostname of this endpoint. This field may be used by consumers of endpoints to distinguish endpoints from each other (e.g. in DNS names). Multiple endpoints which use the same hostname should be considered fungible (e.g. multiple A values in DNS). Must be lowercase and pass DNS Label (RFC 1123) validation.", "type": "string" }, + "nodeName": { + "description": "nodeName represents the name of the Node hosting this endpoint. This can be used to determine endpoints local to a Node.", + "type": "string" + }, "targetRef": { "$ref": "#/definitions/io.k8s.api.core.v1.ObjectReference", "description": "targetRef is a reference to a Kubernetes object that represents this endpoint." @@ -10905,7 +10909,7 @@ "additionalProperties": { "type": "string" }, - "description": "topology contains arbitrary topology information associated with the endpoint. These key/value pairs must conform with the label format. https://kubernetes.io/docs/concepts/overview/working-with-objects/labels Topology may include a maximum of 16 key/value pairs. This includes, but is not limited to the following well known keys: * kubernetes.io/hostname: the value indicates the hostname of the node\n where the endpoint is located. This should match the corresponding\n node label.\n* topology.kubernetes.io/zone: the value indicates the zone where the\n endpoint is located. This should match the corresponding node label.\n* topology.kubernetes.io/region: the value indicates the region where the\n endpoint is located. This should match the corresponding node label.", + "description": "topology contains arbitrary topology information associated with the endpoint. These key/value pairs must conform with the label format. https://kubernetes.io/docs/concepts/overview/working-with-objects/labels Topology may include a maximum of 16 key/value pairs. This includes, but is not limited to the following well known keys: * kubernetes.io/hostname: the value indicates the hostname of the node\n where the endpoint is located. This should match the corresponding\n node label.\n* topology.kubernetes.io/zone: the value indicates the zone where the\n endpoint is located. This should match the corresponding node label.\n* topology.kubernetes.io/region: the value indicates the region where the\n endpoint is located. This should match the corresponding node label.\nthis field will be deprecated in an upcoming release.", "type": "object" } }, diff --git a/pkg/apis/discovery/types.go b/pkg/apis/discovery/types.go index 6fa6624270c..5d79683067b 100644 --- a/pkg/apis/discovery/types.go +++ b/pkg/apis/discovery/types.go @@ -57,12 +57,6 @@ type EndpointSlice struct { type AddressType string const ( - // AddressTypeIP represents an IP Address. - // This address type has been deprecated and has been replaced by the IPv4 - // and IPv6 adddress types. New resources with this address type will be - // considered invalid. This will be fully removed in 1.18. - // +deprecated - AddressTypeIP = AddressType("IP") // AddressTypeIPv4 represents an IPv4 Address. AddressTypeIPv4 = AddressType(api.IPv4Protocol) // AddressTypeIPv6 represents an IPv6 Address. @@ -105,8 +99,13 @@ type Endpoint struct { // endpoint is located. This should match the corresponding node label. // * topology.kubernetes.io/region: the value indicates the region where the // endpoint is located. This should match the corresponding node label. + // this field will be deprecated in an upcoming release. // +optional Topology map[string]string + // nodeName represents the name of the Node hosting this endpoint. This can + // be used to determine endpoints local to a Node. + // +optional + NodeName *string } // EndpointConditions represents the current condition of an endpoint. diff --git a/pkg/apis/discovery/v1alpha1/zz_generated.conversion.go b/pkg/apis/discovery/v1alpha1/zz_generated.conversion.go index 46412fd0c52..0f65d9579aa 100644 --- a/pkg/apis/discovery/v1alpha1/zz_generated.conversion.go +++ b/pkg/apis/discovery/v1alpha1/zz_generated.conversion.go @@ -99,6 +99,7 @@ func autoConvert_v1alpha1_Endpoint_To_discovery_Endpoint(in *v1alpha1.Endpoint, out.Hostname = (*string)(unsafe.Pointer(in.Hostname)) out.TargetRef = (*core.ObjectReference)(unsafe.Pointer(in.TargetRef)) out.Topology = *(*map[string]string)(unsafe.Pointer(&in.Topology)) + out.NodeName = (*string)(unsafe.Pointer(in.NodeName)) return nil } @@ -115,6 +116,7 @@ func autoConvert_discovery_Endpoint_To_v1alpha1_Endpoint(in *discovery.Endpoint, out.Hostname = (*string)(unsafe.Pointer(in.Hostname)) out.TargetRef = (*v1.ObjectReference)(unsafe.Pointer(in.TargetRef)) out.Topology = *(*map[string]string)(unsafe.Pointer(&in.Topology)) + out.NodeName = (*string)(unsafe.Pointer(in.NodeName)) return nil } diff --git a/pkg/apis/discovery/v1beta1/zz_generated.conversion.go b/pkg/apis/discovery/v1beta1/zz_generated.conversion.go index f06d2d0ef56..2640deee901 100644 --- a/pkg/apis/discovery/v1beta1/zz_generated.conversion.go +++ b/pkg/apis/discovery/v1beta1/zz_generated.conversion.go @@ -99,6 +99,7 @@ func autoConvert_v1beta1_Endpoint_To_discovery_Endpoint(in *v1beta1.Endpoint, ou out.Hostname = (*string)(unsafe.Pointer(in.Hostname)) out.TargetRef = (*core.ObjectReference)(unsafe.Pointer(in.TargetRef)) out.Topology = *(*map[string]string)(unsafe.Pointer(&in.Topology)) + out.NodeName = (*string)(unsafe.Pointer(in.NodeName)) return nil } @@ -115,6 +116,7 @@ func autoConvert_discovery_Endpoint_To_v1beta1_Endpoint(in *discovery.Endpoint, out.Hostname = (*string)(unsafe.Pointer(in.Hostname)) out.TargetRef = (*v1.ObjectReference)(unsafe.Pointer(in.TargetRef)) out.Topology = *(*map[string]string)(unsafe.Pointer(&in.Topology)) + out.NodeName = (*string)(unsafe.Pointer(in.NodeName)) return nil } diff --git a/pkg/apis/discovery/validation/BUILD b/pkg/apis/discovery/validation/BUILD index 1f1a470f8bc..38cd42af359 100644 --- a/pkg/apis/discovery/validation/BUILD +++ b/pkg/apis/discovery/validation/BUILD @@ -9,11 +9,13 @@ go_library( "//pkg/apis/core:go_default_library", "//pkg/apis/core/validation:go_default_library", "//pkg/apis/discovery:go_default_library", + "//pkg/features:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/api/validation:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1/validation:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/util/sets:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/util/validation:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/util/validation/field:go_default_library", + "//staging/src/k8s.io/apiserver/pkg/util/feature:go_default_library", ], ) @@ -24,7 +26,10 @@ go_test( deps = [ "//pkg/apis/core:go_default_library", "//pkg/apis/discovery:go_default_library", + "//pkg/features:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1: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", ], ) diff --git a/pkg/apis/discovery/validation/validation.go b/pkg/apis/discovery/validation/validation.go index 810f2ca124d..71d5ca6cbb5 100644 --- a/pkg/apis/discovery/validation/validation.go +++ b/pkg/apis/discovery/validation/validation.go @@ -22,9 +22,11 @@ import ( "k8s.io/apimachinery/pkg/util/sets" "k8s.io/apimachinery/pkg/util/validation" "k8s.io/apimachinery/pkg/util/validation/field" + utilfeature "k8s.io/apiserver/pkg/util/feature" api "k8s.io/kubernetes/pkg/apis/core" apivalidation "k8s.io/kubernetes/pkg/apis/core/validation" "k8s.io/kubernetes/pkg/apis/discovery" + "k8s.io/kubernetes/pkg/features" ) var ( @@ -33,9 +35,6 @@ var ( string(discovery.AddressTypeIPv6), string(discovery.AddressTypeFQDN), ) - deprecatedAddressTypes = sets.NewString( - string(discovery.AddressTypeIP), - ) supportedPortProtocols = sets.NewString( string(api.ProtocolTCP), string(api.ProtocolUDP), @@ -53,10 +52,10 @@ var ( var ValidateEndpointSliceName = apimachineryvalidation.NameIsDNSSubdomain // ValidateEndpointSlice validates an EndpointSlice. -func ValidateEndpointSlice(endpointSlice *discovery.EndpointSlice, validAddressTypes sets.String) field.ErrorList { +func ValidateEndpointSlice(endpointSlice *discovery.EndpointSlice, allowNodeName bool) field.ErrorList { allErrs := apivalidation.ValidateObjectMeta(&endpointSlice.ObjectMeta, true, ValidateEndpointSliceName, field.NewPath("metadata")) - allErrs = append(allErrs, validateAddressType(endpointSlice.AddressType, validAddressTypes)...) - allErrs = append(allErrs, validateEndpoints(endpointSlice.Endpoints, endpointSlice.AddressType, field.NewPath("endpoints"))...) + allErrs = append(allErrs, validateAddressType(endpointSlice.AddressType)...) + allErrs = append(allErrs, validateEndpoints(endpointSlice.Endpoints, endpointSlice.AddressType, allowNodeName, field.NewPath("endpoints"))...) allErrs = append(allErrs, validatePorts(endpointSlice.Ports, field.NewPath("ports"))...) return allErrs @@ -64,18 +63,33 @@ func ValidateEndpointSlice(endpointSlice *discovery.EndpointSlice, validAddressT // ValidateEndpointSliceCreate validates an EndpointSlice when it is created. func ValidateEndpointSliceCreate(endpointSlice *discovery.EndpointSlice) field.ErrorList { - return ValidateEndpointSlice(endpointSlice, supportedAddressTypes) + // allow NodeName value if the feature gate is set. + allowNodeName := utilfeature.DefaultFeatureGate.Enabled(features.EndpointSliceNodeName) + + return ValidateEndpointSlice(endpointSlice, allowNodeName) } // ValidateEndpointSliceUpdate validates an EndpointSlice when it is updated. func ValidateEndpointSliceUpdate(newEndpointSlice, oldEndpointSlice *discovery.EndpointSlice) field.ErrorList { - allErrs := ValidateEndpointSlice(newEndpointSlice, supportedAddressTypes.Union(deprecatedAddressTypes)) + // allow NodeName value if the feature gate is set. + allowNodeName := utilfeature.DefaultFeatureGate.Enabled(features.EndpointSliceNodeName) + + if !allowNodeName { + for _, ep := range oldEndpointSlice.Endpoints { + if ep.NodeName != nil { + allowNodeName = true + break + } + } + } + + allErrs := ValidateEndpointSlice(newEndpointSlice, allowNodeName) allErrs = append(allErrs, apivalidation.ValidateImmutableField(newEndpointSlice.AddressType, oldEndpointSlice.AddressType, field.NewPath("addressType"))...) return allErrs } -func validateEndpoints(endpoints []discovery.Endpoint, addrType discovery.AddressType, fldPath *field.Path) field.ErrorList { +func validateEndpoints(endpoints []discovery.Endpoint, addrType discovery.AddressType, allowNodeName bool, fldPath *field.Path) field.ErrorList { allErrs := field.ErrorList{} if len(endpoints) > maxEndpoints { @@ -97,10 +111,6 @@ func validateEndpoints(endpoints []discovery.Endpoint, addrType discovery.Addres // This validates known address types, unknown types fall through // and do not get validated. switch addrType { - case discovery.AddressTypeIP: - for _, msg := range validation.IsValidIP(address) { - allErrs = append(allErrs, field.Invalid(addressPath.Index(i), address, msg)) - } case discovery.AddressTypeIPv4: allErrs = append(allErrs, validation.IsValidIPv4Address(addressPath.Index(i), address)...) case discovery.AddressTypeIPv6: @@ -110,6 +120,17 @@ func validateEndpoints(endpoints []discovery.Endpoint, addrType discovery.Addres } } + if endpoint.NodeName != nil { + nnPath := idxPath.Child("nodeName") + if allowNodeName { + for _, msg := range apivalidation.ValidateNodeName(*endpoint.NodeName, false) { + allErrs = append(allErrs, field.Invalid(nnPath, *endpoint.NodeName, msg)) + } + } else { + allErrs = append(allErrs, field.Forbidden(nnPath, "may not be set unless EndpointSliceNodeName feature gate is enabled")) + } + } + topologyPath := idxPath.Child("topology") if len(endpoint.Topology) > maxTopologyLabels { allErrs = append(allErrs, field.TooMany(topologyPath, len(endpoint.Topology), maxTopologyLabels)) @@ -162,13 +183,13 @@ func validatePorts(endpointPorts []discovery.EndpointPort, fldPath *field.Path) return allErrs } -func validateAddressType(addressType discovery.AddressType, validAddressTypes sets.String) field.ErrorList { +func validateAddressType(addressType discovery.AddressType) field.ErrorList { allErrs := field.ErrorList{} if addressType == "" { allErrs = append(allErrs, field.Required(field.NewPath("addressType"), "")) - } else if !validAddressTypes.Has(string(addressType)) { - allErrs = append(allErrs, field.NotSupported(field.NewPath("addressType"), addressType, validAddressTypes.List())) + } else if !supportedAddressTypes.Has(string(addressType)) { + allErrs = append(allErrs, field.NotSupported(field.NewPath("addressType"), addressType, supportedAddressTypes.List())) } return allErrs diff --git a/pkg/apis/discovery/validation/validation_test.go b/pkg/apis/discovery/validation/validation_test.go index 060545f93ab..b54c7fcc494 100644 --- a/pkg/apis/discovery/validation/validation_test.go +++ b/pkg/apis/discovery/validation/validation_test.go @@ -22,8 +22,11 @@ import ( "testing" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + utilfeature "k8s.io/apiserver/pkg/util/feature" + featuregatetesting "k8s.io/component-base/featuregate/testing" api "k8s.io/kubernetes/pkg/apis/core" "k8s.io/kubernetes/pkg/apis/discovery" + "k8s.io/kubernetes/pkg/features" utilpointer "k8s.io/utils/pointer" ) @@ -92,7 +95,7 @@ func TestValidateEndpointSlice(t *testing.T) { expectedErrors: 0, endpointSlice: &discovery.EndpointSlice{ ObjectMeta: standardMeta, - AddressType: discovery.AddressTypeIP, + AddressType: discovery.AddressTypeIPv4, Ports: []discovery.EndpointPort{{ Name: utilpointer.StringPtr("one"), Protocol: protocolPtr(api.ProtocolTCP), @@ -378,7 +381,7 @@ func TestValidateEndpointSlice(t *testing.T) { expectedErrors: 1, endpointSlice: &discovery.EndpointSlice{ ObjectMeta: standardMeta, - AddressType: discovery.AddressTypeIP, + AddressType: discovery.AddressTypeIPv4, Ports: []discovery.EndpointPort{{ Name: utilpointer.StringPtr("http"), Protocol: protocolPtr(api.ProtocolTCP), @@ -438,7 +441,7 @@ func TestValidateEndpointSlice(t *testing.T) { expectedErrors: 1, endpointSlice: &discovery.EndpointSlice{ ObjectMeta: standardMeta, - AddressType: discovery.AddressTypeIP, + AddressType: discovery.AddressTypeIPv4, Ports: []discovery.EndpointPort{{ Name: utilpointer.StringPtr("http"), Protocol: protocolPtr(api.ProtocolTCP), @@ -458,7 +461,7 @@ func TestValidateEndpointSlice(t *testing.T) { for name, testCase := range testCases { t.Run(name, func(t *testing.T) { - errs := ValidateEndpointSlice(testCase.endpointSlice, supportedAddressTypes.Union(deprecatedAddressTypes)) + errs := ValidateEndpointSlice(testCase.endpointSlice, true) if len(errs) != testCase.expectedErrors { t.Errorf("Expected %d errors, got %d errors: %v", testCase.expectedErrors, len(errs), errs) } @@ -473,8 +476,9 @@ func TestValidateEndpointSliceCreate(t *testing.T) { } testCases := map[string]struct { - expectedErrors int - endpointSlice *discovery.EndpointSlice + expectedErrors int + endpointSlice *discovery.EndpointSlice + nodeNameGateEnabled bool }{ "good-slice": { expectedErrors: 0, @@ -491,13 +495,64 @@ func TestValidateEndpointSliceCreate(t *testing.T) { }}, }, }, + "good-slice-node-name": { + expectedErrors: 0, + nodeNameGateEnabled: true, + endpointSlice: &discovery.EndpointSlice{ + ObjectMeta: standardMeta, + AddressType: discovery.AddressTypeIPv4, + Ports: []discovery.EndpointPort{{ + Name: utilpointer.StringPtr("http"), + Protocol: protocolPtr(api.ProtocolTCP), + }}, + Endpoints: []discovery.Endpoint{{ + Addresses: generateIPAddresses(1), + Hostname: utilpointer.StringPtr("valid-123"), + NodeName: utilpointer.StringPtr("valid-node-name"), + }}, + }, + }, // expected failures + "bad-node-name": { + expectedErrors: 1, + nodeNameGateEnabled: true, + endpointSlice: &discovery.EndpointSlice{ + ObjectMeta: standardMeta, + AddressType: discovery.AddressTypeIPv4, + Ports: []discovery.EndpointPort{{ + Name: utilpointer.StringPtr("http"), + Protocol: protocolPtr(api.ProtocolTCP), + }}, + Endpoints: []discovery.Endpoint{{ + Addresses: generateIPAddresses(1), + Hostname: utilpointer.StringPtr("valid-123"), + NodeName: utilpointer.StringPtr("INvalid-node-name"), + }}, + }, + }, + "node-name-disabled": { + expectedErrors: 1, + nodeNameGateEnabled: false, + endpointSlice: &discovery.EndpointSlice{ + ObjectMeta: standardMeta, + AddressType: discovery.AddressTypeIPv4, + Ports: []discovery.EndpointPort{{ + Name: utilpointer.StringPtr("http"), + Protocol: protocolPtr(api.ProtocolTCP), + }}, + Endpoints: []discovery.Endpoint{{ + Addresses: generateIPAddresses(1), + Hostname: utilpointer.StringPtr("valid-123"), + NodeName: utilpointer.StringPtr("valid-node-name"), + }}, + }, + }, "deprecated-address-type": { expectedErrors: 1, endpointSlice: &discovery.EndpointSlice{ ObjectMeta: standardMeta, - AddressType: discovery.AddressTypeIP, + AddressType: discovery.AddressType("IP"), Ports: []discovery.EndpointPort{{ Name: utilpointer.StringPtr("http"), Protocol: protocolPtr(api.ProtocolTCP), @@ -524,6 +579,8 @@ func TestValidateEndpointSliceCreate(t *testing.T) { } for name, testCase := range testCases { + defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.EndpointSliceNodeName, testCase.nodeNameGateEnabled)() + t.Run(name, func(t *testing.T) { errs := ValidateEndpointSliceCreate(testCase.endpointSlice) if len(errs) != testCase.expectedErrors { @@ -537,9 +594,10 @@ func TestValidateEndpointSliceUpdate(t *testing.T) { standardMeta := metav1.ObjectMeta{Name: "es1", Namespace: "test"} testCases := map[string]struct { - expectedErrors int - newEndpointSlice *discovery.EndpointSlice - oldEndpointSlice *discovery.EndpointSlice + expectedErrors int + nodeNameGateEnabled bool + newEndpointSlice *discovery.EndpointSlice + oldEndpointSlice *discovery.EndpointSlice }{ "valid and identical slices": { newEndpointSlice: &discovery.EndpointSlice{ @@ -552,21 +610,99 @@ func TestValidateEndpointSliceUpdate(t *testing.T) { }, expectedErrors: 0, }, - "deprecated address type": { - expectedErrors: 0, + "node name set before + after, gate disabled": { newEndpointSlice: &discovery.EndpointSlice{ ObjectMeta: standardMeta, - AddressType: discovery.AddressTypeIP, + AddressType: discovery.AddressTypeIPv4, + Endpoints: []discovery.Endpoint{{ + Addresses: []string{"10.1.2.3"}, + NodeName: utilpointer.StringPtr("foo"), + }}, }, oldEndpointSlice: &discovery.EndpointSlice{ ObjectMeta: standardMeta, - AddressType: discovery.AddressTypeIP, + AddressType: discovery.AddressTypeIPv4, + Endpoints: []discovery.Endpoint{{ + Addresses: []string{"10.1.2.3"}, + NodeName: utilpointer.StringPtr("foo"), + }}, + }, + expectedErrors: 0, + }, + "node name set after, gate enabled": { + nodeNameGateEnabled: true, + newEndpointSlice: &discovery.EndpointSlice{ + ObjectMeta: standardMeta, + AddressType: discovery.AddressTypeIPv4, + Endpoints: []discovery.Endpoint{{ + Addresses: []string{"10.1.2.3"}, + }}, + }, + oldEndpointSlice: &discovery.EndpointSlice{ + ObjectMeta: standardMeta, + AddressType: discovery.AddressTypeIPv4, + Endpoints: []discovery.Endpoint{{ + Addresses: []string{"10.1.2.3"}, + NodeName: utilpointer.StringPtr("foo"), + }}, + }, + expectedErrors: 0, + }, + + // expected errors + "invalide node name set after, gate enabled": { + nodeNameGateEnabled: true, + newEndpointSlice: &discovery.EndpointSlice{ + ObjectMeta: standardMeta, + AddressType: discovery.AddressTypeIPv4, + Endpoints: []discovery.Endpoint{{ + Addresses: []string{"10.1.2.3"}, + NodeName: utilpointer.StringPtr("INVALID foo"), + }}, + }, + oldEndpointSlice: &discovery.EndpointSlice{ + ObjectMeta: standardMeta, + AddressType: discovery.AddressTypeIPv4, + Endpoints: []discovery.Endpoint{{ + Addresses: []string{"10.1.2.3"}, + }}, + }, + expectedErrors: 1, + }, + "node name set after, gate disabled": { + nodeNameGateEnabled: false, + newEndpointSlice: &discovery.EndpointSlice{ + ObjectMeta: standardMeta, + AddressType: discovery.AddressTypeIPv4, + Endpoints: []discovery.Endpoint{{ + Addresses: []string{"10.1.2.3"}, + }}, + }, + oldEndpointSlice: &discovery.EndpointSlice{ + ObjectMeta: standardMeta, + AddressType: discovery.AddressTypeIPv4, + Endpoints: []discovery.Endpoint{{ + Addresses: []string{"10.1.2.3"}, + NodeName: utilpointer.StringPtr("foo"), + }}, + }, + expectedErrors: 0, + }, + "deprecated address type": { + expectedErrors: 1, + newEndpointSlice: &discovery.EndpointSlice{ + ObjectMeta: standardMeta, + AddressType: discovery.AddressType("IP"), + }, + oldEndpointSlice: &discovery.EndpointSlice{ + ObjectMeta: standardMeta, + AddressType: discovery.AddressType("IP"), }, }, "valid and identical slices with different address types": { newEndpointSlice: &discovery.EndpointSlice{ ObjectMeta: standardMeta, - AddressType: discovery.AddressTypeIP, + AddressType: discovery.AddressTypeIPv4, }, oldEndpointSlice: &discovery.EndpointSlice{ ObjectMeta: standardMeta, @@ -577,7 +713,7 @@ func TestValidateEndpointSliceUpdate(t *testing.T) { "invalid slices with valid address types": { newEndpointSlice: &discovery.EndpointSlice{ ObjectMeta: standardMeta, - AddressType: discovery.AddressTypeIP, + AddressType: discovery.AddressTypeIPv4, Ports: []discovery.EndpointPort{{ Name: utilpointer.StringPtr(""), Protocol: protocolPtr(api.Protocol("invalid")), @@ -585,7 +721,7 @@ func TestValidateEndpointSliceUpdate(t *testing.T) { }, oldEndpointSlice: &discovery.EndpointSlice{ ObjectMeta: standardMeta, - AddressType: discovery.AddressTypeIP, + AddressType: discovery.AddressTypeIPv4, }, expectedErrors: 1, }, @@ -593,6 +729,8 @@ func TestValidateEndpointSliceUpdate(t *testing.T) { for name, testCase := range testCases { t.Run(name, func(t *testing.T) { + defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.EndpointSliceNodeName, testCase.nodeNameGateEnabled)() + errs := ValidateEndpointSliceUpdate(testCase.newEndpointSlice, testCase.oldEndpointSlice) if len(errs) != testCase.expectedErrors { t.Errorf("Expected %d errors, got %d errors: %v", testCase.expectedErrors, len(errs), errs) diff --git a/pkg/apis/discovery/zz_generated.deepcopy.go b/pkg/apis/discovery/zz_generated.deepcopy.go index bb6b56c0763..aba324f34c7 100644 --- a/pkg/apis/discovery/zz_generated.deepcopy.go +++ b/pkg/apis/discovery/zz_generated.deepcopy.go @@ -51,6 +51,11 @@ func (in *Endpoint) DeepCopyInto(out *Endpoint) { (*out)[key] = val } } + if in.NodeName != nil { + in, out := &in.NodeName, &out.NodeName + *out = new(string) + **out = **in + } return } diff --git a/staging/src/k8s.io/api/discovery/v1alpha1/generated.pb.go b/staging/src/k8s.io/api/discovery/v1alpha1/generated.pb.go index 4f1f7306690..5cbee6168c1 100644 --- a/staging/src/k8s.io/api/discovery/v1alpha1/generated.pb.go +++ b/staging/src/k8s.io/api/discovery/v1alpha1/generated.pb.go @@ -200,57 +200,58 @@ func init() { } var fileDescriptor_772f83c5b34e07a5 = []byte{ - // 787 bytes of a gzipped FileDescriptorProto + // 801 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, + 0x10, 0x8d, 0x27, 0x13, 0xd6, 0xee, 0xec, 0x88, 0xdd, 0x16, 0x87, 0x68, 0x00, 0x7b, 0x14, 0x84, + 0x88, 0x34, 0xd0, 0x26, 0x23, 0x40, 0x2b, 0x38, 0x8d, 0x61, 0xf9, 0x90, 0x60, 0x19, 0x7a, 0xe7, + 0x80, 0x10, 0x07, 0x7a, 0xec, 0x5a, 0xc7, 0x24, 0x76, 0x5b, 0xdd, 0x9d, 0x48, 0xb9, 0xf1, 0x0f, + 0xe0, 0x47, 0x21, 0x34, 0xc7, 0x3d, 0xee, 0xc9, 0x62, 0xbc, 0x12, 0x3f, 0x62, 0x4f, 0xa8, 0xdb, + 0x9f, 0x43, 0x80, 0xcd, 0xcd, 0xfd, 0xaa, 0xde, 0xab, 0x7a, 0xe5, 0x2a, 0xf4, 0xf9, 0xf2, 0x81, + 0x24, 0x09, 0xf7, 0x97, 0xeb, 0x2b, 0x10, 0x19, 0x28, 0x90, 0xfe, 0x06, 0xb2, 0x88, 0x0b, 0xbf, + 0x0e, 0xb0, 0x3c, 0xf1, 0xa3, 0x44, 0x86, 0x7c, 0x03, 0x62, 0xeb, 0x6f, 0xe6, 0x6c, 0x95, 0x2f, + 0xd8, 0xdc, 0x8f, 0x21, 0x03, 0xc1, 0x14, 0x44, 0x24, 0x17, 0x5c, 0x71, 0xfc, 0x66, 0x95, 0x4e, + 0x58, 0x9e, 0x90, 0x36, 0x9d, 0x34, 0xe9, 0xc7, 0xef, 0xc5, 0x89, 0x5a, 0xac, 0xaf, 0x48, 0xc8, + 0x53, 0x3f, 0xe6, 0x31, 0xf7, 0x0d, 0xeb, 0x6a, 0xfd, 0xc4, 0xbc, 0xcc, 0xc3, 0x7c, 0x55, 0x6a, + 0xc7, 0xd3, 0x5e, 0xf1, 0x90, 0x0b, 0xf0, 0x37, 0x3b, 0x15, 0x8f, 0x3f, 0xe8, 0x72, 0x52, 0x16, + 0x2e, 0x92, 0x4c, 0xf7, 0x97, 0x2f, 0x63, 0x0d, 0x48, 0x3f, 0x05, 0xc5, 0xfe, 0x8d, 0xe5, 0xff, + 0x17, 0x4b, 0xac, 0x33, 0x95, 0xa4, 0xb0, 0x43, 0xf8, 0xe8, 0x65, 0x04, 0x19, 0x2e, 0x20, 0x65, + 0xff, 0xe4, 0x4d, 0xff, 0x1a, 0x22, 0xfb, 0x61, 0x16, 0xe5, 0x3c, 0xc9, 0x14, 0x3e, 0x45, 0x0e, + 0x8b, 0x22, 0x01, 0x52, 0x82, 0x9c, 0x58, 0x27, 0xc3, 0x99, 0x13, 0x1c, 0x95, 0x85, 0xe7, 0x9c, + 0x37, 0x20, 0xed, 0xe2, 0x18, 0x10, 0x0a, 0x79, 0x16, 0x25, 0x2a, 0xe1, 0x99, 0x9c, 0x1c, 0x9c, + 0x58, 0xb3, 0xf1, 0xd9, 0x9c, 0xfc, 0xef, 0x7c, 0x49, 0x53, 0xe9, 0xd3, 0x96, 0x18, 0xe0, 0xeb, + 0xc2, 0x1b, 0x94, 0x85, 0x87, 0x3a, 0x8c, 0xf6, 0x84, 0xf1, 0x0c, 0xd9, 0x0b, 0x2e, 0x55, 0xc6, + 0x52, 0x98, 0x0c, 0x4f, 0xac, 0x99, 0x13, 0xdc, 0x2d, 0x0b, 0xcf, 0xfe, 0xb2, 0xc6, 0x68, 0x1b, + 0xc5, 0x17, 0xc8, 0x51, 0x4c, 0xc4, 0xa0, 0x28, 0x3c, 0x99, 0x1c, 0x9a, 0x7e, 0xde, 0xea, 0xf7, + 0xa3, 0xff, 0x10, 0xd9, 0xcc, 0xc9, 0xb7, 0x57, 0x3f, 0x43, 0xa8, 0x93, 0x40, 0x40, 0x16, 0x42, + 0x65, 0xf1, 0xb2, 0x61, 0xd2, 0x4e, 0x04, 0x87, 0xc8, 0x56, 0x3c, 0xe7, 0x2b, 0x1e, 0x6f, 0x27, + 0xa3, 0x93, 0xe1, 0x6c, 0x7c, 0xf6, 0xe1, 0x9e, 0x06, 0xc9, 0x65, 0xcd, 0x7b, 0x98, 0x29, 0xb1, + 0x0d, 0xee, 0xd5, 0x26, 0xed, 0x06, 0xa6, 0xad, 0xb0, 0x36, 0x98, 0xf1, 0x08, 0x1e, 0x69, 0x83, + 0xaf, 0x74, 0x06, 0x1f, 0xd5, 0x18, 0x6d, 0xa3, 0xc7, 0x9f, 0xa0, 0xa3, 0x5b, 0xb2, 0xf8, 0x1e, + 0x1a, 0x2e, 0x61, 0x3b, 0xb1, 0x34, 0x8b, 0xea, 0x4f, 0xfc, 0x1a, 0x1a, 0x6d, 0xd8, 0x6a, 0x0d, + 0xe6, 0x7f, 0x38, 0xb4, 0x7a, 0x7c, 0x7c, 0xf0, 0xc0, 0x9a, 0xfe, 0x6a, 0x21, 0xbc, 0x3b, 0x7e, + 0xec, 0xa1, 0x91, 0x00, 0x16, 0x55, 0x22, 0x76, 0xe0, 0x94, 0x85, 0x37, 0xa2, 0x1a, 0xa0, 0x15, + 0x8e, 0xdf, 0x46, 0x77, 0x24, 0x88, 0x4d, 0x92, 0xc5, 0x46, 0xd3, 0x0e, 0xc6, 0x65, 0xe1, 0xdd, + 0x79, 0x5c, 0x41, 0xb4, 0x89, 0xe1, 0x39, 0x1a, 0x2b, 0x10, 0x69, 0x92, 0x31, 0xa5, 0x53, 0x87, + 0x26, 0xf5, 0xd5, 0xb2, 0xf0, 0xc6, 0x97, 0x1d, 0x4c, 0xfb, 0x39, 0xd3, 0x3f, 0x2c, 0x74, 0xb7, + 0xe9, 0xe8, 0x82, 0x0b, 0x85, 0xdf, 0x40, 0x87, 0xe6, 0x37, 0x1b, 0x3f, 0x81, 0x5d, 0x16, 0xde, + 0xa1, 0x99, 0x80, 0x41, 0xf1, 0x17, 0xc8, 0x36, 0x2b, 0x1b, 0xf2, 0x55, 0xe5, 0x2e, 0x38, 0xd5, + 0x73, 0xba, 0xa8, 0xb1, 0x17, 0x85, 0xf7, 0xfa, 0xee, 0x39, 0x92, 0x26, 0x4c, 0x5b, 0xb2, 0x2e, + 0x93, 0x73, 0xa1, 0x4c, 0x8f, 0xa3, 0xaa, 0x8c, 0x2e, 0x4f, 0x0d, 0xaa, 0x8d, 0xb0, 0x3c, 0x6f, + 0x68, 0x66, 0x8f, 0x9c, 0xca, 0xc8, 0x79, 0x07, 0xd3, 0x7e, 0xce, 0xf4, 0xf9, 0x01, 0x3a, 0x6a, + 0x8c, 0x3c, 0x5e, 0x25, 0x21, 0xe0, 0x9f, 0x90, 0xad, 0x2f, 0x3b, 0x62, 0x8a, 0x19, 0x37, 0xe3, + 0xb3, 0xf7, 0x7b, 0x8b, 0xd3, 0x1e, 0x28, 0xc9, 0x97, 0xb1, 0x06, 0x24, 0xd1, 0xd9, 0xdd, 0x6e, + 0x7e, 0x03, 0x8a, 0x75, 0x87, 0xd1, 0x61, 0xb4, 0x55, 0xc5, 0x9f, 0xa1, 0x71, 0x7d, 0x8a, 0x97, + 0xdb, 0x1c, 0xea, 0x36, 0xa7, 0x35, 0x65, 0x7c, 0xde, 0x85, 0x5e, 0xdc, 0x7e, 0xd2, 0x3e, 0x0d, + 0x7f, 0x8f, 0x1c, 0xa8, 0x1b, 0xd7, 0x27, 0xac, 0x37, 0xfc, 0x9d, 0x3d, 0x37, 0x3c, 0xb8, 0x5f, + 0x17, 0x73, 0x1a, 0x44, 0xd2, 0x4e, 0x0c, 0x5f, 0xa0, 0x91, 0x1e, 0xa7, 0x9c, 0x0c, 0x8d, 0xea, + 0xe9, 0x9e, 0xaa, 0xfa, 0x47, 0x04, 0x47, 0xb5, 0xf2, 0x48, 0xbf, 0x24, 0xad, 0x84, 0xa6, 0xbf, + 0x5b, 0xe8, 0xfe, 0xad, 0x29, 0x7f, 0x9d, 0x48, 0x85, 0x7f, 0xdc, 0x99, 0x34, 0xd9, 0x6f, 0xd2, + 0x9a, 0x6d, 0xe6, 0xdc, 0xde, 0x66, 0x83, 0xf4, 0xa6, 0xfc, 0x1d, 0x1a, 0x25, 0x0a, 0xd2, 0x66, + 0x36, 0xef, 0xee, 0xe9, 0xc2, 0xb4, 0xd7, 0xd9, 0xf8, 0x4a, 0x4b, 0xd0, 0x4a, 0x29, 0x20, 0xd7, + 0x37, 0xee, 0xe0, 0xe9, 0x8d, 0x3b, 0x78, 0x76, 0xe3, 0x0e, 0x7e, 0x29, 0x5d, 0xeb, 0xba, 0x74, + 0xad, 0xa7, 0xa5, 0x6b, 0x3d, 0x2b, 0x5d, 0xeb, 0xcf, 0xd2, 0xb5, 0x7e, 0x7b, 0xee, 0x0e, 0x7e, + 0xb0, 0x1b, 0xcd, 0xbf, 0x03, 0x00, 0x00, 0xff, 0xff, 0x03, 0x95, 0x92, 0xa5, 0xfa, 0x06, 0x00, + 0x00, } func (m *Endpoint) Marshal() (dAtA []byte, err error) { @@ -273,6 +274,13 @@ func (m *Endpoint) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + if m.NodeName != nil { + i -= len(*m.NodeName) + copy(dAtA[i:], *m.NodeName) + i = encodeVarintGenerated(dAtA, i, uint64(len(*m.NodeName))) + i-- + dAtA[i] = 0x32 + } if len(m.Topology) > 0 { keysForTopology := make([]string, 0, len(m.Topology)) for k := range m.Topology { @@ -594,6 +602,10 @@ func (m *Endpoint) Size() (n int) { n += mapEntrySize + 1 + sovGenerated(uint64(mapEntrySize)) } } + if m.NodeName != nil { + l = len(*m.NodeName) + n += 1 + l + sovGenerated(uint64(l)) + } return n } @@ -707,6 +719,7 @@ func (this *Endpoint) String() string { `Hostname:` + valueToStringGenerated(this.Hostname) + `,`, `TargetRef:` + strings.Replace(fmt.Sprintf("%v", this.TargetRef), "ObjectReference", "v1.ObjectReference", 1) + `,`, `Topology:` + mapStringForTopology + `,`, + `NodeName:` + valueToStringGenerated(this.NodeName) + `,`, `}`, }, "") return s @@ -1073,6 +1086,39 @@ func (m *Endpoint) Unmarshal(dAtA []byte) error { } m.Topology[mapkey] = mapvalue iNdEx = postIndex + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field NodeName", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthGenerated + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + s := string(dAtA[iNdEx:postIndex]) + m.NodeName = &s + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipGenerated(dAtA[iNdEx:]) diff --git a/staging/src/k8s.io/api/discovery/v1alpha1/generated.proto b/staging/src/k8s.io/api/discovery/v1alpha1/generated.proto index dc9a168b1f7..f85ea034b79 100644 --- a/staging/src/k8s.io/api/discovery/v1alpha1/generated.proto +++ b/staging/src/k8s.io/api/discovery/v1alpha1/generated.proto @@ -67,8 +67,14 @@ message Endpoint { // endpoint is located. This should match the corresponding node label. // * topology.kubernetes.io/region: the value indicates the region where the // endpoint is located. This should match the corresponding node label. + // This field will be deprecated in an upcoming release. // +optional map topology = 5; + + // nodeName represents the name of the Node hosting this endpoint. This can + // be used to determine endpoints local to a Node. + // +optional + optional string nodeName = 6; } // EndpointConditions represents the current condition of an endpoint. diff --git a/staging/src/k8s.io/api/discovery/v1alpha1/types.go b/staging/src/k8s.io/api/discovery/v1alpha1/types.go index a846cd7cacc..ca1dca3cc16 100644 --- a/staging/src/k8s.io/api/discovery/v1alpha1/types.go +++ b/staging/src/k8s.io/api/discovery/v1alpha1/types.go @@ -106,8 +106,13 @@ type Endpoint struct { // endpoint is located. This should match the corresponding node label. // * topology.kubernetes.io/region: the value indicates the region where the // endpoint is located. This should match the corresponding node label. + // This field will be deprecated in an upcoming release. // +optional Topology map[string]string `json:"topology,omitempty" protobuf:"bytes,5,opt,name=topology"` + // nodeName represents the name of the Node hosting this endpoint. This can + // be used to determine endpoints local to a Node. + // +optional + NodeName *string `json:"nodeName,omitempty" protobuf:"bytes,6,opt,name=nodeName"` } // EndpointConditions represents the current condition of an endpoint. diff --git a/staging/src/k8s.io/api/discovery/v1alpha1/types_swagger_doc_generated.go b/staging/src/k8s.io/api/discovery/v1alpha1/types_swagger_doc_generated.go index 6e8ada2d0c8..8012d36a2c1 100644 --- a/staging/src/k8s.io/api/discovery/v1alpha1/types_swagger_doc_generated.go +++ b/staging/src/k8s.io/api/discovery/v1alpha1/types_swagger_doc_generated.go @@ -33,7 +33,8 @@ var map_Endpoint = map[string]string{ "conditions": "conditions contains information about the current status of the endpoint.", "hostname": "hostname of this endpoint. This field may be used by consumers of endpoints to distinguish endpoints from each other (e.g. in DNS names). Multiple endpoints which use the same hostname should be considered fungible (e.g. multiple A values in DNS). Must be lowercase and pass DNS label (RFC 1123) validation.", "targetRef": "targetRef is a reference to a Kubernetes object that represents this endpoint.", - "topology": "topology contains arbitrary topology information associated with the endpoint. These key/value pairs must conform with the label format. https://kubernetes.io/docs/concepts/overview/working-with-objects/labels Topology may include a maximum of 16 key/value pairs. This includes, but is not limited to the following well known keys: * kubernetes.io/hostname: the value indicates the hostname of the node\n where the endpoint is located. This should match the corresponding\n node label.\n* topology.kubernetes.io/zone: the value indicates the zone where the\n endpoint is located. This should match the corresponding node label.\n* topology.kubernetes.io/region: the value indicates the region where the\n endpoint is located. This should match the corresponding node label.", + "topology": "topology contains arbitrary topology information associated with the endpoint. These key/value pairs must conform with the label format. https://kubernetes.io/docs/concepts/overview/working-with-objects/labels Topology may include a maximum of 16 key/value pairs. This includes, but is not limited to the following well known keys: * kubernetes.io/hostname: the value indicates the hostname of the node\n where the endpoint is located. This should match the corresponding\n node label.\n* topology.kubernetes.io/zone: the value indicates the zone where the\n endpoint is located. This should match the corresponding node label.\n* topology.kubernetes.io/region: the value indicates the region where the\n endpoint is located. This should match the corresponding node label.\nThis field will be deprecated in an upcoming release.", + "nodeName": "nodeName represents the name of the Node hosting this endpoint. This can be used to determine endpoints local to a Node.", } func (Endpoint) SwaggerDoc() map[string]string { diff --git a/staging/src/k8s.io/api/discovery/v1alpha1/zz_generated.deepcopy.go b/staging/src/k8s.io/api/discovery/v1alpha1/zz_generated.deepcopy.go index c29e1ac34d7..13e54d50071 100644 --- a/staging/src/k8s.io/api/discovery/v1alpha1/zz_generated.deepcopy.go +++ b/staging/src/k8s.io/api/discovery/v1alpha1/zz_generated.deepcopy.go @@ -51,6 +51,11 @@ func (in *Endpoint) DeepCopyInto(out *Endpoint) { (*out)[key] = val } } + if in.NodeName != nil { + in, out := &in.NodeName, &out.NodeName + *out = new(string) + **out = **in + } return } diff --git a/staging/src/k8s.io/api/discovery/v1beta1/generated.pb.go b/staging/src/k8s.io/api/discovery/v1beta1/generated.pb.go index d01f1351733..6caab402ca3 100644 --- a/staging/src/k8s.io/api/discovery/v1beta1/generated.pb.go +++ b/staging/src/k8s.io/api/discovery/v1beta1/generated.pb.go @@ -200,56 +200,57 @@ func init() { } var fileDescriptor_ece80bbc872d519b = []byte{ - // 784 bytes of a gzipped FileDescriptorProto + // 798 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, + 0x10, 0x8d, 0x27, 0x63, 0xc6, 0xee, 0xec, 0x88, 0xdd, 0x16, 0x87, 0x68, 0x58, 0xd9, 0xa3, 0x20, + 0x50, 0xc4, 0x68, 0x6d, 0x66, 0xb5, 0x42, 0x2b, 0x38, 0x8d, 0x61, 0x04, 0x48, 0xb0, 0x1b, 0xf5, + 0x46, 0x42, 0x42, 0x1c, 0xe8, 0xd8, 0xb5, 0x8e, 0x49, 0xec, 0xb6, 0xba, 0x3b, 0x91, 0x72, 0xe3, + 0x1f, 0xc0, 0x7f, 0x42, 0x42, 0x73, 0xdc, 0xe3, 0x9e, 0x2c, 0x62, 0xf8, 0x15, 0x7b, 0x42, 0xdd, + 0xfe, 0x4a, 0x08, 0x1f, 0xb9, 0x75, 0xbf, 0xaa, 0xf7, 0xaa, 0x5e, 0x75, 0x17, 0xba, 0x5d, 0x3c, + 0x15, 0x5e, 0xc2, 0xfc, 0xc5, 0x6a, 0x06, 0x3c, 0x03, 0x09, 0xc2, 0x5f, 0x43, 0x16, 0x31, 0xee, + 0xd7, 0x01, 0x9a, 0x27, 0x7e, 0x94, 0x88, 0x90, 0xad, 0x81, 0x6f, 0xfc, 0xf5, 0xf5, 0x0c, 0x24, + 0xbd, 0xf6, 0x63, 0xc8, 0x80, 0x53, 0x09, 0x91, 0x97, 0x73, 0x26, 0x19, 0x7e, 0x58, 0x65, 0x7b, + 0x34, 0x4f, 0xbc, 0x36, 0xdb, 0xab, 0xb3, 0x2f, 0x1e, 0xc5, 0x89, 0x9c, 0xaf, 0x66, 0x5e, 0xc8, + 0x52, 0x3f, 0x66, 0x31, 0xf3, 0x35, 0x69, 0xb6, 0x7a, 0xa9, 0x6f, 0xfa, 0xa2, 0x4f, 0x95, 0xd8, + 0xc5, 0x68, 0xa7, 0x74, 0xc8, 0x38, 0xf8, 0xeb, 0x83, 0x82, 0x17, 0x4f, 0xba, 0x9c, 0x94, 0x86, + 0xf3, 0x24, 0x53, 0xdd, 0xe5, 0x8b, 0x58, 0x01, 0xc2, 0x4f, 0x41, 0xd2, 0x7f, 0x62, 0xf9, 0xff, + 0xc6, 0xe2, 0xab, 0x4c, 0x26, 0x29, 0x1c, 0x10, 0x3e, 0xfe, 0x3f, 0x82, 0x08, 0xe7, 0x90, 0xd2, + 0xbf, 0xf3, 0x46, 0x7f, 0xf6, 0x91, 0x75, 0x9b, 0x45, 0x39, 0x4b, 0x32, 0x89, 0xaf, 0x90, 0x4d, + 0xa3, 0x88, 0x83, 0x10, 0x20, 0x86, 0xc6, 0x65, 0x7f, 0x6c, 0x07, 0xe7, 0x65, 0xe1, 0xda, 0x37, + 0x0d, 0x48, 0xba, 0x38, 0x8e, 0x10, 0x0a, 0x59, 0x16, 0x25, 0x32, 0x61, 0x99, 0x18, 0x9e, 0x5c, + 0x1a, 0xe3, 0xc1, 0xe3, 0x8f, 0xbc, 0xff, 0x1a, 0xaf, 0xd7, 0x14, 0xfa, 0xac, 0xe5, 0x05, 0xf8, + 0xae, 0x70, 0x7b, 0x65, 0xe1, 0xa2, 0x0e, 0x23, 0x3b, 0xba, 0x78, 0x8c, 0xac, 0x39, 0x13, 0x32, + 0xa3, 0x29, 0x0c, 0xfb, 0x97, 0xc6, 0xd8, 0x0e, 0xee, 0x95, 0x85, 0x6b, 0x7d, 0x59, 0x63, 0xa4, + 0x8d, 0xe2, 0x09, 0xb2, 0x25, 0xe5, 0x31, 0x48, 0x02, 0x2f, 0x87, 0xa7, 0xba, 0x9d, 0xf7, 0x76, + 0xdb, 0x51, 0x0f, 0xe4, 0xad, 0xaf, 0xbd, 0xe7, 0xb3, 0x1f, 0x21, 0x54, 0x49, 0xc0, 0x21, 0x0b, + 0xa1, 0x72, 0x38, 0x6d, 0x98, 0xa4, 0x13, 0xc1, 0x33, 0x64, 0x49, 0x96, 0xb3, 0x25, 0x8b, 0x37, + 0x43, 0xf3, 0xb2, 0x3f, 0x1e, 0x3c, 0x7e, 0x72, 0x9c, 0x3f, 0x6f, 0x5a, 0xd3, 0x6e, 0x33, 0xc9, + 0x37, 0xc1, 0xfd, 0xda, 0xa3, 0xd5, 0xc0, 0xa4, 0xd5, 0x55, 0xfe, 0x32, 0x16, 0xc1, 0x33, 0xe5, + 0xef, 0xad, 0xce, 0xdf, 0xb3, 0x1a, 0x23, 0x6d, 0xf4, 0xe2, 0x53, 0x74, 0xbe, 0x27, 0x8b, 0xef, + 0xa3, 0xfe, 0x02, 0x36, 0x43, 0x43, 0xb1, 0x88, 0x3a, 0xe2, 0x77, 0x90, 0xb9, 0xa6, 0xcb, 0x15, + 0xe8, 0xd7, 0xb0, 0x49, 0x75, 0xf9, 0xe4, 0xe4, 0xa9, 0x31, 0xfa, 0xd9, 0x40, 0xf8, 0x70, 0xfa, + 0xd8, 0x45, 0x26, 0x07, 0x1a, 0x55, 0x22, 0x56, 0x60, 0x97, 0x85, 0x6b, 0x12, 0x05, 0x90, 0x0a, + 0xc7, 0xef, 0xa3, 0x33, 0x01, 0x7c, 0x9d, 0x64, 0xb1, 0xd6, 0xb4, 0x82, 0x41, 0x59, 0xb8, 0x67, + 0x2f, 0x2a, 0x88, 0x34, 0x31, 0x7c, 0x8d, 0x06, 0x12, 0x78, 0x9a, 0x64, 0x54, 0xaa, 0xd4, 0xbe, + 0x4e, 0x7d, 0xbb, 0x2c, 0xdc, 0xc1, 0xb4, 0x83, 0xc9, 0x6e, 0xce, 0xe8, 0x37, 0x03, 0xdd, 0x6b, + 0x3a, 0x9a, 0x30, 0x2e, 0xf1, 0x43, 0x74, 0xaa, 0x5f, 0x59, 0xfb, 0x09, 0xac, 0xb2, 0x70, 0x4f, + 0xf5, 0x04, 0x34, 0x8a, 0xbf, 0x40, 0x96, 0xfe, 0xb0, 0x21, 0x5b, 0x56, 0xee, 0x82, 0x2b, 0x35, + 0xa7, 0x49, 0x8d, 0xbd, 0x29, 0xdc, 0x77, 0x0f, 0x97, 0xd1, 0x6b, 0xc2, 0xa4, 0x25, 0xab, 0x32, + 0x39, 0xe3, 0x52, 0xf7, 0x68, 0x56, 0x65, 0x54, 0x79, 0xa2, 0x51, 0x65, 0x84, 0xe6, 0x79, 0x43, + 0xd3, 0xdf, 0xc8, 0xae, 0x8c, 0xdc, 0x74, 0x30, 0xd9, 0xcd, 0x19, 0x6d, 0x4f, 0xd0, 0x79, 0x63, + 0xe4, 0xc5, 0x32, 0x09, 0x01, 0xff, 0x80, 0x2c, 0xb5, 0xd7, 0x11, 0x95, 0x54, 0xbb, 0xd9, 0xdf, + 0x8b, 0x76, 0x3d, 0xbd, 0x7c, 0x11, 0x2b, 0x40, 0x78, 0x2a, 0xbb, 0xfb, 0x9a, 0xdf, 0x80, 0xa4, + 0xdd, 0x5e, 0x74, 0x18, 0x69, 0x55, 0xf1, 0xe7, 0x68, 0x50, 0x2f, 0xe2, 0x74, 0x93, 0x43, 0xdd, + 0xe6, 0xa8, 0xa6, 0x0c, 0x6e, 0xba, 0xd0, 0x9b, 0xfd, 0x2b, 0xd9, 0xa5, 0xe1, 0x6f, 0x91, 0x0d, + 0x75, 0xe3, 0x6a, 0x81, 0xd5, 0x07, 0xff, 0xe0, 0xb8, 0x0f, 0x1e, 0x3c, 0xa8, 0x6b, 0xd9, 0x0d, + 0x22, 0x48, 0xa7, 0x85, 0x9f, 0x23, 0x53, 0x4d, 0x53, 0x0c, 0xfb, 0x5a, 0xf4, 0xc3, 0xe3, 0x44, + 0xd5, 0x33, 0x04, 0xe7, 0xb5, 0xb0, 0xa9, 0x6e, 0x82, 0x54, 0x3a, 0xa3, 0x5f, 0x0d, 0xf4, 0x60, + 0x6f, 0xc6, 0x5f, 0x27, 0x42, 0xe2, 0xef, 0x0f, 0xe6, 0xec, 0x1d, 0x37, 0x67, 0xc5, 0xd6, 0x53, + 0x6e, 0x37, 0xb3, 0x41, 0x76, 0x66, 0x3c, 0x41, 0x66, 0x22, 0x21, 0x6d, 0x26, 0x73, 0x75, 0x9c, + 0x09, 0xdd, 0x5d, 0xe7, 0xe2, 0x2b, 0xa5, 0x40, 0x2a, 0xa1, 0xe0, 0xd1, 0xdd, 0xd6, 0xe9, 0xbd, + 0xda, 0x3a, 0xbd, 0xd7, 0x5b, 0xa7, 0xf7, 0x53, 0xe9, 0x18, 0x77, 0xa5, 0x63, 0xbc, 0x2a, 0x1d, + 0xe3, 0x75, 0xe9, 0x18, 0xbf, 0x97, 0x8e, 0xf1, 0xcb, 0x1f, 0x4e, 0xef, 0xbb, 0xb3, 0x5a, 0xf2, + 0xaf, 0x00, 0x00, 0x00, 0xff, 0xff, 0xa6, 0x35, 0xe6, 0xf5, 0xf2, 0x06, 0x00, 0x00, } func (m *Endpoint) Marshal() (dAtA []byte, err error) { @@ -272,6 +273,13 @@ func (m *Endpoint) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + if m.NodeName != nil { + i -= len(*m.NodeName) + copy(dAtA[i:], *m.NodeName) + i = encodeVarintGenerated(dAtA, i, uint64(len(*m.NodeName))) + i-- + dAtA[i] = 0x32 + } if len(m.Topology) > 0 { keysForTopology := make([]string, 0, len(m.Topology)) for k := range m.Topology { @@ -593,6 +601,10 @@ func (m *Endpoint) Size() (n int) { n += mapEntrySize + 1 + sovGenerated(uint64(mapEntrySize)) } } + if m.NodeName != nil { + l = len(*m.NodeName) + n += 1 + l + sovGenerated(uint64(l)) + } return n } @@ -706,6 +718,7 @@ func (this *Endpoint) String() string { `Hostname:` + valueToStringGenerated(this.Hostname) + `,`, `TargetRef:` + strings.Replace(fmt.Sprintf("%v", this.TargetRef), "ObjectReference", "v1.ObjectReference", 1) + `,`, `Topology:` + mapStringForTopology + `,`, + `NodeName:` + valueToStringGenerated(this.NodeName) + `,`, `}`, }, "") return s @@ -1072,6 +1085,39 @@ func (m *Endpoint) Unmarshal(dAtA []byte) error { } m.Topology[mapkey] = mapvalue iNdEx = postIndex + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field NodeName", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthGenerated + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + s := string(dAtA[iNdEx:postIndex]) + m.NodeName = &s + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipGenerated(dAtA[iNdEx:]) diff --git a/staging/src/k8s.io/api/discovery/v1beta1/generated.proto b/staging/src/k8s.io/api/discovery/v1beta1/generated.proto index cdf1a21999b..6c67870248a 100644 --- a/staging/src/k8s.io/api/discovery/v1beta1/generated.proto +++ b/staging/src/k8s.io/api/discovery/v1beta1/generated.proto @@ -67,8 +67,14 @@ message Endpoint { // endpoint is located. This should match the corresponding node label. // * topology.kubernetes.io/region: the value indicates the region where the // endpoint is located. This should match the corresponding node label. + // this field will be deprecated in an upcoming release. // +optional map topology = 5; + + // nodeName represents the name of the Node hosting this endpoint. This can + // be used to determine endpoints local to a Node. + // +optional + optional string nodeName = 6; } // EndpointConditions represents the current condition of an endpoint. diff --git a/staging/src/k8s.io/api/discovery/v1beta1/types.go b/staging/src/k8s.io/api/discovery/v1beta1/types.go index d65a079876a..56b41a5ec43 100644 --- a/staging/src/k8s.io/api/discovery/v1beta1/types.go +++ b/staging/src/k8s.io/api/discovery/v1beta1/types.go @@ -60,12 +60,6 @@ type EndpointSlice struct { type AddressType string const ( - // AddressTypeIP represents an IP Address. - // This address type has been deprecated and has been replaced by the IPv4 - // and IPv6 adddress types. New resources with this address type will be - // considered invalid. This will be fully removed in 1.18. - // +deprecated - AddressTypeIP = AddressType("IP") // AddressTypeIPv4 represents an IPv4 Address. AddressTypeIPv4 = AddressType(v1.IPv4Protocol) // AddressTypeIPv6 represents an IPv6 Address. @@ -108,8 +102,13 @@ type Endpoint struct { // endpoint is located. This should match the corresponding node label. // * topology.kubernetes.io/region: the value indicates the region where the // endpoint is located. This should match the corresponding node label. + // this field will be deprecated in an upcoming release. // +optional Topology map[string]string `json:"topology,omitempty" protobuf:"bytes,5,opt,name=topology"` + // nodeName represents the name of the Node hosting this endpoint. This can + // be used to determine endpoints local to a Node. + // +optional + NodeName *string `json:"nodeName,omitempty" protobuf:"bytes,6,opt,name=nodeName"` } // EndpointConditions represents the current condition of an endpoint. diff --git a/staging/src/k8s.io/api/discovery/v1beta1/types_swagger_doc_generated.go b/staging/src/k8s.io/api/discovery/v1beta1/types_swagger_doc_generated.go index 07033c48497..1f7034bda58 100644 --- a/staging/src/k8s.io/api/discovery/v1beta1/types_swagger_doc_generated.go +++ b/staging/src/k8s.io/api/discovery/v1beta1/types_swagger_doc_generated.go @@ -33,7 +33,8 @@ var map_Endpoint = map[string]string{ "conditions": "conditions contains information about the current status of the endpoint.", "hostname": "hostname of this endpoint. This field may be used by consumers of endpoints to distinguish endpoints from each other (e.g. in DNS names). Multiple endpoints which use the same hostname should be considered fungible (e.g. multiple A values in DNS). Must be lowercase and pass DNS Label (RFC 1123) validation.", "targetRef": "targetRef is a reference to a Kubernetes object that represents this endpoint.", - "topology": "topology contains arbitrary topology information associated with the endpoint. These key/value pairs must conform with the label format. https://kubernetes.io/docs/concepts/overview/working-with-objects/labels Topology may include a maximum of 16 key/value pairs. This includes, but is not limited to the following well known keys: * kubernetes.io/hostname: the value indicates the hostname of the node\n where the endpoint is located. This should match the corresponding\n node label.\n* topology.kubernetes.io/zone: the value indicates the zone where the\n endpoint is located. This should match the corresponding node label.\n* topology.kubernetes.io/region: the value indicates the region where the\n endpoint is located. This should match the corresponding node label.", + "topology": "topology contains arbitrary topology information associated with the endpoint. These key/value pairs must conform with the label format. https://kubernetes.io/docs/concepts/overview/working-with-objects/labels Topology may include a maximum of 16 key/value pairs. This includes, but is not limited to the following well known keys: * kubernetes.io/hostname: the value indicates the hostname of the node\n where the endpoint is located. This should match the corresponding\n node label.\n* topology.kubernetes.io/zone: the value indicates the zone where the\n endpoint is located. This should match the corresponding node label.\n* topology.kubernetes.io/region: the value indicates the region where the\n endpoint is located. This should match the corresponding node label.\nthis field will be deprecated in an upcoming release.", + "nodeName": "nodeName represents the name of the Node hosting this endpoint. This can be used to determine endpoints local to a Node.", } func (Endpoint) SwaggerDoc() map[string]string { diff --git a/staging/src/k8s.io/api/discovery/v1beta1/zz_generated.deepcopy.go b/staging/src/k8s.io/api/discovery/v1beta1/zz_generated.deepcopy.go index 65c3a017311..7076553d291 100644 --- a/staging/src/k8s.io/api/discovery/v1beta1/zz_generated.deepcopy.go +++ b/staging/src/k8s.io/api/discovery/v1beta1/zz_generated.deepcopy.go @@ -51,6 +51,11 @@ func (in *Endpoint) DeepCopyInto(out *Endpoint) { (*out)[key] = val } } + if in.NodeName != nil { + in, out := &in.NodeName, &out.NodeName + *out = new(string) + **out = **in + } return } From e9573eef4c918043b497068e5d018f36c9b65f6b Mon Sep 17 00:00:00 2001 From: Rob Scott Date: Tue, 10 Nov 2020 17:49:53 -0800 Subject: [PATCH 2/8] Adding EndpointSliceNodeName feature gate --- pkg/features/kube_features.go | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/pkg/features/kube_features.go b/pkg/features/kube_features.go index 624c7e1c807..0a8f8259b04 100644 --- a/pkg/features/kube_features.go +++ b/pkg/features/kube_features.go @@ -667,6 +667,12 @@ const ( // Enable Terminating condition in Endpoint Slices. EndpointSliceTerminatingCondition featuregate.Feature = "EndpointSliceTerminatingCondition" + // owner: @robscott + // alpha: v1.20 + // + // Enable NodeName field on Endpoint Slices. + EndpointSliceNodeName featuregate.Feature = "EndpointSliceNodeName" + // owner: @derekwaynecarr // alpha: v1.20 // @@ -765,6 +771,7 @@ var defaultKubernetesFeatureGates = map[featuregate.Feature]featuregate.FeatureS EndpointSlice: {Default: true, PreRelease: featuregate.Beta}, EndpointSliceProxying: {Default: true, PreRelease: featuregate.Beta}, EndpointSliceTerminatingCondition: {Default: false, PreRelease: featuregate.Alpha}, + EndpointSliceNodeName: {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.23 From d9854387721d2bf18b33188b185763d023fa3bd9 Mon Sep 17 00:00:00 2001 From: Rob Scott Date: Tue, 10 Nov 2020 17:50:39 -0800 Subject: [PATCH 3/8] Updating EndpointSlice controllers to support NodeName field --- .../endpointslice/reconciler_test.go | 2 +- pkg/controller/endpointslice/utils.go | 4 + pkg/controller/endpointslice/utils_test.go | 21 +++++ pkg/controller/endpointslicemirroring/BUILD | 5 ++ .../endpointslicemirroring/utils.go | 5 ++ .../endpointslicemirroring/utils_test.go | 84 +++++++++++++++++++ 6 files changed, 120 insertions(+), 1 deletion(-) diff --git a/pkg/controller/endpointslice/reconciler_test.go b/pkg/controller/endpointslice/reconciler_test.go index a4d37d7a2f5..95e88b2fa5f 100644 --- a/pkg/controller/endpointslice/reconciler_test.go +++ b/pkg/controller/endpointslice/reconciler_test.go @@ -917,7 +917,7 @@ func TestReconcileEndpointSlicesReplaceDeprecated(t *testing.T) { namespace := "test" svc, endpointMeta := newServiceAndEndpointMeta("foo", namespace) - endpointMeta.AddressType = discovery.AddressTypeIP + endpointMeta.AddressType = discovery.AddressType("IP") existingSlices := []*discovery.EndpointSlice{} pods := []*corev1.Pod{} diff --git a/pkg/controller/endpointslice/utils.go b/pkg/controller/endpointslice/utils.go index 29b52c127b5..d3738cc7af4 100644 --- a/pkg/controller/endpointslice/utils.go +++ b/pkg/controller/endpointslice/utils.go @@ -87,6 +87,10 @@ func podToEndpoint(pod *corev1.Pod, node *corev1.Node, service *corev1.Service, ep.Conditions.Terminating = &terminating } + if pod.Spec.NodeName != "" && utilfeature.DefaultFeatureGate.Enabled(features.EndpointSliceNodeName) { + ep.NodeName = &pod.Spec.NodeName + } + if endpointutil.ShouldSetHostname(pod, service) { ep.Hostname = &pod.Spec.Hostname } diff --git a/pkg/controller/endpointslice/utils_test.go b/pkg/controller/endpointslice/utils_test.go index 68f654f9fc4..5c61dc3946f 100644 --- a/pkg/controller/endpointslice/utils_test.go +++ b/pkg/controller/endpointslice/utils_test.go @@ -252,6 +252,7 @@ func TestPodToEndpoint(t *testing.T) { expectedEndpoint discovery.Endpoint publishNotReadyAddresses bool terminatingGateEnabled bool + nodeNameGateEnabled bool }{ { name: "Ready pod", @@ -321,6 +322,25 @@ func TestPodToEndpoint(t *testing.T) { }, }, }, + { + name: "Ready pod + node name gate enabled", + pod: readyPod, + svc: &svc, + nodeNameGateEnabled: true, + expectedEndpoint: discovery.Endpoint{ + Addresses: []string{"1.2.3.5"}, + Conditions: discovery.EndpointConditions{Ready: utilpointer.BoolPtr(true)}, + Topology: map[string]string{"kubernetes.io/hostname": "node-1"}, + NodeName: utilpointer.StringPtr("node-1"), + TargetRef: &v1.ObjectReference{ + Kind: "Pod", + Namespace: ns, + Name: readyPod.Name, + UID: readyPod.UID, + ResourceVersion: readyPod.ResourceVersion, + }, + }, + }, { name: "Ready pod + node labels", pod: readyPod, @@ -499,6 +519,7 @@ func TestPodToEndpoint(t *testing.T) { for _, testCase := range testCases { t.Run(testCase.name, func(t *testing.T) { defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.EndpointSliceTerminatingCondition, testCase.terminatingGateEnabled)() + defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.EndpointSliceNodeName, testCase.nodeNameGateEnabled)() endpoint := podToEndpoint(testCase.pod, testCase.node, testCase.svc, discovery.AddressTypeIPv4) if !reflect.DeepEqual(testCase.expectedEndpoint, endpoint) { diff --git a/pkg/controller/endpointslicemirroring/BUILD b/pkg/controller/endpointslicemirroring/BUILD index 41919a6f2d7..ede6890250e 100644 --- a/pkg/controller/endpointslicemirroring/BUILD +++ b/pkg/controller/endpointslicemirroring/BUILD @@ -18,6 +18,7 @@ go_library( "//pkg/controller:go_default_library", "//pkg/controller/endpointslicemirroring/metrics:go_default_library", "//pkg/controller/util/endpoint:go_default_library", + "//pkg/features:go_default_library", "//staging/src/k8s.io/api/core/v1:go_default_library", "//staging/src/k8s.io/api/discovery/v1beta1:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/api/equality:go_default_library", @@ -28,6 +29,7 @@ go_library( "//staging/src/k8s.io/apimachinery/pkg/types:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/util/runtime:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/util/wait:go_default_library", + "//staging/src/k8s.io/apiserver/pkg/util/feature:go_default_library", "//staging/src/k8s.io/client-go/informers/core/v1:go_default_library", "//staging/src/k8s.io/client-go/informers/discovery/v1beta1:go_default_library", "//staging/src/k8s.io/client-go/kubernetes:go_default_library", @@ -58,6 +60,7 @@ go_test( deps = [ "//pkg/controller:go_default_library", "//pkg/controller/endpointslicemirroring/metrics:go_default_library", + "//pkg/features:go_default_library", "//staging/src/k8s.io/api/core/v1:go_default_library", "//staging/src/k8s.io/api/discovery/v1beta1:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", @@ -66,6 +69,7 @@ go_test( "//staging/src/k8s.io/apimachinery/pkg/types:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/util/rand:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/util/wait:go_default_library", + "//staging/src/k8s.io/apiserver/pkg/util/feature:go_default_library", "//staging/src/k8s.io/client-go/informers:go_default_library", "//staging/src/k8s.io/client-go/kubernetes/fake:go_default_library", "//staging/src/k8s.io/client-go/kubernetes/scheme:go_default_library", @@ -73,6 +77,7 @@ go_test( "//staging/src/k8s.io/client-go/tools/cache:go_default_library", "//staging/src/k8s.io/client-go/tools/leaderelection/resourcelock:go_default_library", "//staging/src/k8s.io/client-go/tools/record:go_default_library", + "//staging/src/k8s.io/component-base/featuregate/testing:go_default_library", "//staging/src/k8s.io/component-base/metrics/testutil:go_default_library", "//vendor/github.com/stretchr/testify/assert:go_default_library", "//vendor/k8s.io/utils/pointer:go_default_library", diff --git a/pkg/controller/endpointslicemirroring/utils.go b/pkg/controller/endpointslicemirroring/utils.go index 66ddbf8eee9..3eb555f263d 100644 --- a/pkg/controller/endpointslicemirroring/utils.go +++ b/pkg/controller/endpointslicemirroring/utils.go @@ -27,10 +27,12 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime/schema" utilruntime "k8s.io/apimachinery/pkg/util/runtime" + utilfeature "k8s.io/apiserver/pkg/util/feature" "k8s.io/client-go/tools/cache" "k8s.io/client-go/tools/leaderelection/resourcelock" "k8s.io/kubernetes/pkg/apis/discovery/validation" endpointutil "k8s.io/kubernetes/pkg/controller/util/endpoint" + "k8s.io/kubernetes/pkg/features" ) // addrTypePortMapKey is used to uniquely identify groups of endpoint ports and @@ -138,6 +140,9 @@ func addressToEndpoint(address corev1.EndpointAddress, ready bool) *discovery.En endpoint.Topology = map[string]string{ "kubernetes.io/hostname": *address.NodeName, } + if utilfeature.DefaultFeatureGate.Enabled(features.EndpointSliceNodeName) { + endpoint.NodeName = address.NodeName + } } if address.Hostname != "" { endpoint.Hostname = &address.Hostname diff --git a/pkg/controller/endpointslicemirroring/utils_test.go b/pkg/controller/endpointslicemirroring/utils_test.go index 9944f191edf..3b6bd5cf4be 100644 --- a/pkg/controller/endpointslicemirroring/utils_test.go +++ b/pkg/controller/endpointslicemirroring/utils_test.go @@ -27,8 +27,12 @@ import ( "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/util/rand" + utilfeature "k8s.io/apiserver/pkg/util/feature" "k8s.io/client-go/kubernetes/fake" k8stesting "k8s.io/client-go/testing" + featuregatetesting "k8s.io/component-base/featuregate/testing" + "k8s.io/kubernetes/pkg/features" + utilpointer "k8s.io/utils/pointer" ) func TestNewEndpointSlice(t *testing.T) { @@ -76,6 +80,86 @@ func TestNewEndpointSlice(t *testing.T) { } } +func TestAddressToEndpoint(t *testing.T) { + testCases := []struct { + name string + epAddress v1.EndpointAddress + expectedEndpoint discovery.Endpoint + ready bool + nodeNameGateEnabled bool + }{{ + name: "simple + gate enabled", + epAddress: v1.EndpointAddress{ + IP: "10.1.2.3", + Hostname: "foo", + NodeName: utilpointer.StringPtr("node-abc"), + TargetRef: &v1.ObjectReference{ + APIVersion: "v1", + Kind: "Pod", + Namespace: "default", + Name: "foo", + }, + }, + ready: true, + nodeNameGateEnabled: true, + expectedEndpoint: discovery.Endpoint{ + Addresses: []string{"10.1.2.3"}, + Hostname: utilpointer.StringPtr("foo"), + Conditions: discovery.EndpointConditions{ + Ready: utilpointer.BoolPtr(true), + }, + Topology: map[string]string{ + "kubernetes.io/hostname": "node-abc", + }, + TargetRef: &v1.ObjectReference{ + APIVersion: "v1", + Kind: "Pod", + Namespace: "default", + Name: "foo", + }, + NodeName: utilpointer.StringPtr("node-abc"), + }, + }, { + name: "simple + gate disabled", + epAddress: v1.EndpointAddress{ + IP: "10.1.2.3", + Hostname: "foo", + NodeName: utilpointer.StringPtr("node-abc"), + TargetRef: &v1.ObjectReference{ + APIVersion: "v1", + Kind: "Pod", + Namespace: "default", + Name: "foo", + }, + }, + ready: true, + nodeNameGateEnabled: false, + expectedEndpoint: discovery.Endpoint{ + Addresses: []string{"10.1.2.3"}, + Hostname: utilpointer.StringPtr("foo"), + Conditions: discovery.EndpointConditions{ + Ready: utilpointer.BoolPtr(true), + }, + Topology: map[string]string{ + "kubernetes.io/hostname": "node-abc", + }, + TargetRef: &v1.ObjectReference{ + APIVersion: "v1", + Kind: "Pod", + Namespace: "default", + Name: "foo", + }, + }, + }} + + for _, tc := range testCases { + defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.EndpointSliceNodeName, tc.nodeNameGateEnabled)() + + ep := addressToEndpoint(tc.epAddress, tc.ready) + assert.EqualValues(t, tc.expectedEndpoint, *ep) + } +} + // Test helpers func newClientset() *fake.Clientset { From 877ad98c5349d8be1474b4be92bbf7fb620f4f49 Mon Sep 17 00:00:00 2001 From: Rob Scott Date: Tue, 10 Nov 2020 17:51:02 -0800 Subject: [PATCH 4/8] Updating ControlPlane to support NodeName field --- pkg/controlplane/reconcilers/BUILD | 2 ++ .../reconcilers/endpointsadapter.go | 21 ++++++++++++------- .../reconcilers/endpointsadapter_test.go | 2 +- 3 files changed, 17 insertions(+), 8 deletions(-) diff --git a/pkg/controlplane/reconcilers/BUILD b/pkg/controlplane/reconcilers/BUILD index 55146b8b1fb..6e108dfb3fc 100644 --- a/pkg/controlplane/reconcilers/BUILD +++ b/pkg/controlplane/reconcilers/BUILD @@ -14,6 +14,7 @@ go_library( visibility = ["//visibility:public"], deps = [ "//pkg/api/v1/endpoints: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", @@ -23,6 +24,7 @@ go_library( "//staging/src/k8s.io/apiserver/pkg/endpoints/request:go_default_library", "//staging/src/k8s.io/apiserver/pkg/registry/rest:go_default_library", "//staging/src/k8s.io/apiserver/pkg/storage:go_default_library", + "//staging/src/k8s.io/apiserver/pkg/util/feature:go_default_library", "//staging/src/k8s.io/client-go/kubernetes/typed/core/v1:go_default_library", "//staging/src/k8s.io/client-go/kubernetes/typed/discovery/v1beta1:go_default_library", "//staging/src/k8s.io/client-go/util/retry:go_default_library", diff --git a/pkg/controlplane/reconcilers/endpointsadapter.go b/pkg/controlplane/reconcilers/endpointsadapter.go index 54e74c15bae..99d8f6b62a5 100644 --- a/pkg/controlplane/reconcilers/endpointsadapter.go +++ b/pkg/controlplane/reconcilers/endpointsadapter.go @@ -23,8 +23,10 @@ import ( apiequality "k8s.io/apimachinery/pkg/api/equality" "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + utilfeature "k8s.io/apiserver/pkg/util/feature" corev1client "k8s.io/client-go/kubernetes/typed/core/v1" discoveryclient "k8s.io/client-go/kubernetes/typed/discovery/v1beta1" + "k8s.io/kubernetes/pkg/features" utilnet "k8s.io/utils/net" ) @@ -166,17 +168,22 @@ func getEndpointsFromAddresses(addresses []corev1.EndpointAddress, addressType d // endpointFromAddress generates an Endpoint from an EndpointAddress resource. func endpointFromAddress(address corev1.EndpointAddress, ready bool) discovery.Endpoint { - topology := map[string]string{} - if address.NodeName != nil { - topology["kubernetes.io/hostname"] = *address.NodeName - } - - return discovery.Endpoint{ + ep := discovery.Endpoint{ Addresses: []string{address.IP}, Conditions: discovery.EndpointConditions{Ready: &ready}, TargetRef: address.TargetRef, - Topology: topology, } + + if address.NodeName != nil { + ep.Topology = map[string]string{ + "kubernetes.io/hostname": *address.NodeName, + } + if utilfeature.DefaultFeatureGate.Enabled(features.EndpointSliceNodeName) { + ep.NodeName = address.NodeName + } + } + + return ep } // allAddressesIPv6 returns true if all provided addresses are IPv6. diff --git a/pkg/controlplane/reconcilers/endpointsadapter_test.go b/pkg/controlplane/reconcilers/endpointsadapter_test.go index d8febe69a3e..13a34aa5ffb 100644 --- a/pkg/controlplane/reconcilers/endpointsadapter_test.go +++ b/pkg/controlplane/reconcilers/endpointsadapter_test.go @@ -232,7 +232,7 @@ func TestEndpointsAdapterUpdate(t *testing.T) { // with one that has an IPv4 address type. endpoints4, _ := generateEndpointsAndSlice("foo", "testing", []int{80}, []string{"10.1.2.7", "10.1.2.8"}) _, epSlice4IP := generateEndpointsAndSlice("foo", "testing", []int{80}, []string{"10.1.2.7", "10.1.2.8"}) - epSlice4IP.AddressType = discovery.AddressTypeIP + epSlice4IP.AddressType = discovery.AddressType("IP") _, epSlice4IPv4 := generateEndpointsAndSlice("foo", "testing", []int{80}, []string{"10.1.2.7", "10.1.2.8"}) testCases := map[string]struct { From 506861c0a045e4f387148c5d67cac7c80f1887f6 Mon Sep 17 00:00:00 2001 From: Rob Scott Date: Tue, 10 Nov 2020 17:51:32 -0800 Subject: [PATCH 5/8] Removing "IP" from supported EndpointSlice address types in kube-proxy --- pkg/proxy/endpoints.go | 1 - 1 file changed, 1 deletion(-) diff --git a/pkg/proxy/endpoints.go b/pkg/proxy/endpoints.go index fdbe1315be2..0de805dc2c1 100644 --- a/pkg/proxy/endpoints.go +++ b/pkg/proxy/endpoints.go @@ -36,7 +36,6 @@ import ( ) var supportedEndpointSliceAddressTypes = sets.NewString( - string(discovery.AddressTypeIP), // IP is a deprecated address type string(discovery.AddressTypeIPv4), string(discovery.AddressTypeIPv6), ) From ff46573692b46113e6ed30d3a3eba7b00457a38d Mon Sep 17 00:00:00 2001 From: Rob Scott Date: Wed, 11 Nov 2020 10:29:43 -0800 Subject: [PATCH 6/8] Cleaning up EndpointSlice update validation tests --- .../discovery/validation/validation_test.go | 54 +++++++++---------- 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/pkg/apis/discovery/validation/validation_test.go b/pkg/apis/discovery/validation/validation_test.go index b54c7fcc494..6457da08499 100644 --- a/pkg/apis/discovery/validation/validation_test.go +++ b/pkg/apis/discovery/validation/validation_test.go @@ -596,22 +596,22 @@ func TestValidateEndpointSliceUpdate(t *testing.T) { testCases := map[string]struct { expectedErrors int nodeNameGateEnabled bool - newEndpointSlice *discovery.EndpointSlice oldEndpointSlice *discovery.EndpointSlice + newEndpointSlice *discovery.EndpointSlice }{ "valid and identical slices": { - newEndpointSlice: &discovery.EndpointSlice{ + oldEndpointSlice: &discovery.EndpointSlice{ ObjectMeta: standardMeta, AddressType: discovery.AddressTypeIPv6, }, - oldEndpointSlice: &discovery.EndpointSlice{ + newEndpointSlice: &discovery.EndpointSlice{ ObjectMeta: standardMeta, AddressType: discovery.AddressTypeIPv6, }, expectedErrors: 0, }, "node name set before + after, gate disabled": { - newEndpointSlice: &discovery.EndpointSlice{ + oldEndpointSlice: &discovery.EndpointSlice{ ObjectMeta: standardMeta, AddressType: discovery.AddressTypeIPv4, Endpoints: []discovery.Endpoint{{ @@ -619,7 +619,7 @@ func TestValidateEndpointSliceUpdate(t *testing.T) { NodeName: utilpointer.StringPtr("foo"), }}, }, - oldEndpointSlice: &discovery.EndpointSlice{ + newEndpointSlice: &discovery.EndpointSlice{ ObjectMeta: standardMeta, AddressType: discovery.AddressTypeIPv4, Endpoints: []discovery.Endpoint{{ @@ -631,14 +631,14 @@ func TestValidateEndpointSliceUpdate(t *testing.T) { }, "node name set after, gate enabled": { nodeNameGateEnabled: true, - newEndpointSlice: &discovery.EndpointSlice{ + oldEndpointSlice: &discovery.EndpointSlice{ ObjectMeta: standardMeta, AddressType: discovery.AddressTypeIPv4, Endpoints: []discovery.Endpoint{{ Addresses: []string{"10.1.2.3"}, }}, }, - oldEndpointSlice: &discovery.EndpointSlice{ + newEndpointSlice: &discovery.EndpointSlice{ ObjectMeta: standardMeta, AddressType: discovery.AddressTypeIPv4, Endpoints: []discovery.Endpoint{{ @@ -652,6 +652,13 @@ func TestValidateEndpointSliceUpdate(t *testing.T) { // expected errors "invalide node name set after, gate enabled": { nodeNameGateEnabled: true, + oldEndpointSlice: &discovery.EndpointSlice{ + ObjectMeta: standardMeta, + AddressType: discovery.AddressTypeIPv4, + Endpoints: []discovery.Endpoint{{ + Addresses: []string{"10.1.2.3"}, + }}, + }, newEndpointSlice: &discovery.EndpointSlice{ ObjectMeta: standardMeta, AddressType: discovery.AddressTypeIPv4, @@ -660,25 +667,18 @@ func TestValidateEndpointSliceUpdate(t *testing.T) { NodeName: utilpointer.StringPtr("INVALID foo"), }}, }, - oldEndpointSlice: &discovery.EndpointSlice{ - ObjectMeta: standardMeta, - AddressType: discovery.AddressTypeIPv4, - Endpoints: []discovery.Endpoint{{ - Addresses: []string{"10.1.2.3"}, - }}, - }, expectedErrors: 1, }, "node name set after, gate disabled": { nodeNameGateEnabled: false, - newEndpointSlice: &discovery.EndpointSlice{ + oldEndpointSlice: &discovery.EndpointSlice{ ObjectMeta: standardMeta, AddressType: discovery.AddressTypeIPv4, Endpoints: []discovery.Endpoint{{ Addresses: []string{"10.1.2.3"}, }}, }, - oldEndpointSlice: &discovery.EndpointSlice{ + newEndpointSlice: &discovery.EndpointSlice{ ObjectMeta: standardMeta, AddressType: discovery.AddressTypeIPv4, Endpoints: []discovery.Endpoint{{ @@ -686,31 +686,35 @@ func TestValidateEndpointSliceUpdate(t *testing.T) { NodeName: utilpointer.StringPtr("foo"), }}, }, - expectedErrors: 0, + expectedErrors: 1, }, "deprecated address type": { expectedErrors: 1, - newEndpointSlice: &discovery.EndpointSlice{ + oldEndpointSlice: &discovery.EndpointSlice{ ObjectMeta: standardMeta, AddressType: discovery.AddressType("IP"), }, - oldEndpointSlice: &discovery.EndpointSlice{ + newEndpointSlice: &discovery.EndpointSlice{ ObjectMeta: standardMeta, AddressType: discovery.AddressType("IP"), }, }, "valid and identical slices with different address types": { - newEndpointSlice: &discovery.EndpointSlice{ - ObjectMeta: standardMeta, - AddressType: discovery.AddressTypeIPv4, - }, oldEndpointSlice: &discovery.EndpointSlice{ ObjectMeta: standardMeta, AddressType: discovery.AddressType("other"), }, + newEndpointSlice: &discovery.EndpointSlice{ + ObjectMeta: standardMeta, + AddressType: discovery.AddressTypeIPv4, + }, expectedErrors: 1, }, "invalid slices with valid address types": { + oldEndpointSlice: &discovery.EndpointSlice{ + ObjectMeta: standardMeta, + AddressType: discovery.AddressTypeIPv4, + }, newEndpointSlice: &discovery.EndpointSlice{ ObjectMeta: standardMeta, AddressType: discovery.AddressTypeIPv4, @@ -719,10 +723,6 @@ func TestValidateEndpointSliceUpdate(t *testing.T) { Protocol: protocolPtr(api.Protocol("invalid")), }}, }, - oldEndpointSlice: &discovery.EndpointSlice{ - ObjectMeta: standardMeta, - AddressType: discovery.AddressTypeIPv4, - }, expectedErrors: 1, }, } From b98cab77409ea85cd535ea832d5d6b11da41ec9f Mon Sep 17 00:00:00 2001 From: Rob Scott Date: Wed, 11 Nov 2020 11:17:09 -0800 Subject: [PATCH 7/8] Updating EndpointSlice strategy to cover alpha NodeName field --- .../discovery/endpointslice/strategy.go | 73 +++++--- .../discovery/endpointslice/strategy_test.go | 174 +++++++++++++++++- 2 files changed, 208 insertions(+), 39 deletions(-) diff --git a/pkg/registry/discovery/endpointslice/strategy.go b/pkg/registry/discovery/endpointslice/strategy.go index 9e2c48bc246..92cb2e65e06 100644 --- a/pkg/registry/discovery/endpointslice/strategy.go +++ b/pkg/registry/discovery/endpointslice/strategy.go @@ -50,7 +50,7 @@ func (endpointSliceStrategy) PrepareForCreate(ctx context.Context, obj runtime.O endpointSlice := obj.(*discovery.EndpointSlice) endpointSlice.Generation = 1 - dropDisabledConditionsOnCreate(endpointSlice) + dropDisabledFieldsOnCreate(endpointSlice) } // PrepareForUpdate clears fields that are not allowed to be set by end users on update. @@ -72,7 +72,7 @@ func (endpointSliceStrategy) PrepareForUpdate(ctx context.Context, obj, old runt newEPS.ObjectMeta = ogNewMeta oldEPS.ObjectMeta = ogOldMeta - dropDisabledConditionsOnUpdate(oldEPS, newEPS) + dropDisabledFieldsOnUpdate(oldEPS, newEPS) } // Validate validates a new EndpointSlice. @@ -103,41 +103,56 @@ 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 - } +// dropDisabledConditionsOnCreate will drop any fields that are disabled. +func dropDisabledFieldsOnCreate(endpointSlice *discovery.EndpointSlice) { + dropNodeName := !utilfeature.DefaultFeatureGate.Enabled(features.EndpointSliceNodeName) + dropTerminating := !utilfeature.DefaultFeatureGate.Enabled(features.EndpointSliceTerminatingCondition) - // 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 + if dropNodeName || dropTerminating { + for i := range endpointSlice.Endpoints { + if dropNodeName { + endpointSlice.Endpoints[i].NodeName = nil + } + if dropTerminating { + 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 +// dropDisabledFieldsOnUpdate will drop any disable fields that have not already +// been set on the EndpointSlice. +func dropDisabledFieldsOnUpdate(oldEPS, newEPS *discovery.EndpointSlice) { + dropNodeName := !utilfeature.DefaultFeatureGate.Enabled(features.EndpointSliceNodeName) + if dropNodeName { + for _, ep := range oldEPS.Endpoints { + if ep.NodeName != nil { + dropNodeName = false + break + } } } - if dropConditions { + dropTerminating := !utilfeature.DefaultFeatureGate.Enabled(features.EndpointSliceTerminatingCondition) + if dropTerminating { + for _, ep := range oldEPS.Endpoints { + if ep.Conditions.Serving != nil || ep.Conditions.Terminating != nil { + dropTerminating = false + break + } + } + } + + if dropNodeName || dropTerminating { for i := range newEPS.Endpoints { - newEPS.Endpoints[i].Conditions.Serving = nil - newEPS.Endpoints[i].Conditions.Terminating = nil + if dropNodeName { + newEPS.Endpoints[i].NodeName = nil + } + if dropTerminating { + newEPS.Endpoints[i].Conditions.Serving = nil + newEPS.Endpoints[i].Conditions.Terminating = nil + } } } } diff --git a/pkg/registry/discovery/endpointslice/strategy_test.go b/pkg/registry/discovery/endpointslice/strategy_test.go index 81bb687b8eb..fce0bf76231 100644 --- a/pkg/registry/discovery/endpointslice/strategy_test.go +++ b/pkg/registry/discovery/endpointslice/strategy_test.go @@ -27,15 +27,16 @@ import ( utilpointer "k8s.io/utils/pointer" ) -func Test_dropConditionsOnCreate(t *testing.T) { +func Test_dropDisabledFieldsOnCreate(t *testing.T) { testcases := []struct { name string terminatingGateEnabled bool + nodeNameGateEnabled bool eps *discovery.EndpointSlice expectedEPS *discovery.EndpointSlice }{ { - name: "gate enabled, field should be allowed", + name: "terminating gate enabled, field should be allowed", terminatingGateEnabled: true, eps: &discovery.EndpointSlice{ Endpoints: []discovery.Endpoint{ @@ -83,7 +84,7 @@ func Test_dropConditionsOnCreate(t *testing.T) { }, }, { - name: "gate disabled, field should be set to nil", + name: "terminating gate disabled, field should be set to nil", terminatingGateEnabled: false, eps: &discovery.EndpointSlice{ Endpoints: []discovery.Endpoint{ @@ -130,13 +131,62 @@ func Test_dropConditionsOnCreate(t *testing.T) { }, }, }, + { + name: "node name gate enabled, field should be allowed", + nodeNameGateEnabled: true, + eps: &discovery.EndpointSlice{ + Endpoints: []discovery.Endpoint{ + { + NodeName: utilpointer.StringPtr("node-1"), + }, + { + NodeName: utilpointer.StringPtr("node-2"), + }, + }, + }, + expectedEPS: &discovery.EndpointSlice{ + Endpoints: []discovery.Endpoint{ + { + NodeName: utilpointer.StringPtr("node-1"), + }, + { + NodeName: utilpointer.StringPtr("node-2"), + }, + }, + }, + }, + { + name: "node name gate disabled, field should be allowed", + nodeNameGateEnabled: false, + eps: &discovery.EndpointSlice{ + Endpoints: []discovery.Endpoint{ + { + NodeName: utilpointer.StringPtr("node-1"), + }, + { + NodeName: utilpointer.StringPtr("node-2"), + }, + }, + }, + expectedEPS: &discovery.EndpointSlice{ + Endpoints: []discovery.Endpoint{ + { + NodeName: nil, + }, + { + NodeName: nil, + }, + }, + }, + }, } for _, testcase := range testcases { t.Run(testcase.name, func(t *testing.T) { defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.EndpointSliceTerminatingCondition, testcase.terminatingGateEnabled)() + defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.EndpointSliceNodeName, testcase.nodeNameGateEnabled)() - dropDisabledConditionsOnCreate(testcase.eps) + dropDisabledFieldsOnCreate(testcase.eps) if !apiequality.Semantic.DeepEqual(testcase.eps, testcase.expectedEPS) { t.Logf("actual endpointslice: %v", testcase.eps) t.Logf("expected endpointslice: %v", testcase.expectedEPS) @@ -146,16 +196,17 @@ func Test_dropConditionsOnCreate(t *testing.T) { } } -func Test_dropTerminatingConditionOnUpdate(t *testing.T) { +func Test_dropDisabledFieldsOnUpdate(t *testing.T) { testcases := []struct { name string terminatingGateEnabled bool + nodeNameGateEnabled bool oldEPS *discovery.EndpointSlice newEPS *discovery.EndpointSlice expectedEPS *discovery.EndpointSlice }{ { - name: "gate enabled, field should be allowed", + name: "terminating gate enabled, field should be allowed", terminatingGateEnabled: true, oldEPS: &discovery.EndpointSlice{ Endpoints: []discovery.Endpoint{ @@ -225,7 +276,7 @@ func Test_dropTerminatingConditionOnUpdate(t *testing.T) { }, }, { - name: "gate disabled, and not set on existing EPS", + name: "terminating gate disabled, and not set on existing EPS", terminatingGateEnabled: false, oldEPS: &discovery.EndpointSlice{ Endpoints: []discovery.Endpoint{ @@ -295,7 +346,7 @@ func Test_dropTerminatingConditionOnUpdate(t *testing.T) { }, }, { - name: "gate disabled, and set on existing EPS", + name: "terminating gate disabled, and set on existing EPS", terminatingGateEnabled: false, oldEPS: &discovery.EndpointSlice{ Endpoints: []discovery.Endpoint{ @@ -365,7 +416,7 @@ func Test_dropTerminatingConditionOnUpdate(t *testing.T) { }, }, { - name: "gate disabled, and set on existing EPS with new values", + name: "terminating gate disabled, and set on existing EPS with new values", terminatingGateEnabled: false, oldEPS: &discovery.EndpointSlice{ Endpoints: []discovery.Endpoint{ @@ -431,13 +482,116 @@ func Test_dropTerminatingConditionOnUpdate(t *testing.T) { }, }, }, + { + name: "node name gate enabled, set on new EPS", + nodeNameGateEnabled: true, + oldEPS: &discovery.EndpointSlice{ + Endpoints: []discovery.Endpoint{ + { + NodeName: nil, + }, + { + NodeName: nil, + }, + }, + }, + newEPS: &discovery.EndpointSlice{ + Endpoints: []discovery.Endpoint{ + { + NodeName: utilpointer.StringPtr("node-1"), + }, + { + NodeName: utilpointer.StringPtr("node-2"), + }, + }, + }, + expectedEPS: &discovery.EndpointSlice{ + Endpoints: []discovery.Endpoint{ + { + NodeName: utilpointer.StringPtr("node-1"), + }, + { + NodeName: utilpointer.StringPtr("node-2"), + }, + }, + }, + }, + { + name: "node name gate disabled, set on new EPS", + nodeNameGateEnabled: false, + oldEPS: &discovery.EndpointSlice{ + Endpoints: []discovery.Endpoint{ + { + NodeName: nil, + }, + { + NodeName: nil, + }, + }, + }, + newEPS: &discovery.EndpointSlice{ + Endpoints: []discovery.Endpoint{ + { + NodeName: utilpointer.StringPtr("node-1"), + }, + { + NodeName: utilpointer.StringPtr("node-2"), + }, + }, + }, + expectedEPS: &discovery.EndpointSlice{ + Endpoints: []discovery.Endpoint{ + { + NodeName: nil, + }, + { + NodeName: nil, + }, + }, + }, + }, + { + name: "node name gate disabled, set on old and updated EPS", + nodeNameGateEnabled: false, + oldEPS: &discovery.EndpointSlice{ + Endpoints: []discovery.Endpoint{ + { + NodeName: utilpointer.StringPtr("node-1-old"), + }, + { + NodeName: utilpointer.StringPtr("node-2-old"), + }, + }, + }, + newEPS: &discovery.EndpointSlice{ + Endpoints: []discovery.Endpoint{ + { + NodeName: utilpointer.StringPtr("node-1"), + }, + { + NodeName: utilpointer.StringPtr("node-2"), + }, + }, + }, + expectedEPS: &discovery.EndpointSlice{ + Endpoints: []discovery.Endpoint{ + { + NodeName: utilpointer.StringPtr("node-1"), + }, + { + NodeName: utilpointer.StringPtr("node-2"), + }, + }, + }, + }, } for _, testcase := range testcases { t.Run(testcase.name, func(t *testing.T) { defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.EndpointSliceTerminatingCondition, testcase.terminatingGateEnabled)() + defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.EndpointSliceNodeName, testcase.nodeNameGateEnabled)() - dropDisabledConditionsOnUpdate(testcase.oldEPS, testcase.newEPS) + dropDisabledFieldsOnUpdate(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) From 84e4b30a3e9076312e3bb6953f1000aaaae20056 Mon Sep 17 00:00:00 2001 From: Rob Scott Date: Thu, 12 Nov 2020 12:17:26 -0800 Subject: [PATCH 8/8] Updates related to PR feedback - Remove feature gate consideration from EndpointSlice validation - Deprecate topology field, note that it will be removed in future release - Update kube-proxy to check for NodeName if feature gate is enabled - Add comments indicating the feature gates that can be used to enable alpha API fields - Add comments explaining use of deprecated address type in tests --- api/openapi-spec/swagger.json | 8 +- pkg/apis/discovery/types.go | 20 ++-- pkg/apis/discovery/validation/BUILD | 5 - pkg/apis/discovery/validation/validation.go | 35 ++----- .../discovery/validation/validation_test.go | 93 +------------------ .../endpointslice/reconciler_test.go | 1 + .../reconcilers/endpointsadapter_test.go | 1 + pkg/proxy/BUILD | 2 + pkg/proxy/endpointslicecache.go | 19 +++- .../api/discovery/v1alpha1/generated.proto | 20 ++-- .../k8s.io/api/discovery/v1alpha1/types.go | 20 ++-- .../v1alpha1/types_swagger_doc_generated.go | 8 +- .../api/discovery/v1beta1/generated.proto | 20 ++-- .../src/k8s.io/api/discovery/v1beta1/types.go | 20 ++-- .../v1beta1/types_swagger_doc_generated.go | 8 +- 15 files changed, 104 insertions(+), 176 deletions(-) diff --git a/api/openapi-spec/swagger.json b/api/openapi-spec/swagger.json index 7366e8bc361..87dbb125d91 100644 --- a/api/openapi-spec/swagger.json +++ b/api/openapi-spec/swagger.json @@ -10898,7 +10898,7 @@ "type": "string" }, "nodeName": { - "description": "nodeName represents the name of the Node hosting this endpoint. This can be used to determine endpoints local to a Node.", + "description": "nodeName represents the name of the Node hosting this endpoint. This can be used to determine endpoints local to a Node. This field can be enabled with the EndpointSliceNodeName feature gate.", "type": "string" }, "targetRef": { @@ -10909,7 +10909,7 @@ "additionalProperties": { "type": "string" }, - "description": "topology contains arbitrary topology information associated with the endpoint. These key/value pairs must conform with the label format. https://kubernetes.io/docs/concepts/overview/working-with-objects/labels Topology may include a maximum of 16 key/value pairs. This includes, but is not limited to the following well known keys: * kubernetes.io/hostname: the value indicates the hostname of the node\n where the endpoint is located. This should match the corresponding\n node label.\n* topology.kubernetes.io/zone: the value indicates the zone where the\n endpoint is located. This should match the corresponding node label.\n* topology.kubernetes.io/region: the value indicates the region where the\n endpoint is located. This should match the corresponding node label.\nthis field will be deprecated in an upcoming release.", + "description": "topology contains arbitrary topology information associated with the endpoint. These key/value pairs must conform with the label format. https://kubernetes.io/docs/concepts/overview/working-with-objects/labels Topology may include a maximum of 16 key/value pairs. This includes, but is not limited to the following well known keys: * kubernetes.io/hostname: the value indicates the hostname of the node\n where the endpoint is located. This should match the corresponding\n node label.\n* topology.kubernetes.io/zone: the value indicates the zone where the\n endpoint is located. This should match the corresponding node label.\n* topology.kubernetes.io/region: the value indicates the region where the\n endpoint is located. This should match the corresponding node label.\nThis field is deprecated and will be removed in future api versions.", "type": "object" } }, @@ -10926,11 +10926,11 @@ "type": "boolean" }, "serving": { - "description": "serving is identical to ready except that it is set regardless of the terminating state of endpoints. This condition should be set to true for a ready endpoint that is terminating. If nil, consumers should defer to the ready condition.", + "description": "serving is identical to ready except that it is set regardless of the terminating state of endpoints. This condition should be set to true for a ready endpoint that is terminating. If nil, consumers should defer to the ready condition. This field can be enabled with the EndpointSliceTerminatingCondition feature gate.", "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.", + "description": "terminating indicates that this endpoint is terminating. A nil value indicates an unknown state. Consumers should interpret this unknown state to mean that the endpoint is not terminating. This field can be enabled with the EndpointSliceTerminatingCondition feature gate.", "type": "boolean" } }, diff --git a/pkg/apis/discovery/types.go b/pkg/apis/discovery/types.go index 5d79683067b..1201d4abbc4 100644 --- a/pkg/apis/discovery/types.go +++ b/pkg/apis/discovery/types.go @@ -99,11 +99,12 @@ type Endpoint struct { // endpoint is located. This should match the corresponding node label. // * topology.kubernetes.io/region: the value indicates the region where the // endpoint is located. This should match the corresponding node label. - // this field will be deprecated in an upcoming release. + // This field is deprecated and will be removed in future api versions. // +optional Topology map[string]string // nodeName represents the name of the Node hosting this endpoint. This can - // be used to determine endpoints local to a Node. + // be used to determine endpoints local to a Node. This field can be enabled + // with the EndpointSliceNodeName feature gate. // +optional NodeName *string } @@ -117,15 +118,18 @@ type EndpointConditions struct { // "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. + // serving is identical to ready except that it is set regardless of the + // terminating state of endpoints. This condition should be set to true for + // a ready endpoint that is terminating. If nil, consumers should defer to + // the ready condition. This field can be enabled with the + // EndpointSliceTerminatingCondition feature gate. // +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. + // terminating indicates that this endpoint is terminating. A nil value + // indicates an unknown state. Consumers should interpret this unknown state + // to mean that the endpoint is not terminating. This field can be enabled + // with the EndpointSliceTerminatingCondition feature gate. // +optional Terminating *bool } diff --git a/pkg/apis/discovery/validation/BUILD b/pkg/apis/discovery/validation/BUILD index 38cd42af359..1f1a470f8bc 100644 --- a/pkg/apis/discovery/validation/BUILD +++ b/pkg/apis/discovery/validation/BUILD @@ -9,13 +9,11 @@ go_library( "//pkg/apis/core:go_default_library", "//pkg/apis/core/validation:go_default_library", "//pkg/apis/discovery:go_default_library", - "//pkg/features:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/api/validation:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1/validation:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/util/sets:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/util/validation:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/util/validation/field:go_default_library", - "//staging/src/k8s.io/apiserver/pkg/util/feature:go_default_library", ], ) @@ -26,10 +24,7 @@ go_test( deps = [ "//pkg/apis/core:go_default_library", "//pkg/apis/discovery:go_default_library", - "//pkg/features:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1: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", ], ) diff --git a/pkg/apis/discovery/validation/validation.go b/pkg/apis/discovery/validation/validation.go index 71d5ca6cbb5..8499e7a696a 100644 --- a/pkg/apis/discovery/validation/validation.go +++ b/pkg/apis/discovery/validation/validation.go @@ -22,11 +22,9 @@ import ( "k8s.io/apimachinery/pkg/util/sets" "k8s.io/apimachinery/pkg/util/validation" "k8s.io/apimachinery/pkg/util/validation/field" - utilfeature "k8s.io/apiserver/pkg/util/feature" api "k8s.io/kubernetes/pkg/apis/core" apivalidation "k8s.io/kubernetes/pkg/apis/core/validation" "k8s.io/kubernetes/pkg/apis/discovery" - "k8s.io/kubernetes/pkg/features" ) var ( @@ -52,10 +50,10 @@ var ( var ValidateEndpointSliceName = apimachineryvalidation.NameIsDNSSubdomain // ValidateEndpointSlice validates an EndpointSlice. -func ValidateEndpointSlice(endpointSlice *discovery.EndpointSlice, allowNodeName bool) field.ErrorList { +func ValidateEndpointSlice(endpointSlice *discovery.EndpointSlice) field.ErrorList { allErrs := apivalidation.ValidateObjectMeta(&endpointSlice.ObjectMeta, true, ValidateEndpointSliceName, field.NewPath("metadata")) allErrs = append(allErrs, validateAddressType(endpointSlice.AddressType)...) - allErrs = append(allErrs, validateEndpoints(endpointSlice.Endpoints, endpointSlice.AddressType, allowNodeName, field.NewPath("endpoints"))...) + allErrs = append(allErrs, validateEndpoints(endpointSlice.Endpoints, endpointSlice.AddressType, field.NewPath("endpoints"))...) allErrs = append(allErrs, validatePorts(endpointSlice.Ports, field.NewPath("ports"))...) return allErrs @@ -63,33 +61,18 @@ func ValidateEndpointSlice(endpointSlice *discovery.EndpointSlice, allowNodeName // ValidateEndpointSliceCreate validates an EndpointSlice when it is created. func ValidateEndpointSliceCreate(endpointSlice *discovery.EndpointSlice) field.ErrorList { - // allow NodeName value if the feature gate is set. - allowNodeName := utilfeature.DefaultFeatureGate.Enabled(features.EndpointSliceNodeName) - - return ValidateEndpointSlice(endpointSlice, allowNodeName) + return ValidateEndpointSlice(endpointSlice) } // ValidateEndpointSliceUpdate validates an EndpointSlice when it is updated. func ValidateEndpointSliceUpdate(newEndpointSlice, oldEndpointSlice *discovery.EndpointSlice) field.ErrorList { - // allow NodeName value if the feature gate is set. - allowNodeName := utilfeature.DefaultFeatureGate.Enabled(features.EndpointSliceNodeName) - - if !allowNodeName { - for _, ep := range oldEndpointSlice.Endpoints { - if ep.NodeName != nil { - allowNodeName = true - break - } - } - } - - allErrs := ValidateEndpointSlice(newEndpointSlice, allowNodeName) + allErrs := ValidateEndpointSlice(newEndpointSlice) allErrs = append(allErrs, apivalidation.ValidateImmutableField(newEndpointSlice.AddressType, oldEndpointSlice.AddressType, field.NewPath("addressType"))...) return allErrs } -func validateEndpoints(endpoints []discovery.Endpoint, addrType discovery.AddressType, allowNodeName bool, fldPath *field.Path) field.ErrorList { +func validateEndpoints(endpoints []discovery.Endpoint, addrType discovery.AddressType, fldPath *field.Path) field.ErrorList { allErrs := field.ErrorList{} if len(endpoints) > maxEndpoints { @@ -122,12 +105,8 @@ func validateEndpoints(endpoints []discovery.Endpoint, addrType discovery.Addres if endpoint.NodeName != nil { nnPath := idxPath.Child("nodeName") - if allowNodeName { - for _, msg := range apivalidation.ValidateNodeName(*endpoint.NodeName, false) { - allErrs = append(allErrs, field.Invalid(nnPath, *endpoint.NodeName, msg)) - } - } else { - allErrs = append(allErrs, field.Forbidden(nnPath, "may not be set unless EndpointSliceNodeName feature gate is enabled")) + for _, msg := range apivalidation.ValidateNodeName(*endpoint.NodeName, false) { + allErrs = append(allErrs, field.Invalid(nnPath, *endpoint.NodeName, msg)) } } diff --git a/pkg/apis/discovery/validation/validation_test.go b/pkg/apis/discovery/validation/validation_test.go index 6457da08499..5c7d478eb7e 100644 --- a/pkg/apis/discovery/validation/validation_test.go +++ b/pkg/apis/discovery/validation/validation_test.go @@ -22,11 +22,8 @@ import ( "testing" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - utilfeature "k8s.io/apiserver/pkg/util/feature" - featuregatetesting "k8s.io/component-base/featuregate/testing" api "k8s.io/kubernetes/pkg/apis/core" "k8s.io/kubernetes/pkg/apis/discovery" - "k8s.io/kubernetes/pkg/features" utilpointer "k8s.io/utils/pointer" ) @@ -461,7 +458,7 @@ func TestValidateEndpointSlice(t *testing.T) { for name, testCase := range testCases { t.Run(name, func(t *testing.T) { - errs := ValidateEndpointSlice(testCase.endpointSlice, true) + errs := ValidateEndpointSlice(testCase.endpointSlice) if len(errs) != testCase.expectedErrors { t.Errorf("Expected %d errors, got %d errors: %v", testCase.expectedErrors, len(errs), errs) } @@ -496,8 +493,7 @@ func TestValidateEndpointSliceCreate(t *testing.T) { }, }, "good-slice-node-name": { - expectedErrors: 0, - nodeNameGateEnabled: true, + expectedErrors: 0, endpointSlice: &discovery.EndpointSlice{ ObjectMeta: standardMeta, AddressType: discovery.AddressTypeIPv4, @@ -515,8 +511,7 @@ func TestValidateEndpointSliceCreate(t *testing.T) { // expected failures "bad-node-name": { - expectedErrors: 1, - nodeNameGateEnabled: true, + expectedErrors: 1, endpointSlice: &discovery.EndpointSlice{ ObjectMeta: standardMeta, AddressType: discovery.AddressTypeIPv4, @@ -531,23 +526,6 @@ func TestValidateEndpointSliceCreate(t *testing.T) { }}, }, }, - "node-name-disabled": { - expectedErrors: 1, - nodeNameGateEnabled: false, - endpointSlice: &discovery.EndpointSlice{ - ObjectMeta: standardMeta, - AddressType: discovery.AddressTypeIPv4, - Ports: []discovery.EndpointPort{{ - Name: utilpointer.StringPtr("http"), - Protocol: protocolPtr(api.ProtocolTCP), - }}, - Endpoints: []discovery.Endpoint{{ - Addresses: generateIPAddresses(1), - Hostname: utilpointer.StringPtr("valid-123"), - NodeName: utilpointer.StringPtr("valid-node-name"), - }}, - }, - }, "deprecated-address-type": { expectedErrors: 1, endpointSlice: &discovery.EndpointSlice{ @@ -579,8 +557,6 @@ func TestValidateEndpointSliceCreate(t *testing.T) { } for name, testCase := range testCases { - defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.EndpointSliceNodeName, testCase.nodeNameGateEnabled)() - t.Run(name, func(t *testing.T) { errs := ValidateEndpointSliceCreate(testCase.endpointSlice) if len(errs) != testCase.expectedErrors { @@ -610,48 +586,9 @@ func TestValidateEndpointSliceUpdate(t *testing.T) { }, expectedErrors: 0, }, - "node name set before + after, gate disabled": { - oldEndpointSlice: &discovery.EndpointSlice{ - ObjectMeta: standardMeta, - AddressType: discovery.AddressTypeIPv4, - Endpoints: []discovery.Endpoint{{ - Addresses: []string{"10.1.2.3"}, - NodeName: utilpointer.StringPtr("foo"), - }}, - }, - newEndpointSlice: &discovery.EndpointSlice{ - ObjectMeta: standardMeta, - AddressType: discovery.AddressTypeIPv4, - Endpoints: []discovery.Endpoint{{ - Addresses: []string{"10.1.2.3"}, - NodeName: utilpointer.StringPtr("foo"), - }}, - }, - expectedErrors: 0, - }, - "node name set after, gate enabled": { - nodeNameGateEnabled: true, - oldEndpointSlice: &discovery.EndpointSlice{ - ObjectMeta: standardMeta, - AddressType: discovery.AddressTypeIPv4, - Endpoints: []discovery.Endpoint{{ - Addresses: []string{"10.1.2.3"}, - }}, - }, - newEndpointSlice: &discovery.EndpointSlice{ - ObjectMeta: standardMeta, - AddressType: discovery.AddressTypeIPv4, - Endpoints: []discovery.Endpoint{{ - Addresses: []string{"10.1.2.3"}, - NodeName: utilpointer.StringPtr("foo"), - }}, - }, - expectedErrors: 0, - }, // expected errors - "invalide node name set after, gate enabled": { - nodeNameGateEnabled: true, + "invalide node name set": { oldEndpointSlice: &discovery.EndpointSlice{ ObjectMeta: standardMeta, AddressType: discovery.AddressTypeIPv4, @@ -669,25 +606,7 @@ func TestValidateEndpointSliceUpdate(t *testing.T) { }, expectedErrors: 1, }, - "node name set after, gate disabled": { - nodeNameGateEnabled: false, - oldEndpointSlice: &discovery.EndpointSlice{ - ObjectMeta: standardMeta, - AddressType: discovery.AddressTypeIPv4, - Endpoints: []discovery.Endpoint{{ - Addresses: []string{"10.1.2.3"}, - }}, - }, - newEndpointSlice: &discovery.EndpointSlice{ - ObjectMeta: standardMeta, - AddressType: discovery.AddressTypeIPv4, - Endpoints: []discovery.Endpoint{{ - Addresses: []string{"10.1.2.3"}, - NodeName: utilpointer.StringPtr("foo"), - }}, - }, - expectedErrors: 1, - }, + "deprecated address type": { expectedErrors: 1, oldEndpointSlice: &discovery.EndpointSlice{ @@ -729,8 +648,6 @@ func TestValidateEndpointSliceUpdate(t *testing.T) { for name, testCase := range testCases { t.Run(name, func(t *testing.T) { - defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.EndpointSliceNodeName, testCase.nodeNameGateEnabled)() - errs := ValidateEndpointSliceUpdate(testCase.newEndpointSlice, testCase.oldEndpointSlice) if len(errs) != testCase.expectedErrors { t.Errorf("Expected %d errors, got %d errors: %v", testCase.expectedErrors, len(errs), errs) diff --git a/pkg/controller/endpointslice/reconciler_test.go b/pkg/controller/endpointslice/reconciler_test.go index 95e88b2fa5f..522881dda83 100644 --- a/pkg/controller/endpointslice/reconciler_test.go +++ b/pkg/controller/endpointslice/reconciler_test.go @@ -917,6 +917,7 @@ func TestReconcileEndpointSlicesReplaceDeprecated(t *testing.T) { namespace := "test" svc, endpointMeta := newServiceAndEndpointMeta("foo", namespace) + // "IP" is a deprecated address type, ensuring that it is handled properly. endpointMeta.AddressType = discovery.AddressType("IP") existingSlices := []*discovery.EndpointSlice{} diff --git a/pkg/controlplane/reconcilers/endpointsadapter_test.go b/pkg/controlplane/reconcilers/endpointsadapter_test.go index 13a34aa5ffb..531b1e2a31c 100644 --- a/pkg/controlplane/reconcilers/endpointsadapter_test.go +++ b/pkg/controlplane/reconcilers/endpointsadapter_test.go @@ -232,6 +232,7 @@ func TestEndpointsAdapterUpdate(t *testing.T) { // with one that has an IPv4 address type. endpoints4, _ := generateEndpointsAndSlice("foo", "testing", []int{80}, []string{"10.1.2.7", "10.1.2.8"}) _, epSlice4IP := generateEndpointsAndSlice("foo", "testing", []int{80}, []string{"10.1.2.7", "10.1.2.8"}) + // "IP" is a deprecated address type, ensuring that it is handled properly. epSlice4IP.AddressType = discovery.AddressType("IP") _, epSlice4IPv4 := generateEndpointsAndSlice("foo", "testing", []int{80}, []string{"10.1.2.7", "10.1.2.8"}) diff --git a/pkg/proxy/BUILD b/pkg/proxy/BUILD index 8b3ac576e71..089a9d6df96 100644 --- a/pkg/proxy/BUILD +++ b/pkg/proxy/BUILD @@ -19,6 +19,7 @@ go_library( importpath = "k8s.io/kubernetes/pkg/proxy", deps = [ "//pkg/api/v1/service:go_default_library", + "//pkg/features:go_default_library", "//pkg/proxy/config:go_default_library", "//pkg/proxy/metrics:go_default_library", "//pkg/proxy/util:go_default_library", @@ -26,6 +27,7 @@ go_library( "//staging/src/k8s.io/api/discovery/v1beta1:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/types:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/util/sets:go_default_library", + "//staging/src/k8s.io/apiserver/pkg/util/feature:go_default_library", "//staging/src/k8s.io/client-go/tools/record:go_default_library", "//vendor/k8s.io/klog/v2:go_default_library", "//vendor/k8s.io/utils/net:go_default_library", diff --git a/pkg/proxy/endpointslicecache.go b/pkg/proxy/endpointslicecache.go index b049481a372..bb180adcfcd 100644 --- a/pkg/proxy/endpointslicecache.go +++ b/pkg/proxy/endpointslicecache.go @@ -26,8 +26,10 @@ import ( "k8s.io/api/core/v1" discovery "k8s.io/api/discovery/v1beta1" "k8s.io/apimachinery/pkg/types" + utilfeature "k8s.io/apiserver/pkg/util/feature" "k8s.io/client-go/tools/record" "k8s.io/klog/v2" + "k8s.io/kubernetes/pkg/features" utilproxy "k8s.io/kubernetes/pkg/proxy/util" utilnet "k8s.io/utils/net" ) @@ -76,6 +78,7 @@ type endpointSliceInfo struct { // Addresses and Topology are copied from EndpointSlice Endpoints. type endpointInfo struct { Addresses []string + NodeName *string Topology map[string]string } @@ -120,10 +123,14 @@ func newEndpointSliceInfo(endpointSlice *discovery.EndpointSlice, remove bool) * if !remove { for _, endpoint := range endpointSlice.Endpoints { if endpoint.Conditions.Ready == nil || *endpoint.Conditions.Ready { - esInfo.Endpoints = append(esInfo.Endpoints, &endpointInfo{ + eInfo := endpointInfo{ Addresses: endpoint.Addresses, Topology: endpoint.Topology, - }) + } + if utilfeature.DefaultFeatureGate.Enabled(features.EndpointSliceNodeName) { + eInfo.NodeName = endpoint.NodeName + } + esInfo.Endpoints = append(esInfo.Endpoints, &eInfo) } } @@ -255,7 +262,13 @@ func (cache *EndpointSliceCache) addEndpointsByIP(serviceNN types.NamespacedName continue } - isLocal := cache.isLocal(endpoint.Topology[v1.LabelHostname]) + isLocal := false + if endpoint.NodeName != nil { + isLocal = cache.isLocal(*endpoint.NodeName) + } else { + isLocal = cache.isLocal(endpoint.Topology[v1.LabelHostname]) + } + endpointInfo := newBaseEndpointInfo(endpoint.Addresses[0], portNum, isLocal, endpoint.Topology) // This logic ensures we're deduping potential overlapping endpoints diff --git a/staging/src/k8s.io/api/discovery/v1alpha1/generated.proto b/staging/src/k8s.io/api/discovery/v1alpha1/generated.proto index f85ea034b79..4b66a6c57bf 100644 --- a/staging/src/k8s.io/api/discovery/v1alpha1/generated.proto +++ b/staging/src/k8s.io/api/discovery/v1alpha1/generated.proto @@ -67,12 +67,13 @@ message Endpoint { // endpoint is located. This should match the corresponding node label. // * topology.kubernetes.io/region: the value indicates the region where the // endpoint is located. This should match the corresponding node label. - // This field will be deprecated in an upcoming release. + // This field is deprecated and will be removed in future api versions. // +optional map topology = 5; // nodeName represents the name of the Node hosting this endpoint. This can - // be used to determine endpoints local to a Node. + // be used to determine endpoints local to a Node. This field can be enabled + // with the EndpointSliceNodeName feature gate. // +optional optional string nodeName = 6; } @@ -87,15 +88,18 @@ message EndpointConditions { // +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. + // serving is identical to ready except that it is set regardless of the + // terminating state of endpoints. This condition should be set to true for + // a ready endpoint that is terminating. If nil, consumers should defer to + // the ready condition. This field can be enabled with the + // EndpointSliceTerminatingCondition feature gate. // +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. + // terminating indicates that this endpoint is terminating. A nil value + // indicates an unknown state. Consumers should interpret this unknown state + // to mean that the endpoint is not terminating. This field can be enabled + // with the EndpointSliceTerminatingCondition feature gate. // +optional optional bool terminating = 3; } diff --git a/staging/src/k8s.io/api/discovery/v1alpha1/types.go b/staging/src/k8s.io/api/discovery/v1alpha1/types.go index ca1dca3cc16..34b706ea897 100644 --- a/staging/src/k8s.io/api/discovery/v1alpha1/types.go +++ b/staging/src/k8s.io/api/discovery/v1alpha1/types.go @@ -106,11 +106,12 @@ type Endpoint struct { // endpoint is located. This should match the corresponding node label. // * topology.kubernetes.io/region: the value indicates the region where the // endpoint is located. This should match the corresponding node label. - // This field will be deprecated in an upcoming release. + // This field is deprecated and will be removed in future api versions. // +optional Topology map[string]string `json:"topology,omitempty" protobuf:"bytes,5,opt,name=topology"` // nodeName represents the name of the Node hosting this endpoint. This can - // be used to determine endpoints local to a Node. + // be used to determine endpoints local to a Node. This field can be enabled + // with the EndpointSliceNodeName feature gate. // +optional NodeName *string `json:"nodeName,omitempty" protobuf:"bytes,6,opt,name=nodeName"` } @@ -125,15 +126,18 @@ type EndpointConditions struct { // +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. + // serving is identical to ready except that it is set regardless of the + // terminating state of endpoints. This condition should be set to true for + // a ready endpoint that is terminating. If nil, consumers should defer to + // the ready condition. This field can be enabled with the + // EndpointSliceTerminatingCondition feature gate. // +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. + // terminating indicates that this endpoint is terminating. A nil value + // indicates an unknown state. Consumers should interpret this unknown state + // to mean that the endpoint is not terminating. This field can be enabled + // with the EndpointSliceTerminatingCondition feature gate. // +optional Terminating *bool `json:"terminating,omitempty" protobuf:"bytes,3,name=terminating"` } diff --git a/staging/src/k8s.io/api/discovery/v1alpha1/types_swagger_doc_generated.go b/staging/src/k8s.io/api/discovery/v1alpha1/types_swagger_doc_generated.go index 8012d36a2c1..f6c983689a1 100644 --- a/staging/src/k8s.io/api/discovery/v1alpha1/types_swagger_doc_generated.go +++ b/staging/src/k8s.io/api/discovery/v1alpha1/types_swagger_doc_generated.go @@ -33,8 +33,8 @@ var map_Endpoint = map[string]string{ "conditions": "conditions contains information about the current status of the endpoint.", "hostname": "hostname of this endpoint. This field may be used by consumers of endpoints to distinguish endpoints from each other (e.g. in DNS names). Multiple endpoints which use the same hostname should be considered fungible (e.g. multiple A values in DNS). Must be lowercase and pass DNS label (RFC 1123) validation.", "targetRef": "targetRef is a reference to a Kubernetes object that represents this endpoint.", - "topology": "topology contains arbitrary topology information associated with the endpoint. These key/value pairs must conform with the label format. https://kubernetes.io/docs/concepts/overview/working-with-objects/labels Topology may include a maximum of 16 key/value pairs. This includes, but is not limited to the following well known keys: * kubernetes.io/hostname: the value indicates the hostname of the node\n where the endpoint is located. This should match the corresponding\n node label.\n* topology.kubernetes.io/zone: the value indicates the zone where the\n endpoint is located. This should match the corresponding node label.\n* topology.kubernetes.io/region: the value indicates the region where the\n endpoint is located. This should match the corresponding node label.\nThis field will be deprecated in an upcoming release.", - "nodeName": "nodeName represents the name of the Node hosting this endpoint. This can be used to determine endpoints local to a Node.", + "topology": "topology contains arbitrary topology information associated with the endpoint. These key/value pairs must conform with the label format. https://kubernetes.io/docs/concepts/overview/working-with-objects/labels Topology may include a maximum of 16 key/value pairs. This includes, but is not limited to the following well known keys: * kubernetes.io/hostname: the value indicates the hostname of the node\n where the endpoint is located. This should match the corresponding\n node label.\n* topology.kubernetes.io/zone: the value indicates the zone where the\n endpoint is located. This should match the corresponding node label.\n* topology.kubernetes.io/region: the value indicates the region where the\n endpoint is located. This should match the corresponding node label.\nThis field is deprecated and will be removed in future api versions.", + "nodeName": "nodeName represents the name of the Node hosting this endpoint. This can be used to determine endpoints local to a Node. This field can be enabled with the EndpointSliceNodeName feature gate.", } func (Endpoint) SwaggerDoc() map[string]string { @@ -44,8 +44,8 @@ func (Endpoint) SwaggerDoc() map[string]string { var map_EndpointConditions = map[string]string{ "": "EndpointConditions represents the current condition of an endpoint.", "ready": "ready indicates that this endpoint is prepared to receive traffic, according to whatever system is managing the endpoint. A nil value indicates an unknown state. In most cases consumers should interpret this unknown state as ready. For compatibility reasons, ready should never be \"true\" for terminating endpoints.", - "serving": "serving is identical to ready except that it is set regardless of the terminating state of endpoints. This condition should be set to true for a ready endpoint that is terminating. If nil, consumers should defer to the ready condition.", - "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.", + "serving": "serving is identical to ready except that it is set regardless of the terminating state of endpoints. This condition should be set to true for a ready endpoint that is terminating. If nil, consumers should defer to the ready condition. This field can be enabled with the EndpointSliceTerminatingCondition feature gate.", + "terminating": "terminating indicates that this endpoint is terminating. A nil value indicates an unknown state. Consumers should interpret this unknown state to mean that the endpoint is not terminating. This field can be enabled with the EndpointSliceTerminatingCondition feature gate.", } func (EndpointConditions) SwaggerDoc() map[string]string { diff --git a/staging/src/k8s.io/api/discovery/v1beta1/generated.proto b/staging/src/k8s.io/api/discovery/v1beta1/generated.proto index 6c67870248a..e5d21caadf2 100644 --- a/staging/src/k8s.io/api/discovery/v1beta1/generated.proto +++ b/staging/src/k8s.io/api/discovery/v1beta1/generated.proto @@ -67,12 +67,13 @@ message Endpoint { // endpoint is located. This should match the corresponding node label. // * topology.kubernetes.io/region: the value indicates the region where the // endpoint is located. This should match the corresponding node label. - // this field will be deprecated in an upcoming release. + // This field is deprecated and will be removed in future api versions. // +optional map topology = 5; // nodeName represents the name of the Node hosting this endpoint. This can - // be used to determine endpoints local to a Node. + // be used to determine endpoints local to a Node. This field can be enabled + // with the EndpointSliceNodeName feature gate. // +optional optional string nodeName = 6; } @@ -87,15 +88,18 @@ message EndpointConditions { // +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. + // serving is identical to ready except that it is set regardless of the + // terminating state of endpoints. This condition should be set to true for + // a ready endpoint that is terminating. If nil, consumers should defer to + // the ready condition. This field can be enabled with the + // EndpointSliceTerminatingCondition feature gate. // +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. + // terminating indicates that this endpoint is terminating. A nil value + // indicates an unknown state. Consumers should interpret this unknown state + // to mean that the endpoint is not terminating. This field can be enabled + // with the EndpointSliceTerminatingCondition feature gate. // +optional optional bool terminating = 3; } diff --git a/staging/src/k8s.io/api/discovery/v1beta1/types.go b/staging/src/k8s.io/api/discovery/v1beta1/types.go index 56b41a5ec43..e14088e8b2c 100644 --- a/staging/src/k8s.io/api/discovery/v1beta1/types.go +++ b/staging/src/k8s.io/api/discovery/v1beta1/types.go @@ -102,11 +102,12 @@ type Endpoint struct { // endpoint is located. This should match the corresponding node label. // * topology.kubernetes.io/region: the value indicates the region where the // endpoint is located. This should match the corresponding node label. - // this field will be deprecated in an upcoming release. + // This field is deprecated and will be removed in future api versions. // +optional Topology map[string]string `json:"topology,omitempty" protobuf:"bytes,5,opt,name=topology"` // nodeName represents the name of the Node hosting this endpoint. This can - // be used to determine endpoints local to a Node. + // be used to determine endpoints local to a Node. This field can be enabled + // with the EndpointSliceNodeName feature gate. // +optional NodeName *string `json:"nodeName,omitempty" protobuf:"bytes,6,opt,name=nodeName"` } @@ -121,15 +122,18 @@ type EndpointConditions struct { // +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. + // serving is identical to ready except that it is set regardless of the + // terminating state of endpoints. This condition should be set to true for + // a ready endpoint that is terminating. If nil, consumers should defer to + // the ready condition. This field can be enabled with the + // EndpointSliceTerminatingCondition feature gate. // +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. + // terminating indicates that this endpoint is terminating. A nil value + // indicates an unknown state. Consumers should interpret this unknown state + // to mean that the endpoint is not terminating. This field can be enabled + // with the EndpointSliceTerminatingCondition feature gate. // +optional Terminating *bool `json:"terminating,omitempty" protobuf:"bytes,3,name=terminating"` } diff --git a/staging/src/k8s.io/api/discovery/v1beta1/types_swagger_doc_generated.go b/staging/src/k8s.io/api/discovery/v1beta1/types_swagger_doc_generated.go index 1f7034bda58..d48b93d8b50 100644 --- a/staging/src/k8s.io/api/discovery/v1beta1/types_swagger_doc_generated.go +++ b/staging/src/k8s.io/api/discovery/v1beta1/types_swagger_doc_generated.go @@ -33,8 +33,8 @@ var map_Endpoint = map[string]string{ "conditions": "conditions contains information about the current status of the endpoint.", "hostname": "hostname of this endpoint. This field may be used by consumers of endpoints to distinguish endpoints from each other (e.g. in DNS names). Multiple endpoints which use the same hostname should be considered fungible (e.g. multiple A values in DNS). Must be lowercase and pass DNS Label (RFC 1123) validation.", "targetRef": "targetRef is a reference to a Kubernetes object that represents this endpoint.", - "topology": "topology contains arbitrary topology information associated with the endpoint. These key/value pairs must conform with the label format. https://kubernetes.io/docs/concepts/overview/working-with-objects/labels Topology may include a maximum of 16 key/value pairs. This includes, but is not limited to the following well known keys: * kubernetes.io/hostname: the value indicates the hostname of the node\n where the endpoint is located. This should match the corresponding\n node label.\n* topology.kubernetes.io/zone: the value indicates the zone where the\n endpoint is located. This should match the corresponding node label.\n* topology.kubernetes.io/region: the value indicates the region where the\n endpoint is located. This should match the corresponding node label.\nthis field will be deprecated in an upcoming release.", - "nodeName": "nodeName represents the name of the Node hosting this endpoint. This can be used to determine endpoints local to a Node.", + "topology": "topology contains arbitrary topology information associated with the endpoint. These key/value pairs must conform with the label format. https://kubernetes.io/docs/concepts/overview/working-with-objects/labels Topology may include a maximum of 16 key/value pairs. This includes, but is not limited to the following well known keys: * kubernetes.io/hostname: the value indicates the hostname of the node\n where the endpoint is located. This should match the corresponding\n node label.\n* topology.kubernetes.io/zone: the value indicates the zone where the\n endpoint is located. This should match the corresponding node label.\n* topology.kubernetes.io/region: the value indicates the region where the\n endpoint is located. This should match the corresponding node label.\nThis field is deprecated and will be removed in future api versions.", + "nodeName": "nodeName represents the name of the Node hosting this endpoint. This can be used to determine endpoints local to a Node. This field can be enabled with the EndpointSliceNodeName feature gate.", } func (Endpoint) SwaggerDoc() map[string]string { @@ -44,8 +44,8 @@ func (Endpoint) SwaggerDoc() map[string]string { var map_EndpointConditions = map[string]string{ "": "EndpointConditions represents the current condition of an endpoint.", "ready": "ready indicates that this endpoint is prepared to receive traffic, according to whatever system is managing the endpoint. A nil value indicates an unknown state. In most cases consumers should interpret this unknown state as ready. For compatibility reasons, ready should never be \"true\" for terminating endpoints.", - "serving": "serving is identical to ready except that it is set regardless of the terminating state of endpoints. This condition should be set to true for a ready endpoint that is terminating. If nil, consumers should defer to the ready condition.", - "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.", + "serving": "serving is identical to ready except that it is set regardless of the terminating state of endpoints. This condition should be set to true for a ready endpoint that is terminating. If nil, consumers should defer to the ready condition. This field can be enabled with the EndpointSliceTerminatingCondition feature gate.", + "terminating": "terminating indicates that this endpoint is terminating. A nil value indicates an unknown state. Consumers should interpret this unknown state to mean that the endpoint is not terminating. This field can be enabled with the EndpointSliceTerminatingCondition feature gate.", } func (EndpointConditions) SwaggerDoc() map[string]string {