utils.go 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153
  1. package testutil
  2. import (
  3. "errors"
  4. "fmt"
  5. "io"
  6. "io/ioutil"
  7. "os"
  8. "os/exec"
  9. "path/filepath"
  10. "strings"
  11. "time"
  12. "github.com/docker/docker/pkg/stringutils"
  13. "github.com/docker/docker/pkg/system"
  14. )
  15. func runCommandWithOutput(cmd *exec.Cmd) (output string, exitCode int, err error) {
  16. out, err := cmd.CombinedOutput()
  17. exitCode = system.ProcessExitCode(err)
  18. output = string(out)
  19. return
  20. }
  21. // RunCommandPipelineWithOutput runs the array of commands with the output
  22. // of each pipelined with the following (like cmd1 | cmd2 | cmd3 would do).
  23. // It returns the final output, the exitCode different from 0 and the error
  24. // if something bad happened.
  25. func RunCommandPipelineWithOutput(cmds ...*exec.Cmd) (output string, exitCode int, err error) {
  26. if len(cmds) < 2 {
  27. return "", 0, errors.New("pipeline does not have multiple cmds")
  28. }
  29. // connect stdin of each cmd to stdout pipe of previous cmd
  30. for i, cmd := range cmds {
  31. if i > 0 {
  32. prevCmd := cmds[i-1]
  33. cmd.Stdin, err = prevCmd.StdoutPipe()
  34. if err != nil {
  35. return "", 0, fmt.Errorf("cannot set stdout pipe for %s: %v", cmd.Path, err)
  36. }
  37. }
  38. }
  39. // start all cmds except the last
  40. for _, cmd := range cmds[:len(cmds)-1] {
  41. if err = cmd.Start(); err != nil {
  42. return "", 0, fmt.Errorf("starting %s failed with error: %v", cmd.Path, err)
  43. }
  44. }
  45. defer func() {
  46. var pipeErrMsgs []string
  47. // wait all cmds except the last to release their resources
  48. for _, cmd := range cmds[:len(cmds)-1] {
  49. if pipeErr := cmd.Wait(); pipeErr != nil {
  50. pipeErrMsgs = append(pipeErrMsgs, fmt.Sprintf("command %s failed with error: %v", cmd.Path, pipeErr))
  51. }
  52. }
  53. if len(pipeErrMsgs) > 0 && err == nil {
  54. err = fmt.Errorf("pipelineError from Wait: %v", strings.Join(pipeErrMsgs, ", "))
  55. }
  56. }()
  57. // wait on last cmd
  58. return runCommandWithOutput(cmds[len(cmds)-1])
  59. }
  60. // RandomTmpDirPath provides a temporary path with rand string appended.
  61. // does not create or checks if it exists.
  62. func RandomTmpDirPath(s string, platform string) string {
  63. tmp := "/tmp"
  64. if platform == "windows" {
  65. tmp = os.Getenv("TEMP")
  66. }
  67. path := filepath.Join(tmp, fmt.Sprintf("%s.%s", s, stringutils.GenerateRandomAlphaOnlyString(10)))
  68. if platform == "windows" {
  69. return filepath.FromSlash(path) // Using \
  70. }
  71. return filepath.ToSlash(path) // Using /
  72. }
  73. // ConsumeWithSpeed reads chunkSize bytes from reader before sleeping
  74. // for interval duration. Returns total read bytes. Send true to the
  75. // stop channel to return before reading to EOF on the reader.
  76. func ConsumeWithSpeed(reader io.Reader, chunkSize int, interval time.Duration, stop chan bool) (n int, err error) {
  77. buffer := make([]byte, chunkSize)
  78. for {
  79. var readBytes int
  80. readBytes, err = reader.Read(buffer)
  81. n += readBytes
  82. if err != nil {
  83. if err == io.EOF {
  84. err = nil
  85. }
  86. return
  87. }
  88. select {
  89. case <-stop:
  90. return
  91. case <-time.After(interval):
  92. }
  93. }
  94. }
  95. // ParseCgroupPaths parses 'procCgroupData', which is output of '/proc/<pid>/cgroup', and returns
  96. // a map which cgroup name as key and path as value.
  97. func ParseCgroupPaths(procCgroupData string) map[string]string {
  98. cgroupPaths := map[string]string{}
  99. for _, line := range strings.Split(procCgroupData, "\n") {
  100. parts := strings.Split(line, ":")
  101. if len(parts) != 3 {
  102. continue
  103. }
  104. cgroupPaths[parts[1]] = parts[2]
  105. }
  106. return cgroupPaths
  107. }
  108. // ChannelBuffer holds a chan of byte array that can be populate in a goroutine.
  109. type ChannelBuffer struct {
  110. C chan []byte
  111. }
  112. // Write implements Writer.
  113. func (c *ChannelBuffer) Write(b []byte) (int, error) {
  114. c.C <- b
  115. return len(b), nil
  116. }
  117. // Close closes the go channel.
  118. func (c *ChannelBuffer) Close() error {
  119. close(c.C)
  120. return nil
  121. }
  122. // ReadTimeout reads the content of the channel in the specified byte array with
  123. // the specified duration as timeout.
  124. func (c *ChannelBuffer) ReadTimeout(p []byte, n time.Duration) (int, error) {
  125. select {
  126. case b := <-c.C:
  127. return copy(p[0:], b), nil
  128. case <-time.After(n):
  129. return -1, fmt.Errorf("timeout reading from channel")
  130. }
  131. }
  132. // ReadBody read the specified ReadCloser content and returns it
  133. func ReadBody(b io.ReadCloser) ([]byte, error) {
  134. defer b.Close()
  135. return ioutil.ReadAll(b)
  136. }