Merge pull request #265 from vieux/swarmkit

Add swarm management API endpoints
This commit is contained in:
Arnaud Porterie 2016-06-13 23:11:34 +00:00 коммит произвёл GitHub
Родитель bf992794fd 2f9f0bff7a
Коммит 6b2f24f16a
44 изменённых файлов: 2034 добавлений и 1 удалений

Просмотреть файл

@ -4,7 +4,7 @@ all: deps test validate
deps:
go get -t ./...
go get github.com/golang/lint/golint
go get -u github.com/golang/lint/golint
test:
go test -race -cover ./...

Просмотреть файл

@ -120,3 +120,54 @@ func IsErrUnauthorized(err error) bool {
_, ok := err.(unauthorizedError)
return ok
}
// nodeNotFoundError implements an error returned when a node is not found.
type nodeNotFoundError struct {
nodeID string
}
// Error returns a string representation of a nodeNotFoundError
func (e nodeNotFoundError) Error() string {
return fmt.Sprintf("Error: No such node: %s", e.nodeID)
}
// IsErrNodeNotFound returns true if the error is caused
// when a node is not found.
func IsErrNodeNotFound(err error) bool {
_, ok := err.(nodeNotFoundError)
return ok
}
// serviceNotFoundError implements an error returned when a service is not found.
type serviceNotFoundError struct {
serviceID string
}
// Error returns a string representation of a serviceNotFoundError
func (e serviceNotFoundError) Error() string {
return fmt.Sprintf("Error: No such service: %s", e.serviceID)
}
// IsErrServiceNotFound returns true if the error is caused
// when a service is not found.
func IsErrServiceNotFound(err error) bool {
_, ok := err.(serviceNotFoundError)
return ok
}
// taskNotFoundError implements an error returned when a task is not found.
type taskNotFoundError struct {
taskID string
}
// Error returns a string representation of a taskNotFoundError
func (e taskNotFoundError) Error() string {
return fmt.Sprintf("Error: No such task: %s", e.taskID)
}
// IsErrTaskNotFound returns true if the error is caused
// when a task is not found.
func IsErrTaskNotFound(err error) bool {
_, ok := err.(taskNotFoundError)
return ok
}

Просмотреть файл

@ -11,6 +11,7 @@ import (
"github.com/docker/engine-api/types/filters"
"github.com/docker/engine-api/types/network"
"github.com/docker/engine-api/types/registry"
"github.com/docker/engine-api/types/swarm"
)
// APIClient is an interface that clients that talk with a docker server must implement.
@ -19,6 +20,22 @@ type APIClient 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)
SwarmInit(ctx context.Context, req swarm.InitRequest) (string, error)
SwarmJoin(ctx context.Context, req swarm.JoinRequest) error
SwarmLeave(ctx context.Context, force bool) error
SwarmInspect(ctx context.Context) (swarm.Swarm, error)
SwarmUpdate(ctx context.Context, version swarm.Version, swarm swarm.Spec) error
NodeInspect(ctx context.Context, nodeID string) (swarm.Node, error)
NodeList(ctx context.Context, options types.NodeListOptions) ([]swarm.Node, error)
NodeRemove(ctx context.Context, nodeID string) error
NodeUpdate(ctx context.Context, nodeID string, version swarm.Version, node swarm.NodeSpec) error
ServiceCreate(ctx context.Context, service swarm.ServiceSpec) (types.ServiceCreateResponse, error)
ServiceInspect(ctx context.Context, serviceID string) (swarm.Service, error)
ServiceList(ctx context.Context, options types.ServiceListOptions) ([]swarm.Service, error)
ServiceRemove(ctx context.Context, serviceID string) error
ServiceUpdate(ctx context.Context, serviceID string, version swarm.Version, service swarm.ServiceSpec) error
TaskInspectWithRaw(ctx context.Context, taskID string) (swarm.Task, []byte, error)
TaskList(ctx context.Context, options types.TaskListOptions) ([]swarm.Task, 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)
ContainerCreate(ctx context.Context, config *container.Config, hostConfig *container.HostConfig, networkingConfig *network.NetworkingConfig, containerName string) (types.ContainerCreateResponse, error)

25
client/node_inspect.go Normal file
Просмотреть файл

@ -0,0 +1,25 @@
package client
import (
"encoding/json"
"net/http"
"github.com/docker/engine-api/types/swarm"
"golang.org/x/net/context"
)
// NodeInspect returns the node information.
func (cli *Client) NodeInspect(ctx context.Context, nodeID string) (swarm.Node, error) {
serverResp, err := cli.get(ctx, "/nodes/"+nodeID, nil, nil)
if err != nil {
if serverResp.statusCode == http.StatusNotFound {
return swarm.Node{}, nodeNotFoundError{nodeID}
}
return swarm.Node{}, err
}
var response swarm.Node
err = json.NewDecoder(serverResp.body).Decode(&response)
ensureReaderClosed(serverResp)
return response, err
}

Просмотреть файл

@ -0,0 +1,65 @@
package client
import (
"bytes"
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"strings"
"testing"
"github.com/docker/engine-api/types/swarm"
"golang.org/x/net/context"
)
func TestNodeInspectError(t *testing.T) {
client := &Client{
transport: newMockClient(nil, errorMock(http.StatusInternalServerError, "Server error")),
}
_, err := client.NodeInspect(context.Background(), "nothing")
if err == nil || err.Error() != "Error response from daemon: Server error" {
t.Fatalf("expected a Server Error, got %v", err)
}
}
func TestNodeInspectNodeNotFound(t *testing.T) {
client := &Client{
transport: newMockClient(nil, errorMock(http.StatusNotFound, "Server error")),
}
_, err := client.NodeInspect(context.Background(), "unknown")
if err == nil || !IsErrNodeNotFound(err) {
t.Fatalf("expected an nodeNotFoundError error, got %v", err)
}
}
func TestNodeInspect(t *testing.T) {
expectedURL := "/nodes/node_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)
}
content, err := json.Marshal(swarm.Node{
ID: "node_id",
})
if err != nil {
return nil, err
}
return &http.Response{
StatusCode: http.StatusOK,
Body: ioutil.NopCloser(bytes.NewReader(content)),
}, nil
}),
}
nodeInspect, err := client.NodeInspect(context.Background(), "node_id")
if err != nil {
t.Fatal(err)
}
if nodeInspect.ID != "node_id" {
t.Fatalf("expected `node_id`, got %s", nodeInspect.ID)
}
}

36
client/node_list.go Normal file
Просмотреть файл

