daemon/checkpoint.go
d8fef66b
 package daemon
 
 import (
ddae20c0
 	"context"
d8fef66b
 	"encoding/json"
 	"fmt"
 	"io/ioutil"
 	"os"
 	"path/filepath"
 
 	"github.com/docker/docker/api/types"
22b24641
 	"github.com/docker/docker/daemon/names"
c90ec051
 )
 
 var (
22b24641
 	validCheckpointNameChars   = names.RestrictedNameChars
 	validCheckpointNamePattern = names.RestrictedNamePattern
d8fef66b
 )
 
d400518f
 // getCheckpointDir verifies checkpoint directory for create,remove, list options and checks if checkpoint already exists
ddae20c0
 func getCheckpointDir(checkDir, checkpointID, ctrName, ctrID, ctrCheckpointDir string, create bool) (string, error) {
d400518f
 	var checkpointDir string
 	var err2 error
 	if checkDir != "" {
e51aec99
 		checkpointDir = checkDir
d400518f
 	} else {
 		checkpointDir = ctrCheckpointDir
 	}
 	checkpointAbsDir := filepath.Join(checkpointDir, checkpointID)
 	stat, err := os.Stat(checkpointAbsDir)
 	if create {
 		switch {
 		case err == nil && stat.IsDir():
 			err2 = fmt.Errorf("checkpoint with name %s already exists for container %s", checkpointID, ctrName)
 		case err != nil && os.IsNotExist(err):
ddae20c0
 			err2 = os.MkdirAll(checkpointAbsDir, 0700)
d400518f
 		case err != nil:
 			err2 = err
 		case err == nil:
 			err2 = fmt.Errorf("%s exists and is not a directory", checkpointAbsDir)
 		}
 	} else {
 		switch {
 		case err != nil:
 			err2 = fmt.Errorf("checkpoint %s does not exists for container %s", checkpointID, ctrName)
 		case err == nil && stat.IsDir():
 			err2 = nil
 		case err == nil:
 			err2 = fmt.Errorf("%s exists and is not a directory", checkpointAbsDir)
 		}
 	}
ddae20c0
 	return checkpointAbsDir, err2
d400518f
 }
 
d8fef66b
 // CheckpointCreate checkpoints the process running in a container with CRIU
 func (daemon *Daemon) CheckpointCreate(name string, config types.CheckpointCreateOptions) error {
 	container, err := daemon.GetContainer(name)
 	if err != nil {
 		return err
 	}
 
 	if !container.IsRunning() {
 		return fmt.Errorf("Container %s not running", name)
 	}
 
ddae20c0
 	if container.Config.Tty {
 		return fmt.Errorf("checkpoint not support on containers with tty")
 	}
 
c90ec051
 	if !validCheckpointNamePattern.MatchString(config.CheckpointID) {
 		return fmt.Errorf("Invalid checkpoint ID (%s), only %s are allowed", config.CheckpointID, validCheckpointNameChars)
 	}
 
d400518f
 	checkpointDir, err := getCheckpointDir(config.CheckpointDir, config.CheckpointID, name, container.ID, container.CheckpointDir(), true)
 	if err != nil {
 		return fmt.Errorf("cannot checkpoint container %s: %s", name, err)
 	}
 
ddae20c0
 	err = daemon.containerd.CreateCheckpoint(context.Background(), container.ID, checkpointDir, config.Exit)
d8fef66b
 	if err != nil {
ddae20c0
 		os.RemoveAll(checkpointDir)
d8fef66b
 		return fmt.Errorf("Cannot checkpoint container %s: %s", name, err)
 	}
 
 	daemon.LogContainerEvent(container, "checkpoint")
 
 	return nil
 }
 
 // CheckpointDelete deletes the specified checkpoint
bd7d5129
 func (daemon *Daemon) CheckpointDelete(name string, config types.CheckpointDeleteOptions) error {
d8fef66b
 	container, err := daemon.GetContainer(name)
 	if err != nil {
 		return err
 	}
d400518f
 	checkpointDir, err := getCheckpointDir(config.CheckpointDir, config.CheckpointID, name, container.ID, container.CheckpointDir(), false)
 	if err == nil {
 		return os.RemoveAll(filepath.Join(checkpointDir, config.CheckpointID))
bd7d5129
 	}
d400518f
 	return err
d8fef66b
 }
 
9b4ba34d
 // CheckpointList lists all checkpoints of the specified container
bd7d5129
 func (daemon *Daemon) CheckpointList(name string, config types.CheckpointListOptions) ([]types.Checkpoint, error) {
9b4ba34d
 	var out []types.Checkpoint
d8fef66b
 
 	container, err := daemon.GetContainer(name)
 	if err != nil {
9b4ba34d
 		return nil, err
d8fef66b
 	}
 
c0d2ec1e
 	checkpointDir, err := getCheckpointDir(config.CheckpointDir, "", name, container.ID, container.CheckpointDir(), false)
130d1491
 	if err != nil {
 		return nil, err
 	}
bd7d5129
 
d8fef66b
 	if err := os.MkdirAll(checkpointDir, 0755); err != nil {
 		return nil, err
 	}
 
 	dirs, err := ioutil.ReadDir(checkpointDir)
 	if err != nil {
 		return nil, err
 	}
 
 	for _, d := range dirs {
 		if !d.IsDir() {
 			continue
 		}
 		path := filepath.Join(checkpointDir, d.Name(), "config.json")
 		data, err := ioutil.ReadFile(path)
 		if err != nil {
 			return nil, err
 		}
 		var cpt types.Checkpoint
 		if err := json.Unmarshal(data, &cpt); err != nil {
 			return nil, err
 		}
 		out = append(out, cpt)
 	}
 
 	return out, nil
 }