mirror of
https://github.com/moby/moby.git
synced 2022-11-09 12:21:53 -05:00
Merge pull request #26709 from boucher/checkpoint-dir
Allow providing a custom storage directory for docker checkpoints
This commit is contained in:
commit
b57f321a4a
24 changed files with 167 additions and 57 deletions
|
@ -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
|
CheckpointDelete(container string, config types.CheckpointDeleteOptions) error
|
||||||
CheckpointList(container string) ([]types.Checkpoint, error)
|
CheckpointList(container string, config types.CheckpointListOptions) ([]types.Checkpoint, error)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
CheckpointID string
|
||||||
Exit bool
|
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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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",
|
||||||
|
|
|
@ -10,9 +10,10 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
type createOptions struct {
|
type createOptions struct {
|
||||||
container string
|
container string
|
||||||
checkpoint string
|
checkpoint string
|
||||||
leaveRunning bool
|
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,
|
CheckpointID: opts.checkpoint,
|
||||||
Exit: !opts.leaveRunning,
|
CheckpointDir: opts.checkpointDir,
|
||||||
|
Exit: !opts.leaveRunning,
|
||||||
}
|
}
|
||||||
|
|
||||||
err := client.CheckpointCreate(context.Background(), opts.container, checkpointOpts)
|
err := client.CheckpointCreate(context.Background(), opts.container, checkpointOpts)
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,10 +17,11 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
type startOptions struct {
|
type startOptions struct {
|
||||||
attach bool
|
attach bool
|
||||||
openStdin bool
|
openStdin bool
|
||||||
detachKeys string
|
detachKeys string
|
||||||
checkpoint 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)
|
||||||
|
|
||||||
|
|
|
@ -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 {
|
func (cli *Client) CheckpointDelete(ctx context.Context, containerID string, options types.CheckpointDeleteOptions) error {
|
||||||
resp, err := cli.delete(ctx, "/containers/"+containerID+"/checkpoints/"+checkpointID, nil, nil)
|
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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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
|
CheckpointDelete(ctx context.Context, container string, options types.CheckpointDeleteOptions) error
|
||||||
CheckpointList(ctx context.Context, container string) ([]types.Checkpoint, 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
|
||||||
|
|
|
@ -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()
|
var checkpointDir string
|
||||||
return os.RemoveAll(filepath.Join(checkpointDir, checkpoint))
|
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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Add table
Reference in a new issue