Merge pull request #8230 from duglin/Issue6820
add wildcard support to COPY/ADD (part 2 of issue #6820)
This commit is contained in:
commit
1cd6135972
|
@ -102,7 +102,7 @@ func (b *Builder) commit(id string, autoCmd []string, comment string) error {
|
||||||
type copyInfo struct {
|
type copyInfo struct {
|
||||||
origPath string
|
origPath string
|
||||||
destPath string
|
destPath string
|
||||||
hashPath string
|
hash string
|
||||||
decompress bool
|
decompress bool
|
||||||
tmpDir string
|
tmpDir string
|
||||||
}
|
}
|
||||||
|
@ -118,14 +118,7 @@ func (b *Builder) runContextCommand(args []string, allowRemote bool, allowDecomp
|
||||||
|
|
||||||
dest := args[len(args)-1] // last one is always the dest
|
dest := args[len(args)-1] // last one is always the dest
|
||||||
|
|
||||||
if len(args) > 2 && dest[len(dest)-1] != '/' {
|
copyInfos := []*copyInfo{}
|
||||||
return fmt.Errorf("When using %s with more than one source file, the destination must be a directory and end with a /", cmdName)
|
|
||||||
}
|
|
||||||
|
|
||||||
copyInfos := make([]copyInfo, len(args)-1)
|
|
||||||
hasHash := false
|
|
||||||
srcPaths := ""
|
|
||||||
origPaths := ""
|
|
||||||
|
|
||||||
b.Config.Image = b.image
|
b.Config.Image = b.image
|
||||||
|
|
||||||
|
@ -140,28 +133,44 @@ func (b *Builder) runContextCommand(args []string, allowRemote bool, allowDecomp
|
||||||
// Loop through each src file and calculate the info we need to
|
// Loop through each src file and calculate the info we need to
|
||||||
// do the copy (e.g. hash value if cached). Don't actually do
|
// do the copy (e.g. hash value if cached). Don't actually do
|
||||||
// the copy until we've looked at all src files
|
// the copy until we've looked at all src files
|
||||||
for i, orig := range args[0 : len(args)-1] {
|
for _, orig := range args[0 : len(args)-1] {
|
||||||
ci := ©Infos[i]
|
err := calcCopyInfo(b, cmdName, ©Infos, orig, dest, allowRemote, allowDecompression)
|
||||||
ci.origPath = orig
|
|
||||||
ci.destPath = dest
|
|
||||||
ci.decompress = true
|
|
||||||
|
|
||||||
err := calcCopyInfo(b, cmdName, ci, allowRemote, allowDecompression)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
origPaths += " " + ci.origPath // will have leading space
|
|
||||||
if ci.hashPath == "" {
|
|
||||||
srcPaths += " " + ci.origPath // note leading space
|
|
||||||
} else {
|
|
||||||
srcPaths += " " + ci.hashPath // note leading space
|
|
||||||
hasHash = true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if len(copyInfos) == 0 {
|
||||||
|
return fmt.Errorf("No source files were specified")
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(copyInfos) > 1 && !strings.HasSuffix(dest, "/") {
|
||||||
|
return fmt.Errorf("When using %s with more than one source file, the destination must be a directory and end with a /", cmdName)
|
||||||
|
}
|
||||||
|
|
||||||
|
// For backwards compat, if there's just one CI then use it as the
|
||||||
|
// cache look-up string, otherwise hash 'em all into one
|
||||||
|
var srcHash string
|
||||||
|
var origPaths string
|
||||||
|
|
||||||
|
if len(copyInfos) == 1 {
|
||||||
|
srcHash = copyInfos[0].hash
|
||||||
|
origPaths = copyInfos[0].origPath
|
||||||
|
} else {
|
||||||
|
var hashs []string
|
||||||
|
var origs []string
|
||||||
|
for _, ci := range copyInfos {
|
||||||
|
hashs = append(hashs, ci.hash)
|
||||||
|
origs = append(origs, ci.origPath)
|
||||||
|
}
|
||||||
|
hasher := sha256.New()
|
||||||
|
hasher.Write([]byte(strings.Join(hashs, ",")))
|
||||||
|
srcHash = "multi:" + hex.EncodeToString(hasher.Sum(nil))
|
||||||
|
origPaths = strings.Join(origs, " ")
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd := b.Config.Cmd
|
cmd := b.Config.Cmd
|
||||||
b.Config.Cmd = []string{"/bin/sh", "-c", fmt.Sprintf("#(nop) %s%s in %s", cmdName, srcPaths, dest)}
|
b.Config.Cmd = []string{"/bin/sh", "-c", fmt.Sprintf("#(nop) %s %s in %s", cmdName, srcHash, dest)}
|
||||||
defer func(cmd []string) { b.Config.Cmd = cmd }(cmd)
|
defer func(cmd []string) { b.Config.Cmd = cmd }(cmd)
|
||||||
|
|
||||||
hit, err := b.probeCache()
|
hit, err := b.probeCache()
|
||||||
|
@ -169,7 +178,7 @@ func (b *Builder) runContextCommand(args []string, allowRemote bool, allowDecomp
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
// If we do not have at least one hash, never use the cache
|
// If we do not have at least one hash, never use the cache
|
||||||
if hit && hasHash {
|
if hit && b.UtilizeCache {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -190,24 +199,32 @@ func (b *Builder) runContextCommand(args []string, allowRemote bool, allowDecomp
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := b.commit(container.ID, cmd, fmt.Sprintf("%s%s in %s", cmdName, origPaths, dest)); err != nil {
|
if err := b.commit(container.ID, cmd, fmt.Sprintf("%s %s in %s", cmdName, origPaths, dest)); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func calcCopyInfo(b *Builder, cmdName string, ci *copyInfo, allowRemote bool, allowDecompression bool) error {
|
func calcCopyInfo(b *Builder, cmdName string, cInfos *[]*copyInfo, origPath string, destPath string, allowRemote bool, allowDecompression bool) error {
|
||||||
var (
|
|
||||||
remoteHash string
|
|
||||||
isRemote bool
|
|
||||||
)
|
|
||||||
|
|
||||||
saveOrig := ci.origPath
|
if origPath != "" && origPath[0] == '/' && len(origPath) > 1 {
|
||||||
isRemote = utils.IsURL(ci.origPath)
|
origPath = origPath[1:]
|
||||||
|
}
|
||||||
|
origPath = strings.TrimPrefix(origPath, "./")
|
||||||
|
|
||||||
|
// In the remote/URL case, download it and gen its hashcode
|
||||||
|
if utils.IsURL(origPath) {
|
||||||
|
if !allowRemote {
|
||||||
|
return fmt.Errorf("Source can't be a URL for %s", cmdName)
|
||||||
|
}
|
||||||
|
|
||||||
|
ci := copyInfo{}
|
||||||
|
ci.origPath = origPath
|
||||||
|
ci.hash = origPath // default to this but can change
|
||||||
|
ci.destPath = destPath
|
||||||
|
ci.decompress = false
|
||||||
|
*cInfos = append(*cInfos, &ci)
|
||||||
|
|
||||||
if isRemote && !allowRemote {
|
|
||||||
return fmt.Errorf("Source can't be an URL for %s", cmdName)
|
|
||||||
} else if isRemote {
|
|
||||||
// Initiate the download
|
// Initiate the download
|
||||||
resp, err := utils.Download(ci.origPath)
|
resp, err := utils.Download(ci.origPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -243,24 +260,9 @@ func calcCopyInfo(b *Builder, cmdName string, ci *copyInfo, allowRemote bool, al
|
||||||
|
|
||||||
ci.origPath = path.Join(filepath.Base(tmpDirName), filepath.Base(tmpFileName))
|
ci.origPath = path.Join(filepath.Base(tmpDirName), filepath.Base(tmpFileName))
|
||||||
|
|
||||||
// Process the checksum
|
|
||||||
r, err := archive.Tar(tmpFileName, archive.Uncompressed)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
tarSum, err := tarsum.NewTarSum(r, true, tarsum.Version0)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if _, err := io.Copy(ioutil.Discard, tarSum); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
remoteHash = tarSum.Sum(nil)
|
|
||||||
r.Close()
|
|
||||||
|
|
||||||
// If the destination is a directory, figure out the filename.
|
// If the destination is a directory, figure out the filename.
|
||||||
if strings.HasSuffix(ci.destPath, "/") {
|
if strings.HasSuffix(ci.destPath, "/") {
|
||||||
u, err := url.Parse(saveOrig)
|
u, err := url.Parse(origPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -275,62 +277,113 @@ func calcCopyInfo(b *Builder, cmdName string, ci *copyInfo, allowRemote bool, al
|
||||||
}
|
}
|
||||||
ci.destPath = ci.destPath + filename
|
ci.destPath = ci.destPath + filename
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if err := b.checkPathForAddition(ci.origPath); err != nil {
|
// Calc the checksum, only if we're using the cache
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Hash path and check the cache
|
|
||||||
if b.UtilizeCache {
|
if b.UtilizeCache {
|
||||||
var (
|
r, err := archive.Tar(tmpFileName, archive.Uncompressed)
|
||||||
sums = b.context.GetSums()
|
if err != nil {
|
||||||
)
|
|
||||||
|
|
||||||
if remoteHash != "" {
|
|
||||||
ci.hashPath = remoteHash
|
|
||||||
} else if fi, err := os.Stat(path.Join(b.contextPath, ci.origPath)); err != nil {
|
|
||||||
return err
|
return err
|
||||||
} else if fi.IsDir() {
|
}
|
||||||
|
tarSum, err := tarsum.NewTarSum(r, true, tarsum.Version0)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if _, err := io.Copy(ioutil.Discard, tarSum); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
ci.hash = tarSum.Sum(nil)
|
||||||
|
r.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deal with wildcards
|
||||||
|
if ContainsWildcards(origPath) {
|
||||||
|
for _, fileInfo := range b.context.GetSums() {
|
||||||
|
if fileInfo.Name() == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
match, _ := path.Match(origPath, fileInfo.Name())
|
||||||
|
if !match {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
calcCopyInfo(b, cmdName, cInfos, fileInfo.Name(), destPath, allowRemote, allowDecompression)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Must be a dir or a file
|
||||||
|
|
||||||
|
if err := b.checkPathForAddition(origPath); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
fi, _ := os.Stat(path.Join(b.contextPath, origPath))
|
||||||
|
|
||||||
|
ci := copyInfo{}
|
||||||
|
ci.origPath = origPath
|
||||||
|
ci.hash = origPath
|
||||||
|
ci.destPath = destPath
|
||||||
|
ci.decompress = allowDecompression
|
||||||
|
*cInfos = append(*cInfos, &ci)
|
||||||
|
|
||||||
|
// If not using cache don't need to do anything else.
|
||||||
|
// If we are using a cache then calc the hash for the src file/dir
|
||||||
|
if !b.UtilizeCache {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deal with the single file case
|
||||||
|
if !fi.IsDir() {
|
||||||
|
// This will match first file in sums of the archive
|
||||||
|
fis := b.context.GetSums().GetFile(ci.origPath)
|
||||||
|
if fis != nil {
|
||||||
|
ci.hash = "file:" + fis.Sum()
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Must be a dir
|
||||||
var subfiles []string
|
var subfiles []string
|
||||||
absOrigPath := path.Join(b.contextPath, ci.origPath)
|
absOrigPath := path.Join(b.contextPath, ci.origPath)
|
||||||
|
|
||||||
// Add a trailing / to make sure we only
|
// Add a trailing / to make sure we only pick up nested files under
|
||||||
// pick up nested files under the dir and
|
// the dir and not sibling files of the dir that just happen to
|
||||||
// not sibling files of the dir that just
|
// start with the same chars
|
||||||
// happen to start with the same chars
|
|
||||||
if !strings.HasSuffix(absOrigPath, "/") {
|
if !strings.HasSuffix(absOrigPath, "/") {
|
||||||
absOrigPath += "/"
|
absOrigPath += "/"
|
||||||
}
|
}
|
||||||
for _, fileInfo := range sums {
|
|
||||||
|
// Need path w/o / too to find matching dir w/o trailing /
|
||||||
|
absOrigPathNoSlash := absOrigPath[:len(absOrigPath)-1]
|
||||||
|
|
||||||
|
for _, fileInfo := range b.context.GetSums() {
|
||||||
absFile := path.Join(b.contextPath, fileInfo.Name())
|
absFile := path.Join(b.contextPath, fileInfo.Name())
|
||||||
if strings.HasPrefix(absFile, absOrigPath) {
|
if strings.HasPrefix(absFile, absOrigPath) || absFile == absOrigPathNoSlash {
|
||||||
subfiles = append(subfiles, fileInfo.Sum())
|
subfiles = append(subfiles, fileInfo.Sum())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
sort.Strings(subfiles)
|
sort.Strings(subfiles)
|
||||||
hasher := sha256.New()
|
hasher := sha256.New()
|
||||||
hasher.Write([]byte(strings.Join(subfiles, ",")))
|
hasher.Write([]byte(strings.Join(subfiles, ",")))
|
||||||
ci.hashPath = "dir:" + hex.EncodeToString(hasher.Sum(nil))
|
ci.hash = "dir:" + hex.EncodeToString(hasher.Sum(nil))
|
||||||
} else {
|
|
||||||
if ci.origPath[0] == '/' && len(ci.origPath) > 1 {
|
|
||||||
ci.origPath = ci.origPath[1:]
|
|
||||||
}
|
|
||||||
ci.origPath = strings.TrimPrefix(ci.origPath, "./")
|
|
||||||
// This will match on the first file in sums of the archive
|
|
||||||
if fis := sums.GetFile(ci.origPath); fis != nil {
|
|
||||||
ci.hashPath = "file:" + fis.Sum()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
if !allowDecompression || isRemote {
|
|
||||||
ci.decompress = false
|
|
||||||
}
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ContainsWildcards(name string) bool {
|
||||||
|
for i := 0; i < len(name); i++ {
|
||||||
|
ch := name[i]
|
||||||
|
if ch == '\\' {
|
||||||
|
i++
|
||||||
|
} else if ch == '*' || ch == '?' || ch == '[' {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
func (b *Builder) pullImage(name string) (*imagepkg.Image, error) {
|
func (b *Builder) pullImage(name string) (*imagepkg.Image, error) {
|
||||||
remote, tag := parsers.ParseRepositoryTag(name)
|
remote, tag := parsers.ParseRepositoryTag(name)
|
||||||
if tag == "" {
|
if tag == "" {
|
||||||
|
|
|
@ -295,11 +295,18 @@ The `ADD` instruction copies new files,directories or remote file URLs to
|
||||||
the filesystem of the container from `<src>` and add them to the at
|
the filesystem of the container from `<src>` and add them to the at
|
||||||
path `<dest>`.
|
path `<dest>`.
|
||||||
|
|
||||||
Multiple <src> resource may be specified but if they are files or
|
Multiple `<src>` resource may be specified but if they are files or
|
||||||
directories then they must be relative to the source directory that is
|
directories then they must be relative to the source directory that is
|
||||||
being built (the context of the build).
|
being built (the context of the build).
|
||||||
|
|
||||||
`<dest>` is the absolute path to which the source will be copied inside the
|
Each `<src>` may contain wildcards and matching will be done using Go's
|
||||||
|
[filepath.Match](http://golang.org/pkg/path/filepath#Match) rules.
|
||||||
|
For most command line uses this should act as expected, for example:
|
||||||
|
|
||||||
|
ADD hom* /mydir/ # adds all files starting with "hom"
|
||||||
|
ADD hom?.txt /mydir/ # ? is replaced with any single character
|
||||||
|
|
||||||
|
The `<dest>` is the absolute path to which the source will be copied inside the
|
||||||
destination container.
|
destination container.
|
||||||
|
|
||||||
All new files and directories are created with a UID and GID of 0.
|
All new files and directories are created with a UID and GID of 0.
|
||||||
|
@ -360,8 +367,9 @@ The copy obeys the following rules:
|
||||||
will be considered a directory and the contents of `<src>` will be written
|
will be considered a directory and the contents of `<src>` will be written
|
||||||
at `<dest>/base(<src>)`.
|
at `<dest>/base(<src>)`.
|
||||||
|
|
||||||
- If multiple `<src>` resources are specified then `<dest>` must be a
|
- If multiple `<src>` resources are specified, either directly or due to the
|
||||||
directory, and it must end with a slash `/`.
|
use of a wildcard, then `<dest>` must be a directory, and it must end with
|
||||||
|
a slash `/`.
|
||||||
|
|
||||||
- If `<dest>` does not end with a trailing slash, it will be considered a
|
- If `<dest>` does not end with a trailing slash, it will be considered a
|
||||||
regular file and the contents of `<src>` will be written at `<dest>`.
|
regular file and the contents of `<src>` will be written at `<dest>`.
|
||||||
|
@ -377,11 +385,18 @@ The `COPY` instruction copies new files,directories or remote file URLs to
|
||||||
the filesystem of the container from `<src>` and add them to the at
|
the filesystem of the container from `<src>` and add them to the at
|
||||||
path `<dest>`.
|
path `<dest>`.
|
||||||
|
|
||||||
Multiple <src> resource may be specified but if they are files or
|
Multiple `<src>` resource may be specified but if they are files or
|
||||||
directories then they must be relative to the source directory that is being
|
directories then they must be relative to the source directory that is being
|
||||||
built (the context of the build).
|
built (the context of the build).
|
||||||
|
|
||||||
`<dest>` is the absolute path to which the source will be copied inside the
|
Each `<src>` may contain wildcards and matching will be done using Go's
|
||||||
|
[filepath.Match](http://golang.org/pkg/path/filepath#Match) rules.
|
||||||
|
For most command line uses this should act as expected, for example:
|
||||||
|
|
||||||
|
COPY hom* /mydir/ # adds all files starting with "hom"
|
||||||
|
COPY hom?.txt /mydir/ # ? is replaced with any single character
|
||||||
|
|
||||||
|
The `<dest>` is the absolute path to which the source will be copied inside the
|
||||||
destination container.
|
destination container.
|
||||||
|
|
||||||
All new files and directories are created with a UID and GID of 0.
|
All new files and directories are created with a UID and GID of 0.
|
||||||
|
@ -405,8 +420,9 @@ The copy obeys the following rules:
|
||||||
will be considered a directory and the contents of `<src>` will be written
|
will be considered a directory and the contents of `<src>` will be written
|
||||||
at `<dest>/base(<src>)`.
|
at `<dest>/base(<src>)`.
|
||||||
|
|
||||||
- If multiple `<src>` resources are specified then `<dest>` must be a
|
- If multiple `<src>` resources are specified, either directly or due to the
|
||||||
directory, and it must end with a slash `/`.
|
use of a wildcard, then `<dest>` must be a directory, and it must end with
|
||||||
|
a slash `/`.
|
||||||
|
|
||||||
- If `<dest>` does not end with a trailing slash, it will be considered a
|
- If `<dest>` does not end with a trailing slash, it will be considered a
|
||||||
regular file and the contents of `<src>` will be written at `<dest>`.
|
regular file and the contents of `<src>` will be written at `<dest>`.
|
||||||
|
|
|
@ -174,6 +174,29 @@ func TestBuildAddMultipleFilesToFile(t *testing.T) {
|
||||||
logDone("build - multiple add files to file")
|
logDone("build - multiple add files to file")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestBuildAddMultipleFilesToFileWild(t *testing.T) {
|
||||||
|
name := "testaddmultiplefilestofilewild"
|
||||||
|
defer deleteImages(name)
|
||||||
|
ctx, err := fakeContext(`FROM scratch
|
||||||
|
ADD file*.txt test
|
||||||
|
`,
|
||||||
|
map[string]string{
|
||||||
|
"file1.txt": "test1",
|
||||||
|
"file2.txt": "test1",
|
||||||
|
})
|
||||||
|
defer ctx.Close()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
expected := "When using ADD with more than one source file, the destination must be a directory and end with a /"
|
||||||
|
if _, err := buildImageFromContext(name, ctx, true); err == nil || !strings.Contains(err.Error(), expected) {
|
||||||
|
t.Fatalf("Wrong error: (should contain \"%s\") got:\n%v", expected, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
logDone("build - multiple add files to file wild")
|
||||||
|
}
|
||||||
|
|
||||||
func TestBuildCopyMultipleFilesToFile(t *testing.T) {
|
func TestBuildCopyMultipleFilesToFile(t *testing.T) {
|
||||||
name := "testcopymultiplefilestofile"
|
name := "testcopymultiplefilestofile"
|
||||||
defer deleteImages(name)
|
defer deleteImages(name)
|
||||||
|
@ -197,6 +220,136 @@ func TestBuildCopyMultipleFilesToFile(t *testing.T) {
|
||||||
logDone("build - multiple copy files to file")
|
logDone("build - multiple copy files to file")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestBuildCopyWildcard(t *testing.T) {
|
||||||
|
name := "testcopywildcard"
|
||||||
|
defer deleteImages(name)
|
||||||
|
server, err := fakeStorage(map[string]string{
|
||||||
|
"robots.txt": "hello",
|
||||||
|
"index.html": "world",
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer server.Close()
|
||||||
|
ctx, err := fakeContext(fmt.Sprintf(`FROM busybox
|
||||||
|
COPY file*.txt /tmp/
|
||||||
|
RUN ls /tmp/file1.txt /tmp/file2.txt
|
||||||
|
RUN mkdir /tmp1
|
||||||
|
COPY dir* /tmp1/
|
||||||
|
RUN ls /tmp1/dirt /tmp1/nested_file /tmp1/nested_dir/nest_nest_file
|
||||||
|
RUN mkdir /tmp2
|
||||||
|
ADD dir/*dir %s/robots.txt /tmp2/
|
||||||
|
RUN ls /tmp2/nest_nest_file /tmp2/robots.txt
|
||||||
|
`, server.URL),
|
||||||
|
map[string]string{
|
||||||
|
"file1.txt": "test1",
|
||||||
|
"file2.txt": "test2",
|
||||||
|
"dir/nested_file": "nested file",
|
||||||
|
"dir/nested_dir/nest_nest_file": "2 times nested",
|
||||||
|
"dirt": "dirty",
|
||||||
|
})
|
||||||
|
defer ctx.Close()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
id1, err := buildImageFromContext(name, ctx, true)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now make sure we use a cache the 2nd time
|
||||||
|
id2, err := buildImageFromContext(name, ctx, true)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if id1 != id2 {
|
||||||
|
t.Fatal(fmt.Errorf("Didn't use the cache"))
|
||||||
|
}
|
||||||
|
|
||||||
|
logDone("build - copy wild card")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBuildCopyWildcardNoFind(t *testing.T) {
|
||||||
|
name := "testcopywildcardnofind"
|
||||||
|
defer deleteImages(name)
|
||||||
|
ctx, err := fakeContext(`FROM busybox
|
||||||
|
COPY file*.txt /tmp/
|
||||||
|
`, nil)
|
||||||
|
defer ctx.Close()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = buildImageFromContext(name, ctx, true)
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal(fmt.Errorf("Should have failed to find a file"))
|
||||||
|
}
|
||||||
|
if !strings.Contains(err.Error(), "No source files were specified") {
|
||||||
|
t.Fatalf("Wrong error %v, must be about no source files", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
logDone("build - copy wild card no find")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBuildCopyWildcardCache(t *testing.T) {
|
||||||
|
name := "testcopywildcardcache"
|
||||||
|
defer deleteImages(name)
|
||||||
|
server, err := fakeStorage(map[string]string{
|
||||||
|
"robots.txt": "hello",
|
||||||
|
"index.html": "world",
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer server.Close()
|
||||||
|
ctx, err := fakeContext(`FROM busybox
|
||||||
|
COPY file1.txt /tmp/
|
||||||
|
`,
|
||||||
|
map[string]string{
|
||||||
|
"file1.txt": "test1",
|
||||||
|
})
|
||||||
|
defer ctx.Close()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
id1, err := buildImageFromContext(name, ctx, true)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now make sure we use a cache the 2nd time even with wild card
|
||||||
|
ctx2, err := fakeContext(`FROM busybox
|
||||||
|
COPY file*.txt /tmp/
|
||||||
|
`,
|
||||||
|
map[string]string{
|
||||||
|
"file1.txt": "test1",
|
||||||
|
})
|
||||||
|
defer ctx2.Close()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
id2, err := buildImageFromContext(name, ctx2, true)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if id1 != id2 {
|
||||||
|
t.Fatal(fmt.Errorf("Didn't use the cache"))
|
||||||
|
}
|
||||||
|
|
||||||
|
logDone("build - copy wild card cache")
|
||||||
|
}
|
||||||
|
|
||||||
func TestBuildAddSingleFileToNonExistDir(t *testing.T) {
|
func TestBuildAddSingleFileToNonExistDir(t *testing.T) {
|
||||||
name := "testaddsinglefiletononexistdir"
|
name := "testaddsinglefiletononexistdir"
|
||||||
defer deleteImages(name)
|
defer deleteImages(name)
|
||||||
|
|
Loading…
Reference in New Issue