Skip to content

Commit

Permalink
Merge pull request #5521 from sipsma/improve-contenthash-handle-change
Browse files Browse the repository at this point in the history
contenthash: don't delete records when a directory is only modified
  • Loading branch information
sipsma authored Nov 15, 2024
2 parents cb36999 + c10b272 commit 66996a6
Show file tree
Hide file tree
Showing 2 changed files with 82 additions and 1 deletion.
5 changes: 4 additions & 1 deletion cache/contenthash/checksum.go
Original file line number Diff line number Diff line change
Expand Up @@ -352,9 +352,12 @@ func (cc *cacheContext) HandleChange(kind fsutil.ChangeKind, p string, fi os.Fil
return errors.Errorf("invalid fileinfo: %s", p)
}

// if we are replacing a directory with a non-directory, rm -rf the tree under the existing dir
v, ok := cc.node.Get(k)
if ok {
deleteDir(v)
if v.Type == CacheRecordTypeDir && !fi.IsDir() {
deleteDir(v)
}
}

cr := &CacheRecord{
Expand Down
78 changes: 78 additions & 0 deletions cache/contenthash/checksum_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1400,6 +1400,84 @@ func TestPersistence(t *testing.T) {
require.Equal(t, dgstFileData0, dgst)
}

func TestChecksumUpdateDirectory(t *testing.T) {
t.Parallel()
tmpdir := t.TempDir()

snapshotter, err := native.NewSnapshotter(filepath.Join(tmpdir, "snapshots"))
require.NoError(t, err)
t.Cleanup(func() {
require.NoError(t, snapshotter.Close())
})

cm, cleanup := setupCacheManager(t, tmpdir, "native", snapshotter)
t.Cleanup(cleanup)

ch := []string{
"ADD d0 dir",
"ADD d0/foo dir",
"ADD d0/foo/bar file data0",
"ADD d0/foo/subdir1 dir",
"ADD d0/foo/subdir1/baz file data1",
"ADD d0/foo/subdir2 dir",
}

ref := createRef(t, cm, nil)

cc, err := newCacheContext(ref)
require.NoError(t, err)

err = emit(cc.HandleChange, changeStream(ch))
require.NoError(t, err)

fooDgst1, err := cc.Checksum(context.TODO(), ref, "d0/foo", ChecksumOpts{}, nil)
require.NoError(t, err)
require.Equal(t, digest.Digest("sha256:e76717544f71725bd759a981554ca17c286b3d222598f46a671b983fd2b8172d"), fooDgst1)

barDgst1, err := cc.Checksum(context.TODO(), ref, "d0/foo/bar", ChecksumOpts{}, nil)
require.NoError(t, err)
require.Equal(t, digest.Digest("sha256:cd8e75bca50f2d695f220d0cb0997d8ead387e4f926e8669a92d7f104cc9885b"), barDgst1)

// change d0/foo's permissions
updateFooCh := parseChange("CHG d0/foo dir")
fi, ok := updateFooCh.fi.(*fsutil.StatInfo)
require.True(t, ok)
prevMode := fi.Stat.Mode
fi.Stat.Mode = uint32(os.ModeDir) | 0700
require.NotEqual(t, prevMode, fi.Stat.Mode) // sanity check we actually changed something

err = emit(cc.HandleChange, []*change{updateFooCh})
require.NoError(t, err)

// d0/foo should have a different digest now
fooDgst2, err := cc.Checksum(context.TODO(), ref, "d0/foo", ChecksumOpts{}, nil)
require.NoError(t, err)
require.NotEqual(t, fooDgst1, fooDgst2)
require.Equal(t, digest.Digest("sha256:3a729f6ba0d3d74c6ade7d118b08b46e37e447afdad7fc6e258dbba12fa80141"), fooDgst2)

// but files under the dir should be the same as before
barDgst2, err := cc.Checksum(context.TODO(), ref, "d0/foo/bar", ChecksumOpts{}, nil)
require.NoError(t, err)
require.Equal(t, barDgst1, barDgst2)

// replace d0/foo with a file
err = emit(cc.HandleChange, changeStream([]string{
"CHG d0/foo file data2",
}))
require.NoError(t, err)

// d0/foo should again have a different digest now
fooDgst3, err := cc.Checksum(context.TODO(), ref, "d0/foo", ChecksumOpts{}, nil)
require.NoError(t, err)
require.NotEqual(t, fooDgst1, fooDgst3)
require.NotEqual(t, fooDgst2, fooDgst3)
require.Equal(t, digest.Digest("sha256:1c67653c3cf95b12a0014e2c4cd1d776b474b3218aee54155d6ae27b9b999c54"), fooDgst3)

// files under the old dir should not exist anymore
_, err = cc.Checksum(context.TODO(), ref, "d0/foo/bar", ChecksumOpts{}, nil)
require.ErrorContains(t, err, "not found")
}

func createRef(t *testing.T, cm cache.Manager, files []string) cache.ImmutableRef {
if runtime.GOOS == "windows" && len(files) > 0 {
// lm.Mount() will fail
Expand Down

0 comments on commit 66996a6

Please sign in to comment.