@ -0,0 +1,36 @@
package client
import (
"encoding/json"
"net/url"
"github.com/docker/engine-api/types"
"github.com/docker/engine-api/types/filters"
"github.com/docker/engine-api/types/swarm"
"golang.org/x/net/context"
)
// NodeList returns the list of nodes.
func (cli *Client) NodeList(ctx context.Context, options types.NodeListOptions) ([]swarm.Node, error) {
query := url.Values{}
if options.Filter.Len() > 0 {
filterJSON, err := filters.ToParam(options.Filter)
if err != nil {
return nil, err
}
query.Set("filters", filterJSON)
}
resp, err := cli.get(ctx, "/nodes", query, nil)
if err != nil {
return nil, err
}
var nodes []swarm.Node
err = json.NewDecoder(resp.body).Decode(&nodes)
ensureReaderClosed(resp)
return nodes, err
}

94
client/node_list_test.go Normal file
Просмотреть файл

@ -0,0 +1,94 @@
package client
import (
"bytes"
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"strings"
"testing"
"github.com/docker/engine-api/types"
"github.com/docker/engine-api/types/filters"
"github.com/docker/engine-api/types/swarm"
"golang.org/x/net/context"
)
func TestNodeListError(t *testing.T) {
client := &Client{
transport: newMockClient(nil, errorMock(http.StatusInternalServerError, "Server error")),
}
_, err := client.NodeList(context.Background(), types.NodeListOptions{})
if err == nil || err.Error() != "Error response from daemon: Server error" {
t.Fatalf("expected a Server Error, got %v", err)
}
}
func TestNodeList(t *testing.T) {
expectedURL := "/nodes"
filters := filters.NewArgs()
filters.Add("label", "label1")
filters.Add("label", "label2")
listCases := []struct {
options types.NodeListOptions
expectedQueryParams map[string]string
}{
{
options: types.NodeListOptions{},
expectedQueryParams: map[string]string{
"filters": "",
},
},
{
options: types.NodeListOptions{
Filter: filters,
},
expectedQueryParams: map[string]string{
"filters": `{"label":{"label1":true,"label2":true}}`,
},
},
}
for _, listCase := range listCases {
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)
}
query := req.URL.Query()
for key, expected := range listCase.expectedQueryParams {
actual := query.Get(key)
if actual != expected {
return nil, fmt.Errorf("%s not set in URL query properly. Expected '%s', got %s", key, expected, actual)
}
}
content, err := json.Marshal([]swarm.Node{
{
ID: "node_id1",
},
{
ID: "node_id2",
},
})
if err != nil {
return nil, err
}
return &http.Response{
StatusCode: http.StatusOK,
Body: ioutil.NopCloser(bytes.NewReader(content)),
}, nil
}),
}
nodes, err := client.NodeList(context.Background(), listCase.options)
if err != nil {
t.Fatal(err)
}
if len(nodes) != 2 {
t.Fatalf("expected 2 nodes, got %v", nodes)
}
}
}

10
client/node_remove.go Normal file
Просмотреть файл

@ -0,0 +1,10 @@
package client
import "golang.org/x/net/context"
// NodeRemove removes a Node.
func (cli *Client) NodeRemove(ctx context.Context, nodeID string) error {
resp, err := cli.delete(ctx, "/nodes/"+nodeID, 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 TestNodeRemoveError(t *testing.T) {
client := &Client{
transport: newMockClient(nil, errorMock(http.StatusInternalServerError, "Server error")),
}
err := client.NodeRemove(context.Background(), "node_id")
if err == nil || err.Error() != "Error response from daemon: Server error" {
t.Fatalf("expected a Server Error, got %v", err)
}
}
func TestNodeRemove(t *testing.T) {
expectedURL := "/nodes/node_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("body"))),
}, nil
}),
}
err := client.NodeRemove(context.Background(), "node_id")
if err != nil {
t.Fatal(err)
}
}

18
client/node_update.go Normal file
Просмотреть файл

@ -0,0 +1,18 @@
package client
import (
"net/url"
"strconv"
"github.com/docker/engine-api/types/swarm"
"golang.org/x/net/context"
)
// NodeUpdate updates a Node.
func (cli *Client) NodeUpdate(ctx context.Context, nodeID string, version swarm.Version, node swarm.NodeSpec) error {
query := url.Values{}
query.Set("version", strconv.FormatUint(version.Index, 10))
resp, err := cli.post(ctx, "/nodes/"+nodeID+"/update", query, node, nil)
ensureReaderClosed(resp)
return err
}

Просмотреть файл

@ -0,0 +1,49 @@
package client
import (
"bytes"
"fmt"
"io/ioutil"
"net/http"
"strings"
"testing"
"golang.org/x/net/context"
"github.com/docker/engine-api/types/swarm"
)
func TestNodeUpdateError(t *testing.T) {
client := &Client{
transport: newMockClient(nil, errorMock(http.StatusInternalServerError, "Server error")),
}
err := client.NodeUpdate(context.Background(), "node_id", swarm.Version{}, swarm.NodeSpec{})
if err == nil || err.Error() != "Error response from daemon: Server error" {
t.Fatalf("expected a Server Error, got %v", err)
}
}
func TestNodeUpdate(t *testing.T) {
expectedURL := "/nodes/node_id/update"
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)
}
return &http.Response{
StatusCode: http.StatusOK,
Body: ioutil.NopCloser(bytes.NewReader([]byte("body"))),
}, nil
}),
}
err := client.NodeUpdate(context.Background(), "node_id", swarm.Version{}, swarm.NodeSpec{})
if err != nil {
t.Fatal(err)
}
}

22
client/service_create.go Normal file
Просмотреть файл

@ -0,0 +1,22 @@
package client
import (
"encoding/json"
"github.com/docker/engine-api/types"
"github.com/docker/engine-api/types/swarm"
"golang.org/x/net/context"
)
// ServiceCreate creates a new Service.
func (cli *Client) ServiceCreate(ctx context.Context, service swarm.ServiceSpec) (types.ServiceCreateResponse, error) {
var response types.ServiceCreateResponse
resp, err := cli.post(ctx, "/services/create", nil, service, nil)
if err != nil {
return response, err
}
err = json.NewDecoder(resp.body).Decode(&response)
ensureReaderClosed(resp)
return response, err
}

Просмотреть файл

@ -0,0 +1,57 @@
package client
import (
"bytes"
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"strings"
"testing"
"github.com/docker/engine-api/types"
"github.com/docker/engine-api/types/swarm"
"golang.org/x/net/context"
)
func TestServiceCreateError(t *testing.T) {
client := &Client{
transport: newMockClient(nil, errorMock(http.StatusInternalServerError, "Server error")),
}
_, err := client.ServiceCreate(context.Background(), swarm.ServiceSpec{})
if err == nil || err.Error() != "Error response from daemon: Server error" {
t.Fatalf("expected a Server Error, got %v", err)
}
}
func TestServiceCreate(t *testing.T) {
expectedURL := "/services/create"
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)
}
b, err := json.Marshal(types.ServiceCreateResponse{
ID: "service_id",
})
if err != nil {
return nil, err
}
return &http.Response{
StatusCode: http.StatusOK,
Body: ioutil.NopCloser(bytes.NewReader(b)),
}, nil
}),
}
r, err := client.ServiceCreate(context.Background(), swarm.ServiceSpec{})
if err != nil {
t.Fatal(err)
}
if r.ID != "service_id" {
t.Fatalf("expected `service_id`, got %s", r.ID)
}
}

