Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 3 additions & 4 deletions cli/command/builder/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,17 @@ package builder
import (
"context"

"github.com/moby/moby/api/types/build"
"github.com/moby/moby/client"
)

type fakeClient struct {
client.Client
builderPruneFunc func(ctx context.Context, opts client.BuildCachePruneOptions) (*build.CachePruneReport, error)
builderPruneFunc func(ctx context.Context, opts client.BuildCachePruneOptions) (client.BuildCachePruneResult, error)
}

func (c *fakeClient) BuildCachePrune(ctx context.Context, opts client.BuildCachePruneOptions) (*build.CachePruneReport, error) {
func (c *fakeClient) BuildCachePrune(ctx context.Context, opts client.BuildCachePruneOptions) (client.BuildCachePruneResult, error) {
if c.builderPruneFunc != nil {
return c.builderPruneFunc(ctx, opts)
}
return nil, nil
return client.BuildCachePruneResult{}, nil
}
4 changes: 2 additions & 2 deletions cli/command/builder/prune.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,15 +87,15 @@ func runPrune(ctx context.Context, dockerCli command.Cli, options pruneOptions)
}
}

report, err := dockerCli.Client().BuildCachePrune(ctx, client.BuildCachePruneOptions{
resp, err := dockerCli.Client().BuildCachePrune(ctx, client.BuildCachePruneOptions{
All: options.all,
ReservedSpace: options.reservedSpace.Value(),
Filters: pruneFilters,
})
if err != nil {
return 0, "", err
}

report := resp.Report
if len(report.CachesDeleted) > 0 {
var sb strings.Builder
sb.WriteString("Deleted build cache objects:\n")
Expand Down
5 changes: 2 additions & 3 deletions cli/command/builder/prune_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import (
"testing"

"github.com/docker/cli/internal/test"
"github.com/moby/moby/api/types/build"
"github.com/moby/moby/client"
)

Expand All @@ -16,8 +15,8 @@ func TestBuilderPromptTermination(t *testing.T) {
t.Cleanup(cancel)

cli := test.NewFakeCli(&fakeClient{
builderPruneFunc: func(ctx context.Context, opts client.BuildCachePruneOptions) (*build.CachePruneReport, error) {
return nil, errors.New("fakeClient builderPruneFunc should not be called")
builderPruneFunc: func(ctx context.Context, opts client.BuildCachePruneOptions) (client.BuildCachePruneResult, error) {
return client.BuildCachePruneResult{}, errors.New("fakeClient builderPruneFunc should not be called")
},
})
cmd := newPruneCommand(cli)
Expand Down
71 changes: 61 additions & 10 deletions cli/command/container/opts.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"encoding/json"
"errors"
"fmt"
"net/netip"
"os"
"path"
"path/filepath"
Expand Down Expand Up @@ -425,15 +426,46 @@ func parse(flags *pflag.FlagSet, copts *containerOptions, serverOS string) (*con
entrypoint = []string{""}
}

// TODO(thaJeztah): remove uses of go-connections/nat here.
convertedOpts, err := convertToStandardNotation(copts.publish.GetSlice())
if err != nil {
return nil, err
}

ports, portBindings, err := nat.ParsePortSpecs(convertedOpts)
ports, natPortBindings, err := nat.ParsePortSpecs(convertedOpts)
if err != nil {
return nil, err
}
portBindings := network.PortMap{}
for port, bindings := range natPortBindings {
p, err := network.ParsePort(string(port))
if err != nil {
return nil, err
}
portBindings[p] = []network.PortBinding{}
for _, b := range bindings {
var hostIP netip.Addr
if b.HostIP != "" {
hostIP, err = netip.ParseAddr(b.HostIP)
if err != nil {
return nil, err
}
}
portBindings[p] = append(portBindings[p], network.PortBinding{
HostIP: hostIP,
HostPort: b.HostPort,
})
}
}

exposedPorts := network.PortSet{}
for port := range ports {
p, err := network.ParsePort(string(port))
if err != nil {
return nil, err
}
exposedPorts[p] = struct{}{}
}

// Merge in exposed ports to the map of published ports
for _, e := range copts.expose.GetSlice() {
Expand Down Expand Up @@ -625,7 +657,7 @@ func parse(flags *pflag.FlagSet, copts *containerOptions, serverOS string) (*con
config := &container.Config{
Hostname: copts.hostname,
Domainname: copts.domainname,
ExposedPorts: ports,
ExposedPorts: exposedPorts,
User: copts.user,
Tty: copts.tty,
OpenStdin: copts.stdin,
Expand Down Expand Up @@ -661,7 +693,7 @@ func parse(flags *pflag.FlagSet, copts *containerOptions, serverOS string) (*con
// but pre created containers can still have those nil values.
// See https://github.com/docker/docker/pull/17779
// for a more detailed explanation on why we don't want that.
DNS: copts.dns.GetAllOrEmpty(),
DNS: toNetipAddrSlice(copts.dns.GetAllOrEmpty()),
DNSSearch: copts.dnsSearch.GetAllOrEmpty(),
DNSOptions: copts.dnsOptions.GetAllOrEmpty(),
ExtraHosts: copts.extraHosts.GetSlice(),
Expand Down Expand Up @@ -804,10 +836,10 @@ func applyContainerOptions(n *opts.NetworkAttachmentOpts, copts *containerOption
if len(n.Links) > 0 && copts.links.Len() > 0 {
return invalidParameter(errors.New("conflicting options: cannot specify both --link and per-network links"))
}
if n.IPv4Address != "" && copts.ipv4Address != "" {
if n.IPv4Address.IsValid() && copts.ipv4Address != "" {
return invalidParameter(errors.New("conflicting options: cannot specify both --ip and per-network IPv4 address"))
}
if n.IPv6Address != "" && copts.ipv6Address != "" {
if n.IPv6Address.IsValid() && copts.ipv6Address != "" {
return invalidParameter(errors.New("conflicting options: cannot specify both --ip6 and per-network IPv6 address"))
}
if n.MacAddress != "" && copts.macAddress != "" {
Expand All @@ -827,17 +859,24 @@ func applyContainerOptions(n *opts.NetworkAttachmentOpts, copts *containerOption
copy(n.Links, copts.links.GetSlice())
}
if copts.ipv4Address != "" {
n.IPv4Address = copts.ipv4Address
var err error
n.IPv4Address, err = netip.ParseAddr(copts.ipv4Address)
if err != nil {
return err
}
}
if copts.ipv6Address != "" {
n.IPv6Address = copts.ipv6Address
var err error
n.IPv6Address, err = netip.ParseAddr(copts.ipv6Address)
if err != nil {
return err
}
}
if copts.macAddress != "" {
n.MacAddress = copts.macAddress
}
if copts.linkLocalIPs.Len() > 0 {
n.LinkLocalIPs = make([]string, copts.linkLocalIPs.Len())
copy(n.LinkLocalIPs, copts.linkLocalIPs.GetSlice())
n.LinkLocalIPs = toNetipAddrSlice(copts.linkLocalIPs.GetSlice())
}
return nil
}
Expand Down Expand Up @@ -866,7 +905,7 @@ func parseNetworkAttachmentOpt(ep opts.NetworkAttachmentOpts) (*network.Endpoint
if len(ep.Links) > 0 {
epConfig.Links = ep.Links
}
if ep.IPv4Address != "" || ep.IPv6Address != "" || len(ep.LinkLocalIPs) > 0 {
if ep.IPv4Address.IsValid() || ep.IPv6Address.IsValid() || len(ep.LinkLocalIPs) > 0 {
epConfig.IPAMConfig = &network.EndpointIPAMConfig{
IPv4Address: ep.IPv4Address,
IPv6Address: ep.IPv6Address,
Expand Down Expand Up @@ -1130,3 +1169,15 @@ func validateAttach(val string) (string, error) {
}
return val, errors.New("valid streams are STDIN, STDOUT and STDERR")
}

func toNetipAddrSlice(ips []string) []netip.Addr {
netips := make([]netip.Addr, 0, len(ips))
for _, ip := range ips {
addr, err := netip.ParseAddr(ip)
if err != nil {
continue
}
netips = append(netips, addr)
}
return netips
}
37 changes: 19 additions & 18 deletions cli/command/container/opts_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"errors"
"fmt"
"io"
"net/netip"
"os"
"runtime"
"strings"
Expand Down Expand Up @@ -438,12 +439,12 @@ func TestParseWithExpose(t *testing.T) {
"8080-NaN/tcp": `invalid range format for --expose: 8080-NaN/tcp, error: strconv.ParseUint: parsing "NaN": invalid syntax`,
"1234567890-8080/tcp": `invalid range format for --expose: 1234567890-8080/tcp, error: strconv.ParseUint: parsing "1234567890": value out of range`,
}
valids := map[string][]container.PortRangeProto{
"8080/tcp": {"8080/tcp"},
"8080/udp": {"8080/udp"},
"8080/ncp": {"8080/ncp"},
"8080-8080/udp": {"8080/udp"},
"8080-8082/tcp": {"8080/tcp", "8081/tcp", "8082/tcp"},
valids := map[string][]networktypes.Port{
"8080/tcp": {networktypes.MustParsePort("8080/tcp")},
"8080/udp": {networktypes.MustParsePort("8080/udp")},
"8080/ncp": {networktypes.MustParsePort("8080/ncp")},
"8080-8080/udp": {networktypes.MustParsePort("8080/udp")},
"8080-8082/tcp": {networktypes.MustParsePort("8080/tcp"), networktypes.MustParsePort("8081/tcp"), networktypes.MustParsePort("8082/tcp")},
}
for expose, expectedError := range invalids {
if _, _, _, err := parseRun([]string{fmt.Sprintf("--expose=%v", expose), "img", "cmd"}); err == nil || err.Error() != expectedError {
Expand Down Expand Up @@ -472,7 +473,7 @@ func TestParseWithExpose(t *testing.T) {
if len(config.ExposedPorts) != 2 {
t.Fatalf("Expected 2 exposed ports, got %v", config.ExposedPorts)
}
ports := []container.PortRangeProto{"80/tcp", "81/tcp"}
ports := []networktypes.Port{networktypes.MustParsePort("80/tcp"), networktypes.MustParsePort("81/tcp")}
for _, port := range ports {
if _, ok := config.ExposedPorts[port]; !ok {
t.Fatalf("Expected %v, got %v", ports, config.ExposedPorts)
Expand Down Expand Up @@ -607,9 +608,9 @@ func TestParseNetworkConfig(t *testing.T) {
expected: map[string]*networktypes.EndpointSettings{
"net1": {
IPAMConfig: &networktypes.EndpointIPAMConfig{
IPv4Address: "172.20.88.22",
IPv6Address: "2001:db8::8822",
LinkLocalIPs: []string{"169.254.2.2", "fe80::169:254:2:2"},
IPv4Address: netip.MustParseAddr("172.20.88.22"),
IPv6Address: netip.MustParseAddr("2001:db8::8822"),
LinkLocalIPs: []netip.Addr{netip.MustParseAddr("169.254.2.2"), netip.MustParseAddr("fe80::169:254:2:2")},
},
Links: []string{"foo:bar", "bar:baz"},
Aliases: []string{"web1", "web2"},
Expand Down Expand Up @@ -637,9 +638,9 @@ func TestParseNetworkConfig(t *testing.T) {
"net1": {
DriverOpts: map[string]string{"field1": "value1"},
IPAMConfig: &networktypes.EndpointIPAMConfig{
IPv4Address: "172.20.88.22",
IPv6Address: "2001:db8::8822",
LinkLocalIPs: []string{"169.254.2.2", "fe80::169:254:2:2"},
IPv4Address: netip.MustParseAddr("172.20.88.22"),
IPv6Address: netip.MustParseAddr("2001:db8::8822"),
LinkLocalIPs: []netip.Addr{netip.MustParseAddr("169.254.2.2"), netip.MustParseAddr("fe80::169:254:2:2")},
},
Links: []string{"foo:bar", "bar:baz"},
Aliases: []string{"web1", "web2"},
Expand All @@ -648,15 +649,15 @@ func TestParseNetworkConfig(t *testing.T) {
"net3": {
DriverOpts: map[string]string{"field3": "value3"},
IPAMConfig: &networktypes.EndpointIPAMConfig{
IPv4Address: "172.20.88.22",
IPv6Address: "2001:db8::8822",
IPv4Address: netip.MustParseAddr("172.20.88.22"),
IPv6Address: netip.MustParseAddr("2001:db8::8822"),
},
Aliases: []string{"web3"},
},
"net4": {
MacAddress: "02:32:1c:23:00:04",
IPAMConfig: &networktypes.EndpointIPAMConfig{
LinkLocalIPs: []string{"169.254.169.254"},
LinkLocalIPs: []netip.Addr{netip.MustParseAddr("169.254.169.254")},
},
},
},
Expand All @@ -672,8 +673,8 @@ func TestParseNetworkConfig(t *testing.T) {
"field2": "value2",
},
IPAMConfig: &networktypes.EndpointIPAMConfig{
IPv4Address: "172.20.88.22",
IPv6Address: "2001:db8::8822",
IPv4Address: netip.MustParseAddr("172.20.88.22"),
IPv6Address: netip.MustParseAddr("2001:db8::8822"),
},
Aliases: []string{"web1", "web2"},
MacAddress: "02:32:1c:23:00:04",
Expand Down
18 changes: 7 additions & 11 deletions cli/command/container/port.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,13 @@ import (
"fmt"
"net"
"sort"
"strconv"
"strings"

"github.com/docker/cli/cli"
"github.com/docker/cli/cli/command"
"github.com/docker/cli/cli/command/completion"
"github.com/fvbommel/sortorder"
"github.com/moby/moby/api/types/container"
"github.com/moby/moby/api/types/network"
"github.com/spf13/cobra"
)

Expand Down Expand Up @@ -60,24 +59,21 @@ func runPort(ctx context.Context, dockerCli command.Cli, opts *portOptions) erro

var out []string
if opts.port != "" {
port, proto, _ := strings.Cut(opts.port, "/")
if proto == "" {
proto = "tcp"
port, err := network.ParsePort(opts.port)
if err != nil {
return err
}
if _, err = strconv.ParseUint(port, 10, 16); err != nil {
return fmt.Errorf("invalid port (%s): %w", port, err)
}
frontends, exists := c.NetworkSettings.Ports[container.PortRangeProto(port+"/"+proto)]
frontends, exists := c.NetworkSettings.Ports[port]
if !exists || len(frontends) == 0 {
return fmt.Errorf("no public port '%s' published for %s", opts.port, opts.container)
}
for _, frontend := range frontends {
out = append(out, net.JoinHostPort(frontend.HostIP, frontend.HostPort))
out = append(out, net.JoinHostPort(frontend.HostIP.String(), frontend.HostPort))
}
} else {
for from, frontends := range c.NetworkSettings.Ports {
for _, frontend := range frontends {
out = append(out, fmt.Sprintf("%s -> %s", from, net.JoinHostPort(frontend.HostIP, frontend.HostPort)))
out = append(out, fmt.Sprintf("%s -> %s", from, net.JoinHostPort(frontend.HostIP.String(), frontend.HostPort)))
}
}
}
Expand Down
Loading
Loading