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 }