github.com/eljojo/docker@v1.6.0-rc4/integration-cli/utils.go (about) 1 package main 2 3 import ( 4 "bytes" 5 "encoding/json" 6 "errors" 7 "fmt" 8 "io" 9 "math/rand" 10 "net/http" 11 "net/http/httptest" 12 "os" 13 "os/exec" 14 "path" 15 "reflect" 16 "strings" 17 "syscall" 18 "time" 19 20 "github.com/docker/docker/vendor/src/code.google.com/p/go/src/pkg/archive/tar" 21 ) 22 23 func getExitCode(err error) (int, error) { 24 exitCode := 0 25 if exiterr, ok := err.(*exec.ExitError); ok { 26 if procExit := exiterr.Sys().(syscall.WaitStatus); ok { 27 return procExit.ExitStatus(), nil 28 } 29 } 30 return exitCode, fmt.Errorf("failed to get exit code") 31 } 32 33 func processExitCode(err error) (exitCode int) { 34 if err != nil { 35 var exiterr error 36 if exitCode, exiterr = getExitCode(err); exiterr != nil { 37 // TODO: Fix this so we check the error's text. 38 // we've failed to retrieve exit code, so we set it to 127 39 exitCode = 127 40 } 41 } 42 return 43 } 44 45 func IsKilled(err error) bool { 46 if exitErr, ok := err.(*exec.ExitError); ok { 47 sys := exitErr.ProcessState.Sys() 48 status, ok := sys.(syscall.WaitStatus) 49 if !ok { 50 return false 51 } 52 return status.Signaled() && status.Signal() == os.Kill 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 if err != nil { 145 return "", 0, fmt.Errorf("cannot set stdout pipe for %s: %v", cmd.Path, err) 146 } 147 } 148 } 149 150 // start all cmds except the last 151 for _, cmd := range cmds[:len(cmds)-1] { 152 if err = cmd.Start(); err != nil { 153 return "", 0, fmt.Errorf("starting %s failed with error: %v", cmd.Path, err) 154 } 155 } 156 157 defer func() { 158 // wait all cmds except the last to release their resources 159 for _, cmd := range cmds[:len(cmds)-1] { 160 cmd.Wait() 161 } 162 }() 163 164 // wait on last cmd 165 return runCommandWithOutput(cmds[len(cmds)-1]) 166 } 167 168 func logDone(message string) { 169 fmt.Printf("[PASSED]: %s\n", message) 170 } 171 172 func stripTrailingCharacters(target string) string { 173 return strings.TrimSpace(target) 174 } 175 176 func unmarshalJSON(data []byte, result interface{}) error { 177 err := json.Unmarshal(data, result) 178 if err != nil { 179 return err 180 } 181 182 return nil 183 } 184 185 func convertSliceOfStringsToMap(input []string) map[string]struct{} { 186 output := make(map[string]struct{}) 187 for _, v := range input { 188 output[v] = struct{}{} 189 } 190 return output 191 } 192 193 func waitForContainer(contID string, args ...string) error { 194 args = append([]string{"run", "--name", contID}, args...) 195 cmd := exec.Command(dockerBinary, args...) 196 if _, err := runCommand(cmd); err != nil { 197 return err 198 } 199 200 if err := waitRun(contID); err != nil { 201 return err 202 } 203 204 return nil 205 } 206 207 func waitRun(contID string) error { 208 return waitInspect(contID, "{{.State.Running}}", "true", 5) 209 } 210 211 func waitInspect(name, expr, expected string, timeout int) error { 212 after := time.After(time.Duration(timeout) * time.Second) 213 214 for { 215 cmd := exec.Command(dockerBinary, "inspect", "-f", expr, name) 216 out, _, err := runCommandWithOutput(cmd) 217 if err != nil { 218 return fmt.Errorf("error executing docker inspect: %v", err) 219 } 220 221 out = strings.TrimSpace(out) 222 if out == expected { 223 break 224 } 225 226 select { 227 case <-after: 228 return fmt.Errorf("condition \"%q == %q\" not true in time", out, expected) 229 default: 230 } 231 232 time.Sleep(100 * time.Millisecond) 233 } 234 return nil 235 } 236 237 func compareDirectoryEntries(e1 []os.FileInfo, e2 []os.FileInfo) error { 238 var ( 239 e1Entries = make(map[string]struct{}) 240 e2Entries = make(map[string]struct{}) 241 ) 242 for _, e := range e1 { 243 e1Entries[e.Name()] = struct{}{} 244 } 245 for _, e := range e2 { 246 e2Entries[e.Name()] = struct{}{} 247 } 248 if !reflect.DeepEqual(e1Entries, e2Entries) { 249 return fmt.Errorf("entries differ") 250 } 251 return nil 252 } 253 254 func ListTar(f io.Reader) ([]string, error) { 255 tr := tar.NewReader(f) 256 var entries []string 257 258 for { 259 th, err := tr.Next() 260 if err == io.EOF { 261 // end of tar archive 262 return entries, nil 263 } 264 if err != nil { 265 return entries, err 266 } 267 entries = append(entries, th.Name) 268 } 269 } 270 271 type FileServer struct { 272 *httptest.Server 273 } 274 275 func fileServer(files map[string]string) (*FileServer, error) { 276 var handler http.HandlerFunc = func(w http.ResponseWriter, r *http.Request) { 277 if filePath, found := files[r.URL.Path]; found { 278 http.ServeFile(w, r, filePath) 279 } else { 280 http.Error(w, http.StatusText(404), 404) 281 } 282 } 283 284 for _, file := range files { 285 if _, err := os.Stat(file); err != nil { 286 return nil, err 287 } 288 } 289 server := httptest.NewServer(handler) 290 return &FileServer{ 291 Server: server, 292 }, nil 293 } 294 295 func copyWithCP(source, target string) error { 296 copyCmd := exec.Command("cp", "-rp", source, target) 297 out, exitCode, err := runCommandWithOutput(copyCmd) 298 if err != nil || exitCode != 0 { 299 return fmt.Errorf("failed to copy: error: %q ,output: %q", err, out) 300 } 301 return nil 302 } 303 304 func makeRandomString(n int) string { 305 // make a really long string 306 letters := []byte("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ") 307 b := make([]byte, n) 308 r := rand.New(rand.NewSource(time.Now().UTC().UnixNano())) 309 for i := range b { 310 b[i] = letters[r.Intn(len(letters))] 311 } 312 return string(b) 313 } 314 315 // randomUnixTmpDirPath provides a temporary unix path with rand string appended. 316 // does not create or checks if it exists. 317 func randomUnixTmpDirPath(s string) string { 318 return path.Join("/tmp", fmt.Sprintf("%s.%s", s, makeRandomString(10))) 319 } 320 321 // Reads chunkSize bytes from reader after every interval. 322 // Returns total read bytes. 323 func consumeWithSpeed(reader io.Reader, chunkSize int, interval time.Duration, stop chan bool) (n int, err error) { 324 buffer := make([]byte, chunkSize) 325 for { 326 select { 327 case <-stop: 328 return 329 default: 330 var readBytes int 331 readBytes, err = reader.Read(buffer) 332 n += readBytes 333 if err != nil { 334 if err == io.EOF { 335 err = nil 336 } 337 return 338 } 339 time.Sleep(interval) 340 } 341 } 342 } 343 344 // Parses 'procCgroupData', which is output of '/proc/<pid>/cgroup', and returns 345 // a map which cgroup name as key and path as value. 346 func parseCgroupPaths(procCgroupData string) map[string]string { 347 cgroupPaths := map[string]string{} 348 for _, line := range strings.Split(procCgroupData, "\n") { 349 parts := strings.Split(line, ":") 350 if len(parts) != 3 { 351 continue 352 } 353 cgroupPaths[parts[1]] = parts[2] 354 } 355 return cgroupPaths 356 }