runtime: remove old store

Remove old store for Kata 2.0.

Fixes: #368

Signed-off-by: bin liu <bin@hyper.sh>
This commit is contained in:
bin liu 2020-07-01 14:32:43 +08:00
parent 4b01ec1f3c
commit 5200ac0678
13 changed files with 29 additions and 1655 deletions

View File

@ -17,7 +17,6 @@ import (
"github.com/kata-containers/kata-containers/src/runtime/virtcontainers/pkg/cgroups" "github.com/kata-containers/kata-containers/src/runtime/virtcontainers/pkg/cgroups"
"github.com/kata-containers/kata-containers/src/runtime/virtcontainers/pkg/compatoci" "github.com/kata-containers/kata-containers/src/runtime/virtcontainers/pkg/compatoci"
vcTypes "github.com/kata-containers/kata-containers/src/runtime/virtcontainers/pkg/types" vcTypes "github.com/kata-containers/kata-containers/src/runtime/virtcontainers/pkg/types"
"github.com/kata-containers/kata-containers/src/runtime/virtcontainers/store"
"github.com/kata-containers/kata-containers/src/runtime/virtcontainers/types" "github.com/kata-containers/kata-containers/src/runtime/virtcontainers/types"
specs "github.com/opencontainers/runtime-spec/specs-go" specs "github.com/opencontainers/runtime-spec/specs-go"
opentracing "github.com/opentracing/opentracing-go" opentracing "github.com/opentracing/opentracing-go"
@ -52,7 +51,6 @@ func SetLogger(ctx context.Context, logger *logrus.Entry) {
deviceApi.SetLogger(virtLog) deviceApi.SetLogger(virtLog)
compatoci.SetLogger(virtLog) compatoci.SetLogger(virtLog)
store.SetLogger(virtLog)
deviceConfig.SetLogger(virtLog) deviceConfig.SetLogger(virtLog)
cgroups.SetLogger(virtLog) cgroups.SetLogger(virtLog)
} }

View File

