func (*DefaultRemoteAttach) Attach(method string, url *url.URL, config *client.Config, stdin io.Reader, stdout, stderr io.Writer, tty bool) error { exec, err := remotecommand.NewExecutor(config, method, url) if err != nil { return err } return exec.Stream(stdin, stdout, stderr, tty) }
func execute(method string, url *url.URL, config *restclient.Config, stdin io.Reader, stdout, stderr io.Writer, tty bool) error { exec, err := remotecommand.NewExecutor(config, method, url) if err != nil { return err } return exec.Stream(remotecommandserver.SupportedStreamingProtocols, stdin, stdout, stderr, tty) }
func TestForwardPortsReturnsErrorWhenAllBindsFailed(t *testing.T) { server := httptest.NewServer(fakePortForwardServer(t, "allBindsFailed", nil, nil)) // TODO: Uncomment when fix #19254 // defer server.Close() url, _ := url.Parse(server.URL) exec, err := remotecommand.NewExecutor(&restclient.Config{}, "POST", url) if err != nil { t.Fatal(err) } stopChan1 := make(chan struct{}, 1) defer close(stopChan1) pf1, err := New(exec, []string{"5555"}, stopChan1) if err != nil { t.Fatalf("error creating pf1: %v", err) } go pf1.ForwardPorts() <-pf1.Ready stopChan2 := make(chan struct{}, 1) pf2, err := New(exec, []string{"5555"}, stopChan2) 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") } }
func TestServePortForward(t *testing.T) { s, testServer := startTestServer(t) defer testServer.Close() podSandboxID := testPodSandboxID resp, err := s.GetPortForward(&runtimeapi.PortForwardRequest{ PodSandboxId: &podSandboxID, }) require.NoError(t, err) reqURL, err := url.Parse(resp.GetUrl()) require.NoError(t, err) exec, err := remotecommand.NewExecutor(&restclient.Config{}, "POST", reqURL) require.NoError(t, err) streamConn, _, err := exec.Dial(kubeletportforward.PortForwardProtocolV1Name) require.NoError(t, err) defer streamConn.Close() // Create the streams. headers := http.Header{} // Error stream is required, but unused in this test. headers.Set(api.StreamType, api.StreamTypeError) headers.Set(api.PortHeader, strconv.Itoa(testPort)) _, err = streamConn.CreateStream(headers) require.NoError(t, err) // Setup the data stream. headers.Set(api.StreamType, api.StreamTypeData) headers.Set(api.PortHeader, strconv.Itoa(testPort)) stream, err := streamConn.CreateStream(headers) require.NoError(t, err) doClientStreams(t, "portforward", stream, stream, nil) }
// ForwardPorts will forward a set of ports from a pod, the stopChan will stop the forwarding // when it's closed or receives a struct{} func (f *portForwarder) ForwardPorts(ports []string, stopChan <-chan struct{}) error { req := f.Client.RESTClient.Post(). Resource("pods"). Namespace(f.Namespace). Name(f.PodName). SubResource("portforward") dialer, err := remotecommand.NewExecutor(f.Config, "POST", req.URL()) if err != nil { return err } // TODO: Make os.Stdout/Stderr configurable // TODO: Accept a ready channel? fw, err := portforward.New(dialer, ports, stopChan, nil, f.Out, f.ErrOut) if err != nil { return err } ready := make(chan struct{}) errChan := make(chan error) fw.Ready = ready go func() { errChan <- fw.ForwardPorts() }() select { case <-ready: return nil case err = <-errChan: return err } }
// ForwardPort opens a tunnel to a kubernetes pod func (c *Client) ForwardPort(namespace, podName string, remote int) (*Tunnel, error) { client, err := c.Client() if err != nil { return nil, err } config, err := c.ClientConfig() if err != nil { return nil, err } // Build a url to the portforward endpoing // example: http://localhost:8080/api/v1/namespaces/helm/pods/tiller-deploy-9itlq/portforward u := client.RESTClient.Post(). Resource("pods"). Namespace(namespace). Name(podName). SubResource("portforward").URL() dialer, err := remotecommand.NewExecutor(config, "POST", u) if err != nil { return nil, err } local, err := getAvailablePort() if err != nil { return nil, err } t := &Tunnel{ Local: local, Remote: remote, stopChan: make(chan struct{}, 1), } ports := []string{fmt.Sprintf("%d:%d", local, remote)} var b bytes.Buffer pf, err := portforward.New(dialer, ports, t.stopChan, &b, &b) if err != nil { return nil, err } errChan := make(chan error) go func() { errChan <- pf.ForwardPorts() }() select { case err = <-errChan: return t, fmt.Errorf("Error forwarding ports: %v\n", err) case <-pf.Ready: return t, nil } }
func (f *defaultPortForwarder) ForwardPorts(method string, url *url.URL, opts PortForwardOptions) error { dialer, err := remotecommand.NewExecutor(opts.Config, method, url) if err != nil { return err } fw, err := portforward.New(dialer, opts.Ports, opts.StopChannel, opts.ReadyChannel, f.cmdOut, f.cmdErr) if err != nil { return err } return fw.ForwardPorts() }
func (f *defaultPortForwarder) ForwardPorts(method string, url *url.URL, config *restclient.Config, ports []string, stopChan <-chan struct{}) error { dialer, err := remotecommand.NewExecutor(config, method, url) if err != nil { return err } fw, err := portforward.New(dialer, ports, stopChan, f.cmdOut, f.cmdErr) if err != nil { return err } return fw.ForwardPorts() }
func (*DefaultRemoteAttach) Attach(method string, url *url.URL, config *restclient.Config, stdin io.Reader, stdout, stderr io.Writer, tty bool, terminalSizeQueue term.TerminalSizeQueue) error { exec, err := remotecommand.NewExecutor(config, method, url) if err != nil { return err } return exec.Stream(remotecommand.StreamOptions{ SupportedProtocols: remotecommandserver.SupportedStreamingProtocols, Stdin: stdin, Stdout: stdout, Stderr: stderr, Tty: tty, TerminalSizeQueue: terminalSizeQueue, }) }
// Run the remote command test. // commandType is either "exec" or "attach". func runRemoteCommandTest(t *testing.T, commandType string) { rt := newFakeRuntime(t) s, err := NewServer(DefaultConfig, rt) require.NoError(t, err) testServer := httptest.NewServer(s) defer testServer.Close() testURL, err := url.Parse(testServer.URL) require.NoError(t, err) query := url.Values{} query.Add(urlParamStdin, "1") query.Add(urlParamStdout, "1") query.Add(urlParamStderr, "1") loc := &url.URL{ Scheme: testURL.Scheme, Host: testURL.Host, RawQuery: query.Encode(), } wg := sync.WaitGroup{} wg.Add(2) stdinR, stdinW := io.Pipe() stdoutR, stdoutW := io.Pipe() stderrR, stderrW := io.Pipe() go func() { defer wg.Done() loc.Path = fmt.Sprintf("/%s/%s", commandType, testContainerID) exec, err := remotecommand.NewExecutor(&restclient.Config{}, "POST", loc) require.NoError(t, err) opts := remotecommand.StreamOptions{ SupportedProtocols: kubeletremotecommand.SupportedStreamingProtocols, Stdin: stdinR, Stdout: stdoutW, Stderr: stderrW, Tty: false, TerminalSizeQueue: nil, } require.NoError(t, exec.Stream(opts)) }() go func() { defer wg.Done() doClientStreams(t, commandType, stdinW, stdoutR, stderrR) }() wg.Wait() }
// ForwardPort opens a tunnel to a kubernetes pod func (t *Tunnel) ForwardPort() error { // Build a url to the portforward endpoint // example: http://localhost:8080/api/v1/namespaces/helm/pods/tiller-deploy-9itlq/portforward u := t.client.Post(). Resource("pods"). Namespace(t.Namespace). Name(t.PodName). SubResource("portforward").URL() dialer, err := remotecommand.NewExecutor(t.config, "POST", u) if err != nil { return err } local, err := getAvailablePort() if err != nil { return fmt.Errorf("could not find an available port: %s", err) } t.Local = local ports := []string{fmt.Sprintf("%d:%d", t.Local, t.Remote)} pf, err := portforward.New(dialer, ports, t.stopChan, t.readyChan, t.Out, t.Out) if err != nil { return err } errChan := make(chan error) go func() { errChan <- pf.ForwardPorts() }() select { case err = <-errChan: return fmt.Errorf("forwarding ports: %v", err) case <-pf.Ready: return nil } }
// StartForwardingToPod starts forwarding requests to the given pod on the given target port // If no localPort has been defined on the tunnel, a random available port will be assigned // The tunnel is started in the background (using a goroutine), and will need to be stopped with Stop() // It returns an error if it can't start the tunnel. func (tunnel *Tunnel) StartForwardingToPod(podName string, namespace string, targetPort int, restClient *kclientapi.RESTClient, clientConfig *kclientapi.Config) error { req := restClient.Post(). Resource("pods"). Namespace(namespace). Name(podName). SubResource("portforward") if tunnel.LocalPort == 0 { port, err := getRandomAvailableLocalPort() if err != nil { return err } tunnel.LocalPort = port } port := fmt.Sprintf("%v:%v", tunnel.LocalPort, targetPort) ports := []string{port} dialer, err := remotecommand.NewExecutor(clientConfig, "POST", req.URL()) if err != nil { return err } fw, err := portforward.New(dialer, ports, tunnel.stopChan) if err != nil { return err } go func(localPort int) { err = fw.ForwardPorts() if err != nil { fmt.Printf("Failed to forward localPort %v to remotePort %v on pod %s: %v\n", localPort, targetPort, podName, err) } }(tunnel.LocalPort) return nil }
func TestServePortForward(t *testing.T) { rt := newFakeRuntime(t) s, err := NewServer(DefaultConfig, rt) require.NoError(t, err) testServer := httptest.NewServer(s) defer testServer.Close() testURL, err := url.Parse(testServer.URL) require.NoError(t, err) loc := &url.URL{ Scheme: testURL.Scheme, Host: testURL.Host, } loc.Path = fmt.Sprintf("/%s/%s", "portforward", testPodSandboxID) exec, err := remotecommand.NewExecutor(&restclient.Config{}, "POST", loc) require.NoError(t, err) streamConn, _, err := exec.Dial(kubeletportforward.PortForwardProtocolV1Name) require.NoError(t, err) defer streamConn.Close() // Create the streams. headers := http.Header{} // Error stream is required, but unused in this test. headers.Set(api.StreamType, api.StreamTypeError) headers.Set(api.PortHeader, strconv.Itoa(testPort)) _, err = streamConn.CreateStream(headers) require.NoError(t, err) // Setup the data stream. headers.Set(api.StreamType, api.StreamTypeData) headers.Set(api.PortHeader, strconv.Itoa(testPort)) stream, err := streamConn.CreateStream(headers) require.NoError(t, err) doClientStreams(t, "portforward", stream, stream, nil) }
func TestForwardPorts(t *testing.T) { tests := map[string]struct { ports []string clientSends map[uint16]string serverSends map[uint16]string }{ "forward 1 port with no data either direction": { ports: []string{"5000"}, }, "forward 2 ports with bidirectional data": { ports: []string{"5001", "6000"}, clientSends: map[uint16]string{ 5001: "abcd", 6000: "ghij", }, serverSends: map[uint16]string{ 5001: "1234", 6000: "5678", }, }, } for testName, test := range tests { server := httptest.NewServer(fakePortForwardServer(t, testName, test.serverSends, test.clientSends)) url, _ := url.Parse(server.URL) exec, err := remotecommand.NewExecutor(&restclient.Config{}, "POST", url) if err != nil { t.Fatal(err) } stopChan := make(chan struct{}, 1) pf, err := New(exec, test.ports, stopChan) if err != nil { t.Fatalf("%s: unexpected error calling New: %v", testName, err) } doneChan := make(chan error) go func() { doneChan <- pf.ForwardPorts() }() <-pf.Ready for port, data := range test.clientSends { clientConn, err := net.Dial("tcp", fmt.Sprintf("localhost:%d", port)) if err != nil { t.Errorf("%s: error dialing %d: %s", testName, port, err) // TODO: Uncomment when fix #19254 // server.Close() continue } defer clientConn.Close() n, err := clientConn.Write([]byte(data)) if err != nil && err != io.EOF { t.Errorf("%s: Error sending data '%s': %s", testName, data, err) // TODO: Uncomment when fix #19254 // server.Close() continue } if n == 0 { t.Errorf("%s: unexpected write of 0 bytes", testName) // TODO: Uncomment when fix #19254 // server.Close() continue } b := make([]byte, 4) n, err = clientConn.Read(b) if err != nil && err != io.EOF { t.Errorf("%s: Error reading data: %s", testName, err) // TODO: Uncomment when fix #19254 // server.Close() continue } if !bytes.Equal([]byte(test.serverSends[port]), b) { t.Errorf("%s: expected to read '%s', got '%s'", testName, test.serverSends[port], b) // TODO: Uncomment when fix #19254 // server.Close() continue } } // tell r.ForwardPorts to stop close(stopChan) // wait for r.ForwardPorts to actually return err = <-doneChan if err != nil { t.Errorf("%s: unexpected error: %s", testName, err) } // TODO: Uncomment when fix #19254 // server.Close() } }
func (k *KubeExecutor) ConnectAndExec(host, namespace, resource string, commands []string, timeoutMinutes int) ([]string, error) { // Used to return command output buffers := make([]string, len(commands)) // Create a Kube client configuration clientConfig := &restclient.Config{} clientConfig.Host = k.config.Host clientConfig.CertFile = k.config.CertFile clientConfig.Insecure = k.config.Insecure // Login if k.config.User != "" && k.config.Password != "" { token, err := tokenCreator(clientConfig, nil, k.config.User, k.config.Password) if err != nil { logger.Err(err) return nil, fmt.Errorf("User %v credentials not accepted", k.config.User) } clientConfig.BearerToken = token } // Get a client conn, err := client.New(clientConfig) if err != nil { logger.Err(err) return nil, fmt.Errorf("Unable to create a client connection") } // Get pod name var podName string if k.config.UsePodNames { podName = host } else { // 'host' is actually the value for the label with a key // of 'glusterid' selector, err := labels.Parse(KubeGlusterFSPodLabelKey + "==" + host) if err != nil { logger.Err(err) return nil, fmt.Errorf("Unable to get pod with a matching label of %v==%v", KubeGlusterFSPodLabelKey, host) } // Get a list of pods pods, err := conn.Pods(namespace).List(api.ListOptions{ LabelSelector: selector, FieldSelector: fields.Everything(), }) if err != nil { logger.Err(err) return nil, fmt.Errorf("Failed to get list of pods") } numPods := len(pods.Items) if numPods == 0 { // No pods found with that label err := fmt.Errorf("No pods with the label '%v=%v' were found", KubeGlusterFSPodLabelKey, host) logger.Critical(err.Error()) return nil, err } else if numPods > 1 { // There are more than one pod with the same label err := fmt.Errorf("Found %v pods with the sharing the same label '%v=%v'", numPods, KubeGlusterFSPodLabelKey, host) logger.Critical(err.Error()) return nil, err } // Get pod name podName = pods.Items[0].ObjectMeta.Name } for index, command := range commands { // Remove any whitespace command = strings.Trim(command, " ") // Determine if we should use sudo if k.config.Sudo { command = "sudo " + command } // Create REST command req := conn.RESTClient.Post(). Resource(resource). Name(podName). Namespace(namespace). SubResource("exec") req.VersionedParams(&api.PodExecOptions{ Command: []string{"/bin/bash", "-c", command}, Stdout: true, Stderr: true, }, api.ParameterCodec) // Create SPDY connection exec, err := remotecommand.NewExecutor(clientConfig, "POST", req.URL()) if err != nil { logger.Err(err) return nil, fmt.Errorf("Unable to setup a session with %v", podName) } // Create a buffer to trap session output var b bytes.Buffer var berr bytes.Buffer // Excute command err = exec.Stream(nil, &b, &berr, false) if err != nil { logger.LogError("Failed to run command [%v] on %v: Err[%v]: Stdout [%v]: Stderr [%v]", command, podName, err, b.String(), berr.String()) return nil, fmt.Errorf("Unable to execute command on %v: %v", podName, berr.String()) } logger.Debug("Host: %v Command: %v\nResult: %v", podName, command, b.String()) buffers[index] = b.String() } return buffers, nil }
// Run the remote command test. // commandType is either "exec" or "attach". func runRemoteCommandTest(t *testing.T, commandType string) { s, testServer := startTestServer(t) defer testServer.Close() var reqURL *url.URL stdin := true containerID := testContainerID switch commandType { case "exec": resp, err := s.GetExec(&runtimeapi.ExecRequest{ ContainerId: &containerID, Cmd: []string{"echo"}, Stdin: &stdin, }) require.NoError(t, err) reqURL, err = url.Parse(resp.GetUrl()) require.NoError(t, err) case "attach": resp, err := s.GetAttach(&runtimeapi.AttachRequest{ ContainerId: &containerID, Stdin: &stdin, }) require.NoError(t, err) reqURL, err = url.Parse(resp.GetUrl()) require.NoError(t, err) } wg := sync.WaitGroup{} wg.Add(2) stdinR, stdinW := io.Pipe() stdoutR, stdoutW := io.Pipe() stderrR, stderrW := io.Pipe() go func() { defer wg.Done() exec, err := remotecommand.NewExecutor(&restclient.Config{}, "POST", reqURL) require.NoError(t, err) opts := remotecommand.StreamOptions{ SupportedProtocols: kubeletremotecommand.SupportedStreamingProtocols, Stdin: stdinR, Stdout: stdoutW, Stderr: stderrW, Tty: false, TerminalSizeQueue: nil, } require.NoError(t, exec.Stream(opts)) }() go func() { defer wg.Done() doClientStreams(t, commandType, stdinW, stdoutR, stderrR) }() wg.Wait() // Repeat request with the same URL should be a 404. resp, err := http.Get(reqURL.String()) require.NoError(t, err) assert.Equal(t, http.StatusNotFound, resp.StatusCode) }