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