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:
parent
6fcc9141d7
commit
2e482c86bc
3 changed files with 81 additions and 3 deletions
|
@ -9,6 +9,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
|
@ -254,8 +255,21 @@ func calcCopyInfo(b *Builder, cmdName string, cInfos *[]*copyInfo, origPath stri
|
||||||
fmt.Fprintf(b.OutStream, "\n")
|
fmt.Fprintf(b.OutStream, "\n")
|
||||||
tmpFile.Close()
|
tmpFile.Close()
|
||||||
|
|
||||||
// Remove the mtime of the newly created tmp file
|
// Set the mtime to the Last-Modified header value if present
|
||||||
if err := system.UtimesNano(tmpFileName, make([]syscall.Timespec, 2)); err != nil {
|
// 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
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -376,7 +376,11 @@ 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.
|
||||||
|
|
||||||
In the case where `<src>` is a remote file URL, the destination will
|
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**:
|
> **Note**:
|
||||||
> If you build by passing a `Dockerfile` through STDIN (`docker
|
> If you build by passing a `Dockerfile` through STDIN (`docker
|
||||||
|
|
|
@ -7,9 +7,11 @@ import (
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
|
"path"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
|
"syscall"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
@ -2214,6 +2216,64 @@ func TestBuildADDRemoteFileWithoutCache(t *testing.T) {
|
||||||
logDone("build - add remote file without cache")
|
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) {
|
func TestBuildADDLocalAndRemoteFilesWithCache(t *testing.T) {
|
||||||
name := "testbuildaddlocalandremotefilewithcache"
|
name := "testbuildaddlocalandremotefilewithcache"
|
||||||
defer deleteImages(name)
|
defer deleteImages(name)
|
||||||
|
|
Loading…
Reference in a new issue