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 }