mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-29 22:46:12 +00:00
Merge pull request #3738 from smreed/randomize-roundrobin-endpoints
Randomize roundrobin endpoints
This commit is contained in:
commit
c292aa54c4
@ -25,6 +25,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/util/slice"
|
||||
"github.com/golang/glog"
|
||||
)
|
||||
|
||||
@ -210,14 +211,14 @@ func (lb *LoadBalancerRR) OnUpdate(endpoints []api.Endpoints) {
|
||||
for _, endpoint := range endpoints {
|
||||
existingEndpoints, exists := lb.endpointsMap[endpoint.Name]
|
||||
validEndpoints := filterValidEndpoints(endpoint.Endpoints)
|
||||
if !exists || !reflect.DeepEqual(existingEndpoints, validEndpoints) {
|
||||
if !exists || !reflect.DeepEqual(slice.SortStrings(slice.CopyStrings(existingEndpoints)), slice.SortStrings(validEndpoints)) {
|
||||
glog.V(3).Infof("LoadBalancerRR: Setting endpoints for %s to %+v", endpoint.Name, endpoint.Endpoints)
|
||||
updateServiceDetailMap(lb, endpoint.Name, validEndpoints)
|
||||
// On update can be called without NewService being called externally.
|
||||
// to be safe we will call it here. A new service will only be created
|
||||
// if one does not already exist.
|
||||
lb.NewService(endpoint.Name, api.AffinityTypeNone, 0)
|
||||
lb.endpointsMap[endpoint.Name] = validEndpoints
|
||||
lb.endpointsMap[endpoint.Name] = slice.ShuffleStrings(validEndpoints)
|
||||
|
||||
// Reset the round-robin index.
|
||||
lb.rrIndex[endpoint.Name] = 0
|
||||
|
@ -75,7 +75,7 @@ func expectEndpoint(t *testing.T, loadBalancer *LoadBalancerRR, service string,
|
||||
t.Errorf("Didn't find a service for %s, expected %s, failed with: %v", service, expected, err)
|
||||
}
|
||||
if endpoint != expected {
|
||||
t.Errorf("Didn't get expected endpoint for service %s, expected %s, got: %s", service, expected, endpoint)
|
||||
t.Errorf("Didn't get expected endpoint for service %s client %v, expected %s, got: %s", service, netaddr, expected, endpoint)
|
||||
}
|
||||
}
|
||||
|
||||
@ -109,10 +109,11 @@ func TestLoadBalanceWorksWithMultipleEndpoints(t *testing.T) {
|
||||
Endpoints: []string{"endpoint:1", "endpoint:2", "endpoint:3"},
|
||||
}
|
||||
loadBalancer.OnUpdate(endpoints)
|
||||
expectEndpoint(t, loadBalancer, "foo", "endpoint:1", nil)
|
||||
expectEndpoint(t, loadBalancer, "foo", "endpoint:2", nil)
|
||||
expectEndpoint(t, loadBalancer, "foo", "endpoint:3", nil)
|
||||
expectEndpoint(t, loadBalancer, "foo", "endpoint:1", nil)
|
||||
shuffledEndpoints := loadBalancer.endpointsMap["foo"]
|
||||
expectEndpoint(t, loadBalancer, "foo", shuffledEndpoints[0], nil)
|
||||
expectEndpoint(t, loadBalancer, "foo", shuffledEndpoints[1], nil)
|
||||
expectEndpoint(t, loadBalancer, "foo", shuffledEndpoints[2], nil)
|
||||
expectEndpoint(t, loadBalancer, "foo", shuffledEndpoints[0], nil)
|
||||
}
|
||||
|
||||
func TestLoadBalanceWorksWithMultipleEndpointsAndUpdates(t *testing.T) {
|
||||
@ -127,21 +128,23 @@ func TestLoadBalanceWorksWithMultipleEndpointsAndUpdates(t *testing.T) {
|
||||
Endpoints: []string{"endpoint:1", "endpoint:2", "endpoint:3"},
|
||||
}
|
||||
loadBalancer.OnUpdate(endpoints)
|
||||
expectEndpoint(t, loadBalancer, "foo", "endpoint:1", nil)
|
||||
expectEndpoint(t, loadBalancer, "foo", "endpoint:2", nil)
|
||||
expectEndpoint(t, loadBalancer, "foo", "endpoint:3", nil)
|
||||
expectEndpoint(t, loadBalancer, "foo", "endpoint:1", nil)
|
||||
expectEndpoint(t, loadBalancer, "foo", "endpoint:2", nil)
|
||||
shuffledEndpoints := loadBalancer.endpointsMap["foo"]
|
||||
expectEndpoint(t, loadBalancer, "foo", shuffledEndpoints[0], nil)
|
||||
expectEndpoint(t, loadBalancer, "foo", shuffledEndpoints[1], nil)
|
||||
expectEndpoint(t, loadBalancer, "foo", shuffledEndpoints[2], nil)
|
||||
expectEndpoint(t, loadBalancer, "foo", shuffledEndpoints[0], nil)
|
||||
expectEndpoint(t, loadBalancer, "foo", shuffledEndpoints[1], nil)
|
||||
// Then update the configuration with one fewer endpoints, make sure
|
||||
// we start in the beginning again
|
||||
endpoints[0] = api.Endpoints{ObjectMeta: api.ObjectMeta{Name: "foo"},
|
||||
Endpoints: []string{"endpoint:8", "endpoint:9"},
|
||||
}
|
||||
loadBalancer.OnUpdate(endpoints)
|
||||
expectEndpoint(t, loadBalancer, "foo", "endpoint:8", nil)
|
||||
expectEndpoint(t, loadBalancer, "foo", "endpoint:9", nil)
|
||||
expectEndpoint(t, loadBalancer, "foo", "endpoint:8", nil)
|
||||
expectEndpoint(t, loadBalancer, "foo", "endpoint:9", nil)
|
||||
shuffledEndpoints = loadBalancer.endpointsMap["foo"]
|
||||
expectEndpoint(t, loadBalancer, "foo", shuffledEndpoints[0], nil)
|
||||
expectEndpoint(t, loadBalancer, "foo", shuffledEndpoints[1], nil)
|
||||
expectEndpoint(t, loadBalancer, "foo", shuffledEndpoints[0], nil)
|
||||
expectEndpoint(t, loadBalancer, "foo", shuffledEndpoints[1], nil)
|
||||
// Clear endpoints
|
||||
endpoints[0] = api.Endpoints{ObjectMeta: api.ObjectMeta{Name: "foo"}, Endpoints: []string{}}
|
||||
loadBalancer.OnUpdate(endpoints)
|
||||
@ -168,17 +171,19 @@ func TestLoadBalanceWorksWithServiceRemoval(t *testing.T) {
|
||||
Endpoints: []string{"endpoint:4", "endpoint:5"},
|
||||
}
|
||||
loadBalancer.OnUpdate(endpoints)
|
||||
expectEndpoint(t, loadBalancer, "foo", "endpoint:1", nil)
|
||||
expectEndpoint(t, loadBalancer, "foo", "endpoint:2", nil)
|
||||
expectEndpoint(t, loadBalancer, "foo", "endpoint:3", nil)
|
||||
expectEndpoint(t, loadBalancer, "foo", "endpoint:1", nil)
|
||||
expectEndpoint(t, loadBalancer, "foo", "endpoint:2", nil)
|
||||
shuffledFooEndpoints := loadBalancer.endpointsMap["foo"]
|
||||
expectEndpoint(t, loadBalancer, "foo", shuffledFooEndpoints[0], nil)
|
||||
expectEndpoint(t, loadBalancer, "foo", shuffledFooEndpoints[1], nil)
|
||||
expectEndpoint(t, loadBalancer, "foo", shuffledFooEndpoints[2], nil)
|
||||
expectEndpoint(t, loadBalancer, "foo", shuffledFooEndpoints[0], nil)
|
||||
expectEndpoint(t, loadBalancer, "foo", shuffledFooEndpoints[1], nil)
|
||||
|
||||
expectEndpoint(t, loadBalancer, "bar", "endpoint:4", nil)
|
||||
expectEndpoint(t, loadBalancer, "bar", "endpoint:5", nil)
|
||||
expectEndpoint(t, loadBalancer, "bar", "endpoint:4", nil)
|
||||
expectEndpoint(t, loadBalancer, "bar", "endpoint:5", nil)
|
||||
expectEndpoint(t, loadBalancer, "bar", "endpoint:4", nil)
|
||||
shuffledBarEndpoints := loadBalancer.endpointsMap["bar"]
|
||||
expectEndpoint(t, loadBalancer, "bar", shuffledBarEndpoints[0], nil)
|
||||
expectEndpoint(t, loadBalancer, "bar", shuffledBarEndpoints[1], nil)
|
||||
expectEndpoint(t, loadBalancer, "bar", shuffledBarEndpoints[0], nil)
|
||||
expectEndpoint(t, loadBalancer, "bar", shuffledBarEndpoints[1], nil)
|
||||
expectEndpoint(t, loadBalancer, "bar", shuffledBarEndpoints[0], nil)
|
||||
|
||||
// Then update the configuration by removing foo
|
||||
loadBalancer.OnUpdate(endpoints[1:])
|
||||
@ -188,10 +193,10 @@ func TestLoadBalanceWorksWithServiceRemoval(t *testing.T) {
|
||||
}
|
||||
|
||||
// but bar is still there, and we continue RR from where we left off.
|
||||
expectEndpoint(t, loadBalancer, "bar", "endpoint:5", nil)
|
||||
expectEndpoint(t, loadBalancer, "bar", "endpoint:4", nil)
|
||||
expectEndpoint(t, loadBalancer, "bar", "endpoint:5", nil)
|
||||
expectEndpoint(t, loadBalancer, "bar", "endpoint:4", nil)
|
||||
expectEndpoint(t, loadBalancer, "bar", shuffledBarEndpoints[1], nil)
|
||||
expectEndpoint(t, loadBalancer, "bar", shuffledBarEndpoints[0], nil)
|
||||
expectEndpoint(t, loadBalancer, "bar", shuffledBarEndpoints[1], nil)
|
||||
expectEndpoint(t, loadBalancer, "bar", shuffledBarEndpoints[0], nil)
|
||||
}
|
||||
|
||||
func TestStickyLoadBalanceWorksWithSingleEndpoint(t *testing.T) {
|
||||
@ -232,14 +237,15 @@ func TestStickyLoadBalanaceWorksWithMultipleEndpoints(t *testing.T) {
|
||||
Endpoints: []string{"endpoint:1", "endpoint:2", "endpoint:3"},
|
||||
}
|
||||
loadBalancer.OnUpdate(endpoints)
|
||||
expectEndpoint(t, loadBalancer, "foo", "endpoint:1", client1)
|
||||
expectEndpoint(t, loadBalancer, "foo", "endpoint:1", client1)
|
||||
expectEndpoint(t, loadBalancer, "foo", "endpoint:2", client2)
|
||||
expectEndpoint(t, loadBalancer, "foo", "endpoint:2", client2)
|
||||
expectEndpoint(t, loadBalancer, "foo", "endpoint:3", client3)
|
||||
expectEndpoint(t, loadBalancer, "foo", "endpoint:3", client3)
|
||||
expectEndpoint(t, loadBalancer, "foo", "endpoint:1", client1)
|
||||
expectEndpoint(t, loadBalancer, "foo", "endpoint:1", client1)
|
||||
shuffledEndpoints := loadBalancer.endpointsMap["foo"]
|
||||
expectEndpoint(t, loadBalancer, "foo", shuffledEndpoints[0], client1)
|
||||
expectEndpoint(t, loadBalancer, "foo", shuffledEndpoints[0], client1)
|
||||
expectEndpoint(t, loadBalancer, "foo", shuffledEndpoints[1], client2)
|
||||
expectEndpoint(t, loadBalancer, "foo", shuffledEndpoints[1], client2)
|
||||
expectEndpoint(t, loadBalancer, "foo", shuffledEndpoints[2], client3)
|
||||
expectEndpoint(t, loadBalancer, "foo", shuffledEndpoints[2], client3)
|
||||
expectEndpoint(t, loadBalancer, "foo", shuffledEndpoints[0], client1)
|
||||
expectEndpoint(t, loadBalancer, "foo", shuffledEndpoints[0], client1)
|
||||
}
|
||||
|
||||
func TestStickyLoadBalanaceWorksWithMultipleEndpointsStickyNone(t *testing.T) {
|
||||
@ -259,14 +265,15 @@ func TestStickyLoadBalanaceWorksWithMultipleEndpointsStickyNone(t *testing.T) {
|
||||
Endpoints: []string{"endpoint:1", "endpoint:2", "endpoint:3"},
|
||||
}
|
||||
loadBalancer.OnUpdate(endpoints)
|
||||
expectEndpoint(t, loadBalancer, "foo", "endpoint:1", client1)
|
||||
expectEndpoint(t, loadBalancer, "foo", "endpoint:2", client1)
|
||||
expectEndpoint(t, loadBalancer, "foo", "endpoint:3", client2)
|
||||
expectEndpoint(t, loadBalancer, "foo", "endpoint:1", client2)
|
||||
expectEndpoint(t, loadBalancer, "foo", "endpoint:2", client3)
|
||||
expectEndpoint(t, loadBalancer, "foo", "endpoint:3", client3)
|
||||
expectEndpoint(t, loadBalancer, "foo", "endpoint:1", client1)
|
||||
expectEndpoint(t, loadBalancer, "foo", "endpoint:2", client1)
|
||||
shuffledEndpoints := loadBalancer.endpointsMap["foo"]
|
||||
expectEndpoint(t, loadBalancer, "foo", shuffledEndpoints[0], client1)
|
||||
expectEndpoint(t, loadBalancer, "foo", shuffledEndpoints[1], client1)
|
||||
expectEndpoint(t, loadBalancer, "foo", shuffledEndpoints[2], client2)
|
||||
expectEndpoint(t, loadBalancer, "foo", shuffledEndpoints[0], client2)
|
||||
expectEndpoint(t, loadBalancer, "foo", shuffledEndpoints[1], client3)
|
||||
expectEndpoint(t, loadBalancer, "foo", shuffledEndpoints[2], client3)
|
||||
expectEndpoint(t, loadBalancer, "foo", shuffledEndpoints[0], client1)
|
||||
expectEndpoint(t, loadBalancer, "foo", shuffledEndpoints[1], client1)
|
||||
}
|
||||
|
||||
func TestStickyLoadBalanaceWorksWithMultipleEndpointsRemoveOne(t *testing.T) {
|
||||
@ -289,32 +296,45 @@ func TestStickyLoadBalanaceWorksWithMultipleEndpointsRemoveOne(t *testing.T) {
|
||||
Endpoints: []string{"endpoint:1", "endpoint:2", "endpoint:3"},
|
||||
}
|
||||
loadBalancer.OnUpdate(endpoints)
|
||||
expectEndpoint(t, loadBalancer, "foo", "endpoint:1", client1)
|
||||
expectEndpoint(t, loadBalancer, "foo", "endpoint:1", client1)
|
||||
expectEndpoint(t, loadBalancer, "foo", "endpoint:2", client2)
|
||||
expectEndpoint(t, loadBalancer, "foo", "endpoint:2", client2)
|
||||
expectEndpoint(t, loadBalancer, "foo", "endpoint:3", client3)
|
||||
shuffledEndpoints := loadBalancer.endpointsMap["foo"]
|
||||
expectEndpoint(t, loadBalancer, "foo", shuffledEndpoints[0], client1)
|
||||
client1Endpoint := shuffledEndpoints[0]
|
||||
expectEndpoint(t, loadBalancer, "foo", shuffledEndpoints[0], client1)
|
||||
expectEndpoint(t, loadBalancer, "foo", shuffledEndpoints[1], client2)
|
||||
client2Endpoint := shuffledEndpoints[1]
|
||||
expectEndpoint(t, loadBalancer, "foo", shuffledEndpoints[1], client2)
|
||||
expectEndpoint(t, loadBalancer, "foo", shuffledEndpoints[2], client3)
|
||||
client3Endpoint := shuffledEndpoints[2]
|
||||
|
||||
endpoints[0] = api.Endpoints{
|
||||
ObjectMeta: api.ObjectMeta{Name: "foo"},
|
||||
Endpoints: []string{"endpoint:1", "endpoint:2"},
|
||||
}
|
||||
loadBalancer.OnUpdate(endpoints)
|
||||
expectEndpoint(t, loadBalancer, "foo", "endpoint:1", client1)
|
||||
expectEndpoint(t, loadBalancer, "foo", "endpoint:2", client2)
|
||||
expectEndpoint(t, loadBalancer, "foo", "endpoint:1", client3)
|
||||
shuffledEndpoints = loadBalancer.endpointsMap["foo"]
|
||||
if client1Endpoint == "endpoint:3" {
|
||||
client1Endpoint = shuffledEndpoints[0]
|
||||
} else if client2Endpoint == "endpoint:3" {
|
||||
client2Endpoint = shuffledEndpoints[0]
|
||||
} else if client3Endpoint == "endpoint:3" {
|
||||
client3Endpoint = shuffledEndpoints[0]
|
||||
}
|
||||
expectEndpoint(t, loadBalancer, "foo", client1Endpoint, client1)
|
||||
expectEndpoint(t, loadBalancer, "foo", client2Endpoint, client2)
|
||||
expectEndpoint(t, loadBalancer, "foo", client3Endpoint, client3)
|
||||
|
||||
endpoints[0] = api.Endpoints{
|
||||
ObjectMeta: api.ObjectMeta{Name: "foo"},
|
||||
Endpoints: []string{"endpoint:1", "endpoint:2", "endpoint:4"},
|
||||
}
|
||||
loadBalancer.OnUpdate(endpoints)
|
||||
expectEndpoint(t, loadBalancer, "foo", "endpoint:1", client1)
|
||||
expectEndpoint(t, loadBalancer, "foo", "endpoint:2", client2)
|
||||
expectEndpoint(t, loadBalancer, "foo", "endpoint:1", client3)
|
||||
expectEndpoint(t, loadBalancer, "foo", "endpoint:1", client4)
|
||||
expectEndpoint(t, loadBalancer, "foo", "endpoint:2", client5)
|
||||
expectEndpoint(t, loadBalancer, "foo", "endpoint:4", client6)
|
||||
shuffledEndpoints = loadBalancer.endpointsMap["foo"]
|
||||
expectEndpoint(t, loadBalancer, "foo", client1Endpoint, client1)
|
||||
expectEndpoint(t, loadBalancer, "foo", client2Endpoint, client2)
|
||||
expectEndpoint(t, loadBalancer, "foo", client3Endpoint, client3)
|
||||
expectEndpoint(t, loadBalancer, "foo", shuffledEndpoints[0], client4)
|
||||
expectEndpoint(t, loadBalancer, "foo", shuffledEndpoints[1], client5)
|
||||
expectEndpoint(t, loadBalancer, "foo", shuffledEndpoints[2], client6)
|
||||
}
|
||||
|
||||
func TestStickyLoadBalanceWorksWithMultipleEndpointsAndUpdates(t *testing.T) {
|
||||
@ -334,24 +354,26 @@ func TestStickyLoadBalanceWorksWithMultipleEndpointsAndUpdates(t *testing.T) {
|
||||
Endpoints: []string{"endpoint:1", "endpoint:2", "endpoint:3"},
|
||||
}
|
||||
loadBalancer.OnUpdate(endpoints)
|
||||
expectEndpoint(t, loadBalancer, "foo", "endpoint:1", client1)
|
||||
expectEndpoint(t, loadBalancer, "foo", "endpoint:1", client1)
|
||||
expectEndpoint(t, loadBalancer, "foo", "endpoint:2", client2)
|
||||
expectEndpoint(t, loadBalancer, "foo", "endpoint:2", client2)
|
||||
expectEndpoint(t, loadBalancer, "foo", "endpoint:3", client3)
|
||||
expectEndpoint(t, loadBalancer, "foo", "endpoint:1", client1)
|
||||
expectEndpoint(t, loadBalancer, "foo", "endpoint:2", client2)
|
||||
shuffledEndpoints := loadBalancer.endpointsMap["foo"]
|
||||
expectEndpoint(t, loadBalancer, "foo", shuffledEndpoints[0], client1)
|
||||
expectEndpoint(t, loadBalancer, "foo", shuffledEndpoints[0], client1)
|
||||
expectEndpoint(t, loadBalancer, "foo", shuffledEndpoints[1], client2)
|
||||
expectEndpoint(t, loadBalancer, "foo", shuffledEndpoints[1], client2)
|
||||
expectEndpoint(t, loadBalancer, "foo", shuffledEndpoints[2], client3)
|
||||
expectEndpoint(t, loadBalancer, "foo", shuffledEndpoints[0], client1)
|
||||
expectEndpoint(t, loadBalancer, "foo", shuffledEndpoints[1], client2)
|
||||
// Then update the configuration with one fewer endpoints, make sure
|
||||
// we start in the beginning again
|
||||
endpoints[0] = api.Endpoints{ObjectMeta: api.ObjectMeta{Name: "foo"},
|
||||
Endpoints: []string{"endpoint:4", "endpoint:5"},
|
||||
}
|
||||
loadBalancer.OnUpdate(endpoints)
|
||||
expectEndpoint(t, loadBalancer, "foo", "endpoint:4", client1)
|
||||
expectEndpoint(t, loadBalancer, "foo", "endpoint:5", client2)
|
||||
expectEndpoint(t, loadBalancer, "foo", "endpoint:4", client1)
|
||||
expectEndpoint(t, loadBalancer, "foo", "endpoint:5", client2)
|
||||
expectEndpoint(t, loadBalancer, "foo", "endpoint:5", client2)
|
||||
shuffledEndpoints = loadBalancer.endpointsMap["foo"]
|
||||
expectEndpoint(t, loadBalancer, "foo", shuffledEndpoints[0], client1)
|
||||
expectEndpoint(t, loadBalancer, "foo", shuffledEndpoints[1], client2)
|
||||
expectEndpoint(t, loadBalancer, "foo", shuffledEndpoints[0], client1)
|
||||
expectEndpoint(t, loadBalancer, "foo", shuffledEndpoints[1], client2)
|
||||
expectEndpoint(t, loadBalancer, "foo", shuffledEndpoints[1], client2)
|
||||
|
||||
// Clear endpoints
|
||||
endpoints[0] = api.Endpoints{ObjectMeta: api.ObjectMeta{Name: "foo"}, Endpoints: []string{}}
|
||||
@ -384,19 +406,21 @@ func TestStickyLoadBalanceWorksWithServiceRemoval(t *testing.T) {
|
||||
Endpoints: []string{"endpoint:4", "endpoint:5"},
|
||||
}
|
||||
loadBalancer.OnUpdate(endpoints)
|
||||
expectEndpoint(t, loadBalancer, "foo", "endpoint:1", client1)
|
||||
expectEndpoint(t, loadBalancer, "foo", "endpoint:2", client2)
|
||||
expectEndpoint(t, loadBalancer, "foo", "endpoint:3", client3)
|
||||
expectEndpoint(t, loadBalancer, "foo", "endpoint:3", client3)
|
||||
expectEndpoint(t, loadBalancer, "foo", "endpoint:1", client1)
|
||||
expectEndpoint(t, loadBalancer, "foo", "endpoint:2", client2)
|
||||
shuffledFooEndpoints := loadBalancer.endpointsMap["foo"]
|
||||
expectEndpoint(t, loadBalancer, "foo", shuffledFooEndpoints[0], client1)
|
||||
expectEndpoint(t, loadBalancer, "foo", shuffledFooEndpoints[1], client2)
|
||||
expectEndpoint(t, loadBalancer, "foo", shuffledFooEndpoints[2], client3)
|
||||
expectEndpoint(t, loadBalancer, "foo", shuffledFooEndpoints[2], client3)
|
||||
expectEndpoint(t, loadBalancer, "foo", shuffledFooEndpoints[0], client1)
|
||||
expectEndpoint(t, loadBalancer, "foo", shuffledFooEndpoints[1], client2)
|
||||
|
||||
expectEndpoint(t, loadBalancer, "bar", "endpoint:4", client1)
|
||||
expectEndpoint(t, loadBalancer, "bar", "endpoint:5", client2)
|
||||
expectEndpoint(t, loadBalancer, "bar", "endpoint:4", client1)
|
||||
expectEndpoint(t, loadBalancer, "bar", "endpoint:5", client2)
|
||||
expectEndpoint(t, loadBalancer, "bar", "endpoint:4", client1)
|
||||
expectEndpoint(t, loadBalancer, "bar", "endpoint:4", client1)
|
||||
shuffledBarEndpoints := loadBalancer.endpointsMap["bar"]
|
||||
expectEndpoint(t, loadBalancer, "foo", shuffledFooEndpoints[0], client1)
|
||||
expectEndpoint(t, loadBalancer, "foo", shuffledFooEndpoints[1], client2)
|
||||
expectEndpoint(t, loadBalancer, "foo", shuffledFooEndpoints[0], client1)
|
||||
expectEndpoint(t, loadBalancer, "foo", shuffledFooEndpoints[1], client2)
|
||||
expectEndpoint(t, loadBalancer, "foo", shuffledFooEndpoints[0], client1)
|
||||
expectEndpoint(t, loadBalancer, "foo", shuffledFooEndpoints[0], client1)
|
||||
|
||||
// Then update the configuration by removing foo
|
||||
loadBalancer.OnUpdate(endpoints[1:])
|
||||
@ -406,10 +430,11 @@ func TestStickyLoadBalanceWorksWithServiceRemoval(t *testing.T) {
|
||||
}
|
||||
|
||||
// but bar is still there, and we continue RR from where we left off.
|
||||
expectEndpoint(t, loadBalancer, "bar", "endpoint:4", client1)
|
||||
expectEndpoint(t, loadBalancer, "bar", "endpoint:5", client2)
|
||||
expectEndpoint(t, loadBalancer, "bar", "endpoint:4", client1)
|
||||
expectEndpoint(t, loadBalancer, "bar", "endpoint:5", client2)
|
||||
expectEndpoint(t, loadBalancer, "bar", "endpoint:4", client1)
|
||||
expectEndpoint(t, loadBalancer, "bar", "endpoint:4", client1)
|
||||
shuffledBarEndpoints = loadBalancer.endpointsMap["bar"]
|
||||
expectEndpoint(t, loadBalancer, "bar", shuffledBarEndpoints[0], client1)
|
||||
expectEndpoint(t, loadBalancer, "bar", shuffledBarEndpoints[1], client2)
|
||||
expectEndpoint(t, loadBalancer, "bar", shuffledBarEndpoints[0], client1)
|
||||
expectEndpoint(t, loadBalancer, "bar", shuffledBarEndpoints[1], client2)
|
||||
expectEndpoint(t, loadBalancer, "bar", shuffledBarEndpoints[0], client1)
|
||||
expectEndpoint(t, loadBalancer, "bar", shuffledBarEndpoints[0], client1)
|
||||
}
|
||||
|
49
pkg/util/slice/slice.go
Normal file
49
pkg/util/slice/slice.go
Normal file
@ -0,0 +1,49 @@
|
||||
/*
|
||||
Copyright 2015 Google Inc. All rights reserved.
|
||||
|
||||
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 slice provides utility methods for common operations on slices.
|
||||
package slice
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
"sort"
|
||||
)
|
||||
|
||||
// CopyStrings copies the contents of the specified string slice
|
||||
// into a new slice.
|
||||
func CopyStrings(s []string) []string {
|
||||
c := make([]string, len(s))
|
||||
copy(c, s)
|
||||
return c
|
||||
}
|
||||
|
||||
// SortStrings sorts the specified string slice in place. It returns the same
|
||||
// slice that was provided in order to facilitate method chaining.
|
||||
func SortStrings(s []string) []string {
|
||||
sort.Strings(s)
|
||||
return s
|
||||
}
|
||||
|
||||
// ShuffleStrings copies strings from the specified slice into a copy in random
|
||||
// order. It returns a new slice.
|
||||
func ShuffleStrings(s []string) []string {
|
||||
shuffled := make([]string, len(s))
|
||||
perm := rand.Perm(len(s))
|
||||
for i, j := range perm {
|
||||
shuffled[j] = s[i]
|
||||
}
|
||||
return shuffled
|
||||
}
|
70
pkg/util/slice/slice_test.go
Normal file
70
pkg/util/slice/slice_test.go
Normal file
@ -0,0 +1,70 @@
|
||||
/*
|
||||
Copyright 2015 Google Inc. All rights reserved.
|
||||
|
||||
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 slice
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestCopyStrings(t *testing.T) {
|
||||
src := []string{"a", "c", "b"}
|
||||
dest := CopyStrings(src)
|
||||
|
||||
if !reflect.DeepEqual(src, dest) {
|
||||
t.Errorf("%v and %v are not equal", src, dest)
|
||||
}
|
||||
|
||||
src[0] = "A"
|
||||
if reflect.DeepEqual(src, dest) {
|
||||
t.Errorf("CopyStrings didn't make a copy")
|
||||
}
|
||||
}
|
||||
|
||||
func TestSortStrings(t *testing.T) {
|
||||
src := []string{"a", "c", "b"}
|
||||
dest := SortStrings(src)
|
||||
expected := []string{"a", "b", "c"}
|
||||
|
||||
if !reflect.DeepEqual(dest, expected) {
|
||||
t.Errorf("SortString didn't sort the strings")
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(src, expected) {
|
||||
t.Errorf("SortString didn't sort in place")
|
||||
}
|
||||
}
|
||||
|
||||
func TestShuffleStrings(t *testing.T) {
|
||||
src := []string{"a", "b", "c", "d", "e", "f"}
|
||||
dest := ShuffleStrings(src)
|
||||
|
||||
if len(src) != len(dest) {
|
||||
t.Errorf("Shuffled slice is wrong length, expected %v got %v", len(src), len(dest))
|
||||
}
|
||||
|
||||
m := make(map[string]bool, len(dest))
|
||||
for _, s := range dest {
|
||||
m[s] = true
|
||||
}
|
||||
|
||||
for _, k := range src {
|
||||
if _, exists := m[k]; !exists {
|
||||
t.Errorf("Element %v missing from shuffled slice", k)
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user