2018-10-23 11:05:44 -04:00
|
|
|
package service
|
2017-01-26 16:08:07 -05:00
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
|
|
|
"encoding/json"
|
2017-06-15 12:28:08 -04:00
|
|
|
"fmt"
|
2017-01-26 16:08:07 -05:00
|
|
|
"strings"
|
|
|
|
"testing"
|
|
|
|
|
2018-10-23 11:05:44 -04:00
|
|
|
"github.com/docker/cli/cli/command/formatter"
|
2017-01-26 16:08:07 -05:00
|
|
|
"github.com/docker/docker/api/types/swarm"
|
2020-02-22 12:12:14 -05:00
|
|
|
"gotest.tools/v3/assert"
|
|
|
|
is "gotest.tools/v3/assert/cmp"
|
|
|
|
"gotest.tools/v3/golden"
|
2017-01-26 16:08:07 -05:00
|
|
|
)
|
|
|
|
|
|
|
|
func TestServiceContextWrite(t *testing.T) {
|
2020-01-09 13:17:43 -05:00
|
|
|
var (
|
|
|
|
// we need a pair of variables for setting the job parameters, because
|
|
|
|
// those parameters take pointers to uint64, which we can't make as a
|
|
|
|
// literal
|
|
|
|
varThree uint64 = 3
|
|
|
|
varTen uint64 = 10
|
|
|
|
)
|
2017-01-26 16:08:07 -05:00
|
|
|
cases := []struct {
|
2018-10-23 11:05:44 -04:00
|
|
|
context formatter.Context
|
2017-01-26 16:08:07 -05:00
|
|
|
expected string
|
|
|
|
}{
|
|
|
|
// Errors
|
|
|
|
{
|
2018-10-23 11:05:44 -04:00
|
|
|
formatter.Context{Format: "{{InvalidFunction}}"},
|
linting: fix incorrectly formatted errors (revive)
cli/compose/interpolation/interpolation.go:102:4: error-strings: error strings should not be capitalized or end with punctuation or a newline (revive)
"invalid interpolation format for %s: %#v. You may need to escape any $ with another $.",
^
cli/command/stack/loader/loader.go:30:30: error-strings: error strings should not be capitalized or end with punctuation or a newline (revive)
return nil, errors.Errorf("Compose file contains unsupported options:\n\n%s\n",
^
cli/command/formatter/formatter.go:76:30: error-strings: error strings should not be capitalized or end with punctuation or a newline (revive)
return tmpl, errors.Errorf("Template parsing error: %v\n", err)
^
cli/command/formatter/formatter.go:97:24: error-strings: error strings should not be capitalized or end with punctuation or a newline (revive)
return errors.Errorf("Template parsing error: %v\n", err)
^
cli/command/image/build.go:257:25: error-strings: error strings should not be capitalized or end with punctuation or a newline (revive)
return errors.Errorf("error checking context: '%s'.", err)
^
cli/command/volume/create.go:35:27: error-strings: error strings should not be capitalized or end with punctuation or a newline (revive)
return errors.Errorf("Conflicting options: either specify --name or provide positional arg, not both\n")
^
cli/command/container/create.go:160:24: error-strings: error strings should not be capitalized or end with punctuation or a newline (revive)
return errors.Errorf("failed to remove the CID file '%s': %s \n", cid.path, err)
^
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2022-03-27 15:13:03 -04:00
|
|
|
`template parsing error: template: :1: function "InvalidFunction" not defined`,
|
2017-01-26 16:08:07 -05:00
|
|
|
},
|
|
|
|
{
|
2018-10-23 11:05:44 -04:00
|
|
|
formatter.Context{Format: "{{nil}}"},
|
linting: fix incorrectly formatted errors (revive)
cli/compose/interpolation/interpolation.go:102:4: error-strings: error strings should not be capitalized or end with punctuation or a newline (revive)
"invalid interpolation format for %s: %#v. You may need to escape any $ with another $.",
^
cli/command/stack/loader/loader.go:30:30: error-strings: error strings should not be capitalized or end with punctuation or a newline (revive)
return nil, errors.Errorf("Compose file contains unsupported options:\n\n%s\n",
^
cli/command/formatter/formatter.go:76:30: error-strings: error strings should not be capitalized or end with punctuation or a newline (revive)
return tmpl, errors.Errorf("Template parsing error: %v\n", err)
^
cli/command/formatter/formatter.go:97:24: error-strings: error strings should not be capitalized or end with punctuation or a newline (revive)
return errors.Errorf("Template parsing error: %v\n", err)
^
cli/command/image/build.go:257:25: error-strings: error strings should not be capitalized or end with punctuation or a newline (revive)
return errors.Errorf("error checking context: '%s'.", err)
^
cli/command/volume/create.go:35:27: error-strings: error strings should not be capitalized or end with punctuation or a newline (revive)
return errors.Errorf("Conflicting options: either specify --name or provide positional arg, not both\n")
^
cli/command/container/create.go:160:24: error-strings: error strings should not be capitalized or end with punctuation or a newline (revive)
return errors.Errorf("failed to remove the CID file '%s': %s \n", cid.path, err)
^
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2022-03-27 15:13:03 -04:00
|
|
|
`template parsing error: template: :1:2: executing "" at <nil>: nil is not a command`,
|
2017-01-26 16:08:07 -05:00
|
|
|
},
|
|
|
|
// Table format
|
|
|
|
{
|
2018-10-23 11:05:44 -04:00
|
|
|
formatter.Context{Format: NewListFormat("table", false)},
|
2020-08-28 17:00:21 -04:00
|
|
|
`ID NAME MODE REPLICAS IMAGE PORTS
|
|
|
|
02_bar bar replicated 2/4 *:80->8090/udp
|
|
|
|
01_baz baz global 1/3 *:80->8080/tcp
|
|
|
|
04_qux2 qux2 replicated 3/3 (max 2 per node)
|
|
|
|
03_qux10 qux10 replicated 2/3 (max 1 per node)
|
|
|
|
05_job1 zarp1 replicated job 2/3 (5/10 completed)
|
|
|
|
06_job2 zarp2 global job 1/1 (3/4 completed)
|
2017-01-26 16:08:07 -05:00
|
|
|
`,
|
|
|
|
},
|
|
|
|
{
|
2018-10-23 11:05:44 -04:00
|
|
|
formatter.Context{Format: NewListFormat("table", true)},
|
2019-10-23 18:16:35 -04:00
|
|
|
`02_bar
|
|
|
|
01_baz
|
|
|
|
04_qux2
|
|
|
|
03_qux10
|
2020-01-09 13:17:43 -05:00
|
|
|
05_job1
|
|
|
|
06_job2
|
2017-01-26 16:08:07 -05:00
|
|
|
`,
|
|
|
|
},
|
|
|
|
{
|
2019-10-23 18:16:35 -04:00
|
|
|
formatter.Context{Format: NewListFormat("table {{.Name}}\t{{.Mode}}", false)},
|
2020-08-28 17:00:21 -04:00
|
|
|
`NAME MODE
|
|
|
|
bar replicated
|
|
|
|
baz global
|
|
|
|
qux2 replicated
|
|
|
|
qux10 replicated
|
|
|
|
zarp1 replicated job
|
|
|
|
zarp2 global job
|
2017-01-26 16:08:07 -05:00
|
|
|
`,
|
|
|
|
},
|
|
|
|
{
|
2018-10-23 11:05:44 -04:00
|
|
|
formatter.Context{Format: NewListFormat("table {{.Name}}", true)},
|
2017-01-26 16:08:07 -05:00
|
|
|
`NAME
|
|
|
|
bar
|
2019-10-23 18:16:35 -04:00
|
|
|
baz
|
|
|
|
qux2
|
|
|
|
qux10
|
2020-01-09 13:17:43 -05:00
|
|
|
zarp1
|
|
|
|
zarp2
|
2017-01-26 16:08:07 -05:00
|
|
|
`,
|
|
|
|
},
|
|
|
|
// Raw Format
|
|
|
|
{
|
2018-10-23 11:05:44 -04:00
|
|
|
formatter.Context{Format: NewListFormat("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
|
|
|
},
|
|
|
|
{
|
2018-10-23 11:05:44 -04:00
|
|
|
formatter.Context{Format: NewListFormat("raw", true)},
|
2019-10-23 18:16:35 -04:00
|
|
|
`id: 02_bar
|
|
|
|
id: 01_baz
|
|
|
|
id: 04_qux2
|
|
|
|
id: 03_qux10
|
2020-01-09 13:17:43 -05:00
|
|
|
id: 05_job1
|
|
|
|
id: 06_job2
|
2017-01-26 16:08:07 -05:00
|
|
|
`,
|
|
|
|
},
|
|
|
|
// Custom Format
|
|
|
|
{
|
2018-10-23 11:05:44 -04:00
|
|
|
formatter.Context{Format: NewListFormat("{{.Name}}", false)},
|
2019-10-23 18:16:35 -04:00
|
|
|
`bar
|
|
|
|
baz
|
|
|
|
qux2
|
|
|
|
qux10
|
2020-01-09 13:17:43 -05:00
|
|
|
zarp1
|
|
|
|
zarp2
|
2017-01-26 16:08:07 -05:00
|
|
|
`,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
2020-08-28 17:00:21 -04:00
|
|
|
services := []swarm.Service{
|
|
|
|
{
|
|
|
|
ID: "01_baz",
|
|
|
|
Spec: swarm.ServiceSpec{
|
|
|
|
Annotations: swarm.Annotations{Name: "baz"},
|
|
|
|
Mode: swarm.ServiceMode{
|
|
|
|
Global: &swarm.GlobalService{},
|
2019-10-23 18:16:35 -04:00
|
|
|
},
|
2017-02-08 01:51:33 -05:00
|
|
|
},
|
2020-08-28 17:00:21 -04:00
|
|
|
Endpoint: swarm.Endpoint{
|
|
|
|
Ports: []swarm.PortConfig{
|
|
|
|
{
|
|
|
|
PublishMode: "ingress",
|
|
|
|
PublishedPort: 80,
|
|
|
|
TargetPort: 8080,
|
|
|
|
Protocol: "tcp",
|
2017-02-08 01:51:33 -05:00
|
|
|
},
|
|
|
|
},
|
2020-08-28 17:00:21 -04:00
|
|
|
},
|
|
|
|
ServiceStatus: &swarm.ServiceStatus{
|
|
|
|
RunningTasks: 1,
|
|
|
|
DesiredTasks: 3,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
ID: "02_bar",
|
|
|
|
Spec: swarm.ServiceSpec{
|
|
|
|
Annotations: swarm.Annotations{Name: "bar"},
|
|
|
|
Mode: swarm.ServiceMode{
|
|
|
|
Replicated: &swarm.ReplicatedService{},
|
2019-10-23 18:16:35 -04:00
|
|
|
},
|
2017-02-08 01:51:33 -05:00
|
|
|
},
|
2020-08-28 17:00:21 -04:00
|
|
|
Endpoint: swarm.Endpoint{
|
|
|
|
Ports: []swarm.PortConfig{
|
|
|
|
{
|
|
|
|
PublishMode: "ingress",
|
|
|
|
PublishedPort: 80,
|
|
|
|
TargetPort: 8090,
|
|
|
|
Protocol: "udp",
|
2019-10-23 18:16:35 -04:00
|
|
|
},
|
|
|
|
},
|
2017-01-26 16:08:07 -05:00
|
|
|
},
|
2020-08-28 17:00:21 -04:00
|
|
|
ServiceStatus: &swarm.ServiceStatus{
|
|
|
|
RunningTasks: 2,
|
|
|
|
DesiredTasks: 4,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
ID: "03_qux10",
|
|
|
|
Spec: swarm.ServiceSpec{
|
|
|
|
Annotations: swarm.Annotations{Name: "qux10"},
|
|
|
|
Mode: swarm.ServiceMode{
|
|
|
|
Replicated: &swarm.ReplicatedService{},
|
2019-10-23 18:16:35 -04:00
|
|
|
},
|
2020-08-28 17:00:21 -04:00
|
|
|
TaskTemplate: swarm.TaskSpec{
|
|
|
|
Placement: &swarm.Placement{MaxReplicas: 1},
|
2019-10-23 18:16:35 -04:00
|
|
|
},
|
2017-01-26 16:08:07 -05:00
|
|
|
},
|
2020-08-28 17:00:21 -04:00
|
|
|
ServiceStatus: &swarm.ServiceStatus{
|
|
|
|
RunningTasks: 2,
|
|
|
|
DesiredTasks: 3,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
ID: "04_qux2",
|
|
|
|
Spec: swarm.ServiceSpec{
|
|
|
|
Annotations: swarm.Annotations{Name: "qux2"},
|
|
|
|
Mode: swarm.ServiceMode{
|
|
|
|
Replicated: &swarm.ReplicatedService{},
|
2020-01-09 13:17:43 -05:00
|
|
|
},
|
2020-08-28 17:00:21 -04:00
|
|
|
TaskTemplate: swarm.TaskSpec{
|
|
|
|
Placement: &swarm.Placement{MaxReplicas: 2},
|
2020-01-09 13:17:43 -05:00
|
|
|
},
|
|
|
|
},
|
2020-08-28 17:00:21 -04:00
|
|
|
ServiceStatus: &swarm.ServiceStatus{
|
|
|
|
RunningTasks: 3,
|
|
|
|
DesiredTasks: 3,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
ID: "05_job1",
|
|
|
|
Spec: swarm.ServiceSpec{
|
|
|
|
Annotations: swarm.Annotations{Name: "zarp1"},
|
|
|
|
Mode: swarm.ServiceMode{
|
|
|
|
ReplicatedJob: &swarm.ReplicatedJob{
|
|
|
|
MaxConcurrent: &varThree,
|
|
|
|
TotalCompletions: &varTen,
|
2020-01-09 13:17:43 -05:00
|
|
|
},
|
|
|
|
},
|
2020-08-28 17:00:21 -04:00
|
|
|
},
|
|
|
|
ServiceStatus: &swarm.ServiceStatus{
|
|
|
|
RunningTasks: 2,
|
|
|
|
DesiredTasks: 3,
|
|
|
|
CompletedTasks: 5,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
ID: "06_job2",
|
|
|
|
Spec: swarm.ServiceSpec{
|
|
|
|
Annotations: swarm.Annotations{Name: "zarp2"},
|
|
|
|
Mode: swarm.ServiceMode{
|
|
|
|
GlobalJob: &swarm.GlobalJob{},
|
2020-01-09 13:17:43 -05:00
|
|
|
},
|
|
|
|
},
|
2020-08-28 17:00:21 -04:00
|
|
|
ServiceStatus: &swarm.ServiceStatus{
|
|
|
|
RunningTasks: 1,
|
|
|
|
DesiredTasks: 1,
|
|
|
|
CompletedTasks: 3,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, tc := range cases {
|
|
|
|
tc := tc
|
|
|
|
t.Run(string(tc.context.Format), func(t *testing.T) {
|
|
|
|
var out bytes.Buffer
|
|
|
|
tc.context.Output = &out
|
|
|
|
|
|
|
|
if err := ListFormatWrite(tc.context, services); err != nil {
|
|
|
|
assert.Error(t, err, tc.expected)
|
|
|
|
} else {
|
|
|
|
assert.Equal(t, out.String(), tc.expected)
|
|
|
|
}
|
|
|
|
})
|
2017-01-26 16:08:07 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestServiceContextWriteJSON(t *testing.T) {
|
|
|
|
services := []swarm.Service{
|
2017-02-08 01:51:33 -05:00
|
|
|
{
|
2019-10-23 18:16:35 -04:00
|
|
|
ID: "01_baz",
|
2017-02-08 01:51:33 -05:00
|
|
|
Spec: swarm.ServiceSpec{
|
|
|
|
Annotations: swarm.Annotations{Name: "baz"},
|
2019-10-23 18:16:35 -04:00
|
|
|
Mode: swarm.ServiceMode{
|
|
|
|
Global: &swarm.GlobalService{},
|
|
|
|
},
|
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
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
2019-10-23 18:16:35 -04:00
|
|
|
ServiceStatus: &swarm.ServiceStatus{
|
|
|
|
RunningTasks: 1,
|
|
|
|
DesiredTasks: 3,
|
|
|
|
},
|
2017-02-08 01:51:33 -05:00
|
|
|
},
|
|
|
|
{
|
2019-10-23 18:16:35 -04:00
|
|
|
ID: "02_bar",
|
2017-02-08 01:51:33 -05:00
|
|
|
Spec: swarm.ServiceSpec{
|
|
|
|
Annotations: swarm.Annotations{Name: "bar"},
|
2019-10-23 18:16:35 -04:00
|
|
|
Mode: swarm.ServiceMode{
|
|
|
|
Replicated: &swarm.ReplicatedService{},
|
|
|
|
},
|
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
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
2019-10-23 18:16:35 -04:00
|
|
|
ServiceStatus: &swarm.ServiceStatus{
|
|
|
|
RunningTasks: 2,
|
|
|
|
DesiredTasks: 4,
|
|
|
|
},
|
2017-01-26 16:08:07 -05:00
|
|
|
},
|
|
|
|
}
|
2023-11-20 12:04:36 -05:00
|
|
|
expectedJSONs := []map[string]any{
|
2019-10-23 18:16:35 -04:00
|
|
|
{"ID": "02_bar", "Name": "bar", "Mode": "replicated", "Replicas": "2/4", "Image": "", "Ports": "*:80->8080/tcp"},
|
|
|
|
{"ID": "01_baz", "Name": "baz", "Mode": "global", "Replicas": "1/3", "Image": "", "Ports": "*:80->8080/tcp"},
|
2017-01-26 16:08:07 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
out := bytes.NewBufferString("")
|
2019-10-23 18:16:35 -04:00
|
|
|
err := ListFormatWrite(formatter.Context{Format: "{{json .}}", Output: out}, services)
|
2017-01-26 16:08:07 -05:00
|
|
|
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)
|
2023-11-20 12:04:36 -05:00
|
|
|
var m map[string]any
|
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
|
|
|
}
|
|
|
|
}
|
2022-09-29 11:21:51 -04:00
|
|
|
|
2017-01-26 16:08:07 -05:00
|
|
|
func TestServiceContextWriteJSONField(t *testing.T) {
|
|
|
|
services := []swarm.Service{
|
2019-10-23 18:16:35 -04:00
|
|
|
{
|
|
|
|
ID: "01_baz",
|
|
|
|
Spec: swarm.ServiceSpec{
|
|
|
|
Annotations: swarm.Annotations{Name: "baz"},
|
|
|
|
Mode: swarm.ServiceMode{
|
|
|
|
Global: &swarm.GlobalService{},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
ServiceStatus: &swarm.ServiceStatus{
|
|
|
|
RunningTasks: 2,
|
|
|
|
DesiredTasks: 4,
|
|
|
|
},
|
2017-01-26 16:08:07 -05:00
|
|
|
},
|
2019-10-23 18:16:35 -04:00
|
|
|
{
|
|
|
|
ID: "24_bar",
|
|
|
|
Spec: swarm.ServiceSpec{
|
|
|
|
Annotations: swarm.Annotations{Name: "bar"},
|
|
|
|
Mode: swarm.ServiceMode{
|
|
|
|
Replicated: &swarm.ReplicatedService{},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
ServiceStatus: &swarm.ServiceStatus{
|
|
|
|
RunningTasks: 2,
|
|
|
|
DesiredTasks: 4,
|
|
|
|
},
|
2017-01-26 16:08:07 -05:00
|
|
|
},
|
|
|
|
}
|
|
|
|
out := bytes.NewBufferString("")
|
2019-10-23 18:16:35 -04:00
|
|
|
err := ListFormatWrite(formatter.Context{Format: "{{json .Name}}", Output: out}, services)
|
2017-01-26 16:08:07 -05:00
|
|
|
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
|
|
|
}
|