Skip to content

Commit 66996a6

Browse files
authored
Merge pull request moby#5521 from sipsma/improve-contenthash-handle-change
contenthash: don't delete records when a directory is only modified
2 parents cb36999 + c10b272 commit 66996a6

File tree

2 files changed

+82
-1
lines changed

2 files changed

+82
-1
lines changed

cache/contenthash/checksum.go

+4-1
Original file line numberDiff line numberDiff line change
@@ -352,9 +352,12 @@ func (cc *cacheContext) HandleChange(kind fsutil.ChangeKind, p string, fi os.Fil
352352
return errors.Errorf("invalid fileinfo: %s", p)
353353
}
354354

355+
// if we are replacing a directory with a non-directory, rm -rf the tree under the existing dir
355356
v, ok := cc.node.Get(k)
356357
if ok {
357-
deleteDir(v)
358+
if v.Type == CacheRecordTypeDir && !fi.IsDir() {
359+
deleteDir(v)
360+
}
358361
}
359362

360363
cr := &CacheRecord{

cache/contenthash/checksum_test.go

+78
Original file line numberDiff line numberDiff line change
@@ -1400,6 +1400,84 @@ func TestPersistence(t *testing.T) {
14001400
require.Equal(t, dgstFileData0, dgst)
14011401
}
14021402

1403+
func TestChecksumUpdateDirectory(t *testing.T) {
1404+
t.Parallel()
1405+
tmpdir := t.TempDir()
1406+
1407+
snapshotter, err := native.NewSnapshotter(filepath.Join(tmpdir, "snapshots"))
1408+
require.NoError(t, err)
1409+
t.Cleanup(func() {
1410+
require.NoError(t, snapshotter.Close())
1411+
})
1412+
1413+
cm, cleanup := setupCacheManager(t, tmpdir, "native", snapshotter)
1414+
t.Cleanup(cleanup)
1415+
1416+
ch := []string{
1417+
"ADD d0 dir",
1418+
"ADD d0/foo dir",
1419+
"ADD d0/foo/bar file data0",
1420+
"ADD d0/foo/subdir1 dir",
1421+
"ADD d0/foo/subdir1/baz file data1",
1422+
"ADD d0/foo/subdir2 dir",
1423+
}
1424+
1425+
ref := createRef(t, cm, nil)
1426+
1427+
cc, err := newCacheContext(ref)
1428+
require.NoError(t, err)
1429+
1430+
err = emit(cc.HandleChange, changeStream(ch))
1431+
require.NoError(t, err)
1432+
1433+
fooDgst1, err := cc.Checksum(context.TODO(), ref, "d0/foo", ChecksumOpts{}, nil)
1434+
require.NoError(t, err)
1435+
require.Equal(t, digest.Digest("sha256:e76717544f71725bd759a981554ca17c286b3d222598f46a671b983fd2b8172d"), fooDgst1)
1436+
1437+
barDgst1, err := cc.Checksum(context.TODO(), ref, "d0/foo/bar", ChecksumOpts{}, nil)
1438+
require.NoError(t, err)
1439+
require.Equal(t, digest.Digest("sha256:cd8e75bca50f2d695f220d0cb0997d8ead387e4f926e8669a92d7f104cc9885b"), barDgst1)
1440+
1441+
// change d0/foo's permissions
1442+
updateFooCh := parseChange("CHG d0/foo dir")
1443+
fi, ok := updateFooCh.fi.(*fsutil.StatInfo)
1444+
require.True(t, ok)
1445+
prevMode := fi.Stat.Mode
1446+
fi.Stat.Mode = uint32(os.ModeDir) | 0700
1447+
require.NotEqual(t, prevMode, fi.Stat.Mode) // sanity check we actually changed something
1448+
1449+
err = emit(cc.HandleChange, []*change{updateFooCh})
1450+
require.NoError(t, err)
1451+
1452+
// d0/foo should have a different digest now
1453+
fooDgst2, err := cc.Checksum(context.TODO(), ref, "d0/foo", ChecksumOpts{}, nil)
1454+
require.NoError(t, err)
1455+
require.NotEqual(t, fooDgst1, fooDgst2)
1456+
require.Equal(t, digest.Digest("sha256:3a729f6ba0d3d74c6ade7d118b08b46e37e447afdad7fc6e258dbba12fa80141"), fooDgst2)
1457+
1458+
// but files under the dir should be the same as before
1459+
barDgst2, err := cc.Checksum(context.TODO(), ref, "d0/foo/bar", ChecksumOpts{}, nil)
1460+
require.NoError(t, err)
1461+
require.Equal(t, barDgst1, barDgst2)
1462+
1463+
// replace d0/foo with a file
1464+
err = emit(cc.HandleChange, changeStream([]string{
1465+
"CHG d0/foo file data2",
1466+
}))
1467+
require.NoError(t, err)
1468+
1469+
// d0/foo should again have a different digest now
1470+
fooDgst3, err := cc.Checksum(context.TODO(), ref, "d0/foo", ChecksumOpts{}, nil)
1471+
require.NoError(t, err)
1472+
require.NotEqual(t, fooDgst1, fooDgst3)
1473+
require.NotEqual(t, fooDgst2, fooDgst3)
1474+
require.Equal(t, digest.Digest("sha256:1c67653c3cf95b12a0014e2c4cd1d776b474b3218aee54155d6ae27b9b999c54"), fooDgst3)
1475+
1476+
// files under the old dir should not exist anymore
1477+
_, err = cc.Checksum(context.TODO(), ref, "d0/foo/bar", ChecksumOpts{}, nil)
1478+
require.ErrorContains(t, err, "not found")
1479+
}
1480+
14031481
func createRef(t *testing.T, cm cache.Manager, files []string) cache.ImmutableRef {
14041482
if runtime.GOOS == "windows" && len(files) > 0 {
14051483
// lm.Mount() will fail

0 commit comments

Comments
 (0)