lazycontext.go 2.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101
  1. package remotecontext
  2. import (
  3. "encoding/hex"
  4. "os"
  5. "path/filepath"
  6. "runtime"
  7. "strings"
  8. "github.com/docker/docker/builder"
  9. "github.com/docker/docker/pkg/pools"
  10. "github.com/pkg/errors"
  11. )
  12. // NewLazyContext creates a new LazyContext. LazyContext defines a hashed build
  13. // context based on a root directory. Individual files are hashed first time
  14. // they are asked. It is not safe to call methods of LazyContext concurrently.
  15. func NewLazyContext(root string) (builder.Source, error) {
  16. return &lazyContext{
  17. root: root,
  18. sums: make(map[string]string),
  19. }, nil
  20. }
  21. type lazyContext struct {
  22. root string
  23. sums map[string]string
  24. }
  25. func (c *lazyContext) Root() string {
  26. return c.root
  27. }
  28. func (c *lazyContext) Close() error {
  29. return nil
  30. }
  31. func (c *lazyContext) Hash(path string) (string, error) {
  32. cleanPath, fullPath, err := normalize(path, c.root)
  33. if err != nil {
  34. return "", err
  35. }
  36. fi, err := os.Lstat(fullPath)
  37. if err != nil {
  38. return "", err
  39. }
  40. relPath, err := Rel(c.root, fullPath)
  41. if err != nil {
  42. return "", errors.WithStack(convertPathError(err, cleanPath))
  43. }
  44. sum, ok := c.sums[relPath]
  45. if !ok {
  46. sum, err = c.prepareHash(relPath, fi)
  47. if err != nil {
  48. return "", err
  49. }
  50. }
  51. return sum, nil
  52. }
  53. func (c *lazyContext) prepareHash(relPath string, fi os.FileInfo) (string, error) {
  54. p := filepath.Join(c.root, relPath)
  55. h, err := NewFileHash(p, relPath, fi)
  56. if err != nil {
  57. return "", errors.Wrapf(err, "failed to create hash for %s", relPath)
  58. }
  59. if fi.Mode().IsRegular() && fi.Size() > 0 {
  60. f, err := os.Open(p)
  61. if err != nil {
  62. return "", errors.Wrapf(err, "failed to open %s", relPath)
  63. }
  64. defer f.Close()
  65. if _, err := pools.Copy(h, f); err != nil {
  66. return "", errors.Wrapf(err, "failed to copy file data for %s", relPath)
  67. }
  68. }
  69. sum := hex.EncodeToString(h.Sum(nil))
  70. c.sums[relPath] = sum
  71. return sum, nil
  72. }
  73. // Rel makes a path relative to base path. Same as `filepath.Rel` but can also
  74. // handle UUID paths in windows.
  75. func Rel(basepath, targpath string) (string, error) {
  76. // filepath.Rel can't handle UUID paths in windows
  77. if runtime.GOOS == "windows" {
  78. pfx := basepath + `\`
  79. if strings.HasPrefix(targpath, pfx) {
  80. p := strings.TrimPrefix(targpath, pfx)
  81. if p == "" {
  82. p = "."
  83. }
  84. return p, nil
  85. }
  86. }
  87. return filepath.Rel(basepath, targpath)
  88. }