Преглед изворни кода

Allow providing a custom storage directory for docker checkpoints

Signed-off-by: boucher <rboucher@gmail.com>
boucher пре 8 година
родитељ
комит
bd7d51292c

+ 2 - 2
api/server/router/checkpoint/backend.go

@@ -5,6 +5,6 @@ import "github.com/docker/docker/api/types"
 // Backend for Checkpoint
 // Backend for Checkpoint
 type Backend interface {
 type Backend interface {
 	CheckpointCreate(container string, config types.CheckpointCreateOptions) error
 	CheckpointCreate(container string, config types.CheckpointCreateOptions) error
-	CheckpointDelete(container string, checkpointID string) error
-	CheckpointList(container string) ([]types.Checkpoint, error)
+	CheckpointDelete(container string, config types.CheckpointDeleteOptions) error
+	CheckpointList(container string, config types.CheckpointListOptions) ([]types.Checkpoint, error)
 }
 }

+ 9 - 2
api/server/router/checkpoint/checkpoint_routes.go

@@ -35,7 +35,10 @@ func (s *checkpointRouter) getContainerCheckpoints(ctx context.Context, w http.R
 		return err
 		return err
 	}
 	}
 
 
-	checkpoints, err := s.backend.CheckpointList(vars["name"])
+	checkpoints, err := s.backend.CheckpointList(vars["name"], types.CheckpointListOptions{
+		CheckpointDir: r.Form.Get("dir"),
+	})
+
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}
@@ -48,7 +51,11 @@ func (s *checkpointRouter) deleteContainerCheckpoint(ctx context.Context, w http
 		return err
 		return err
 	}
 	}
 
 
-	err := s.backend.CheckpointDelete(vars["name"], vars["checkpoint"])
+	err := s.backend.CheckpointDelete(vars["name"], types.CheckpointDeleteOptions{
+		CheckpointDir: r.Form.Get("dir"),
+		CheckpointID:  vars["checkpoint"],
+	})
+
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}

+ 1 - 1
api/server/router/container/backend.go

@@ -39,7 +39,7 @@ type stateBackend interface {
 	ContainerResize(name string, height, width int) error
 	ContainerResize(name string, height, width int) error
 	ContainerRestart(name string, seconds *int) error
 	ContainerRestart(name string, seconds *int) error
 	ContainerRm(name string, config *types.ContainerRmConfig) error
 	ContainerRm(name string, config *types.ContainerRmConfig) error
-	ContainerStart(name string, hostConfig *container.HostConfig, validateHostname bool, checkpoint string) error
+	ContainerStart(name string, hostConfig *container.HostConfig, validateHostname bool, checkpoint string, checkpointDir string) error
 	ContainerStop(name string, seconds *int) error
 	ContainerStop(name string, seconds *int) error
 	ContainerUnpause(name string) error
 	ContainerUnpause(name string) error
 	ContainerUpdate(name string, hostConfig *container.HostConfig, validateHostname bool) (types.ContainerUpdateResponse, error)
 	ContainerUpdate(name string, hostConfig *container.HostConfig, validateHostname bool) (types.ContainerUpdateResponse, error)

+ 2 - 1
api/server/router/container/container_routes.go

@@ -155,8 +155,9 @@ func (s *containerRouter) postContainersStart(ctx context.Context, w http.Respon
 	}
 	}
 
 
 	checkpoint := r.Form.Get("checkpoint")
 	checkpoint := r.Form.Get("checkpoint")
+	checkpointDir := r.Form.Get("checkpoint-dir")
 	validateHostname := versions.GreaterThanOrEqualTo(version, "1.24")
 	validateHostname := versions.GreaterThanOrEqualTo(version, "1.24")
-	if err := s.backend.ContainerStart(vars["name"], hostConfig, validateHostname, checkpoint); err != nil {
+	if err := s.backend.ContainerStart(vars["name"], hostConfig, validateHostname, checkpoint, checkpointDir); err != nil {
 		return err
 		return err
 	}
 	}
 
 

+ 16 - 3
api/types/client.go

