github.com/Cloud-Foundations/Dominator@v0.3.4/dom/lib/buildUpdateRequest_test.go (about)

     1  package lib
     2  
     3  import (
     4  	"encoding/json"
     5  	"fmt"
     6  	"reflect"
     7  	"testing"
     8  
     9  	"github.com/Cloud-Foundations/Dominator/lib/filesystem"
    10  	"github.com/Cloud-Foundations/Dominator/lib/filter"
    11  	"github.com/Cloud-Foundations/Dominator/lib/hash"
    12  	"github.com/Cloud-Foundations/Dominator/lib/image"
    13  	"github.com/Cloud-Foundations/Dominator/lib/log/testlogger"
    14  	subproto "github.com/Cloud-Foundations/Dominator/proto/sub"
    15  )
    16  
    17  type fileInfo struct {
    18  	size    uint64
    19  	hashVal hash.Hash
    20  	uid     uint32
    21  }
    22  
    23  func TestSameFile(t *testing.T) {
    24  	request := makeUpdateRequest(t, testDataFile0(0), testDataFile0(0))
    25  	if len(request.PathsToDelete) != 0 {
    26  		t.Errorf("number of paths to delete: %d != 0",
    27  			len(request.PathsToDelete))
    28  	}
    29  }
    30  
    31  func TestFileToDelete(t *testing.T) {
    32  	request := makeUpdateRequest(t, testDataFile1(0), testDataFile0(0))
    33  	if len(request.PathsToDelete) != 1 {
    34  		t.Errorf("number of paths to delete: %d != 1",
    35  			len(request.PathsToDelete))
    36  	}
    37  }
    38  
    39  func TestFileToChange(t *testing.T) {
    40  	request := makeUpdateRequest(t, testDataFile0(0), testDataFile0(1))
    41  	if reflect.DeepEqual(request, subproto.UpdateRequest{}) {
    42  		t.Error("Inode not being changed")
    43  	}
    44  	if len(request.InodesToChange) != 1 {
    45  		t.Error("Inode not being changed")
    46  	}
    47  }
    48  
    49  func TestSameOnlyDirectory(t *testing.T) {
    50  	request := makeUpdateRequest(t, testDataDirectory0(), testDataDirectory0())
    51  	if len(request.PathsToDelete) != 0 {
    52  		t.Errorf("number of paths to delete: %d != 0",
    53  			len(request.PathsToDelete))
    54  	}
    55  }
    56  
    57  func TestOnlyDirectoryToDelete(t *testing.T) {
    58  	request := makeUpdateRequest(t, testDataDirectory2(), testDataDirectory0())
    59  	if len(request.PathsToDelete) != 1 {
    60  		t.Errorf("number of paths to delete: %d != 1",
    61  			len(request.PathsToDelete))
    62  	}
    63  }
    64  
    65  func TestExtraDirectoryToDelete(t *testing.T) {
    66  	request := makeUpdateRequest(t, testDataDirectory0(), testDataDirectory01())
    67  	if len(request.PathsToDelete) != 1 {
    68  		t.Errorf("number of paths to delete: %d != 1",
    69  			len(request.PathsToDelete))
    70  	}
    71  }
    72  
    73  func TestLinkFiles(t *testing.T) {
    74  	request := makeUpdateRequest(t, testDataLinkedFiles(2),
    75  		testDataDuplicateFiles())
    76  	if len(request.HardlinksToMake) != 1 {
    77  		t.Error("File not being linked")
    78  	}
    79  	req := subproto.UpdateRequest{
    80  		HardlinksToMake: request.HardlinksToMake,
    81  	}
    82  	if !reflect.DeepEqual(request, req) {
    83  		t.Error("Unexpected changes being made")
    84  	}
    85  }
    86  
    87  func TestSplitHardlinks(t *testing.T) {
    88  	request := makeUpdateRequest(t, testDataDuplicateFiles(),
    89  		testDataLinkedFiles(2))
    90  	if len(request.FilesToCopyToCache) != 1 {
    91  		t.Error("File not being copied to cache")
    92  	}
    93  	if len(request.InodesToMake) != 1 {
    94  		t.Error("Inode not being created")
    95  	}
    96  	req := subproto.UpdateRequest{
    97  		FilesToCopyToCache: request.FilesToCopyToCache,
    98  		InodesToMake:       request.InodesToMake,
    99  	}
   100  	if !reflect.DeepEqual(request, req) {
   101  		t.Error("Unexpected changes being made")
   102  	}
   103  }
   104  
   105  func TestSplitHashes1(t *testing.T) {
   106  	request := makeUpdateRequest(t,
   107  		testDataMulti(
   108  			[]fileInfo{{101, hash1, 1}, {101, hash1, 0}},
   109  			[]uint64{1, 2}),
   110  		testDataLinkedFiles(2))
   111  	if len(request.FilesToCopyToCache) != 1 {
   112  		t.Error("File not being copied to cache")
   113  	}
   114  	if request.FilesToCopyToCache[0].DoHardlink {
   115  		t.Errorf("%s is being hardlinked to cache",
   116  			request.FilesToCopyToCache[0].Name)
   117  	}
   118  	if len(request.InodesToMake) != 1 {
   119  		t.Error("Inode not being created")
   120  	}
   121  	if request.InodesToMake[0].Name != "/file1" {
   122  		t.Error("/file1 not being created")
   123  	}
   124  	req := subproto.UpdateRequest{
   125  		FilesToCopyToCache: request.FilesToCopyToCache,
   126  		InodesToMake:       request.InodesToMake,
   127  	}
   128  	if !reflect.DeepEqual(request, req) {
   129  		t.Error("Unexpected changes being made")
   130  	}
   131  }
   132  
   133  func TestSplitHashes2(t *testing.T) {
   134  	request := makeUpdateRequest(t,
   135  		testDataMulti(
   136  			[]fileInfo{{101, hash1, 0}, {101, hash1, 1}},
   137  			[]uint64{1, 2}),
   138  		testDataLinkedFiles(2))
   139  	if len(request.FilesToCopyToCache) != 1 {
   140  		t.Error("File not being copied to cache")
   141  	}
   142  	if len(request.InodesToMake) != 1 {
   143  		t.Error("Inode not being created")
   144  	}
   145  	req := subproto.UpdateRequest{
   146  		FilesToCopyToCache: request.FilesToCopyToCache,
   147  		InodesToMake:       request.InodesToMake,
   148  	}
   149  	if !reflect.DeepEqual(request, req) {
   150  		t.Error("Unexpected changes being made")
   151  	}
   152  }
   153  
   154  func TestSplitHashes3(t *testing.T) {
   155  	request := makeUpdateRequest(t,
   156  		testDataMulti(
   157  			[]fileInfo{{101, hash1, 1}, {101, hash1, 0}},
   158  			[]uint64{1, 2, 2}),
   159  		testDataLinkedFiles(3))
   160  	if len(request.FilesToCopyToCache) != 1 {
   161  		t.Error("File not being copied to cache")
   162  	}
   163  	if request.FilesToCopyToCache[0].DoHardlink {
   164  		t.Errorf("%s is being hardlinked to cache",
   165  			request.FilesToCopyToCache[0].Name)
   166  	}
   167  	if len(request.InodesToMake) != 1 {
   168  		t.Error("Inode not being created")
   169  	}
   170  	if request.InodesToMake[0].Name != "/file1" {
   171  		t.Error("/file1 not being created")
   172  	}
   173  	req := subproto.UpdateRequest{
   174  		FilesToCopyToCache: request.FilesToCopyToCache,
   175  		InodesToMake:       request.InodesToMake,
   176  	}
   177  	if !reflect.DeepEqual(request, req) {
   178  		t.Error("Unexpected changes being made")
   179  	}
   180  }
   181  
   182  func TestSplitHashes4(t *testing.T) {
   183  	request := makeUpdateRequest(t,
   184  		testDataMulti(
   185  			[]fileInfo{{101, hash1, 1}, {101, hash1, 0}, {101, hash1, 0}},
   186  			[]uint64{1, 2, 2, 1, 3}),
   187  		testDataLinkedFiles(4))
   188  	if len(request.FilesToCopyToCache) != 1 {
   189  		t.Error("File not being copied to cache")
   190  	}
   191  	if request.FilesToCopyToCache[0].DoHardlink {
   192  		t.Errorf("%s is being hardlinked to cache",
   193  			request.FilesToCopyToCache[0].Name)
   194  	}
   195  	if len(request.InodesToMake) != 2 {
   196  		t.Error("Inodes not being created")
   197  	}
   198  	if request.InodesToMake[0].Name != "/file1" {
   199  		t.Error("/file1 not being created")
   200  	}
   201  	if request.InodesToMake[1].Name != "/file5" {
   202  		t.Error("/file5 not being created")
   203  	}
   204  	if len(request.HardlinksToMake) != 1 {
   205  		t.Error("File not being linked")
   206  	}
   207  	if request.HardlinksToMake[0].NewLink != "/file4" ||
   208  		request.HardlinksToMake[0].Target != "/file1" {
   209  		t.Errorf("/file4 not being linked to /file1")
   210  	}
   211  	if len(request.MultiplyUsedObjects) != 1 {
   212  		t.Error("Object not being duplicated")
   213  	}
   214  	req := subproto.UpdateRequest{
   215  		FilesToCopyToCache:  request.FilesToCopyToCache,
   216  		InodesToMake:        request.InodesToMake,
   217  		HardlinksToMake:     request.HardlinksToMake,
   218  		MultiplyUsedObjects: request.MultiplyUsedObjects,
   219  	}
   220  	if !reflect.DeepEqual(request, req) {
   221  		t.Error("Unexpected changes being made")
   222  	}
   223  }
   224  
   225  func TestSplitHashes5(t *testing.T) {
   226  	request := makeUpdateRequest(t,
   227  		testDataMulti(
   228  			[]fileInfo{{101, hash1, 1}, {101, hash1, 0}, {101, hash1, 1}},
   229  			[]uint64{1, 2, 2, 1, 3}),
   230  		testDataMulti(
   231  			[]fileInfo{{101, hash1, 1}, {101, hash1, 0}, {101, hash1, 0}},
   232  			[]uint64{1, 2, 1, 2, 3}))
   233  	if len(request.HardlinksToMake) != 2 {
   234  		t.Errorf("#HardlinksToMake: %d != 2", len(request.HardlinksToMake))
   235  	}
   236  	if request.HardlinksToMake[0].NewLink != "/file3" ||
   237  		request.HardlinksToMake[0].Target != "/file2" {
   238  		t.Errorf("/file3 not being linked to /file2")
   239  	}
   240  	if request.HardlinksToMake[1].NewLink != "/file4" ||
   241  		request.HardlinksToMake[1].Target != "/file1" {
   242  		t.Errorf("/file4 not being linked to /file1")
   243  	}
   244  	if len(request.InodesToChange) < 1 {
   245  		t.Error("Inode not being changed")
   246  	}
   247  	if len(request.InodesToChange) > 1 {
   248  		t.Error("Too many inodes being changed")
   249  	}
   250  	if request.InodesToChange[0].Name != "/file5" {
   251  		t.Error("/file5 not being changed")
   252  	}
   253  	req := subproto.UpdateRequest{
   254  		HardlinksToMake: request.HardlinksToMake,
   255  		InodesToChange:  request.InodesToChange,
   256  	}
   257  	if !reflect.DeepEqual(request, req) {
   258  		t.Error("Unexpected changes being made")
   259  	}
   260  }
   261  
   262  func makeUpdateRequest(t *testing.T, imageFS *filesystem.FileSystem,
   263  	subFS *filesystem.FileSystem) subproto.UpdateRequest {
   264  	fetchedObjects := make(map[hash.Hash]struct{}, len(imageFS.InodeTable))
   265  	for hashVal := range imageFS.HashToInodesTable() {
   266  		fetchedObjects[hashVal] = struct{}{}
   267  	}
   268  	for hashVal := range subFS.HashToInodesTable() {
   269  		delete(fetchedObjects, hashVal)
   270  	}
   271  	objectCache := make([]hash.Hash, 0, len(fetchedObjects))
   272  	for hashVal := range fetchedObjects {
   273  		objectCache = append(objectCache, hashVal)
   274  	}
   275  	imageFS.BuildEntryMap()
   276  	if err := subFS.RebuildInodePointers(); err != nil {
   277  		panic(err)
   278  	}
   279  	subFS.BuildEntryMap()
   280  	if err := imageFS.RebuildInodePointers(); err != nil {
   281  		panic(err)
   282  	}
   283  	subObj := Sub{FileSystem: subFS, ObjectCache: objectCache}
   284  	var request subproto.UpdateRequest
   285  	emptyFilter, _ := filter.New(nil)
   286  	BuildUpdateRequest(subObj,
   287  		&image.Image{FileSystem: imageFS, Filter: emptyFilter},
   288  		&request, false, false, testlogger.New(t))
   289  	reqTxt, err := json.MarshalIndent(request, "", "    ")
   290  	if err != nil {
   291  		t.Fatal(err)
   292  	} else {
   293  		t.Logf("%s", reqTxt)
   294  	}
   295  	return request
   296  }
   297  
   298  func testDataDirectory0() *filesystem.FileSystem {
   299  	return &filesystem.FileSystem{
   300  		InodeTable: filesystem.InodeTable{
   301  			1: &filesystem.DirectoryInode{},
   302  		},
   303  		DirectoryInode: filesystem.DirectoryInode{
   304  			EntryList: []*filesystem.DirectoryEntry{
   305  				{
   306  					Name:        "dir0",
   307  					InodeNumber: 1,
   308  				},
   309  			},
   310  		},
   311  	}
   312  }
   313  
   314  func testDataDirectory01() *filesystem.FileSystem {
   315  	return &filesystem.FileSystem{
   316  		InodeTable: filesystem.InodeTable{
   317  			1: &filesystem.DirectoryInode{},
   318  			2: &filesystem.DirectoryInode{},
   319  		},
   320  		DirectoryInode: filesystem.DirectoryInode{
   321  			EntryList: []*filesystem.DirectoryEntry{
   322  				{
   323  					Name:        "dir0",
   324  					InodeNumber: 1,
   325  				},
   326  				{
   327  					Name:        "dir1",
   328  					InodeNumber: 2,
   329  				},
   330  			},
   331  		},
   332  	}
   333  }
   334  
   335  func testDataDirectory2() *filesystem.FileSystem {
   336  	return &filesystem.FileSystem{
   337  		InodeTable: filesystem.InodeTable{
   338  			1: &filesystem.DirectoryInode{},
   339  		},
   340  		DirectoryInode: filesystem.DirectoryInode{
   341  			EntryList: []*filesystem.DirectoryEntry{
   342  				{
   343  					Name:        "dir2",
   344  					InodeNumber: 1,
   345  				},
   346  			},
   347  		},
   348  	}
   349  }
   350  
   351  func testDataFile0(uid uint32) *filesystem.FileSystem {
   352  	return &filesystem.FileSystem{
   353  		InodeTable: filesystem.InodeTable{
   354  			1: &filesystem.RegularInode{Size: 100, Hash: hash0, Uid: uid},
   355  		},
   356  		DirectoryInode: filesystem.DirectoryInode{
   357  			EntryList: []*filesystem.DirectoryEntry{
   358  				{
   359  					Name:        "file0",
   360  					InodeNumber: 1,
   361  				},
   362  			},
   363  		},
   364  	}
   365  }
   366  
   367  func testDataFile1(uid uint32) *filesystem.FileSystem {
   368  	return &filesystem.FileSystem{
   369  		InodeTable: filesystem.InodeTable{
   370  			1: &filesystem.RegularInode{Size: 101, Hash: hash1, Uid: uid},
   371  		},
   372  		DirectoryInode: filesystem.DirectoryInode{
   373  			EntryList: []*filesystem.DirectoryEntry{
   374  				{
   375  					Name:        "file1",
   376  					InodeNumber: 1,
   377  				},
   378  			},
   379  		},
   380  	}
   381  }
   382  
   383  func testDataDuplicateFiles() *filesystem.FileSystem {
   384  	return &filesystem.FileSystem{
   385  		InodeTable: filesystem.InodeTable{
   386  			1: &filesystem.RegularInode{Size: 101, Hash: hash1},
   387  			2: &filesystem.RegularInode{Size: 101, Hash: hash1},
   388  		},
   389  		DirectoryInode: filesystem.DirectoryInode{
   390  			EntryList: []*filesystem.DirectoryEntry{
   391  				{
   392  					Name:        "file1",
   393  					InodeNumber: 1,
   394  				},
   395  				{
   396  					Name:        "file2",
   397  					InodeNumber: 2,
   398  				},
   399  			},
   400  		},
   401  	}
   402  }
   403  
   404  func testDataLinkedFiles(nFiles int) *filesystem.FileSystem {
   405  	entries := make([]*filesystem.DirectoryEntry, 0, nFiles)
   406  	for i := 0; i < nFiles; i++ {
   407  		entries = append(entries, &filesystem.DirectoryEntry{
   408  			Name:        fmt.Sprintf("file%d", i+1),
   409  			InodeNumber: 1,
   410  		})
   411  	}
   412  	return &filesystem.FileSystem{
   413  		InodeTable: filesystem.InodeTable{
   414  			1: &filesystem.RegularInode{Size: 101, Hash: hash1},
   415  		},
   416  		DirectoryInode: filesystem.DirectoryInode{EntryList: entries},
   417  	}
   418  }
   419  
   420  func testDataMulti(fInfos []fileInfo, files []uint64) *filesystem.FileSystem {
   421  	inodes := make(filesystem.InodeTable, len(fInfos))
   422  	for i := uint64(0); i < uint64(len(fInfos)); i++ {
   423  		fi := fInfos[i]
   424  		inodes[i+1] = &filesystem.RegularInode{
   425  			Size: fi.size,
   426  			Hash: fi.hashVal,
   427  			Uid:  fi.uid,
   428  		}
   429  	}
   430  	entries := make([]*filesystem.DirectoryEntry, 0, len(files))
   431  	for i := 0; i < len(files); i++ {
   432  		entries = append(entries, &filesystem.DirectoryEntry{
   433  			Name:        fmt.Sprintf("file%d", i+1),
   434  			InodeNumber: files[i],
   435  		})
   436  	}
   437  	return &filesystem.FileSystem{
   438  		InodeTable:     inodes,
   439  		DirectoryInode: filesystem.DirectoryInode{EntryList: entries},
   440  	}
   441  }
   442  
   443  var (
   444  	hash0 hash.Hash = hash.Hash{0xde, 0xad}
   445  	hash1 hash.Hash = hash.Hash{0xbe, 0xef}
   446  )