github.com/kata-containers/runtime@v0.0.0-20210505125100-04f29832a923/virtcontainers/store/vc.go (about)

     1  // Copyright (c) 2019 Intel Corporation
     2  //
     3  // SPDX-License-Identifier: Apache-2.0
     4  //
     5  
     6  package store
     7  
     8  import (
     9  	"context"
    10  	"encoding/json"
    11  	"fmt"
    12  	"path/filepath"
    13  
    14  	"github.com/kata-containers/runtime/virtcontainers/device/api"
    15  	"github.com/kata-containers/runtime/virtcontainers/device/config"
    16  	"github.com/kata-containers/runtime/virtcontainers/device/drivers"
    17  	"github.com/kata-containers/runtime/virtcontainers/types"
    18  )
    19  
    20  // VCStorePrefix is only used for tests to config a temp store dir
    21  var VCStorePrefix = ""
    22  
    23  // VCStore is a virtcontainers specific Store.
    24  // Virtcontainers typically needs a configuration Store for
    25  // storing permanent items across reboots.
    26  // It also needs a state Store for storing states and other run-time
    27  // related items. Those should not survive a reboot.
    28  //
    29  // VCStore simply dispatches items into the right Store.
    30  type VCStore struct {
    31  	config, state, uuid *Store
    32  }
    33  
    34  func (s *VCStore) itemToStore(item Item) *Store {
    35  	switch item {
    36  	case Configuration:
    37  		return s.config
    38  	case State, Network, Hypervisor, Agent, Process, Lock, Mounts, Devices, DeviceIDs:
    39  		return s.state
    40  	case UUID:
    41  		return s.uuid
    42  	}
    43  
    44  	return s.state
    45  }
    46  
    47  // NewVCStore creates a virtcontainers specific Store.
    48  func NewVCStore(ctx context.Context, configRoot, stateRoot string) (*VCStore, error) {
    49  	config, err := New(ctx, configRoot)
    50  	if err != nil {
    51  		fmt.Printf("config root %s\n", configRoot)
    52  		return nil, err
    53  	}
    54  
    55  	state, err := New(ctx, stateRoot)
    56  	if err != nil {
    57  		return nil, err
    58  	}
    59  
    60  	uuid, err := New(ctx, VCStoreUUIDPath())
    61  	if err != nil {
    62  		return nil, err
    63  	}
    64  
    65  	return &VCStore{
    66  		config: config,
    67  		state:  state,
    68  		uuid:   uuid,
    69  	}, nil
    70  }
    71  
    72  // NewVCSandboxStore creates a virtcontainers sandbox Store, with filesystem backend.
    73  func NewVCSandboxStore(ctx context.Context, sandboxID string) (*VCStore, error) {
    74  	if sandboxID == "" {
    75  		return nil, fmt.Errorf("sandbox ID can not be empty")
    76  	}
    77  
    78  	return NewVCStore(ctx,
    79  		SandboxConfigurationRoot(sandboxID),
    80  		SandboxRuntimeRoot(sandboxID),
    81  	)
    82  }
    83  
    84  // NewVCContainerStore creates a virtcontainers container Store, with filesystem backend.
    85  func NewVCContainerStore(ctx context.Context, sandboxID, containerID string) (*VCStore, error) {
    86  	if sandboxID == "" {
    87  		return nil, fmt.Errorf("sandbox ID can not be empty")
    88  	}
    89  
    90  	if containerID == "" {
    91  		return nil, fmt.Errorf("container ID can not be empty")
    92  	}
    93  
    94  	return NewVCStore(ctx,
    95  		ContainerConfigurationRoot(sandboxID, containerID),
    96  		ContainerRuntimeRoot(sandboxID, containerID),
    97  	)
    98  }
    99  
   100  // Store stores a virtcontainers item into the right Store.
   101  func (s *VCStore) Store(item Item, data interface{}) error {
   102  	return s.itemToStore(item).Store(item, data)
   103  }
   104  
   105  // Load loads a virtcontainers item from the right Store.
   106  func (s *VCStore) Load(item Item, data interface{}) error {
   107  	return s.itemToStore(item).Load(item, data)
   108  }
   109  
   110  // Delete deletes all artifacts created by a VCStore.
   111  // Both config and state Stores are also removed from the manager.
   112  func (s *VCStore) Delete() error {
   113  	if err := s.config.Delete(); err != nil {
   114  		return err
   115  	}
   116  
   117  	if err := s.state.Delete(); err != nil {
   118  		return err
   119  	}
   120  
   121  	return nil
   122  }
   123  
   124  // LoadState loads an returns a virtcontainer state
   125  func (s *VCStore) LoadState() (types.SandboxState, error) {
   126  	var state types.SandboxState
   127  
   128  	if err := s.state.Load(State, &state); err != nil {
   129  		return types.SandboxState{}, err
   130  	}
   131  
   132  	return state, nil
   133  }
   134  
   135  // LoadContainerState loads an returns a virtcontainer state
   136  func (s *VCStore) LoadContainerState() (types.ContainerState, error) {
   137  	var state types.ContainerState
   138  
   139  	if err := s.state.Load(State, &state); err != nil {
   140  		return types.ContainerState{}, err
   141  	}
   142  
   143  	return state, nil
   144  }
   145  
   146  // TypedDevice is used as an intermediate representation for marshalling
   147  // and unmarshalling Device implementations.
   148  type TypedDevice struct {
   149  	Type string
   150  
   151  	// Data is assigned the Device object.
   152  	// This being declared as RawMessage prevents it from being  marshalled/unmarshalled.
   153  	// We do that explicitly depending on Type.
   154  	Data json.RawMessage
   155  }
   156  
   157  // StoreDevices stores a virtcontainers devices slice.
   158  // The Device slice is first marshalled into a TypedDevice
   159  // one to include the type of the Device objects.
   160  func (s *VCStore) StoreDevices(devices []api.Device) error {
   161  	var typedDevices []TypedDevice
   162  
   163  	for _, d := range devices {
   164  		tempJSON, _ := json.Marshal(d)
   165  		typedDevice := TypedDevice{
   166  			Type: string(d.DeviceType()),
   167  			Data: tempJSON,
   168  		}
   169  		typedDevices = append(typedDevices, typedDevice)
   170  	}
   171  
   172  	return s.state.Store(Devices, typedDevices)
   173  }
   174  
   175  // LoadDevices loads an returns a virtcontainer devices slice.
   176  // We need a custom unmarshalling routine for translating TypedDevices
   177  // into api.Devices based on their type.
   178  func (s *VCStore) LoadDevices() ([]api.Device, error) {
   179  	var typedDevices []TypedDevice
   180  	var devices []api.Device
   181  
   182  	if err := s.state.Load(Devices, &typedDevices); err != nil {
   183  		return []api.Device{}, err
   184  	}
   185  
   186  	for _, d := range typedDevices {
   187  		switch d.Type {
   188  		case string(config.DeviceVFIO):
   189  			// TODO: remove dependency of drivers package
   190  			var device drivers.VFIODevice
   191  			if err := json.Unmarshal(d.Data, &device); err != nil {
   192  				return []api.Device{}, err
   193  			}
   194  			devices = append(devices, &device)
   195  		case string(config.DeviceBlock):
   196  			// TODO: remove dependency of drivers package
   197  			var device drivers.BlockDevice
   198  			if err := json.Unmarshal(d.Data, &device); err != nil {
   199  				return []api.Device{}, err
   200  			}
   201  			devices = append(devices, &device)
   202  		case string(config.DeviceGeneric):
   203  			// TODO: remove dependency of drivers package
   204  			var device drivers.GenericDevice
   205  			if err := json.Unmarshal(d.Data, &device); err != nil {
   206  				return []api.Device{}, err
   207  			}
   208  			devices = append(devices, &device)
   209  		default:
   210  			return []api.Device{}, fmt.Errorf("Unknown device type, could not unmarshal")
   211  		}
   212  	}
   213  
   214  	return devices, nil
   215  }
   216  
   217  // Raw creates a raw item in the virtcontainer state Store. A raw
   218  // item is a custom one, not defined through the Item enum, and that
   219  // the caller needs to handle directly.
   220  // Typically this is used to create a custom virtcontainers file.
   221  // For example the Firecracker code uses this API to create temp
   222  // files under the sandbox state root path, and uses them as block
   223  // driver backend placeholder.
   224  func (s *VCStore) Raw(id string) (string, error) {
   225  	return s.state.Raw(id)
   226  }
   227  
   228  // Lock takes an exclusive lock on the virtcontainers state Lock item.
   229  func (s *VCStore) Lock() (string, error) {
   230  	return s.state.ItemLock(Lock, true)
   231  }
   232  
   233  // RLock takes a shared lock on the virtcontainers state Lock item.
   234  func (s *VCStore) RLock() (string, error) {
   235  	return s.state.ItemLock(Lock, false)
   236  }
   237  
   238  // Unlock unlocks the virtcontainers state Lock item.
   239  func (s *VCStore) Unlock(token string) error {
   240  	return s.state.ItemUnlock(Lock, token)
   241  }
   242  
   243  // Utilities for virtcontainers
   244  
   245  // SandboxConfigurationRoot returns a virtcontainers sandbox configuration root URL.
   246  // This will hold across host reboot persistent data about a sandbox configuration.
   247  // It should look like file:///var/lib/vc/sbs/<sandboxID>/
   248  // Or for rootless: file://<rootlessDir>/var/lib/vc/sbs/<sandboxID>/
   249  func SandboxConfigurationRoot(id string) string {
   250  	return filesystemScheme + "://" + SandboxConfigurationRootPath(id)
   251  }
   252  
   253  // SandboxConfigurationRootPath returns a virtcontainers sandbox configuration root path.
   254  func SandboxConfigurationRootPath(id string) string {
   255  	return filepath.Join(VCStorePrefix, ConfigStoragePath(), id)
   256  }
   257  
   258  // SandboxConfigurationItemPath returns a virtcontainers sandbox configuration item path.
   259  func SandboxConfigurationItemPath(id string, item Item) (string, error) {
   260  	if id == "" {
   261  		return "", fmt.Errorf("Empty sandbox ID")
   262  	}
   263  
   264  	itemFile, err := itemToFile(item)
   265  	if err != nil {
   266  		return "", err
   267  	}
   268  
   269  	return filepath.Join(VCStorePrefix, ConfigStoragePath(), id, itemFile), nil
   270  }
   271  
   272  // VCStoreUUIDPath returns a virtcontainers runtime uuid URL.
   273  func VCStoreUUIDPath() string {
   274  	return filesystemScheme + "://" + filepath.Join(VCStorePrefix, VMUUIDStoragePath())
   275  }
   276  
   277  // SandboxRuntimeRoot returns a virtcontainers sandbox runtime root URL.
   278  // This will hold data related to a sandbox run-time state that will not
   279  // be persistent across host reboots.
   280  // It should look like file:///run/vc/sbs/<sandboxID>/
   281  // or if rootless: file://<rootlessDir>/run/vc/sbs/<sandboxID>/
   282  func SandboxRuntimeRoot(id string) string {
   283  	return filesystemScheme + "://" + SandboxRuntimeRootPath(id)
   284  }
   285  
   286  // SandboxRuntimeRootPath returns a virtcontainers sandbox runtime root path.
   287  func SandboxRuntimeRootPath(id string) string {
   288  	return filepath.Join(VCStorePrefix, RunStoragePath(), id)
   289  }
   290  
   291  // SandboxRuntimeItemPath returns a virtcontainers sandbox runtime item path.
   292  func SandboxRuntimeItemPath(id string, item Item) (string, error) {
   293  	if id == "" {
   294  		return "", fmt.Errorf("Empty sandbox ID")
   295  	}
   296  
   297  	itemFile, err := itemToFile(item)
   298  	if err != nil {
   299  		return "", err
   300  	}
   301  
   302  	return filepath.Join(RunStoragePath(), id, itemFile), nil
   303  }
   304  
   305  // ContainerConfigurationRoot returns a virtcontainers container configuration root URL.
   306  // This will hold across host reboot persistent data about a container configuration.
   307  // It should look like file:///var/lib/vc/sbs/<sandboxID>/<containerID>
   308  // Or if rootless file://<rootlessDir>/var/lib/vc/sbs/<sandboxID>/<containerID>
   309  func ContainerConfigurationRoot(sandboxID, containerID string) string {
   310  	return filesystemScheme + "://" + ContainerConfigurationRootPath(sandboxID, containerID)
   311  }
   312  
   313  // ContainerConfigurationRootPath returns a virtcontainers container configuration root path.
   314  func ContainerConfigurationRootPath(sandboxID, containerID string) string {
   315  	return filepath.Join(VCStorePrefix, ConfigStoragePath(), sandboxID, containerID)
   316  }
   317  
   318  // ContainerRuntimeRoot returns a virtcontainers container runtime root URL.
   319  // This will hold data related to a container run-time state that will not
   320  // be persistent across host reboots.
   321  // It should look like file:///run/vc/sbs/<sandboxID>/<containerID>/
   322  // Or for rootless file://<rootlessDir>/run/vc/sbs/<sandboxID>/<containerID>/
   323  func ContainerRuntimeRoot(sandboxID, containerID string) string {
   324  	return filesystemScheme + "://" + ContainerRuntimeRootPath(sandboxID, containerID)
   325  }
   326  
   327  // ContainerRuntimeRootPath returns a virtcontainers container runtime root path.
   328  func ContainerRuntimeRootPath(sandboxID, containerID string) string {
   329  	return filepath.Join(VCStorePrefix, RunStoragePath(), sandboxID, containerID)
   330  }
   331  
   332  // VCSandboxStoreExists returns true if a sandbox store already exists.
   333  func VCSandboxStoreExists(ctx context.Context, sandboxID string) bool {
   334  	s := stores.findStore(SandboxConfigurationRoot(sandboxID))
   335  	return s != nil
   336  }