github.com/NVIDIA/aistore@v1.3.23-0.20240517131212-7df6609be51d/core/lom_xattr_test.go (about)

     1  // Package core_test provides tests for cluster package
     2  /*
     3  * Copyright (c) 2018-2022, NVIDIA CORPORATION. All rights reserved.
     4   */
     5  package core_test
     6  
     7  import (
     8  	"os"
     9  
    10  	"github.com/NVIDIA/aistore/api/apc"
    11  	"github.com/NVIDIA/aistore/cmn"
    12  	"github.com/NVIDIA/aistore/cmn/cos"
    13  	"github.com/NVIDIA/aistore/core"
    14  	"github.com/NVIDIA/aistore/core/meta"
    15  	"github.com/NVIDIA/aistore/core/mock"
    16  	"github.com/NVIDIA/aistore/fs"
    17  	. "github.com/onsi/ginkgo/v2"
    18  	. "github.com/onsi/gomega"
    19  )
    20  
    21  var _ = Describe("LOM Xattributes", func() {
    22  	const (
    23  		tmpDir     = "/tmp/lom_xattr_test"
    24  		xattrMpath = tmpDir + "/xattr"
    25  		copyMpath  = tmpDir + "/copy"
    26  
    27  		bucketLocal  = "LOM_TEST_Local"
    28  		bucketCached = "LOM_TEST_Cached"
    29  	)
    30  
    31  	localBck := cmn.Bck{Name: bucketLocal, Provider: apc.AIS, Ns: cmn.NsGlobal}
    32  	cachedBck := cmn.Bck{Name: bucketCached, Provider: apc.AIS, Ns: cmn.NsGlobal}
    33  
    34  	fs.CSM.Reg(fs.ObjectType, &fs.ObjectContentResolver{}, true)
    35  	fs.CSM.Reg(fs.WorkfileType, &fs.WorkfileContentResolver{}, true)
    36  
    37  	var (
    38  		copyMpathInfo *fs.Mountpath
    39  		mix           = fs.Mountpath{Path: xattrMpath}
    40  		bmdMock       = mock.NewBaseBownerMock(
    41  			meta.NewBck(
    42  				bucketLocal, apc.AIS, cmn.NsGlobal,
    43  				&cmn.Bprops{Cksum: cmn.CksumConf{Type: cos.ChecksumXXHash}, BID: 201},
    44  			),
    45  			meta.NewBck(
    46  				bucketCached, apc.AIS, cmn.NsGlobal,
    47  				&cmn.Bprops{
    48  					Cksum:       cmn.CksumConf{Type: cos.ChecksumXXHash},
    49  					WritePolicy: cmn.WritePolicyConf{Data: apc.WriteImmediate, MD: apc.WriteNever},
    50  					BID:         202,
    51  				},
    52  			),
    53  		)
    54  	)
    55  
    56  	BeforeEach(func() {
    57  		_ = cos.CreateDir(xattrMpath)
    58  		_ = cos.CreateDir(copyMpath)
    59  
    60  		_, _ = fs.Add(xattrMpath, "daeID")
    61  		_, _ = fs.Add(copyMpath, "daeID")
    62  
    63  		available := fs.GetAvail()
    64  		copyMpathInfo = available[copyMpath]
    65  
    66  		_ = mock.NewTarget(bmdMock)
    67  	})
    68  
    69  	AfterEach(func() {
    70  		_, _ = fs.Remove(xattrMpath)
    71  		_, _ = fs.Remove(copyMpath)
    72  		_ = os.RemoveAll(tmpDir)
    73  	})
    74  
    75  	Describe("xattrs", func() {
    76  		var (
    77  			testFileSize   = 456
    78  			testObjectName = "xattr-foldr/test-obj.ext"
    79  
    80  			// Bucket needs to have checksum enabled
    81  			localFQN  = mix.MakePathFQN(&localBck, fs.ObjectType, testObjectName+".qqq")
    82  			cachedFQN = mix.MakePathFQN(&cachedBck, fs.ObjectType, testObjectName)
    83  
    84  			fqns []string
    85  		)
    86  
    87  		BeforeEach(func() {
    88  			fqns = []string{
    89  				copyMpathInfo.MakePathFQN(&localBck, fs.ObjectType, "copy/111/fqn"),
    90  				copyMpathInfo.MakePathFQN(&localBck, fs.ObjectType, "other/copy/fqn"),
    91  			}
    92  
    93  			// NOTE:
    94  			// the test creates copies; there's a built-in assumption that `mi` will be
    95  			// the HRW mountpath,
    96  			// while `mi2` will not (and, therefore, can be used to place the copy).
    97  			// Ultimately, this depends on the specific HRW hash; adding
    98  			// Expect(lom.IsHRW()).To(Be...)) to catch that sooner.
    99  
   100  			for _, fqn := range fqns {
   101  				lom := filePut(fqn, testFileSize)
   102  				Expect(lom.IsHRW()).To(BeFalse())
   103  			}
   104  		})
   105  
   106  		Describe("Persist", func() {
   107  			It("should save correct meta to disk", func() {
   108  				lom := filePut(localFQN, testFileSize)
   109  				Expect(lom.IsHRW()).To(BeTrue())
   110  				lom.Lock(true)
   111  				defer lom.Unlock(true)
   112  				lom.SetCksum(cos.NewCksum(cos.ChecksumXXHash, "test_checksum"))
   113  				lom.SetVersion("dummy_version")
   114  				lom.SetCustomMD(cos.StrKVs{
   115  					cmn.SourceObjMD: apc.GCP,
   116  					cmn.ETag:        "etag",
   117  					cmn.CRC32CObjMD: "crc32",
   118  				})
   119  				Expect(lom.AddCopy(fqns[0], copyMpathInfo)).NotTo(HaveOccurred())
   120  				Expect(lom.AddCopy(fqns[1], copyMpathInfo)).NotTo(HaveOccurred())
   121  				Expect(persist(lom)).NotTo(HaveOccurred())
   122  
   123  				b, err := fs.GetXattr(localFQN, core.XattrLOM)
   124  				Expect(b).ToNot(BeEmpty())
   125  				Expect(err).NotTo(HaveOccurred())
   126  
   127  				hrwLom := &core.LOM{ObjName: testObjectName}
   128  				Expect(hrwLom.InitBck(&localBck)).NotTo(HaveOccurred())
   129  				hrwLom.UncacheUnless()
   130  
   131  				newLom := NewBasicLom(localFQN)
   132  				err = newLom.Load(false, true)
   133  				Expect(err).NotTo(HaveOccurred())
   134  				Expect(lom.Checksum()).To(BeEquivalentTo(newLom.Checksum()))
   135  				Expect(lom.Version()).To(BeEquivalentTo(newLom.Version()))
   136  				Expect(lom.GetCopies()).To(HaveLen(3))
   137  				Expect(lom.GetCopies()).To(BeEquivalentTo(newLom.GetCopies()))
   138  				Expect(lom.GetCustomMD()).To(HaveLen(3))
   139  				Expect(lom.GetCustomMD()).To(BeEquivalentTo(newLom.GetCustomMD()))
   140  			})
   141  
   142  			It("should _not_ save meta to disk", func() {
   143  				lom := filePut(cachedFQN, testFileSize)
   144  				Expect(lom.IsHRW()).To(BeTrue())
   145  				lom.Lock(true)
   146  				defer lom.Unlock(true)
   147  				lom.SetCksum(cos.NewCksum(cos.ChecksumXXHash, "test_checksum"))
   148  				lom.SetVersion("dummy_version")
   149  				Expect(persist(lom)).NotTo(HaveOccurred())
   150  
   151  				lom.SetCustomMD(cos.StrKVs{
   152  					cmn.SourceObjMD: apc.GCP,
   153  					cmn.ETag:        "etag",
   154  					cmn.CRC32CObjMD: "crc32",
   155  				})
   156  				Expect(lom.AddCopy(fqns[0], copyMpathInfo)).NotTo(HaveOccurred())
   157  				Expect(lom.AddCopy(fqns[1], copyMpathInfo)).NotTo(HaveOccurred())
   158  				Expect(persist(lom)).NotTo(HaveOccurred())
   159  
   160  				b, err := fs.GetXattr(cachedFQN, core.XattrLOM)
   161  				Expect(b).To(BeEmpty())
   162  				Expect(err).To(HaveOccurred())
   163  
   164  				hrwLom := &core.LOM{ObjName: testObjectName}
   165  				Expect(hrwLom.InitBck(&localBck)).NotTo(HaveOccurred())
   166  				hrwLom.UncacheUnless()
   167  
   168  				ver := lom.Version()
   169  				lom.UncacheUnless()
   170  
   171  				newLom := NewBasicLom(cachedFQN)
   172  				err = newLom.Load(false, true)
   173  				Expect(err).NotTo(HaveOccurred())
   174  				Expect(lom.Checksum()).To(BeEquivalentTo(newLom.Checksum()))
   175  				Expect(ver).To(BeEquivalentTo(newLom.Version()))
   176  				Expect(lom.GetCopies()).To(HaveLen(3))
   177  				Expect(lom.GetCopies()).To(BeEquivalentTo(newLom.GetCopies()))
   178  				Expect(lom.GetCustomMD()).To(HaveLen(3))
   179  				Expect(lom.GetCustomMD()).To(BeEquivalentTo(newLom.GetCustomMD()))
   180  			})
   181  
   182  			It("should copy object with meta in memory", func() {
   183  				lom := filePut(cachedFQN, testFileSize)
   184  				lom.Lock(true)
   185  				defer lom.Unlock(true)
   186  				cksumHash, err := lom.ComputeCksum(lom.CksumType())
   187  				Expect(err).NotTo(HaveOccurred())
   188  				lom.SetCksum(cksumHash.Clone())
   189  				lom.SetVersion("first_version")
   190  				Expect(persist(lom)).NotTo(HaveOccurred())
   191  
   192  				lom.SetCustomMD(cos.StrKVs{
   193  					cmn.SourceObjMD: apc.GCP,
   194  					cmn.ETag:        "etag",
   195  					cmn.CRC32CObjMD: "crc32",
   196  				})
   197  				Expect(lom.AddCopy(fqns[0], copyMpathInfo)).NotTo(HaveOccurred())
   198  				Expect(lom.AddCopy(fqns[1], copyMpathInfo)).NotTo(HaveOccurred())
   199  				lom.SetVersion("second_version")
   200  				Expect(persist(lom)).NotTo(HaveOccurred())
   201  
   202  				b, err := fs.GetXattr(cachedFQN, core.XattrLOM)
   203  				Expect(b).To(BeEmpty())
   204  				Expect(err).To(HaveOccurred())
   205  
   206  				hrwLom := &core.LOM{ObjName: testObjectName}
   207  				Expect(hrwLom.InitBck(&localBck)).NotTo(HaveOccurred())
   208  				hrwLom.UncacheUnless()
   209  
   210  				lom.UncacheUnless()
   211  				lom.Load(true, false)
   212  				Expect(lom.Version()).To(BeEquivalentTo("second_version"))
   213  				Expect(lom.GetCopies()).To(HaveLen(3))
   214  
   215  				buf := make([]byte, cos.KiB)
   216  				newLom, err := lom.Copy2FQN(cachedFQN+"-copy", buf)
   217  				Expect(err).NotTo(HaveOccurred())
   218  
   219  				err = newLom.Load(false, false)
   220  				Expect(err).NotTo(HaveOccurred())
   221  				Expect(lom.Checksum()).To(BeEquivalentTo(newLom.Checksum()))
   222  				Expect(lom.Version()).To(BeEquivalentTo(newLom.Version()))
   223  				Expect(newLom.GetCustomMD()).To(HaveLen(3))
   224  				Expect(lom.GetCustomMD()).To(BeEquivalentTo(newLom.GetCustomMD()))
   225  			})
   226  
   227  			It("should override old values", func() {
   228  				lom := filePut(localFQN, testFileSize)
   229  				lom.Lock(true)
   230  				defer lom.Unlock(true)
   231  				lom.SetCksum(cos.NewCksum(cos.ChecksumXXHash, "test_checksum"))
   232  				lom.SetVersion("dummy_version1")
   233  				Expect(lom.AddCopy(fqns[0], copyMpathInfo)).NotTo(HaveOccurred())
   234  				Expect(lom.AddCopy(fqns[1], copyMpathInfo)).NotTo(HaveOccurred())
   235  
   236  				lom.SetCksum(cos.NewCksum(cos.ChecksumXXHash, "test_checksum"))
   237  				lom.SetVersion("dummy_version2")
   238  				Expect(lom.AddCopy(fqns[0], copyMpathInfo)).NotTo(HaveOccurred())
   239  				Expect(lom.AddCopy(fqns[1], copyMpathInfo)).NotTo(HaveOccurred())
   240  				Expect(persist(lom)).NotTo(HaveOccurred())
   241  
   242  				b, err := fs.GetXattr(localFQN, core.XattrLOM)
   243  				Expect(b).ToNot(BeEmpty())
   244  				Expect(err).NotTo(HaveOccurred())
   245  
   246  				hrwLom := &core.LOM{ObjName: testObjectName}
   247  				Expect(hrwLom.InitBck(&localBck)).NotTo(HaveOccurred())
   248  				hrwLom.UncacheUnless()
   249  
   250  				newLom := NewBasicLom(localFQN)
   251  				err = newLom.Load(false, true)
   252  				Expect(err).NotTo(HaveOccurred())
   253  				Expect(lom.Checksum()).To(BeEquivalentTo(newLom.Checksum()))
   254  				Expect(lom.Version()).To(BeEquivalentTo(newLom.Version()))
   255  				Expect(lom.GetCopies()).To(HaveLen(3))
   256  				Expect(lom.GetCopies()).To(BeEquivalentTo(newLom.GetCopies()))
   257  			})
   258  		})
   259  
   260  		Describe("LoadMetaFromFS", func() {
   261  			It("should read fresh meta from fs", func() {
   262  				createTestFile(localFQN, testFileSize)
   263  				lom1 := NewBasicLom(localFQN)
   264  				lom2 := NewBasicLom(localFQN)
   265  				lom1.Lock(true)
   266  				defer lom1.Unlock(true)
   267  				lom1.SetCksum(cos.NewCksum(cos.ChecksumXXHash, "test_checksum"))
   268  				lom1.SetVersion("dummy_version")
   269  				Expect(lom1.AddCopy(fqns[0], copyMpathInfo)).NotTo(HaveOccurred())
   270  				Expect(lom1.AddCopy(fqns[1], copyMpathInfo)).NotTo(HaveOccurred())
   271  				Expect(persist(lom1)).NotTo(HaveOccurred())
   272  
   273  				err := lom2.LoadMetaFromFS()
   274  				Expect(err).NotTo(HaveOccurred())
   275  
   276  				Expect(lom1.Checksum()).To(BeEquivalentTo(lom2.Checksum()))
   277  				Expect(lom1.Version(true)).To(BeEquivalentTo(lom2.Version(true)))
   278  				Expect(lom1.GetCopies()).To(HaveLen(3))
   279  				Expect(lom1.GetCopies()).To(BeEquivalentTo(lom2.GetCopies()))
   280  			})
   281  
   282  			Describe("error cases", func() {
   283  				var lom *core.LOM
   284  
   285  				BeforeEach(func() {
   286  					createTestFile(localFQN, testFileSize)
   287  					lom = NewBasicLom(localFQN)
   288  					lom.Lock(true)
   289  					defer lom.Unlock(true)
   290  					lom.SetCksum(cos.NewCksum(cos.ChecksumXXHash, "test_checksum"))
   291  					lom.SetVersion("dummy_version")
   292  					Expect(lom.AddCopy(fqns[0], copyMpathInfo)).NotTo(HaveOccurred())
   293  					Expect(lom.AddCopy(fqns[1], copyMpathInfo)).NotTo(HaveOccurred())
   294  					Expect(persist(lom)).NotTo(HaveOccurred())
   295  				})
   296  
   297  				It("should fail when checksum does not match", func() {
   298  					b, err := fs.GetXattr(localFQN, core.XattrLOM)
   299  					Expect(err).NotTo(HaveOccurred())
   300  
   301  					b[2]++ // changing first byte of meta checksum
   302  					Expect(fs.SetXattr(localFQN, core.XattrLOM, b)).NotTo(HaveOccurred())
   303  					err = lom.LoadMetaFromFS()
   304  					Expect(err.Error()).To(ContainSubstring("BAD META CHECKSUM"))
   305  				})
   306  
   307  				It("should fail when checksum type is invalid", func() {
   308  					b, err := fs.GetXattr(localFQN, core.XattrLOM)
   309  					Expect(err).NotTo(HaveOccurred())
   310  
   311  					b[1] = 200 // corrupting checksum type
   312  					Expect(fs.SetXattr(localFQN, core.XattrLOM, b)).NotTo(HaveOccurred())
   313  					err = lom.LoadMetaFromFS()
   314  					Expect(err).To(MatchError("invalid lmeta: unknown checksum 200"))
   315  
   316  					b, err = fs.GetXattr(localFQN, core.XattrLOM)
   317  					Expect(err).NotTo(HaveOccurred())
   318  
   319  					b[1] = 0 // corrupting checksum type
   320  					Expect(fs.SetXattr(localFQN, core.XattrLOM, b)).NotTo(HaveOccurred())
   321  					err = lom.LoadMetaFromFS()
   322  					Expect(err).To(MatchError("invalid lmeta: unknown checksum 0"))
   323  				})
   324  
   325  				It("should fail when metadata version is invalid", func() {
   326  					b, err := fs.GetXattr(localFQN, core.XattrLOM)
   327  					Expect(err).NotTo(HaveOccurred())
   328  
   329  					b[0] = 128 // corrupting metadata version
   330  					Expect(fs.SetXattr(localFQN, core.XattrLOM, b)).NotTo(HaveOccurred())
   331  					err = lom.LoadMetaFromFS()
   332  					Expect(err).To(MatchError("invalid lmeta: unknown version 128"))
   333  
   334  					b[0] = 0 // corrupting metadata version
   335  					Expect(fs.SetXattr(localFQN, core.XattrLOM, b)).NotTo(HaveOccurred())
   336  					err = lom.LoadMetaFromFS()
   337  					Expect(err).To(MatchError("invalid lmeta: unknown version 0"))
   338  				})
   339  
   340  				It("should fail when metadata is too short", func() {
   341  					Expect(fs.SetXattr(localFQN, core.XattrLOM, []byte{1})).NotTo(HaveOccurred())
   342  					err := lom.LoadMetaFromFS()
   343  					Expect(err).To(HaveOccurred())
   344  
   345  					Expect(fs.SetXattr(localFQN, core.XattrLOM, []byte{1, 1, 2})).NotTo(HaveOccurred())
   346  					err = lom.LoadMetaFromFS()
   347  					Expect(err).To(MatchError("invalid lmeta: too short (3)"))
   348  				})
   349  
   350  				It("should fail when meta is corrupted", func() {
   351  					// This test is supposed to end with LoadMetaFromFS error
   352  					// not with nil pointer exception / panic
   353  					b, err := fs.GetXattr(localFQN, core.XattrLOM)
   354  					Expect(err).NotTo(HaveOccurred())
   355  					copy(b[40:], "1321wr")
   356  					Expect(fs.SetXattr(localFQN, core.XattrLOM, b)).NotTo(HaveOccurred())
   357  
   358  					err = lom.LoadMetaFromFS()
   359  					Expect(err.Error()).To(ContainSubstring("BAD META CHECKSUM"))
   360  				})
   361  			})
   362  		})
   363  	})
   364  })