Skip to content

Commit

Permalink
Allow providing a custom storage directory for docker checkpoints
Browse files Browse the repository at this point in the history
Signed-off-by: boucher <[email protected]>
  • Loading branch information
boucher committed Oct 28, 2016
1 parent 0aaef96 commit bd7d512
Show file tree
Hide file tree
Showing 24 changed files with 167 additions and 57 deletions.
4 changes: 2 additions & 2 deletions api/server/router/checkpoint/backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
11 changes: 9 additions & 2 deletions api/server/router/checkpoint/checkpoint_routes.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand All @@ -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
}
Expand Down
2 changes: 1 addition & 1 deletion api/server/router/container/backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
3 changes: 2 additions & 1 deletion api/server/router/container/container_routes.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}

Expand Down
19 changes: 16 additions & 3 deletions api/types/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion builder/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion builder/dockerfile/internals.go
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
13 changes: 8 additions & 5 deletions cli/command/checkpoint/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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
}
Expand All @@ -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)
Expand Down
25 changes: 21 additions & 4 deletions cli/command/checkpoint/list.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down
26 changes: 22 additions & 4 deletions cli/command/checkpoint/remove.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
16 changes: 10 additions & 6 deletions cli/command/container/start.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand All @@ -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
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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)

Expand Down
12 changes: 10 additions & 2 deletions client/checkpoint_delete.go
Original file line number Diff line number Diff line change
@@ -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
}
11 changes: 9 additions & 2 deletions client/checkpoint_delete_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"strings"
"testing"

"github.com/docker/docker/api/types"
"golang.org/x/net/context"
)

Expand All @@ -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)
}
Expand All @@ -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)
}
Expand Down
10 changes: 8 additions & 2 deletions client/checkpoint_list.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down
4 changes: 2 additions & 2 deletions client/checkpoint_list_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
Expand Down Expand Up @@ -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)
}
Expand Down
3 changes: 3 additions & 0 deletions client/container_start.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
4 changes: 2 additions & 2 deletions client/interface_experimental.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Loading

0 comments on commit bd7d512

Please sign in to comment.