Store the container specs in the statefile for spec validation.

- Append all the container specs to the statefile during checkpoint.
- These specs are retrieved during restore and compare/validate with the new
specs passed during the restore.

PiperOrigin-RevId: 673550450
This commit is contained in:
Nayana Bidari
2024-09-11 15:06:04 -07:00
committed by gVisor bot
parent 64de876102
commit 504e98ccad
3 changed files with 64 additions and 2 deletions
+2
View File
@@ -56,6 +56,7 @@ func getSaveOpts(l *Loader, k *kernel.Kernel, isResume bool) state.SaveOpts {
func getTargetForSaveResume(l *Loader) func(k *kernel.Kernel) {
return func(k *kernel.Kernel) {
l.addContainerSpecsToCheckpoint()
saveOpts := getSaveOpts(l, k, true /* isResume */)
// Store the state file contents in a buffer for save-resume.
// There is no need to verify the state file, we just need the
@@ -74,6 +75,7 @@ func getTargetForSaveRestore(l *Loader, files []*fd.FD) func(k *kernel.Kernel) {
var once sync.Once
return func(k *kernel.Kernel) {
once.Do(func() {
l.addContainerSpecsToCheckpoint()
saveOpts := getSaveOpts(l, k, false /* isResume */)
saveOpts.Destination = files[0]
if len(files) == 3 {
+43 -2
View File
@@ -16,6 +16,7 @@
package boot
import (
"encoding/json"
"errors"
"fmt"
mrand "math/rand"
@@ -363,8 +364,16 @@ type Args struct {
SaveFDs []*fd.FD
}
// make sure stdioFDs are always the same on initial start and on restore
const startingStdioFD = 256
const (
// startingStdioFD is the starting stdioFD number used during sandbox
// start and restore. This makes sure the stdioFDs are always the same
// on initial start and on restore.
startingStdioFD = 256
// containerSpecsKey is the key used to add and pop the container specs to the
// kernel during save/restore.
containerSpecsKey = "container_specs"
)
func getRootCredentials(spec *specs.Spec, conf *config.Config, userNs *auth.UserNamespace) *auth.Credentials {
// Create capabilities.
@@ -1941,3 +1950,35 @@ func (l *Loader) containerRuntimeState(cid string) ContainerRuntimeState {
// Init process has stopped, but no one has called wait on it yet.
return RuntimeStateStopped
}
// addContainerSpecsToCheckpoint adds the container specs to the kernel.
func (l *Loader) addContainerSpecsToCheckpoint() {
l.mu.Lock()
s := l.containerSpecs
l.mu.Unlock()
specsMap := make(map[string][]byte)
for k, v := range s {
data, err := json.Marshal(v)
if err != nil {
log.Warningf("json marshal error for specs %v", err)
return
}
specsMap[k] = data
}
l.k.AddStateToCheckpoint(containerSpecsKey, specsMap)
}
// popContainerSpecsFromCheckpoint pops all the container specs from the kernel.
func popContainerSpecsFromCheckpoint(k *kernel.Kernel) (map[string]*specs.Spec, error) {
specsMap := (k.PopCheckpointState(containerSpecsKey)).(map[string][]byte)
oldSpecs := make(map[string]*specs.Spec)
for k, v := range specsMap {
var s specs.Spec
if err := json.Unmarshal(v, &s); err != nil {
return nil, fmt.Errorf("json unmarshal error for specs %v", err)
}
oldSpecs[k] = &s
}
return oldSpecs, nil
}
+19
View File
@@ -137,6 +137,14 @@ func createNetworkStackForRestore(l *Loader) (*stack.Stack, inet.Stack) {
return nil, hostinet.NewStack()
}
// Validate OCI specs before restoring the containers.
func validateSpecs(oldSpecs, newSpecs map[string]*specs.Spec) error {
if len(oldSpecs) != len(newSpecs) {
return fmt.Errorf("incorrect number of specs during checkpoint and restore")
}
return nil
}
func (r *restorer) restore(l *Loader) error {
log.Infof("Starting to restore %d containers", len(r.containers))
@@ -226,6 +234,14 @@ func (r *restorer) restore(l *Loader) error {
return err
}
oldSpecs, err := popContainerSpecsFromCheckpoint(l.k)
if err != nil {
return err
}
if err := validateSpecs(oldSpecs, l.containerSpecs); err != nil {
return err
}
// Since we have a new kernel we also must make a new watchdog.
dogOpts := watchdog.DefaultOpts
dogOpts.TaskTimeoutAction = l.root.conf.WatchdogAction
@@ -328,6 +344,9 @@ func (l *Loader) save(o *control.SaveOpts) (err error) {
}
o.Metadata["container_count"] = strconv.Itoa(l.containerCount())
// Save container specs.
l.addContainerSpecsToCheckpoint()
if err := preSaveImpl(l, o); err != nil {
return err
}