Browse code

Allow providing a custom storage directory for docker checkpoints

Signed-off-by: boucher <rboucher@gmail.com>

boucher authored on 2016/09/20 01:01:16
Showing 24 changed files
... ...
@@ -5,6 +5,6 @@ import "github.com/docker/docker/api/types"
5 5
 // Backend for Checkpoint
6 6
 type Backend interface {
7 7
 	CheckpointCreate(container string, config types.CheckpointCreateOptions) error
8
-	CheckpointDelete(container string, checkpointID string) error
9
-	CheckpointList(container string) ([]types.Checkpoint, error)
8
+	CheckpointDelete(container string, config types.CheckpointDeleteOptions) error
9
+	CheckpointList(container string, config types.CheckpointListOptions) ([]types.Checkpoint, error)
10 10
 }
... ...
@@ -35,7 +35,10 @@ func (s *checkpointRouter) getContainerCheckpoints(ctx context.Context, w http.R
35 35
 		return err
36 36
 	}
37 37
 
38
-	checkpoints, err := s.backend.CheckpointList(vars["name"])
38
+	checkpoints, err := s.backend.CheckpointList(vars["name"], types.CheckpointListOptions{
39
+		CheckpointDir: r.Form.Get("dir"),
40
+	})
41
+
39 42
 	if err != nil {
40 43
 		return err
41 44
 	}
... ...
@@ -48,7 +51,11 @@ func (s *checkpointRouter) deleteContainerCheckpoint(ctx context.Context, w http
48 48
 		return err
49 49
 	}
50 50
 
51
-	err := s.backend.CheckpointDelete(vars["name"], vars["checkpoint"])
51
+	err := s.backend.CheckpointDelete(vars["name"], types.CheckpointDeleteOptions{
52
+		CheckpointDir: r.Form.Get("dir"),
53
+		CheckpointID:  vars["checkpoint"],
54
+	})
55
+
52 56
 	if err != nil {
53 57
 		return err
54 58
 	}