25
client/service_inspect.go Normal file
Просмотреть файл

@ -0,0 +1,25 @@
package client
import (
"encoding/json"
"net/http"
"github.com/docker/engine-api/types/swarm"
"golang.org/x/net/context"
)
// ServiceInspect returns the service information.
func (cli *Client) ServiceInspect(ctx context.Context, serviceID string) (swarm.Service, error) {
serverResp, err := cli.get(ctx, "/services/"+serviceID, nil, nil)
if err != nil {
if serverResp.statusCode == http.StatusNotFound {
return swarm.Service{}, serviceNotFoundError{serviceID}
}
return swarm.Service{}, err
}
var response swarm.Service
err = json.NewDecoder(serverResp.body).Decode(&response)
ensureReaderClosed(serverResp)
return response, err
}

Просмотреть файл

@ -0,0 +1,65 @@
package client
import (
"bytes"
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"strings"
"testing"
"github.com/docker/engine-api/types/swarm"
"golang.org/x/net/context"
)
func TestServiceInspectError(t *testing.T) {
client := &Client{
transport: newMockClient(nil, errorMock(http.StatusInternalServerError, "Server error")),
}
_, err := client.ServiceInspect(context.Background(), "nothing")
if err == nil || err.Error() != "Error response from daemon: Server error" {
t.Fatalf("expected a Server Error, got %v", err)
}
}
func TestServiceInspectServiceNotFound(t *testing.T) {
client := &Client{
transport: newMockClient(nil, errorMock(http.StatusNotFound, "Server error")),
}
_, err := client.ServiceInspect(context.Background(), "unknown")
if err == nil || !IsErrServiceNotFound(err) {
t.Fatalf("expected an serviceNotFoundError error, got %v", err)
}
}
func TestServiceInspect(t *testing.T) {
expectedURL := "/services/service_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)
}
content, err := json.Marshal(swarm.Service{
ID: "service_id",
})
if err != nil {
return nil, err
}
return &http.Response{
StatusCode: http.StatusOK,
Body: ioutil.NopCloser(bytes.NewReader(content)),
}, nil
}),
}
serviceInspect, err := client.ServiceInspect(context.Background(), "service_id")
if err != nil {
t.Fatal(err)
}
if serviceInspect.ID != "service_id" {
t.Fatalf("expected `service_id`, got %s", serviceInspect.ID)
}
}

35
client/service_list.go Normal file
Просмотреть файл

@ -0,0 +1,35 @@
package client
import (
"encoding/json"
"net/url"
"github.com/docker/engine-api/types"
"github.com/docker/engine-api/types/filters"
"github.com/docker/engine-api/types/swarm"
"golang.org/x/net/context"
)
// ServiceList returns the list of services.
func (cli *Client) ServiceList(ctx context.Context, options types.ServiceListOptions) ([]swarm.Service, error) {
query := url.Values{}
if options.Filter.Len() > 0 {
filterJSON, err := filters.ToParam(options.Filter)
if err != nil {
return nil, err
}
query.Set("filters", filterJSON)
}
resp, err := cli.get(ctx, "/services", query, nil)
if err != nil {
return nil, err
}
var services []swarm.Service
err = json.NewDecoder(resp.body).Decode(&services)
ensureReaderClosed(resp)
return services, err
}

Просмотреть файл

@ -0,0 +1,94 @@
package client
import (
"bytes"
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"strings"
"testing"
"github.com/docker/engine-api/types"
"github.com/docker/engine-api/types/filters"
"github.com/docker/engine-api/types/swarm"
"golang.org/x/net/context"
)
func TestServiceListError(t *testing.T) {
client := &Client{
transport: newMockClient(nil, errorMock(http.StatusInternalServerError, "Server error")),
}
_, err := client.ServiceList(context.Background(), types.ServiceListOptions{})
if err == nil || err.Error() != "Error response from daemon: Server error" {
t.Fatalf("expected a Server Error, got %v", err)
}
}
func TestServiceList(t *testing.T) {
expectedURL := "/services"
filters := filters.NewArgs()
filters.Add("label", "label1")
filters.Add("label", "label2")
listCases := []struct {
options types.ServiceListOptions
expectedQueryParams map[string]string
}{
{
options: types.ServiceListOptions{},
expectedQueryParams: map[string]string{
"filters": "",
},
},
{
options: types.ServiceListOptions{
Filter: filters,
},
expectedQueryParams: map[string]string{
"filters": `{"label":{"label1":true,"label2":true}}`,
},
},
}
for _, listCase := range listCases {
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)
}
query := req.URL.Query()
for key, expected := range listCase.expectedQueryParams {
actual := query.Get(key)
if actual != expected {
return nil, fmt.Errorf("%s not set in URL query properly. Expected '%s', got %s", key, expected, actual)
}
}
content, err := json.Marshal([]swarm.Service{
{
ID: "service_id1",
},
{
ID: "service_id2",
},
})
if err != nil {
return nil, err
}
return &http.Response{
StatusCode: http.StatusOK,
Body: ioutil.NopCloser(bytes.NewReader(content)),
}, nil
}),
}
services, err := client.ServiceList(context.Background(), listCase.options)
if err != nil {
t.Fatal(err)
}
if len(services) != 2 {
t.Fatalf("expected 2 services, got %v", services)
}
}
}

10
client/service_remove.go Normal file
Просмотреть файл

@ -0,0 +1,10 @@
package client
import "golang.org/x/net/context"
// ServiceRemove kills and removes a service.
func (cli *Client) ServiceRemove(ctx context.Context, serviceID string) error {
resp, err := cli.delete(ctx, "/services/"+serviceID, 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 TestServiceRemoveError(t *testing.T) {
client := &Client{
transport: newMockClient(nil, errorMock(http.StatusInternalServerError, "Server error")),
}
err := client.ServiceRemove(context.Background(), "service_id")
if err == nil || err.Error() != "Error response from daemon: Server error" {
t.Fatalf("expected a Server Error, got %v", err)
}
}
func TestServiceRemove(t *testing.T) {
expectedURL := "/services/service_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("body"))),
}, nil
}),
}
err := client.ServiceRemove(context.Background(), "service_id")
if err != nil {
t.Fatal(err)
}
}

18
client/service_update.go Normal file
Просмотреть файл

