github.com/wulonghui/docker@v1.8.0-rc2/utils/utils.go (about) 1 package utils 2 3 import ( 4 "bufio" 5 "crypto/sha1" 6 "encoding/hex" 7 "fmt" 8 "io" 9 "io/ioutil" 10 "os" 11 "os/exec" 12 "path/filepath" 13 "runtime" 14 "strings" 15 16 "github.com/docker/docker/autogen/dockerversion" 17 "github.com/docker/docker/pkg/archive" 18 "github.com/docker/docker/pkg/fileutils" 19 "github.com/docker/docker/pkg/stringid" 20 ) 21 22 // SelfPath figures out the absolute path of our own binary (if it's still around). 23 func SelfPath() string { 24 path, err := exec.LookPath(os.Args[0]) 25 if err != nil { 26 if os.IsNotExist(err) { 27 return "" 28 } 29 if execErr, ok := err.(*exec.Error); ok && os.IsNotExist(execErr.Err) { 30 return "" 31 } 32 panic(err) 33 } 34 path, err = filepath.Abs(path) 35 if err != nil { 36 if os.IsNotExist(err) { 37 return "" 38 } 39 panic(err) 40 } 41 return path 42 } 43 44 func dockerInitSha1(target string) string { 45 f, err := os.Open(target) 46 if err != nil { 47 return "" 48 } 49 defer f.Close() 50 h := sha1.New() 51 _, err = io.Copy(h, f) 52 if err != nil { 53 return "" 54 } 55 return hex.EncodeToString(h.Sum(nil)) 56 } 57 58 func isValidDockerInitPath(target string, selfPath string) bool { // target and selfPath should be absolute (InitPath and SelfPath already do this) 59 if target == "" { 60 return false 61 } 62 if dockerversion.IAMSTATIC == "true" { 63 if selfPath == "" { 64 return false 65 } 66 if target == selfPath { 67 return true 68 } 69 targetFileInfo, err := os.Lstat(target) 70 if err != nil { 71 return false 72 } 73 selfPathFileInfo, err := os.Lstat(selfPath) 74 if err != nil { 75 return false 76 } 77 return os.SameFile(targetFileInfo, selfPathFileInfo) 78 } 79 return dockerversion.INITSHA1 != "" && dockerInitSha1(target) == dockerversion.INITSHA1 80 } 81 82 // DockerInitPath figures out the path of our dockerinit (which may be SelfPath()) 83 func DockerInitPath(localCopy string) string { 84 selfPath := SelfPath() 85 if isValidDockerInitPath(selfPath, selfPath) { 86 // if we're valid, don't bother checking anything else 87 return selfPath 88 } 89 var possibleInits = []string{ 90 localCopy, 91 dockerversion.INITPATH, 92 filepath.Join(filepath.Dir(selfPath), "dockerinit"), 93 94 // FHS 3.0 Draft: "/usr/libexec includes internal binaries that are not intended to be executed directly by users or shell scripts. Applications may use a single subdirectory under /usr/libexec." 95 // https://www.linuxbase.org/betaspecs/fhs/fhs.html#usrlibexec 96 "/usr/libexec/docker/dockerinit", 97 "/usr/local/libexec/docker/dockerinit", 98 99 // FHS 2.3: "/usr/lib includes object files, libraries, and internal binaries that are not intended to be executed directly by users or shell scripts." 100 // https://refspecs.linuxfoundation.org/FHS_2.3/fhs-2.3.html#USRLIBLIBRARIESFORPROGRAMMINGANDPA 101 "/usr/lib/docker/dockerinit", 102 "/usr/local/lib/docker/dockerinit", 103 } 104 for _, dockerInit := range possibleInits { 105 if dockerInit == "" { 106 continue 107 } 108 path, err := exec.LookPath(dockerInit) 109 if err == nil { 110 path, err = filepath.Abs(path) 111 if err != nil { 112 // LookPath already validated that this file exists and is executable (following symlinks), so how could Abs fail? 113 panic(err) 114 } 115 if isValidDockerInitPath(path, selfPath) { 116 return path 117 } 118 } 119 } 120 return "" 121 } 122 123 var globalTestID string 124 125 // TestDirectory creates a new temporary directory and returns its path. 126 // The contents of directory at path `templateDir` is copied into the 127 // new directory. 128 func TestDirectory(templateDir string) (dir string, err error) { 129 if globalTestID == "" { 130 globalTestID = stringid.GenerateRandomID()[:4] 131 } 132 prefix := fmt.Sprintf("docker-test%s-%s-", globalTestID, GetCallerName(2)) 133 if prefix == "" { 134 prefix = "docker-test-" 135 } 136 dir, err = ioutil.TempDir("", prefix) 137 if err = os.Remove(dir); err != nil { 138 return 139 } 140 if templateDir != "" { 141 if err = archive.CopyWithTar(templateDir, dir); err != nil { 142 return 143 } 144 } 145 return 146 } 147 148 // GetCallerName introspects the call stack and returns the name of the 149 // function `depth` levels down in the stack. 150 func GetCallerName(depth int) string { 151 // Use the caller function name as a prefix. 152 // This helps trace temp directories back to their test. 153 pc, _, _, _ := runtime.Caller(depth + 1) 154 callerLongName := runtime.FuncForPC(pc).Name() 155 parts := strings.Split(callerLongName, ".") 156 callerShortName := parts[len(parts)-1] 157 return callerShortName 158 } 159 160 // ReplaceOrAppendEnvValues returns the defaults with the overrides either 161 // replaced by env key or appended to the list 162 func ReplaceOrAppendEnvValues(defaults, overrides []string) []string { 163 cache := make(map[string]int, len(defaults)) 164 for i, e := range defaults { 165 parts := strings.SplitN(e, "=", 2) 166 cache[parts[0]] = i 167 } 168 169 for _, value := range overrides { 170 // Values w/o = means they want this env to be removed/unset. 171 if !strings.Contains(value, "=") { 172 if i, exists := cache[value]; exists { 173 defaults[i] = "" // Used to indicate it should be removed 174 } 175 continue 176 } 177 178 // Just do a normal set/update 179 parts := strings.SplitN(value, "=", 2) 180 if i, exists := cache[parts[0]]; exists { 181 defaults[i] = value 182 } else { 183 defaults = append(defaults, value) 184 } 185 } 186 187 // Now remove all entries that we want to "unset" 188 for i := 0; i < len(defaults); i++ { 189 if defaults[i] == "" { 190 defaults = append(defaults[:i], defaults[i+1:]...) 191 i-- 192 } 193 } 194 195 return defaults 196 } 197 198 // ValidateContextDirectory checks if all the contents of the directory 199 // can be read and returns an error if some files can't be read 200 // symlinks which point to non-existing files don't trigger an error 201 func ValidateContextDirectory(srcPath string, excludes []string) error { 202 return filepath.Walk(filepath.Join(srcPath, "."), func(filePath string, f os.FileInfo, err error) error { 203 // skip this directory/file if it's not in the path, it won't get added to the context 204 if relFilePath, err := filepath.Rel(srcPath, filePath); err != nil { 205 return err 206 } else if skip, err := fileutils.Matches(relFilePath, excludes); err != nil { 207 return err 208 } else if skip { 209 if f.IsDir() { 210 return filepath.SkipDir 211 } 212 return nil 213 } 214 215 if err != nil { 216 if os.IsPermission(err) { 217 return fmt.Errorf("can't stat '%s'", filePath) 218 } 219 if os.IsNotExist(err) { 220 return nil 221 } 222 return err 223 } 224 225 // skip checking if symlinks point to non-existing files, such symlinks can be useful 226 // also skip named pipes, because they hanging on open 227 if f.Mode()&(os.ModeSymlink|os.ModeNamedPipe) != 0 { 228 return nil 229 } 230 231 if !f.IsDir() { 232 currentFile, err := os.Open(filePath) 233 if err != nil && os.IsPermission(err) { 234 return fmt.Errorf("no permission to read from '%s'", filePath) 235 } 236 currentFile.Close() 237 } 238 return nil 239 }) 240 } 241 242 // ReadDockerIgnore reads a .dockerignore file and returns the list of file patterns 243 // to ignore. Note this will trim whitespace from each line as well 244 // as use GO's "clean" func to get the shortest/cleanest path for each. 245 func ReadDockerIgnore(path string) ([]string, error) { 246 // Note that a missing .dockerignore file isn't treated as an error 247 reader, err := os.Open(path) 248 if err != nil { 249 if !os.IsNotExist(err) { 250 return nil, fmt.Errorf("Error reading '%s': %v", path, err) 251 } 252 return nil, nil 253 } 254 defer reader.Close() 255 256 scanner := bufio.NewScanner(reader) 257 var excludes []string 258 259 for scanner.Scan() { 260 pattern := strings.TrimSpace(scanner.Text()) 261 if pattern == "" { 262 continue 263 } 264 pattern = filepath.Clean(pattern) 265 excludes = append(excludes, pattern) 266 } 267 if err = scanner.Err(); err != nil { 268 return nil, fmt.Errorf("Error reading '%s': %v", path, err) 269 } 270 return excludes, nil 271 } 272 273 // ImageReference combines `repo` and `ref` and returns a string representing 274 // the combination. If `ref` is a digest (meaning it's of the form 275 // <algorithm>:<digest>, the returned string is <repo>@<ref>. Otherwise, 276 // ref is assumed to be a tag, and the returned string is <repo>:<tag>. 277 func ImageReference(repo, ref string) string { 278 if DigestReference(ref) { 279 return repo + "@" + ref 280 } 281 return repo + ":" + ref 282 } 283 284 // DigestReference returns true if ref is a digest reference; i.e. if it 285 // is of the form <algorithm>:<digest>. 286 func DigestReference(ref string) bool { 287 return strings.Contains(ref, ":") 288 }