123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687 |
- // +build !windows
- /*
- Copyright 2019 The Kubernetes Authors.
- 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.
- */
- package dockershim
- import (
- "bytes"
- "fmt"
- "io"
- "os/exec"
- "strings"
- "k8s.io/klog"
- )
- func (r *streamingRuntime) portForward(podSandboxID string, port int32, stream io.ReadWriteCloser) error {
- container, err := r.client.InspectContainer(podSandboxID)
- if err != nil {
- return err
- }
- if !container.State.Running {
- return fmt.Errorf("container not running (%s)", container.ID)
- }
- containerPid := container.State.Pid
- socatPath, lookupErr := exec.LookPath("socat")
- if lookupErr != nil {
- return fmt.Errorf("unable to do port forwarding: socat not found")
- }
- args := []string{"-t", fmt.Sprintf("%d", containerPid), "-n", socatPath, "-", fmt.Sprintf("TCP4:localhost:%d", port)}
- nsenterPath, lookupErr := exec.LookPath("nsenter")
- if lookupErr != nil {
- return fmt.Errorf("unable to do port forwarding: nsenter not found")
- }
- commandString := fmt.Sprintf("%s %s", nsenterPath, strings.Join(args, " "))
- klog.V(4).Infof("executing port forwarding command: %s", commandString)
- command := exec.Command(nsenterPath, args...)
- command.Stdout = stream
- stderr := new(bytes.Buffer)
- command.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.
- inPipe, err := command.StdinPipe()
- if err != nil {
- return fmt.Errorf("unable to do port forwarding: error creating stdin pipe: %v", err)
- }
- go func() {
- io.Copy(inPipe, stream)
- inPipe.Close()
- }()
- if err := command.Run(); err != nil {
- return fmt.Errorf("%v: %s", err, stderr.String())
- }
- return nil
- }
|