@ -0,0 +1,18 @@
package client
import (
"net/url"
"strconv"
"github.com/docker/engine-api/types/swarm"
"golang.org/x/net/context"
)
// ServiceUpdate updates a Service.
func (cli *Client) ServiceUpdate(ctx context.Context, serviceID string, version swarm.Version, service swarm.ServiceSpec) error {
query := url.Values{}
query.Set("version", strconv.FormatUint(version.Index, 10))
resp, err := cli.post(ctx, "/services/"+serviceID+"/update", query, service, nil)
ensureReaderClosed(resp)
return err
}

Просмотреть файл

@ -0,0 +1,76 @@
package client
import (
"bytes"
"fmt"
"io/ioutil"
"net/http"
"strings"
"testing"
"golang.org/x/net/context"
"github.com/docker/engine-api/types/swarm"
)
func TestServiceUpdateError(t *testing.T) {
client := &Client{
transport: newMockClient(nil, errorMock(http.StatusInternalServerError, "Server error")),
}
err := client.ServiceUpdate(context.Background(), "service_id", swarm.Version{}, swarm.ServiceSpec{})
if err == nil || err.Error() != "Error response from daemon: Server error" {
t.Fatalf("expected a Server Error, got %v", err)
}
}
func TestServiceUpdate(t *testing.T) {
expectedURL := "/services/service_id/update"
updateCases := []struct {
swarmVersion swarm.Version
expectedVersion string
}{
{
expectedVersion: "0",
},
{
swarmVersion: swarm.Version{
Index: 0,
},
expectedVersion: "0",
},
{
swarmVersion: swarm.Version{
Index: 10,
},
expectedVersion: "10",
},
}
for _, updateCase := range updateCases {
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)
}
version := req.URL.Query().Get("version")
if version != updateCase.expectedVersion {
return nil, fmt.Errorf("version not set in URL query properly, expected '%s', got %s", updateCase.expectedVersion, version)
}
return &http.Response{
StatusCode: http.StatusOK,
Body: ioutil.NopCloser(bytes.NewReader([]byte("body"))),
}, nil
}),
}
err := client.ServiceUpdate(context.Background(), "service_id", updateCase.swarmVersion, swarm.ServiceSpec{})
if err != nil {
t.Fatal(err)
}
}
}

21
client/swarm_init.go Normal file
Просмотреть файл

@ -0,0 +1,21 @@
package client
import (
"encoding/json"
"github.com/docker/engine-api/types/swarm"
"golang.org/x/net/context"
)
// SwarmInit initializes the Swarm.
func (cli *Client) SwarmInit(ctx context.Context, req swarm.InitRequest) (string, error) {
serverResp, err := cli.post(ctx, "/swarm/init", nil, req, nil)
if err != nil {
return "", err
}
var response string
err = json.NewDecoder(serverResp.body).Decode(&response)
ensureReaderClosed(serverResp)
return response, err
}

54
client/swarm_init_test.go Normal file
Просмотреть файл

@ -0,0 +1,54 @@
package client
import (
"bytes"
"fmt"
"io/ioutil"
"net/http"
"strings"
"testing"
"golang.org/x/net/context"
"github.com/docker/engine-api/types/swarm"
)
func TestSwarmInitError(t *testing.T) {
client := &Client{
transport: newMockClient(nil, errorMock(http.StatusInternalServerError, "Server error")),
}
_, err := client.SwarmInit(context.Background(), swarm.InitRequest{})
if err == nil || err.Error() != "Error response from daemon: Server error" {
t.Fatalf("expected a Server Error, got %v", err)
}
}
func TestSwarmInit(t *testing.T) {
expectedURL := "/swarm/init"
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)
}
return &http.Response{
StatusCode: http.StatusOK,
Body: ioutil.NopCloser(bytes.NewReader([]byte(`"body"`))),
}, nil
}),
}
resp, err := client.SwarmInit(context.Background(), swarm.InitRequest{
ListenAddr: "0.0.0.0:2377",
})
if err != nil {
t.Fatal(err)
}
if resp != "body" {
t.Fatalf("Expected 'body', got %s", resp)
}
}

21
client/swarm_inspect.go Normal file
Просмотреть файл

@ -0,0 +1,21 @@
package client
import (
"encoding/json"
"github.com/docker/engine-api/types/swarm"
"golang.org/x/net/context"
)
// SwarmInspect inspects the Swarm.
func (cli *Client) SwarmInspect(ctx context.Context) (swarm.Swarm, error) {
serverResp, err := cli.get(ctx, "/swarm", nil, nil)
if err != nil {
return swarm.Swarm{}, err
}
var response swarm.Swarm
err = json.NewDecoder(serverResp.body).Decode(&response)
ensureReaderClosed(serverResp)
return response, err
}

Просмотреть файл

@ -0,0 +1,54 @@
package client
import (
"bytes"
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"strings"
"testing"
"github.com/docker/engine-api/types/swarm"
"golang.org/x/net/context"
)
func TestSwarmInspectError(t *testing.T) {
client := &Client{
transport: newMockClient(nil, errorMock(http.StatusInternalServerError, "Server error")),
}
_, err := client.SwarmInspect(context.Background())
if err == nil || err.Error() != "Error response from daemon: Server error" {
t.Fatalf("expected a Server Error, got %v", err)
}
}
func TestSwarmInspect(t *testing.T) {
expectedURL := "/swarm"
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(swarm.Swarm{
ID: "swarm_id",
})
if err != nil {
return nil, err
}
return &http.Response{
StatusCode: http.StatusOK,
Body: ioutil.NopCloser(bytes.NewReader(content)),
}, nil
}),
}
swarmInspect, err := client.SwarmInspect(context.Background())
if err != nil {
t.Fatal(err)
}
if swarmInspect.ID != "swarm_id" {
t.Fatalf("expected `swarm_id`, got %s", swarmInspect.ID)
}
}

13
client/swarm_join.go Normal file
Просмотреть файл

@ -0,0 +1,13 @@
package client
import (
"github.com/docker/engine-api/types/swarm"
"golang.org/x/net/context"
)
// SwarmJoin joins the Swarm.
func (cli *Client) SwarmJoin(ctx context.Context, req swarm.JoinRequest) error {
resp, err := cli.post(ctx, "/swarm/join", nil, req, nil)
ensureReaderClosed(resp)
return err
}

51
client/swarm_join_test.go Normal file
Просмотреть файл

@ -0,0 +1,51 @@
package client
import (
"bytes"
"fmt"
"io/ioutil"
"net/http"
"strings"
"testing"
"golang.org/x/net/context"
"github.com/docker/engine-api/types/swarm"
)
func TestSwarmJoinError(t *testing.T) {
client := &Client{
transport: newMockClient(nil, errorMock(http.StatusInternalServerError, "Server error")),
}
err := client.SwarmJoin(context.Background(), swarm.JoinRequest{})
if err == nil || err.Error() != "Error response from daemon: Server error" {
t.Fatalf("expected a Server Error, got %v", err)
}
}
func TestSwarmJoin(t *testing.T) {
expectedURL := "/swarm/join"
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)
}
return &http.Response{
StatusCode: http.StatusOK,
Body: ioutil.NopCloser(bytes.NewReader([]byte(""))),
}, nil
}),
}
err := client.SwarmJoin(context.Background(), swarm.JoinRequest{
ListenAddr: "0.0.0.0:2377",
})
if err != nil {
t.Fatal(err)
}
}

