Browse code

Add `PORTS` field for `docker service ls` (`ingress`)

This fix is related to 30232 wherw `docker service ls`
does not show `PORTS` information like `docker service ps`.

This fix adds `PORTS` fields for services that publish
ports in ingress mode.

Additional unit tests cases have been updated.

This fix is related to 30232.

Signed-off-by: Yong Tang <yong.tang.github@outlook.com>

Yong Tang authored on 2017/02/08 15:51:33
Showing 3 changed files
... ...
@@ -1,6 +1,7 @@
1 1
 package formatter
2 2
 
3 3
 import (
4
+	"fmt"
4 5
 	"strings"
5 6
 	"time"
6 7
 
... ...
@@ -391,7 +392,7 @@ func (ctx *serviceInspectContext) Ports() []swarm.PortConfig {
391 391
 }
392 392
 
393 393
 const (
394
-	defaultServiceTableFormat = "table {{.ID}}\t{{.Name}}\t{{.Mode}}\t{{.Replicas}}\t{{.Image}}"
394
+	defaultServiceTableFormat = "table {{.ID}}\t{{.Name}}\t{{.Mode}}\t{{.Replicas}}\t{{.Image}}\t{{.Ports}}"
395 395
 
396 396
 	serviceIDHeader = "ID"
397 397
 	modeHeader      = "MODE"
... ...
@@ -410,7 +411,7 @@ func NewServiceListFormat(source string, quiet bool) Format {
410 410
 		if quiet {
411 411
 			return `id: {{.ID}}`
412 412
 		}
413
-		return `id: {{.ID}}\nname: {{.Name}}\nmode: {{.Mode}}\nreplicas: {{.Replicas}}\nimage: {{.Image}}\n`
413
+		return `id: {{.ID}}\nname: {{.Name}}\nmode: {{.Mode}}\nreplicas: {{.Replicas}}\nimage: {{.Image}}\nports: {{.Ports}}\n`
414 414
 	}
415 415
 	return Format(source)
416 416
 }
... ...
@@ -439,6 +440,7 @@ func ServiceListWrite(ctx Context, services []swarm.Service, info map[string]Ser
439 439
 		"Mode":     modeHeader,
440 440
 		"Replicas": replicasHeader,
441 441
 		"Image":    imageHeader,
442
+		"Ports":    portsHeader,
442 443
 	}
443 444
 	return ctx.Write(&serviceCtx, render)
444 445
 }
... ...
@@ -483,3 +485,20 @@ func (c *serviceContext) Image() string {
483 483
 
484 484
 	return image
485 485
 }
