Merge pull request #51260 from vvoland/client-node-opts

client/node: Wrap options and output
This commit is contained in:
Paweł Gronowski
2025-10-22 13:09:21 +02:00
committed by GitHub
22 changed files with 145 additions and 76 deletions

View File

@@ -137,10 +137,10 @@ type NetworkAPIClient interface {
// NodeAPIClient defines API client methods for the nodes // NodeAPIClient defines API client methods for the nodes
type NodeAPIClient interface { type NodeAPIClient interface {
NodeInspectWithRaw(ctx context.Context, nodeID string) (swarm.Node, []byte, error) NodeInspect(ctx context.Context, nodeID string, options NodeInspectOptions) (NodeInspectResult, error)
NodeList(ctx context.Context, options NodeListOptions) ([]swarm.Node, error) NodeList(ctx context.Context, options NodeListOptions) (NodeListResult, error)
NodeRemove(ctx context.Context, nodeID string, options NodeRemoveOptions) error NodeRemove(ctx context.Context, nodeID string, options NodeRemoveOptions) (NodeRemoveResult, error)
NodeUpdate(ctx context.Context, nodeID string, version swarm.Version, node swarm.NodeSpec) error NodeUpdate(ctx context.Context, nodeID string, options NodeUpdateOptions) (NodeUpdateResult, error)
} }
// PluginAPIClient defines API client methods for the plugins // PluginAPIClient defines API client methods for the plugins

View File

@@ -9,25 +9,30 @@ import (
"github.com/moby/moby/api/types/swarm" "github.com/moby/moby/api/types/swarm"
) )
// NodeInspectWithRaw returns the node information. type NodeInspectResult struct {
func (cli *Client) NodeInspectWithRaw(ctx context.Context, nodeID string) (swarm.Node, []byte, error) { Node swarm.Node
Raw []byte
}
// NodeInspect returns the node information.
func (cli *Client) NodeInspect(ctx context.Context, nodeID string, options NodeInspectOptions) (NodeInspectResult, error) {
nodeID, err := trimID("node", nodeID) nodeID, err := trimID("node", nodeID)
if err != nil { if err != nil {
return swarm.Node{}, nil, err return NodeInspectResult{}, err
} }
resp, err := cli.get(ctx, "/nodes/"+nodeID, nil, nil) resp, err := cli.get(ctx, "/nodes/"+nodeID, nil, nil)
defer ensureReaderClosed(resp) defer ensureReaderClosed(resp)
if err != nil { if err != nil {
return swarm.Node{}, nil, err return NodeInspectResult{}, err
} }
body, err := io.ReadAll(resp.Body) body, err := io.ReadAll(resp.Body)
if err != nil { if err != nil {
return swarm.Node{}, nil, err return NodeInspectResult{}, err
} }
var response swarm.Node var response swarm.Node
rdr := bytes.NewReader(body) rdr := bytes.NewReader(body)
err = json.NewDecoder(rdr).Decode(&response) err = json.NewDecoder(rdr).Decode(&response)
return response, body, err return NodeInspectResult{Node: response, Raw: body}, err
} }

View File

