Splitting IP address type into IPv4 and IPv6 for EndpointSlices

This commit is contained in:
Rob Scott 2019-11-06 22:46:04 -08:00
parent bcb171b375
commit 0fa9981e01
No known key found for this signature in database
GPG Key ID: 53504C654CF4B3EE
36 changed files with 707 additions and 350 deletions

View File

@ -12025,7 +12025,7 @@
"description": "Endpoint represents a single logical \"backend\" implementing a service.",
"properties": {
"addresses": {
"description": "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 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.",
"description": "addresses of this endpoint. The contents of this field are interpreted according to the corresponding EndpointSlice addressType field. Consumers 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.",
"items": {
"type": "string"
},
@ -12094,7 +12094,7 @@
"description": "EndpointSlice represents a subset of the endpoints that implement a service. For a given service there may be multiple EndpointSlice objects, selected by labels, which must be joined to produce the full set of endpoints.",
"properties": {
"addressType": {
"description": "addressType specifies the type of address carried by this EndpointSlice. 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\n addresses.\n* FQDN: Represents a Fully Qualified Domain Name. Default is IP",
"description": "addressType specifies the type of address carried by this EndpointSlice. All addresses in this slice must be the same type. This field is immutable after creation. The following address types are currently supported: * IPv4: Represents an IPv4 Address. * IPv6: Represents an IPv6 Address. * FQDN: Represents a Fully Qualified Domain Name.",
"type": "string"
},
"apiVersion": {
@ -12127,6 +12127,7 @@
}
},
"required": [
"addressType",
"endpoints"
],
"type": "object",

View File

@ -30,11 +30,8 @@ var Funcs = func(codecs runtimeserializer.CodecFactory) []interface{} {
func(obj *discovery.EndpointSlice, c fuzz.Continue) {
c.FuzzNoCustom(obj) // fuzz self without calling this function again
// match defaults
if obj.AddressType == nil {
ipAddressType := discovery.AddressTypeIP
obj.AddressType = &ipAddressType
}
addressTypes := []discovery.AddressType{discovery.AddressTypeIPv4, discovery.AddressTypeIPv6, discovery.AddressTypeFQDN}
obj.AddressType = addressTypes[c.Rand.Intn(len(addressTypes))]
for i, endpointPort := range obj.Ports {
if endpointPort.Name == nil {

View File

@ -32,13 +32,13 @@ 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. The following address
// types are currently supported:
// * IP: Represents an IP Address. This can include both IPv4 and IPv6
// addresses.
// All addresses in this slice must be the same type. This field is
// immutable after creation. The following address types are currently
// supported:
// * IPv4: Represents an IPv4 Address.
// * IPv6: Represents an IPv6 Address.
// * FQDN: Represents a Fully Qualified Domain Name.
// +optional
AddressType *AddressType
AddressType AddressType
// endpoints is a list of unique endpoints in this slice. Each slice may
// include a maximum of 1000 endpoints.
// +listType=atomic
@ -57,22 +57,27 @@ type EndpointSlice struct {
type AddressType string
const (
// AddressTypeIP represents an IP Address. Inclusive of IPv4 and IPv6
// addresses.
// 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")
// AddressTypeFQDN represents a Fully Qualified Domain Name.
// AddressTypeIPv4 represents an IPv4 Address.
AddressTypeIPv4 = AddressType(api.IPv4Protocol)
// AddressTypeIPv6 represents an IPv6 Address.
AddressTypeIPv6 = AddressType(api.IPv6Protocol)
// AddressTypeFQDN represents a FQDN.
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 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.
// according to the corresponding EndpointSlice addressType field. Consumers
// 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
// conditions contains information about the current status of the endpoint.

View File

@ -23,21 +23,14 @@ import (
)
var (
defaultAddressType = discoveryv1alpha1.AddressTypeIP
defaultPortName = ""
defaultProtocol = v1.ProtocolTCP
defaultPortName = ""
defaultProtocol = v1.ProtocolTCP
)
func addDefaultingFuncs(scheme *runtime.Scheme) error {
return RegisterDefaults(scheme)
}
func SetDefaults_EndpointSlice(obj *discoveryv1alpha1.EndpointSlice) {
if obj.AddressType == nil {
obj.AddressType = &defaultAddressType
}
}
func SetDefaults_EndpointPort(obj *discoveryv1alpha1.EndpointPort) {
if obj.Name == nil {
obj.Name = &defaultPortName

View File

@ -33,8 +33,6 @@ func TestSetDefaultEndpointPort(t *testing.T) {
fooStr := "foo"
protoTCP := v1.ProtocolTCP
protoUDP := v1.ProtocolUDP
ipAddressType := discovery.AddressTypeIP
otherAddressType := discovery.AddressType("other")
tests := map[string]struct {
original *discovery.EndpointSlice
@ -45,7 +43,6 @@ func TestSetDefaultEndpointPort(t *testing.T) {
Port: utilpointer.Int32Ptr(80),
}}},
expected: &discovery.EndpointSlice{
AddressType: &ipAddressType,
Ports: []discovery.EndpointPort{{
Name: &emptyStr,
Protocol: &protoTCP,
@ -55,14 +52,12 @@ func TestSetDefaultEndpointPort(t *testing.T) {
},
"should not overwrite values with defaults when set": {
original: &discovery.EndpointSlice{
AddressType: &otherAddressType,
Ports: []discovery.EndpointPort{{
Name: &fooStr,
Protocol: &protoUDP,
}},
},
expected: &discovery.EndpointSlice{
AddressType: &otherAddressType,
Ports: []discovery.EndpointPort{{
Name: &fooStr,
Protocol: &protoUDP,

View File

@ -171,7 +171,7 @@ func Convert_discovery_EndpointPort_To_v1alpha1_EndpointPort(in *discovery.Endpo
func autoConvert_v1alpha1_EndpointSlice_To_discovery_EndpointSlice(in *v1alpha1.EndpointSlice, out *discovery.EndpointSlice, s conversion.Scope) error {
out.ObjectMeta = in.ObjectMeta
out.AddressType = (*discovery.AddressType)(unsafe.Pointer(in.AddressType))
out.AddressType = discovery.AddressType(in.AddressType)
out.Endpoints = *(*[]discovery.Endpoint)(unsafe.Pointer(&in.Endpoints))
out.Ports = *(*[]discovery.EndpointPort)(unsafe.Pointer(&in.Ports))
return nil
@ -184,7 +184,7 @@ func Convert_v1alpha1_EndpointSlice_To_discovery_EndpointSlice(in *v1alpha1.Endp
func autoConvert_discovery_EndpointSlice_To_v1alpha1_EndpointSlice(in *discovery.EndpointSlice, out *v1alpha1.EndpointSlice, s conversion.Scope) error {
out.ObjectMeta = in.ObjectMeta
out.AddressType = (*v1alpha1.AddressType)(unsafe.Pointer(in.AddressType))
out.AddressType = v1alpha1.AddressType(in.AddressType)
out.Endpoints = *(*[]v1alpha1.Endpoint)(unsafe.Pointer(&in.Endpoints))
out.Ports = *(*[]v1alpha1.EndpointPort)(unsafe.Pointer(&in.Ports))
return nil

View File

@ -35,7 +35,6 @@ func RegisterDefaults(scheme *runtime.Scheme) error {
}
func SetObjectDefaults_EndpointSlice(in *v1alpha1.EndpointSlice) {
SetDefaults_EndpointSlice(in)
for i := range in.Ports {
a := &in.Ports[i]
SetDefaults_EndpointPort(a)

View File

@ -28,12 +28,23 @@ import (
)
var (
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
maxPorts = 100
maxEndpoints = 1000
supportedAddressTypes = sets.NewString(
string(discovery.AddressTypeIPv4),
string(discovery.AddressTypeIPv6),
string(discovery.AddressTypeFQDN),
)
deprecatedAddressTypes = sets.NewString(
string(discovery.AddressTypeIP),
)
supportedPortProtocols = sets.NewString(
string(api.ProtocolTCP),
string(api.ProtocolUDP),
string(api.ProtocolSCTP),
)
maxTopologyLabels = 16
maxAddresses = 100
maxPorts = 100
maxEndpoints = 1000
)
// ValidateEndpointSliceName can be used to check whether the given endpoint
@ -42,33 +53,24 @@ var (
var ValidateEndpointSliceName = apimachineryvalidation.NameIsDNSSubdomain
// ValidateEndpointSlice validates an EndpointSlice.
func ValidateEndpointSlice(endpointSlice *discovery.EndpointSlice) field.ErrorList {
func ValidateEndpointSlice(endpointSlice *discovery.EndpointSlice, validAddressTypes sets.String) 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"), ""))
} else {
addrType = *endpointSlice.AddressType
}
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"))...)
allErrs = append(allErrs, validateAddressType(endpointSlice.AddressType, validAddressTypes)...)
allErrs = append(allErrs, validateEndpoints(endpointSlice.Endpoints, endpointSlice.AddressType, 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)
}
// ValidateEndpointSliceUpdate validates an EndpointSlice when it is updated.
func ValidateEndpointSliceUpdate(newEndpointSlice, oldEndpointSlice *discovery.EndpointSlice) field.ErrorList {
allErrs := ValidateEndpointSlice(newEndpointSlice)
allErrs = append(allErrs, apivalidation.ValidateImmutableField(*newEndpointSlice.AddressType, *oldEndpointSlice.AddressType, field.NewPath("addressType"))...)
allErrs := ValidateEndpointSlice(newEndpointSlice, supportedAddressTypes.Union(deprecatedAddressTypes))
allErrs = append(allErrs, apivalidation.ValidateImmutableField(newEndpointSlice.AddressType, oldEndpointSlice.AddressType, field.NewPath("addressType"))...)
return allErrs
}
@ -92,11 +94,17 @@ func validateEndpoints(endpoints []discovery.Endpoint, addrType discovery.Addres
}
for i, address := range endpoint.Addresses {
// 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:
allErrs = append(allErrs, validation.IsValidIPv6Address(addressPath.Index(i), address)...)
case discovery.AddressTypeFQDN:
allErrs = append(allErrs, validation.IsFullyQualifiedDomainName(addressPath.Index(i), address)...)
}
@ -153,3 +161,15 @@ func validatePorts(endpointPorts []discovery.EndpointPort, fldPath *field.Path)
return allErrs
}
func validateAddressType(addressType discovery.AddressType, validAddressTypes sets.String) 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()))
}
return allErrs
}

View File

@ -41,7 +41,7 @@ func TestValidateEndpointSlice(t *testing.T) {
expectedErrors: 0,
endpointSlice: &discovery.EndpointSlice{
ObjectMeta: standardMeta,
AddressType: addressTypePtr(discovery.AddressTypeIP),
AddressType: discovery.AddressTypeIPv4,
Ports: []discovery.EndpointPort{{
Name: utilpointer.StringPtr("http"),
Protocol: protocolPtr(api.ProtocolTCP),
@ -56,7 +56,7 @@ func TestValidateEndpointSlice(t *testing.T) {
expectedErrors: 0,
endpointSlice: &discovery.EndpointSlice{
ObjectMeta: standardMeta,
AddressType: addressTypePtr(discovery.AddressTypeFQDN),
AddressType: discovery.AddressTypeFQDN,
Ports: []discovery.EndpointPort{{
Name: utilpointer.StringPtr("http"),
Protocol: protocolPtr(api.ProtocolTCP),
@ -71,7 +71,7 @@ func TestValidateEndpointSlice(t *testing.T) {
expectedErrors: 0,
endpointSlice: &discovery.EndpointSlice{
ObjectMeta: standardMeta,
AddressType: addressTypePtr(discovery.AddressTypeIP),
AddressType: discovery.AddressTypeIPv4,
Ports: []discovery.EndpointPort{{
Name: utilpointer.StringPtr("tcp"),
Protocol: protocolPtr(api.ProtocolTCP),
@ -92,7 +92,7 @@ func TestValidateEndpointSlice(t *testing.T) {
expectedErrors: 0,
endpointSlice: &discovery.EndpointSlice{
ObjectMeta: standardMeta,
AddressType: addressTypePtr(discovery.AddressTypeIP),
AddressType: discovery.AddressTypeIP,
Ports: []discovery.EndpointPort{{
Name: utilpointer.StringPtr("one"),
Protocol: protocolPtr(api.ProtocolTCP),
@ -120,7 +120,7 @@ func TestValidateEndpointSlice(t *testing.T) {
expectedErrors: 0,
endpointSlice: &discovery.EndpointSlice{
ObjectMeta: standardMeta,
AddressType: addressTypePtr(discovery.AddressTypeIP),
AddressType: discovery.AddressTypeIPv4,
Ports: []discovery.EndpointPort{{
Name: utilpointer.StringPtr(""),
Protocol: protocolPtr(api.ProtocolTCP),
@ -137,7 +137,7 @@ func TestValidateEndpointSlice(t *testing.T) {
expectedErrors: 0,
endpointSlice: &discovery.EndpointSlice{
ObjectMeta: standardMeta,
AddressType: addressTypePtr(discovery.AddressTypeIP),
AddressType: discovery.AddressTypeIPv4,
Ports: []discovery.EndpointPort{{
Name: utilpointer.StringPtr(strings.Repeat("a", 63)),
Protocol: protocolPtr(api.ProtocolTCP),
@ -151,7 +151,7 @@ func TestValidateEndpointSlice(t *testing.T) {
expectedErrors: 0,
endpointSlice: &discovery.EndpointSlice{
ObjectMeta: standardMeta,
AddressType: addressTypePtr(discovery.AddressTypeIP),
AddressType: discovery.AddressTypeIPv4,
Ports: []discovery.EndpointPort{},
Endpoints: []discovery.Endpoint{},
},
@ -160,7 +160,7 @@ func TestValidateEndpointSlice(t *testing.T) {
expectedErrors: 0,
endpointSlice: &discovery.EndpointSlice{
ObjectMeta: standardMeta,
AddressType: addressTypePtr(discovery.AddressTypeIP),
AddressType: discovery.AddressTypeIPv4,
Ports: generatePorts(1),
Endpoints: generateEndpoints(maxEndpoints),
},
@ -169,7 +169,7 @@ func TestValidateEndpointSlice(t *testing.T) {
expectedErrors: 0,
endpointSlice: &discovery.EndpointSlice{
ObjectMeta: standardMeta,
AddressType: addressTypePtr(discovery.AddressTypeIP),
AddressType: discovery.AddressTypeIPv4,
Ports: generatePorts(maxPorts),
},
},
@ -177,7 +177,7 @@ func TestValidateEndpointSlice(t *testing.T) {
expectedErrors: 0,
endpointSlice: &discovery.EndpointSlice{
ObjectMeta: standardMeta,
AddressType: addressTypePtr(discovery.AddressTypeIP),
AddressType: discovery.AddressTypeIPv4,
Ports: []discovery.EndpointPort{{
Name: utilpointer.StringPtr("http"),
Protocol: protocolPtr(api.ProtocolTCP),
@ -191,7 +191,7 @@ func TestValidateEndpointSlice(t *testing.T) {
expectedErrors: 0,
endpointSlice: &discovery.EndpointSlice{
ObjectMeta: standardMeta,
AddressType: addressTypePtr(discovery.AddressTypeIP),
AddressType: discovery.AddressTypeIPv4,
Ports: []discovery.EndpointPort{{
Name: utilpointer.StringPtr("http"),
Protocol: protocolPtr(api.ProtocolTCP),
@ -208,7 +208,7 @@ func TestValidateEndpointSlice(t *testing.T) {
expectedErrors: 1,
endpointSlice: &discovery.EndpointSlice{
ObjectMeta: standardMeta,
AddressType: addressTypePtr(discovery.AddressTypeIP),
AddressType: discovery.AddressTypeIPv4,
Ports: []discovery.EndpointPort{{
Name: utilpointer.StringPtr(""),
Protocol: protocolPtr(api.ProtocolTCP),
@ -223,7 +223,7 @@ func TestValidateEndpointSlice(t *testing.T) {
expectedErrors: 1,
endpointSlice: &discovery.EndpointSlice{
ObjectMeta: standardMeta,
AddressType: addressTypePtr(discovery.AddressTypeIP),
AddressType: discovery.AddressTypeIPv4,
Ports: []discovery.EndpointPort{{
Name: utilpointer.StringPtr("aCapital"),
Protocol: protocolPtr(api.ProtocolTCP),
@ -235,7 +235,7 @@ func TestValidateEndpointSlice(t *testing.T) {
expectedErrors: 1,
endpointSlice: &discovery.EndpointSlice{
ObjectMeta: standardMeta,
AddressType: addressTypePtr(discovery.AddressTypeIP),
AddressType: discovery.AddressTypeIPv4,
Ports: []discovery.EndpointPort{{
Name: utilpointer.StringPtr("almost_valid"),
Protocol: protocolPtr(api.ProtocolTCP),
@ -247,7 +247,7 @@ func TestValidateEndpointSlice(t *testing.T) {
expectedErrors: 1,
endpointSlice: &discovery.EndpointSlice{
ObjectMeta: standardMeta,
AddressType: addressTypePtr(discovery.AddressTypeIP),
AddressType: discovery.AddressTypeIPv4,
Ports: []discovery.EndpointPort{{
Name: utilpointer.StringPtr(strings.Repeat("a", 64)),
Protocol: protocolPtr(api.ProtocolTCP),
@ -259,7 +259,7 @@ func TestValidateEndpointSlice(t *testing.T) {
expectedErrors: 1,
endpointSlice: &discovery.EndpointSlice{
ObjectMeta: standardMeta,
AddressType: addressTypePtr(discovery.AddressTypeIP),
AddressType: discovery.AddressTypeIPv4,
Ports: []discovery.EndpointPort{{
Name: utilpointer.StringPtr("http"),
Protocol: protocolPtr(api.Protocol("foo")),
@ -270,7 +270,7 @@ func TestValidateEndpointSlice(t *testing.T) {
expectedErrors: 1,
endpointSlice: &discovery.EndpointSlice{
ObjectMeta: standardMeta,
AddressType: addressTypePtr(discovery.AddressTypeIP),
AddressType: discovery.AddressTypeIPv4,
Ports: generatePorts(maxPorts + 1),
},
},
@ -278,7 +278,7 @@ func TestValidateEndpointSlice(t *testing.T) {
expectedErrors: 1,
endpointSlice: &discovery.EndpointSlice{
ObjectMeta: standardMeta,
AddressType: addressTypePtr(discovery.AddressTypeIP),
AddressType: discovery.AddressTypeIPv4,
Ports: generatePorts(1),
Endpoints: generateEndpoints(maxEndpoints + 1),
},
@ -287,7 +287,7 @@ func TestValidateEndpointSlice(t *testing.T) {
expectedErrors: 1,
endpointSlice: &discovery.EndpointSlice{
ObjectMeta: standardMeta,
AddressType: addressTypePtr(discovery.AddressTypeIP),
AddressType: discovery.AddressTypeIPv4,
Ports: []discovery.EndpointPort{{
Name: utilpointer.StringPtr("http"),
Protocol: protocolPtr(api.ProtocolTCP),
@ -301,7 +301,7 @@ func TestValidateEndpointSlice(t *testing.T) {
expectedErrors: 1,
endpointSlice: &discovery.EndpointSlice{
ObjectMeta: standardMeta,
AddressType: addressTypePtr(discovery.AddressTypeIP),
AddressType: discovery.AddressTypeIPv4,
Ports: []discovery.EndpointPort{{
Name: utilpointer.StringPtr("http"),
Protocol: protocolPtr(api.ProtocolTCP),
@ -311,25 +311,11 @@ func TestValidateEndpointSlice(t *testing.T) {
}},
},
},
"bad-address-type": {
expectedErrors: 1,
endpointSlice: &discovery.EndpointSlice{
ObjectMeta: standardMeta,
AddressType: addressTypePtr(discovery.AddressType("other")),
Ports: []discovery.EndpointPort{{
Name: utilpointer.StringPtr("http"),
Protocol: protocolPtr(api.ProtocolTCP),
}},
Endpoints: []discovery.Endpoint{{
Addresses: generateIPAddresses(1),
}},
},
},
"bad-topology-key": {
expectedErrors: 1,
endpointSlice: &discovery.EndpointSlice{
ObjectMeta: standardMeta,
AddressType: addressTypePtr(discovery.AddressTypeIP),
AddressType: discovery.AddressTypeIPv4,
Ports: []discovery.EndpointPort{{
Name: utilpointer.StringPtr("http"),
Protocol: protocolPtr(api.ProtocolTCP),
@ -344,7 +330,7 @@ func TestValidateEndpointSlice(t *testing.T) {
expectedErrors: 1,
endpointSlice: &discovery.EndpointSlice{
ObjectMeta: standardMeta,
AddressType: addressTypePtr(discovery.AddressTypeIP),
AddressType: discovery.AddressTypeIPv4,
Ports: []discovery.EndpointPort{{
Name: utilpointer.StringPtr("http"),
Protocol: protocolPtr(api.ProtocolTCP),
@ -359,7 +345,7 @@ func TestValidateEndpointSlice(t *testing.T) {
expectedErrors: 1,
endpointSlice: &discovery.EndpointSlice{
ObjectMeta: standardMeta,
AddressType: addressTypePtr(discovery.AddressTypeIP),
AddressType: discovery.AddressTypeIPv4,
Ports: []discovery.EndpointPort{{
Name: utilpointer.StringPtr("http"),
Protocol: protocolPtr(api.ProtocolTCP),
@ -377,7 +363,7 @@ func TestValidateEndpointSlice(t *testing.T) {
Name: "*&^",
Namespace: "foo",
},
AddressType: addressTypePtr(discovery.AddressTypeIP),
AddressType: discovery.AddressTypeIPv4,
Ports: []discovery.EndpointPort{{
Name: utilpointer.StringPtr("http"),
Protocol: protocolPtr(api.ProtocolTCP),
@ -392,7 +378,7 @@ func TestValidateEndpointSlice(t *testing.T) {
expectedErrors: 1,
endpointSlice: &discovery.EndpointSlice{
ObjectMeta: standardMeta,
AddressType: addressTypePtr(discovery.AddressTypeIP),
AddressType: discovery.AddressTypeIP,
Ports: []discovery.EndpointPort{{
Name: utilpointer.StringPtr("http"),
Protocol: protocolPtr(api.ProtocolTCP),
@ -403,11 +389,41 @@ func TestValidateEndpointSlice(t *testing.T) {
}},
},
},
"bad-ipv4": {
expectedErrors: 2,
endpointSlice: &discovery.EndpointSlice{
ObjectMeta: standardMeta,
AddressType: discovery.AddressTypeIPv4,
Ports: []discovery.EndpointPort{{
Name: utilpointer.StringPtr("http"),
Protocol: protocolPtr(api.ProtocolTCP),
}},
Endpoints: []discovery.Endpoint{{
Addresses: []string{"123.456.789.012", "2001:4860:4860::8888"},
Hostname: utilpointer.StringPtr("valid-123"),
}},
},
},
"bad-ipv6": {
expectedErrors: 2,
endpointSlice: &discovery.EndpointSlice{
ObjectMeta: standardMeta,
AddressType: discovery.AddressTypeIPv6,
Ports: []discovery.EndpointPort{{
Name: utilpointer.StringPtr("http"),
Protocol: protocolPtr(api.ProtocolTCP),
}},
Endpoints: []discovery.Endpoint{{
Addresses: []string{"123.456.789.012", "2001:4860:4860:defg"},
Hostname: utilpointer.StringPtr("valid-123"),
}},
},
},
"bad-fqdns": {
expectedErrors: 4,
endpointSlice: &discovery.EndpointSlice{
ObjectMeta: standardMeta,
AddressType: addressTypePtr(discovery.AddressTypeFQDN),
AddressType: discovery.AddressTypeFQDN,
Ports: []discovery.EndpointPort{{
Name: utilpointer.StringPtr("http"),
Protocol: protocolPtr(api.ProtocolTCP),
@ -422,7 +438,7 @@ func TestValidateEndpointSlice(t *testing.T) {
expectedErrors: 1,
endpointSlice: &discovery.EndpointSlice{
ObjectMeta: standardMeta,
AddressType: addressTypePtr(discovery.AddressTypeIP),
AddressType: discovery.AddressTypeIP,
Ports: []discovery.EndpointPort{{
Name: utilpointer.StringPtr("http"),
Protocol: protocolPtr(api.ProtocolTCP),
@ -436,15 +452,80 @@ func TestValidateEndpointSlice(t *testing.T) {
},
"empty-everything": {
expectedErrors: 3,
endpointSlice: &discovery.EndpointSlice{},
},
}
for name, testCase := range testCases {
t.Run(name, func(t *testing.T) {
errs := ValidateEndpointSlice(testCase.endpointSlice, supportedAddressTypes.Union(deprecatedAddressTypes))
if len(errs) != testCase.expectedErrors {
t.Errorf("Expected %d errors, got %d errors: %v", testCase.expectedErrors, len(errs), errs)
}
})
}
}
func TestValidateEndpointSliceCreate(t *testing.T) {
standardMeta := metav1.ObjectMeta{
Name: "hello",
Namespace: "world",
}
testCases := map[string]struct {
expectedErrors int
endpointSlice *discovery.EndpointSlice
}{
"good-slice": {
expectedErrors: 0,
endpointSlice: &discovery.EndpointSlice{
AddressType: addressTypePtr(""),
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"),
}},
},
},
// expected failures
"deprecated-address-type": {
expectedErrors: 1,
endpointSlice: &discovery.EndpointSlice{
ObjectMeta: standardMeta,
AddressType: discovery.AddressTypeIP,
Ports: []discovery.EndpointPort{{
Name: utilpointer.StringPtr("http"),
Protocol: protocolPtr(api.ProtocolTCP),
}},
Endpoints: []discovery.Endpoint{{
Addresses: generateIPAddresses(1),
}},
},
},
"bad-address-type": {
expectedErrors: 1,
endpointSlice: &discovery.EndpointSlice{
ObjectMeta: standardMeta,
AddressType: discovery.AddressType("other"),
Ports: []discovery.EndpointPort{{
Name: utilpointer.StringPtr("http"),
Protocol: protocolPtr(api.ProtocolTCP),
}},
Endpoints: []discovery.Endpoint{{
Addresses: generateIPAddresses(1),
}},
},
},
}
for name, testCase := range testCases {
t.Run(name, func(t *testing.T) {
errs := ValidateEndpointSlice(testCase.endpointSlice)
errs := ValidateEndpointSliceCreate(testCase.endpointSlice)
if len(errs) != testCase.expectedErrors {
t.Errorf("Expected %d errors, got %d errors: %v", testCase.expectedErrors, len(errs), errs)
}
@ -463,29 +544,40 @@ func TestValidateEndpointSliceUpdate(t *testing.T) {
"valid and identical slices": {
newEndpointSlice: &discovery.EndpointSlice{
ObjectMeta: standardMeta,
AddressType: addressTypePtr(discovery.AddressTypeIP),
AddressType: discovery.AddressTypeIPv6,
},
oldEndpointSlice: &discovery.EndpointSlice{
ObjectMeta: standardMeta,
AddressType: addressTypePtr(discovery.AddressTypeIP),
AddressType: discovery.AddressTypeIPv6,
},
expectedErrors: 0,
},
"deprecated address type": {
expectedErrors: 0,
newEndpointSlice: &discovery.EndpointSlice{
ObjectMeta: standardMeta,
AddressType: discovery.AddressTypeIP,
},
oldEndpointSlice: &discovery.EndpointSlice{
ObjectMeta: standardMeta,
AddressType: discovery.AddressTypeIP,
},
},
"valid and identical slices with different address types": {
newEndpointSlice: &discovery.EndpointSlice{
ObjectMeta: standardMeta,
AddressType: addressTypePtr(discovery.AddressTypeIP),
AddressType: discovery.AddressTypeIP,
},
oldEndpointSlice: &discovery.EndpointSlice{
ObjectMeta: standardMeta,
AddressType: addressTypePtr(discovery.AddressType("other")),
AddressType: discovery.AddressType("other"),
},
expectedErrors: 1,
},
"invalid slices with valid address types": {
newEndpointSlice: &discovery.EndpointSlice{
ObjectMeta: standardMeta,
AddressType: addressTypePtr(discovery.AddressTypeIP),
AddressType: discovery.AddressTypeIP,
Ports: []discovery.EndpointPort{{
Name: utilpointer.StringPtr(""),
Protocol: protocolPtr(api.Protocol("invalid")),
@ -493,7 +585,7 @@ func TestValidateEndpointSliceUpdate(t *testing.T) {
},
oldEndpointSlice: &discovery.EndpointSlice{
ObjectMeta: standardMeta,
AddressType: addressTypePtr(discovery.AddressTypeIP),
AddressType: discovery.AddressTypeIP,
},
expectedErrors: 1,
},
@ -515,10 +607,6 @@ func protocolPtr(protocol api.Protocol) *api.Protocol {
return &protocol
}
func addressTypePtr(addressType discovery.AddressType) *discovery.AddressType {
return &addressType
}
func generatePorts(n int) []discovery.EndpointPort {
ports := []discovery.EndpointPort{}
for i := 0; i < n; i++ {

View File

@ -126,11 +126,6 @@ func (in *EndpointSlice) DeepCopyInto(out *EndpointSlice) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
if in.AddressType != nil {
in, out := &in.AddressType, &out.AddressType
*out = new(AddressType)
**out = **in
}
if in.Endpoints != nil {
in, out := &in.Endpoints, &out.Endpoints
*out = make([]Endpoint, len(*in))

View File

@ -41,6 +41,7 @@ go_library(
"//staging/src/k8s.io/client-go/util/workqueue:go_default_library",
"//staging/src/k8s.io/component-base/metrics/prometheus/ratelimiter:go_default_library",
"//vendor/k8s.io/klog:go_default_library",
"//vendor/k8s.io/utils/net:go_default_library",
],
)

View File

@ -198,6 +198,7 @@ func TestSyncServiceEndpointSliceLabelSelection(t *testing.T) {
discovery.LabelManagedBy: controllerName,
},
},
AddressType: discovery.AddressTypeIPv4,
}, {
ObjectMeta: metav1.ObjectMeta{
Name: "matching-2",
@ -207,6 +208,7 @@ func TestSyncServiceEndpointSliceLabelSelection(t *testing.T) {
discovery.LabelManagedBy: controllerName,
},
},
AddressType: discovery.AddressTypeIPv4,
}, {
ObjectMeta: metav1.ObjectMeta{
Name: "partially-matching-1",
@ -215,6 +217,7 @@ func TestSyncServiceEndpointSliceLabelSelection(t *testing.T) {
discovery.LabelServiceName: serviceName,
},
},
AddressType: discovery.AddressTypeIPv4,
}, {
ObjectMeta: metav1.ObjectMeta{
Name: "not-matching-1",
@ -224,6 +227,7 @@ func TestSyncServiceEndpointSliceLabelSelection(t *testing.T) {
discovery.LabelManagedBy: controllerName,
},
},
AddressType: discovery.AddressTypeIPv4,
}, {
ObjectMeta: metav1.ObjectMeta{
Name: "not-matching-2",
@ -233,6 +237,7 @@ func TestSyncServiceEndpointSliceLabelSelection(t *testing.T) {
discovery.LabelManagedBy: "something-else",
},
},
AddressType: discovery.AddressTypeIPv4,
}}
// need to add them to both store and fake clientset
@ -272,16 +277,13 @@ func TestSyncServiceFull(t *testing.T) {
client, esController := newController([]string{"node-1"})
namespace := metav1.NamespaceDefault
serviceName := "all-the-protocols"
ipv6Family := v1.IPv6Protocol
// pod 1 only uses PodIP status attr
pod1 := newPod(1, namespace, true, 0)
pod1.Status.PodIP = "1.2.3.4"
pod1.Status.PodIPs = []v1.PodIP{}
pod1.Status.PodIPs = []v1.PodIP{{IP: "1.2.3.4"}}
esController.podStore.Add(pod1)
// pod 2 only uses PodIPs status attr
pod2 := newPod(2, namespace, true, 0)
pod2.Status.PodIP = ""
pod2.Status.PodIPs = []v1.PodIP{{IP: "1.2.3.5"}, {IP: "1234::5678:0000:0000:9abc:def0"}}
esController.podStore.Add(pod2)
@ -300,6 +302,7 @@ func TestSyncServiceFull(t *testing.T) {
{Name: "sctp-example", TargetPort: intstr.FromInt(3456), Protocol: v1.ProtocolSCTP},
},
Selector: map[string]string{"foo": "bar"},
IPFamily: &ipv6Family,
},
}
esController.serviceStore.Add(service)
@ -318,7 +321,7 @@ func TestSyncServiceFull(t *testing.T) {
// ensure all attributes of endpoint slice match expected state
slice := sliceList.Items[0]
assert.Len(t, slice.Endpoints, 2, "Expected 2 endpoints in first slice")
assert.Len(t, slice.Endpoints, 1, "Expected 1 endpoints in first slice")
assert.Equal(t, slice.Annotations["endpoints.kubernetes.io/last-change-trigger-time"], serviceCreateTime.Format(time.RFC3339Nano))
assert.ElementsMatch(t, []discovery.EndpointPort{{
Name: strPtr("tcp-example"),
@ -336,12 +339,7 @@ func TestSyncServiceFull(t *testing.T) {
assert.ElementsMatch(t, []discovery.Endpoint{{
Conditions: discovery.EndpointConditions{Ready: utilpointer.BoolPtr(true)},
Addresses: []string{"1.2.3.4"},
TargetRef: &v1.ObjectReference{Kind: "Pod", Namespace: namespace, Name: pod1.Name},
Topology: map[string]string{"kubernetes.io/hostname": "node-1"},
}, {
Conditions: discovery.EndpointConditions{Ready: utilpointer.BoolPtr(true)},
Addresses: []string{"1.2.3.5", "1234::5678:0000:0000:9abc:def0"},
Addresses: []string{"1234::5678:0000:0000:9abc:def0"},
TargetRef: &v1.ObjectReference{Kind: "Pod", Namespace: namespace, Name: pod2.Name},
Topology: map[string]string{"kubernetes.io/hostname": "node-1"},
}}, slice.Endpoints)

View File

@ -47,7 +47,7 @@ type reconciler struct {
// that logic in reconciler
type endpointMeta struct {
Ports []discovery.EndpointPort `json:"ports" protobuf:"bytes,2,rep,name=ports"`
AddressType *discovery.AddressType `json:"addressType" protobuf:"bytes,3,rep,name=addressType"`
AddressType discovery.AddressType `json:"addressType" protobuf:"bytes,3,rep,name=addressType"`
}
// reconcile takes a set of pods currently matching a service selector and
@ -55,13 +55,26 @@ type endpointMeta struct {
// slices for the given service. It creates, updates, or deletes endpoint slices
// to ensure the desired set of pods are represented by endpoint slices.
func (r *reconciler) reconcile(service *corev1.Service, pods []*corev1.Pod, existingSlices []*discovery.EndpointSlice, triggerTime time.Time) error {
addressType := discovery.AddressTypeIPv4
if service.Spec.IPFamily != nil && *service.Spec.IPFamily == corev1.IPv6Protocol {
addressType = discovery.AddressTypeIPv6
}
slicesToCreate := []*discovery.EndpointSlice{}
slicesToUpdate := []*discovery.EndpointSlice{}
slicesToDelete := []*discovery.EndpointSlice{}
// Build data structures for existing state.
existingSlicesByPortMap := map[endpointutil.PortMapKey][]*discovery.EndpointSlice{}
numExistingEndpoints := 0
for _, existingSlice := range existingSlices {
epHash := endpointutil.NewPortMapKey(existingSlice.Ports)
existingSlicesByPortMap[epHash] = append(existingSlicesByPortMap[epHash], existingSlice)
numExistingEndpoints += len(existingSlice.Endpoints)
if existingSlice.AddressType == addressType {
epHash := endpointutil.NewPortMapKey(existingSlice.Ports)
existingSlicesByPortMap[epHash] = append(existingSlicesByPortMap[epHash], existingSlice)
numExistingEndpoints += len(existingSlice.Endpoints)
} else {
slicesToDelete = append(slicesToDelete, existingSlice)
}
}
// Build data structures for desired state.
@ -78,10 +91,8 @@ func (r *reconciler) reconcile(service *corev1.Service, pods []*corev1.Pod, exis
}
if _, ok := desiredMetaByPortMap[epHash]; !ok {
// TODO: Support multiple backend types
ipAddressType := discovery.AddressTypeIP
desiredMetaByPortMap[epHash] = &endpointMeta{
AddressType: &ipAddressType,
AddressType: addressType,
Ports: endpointPorts,
}
}
@ -90,16 +101,14 @@ func (r *reconciler) reconcile(service *corev1.Service, pods []*corev1.Pod, exis
if err != nil {
return err
}
endpoint := podToEndpoint(pod, node, service)
desiredEndpointsByPortMap[epHash].Insert(&endpoint)
numDesiredEndpoints++
if len(endpoint.Addresses) > 0 {
desiredEndpointsByPortMap[epHash].Insert(&endpoint)
numDesiredEndpoints++
}
}
}
slicesToCreate := []*discovery.EndpointSlice{}
slicesToUpdate := []*discovery.EndpointSlice{}
sliceNamesToDelete := sets.String{}
spMetrics := metrics.NewServicePortCache()
totalAdded := 0
totalRemoved := 0
@ -107,7 +116,7 @@ func (r *reconciler) reconcile(service *corev1.Service, pods []*corev1.Pod, exis
// Determine changes necessary for each group of slices by port map.
for portMap, desiredEndpoints := range desiredEndpointsByPortMap {
numEndpoints := len(desiredEndpoints)
pmSlicesToCreate, pmSlicesToUpdate, pmSliceNamesToDelete, added, removed := r.reconcileByPortMapping(
pmSlicesToCreate, pmSlicesToUpdate, pmSlicesToDelete, added, removed := r.reconcileByPortMapping(
service, existingSlicesByPortMap[portMap], desiredEndpoints, desiredMetaByPortMap[portMap])
totalAdded += added
@ -115,7 +124,7 @@ func (r *reconciler) reconcile(service *corev1.Service, pods []*corev1.Pod, exis
spMetrics.Set(portMap, metrics.EfficiencyInfo{
Endpoints: numEndpoints,
Slices: len(existingSlicesByPortMap[portMap]) + len(pmSlicesToCreate) - len(pmSliceNamesToDelete),
Slices: len(existingSlicesByPortMap[portMap]) + len(pmSlicesToCreate) - len(pmSlicesToDelete),
})
if len(pmSlicesToCreate) > 0 {
@ -124,8 +133,8 @@ func (r *reconciler) reconcile(service *corev1.Service, pods []*corev1.Pod, exis
if len(pmSlicesToUpdate) > 0 {
slicesToUpdate = append(slicesToUpdate, pmSlicesToUpdate...)
}
if pmSliceNamesToDelete.Len() > 0 {
sliceNamesToDelete = sliceNamesToDelete.Union(pmSliceNamesToDelete)
if len(pmSlicesToDelete) > 0 {
slicesToDelete = append(slicesToDelete, pmSlicesToDelete...)
}
}
@ -134,14 +143,14 @@ func (r *reconciler) reconcile(service *corev1.Service, pods []*corev1.Pod, exis
for portMap, existingSlices := range existingSlicesByPortMap {
if _, ok := desiredEndpointsByPortMap[portMap]; !ok {
for _, existingSlice := range existingSlices {
sliceNamesToDelete.Insert(existingSlice.Name)
slicesToDelete = append(slicesToDelete, existingSlice)
}
}
}
// When no endpoint slices would usually exist, we need to add a placeholder.
if len(existingSlices) == sliceNamesToDelete.Len() && len(slicesToCreate) < 1 {
placeholderSlice := newEndpointSlice(service, &endpointMeta{Ports: []discovery.EndpointPort{}})
if len(existingSlices) == len(slicesToDelete) && len(slicesToCreate) < 1 {
placeholderSlice := newEndpointSlice(service, &endpointMeta{Ports: []discovery.EndpointPort{}, AddressType: addressType})
slicesToCreate = append(slicesToCreate, placeholderSlice)
spMetrics.Set(endpointutil.NewPortMapKey(placeholderSlice.Ports), metrics.EfficiencyInfo{
Endpoints: 0,
@ -155,27 +164,41 @@ func (r *reconciler) reconcile(service *corev1.Service, pods []*corev1.Pod, exis
serviceNN := types.NamespacedName{Name: service.Name, Namespace: service.Namespace}
r.metricsCache.UpdateServicePortCache(serviceNN, spMetrics)
return r.finalize(service, slicesToCreate, slicesToUpdate, sliceNamesToDelete, triggerTime)
return r.finalize(service, slicesToCreate, slicesToUpdate, slicesToDelete, triggerTime)
}
// finalize creates, updates, and deletes slices as specified
func (r *reconciler) finalize(
service *corev1.Service,
slicesToCreate,
slicesToUpdate []*discovery.EndpointSlice,
sliceNamesToDelete sets.String,
slicesToUpdate,
slicesToDelete []*discovery.EndpointSlice,
triggerTime time.Time,
) error {
errs := []error{}
// If there are slices to create and delete, change the creates to updates
// of the slices that would otherwise be deleted.
for len(slicesToCreate) > 0 && sliceNamesToDelete.Len() > 0 {
sliceName, _ := sliceNamesToDelete.PopAny()
for i := 0; i < len(slicesToDelete); {
if len(slicesToCreate) == 0 {
break
}
sliceToDelete := slicesToDelete[i]
slice := slicesToCreate[len(slicesToCreate)-1]
slicesToCreate = slicesToCreate[:len(slicesToCreate)-1]
slice.Name = sliceName
slicesToUpdate = append(slicesToUpdate, slice)
// Only update EndpointSlices that have the same AddressType as this
// field is considered immutable. Since Services also consider IPFamily
// immutable, the only case where this should matter will be the
// migration from IP to IPv4 and IPv6 AddressTypes, where there's a
// chance EndpointSlices with an IP AddressType would otherwise be
// updated to IPv4 or IPv6 without this check.
if sliceToDelete.AddressType == slice.AddressType {
slice.Name = sliceToDelete.Name
slicesToCreate = slicesToCreate[:len(slicesToCreate)-1]
slicesToUpdate = append(slicesToUpdate, slice)
slicesToDelete = append(slicesToDelete[:i], slicesToDelete[i+1:]...)
} else {
i++
}
}
for _, endpointSlice := range slicesToCreate {
@ -202,11 +225,10 @@ func (r *reconciler) finalize(
}
}
for sliceNamesToDelete.Len() > 0 {
sliceName, _ := sliceNamesToDelete.PopAny()
err := r.client.DiscoveryV1alpha1().EndpointSlices(service.Namespace).Delete(sliceName, &metav1.DeleteOptions{})
for _, endpointSlice := range slicesToDelete {
err := r.client.DiscoveryV1alpha1().EndpointSlices(service.Namespace).Delete(endpointSlice.Name, &metav1.DeleteOptions{})
if err != nil {
errs = append(errs, fmt.Errorf("Error deleting %s EndpointSlice for Service %s/%s: %v", sliceName, service.Namespace, service.Name, err))
errs = append(errs, fmt.Errorf("Error deleting %s EndpointSlice for Service %s/%s: %v", endpointSlice.Name, service.Namespace, service.Name, err))
} else {
metrics.EndpointSliceChanges.WithLabelValues("delete").Inc()
}
@ -229,7 +251,7 @@ func (r *reconciler) reconcileByPortMapping(
existingSlices []*discovery.EndpointSlice,
desiredSet endpointSet,
endpointMeta *endpointMeta,
) ([]*discovery.EndpointSlice, []*discovery.EndpointSlice, sets.String, int, int) {
) ([]*discovery.EndpointSlice, []*discovery.EndpointSlice, []*discovery.EndpointSlice, int, int) {
slicesByName := map[string]*discovery.EndpointSlice{}
sliceNamesUnchanged := sets.String{}
sliceNamesToUpdate := sets.String{}
@ -345,7 +367,13 @@ func (r *reconciler) reconcileByPortMapping(
slicesToUpdate = append(slicesToUpdate, slicesByName[sliceName])
}
return slicesToCreate, slicesToUpdate, sliceNamesToDelete, numAdded, numRemoved
// Build slicesToDelete from slice names.
slicesToDelete := []*discovery.EndpointSlice{}
for _, sliceName := range sliceNamesToDelete.UnsortedList() {
slicesToDelete = append(slicesToDelete, slicesByName[sliceName])
}
return slicesToCreate, slicesToUpdate, slicesToDelete, numAdded, numRemoved
}
func (r *reconciler) deleteService(namespace, name string) {

View File

@ -18,6 +18,8 @@ package endpointslice
import (
"fmt"
"reflect"
"strings"
"testing"
"time"
@ -65,11 +67,14 @@ func TestReconcileEmpty(t *testing.T) {
// Given a single pod matching a service selector and no existing endpoint slices,
// a slice should be created
func TestReconcile1Pod(t *testing.T) {
client := newClientset()
setupMetrics()
namespace := "test"
svc, _ := newServiceAndEndpointMeta("foo", namespace)
ipv6Family := corev1.IPv6Protocol
svcv4, _ := newServiceAndEndpointMeta("foo", namespace)
svcv6, _ := newServiceAndEndpointMeta("foo", namespace)
svcv6.Spec.IPFamily = &ipv6Family
pod1 := newPod(1, namespace, true, 1)
pod1.Status.PodIPs = []corev1.PodIP{{IP: "1.2.3.4"}, {IP: "1234::5678:0000:0000:9abc:def0"}}
pod1.Spec.Hostname = "example-hostname"
node1 := &corev1.Node{
ObjectMeta: metav1.ObjectMeta{
@ -81,33 +86,92 @@ func TestReconcile1Pod(t *testing.T) {
},
}
triggerTime := time.Now()
r := newReconciler(client, []*corev1.Node{node1}, defaultMaxEndpointsPerSlice)
reconcileHelper(t, r, &svc, []*corev1.Pod{pod1}, []*discovery.EndpointSlice{}, triggerTime)
assert.Len(t, client.Actions(), 1, "Expected 1 additional clientset action")
testCases := map[string]struct {
service corev1.Service
expectedAddressType discovery.AddressType
expectedEndpoint discovery.Endpoint
}{
"ipv4": {
service: svcv4,
expectedAddressType: discovery.AddressTypeIPv4,
expectedEndpoint: discovery.Endpoint{
Addresses: []string{"1.2.3.4"},
Conditions: discovery.EndpointConditions{Ready: utilpointer.BoolPtr(true)},
Topology: map[string]string{
"kubernetes.io/hostname": "node-1",
"topology.kubernetes.io/zone": "us-central1-a",
"topology.kubernetes.io/region": "us-central1",
},
TargetRef: &corev1.ObjectReference{
Kind: "Pod",
Namespace: namespace,
Name: "pod1",
},
},
},
"ipv6": {
service: svcv6,
expectedAddressType: discovery.AddressTypeIPv6,
expectedEndpoint: discovery.Endpoint{
Addresses: []string{"1234::5678:0000:0000:9abc:def0"},
Conditions: discovery.EndpointConditions{Ready: utilpointer.BoolPtr(true)},
Topology: map[string]string{
"kubernetes.io/hostname": "node-1",
"topology.kubernetes.io/zone": "us-central1-a",
"topology.kubernetes.io/region": "us-central1",
},
TargetRef: &corev1.ObjectReference{
Kind: "Pod",
Namespace: namespace,
Name: "pod1",
},
},
},
}
slices := fetchEndpointSlices(t, client, namespace)
assert.Len(t, slices, 1, "Expected 1 endpoint slices")
assert.Regexp(t, "^"+svc.Name, slices[0].Name)
assert.Equal(t, svc.Name, slices[0].Labels[discovery.LabelServiceName])
assert.Equal(t, slices[0].Annotations, map[string]string{
"endpoints.kubernetes.io/last-change-trigger-time": triggerTime.Format(time.RFC3339Nano),
})
assert.EqualValues(t, []discovery.Endpoint{{
Addresses: []string{"1.2.3.5"},
Conditions: discovery.EndpointConditions{Ready: utilpointer.BoolPtr(true)},
Topology: map[string]string{
"kubernetes.io/hostname": "node-1",
"topology.kubernetes.io/zone": "us-central1-a",
"topology.kubernetes.io/region": "us-central1",
},
TargetRef: &corev1.ObjectReference{
Kind: "Pod",
Namespace: namespace,
Name: "pod1",
},
}}, slices[0].Endpoints)
expectMetrics(t, expectedMetrics{desiredSlices: 1, actualSlices: 1, desiredEndpoints: 1, addedPerSync: 1, removedPerSync: 0, numCreated: 1, numUpdated: 0, numDeleted: 0})
for name, testCase := range testCases {
t.Run(name, func(t *testing.T) {
client := newClientset()
setupMetrics()
triggerTime := time.Now()
r := newReconciler(client, []*corev1.Node{node1}, defaultMaxEndpointsPerSlice)
reconcileHelper(t, r, &testCase.service, []*corev1.Pod{pod1}, []*discovery.EndpointSlice{}, triggerTime)
if len(client.Actions()) != 1 {
t.Errorf("Expected 1 clientset action, got %d", len(client.Actions()))
}
slices := fetchEndpointSlices(t, client, namespace)
if len(slices) != 1 {
t.Fatalf("Expected 1 EndpointSlice, got %d", len(slices))
}
slice := slices[0]
if !strings.HasPrefix(slice.Name, testCase.service.Name) {
t.Errorf("Expected EndpointSlice name to start with %s, got %s", testCase.service.Name, slice.Name)
}
if slice.Labels[discovery.LabelServiceName] != testCase.service.Name {
t.Errorf("Expected EndpointSlice to have label set with %s value, got %s", testCase.service.Name, slice.Labels[discovery.LabelServiceName])
}
if slice.Annotations[corev1.EndpointsLastChangeTriggerTime] != triggerTime.Format(time.RFC3339Nano) {
t.Errorf("Expected EndpointSlice trigger time annotation to be %s, got %s", triggerTime.Format(time.RFC3339Nano), slice.Annotations[corev1.EndpointsLastChangeTriggerTime])
}
if len(slice.Endpoints) != 1 {
t.Fatalf("Expected 1 Endpoint, got %d", len(slice.Endpoints))
}
endpoint := slice.Endpoints[0]
if !reflect.DeepEqual(endpoint, testCase.expectedEndpoint) {
t.Errorf("Expected endpoint: %+v, got: %+v", testCase.expectedEndpoint, endpoint)
}
expectMetrics(t, expectedMetrics{desiredSlices: 1, actualSlices: 1, desiredEndpoints: 1, addedPerSync: 1, removedPerSync: 0, numCreated: 1, numUpdated: 0, numDeleted: 0})
})
}
}
// given an existing endpoint slice and no pods matching the service, the existing
@ -437,6 +501,56 @@ func TestReconcileEndpointSlicesUpdatePacking(t *testing.T) {
expectUnorderedSlicesWithLengths(t, fetchEndpointSlices(t, client, namespace), []int{95, 20})
}
// In this test, we want to verify that old EndpointSlices with a deprecated IP
// address type will be replaced with a newer IPv4 type.
func TestReconcileEndpointSlicesReplaceDeprecated(t *testing.T) {
client := newClientset()
setupMetrics()
namespace := "test"
svc, endpointMeta := newServiceAndEndpointMeta("foo", namespace)
endpointMeta.AddressType = discovery.AddressTypeIP
existingSlices := []*discovery.EndpointSlice{}
pods := []*corev1.Pod{}
slice1 := newEmptyEndpointSlice(1, namespace, endpointMeta, svc)
for i := 0; i < 80; i++ {
pod := newPod(i, namespace, true, 1)
slice1.Endpoints = append(slice1.Endpoints, podToEndpoint(pod, &corev1.Node{}, &corev1.Service{Spec: corev1.ServiceSpec{}}))
pods = append(pods, pod)
}
existingSlices = append(existingSlices, slice1)
slice2 := newEmptyEndpointSlice(2, namespace, endpointMeta, svc)
for i := 100; i < 150; i++ {
pod := newPod(i, namespace, true, 1)
slice2.Endpoints = append(slice2.Endpoints, podToEndpoint(pod, &corev1.Node{}, &corev1.Service{Spec: corev1.ServiceSpec{}}))
pods = append(pods, pod)
}
existingSlices = append(existingSlices, slice2)
createEndpointSlices(t, client, namespace, existingSlices)
r := newReconciler(client, []*corev1.Node{{ObjectMeta: metav1.ObjectMeta{Name: "node-1"}}}, defaultMaxEndpointsPerSlice)
reconcileHelper(t, r, &svc, pods, existingSlices, time.Now())
// ensure that both original endpoint slices have been deleted
expectActions(t, client.Actions(), 2, "delete", "endpointslices")
endpointSlices := fetchEndpointSlices(t, client, namespace)
// since this involved replacing both EndpointSlices, the result should be
// perfectly packed.
expectUnorderedSlicesWithLengths(t, endpointSlices, []int{100, 30})
for _, endpointSlice := range endpointSlices {
if endpointSlice.AddressType != discovery.AddressTypeIPv4 {
t.Errorf("Expected address type to be IPv4, got %s", endpointSlice.AddressType)
}
}
}
// Named ports can map to different port numbers on different pods.
// This test ensures that EndpointSlices are grouped correctly in that case.
func TestReconcileEndpointSlicesNamedPorts(t *testing.T) {
@ -489,7 +603,6 @@ func TestReconcileEndpointSlicesNamedPorts(t *testing.T) {
// generate data structures for expected slice ports and address types
protoTCP := corev1.ProtocolTCP
ipAddressType := discovery.AddressTypeIP
expectedSlices := []discovery.EndpointSlice{}
for i := range fetchedSlices {
expectedSlices = append(expectedSlices, discovery.EndpointSlice{
@ -498,7 +611,7 @@ func TestReconcileEndpointSlicesNamedPorts(t *testing.T) {
Protocol: &protoTCP,
Port: utilpointer.Int32Ptr(int32(8080 + i)),
}},
AddressType: &ipAddressType,
AddressType: discovery.AddressTypeIPv4,
})
}

View File

@ -31,6 +31,7 @@ import (
api "k8s.io/kubernetes/pkg/apis/core"
"k8s.io/kubernetes/pkg/apis/discovery/validation"
endpointutil "k8s.io/kubernetes/pkg/controller/util/endpoint"
utilnet "k8s.io/utils/net"
)
// podEndpointChanged returns true if the results of podToEndpoint are different
@ -70,7 +71,7 @@ func podToEndpoint(pod *corev1.Pod, node *corev1.Node, service *corev1.Service)
ready := service.Spec.PublishNotReadyAddresses || podutil.IsPodReady(pod)
ep := discovery.Endpoint{
Addresses: getEndpointAddresses(pod.Status),
Addresses: getEndpointAddresses(pod.Status, service.Spec.IPFamily),
Conditions: discovery.EndpointConditions{
Ready: &ready,
},
@ -124,16 +125,18 @@ func getEndpointPorts(service *corev1.Service, pod *corev1.Pod) []discovery.Endp
}
// getEndpointAddresses returns a list of addresses generated from a pod status.
func getEndpointAddresses(podStatus corev1.PodStatus) []string {
if len(podStatus.PodIPs) > 1 {
addresss := []string{}
for _, podIP := range podStatus.PodIPs {
addresss = append(addresss, podIP.IP)
func getEndpointAddresses(podStatus corev1.PodStatus, ipFamily *corev1.IPFamily) []string {
isIPv6Family := ipFamily != nil && *ipFamily == corev1.IPv6Protocol
addresses := []string{}
for _, podIP := range podStatus.PodIPs {
isIPv6PodIP := utilnet.IsIPv6String(podIP.IP)
if isIPv6Family == isIPv6PodIP {
addresses = append(addresses, podIP.IP)
}
return addresss
}
return []string{podStatus.PodIP}
return addresses
}
// endpointsEqualBeyondHash returns true if endpoints have equal attributes

View File

@ -38,12 +38,12 @@ import (
)
func TestNewEndpointSlice(t *testing.T) {
ipAddressType := discovery.AddressTypeIP
ipAddressType := discovery.AddressTypeIPv4
portName := "foo"
protocol := v1.ProtocolTCP
endpointMeta := endpointMeta{
Ports: []discovery.EndpointPort{{Name: &portName, Protocol: &protocol}},
AddressType: &ipAddressType,
AddressType: ipAddressType,
}
service := v1.Service{
ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "test"},
@ -204,7 +204,7 @@ func TestPodToEndpoint(t *testing.T) {
node: node1,
svc: &svc,
expectedEndpoint: discovery.Endpoint{
Addresses: []string{"1.2.3.4", "1234::5678:0000:0000:9abc:def0"},
Addresses: []string{"1.2.3.4"},
Conditions: discovery.EndpointConditions{Ready: utilpointer.BoolPtr(true)},
Topology: map[string]string{
"kubernetes.io/hostname": "node-1",
@ -283,11 +283,11 @@ func TestPodChangedWithPodEndpointChanged(t *testing.T) {
}
newPod.ObjectMeta.ResourceVersion = oldPod.ObjectMeta.ResourceVersion
newPod.Status.PodIP = "1.2.3.1"
newPod.Status.PodIPs = []v1.PodIP{{IP: "1.2.3.1"}}
if !podChangedHelper(oldPod, newPod, podEndpointChanged) {
t.Errorf("Expected pod to be changed with pod IP address change")
}
newPod.Status.PodIP = oldPod.Status.PodIP
newPod.Status.PodIPs = oldPod.Status.PodIPs
newPod.ObjectMeta.Name = "wrong-name"
if !podChangedHelper(oldPod, newPod, podEndpointChanged) {
@ -333,6 +333,9 @@ func newPod(n int, namespace string, ready bool, nPorts int) *v1.Pod {
},
Status: v1.PodStatus{
PodIP: fmt.Sprintf("1.2.3.%d", 4+n),
PodIPs: []v1.PodIP{{
IP: fmt.Sprintf("1.2.3.%d", 4+n),
}},
Conditions: []v1.PodCondition{
{
Type: v1.PodReady,
@ -381,10 +384,10 @@ func newServiceAndEndpointMeta(name, namespace string) (v1.Service, endpointMeta
},
}
ipAddressType := discovery.AddressTypeIP
addressType := discovery.AddressTypeIPv4
protocol := v1.ProtocolTCP
endpointMeta := endpointMeta{
AddressType: &ipAddressType,
AddressType: addressType,
Ports: []discovery.EndpointPort{{Name: &name, Port: &portNum, Protocol: &protocol}},
}

View File

@ -27,6 +27,7 @@ go_library(
"//staging/src/k8s.io/client-go/kubernetes/typed/discovery/v1alpha1:go_default_library",
"//staging/src/k8s.io/client-go/util/retry:go_default_library",
"//vendor/k8s.io/klog:go_default_library",
"//vendor/k8s.io/utils/net:go_default_library",
],
)

View File

@ -24,6 +24,7 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
corev1client "k8s.io/client-go/kubernetes/typed/core/v1"
discoveryclient "k8s.io/client-go/kubernetes/typed/discovery/v1alpha1"
utilnet "k8s.io/utils/net"
)
// EndpointsAdapter provides a simple interface for reading and writing both
@ -95,6 +96,16 @@ func (adapter *EndpointsAdapter) EnsureEndpointSliceFromEndpoints(namespace stri
return err
}
// required for transition from IP to IPv4 address type.
if currentEndpointSlice.AddressType != endpointSlice.AddressType {
err = adapter.endpointSliceClient.EndpointSlices(namespace).Delete(endpointSlice.Name, &metav1.DeleteOptions{})
if err != nil {
return err
}
_, err = adapter.endpointSliceClient.EndpointSlices(namespace).Create(endpointSlice)
return err
}
if apiequality.Semantic.DeepEqual(currentEndpointSlice.Endpoints, endpointSlice.Endpoints) &&
apiequality.Semantic.DeepEqual(currentEndpointSlice.Ports, endpointSlice.Ports) &&
apiequality.Semantic.DeepEqual(currentEndpointSlice.Labels, endpointSlice.Labels) {
@ -112,8 +123,9 @@ func endpointSliceFromEndpoints(endpoints *corev1.Endpoints) *discovery.Endpoint
endpointSlice.Name = endpoints.Name
endpointSlice.Labels = map[string]string{discovery.LabelServiceName: endpoints.Name}
ipAddressType := discovery.AddressTypeIP
endpointSlice.AddressType = &ipAddressType
// TODO: Add support for IPv6 addresses here (and in the rest of
// EndpointsAdapter).
endpointSlice.AddressType = discovery.AddressTypeIPv4
if len(endpoints.Subsets) > 0 {
subset := endpoints.Subsets[0]
@ -124,17 +136,28 @@ func endpointSliceFromEndpoints(endpoints *corev1.Endpoints) *discovery.Endpoint
Protocol: &subset.Ports[i].Protocol,
})
}
for _, address := range subset.Addresses {
endpointSlice.Endpoints = append(endpointSlice.Endpoints, endpointFromAddress(address, true))
}
for _, address := range subset.NotReadyAddresses {
endpointSlice.Endpoints = append(endpointSlice.Endpoints, endpointFromAddress(address, false))
}
endpointSlice.Endpoints = append(endpointSlice.Endpoints, getEndpointsFromAddresses(subset.Addresses, endpointSlice.AddressType, true)...)
endpointSlice.Endpoints = append(endpointSlice.Endpoints, getEndpointsFromAddresses(subset.NotReadyAddresses, endpointSlice.AddressType, false)...)
}
return endpointSlice
}
// getEndpointsFromAddresses returns a list of Endpoints from addresses that
// match the provided address type.
func getEndpointsFromAddresses(addresses []corev1.EndpointAddress, addressType discovery.AddressType, ready bool) []discovery.Endpoint {
endpoints := []discovery.Endpoint{}
isIPv6AddressType := addressType == discovery.AddressTypeIPv6
for _, address := range addresses {
if utilnet.IsIPv6String(address.IP) == isIPv6AddressType {
endpoints = append(endpoints, endpointFromAddress(address, ready))
}
}
return endpoints
}
// endpointFromAddress generates an Endpoint from an EndpointAddress resource.
func endpointFromAddress(address corev1.EndpointAddress, ready bool) discovery.Endpoint {
topology := map[string]string{}

View File

@ -105,6 +105,11 @@ func TestEndpointsAdapterGet(t *testing.T) {
func TestEndpointsAdapterCreate(t *testing.T) {
endpoints1, epSlice1 := generateEndpointsAndSlice("foo", "testing", []int{80}, []string{"10.1.2.3", "10.1.2.4"})
// even if an Endpoints resource includes an IPv6 address, it should not be
// included in the corresponding EndpointSlice.
endpoints2, _ := generateEndpointsAndSlice("foo", "testing", []int{80}, []string{"10.1.2.5", "10.1.2.6", "1234::5678:0000:0000:9abc:def0"})
_, epSlice2 := generateEndpointsAndSlice("foo", "testing", []int{80}, []string{"10.1.2.5", "10.1.2.6"})
testCases := map[string]struct {
endpointSlicesEnabled bool
expectedError error
@ -124,6 +129,15 @@ func TestEndpointsAdapterCreate(t *testing.T) {
namespaceParam: endpoints1.Namespace,
endpointsParam: endpoints1,
},
"single-endpoint-with-ipv6": {
endpointSlicesEnabled: true,
expectedError: nil,
expectedEndpoints: endpoints2,
expectedEndpointSlice: epSlice2,
endpoints: []*corev1.Endpoints{},
namespaceParam: endpoints2.Namespace,
endpointsParam: endpoints2,
},
"single-endpoint-no-slices": {
endpointSlicesEnabled: false,
expectedError: nil,
@ -199,6 +213,13 @@ func TestEndpointsAdapterUpdate(t *testing.T) {
endpoints2, epSlice2 := generateEndpointsAndSlice("foo", "testing", []int{80, 443}, []string{"10.1.2.3", "10.1.2.4", "10.1.2.5"})
endpoints3, _ := generateEndpointsAndSlice("bar", "testing", []int{80, 443}, []string{"10.1.2.3", "10.1.2.4", "10.1.2.5"})
// ensure that EndpointSlice with deprecated IP address type is replaced
// 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
_, epSlice4IPv4 := generateEndpointsAndSlice("foo", "testing", []int{80}, []string{"10.1.2.7", "10.1.2.8"})
testCases := map[string]struct {
endpointSlicesEnabled bool
expectedError error
@ -218,6 +239,16 @@ func TestEndpointsAdapterUpdate(t *testing.T) {
namespaceParam: "testing",
endpointsParam: endpoints1,
},
"existing-endpointslice-replaced-with-updated-ipv4-address-type": {
endpointSlicesEnabled: true,
expectedError: nil,
expectedEndpoints: endpoints4,
expectedEndpointSlice: epSlice4IPv4,
endpoints: []*corev1.Endpoints{endpoints4},
endpointSlices: []*discovery.EndpointSlice{epSlice4IP},
namespaceParam: "testing",
endpointsParam: endpoints4,
},
"add-ports-and-ips": {
endpointSlicesEnabled: true,
expectedError: nil,
@ -291,9 +322,8 @@ func TestEndpointsAdapterUpdate(t *testing.T) {
func generateEndpointsAndSlice(name, namespace string, ports []int, addresses []string) (*corev1.Endpoints, *discovery.EndpointSlice) {
objectMeta := metav1.ObjectMeta{Name: name, Namespace: namespace}
trueBool := true
addressTypeIP := discovery.AddressTypeIP
epSlice := &discovery.EndpointSlice{ObjectMeta: objectMeta, AddressType: &addressTypeIP}
epSlice := &discovery.EndpointSlice{ObjectMeta: objectMeta, AddressType: discovery.AddressTypeIPv4}
epSlice.Labels = map[string]string{discovery.LabelServiceName: name}
subset := corev1.EndpointSubset{}

View File

@ -1173,11 +1173,7 @@ func printEndpointSlice(obj *discovery.EndpointSlice, options printers.GenerateO
row := metav1beta1.TableRow{
Object: runtime.RawExtension{Object: obj},
}
addressType := "<unset>"
if obj.AddressType != nil {
addressType = string(*obj.AddressType)
}
row.Cells = append(row.Cells, obj.Name, addressType, formatDiscoveryPorts(obj.Ports), formatDiscoveryEndpoints(obj.Endpoints), translateTimestampSince(obj.CreationTimestamp))
row.Cells = append(row.Cells, obj.Name, string(obj.AddressType), formatDiscoveryPorts(obj.Ports), formatDiscoveryEndpoints(obj.Endpoints), translateTimestampSince(obj.CreationTimestamp))
return []metav1beta1.TableRow{row}, nil
}

View File

@ -4736,7 +4736,6 @@ func TestPrintEndpoint(t *testing.T) {
}
func TestPrintEndpointSlice(t *testing.T) {
ipAddressType := discovery.AddressTypeIP
tcpProtocol := api.ProtocolTCP
tests := []struct {
@ -4749,7 +4748,7 @@ func TestPrintEndpointSlice(t *testing.T) {
Name: "abcslice.123",
CreationTimestamp: metav1.Time{Time: time.Now().Add(1.9e9)},
},
AddressType: &ipAddressType,
AddressType: discovery.AddressTypeIPv4,
Ports: []discovery.EndpointPort{{
Name: utilpointer.StringPtr("http"),
Port: utilpointer.Int32Ptr(80),
@ -4760,14 +4759,14 @@ func TestPrintEndpointSlice(t *testing.T) {
}},
},
// Columns: Name, AddressType, Ports, Endpoints, Age
expected: []metav1beta1.TableRow{{Cells: []interface{}{"abcslice.123", "IP", "80", "10.1.2.3,2001:db8::1234:5678", "0s"}}},
expected: []metav1beta1.TableRow{{Cells: []interface{}{"abcslice.123", "IPv4", "80", "10.1.2.3,2001:db8::1234:5678", "0s"}}},
}, {
endpointSlice: discovery.EndpointSlice{
ObjectMeta: metav1.ObjectMeta{
Name: "longerslicename.123",
CreationTimestamp: metav1.Time{Time: time.Now().Add(-3e11)},
},
AddressType: &ipAddressType,
AddressType: discovery.AddressTypeIPv6,
Ports: []discovery.EndpointPort{{
Name: utilpointer.StringPtr("http"),
Port: utilpointer.Int32Ptr(80),
@ -4784,14 +4783,14 @@ func TestPrintEndpointSlice(t *testing.T) {
}},
},
// Columns: Name, AddressType, Ports, Endpoints, Age
expected: []metav1beta1.TableRow{{Cells: []interface{}{"longerslicename.123", "IP", "80,443", "10.1.2.3,2001:db8::1234:5678,10.2.3.4 + 1 more...", "5m"}}},
expected: []metav1beta1.TableRow{{Cells: []interface{}{"longerslicename.123", "IPv6", "80,443", "10.1.2.3,2001:db8::1234:5678,10.2.3.4 + 1 more...", "5m"}}},
}, {
endpointSlice: discovery.EndpointSlice{
ObjectMeta: metav1.ObjectMeta{
Name: "multiportslice.123",
CreationTimestamp: metav1.Time{Time: time.Now().Add(-3e11)},
},
AddressType: &ipAddressType,
AddressType: discovery.AddressTypeIPv4,
Ports: []discovery.EndpointPort{{
Name: utilpointer.StringPtr("http"),
Port: utilpointer.Int32Ptr(80),
@ -4816,7 +4815,7 @@ func TestPrintEndpointSlice(t *testing.T) {
}},
},
// Columns: Name, AddressType, Ports, Endpoints, Age
expected: []metav1beta1.TableRow{{Cells: []interface{}{"multiportslice.123", "IP", "80,443,3000 + 1 more...", "10.1.2.3,2001:db8::1234:5678,10.2.3.4 + 1 more...", "5m"}}},
expected: []metav1beta1.TableRow{{Cells: []interface{}{"multiportslice.123", "IPv4", "80,443,3000 + 1 more...", "10.1.2.3,2001:db8::1234:5678,10.2.3.4 + 1 more...", "5m"}}},
},
}

View File

@ -336,7 +336,6 @@ func TestEsInfoChanged(t *testing.T) {
}
func generateEndpointSliceWithOffset(serviceName, namespace string, sliceNum, offset, numEndpoints, unreadyMod int, hosts []string, portNums []*int32) *discovery.EndpointSlice {
ipAddressType := discovery.AddressTypeIP
tcpProtocol := v1.ProtocolTCP
endpointSlice := &discovery.EndpointSlice{
@ -346,7 +345,7 @@ func generateEndpointSliceWithOffset(serviceName, namespace string, sliceNum, of
Labels: map[string]string{discovery.LabelServiceName: serviceName},
},
Ports: []discovery.EndpointPort{},
AddressType: &ipAddressType,
AddressType: discovery.AddressTypeIPv4,
Endpoints: []discovery.Endpoint{},
}

View File

@ -2390,7 +2390,6 @@ COMMIT
},
})
ipAddressType := discovery.AddressTypeIP
tcpProtocol := v1.ProtocolTCP
endpointSlice := &discovery.EndpointSlice{
ObjectMeta: metav1.ObjectMeta{
@ -2403,7 +2402,7 @@ COMMIT
Port: utilpointer.Int32Ptr(80),
Protocol: &tcpProtocol,
}},
AddressType: &ipAddressType,
AddressType: discovery.AddressTypeIPv4,
Endpoints: []discovery.Endpoint{{
Addresses: []string{"10.0.1.1"},
Conditions: discovery.EndpointConditions{Ready: utilpointer.BoolPtr(true)},

View File

@ -3713,7 +3713,6 @@ func TestEndpointSliceE2E(t *testing.T) {
})
// Add initial endpoint slice
ipAddressType := discovery.AddressTypeIP
tcpProtocol := v1.ProtocolTCP
endpointSlice := &discovery.EndpointSlice{
ObjectMeta: metav1.ObjectMeta{
@ -3726,7 +3725,7 @@ func TestEndpointSliceE2E(t *testing.T) {
Port: utilpointer.Int32Ptr(80),
Protocol: &tcpProtocol,
}},
AddressType: &ipAddressType,
AddressType: discovery.AddressTypeIPv4,
Endpoints: []discovery.Endpoint{{
Addresses: []string{"10.0.1.1"},
Conditions: discovery.EndpointConditions{Ready: utilpointer.BoolPtr(true)},

View File

@ -72,7 +72,7 @@ func (endpointSliceStrategy) PrepareForUpdate(ctx context.Context, obj, old runt
// Validate validates a new EndpointSlice.
func (endpointSliceStrategy) Validate(ctx context.Context, obj runtime.Object) field.ErrorList {
endpointSlice := obj.(*discovery.EndpointSlice)
err := validation.ValidateEndpointSlice(endpointSlice)
err := validation.ValidateEndpointSliceCreate(endpointSlice)
return err
}

View File

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

View File

@ -32,12 +32,10 @@ option go_package = "v1alpha1";
// Endpoint represents a single logical "backend" implementing a service.
message Endpoint {
// 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 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.
// according to the corresponding EndpointSlice addressType field. Consumers
// 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
repeated string addresses = 1;
@ -123,13 +121,12 @@ message EndpointSlice {
optional k8s.io.apimachinery.pkg.apis.meta.v1.ObjectMeta metadata = 1;
// addressType specifies the type of address carried by this EndpointSlice.
// 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.
// All addresses in this slice must be the same type. This field is
// immutable after creation. The following address types are currently
// supported:
// * IPv4: Represents an IPv4 Address.
// * IPv6: Represents an IPv6 Address.
// * FQDN: Represents a Fully Qualified Domain Name.
// Default is IP
// +optional
optional string addressType = 4;
// endpoints is a list of unique endpoints in this slice. Each slice may

View File

@ -33,14 +33,13 @@ type EndpointSlice struct {
// +optional
metav1.ObjectMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"`
// addressType specifies the type of address carried by this EndpointSlice.
// 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.
// All addresses in this slice must be the same type. This field is
// immutable after creation. The following address types are currently
// supported:
// * IPv4: Represents an IPv4 Address.
// * IPv6: Represents an IPv6 Address.
// * FQDN: Represents a Fully Qualified Domain Name.
// Default is IP
// +optional
AddressType *AddressType `json:"addressType" protobuf:"bytes,4,rep,name=addressType"`
AddressType AddressType `json:"addressType" protobuf:"bytes,4,rep,name=addressType"`
// endpoints is a list of unique endpoints in this slice. Each slice may
// include a maximum of 1000 endpoints.
// +listType=atomic
@ -59,22 +58,27 @@ type EndpointSlice struct {
type AddressType string
const (
// AddressTypeIP represents an IP Address. Inclusive of IPv4 and IPv6
// addresses.
// 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")
// AddressTypeFQDN represents a Fully Qualified Domain Name.
// AddressTypeIPv4 represents an IPv4 Address.
AddressTypeIPv4 = AddressType(v1.IPv4Protocol)
// AddressTypeIPv6 represents an IPv6 Address.
AddressTypeIPv6 = AddressType(v1.IPv6Protocol)
// AddressTypeFQDN represents a FQDN.
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 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.
// according to the corresponding EndpointSlice addressType field. Consumers
// 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 `json:"addresses" protobuf:"bytes,1,rep,name=addresses"`
// conditions contains information about the current status of the endpoint.

View File

@ -29,7 +29,7 @@ package v1alpha1
// AUTO-GENERATED FUNCTIONS START HERE. DO NOT EDIT.
var map_Endpoint = map[string]string{
"": "Endpoint represents a single logical \"backend\" implementing a service.",
"addresses": "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 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.",
"addresses": "addresses of this endpoint. The contents of this field are interpreted according to the corresponding EndpointSlice addressType field. Consumers 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.",
"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 pass DNS Label (RFC 1123) validation.",
"targetRef": "targetRef is a reference to a Kubernetes object that represents this endpoint.",
@ -64,7 +64,7 @@ func (EndpointPort) SwaggerDoc() map[string]string {
var map_EndpointSlice = map[string]string{
"": "EndpointSlice represents a subset of the endpoints that implement a service. For a given service there may be multiple EndpointSlice objects, selected by labels, which must be joined to produce the full set of endpoints.",
"metadata": "Standard object's metadata.",
"addressType": "addressType specifies the type of address carried by this EndpointSlice. 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\n addresses.\n* FQDN: Represents a Fully Qualified Domain Name. Default is IP",
"addressType": "addressType specifies the type of address carried by this EndpointSlice. All addresses in this slice must be the same type. This field is immutable after creation. The following address types are currently supported: * IPv4: Represents an IPv4 Address. * IPv6: Represents an IPv6 Address. * FQDN: Represents a Fully Qualified Domain Name.",
"endpoints": "endpoints is a list of unique endpoints in this slice. Each slice may include a maximum of 1000 endpoints.",
"ports": "ports specifies the list of network ports exposed by each endpoint in this slice. Each port must have a unique name. When ports is empty, it indicates that there are no defined ports. When a port is defined with a nil port value, it indicates \"all ports\". Each slice may include a maximum of 100 ports.",
}

View File

@ -126,11 +126,6 @@ func (in *EndpointSlice) DeepCopyInto(out *EndpointSlice) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
if in.AddressType != nil {
in, out := &in.AddressType, &out.AddressType
*out = new(AddressType)
**out = **in
}
if in.Endpoints != nil {
in, out := &in.Endpoints, &out.Endpoints
*out = make([]Endpoint, len(*in))

View File

@ -309,6 +309,26 @@ func IsValidIP(value string) []string {
return nil
}
// IsValidIPv4Address tests that the argument is a valid IPv4 address.
func IsValidIPv4Address(fldPath *field.Path, value string) field.ErrorList {
var allErrors field.ErrorList
ip := net.ParseIP(value)
if ip == nil || ip.To4() == nil {
allErrors = append(allErrors, field.Invalid(fldPath, value, "must be a valid IPv4 address"))
}
return allErrors
}
// IsValidIPv6Address tests that the argument is a valid IPv6 address.
func IsValidIPv6Address(fldPath *field.Path, value string) field.ErrorList {
var allErrors field.ErrorList
ip := net.ParseIP(value)
if ip == nil || ip.To4() != nil {
allErrors = append(allErrors, field.Invalid(fldPath, value, "must be a valid IPv6 address"))
}
return allErrors
}
const percentFmt string = "[0-9]+%"
const percentErrMsg string = "a valid percent string must be a numeric string followed by an ending '%'"

View File

@ -355,6 +355,75 @@ func TestIsValidIP(t *testing.T) {
}
}
func TestIsValidIPv4Address(t *testing.T) {
goodValues := []string{
"1.1.1.1",
"1.1.1.01",
"255.0.0.1",
"1.0.0.0",
"0.0.0.0",
}
for _, val := range goodValues {
if msgs := IsValidIPv4Address(field.NewPath(""), val); len(msgs) != 0 {
t.Errorf("expected %q to be valid IPv4 address: %v", val, msgs)
}
}
badValues := []string{
"[2001:db8:0:1]:80",
"myhost.mydomain",
"-1.0.0.0",
"[2001:db8:0:1]",
"a",
"2001:4860:4860::8888",
"::fff:1.1.1.1",
"::1",
"2a00:79e0:2:0:f1c3:e797:93c1:df80",
"::",
}
for _, val := range badValues {
if msgs := IsValidIPv4Address(field.NewPath(""), val); len(msgs) == 0 {
t.Errorf("expected %q to be invalid IPv4 address", val)
}
}
}
func TestIsValidIPv6Address(t *testing.T) {
goodValues := []string{
"2001:4860:4860::8888",
"2a00:79e0:2:0:f1c3:e797:93c1:df80",
"2001:0db8:85a3:0000:0000:8a2e:0370:7334",
"::fff:1.1.1.1",
"::1",
"::",
}
for _, val := range goodValues {
if msgs := IsValidIPv6Address(field.NewPath(""), val); len(msgs) != 0 {
t.Errorf("expected %q to be valid IPv6 address: %v", val, msgs)
}
}
badValues := []string{
"1.1.1.1",
"1.1.1.01",
"255.0.0.1",
"1.0.0.0",
"0.0.0.0",
"[2001:db8:0:1]:80",
"myhost.mydomain",
"2001:0db8:85a3:0000:0000:8a2e:0370:7334:2001:0db8:85a3:0000:0000:8a2e:0370:7334",
"-1.0.0.0",
"[2001:db8:0:1]",
"a",
}
for _, val := range badValues {
if msgs := IsValidIPv6Address(field.NewPath(""), val); len(msgs) == 0 {
t.Errorf("expected %q to be invalid IPv6 address", val)
}
}
}
func TestIsHTTPHeaderName(t *testing.T) {
goodValues := []string{
// Common ones

View File

@ -2644,11 +2644,7 @@ func describeEndpointSlice(eps *discoveryv1alpha1.EndpointSlice, events *corev1.
printLabelsMultiline(w, "Labels", eps.Labels)
printAnnotationsMultiline(w, "Annotations", eps.Annotations)
addressType := "<unset>"
if eps.AddressType != nil {
addressType = string(*eps.AddressType)
}
w.Write(LEVEL_0, "AddressType:\t%s\n", addressType)
w.Write(LEVEL_0, "AddressType:\t%s\n", string(eps.AddressType))
if len(eps.Ports) == 0 {
w.Write(LEVEL_0, "Ports: <unset>\n")

View File

@ -3261,7 +3261,6 @@ func TestDescribeStatefulSet(t *testing.T) {
}
func TestDescribeEndpointSlice(t *testing.T) {
addressTypeIP := discoveryv1alpha1.AddressTypeIP
protocolTCP := corev1.ProtocolTCP
port80 := int32(80)
@ -3270,7 +3269,7 @@ func TestDescribeEndpointSlice(t *testing.T) {
Name: "foo.123",
Namespace: "bar",
},
AddressType: &addressTypeIP,
AddressType: discoveryv1alpha1.AddressTypeIPv4,
Endpoints: []discoveryv1alpha1.Endpoint{
{
Addresses: []string{"1.2.3.4", "1.2.3.5"},
@ -3309,7 +3308,7 @@ func TestDescribeEndpointSlice(t *testing.T) {
Namespace: bar
Labels: <none>
Annotations: <none>
AddressType: IP
AddressType: IPv4
Ports:
Name Port Protocol
---- ---- --------

View File

@ -1390,9 +1390,6 @@ func getStubObj(gvr schema.GroupVersionResource, resource metav1.APIResource) (*
func createOrGetResource(client dynamic.Interface, gvr schema.GroupVersionResource, resource metav1.APIResource) (*unstructured.Unstructured, error) {
stubObj, err := getStubObj(gvr, resource)
if gvr.Group == "discovery.k8s.io" {
fmt.Printf("stubObj =====> %v\n", stubObj)
}
if err != nil {
return nil, err
}

View File

@ -239,7 +239,7 @@ func GetEtcdStorageDataForNamespace(namespace string) map[schema.GroupVersionRes
// k8s.io/kubernetes/pkg/apis/discovery/v1alpha1
gvr("discovery.k8s.io", "v1alpha1", "endpointslices"): {
Stub: `{"metadata": {"name": "slice1"}, "protocol": "TCP", "ports": [], "endpoints": []}`,
Stub: `{"metadata": {"name": "slice1"}, "addressType": "IPv4", "protocol": "TCP", "ports": [], "endpoints": []}`,
ExpectedEtcdPath: "/registry/endpointslices/" + namespace + "/slice1",
},
// --