2015-01-08 20:41:38 +00:00
|
|
|
/*
|
2016-06-03 00:25:58 +00:00
|
|
|
Copyright 2015 The Kubernetes Authors.
|
2015-01-08 20:41:38 +00:00
|
|
|
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
you may not use this file except in compliance with the License.
|
|
|
|
You may obtain a copy of the License at
|
|
|
|
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
See the License for the specific language governing permissions and
|
|
|
|
limitations under the License.
|
|
|
|
*/
|
|
|
|
|
2017-01-27 15:28:10 +00:00
|
|
|
package tests
|
2015-01-08 20:41:38 +00:00
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
|
|
|
"fmt"
|
|
|
|
"io"
|
|
|
|
"net"
|
|
|
|
"net/http"
|
2015-09-22 20:29:51 +00:00
|
|
|
"net/http/httptest"
|
|
|
|
"net/url"
|
2015-11-09 21:22:42 +00:00
|
|
|
"os"
|
2015-08-14 05:34:26 +00:00
|
|
|
"strings"
|
2015-01-08 20:41:38 +00:00
|
|
|
"sync"
|
|
|
|
"testing"
|
|
|
|
"time"
|
|
|
|
|
2017-01-11 14:09:48 +00:00
|
|
|
"k8s.io/apimachinery/pkg/types"
|
2017-01-19 18:27:59 +00:00
|
|
|
restclient "k8s.io/client-go/rest"
|
2017-01-27 15:28:10 +00:00
|
|
|
. "k8s.io/client-go/tools/portforward"
|
2017-07-07 22:22:39 +00:00
|
|
|
"k8s.io/client-go/transport/spdy"
|
2016-10-25 17:54:45 +00:00
|
|
|
"k8s.io/kubernetes/pkg/kubelet/server/portforward"
|
2015-01-08 20:41:38 +00:00
|
|
|
)
|
|
|
|
|
2015-09-22 20:29:51 +00:00
|
|
|
// fakePortForwarder simulates port forwarding for testing. It implements
|
2016-10-25 17:54:45 +00:00
|
|
|
// portforward.PortForwarder.
|
2015-09-22 20:29:51 +00:00
|
|
|
type fakePortForwarder struct {
|
|
|
|
lock sync.Mutex
|
Fix TestPortForward flake
TestPortForward was failing occasionally due to the way the test was
written. It created a port forwarding session, then connected a client
to the local port, attempted to send some data, attempted to receive
some data, and then tore down the port forwarding session.
Unfortunately, some times the attempt to send data from the client to
the remote would be enqueued but not processed by the time the test tore
down everything. As a result, the data stream could get closed before
the client's data was transmitted to the server. If this happened, you'd
see an error such as 'forward 2 ports with bidirectional data: server
expected to receive "ghij", got "" for port 6000'.
This fixes the test by serializing the data flow: the client writes to
the remote, the remote waits to receive that data, the remote writes to
the client, and the client waits to receive the data from the remote.
This all takes place prior to the test tearing down port forwarding.
2015-11-24 20:38:11 +00:00
|
|
|
// stores data expected from the stream per port
|
2017-01-07 05:06:19 +00:00
|
|
|
expected map[int32]string
|
2015-09-22 20:29:51 +00:00
|
|
|
// stores data received from the stream per port
|
2017-01-07 05:06:19 +00:00
|
|
|
received map[int32]string
|
2015-09-22 20:29:51 +00:00
|
|
|
// data to be sent to the stream per port
|
2017-01-07 05:06:19 +00:00
|
|
|
send map[int32]string
|
2015-09-22 20:29:51 +00:00
|
|
|
}
|
|
|
|
|
2016-10-25 17:54:45 +00:00
|
|
|
var _ portforward.PortForwarder = &fakePortForwarder{}
|
2015-09-22 20:29:51 +00:00
|
|
|
|
2017-01-07 05:06:19 +00:00
|
|
|
func (pf *fakePortForwarder) PortForward(name string, uid types.UID, port int32, stream io.ReadWriteCloser) error {
|
2015-09-22 20:29:51 +00:00
|
|
|
defer stream.Close()
|
|
|
|
|
Fix TestPortForward flake
TestPortForward was failing occasionally due to the way the test was
written. It created a port forwarding session, then connected a client
to the local port, attempted to send some data, attempted to receive
some data, and then tore down the port forwarding session.
Unfortunately, some times the attempt to send data from the client to
the remote would be enqueued but not processed by the time the test tore
down everything. As a result, the data stream could get closed before
the client's data was transmitted to the server. If this happened, you'd
see an error such as 'forward 2 ports with bidirectional data: server
expected to receive "ghij", got "" for port 6000'.
This fixes the test by serializing the data flow: the client writes to
the remote, the remote waits to receive that data, the remote writes to
the client, and the client waits to receive the data from the remote.
This all takes place prior to the test tearing down port forwarding.
2015-11-24 20:38:11 +00:00
|
|
|
// read from the client
|
|
|
|
received := make([]byte, len(pf.expected[port]))
|
|
|
|
n, err := stream.Read(received)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("error reading from client for port %d: %v", port, err)
|
|
|
|
}
|
|
|
|
if n != len(pf.expected[port]) {
|
|
|
|
return fmt.Errorf("unexpected length read from client for port %d: got %d, expected %d. data=%q", port, n, len(pf.expected[port]), string(received))
|
|
|
|
}
|
2015-09-22 20:29:51 +00:00
|
|
|
|
Fix TestPortForward flake
TestPortForward was failing occasionally due to the way the test was
written. It created a port forwarding session, then connected a client
to the local port, attempted to send some data, attempted to receive
some data, and then tore down the port forwarding session.
Unfortunately, some times the attempt to send data from the client to
the remote would be enqueued but not processed by the time the test tore
down everything. As a result, the data stream could get closed before
the client's data was transmitted to the server. If this happened, you'd
see an error such as 'forward 2 ports with bidirectional data: server
expected to receive "ghij", got "" for port 6000'.
This fixes the test by serializing the data flow: the client writes to
the remote, the remote waits to receive that data, the remote writes to
the client, and the client waits to receive the data from the remote.
This all takes place prior to the test tearing down port forwarding.
2015-11-24 20:38:11 +00:00
|
|
|
// store the received content
|
|
|
|
pf.lock.Lock()
|
|
|
|
pf.received[port] = string(received)
|
|
|
|
pf.lock.Unlock()
|
2015-09-22 20:29:51 +00:00
|
|
|
|
Fix TestPortForward flake
TestPortForward was failing occasionally due to the way the test was
written. It created a port forwarding session, then connected a client
to the local port, attempted to send some data, attempted to receive
some data, and then tore down the port forwarding session.
Unfortunately, some times the attempt to send data from the client to
the remote would be enqueued but not processed by the time the test tore
down everything. As a result, the data stream could get closed before
the client's data was transmitted to the server. If this happened, you'd
see an error such as 'forward 2 ports with bidirectional data: server
expected to receive "ghij", got "" for port 6000'.
This fixes the test by serializing the data flow: the client writes to
the remote, the remote waits to receive that data, the remote writes to
the client, and the client waits to receive the data from the remote.
This all takes place prior to the test tearing down port forwarding.
2015-11-24 20:38:11 +00:00
|
|
|
// send the hardcoded data to the client
|
|
|
|
io.Copy(stream, strings.NewReader(pf.send[port]))
|
2015-09-22 20:29:51 +00:00
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// fakePortForwardServer creates an HTTP server that can handle port forwarding
|
|
|
|
// requests.
|
2017-01-07 05:06:19 +00:00
|
|
|
func fakePortForwardServer(t *testing.T, testName string, serverSends, expectedFromClient map[int32]string) http.HandlerFunc {
|
2015-09-22 20:29:51 +00:00
|
|
|
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
|
|
|
pf := &fakePortForwarder{
|
Fix TestPortForward flake
TestPortForward was failing occasionally due to the way the test was
written. It created a port forwarding session, then connected a client
to the local port, attempted to send some data, attempted to receive
some data, and then tore down the port forwarding session.
Unfortunately, some times the attempt to send data from the client to
the remote would be enqueued but not processed by the time the test tore
down everything. As a result, the data stream could get closed before
the client's data was transmitted to the server. If this happened, you'd
see an error such as 'forward 2 ports with bidirectional data: server
expected to receive "ghij", got "" for port 6000'.
This fixes the test by serializing the data flow: the client writes to
the remote, the remote waits to receive that data, the remote writes to
the client, and the client waits to receive the data from the remote.
This all takes place prior to the test tearing down port forwarding.
2015-11-24 20:38:11 +00:00
|
|
|
expected: expectedFromClient,
|
2017-01-07 05:06:19 +00:00
|
|
|
received: make(map[int32]string),
|
2015-09-22 20:29:51 +00:00
|
|
|
send: serverSends,
|
|
|
|
}
|
2017-01-07 05:06:19 +00:00
|
|
|
portforward.ServePortForward(w, req, pf, "pod", "uid", nil, 0, 10*time.Second, portforward.SupportedProtocols)
|
2015-09-22 20:29:51 +00:00
|
|
|
|
|
|
|
for port, expected := range expectedFromClient {
|
|
|
|
actual, ok := pf.received[port]
|
|
|
|
if !ok {
|
|
|
|
t.Errorf("%s: server didn't receive any data for port %d", testName, port)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
if expected != actual {
|
|
|
|
t.Errorf("%s: server expected to receive %q, got %q for port %d", testName, expected, actual, port)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for port, actual := range pf.received {
|
|
|
|
if _, ok := expectedFromClient[port]; !ok {
|
|
|
|
t.Errorf("%s: server unexpectedly received %q for port %d", testName, actual, port)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2015-01-08 20:41:38 +00:00
|
|
|
func TestForwardPorts(t *testing.T) {
|
2015-09-22 20:29:51 +00:00
|
|
|
tests := map[string]struct {
|
|
|
|
ports []string
|
2017-01-07 05:06:19 +00:00
|
|
|
clientSends map[int32]string
|
|
|
|
serverSends map[int32]string
|
2015-01-08 20:41:38 +00:00
|
|
|
}{
|
2015-09-22 20:29:51 +00:00
|
|
|
"forward 1 port with no data either direction": {
|
|
|
|
ports: []string{"5000"},
|
2015-01-08 20:41:38 +00:00
|
|
|
},
|
2015-09-22 20:29:51 +00:00
|
|
|
"forward 2 ports with bidirectional data": {
|
|
|
|
ports: []string{"5001", "6000"},
|
2017-01-07 05:06:19 +00:00
|
|
|
clientSends: map[int32]string{
|
2015-08-03 19:59:49 +00:00
|
|
|
5001: "abcd",
|
2015-01-08 20:41:38 +00:00
|
|
|
6000: "ghij",
|
|
|
|
},
|
2017-01-07 05:06:19 +00:00
|
|
|
serverSends: map[int32]string{
|
2015-08-03 19:59:49 +00:00
|
|
|
5001: "1234",
|
2015-01-08 20:41:38 +00:00
|
|
|
6000: "5678",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
2015-09-22 20:29:51 +00:00
|
|
|
for testName, test := range tests {
|
|
|
|
server := httptest.NewServer(fakePortForwardServer(t, testName, test.serverSends, test.clientSends))
|
2015-01-08 20:41:38 +00:00
|
|
|
|
2017-07-07 22:22:39 +00:00
|
|
|
transport, upgrader, err := spdy.RoundTripperFor(&restclient.Config{})
|
2015-09-27 00:00:39 +00:00
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
2015-01-08 20:41:38 +00:00
|
|
|
}
|
2017-07-07 22:22:39 +00:00
|
|
|
url, _ := url.Parse(server.URL)
|
|
|
|
dialer := spdy.NewDialer(upgrader, &http.Client{Transport: transport}, "POST", url)
|
2015-09-22 20:29:51 +00:00
|
|
|
|
|
|
|
stopChan := make(chan struct{}, 1)
|
2016-08-08 12:31:15 +00:00
|
|
|
readyChan := make(chan struct{})
|
2015-09-22 20:29:51 +00:00
|
|
|
|
2017-07-07 22:22:39 +00:00
|
|
|
pf, err := New(dialer, test.ports, stopChan, readyChan, os.Stdout, os.Stderr)
|
2015-09-22 20:29:51 +00:00
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("%s: unexpected error calling New: %v", testName, err)
|
2015-01-08 20:41:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
doneChan := make(chan error)
|
|
|
|
go func() {
|
|
|
|
doneChan <- pf.ForwardPorts()
|
|
|
|
}()
|
2015-02-20 21:29:42 +00:00
|
|
|
<-pf.Ready
|
2015-01-08 20:41:38 +00:00
|
|
|
|
2015-09-22 20:29:51 +00:00
|
|
|
for port, data := range test.clientSends {
|
2015-01-08 20:41:38 +00:00
|
|
|
clientConn, err := net.Dial("tcp", fmt.Sprintf("localhost:%d", port))
|
|
|
|
if err != nil {
|
2015-09-22 20:29:51 +00:00
|
|
|
t.Errorf("%s: error dialing %d: %s", testName, port, err)
|
2016-04-21 11:50:55 +00:00
|
|
|
server.Close()
|
2015-09-22 20:29:51 +00:00
|
|
|
continue
|
2015-01-08 20:41:38 +00:00
|
|
|
}
|
|
|
|
defer clientConn.Close()
|
|
|
|
|
|
|
|
n, err := clientConn.Write([]byte(data))
|
|
|
|
if err != nil && err != io.EOF {
|
2015-09-22 20:29:51 +00:00
|
|
|
t.Errorf("%s: Error sending data '%s': %s", testName, data, err)
|
2016-04-21 11:50:55 +00:00
|
|
|
server.Close()
|
2015-09-22 20:29:51 +00:00
|
|
|
continue
|
2015-01-08 20:41:38 +00:00
|
|
|
}
|
|
|
|
if n == 0 {
|
2015-09-22 20:29:51 +00:00
|
|
|
t.Errorf("%s: unexpected write of 0 bytes", testName)
|
2016-04-21 11:50:55 +00:00
|
|
|
server.Close()
|
2015-09-22 20:29:51 +00:00
|
|
|
continue
|
2015-01-08 20:41:38 +00:00
|
|
|
}
|
|
|
|
b := make([]byte, 4)
|
|
|
|
n, err = clientConn.Read(b)
|
|
|
|
if err != nil && err != io.EOF {
|
2015-09-22 20:29:51 +00:00
|
|
|
t.Errorf("%s: Error reading data: %s", testName, err)
|
2016-04-21 11:50:55 +00:00
|
|
|
server.Close()
|
2015-09-22 20:29:51 +00:00
|
|
|
continue
|
2015-01-08 20:41:38 +00:00
|
|
|
}
|
2015-09-22 20:29:51 +00:00
|
|
|
if !bytes.Equal([]byte(test.serverSends[port]), b) {
|
|
|
|
t.Errorf("%s: expected to read '%s', got '%s'", testName, test.serverSends[port], b)
|
2016-04-21 11:50:55 +00:00
|
|
|
server.Close()
|
2015-09-22 20:29:51 +00:00
|
|
|
continue
|
2015-01-08 20:41:38 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
// tell r.ForwardPorts to stop
|
|
|
|
close(stopChan)
|
|
|
|
|
|
|
|
// wait for r.ForwardPorts to actually return
|
2015-02-20 21:29:42 +00:00
|
|
|
err = <-doneChan
|
|
|
|
if err != nil {
|
2015-09-22 20:29:51 +00:00
|
|
|
t.Errorf("%s: unexpected error: %s", testName, err)
|
2015-01-08 20:41:38 +00:00
|
|
|
}
|
2016-04-21 11:50:55 +00:00
|
|
|
server.Close()
|
2015-01-08 20:41:38 +00:00
|
|
|
}
|
2015-04-21 18:15:54 +00:00
|
|
|
|
2015-01-08 20:41:38 +00:00
|
|
|
}
|
2015-08-03 19:59:49 +00:00
|
|
|
|
|
|
|
func TestForwardPortsReturnsErrorWhenAllBindsFailed(t *testing.T) {
|
2015-09-22 20:29:51 +00:00
|
|
|
server := httptest.NewServer(fakePortForwardServer(t, "allBindsFailed", nil, nil))
|
2016-04-21 11:50:55 +00:00
|
|
|
defer server.Close()
|
2015-09-22 20:29:51 +00:00
|
|
|
|
2017-07-07 22:22:39 +00:00
|
|
|
transport, upgrader, err := spdy.RoundTripperFor(&restclient.Config{})
|
2015-09-27 00:00:39 +00:00
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
2015-09-22 20:29:51 +00:00
|
|
|
}
|
2017-07-07 22:22:39 +00:00
|
|
|
url, _ := url.Parse(server.URL)
|
|
|
|
dialer := spdy.NewDialer(upgrader, &http.Client{Transport: transport}, "POST", url)
|
2015-09-22 20:29:51 +00:00
|
|
|
|
2015-08-03 19:59:49 +00:00
|
|
|
stopChan1 := make(chan struct{}, 1)
|
|
|
|
defer close(stopChan1)
|
2016-08-08 12:31:15 +00:00
|
|
|
readyChan1 := make(chan struct{})
|
2015-08-03 19:59:49 +00:00
|
|
|
|
2017-07-07 22:22:39 +00:00
|
|
|
pf1, err := New(dialer, []string{"5555"}, stopChan1, readyChan1, os.Stdout, os.Stderr)
|
2015-08-03 19:59:49 +00:00
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("error creating pf1: %v", err)
|
|
|
|
}
|
|
|
|
go pf1.ForwardPorts()
|
|
|
|
<-pf1.Ready
|
|
|
|
|
|
|
|
stopChan2 := make(chan struct{}, 1)
|
2016-08-08 12:31:15 +00:00
|
|
|
readyChan2 := make(chan struct{})
|
2017-07-07 22:22:39 +00:00
|
|
|
pf2, err := New(dialer, []string{"5555"}, stopChan2, readyChan2, os.Stdout, os.Stderr)
|
2015-08-03 19:59:49 +00:00
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("error creating pf2: %v", err)
|
|
|
|
}
|
|
|
|
if err := pf2.ForwardPorts(); err == nil {
|
|
|
|
t.Fatal("expected non-nil error for pf2.ForwardPorts")
|
|
|
|
}
|
|
|
|
}
|