github.com/mgoltzsche/ctnr@v0.7.1-alpha/pkg/fs/source/sourcetar_test.go (about) 1 package source 2 3 import ( 4 "bytes" 5 "fmt" 6 "io/ioutil" 7 "os" 8 "os/exec" 9 "path/filepath" 10 "sort" 11 "strings" 12 "syscall" 13 "testing" 14 "time" 15 16 "github.com/mgoltzsche/ctnr/pkg/fs" 17 "github.com/mgoltzsche/ctnr/pkg/fs/testutils" 18 "github.com/openSUSE/umoci/pkg/fseval" 19 "github.com/stretchr/testify/assert" 20 "github.com/stretchr/testify/require" 21 "golang.org/x/sys/unix" 22 ) 23 24 func expectedWrittenPaths(prefix string) []string { 25 usr := "" 26 uid := os.Geteuid() 27 gid := os.Getegid() 28 if uid != 0 || gid != 0 { 29 usr = fmt.Sprintf(" usr=%d:%d", uid, gid) 30 } 31 return []string{ 32 filepath.Clean(prefix+string(os.PathSeparator)) + " type=dir" + usr + " mode=755", 33 prefix + "/etc type=dir" + usr + " mode=755", 34 prefix + "/etc/_symlink type=symlink" + usr + " link=fileA", 35 prefix + "/etc/dirA type=dir" + usr + " mode=755", 36 prefix + "/etc/dirB type=dir" + usr + " mode=750", 37 prefix + "/etc/dirB/fileC type=file" + usr + " mode=440 size=7 hash=sha256:ed7002b439e9ac845f22357d822bac1444730fbdb6016d3ec9432297b9ec9f73", 38 prefix + "/etc/fifo type=fifo" + usr + " mode=640", 39 prefix + "/etc/fileA type=file" + usr + " mode=644 size=7 hash=sha256:ed7002b439e9ac845f22357d822bac1444730fbdb6016d3ec9432297b9ec9f73", 40 // ATTENTION: link order not guaranteed - depends on external tool 41 prefix + "/etc/fileB hlink=" + prefix + "/etc/link", 42 prefix + "/etc/link type=file" + usr + " mode=750 size=7 hash=sha256:ed7002b439e9ac845f22357d822bac1444730fbdb6016d3ec9432297b9ec9f73", 43 } 44 } 45 46 func TestSourceTar(t *testing.T) { 47 tmpDir, rootfs := createTestFileSystem(t) 48 defer os.RemoveAll(tmpDir) 49 tarFile1 := filepath.Join(tmpDir, "a1.tar") 50 tarFile2 := filepath.Join(tmpDir, "a2.tar") 51 tarDir(t, rootfs, "-cf", tarFile1) 52 changedFile := filepath.Join(rootfs, "changedfile") 53 err := ioutil.WriteFile(changedFile, []byte("data"), 0644) 54 require.NoError(t, err) 55 tarDir(t, rootfs, "-cf", tarFile2) 56 57 // Test attributes 58 testee := NewSourceTar(tarFile1) 59 a := testee.Attrs() 60 if a.NodeType != fs.TypeOverlay { 61 t.Error("type != TypeOverlay") 62 t.FailNow() 63 } 64 wa, err := testee.DeriveAttrs() 65 require.NoError(t, err) 66 hash1 := wa.Hash 67 testee = NewSourceTar(tarFile2) 68 wa, err = testee.DeriveAttrs() 69 require.NoError(t, err) 70 hash2 := wa.Hash 71 if hash1 == hash2 { 72 t.Error("hash1 == hash2") 73 } 74 testee = NewSourceTar(tarFile1) 75 wa, err = testee.DeriveAttrs() 76 require.NoError(t, err) 77 hash2 = wa.Hash 78 if hash1 != hash2 { 79 t.Error("hash1 != hash1") 80 } 81 82 // Test extraction 83 // ATTENTION: Time metadata cannot be tested here due to comparison with external tar tool. Must be tested in fsbuilder test 84 destPath := filepath.Join("/a", "b") 85 w := testutils.NewWriterMock(t, fs.AttrsHash) 86 err = testee.Write(destPath, "", &testutils.ExpandingWriterMock{w}, nil) 87 require.NoError(t, err) 88 sort.Strings(w.Written) 89 expected := expectedWrittenPaths("/a/b") 90 written := "\n " + strings.Join(w.Written, "\n ") 91 assert.True(t, len(expected) == len(w.Written), "extracted tar source: "+written) 92 assert.Equal(t, expected[:len(expected)-2], w.Written[:len(w.Written)-2], "extracted tar source") 93 if strings.Index(w.Written[len(w.Written)-1], "/a/b/etc/link hlink=/a/b/etc/fileB") != 0 && 94 strings.Index(w.Written[len(w.Written)-2], "/a/b/etc/fileB hlink=/a/b/etc/link") != 0 { 95 t.Errorf("did not write hardlinks properly: " + written) 96 } 97 } 98 99 func prefixedPaths(paths []string, prefix string) []string { 100 r := []string{} 101 for _, line := range paths { 102 r = append(r, prefix+line) 103 } 104 return r 105 } 106 107 func createTestFileSystem(t *testing.T) (tmpDir string, rootfs string) { 108 mtime, err := time.Parse(time.RFC3339, "2018-01-23T01:01:42Z") 109 require.NoError(t, err) 110 mtime = time.Unix(mtime.Unix(), 900000000) 111 times := fs.FileTimes{Mtime: mtime} 112 tmpDir, err = ioutil.TempDir("", "testfs-") 113 require.NoError(t, err) 114 rootfs = filepath.Join(tmpDir, "rootfs") 115 createTestFile(t, rootfs, "/etc/fileA", fs.FileAttrs{Mode: 0644, FileTimes: times}) 116 createTestFile(t, rootfs, "/etc/fileB", fs.FileAttrs{Mode: 0750, FileTimes: times}) 117 dirA := filepath.Join(rootfs, "/etc/dirA") 118 err = os.Mkdir(dirA, 0755) 119 require.NoError(t, err) 120 err = fseval.RootlessFsEval.Lutimes(dirA, time.Now(), mtime) 121 require.NoError(t, err) 122 dirB := filepath.Join(rootfs, "/etc/dirB") 123 err = os.Mkdir(dirB, 0750) 124 require.NoError(t, err) 125 err = fseval.RootlessFsEval.Lutimes(dirB, time.Now(), mtime) 126 require.NoError(t, err) 127 createTestFile(t, rootfs, "/etc/dirB/fileC", fs.FileAttrs{Mode: 0440, FileTimes: times}) 128 symlink := filepath.Join(rootfs, "/etc/_symlink") 129 err = os.Symlink("fileA", symlink) 130 require.NoError(t, err) 131 err = fseval.RootlessFsEval.Lutimes(symlink, time.Now(), mtime) 132 require.NoError(t, err) 133 err = os.Link(filepath.Join(rootfs, "/etc/fileB"), filepath.Join(rootfs, "/etc/link")) 134 require.NoError(t, err) 135 err = unix.Mknod(filepath.Join(rootfs, "/etc/fifo"), syscall.S_IFIFO|0640, 0) 136 require.NoError(t, err) 137 return 138 } 139 140 func createTestFile(t *testing.T, fs, file string, a fs.FileAttrs) { 141 file = filepath.Join(fs, file) 142 err := os.MkdirAll(filepath.Dir(file), 0755) 143 require.NoError(t, err) 144 err = ioutil.WriteFile(file, []byte("content"), a.Mode.Perm()) 145 require.NoError(t, err) 146 err = fseval.RootlessFsEval.Lutimes(file, time.Now(), a.Mtime) 147 require.NoError(t, err) 148 } 149 150 func tarDir(t *testing.T, dir, opts, tarFile string) { 151 cmd := exec.Command("tar", opts, tarFile, ".") 152 cmd.Dir = dir 153 var buf bytes.Buffer 154 cmd.Stdout = &buf 155 cmd.Stderr = &buf 156 err := cmd.Run() 157 require.NoError(t, err, buf.String()) 158 }