Skip to content
This repository has been archived by the owner on Mar 9, 2022. It is now read-only.

Commit

Permalink
Getting rid of socat
Browse files Browse the repository at this point in the history
Signed-off-by: abhi <[email protected]>
  • Loading branch information
abhi committed Mar 24, 2018
1 parent 616831f commit 65709e2
Showing 1 changed file with 22 additions and 40 deletions.
62 changes: 22 additions & 40 deletions pkg/server/sandbox_portforward.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,10 @@ limitations under the License.
package server

import (
"bytes"
"fmt"
"io"
"os/exec"
"strings"
"net"
"sync"

"github.com/containernetworking/plugins/pkg/ns"
"github.com/pkg/errors"
Expand All @@ -34,7 +33,6 @@ import (

// PortForward prepares a streaming endpoint to forward ports from a PodSandbox, and returns the address.
func (c *criService) PortForward(ctx context.Context, r *runtime.PortForwardRequest) (retRes *runtime.PortForwardResponse, retErr error) {
// TODO(random-liu): Run a socat container inside the sandbox to do portforward.
sandbox, err := c.sandboxStore.Get(r.GetPodSandboxId())
if err != nil {
return nil, errors.Wrapf(err, "failed to find sandbox %q", r.GetPodSandboxId())
Expand All @@ -46,9 +44,8 @@ func (c *criService) PortForward(ctx context.Context, r *runtime.PortForwardRequ
return c.streamServer.GetPortForward(r)
}

// portForward requires `socat` on the node, it uses netns to enter the
// sandbox namespace, and run `socat` inside the namespace to forward stream for a specific
// port. The `socat` command keeps running until it exits or client disconnect.
// portForward requires it uses netns to enter the sandbox namespace,
// and forward stream for a specific port.
func (c *criService) portForward(id string, port int32, stream io.ReadWriteCloser) error {
s, err := c.sandboxStore.Get(id)
if err != nil {
Expand All @@ -58,46 +55,31 @@ func (c *criService) portForward(id string, port int32, stream io.ReadWriteClose
return errors.Errorf("failed to find network namespace fo sandbox %q in store", id)
}

socat, err := exec.LookPath("socat")
if err != nil {
return errors.Wrap(err, "failed to find socat")
}

// Check following links for meaning of the options:
// * socat: https://linux.die.net/man/1/socat
args := []string{"-", fmt.Sprintf("TCP4:localhost:%d", port)}
logrus.Infof("Executing port forwarding command: %s %s", socat, strings.Join(args, " "))
err = s.NetNS.GetNs().Do(func(_ ns.NetNS) error {
cmd := exec.Command(socat, args...)
cmd.Stdout = stream

stderr := new(bytes.Buffer)
cmd.Stderr = stderr

// If we use Stdin, command.Run() won't return until the goroutine that's copying
// from stream finishes. Unfortunately, if you have a client like telnet connected
// via port forwarding, as long as the user's telnet client is connected to the user's
// local listener that port forwarding sets up, the telnet session never exits. This
// means that even if socat has finished running, command.Run() won't ever return
// (because the client still has the connection and stream open).
//
// The work around is to use StdinPipe(), as Wait() (called by Run()) closes the pipe
// when the command (socat) exits.
in, err := cmd.StdinPipe()
var wg sync.WaitGroup
client, err := net.Dial("tcp4", fmt.Sprintf("localhost:%d", port))
if err != nil {
return errors.Wrap(err, "failed to create stdin pipe")
return errors.Wrap(err, "failed to dial")
}
defer client.Close()

wg.Add(1)
go func() {
if _, err := io.Copy(in, stream); err != nil {
logrus.WithError(err).Errorf("Failed to copy port forward input for %q port %d", id, port)
if _, err := io.Copy(client, stream); err != nil {
logrus.WithError(err).Errorf("Failed to copy port forward input from %q port %d", id, port)
}
in.Close()
logrus.Debugf("Finish copy port forward input for %q port %d: %v", id, port)
wg.Done()
}()
wg.Add(1)
go func() {
if _, err := io.Copy(stream, client); err != nil {
logrus.WithError(err).Errorf("Failed to copy port forward output for %q port %d", id, port)
}
wg.Done()
}()
wg.Wait()
logrus.Infof("Finish copy port forward input for %q port %d: %v", id, port)

if err := cmd.Run(); err != nil {
return errors.Wrapf(err, "socat command returns error, stderr: %q", stderr.String())
}
return nil
})
if err != nil {
Expand Down

0 comments on commit 65709e2

Please sign in to comment.