@@ -19,7 +19,7 @@ func TestNodeInspectError(t *testing.T) {
client, err := NewClientWithOpts(WithMockClient(errorMock(http.StatusInternalServerError, "Server error"))) client, err := NewClientWithOpts(WithMockClient(errorMock(http.StatusInternalServerError, "Server error")))
assert.NilError(t, err) assert.NilError(t, err)
_, _, err = client.NodeInspectWithRaw(context.Background(), "nothing") _, err = client.NodeInspect(context.Background(), "nothing", NodeInspectOptions{})
assert.Check(t, is.ErrorType(err, cerrdefs.IsInternal)) assert.Check(t, is.ErrorType(err, cerrdefs.IsInternal))
} }
@@ -27,7 +27,7 @@ func TestNodeInspectNodeNotFound(t *testing.T) {
client, err := NewClientWithOpts(WithMockClient(errorMock(http.StatusNotFound, "Server error"))) client, err := NewClientWithOpts(WithMockClient(errorMock(http.StatusNotFound, "Server error")))
assert.NilError(t, err) assert.NilError(t, err)
_, _, err = client.NodeInspectWithRaw(context.Background(), "unknown") _, err = client.NodeInspect(context.Background(), "unknown", NodeInspectOptions{})
assert.Check(t, is.ErrorType(err, cerrdefs.IsNotFound)) assert.Check(t, is.ErrorType(err, cerrdefs.IsNotFound))
} }
@@ -36,11 +36,11 @@ func TestNodeInspectWithEmptyID(t *testing.T) {
return nil, errors.New("should not make request") return nil, errors.New("should not make request")
})) }))
assert.NilError(t, err) assert.NilError(t, err)
_, _, err = client.NodeInspectWithRaw(context.Background(), "") _, err = client.NodeInspect(context.Background(), "", NodeInspectOptions{})
assert.Check(t, is.ErrorType(err, cerrdefs.IsInvalidArgument)) assert.Check(t, is.ErrorType(err, cerrdefs.IsInvalidArgument))
assert.Check(t, is.ErrorContains(err, "value is empty")) assert.Check(t, is.ErrorContains(err, "value is empty"))
_, _, err = client.NodeInspectWithRaw(context.Background(), " ") _, err = client.NodeInspect(context.Background(), " ", NodeInspectOptions{})
assert.Check(t, is.ErrorType(err, cerrdefs.IsInvalidArgument)) assert.Check(t, is.ErrorType(err, cerrdefs.IsInvalidArgument))
assert.Check(t, is.ErrorContains(err, "value is empty")) assert.Check(t, is.ErrorContains(err, "value is empty"))
} }
@@ -64,7 +64,7 @@ func TestNodeInspect(t *testing.T) {
})) }))
assert.NilError(t, err) assert.NilError(t, err)
nodeInspect, _, err := client.NodeInspectWithRaw(context.Background(), "node_id") result, err := client.NodeInspect(context.Background(), "node_id", NodeInspectOptions{})
assert.NilError(t, err) assert.NilError(t, err)
assert.Check(t, is.Equal(nodeInspect.ID, "node_id")) assert.Check(t, is.Equal(result.Node.ID, "node_id"))
} }

View File

@@ -8,17 +8,21 @@ import (
"github.com/moby/moby/api/types/swarm" "github.com/moby/moby/api/types/swarm"
) )
type NodeListResult struct {
Items []swarm.Node
}
// NodeList returns the list of nodes. // NodeList returns the list of nodes.
func (cli *Client) NodeList(ctx context.Context, options NodeListOptions) ([]swarm.Node, error) { func (cli *Client) NodeList(ctx context.Context, options NodeListOptions) (NodeListResult, error) {
query := url.Values{} query := url.Values{}
options.Filters.updateURLValues(query) options.Filters.updateURLValues(query)
resp, err := cli.get(ctx, "/nodes", query, nil) resp, err := cli.get(ctx, "/nodes", query, nil)
defer ensureReaderClosed(resp) defer ensureReaderClosed(resp)
if err != nil { if err != nil {
return nil, err return NodeListResult{}, err
} }
var nodes []swarm.Node var nodes []swarm.Node
err = json.NewDecoder(resp.Body).Decode(&nodes) err = json.NewDecoder(resp.Body).Decode(&nodes)
return nodes, err return NodeListResult{Items: nodes}, err
} }

View File

@@ -76,8 +76,8 @@ func TestNodeList(t *testing.T) {
})) }))
assert.NilError(t, err) assert.NilError(t, err)
nodes, err := client.NodeList(context.Background(), listCase.options) result, err := client.NodeList(context.Background(), listCase.options)
assert.NilError(t, err) assert.NilError(t, err)
assert.Check(t, is.Len(nodes, 2)) assert.Check(t, is.Len(result.Items, 2))
} }
} }

