proxier_test.go 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476
  1. /*
  2. Copyright 2015 The Kubernetes Authors.
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. */
  13. package iptables
  14. import (
  15. "testing"
  16. "fmt"
  17. "net"
  18. "strings"
  19. "k8s.io/kubernetes/pkg/api"
  20. "k8s.io/kubernetes/pkg/proxy"
  21. "k8s.io/kubernetes/pkg/types"
  22. "k8s.io/kubernetes/pkg/util/exec"
  23. utiliptables "k8s.io/kubernetes/pkg/util/iptables"
  24. )
  25. func checkAllLines(t *testing.T, table utiliptables.Table, save []byte, expectedLines map[utiliptables.Chain]string) {
  26. chainLines := utiliptables.GetChainLines(table, save)
  27. for chain, line := range chainLines {
  28. if expected, exists := expectedLines[chain]; exists {
  29. if expected != line {
  30. t.Errorf("getChainLines expected chain line not present. For chain: %s Expected: %s Got: %s", chain, expected, line)
  31. }
  32. } else {
  33. t.Errorf("getChainLines expected chain not present: %s", chain)
  34. }
  35. }
  36. }
  37. func TestReadLinesFromByteBuffer(t *testing.T) {
  38. testFn := func(byteArray []byte, expected []string) {
  39. index := 0
  40. readIndex := 0
  41. for ; readIndex < len(byteArray); index++ {
  42. line, n := utiliptables.ReadLine(readIndex, byteArray)
  43. readIndex = n
  44. if expected[index] != line {
  45. t.Errorf("expected:%q, actual:%q", expected[index], line)
  46. }
  47. } // for
  48. if readIndex < len(byteArray) {
  49. t.Errorf("Byte buffer was only partially read. Buffer length is:%d, readIndex is:%d", len(byteArray), readIndex)
  50. }
  51. if index < len(expected) {
  52. t.Errorf("All expected strings were not compared. expected arr length:%d, matched count:%d", len(expected), index-1)
  53. }
  54. }
  55. byteArray1 := []byte("\n Line 1 \n\n\n L ine4 \nLine 5 \n \n")
  56. expected1 := []string{"", "Line 1", "", "", "L ine4", "Line 5", ""}
  57. testFn(byteArray1, expected1)
  58. byteArray1 = []byte("")
  59. expected1 = []string{}
  60. testFn(byteArray1, expected1)
  61. byteArray1 = []byte("\n\n")
  62. expected1 = []string{"", ""}
  63. testFn(byteArray1, expected1)
  64. }
  65. func TestGetChainLines(t *testing.T) {
  66. iptables_save := `# Generated by iptables-save v1.4.7 on Wed Oct 29 14:56:01 2014
  67. *nat
  68. :PREROUTING ACCEPT [2136997:197881818]
  69. :POSTROUTING ACCEPT [4284525:258542680]
  70. :OUTPUT ACCEPT [5901660:357267963]
  71. -A PREROUTING -m addrtype --dst-type LOCAL -j DOCKER
  72. COMMIT
  73. # Completed on Wed Oct 29 14:56:01 2014`
  74. expected := map[utiliptables.Chain]string{
  75. utiliptables.ChainPrerouting: ":PREROUTING ACCEPT [2136997:197881818]",
  76. utiliptables.ChainPostrouting: ":POSTROUTING ACCEPT [4284525:258542680]",
  77. utiliptables.ChainOutput: ":OUTPUT ACCEPT [5901660:357267963]",
  78. }
  79. checkAllLines(t, utiliptables.TableNAT, []byte(iptables_save), expected)
  80. }
  81. func TestGetChainLinesMultipleTables(t *testing.T) {
  82. iptables_save := `# Generated by iptables-save v1.4.21 on Fri Aug 7 14:47:37 2015
  83. *nat
  84. :PREROUTING ACCEPT [2:138]
  85. :INPUT ACCEPT [0:0]
  86. :OUTPUT ACCEPT [0:0]
  87. :POSTROUTING ACCEPT [0:0]
  88. :DOCKER - [0:0]
  89. :KUBE-NODEPORT-CONTAINER - [0:0]
  90. :KUBE-NODEPORT-HOST - [0:0]
  91. :KUBE-PORTALS-CONTAINER - [0:0]
  92. :KUBE-PORTALS-HOST - [0:0]
  93. :KUBE-SVC-1111111111111111 - [0:0]
  94. :KUBE-SVC-2222222222222222 - [0:0]
  95. :KUBE-SVC-3333333333333333 - [0:0]
  96. :KUBE-SVC-4444444444444444 - [0:0]
  97. :KUBE-SVC-5555555555555555 - [0:0]
  98. :KUBE-SVC-6666666666666666 - [0:0]
  99. -A PREROUTING -m comment --comment "handle ClusterIPs; NOTE: this must be before the NodePort rules" -j KUBE-PORTALS-CONTAINER
  100. -A PREROUTING -m addrtype --dst-type LOCAL -j DOCKER
  101. -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
  102. -A OUTPUT -m comment --comment "handle ClusterIPs; NOTE: this must be before the NodePort rules" -j KUBE-PORTALS-HOST
  103. -A OUTPUT ! -d 127.0.0.0/8 -m addrtype --dst-type LOCAL -j DOCKER
  104. -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
  105. -A POSTROUTING -s 10.246.1.0/24 ! -o cbr0 -j MASQUERADE
  106. -A POSTROUTING -s 10.0.2.15/32 -d 10.0.2.15/32 -m comment --comment "handle pod connecting to self" -j MASQUERADE
  107. -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
  108. -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
  109. -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
  110. -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
  111. -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
  112. -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
  113. -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
  114. -A KUBE-SVC-2222222222222222 -m comment --comment "kube-system/kube-dns:dns-tcp" -j KUBE-SVC-3333333333333333
  115. -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
  116. -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
  117. -A KUBE-SVC-5555555555555555 -m comment --comment "default/kubernetes:" -j KUBE-SVC-4444444444444444
  118. -A KUBE-SVC-6666666666666666 -m comment --comment "kube-system/kube-dns:dns" -j KUBE-SVC-1111111111111111
  119. COMMIT
  120. # Completed on Fri Aug 7 14:47:37 2015
  121. # Generated by iptables-save v1.4.21 on Fri Aug 7 14:47:37 2015
  122. *filter
  123. :INPUT ACCEPT [17514:83115836]
  124. :FORWARD ACCEPT [0:0]
  125. :OUTPUT ACCEPT [8909:688225]
  126. :DOCKER - [0:0]
  127. -A FORWARD -o cbr0 -j DOCKER
  128. -A FORWARD -o cbr0 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
  129. -A FORWARD -i cbr0 ! -o cbr0 -j ACCEPT
  130. -A FORWARD -i cbr0 -o cbr0 -j ACCEPT
  131. COMMIT
  132. `
  133. expected := map[utiliptables.Chain]string{
  134. utiliptables.ChainPrerouting: ":PREROUTING ACCEPT [2:138]",
  135. utiliptables.Chain("INPUT"): ":INPUT ACCEPT [0:0]",
  136. utiliptables.Chain("OUTPUT"): ":OUTPUT ACCEPT [0:0]",
  137. utiliptables.ChainPostrouting: ":POSTROUTING ACCEPT [0:0]",
  138. utiliptables.Chain("DOCKER"): ":DOCKER - [0:0]",
  139. utiliptables.Chain("KUBE-NODEPORT-CONTAINER"): ":KUBE-NODEPORT-CONTAINER - [0:0]",
  140. utiliptables.Chain("KUBE-NODEPORT-HOST"): ":KUBE-NODEPORT-HOST - [0:0]",
  141. utiliptables.Chain("KUBE-PORTALS-CONTAINER"): ":KUBE-PORTALS-CONTAINER - [0:0]",
  142. utiliptables.Chain("KUBE-PORTALS-HOST"): ":KUBE-PORTALS-HOST - [0:0]",
  143. utiliptables.Chain("KUBE-SVC-1111111111111111"): ":KUBE-SVC-1111111111111111 - [0:0]",
  144. utiliptables.Chain("KUBE-SVC-2222222222222222"): ":KUBE-SVC-2222222222222222 - [0:0]",
  145. utiliptables.Chain("KUBE-SVC-3333333333333333"): ":KUBE-SVC-3333333333333333 - [0:0]",
  146. utiliptables.Chain("KUBE-SVC-4444444444444444"): ":KUBE-SVC-4444444444444444 - [0:0]",
  147. utiliptables.Chain("KUBE-SVC-5555555555555555"): ":KUBE-SVC-5555555555555555 - [0:0]",
  148. utiliptables.Chain("KUBE-SVC-6666666666666666"): ":KUBE-SVC-6666666666666666 - [0:0]",
  149. }
  150. checkAllLines(t, utiliptables.TableNAT, []byte(iptables_save), expected)
  151. }
  152. func TestGetRemovedEndpoints(t *testing.T) {
  153. testCases := []struct {
  154. currentEndpoints []string
  155. newEndpoints []string
  156. removedEndpoints []string
  157. }{
  158. {
  159. currentEndpoints: []string{"10.0.2.1:80", "10.0.2.2:80"},
  160. newEndpoints: []string{"10.0.2.1:80", "10.0.2.2:80"},
  161. removedEndpoints: []string{},
  162. },
  163. {
  164. currentEndpoints: []string{"10.0.2.1:80", "10.0.2.2:80", "10.0.2.3:80"},
  165. newEndpoints: []string{"10.0.2.1:80", "10.0.2.2:80"},
  166. removedEndpoints: []string{"10.0.2.3:80"},
  167. },
  168. {
  169. currentEndpoints: []string{},
  170. newEndpoints: []string{"10.0.2.1:80", "10.0.2.2:80"},
  171. removedEndpoints: []string{},
  172. },
  173. {
  174. currentEndpoints: []string{"10.0.2.1:80", "10.0.2.2:80"},
  175. newEndpoints: []string{},
  176. removedEndpoints: []string{"10.0.2.1:80", "10.0.2.2:80"},
  177. },
  178. {
  179. currentEndpoints: []string{"10.0.2.1:80", "10.0.2.2:80", "10.0.2.2:443"},
  180. newEndpoints: []string{"10.0.2.1:80", "10.0.2.2:80"},
  181. removedEndpoints: []string{"10.0.2.2:443"},
  182. },
  183. }
  184. for i := range testCases {
  185. res := getRemovedEndpoints(testCases[i].currentEndpoints, testCases[i].newEndpoints)
  186. if !slicesEquiv(res, testCases[i].removedEndpoints) {
  187. t.Errorf("Expected: %v, but getRemovedEndpoints returned: %v", testCases[i].removedEndpoints, res)
  188. }
  189. }
  190. }
  191. func TestExecConntrackTool(t *testing.T) {
  192. fcmd := exec.FakeCmd{
  193. CombinedOutputScript: []exec.FakeCombinedOutputAction{
  194. func() ([]byte, error) { return []byte("1 flow entries have been deleted"), nil },
  195. func() ([]byte, error) { return []byte("1 flow entries have been deleted"), nil },
  196. func() ([]byte, error) {
  197. return []byte(""), fmt.Errorf("conntrack v1.4.2 (conntrack-tools): 0 flow entries have been deleted.")
  198. },
  199. },
  200. }
  201. fexec := exec.FakeExec{
  202. CommandScript: []exec.FakeCommandAction{
  203. func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) },
  204. func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) },
  205. func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) },
  206. },
  207. LookPathFunc: func(cmd string) (string, error) { return cmd, nil },
  208. }
  209. fakeProxier := Proxier{exec: &fexec}
  210. testCases := [][]string{
  211. {"-L", "-p", "udp"},
  212. {"-D", "-p", "udp", "-d", "10.0.240.1"},
  213. {"-D", "-p", "udp", "--orig-dst", "10.240.0.2", "--dst-nat", "10.0.10.2"},
  214. }
  215. expectErr := []bool{false, false, true}
  216. for i := range testCases {
  217. err := fakeProxier.execConntrackTool(testCases[i]...)
  218. if expectErr[i] {
  219. if err == nil {
  220. t.Errorf("expected err, got %v", err)
  221. }
  222. } else {
  223. if err != nil {
  224. t.Errorf("expected success, got %v", err)
  225. }
  226. }
  227. execCmd := strings.Join(fcmd.CombinedOutputLog[i], " ")
  228. expectCmd := fmt.Sprintf("%s %s", "conntrack", strings.Join(testCases[i], " "))
  229. if execCmd != expectCmd {
  230. t.Errorf("expect execute command: %s, but got: %s", expectCmd, execCmd)
  231. }
  232. }
  233. }
  234. func newFakeServiceInfo(service proxy.ServicePortName, ip net.IP, protocol api.Protocol) *serviceInfo {
  235. return &serviceInfo{
  236. sessionAffinityType: api.ServiceAffinityNone, // default
  237. stickyMaxAgeSeconds: 180, // TODO: paramaterize this in the API.
  238. clusterIP: ip,
  239. protocol: protocol,
  240. }
  241. }
  242. func TestDeleteEndpointConnections(t *testing.T) {
  243. fcmd := exec.FakeCmd{
  244. CombinedOutputScript: []exec.FakeCombinedOutputAction{
  245. func() ([]byte, error) { return []byte("1 flow entries have been deleted"), nil },
  246. func() ([]byte, error) {
  247. return []byte(""), fmt.Errorf("conntrack v1.4.2 (conntrack-tools): 0 flow entries have been deleted.")
  248. },
  249. },
  250. }
  251. fexec := exec.FakeExec{
  252. CommandScript: []exec.FakeCommandAction{
  253. func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) },
  254. func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) },
  255. },
  256. LookPathFunc: func(cmd string) (string, error) { return cmd, nil },
  257. }
  258. serviceMap := make(map[proxy.ServicePortName]*serviceInfo)
  259. svc1 := proxy.ServicePortName{NamespacedName: types.NamespacedName{Namespace: "ns1", Name: "svc1"}, Port: ""}
  260. svc2 := proxy.ServicePortName{NamespacedName: types.NamespacedName{Namespace: "ns1", Name: "svc2"}, Port: ""}
  261. serviceMap[svc1] = newFakeServiceInfo(svc1, net.IPv4(10, 20, 30, 40), api.ProtocolUDP)
  262. serviceMap[svc2] = newFakeServiceInfo(svc1, net.IPv4(10, 20, 30, 41), api.ProtocolTCP)
  263. fakeProxier := Proxier{exec: &fexec, serviceMap: serviceMap}
  264. testCases := []endpointServicePair{
  265. {
  266. endpoint: "10.240.0.3:80",
  267. servicePortName: svc1,
  268. },
  269. {
  270. endpoint: "10.240.0.4:80",
  271. servicePortName: svc1,
  272. },
  273. {
  274. endpoint: "10.240.0.5:80",
  275. servicePortName: svc2,
  276. },
  277. }
  278. expectCommandExecCount := 0
  279. for i := range testCases {
  280. input := map[endpointServicePair]bool{testCases[i]: true}
  281. fakeProxier.deleteEndpointConnections(input)
  282. svcInfo := fakeProxier.serviceMap[testCases[i].servicePortName]
  283. if svcInfo.protocol == api.ProtocolUDP {
  284. svcIp := svcInfo.clusterIP.String()
  285. endpointIp := strings.Split(testCases[i].endpoint, ":")[0]
  286. expectCommand := fmt.Sprintf("conntrack -D --orig-dst %s --dst-nat %s -p udp", svcIp, endpointIp)
  287. execCommand := strings.Join(fcmd.CombinedOutputLog[expectCommandExecCount], " ")
  288. if expectCommand != execCommand {
  289. t.Errorf("Exepect comand: %s, but executed %s", expectCommand, execCommand)
  290. }
  291. expectCommandExecCount += 1
  292. }
  293. if expectCommandExecCount != fexec.CommandCalls {
  294. t.Errorf("Exepect comand executed %d times, but got %d", expectCommandExecCount, fexec.CommandCalls)
  295. }
  296. }
  297. }
  298. func TestDeleteServiceConnections(t *testing.T) {
  299. fcmd := exec.FakeCmd{
  300. CombinedOutputScript: []exec.FakeCombinedOutputAction{
  301. func() ([]byte, error) { return []byte("1 flow entries have been deleted"), nil },
  302. func() ([]byte, error) { return []byte("1 flow entries have been deleted"), nil },
  303. func() ([]byte, error) {
  304. return []byte(""), fmt.Errorf("conntrack v1.4.2 (conntrack-tools): 0 flow entries have been deleted.")
  305. },
  306. },
  307. }
  308. fexec := exec.FakeExec{
  309. CommandScript: []exec.FakeCommandAction{
  310. func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) },
  311. func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) },
  312. func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) },
  313. },
  314. LookPathFunc: func(cmd string) (string, error) { return cmd, nil },
  315. }
  316. fakeProxier := Proxier{exec: &fexec}
  317. testCases := [][]string{
  318. {
  319. "10.240.0.3",
  320. "10.240.0.5",
  321. },
  322. {
  323. "10.240.0.4",
  324. },
  325. }
  326. svcCount := 0
  327. for i := range testCases {
  328. fakeProxier.deleteServiceConnections(testCases[i])
  329. for _, ip := range testCases[i] {
  330. expectCommand := fmt.Sprintf("conntrack -D --orig-dst %s -p udp", ip)
  331. execCommand := strings.Join(fcmd.CombinedOutputLog[svcCount], " ")
  332. if expectCommand != execCommand {
  333. t.Errorf("Exepect comand: %s, but executed %s", expectCommand, execCommand)
  334. }
  335. svcCount += 1
  336. }
  337. if svcCount != fexec.CommandCalls {
  338. t.Errorf("Exepect comand executed %d times, but got %d", svcCount, fexec.CommandCalls)
  339. }
  340. }
  341. }
  342. type fakeClosable struct {
  343. closed bool
  344. }
  345. func (c *fakeClosable) Close() error {
  346. c.closed = true
  347. return nil
  348. }
  349. func TestRevertPorts(t *testing.T) {
  350. testCases := []struct {
  351. replacementPorts []localPort
  352. existingPorts []localPort
  353. expectToBeClose []bool
  354. }{
  355. {
  356. replacementPorts: []localPort{
  357. {port: 5001},
  358. {port: 5002},
  359. {port: 5003},
  360. },
  361. existingPorts: []localPort{},
  362. expectToBeClose: []bool{true, true, true},
  363. },
  364. {
  365. replacementPorts: []localPort{},
  366. existingPorts: []localPort{
  367. {port: 5001},
  368. {port: 5002},
  369. {port: 5003},
  370. },
  371. expectToBeClose: []bool{},
  372. },
  373. {
  374. replacementPorts: []localPort{
  375. {port: 5001},
  376. {port: 5002},
  377. {port: 5003},
  378. },
  379. existingPorts: []localPort{
  380. {port: 5001},
  381. {port: 5002},
  382. {port: 5003},
  383. },
  384. expectToBeClose: []bool{false, false, false},
  385. },
  386. {
  387. replacementPorts: []localPort{
  388. {port: 5001},
  389. {port: 5002},
  390. {port: 5003},
  391. },
  392. existingPorts: []localPort{
  393. {port: 5001},
  394. {port: 5003},
  395. },
  396. expectToBeClose: []bool{false, true, false},
  397. },
  398. {
  399. replacementPorts: []localPort{
  400. {port: 5001},
  401. {port: 5002},
  402. {port: 5003},
  403. },
  404. existingPorts: []localPort{
  405. {port: 5001},
  406. {port: 5002},
  407. {port: 5003},
  408. {port: 5004},
  409. },
  410. expectToBeClose: []bool{false, false, false},
  411. },
  412. }
  413. for i, tc := range testCases {
  414. replacementPortsMap := make(map[localPort]closeable)
  415. for _, lp := range tc.replacementPorts {
  416. replacementPortsMap[lp] = &fakeClosable{}
  417. }
  418. existingPortsMap := make(map[localPort]closeable)
  419. for _, lp := range tc.existingPorts {
  420. existingPortsMap[lp] = &fakeClosable{}
  421. }
  422. revertPorts(replacementPortsMap, existingPortsMap)
  423. for j, expectation := range tc.expectToBeClose {
  424. if replacementPortsMap[tc.replacementPorts[j]].(*fakeClosable).closed != expectation {
  425. t.Errorf("Expect replacement localport %v to be %v in test case %v", tc.replacementPorts[j], expectation, i)
  426. }
  427. }
  428. for _, lp := range tc.existingPorts {
  429. if existingPortsMap[lp].(*fakeClosable).closed == true {
  430. t.Errorf("Expect existing localport %v to be false in test case %v", lp, i)
  431. }
  432. }
  433. }
  434. }
  435. // TODO(thockin): add a test for syncProxyRules() or break it down further and test the pieces.