mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-25 04:33:26 +00:00
Add PreferSameTrafficDistribution feature gate and associated API.
This commit is contained in:
parent
6ca82f9c16
commit
4435ead24a
@ -4562,12 +4562,27 @@ const (
|
|||||||
|
|
||||||
// These are valid values for the TrafficDistribution field of a Service.
|
// These are valid values for the TrafficDistribution field of a Service.
|
||||||
const (
|
const (
|
||||||
// Indicates a preference for routing traffic to endpoints that are in the
|
// Indicates a preference for routing traffic to endpoints that are in the same
|
||||||
// same zone as the client. Setting this value gives implementations
|
// zone as the client. Users should not set this value unless they have ensured
|
||||||
// permission to make different tradeoffs, e.g. optimizing for proximity
|
// that clients and endpoints are distributed in such a way that the "same zone"
|
||||||
// rather than equal distribution of load. Users should not set this value
|
// preference will not result in endpoints getting overloaded.
|
||||||
// if such tradeoffs are not acceptable.
|
|
||||||
ServiceTrafficDistributionPreferClose = "PreferClose"
|
ServiceTrafficDistributionPreferClose = "PreferClose"
|
||||||
|
|
||||||
|
// Indicates a preference for routing traffic to endpoints that are in the same
|
||||||
|
// zone as the client. Users should not set this value unless they have ensured
|
||||||
|
// that clients and endpoints are distributed in such a way that the "same zone"
|
||||||
|
// preference will not result in endpoints getting overloaded.
|
||||||
|
// This is an alias for "PreferClose", but it is an Alpha feature and is only
|
||||||
|
// recognized if the PreferSameTrafficDistribution feature gate is enabled.
|
||||||
|
ServiceTrafficDistributionPreferSameZone = "PreferSameZone"
|
||||||
|
|
||||||
|
// Indicates a preference for routing traffic to endpoints that are on the same
|
||||||
|
// node as the client. Users should not set this value unless they have ensured
|
||||||
|
// that clients and endpoints are distributed in such a way that the "same node"
|
||||||
|
// preference will not result in endpoints getting overloaded.
|
||||||
|
// This is an Alpha feature and is only recognized if the
|
||||||
|
// PreferSameTrafficDistribution feature gate is enabled.
|
||||||
|
ServiceTrafficDistributionPreferSameNode = "PreferSameNode"
|
||||||
)
|
)
|
||||||
|
|
||||||
// These are the valid conditions of a service.
|
// These are the valid conditions of a service.
|
||||||
|
@ -25,6 +25,7 @@ import (
|
|||||||
"path/filepath"
|
"path/filepath"
|
||||||
"reflect"
|
"reflect"
|
||||||
"regexp"
|
"regexp"
|
||||||
|
"slices"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"unicode"
|
"unicode"
|
||||||
@ -6194,8 +6195,21 @@ func validateServiceTrafficDistribution(service *core.Service) field.ErrorList {
|
|||||||
return allErrs
|
return allErrs
|
||||||
}
|
}
|
||||||
|
|
||||||
if *service.Spec.TrafficDistribution != v1.ServiceTrafficDistributionPreferClose {
|
var supportedTrafficDistribution []string
|
||||||
allErrs = append(allErrs, field.NotSupported(field.NewPath("spec").Child("trafficDistribution"), *service.Spec.TrafficDistribution, []string{v1.ServiceTrafficDistributionPreferClose}))
|
if !utilfeature.DefaultFeatureGate.Enabled(features.PreferSameTrafficDistribution) {
|
||||||
|
supportedTrafficDistribution = []string{
|
||||||
|
v1.ServiceTrafficDistributionPreferClose,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
supportedTrafficDistribution = []string{
|
||||||
|
v1.ServiceTrafficDistributionPreferClose,
|
||||||
|
v1.ServiceTrafficDistributionPreferSameZone,
|
||||||
|
v1.ServiceTrafficDistributionPreferSameNode,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !slices.Contains(supportedTrafficDistribution, *service.Spec.TrafficDistribution) {
|
||||||
|
allErrs = append(allErrs, field.NotSupported(field.NewPath("spec").Child("trafficDistribution"), *service.Spec.TrafficDistribution, supportedTrafficDistribution))
|
||||||
}
|
}
|
||||||
|
|
||||||
return allErrs
|
return allErrs
|
||||||
|
@ -16413,12 +16413,38 @@ func TestValidateServiceCreate(t *testing.T) {
|
|||||||
s.Spec.TrafficDistribution = ptr.To("PreferClose")
|
s.Spec.TrafficDistribution = ptr.To("PreferClose")
|
||||||
},
|
},
|
||||||
numErrs: 0,
|
numErrs: 0,
|
||||||
|
}, {
|
||||||
|
name: "valid: trafficDistribution field set to PreferSameZone with feature gate",
|
||||||
|
tweakSvc: func(s *core.Service) {
|
||||||
|
s.Spec.TrafficDistribution = ptr.To("PreferSameZone")
|
||||||
|
},
|
||||||
|
featureGates: []featuregate.Feature{features.PreferSameTrafficDistribution},
|
||||||
|
numErrs: 0,
|
||||||
|
}, {
|
||||||
|
name: "valid: trafficDistribution field set to PreferSameNode with feature gate",
|
||||||
|
tweakSvc: func(s *core.Service) {
|
||||||
|
s.Spec.TrafficDistribution = ptr.To("PreferSameNode")
|
||||||
|
},
|
||||||
|
featureGates: []featuregate.Feature{features.PreferSameTrafficDistribution},
|
||||||
|
numErrs: 0,
|
||||||
}, {
|
}, {
|
||||||
name: "invalid: trafficDistribution field set to Random",
|
name: "invalid: trafficDistribution field set to Random",
|
||||||
tweakSvc: func(s *core.Service) {
|
tweakSvc: func(s *core.Service) {
|
||||||
s.Spec.TrafficDistribution = ptr.To("Random")
|
s.Spec.TrafficDistribution = ptr.To("Random")
|
||||||
},
|
},
|
||||||
numErrs: 1,
|
numErrs: 1,
|
||||||
|
}, {
|
||||||
|
name: "invalid: trafficDistribution field set to PreferSameZone without feature gate",
|
||||||
|
tweakSvc: func(s *core.Service) {
|
||||||
|
s.Spec.TrafficDistribution = ptr.To("PreferSameZone")
|
||||||
|
},
|
||||||
|
numErrs: 1,
|
||||||
|
}, {
|
||||||
|
name: "invalid: trafficDistribution field set to PreferSameNode without feature gate",
|
||||||
|
tweakSvc: func(s *core.Service) {
|
||||||
|
s.Spec.TrafficDistribution = ptr.To("PreferSameNode")
|
||||||
|
},
|
||||||
|
numErrs: 1,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -133,9 +133,16 @@ type EndpointConditions struct {
|
|||||||
|
|
||||||
// EndpointHints provides hints describing how an endpoint should be consumed.
|
// EndpointHints provides hints describing how an endpoint should be consumed.
|
||||||
type EndpointHints struct {
|
type EndpointHints struct {
|
||||||
// forZones indicates the zone(s) this endpoint should be consumed by to
|
// forZones indicates the zone(s) this endpoint should be consumed by when
|
||||||
// enable topology aware routing. May contain a maximum of 8 entries.
|
// using topology aware routing. May contain a maximum of 8 entries.
|
||||||
ForZones []ForZone
|
ForZones []ForZone
|
||||||
|
|
||||||
|
// forNodes indicates the node(s) this endpoint should be consumed by when
|
||||||
|
// using topology aware routing.
|
||||||
|
// This is an Alpha feature and is only used when the PreferSameTrafficDistribution
|
||||||
|
// feature gate is enabled. May contain a maximum of 8 entries.
|
||||||
|
// +featureGate=PreferSameTrafficDistribution
|
||||||
|
ForNodes []ForNode
|
||||||
}
|
}
|
||||||
|
|
||||||
// ForZone provides information about which zones should consume this endpoint.
|
// ForZone provides information about which zones should consume this endpoint.
|
||||||
@ -144,6 +151,12 @@ type ForZone struct {
|
|||||||
Name string
|
Name string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ForNode provides information about which nodes should consume this endpoint.
|
||||||
|
type ForNode struct {
|
||||||
|
// name represents the name of the node.
|
||||||
|
Name string
|
||||||
|
}
|
||||||
|
|
||||||
// EndpointPort represents a Port used by an EndpointSlice.
|
// EndpointPort represents a Port used by an EndpointSlice.
|
||||||
type EndpointPort struct {
|
type EndpointPort struct {
|
||||||
// The name of this port. All ports in an EndpointSlice must have a unique
|
// The name of this port. All ports in an EndpointSlice must have a unique
|
||||||
|
@ -49,6 +49,7 @@ var (
|
|||||||
maxPorts = 20000
|
maxPorts = 20000
|
||||||
maxEndpoints = 1000
|
maxEndpoints = 1000
|
||||||
maxZoneHints = 8
|
maxZoneHints = 8
|
||||||
|
maxNodeHints = 8
|
||||||
)
|
)
|
||||||
|
|
||||||
// ValidateEndpointSliceName can be used to check whether the given endpoint
|
// ValidateEndpointSliceName can be used to check whether the given endpoint
|
||||||
@ -240,5 +241,25 @@ func validateHints(endpointHints *discovery.EndpointHints, fldPath *field.Path)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fnPath := fldPath.Child("forNodes")
|
||||||
|
if len(endpointHints.ForNodes) > maxNodeHints {
|
||||||
|
allErrs = append(allErrs, field.TooMany(fnPath, len(endpointHints.ForNodes), maxNodeHints))
|
||||||
|
return allErrs
|
||||||
|
}
|
||||||
|
|
||||||
|
nodeNames := make([]string, 0, len(endpointHints.ForNodes))
|
||||||
|
for i, forNode := range endpointHints.ForNodes {
|
||||||
|
nodePath := fnPath.Index(i).Child("name")
|
||||||
|
if slices.Contains(nodeNames, forNode.Name) {
|
||||||
|
allErrs = append(allErrs, field.Duplicate(nodePath, forNode.Name))
|
||||||
|
} else {
|
||||||
|
nodeNames = append(nodeNames, forNode.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, msg := range apivalidation.ValidateNodeName(forNode.Name, false) {
|
||||||
|
allErrs = append(allErrs, field.Invalid(nodePath, forNode.Name, msg))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return allErrs
|
return allErrs
|
||||||
}
|
}
|
||||||
|
@ -235,6 +235,7 @@ func TestValidateEndpointSlice(t *testing.T) {
|
|||||||
Addresses: generateIPAddresses(1),
|
Addresses: generateIPAddresses(1),
|
||||||
Hints: &discovery.EndpointHints{
|
Hints: &discovery.EndpointHints{
|
||||||
ForZones: []discovery.ForZone{{Name: "zone-a"}},
|
ForZones: []discovery.ForZone{{Name: "zone-a"}},
|
||||||
|
ForNodes: []discovery.ForNode{{Name: "node-1"}},
|
||||||
},
|
},
|
||||||
}},
|
}},
|
||||||
},
|
},
|
||||||
@ -518,7 +519,7 @@ func TestValidateEndpointSlice(t *testing.T) {
|
|||||||
}},
|
}},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"invalid-hints": {
|
"invalid-zone-hint": {
|
||||||
expectedErrors: 1,
|
expectedErrors: 1,
|
||||||
endpointSlice: &discovery.EndpointSlice{
|
endpointSlice: &discovery.EndpointSlice{
|
||||||
ObjectMeta: standardMeta,
|
ObjectMeta: standardMeta,
|
||||||
@ -535,7 +536,7 @@ func TestValidateEndpointSlice(t *testing.T) {
|
|||||||
}},
|
}},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"overlapping-hints": {
|
"overlapping-zone-hints": {
|
||||||
expectedErrors: 1,
|
expectedErrors: 1,
|
||||||
endpointSlice: &discovery.EndpointSlice{
|
endpointSlice: &discovery.EndpointSlice{
|
||||||
ObjectMeta: standardMeta,
|
ObjectMeta: standardMeta,
|
||||||
@ -556,7 +557,7 @@ func TestValidateEndpointSlice(t *testing.T) {
|
|||||||
}},
|
}},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"too-many-hints": {
|
"too-many-zone-hints": {
|
||||||
expectedErrors: 1,
|
expectedErrors: 1,
|
||||||
endpointSlice: &discovery.EndpointSlice{
|
endpointSlice: &discovery.EndpointSlice{
|
||||||
ObjectMeta: standardMeta,
|
ObjectMeta: standardMeta,
|
||||||
@ -583,6 +584,74 @@ func TestValidateEndpointSlice(t *testing.T) {
|
|||||||
}},
|
}},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
"invalid-node-hints": {
|
||||||
|
expectedErrors: 2,
|
||||||
|
endpointSlice: &discovery.EndpointSlice{
|
||||||
|
ObjectMeta: standardMeta,
|
||||||
|
AddressType: discovery.AddressTypeIPv4,
|
||||||
|
Ports: []discovery.EndpointPort{{
|
||||||
|
Name: ptr.To("http"),
|
||||||
|
Protocol: ptr.To(api.ProtocolTCP),
|
||||||
|
}},
|
||||||
|
Endpoints: []discovery.Endpoint{{
|
||||||
|
Addresses: generateIPAddresses(1),
|
||||||
|
Hints: &discovery.EndpointHints{
|
||||||
|
ForNodes: []discovery.ForNode{
|
||||||
|
{Name: "!@#$!@"},
|
||||||
|
{Name: ""},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"overlapping-node-hints": {
|
||||||
|
expectedErrors: 1,
|
||||||
|
endpointSlice: &discovery.EndpointSlice{
|
||||||
|
ObjectMeta: standardMeta,
|
||||||
|
AddressType: discovery.AddressTypeIPv4,
|
||||||
|
Ports: []discovery.EndpointPort{{
|
||||||
|
Name: ptr.To("http"),
|
||||||
|
Protocol: ptr.To(api.ProtocolTCP),
|
||||||
|
}},
|
||||||
|
Endpoints: []discovery.Endpoint{{
|
||||||
|
Addresses: generateIPAddresses(1),
|
||||||
|
Hints: &discovery.EndpointHints{
|
||||||
|
ForNodes: []discovery.ForNode{
|
||||||
|
{Name: "node-1"},
|
||||||
|
{Name: "node-2"},
|
||||||
|
{Name: "node-1"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"too-many-node-hints": {
|
||||||
|
expectedErrors: 1,
|
||||||
|
endpointSlice: &discovery.EndpointSlice{
|
||||||
|
ObjectMeta: standardMeta,
|
||||||
|
AddressType: discovery.AddressTypeIPv4,
|
||||||
|
Ports: []discovery.EndpointPort{{
|
||||||
|
Name: ptr.To("http"),
|
||||||
|
Protocol: ptr.To(api.ProtocolTCP),
|
||||||
|
}},
|
||||||
|
Endpoints: []discovery.Endpoint{{
|
||||||
|
Addresses: generateIPAddresses(1),
|
||||||
|
Hints: &discovery.EndpointHints{
|
||||||
|
ForNodes: []discovery.ForNode{
|
||||||
|
{Name: "node-1"},
|
||||||
|
{Name: "node-2"},
|
||||||
|
{Name: "node-3"},
|
||||||
|
{Name: "node-4"},
|
||||||
|
{Name: "node-5"},
|
||||||
|
{Name: "node-6"},
|
||||||
|
{Name: "node-7"},
|
||||||
|
{Name: "node-8"},
|
||||||
|
{Name: "node-9"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}},
|
||||||
|
},
|
||||||
|
},
|
||||||
"empty-everything": {
|
"empty-everything": {
|
||||||
expectedErrors: 3,
|
expectedErrors: 3,
|
||||||
endpointSlice: &discovery.EndpointSlice{},
|
endpointSlice: &discovery.EndpointSlice{},
|
||||||
|
@ -557,6 +557,12 @@ const (
|
|||||||
// Enables PortForward to be proxied with a websocket client
|
// Enables PortForward to be proxied with a websocket client
|
||||||
PortForwardWebsockets featuregate.Feature = "PortForwardWebsockets"
|
PortForwardWebsockets featuregate.Feature = "PortForwardWebsockets"
|
||||||
|
|
||||||
|
// owner: @danwinship
|
||||||
|
// kep: https://kep.k8s.io/3015
|
||||||
|
//
|
||||||
|
// Enables PreferSameZone and PreferSameNode values for trafficDistribution
|
||||||
|
PreferSameTrafficDistribution featuregate.Feature = "PreferSameTrafficDistribution"
|
||||||
|
|
||||||
// owner: @jessfraz
|
// owner: @jessfraz
|
||||||
//
|
//
|
||||||
// Enables control over ProcMountType for containers.
|
// Enables control over ProcMountType for containers.
|
||||||
|
@ -621,6 +621,10 @@ var defaultVersionedKubernetesFeatureGates = map[featuregate.Feature]featuregate
|
|||||||
{Version: version.MustParse("1.31"), Default: true, PreRelease: featuregate.Beta},
|
{Version: version.MustParse("1.31"), Default: true, PreRelease: featuregate.Beta},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
PreferSameTrafficDistribution: {
|
||||||
|
{Version: version.MustParse("1.33"), Default: false, PreRelease: featuregate.Alpha},
|
||||||
|
},
|
||||||
|
|
||||||
ProcMountType: {
|
ProcMountType: {
|
||||||
{Version: version.MustParse("1.12"), Default: false, PreRelease: featuregate.Alpha},
|
{Version: version.MustParse("1.12"), Default: false, PreRelease: featuregate.Alpha},
|
||||||
{Version: version.MustParse("1.31"), Default: false, PreRelease: featuregate.Beta},
|
{Version: version.MustParse("1.31"), Default: false, PreRelease: featuregate.Beta},
|
||||||
|
@ -142,12 +142,16 @@ func (endpointSliceStrategy) AllowUnconditionalUpdate() bool {
|
|||||||
// dropDisabledConditionsOnCreate will drop any fields that are disabled.
|
// dropDisabledConditionsOnCreate will drop any fields that are disabled.
|
||||||
func dropDisabledFieldsOnCreate(endpointSlice *discovery.EndpointSlice) {
|
func dropDisabledFieldsOnCreate(endpointSlice *discovery.EndpointSlice) {
|
||||||
dropHints := !utilfeature.DefaultFeatureGate.Enabled(features.TopologyAwareHints)
|
dropHints := !utilfeature.DefaultFeatureGate.Enabled(features.TopologyAwareHints)
|
||||||
|
dropNodeHints := !utilfeature.DefaultFeatureGate.Enabled(features.PreferSameTrafficDistribution)
|
||||||
|
if !dropHints && !dropNodeHints {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if dropHints {
|
for i := range endpointSlice.Endpoints {
|
||||||
for i := range endpointSlice.Endpoints {
|
if dropHints {
|
||||||
if dropHints {
|
endpointSlice.Endpoints[i].Hints = nil
|
||||||
endpointSlice.Endpoints[i].Hints = nil
|
} else if endpointSlice.Endpoints[i].Hints != nil {
|
||||||
}
|
endpointSlice.Endpoints[i].Hints.ForNodes = nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -156,20 +160,27 @@ func dropDisabledFieldsOnCreate(endpointSlice *discovery.EndpointSlice) {
|
|||||||
// been set on the EndpointSlice.
|
// been set on the EndpointSlice.
|
||||||
func dropDisabledFieldsOnUpdate(oldEPS, newEPS *discovery.EndpointSlice) {
|
func dropDisabledFieldsOnUpdate(oldEPS, newEPS *discovery.EndpointSlice) {
|
||||||
dropHints := !utilfeature.DefaultFeatureGate.Enabled(features.TopologyAwareHints)
|
dropHints := !utilfeature.DefaultFeatureGate.Enabled(features.TopologyAwareHints)
|
||||||
if dropHints {
|
dropNodeHints := !utilfeature.DefaultFeatureGate.Enabled(features.PreferSameTrafficDistribution)
|
||||||
|
if dropHints || dropNodeHints {
|
||||||
for _, ep := range oldEPS.Endpoints {
|
for _, ep := range oldEPS.Endpoints {
|
||||||
if ep.Hints != nil {
|
if ep.Hints != nil {
|
||||||
dropHints = false
|
dropHints = false
|
||||||
break
|
if ep.Hints.ForNodes != nil {
|
||||||
|
dropNodeHints = false
|
||||||
|
break
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if !dropHints && !dropNodeHints {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if dropHints {
|
for i := range newEPS.Endpoints {
|
||||||
for i := range newEPS.Endpoints {
|
if dropHints {
|
||||||
if dropHints {
|
newEPS.Endpoints[i].Hints = nil
|
||||||
newEPS.Endpoints[i].Hints = nil
|
} else if newEPS.Endpoints[i].Hints != nil {
|
||||||
}
|
newEPS.Endpoints[i].Hints.ForNodes = nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -36,30 +36,77 @@ import (
|
|||||||
|
|
||||||
func Test_dropDisabledFieldsOnCreate(t *testing.T) {
|
func Test_dropDisabledFieldsOnCreate(t *testing.T) {
|
||||||
testcases := []struct {
|
testcases := []struct {
|
||||||
name string
|
name string
|
||||||
hintsGateEnabled bool
|
preferSameEnabled bool
|
||||||
eps *discovery.EndpointSlice
|
eps *discovery.EndpointSlice
|
||||||
expectedEPS *discovery.EndpointSlice
|
expectedEPS *discovery.EndpointSlice
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "node name gate enabled, field should be allowed",
|
name: "PreferSameTrafficDistribution gate enabled, ForNodes should be allowed",
|
||||||
|
preferSameEnabled: true,
|
||||||
eps: &discovery.EndpointSlice{
|
eps: &discovery.EndpointSlice{
|
||||||
Endpoints: []discovery.Endpoint{
|
Endpoints: []discovery.Endpoint{
|
||||||
{
|
{
|
||||||
NodeName: ptr.To("node-1"),
|
Hints: &discovery.EndpointHints{
|
||||||
|
ForZones: []discovery.ForZone{{Name: "zone-a"}},
|
||||||
|
ForNodes: []discovery.ForNode{{Name: "node-1"}},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
NodeName: ptr.To("node-2"),
|
Hints: &discovery.EndpointHints{
|
||||||
|
ForZones: []discovery.ForZone{{Name: "zone-a"}},
|
||||||
|
ForNodes: []discovery.ForNode{{Name: "node-2"}},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
expectedEPS: &discovery.EndpointSlice{
|
expectedEPS: &discovery.EndpointSlice{
|
||||||
Endpoints: []discovery.Endpoint{
|
Endpoints: []discovery.Endpoint{
|
||||||
{
|
{
|
||||||
NodeName: ptr.To("node-1"),
|
Hints: &discovery.EndpointHints{
|
||||||
|
ForZones: []discovery.ForZone{{Name: "zone-a"}},
|
||||||
|
ForNodes: []discovery.ForNode{{Name: "node-1"}},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
NodeName: ptr.To("node-2"),
|
Hints: &discovery.EndpointHints{
|
||||||
|
ForZones: []discovery.ForZone{{Name: "zone-a"}},
|
||||||
|
ForNodes: []discovery.ForNode{{Name: "node-2"}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "PreferSameTrafficDistribution gate disabled, ForNodes should not be allowed",
|
||||||
|
preferSameEnabled: false,
|
||||||
|
eps: &discovery.EndpointSlice{
|
||||||
|
Endpoints: []discovery.Endpoint{
|
||||||
|
{
|
||||||
|
Hints: &discovery.EndpointHints{
|
||||||
|
ForZones: []discovery.ForZone{{Name: "zone-a"}},
|
||||||
|
ForNodes: []discovery.ForNode{{Name: "node-1"}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Hints: &discovery.EndpointHints{
|
||||||
|
ForZones: []discovery.ForZone{{Name: "zone-a"}},
|
||||||
|
ForNodes: []discovery.ForNode{{Name: "node-2"}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expectedEPS: &discovery.EndpointSlice{
|
||||||
|
Endpoints: []discovery.Endpoint{
|
||||||
|
{
|
||||||
|
Hints: &discovery.EndpointHints{
|
||||||
|
ForZones: []discovery.ForZone{{Name: "zone-a"}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Hints: &discovery.EndpointHints{
|
||||||
|
ForZones: []discovery.ForZone{{Name: "zone-a"}},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -68,10 +115,7 @@ func Test_dropDisabledFieldsOnCreate(t *testing.T) {
|
|||||||
|
|
||||||
for _, testcase := range testcases {
|
for _, testcase := range testcases {
|
||||||
t.Run(testcase.name, func(t *testing.T) {
|
t.Run(testcase.name, func(t *testing.T) {
|
||||||
if !testcase.hintsGateEnabled {
|
featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.PreferSameTrafficDistribution, testcase.preferSameEnabled)
|
||||||
featuregatetesting.SetFeatureGateEmulationVersionDuringTest(t, utilfeature.DefaultFeatureGate, version.MustParse("1.32"))
|
|
||||||
}
|
|
||||||
featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.TopologyAwareHints, testcase.hintsGateEnabled)
|
|
||||||
|
|
||||||
dropDisabledFieldsOnCreate(testcase.eps)
|
dropDisabledFieldsOnCreate(testcase.eps)
|
||||||
if !apiequality.Semantic.DeepEqual(testcase.eps, testcase.expectedEPS) {
|
if !apiequality.Semantic.DeepEqual(testcase.eps, testcase.expectedEPS) {
|
||||||
@ -85,78 +129,13 @@ func Test_dropDisabledFieldsOnCreate(t *testing.T) {
|
|||||||
|
|
||||||
func Test_dropDisabledFieldsOnUpdate(t *testing.T) {
|
func Test_dropDisabledFieldsOnUpdate(t *testing.T) {
|
||||||
testcases := []struct {
|
testcases := []struct {
|
||||||
name string
|
name string
|
||||||
hintsGateEnabled bool
|
hintsGateEnabled bool
|
||||||
oldEPS *discovery.EndpointSlice
|
preferSameEnabled bool
|
||||||
newEPS *discovery.EndpointSlice
|
oldEPS *discovery.EndpointSlice
|
||||||
expectedEPS *discovery.EndpointSlice
|
newEPS *discovery.EndpointSlice
|
||||||
|
expectedEPS *discovery.EndpointSlice
|
||||||
}{
|
}{
|
||||||
{
|
|
||||||
name: "node name gate enabled, set on new EPS",
|
|
||||||
oldEPS: &discovery.EndpointSlice{
|
|
||||||
Endpoints: []discovery.Endpoint{
|
|
||||||
{
|
|
||||||
NodeName: nil,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
NodeName: nil,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
newEPS: &discovery.EndpointSlice{
|
|
||||||
Endpoints: []discovery.Endpoint{
|
|
||||||
{
|
|
||||||
NodeName: ptr.To("node-1"),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
NodeName: ptr.To("node-2"),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
expectedEPS: &discovery.EndpointSlice{
|
|
||||||
Endpoints: []discovery.Endpoint{
|
|
||||||
{
|
|
||||||
NodeName: ptr.To("node-1"),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
NodeName: ptr.To("node-2"),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "node name gate disabled, set on old and updated EPS",
|
|
||||||
oldEPS: &discovery.EndpointSlice{
|
|
||||||
Endpoints: []discovery.Endpoint{
|
|
||||||
{
|
|
||||||
NodeName: ptr.To("node-1-old"),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
NodeName: ptr.To("node-2-old"),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
newEPS: &discovery.EndpointSlice{
|
|
||||||
Endpoints: []discovery.Endpoint{
|
|
||||||
{
|
|
||||||
NodeName: ptr.To("node-1"),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
NodeName: ptr.To("node-2"),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
expectedEPS: &discovery.EndpointSlice{
|
|
||||||
Endpoints: []discovery.Endpoint{
|
|
||||||
{
|
|
||||||
NodeName: ptr.To("node-1"),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
NodeName: ptr.To("node-2"),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
name: "hints gate enabled, set on new EPS",
|
name: "hints gate enabled, set on new EPS",
|
||||||
hintsGateEnabled: true,
|
hintsGateEnabled: true,
|
||||||
@ -283,6 +262,151 @@ func Test_dropDisabledFieldsOnUpdate(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "PreferSameTrafficDistribution gate enabled, set on new EPS",
|
||||||
|
hintsGateEnabled: true,
|
||||||
|
preferSameEnabled: true,
|
||||||
|
oldEPS: &discovery.EndpointSlice{
|
||||||
|
Endpoints: []discovery.Endpoint{
|
||||||
|
{
|
||||||
|
Hints: nil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Hints: nil,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
newEPS: &discovery.EndpointSlice{
|
||||||
|
Endpoints: []discovery.Endpoint{
|
||||||
|
{
|
||||||
|
Hints: &discovery.EndpointHints{
|
||||||
|
ForZones: []discovery.ForZone{{Name: "zone-a"}},
|
||||||
|
ForNodes: []discovery.ForNode{{Name: "node-1"}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Hints: &discovery.EndpointHints{
|
||||||
|
ForZones: []discovery.ForZone{{Name: "zone-a"}},
|
||||||
|
ForNodes: []discovery.ForNode{{Name: "node-2"}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expectedEPS: &discovery.EndpointSlice{
|
||||||
|
Endpoints: []discovery.Endpoint{
|
||||||
|
{
|
||||||
|
Hints: &discovery.EndpointHints{
|
||||||
|
ForZones: []discovery.ForZone{{Name: "zone-a"}},
|
||||||
|
ForNodes: []discovery.ForNode{{Name: "node-1"}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Hints: &discovery.EndpointHints{
|
||||||
|
ForZones: []discovery.ForZone{{Name: "zone-a"}},
|
||||||
|
ForNodes: []discovery.ForNode{{Name: "node-2"}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "PreferSameTrafficDistribution gate disabled, set on new EPS",
|
||||||
|
hintsGateEnabled: true,
|
||||||
|
preferSameEnabled: false,
|
||||||
|
oldEPS: &discovery.EndpointSlice{
|
||||||
|
Endpoints: []discovery.Endpoint{
|
||||||
|
{
|
||||||
|
Hints: nil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Hints: nil,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
newEPS: &discovery.EndpointSlice{
|
||||||
|
Endpoints: []discovery.Endpoint{
|
||||||
|
{
|
||||||
|
Hints: &discovery.EndpointHints{
|
||||||
|
ForZones: []discovery.ForZone{{Name: "zone-a"}},
|
||||||
|
ForNodes: []discovery.ForNode{{Name: "node-1"}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Hints: &discovery.EndpointHints{
|
||||||
|
ForZones: []discovery.ForZone{{Name: "zone-a"}},
|
||||||
|
ForNodes: []discovery.ForNode{{Name: "node-2"}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expectedEPS: &discovery.EndpointSlice{
|
||||||
|
Endpoints: []discovery.Endpoint{
|
||||||
|
{
|
||||||
|
Hints: &discovery.EndpointHints{
|
||||||
|
ForZones: []discovery.ForZone{{Name: "zone-a"}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Hints: &discovery.EndpointHints{
|
||||||
|
ForZones: []discovery.ForZone{{Name: "zone-a"}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "PreferSameTrafficDiscovery gate disabled, set on new and old EPS",
|
||||||
|
hintsGateEnabled: true,
|
||||||
|
preferSameEnabled: false,
|
||||||
|
oldEPS: &discovery.EndpointSlice{
|
||||||
|
Endpoints: []discovery.Endpoint{
|
||||||
|
{
|
||||||
|
Hints: &discovery.EndpointHints{
|
||||||
|
ForZones: []discovery.ForZone{{Name: "zone-a-old"}},
|
||||||
|
ForNodes: []discovery.ForNode{{Name: "node-1-old"}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Hints: &discovery.EndpointHints{
|
||||||
|
ForZones: []discovery.ForZone{{Name: "zone-a-old"}},
|
||||||
|
ForNodes: []discovery.ForNode{{Name: "node-2-old"}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
newEPS: &discovery.EndpointSlice{
|
||||||
|
Endpoints: []discovery.Endpoint{
|
||||||
|
{
|
||||||
|
Hints: &discovery.EndpointHints{
|
||||||
|
ForZones: []discovery.ForZone{{Name: "zone-a"}},
|
||||||
|
ForNodes: []discovery.ForNode{{Name: "node-1"}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Hints: &discovery.EndpointHints{
|
||||||
|
ForZones: []discovery.ForZone{{Name: "zone-a"}},
|
||||||
|
ForNodes: []discovery.ForNode{{Name: "node-2"}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expectedEPS: &discovery.EndpointSlice{
|
||||||
|
Endpoints: []discovery.Endpoint{
|
||||||
|
{
|
||||||
|
Hints: &discovery.EndpointHints{
|
||||||
|
ForZones: []discovery.ForZone{{Name: "zone-a"}},
|
||||||
|
ForNodes: []discovery.ForNode{{Name: "node-1"}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Hints: &discovery.EndpointHints{
|
||||||
|
ForZones: []discovery.ForZone{{Name: "zone-a"}},
|
||||||
|
ForNodes: []discovery.ForNode{{Name: "node-2"}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, testcase := range testcases {
|
for _, testcase := range testcases {
|
||||||
@ -291,6 +415,9 @@ func Test_dropDisabledFieldsOnUpdate(t *testing.T) {
|
|||||||
featuregatetesting.SetFeatureGateEmulationVersionDuringTest(t, utilfeature.DefaultFeatureGate, version.MustParse("1.32"))
|
featuregatetesting.SetFeatureGateEmulationVersionDuringTest(t, utilfeature.DefaultFeatureGate, version.MustParse("1.32"))
|
||||||
}
|
}
|
||||||
featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.TopologyAwareHints, testcase.hintsGateEnabled)
|
featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.TopologyAwareHints, testcase.hintsGateEnabled)
|
||||||
|
if testcase.hintsGateEnabled {
|
||||||
|
featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.PreferSameTrafficDistribution, testcase.preferSameEnabled)
|
||||||
|
}
|
||||||
|
|
||||||
dropDisabledFieldsOnUpdate(testcase.oldEPS, testcase.newEPS)
|
dropDisabledFieldsOnUpdate(testcase.oldEPS, testcase.newEPS)
|
||||||
if !apiequality.Semantic.DeepEqual(testcase.newEPS, testcase.expectedEPS) {
|
if !apiequality.Semantic.DeepEqual(testcase.newEPS, testcase.expectedEPS) {
|
||||||
|
@ -5374,12 +5374,27 @@ const (
|
|||||||
|
|
||||||
// These are valid values for the TrafficDistribution field of a Service.
|
// These are valid values for the TrafficDistribution field of a Service.
|
||||||
const (
|
const (
|
||||||
// Indicates a preference for routing traffic to endpoints that are in the
|
// Indicates a preference for routing traffic to endpoints that are in the same
|
||||||
// same zone as the client. Setting this value gives implementations
|
// zone as the client. Users should not set this value unless they have ensured
|
||||||
// permission to make different tradeoffs, e.g. optimizing for proximity
|
// that clients and endpoints are distributed in such a way that the "same zone"
|
||||||
// rather than equal distribution of load. Users should not set this value
|
// preference will not result in endpoints getting overloaded.
|
||||||
// if such tradeoffs are not acceptable.
|
|
||||||
ServiceTrafficDistributionPreferClose = "PreferClose"
|
ServiceTrafficDistributionPreferClose = "PreferClose"
|
||||||
|
|
||||||
|
// Indicates a preference for routing traffic to endpoints that are in the same
|
||||||
|
// zone as the client. Users should not set this value unless they have ensured
|
||||||
|
// that clients and endpoints are distributed in such a way that the "same zone"
|
||||||
|
// preference will not result in endpoints getting overloaded.
|
||||||
|
// This is an alias for "PreferClose", but it is an Alpha feature and is only
|
||||||
|
// recognized if the PreferSameTrafficDistribution feature gate is enabled.
|
||||||
|
ServiceTrafficDistributionPreferSameZone = "PreferSameZone"
|
||||||
|
|
||||||
|
// Indicates a preference for routing traffic to endpoints that are on the same
|
||||||
|
// node as the client. Users should not set this value unless they have ensured
|
||||||
|
// that clients and endpoints are distributed in such a way that the "same node"
|
||||||
|
// preference will not result in endpoints getting overloaded.
|
||||||
|
// This is an Alpha feature and is only recognized if the
|
||||||
|
// PreferSameTrafficDistribution feature gate is enabled.
|
||||||
|
ServiceTrafficDistributionPreferSameNode = "PreferSameNode"
|
||||||
)
|
)
|
||||||
|
|
||||||
// These are the valid conditions of a service.
|
// These are the valid conditions of a service.
|
||||||
|
@ -159,10 +159,17 @@ type EndpointConditions struct {
|
|||||||
|
|
||||||
// EndpointHints provides hints describing how an endpoint should be consumed.
|
// EndpointHints provides hints describing how an endpoint should be consumed.
|
||||||
type EndpointHints struct {
|
type EndpointHints struct {
|
||||||
// forZones indicates the zone(s) this endpoint should be consumed by to
|
// forZones indicates the zone(s) this endpoint should be consumed by when
|
||||||
// enable topology aware routing.
|
// using topology aware routing. May contain a maximum of 8 entries.
|
||||||
// +listType=atomic
|
// +listType=atomic
|
||||||
ForZones []ForZone `json:"forZones,omitempty" protobuf:"bytes,1,name=forZones"`
|
ForZones []ForZone `json:"forZones,omitempty" protobuf:"bytes,1,name=forZones"`
|
||||||
|
|
||||||
|
// forNodes indicates the node(s) this endpoint should be consumed by when
|
||||||
|
// using topology aware routing. May contain a maximum of 8 entries.
|
||||||
|
// This is an Alpha feature and is only used when the PreferSameTrafficDistribution
|
||||||
|
// feature gate is enabled.
|
||||||
|
// +listType=atomic
|
||||||
|
ForNodes []ForNode `json:"forNodes,omitempty" protobuf:"bytes,2,name=forNodes"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// ForZone provides information about which zones should consume this endpoint.
|
// ForZone provides information about which zones should consume this endpoint.
|
||||||
@ -171,6 +178,12 @@ type ForZone struct {
|
|||||||
Name string `json:"name" protobuf:"bytes,1,name=name"`
|
Name string `json:"name" protobuf:"bytes,1,name=name"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ForNode provides information about which nodes should consume this endpoint.
|
||||||
|
type ForNode struct {
|
||||||
|
// name represents the name of the node.
|
||||||
|
Name string `json:"name" protobuf:"bytes,1,name=name"`
|
||||||
|
}
|
||||||
|
|
||||||
// EndpointPort represents a Port used by an EndpointSlice
|
// EndpointPort represents a Port used by an EndpointSlice
|
||||||
// +structType=atomic
|
// +structType=atomic
|
||||||
type EndpointPort struct {
|
type EndpointPort struct {
|
||||||
|
@ -161,6 +161,13 @@ type EndpointHints struct {
|
|||||||
// enable topology aware routing. May contain a maximum of 8 entries.
|
// enable topology aware routing. May contain a maximum of 8 entries.
|
||||||
// +listType=atomic
|
// +listType=atomic
|
||||||
ForZones []ForZone `json:"forZones,omitempty" protobuf:"bytes,1,name=forZones"`
|
ForZones []ForZone `json:"forZones,omitempty" protobuf:"bytes,1,name=forZones"`
|
||||||
|
|
||||||
|
// forNodes indicates the node(s) this endpoint should be consumed by when
|
||||||
|
// using topology aware routing. May contain a maximum of 8 entries.
|
||||||
|
// This is an Alpha feature and is only used when the PreferSameTrafficDistribution
|
||||||
|
// feature gate is enabled.
|
||||||
|
// +listType=atomic
|
||||||
|
ForNodes []string `json:"forNodes,omitempty" protobuf:"bytes,2,name=forNodes"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// ForZone provides information about which zones should consume this endpoint.
|
// ForZone provides information about which zones should consume this endpoint.
|
||||||
|
@ -1049,6 +1049,12 @@
|
|||||||
lockToDefault: false
|
lockToDefault: false
|
||||||
preRelease: Beta
|
preRelease: Beta
|
||||||
version: "1.31"
|
version: "1.31"
|
||||||
|
- name: PreferSameTrafficDistribution
|
||||||
|
versionedSpecs:
|
||||||
|
- default: false
|
||||||
|
lockToDefault: false
|
||||||
|
preRelease: Alpha
|
||||||
|
version: "1.33"
|
||||||
- name: ProcMountType
|
- name: ProcMountType
|
||||||
versionedSpecs:
|
versionedSpecs:
|
||||||
- default: false
|
- default: false
|
||||||
|
Loading…
Reference in New Issue
Block a user