k3s/pkg/proxy/iptables/proxier_test.go

2258 lines
75 KiB
Go
Raw Normal View History

2015-08-11 02:47:13 +00:00
/*
Copyright 2015 The Kubernetes Authors.
2015-08-11 02:47:13 +00:00
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 iptables
import (
"bytes"
2017-12-10 07:12:23 +00:00
"fmt"
"net"
"reflect"
"strconv"
2017-12-10 07:12:23 +00:00
"strings"
2015-08-11 02:47:13 +00:00
"testing"
"time"
"github.com/golang/glog"
2018-08-15 13:51:19 +00:00
"k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2017-01-11 14:09:48 +00:00
"k8s.io/apimachinery/pkg/types"
2017-01-27 20:42:17 +00:00
"k8s.io/apimachinery/pkg/util/intstr"
2016-04-13 00:45:39 +00:00
"k8s.io/kubernetes/pkg/proxy"
2017-08-22 02:49:24 +00:00
utilproxy "k8s.io/kubernetes/pkg/proxy/util"
2018-01-17 02:30:19 +00:00
utilproxytest "k8s.io/kubernetes/pkg/proxy/util/testing"
"k8s.io/kubernetes/pkg/util/async"
"k8s.io/kubernetes/pkg/util/conntrack"
utiliptables "k8s.io/kubernetes/pkg/util/iptables"
2016-09-27 02:48:21 +00:00
iptablestest "k8s.io/kubernetes/pkg/util/iptables/testing"
2017-07-19 05:58:53 +00:00
"k8s.io/utils/exec"
fakeexec "k8s.io/utils/exec/testing"
2015-08-11 02:47:13 +00:00
)
func checkAllLines(t *testing.T, table utiliptables.Table, save []byte, expectedLines map[utiliptables.Chain]string) {
2016-05-11 20:25:14 +00:00
chainLines := utiliptables.GetChainLines(table, save)
for chain, lineBytes := range chainLines {
line := string(lineBytes)
2015-08-11 02:47:13 +00:00
if expected, exists := expectedLines[chain]; exists {
if expected != line {
t.Errorf("getChainLines expected chain line not present. For chain: %s Expected: %s Got: %s", chain, expected, line)
}
} else {
t.Errorf("getChainLines expected chain not present: %s", chain)
}
}
}
func TestGetChainLines(t *testing.T) {
iptablesSave := `# Generated by iptables-save v1.4.7 on Wed Oct 29 14:56:01 2014
2015-08-11 02:47:13 +00:00
*nat
:PREROUTING ACCEPT [2136997:197881818]
:POSTROUTING ACCEPT [4284525:258542680]
:OUTPUT ACCEPT [5901660:357267963]
-A PREROUTING -m addrtype --dst-type LOCAL -j DOCKER
COMMIT
# Completed on Wed Oct 29 14:56:01 2014`
expected := map[utiliptables.Chain]string{
utiliptables.ChainPrerouting: ":PREROUTING ACCEPT [2136997:197881818]",
utiliptables.ChainPostrouting: ":POSTROUTING ACCEPT [4284525:258542680]",
utiliptables.ChainOutput: ":OUTPUT ACCEPT [5901660:357267963]",
}
checkAllLines(t, utiliptables.TableNAT, []byte(iptablesSave), expected)
2015-08-11 02:47:13 +00:00
}
func TestGetChainLinesMultipleTables(t *testing.T) {
iptablesSave := `# Generated by iptables-save v1.4.21 on Fri Aug 7 14:47:37 2015
2015-08-11 02:47:13 +00:00
*nat
:PREROUTING ACCEPT [2:138]
:INPUT ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
:POSTROUTING ACCEPT [0:0]
:DOCKER - [0:0]
:KUBE-NODEPORT-CONTAINER - [0:0]
:KUBE-NODEPORT-HOST - [0:0]
:KUBE-PORTALS-CONTAINER - [0:0]
:KUBE-PORTALS-HOST - [0:0]
:KUBE-SVC-1111111111111111 - [0:0]
:KUBE-SVC-2222222222222222 - [0:0]
:KUBE-SVC-3333333333333333 - [0:0]
:KUBE-SVC-4444444444444444 - [0:0]
:KUBE-SVC-5555555555555555 - [0:0]
:KUBE-SVC-6666666666666666 - [0:0]
2015-08-11 02:47:13 +00:00
-A PREROUTING -m comment --comment "handle ClusterIPs; NOTE: this must be before the NodePort rules" -j KUBE-PORTALS-CONTAINER
-A PREROUTING -m addrtype --dst-type LOCAL -j DOCKER
-A PREROUTING -m addrtype --dst-type LOCAL -m comment --comment "handle service NodePorts; NOTE: this must be the last rule in the chain" -j KUBE-NODEPORT-CONTAINER
-A OUTPUT -m comment --comment "handle ClusterIPs; NOTE: this must be before the NodePort rules" -j KUBE-PORTALS-HOST
-A OUTPUT ! -d 127.0.0.0/8 -m addrtype --dst-type LOCAL -j DOCKER
-A OUTPUT -m addrtype --dst-type LOCAL -m comment --comment "handle service NodePorts; NOTE: this must be the last rule in the chain" -j KUBE-NODEPORT-HOST
-A POSTROUTING -s 10.246.1.0/24 ! -o cbr0 -j MASQUERADE
-A POSTROUTING -s 10.0.2.15/32 -d 10.0.2.15/32 -m comment --comment "handle pod connecting to self" -j MASQUERADE
-A KUBE-PORTALS-CONTAINER -d 10.247.0.1/32 -p tcp -m comment --comment "portal for default/kubernetes:" -m state --state NEW -m tcp --dport 443 -j KUBE-SVC-5555555555555555
-A KUBE-PORTALS-CONTAINER -d 10.247.0.10/32 -p udp -m comment --comment "portal for kube-system/kube-dns:dns" -m state --state NEW -m udp --dport 53 -j KUBE-SVC-6666666666666666
-A KUBE-PORTALS-CONTAINER -d 10.247.0.10/32 -p tcp -m comment --comment "portal for kube-system/kube-dns:dns-tcp" -m state --state NEW -m tcp --dport 53 -j KUBE-SVC-2222222222222222
-A KUBE-PORTALS-HOST -d 10.247.0.1/32 -p tcp -m comment --comment "portal for default/kubernetes:" -m state --state NEW -m tcp --dport 443 -j KUBE-SVC-5555555555555555
-A KUBE-PORTALS-HOST -d 10.247.0.10/32 -p udp -m comment --comment "portal for kube-system/kube-dns:dns" -m state --state NEW -m udp --dport 53 -j KUBE-SVC-6666666666666666
-A KUBE-PORTALS-HOST -d 10.247.0.10/32 -p tcp -m comment --comment "portal for kube-system/kube-dns:dns-tcp" -m state --state NEW -m tcp --dport 53 -j KUBE-SVC-2222222222222222
-A KUBE-SVC-1111111111111111 -p udp -m comment --comment "kube-system/kube-dns:dns" -m recent --set --name KUBE-SVC-1111111111111111 --mask 255.255.255.255 --rsource -j DNAT --to-destination 10.246.1.2:53
-A KUBE-SVC-2222222222222222 -m comment --comment "kube-system/kube-dns:dns-tcp" -j KUBE-SVC-3333333333333333
-A KUBE-SVC-3333333333333333 -p tcp -m comment --comment "kube-system/kube-dns:dns-tcp" -m recent --set --name KUBE-SVC-3333333333333333 --mask 255.255.255.255 --rsource -j DNAT --to-destination 10.246.1.2:53
-A KUBE-SVC-4444444444444444 -p tcp -m comment --comment "default/kubernetes:" -m recent --set --name KUBE-SVC-4444444444444444 --mask 255.255.255.255 --rsource -j DNAT --to-destination 10.245.1.2:443
-A KUBE-SVC-5555555555555555 -m comment --comment "default/kubernetes:" -j KUBE-SVC-4444444444444444
-A KUBE-SVC-6666666666666666 -m comment --comment "kube-system/kube-dns:dns" -j KUBE-SVC-1111111111111111
2015-08-11 02:47:13 +00:00
COMMIT
# Completed on Fri Aug 7 14:47:37 2015
# Generated by iptables-save v1.4.21 on Fri Aug 7 14:47:37 2015
*filter
:INPUT ACCEPT [17514:83115836]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [8909:688225]
:DOCKER - [0:0]
-A FORWARD -o cbr0 -j DOCKER
-A FORWARD -o cbr0 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
-A FORWARD -i cbr0 ! -o cbr0 -j ACCEPT
-A FORWARD -i cbr0 -o cbr0 -j ACCEPT
COMMIT
`
expected := map[utiliptables.Chain]string{
utiliptables.ChainPrerouting: ":PREROUTING ACCEPT [2:138]",
utiliptables.Chain("INPUT"): ":INPUT ACCEPT [0:0]",
utiliptables.Chain("OUTPUT"): ":OUTPUT ACCEPT [0:0]",
utiliptables.ChainPostrouting: ":POSTROUTING ACCEPT [0:0]",
utiliptables.Chain("DOCKER"): ":DOCKER - [0:0]",
utiliptables.Chain("KUBE-NODEPORT-CONTAINER"): ":KUBE-NODEPORT-CONTAINER - [0:0]",
utiliptables.Chain("KUBE-NODEPORT-HOST"): ":KUBE-NODEPORT-HOST - [0:0]",
utiliptables.Chain("KUBE-PORTALS-CONTAINER"): ":KUBE-PORTALS-CONTAINER - [0:0]",
utiliptables.Chain("KUBE-PORTALS-HOST"): ":KUBE-PORTALS-HOST - [0:0]",
utiliptables.Chain("KUBE-SVC-1111111111111111"): ":KUBE-SVC-1111111111111111 - [0:0]",
utiliptables.Chain("KUBE-SVC-2222222222222222"): ":KUBE-SVC-2222222222222222 - [0:0]",
utiliptables.Chain("KUBE-SVC-3333333333333333"): ":KUBE-SVC-3333333333333333 - [0:0]",
utiliptables.Chain("KUBE-SVC-4444444444444444"): ":KUBE-SVC-4444444444444444 - [0:0]",
utiliptables.Chain("KUBE-SVC-5555555555555555"): ":KUBE-SVC-5555555555555555 - [0:0]",
utiliptables.Chain("KUBE-SVC-6666666666666666"): ":KUBE-SVC-6666666666666666 - [0:0]",
2015-08-11 02:47:13 +00:00
}
checkAllLines(t, utiliptables.TableNAT, []byte(iptablesSave), expected)
2015-08-11 02:47:13 +00:00
}
2018-08-15 13:51:19 +00:00
func newFakeServiceInfo(service proxy.ServicePortName, ip net.IP, port int, protocol v1.Protocol, onlyNodeLocalEndpoints bool) *serviceInfo {
2016-04-13 00:45:39 +00:00
return &serviceInfo{
BaseServiceInfo: &proxy.BaseServiceInfo{
2018-08-15 13:51:19 +00:00
SessionAffinityType: v1.ServiceAffinityNone, // default
StickyMaxAgeSeconds: int(v1.DefaultClientIPServiceAffinitySeconds), // default
ClusterIP: ip,
Port: port,
Protocol: protocol,
OnlyNodeLocalEndpoints: onlyNodeLocalEndpoints,
},
2016-04-13 00:45:39 +00:00
}
}
func TestDeleteEndpointConnections(t *testing.T) {
const (
UDP = v1.ProtocolUDP
TCP = v1.ProtocolTCP
SCTP = v1.ProtocolSCTP
)
testCases := []struct {
description string
svcName string
svcIP string
svcPort int
2018-08-15 13:51:19 +00:00
protocol v1.Protocol
2017-12-10 07:12:23 +00:00
endpoint string // IP:port endpoint
epSvcPair proxy.ServiceEndpoint // Will be generated by test
simulatedErr string
}{
{
description: "V4 UDP",
svcName: "v4-udp",
svcIP: "10.96.1.1",
svcPort: 80,
protocol: UDP,
endpoint: "10.240.0.3:80",
}, {
description: "V4 TCP",
svcName: "v4-tcp",
svcIP: "10.96.2.2",
svcPort: 80,
protocol: TCP,
endpoint: "10.240.0.4:80",
},{
description: "V4 SCTP",
svcName: "v4-sctp",
svcIP: "10.96.3.3",
svcPort: 80,
protocol: SCTP,
endpoint: "10.240.0.5:80",
},{
description: "V4 UDP, nothing to delete, benign error",
svcName: "v4-udp-nothing-to-delete",
svcIP: "10.96.1.1",
svcPort: 80,
protocol: UDP,
endpoint: "10.240.0.3:80",
simulatedErr: conntrack.NoConnectionToDelete,
}, {
description: "V4 UDP, unexpected error, should be glogged",
svcName: "v4-udp-simulated-error",
svcIP: "10.96.1.1",
svcPort: 80,
protocol: UDP,
endpoint: "10.240.0.3:80",
simulatedErr: "simulated error",
}, {
description: "V6 UDP",
svcName: "v6-udp",
svcIP: "fd00:1234::20",
svcPort: 80,
protocol: UDP,
endpoint: "[2001:db8::2]:80",
}, {
description: "V6 TCP",
svcName: "v6-tcp",
svcIP: "fd00:1234::30",
svcPort: 80,
protocol: TCP,
endpoint: "[2001:db8::3]:80",
}, {
description: "V6 SCTP",
svcName: "v6-sctp",
svcIP: "fd00:1234::40",
svcPort: 80,
protocol: SCTP,
endpoint: "[2001:db8::4]:80",
2016-04-13 00:45:39 +00:00
},
}
// Create a service map that has service info entries for all test cases
// and generate an endpoint service pair for each test case
2017-12-10 07:12:23 +00:00
serviceMap := make(map[proxy.ServicePortName]proxy.ServicePort)
for i, tc := range testCases {
svc := proxy.ServicePortName{
NamespacedName: types.NamespacedName{Namespace: "ns1", Name: tc.svcName},
Port: "p80",
}
serviceMap[svc] = newFakeServiceInfo(svc, net.ParseIP(tc.svcIP), 80, tc.protocol, false)
2017-12-10 07:12:23 +00:00
testCases[i].epSvcPair = proxy.ServiceEndpoint{
Endpoint: tc.endpoint,
ServicePortName: svc,
}
}
// Create a fake executor for the conntrack utility. This should only be
// invoked for UDP connections, since no conntrack cleanup is needed for TCP
fcmd := fakeexec.FakeCmd{}
2017-07-19 05:58:53 +00:00
fexec := fakeexec.FakeExec{
2016-04-13 00:45:39 +00:00
LookPathFunc: func(cmd string) (string, error) { return cmd, nil },
}
execFunc := func(cmd string, args ...string) exec.Cmd {
return fakeexec.InitFakeCmd(&fcmd, cmd, args...)
}
for _, tc := range testCases {
if tc.protocol == UDP {
var cmdOutput string
var simErr error
if tc.simulatedErr == "" {
cmdOutput = "1 flow entries have been deleted"
} else {
simErr = fmt.Errorf(tc.simulatedErr)
}
cmdFunc := func() ([]byte, error) { return []byte(cmdOutput), simErr }
fcmd.CombinedOutputScript = append(fcmd.CombinedOutputScript, cmdFunc)
fexec.CommandScript = append(fexec.CommandScript, execFunc)
}
}
2016-04-13 00:45:39 +00:00
// Create a proxier using the fake conntrack executor and service map
2016-04-13 00:45:39 +00:00
fakeProxier := Proxier{exec: &fexec, serviceMap: serviceMap}
// Run the test cases
for _, tc := range testCases {
priorExecs := fexec.CommandCalls
priorGlogErrs := glog.Stats.Error.Lines()
2016-04-13 00:45:39 +00:00
2017-12-10 07:12:23 +00:00
input := []proxy.ServiceEndpoint{tc.epSvcPair}
2016-04-13 00:45:39 +00:00
fakeProxier.deleteEndpointConnections(input)
// For UDP connections, check the executed conntrack command
var expExecs int
if tc.protocol == UDP {
isIPv6 := func(ip string) bool {
netIP := net.ParseIP(ip)
if netIP.To4() == nil {
return true
}
return false
}
endpointIP := utilproxy.IPPart(tc.endpoint)
expectCommand := fmt.Sprintf("conntrack -D --orig-dst %s --dst-nat %s -p udp", tc.svcIP, endpointIP)
if isIPv6(endpointIP) {
expectCommand += " -f ipv6"
}
actualCommand := strings.Join(fcmd.CombinedOutputLog[fexec.CommandCalls-1], " ")
if actualCommand != expectCommand {
t.Errorf("%s: Expected command: %s, but executed %s", tc.description, expectCommand, actualCommand)
2016-04-13 00:45:39 +00:00
}
expExecs = 1
2016-04-13 00:45:39 +00:00
}
// Check the number of times conntrack was executed
execs := fexec.CommandCalls - priorExecs
if execs != expExecs {
t.Errorf("%s: Expected conntrack to be executed %d times, but got %d", tc.description, expExecs, execs)
}
// Check the number of new glog errors
var expGlogErrs int64
if tc.simulatedErr != "" && tc.simulatedErr != conntrack.NoConnectionToDelete {
expGlogErrs = 1
}
glogErrs := glog.Stats.Error.Lines() - priorGlogErrs
if glogErrs != expGlogErrs {
t.Errorf("%s: Expected %d glogged errors, but got %d", tc.description, expGlogErrs, glogErrs)
2016-04-13 00:45:39 +00:00
}
}
}
2016-04-22 23:13:06 +00:00
type fakeClosable struct {
closed bool
}
func (c *fakeClosable) Close() error {
c.closed = true
return nil
}
2016-09-27 02:48:21 +00:00
// fakePortOpener implements portOpener.
type fakePortOpener struct {
2017-08-22 02:49:24 +00:00
openPorts []*utilproxy.LocalPort
2016-09-27 02:48:21 +00:00
}
// OpenLocalPort fakes out the listen() and bind() used by syncProxyRules
// to lock a local port.
2017-08-22 02:49:24 +00:00
func (f *fakePortOpener) OpenLocalPort(lp *utilproxy.LocalPort) (utilproxy.Closeable, error) {
2016-09-27 02:48:21 +00:00
f.openPorts = append(f.openPorts, lp)
return nil, nil
}
type fakeHealthChecker struct {
services map[types.NamespacedName]uint16
endpoints map[types.NamespacedName]int
}
2017-02-04 02:45:52 +00:00
func newFakeHealthChecker() *fakeHealthChecker {
return &fakeHealthChecker{
services: map[types.NamespacedName]uint16{},
endpoints: map[types.NamespacedName]int{},
}
}
func (fake *fakeHealthChecker) SyncServices(newServices map[types.NamespacedName]uint16) error {
fake.services = newServices
return nil
}
func (fake *fakeHealthChecker) SyncEndpoints(newEndpoints map[types.NamespacedName]int) error {
fake.endpoints = newEndpoints
return nil
}
2017-02-04 02:45:52 +00:00
const testHostname = "test-hostname"
2016-09-27 02:48:21 +00:00
func NewFakeProxier(ipt utiliptables.Interface) *Proxier {
// TODO: Call NewProxier after refactoring out the goroutine
// invocation into a Run() method.
p := &Proxier{
2017-07-19 05:58:53 +00:00
exec: &fakeexec.FakeExec{},
2017-12-10 07:12:23 +00:00
serviceMap: make(proxy.ServiceMap),
serviceChanges: proxy.NewServiceChangeTracker(newServiceInfo, nil, nil),
2017-12-10 07:12:23 +00:00
endpointsMap: make(proxy.EndpointsMap),
endpointsChanges: proxy.NewEndpointChangeTracker(testHostname, newEndpointInfo, nil, nil),
iptables: ipt,
clusterCIDR: "10.0.0.0/24",
hostname: testHostname,
2017-08-22 02:49:24 +00:00
portsMap: make(map[utilproxy.LocalPort]utilproxy.Closeable),
portMapper: &fakePortOpener{[]*utilproxy.LocalPort{}},
healthChecker: newFakeHealthChecker(),
precomputedProbabilities: make([]string, 0, 1001),
iptablesData: bytes.NewBuffer(nil),
filterChains: bytes.NewBuffer(nil),
filterRules: bytes.NewBuffer(nil),
natChains: bytes.NewBuffer(nil),
natRules: bytes.NewBuffer(nil),
2018-01-17 02:30:19 +00:00
nodePortAddresses: make([]string, 0),
networkInterfacer: utilproxytest.NewFakeNetwork(),
2016-09-27 02:48:21 +00:00
}
p.syncRunner = async.NewBoundedFrequencyRunner("test-sync-runner", p.syncProxyRules, 0, time.Minute, 1)
return p
2016-09-27 02:48:21 +00:00
}
func hasSessionAffinityRule(rules []iptablestest.Rule) bool {
for _, r := range rules {
if _, ok := r[iptablestest.Recent]; ok {
return true
}
}
return false
}
func hasJump(rules []iptablestest.Rule, destChain, destIP string, destPort int) bool {
destPortStr := strconv.Itoa(destPort)
2016-11-02 11:25:07 +00:00
match := false
2016-09-27 02:48:21 +00:00
for _, r := range rules {
if r[iptablestest.Jump] == destChain {
2016-11-02 11:25:07 +00:00
match = true
2016-09-27 02:48:21 +00:00
if destIP != "" {
if strings.Contains(r[iptablestest.Destination], destIP) && (strings.Contains(r[iptablestest.DPort], destPortStr) || r[iptablestest.DPort] == "") {
2016-11-02 11:25:07 +00:00
return true
}
match = false
2016-09-27 02:48:21 +00:00
}
if destPort != 0 {
if strings.Contains(r[iptablestest.DPort], destPortStr) && (strings.Contains(r[iptablestest.Destination], destIP) || r[iptablestest.Destination] == "") {
2016-11-02 11:25:07 +00:00
return true
}
match = false
2016-09-27 02:48:21 +00:00
}
}
}
2016-11-02 11:25:07 +00:00
return match
}
func TestHasJump(t *testing.T) {
testCases := map[string]struct {
rules []iptablestest.Rule
destChain string
destIP string
destPort int
2016-11-02 11:25:07 +00:00
expected bool
}{
"case 1": {
// Match the 1st rule(both dest IP and dest Port)
rules: []iptablestest.Rule{
{"-d ": "10.20.30.41/32", "--dport ": "80", "-p ": "tcp", "-j ": "REJECT"},
{"--dport ": "3001", "-p ": "tcp", "-j ": "KUBE-MARK-MASQ"},
},
destChain: "REJECT",
destIP: "10.20.30.41",
destPort: 80,
2016-11-02 11:25:07 +00:00
expected: true,
},
"case 2": {
// Match the 2nd rule(dest Port)
rules: []iptablestest.Rule{
{"-d ": "10.20.30.41/32", "-p ": "tcp", "-j ": "REJECT"},
{"--dport ": "3001", "-p ": "tcp", "-j ": "REJECT"},
},
destChain: "REJECT",
destIP: "",
destPort: 3001,
2016-11-02 11:25:07 +00:00
expected: true,
},
"case 3": {
// Match both dest IP and dest Port
rules: []iptablestest.Rule{
{"-d ": "1.2.3.4/32", "--dport ": "80", "-p ": "tcp", "-j ": "KUBE-XLB-GF53O3C2HZEXL2XN"},
},
destChain: "KUBE-XLB-GF53O3C2HZEXL2XN",
destIP: "1.2.3.4",
destPort: 80,
2016-11-02 11:25:07 +00:00
expected: true,
},
"case 4": {
// Match dest IP but doesn't match dest Port
rules: []iptablestest.Rule{
{"-d ": "1.2.3.4/32", "--dport ": "80", "-p ": "tcp", "-j ": "KUBE-XLB-GF53O3C2HZEXL2XN"},
},
destChain: "KUBE-XLB-GF53O3C2HZEXL2XN",
destIP: "1.2.3.4",
destPort: 8080,
2016-11-02 11:25:07 +00:00
expected: false,
},
"case 5": {
// Match dest Port but doesn't match dest IP
rules: []iptablestest.Rule{
{"-d ": "1.2.3.4/32", "--dport ": "80", "-p ": "tcp", "-j ": "KUBE-XLB-GF53O3C2HZEXL2XN"},
},
destChain: "KUBE-XLB-GF53O3C2HZEXL2XN",
destIP: "10.20.30.40",
destPort: 80,
2016-11-02 11:25:07 +00:00
expected: false,
},
"case 6": {
// Match the 2nd rule(dest IP)
rules: []iptablestest.Rule{
{"-d ": "10.20.30.41/32", "-p ": "tcp", "-j ": "REJECT"},
{"-d ": "1.2.3.4/32", "-p ": "tcp", "-j ": "REJECT"},
{"--dport ": "3001", "-p ": "tcp", "-j ": "REJECT"},
},
destChain: "REJECT",
destIP: "1.2.3.4",
destPort: 8080,
2016-11-02 11:25:07 +00:00
expected: true,
},
"case 7": {
// Match the 2nd rule(dest Port)
rules: []iptablestest.Rule{
{"-d ": "10.20.30.41/32", "-p ": "tcp", "-j ": "REJECT"},
{"--dport ": "3001", "-p ": "tcp", "-j ": "REJECT"},
},
destChain: "REJECT",
destIP: "1.2.3.4",
destPort: 3001,
2016-11-02 11:25:07 +00:00
expected: true,
},
"case 8": {
// Match the 1st rule(dest IP)
rules: []iptablestest.Rule{
{"-d ": "10.20.30.41/32", "-p ": "tcp", "-j ": "REJECT"},
{"--dport ": "3001", "-p ": "tcp", "-j ": "REJECT"},
},
destChain: "REJECT",
destIP: "10.20.30.41",
destPort: 8080,
2016-11-02 11:25:07 +00:00
expected: true,
},
"case 9": {
rules: []iptablestest.Rule{
{"-j ": "KUBE-SEP-LWSOSDSHMKPJHHJV"},
},
destChain: "KUBE-SEP-LWSOSDSHMKPJHHJV",
destIP: "",
destPort: 0,
2016-11-02 11:25:07 +00:00
expected: true,
},
"case 10": {
rules: []iptablestest.Rule{
{"-j ": "KUBE-SEP-FOO"},
},
destChain: "KUBE-SEP-BAR",
destIP: "",
destPort: 0,
2016-11-02 11:25:07 +00:00
expected: false,
},
}
for k, tc := range testCases {
if got := hasJump(tc.rules, tc.destChain, tc.destIP, tc.destPort); got != tc.expected {
t.Errorf("%v: expected %v, got %v", k, tc.expected, got)
}
}
2016-09-27 02:48:21 +00:00
}
func hasDNAT(rules []iptablestest.Rule, endpoint string) bool {
for _, r := range rules {
if r[iptablestest.ToDest] == endpoint {
return true
}
}
return false
}
func errorf(msg string, rules []iptablestest.Rule, t *testing.T) {
for _, r := range rules {
t.Logf("%q", r)
2016-09-27 02:48:21 +00:00
}
t.Errorf("%v", msg)
}
func TestClusterIPReject(t *testing.T) {
ipt := iptablestest.NewFake()
fp := NewFakeProxier(ipt)
svcIP := "10.20.30.41"
svcPort := 80
svcPortName := proxy.ServicePortName{
NamespacedName: makeNSN("ns1", "svc1"),
Port: "p80",
}
makeServiceMap(fp,
2018-08-15 13:51:19 +00:00
makeTestService(svcPortName.Namespace, svcPortName.Name, func(svc *v1.Service) {
svc.Spec.ClusterIP = svcIP
2018-08-15 13:51:19 +00:00
svc.Spec.Ports = []v1.ServicePort{{
Name: svcPortName.Port,
Port: int32(svcPort),
2018-08-15 13:51:19 +00:00
Protocol: v1.ProtocolTCP,
}}
}),
)
makeEndpointsMap(fp)
fp.syncProxyRules()
2016-09-27 02:48:21 +00:00
2018-08-15 13:51:19 +00:00
svcChain := string(servicePortChainName(svcPortName.String(), strings.ToLower(string(v1.ProtocolTCP))))
2016-09-27 02:48:21 +00:00
svcRules := ipt.GetRules(svcChain)
if len(svcRules) != 0 {
errorf(fmt.Sprintf("Unexpected rule for chain %v service %v without endpoints", svcChain, svcPortName), svcRules, t)
2016-09-27 02:48:21 +00:00
}
kubeSvcRules := ipt.GetRules(string(kubeServicesChain))
if !hasJump(kubeSvcRules, iptablestest.Reject, svcIP, svcPort) {
errorf(fmt.Sprintf("Failed to find a %v rule for service %v with no endpoints", iptablestest.Reject, svcPortName), kubeSvcRules, t)
2016-09-27 02:48:21 +00:00
}
}
func TestClusterIPEndpointsJump(t *testing.T) {
ipt := iptablestest.NewFake()
fp := NewFakeProxier(ipt)
svcIP := "10.20.30.41"
svcPort := 80
svcPortName := proxy.ServicePortName{
NamespacedName: makeNSN("ns1", "svc1"),
Port: "p80",
}
makeServiceMap(fp,
2018-08-15 13:51:19 +00:00
makeTestService(svcPortName.Namespace, svcPortName.Name, func(svc *v1.Service) {
svc.Spec.ClusterIP = svcIP
2018-08-15 13:51:19 +00:00
svc.Spec.Ports = []v1.ServicePort{{
Name: svcPortName.Port,
Port: int32(svcPort),
2018-08-15 13:51:19 +00:00
Protocol: v1.ProtocolTCP,
}}
}),
)
epIP := "10.180.0.1"
makeEndpointsMap(fp,
2018-08-15 13:51:19 +00:00
makeTestEndpoints(svcPortName.Namespace, svcPortName.Name, func(ept *v1.Endpoints) {
ept.Subsets = []v1.EndpointSubset{{
Addresses: []v1.EndpointAddress{{
IP: epIP,
}},
2018-08-15 13:51:19 +00:00
Ports: []v1.EndpointPort{{
Name: svcPortName.Port,
Port: int32(svcPort),
}},
}}
}),
2017-04-07 14:26:26 +00:00
)
fp.syncProxyRules()
2016-09-27 02:48:21 +00:00
epStr := fmt.Sprintf("%s:%d", epIP, svcPort)
2018-08-15 13:51:19 +00:00
svcChain := string(servicePortChainName(svcPortName.String(), strings.ToLower(string(v1.ProtocolTCP))))
epChain := string(servicePortEndpointChainName(svcPortName.String(), strings.ToLower(string(v1.ProtocolTCP)), epStr))
2016-09-27 02:48:21 +00:00
kubeSvcRules := ipt.GetRules(string(kubeServicesChain))
if !hasJump(kubeSvcRules, svcChain, svcIP, svcPort) {
2016-09-27 02:48:21 +00:00
errorf(fmt.Sprintf("Failed to find jump from KUBE-SERVICES to %v chain", svcChain), kubeSvcRules, t)
}
svcRules := ipt.GetRules(svcChain)
if !hasJump(svcRules, epChain, "", 0) {
2016-09-27 02:48:21 +00:00
errorf(fmt.Sprintf("Failed to jump to ep chain %v", epChain), svcRules, t)
}
epRules := ipt.GetRules(epChain)
if !hasDNAT(epRules, epStr) {
errorf(fmt.Sprintf("Endpoint chain %v lacks DNAT to %v", epChain, epStr), epRules, t)
2016-09-27 02:48:21 +00:00
}
}
func TestLoadBalancer(t *testing.T) {
ipt := iptablestest.NewFake()
fp := NewFakeProxier(ipt)
svcIP := "10.20.30.41"
svcPort := 80
svcNodePort := 3001
svcLBIP := "1.2.3.4"
svcPortName := proxy.ServicePortName{
NamespacedName: makeNSN("ns1", "svc1"),
Port: "p80",
}
makeServiceMap(fp,
2018-08-15 13:51:19 +00:00
makeTestService(svcPortName.Namespace, svcPortName.Name, func(svc *v1.Service) {
svc.Spec.Type = "LoadBalancer"
svc.Spec.ClusterIP = svcIP
2018-08-15 13:51:19 +00:00
svc.Spec.Ports = []v1.ServicePort{{
Name: svcPortName.Port,
Port: int32(svcPort),
2018-08-15 13:51:19 +00:00
Protocol: v1.ProtocolTCP,
NodePort: int32(svcNodePort),
}}
2018-08-15 13:51:19 +00:00
svc.Status.LoadBalancer.Ingress = []v1.LoadBalancerIngress{{
IP: svcLBIP,
}}
}),
)
2016-09-27 02:48:21 +00:00
epIP := "10.180.0.1"
makeEndpointsMap(fp,
2018-08-15 13:51:19 +00:00
makeTestEndpoints(svcPortName.Namespace, svcPortName.Name, func(ept *v1.Endpoints) {
ept.Subsets = []v1.EndpointSubset{{
Addresses: []v1.EndpointAddress{{
IP: epIP,
}},
2018-08-15 13:51:19 +00:00
Ports: []v1.EndpointPort{{
Name: svcPortName.Port,
Port: int32(svcPort),
}},
}}
}),
2017-04-07 14:26:26 +00:00
)
2016-09-27 02:48:21 +00:00
fp.syncProxyRules()
2016-09-27 02:48:21 +00:00
2018-08-15 13:51:19 +00:00
proto := strings.ToLower(string(v1.ProtocolTCP))
fwChain := string(serviceFirewallChainName(svcPortName.String(), proto))
svcChain := string(servicePortChainName(svcPortName.String(), proto))
2016-09-27 02:48:21 +00:00
kubeSvcRules := ipt.GetRules(string(kubeServicesChain))
if !hasJump(kubeSvcRules, fwChain, svcLBIP, svcPort) {
2016-09-27 02:48:21 +00:00
errorf(fmt.Sprintf("Failed to find jump to firewall chain %v", fwChain), kubeSvcRules, t)
}
fwRules := ipt.GetRules(fwChain)
if !hasJump(fwRules, svcChain, "", 0) || !hasJump(fwRules, string(KubeMarkMasqChain), "", 0) {
2016-09-27 02:48:21 +00:00
errorf(fmt.Sprintf("Failed to find jump from firewall chain %v to svc chain %v", fwChain, svcChain), fwRules, t)
}
}
func TestNodePort(t *testing.T) {
ipt := iptablestest.NewFake()
fp := NewFakeProxier(ipt)
svcIP := "10.20.30.41"
svcPort := 80
svcNodePort := 3001
svcPortName := proxy.ServicePortName{
NamespacedName: makeNSN("ns1", "svc1"),
Port: "p80",
}
makeServiceMap(fp,
2018-08-15 13:51:19 +00:00
makeTestService(svcPortName.Namespace, svcPortName.Name, func(svc *v1.Service) {
svc.Spec.Type = "NodePort"
svc.Spec.ClusterIP = svcIP
2018-08-15 13:51:19 +00:00
svc.Spec.Ports = []v1.ServicePort{{
Name: svcPortName.Port,
Port: int32(svcPort),
2018-08-15 13:51:19 +00:00
Protocol: v1.ProtocolTCP,
NodePort: int32(svcNodePort),
}}
}),
)
2016-09-27 02:48:21 +00:00
epIP := "10.180.0.1"
makeEndpointsMap(fp,
2018-08-15 13:51:19 +00:00
makeTestEndpoints(svcPortName.Namespace, svcPortName.Name, func(ept *v1.Endpoints) {
ept.Subsets = []v1.EndpointSubset{{
Addresses: []v1.EndpointAddress{{
IP: epIP,
}},
2018-08-15 13:51:19 +00:00
Ports: []v1.EndpointPort{{
Name: svcPortName.Port,
Port: int32(svcPort),
}},
}}
}),
2017-04-07 14:26:26 +00:00
)
2016-09-27 02:48:21 +00:00
2018-01-17 02:30:19 +00:00
itf := net.Interface{Index: 0, MTU: 0, Name: "lo", HardwareAddr: nil, Flags: 0}
addrs := []net.Addr{utilproxytest.AddrStruct{Val: "127.0.0.1/16"}}
itf1 := net.Interface{Index: 1, MTU: 0, Name: "eth1", HardwareAddr: nil, Flags: 0}
addrs1 := []net.Addr{utilproxytest.AddrStruct{Val: "::1/128"}}
fp.networkInterfacer.(*utilproxytest.FakeNetwork).AddInterfaceAddr(&itf, addrs)
fp.networkInterfacer.(*utilproxytest.FakeNetwork).AddInterfaceAddr(&itf1, addrs1)
fp.nodePortAddresses = []string{}
fp.syncProxyRules()
2016-09-27 02:48:21 +00:00
2018-08-15 13:51:19 +00:00
proto := strings.ToLower(string(v1.ProtocolTCP))
svcChain := string(servicePortChainName(svcPortName.String(), proto))
2016-09-27 02:48:21 +00:00
kubeNodePortRules := ipt.GetRules(string(kubeNodePortsChain))
if !hasJump(kubeNodePortRules, svcChain, "", svcNodePort) {
2016-09-27 02:48:21 +00:00
errorf(fmt.Sprintf("Failed to find jump to svc chain %v", svcChain), kubeNodePortRules, t)
}
}
func TestExternalIPsReject(t *testing.T) {
ipt := iptablestest.NewFake()
fp := NewFakeProxier(ipt)
svcIP := "10.20.30.41"
svcPort := 80
svcExternalIPs := "50.60.70.81"
svcPortName := proxy.ServicePortName{
NamespacedName: makeNSN("ns1", "svc1"),
Port: "p80",
}
makeServiceMap(fp,
2018-08-15 13:51:19 +00:00
makeTestService(svcPortName.Namespace, svcPortName.Name, func(svc *v1.Service) {
svc.Spec.Type = "ClusterIP"
svc.Spec.ClusterIP = svcIP
svc.Spec.ExternalIPs = []string{svcExternalIPs}
2018-08-15 13:51:19 +00:00
svc.Spec.Ports = []v1.ServicePort{{
Name: svcPortName.Port,
Port: int32(svcPort),
2018-08-15 13:51:19 +00:00
Protocol: v1.ProtocolTCP,
TargetPort: intstr.FromInt(svcPort),
}}
}),
)
makeEndpointsMap(fp)
fp.syncProxyRules()
kubeSvcRules := ipt.GetRules(string(kubeExternalServicesChain))
if !hasJump(kubeSvcRules, iptablestest.Reject, svcExternalIPs, svcPort) {
errorf(fmt.Sprintf("Failed to find a %v rule for externalIP %v with no endpoints", iptablestest.Reject, svcPortName), kubeSvcRules, t)
}
}
func TestNodePortReject(t *testing.T) {
ipt := iptablestest.NewFake()
fp := NewFakeProxier(ipt)
svcIP := "10.20.30.41"
svcPort := 80
svcNodePort := 3001
svcPortName := proxy.ServicePortName{
NamespacedName: makeNSN("ns1", "svc1"),
Port: "p80",
}
makeServiceMap(fp,
2018-08-15 13:51:19 +00:00
makeTestService(svcPortName.Namespace, svcPortName.Name, func(svc *v1.Service) {
svc.Spec.Type = "NodePort"
svc.Spec.ClusterIP = svcIP
2018-08-15 13:51:19 +00:00
svc.Spec.Ports = []v1.ServicePort{{
Name: svcPortName.Port,
Port: int32(svcPort),
2018-08-15 13:51:19 +00:00
Protocol: v1.ProtocolTCP,
NodePort: int32(svcNodePort),
}}
}),
)
makeEndpointsMap(fp)
fp.syncProxyRules()
kubeSvcRules := ipt.GetRules(string(kubeExternalServicesChain))
if !hasJump(kubeSvcRules, iptablestest.Reject, svcIP, svcNodePort) {
errorf(fmt.Sprintf("Failed to find a %v rule for service %v with no endpoints", iptablestest.Reject, svcPortName), kubeSvcRules, t)
}
}
func strPtr(s string) *string {
return &s
}
2016-09-27 02:48:21 +00:00
func TestOnlyLocalLoadBalancing(t *testing.T) {
ipt := iptablestest.NewFake()
fp := NewFakeProxier(ipt)
svcIP := "10.20.30.41"
svcPort := 80
svcNodePort := 3001
svcLBIP := "1.2.3.4"
svcPortName := proxy.ServicePortName{
NamespacedName: makeNSN("ns1", "svc1"),
Port: "p80",
}
svcSessionAffinityTimeout := int32(10800)
makeServiceMap(fp,
2018-08-15 13:51:19 +00:00
makeTestService(svcPortName.Namespace, svcPortName.Name, func(svc *v1.Service) {
svc.Spec.Type = "LoadBalancer"
svc.Spec.ClusterIP = svcIP
2018-08-15 13:51:19 +00:00
svc.Spec.Ports = []v1.ServicePort{{
Name: svcPortName.Port,
Port: int32(svcPort),
2018-08-15 13:51:19 +00:00
Protocol: v1.ProtocolTCP,
NodePort: int32(svcNodePort),
}}
2018-08-15 13:51:19 +00:00
svc.Status.LoadBalancer.Ingress = []v1.LoadBalancerIngress{{
IP: svcLBIP,
}}
2018-08-15 13:51:19 +00:00
svc.Spec.ExternalTrafficPolicy = v1.ServiceExternalTrafficPolicyTypeLocal
svc.Spec.SessionAffinity = v1.ServiceAffinityClientIP
svc.Spec.SessionAffinityConfig = &v1.SessionAffinityConfig{
ClientIP: &v1.ClientIPConfig{TimeoutSeconds: &svcSessionAffinityTimeout},
}
}),
)
epIP1 := "10.180.0.1"
epIP2 := "10.180.2.1"
epStrLocal := fmt.Sprintf("%s:%d", epIP1, svcPort)
epStrNonLocal := fmt.Sprintf("%s:%d", epIP2, svcPort)
makeEndpointsMap(fp,
2018-08-15 13:51:19 +00:00
makeTestEndpoints(svcPortName.Namespace, svcPortName.Name, func(ept *v1.Endpoints) {
ept.Subsets = []v1.EndpointSubset{{
Addresses: []v1.EndpointAddress{{
IP: epIP1,
NodeName: nil,
}, {
IP: epIP2,
NodeName: strPtr(testHostname),
}},
2018-08-15 13:51:19 +00:00
Ports: []v1.EndpointPort{{
Name: svcPortName.Port,
Port: int32(svcPort),
}},
}}
}),
2017-04-07 14:26:26 +00:00
)
fp.syncProxyRules()
2016-09-27 02:48:21 +00:00
2018-08-15 13:51:19 +00:00
proto := strings.ToLower(string(v1.ProtocolTCP))
fwChain := string(serviceFirewallChainName(svcPortName.String(), proto))
lbChain := string(serviceLBChainName(svcPortName.String(), proto))
2016-09-27 02:48:21 +00:00
2018-08-15 13:51:19 +00:00
nonLocalEpChain := string(servicePortEndpointChainName(svcPortName.String(), strings.ToLower(string(v1.ProtocolTCP)), epStrLocal))
localEpChain := string(servicePortEndpointChainName(svcPortName.String(), strings.ToLower(string(v1.ProtocolTCP)), epStrNonLocal))
2016-09-27 02:48:21 +00:00
kubeSvcRules := ipt.GetRules(string(kubeServicesChain))
if !hasJump(kubeSvcRules, fwChain, svcLBIP, svcPort) {
2016-09-27 02:48:21 +00:00
errorf(fmt.Sprintf("Failed to find jump to firewall chain %v", fwChain), kubeSvcRules, t)
}
fwRules := ipt.GetRules(fwChain)
if !hasJump(fwRules, lbChain, "", 0) {
2016-09-27 02:48:21 +00:00
errorf(fmt.Sprintf("Failed to find jump from firewall chain %v to svc chain %v", fwChain, lbChain), fwRules, t)
}
if hasJump(fwRules, string(KubeMarkMasqChain), "", 0) {
2016-09-27 02:48:21 +00:00
errorf(fmt.Sprintf("Found jump from fw chain %v to MASQUERADE", fwChain), fwRules, t)
}
lbRules := ipt.GetRules(lbChain)
if hasJump(lbRules, nonLocalEpChain, "", 0) {
errorf(fmt.Sprintf("Found jump from lb chain %v to non-local ep %v", lbChain, epStrLocal), lbRules, t)
2016-09-27 02:48:21 +00:00
}
if !hasJump(lbRules, localEpChain, "", 0) {
errorf(fmt.Sprintf("Didn't find jump from lb chain %v to local ep %v", lbChain, epStrNonLocal), lbRules, t)
2016-09-27 02:48:21 +00:00
}
if !hasSessionAffinityRule(lbRules) {
errorf(fmt.Sprintf("Didn't find session affinity rule from lb chain %v", lbChain), lbRules, t)
}
2016-09-27 02:48:21 +00:00
}
func TestOnlyLocalNodePortsNoClusterCIDR(t *testing.T) {
ipt := iptablestest.NewFake()
fp := NewFakeProxier(ipt)
// set cluster CIDR to empty before test
fp.clusterCIDR = ""
onlyLocalNodePorts(t, fp, ipt)
}
2016-09-27 02:48:21 +00:00
func TestOnlyLocalNodePorts(t *testing.T) {
ipt := iptablestest.NewFake()
fp := NewFakeProxier(ipt)
onlyLocalNodePorts(t, fp, ipt)
}
func onlyLocalNodePorts(t *testing.T, fp *Proxier, ipt *iptablestest.FakeIPTables) {
shouldLBTOSVCRuleExist := len(fp.clusterCIDR) > 0
svcIP := "10.20.30.41"
svcPort := 80
svcNodePort := 3001
svcPortName := proxy.ServicePortName{
NamespacedName: makeNSN("ns1", "svc1"),
Port: "p80",
}
makeServiceMap(fp,
2018-08-15 13:51:19 +00:00
makeTestService(svcPortName.Namespace, svcPortName.Name, func(svc *v1.Service) {
svc.Spec.Type = "NodePort"
svc.Spec.ClusterIP = svcIP
2018-08-15 13:51:19 +00:00
svc.Spec.Ports = []v1.ServicePort{{
Name: svcPortName.Port,
Port: int32(svcPort),
2018-08-15 13:51:19 +00:00
Protocol: v1.ProtocolTCP,
NodePort: int32(svcNodePort),
}}
2018-08-15 13:51:19 +00:00
svc.Spec.ExternalTrafficPolicy = v1.ServiceExternalTrafficPolicyTypeLocal
}),
)
epIP1 := "10.180.0.1"
epIP2 := "10.180.2.1"
epStrLocal := fmt.Sprintf("%s:%d", epIP1, svcPort)
epStrNonLocal := fmt.Sprintf("%s:%d", epIP2, svcPort)
makeEndpointsMap(fp,
2018-08-15 13:51:19 +00:00
makeTestEndpoints(svcPortName.Namespace, svcPortName.Name, func(ept *v1.Endpoints) {
ept.Subsets = []v1.EndpointSubset{{
Addresses: []v1.EndpointAddress{{
IP: epIP1,
NodeName: nil,
}, {
IP: epIP2,
NodeName: strPtr(testHostname),
}},
2018-08-15 13:51:19 +00:00
Ports: []v1.EndpointPort{{
Name: svcPortName.Port,
Port: int32(svcPort),
}},
}}
}),
2017-04-07 14:26:26 +00:00
)
2018-01-17 02:30:19 +00:00
itf := net.Interface{Index: 0, MTU: 0, Name: "eth0", HardwareAddr: nil, Flags: 0}
addrs := []net.Addr{utilproxytest.AddrStruct{Val: "10.20.30.51/24"}}
fp.networkInterfacer.(*utilproxytest.FakeNetwork).AddInterfaceAddr(&itf, addrs)
fp.nodePortAddresses = []string{"10.20.30.0/24"}
fp.syncProxyRules()
2016-09-27 02:48:21 +00:00
2018-08-15 13:51:19 +00:00
proto := strings.ToLower(string(v1.ProtocolTCP))
lbChain := string(serviceLBChainName(svcPortName.String(), proto))
2016-09-27 02:48:21 +00:00
nonLocalEpChain := string(servicePortEndpointChainName(svcPortName.String(), proto, epStrLocal))
localEpChain := string(servicePortEndpointChainName(svcPortName.String(), proto, epStrNonLocal))
2016-09-27 02:48:21 +00:00
kubeNodePortRules := ipt.GetRules(string(kubeNodePortsChain))
if !hasJump(kubeNodePortRules, lbChain, "", svcNodePort) {
2016-09-27 02:48:21 +00:00
errorf(fmt.Sprintf("Failed to find jump to lb chain %v", lbChain), kubeNodePortRules, t)
}
2018-01-08 02:44:47 +00:00
if !hasJump(kubeNodePortRules, string(KubeMarkMasqChain), "", svcNodePort) {
errorf(fmt.Sprintf("Failed to find jump to %s chain for destination IP %d", KubeMarkMasqChain, svcNodePort), kubeNodePortRules, t)
}
2018-01-17 02:30:19 +00:00
kubeServiceRules := ipt.GetRules(string(kubeServicesChain))
if !hasJump(kubeServiceRules, string(kubeNodePortsChain), "10.20.30.51", 0) {
errorf(fmt.Sprintf("Failed to find jump to KUBE-NODEPORTS chain %v", string(kubeNodePortsChain)), kubeServiceRules, t)
}
svcChain := string(servicePortChainName(svcPortName.String(), proto))
2016-09-27 02:48:21 +00:00
lbRules := ipt.GetRules(lbChain)
if hasJump(lbRules, nonLocalEpChain, "", 0) {
errorf(fmt.Sprintf("Found jump from lb chain %v to non-local ep %v", lbChain, epStrLocal), lbRules, t)
2016-09-27 02:48:21 +00:00
}
if hasJump(lbRules, svcChain, "", 0) != shouldLBTOSVCRuleExist {
prefix := "Did not find "
if !shouldLBTOSVCRuleExist {
prefix = "Found "
}
errorf(fmt.Sprintf("%s jump from lb chain %v to svc %v", prefix, lbChain, svcChain), lbRules, t)
}
if !hasJump(lbRules, localEpChain, "", 0) {
errorf(fmt.Sprintf("Didn't find jump from lb chain %v to local ep %v", lbChain, epStrLocal), lbRules, t)
2016-09-27 02:48:21 +00:00
}
}
2018-08-15 13:51:19 +00:00
func makeTestService(namespace, name string, svcFunc func(*v1.Service)) *v1.Service {
svc := &v1.Service{
ObjectMeta: metav1.ObjectMeta{
Name: name,
Namespace: namespace,
Annotations: map[string]string{},
},
2018-08-15 13:51:19 +00:00
Spec: v1.ServiceSpec{},
Status: v1.ServiceStatus{},
}
svcFunc(svc)
return svc
}
2018-08-15 13:51:19 +00:00
func addTestPort(array []v1.ServicePort, name string, protocol v1.Protocol, port, nodeport int32, targetPort int) []v1.ServicePort {
svcPort := v1.ServicePort{
Name: name,
Protocol: protocol,
Port: port,
NodePort: nodeport,
TargetPort: intstr.FromInt(targetPort),
}
return append(array, svcPort)
}
func TestBuildServiceMapAddRemove(t *testing.T) {
ipt := iptablestest.NewFake()
fp := NewFakeProxier(ipt)
2018-08-15 13:51:19 +00:00
services := []*v1.Service{
makeTestService("somewhere-else", "cluster-ip", func(svc *v1.Service) {
svc.Spec.Type = v1.ServiceTypeClusterIP
svc.Spec.ClusterIP = "172.16.55.4"
svc.Spec.Ports = addTestPort(svc.Spec.Ports, "something", "UDP", 1234, 4321, 0)
svc.Spec.Ports = addTestPort(svc.Spec.Ports, "somethingelse", "UDP", 1235, 5321, 0)
svc.Spec.Ports = addTestPort(svc.Spec.Ports, "sctpport", "SCTP", 1236, 6321, 0)
}),
2018-08-15 13:51:19 +00:00
makeTestService("somewhere-else", "node-port", func(svc *v1.Service) {
svc.Spec.Type = v1.ServiceTypeNodePort
svc.Spec.ClusterIP = "172.16.55.10"
svc.Spec.Ports = addTestPort(svc.Spec.Ports, "blahblah", "UDP", 345, 678, 0)
svc.Spec.Ports = addTestPort(svc.Spec.Ports, "moreblahblah", "TCP", 344, 677, 0)
svc.Spec.Ports = addTestPort(svc.Spec.Ports, "muchmoreblah", "SCTP", 343, 676, 0)
}),
2018-08-15 13:51:19 +00:00
makeTestService("somewhere", "load-balancer", func(svc *v1.Service) {
svc.Spec.Type = v1.ServiceTypeLoadBalancer
svc.Spec.ClusterIP = "172.16.55.11"
svc.Spec.LoadBalancerIP = "5.6.7.8"
svc.Spec.Ports = addTestPort(svc.Spec.Ports, "foobar", "UDP", 8675, 30061, 7000)
svc.Spec.Ports = addTestPort(svc.Spec.Ports, "baz", "UDP", 8676, 30062, 7001)
2018-08-15 13:51:19 +00:00
svc.Status.LoadBalancer = v1.LoadBalancerStatus{
Ingress: []v1.LoadBalancerIngress{
{IP: "10.1.2.4"},
},
}
}),
2018-08-15 13:51:19 +00:00
makeTestService("somewhere", "only-local-load-balancer", func(svc *v1.Service) {
svc.Spec.Type = v1.ServiceTypeLoadBalancer
svc.Spec.ClusterIP = "172.16.55.12"
svc.Spec.LoadBalancerIP = "5.6.7.8"
svc.Spec.Ports = addTestPort(svc.Spec.Ports, "foobar2", "UDP", 8677, 30063, 7002)
svc.Spec.Ports = addTestPort(svc.Spec.Ports, "baz", "UDP", 8678, 30064, 7003)
2018-08-15 13:51:19 +00:00
svc.Status.LoadBalancer = v1.LoadBalancerStatus{
Ingress: []v1.LoadBalancerIngress{
{IP: "10.1.2.3"},
},
}
2018-08-15 13:51:19 +00:00
svc.Spec.ExternalTrafficPolicy = v1.ServiceExternalTrafficPolicyTypeLocal
svc.Spec.HealthCheckNodePort = 345
}),
}
for i := range services {
fp.OnServiceAdd(services[i])
}
2017-12-10 07:12:23 +00:00
result := proxy.UpdateServiceMap(fp.serviceMap, fp.serviceChanges)
if len(fp.serviceMap) != 10 {
t.Errorf("expected service map length 10, got %v", fp.serviceMap)
}
// The only-local-loadbalancer ones get added
2017-12-10 07:12:23 +00:00
if len(result.HCServiceNodePorts) != 1 {
t.Errorf("expected 1 healthcheck port, got %v", result.HCServiceNodePorts)
} else {
nsn := makeNSN("somewhere", "only-local-load-balancer")
2017-12-10 07:12:23 +00:00
if port, found := result.HCServiceNodePorts[nsn]; !found || port != 345 {
t.Errorf("expected healthcheck port [%q]=345: got %v", nsn, result.HCServiceNodePorts)
}
}
2017-12-10 07:12:23 +00:00
if len(result.UDPStaleClusterIP) != 0 {
// Services only added, so nothing stale yet
2017-12-10 07:12:23 +00:00
t.Errorf("expected stale UDP services length 0, got %d", len(result.UDPStaleClusterIP))
}
// Remove some stuff
// oneService is a modification of services[0] with removed first port.
2018-08-15 13:51:19 +00:00
oneService := makeTestService("somewhere-else", "cluster-ip", func(svc *v1.Service) {
svc.Spec.Type = v1.ServiceTypeClusterIP
svc.Spec.ClusterIP = "172.16.55.4"
svc.Spec.Ports = addTestPort(svc.Spec.Ports, "somethingelse", "UDP", 1235, 5321, 0)
})
fp.OnServiceUpdate(services[0], oneService)
fp.OnServiceDelete(services[1])
fp.OnServiceDelete(services[2])
fp.OnServiceDelete(services[3])
2017-12-10 07:12:23 +00:00
result = proxy.UpdateServiceMap(fp.serviceMap, fp.serviceChanges)
if len(fp.serviceMap) != 1 {
t.Errorf("expected service map length 1, got %v", fp.serviceMap)
}
2017-12-10 07:12:23 +00:00
if len(result.HCServiceNodePorts) != 0 {
t.Errorf("expected 0 healthcheck ports, got %v", result.HCServiceNodePorts)
}
// All services but one were deleted. While you'd expect only the ClusterIPs
// from the three deleted services here, we still have the ClusterIP for
// the not-deleted service, because one of it's ServicePorts was deleted.
expectedStaleUDPServices := []string{"172.16.55.10", "172.16.55.4", "172.16.55.11", "172.16.55.12"}
2017-12-10 07:12:23 +00:00
if len(result.UDPStaleClusterIP) != len(expectedStaleUDPServices) {
t.Errorf("expected stale UDP services length %d, got %v", len(expectedStaleUDPServices), result.UDPStaleClusterIP.UnsortedList())
}
for _, ip := range expectedStaleUDPServices {
2017-12-10 07:12:23 +00:00
if !result.UDPStaleClusterIP.Has(ip) {
t.Errorf("expected stale UDP service service %s", ip)
}
}
}
func TestBuildServiceMapServiceHeadless(t *testing.T) {
ipt := iptablestest.NewFake()
fp := NewFakeProxier(ipt)
makeServiceMap(fp,
2018-08-15 13:51:19 +00:00
makeTestService("somewhere-else", "headless", func(svc *v1.Service) {
svc.Spec.Type = v1.ServiceTypeClusterIP
svc.Spec.ClusterIP = v1.ClusterIPNone
svc.Spec.Ports = addTestPort(svc.Spec.Ports, "rpc", "UDP", 1234, 0, 0)
}),
2018-08-15 13:51:19 +00:00
makeTestService("somewhere-else", "headless-without-port", func(svc *v1.Service) {
svc.Spec.Type = v1.ServiceTypeClusterIP
svc.Spec.ClusterIP = v1.ClusterIPNone
}),
)
// Headless service should be ignored
2017-12-10 07:12:23 +00:00
result := proxy.UpdateServiceMap(fp.serviceMap, fp.serviceChanges)
if len(fp.serviceMap) != 0 {
t.Errorf("expected service map length 0, got %d", len(fp.serviceMap))
}
// No proxied services, so no healthchecks
2017-12-10 07:12:23 +00:00
if len(result.HCServiceNodePorts) != 0 {
t.Errorf("expected healthcheck ports length 0, got %d", len(result.HCServiceNodePorts))
}
2017-12-10 07:12:23 +00:00
if len(result.UDPStaleClusterIP) != 0 {
t.Errorf("expected stale UDP services length 0, got %d", len(result.UDPStaleClusterIP))
}
}
func TestBuildServiceMapServiceTypeExternalName(t *testing.T) {
ipt := iptablestest.NewFake()
fp := NewFakeProxier(ipt)
makeServiceMap(fp,
2018-08-15 13:51:19 +00:00
makeTestService("somewhere-else", "external-name", func(svc *v1.Service) {
svc.Spec.Type = v1.ServiceTypeExternalName
svc.Spec.ClusterIP = "172.16.55.4" // Should be ignored
svc.Spec.ExternalName = "foo2.bar.com"
svc.Spec.Ports = addTestPort(svc.Spec.Ports, "blah", "UDP", 1235, 5321, 0)
}),
)
2017-12-10 07:12:23 +00:00
result := proxy.UpdateServiceMap(fp.serviceMap, fp.serviceChanges)
if len(fp.serviceMap) != 0 {
t.Errorf("expected service map length 0, got %v", fp.serviceMap)
}
// No proxied services, so no healthchecks
2017-12-10 07:12:23 +00:00
if len(result.HCServiceNodePorts) != 0 {
t.Errorf("expected healthcheck ports length 0, got %v", result.HCServiceNodePorts)
}
2017-12-10 07:12:23 +00:00
if len(result.UDPStaleClusterIP) != 0 {
t.Errorf("expected stale UDP services length 0, got %v", result.UDPStaleClusterIP)
}
}
func TestBuildServiceMapServiceUpdate(t *testing.T) {
ipt := iptablestest.NewFake()
fp := NewFakeProxier(ipt)
2018-08-15 13:51:19 +00:00
servicev1 := makeTestService("somewhere", "some-service", func(svc *v1.Service) {
svc.Spec.Type = v1.ServiceTypeClusterIP
svc.Spec.ClusterIP = "172.16.55.4"
svc.Spec.Ports = addTestPort(svc.Spec.Ports, "something", "UDP", 1234, 4321, 0)
svc.Spec.Ports = addTestPort(svc.Spec.Ports, "somethingelse", "TCP", 1235, 5321, 0)
})
2018-08-15 13:51:19 +00:00
servicev2 := makeTestService("somewhere", "some-service", func(svc *v1.Service) {
svc.Spec.Type = v1.ServiceTypeLoadBalancer
svc.Spec.ClusterIP = "172.16.55.4"
svc.Spec.LoadBalancerIP = "5.6.7.8"
svc.Spec.Ports = addTestPort(svc.Spec.Ports, "something", "UDP", 1234, 4321, 7002)
svc.Spec.Ports = addTestPort(svc.Spec.Ports, "somethingelse", "TCP", 1235, 5321, 7003)
2018-08-15 13:51:19 +00:00
svc.Status.LoadBalancer = v1.LoadBalancerStatus{
Ingress: []v1.LoadBalancerIngress{
{IP: "10.1.2.3"},
},
}
2018-08-15 13:51:19 +00:00
svc.Spec.ExternalTrafficPolicy = v1.ServiceExternalTrafficPolicyTypeLocal
svc.Spec.HealthCheckNodePort = 345
})
fp.OnServiceAdd(servicev1)
2017-12-10 07:12:23 +00:00
result := proxy.UpdateServiceMap(fp.serviceMap, fp.serviceChanges)
if len(fp.serviceMap) != 2 {
t.Errorf("expected service map length 2, got %v", fp.serviceMap)
}
2017-12-10 07:12:23 +00:00
if len(result.HCServiceNodePorts) != 0 {
t.Errorf("expected healthcheck ports length 0, got %v", result.HCServiceNodePorts)
}
2017-12-10 07:12:23 +00:00
if len(result.UDPStaleClusterIP) != 0 {
// Services only added, so nothing stale yet
2017-12-10 07:12:23 +00:00
t.Errorf("expected stale UDP services length 0, got %d", len(result.UDPStaleClusterIP))
}
// Change service to load-balancer
fp.OnServiceUpdate(servicev1, servicev2)
2017-12-10 07:12:23 +00:00
result = proxy.UpdateServiceMap(fp.serviceMap, fp.serviceChanges)
if len(fp.serviceMap) != 2 {
t.Errorf("expected service map length 2, got %v", fp.serviceMap)
}
2017-12-10 07:12:23 +00:00
if len(result.HCServiceNodePorts) != 1 {
t.Errorf("expected healthcheck ports length 1, got %v", result.HCServiceNodePorts)
}
2017-12-10 07:12:23 +00:00
if len(result.UDPStaleClusterIP) != 0 {
t.Errorf("expected stale UDP services length 0, got %v", result.UDPStaleClusterIP.UnsortedList())
}
// No change; make sure the service map stays the same and there are
// no health-check changes
fp.OnServiceUpdate(servicev2, servicev2)
2017-12-10 07:12:23 +00:00
result = proxy.UpdateServiceMap(fp.serviceMap, fp.serviceChanges)
if len(fp.serviceMap) != 2 {
t.Errorf("expected service map length 2, got %v", fp.serviceMap)
}
2017-12-10 07:12:23 +00:00
if len(result.HCServiceNodePorts) != 1 {
t.Errorf("expected healthcheck ports length 1, got %v", result.HCServiceNodePorts)
}
2017-12-10 07:12:23 +00:00
if len(result.UDPStaleClusterIP) != 0 {
t.Errorf("expected stale UDP services length 0, got %v", result.UDPStaleClusterIP.UnsortedList())
}
// And back to ClusterIP
fp.OnServiceUpdate(servicev2, servicev1)
2017-12-10 07:12:23 +00:00
result = proxy.UpdateServiceMap(fp.serviceMap, fp.serviceChanges)
if len(fp.serviceMap) != 2 {
t.Errorf("expected service map length 2, got %v", fp.serviceMap)
}
2017-12-10 07:12:23 +00:00
if len(result.HCServiceNodePorts) != 0 {
t.Errorf("expected healthcheck ports length 0, got %v", result.HCServiceNodePorts)
}
2017-12-10 07:12:23 +00:00
if len(result.UDPStaleClusterIP) != 0 {
// Services only added, so nothing stale yet
2017-12-10 07:12:23 +00:00
t.Errorf("expected stale UDP services length 0, got %d", len(result.UDPStaleClusterIP))
}
}
2018-08-15 13:51:19 +00:00
func makeTestEndpoints(namespace, name string, eptFunc func(*v1.Endpoints)) *v1.Endpoints {
ept := &v1.Endpoints{
ObjectMeta: metav1.ObjectMeta{
Name: name,
Namespace: namespace,
},
}
eptFunc(ept)
return ept
}
2018-08-15 13:51:19 +00:00
func makeEndpointsMap(proxier *Proxier, allEndpoints ...*v1.Endpoints) {
for i := range allEndpoints {
proxier.OnEndpointsAdd(allEndpoints[i])
2017-04-07 14:26:26 +00:00
}
proxier.mu.Lock()
defer proxier.mu.Unlock()
proxier.endpointsSynced = true
2017-04-07 14:26:26 +00:00
}
func makeNSN(namespace, name string) types.NamespacedName {
return types.NamespacedName{Namespace: namespace, Name: name}
}
func makeServicePortName(ns, name, port string) proxy.ServicePortName {
return proxy.ServicePortName{
NamespacedName: makeNSN(ns, name),
Port: port,
}
}
2018-08-15 13:51:19 +00:00
func makeServiceMap(proxier *Proxier, allServices ...*v1.Service) {
for i := range allServices {
proxier.OnServiceAdd(allServices[i])
}
proxier.mu.Lock()
defer proxier.mu.Unlock()
proxier.servicesSynced = true
}
2017-12-10 07:12:23 +00:00
func compareEndpointsMaps(t *testing.T, tci int, newMap proxy.EndpointsMap, expected map[proxy.ServicePortName][]*endpointsInfo) {
if len(newMap) != len(expected) {
t.Errorf("[%d] expected %d results, got %d: %v", tci, len(expected), len(newMap), newMap)
}
for x := range expected {
if len(newMap[x]) != len(expected[x]) {
t.Errorf("[%d] expected %d endpoints for %v, got %d", tci, len(expected[x]), x, len(newMap[x]))
} else {
for i := range expected[x] {
2017-12-10 07:12:23 +00:00
newEp, ok := newMap[x][i].(*endpointsInfo)
if !ok {
t.Errorf("Failed to cast endpointsInfo")
continue
}
if newEp.Endpoint != expected[x][i].Endpoint ||
newEp.IsLocal != expected[x][i].IsLocal ||
newEp.protocol != expected[x][i].protocol ||
newEp.chainName != expected[x][i].chainName {
2017-12-10 07:12:23 +00:00
t.Errorf("[%d] expected new[%v][%d] to be %v, got %v", tci, x, i, expected[x][i], newEp)
}
}
}
}
}
func Test_updateEndpointsMap(t *testing.T) {
2017-05-15 07:55:15 +00:00
var nodeName = testHostname
2018-08-15 13:51:19 +00:00
emptyEndpoint := func(ept *v1.Endpoints) {
ept.Subsets = []v1.EndpointSubset{}
2017-07-06 22:58:01 +00:00
}
2018-08-15 13:51:19 +00:00
unnamedPort := func(ept *v1.Endpoints) {
ept.Subsets = []v1.EndpointSubset{{
Addresses: []v1.EndpointAddress{{
IP: "1.1.1.1",
}},
2018-08-15 13:51:19 +00:00
Ports: []v1.EndpointPort{{
Port: 11,
}},
}}
}
2018-08-15 13:51:19 +00:00
unnamedPortLocal := func(ept *v1.Endpoints) {
ept.Subsets = []v1.EndpointSubset{{
Addresses: []v1.EndpointAddress{{
IP: "1.1.1.1",
NodeName: &nodeName,
}},
2018-08-15 13:51:19 +00:00
Ports: []v1.EndpointPort{{
Port: 11,
}},
}}
}
2018-08-15 13:51:19 +00:00
namedPortLocal := func(ept *v1.Endpoints) {
ept.Subsets = []v1.EndpointSubset{{
Addresses: []v1.EndpointAddress{{
IP: "1.1.1.1",
NodeName: &nodeName,
}},
2018-08-15 13:51:19 +00:00
Ports: []v1.EndpointPort{{
Name: "p11",
Port: 11,
}},
}}
}
2018-08-15 13:51:19 +00:00
namedPort := func(ept *v1.Endpoints) {
ept.Subsets = []v1.EndpointSubset{{
Addresses: []v1.EndpointAddress{{
IP: "1.1.1.1",
}},
2018-08-15 13:51:19 +00:00
Ports: []v1.EndpointPort{{
Name: "p11",
Port: 11,
}},
}}
}
2018-08-15 13:51:19 +00:00
namedPortRenamed := func(ept *v1.Endpoints) {
ept.Subsets = []v1.EndpointSubset{{
Addresses: []v1.EndpointAddress{{
IP: "1.1.1.1",
}},
2018-08-15 13:51:19 +00:00
Ports: []v1.EndpointPort{{
Name: "p11-2",
Port: 11,
}},
}}
}
2018-08-15 13:51:19 +00:00
namedPortRenumbered := func(ept *v1.Endpoints) {
ept.Subsets = []v1.EndpointSubset{{
Addresses: []v1.EndpointAddress{{
IP: "1.1.1.1",
}},
2018-08-15 13:51:19 +00:00
Ports: []v1.EndpointPort{{
Name: "p11",
Port: 22,
}},
}}
}
2018-08-15 13:51:19 +00:00
namedPortsLocalNoLocal := func(ept *v1.Endpoints) {
ept.Subsets = []v1.EndpointSubset{{
Addresses: []v1.EndpointAddress{{
IP: "1.1.1.1",
}, {
IP: "1.1.1.2",
NodeName: &nodeName,
}},
2018-08-15 13:51:19 +00:00
Ports: []v1.EndpointPort{{
Name: "p11",
Port: 11,
}, {
Name: "p12",
Port: 12,
}},
}}
}
2018-08-15 13:51:19 +00:00
multipleSubsets := func(ept *v1.Endpoints) {
ept.Subsets = []v1.EndpointSubset{{
Addresses: []v1.EndpointAddress{{
IP: "1.1.1.1",
}},
2018-08-15 13:51:19 +00:00
Ports: []v1.EndpointPort{{
Name: "p11",
Port: 11,
}},
}, {
2018-08-15 13:51:19 +00:00
Addresses: []v1.EndpointAddress{{
IP: "1.1.1.2",
}},
2018-08-15 13:51:19 +00:00
Ports: []v1.EndpointPort{{
Name: "p12",
Port: 12,
}},
}}
}
2018-08-15 13:51:19 +00:00
multipleSubsetsWithLocal := func(ept *v1.Endpoints) {
ept.Subsets = []v1.EndpointSubset{{
Addresses: []v1.EndpointAddress{{
IP: "1.1.1.1",
}},
2018-08-15 13:51:19 +00:00
Ports: []v1.EndpointPort{{
Name: "p11",
Port: 11,
}},
}, {
2018-08-15 13:51:19 +00:00
Addresses: []v1.EndpointAddress{{
IP: "1.1.1.2",
NodeName: &nodeName,
}},
2018-08-15 13:51:19 +00:00
Ports: []v1.EndpointPort{{
Name: "p12",
Port: 12,
}},
}}
}
2018-08-15 13:51:19 +00:00
multipleSubsetsMultiplePortsLocal := func(ept *v1.Endpoints) {
ept.Subsets = []v1.EndpointSubset{{
Addresses: []v1.EndpointAddress{{
IP: "1.1.1.1",
NodeName: &nodeName,
}},
2018-08-15 13:51:19 +00:00
Ports: []v1.EndpointPort{{
Name: "p11",
Port: 11,
}, {
Name: "p12",
Port: 12,
}},
}, {
2018-08-15 13:51:19 +00:00
Addresses: []v1.EndpointAddress{{
IP: "1.1.1.3",
}},
2018-08-15 13:51:19 +00:00
Ports: []v1.EndpointPort{{
Name: "p13",
Port: 13,
}},
}}
}
2018-08-15 13:51:19 +00:00
multipleSubsetsIPsPorts1 := func(ept *v1.Endpoints) {
ept.Subsets = []v1.EndpointSubset{{
Addresses: []v1.EndpointAddress{{
IP: "1.1.1.1",
}, {
IP: "1.1.1.2",
NodeName: &nodeName,
}},
2018-08-15 13:51:19 +00:00
Ports: []v1.EndpointPort{{
Name: "p11",
Port: 11,
}, {
Name: "p12",
Port: 12,
}},
}, {
2018-08-15 13:51:19 +00:00
Addresses: []v1.EndpointAddress{{
IP: "1.1.1.3",
}, {
IP: "1.1.1.4",
NodeName: &nodeName,
}},
2018-08-15 13:51:19 +00:00
Ports: []v1.EndpointPort{{
Name: "p13",
Port: 13,
}, {
Name: "p14",
Port: 14,
}},
}}
}
2018-08-15 13:51:19 +00:00
multipleSubsetsIPsPorts2 := func(ept *v1.Endpoints) {
ept.Subsets = []v1.EndpointSubset{{
Addresses: []v1.EndpointAddress{{
IP: "2.2.2.1",
}, {
IP: "2.2.2.2",
NodeName: &nodeName,
}},
2018-08-15 13:51:19 +00:00
Ports: []v1.EndpointPort{{
Name: "p21",
Port: 21,
}, {
Name: "p22",
Port: 22,
}},
}}
}
2018-08-15 13:51:19 +00:00
complexBefore1 := func(ept *v1.Endpoints) {
ept.Subsets = []v1.EndpointSubset{{
Addresses: []v1.EndpointAddress{{
IP: "1.1.1.1",
}},
2018-08-15 13:51:19 +00:00
Ports: []v1.EndpointPort{{
Name: "p11",
Port: 11,
}},
}}
}
2018-08-15 13:51:19 +00:00
complexBefore2 := func(ept *v1.Endpoints) {
ept.Subsets = []v1.EndpointSubset{{
Addresses: []v1.EndpointAddress{{
IP: "2.2.2.2",
NodeName: &nodeName,
}, {
IP: "2.2.2.22",
NodeName: &nodeName,
}},
2018-08-15 13:51:19 +00:00
Ports: []v1.EndpointPort{{
Name: "p22",
Port: 22,
}},
}, {
2018-08-15 13:51:19 +00:00
Addresses: []v1.EndpointAddress{{
IP: "2.2.2.3",
NodeName: &nodeName,
}},
2018-08-15 13:51:19 +00:00
Ports: []v1.EndpointPort{{
Name: "p23",
Port: 23,
}},
}}
}
2018-08-15 13:51:19 +00:00
complexBefore4 := func(ept *v1.Endpoints) {
ept.Subsets = []v1.EndpointSubset{{
Addresses: []v1.EndpointAddress{{
IP: "4.4.4.4",
NodeName: &nodeName,
}, {
IP: "4.4.4.5",
NodeName: &nodeName,
}},
2018-08-15 13:51:19 +00:00
Ports: []v1.EndpointPort{{
Name: "p44",
Port: 44,
}},
}, {
2018-08-15 13:51:19 +00:00
Addresses: []v1.EndpointAddress{{
IP: "4.4.4.6",
NodeName: &nodeName,
}},
2018-08-15 13:51:19 +00:00
Ports: []v1.EndpointPort{{
Name: "p45",
Port: 45,
}},
}}
}
2018-08-15 13:51:19 +00:00
complexAfter1 := func(ept *v1.Endpoints) {
ept.Subsets = []v1.EndpointSubset{{
Addresses: []v1.EndpointAddress{{
IP: "1.1.1.1",
}, {
IP: "1.1.1.11",
}},
2018-08-15 13:51:19 +00:00
Ports: []v1.EndpointPort{{
Name: "p11",
Port: 11,
}},
}, {
2018-08-15 13:51:19 +00:00
Addresses: []v1.EndpointAddress{{
IP: "1.1.1.2",
}},
2018-08-15 13:51:19 +00:00
Ports: []v1.EndpointPort{{
Name: "p12",
Port: 12,
}, {
Name: "p122",
Port: 122,
}},
}}
}
2018-08-15 13:51:19 +00:00
complexAfter3 := func(ept *v1.Endpoints) {
ept.Subsets = []v1.EndpointSubset{{
Addresses: []v1.EndpointAddress{{
IP: "3.3.3.3",
}},
2018-08-15 13:51:19 +00:00
Ports: []v1.EndpointPort{{
Name: "p33",
Port: 33,
}},
}}
}
2018-08-15 13:51:19 +00:00
complexAfter4 := func(ept *v1.Endpoints) {
ept.Subsets = []v1.EndpointSubset{{
Addresses: []v1.EndpointAddress{{
IP: "4.4.4.4",
NodeName: &nodeName,
}},
2018-08-15 13:51:19 +00:00
Ports: []v1.EndpointPort{{
Name: "p44",
Port: 44,
}},
}}
}
2017-02-04 00:05:09 +00:00
testCases := []struct {
// previousEndpoints and currentEndpoints are used to call appropriate
// handlers OnEndpoints* (based on whether corresponding values are nil
// or non-nil) and must be of equal length.
2018-08-15 13:51:19 +00:00
previousEndpoints []*v1.Endpoints
currentEndpoints []*v1.Endpoints
2017-07-06 22:58:01 +00:00
oldEndpoints map[proxy.ServicePortName][]*endpointsInfo
expectedResult map[proxy.ServicePortName][]*endpointsInfo
2017-12-10 07:12:23 +00:00
expectedStaleEndpoints []proxy.ServiceEndpoint
2017-07-06 22:58:01 +00:00
expectedStaleServiceNames map[proxy.ServicePortName]bool
expectedHealthchecks map[types.NamespacedName]int
2017-02-04 00:05:09 +00:00
}{{
// Case[0]: nothing
2017-07-06 22:58:01 +00:00
oldEndpoints: map[proxy.ServicePortName][]*endpointsInfo{},
expectedResult: map[proxy.ServicePortName][]*endpointsInfo{},
2017-12-10 07:12:23 +00:00
expectedStaleEndpoints: []proxy.ServiceEndpoint{},
2017-07-06 22:58:01 +00:00
expectedStaleServiceNames: map[proxy.ServicePortName]bool{},
expectedHealthchecks: map[types.NamespacedName]int{},
2017-02-04 00:05:09 +00:00
}, {
// Case[1]: no change, unnamed port
2018-08-15 13:51:19 +00:00
previousEndpoints: []*v1.Endpoints{
makeTestEndpoints("ns1", "ep1", unnamedPort),
},
2018-08-15 13:51:19 +00:00
currentEndpoints: []*v1.Endpoints{
makeTestEndpoints("ns1", "ep1", unnamedPort),
},
2017-02-04 00:05:09 +00:00
oldEndpoints: map[proxy.ServicePortName][]*endpointsInfo{
makeServicePortName("ns1", "ep1", ""): {
{BaseEndpointInfo: &proxy.BaseEndpointInfo{Endpoint: "1.1.1.1:11", IsLocal: false}},
2017-02-04 00:05:09 +00:00
},
},
expectedResult: map[proxy.ServicePortName][]*endpointsInfo{
makeServicePortName("ns1", "ep1", ""): {
{BaseEndpointInfo: &proxy.BaseEndpointInfo{Endpoint: "1.1.1.1:11", IsLocal: false}},
2017-02-04 00:05:09 +00:00
},
},
2017-12-10 07:12:23 +00:00
expectedStaleEndpoints: []proxy.ServiceEndpoint{},
2017-07-06 22:58:01 +00:00
expectedStaleServiceNames: map[proxy.ServicePortName]bool{},
expectedHealthchecks: map[types.NamespacedName]int{},
2017-02-04 00:05:09 +00:00
}, {
// Case[2]: no change, named port, local
2018-08-15 13:51:19 +00:00
previousEndpoints: []*v1.Endpoints{
makeTestEndpoints("ns1", "ep1", namedPortLocal),
},
2018-08-15 13:51:19 +00:00
currentEndpoints: []*v1.Endpoints{
makeTestEndpoints("ns1", "ep1", namedPortLocal),
},
2017-02-04 00:05:09 +00:00
oldEndpoints: map[proxy.ServicePortName][]*endpointsInfo{
makeServicePortName("ns1", "ep1", "p11"): {
{BaseEndpointInfo: &proxy.BaseEndpointInfo{Endpoint: "1.1.1.1:11", IsLocal: true}},
2017-02-04 00:05:09 +00:00
},
},
expectedResult: map[proxy.ServicePortName][]*endpointsInfo{
makeServicePortName("ns1", "ep1", "p11"): {
{BaseEndpointInfo: &proxy.BaseEndpointInfo{Endpoint: "1.1.1.1:11", IsLocal: true}},
2017-02-04 00:05:09 +00:00
},
},
2017-12-10 07:12:23 +00:00
expectedStaleEndpoints: []proxy.ServiceEndpoint{},
2017-07-06 22:58:01 +00:00
expectedStaleServiceNames: map[proxy.ServicePortName]bool{},
expectedHealthchecks: map[types.NamespacedName]int{
makeNSN("ns1", "ep1"): 1,
},
2017-02-04 00:05:09 +00:00
}, {
// Case[3]: no change, multiple subsets
2018-08-15 13:51:19 +00:00
previousEndpoints: []*v1.Endpoints{
makeTestEndpoints("ns1", "ep1", multipleSubsets),
},
2018-08-15 13:51:19 +00:00
currentEndpoints: []*v1.Endpoints{
makeTestEndpoints("ns1", "ep1", multipleSubsets),
},
2017-02-04 00:05:09 +00:00
oldEndpoints: map[proxy.ServicePortName][]*endpointsInfo{
makeServicePortName("ns1", "ep1", "p11"): {
{BaseEndpointInfo: &proxy.BaseEndpointInfo{Endpoint: "1.1.1.1:11", IsLocal: false}},
2017-02-04 00:05:09 +00:00
},
makeServicePortName("ns1", "ep1", "p12"): {
{BaseEndpointInfo: &proxy.BaseEndpointInfo{Endpoint: "1.1.1.2:12", IsLocal: false}},
2017-02-04 00:05:09 +00:00
},
},
expectedResult: map[proxy.ServicePortName][]*endpointsInfo{
makeServicePortName("ns1", "ep1", "p11"): {
{BaseEndpointInfo: &proxy.BaseEndpointInfo{Endpoint: "1.1.1.1:11", IsLocal: false}},
2017-02-04 00:05:09 +00:00
},
makeServicePortName("ns1", "ep1", "p12"): {
{BaseEndpointInfo: &proxy.BaseEndpointInfo{Endpoint: "1.1.1.2:12", IsLocal: false}},
2017-02-04 00:05:09 +00:00
},
},
2017-12-10 07:12:23 +00:00
expectedStaleEndpoints: []proxy.ServiceEndpoint{},
2017-07-06 22:58:01 +00:00
expectedStaleServiceNames: map[proxy.ServicePortName]bool{},
expectedHealthchecks: map[types.NamespacedName]int{},
2017-02-04 00:05:09 +00:00
}, {
// Case[4]: no change, multiple subsets, multiple ports, local
2018-08-15 13:51:19 +00:00
previousEndpoints: []*v1.Endpoints{
makeTestEndpoints("ns1", "ep1", multipleSubsetsMultiplePortsLocal),
},
2018-08-15 13:51:19 +00:00
currentEndpoints: []*v1.Endpoints{
makeTestEndpoints("ns1", "ep1", multipleSubsetsMultiplePortsLocal),
},
2017-02-04 00:05:09 +00:00
oldEndpoints: map[proxy.ServicePortName][]*endpointsInfo{
makeServicePortName("ns1", "ep1", "p11"): {
{BaseEndpointInfo: &proxy.BaseEndpointInfo{Endpoint: "1.1.1.1:11", IsLocal: true}},
2017-02-04 00:05:09 +00:00
},
makeServicePortName("ns1", "ep1", "p12"): {
{BaseEndpointInfo: &proxy.BaseEndpointInfo{Endpoint: "1.1.1.1:12", IsLocal: true}},
2017-02-04 00:05:09 +00:00
},
makeServicePortName("ns1", "ep1", "p13"): {
{BaseEndpointInfo: &proxy.BaseEndpointInfo{Endpoint: "1.1.1.3:13", IsLocal: false}},
2017-02-04 00:05:09 +00:00
},
},
expectedResult: map[proxy.ServicePortName][]*endpointsInfo{
makeServicePortName("ns1", "ep1", "p11"): {
{BaseEndpointInfo: &proxy.BaseEndpointInfo{Endpoint: "1.1.1.1:11", IsLocal: true}},
2017-02-04 00:05:09 +00:00
},
makeServicePortName("ns1", "ep1", "p12"): {
{BaseEndpointInfo: &proxy.BaseEndpointInfo{Endpoint: "1.1.1.1:12", IsLocal: true}},
2017-02-04 00:05:09 +00:00
},
makeServicePortName("ns1", "ep1", "p13"): {
{BaseEndpointInfo: &proxy.BaseEndpointInfo{Endpoint: "1.1.1.3:13", IsLocal: false}},
2017-02-04 00:05:09 +00:00
},
},
2017-12-10 07:12:23 +00:00
expectedStaleEndpoints: []proxy.ServiceEndpoint{},
2017-07-06 22:58:01 +00:00
expectedStaleServiceNames: map[proxy.ServicePortName]bool{},
expectedHealthchecks: map[types.NamespacedName]int{
makeNSN("ns1", "ep1"): 1,
},
2017-02-04 00:05:09 +00:00
}, {
// Case[5]: no change, multiple endpoints, subsets, IPs, and ports
2018-08-15 13:51:19 +00:00
previousEndpoints: []*v1.Endpoints{
makeTestEndpoints("ns1", "ep1", multipleSubsetsIPsPorts1),
makeTestEndpoints("ns2", "ep2", multipleSubsetsIPsPorts2),
},
2018-08-15 13:51:19 +00:00
currentEndpoints: []*v1.Endpoints{
makeTestEndpoints("ns1", "ep1", multipleSubsetsIPsPorts1),
makeTestEndpoints("ns2", "ep2", multipleSubsetsIPsPorts2),
},
2017-02-04 00:05:09 +00:00
oldEndpoints: map[proxy.ServicePortName][]*endpointsInfo{
makeServicePortName("ns1", "ep1", "p11"): {
{BaseEndpointInfo: &proxy.BaseEndpointInfo{Endpoint: "1.1.1.1:11", IsLocal: false}},
{BaseEndpointInfo: &proxy.BaseEndpointInfo{Endpoint: "1.1.1.2:11", IsLocal: true}},
2017-02-04 00:05:09 +00:00
},
makeServicePortName("ns1", "ep1", "p12"): {
{BaseEndpointInfo: &proxy.BaseEndpointInfo{Endpoint: "1.1.1.1:12", IsLocal: false}},
{BaseEndpointInfo: &proxy.BaseEndpointInfo{Endpoint: "1.1.1.2:12", IsLocal: true}},
2017-02-04 00:05:09 +00:00
},
makeServicePortName("ns1", "ep1", "p13"): {
{BaseEndpointInfo: &proxy.BaseEndpointInfo{Endpoint: "1.1.1.3:13", IsLocal: false}},
{BaseEndpointInfo: &proxy.BaseEndpointInfo{Endpoint: "1.1.1.4:13", IsLocal: true}},
2017-02-04 00:05:09 +00:00
},
makeServicePortName("ns1", "ep1", "p14"): {
{BaseEndpointInfo: &proxy.BaseEndpointInfo{Endpoint: "1.1.1.3:14", IsLocal: false}},
{BaseEndpointInfo: &proxy.BaseEndpointInfo{Endpoint: "1.1.1.4:14", IsLocal: true}},
2017-02-04 00:05:09 +00:00
},
makeServicePortName("ns2", "ep2", "p21"): {
{BaseEndpointInfo: &proxy.BaseEndpointInfo{Endpoint: "2.2.2.1:21", IsLocal: false}},
{BaseEndpointInfo: &proxy.BaseEndpointInfo{Endpoint: "2.2.2.2:21", IsLocal: true}},
2017-02-04 00:05:09 +00:00
},
makeServicePortName("ns2", "ep2", "p22"): {
{BaseEndpointInfo: &proxy.BaseEndpointInfo{Endpoint: "2.2.2.1:22", IsLocal: false}},
{BaseEndpointInfo: &proxy.BaseEndpointInfo{Endpoint: "2.2.2.2:22", IsLocal: true}},
2017-02-04 00:05:09 +00:00
},
},
expectedResult: map[proxy.ServicePortName][]*endpointsInfo{
makeServicePortName("ns1", "ep1", "p11"): {
{BaseEndpointInfo: &proxy.BaseEndpointInfo{Endpoint: "1.1.1.1:11", IsLocal: false}},
{BaseEndpointInfo: &proxy.BaseEndpointInfo{Endpoint: "1.1.1.2:11", IsLocal: true}},
2017-02-04 00:05:09 +00:00
},
makeServicePortName("ns1", "ep1", "p12"): {
{BaseEndpointInfo: &proxy.BaseEndpointInfo{Endpoint: "1.1.1.1:12", IsLocal: false}},
{BaseEndpointInfo: &proxy.BaseEndpointInfo{Endpoint: "1.1.1.2:12", IsLocal: true}},
2017-02-04 00:05:09 +00:00
},
makeServicePortName("ns1", "ep1", "p13"): {
{BaseEndpointInfo: &proxy.BaseEndpointInfo{Endpoint: "1.1.1.3:13", IsLocal: false}},
{BaseEndpointInfo: &proxy.BaseEndpointInfo{Endpoint: "1.1.1.4:13", IsLocal: true}},
2017-02-04 00:05:09 +00:00
},
makeServicePortName("ns1", "ep1", "p14"): {
{BaseEndpointInfo: &proxy.BaseEndpointInfo{Endpoint: "1.1.1.3:14", IsLocal: false}},
{BaseEndpointInfo: &proxy.BaseEndpointInfo{Endpoint: "1.1.1.4:14", IsLocal: true}},
2017-02-04 00:05:09 +00:00
},
makeServicePortName("ns2", "ep2", "p21"): {
{BaseEndpointInfo: &proxy.BaseEndpointInfo{Endpoint: "2.2.2.1:21", IsLocal: false}},
{BaseEndpointInfo: &proxy.BaseEndpointInfo{Endpoint: "2.2.2.2:21", IsLocal: true}},
2017-02-04 00:05:09 +00:00
},
makeServicePortName("ns2", "ep2", "p22"): {
{BaseEndpointInfo: &proxy.BaseEndpointInfo{Endpoint: "2.2.2.1:22", IsLocal: false}},
{BaseEndpointInfo: &proxy.BaseEndpointInfo{Endpoint: "2.2.2.2:22", IsLocal: true}},
2017-02-04 00:05:09 +00:00
},
},
2017-12-10 07:12:23 +00:00
expectedStaleEndpoints: []proxy.ServiceEndpoint{},
2017-07-06 22:58:01 +00:00
expectedStaleServiceNames: map[proxy.ServicePortName]bool{},
expectedHealthchecks: map[types.NamespacedName]int{
makeNSN("ns1", "ep1"): 2,
makeNSN("ns2", "ep2"): 1,
},
2017-02-04 00:05:09 +00:00
}, {
// Case[6]: add an Endpoints
2018-08-15 13:51:19 +00:00
previousEndpoints: []*v1.Endpoints{
nil,
},
2018-08-15 13:51:19 +00:00
currentEndpoints: []*v1.Endpoints{
makeTestEndpoints("ns1", "ep1", unnamedPortLocal),
},
oldEndpoints: map[proxy.ServicePortName][]*endpointsInfo{},
2017-02-04 00:05:09 +00:00
expectedResult: map[proxy.ServicePortName][]*endpointsInfo{
makeServicePortName("ns1", "ep1", ""): {
{BaseEndpointInfo: &proxy.BaseEndpointInfo{Endpoint: "1.1.1.1:11", IsLocal: true}},
2017-02-04 00:05:09 +00:00
},
},
2017-12-10 07:12:23 +00:00
expectedStaleEndpoints: []proxy.ServiceEndpoint{},
2017-07-06 22:58:01 +00:00
expectedStaleServiceNames: map[proxy.ServicePortName]bool{
makeServicePortName("ns1", "ep1", ""): true,
},
expectedHealthchecks: map[types.NamespacedName]int{
makeNSN("ns1", "ep1"): 1,
},
2017-02-04 00:05:09 +00:00
}, {
// Case[7]: remove an Endpoints
2018-08-15 13:51:19 +00:00
previousEndpoints: []*v1.Endpoints{
makeTestEndpoints("ns1", "ep1", unnamedPortLocal),
},
2018-08-15 13:51:19 +00:00
currentEndpoints: []*v1.Endpoints{
nil,
},
2017-02-04 00:05:09 +00:00
oldEndpoints: map[proxy.ServicePortName][]*endpointsInfo{
makeServicePortName("ns1", "ep1", ""): {
{BaseEndpointInfo: &proxy.BaseEndpointInfo{Endpoint: "1.1.1.1:11", IsLocal: true}},
2017-02-04 00:05:09 +00:00
},
},
expectedResult: map[proxy.ServicePortName][]*endpointsInfo{},
2017-12-10 07:12:23 +00:00
expectedStaleEndpoints: []proxy.ServiceEndpoint{{
Endpoint: "1.1.1.1:11",
ServicePortName: makeServicePortName("ns1", "ep1", ""),
2017-02-04 00:05:09 +00:00
}},
2017-07-06 22:58:01 +00:00
expectedStaleServiceNames: map[proxy.ServicePortName]bool{},
expectedHealthchecks: map[types.NamespacedName]int{},
2017-02-04 00:05:09 +00:00
}, {
// Case[8]: add an IP and port
2018-08-15 13:51:19 +00:00
previousEndpoints: []*v1.Endpoints{
makeTestEndpoints("ns1", "ep1", namedPort),
},
2018-08-15 13:51:19 +00:00
currentEndpoints: []*v1.Endpoints{
makeTestEndpoints("ns1", "ep1", namedPortsLocalNoLocal),
},
2017-02-04 00:05:09 +00:00
oldEndpoints: map[proxy.ServicePortName][]*endpointsInfo{
makeServicePortName("ns1", "ep1", "p11"): {
{BaseEndpointInfo: &proxy.BaseEndpointInfo{Endpoint: "1.1.1.1:11", IsLocal: false}},
2017-02-04 00:05:09 +00:00
},
},
expectedResult: map[proxy.ServicePortName][]*endpointsInfo{
makeServicePortName("ns1", "ep1", "p11"): {
{BaseEndpointInfo: &proxy.BaseEndpointInfo{Endpoint: "1.1.1.1:11", IsLocal: false}},
{BaseEndpointInfo: &proxy.BaseEndpointInfo{Endpoint: "1.1.1.2:11", IsLocal: true}},
2017-02-04 00:05:09 +00:00
},
makeServicePortName("ns1", "ep1", "p12"): {
{BaseEndpointInfo: &proxy.BaseEndpointInfo{Endpoint: "1.1.1.1:12", IsLocal: false}},
{BaseEndpointInfo: &proxy.BaseEndpointInfo{Endpoint: "1.1.1.2:12", IsLocal: true}},
2017-02-04 00:05:09 +00:00
},
},
2017-12-10 07:12:23 +00:00
expectedStaleEndpoints: []proxy.ServiceEndpoint{},
2017-07-06 22:58:01 +00:00
expectedStaleServiceNames: map[proxy.ServicePortName]bool{
makeServicePortName("ns1", "ep1", "p12"): true,
},
expectedHealthchecks: map[types.NamespacedName]int{
makeNSN("ns1", "ep1"): 1,
},
2017-02-04 00:05:09 +00:00
}, {
// Case[9]: remove an IP and port
2018-08-15 13:51:19 +00:00
previousEndpoints: []*v1.Endpoints{
makeTestEndpoints("ns1", "ep1", namedPortsLocalNoLocal),
},
2018-08-15 13:51:19 +00:00
currentEndpoints: []*v1.Endpoints{
makeTestEndpoints("ns1", "ep1", namedPort),
},
2017-02-04 00:05:09 +00:00
oldEndpoints: map[proxy.ServicePortName][]*endpointsInfo{
makeServicePortName("ns1", "ep1", "p11"): {
{BaseEndpointInfo: &proxy.BaseEndpointInfo{Endpoint: "1.1.1.1:11", IsLocal: false}},
{BaseEndpointInfo: &proxy.BaseEndpointInfo{Endpoint: "1.1.1.2:11", IsLocal: true}},
2017-02-04 00:05:09 +00:00
},
makeServicePortName("ns1", "ep1", "p12"): {
{BaseEndpointInfo: &proxy.BaseEndpointInfo{Endpoint: "1.1.1.1:12", IsLocal: false}},
{BaseEndpointInfo: &proxy.BaseEndpointInfo{Endpoint: "1.1.1.2:12", IsLocal: true}},
2017-02-04 00:05:09 +00:00
},
},
expectedResult: map[proxy.ServicePortName][]*endpointsInfo{
makeServicePortName("ns1", "ep1", "p11"): {
{BaseEndpointInfo: &proxy.BaseEndpointInfo{Endpoint: "1.1.1.1:11", IsLocal: false}},
2017-02-04 00:05:09 +00:00
},
},
2017-12-10 07:12:23 +00:00
expectedStaleEndpoints: []proxy.ServiceEndpoint{{
Endpoint: "1.1.1.2:11",
ServicePortName: makeServicePortName("ns1", "ep1", "p11"),
2017-02-04 00:05:09 +00:00
}, {
2017-12-10 07:12:23 +00:00
Endpoint: "1.1.1.1:12",
ServicePortName: makeServicePortName("ns1", "ep1", "p12"),
2017-02-04 00:05:09 +00:00
}, {
2017-12-10 07:12:23 +00:00
Endpoint: "1.1.1.2:12",
ServicePortName: makeServicePortName("ns1", "ep1", "p12"),
2017-02-04 00:05:09 +00:00
}},
2017-07-06 22:58:01 +00:00
expectedStaleServiceNames: map[proxy.ServicePortName]bool{},
expectedHealthchecks: map[types.NamespacedName]int{},
2017-02-04 00:05:09 +00:00
}, {
// Case[10]: add a subset
2018-08-15 13:51:19 +00:00
previousEndpoints: []*v1.Endpoints{
makeTestEndpoints("ns1", "ep1", namedPort),
},
2018-08-15 13:51:19 +00:00
currentEndpoints: []*v1.Endpoints{
makeTestEndpoints("ns1", "ep1", multipleSubsetsWithLocal),
},
2017-02-04 00:05:09 +00:00
oldEndpoints: map[proxy.ServicePortName][]*endpointsInfo{
makeServicePortName("ns1", "ep1", "p11"): {
{BaseEndpointInfo: &proxy.BaseEndpointInfo{Endpoint: "1.1.1.1:11", IsLocal: false}},
2017-02-04 00:05:09 +00:00
},
},
expectedResult: map[proxy.ServicePortName][]*endpointsInfo{
makeServicePortName("ns1", "ep1", "p11"): {
{BaseEndpointInfo: &proxy.BaseEndpointInfo{Endpoint: "1.1.1.1:11", IsLocal: false}},
2017-02-04 00:05:09 +00:00
},
makeServicePortName("ns1", "ep1", "p12"): {
{BaseEndpointInfo: &proxy.BaseEndpointInfo{Endpoint: "1.1.1.2:12", IsLocal: true}},
2017-02-04 00:05:09 +00:00
},
},
2017-12-10 07:12:23 +00:00
expectedStaleEndpoints: []proxy.ServiceEndpoint{},
2017-07-06 22:58:01 +00:00
expectedStaleServiceNames: map[proxy.ServicePortName]bool{
makeServicePortName("ns1", "ep1", "p12"): true,
},
expectedHealthchecks: map[types.NamespacedName]int{
makeNSN("ns1", "ep1"): 1,
},
2017-02-04 00:05:09 +00:00
}, {
// Case[11]: remove a subset
2018-08-15 13:51:19 +00:00
previousEndpoints: []*v1.Endpoints{
makeTestEndpoints("ns1", "ep1", multipleSubsets),
},
2018-08-15 13:51:19 +00:00
currentEndpoints: []*v1.Endpoints{
makeTestEndpoints("ns1", "ep1", namedPort),
},
2017-02-04 00:05:09 +00:00
oldEndpoints: map[proxy.ServicePortName][]*endpointsInfo{
makeServicePortName("ns1", "ep1", "p11"): {
{BaseEndpointInfo: &proxy.BaseEndpointInfo{Endpoint: "1.1.1.1:11", IsLocal: false}},
2017-02-04 00:05:09 +00:00
},
makeServicePortName("ns1", "ep1", "p12"): {
{BaseEndpointInfo: &proxy.BaseEndpointInfo{Endpoint: "1.1.1.2:12", IsLocal: false}},
2017-02-04 00:05:09 +00:00
},
},
expectedResult: map[proxy.ServicePortName][]*endpointsInfo{
makeServicePortName("ns1", "ep1", "p11"): {
{BaseEndpointInfo: &proxy.BaseEndpointInfo{Endpoint: "1.1.1.1:11", IsLocal: false}},
2017-02-04 00:05:09 +00:00
},
},
2017-12-10 07:12:23 +00:00
expectedStaleEndpoints: []proxy.ServiceEndpoint{{
Endpoint: "1.1.1.2:12",
ServicePortName: makeServicePortName("ns1", "ep1", "p12"),
2017-02-04 00:05:09 +00:00
}},
2017-07-06 22:58:01 +00:00
expectedStaleServiceNames: map[proxy.ServicePortName]bool{},
expectedHealthchecks: map[types.NamespacedName]int{},
2017-02-04 00:05:09 +00:00
}, {
// Case[12]: rename a port
2018-08-15 13:51:19 +00:00
previousEndpoints: []*v1.Endpoints{
makeTestEndpoints("ns1", "ep1", namedPort),
},
2018-08-15 13:51:19 +00:00
currentEndpoints: []*v1.Endpoints{
makeTestEndpoints("ns1", "ep1", namedPortRenamed),
},
2017-02-04 00:05:09 +00:00
oldEndpoints: map[proxy.ServicePortName][]*endpointsInfo{
makeServicePortName("ns1", "ep1", "p11"): {
{BaseEndpointInfo: &proxy.BaseEndpointInfo{Endpoint: "1.1.1.1:11", IsLocal: false}},
2017-02-04 00:05:09 +00:00
},
},
expectedResult: map[proxy.ServicePortName][]*endpointsInfo{
makeServicePortName("ns1", "ep1", "p11-2"): {
{BaseEndpointInfo: &proxy.BaseEndpointInfo{Endpoint: "1.1.1.1:11", IsLocal: false}},
2017-02-04 00:05:09 +00:00
},
},
2017-12-10 07:12:23 +00:00
expectedStaleEndpoints: []proxy.ServiceEndpoint{{
Endpoint: "1.1.1.1:11",
ServicePortName: makeServicePortName("ns1", "ep1", "p11"),
2017-02-04 00:05:09 +00:00
}},
2017-07-06 22:58:01 +00:00
expectedStaleServiceNames: map[proxy.ServicePortName]bool{
makeServicePortName("ns1", "ep1", "p11-2"): true,
},
expectedHealthchecks: map[types.NamespacedName]int{},
2017-02-04 00:05:09 +00:00
}, {
// Case[13]: renumber a port
2018-08-15 13:51:19 +00:00
previousEndpoints: []*v1.Endpoints{
makeTestEndpoints("ns1", "ep1", namedPort),
},
2018-08-15 13:51:19 +00:00
currentEndpoints: []*v1.Endpoints{
makeTestEndpoints("ns1", "ep1", namedPortRenumbered),
},
2017-02-04 00:05:09 +00:00
oldEndpoints: map[proxy.ServicePortName][]*endpointsInfo{
makeServicePortName("ns1", "ep1", "p11"): {
{BaseEndpointInfo: &proxy.BaseEndpointInfo{Endpoint: "1.1.1.1:11", IsLocal: false}},
2017-02-04 00:05:09 +00:00
},
},
expectedResult: map[proxy.ServicePortName][]*endpointsInfo{
makeServicePortName("ns1", "ep1", "p11"): {
{BaseEndpointInfo: &proxy.BaseEndpointInfo{Endpoint: "1.1.1.1:22", IsLocal: false}},
2017-02-04 00:05:09 +00:00
},
},
2017-12-10 07:12:23 +00:00
expectedStaleEndpoints: []proxy.ServiceEndpoint{{
Endpoint: "1.1.1.1:11",
ServicePortName: makeServicePortName("ns1", "ep1", "p11"),
2017-02-04 00:05:09 +00:00
}},
2017-07-06 22:58:01 +00:00
expectedStaleServiceNames: map[proxy.ServicePortName]bool{},
expectedHealthchecks: map[types.NamespacedName]int{},
2017-02-04 00:05:09 +00:00
}, {
// Case[14]: complex add and remove
2018-08-15 13:51:19 +00:00
previousEndpoints: []*v1.Endpoints{
makeTestEndpoints("ns1", "ep1", complexBefore1),
makeTestEndpoints("ns2", "ep2", complexBefore2),
nil,
makeTestEndpoints("ns4", "ep4", complexBefore4),
},
2018-08-15 13:51:19 +00:00
currentEndpoints: []*v1.Endpoints{
makeTestEndpoints("ns1", "ep1", complexAfter1),
nil,
makeTestEndpoints("ns3", "ep3", complexAfter3),
makeTestEndpoints("ns4", "ep4", complexAfter4),
},
2017-02-04 00:05:09 +00:00
oldEndpoints: map[proxy.ServicePortName][]*endpointsInfo{
makeServicePortName("ns1", "ep1", "p11"): {
{BaseEndpointInfo: &proxy.BaseEndpointInfo{Endpoint: "1.1.1.1:11", IsLocal: false}},
2017-02-04 00:05:09 +00:00
},
makeServicePortName("ns2", "ep2", "p22"): {
{BaseEndpointInfo: &proxy.BaseEndpointInfo{Endpoint: "2.2.2.2:22", IsLocal: true}},
{BaseEndpointInfo: &proxy.BaseEndpointInfo{Endpoint: "2.2.2.22:22", IsLocal: true}},
2017-02-04 00:05:09 +00:00
},
makeServicePortName("ns2", "ep2", "p23"): {
{BaseEndpointInfo: &proxy.BaseEndpointInfo{Endpoint: "2.2.2.3:23", IsLocal: true}},
2017-02-04 00:05:09 +00:00
},
makeServicePortName("ns4", "ep4", "p44"): {
{BaseEndpointInfo: &proxy.BaseEndpointInfo{Endpoint: "4.4.4.4:44", IsLocal: true}},
{BaseEndpointInfo: &proxy.BaseEndpointInfo{Endpoint: "4.4.4.5:44", IsLocal: true}},
2017-02-04 00:05:09 +00:00
},
makeServicePortName("ns4", "ep4", "p45"): {
{BaseEndpointInfo: &proxy.BaseEndpointInfo{Endpoint: "4.4.4.6:45", IsLocal: true}},
2017-02-04 00:05:09 +00:00
},
},
expectedResult: map[proxy.ServicePortName][]*endpointsInfo{
makeServicePortName("ns1", "ep1", "p11"): {
{BaseEndpointInfo: &proxy.BaseEndpointInfo{Endpoint: "1.1.1.1:11", IsLocal: false}},
{BaseEndpointInfo: &proxy.BaseEndpointInfo{Endpoint: "1.1.1.11:11", IsLocal: false}},
2017-02-04 00:05:09 +00:00
},
makeServicePortName("ns1", "ep1", "p12"): {
{BaseEndpointInfo: &proxy.BaseEndpointInfo{Endpoint: "1.1.1.2:12", IsLocal: false}},
2017-02-04 00:05:09 +00:00
},
makeServicePortName("ns1", "ep1", "p122"): {
{BaseEndpointInfo: &proxy.BaseEndpointInfo{Endpoint: "1.1.1.2:122", IsLocal: false}},
2017-02-04 00:05:09 +00:00
},
makeServicePortName("ns3", "ep3", "p33"): {
{BaseEndpointInfo: &proxy.BaseEndpointInfo{Endpoint: "3.3.3.3:33", IsLocal: false}},
2017-02-04 00:05:09 +00:00
},
makeServicePortName("ns4", "ep4", "p44"): {
{BaseEndpointInfo: &proxy.BaseEndpointInfo{Endpoint: "4.4.4.4:44", IsLocal: true}},
2017-02-04 00:05:09 +00:00
},
},
2017-12-10 07:12:23 +00:00
expectedStaleEndpoints: []proxy.ServiceEndpoint{{
Endpoint: "2.2.2.2:22",
ServicePortName: makeServicePortName("ns2", "ep2", "p22"),
2017-02-04 00:05:09 +00:00
}, {
2017-12-10 07:12:23 +00:00
Endpoint: "2.2.2.22:22",
ServicePortName: makeServicePortName("ns2", "ep2", "p22"),
2017-02-04 00:05:09 +00:00
}, {
2017-12-10 07:12:23 +00:00
Endpoint: "2.2.2.3:23",
ServicePortName: makeServicePortName("ns2", "ep2", "p23"),
2017-02-04 00:05:09 +00:00
}, {
2017-12-10 07:12:23 +00:00
Endpoint: "4.4.4.5:44",
ServicePortName: makeServicePortName("ns4", "ep4", "p44"),
2017-02-04 00:05:09 +00:00
}, {
2017-12-10 07:12:23 +00:00
Endpoint: "4.4.4.6:45",
ServicePortName: makeServicePortName("ns4", "ep4", "p45"),
2017-02-04 00:05:09 +00:00
}},
2017-07-06 22:58:01 +00:00
expectedStaleServiceNames: map[proxy.ServicePortName]bool{
makeServicePortName("ns1", "ep1", "p12"): true,
makeServicePortName("ns1", "ep1", "p122"): true,
makeServicePortName("ns3", "ep3", "p33"): true,
},
expectedHealthchecks: map[types.NamespacedName]int{
makeNSN("ns4", "ep4"): 1,
},
2017-07-06 22:58:01 +00:00
}, {
// Case[15]: change from 0 endpoint address to 1 unnamed port
2018-08-15 13:51:19 +00:00
previousEndpoints: []*v1.Endpoints{
2017-07-06 22:58:01 +00:00
makeTestEndpoints("ns1", "ep1", emptyEndpoint),
},
2018-08-15 13:51:19 +00:00
currentEndpoints: []*v1.Endpoints{
2017-07-06 22:58:01 +00:00
makeTestEndpoints("ns1", "ep1", unnamedPort),
},
oldEndpoints: map[proxy.ServicePortName][]*endpointsInfo{},
expectedResult: map[proxy.ServicePortName][]*endpointsInfo{
makeServicePortName("ns1", "ep1", ""): {
{BaseEndpointInfo: &proxy.BaseEndpointInfo{Endpoint: "1.1.1.1:11", IsLocal: false}},
2017-07-06 22:58:01 +00:00
},
},
2017-12-10 07:12:23 +00:00
expectedStaleEndpoints: []proxy.ServiceEndpoint{},
2017-07-06 22:58:01 +00:00
expectedStaleServiceNames: map[proxy.ServicePortName]bool{
makeServicePortName("ns1", "ep1", ""): true,
},
expectedHealthchecks: map[types.NamespacedName]int{},
},
}
2017-02-04 00:05:09 +00:00
for tci, tc := range testCases {
ipt := iptablestest.NewFake()
fp := NewFakeProxier(ipt)
fp.hostname = nodeName
// First check that after adding all previous versions of endpoints,
// the fp.oldEndpoints is as we expect.
for i := range tc.previousEndpoints {
if tc.previousEndpoints[i] != nil {
fp.OnEndpointsAdd(tc.previousEndpoints[i])
}
2017-02-04 00:05:09 +00:00
}
2017-12-10 07:12:23 +00:00
proxy.UpdateEndpointsMap(fp.endpointsMap, fp.endpointsChanges)
compareEndpointsMaps(t, tci, fp.endpointsMap, tc.oldEndpoints)
// Now let's call appropriate handlers to get to state we want to be.
if len(tc.previousEndpoints) != len(tc.currentEndpoints) {
t.Fatalf("[%d] different lengths of previous and current endpoints", tci)
continue
}
for i := range tc.previousEndpoints {
prev, curr := tc.previousEndpoints[i], tc.currentEndpoints[i]
switch {
case prev == nil:
fp.OnEndpointsAdd(curr)
case curr == nil:
fp.OnEndpointsDelete(prev)
default:
fp.OnEndpointsUpdate(prev, curr)
2017-02-04 00:05:09 +00:00
}
}
2017-12-10 07:12:23 +00:00
result := proxy.UpdateEndpointsMap(fp.endpointsMap, fp.endpointsChanges)
newMap := fp.endpointsMap
compareEndpointsMaps(t, tci, newMap, tc.expectedResult)
2017-12-10 07:12:23 +00:00
if len(result.StaleEndpoints) != len(tc.expectedStaleEndpoints) {
t.Errorf("[%d] expected %d staleEndpoints, got %d: %v", tci, len(tc.expectedStaleEndpoints), len(result.StaleEndpoints), result.StaleEndpoints)
2017-07-06 22:58:01 +00:00
}
for _, x := range tc.expectedStaleEndpoints {
2017-12-10 07:12:23 +00:00
found := false
for _, stale := range result.StaleEndpoints {
if stale == x {
found = true
break
}
}
if !found {
t.Errorf("[%d] expected staleEndpoints[%v], but didn't find it: %v", tci, x, result.StaleEndpoints)
2017-07-06 22:58:01 +00:00
}
}
2017-12-10 07:12:23 +00:00
if len(result.StaleServiceNames) != len(tc.expectedStaleServiceNames) {
t.Errorf("[%d] expected %d staleServiceNames, got %d: %v", tci, len(tc.expectedStaleServiceNames), len(result.StaleServiceNames), result.StaleServiceNames)
2017-02-04 00:05:09 +00:00
}
2017-07-06 22:58:01 +00:00
for svcName := range tc.expectedStaleServiceNames {
2017-12-10 07:12:23 +00:00
found := false
for _, stale := range result.StaleServiceNames {
if stale == svcName {
found = true
}
}
if !found {
t.Errorf("[%d] expected staleServiceNames[%v], but didn't find it: %v", tci, svcName, result.StaleServiceNames)
2017-02-04 00:05:09 +00:00
}
}
2017-12-10 07:12:23 +00:00
if !reflect.DeepEqual(result.HCEndpointsLocalIPSize, tc.expectedHealthchecks) {
t.Errorf("[%d] expected healthchecks %v, got %v", tci, tc.expectedHealthchecks, result.HCEndpointsLocalIPSize)
}
2017-02-04 00:05:09 +00:00
}
}
2016-09-27 02:48:21 +00:00
// TODO(thockin): add *more* tests for syncProxyRules() or break it down further and test the pieces.