@ -30,7 +30,6 @@ import (
"github.com/kata-containers/kata-containers/src/runtime/virtcontainers/device/config" "github.com/kata-containers/kata-containers/src/runtime/virtcontainers/device/config"
"github.com/kata-containers/kata-containers/src/runtime/virtcontainers/device/manager" "github.com/kata-containers/kata-containers/src/runtime/virtcontainers/device/manager"
"github.com/kata-containers/kata-containers/src/runtime/virtcontainers/pkg/rootless" "github.com/kata-containers/kata-containers/src/runtime/virtcontainers/pkg/rootless"
"github.com/kata-containers/kata-containers/src/runtime/virtcontainers/store"
) )
// https://github.com/torvalds/linux/blob/master/include/uapi/linux/major.h // https://github.com/torvalds/linux/blob/master/include/uapi/linux/major.h
@ -337,8 +336,6 @@ type Container struct {
systemMountsInfo SystemMountsInfo systemMountsInfo SystemMountsInfo
ctx context.Context ctx context.Context
store *store.VCStore
} }
// ID returns the container identifier string. // ID returns the container identifier string.
@ -430,17 +427,9 @@ func (c *Container) setContainerState(state types.StateString) error {
// update in-memory state // update in-memory state
c.state.State = state c.state.State = state
if useOldStore(c.sandbox.ctx) { // flush data to storage
// experimental runtime use "persist.json" which doesn't need "state.json" anymore if err := c.sandbox.Save(); err != nil {
// update on-disk state return err
if err := c.store.Store(store.State, c.state); err != nil {
return err
}
} else {
// flush data to storage
if err := c.sandbox.Save(); err != nil {
return err
}
} }
return nil return nil
@ -724,33 +713,16 @@ func newContainer(sandbox *Sandbox, contConfig *ContainerConfig) (*Container, er
ctx: sandbox.ctx, ctx: sandbox.ctx,
} }
if useOldStore(sandbox.ctx) { // experimental runtime use "persist.json" instead of legacy "state.json" as storage
ctrStore, err := store.NewVCContainerStore(sandbox.ctx, c.sandboxID, c.id) err := c.Restore()
if err != nil { if err == nil {
return nil, err //container restored
} return c, nil
c.store = ctrStore }
state, err := c.store.LoadContainerState()
if err == nil {
c.state = state
}
var process Process // Unexpected error
if err := c.store.Load(store.Process, &process); err == nil { if !os.IsNotExist(err) && err != errContainerPersistNotExist {
c.process = process return nil, err
}
} else {
// experimental runtime use "persist.json" instead of legacy "state.json" as storage
err := c.Restore()
if err == nil {
//container restored
return c, nil
}
// Unexpected error
if !os.IsNotExist(err) && err != errContainerPersistNotExist {
return nil, err
}
} }
// Go to next step for first created container // Go to next step for first created container
@ -765,35 +737,7 @@ func newContainer(sandbox *Sandbox, contConfig *ContainerConfig) (*Container, er
return c, nil return c, nil
} }
func (c *Container) loadMounts() ([]Mount, error) {
var mounts []Mount
if err := c.store.Load(store.Mounts, &mounts); err != nil {
return []Mount{}, err
}
return mounts, nil
}
func (c *Container) loadDevices() ([]ContainerDevice, error) {
var devices []ContainerDevice
if err := c.store.Load(store.DeviceIDs, &devices); err != nil {
return []ContainerDevice{}, err
}
return devices, nil
}
func (c *Container) createMounts() error { func (c *Container) createMounts() error {
if useOldStore(c.sandbox.ctx) {
mounts, err := c.loadMounts()
if err == nil {
// restore mounts from disk
c.mounts = mounts
return nil
}
}
// Create block devices for newly created container // Create block devices for newly created container
if err := c.createBlockDevices(); err != nil { if err := c.createBlockDevices(); err != nil {
return err return err
@ -803,18 +747,6 @@ func (c *Container) createMounts() error {
} }
func (c *Container) createDevices(contConfig *ContainerConfig) error { func (c *Container) createDevices(contConfig *ContainerConfig) error {
// If sandbox supports "newstore", only newly created container can reach this function,
// so we don't call restore when `supportNewStore` is true
if useOldStore(c.sandbox.ctx) {
// Devices will be found in storage after create stage has completed.
// We load devices from storage at all other stages.
storedDevices, err := c.loadDevices()
if err == nil {
c.devices = storedDevices
return nil
}
}
// If devices were not found in storage, create Device implementations // If devices were not found in storage, create Device implementations
// from the configuration. This should happen at create. // from the configuration. This should happen at create.
var storedDevices []ContainerDevice var storedDevices []ContainerDevice

View File

@ -30,7 +30,6 @@ import (
"github.com/kata-containers/kata-containers/src/runtime/virtcontainers/pkg/rootless" "github.com/kata-containers/kata-containers/src/runtime/virtcontainers/pkg/rootless"
vcTypes "github.com/kata-containers/kata-containers/src/runtime/virtcontainers/pkg/types" vcTypes "github.com/kata-containers/kata-containers/src/runtime/virtcontainers/pkg/types"
"github.com/kata-containers/kata-containers/src/runtime/virtcontainers/pkg/uuid" "github.com/kata-containers/kata-containers/src/runtime/virtcontainers/pkg/uuid"
"github.com/kata-containers/kata-containers/src/runtime/virtcontainers/store"
"github.com/kata-containers/kata-containers/src/runtime/virtcontainers/types" "github.com/kata-containers/kata-containers/src/runtime/virtcontainers/types"
"github.com/gogo/protobuf/proto" "github.com/gogo/protobuf/proto"
@ -345,12 +344,6 @@ func (k *kataAgent) init(ctx context.Context, sandbox *Sandbox, config interface
k.proxyBuiltIn = isProxyBuiltIn(sandbox.config.ProxyType) k.proxyBuiltIn = isProxyBuiltIn(sandbox.config.ProxyType)
// Fetch agent runtime info.
if useOldStore(sandbox.ctx) {
if err := sandbox.store.Load(store.Agent, &k.state); err != nil {
k.Logger().Debug("Could not retrieve anything from storage")
}
}
return disableVMShutdown, nil return disableVMShutdown, nil
} }

View File

@ -6,14 +6,12 @@
package virtcontainers package virtcontainers
import ( import (
"context"
"errors" "errors"
"github.com/kata-containers/kata-containers/src/runtime/virtcontainers/device/api" "github.com/kata-containers/kata-containers/src/runtime/virtcontainers/device/api"
exp "github.com/kata-containers/kata-containers/src/runtime/virtcontainers/experimental" exp "github.com/kata-containers/kata-containers/src/runtime/virtcontainers/experimental"
"github.com/kata-containers/kata-containers/src/runtime/virtcontainers/persist" "github.com/kata-containers/kata-containers/src/runtime/virtcontainers/persist"
persistapi "github.com/kata-containers/kata-containers/src/runtime/virtcontainers/persist/api" persistapi "github.com/kata-containers/kata-containers/src/runtime/virtcontainers/persist/api"
"github.com/kata-containers/kata-containers/src/runtime/virtcontainers/store"
"github.com/kata-containers/kata-containers/src/runtime/virtcontainers/types" "github.com/kata-containers/kata-containers/src/runtime/virtcontainers/types"
"github.com/mitchellh/mapstructure" "github.com/mitchellh/mapstructure"
) )
@ -579,25 +577,3 @@ func loadSandboxConfig(id string) (*SandboxConfig, error) {
} }
return sconfig, nil return sconfig, nil
} }
var oldstoreKey = struct{}{}
func loadSandboxConfigFromOldStore(ctx context.Context, sid string) (*SandboxConfig, context.Context, error) {
var config SandboxConfig
// We're bootstrapping
vcStore, err := store.NewVCSandboxStore(ctx, sid)
if err != nil {
return nil, ctx, err
}
if err := vcStore.Load(store.Configuration, &config); err != nil {
return nil, ctx, err
}
return &config, context.WithValue(ctx, oldstoreKey, true), nil
}
func useOldStore(ctx context.Context) bool {
v := ctx.Value(oldstoreKey)
return v != nil
}

View File

@ -25,7 +25,6 @@ import (
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
"github.com/vishvananda/netlink" "github.com/vishvananda/netlink"
"github.com/kata-containers/kata-containers/src/runtime/virtcontainers/pkg/agent/protocols/grpc"
"github.com/kata-containers/kata-containers/src/runtime/virtcontainers/device/api" "github.com/kata-containers/kata-containers/src/runtime/virtcontainers/device/api"
"github.com/kata-containers/kata-containers/src/runtime/virtcontainers/device/config" "github.com/kata-containers/kata-containers/src/runtime/virtcontainers/device/config"
"github.com/kata-containers/kata-containers/src/runtime/virtcontainers/device/drivers" "github.com/kata-containers/kata-containers/src/runtime/virtcontainers/device/drivers"
@ -33,12 +32,12 @@ import (
exp "github.com/kata-containers/kata-containers/src/runtime/virtcontainers/experimental" exp "github.com/kata-containers/kata-containers/src/runtime/virtcontainers/experimental"
"github.com/kata-containers/kata-containers/src/runtime/virtcontainers/persist" "github.com/kata-containers/kata-containers/src/runtime/virtcontainers/persist"
persistapi "github.com/kata-containers/kata-containers/src/runtime/virtcontainers/persist/api" persistapi "github.com/kata-containers/kata-containers/src/runtime/virtcontainers/persist/api"
"github.com/kata-containers/kata-containers/src/runtime/virtcontainers/pkg/agent/protocols/grpc"
"github.com/kata-containers/kata-containers/src/runtime/virtcontainers/pkg/annotations" "github.com/kata-containers/kata-containers/src/runtime/virtcontainers/pkg/annotations"
vccgroups "github.com/kata-containers/kata-containers/src/runtime/virtcontainers/pkg/cgroups" vccgroups "github.com/kata-containers/kata-containers/src/runtime/virtcontainers/pkg/cgroups"
"github.com/kata-containers/kata-containers/src/runtime/virtcontainers/pkg/compatoci" "github.com/kata-containers/kata-containers/src/runtime/virtcontainers/pkg/compatoci"
"github.com/kata-containers/kata-containers/src/runtime/virtcontainers/pkg/rootless" "github.com/kata-containers/kata-containers/src/runtime/virtcontainers/pkg/rootless"
vcTypes "github.com/kata-containers/kata-containers/src/runtime/virtcontainers/pkg/types" vcTypes "github.com/kata-containers/kata-containers/src/runtime/virtcontainers/pkg/types"
"github.com/kata-containers/kata-containers/src/runtime/virtcontainers/store"
"github.com/kata-containers/kata-containers/src/runtime/virtcontainers/types" "github.com/kata-containers/kata-containers/src/runtime/virtcontainers/types"
"github.com/kata-containers/kata-containers/src/runtime/virtcontainers/utils" "github.com/kata-containers/kata-containers/src/runtime/virtcontainers/utils"
) )
@ -183,9 +182,7 @@ type Sandbox struct {
factory Factory factory Factory
hypervisor hypervisor hypervisor hypervisor
agent agent agent agent
store *store.VCStore newStore persistapi.PersistDriver
// store is used to replace VCStore step by step
newStore persistapi.PersistDriver
network Network network Network
monitor *monitor monitor *monitor
@ -559,51 +556,18 @@ func newSandbox(ctx context.Context, sandboxConfig SandboxConfig, factory Factor
sandboxConfig.HypervisorConfig.SELinuxProcessLabel = spec.Process.SelinuxLabel sandboxConfig.HypervisorConfig.SELinuxProcessLabel = spec.Process.SelinuxLabel
} }
if useOldStore(ctx) { s.devManager = deviceManager.NewDeviceManager(sandboxConfig.HypervisorConfig.BlockDeviceDriver,
vcStore, err := store.NewVCSandboxStore(ctx, s.id) sandboxConfig.HypervisorConfig.EnableVhostUserStore,
if err != nil { sandboxConfig.HypervisorConfig.VhostUserStorePath, nil)
return nil, err
}
s.store = vcStore // Ignore the error. Restore can fail for a new sandbox
if err := s.Restore(); err != nil {
s.Logger().WithError(err).Debug("restore sandbox failed")
}
// Fetch sandbox network to be able to access it from the sandbox structure. // new store doesn't require hypervisor to be stored immediately
var networkNS NetworkNamespace if err = s.hypervisor.createSandbox(ctx, s.id, s.networkNS, &sandboxConfig.HypervisorConfig, s.stateful); err != nil {
if err = s.store.Load(store.Network, &networkNS); err == nil { return nil, err
s.networkNS = networkNS
}
devices, err := s.store.LoadDevices()
if err != nil {
s.Logger().WithError(err).WithField("sandboxid", s.id).Warning("load sandbox devices failed")
}
s.devManager = deviceManager.NewDeviceManager(sandboxConfig.HypervisorConfig.BlockDeviceDriver,
sandboxConfig.HypervisorConfig.EnableVhostUserStore,
sandboxConfig.HypervisorConfig.VhostUserStorePath, devices)
// Load sandbox state. The hypervisor.createSandbox call, may need to access statei.
state, err := s.store.LoadState()
if err == nil {
s.state = state
}
if err = s.hypervisor.createSandbox(ctx, s.id, s.networkNS, &sandboxConfig.HypervisorConfig, s.stateful); err != nil {
return nil, err
}
} else {
s.devManager = deviceManager.NewDeviceManager(sandboxConfig.HypervisorConfig.BlockDeviceDriver,
sandboxConfig.HypervisorConfig.EnableVhostUserStore,
sandboxConfig.HypervisorConfig.VhostUserStorePath, nil)
// Ignore the error. Restore can fail for a new sandbox
if err := s.Restore(); err != nil {
s.Logger().WithError(err).Debug("restore sandbox failed")
}
// new store doesn't require hypervisor to be stored immediately
if err = s.hypervisor.createSandbox(ctx, s.id, s.networkNS, &sandboxConfig.HypervisorConfig, s.stateful); err != nil {
return nil, err
}
} }
if err := s.createCgroupManager(); err != nil { if err := s.createCgroupManager(); err != nil {
@ -717,22 +681,15 @@ func fetchSandbox(ctx context.Context, sandboxID string) (sandbox *Sandbox, err
var config SandboxConfig var config SandboxConfig
// Try to load sandbox config from old store at first. // load sandbox config fromld store.
c, ctx, err := loadSandboxConfigFromOldStore(ctx, sandboxID) c, err := loadSandboxConfig(sandboxID)
if err != nil { if err != nil {
virtLog.Warningf("failed to get sandbox config from old store: %v", err) virtLog.Warningf("failed to get sandbox config from new store: %v", err)
// If we failed to load sandbox config from old store, try again with new store. return nil, err
c, err = loadSandboxConfig(sandboxID)
if err != nil {
virtLog.Warningf("failed to get sandbox config from new store: %v", err)
return nil, err
}
} }
config = *c config = *c
if useOldStore(ctx) {
virtLog.Infof("Warning: old store has been deprecated.")
}
// fetchSandbox is not suppose to create new sandbox VM. // fetchSandbox is not suppose to create new sandbox VM.
sandbox, err = createSandbox(ctx, config, nil) sandbox, err = createSandbox(ctx, config, nil)
if err != nil { if err != nil {
@ -1594,9 +1551,6 @@ func (s *Sandbox) setSandboxState(state types.StateString) error {
// update in-memory state // update in-memory state
s.state.State = state s.state.State = state
if useOldStore(s.ctx) {
return s.store.Store(store.State, s.state)
}
return nil return nil
} }

View File

@ -1,58 +0,0 @@
// Copyright (c) 2019 Intel Corporation
//
// SPDX-License-Identifier: Apache-2.0
//
package store
import (
"context"
"fmt"
)
type backendType string
const (
filesystemBackend backendType = "filesystem"
)
const (
filesystemScheme string = "file"
)
func schemeToBackendType(scheme string) (backendType, error) {
switch scheme {
case filesystemScheme:
return filesystemBackend, nil
}
return "", fmt.Errorf("Unsupported scheme %s", scheme)
}
func newBackend(scheme string) (backend, error) {
t, err := schemeToBackendType(scheme)
if err != nil {
return nil, err
}
switch t {
case filesystemBackend:
return &filesystem{}, nil
}
return nil, fmt.Errorf("Unsupported scheme %s", scheme)
}
type backend interface {
new(ctx context.Context, path string, host string) error
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)
lock(item Item, exclusive bool) (string, error)
unlock(item Item, token string) error
}

View File

@ -1,355 +0,0 @@
// Copyright (c) 2019 Intel Corporation
//
// SPDX-License-Identifier: Apache-2.0
//
package store
import (
"context"
"encoding/json"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"syscall"
"github.com/kata-containers/kata-containers/src/runtime/virtcontainers/pkg/rootless"
"github.com/kata-containers/kata-containers/src/runtime/virtcontainers/pkg/uuid"
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"
// uuidFile is the file name storing a guest VM uuid state.
uuidFile = "uuid.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"
// UUIDPathSuffix is the suffix used for uuid storage
const UUIDPathSuffix = "uuid"
// ConfigStoragePath is the sandbox configuration directory.
// It will contain one config.json file for each created sandbox.
// The function is declared this way for mocking in unit tests
var ConfigStoragePath = func() string {
path := filepath.Join("/var/lib", StoragePathSuffix, SandboxPathSuffix)
if rootless.IsRootless() {
return filepath.Join(rootless.GetRootlessDir(), path)
}
return path
}
// RunStoragePath is the sandbox runtime directory.
// It will contain one state.json and one lock file for each created sandbox.
// The function is declared this way for mocking in unit tests
var RunStoragePath = func() string {
path := filepath.Join("/run", StoragePathSuffix, SandboxPathSuffix)
if rootless.IsRootless() {
return filepath.Join(rootless.GetRootlessDir(), path)
}
return path
}
// RunVMStoragePath is the vm directory.
// It will contain all guest vm sockets and shared mountpoints.
// The function is declared this way for mocking in unit tests
var RunVMStoragePath = func() string {
path := filepath.Join("/run", StoragePathSuffix, VMPathSuffix)
if rootless.IsRootless() {
return filepath.Join(rootless.GetRootlessDir(), path)
}
return path
}
// VMUUIDStoragePath is the uuid directory.
// It will contain all uuid info used by guest vm.
var VMUUIDStoragePath = func() string {
path := filepath.Join("/var/lib", StoragePathSuffix, UUIDPathSuffix)
if rootless.IsRootless() {
return filepath.Join(rootless.GetRootlessDir(), path)
}
return path
}
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
case UUID:
return uuidFile, nil
}
return "", fmt.Errorf("Unknown item %s", item)
}
type filesystem struct {
ctx context.Context
path string
rawPath string
lockTokens map[string]*os.File
}
// 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, a lock file and a raw files directory.
// Root directory
f.logger().WithField("path", f.path).Debugf("Creating root directory")
if err := os.MkdirAll(f.path, DirMode); err != nil {
return err
}
// Raw directory
f.logger().WithField("path", f.rawPath).Debugf("Creating raw directory")
if err := os.MkdirAll(f.rawPath, 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.rawPath = filepath.Join(f.path, "raw")
f.lockTokens = make(map[string]*os.File)
f.logger().Debugf("New filesystem store backend for %s", path)
return f.initialize()
}
func (f *filesystem) delete() error {
f.logger().WithField("path", f.path).Debugf("Deleting files")
return 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
}
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
}
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
}

View File

@ -1,153 +0,0 @@
// Copyright (c) 2019 Intel Corporation
//
// SPDX-License-Identifier: Apache-2.0
//
package store
import (
"context"
"io/ioutil"
"os"
"path/filepath"
"testing"
"github.com/stretchr/testify/assert"
)
type TestNoopStructure struct {
Field1 string
Field2 string
}
var rootPath = func() string {
dir, _ := ioutil.TempDir("", "")
return dir
}()
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)
}
func TestStoreFilesystemDelete(t *testing.T) {
f := filesystem{}
err := f.new(context.Background(), rootPath, "")
assert.Nil(t, err)
data := TestNoopStructure{
Field1: "value1",
Field2: "value2",
}
// Store test data
err = f.store(State, data)
assert.Nil(t, err)
err = f.delete()
assert.Nil(t, err)
_, 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"))
}
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)
}

