github.com/jiasir/docker@v1.3.3-0.20170609024000-252e610103e7/builder/remotecontext/lazycontext.go (about)

     1  package 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  // NewLazyContext 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 NewLazyContext(root string) (builder.Source, error) {
    19  	return &lazyContext{
    20  		root: root,
    21  		sums: make(map[string]string),
    22  	}, nil
    23  }
    24  
    25  type lazyContext struct {
    26  	root string
    27  	sums map[string]string
    28  }
    29  
    30  func (c *lazyContext) Root() string {
    31  	return c.root
    32  }
    33  
    34  func (c *lazyContext) Close() error {
    35  	return nil
    36  }
    37  
    38  func (c *lazyContext) Hash(path string) (string, error) {
    39  	cleanPath, fullPath, err := normalize(path, c.root)
    40  	if err != nil {
    41  		return "", err
    42  	}
    43  
    44  	fi, err := os.Lstat(fullPath)
    45  	if err != nil {
    46  		return "", err
    47  	}
    48  
    49  	relPath, err := Rel(c.root, fullPath)
    50  	if err != nil {
    51  		return "", errors.WithStack(convertPathError(err, cleanPath))
    52  	}
    53  
    54  	sum, ok := c.sums[relPath]
    55  	if !ok {
    56  		sum, err = c.prepareHash(relPath, fi)
    57  		if err != nil {
    58  			return "", err
    59  		}
    60  	}
    61  
    62  	return sum, nil
    63  }
    64  
    65  func (c *lazyContext) prepareHash(relPath string, fi os.FileInfo) (string, error) {
    66  	p := filepath.Join(c.root, relPath)
    67  	h, err := NewFileHash(p, relPath, fi)
    68  	if err != nil {
    69  		return "", errors.Wrapf(err, "failed to create hash for %s", relPath)
    70  	}
    71  	if fi.Mode().IsRegular() && fi.Size() > 0 {
    72  		f, err := os.Open(p)
    73  		if err != nil {
    74  			return "", errors.Wrapf(err, "failed to open %s", relPath)
    75  		}
    76  		defer f.Close()
    77  		if _, err := pools.Copy(h, f); err != nil {
    78  			return "", errors.Wrapf(err, "failed to copy file data for %s", relPath)
    79  		}
    80  	}
    81  	sum := hex.EncodeToString(h.Sum(nil))
    82  	c.sums[relPath] = sum
    83  	return sum, nil
    84  }
    85  
    86  // Rel makes a path relative to base path. Same as `filepath.Rel` but can also
    87  // handle UUID paths in windows.
    88  func Rel(basepath, targpath string) (string, error) {
    89  	// filepath.Rel can't handle UUID paths in windows
    90  	if runtime.GOOS == "windows" {
    91  		pfx := basepath + `\`
    92  		if strings.HasPrefix(targpath, pfx) {
    93  			p := strings.TrimPrefix(targpath, pfx)
    94  			if p == "" {
    95  				p = "."
    96  			}
    97  			return p, nil
    98  		}
    99  	}
   100  	return filepath.Rel(basepath, targpath)
   101  }