github.com/cs3org/reva/v2@v2.27.7/pkg/storage/utils/decomposedfs/node/locks_test.go (about)

     1  // Copyright 2018-2021 CERN
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  //
    15  // In applying this license, CERN does not waive the privileges and immunities
    16  // granted to it by virtue of its status as an Intergovernmental Organization
    17  // or submit itself to any jurisdiction.
    18  
    19  package node_test
    20  
    21  import (
    22  	"context"
    23  	"os"
    24  
    25  	"github.com/google/uuid"
    26  	. "github.com/onsi/ginkgo/v2"
    27  	. "github.com/onsi/gomega"
    28  	"google.golang.org/protobuf/testing/protocmp"
    29  
    30  	userpb "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1"
    31  	provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
    32  	ctxpkg "github.com/cs3org/reva/v2/pkg/ctx"
    33  	"github.com/cs3org/reva/v2/pkg/errtypes"
    34  	"github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/node"
    35  	helpers "github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/testhelpers"
    36  )
    37  
    38  var _ = Describe("Node locks", func() {
    39  	var (
    40  		env *helpers.TestEnv
    41  
    42  		lockByUser      *provider.Lock
    43  		wrongLockByUser *provider.Lock
    44  		lockByApp       *provider.Lock
    45  		wrongLockByApp  *provider.Lock
    46  		n               *node.Node
    47  		n2              *node.Node
    48  
    49  		otherUser = &userpb.User{
    50  			Id: &userpb.UserId{
    51  				Idp:      "idp",
    52  				OpaqueId: "otheruserid",
    53  				Type:     userpb.UserType_USER_TYPE_PRIMARY,
    54  			},
    55  			Username: "foo",
    56  		}
    57  		otherCtx = ctxpkg.ContextSetUser(context.Background(), otherUser)
    58  	)
    59  
    60  	BeforeEach(func() {
    61  		var err error
    62  		env, err = helpers.NewTestEnv(nil)
    63  		Expect(err).ToNot(HaveOccurred())
    64  
    65  		lockByUser = &provider.Lock{
    66  			Type:   provider.LockType_LOCK_TYPE_EXCL,
    67  			User:   env.Owner.Id,
    68  			LockId: uuid.New().String(),
    69  		}
    70  		wrongLockByUser = &provider.Lock{
    71  			Type:   provider.LockType_LOCK_TYPE_EXCL,
    72  			User:   env.Owner.Id,
    73  			LockId: uuid.New().String(),
    74  		}
    75  		lockByApp = &provider.Lock{
    76  			Type:    provider.LockType_LOCK_TYPE_WRITE,
    77  			AppName: "app1",
    78  			LockId:  uuid.New().String(),
    79  		}
    80  		wrongLockByApp = &provider.Lock{
    81  			Type:    provider.LockType_LOCK_TYPE_WRITE,
    82  			AppName: "app2",
    83  			LockId:  uuid.New().String(),
    84  		}
    85  		spaceResID, err := env.CreateTestStorageSpace("project", &provider.Quota{QuotaMaxBytes: 2000})
    86  		Expect(err).ToNot(HaveOccurred())
    87  		n, err = env.CreateTestFile("tobelockedid", "blob", spaceResID.OpaqueId, spaceResID.OpaqueId, 10)
    88  		Expect(err).ToNot(HaveOccurred())
    89  		n2, err = env.CreateTestFile("neverlockedlockedid", "blob", spaceResID.OpaqueId, spaceResID.OpaqueId, 10)
    90  		Expect(err).ToNot(HaveOccurred())
    91  	})
    92  
    93  	AfterEach(func() {
    94  		if env != nil {
    95  			env.Cleanup()
    96  		}
    97  	})
    98  
    99  	Describe("SetLock for a user", func() {
   100  		It("sets the lock", func() {
   101  			_, err := os.Stat(n.LockFilePath())
   102  			Expect(err).To(HaveOccurred())
   103  
   104  			err = n.SetLock(env.Ctx, lockByUser)
   105  			Expect(err).ToNot(HaveOccurred())
   106  
   107  			_, err = os.Stat(n.LockFilePath())
   108  			Expect(err).ToNot(HaveOccurred())
   109  		})
   110  
   111  		It("refuses to set a lock if already locked", func() {
   112  			err := n.SetLock(env.Ctx, lockByUser)
   113  			Expect(err).ToNot(HaveOccurred())
   114  
   115  			err = n.SetLock(env.Ctx, lockByUser)
   116  			Expect(err).To(HaveOccurred())
   117  			_, ok := err.(errtypes.PreconditionFailed)
   118  			Expect(ok).To(BeTrue())
   119  		})
   120  	})
   121  
   122  	Describe("SetLock for an app", func() {
   123  		It("sets the lock", func() {
   124  			_, err := os.Stat(n.LockFilePath())
   125  			Expect(err).To(HaveOccurred())
   126  
   127  			err = n.SetLock(env.Ctx, lockByApp)
   128  			Expect(err).ToNot(HaveOccurred())
   129  
   130  			_, err = os.Stat(n.LockFilePath())
   131  			Expect(err).ToNot(HaveOccurred())
   132  		})
   133  
   134  		It("refuses to set a lock if already locked", func() {
   135  			err := n.SetLock(env.Ctx, lockByApp)
   136  			Expect(err).ToNot(HaveOccurred())
   137  
   138  			err = n.SetLock(env.Ctx, lockByApp)
   139  			Expect(err).To(HaveOccurred())
   140  			_, ok := err.(errtypes.PreconditionFailed)
   141  			Expect(ok).To(BeTrue())
   142  		})
   143  
   144  	})
   145  
   146  	Context("with an existing lock for a user", func() {
   147  		BeforeEach(func() {
   148  			err := n.SetLock(env.Ctx, lockByUser)
   149  			Expect(err).ToNot(HaveOccurred())
   150  		})
   151  
   152  		Describe("ReadLock", func() {
   153  			It("returns the lock", func() {
   154  				l, err := n.ReadLock(env.Ctx, false)
   155  				Expect(err).ToNot(HaveOccurred())
   156  				Expect(l).To(BeComparableTo(lockByUser, protocmp.Transform()))
   157  			})
   158  
   159  			It("reports an error when the node wasn't locked", func() {
   160  				_, err := n2.ReadLock(env.Ctx, false)
   161  				Expect(err).To(HaveOccurred())
   162  				Expect(err.Error()).To(ContainSubstring("no lock found"))
   163  			})
   164  		})
   165  
   166  		Describe("RefreshLock", func() {
   167  			var (
   168  				newLock *provider.Lock
   169  			)
   170  
   171  			JustBeforeEach(func() {
   172  				newLock = &provider.Lock{
   173  					Type:   provider.LockType_LOCK_TYPE_EXCL,
   174  					User:   env.Owner.Id,
   175  					LockId: lockByUser.LockId,
   176  				}
   177  			})
   178  
   179  			It("fails when the node is unlocked", func() {
   180  				err := n2.RefreshLock(env.Ctx, lockByUser, "")
   181  				Expect(err).To(HaveOccurred())
   182  				Expect(err.Error()).To(ContainSubstring("precondition failed"))
   183  			})
   184  
   185  			It("refuses to refresh the lock without holding the lock", func() {
   186  				newLock.LockId = "somethingsomething"
   187  				err := n.RefreshLock(env.Ctx, newLock, "")
   188  				Expect(err).To(HaveOccurred())
   189  				Expect(err.Error()).To(ContainSubstring("mismatching"))
   190  			})
   191  
   192  			It("refuses to refresh the lock for other users than the lock holder", func() {
   193  				err := n.RefreshLock(otherCtx, newLock, "")
   194  				Expect(err).To(HaveOccurred())
   195  				Expect(err.Error()).To(ContainSubstring("permission denied"))
   196  			})
   197  
   198  			It("refuses to change the lock holder", func() {
   199  				newLock.User = otherUser.Id
   200  				err := n.RefreshLock(env.Ctx, newLock, "")
   201  				Expect(err).To(HaveOccurred())
   202  				Expect(err.Error()).To(ContainSubstring("permission denied"))
   203  			})
   204  
   205  			It("refuses to change the lock when we do not send the correct lock id for the old lock", func() {
   206  				newLock.LockId = "somethingsomething"
   207  				err := n.RefreshLock(env.Ctx, newLock, "somethingdifferent")
   208  				Expect(err).To(HaveOccurred())
   209  				Expect(err.Error()).To(ContainSubstring("mismatching"))
   210  			})
   211  
   212  			It("refreshes the lock when we send a new lock and the correct lock id for the old lock", func() {
   213  				newLock.LockId = "somethingsomething"
   214  				err := n.RefreshLock(env.Ctx, newLock, lockByUser.LockId)
   215  				Expect(err).ToNot(HaveOccurred())
   216  			})
   217  
   218  			It("refreshes the lock", func() {
   219  				err := n.RefreshLock(env.Ctx, newLock, "")
   220  				Expect(err).ToNot(HaveOccurred())
   221  			})
   222  
   223  			It("refreshes the lock and unlocks with the new lock", func() {
   224  				err := n.RefreshLock(env.Ctx, newLock, lockByUser.LockId)
   225  				Expect(err).ToNot(HaveOccurred())
   226  				err = n.Unlock(env.Ctx, newLock)
   227  				Expect(err).ToNot(HaveOccurred())
   228  			})
   229  		})
   230  
   231  		Describe("Unlock", func() {
   232  			It("refuses to unlock without having a lock", func() {
   233  				err := n.Unlock(env.Ctx, nil)
   234  				Expect(err.Error()).To(ContainSubstring(lockByUser.LockId))
   235  			})
   236  
   237  			It("refuses to unlock without having the proper lock", func() {
   238  				err := n.Unlock(env.Ctx, nil)
   239  				Expect(err.Error()).To(ContainSubstring(lockByUser.LockId))
   240  
   241  				err = n.Unlock(env.Ctx, wrongLockByUser)
   242  				Expect(err.Error()).To(ContainSubstring(lockByUser.LockId))
   243  			})
   244  
   245  			It("refuses to unlock for others even if they have the lock", func() {
   246  				err := n.Unlock(otherCtx, lockByUser)
   247  				Expect(err).To(HaveOccurred())
   248  				Expect(err.Error()).To(ContainSubstring("mismatching"))
   249  			})
   250  
   251  			It("unlocks when the owner uses the lock", func() {
   252  				err := n.Unlock(env.Ctx, lockByUser)
   253  				Expect(err).ToNot(HaveOccurred())
   254  
   255  				_, err = os.Stat(n.LockFilePath())
   256  				Expect(err).To(HaveOccurred())
   257  			})
   258  
   259  			It("fails to unlock an unlocked node", func() {
   260  				err := n.Unlock(env.Ctx, lockByUser)
   261  				Expect(err).ToNot(HaveOccurred())
   262  
   263  				err = n.Unlock(env.Ctx, lockByUser)
   264  				Expect(err).To(HaveOccurred())
   265  				Expect(err.Error()).To(ContainSubstring("lock does not exist"))
   266  			})
   267  		})
   268  	})
   269  
   270  	Context("with an existing lock for an app", func() {
   271  		BeforeEach(func() {
   272  			err := n.SetLock(env.Ctx, lockByApp)
   273  			Expect(err).ToNot(HaveOccurred())
   274  		})
   275  
   276  		Describe("ReadLock", func() {
   277  			It("returns the lock", func() {
   278  				l, err := n.ReadLock(env.Ctx, false)
   279  				Expect(err).ToNot(HaveOccurred())
   280  				Expect(l).To(Equal(lockByApp))
   281  			})
   282  
   283  			It("reports an error when the node wasn't locked", func() {
   284  				_, err := n2.ReadLock(env.Ctx, false)
   285  				Expect(err).To(HaveOccurred())
   286  				Expect(err.Error()).To(ContainSubstring("no lock found"))
   287  			})
   288  		})
   289  
   290  		Describe("RefreshLock", func() {
   291  			var (
   292  				newLock *provider.Lock
   293  			)
   294  
   295  			JustBeforeEach(func() {
   296  				newLock = &provider.Lock{
   297  					Type:    provider.LockType_LOCK_TYPE_EXCL,
   298  					AppName: lockByApp.AppName,
   299  					LockId:  lockByApp.LockId,
   300  				}
   301  			})
   302  
   303  			It("fails when the node is unlocked", func() {
   304  				err := n2.RefreshLock(env.Ctx, lockByApp, "")
   305  				Expect(err).To(HaveOccurred())
   306  				Expect(err.Error()).To(ContainSubstring("precondition failed"))
   307  			})
   308  
   309  			It("refuses to refresh the lock without holding the lock", func() {
   310  				newLock.LockId = "somethingsomething"
   311  				err := n.RefreshLock(env.Ctx, newLock, "")
   312  				Expect(err).To(HaveOccurred())
   313  				Expect(err.Error()).To(ContainSubstring("mismatching"))
   314  			})
   315  
   316  			It("refreshes the lock for other users", func() {
   317  				err := n.RefreshLock(otherCtx, lockByApp, "")
   318  				Expect(err).ToNot(HaveOccurred())
   319  			})
   320  
   321  			It("refuses to change the lock holder", func() {
   322  				newLock.AppName = wrongLockByApp.AppName
   323  				err := n.RefreshLock(env.Ctx, newLock, "")
   324  				Expect(err).To(HaveOccurred())
   325  				Expect(err.Error()).To(ContainSubstring("permission denied"))
   326  			})
   327  
   328  			It("refuses to change the lock when we do not send the correct lock id for the old lock", func() {
   329  				newLock.LockId = "somethingsomething"
   330  				err := n.RefreshLock(env.Ctx, newLock, "somethingdifferent")
   331  				Expect(err).To(HaveOccurred())
   332  				Expect(err.Error()).To(ContainSubstring("mismatching"))
   333  			})
   334  
   335  			It("refreshes the lock when we send a new lock and the correct lock id for the old lock", func() {
   336  				newLock.LockId = "somethingsomething"
   337  				err := n.RefreshLock(env.Ctx, newLock, lockByApp.LockId)
   338  				Expect(err).ToNot(HaveOccurred())
   339  			})
   340  
   341  			It("refreshes the lock", func() {
   342  				err := n.RefreshLock(env.Ctx, newLock, "")
   343  				Expect(err).ToNot(HaveOccurred())
   344  			})
   345  			It("refreshes the lock and unlocks it with the new lock", func() {
   346  				newLock.LockId = "somethingnew"
   347  				err := n.RefreshLock(env.Ctx, newLock, lockByApp.LockId)
   348  				Expect(err).ToNot(HaveOccurred())
   349  				err = n.Unlock(env.Ctx, newLock)
   350  				Expect(err).ToNot(HaveOccurred())
   351  			})
   352  		})
   353  
   354  		Describe("Unlock", func() {
   355  			It("refuses to unlock without having a lock", func() {
   356  				err := n.Unlock(env.Ctx, nil)
   357  				Expect(err.Error()).To(ContainSubstring(lockByApp.LockId))
   358  			})
   359  
   360  			It("refuses to unlock without having the proper lock", func() {
   361  				err := n.Unlock(env.Ctx, nil)
   362  				Expect(err.Error()).To(ContainSubstring(lockByApp.LockId))
   363  
   364  				err = n.Unlock(env.Ctx, wrongLockByUser)
   365  				Expect(err.Error()).To(ContainSubstring(lockByApp.LockId))
   366  			})
   367  
   368  			It("accepts to unlock for others if they have the lock", func() {
   369  				err := n.Unlock(otherCtx, lockByApp)
   370  				Expect(err).ToNot(HaveOccurred())
   371  			})
   372  
   373  			It("unlocks when the owner uses the lock", func() {
   374  				err := n.Unlock(env.Ctx, lockByApp)
   375  				Expect(err).ToNot(HaveOccurred())
   376  
   377  				_, err = os.Stat(n.LockFilePath())
   378  				Expect(err).To(HaveOccurred())
   379  			})
   380  
   381  			It("fails to unlock an unlocked node", func() {
   382  				err := n.Unlock(env.Ctx, lockByApp)
   383  				Expect(err).ToNot(HaveOccurred())
   384  
   385  				err = n.Unlock(env.Ctx, lockByApp)
   386  				Expect(err).To(HaveOccurred())
   387  				Expect(err.Error()).To(ContainSubstring("lock does not exist"))
   388  			})
   389  		})
   390  	})
   391  })