View File

@@ -5,11 +5,14 @@ import (
"net/url" "net/url"
) )
type NodeRemoveResult struct {
}
// NodeRemove removes a Node. // NodeRemove removes a Node.
func (cli *Client) NodeRemove(ctx context.Context, nodeID string, options NodeRemoveOptions) error { func (cli *Client) NodeRemove(ctx context.Context, nodeID string, options NodeRemoveOptions) (NodeRemoveResult, error) {
nodeID, err := trimID("node", nodeID) nodeID, err := trimID("node", nodeID)
if err != nil { if err != nil {
return err return NodeRemoveResult{}, err
} }
query := url.Values{} query := url.Values{}
@@ -19,5 +22,5 @@ func (cli *Client) NodeRemove(ctx context.Context, nodeID string, options NodeRe
resp, err := cli.delete(ctx, "/nodes/"+nodeID, query, nil) resp, err := cli.delete(ctx, "/nodes/"+nodeID, query, nil)
defer ensureReaderClosed(resp) defer ensureReaderClosed(resp)
return err return NodeRemoveResult{}, err
} }

View File

@@ -17,14 +17,14 @@ func TestNodeRemoveError(t *testing.T) {
client, err := NewClientWithOpts(WithMockClient(errorMock(http.StatusInternalServerError, "Server error"))) client, err := NewClientWithOpts(WithMockClient(errorMock(http.StatusInternalServerError, "Server error")))
assert.NilError(t, err) assert.NilError(t, err)
err = client.NodeRemove(context.Background(), "node_id", NodeRemoveOptions{Force: false}) _, err = client.NodeRemove(context.Background(), "node_id", NodeRemoveOptions{Force: false})
assert.Check(t, is.ErrorType(err, cerrdefs.IsInternal)) assert.Check(t, is.ErrorType(err, cerrdefs.IsInternal))
err = client.NodeRemove(context.Background(), "", NodeRemoveOptions{Force: false}) _, err = client.NodeRemove(context.Background(), "", NodeRemoveOptions{Force: false})
assert.Check(t, is.ErrorType(err, cerrdefs.IsInvalidArgument)) assert.Check(t, is.ErrorType(err, cerrdefs.IsInvalidArgument))
assert.Check(t, is.ErrorContains(err, "value is empty")) assert.Check(t, is.ErrorContains(err, "value is empty"))
err = client.NodeRemove(context.Background(), " ", NodeRemoveOptions{Force: false}) _, err = client.NodeRemove(context.Background(), " ", NodeRemoveOptions{Force: false})
assert.Check(t, is.ErrorType(err, cerrdefs.IsInvalidArgument)) assert.Check(t, is.ErrorType(err, cerrdefs.IsInvalidArgument))
assert.Check(t, is.ErrorContains(err, "value is empty")) assert.Check(t, is.ErrorContains(err, "value is empty"))
} }
@@ -62,7 +62,7 @@ func TestNodeRemove(t *testing.T) {
})) }))
assert.NilError(t, err) assert.NilError(t, err)
err = client.NodeRemove(context.Background(), "node_id", NodeRemoveOptions{Force: removeCase.force}) _, err = client.NodeRemove(context.Background(), "node_id", NodeRemoveOptions{Force: removeCase.force})
assert.NilError(t, err) assert.NilError(t, err)
} }
} }

View File

