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  }