View File

@ -1,281 +0,0 @@
// Copyright (c) 2019 Intel Corporation
//
// SPDX-License-Identifier: Apache-2.0
//
package store
import (
"context"
"fmt"
"net/url"
"sync"
opentracing "github.com/opentracing/opentracing-go"
"github.com/sirupsen/logrus"
)
// Item represents a virtcontainers items that will be managed through the store.
type Item uint8
const (
// Configuration represents a configuration item to be stored
Configuration Item = iota
// State represents a state item to be stored.
State
// Network represents a networking item to be stored.
Network
// Hypervisor represents an hypervisor item to be stored.
Hypervisor
// Agent represents a agent item to be stored.
Agent
// Process represents a container process item to be stored.
Process
// Lock represents a lock item to be stored.
Lock
// Mounts represents a set of mounts related item to be stored.
Mounts
// Devices represents a set of devices related item to be stored.
Devices
// DeviceIDs represents a set of reference IDs item to be stored.
DeviceIDs
// UUID represents a set of uuids item to be stored.
UUID
)
func (i Item) String() string {
switch i {
case Configuration:
return "Configuration"
case State:
return "State"
case Network:
return "Network"
case Hypervisor:
return "Hypervisor"
case Agent:
return "Agent"
case Process:
return "Process"
case Lock:
return "Lock"
case Mounts:
return "Mounts"
case Devices:
return "Devices"
case DeviceIDs:
return "Device IDs"
}
return ""
}
// Store is an opaque structure representing a virtcontainers Store.
type Store struct {
sync.RWMutex
ctx context.Context
url string
scheme string
path string
host string
backend backend
}
type manager struct {
sync.RWMutex
stores map[string]*Store
}
var stores = &manager{stores: make(map[string]*Store)}
func (m *manager) addStore(s *Store) (rs *Store, err error) {
if s == nil {
return nil, fmt.Errorf("Store can not be nil")
}
if s.url == "" {
return nil, fmt.Errorf("Store URL can not be nil")
}
m.Lock()
defer m.Unlock()
if m.stores[s.url] == nil {
m.stores[s.url] = s
}
return m.stores[s.url], nil
}
func (m *manager) removeStore(url string) {
m.Lock()
defer m.Unlock()
delete(m.stores, url)
}
func (m *manager) findStore(url string) *Store {
m.RLock()
defer m.RUnlock()
return m.stores[url]
}
// New will return a new virtcontainers Store.
// If there is already a Store for the URL, we will re-use it.
// Otherwise a new Store is created.
func New(ctx context.Context, storeURL string) (*Store, error) {
// Do we already have such store?
if s := stores.findStore(storeURL); s != nil {
return s, nil
}
u, err := url.Parse(storeURL)
if err != nil {
return nil, err
}
s := &Store{
ctx: ctx,
url: storeURL,
scheme: u.Scheme,
path: u.Path,
host: u.Host,
}
backend, err := newBackend(s.scheme)
if err != nil {
return nil, err
}
s.backend = backend
// Create new backend
if err = s.backend.new(ctx, s.path, s.host); err != nil {
return nil, err
}
if s, err = stores.addStore(s); err != nil {
return nil, err
}
return s, nil
}
// DeleteAll deletes all Stores from the manager.
func DeleteAll() {
for _, s := range stores.stores {
s.Delete()
}
}
var storeLog = logrus.WithField("source", "virtcontainers/store")
// SetLogger sets the custom logger to be used by this package. If not called,
// the package will create its own logger.
func SetLogger(logger *logrus.Entry) {
fields := storeLog.Data
storeLog = logger.WithFields(fields)
}
// Logger returns a logrus logger appropriate for logging Store messages
func (s *Store) Logger() *logrus.Entry {
return storeLog.WithFields(logrus.Fields{
"subsystem": "store",
"path": s.path,
})
}
func (s *Store) trace(name string) (opentracing.Span, context.Context) {
if s.ctx == nil {
s.Logger().WithField("type", "bug").Error("trace called before context set")
s.ctx = context.Background()
}
span, ctx := opentracing.StartSpanFromContext(s.ctx, name)
span.SetTag("subsystem", "store")
span.SetTag("path", s.path)
return span, ctx
}
// Load loads a virtcontainers item from a Store.
func (s *Store) Load(item Item, data interface{}) error {
span, _ := s.trace("Load")
defer span.Finish()
span.SetTag("item", item)
s.RLock()
defer s.RUnlock()
return s.backend.load(item, data)
}
// Store stores a virtcontainers item into a Store.
func (s *Store) Store(item Item, data interface{}) error {
span, _ := s.trace("Store")
defer span.Finish()
span.SetTag("item", item)
s.Lock()
defer s.Unlock()
return s.backend.store(item, data)
}
// Delete deletes all artifacts created by a Store.
// The Store is also removed from the manager.
func (s *Store) Delete() error {
span, _ := s.trace("Store")
defer span.Finish()
s.Lock()
defer s.Unlock()
if err := s.backend.delete(); err != nil {
return err
}
stores.removeStore(s.url)
s.url = ""
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)
}
// 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)
}

