github.com/justincormack/cli@v0.0.0-20201215022714-831ebeae9675/cli/command/service/formatter_test.go (about) 1 package service 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/docker/api/types/swarm" 12 "gotest.tools/v3/assert" 13 is "gotest.tools/v3/assert/cmp" 14 "gotest.tools/v3/golden" 15 ) 16 17 func TestServiceContextWrite(t *testing.T) { 18 var ( 19 // we need a pair of variables for setting the job parameters, because 20 // those parameters take pointers to uint64, which we can't make as a 21 // literal 22 varThree uint64 = 3 23 varTen uint64 = 10 24 ) 25 cases := []struct { 26 context formatter.Context 27 expected string 28 }{ 29 // Errors 30 { 31 formatter.Context{Format: "{{InvalidFunction}}"}, 32 `Template parsing error: template: :1: function "InvalidFunction" not defined 33 `, 34 }, 35 { 36 formatter.Context{Format: "{{nil}}"}, 37 `Template parsing error: template: :1:2: executing "" at <nil>: nil is not a command 38 `, 39 }, 40 // Table format 41 { 42 formatter.Context{Format: NewListFormat("table", false)}, 43 `ID NAME MODE REPLICAS IMAGE PORTS 44 02_bar bar replicated 2/4 *:80->8090/udp 45 01_baz baz global 1/3 *:80->8080/tcp 46 04_qux2 qux2 replicated 3/3 (max 2 per node) 47 03_qux10 qux10 replicated 2/3 (max 1 per node) 48 05_job1 zarp1 replicated job 2/3 (5/10 completed) 49 06_job2 zarp2 global job 1/1 (3/4 completed) 50 `, 51 }, 52 { 53 formatter.Context{Format: NewListFormat("table", true)}, 54 `02_bar 55 01_baz 56 04_qux2 57 03_qux10 58 05_job1 59 06_job2 60 `, 61 }, 62 { 63 formatter.Context{Format: NewListFormat("table {{.Name}}\t{{.Mode}}", false)}, 64 `NAME MODE 65 bar replicated 66 baz global 67 qux2 replicated 68 qux10 replicated 69 zarp1 replicated job 70 zarp2 global job 71 `, 72 }, 73 { 74 formatter.Context{Format: NewListFormat("table {{.Name}}", true)}, 75 `NAME 76 bar 77 baz 78 qux2 79 qux10 80 zarp1 81 zarp2 82 `, 83 }, 84 // Raw Format 85 { 86 formatter.Context{Format: NewListFormat("raw", false)}, 87 string(golden.Get(t, "service-context-write-raw.golden")), 88 }, 89 { 90 formatter.Context{Format: NewListFormat("raw", true)}, 91 `id: 02_bar 92 id: 01_baz 93 id: 04_qux2 94 id: 03_qux10 95 id: 05_job1 96 id: 06_job2 97 `, 98 }, 99 // Custom Format 100 { 101 formatter.Context{Format: NewListFormat("{{.Name}}", false)}, 102 `bar 103 baz 104 qux2 105 qux10 106 zarp1 107 zarp2 108 `, 109 }, 110 } 111 112 services := []swarm.Service{ 113 { 114 ID: "01_baz", 115 Spec: swarm.ServiceSpec{ 116 Annotations: swarm.Annotations{Name: "baz"}, 117 Mode: swarm.ServiceMode{ 118 Global: &swarm.GlobalService{}, 119 }, 120 }, 121 Endpoint: swarm.Endpoint{ 122 Ports: []swarm.PortConfig{ 123 { 124 PublishMode: "ingress", 125 PublishedPort: 80, 126 TargetPort: 8080, 127 Protocol: "tcp", 128 }, 129 }, 130 }, 131 ServiceStatus: &swarm.ServiceStatus{ 132 RunningTasks: 1, 133 DesiredTasks: 3, 134 }, 135 }, 136 { 137 ID: "02_bar", 138 Spec: swarm.ServiceSpec{ 139 Annotations: swarm.Annotations{Name: "bar"}, 140 Mode: swarm.ServiceMode{ 141 Replicated: &swarm.ReplicatedService{}, 142 }, 143 }, 144 Endpoint: swarm.Endpoint{ 145 Ports: []swarm.PortConfig{ 146 { 147 PublishMode: "ingress", 148 PublishedPort: 80, 149 TargetPort: 8090, 150 Protocol: "udp", 151 }, 152 }, 153 }, 154 ServiceStatus: &swarm.ServiceStatus{ 155 RunningTasks: 2, 156 DesiredTasks: 4, 157 }, 158 }, 159 { 160 ID: "03_qux10", 161 Spec: swarm.ServiceSpec{ 162 Annotations: swarm.Annotations{Name: "qux10"}, 163 Mode: swarm.ServiceMode{ 164 Replicated: &swarm.ReplicatedService{}, 165 }, 166 TaskTemplate: swarm.TaskSpec{ 167 Placement: &swarm.Placement{MaxReplicas: 1}, 168 }, 169 }, 170 ServiceStatus: &swarm.ServiceStatus{ 171 RunningTasks: 2, 172 DesiredTasks: 3, 173 }, 174 }, 175 { 176 ID: "04_qux2", 177 Spec: swarm.ServiceSpec{ 178 Annotations: swarm.Annotations{Name: "qux2"}, 179 Mode: swarm.ServiceMode{ 180 Replicated: &swarm.ReplicatedService{}, 181 }, 182 TaskTemplate: swarm.TaskSpec{ 183 Placement: &swarm.Placement{MaxReplicas: 2}, 184 }, 185 }, 186 ServiceStatus: &swarm.ServiceStatus{ 187 RunningTasks: 3, 188 DesiredTasks: 3, 189 }, 190 }, 191 { 192 ID: "05_job1", 193 Spec: swarm.ServiceSpec{ 194 Annotations: swarm.Annotations{Name: "zarp1"}, 195 Mode: swarm.ServiceMode{ 196 ReplicatedJob: &swarm.ReplicatedJob{ 197 MaxConcurrent: &varThree, 198 TotalCompletions: &varTen, 199 }, 200 }, 201 }, 202 ServiceStatus: &swarm.ServiceStatus{ 203 RunningTasks: 2, 204 DesiredTasks: 3, 205 CompletedTasks: 5, 206 }, 207 }, 208 { 209 ID: "06_job2", 210 Spec: swarm.ServiceSpec{ 211 Annotations: swarm.Annotations{Name: "zarp2"}, 212 Mode: swarm.ServiceMode{ 213 GlobalJob: &swarm.GlobalJob{}, 214 }, 215 }, 216 ServiceStatus: &swarm.ServiceStatus{ 217 RunningTasks: 1, 218 DesiredTasks: 1, 219 CompletedTasks: 3, 220 }, 221 }, 222 } 223 224 for _, tc := range cases { 225 tc := tc 226 t.Run(string(tc.context.Format), func(t *testing.T) { 227 var out bytes.Buffer 228 tc.context.Output = &out 229 230 if err := ListFormatWrite(tc.context, services); err != nil { 231 assert.Error(t, err, tc.expected) 232 } else { 233 assert.Equal(t, out.String(), tc.expected) 234 } 235 }) 236 } 237 } 238 239 func TestServiceContextWriteJSON(t *testing.T) { 240 services := []swarm.Service{ 241 { 242 ID: "01_baz", 243 Spec: swarm.ServiceSpec{ 244 Annotations: swarm.Annotations{Name: "baz"}, 245 Mode: swarm.ServiceMode{ 246 Global: &swarm.GlobalService{}, 247 }, 248 }, 249 Endpoint: swarm.Endpoint{ 250 Ports: []swarm.PortConfig{ 251 { 252 PublishMode: "ingress", 253 PublishedPort: 80, 254 TargetPort: 8080, 255 Protocol: "tcp", 256 }, 257 }, 258 }, 259 ServiceStatus: &swarm.ServiceStatus{ 260 RunningTasks: 1, 261 DesiredTasks: 3, 262 }, 263 }, 264 { 265 ID: "02_bar", 266 Spec: swarm.ServiceSpec{ 267 Annotations: swarm.Annotations{Name: "bar"}, 268 Mode: swarm.ServiceMode{ 269 Replicated: &swarm.ReplicatedService{}, 270 }, 271 }, 272 Endpoint: swarm.Endpoint{ 273 Ports: []swarm.PortConfig{ 274 { 275 PublishMode: "ingress", 276 PublishedPort: 80, 277 TargetPort: 8080, 278 Protocol: "tcp", 279 }, 280 }, 281 }, 282 ServiceStatus: &swarm.ServiceStatus{ 283 RunningTasks: 2, 284 DesiredTasks: 4, 285 }, 286 }, 287 } 288 expectedJSONs := []map[string]interface{}{ 289 {"ID": "02_bar", "Name": "bar", "Mode": "replicated", "Replicas": "2/4", "Image": "", "Ports": "*:80->8080/tcp"}, 290 {"ID": "01_baz", "Name": "baz", "Mode": "global", "Replicas": "1/3", "Image": "", "Ports": "*:80->8080/tcp"}, 291 } 292 293 out := bytes.NewBufferString("") 294 err := ListFormatWrite(formatter.Context{Format: "{{json .}}", Output: out}, services) 295 if err != nil { 296 t.Fatal(err) 297 } 298 for i, line := range strings.Split(strings.TrimSpace(out.String()), "\n") { 299 msg := fmt.Sprintf("Output: line %d: %s", i, line) 300 var m map[string]interface{} 301 err := json.Unmarshal([]byte(line), &m) 302 assert.NilError(t, err, msg) 303 assert.Check(t, is.DeepEqual(expectedJSONs[i], m), msg) 304 } 305 } 306 func TestServiceContextWriteJSONField(t *testing.T) { 307 services := []swarm.Service{ 308 { 309 ID: "01_baz", 310 Spec: swarm.ServiceSpec{ 311 Annotations: swarm.Annotations{Name: "baz"}, 312 Mode: swarm.ServiceMode{ 313 Global: &swarm.GlobalService{}, 314 }, 315 }, 316 ServiceStatus: &swarm.ServiceStatus{ 317 RunningTasks: 2, 318 DesiredTasks: 4, 319 }, 320 }, 321 { 322 ID: "24_bar", 323 Spec: swarm.ServiceSpec{ 324 Annotations: swarm.Annotations{Name: "bar"}, 325 Mode: swarm.ServiceMode{ 326 Replicated: &swarm.ReplicatedService{}, 327 }, 328 }, 329 ServiceStatus: &swarm.ServiceStatus{ 330 RunningTasks: 2, 331 DesiredTasks: 4, 332 }, 333 }, 334 } 335 out := bytes.NewBufferString("") 336 err := ListFormatWrite(formatter.Context{Format: "{{json .Name}}", Output: out}, services) 337 if err != nil { 338 t.Fatal(err) 339 } 340 for i, line := range strings.Split(strings.TrimSpace(out.String()), "\n") { 341 msg := fmt.Sprintf("Output: line %d: %s", i, line) 342 var s string 343 err := json.Unmarshal([]byte(line), &s) 344 assert.NilError(t, err, msg) 345 assert.Check(t, is.Equal(services[i].Spec.Name, s), msg) 346 } 347 } 348 349 func TestServiceContext_Ports(t *testing.T) { 350 c := serviceContext{ 351 service: swarm.Service{ 352 Endpoint: swarm.Endpoint{ 353 Ports: []swarm.PortConfig{ 354 { 355 Protocol: "tcp", 356 TargetPort: 80, 357 PublishedPort: 81, 358 PublishMode: "ingress", 359 }, 360 { 361 Protocol: "tcp", 362 TargetPort: 80, 363 PublishedPort: 80, 364 PublishMode: "ingress", 365 }, 366 { 367 Protocol: "tcp", 368 TargetPort: 95, 369 PublishedPort: 95, 370 PublishMode: "ingress", 371 }, 372 { 373 Protocol: "tcp", 374 TargetPort: 90, 375 PublishedPort: 90, 376 PublishMode: "ingress", 377 }, 378 { 379 Protocol: "tcp", 380 TargetPort: 91, 381 PublishedPort: 91, 382 PublishMode: "ingress", 383 }, 384 { 385 Protocol: "tcp", 386 TargetPort: 92, 387 PublishedPort: 92, 388 PublishMode: "ingress", 389 }, 390 { 391 Protocol: "tcp", 392 TargetPort: 93, 393 PublishedPort: 93, 394 PublishMode: "ingress", 395 }, 396 { 397 Protocol: "tcp", 398 TargetPort: 94, 399 PublishedPort: 94, 400 PublishMode: "ingress", 401 }, 402 { 403 Protocol: "udp", 404 TargetPort: 95, 405 PublishedPort: 95, 406 PublishMode: "ingress", 407 }, 408 { 409 Protocol: "udp", 410 TargetPort: 90, 411 PublishedPort: 90, 412 PublishMode: "ingress", 413 }, 414 { 415 Protocol: "udp", 416 TargetPort: 96, 417 PublishedPort: 96, 418 PublishMode: "ingress", 419 }, 420 { 421 Protocol: "udp", 422 TargetPort: 91, 423 PublishedPort: 91, 424 PublishMode: "ingress", 425 }, 426 { 427 Protocol: "udp", 428 TargetPort: 92, 429 PublishedPort: 92, 430 PublishMode: "ingress", 431 }, 432 { 433 Protocol: "udp", 434 TargetPort: 93, 435 PublishedPort: 93, 436 PublishMode: "ingress", 437 }, 438 { 439 Protocol: "udp", 440 TargetPort: 94, 441 PublishedPort: 94, 442 PublishMode: "ingress", 443 }, 444 { 445 Protocol: "tcp", 446 TargetPort: 60, 447 PublishedPort: 60, 448 PublishMode: "ingress", 449 }, 450 { 451 Protocol: "tcp", 452 TargetPort: 61, 453 PublishedPort: 61, 454 PublishMode: "ingress", 455 }, 456 { 457 Protocol: "tcp", 458 TargetPort: 61, 459 PublishedPort: 62, 460 PublishMode: "ingress", 461 }, 462 { 463 Protocol: "sctp", 464 TargetPort: 97, 465 PublishedPort: 97, 466 PublishMode: "ingress", 467 }, 468 { 469 Protocol: "sctp", 470 TargetPort: 98, 471 PublishedPort: 98, 472 PublishMode: "ingress", 473 }, 474 }, 475 }, 476 }, 477 } 478 479 assert.Check(t, is.Equal("*:97-98->97-98/sctp, *:60-61->60-61/tcp, *:62->61/tcp, *:80-81->80/tcp, *:90-95->90-95/tcp, *:90-96->90-96/udp", c.Ports())) 480 }