Skip to content
This repository has been archived by the owner on May 12, 2021. It is now read-only.

Commit

Permalink
virtcontainers: store: Implement the filesystem backend
Browse files Browse the repository at this point in the history
new() only creates the backend and initialized the first layout.

Signed-off-by: Samuel Ortiz <[email protected]>
  • Loading branch information
Samuel Ortiz committed Feb 6, 2019
1 parent d22cdf2 commit f2ab58d
Show file tree
Hide file tree
Showing 4 changed files with 295 additions and 72 deletions.
70 changes: 0 additions & 70 deletions virtcontainers/store/filesystem.go

This file was deleted.

227 changes: 227 additions & 0 deletions virtcontainers/store/filesystem_backend.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,227 @@
// Copyright (c) 2019 Intel Corporation
//
// SPDX-License-Identifier: Apache-2.0
//

package store

import (
"context"
"encoding/json"
"fmt"
"io/ioutil"
"os"
"path/filepath"

opentracing "github.com/opentracing/opentracing-go"
"github.com/sirupsen/logrus"
)

const (
// ConfigurationFile is the file name used for every JSON sandbox configuration.
ConfigurationFile string = "config.json"

// StateFile is the file name storing a sandbox state.
StateFile = "state.json"

// NetworkFile is the file name storing a sandbox network.
NetworkFile = "network.json"

// HypervisorFile is the file name storing a hypervisor's state.
HypervisorFile = "hypervisor.json"

// AgentFile is the file name storing an agent's state.
AgentFile = "agent.json"

// ProcessFile is the file name storing a container process.
ProcessFile = "process.json"

// LockFile is the file name locking the usage of a resource.
LockFile = "lock"

// MountsFile is the file name storing a container's mount points.
MountsFile = "mounts.json"

// DevicesFile is the file name storing a container's devices.
DevicesFile = "devices.json"
)

// DirMode is the permission bits used for creating a directory
const DirMode = os.FileMode(0750) | os.ModeDir

// StoragePathSuffix is the suffix used for all storage paths
//
// Note: this very brief path represents "virtcontainers". It is as
// terse as possible to minimise path length.
const StoragePathSuffix = "vc"

// SandboxPathSuffix is the suffix used for sandbox storage
const SandboxPathSuffix = "sbs"

// VMPathSuffix is the suffix used for guest VMs.
const VMPathSuffix = "vm"

// ConfigStoragePath is the sandbox configuration directory.
// It will contain one config.json file for each created sandbox.
var ConfigStoragePath = filepath.Join("/var/lib", StoragePathSuffix, SandboxPathSuffix)

// RunStoragePath is the sandbox runtime directory.
// It will contain one state.json and one lock file for each created sandbox.
var RunStoragePath = filepath.Join("/run", StoragePathSuffix, SandboxPathSuffix)

// RunVMStoragePath is the vm directory.
// It will contain all guest vm sockets and shared mountpoints.
var RunVMStoragePath = filepath.Join("/run", StoragePathSuffix, VMPathSuffix)

func itemToFile(item Item) (string, error) {
switch item {
case Configuration:
return ConfigurationFile, nil
case State:
return StateFile, nil
case Network:
return NetworkFile, nil
case Hypervisor:
return HypervisorFile, nil
case Agent:
return AgentFile, nil
case Process:
return ProcessFile, nil
case Lock:
return LockFile, nil
case Mounts:
return MountsFile, nil
case Devices, DeviceIDs:
return DevicesFile, nil
}

return "", fmt.Errorf("Unknown item %s", item)
}

type filesystem struct {
ctx context.Context

path string
}

// Logger returns a logrus logger appropriate for logging Store filesystem messages
func (f *filesystem) logger() *logrus.Entry {
return storeLog.WithFields(logrus.Fields{
"subsystem": "store",
"backend": "filesystem",
"path": f.path,
})
}

func (f *filesystem) trace(name string) (opentracing.Span, context.Context) {
if f.ctx == nil {
f.logger().WithField("type", "bug").Error("trace called before context set")
f.ctx = context.Background()
}

span, ctx := opentracing.StartSpanFromContext(f.ctx, name)

span.SetTag("subsystem", "store")
span.SetTag("type", "filesystem")
span.SetTag("path", f.path)

return span, ctx
}