@@ -3,20 +3,21 @@ package client
import ( import (
"context" "context"
"net/url" "net/url"
"github.com/moby/moby/api/types/swarm"
) )
type NodeUpdateResult struct {
}
// NodeUpdate updates a Node. // NodeUpdate updates a Node.
func (cli *Client) NodeUpdate(ctx context.Context, nodeID string, version swarm.Version, node swarm.NodeSpec) error { func (cli *Client) NodeUpdate(ctx context.Context, nodeID string, options NodeUpdateOptions) (NodeUpdateResult, error) {
nodeID, err := trimID("node", nodeID) nodeID, err := trimID("node", nodeID)
if err != nil { if err != nil {
return err return NodeUpdateResult{}, err
} }
query := url.Values{} query := url.Values{}
query.Set("version", version.String()) query.Set("version", options.Version.String())
resp, err := cli.post(ctx, "/nodes/"+nodeID+"/update", query, node, nil) resp, err := cli.post(ctx, "/nodes/"+nodeID+"/update", query, options.Node, nil)
defer ensureReaderClosed(resp) defer ensureReaderClosed(resp)
return err return NodeUpdateResult{}, err
} }

View File

@@ -17,14 +17,23 @@ func TestNodeUpdateError(t *testing.T) {
client, err := NewClientWithOpts(WithMockClient(errorMock(http.StatusInternalServerError, "Server error"))) client, err := NewClientWithOpts(WithMockClient(errorMock(http.StatusInternalServerError, "Server error")))
assert.NilError(t, err) assert.NilError(t, err)
err = client.NodeUpdate(context.Background(), "node_id", swarm.Version{}, swarm.NodeSpec{}) _, err = client.NodeUpdate(context.Background(), "node_id", NodeUpdateOptions{
Version: swarm.Version{},
Node: swarm.NodeSpec{},
})
assert.Check(t, is.ErrorType(err, cerrdefs.IsInternal)) assert.Check(t, is.ErrorType(err, cerrdefs.IsInternal))
err = client.NodeUpdate(context.Background(), "", swarm.Version{}, swarm.NodeSpec{}) _, err = client.NodeUpdate(context.Background(), "", NodeUpdateOptions{
Version: swarm.Version{},
Node: swarm.NodeSpec{},
})
assert.Check(t, is.ErrorType(err, cerrdefs.IsInvalidArgument)) assert.Check(t, is.ErrorType(err, cerrdefs.IsInvalidArgument))
assert.Check(t, is.ErrorContains(err, "value is empty")) assert.Check(t, is.ErrorContains(err, "value is empty"))
err = client.NodeUpdate(context.Background(), " ", swarm.Version{}, swarm.NodeSpec{}) _, err = client.NodeUpdate(context.Background(), " ", NodeUpdateOptions{
Version: swarm.Version{},
Node: swarm.NodeSpec{},
})
assert.Check(t, is.ErrorType(err, cerrdefs.IsInvalidArgument)) assert.Check(t, is.ErrorType(err, cerrdefs.IsInvalidArgument))
assert.Check(t, is.ErrorContains(err, "value is empty")) assert.Check(t, is.ErrorContains(err, "value is empty"))
} }
@@ -43,6 +52,9 @@ func TestNodeUpdate(t *testing.T) {
})) }))
assert.NilError(t, err) assert.NilError(t, err)
err = client.NodeUpdate(context.Background(), "node_id", swarm.Version{}, swarm.NodeSpec{}) _, err = client.NodeUpdate(context.Background(), "node_id", NodeUpdateOptions{
Version: swarm.Version{},
Node: swarm.NodeSpec{},
})
assert.NilError(t, err) assert.NilError(t, err)
} }

View File

@@ -0,0 +1,5 @@
package client
// NodeInspectOptions holds parameters to inspect nodes with.
type NodeInspectOptions struct {
}

View File

@@ -0,0 +1,9 @@
package client
import "github.com/moby/moby/api/types/swarm"
// NodeUpdateOptions holds parameters to update nodes with.
type NodeUpdateOptions struct {
Version swarm.Version
Node swarm.NodeSpec
}

View File

