github.com/jiasir/docker@v1.3.3-0.20170609024000-252e610103e7/pkg/testutil/utils.go (about)

     1  package testutil
     2  
     3  import (
     4  	"archive/tar"
     5  	"errors"
     6  	"fmt"
     7  	"io"
     8  	"io/ioutil"
     9  	"os"
    10  	"os/exec"
    11  	"path/filepath"
    12  	"reflect"
    13  	"strings"
    14  	"syscall"
    15  	"time"
    16  
    17  	"github.com/docker/docker/pkg/stringutils"
    18  	"github.com/docker/docker/pkg/system"
    19  )
    20  
    21  // IsKilled process the specified error and returns whether the process was killed or not.
    22  func IsKilled(err error) bool {
    23  	if exitErr, ok := err.(*exec.ExitError); ok {
    24  		status, ok := exitErr.Sys().(syscall.WaitStatus)
    25  		if !ok {
    26  			return false
    27  		}
    28  		// status.ExitStatus() is required on Windows because it does not
    29  		// implement Signal() nor Signaled(). Just check it had a bad exit
    30  		// status could mean it was killed (and in tests we do kill)
    31  		return (status.Signaled() && status.Signal() == os.Kill) || status.ExitStatus() != 0
    32  	}
    33  	return false
    34  }
    35  
    36  func runCommandWithOutput(cmd *exec.Cmd) (output string, exitCode int, err error) {
    37  	out, err := cmd.CombinedOutput()
    38  	exitCode = system.ProcessExitCode(err)
    39  	output = string(out)
    40  	return
    41  }
    42  
    43  // RunCommandPipelineWithOutput runs the array of commands with the output
    44  // of each pipelined with the following (like cmd1 | cmd2 | cmd3 would do).
    45  // It returns the final output, the exitCode different from 0 and the error
    46  // if something bad happened.
    47  func RunCommandPipelineWithOutput(cmds ...*exec.Cmd) (output string, exitCode int, err error) {
    48  	if len(cmds) < 2 {
    49  		return "", 0, errors.New("pipeline does not have multiple cmds")
    50  	}
    51  
    52  	// connect stdin of each cmd to stdout pipe of previous cmd
    53  	for i, cmd := range cmds {
    54  		if i > 0 {
    55  			prevCmd := cmds[i-1]
    56  			cmd.Stdin, err = prevCmd.StdoutPipe()
    57  
    58  			if err != nil {
    59  				return "", 0, fmt.Errorf("cannot set stdout pipe for %s: %v", cmd.Path, err)
    60  			}
    61  		}
    62  	}
    63  
    64  	// start all cmds except the last
    65  	for _, cmd := range cmds[:len(cmds)-1] {
    66  		if err = cmd.Start(); err != nil {
    67  			return "", 0, fmt.Errorf("starting %s failed with error: %v", cmd.Path, err)
    68  		}
    69  	}
    70  
    71  	defer func() {
    72  		var pipeErrMsgs []string
    73  		// wait all cmds except the last to release their resources
    74  		for _, cmd := range cmds[:len(cmds)-1] {
    75  			if pipeErr := cmd.Wait(); pipeErr != nil {
    76  				pipeErrMsgs = append(pipeErrMsgs, fmt.Sprintf("command %s failed with error: %v", cmd.Path, pipeErr))
    77  			}
    78  		}
    79  		if len(pipeErrMsgs) > 0 && err == nil {
    80  			err = fmt.Errorf("pipelineError from Wait: %v", strings.Join(pipeErrMsgs, ", "))
    81  		}
    82  	}()
    83  
    84  	// wait on last cmd
    85  	return runCommandWithOutput(cmds[len(cmds)-1])
    86  }
    87  
    88  // ConvertSliceOfStringsToMap converts a slices of string in a map
    89  // with the strings as key and an empty string as values.
    90  func ConvertSliceOfStringsToMap(input []string) map[string]struct{} {
    91  	output := make(map[string]struct{})
    92  	for _, v := range input {
    93  		output[v] = struct{}{}
    94  	}
    95  	return output
    96  }
    97  
    98  // CompareDirectoryEntries compares two sets of FileInfo (usually taken from a directory)
    99  // and returns an error if different.
   100  func CompareDirectoryEntries(e1 []os.FileInfo, e2 []os.FileInfo) error {
   101  	var (
   102  		e1Entries = make(map[string]struct{})
   103  		e2Entries = make(map[string]struct{})
   104  	)
   105  	for _, e := range e1 {
   106  		e1Entries[e.Name()] = struct{}{}
   107  	}
   108  	for _, e := range e2 {
   109  		e2Entries[e.Name()] = struct{}{}
   110  	}
   111  	if !reflect.DeepEqual(e1Entries, e2Entries) {
   112  		return fmt.Errorf("entries differ")
   113  	}
   114  	return nil
   115  }
   116  
   117  // ListTar lists the entries of a tar.
   118  func ListTar(f io.Reader) ([]string, error) {
   119  	tr := tar.NewReader(f)
   120  	var entries []string
   121  
   122  	for {
   123  		th, err := tr.Next()
   124  		if err == io.EOF {
   125  			// end of tar archive
   126  			return entries, nil
   127  		}
   128  		if err != nil {
   129  			return entries, err
   130  		}
   131  		entries = append(entries, th.Name)
   132  	}
   133  }
   134  
   135  // RandomTmpDirPath provides a temporary path with rand string appended.
   136  // does not create or checks if it exists.
   137  func RandomTmpDirPath(s string, platform string) string {
   138  	tmp := "/tmp"
   139  	if platform == "windows" {
   140  		tmp = os.Getenv("TEMP")
   141  	}
   142  	path := filepath.Join(tmp, fmt.Sprintf("%s.%s", s, stringutils.GenerateRandomAlphaOnlyString(10)))
   143  	if platform == "windows" {
   144  		return filepath.FromSlash(path) // Using \
   145  	}
   146  	return filepath.ToSlash(path) // Using /
   147  }
   148  
   149  // ConsumeWithSpeed reads chunkSize bytes from reader before sleeping
   150  // for interval duration. Returns total read bytes. Send true to the
   151  // stop channel to return before reading to EOF on the reader.
   152  func ConsumeWithSpeed(reader io.Reader, chunkSize int, interval time.Duration, stop chan bool) (n int, err error) {
   153  	buffer := make([]byte, chunkSize)
   154  	for {
   155  		var readBytes int
   156  		readBytes, err = reader.Read(buffer)
   157  		n += readBytes
   158  		if err != nil {
   159  			if err == io.EOF {
   160  				err = nil
   161  			}
   162  			return
   163  		}
   164  		select {
   165  		case <-stop:
   166  			return
   167  		case <-time.After(interval):
   168  		}
   169  	}
   170  }
   171  
   172  // ParseCgroupPaths parses 'procCgroupData', which is output of '/proc/<pid>/cgroup', and returns
   173  // a map which cgroup name as key and path as value.
   174  func ParseCgroupPaths(procCgroupData string) map[string]string {
   175  	cgroupPaths := map[string]string{}
   176  	for _, line := range strings.Split(procCgroupData, "\n") {
   177  		parts := strings.Split(line, ":")
   178  		if len(parts) != 3 {
   179  			continue
   180  		}
   181  		cgroupPaths[parts[1]] = parts[2]
   182  	}
   183  	return cgroupPaths
   184  }
   185  
   186  // ChannelBuffer holds a chan of byte array that can be populate in a goroutine.
   187  type ChannelBuffer struct {
   188  	C chan []byte
   189  }
   190  
   191  // Write implements Writer.
   192  func (c *ChannelBuffer) Write(b []byte) (int, error) {
   193  	c.C <- b
   194  	return len(b), nil
   195  }
   196  
   197  // Close closes the go channel.
   198  func (c *ChannelBuffer) Close() error {
   199  	close(c.C)
   200  	return nil
   201  }
   202  
   203  // ReadTimeout reads the content of the channel in the specified byte array with
   204  // the specified duration as timeout.
   205  func (c *ChannelBuffer) ReadTimeout(p []byte, n time.Duration) (int, error) {
   206  	select {
   207  	case b := <-c.C:
   208  		return copy(p[0:], b), nil
   209  	case <-time.After(n):
   210  		return -1, fmt.Errorf("timeout reading from channel")
   211  	}
   212  }
   213  
   214  // ReadBody read the specified ReadCloser content and returns it
   215  func ReadBody(b io.ReadCloser) ([]byte, error) {
   216  	defer b.Close()
   217  	return ioutil.ReadAll(b)
   218  }