From bd7d51292c399edba5f339e6be55fac6c0811ff5 Mon Sep 17 00:00:00 2001 From: boucher Date: Mon, 19 Sep 2016 12:01:16 -0400 Subject: [PATCH] Allow providing a custom storage directory for docker checkpoints Signed-off-by: boucher --- api/server/router/checkpoint/backend.go | 4 +-- .../router/checkpoint/checkpoint_routes.go | 11 +++++-- api/server/router/container/backend.go | 2 +- .../router/container/container_routes.go | 3 +- api/types/client.go | 19 ++++++++++-- builder/builder.go | 2 +- builder/dockerfile/internals.go | 2 +- cli/command/checkpoint/create.go | 13 +++++--- cli/command/checkpoint/list.go | 25 ++++++++++++--- cli/command/checkpoint/remove.go | 26 +++++++++++++--- cli/command/container/start.go | 16 ++++++---- client/checkpoint_delete.go | 12 +++++-- client/checkpoint_delete_test.go | 11 +++++-- client/checkpoint_list.go | 10 ++++-- client/checkpoint_list_test.go | 4 +-- client/container_start.go | 3 ++ client/interface_experimental.go | 4 +-- daemon/checkpoint.go | 31 +++++++++++++++---- daemon/cluster/executor/backend.go | 2 +- daemon/cluster/executor/container/adapter.go | 2 +- daemon/daemon.go | 4 +-- daemon/monitor.go | 2 +- daemon/restart.go | 2 +- daemon/start.go | 14 ++++++--- 24 files changed, 167 insertions(+), 57 deletions(-) diff --git a/api/server/router/checkpoint/backend.go b/api/server/router/checkpoint/backend.go index e1b68f44cd..8810f88b72 100644 --- a/api/server/router/checkpoint/backend.go +++ b/api/server/router/checkpoint/backend.go @@ -5,6 +5,6 @@ import "github.com/docker/docker/api/types" // Backend for Checkpoint type Backend interface { 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) } diff --git a/api/server/router/checkpoint/checkpoint_routes.go b/api/server/router/checkpoint/checkpoint_routes.go index 35e9d26c7a..f988431191 100644 --- a/api/server/router/checkpoint/checkpoint_routes.go +++ b/api/server/router/checkpoint/checkpoint_routes.go @@ -35,7 +35,10 @@ func (s *checkpointRouter) getContainerCheckpoints(ctx context.Context, w http.R 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 { return err } @@ -48,7 +51,11 @@ func (s *checkpointRouter) deleteContainerCheckpoint(ctx context.Context, w http 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 { return err } diff --git a/api/server/router/container/backend.go b/api/server/router/container/backend.go index 2e8968d2e1..c81a9788d6 100644 --- a/api/server/router/container/backend.go +++ b/api/server/router/container/backend.go @@ -39,7 +39,7 @@ type stateBackend interface { ContainerResize(name string, height, width int) error ContainerRestart(name string, seconds *int) 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 ContainerUnpause(name string) error ContainerUpdate(name string, hostConfig *container.HostConfig, validateHostname bool) (types.ContainerUpdateResponse, error) diff --git a/api/server/router/container/container_routes.go b/api/server/router/container/container_routes.go index 001ad3a3ee..f721268ba8 100644 --- a/api/server/router/container/container_routes.go +++ b/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") + checkpointDir := r.Form.Get("checkpoint-dir") 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 } diff --git a/api/types/client.go b/api/types/client.go index 45a2b1f070..8815aba590 100644 --- a/api/types/client.go +++ b/api/types/client.go @@ -12,8 +12,20 @@ import ( // CheckpointCreateOptions holds parameters to create a checkpoint from a container 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. @@ -77,7 +89,8 @@ type ContainerRemoveOptions struct { // ContainerStartOptions holds parameters to start containers. type ContainerStartOptions struct { - CheckpointID string + CheckpointID string + CheckpointDir string } // CopyToContainerOptions holds information diff --git a/builder/builder.go b/builder/builder.go index 4400f5904c..0ed052731d 100644 --- a/builder/builder.go +++ b/builder/builder.go @@ -124,7 +124,7 @@ type Backend interface { // ContainerKill stops the container execution abruptly. ContainerKill(containerID string, sig uint64) error // 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(containerID string, timeout time.Duration) (int, error) // ContainerUpdateCmdOnBuild updates container.Path and container.Args diff --git a/builder/dockerfile/internals.go b/builder/dockerfile/internals.go index 796866cef2..cf1015ca46 100644 --- a/builder/dockerfile/internals.go +++ b/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) if cancelErr := <-cancelErrCh; cancelErr != nil { logrus.Debugf("Build cancelled (%v) and got an error from ContainerStart: %v", diff --git a/cli/command/checkpoint/create.go b/cli/command/checkpoint/create.go index d369718119..646901ccd6 100644 --- a/cli/command/checkpoint/create.go +++ b/cli/command/checkpoint/create.go @@ -10,9 +10,10 @@ import ( ) type createOptions struct { - container string - checkpoint string - leaveRunning bool + container string + checkpoint string + checkpointDir string + leaveRunning bool } func newCreateCommand(dockerCli *command.DockerCli) *cobra.Command { @@ -31,6 +32,7 @@ func newCreateCommand(dockerCli *command.DockerCli) *cobra.Command { flags := cmd.Flags() 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 } @@ -39,8 +41,9 @@ func runCreate(dockerCli *command.DockerCli, opts createOptions) error { client := dockerCli.Client() 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) diff --git a/cli/command/checkpoint/list.go b/cli/command/checkpoint/list.go index 7ba035890b..fef91a4ccd 100644 --- a/cli/command/checkpoint/list.go +++ b/cli/command/checkpoint/list.go @@ -6,27 +6,44 @@ import ( "golang.org/x/net/context" + "github.com/docker/docker/api/types" "github.com/docker/docker/cli" "github.com/docker/docker/cli/command" "github.com/spf13/cobra" ) +type listOptions struct { + checkpointDir string +} + func newListCommand(dockerCli *command.DockerCli) *cobra.Command { - return &cobra.Command{ + var opts listOptions + + cmd := &cobra.Command{ Use: "ls CONTAINER", Aliases: []string{"list"}, Short: "List checkpoints for a container", Args: cli.ExactArgs(1), 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() - checkpoints, err := client.CheckpointList(context.Background(), container) + listOpts := types.CheckpointListOptions{ + CheckpointDir: opts.checkpointDir, + } + + checkpoints, err := client.CheckpointList(context.Background(), container, listOpts) if err != nil { return err } diff --git a/cli/command/checkpoint/remove.go b/cli/command/checkpoint/remove.go index 82ce62312b..c6ec56df84 100644 --- a/cli/command/checkpoint/remove.go +++ b/cli/command/checkpoint/remove.go @@ -3,24 +3,42 @@ package checkpoint import ( "golang.org/x/net/context" + "github.com/docker/docker/api/types" "github.com/docker/docker/cli" "github.com/docker/docker/cli/command" "github.com/spf13/cobra" ) +type removeOptions struct { + checkpointDir string +} + func newRemoveCommand(dockerCli *command.DockerCli) *cobra.Command { - return &cobra.Command{ + var opts removeOptions + + cmd := &cobra.Command{ Use: "rm CONTAINER CHECKPOINT", Aliases: []string{"remove"}, Short: "Remove a checkpoint", Args: cli.ExactArgs(2), 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() - return client.CheckpointDelete(context.Background(), container, checkpoint) + + removeOpts := types.CheckpointDeleteOptions{ + CheckpointID: checkpoint, + CheckpointDir: opts.checkpointDir, + } + + return client.CheckpointDelete(context.Background(), container, removeOpts) } diff --git a/cli/command/container/start.go b/cli/command/container/start.go index 8693b3a550..8e0654da37 100644 --- a/cli/command/container/start.go +++ b/cli/command/container/start.go @@ -17,10 +17,11 @@ import ( ) type startOptions struct { - attach bool - openStdin bool - detachKeys string - checkpoint string + attach bool + openStdin bool + detachKeys string + checkpoint string + checkpointDir string containers []string } @@ -46,6 +47,7 @@ func NewStartCommand(dockerCli *command.DockerCli) *cobra.Command { if dockerCli.HasExperimental() { flags.StringVar(&opts.checkpoint, "checkpoint", "", "Restore from this checkpoint") + flags.StringVar(&opts.checkpointDir, "checkpoint-dir", "", "Use a custom checkpoint storage directory") } 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. statusChan := waitExitOrRemoved(dockerCli, ctx, c.ID, c.HostConfig.AutoRemove) startOptions := types.ContainerStartOptions{ - CheckpointID: opts.checkpoint, + CheckpointID: opts.checkpoint, + CheckpointDir: opts.checkpointDir, } // 4. Start the container. @@ -145,7 +148,8 @@ func runStart(dockerCli *command.DockerCli, opts *startOptions) error { } container := opts.containers[0] startOptions := types.ContainerStartOptions{ - CheckpointID: opts.checkpoint, + CheckpointID: opts.checkpoint, + CheckpointDir: opts.checkpointDir, } return dockerCli.Client().ContainerStart(ctx, container, startOptions) diff --git a/client/checkpoint_delete.go b/client/checkpoint_delete.go index a4e9ed0c06..e6e75588b1 100644 --- a/client/checkpoint_delete.go +++ b/client/checkpoint_delete.go @@ -1,12 +1,20 @@ package client import ( + "net/url" + + "github.com/docker/docker/api/types" "golang.org/x/net/context" ) // 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) return err } diff --git a/client/checkpoint_delete_test.go b/client/checkpoint_delete_test.go index 23931c6523..a78b050487 100644 --- a/client/checkpoint_delete_test.go +++ b/client/checkpoint_delete_test.go @@ -8,6 +8,7 @@ import ( "strings" "testing" + "github.com/docker/docker/api/types" "golang.org/x/net/context" ) @@ -16,7 +17,10 @@ func TestCheckpointDeleteError(t *testing.T) { 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" { 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 { t.Fatal(err) } diff --git a/client/checkpoint_list.go b/client/checkpoint_list.go index bb471e0056..8eb720a6b2 100644 --- a/client/checkpoint_list.go +++ b/client/checkpoint_list.go @@ -2,16 +2,22 @@ package client import ( "encoding/json" + "net/url" "github.com/docker/docker/api/types" "golang.org/x/net/context" ) // 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 - 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 { return checkpoints, err } diff --git a/client/checkpoint_list_test.go b/client/checkpoint_list_test.go index e636995bc1..6c90f61e8c 100644 --- a/client/checkpoint_list_test.go +++ b/client/checkpoint_list_test.go @@ -18,7 +18,7 @@ func TestCheckpointListError(t *testing.T) { 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" { 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 { t.Fatal(err) } diff --git a/client/container_start.go b/client/container_start.go index 44bb0080c0..b1f08de416 100644 --- a/client/container_start.go +++ b/client/container_start.go @@ -14,6 +14,9 @@ func (cli *Client) ContainerStart(ctx context.Context, containerID string, optio if len(options.CheckpointID) != 0 { 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) ensureReaderClosed(resp) diff --git a/client/interface_experimental.go b/client/interface_experimental.go index ddb9f79b5a..4f5cf853b8 100644 --- a/client/interface_experimental.go +++ b/client/interface_experimental.go @@ -13,8 +13,8 @@ type apiClientExperimental interface { // CheckpointAPIClient defines API client methods for the checkpoints type CheckpointAPIClient interface { 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 diff --git a/daemon/checkpoint.go b/daemon/checkpoint.go index 010ce31e88..417ae59f2d 100644 --- a/daemon/checkpoint.go +++ b/daemon/checkpoint.go @@ -21,7 +21,14 @@ func (daemon *Daemon) CheckpointCreate(name string, config types.CheckpointCreat 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 { 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 -func (daemon *Daemon) CheckpointDelete(name string, checkpoint string) error { +func (daemon *Daemon) CheckpointDelete(name string, config types.CheckpointDeleteOptions) error { container, err := daemon.GetContainer(name) if err != nil { 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 -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 container, err := daemon.GetContainer(name) @@ -51,7 +64,13 @@ func (daemon *Daemon) CheckpointList(name string) ([]types.Checkpoint, error) { 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 { return nil, err } diff --git a/daemon/cluster/executor/backend.go b/daemon/cluster/executor/backend.go index fb7c822225..2f91e78784 100644 --- a/daemon/cluster/executor/backend.go +++ b/daemon/cluster/executor/backend.go @@ -24,7 +24,7 @@ type Backend interface { 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 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 ConnectContainerToNetwork(containerName, networkName string, endpointConfig *network.EndpointSettings) error UpdateContainerServiceConfig(containerName string, serviceConfig *clustertypes.ServiceConfig) error diff --git a/daemon/cluster/executor/container/adapter.go b/daemon/cluster/executor/container/adapter.go index 56f8c4067e..7b6d1069ef 100644 --- a/daemon/cluster/executor/container/adapter.go +++ b/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 { version := httputils.VersionFromContext(ctx) 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) { diff --git a/daemon/daemon.go b/daemon/daemon.go index 9ef1b9f0af..9be801e8cb 100644 --- a/daemon/daemon.go +++ b/daemon/daemon.go @@ -305,7 +305,7 @@ func (daemon *Daemon) restore() error { // Make sure networks are available before starting 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) } close(chNotify) @@ -372,7 +372,7 @@ func (daemon *Daemon) RestartSwarmContainers() { group.Add(1) go func(c *container.Container) { defer group.Done() - if err := daemon.containerStart(c, "", true); err != nil { + if err := daemon.containerStart(c, "", "", true); err != nil { logrus.Error(err) } }(c) diff --git a/daemon/monitor.go b/daemon/monitor.go index 49f9c49ea2..c05d1336c0 100644 --- a/daemon/monitor.go +++ b/daemon/monitor.go @@ -62,7 +62,7 @@ func (daemon *Daemon) StateChanged(id string, e libcontainerd.StateInfo) error { go func() { err := <-wait 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) } } diff --git a/daemon/restart.go b/daemon/restart.go index cdf21af293..79292f3752 100644 --- a/daemon/restart.go +++ b/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 } diff --git a/daemon/start.go b/daemon/start.go index 475f6e600c..bef25d5c39 100644 --- a/daemon/start.go +++ b/daemon/start.go @@ -19,7 +19,7 @@ import ( ) // 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() { 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 daemon.containerStart(container, checkpoint, true) + return daemon.containerStart(container, checkpoint, checkpointDir, true) } // Start starts a container 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 // container needs, such as storage and networking, as well as links // between containers. The container is left waiting for a signal to // 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() container.Lock() defer container.Unlock() @@ -155,7 +155,11 @@ func (daemon *Daemon) containerStart(container *container.Container, checkpoint 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) logrus.Errorf("Create container failed with error: %s", errDesc) // if we receive an internal error from the initial start of a container then lets