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