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