View File

@ -1,149 +0,0 @@
// Copyright (c) 2019 Intel Corporation
//
// SPDX-License-Identifier: Apache-2.0
//
package store
import (
"context"
"io/ioutil"
"os"
"path/filepath"
"testing"
"github.com/stretchr/testify/assert"
)
const testSandboxID = "7f49d00d-1995-4156-8c79-5f5ab24ce138"
var sandboxDirConfig = ""
var sandboxFileConfig = ""
var sandboxDirState = ""
var sandboxDirLock = ""
var sandboxFileState = ""
var sandboxFileLock = ""
var storeRoot, storeRootDir = func() (string, string) {
dir, _ := ioutil.TempDir("", "")
return "file://" + dir, dir
}()
var testDir = ""
var ConfigStoragePathSaved = func() string { return "" }
var RunStoragePathSaved = func() string { return "" }
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, storeRootDir)
}
func TestDeleteStore(t *testing.T) {
s, err := New(context.Background(), storeRoot)
assert.Nil(t, err)
err = s.Delete()
assert.Nil(t, err)
// We should no longer find storeRoot
newStore := stores.findStore(storeRoot)
assert.Nil(t, newStore, "findStore should not have found %s", storeRoot)
}
func TestManagerAddStore(t *testing.T) {
s, err := New(context.Background(), storeRoot)
assert.Nil(t, err)
defer stores.removeStore(storeRoot)
// Positive find
newStore := stores.findStore(storeRoot)
assert.NotNil(t, newStore, "findStore failed")
// Duplicate, should fail
ns, err := stores.addStore(s)
assert.Nil(t, err, "addStore should not failed")
assert.Equal(t, s, ns)
// Try with an empty URL
sEmpty, err := New(context.Background(), storeRoot)
assert.Nil(t, err)
sEmpty.url = ""
_, err = stores.addStore(sEmpty)
assert.NotNil(t, err, "addStore should have failed on an empty store URL")
}
func TestManagerRemoveStore(t *testing.T) {
_, err := New(context.Background(), storeRoot)
assert.Nil(t, err)
// Positive find
newStore := stores.findStore(storeRoot)
assert.NotNil(t, newStore, "findStore failed")
// Negative removal
stores.removeStore(storeRoot + "foobar")
// We should still find storeRoot
newStore = stores.findStore(storeRoot)
assert.NotNil(t, newStore, "findStore failed")
// Positive removal
stores.removeStore(storeRoot)
// We should no longer find storeRoot
newStore = stores.findStore(storeRoot)
assert.Nil(t, newStore, "findStore should not have found %s", storeRoot)
}
func TestManagerFindStore(t *testing.T) {
_, err := New(context.Background(), storeRoot)
assert.Nil(t, err)
defer stores.removeStore(storeRoot)
// Positive find
newStore := stores.findStore(storeRoot)
assert.NotNil(t, newStore, "findStore failed")
// Negative find
newStore = stores.findStore(storeRoot + "foobar")
assert.Nil(t, newStore, "findStore should not have found a new store")
}
// TestMain is the common main function used by ALL the test functions
// for the store.
func TestMain(m *testing.M) {
setup()
rt := m.Run()
shutdown()
os.Exit(rt)
}
func shutdown() {
os.RemoveAll(testDir)
ConfigStoragePath = ConfigStoragePathSaved
RunStoragePath = RunStoragePathSaved
}
func setup() {
var err error
testDir, err = ioutil.TempDir("", "store-tmp-")
if err != nil {
panic(err)
}
ConfigStoragePathSaved = ConfigStoragePath
RunStoragePathSaved = RunStoragePath
// allow the tests to run without affecting the host system.
ConfigStoragePath = func() string { return filepath.Join(testDir, StoragePathSuffix, "config") }
RunStoragePath = func() string { return filepath.Join(testDir, StoragePathSuffix, "run") }
// set now that ConfigStoragePath has been overridden.
sandboxDirConfig = filepath.Join(ConfigStoragePath(), testSandboxID)
sandboxFileConfig = filepath.Join(ConfigStoragePath(), testSandboxID, ConfigurationFile)
sandboxDirState = filepath.Join(RunStoragePath(), testSandboxID)
sandboxDirLock = filepath.Join(RunStoragePath(), testSandboxID)
sandboxFileState = filepath.Join(RunStoragePath(), testSandboxID, StateFile)
sandboxFileLock = filepath.Join(RunStoragePath(), testSandboxID, LockFile)
}

