add: add a DryRun flag to AddAndCopyOptions
Add a DryRun flag to AddAndCopyOptions, so that we can "copy" content to digest it. Signed-off-by: Nalin Dahyabhai <nalin@redhat.com> Closes: #1792 Approved by: TomSweeneyRedHat
This commit is contained in:
parent
36dcedbabd
commit
db2b3e48ac
46
add.go
46
add.go
|
@ -35,28 +35,36 @@ type AddAndCopyOptions struct {
|
||||||
Hasher io.Writer
|
Hasher io.Writer
|
||||||
// Excludes is the contents of the .dockerignore file
|
// Excludes is the contents of the .dockerignore file
|
||||||
Excludes []string
|
Excludes []string
|
||||||
// The base directory for Excludes and data to copy in
|
// ContextDir is the base directory for Excludes for content being copied
|
||||||
ContextDir string
|
ContextDir string
|
||||||
// ID mapping options to use when contents to be copied are part of
|
// ID mapping options to use when contents to be copied are part of
|
||||||
// another container, and need ownerships to be mapped from the host to
|
// another container, and need ownerships to be mapped from the host to
|
||||||
// that container's values before copying them into the container.
|
// that container's values before copying them into the container.
|
||||||
IDMappingOptions *IDMappingOptions
|
IDMappingOptions *IDMappingOptions
|
||||||
|
// DryRun indicates that the content should be digested, but not actually
|
||||||
|
// copied into the container.
|
||||||
|
DryRun bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// addURL copies the contents of the source URL to the destination. This is
|
// addURL copies the contents of the source URL to the destination. This is
|
||||||
// its own function so that deferred closes happen after we're done pulling
|
// its own function so that deferred closes happen after we're done pulling
|
||||||
// down each item of potentially many.
|
// down each item of potentially many.
|
||||||
func addURL(destination, srcurl string, owner idtools.IDPair, hasher io.Writer) error {
|
func addURL(destination, srcurl string, owner idtools.IDPair, hasher io.Writer, dryRun bool) error {
|
||||||
logrus.Debugf("saving %q to %q", srcurl, destination)
|
|
||||||
resp, err := http.Get(srcurl)
|
resp, err := http.Get(srcurl)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrapf(err, "error getting %q", srcurl)
|
return errors.Wrapf(err, "error getting %q", srcurl)
|
||||||
}
|
}
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
thisWriter := hasher
|
||||||
|
|
||||||
|
if !dryRun {
|
||||||
|
logrus.Debugf("saving %q to %q", srcurl, destination)
|
||||||
f, err := os.Create(destination)
|
f, err := os.Create(destination)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrapf(err, "error creating %q", destination)
|
return errors.Wrapf(err, "error creating %q", destination)
|
||||||
}
|
}
|
||||||
|
defer f.Close()
|
||||||
if err = f.Chown(owner.UID, owner.GID); err != nil {
|
if err = f.Chown(owner.UID, owner.GID); err != nil {
|
||||||
return errors.Wrapf(err, "error setting owner of %q to %d:%d", destination, owner.UID, owner.GID)
|
return errors.Wrapf(err, "error setting owner of %q to %d:%d", destination, owner.UID, owner.GID)
|
||||||
}
|
}
|
||||||
|
@ -71,21 +79,24 @@ func addURL(destination, srcurl string, owner idtools.IDPair, hasher io.Writer)
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
defer f.Close()
|
defer func() {
|
||||||
bodyReader := io.Reader(resp.Body)
|
if err2 := f.Chmod(0600); err2 != nil {
|
||||||
if hasher != nil {
|
logrus.Debugf("error setting permissions on %q: %v", destination, err2)
|
||||||
bodyReader = io.TeeReader(bodyReader, hasher)
|
|
||||||
}
|
}
|
||||||
n, err := io.Copy(f, bodyReader)
|
}()
|
||||||
|
if thisWriter != nil {
|
||||||
|
thisWriter = io.MultiWriter(f, thisWriter)
|
||||||
|
} else {
|
||||||
|
thisWriter = f
|
||||||
|
}
|
||||||
|
}
|
||||||
|
n, err := io.Copy(thisWriter, resp.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrapf(err, "error reading contents for %q from %q", destination, srcurl)
|
return errors.Wrapf(err, "error reading contents for %q from %q", destination, srcurl)
|
||||||
}
|
}
|
||||||
if resp.ContentLength >= 0 && n != resp.ContentLength {
|
if resp.ContentLength >= 0 && n != resp.ContentLength {
|
||||||
return errors.Errorf("error reading contents for %q from %q: wrong length (%d != %d)", destination, srcurl, n, resp.ContentLength)
|
return errors.Errorf("error reading contents for %q from %q: wrong length (%d != %d)", destination, srcurl, n, resp.ContentLength)
|
||||||
}
|
}
|
||||||
if err := f.Chmod(0600); err != nil {
|
|
||||||
return errors.Wrapf(err, "error setting permissions on %q", destination)
|
|
||||||
}
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -118,6 +129,8 @@ func (b *Builder) Add(destination string, extract bool, options AddAndCopyOption
|
||||||
}
|
}
|
||||||
hostOwner := idtools.IDPair{UID: int(hostUID), GID: int(hostGID)}
|
hostOwner := idtools.IDPair{UID: int(hostUID), GID: int(hostGID)}
|
||||||
dest := mountPoint
|
dest := mountPoint
|
||||||
|
if !options.DryRun {
|
||||||
|
// Resolve the destination if it was specified as a relative path.
|
||||||
if destination != "" && filepath.IsAbs(destination) {
|
if destination != "" && filepath.IsAbs(destination) {
|
||||||
dir := filepath.Dir(destination)
|
dir := filepath.Dir(destination)
|
||||||
if dir != "." && dir != "/" {
|
if dir != "." && dir != "/" {
|
||||||
|
@ -144,6 +157,7 @@ func (b *Builder) Add(destination string, extract bool, options AddAndCopyOption
|
||||||
if destpfi, err2 := os.Stat(filepath.Dir(dest)); err2 == nil && !destpfi.IsDir() {
|
if destpfi, err2 := os.Stat(filepath.Dir(dest)); err2 == nil && !destpfi.IsDir() {
|
||||||
return errors.Errorf("%q already exists, but is not a subdirectory)", filepath.Dir(dest))
|
return errors.Errorf("%q already exists, but is not a subdirectory)", filepath.Dir(dest))
|
||||||
}
|
}
|
||||||
|
}
|
||||||
// Now look at the destination itself.
|
// Now look at the destination itself.
|
||||||
destfi, err := os.Stat(dest)
|
destfi, err := os.Stat(dest)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -155,9 +169,9 @@ func (b *Builder) Add(destination string, extract bool, options AddAndCopyOption
|
||||||
if len(source) > 1 && (destfi == nil || !destfi.IsDir()) {
|
if len(source) > 1 && (destfi == nil || !destfi.IsDir()) {
|
||||||
return errors.Errorf("destination %q is not a directory", dest)
|
return errors.Errorf("destination %q is not a directory", dest)
|
||||||
}
|
}
|
||||||
copyFileWithTar := b.copyFileWithTar(options.IDMappingOptions, &containerOwner, options.Hasher)
|
copyFileWithTar := b.copyFileWithTar(options.IDMappingOptions, &containerOwner, options.Hasher, options.DryRun)
|
||||||
copyWithTar := b.copyWithTar(options.IDMappingOptions, &containerOwner, options.Hasher)
|
copyWithTar := b.copyWithTar(options.IDMappingOptions, &containerOwner, options.Hasher, options.DryRun)
|
||||||
untarPath := b.untarPath(nil, options.Hasher)
|
untarPath := b.untarPath(nil, options.Hasher, options.DryRun)
|
||||||
err = addHelper(excludes, extract, dest, destfi, hostOwner, options, copyFileWithTar, copyWithTar, untarPath, source...)
|
err = addHelper(excludes, extract, dest, destfi, hostOwner, options, copyFileWithTar, copyWithTar, untarPath, source...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -245,7 +259,7 @@ func addHelper(excludes *fileutils.PatternMatcher, extract bool, dest string, de
|
||||||
if destfi != nil && destfi.IsDir() {
|
if destfi != nil && destfi.IsDir() {
|
||||||
d = filepath.Join(dest, path.Base(url.Path))
|
d = filepath.Join(dest, path.Base(url.Path))
|
||||||
}
|
}
|
||||||
if err = addURL(d, src, hostOwner, options.Hasher); err != nil {
|
if err = addURL(d, src, hostOwner, options.Hasher, options.DryRun); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
continue
|
continue
|
||||||
|
@ -273,9 +287,11 @@ func addHelper(excludes *fileutils.PatternMatcher, extract bool, dest string, de
|
||||||
// the source directory into the target directory. Try
|
// the source directory into the target directory. Try
|
||||||
// to create it first, so that if there's a problem,
|
// to create it first, so that if there's a problem,
|
||||||
// we'll discover why that won't work.
|
// we'll discover why that won't work.
|
||||||
|
if !options.DryRun {
|
||||||
if err = idtools.MkdirAllAndChownNew(dest, 0755, hostOwner); err != nil {
|
if err = idtools.MkdirAllAndChownNew(dest, 0755, hostOwner); err != nil {
|
||||||
return errors.Wrapf(err, "error creating directory %q", dest)
|
return errors.Wrapf(err, "error creating directory %q", dest)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
logrus.Debugf("copying %q to %q", esrc+string(os.PathSeparator)+"*", dest+string(os.PathSeparator)+"*")
|
logrus.Debugf("copying %q to %q", esrc+string(os.PathSeparator)+"*", dest+string(os.PathSeparator)+"*")
|
||||||
if excludes == nil || !excludes.Exclusions() {
|
if excludes == nil || !excludes.Exclusions() {
|
||||||
if err = copyWithTar(esrc, dest); err != nil {
|
if err = copyWithTar(esrc, dest); err != nil {
|
||||||
|
|
|
@ -431,7 +431,7 @@ func (b *Builder) setupMounts(mountPoint string, spec *specs.Spec, bundlePath st
|
||||||
|
|
||||||
// Add temporary copies of the contents of volume locations at the
|
// Add temporary copies of the contents of volume locations at the
|
||||||
// volume locations, unless we already have something there.
|
// volume locations, unless we already have something there.
|
||||||
copyWithTar := b.copyWithTar(nil, nil, nil)
|
copyWithTar := b.copyWithTar(nil, nil, nil, false)
|
||||||
builtins, err := runSetupBuiltinVolumes(b.MountLabel, mountPoint, cdir, copyWithTar, builtinVolumes, int(rootUID), int(rootGID))
|
builtins, err := runSetupBuiltinVolumes(b.MountLabel, mountPoint, cdir, copyWithTar, builtinVolumes, int(rootUID), int(rootGID))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
35
util.go
35
util.go
|
@ -3,6 +3,7 @@ package buildah
|
||||||
import (
|
import (
|
||||||
"archive/tar"
|
"archive/tar"
|
||||||
"io"
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
|
@ -112,7 +113,7 @@ func convertRuntimeIDMaps(UIDMap, GIDMap []rspec.LinuxIDMapping) ([]idtools.IDMa
|
||||||
// of any container, or another container, into our working container, mapping
|
// of any container, or another container, into our working container, mapping
|
||||||
// read permissions using the passed-in ID maps, writing using the container's
|
// read permissions using the passed-in ID maps, writing using the container's
|
||||||
// ID mappings, possibly overridden using the passed-in chownOpts
|
// ID mappings, possibly overridden using the passed-in chownOpts
|
||||||
func (b *Builder) copyFileWithTar(tarIDMappingOptions *IDMappingOptions, chownOpts *idtools.IDPair, hasher io.Writer) func(src, dest string) error {
|
func (b *Builder) copyFileWithTar(tarIDMappingOptions *IDMappingOptions, chownOpts *idtools.IDPair, hasher io.Writer, dryRun bool) func(src, dest string) error {
|
||||||
if tarIDMappingOptions == nil {
|
if tarIDMappingOptions == nil {
|
||||||
tarIDMappingOptions = &IDMappingOptions{
|
tarIDMappingOptions = &IDMappingOptions{
|
||||||
HostUIDMapping: true,
|
HostUIDMapping: true,
|
||||||
|
@ -207,7 +208,7 @@ func (b *Builder) copyFileWithTar(tarIDMappingOptions *IDMappingOptions, chownOp
|
||||||
pipeWriter = nil
|
pipeWriter = nil
|
||||||
}(f)
|
}(f)
|
||||||
|
|
||||||
untar := b.untar(chownOpts, hasher)
|
untar := b.untar(chownOpts, hasher, dryRun)
|
||||||
err = untar(pipeReader, b.MountPoint)
|
err = untar(pipeReader, b.MountPoint)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
err = copyErr
|
err = copyErr
|
||||||
|
@ -224,9 +225,9 @@ func (b *Builder) copyFileWithTar(tarIDMappingOptions *IDMappingOptions, chownOp
|
||||||
// our container or from another container, into our working container, mapping
|
// our container or from another container, into our working container, mapping
|
||||||
// permissions at read-time using the container's ID maps, with ownership at
|
// permissions at read-time using the container's ID maps, with ownership at
|
||||||
// write-time possibly overridden using the passed-in chownOpts
|
// write-time possibly overridden using the passed-in chownOpts
|
||||||
func (b *Builder) copyWithTar(tarIDMappingOptions *IDMappingOptions, chownOpts *idtools.IDPair, hasher io.Writer) func(src, dest string) error {
|
func (b *Builder) copyWithTar(tarIDMappingOptions *IDMappingOptions, chownOpts *idtools.IDPair, hasher io.Writer, dryRun bool) func(src, dest string) error {
|
||||||
tar := b.tarPath(tarIDMappingOptions)
|
tar := b.tarPath(tarIDMappingOptions)
|
||||||
untar := b.untar(chownOpts, hasher)
|
untar := b.untar(chownOpts, hasher, dryRun)
|
||||||
return func(src, dest string) error {
|
return func(src, dest string) error {
|
||||||
rc, err := tar(src)
|
rc, err := tar(src)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -239,8 +240,22 @@ func (b *Builder) copyWithTar(tarIDMappingOptions *IDMappingOptions, chownOpts *
|
||||||
// untarPath returns a function which extracts an archive in a specified
|
// untarPath returns a function which extracts an archive in a specified
|
||||||
// location into our working container, mapping permissions using the
|
// location into our working container, mapping permissions using the
|
||||||
// container's ID maps, possibly overridden using the passed-in chownOpts
|
// container's ID maps, possibly overridden using the passed-in chownOpts
|
||||||
func (b *Builder) untarPath(chownOpts *idtools.IDPair, hasher io.Writer) func(src, dest string) error {
|
func (b *Builder) untarPath(chownOpts *idtools.IDPair, hasher io.Writer, dryRun bool) func(src, dest string) error {
|
||||||
convertedUIDMap, convertedGIDMap := convertRuntimeIDMaps(b.IDMappingOptions.UIDMap, b.IDMappingOptions.GIDMap)
|
convertedUIDMap, convertedGIDMap := convertRuntimeIDMaps(b.IDMappingOptions.UIDMap, b.IDMappingOptions.GIDMap)
|
||||||
|
if dryRun {
|
||||||
|
return func(src, dest string) error {
|
||||||
|
if hasher == nil {
|
||||||
|
hasher = ioutil.Discard
|
||||||
|
}
|
||||||
|
f, err := os.Open(src)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(err, "error opening %q", src)
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
_, err = io.Copy(hasher, f)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
return chrootarchive.UntarPathAndChown(chownOpts, hasher, convertedUIDMap, convertedGIDMap)
|
return chrootarchive.UntarPathAndChown(chownOpts, hasher, convertedUIDMap, convertedGIDMap)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -272,7 +287,7 @@ func (b *Builder) tarPath(idMappingOptions *IDMappingOptions) func(path string)
|
||||||
// untar returns a function which extracts an archive stream to a specified
|
// untar returns a function which extracts an archive stream to a specified
|
||||||
// location in the container's filesystem, mapping permissions using the
|
// location in the container's filesystem, mapping permissions using the
|
||||||
// container's ID maps, possibly overridden using the passed-in chownOpts
|
// container's ID maps, possibly overridden using the passed-in chownOpts
|
||||||
func (b *Builder) untar(chownOpts *idtools.IDPair, hasher io.Writer) func(tarArchive io.ReadCloser, dest string) error {
|
func (b *Builder) untar(chownOpts *idtools.IDPair, hasher io.Writer, dryRun bool) func(tarArchive io.ReadCloser, dest string) error {
|
||||||
convertedUIDMap, convertedGIDMap := convertRuntimeIDMaps(b.IDMappingOptions.UIDMap, b.IDMappingOptions.GIDMap)
|
convertedUIDMap, convertedGIDMap := convertRuntimeIDMaps(b.IDMappingOptions.UIDMap, b.IDMappingOptions.GIDMap)
|
||||||
untarMappings := idtools.NewIDMappingsFromMaps(convertedUIDMap, convertedGIDMap)
|
untarMappings := idtools.NewIDMappingsFromMaps(convertedUIDMap, convertedGIDMap)
|
||||||
options := &archive.TarOptions{
|
options := &archive.TarOptions{
|
||||||
|
@ -281,6 +296,14 @@ func (b *Builder) untar(chownOpts *idtools.IDPair, hasher io.Writer) func(tarArc
|
||||||
ChownOpts: chownOpts,
|
ChownOpts: chownOpts,
|
||||||
}
|
}
|
||||||
untar := chrootarchive.Untar
|
untar := chrootarchive.Untar
|
||||||
|
if dryRun {
|
||||||
|
untar = func(tarArchive io.Reader, dest string, options *archive.TarOptions) error {
|
||||||
|
if _, err := io.Copy(ioutil.Discard, tarArchive); err != nil {
|
||||||
|
return errors.Wrapf(err, "error digesting tar stream")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
if hasher != nil {
|
if hasher != nil {
|
||||||
originalUntar := untar
|
originalUntar := untar
|
||||||
untar = func(tarArchive io.Reader, dest string, options *archive.TarOptions) error {
|
untar = func(tarArchive io.Reader, dest string, options *archive.TarOptions) error {
|
||||||
|
|
Loading…
Reference in New Issue