diff --git a/virtcontainers/store/backend.go b/virtcontainers/store/backend.go index c5a6a7e39d..e183e96054 100644 --- a/virtcontainers/store/backend.go +++ b/virtcontainers/store/backend.go @@ -48,4 +48,9 @@ type backend interface { delete() error load(item Item, data interface{}) error store(item Item, data interface{}) error + // raw creates a raw Store item. A raw item is one that is + // not defined through the Item enum. + // The caller gets an item URL back and handles it directly, + // outside of the top level Store API. + raw(id string) (string, error) } diff --git a/virtcontainers/store/filesystem_backend.go b/virtcontainers/store/filesystem_backend.go index efe3f26f8c..5c6206d318 100644 --- a/virtcontainers/store/filesystem_backend.go +++ b/virtcontainers/store/filesystem_backend.go @@ -101,7 +101,8 @@ func itemToFile(item Item) (string, error) { type filesystem struct { ctx context.Context - path string + path string + rawPath string } // Logger returns a logrus logger appropriate for logging Store filesystem messages @@ -144,13 +145,18 @@ func (f *filesystem) initialize() error { } // Our root path does not exist, we need to create the initial layout: - // The root directory and a lock file + // The root directory, a lock file and a raw files directory. // Root directory if err := os.MkdirAll(f.path, DirMode); err != nil { return err } + // Raw directory + if err := os.MkdirAll(f.rawPath, DirMode); err != nil { + return err + } + // Lock lockPath := filepath.Join(f.path, LockFile) @@ -167,6 +173,7 @@ func (f *filesystem) initialize() error { 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.logger().Debugf("New filesystem store backend for %s", path) @@ -225,3 +232,30 @@ func (f *filesystem) store(item Item, data interface{}) error { return nil } + +func (f *filesystem) raw(id string) (string, error) { + span, _ := f.trace("raw") + defer span.Finish() + + span.SetTag("id", id) + + // We must use the item ID. + if id != "" { + filePath := filepath.Join(f.rawPath, id) + file, err := os.Create(filePath) + if err != nil { + return "", err + } + + return filesystemScheme + "://" + file.Name(), nil + } + + // Generate a random temporary file. + file, err := ioutil.TempFile(f.rawPath, "raw-") + if err != nil { + return "", err + } + defer file.Close() + + return filesystemScheme + "://" + file.Name(), nil +} diff --git a/virtcontainers/store/filesystem_backend_test.go b/virtcontainers/store/filesystem_backend_test.go index b8a3bbd92a..4d62a043c0 100644 --- a/virtcontainers/store/filesystem_backend_test.go +++ b/virtcontainers/store/filesystem_backend_test.go @@ -87,3 +87,15 @@ func TestStoreFilesystemDelete(t *testing.T) { _, err = os.Stat(f.path) assert.NotNil(t, err) } + +func TestStoreFilesystemRaw(t *testing.T) { + f := filesystem{} + + err := f.new(context.Background(), rootPath, "") + defer f.delete() + assert.Nil(t, err) + + path, err := f.raw("roah") + assert.Nil(t, err) + assert.Equal(t, path, filesystemScheme+"://"+filepath.Join(rootPath, "raw", "roah")) +} diff --git a/virtcontainers/store/manager.go b/virtcontainers/store/manager.go index ef4a09f78b..d5e96b2e48 100644 --- a/virtcontainers/store/manager.go +++ b/virtcontainers/store/manager.go @@ -248,3 +248,16 @@ func (s *Store) Delete() error { return nil } + +// Raw creates a raw item to be handled directly by the API caller. +// It returns a full URL to the item and the caller is responsible +// for handling the item through this URL. +func (s *Store) Raw(id string) (string, error) { + span, _ := s.trace("Raw") + defer span.Finish() + + s.Lock() + defer s.Unlock() + + return s.backend.raw(id) +} diff --git a/virtcontainers/store/vc.go b/virtcontainers/store/vc.go index fb43e6fd23..d3b0835624 100644 --- a/virtcontainers/store/vc.go +++ b/virtcontainers/store/vc.go @@ -191,6 +191,17 @@ func (s *VCStore) LoadDevices() ([]api.Device, error) { return devices, nil } +// Raw creates a raw item in the virtcontainer state Store. A raw +// item is a custom one, not defined through the Item enum, and that +// the caller needs to handle directly. +// Typically this is used to create a custom virtcontainers file. +// For example the Firecracker code uses this API to create temp +// files under the sandbox state root path, and uses them as block +// driver backend placeholder. +func (s *VCStore) Raw(id string) (string, error) { + return s.state.Raw(id) +} + // Utilities for virtcontainers // SandboxConfigurationRoot returns a virtcontainers sandbox configuration root URL.