github.com/vieux/docker@v0.6.3-0.20161004191708-e097c2a938c7/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 ) 19 20 // IsKilled process the specified error and returns whether the process was killed or not. 21 func IsKilled(err error) bool { 22 if exitErr, ok := err.(*exec.ExitError); ok { 23 status, ok := exitErr.Sys().(syscall.WaitStatus) 24 if !ok { 25 return false 26 } 27 // status.ExitStatus() is required on Windows because it does not 28 // implement Signal() nor Signaled(). Just check it had a bad exit 29 // status could mean it was killed (and in tests we do kill) 30 return (status.Signaled() && status.Signal() == os.Kill) || status.ExitStatus() != 0 31 } 32 return false 33 } 34 35 func runCommandWithOutput(cmd *exec.Cmd) (output string, exitCode int, err error) { 36 exitCode = 0 37 out, err := cmd.CombinedOutput() 38 exitCode = icmd.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 // RunAtDifferentDate runs the specified function with the given time. 215 // It changes the date of the system, which can led to weird behaviors. 216 func RunAtDifferentDate(date time.Time, block func()) { 217 // Layout for date. MMDDhhmmYYYY 218 const timeLayout = "010203042006" 219 // Ensure we bring time back to now 220 now := time.Now().Format(timeLayout) 221 defer icmd.RunCommand("date", now) 222 223 icmd.RunCommand("date", date.Format(timeLayout)) 224 block() 225 return 226 }