Use net.JoinHostPort() to fix formatting with IPv6 addresses

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
This commit is contained in:
Sebastiaan van Stijn 2021-02-15 16:00:07 +01:00
parent daf5f126ad
commit 168173a3f1
No known key found for this signature in database
GPG Key ID: 76698F39D527CE8C
7 changed files with 111 additions and 4 deletions

View File

@ -3,6 +3,7 @@ package container
import ( import (
"context" "context"
"fmt" "fmt"
"net"
"strings" "strings"
"github.com/docker/cli/cli" "github.com/docker/cli/cli"
@ -61,7 +62,7 @@ func runPort(dockerCli command.Cli, opts *portOptions) error {
} }
if frontends, exists := c.NetworkSettings.Ports[newP]; exists && frontends != nil { if frontends, exists := c.NetworkSettings.Ports[newP]; exists && frontends != nil {
for _, frontend := range frontends { for _, frontend := range frontends {
fmt.Fprintf(dockerCli.Out(), "%s:%s\n", frontend.HostIP, frontend.HostPort) fmt.Fprintln(dockerCli.Out(), net.JoinHostPort(frontend.HostIP, frontend.HostPort))
} }
return nil return nil
} }
@ -70,7 +71,7 @@ func runPort(dockerCli command.Cli, opts *portOptions) error {
for from, frontends := range c.NetworkSettings.Ports { for from, frontends := range c.NetworkSettings.Ports {
for _, frontend := range frontends { for _, frontend := range frontends {
fmt.Fprintf(dockerCli.Out(), "%s -> %s:%s\n", from, frontend.HostIP, frontend.HostPort) fmt.Fprintf(dockerCli.Out(), "%s -> %s\n", from, net.JoinHostPort(frontend.HostIP, frontend.HostPort))
} }
} }

View File

@ -0,0 +1,57 @@
package container
import (
"io/ioutil"
"testing"
"github.com/docker/cli/internal/test"
"github.com/docker/docker/api/types"
"github.com/docker/go-connections/nat"
"gotest.tools/v3/assert"
"gotest.tools/v3/golden"
)
func TestNewPortCommandOutput(t *testing.T) {
testCases := []struct {
name string
ips []string
}{
{
name: "container-port-ipv4",
ips: []string{"0.0.0.0"},
},
{
name: "container-port-ipv6",
ips: []string{"::"},
},
{
name: "container-port-ipv6-and-ipv4",
ips: []string{"::", "0.0.0.0"},
},
}
for _, tc := range testCases {
tc := tc
t.Run(tc.name, func(t *testing.T) {
cli := test.NewFakeCli(&fakeClient{
inspectFunc: func(string) (types.ContainerJSON, error) {
ci := types.ContainerJSON{NetworkSettings: &types.NetworkSettings{}}
ci.NetworkSettings.Ports = nat.PortMap{
"80/tcp": make([]nat.PortBinding, len(tc.ips)),
}
for i, ip := range tc.ips {
ci.NetworkSettings.Ports["80/tcp"][i] = nat.PortBinding{
HostIP: ip, HostPort: "3456",
}
}
return ci, nil
},
}, test.EnableContentTrust)
cmd := NewPortCommand(cli)
cmd.SetErr(ioutil.Discard)
cmd.SetArgs([]string{"some_container", "80"})
err := cmd.Execute()
assert.NilError(t, err)
golden.Assert(t, cli.OutBuffer().String(), tc.name+".golden")
})
}
}

View File

@ -0,0 +1 @@
0.0.0.0:3456

View File

@ -0,0 +1,2 @@
[::]:3456
0.0.0.0:3456

View File

@ -0,0 +1 @@
[::]:3456

View File

@ -3,6 +3,7 @@ package opts
import ( import (
"encoding/csv" "encoding/csv"
"fmt" "fmt"
"net"
"regexp" "regexp"
"strconv" "strconv"
"strings" "strings"
@ -148,8 +149,8 @@ func ConvertPortToPortConfig(
ports := []swarm.PortConfig{} ports := []swarm.PortConfig{}
for _, binding := range portBindings[port] { for _, binding := range portBindings[port] {
if binding.HostIP != "" && binding.HostIP != "0.0.0.0" { if p := net.ParseIP(binding.HostIP); p != nil && !p.IsUnspecified() {
logrus.Warnf("ignoring IP-address (%s:%s:%s) service will listen on '0.0.0.0'", binding.HostIP, binding.HostPort, port) logrus.Warnf("ignoring IP-address (%s:%s) service will listen on '0.0.0.0'", net.JoinHostPort(binding.HostIP, binding.HostPort), port)
} }
startHostPort, endHostPort, err := nat.ParsePortRange(binding.HostPort) startHostPort, endHostPort, err := nat.ParsePortRange(binding.HostPort)

View File

@ -1,9 +1,13 @@
package opts package opts
import ( import (
"bytes"
"os"
"testing" "testing"
"github.com/docker/docker/api/types/swarm" "github.com/docker/docker/api/types/swarm"
"github.com/docker/go-connections/nat"
"github.com/sirupsen/logrus"
"gotest.tools/v3/assert" "gotest.tools/v3/assert"
is "gotest.tools/v3/assert/cmp" is "gotest.tools/v3/assert/cmp"
) )
@ -316,6 +320,46 @@ func TestPortOptInvalidSimpleSyntax(t *testing.T) {
} }
} }
func TestConvertPortToPortConfigWithIP(t *testing.T) {
testCases := []struct {
value string
expectedWarning string
}{
{
value: "0.0.0.0",
},
{
value: "::",
},
{
value: "192.168.1.5",
expectedWarning: `ignoring IP-address (192.168.1.5:2345:80/tcp) service will listen on '0.0.0.0'`,
},
{
value: "::2",
expectedWarning: `ignoring IP-address ([::2]:2345:80/tcp) service will listen on '0.0.0.0'`,
},
}
var b bytes.Buffer
logrus.SetOutput(&b)
for _, tc := range testCases {
tc := tc
t.Run(tc.value, func(t *testing.T) {
_, err := ConvertPortToPortConfig("80/tcp", map[nat.Port][]nat.PortBinding{
"80/tcp": {{HostIP: tc.value, HostPort: "2345"}},
})
assert.NilError(t, err)
if tc.expectedWarning == "" {
assert.Equal(t, b.String(), "")
} else {
assert.Assert(t, is.Contains(b.String(), tc.expectedWarning))
}
})
}
logrus.SetOutput(os.Stderr)
}
func assertContains(t *testing.T, portConfigs []swarm.PortConfig, expected swarm.PortConfig) { func assertContains(t *testing.T, portConfigs []swarm.PortConfig, expected swarm.PortConfig) {
var contains = false var contains = false
for _, portConfig := range portConfigs { for _, portConfig := range portConfigs {