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>
| ... | ... |
@@ -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 |