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 )