2017-03-07 04:39:22 +08:00
|
|
|
package buildah
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"io"
|
|
|
|
"net/http"
|
2017-03-08 02:26:17 +08:00
|
|
|
"net/url"
|
2017-03-07 04:39:22 +08:00
|
|
|
"os"
|
|
|
|
"path"
|
|
|
|
"path/filepath"
|
|
|
|
"strings"
|
|
|
|
|
|
|
|
"github.com/Sirupsen/logrus"
|
|
|
|
"github.com/containers/storage/pkg/archive"
|
2017-03-22 02:26:00 +08:00
|
|
|
"github.com/containers/storage/pkg/chrootarchive"
|
2017-03-07 04:39:22 +08:00
|
|
|
)
|
|
|
|
|
|
|
|
// 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
|
|
|
|
// down each item of potentially many.
|
|
|
|
func addUrl(destination, srcurl string) error {
|
|
|
|
logrus.Debugf("saving %q to %q", srcurl, destination)
|
|
|
|
resp, err := http.Get(srcurl)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("error getting %q: %v", srcurl, err)
|
|
|
|
}
|
|
|
|
defer resp.Body.Close()
|
|
|
|
f, err := os.Create(destination)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("error creating %q: %v", destination, err)
|
|
|
|
}
|
|
|
|
defer f.Close()
|
|
|
|
n, err := io.Copy(f, resp.Body)
|
|
|
|
if resp.ContentLength >= 0 && n != resp.ContentLength {
|
|
|
|
return fmt.Errorf("error reading contents for %q: wrong length (%d != %d)", destination, n, resp.ContentLength)
|
|
|
|
}
|
|
|
|
if err := f.Chmod(0755); err != nil {
|
|
|
|
return fmt.Errorf("error setting permissions on %q: %v", destination, err)
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2017-03-24 01:48:23 +08:00
|
|
|
// Add copies the contents of the specified sources into the container's root
|
|
|
|
// filesystem, optionally extracting contents of local files that look like
|
|
|
|
// non-empty archives.
|
2017-03-07 04:39:22 +08:00
|
|
|
func (b *Builder) Add(destination string, extract bool, source ...string) error {
|
2017-03-24 01:48:23 +08:00
|
|
|
mountPoint, err := b.Mount("")
|
|
|
|
if err != nil {
|
|
|
|
return err
|
2017-03-07 04:39:22 +08:00
|
|
|
}
|
2017-03-24 01:48:23 +08:00
|
|
|
defer func() {
|
|
|
|
if err2 := b.Unmount(); err2 != nil {
|
|
|
|
logrus.Errorf("error unmounting container: %v", err2)
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
dest := mountPoint
|
2017-03-07 04:39:22 +08:00
|
|
|
if destination != "" && filepath.IsAbs(destination) {
|
|
|
|
dest = filepath.Join(dest, destination)
|
|
|
|
} else {
|
2017-03-24 01:47:07 +08:00
|
|
|
if err := os.MkdirAll(filepath.Join(dest, b.Workdir), 0755); err != nil {
|
2017-03-28 15:02:41 +08:00
|
|
|
return fmt.Errorf("error ensuring directory %q exists: %v)", filepath.Join(dest, b.Workdir), err)
|
2017-03-24 01:47:07 +08:00
|
|
|
}
|
2017-03-07 04:39:22 +08:00
|
|
|
dest = filepath.Join(dest, b.Workdir, destination)
|
|
|
|
}
|
2017-03-28 03:35:09 +08:00
|
|
|
// If the destination was explicitly marked as a directory by ending it
|
|
|
|
// with a '/', create it so that we can be sure that it's a directory,
|
|
|
|
// and any files we're copying will be placed in the directory.
|
|
|
|
if len(destination) > 0 && destination[len(destination)-1] == os.PathSeparator {
|
|
|
|
if err := os.MkdirAll(dest, 0755); err != nil {
|
|
|
|
return fmt.Errorf("error ensuring directory %q exists: %v)", dest, err)
|
|
|
|
}
|
|
|
|
}
|
2017-03-24 01:49:38 +08:00
|
|
|
// Make sure the destination's parent directory is usable.
|
|
|
|
if fi, err := os.Stat(filepath.Dir(dest)); err == nil && !fi.Mode().IsDir() {
|
|
|
|
return fmt.Errorf("%q already exists, but is not a subdirectory)", filepath.Dir(dest))
|
2017-03-08 02:26:17 +08:00
|
|
|
}
|
2017-03-24 01:49:38 +08:00
|
|
|
// Now look at the destination itself.
|
|
|
|
destfi, err := os.Stat(dest)
|
|
|
|
if err != nil {
|
|
|
|
if !os.IsNotExist(err) {
|
|
|
|
return fmt.Errorf("couldn't determine what %q is: %v)", dest, err)
|
|
|
|
}
|
|
|
|
destfi = nil
|
|
|
|
}
|
|
|
|
if len(source) > 1 && (destfi == nil || !destfi.Mode().IsDir()) {
|
|
|
|
return fmt.Errorf("destination %q is not a directory", dest)
|
2017-03-08 02:26:17 +08:00
|
|
|
}
|
2017-03-07 04:39:22 +08:00
|
|
|
for _, src := range source {
|
|
|
|
if strings.HasPrefix(src, "http://") || strings.HasPrefix(src, "https://") {
|
|
|
|
// We assume that source is a file, and we're copying
|
2017-03-28 15:02:41 +08:00
|
|
|
// it to the destination. If the destination is
|
|
|
|
// already a directory, create a file inside of it.
|
|
|
|
// Otherwise, the destination is the file to which
|
|
|
|
// we'll save the contents.
|
2017-03-08 02:26:17 +08:00
|
|
|
url, err := url.Parse(src)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("error parsing URL %q: %v", src, err)
|
2017-03-07 04:39:22 +08:00
|
|
|
}
|
2017-03-24 01:49:38 +08:00
|
|
|
d := dest
|
|
|
|
if destfi != nil && destfi.Mode().IsDir() {
|
|
|
|
d = filepath.Join(dest, path.Base(url.Path))
|
|
|
|
}
|
2017-03-07 04:39:22 +08:00
|
|
|
if err := addUrl(d, src); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
continue
|
|
|
|
}
|
2017-03-24 01:49:38 +08:00
|
|
|
srcfi, err := os.Stat(src)
|
2017-03-07 04:39:22 +08:00
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("error reading %q: %v", src, err)
|
|
|
|
}
|
2017-03-24 01:49:38 +08:00
|
|
|
if srcfi.Mode().IsDir() {
|
2017-03-28 15:02:41 +08:00
|
|
|
// The source is a directory, so copy the contents of
|
|
|
|
// the source directory into the target directory. Try
|
|
|
|
// to create it first, so that if there's a problem,
|
|
|
|
// we'll discover why that won't work.
|
2017-03-24 01:49:38 +08:00
|
|
|
d := dest
|
2017-03-08 02:26:17 +08:00
|
|
|
if err := os.MkdirAll(d, 0755); err != nil {
|
2017-03-28 15:02:41 +08:00
|
|
|
return fmt.Errorf("error ensuring directory %q exists: %v)", d, err)
|
2017-03-07 04:39:22 +08:00
|
|
|
}
|
|
|
|
logrus.Debugf("copying %q to %q", src+string(os.PathSeparator)+"*", d+string(os.PathSeparator)+"*")
|
2017-03-22 02:26:00 +08:00
|
|
|
if err := chrootarchive.CopyWithTar(src, d); err != nil {
|
2017-03-07 04:39:22 +08:00
|
|
|
return fmt.Errorf("error copying %q to %q: %v", src, d, err)
|
|
|
|
}
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
if !extract || !archive.IsArchivePath(src) {
|
|
|
|
// This source is a file, and either it's not an
|
|
|
|
// archive, or we don't care whether or not it's an
|
2017-03-08 02:26:17 +08:00
|
|
|
// archive.
|
2017-03-24 01:49:38 +08:00
|
|
|
d := dest
|
|
|
|
if destfi != nil && destfi.Mode().IsDir() {
|
|
|
|
d = filepath.Join(dest, filepath.Base(src))
|
|
|
|
}
|
2017-03-07 04:39:22 +08:00
|
|
|
// Copy the file, preserving attributes.
|
|
|
|
logrus.Debugf("copying %q to %q", src, d)
|
2017-03-22 02:26:00 +08:00
|
|
|
if err := chrootarchive.CopyFileWithTar(src, d); err != nil {
|
2017-03-07 04:39:22 +08:00
|
|
|
return fmt.Errorf("error copying %q to %q: %v", src, d, err)
|
|
|
|
}
|
|
|
|
continue
|
|
|
|
}
|
2017-03-08 02:26:17 +08:00
|
|
|
// We're extracting an archive into the destination directory.
|
2017-03-07 04:39:22 +08:00
|
|
|
logrus.Debugf("extracting contents of %q into %q", src, dest)
|
2017-03-22 02:26:00 +08:00
|
|
|
if err := chrootarchive.UntarPath(src, dest); err != nil {
|
2017-03-07 04:39:22 +08:00
|
|
|
return fmt.Errorf("error extracting %q into %q: %v", src, dest, err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|