github.com/rsampaio/docker@v0.7.2-0.20150827203920-fdc73cc3fc31/integration-cli/utils.go (about) 1 package main 2 3 import ( 4 "archive/tar" 5 "bytes" 6 "encoding/json" 7 "errors" 8 "fmt" 9 "io" 10 "os" 11 "os/exec" 12 "path" 13 "reflect" 14 "strings" 15 "syscall" 16 "time" 17 18 "github.com/docker/docker/pkg/stringutils" 19 ) 20 21 func getExitCode(err error) (int, error) { 22 exitCode := 0 23 if exiterr, ok := err.(*exec.ExitError); ok { 24 if procExit, ok := exiterr.Sys().(syscall.WaitStatus); ok { 25 return procExit.ExitStatus(), nil 26 } 27 } 28 return exitCode, fmt.Errorf("failed to get exit code") 29 } 30 31 func processExitCode(err error) (exitCode int) { 32 if err != nil { 33 var exiterr error 34 if exitCode, exiterr = getExitCode(err); exiterr != nil { 35 // TODO: Fix this so we check the error's text. 36 // we've failed to retrieve exit code, so we set it to 127 37 exitCode = 127 38 } 39 } 40 return 41 } 42 43 func isKilled(err error) bool { 44 if exitErr, ok := err.(*exec.ExitError); ok { 45 status, ok := exitErr.Sys().(syscall.WaitStatus) 46 if !ok { 47 return false 48 } 49 // status.ExitStatus() is required on Windows because it does not 50 // implement Signal() nor Signaled(). Just check it had a bad exit 51 // status could mean it was killed (and in tests we do kill) 52 return (status.Signaled() && status.Signal() == os.Kill) || status.ExitStatus() != 0 53 } 54 return false 55 } 56 57 func runCommandWithOutput(cmd *exec.Cmd) (output string, exitCode int, err error) { 58 exitCode = 0 59 out, err := cmd.CombinedOutput() 60 exitCode = processExitCode(err) 61 output = string(out) 62 return 63 } 64 65 func runCommandWithStdoutStderr(cmd *exec.Cmd) (stdout string, stderr string, exitCode int, err error) { 66 var ( 67 stderrBuffer, stdoutBuffer bytes.Buffer 68 ) 69 exitCode = 0 70 cmd.Stderr = &stderrBuffer 71 cmd.Stdout = &stdoutBuffer 72 err = cmd.Run() 73 exitCode = processExitCode(err) 74 75 stdout = stdoutBuffer.String() 76 stderr = stderrBuffer.String() 77 return 78 } 79 80 func runCommandWithOutputForDuration(cmd *exec.Cmd, duration time.Duration) (output string, exitCode int, timedOut bool, err error) { 81 var outputBuffer bytes.Buffer 82 if cmd.Stdout != nil { 83 err = errors.New("cmd.Stdout already set") 84 return 85 } 86 cmd.Stdout = &outputBuffer 87 88 if cmd.Stderr != nil { 89 err = errors.New("cmd.Stderr already set") 90 return 91 } 92 cmd.Stderr = &outputBuffer 93 94 done := make(chan error) 95 go func() { 96 exitErr := cmd.Run() 97 exitCode = processExitCode(exitErr) 98 done <- exitErr 99 }() 100 101 select { 102 case <-time.After(duration): 103 killErr := cmd.Process.Kill() 104 if killErr != nil { 105 fmt.Printf("failed to kill (pid=%d): %v\n", cmd.Process.Pid, killErr) 106 } 107 timedOut = true 108 break 109 case err = <-done: 110 break 111 } 112 output = outputBuffer.String() 113 return 114 } 115 116 var errCmdTimeout = fmt.Errorf("command timed out") 117 118 func runCommandWithOutputAndTimeout(cmd *exec.Cmd, timeout time.Duration) (output string, exitCode int, err error) { 119 var timedOut bool 120 output, exitCode, timedOut, err = runCommandWithOutputForDuration(cmd, timeout) 121 if timedOut { 122 err = errCmdTimeout 123 } 124 return 125 } 126 127 func runCommand(cmd *exec.Cmd) (exitCode int, err error) { 128 exitCode = 0 129 err = cmd.Run() 130 exitCode = processExitCode(err) 131 return 132 } 133 134 func runCommandPipelineWithOutput(cmds ...*exec.Cmd) (output string, exitCode int, err error) { 135 if len(cmds) < 2 { 136 return "", 0, errors.New("pipeline does not have multiple cmds") 137 } 138 139 // connect stdin of each cmd to stdout pipe of previous cmd 140 for i, cmd := range cmds { 141 if i > 0 { 142 prevCmd := cmds[i-1] 143 cmd.Stdin, err = prevCmd.StdoutPipe() 144 145 if err != nil { 146 return "", 0, fmt.Errorf("cannot set stdout pipe for %s: %v", cmd.Path, err) 147 } 148 } 149 } 150 151 // start all cmds except the last 152 for _, cmd := range cmds[:len(cmds)-1] { 153 if err = cmd.Start(); err != nil { 154 return "", 0, fmt.Errorf("starting %s failed with error: %v", cmd.Path, err) 155 } 156 } 157 158 defer func() { 159 // wait all cmds except the last to release their resources 160 for _, cmd := range cmds[:len(cmds)-1] { 161 cmd.Wait() 162 } 163 }() 164 165 // wait on last cmd 166 return runCommandWithOutput(cmds[len(cmds)-1]) 167 } 168 169 func unmarshalJSON(data []byte, result interface{}) error { 170 if err := json.Unmarshal(data, result); err != nil { 171 return err 172 } 173 174 return nil 175 } 176 177 func convertSliceOfStringsToMap(input []string) map[string]struct{} { 178 output := make(map[string]struct{}) 179 for _, v := range input { 180 output[v] = struct{}{} 181 } 182 return output 183 } 184 185 func waitForContainer(contID string, args ...string) error { 186 args = append([]string{"run", "--name", contID}, args...) 187 cmd := exec.Command(dockerBinary, args...) 188 if _, err := runCommand(cmd); err != nil { 189 return err 190 } 191 192 if err := waitRun(contID); err != nil { 193 return err 194 } 195 196 return nil 197 } 198 199 // waitRun will wait for the specified container to be running, maximum 5 seconds. 200 func waitRun(contID string) error { 201 return waitInspect(contID, "{{.State.Running}}", "true", 5) 202 } 203 204 // waitInspect will wait for the specified container to have the specified string 205 // in the inspect output. It will wait until the specified timeout (in seconds) 206 // is reached. 207 func waitInspect(name, expr, expected string, timeout int) error { 208 after := time.After(time.Duration(timeout) * time.Second) 209 210 for { 211 cmd := exec.Command(dockerBinary, "inspect", "-f", expr, name) 212 out, _, err := runCommandWithOutput(cmd) 213 if err != nil { 214 if !strings.Contains(out, "No such") { 215 return fmt.Errorf("error executing docker inspect: %v\n%s", err, out) 216 } 217 select { 218 case <-after: 219 return err 220 default: 221 time.Sleep(10 * time.Millisecond) 222 continue 223 } 224 } 225 226 out = strings.TrimSpace(out) 227 if out == expected { 228 break 229 } 230 231 select { 232 case <-after: 233 return fmt.Errorf("condition \"%q == %q\" not true in time", out, expected) 234 default: 235 } 236 237 time.Sleep(100 * time.Millisecond) 238 } 239 return nil 240 } 241 242 func compareDirectoryEntries(e1 []os.FileInfo, e2 []os.FileInfo) error { 243 var ( 244 e1Entries = make(map[string]struct{}) 245 e2Entries = make(map[string]struct{}) 246 ) 247 for _, e := range e1 { 248 e1Entries[e.Name()] = struct{}{} 249 } 250 for _, e := range e2 { 251 e2Entries[e.Name()] = struct{}{} 252 } 253 if !reflect.DeepEqual(e1Entries, e2Entries) { 254 return fmt.Errorf("entries differ") 255 } 256 return nil 257 } 258 259 func listTar(f io.Reader) ([]string, error) { 260 tr := tar.NewReader(f) 261 var entries []string 262 263 for { 264 th, err := tr.Next() 265 if err == io.EOF { 266 // end of tar archive 267 return entries, nil 268 } 269 if err != nil { 270 return entries, err 271 } 272 entries = append(entries, th.Name) 273 } 274 } 275 276 // randomUnixTmpDirPath provides a temporary unix path with rand string appended. 277 // does not create or checks if it exists. 278 func randomUnixTmpDirPath(s string) string { 279 return path.Join("/tmp", fmt.Sprintf("%s.%s", s, stringutils.GenerateRandomAlphaOnlyString(10))) 280 } 281 282 // Reads chunkSize bytes from reader after every interval. 283 // Returns total read bytes. 284 func consumeWithSpeed(reader io.Reader, chunkSize int, interval time.Duration, stop chan bool) (n int, err error) { 285 buffer := make([]byte, chunkSize) 286 for { 287 select { 288 case <-stop: 289 return 290 default: 291 var readBytes int 292 readBytes, err = reader.Read(buffer) 293 n += readBytes 294 if err != nil { 295 if err == io.EOF { 296 err = nil 297 } 298 return 299 } 300 time.Sleep(interval) 301 } 302 } 303 } 304 305 // Parses 'procCgroupData', which is output of '/proc/<pid>/cgroup', and returns 306 // a map which cgroup name as key and path as value. 307 func parseCgroupPaths(procCgroupData string) map[string]string { 308 cgroupPaths := map[string]string{} 309 for _, line := range strings.Split(procCgroupData, "\n") { 310 parts := strings.Split(line, ":") 311 if len(parts) != 3 { 312 continue 313 } 314 cgroupPaths[parts[1]] = parts[2] 315 } 316 return cgroupPaths 317 } 318 319 type channelBuffer struct { 320 c chan []byte 321 } 322 323 func (c *channelBuffer) Write(b []byte) (int, error) { 324 c.c <- b 325 return len(b), nil 326 } 327 328 func (c *channelBuffer) Close() error { 329 close(c.c) 330 return nil 331 } 332 333 func (c *channelBuffer) ReadTimeout(p []byte, n time.Duration) (int, error) { 334 select { 335 case b := <-c.c: 336 return copy(p[0:], b), nil 337 case <-time.After(n): 338 return -1, fmt.Errorf("timeout reading from channel") 339 } 340 } 341 342 func runAtDifferentDate(date time.Time, block func()) { 343 // Layout for date. MMDDhhmmYYYY 344 const timeLayout = "010203042006" 345 // Ensure we bring time back to now 346 now := time.Now().Format(timeLayout) 347 dateReset := exec.Command("date", now) 348 defer runCommand(dateReset) 349 350 dateChange := exec.Command("date", date.Format(timeLayout)) 351 runCommand(dateChange) 352 block() 353 return 354 }