Adding FQDN address type for EndpointSlice

This commit is contained in:
Rob Scott
2019-10-18 11:18:58 -07:00
parent cd274ff270
commit d410bd28c3
9 changed files with 229 additions and 45 deletions

View File

@@ -32,7 +32,11 @@ type EndpointSlice struct {
// +optional
metav1.ObjectMeta
// addressType specifies the type of address carried by this EndpointSlice.
// All addresses in this slice must be the same type.
// All addresses in this slice must be the same type. The following address
// types are currently supported:
// * IP: Represents an IP Address. This can include both IPv4 and IPv6
// addresses.
// * FQDN: Represents a Fully Qualified Domain Name.
// +optional
AddressType *AddressType
// endpoints is a list of unique endpoints in this slice. Each slice may
@@ -53,17 +57,21 @@ type EndpointSlice struct {
type AddressType string
const (
// AddressTypeIP represents an IP Address.
// AddressTypeIP represents an IP Address. Inclusive of IPv4 and IPv6
// addresses.
AddressTypeIP = AddressType("IP")
// AddressTypeFQDN represents a Fully Qualified Domain Name.
AddressTypeFQDN = AddressType("FQDN")
)
// Endpoint represents a single logical "backend" implementing a service.
type Endpoint struct {
// addresses of this endpoint. The contents of this field are interpreted
// according to the corresponding EndpointSlice addressType field. This
// allows for cases like dual-stack (IPv4 and IPv6) networking. Consumers
// (e.g. kube-proxy) must handle different types of addresses in the context
// of their own capabilities. This must contain at least one address but no
// allows for cases like dual-stack networking where both IPv4 and IPv6
// addresses would be included with the IP addressType. Consumers (e.g.
// kube-proxy) must handle different types of addresses in the context of
// their own capabilities. This must contain at least one address but no
// more than 100.
// +listType=set
Addresses []string

View File

@@ -28,7 +28,7 @@ import (
)
var (
supportedAddressTypes = sets.NewString(string(discovery.AddressTypeIP))
supportedAddressTypes = sets.NewString(string(discovery.AddressTypeIP), string(discovery.AddressTypeFQDN))
supportedPortProtocols = sets.NewString(string(api.ProtocolTCP), string(api.ProtocolUDP), string(api.ProtocolSCTP))
maxTopologyLabels = 16
maxAddresses = 100
@@ -45,6 +45,8 @@ var ValidateEndpointSliceName = apimachineryvalidation.NameIsDNSSubdomain
func ValidateEndpointSlice(endpointSlice *discovery.EndpointSlice) field.ErrorList {
allErrs := apivalidation.ValidateObjectMeta(&endpointSlice.ObjectMeta, true, ValidateEndpointSliceName, field.NewPath("metadata"))
// AddressType should have had a default value set at this point, this is
// just a safety check if for some reason that changes or doesn't work.
addrType := discovery.AddressType("")
if endpointSlice.AddressType == nil {
allErrs = append(allErrs, field.Required(field.NewPath("addressType"), ""))
@@ -52,8 +54,8 @@ func ValidateEndpointSlice(endpointSlice *discovery.EndpointSlice) field.ErrorLi
addrType = *endpointSlice.AddressType
}
if endpointSlice.AddressType != nil && !supportedAddressTypes.Has(string(*endpointSlice.AddressType)) {
allErrs = append(allErrs, field.NotSupported(field.NewPath("addressType"), *endpointSlice.AddressType, supportedAddressTypes.List()))
if !supportedAddressTypes.Has(string(addrType)) {
allErrs = append(allErrs, field.NotSupported(field.NewPath("addressType"), addrType, supportedAddressTypes.List()))
}
allErrs = append(allErrs, validateEndpoints(endpointSlice.Endpoints, addrType, field.NewPath("endpoints"))...)
@@ -83,17 +85,20 @@ func validateEndpoints(endpoints []discovery.Endpoint, addrType discovery.Addres
idxPath := fldPath.Index(i)
addressPath := idxPath.Child("addresses")
if addrType == discovery.AddressTypeIP {
if len(endpoint.Addresses) == 0 {
allErrs = append(allErrs, field.Required(addressPath, "must contain at least 1 address"))
} else if len(endpoint.Addresses) > maxAddresses {
allErrs = append(allErrs, field.TooMany(addressPath, len(endpoint.Addresses), maxAddresses))
}
if len(endpoint.Addresses) == 0 {
allErrs = append(allErrs, field.Required(addressPath, "must contain at least 1 address"))
} else if len(endpoint.Addresses) > maxAddresses {
allErrs = append(allErrs, field.TooMany(addressPath, len(endpoint.Addresses), maxAddresses))
}
for i, address := range endpoint.Addresses {
for i, address := range endpoint.Addresses {
switch addrType {
case discovery.AddressTypeIP:
for _, msg := range validation.IsValidIP(address) {
allErrs = append(allErrs, field.Invalid(addressPath.Index(i), address, msg))
}
case discovery.AddressTypeFQDN:
allErrs = append(allErrs, validation.IsFullyQualifiedDomainName(addressPath.Index(i), address)...)
}
}

View File

@@ -46,7 +46,22 @@ func TestValidateEndpointSlice(t *testing.T) {
Protocol: protocolPtr(api.ProtocolTCP),
}},
Endpoints: []discovery.Endpoint{{
Addresses: generateAddresses(1),
Addresses: generateIPAddresses(1),
Hostname: utilpointer.StringPtr("valid-123"),
}},
},
},
"good-fqdns": {
expectedErrors: 0,
endpointSlice: &discovery.EndpointSlice{
ObjectMeta: standardMeta,
AddressType: addressTypePtr(discovery.AddressTypeFQDN),
Ports: []discovery.EndpointPort{{
Name: utilpointer.StringPtr("http"),
Protocol: protocolPtr(api.ProtocolTCP),
}},
Endpoints: []discovery.Endpoint{{
Addresses: []string{"foo.example.com", "example.com", "example.com.", "hyphens-are-good.example.com"},
Hostname: utilpointer.StringPtr("valid-123"),
}},
},
@@ -67,7 +82,7 @@ func TestValidateEndpointSlice(t *testing.T) {
Protocol: protocolPtr(api.ProtocolSCTP),
}},
Endpoints: []discovery.Endpoint{{
Addresses: generateAddresses(1),
Addresses: generateIPAddresses(1),
Hostname: utilpointer.StringPtr("valid-123"),
}},
},
@@ -85,7 +100,7 @@ func TestValidateEndpointSlice(t *testing.T) {
Protocol: protocolPtr(api.ProtocolTCP),
}},
Endpoints: []discovery.Endpoint{{
Addresses: generateAddresses(1),
Addresses: generateIPAddresses(1),
}},
},
},
@@ -125,7 +140,7 @@ func TestValidateEndpointSlice(t *testing.T) {
Protocol: protocolPtr(api.ProtocolTCP),
}},
Endpoints: []discovery.Endpoint{{
Addresses: generateAddresses(maxAddresses),
Addresses: generateIPAddresses(maxAddresses),
}},
},
},
@@ -139,7 +154,7 @@ func TestValidateEndpointSlice(t *testing.T) {
Protocol: protocolPtr(api.ProtocolTCP),
}},
Endpoints: []discovery.Endpoint{{
Addresses: generateAddresses(1),
Addresses: generateIPAddresses(1),
Topology: generateTopology(maxTopologyLabels),
}},
},
@@ -223,7 +238,7 @@ func TestValidateEndpointSlice(t *testing.T) {
Protocol: protocolPtr(api.ProtocolTCP),
}},
Endpoints: []discovery.Endpoint{{
Addresses: generateAddresses(0),
Addresses: generateIPAddresses(0),
}},
},
},
@@ -237,7 +252,7 @@ func TestValidateEndpointSlice(t *testing.T) {
Protocol: protocolPtr(api.ProtocolTCP),
}},
Endpoints: []discovery.Endpoint{{
Addresses: generateAddresses(maxAddresses + 1),
Addresses: generateIPAddresses(maxAddresses + 1),
}},
},
},
@@ -251,7 +266,7 @@ func TestValidateEndpointSlice(t *testing.T) {
Protocol: protocolPtr(api.ProtocolTCP),
}},
Endpoints: []discovery.Endpoint{{
Addresses: generateAddresses(1),
Addresses: generateIPAddresses(1),
}},
},
},
@@ -265,7 +280,7 @@ func TestValidateEndpointSlice(t *testing.T) {
Protocol: protocolPtr(api.ProtocolTCP),
}},
Endpoints: []discovery.Endpoint{{
Addresses: generateAddresses(1),
Addresses: generateIPAddresses(1),
Topology: map[string]string{"--INVALID": "example"},
}},
},
@@ -280,7 +295,7 @@ func TestValidateEndpointSlice(t *testing.T) {
Protocol: protocolPtr(api.ProtocolTCP),
}},
Endpoints: []discovery.Endpoint{{
Addresses: generateAddresses(1),
Addresses: generateIPAddresses(1),
Topology: generateTopology(maxTopologyLabels + 1),
}},
},
@@ -295,7 +310,7 @@ func TestValidateEndpointSlice(t *testing.T) {
Protocol: protocolPtr(api.ProtocolTCP),
}},
Endpoints: []discovery.Endpoint{{
Addresses: generateAddresses(1),
Addresses: generateIPAddresses(1),
Hostname: utilpointer.StringPtr("--INVALID"),
}},
},
@@ -313,14 +328,46 @@ func TestValidateEndpointSlice(t *testing.T) {
Protocol: protocolPtr(api.ProtocolTCP),
}},
Endpoints: []discovery.Endpoint{{
Addresses: generateAddresses(1),
Addresses: generateIPAddresses(1),
Hostname: utilpointer.StringPtr("valid-123"),
}},
},
},
"bad-ip": {
expectedErrors: 1,
endpointSlice: &discovery.EndpointSlice{
ObjectMeta: standardMeta,
AddressType: addressTypePtr(discovery.AddressTypeIP),
Ports: []discovery.EndpointPort{{
Name: utilpointer.StringPtr("http"),
Protocol: protocolPtr(api.ProtocolTCP),
}},
Endpoints: []discovery.Endpoint{{
Addresses: []string{"123.456.789.012"},
Hostname: utilpointer.StringPtr("valid-123"),
}},
},
},
"bad-fqdns": {
expectedErrors: 4,
endpointSlice: &discovery.EndpointSlice{
ObjectMeta: standardMeta,
AddressType: addressTypePtr(discovery.AddressTypeFQDN),
Ports: []discovery.EndpointPort{{
Name: utilpointer.StringPtr("http"),
Protocol: protocolPtr(api.ProtocolTCP),
}},
Endpoints: []discovery.Endpoint{{
Addresses: []string{"foo.*", "FOO.example.com", "underscores_are_bad.example.com", "*.example.com"},
Hostname: utilpointer.StringPtr("valid-123"),
}},
},
},
"empty-everything": {
expectedErrors: 3,
endpointSlice: &discovery.EndpointSlice{},
endpointSlice: &discovery.EndpointSlice{
AddressType: addressTypePtr(""),
},
},
}
@@ -422,7 +469,7 @@ func generateEndpoints(n int) []discovery.Endpoint {
return endpoints
}
func generateAddresses(n int) []string {
func generateIPAddresses(n int) []string {
addresses := []string{}
for i := 0; i < n; i++ {
addresses = append(addresses, fmt.Sprintf("10.1.2.%d", i%255))