github.com/Heebron/moby@v0.0.0-20221111184709-6eab4f55faf7/builder/remotecontext/lazycontext.go (about)

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