@@ -12,8 +12,20 @@ import (
 
 
 // CheckpointCreateOptions holds parameters to create a checkpoint from a container
 // CheckpointCreateOptions holds parameters to create a checkpoint from a container
 type CheckpointCreateOptions struct {
 type CheckpointCreateOptions struct {
-	CheckpointID string
-	Exit         bool
+	CheckpointID  string
+	CheckpointDir string
+	Exit          bool
+}
+
+// CheckpointListOptions holds parameters to list checkpoints for a container
+type CheckpointListOptions struct {
+	CheckpointDir string
+}
+
+// CheckpointDeleteOptions holds parameters to delete a checkpoint from a container
+type CheckpointDeleteOptions struct {
+	CheckpointID  string
+	CheckpointDir string
 }
 }
 
 
 // ContainerAttachOptions holds parameters to attach to a container.
 // ContainerAttachOptions holds parameters to attach to a container.
@@ -77,7 +89,8 @@ type ContainerRemoveOptions struct {
 
 
 // ContainerStartOptions holds parameters to start containers.
 // ContainerStartOptions holds parameters to start containers.
 type ContainerStartOptions struct {
 type ContainerStartOptions struct {
-	CheckpointID string
+	CheckpointID  string
+	CheckpointDir string
 }
 }
 
 
 // CopyToContainerOptions holds information
 // CopyToContainerOptions holds information

+ 1 - 1
builder/builder.go

@@ -124,7 +124,7 @@ type Backend interface {
 	// ContainerKill stops the container execution abruptly.
 	// ContainerKill stops the container execution abruptly.
 	ContainerKill(containerID string, sig uint64) error
 	ContainerKill(containerID string, sig uint64) error
 	// ContainerStart starts a new container
 	// ContainerStart starts a new container
-	ContainerStart(containerID string, hostConfig *container.HostConfig, validateHostname bool, checkpoint string) error
+	ContainerStart(containerID string, hostConfig *container.HostConfig, validateHostname bool, checkpoint string, checkpointDir string) error
 	// ContainerWait stops processing until the given container is stopped.
 	// ContainerWait stops processing until the given container is stopped.
 	ContainerWait(containerID string, timeout time.Duration) (int, error)
 	ContainerWait(containerID string, timeout time.Duration) (int, error)
 	// ContainerUpdateCmdOnBuild updates container.Path and container.Args
 	// ContainerUpdateCmdOnBuild updates container.Path and container.Args

+ 1 - 1
builder/dockerfile/internals.go

@@ -537,7 +537,7 @@ func (b *Builder) run(cID string) (err error) {
 		}
 		}
 	}()
 	}()
 
 
