c63ea32a17
This utility wasn't very related to all other utilities in pkg/ioutils. Moving it to longpath to also make it more clear what it does. It looks like there's only a single (public) external consumer of this utility, and only used in a test, and it's not 100% clear if it was intentional to use our package, of if it was a case of "I actually meant `io/ioutil.MkdirTemp`" so we could consider skipping the alias. While moving the package, I also renamed `TempDir` to `MkdirTemp`, which is the signature it matches in "os" from stdlib. Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
125 lines
3.2 KiB
Go
125 lines
3.2 KiB
Go
package remotecontext // import "github.com/docker/docker/builder/remotecontext"
|
|
|
|
import (
|
|
"io"
|
|
"os"
|
|
"path/filepath"
|
|
|
|
"github.com/docker/docker/builder"
|
|
"github.com/docker/docker/pkg/archive"
|
|
"github.com/docker/docker/pkg/chrootarchive"
|
|
"github.com/docker/docker/pkg/containerfs"
|
|
"github.com/docker/docker/pkg/longpath"
|
|
"github.com/docker/docker/pkg/tarsum"
|
|
"github.com/pkg/errors"
|
|
)
|
|
|
|
type archiveContext struct {
|
|
root string
|
|
sums tarsum.FileInfoSums
|
|
}
|
|
|
|
func (c *archiveContext) Close() error {
|
|
return os.RemoveAll(c.root)
|
|
}
|
|
|
|
func convertPathError(err error, cleanpath string) error {
|
|
if err, ok := err.(*os.PathError); ok {
|
|
err.Path = cleanpath
|
|
return err
|
|
}
|
|
return err
|
|
}
|
|
|
|
type modifiableContext interface {
|
|
builder.Source
|
|
// Remove deletes the entry specified by `path`.
|
|
// It is usual for directory entries to delete all its subentries.
|
|
Remove(path string) error
|
|
}
|
|
|
|
// FromArchive returns a build source from a tar stream.
|
|
//
|
|
// It extracts the tar stream to a temporary folder that is deleted as soon as
|
|
// the Context is closed.
|
|
// As the extraction happens, a tarsum is calculated for every file, and the set of
|
|
// all those sums then becomes the source of truth for all operations on this Context.
|
|
//
|
|
// Closing tarStream has to be done by the caller.
|
|
func FromArchive(tarStream io.Reader) (builder.Source, error) {
|
|
root, err := longpath.MkdirTemp("", "docker-builder")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Assume local file system. Since it's coming from a tar file.
|
|
tsc := &archiveContext{root: root}
|
|
|
|
// Make sure we clean-up upon error. In the happy case the caller
|
|
// is expected to manage the clean-up
|
|
defer func() {
|
|
if err != nil {
|
|
tsc.Close()
|
|
}
|
|
}()
|
|
|
|
decompressedStream, err := archive.DecompressStream(tarStream)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
sum, err := tarsum.NewTarSum(decompressedStream, true, tarsum.Version1)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
err = chrootarchive.Untar(sum, root, nil)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
tsc.sums = sum.GetSums()
|
|
return tsc, nil
|
|
}
|
|
|
|
func (c *archiveContext) Root() string {
|
|
return c.root
|
|
}
|
|
|
|
func (c *archiveContext) Remove(path string) error {
|
|
_, fullpath, err := normalize(path, c.root)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return os.RemoveAll(fullpath)
|
|
}
|
|
|
|
func (c *archiveContext) Hash(path string) (string, error) {
|
|
cleanpath, fullpath, err := normalize(path, c.root)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
rel, err := filepath.Rel(c.root, fullpath)
|
|
if err != nil {
|
|
return "", convertPathError(err, cleanpath)
|
|
}
|
|
|
|
// Use the checksum of the followed path(not the possible symlink) because
|
|
// this is the file that is actually copied.
|
|
if tsInfo := c.sums.GetFile(filepath.ToSlash(rel)); tsInfo != nil {
|
|
return tsInfo.Sum(), nil
|
|
}
|
|
// We set sum to path by default for the case where GetFile returns nil.
|
|
// The usual case is if relative path is empty.
|
|
return path, nil // backwards compat TODO: see if really needed
|
|
}
|
|
|
|
func normalize(path string, root string) (cleanPath, fullPath string, err error) {
|
|
cleanPath = filepath.Clean(string(filepath.Separator) + path)[1:]
|
|
fullPath, err = containerfs.ResolveScopedPath(root, path)
|
|
if err != nil {
|
|
return "", "", errors.Wrapf(err, "forbidden path outside the build context: %s (%s)", path, cleanPath)
|
|
}
|
|
return
|
|
}
|