View File

@ -1,336 +0,0 @@
// Copyright (c) 2019 Intel Corporation
//
// SPDX-License-Identifier: Apache-2.0
//
package store
import (
"context"
"encoding/json"
"fmt"
"path/filepath"
"github.com/kata-containers/kata-containers/src/runtime/virtcontainers/device/api"
"github.com/kata-containers/kata-containers/src/runtime/virtcontainers/device/config"
"github.com/kata-containers/kata-containers/src/runtime/virtcontainers/device/drivers"
"github.com/kata-containers/kata-containers/src/runtime/virtcontainers/types"
)
// VCStorePrefix is only used for tests to config a temp store dir
var VCStorePrefix = ""
// VCStore is a virtcontainers specific Store.
// Virtcontainers typically needs a configuration Store for
// storing permanent items across reboots.
// It also needs a state Store for storing states and other run-time
// related items. Those should not survive a reboot.
//
// VCStore simply dispatches items into the right Store.
type VCStore struct {
config, state, uuid *Store
}
func (s *VCStore) itemToStore(item Item) *Store {
switch item {
case Configuration:
return s.config
case State, Network, Hypervisor, Agent, Process, Lock, Mounts, Devices, DeviceIDs:
return s.state
case UUID:
return s.uuid
}
return s.state
}
// NewVCStore creates a virtcontainers specific Store.
func NewVCStore(ctx context.Context, configRoot, stateRoot string) (*VCStore, error) {
config, err := New(ctx, configRoot)
if err != nil {
fmt.Printf("config root %s\n", configRoot)
return nil, err
}
state, err := New(ctx, stateRoot)
if err != nil {
return nil, err
}
uuid, err := New(ctx, VCStoreUUIDPath())
if err != nil {
return nil, err
}
return &VCStore{
config: config,
state: state,
uuid: uuid,
}, nil
}
// NewVCSandboxStore creates a virtcontainers sandbox Store, with filesystem backend.
func NewVCSandboxStore(ctx context.Context, sandboxID string) (*VCStore, error) {
if sandboxID == "" {
return nil, fmt.Errorf("sandbox ID can not be empty")
}
return NewVCStore(ctx,
SandboxConfigurationRoot(sandboxID),
SandboxRuntimeRoot(sandboxID),
)
}
// NewVCContainerStore creates a virtcontainers container Store, with filesystem backend.
func NewVCContainerStore(ctx context.Context, sandboxID, containerID string) (*VCStore, error) {
if sandboxID == "" {
return nil, fmt.Errorf("sandbox ID can not be empty")
}
if containerID == "" {
return nil, fmt.Errorf("container ID can not be empty")
}
return NewVCStore(ctx,
ContainerConfigurationRoot(sandboxID, containerID),
ContainerRuntimeRoot(sandboxID, containerID),
)
}
// Store stores a virtcontainers item into the right Store.
func (s *VCStore) Store(item Item, data interface{}) error {
return s.itemToStore(item).Store(item, data)
}
// Load loads a virtcontainers item from the right Store.
func (s *VCStore) Load(item Item, data interface{}) error {
return s.itemToStore(item).Load(item, data)
}
// Delete deletes all artifacts created by a VCStore.
// Both config and state Stores are also removed from the manager.
func (s *VCStore) Delete() error {
if err := s.config.Delete(); err != nil {
return err
}
if err := s.state.Delete(); err != nil {
return err
}
return nil
}
// LoadState loads an returns a virtcontainer state
func (s *VCStore) LoadState() (types.SandboxState, error) {
var state types.SandboxState
if err := s.state.Load(State, &state); err != nil {
return types.SandboxState{}, err
}
return state, nil
}
// LoadContainerState loads an returns a virtcontainer state
func (s *VCStore) LoadContainerState() (types.ContainerState, error) {
var state types.ContainerState
if err := s.state.Load(State, &state); err != nil {
return types.ContainerState{}, err
}
return state, nil
}
// TypedDevice is used as an intermediate representation for marshalling
// and unmarshalling Device implementations.
type TypedDevice struct {
Type string
// Data is assigned the Device object.
// This being declared as RawMessage prevents it from being marshalled/unmarshalled.
// We do that explicitly depending on Type.
Data json.RawMessage
}
// StoreDevices stores a virtcontainers devices slice.
// The Device slice is first marshalled into a TypedDevice
// one to include the type of the Device objects.
func (s *VCStore) StoreDevices(devices []api.Device) error {
var typedDevices []TypedDevice
for _, d := range devices {
tempJSON, _ := json.Marshal(d)
typedDevice := TypedDevice{
Type: string(d.DeviceType()),
Data: tempJSON,
}
typedDevices = append(typedDevices, typedDevice)
}
return s.state.Store(Devices, typedDevices)
}
// LoadDevices loads an returns a virtcontainer devices slice.
// We need a custom unmarshalling routine for translating TypedDevices
// into api.Devices based on their type.
func (s *VCStore) LoadDevices() ([]api.Device, error) {
var typedDevices []TypedDevice
var devices []api.Device
if err := s.state.Load(Devices, &typedDevices); err != nil {
return []api.Device{}, err
}
for _, d := range typedDevices {
switch d.Type {
case string(config.DeviceVFIO):
// TODO: remove dependency of drivers package
var device drivers.VFIODevice
if err := json.Unmarshal(d.Data, &device); err != nil {
return []api.Device{}, err
}
devices = append(devices, &device)
case string(config.DeviceBlock):
// TODO: remove dependency of drivers package
var device drivers.BlockDevice
if err := json.Unmarshal(d.Data, &device); err != nil {
return []api.Device{}, err
}
devices = append(devices, &device)
case string(config.DeviceGeneric):
// TODO: remove dependency of drivers package
var device drivers.GenericDevice
if err := json.Unmarshal(d.Data, &device); err != nil {
return []api.Device{}, err
}
devices = append(devices, &device)
default:
return []api.Device{}, fmt.Errorf("Unknown device type, could not unmarshal")
}
}
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)
}
// 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.
// This will hold across host reboot persistent data about a sandbox configuration.
// It should look like file:///var/lib/vc/sbs/<sandboxID>/
// Or for rootless: file://<rootlessDir>/var/lib/vc/sbs/<sandboxID>/
func SandboxConfigurationRoot(id string) string {
return filesystemScheme + "://" + SandboxConfigurationRootPath(id)
}
// SandboxConfigurationRootPath returns a virtcontainers sandbox configuration root path.
func SandboxConfigurationRootPath(id string) string {
return filepath.Join(VCStorePrefix, ConfigStoragePath(), id)
}
// SandboxConfigurationItemPath returns a virtcontainers sandbox configuration item path.
func SandboxConfigurationItemPath(id string, item Item) (string, error) {
if id == "" {
return "", fmt.Errorf("Empty sandbox ID")
}
itemFile, err := itemToFile(item)
if err != nil {
return "", err
}
return filepath.Join(VCStorePrefix, ConfigStoragePath(), id, itemFile), nil
}
// VCStoreUUIDPath returns a virtcontainers runtime uuid URL.
func VCStoreUUIDPath() string {
return filesystemScheme + "://" + filepath.Join(VCStorePrefix, VMUUIDStoragePath())
}
// SandboxRuntimeRoot returns a virtcontainers sandbox runtime root URL.
// This will hold data related to a sandbox run-time state that will not
// be persistent across host reboots.
// It should look like file:///run/vc/sbs/<sandboxID>/
// or if rootless: file://<rootlessDir>/run/vc/sbs/<sandboxID>/
func SandboxRuntimeRoot(id string) string {
return filesystemScheme + "://" + SandboxRuntimeRootPath(id)
}
// SandboxRuntimeRootPath returns a virtcontainers sandbox runtime root path.
func SandboxRuntimeRootPath(id string) string {
return filepath.Join(VCStorePrefix, RunStoragePath(), id)
}
// SandboxRuntimeItemPath returns a virtcontainers sandbox runtime item path.
func SandboxRuntimeItemPath(id string, item Item) (string, error) {
if id == "" {
return "", fmt.Errorf("Empty sandbox ID")
}
itemFile, err := itemToFile(item)
if err != nil {
return "", err
}
return filepath.Join(RunStoragePath(), id, itemFile), nil
}
// ContainerConfigurationRoot returns a virtcontainers container configuration root URL.
// This will hold across host reboot persistent data about a container configuration.
// It should look like file:///var/lib/vc/sbs/<sandboxID>/<containerID>
// Or if rootless file://<rootlessDir>/var/lib/vc/sbs/<sandboxID>/<containerID>
func ContainerConfigurationRoot(sandboxID, containerID string) string {
return filesystemScheme + "://" + ContainerConfigurationRootPath(sandboxID, containerID)
}
// ContainerConfigurationRootPath returns a virtcontainers container configuration root path.
func ContainerConfigurationRootPath(sandboxID, containerID string) string {
return filepath.Join(VCStorePrefix, ConfigStoragePath(), sandboxID, containerID)
}
// ContainerRuntimeRoot returns a virtcontainers container runtime root URL.
// This will hold data related to a container run-time state that will not
// be persistent across host reboots.
// It should look like file:///run/vc/sbs/<sandboxID>/<containerID>/
// Or for rootless file://<rootlessDir>/run/vc/sbs/<sandboxID>/<containerID>/
func ContainerRuntimeRoot(sandboxID, containerID string) string {
return filesystemScheme + "://" + ContainerRuntimeRootPath(sandboxID, containerID)
}
// ContainerRuntimeRootPath returns a virtcontainers container runtime root path.
func ContainerRuntimeRootPath(sandboxID, containerID string) string {
return filepath.Join(VCStorePrefix, RunStoragePath(), sandboxID, containerID)
}
// VCSandboxStoreExists returns true if a sandbox store already exists.
func VCSandboxStoreExists(ctx context.Context, sandboxID string) bool {
s := stores.findStore(SandboxConfigurationRoot(sandboxID))
return s != nil
}

