Wire through CRI ContainerCheckpoint RPC

This connects the new CRI ContainerCheckpoint RPC to the existing
internal checkpoint functions. With this commit it is possible
to checkpoint a container in Kubernetes using the Forensic Container
Checkpointing KEP (#2008):

 # curl X POST "https://localhost:10250/checkpoint/namespace/podId/container"

Which will result in containerd creating a checkpoint in the location
specified by Kubernetes (usually /var/lib/kubelet/checkpoints).

This is a Linux only feature because CRIU only exists on Linux.

Rewritten with the help of Phil Estes.

Signed-off-by: Phil Estes <estesp@gmail.com>
Signed-off-by: Adrian Reber <areber@redhat.com>
This commit is contained in:
Adrian Reber 2022-05-20 06:38:33 +00:00
parent e53663cca7
commit f25770e48d
20 changed files with 4085 additions and 1 deletions

2
go.mod
View File

@ -8,6 +8,8 @@ require (
github.com/AdamKorcz/go-118-fuzz-build v0.0.0-20230306123547-8075edf89bb0
github.com/Microsoft/go-winio v0.6.1
github.com/Microsoft/hcsshim v0.12.0
github.com/checkpoint-restore/checkpointctl v1.1.0
github.com/checkpoint-restore/go-criu/v7 v7.0.0
github.com/containerd/btrfs/v2 v2.0.0
github.com/containerd/cgroups/v3 v3.0.3
github.com/containerd/console v1.0.4

4
go.sum
View File

@ -63,6 +63,10 @@ github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyY
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/checkpoint-restore/checkpointctl v1.1.0 h1:plS/2zBzbAXO6DH/H+TqD7ZGhz8iQVb+NLgsOJSTWaw=
github.com/checkpoint-restore/checkpointctl v1.1.0/go.mod h1:DtPd9M4bt/jdt+7DodFxm0lrzdevabk3cbni/FL4BY0=
github.com/checkpoint-restore/go-criu/v7 v7.0.0 h1:R4UF/njKOuq8ooG7naFGsCeKsjv5j+rIhgFgSSeC2KY=
github.com/checkpoint-restore/go-criu/v7 v7.0.0/go.mod h1:xD1v3cPww1QYpJR3+XTTdC8hYubPnptIPsT1daXhbr4=
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=

View File

@ -565,7 +565,7 @@ func (in *instrumentedService) CheckpointContainer(ctx context.Context, r *runti
}
}()
res, err = in.c.CheckpointContainer(ctx, r)
res, err = in.c.CheckpointContainer(ctrdutil.WithNamespace(ctx), r)
return res, errdefs.ToGRPC(err)
}

View File

