github.com/rsampaio/docker@v0.7.2-0.20150827203920-fdc73cc3fc31/integration-cli/utils.go (about)

     1  package main
     2  
     3  import (
     4  	"archive/tar"
     5  	"bytes"
     6  	"encoding/json"
     7  	"errors"
     8  	"fmt"
     9  	"io"
    10  	"os"
    11  	"os/exec"
    12  	"path"
    13  	"reflect"
    14  	"strings"
    15  	"syscall"
    16  	"time"
    17  
    18  	"github.com/docker/docker/pkg/stringutils"
    19  )
    20  
    21  func getExitCode(err error) (int, error) {
    22  	exitCode := 0
    23  	if exiterr, ok := err.(*exec.ExitError); ok {
    24  		if procExit, ok := exiterr.Sys().(syscall.WaitStatus); ok {
    25  			return procExit.ExitStatus(), nil
    26  		}
    27  	}
    28  	return exitCode, fmt.Errorf("failed to get exit code")
    29  }
    30  
    31  func processExitCode(err error) (exitCode int) {
    32  	if err != nil {
    33  		var exiterr error
    34  		if exitCode, exiterr = getExitCode(err); exiterr != nil {
    35  			// TODO: Fix this so we check the error's text.
    36  			// we've failed to retrieve exit code, so we set it to 127
    37  			exitCode = 127
    38  		}
    39  	}
    40  	return
    41  }
    42  
    43  func isKilled(err error) bool {
    44  	if exitErr, ok := err.(*exec.ExitError); ok {
    45  		status, ok := exitErr.Sys().(syscall.WaitStatus)
    46  		if !ok {
    47  			return false
    48  		}
    49  		// status.ExitStatus() is required on Windows because it does not
    50  		// implement Signal() nor Signaled(). Just check it had a bad exit
    51  		// status could mean it was killed (and in tests we do kill)
    52  		return (status.Signaled() && status.Signal() == os.Kill) || status.ExitStatus() != 0
    53  	}
    54  	return false
    55  }
    56  
    57  func runCommandWithOutput(cmd *exec.Cmd) (output string, exitCode int, err error) {
    58  	exitCode = 0
    59  	out, err := cmd.CombinedOutput()
    60  	exitCode = processExitCode(err)
    61  	output = string(out)
    62  	return
    63  }
    64  
    65  func runCommandWithStdoutStderr(cmd *exec.Cmd) (stdout string, stderr string, exitCode int, err error) {
    66  	var (
    67  		stderrBuffer, stdoutBuffer bytes.Buffer
    68  	)
    69  	exitCode = 0
    70  	cmd.Stderr = &stderrBuffer
    71  	cmd.Stdout = &stdoutBuffer
    72  	err = cmd.Run()
    73  	exitCode = processExitCode(err)
    74  
    75  	stdout = stdoutBuffer.String()
    76  	stderr = stderrBuffer.String()
    77  	return
    78  }
    79  
    80  func runCommandWithOutputForDuration(cmd *exec.Cmd, duration time.Duration) (output string, exitCode int, timedOut bool, err error) {
    81  	var outputBuffer bytes.Buffer
    82  	if cmd.Stdout != nil {
    83  		err = errors.New("cmd.Stdout already set")
    84  		return
    85  	}
    86  	cmd.Stdout = &outputBuffer
    87  
    88  	if cmd.Stderr != nil {
    89  		err = errors.New("cmd.Stderr already set")
    90  		return
    91  	}
    92  	cmd.Stderr = &outputBuffer
    93  
    94  	done := make(chan error)
    95  	go func() {
    96  		exitErr := cmd.Run()
    97  		exitCode = processExitCode(exitErr)
    98  		done <- exitErr
    99  	}()
   100  
   101  	select {
   102  	case <-time.After(duration):
   103  		killErr := cmd.Process.Kill()
   104  		if killErr != nil {
   105  			fmt.Printf("failed to kill (pid=%d): %v\n", cmd.Process.Pid, killErr)
   106  		}
   107  		timedOut = true
   108  		break
   109  	case err = <-done:
   110  		break
   111  	}
   112  	output = outputBuffer.String()
   113  	return
   114  }
   115  
   116  var errCmdTimeout = fmt.Errorf("command timed out")
   117  
   118  func runCommandWithOutputAndTimeout(cmd *exec.Cmd, timeout time.Duration) (output string, exitCode int, err error) {
   119  	var timedOut bool
   120  	output, exitCode, timedOut, err = runCommandWithOutputForDuration(cmd, timeout)
   121  	if timedOut {
   122  		err = errCmdTimeout
   123  	}
   124  	return
   125  }
   126  
   127  func runCommand(cmd *exec.Cmd) (exitCode int, err error) {
   128  	exitCode = 0
   129  	err = cmd.Run()
   130  	exitCode = processExitCode(err)
   131  	return
   132  }
   133  
   134  func runCommandPipelineWithOutput(cmds ...*exec.Cmd) (output string, exitCode int, err error) {
   135  	if len(cmds) < 2 {
   136  		return "", 0, errors.New("pipeline does not have multiple cmds")
   137  	}
   138  
   139  	// connect stdin of each cmd to stdout pipe of previous cmd
   140  	for i, cmd := range cmds {
   141  		if i > 0 {
   142  			prevCmd := cmds[i-1]
   143  			cmd.Stdin, err = prevCmd.StdoutPipe()
   144  
   145  			if err != nil {
   146  				return "", 0, fmt.Errorf("cannot set stdout pipe for %s: %v", cmd.Path, err)
   147  			}
   148  		}
   149  	}
   150  
   151  	// start all cmds except the last
   152  	for _, cmd := range cmds[:len(cmds)-1] {
   153  		if err = cmd.Start(); err != nil {
   154  			return "", 0, fmt.Errorf("starting %s failed with error: %v", cmd.Path, err)
   155  		}
   156  	}
   157  
   158  	defer func() {
   159  		// wait all cmds except the last to release their resources
   160  		for _, cmd := range cmds[:len(cmds)-1] {
   161  			cmd.Wait()
   162  		}
   163  	}()
   164  
   165  	// wait on last cmd
   166  	return runCommandWithOutput(cmds[len(cmds)-1])
   167  }
   168  
   169  func unmarshalJSON(data []byte, result interface{}) error {
   170  	if err := json.Unmarshal(data, result); err != nil {
   171  		return err
   172  	}
   173  
   174  	return nil
   175  }
   176  
   177  func convertSliceOfStringsToMap(input []string) map[string]struct{} {
   178  	output := make(map[string]struct{})
   179  	for _, v := range input {
   180  		output[v] = struct{}{}
   181  	}
   182  	return output
   183  }
   184  
   185  func waitForContainer(contID string, args ...string) error {
   186  	args = append([]string{"run", "--name", contID}, args...)
   187  	cmd := exec.Command(dockerBinary, args...)
   188  	if _, err := runCommand(cmd); err != nil {
   189  		return err
   190  	}
   191  
   192  	if err := waitRun(contID); err != nil {
   193  		return err
   194  	}
   195  
   196  	return nil
   197  }
   198  
   199  // waitRun will wait for the specified container to be running, maximum 5 seconds.
   200  func waitRun(contID string) error {
   201  	return waitInspect(contID, "{{.State.Running}}", "true", 5)
   202  }
   203  
   204  // waitInspect will wait for the specified container to have the specified string
   205  // in the inspect output. It will wait until the specified timeout (in seconds)
   206  // is reached.
   207  func waitInspect(name, expr, expected string, timeout int) error {
   208  	after := time.After(time.Duration(timeout) * time.Second)
   209  
   210  	for {
   211  		cmd := exec.Command(dockerBinary, "inspect", "-f", expr, name)
   212  		out, _, err := runCommandWithOutput(cmd)
   213  		if err != nil {
   214  			if !strings.Contains(out, "No such") {
   215  				return fmt.Errorf("error executing docker inspect: %v\n%s", err, out)
   216  			}
   217  			select {
   218  			case <-after:
   219  				return err
   220  			default:
   221  				time.Sleep(10 * time.Millisecond)
   222  				continue
   223  			}
   224  		}
   225  
   226  		out = strings.TrimSpace(out)
   227  		if out == expected {
   228  			break
   229  		}
   230  
   231  		select {
   232  		case <-after:
   233  			return fmt.Errorf("condition \"%q == %q\" not true in time", out, expected)
   234  		default:
   235  		}
   236  
   237  		time.Sleep(100 * time.Millisecond)
   238  	}
   239  	return nil
   240  }
   241  
   242  func compareDirectoryEntries(e1 []os.FileInfo, e2 []os.FileInfo) error {
   243  	var (
   244  		e1Entries = make(map[string]struct{})
   245  		e2Entries = make(map[string]struct{})
   246  	)
   247  	for _, e := range e1 {
   248  		e1Entries[e.Name()] = struct{}{}
   249  	}
   250  	for _, e := range e2 {
   251  		e2Entries[e.Name()] = struct{}{}
   252  	}
   253  	if !reflect.DeepEqual(e1Entries, e2Entries) {
   254  		return fmt.Errorf("entries differ")
   255  	}
   256  	return nil
   257  }
   258  
   259  func listTar(f io.Reader) ([]string, error) {
   260  	tr := tar.NewReader(f)
   261  	var entries []string
   262  
   263  	for {
   264  		th, err := tr.Next()
   265  		if err == io.EOF {
   266  			// end of tar archive
   267  			return entries, nil
   268  		}
   269  		if err != nil {
   270  			return entries, err
   271  		}
   272  		entries = append(entries, th.Name)
   273  	}
   274  }
   275  
   276  // randomUnixTmpDirPath provides a temporary unix path with rand string appended.
   277  // does not create or checks if it exists.
   278  func randomUnixTmpDirPath(s string) string {
   279  	return path.Join("/tmp", fmt.Sprintf("%s.%s", s, stringutils.GenerateRandomAlphaOnlyString(10)))
   280  }
   281  
   282  // Reads chunkSize bytes from reader after every interval.
   283  // Returns total read bytes.
   284  func consumeWithSpeed(reader io.Reader, chunkSize int, interval time.Duration, stop chan bool) (n int, err error) {
   285  	buffer := make([]byte, chunkSize)
   286  	for {
   287  		select {
   288  		case <-stop:
   289  			return
   290  		default:
   291  			var readBytes int
   292  			readBytes, err = reader.Read(buffer)
   293  			n += readBytes
   294  			if err != nil {
   295  				if err == io.EOF {
   296  					err = nil
   297  				}
   298  				return
   299  			}
   300  			time.Sleep(interval)
   301  		}
   302  	}
   303  }
   304  
   305  // Parses 'procCgroupData', which is output of '/proc/<pid>/cgroup', and returns
   306  // a map which cgroup name as key and path as value.
   307  func parseCgroupPaths(procCgroupData string) map[string]string {
   308  	cgroupPaths := map[string]string{}
   309  	for _, line := range strings.Split(procCgroupData, "\n") {
   310  		parts := strings.Split(line, ":")
   311  		if len(parts) != 3 {
   312  			continue
   313  		}
   314  		cgroupPaths[parts[1]] = parts[2]
   315  	}
   316  	return cgroupPaths
   317  }
   318  
   319  type channelBuffer struct {
   320  	c chan []byte
   321  }
   322  
   323  func (c *channelBuffer) Write(b []byte) (int, error) {
   324  	c.c <- b
   325  	return len(b), nil
   326  }
   327  
   328  func (c *channelBuffer) Close() error {
   329  	close(c.c)
   330  	return nil
   331  }
   332  
   333  func (c *channelBuffer) ReadTimeout(p []byte, n time.Duration) (int, error) {
   334  	select {
   335  	case b := <-c.c:
   336  		return copy(p[0:], b), nil
   337  	case <-time.After(n):
   338  		return -1, fmt.Errorf("timeout reading from channel")
   339  	}
   340  }
   341  
   342  func runAtDifferentDate(date time.Time, block func()) {
   343  	// Layout for date. MMDDhhmmYYYY
   344  	const timeLayout = "010203042006"
   345  	// Ensure we bring time back to now
   346  	now := time.Now().Format(timeLayout)
   347  	dateReset := exec.Command("date", now)
   348  	defer runCommand(dateReset)
   349  
   350  	dateChange := exec.Command("date", date.Format(timeLayout))
   351  	runCommand(dateChange)
   352  	block()
   353  	return
   354  }