Merge pull request #73676 from martin-helmich/bugfix/expose-forwarded-local-port

client-go: Dynamically assigned local port number not retrievable when port-forwarding

Kubernetes-commit: 38a325250fbefa8785740d00358978eefa160dde
This commit is contained in:
Kubernetes Publisher 2019-02-26 20:55:12 -08:00
commit 269fa37ba0
3 changed files with 131 additions and 54 deletions

104
Godeps/Godeps.json generated
View File

@ -1,7 +1,7 @@
{ {
"ImportPath": "k8s.io/client-go", "ImportPath": "k8s.io/client-go",
"GoVersion": "go1.11", "GoVersion": "go1.11",
"GodepVersion": "v80-k8s-r1", "GodepVersion": "v80",
"Packages": [ "Packages": [
"./..." "./..."
], ],
@ -412,207 +412,207 @@
}, },
{ {
"ImportPath": "k8s.io/apimachinery/pkg/api/apitesting", "ImportPath": "k8s.io/apimachinery/pkg/api/apitesting",
"Rev": "f951fa7b8c72cf726656de66d834a518359ce6e7" "Rev": "dcb391cde5ca0298013d43336817d20b74650702"
}, },
{ {
"ImportPath": "k8s.io/apimachinery/pkg/api/apitesting/fuzzer", "ImportPath": "k8s.io/apimachinery/pkg/api/apitesting/fuzzer",
"Rev": "f951fa7b8c72cf726656de66d834a518359ce6e7" "Rev": "dcb391cde5ca0298013d43336817d20b74650702"
}, },
{ {
"ImportPath": "k8s.io/apimachinery/pkg/api/apitesting/roundtrip", "ImportPath": "k8s.io/apimachinery/pkg/api/apitesting/roundtrip",
"Rev": "f951fa7b8c72cf726656de66d834a518359ce6e7" "Rev": "dcb391cde5ca0298013d43336817d20b74650702"
}, },
{ {
"ImportPath": "k8s.io/apimachinery/pkg/api/equality", "ImportPath": "k8s.io/apimachinery/pkg/api/equality",
"Rev": "f951fa7b8c72cf726656de66d834a518359ce6e7" "Rev": "dcb391cde5ca0298013d43336817d20b74650702"
}, },
{ {
"ImportPath": "k8s.io/apimachinery/pkg/api/errors", "ImportPath": "k8s.io/apimachinery/pkg/api/errors",
"Rev": "f951fa7b8c72cf726656de66d834a518359ce6e7" "Rev": "dcb391cde5ca0298013d43336817d20b74650702"
}, },
{ {
"ImportPath": "k8s.io/apimachinery/pkg/api/meta", "ImportPath": "k8s.io/apimachinery/pkg/api/meta",
"Rev": "f951fa7b8c72cf726656de66d834a518359ce6e7" "Rev": "dcb391cde5ca0298013d43336817d20b74650702"
}, },
{ {
"ImportPath": "k8s.io/apimachinery/pkg/api/resource", "ImportPath": "k8s.io/apimachinery/pkg/api/resource",
"Rev": "f951fa7b8c72cf726656de66d834a518359ce6e7" "Rev": "dcb391cde5ca0298013d43336817d20b74650702"
}, },
{ {
"ImportPath": "k8s.io/apimachinery/pkg/apis/meta/fuzzer", "ImportPath": "k8s.io/apimachinery/pkg/apis/meta/fuzzer",
"Rev": "f951fa7b8c72cf726656de66d834a518359ce6e7" "Rev": "dcb391cde5ca0298013d43336817d20b74650702"
}, },
{ {
"ImportPath": "k8s.io/apimachinery/pkg/apis/meta/internalversion", "ImportPath": "k8s.io/apimachinery/pkg/apis/meta/internalversion",
"Rev": "f951fa7b8c72cf726656de66d834a518359ce6e7" "Rev": "dcb391cde5ca0298013d43336817d20b74650702"
}, },
{ {
"ImportPath": "k8s.io/apimachinery/pkg/apis/meta/v1", "ImportPath": "k8s.io/apimachinery/pkg/apis/meta/v1",
"Rev": "f951fa7b8c72cf726656de66d834a518359ce6e7" "Rev": "dcb391cde5ca0298013d43336817d20b74650702"
}, },
{ {
"ImportPath": "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured", "ImportPath": "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured",
"Rev": "f951fa7b8c72cf726656de66d834a518359ce6e7" "Rev": "dcb391cde5ca0298013d43336817d20b74650702"
}, },
{ {
"ImportPath": "k8s.io/apimachinery/pkg/apis/meta/v1beta1", "ImportPath": "k8s.io/apimachinery/pkg/apis/meta/v1beta1",
"Rev": "f951fa7b8c72cf726656de66d834a518359ce6e7" "Rev": "dcb391cde5ca0298013d43336817d20b74650702"
}, },
{ {
"ImportPath": "k8s.io/apimachinery/pkg/conversion", "ImportPath": "k8s.io/apimachinery/pkg/conversion",
"Rev": "f951fa7b8c72cf726656de66d834a518359ce6e7" "Rev": "dcb391cde5ca0298013d43336817d20b74650702"
}, },
{ {
"ImportPath": "k8s.io/apimachinery/pkg/conversion/queryparams", "ImportPath": "k8s.io/apimachinery/pkg/conversion/queryparams",
"Rev": "f951fa7b8c72cf726656de66d834a518359ce6e7" "Rev": "dcb391cde5ca0298013d43336817d20b74650702"
}, },
{ {
"ImportPath": "k8s.io/apimachinery/pkg/fields", "ImportPath": "k8s.io/apimachinery/pkg/fields",
"Rev": "f951fa7b8c72cf726656de66d834a518359ce6e7" "Rev": "dcb391cde5ca0298013d43336817d20b74650702"
}, },
{ {
"ImportPath": "k8s.io/apimachinery/pkg/labels", "ImportPath": "k8s.io/apimachinery/pkg/labels",
"Rev": "f951fa7b8c72cf726656de66d834a518359ce6e7" "Rev": "dcb391cde5ca0298013d43336817d20b74650702"
}, },
{ {
"ImportPath": "k8s.io/apimachinery/pkg/runtime", "ImportPath": "k8s.io/apimachinery/pkg/runtime",
"Rev": "f951fa7b8c72cf726656de66d834a518359ce6e7" "Rev": "dcb391cde5ca0298013d43336817d20b74650702"
}, },
{ {
"ImportPath": "k8s.io/apimachinery/pkg/runtime/schema", "ImportPath": "k8s.io/apimachinery/pkg/runtime/schema",
"Rev": "f951fa7b8c72cf726656de66d834a518359ce6e7" "Rev": "dcb391cde5ca0298013d43336817d20b74650702"
}, },
{ {
"ImportPath": "k8s.io/apimachinery/pkg/runtime/serializer", "ImportPath": "k8s.io/apimachinery/pkg/runtime/serializer",
"Rev": "f951fa7b8c72cf726656de66d834a518359ce6e7" "Rev": "dcb391cde5ca0298013d43336817d20b74650702"
}, },
{ {
"ImportPath": "k8s.io/apimachinery/pkg/runtime/serializer/json", "ImportPath": "k8s.io/apimachinery/pkg/runtime/serializer/json",
"Rev": "f951fa7b8c72cf726656de66d834a518359ce6e7" "Rev": "dcb391cde5ca0298013d43336817d20b74650702"
}, },
{ {
"ImportPath": "k8s.io/apimachinery/pkg/runtime/serializer/protobuf", "ImportPath": "k8s.io/apimachinery/pkg/runtime/serializer/protobuf",
"Rev": "f951fa7b8c72cf726656de66d834a518359ce6e7" "Rev": "dcb391cde5ca0298013d43336817d20b74650702"
}, },
{ {
"ImportPath": "k8s.io/apimachinery/pkg/runtime/serializer/recognizer", "ImportPath": "k8s.io/apimachinery/pkg/runtime/serializer/recognizer",
"Rev": "f951fa7b8c72cf726656de66d834a518359ce6e7" "Rev": "dcb391cde5ca0298013d43336817d20b74650702"
}, },
{ {
"ImportPath": "k8s.io/apimachinery/pkg/runtime/serializer/streaming", "ImportPath": "k8s.io/apimachinery/pkg/runtime/serializer/streaming",
"Rev": "f951fa7b8c72cf726656de66d834a518359ce6e7" "Rev": "dcb391cde5ca0298013d43336817d20b74650702"
}, },
{ {
"ImportPath": "k8s.io/apimachinery/pkg/runtime/serializer/versioning", "ImportPath": "k8s.io/apimachinery/pkg/runtime/serializer/versioning",
"Rev": "f951fa7b8c72cf726656de66d834a518359ce6e7" "Rev": "dcb391cde5ca0298013d43336817d20b74650702"
}, },
{ {
"ImportPath": "k8s.io/apimachinery/pkg/selection", "ImportPath": "k8s.io/apimachinery/pkg/selection",
"Rev": "f951fa7b8c72cf726656de66d834a518359ce6e7" "Rev": "dcb391cde5ca0298013d43336817d20b74650702"
}, },
{ {
"ImportPath": "k8s.io/apimachinery/pkg/types", "ImportPath": "k8s.io/apimachinery/pkg/types",
"Rev": "f951fa7b8c72cf726656de66d834a518359ce6e7" "Rev": "dcb391cde5ca0298013d43336817d20b74650702"
}, },
{ {
"ImportPath": "k8s.io/apimachinery/pkg/util/cache", "ImportPath": "k8s.io/apimachinery/pkg/util/cache",
"Rev": "f951fa7b8c72cf726656de66d834a518359ce6e7" "Rev": "dcb391cde5ca0298013d43336817d20b74650702"
}, },
{ {
"ImportPath": "k8s.io/apimachinery/pkg/util/clock", "ImportPath": "k8s.io/apimachinery/pkg/util/clock",
"Rev": "f951fa7b8c72cf726656de66d834a518359ce6e7" "Rev": "dcb391cde5ca0298013d43336817d20b74650702"
}, },
{ {
"ImportPath": "k8s.io/apimachinery/pkg/util/diff", "ImportPath": "k8s.io/apimachinery/pkg/util/diff",
"Rev": "f951fa7b8c72cf726656de66d834a518359ce6e7" "Rev": "dcb391cde5ca0298013d43336817d20b74650702"
}, },
{ {
"ImportPath": "k8s.io/apimachinery/pkg/util/errors", "ImportPath": "k8s.io/apimachinery/pkg/util/errors",
"Rev": "f951fa7b8c72cf726656de66d834a518359ce6e7" "Rev": "dcb391cde5ca0298013d43336817d20b74650702"
}, },
{ {
"ImportPath": "k8s.io/apimachinery/pkg/util/framer", "ImportPath": "k8s.io/apimachinery/pkg/util/framer",
"Rev": "f951fa7b8c72cf726656de66d834a518359ce6e7" "Rev": "dcb391cde5ca0298013d43336817d20b74650702"
}, },
{ {
"ImportPath": "k8s.io/apimachinery/pkg/util/httpstream", "ImportPath": "k8s.io/apimachinery/pkg/util/httpstream",
"Rev": "f951fa7b8c72cf726656de66d834a518359ce6e7" "Rev": "dcb391cde5ca0298013d43336817d20b74650702"
}, },
{ {
"ImportPath": "k8s.io/apimachinery/pkg/util/httpstream/spdy", "ImportPath": "k8s.io/apimachinery/pkg/util/httpstream/spdy",
"Rev": "f951fa7b8c72cf726656de66d834a518359ce6e7" "Rev": "dcb391cde5ca0298013d43336817d20b74650702"
}, },
{ {
"ImportPath": "k8s.io/apimachinery/pkg/util/intstr", "ImportPath": "k8s.io/apimachinery/pkg/util/intstr",
"Rev": "f951fa7b8c72cf726656de66d834a518359ce6e7" "Rev": "dcb391cde5ca0298013d43336817d20b74650702"
}, },
{ {
"ImportPath": "k8s.io/apimachinery/pkg/util/json", "ImportPath": "k8s.io/apimachinery/pkg/util/json",
"Rev": "f951fa7b8c72cf726656de66d834a518359ce6e7" "Rev": "dcb391cde5ca0298013d43336817d20b74650702"
}, },
{ {
"ImportPath": "k8s.io/apimachinery/pkg/util/mergepatch", "ImportPath": "k8s.io/apimachinery/pkg/util/mergepatch",
"Rev": "f951fa7b8c72cf726656de66d834a518359ce6e7" "Rev": "dcb391cde5ca0298013d43336817d20b74650702"
}, },
{ {
"ImportPath": "k8s.io/apimachinery/pkg/util/naming", "ImportPath": "k8s.io/apimachinery/pkg/util/naming",
"Rev": "f951fa7b8c72cf726656de66d834a518359ce6e7" "Rev": "dcb391cde5ca0298013d43336817d20b74650702"
}, },
{ {
"ImportPath": "k8s.io/apimachinery/pkg/util/net", "ImportPath": "k8s.io/apimachinery/pkg/util/net",
"Rev": "f951fa7b8c72cf726656de66d834a518359ce6e7" "Rev": "dcb391cde5ca0298013d43336817d20b74650702"
}, },
{ {
"ImportPath": "k8s.io/apimachinery/pkg/util/remotecommand", "ImportPath": "k8s.io/apimachinery/pkg/util/remotecommand",
"Rev": "f951fa7b8c72cf726656de66d834a518359ce6e7" "Rev": "dcb391cde5ca0298013d43336817d20b74650702"
}, },
{ {
"ImportPath": "k8s.io/apimachinery/pkg/util/runtime", "ImportPath": "k8s.io/apimachinery/pkg/util/runtime",
"Rev": "f951fa7b8c72cf726656de66d834a518359ce6e7" "Rev": "dcb391cde5ca0298013d43336817d20b74650702"
}, },
{ {
"ImportPath": "k8s.io/apimachinery/pkg/util/sets", "ImportPath": "k8s.io/apimachinery/pkg/util/sets",
"Rev": "f951fa7b8c72cf726656de66d834a518359ce6e7" "Rev": "dcb391cde5ca0298013d43336817d20b74650702"
}, },
{ {
"ImportPath": "k8s.io/apimachinery/pkg/util/strategicpatch", "ImportPath": "k8s.io/apimachinery/pkg/util/strategicpatch",
"Rev": "f951fa7b8c72cf726656de66d834a518359ce6e7" "Rev": "dcb391cde5ca0298013d43336817d20b74650702"
}, },
{ {
"ImportPath": "k8s.io/apimachinery/pkg/util/validation", "ImportPath": "k8s.io/apimachinery/pkg/util/validation",
"Rev": "f951fa7b8c72cf726656de66d834a518359ce6e7" "Rev": "dcb391cde5ca0298013d43336817d20b74650702"
}, },
{ {
"ImportPath": "k8s.io/apimachinery/pkg/util/validation/field", "ImportPath": "k8s.io/apimachinery/pkg/util/validation/field",
"Rev": "f951fa7b8c72cf726656de66d834a518359ce6e7" "Rev": "dcb391cde5ca0298013d43336817d20b74650702"
}, },
{ {
"ImportPath": "k8s.io/apimachinery/pkg/util/wait", "ImportPath": "k8s.io/apimachinery/pkg/util/wait",
"Rev": "f951fa7b8c72cf726656de66d834a518359ce6e7" "Rev": "dcb391cde5ca0298013d43336817d20b74650702"
}, },
{ {
"ImportPath": "k8s.io/apimachinery/pkg/util/yaml", "ImportPath": "k8s.io/apimachinery/pkg/util/yaml",
"Rev": "f951fa7b8c72cf726656de66d834a518359ce6e7" "Rev": "dcb391cde5ca0298013d43336817d20b74650702"
}, },
{ {
"ImportPath": "k8s.io/apimachinery/pkg/version", "ImportPath": "k8s.io/apimachinery/pkg/version",
"Rev": "f951fa7b8c72cf726656de66d834a518359ce6e7" "Rev": "dcb391cde5ca0298013d43336817d20b74650702"
}, },
{ {
"ImportPath": "k8s.io/apimachinery/pkg/watch", "ImportPath": "k8s.io/apimachinery/pkg/watch",
"Rev": "f951fa7b8c72cf726656de66d834a518359ce6e7" "Rev": "dcb391cde5ca0298013d43336817d20b74650702"
}, },
{ {
"ImportPath": "k8s.io/apimachinery/third_party/forked/golang/json", "ImportPath": "k8s.io/apimachinery/third_party/forked/golang/json",
"Rev": "f951fa7b8c72cf726656de66d834a518359ce6e7" "Rev": "dcb391cde5ca0298013d43336817d20b74650702"
}, },
{ {
"ImportPath": "k8s.io/apimachinery/third_party/forked/golang/netutil", "ImportPath": "k8s.io/apimachinery/third_party/forked/golang/netutil",
"Rev": "f951fa7b8c72cf726656de66d834a518359ce6e7" "Rev": "dcb391cde5ca0298013d43336817d20b74650702"
}, },
{ {
"ImportPath": "k8s.io/apimachinery/third_party/forked/golang/reflect", "ImportPath": "k8s.io/apimachinery/third_party/forked/golang/reflect",
"Rev": "f951fa7b8c72cf726656de66d834a518359ce6e7" "Rev": "dcb391cde5ca0298013d43336817d20b74650702"
}, },
{ {
"ImportPath": "k8s.io/klog", "ImportPath": "k8s.io/klog",

View File

@ -205,8 +205,9 @@ func (pf *PortForwarder) forward() error {
var err error var err error
listenSuccess := false listenSuccess := false
for _, port := range pf.ports { for i := range pf.ports {
err = pf.listenOnPort(&port) port := &pf.ports[i]
err = pf.listenOnPort(port)
switch { switch {
case err == nil: case err == nil:
listenSuccess = true listenSuccess = true

View File

@ -18,11 +18,13 @@ package portforward
import ( import (
"net" "net"
"net/http"
"os" "os"
"reflect" "reflect"
"sort" "sort"
"strings" "strings"
"testing" "testing"
"time"
"k8s.io/apimachinery/pkg/util/httpstream" "k8s.io/apimachinery/pkg/util/httpstream"
) )
@ -39,6 +41,37 @@ func (d *fakeDialer) Dial(protocols ...string) (httpstream.Connection, string, e
return d.conn, d.negotiatedProtocol, d.err return d.conn, d.negotiatedProtocol, d.err
} }
type fakeConnection struct {
closed bool
closeChan chan bool
}
func newFakeConnection() httpstream.Connection {
return &fakeConnection{
closeChan: make(chan bool),
}
}
func (c *fakeConnection) CreateStream(headers http.Header) (httpstream.Stream, error) {
return nil, nil
}
func (c *fakeConnection) Close() error {
if !c.closed {
c.closed = true
close(c.closeChan)
}
return nil
}
func (c *fakeConnection) CloseChan() <-chan bool {
return c.closeChan
}
func (c *fakeConnection) SetIdleTimeout(timeout time.Duration) {
// no-op
}
func TestParsePortsAndNew(t *testing.T) { func TestParsePortsAndNew(t *testing.T) {
tests := []struct { tests := []struct {
input []string input []string
@ -310,3 +343,46 @@ func TestGetListener(t *testing.T) {
} }
} }
func TestGetPortsReturnsDynamicallyAssignedLocalPort(t *testing.T) {
dialer := &fakeDialer{
conn: newFakeConnection(),
}
stopChan := make(chan struct{})
readyChan := make(chan struct{})
errChan := make(chan error)
defer func() {
close(stopChan)
forwardErr := <-errChan
if forwardErr != nil {
t.Fatalf("ForwardPorts returned error: %s", forwardErr)
}
}()
pf, err := New(dialer, []string{":5000"}, stopChan, readyChan, os.Stdout, os.Stderr)
if err != nil {
t.Fatalf("error while calling New: %s", err)
}
go func() {
errChan <- pf.ForwardPorts()
close(errChan)
}()
<-pf.Ready
ports, err := pf.GetPorts()
if len(ports) != 1 {
t.Fatalf("expected 1 port, got %d", len(ports))
}
port := ports[0]
if port.Local == 0 {
t.Fatalf("local port is 0, expected != 0")
}
}