View File

@ -1,142 +0,0 @@
// Copyright (c) 2019 Intel Corporation
//
// SPDX-License-Identifier: Apache-2.0
//
package store
import (
"context"
"fmt"
"io/ioutil"
"os"
"testing"
"github.com/stretchr/testify/assert"
)
func TestStoreVCRoots(t *testing.T) {
rootURL := filesystemScheme + "://" + ConfigStoragePath()
sandboxID := "sandbox"
containerID := "container"
sConfigRoot := rootURL + "/" + sandboxID
cConfigRoot := rootURL + "/" + sandboxID + "/" + containerID
assert.Equal(t, SandboxConfigurationRoot(sandboxID), sConfigRoot)
assert.Equal(t, ContainerConfigurationRoot(sandboxID, containerID), cConfigRoot)
}
func testStoreVCSandboxDir(t *testing.T, item Item, expected string) error {
var dir string
if item == Configuration {
dir = SandboxConfigurationRootPath(testSandboxID)
} else {
dir = SandboxRuntimeRootPath(testSandboxID)
}
if dir != expected {
return fmt.Errorf("Unexpected sandbox directory %s vs %s", dir, expected)
}
return nil
}
func testStoreVCSandboxFile(t *testing.T, item Item, expected string) error {
var file string
var err error
if item == Configuration {
file, err = SandboxConfigurationItemPath(testSandboxID, item)
} else {
file, err = SandboxRuntimeItemPath(testSandboxID, item)
}
if err != nil {
return err
}
if file != expected {
return fmt.Errorf("Unexpected sandbox file %s vs %s", file, expected)
}
return nil
}
func TestStoreVCSandboxDirConfig(t *testing.T) {
err := testStoreVCSandboxDir(t, Configuration, sandboxDirConfig)
assert.Nil(t, err)
}
func TestStoreVCSandboxDirState(t *testing.T) {
err := testStoreVCSandboxDir(t, State, sandboxDirState)
assert.Nil(t, err)
}
func TestStoreVCSandboxDirLock(t *testing.T) {
err := testStoreVCSandboxDir(t, Lock, sandboxDirLock)
assert.Nil(t, err)
}
func TestStoreVCSandboxFileConfig(t *testing.T) {
err := testStoreVCSandboxFile(t, Configuration, sandboxFileConfig)
assert.Nil(t, err)
}
func TestStoreVCSandboxFileState(t *testing.T) {
err := testStoreVCSandboxFile(t, State, sandboxFileState)
assert.Nil(t, err)
}
func TestStoreVCSandboxFileLock(t *testing.T) {
err := testStoreVCSandboxFile(t, Lock, sandboxFileLock)
assert.Nil(t, err)
}
func TestStoreVCSandboxFileNegative(t *testing.T) {
_, err := SandboxConfigurationItemPath("", State)
assert.NotNil(t, err)
_, err = SandboxRuntimeItemPath("", State)
assert.NotNil(t, err)
}
func TestStoreVCNewVCSandboxStore(t *testing.T) {
testDir, _ := ioutil.TempDir("", "vmfactory-tmp-")
defer func() {
os.RemoveAll(testDir)
}()
var savedStorePath = VCStorePrefix
VCStorePrefix = testDir
defer func() {
VCStorePrefix = savedStorePath
}()
_, err := NewVCSandboxStore(context.Background(), testSandboxID)
assert.Nil(t, err)
_, err = NewVCSandboxStore(context.Background(), "")
assert.NotNil(t, err)
}
func TestStoreVCNewVCContainerStore(t *testing.T) {
testDir, _ := ioutil.TempDir("", "vmfactory-tmp-")
defer func() {
os.RemoveAll(testDir)
}()
var savedStorePath = VCStorePrefix
VCStorePrefix = testDir
defer func() {
VCStorePrefix = savedStorePath
}()
_, err := NewVCContainerStore(context.Background(), testSandboxID, "foobar")
assert.Nil(t, err)
_, err = NewVCContainerStore(context.Background(), "", "foobar")
assert.NotNil(t, err)
_, err = NewVCContainerStore(context.Background(), "", "foobar")
assert.NotNil(t, err)
}

