github.com/wulonghui/docker@v1.8.0-rc2/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 func waitRun(contID string) error { 200 return waitInspect(contID, "{{.State.Running}}", "true", 5) 201 } 202 203 func waitInspect(name, expr, expected string, timeout int) error { 204 after := time.After(time.Duration(timeout) * time.Second) 205 206 for { 207 cmd := exec.Command(dockerBinary, "inspect", "-f", expr, name) 208 out, _, err := runCommandWithOutput(cmd) 209 if err != nil { 210 if !strings.Contains(out, "No such") { 211 return fmt.Errorf("error executing docker inspect: %v\n%s", err, out) 212 } 213 select { 214 case <-after: 215 return err 216 default: 217 time.Sleep(10 * time.Millisecond) 218 continue 219 } 220 } 221 222 out = strings.TrimSpace(out) 223 if out == expected { 224 break 225 } 226 227 select { 228 case <-after: 229 return fmt.Errorf("condition \"%q == %q\" not true in time", out, expected) 230 default: 231 } 232 233 time.Sleep(100 * time.Millisecond) 234 } 235 return nil 236 } 237 238 func compareDirectoryEntries(e1 []os.FileInfo, e2 []os.FileInfo) error { 239 var ( 240 e1Entries = make(map[string]struct{}) 241 e2Entries = make(map[string]struct{}) 242 ) 243 for _, e := range e1 { 244 e1Entries[e.Name()] = struct{}{} 245 } 246 for _, e := range e2 { 247 e2Entries[e.Name()] = struct{}{} 248 } 249 if !reflect.DeepEqual(e1Entries, e2Entries) { 250 return fmt.Errorf("entries differ") 251 } 252 return nil 253 } 254 255 func listTar(f io.Reader) ([]string, error) { 256 tr := tar.NewReader(f) 257 var entries []string 258 259 for { 260 th, err := tr.Next() 261 if err == io.EOF { 262 // end of tar archive 263 return entries, nil 264 } 265 if err != nil { 266 return entries, err 267 } 268 entries = append(entries, th.Name) 269 } 270 } 271 272 // randomUnixTmpDirPath provides a temporary unix path with rand string appended. 273 // does not create or checks if it exists. 274 func randomUnixTmpDirPath(s string) string { 275 return path.Join("/tmp", fmt.Sprintf("%s.%s", s, stringutils.GenerateRandomAlphaOnlyString(10))) 276 } 277 278 // Reads chunkSize bytes from reader after every interval. 279 // Returns total read bytes. 280 func consumeWithSpeed(reader io.Reader, chunkSize int, interval time.Duration, stop chan bool) (n int, err error) { 281 buffer := make([]byte, chunkSize) 282 for { 283 select { 284 case <-stop: 285 return 286 default: 287 var readBytes int 288 readBytes, err = reader.Read(buffer) 289 n += readBytes 290 if err != nil { 291 if err == io.EOF { 292 err = nil 293 } 294 return 295 } 296 time.Sleep(interval) 297 } 298 } 299 } 300 301 // Parses 'procCgroupData', which is output of '/proc/<pid>/cgroup', and returns 302 // a map which cgroup name as key and path as value. 303 func parseCgroupPaths(procCgroupData string) map[string]string { 304 cgroupPaths := map[string]string{} 305 for _, line := range strings.Split(procCgroupData, "\n") { 306 parts := strings.Split(line, ":") 307 if len(parts) != 3 { 308 continue 309 } 310 cgroupPaths[parts[1]] = parts[2] 311 } 312 return cgroupPaths 313 } 314 315 type channelBuffer struct { 316 c chan []byte 317 } 318 319 func (c *channelBuffer) Write(b []byte) (int, error) { 320 c.c <- b 321 return len(b), nil 322 } 323 324 func (c *channelBuffer) Close() error { 325 close(c.c) 326 return nil 327 } 328 329 func (c *channelBuffer) ReadTimeout(p []byte, n time.Duration) (int, error) { 330 select { 331 case b := <-c.c: 332 return copy(p[0:], b), nil 333 case <-time.After(n): 334 return -1, fmt.Errorf("timeout reading from channel") 335 } 336 } 337 338 func runAtDifferentDate(date time.Time, block func()) { 339 // Layout for date. MMDDhhmmYYYY 340 const timeLayout = "010203042006" 341 // Ensure we bring time back to now 342 now := time.Now().Format(timeLayout) 343 dateReset := exec.Command("date", now) 344 defer runCommand(dateReset) 345 346 dateChange := exec.Command("date", date.Format(timeLayout)) 347 runCommand(dateChange) 348 block() 349 return 350 }