github.com/brahmaroutu/docker@v1.2.1-0.20160809185609-eb28dde01f16/api/client/utils.go (about)

     1  package client
     2  
     3  import (
     4  	"fmt"
     5  	"io"
     6  	"io/ioutil"
     7  	"os"
     8  	gosignal "os/signal"
     9  	"path/filepath"
    10  	"runtime"
    11  	"strings"
    12  	"time"
    13  
    14  	"golang.org/x/net/context"
    15  
    16  	"github.com/Sirupsen/logrus"
    17  	"github.com/docker/docker/pkg/signal"
    18  	"github.com/docker/docker/pkg/term"
    19  	"github.com/docker/engine-api/client"
    20  	"github.com/docker/engine-api/types"
    21  )
    22  
    23  func (cli *DockerCli) resizeTty(ctx context.Context, id string, isExec bool) {
    24  	height, width := cli.GetTtySize()
    25  	cli.ResizeTtyTo(ctx, id, height, width, isExec)
    26  }
    27  
    28  // ResizeTtyTo resizes tty to specific height and width
    29  // TODO: this can be unexported again once all container related commands move to package container
    30  func (cli *DockerCli) ResizeTtyTo(ctx context.Context, id string, height, width int, isExec bool) {
    31  	if height == 0 && width == 0 {
    32  		return
    33  	}
    34  
    35  	options := types.ResizeOptions{
    36  		Height: height,
    37  		Width:  width,
    38  	}
    39  
    40  	var err error
    41  	if isExec {
    42  		err = cli.client.ContainerExecResize(ctx, id, options)
    43  	} else {
    44  		err = cli.client.ContainerResize(ctx, id, options)
    45  	}
    46  
    47  	if err != nil {
    48  		logrus.Debugf("Error resize: %s", err)
    49  	}
    50  }
    51  
    52  // GetExecExitCode perform an inspect on the exec command. It returns
    53  // the running state and the exit code.
    54  func (cli *DockerCli) GetExecExitCode(ctx context.Context, execID string) (bool, int, error) {
    55  	resp, err := cli.client.ContainerExecInspect(ctx, execID)
    56  	if err != nil {
    57  		// If we can't connect, then the daemon probably died.
    58  		if err != client.ErrConnectionFailed {
    59  			return false, -1, err
    60  		}
    61  		return false, -1, nil
    62  	}
    63  
    64  	return resp.Running, resp.ExitCode, nil
    65  }
    66  
    67  // MonitorTtySize updates the container tty size when the terminal tty changes size
    68  func (cli *DockerCli) MonitorTtySize(ctx context.Context, id string, isExec bool) error {
    69  	cli.resizeTty(ctx, id, isExec)
    70  
    71  	if runtime.GOOS == "windows" {
    72  		go func() {
    73  			prevH, prevW := cli.GetTtySize()
    74  			for {
    75  				time.Sleep(time.Millisecond * 250)
    76  				h, w := cli.GetTtySize()
    77  
    78  				if prevW != w || prevH != h {
    79  					cli.resizeTty(ctx, id, isExec)
    80  				}
    81  				prevH = h
    82  				prevW = w
    83  			}
    84  		}()
    85  	} else {
    86  		sigchan := make(chan os.Signal, 1)
    87  		gosignal.Notify(sigchan, signal.SIGWINCH)
    88  		go func() {
    89  			for range sigchan {
    90  				cli.resizeTty(ctx, id, isExec)
    91  			}
    92  		}()
    93  	}
    94  	return nil
    95  }
    96  
    97  // GetTtySize returns the height and width in characters of the tty
    98  func (cli *DockerCli) GetTtySize() (int, int) {
    99  	if !cli.isTerminalOut {
   100  		return 0, 0
   101  	}
   102  	ws, err := term.GetWinsize(cli.outFd)
   103  	if err != nil {
   104  		logrus.Debugf("Error getting size: %s", err)
   105  		if ws == nil {
   106  			return 0, 0
   107  		}
   108  	}
   109  	return int(ws.Height), int(ws.Width)
   110  }
   111  
   112  // CopyToFile writes the content of the reader to the specified file
   113  func CopyToFile(outfile string, r io.Reader) error {
   114  	tmpFile, err := ioutil.TempFile(filepath.Dir(outfile), ".docker_temp_")
   115  	if err != nil {
   116  		return err
   117  	}
   118  
   119  	tmpPath := tmpFile.Name()
   120  
   121  	_, err = io.Copy(tmpFile, r)
   122  	tmpFile.Close()
   123  
   124  	if err != nil {
   125  		os.Remove(tmpPath)
   126  		return err
   127  	}
   128  
   129  	if err = os.Rename(tmpPath, outfile); err != nil {
   130  		os.Remove(tmpPath)
   131  		return err
   132  	}
   133  
   134  	return nil
   135  }
   136  
   137  // ForwardAllSignals forwards signals to the container
   138  // TODO: this can be unexported again once all container commands are under
   139  // api/client/container
   140  func (cli *DockerCli) ForwardAllSignals(ctx context.Context, cid string) chan os.Signal {
   141  	sigc := make(chan os.Signal, 128)
   142  	signal.CatchAll(sigc)
   143  	go func() {
   144  		for s := range sigc {
   145  			if s == signal.SIGCHLD || s == signal.SIGPIPE {
   146  				continue
   147  			}
   148  			var sig string
   149  			for sigStr, sigN := range signal.SignalMap {
   150  				if sigN == s {
   151  					sig = sigStr
   152  					break
   153  				}
   154  			}
   155  			if sig == "" {
   156  				fmt.Fprintf(cli.err, "Unsupported signal: %v. Discarding.\n", s)
   157  				continue
   158  			}
   159  
   160  			if err := cli.client.ContainerKill(ctx, cid, sig); err != nil {
   161  				logrus.Debugf("Error sending signal: %s", err)
   162  			}
   163  		}
   164  	}()
   165  	return sigc
   166  }
   167  
   168  // capitalizeFirst capitalizes the first character of string
   169  func capitalizeFirst(s string) string {
   170  	switch l := len(s); l {
   171  	case 0:
   172  		return s
   173  	case 1:
   174  		return strings.ToLower(s)
   175  	default:
   176  		return strings.ToUpper(string(s[0])) + strings.ToLower(s[1:])
   177  	}
   178  }
   179  
   180  // PrettyPrint outputs arbitrary data for human formatted output by uppercasing the first letter.
   181  func PrettyPrint(i interface{}) string {
   182  	switch t := i.(type) {
   183  	case nil:
   184  		return "None"
   185  	case string:
   186  		return capitalizeFirst(t)
   187  	default:
   188  		return capitalizeFirst(fmt.Sprintf("%s", t))
   189  	}
   190  }