зеркало из https://github.com/docker/engine-api.git
Merge pull request #202 from boucher/checkpoints
Add Checkpoint Create/List/Delete methods.
This commit is contained in:
Коммит
1f09e8f4d7
|
@ -0,0 +1,13 @@
|
||||||
|
package client
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/docker/engine-api/types"
|
||||||
|
"golang.org/x/net/context"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CheckpointCreate creates a checkpoint from the given container with the given name
|
||||||
|
func (cli *Client) CheckpointCreate(ctx context.Context, container string, options types.CheckpointCreateOptions) error {
|
||||||
|
resp, err := cli.post(ctx, "/containers/"+container+"/checkpoints", nil, options, nil)
|
||||||
|
ensureReaderClosed(resp)
|
||||||
|
return err
|
||||||
|
}
|
|
@ -0,0 +1,73 @@
|
||||||
|
package client
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/docker/engine-api/types"
|
||||||
|
"golang.org/x/net/context"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestCheckpointCreateError(t *testing.T) {
|
||||||
|
client := &Client{
|
||||||
|
transport: newMockClient(nil, errorMock(http.StatusInternalServerError, "Server error")),
|
||||||
|
}
|
||||||
|
err := client.CheckpointCreate(context.Background(), "nothing", types.CheckpointCreateOptions{
|
||||||
|
CheckpointID: "noting",
|
||||||
|
Exit: true,
|
||||||
|
})
|
||||||
|
|
||||||
|
if err == nil || err.Error() != "Error response from daemon: Server error" {
|
||||||
|
t.Fatalf("expected a Server Error, got %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCheckpointCreate(t *testing.T) {
|
||||||
|
expectedContainerID := "container_id"
|
||||||
|
expectedCheckpointID := "checkpoint_id"
|
||||||
|
expectedURL := "/containers/container_id/checkpoints"
|
||||||
|
|
||||||
|
client := &Client{
|
||||||
|
transport: newMockClient(nil, func(req *http.Request) (*http.Response, error) {
|
||||||
|
if !strings.HasPrefix(req.URL.Path, expectedURL) {
|
||||||
|
return nil, fmt.Errorf("Expected URL '%s', got '%s'", expectedURL, req.URL)
|
||||||
|
}
|
||||||
|
|
||||||
|
if req.Method != "POST" {
|
||||||
|
return nil, fmt.Errorf("expected POST method, got %s", req.Method)
|
||||||
|
}
|
||||||
|
|
||||||
|
createOptions := &types.CheckpointCreateOptions{}
|
||||||
|
if err := json.NewDecoder(req.Body).Decode(createOptions); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if createOptions.CheckpointID != expectedCheckpointID {
|
||||||
|
return nil, fmt.Errorf("expected CheckpointID to be 'checkpoint_id', got %v", createOptions.CheckpointID)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !createOptions.Exit {
|
||||||
|
return nil, fmt.Errorf("expected Exit to be true")
|
||||||
|
}
|
||||||
|
|
||||||
|
return &http.Response{
|
||||||
|
StatusCode: http.StatusOK,
|
||||||
|
Body: ioutil.NopCloser(bytes.NewReader([]byte(""))),
|
||||||
|
}, nil
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
|
||||||
|
err := client.CheckpointCreate(context.Background(), expectedContainerID, types.CheckpointCreateOptions{
|
||||||
|
CheckpointID: expectedCheckpointID,
|
||||||
|
Exit: true,
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
package client
|
||||||
|
|
||||||
|
import (
|
||||||
|
"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)
|
||||||
|
ensureReaderClosed(resp)
|
||||||
|
return err
|
||||||
|
}
|
|
@ -0,0 +1,47 @@
|
||||||
|
package client
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"golang.org/x/net/context"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestCheckpointDeleteError(t *testing.T) {
|
||||||
|
client := &Client{
|
||||||
|
transport: newMockClient(nil, errorMock(http.StatusInternalServerError, "Server error")),
|
||||||
|
}
|
||||||
|
|
||||||
|
err := client.CheckpointDelete(context.Background(), "container_id", "checkpoint_id")
|
||||||
|
if err == nil || err.Error() != "Error response from daemon: Server error" {
|
||||||
|
t.Fatalf("expected a Server Error, got %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCheckpointDelete(t *testing.T) {
|
||||||
|
expectedURL := "/containers/container_id/checkpoints/checkpoint_id"
|
||||||
|
|
||||||
|
client := &Client{
|
||||||
|
transport: newMockClient(nil, func(req *http.Request) (*http.Response, error) {
|
||||||
|
if !strings.HasPrefix(req.URL.Path, expectedURL) {
|
||||||
|
return nil, fmt.Errorf("Expected URL '%s', got '%s'", expectedURL, req.URL)
|
||||||
|
}
|
||||||
|
if req.Method != "DELETE" {
|
||||||
|
return nil, fmt.Errorf("expected DELETE method, got %s", req.Method)
|
||||||
|
}
|
||||||
|
return &http.Response{
|
||||||
|
StatusCode: http.StatusOK,
|
||||||
|
Body: ioutil.NopCloser(bytes.NewReader([]byte(""))),
|
||||||
|
}, nil
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
|
||||||
|
err := client.CheckpointDelete(context.Background(), "container_id", "checkpoint_id")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,22 @@
|
||||||
|
package client
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
|
||||||
|
"github.com/docker/engine-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) {
|
||||||
|
var checkpoints []types.Checkpoint
|
||||||
|
|
||||||
|
resp, err := cli.get(ctx, "/containers/"+container+"/checkpoints", nil, nil)
|
||||||
|
if err != nil {
|
||||||
|
return checkpoints, err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = json.NewDecoder(resp.body).Decode(&checkpoints)
|
||||||
|
ensureReaderClosed(resp)
|
||||||
|
return checkpoints, err
|
||||||
|
}
|
|
@ -0,0 +1,57 @@
|
||||||
|
package client
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/docker/engine-api/types"
|
||||||
|
"golang.org/x/net/context"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestCheckpointListError(t *testing.T) {
|
||||||
|
client := &Client{
|
||||||
|
transport: newMockClient(nil, errorMock(http.StatusInternalServerError, "Server error")),
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := client.CheckpointList(context.Background(), "container_id")
|
||||||
|
if err == nil || err.Error() != "Error response from daemon: Server error" {
|
||||||
|
t.Fatalf("expected a Server Error, got %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCheckpointList(t *testing.T) {
|
||||||
|
expectedURL := "/containers/container_id/checkpoints"
|
||||||
|
|
||||||
|
client := &Client{
|
||||||
|
transport: newMockClient(nil, func(req *http.Request) (*http.Response, error) {
|
||||||
|
if !strings.HasPrefix(req.URL.Path, expectedURL) {
|
||||||
|
return nil, fmt.Errorf("Expected URL '%s', got '%s'", expectedURL, req.URL)
|
||||||
|
}
|
||||||
|
content, err := json.Marshal([]types.Checkpoint{
|
||||||
|
{
|
||||||
|
Name: "checkpoint",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &http.Response{
|
||||||
|
StatusCode: http.StatusOK,
|
||||||
|
Body: ioutil.NopCloser(bytes.NewReader(content)),
|
||||||
|
}, nil
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
|
||||||
|
checkpoints, err := client.CheckpointList(context.Background(), "container_id")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if len(checkpoints) != 1 {
|
||||||
|
t.Fatalf("expected 1 checkpoint, got %v", checkpoints)
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,10 +1,17 @@
|
||||||
package client
|
package client
|
||||||
|
|
||||||
import "golang.org/x/net/context"
|
import (
|
||||||
|
"net/url"
|
||||||
|
|
||||||
|
"golang.org/x/net/context"
|
||||||
|
)
|
||||||
|
|
||||||
// ContainerStart sends a request to the docker daemon to start a container.
|
// ContainerStart sends a request to the docker daemon to start a container.
|
||||||
func (cli *Client) ContainerStart(ctx context.Context, containerID string) error {
|
func (cli *Client) ContainerStart(ctx context.Context, containerID string, checkpointID string) error {
|
||||||
resp, err := cli.post(ctx, "/containers/"+containerID+"/start", nil, nil, nil)
|
query := url.Values{}
|
||||||
|
query.Set("checkpoint", checkpointID)
|
||||||
|
|
||||||
|
resp, err := cli.post(ctx, "/containers/"+containerID+"/start", query, nil, nil)
|
||||||
ensureReaderClosed(resp)
|
ensureReaderClosed(resp)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,7 +16,7 @@ func TestContainerStartError(t *testing.T) {
|
||||||
client := &Client{
|
client := &Client{
|
||||||
transport: newMockClient(nil, errorMock(http.StatusInternalServerError, "Server error")),
|
transport: newMockClient(nil, errorMock(http.StatusInternalServerError, "Server error")),
|
||||||
}
|
}
|
||||||
err := client.ContainerStart(context.Background(), "nothing")
|
err := client.ContainerStart(context.Background(), "nothing", "")
|
||||||
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)
|
||||||
}
|
}
|
||||||
|
@ -36,6 +36,12 @@ func TestContainerStart(t *testing.T) {
|
||||||
return nil, fmt.Errorf("Unable to parse json: %s", err)
|
return nil, fmt.Errorf("Unable to parse json: %s", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
checkpoint := req.URL.Query().Get("checkpoint")
|
||||||
|
if checkpoint != "checkpoint_id" {
|
||||||
|
return nil, fmt.Errorf("checkpoint not set in URL query properly. Expected 'checkpoint_id', got %s", checkpoint)
|
||||||
|
}
|
||||||
|
|
||||||
return &http.Response{
|
return &http.Response{
|
||||||
StatusCode: http.StatusOK,
|
StatusCode: http.StatusOK,
|
||||||
Body: ioutil.NopCloser(bytes.NewReader([]byte(""))),
|
Body: ioutil.NopCloser(bytes.NewReader([]byte(""))),
|
||||||
|
@ -43,7 +49,7 @@ func TestContainerStart(t *testing.T) {
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
|
|
||||||
err := client.ContainerStart(context.Background(), "container_id")
|
err := client.ContainerStart(context.Background(), "container_id", "checkpoint_id")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,6 +15,9 @@ import (
|
||||||
// APIClient is an interface that clients that talk with a docker server must implement.
|
// APIClient is an interface that clients that talk with a docker server must implement.
|
||||||
type APIClient interface {
|
type APIClient interface {
|
||||||
ClientVersion() string
|
ClientVersion() string
|
||||||
|
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)
|
||||||
ContainerAttach(ctx context.Context, container string, options types.ContainerAttachOptions) (types.HijackedResponse, error)
|
ContainerAttach(ctx context.Context, container string, options types.ContainerAttachOptions) (types.HijackedResponse, error)
|
||||||
ContainerCommit(ctx context.Context, container string, options types.ContainerCommitOptions) (types.ContainerCommitResponse, error)
|
ContainerCommit(ctx context.Context, container string, options types.ContainerCommitOptions) (types.ContainerCommitResponse, error)
|
||||||
ContainerCreate(ctx context.Context, config *container.Config, hostConfig *container.HostConfig, networkingConfig *network.NetworkingConfig, containerName string) (types.ContainerCreateResponse, error)
|
ContainerCreate(ctx context.Context, config *container.Config, hostConfig *container.HostConfig, networkingConfig *network.NetworkingConfig, containerName string) (types.ContainerCreateResponse, error)
|
||||||
|
@ -37,7 +40,7 @@ type APIClient interface {
|
||||||
ContainerRestart(ctx context.Context, container string, timeout int) error
|
ContainerRestart(ctx context.Context, container string, timeout int) error
|
||||||
ContainerStatPath(ctx context.Context, container, path string) (types.ContainerPathStat, error)
|
ContainerStatPath(ctx context.Context, container, path string) (types.ContainerPathStat, error)
|
||||||
ContainerStats(ctx context.Context, container string, stream bool) (io.ReadCloser, error)
|
ContainerStats(ctx context.Context, container string, stream bool) (io.ReadCloser, error)
|
||||||
ContainerStart(ctx context.Context, container string) error
|
ContainerStart(ctx context.Context, container string, checkpointID string) error
|
||||||
ContainerStop(ctx context.Context, container string, timeout int) error
|
ContainerStop(ctx context.Context, container string, timeout int) error
|
||||||
ContainerTop(ctx context.Context, container string, arguments []string) (types.ContainerProcessList, error)
|
ContainerTop(ctx context.Context, container string, arguments []string) (types.ContainerProcessList, error)
|
||||||
ContainerUnpause(ctx context.Context, container string) error
|
ContainerUnpause(ctx context.Context, container string) error
|
||||||
|
|
|
@ -10,6 +10,12 @@ import (
|
||||||
"github.com/docker/go-units"
|
"github.com/docker/go-units"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// CheckpointCreateOptions holds parameters to create a checkpoint from a container
|
||||||
|
type CheckpointCreateOptions struct {
|
||||||
|
CheckpointID string
|
||||||
|
Exit bool
|
||||||
|
}
|
||||||
|
|
||||||
// ContainerAttachOptions holds parameters to attach to a container.
|
// ContainerAttachOptions holds parameters to attach to a container.
|
||||||
type ContainerAttachOptions struct {
|
type ContainerAttachOptions struct {
|
||||||
Stream bool
|
Stream bool
|
||||||
|
|
|
@ -471,3 +471,8 @@ type NetworkDisconnect struct {
|
||||||
Container string
|
Container string
|
||||||
Force bool
|
Force bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Checkpoint represents the details of a checkpoint
|
||||||
|
type Checkpoint struct {
|
||||||
|
Name string // Name is the name of the checkpoint
|
||||||
|
}
|
||||||
|
|
Загрузка…
Ссылка в новой задаче