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