Beispiel #1
0
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)
}
Beispiel #2
0
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)
}
Beispiel #3
0
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")
	}
}
Beispiel #4
0
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)
}
Beispiel #5
0
// 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
	}
}
Beispiel #6
0
// 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
	}
}
Beispiel #7
0
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()
}
Beispiel #8
0
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()
}
Beispiel #9
0
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,
	})
}
Beispiel #10
0
// 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()
}
Beispiel #11
0
// 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
}
Beispiel #13
0
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)
}
Beispiel #14
0
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()
	}

}
Beispiel #15
0
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
}
Beispiel #16
0
// 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)
}