github.com/cs3org/reva/v2@v2.27.7/pkg/storage/fs/posix/tree/tree_test.go (about) 1 package tree_test 2 3 import ( 4 "crypto/rand" 5 "log" 6 "os" 7 "os/exec" 8 "strings" 9 "time" 10 11 provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1" 12 helpers "github.com/cs3org/reva/v2/pkg/storage/fs/posix/testhelpers" 13 "github.com/shirou/gopsutil/process" 14 15 . "github.com/onsi/ginkgo/v2" 16 . "github.com/onsi/gomega" 17 ) 18 19 func generateRandomString(length int) (string, error) { 20 const charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" 21 charsetLength := len(charset) 22 23 randomBytes := make([]byte, length) 24 _, err := rand.Read(randomBytes) 25 if err != nil { 26 return "", err 27 } 28 29 for i := 0; i < length; i++ { 30 randomBytes[i] = charset[int(randomBytes[i])%charsetLength] 31 } 32 33 return string(randomBytes), nil 34 } 35 36 var ( 37 env *helpers.TestEnv 38 39 root string 40 ) 41 42 var _ = SynchronizedBeforeSuite(func() { 43 var err error 44 env, err = helpers.NewTestEnv(nil) 45 Expect(err).ToNot(HaveOccurred()) 46 47 Eventually(func() bool { 48 // Get all running processes 49 processes, err := process.Processes() 50 if err != nil { 51 panic("could not get processes: " + err.Error()) 52 } 53 54 // Search for the process named "inotifywait" 55 for _, p := range processes { 56 name, err := p.Name() 57 if err != nil { 58 log.Println(err) 59 continue 60 } 61 62 if strings.Contains(name, "inotifywait") { 63 // Give it some time to setup the watches 64 time.Sleep(2 * time.Second) 65 return true 66 } 67 } 68 return false 69 }).Should(BeTrue()) 70 }, func() {}) 71 72 var _ = SynchronizedAfterSuite(func() {}, func() { 73 if env != nil { 74 env.Cleanup() 75 } 76 }) 77 78 var _ = Describe("Tree", func() { 79 var ( 80 subtree string 81 ) 82 83 BeforeEach(func() { 84 SetDefaultEventuallyTimeout(15 * time.Second) 85 86 var err error 87 subtree, err = generateRandomString(10) 88 Expect(err).ToNot(HaveOccurred()) 89 subtree = "/" + subtree 90 root = env.Root + "/users/" + env.Owner.Username + subtree 91 Expect(os.Mkdir(root, 0700)).To(Succeed()) 92 93 Eventually(func(g Gomega) { 94 n, err := env.Lookup.NodeFromResource(env.Ctx, &provider.Reference{ 95 ResourceId: env.SpaceRootRes, 96 Path: subtree, 97 }) 98 g.Expect(err).ToNot(HaveOccurred()) 99 g.Expect(n.Exists).To(BeTrue()) 100 }).Should(Succeed()) 101 }) 102 103 Describe("assimilation", func() { 104 Describe("of files", func() { 105 It("handles new files", func() { 106 _, err := os.Create(root + "/assimilated.txt") 107 Expect(err).ToNot(HaveOccurred()) 108 109 Eventually(func(g Gomega) { 110 n, err := env.Lookup.NodeFromResource(env.Ctx, &provider.Reference{ 111 ResourceId: env.SpaceRootRes, 112 Path: subtree + "/assimilated.txt", 113 }) 114 g.Expect(err).ToNot(HaveOccurred()) 115 g.Expect(n).ToNot(BeNil()) 116 g.Expect(n.Type(env.Ctx)).To(Equal(provider.ResourceType_RESOURCE_TYPE_FILE)) 117 g.Expect(n.ID).ToNot(BeEmpty()) 118 g.Expect(n.Blobsize).To(Equal(int64(0))) 119 }).ProbeEvery(200 * time.Millisecond).Should(Succeed()) 120 }) 121 122 It("handles changed files", func() { 123 // Create empty file 124 _, err := os.Create(root + "/changed.txt") 125 Expect(err).ToNot(HaveOccurred()) 126 127 Eventually(func(g Gomega) { 128 n, err := env.Lookup.NodeFromResource(env.Ctx, &provider.Reference{ 129 ResourceId: env.SpaceRootRes, 130 Path: subtree + "/changed.txt", 131 }) 132 g.Expect(err).ToNot(HaveOccurred()) 133 g.Expect(n).ToNot(BeNil()) 134 g.Expect(n.ID).ToNot(BeEmpty()) 135 g.Expect(n.Blobsize).To(Equal(int64(0))) 136 }).ProbeEvery(200 * time.Millisecond).Should(Succeed()) 137 138 // Change file content 139 Expect(os.WriteFile(root+"/changed.txt", []byte("hello world"), 0600)).To(Succeed()) 140 141 Eventually(func(g Gomega) { 142 n, err := env.Lookup.NodeFromResource(env.Ctx, &provider.Reference{ 143 ResourceId: env.SpaceRootRes, 144 Path: subtree + "/changed.txt", 145 }) 146 g.Expect(err).ToNot(HaveOccurred()) 147 g.Expect(n).ToNot(BeNil()) 148 g.Expect(n.Type(env.Ctx)).To(Equal(provider.ResourceType_RESOURCE_TYPE_FILE)) 149 g.Expect(n.ID).ToNot(BeEmpty()) 150 g.Expect(n.Blobsize).To(Equal(int64(11))) 151 }).Should(Succeed()) 152 }) 153 154 It("handles deleted files", func() { 155 _, err := os.Create(root + "/deleted.txt") 156 Expect(err).ToNot(HaveOccurred()) 157 158 Eventually(func(g Gomega) { 159 n, err := env.Lookup.NodeFromResource(env.Ctx, &provider.Reference{ 160 ResourceId: env.SpaceRootRes, 161 Path: subtree + "/deleted.txt", 162 }) 163 g.Expect(err).ToNot(HaveOccurred()) 164 g.Expect(n).ToNot(BeNil()) 165 g.Expect(n.Type(env.Ctx)).To(Equal(provider.ResourceType_RESOURCE_TYPE_FILE)) 166 g.Expect(n.ID).ToNot(BeEmpty()) 167 }).Should(Succeed()) 168 169 Expect(os.Remove(root + "/deleted.txt")).To(Succeed()) 170 171 Eventually(func(g Gomega) { 172 n, err := env.Lookup.NodeFromResource(env.Ctx, &provider.Reference{ 173 ResourceId: env.SpaceRootRes, 174 Path: subtree + "/deleted.txt", 175 }) 176 g.Expect(err).ToNot(HaveOccurred()) 177 g.Expect(n.Exists).To(BeFalse()) 178 }).Should(Succeed()) 179 }) 180 181 It("handles moved files", func() { 182 // Create empty file 183 _, err := os.Create(root + "/original.txt") 184 Expect(err).ToNot(HaveOccurred()) 185 186 fileID := "" 187 // Wait for the file to be indexed 188 Eventually(func(g Gomega) { 189 n, err := env.Lookup.NodeFromResource(env.Ctx, &provider.Reference{ 190 ResourceId: env.SpaceRootRes, 191 Path: subtree + "/original.txt", 192 }) 193 g.Expect(err).ToNot(HaveOccurred()) 194 g.Expect(n).ToNot(BeNil()) 195 g.Expect(n.Type(env.Ctx)).To(Equal(provider.ResourceType_RESOURCE_TYPE_FILE)) 196 g.Expect(n.ID).ToNot(BeEmpty()) 197 fileID = n.ID 198 g.Expect(n.Blobsize).To(Equal(int64(0))) 199 }).Should(Succeed()) 200 201 // Move file 202 Expect(os.Rename(root+"/original.txt", root+"/moved.txt")).To(Succeed()) 203 204 // Wait for the file to be indexed 205 Eventually(func(g Gomega) { 206 n, err := env.Lookup.NodeFromResource(env.Ctx, &provider.Reference{ 207 ResourceId: env.SpaceRootRes, 208 Path: subtree + "/original.txt", 209 }) 210 g.Expect(err).ToNot(HaveOccurred()) 211 g.Expect(n.Exists).To(BeFalse()) 212 }).Should(Succeed()) 213 214 Eventually(func(g Gomega) { 215 n, err := env.Lookup.NodeFromResource(env.Ctx, &provider.Reference{ 216 ResourceId: env.SpaceRootRes, 217 Path: subtree + "/moved.txt", 218 }) 219 g.Expect(err).ToNot(HaveOccurred()) 220 g.Expect(n).ToNot(BeNil()) 221 g.Expect(n.Type(env.Ctx)).To(Equal(provider.ResourceType_RESOURCE_TYPE_FILE)) 222 g.Expect(n.ID).To(Equal(fileID)) 223 g.Expect(n.Blobsize).To(Equal(int64(0))) 224 }).Should(Succeed()) 225 }) 226 227 It("handles id clashes", func() { 228 // Create empty file 229 _, err := os.Create(root + "/original.txt") 230 Expect(err).ToNot(HaveOccurred()) 231 232 fileID := "" 233 // Wait for the file to be indexed 234 Eventually(func(g Gomega) { 235 n, err := env.Lookup.NodeFromResource(env.Ctx, &provider.Reference{ 236 ResourceId: env.SpaceRootRes, 237 Path: subtree + "/original.txt", 238 }) 239 g.Expect(err).ToNot(HaveOccurred()) 240 g.Expect(n).ToNot(BeNil()) 241 g.Expect(n.Type(env.Ctx)).To(Equal(provider.ResourceType_RESOURCE_TYPE_FILE)) 242 g.Expect(n.ID).ToNot(BeEmpty()) 243 fileID = n.ID 244 g.Expect(n.Blobsize).To(Equal(int64(0))) 245 }).Should(Succeed()) 246 247 // cp file 248 cmd := exec.Command("cp", "-a", root+"/original.txt", root+"/moved.txt") 249 err = cmd.Run() 250 Expect(err).ToNot(HaveOccurred()) 251 252 Eventually(func(g Gomega) { 253 n, err := env.Lookup.NodeFromResource(env.Ctx, &provider.Reference{ 254 ResourceId: env.SpaceRootRes, 255 Path: subtree + "/moved.txt", 256 }) 257 g.Expect(err).ToNot(HaveOccurred()) 258 g.Expect(n).ToNot(BeNil()) 259 g.Expect(n.Type(env.Ctx)).To(Equal(provider.ResourceType_RESOURCE_TYPE_FILE)) 260 g.Expect(n.ID).ToNot(BeEmpty()) 261 g.Expect(n.ID).ToNot(Equal(fileID)) 262 g.Expect(n.Blobsize).To(Equal(int64(0))) 263 }).Should(Succeed()) 264 }) 265 }) 266 267 Describe("of directories", func() { 268 It("handles new directories", func() { 269 Expect(os.Mkdir(root+"/assimilated", 0700)).To(Succeed()) 270 271 Eventually(func(g Gomega) { 272 n, err := env.Lookup.NodeFromResource(env.Ctx, &provider.Reference{ 273 ResourceId: env.SpaceRootRes, 274 Path: subtree + "/assimilated", 275 }) 276 g.Expect(err).ToNot(HaveOccurred()) 277 g.Expect(n).ToNot(BeNil()) 278 g.Expect(n.Type(env.Ctx)).To(Equal(provider.ResourceType_RESOURCE_TYPE_CONTAINER)) 279 g.Expect(n.ID).ToNot(BeEmpty()) 280 }).Should(Succeed()) 281 }) 282 283 It("handles files in directories", func() { 284 Expect(os.Mkdir(root+"/assimilated", 0700)).To(Succeed()) 285 time.Sleep(100 * time.Millisecond) // Give it some time to settle down 286 Expect(os.WriteFile(root+"/assimilated/file.txt", []byte("hello world"), 0600)).To(Succeed()) 287 288 Eventually(func(g Gomega) { 289 n, err := env.Lookup.NodeFromResource(env.Ctx, &provider.Reference{ 290 ResourceId: env.SpaceRootRes, 291 Path: subtree + "/assimilated", 292 }) 293 g.Expect(err).ToNot(HaveOccurred()) 294 g.Expect(n).ToNot(BeNil()) 295 g.Expect(n.Type(env.Ctx)).To(Equal(provider.ResourceType_RESOURCE_TYPE_CONTAINER)) 296 g.Expect(n.ID).ToNot(BeEmpty()) 297 g.Expect(n.GetTreeSize(env.Ctx)).To(Equal(uint64(11))) 298 }).Should(Succeed()) 299 }) 300 301 It("handles deleted directories", func() { 302 Expect(os.Mkdir(root+"/deleted", 0700)).To(Succeed()) 303 304 Eventually(func(g Gomega) { 305 n, err := env.Lookup.NodeFromResource(env.Ctx, &provider.Reference{ 306 ResourceId: env.SpaceRootRes, 307 Path: subtree + "/deleted", 308 }) 309 g.Expect(err).ToNot(HaveOccurred()) 310 g.Expect(n).ToNot(BeNil()) 311 g.Expect(n.Type(env.Ctx)).To(Equal(provider.ResourceType_RESOURCE_TYPE_CONTAINER)) 312 g.Expect(n.ID).ToNot(BeEmpty()) 313 }).Should(Succeed()) 314 315 Expect(os.Remove(root + "/deleted")).To(Succeed()) 316 317 Eventually(func(g Gomega) { 318 n, err := env.Lookup.NodeFromResource(env.Ctx, &provider.Reference{ 319 ResourceId: env.SpaceRootRes, 320 Path: subtree + "/deleted", 321 }) 322 g.Expect(err).ToNot(HaveOccurred()) 323 g.Expect(n.Exists).To(BeFalse()) 324 }).Should(Succeed()) 325 }) 326 327 It("handles moved directories", func() { 328 Expect(os.Mkdir(root+"/original", 0700)).To(Succeed()) 329 time.Sleep(100 * time.Millisecond) // Give it some time to settle down 330 Expect(os.WriteFile(root+"/original/file.txt", []byte("hello world"), 0600)).To(Succeed()) 331 332 dirId := "" 333 Eventually(func(g Gomega) { 334 n, err := env.Lookup.NodeFromResource(env.Ctx, &provider.Reference{ 335 ResourceId: env.SpaceRootRes, 336 Path: subtree + "/original", 337 }) 338 g.Expect(err).ToNot(HaveOccurred()) 339 g.Expect(n).ToNot(BeNil()) 340 g.Expect(n.Type(env.Ctx)).To(Equal(provider.ResourceType_RESOURCE_TYPE_CONTAINER)) 341 g.Expect(n.ID).ToNot(BeEmpty()) 342 g.Expect(n.GetTreeSize(env.Ctx)).To(Equal(uint64(11))) 343 dirId = n.ID 344 }).Should(Succeed()) 345 346 Expect(os.Rename(root+"/original", root+"/moved")).To(Succeed()) 347 348 Eventually(func(g Gomega) { 349 n, err := env.Lookup.NodeFromResource(env.Ctx, &provider.Reference{ 350 ResourceId: env.SpaceRootRes, 351 Path: subtree + "/original", 352 }) 353 g.Expect(err).ToNot(HaveOccurred()) 354 g.Expect(n.Exists).To(BeFalse()) 355 }).Should(Succeed()) 356 357 Eventually(func(g Gomega) { 358 n, err := env.Lookup.NodeFromResource(env.Ctx, &provider.Reference{ 359 ResourceId: env.SpaceRootRes, 360 Path: subtree + "/moved", 361 }) 362 g.Expect(err).ToNot(HaveOccurred()) 363 g.Expect(n).ToNot(BeNil()) 364 g.Expect(n.Exists).To(BeTrue()) 365 g.Expect(n.Type(env.Ctx)).To(Equal(provider.ResourceType_RESOURCE_TYPE_CONTAINER)) 366 g.Expect(n.ID).To(Equal(dirId)) 367 g.Expect(n.GetTreeSize(env.Ctx)).To(Equal(uint64(11))) 368 }).Should(Succeed()) 369 }) 370 }) 371 }) 372 373 Describe("propagation", func() { 374 It("propagates new files in a directory", func() { 375 Expect(os.Mkdir(root+"/assimilated", 0700)).To(Succeed()) 376 time.Sleep(100 * time.Millisecond) // Give it some time to settle down 377 Expect(os.WriteFile(root+"/assimilated/file.txt", []byte("hello world"), 0600)).To(Succeed()) 378 379 Eventually(func(g Gomega) { 380 n, err := env.Lookup.NodeFromResource(env.Ctx, &provider.Reference{ 381 ResourceId: env.SpaceRootRes, 382 383 Path: subtree + "/assimilated", 384 }) 385 g.Expect(err).ToNot(HaveOccurred()) 386 g.Expect(n).ToNot(BeNil()) 387 g.Expect(n.Type(env.Ctx)).To(Equal(provider.ResourceType_RESOURCE_TYPE_CONTAINER)) 388 g.Expect(n.ID).ToNot(BeEmpty()) 389 g.Expect(n.GetTreeSize(env.Ctx)).To(Equal(uint64(11))) 390 }).Should(Succeed()) 391 392 Expect(os.WriteFile(root+"/assimilated/file2.txt", []byte("hello world"), 0600)).To(Succeed()) 393 394 Eventually(func(g Gomega) { 395 n, err := env.Lookup.NodeFromResource(env.Ctx, &provider.Reference{ 396 ResourceId: env.SpaceRootRes, 397 398 Path: subtree + "/assimilated", 399 }) 400 g.Expect(err).ToNot(HaveOccurred()) 401 g.Expect(n).ToNot(BeNil()) 402 g.Expect(n.Type(env.Ctx)).To(Equal(provider.ResourceType_RESOURCE_TYPE_CONTAINER)) 403 g.Expect(n.ID).ToNot(BeEmpty()) 404 g.Expect(n.GetTreeSize(env.Ctx)).To(Equal(uint64(22))) 405 }).Should(Succeed()) 406 }) 407 408 It("propagates new files in a directory to the parent", func() { 409 Expect(env.Tree.WarmupIDCache(env.Root, false, true)).To(Succeed()) 410 Expect(os.Mkdir(root+"/assimilated", 0700)).To(Succeed()) 411 time.Sleep(100 * time.Millisecond) // Give it some time to settle down 412 Expect(os.WriteFile(root+"/assimilated/file.txt", []byte("hello world"), 0600)).To(Succeed()) 413 414 Eventually(func(g Gomega) { 415 n, err := env.Lookup.NodeFromResource(env.Ctx, &provider.Reference{ 416 ResourceId: env.SpaceRootRes, 417 418 Path: subtree + "/assimilated", 419 }) 420 g.Expect(err).ToNot(HaveOccurred()) 421 g.Expect(n).ToNot(BeNil()) 422 g.Expect(n.Type(env.Ctx)).To(Equal(provider.ResourceType_RESOURCE_TYPE_CONTAINER)) 423 g.Expect(n.ID).ToNot(BeEmpty()) 424 g.Expect(n.GetTreeSize(env.Ctx)).To(Equal(uint64(11))) 425 }).Should(Succeed()) 426 427 Eventually(func(g Gomega) { 428 n, err := env.Lookup.NodeFromResource(env.Ctx, &provider.Reference{ 429 ResourceId: env.SpaceRootRes, 430 431 Path: subtree, 432 }) 433 g.Expect(err).ToNot(HaveOccurred()) 434 g.Expect(n).ToNot(BeNil()) 435 g.Expect(n.Type(env.Ctx)).To(Equal(provider.ResourceType_RESOURCE_TYPE_CONTAINER)) 436 g.Expect(n.ID).ToNot(BeEmpty()) 437 g.Expect(n.GetTreeSize(env.Ctx)).To(Equal(uint64(11))) 438 }).Should(Succeed()) 439 440 Expect(os.WriteFile(root+"/assimilated/file2.txt", []byte("hello world"), 0600)).To(Succeed()) 441 442 Eventually(func(g Gomega) { 443 n, err := env.Lookup.NodeFromResource(env.Ctx, &provider.Reference{ 444 ResourceId: env.SpaceRootRes, 445 446 Path: subtree + "/assimilated", 447 }) 448 g.Expect(err).ToNot(HaveOccurred()) 449 g.Expect(n).ToNot(BeNil()) 450 g.Expect(n.Type(env.Ctx)).To(Equal(provider.ResourceType_RESOURCE_TYPE_CONTAINER)) 451 g.Expect(n.ID).ToNot(BeEmpty()) 452 g.Expect(n.GetTreeSize(env.Ctx)).To(Equal(uint64(22))) 453 }).Should(Succeed()) 454 455 Eventually(func(g Gomega) { 456 n, err := env.Lookup.NodeFromResource(env.Ctx, &provider.Reference{ 457 ResourceId: env.SpaceRootRes, 458 459 Path: subtree, 460 }) 461 g.Expect(err).ToNot(HaveOccurred()) 462 g.Expect(n).ToNot(BeNil()) 463 g.Expect(n.Type(env.Ctx)).To(Equal(provider.ResourceType_RESOURCE_TYPE_CONTAINER)) 464 g.Expect(n.ID).ToNot(BeEmpty()) 465 g.Expect(n.GetTreeSize(env.Ctx)).To(Equal(uint64(22))) 466 }).Should(Succeed()) 467 }) 468 469 }) 470 })