utils.go 8.3 KB

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