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