2017-01-26 16:08:07 -05:00
|
|
|
package formatter
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
|
|
|
"encoding/json"
|
2017-06-15 12:28:08 -04:00
|
|
|
"fmt"
|
2017-01-26 16:08:07 -05:00
|
|
|
"strings"
|
|
|
|
"testing"
|
|
|
|
|
|
|
|
"github.com/docker/docker/api/types/swarm"
|
2018-03-05 18:53:52 -05:00
|
|
|
"github.com/gotestyourself/gotestyourself/assert"
|
|
|
|
is "github.com/gotestyourself/gotestyourself/assert/cmp"
|
2017-09-07 17:50:44 -04:00
|
|
|
"github.com/gotestyourself/gotestyourself/golden"
|
2017-01-26 16:08:07 -05:00
|
|
|
)
|
|
|
|
|
|
|
|
func TestServiceContextWrite(t *testing.T) {
|
|
|
|
cases := []struct {
|
|
|
|
context Context
|
|
|
|
expected string
|
|
|
|
}{
|
|
|
|
// Errors
|
|
|
|
{
|
|
|
|
Context{Format: "{{InvalidFunction}}"},
|
|
|
|
`Template parsing error: template: :1: function "InvalidFunction" not defined
|
|
|
|
`,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Context{Format: "{{nil}}"},
|
|
|
|
`Template parsing error: template: :1:2: executing "" at <nil>: nil is not a command
|
|
|
|
`,
|
|
|
|
},
|
|
|
|
// Table format
|
|
|
|
{
|
|
|
|
Context{Format: NewServiceListFormat("table", false)},
|
2017-02-08 01:51:33 -05:00
|
|
|
`ID NAME MODE REPLICAS IMAGE PORTS
|
|
|
|
id_baz baz global 2/4 *:80->8080/tcp
|
|
|
|
id_bar bar replicated 2/4 *:80->8080/tcp
|
2017-01-26 16:08:07 -05:00
|
|
|
`,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Context{Format: NewServiceListFormat("table", true)},
|
|
|
|
`id_baz
|
|
|
|
id_bar
|
|
|
|
`,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Context{Format: NewServiceListFormat("table {{.Name}}", false)},
|
|
|
|
`NAME
|
|
|
|
baz
|
|
|
|
bar
|
|
|
|
`,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Context{Format: NewServiceListFormat("table {{.Name}}", true)},
|
|
|
|
`NAME
|
|
|
|
baz
|
|
|
|
bar
|
|
|
|
`,
|
|
|
|
},
|
|
|
|
// Raw Format
|
|
|
|
{
|
|
|
|
Context{Format: NewServiceListFormat("raw", false)},
|
2017-09-07 17:50:44 -04:00
|
|
|
string(golden.Get(t, "service-context-write-raw.golden")),
|
2017-01-26 16:08:07 -05:00
|
|
|
},
|
|
|
|
{
|
|
|
|
Context{Format: NewServiceListFormat("raw", true)},
|
|
|
|
`id: id_baz
|
|
|
|
id: id_bar
|
|
|
|
`,
|
|
|
|
},
|
|
|
|
// Custom Format
|
|
|
|
{
|
|
|
|
Context{Format: NewServiceListFormat("{{.Name}}", false)},
|
|
|
|
`baz
|
|
|
|
bar
|
|
|
|
`,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, testcase := range cases {
|
|
|
|
services := []swarm.Service{
|
2017-02-08 01:51:33 -05:00
|
|
|
{
|
|
|
|
ID: "id_baz",
|
|
|
|
Spec: swarm.ServiceSpec{
|
|
|
|
Annotations: swarm.Annotations{Name: "baz"},
|
2017-07-31 18:45:50 -04:00
|
|
|
},
|
|
|
|
Endpoint: swarm.Endpoint{
|
|
|
|
Ports: []swarm.PortConfig{
|
|
|
|
{
|
|
|
|
PublishMode: "ingress",
|
|
|
|
PublishedPort: 80,
|
|
|
|
TargetPort: 8080,
|
|
|
|
Protocol: "tcp",
|
2017-02-08 01:51:33 -05:00
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
ID: "id_bar",
|
|
|
|
Spec: swarm.ServiceSpec{
|
|
|
|
Annotations: swarm.Annotations{Name: "bar"},
|
2017-07-31 18:45:50 -04:00
|
|
|
},
|
|
|
|
Endpoint: swarm.Endpoint{
|
|
|
|
Ports: []swarm.PortConfig{
|
|
|
|
{
|
|
|
|
PublishMode: "ingress",
|
|
|
|
PublishedPort: 80,
|
|
|
|
TargetPort: 8080,
|
|
|
|
Protocol: "tcp",
|
2017-02-08 01:51:33 -05:00
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
2017-01-26 16:08:07 -05:00
|
|
|
}
|
|
|
|
info := map[string]ServiceListInfo{
|
|
|
|
"id_baz": {
|
|
|
|
Mode: "global",
|
|
|
|
Replicas: "2/4",
|
|
|
|
},
|
|
|
|
"id_bar": {
|
|
|
|
Mode: "replicated",
|
|
|
|
Replicas: "2/4",
|
|
|
|
},
|
|
|
|
}
|
|
|
|
out := bytes.NewBufferString("")
|
|
|
|
testcase.context.Output = out
|
|
|
|
err := ServiceListWrite(testcase.context, services, info)
|
|
|
|
if err != nil {
|
2018-03-06 15:54:24 -05:00
|
|
|
assert.Error(t, err, testcase.expected)
|
2017-01-26 16:08:07 -05:00
|
|
|
} else {
|
2018-03-05 18:53:52 -05:00
|
|
|
assert.Check(t, is.Equal(testcase.expected, out.String()))
|
2017-01-26 16:08:07 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestServiceContextWriteJSON(t *testing.T) {
|
|
|
|
services := []swarm.Service{
|
2017-02-08 01:51:33 -05:00
|
|
|
{
|
|
|
|
ID: "id_baz",
|
|
|
|
Spec: swarm.ServiceSpec{
|
|
|
|
Annotations: swarm.Annotations{Name: "baz"},
|
2017-07-31 18:45:50 -04:00
|
|
|
},
|
|
|
|
Endpoint: swarm.Endpoint{
|
|
|
|
Ports: []swarm.PortConfig{
|
|
|
|
{
|
|
|
|
PublishMode: "ingress",
|
|
|
|
PublishedPort: 80,
|
|
|
|
TargetPort: 8080,
|
|
|
|
Protocol: "tcp",
|
2017-02-08 01:51:33 -05:00
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
ID: "id_bar",
|
|
|
|
Spec: swarm.ServiceSpec{
|
|
|
|
Annotations: swarm.Annotations{Name: "bar"},
|
2017-07-31 18:45:50 -04:00
|
|
|
},
|
|
|
|
Endpoint: swarm.Endpoint{
|
|
|
|
Ports: []swarm.PortConfig{
|
|
|
|
{
|
|
|
|
PublishMode: "ingress",
|
|
|
|
PublishedPort: 80,
|
|
|
|
TargetPort: 8080,
|
|
|
|
Protocol: "tcp",
|
2017-02-08 01:51:33 -05:00
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
2017-01-26 16:08:07 -05:00
|
|
|
}
|
|
|
|
info := map[string]ServiceListInfo{
|
|
|
|
"id_baz": {
|
|
|
|
Mode: "global",
|
|
|
|
Replicas: "2/4",
|
|
|
|
},
|
|
|
|
"id_bar": {
|
|
|
|
Mode: "replicated",
|
|
|
|
Replicas: "2/4",
|
|
|
|
},
|
|
|
|
}
|
|
|
|
expectedJSONs := []map[string]interface{}{
|
2017-02-08 01:51:33 -05:00
|
|
|
{"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"},
|
2017-01-26 16:08:07 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
out := bytes.NewBufferString("")
|
|
|
|
err := ServiceListWrite(Context{Format: "{{json .}}", Output: out}, services, info)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
for i, line := range strings.Split(strings.TrimSpace(out.String()), "\n") {
|
2017-06-15 12:28:08 -04:00
|
|
|
msg := fmt.Sprintf("Output: line %d: %s", i, line)
|
2017-01-26 16:08:07 -05:00
|
|
|
var m map[string]interface{}
|
2017-06-15 12:28:08 -04:00
|
|
|
err := json.Unmarshal([]byte(line), &m)
|
2018-03-05 18:53:52 -05:00
|
|
|
assert.NilError(t, err, msg)
|
|
|
|
assert.Check(t, is.DeepEqual(expectedJSONs[i], m), msg)
|
2017-01-26 16:08:07 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
func TestServiceContextWriteJSONField(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"}}},
|
|
|
|
}
|
|
|
|
info := map[string]ServiceListInfo{
|
|
|
|
"id_baz": {
|
|
|
|
Mode: "global",
|
|
|
|
Replicas: "2/4",
|
|
|
|
},
|
|
|
|
"id_bar": {
|
|
|
|
Mode: "replicated",
|
|
|
|
Replicas: "2/4",
|
|
|
|
},
|
|
|
|
}
|
|
|
|
out := bytes.NewBufferString("")
|
|
|
|
err := ServiceListWrite(Context{Format: "{{json .Name}}", Output: out}, services, info)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
for i, line := range strings.Split(strings.TrimSpace(out.String()), "\n") {
|
2017-06-15 12:28:08 -04:00
|
|
|
msg := fmt.Sprintf("Output: line %d: %s", i, line)
|
2017-01-26 16:08:07 -05:00
|
|
|
var s string
|
2017-06-15 12:28:08 -04:00
|
|
|
err := json.Unmarshal([]byte(line), &s)
|
2018-03-05 18:53:52 -05:00
|
|
|
assert.NilError(t, err, msg)
|
|
|
|
assert.Check(t, is.Equal(services[i].Spec.Name, s), msg)
|
2017-01-26 16:08:07 -05:00
|
|
|
}
|
|
|
|
}
|
Improve presentation of published port ranges
Port mappings in `docker service ls` are quite verbose, and occupy a lot of
space when ranges of ports are published.
This patch improves the output by reconstructing ranges of ports.
Given the following service;
$ docker service create \
-p 60-61:60-61 \
-p 62:61 \
-p 80:80 \
-p 81:80 \
-p 90-95:90-95 \
-p 90-92:90-92/udp \
-p 93-96:93-96/udp \
--name foo \
nginx:alpine
Before this patch is applied:
$ docker service ls
ID NAME MODE REPLICAS IMAGE PORTS
u1kwguv841qg foo replicated 1/1 nginx:alpine *:60->60/tcp,*:61->61/tcp,*:62->61/tcp,*:80->80/tcp,*:81->80/tcp,*:90->90/tcp,*:91->91/tcp,*:92->92/tcp,*:93->93/tcp,*:94->94/tcp,*:95->95/tcp,*:90->90/udp,*:91->91/udp,*:92->92/udp,*:93->93/udp,*:94->94/udp,*:95->95/udp,*:96->96/udp
After this patch is applied:
$ docker service ls
ID NAME MODE REPLICAS IMAGE PORTS
u1kwguv841qg foo replicated 1/1 nginx:alpine *:60-62->60-61/tcp,*:80-81->80/tcp,*:90-95->90-95/tcp,*:90-96->90-96/udp
Additional enhancements can still be made, and marked as TODO in this change;
- combine non-consecutive ports mapped to a single port (`80->80`, `81->80`,
`84->80`, `86->80`, `87->80`); to be printed as `*:80-81,84,86-87->80`.
- combine `tcp` and `udp` mappings if their port-mapping is the same;
print `*:80-81->80-81/tcp+udp` instead of `*:80-81->80-81/tcp, *:80-81->80-81/udp`
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2017-10-02 06:50:52 -04:00
|
|
|
|
|
|
|
func TestServiceContext_Ports(t *testing.T) {
|
|
|
|
c := serviceContext{
|
|
|
|
service: swarm.Service{
|
|
|
|
Endpoint: swarm.Endpoint{
|
|
|
|
Ports: []swarm.PortConfig{
|
|
|
|
{
|
|
|
|
Protocol: "tcp",
|
|
|
|
TargetPort: 80,
|
|
|
|
PublishedPort: 81,
|
|
|
|
PublishMode: "ingress",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Protocol: "tcp",
|
|
|
|
TargetPort: 80,
|
|
|
|
PublishedPort: 80,
|
|
|
|
PublishMode: "ingress",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Protocol: "tcp",
|
|
|
|
TargetPort: 95,
|
|
|
|
PublishedPort: 95,
|
|
|
|
PublishMode: "ingress",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Protocol: "tcp",
|
|
|
|
TargetPort: 90,
|
|
|
|
PublishedPort: 90,
|
|
|
|
PublishMode: "ingress",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Protocol: "tcp",
|
|
|
|
TargetPort: 91,
|
|
|
|
PublishedPort: 91,
|
|
|
|
PublishMode: "ingress",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Protocol: "tcp",
|
|
|
|
TargetPort: 92,
|
|
|
|
PublishedPort: 92,
|
|
|
|
PublishMode: "ingress",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Protocol: "tcp",
|
|
|
|
TargetPort: 93,
|
|
|
|
PublishedPort: 93,
|
|
|
|
PublishMode: "ingress",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Protocol: "tcp",
|
|
|
|
TargetPort: 94,
|
|
|
|
PublishedPort: 94,
|
|
|
|
PublishMode: "ingress",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Protocol: "udp",
|
|
|
|
TargetPort: 95,
|
|
|
|
PublishedPort: 95,
|
|
|
|
PublishMode: "ingress",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Protocol: "udp",
|
|
|
|
TargetPort: 90,
|
|
|
|
PublishedPort: 90,
|
|
|
|
PublishMode: "ingress",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Protocol: "udp",
|
|
|
|
TargetPort: 96,
|
|
|
|
PublishedPort: 96,
|
|
|
|
PublishMode: "ingress",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Protocol: "udp",
|
|
|
|
TargetPort: 91,
|
|
|
|
PublishedPort: 91,
|
|
|
|
PublishMode: "ingress",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Protocol: "udp",
|
|
|
|
TargetPort: 92,
|
|
|
|
PublishedPort: 92,
|
|
|
|
PublishMode: "ingress",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Protocol: "udp",
|
|
|
|
TargetPort: 93,
|
|
|
|
PublishedPort: 93,
|
|
|
|
PublishMode: "ingress",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Protocol: "udp",
|
|
|
|
TargetPort: 94,
|
|
|
|
PublishedPort: 94,
|
|
|
|
PublishMode: "ingress",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Protocol: "tcp",
|
|
|
|
TargetPort: 60,
|
|
|
|
PublishedPort: 60,
|
|
|
|
PublishMode: "ingress",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Protocol: "tcp",
|
|
|
|
TargetPort: 61,
|
|
|
|
PublishedPort: 61,
|
|
|
|
PublishMode: "ingress",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Protocol: "tcp",
|
|
|
|
TargetPort: 61,
|
|
|
|
PublishedPort: 62,
|
|
|
|
PublishMode: "ingress",
|
|
|
|
},
|
2017-06-13 23:42:58 -04:00
|
|
|
{
|
|
|
|
Protocol: "sctp",
|
|
|
|
TargetPort: 97,
|
|
|
|
PublishedPort: 97,
|
|
|
|
PublishMode: "ingress",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Protocol: "sctp",
|
|
|
|
TargetPort: 98,
|
|
|
|
PublishedPort: 98,
|
|
|
|
PublishMode: "ingress",
|
|
|
|
},
|
Improve presentation of published port ranges
Port mappings in `docker service ls` are quite verbose, and occupy a lot of
space when ranges of ports are published.
This patch improves the output by reconstructing ranges of ports.
Given the following service;
$ docker service create \
-p 60-61:60-61 \
-p 62:61 \
-p 80:80 \
-p 81:80 \
-p 90-95:90-95 \
-p 90-92:90-92/udp \
-p 93-96:93-96/udp \
--name foo \
nginx:alpine
Before this patch is applied:
$ docker service ls
ID NAME MODE REPLICAS IMAGE PORTS
u1kwguv841qg foo replicated 1/1 nginx:alpine *:60->60/tcp,*:61->61/tcp,*:62->61/tcp,*:80->80/tcp,*:81->80/tcp,*:90->90/tcp,*:91->91/tcp,*:92->92/tcp,*:93->93/tcp,*:94->94/tcp,*:95->95/tcp,*:90->90/udp,*:91->91/udp,*:92->92/udp,*:93->93/udp,*:94->94/udp,*:95->95/udp,*:96->96/udp
After this patch is applied:
$ docker service ls
ID NAME MODE REPLICAS IMAGE PORTS
u1kwguv841qg foo replicated 1/1 nginx:alpine *:60-62->60-61/tcp,*:80-81->80/tcp,*:90-95->90-95/tcp,*:90-96->90-96/udp
Additional enhancements can still be made, and marked as TODO in this change;
- combine non-consecutive ports mapped to a single port (`80->80`, `81->80`,
`84->80`, `86->80`, `87->80`); to be printed as `*:80-81,84,86-87->80`.
- combine `tcp` and `udp` mappings if their port-mapping is the same;
print `*:80-81->80-81/tcp+udp` instead of `*:80-81->80-81/tcp, *:80-81->80-81/udp`
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2017-10-02 06:50:52 -04:00
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
2018-03-05 18:53:52 -05:00
|
|
|
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()))
|
Improve presentation of published port ranges
Port mappings in `docker service ls` are quite verbose, and occupy a lot of
space when ranges of ports are published.
This patch improves the output by reconstructing ranges of ports.
Given the following service;
$ docker service create \
-p 60-61:60-61 \
-p 62:61 \
-p 80:80 \
-p 81:80 \
-p 90-95:90-95 \
-p 90-92:90-92/udp \
-p 93-96:93-96/udp \
--name foo \
nginx:alpine
Before this patch is applied:
$ docker service ls
ID NAME MODE REPLICAS IMAGE PORTS
u1kwguv841qg foo replicated 1/1 nginx:alpine *:60->60/tcp,*:61->61/tcp,*:62->61/tcp,*:80->80/tcp,*:81->80/tcp,*:90->90/tcp,*:91->91/tcp,*:92->92/tcp,*:93->93/tcp,*:94->94/tcp,*:95->95/tcp,*:90->90/udp,*:91->91/udp,*:92->92/udp,*:93->93/udp,*:94->94/udp,*:95->95/udp,*:96->96/udp
After this patch is applied:
$ docker service ls
ID NAME MODE REPLICAS IMAGE PORTS
u1kwguv841qg foo replicated 1/1 nginx:alpine *:60-62->60-61/tcp,*:80-81->80/tcp,*:90-95->90-95/tcp,*:90-96->90-96/udp
Additional enhancements can still be made, and marked as TODO in this change;
- combine non-consecutive ports mapped to a single port (`80->80`, `81->80`,
`84->80`, `86->80`, `87->80`); to be printed as `*:80-81,84,86-87->80`.
- combine `tcp` and `udp` mappings if their port-mapping is the same;
print `*:80-81->80-81/tcp+udp` instead of `*:80-81->80-81/tcp, *:80-81->80-81/udp`
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2017-10-02 06:50:52 -04:00
|
|
|
}
|