Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
112 changes: 109 additions & 3 deletions pkg/shim/v1/runsc/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ import (
"github.com/sirupsen/logrus"
"golang.org/x/sys/unix"
"gvisor.dev/gvisor/pkg/cleanup"
"gvisor.dev/gvisor/pkg/shim/v1/runtimeoptions/v14"
v14 "gvisor.dev/gvisor/pkg/shim/v1/runtimeoptions/v14"

"gvisor.dev/gvisor/pkg/shim/v1/extension"
"gvisor.dev/gvisor/pkg/shim/v1/proc"
Expand Down Expand Up @@ -324,11 +324,16 @@ func (s *runscService) Create(ctx context.Context, r *taskAPI.CreateTaskRequest)
// Convert from types.Mount to proc.Mount.
var mounts []proc.Mount
for _, m := range r.Rootfs {
// options := m.Options
options, err := s.overrideRootMountOptionsForOverlayNone(m.Options)
if err != nil {
return nil, err
}
mounts = append(mounts, proc.Mount{
Type: m.Type,
Source: m.Source,
Target: m.Target,
Options: m.Options,
Options: options,
})
}

Expand Down Expand Up @@ -609,7 +614,23 @@ func (s *runscService) CloseIO(ctx context.Context, r *taskAPI.CloseIORequest) (

// Checkpoint checkpoints the container.
func (s *runscService) Checkpoint(ctx context.Context, r *taskAPI.CheckpointTaskRequest) (*types.Empty, error) {
return empty, errdefs.ErrNotImplemented
if s.task == nil {
log.L.Debugf("Checkpoint error, id: %s: container not created", r.ID)
return nil, errdefs.ToGRPCf(errdefs.ErrFailedPrecondition, "container must be created")
}

opts := &runsccmd.CheckpointOpts{
ImagePath: r.Path,
}

err := s.task.Runtime().Checkpoint(ctx, r.ID, opts)
if err != nil {
log.L.Debugf("Checkpoint failed: %v", err)

return nil, err
}
log.L.Debugf("Checkpoint succeeded")
return empty, nil
}

// Restore restores the container.
Expand Down Expand Up @@ -908,6 +929,7 @@ func newInit(path, workDir, namespace string, platform stdio.Platform, r *proc.C
return nil, fmt.Errorf("update volume annotations: %w", err)
}
updated = setPodCgroup(spec) || updated
updated = removeOverlayRootfsMount(spec) || updated

if updated {
if err := utils.WriteSpec(r.Bundle, spec); err != nil {
Expand Down Expand Up @@ -973,3 +995,87 @@ func setPodCgroup(spec *specs.Spec) bool {
}
return false
}

// overrideRootMountOptionsForOverlayNone modifies mount options to use custom overlay directories
// when overlay none option is set.
func (s *runscService) overrideRootMountOptionsForOverlayNone(options []string) ([]string, error) {
log.L.Debugf("Overriding root mount options for overlay none")
spec, err := utils.ReadSpec(s.bundle)
if err != nil {
return nil, fmt.Errorf("failed to read spec: %w", err)
}
const rootfsOverlayAnnotationKey = "dev.gvisor.spec.rootfs.overlay"
isOverlay2None := spec.Annotations[rootfsOverlayAnnotationKey] == ""

if !isOverlay2None {
return options, nil
}

mountPathToUseForOverlay, err := getPersistentRootFSMountPath(spec)
if err != nil {
return options, nil
}
mountPathInHost := ""
for _, m := range spec.Mounts {
if m.Destination == mountPathToUseForOverlay {
mountPathInHost = m.Source
break
}
}

if mountPathInHost == "" {
log.L.Debugf("No mount found for %s, not overriding overlay none options", mountPathToUseForOverlay)
return options, nil
}

log.L.Debugf("Using mount path %s for overlay none", mountPathInHost)

modifiedOptions := make([]string, len(options))
for i, opt := range options {
if strings.HasPrefix(opt, "upperdir=") {
upperDirPath := mountPathInHost + "/upperdir"
if err := os.MkdirAll(upperDirPath, 0755); err != nil {
return nil, fmt.Errorf("failed to create upperdir %s: %w", upperDirPath, err)
}
modifiedOptions[i] = "upperdir=" + upperDirPath
} else if strings.HasPrefix(opt, "workdir=") {
workDirPath := mountPathInHost + "/workdir"
if err := os.MkdirAll(workDirPath, 0755); err != nil {
return nil, fmt.Errorf("failed to create workdir %s: %w", workDirPath, err)
}
modifiedOptions[i] = "workdir=" + workDirPath
} else {
modifiedOptions[i] = opt
}
}
return modifiedOptions, nil
}

func removeOverlayRootfsMount(spec *specs.Spec) bool {
var updated bool
var mounts []specs.Mount
mountPathToUseForOverlay, err := getPersistentRootFSMountPath(spec)
if err != nil {
return false
}
for _, m := range spec.Mounts {
if m.Destination == mountPathToUseForOverlay {
updated = true
continue
}
mounts = append(mounts, m)
}
if updated {
spec.Mounts = mounts
}
return updated
}

func getPersistentRootFSMountPath(spec *specs.Spec) (string, error) {
const persistentRootFSMountPathAnnotationKey = "dev.gvisor.persistent-rootfs.mount-path"
path := spec.Annotations[persistentRootFSMountPathAnnotationKey]
if path == "" {
return "", fmt.Errorf("persistent rootfs mount path annotation not found")
}
return path, nil
}
26 changes: 26 additions & 0 deletions pkg/shim/v1/runsccmd/runsc.go
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,32 @@ func (r *Runsc) Restore(context context.Context, id string, cio runc.IO, opts *R
return r.start(context, cio, r.command(context, append(args, id)...))
}

// CheckpointOpts is a set of options to runsc.Checkpoint().
type CheckpointOpts struct {
ImagePath string
LeaveRunning bool
}

func (o *CheckpointOpts) args() []string {
var out []string
if o.ImagePath != "" {
out = append(out, fmt.Sprintf("--image-path=%s", o.ImagePath))
}
if o.LeaveRunning {
out = append(out, "--leave-running")
}
return out
}

// Checkpoint checkpoints the container.
func (r *Runsc) Checkpoint(context context.Context, id string, opts *CheckpointOpts) error {
args := []string{"checkpoint"}
if opts != nil {
args = append(args, opts.args()...)
}
return r.runOrError(r.command(context, append(args, id)...))
}

type waitResult struct {
ID string `json:"id"`
ExitStatus int `json:"exitStatus"`
Expand Down