| /* |
| Copyright 2019 The Kubernetes Authors. |
| |
| Licensed under the Apache License, Version 2.0 (the "License"); |
| you may not use this file except in compliance with the License. |
| You may obtain a copy of the License at |
| |
| http://www.apache.org/licenses/LICENSE-2.0 |
| |
| Unless required by applicable law or agreed to in writing, software |
| distributed under the License is distributed on an "AS IS" BASIS, |
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| See the License for the specific language governing permissions and |
| limitations under the License. |
| */ |
| |
| package proxy |
| |
| import ( |
| "fmt" |
| "testing" |
| |
| v1 "k8s.io/api/core/v1" |
| kerrors "k8s.io/apimachinery/pkg/util/errors" |
| "k8s.io/apimachinery/pkg/util/sets" |
| utilfeature "k8s.io/apiserver/pkg/util/feature" |
| featuregatetesting "k8s.io/component-base/featuregate/testing" |
| "k8s.io/kubernetes/pkg/features" |
| ) |
| |
| func checkExpectedEndpoints(expected sets.Set[string], actual []Endpoint) error { |
| var errs []error |
| |
| expectedCopy := sets.New[string](expected.UnsortedList()...) |
| for _, ep := range actual { |
| if !expectedCopy.Has(ep.String()) { |
| errs = append(errs, fmt.Errorf("unexpected endpoint %v", ep)) |
| } |
| expectedCopy.Delete(ep.String()) |
| } |
| if len(expectedCopy) > 0 { |
| errs = append(errs, fmt.Errorf("missing endpoints %v", expectedCopy.UnsortedList())) |
| } |
| |
| return kerrors.NewAggregate(errs) |
| } |
| |
| func TestCategorizeEndpoints(t *testing.T) { |
| testCases := []struct { |
| name string |
| hintsEnabled bool |
| trafficDistFeatureEnabled bool |
| pteEnabled bool |
| nodeLabels map[string]string |
| serviceInfo ServicePort |
| endpoints []Endpoint |
| |
| // We distinguish `nil` ("service doesn't use this kind of endpoints") from |
| // `sets.Set[string]()` ("service uses this kind of endpoints but has no endpoints"). |
| // allEndpoints can be left unset if only one of clusterEndpoints and |
| // localEndpoints is set, and allEndpoints is identical to it. |
| // onlyRemoteEndpoints should be true if CategorizeEndpoints returns true for |
| // hasAnyEndpoints despite allEndpoints being empty. |
| clusterEndpoints sets.Set[string] |
| localEndpoints sets.Set[string] |
| allEndpoints sets.Set[string] |
| onlyRemoteEndpoints bool |
| }{{ |
| name: "hints enabled, hints annotation == auto", |
| hintsEnabled: true, |
| nodeLabels: map[string]string{v1.LabelTopologyZone: "zone-a"}, |
| serviceInfo: &BaseServicePortInfo{hintsAnnotation: "auto"}, |
| endpoints: []Endpoint{ |
| &BaseEndpointInfo{endpoint: "10.1.2.3:80", zoneHints: sets.New[string]("zone-a"), ready: true}, |
| &BaseEndpointInfo{endpoint: "10.1.2.4:80", zoneHints: sets.New[string]("zone-b"), ready: true}, |
| &BaseEndpointInfo{endpoint: "10.1.2.5:80", zoneHints: sets.New[string]("zone-c"), ready: true}, |
| &BaseEndpointInfo{endpoint: "10.1.2.6:80", zoneHints: sets.New[string]("zone-a"), ready: true}, |
| }, |
| clusterEndpoints: sets.New[string]("10.1.2.3:80", "10.1.2.6:80"), |
| localEndpoints: nil, |
| }, { |
| name: "hints, hints annotation == disabled, hints ignored", |
| hintsEnabled: true, |
| nodeLabels: map[string]string{v1.LabelTopologyZone: "zone-a"}, |
| serviceInfo: &BaseServicePortInfo{hintsAnnotation: "disabled"}, |
| endpoints: []Endpoint{ |
| &BaseEndpointInfo{endpoint: "10.1.2.3:80", zoneHints: sets.New[string]("zone-a"), ready: true}, |
| &BaseEndpointInfo{endpoint: "10.1.2.4:80", zoneHints: sets.New[string]("zone-b"), ready: true}, |
| &BaseEndpointInfo{endpoint: "10.1.2.5:80", zoneHints: sets.New[string]("zone-c"), ready: true}, |
| &BaseEndpointInfo{endpoint: "10.1.2.6:80", zoneHints: sets.New[string]("zone-a"), ready: true}, |
| }, |
| clusterEndpoints: sets.New[string]("10.1.2.3:80", "10.1.2.4:80", "10.1.2.5:80", "10.1.2.6:80"), |
| localEndpoints: nil, |
| }, { |
| name: "hints disabled, hints annotation == auto", |
| hintsEnabled: false, |
| nodeLabels: map[string]string{v1.LabelTopologyZone: "zone-a"}, |
| serviceInfo: &BaseServicePortInfo{hintsAnnotation: "auto"}, |
| endpoints: []Endpoint{ |
| &BaseEndpointInfo{endpoint: "10.1.2.3:80", zoneHints: sets.New[string]("zone-a"), ready: true}, |
| &BaseEndpointInfo{endpoint: "10.1.2.4:80", zoneHints: sets.New[string]("zone-b"), ready: true}, |
| &BaseEndpointInfo{endpoint: "10.1.2.5:80", zoneHints: sets.New[string]("zone-c"), ready: true}, |
| &BaseEndpointInfo{endpoint: "10.1.2.6:80", zoneHints: sets.New[string]("zone-a"), ready: true}, |
| }, |
| clusterEndpoints: sets.New[string]("10.1.2.3:80", "10.1.2.4:80", "10.1.2.5:80", "10.1.2.6:80"), |
| localEndpoints: nil, |
| }, { |
| |
| name: "hints, hints annotation == aUto (wrong capitalization), hints no longer ignored", |
| hintsEnabled: true, |
| nodeLabels: map[string]string{v1.LabelTopologyZone: "zone-a"}, |
| serviceInfo: &BaseServicePortInfo{hintsAnnotation: "aUto"}, |
| endpoints: []Endpoint{ |
| &BaseEndpointInfo{endpoint: "10.1.2.3:80", zoneHints: sets.New[string]("zone-a"), ready: true}, |
| &BaseEndpointInfo{endpoint: "10.1.2.4:80", zoneHints: sets.New[string]("zone-b"), ready: true}, |
| &BaseEndpointInfo{endpoint: "10.1.2.5:80", zoneHints: sets.New[string]("zone-c"), ready: true}, |
| &BaseEndpointInfo{endpoint: "10.1.2.6:80", zoneHints: sets.New[string]("zone-a"), ready: true}, |
| }, |
| clusterEndpoints: sets.New[string]("10.1.2.3:80", "10.1.2.6:80"), |
| localEndpoints: nil, |
| }, { |
| name: "hints, hints annotation empty, hints ignored", |
| hintsEnabled: true, |
| nodeLabels: map[string]string{v1.LabelTopologyZone: "zone-a"}, |
| serviceInfo: &BaseServicePortInfo{}, |
| endpoints: []Endpoint{ |
| &BaseEndpointInfo{endpoint: "10.1.2.3:80", zoneHints: sets.New[string]("zone-a"), ready: true}, |
| &BaseEndpointInfo{endpoint: "10.1.2.4:80", zoneHints: sets.New[string]("zone-b"), ready: true}, |
| &BaseEndpointInfo{endpoint: "10.1.2.5:80", zoneHints: sets.New[string]("zone-c"), ready: true}, |
| &BaseEndpointInfo{endpoint: "10.1.2.6:80", zoneHints: sets.New[string]("zone-a"), ready: true}, |
| }, |
| clusterEndpoints: sets.New[string]("10.1.2.3:80", "10.1.2.4:80", "10.1.2.5:80", "10.1.2.6:80"), |
| localEndpoints: nil, |
| }, { |
| name: "hints, hints annotation empty but trafficDist feature gate enabled, hints are not ignored", |
| hintsEnabled: true, |
| trafficDistFeatureEnabled: true, |
| nodeLabels: map[string]string{v1.LabelTopologyZone: "zone-a"}, |
| serviceInfo: &BaseServicePortInfo{}, |
| endpoints: []Endpoint{ |
| &BaseEndpointInfo{endpoint: "10.1.2.3:80", zoneHints: sets.New[string]("zone-a"), ready: true}, |
| &BaseEndpointInfo{endpoint: "10.1.2.4:80", zoneHints: sets.New[string]("zone-b"), ready: true}, |
| &BaseEndpointInfo{endpoint: "10.1.2.5:80", zoneHints: sets.New[string]("zone-c"), ready: true}, |
| &BaseEndpointInfo{endpoint: "10.1.2.6:80", zoneHints: sets.New[string]("zone-a"), ready: true}, |
| }, |
| clusterEndpoints: sets.New[string]("10.1.2.3:80", "10.1.2.6:80"), |
| localEndpoints: nil, |
| }, { |
| name: "hints disabled, trafficDist feature gate enabled, hints are not ignored", |
| hintsEnabled: false, |
| trafficDistFeatureEnabled: true, |
| nodeLabels: map[string]string{v1.LabelTopologyZone: "zone-a"}, |
| serviceInfo: &BaseServicePortInfo{}, |
| endpoints: []Endpoint{ |
| &BaseEndpointInfo{endpoint: "10.1.2.3:80", zoneHints: sets.New[string]("zone-a"), ready: true}, |
| &BaseEndpointInfo{endpoint: "10.1.2.4:80", zoneHints: sets.New[string]("zone-b"), ready: true}, |
| &BaseEndpointInfo{endpoint: "10.1.2.5:80", zoneHints: sets.New[string]("zone-c"), ready: true}, |
| &BaseEndpointInfo{endpoint: "10.1.2.6:80", zoneHints: sets.New[string]("zone-a"), ready: true}, |
| }, |
| clusterEndpoints: sets.New[string]("10.1.2.3:80", "10.1.2.6:80"), |
| localEndpoints: nil, |
| }, { |
| name: "externalTrafficPolicy: Local, topology ignored for Local endpoints", |
| hintsEnabled: true, |
| trafficDistFeatureEnabled: true, |
| nodeLabels: map[string]string{v1.LabelTopologyZone: "zone-a"}, |
| serviceInfo: &BaseServicePortInfo{externalPolicyLocal: true, nodePort: 8080, hintsAnnotation: "auto"}, |
| endpoints: []Endpoint{ |
| &BaseEndpointInfo{endpoint: "10.1.2.3:80", zoneHints: sets.New[string]("zone-a"), ready: true, isLocal: true}, |
| &BaseEndpointInfo{endpoint: "10.1.2.4:80", zoneHints: sets.New[string]("zone-b"), ready: true, isLocal: true}, |
| &BaseEndpointInfo{endpoint: "10.1.2.5:80", zoneHints: sets.New[string]("zone-c"), ready: true}, |
| &BaseEndpointInfo{endpoint: "10.1.2.6:80", zoneHints: sets.New[string]("zone-a"), ready: true}, |
| }, |
| clusterEndpoints: sets.New[string]("10.1.2.3:80", "10.1.2.6:80"), |
| localEndpoints: sets.New[string]("10.1.2.3:80", "10.1.2.4:80"), |
| allEndpoints: sets.New[string]("10.1.2.3:80", "10.1.2.4:80", "10.1.2.6:80"), |
| }, { |
| name: "internalTrafficPolicy: Local, topology ignored for Local endpoints", |
| hintsEnabled: true, |
| trafficDistFeatureEnabled: true, |
| nodeLabels: map[string]string{v1.LabelTopologyZone: "zone-a"}, |
| serviceInfo: &BaseServicePortInfo{internalPolicyLocal: true, hintsAnnotation: "auto", externalPolicyLocal: false, nodePort: 8080}, |
| endpoints: []Endpoint{ |
| &BaseEndpointInfo{endpoint: "10.1.2.3:80", zoneHints: sets.New[string]("zone-a"), ready: true, isLocal: true}, |
| &BaseEndpointInfo{endpoint: "10.1.2.4:80", zoneHints: sets.New[string]("zone-b"), ready: true, isLocal: true}, |
| &BaseEndpointInfo{endpoint: "10.1.2.5:80", zoneHints: sets.New[string]("zone-c"), ready: true}, |
| &BaseEndpointInfo{endpoint: "10.1.2.6:80", zoneHints: sets.New[string]("zone-a"), ready: true}, |
| }, |
| clusterEndpoints: sets.New[string]("10.1.2.3:80", "10.1.2.6:80"), |
| localEndpoints: sets.New[string]("10.1.2.3:80", "10.1.2.4:80"), |
| allEndpoints: sets.New[string]("10.1.2.3:80", "10.1.2.4:80", "10.1.2.6:80"), |
| }, { |
| name: "empty node labels", |
| hintsEnabled: true, |
| nodeLabels: map[string]string{}, |
| serviceInfo: &BaseServicePortInfo{hintsAnnotation: "auto"}, |
| endpoints: []Endpoint{ |
| &BaseEndpointInfo{endpoint: "10.1.2.3:80", zoneHints: sets.New[string]("zone-a"), ready: true}, |
| }, |
| clusterEndpoints: sets.New[string]("10.1.2.3:80"), |
| localEndpoints: nil, |
| }, { |
| name: "empty zone label", |
| hintsEnabled: true, |
| nodeLabels: map[string]string{v1.LabelTopologyZone: ""}, |
| serviceInfo: &BaseServicePortInfo{hintsAnnotation: "auto"}, |
| endpoints: []Endpoint{ |
| &BaseEndpointInfo{endpoint: "10.1.2.3:80", zoneHints: sets.New[string]("zone-a"), ready: true}, |
| }, |
| clusterEndpoints: sets.New[string]("10.1.2.3:80"), |
| localEndpoints: nil, |
| }, { |
| name: "node in different zone, no endpoint filtering", |
| hintsEnabled: true, |
| nodeLabels: map[string]string{v1.LabelTopologyZone: "zone-b"}, |
| serviceInfo: &BaseServicePortInfo{hintsAnnotation: "auto"}, |
| endpoints: []Endpoint{ |
| &BaseEndpointInfo{endpoint: "10.1.2.3:80", zoneHints: sets.New[string]("zone-a"), ready: true}, |
| }, |
| clusterEndpoints: sets.New[string]("10.1.2.3:80"), |
| localEndpoints: nil, |
| }, { |
| name: "normal endpoint filtering, auto annotation", |
| hintsEnabled: true, |
| nodeLabels: map[string]string{v1.LabelTopologyZone: "zone-a"}, |
| serviceInfo: &BaseServicePortInfo{hintsAnnotation: "auto"}, |
| endpoints: []Endpoint{ |
| &BaseEndpointInfo{endpoint: "10.1.2.3:80", zoneHints: sets.New[string]("zone-a"), ready: true}, |
| &BaseEndpointInfo{endpoint: "10.1.2.4:80", zoneHints: sets.New[string]("zone-b"), ready: true}, |
| &BaseEndpointInfo{endpoint: "10.1.2.5:80", zoneHints: sets.New[string]("zone-c"), ready: true}, |
| &BaseEndpointInfo{endpoint: "10.1.2.6:80", zoneHints: sets.New[string]("zone-a"), ready: true}, |
| }, |
| clusterEndpoints: sets.New[string]("10.1.2.3:80", "10.1.2.6:80"), |
| localEndpoints: nil, |
| }, { |
| name: "unready endpoint", |
| hintsEnabled: true, |
| nodeLabels: map[string]string{v1.LabelTopologyZone: "zone-a"}, |
| serviceInfo: &BaseServicePortInfo{hintsAnnotation: "auto"}, |
| endpoints: []Endpoint{ |
| &BaseEndpointInfo{endpoint: "10.1.2.3:80", zoneHints: sets.New[string]("zone-a"), ready: true}, |
| &BaseEndpointInfo{endpoint: "10.1.2.4:80", zoneHints: sets.New[string]("zone-b"), ready: true}, |
| &BaseEndpointInfo{endpoint: "10.1.2.5:80", zoneHints: sets.New[string]("zone-c"), ready: true}, |
| &BaseEndpointInfo{endpoint: "10.1.2.6:80", zoneHints: sets.New[string]("zone-a"), ready: false}, |
| }, |
| clusterEndpoints: sets.New[string]("10.1.2.3:80"), |
| localEndpoints: nil, |
| }, { |
| name: "only unready endpoints in same zone (should not filter)", |
| hintsEnabled: true, |
| nodeLabels: map[string]string{v1.LabelTopologyZone: "zone-a"}, |
| serviceInfo: &BaseServicePortInfo{hintsAnnotation: "auto"}, |
| endpoints: []Endpoint{ |
| &BaseEndpointInfo{endpoint: "10.1.2.3:80", zoneHints: sets.New[string]("zone-a"), ready: false}, |
| &BaseEndpointInfo{endpoint: "10.1.2.4:80", zoneHints: sets.New[string]("zone-b"), ready: true}, |
| &BaseEndpointInfo{endpoint: "10.1.2.5:80", zoneHints: sets.New[string]("zone-c"), ready: true}, |
| &BaseEndpointInfo{endpoint: "10.1.2.6:80", zoneHints: sets.New[string]("zone-a"), ready: false}, |
| }, |
| clusterEndpoints: sets.New[string]("10.1.2.4:80", "10.1.2.5:80"), |
| localEndpoints: nil, |
| }, { |
| name: "normal endpoint filtering, Auto annotation", |
| hintsEnabled: true, |
| nodeLabels: map[string]string{v1.LabelTopologyZone: "zone-a"}, |
| serviceInfo: &BaseServicePortInfo{hintsAnnotation: "Auto"}, |
| endpoints: []Endpoint{ |
| &BaseEndpointInfo{endpoint: "10.1.2.3:80", zoneHints: sets.New[string]("zone-a"), ready: true}, |
| &BaseEndpointInfo{endpoint: "10.1.2.4:80", zoneHints: sets.New[string]("zone-b"), ready: true}, |
| &BaseEndpointInfo{endpoint: "10.1.2.5:80", zoneHints: sets.New[string]("zone-c"), ready: true}, |
| &BaseEndpointInfo{endpoint: "10.1.2.6:80", zoneHints: sets.New[string]("zone-a"), ready: true}, |
| }, |
| clusterEndpoints: sets.New[string]("10.1.2.3:80", "10.1.2.6:80"), |
| localEndpoints: nil, |
| }, { |
| name: "hintsAnnotation empty, no filtering applied", |
| hintsEnabled: true, |
| nodeLabels: map[string]string{v1.LabelTopologyZone: "zone-a"}, |
| serviceInfo: &BaseServicePortInfo{hintsAnnotation: ""}, |
| endpoints: []Endpoint{ |
| &BaseEndpointInfo{endpoint: "10.1.2.3:80", zoneHints: sets.New[string]("zone-a"), ready: true}, |
| &BaseEndpointInfo{endpoint: "10.1.2.4:80", zoneHints: sets.New[string]("zone-b"), ready: true}, |
| &BaseEndpointInfo{endpoint: "10.1.2.5:80", zoneHints: sets.New[string]("zone-c"), ready: true}, |
| &BaseEndpointInfo{endpoint: "10.1.2.6:80", zoneHints: sets.New[string]("zone-a"), ready: true}, |
| }, |
| clusterEndpoints: sets.New[string]("10.1.2.3:80", "10.1.2.4:80", "10.1.2.5:80", "10.1.2.6:80"), |
| localEndpoints: nil, |
| }, { |
| name: "hintsAnnotation disabled, no filtering applied", |
| hintsEnabled: true, |
| nodeLabels: map[string]string{v1.LabelTopologyZone: "zone-a"}, |
| serviceInfo: &BaseServicePortInfo{hintsAnnotation: "disabled"}, |
| endpoints: []Endpoint{ |
| &BaseEndpointInfo{endpoint: "10.1.2.3:80", zoneHints: sets.New[string]("zone-a"), ready: true}, |
| &BaseEndpointInfo{endpoint: "10.1.2.4:80", zoneHints: sets.New[string]("zone-b"), ready: true}, |
| &BaseEndpointInfo{endpoint: "10.1.2.5:80", zoneHints: sets.New[string]("zone-c"), ready: true}, |
| &BaseEndpointInfo{endpoint: "10.1.2.6:80", zoneHints: sets.New[string]("zone-a"), ready: true}, |
| }, |
| clusterEndpoints: sets.New[string]("10.1.2.3:80", "10.1.2.4:80", "10.1.2.5:80", "10.1.2.6:80"), |
| localEndpoints: nil, |
| }, { |
| name: "missing hints, no filtering applied", |
| hintsEnabled: true, |
| nodeLabels: map[string]string{v1.LabelTopologyZone: "zone-a"}, |
| serviceInfo: &BaseServicePortInfo{hintsAnnotation: "auto"}, |
| endpoints: []Endpoint{ |
| &BaseEndpointInfo{endpoint: "10.1.2.3:80", zoneHints: sets.New[string]("zone-a"), ready: true}, |
| &BaseEndpointInfo{endpoint: "10.1.2.4:80", zoneHints: sets.New[string]("zone-b"), ready: true}, |
| &BaseEndpointInfo{endpoint: "10.1.2.5:80", zoneHints: nil, ready: true}, |
| &BaseEndpointInfo{endpoint: "10.1.2.6:80", zoneHints: sets.New[string]("zone-a"), ready: true}, |
| }, |
| clusterEndpoints: sets.New[string]("10.1.2.3:80", "10.1.2.4:80", "10.1.2.5:80", "10.1.2.6:80"), |
| localEndpoints: nil, |
| }, { |
| name: "multiple hints per endpoint, filtering includes any endpoint with zone included", |
| hintsEnabled: true, |
| nodeLabels: map[string]string{v1.LabelTopologyZone: "zone-c"}, |
| serviceInfo: &BaseServicePortInfo{hintsAnnotation: "auto"}, |
| endpoints: []Endpoint{ |
| &BaseEndpointInfo{endpoint: "10.1.2.3:80", zoneHints: sets.New[string]("zone-a", "zone-b", "zone-c"), ready: true}, |
| &BaseEndpointInfo{endpoint: "10.1.2.4:80", zoneHints: sets.New[string]("zone-b", "zone-c"), ready: true}, |
| &BaseEndpointInfo{endpoint: "10.1.2.5:80", zoneHints: sets.New[string]("zone-b", "zone-d"), ready: true}, |
| &BaseEndpointInfo{endpoint: "10.1.2.6:80", zoneHints: sets.New[string]("zone-c"), ready: true}, |
| }, |
| clusterEndpoints: sets.New[string]("10.1.2.3:80", "10.1.2.4:80", "10.1.2.6:80"), |
| localEndpoints: nil, |
| }, { |
| name: "conflicting topology and localness require merging allEndpoints", |
| hintsEnabled: true, |
| nodeLabels: map[string]string{v1.LabelTopologyZone: "zone-a"}, |
| serviceInfo: &BaseServicePortInfo{internalPolicyLocal: false, externalPolicyLocal: true, nodePort: 8080, hintsAnnotation: "auto"}, |
| endpoints: []Endpoint{ |
| &BaseEndpointInfo{endpoint: "10.0.0.0:80", zoneHints: sets.New[string]("zone-a"), ready: true, isLocal: true}, |
| &BaseEndpointInfo{endpoint: "10.0.0.1:80", zoneHints: sets.New[string]("zone-b"), ready: true, isLocal: true}, |
| &BaseEndpointInfo{endpoint: "10.0.0.2:80", zoneHints: sets.New[string]("zone-a"), ready: true, isLocal: false}, |
| &BaseEndpointInfo{endpoint: "10.0.0.3:80", zoneHints: sets.New[string]("zone-b"), ready: true, isLocal: false}, |
| }, |
| clusterEndpoints: sets.New[string]("10.0.0.0:80", "10.0.0.2:80"), |
| localEndpoints: sets.New[string]("10.0.0.0:80", "10.0.0.1:80"), |
| allEndpoints: sets.New[string]("10.0.0.0:80", "10.0.0.1:80", "10.0.0.2:80"), |
| }, { |
| name: "internalTrafficPolicy: Local, with empty endpoints", |
| serviceInfo: &BaseServicePortInfo{internalPolicyLocal: true}, |
| endpoints: []Endpoint{}, |
| clusterEndpoints: nil, |
| localEndpoints: sets.New[string](), |
| }, { |
| name: "internalTrafficPolicy: Local, but all endpoints are remote", |
| serviceInfo: &BaseServicePortInfo{internalPolicyLocal: true}, |
| endpoints: []Endpoint{ |
| &BaseEndpointInfo{endpoint: "10.0.0.0:80", ready: true, isLocal: false}, |
| &BaseEndpointInfo{endpoint: "10.0.0.1:80", ready: true, isLocal: false}, |
| }, |
| clusterEndpoints: nil, |
| localEndpoints: sets.New[string](), |
| onlyRemoteEndpoints: true, |
| }, { |
| name: "internalTrafficPolicy: Local, all endpoints are local", |
| serviceInfo: &BaseServicePortInfo{internalPolicyLocal: true}, |
| endpoints: []Endpoint{ |
| &BaseEndpointInfo{endpoint: "10.0.0.0:80", ready: true, isLocal: true}, |
| &BaseEndpointInfo{endpoint: "10.0.0.1:80", ready: true, isLocal: true}, |
| }, |
| clusterEndpoints: nil, |
| localEndpoints: sets.New[string]("10.0.0.0:80", "10.0.0.1:80"), |
| }, { |
| name: "internalTrafficPolicy: Local, some endpoints are local", |
| serviceInfo: &BaseServicePortInfo{internalPolicyLocal: true}, |
| endpoints: []Endpoint{ |
| &BaseEndpointInfo{endpoint: "10.0.0.0:80", ready: true, isLocal: true}, |
| &BaseEndpointInfo{endpoint: "10.0.0.1:80", ready: true, isLocal: false}, |
| }, |
| clusterEndpoints: nil, |
| localEndpoints: sets.New[string]("10.0.0.0:80"), |
| }, { |
| name: "Cluster traffic policy, endpoints not Ready", |
| serviceInfo: &BaseServicePortInfo{}, |
| endpoints: []Endpoint{ |
| &BaseEndpointInfo{endpoint: "10.0.0.0:80", ready: false}, |
| &BaseEndpointInfo{endpoint: "10.0.0.1:80", ready: false}, |
| }, |
| clusterEndpoints: sets.New[string](), |
| localEndpoints: nil, |
| }, { |
| name: "Cluster traffic policy, some endpoints are Ready", |
| serviceInfo: &BaseServicePortInfo{}, |
| endpoints: []Endpoint{ |
| &BaseEndpointInfo{endpoint: "10.0.0.0:80", ready: false}, |
| &BaseEndpointInfo{endpoint: "10.0.0.1:80", ready: true}, |
| }, |
| clusterEndpoints: sets.New[string]("10.0.0.1:80"), |
| localEndpoints: nil, |
| }, { |
| name: "Cluster traffic policy, all endpoints are terminating", |
| pteEnabled: true, |
| serviceInfo: &BaseServicePortInfo{}, |
| endpoints: []Endpoint{ |
| &BaseEndpointInfo{endpoint: "10.0.0.0:80", ready: false, serving: true, terminating: true, isLocal: true}, |
| &BaseEndpointInfo{endpoint: "10.0.0.1:80", ready: false, serving: true, terminating: true, isLocal: false}, |
| }, |
| clusterEndpoints: sets.New[string]("10.0.0.0:80", "10.0.0.1:80"), |
| localEndpoints: nil, |
| }, { |
| name: "iTP: Local, eTP: Cluster, some endpoints local", |
| serviceInfo: &BaseServicePortInfo{internalPolicyLocal: true, externalPolicyLocal: false, nodePort: 8080}, |
| endpoints: []Endpoint{ |
| &BaseEndpointInfo{endpoint: "10.0.0.0:80", ready: true, isLocal: true}, |
| &BaseEndpointInfo{endpoint: "10.0.0.1:80", ready: true, isLocal: false}, |
| }, |
| clusterEndpoints: sets.New[string]("10.0.0.0:80", "10.0.0.1:80"), |
| localEndpoints: sets.New[string]("10.0.0.0:80"), |
| allEndpoints: sets.New[string]("10.0.0.0:80", "10.0.0.1:80"), |
| }, { |
| name: "iTP: Cluster, eTP: Local, some endpoints local", |
| serviceInfo: &BaseServicePortInfo{internalPolicyLocal: false, externalPolicyLocal: true, nodePort: 8080}, |
| endpoints: []Endpoint{ |
| &BaseEndpointInfo{endpoint: "10.0.0.0:80", ready: true, isLocal: true}, |
| &BaseEndpointInfo{endpoint: "10.0.0.1:80", ready: true, isLocal: false}, |
| }, |
| clusterEndpoints: sets.New[string]("10.0.0.0:80", "10.0.0.1:80"), |
| localEndpoints: sets.New[string]("10.0.0.0:80"), |
| allEndpoints: sets.New[string]("10.0.0.0:80", "10.0.0.1:80"), |
| }, { |
| name: "iTP: Local, eTP: Local, some endpoints local", |
| serviceInfo: &BaseServicePortInfo{internalPolicyLocal: true, externalPolicyLocal: true, nodePort: 8080}, |
| endpoints: []Endpoint{ |
| &BaseEndpointInfo{endpoint: "10.0.0.0:80", ready: true, isLocal: true}, |
| &BaseEndpointInfo{endpoint: "10.0.0.1:80", ready: true, isLocal: false}, |
| }, |
| clusterEndpoints: sets.New[string]("10.0.0.0:80", "10.0.0.1:80"), |
| localEndpoints: sets.New[string]("10.0.0.0:80"), |
| allEndpoints: sets.New[string]("10.0.0.0:80", "10.0.0.1:80"), |
| }, { |
| name: "iTP: Local, eTP: Local, all endpoints remote", |
| serviceInfo: &BaseServicePortInfo{internalPolicyLocal: true, externalPolicyLocal: true, nodePort: 8080}, |
| endpoints: []Endpoint{ |
| &BaseEndpointInfo{endpoint: "10.0.0.0:80", ready: true, isLocal: false}, |
| &BaseEndpointInfo{endpoint: "10.0.0.1:80", ready: true, isLocal: false}, |
| }, |
| clusterEndpoints: sets.New[string]("10.0.0.0:80", "10.0.0.1:80"), |
| localEndpoints: sets.New[string](), |
| allEndpoints: sets.New[string]("10.0.0.0:80", "10.0.0.1:80"), |
| }, { |
| name: "iTP: Local, eTP: Local, all endpoints remote and terminating", |
| pteEnabled: true, |
| serviceInfo: &BaseServicePortInfo{internalPolicyLocal: true, externalPolicyLocal: true, nodePort: 8080}, |
| endpoints: []Endpoint{ |
| &BaseEndpointInfo{endpoint: "10.0.0.0:80", ready: false, serving: true, terminating: true, isLocal: false}, |
| &BaseEndpointInfo{endpoint: "10.0.0.1:80", ready: false, serving: true, terminating: true, isLocal: false}, |
| }, |
| clusterEndpoints: sets.New[string]("10.0.0.0:80", "10.0.0.1:80"), |
| localEndpoints: sets.New[string](), |
| allEndpoints: sets.New[string]("10.0.0.0:80", "10.0.0.1:80"), |
| onlyRemoteEndpoints: true, |
| }, { |
| name: "iTP: Cluster, eTP: Local, with terminating endpoints", |
| pteEnabled: true, |
| serviceInfo: &BaseServicePortInfo{internalPolicyLocal: false, externalPolicyLocal: true, nodePort: 8080}, |
| endpoints: []Endpoint{ |
| &BaseEndpointInfo{endpoint: "10.0.0.0:80", ready: true, isLocal: false}, |
| &BaseEndpointInfo{endpoint: "10.0.0.1:80", ready: false, serving: false, isLocal: true}, |
| &BaseEndpointInfo{endpoint: "10.0.0.2:80", ready: false, serving: true, terminating: true, isLocal: true}, |
| &BaseEndpointInfo{endpoint: "10.0.0.3:80", ready: false, serving: true, terminating: true, isLocal: false}, |
| }, |
| clusterEndpoints: sets.New[string]("10.0.0.0:80"), |
| localEndpoints: sets.New[string]("10.0.0.2:80"), |
| allEndpoints: sets.New[string]("10.0.0.0:80", "10.0.0.2:80"), |
| }, { |
| name: "externalTrafficPolicy ignored if not externally accessible", |
| serviceInfo: &BaseServicePortInfo{externalPolicyLocal: true}, |
| endpoints: []Endpoint{ |
| &BaseEndpointInfo{endpoint: "10.0.0.0:80", ready: true, isLocal: false}, |
| &BaseEndpointInfo{endpoint: "10.0.0.1:80", ready: true, isLocal: true}, |
| }, |
| clusterEndpoints: sets.New[string]("10.0.0.0:80", "10.0.0.1:80"), |
| localEndpoints: nil, |
| allEndpoints: sets.New[string]("10.0.0.0:80", "10.0.0.1:80"), |
| }, { |
| name: "no cluster endpoints for iTP:Local internal-only service", |
| serviceInfo: &BaseServicePortInfo{internalPolicyLocal: true}, |
| endpoints: []Endpoint{ |
| &BaseEndpointInfo{endpoint: "10.0.0.0:80", ready: true, isLocal: false}, |
| &BaseEndpointInfo{endpoint: "10.0.0.1:80", ready: true, isLocal: true}, |
| }, |
| clusterEndpoints: nil, |
| localEndpoints: sets.New[string]("10.0.0.1:80"), |
| allEndpoints: sets.New[string]("10.0.0.1:80"), |
| }} |
| |
| for _, tc := range testCases { |
| t.Run(tc.name, func(t *testing.T) { |
| defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.TopologyAwareHints, tc.hintsEnabled)() |
| defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.ServiceTrafficDistribution, tc.trafficDistFeatureEnabled)() |
| |
| clusterEndpoints, localEndpoints, allEndpoints, hasAnyEndpoints := CategorizeEndpoints(tc.endpoints, tc.serviceInfo, tc.nodeLabels) |
| |
| if tc.clusterEndpoints == nil && clusterEndpoints != nil { |
| t.Errorf("expected no cluster endpoints but got %v", clusterEndpoints) |
| } else { |
| err := checkExpectedEndpoints(tc.clusterEndpoints, clusterEndpoints) |
| if err != nil { |
| t.Errorf("error with cluster endpoints: %v", err) |
| } |
| } |
| |
| if tc.localEndpoints == nil && localEndpoints != nil { |
| t.Errorf("expected no local endpoints but got %v", localEndpoints) |
| } else { |
| err := checkExpectedEndpoints(tc.localEndpoints, localEndpoints) |
| if err != nil { |
| t.Errorf("error with local endpoints: %v", err) |
| } |
| } |
| |
| var expectedAllEndpoints sets.Set[string] |
| if tc.clusterEndpoints != nil && tc.localEndpoints == nil { |
| expectedAllEndpoints = tc.clusterEndpoints |
| } else if tc.localEndpoints != nil && tc.clusterEndpoints == nil { |
| expectedAllEndpoints = tc.localEndpoints |
| } else { |
| expectedAllEndpoints = tc.allEndpoints |
| } |
| err := checkExpectedEndpoints(expectedAllEndpoints, allEndpoints) |
| if err != nil { |
| t.Errorf("error with allEndpoints: %v", err) |
| } |
| |
| expectedHasAnyEndpoints := len(expectedAllEndpoints) > 0 || tc.onlyRemoteEndpoints |
| if expectedHasAnyEndpoints != hasAnyEndpoints { |
| t.Errorf("expected hasAnyEndpoints=%v, got %v", expectedHasAnyEndpoints, hasAnyEndpoints) |
| } |
| }) |
| } |
| } |