Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add test for NewReproducibleTar #14

Merged
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
2 changes: 1 addition & 1 deletion pkg/executor/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ func newStageBuilder(args *dockerfile.BuildArgs, opts *config.KanikoOptions, sta
if !opts.Reproducible {
snapshotter = snapshot.NewSnapshotter(l, config.RootDir)
} else {
snapshotter = snapshot.NewCanonicalSnapshotter(l, config.RootDir)
snapshotter = snapshot.NewReproducibleSnapshotter(l, config.RootDir)
}

digest, err := sourceImage.Digest()
Expand Down
22 changes: 11 additions & 11 deletions pkg/snapshot/snapshot.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,21 +39,21 @@ var snapshotPathPrefix = ""

// Snapshotter holds the root directory from which to take snapshots, and a list of snapshots taken
type Snapshotter struct {
l *LayeredMap
directory string
ignorelist []util.IgnoreListEntry
canonical bool
l *LayeredMap
directory string
ignorelist []util.IgnoreListEntry
reproducible bool
}

// NewSnapshotter creates a new snapshotter rooted at d
func NewSnapshotter(l *LayeredMap, d string) *Snapshotter {
return &Snapshotter{l: l, directory: d, ignorelist: util.IgnoreList()}
}

// NewCanonicalSnapshotter creates a new snapshotter rooted at d that produces
// NewReproducibleSnapshotter creates a new snapshotter rooted at d that produces
// reproducible snapshots.
func NewCanonicalSnapshotter(l *LayeredMap, d string) *Snapshotter {
return &Snapshotter{l: l, directory: d, ignorelist: util.IgnoreList(), canonical: true}
func NewReproducibleSnapshotter(l *LayeredMap, d string) *Snapshotter {
return &Snapshotter{l: l, directory: d, ignorelist: util.IgnoreList(), reproducible: true}
}

// Init initializes a new snapshotter
Expand Down Expand Up @@ -120,10 +120,10 @@ func (s *Snapshotter) TakeSnapshot(files []string, shdCheckDelete bool, forceBui
}

var t util.Tar
if !s.canonical {
if !s.reproducible {
t = util.NewTar(f)
} else {
t = util.NewCanonicalTar(f)
t = util.NewReproducibleTar(f)
}
defer t.Close()
if err := writeToTar(t, filesToAdd, filesToWhiteout); err != nil {
Expand All @@ -141,10 +141,10 @@ func (s *Snapshotter) TakeSnapshotFS() (string, error) {
}
defer f.Close()
var t util.Tar
if !s.canonical {
if !s.reproducible {
t = util.NewTar(f)
} else {
t = util.NewCanonicalTar(f)
t = util.NewReproducibleTar(f)
}
defer t.Close()

Expand Down
18 changes: 9 additions & 9 deletions pkg/util/tar_util.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,9 @@ import (

// Tar knows how to write files to a tar file.
type Tar struct {
hardlinks map[uint64]string
w *tar.Writer
canonical bool
hardlinks map[uint64]string
w *tar.Writer
ignoreTimestamps bool
}

// NewTar will create an instance of Tar that can write files to the writer at f.
Expand All @@ -52,14 +52,14 @@ func NewTar(f io.Writer) Tar {
}
}

// NewCanonicalTar will create an instance of Tar that can write files to the
// NewReproducibleTar will create an instance of Tar that can write files to the
// writer at f, ignoring timestamps to produce a canonical archive.
func NewCanonicalTar(f io.Writer) Tar {
func NewReproducibleTar(f io.Writer) Tar {
w := tar.NewWriter(f)
return Tar{
w: w,
hardlinks: map[uint64]string{},
canonical: true,
w: w,
hardlinks: map[uint64]string{},
ignoreTimestamps: true,
}
}

Expand Down Expand Up @@ -110,7 +110,7 @@ func (t *Tar) AddFileToTar(p string) error {
if err != nil {
return err
}
if t.canonical {
if t.ignoreTimestamps {
ct := time.Time{}
hdr.ModTime = ct

Expand Down
57 changes: 57 additions & 0 deletions pkg/util/tar_util_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (
"compress/gzip"
"fmt"
"io"
"io/fs"
"os"
"path/filepath"
"testing"
Expand Down Expand Up @@ -153,6 +154,9 @@ func Test_CreateTarballOfDirectory(t *testing.T) {
// skip directory
continue
}
if modTime := fileInfo.ModTime(); modTime.Equal(time.Unix(0, 0)) {
t.Errorf("unexpected modtime %q of %q", modTime, fileInfo.Name())
}
file, err := os.Open(filePath)
testutil.CheckError(t, wantErr, err)
body, err := io.ReadAll(file)
Expand All @@ -172,3 +176,56 @@ func createFilesInTempDir(t *testing.T, tmpDir string) {
}
}
}

func Test_NewReproducibleTar(t *testing.T) {
tmpDir := t.TempDir()
createFilesInTempDir(t, tmpDir)
f := &bytes.Buffer{}

// Create tarball ignoring timestamps
tw := NewReproducibleTar(f)
if err := filepath.WalkDir(tmpDir, func(path string, _ fs.DirEntry, err error) error {
if err != nil {
return err
}
if !filepath.IsAbs(path) {
return fmt.Errorf("path %v is not absolute", path)
}
return tw.AddFileToTar(path)
}); err != nil {
t.Fatalf("failed to create tar of %q: %s", tmpDir, err.Error())
}

extracedFilesDir := filepath.Join(tmpDir, "extracted")
if err := os.Mkdir(extracedFilesDir, 0755); err != nil {
t.Fatal(err)
}
files, err := UnTar(f, extracedFilesDir)
if err != nil {
t.Fatalf("untar: %s", err.Error())
}
for _, filePath := range files {
fileInfo, err := os.Lstat(filePath)
if err != nil {
t.Fatalf("stat %q: %s", filePath, err.Error())
}
if fileInfo.IsDir() {
// skip directory
continue
}
// In a 'reproducible' tar, all timestamps should be set to zero
if modTime := fileInfo.ModTime(); !modTime.Equal(time.Unix(0, 0)) {
t.Errorf("unexpected modtime %q of %q", modTime, filePath)
}
file, err := os.Open(filePath)
if err != nil {
t.Fatalf("open file %q: %s", filePath, err.Error())
}
body, err := io.ReadAll(file)
if err != nil {
t.Fatalf("read file %q: %s", filePath, err.Error())
}
index := filepath.Base(filePath)
testutil.CheckDeepEqual(t, string(body), fmt.Sprintf("hello from %s\n", index))
}
}