... ...
@@ -39,7 +39,7 @@ type stateBackend interface {
39 39
 	ContainerResize(name string, height, width int) error
40 40
 	ContainerRestart(name string, seconds *int) error
41 41
 	ContainerRm(name string, config *types.ContainerRmConfig) error
42
-	ContainerStart(name string, hostConfig *container.HostConfig, validateHostname bool, checkpoint string) error
42
+	ContainerStart(name string, hostConfig *container.HostConfig, validateHostname bool, checkpoint string, checkpointDir string) error
43 43
 	ContainerStop(name string, seconds *int) error
44 44
 	ContainerUnpause(name string) error
45 45
 	ContainerUpdate(name string, hostConfig *container.HostConfig, validateHostname bool) (types.ContainerUpdateResponse, error)
... ...
@@ -155,8 +155,9 @@ func (s *containerRouter) postContainersStart(ctx context.Context, w http.Respon
155 155
 	}
156 156
 
157 157
 	checkpoint := r.Form.Get("checkpoint")
158
+	checkpointDir := r.Form.Get("checkpoint-dir")
158 159
 	validateHostname := versions.GreaterThanOrEqualTo(version, "1.24")
159
-	if err := s.backend.ContainerStart(vars["name"], hostConfig, validateHostname, checkpoint); err != nil {
160
+	if err := s.backend.ContainerStart(vars["name"], hostConfig, validateHostname, checkpoint, checkpointDir); err != nil {
160 161
 		return err
161 162
 	}
162 163
 
... ...
@@ -12,8 +12,20 @@ import (
12 12
 
13 13
 // CheckpointCreateOptions holds parameters to create a checkpoint from a container
14 14
 type CheckpointCreateOptions struct {
15
-	CheckpointID string
16
-	Exit         bool
15
+	CheckpointID  string
16
+	CheckpointDir string
17
+	Exit          bool
18
+}
19
+
20
+// CheckpointListOptions holds parameters to list checkpoints for a container
21
+type CheckpointListOptions struct {
22
+	CheckpointDir string
23
+}
24
+
25
+// CheckpointDeleteOptions holds parameters to delete a checkpoint from a container
26
+type CheckpointDeleteOptions struct {
27
+	CheckpointID  string
28
+	CheckpointDir string
17 29
 }
18 30
 
19 31
 // ContainerAttachOptions holds parameters to attach to a container.
... ...
@@ -77,7 +89,8 @@ type ContainerRemoveOptions struct {
77 77
 
78 78
 // ContainerStartOptions holds parameters to start containers.
79 79
 type ContainerStartOptions struct {
80
-	CheckpointID string
80
+	CheckpointID  string
81
+	CheckpointDir string
81 82
 }
82 83
 
83 84
 // CopyToContainerOptions holds information
... ...
@@ -124,7 +124,7 @@ type Backend interface {
124 124
 	// ContainerKill stops the container execution abruptly.
125 125
 	ContainerKill(containerID string, sig uint64) error
126 126
 	// ContainerStart starts a new container
127
-	ContainerStart(containerID string, hostConfig *container.HostConfig, validateHostname bool, checkpoint string) error
127
+	ContainerStart(containerID string, hostConfig *container.HostConfig, validateHostname bool, checkpoint string, checkpointDir string) error
128 128
 	// ContainerWait stops processing until the given container is stopped.
129 129
 	ContainerWait(containerID string, timeout time.Duration) (int, error)
130 130
 	// ContainerUpdateCmdOnBuild updates container.Path and container.Args
... ...
@@ -537,7 +537,7 @@ func (b *Builder) run(cID string) (err error) {
537 537
 		}
538 538
 	}()
539 539
 
540
-	if err := b.docker.ContainerStart(cID, nil, true, ""); err != nil {
540
+	if err := b.docker.ContainerStart(cID, nil, true, "", ""); err != nil {
541 541
 		close(finished)
542 542
 		if cancelErr := <-cancelErrCh; cancelErr != nil {
543 543
 			logrus.Debugf("Build cancelled (%v) and got an error from ContainerStart: %v",
... ...
@@ -10,9 +10,10 @@ import (
10 10
 )
11 11
 
12 12
 type createOptions struct {
13
-	container    string
14
-	checkpoint   string
15
-	leaveRunning bool
13
+	container     string
14
+	checkpoint    string
15
+	checkpointDir string
16
+	leaveRunning  bool
16 17
 }
17 18
 
18 19
 func newCreateCommand(dockerCli *command.DockerCli) *cobra.Command {
... ...
@@ -31,6 +32,7 @@ func newCreateCommand(dockerCli *command.DockerCli) *cobra.Command {
31 31
 
32 32
 	flags := cmd.Flags()
33 33
 	flags.BoolVar(&opts.leaveRunning, "leave-running", false, "leave the container running after checkpoint")
34
+	flags.StringVarP(&opts.checkpointDir, "checkpoint-dir", "", "", "use a custom checkpoint storage directory")
34 35
 
35 36
 	return cmd
36 37
 }
... ...
@@ -39,8 +41,9 @@ func runCreate(dockerCli *command.DockerCli, opts createOptions) error {
39 39
 	client := dockerCli.Client()
40 40
 
41 41
 	checkpointOpts := types.CheckpointCreateOptions{
42
-		CheckpointID: opts.checkpoint,
43
-		Exit:         !opts.leaveRunning,
42
+		CheckpointID:  opts.checkpoint,
43
+		CheckpointDir: opts.checkpointDir,
44
+		Exit:          !opts.leaveRunning,
44 45
 	}
45 46
 
46 47
 	err := client.CheckpointCreate(context.Background(), opts.container, checkpointOpts)
... ...
@@ -6,27 +6,44 @@ import (
6 6
 
7 7
 	"golang.org/x/net/context"
8 8
 
9
+	"github.com/docker/docker/api/types"
9 10
 	"github.com/docker/docker/cli"
10 11
 	"github.com/docker/docker/cli/command"
11 12
 	"github.com/spf13/cobra"
12 13
 )
13 14
 
15
+type listOptions struct {
16
+	checkpointDir string
17
+}
18
+
14 19
 func newListCommand(dockerCli *command.DockerCli) *cobra.Command {
15
-	return &cobra.Command{
20
+	var opts listOptions
21
+
22
+	cmd := &cobra.Command{
16 23
 		Use:     "ls CONTAINER",
17 24
 		Aliases: []string{"list"},
18 25
 		Short:   "List checkpoints for a container",
19 26
 		Args:    cli.ExactArgs(1),
20 27
 		RunE: func(cmd *cobra.Command, args []string) error {
21
-			return runList(dockerCli, args[0])
28
+			return runList(dockerCli, args[0], opts)
22 29
 		},
23 30
 	}
31
+
32
+	flags := cmd.Flags()
33
+	flags.StringVarP(&opts.checkpointDir, "checkpoint-dir", "", "", "use a custom checkpoint storage directory")
34
+
35
+	return cmd
36
+
24 37
 }
25 38
 
26
-func runList(dockerCli *command.DockerCli, container string) error {
39
+func runList(dockerCli *command.DockerCli, container string, opts listOptions) error {
27 40
 	client := dockerCli.Client()
28 41
 
29
-	checkpoints, err := client.CheckpointList(context.Background(), container)
42
+	listOpts := types.CheckpointListOptions{
43
+		CheckpointDir: opts.checkpointDir,
44
+	}
45
+
46
+	checkpoints, err := client.CheckpointList(context.Background(), container, listOpts)
30 47
 	if err != nil {
31 48
 		return err
32 49
 	}
... ...
@@ -3,24 +3,42 @@ package checkpoint
3 3
 import (
4 4
 	"golang.org/x/net/context"
5 5
 
6
+	"github.com/docker/docker/api/types"
6 7
 	"github.com/docker/docker/cli"
7 8
 	"github.com/docker/docker/cli/command"
8 9
 	"github.com/spf13/cobra"
9 10
 )
10 11
 
12
+type removeOptions struct {
13
+	checkpointDir string
14
+}
15
+
11 16
 func newRemoveCommand(dockerCli *command.DockerCli) *cobra.Command {
12
-	return &cobra.Command{
17
+	var opts removeOptions
18
+
19
+	cmd := &cobra.Command{
13 20
 		Use:     "rm CONTAINER CHECKPOINT",
14 21
 		Aliases: []string{"remove"},
15 22
 		Short:   "Remove a checkpoint",
16 23
 		Args:    cli.ExactArgs(2),
17 24
 		RunE: func(cmd *cobra.Command, args []string) error {
18
-			return runRemove(dockerCli, args[0], args[1])
25
+			return runRemove(dockerCli, args[0], args[1], opts)
19 26
 		},
20 27
 	}
28
+
29
+	flags := cmd.Flags()
30
+	flags.StringVarP(&opts.checkpointDir, "checkpoint-dir", "", "", "use a custom checkpoint storage directory")
31
+
32
+	return cmd
21 33
 }
22 34
 
23
-func runRemove(dockerCli *command.DockerCli, container string, checkpoint string) error {
35
+func runRemove(dockerCli *command.DockerCli, container string, checkpoint string, opts removeOptions) error {
24 36
 	client := dockerCli.Client()
25
-	return client.CheckpointDelete(context.Background(), container, checkpoint)
37
+
38
+	removeOpts := types.CheckpointDeleteOptions{
39
+		CheckpointID:  checkpoint,
40
+		CheckpointDir: opts.checkpointDir,
41
+	}
42
+
43
+	return client.CheckpointDelete(context.Background(), container, removeOpts)
26 44
 }
... ...
@@ -17,10 +17,11 @@ import (
17 17
 )
18 18
 
19 19
 type startOptions struct {
20
-	attach     bool
21
-	openStdin  bool
22
-	detachKeys string
23
-	checkpoint string
20
+	attach        bool
21
+	openStdin     bool
22
+	detachKeys    string
23
+	checkpoint    string
24
+	checkpointDir string
24 25
 
25 26
 	containers []string
26 27
 }
... ...
@@ -46,6 +47,7 @@ func NewStartCommand(dockerCli *command.DockerCli) *cobra.Command {
46 46
 
47 47
 	if dockerCli.HasExperimental() {
48 48
 		flags.StringVar(&opts.checkpoint, "checkpoint", "", "Restore from this checkpoint")
49
+		flags.StringVar(&opts.checkpointDir, "checkpoint-dir", "", "Use a custom checkpoint storage directory")
49 50
 	}
50 51
 
51 52
 	return cmd
... ...
@@ -112,7 +114,8 @@ func runStart(dockerCli *command.DockerCli, opts *startOptions) error {
112 112
 		// no matter it's detached, removed on daemon side(--rm) or exit normally.
113 113
 		statusChan := waitExitOrRemoved(dockerCli, ctx, c.ID, c.HostConfig.AutoRemove)
114 114
 		startOptions := types.ContainerStartOptions{
115
-			CheckpointID: opts.checkpoint,
115
+			CheckpointID:  opts.checkpoint,
116
+			CheckpointDir: opts.checkpointDir,
116 117
 		}
117 118
 
118 119
 		// 4. Start the container.
... ...
@@ -145,7 +148,8 @@ func runStart(dockerCli *command.DockerCli, opts *startOptions) error {
145 145
 		}
146 146
 		container := opts.containers[0]
147 147
 		startOptions := types.ContainerStartOptions{
148
-			CheckpointID: opts.checkpoint,
148
+			CheckpointID:  opts.checkpoint,
149
+			CheckpointDir: opts.checkpointDir,
149 150
 		}
150 151
 		return dockerCli.Client().ContainerStart(ctx, container, startOptions)
151 152
 
... ...
@@ -1,12 +1,20 @@
1 1
 package client
2 2
 
3 3
 import (
4
+	"net/url"
5
+
6
+	"github.com/docker/docker/api/types"
4 7
 	"golang.org/x/net/context"
5 8
 )
6 9
 
7 10
 // CheckpointDelete deletes the checkpoint with the given name from the given container
8
-func (cli *Client) CheckpointDelete(ctx context.Context, containerID string, checkpointID string) error {
9
-	resp, err := cli.delete(ctx, "/containers/"+containerID+"/checkpoints/"+checkpointID, nil, nil)
11
+func (cli *Client) CheckpointDelete(ctx context.Context, containerID string, options types.CheckpointDeleteOptions) error {
12
+	query := url.Values{}
13
+	if options.CheckpointDir != "" {
14
+		query.Set("dir", options.CheckpointDir)
15
+	}
16
+
17
+	resp, err := cli.delete(ctx, "/containers/"+containerID+"/checkpoints/"+options.CheckpointID, query, nil)
10 18
 	ensureReaderClosed(resp)
11 19
 	return err
12 20
 }
... ...
@@ -8,6 +8,7 @@ import (
8 8
 	"strings"
9 9
 	"testing"
10 10
 
11
+	"github.com/docker/docker/api/types"
11 12
 	"golang.org/x/net/context"
12 13
 )
13 14
 
... ...
@@ -16,7 +17,10 @@ func TestCheckpointDeleteError(t *testing.T) {
16 16
 		client: newMockClient(errorMock(http.StatusInternalServerError, "Server error")),
17 17
 	}
18 18
 
19
-	err := client.CheckpointDelete(context.Background(), "container_id", "checkpoint_id")
19
+	err := client.CheckpointDelete(context.Background(), "container_id", types.CheckpointDeleteOptions{
20
+		CheckpointID: "checkpoint_id",
21
+	})
22
+
20 23
 	if err == nil || err.Error() != "Error response from daemon: Server error" {
21 24
 		t.Fatalf("expected a Server Error, got %v", err)
22 25
 	}
... ...
@@ -40,7 +44,10 @@ func TestCheckpointDelete(t *testing.T) {
40 40
 		}),
41 41
 	}
42 42
 
43
-	err := client.CheckpointDelete(context.Background(), "container_id", "checkpoint_id")
43
+	err := client.CheckpointDelete(context.Background(), "container_id", types.CheckpointDeleteOptions{
44
+		CheckpointID: "checkpoint_id",
45
+	})
46
+
44 47
 	if err != nil {
45 48
 		t.Fatal(err)
46 49
 	}
... ...
@@ -2,16 +2,22 @@ package client
2 2
 
3 3
 import (
4 4
 	"encoding/json"
5
+	"net/url"
5 6
 
6 7
 	"github.com/docker/docker/api/types"
7 8
 	"golang.org/x/net/context"
8 9
 )
9 10
 
10 11
 // CheckpointList returns the volumes configured in the docker host.
11
-func (cli *Client) CheckpointList(ctx context.Context, container string) ([]types.Checkpoint, error) {
12
+func (cli *Client) CheckpointList(ctx context.Context, container string, options types.CheckpointListOptions) ([]types.Checkpoint, error) {
12 13
 	var checkpoints []types.Checkpoint
13 14
 
14
-	resp, err := cli.get(ctx, "/containers/"+container+"/checkpoints", nil, nil)
15
+	query := url.Values{}
16
+	if options.CheckpointDir != "" {
17
+		query.Set("dir", options.CheckpointDir)
18
+	}
19
+
20
+	resp, err := cli.get(ctx, "/containers/"+container+"/checkpoints", query, nil)
15 21
 	if err != nil {
16 22
 		return checkpoints, err
17 23
 	}
... ...
@@ -18,7 +18,7 @@ func TestCheckpointListError(t *testing.T) {
18 18
 		client: newMockClient(errorMock(http.StatusInternalServerError, "Server error")),
19 19
 	}
20 20
 
21
-	_, err := client.CheckpointList(context.Background(), "container_id")
21
+	_, err := client.CheckpointList(context.Background(), "container_id", types.CheckpointListOptions{})
22 22
 	if err == nil || err.Error() != "Error response from daemon: Server error" {
23 23
 		t.Fatalf("expected a Server Error, got %v", err)
24 24
 	}
... ...
@@ -47,7 +47,7 @@ func TestCheckpointList(t *testing.T) {
47 47
 		}),
48 48
 	}
49 49
 
50
-	checkpoints, err := client.CheckpointList(context.Background(), "container_id")
50
+	checkpoints, err := client.CheckpointList(context.Background(), "container_id", types.CheckpointListOptions{})
51 51
 	if err != nil {
52 52
 		t.Fatal(err)
53 53
 	}
... ...
@@ -14,6 +14,9 @@ func (cli *Client) ContainerStart(ctx context.Context, containerID string, optio
14 14
 	if len(options.CheckpointID) != 0 {
15 15
 		query.Set("checkpoint", options.CheckpointID)
16 16
 	}
17
+	if len(options.CheckpointDir) != 0 {
18
+		query.Set("checkpoint-dir", options.CheckpointDir)
19
+	}
17 20
 
18 21
 	resp, err := cli.post(ctx, "/containers/"+containerID+"/start", query, nil, nil)
19 22
 	ensureReaderClosed(resp)
... ...
@@ -13,8 +13,8 @@ type apiClientExperimental interface {
13 13
 // CheckpointAPIClient defines API client methods for the checkpoints
14 14
 type CheckpointAPIClient interface {
15 15
 	CheckpointCreate(ctx context.Context, container string, options types.CheckpointCreateOptions) error
16
-	CheckpointDelete(ctx context.Context, container string, checkpointID string) error
17
-	CheckpointList(ctx context.Context, container string) ([]types.Checkpoint, error)
16
+	CheckpointDelete(ctx context.Context, container string, options types.CheckpointDeleteOptions) error
17
+	CheckpointList(ctx context.Context, container string, options types.CheckpointListOptions) ([]types.Checkpoint, error)
18 18
 }
19 19
 
20 20
 // PluginAPIClient defines API client methods for the plugins
... ...
@@ -21,7 +21,14 @@ func (daemon *Daemon) CheckpointCreate(name string, config types.CheckpointCreat
21 21
 		return fmt.Errorf("Container %s not running", name)
22 22
 	}
23 23
 
24
-	err = daemon.containerd.CreateCheckpoint(container.ID, config.CheckpointID, container.CheckpointDir(), config.Exit)
24
+	var checkpointDir string
25
+	if config.CheckpointDir != "" {
26
+		checkpointDir = config.CheckpointDir
27
+	} else {
28
+		checkpointDir = container.CheckpointDir()
29
+	}
30
+
31
+	err = daemon.containerd.CreateCheckpoint(container.ID, config.CheckpointID, checkpointDir, config.Exit)
25 32
 	if err != nil {
26 33
 		return fmt.Errorf("Cannot checkpoint container %s: %s", name, err)
27 34
 	}
... ...
@@ -32,18 +39,24 @@ func (daemon *Daemon) CheckpointCreate(name string, config types.CheckpointCreat
32 32
 }
33 33
 
34 34
 // CheckpointDelete deletes the specified checkpoint
35
-func (daemon *Daemon) CheckpointDelete(name string, checkpoint string) error {
35
+func (daemon *Daemon) CheckpointDelete(name string, config types.CheckpointDeleteOptions) error {
36 36
 	container, err := daemon.GetContainer(name)
37 37
 	if err != nil {
38 38
 		return err
39 39
 	}
40 40
 
41
-	checkpointDir := container.CheckpointDir()
42
-	return os.RemoveAll(filepath.Join(checkpointDir, checkpoint))
41
+	var checkpointDir string
42
+	if config.CheckpointDir != "" {
43
+		checkpointDir = config.CheckpointDir
44
+	} else {
45
+		checkpointDir = container.CheckpointDir()
46
+	}
47
+
48
+	return os.RemoveAll(filepath.Join(checkpointDir, config.CheckpointID))
43 49
 }
44 50
 
45 51
 // CheckpointList lists all checkpoints of the specified container
46
-func (daemon *Daemon) CheckpointList(name string) ([]types.Checkpoint, error) {
52
+func (daemon *Daemon) CheckpointList(name string, config types.CheckpointListOptions) ([]types.Checkpoint, error) {
47 53
 	var out []types.Checkpoint
48 54
 
49 55
 	container, err := daemon.GetContainer(name)
... ...
@@ -51,7 +64,13 @@ func (daemon *Daemon) CheckpointList(name string) ([]types.Checkpoint, error) {
51 51
 		return nil, err
52 52
 	}
53 53
 
54
-	checkpointDir := container.CheckpointDir()
54
+	var checkpointDir string
55
+	if config.CheckpointDir != "" {
56
+		checkpointDir = config.CheckpointDir
57
+	} else {
58
+		checkpointDir = container.CheckpointDir()
59
+	}
60
+
55 61
 	if err := os.MkdirAll(checkpointDir, 0755); err != nil {
56 62
 		return nil, err
57 63
 	}
... ...
@@ -24,7 +24,7 @@ type Backend interface {
24 24
 	SetupIngress(req clustertypes.NetworkCreateRequest, nodeIP string) error
25 25
 	PullImage(ctx context.Context, image, tag string, metaHeaders map[string][]string, authConfig *types.AuthConfig, outStream io.Writer) error
26 26
 	CreateManagedContainer(config types.ContainerCreateConfig, validateHostname bool) (types.ContainerCreateResponse, error)
27
-	ContainerStart(name string, hostConfig *container.HostConfig, validateHostname bool, checkpoint string) error
27
+	ContainerStart(name string, hostConfig *container.HostConfig, validateHostname bool, checkpoint string, checkpointDir string) error
28 28
 	ContainerStop(name string, seconds *int) error
29 29
 	ConnectContainerToNetwork(containerName, networkName string, endpointConfig *network.EndpointSettings) error
30 30
 	UpdateContainerServiceConfig(containerName string, serviceConfig *clustertypes.ServiceConfig) error
... ...
@@ -224,7 +224,7 @@ func (c *containerAdapter) create(ctx context.Context) error {
224 224
 func (c *containerAdapter) start(ctx context.Context) error {
225 225
 	version := httputils.VersionFromContext(ctx)
226 226
 	validateHostname := versions.GreaterThanOrEqualTo(version, "1.24")
227
-	return c.backend.ContainerStart(c.container.name(), nil, validateHostname, "")
227
+	return c.backend.ContainerStart(c.container.name(), nil, validateHostname, "", "")
228 228
 }
229 229
 
230 230
 func (c *containerAdapter) inspect(ctx context.Context) (types.ContainerJSON, error) {
... ...
@@ -305,7 +305,7 @@ func (daemon *Daemon) restore() error {
305 305
 
306 306
 			// Make sure networks are available before starting
307 307
 			daemon.waitForNetworks(c)
308
-			if err := daemon.containerStart(c, "", true); err != nil {
308
+			if err := daemon.containerStart(c, "", "", true); err != nil {
309 309
 				logrus.Errorf("Failed to start container %s: %s", c.ID, err)
310 310
 			}
311 311
 			close(chNotify)
... ...
@@ -372,7 +372,7 @@ func (daemon *Daemon) RestartSwarmContainers() {
372 372
 				group.Add(1)
373 373
 				go func(c *container.Container) {
374 374
 					defer group.Done()
375
-					if err := daemon.containerStart(c, "", true); err != nil {
375
+					if err := daemon.containerStart(c, "", "", true); err != nil {
376 376
 						logrus.Error(err)
377 377
 					}
378 378
 				}(c)
... ...
@@ -62,7 +62,7 @@ func (daemon *Daemon) StateChanged(id string, e libcontainerd.StateInfo) error {
62 62
 			go func() {
63 63
 				err := <-wait
64 64
 				if err == nil {
65
-					if err = daemon.containerStart(c, "", false); err != nil {
65
+					if err = daemon.containerStart(c, "", "", false); err != nil {
66 66
 						logrus.Debugf("failed to restart contianer: %+v", err)
67 67
 					}
68 68
 				}
... ...
@@ -61,7 +61,7 @@ func (daemon *Daemon) containerRestart(container *container.Container, seconds i
61 61
 		}
62 62
 	}
63 63
 
64
-	if err := daemon.containerStart(container, "", true); err != nil {
64
+	if err := daemon.containerStart(container, "", "", true); err != nil {
65 65
 		return err
66 66
 	}
67 67
 
... ...
@@ -19,7 +19,7 @@ import (
19 19
 )
20 20
 
21 21
 // ContainerStart starts a container.
22
-func (daemon *Daemon) ContainerStart(name string, hostConfig *containertypes.HostConfig, validateHostname bool, checkpoint string) error {
22
+func (daemon *Daemon) ContainerStart(name string, hostConfig *containertypes.HostConfig, validateHostname bool, checkpoint string, checkpointDir string) error {
23 23
 	if checkpoint != "" && !daemon.HasExperimental() {
24 24
 		return errors.NewBadRequestError(fmt.Errorf("checkpoint is only supported in experimental mode"))
25 25
 	}
... ...
@@ -82,19 +82,19 @@ func (daemon *Daemon) ContainerStart(name string, hostConfig *containertypes.Hos
82 82
 		return err
83 83
 	}
84 84
 
85
-	return daemon.containerStart(container, checkpoint, true)
85
+	return daemon.containerStart(container, checkpoint, checkpointDir, true)
86 86
 }
87 87
 
88 88
 // Start starts a container
89 89
 func (daemon *Daemon) Start(container *container.Container) error {
90
-	return daemon.containerStart(container, "", true)
90
+	return daemon.containerStart(container, "", "", true)
91 91
 }
92 92
 
93 93
 // containerStart prepares the container to run by setting up everything the
94 94
 // container needs, such as storage and networking, as well as links
95 95
 // between containers. The container is left waiting for a signal to
96 96
 // begin running.
97
-func (daemon *Daemon) containerStart(container *container.Container, checkpoint string, resetRestartManager bool) (err error) {
97
+func (daemon *Daemon) containerStart(container *container.Container, checkpoint string, checkpointDir string, resetRestartManager bool) (err error) {
98 98
 	start := time.Now()
99 99
 	container.Lock()
100 100
 	defer container.Unlock()
... ...
@@ -155,7 +155,11 @@ func (daemon *Daemon) containerStart(container *container.Container, checkpoint
155 155
 		container.ResetRestartManager(true)
156 156
 	}
157 157
 
158
-	if err := daemon.containerd.Create(container.ID, checkpoint, container.CheckpointDir(), *spec, container.InitializeStdio, createOptions...); err != nil {
158
+	if checkpointDir == "" {
159
+		checkpointDir = container.CheckpointDir()
160
+	}
161
+
162
+	if err := daemon.containerd.Create(container.ID, checkpoint, checkpointDir, *spec, container.InitializeStdio, createOptions...); err != nil {
159 163
 		errDesc := grpc.ErrorDesc(err)
160 164
 		logrus.Errorf("Create container failed with error: %s", errDesc)
161 165
 		// if we receive an internal error from the initial start of a container then lets