mirror of
https://github.com/kata-containers/kata-containers.git
synced 2025-06-27 07:48:55 +00:00
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:
parent
4b01ec1f3c
commit
5200ac0678
@ -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/compatoci"
|
||||
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"
|
||||
specs "github.com/opencontainers/runtime-spec/specs-go"
|
||||
opentracing "github.com/opentracing/opentracing-go"
|
||||
@ -52,7 +51,6 @@ func SetLogger(ctx context.Context, logger *logrus.Entry) {
|
||||
|
||||
deviceApi.SetLogger(virtLog)
|
||||
compatoci.SetLogger(virtLog)
|
||||
store.SetLogger(virtLog)
|
||||
deviceConfig.SetLogger(virtLog)
|
||||
cgroups.SetLogger(virtLog)
|
||||
}
|
||||
|
@ -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/manager"
|
||||
"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
|
||||
@ -337,8 +336,6 @@ type Container struct {
|
||||
systemMountsInfo SystemMountsInfo
|
||||
|
||||
ctx context.Context
|
||||
|
||||
store *store.VCStore
|
||||
}
|
||||
|
||||
// ID returns the container identifier string.
|
||||
@ -430,17 +427,9 @@ func (c *Container) setContainerState(state types.StateString) error {
|
||||
// update in-memory state
|
||||
c.state.State = state
|
||||
|
||||
if useOldStore(c.sandbox.ctx) {
|
||||
// experimental runtime use "persist.json" which doesn't need "state.json" anymore
|
||||
// update on-disk state
|
||||
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
|
||||
}
|
||||
// flush data to storage
|
||||
if err := c.sandbox.Save(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
@ -724,33 +713,16 @@ func newContainer(sandbox *Sandbox, contConfig *ContainerConfig) (*Container, er
|
||||
ctx: sandbox.ctx,
|
||||
}
|
||||
|
||||
if useOldStore(sandbox.ctx) {
|
||||
ctrStore, err := store.NewVCContainerStore(sandbox.ctx, c.sandboxID, c.id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
c.store = ctrStore
|
||||
state, err := c.store.LoadContainerState()
|
||||
if err == nil {
|
||||
c.state = state
|
||||
}
|
||||
// experimental runtime use "persist.json" instead of legacy "state.json" as storage
|
||||
err := c.Restore()
|
||||
if err == nil {
|
||||
//container restored
|
||||
return c, nil
|
||||
}
|
||||
|
||||
var process Process
|
||||
if err := c.store.Load(store.Process, &process); err == nil {
|
||||
c.process = process
|
||||
}
|
||||
} 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
|
||||
}
|
||||
// Unexpected error
|
||||
if !os.IsNotExist(err) && err != errContainerPersistNotExist {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Go to next step for first created container
|
||||
@ -765,35 +737,7 @@ func newContainer(sandbox *Sandbox, contConfig *ContainerConfig) (*Container, er
|
||||
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 {
|
||||
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
|
||||
if err := c.createBlockDevices(); err != nil {
|
||||
return err
|
||||
@ -803,18 +747,6 @@ func (c *Container) createMounts() 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
|
||||
// from the configuration. This should happen at create.
|
||||
var storedDevices []ContainerDevice
|
||||
|
@ -30,7 +30,6 @@ import (
|
||||
"github.com/kata-containers/kata-containers/src/runtime/virtcontainers/pkg/rootless"
|
||||
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/store"
|
||||
"github.com/kata-containers/kata-containers/src/runtime/virtcontainers/types"
|
||||
|
||||
"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)
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
|
@ -6,14 +6,12 @@
|
||||
package virtcontainers
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
|
||||
"github.com/kata-containers/kata-containers/src/runtime/virtcontainers/device/api"
|
||||
exp "github.com/kata-containers/kata-containers/src/runtime/virtcontainers/experimental"
|
||||
"github.com/kata-containers/kata-containers/src/runtime/virtcontainers/persist"
|
||||
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/mitchellh/mapstructure"
|
||||
)
|
||||
@ -579,25 +577,3 @@ func loadSandboxConfig(id string) (*SandboxConfig, error) {
|
||||
}
|
||||
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
|
||||
}
|
||||
|
@ -25,7 +25,6 @@ import (
|
||||
"github.com/sirupsen/logrus"
|
||||
"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/config"
|
||||
"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"
|
||||
"github.com/kata-containers/kata-containers/src/runtime/virtcontainers/persist"
|
||||
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"
|
||||
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/rootless"
|
||||
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/utils"
|
||||
)
|
||||
@ -183,9 +182,7 @@ type Sandbox struct {
|
||||
factory Factory
|
||||
hypervisor hypervisor
|
||||
agent agent
|
||||
store *store.VCStore
|
||||
// store is used to replace VCStore step by step
|
||||
newStore persistapi.PersistDriver
|
||||
newStore persistapi.PersistDriver
|
||||
|
||||
network Network
|
||||
monitor *monitor
|
||||
@ -559,51 +556,18 @@ func newSandbox(ctx context.Context, sandboxConfig SandboxConfig, factory Factor
|
||||
sandboxConfig.HypervisorConfig.SELinuxProcessLabel = spec.Process.SelinuxLabel
|
||||
}
|
||||
|
||||
if useOldStore(ctx) {
|
||||
vcStore, err := store.NewVCSandboxStore(ctx, s.id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
s.devManager = deviceManager.NewDeviceManager(sandboxConfig.HypervisorConfig.BlockDeviceDriver,
|
||||
sandboxConfig.HypervisorConfig.EnableVhostUserStore,
|
||||
sandboxConfig.HypervisorConfig.VhostUserStorePath, nil)
|
||||
|
||||
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.
|
||||
var networkNS NetworkNamespace
|
||||
if err = s.store.Load(store.Network, &networkNS); err == nil {
|
||||
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
|
||||
}
|
||||
// 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 {
|
||||
@ -717,22 +681,15 @@ func fetchSandbox(ctx context.Context, sandboxID string) (sandbox *Sandbox, err
|
||||
|
||||
var config SandboxConfig
|
||||
|
||||
// Try to load sandbox config from old store at first.
|
||||
c, ctx, err := loadSandboxConfigFromOldStore(ctx, sandboxID)
|
||||
// load sandbox config fromld store.
|
||||
c, err := loadSandboxConfig(sandboxID)
|
||||
if err != nil {
|
||||
virtLog.Warningf("failed to get sandbox config from old store: %v", err)
|
||||
// If we failed to load sandbox config from old store, try again with new store.
|
||||
c, err = loadSandboxConfig(sandboxID)
|
||||
if err != nil {
|
||||
virtLog.Warningf("failed to get sandbox config from new store: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
virtLog.Warningf("failed to get sandbox config from new store: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
config = *c
|
||||
|
||||
if useOldStore(ctx) {
|
||||
virtLog.Infof("Warning: old store has been deprecated.")
|
||||
}
|
||||
// fetchSandbox is not suppose to create new sandbox VM.
|
||||
sandbox, err = createSandbox(ctx, config, nil)
|
||||
if err != nil {
|
||||
@ -1594,9 +1551,6 @@ func (s *Sandbox) setSandboxState(state types.StateString) error {
|
||||
// update in-memory state
|
||||
s.state.State = state
|
||||
|
||||
if useOldStore(s.ctx) {
|
||||
return s.store.Store(store.State, s.state)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
}
|
@ -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
|
||||
}
|
@ -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)
|
||||
}
|
@ -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)
|
||||
}
|
@ -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)
|
||||
}
|
@ -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
|
||||
}
|
@ -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)
|
||||
}
|
@ -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/fs"
|
||||
"github.com/kata-containers/kata-containers/src/runtime/virtcontainers/store"
|
||||
"github.com/kata-containers/kata-containers/src/runtime/virtcontainers/utils"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
@ -64,14 +63,10 @@ func cleanUp() {
|
||||
os.RemoveAll(testDir)
|
||||
os.MkdirAll(testDir, DirMode)
|
||||
|
||||
store.DeleteAll()
|
||||
store.VCStorePrefix = ""
|
||||
|
||||
setup()
|
||||
}
|
||||
|
||||
func setup() {
|
||||
store.VCStorePrefix = testDir
|
||||
os.Mkdir(filepath.Join(testDir, testBundle), DirMode)
|
||||
|
||||
for _, filename := range []string{testQemuKernelPath, testQemuInitrdPath, testQemuImagePath, testQemuPath} {
|
||||
|
Loading…
Reference in New Issue
Block a user