diff --git a/runsc/boot/BUILD b/runsc/boot/BUILD index ebe27a137d..2b7f293d13 100644 --- a/runsc/boot/BUILD +++ b/runsc/boot/BUILD @@ -132,6 +132,7 @@ go_library( "//runsc/profile", "//runsc/specutils", "//runsc/specutils/seccomp", + "//runsc/version", "@com_github_opencontainers_runtime_spec//specs-go:go_default_library", "@com_github_syndtr_gocapability//capability:go_default_library", "@org_golang_google_protobuf//proto:go_default_library", diff --git a/runsc/boot/autosave.go b/runsc/boot/autosave.go index d2c5e89073..87cfb463da 100644 --- a/runsc/boot/autosave.go +++ b/runsc/boot/autosave.go @@ -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.addVersionToCheckpoint() l.addContainerSpecsToCheckpoint() saveOpts := getSaveOpts(l, k, true /* isResume */) // Store the state file contents in a buffer for save-resume. @@ -75,6 +76,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.addVersionToCheckpoint() l.addContainerSpecsToCheckpoint() saveOpts := getSaveOpts(l, k, false /* isResume */) saveOpts.Destination = files[0] diff --git a/runsc/boot/loader.go b/runsc/boot/loader.go index 97ecbd4eb8..5ba8f3ff2c 100644 --- a/runsc/boot/loader.go +++ b/runsc/boot/loader.go @@ -82,6 +82,7 @@ import ( "gvisor.dev/gvisor/runsc/profile" "gvisor.dev/gvisor/runsc/specutils" "gvisor.dev/gvisor/runsc/specutils/seccomp" + "gvisor.dev/gvisor/runsc/version" // Top-level inet providers. "gvisor.dev/gvisor/pkg/sentry/socket/hostinet" @@ -369,6 +370,10 @@ const ( // containerSpecsKey is the key used to add and pop the container specs to the // kernel during save/restore. containerSpecsKey = "container_specs" + + // versionKey is the key used to add and pop runsc version to the kernel + // during save/restore. + versionKey = "runsc_version" ) func getRootCredentials(spec *specs.Spec, conf *config.Config, userNs *auth.UserNamespace) *auth.Credentials { @@ -1989,3 +1994,13 @@ func popContainerSpecsFromCheckpoint(k *kernel.Kernel) (map[string]*specs.Spec, } return oldSpecs, nil } + +// addVersionToCheckpoint adds the runsc version to the kernel. +func (l *Loader) addVersionToCheckpoint() { + l.k.AddStateToCheckpoint(versionKey, version.Version()) +} + +// popVersionFromCheckpoint pops the runsc version from the kernel. +func popVersionFromCheckpoint(k *kernel.Kernel) string { + return (k.PopCheckpointState(versionKey)).(string) +} diff --git a/runsc/boot/restore.go b/runsc/boot/restore.go index 1c0e28f791..434bbae364 100644 --- a/runsc/boot/restore.go +++ b/runsc/boot/restore.go @@ -46,6 +46,7 @@ import ( "gvisor.dev/gvisor/pkg/tcpip/stack" "gvisor.dev/gvisor/runsc/boot/pprof" "gvisor.dev/gvisor/runsc/config" + "gvisor.dev/gvisor/runsc/version" ) const ( @@ -350,7 +351,7 @@ func validateSpecForContainer(oldSpec, newSpec *specs.Spec, cName string) error } } - // TODO(b/359591006): Validate runsc version, Linux.Resources, Process.Capabilities and Annotations. + // TODO(b/359591006): Validate Linux.Resources, Process.Capabilities and Annotations. // TODO(b/359591006): Check other remaining fields for equality. return nil } @@ -464,6 +465,12 @@ func (r *restorer) restore(l *Loader) error { return err } + checkpointVersion := popVersionFromCheckpoint(l.k) + currentVersion := version.Version() + if checkpointVersion != currentVersion { + return fmt.Errorf("runsc version does not match across checkpoint restore, checkpoint: %v current: %v", checkpointVersion, currentVersion) + } + oldSpecs, err := popContainerSpecsFromCheckpoint(l.k) if err != nil { return err @@ -578,6 +585,9 @@ func (l *Loader) save(o *control.SaveOpts) (err error) { } o.Metadata["container_count"] = strconv.Itoa(l.containerCount()) + // Save runsc version. + l.addVersionToCheckpoint() + // Save container specs. l.addContainerSpecsToCheckpoint()