486
+
487
+func (c *serviceContext) Ports() string {
488
+	if c.service.Spec.EndpointSpec == nil || c.service.Spec.EndpointSpec.Ports == nil {
489
+		return ""
490
+	}
491
+	ports := []string{}
492
+	for _, pConfig := range c.service.Spec.EndpointSpec.Ports {
493
+		if pConfig.PublishMode == swarm.PortConfigPublishModeIngress {
494
+			ports = append(ports, fmt.Sprintf("*:%d->%d/%s",
495
+				pConfig.PublishedPort,
496
+				pConfig.TargetPort,
497
+				pConfig.Protocol,
498
+			))
499
+		}
500
+	}
501
+	return strings.Join(ports, ",")
502
+}
... ...
@@ -29,9 +29,9 @@ func TestServiceContextWrite(t *testing.T) {
29 29
 		// Table format
30 30
 		{
31 31
 			Context{Format: NewServiceListFormat("table", false)},
32
-			`ID                  NAME                MODE                REPLICAS            IMAGE
33
-id_baz              baz                 global              2/4                 
34
-id_bar              bar                 replicated          2/4                 
32
+			`ID                  NAME                MODE                REPLICAS            IMAGE               PORTS
33
+id_baz              baz                 global              2/4                                     *:80->8080/tcp
34
+id_bar              bar                 replicated          2/4                                     *:80->8080/tcp
35 35
 `,
36 36
 		},
37 37
 		{
... ...
@@ -62,12 +62,14 @@ name: baz
62 62
 mode: global
63 63
 replicas: 2/4
64 64
 image: 
65
+ports: *:80->8080/tcp
65 66
 
66 67
 id: id_bar
67 68
 name: bar
68 69
 mode: replicated
69 70
 replicas: 2/4
70 71
 image: 
72
+ports: *:80->8080/tcp
71 73
 
72 74
 `,
73 75
 		},
... ...
@@ -88,8 +90,38 @@ bar
88 88
 
89 89
 	for _, testcase := range cases {
90 90
 		services := []swarm.Service{
91
-			{ID: "id_baz", Spec: swarm.ServiceSpec{Annotations: swarm.Annotations{Name: "baz"}}},
92
-			{ID: "id_bar", Spec: swarm.ServiceSpec{Annotations: swarm.Annotations{Name: "bar"}}},
91
+			{
92
+				ID: "id_baz",
93
+				Spec: swarm.ServiceSpec{
94
+					Annotations: swarm.Annotations{Name: "baz"},
95
+					EndpointSpec: &swarm.EndpointSpec{
96
+						Ports: []swarm.PortConfig{
97
+							{
98
+								PublishMode:   "ingress",
99
+								PublishedPort: 80,
100
+								TargetPort:    8080,
101
+								Protocol:      "tcp",
102
+							},
103
+						},
104
+					},
105
+				},
106
+			},
107
+			{
108
+				ID: "id_bar",
109
+				Spec: swarm.ServiceSpec{
110
+					Annotations: swarm.Annotations{Name: "bar"},
111
+					EndpointSpec: &swarm.EndpointSpec{
112
+						Ports: []swarm.PortConfig{
113
+							{
114
+								PublishMode:   "ingress",
115
+								PublishedPort: 80,
116
+								TargetPort:    8080,
117
+								Protocol:      "tcp",
118
+							},
119
+						},
120
+					},
121
+				},
122
+			},
93 123
 		}
94 124
 		info := map[string]ServiceListInfo{
95 125
 			"id_baz": {
... ...
@@ -114,8 +146,38 @@ bar
114 114
 
115 115
 func TestServiceContextWriteJSON(t *testing.T) {
116 116
 	services := []swarm.Service{
117
-		{ID: "id_baz", Spec: swarm.ServiceSpec{Annotations: swarm.Annotations{Name: "baz"}}},
118
-		{ID: "id_bar", Spec: swarm.ServiceSpec{Annotations: swarm.Annotations{Name: "bar"}}},
117
+		{
118
+			ID: "id_baz",
119
+			Spec: swarm.ServiceSpec{
120
+				Annotations: swarm.Annotations{Name: "baz"},
121
+				EndpointSpec: &swarm.EndpointSpec{
122
+					Ports: []swarm.PortConfig{
123
+						{
124
+							PublishMode:   "ingress",
125
+							PublishedPort: 80,
126
+							TargetPort:    8080,
127
+							Protocol:      "tcp",
128
+						},
129
+					},
130
+				},
131
+			},
132
+		},
133
+		{
134
+			ID: "id_bar",
135
+			Spec: swarm.ServiceSpec{
136
+				Annotations: swarm.Annotations{Name: "bar"},
137
+				EndpointSpec: &swarm.EndpointSpec{
138
+					Ports: []swarm.PortConfig{
139
+						{
140
+							PublishMode:   "ingress",
141
+							PublishedPort: 80,
142
+							TargetPort:    8080,
143
+							Protocol:      "tcp",
144
+						},
145
+					},
146
+				},
147
+			},
148
+		},
119 149
 	}
120 150
 	info := map[string]ServiceListInfo{
121 151
 		"id_baz": {
... ...
@@ -128,8 +190,8 @@ func TestServiceContextWriteJSON(t *testing.T) {
128 128
 		},
129 129
 	}
130 130
 	expectedJSONs := []map[string]interface{}{
131
-		{"ID": "id_baz", "Name": "baz", "Mode": "global", "Replicas": "2/4", "Image": ""},
132
-		{"ID": "id_bar", "Name": "bar", "Mode": "replicated", "Replicas": "2/4", "Image": ""},
131
+		{"ID": "id_baz", "Name": "baz", "Mode": "global", "Replicas": "2/4", "Image": "", "Ports": "*:80->8080/tcp"},
132
+		{"ID": "id_bar", "Name": "bar", "Mode": "replicated", "Replicas": "2/4", "Image": "", "Ports": "*:80->8080/tcp"},
133 133
 	}
134 134
 
135 135
 	out := bytes.NewBufferString("")
... ...
@@ -137,6 +137,7 @@ Placeholder | Description
137 137
 `.Mode`     | Service mode (replicated, global)
138 138
 `.Replicas` | Service replicas
139 139
 `.Image`    | Service image
140
+`.Ports`    | Service ports published in ingress mode
140 141
 
141 142
 When using the `--format` option, the `service ls` command will either
142 143
 output the data exactly as the template declares or, when using the