Use the HTTP Last-Modified http header as the mtime value for ADD cmd when present

Closes #8331

Signed-off-by: Doug Davis <dug@us.ibm.com>
This commit is contained in:
Doug Davis 2014-10-22 11:16:42 -07:00
parent 6fcc9141d7
commit 2e482c86bc
3 changed files with 81 additions and 3 deletions

View file

@ -9,6 +9,7 @@ import (
"fmt"
"io"
"io/ioutil"
"net/http"
"net/url"
"os"
"path"
@ -254,8 +255,21 @@ func calcCopyInfo(b *Builder, cmdName string, cInfos *[]*copyInfo, origPath stri
fmt.Fprintf(b.OutStream, "\n")
tmpFile.Close()
// Remove the mtime of the newly created tmp file
if err := system.UtimesNano(tmpFileName, make([]syscall.Timespec, 2)); err != nil {
// Set the mtime to the Last-Modified header value if present
// Otherwise just remove atime and mtime
times := make([]syscall.Timespec, 2)
lastMod := resp.Header.Get("Last-Modified")
if lastMod != "" {
mTime, err := http.ParseTime(lastMod)
// If we can't parse it then just let it default to 'zero'
// otherwise use the parsed time value
if err == nil {
times[1] = syscall.NsecToTimespec(mTime.UnixNano())
}
}
if err := system.UtimesNano(tmpFileName, times); err != nil {
return err
}

View file

@ -376,7 +376,11 @@ destination container.
All new files and directories are created with a UID and GID of 0.
In the case where `<src>` is a remote file URL, the destination will
have permissions of 600.
have permissions of 600. If the remote file being retrieved has an HTTP
`Last-Modified` header, the timestamp from that header will be used
to set the `mtime` on the destination file. Then, like any other file
processed during an `ADD`, `mtime` will be included in the determination
of whether or not the file has changed and the cache should be updated.
> **Note**:
> If you build by passing a `Dockerfile` through STDIN (`docker

View file

@ -7,9 +7,11 @@ import (
"io/ioutil"
"os"
"os/exec"
"path"
"path/filepath"
"regexp"
"strings"
"syscall"
"testing"
"time"
@ -2214,6 +2216,64 @@ func TestBuildADDRemoteFileWithoutCache(t *testing.T) {
logDone("build - add remote file without cache")
}
func TestBuildADDRemoteFileMTime(t *testing.T) {
name := "testbuildaddremotefilemtime"
defer deleteImages(name)
server, err := fakeStorage(map[string]string{"baz": "hello"})
if err != nil {
t.Fatal(err)
}
defer server.Close()
ctx, err := fakeContext(fmt.Sprintf(`FROM scratch
MAINTAINER dockerio
ADD %s/baz /usr/lib/baz/quux`, server.URL), nil)
if err != nil {
t.Fatal(err)
}
defer ctx.Close()
id1, err := buildImageFromContext(name, ctx, true)
if err != nil {
t.Fatal(err)
}
id2, err := buildImageFromContext(name, ctx, true)
if err != nil {
t.Fatal(err)
}
if id1 != id2 {
t.Fatal("The cache should have been used but wasn't - #1")
}
// Now set baz's times to anything else and redo the build
// This time the cache should not be used
bazPath := path.Join(server.FakeContext.Dir, "baz")
err = syscall.UtimesNano(bazPath, make([]syscall.Timespec, 2))
if err != nil {
t.Fatalf("Error setting mtime on %q: %v", bazPath, err)
}
id3, err := buildImageFromContext(name, ctx, true)
if err != nil {
t.Fatal(err)
}
if id1 == id3 {
t.Fatal("The cache should not have been used but was")
}
// And for good measure do it again and make sure cache is used this time
id4, err := buildImageFromContext(name, ctx, true)
if err != nil {
t.Fatal(err)
}
if id3 != id4 {
t.Fatal("The cache should have been used but wasn't - #2")
}
logDone("build - add remote file testing mtime")
}
func TestBuildADDLocalAndRemoteFilesWithCache(t *testing.T) {
name := "testbuildaddlocalandremotefilewithcache"
defer deleteImages(name)