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