func TestServePortForwardIdleTimeout(t *testing.T) { fw := newServerTest() fw.fakeKubelet.streamingConnectionIdleTimeoutFunc = func() time.Duration { return 100 * time.Millisecond } podNamespace := "other" podName := "foo" url := fw.testHTTPServer.URL + "/portForward/" + podNamespace + "/" + podName upgradeRoundTripper := spdy.NewRoundTripper(nil) c := &http.Client{Transport: upgradeRoundTripper} resp, err := c.Post(url, "", nil) if err != nil { t.Fatalf("Got error POSTing: %v", err) } defer resp.Body.Close() conn, err := upgradeRoundTripper.NewConnection(resp) if err != nil { t.Fatalf("Unexpected error creating streaming connection: %s", err) } if conn == nil { t.Fatal("Unexpected nil connection") } defer conn.Close() <-conn.CloseChan() }
// maybeWrapForConnectionUpgrades wraps the roundtripper for upgrades. The bool indicates if it was wrapped func (r *proxyHandler) maybeWrapForConnectionUpgrades(rt http.RoundTripper, req *http.Request) (http.RoundTripper, bool, error) { connectionHeader := req.Header.Get("Connection") if len(connectionHeader) == 0 { return rt, false, nil } cfg := r.getRESTConfig() tlsConfig, err := restclient.TLSConfigFor(cfg) if err != nil { return nil, true, err } upgradeRoundTripper := spdy.NewRoundTripper(tlsConfig) wrappedRT, err := restclient.HTTPWrappersForConfig(cfg, upgradeRoundTripper) if err != nil { return nil, true, err } return wrappedRT, true, nil }
// NewExecutor connects to the provided server and upgrades the connection to // multiplexed bidirectional streams. The current implementation uses SPDY, // but this could be replaced with HTTP/2 once it's available, or something else. // TODO: the common code between this and portforward could be abstracted. func NewExecutor(config *restclient.Config, method string, url *url.URL) (StreamExecutor, error) { tlsConfig, err := restclient.TLSConfigFor(config) if err != nil { return nil, err } upgradeRoundTripper := spdy.NewRoundTripper(tlsConfig) wrapper, err := restclient.HTTPWrappersForConfig(config, upgradeRoundTripper) if err != nil { return nil, err } return &streamExecutor{ upgrader: upgradeRoundTripper, transport: wrapper, method: method, url: url, }, nil }
func (h *binaryInstantiateHandler) handle(r io.Reader) (runtime.Object, error) { h.options.Name = h.name if err := rest.BeforeCreate(BinaryStrategy, h.ctx, h.options); err != nil { glog.Infof("failed to validate binary: %#v", h.options) return nil, err } request := &buildapi.BuildRequest{} request.Name = h.name if len(h.options.Commit) > 0 { request.Revision = &buildapi.SourceRevision{ Git: &buildapi.GitSourceRevision{ Committer: buildapi.SourceControlUser{ Name: h.options.CommitterName, Email: h.options.CommitterEmail, }, Author: buildapi.SourceControlUser{ Name: h.options.AuthorName, Email: h.options.AuthorEmail, }, Message: h.options.Message, Commit: h.options.Commit, }, } } request.Binary = &buildapi.BinaryBuildSource{ AsFile: h.options.AsFile, } build, err := h.r.Generator.Instantiate(h.ctx, request) if err != nil { glog.Infof("failed to instantiate: %#v", request) return nil, err } latest, ok, err := registry.WaitForRunningBuild(h.r.Watcher, h.ctx, build, h.r.Timeout) if err != nil { switch latest.Status.Phase { case buildapi.BuildPhaseError: return nil, errors.NewBadRequest(fmt.Sprintf("build %s encountered an error: %s", build.Name, buildutil.NoBuildLogsMessage)) case buildapi.BuildPhaseCancelled: return nil, errors.NewBadRequest(fmt.Sprintf("build %s was cancelled: %s", build.Name, buildutil.NoBuildLogsMessage)) } return nil, errors.NewBadRequest(fmt.Sprintf("unable to wait for build %s to run: %v", build.Name, err)) } if !ok { return nil, errors.NewTimeoutError(fmt.Sprintf("timed out waiting for build %s to start after %s", build.Name, h.r.Timeout), 0) } if latest.Status.Phase != buildapi.BuildPhaseRunning { return nil, errors.NewBadRequest(fmt.Sprintf("build %s is no longer running, cannot upload file: %s", build.Name, build.Status.Phase)) } // The container should be the default build container, so setting it to blank buildPodName := buildutil.GetBuildPodName(build) opts := &kapi.PodAttachOptions{ Stdin: true, } location, transport, err := pod.AttachLocation(h.r.PodGetter, h.r.ConnectionInfo, h.ctx, buildPodName, opts) if err != nil { if errors.IsNotFound(err) { return nil, errors.NewNotFound(kapi.Resource("pod"), buildPodName) } return nil, errors.NewBadRequest(err.Error()) } rawTransport, ok := transport.(*http.Transport) if !ok { return nil, errors.NewInternalError(fmt.Errorf("unable to connect to node, unrecognized type: %v", reflect.TypeOf(transport))) } upgrader := spdy.NewRoundTripper(rawTransport.TLSClientConfig) exec, err := remotecommand.NewStreamExecutor(upgrader, nil, "POST", location) if err != nil { return nil, errors.NewInternalError(fmt.Errorf("unable to connect to server: %v", err)) } if err := exec.Stream(r, nil, nil, false); err != nil { return nil, errors.NewInternalError(err) } return latest, nil }
func TestServeExecInContainer(t *testing.T) { tests := []struct { stdin bool stdout bool stderr bool tty bool responseStatusCode int uid bool }{ {responseStatusCode: http.StatusBadRequest}, {stdin: true, responseStatusCode: http.StatusSwitchingProtocols}, {stdout: true, responseStatusCode: http.StatusSwitchingProtocols}, {stderr: true, responseStatusCode: http.StatusSwitchingProtocols}, {stdout: true, stderr: true, responseStatusCode: http.StatusSwitchingProtocols}, {stdout: true, stderr: true, tty: true, responseStatusCode: http.StatusSwitchingProtocols}, {stdin: true, stdout: true, stderr: true, responseStatusCode: http.StatusSwitchingProtocols}, } for i, test := range tests { fw := newServerTest() fw.fakeKubelet.streamingConnectionIdleTimeoutFunc = func() time.Duration { return 0 } podNamespace := "other" podName := "foo" expectedPodName := getPodName(podName, podNamespace) expectedUid := "9b01b80f-8fb4-11e4-95ab-4200af06647" expectedContainerName := "baz" expectedCommand := "ls -a" expectedStdin := "stdin" expectedStdout := "stdout" expectedStderr := "stderr" execFuncDone := make(chan struct{}) clientStdoutReadDone := make(chan struct{}) clientStderrReadDone := make(chan struct{}) fw.fakeKubelet.execFunc = func(podFullName string, uid types.UID, containerName string, cmd []string, in io.Reader, out, stderr io.WriteCloser, tty bool) error { defer close(execFuncDone) if podFullName != expectedPodName { t.Fatalf("%d: podFullName: expected %s, got %s", i, expectedPodName, podFullName) } if test.uid && string(uid) != expectedUid { t.Fatalf("%d: uid: expected %v, got %v", i, expectedUid, uid) } if containerName != expectedContainerName { t.Fatalf("%d: containerName: expected %s, got %s", i, expectedContainerName, containerName) } if strings.Join(cmd, " ") != expectedCommand { t.Fatalf("%d: cmd: expected: %s, got %v", i, expectedCommand, cmd) } if test.stdin { if in == nil { t.Fatalf("%d: stdin: expected non-nil", i) } b := make([]byte, 10) n, err := in.Read(b) if err != nil { t.Fatalf("%d: error reading from stdin: %v", i, err) } if e, a := expectedStdin, string(b[0:n]); e != a { t.Fatalf("%d: stdin: expected to read %v, got %v", i, e, a) } } else if in != nil { t.Fatalf("%d: stdin: expected nil: %#v", i, in) } if test.stdout { if out == nil { t.Fatalf("%d: stdout: expected non-nil", i) } _, err := out.Write([]byte(expectedStdout)) if err != nil { t.Fatalf("%d:, error writing to stdout: %v", i, err) } out.Close() <-clientStdoutReadDone } else if out != nil { t.Fatalf("%d: stdout: expected nil: %#v", i, out) } if tty { if stderr != nil { t.Fatalf("%d: tty set but received non-nil stderr: %v", i, stderr) } } else if test.stderr { if stderr == nil { t.Fatalf("%d: stderr: expected non-nil", i) } _, err := stderr.Write([]byte(expectedStderr)) if err != nil { t.Fatalf("%d:, error writing to stderr: %v", i, err) } stderr.Close() <-clientStderrReadDone } else if stderr != nil { t.Fatalf("%d: stderr: expected nil: %#v", i, stderr) } return nil } var url string if test.uid { url = fw.testHTTPServer.URL + "/exec/" + podNamespace + "/" + podName + "/" + expectedUid + "/" + expectedContainerName + "?command=ls&command=-a" } else { url = fw.testHTTPServer.URL + "/exec/" + podNamespace + "/" + podName + "/" + expectedContainerName + "?command=ls&command=-a" } if test.stdin { url += "&" + api.ExecStdinParam + "=1" } if test.stdout { url += "&" + api.ExecStdoutParam + "=1" } if test.stderr && !test.tty { url += "&" + api.ExecStderrParam + "=1" } if test.tty { url += "&" + api.ExecTTYParam + "=1" } var ( resp *http.Response err error upgradeRoundTripper httpstream.UpgradeRoundTripper c *http.Client ) if test.responseStatusCode != http.StatusSwitchingProtocols { c = &http.Client{} } else { upgradeRoundTripper = spdy.NewRoundTripper(nil) c = &http.Client{Transport: upgradeRoundTripper} } resp, err = c.Post(url, "", nil) if err != nil { t.Fatalf("%d: Got error POSTing: %v", i, err) } defer resp.Body.Close() _, err = ioutil.ReadAll(resp.Body) if err != nil { t.Errorf("%d: Error reading response body: %v", i, err) } if e, a := test.responseStatusCode, resp.StatusCode; e != a { t.Fatalf("%d: response status: expected %v, got %v", i, e, a) } if test.responseStatusCode != http.StatusSwitchingProtocols { continue } conn, err := upgradeRoundTripper.NewConnection(resp) if err != nil { t.Fatalf("Unexpected error creating streaming connection: %s", err) } if conn == nil { t.Fatalf("%d: unexpected nil conn", i) } defer conn.Close() h := http.Header{} h.Set(api.StreamType, api.StreamTypeError) errorStream, err := conn.CreateStream(h) if err != nil { t.Fatalf("%d: error creating error stream: %v", i, err) } defer errorStream.Reset() if test.stdin { h.Set(api.StreamType, api.StreamTypeStdin) stream, err := conn.CreateStream(h) if err != nil { t.Fatalf("%d: error creating stdin stream: %v", i, err) } defer stream.Reset() _, err = stream.Write([]byte(expectedStdin)) if err != nil { t.Fatalf("%d: error writing to stdin stream: %v", i, err) } } var stdoutStream httpstream.Stream if test.stdout { h.Set(api.StreamType, api.StreamTypeStdout) stdoutStream, err = conn.CreateStream(h) if err != nil { t.Fatalf("%d: error creating stdout stream: %v", i, err) } defer stdoutStream.Reset() } var stderrStream httpstream.Stream if test.stderr && !test.tty { h.Set(api.StreamType, api.StreamTypeStderr) stderrStream, err = conn.CreateStream(h) if err != nil { t.Fatalf("%d: error creating stderr stream: %v", i, err) } defer stderrStream.Reset() } if test.stdout { output := make([]byte, 10) n, err := stdoutStream.Read(output) close(clientStdoutReadDone) if err != nil { t.Fatalf("%d: error reading from stdout stream: %v", i, err) } if e, a := expectedStdout, string(output[0:n]); e != a { t.Fatalf("%d: stdout: expected '%v', got '%v'", i, e, a) } } if test.stderr && !test.tty { output := make([]byte, 10) n, err := stderrStream.Read(output) close(clientStderrReadDone) if err != nil { t.Fatalf("%d: error reading from stderr stream: %v", i, err) } if e, a := expectedStderr, string(output[0:n]); e != a { t.Fatalf("%d: stderr: expected '%v', got '%v'", i, e, a) } } <-execFuncDone } }
func TestServePortForward(t *testing.T) { tests := []struct { port string uid bool clientData string containerData string shouldError bool }{ {port: "", shouldError: true}, {port: "abc", shouldError: true}, {port: "-1", shouldError: true}, {port: "65536", shouldError: true}, {port: "0", shouldError: true}, {port: "1", shouldError: false}, {port: "8000", shouldError: false}, {port: "8000", clientData: "client data", containerData: "container data", shouldError: false}, {port: "65535", shouldError: false}, {port: "65535", uid: true, shouldError: false}, } podNamespace := "other" podName := "foo" expectedPodName := getPodName(podName, podNamespace) expectedUid := "9b01b80f-8fb4-11e4-95ab-4200af06647" for i, test := range tests { fw := newServerTest() fw.fakeKubelet.streamingConnectionIdleTimeoutFunc = func() time.Duration { return 0 } portForwardFuncDone := make(chan struct{}) fw.fakeKubelet.portForwardFunc = func(name string, uid types.UID, port uint16, stream io.ReadWriteCloser) error { defer close(portForwardFuncDone) if e, a := expectedPodName, name; e != a { t.Fatalf("%d: pod name: expected '%v', got '%v'", i, e, a) } if e, a := expectedUid, uid; test.uid && e != string(a) { t.Fatalf("%d: uid: expected '%v', got '%v'", i, e, a) } p, err := strconv.ParseUint(test.port, 10, 16) if err != nil { t.Fatalf("%d: error parsing port string '%s': %v", i, test.port, err) } if e, a := uint16(p), port; e != a { t.Fatalf("%d: port: expected '%v', got '%v'", i, e, a) } if test.clientData != "" { fromClient := make([]byte, 32) n, err := stream.Read(fromClient) if err != nil { t.Fatalf("%d: error reading client data: %v", i, err) } if e, a := test.clientData, string(fromClient[0:n]); e != a { t.Fatalf("%d: client data: expected to receive '%v', got '%v'", i, e, a) } } if test.containerData != "" { _, err := stream.Write([]byte(test.containerData)) if err != nil { t.Fatalf("%d: error writing container data: %v", i, err) } } return nil } var url string if test.uid { url = fmt.Sprintf("%s/portForward/%s/%s/%s", fw.testHTTPServer.URL, podNamespace, podName, expectedUid) } else { url = fmt.Sprintf("%s/portForward/%s/%s", fw.testHTTPServer.URL, podNamespace, podName) } upgradeRoundTripper := spdy.NewRoundTripper(nil) c := &http.Client{Transport: upgradeRoundTripper} resp, err := c.Post(url, "", nil) if err != nil { t.Fatalf("%d: Got error POSTing: %v", i, err) } defer resp.Body.Close() conn, err := upgradeRoundTripper.NewConnection(resp) if err != nil { t.Fatalf("Unexpected error creating streaming connection: %s", err) } if conn == nil { t.Fatalf("%d: Unexpected nil connection", i) } defer conn.Close() headers := http.Header{} headers.Set("streamType", "error") headers.Set("port", test.port) errorStream, err := conn.CreateStream(headers) _ = errorStream haveErr := err != nil if e, a := test.shouldError, haveErr; e != a { t.Fatalf("%d: create stream: expected err=%t, got %t: %v", i, e, a, err) } if test.shouldError { continue } headers.Set("streamType", "data") headers.Set("port", test.port) dataStream, err := conn.CreateStream(headers) haveErr = err != nil if e, a := test.shouldError, haveErr; e != a { t.Fatalf("%d: create stream: expected err=%t, got %t: %v", i, e, a, err) } if test.clientData != "" { _, err := dataStream.Write([]byte(test.clientData)) if err != nil { t.Fatalf("%d: unexpected error writing client data: %v", i, err) } } if test.containerData != "" { fromContainer := make([]byte, 32) n, err := dataStream.Read(fromContainer) if err != nil { t.Fatalf("%d: unexpected error reading container data: %v", i, err) } if e, a := test.containerData, string(fromContainer[0:n]); e != a { t.Fatalf("%d: expected to receive '%v' from container, got '%v'", i, e, a) } } <-portForwardFuncDone } }