123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476 |
- /*
- Copyright 2015 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 iptables
- import (
- "testing"
- "fmt"
- "net"
- "strings"
- "k8s.io/kubernetes/pkg/api"
- "k8s.io/kubernetes/pkg/proxy"
- "k8s.io/kubernetes/pkg/types"
- "k8s.io/kubernetes/pkg/util/exec"
- utiliptables "k8s.io/kubernetes/pkg/util/iptables"
- )
- func checkAllLines(t *testing.T, table utiliptables.Table, save []byte, expectedLines map[utiliptables.Chain]string) {
- chainLines := utiliptables.GetChainLines(table, save)
- for chain, line := range chainLines {
- 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 TestReadLinesFromByteBuffer(t *testing.T) {
- testFn := func(byteArray []byte, expected []string) {
- index := 0
- readIndex := 0
- for ; readIndex < len(byteArray); index++ {
- line, n := utiliptables.ReadLine(readIndex, byteArray)
- readIndex = n
- if expected[index] != line {
- t.Errorf("expected:%q, actual:%q", expected[index], line)
- }
- } // for
- if readIndex < len(byteArray) {
- t.Errorf("Byte buffer was only partially read. Buffer length is:%d, readIndex is:%d", len(byteArray), readIndex)
- }
- if index < len(expected) {
- t.Errorf("All expected strings were not compared. expected arr length:%d, matched count:%d", len(expected), index-1)
- }
- }
- byteArray1 := []byte("\n Line 1 \n\n\n L ine4 \nLine 5 \n \n")
- expected1 := []string{"", "Line 1", "", "", "L ine4", "Line 5", ""}
- testFn(byteArray1, expected1)
- byteArray1 = []byte("")
- expected1 = []string{}
- testFn(byteArray1, expected1)
- byteArray1 = []byte("\n\n")
- expected1 = []string{"", ""}
- testFn(byteArray1, expected1)
- }
- func TestGetChainLines(t *testing.T) {
- iptables_save := `# Generated by iptables-save v1.4.7 on Wed Oct 29 14:56:01 2014
- *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(iptables_save), expected)
- }
- func TestGetChainLinesMultipleTables(t *testing.T) {
- iptables_save := `# Generated by iptables-save v1.4.21 on Fri Aug 7 14:47:37 2015
- *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]
- -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
- 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]",
- }
- checkAllLines(t, utiliptables.TableNAT, []byte(iptables_save), expected)
- }
- func TestGetRemovedEndpoints(t *testing.T) {
- testCases := []struct {
- currentEndpoints []string
- newEndpoints []string
- removedEndpoints []string
- }{
- {
- currentEndpoints: []string{"10.0.2.1:80", "10.0.2.2:80"},
- newEndpoints: []string{"10.0.2.1:80", "10.0.2.2:80"},
- removedEndpoints: []string{},
- },
- {
- currentEndpoints: []string{"10.0.2.1:80", "10.0.2.2:80", "10.0.2.3:80"},
- newEndpoints: []string{"10.0.2.1:80", "10.0.2.2:80"},
- removedEndpoints: []string{"10.0.2.3:80"},
- },
- {
- currentEndpoints: []string{},
- newEndpoints: []string{"10.0.2.1:80", "10.0.2.2:80"},
- removedEndpoints: []string{},
- },
- {
- currentEndpoints: []string{"10.0.2.1:80", "10.0.2.2:80"},
- newEndpoints: []string{},
- removedEndpoints: []string{"10.0.2.1:80", "10.0.2.2:80"},
- },
- {
- currentEndpoints: []string{"10.0.2.1:80", "10.0.2.2:80", "10.0.2.2:443"},
- newEndpoints: []string{"10.0.2.1:80", "10.0.2.2:80"},
- removedEndpoints: []string{"10.0.2.2:443"},
- },
- }
- for i := range testCases {
- res := getRemovedEndpoints(testCases[i].currentEndpoints, testCases[i].newEndpoints)
- if !slicesEquiv(res, testCases[i].removedEndpoints) {
- t.Errorf("Expected: %v, but getRemovedEndpoints returned: %v", testCases[i].removedEndpoints, res)
- }
- }
- }
- func TestExecConntrackTool(t *testing.T) {
- fcmd := exec.FakeCmd{
- CombinedOutputScript: []exec.FakeCombinedOutputAction{
- func() ([]byte, error) { return []byte("1 flow entries have been deleted"), nil },
- func() ([]byte, error) { return []byte("1 flow entries have been deleted"), nil },
- func() ([]byte, error) {
- return []byte(""), fmt.Errorf("conntrack v1.4.2 (conntrack-tools): 0 flow entries have been deleted.")
- },
- },
- }
- fexec := exec.FakeExec{
- CommandScript: []exec.FakeCommandAction{
- func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) },
- func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) },
- func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) },
- },
- LookPathFunc: func(cmd string) (string, error) { return cmd, nil },
- }
- fakeProxier := Proxier{exec: &fexec}
- testCases := [][]string{
- {"-L", "-p", "udp"},
- {"-D", "-p", "udp", "-d", "10.0.240.1"},
- {"-D", "-p", "udp", "--orig-dst", "10.240.0.2", "--dst-nat", "10.0.10.2"},
- }
- expectErr := []bool{false, false, true}
- for i := range testCases {
- err := fakeProxier.execConntrackTool(testCases[i]...)
- if expectErr[i] {
- if err == nil {
- t.Errorf("expected err, got %v", err)
- }
- } else {
- if err != nil {
- t.Errorf("expected success, got %v", err)
- }
- }
- execCmd := strings.Join(fcmd.CombinedOutputLog[i], " ")
- expectCmd := fmt.Sprintf("%s %s", "conntrack", strings.Join(testCases[i], " "))
- if execCmd != expectCmd {
- t.Errorf("expect execute command: %s, but got: %s", expectCmd, execCmd)
- }
- }
- }
- func newFakeServiceInfo(service proxy.ServicePortName, ip net.IP, protocol api.Protocol) *serviceInfo {
- return &serviceInfo{
- sessionAffinityType: api.ServiceAffinityNone, // default
- stickyMaxAgeSeconds: 180, // TODO: paramaterize this in the API.
- clusterIP: ip,
- protocol: protocol,
- }
- }
- func TestDeleteEndpointConnections(t *testing.T) {
- fcmd := exec.FakeCmd{
- CombinedOutputScript: []exec.FakeCombinedOutputAction{
- func() ([]byte, error) { return []byte("1 flow entries have been deleted"), nil },
- func() ([]byte, error) {
- return []byte(""), fmt.Errorf("conntrack v1.4.2 (conntrack-tools): 0 flow entries have been deleted.")
- },
- },
- }
- fexec := exec.FakeExec{
- CommandScript: []exec.FakeCommandAction{
- func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) },
- func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) },
- },
- LookPathFunc: func(cmd string) (string, error) { return cmd, nil },
- }
- serviceMap := make(map[proxy.ServicePortName]*serviceInfo)
- svc1 := proxy.ServicePortName{NamespacedName: types.NamespacedName{Namespace: "ns1", Name: "svc1"}, Port: ""}
- svc2 := proxy.ServicePortName{NamespacedName: types.NamespacedName{Namespace: "ns1", Name: "svc2"}, Port: ""}
- serviceMap[svc1] = newFakeServiceInfo(svc1, net.IPv4(10, 20, 30, 40), api.ProtocolUDP)
- serviceMap[svc2] = newFakeServiceInfo(svc1, net.IPv4(10, 20, 30, 41), api.ProtocolTCP)
- fakeProxier := Proxier{exec: &fexec, serviceMap: serviceMap}
- testCases := []endpointServicePair{
- {
- endpoint: "10.240.0.3:80",
- servicePortName: svc1,
- },
- {
- endpoint: "10.240.0.4:80",
- servicePortName: svc1,
- },
- {
- endpoint: "10.240.0.5:80",
- servicePortName: svc2,
- },
- }
- expectCommandExecCount := 0
- for i := range testCases {
- input := map[endpointServicePair]bool{testCases[i]: true}
- fakeProxier.deleteEndpointConnections(input)
- svcInfo := fakeProxier.serviceMap[testCases[i].servicePortName]
- if svcInfo.protocol == api.ProtocolUDP {
- svcIp := svcInfo.clusterIP.String()
- endpointIp := strings.Split(testCases[i].endpoint, ":")[0]
- expectCommand := fmt.Sprintf("conntrack -D --orig-dst %s --dst-nat %s -p udp", svcIp, endpointIp)
- execCommand := strings.Join(fcmd.CombinedOutputLog[expectCommandExecCount], " ")
- if expectCommand != execCommand {
- t.Errorf("Exepect comand: %s, but executed %s", expectCommand, execCommand)
- }
- expectCommandExecCount += 1
- }
- if expectCommandExecCount != fexec.CommandCalls {
- t.Errorf("Exepect comand executed %d times, but got %d", expectCommandExecCount, fexec.CommandCalls)
- }
- }
- }
- func TestDeleteServiceConnections(t *testing.T) {
- fcmd := exec.FakeCmd{
- CombinedOutputScript: []exec.FakeCombinedOutputAction{
- func() ([]byte, error) { return []byte("1 flow entries have been deleted"), nil },
- func() ([]byte, error) { return []byte("1 flow entries have been deleted"), nil },
- func() ([]byte, error) {
- return []byte(""), fmt.Errorf("conntrack v1.4.2 (conntrack-tools): 0 flow entries have been deleted.")
- },
- },
- }
- fexec := exec.FakeExec{
- CommandScript: []exec.FakeCommandAction{
- func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) },
- func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) },
- func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) },
- },
- LookPathFunc: func(cmd string) (string, error) { return cmd, nil },
- }
- fakeProxier := Proxier{exec: &fexec}
- testCases := [][]string{
- {
- "10.240.0.3",
- "10.240.0.5",
- },
- {
- "10.240.0.4",
- },
- }
- svcCount := 0
- for i := range testCases {
- fakeProxier.deleteServiceConnections(testCases[i])
- for _, ip := range testCases[i] {
- expectCommand := fmt.Sprintf("conntrack -D --orig-dst %s -p udp", ip)
- execCommand := strings.Join(fcmd.CombinedOutputLog[svcCount], " ")
- if expectCommand != execCommand {
- t.Errorf("Exepect comand: %s, but executed %s", expectCommand, execCommand)
- }
- svcCount += 1
- }
- if svcCount != fexec.CommandCalls {
- t.Errorf("Exepect comand executed %d times, but got %d", svcCount, fexec.CommandCalls)
- }
- }
- }
- type fakeClosable struct {
- closed bool
- }
- func (c *fakeClosable) Close() error {
- c.closed = true
- return nil
- }
- func TestRevertPorts(t *testing.T) {
- testCases := []struct {
- replacementPorts []localPort
- existingPorts []localPort
- expectToBeClose []bool
- }{
- {
- replacementPorts: []localPort{
- {port: 5001},
- {port: 5002},
- {port: 5003},
- },
- existingPorts: []localPort{},
- expectToBeClose: []bool{true, true, true},
- },
- {
- replacementPorts: []localPort{},
- existingPorts: []localPort{
- {port: 5001},
- {port: 5002},
- {port: 5003},
- },
- expectToBeClose: []bool{},
- },
- {
- replacementPorts: []localPort{
- {port: 5001},
- {port: 5002},
- {port: 5003},
- },
- existingPorts: []localPort{
- {port: 5001},
- {port: 5002},
- {port: 5003},
- },
- expectToBeClose: []bool{false, false, false},
- },
- {
- replacementPorts: []localPort{
- {port: 5001},
- {port: 5002},
- {port: 5003},
- },
- existingPorts: []localPort{
- {port: 5001},
- {port: 5003},
- },
- expectToBeClose: []bool{false, true, false},
- },
- {
- replacementPorts: []localPort{
- {port: 5001},
- {port: 5002},
- {port: 5003},
- },
- existingPorts: []localPort{
- {port: 5001},
- {port: 5002},
- {port: 5003},
- {port: 5004},
- },
- expectToBeClose: []bool{false, false, false},
- },
- }
- for i, tc := range testCases {
- replacementPortsMap := make(map[localPort]closeable)
- for _, lp := range tc.replacementPorts {
- replacementPortsMap[lp] = &fakeClosable{}
- }
- existingPortsMap := make(map[localPort]closeable)
- for _, lp := range tc.existingPorts {
- existingPortsMap[lp] = &fakeClosable{}
- }
- revertPorts(replacementPortsMap, existingPortsMap)
- for j, expectation := range tc.expectToBeClose {
- if replacementPortsMap[tc.replacementPorts[j]].(*fakeClosable).closed != expectation {
- t.Errorf("Expect replacement localport %v to be %v in test case %v", tc.replacementPorts[j], expectation, i)
- }
- }
- for _, lp := range tc.existingPorts {
- if existingPortsMap[lp].(*fakeClosable).closed == true {
- t.Errorf("Expect existing localport %v to be false in test case %v", lp, i)
- }
- }
- }
- }
- // TODO(thockin): add a test for syncProxyRules() or break it down further and test the pieces.
|