github.com/getong/docker@v1.13.1/pkg/integration/utils.go (about)

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