utils.go 6.1 KB

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