utils.go 6.3 KB

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