utils.go 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218
  1. package testutil
  2. import (
  3. "archive/tar"
  4. "errors"
  5. "fmt"
  6. "io"
  7. "io/ioutil"
  8. "os"
  9. "os/exec"
  10. "path/filepath"
  11. "reflect"
  12. "strings"
  13. "syscall"
  14. "time"
  15. "github.com/docker/docker/pkg/stringutils"
  16. "github.com/docker/docker/pkg/system"
  17. )
  18. // IsKilled process the specified error and returns whether the process was killed or not.
  19. func IsKilled(err error) bool {
  20. if exitErr, ok := err.(*exec.ExitError); ok {
  21. status, ok := exitErr.Sys().(syscall.WaitStatus)
  22. if !ok {
  23. return false
  24. }
  25. // status.ExitStatus() is required on Windows because it does not
  26. // implement Signal() nor Signaled(). Just check it had a bad exit
  27. // status could mean it was killed (and in tests we do kill)
  28. return (status.Signaled() && status.Signal() == os.Kill) || status.ExitStatus() != 0
  29. }
  30. return false
  31. }
  32. func runCommandWithOutput(cmd *exec.Cmd) (output string, exitCode int, err error) {
  33. out, err := cmd.CombinedOutput()
  34. exitCode = system.ProcessExitCode(err)
  35. output = string(out)
  36. return
  37. }
  38. // RunCommandPipelineWithOutput runs the array of commands with the output
  39. // of each pipelined with the following (like cmd1 | cmd2 | cmd3 would do).
  40. // It returns the final output, the exitCode different from 0 and the error
  41. // if something bad happened.
  42. func RunCommandPipelineWithOutput(cmds ...*exec.Cmd) (output string, exitCode int, err error) {
  43. if len(cmds) < 2 {
  44. return "", 0, errors.New("pipeline does not have multiple cmds")
  45. }
  46. // connect stdin of each cmd to stdout pipe of previous cmd
  47. for i, cmd := range cmds {
  48. if i > 0 {
  49. prevCmd := cmds[i-1]
  50. cmd.Stdin, err = prevCmd.StdoutPipe()
  51. if err != nil {
  52. return "", 0, fmt.Errorf("cannot set stdout pipe for %s: %v", cmd.Path, err)
  53. }
  54. }
  55. }
  56. // start all cmds except the last
  57. for _, cmd := range cmds[:len(cmds)-1] {
  58. if err = cmd.Start(); err != nil {
  59. return "", 0, fmt.Errorf("starting %s failed with error: %v", cmd.Path, err)
  60. }
  61. }
  62. defer func() {
  63. var pipeErrMsgs []string
  64. // wait all cmds except the last to release their resources
  65. for _, cmd := range cmds[:len(cmds)-1] {
  66. if pipeErr := cmd.Wait(); pipeErr != nil {
  67. pipeErrMsgs = append(pipeErrMsgs, fmt.Sprintf("command %s failed with error: %v", cmd.Path, pipeErr))
  68. }
  69. }
  70. if len(pipeErrMsgs) > 0 && err == nil {
  71. err = fmt.Errorf("pipelineError from Wait: %v", strings.Join(pipeErrMsgs, ", "))
  72. }
  73. }()
  74. // wait on last cmd
  75. return runCommandWithOutput(cmds[len(cmds)-1])
  76. }
  77. // ConvertSliceOfStringsToMap converts a slices of string in a map
  78. // with the strings as key and an empty string as values.
  79. func ConvertSliceOfStringsToMap(input []string) map[string]struct{} {
  80. output := make(map[string]struct{})
  81. for _, v := range input {
  82. output[v] = struct{}{}
  83. }
  84. return output
  85. }
  86. // CompareDirectoryEntries compares two sets of FileInfo (usually taken from a directory)
  87. // and returns an error if different.
  88. func CompareDirectoryEntries(e1 []os.FileInfo, e2 []os.FileInfo) error {
  89. var (
  90. e1Entries = make(map[string]struct{})
  91. e2Entries = make(map[string]struct{})
  92. )
  93. for _, e := range e1 {
  94. e1Entries[e.Name()] = struct{}{}
  95. }
  96. for _, e := range e2 {
  97. e2Entries[e.Name()] = struct{}{}
  98. }
  99. if !reflect.DeepEqual(e1Entries, e2Entries) {
  100. return fmt.Errorf("entries differ")
  101. }
  102. return nil
  103. }
  104. // ListTar lists the entries of a tar.
  105. func ListTar(f io.Reader) ([]string, error) {
  106. tr := tar.NewReader(f)
  107. var entries []string
  108. for {
  109. th, err := tr.Next()
  110. if err == io.EOF {
  111. // end of tar archive
  112. return entries, nil
  113. }
  114. if err != nil {
  115. return entries, err
  116. }
  117. entries = append(entries, th.Name)
  118. }
  119. }
  120. // RandomTmpDirPath provides a temporary path with rand string appended.
  121. // does not create or checks if it exists.
  122. func RandomTmpDirPath(s string, platform string) string {
  123. tmp := "/tmp"
  124. if platform == "windows" {
  125. tmp = os.Getenv("TEMP")
  126. }
  127. path := filepath.Join(tmp, fmt.Sprintf("%s.%s", s, stringutils.GenerateRandomAlphaOnlyString(10)))
  128. if platform == "windows" {
  129. return filepath.FromSlash(path) // Using \
  130. }
  131. return filepath.ToSlash(path) // Using /
  132. }
  133. // ConsumeWithSpeed reads chunkSize bytes from reader before sleeping
  134. // for interval duration. Returns total read bytes. Send true to the
  135. // stop channel to return before reading to EOF on the reader.
  136. func ConsumeWithSpeed(reader io.Reader, chunkSize int, interval time.Duration, stop chan bool) (n int, err error) {
  137. buffer := make([]byte, chunkSize)
  138. for {
  139. var readBytes int
  140. readBytes, err = reader.Read(buffer)
  141. n += readBytes
  142. if err != nil {
  143. if err == io.EOF {
  144. err = nil
  145. }
  146. return
  147. }
  148. select {
  149. case <-stop:
  150. return
  151. case <-time.After(interval):
  152. }
  153. }
  154. }
  155. // ParseCgroupPaths parses 'procCgroupData', which is output of '/proc/<pid>/cgroup', and returns
  156. // a map which cgroup name as key and path as value.
  157. func ParseCgroupPaths(procCgroupData string) map[string]string {
  158. cgroupPaths := map[string]string{}
  159. for _, line := range strings.Split(procCgroupData, "\n") {
  160. parts := strings.Split(line, ":")
  161. if len(parts) != 3 {
  162. continue
  163. }
  164. cgroupPaths[parts[1]] = parts[2]
  165. }
  166. return cgroupPaths
  167. }
  168. // ChannelBuffer holds a chan of byte array that can be populate in a goroutine.
  169. type ChannelBuffer struct {
  170. C chan []byte
  171. }
  172. // Write implements Writer.
  173. func (c *ChannelBuffer) Write(b []byte) (int, error) {
  174. c.C <- b
  175. return len(b), nil
  176. }
  177. // Close closes the go channel.
  178. func (c *ChannelBuffer) Close() error {
  179. close(c.C)
  180. return nil
  181. }
  182. // ReadTimeout reads the content of the channel in the specified byte array with
  183. // the specified duration as timeout.
  184. func (c *ChannelBuffer) ReadTimeout(p []byte, n time.Duration) (int, error) {
  185. select {
  186. case b := <-c.C:
  187. return copy(p[0:], b), nil
  188. case <-time.After(n):
  189. return -1, fmt.Errorf("timeout reading from channel")
  190. }
  191. }
  192. // ReadBody read the specified ReadCloser content and returns it
  193. func ReadBody(b io.ReadCloser) ([]byte, error) {
  194. defer b.Close()
  195. return ioutil.ReadAll(b)
  196. }