18
client/swarm_leave.go Normal file
Просмотреть файл

@ -0,0 +1,18 @@
package client
import (
"net/url"
"golang.org/x/net/context"
)
// SwarmLeave leaves the Swarm.
func (cli *Client) SwarmLeave(ctx context.Context, force bool) error {
query := url.Values{}
if force {
query.Set("force", "1")
}
resp, err := cli.post(ctx, "/swarm/leave", query, nil, nil)
ensureReaderClosed(resp)
return err
}

Просмотреть файл

@ -0,0 +1,66 @@
package client
import (
"bytes"
"fmt"
"io/ioutil"
"net/http"
"strings"
"testing"
"golang.org/x/net/context"
)
func TestSwarmLeaveError(t *testing.T) {
client := &Client{
transport: newMockClient(nil, errorMock(http.StatusInternalServerError, "Server error")),
}
err := client.SwarmLeave(context.Background(), false)
if err == nil || err.Error() != "Error response from daemon: Server error" {
t.Fatalf("expected a Server Error, got %v", err)
}
}
func TestSwarmLeave(t *testing.T) {
expectedURL := "/swarm/leave"
leaveCases := []struct {
force bool
expectedForce string
}{
{
expectedForce: "",
},
{
force: true,
expectedForce: "1",
},
}
for _, leaveCase := range leaveCases {
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)
}
force := req.URL.Query().Get("force")
if force != leaveCase.expectedForce {
return nil, fmt.Errorf("force not set in URL query properly. expected '%s', got %s", leaveCase.expectedForce, force)
}
return &http.Response{
StatusCode: http.StatusOK,
Body: ioutil.NopCloser(bytes.NewReader([]byte(""))),
}, nil
}),
}
err := client.SwarmLeave(context.Background(), leaveCase.force)
if err != nil {
t.Fatal(err)
}
}
}

18
client/swarm_update.go Normal file
Просмотреть файл

@ -0,0 +1,18 @@
package client
import (
"net/url"
"strconv"
"github.com/docker/engine-api/types/swarm"
"golang.org/x/net/context"
)
// SwarmUpdate updates the Swarm.
func (cli *Client) SwarmUpdate(ctx context.Context, version swarm.Version, swarm swarm.Spec) error {
query := url.Values{}
query.Set("version", strconv.FormatUint(version.Index, 10))
resp, err := cli.post(ctx, "/swarm/update", query, swarm, nil)
ensureReaderClosed(resp)
return err
}

Просмотреть файл

@ -0,0 +1,49 @@
package client
import (
"bytes"
"fmt"
"io/ioutil"
"net/http"
"strings"
"testing"
"golang.org/x/net/context"
"github.com/docker/engine-api/types/swarm"
)
func TestSwarmUpdateError(t *testing.T) {
client := &Client{
transport: newMockClient(nil, errorMock(http.StatusInternalServerError, "Server error")),
}
err := client.SwarmUpdate(context.Background(), swarm.Version{}, swarm.Spec{})
if err == nil || err.Error() != "Error response from daemon: Server error" {
t.Fatalf("expected a Server Error, got %v", err)
}
}
func TestSwarmUpdate(t *testing.T) {
expectedURL := "/swarm/update"
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)
}
return &http.Response{
StatusCode: http.StatusOK,
Body: ioutil.NopCloser(bytes.NewReader([]byte(""))),
}, nil
}),
}
err := client.SwarmUpdate(context.Background(), swarm.Version{}, swarm.Spec{})
if err != nil {
t.Fatal(err)
}
}

34
client/task_inspect.go Normal file
Просмотреть файл

@ -0,0 +1,34 @@
package client
import (
"bytes"
"encoding/json"
"io/ioutil"
"net/http"
"github.com/docker/engine-api/types/swarm"
"golang.org/x/net/context"
)
// TaskInspectWithRaw returns the task information and its raw representation..
func (cli *Client) TaskInspectWithRaw(ctx context.Context, taskID string) (swarm.Task, []byte, error) {
serverResp, err := cli.get(ctx, "/tasks/"+taskID, nil, nil)
if err != nil {
if serverResp.statusCode == http.StatusNotFound {
return swarm.Task{}, nil, taskNotFoundError{taskID}
}
return swarm.Task{}, nil, err
}
defer ensureReaderClosed(serverResp)
body, err := ioutil.ReadAll(serverResp.body)
if err != nil {
return swarm.Task{}, nil, err
}
var response swarm.Task
rdr := bytes.NewReader(body)
err = json.NewDecoder(rdr).Decode(&response)
return response, body, err
}

Просмотреть файл

@ -0,0 +1,54 @@
package client
import (
"bytes"
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"strings"
"testing"
"github.com/docker/engine-api/types/swarm"
"golang.org/x/net/context"
)
func TestTaskInspectError(t *testing.T) {
client := &Client{
transport: newMockClient(nil, errorMock(http.StatusInternalServerError, "Server error")),
}
_, _, err := client.TaskInspectWithRaw(context.Background(), "nothing")
if err == nil || err.Error() != "Error response from daemon: Server error" {
t.Fatalf("expected a Server Error, got %v", err)
}
}
func TestTaskInspect(t *testing.T) {
expectedURL := "/tasks/task_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)
}
content, err := json.Marshal(swarm.Task{
ID: "task_id",
})
if err != nil {
return nil, err
}
return &http.Response{
StatusCode: http.StatusOK,
Body: ioutil.NopCloser(bytes.NewReader(content)),
}, nil
}),
}
taskInspect, _, err := client.TaskInspectWithRaw(context.Background(), "task_id")
if err != nil {
t.Fatal(err)
}
if taskInspect.ID != "task_id" {
t.Fatalf("expected `task_id`, got %s", taskInspect.ID)
}
}

35
client/task_list.go Normal file
Просмотреть файл

@ -0,0 +1,35 @@
package client
import (
"encoding/json"
"net/url"
"github.com/docker/engine-api/types"
"github.com/docker/engine-api/types/filters"
"github.com/docker/engine-api/types/swarm"
"golang.org/x/net/context"
)
// TaskList returns the list of tasks.
func (cli *Client) TaskList(ctx context.Context, options types.TaskListOptions) ([]swarm.Task, error) {
query := url.Values{}
if options.Filter.Len() > 0 {
filterJSON, err := filters.ToParam(options.Filter)
if err != nil {
return nil, err
}
query.Set("filters", filterJSON)
}
resp, err := cli.get(ctx, "/tasks", query, nil)
if err != nil {
return nil, err
}
var tasks []swarm.Task
err = json.NewDecoder(resp.body).Decode(&tasks)
ensureReaderClosed(resp)
return tasks, err
}