-	if err := b.docker.ContainerStart(cID, nil, true, ""); err != nil {
+	if err := b.docker.ContainerStart(cID, nil, true, "", ""); err != nil {
 		close(finished)
 		close(finished)
 		if cancelErr := <-cancelErrCh; cancelErr != nil {
 		if cancelErr := <-cancelErrCh; cancelErr != nil {
 			logrus.Debugf("Build cancelled (%v) and got an error from ContainerStart: %v",
 			logrus.Debugf("Build cancelled (%v) and got an error from ContainerStart: %v",

+ 8 - 5
cli/command/checkpoint/create.go

@@ -10,9 +10,10 @@ import (
 )
 )
 
 
 type createOptions struct {
 type createOptions struct {
-	container    string
-	checkpoint   string
-	leaveRunning bool
+	container     string
+	checkpoint    string
+	checkpointDir string
+	leaveRunning  bool
 }
 }
 
 
 func newCreateCommand(dockerCli *command.DockerCli) *cobra.Command {
 func newCreateCommand(dockerCli *command.DockerCli) *cobra.Command {
@@ -31,6 +32,7 @@ func newCreateCommand(dockerCli *command.DockerCli) *cobra.Command {
 
 
 	flags := cmd.Flags()
 	flags := cmd.Flags()
 	flags.BoolVar(&opts.leaveRunning, "leave-running", false, "leave the container running after checkpoint")
 	flags.BoolVar(&opts.leaveRunning, "leave-running", false, "leave the container running after checkpoint")
+	flags.StringVarP(&opts.checkpointDir, "checkpoint-dir", "", "", "use a custom checkpoint storage directory")
 
 
 	return cmd
 	return cmd
 }
 }
@@ -39,8 +41,9 @@ func runCreate(dockerCli *command.DockerCli, opts createOptions) error {
 	client := dockerCli.Client()
 	client := dockerCli.Client()
 
 
 	checkpointOpts := types.CheckpointCreateOptions{
 	checkpointOpts := types.CheckpointCreateOptions{
-		CheckpointID: opts.checkpoint,
-		Exit:         !opts.leaveRunning,
+		CheckpointID:  opts.checkpoint,
+		CheckpointDir: opts.checkpointDir,
+		Exit:          !opts.leaveRunning,
 	}
 	}
 
 
 	err := client.CheckpointCreate(context.Background(), opts.container, checkpointOpts)
 	err := client.CheckpointCreate(context.Background(), opts.container, checkpointOpts)

+ 21 - 4
cli/command/checkpoint/list.go

@@ -6,27 +6,44 @@ import (
 
 
 	"golang.org/x/net/context"
 	"golang.org/x/net/context"
 
 
+	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/cli"
 	"github.com/docker/docker/cli"
 	"github.com/docker/docker/cli/command"
 	"github.com/docker/docker/cli/command"
 	"github.com/spf13/cobra"
 	"github.com/spf13/cobra"
 )
 )
 
 
+type listOptions struct {
+	checkpointDir string
+}
+
 func newListCommand(dockerCli *command.DockerCli) *cobra.Command {
 func newListCommand(dockerCli *command.DockerCli) *cobra.Command {
-	return &cobra.Command{
+	var opts listOptions
+
+	cmd := &cobra.Command{
 		Use:     "ls CONTAINER",
 		Use:     "ls CONTAINER",
 		Aliases: []string{"list"},
 		Aliases: []string{"list"},
 		Short:   "List checkpoints for a container",
 		Short:   "List checkpoints for a container",
 		Args:    cli.ExactArgs(1),
 		Args:    cli.ExactArgs(1),
 		RunE: func(cmd *cobra.Command, args []string) error {
 		RunE: func(cmd *cobra.Command, args []string) error {
-			return runList(dockerCli, args[0])
+			return runList(dockerCli, args[0], opts)
 		},
 		},
 	}
 	}
+
+	flags := cmd.Flags()
+	flags.StringVarP(&opts.checkpointDir, "checkpoint-dir", "", "", "use a custom checkpoint storage directory")
+
+	return cmd
+
 }
 }
 
 
-func runList(dockerCli *command.DockerCli, container string) error {
+func runList(dockerCli *command.DockerCli, container string, opts listOptions) error {
 	client := dockerCli.Client()
 	client := dockerCli.Client()
 
 
-	checkpoints, err := client.CheckpointList(context.Background(), container)
+	listOpts := types.CheckpointListOptions{
+		CheckpointDir: opts.checkpointDir,
+	}
+
+	checkpoints, err := client.CheckpointList(context.Background(), container, listOpts)
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}

+ 22 - 4
cli/command/checkpoint/remove.go

@@ -3,24 +3,42 @@ package checkpoint
 import (
 import (
 	"golang.org/x/net/context"
 	"golang.org/x/net/context"
 
 
+	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/cli"
 	"github.com/docker/docker/cli"
 	"github.com/docker/docker/cli/command"
 	"github.com/docker/docker/cli/command"
 	"github.com/spf13/cobra"
 	"github.com/spf13/cobra"
 )
 )
 
 
+type removeOptions struct {
+	checkpointDir string
+}
+
 func newRemoveCommand(dockerCli *command.DockerCli) *cobra.Command {
 func newRemoveCommand(dockerCli *command.DockerCli) *cobra.Command {
-	return &cobra.Command{
+	var opts removeOptions
+
+	cmd := &cobra.Command{
 		Use:     "rm CONTAINER CHECKPOINT",
 		Use:     "rm CONTAINER CHECKPOINT",
 		Aliases: []string{"remove"},
 		Aliases: []string{"remove"},
 		Short:   "Remove a checkpoint",
 		Short:   "Remove a checkpoint",
 		Args:    cli.ExactArgs(2),
 		Args:    cli.ExactArgs(2),
 		RunE: func(cmd *cobra.Command, args []string) error {
 		RunE: func(cmd *cobra.Command, args []string) error {
-			return runRemove(dockerCli, args[0], args[1])
+			return runRemove(dockerCli, args[0], args[1], opts)
 		},
 		},
 	}
 	}
+
+	flags := cmd.Flags()
+	flags.StringVarP(&opts.checkpointDir, "checkpoint-dir", "", "", "use a custom checkpoint storage directory")
+
+	return cmd
 }
 }
 
 
-func runRemove(dockerCli *command.DockerCli, container string, checkpoint string) error {
+func runRemove(dockerCli *command.DockerCli, container string, checkpoint string, opts removeOptions) error {
 	client := dockerCli.Client()
 	client := dockerCli.Client()
-	return client.CheckpointDelete(context.Background(), container, checkpoint)
+
+	removeOpts := types.CheckpointDeleteOptions{
+		CheckpointID:  checkpoint,
+		CheckpointDir: opts.checkpointDir,
+	}
+
+	return client.CheckpointDelete(context.Background(), container, removeOpts)
 }
 }

+ 10 - 6
cli/command/container/start.go

@@ -17,10 +17,11 @@ import (
 )
 )
 
 
 type startOptions struct {
 type startOptions struct {
-	attach     bool
-	openStdin  bool
-	detachKeys string
-	checkpoint string
+	attach        bool
+	openStdin     bool
+	detachKeys    string
+	checkpoint    string
+	checkpointDir string
 
 
 	containers []string
 	containers []string
 }
 }
@@ -46,6 +47,7 @@ func NewStartCommand(dockerCli *command.DockerCli) *cobra.Command {
 
 
 	if dockerCli.HasExperimental() {
 	if dockerCli.HasExperimental() {
 		flags.StringVar(&opts.checkpoint, "checkpoint", "", "Restore from this checkpoint")
 		flags.StringVar(&opts.checkpoint, "checkpoint", "", "Restore from this checkpoint")
+		flags.StringVar(&opts.checkpointDir, "checkpoint-dir", "", "Use a custom checkpoint storage directory")
 	}
 	}
 
 
 	return cmd
 	return cmd
@@ -112,7 +114,8 @@ func runStart(dockerCli *command.DockerCli, opts *startOptions) error {
 		// no matter it's detached, removed on daemon side(--rm) or exit normally.
 		// no matter it's detached, removed on daemon side(--rm) or exit normally.
 		statusChan := waitExitOrRemoved(dockerCli, ctx, c.ID, c.HostConfig.AutoRemove)
 		statusChan := waitExitOrRemoved(dockerCli, ctx, c.ID, c.HostConfig.AutoRemove)
 		startOptions := types.ContainerStartOptions{
 		startOptions := types.ContainerStartOptions{
-			CheckpointID: opts.checkpoint,
+			CheckpointID:  opts.checkpoint,
+			CheckpointDir: opts.checkpointDir,
 		}
 		}
 
 
 		// 4. Start the container.
 		// 4. Start the container.
@@ -145,7 +148,8 @@ func runStart(dockerCli *command.DockerCli, opts *startOptions) error {
 		}
 		}
 		container := opts.containers[0]
 		container := opts.containers[0]
 		startOptions := types.ContainerStartOptions{
 		startOptions := types.ContainerStartOptions{
-			CheckpointID: opts.checkpoint,
+			CheckpointID:  opts.checkpoint,
+			CheckpointDir: opts.checkpointDir,
 		}
 		}
 		return dockerCli.Client().ContainerStart(ctx, container, startOptions)
 		return dockerCli.Client().ContainerStart(ctx, container, startOptions)
 
 

+ 10 - 2
client/checkpoint_delete.go

@@ -1,12 +1,20 @@
 package client
 package client
 
 
 import (
 import (
+	"net/url"
+
+	"github.com/docker/docker/api/types"
 	"golang.org/x/net/context"
 	"golang.org/x/net/context"
 )
 )
 
 
 // CheckpointDelete deletes the checkpoint with the given name from the given container
 // CheckpointDelete deletes the checkpoint with the given name from the given container
-func (cli *Client) CheckpointDelete(ctx context.Context, containerID string, checkpointID string) error {
-	resp, err := cli.delete(ctx, "/containers/"+containerID+"/checkpoints/"+checkpointID, nil, nil)
+func (cli *Client) CheckpointDelete(ctx context.Context, containerID string, options types.CheckpointDeleteOptions) error {
+	query := url.Values{}
+	if options.CheckpointDir != "" {
+		query.Set("dir", options.CheckpointDir)
+	}
+
+	resp, err := cli.delete(ctx, "/containers/"+containerID+"/checkpoints/"+options.CheckpointID, query, nil)
 	ensureReaderClosed(resp)
 	ensureReaderClosed(resp)
 	return err
 	return err
 }
 }

+ 9 - 2
client/checkpoint_delete_test.go

@@ -8,6 +8,7 @@ import (
 	"strings"
 	"strings"
 	"testing"
 	"testing"
 
 
+	"github.com/docker/docker/api/types"
 	"golang.org/x/net/context"
 	"golang.org/x/net/context"
 )
 )
 
 
@@ -16,7 +17,10 @@ func TestCheckpointDeleteError(t *testing.T) {
 		client: newMockClient(errorMock(http.StatusInternalServerError, "Server error")),
 		client: newMockClient(errorMock(http.StatusInternalServerError, "Server error")),
 	}
 	}
 
 
-	err := client.CheckpointDelete(context.Background(), "container_id", "checkpoint_id")
+	err := client.CheckpointDelete(context.Background(), "container_id", types.CheckpointDeleteOptions{
+		CheckpointID: "checkpoint_id",
+	})
+
 	if err == nil || err.Error() != "Error response from daemon: Server error" {
 	if err == nil || err.Error() != "Error response from daemon: Server error" {
 		t.Fatalf("expected a Server Error, got %v", err)
 		t.Fatalf("expected a Server Error, got %v", err)
 	}
 	}
@@ -40,7 +44,10 @@ func TestCheckpointDelete(t *testing.T) {
 		}),
 		}),
 	}
 	}
 
 