func (f *filesystem) itemToPath(item Item) (string, error) {
fileName, err := itemToFile(item)
if err != nil {
return "", err
}

return filepath.Join(f.path, fileName), nil
}

func (f *filesystem) initialize() error {
_, err := os.Stat(f.path)
if err == nil {
return nil
}

// Our root path does not exist, we need to create the initial layout:
// The root directory and a lock file

// Root directory
if err := os.MkdirAll(f.path, DirMode); err != nil {
return err
}

// Lock
lockPath := filepath.Join(f.path, LockFile)

lockFile, err := os.Create(lockPath)
if err != nil {
f.delete()
return err
}
lockFile.Close()

return nil
}

func (f *filesystem) new(ctx context.Context, path string, host string) error {
f.ctx = ctx
f.path = path

f.logger().Debugf("New filesystem store backend for %s", path)

return f.initialize()
}

func (f *filesystem) delete() {
os.RemoveAll(f.path)
}

func (f *filesystem) load(item Item, data interface{}) error {
span, _ := f.trace("load")
defer span.Finish()

span.SetTag("item", item)

filePath, err := f.itemToPath(item)
if err != nil {
return err
}

fileData, err := ioutil.ReadFile(filePath)
if err != nil {
return err
}

if err := json.Unmarshal(fileData, data); err != nil {
return err
}

return nil
}

func (f *filesystem) store(item Item, data interface{}) error {
span, _ := f.trace("store")
defer span.Finish()

span.SetTag("item", item)

filePath, err := f.itemToPath(item)
if err != nil {
return err
}

file, err := os.Create(filePath)
if err != nil {
return err
}
defer file.Close()

jsonOut, err := json.Marshal(data)
if err != nil {
return fmt.Errorf("Could not marshall data: %s", err)
}
file.Write(jsonOut)

return nil
}
66 changes: 66 additions & 0 deletions virtcontainers/store/filesystem_backend_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
// Copyright (c) 2019 Intel Corporation
//
// SPDX-License-Identifier: Apache-2.0
//

package store

import (
"context"
"io/ioutil"
"path/filepath"
"testing"

"github.com/stretchr/testify/assert"
)

type TestNoopStructure struct {
Field1 string
Field2 string
}

var rootPath = "/tmp/root1/"
var expectedFilesystemData = "{\"Field1\":\"value1\",\"Field2\":\"value2\"}"

func TestStoreFilesystemStore(t *testing.T) {
f := filesystem{}

err := f.new(context.Background(), rootPath, "")
defer f.delete()
assert.Nil(t, err)

data := TestNoopStructure{
Field1: "value1",
Field2: "value2",
}

err = f.store(State, data)
assert.Nil(t, err)

filesystemData, err := ioutil.ReadFile(filepath.Join(rootPath, StateFile))
assert.Nil(t, err)
assert.Equal(t, string(filesystemData), expectedFilesystemData)
}

func TestStoreFilesystemLoad(t *testing.T) {
f := filesystem{}

err := f.new(context.Background(), rootPath, "")
defer f.delete()
assert.Nil(t, err)

data := TestNoopStructure{
Field1: "value1",
Field2: "value2",
}

// Store test data
err = f.store(State, data)
assert.Nil(t, err)

// Load and compare
newData := TestNoopStructure{}
err = f.load(State, &newData)
assert.Nil(t, err)
assert.Equal(t, newData, data)
}
4 changes: 2 additions & 2 deletions virtcontainers/store/manager_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,14 @@ import (
"github.com/stretchr/testify/assert"
)

var storeRoot = "file:///root1/"
var storeRoot = "file:///tmp/root1/"

func TestNewStore(t *testing.T) {
s, err := New(context.Background(), storeRoot)
assert.Nil(t, err)
assert.Equal(t, s.scheme, "file")
assert.Equal(t, s.host, "")
assert.Equal(t, s.path, "/root1/")
assert.Equal(t, s.path, "/tmp/root1/")
}

func TestManagerAddStore(t *testing.T) {
Expand Down

0 comments on commit f2ab58d

Please sign in to comment.