From bd4105de0ee4478fc7dea905dc6ed663fb76f622 Mon Sep 17 00:00:00 2001 From: Mikalai Radchuk Date: Mon, 12 Nov 2018 00:06:35 +0000 Subject: [PATCH] Fixes address parsing in port-forward The rules for address parsing are: * Explicitly specified addresses must bind successfully * `localhost` is pinned to `127.0.0.1` and `::1` and at least one of those must bind successfully This change also makes output of the command consistent between runs with the same arguments. Previously the command was using the range via map of addresses which sometimes was producing different output because the order of values is not guaranteed in Go. Kubernetes-commit: 4ee2010111af602988bf9bedf66d1a0c36419b09 --- tools/portforward/portforward.go | 16 ++++++-- tools/portforward/portforward_test.go | 56 +++++++++++++++++++++++++++ 2 files changed, 68 insertions(+), 4 deletions(-) diff --git a/tools/portforward/portforward.go b/tools/portforward/portforward.go index 0e9b369a..357680ed 100644 --- a/tools/portforward/portforward.go +++ b/tools/portforward/portforward.go @@ -23,6 +23,7 @@ import ( "io/ioutil" "net" "net/http" + "sort" "strconv" "strings" "sync" @@ -122,10 +123,14 @@ func parseAddresses(addressesToParse []string) ([]listenAddress, error) { parsed := make(map[string]listenAddress) for _, address := range addressesToParse { if address == "localhost" { - ip := listenAddress{address: "127.0.0.1", protocol: "tcp4", failureMode: "all"} - parsed[ip.address] = ip - ip = listenAddress{address: "::1", protocol: "tcp6", failureMode: "all"} - parsed[ip.address] = ip + if _, exists := parsed["127.0.0.1"]; !exists { + ip := listenAddress{address: "127.0.0.1", protocol: "tcp4", failureMode: "all"} + parsed[ip.address] = ip + } + if _, exists := parsed["::1"]; !exists { + ip := listenAddress{address: "::1", protocol: "tcp6", failureMode: "all"} + parsed[ip.address] = ip + } } else if net.ParseIP(address).To4() != nil { parsed[address] = listenAddress{address: address, protocol: "tcp4", failureMode: "any"} } else if net.ParseIP(address) != nil { @@ -140,6 +145,9 @@ func parseAddresses(addressesToParse []string) ([]listenAddress, error) { addresses[id] = v id++ } + // Sort addresses before returning to get a stable order + sort.Slice(addresses, func(i, j int) bool { return addresses[i].address < addresses[j].address }) + return addresses, nil } diff --git a/tools/portforward/portforward_test.go b/tools/portforward/portforward_test.go index ff2401e7..dd8d4fd5 100644 --- a/tools/portforward/portforward_test.go +++ b/tools/portforward/portforward_test.go @@ -83,6 +83,62 @@ func TestParsePortsAndNew(t *testing.T) { {protocol: "tcp6", address: "::1", failureMode: "all"}, }, }, + { + input: []string{"5000:5000"}, + addresses: []string{"localhost", "::1"}, + expectedPorts: []ForwardedPort{ + {5000, 5000}, + }, + expectedAddresses: []listenAddress{ + {protocol: "tcp4", address: "127.0.0.1", failureMode: "all"}, + {protocol: "tcp6", address: "::1", failureMode: "any"}, + }, + }, + { + input: []string{"5000:5000"}, + addresses: []string{"localhost", "127.0.0.1", "::1"}, + expectedPorts: []ForwardedPort{ + {5000, 5000}, + }, + expectedAddresses: []listenAddress{ + {protocol: "tcp4", address: "127.0.0.1", failureMode: "any"}, + {protocol: "tcp6", address: "::1", failureMode: "any"}, + }, + }, + { + input: []string{"5000:5000"}, + addresses: []string{"localhost", "127.0.0.1", "10.10.10.1"}, + expectedPorts: []ForwardedPort{ + {5000, 5000}, + }, + expectedAddresses: []listenAddress{ + {protocol: "tcp4", address: "127.0.0.1", failureMode: "any"}, + {protocol: "tcp6", address: "::1", failureMode: "all"}, + {protocol: "tcp4", address: "10.10.10.1", failureMode: "any"}, + }, + }, + { + input: []string{"5000:5000"}, + addresses: []string{"127.0.0.1", "::1", "localhost"}, + expectedPorts: []ForwardedPort{ + {5000, 5000}, + }, + expectedAddresses: []listenAddress{ + {protocol: "tcp4", address: "127.0.0.1", failureMode: "any"}, + {protocol: "tcp6", address: "::1", failureMode: "any"}, + }, + }, + { + input: []string{"5000:5000"}, + addresses: []string{"10.0.0.1", "127.0.0.1"}, + expectedPorts: []ForwardedPort{ + {5000, 5000}, + }, + expectedAddresses: []listenAddress{ + {protocol: "tcp4", address: "10.0.0.1", failureMode: "any"}, + {protocol: "tcp4", address: "127.0.0.1", failureMode: "any"}, + }, + }, { input: []string{"5000", "5000:5000", "8888:5000", "5000:8888", ":5000", "0:5000"}, addresses: []string{"127.0.0.1", "::1"},