Bugfix: Reproducible layers with whiteout

When deleting files of previous layers, the whiteout files
were not added to the tar file in a consistent order.

This change adds a stable sorting to the whiteout files and
adds unit tests to check for stable sorting.
This commit is contained in:
Christopher Hlubek 2020-07-16 10:56:59 +02:00
parent 0c71a1bb0e
commit 699a4bee32
2 changed files with 160 additions and 0 deletions

View File

@ -108,6 +108,9 @@ func (s *Snapshotter) TakeSnapshot(files []string, shdCheckDelete bool) (string,
}
}
}
sort.Strings(filesToWhiteout)
t := util.NewTar(f)
defer t.Close()
if err := writeToTar(t, filesToAdd, filesToWhiteout); err != nil {
@ -178,7 +181,10 @@ func (s *Snapshotter) scanFullFilesystem() ([]string, []string, error) {
}
}
timing.DefaultRun.Stop(timer)
sort.Strings(filesToAdd)
sort.Strings(filesToWhiteOut)
// Add files to the layered map
for _, file := range filesToAdd {
if err := s.l.Add(file); err != nil {

View File

@ -391,6 +391,160 @@ func TestSnasphotPreservesFileOrder(t *testing.T) {
}
}
func TestSnasphotPreservesWhiteoutOrder(t *testing.T) {
newFiles := map[string]string{
"foo": "newbaz1",
"bar/bat": "baz",
"bar/qux": "quuz",
"qux": "quuz",
"corge": "grault",
"garply": "waldo",
"fred": "plugh",
"xyzzy": "thud",
}
newFileNames := []string{}
for fileName := range newFiles {
newFileNames = append(newFileNames, fileName)
}
filesInTars := [][]string{}
for i := 0; i <= 2; i++ {
testDir, snapshotter, cleanup, err := setUpTest()
testDirWithoutLeadingSlash := strings.TrimLeft(testDir, "/")
defer cleanup()
if err != nil {
t.Fatal(err)
}
// Make some changes to the filesystem
if err := testutil.SetupFiles(testDir, newFiles); err != nil {
t.Fatalf("Error setting up fs: %s", err)
}
filesToSnapshot := []string{}
for _, file := range newFileNames {
filesToSnapshot = append(filesToSnapshot, filepath.Join(testDir, file))
}
// Take a snapshot
_, err = snapshotter.TakeSnapshot(filesToSnapshot, false)
if err != nil {
t.Fatalf("Error taking snapshot of fs: %s", err)
}
// Delete all files
for p := range newFiles {
err := os.Remove(filepath.Join(testDir, p))
if err != nil {
t.Fatalf("Error deleting file: %s", err)
}
}
// Take a snapshot again
tarPath, err := snapshotter.TakeSnapshot(filesToSnapshot, true)
if err != nil {
t.Fatalf("Error taking snapshot of fs: %s", err)
}
f, err := os.Open(tarPath)
if err != nil {
t.Fatal(err)
}
tr := tar.NewReader(f)
filesInTars = append(filesInTars, []string{})
for {
hdr, err := tr.Next()
if err == io.EOF {
break
}
if err != nil {
t.Fatal(err)
}
filesInTars[i] = append(filesInTars[i], strings.TrimPrefix(hdr.Name, testDirWithoutLeadingSlash))
}
}
// Check contents of all snapshots, make sure files appear in consistent order
for i := 1; i < len(filesInTars); i++ {
testutil.CheckErrorAndDeepEqual(t, false, nil, filesInTars[0], filesInTars[i])
}
}
func TestSnasphotFSPreservesWhiteoutOrder(t *testing.T) {
newFiles := map[string]string{
"foo": "newbaz1",
"bar/bat": "baz",
"bar/qux": "quuz",
"qux": "quuz",
"corge": "grault",
"garply": "waldo",
"fred": "plugh",
"xyzzy": "thud",
}
filesInTars := [][]string{}
for i := 0; i <= 2; i++ {
testDir, snapshotter, cleanup, err := setUpTest()
testDirWithoutLeadingSlash := strings.TrimLeft(testDir, "/")
defer cleanup()
if err != nil {
t.Fatal(err)
}
// Make some changes to the filesystem
if err := testutil.SetupFiles(testDir, newFiles); err != nil {
t.Fatalf("Error setting up fs: %s", err)
}
// Take a snapshot
_, err = snapshotter.TakeSnapshotFS()
if err != nil {
t.Fatalf("Error taking snapshot of fs: %s", err)
}
// Delete all files
for p := range newFiles {
err := os.Remove(filepath.Join(testDir, p))
if err != nil {
t.Fatalf("Error deleting file: %s", err)
}
}
// Take a snapshot again
tarPath, err := snapshotter.TakeSnapshotFS()
if err != nil {
t.Fatalf("Error taking snapshot of fs: %s", err)
}
f, err := os.Open(tarPath)
if err != nil {
t.Fatal(err)
}
tr := tar.NewReader(f)
filesInTars = append(filesInTars, []string{})
for {
hdr, err := tr.Next()
if err == io.EOF {
break
}
if err != nil {
t.Fatal(err)
}
filesInTars[i] = append(filesInTars[i], strings.TrimPrefix(hdr.Name, testDirWithoutLeadingSlash))
}
}
// Check contents of all snapshots, make sure files appear in consistent order
for i := 1; i < len(filesInTars); i++ {
testutil.CheckErrorAndDeepEqual(t, false, nil, filesInTars[0], filesInTars[i])
}
}
func TestSnapshotOmitsUnameGname(t *testing.T) {
_, snapshotter, cleanup, err := setUpTest()