diff --git a/pkg/kubectl/cmd/portforward/portforward.go b/pkg/kubectl/cmd/portforward/portforward.go index ccd925207a7..d31a0193488 100644 --- a/pkg/kubectl/cmd/portforward/portforward.go +++ b/pkg/kubectl/cmd/portforward/portforward.go @@ -120,7 +120,7 @@ func NewCmdPortForward(f cmdutil.Factory, streams genericclioptions.IOStreams) * }, } cmdutil.AddPodRunningTimeoutFlag(cmd, defaultPodPortForwardWaitTimeout) - cmd.Flags().StringSliceVar(&opts.Address, "address", []string{"localhost"}, "Addresses to listen on (comma separated)") + cmd.Flags().StringSliceVar(&opts.Address, "address", []string{"localhost"}, "Addresses to listen on (comma separated). Only accepts IP addresses or localhost as a value. When localhost is supplied, kubectl will try to bind on both 127.0.0.1 and ::1 and will fail if neither of these addresses are available to bind.") // TODO support UID return cmd } diff --git a/staging/src/k8s.io/client-go/tools/portforward/portforward.go b/staging/src/k8s.io/client-go/tools/portforward/portforward.go index 0e9b369a983..357680ede04 100644 --- a/staging/src/k8s.io/client-go/tools/portforward/portforward.go +++ b/staging/src/k8s.io/client-go/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/staging/src/k8s.io/client-go/tools/portforward/portforward_test.go b/staging/src/k8s.io/client-go/tools/portforward/portforward_test.go index ff2401e76bf..dd8d4fd5e5b 100644 --- a/staging/src/k8s.io/client-go/tools/portforward/portforward_test.go +++ b/staging/src/k8s.io/client-go/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"},