github.com/GoogleContainerTools/kaniko@v1.23.0/pkg/util/fs_util_test.go (about)

     1  /*
     2  Copyright 2018 Google LLC
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package util
    18  
    19  import (
    20  	"archive/tar"
    21  	"bytes"
    22  	"fmt"
    23  	"io"
    24  	"io/fs"
    25  	"os"
    26  	"path/filepath"
    27  	"reflect"
    28  	"sort"
    29  	"strings"
    30  	"testing"
    31  	"time"
    32  
    33  	"github.com/GoogleContainerTools/kaniko/pkg/config"
    34  	"github.com/GoogleContainerTools/kaniko/pkg/constants"
    35  	"github.com/GoogleContainerTools/kaniko/pkg/mocks/go-containerregistry/mockv1"
    36  	"github.com/GoogleContainerTools/kaniko/testutil"
    37  	"github.com/golang/mock/gomock"
    38  	v1 "github.com/google/go-containerregistry/pkg/v1"
    39  	"github.com/google/go-containerregistry/pkg/v1/types"
    40  )
    41  
    42  func Test_DetectFilesystemSkiplist(t *testing.T) {
    43  	testDir := t.TempDir()
    44  	fileContents := `
    45  	228 122 0:90 / / rw,relatime - aufs none rw,si=f8e2406af90782bc,dio,dirperm1
    46  	229 228 0:98 / /proc rw,nosuid,nodev,noexec,relatime - proc proc rw
    47  	230 228 0:99 / /dev rw,nosuid - tmpfs tmpfs rw,size=65536k,mode=755
    48  	231 230 0:100 / /dev/pts rw,nosuid,noexec,relatime - devpts devpts rw,gid=5,mode=620,ptmxmode=666
    49  	232 228 0:101 / /sys ro,nosuid,nodev,noexec,relatime - sysfs sysfs ro`
    50  
    51  	path := filepath.Join(testDir, "mountinfo")
    52  	if err := os.MkdirAll(filepath.Dir(path), 0o750); err != nil {
    53  		t.Fatalf("Error creating tempdir: %s", err)
    54  	}
    55  	if err := os.WriteFile(path, []byte(fileContents), 0o644); err != nil {
    56  		t.Fatalf("Error writing file contents to %s: %s", path, err)
    57  	}
    58  
    59  	err := DetectFilesystemIgnoreList(path)
    60  	expectedSkiplist := []IgnoreListEntry{
    61  		{"/kaniko", false},
    62  		{"/proc", false},
    63  		{"/dev", false},
    64  		{"/dev/pts", false},
    65  		{"/sys", false},
    66  		{"/etc/mtab", false},
    67  		{"/tmp/apt-key-gpghome", true},
    68  	}
    69  	actualSkiplist := ignorelist
    70  	sort.Slice(actualSkiplist, func(i, j int) bool {
    71  		return actualSkiplist[i].Path < actualSkiplist[j].Path
    72  	})
    73  	sort.Slice(expectedSkiplist, func(i, j int) bool {
    74  		return expectedSkiplist[i].Path < expectedSkiplist[j].Path
    75  	})
    76  	testutil.CheckErrorAndDeepEqual(t, false, err, expectedSkiplist, actualSkiplist)
    77  }
    78  
    79  func Test_AddToIgnoreList(t *testing.T) {
    80  	t.Cleanup(func() {
    81  		ignorelist = append([]IgnoreListEntry{}, defaultIgnoreList...)
    82  	})
    83  
    84  	AddToIgnoreList(IgnoreListEntry{
    85  		Path:            "/tmp",
    86  		PrefixMatchOnly: false,
    87  	})
    88  
    89  	if !CheckIgnoreList("/tmp") {
    90  		t.Errorf("CheckIgnoreList() = %v, want %v", false, true)
    91  	}
    92  }
    93  
    94  var tests = []struct {
    95  	files         map[string]string
    96  	directory     string
    97  	expectedFiles []string
    98  }{
    99  	{
   100  		files: map[string]string{
   101  			"/workspace/foo/a": "baz1",
   102  			"/workspace/foo/b": "baz2",
   103  			"/kaniko/file":     "file",
   104  		},
   105  		directory: "/workspace/foo/",
   106  		expectedFiles: []string{
   107  			"workspace/foo/a",
   108  			"workspace/foo/b",
   109  			"workspace/foo",
   110  		},
   111  	},
   112  	{
   113  		files: map[string]string{
   114  			"/workspace/foo/a": "baz1",
   115  		},
   116  		directory: "/workspace/foo/a",
   117  		expectedFiles: []string{
   118  			"workspace/foo/a",
   119  		},
   120  	},
   121  	{
   122  		files: map[string]string{
   123  			"/workspace/foo/a": "baz1",
   124  			"/workspace/foo/b": "baz2",
   125  			"/workspace/baz":   "hey",
   126  			"/kaniko/file":     "file",
   127  		},
   128  		directory: "/workspace",
   129  		expectedFiles: []string{
   130  			"workspace/foo/a",
   131  			"workspace/foo/b",
   132  			"workspace/baz",
   133  			"workspace",
   134  			"workspace/foo",
   135  		},
   136  	},
   137  	{
   138  		files: map[string]string{
   139  			"/workspace/foo/a": "baz1",
   140  			"/workspace/foo/b": "baz2",
   141  		},
   142  		directory: "",
   143  		expectedFiles: []string{
   144  			"workspace/foo/a",
   145  			"workspace/foo/b",
   146  			"workspace",
   147  			"workspace/foo",
   148  			".",
   149  		},
   150  	},
   151  }
   152  
   153  func Test_RelativeFiles(t *testing.T) {
   154  	for _, test := range tests {
   155  		testDir := t.TempDir()
   156  		if err := testutil.SetupFiles(testDir, test.files); err != nil {
   157  			t.Fatalf("err setting up files: %v", err)
   158  		}
   159  		actualFiles, err := RelativeFiles(test.directory, testDir)
   160  		sort.Strings(actualFiles)
   161  		sort.Strings(test.expectedFiles)
   162  		testutil.CheckErrorAndDeepEqual(t, false, err, test.expectedFiles, actualFiles)
   163  	}
   164  }
   165  
   166  func Test_ParentDirectories(t *testing.T) {
   167  	tests := []struct {
   168  		name     string
   169  		path     string
   170  		rootDir  string
   171  		expected []string
   172  	}{
   173  		{
   174  			name:    "regular path",
   175  			path:    "/path/to/dir",
   176  			rootDir: "/",
   177  			expected: []string{
   178  				"/",
   179  				"/path",
   180  				"/path/to",
   181  			},
   182  		},
   183  		{
   184  			name:    "current directory",
   185  			path:    ".",
   186  			rootDir: "/",
   187  			expected: []string{
   188  				"/",
   189  			},
   190  		},
   191  		{
   192  			name:    "non / root directory",
   193  			path:    "/tmp/kaniko/test/another/dir",
   194  			rootDir: "/tmp/kaniko/",
   195  			expected: []string{
   196  				"/tmp/kaniko",
   197  				"/tmp/kaniko/test",
   198  				"/tmp/kaniko/test/another",
   199  			},
   200  		},
   201  		{
   202  			name:    "non / root director same path",
   203  			path:    "/tmp/123",
   204  			rootDir: "/tmp/123",
   205  			expected: []string{
   206  				"/tmp/123",
   207  			},
   208  		},
   209  		{
   210  			name:    "non / root directory path",
   211  			path:    "/tmp/120162240/kaniko",
   212  			rootDir: "/tmp/120162240",
   213  			expected: []string{
   214  				"/tmp/120162240",
   215  			},
   216  		},
   217  	}
   218  
   219  	for _, tt := range tests {
   220  		t.Run(tt.name, func(t *testing.T) {
   221  			original := config.RootDir
   222  			defer func() { config.RootDir = original }()
   223  			config.RootDir = tt.rootDir
   224  			actual := ParentDirectories(tt.path)
   225  
   226  			testutil.CheckErrorAndDeepEqual(t, false, nil, tt.expected, actual)
   227  		})
   228  	}
   229  }
   230  
   231  func Test_ParentDirectoriesWithoutLeadingSlash(t *testing.T) {
   232  	tests := []struct {
   233  		name     string
   234  		path     string
   235  		expected []string
   236  	}{
   237  		{
   238  			name: "regular path",
   239  			path: "/path/to/dir",
   240  			expected: []string{
   241  				"/",
   242  				"path",
   243  				"path/to",
   244  			},
   245  		},
   246  		{
   247  			name: "current directory",
   248  			path: ".",
   249  			expected: []string{
   250  				"/",
   251  			},
   252  		},
   253  	}
   254  
   255  	for _, tt := range tests {
   256  		t.Run(tt.name, func(t *testing.T) {
   257  			actual := ParentDirectoriesWithoutLeadingSlash(tt.path)
   258  			testutil.CheckErrorAndDeepEqual(t, false, nil, tt.expected, actual)
   259  		})
   260  	}
   261  }
   262  
   263  func Test_CheckIgnoreList(t *testing.T) {
   264  	type args struct {
   265  		path       string
   266  		ignorelist []IgnoreListEntry
   267  	}
   268  	tests := []struct {
   269  		name string
   270  		args args
   271  		want bool
   272  	}{
   273  		{
   274  			name: "file ignored",
   275  			args: args{
   276  				path:       "/foo",
   277  				ignorelist: []IgnoreListEntry{{"/foo", false}},
   278  			},
   279  			want: true,
   280  		},
   281  		{
   282  			name: "directory ignored",
   283  			args: args{
   284  				path:       "/foo/bar",
   285  				ignorelist: []IgnoreListEntry{{"/foo", false}},
   286  			},
   287  			want: true,
   288  		},
   289  		{
   290  			name: "grandparent ignored",
   291  			args: args{
   292  				path:       "/foo/bar/baz",
   293  				ignorelist: []IgnoreListEntry{{"/foo", false}},
   294  			},
   295  			want: true,
   296  		},
   297  		{
   298  			name: "sibling ignored",
   299  			args: args{
   300  				path:       "/foo/bar/baz",
   301  				ignorelist: []IgnoreListEntry{{"/foo/bat", false}},
   302  			},
   303  			want: false,
   304  		},
   305  		{
   306  			name: "prefix match only ",
   307  			args: args{
   308  				path:       "/tmp/apt-key-gpghome.xft/gpg.key",
   309  				ignorelist: []IgnoreListEntry{{"/tmp/apt-key-gpghome.*", true}},
   310  			},
   311  			want: true,
   312  		},
   313  	}
   314  	for _, tt := range tests {
   315  		t.Run(tt.name, func(t *testing.T) {
   316  			original := ignorelist
   317  			defer func() {
   318  				ignorelist = original
   319  			}()
   320  			ignorelist = tt.args.ignorelist
   321  			got := CheckIgnoreList(tt.args.path)
   322  			if got != tt.want {
   323  				t.Errorf("CheckIgnoreList() = %v, want %v", got, tt.want)
   324  			}
   325  		})
   326  	}
   327  }
   328  
   329  func TestHasFilepathPrefix(t *testing.T) {
   330  	type args struct {
   331  		path            string
   332  		prefix          string
   333  		prefixMatchOnly bool
   334  	}
   335  	tests := []struct {
   336  		name string
   337  		args args
   338  		want bool
   339  	}{
   340  		{
   341  			name: "parent",
   342  			args: args{
   343  				path:            "/foo/bar",
   344  				prefix:          "/foo",
   345  				prefixMatchOnly: false,
   346  			},
   347  			want: true,
   348  		},
   349  		{
   350  			name: "nested parent",
   351  			args: args{
   352  				path:            "/foo/bar/baz",
   353  				prefix:          "/foo/bar",
   354  				prefixMatchOnly: false,
   355  			},
   356  			want: true,
   357  		},
   358  		{
   359  			name: "sibling",
   360  			args: args{
   361  				path:            "/foo/bar",
   362  				prefix:          "/bar",
   363  				prefixMatchOnly: false,
   364  			},
   365  			want: false,
   366  		},
   367  		{
   368  			name: "nested sibling",
   369  			args: args{
   370  				path:            "/foo/bar/baz",
   371  				prefix:          "/foo/bar",
   372  				prefixMatchOnly: false,
   373  			},
   374  			want: true,
   375  		},
   376  		{
   377  			name: "name prefix",
   378  			args: args{
   379  				path:            "/foo2/bar",
   380  				prefix:          "/foo",
   381  				prefixMatchOnly: false,
   382  			},
   383  			want: false,
   384  		},
   385  		{
   386  			name: "prefix match only (volume)",
   387  			args: args{
   388  				path:            "/foo",
   389  				prefix:          "/foo",
   390  				prefixMatchOnly: true,
   391  			},
   392  			want: false,
   393  		},
   394  	}
   395  	for _, tt := range tests {
   396  		t.Run(tt.name, func(t *testing.T) {
   397  			if got := HasFilepathPrefix(tt.args.path, tt.args.prefix, tt.args.prefixMatchOnly); got != tt.want {
   398  				t.Errorf("HasFilepathPrefix() = %v, want %v", got, tt.want)
   399  			}
   400  		})
   401  	}
   402  }
   403  
   404  func BenchmarkHasFilepathPrefix(b *testing.B) {
   405  	tests := []struct {
   406  		path            string
   407  		prefix          string
   408  		prefixMatchOnly bool
   409  	}{
   410  		{
   411  			path:            "/foo/bar",
   412  			prefix:          "/foo",
   413  			prefixMatchOnly: true,
   414  		},
   415  		{
   416  			path:            "/foo/bar/baz",
   417  			prefix:          "/foo",
   418  			prefixMatchOnly: true,
   419  		},
   420  		{
   421  			path:            "/foo/bar/baz/foo",
   422  			prefix:          "/foo",
   423  			prefixMatchOnly: true,
   424  		},
   425  		{
   426  			path:            "/foo/bar/baz/foo/foobar",
   427  			prefix:          "/foo",
   428  			prefixMatchOnly: true,
   429  		},
   430  		{
   431  			path:            "/foo/bar",
   432  			prefix:          "/foo/bar",
   433  			prefixMatchOnly: true,
   434  		},
   435  		{
   436  			path:            "/foo/bar/baz",
   437  			prefix:          "/foo/bar",
   438  			prefixMatchOnly: true,
   439  		},
   440  		{
   441  			path:            "/foo/bar/baz/foo",
   442  			prefix:          "/foo/bar",
   443  			prefixMatchOnly: true,
   444  		},
   445  		{
   446  			path:            "/foo/bar/baz/foo/foobar",
   447  			prefix:          "/foo/bar",
   448  			prefixMatchOnly: true,
   449  		},
   450  		{
   451  			path:            "/foo/bar",
   452  			prefix:          "/foo/bar/baz",
   453  			prefixMatchOnly: true,
   454  		},
   455  		{
   456  			path:            "/foo/bar/baz",
   457  			prefix:          "/foo/bar/baz",
   458  			prefixMatchOnly: true,
   459  		},
   460  		{
   461  			path:            "/foo/bar/baz/foo",
   462  			prefix:          "/foo/bar/baz",
   463  			prefixMatchOnly: true,
   464  		},
   465  		{
   466  			path:            "/foo/bar/baz/foo/foobar",
   467  			prefix:          "/foo/bar/baz",
   468  			prefixMatchOnly: true,
   469  		},
   470  	}
   471  	for _, ts := range tests {
   472  		name := fmt.Sprint("PathDepth=", strings.Count(ts.path, "/"), ",PrefixDepth=", strings.Count(ts.prefix, "/"))
   473  		b.Run(name, func(b *testing.B) {
   474  			b.ReportAllocs()
   475  			for i := 0; i < b.N; i++ {
   476  				HasFilepathPrefix(ts.path, ts.prefix, ts.prefixMatchOnly)
   477  			}
   478  		})
   479  	}
   480  }
   481  
   482  type checker func(root string, t *testing.T)
   483  
   484  func fileExists(p string) checker {
   485  	return func(root string, t *testing.T) {
   486  		_, err := os.Stat(filepath.Join(root, p))
   487  		if err != nil {
   488  			t.Fatalf("File %s does not exist", filepath.Join(root, p))
   489  		}
   490  	}
   491  }
   492  
   493  func fileMatches(p string, c []byte) checker {
   494  	return func(root string, t *testing.T) {
   495  		actual, err := os.ReadFile(filepath.Join(root, p))
   496  		if err != nil {
   497  			t.Fatalf("error reading file: %s", p)
   498  		}
   499  		if !reflect.DeepEqual(actual, c) {
   500  			t.Errorf("file contents do not match. %v!=%v", actual, c)
   501  		}
   502  	}
   503  }
   504  
   505  func timesMatch(p string, fTime time.Time) checker {
   506  	return func(root string, t *testing.T) {
   507  		fi, err := os.Stat(filepath.Join(root, p))
   508  		if err != nil {
   509  			t.Fatalf("error statting file %s", p)
   510  		}
   511  
   512  		if fi.ModTime().UTC() != fTime.UTC() {
   513  			t.Errorf("Expected modtime to equal %v but was %v", fTime, fi.ModTime())
   514  		}
   515  	}
   516  }
   517  
   518  func permissionsMatch(p string, perms os.FileMode) checker {
   519  	return func(root string, t *testing.T) {
   520  		fi, err := os.Stat(filepath.Join(root, p))
   521  		if err != nil {
   522  			t.Fatalf("error statting file %s", p)
   523  		}
   524  		if fi.Mode() != perms {
   525  			t.Errorf("Permissions do not match. %s != %s", fi.Mode(), perms)
   526  		}
   527  	}
   528  }
   529  
   530  func linkPointsTo(src, dst string) checker {
   531  	return func(root string, t *testing.T) {
   532  		link := filepath.Join(root, src)
   533  		got, err := os.Readlink(link)
   534  		if err != nil {
   535  			t.Fatalf("error reading link %s: %s", link, err)
   536  		}
   537  		if got != dst {
   538  			t.Errorf("link destination does not match: %s != %s", got, dst)
   539  		}
   540  	}
   541  }
   542  
   543  func filesAreHardlinks(first, second string) checker {
   544  	return func(root string, t *testing.T) {
   545  		fi1, err := os.Stat(filepath.Join(root, first))
   546  		if err != nil {
   547  			t.Fatalf("error getting file %s", first)
   548  		}
   549  		fi2, err := os.Stat(filepath.Join(root, second))
   550  		if err != nil {
   551  			t.Fatalf("error getting file %s", second)
   552  		}
   553  		stat1 := getSyscallStatT(fi1)
   554  		stat2 := getSyscallStatT(fi2)
   555  		if stat1.Ino != stat2.Ino {
   556  			t.Errorf("%s and %s aren't hardlinks as they dont' have the same inode", first, second)
   557  		}
   558  	}
   559  }
   560  
   561  func fileHeader(name string, contents string, mode int64, fTime time.Time) *tar.Header {
   562  	return &tar.Header{
   563  		Name:       name,
   564  		Size:       int64(len(contents)),
   565  		Mode:       mode,
   566  		Typeflag:   tar.TypeReg,
   567  		Uid:        os.Getuid(),
   568  		Gid:        os.Getgid(),
   569  		AccessTime: fTime,
   570  		ModTime:    fTime,
   571  	}
   572  }
   573  
   574  func linkHeader(name, linkname string) *tar.Header {
   575  	return &tar.Header{
   576  		Name:     name,
   577  		Size:     0,
   578  		Typeflag: tar.TypeSymlink,
   579  		Linkname: linkname,
   580  	}
   581  }
   582  
   583  func hardlinkHeader(name, linkname string) *tar.Header {
   584  	return &tar.Header{
   585  		Name:     name,
   586  		Size:     0,
   587  		Typeflag: tar.TypeLink,
   588  		Linkname: linkname,
   589  	}
   590  }
   591  
   592  func dirHeader(name string, mode int64) *tar.Header {
   593  	return &tar.Header{
   594  		Name:     name,
   595  		Size:     0,
   596  		Typeflag: tar.TypeDir,
   597  		Mode:     mode,
   598  		Uid:      os.Getuid(),
   599  		Gid:      os.Getgid(),
   600  	}
   601  }
   602  
   603  func createUncompressedTar(fileContents map[string]string, tarFileName, testDir string) error {
   604  	if err := testutil.SetupFiles(testDir, fileContents); err != nil {
   605  		return err
   606  	}
   607  	tarFile, err := os.Create(filepath.Join(testDir, tarFileName))
   608  	if err != nil {
   609  		return err
   610  	}
   611  	t := NewTar(tarFile)
   612  	defer t.Close()
   613  	for file := range fileContents {
   614  		filePath := filepath.Join(testDir, file)
   615  		if err := t.AddFileToTar(filePath); err != nil {
   616  			return err
   617  		}
   618  	}
   619  	return nil
   620  }
   621  
   622  func Test_UnTar(t *testing.T) {
   623  	tcs := []struct {
   624  		name             string
   625  		setupTarContents map[string]string
   626  		tarFileName      string
   627  		destination      string
   628  		expectedFileList []string
   629  		errorExpected    bool
   630  	}{
   631  		{
   632  			name: "multfile tar",
   633  			setupTarContents: map[string]string{
   634  				"foo/file1": "hello World",
   635  				"bar/file1": "hello World",
   636  				"bar/file2": "hello World",
   637  				"file1":     "hello World",
   638  			},
   639  			tarFileName:      "test.tar",
   640  			destination:      "/",
   641  			expectedFileList: []string{"foo/file1", "bar/file1", "bar/file2", "file1"},
   642  			errorExpected:    false,
   643  		},
   644  		{
   645  			name:             "empty tar",
   646  			setupTarContents: map[string]string{},
   647  			tarFileName:      "test.tar",
   648  			destination:      "/",
   649  			expectedFileList: nil,
   650  			errorExpected:    false,
   651  		},
   652  	}
   653  	for _, tc := range tcs {
   654  		t.Run(tc.name, func(t *testing.T) {
   655  			testDir := t.TempDir()
   656  			if err := createUncompressedTar(tc.setupTarContents, tc.tarFileName, testDir); err != nil {
   657  				t.Fatal(err)
   658  			}
   659  			file, err := os.Open(filepath.Join(testDir, tc.tarFileName))
   660  			if err != nil {
   661  				t.Fatal(err)
   662  			}
   663  			fileList, err := UnTar(file, tc.destination)
   664  			if err != nil {
   665  				t.Fatal(err)
   666  			}
   667  			// update expectedFileList to take into factor temp directory
   668  			for i, file := range tc.expectedFileList {
   669  				tc.expectedFileList[i] = filepath.Join(testDir, file)
   670  			}
   671  			// sort both slices to ensure objects are in the same order for deep equals
   672  			sort.Strings(tc.expectedFileList)
   673  			sort.Strings(fileList)
   674  			testutil.CheckErrorAndDeepEqual(t, tc.errorExpected, err, tc.expectedFileList, fileList)
   675  		})
   676  	}
   677  }
   678  
   679  func TestExtractFile(t *testing.T) {
   680  	type tc struct {
   681  		name     string
   682  		hdrs     []*tar.Header
   683  		tmpdir   string
   684  		contents []byte
   685  		checkers []checker
   686  	}
   687  
   688  	defaultTestTime, err := time.Parse(time.RFC3339, "1912-06-23T00:00:00Z")
   689  	if err != nil {
   690  		t.Fatal(err)
   691  	}
   692  
   693  	tcs := []tc{
   694  		{
   695  			name:     "normal file",
   696  			contents: []byte("helloworld"),
   697  			hdrs:     []*tar.Header{fileHeader("./bar", "helloworld", 0o644, defaultTestTime)},
   698  			checkers: []checker{
   699  				fileExists("/bar"),
   700  				fileMatches("/bar", []byte("helloworld")),
   701  				permissionsMatch("/bar", 0o644),
   702  				timesMatch("/bar", defaultTestTime),
   703  			},
   704  		},
   705  		{
   706  			name:     "normal file, directory does not exist",
   707  			contents: []byte("helloworld"),
   708  			hdrs:     []*tar.Header{fileHeader("./foo/bar", "helloworld", 0o644, defaultTestTime)},
   709  			checkers: []checker{
   710  				fileExists("/foo/bar"),
   711  				fileMatches("/foo/bar", []byte("helloworld")),
   712  				permissionsMatch("/foo/bar", 0o644),
   713  				permissionsMatch("/foo", 0o755|os.ModeDir),
   714  			},
   715  		},
   716  		{
   717  			name:     "normal file, directory is created after",
   718  			contents: []byte("helloworld"),
   719  			hdrs: []*tar.Header{
   720  				fileHeader("./foo/bar", "helloworld", 0o644, defaultTestTime),
   721  				dirHeader("./foo", 0o722),
   722  			},
   723  			checkers: []checker{
   724  				fileExists("/foo/bar"),
   725  				fileMatches("/foo/bar", []byte("helloworld")),
   726  				permissionsMatch("/foo/bar", 0o644),
   727  				permissionsMatch("/foo", 0o722|os.ModeDir),
   728  			},
   729  		},
   730  		{
   731  			name: "symlink",
   732  			hdrs: []*tar.Header{linkHeader("./bar", "bar/bat")},
   733  			checkers: []checker{
   734  				linkPointsTo("/bar", "bar/bat"),
   735  			},
   736  		},
   737  		{
   738  			name: "symlink relative path",
   739  			hdrs: []*tar.Header{linkHeader("./bar", "./foo/bar/baz")},
   740  			checkers: []checker{
   741  				linkPointsTo("/bar", "./foo/bar/baz"),
   742  			},
   743  		},
   744  		{
   745  			name: "symlink parent does not exist",
   746  			hdrs: []*tar.Header{linkHeader("./foo/bar/baz", "../../bat")},
   747  			checkers: []checker{
   748  				linkPointsTo("/foo/bar/baz", "../../bat"),
   749  			},
   750  		},
   751  		{
   752  			name: "symlink parent does not exist 2",
   753  			hdrs: []*tar.Header{linkHeader("./foo/bar/baz", "../../bat")},
   754  			checkers: []checker{
   755  				linkPointsTo("/foo/bar/baz", "../../bat"),
   756  				permissionsMatch("/foo", 0o755|os.ModeDir),
   757  				permissionsMatch("/foo/bar", 0o755|os.ModeDir),
   758  			},
   759  		},
   760  		{
   761  			name:   "hardlink",
   762  			tmpdir: "/tmp/hardlink",
   763  			hdrs: []*tar.Header{
   764  				fileHeader("/bin/gzip", "gzip-binary", 0o751, defaultTestTime),
   765  				hardlinkHeader("/bin/uncompress", "/bin/gzip"),
   766  			},
   767  			checkers: []checker{
   768  				fileExists("/bin/gzip"),
   769  				filesAreHardlinks("/bin/uncompress", "/bin/gzip"),
   770  			},
   771  		},
   772  		{
   773  			name:     "file with setuid bit",
   774  			contents: []byte("helloworld"),
   775  			hdrs:     []*tar.Header{fileHeader("./bar", "helloworld", 0o4644, defaultTestTime)},
   776  			checkers: []checker{
   777  				fileExists("/bar"),
   778  				fileMatches("/bar", []byte("helloworld")),
   779  				permissionsMatch("/bar", 0o644|os.ModeSetuid),
   780  			},
   781  		},
   782  		{
   783  			name:     "dir with sticky bit",
   784  			contents: []byte("helloworld"),
   785  			hdrs: []*tar.Header{
   786  				dirHeader("./foo", 0o1755),
   787  				fileHeader("./foo/bar", "helloworld", 0o644, defaultTestTime),
   788  			},
   789  			checkers: []checker{
   790  				fileExists("/foo/bar"),
   791  				fileMatches("/foo/bar", []byte("helloworld")),
   792  				permissionsMatch("/foo/bar", 0o644),
   793  				permissionsMatch("/foo", 0o755|os.ModeDir|os.ModeSticky),
   794  			},
   795  		},
   796  	}
   797  
   798  	for _, tc := range tcs {
   799  		t.Run(tc.name, func(t *testing.T) {
   800  			tc := tc
   801  			t.Parallel()
   802  			r := ""
   803  
   804  			if tc.tmpdir != "" {
   805  				r = tc.tmpdir
   806  			} else {
   807  				r = t.TempDir()
   808  			}
   809  			defer os.RemoveAll(r)
   810  
   811  			for _, hdr := range tc.hdrs {
   812  				if err := ExtractFile(r, hdr, filepath.Clean(hdr.Name), bytes.NewReader(tc.contents)); err != nil {
   813  					t.Fatal(err)
   814  				}
   815  			}
   816  			for _, checker := range tc.checkers {
   817  				checker(r, t)
   818  			}
   819  		})
   820  	}
   821  }
   822  
   823  func TestCopySymlink(t *testing.T) {
   824  	type tc struct {
   825  		name       string
   826  		linkTarget string
   827  		dest       string
   828  		beforeLink func(r string) error
   829  	}
   830  
   831  	tcs := []tc{{
   832  		name:       "absolute symlink",
   833  		linkTarget: "/abs/dest",
   834  	}, {
   835  		name:       "relative symlink",
   836  		linkTarget: "rel",
   837  	}, {
   838  		name:       "symlink copy overwrites existing file",
   839  		linkTarget: "/abs/dest",
   840  		dest:       "overwrite_me",
   841  		beforeLink: func(r string) error {
   842  			return os.WriteFile(filepath.Join(r, "overwrite_me"), nil, 0o644)
   843  		},
   844  	}}
   845  
   846  	for _, tc := range tcs {
   847  		t.Run(tc.name, func(t *testing.T) {
   848  			tc := tc
   849  			t.Parallel()
   850  			r := t.TempDir()
   851  			os.MkdirAll(filepath.Join(r, filepath.Dir(tc.linkTarget)), 0o777)
   852  			tc.linkTarget = filepath.Join(r, tc.linkTarget)
   853  			os.WriteFile(tc.linkTarget, nil, 0o644)
   854  
   855  			if tc.beforeLink != nil {
   856  				if err := tc.beforeLink(r); err != nil {
   857  					t.Fatal(err)
   858  				}
   859  			}
   860  			link := filepath.Join(r, "link")
   861  			dest := filepath.Join(r, "copy")
   862  			if tc.dest != "" {
   863  				dest = filepath.Join(r, tc.dest)
   864  			}
   865  			if err := os.Symlink(tc.linkTarget, link); err != nil {
   866  				t.Fatal(err)
   867  			}
   868  			if _, err := CopySymlink(link, dest, FileContext{}); err != nil {
   869  				t.Fatal(err)
   870  			}
   871  			if _, err := os.Lstat(dest); err != nil {
   872  				t.Fatalf("error reading link %s: %s", link, err)
   873  			}
   874  		})
   875  	}
   876  }
   877  
   878  func Test_childDirInSkiplist(t *testing.T) {
   879  	type args struct {
   880  		path       string
   881  		ignorelist []IgnoreListEntry
   882  	}
   883  	tests := []struct {
   884  		name string
   885  		args args
   886  		want bool
   887  	}{
   888  		{
   889  			name: "not in ignorelist",
   890  			args: args{
   891  				path: "/foo",
   892  			},
   893  			want: false,
   894  		},
   895  		{
   896  			name: "child in ignorelist",
   897  			args: args{
   898  				path: "/foo",
   899  				ignorelist: []IgnoreListEntry{
   900  					{
   901  						Path: "/foo/bar",
   902  					},
   903  				},
   904  			},
   905  			want: true,
   906  		},
   907  	}
   908  	oldIgnoreList := ignorelist
   909  	defer func() {
   910  		ignorelist = oldIgnoreList
   911  	}()
   912  
   913  	for _, tt := range tests {
   914  		t.Run(tt.name, func(t *testing.T) {
   915  			ignorelist = tt.args.ignorelist
   916  			if got := childDirInIgnoreList(tt.args.path); got != tt.want {
   917  				t.Errorf("childDirInIgnoreList() = %v, want %v", got, tt.want)
   918  			}
   919  		})
   920  	}
   921  }
   922  
   923  func Test_correctDockerignoreFileIsUsed(t *testing.T) {
   924  	type args struct {
   925  		dockerfilepath string
   926  		buildcontext   string
   927  		excluded       []string
   928  		included       []string
   929  	}
   930  	tests := []struct {
   931  		name string
   932  		args args
   933  	}{
   934  		{
   935  			name: "relative dockerfile used",
   936  			args: args{
   937  				dockerfilepath: "../../integration/dockerfiles/Dockerfile_dockerignore_relative",
   938  				buildcontext:   "../../integration/",
   939  				excluded:       []string{"ignore_relative/bar"},
   940  				included:       []string{"ignore_relative/foo", "ignore/bar"},
   941  			},
   942  		},
   943  		{
   944  			name: "context dockerfile is used",
   945  			args: args{
   946  				dockerfilepath: "../../integration/dockerfiles/Dockerfile_test_dockerignore",
   947  				buildcontext:   "../../integration/",
   948  				excluded:       []string{"ignore/bar"},
   949  				included:       []string{"ignore/foo", "ignore_relative/bar"},
   950  			},
   951  		},
   952  	}
   953  	for _, tt := range tests {
   954  		fileContext, err := NewFileContextFromDockerfile(tt.args.dockerfilepath, tt.args.buildcontext)
   955  		if err != nil {
   956  			t.Fatal(err)
   957  		}
   958  		for _, excl := range tt.args.excluded {
   959  			t.Run(tt.name+" to exclude "+excl, func(t *testing.T) {
   960  				if !fileContext.ExcludesFile(excl) {
   961  					t.Errorf("'%v' not excluded", excl)
   962  				}
   963  			})
   964  		}
   965  		for _, incl := range tt.args.included {
   966  			t.Run(tt.name+" to include "+incl, func(t *testing.T) {
   967  				if fileContext.ExcludesFile(incl) {
   968  					t.Errorf("'%v' not included", incl)
   969  				}
   970  			})
   971  		}
   972  	}
   973  }
   974  
   975  func Test_CopyFile_skips_self(t *testing.T) {
   976  	t.Parallel()
   977  	tempDir := t.TempDir()
   978  
   979  	tempFile := filepath.Join(tempDir, "foo")
   980  	expected := "bar"
   981  
   982  	if err := os.WriteFile(
   983  		tempFile,
   984  		[]byte(expected),
   985  		0o755,
   986  	); err != nil {
   987  		t.Fatal(err)
   988  	}
   989  
   990  	ignored, err := CopyFile(tempFile, tempFile, FileContext{}, DoNotChangeUID, DoNotChangeGID, fs.FileMode(0o600), true)
   991  	if err != nil {
   992  		t.Fatal(err)
   993  	}
   994  
   995  	if ignored {
   996  		t.Fatal("expected file to NOT be ignored")
   997  	}
   998  
   999  	// Ensure file has expected contents
  1000  	actualData, err := os.ReadFile(tempFile)
  1001  	if err != nil {
  1002  		t.Fatal(err)
  1003  	}
  1004  
  1005  	if actual := string(actualData); actual != expected {
  1006  		t.Fatalf("expected file contents to be %q, but got %q", expected, actual)
  1007  	}
  1008  }
  1009  
  1010  func fakeExtract(_ string, _ *tar.Header, _ string, _ io.Reader) error {
  1011  	return nil
  1012  }
  1013  
  1014  func Test_GetFSFromLayers_with_whiteouts_include_whiteout_enabled(t *testing.T) {
  1015  	resetMountInfoFile := provideEmptyMountinfoFile()
  1016  	defer resetMountInfoFile()
  1017  
  1018  	ctrl := gomock.NewController(t)
  1019  
  1020  	root := t.TempDir()
  1021  	// Write a whiteout path
  1022  	d1 := []byte("Hello World\n")
  1023  	if err := os.WriteFile(filepath.Join(root, "foobar"), d1, 0o644); err != nil {
  1024  		t.Fatal(err)
  1025  	}
  1026  
  1027  	opts := []FSOpt{
  1028  		// I'd rather use the real func (util.ExtractFile)
  1029  		// but you have to be root to chown
  1030  		ExtractFunc(fakeExtract),
  1031  		IncludeWhiteout(),
  1032  	}
  1033  
  1034  	expectErr := false
  1035  
  1036  	f := func(expectedFiles []string, tw *tar.Writer) {
  1037  		for _, f := range expectedFiles {
  1038  			f := strings.TrimPrefix(strings.TrimPrefix(f, root), "/")
  1039  
  1040  			hdr := &tar.Header{
  1041  				Name: f,
  1042  				Mode: 0o644,
  1043  				Size: int64(len("Hello World\n")),
  1044  			}
  1045  
  1046  			if err := tw.WriteHeader(hdr); err != nil {
  1047  				t.Fatal(err)
  1048  			}
  1049  
  1050  			if _, err := tw.Write([]byte("Hello World\n")); err != nil {
  1051  				t.Fatal(err)
  1052  			}
  1053  		}
  1054  
  1055  		if err := tw.Close(); err != nil {
  1056  			t.Fatal(err)
  1057  		}
  1058  	}
  1059  
  1060  	expectedFiles := []string{
  1061  		filepath.Join(root, "foobar"),
  1062  	}
  1063  
  1064  	buf := new(bytes.Buffer)
  1065  	tw := tar.NewWriter(buf)
  1066  
  1067  	f(expectedFiles, tw)
  1068  
  1069  	mockLayer := mockv1.NewMockLayer(ctrl)
  1070  	mockLayer.EXPECT().MediaType().Return(types.OCILayer, nil)
  1071  
  1072  	rc := io.NopCloser(buf)
  1073  	mockLayer.EXPECT().Uncompressed().Return(rc, nil)
  1074  
  1075  	secondLayerFiles := []string{
  1076  		filepath.Join(root, ".wh.foobar"),
  1077  	}
  1078  
  1079  	buf = new(bytes.Buffer)
  1080  	tw = tar.NewWriter(buf)
  1081  
  1082  	f(secondLayerFiles, tw)
  1083  
  1084  	mockLayer2 := mockv1.NewMockLayer(ctrl)
  1085  	mockLayer2.EXPECT().MediaType().Return(types.OCILayer, nil)
  1086  
  1087  	rc = io.NopCloser(buf)
  1088  	mockLayer2.EXPECT().Uncompressed().Return(rc, nil)
  1089  
  1090  	layers := []v1.Layer{
  1091  		mockLayer,
  1092  		mockLayer2,
  1093  	}
  1094  
  1095  	expectedFiles = append(expectedFiles, secondLayerFiles...)
  1096  
  1097  	actualFiles, err := GetFSFromLayers(root, layers, opts...)
  1098  
  1099  	assertGetFSFromLayers(
  1100  		t,
  1101  		actualFiles,
  1102  		expectedFiles,
  1103  		err,
  1104  		expectErr,
  1105  	)
  1106  	// Make sure whiteout files are removed form the root.
  1107  	_, err = os.Lstat(filepath.Join(root, "foobar"))
  1108  	if err == nil || !os.IsNotExist(err) {
  1109  		t.Errorf("expected whiteout foobar file to be deleted. However found it.")
  1110  	}
  1111  }
  1112  
  1113  func provideEmptyMountinfoFile() func() {
  1114  	// Provide empty mountinfo file to prevent /tmp from ending up in ignore list on
  1115  	// distributions with /tmp mountpoint. Otherwise, tests expecting operations in /tmp
  1116  	// can fail.
  1117  	config.MountInfoPath = "/dev/null"
  1118  	return func() {
  1119  		config.MountInfoPath = constants.MountInfoPath
  1120  	}
  1121  }
  1122  
  1123  func Test_GetFSFromLayers_with_whiteouts_include_whiteout_disabled(t *testing.T) {
  1124  	resetMountInfoFile := provideEmptyMountinfoFile()
  1125  	defer resetMountInfoFile()
  1126  
  1127  	ctrl := gomock.NewController(t)
  1128  
  1129  	root := t.TempDir()
  1130  	// Write a whiteout path
  1131  	d1 := []byte("Hello World\n")
  1132  	if err := os.WriteFile(filepath.Join(root, "foobar"), d1, 0o644); err != nil {
  1133  		t.Fatal(err)
  1134  	}
  1135  
  1136  	opts := []FSOpt{
  1137  		// I'd rather use the real func (util.ExtractFile)
  1138  		// but you have to be root to chown
  1139  		ExtractFunc(fakeExtract),
  1140  	}
  1141  
  1142  	expectErr := false
  1143  
  1144  	f := func(expectedFiles []string, tw *tar.Writer) {
  1145  		for _, f := range expectedFiles {
  1146  			f := strings.TrimPrefix(strings.TrimPrefix(f, root), "/")
  1147  
  1148  			hdr := &tar.Header{
  1149  				Name: f,
  1150  				Mode: 0o644,
  1151  				Size: int64(len("Hello world\n")),
  1152  			}
  1153  
  1154  			if err := tw.WriteHeader(hdr); err != nil {
  1155  				t.Fatal(err)
  1156  			}
  1157  
  1158  			if _, err := tw.Write([]byte("Hello world\n")); err != nil {
  1159  				t.Fatal(err)
  1160  			}
  1161  		}
  1162  
  1163  		if err := tw.Close(); err != nil {
  1164  			t.Fatal(err)
  1165  		}
  1166  	}
  1167  
  1168  	expectedFiles := []string{
  1169  		filepath.Join(root, "foobar"),
  1170  	}
  1171  
  1172  	buf := new(bytes.Buffer)
  1173  	tw := tar.NewWriter(buf)
  1174  
  1175  	f(expectedFiles, tw)
  1176  
  1177  	mockLayer := mockv1.NewMockLayer(ctrl)
  1178  	mockLayer.EXPECT().MediaType().Return(types.OCILayer, nil)
  1179  	layerFiles := []string{
  1180  		filepath.Join(root, "foobar"),
  1181  	}
  1182  	buf = new(bytes.Buffer)
  1183  	tw = tar.NewWriter(buf)
  1184  
  1185  	f(layerFiles, tw)
  1186  
  1187  	rc := io.NopCloser(buf)
  1188  	mockLayer.EXPECT().Uncompressed().Return(rc, nil)
  1189  
  1190  	secondLayerFiles := []string{
  1191  		filepath.Join(root, ".wh.foobar"),
  1192  	}
  1193  
  1194  	buf = new(bytes.Buffer)
  1195  	tw = tar.NewWriter(buf)
  1196  
  1197  	f(secondLayerFiles, tw)
  1198  
  1199  	mockLayer2 := mockv1.NewMockLayer(ctrl)
  1200  	mockLayer2.EXPECT().MediaType().Return(types.OCILayer, nil)
  1201  
  1202  	rc = io.NopCloser(buf)
  1203  	mockLayer2.EXPECT().Uncompressed().Return(rc, nil)
  1204  
  1205  	layers := []v1.Layer{
  1206  		mockLayer,
  1207  		mockLayer2,
  1208  	}
  1209  
  1210  	actualFiles, err := GetFSFromLayers(root, layers, opts...)
  1211  
  1212  	assertGetFSFromLayers(
  1213  		t,
  1214  		actualFiles,
  1215  		expectedFiles,
  1216  		err,
  1217  		expectErr,
  1218  	)
  1219  	// Make sure whiteout files are removed form the root.
  1220  	_, err = os.Lstat(filepath.Join(root, "foobar"))
  1221  	if err == nil || !os.IsNotExist(err) {
  1222  		t.Errorf("expected whiteout foobar file to be deleted. However found it.")
  1223  	}
  1224  }
  1225  
  1226  func Test_GetFSFromLayers_ignorelist(t *testing.T) {
  1227  	resetMountInfoFile := provideEmptyMountinfoFile()
  1228  	defer resetMountInfoFile()
  1229  
  1230  	ctrl := gomock.NewController(t)
  1231  
  1232  	root := t.TempDir()
  1233  	// Write a whiteout path
  1234  	fileContents := []byte("Hello World\n")
  1235  	if err := os.Mkdir(filepath.Join(root, "testdir"), 0o775); err != nil {
  1236  		t.Fatal(err)
  1237  	}
  1238  
  1239  	opts := []FSOpt{
  1240  		// I'd rather use the real func (util.ExtractFile)
  1241  		// but you have to be root to chown
  1242  		ExtractFunc(fakeExtract),
  1243  		IncludeWhiteout(),
  1244  	}
  1245  
  1246  	f := func(expectedFiles []string, tw *tar.Writer) {
  1247  		for _, f := range expectedFiles {
  1248  			f := strings.TrimPrefix(strings.TrimPrefix(f, root), "/")
  1249  
  1250  			hdr := &tar.Header{
  1251  				Name: f,
  1252  				Mode: 0o644,
  1253  				Size: int64(len(string(fileContents))),
  1254  			}
  1255  
  1256  			if err := tw.WriteHeader(hdr); err != nil {
  1257  				t.Fatal(err)
  1258  			}
  1259  
  1260  			if _, err := tw.Write(fileContents); err != nil {
  1261  				t.Fatal(err)
  1262  			}
  1263  		}
  1264  
  1265  		if err := tw.Close(); err != nil {
  1266  			t.Fatal(err)
  1267  		}
  1268  	}
  1269  
  1270  	// first, testdir is not in ignorelist, so it should be deleted
  1271  	expectedFiles := []string{
  1272  		filepath.Join(root, ".wh.testdir"),
  1273  		filepath.Join(root, "testdir", "file"),
  1274  		filepath.Join(root, "other-file"),
  1275  	}
  1276  
  1277  	buf := new(bytes.Buffer)
  1278  	tw := tar.NewWriter(buf)
  1279  
  1280  	f(expectedFiles, tw)
  1281  
  1282  	mockLayer := mockv1.NewMockLayer(ctrl)
  1283  	mockLayer.EXPECT().MediaType().Return(types.OCILayer, nil)
  1284  	layerFiles := []string{
  1285  		filepath.Join(root, ".wh.testdir"),
  1286  		filepath.Join(root, "testdir", "file"),
  1287  		filepath.Join(root, "other-file"),
  1288  	}
  1289  	buf = new(bytes.Buffer)
  1290  	tw = tar.NewWriter(buf)
  1291  
  1292  	f(layerFiles, tw)
  1293  
  1294  	rc := io.NopCloser(buf)
  1295  	mockLayer.EXPECT().Uncompressed().Return(rc, nil)
  1296  
  1297  	layers := []v1.Layer{
  1298  		mockLayer,
  1299  	}
  1300  
  1301  	actualFiles, err := GetFSFromLayers(root, layers, opts...)
  1302  	assertGetFSFromLayers(
  1303  		t,
  1304  		actualFiles,
  1305  		expectedFiles,
  1306  		err,
  1307  		false,
  1308  	)
  1309  
  1310  	// Make sure whiteout files are removed form the root.
  1311  	_, err = os.Lstat(filepath.Join(root, "testdir"))
  1312  	if err == nil || !os.IsNotExist(err) {
  1313  		t.Errorf("expected testdir to be deleted. However found it.")
  1314  	}
  1315  
  1316  	// second, testdir is in ignorelist, so it should not be deleted
  1317  	original := append([]IgnoreListEntry{}, defaultIgnoreList...)
  1318  	defer func() {
  1319  		defaultIgnoreList = original
  1320  	}()
  1321  	defaultIgnoreList = append(defaultIgnoreList, IgnoreListEntry{
  1322  		Path: filepath.Join(root, "testdir"),
  1323  	})
  1324  	if err := os.Mkdir(filepath.Join(root, "testdir"), 0o775); err != nil {
  1325  		t.Fatal(err)
  1326  	}
  1327  
  1328  	expectedFiles = []string{
  1329  		filepath.Join(root, "other-file"),
  1330  	}
  1331  
  1332  	buf = new(bytes.Buffer)
  1333  	tw = tar.NewWriter(buf)
  1334  
  1335  	f(expectedFiles, tw)
  1336  
  1337  	mockLayer = mockv1.NewMockLayer(ctrl)
  1338  	mockLayer.EXPECT().MediaType().Return(types.OCILayer, nil)
  1339  	layerFiles = []string{
  1340  		filepath.Join(root, ".wh.testdir"),
  1341  		filepath.Join(root, "other-file"),
  1342  	}
  1343  	buf = new(bytes.Buffer)
  1344  	tw = tar.NewWriter(buf)
  1345  
  1346  	f(layerFiles, tw)
  1347  
  1348  	rc = io.NopCloser(buf)
  1349  	mockLayer.EXPECT().Uncompressed().Return(rc, nil)
  1350  
  1351  	layers = []v1.Layer{
  1352  		mockLayer,
  1353  	}
  1354  
  1355  	actualFiles, err = GetFSFromLayers(root, layers, opts...)
  1356  	assertGetFSFromLayers(
  1357  		t,
  1358  		actualFiles,
  1359  		expectedFiles,
  1360  		err,
  1361  		false,
  1362  	)
  1363  
  1364  	// Make sure testdir still exists.
  1365  	_, err = os.Lstat(filepath.Join(root, "testdir"))
  1366  	if err != nil {
  1367  		t.Errorf("expected testdir to exist, but could not Lstat it: %v", err)
  1368  	}
  1369  }
  1370  
  1371  func Test_GetFSFromLayers(t *testing.T) {
  1372  	ctrl := gomock.NewController(t)
  1373  
  1374  	root := t.TempDir()
  1375  
  1376  	opts := []FSOpt{
  1377  		// I'd rather use the real func (util.ExtractFile)
  1378  		// but you have to be root to chown
  1379  		ExtractFunc(fakeExtract),
  1380  	}
  1381  
  1382  	expectErr := false
  1383  	expectedFiles := []string{
  1384  		filepath.Join(root, "foobar"),
  1385  	}
  1386  
  1387  	buf := new(bytes.Buffer)
  1388  	tw := tar.NewWriter(buf)
  1389  
  1390  	for _, f := range expectedFiles {
  1391  		f := strings.TrimPrefix(strings.TrimPrefix(f, root), "/")
  1392  
  1393  		hdr := &tar.Header{
  1394  			Name: f,
  1395  			Mode: 0o644,
  1396  			Size: int64(len("Hello world\n")),
  1397  		}
  1398  
  1399  		if err := tw.WriteHeader(hdr); err != nil {
  1400  			t.Fatal(err)
  1401  		}
  1402  
  1403  		if _, err := tw.Write([]byte("Hello world\n")); err != nil {
  1404  			t.Fatal(err)
  1405  		}
  1406  	}
  1407  
  1408  	if err := tw.Close(); err != nil {
  1409  		t.Fatal(err)
  1410  	}
  1411  
  1412  	mockLayer := mockv1.NewMockLayer(ctrl)
  1413  	mockLayer.EXPECT().MediaType().Return(types.OCILayer, nil)
  1414  
  1415  	rc := io.NopCloser(buf)
  1416  	mockLayer.EXPECT().Uncompressed().Return(rc, nil)
  1417  
  1418  	layers := []v1.Layer{
  1419  		mockLayer,
  1420  	}
  1421  
  1422  	actualFiles, err := GetFSFromLayers(root, layers, opts...)
  1423  
  1424  	assertGetFSFromLayers(
  1425  		t,
  1426  		actualFiles,
  1427  		expectedFiles,
  1428  		err,
  1429  		expectErr,
  1430  	)
  1431  }
  1432  
  1433  func assertGetFSFromLayers(
  1434  	t *testing.T,
  1435  	actualFiles []string,
  1436  	expectedFiles []string,
  1437  	err error,
  1438  	expectErr bool, //nolint:unparam
  1439  ) {
  1440  	t.Helper()
  1441  	if !expectErr && err != nil {
  1442  		t.Error(err)
  1443  		t.FailNow()
  1444  	} else if expectErr && err == nil {
  1445  		t.Error("expected err to not be nil")
  1446  		t.FailNow()
  1447  	}
  1448  
  1449  	if len(actualFiles) != len(expectedFiles) {
  1450  		t.Errorf("expected %s to equal %s", actualFiles, expectedFiles)
  1451  		t.FailNow()
  1452  	}
  1453  
  1454  	for i := range expectedFiles {
  1455  		if actualFiles[i] != expectedFiles[i] {
  1456  			t.Errorf("expected %s to equal %s", actualFiles[i], expectedFiles[i])
  1457  		}
  1458  	}
  1459  }
  1460  
  1461  func TestInitIgnoreList(t *testing.T) {
  1462  	mountInfo := `36 35 98:0 /kaniko /test/kaniko rw,noatime master:1 - ext3 /dev/root rw,errors=continue
  1463  36 35 98:0 /proc /test/proc rw,noatime master:1 - ext3 /dev/root rw,errors=continue
  1464  `
  1465  	mFile, err := os.CreateTemp("", "mountinfo")
  1466  	if err != nil {
  1467  		t.Fatal(err)
  1468  	}
  1469  	defer mFile.Close()
  1470  	if _, err := mFile.WriteString(mountInfo); err != nil {
  1471  		t.Fatal(err)
  1472  	}
  1473  	config.MountInfoPath = mFile.Name()
  1474  	defer func() {
  1475  		config.MountInfoPath = constants.MountInfoPath
  1476  	}()
  1477  
  1478  	expected := []IgnoreListEntry{
  1479  		{
  1480  			Path:            "/kaniko",
  1481  			PrefixMatchOnly: false,
  1482  		},
  1483  		{
  1484  			Path:            "/test/kaniko",
  1485  			PrefixMatchOnly: false,
  1486  		},
  1487  		{
  1488  			Path:            "/test/proc",
  1489  			PrefixMatchOnly: false,
  1490  		},
  1491  		{
  1492  			Path:            "/etc/mtab",
  1493  			PrefixMatchOnly: false,
  1494  		},
  1495  		{
  1496  			Path:            "/tmp/apt-key-gpghome",
  1497  			PrefixMatchOnly: true,
  1498  		},
  1499  	}
  1500  
  1501  	original := append([]IgnoreListEntry{}, ignorelist...)
  1502  	defer func() { ignorelist = original }()
  1503  
  1504  	err = InitIgnoreList()
  1505  	if err != nil {
  1506  		t.Fatal(err)
  1507  	}
  1508  	sort.Slice(expected, func(i, j int) bool {
  1509  		return expected[i].Path < expected[j].Path
  1510  	})
  1511  	sort.Slice(ignorelist, func(i, j int) bool {
  1512  		return ignorelist[i].Path < ignorelist[j].Path
  1513  	})
  1514  	testutil.CheckDeepEqual(t, expected, ignorelist)
  1515  }
  1516  
  1517  func Test_setFileTimes(t *testing.T) {
  1518  	testDir := t.TempDir()
  1519  
  1520  	p := filepath.Join(testDir, "foo.txt")
  1521  
  1522  	if err := os.WriteFile(p, []byte("meow"), 0o777); err != nil {
  1523  		t.Fatal(err)
  1524  	}
  1525  
  1526  	type testcase struct {
  1527  		desc  string
  1528  		path  string
  1529  		aTime time.Time
  1530  		mTime time.Time
  1531  	}
  1532  
  1533  	testCases := []testcase{
  1534  		{
  1535  			desc: "zero for mod and access",
  1536  			path: p,
  1537  		},
  1538  		{
  1539  			desc:  "zero for mod",
  1540  			path:  p,
  1541  			aTime: time.Now(),
  1542  		},
  1543  		{
  1544  			desc:  "zero for access",
  1545  			path:  p,
  1546  			mTime: time.Now(),
  1547  		},
  1548  		{
  1549  			desc:  "both non-zero",
  1550  			path:  p,
  1551  			mTime: time.Now(),
  1552  			aTime: time.Now(),
  1553  		},
  1554  	}
  1555  
  1556  	for _, tc := range testCases {
  1557  		t.Run(tc.desc, func(t *testing.T) {
  1558  			err := setFileTimes(tc.path, tc.aTime, tc.mTime)
  1559  			if err != nil {
  1560  				t.Errorf("expected err to be nil not %s", err)
  1561  			}
  1562  		})
  1563  	}
  1564  }