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

     1  package source
     2  
     3  import (
     4  	"fmt"
     5  	"io/ioutil"
     6  	"os"
     7  	"path/filepath"
     8  	"strings"
     9  	"testing"
    10  	"time"
    11  
    12  	"github.com/mgoltzsche/ctnr/pkg/fs"
    13  	"github.com/mgoltzsche/ctnr/pkg/fs/testutils"
    14  	"github.com/mgoltzsche/ctnr/pkg/idutils"
    15  	"github.com/openSUSE/umoci/pkg/fseval"
    16  	"github.com/stretchr/testify/assert"
    17  	"github.com/stretchr/testify/require"
    18  )
    19  
    20  func TestSources(t *testing.T) {
    21  	tmpDir, rootfs := createTestFileSystem(t)
    22  	defer os.RemoveAll(tmpDir)
    23  
    24  	// Prepare source files
    25  	tarFile := filepath.Join(tmpDir, "archive.tar")
    26  	tarGzFile := filepath.Join(tmpDir, "archive.tar.gz")
    27  	tarBzFile := filepath.Join(tmpDir, "archive.tar.bz")
    28  	tarDir(t, rootfs, "-cf", tarFile)
    29  	tarDir(t, rootfs, "-czf", tarGzFile)
    30  	tarDir(t, rootfs, "-cjf", tarBzFile)
    31  	srcFile := filepath.Join(tmpDir, "sourcefile")
    32  	srcLink := filepath.Join(tmpDir, "sourcelink")
    33  	err := ioutil.WriteFile(srcFile, []byte("content"), 0644)
    34  	require.NoError(t, err)
    35  	err = os.Symlink(srcFile, srcLink)
    36  	require.NoError(t, err)
    37  
    38  	mtime, err := time.Parse(time.RFC3339, "2018-01-23T01:01:42Z")
    39  	require.NoError(t, err)
    40  	atime, err := time.Parse(time.RFC3339, "2018-01-23T01:02:42Z")
    41  	require.NoError(t, err)
    42  
    43  	// Test source file overlay variants
    44  	testee := NewSources(fseval.RootlessFsEval, fs.NewRootlessAttrMapper(idutils.MapRootless))
    45  	factory := func(file string, fi os.FileInfo, usr *idutils.UserIds) (fs.Source, error) {
    46  		return testee.File(file, fi, usr)
    47  	}
    48  	factoryX := func(file string, fi os.FileInfo, usr *idutils.UserIds) (fs.Source, error) {
    49  		return testee.FileOverlay(file, fi, usr)
    50  	}
    51  	expectedArchivePaths := toWrittenPathMap(expectedWrittenPaths("/overlayx"))
    52  	expectedArchivePaths["/"] = true
    53  	expectedFilePaths := map[string]bool{"/": true, "/overlayx": true}
    54  	for _, c := range []struct {
    55  		t             fs.NodeType
    56  		factory       func(file string, fi os.FileInfo, usr *idutils.UserIds) (fs.Source, error)
    57  		file          string
    58  		expectedUsr   *idutils.UserIds
    59  		expectedPaths map[string]bool
    60  	}{
    61  		{fs.TypeFile, factory, srcFile, &idutils.UserIds{1, 33}, expectedFilePaths},
    62  		{fs.TypeFile, factoryX, srcFile, nil, expectedFilePaths},
    63  		{fs.TypeSymlink, factory, srcLink, nil, expectedFilePaths},
    64  		{fs.TypeSymlink, factoryX, srcLink, nil, expectedFilePaths},
    65  		{fs.TypeOverlay, factoryX, tarFile, nil, expectedArchivePaths},
    66  		{fs.TypeOverlay, factoryX, tarGzFile, nil, expectedArchivePaths},
    67  		{fs.TypeOverlay, factoryX, tarBzFile, nil, expectedArchivePaths},
    68  		// TODO: test device and URL
    69  	} {
    70  		writerMock := testutils.NewWriterMock(t, fs.AttrsAll)
    71  		fi, err := os.Lstat(c.file)
    72  		require.NoError(t, err)
    73  		src, err := c.factory(c.file, fi, nil)
    74  		require.NoError(t, err)
    75  		a := src.Attrs()
    76  		st := a.NodeType
    77  		if st != c.t {
    78  			t.Errorf("%s: expected type %s but was %s", c.file, c.t, st)
    79  			t.FailNow()
    80  		}
    81  
    82  		// Test hash
    83  		wa, err := src.DeriveAttrs()
    84  		require.NoError(t, err)
    85  		hash1 := wa.Hash
    86  
    87  		src, err = c.factory(c.file, fi, &idutils.UserIds{99997, 99997})
    88  		require.NoError(t, err)
    89  		wa, err = src.DeriveAttrs()
    90  		require.NoError(t, err)
    91  		hash2 := wa.Hash
    92  		if hash2 == "" && c.t == fs.TypeFile {
    93  			t.Errorf("%s: source file hash is empty", c.file)
    94  		}
    95  		if hash1 != hash2 {
    96  			t.Errorf("%s: hash1 != hash1", c.file)
    97  		}
    98  
    99  		// Test write
   100  		writerMock.Dir("/", "", fs.FileAttrs{Mode: 0755})
   101  		err = src.Write("/overlayx", "", &testutils.ExpandingWriterMock{writerMock}, map[fs.Source]string{})
   102  		require.NoError(t, err)
   103  		if !assert.Equal(t, c.expectedPaths, writerMock.WrittenPaths, c.file) {
   104  			t.FailNow()
   105  		}
   106  	}
   107  
   108  	// Test if actual file attributes applied
   109  	testee = NewSources(fseval.RootlessFsEval, fs.NewAttrMapper(idutils.MapIdentity))
   110  	err = fseval.RootlessFsEval.Lutimes(srcFile, atime, mtime)
   111  	require.NoError(t, err)
   112  	uid := os.Geteuid()
   113  	gid := os.Getegid()
   114  	writerMock := testutils.NewWriterMock(t, fs.AttrsAll)
   115  	fi, err := os.Lstat(srcFile)
   116  	require.NoError(t, err)
   117  	r, err := testee.File(srcFile, fi, nil)
   118  	require.NoError(t, err)
   119  	err = r.Write("/file1", "file1", writerMock, map[fs.Source]string{})
   120  	require.NoError(t, err)
   121  	usr := ""
   122  	if uid != 0 || gid != 0 {
   123  		usr = fmt.Sprintf(" usr=%d:%d", uid, gid)
   124  	}
   125  	expected := fmt.Sprintf("/file1 type=file%s mode=644 size=7 mtime=1516669302", usr)
   126  	expected += " atime=1516669362 hash=sha256:ed7002b439e9ac845f22357d822bac1444730fbdb6016d3ec9432297b9ec9f73"
   127  	assert.Equal(t, []string{expected}, writerMock.Written, "sources should map file attributes")
   128  }
   129  
   130  func toWrittenPathMap(paths []string) map[string]bool {
   131  	r := map[string]bool{}
   132  	for _, line := range paths {
   133  		r[strings.Split(line, " ")[0]] = true
   134  	}
   135  	return r
   136  }