diff --git a/pkg/proxy/ipvs/BUILD b/pkg/proxy/ipvs/BUILD index 667eacea905..39b2ce85406 100644 --- a/pkg/proxy/ipvs/BUILD +++ b/pkg/proxy/ipvs/BUILD @@ -9,6 +9,7 @@ load( go_test( name = "go_default_test", srcs = [ + "graceful_termination_test.go", "ipset_test.go", "proxier_test.go", ], diff --git a/pkg/proxy/ipvs/graceful_termination_test.go b/pkg/proxy/ipvs/graceful_termination_test.go new file mode 100644 index 00000000000..c99feef993f --- /dev/null +++ b/pkg/proxy/ipvs/graceful_termination_test.go @@ -0,0 +1,338 @@ +/* +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 ipvs + +import ( + "net" + "reflect" + "testing" + + utilipvs "k8s.io/kubernetes/pkg/util/ipvs" + utilipvstest "k8s.io/kubernetes/pkg/util/ipvs/testing" +) + +func Test_GracefulDeleteRS(t *testing.T) { + tests := []struct { + name string + vs *utilipvs.VirtualServer + rs *utilipvs.RealServer + existingIPVS *utilipvstest.FakeIPVS + expectedIPVS *utilipvstest.FakeIPVS + err error + }{ + { + name: "graceful delete, no connections results in deleting the real server immediatetly", + vs: &utilipvs.VirtualServer{ + Address: net.ParseIP("1.1.1.1"), + Protocol: "tcp", + Port: uint16(80), + }, + rs: &utilipvs.RealServer{ + Address: net.ParseIP("10.0.0.1"), + Port: uint16(80), + Weight: 100, + ActiveConn: 0, + InactiveConn: 0, + }, + existingIPVS: &utilipvstest.FakeIPVS{ + Services: map[utilipvstest.ServiceKey]*utilipvs.VirtualServer{ + { + IP: "1.1.1.1", + Port: 80, + Protocol: "tcp", + }: { + Address: net.ParseIP("1.1.1.1"), + Protocol: "tcp", + Port: uint16(80), + }, + }, + Destinations: map[utilipvstest.ServiceKey][]*utilipvs.RealServer{ + { + IP: "1.1.1.1", + Port: 80, + Protocol: "tcp", + }: { + { + Address: net.ParseIP("10.0.0.1"), + Port: uint16(80), + Weight: 100, + ActiveConn: 0, + InactiveConn: 0, + }, + }, + }, + }, + expectedIPVS: &utilipvstest.FakeIPVS{ + Services: map[utilipvstest.ServiceKey]*utilipvs.VirtualServer{ + { + IP: "1.1.1.1", + Port: 80, + Protocol: "tcp", + }: { + Address: net.ParseIP("1.1.1.1"), + Protocol: "tcp", + Port: uint16(80), + }, + }, + Destinations: map[utilipvstest.ServiceKey][]*utilipvs.RealServer{ + { + IP: "1.1.1.1", + Port: 80, + Protocol: "tcp", + }: {}, + }, + }, + err: nil, + }, + { + name: "graceful delete, real server has active connections, weight should be 0 but don't delete", + vs: &utilipvs.VirtualServer{ + Address: net.ParseIP("1.1.1.1"), + Protocol: "tcp", + Port: uint16(80), + }, + rs: &utilipvs.RealServer{ + Address: net.ParseIP("10.0.0.1"), + Port: uint16(80), + Weight: 100, + ActiveConn: 10, + InactiveConn: 0, + }, + existingIPVS: &utilipvstest.FakeIPVS{ + Services: map[utilipvstest.ServiceKey]*utilipvs.VirtualServer{ + { + IP: "1.1.1.1", + Port: 80, + Protocol: "tcp", + }: { + Address: net.ParseIP("1.1.1.1"), + Protocol: "tcp", + Port: uint16(80), + }, + }, + Destinations: map[utilipvstest.ServiceKey][]*utilipvs.RealServer{ + { + IP: "1.1.1.1", + Port: 80, + Protocol: "tcp", + }: { + { + Address: net.ParseIP("10.0.0.1"), + Port: uint16(80), + Weight: 100, + ActiveConn: 10, + InactiveConn: 0, + }, + }, + }, + }, + expectedIPVS: &utilipvstest.FakeIPVS{ + Services: map[utilipvstest.ServiceKey]*utilipvs.VirtualServer{ + { + IP: "1.1.1.1", + Port: 80, + Protocol: "tcp", + }: { + Address: net.ParseIP("1.1.1.1"), + Protocol: "tcp", + Port: uint16(80), + }, + }, + Destinations: map[utilipvstest.ServiceKey][]*utilipvs.RealServer{ + { + IP: "1.1.1.1", + Port: 80, + Protocol: "tcp", + }: { + { + Address: net.ParseIP("10.0.0.1"), + Port: uint16(80), + Weight: 0, + ActiveConn: 10, + InactiveConn: 0, + }, + }, + }, + }, + err: nil, + }, + { + name: "graceful delete, real server has in-active connections, weight should be 0 but don't delete", + vs: &utilipvs.VirtualServer{ + Address: net.ParseIP("1.1.1.1"), + Protocol: "tcp", + Port: uint16(80), + }, + rs: &utilipvs.RealServer{ + Address: net.ParseIP("10.0.0.1"), + Port: uint16(80), + Weight: 100, + ActiveConn: 0, + InactiveConn: 10, + }, + existingIPVS: &utilipvstest.FakeIPVS{ + Services: map[utilipvstest.ServiceKey]*utilipvs.VirtualServer{ + { + IP: "1.1.1.1", + Port: 80, + Protocol: "tcp", + }: { + Address: net.ParseIP("1.1.1.1"), + Protocol: "tcp", + Port: uint16(80), + }, + }, + Destinations: map[utilipvstest.ServiceKey][]*utilipvs.RealServer{ + { + IP: "1.1.1.1", + Port: 80, + Protocol: "tcp", + }: { + { + Address: net.ParseIP("10.0.0.1"), + Port: uint16(80), + Weight: 100, + ActiveConn: 0, + InactiveConn: 10, + }, + }, + }, + }, + expectedIPVS: &utilipvstest.FakeIPVS{ + Services: map[utilipvstest.ServiceKey]*utilipvs.VirtualServer{ + { + IP: "1.1.1.1", + Port: 80, + Protocol: "tcp", + }: { + Address: net.ParseIP("1.1.1.1"), + Protocol: "tcp", + Port: uint16(80), + }, + }, + Destinations: map[utilipvstest.ServiceKey][]*utilipvs.RealServer{ + { + IP: "1.1.1.1", + Port: 80, + Protocol: "tcp", + }: { + { + Address: net.ParseIP("10.0.0.1"), + Port: uint16(80), + Weight: 0, + ActiveConn: 0, + InactiveConn: 10, + }, + }, + }, + }, + err: nil, + }, + { + name: "graceful delete, real server mismatch should be no-op", + vs: &utilipvs.VirtualServer{ + Address: net.ParseIP("1.1.1.1"), + Protocol: "tcp", + Port: uint16(80), + }, + rs: &utilipvs.RealServer{ + Address: net.ParseIP("10.0.0.1"), + Port: uint16(81), // port mismatched + Weight: 100, + ActiveConn: 0, + InactiveConn: 10, + }, + existingIPVS: &utilipvstest.FakeIPVS{ + Services: map[utilipvstest.ServiceKey]*utilipvs.VirtualServer{ + { + IP: "1.1.1.1", + Port: 80, + Protocol: "tcp", + }: { + Address: net.ParseIP("1.1.1.1"), + Protocol: "tcp", + Port: uint16(80), + }, + }, + Destinations: map[utilipvstest.ServiceKey][]*utilipvs.RealServer{ + { + IP: "1.1.1.1", + Port: 80, + Protocol: "tcp", + }: { + { + Address: net.ParseIP("10.0.0.1"), + Port: uint16(80), + Weight: 100, + ActiveConn: 0, + InactiveConn: 10, + }, + }, + }, + }, + expectedIPVS: &utilipvstest.FakeIPVS{ + Services: map[utilipvstest.ServiceKey]*utilipvs.VirtualServer{ + { + IP: "1.1.1.1", + Port: 80, + Protocol: "tcp", + }: { + Address: net.ParseIP("1.1.1.1"), + Protocol: "tcp", + Port: uint16(80), + }, + }, + Destinations: map[utilipvstest.ServiceKey][]*utilipvs.RealServer{ + { + IP: "1.1.1.1", + Port: 80, + Protocol: "tcp", + }: { + { + Address: net.ParseIP("10.0.0.1"), + Port: uint16(80), + Weight: 100, + ActiveConn: 0, + InactiveConn: 10, + }, + }, + }, + }, + err: nil, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + ipvs := test.existingIPVS + gracefulTerminationManager := NewGracefulTerminationManager(ipvs) + + err := gracefulTerminationManager.GracefulDeleteRS(test.vs, test.rs) + if err != test.err { + t.Logf("actual err: %v", err) + t.Logf("expected err: %v", test.err) + t.Errorf("unexpected error") + } + + if !reflect.DeepEqual(ipvs, test.expectedIPVS) { + t.Logf("actual: %+v", ipvs) + t.Logf("expected : %+v", test.expectedIPVS) + t.Errorf("unexpected IPVS servers") + } + }) + } +} diff --git a/pkg/util/ipvs/testing/fake.go b/pkg/util/ipvs/testing/fake.go index bfc854bb365..14d16e893d7 100644 --- a/pkg/util/ipvs/testing/fake.go +++ b/pkg/util/ipvs/testing/fake.go @@ -27,47 +27,49 @@ import ( //FakeIPVS no-op implementation of ipvs Interface type FakeIPVS struct { Scheduler string - Services map[serviceKey]*utilipvs.VirtualServer - Destinations map[serviceKey][]*utilipvs.RealServer + Services map[ServiceKey]*utilipvs.VirtualServer + Destinations map[ServiceKey][]*utilipvs.RealServer } -type serviceKey struct { +// ServiceKey uniquely identifies a Service for an IPVS virtual server +type ServiceKey struct { IP string Port uint16 Protocol string } -func (s *serviceKey) String() string { +func (s *ServiceKey) String() string { return fmt.Sprintf("%s:%d/%s", s.IP, s.Port, s.Protocol) } -type realServerKey struct { +// RealServerKey uniquely identifies an Endpoint for an IPVS real server +type RealServerKey struct { Address net.IP Port uint16 } -func (r *realServerKey) String() string { +func (r *RealServerKey) String() string { return net.JoinHostPort(r.Address.String(), strconv.Itoa(int(r.Port))) } //NewFake creates a fake ipvs implementation - a cache store. func NewFake() *FakeIPVS { return &FakeIPVS{ - Services: make(map[serviceKey]*utilipvs.VirtualServer), - Destinations: make(map[serviceKey][]*utilipvs.RealServer), + Services: make(map[ServiceKey]*utilipvs.VirtualServer), + Destinations: make(map[ServiceKey][]*utilipvs.RealServer), } } -func toServiceKey(serv *utilipvs.VirtualServer) serviceKey { - return serviceKey{ +func toServiceKey(serv *utilipvs.VirtualServer) ServiceKey { + return ServiceKey{ IP: serv.Address.String(), Port: serv.Port, Protocol: serv.Protocol, } } -func toRealServerKey(rs *utilipvs.RealServer) *realServerKey { - return &realServerKey{ +func toRealServerKey(rs *utilipvs.RealServer) *RealServerKey { + return &RealServerKey{ Address: rs.Address, Port: rs.Port, }