@@ -178,12 +178,12 @@ func (d *Daemon) CheckLeader(ctx context.Context) func(t *testing.T) (any, strin
errList := "could not get node list" errList := "could not get node list"
ls, err := cli.NodeList(ctx, client.NodeListOptions{}) result, err := cli.NodeList(ctx, client.NodeListOptions{})
if err != nil { if err != nil {
return err, errList return err, errList
} }
for _, node := range ls { for _, node := range result.Items {
if node.ManagerStatus != nil && node.ManagerStatus.Leader { if node.ManagerStatus != nil && node.ManagerStatus.Leader {
return nil, "" return nil, ""
} }

View File

@@ -159,13 +159,13 @@ func JobComplete(ctx context.Context, apiClient client.ServiceAPIClient, service
func HasLeader(ctx context.Context, apiClient client.NodeAPIClient) func(log poll.LogT) poll.Result { func HasLeader(ctx context.Context, apiClient client.NodeAPIClient) func(log poll.LogT) poll.Result {
return func(log poll.LogT) poll.Result { return func(log poll.LogT) poll.Result {
nodes, err := apiClient.NodeList(ctx, client.NodeListOptions{ result, err := apiClient.NodeList(ctx, client.NodeListOptions{
Filters: make(client.Filters).Add("role", "manager"), Filters: make(client.Filters).Add("role", "manager"),
}) })
if err != nil { if err != nil {
return poll.Error(err) return poll.Error(err)
} }
for _, node := range nodes { for _, node := range result.Items {
if node.ManagerStatus != nil && node.ManagerStatus.Leader { if node.ManagerStatus != nil && node.ManagerStatus.Leader {
return poll.Success() return poll.Success()
} }

View File

@@ -165,11 +165,11 @@ func TestInspectNetwork(t *testing.T) {
t.Run("BeforeLeaderChange", checkNetworkInspect) t.Run("BeforeLeaderChange", checkNetworkInspect)
leaderID := func() string { leaderID := func() string {
ls, err := c1.NodeList(ctx, client.NodeListOptions{ result, err := c1.NodeList(ctx, client.NodeListOptions{
Filters: make(client.Filters).Add("role", "manager"), Filters: make(client.Filters).Add("role", "manager"),
}) })
assert.NilError(t, err) assert.NilError(t, err)
for _, node := range ls { for _, node := range result.Items {
if node.ManagerStatus != nil && node.ManagerStatus.Leader { if node.ManagerStatus != nil && node.ManagerStatus.Leader {
return node.ID return node.ID
} }

View File

@@ -20,7 +20,7 @@ func (d *Daemon) GetNode(ctx context.Context, t testing.TB, id string, errCheck
cli := d.NewClientT(t) cli := d.NewClientT(t)
defer cli.Close() defer cli.Close()
node, _, err := cli.NodeInspectWithRaw(ctx, id) result, err := cli.NodeInspect(ctx, id, client.NodeInspectOptions{})
if err != nil { if err != nil {
for _, f := range errCheck { for _, f := range errCheck {
if f(err) { if f(err) {
@@ -28,9 +28,9 @@ func (d *Daemon) GetNode(ctx context.Context, t testing.TB, id string, errCheck
} }
} }
} }
assert.NilError(t, err, "[%s] (*Daemon).GetNode: NodeInspectWithRaw(%q) failed", d.id, id) assert.NilError(t, err, "[%s] (*Daemon).GetNode: NodeInspect(%q) failed", d.id, id)
assert.Check(t, node.ID == id) assert.Check(t, result.Node.ID == id)
return &node return &result.Node
} }
// RemoveNode removes the specified node // RemoveNode removes the specified node
@@ -42,7 +42,7 @@ func (d *Daemon) RemoveNode(ctx context.Context, t testing.TB, id string, force
options := client.NodeRemoveOptions{ options := client.NodeRemoveOptions{
Force: force, Force: force,
} }
err := cli.NodeRemove(ctx, id, options) _, err := cli.NodeRemove(ctx, id, options)
assert.NilError(t, err) assert.NilError(t, err)
} }
@@ -58,7 +58,10 @@ func (d *Daemon) UpdateNode(ctx context.Context, t testing.TB, id string, f ...N
fn(node) fn(node)
} }
err := cli.NodeUpdate(ctx, node.ID, node.Version, node.Spec) _, err := cli.NodeUpdate(ctx, node.ID, client.NodeUpdateOptions{
Version: node.Version,
Node: node.Spec,
})
if i < 10 && err != nil && strings.Contains(err.Error(), "update out of sequence") { if i < 10 && err != nil && strings.Contains(err.Error(), "update out of sequence") {
time.Sleep(100 * time.Millisecond) time.Sleep(100 * time.Millisecond)
continue continue
@@ -74,8 +77,8 @@ func (d *Daemon) ListNodes(ctx context.Context, t testing.TB) []swarm.Node {
cli := d.NewClientT(t) cli := d.NewClientT(t)
defer cli.Close() defer cli.Close()
nodes, err := cli.NodeList(ctx, client.NodeListOptions{}) result, err := cli.NodeList(ctx, client.NodeListOptions{})
assert.NilError(t, err) assert.NilError(t, err)
return nodes return result.Items
} }

View File

@@ -137,10 +137,10 @@ type NetworkAPIClient interface {
// NodeAPIClient defines API client methods for the nodes // NodeAPIClient defines API client methods for the nodes
type NodeAPIClient interface { type NodeAPIClient interface {
NodeInspectWithRaw(ctx context.Context, nodeID string) (swarm.Node, []byte, error) NodeInspect(ctx context.Context, nodeID string, options NodeInspectOptions) (NodeInspectResult, error)
NodeList(ctx context.Context, options NodeListOptions) ([]swarm.Node, error) NodeList(ctx context.Context, options NodeListOptions) (NodeListResult, error)
NodeRemove(ctx context.Context, nodeID string, options NodeRemoveOptions) error NodeRemove(ctx context.Context, nodeID string, options NodeRemoveOptions) (NodeRemoveResult, error)
NodeUpdate(ctx context.Context, nodeID string, version swarm.Version, node swarm.NodeSpec) error NodeUpdate(ctx context.Context, nodeID string, options NodeUpdateOptions) (NodeUpdateResult, error)
} }
// PluginAPIClient defines API client methods for the plugins // PluginAPIClient defines API client methods for the plugins

View File

@@ -9,25 +9,30 @@ import (
"github.com/moby/moby/api/types/swarm" "github.com/moby/moby/api/types/swarm"
) )
// NodeInspectWithRaw returns the node information. type NodeInspectResult struct {
func (cli *Client) NodeInspectWithRaw(ctx context.Context, nodeID string) (swarm.Node, []byte, error) { Node swarm.Node
Raw []byte
}
// NodeInspect returns the node information.
func (cli *Client) NodeInspect(ctx context.Context, nodeID string, options NodeInspectOptions) (NodeInspectResult, error) {
nodeID, err := trimID("node", nodeID) nodeID, err := trimID("node", nodeID)
if err != nil { if err != nil {
return swarm.Node{}, nil, err return NodeInspectResult{}, err
} }
resp, err := cli.get(ctx, "/nodes/"+nodeID, nil, nil) resp, err := cli.get(ctx, "/nodes/"+nodeID, nil, nil)
defer ensureReaderClosed(resp) defer ensureReaderClosed(resp)
if err != nil { if err != nil {
return swarm.Node{}, nil, err return NodeInspectResult{}, err
} }
body, err := io.ReadAll(resp.Body) body, err := io.ReadAll(resp.Body)
if err != nil { if err != nil {
return swarm.Node{}, nil, err return NodeInspectResult{}, err
} }
var response swarm.Node var response swarm.Node
rdr := bytes.NewReader(body) rdr := bytes.NewReader(body)
err = json.NewDecoder(rdr).Decode(&response) err = json.NewDecoder(rdr).Decode(&response)
return response, body, err return NodeInspectResult{Node: response, Raw: body}, err
} }

View File

@@ -8,17 +8,21 @@ import (
"github.com/moby/moby/api/types/swarm" "github.com/moby/moby/api/types/swarm"
) )
type NodeListResult struct {
Items []swarm.Node
}
// NodeList returns the list of nodes. // NodeList returns the list of nodes.
func (cli *Client) NodeList(ctx context.Context, options NodeListOptions) ([]swarm.Node, error) { func (cli *Client) NodeList(ctx context.Context, options NodeListOptions) (NodeListResult, error) {
query := url.Values{} query := url.Values{}
options.Filters.updateURLValues(query) options.Filters.updateURLValues(query)
resp, err := cli.get(ctx, "/nodes", query, nil) resp, err := cli.get(ctx, "/nodes", query, nil)
defer ensureReaderClosed(resp) defer ensureReaderClosed(resp)
if err != nil { if err != nil {
return nil, err return NodeListResult{}, err
} }
var nodes []swarm.Node var nodes []swarm.Node
err = json.NewDecoder(resp.Body).Decode(&nodes) err = json.NewDecoder(resp.Body).Decode(&nodes)
return nodes, err return NodeListResult{Items: nodes}, err
} }

View File

@@ -5,11 +5,14 @@ import (
"net/url" "net/url"
) )
type NodeRemoveResult struct {
}
// NodeRemove removes a Node. // NodeRemove removes a Node.
func (cli *Client) NodeRemove(ctx context.Context, nodeID string, options NodeRemoveOptions) error { func (cli *Client) NodeRemove(ctx context.Context, nodeID string, options NodeRemoveOptions) (NodeRemoveResult, error) {
nodeID, err := trimID("node", nodeID) nodeID, err := trimID("node", nodeID)
if err != nil { if err != nil {
return err return NodeRemoveResult{}, err
} }
query := url.Values{} query := url.Values{}
@@ -19,5 +22,5 @@ func (cli *Client) NodeRemove(ctx context.Context, nodeID string, options NodeRe
resp, err := cli.delete(ctx, "/nodes/"+nodeID, query, nil) resp, err := cli.delete(ctx, "/nodes/"+nodeID, query, nil)
defer ensureReaderClosed(resp) defer ensureReaderClosed(resp)
return err return NodeRemoveResult{}, err
} }

View File

@@ -3,20 +3,21 @@ package client
import ( import (
"context" "context"
"net/url" "net/url"
"github.com/moby/moby/api/types/swarm"
) )
type NodeUpdateResult struct {
}
// NodeUpdate updates a Node. // NodeUpdate updates a Node.
func (cli *Client) NodeUpdate(ctx context.Context, nodeID string, version swarm.Version, node swarm.NodeSpec) error { func (cli *Client) NodeUpdate(ctx context.Context, nodeID string, options NodeUpdateOptions) (NodeUpdateResult, error) {
nodeID, err := trimID("node", nodeID) nodeID, err := trimID("node", nodeID)
if err != nil { if err != nil {
return err return NodeUpdateResult{}, err
} }
query := url.Values{} query := url.Values{}
query.Set("version", version.String()) query.Set("version", options.Version.String())
resp, err := cli.post(ctx, "/nodes/"+nodeID+"/update", query, node, nil) resp, err := cli.post(ctx, "/nodes/"+nodeID+"/update", query, options.Node, nil)
defer ensureReaderClosed(resp) defer ensureReaderClosed(resp)
return err return NodeUpdateResult{}, err
} }

View File

@@ -0,0 +1,5 @@
package client
// NodeInspectOptions holds parameters to inspect nodes with.
type NodeInspectOptions struct {
}

View File

@@ -0,0 +1,9 @@
package client
import "github.com/moby/moby/api/types/swarm"
// NodeUpdateOptions holds parameters to update nodes with.
type NodeUpdateOptions struct {
Version swarm.Version
Node swarm.NodeSpec
}