@ -1,3 +1,5 @@
//go:build !linux
/*
Copyright The containerd Authors.
@ -18,6 +20,7 @@ package server
import (
"context"
"time"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
@ -25,5 +28,7 @@ import (
)
func (c *criService) CheckpointContainer(ctx context.Context, r *runtime.CheckpointContainerRequest) (res *runtime.CheckpointContainerResponse, err error) {
// The next line is just needed to make the linter happy.
containerCheckpointTimer.WithValues("no-runtime").UpdateSince(time.Now())
return nil, status.Errorf(codes.Unimplemented, "method CheckpointContainer not implemented")
}

View File

@ -0,0 +1,306 @@
//go:build linux
/*
Copyright The containerd Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package server
import (
"archive/tar"
"context"
"encoding/json"
"errors"
"fmt"
"io"
"os"
"path/filepath"
"strings"
"time"
crmetadata "github.com/checkpoint-restore/checkpointctl/lib"
"github.com/checkpoint-restore/go-criu/v7"
"github.com/containerd/containerd/v2/core/content"
"github.com/containerd/containerd/v2/core/images"
"github.com/containerd/containerd/v2/core/runtime/v2/runc/options"
"github.com/containerd/containerd/v2/pkg/archive"
"github.com/containerd/containerd/v2/plugins"
"github.com/containerd/containerd/v2/protobuf/proto"
ptypes "github.com/containerd/containerd/v2/protobuf/types"
"github.com/containerd/log"
v1 "github.com/opencontainers/image-spec/specs-go/v1"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
"github.com/containerd/containerd/v2/client"
runtime "k8s.io/cri-api/pkg/apis/runtime/v1"
)
// PodCriuVersion is the version of CRIU needed for
// checkpointing and restoring containers out of and into Pods.
const podCriuVersion = 31600
// CheckForCriu uses CRIU's go bindings to check if the CRIU
// binary exists and if it at least the version Podman needs.
func checkForCriu(version int) error {
c := criu.MakeCriu()
criuVersion, err := c.GetCriuVersion()
if err != nil {
return fmt.Errorf("failed to check for criu version: %w", err)
}
if criuVersion >= version {
return nil
}
return fmt.Errorf("checkpoint/restore requires at least CRIU %d, current version is %d", version, criuVersion)
}
func (c *criService) CheckpointContainer(ctx context.Context, r *runtime.CheckpointContainerRequest) (*runtime.CheckpointContainerResponse, error) {
start := time.Now()
if err := checkForCriu(podCriuVersion); err != nil {
// This is the wrong error message and needs to be adapted once
// Kubernetes (the e2e_node/checkpoint) test has been changed to
// handle too old or missing CRIU error messages.
log.G(ctx).WithError(err).Errorf("Failed to checkpoint container %q", r.GetContainerId())
return nil, status.Errorf(codes.Unimplemented, "method CheckpointContainer not implemented")
}
container, err := c.containerStore.Get(r.GetContainerId())
if err != nil {
return nil, fmt.Errorf("an error occurred when try to find container %q: %w", r.GetContainerId(), err)
}
state := container.Status.Get().State()
if state != runtime.ContainerState_CONTAINER_RUNNING {
return nil, fmt.Errorf(
"container %q is in %s state. only %s containers can be checkpointed",
r.GetContainerId(),
criContainerStateToString(state),
criContainerStateToString(runtime.ContainerState_CONTAINER_RUNNING),
)
}
imageRef := container.ImageRef
image, err := c.GetImage(imageRef)
if err != nil {
return nil, fmt.Errorf("getting container image failed: %w", err)
}
i, err := container.Container.Info(ctx)
if err != nil {
return nil, fmt.Errorf("get container info: %w", err)
}
configJSON, err := json.Marshal(&crmetadata.ContainerConfig{
ID: container.ID,
Name: container.Name,
RootfsImageName: func() string {
if len(image.References) > 0 {
return image.References[0]
}
return ""
}(),
RootfsImageRef: imageRef,
OCIRuntime: i.Runtime.Name,
RootfsImage: container.Config.GetImage().UserSpecifiedImage,
CheckpointedAt: time.Now(),
CreatedTime: i.CreatedAt,
})
if err != nil {
return nil, fmt.Errorf("generating container config JSON failed: %w", err)
}
task, err := container.Container.Task(ctx, nil)
if err != nil {
return nil, fmt.Errorf("failed to get task for container %q: %w", r.GetContainerId(), err)
}
img, err := task.Checkpoint(ctx, []client.CheckpointTaskOpts{withCheckpointOpts(i.Runtime.Name, c.getContainerRootDir(r.GetContainerId()))}...)
if err != nil {
return nil, fmt.Errorf("checkpointing container %q failed: %w", r.GetContainerId(), err)
}
// the checkpoint image has been provided as an index with manifests representing the tar of criu data, the rw layer, and the config
var (
index v1.Index
rawIndex []byte
targetDesc = img.Target()
contentStore = img.ContentStore()
)
rawIndex, err = content.ReadBlob(ctx, contentStore, targetDesc)
if err != nil {
return nil, fmt.Errorf("failed to retrieve checkpoint index blob from content store: %w", err)
}
if err = json.Unmarshal(rawIndex, &index); err != nil {
return nil, fmt.Errorf("failed to unmarshall blob into checkpoint data OCI index: %w", err)
}
cpPath := filepath.Join(c.getContainerRootDir(r.GetContainerId()), "ctrd-checkpoint")
if err := os.MkdirAll(cpPath, 0o700); err != nil {
return nil, err
}
defer os.RemoveAll(cpPath)
if err := os.WriteFile(filepath.Join(cpPath, crmetadata.ConfigDumpFile), configJSON, 0o600); err != nil {
return nil, err
}
// walk the manifests and pull out the blobs that we need to save in the checkpoint tarball:
// - the checkpoint criu data
// - the rw diff tarball
// - the spec blob
for _, manifest := range index.Manifests {
switch manifest.MediaType {
case images.MediaTypeContainerd1Checkpoint:
if err := writeCriuCheckpointData(ctx, contentStore, manifest, cpPath); err != nil {
return nil, fmt.Errorf("failed to copy CRIU checkpoint blob to checkpoint dir: %w", err)
}
case v1.MediaTypeImageLayerGzip:
if err := writeRootFsDiffTar(ctx, contentStore, manifest, cpPath); err != nil {
return nil, fmt.Errorf("failed to copy rw filesystem layer blob to checkpoint dir: %w", err)
}
case images.MediaTypeContainerd1CheckpointConfig:
if err := writeSpecDumpFile(ctx, contentStore, manifest, cpPath); err != nil {
return nil, fmt.Errorf("failed to copy container spec blob to checkpoint dir: %w", err)
}
default:
}
}
// write final tarball of all content
tar := archive.Diff(ctx, "", cpPath)
outFile, err := os.OpenFile(r.Location, os.O_RDWR|os.O_CREATE, 0600)
if err != nil {
return nil, err
}
defer outFile.Close()
_, err = io.Copy(outFile, tar)
if err != nil {
return nil, err
}
if err := tar.Close(); err != nil {
return nil, err
}
containerCheckpointTimer.WithValues(i.Runtime.Name).UpdateSince(start)
return &runtime.CheckpointContainerResponse{}, nil
}
func withCheckpointOpts(rt, rootDir string) client.CheckpointTaskOpts {
return func(r *client.CheckpointTaskInfo) error {
// Kubernetes currently supports checkpointing of container
// as part of the Forensic Container Checkpointing KEP.
// This implies that the container is never stopped
leaveRunning := true
switch rt {
case plugins.RuntimeRuncV2:
if r.Options == nil {
r.Options = &options.CheckpointOptions{}
}
opts, _ := r.Options.(*options.CheckpointOptions)
opts.Exit = !leaveRunning
opts.WorkPath = rootDir
}
return nil
}
}
func writeCriuCheckpointData(ctx context.Context, store content.Store, desc v1.Descriptor, cpPath string) error {
ra, err := store.ReaderAt(ctx, desc)
if err != nil {
return err
}
defer ra.Close()
checkpointDirectory := filepath.Join(cpPath, crmetadata.CheckpointDirectory)
// This is the criu data tarball. Let's unpack it
// and put it into the crmetadata.CheckpointDirectory directory.
if err := os.MkdirAll(checkpointDirectory, 0o700); err != nil {
return err
}
tr := tar.NewReader(content.NewReader(ra))
for {
header, err := tr.Next()
if err != nil {
if errors.Is(err, io.EOF) {
break
}
return err
}
if strings.Contains(header.Name, "..") {
return fmt.Errorf("found illegal string '..' in checkpoint archive")
}
destFile, err := os.Create(filepath.Join(checkpointDirectory, header.Name))
if err != nil {
return err
}
defer destFile.Close()
_, err = io.CopyN(destFile, tr, header.Size)
if err != nil {
return err
}
}
return nil
}
func writeRootFsDiffTar(ctx context.Context, store content.Store, desc v1.Descriptor, cpPath string) error {
ra, err := store.ReaderAt(ctx, desc)
if err != nil {
return err
}
defer ra.Close()
// the rw layer tarball
f, err := os.Create(filepath.Join(cpPath, crmetadata.RootFsDiffTar))
if err != nil {
return err
}
defer f.Close()
_, err = io.Copy(f, content.NewReader(ra))
if err != nil {
return err
}
return nil
}
func writeSpecDumpFile(ctx context.Context, store content.Store, desc v1.Descriptor, cpPath string) error {
// this is the container spec
f, err := os.Create(filepath.Join(cpPath, crmetadata.SpecDumpFile))
if err != nil {
return err
}
defer f.Close()
data, err := content.ReadBlob(ctx, store, desc)
if err != nil {
return err
}
var any ptypes.Any
if err := proto.Unmarshal(data, &any); err != nil {
return err
}
_, err = f.Write(any.Value)
if err != nil {
return err
}
return nil
}

View File

@ -35,6 +35,7 @@ var (
containerStopTimer metrics.LabeledTimer
containerStartTimer metrics.LabeledTimer
containerEventsDroppedCount metrics.Counter
containerCheckpointTimer metrics.LabeledTimer
networkPluginOperations metrics.LabeledCounter
networkPluginOperationsErrors metrics.LabeledCounter
@ -59,6 +60,7 @@ func init() {
containerStopTimer = ns.NewLabeledTimer("container_stop", "time to stop a container", "runtime")
containerStartTimer = ns.NewLabeledTimer("container_start", "time to start a container", "runtime")
containerEventsDroppedCount = ns.NewCounter("container_events_dropped", "count container discarding event total from server start")
containerCheckpointTimer = ns.NewLabeledTimer("container_checkpoint", "time to checkpoint a container", "runtime")
networkPluginOperations = ns.NewLabeledCounter("network_plugin_operations_total", "cumulative number of network plugin operations by operation type", "operation_type")
networkPluginOperationsErrors = ns.NewLabeledCounter("network_plugin_operations_errors_total", "cumulative number of network plugin operations by operation type", "operation_type")

View File

@ -0,0 +1,201 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "{}"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright {yyyy} {name of copyright owner}
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

@ -0,0 +1,145 @@
// SPDX-License-Identifier: Apache-2.0
package metadata
import (
"encoding/json"
"fmt"
"os"
"path/filepath"
"time"
spec "github.com/opencontainers/runtime-spec/specs-go"
)
const (
// container archive
ConfigDumpFile = "config.dump"
SpecDumpFile = "spec.dump"
NetworkStatusFile = "network.status"
CheckpointDirectory = "checkpoint"
CheckpointVolumesDirectory = "volumes"
DevShmCheckpointTar = "devshm-checkpoint.tar"
RootFsDiffTar = "rootfs-diff.tar"
DeletedFilesFile = "deleted.files"
DumpLogFile = "dump.log"
RestoreLogFile = "restore.log"
// pod archive
PodOptionsFile = "pod.options"
PodDumpFile = "pod.dump"
// containerd only
StatusFile = "status"
// CRIU Images
PagesPrefix = "pages-"
AmdgpuPagesPrefix = "amdgpu-pages-"
)
// This is a reduced copy of what Podman uses to store checkpoint metadata
type ContainerConfig struct {
ID string `json:"id"`
Name string `json:"name"`
RootfsImage string `json:"rootfsImage,omitempty"`
RootfsImageRef string `json:"rootfsImageRef,omitempty"`
RootfsImageName string `json:"rootfsImageName,omitempty"`
OCIRuntime string `json:"runtime,omitempty"`
CreatedTime time.Time `json:"createdTime"`
CheckpointedAt time.Time `json:"checkpointedTime"`
RestoredAt time.Time `json:"restoredTime"`
Restored bool `json:"restored"`
}
type ContainerdStatus struct {
CreatedAt int64
StartedAt int64
FinishedAt int64
ExitCode int32
Pid uint32
Reason string
Message string
}
// This structure is used by the KubernetesContainerCheckpointMetadata structure
type KubernetesCheckpoint struct {
Archive string `json:"archive,omitempty"`
Size int64 `json:"size,omitempty"`
Timestamp int64 `json:"timestamp,omitempty"`
}
// This structure is the basis for Kubernetes to track how many checkpoints
// for a certain container have been created.
type KubernetesContainerCheckpointMetadata struct {
PodFullName string `json:"podFullName,omitempty"`
ContainerName string `json:"containerName,omitempty"`
TotalSize int64 `json:"totalSize,omitempty"`
Checkpoints []KubernetesCheckpoint `json:"checkpoints"`
}
func ReadContainerCheckpointSpecDump(checkpointDirectory string) (*spec.Spec, string, error) {
var specDump spec.Spec
specDumpFile, err := ReadJSONFile(&specDump, checkpointDirectory, SpecDumpFile)
return &specDump, specDumpFile, err
}
func ReadContainerCheckpointConfigDump(checkpointDirectory string) (*ContainerConfig, string, error) {
var containerConfig ContainerConfig
configDumpFile, err := ReadJSONFile(&containerConfig, checkpointDirectory, ConfigDumpFile)
return &containerConfig, configDumpFile, err
}
func ReadContainerCheckpointDeletedFiles(checkpointDirectory string) ([]string, string, error) {
var deletedFiles []string
deletedFilesFile, err := ReadJSONFile(&deletedFiles, checkpointDirectory, DeletedFilesFile)
return deletedFiles, deletedFilesFile, err
}
func ReadContainerCheckpointStatusFile(checkpointDirectory string) (*ContainerdStatus, string, error) {
var containerdStatus ContainerdStatus
statusFile, err := ReadJSONFile(&containerdStatus, checkpointDirectory, StatusFile)
return &containerdStatus, statusFile, err
}
// WriteJSONFile marshalls and writes the given data to a JSON file
func WriteJSONFile(v interface{}, dir, file string) (string, error) {
fileJSON, err := json.MarshalIndent(v, "", " ")
if err != nil {
return "", fmt.Errorf("error marshalling JSON: %w", err)
}
file = filepath.Join(dir, file)
if err := os.WriteFile(file, fileJSON, 0o600); err != nil {
return "", err
}
return file, nil
}
func ReadJSONFile(v interface{}, dir, file string) (string, error) {
file = filepath.Join(dir, file)
content, err := os.ReadFile(file)
if err != nil {
return "", err
}
if err = json.Unmarshal(content, v); err != nil {
return "", fmt.Errorf("failed to unmarshal %s: %w", file, err)
}
return file, nil
}
func ByteToString(b int64) string {
const unit = 1024
if b < unit {
return fmt.Sprintf("%d B", b)
}
div, exp := int64(unit), 0
for n := b / unit; n >= unit; n /= unit {
div *= unit
exp++
}
return fmt.Sprintf("%.1f %ciB",
float64(b)/float64(div), "KMGTPE"[exp])
}

View File

@ -0,0 +1,17 @@
test/test
test/test.coverage
test/piggie/piggie
test/phaul/phaul
test/phaul/phaul.coverage
test/loop/loop
test/mmapper/mmapper
test/crit/crit-test
test/crit/test-imgs
test/crit/crit-test.coverage
test/.coverage/
image
scripts/magic-gen/*.h
scripts/magic-gen/expected.go
scripts/magic-gen/output.go
crit/bin
crit/test-imgs/

View File

@ -0,0 +1,18 @@
linters:
presets:
- bugs
- performance
- unused
- format
disable:
- musttag
enable:
- whitespace
- misspell
- dupl
- gosimple
- stylecheck
linters-settings:
exhaustive:
default-signifies-exhaustive: true

201
vendor/github.com/checkpoint-restore/go-criu/v7/LICENSE generated vendored Normal file
View File

@ -0,0 +1,201 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "{}"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright {yyyy} {name of copyright owner}
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

@ -0,0 +1,4 @@
Adrian Reber <areber@redhat.com>
Kir Kolyshkin <kolyshkin@gmail.com>
Prajwal S N <prajwalnadig21@gmail.com>
Radostin Stoyanov <rstoyanov@fedoraproject.org>

View File

@ -0,0 +1,45 @@
SHELL = /bin/bash
GO ?= go
CC ?= gcc
all: build
lint:
golangci-lint run ./...
build: rpc/rpc.pb.go stats/stats.pb.go
$(GO) build -v ./...
# Build crit binary
$(MAKE) -C crit bin/crit
test: build
$(MAKE) -C test
coverage:
$(MAKE) -C test coverage
codecov:
$(MAKE) -C test codecov
rpc/rpc.proto:
curl -sSL https://raw.githubusercontent.com/checkpoint-restore/criu/master/images/rpc.proto -o $@
rpc/rpc.pb.go: rpc/rpc.proto
protoc --go_out=. --go_opt=M$^=rpc/ $^
stats/stats.proto:
curl -sSL https://raw.githubusercontent.com/checkpoint-restore/criu/master/images/stats.proto -o $@
stats/stats.pb.go: stats/stats.proto
protoc --go_out=. --go_opt=M$^=stats/ $^
vendor:
$(GO) mod tidy
$(GO) mod vendor
$(GO) mod verify
clean:
$(MAKE) -C crit/ clean
$(MAKE) -C test/ clean
.PHONY: build test lint vendor coverage codecov clean

View File

@ -0,0 +1,111 @@
<!-- markdownlint-configure-file { "no-hard-tabs": { "code_blocks": false } } -->
# go-criu -- Go bindings for CRIU
[![test](https://github.com/checkpoint-restore/go-criu/workflows/ci/badge.svg?branch=master)](https://github.com/checkpoint-restore/go-criu/actions?query=workflow%3Aci)
[![verify](https://github.com/checkpoint-restore/go-criu/workflows/verify/badge.svg?branch=master)](https://github.com/checkpoint-restore/go-criu/actions?query=workflow%3Averify)
[![Go Reference](https://pkg.go.dev/badge/github.com/checkpoint-restore/go-criu.svg)](https://pkg.go.dev/github.com/checkpoint-restore/go-criu)
This repository provides Go bindings for [CRIU](https://criu.org/).
The code is based on the Go-based PHaul implementation from the CRIU repository.
For easier inclusion into other Go projects, the CRIU Go bindings have been
moved to this repository.
## CRIU
The Go bindings provide an easy way to use the CRIU RPC calls from Go without
the need to set up all the infrastructure to make the actual RPC connection to CRIU.
The following example would print the version of CRIU:
```go
import (
"log"
"github.com/checkpoint-restore/go-criu/v7"
)
func main() {
c := criu.MakeCriu()
version, err := c.GetCriuVersion()
if err != nil {
log.Fatalln(err)
}
log.Println(version)
}
```
or to just check if at least a certain CRIU version is installed:
```go
c := criu.MakeCriu()
result, err := c.IsCriuAtLeast(31100)
```
## CRIT
The `crit` package provides bindings to decode, encode, and manipulate
CRIU image files natively within Go. It also provides a CLI tool similar
to the original CRIT Python tool. To get started with this, see the docs
at [CRIT (Go library)](https://criu.org/CRIT_%28Go_library%29).
## Releases
The first go-criu release was 3.11 based on CRIU 3.11. The initial plan
was to follow CRIU so that go-criu would carry the same version number as
CRIU.
As go-criu is imported in other projects and as Go modules are expected
to follow Semantic Versioning go-criu will also follow Semantic Versioning
starting with the 4.0.0 release.
The following table shows the relation between go-criu and criu versions:
| Major version | Latest release | CRIU version |
| -------------- | -------------- | ------------ |
| v7             | 7.0.0         | 3.18         |
| v6             | 6.3.0         | 3.17         |
| v5             | 5.3.0         | 3.16         |
| v5             | 5.0.0         | 3.15         |
| v4             | 4.1.0         | 3.14         |
## How to contribute
While bug fixes can first be identified via an "issue", that is not required.
It's ok to just open up a PR with the fix, but make sure you include the same
information you would have included in an issue - like how to reproduce it.
PRs for new features should include some background on what use cases the
new code is trying to address. When possible and when it makes sense, try to
break-up larger PRs into smaller ones - it's easier to review smaller
code changes. But only if those smaller ones make sense as stand-alone PRs.
Regardless of the type of PR, all PRs should include:
* well documented code changes
* additional testcases. Ideally, they should fail w/o your code change applied
* documentation changes
Squash your commits into logical pieces of work that might want to be reviewed
separate from the rest of the PRs. Ideally, each commit should implement a
single idea, and the PR branch should pass the tests at every commit. GitHub
makes it easy to review the cumulative effect of many commits; so, when in
doubt, use smaller commits.
PRs that fix issues should include a reference like `Closes #XXXX` in the
commit message so that github will automatically close the referenced issue
when the PR is merged.
Contributors must assert that they are in compliance with the [Developer
Certificate of Origin 1.1](http://developercertificate.org/). This is achieved
by adding a "Signed-off-by" line containing the contributor's name and e-mail
to every commit message. Your signature certifies that you wrote the patch or
otherwise have the right to pass it on as an open-source patch.
## License and copyright
Unless mentioned otherwise in a specific file's header, all code in
this project is released under the Apache 2.0 license.
The author of a change remains the copyright holder of their code
(no copyright assignment). The list of authors and contributors can be
retrieved from the git commit history and in some cases, the file headers.

View File

@ -0,0 +1,45 @@
package criu
import (
"fmt"
"github.com/checkpoint-restore/go-criu/v7/rpc"
)
// Feature checking in go-criu is based on the libcriu feature checking function.
// Feature checking allows the user to check if CRIU supports
// certain features. There are CRIU features which do not depend
// on the version of CRIU but on kernel features or architecture.
//
// One example is memory tracking. Memory tracking can be disabled
// in the kernel or there are architectures which do not support
// it (aarch64 for example). By using the feature check a libcriu
// user can easily query CRIU if a certain feature is available.
//
// The features which should be checked can be marked in the
// structure 'struct criu_feature_check'. Each structure member
// that is set to true will result in CRIU checking for the
// availability of that feature in the current combination of
// CRIU/kernel/architecture.
//
// Available features will be set to true when the function
// returns successfully. Missing features will be set to false.
func (c *Criu) FeatureCheck(features *rpc.CriuFeatures) (*rpc.CriuFeatures, error) {
resp, err := c.doSwrkWithResp(
rpc.CriuReqType_FEATURE_CHECK,
nil,
nil,
features,
)
if err != nil {
return nil, err
}
if resp.GetType() != rpc.CriuReqType_FEATURE_CHECK {
return nil, fmt.Errorf("unexpected CRIU RPC response")
}
return features, nil
}

264
vendor/github.com/checkpoint-restore/go-criu/v7/main.go generated vendored Normal file
View File

@ -0,0 +1,264 @@
package criu
import (
"errors"
"fmt"
"os"
"os/exec"
"strconv"
"syscall"
"github.com/checkpoint-restore/go-criu/v7/rpc"
"google.golang.org/protobuf/proto"
)
// Criu struct
type Criu struct {
swrkCmd *exec.Cmd
swrkSk *os.File
swrkPath string
}
// MakeCriu returns the Criu object required for most operations
func MakeCriu() *Criu {
return &Criu{
swrkPath: "criu",
}
}
// SetCriuPath allows setting the path to the CRIU binary
// if it is in a non standard location
func (c *Criu) SetCriuPath(path string) {
c.swrkPath = path
}
// Prepare sets up everything for the RPC communication to CRIU
func (c *Criu) Prepare() error {
fds, err := syscall.Socketpair(syscall.AF_LOCAL, syscall.SOCK_SEQPACKET, 0)
if err != nil {
return err
}
cln := os.NewFile(uintptr(fds[0]), "criu-xprt-cln")
syscall.CloseOnExec(fds[0])
srv := os.NewFile(uintptr(fds[1]), "criu-xprt-srv")
defer srv.Close()
args := []string{"swrk", strconv.Itoa(fds[1])}
// #nosec G204
cmd := exec.Command(c.swrkPath, args...)
err = cmd.Start()
if err != nil {
cln.Close()
return err
}
c.swrkCmd = cmd
c.swrkSk = cln
return nil
}
// Cleanup cleans up
func (c *Criu) Cleanup() {
if c.swrkCmd != nil {
c.swrkSk.Close()
c.swrkSk = nil
_ = c.swrkCmd.Wait()
c.swrkCmd = nil
}
}
func (c *Criu) sendAndRecv(reqB []byte) ([]byte, int, error) {
cln := c.swrkSk
_, err := cln.Write(reqB)
if err != nil {
return nil, 0, err
}
respB := make([]byte, 2*4096)
n, err := cln.Read(respB)
if err != nil {
return nil, 0, err
}
return respB, n, nil
}
func (c *Criu) doSwrk(reqType rpc.CriuReqType, opts *rpc.CriuOpts, nfy Notify) error {
resp, err := c.doSwrkWithResp(reqType, opts, nfy, nil)
if err != nil {
return err
}
respType := resp.GetType()
if respType != reqType {
return errors.New("unexpected CRIU RPC response")
}
return nil
}
func (c *Criu) doSwrkWithResp(reqType rpc.CriuReqType, opts *rpc.CriuOpts, nfy Notify, features *rpc.CriuFeatures) (*rpc.CriuResp, error) {
var resp *rpc.CriuResp
req := rpc.CriuReq{
Type: &reqType,
Opts: opts,
}
if nfy != nil {
opts.NotifyScripts = proto.Bool(true)
}
if features != nil {
req.Features = features
}
if c.swrkCmd == nil {
err := c.Prepare()
if err != nil {
return nil, err
}
defer c.Cleanup()
}
for {
reqB, err := proto.Marshal(&req)
if err != nil {
return nil, err
}
respB, respS, err := c.sendAndRecv(reqB)
if err != nil {
return nil, err
}
resp = &rpc.CriuResp{}
err = proto.Unmarshal(respB[:respS], resp)
if err != nil {
return nil, err
}
if !resp.GetSuccess() {
return resp, fmt.Errorf("operation failed (msg:%s err:%d)",
resp.GetCrErrmsg(), resp.GetCrErrno())
}
respType := resp.GetType()
if respType != rpc.CriuReqType_NOTIFY {
break
}
if nfy == nil {
return resp, errors.New("unexpected notify")
}
notify := resp.GetNotify()
switch notify.GetScript() {
case "pre-dump":
err = nfy.PreDump()
case "post-dump":
err = nfy.PostDump()
case "pre-restore":
err = nfy.PreRestore()
case "post-restore":
err = nfy.PostRestore(notify.GetPid())
case "network-lock":
err = nfy.NetworkLock()
case "network-unlock":
err = nfy.NetworkUnlock()
case "setup-namespaces":
err = nfy.SetupNamespaces(notify.GetPid())
case "post-setup-namespaces":
err = nfy.PostSetupNamespaces()
case "post-resume":
err = nfy.PostResume()
default:
err = nil
}
if err != nil {
return resp, err
}
req = rpc.CriuReq{
Type: &respType,
NotifySuccess: proto.Bool(true),
}
}
return resp, nil
}
// Dump dumps a process
func (c *Criu) Dump(opts *rpc.CriuOpts, nfy Notify) error {
return c.doSwrk(rpc.CriuReqType_DUMP, opts, nfy)
}
// Restore restores a process
func (c *Criu) Restore(opts *rpc.CriuOpts, nfy Notify) error {
return c.doSwrk(rpc.CriuReqType_RESTORE, opts, nfy)
}
// PreDump does a pre-dump
func (c *Criu) PreDump(opts *rpc.CriuOpts, nfy Notify) error {
return c.doSwrk(rpc.CriuReqType_PRE_DUMP, opts, nfy)
}
// StartPageServer starts the page server
func (c *Criu) StartPageServer(opts *rpc.CriuOpts) error {
return c.doSwrk(rpc.CriuReqType_PAGE_SERVER, opts, nil)
}
// StartPageServerChld starts the page server and returns PID and port
func (c *Criu) StartPageServerChld(opts *rpc.CriuOpts) (int, int, error) {
resp, err := c.doSwrkWithResp(rpc.CriuReqType_PAGE_SERVER_CHLD, opts, nil, nil)
if err != nil {
return 0, 0, err
}
return int(resp.Ps.GetPid()), int(resp.Ps.GetPort()), nil
}
// GetCriuVersion executes the VERSION RPC call and returns the version
// as an integer. Major * 10000 + Minor * 100 + SubLevel
func (c *Criu) GetCriuVersion() (int, error) {
resp, err := c.doSwrkWithResp(rpc.CriuReqType_VERSION, nil, nil, nil)
if err != nil {
return 0, err
}
if resp.GetType() != rpc.CriuReqType_VERSION {
return 0, fmt.Errorf("unexpected CRIU RPC response")
}
version := int(*resp.GetVersion().MajorNumber) * 10000
version += int(*resp.GetVersion().MinorNumber) * 100
if resp.GetVersion().Sublevel != nil {
version += int(*resp.GetVersion().Sublevel)
}
if resp.GetVersion().Gitid != nil {
// taken from runc: if it is a git release -> increase minor by 1
version -= (version % 100)
version += 100
}
return version, nil
}
// IsCriuAtLeast checks if the version is at least the same
// as the parameter version
func (c *Criu) IsCriuAtLeast(version int) (bool, error) {
criuVersion, err := c.GetCriuVersion()
if err != nil {
return false, err
}
if criuVersion >= version {
return true, nil
}
return false, nil
}

View File

@ -0,0 +1,62 @@
package criu
// Notify interface
type Notify interface {
PreDump() error
PostDump() error
PreRestore() error
PostRestore(pid int32) error
NetworkLock() error
NetworkUnlock() error
SetupNamespaces(pid int32) error
PostSetupNamespaces() error
PostResume() error
}
// NoNotify struct
type NoNotify struct{}
// PreDump NoNotify
func (c NoNotify) PreDump() error {
return nil
}
// PostDump NoNotify
func (c NoNotify) PostDump() error {
return nil
}
// PreRestore NoNotify
func (c NoNotify) PreRestore() error {
return nil
}
// PostRestore NoNotify
func (c NoNotify) PostRestore(pid int32) error {
return nil
}
// NetworkLock NoNotify
func (c NoNotify) NetworkLock() error {
return nil
}
// NetworkUnlock NoNotify
func (c NoNotify) NetworkUnlock() error {
return nil
}
// SetupNamespaces NoNotify
func (c NoNotify) SetupNamespaces(pid int32) error {
return nil
}
// PostSetupNamespaces NoNotify
func (c NoNotify) PostSetupNamespaces() error {
return nil
}
// PostResume NoNotify
func (c NoNotify) PostResume() error {
return nil
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,255 @@
// SPDX-License-Identifier: MIT
syntax = "proto2";
message criu_page_server_info {
optional string address = 1;
optional int32 port = 2;
optional int32 pid = 3;
optional int32 fd = 4;
}
message criu_veth_pair {
required string if_in = 1;
required string if_out = 2;
};
message ext_mount_map {
required string key = 1;
required string val = 2;
};
message join_namespace {
required string ns = 1;
required string ns_file = 2;
optional string extra_opt = 3;
}
message inherit_fd {
required string key = 1;
required int32 fd = 2;
};
message cgroup_root {
optional string ctrl = 1;
required string path = 2;
};
message unix_sk {
required uint32 inode = 1;
};
enum criu_cg_mode {
IGNORE = 0;
CG_NONE = 1;
PROPS = 2;
SOFT = 3;
FULL = 4;
STRICT = 5;
DEFAULT = 6;
};
enum criu_network_lock_method {
IPTABLES = 1;
NFTABLES = 2;
SKIP = 3;
};
enum criu_pre_dump_mode {
SPLICE = 1;
VM_READ = 2;
};
message criu_opts {
required int32 images_dir_fd = 1 [default = -1];
optional string images_dir = 68; /* used only if images_dir_fd == -1 */
optional int32 pid = 2; /* if not set on dump, will dump requesting process */
optional bool leave_running = 3;
optional bool ext_unix_sk = 4;
optional bool tcp_established = 5;
optional bool evasive_devices = 6;
optional bool shell_job = 7;
optional bool file_locks = 8;
optional int32 log_level = 9 [default = 2];
optional string log_file = 10; /* No subdirs are allowed. Consider using work-dir */
optional criu_page_server_info ps = 11;
optional bool notify_scripts = 12;
optional string root = 13;
optional string parent_img = 14;
optional bool track_mem = 15;
optional bool auto_dedup = 16;
optional int32 work_dir_fd = 17;
optional bool link_remap = 18;
repeated criu_veth_pair veths = 19; /* DEPRECATED, use external instead */
optional uint32 cpu_cap = 20 [default = 0xffffffff];
optional bool force_irmap = 21;
repeated string exec_cmd = 22;
repeated ext_mount_map ext_mnt = 23; /* DEPRECATED, use external instead */
optional bool manage_cgroups = 24; /* backward compatibility */
repeated cgroup_root cg_root = 25;
optional bool rst_sibling = 26; /* swrk only */
repeated inherit_fd inherit_fd = 27; /* swrk only */
optional bool auto_ext_mnt = 28;
optional bool ext_sharing = 29;
optional bool ext_masters = 30;
repeated string skip_mnt = 31;
repeated string enable_fs = 32;
repeated unix_sk unix_sk_ino = 33; /* DEPRECATED, use external instead */
optional criu_cg_mode manage_cgroups_mode = 34;
optional uint32 ghost_limit = 35 [default = 0x100000];
repeated string irmap_scan_paths = 36;
repeated string external = 37;
optional uint32 empty_ns = 38;
repeated join_namespace join_ns = 39;
optional string cgroup_props = 41;
optional string cgroup_props_file = 42;
repeated string cgroup_dump_controller = 43;
optional string freeze_cgroup = 44;
optional uint32 timeout = 45;
optional bool tcp_skip_in_flight = 46;
optional bool weak_sysctls = 47;
optional bool lazy_pages = 48;
optional int32 status_fd = 49;
optional bool orphan_pts_master = 50;
optional string config_file = 51;
optional bool tcp_close = 52;
optional string lsm_profile = 53;
optional string tls_cacert = 54;
optional string tls_cacrl = 55;
optional string tls_cert = 56;
optional string tls_key = 57;
optional bool tls = 58;
optional bool tls_no_cn_verify = 59;
optional string cgroup_yard = 60;
optional criu_pre_dump_mode pre_dump_mode = 61 [default = SPLICE];
optional int32 pidfd_store_sk = 62;
optional string lsm_mount_context = 63;
optional criu_network_lock_method network_lock = 64 [default = IPTABLES];
optional bool mntns_compat_mode = 65;
optional bool skip_file_rwx_check = 66;
optional bool unprivileged = 67;
optional bool leave_stopped = 69;
optional bool display_stats = 70;
optional bool log_to_stderr = 71;
/* optional bool check_mounts = 128; */
}
message criu_dump_resp {
optional bool restored = 1;
}
message criu_restore_resp {
required int32 pid = 1;
}
message criu_notify {
optional string script = 1;
optional int32 pid = 2;
}
enum criu_req_type {
EMPTY = 0;
DUMP = 1;
RESTORE = 2;
CHECK = 3;
PRE_DUMP = 4;
PAGE_SERVER = 5;
NOTIFY = 6;
CPUINFO_DUMP = 7;
CPUINFO_CHECK = 8;
FEATURE_CHECK = 9;
VERSION = 10;
WAIT_PID = 11;
PAGE_SERVER_CHLD = 12;
SINGLE_PRE_DUMP = 13;
}
/*
* List of features which can queried via
* CRIU_REQ_TYPE__FEATURE_CHECK
*/
message criu_features {
optional bool mem_track = 1;
optional bool lazy_pages = 2;
optional bool pidfd_store = 3;
}
/*
* Request -- each type corresponds to must-be-there
* request arguments of respective type
*/
message criu_req {
required criu_req_type type = 1;
optional criu_opts opts = 2;
optional bool notify_success = 3;
/*
* When set service won't close the connection but
* will wait for more req-s to appear. Works not
* for all request types.
*/
optional bool keep_open = 4;
/*
* 'features' can be used to query which features
* are supported by the installed criu/kernel
* via RPC.
*/
optional criu_features features = 5;
/* 'pid' is used for WAIT_PID */
optional uint32 pid = 6;
}
/*
* Response -- it states whether the request was served
* and additional request-specific information
*/
message criu_resp {
required criu_req_type type = 1;
required bool success = 2;
optional criu_dump_resp dump = 3;
optional criu_restore_resp restore = 4;
optional criu_notify notify = 5;
optional criu_page_server_info ps = 6;
optional int32 cr_errno = 7;
optional criu_features features = 8;
optional string cr_errmsg = 9;
optional criu_version version = 10;
optional int32 status = 11;
}
/* Answer for criu_req_type.VERSION requests */
message criu_version {
required int32 major_number = 1;
required int32 minor_number = 2;
optional string gitid = 3;
optional int32 sublevel = 4;
optional int32 extra = 5;
optional string name = 6;
}

7
vendor/modules.txt vendored
View File

@ -76,6 +76,13 @@ github.com/cenkalti/backoff/v4
# github.com/cespare/xxhash/v2 v2.2.0
## explicit; go 1.11
github.com/cespare/xxhash/v2
# github.com/checkpoint-restore/checkpointctl v1.1.0
## explicit; go 1.18
github.com/checkpoint-restore/checkpointctl/lib
# github.com/checkpoint-restore/go-criu/v7 v7.0.0
## explicit; go 1.18
github.com/checkpoint-restore/go-criu/v7
github.com/checkpoint-restore/go-criu/v7/rpc
# github.com/cilium/ebpf v0.11.0
## explicit; go 1.19
github.com/cilium/ebpf