94
client/task_list_test.go Normal file
Просмотреть файл

@ -0,0 +1,94 @@
package client
import (
"bytes"
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"strings"
"testing"
"github.com/docker/engine-api/types"
"github.com/docker/engine-api/types/filters"
"github.com/docker/engine-api/types/swarm"
"golang.org/x/net/context"
)
func TestTaskListError(t *testing.T) {
client := &Client{
transport: newMockClient(nil, errorMock(http.StatusInternalServerError, "Server error")),
}
_, err := client.TaskList(context.Background(), types.TaskListOptions{})
if err == nil || err.Error() != "Error response from daemon: Server error" {
t.Fatalf("expected a Server Error, got %v", err)
}
}
func TestTaskList(t *testing.T) {
expectedURL := "/tasks"
filters := filters.NewArgs()
filters.Add("label", "label1")
filters.Add("label", "label2")
listCases := []struct {
options types.TaskListOptions
expectedQueryParams map[string]string
}{
{
options: types.TaskListOptions{},
expectedQueryParams: map[string]string{
"filters": "",
},
},
{
options: types.TaskListOptions{
Filter: filters,
},
expectedQueryParams: map[string]string{
"filters": `{"label":{"label1":true,"label2":true}}`,
},
},
}
for _, listCase := range listCases {
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)
}
query := req.URL.Query()
for key, expected := range listCase.expectedQueryParams {
actual := query.Get(key)
if actual != expected {
return nil, fmt.Errorf("%s not set in URL query properly. Expected '%s', got %s", key, expected, actual)
}
}
content, err := json.Marshal([]swarm.Task{
{
ID: "task_id1",
},
{
ID: "task_id2",
},
})
if err != nil {
return nil, err
}
return &http.Response{
StatusCode: http.StatusOK,
Body: ioutil.NopCloser(bytes.NewReader(content)),
}, nil
}),
}
tasks, err := client.TaskList(context.Background(), listCase.options)
if err != nil {
t.Fatal(err)
}
if len(tasks) != 2 {
t.Fatalf("expected 2 tasks, got %v", tasks)
}
}
}

Просмотреть файл

