gitee.com/leisunstar/runtime@v0.0.0-20200521203717-5cef3e7b53f9/virtcontainers/persist.go (about) 1 // Copyright (c) 2019 Huawei Corporation 2 // 3 // SPDX-License-Identifier: Apache-2.0 4 // 5 6 package virtcontainers 7 8 import ( 9 "context" 10 "errors" 11 12 "github.com/kata-containers/runtime/virtcontainers/device/api" 13 exp "github.com/kata-containers/runtime/virtcontainers/experimental" 14 "github.com/kata-containers/runtime/virtcontainers/persist" 15 persistapi "github.com/kata-containers/runtime/virtcontainers/persist/api" 16 "github.com/kata-containers/runtime/virtcontainers/store" 17 "github.com/kata-containers/runtime/virtcontainers/types" 18 "github.com/mitchellh/mapstructure" 19 ) 20 21 var ( 22 errContainerPersistNotExist = errors.New("container doesn't exist in persist data") 23 ) 24 25 func (s *Sandbox) dumpVersion(ss *persistapi.SandboxState) { 26 // New created sandbox has a uninitialized `PersistVersion` which should be set to current version when do the first saving; 27 // Old restored sandbox should keep its original version and shouldn't be modified any more after it's initialized. 28 ss.PersistVersion = s.state.PersistVersion 29 if ss.PersistVersion == 0 { 30 ss.PersistVersion = persistapi.CurPersistVersion 31 } 32 } 33 34 func (s *Sandbox) dumpState(ss *persistapi.SandboxState, cs map[string]persistapi.ContainerState) { 35 ss.SandboxContainer = s.id 36 ss.GuestMemoryBlockSizeMB = s.state.GuestMemoryBlockSizeMB 37 ss.GuestMemoryHotplugProbe = s.state.GuestMemoryHotplugProbe 38 ss.State = string(s.state.State) 39 ss.CgroupPath = s.state.CgroupPath 40 ss.CgroupPaths = s.state.CgroupPaths 41 42 for id, cont := range s.containers { 43 state := persistapi.ContainerState{} 44 if v, ok := cs[id]; ok { 45 state = v 46 } 47 state.State = string(cont.state.State) 48 state.Rootfs = persistapi.RootfsState{ 49 BlockDeviceID: cont.state.BlockDeviceID, 50 FsType: cont.state.Fstype, 51 } 52 state.CgroupPath = cont.state.CgroupPath 53 cs[id] = state 54 } 55 56 // delete removed containers 57 for id := range cs { 58 if _, ok := s.containers[id]; !ok { 59 delete(cs, id) 60 } 61 } 62 } 63 64 func (s *Sandbox) dumpHypervisor(ss *persistapi.SandboxState) { 65 ss.HypervisorState = s.hypervisor.save() 66 // BlockIndexMap will be moved from sandbox state to hypervisor state later 67 ss.HypervisorState.BlockIndexMap = s.state.BlockIndexMap 68 } 69 70 func deviceToDeviceState(devices []api.Device) (dss []persistapi.DeviceState) { 71 for _, dev := range devices { 72 dss = append(dss, dev.Save()) 73 } 74 return 75 } 76 77 func (s *Sandbox) dumpDevices(ss *persistapi.SandboxState, cs map[string]persistapi.ContainerState) { 78 ss.Devices = deviceToDeviceState(s.devManager.GetAllDevices()) 79 80 for id, cont := range s.containers { 81 state := persistapi.ContainerState{} 82 if v, ok := cs[id]; ok { 83 state = v 84 } 85 86 state.DeviceMaps = nil 87 for _, dev := range cont.devices { 88 state.DeviceMaps = append(state.DeviceMaps, persistapi.DeviceMap{ 89 ID: dev.ID, 90 ContainerPath: dev.ContainerPath, 91 FileMode: dev.FileMode, 92 UID: dev.UID, 93 GID: dev.GID, 94 }) 95 } 96 97 cs[id] = state 98 } 99 100 // delete removed containers 101 for id := range cs { 102 if _, ok := s.containers[id]; !ok { 103 delete(cs, id) 104 } 105 } 106 } 107 108 func (s *Sandbox) dumpProcess(cs map[string]persistapi.ContainerState) { 109 for id, cont := range s.containers { 110 state := persistapi.ContainerState{} 111 if v, ok := cs[id]; ok { 112 state = v 113 } 114 115 state.Process = persistapi.Process{ 116 Token: cont.process.Token, 117 Pid: cont.process.Pid, 118 StartTime: cont.process.StartTime, 119 } 120 121 cs[id] = state 122 } 123 124 // delete removed containers 125 for id := range cs { 126 if _, ok := s.containers[id]; !ok { 127 delete(cs, id) 128 } 129 } 130 } 131 132 func (s *Sandbox) dumpMounts(cs map[string]persistapi.ContainerState) { 133 for id, cont := range s.containers { 134 state := persistapi.ContainerState{} 135 if v, ok := cs[id]; ok { 136 state = v 137 } 138 139 for _, m := range cont.mounts { 140 state.Mounts = append(state.Mounts, persistapi.Mount{ 141 Source: m.Source, 142 Destination: m.Destination, 143 Options: m.Options, 144 HostPath: m.HostPath, 145 ReadOnly: m.ReadOnly, 146 BlockDeviceID: m.BlockDeviceID, 147 }) 148 } 149 150 cs[id] = state 151 } 152 153 // delete removed containers 154 for id := range cs { 155 if _, ok := s.containers[id]; !ok { 156 delete(cs, id) 157 } 158 } 159 } 160 161 func (s *Sandbox) dumpAgent(ss *persistapi.SandboxState) { 162 if s.agent != nil { 163 ss.AgentState = s.agent.save() 164 } 165 } 166 167 func (s *Sandbox) dumpNetwork(ss *persistapi.SandboxState) { 168 ss.Network = persistapi.NetworkInfo{ 169 NetNsPath: s.networkNS.NetNsPath, 170 NetmonPID: s.networkNS.NetmonPID, 171 NetNsCreated: s.networkNS.NetNsCreated, 172 } 173 for _, e := range s.networkNS.Endpoints { 174 ss.Network.Endpoints = append(ss.Network.Endpoints, e.save()) 175 } 176 } 177 178 func (s *Sandbox) dumpConfig(ss *persistapi.SandboxState) { 179 sconfig := s.config 180 ss.Config = persistapi.SandboxConfig{ 181 HypervisorType: string(sconfig.HypervisorType), 182 AgentType: string(sconfig.AgentType), 183 ProxyType: string(sconfig.ProxyType), 184 ProxyConfig: persistapi.ProxyConfig{ 185 Path: sconfig.ProxyConfig.Path, 186 Debug: sconfig.ProxyConfig.Debug, 187 }, 188 ShimType: string(sconfig.ShimType), 189 NetworkConfig: persistapi.NetworkConfig{ 190 NetNSPath: sconfig.NetworkConfig.NetNSPath, 191 NetNsCreated: sconfig.NetworkConfig.NetNsCreated, 192 DisableNewNetNs: sconfig.NetworkConfig.DisableNewNetNs, 193 InterworkingModel: int(sconfig.NetworkConfig.InterworkingModel), 194 }, 195 196 ShmSize: sconfig.ShmSize, 197 SharePidNs: sconfig.SharePidNs, 198 Stateful: sconfig.Stateful, 199 SystemdCgroup: sconfig.SystemdCgroup, 200 SandboxCgroupOnly: sconfig.SandboxCgroupOnly, 201 DisableGuestSeccomp: sconfig.DisableGuestSeccomp, 202 Cgroups: sconfig.Cgroups, 203 } 204 205 for _, e := range sconfig.Experimental { 206 ss.Config.Experimental = append(ss.Config.Experimental, e.Name) 207 } 208 209 ss.Config.HypervisorConfig = persistapi.HypervisorConfig{ 210 NumVCPUs: sconfig.HypervisorConfig.NumVCPUs, 211 DefaultMaxVCPUs: sconfig.HypervisorConfig.DefaultMaxVCPUs, 212 MemorySize: sconfig.HypervisorConfig.MemorySize, 213 DefaultBridges: sconfig.HypervisorConfig.DefaultBridges, 214 Msize9p: sconfig.HypervisorConfig.Msize9p, 215 MemSlots: sconfig.HypervisorConfig.MemSlots, 216 MemOffset: sconfig.HypervisorConfig.MemOffset, 217 VirtioMem: sconfig.HypervisorConfig.VirtioMem, 218 VirtioFSCacheSize: sconfig.HypervisorConfig.VirtioFSCacheSize, 219 KernelPath: sconfig.HypervisorConfig.KernelPath, 220 ImagePath: sconfig.HypervisorConfig.ImagePath, 221 InitrdPath: sconfig.HypervisorConfig.InitrdPath, 222 FirmwarePath: sconfig.HypervisorConfig.FirmwarePath, 223 MachineAccelerators: sconfig.HypervisorConfig.MachineAccelerators, 224 CPUFeatures: sconfig.HypervisorConfig.CPUFeatures, 225 HypervisorPath: sconfig.HypervisorConfig.HypervisorPath, 226 HypervisorCtlPath: sconfig.HypervisorConfig.HypervisorCtlPath, 227 JailerPath: sconfig.HypervisorConfig.JailerPath, 228 BlockDeviceDriver: sconfig.HypervisorConfig.BlockDeviceDriver, 229 HypervisorMachineType: sconfig.HypervisorConfig.HypervisorMachineType, 230 MemoryPath: sconfig.HypervisorConfig.MemoryPath, 231 DevicesStatePath: sconfig.HypervisorConfig.DevicesStatePath, 232 EntropySource: sconfig.HypervisorConfig.EntropySource, 233 SharedFS: sconfig.HypervisorConfig.SharedFS, 234 VirtioFSDaemon: sconfig.HypervisorConfig.VirtioFSDaemon, 235 VirtioFSCache: sconfig.HypervisorConfig.VirtioFSCache, 236 VirtioFSExtraArgs: sconfig.HypervisorConfig.VirtioFSExtraArgs[:], 237 BlockDeviceCacheSet: sconfig.HypervisorConfig.BlockDeviceCacheSet, 238 BlockDeviceCacheDirect: sconfig.HypervisorConfig.BlockDeviceCacheDirect, 239 BlockDeviceCacheNoflush: sconfig.HypervisorConfig.BlockDeviceCacheNoflush, 240 DisableBlockDeviceUse: sconfig.HypervisorConfig.DisableBlockDeviceUse, 241 EnableIOThreads: sconfig.HypervisorConfig.EnableIOThreads, 242 Debug: sconfig.HypervisorConfig.Debug, 243 MemPrealloc: sconfig.HypervisorConfig.MemPrealloc, 244 HugePages: sconfig.HypervisorConfig.HugePages, 245 FileBackedMemRootDir: sconfig.HypervisorConfig.FileBackedMemRootDir, 246 Realtime: sconfig.HypervisorConfig.Realtime, 247 Mlock: sconfig.HypervisorConfig.Mlock, 248 DisableNestingChecks: sconfig.HypervisorConfig.DisableNestingChecks, 249 UseVSock: sconfig.HypervisorConfig.UseVSock, 250 DisableImageNvdimm: sconfig.HypervisorConfig.DisableImageNvdimm, 251 HotplugVFIOOnRootBus: sconfig.HypervisorConfig.HotplugVFIOOnRootBus, 252 PCIeRootPort: sconfig.HypervisorConfig.PCIeRootPort, 253 BootToBeTemplate: sconfig.HypervisorConfig.BootToBeTemplate, 254 BootFromTemplate: sconfig.HypervisorConfig.BootFromTemplate, 255 DisableVhostNet: sconfig.HypervisorConfig.DisableVhostNet, 256 EnableVhostUserStore: sconfig.HypervisorConfig.EnableVhostUserStore, 257 VhostUserStorePath: sconfig.HypervisorConfig.VhostUserStorePath, 258 GuestHookPath: sconfig.HypervisorConfig.GuestHookPath, 259 VMid: sconfig.HypervisorConfig.VMid, 260 } 261 262 if sconfig.AgentType == "kata" { 263 var sagent KataAgentConfig 264 err := mapstructure.Decode(sconfig.AgentConfig, &sagent) 265 if err != nil { 266 s.Logger().WithError(err).Error("internal error: KataAgentConfig failed to decode") 267 } else { 268 ss.Config.KataAgentConfig = &persistapi.KataAgentConfig{ 269 LongLiveConn: sagent.LongLiveConn, 270 UseVSock: sagent.UseVSock, 271 } 272 } 273 } 274 275 if sconfig.ShimType == "kataShim" { 276 var shim ShimConfig 277 err := mapstructure.Decode(sconfig.ShimConfig, &shim) 278 if err != nil { 279 s.Logger().WithError(err).Error("internal error: ShimConfig failed to decode") 280 } else { 281 ss.Config.KataShimConfig = &persistapi.ShimConfig{ 282 Path: shim.Path, 283 Debug: shim.Debug, 284 } 285 } 286 } 287 288 for _, contConf := range sconfig.Containers { 289 ss.Config.ContainerConfigs = append(ss.Config.ContainerConfigs, persistapi.ContainerConfig{ 290 ID: contConf.ID, 291 Annotations: contConf.Annotations, 292 RootFs: contConf.RootFs.Target, 293 Resources: contConf.Resources, 294 }) 295 } 296 } 297 298 func (s *Sandbox) Save() error { 299 var ( 300 ss = persistapi.SandboxState{} 301 cs = make(map[string]persistapi.ContainerState) 302 ) 303 304 s.dumpVersion(&ss) 305 s.dumpState(&ss, cs) 306 s.dumpHypervisor(&ss) 307 s.dumpDevices(&ss, cs) 308 s.dumpProcess(cs) 309 s.dumpMounts(cs) 310 s.dumpAgent(&ss) 311 s.dumpNetwork(&ss) 312 s.dumpConfig(&ss) 313 314 if err := s.newStore.ToDisk(ss, cs); err != nil { 315 return err 316 } 317 318 return nil 319 } 320 321 func (s *Sandbox) loadState(ss persistapi.SandboxState) { 322 s.state.PersistVersion = ss.PersistVersion 323 s.state.GuestMemoryBlockSizeMB = ss.GuestMemoryBlockSizeMB 324 s.state.BlockIndexMap = ss.HypervisorState.BlockIndexMap 325 s.state.State = types.StateString(ss.State) 326 s.state.CgroupPath = ss.CgroupPath 327 s.state.CgroupPaths = ss.CgroupPaths 328 s.state.GuestMemoryHotplugProbe = ss.GuestMemoryHotplugProbe 329 } 330 331 func (c *Container) loadContState(cs persistapi.ContainerState) { 332 c.state = types.ContainerState{ 333 State: types.StateString(cs.State), 334 BlockDeviceID: cs.Rootfs.BlockDeviceID, 335 Fstype: cs.Rootfs.FsType, 336 CgroupPath: cs.CgroupPath, 337 } 338 } 339 340 func (s *Sandbox) loadHypervisor(hs persistapi.HypervisorState) { 341 s.hypervisor.load(hs) 342 } 343 344 func (s *Sandbox) loadAgent(as persistapi.AgentState) { 345 if s.agent != nil { 346 s.agent.load(as) 347 } 348 } 349 350 func (s *Sandbox) loadDevices(devStates []persistapi.DeviceState) { 351 s.devManager.LoadDevices(devStates) 352 } 353 354 func (c *Container) loadContDevices(cs persistapi.ContainerState) { 355 c.devices = nil 356 for _, dev := range cs.DeviceMaps { 357 c.devices = append(c.devices, ContainerDevice{ 358 ID: dev.ID, 359 ContainerPath: dev.ContainerPath, 360 FileMode: dev.FileMode, 361 UID: dev.UID, 362 GID: dev.GID, 363 }) 364 } 365 } 366 367 func (c *Container) loadContMounts(cs persistapi.ContainerState) { 368 c.mounts = nil 369 for _, m := range cs.Mounts { 370 c.mounts = append(c.mounts, Mount{ 371 Source: m.Source, 372 Destination: m.Destination, 373 Options: m.Options, 374 HostPath: m.HostPath, 375 ReadOnly: m.ReadOnly, 376 BlockDeviceID: m.BlockDeviceID, 377 }) 378 } 379 } 380 381 func (c *Container) loadContProcess(cs persistapi.ContainerState) { 382 c.process = Process{ 383 Token: cs.Process.Token, 384 Pid: cs.Process.Pid, 385 StartTime: cs.Process.StartTime, 386 } 387 } 388 389 func (s *Sandbox) loadNetwork(netInfo persistapi.NetworkInfo) { 390 s.networkNS = NetworkNamespace{ 391 NetNsPath: netInfo.NetNsPath, 392 NetmonPID: netInfo.NetmonPID, 393 NetNsCreated: netInfo.NetNsCreated, 394 } 395 396 for _, e := range netInfo.Endpoints { 397 var ep Endpoint 398 switch EndpointType(e.Type) { 399 case PhysicalEndpointType: 400 ep = &PhysicalEndpoint{} 401 case VethEndpointType: 402 ep = &VethEndpoint{} 403 case VhostUserEndpointType: 404 ep = &VhostUserEndpoint{} 405 case BridgedMacvlanEndpointType: 406 ep = &BridgedMacvlanEndpoint{} 407 case MacvtapEndpointType: 408 ep = &MacvtapEndpoint{} 409 case TapEndpointType: 410 ep = &TapEndpoint{} 411 case IPVlanEndpointType: 412 ep = &IPVlanEndpoint{} 413 default: 414 s.Logger().WithField("endpoint-type", e.Type).Error("unknown endpoint type") 415 continue 416 } 417 ep.load(e) 418 s.networkNS.Endpoints = append(s.networkNS.Endpoints, ep) 419 } 420 } 421 422 // Restore will restore sandbox data from persist file on disk 423 func (s *Sandbox) Restore() error { 424 ss, _, err := s.newStore.FromDisk(s.id) 425 if err != nil { 426 return err 427 } 428 429 s.loadState(ss) 430 s.loadHypervisor(ss.HypervisorState) 431 s.loadDevices(ss.Devices) 432 s.loadAgent(ss.AgentState) 433 s.loadNetwork(ss.Network) 434 return nil 435 } 436 437 // Restore will restore container data from persist file on disk 438 func (c *Container) Restore() error { 439 _, css, err := c.sandbox.newStore.FromDisk(c.sandbox.id) 440 if err != nil { 441 return err 442 } 443 444 cs, ok := css[c.id] 445 if !ok { 446 return errContainerPersistNotExist 447 } 448 449 c.loadContState(cs) 450 c.loadContDevices(cs) 451 c.loadContProcess(cs) 452 c.loadContMounts(cs) 453 return nil 454 } 455 456 func loadSandboxConfig(id string) (*SandboxConfig, error) { 457 store, err := persist.GetDriver() 458 if err != nil || store == nil { 459 return nil, errors.New("failed to get fs persist driver") 460 } 461 462 ss, _, err := store.FromDisk(id) 463 if err != nil { 464 return nil, err 465 } 466 467 savedConf := ss.Config 468 sconfig := &SandboxConfig{ 469 ID: id, 470 HypervisorType: HypervisorType(savedConf.HypervisorType), 471 AgentType: AgentType(savedConf.AgentType), 472 ProxyType: ProxyType(savedConf.ProxyType), 473 ProxyConfig: ProxyConfig{ 474 Path: savedConf.ProxyConfig.Path, 475 Debug: savedConf.ProxyConfig.Debug, 476 }, 477 ShimType: ShimType(savedConf.ShimType), 478 NetworkConfig: NetworkConfig{ 479 NetNSPath: savedConf.NetworkConfig.NetNSPath, 480 NetNsCreated: savedConf.NetworkConfig.NetNsCreated, 481 DisableNewNetNs: savedConf.NetworkConfig.DisableNewNetNs, 482 InterworkingModel: NetInterworkingModel(savedConf.NetworkConfig.InterworkingModel), 483 }, 484 485 ShmSize: savedConf.ShmSize, 486 SharePidNs: savedConf.SharePidNs, 487 Stateful: savedConf.Stateful, 488 SystemdCgroup: savedConf.SystemdCgroup, 489 SandboxCgroupOnly: savedConf.SandboxCgroupOnly, 490 DisableGuestSeccomp: savedConf.DisableGuestSeccomp, 491 Cgroups: savedConf.Cgroups, 492 } 493 494 for _, name := range savedConf.Experimental { 495 sconfig.Experimental = append(sconfig.Experimental, *exp.Get(name)) 496 } 497 498 hconf := savedConf.HypervisorConfig 499 sconfig.HypervisorConfig = HypervisorConfig{ 500 NumVCPUs: hconf.NumVCPUs, 501 DefaultMaxVCPUs: hconf.DefaultMaxVCPUs, 502 MemorySize: hconf.MemorySize, 503 DefaultBridges: hconf.DefaultBridges, 504 Msize9p: hconf.Msize9p, 505 MemSlots: hconf.MemSlots, 506 MemOffset: hconf.MemOffset, 507 VirtioMem: hconf.VirtioMem, 508 VirtioFSCacheSize: hconf.VirtioFSCacheSize, 509 KernelPath: hconf.KernelPath, 510 ImagePath: hconf.ImagePath, 511 InitrdPath: hconf.InitrdPath, 512 FirmwarePath: hconf.FirmwarePath, 513 MachineAccelerators: hconf.MachineAccelerators, 514 CPUFeatures: hconf.CPUFeatures, 515 HypervisorPath: hconf.HypervisorPath, 516 HypervisorCtlPath: hconf.HypervisorCtlPath, 517 JailerPath: hconf.JailerPath, 518 BlockDeviceDriver: hconf.BlockDeviceDriver, 519 HypervisorMachineType: hconf.HypervisorMachineType, 520 MemoryPath: hconf.MemoryPath, 521 DevicesStatePath: hconf.DevicesStatePath, 522 EntropySource: hconf.EntropySource, 523 SharedFS: hconf.SharedFS, 524 VirtioFSDaemon: hconf.VirtioFSDaemon, 525 VirtioFSCache: hconf.VirtioFSCache, 526 VirtioFSExtraArgs: hconf.VirtioFSExtraArgs[:], 527 BlockDeviceCacheSet: hconf.BlockDeviceCacheSet, 528 BlockDeviceCacheDirect: hconf.BlockDeviceCacheDirect, 529 BlockDeviceCacheNoflush: hconf.BlockDeviceCacheNoflush, 530 DisableBlockDeviceUse: hconf.DisableBlockDeviceUse, 531 EnableIOThreads: hconf.EnableIOThreads, 532 Debug: hconf.Debug, 533 MemPrealloc: hconf.MemPrealloc, 534 HugePages: hconf.HugePages, 535 FileBackedMemRootDir: hconf.FileBackedMemRootDir, 536 Realtime: hconf.Realtime, 537 Mlock: hconf.Mlock, 538 DisableNestingChecks: hconf.DisableNestingChecks, 539 UseVSock: hconf.UseVSock, 540 DisableImageNvdimm: hconf.DisableImageNvdimm, 541 HotplugVFIOOnRootBus: hconf.HotplugVFIOOnRootBus, 542 PCIeRootPort: hconf.PCIeRootPort, 543 BootToBeTemplate: hconf.BootToBeTemplate, 544 BootFromTemplate: hconf.BootFromTemplate, 545 DisableVhostNet: hconf.DisableVhostNet, 546 EnableVhostUserStore: hconf.EnableVhostUserStore, 547 VhostUserStorePath: hconf.VhostUserStorePath, 548 GuestHookPath: hconf.GuestHookPath, 549 VMid: hconf.VMid, 550 } 551 552 if savedConf.AgentType == "kata" { 553 sconfig.AgentConfig = KataAgentConfig{ 554 LongLiveConn: savedConf.KataAgentConfig.LongLiveConn, 555 UseVSock: savedConf.KataAgentConfig.UseVSock, 556 } 557 } 558 559 if savedConf.ShimType == "kataShim" { 560 sconfig.ShimConfig = ShimConfig{ 561 Path: savedConf.KataShimConfig.Path, 562 Debug: savedConf.KataShimConfig.Debug, 563 } 564 } 565 566 for _, contConf := range savedConf.ContainerConfigs { 567 sconfig.Containers = append(sconfig.Containers, ContainerConfig{ 568 ID: contConf.ID, 569 Annotations: contConf.Annotations, 570 Resources: contConf.Resources, 571 RootFs: RootFs{ 572 Target: contConf.RootFs, 573 }, 574 }) 575 } 576 return sconfig, nil 577 } 578 579 var oldstoreKey = struct{}{} 580 581 func loadSandboxConfigFromOldStore(ctx context.Context, sid string) (*SandboxConfig, context.Context, error) { 582 var config SandboxConfig 583 // We're bootstrapping 584 vcStore, err := store.NewVCSandboxStore(ctx, sid) 585 if err != nil { 586 return nil, ctx, err 587 } 588 589 if err := vcStore.Load(store.Configuration, &config); err != nil { 590 return nil, ctx, err 591 } 592 593 return &config, context.WithValue(ctx, oldstoreKey, true), nil 594 } 595 596 func useOldStore(ctx context.Context) bool { 597 v := ctx.Value(oldstoreKey) 598 return v != nil 599 }