Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion mkctr.go
Original file line number Diff line number Diff line change
Expand Up @@ -568,7 +568,10 @@ func layerFromFiles(logf logf, files map[string]string, layerMediaType types.Med
dirs[dir] = true
return nil
}
for src, dst := range files {
// Visit sources in sorted order so the same inputs always produce the
// same layer.
for _, src := range slices.Sorted(maps.Keys(files)) {
dst := files[src]
err := filepath.WalkDir(src, func(srcWalk string, d fs.DirEntry, err error) error {
path := strings.TrimPrefix(srcWalk, src)
dstWalk := filepath.Join(dst, path)
Expand Down
45 changes: 45 additions & 0 deletions mkctr_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
// Copyright (c) 2021 Tailscale Inc & AUTHORS All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

import (
"os"
"path/filepath"
"testing"

"github.com/google/go-containerregistry/pkg/v1/types"
)

func TestLayerFromFilesReproducible(t *testing.T) {
dir := t.TempDir()
files := map[string]string{}
for _, name := range []string{"a", "b", "c", "d", "e"} {
src := filepath.Join(dir, name)
if err := os.WriteFile(src, []byte(name), 0o644); err != nil {
t.Fatal(err)
}
files[src] = "/usr/local/bin/" + name
}
logf := func(string, ...any) {}

var first string
for i := range 30 {
layer, err := layerFromFiles(logf, files, types.OCILayer)
if err != nil {
t.Fatal(err)
}
d, err := layer.DiffID()
if err != nil {
t.Fatal(err)
}
if i == 0 {
first = d.String()
continue
}
if d.String() != first {
t.Fatalf("build %d produced layer %s; build 0 produced %s; want identical layers for identical inputs", i, d, first)
}
}
}