Skip to content

Commit

Permalink
Merge pull request #96440 from robscott/endpointslice-pre-ga
Browse files Browse the repository at this point in the history
 Adding NodeName to EndpointSlice API, deprecation updates
  • Loading branch information
k8s-ci-robot authored Nov 13, 2020
2 parents ae95984 + 84e4b30 commit 765d949
Show file tree
Hide file tree
Showing 32 changed files with 766 additions and 239 deletions.
10 changes: 7 additions & 3 deletions api/openapi-spec/swagger.json

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

27 changes: 15 additions & 12 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,14 @@ 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 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. This field can be enabled
// with the EndpointSliceNodeName feature gate.
// +optional
NodeName *string
}

// EndpointConditions represents the current condition of an endpoint.
Expand All @@ -118,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
}
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.

28 changes: 14 additions & 14 deletions pkg/apis/discovery/validation/validation.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,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,9 +50,9 @@ var (
var ValidateEndpointSliceName = apimachineryvalidation.NameIsDNSSubdomain

// ValidateEndpointSlice validates an EndpointSlice.
func ValidateEndpointSlice(endpointSlice *discovery.EndpointSlice, validAddressTypes sets.String) 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, validAddressTypes)...)
allErrs = append(allErrs, validateAddressType(endpointSlice.AddressType)...)
allErrs = append(allErrs, validateEndpoints(endpointSlice.Endpoints, endpointSlice.AddressType, field.NewPath("endpoints"))...)
allErrs = append(allErrs, validatePorts(endpointSlice.Ports, field.NewPath("ports"))...)

Expand All @@ -64,12 +61,12 @@ 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)
return ValidateEndpointSlice(endpointSlice)
}

// ValidateEndpointSliceUpdate validates an EndpointSlice when it is updated.
func ValidateEndpointSliceUpdate(newEndpointSlice, oldEndpointSlice *discovery.EndpointSlice) field.ErrorList {
allErrs := ValidateEndpointSlice(newEndpointSlice, supportedAddressTypes.Union(deprecatedAddressTypes))
allErrs := ValidateEndpointSlice(newEndpointSlice)
allErrs = append(allErrs, apivalidation.ValidateImmutableField(newEndpointSlice.AddressType, oldEndpointSlice.AddressType, field.NewPath("addressType"))...)

return allErrs
Expand Down Expand Up @@ -97,10 +94,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 +103,13 @@ func validateEndpoints(endpoints []discovery.Endpoint, addrType discovery.Addres
}
}

if endpoint.NodeName != nil {
nnPath := idxPath.Child("nodeName")
for _, msg := range apivalidation.ValidateNodeName(*endpoint.NodeName, false) {
allErrs = append(allErrs, field.Invalid(nnPath, *endpoint.NodeName, msg))
}
}

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 +162,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
103 changes: 79 additions & 24 deletions pkg/apis/discovery/validation/validation_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,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),
Expand Down Expand Up @@ -378,7 +378,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),
Expand Down Expand Up @@ -438,7 +438,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),
Expand All @@ -458,7 +458,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)
if len(errs) != testCase.expectedErrors {
t.Errorf("Expected %d errors, got %d errors: %v", testCase.expectedErrors, len(errs), errs)
}
Expand All @@ -473,8 +473,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,
Expand All @@ -491,13 +492,45 @@ func TestValidateEndpointSliceCreate(t *testing.T) {
}},
},
},
"good-slice-node-name": {
expectedErrors: 0,
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,
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"),
}},
},
},
"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),
Expand Down Expand Up @@ -537,56 +570,78 @@ 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
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,
},
"deprecated address type": {
expectedErrors: 0,

// expected errors
"invalide node name set": {
oldEndpointSlice: &discovery.EndpointSlice{
ObjectMeta: standardMeta,
AddressType: discovery.AddressTypeIPv4,
Endpoints: []discovery.Endpoint{{
Addresses: []string{"10.1.2.3"},
}},
},
newEndpointSlice: &discovery.EndpointSlice{
ObjectMeta: standardMeta,
AddressType: discovery.AddressTypeIP,
AddressType: discovery.AddressTypeIPv4,
Endpoints: []discovery.Endpoint{{
Addresses: []string{"10.1.2.3"},
NodeName: utilpointer.StringPtr("INVALID foo"),
}},
},
expectedErrors: 1,
},

"deprecated address type": {
expectedErrors: 1,
oldEndpointSlice: &discovery.EndpointSlice{
ObjectMeta: standardMeta,
AddressType: discovery.AddressTypeIP,
AddressType: discovery.AddressType("IP"),
},
},
"valid and identical slices with different address types": {
newEndpointSlice: &discovery.EndpointSlice{
ObjectMeta: standardMeta,
AddressType: discovery.AddressTypeIP,
AddressType: discovery.AddressType("IP"),
},
},
"valid and identical slices with different address types": {
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.AddressTypeIP,
AddressType: discovery.AddressTypeIPv4,
Ports: []discovery.EndpointPort{{
Name: utilpointer.StringPtr(""),
Protocol: protocolPtr(api.Protocol("invalid")),
}},
},
oldEndpointSlice: &discovery.EndpointSlice{
ObjectMeta: standardMeta,
AddressType: discovery.AddressTypeIP,
},
expectedErrors: 1,
},
}
Expand Down
Loading

0 comments on commit 765d949

Please sign in to comment.