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>
This commit is contained in:
Yong Tang 2017-02-07 22:51:33 -08:00
parent d61c5882cf
commit c877c7b2bf
3 changed files with 93 additions and 11 deletions

View file

@ -1,6 +1,7 @@
package formatter
import (
"fmt"
"strings"
"time"
@ -391,7 +392,7 @@ func (ctx *serviceInspectContext) Ports() []swarm.PortConfig {
}
const (
defaultServiceTableFormat = "table {{.ID}}\t{{.Name}}\t{{.Mode}}\t{{.Replicas}}\t{{.Image}}"
defaultServiceTableFormat = "table {{.ID}}\t{{.Name}}\t{{.Mode}}\t{{.Replicas}}\t{{.Image}}\t{{.Ports}}"
serviceIDHeader = "ID"
modeHeader = "MODE"
@ -410,7 +411,7 @@ func NewServiceListFormat(source string, quiet bool) Format {
if quiet {
return `id: {{.ID}}`
}
return `id: {{.ID}}\nname: {{.Name}}\nmode: {{.Mode}}\nreplicas: {{.Replicas}}\nimage: {{.Image}}\n`
return `id: {{.ID}}\nname: {{.Name}}\nmode: {{.Mode}}\nreplicas: {{.Replicas}}\nimage: {{.Image}}\nports: {{.Ports}}\n`
}
return Format(source)
}
@ -439,6 +440,7 @@ func ServiceListWrite(ctx Context, services []swarm.Service, info map[string]Ser
"Mode": modeHeader,
"Replicas": replicasHeader,
"Image": imageHeader,
"Ports": portsHeader,
}
return ctx.Write(&serviceCtx, render)
}
@ -483,3 +485,20 @@ func (c *serviceContext) Image() string {
return image
}
func (c *serviceContext) Ports() string {
if c.service.Spec.EndpointSpec == nil || c.service.Spec.EndpointSpec.Ports == nil {
return ""
}
ports := []string{}
for _, pConfig := range c.service.Spec.EndpointSpec.Ports {
if pConfig.PublishMode == swarm.PortConfigPublishModeIngress {
ports = append(ports, fmt.Sprintf("*:%d->%d/%s",
pConfig.PublishedPort,
pConfig.TargetPort,
pConfig.Protocol,
))
}
}
return strings.Join(ports, ",")
}

View file

@ -29,9 +29,9 @@ func TestServiceContextWrite(t *testing.T) {
// Table format
{
Context{Format: NewServiceListFormat("table", false)},
`ID NAME MODE REPLICAS IMAGE
id_baz baz global 2/4
id_bar bar replicated 2/4
`ID NAME MODE REPLICAS IMAGE PORTS
id_baz baz global 2/4 *:80->8080/tcp
id_bar bar replicated 2/4 *:80->8080/tcp
`,
},
{
@ -62,12 +62,14 @@ name: baz
mode: global
replicas: 2/4
image:
ports: *:80->8080/tcp
id: id_bar
name: bar
mode: replicated
replicas: 2/4
image:
ports: *:80->8080/tcp
`,
},
@ -88,8 +90,38 @@ bar
for _, testcase := range cases {
services := []swarm.Service{
{ID: "id_baz", Spec: swarm.ServiceSpec{Annotations: swarm.Annotations{Name: "baz"}}},
{ID: "id_bar", Spec: swarm.ServiceSpec{Annotations: swarm.Annotations{Name: "bar"}}},
{
ID: "id_baz",
Spec: swarm.ServiceSpec{
Annotations: swarm.Annotations{Name: "baz"},
EndpointSpec: &swarm.EndpointSpec{
Ports: []swarm.PortConfig{
{
PublishMode: "ingress",
PublishedPort: 80,
TargetPort: 8080,
Protocol: "tcp",
},
},
},
},
},
{
ID: "id_bar",
Spec: swarm.ServiceSpec{
Annotations: swarm.Annotations{Name: "bar"},
EndpointSpec: &swarm.EndpointSpec{
Ports: []swarm.PortConfig{
{
PublishMode: "ingress",
PublishedPort: 80,
TargetPort: 8080,
Protocol: "tcp",
},
},
},
},
},
}
info := map[string]ServiceListInfo{
"id_baz": {
@ -114,8 +146,38 @@ bar
func TestServiceContextWriteJSON(t *testing.T) {
services := []swarm.Service{
{ID: "id_baz", Spec: swarm.ServiceSpec{Annotations: swarm.Annotations{Name: "baz"}}},
{ID: "id_bar", Spec: swarm.ServiceSpec{Annotations: swarm.Annotations{Name: "bar"}}},
{
ID: "id_baz",
Spec: swarm.ServiceSpec{
Annotations: swarm.Annotations{Name: "baz"},
EndpointSpec: &swarm.EndpointSpec{
Ports: []swarm.PortConfig{
{
PublishMode: "ingress",
PublishedPort: 80,
TargetPort: 8080,
Protocol: "tcp",
},
},
},
},
},
{
ID: "id_bar",
Spec: swarm.ServiceSpec{
Annotations: swarm.Annotations{Name: "bar"},
EndpointSpec: &swarm.EndpointSpec{
Ports: []swarm.PortConfig{
{
PublishMode: "ingress",
PublishedPort: 80,
TargetPort: 8080,
Protocol: "tcp",
},
},
},
},
},
}
info := map[string]ServiceListInfo{
"id_baz": {
@ -128,8 +190,8 @@ func TestServiceContextWriteJSON(t *testing.T) {
},
}
expectedJSONs := []map[string]interface{}{
{"ID": "id_baz", "Name": "baz", "Mode": "global", "Replicas": "2/4", "Image": ""},
{"ID": "id_bar", "Name": "bar", "Mode": "replicated", "Replicas": "2/4", "Image": ""},
{"ID": "id_baz", "Name": "baz", "Mode": "global", "Replicas": "2/4", "Image": "", "Ports": "*:80->8080/tcp"},
{"ID": "id_bar", "Name": "bar", "Mode": "replicated", "Replicas": "2/4", "Image": "", "Ports": "*:80->8080/tcp"},
}
out := bytes.NewBufferString("")

View file

@ -137,6 +137,7 @@ Placeholder | Description
`.Mode` | Service mode (replicated, global)
`.Replicas` | Service replicas
`.Image` | Service image
`.Ports` | Service ports published in ingress mode
When using the `--format` option, the `service ls` command will either
output the data exactly as the template declares or, when using the