contenthash: include basename in content checksum for wildcards

While we generally ignore the basename in this layer, for wildcards
there in no other place to add the basename to the checksum as they
can not be resolved earlier. Before the basename that was in the
checksum was the wildcard itself, so if the wildcard remained same,
content remained same but the file where wildcard pointed to was
renamed, the cache was not invalidated.

Unfortunately, this change breaks cache for all copy commands that
use a wildcard.

Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
v0.9
Tonis Tiigi 2021-03-12 00:04:47 -08:00
parent 8effd45b99
commit 1982e1e285
3 changed files with 65 additions and 14 deletions

View File

@ -406,17 +406,19 @@ func (cc *cacheContext) ChecksumWildcard(ctx context.Context, mountable cache.Mo
return digest.FromBytes([]byte{}), nil
}
if len(wildcards) > 1 {
digester := digest.Canonical.Digester()
for i, w := range wildcards {
if i != 0 {
digester.Hash().Write([]byte{0})
}
digester.Hash().Write([]byte(w.Record.Digest))
}
return digester.Digest(), nil
if len(wildcards) == 1 && path.Base(p) == path.Base(wildcards[0].Path) {
return wildcards[0].Record.Digest, nil
}
return wildcards[0].Record.Digest, nil
digester := digest.Canonical.Digester()
for i, w := range wildcards {
if i != 0 {
digester.Hash().Write([]byte{0})
}
digester.Hash().Write([]byte(path.Base(w.Path)))
digester.Hash().Write([]byte(w.Record.Digest))
}
return digester.Digest(), nil
}
func (cc *cacheContext) Checksum(ctx context.Context, mountable cache.Mountable, p string, followLinks bool, s session.Group) (digest.Digest, error) {

View File

@ -179,9 +179,9 @@ func TestChecksumWildcard(t *testing.T) {
dgst, err := cc.ChecksumWildcard(context.TODO(), ref, "f*o", false, nil)
require.NoError(t, err)
require.Equal(t, dgstFileData0, dgst)
require.Equal(t, digest.FromBytes(append([]byte("foo"), []byte(dgstFileData0)...)), dgst)
expFoos := digest.Digest("sha256:c9f914ad7ad8fe6092ce67484b43ca39c2087aabf9e4a1b223249b0f8b09b9f2")
expFoos := digest.Digest("sha256:7f51c821895cfc116d3f64231dfb438e87a237ecbbe027cd96b7ee5e763cc569")
dgst, err = cc.ChecksumWildcard(context.TODO(), ref, "f*", false, nil)
require.NoError(t, err)
@ -189,15 +189,17 @@ func TestChecksumWildcard(t *testing.T) {
dgst, err = cc.ChecksumWildcard(context.TODO(), ref, "x/d?", false, nil)
require.NoError(t, err)
require.Equal(t, dgstDirD0, dgst)
require.Equal(t, digest.FromBytes(append([]byte("d0"), []byte(dgstDirD0)...)), dgst)
dgst, err = cc.ChecksumWildcard(context.TODO(), ref, "x/d?/def", true, nil)
require.NoError(t, err)
require.Equal(t, dgstFileData0, dgst)
expFoos2 := digest.Digest("sha256:8afc09c7018d65d5eb318a9ef55cb704dec1f06d288181d913fc27a571aa042d")
dgst, err = cc.ChecksumWildcard(context.TODO(), ref, "y*", true, nil)
require.NoError(t, err)
require.Equal(t, expFoos, dgst)
require.Equal(t, expFoos2, dgst)
err = ref.Release(context.TODO())
require.NoError(t, err)

View File

@ -109,6 +109,7 @@ var allTests = []integration.Test{
testDefaultShellAndPath,
testDockerfileLowercase,
testExportCacheLoop,
testWildcardRenameCache,
}
var fileOpTests = []integration.Test{
@ -3832,6 +3833,52 @@ LABEL foo=bar
require.Equal(t, "baz", v)
}
// #2008
func testWildcardRenameCache(t *testing.T, sb integration.Sandbox) {
skipDockerd(t, sb)
f := getFrontend(t, sb)
dockerfile := []byte(`
FROM alpine
COPY file* /files/
RUN ls /files/file1
`)
dir, err := tmpdir(
fstest.CreateFile("Dockerfile", dockerfile, 0600),
fstest.CreateFile("file1", []byte("foo"), 0600),
)
require.NoError(t, err)
defer os.RemoveAll(dir)
c, err := client.New(context.TODO(), sb.Address())
require.NoError(t, err)
defer c.Close()
destDir, err := ioutil.TempDir("", "buildkit")
require.NoError(t, err)
defer os.RemoveAll(destDir)
_, err = f.Solve(context.TODO(), c, client.SolveOpt{
LocalDirs: map[string]string{
builder.DefaultLocalNameDockerfile: dir,
builder.DefaultLocalNameContext: dir,
},
}, nil)
require.NoError(t, err)
err = os.Rename(filepath.Join(dir, "file1"), filepath.Join(dir, "file2"))
require.NoError(t, err)
// cache should be invalidated and build should fail
_, err = f.Solve(context.TODO(), c, client.SolveOpt{
LocalDirs: map[string]string{
builder.DefaultLocalNameDockerfile: dir,
builder.DefaultLocalNameContext: dir,
},
}, nil)
require.Error(t, err)
}
func testOnBuildCleared(t *testing.T, sb integration.Sandbox) {
f := getFrontend(t, sb)