github.com/docker/docker@v299999999.0.0-20200612211812-aaf470eca7b5+incompatible/builder/remotecontext/lazycontext.go (about)

     1  package remotecontext // import "github.com/docker/docker/builder/remotecontext"
     2  
     3  import (
     4  	"encoding/hex"
     5  	"os"
     6  	"strings"
     7  
     8  	"github.com/docker/docker/builder"
     9  	"github.com/docker/docker/pkg/containerfs"
    10  	"github.com/docker/docker/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  }