View File

@ -18,7 +18,6 @@ import (
"github.com/kata-containers/kata-containers/src/runtime/virtcontainers/persist" "github.com/kata-containers/kata-containers/src/runtime/virtcontainers/persist"
"github.com/kata-containers/kata-containers/src/runtime/virtcontainers/persist/fs" "github.com/kata-containers/kata-containers/src/runtime/virtcontainers/persist/fs"
"github.com/kata-containers/kata-containers/src/runtime/virtcontainers/store"
"github.com/kata-containers/kata-containers/src/runtime/virtcontainers/utils" "github.com/kata-containers/kata-containers/src/runtime/virtcontainers/utils"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
) )
@ -64,14 +63,10 @@ func cleanUp() {
os.RemoveAll(testDir) os.RemoveAll(testDir)
os.MkdirAll(testDir, DirMode) os.MkdirAll(testDir, DirMode)
store.DeleteAll()
store.VCStorePrefix = ""
setup() setup()
} }
func setup() { func setup() {
store.VCStorePrefix = testDir
os.Mkdir(filepath.Join(testDir, testBundle), DirMode) os.Mkdir(filepath.Join(testDir, testBundle), DirMode)
for _, filename := range []string{testQemuKernelPath, testQemuInitrdPath, testQemuImagePath, testQemuPath} { for _, filename := range []string{testQemuKernelPath, testQemuInitrdPath, testQemuImagePath, testQemuPath} {