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  })