-	err := client.CheckpointDelete(context.Background(), "container_id", "checkpoint_id")
+	err := client.CheckpointDelete(context.Background(), "container_id", types.CheckpointDeleteOptions{
+		CheckpointID: "checkpoint_id",
+	})
+
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}

+ 8 - 2
client/checkpoint_list.go

@@ -2,16 +2,22 @@ package client
 
 
 import (
 import (
 	"encoding/json"
 	"encoding/json"
+	"net/url"
 
 
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types"
 	"golang.org/x/net/context"
 	"golang.org/x/net/context"
 )
 )
 
 
 // CheckpointList returns the volumes configured in the docker host.
 // CheckpointList returns the volumes configured in the docker host.
-func (cli *Client) CheckpointList(ctx context.Context, container string) ([]types.Checkpoint, error) {
+func (cli *Client) CheckpointList(ctx context.Context, container string, options types.CheckpointListOptions) ([]types.Checkpoint, error) {
 	var checkpoints []types.Checkpoint
 	var checkpoints []types.Checkpoint
 
 
-	resp, err := cli.get(ctx, "/containers/"+container+"/checkpoints", nil, nil)
+	query := url.Values{}
+	if options.CheckpointDir != "" {
+		query.Set("dir", options.CheckpointDir)
+	}
+
+	resp, err := cli.get(ctx, "/containers/"+container+"/checkpoints", query, nil)
 	if err != nil {
 	if err != nil {
 		return checkpoints, err
 		return checkpoints, err
 	}
 	}

+ 2 - 2
client/checkpoint_list_test.go

@@ -18,7 +18,7 @@ func TestCheckpointListError(t *testing.T) {
 		client: newMockClient(errorMock(http.StatusInternalServerError, "Server error")),
 		client: newMockClient(errorMock(http.StatusInternalServerError, "Server error")),
 	}
 	}
 
 
-	_, err := client.CheckpointList(context.Background(), "container_id")
+	_, err := client.CheckpointList(context.Background(), "container_id", types.CheckpointListOptions{})
 	if err == nil || err.Error() != "Error response from daemon: Server error" {
 	if err == nil || err.Error() != "Error response from daemon: Server error" {
 		t.Fatalf("expected a Server Error, got %v", err)
 		t.Fatalf("expected a Server Error, got %v", err)
 	}
 	}
@@ -47,7 +47,7 @@ func TestCheckpointList(t *testing.T) {
 		}),
 		}),
 	}
 	}
 
 