@ -240,3 +240,25 @@ type VersionResponse struct {
func (v VersionResponse) ServerOK() bool {
return v.Server != nil
}
// NodeListOptions holds parameters to list nodes with.
type NodeListOptions struct {
Filter filters.Args
}
// ServiceCreateResponse contains the information returned to a client
// on the creation of a new service.
type ServiceCreateResponse struct {
// ID is the ID of the created service.
ID string
}
// ServiceListOptions holds parameters to list services with.
type ServiceListOptions struct {
Filter filters.Args
}
// TaskListOptions holds parameters to list tasks with.
type TaskListOptions struct {
Filter filters.Args
}

21
types/swarm/common.go Normal file
Просмотреть файл

@ -0,0 +1,21 @@
package swarm
import "time"
// Version represent the internal object version.
type Version struct {
Index uint64 `json:",omitempty"`
}
// Meta is base object inherited by most of the other once.
type Meta struct {
Version Version `json:",omitempty"`
CreatedAt time.Time `json:",omitempty"`
UpdatedAt time.Time `json:",omitempty"`
}
// Annotations represents how to describe an object.
type Annotations struct {
Name string `json:",omitempty"`
Labels map[string]string `json:",omitempty"`
}

67
types/swarm/container.go Normal file
Просмотреть файл

@ -0,0 +1,67 @@
package swarm
import "time"
// ContainerSpec represents the spec of a container.
type ContainerSpec struct {
Image string `json:",omitempty"`
Labels map[string]string `json:",omitempty"`
Command []string `json:",omitempty"`
Args []string `json:",omitempty"`
Env []string `json:",omitempty"`
Dir string `json:",omitempty"`
User string `json:",omitempty"`
Mounts []Mount `json:",omitempty"`
StopGracePeriod *time.Duration `json:",omitempty"`
}
// MountType represents the type of a mount.
type MountType string
const (
// MountTypeBind BIND
MountTypeBind MountType = "bind"
// MountTypeVolume VOLUME
MountTypeVolume MountType = "volume"
)
// Mount represents a mount (volume).
type Mount struct {
Type MountType `json:",omitempty"`
Source string `json:",omitempty"`
Target string `json:",omitempty"`
Writable bool `json:",omitempty"`
BindOptions *BindOptions `json:",omitempty"`
VolumeOptions *VolumeOptions `json:",omitempty"`
}
// MountPropagation represents the propagation of a mount.
type MountPropagation string
const (
// MountPropagationRPrivate RPRIVATE
MountPropagationRPrivate MountPropagation = "rprivate"
// MountPropagationPrivate PRIVATE
MountPropagationPrivate MountPropagation = "private"
// MountPropagationRShared RSHARED
MountPropagationRShared MountPropagation = "rshared"
// MountPropagationShared SHARED
MountPropagationShared MountPropagation = "shared"
// MountPropagationRSlave RSLAVE
MountPropagationRSlave MountPropagation = "rslave"
// MountPropagationSlave SLAVE
MountPropagationSlave MountPropagation = "slave"
)
// BindOptions define options specific to mounts of type "bind".
type BindOptions struct {
Propagation MountPropagation `json:",omitempty"`
}
// VolumeOptions represents the options for a mount of type volume.
type VolumeOptions struct {
Populate bool `json:",omitempty"`
Labels map[string]string `json:",omitempty"`
DriverConfig *Driver `json:",omitempty"`
}

99
types/swarm/network.go Normal file
Просмотреть файл

@ -0,0 +1,99 @@
package swarm
// Endpoint represents an endpoint.
type Endpoint struct {
Spec EndpointSpec `json:",omitempty"`
Ports []PortConfig `json:",omitempty"`
VirtualIPs []EndpointVirtualIP `json:",omitempty"`
}
// EndpointSpec represents the spec of an endpoint.
type EndpointSpec struct {
Mode ResolutionMode `json:",omitempty"`
Ports []PortConfig `json:",omitempty"`
}
// ResolutionMode represents a resolution mode.
type ResolutionMode string
const (
// ResolutionModeVIP VIP
ResolutionModeVIP ResolutionMode = "vip"
// ResolutionModeDNSRR DNSRR
ResolutionModeDNSRR ResolutionMode = "dnsrr"
)
// PortConfig represents the config of a port.
type PortConfig struct {
Name string `json:",omitempty"`
Protocol PortConfigProtocol `json:",omitempty"`
TargetPort uint32 `json:",omitempty"`
PublishedPort uint32 `json:",omitempty"`
}
// PortConfigProtocol represents the protocol of a port.
type PortConfigProtocol string
const (
// TODO(stevvooe): These should be used generally, not just for PortConfig.
// PortConfigProtocolTCP TCP
PortConfigProtocolTCP PortConfigProtocol = "tcp"
// PortConfigProtocolUDP UDP
PortConfigProtocolUDP PortConfigProtocol = "udp"
)
// EndpointVirtualIP represents the virtual ip of a port.
type EndpointVirtualIP struct {
NetworkID string `json:",omitempty"`
Addr string `json:",omitempty"`
}
// Network represents a network.
type Network struct {
ID string
Meta
Spec NetworkSpec `json:",omitempty"`
DriverState Driver `json:",omitempty"`
IPAMOptions *IPAMOptions `json:",omitempty"`
}
// NetworkSpec represents the spec of a network.
type NetworkSpec struct {
Annotations
DriverConfiguration *Driver `json:",omitempty"`
IPv6Enabled bool `json:",omitempty"`
Internal bool `json:",omitempty"`
IPAMOptions *IPAMOptions `json:",omitempty"`
}
// NetworkAttachmentConfig represents the configuration of a network attachement.
type NetworkAttachmentConfig struct {
Target string `json:",omitempty"`
Aliases []string `json:",omitempty"`
}
// NetworkAttachment represents a network attchement.
type NetworkAttachment struct {
Network Network `json:",omitempty"`
Addresses []string `json:",omitempty"`
}
// IPAMOptions represents ipam options.
type IPAMOptions struct {
Driver Driver `json:",omitempty"`
Configs []IPAMConfig `json:",omitempty"`
}
// IPAMConfig represents ipam configuration.
type IPAMConfig struct {
Subnet string `json:",omitempty"`
Range string `json:",omitempty"`
Gateway string `json:",omitempty"`
}
// Driver represents a driver (network/volume).
type Driver struct {
Name string `json:",omitempty"`
Options map[string]string `json:",omitempty"`
}

118
types/swarm/node.go Normal file
Просмотреть файл

@ -0,0 +1,118 @@
package swarm
// Node represents a node.
type Node struct {
ID string
Meta
Spec NodeSpec `json:",omitempty"`
Description NodeDescription `json:",omitempty"`
Status NodeStatus `json:",omitempty"`
ManagerStatus *ManagerStatus `json:",omitempty"`
}
// NodeSpec represents the spec of a node.
type NodeSpec struct {
Annotations
Role NodeRole `json:",omitempty"`
Membership NodeMembership `json:",omitempty"`
Availability NodeAvailability `json:",omitempty"`
}
// NodeRole represents the role of a node.
type NodeRole string
const (
// NodeRoleWorker WORKER
NodeRoleWorker NodeRole = "worker"
// NodeRoleManager MANAGER
NodeRoleManager NodeRole = "manager"
)
// NodeMembership represents the membership of a node.
type NodeMembership string
const (
// NodeMembershipPending PENDING
NodeMembershipPending NodeMembership = "pending"
// NodeMembershipAccepted ACCEPTED
NodeMembershipAccepted NodeMembership = "accepted"
)
// NodeAvailability represents the availability of a node.
type NodeAvailability string
const (
// NodeAvailabilityActive ACTIVE
NodeAvailabilityActive NodeAvailability = "active"
// NodeAvailabilityPause PAUSE
NodeAvailabilityPause NodeAvailability = "pause"
// NodeAvailabilityDrain DRAIN
NodeAvailabilityDrain NodeAvailability = "drain"
)
// NodeDescription represents the description of a node.
type NodeDescription struct {
Hostname string `json:",omitempty"`
Platform Platform `json:",omitempty"`
Resources Resources `json:",omitempty"`
Engine EngineDescription `json:",omitempty"`
}
// Platform represents the platfrom (Arch/OS).
type Platform struct {
Architecture string `json:",omitempty"`
OS string `json:",omitempty"`
}
// EngineDescription represents the description of an engine.
type EngineDescription struct {
EngineVersion string `json:",omitempty"`
Labels map[string]string `json:",omitempty"`
Plugins []PluginDescription `json:",omitempty"`
}
// PluginDescription represents the description of an engine plugin.
type PluginDescription struct {
Type string `json:",omitempty"`
Name string `json:",omitempty"`
}
// NodeStatus represents the status of a node.
type NodeStatus struct {
State NodeState `json:",omitempty"`
Message string `json:",omitempty"`
}
// Reachability represents the reachability of a node.
type Reachability string
const (
// ReachabilityUnknown UNKNOWN
ReachabilityUnknown Reachability = "unknown"
// ReachabilityUnreachable UNREACHABLE
ReachabilityUnreachable Reachability = "unreachable"
// ReachabilityReachable REACHABLE
ReachabilityReachable Reachability = "reachable"
)
// ManagerStatus represents the status of a manager.
type ManagerStatus struct {
Leader bool `json:",omitempty"`
Reachability Reachability `json:",omitempty"`
Addr string `json:",omitempty"`
}
// NodeState represents the state of a node.
type NodeState string
const (
// NodeStateUnknown UNKNOWN
NodeStateUnknown NodeState = "unknown"
// NodeStateDown DOWN
NodeStateDown NodeState = "down"
// NodeStateReady READY
NodeStateReady NodeState = "ready"
// NodeStateDisconnected DISCONNECTED
NodeStateDisconnected NodeState = "disconnected"
)

44
types/swarm/service.go Normal file
Просмотреть файл

@ -0,0 +1,44 @@
package swarm
import "time"
// Service represents a service.
type Service struct {
ID string
Meta
Spec ServiceSpec `json:",omitempty"`
Endpoint Endpoint `json:",omitempty"`
}
// ServiceSpec represents the spec of a service.
type ServiceSpec struct {
Annotations
// TaskTemplate defines how the service should construct new tasks when
// ochestrating this service.
TaskTemplate TaskSpec `json:",omitempty"`
Mode ServiceMode `json:",omitempty"`
UpdateConfig *UpdateConfig `json:",omitempty"`
Networks []NetworkAttachmentConfig `json:",omitempty"`
EndpointSpec *EndpointSpec `json:",omitempty"`
}
// ServiceMode represents the mode of a service.
type ServiceMode struct {
Replicated *ReplicatedService `json:",omitempty"`
Global *GlobalService `json:",omitempty"`
}
// ReplicatedService is a kind of ServiceMode.
type ReplicatedService struct {
Replicas *uint64 `json:",omitempty"`
}
// GlobalService is a kind of ServiceMode.
type GlobalService struct{}
// UpdateConfig represents the update configuration.
type UpdateConfig struct {
Parallelism uint64 `json:",omitempty"`
Delay time.Duration `json:",omitempty"`
}

107
types/swarm/swarm.go Normal file
Просмотреть файл

@ -0,0 +1,107 @@
package swarm
import "time"
// Swarm represents a swarm.
type Swarm struct {
ID string
Meta
Spec Spec
}
// Spec represents the spec of a swarm.
type Spec struct {
Annotations
AcceptancePolicy AcceptancePolicy `json:",omitempty"`
Orchestration OrchestrationConfig `json:",omitempty"`
Raft RaftConfig `json:",omitempty"`
Dispatcher DispatcherConfig `json:",omitempty"`
CAConfig CAConfig `json:",omitempty"`
}
// AcceptancePolicy represents the list of policies.
type AcceptancePolicy struct {
Policies []Policy `json:",omitempty"`
}
// Policy represents a role, autoaccept and secret.
type Policy struct {
Role NodeRole
Autoaccept bool
Secret string `json:",omitempty"`
}
// OrchestrationConfig represents ochestration configuration.
type OrchestrationConfig struct {
TaskHistoryRetentionLimit int64 `json:",omitempty"`
}
// RaftConfig represents raft configuration.
type RaftConfig struct {
SnapshotInterval uint64 `json:",omitempty"`
KeepOldSnapshots uint64 `json:",omitempty"`
LogEntriesForSlowFollowers uint64 `json:",omitempty"`
HeartbeatTick uint32 `json:",omitempty"`
ElectionTick uint32 `json:",omitempty"`
}
// DispatcherConfig represents dispatcher configuration.
type DispatcherConfig struct {
HeartbeatPeriod uint64 `json:",omitempty"`
}
// CAConfig represents CA configuration.
type CAConfig struct {
NodeCertExpiry time.Duration `json:",omitempty"`
}
// InitRequest is the request used to init a swarm.
type InitRequest struct {
ListenAddr string
ForceNewCluster bool
Spec Spec
}
// JoinRequest is the request used to join a swarm.
type JoinRequest struct {
ListenAddr string
RemoteAddrs []string
Secret string // accept by secret
CACertHash string
Manager bool
}
// LocalNodeState represents the state of the local node.
type LocalNodeState string
const (
// LocalNodeStateInactive INACTIVE
LocalNodeStateInactive LocalNodeState = "inactive"
// LocalNodeStatePending PENDING
LocalNodeStatePending LocalNodeState = "pending"
// LocalNodeStateActive ACTIVE
LocalNodeStateActive LocalNodeState = "active"
// LocalNodeStateError ERROR
LocalNodeStateError LocalNodeState = "error"
)
// Info represents generic information about swarm.
type Info struct {
NodeID string
LocalNodeState LocalNodeState
ControlAvailable bool
Error string
RemoteManagers []Peer
Nodes int
Managers int
CACertHash string
}
// Peer represents a peer.
type Peer struct {
NodeID string
Addr string
}

110
types/swarm/task.go Normal file
Просмотреть файл

@ -0,0 +1,110 @@
package swarm
import "time"
// TaskState represents the state of a task.
type TaskState string
const (
// TaskStateNew NEW
TaskStateNew TaskState = "new"
// TaskStateAllocated ALLOCATED
TaskStateAllocated TaskState = "allocated"
// TaskStatePending PENDING
TaskStatePending TaskState = "pending"
// TaskStateAssigned ASSIGNED
TaskStateAssigned TaskState = "assigned"
// TaskStateAccepted ACCEPTED
TaskStateAccepted TaskState = "accepted"
// TaskStatePreparing PREPARING
TaskStatePreparing TaskState = "preparing"
// TaskStateReady READY
TaskStateReady TaskState = "ready"
// TaskStateStarting STARTING
TaskStateStarting TaskState = "starting"
// TaskStateRunning RUNNING
TaskStateRunning TaskState = "running"
// TaskStateComplete COMPLETE
TaskStateComplete TaskState = "complete"
// TaskStateShutdown SHUTDOWN
TaskStateShutdown TaskState = "shutdown"
// TaskStateFailed FAILED
TaskStateFailed TaskState = "failed"
// TaskStateRejected REJECTED
TaskStateRejected TaskState = "rejected"
)
// Task represents a task.
type Task struct {
ID string
Meta
Spec TaskSpec `json:",omitempty"`
ServiceID string `json:",omitempty"`
Slot int `json:",omitempty"`
NodeID string `json:",omitempty"`
Status TaskStatus `json:",omitempty"`
DesiredState TaskState `json:",omitempty"`
NetworksAttachments []NetworkAttachment `json:",omitempty"`
}
// TaskSpec represents the spec of a task.
type TaskSpec struct {
ContainerSpec ContainerSpec `json:",omitempty"`
Resources *ResourceRequirements `json:",omitempty"`
RestartPolicy *RestartPolicy `json:",omitempty"`
Placement *Placement `json:",omitempty"`
}
// Resources represents resources (CPU/Memory).
type Resources struct {
NanoCPUs int64 `json:",omitempty"`
MemoryBytes int64 `json:",omitempty"`
}
// ResourceRequirements represents resources requirements.
type ResourceRequirements struct {
Limits *Resources `json:",omitempty"`
Reservations *Resources `json:",omitempty"`
}
// Placement represents orchestration parameters.
type Placement struct {
Constraints []string `json:",omitempty"`
}
// RestartPolicy represents the restart policy.
type RestartPolicy struct {
Condition RestartPolicyCondition `json:",omitempty"`
Delay *time.Duration `json:",omitempty"`
MaxAttempts *uint64 `json:",omitempty"`
Window *time.Duration `json:",omitempty"`
}
// RestartPolicyCondition represents when to restart.
type RestartPolicyCondition string
const (
// RestartPolicyConditionNone NONE
RestartPolicyConditionNone RestartPolicyCondition = "none"
// RestartPolicyConditionOnFailure ON_FAILURE
RestartPolicyConditionOnFailure RestartPolicyCondition = "on_failure"
// RestartPolicyConditionAny ANY
RestartPolicyConditionAny RestartPolicyCondition = "any"
)
// TaskStatus represents the status of a task.
type TaskStatus struct {
Timestamp time.Time `json:",omitempty"`
State TaskState `json:",omitempty"`
Message string `json:",omitempty"`
Err string `json:",omitempty"`
ContainerStatus ContainerStatus `json:",omitempty"`
}
// ContainerStatus represents the status of a container.
type ContainerStatus struct {
ContainerID string `json:",omitempty"`
PID int `json:",omitempty"`
ExitCode int `json:",omitempty"`
}

Просмотреть файл

@ -7,6 +7,7 @@ import (
"github.com/docker/engine-api/types/container"
"github.com/docker/engine-api/types/network"
"github.com/docker/engine-api/types/registry"
"github.com/docker/engine-api/types/swarm"
"github.com/docker/go-connections/nat"
)
@ -254,6 +255,7 @@ type Info struct {
SecurityOptions []string
Runtimes map[string]Runtime
DefaultRuntime string
Swarm swarm.Info
}
// PluginsInfo is a temp struct holding Plugins name