From 2ecffda17032ad2fe180fec091358a34aa025830 Mon Sep 17 00:00:00 2001 From: Samuel Ortiz Date: Wed, 9 Jan 2019 14:10:32 +0100 Subject: [PATCH] virtcontainers: store: Add a ItemLock API The ItemLock API allows for taking shared and exclusive locks on all items. For virtcontainers, this is specialized into taking locks on the Lock item, and will be used for sandbox locking. Signed-off-by: Samuel Ortiz --- virtcontainers/store/backend.go | 2 + virtcontainers/store/filesystem_backend.go | 50 +++++++++++++++++++ .../store/filesystem_backend_test.go | 48 ++++++++++++++++++ virtcontainers/store/manager.go | 10 ++++ virtcontainers/store/vc.go | 15 ++++++ 5 files changed, 125 insertions(+) diff --git a/virtcontainers/store/backend.go b/virtcontainers/store/backend.go index e183e96054..99a56317c1 100644 --- a/virtcontainers/store/backend.go +++ b/virtcontainers/store/backend.go @@ -53,4 +53,6 @@ type backend interface { // The caller gets an item URL back and handles it directly, // outside of the top level Store API. raw(id string) (string, error) + lock(item Item, exclusive bool) (string, error) + unlock(item Item, token string) error } diff --git a/virtcontainers/store/filesystem_backend.go b/virtcontainers/store/filesystem_backend.go index 5c6206d318..e57f8b7ed3 100644 --- a/virtcontainers/store/filesystem_backend.go +++ b/virtcontainers/store/filesystem_backend.go @@ -12,7 +12,9 @@ import ( "io/ioutil" "os" "path/filepath" + "syscall" + "github.com/kata-containers/runtime/virtcontainers/pkg/uuid" opentracing "github.com/opentracing/opentracing-go" "github.com/sirupsen/logrus" ) @@ -103,6 +105,8 @@ type filesystem struct { path string rawPath string + + lockTokens map[string]*os.File } // Logger returns a logrus logger appropriate for logging Store filesystem messages @@ -174,6 +178,7 @@ func (f *filesystem) new(ctx context.Context, path string, host string) error { f.ctx = ctx f.path = path f.rawPath = filepath.Join(f.path, "raw") + f.lockTokens = make(map[string]*os.File) f.logger().Debugf("New filesystem store backend for %s", path) @@ -259,3 +264,48 @@ func (f *filesystem) raw(id string) (string, error) { return filesystemScheme + "://" + file.Name(), nil } + +func (f *filesystem) lock(item Item, exclusive bool) (string, error) { + itemPath, err := f.itemToPath(item) + if err != nil { + return "", err + } + + itemFile, err := os.Open(itemPath) + if err != nil { + return "", err + } + + var lockType int + if exclusive { + lockType = syscall.LOCK_EX + } else { + lockType = syscall.LOCK_SH + } + + if err := syscall.Flock(int(itemFile.Fd()), lockType); err != nil { + itemFile.Close() + return "", err + } + + token := uuid.Generate().String() + f.lockTokens[token] = itemFile + + return token, nil +} + +func (f *filesystem) unlock(item Item, token string) error { + itemFile := f.lockTokens[token] + if itemFile == nil { + return fmt.Errorf("No lock for token %s", token) + } + + if err := syscall.Flock(int(itemFile.Fd()), syscall.LOCK_UN); err != nil { + return err + } + + itemFile.Close() + delete(f.lockTokens, token) + + return nil +} diff --git a/virtcontainers/store/filesystem_backend_test.go b/virtcontainers/store/filesystem_backend_test.go index 4d62a043c0..28589ae57f 100644 --- a/virtcontainers/store/filesystem_backend_test.go +++ b/virtcontainers/store/filesystem_backend_test.go @@ -99,3 +99,51 @@ func TestStoreFilesystemRaw(t *testing.T) { assert.Nil(t, err) assert.Equal(t, path, filesystemScheme+"://"+filepath.Join(rootPath, "raw", "roah")) } + +func TestStoreFilesystemLockShared(t *testing.T) { + f := filesystem{} + + err := f.new(context.Background(), rootPath, "") + defer f.delete() + assert.Nil(t, err) + + // Take 2 shared locks + token1, err := f.lock(Lock, false) + assert.Nil(t, err) + + token2, err := f.lock(Lock, false) + assert.Nil(t, err) + + err = f.unlock(Lock, token1) + assert.Nil(t, err) + + err = f.unlock(Lock, token2) + assert.Nil(t, err) + + err = f.unlock(Lock, token2) + assert.NotNil(t, err) +} + +func TestStoreFilesystemLockExclusive(t *testing.T) { + f := filesystem{} + + err := f.new(context.Background(), rootPath, "") + defer f.delete() + assert.Nil(t, err) + + // Take 1 exclusive lock + token, err := f.lock(Lock, true) + assert.Nil(t, err) + + err = f.unlock(Lock, token) + assert.Nil(t, err) + + token, err = f.lock(Lock, true) + assert.Nil(t, err) + + err = f.unlock(Lock, token) + assert.Nil(t, err) + + err = f.unlock(Lock, token) + assert.NotNil(t, err) +} diff --git a/virtcontainers/store/manager.go b/virtcontainers/store/manager.go index d5e96b2e48..fa18ab586d 100644 --- a/virtcontainers/store/manager.go +++ b/virtcontainers/store/manager.go @@ -261,3 +261,13 @@ func (s *Store) Raw(id string) (string, error) { return s.backend.raw(id) } + +// ItemLock takes a lock on an item. +func (s *Store) ItemLock(item Item, exclusive bool) (string, error) { + return s.backend.lock(item, exclusive) +} + +// ItemUnlock unlocks an item. +func (s *Store) ItemUnlock(item Item, token string) error { + return s.backend.unlock(item, token) +} diff --git a/virtcontainers/store/vc.go b/virtcontainers/store/vc.go index d3b0835624..b51cf725aa 100644 --- a/virtcontainers/store/vc.go +++ b/virtcontainers/store/vc.go @@ -202,6 +202,21 @@ func (s *VCStore) Raw(id string) (string, error) { return s.state.Raw(id) } +// Lock takes an exclusive lock on the virtcontainers state Lock item. +func (s *VCStore) Lock() (string, error) { + return s.state.ItemLock(Lock, true) +} + +// RLock takes a shared lock on the virtcontainers state Lock item. +func (s *VCStore) RLock() (string, error) { + return s.state.ItemLock(Lock, false) +} + +// Unlock unlocks the virtcontainers state Lock item. +func (s *VCStore) Unlock(token string) error { + return s.state.ItemUnlock(Lock, token) +} + // Utilities for virtcontainers // SandboxConfigurationRoot returns a virtcontainers sandbox configuration root URL.