Skip to content

Commit

Permalink
Adding NodeName to EndpointSlice API, deprecation updates
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
robscott committed Nov 12, 2020
1 parent fbc589f commit 9613f79
Show file tree
Hide file tree
Showing 18 changed files with 442 additions and 146 deletions.
6 changes: 5 additions & 1 deletion api/openapi-spec/swagger.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

11 changes: 5 additions & 6 deletions pkg/apis/discovery/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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.
Expand Down
2 changes: 2 additions & 0 deletions pkg/apis/discovery/v1alpha1/zz_generated.conversion.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions pkg/apis/discovery/v1beta1/zz_generated.conversion.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions pkg/apis/discovery/validation/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -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",
],
)

Expand All @@ -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",
],
)
Expand Down
53 changes: 37 additions & 16 deletions pkg/apis/discovery/validation/validation.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 (
Expand All @@ -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),
Expand All @@ -53,29 +52,44 @@ 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
}

// 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 {
Expand All @@ -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:
Expand All @@ -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))
Expand Down Expand Up @@ -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
Expand Down
Loading

0 comments on commit 9613f79

Please sign in to comment.