github.com/mgoltzsche/ctnr@v0.7.1-alpha/pkg/fs/writer/tarwriter_test.go (about)

     1  package writer
     2  
     3  import (
     4  	"bytes"
     5  	"io/ioutil"
     6  	"os"
     7  	"os/exec"
     8  	"path/filepath"
     9  	"syscall"
    10  	"testing"
    11  	"time"
    12  
    13  	"github.com/mgoltzsche/ctnr/pkg/fs"
    14  	"github.com/mgoltzsche/ctnr/pkg/fs/source"
    15  	"github.com/mgoltzsche/ctnr/pkg/fs/testutils"
    16  	"github.com/mgoltzsche/ctnr/pkg/idutils"
    17  	"github.com/stretchr/testify/assert"
    18  	"github.com/stretchr/testify/require"
    19  )
    20  
    21  func TestTarWriter(t *testing.T) {
    22  	tmpDir, err := ioutil.TempDir("", "test-tar-writer-")
    23  	require.NoError(t, err)
    24  	defer os.RemoveAll(tmpDir)
    25  	extDir := filepath.Join(tmpDir, "extracted")
    26  	err = os.Mkdir(extDir, 0755)
    27  	require.NoError(t, err)
    28  	tarFile := filepath.Join(tmpDir, "archive.tar")
    29  	f, err := os.OpenFile(tarFile, os.O_CREATE|os.O_RDWR, 0644)
    30  	require.NoError(t, err)
    31  	defer f.Close()
    32  
    33  	testee := NewTarWriter(f)
    34  	defer testee.Close()
    35  
    36  	// Test write operations
    37  	times := fs.FileTimes{}
    38  	times.Mtime, err = time.Parse(time.RFC3339, "2018-02-23T01:01:42Z")
    39  	require.NoError(t, err)
    40  	times.Mtime = time.Unix(times.Mtime.Unix(), 123)
    41  	times.Atime, err = time.Parse(time.RFC3339, "2018-01-23T01:01:43Z")
    42  	require.NoError(t, err)
    43  	usr := idutils.UserIds{uint(os.Getuid()), uint(os.Getgid())}
    44  	dirAttrs := fs.FileAttrs{Mode: os.ModeDir | 0755, UserIds: usr, FileTimes: times}
    45  	dirAttrs2 := dirAttrs
    46  	dirAttrs2.Mode = os.ModeDir | 0775
    47  	fileAttrsA := fs.NodeAttrs{NodeInfo: fs.NodeInfo{fs.TypeFile, fs.FileAttrs{Mode: 0640, UserIds: usr, Size: 1, FileTimes: times}}}
    48  	fileAttrsB := fs.NodeAttrs{NodeInfo: fs.NodeInfo{fs.TypeFile, fs.FileAttrs{Mode: 0755, UserIds: usr, Size: 3, FileTimes: times}}}
    49  	linkAttrs := fs.FileAttrs{Mode: os.ModeSymlink, UserIds: usr, FileTimes: times}
    50  	fifoAttrs := fs.DeviceAttrs{fs.FileAttrs{Mode: syscall.S_IFIFO | 0640, UserIds: usr, FileTimes: times}, 1, 1}
    51  	fileA := source.NewSourceFile(fs.NewReadableBytes([]byte("a")), fileAttrsA.FileAttrs)
    52  	fileB := source.NewSourceFile(fs.NewReadableBytes([]byte("bbb")), fileAttrsB.FileAttrs)
    53  	err = testee.Dir("/", "", dirAttrs2)
    54  	require.NoError(t, err)
    55  	err = testee.Dir("etc", "", dirAttrs2)
    56  	require.NoError(t, err)
    57  	reader, err := testee.File("etc/fileA", fileA)
    58  	require.NoError(t, err)
    59  	assert.Equal(t, fileA, reader, "returned FileSource of File()")
    60  	_, err = testee.File("/etc/fileB", fileB)
    61  	require.NoError(t, err)
    62  	err = testee.Fifo("/etc/fifo", fifoAttrs)
    63  	require.NoError(t, err)
    64  	err = testee.Device("/etc/devA", fifoAttrs)
    65  	require.NoError(t, err)
    66  	err = testee.Link("/etc/link-abs", "/etc/fileA")
    67  	require.NoError(t, err)
    68  	err = testee.Link("/etc/link-rel", "../etc/fileA")
    69  	require.NoError(t, err)
    70  	err = testee.Dir("/dir", "", dirAttrs)
    71  	require.NoError(t, err)
    72  	_, err = testee.File("/dir/file1", fileA)
    73  	require.NoError(t, err)
    74  	_, err = testee.File("/dir/file2", fileB)
    75  	require.NoError(t, err)
    76  	err = testee.Dir("/dir/ndir", "", fs.FileAttrs{Mode: os.ModeDir | 0754, UserIds: usr})
    77  	require.NoError(t, err)
    78  	_, err = testee.File("/dir/ndir/nestedfile", fileA)
    79  	require.NoError(t, err)
    80  	linkAttrs.Symlink = "../etc/fileA"
    81  	err = testee.Symlink("etc/symlink-rel", linkAttrs)
    82  	require.NoError(t, err)
    83  	linkAttrs.Symlink = "/etc/fileB"
    84  	err = testee.Symlink("etc/symlink-abs", linkAttrs)
    85  	require.NoError(t, err)
    86  
    87  	// Test paths sanitized
    88  	linkAttrs.Symlink = filepath.Join("..", "..")
    89  	err = testee.Symlink("etc/symlink-sanitized", linkAttrs)
    90  	require.NoError(t, err)
    91  	_, err = testee.File("/../filepathsanitized", fileA)
    92  	require.NoError(t, err)
    93  
    94  	err = testee.Close()
    95  	require.NoError(t, err)
    96  
    97  	// Untar archive and compare file system without timestamps using tar
    98  	cmd := exec.Command("tar", "-xf", tarFile)
    99  	cmd.Dir = extDir
   100  	var buf bytes.Buffer
   101  	cmd.Stdout = &buf
   102  	cmd.Stderr = &buf
   103  	err = cmd.Run()
   104  	require.NoError(t, err, buf.String())
   105  	// Cannot test time metadata here since tar -xf does not preserve it
   106  	// for every file type
   107  	testutils.AssertFsState(t, extDir, "", testutils.MtreeTestkeywords, `
   108  		# .
   109  		. size=4096 type=dir mode=0775
   110  			filepathsanitized mode=0640
   111  		# dir
   112  		dir type=dir mode=0755
   113  		    file1 mode=0640
   114  		    file2 mode=0755
   115  		
   116  		# dir/ndir
   117  		ndir type=dir mode=0754
   118  			nestedfile type=file mode=0640
   119  		..
   120  		..
   121  		# bin
   122  		etc type=dir mode=0775
   123  			devA mode=0640
   124  			fifo mode=0640
   125  			fileA mode=0640
   126  			fileB mode=0755
   127  			link-abs mode=0640 nlink=3
   128  			link-rel mode=0640 nlink=3
   129  			symlink-abs type=link mode=0777 link=$ROOT/etc/fileB
   130  			symlink-rel type=link mode=0777 link=../etc/fileA
   131  			symlink-sanitized type=link mode=0777 link=..
   132  		..
   133  	`)
   134  	// Test mtime applied to file
   135  	// (Compare only unix seconds since tar does not provide nanoseconds)
   136  	file1 := filepath.Join(extDir, "dir/file1")
   137  	st, err := os.Stat(file1)
   138  	require.NoError(t, err)
   139  	assert.Equal(t, times.Mtime.Unix(), st.ModTime().Unix(), "mtime should be applied")
   140  }