Skip to content

Commit 38a3252

Browse files
authored
Merge pull request kubernetes#73676 from martin-helmich/bugfix/expose-forwarded-local-port
client-go: Dynamically assigned local port number not retrievable when port-forwarding
2 parents 4505f12 + bbddd27 commit 38a3252

File tree

2 files changed

+79
-2
lines changed

2 files changed

+79
-2
lines changed

staging/src/k8s.io/client-go/tools/portforward/portforward.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -205,8 +205,9 @@ func (pf *PortForwarder) forward() error {
205205
var err error
206206

207207
listenSuccess := false
208-
for _, port := range pf.ports {
209-
err = pf.listenOnPort(&port)
208+
for i := range pf.ports {
209+
port := &pf.ports[i]
210+
err = pf.listenOnPort(port)
210211
switch {
211212
case err == nil:
212213
listenSuccess = true

staging/src/k8s.io/client-go/tools/portforward/portforward_test.go

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,13 @@ package portforward
1818

1919
import (
2020
"net"
21+
"net/http"
2122
"os"
2223
"reflect"
2324
"sort"
2425
"strings"
2526
"testing"
27+
"time"
2628

2729
"k8s.io/apimachinery/pkg/util/httpstream"
2830
)
@@ -39,6 +41,37 @@ func (d *fakeDialer) Dial(protocols ...string) (httpstream.Connection, string, e
3941
return d.conn, d.negotiatedProtocol, d.err
4042
}
4143

44+
type fakeConnection struct {
45+
closed bool
46+
closeChan chan bool
47+
}
48+
49+
func newFakeConnection() httpstream.Connection {
50+
return &fakeConnection{
51+
closeChan: make(chan bool),
52+
}
53+
}
54+
55+
func (c *fakeConnection) CreateStream(headers http.Header) (httpstream.Stream, error) {
56+
return nil, nil
57+
}
58+
59+
func (c *fakeConnection) Close() error {
60+
if !c.closed {
61+
c.closed = true
62+
close(c.closeChan)
63+
}
64+
return nil
65+
}
66+
67+
func (c *fakeConnection) CloseChan() <-chan bool {
68+
return c.closeChan
69+
}
70+
71+
func (c *fakeConnection) SetIdleTimeout(timeout time.Duration) {
72+
// no-op
73+
}
74+
4275
func TestParsePortsAndNew(t *testing.T) {
4376
tests := []struct {
4477
input []string
@@ -310,3 +343,46 @@ func TestGetListener(t *testing.T) {
310343

311344
}
312345
}
346+
347+
func TestGetPortsReturnsDynamicallyAssignedLocalPort(t *testing.T) {
348+
dialer := &fakeDialer{
349+
conn: newFakeConnection(),
350+
}
351+
352+
stopChan := make(chan struct{})
353+
readyChan := make(chan struct{})
354+
errChan := make(chan error)
355+
356+
defer func() {
357+
close(stopChan)
358+
359+
forwardErr := <-errChan
360+
if forwardErr != nil {
361+
t.Fatalf("ForwardPorts returned error: %s", forwardErr)
362+
}
363+
}()
364+
365+
pf, err := New(dialer, []string{":5000"}, stopChan, readyChan, os.Stdout, os.Stderr)
366+
367+
if err != nil {
368+
t.Fatalf("error while calling New: %s", err)
369+
}
370+
371+
go func() {
372+
errChan <- pf.ForwardPorts()
373+
close(errChan)
374+
}()
375+
376+
<-pf.Ready
377+
378+
ports, err := pf.GetPorts()
379+
380+
if len(ports) != 1 {
381+
t.Fatalf("expected 1 port, got %d", len(ports))
382+
}
383+
384+
port := ports[0]
385+
if port.Local == 0 {
386+
t.Fatalf("local port is 0, expected != 0")
387+
}
388+
}

0 commit comments

Comments
 (0)