1// Copyright 2016 go-dockerclient authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5package docker
6
7import (
8	"encoding/json"
9	"net/http"
10	"net/url"
11	"reflect"
12	"testing"
13
14	"github.com/docker/docker/api/types/swarm"
15)
16
17func TestListNodes(t *testing.T) {
18	t.Parallel()
19	jsonNodes := `[
20  {
21    "ID": "24ifsmvkjbyhk",
22    "Version": {
23      "Index": 8
24    },
25    "CreatedAt": "2016-06-07T20:31:11.853781916Z",
26    "UpdatedAt": "2016-06-07T20:31:11.999868824Z",
27    "Spec": {
28      "Name": "my-node",
29      "Role": "manager",
30      "Availability": "active",
31      "Labels": {
32          "foo": "bar"
33      }
34    },
35    "Description": {
36      "Hostname": "bf3067039e47",
37      "Platform": {
38        "Architecture": "x86_64",
39        "OS": "linux"
40      },
41      "Resources": {
42        "NanoCPUs": 4000000000,
43        "MemoryBytes": 8272408576
44      },
45      "Engine": {
46        "EngineVersion": "1.12.0-dev",
47        "Labels": {
48            "foo": "bar"
49        },
50        "Plugins": [
51          {
52            "Type": "Volume",
53            "Name": "local"
54          },
55          {
56            "Type": "Network",
57            "Name": "bridge"
58          },
59          {
60            "Type": "Network",
61            "Name": "null"
62          },
63          {
64            "Type": "Network",
65            "Name": "overlay"
66          }
67        ]
68      }
69    },
70    "Status": {
71      "State": "ready"
72    },
73    "ManagerStatus": {
74      "Leader": true,
75      "Reachability": "reachable",
76      "Addr": "172.17.0.2:2377"
77    }
78  }
79]`
80	var expected []swarm.Node
81	err := json.Unmarshal([]byte(jsonNodes), &expected)
82	if err != nil {
83		t.Fatal(err)
84	}
85	client := newTestClient(&FakeRoundTripper{message: jsonNodes, status: http.StatusOK})
86	nodes, err := client.ListNodes(ListNodesOptions{})
87	if err != nil {
88		t.Fatal(err)
89	}
90	if !reflect.DeepEqual(nodes, expected) {
91		t.Errorf("ListNodes: Expected %#v. Got %#v.", expected, nodes)
92	}
93
94}
95
96func TestInspectNode(t *testing.T) {
97	t.Parallel()
98	jsonNode := `{
99  "ID": "24ifsmvkjbyhk",
100  "Version": {
101    "Index": 8
102  },
103  "CreatedAt": "2016-06-07T20:31:11.853781916Z",
104  "UpdatedAt": "2016-06-07T20:31:11.999868824Z",
105  "Spec": {
106    "Name": "my-node",
107    "Role": "manager",
108    "Availability": "active",
109    "Labels": {
110        "foo": "bar"
111    }
112  },
113  "Description": {
114    "Hostname": "bf3067039e47",
115    "Platform": {
116      "Architecture": "x86_64",
117      "OS": "linux"
118    },
119    "Resources": {
120      "NanoCPUs": 4000000000,
121      "MemoryBytes": 8272408576
122    },
123    "Engine": {
124      "EngineVersion": "1.12.0-dev",
125      "Labels": {
126          "foo": "bar"
127      },
128      "Plugins": [
129        {
130          "Type": "Volume",
131          "Name": "local"
132        },
133        {
134          "Type": "Network",
135          "Name": "bridge"
136        },
137        {
138          "Type": "Network",
139          "Name": "null"
140        },
141        {
142          "Type": "Network",
143          "Name": "overlay"
144        }
145      ]
146    }
147  },
148  "Status": {
149    "State": "ready"
150  },
151  "ManagerStatus": {
152    "Leader": true,
153    "Reachability": "reachable",
154    "Addr": "172.17.0.2:2377"
155  }
156}`
157
158	var expected swarm.Node
159	err := json.Unmarshal([]byte(jsonNode), &expected)
160	if err != nil {
161		t.Fatal(err)
162	}
163	fakeRT := &FakeRoundTripper{message: jsonNode, status: http.StatusOK}
164	client := newTestClient(fakeRT)
165	id := "24ifsmvkjbyhk"
166	node, err := client.InspectNode(id)
167	if err != nil {
168		t.Fatal(err)
169	}
170	if !reflect.DeepEqual(*node, expected) {
171		t.Errorf("InspectNode(%q): Expected %#v. Got %#v.", id, expected, node)
172	}
173	expectedURL, _ := url.Parse(client.getURL("/nodes/24ifsmvkjbyhk"))
174	if gotPath := fakeRT.requests[0].URL.Path; gotPath != expectedURL.Path {
175		t.Errorf("InspectNode(%q): Wrong path in request. Want %q. Got %q.", id, expectedURL.Path, gotPath)
176	}
177
178}
179
180func TestInspectNodeNotFound(t *testing.T) {
181	t.Parallel()
182	client := newTestClient(&FakeRoundTripper{message: "no such node", status: http.StatusNotFound})
183	node, err := client.InspectNode("notfound")
184	if node != nil {
185		t.Errorf("InspectNode: Expected <nil> task, got %#v", node)
186	}
187	expected := &NoSuchNode{ID: "notfound"}
188	if !reflect.DeepEqual(err, expected) {
189		t.Errorf("InspectNode: Wrong error returned. Want %#v. Got %#v.", expected, err)
190	}
191}
192
193func TestUpdateNode(t *testing.T) {
194	t.Parallel()
195	fakeRT := &FakeRoundTripper{message: "", status: http.StatusOK}
196	client := newTestClient(fakeRT)
197	id := "4fa6e0f0c6786287e131c3852c58a2e01cc697a68231826813597e4994f1d6e2"
198	opts := UpdateNodeOptions{}
199	err := client.UpdateNode(id, opts)
200	if err != nil {
201		t.Fatal(err)
202	}
203	req := fakeRT.requests[0]
204	if req.Method != "POST" {
205		t.Errorf("UpdateNode: wrong HTTP method. Want %q. Got %q.", "POST", req.Method)
206	}
207	expectedURL, _ := url.Parse(client.getURL("/nodes/" + id + "/update"))
208	if gotPath := req.URL.Path; gotPath != expectedURL.Path {
209		t.Errorf("UpdateNode: Wrong path in request. Want %q. Got %q.", expectedURL.Path, gotPath)
210	}
211	expectedContentType := "application/json"
212	if contentType := req.Header.Get("Content-Type"); contentType != expectedContentType {
213		t.Errorf("UpdateNode: Wrong content-type in request. Want %q. Got %q.", expectedContentType, contentType)
214	}
215	var out UpdateNodeOptions
216	if err := json.NewDecoder(req.Body).Decode(&out); err != nil {
217		t.Fatal(err)
218	}
219	if !reflect.DeepEqual(out, opts) {
220		t.Errorf("UpdateNode: wrong body, got: %#v, want %#v", out, opts)
221	}
222}
223
224func TestUpdateNodeNotFound(t *testing.T) {
225	t.Parallel()
226	client := newTestClient(&FakeRoundTripper{message: "no such node", status: http.StatusNotFound})
227	err := client.UpdateNode("notfound", UpdateNodeOptions{})
228	expected := &NoSuchNode{ID: "notfound"}
229	if !reflect.DeepEqual(err, expected) {
230		t.Errorf("UpdateNode: Wrong error returned. Want %#v. Got %#v.", expected, err)
231	}
232}
233
234func TestRemoveNode(t *testing.T) {
235	t.Parallel()
236	fakeRT := &FakeRoundTripper{message: "", status: http.StatusOK}
237	client := newTestClient(fakeRT)
238	id := "4fa6e0f0c6786287e131c3852c58a2e01cc697a68231826813597e4994f1d6e2"
239	err := client.RemoveNode(RemoveNodeOptions{ID: id})
240	if err != nil {
241		t.Fatal(err)
242	}
243	req := fakeRT.requests[0]
244	if req.Method != "DELETE" {
245		t.Errorf("RemoveNode(%q): wrong HTTP method. Want %q. Got %q.", id, "DELETE", req.Method)
246	}
247	expectedURL, _ := url.Parse(client.getURL("/nodes/" + id))
248	if gotPath := req.URL.Path; gotPath != expectedURL.Path {
249		t.Errorf("RemoveNode(%q): Wrong path in request. Want %q. Got %q.", id, expectedURL.Path, gotPath)
250	}
251}
252
253func TestRemoveNodeNotFound(t *testing.T) {
254	t.Parallel()
255	client := newTestClient(&FakeRoundTripper{message: "no such node", status: http.StatusNotFound})
256	err := client.RemoveNode(RemoveNodeOptions{ID: "notfound"})
257	expected := &NoSuchNode{ID: "notfound"}
258	if !reflect.DeepEqual(err, expected) {
259		t.Errorf("RemoveNode: Wrong error returned. Want %#v. Got %#v.", expected, err)
260	}
261}
262