-	checkpoints, err := client.CheckpointList(context.Background(), "container_id")
+	checkpoints, err := client.CheckpointList(context.Background(), "container_id", types.CheckpointListOptions{})
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}

+ 3 - 0
client/container_start.go

@@ -14,6 +14,9 @@ func (cli *Client) ContainerStart(ctx context.Context, containerID string, optio
 	if len(options.CheckpointID) != 0 {
 	if len(options.CheckpointID) != 0 {
 		query.Set("checkpoint", options.CheckpointID)
 		query.Set("checkpoint", options.CheckpointID)
 	}
 	}
+	if len(options.CheckpointDir) != 0 {
+		query.Set("checkpoint-dir", options.CheckpointDir)
+	}
 
 
 	resp, err := cli.post(ctx, "/containers/"+containerID+"/start", query, nil, nil)
 	resp, err := cli.post(ctx, "/containers/"+containerID+"/start", query, nil, nil)
 	ensureReaderClosed(resp)
 	ensureReaderClosed(resp)

+ 2 - 2
client/interface_experimental.go

@@ -13,8 +13,8 @@ type apiClientExperimental interface {
 // CheckpointAPIClient defines API client methods for the checkpoints
 // CheckpointAPIClient defines API client methods for the checkpoints
 type CheckpointAPIClient interface {
 type CheckpointAPIClient interface {
 	CheckpointCreate(ctx context.Context, container string, options types.CheckpointCreateOptions) error
 	CheckpointCreate(ctx context.Context, container string, options types.CheckpointCreateOptions) error
-	CheckpointDelete(ctx context.Context, container string, checkpointID string) error
-	CheckpointList(ctx context.Context, container string) ([]types.Checkpoint, error)
+	CheckpointDelete(ctx context.Context, container string, options types.CheckpointDeleteOptions) error
+	CheckpointList(ctx context.Context, container string, options types.CheckpointListOptions) ([]types.Checkpoint, error)
 }
 }
 
 
 // PluginAPIClient defines API client methods for the plugins
 // PluginAPIClient defines API client methods for the plugins

+ 25 - 6
daemon/checkpoint.go

@@ -21,7 +21,14 @@ func (daemon *Daemon) CheckpointCreate(name string, config types.CheckpointCreat
 		return fmt.Errorf("Container %s not running", name)
 		return fmt.Errorf("Container %s not running", name)
 	}
 	}
 
 
-	err = daemon.containerd.CreateCheckpoint(container.ID, config.CheckpointID, container.CheckpointDir(), config.Exit)
+	var checkpointDir string
+	if config.CheckpointDir != "" {
+		checkpointDir = config.CheckpointDir
+	} else {
+		checkpointDir = container.CheckpointDir()
+	}
+
+	err = daemon.containerd.CreateCheckpoint(container.ID, config.CheckpointID, checkpointDir, config.Exit)
 	if err != nil {
 	if err != nil {
 		return fmt.Errorf("Cannot checkpoint container %s: %s", name, err)
 		return fmt.Errorf("Cannot checkpoint container %s: %s", name, err)
 	}
 	}
@@ -32,18 +39,24 @@ func (daemon *Daemon) CheckpointCreate(name string, config types.CheckpointCreat
 }
 }
 
 
 // CheckpointDelete deletes the specified checkpoint
 // CheckpointDelete deletes the specified checkpoint
