github.com/itscaro/cli@v0.0.0-20190705081621-c9db0fe93829/cli/command/node/formatter_test.go (about) 1 package node 2 3 import ( 4 "bytes" 5 "encoding/json" 6 "fmt" 7 "strings" 8 "testing" 9 10 "github.com/docker/cli/cli/command/formatter" 11 "github.com/docker/cli/internal/test" 12 "github.com/docker/docker/api/types" 13 "github.com/docker/docker/api/types/swarm" 14 "github.com/docker/docker/pkg/stringid" 15 "gotest.tools/assert" 16 is "gotest.tools/assert/cmp" 17 ) 18 19 func TestNodeContext(t *testing.T) { 20 nodeID := stringid.GenerateRandomID() 21 22 var ctx nodeContext 23 cases := []struct { 24 nodeCtx nodeContext 25 expValue string 26 call func() string 27 }{ 28 {nodeContext{ 29 n: swarm.Node{ID: nodeID}, 30 }, nodeID, ctx.ID}, 31 {nodeContext{ 32 n: swarm.Node{Description: swarm.NodeDescription{Hostname: "node_hostname"}}, 33 }, "node_hostname", ctx.Hostname}, 34 {nodeContext{ 35 n: swarm.Node{Status: swarm.NodeStatus{State: swarm.NodeState("foo")}}, 36 }, "Foo", ctx.Status}, 37 {nodeContext{ 38 n: swarm.Node{Spec: swarm.NodeSpec{Availability: swarm.NodeAvailability("drain")}}, 39 }, "Drain", ctx.Availability}, 40 {nodeContext{ 41 n: swarm.Node{ManagerStatus: &swarm.ManagerStatus{Leader: true}}, 42 }, "Leader", ctx.ManagerStatus}, 43 } 44 45 for _, c := range cases { 46 ctx = c.nodeCtx 47 v := c.call() 48 if strings.Contains(v, ",") { 49 test.CompareMultipleValues(t, v, c.expValue) 50 } else if v != c.expValue { 51 t.Fatalf("Expected %s, was %s\n", c.expValue, v) 52 } 53 } 54 } 55 56 func TestNodeContextWrite(t *testing.T) { 57 cases := []struct { 58 context formatter.Context 59 expected string 60 clusterInfo swarm.ClusterInfo 61 }{ 62 63 // Errors 64 { 65 context: formatter.Context{Format: "{{InvalidFunction}}"}, 66 expected: `Template parsing error: template: :1: function "InvalidFunction" not defined 67 `, 68 clusterInfo: swarm.ClusterInfo{TLSInfo: swarm.TLSInfo{TrustRoot: "hi"}}, 69 }, 70 { 71 context: formatter.Context{Format: "{{nil}}"}, 72 expected: `Template parsing error: template: :1:2: executing "" at <nil>: nil is not a command 73 `, 74 clusterInfo: swarm.ClusterInfo{TLSInfo: swarm.TLSInfo{TrustRoot: "hi"}}, 75 }, 76 // Table format 77 { 78 context: formatter.Context{Format: NewFormat("table", false)}, 79 expected: `ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS ENGINE VERSION 80 nodeID1 foobar_baz Foo Drain Leader 18.03.0-ce 81 nodeID2 foobar_bar Bar Active Reachable 1.2.3 82 nodeID3 foobar_boo Boo Active ` + "\n", // (to preserve whitespace) 83 clusterInfo: swarm.ClusterInfo{TLSInfo: swarm.TLSInfo{TrustRoot: "hi"}}, 84 }, 85 { 86 context: formatter.Context{Format: NewFormat("table", true)}, 87 expected: `nodeID1 88 nodeID2 89 nodeID3 90 `, 91 clusterInfo: swarm.ClusterInfo{TLSInfo: swarm.TLSInfo{TrustRoot: "hi"}}, 92 }, 93 { 94 context: formatter.Context{Format: NewFormat("table {{.Hostname}}", false)}, 95 expected: `HOSTNAME 96 foobar_baz 97 foobar_bar 98 foobar_boo 99 `, 100 clusterInfo: swarm.ClusterInfo{TLSInfo: swarm.TLSInfo{TrustRoot: "hi"}}, 101 }, 102 { 103 context: formatter.Context{Format: NewFormat("table {{.Hostname}}", true)}, 104 expected: `HOSTNAME 105 foobar_baz 106 foobar_bar 107 foobar_boo 108 `, 109 clusterInfo: swarm.ClusterInfo{TLSInfo: swarm.TLSInfo{TrustRoot: "hi"}}, 110 }, 111 { 112 context: formatter.Context{Format: NewFormat("table {{.ID}}\t{{.Hostname}}\t{{.TLSStatus}}", false)}, 113 expected: `ID HOSTNAME TLS STATUS 114 nodeID1 foobar_baz Needs Rotation 115 nodeID2 foobar_bar Ready 116 nodeID3 foobar_boo Unknown 117 `, 118 clusterInfo: swarm.ClusterInfo{TLSInfo: swarm.TLSInfo{TrustRoot: "hi"}}, 119 }, 120 { // no cluster TLS status info, TLS status for all nodes is unknown 121 context: formatter.Context{Format: NewFormat("table {{.ID}}\t{{.Hostname}}\t{{.TLSStatus}}", false)}, 122 expected: `ID HOSTNAME TLS STATUS 123 nodeID1 foobar_baz Unknown 124 nodeID2 foobar_bar Unknown 125 nodeID3 foobar_boo Unknown 126 `, 127 clusterInfo: swarm.ClusterInfo{}, 128 }, 129 // Raw Format 130 { 131 context: formatter.Context{Format: NewFormat("raw", false)}, 132 expected: `node_id: nodeID1 133 hostname: foobar_baz 134 status: Foo 135 availability: Drain 136 manager_status: Leader 137 138 node_id: nodeID2 139 hostname: foobar_bar 140 status: Bar 141 availability: Active 142 manager_status: Reachable 143 144 node_id: nodeID3 145 hostname: foobar_boo 146 status: Boo 147 availability: Active 148 manager_status: ` + "\n\n", // to preserve whitespace 149 clusterInfo: swarm.ClusterInfo{TLSInfo: swarm.TLSInfo{TrustRoot: "hi"}}, 150 }, 151 { 152 context: formatter.Context{Format: NewFormat("raw", true)}, 153 expected: `node_id: nodeID1 154 node_id: nodeID2 155 node_id: nodeID3 156 `, 157 clusterInfo: swarm.ClusterInfo{TLSInfo: swarm.TLSInfo{TrustRoot: "hi"}}, 158 }, 159 // Custom Format 160 { 161 context: formatter.Context{Format: NewFormat("{{.Hostname}} {{.TLSStatus}}", false)}, 162 expected: `foobar_baz Needs Rotation 163 foobar_bar Ready 164 foobar_boo Unknown 165 `, 166 clusterInfo: swarm.ClusterInfo{TLSInfo: swarm.TLSInfo{TrustRoot: "hi"}}, 167 }, 168 } 169 170 for _, testcase := range cases { 171 nodes := []swarm.Node{ 172 { 173 ID: "nodeID1", 174 Description: swarm.NodeDescription{ 175 Hostname: "foobar_baz", 176 TLSInfo: swarm.TLSInfo{TrustRoot: "no"}, 177 Engine: swarm.EngineDescription{EngineVersion: "18.03.0-ce"}, 178 }, 179 Status: swarm.NodeStatus{State: swarm.NodeState("foo")}, 180 Spec: swarm.NodeSpec{Availability: swarm.NodeAvailability("drain")}, 181 ManagerStatus: &swarm.ManagerStatus{Leader: true}, 182 }, 183 { 184 ID: "nodeID2", 185 Description: swarm.NodeDescription{ 186 Hostname: "foobar_bar", 187 TLSInfo: swarm.TLSInfo{TrustRoot: "hi"}, 188 Engine: swarm.EngineDescription{EngineVersion: "1.2.3"}, 189 }, 190 Status: swarm.NodeStatus{State: swarm.NodeState("bar")}, 191 Spec: swarm.NodeSpec{Availability: swarm.NodeAvailability("active")}, 192 ManagerStatus: &swarm.ManagerStatus{ 193 Leader: false, 194 Reachability: swarm.Reachability("Reachable"), 195 }, 196 }, 197 { 198 ID: "nodeID3", 199 Description: swarm.NodeDescription{Hostname: "foobar_boo"}, 200 Status: swarm.NodeStatus{State: swarm.NodeState("boo")}, 201 Spec: swarm.NodeSpec{Availability: swarm.NodeAvailability("active")}, 202 }, 203 } 204 out := bytes.NewBufferString("") 205 testcase.context.Output = out 206 err := FormatWrite(testcase.context, nodes, types.Info{Swarm: swarm.Info{Cluster: &testcase.clusterInfo}}) 207 if err != nil { 208 assert.Error(t, err, testcase.expected) 209 } else { 210 assert.Check(t, is.Equal(testcase.expected, out.String())) 211 } 212 } 213 } 214 215 func TestNodeContextWriteJSON(t *testing.T) { 216 cases := []struct { 217 expected []map[string]interface{} 218 info types.Info 219 }{ 220 { 221 expected: []map[string]interface{}{ 222 {"Availability": "", "Hostname": "foobar_baz", "ID": "nodeID1", "ManagerStatus": "", "Status": "", "Self": false, "TLSStatus": "Unknown", "EngineVersion": "1.2.3"}, 223 {"Availability": "", "Hostname": "foobar_bar", "ID": "nodeID2", "ManagerStatus": "", "Status": "", "Self": false, "TLSStatus": "Unknown", "EngineVersion": ""}, 224 {"Availability": "", "Hostname": "foobar_boo", "ID": "nodeID3", "ManagerStatus": "", "Status": "", "Self": false, "TLSStatus": "Unknown", "EngineVersion": "18.03.0-ce"}, 225 }, 226 info: types.Info{}, 227 }, 228 { 229 expected: []map[string]interface{}{ 230 {"Availability": "", "Hostname": "foobar_baz", "ID": "nodeID1", "ManagerStatus": "", "Status": "", "Self": false, "TLSStatus": "Ready", "EngineVersion": "1.2.3"}, 231 {"Availability": "", "Hostname": "foobar_bar", "ID": "nodeID2", "ManagerStatus": "", "Status": "", "Self": false, "TLSStatus": "Needs Rotation", "EngineVersion": ""}, 232 {"Availability": "", "Hostname": "foobar_boo", "ID": "nodeID3", "ManagerStatus": "", "Status": "", "Self": false, "TLSStatus": "Unknown", "EngineVersion": "18.03.0-ce"}, 233 }, 234 info: types.Info{ 235 Swarm: swarm.Info{ 236 Cluster: &swarm.ClusterInfo{ 237 TLSInfo: swarm.TLSInfo{TrustRoot: "hi"}, 238 RootRotationInProgress: true, 239 }, 240 }, 241 }, 242 }, 243 } 244 245 for _, testcase := range cases { 246 nodes := []swarm.Node{ 247 {ID: "nodeID1", Description: swarm.NodeDescription{Hostname: "foobar_baz", TLSInfo: swarm.TLSInfo{TrustRoot: "hi"}, Engine: swarm.EngineDescription{EngineVersion: "1.2.3"}}}, 248 {ID: "nodeID2", Description: swarm.NodeDescription{Hostname: "foobar_bar", TLSInfo: swarm.TLSInfo{TrustRoot: "no"}}}, 249 {ID: "nodeID3", Description: swarm.NodeDescription{Hostname: "foobar_boo", Engine: swarm.EngineDescription{EngineVersion: "18.03.0-ce"}}}, 250 } 251 out := bytes.NewBufferString("") 252 err := FormatWrite(formatter.Context{Format: "{{json .}}", Output: out}, nodes, testcase.info) 253 if err != nil { 254 t.Fatal(err) 255 } 256 for i, line := range strings.Split(strings.TrimSpace(out.String()), "\n") { 257 msg := fmt.Sprintf("Output: line %d: %s", i, line) 258 var m map[string]interface{} 259 err := json.Unmarshal([]byte(line), &m) 260 assert.NilError(t, err, msg) 261 assert.Check(t, is.DeepEqual(testcase.expected[i], m), msg) 262 } 263 } 264 } 265 266 func TestNodeContextWriteJSONField(t *testing.T) { 267 nodes := []swarm.Node{ 268 {ID: "nodeID1", Description: swarm.NodeDescription{Hostname: "foobar_baz"}}, 269 {ID: "nodeID2", Description: swarm.NodeDescription{Hostname: "foobar_bar"}}, 270 } 271 out := bytes.NewBufferString("") 272 err := FormatWrite(formatter.Context{Format: "{{json .ID}}", Output: out}, nodes, types.Info{}) 273 if err != nil { 274 t.Fatal(err) 275 } 276 for i, line := range strings.Split(strings.TrimSpace(out.String()), "\n") { 277 msg := fmt.Sprintf("Output: line %d: %s", i, line) 278 var s string 279 err := json.Unmarshal([]byte(line), &s) 280 assert.NilError(t, err, msg) 281 assert.Check(t, is.Equal(nodes[i].ID, s), msg) 282 } 283 } 284 285 func TestNodeInspectWriteContext(t *testing.T) { 286 node := swarm.Node{ 287 ID: "nodeID1", 288 Description: swarm.NodeDescription{ 289 Hostname: "foobar_baz", 290 TLSInfo: swarm.TLSInfo{ 291 TrustRoot: "-----BEGIN CERTIFICATE-----\ndata\n-----END CERTIFICATE-----\n", 292 CertIssuerPublicKey: []byte("pubKey"), 293 CertIssuerSubject: []byte("subject"), 294 }, 295 Platform: swarm.Platform{ 296 OS: "linux", 297 Architecture: "amd64", 298 }, 299 Resources: swarm.Resources{ 300 MemoryBytes: 1, 301 }, 302 Engine: swarm.EngineDescription{ 303 EngineVersion: "0.1.1", 304 }, 305 }, 306 Status: swarm.NodeStatus{ 307 State: swarm.NodeState("ready"), 308 Addr: "1.1.1.1", 309 }, 310 Spec: swarm.NodeSpec{ 311 Availability: swarm.NodeAvailability("drain"), 312 Role: swarm.NodeRole("manager"), 313 }, 314 } 315 out := bytes.NewBufferString("") 316 context := formatter.Context{ 317 Format: NewFormat("pretty", false), 318 Output: out, 319 } 320 err := InspectFormatWrite(context, []string{"nodeID1"}, func(string) (interface{}, []byte, error) { 321 return node, nil, nil 322 }) 323 if err != nil { 324 t.Fatal(err) 325 } 326 expected := `ID: nodeID1 327 Hostname: foobar_baz 328 Joined at: 0001-01-01 00:00:00 +0000 utc 329 Status: 330 State: Ready 331 Availability: Drain 332 Address: 1.1.1.1 333 Platform: 334 Operating System: linux 335 Architecture: amd64 336 Resources: 337 CPUs: 0 338 Memory: 1B 339 Engine Version: 0.1.1 340 TLS Info: 341 TrustRoot: 342 -----BEGIN CERTIFICATE----- 343 data 344 -----END CERTIFICATE----- 345 346 Issuer Subject: c3ViamVjdA== 347 Issuer Public Key: cHViS2V5 348 ` 349 assert.Check(t, is.Equal(expected, out.String())) 350 }