//go:build linux // +build linux /* 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 conntrack import ( "fmt" "strings" "testing" v1 "k8s.io/api/core/v1" "k8s.io/utils/exec" fakeexec "k8s.io/utils/exec/testing" ) var success = func() ([]byte, []byte, error) { return []byte("1 flow entries have been deleted"), nil, nil } var nothingToDelete = func() ([]byte, []byte, error) { return []byte(""), nil, fmt.Errorf("conntrack v1.4.2 (conntrack-tools): 0 flow entries have been deleted") } type testCT struct { execCT fcmd *fakeexec.FakeCmd } func makeCT(result fakeexec.FakeAction) *testCT { fcmd := &fakeexec.FakeCmd{ CombinedOutputScript: []fakeexec.FakeAction{result}, } fexec := &fakeexec.FakeExec{ CommandScript: []fakeexec.FakeCommandAction{ func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(fcmd, cmd, args...) }, }, LookPathFunc: func(cmd string) (string, error) { return cmd, nil }, } return &testCT{execCT{fexec}, fcmd} } // Gets the command that ct executed. (If it didn't execute any commands, this will // return "".) func (ct *testCT) getExecutedCommand() string { // FakeExec panics if you try to run more commands than you set it up for. So the // only possibilities here are that we ran 1 command or we ran 0. if ct.execer.(*fakeexec.FakeExec).CommandCalls != 1 { return "" } return strings.Join(ct.fcmd.CombinedOutputLog[0], " ") } func TestExec(t *testing.T) { testCases := []struct { args []string result fakeexec.FakeAction expectErr bool }{ { args: []string{"-D", "-p", "udp", "-d", "10.0.240.1"}, result: success, expectErr: false, }, { args: []string{"-D", "-p", "udp", "--orig-dst", "10.240.0.2", "--dst-nat", "10.0.10.2"}, result: nothingToDelete, expectErr: true, }, } for _, tc := range testCases { ct := makeCT(tc.result) err := ct.exec(tc.args...) if tc.expectErr { if err == nil { t.Errorf("expected err, got %v", err) } } else { if err != nil { t.Errorf("expected success, got %v", err) } } execCmd := ct.getExecutedCommand() expectCmd := "conntrack " + strings.Join(tc.args, " ") if execCmd != expectCmd { t.Errorf("expect execute command: %s, but got: %s", expectCmd, execCmd) } } } func TestClearEntriesForIP(t *testing.T) { testCases := []struct { name string ip string expectCommand string }{ { name: "IPv4", ip: "10.240.0.3", expectCommand: "conntrack -D --orig-dst 10.240.0.3 -p udp", }, { name: "IPv6", ip: "2001:db8::10", expectCommand: "conntrack -D --orig-dst 2001:db8::10 -p udp -f ipv6", }, } for _, tc := range testCases { ct := makeCT(success) if err := ct.ClearEntriesForIP(tc.ip, v1.ProtocolUDP); err != nil { t.Errorf("%s/success: Unexpected error: %v", tc.name, err) } execCommand := ct.getExecutedCommand() if tc.expectCommand != execCommand { t.Errorf("%s/success: Expect command: %s, but executed %s", tc.name, tc.expectCommand, execCommand) } ct = makeCT(nothingToDelete) if err := ct.ClearEntriesForIP(tc.ip, v1.ProtocolUDP); err != nil { t.Errorf("%s/nothing to delete: Unexpected error: %v", tc.name, err) } } } func TestClearEntriesForPort(t *testing.T) { testCases := []struct { name string port int isIPv6 bool expectCommand string }{ { name: "IPv4", port: 8080, isIPv6: false, expectCommand: "conntrack -D -p udp --dport 8080", }, { name: "IPv6", port: 6666, isIPv6: true, expectCommand: "conntrack -D -p udp --dport 6666 -f ipv6", }, } for _, tc := range testCases { ct := makeCT(success) err := ct.ClearEntriesForPort(tc.port, tc.isIPv6, v1.ProtocolUDP) if err != nil { t.Errorf("%s/success: Unexpected error: %v", tc.name, err) } execCommand := ct.getExecutedCommand() if tc.expectCommand != execCommand { t.Errorf("%s/success: Expect command: %s, but executed %s", tc.name, tc.expectCommand, execCommand) } ct = makeCT(nothingToDelete) err = ct.ClearEntriesForPort(tc.port, tc.isIPv6, v1.ProtocolUDP) if err != nil { t.Errorf("%s/nothing to delete: Unexpected error: %v", tc.name, err) } } } func TestClearEntriesForNAT(t *testing.T) { testCases := []struct { name string origin string dest string expectCommand string }{ { name: "IPv4", origin: "1.2.3.4", dest: "10.20.30.40", expectCommand: "conntrack -D --orig-dst 1.2.3.4 --dst-nat 10.20.30.40 -p udp", }, { name: "IPv6", origin: "fd00::600d:f00d", dest: "2001:db8::5", expectCommand: "conntrack -D --orig-dst fd00::600d:f00d --dst-nat 2001:db8::5 -p udp -f ipv6", }, } for _, tc := range testCases { ct := makeCT(success) err := ct.ClearEntriesForNAT(tc.origin, tc.dest, v1.ProtocolUDP) if err != nil { t.Errorf("%s/success: unexpected error: %v", tc.name, err) } execCommand := ct.getExecutedCommand() if tc.expectCommand != execCommand { t.Errorf("%s/success: Expect command: %s, but executed %s", tc.name, tc.expectCommand, execCommand) } ct = makeCT(nothingToDelete) err = ct.ClearEntriesForNAT(tc.origin, tc.dest, v1.ProtocolUDP) if err != nil { t.Errorf("%s/nothing to delete: unexpected error: %v", tc.name, err) } } } func TestClearEntriesForPortNAT(t *testing.T) { testCases := []struct { name string port int dest string expectCommand string }{ { name: "IPv4", port: 30211, dest: "1.2.3.4", expectCommand: "conntrack -D -p udp --dport 30211 --dst-nat 1.2.3.4", }, { name: "IPv6", port: 30212, dest: "2600:5200::7800", expectCommand: "conntrack -D -p udp --dport 30212 --dst-nat 2600:5200::7800 -f ipv6", }, } for _, tc := range testCases { ct := makeCT(success) err := ct.ClearEntriesForPortNAT(tc.dest, tc.port, v1.ProtocolUDP) if err != nil { t.Errorf("%s/success: unexpected error: %v", tc.name, err) } execCommand := ct.getExecutedCommand() if tc.expectCommand != execCommand { t.Errorf("%s/success: Expect command: %s, but executed %s", tc.name, tc.expectCommand, execCommand) } ct = makeCT(nothingToDelete) err = ct.ClearEntriesForPortNAT(tc.dest, tc.port, v1.ProtocolUDP) if err != nil { t.Errorf("%s/nothing to delete: unexpected error: %v", tc.name, err) } } }