-func (daemon *Daemon) CheckpointDelete(name string, checkpoint string) error {
+func (daemon *Daemon) CheckpointDelete(name string, config types.CheckpointDeleteOptions) error {
 	container, err := daemon.GetContainer(name)
 	container, err := daemon.GetContainer(name)
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}
 
 
-	checkpointDir := container.CheckpointDir()
-	return os.RemoveAll(filepath.Join(checkpointDir, checkpoint))
+	var checkpointDir string
+	if config.CheckpointDir != "" {
+		checkpointDir = config.CheckpointDir
+	} else {
+		checkpointDir = container.CheckpointDir()
+	}
+
+	return os.RemoveAll(filepath.Join(checkpointDir, config.CheckpointID))
 }
 }
 
 
 // CheckpointList lists all checkpoints of the specified container
 // CheckpointList lists all checkpoints of the specified container
-func (daemon *Daemon) CheckpointList(name string) ([]types.Checkpoint, error) {
+func (daemon *Daemon) CheckpointList(name string, config types.CheckpointListOptions) ([]types.Checkpoint, error) {
 	var out []types.Checkpoint
 	var out []types.Checkpoint
 
 
 	container, err := daemon.GetContainer(name)
 	container, err := daemon.GetContainer(name)
@@ -51,7 +64,13 @@ func (daemon *Daemon) CheckpointList(name string) ([]types.Checkpoint, error) {
 		return nil, err
 		return nil, err
 	}
 	}
 
 
-	checkpointDir := container.CheckpointDir()
+	var checkpointDir string
+	if config.CheckpointDir != "" {
+		checkpointDir = config.CheckpointDir
+	} else {
+		checkpointDir = container.CheckpointDir()
+	}
+
 	if err := os.MkdirAll(checkpointDir, 0755); err != nil {
 	if err := os.MkdirAll(checkpointDir, 0755); err != nil {
 		return nil, err
 		return nil, err
 	}
 	}

+ 1 - 1
daemon/cluster/executor/backend.go

@@ -24,7 +24,7 @@ type Backend interface {
 	SetupIngress(req clustertypes.NetworkCreateRequest, nodeIP string) error
 	SetupIngress(req clustertypes.NetworkCreateRequest, nodeIP string) error
 	PullImage(ctx context.Context, image, tag string, metaHeaders map[string][]string, authConfig *types.AuthConfig, outStream io.Writer) error
 	PullImage(ctx context.Context, image, tag string, metaHeaders map[string][]string, authConfig *types.AuthConfig, outStream io.Writer) error
 	CreateManagedContainer(config types.ContainerCreateConfig, validateHostname bool) (types.ContainerCreateResponse, error)
 	CreateManagedContainer(config types.ContainerCreateConfig, validateHostname bool) (types.ContainerCreateResponse, error)
-	ContainerStart(name string, hostConfig *container.HostConfig, validateHostname bool, checkpoint string) error
+	ContainerStart(name string, hostConfig *container.HostConfig, validateHostname bool, checkpoint string, checkpointDir string) error
 	ContainerStop(name string, seconds *int) error
 	ContainerStop(name string, seconds *int) error
 	ConnectContainerToNetwork(containerName, networkName string, endpointConfig *network.EndpointSettings) error
 	ConnectContainerToNetwork(containerName, networkName string, endpointConfig *network.EndpointSettings) error
 	UpdateContainerServiceConfig(containerName string, serviceConfig *clustertypes.ServiceConfig) error
 	UpdateContainerServiceConfig(containerName string, serviceConfig *clustertypes.ServiceConfig) error

+ 1 - 1
daemon/cluster/executor/container/adapter.go

@@ -224,7 +224,7 @@ func (c *containerAdapter) create(ctx context.Context) error {
 func (c *containerAdapter) start(ctx context.Context) error {
 func (c *containerAdapter) start(ctx context.Context) error {
 	version := httputils.VersionFromContext(ctx)
 	version := httputils.VersionFromContext(ctx)
 	validateHostname := versions.GreaterThanOrEqualTo(version, "1.24")
 	validateHostname := versions.GreaterThanOrEqualTo(version, "1.24")
-	return c.backend.ContainerStart(c.container.name(), nil, validateHostname, "")
+	return c.backend.ContainerStart(c.container.name(), nil, validateHostname, "", "")
 }
 }
 
 
 func (c *containerAdapter) inspect(ctx context.Context) (types.ContainerJSON, error) {
 func (c *containerAdapter) inspect(ctx context.Context) (types.ContainerJSON, error) {

+ 2 - 2
daemon/daemon.go

@@ -305,7 +305,7 @@ func (daemon *Daemon) restore() error {
 
 
 			// Make sure networks are available before starting
 			// Make sure networks are available before starting
 			daemon.waitForNetworks(c)
 			daemon.waitForNetworks(c)
-			if err := daemon.containerStart(c, "", true); err != nil {
+			if err := daemon.containerStart(c, "", "", true); err != nil {
 				logrus.Errorf("Failed to start container %s: %s", c.ID, err)
 				logrus.Errorf("Failed to start container %s: %s", c.ID, err)
 			}
 			}
 			close(chNotify)
 			close(chNotify)
@@ -372,7 +372,7 @@ func (daemon *Daemon) RestartSwarmContainers() {
 				group.Add(1)
 				group.Add(1)
 				go func(c *container.Container) {
 				go func(c *container.Container) {
 					defer group.Done()
 					defer group.Done()
-					if err := daemon.containerStart(c, "", true); err != nil {
+					if err := daemon.containerStart(c, "", "", true); err != nil {
 						logrus.Error(err)
 						logrus.Error(err)
 					}
 					}
 				}(c)
 				}(c)

+ 1 - 1
daemon/monitor.go

@@ -62,7 +62,7 @@ func (daemon *Daemon) StateChanged(id string, e libcontainerd.StateInfo) error {
 			go func() {
 			go func() {
 				err := <-wait
 				err := <-wait
 				if err == nil {
 				if err == nil {
-					if err = daemon.containerStart(c, "", false); err != nil {
+					if err = daemon.containerStart(c, "", "", false); err != nil {
 						logrus.Debugf("failed to restart contianer: %+v", err)
 						logrus.Debugf("failed to restart contianer: %+v", err)
 					}
 					}
 				}
 				}

+ 1 - 1
daemon/restart.go

@@ -61,7 +61,7 @@ func (daemon *Daemon) containerRestart(container *container.Container, seconds i
 		}
 		}
 	}
 	}
 
 
-	if err := daemon.containerStart(container, "", true); err != nil {
+	if err := daemon.containerStart(container, "", "", true); err != nil {
 		return err
 		return err
 	}
 	}
 
 

+ 9 - 5
daemon/start.go

@@ -19,7 +19,7 @@ import (
 )
 )
 
 
 // ContainerStart starts a container.
 // ContainerStart starts a container.
-func (daemon *Daemon) ContainerStart(name string, hostConfig *containertypes.HostConfig, validateHostname bool, checkpoint string) error {
+func (daemon *Daemon) ContainerStart(name string, hostConfig *containertypes.HostConfig, validateHostname bool, checkpoint string, checkpointDir string) error {
 	if checkpoint != "" && !daemon.HasExperimental() {
 	if checkpoint != "" && !daemon.HasExperimental() {
 		return errors.NewBadRequestError(fmt.Errorf("checkpoint is only supported in experimental mode"))
 		return errors.NewBadRequestError(fmt.Errorf("checkpoint is only supported in experimental mode"))
 	}
 	}
@@ -82,19 +82,19 @@ func (daemon *Daemon) ContainerStart(name string, hostConfig *containertypes.Hos
 		return err
 		return err
 	}
 	}
 
 
-	return daemon.containerStart(container, checkpoint, true)
+	return daemon.containerStart(container, checkpoint, checkpointDir, true)
 }
 }
 
 
 // Start starts a container
 // Start starts a container
 func (daemon *Daemon) Start(container *container.Container) error {
 func (daemon *Daemon) Start(container *container.Container) error {
-	return daemon.containerStart(container, "", true)
+	return daemon.containerStart(container, "", "", true)
 }
 }
 
 
 // containerStart prepares the container to run by setting up everything the
 // containerStart prepares the container to run by setting up everything the
 // container needs, such as storage and networking, as well as links
 // container needs, such as storage and networking, as well as links
 // between containers. The container is left waiting for a signal to
 // between containers. The container is left waiting for a signal to
 // begin running.
 // begin running.
-func (daemon *Daemon) containerStart(container *container.Container, checkpoint string, resetRestartManager bool) (err error) {
+func (daemon *Daemon) containerStart(container *container.Container, checkpoint string, checkpointDir string, resetRestartManager bool) (err error) {
 	start := time.Now()
 	start := time.Now()
 	container.Lock()
 	container.Lock()
 	defer container.Unlock()
 	defer container.Unlock()
@@ -155,7 +155,11 @@ func (daemon *Daemon) containerStart(container *container.Container, checkpoint
 		container.ResetRestartManager(true)
 		container.ResetRestartManager(true)
 	}
 	}
 
 
-	if err := daemon.containerd.Create(container.ID, checkpoint, container.CheckpointDir(), *spec, container.InitializeStdio, createOptions...); err != nil {
+	if checkpointDir == "" {
+		checkpointDir = container.CheckpointDir()
+	}
+
+	if err := daemon.containerd.Create(container.ID, checkpoint, checkpointDir, *spec, container.InitializeStdio, createOptions...); err != nil {
 		errDesc := grpc.ErrorDesc(err)
 		errDesc := grpc.ErrorDesc(err)
 		logrus.Errorf("Create container failed with error: %s", errDesc)
 		logrus.Errorf("Create container failed with error: %s", errDesc)
 		// if we receive an internal error from the initial start of a container then lets
 		// if we receive an internal error from the initial start of a container then lets