mirror of
https://github.com/moby/moby.git
synced 2026-01-11 02:31:44 +00:00
api/types/network: use netip types as appropriate
And generate the ServiceInfo struct from the Swagger spec. Signed-off-by: Cory Snider <csnider@mirantis.com>
This commit is contained in:
committed by
Sebastiaan van Stijn
parent
ef31514a9f
commit
a90adb6dc1
@@ -2455,6 +2455,10 @@ definitions:
|
||||
VIP:
|
||||
type: "string"
|
||||
x-omitempty: false
|
||||
x-go-type:
|
||||
type: Addr
|
||||
import:
|
||||
package: net/netip
|
||||
Ports:
|
||||
type: "array"
|
||||
x-omitempty: false
|
||||
@@ -2464,6 +2468,8 @@ definitions:
|
||||
type: "integer"
|
||||
format: "int"
|
||||
x-omitempty: false
|
||||
x-go-type:
|
||||
type: int
|
||||
Tasks:
|
||||
type: "array"
|
||||
x-omitempty: false
|
||||
@@ -2487,6 +2493,10 @@ definitions:
|
||||
EndpointIP:
|
||||
type: "string"
|
||||
x-omitempty: false
|
||||
x-go-type:
|
||||
type: Addr
|
||||
import:
|
||||
package: net/netip
|
||||
Info:
|
||||
type: "object"
|
||||
x-omitempty: false
|
||||
@@ -2617,10 +2627,18 @@ definitions:
|
||||
type: "string"
|
||||
x-omitempty: false
|
||||
example: "172.19.0.2/16"
|
||||
x-go-type:
|
||||
type: Prefix
|
||||
import:
|
||||
package: net/netip
|
||||
IPv6Address:
|
||||
type: "string"
|
||||
x-omitempty: false
|
||||
example: ""
|
||||
x-go-type:
|
||||
type: Prefix
|
||||
import:
|
||||
package: net/netip
|
||||
|
||||
PeerInfo:
|
||||
description: >
|
||||
@@ -2640,6 +2658,10 @@ definitions:
|
||||
type: "string"
|
||||
x-omitempty: false
|
||||
example: "10.133.77.91"
|
||||
x-go-type:
|
||||
type: Addr
|
||||
import:
|
||||
package: net/netip
|
||||
|
||||
NetworkCreateResponse:
|
||||
description: "OK response to NetworkCreate operation"
|
||||
@@ -2910,6 +2932,10 @@ definitions:
|
||||
IPv4 address.
|
||||
type: "string"
|
||||
example: "172.17.0.4"
|
||||
x-go-type:
|
||||
type: Addr
|
||||
import:
|
||||
package: net/netip
|
||||
IPPrefixLen:
|
||||
description: |
|
||||
Mask length of the IPv4 address.
|
||||
@@ -2920,11 +2946,19 @@ definitions:
|
||||
IPv6 gateway address.
|
||||
type: "string"
|
||||
example: "2001:db8:2::100"
|
||||
x-go-type:
|
||||
type: Addr
|
||||
import:
|
||||
package: net/netip
|
||||
GlobalIPv6Address:
|
||||
description: |
|
||||
Global IPv6 address.
|
||||
type: "string"
|
||||
example: "2001:db8::5689"
|
||||
x-go-type:
|
||||
type: Addr
|
||||
import:
|
||||
package: net/netip
|
||||
GlobalIPv6PrefixLen:
|
||||
description: |
|
||||
Mask length of the global IPv6 address.
|
||||
@@ -2956,13 +2990,25 @@ definitions:
|
||||
IPv4Address:
|
||||
type: "string"
|
||||
example: "172.20.30.33"
|
||||
x-go-type:
|
||||
type: Addr
|
||||
import:
|
||||
package: net/netip
|
||||
IPv6Address:
|
||||
type: "string"
|
||||
example: "2001:db8:abcd::3033"
|
||||
x-go-type:
|
||||
type: Addr
|
||||
import:
|
||||
package: net/netip
|
||||
LinkLocalIPs:
|
||||
type: "array"
|
||||
items:
|
||||
type: "string"
|
||||
x-go-type:
|
||||
type: Addr
|
||||
import:
|
||||
package: net/netip
|
||||
example:
|
||||
- "169.254.34.68"
|
||||
- "fe80::3468"
|
||||
|
||||
@@ -2,6 +2,7 @@ package network
|
||||
|
||||
import (
|
||||
"maps"
|
||||
"net/netip"
|
||||
"slices"
|
||||
)
|
||||
|
||||
@@ -25,11 +26,11 @@ type EndpointSettings struct {
|
||||
// Operational data
|
||||
NetworkID string
|
||||
EndpointID string
|
||||
Gateway string
|
||||
IPAddress string
|
||||
Gateway netip.Addr
|
||||
IPAddress netip.Addr
|
||||
IPPrefixLen int
|
||||
IPv6Gateway string
|
||||
GlobalIPv6Address string
|
||||
IPv6Gateway netip.Addr
|
||||
GlobalIPv6Address netip.Addr
|
||||
GlobalIPv6PrefixLen int
|
||||
// DNSNames holds all the (non fully qualified) DNS names associated to this endpoint. First entry is used to
|
||||
// generate PTR records.
|
||||
@@ -54,9 +55,9 @@ func (es *EndpointSettings) Copy() *EndpointSettings {
|
||||
|
||||
// EndpointIPAMConfig represents IPAM configurations for the endpoint
|
||||
type EndpointIPAMConfig struct {
|
||||
IPv4Address string `json:",omitempty"`
|
||||
IPv6Address string `json:",omitempty"`
|
||||
LinkLocalIPs []string `json:",omitempty"`
|
||||
IPv4Address netip.Addr `json:",omitempty"`
|
||||
IPv6Address netip.Addr `json:",omitempty"`
|
||||
LinkLocalIPs []netip.Addr `json:",omitempty"`
|
||||
}
|
||||
|
||||
// Copy makes a copy of the endpoint ipam config
|
||||
|
||||
@@ -5,6 +5,10 @@ package network
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the swagger generate command
|
||||
|
||||
import (
|
||||
"net/netip"
|
||||
)
|
||||
|
||||
// EndpointResource contains network resources allocated and used for a container in a network.
|
||||
//
|
||||
// swagger:model EndpointResource
|
||||
@@ -24,8 +28,8 @@ type EndpointResource struct {
|
||||
|
||||
// IPv4 address
|
||||
// Example: 172.19.0.2/16
|
||||
IPv4Address string `json:"IPv4Address"`
|
||||
IPv4Address netip.Prefix `json:"IPv4Address"`
|
||||
|
||||
// IPv6 address
|
||||
IPv6Address string `json:"IPv6Address"`
|
||||
IPv6Address netip.Prefix `json:"IPv6Address"`
|
||||
}
|
||||
|
||||
@@ -13,10 +13,10 @@ type IPAM struct {
|
||||
|
||||
// IPAMConfig represents IPAM configurations
|
||||
type IPAMConfig struct {
|
||||
Subnet string `json:",omitempty"`
|
||||
IPRange string `json:",omitempty"`
|
||||
Gateway string `json:",omitempty"`
|
||||
AuxAddress map[string]string `json:"AuxiliaryAddresses,omitempty"`
|
||||
Subnet netip.Prefix `json:",omitempty"`
|
||||
IPRange netip.Prefix `json:",omitempty"`
|
||||
Gateway netip.Addr `json:",omitempty"`
|
||||
AuxAddress map[string]netip.Addr `json:"AuxiliaryAddresses,omitempty"`
|
||||
}
|
||||
|
||||
type SubnetStatuses = map[netip.Prefix]SubnetStatus
|
||||
|
||||
@@ -30,14 +30,6 @@ type CreateRequest struct {
|
||||
Labels map[string]string // Labels holds metadata specific to the network being created.
|
||||
}
|
||||
|
||||
// ServiceInfo represents service parameters with the list of service's tasks
|
||||
type ServiceInfo struct {
|
||||
VIP string
|
||||
Ports []string
|
||||
LocalLBIndex int
|
||||
Tasks []Task
|
||||
}
|
||||
|
||||
// NetworkingConfig represents the container's networking configuration for each of its interfaces
|
||||
// Carries the networking configs specified in the `docker run` and `docker network connect` commands
|
||||
type NetworkingConfig struct {
|
||||
|
||||
@@ -5,6 +5,10 @@ package network
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the swagger generate command
|
||||
|
||||
import (
|
||||
"net/netip"
|
||||
)
|
||||
|
||||
// PeerInfo represents one peer of an overlay network.
|
||||
//
|
||||
// swagger:model PeerInfo
|
||||
@@ -16,5 +20,5 @@ type PeerInfo struct {
|
||||
|
||||
// IP-address of the peer-node in the Swarm cluster.
|
||||
// Example: 10.133.77.91
|
||||
IP string `json:"IP"`
|
||||
IP netip.Addr `json:"IP"`
|
||||
}
|
||||
|
||||
28
api/types/network/service_info.go
Normal file
28
api/types/network/service_info.go
Normal file
@@ -0,0 +1,28 @@
|
||||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
package network
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the swagger generate command
|
||||
|
||||
import (
|
||||
"net/netip"
|
||||
)
|
||||
|
||||
// ServiceInfo represents service parameters with the list of service's tasks
|
||||
//
|
||||
// swagger:model ServiceInfo
|
||||
type ServiceInfo struct {
|
||||
|
||||
// v IP
|
||||
VIP netip.Addr `json:"VIP"`
|
||||
|
||||
// ports
|
||||
Ports []string `json:"Ports"`
|
||||
|
||||
// local l b index
|
||||
LocalLBIndex int `json:"LocalLBIndex"`
|
||||
|
||||
// tasks
|
||||
Tasks []Task `json:"Tasks"`
|
||||
}
|
||||
@@ -5,6 +5,10 @@ package network
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the swagger generate command
|
||||
|
||||
import (
|
||||
"net/netip"
|
||||
)
|
||||
|
||||
// Task carries the information about one backend task
|
||||
//
|
||||
// swagger:model Task
|
||||
@@ -17,7 +21,7 @@ type Task struct {
|
||||
EndpointID string `json:"EndpointID"`
|
||||
|
||||
// endpoint IP
|
||||
EndpointIP string `json:"EndpointIP"`
|
||||
EndpointIP netip.Addr `json:"EndpointIP"`
|
||||
|
||||
// info
|
||||
Info map[string]string `json:"Info"`
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package convert
|
||||
|
||||
import (
|
||||
"maps"
|
||||
"net/netip"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -9,9 +10,11 @@ import (
|
||||
"github.com/moby/moby/api/types/network"
|
||||
types "github.com/moby/moby/api/types/swarm"
|
||||
"github.com/moby/moby/v2/daemon/cluster/convert/netextra"
|
||||
"github.com/moby/moby/v2/daemon/internal/netipstringer"
|
||||
"github.com/moby/moby/v2/daemon/internal/netiputil"
|
||||
"github.com/moby/moby/v2/daemon/internal/sliceutil"
|
||||
"github.com/moby/moby/v2/daemon/libnetwork/scope"
|
||||
"github.com/moby/moby/v2/internal/iterutil"
|
||||
swarmapi "github.com/moby/swarmkit/v2/api"
|
||||
)
|
||||
|
||||
@@ -159,12 +162,22 @@ func BasicNetworkFromGRPC(n swarmapi.Network) network.Network {
|
||||
}
|
||||
ipam.Config = make([]network.IPAMConfig, 0, len(n.IPAM.Configs))
|
||||
for _, ic := range n.IPAM.Configs {
|
||||
ipam.Config = append(ipam.Config, network.IPAMConfig{
|
||||
Subnet: ic.Subnet,
|
||||
IPRange: ic.Range,
|
||||
Gateway: ic.Gateway,
|
||||
AuxAddress: ic.Reserved,
|
||||
})
|
||||
// Best-effort parse of user suppplied values that have
|
||||
// been round-tripped through Swarm's Raft store. It is
|
||||
// far too late to reject bogus values.
|
||||
subnet, _ := netiputil.ParseCIDR(ic.Subnet)
|
||||
iprange, _ := netiputil.ParseCIDR(ic.Range)
|
||||
gw, _ := netip.ParseAddr(ic.Gateway)
|
||||
cfg := network.IPAMConfig{
|
||||
Subnet: subnet.Masked(),
|
||||
IPRange: iprange.Masked(),
|
||||
Gateway: gw.Unmap(),
|
||||
}
|
||||
cfg.AuxAddress = maps.Collect(iterutil.Map2(maps.All(ic.Reserved), func(k, v string) (string, netip.Addr) {
|
||||
addr, _ := netip.ParseAddr(v)
|
||||
return k, addr.Unmap()
|
||||
}))
|
||||
ipam.Config = append(ipam.Config, cfg)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -239,9 +252,9 @@ func BasicNetworkCreateToGRPC(create network.CreateRequest) swarmapi.NetworkSpec
|
||||
ipamSpec := make([]*swarmapi.IPAMConfig, 0, len(create.IPAM.Config))
|
||||
for _, ipamConfig := range create.IPAM.Config {
|
||||
ipamSpec = append(ipamSpec, &swarmapi.IPAMConfig{
|
||||
Subnet: ipamConfig.Subnet,
|
||||
Range: ipamConfig.IPRange,
|
||||
Gateway: ipamConfig.Gateway,
|
||||
Subnet: netipstringer.Prefix(netiputil.Unmap(ipamConfig.Subnet).Masked()),
|
||||
Range: netipstringer.Prefix(netiputil.Unmap(ipamConfig.IPRange).Masked()),
|
||||
Gateway: netipstringer.Addr(ipamConfig.Gateway.Unmap()),
|
||||
})
|
||||
}
|
||||
ns.IPAM.Configs = ipamSpec
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"fmt"
|
||||
"math"
|
||||
"net"
|
||||
"net/netip"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
@@ -21,6 +22,7 @@ import (
|
||||
"github.com/moby/moby/v2/daemon/cluster/convert"
|
||||
executorpkg "github.com/moby/moby/v2/daemon/cluster/executor"
|
||||
clustertypes "github.com/moby/moby/v2/daemon/cluster/provider"
|
||||
"github.com/moby/moby/v2/daemon/internal/netiputil"
|
||||
"github.com/moby/moby/v2/daemon/libnetwork/scope"
|
||||
"github.com/moby/swarmkit/v2/agent/exec"
|
||||
"github.com/moby/swarmkit/v2/api"
|
||||
@@ -548,20 +550,18 @@ func (c *containerConfig) createNetworkingConfig(b executorpkg.Backend) *network
|
||||
}
|
||||
|
||||
func getEndpointConfig(na *api.NetworkAttachment, b executorpkg.Backend) *network.EndpointSettings {
|
||||
var ipv4, ipv6 string
|
||||
var ipv4, ipv6 netip.Addr
|
||||
for _, addr := range na.Addresses {
|
||||
ip, _, err := net.ParseCIDR(addr)
|
||||
pfx, err := netiputil.ParseCIDR(addr)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
ip := pfx.Addr()
|
||||
|
||||
if ip.To4() != nil {
|
||||
ipv4 = ip.String()
|
||||
continue
|
||||
}
|
||||
|
||||
if ip.To16() != nil {
|
||||
ipv6 = ip.String()
|
||||
if ip.Is4() {
|
||||
ipv4 = ip
|
||||
} else {
|
||||
ipv6 = ip
|
||||
}
|
||||
}
|
||||
|
||||
@@ -673,11 +673,18 @@ func networkCreateRequest(name string, nw *api.Network) clustertypes.NetworkCrea
|
||||
Options: nw.IPAM.Driver.Options,
|
||||
}
|
||||
for _, ic := range nw.IPAM.Configs {
|
||||
req.IPAM.Config = append(req.IPAM.Config, network.IPAMConfig{
|
||||
Subnet: ic.Subnet,
|
||||
IPRange: ic.Range,
|
||||
Gateway: ic.Gateway,
|
||||
})
|
||||
// The daemon validates the IPAM configs before creating
|
||||
// the network in Swarm's Raft store, so these values
|
||||
// should always either be empty strings or well-formed
|
||||
// values.
|
||||
cfg, err := ipamConfig(ic)
|
||||
if err != nil {
|
||||
log.G(context.TODO()).WithFields(log.Fields{
|
||||
"network": name,
|
||||
"error": err,
|
||||
}).Warn("invalid Swarm network IPAM config")
|
||||
}
|
||||
req.IPAM.Config = append(req.IPAM.Config, cfg)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -225,10 +225,9 @@ func (e *executor) Configure(ctx context.Context, node *api.Node) error {
|
||||
}
|
||||
|
||||
for _, ic := range ingressNA.Network.IPAM.Configs {
|
||||
c := network.IPAMConfig{
|
||||
Subnet: ic.Subnet,
|
||||
IPRange: ic.Range,
|
||||
Gateway: ic.Gateway,
|
||||
c, err := ipamConfig(ic)
|
||||
if err != nil {
|
||||
swarmlog.G(ctx).WithError(err).Warn("invalid IPAM config for Swarm ingress network")
|
||||
}
|
||||
networkCreateRequest.IPAM.Config = append(networkCreateRequest.IPAM.Config, c)
|
||||
}
|
||||
|
||||
32
daemon/cluster/executor/container/network.go
Normal file
32
daemon/cluster/executor/container/network.go
Normal file
@@ -0,0 +1,32 @@
|
||||
package container
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/moby/moby/api/types/network"
|
||||
"github.com/moby/moby/v2/daemon/internal/netiputil"
|
||||
"github.com/moby/swarmkit/v2/api"
|
||||
)
|
||||
|
||||
func ipamConfig(ic *api.IPAMConfig) (network.IPAMConfig, error) {
|
||||
var (
|
||||
cfg network.IPAMConfig
|
||||
errs []error
|
||||
err error
|
||||
)
|
||||
cfg.Subnet, err = netiputil.MaybeParseCIDR(ic.Subnet)
|
||||
if err != nil {
|
||||
errs = append(errs, fmt.Errorf("invalid subnet: %w", err))
|
||||
}
|
||||
cfg.IPRange, err = netiputil.MaybeParseCIDR(ic.Range)
|
||||
if err != nil {
|
||||
errs = append(errs, fmt.Errorf("invalid ip range: %w", err))
|
||||
}
|
||||
gw, err := netiputil.MaybeParseAddr(ic.Gateway)
|
||||
cfg.Gateway = gw.Unmap()
|
||||
if err != nil {
|
||||
errs = append(errs, fmt.Errorf("invalid gateway: %w", err))
|
||||
}
|
||||
return cfg, errors.Join(errs...)
|
||||
}
|
||||
@@ -103,10 +103,10 @@ func buildSandboxOptions(cfg *config.Config, ctr *container.Container) ([]libnet
|
||||
return nil, errors.New("unable to derive the IP value for host-gateway")
|
||||
}
|
||||
for _, gip := range cfg.HostGatewayIPs {
|
||||
sboxOptions = append(sboxOptions, libnetwork.OptionExtraHost(host, gip.String()))
|
||||
sboxOptions = append(sboxOptions, libnetwork.OptionExtraHost(host, gip.Unmap()))
|
||||
}
|
||||
} else {
|
||||
sboxOptions = append(sboxOptions, libnetwork.OptionExtraHost(host, ip))
|
||||
sboxOptions = append(sboxOptions, libnetwork.OptionExtraHost(host, netip.MustParseAddr(ip).Unmap()))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -297,11 +297,11 @@ func (daemon *Daemon) findAndAttachNetwork(ctr *container.Container, idOrName st
|
||||
|
||||
var addresses []string
|
||||
if epConfig != nil && epConfig.IPAMConfig != nil {
|
||||
if epConfig.IPAMConfig.IPv4Address != "" {
|
||||
addresses = append(addresses, epConfig.IPAMConfig.IPv4Address)
|
||||
if epConfig.IPAMConfig.IPv4Address.IsValid() {
|
||||
addresses = append(addresses, epConfig.IPAMConfig.IPv4Address.Unmap().String())
|
||||
}
|
||||
if epConfig.IPAMConfig.IPv6Address != "" {
|
||||
addresses = append(addresses, epConfig.IPAMConfig.IPv6Address)
|
||||
if epConfig.IPAMConfig.IPv6Address.IsValid() {
|
||||
addresses = append(addresses, epConfig.IPAMConfig.IPv6Address.Unmap().String())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -544,7 +544,7 @@ func validateEndpointSettings(nw *libnetwork.Network, nwName string, epConfig *n
|
||||
// TODO(aker): move this into api/types/network/endpoint.go once enableIPOnPredefinedNetwork and
|
||||
// serviceDiscoveryOnDefaultNetwork are removed.
|
||||
if !containertypes.NetworkMode(nwName).IsUserDefined() {
|
||||
hasStaticAddresses := ipamConfig.IPv4Address != "" || ipamConfig.IPv6Address != ""
|
||||
hasStaticAddresses := ipamConfig.IPv4Address.IsValid() || ipamConfig.IPv6Address.IsValid()
|
||||
// On Linux, user specified IP address is accepted only by networks with user specified subnets.
|
||||
if hasStaticAddresses && !enableIPOnPredefinedNetwork() {
|
||||
errs = append(errs, cerrdefs.ErrInvalidArgument.WithMessage("user specified IP address is supported on user defined networks only"))
|
||||
@@ -554,7 +554,7 @@ func validateEndpointSettings(nw *libnetwork.Network, nwName string, epConfig *n
|
||||
}
|
||||
}
|
||||
|
||||
errs = validateEndpointIPAMConfig(errs, ipamConfig)
|
||||
errs = normalizeEndpointIPAMConfig(errs, ipamConfig)
|
||||
|
||||
if nw != nil {
|
||||
_, _, v4Configs, v6Configs := nw.IpamConfig()
|
||||
@@ -590,28 +590,34 @@ func validateEndpointSettings(nw *libnetwork.Network, nwName string, epConfig *n
|
||||
return nil
|
||||
}
|
||||
|
||||
// validateEndpointIPAMConfig checks whether cfg is valid.
|
||||
func validateEndpointIPAMConfig(errs []error, cfg *networktypes.EndpointIPAMConfig) []error {
|
||||
// normalizeEndpointIPAMConfig checks whether cfg is valid and normalizes cfg in-place.
|
||||
func normalizeEndpointIPAMConfig(errs []error, cfg *networktypes.EndpointIPAMConfig) []error {
|
||||
if cfg == nil {
|
||||
return errs
|
||||
}
|
||||
|
||||
if cfg.IPv4Address != "" {
|
||||
if addr := net.ParseIP(cfg.IPv4Address); addr == nil || addr.To4() == nil || addr.IsUnspecified() {
|
||||
if cfg.IPv4Address.IsValid() {
|
||||
if !cfg.IPv4Address.Is4() && !cfg.IPv4Address.Is4In6() || cfg.IPv4Address.IsUnspecified() {
|
||||
errs = append(errs, fmt.Errorf("invalid IPv4 address: %s", cfg.IPv4Address))
|
||||
}
|
||||
}
|
||||
if cfg.IPv6Address != "" {
|
||||
if addr := net.ParseIP(cfg.IPv6Address); addr == nil || addr.To4() != nil || addr.IsUnspecified() {
|
||||
if cfg.IPv6Address.IsValid() {
|
||||
if !cfg.IPv6Address.Is6() || cfg.IPv6Address.Is4In6() || cfg.IPv6Address.IsUnspecified() || cfg.IPv6Address.Zone() != "" {
|
||||
errs = append(errs, fmt.Errorf("invalid IPv6 address: %s", cfg.IPv6Address))
|
||||
}
|
||||
}
|
||||
for _, addr := range cfg.LinkLocalIPs {
|
||||
if parsed := net.ParseIP(addr); parsed == nil || parsed.IsUnspecified() {
|
||||
if !addr.IsValid() || addr.IsUnspecified() {
|
||||
errs = append(errs, fmt.Errorf("invalid link-local IP address: %s", addr))
|
||||
}
|
||||
}
|
||||
|
||||
cfg.IPv4Address = cfg.IPv4Address.Unmap()
|
||||
cfg.IPv6Address = cfg.IPv6Address.Unmap()
|
||||
for i, addr := range cfg.LinkLocalIPs {
|
||||
cfg.LinkLocalIPs[i] = addr.Unmap()
|
||||
}
|
||||
|
||||
return errs
|
||||
}
|
||||
|
||||
@@ -626,17 +632,16 @@ func validateIPAMConfigIsInRange(errs []error, cfg *networktypes.EndpointIPAMCon
|
||||
return errs
|
||||
}
|
||||
|
||||
func validateEndpointIPAddress(epAddr string, ipamSubnets []*libnetwork.IpamConf) error {
|
||||
if epAddr == "" {
|
||||
func validateEndpointIPAddress(epAddr netip.Addr, ipamSubnets []*libnetwork.IpamConf) error {
|
||||
if !epAddr.IsValid() {
|
||||
return nil
|
||||
}
|
||||
|
||||
var staticSubnet bool
|
||||
parsedAddr := net.ParseIP(epAddr)
|
||||
for _, subnet := range ipamSubnets {
|
||||
if subnet.IsStatic() {
|
||||
staticSubnet = true
|
||||
if subnet.Contains(parsedAddr) {
|
||||
if subnet.Contains(epAddr) {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
@@ -652,11 +657,11 @@ func validateEndpointIPAddress(epAddr string, ipamSubnets []*libnetwork.IpamConf
|
||||
// cleanOperationalData resets the operational data from the passed endpoint settings
|
||||
func cleanOperationalData(es *network.EndpointSettings) {
|
||||
es.EndpointID = ""
|
||||
es.Gateway = ""
|
||||
es.IPAddress = ""
|
||||
es.Gateway = netip.Addr{}
|
||||
es.IPAddress = netip.Addr{}
|
||||
es.IPPrefixLen = 0
|
||||
es.IPv6Gateway = ""
|
||||
es.GlobalIPv6Address = ""
|
||||
es.IPv6Gateway = netip.Addr{}
|
||||
es.GlobalIPv6Address = netip.Addr{}
|
||||
es.GlobalIPv6PrefixLen = 0
|
||||
es.MacAddress = ""
|
||||
if es.IPAMOperational {
|
||||
@@ -743,7 +748,7 @@ func (daemon *Daemon) connectToNetwork(ctx context.Context, cfg *config.Config,
|
||||
endpointConfig.IPAMOperational = false
|
||||
if nwCfg != nil {
|
||||
if epConfig, ok := nwCfg.EndpointsConfig[nwName]; ok {
|
||||
if endpointConfig.IPAMConfig == nil || (endpointConfig.IPAMConfig.IPv4Address == "" && endpointConfig.IPAMConfig.IPv6Address == "" && len(endpointConfig.IPAMConfig.LinkLocalIPs) == 0) {
|
||||
if endpointConfig.IPAMConfig == nil || (!endpointConfig.IPAMConfig.IPv4Address.IsValid() && !endpointConfig.IPAMConfig.IPv6Address.IsValid() && len(endpointConfig.IPAMConfig.LinkLocalIPs) == 0) {
|
||||
endpointConfig.IPAMOperational = true
|
||||
}
|
||||
|
||||
@@ -855,11 +860,11 @@ func updateJoinInfo(networkSettings *network.Settings, n *libnetwork.Network, ep
|
||||
// It is not an error to get an empty endpoint info
|
||||
return nil
|
||||
}
|
||||
if epInfo.Gateway() != nil {
|
||||
networkSettings.Networks[n.Name()].Gateway = epInfo.Gateway().String()
|
||||
if gw, ok := netip.AddrFromSlice(epInfo.Gateway()); ok {
|
||||
networkSettings.Networks[n.Name()].Gateway = gw.Unmap()
|
||||
}
|
||||
if epInfo.GatewayIPv6().To16() != nil {
|
||||
networkSettings.Networks[n.Name()].IPv6Gateway = epInfo.GatewayIPv6().String()
|
||||
if gw6, ok := netip.AddrFromSlice(epInfo.GatewayIPv6()); ok && gw6.Is6() {
|
||||
networkSettings.Networks[n.Name()].IPv6Gateway = gw6
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package daemon
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"net/netip"
|
||||
"testing"
|
||||
|
||||
containertypes "github.com/moby/moby/api/types/container"
|
||||
@@ -67,9 +68,9 @@ func TestEndpointIPAMConfigWithOutOfRangeAddrs(t *testing.T) {
|
||||
{
|
||||
name: "valid config",
|
||||
ipamConfig: &networktypes.EndpointIPAMConfig{
|
||||
IPv4Address: "192.168.100.10",
|
||||
IPv6Address: "2a01:d2:af:420b:25c1:1816:bb33:855c",
|
||||
LinkLocalIPs: []string{"169.254.169.254", "fe80::42:a8ff:fe33:6230"},
|
||||
IPv4Address: netip.MustParseAddr("192.168.100.10"),
|
||||
IPv6Address: netip.MustParseAddr("2a01:d2:af:420b:25c1:1816:bb33:855c"),
|
||||
LinkLocalIPs: []netip.Addr{netip.MustParseAddr("169.254.169.254"), netip.MustParseAddr("fe80::42:a8ff:fe33:6230")},
|
||||
},
|
||||
v4Subnets: []*libnetwork.IpamConf{
|
||||
{PreferredPool: "192.168.100.0/24"},
|
||||
@@ -81,8 +82,8 @@ func TestEndpointIPAMConfigWithOutOfRangeAddrs(t *testing.T) {
|
||||
{
|
||||
name: "static addresses out of range",
|
||||
ipamConfig: &networktypes.EndpointIPAMConfig{
|
||||
IPv4Address: "192.168.100.10",
|
||||
IPv6Address: "2a01:d2:af:420b:25c1:1816:bb33:855c",
|
||||
IPv4Address: netip.MustParseAddr("192.168.100.10"),
|
||||
IPv6Address: netip.MustParseAddr("2a01:d2:af:420b:25c1:1816:bb33:855c"),
|
||||
},
|
||||
v4Subnets: []*libnetwork.IpamConf{
|
||||
{PreferredPool: "192.168.255.0/24"},
|
||||
@@ -98,8 +99,8 @@ func TestEndpointIPAMConfigWithOutOfRangeAddrs(t *testing.T) {
|
||||
{
|
||||
name: "static addresses with dynamic network subnets",
|
||||
ipamConfig: &networktypes.EndpointIPAMConfig{
|
||||
IPv4Address: "192.168.100.10",
|
||||
IPv6Address: "2a01:d2:af:420b:25c1:1816:bb33:855c",
|
||||
IPv4Address: netip.MustParseAddr("192.168.100.10"),
|
||||
IPv6Address: netip.MustParseAddr("2a01:d2:af:420b:25c1:1816:bb33:855c"),
|
||||
},
|
||||
v4Subnets: []*libnetwork.IpamConf{
|
||||
{},
|
||||
@@ -141,38 +142,42 @@ func TestEndpointIPAMConfigWithInvalidConfig(t *testing.T) {
|
||||
{
|
||||
name: "valid config",
|
||||
ipamConfig: &networktypes.EndpointIPAMConfig{
|
||||
IPv4Address: "192.168.100.10",
|
||||
IPv6Address: "2a01:d2:af:420b:25c1:1816:bb33:855c",
|
||||
LinkLocalIPs: []string{"169.254.169.254", "fe80::42:a8ff:fe33:6230"},
|
||||
IPv4Address: netip.MustParseAddr("192.168.100.10"),
|
||||
IPv6Address: netip.MustParseAddr("2a01:d2:af:420b:25c1:1816:bb33:855c"),
|
||||
LinkLocalIPs: []netip.Addr{netip.MustParseAddr("169.254.169.254"), netip.MustParseAddr("fe80::42:a8ff:fe33:6230")},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "invalid IP addresses",
|
||||
ipamConfig: &networktypes.EndpointIPAMConfig{
|
||||
IPv4Address: "foo",
|
||||
IPv6Address: "bar",
|
||||
LinkLocalIPs: []string{"baz", "foobar"},
|
||||
IPv4Address: netip.MustParseAddr("2001::1"),
|
||||
IPv6Address: netip.MustParseAddr("1.2.3.4"),
|
||||
},
|
||||
expectedErrors: []string{
|
||||
"invalid IPv4 address: foo",
|
||||
"invalid IPv6 address: bar",
|
||||
"invalid link-local IP address: baz",
|
||||
"invalid link-local IP address: foobar",
|
||||
"invalid IPv4 address: 2001::1",
|
||||
"invalid IPv6 address: 1.2.3.4",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "ipv6 address with a zone",
|
||||
ipamConfig: &networktypes.EndpointIPAMConfig{IPv6Address: "fe80::1cc0:3e8c:119f:c2e1%ens18"},
|
||||
ipamConfig: &networktypes.EndpointIPAMConfig{IPv6Address: netip.MustParseAddr("fe80::1cc0:3e8c:119f:c2e1%ens18")},
|
||||
expectedErrors: []string{
|
||||
"invalid IPv6 address: fe80::1cc0:3e8c:119f:c2e1%ens18",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "ipv6-mapped ipv4 address",
|
||||
ipamConfig: &networktypes.EndpointIPAMConfig{IPv6Address: netip.MustParseAddr("::ffff:192.168.100.10")},
|
||||
expectedErrors: []string{
|
||||
"invalid IPv6 address: ::ffff:192.168.100.10",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "unspecified address is invalid",
|
||||
ipamConfig: &networktypes.EndpointIPAMConfig{
|
||||
IPv4Address: "0.0.0.0",
|
||||
IPv6Address: "::",
|
||||
LinkLocalIPs: []string{"0.0.0.0", "::"},
|
||||
IPv4Address: netip.IPv4Unspecified(),
|
||||
IPv6Address: netip.IPv6Unspecified(),
|
||||
LinkLocalIPs: []netip.Addr{netip.IPv4Unspecified(), netip.IPv6Unspecified()},
|
||||
},
|
||||
expectedErrors: []string{
|
||||
"invalid IPv4 address: 0.0.0.0",
|
||||
@@ -184,7 +189,7 @@ func TestEndpointIPAMConfigWithInvalidConfig(t *testing.T) {
|
||||
{
|
||||
name: "empty link-local",
|
||||
ipamConfig: &networktypes.EndpointIPAMConfig{
|
||||
LinkLocalIPs: []string{""},
|
||||
LinkLocalIPs: make([]netip.Addr, 1),
|
||||
},
|
||||
expectedErrors: []string{"invalid link-local IP address:"},
|
||||
},
|
||||
@@ -192,7 +197,7 @@ func TestEndpointIPAMConfigWithInvalidConfig(t *testing.T) {
|
||||
|
||||
for _, tc := range tests {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
errs := validateEndpointIPAMConfig(nil, tc.ipamConfig)
|
||||
errs := normalizeEndpointIPAMConfig(nil, tc.ipamConfig)
|
||||
if tc.expectedErrors == nil {
|
||||
assert.NilError(t, errors.Join(errs...))
|
||||
return
|
||||
|
||||
@@ -14,6 +14,7 @@ import (
|
||||
"github.com/containerd/log"
|
||||
"github.com/moby/moby/v2/daemon/config"
|
||||
"github.com/moby/moby/v2/daemon/container"
|
||||
"github.com/moby/moby/v2/daemon/internal/netipstringer"
|
||||
"github.com/moby/moby/v2/daemon/internal/stringid"
|
||||
"github.com/moby/moby/v2/daemon/libnetwork"
|
||||
"github.com/moby/moby/v2/daemon/libnetwork/drivers/bridge"
|
||||
@@ -50,8 +51,8 @@ func (daemon *Daemon) setupLinkedContainers(ctr *container.Container) ([]string,
|
||||
// Allow users to restore the old behavior through this escape hatch.
|
||||
if os.Getenv("DOCKER_KEEP_DEPRECATED_LEGACY_LINKS_ENV_VARS") == "1" {
|
||||
linkEnvVars := links.EnvVars(
|
||||
bridgeSettings.IPAddress,
|
||||
childBridgeSettings.IPAddress,
|
||||
netipstringer.Addr(bridgeSettings.IPAddress.Unmap()),
|
||||
netipstringer.Addr(childBridgeSettings.IPAddress.Unmap()),
|
||||
linkAlias,
|
||||
child.Config.Env,
|
||||
child.Config.ExposedPorts,
|
||||
@@ -104,12 +105,12 @@ func (daemon *Daemon) addLegacyLinks(
|
||||
aliasList = aliasList + " " + child.Name[1:]
|
||||
}
|
||||
defaultNW := child.NetworkSettings.Networks[network.DefaultNetwork]
|
||||
if defaultNW.IPAddress != "" {
|
||||
if err := sb.AddHostsEntry(ctx, aliasList, defaultNW.IPAddress); err != nil {
|
||||
if defaultNW.IPAddress.IsValid() {
|
||||
if err := sb.AddHostsEntry(ctx, aliasList, defaultNW.IPAddress.Unmap()); err != nil {
|
||||
return errors.Wrapf(err, "failed to add address to /etc/hosts for link to %s", child.Name)
|
||||
}
|
||||
}
|
||||
if defaultNW.GlobalIPv6Address != "" {
|
||||
if defaultNW.GlobalIPv6Address.IsValid() {
|
||||
if err := sb.AddHostsEntry(ctx, aliasList, defaultNW.GlobalIPv6Address); err != nil {
|
||||
return errors.Wrapf(err, "failed to add IPv6 address to /etc/hosts for link to %s", child.Name)
|
||||
}
|
||||
@@ -130,7 +131,7 @@ func (daemon *Daemon) addLegacyLinks(
|
||||
return errors.Wrapf(err, "failed to update /etc/hosts of %s for alias %s with IP %s",
|
||||
parent.ID, alias, epConfig.IPAddress)
|
||||
}
|
||||
if epConfig.GlobalIPv6Address != "" {
|
||||
if epConfig.GlobalIPv6Address.IsValid() {
|
||||
if err := psb.UpdateHostsEntry(alias, epConfig.GlobalIPv6Address); err != nil {
|
||||
return errors.Wrapf(err, "failed to update /etc/hosts of %s for alias %s with IP %s",
|
||||
parent.ID, alias, epConfig.GlobalIPv6Address)
|
||||
|
||||
25
daemon/internal/netipstringer/stringer.go
Normal file
25
daemon/internal/netipstringer/stringer.go
Normal file
@@ -0,0 +1,25 @@
|
||||
// Package netipstringer provides utilities to convert netip types to strings
|
||||
// which return the empty string for invalid values.
|
||||
package netipstringer
|
||||
|
||||
import (
|
||||
"net/netip"
|
||||
)
|
||||
|
||||
// Addr returns the string representation of addr.
|
||||
// The empty string is returned if addr is not valid.
|
||||
func Addr(addr netip.Addr) string {
|
||||
if !addr.IsValid() {
|
||||
return ""
|
||||
}
|
||||
return addr.String()
|
||||
}
|
||||
|
||||
// Prefix returns the string representation of prefix.
|
||||
// The empty string is returned if prefix is not valid.
|
||||
func Prefix(prefix netip.Prefix) string {
|
||||
if !prefix.IsValid() {
|
||||
return ""
|
||||
}
|
||||
return prefix.String()
|
||||
}
|
||||
@@ -109,3 +109,21 @@ func ParseCIDR(s string) (netip.Prefix, error) {
|
||||
prefix, err := netip.ParsePrefix(s)
|
||||
return Unmap(prefix), err
|
||||
}
|
||||
|
||||
// MaybeParse decorates a parse function to return no error when parsing the
|
||||
// empty string.
|
||||
func MaybeParse[T any](parse func(string) (T, error)) func(string) (T, error) {
|
||||
return func(s string) (T, error) {
|
||||
var zero T
|
||||
if s == "" {
|
||||
return zero, nil
|
||||
}
|
||||
return parse(s)
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
MaybeParseAddr = MaybeParse(netip.ParseAddr)
|
||||
MaybeParsePrefix = MaybeParse(netip.ParsePrefix)
|
||||
MaybeParseCIDR = MaybeParse(ParseCIDR)
|
||||
)
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"testing"
|
||||
|
||||
"gotest.tools/v3/assert"
|
||||
is "gotest.tools/v3/assert/cmp"
|
||||
)
|
||||
|
||||
func TestLastAddr(t *testing.T) {
|
||||
@@ -70,3 +71,19 @@ func TestParseCIDR(t *testing.T) {
|
||||
assert.Check(t, err != nil, "expected error for invalid input")
|
||||
assert.Check(t, !got.IsValid(), "expected invalid result for invalid input")
|
||||
}
|
||||
|
||||
func TestMaybeParse(t *testing.T) {
|
||||
addr := MaybeParse(netip.ParseAddr)
|
||||
got, err := addr("")
|
||||
assert.Check(t, err)
|
||||
assert.Check(t, !got.IsValid())
|
||||
|
||||
got, err = addr("bogus")
|
||||
assert.Check(t, err != nil)
|
||||
assert.Check(t, !got.IsValid())
|
||||
|
||||
got, err = addr("1.2.3.4")
|
||||
if assert.Check(t, err) {
|
||||
assert.Check(t, is.Equal(got, netip.MustParseAddr("1.2.3.4")))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/netip"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
@@ -67,7 +68,7 @@ func main() {
|
||||
httpIsOk(resp.Body)
|
||||
|
||||
clusterPeers := fetchNodePeers(*ipPtr, *portPtr, "")
|
||||
var networkPeers map[string]string
|
||||
var networkPeers map[string]netip.Addr
|
||||
var joinedNetwork bool
|
||||
if *networkPtr != "" {
|
||||
if *joinPtr {
|
||||
@@ -103,7 +104,7 @@ func main() {
|
||||
}
|
||||
}
|
||||
|
||||
func fetchNodePeers(ip string, port int, network string) map[string]string {
|
||||
func fetchNodePeers(ip string, port int, network string) map[string]netip.Addr {
|
||||
if network == "" {
|
||||
log.G(context.TODO()).Infof("Fetch cluster peers")
|
||||
} else {
|
||||
@@ -134,7 +135,7 @@ func fetchNodePeers(ip string, port int, network string) map[string]string {
|
||||
}
|
||||
|
||||
log.G(context.TODO()).Debugf("Parsing JSON response")
|
||||
result := make(map[string]string, output.Details.(*diagnostic.TablePeersResult).Length)
|
||||
result := make(map[string]netip.Addr, output.Details.(*diagnostic.TablePeersResult).Length)
|
||||
for _, v := range output.Details.(*diagnostic.TablePeersResult).Elements {
|
||||
log.G(context.TODO()).Debugf("name:%s ip:%s", v.Name, v.IP)
|
||||
result[v.Name] = v.IP
|
||||
@@ -142,7 +143,7 @@ func fetchNodePeers(ip string, port int, network string) map[string]string {
|
||||
return result
|
||||
}
|
||||
|
||||
func fetchTable(ip string, port int, network, tableName string, clusterPeers, networkPeers map[string]string, remediate bool) {
|
||||
func fetchTable(ip string, port int, network, tableName string, clusterPeers, networkPeers map[string]netip.Addr, remediate bool) {
|
||||
log.G(context.TODO()).Infof("Fetch %s table and check owners", tableName)
|
||||
resp, err := http.Get(fmt.Sprintf(dumpTable, ip, port, network, tableName))
|
||||
if err != nil {
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
package diagnostic
|
||||
|
||||
import "fmt"
|
||||
import (
|
||||
"fmt"
|
||||
"net/netip"
|
||||
)
|
||||
|
||||
// StringInterface interface that has to be implemented by messages
|
||||
type StringInterface interface {
|
||||
@@ -88,9 +91,9 @@ func (t *TableObj) String() string {
|
||||
|
||||
// PeerEntryObj entry in the networkdb peer table
|
||||
type PeerEntryObj struct {
|
||||
Index int `json:"-"`
|
||||
Name string `json:"-=name"`
|
||||
IP string `json:"ip"`
|
||||
Index int `json:"-"`
|
||||
Name string `json:"-=name"`
|
||||
IP netip.Addr `json:"ip"`
|
||||
}
|
||||
|
||||
func (p *PeerEntryObj) String() string {
|
||||
|
||||
@@ -3,9 +3,14 @@ package driverapi
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"maps"
|
||||
"net"
|
||||
"net/netip"
|
||||
|
||||
"github.com/moby/moby/api/types/network"
|
||||
"github.com/moby/moby/v2/daemon/internal/netiputil"
|
||||
"github.com/moby/moby/v2/daemon/libnetwork/types"
|
||||
"github.com/moby/moby/v2/internal/iterutil"
|
||||
)
|
||||
|
||||
// MarshalJSON encodes IPAMData into json message
|
||||
@@ -101,3 +106,19 @@ func (i *IPAMData) IsV6() bool {
|
||||
func (i *IPAMData) String() string {
|
||||
return fmt.Sprintf("AddressSpace: %s\nPool: %v\nGateway: %v\nAddresses: %v", i.AddressSpace, i.Pool, i.Gateway, i.AuxAddresses)
|
||||
}
|
||||
|
||||
func (i *IPAMData) IPAMConfig() network.IPAMConfig {
|
||||
var c network.IPAMConfig
|
||||
c.Subnet, _ = netiputil.ToPrefix(i.Pool)
|
||||
if i.Gateway != nil {
|
||||
gw, _ := netip.AddrFromSlice(i.Gateway.IP)
|
||||
c.Gateway = gw.Unmap()
|
||||
}
|
||||
if i.AuxAddresses != nil {
|
||||
c.AuxAddress = maps.Collect(iterutil.Map2(maps.All(i.AuxAddresses), func(k string, v *net.IPNet) (string, netip.Addr) {
|
||||
a, _ := netip.AddrFromSlice(v.IP)
|
||||
return k, a.Unmap()
|
||||
}))
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
@@ -4,8 +4,10 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/netip"
|
||||
"slices"
|
||||
|
||||
"github.com/moby/moby/v2/daemon/internal/netiputil"
|
||||
"github.com/moby/moby/v2/daemon/libnetwork/driverapi"
|
||||
"github.com/moby/moby/v2/daemon/libnetwork/types"
|
||||
)
|
||||
@@ -265,11 +267,21 @@ func (epi *EndpointInterface) Address() *net.IPNet {
|
||||
return types.GetIPNetCopy(epi.addr)
|
||||
}
|
||||
|
||||
func (epi *EndpointInterface) Addr() netip.Prefix {
|
||||
p, _ := netiputil.ToPrefix(epi.addr)
|
||||
return p
|
||||
}
|
||||
|
||||
// AddressIPv6 returns the IPv6 address assigned to the endpoint.
|
||||
func (epi *EndpointInterface) AddressIPv6() *net.IPNet {
|
||||
return types.GetIPNetCopy(epi.addrv6)
|
||||
}
|
||||
|
||||
func (epi *EndpointInterface) AddrIPv6() netip.Prefix {
|
||||
p, _ := netiputil.ToPrefix(epi.addrv6)
|
||||
return p
|
||||
}
|
||||
|
||||
// LinkLocalAddresses returns the list of link-local (IPv4/IPv6) addresses assigned to the endpoint.
|
||||
func (epi *EndpointInterface) LinkLocalAddresses() []*net.IPNet {
|
||||
return epi.llAddrs
|
||||
|
||||
@@ -213,7 +213,7 @@ func TestRCModify(t *testing.T) {
|
||||
}
|
||||
rc, err := Parse(bytes.NewBufferString(input), "")
|
||||
assert.NilError(t, err)
|
||||
assert.Check(t, is.DeepEqual(a2s(rc.NameServers()), tc.inputNS))
|
||||
assert.Check(t, is.DeepEqual(a2s(rc.NameServers()), tc.inputNS, cmpopts.EquateEmpty()))
|
||||
assert.Check(t, is.DeepEqual(rc.Search(), tc.inputSearch))
|
||||
assert.Check(t, is.DeepEqual(rc.Options(), tc.inputOptions))
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"net"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"net/netip"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
@@ -85,7 +86,7 @@ func TestNull(t *testing.T) {
|
||||
cnt, err := controller.NewSandbox(context.Background(), "null_container",
|
||||
libnetwork.OptionHostname("test"),
|
||||
libnetwork.OptionDomainname("example.com"),
|
||||
libnetwork.OptionExtraHost("web", "192.168.0.1"))
|
||||
libnetwork.OptionExtraHost("web", netip.MustParseAddr("192.168.0.1")))
|
||||
assert.NilError(t, err)
|
||||
|
||||
network, err := createTestNetwork(controller, "null", "testnull", options.Generic{}, nil, nil)
|
||||
@@ -657,7 +658,7 @@ func TestEndpointDeleteWithActiveContainer(t *testing.T) {
|
||||
cnt, err := controller.NewSandbox(context.Background(), containerID,
|
||||
libnetwork.OptionHostname("test"),
|
||||
libnetwork.OptionDomainname("example.com"),
|
||||
libnetwork.OptionExtraHost("web", "192.168.0.1"))
|
||||
libnetwork.OptionExtraHost("web", netip.MustParseAddr("192.168.0.1")))
|
||||
assert.NilError(t, err)
|
||||
defer func() {
|
||||
assert.Check(t, cnt.Delete(context.Background()))
|
||||
@@ -702,7 +703,7 @@ func TestEndpointMultipleJoins(t *testing.T) {
|
||||
sbx1, err := controller.NewSandbox(context.Background(), containerID,
|
||||
libnetwork.OptionHostname("test"),
|
||||
libnetwork.OptionDomainname("example.com"),
|
||||
libnetwork.OptionExtraHost("web", "192.168.0.1"),
|
||||
libnetwork.OptionExtraHost("web", netip.MustParseAddr("192.168.0.1")),
|
||||
)
|
||||
assert.NilError(t, err)
|
||||
defer func() {
|
||||
@@ -797,7 +798,7 @@ func TestContainerInvalidLeave(t *testing.T) {
|
||||
cnt, err := controller.NewSandbox(context.Background(), containerID,
|
||||
libnetwork.OptionHostname("test"),
|
||||
libnetwork.OptionDomainname("example.com"),
|
||||
libnetwork.OptionExtraHost("web", "192.168.0.1"))
|
||||
libnetwork.OptionExtraHost("web", netip.MustParseAddr("192.168.0.1")))
|
||||
assert.NilError(t, err)
|
||||
defer func() {
|
||||
assert.Check(t, cnt.Delete(context.Background()))
|
||||
@@ -845,7 +846,7 @@ func TestEndpointUpdateParent(t *testing.T) {
|
||||
sbx1, err := controller.NewSandbox(context.Background(), containerID,
|
||||
libnetwork.OptionHostname("test"),
|
||||
libnetwork.OptionDomainname("example.com"),
|
||||
libnetwork.OptionExtraHost("web", "192.168.0.1"))
|
||||
libnetwork.OptionExtraHost("web", netip.MustParseAddr("192.168.0.1")))
|
||||
assert.NilError(t, err)
|
||||
defer func() {
|
||||
assert.Check(t, sbx1.Delete(context.Background()))
|
||||
@@ -855,7 +856,7 @@ func TestEndpointUpdateParent(t *testing.T) {
|
||||
libnetwork.OptionHostname("test2"),
|
||||
libnetwork.OptionDomainname("example.com"),
|
||||
libnetwork.OptionHostsPath("/var/lib/docker/test_network/container2/hosts"),
|
||||
libnetwork.OptionExtraHost("web", "192.168.0.2"))
|
||||
libnetwork.OptionExtraHost("web", netip.MustParseAddr("192.168.0.2")))
|
||||
assert.NilError(t, err)
|
||||
defer func() {
|
||||
assert.Check(t, sbx2.Delete(context.Background()))
|
||||
@@ -979,7 +980,7 @@ func TestHost(t *testing.T) {
|
||||
sbx1, err := controller.NewSandbox(context.Background(), "host_c1",
|
||||
libnetwork.OptionHostname("test1"),
|
||||
libnetwork.OptionDomainname("example.com"),
|
||||
libnetwork.OptionExtraHost("web", "192.168.0.1"),
|
||||
libnetwork.OptionExtraHost("web", netip.MustParseAddr("192.168.0.1")),
|
||||
libnetwork.OptionUseDefaultSandbox())
|
||||
assert.NilError(t, err)
|
||||
defer func() {
|
||||
@@ -989,7 +990,7 @@ func TestHost(t *testing.T) {
|
||||
sbx2, err := controller.NewSandbox(context.Background(), "host_c2",
|
||||
libnetwork.OptionHostname("test2"),
|
||||
libnetwork.OptionDomainname("example.com"),
|
||||
libnetwork.OptionExtraHost("web", "192.168.0.1"),
|
||||
libnetwork.OptionExtraHost("web", netip.MustParseAddr("192.168.0.1")),
|
||||
libnetwork.OptionUseDefaultSandbox())
|
||||
assert.NilError(t, err)
|
||||
defer func() {
|
||||
@@ -1025,7 +1026,7 @@ func TestHost(t *testing.T) {
|
||||
cnt3, err := controller.NewSandbox(context.Background(), "host_c3",
|
||||
libnetwork.OptionHostname("test3"),
|
||||
libnetwork.OptionDomainname("example.com"),
|
||||
libnetwork.OptionExtraHost("web", "192.168.0.1"),
|
||||
libnetwork.OptionExtraHost("web", netip.MustParseAddr("192.168.0.1")),
|
||||
libnetwork.OptionUseDefaultSandbox())
|
||||
assert.NilError(t, err)
|
||||
defer func() {
|
||||
@@ -1126,7 +1127,7 @@ func TestEndpointJoin(t *testing.T) {
|
||||
sb, err := controller.NewSandbox(context.Background(), containerID,
|
||||
libnetwork.OptionHostname("test"),
|
||||
libnetwork.OptionDomainname("example.com"),
|
||||
libnetwork.OptionExtraHost("web", "192.168.0.1"))
|
||||
libnetwork.OptionExtraHost("web", netip.MustParseAddr("192.168.0.1")))
|
||||
assert.NilError(t, err)
|
||||
defer func() {
|
||||
assert.Check(t, sb.Delete(context.Background()))
|
||||
@@ -1229,7 +1230,7 @@ func externalKeyTest(t *testing.T, reexec bool) {
|
||||
libnetwork.OptionHostname("test"),
|
||||
libnetwork.OptionDomainname("example.com"),
|
||||
libnetwork.OptionUseExternalKey(),
|
||||
libnetwork.OptionExtraHost("web", "192.168.0.1"))
|
||||
libnetwork.OptionExtraHost("web", netip.MustParseAddr("192.168.0.1")))
|
||||
assert.NilError(t, err)
|
||||
defer func() {
|
||||
assert.Check(t, cnt.Delete(context.Background()))
|
||||
|
||||
@@ -108,7 +108,7 @@ func (c *IpamConf) Validate() error {
|
||||
}
|
||||
|
||||
// Contains checks whether the ipam master address pool contains [addr].
|
||||
func (c *IpamConf) Contains(addr net.IP) bool {
|
||||
func (c *IpamConf) Contains(addr netip.Addr) bool {
|
||||
if c == nil {
|
||||
return false
|
||||
}
|
||||
@@ -116,7 +116,7 @@ func (c *IpamConf) Contains(addr net.IP) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
_, allowedRange, _ := net.ParseCIDR(c.PreferredPool)
|
||||
allowedRange, _ := netiputil.ParseCIDR(c.PreferredPool)
|
||||
|
||||
return allowedRange.Contains(addr)
|
||||
}
|
||||
@@ -126,6 +126,30 @@ func (c *IpamConf) IsStatic() bool {
|
||||
return c != nil && c.PreferredPool != ""
|
||||
}
|
||||
|
||||
func (c *IpamConf) IPAMConfig() network.IPAMConfig {
|
||||
if c == nil {
|
||||
return network.IPAMConfig{}
|
||||
}
|
||||
|
||||
subnet, _ := netiputil.ParseCIDR(c.PreferredPool)
|
||||
ipr, _ := netiputil.ParseCIDR(c.SubPool)
|
||||
gw, _ := netip.ParseAddr(c.Gateway)
|
||||
|
||||
conf := network.IPAMConfig{
|
||||
Subnet: subnet.Masked(),
|
||||
IPRange: ipr.Masked(),
|
||||
Gateway: gw.Unmap(),
|
||||
}
|
||||
|
||||
if c.AuxAddresses != nil {
|
||||
conf.AuxAddress = maps.Collect(iterutil.Map2(maps.All(c.AuxAddresses), func(k, v string) (string, netip.Addr) {
|
||||
a, _ := netip.ParseAddr(v)
|
||||
return k, a.Unmap()
|
||||
}))
|
||||
}
|
||||
return conf
|
||||
}
|
||||
|
||||
// IpamInfo contains all the ipam related operational info for a network
|
||||
type IpamInfo struct {
|
||||
PoolID string
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
cryptorand "crypto/rand"
|
||||
"fmt"
|
||||
"math/rand/v2"
|
||||
"net/netip"
|
||||
"os"
|
||||
"strings"
|
||||
"sync"
|
||||
@@ -120,7 +121,7 @@ type NetworkDB struct {
|
||||
// PeerInfo represents the peer (gossip cluster) nodes of a network
|
||||
type PeerInfo struct {
|
||||
Name string
|
||||
IP string
|
||||
IP netip.Addr
|
||||
}
|
||||
|
||||
// PeerClusterInfo represents the peer (gossip cluster) nodes
|
||||
@@ -341,9 +342,10 @@ func (nDB *NetworkDB) ClusterPeers() []PeerInfo {
|
||||
defer nDB.RUnlock()
|
||||
peers := make([]PeerInfo, 0, len(nDB.nodes))
|
||||
for _, node := range nDB.nodes {
|
||||
ip, _ := netip.AddrFromSlice(node.Node.Addr)
|
||||
peers = append(peers, PeerInfo{
|
||||
Name: node.Name,
|
||||
IP: node.Node.Addr.String(),
|
||||
IP: ip.Unmap(),
|
||||
})
|
||||
}
|
||||
return peers
|
||||
@@ -356,14 +358,15 @@ func (nDB *NetworkDB) Peers(nid string) []PeerInfo {
|
||||
peers := make([]PeerInfo, 0, len(nDB.networkNodes[nid]))
|
||||
for _, nodeName := range nDB.networkNodes[nid] {
|
||||
if node, ok := nDB.nodes[nodeName]; ok {
|
||||
ip, _ := netip.AddrFromSlice(node.Node.Addr)
|
||||
peers = append(peers, PeerInfo{
|
||||
Name: node.Name,
|
||||
IP: node.Addr.String(),
|
||||
IP: ip.Unmap(),
|
||||
})
|
||||
} else {
|
||||
// Added for testing purposes, this condition should never happen else mean that the network list
|
||||
// is out of sync with the node list
|
||||
peers = append(peers, PeerInfo{Name: nodeName, IP: "unknown"})
|
||||
peers = append(peers, PeerInfo{Name: nodeName})
|
||||
}
|
||||
}
|
||||
return peers
|
||||
|
||||
@@ -92,7 +92,7 @@ func (nDB *NetworkDB) dbPeers(w http.ResponseWriter, r *http.Request) {
|
||||
peers := nDB.Peers(r.Form["nid"][0])
|
||||
rsp := &diagnostic.TableObj{Length: len(peers)}
|
||||
for i, peerInfo := range peers {
|
||||
if peerInfo.IP == "unknown" {
|
||||
if !peerInfo.IP.IsValid() {
|
||||
rsp.Elements = append(rsp.Elements, &diagnostic.PeerEntryObj{Index: i, Name: "orphan-" + peerInfo.Name, IP: peerInfo.IP})
|
||||
} else {
|
||||
rsp.Elements = append(rsp.Elements, &diagnostic.PeerEntryObj{Index: i, Name: peerInfo.Name, IP: peerInfo.IP})
|
||||
|
||||
@@ -81,7 +81,7 @@ type hostsPathConfig struct {
|
||||
|
||||
type extraHost struct {
|
||||
name string
|
||||
IP string
|
||||
IP netip.Addr
|
||||
}
|
||||
|
||||
// These are the container configs used to customize container /etc/resolv.conf file.
|
||||
|
||||
@@ -4,7 +4,6 @@ package libnetwork
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"net/netip"
|
||||
"os"
|
||||
@@ -29,15 +28,15 @@ const (
|
||||
)
|
||||
|
||||
// AddHostsEntry adds an entry to /etc/hosts.
|
||||
func (sb *Sandbox) AddHostsEntry(ctx context.Context, name, ip string) error {
|
||||
func (sb *Sandbox) AddHostsEntry(ctx context.Context, name string, ip netip.Addr) error {
|
||||
sb.config.extraHosts = append(sb.config.extraHosts, extraHost{name: name, IP: ip})
|
||||
return sb.rebuildHostsFile(ctx)
|
||||
}
|
||||
|
||||
// UpdateHostsEntry updates the IP address in a /etc/hosts entry where the
|
||||
// name matches the regular expression regexp.
|
||||
func (sb *Sandbox) UpdateHostsEntry(regexp, ip string) error {
|
||||
return etchosts.Update(sb.config.hostsPath, ip, regexp)
|
||||
func (sb *Sandbox) UpdateHostsEntry(regexp string, ip netip.Addr) error {
|
||||
return etchosts.Update(sb.config.hostsPath, ip.String(), regexp)
|
||||
}
|
||||
|
||||
// rebuildHostsFile builds the container's /etc/hosts file, based on the current
|
||||
@@ -142,11 +141,7 @@ func (sb *Sandbox) buildHostsFile(ctx context.Context, ifaceIPs []netip.Addr) er
|
||||
|
||||
extraContent := make([]etchosts.Record, 0, len(sb.config.extraHosts)+len(ifaceIPs))
|
||||
for _, host := range sb.config.extraHosts {
|
||||
addr, err := netip.ParseAddr(host.IP)
|
||||
if err != nil {
|
||||
return errdefs.InvalidParameter(fmt.Errorf("could not parse extra host IP %s: %v", host.IP, err))
|
||||
}
|
||||
extraContent = append(extraContent, etchosts.Record{Hosts: host.name, IP: addr})
|
||||
extraContent = append(extraContent, etchosts.Record{Hosts: host.name, IP: host.IP})
|
||||
}
|
||||
extraContent = append(extraContent, sb.makeHostsRecs(ifaceIPs)...)
|
||||
|
||||
|
||||
@@ -42,7 +42,7 @@ func OptionOriginHostsPath(path string) SandboxOption {
|
||||
|
||||
// OptionExtraHost function returns an option setter for extra /etc/hosts options
|
||||
// which is a name and IP as strings.
|
||||
func OptionExtraHost(name string, IP string) SandboxOption {
|
||||
func OptionExtraHost(name string, IP netip.Addr) SandboxOption {
|
||||
return func(sb *Sandbox) {
|
||||
sb.config.extraHosts = append(sb.config.extraHosts, extraHost{name: name, IP: IP})
|
||||
}
|
||||
|
||||
@@ -19,6 +19,8 @@ import (
|
||||
"github.com/moby/moby/v2/daemon/config"
|
||||
"github.com/moby/moby/v2/daemon/container"
|
||||
"github.com/moby/moby/v2/daemon/internal/multierror"
|
||||
"github.com/moby/moby/v2/daemon/internal/netipstringer"
|
||||
"github.com/moby/moby/v2/daemon/internal/netiputil"
|
||||
"github.com/moby/moby/v2/daemon/internal/otelutil"
|
||||
"github.com/moby/moby/v2/daemon/libnetwork"
|
||||
lncluster "github.com/moby/moby/v2/daemon/libnetwork/cluster"
|
||||
@@ -32,6 +34,7 @@ import (
|
||||
"github.com/moby/moby/v2/daemon/pkg/opts"
|
||||
"github.com/moby/moby/v2/daemon/server/backend"
|
||||
"github.com/moby/moby/v2/errdefs"
|
||||
"github.com/moby/moby/v2/internal/iterutil"
|
||||
"github.com/moby/moby/v2/pkg/plugingetter"
|
||||
"go.opentelemetry.io/otel/baggage"
|
||||
)
|
||||
@@ -447,13 +450,8 @@ func (daemon *Daemon) pluginRefCount(driver, capability string, mode int) {
|
||||
func validateIpamConfig(data []networktypes.IPAMConfig, enableIPv6 bool) error {
|
||||
var errs []error
|
||||
for _, cfg := range data {
|
||||
subnet, err := netip.ParsePrefix(cfg.Subnet)
|
||||
if err != nil {
|
||||
errs = append(errs, fmt.Errorf("invalid subnet %s: invalid CIDR block notation", cfg.Subnet))
|
||||
continue
|
||||
}
|
||||
subnetFamily := 4
|
||||
if subnet.Addr().Is6() {
|
||||
if cfg.Subnet.Addr().Is6() {
|
||||
subnetFamily = 6
|
||||
}
|
||||
|
||||
@@ -461,20 +459,20 @@ func validateIpamConfig(data []networktypes.IPAMConfig, enableIPv6 bool) error {
|
||||
continue
|
||||
}
|
||||
|
||||
if subnet != subnet.Masked() {
|
||||
errs = append(errs, fmt.Errorf("invalid subnet %s: it should be %s", subnet, subnet.Masked()))
|
||||
if cfg.Subnet != cfg.Subnet.Masked() {
|
||||
errs = append(errs, fmt.Errorf("invalid subnet %s: it should be %s", cfg.Subnet, cfg.Subnet.Masked()))
|
||||
}
|
||||
|
||||
if ipRangeErrs := validateIPRange(cfg.IPRange, subnet, subnetFamily); len(ipRangeErrs) > 0 {
|
||||
if ipRangeErrs := validateIPRange(cfg.IPRange, cfg.Subnet, subnetFamily); len(ipRangeErrs) > 0 {
|
||||
errs = append(errs, ipRangeErrs...)
|
||||
}
|
||||
|
||||
if err := validateAddress(cfg.Gateway, subnet, subnetFamily); err != nil {
|
||||
if err := validateAddress(cfg.Gateway, cfg.Subnet, subnetFamily); err != nil {
|
||||
errs = append(errs, fmt.Errorf("invalid gateway %s: %w", cfg.Gateway, err))
|
||||
}
|
||||
|
||||
for auxName, aux := range cfg.AuxAddress {
|
||||
if err := validateAddress(aux, subnet, subnetFamily); err != nil {
|
||||
if err := validateAddress(aux, cfg.Subnet, subnetFamily); err != nil {
|
||||
errs = append(errs, fmt.Errorf("invalid auxiliary address %s: %w", auxName, err))
|
||||
}
|
||||
}
|
||||
@@ -487,16 +485,12 @@ func validateIpamConfig(data []networktypes.IPAMConfig, enableIPv6 bool) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func validateIPRange(ipRange string, subnet netip.Prefix, subnetFamily int) []error {
|
||||
if ipRange == "" {
|
||||
func validateIPRange(ipRange, subnet netip.Prefix, subnetFamily int) []error {
|
||||
if !ipRange.IsValid() {
|
||||
return nil
|
||||
}
|
||||
prefix, err := netip.ParsePrefix(ipRange)
|
||||
if err != nil {
|
||||
return []error{fmt.Errorf("invalid ip-range %s: invalid CIDR block notation", ipRange)}
|
||||
}
|
||||
family := 4
|
||||
if prefix.Addr().Is6() {
|
||||
if ipRange.Addr().Is6() {
|
||||
family = 6
|
||||
}
|
||||
|
||||
@@ -505,27 +499,23 @@ func validateIPRange(ipRange string, subnet netip.Prefix, subnetFamily int) []er
|
||||
}
|
||||
|
||||
var errs []error
|
||||
if prefix.Bits() < subnet.Bits() {
|
||||
if ipRange.Bits() < subnet.Bits() {
|
||||
errs = append(errs, fmt.Errorf("invalid ip-range %s: CIDR block is bigger than its parent subnet %s", ipRange, subnet))
|
||||
}
|
||||
if prefix != prefix.Masked() {
|
||||
errs = append(errs, fmt.Errorf("invalid ip-range %s: it should be %s", prefix, prefix.Masked()))
|
||||
if ipRange != ipRange.Masked() {
|
||||
errs = append(errs, fmt.Errorf("invalid ip-range %s: it should be %s", ipRange, ipRange.Masked()))
|
||||
}
|
||||
if !subnet.Overlaps(prefix) {
|
||||
if !subnet.Overlaps(ipRange) {
|
||||
errs = append(errs, fmt.Errorf("invalid ip-range %s: parent subnet %s doesn't contain ip-range", ipRange, subnet))
|
||||
}
|
||||
|
||||
return errs
|
||||
}
|
||||
|
||||
func validateAddress(address string, subnet netip.Prefix, subnetFamily int) error {
|
||||
if address == "" {
|
||||
func validateAddress(addr netip.Addr, subnet netip.Prefix, subnetFamily int) error {
|
||||
if !addr.IsValid() {
|
||||
return nil
|
||||
}
|
||||
addr, err := netip.ParseAddr(address)
|
||||
if err != nil {
|
||||
return errors.New("invalid address")
|
||||
}
|
||||
family := 4
|
||||
if addr.Is6() {
|
||||
family = 6
|
||||
@@ -545,16 +535,15 @@ func getIpamConfig(data []networktypes.IPAMConfig) ([]*libnetwork.IpamConf, []*l
|
||||
ipamV4Cfg := []*libnetwork.IpamConf{}
|
||||
ipamV6Cfg := []*libnetwork.IpamConf{}
|
||||
for _, d := range data {
|
||||
iCfg := libnetwork.IpamConf{}
|
||||
iCfg.PreferredPool = d.Subnet
|
||||
iCfg.SubPool = d.IPRange
|
||||
iCfg.Gateway = d.Gateway
|
||||
iCfg.AuxAddresses = d.AuxAddress
|
||||
ip, _, err := net.ParseCIDR(d.Subnet)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("Invalid subnet %s : %v", d.Subnet, err)
|
||||
iCfg := libnetwork.IpamConf{
|
||||
PreferredPool: netipstringer.Prefix(netiputil.Unmap(d.Subnet).Masked()),
|
||||
SubPool: netipstringer.Prefix(netiputil.Unmap(d.IPRange).Masked()),
|
||||
Gateway: netipstringer.Addr(d.Gateway.Unmap()),
|
||||
AuxAddresses: maps.Collect(iterutil.Map2(maps.All(d.AuxAddress), func(k string, v netip.Addr) (string, string) {
|
||||
return k, v.Unmap().String()
|
||||
})),
|
||||
}
|
||||
if ip.To4() != nil {
|
||||
if d.Subnet.Addr().Unmap().Is4() {
|
||||
ipamV4Cfg = append(ipamV4Cfg, &iCfg)
|
||||
} else {
|
||||
ipamV6Cfg = append(ipamV6Cfg, &iCfg)
|
||||
@@ -782,15 +771,17 @@ func buildServiceAttachments(nw *libnetwork.Network) map[string]networktypes.Ser
|
||||
for name, service := range nw.Services() {
|
||||
tasks := make([]networktypes.Task, 0, len(service.Tasks))
|
||||
for _, t := range service.Tasks {
|
||||
eip, _ := netip.ParseAddr(t.EndpointIP)
|
||||
tasks = append(tasks, networktypes.Task{
|
||||
Name: t.Name,
|
||||
EndpointID: t.EndpointID,
|
||||
EndpointIP: t.EndpointIP,
|
||||
EndpointIP: eip.Unmap(),
|
||||
Info: t.Info,
|
||||
})
|
||||
}
|
||||
vip, _ := netip.ParseAddr(service.VIP)
|
||||
services[name] = networktypes.ServiceInfo{
|
||||
VIP: service.VIP,
|
||||
VIP: vip.Unmap(),
|
||||
Ports: service.Ports,
|
||||
Tasks: tasks,
|
||||
LocalLBIndex: service.LocalLBIndex,
|
||||
@@ -826,12 +817,7 @@ func buildIPAMResources(nw *libnetwork.Network) networktypes.IPAM {
|
||||
continue
|
||||
}
|
||||
hasIPv4Config = true
|
||||
ipamConfig = append(ipamConfig, networktypes.IPAMConfig{
|
||||
Subnet: cfg.PreferredPool,
|
||||
IPRange: cfg.SubPool,
|
||||
Gateway: cfg.Gateway,
|
||||
AuxAddress: cfg.AuxAddresses,
|
||||
})
|
||||
ipamConfig = append(ipamConfig, cfg.IPAMConfig())
|
||||
}
|
||||
|
||||
hasIPv6Config := false
|
||||
@@ -840,26 +826,14 @@ func buildIPAMResources(nw *libnetwork.Network) networktypes.IPAM {
|
||||
continue
|
||||
}
|
||||
hasIPv6Config = true
|
||||
ipamConfig = append(ipamConfig, networktypes.IPAMConfig{
|
||||
Subnet: cfg.PreferredPool,
|
||||
IPRange: cfg.SubPool,
|
||||
Gateway: cfg.Gateway,
|
||||
AuxAddress: cfg.AuxAddresses,
|
||||
})
|
||||
ipamConfig = append(ipamConfig, cfg.IPAMConfig())
|
||||
}
|
||||
|
||||
if !hasIPv4Config || !hasIPv6Config {
|
||||
ipv4Info, ipv6Info := nw.IpamInfo()
|
||||
if !hasIPv4Config {
|
||||
for _, info := range ipv4Info {
|
||||
var gw string
|
||||
if info.IPAMData.Gateway != nil {
|
||||
gw = info.IPAMData.Gateway.IP.String()
|
||||
}
|
||||
ipamConfig = append(ipamConfig, networktypes.IPAMConfig{
|
||||
Subnet: info.IPAMData.Pool.String(),
|
||||
Gateway: gw,
|
||||
})
|
||||
ipamConfig = append(ipamConfig, info.IPAMData.IPAMConfig())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -868,14 +842,7 @@ func buildIPAMResources(nw *libnetwork.Network) networktypes.IPAM {
|
||||
if info.IPAMData.Pool == nil {
|
||||
continue
|
||||
}
|
||||
var gw string
|
||||
if info.IPAMData.Gateway != nil {
|
||||
gw = info.IPAMData.Gateway.IP.String()
|
||||
}
|
||||
ipamConfig = append(ipamConfig, networktypes.IPAMConfig{
|
||||
Subnet: info.IPAMData.Pool.String(),
|
||||
Gateway: gw,
|
||||
})
|
||||
ipamConfig = append(ipamConfig, info.IPAMData.IPAMConfig())
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -895,15 +862,9 @@ func buildEndpointResource(ep *libnetwork.Endpoint, info libnetwork.EndpointInfo
|
||||
Name: ep.Name(),
|
||||
}
|
||||
if iface := info.Iface(); iface != nil {
|
||||
if mac := iface.MacAddress(); mac != nil {
|
||||
er.MacAddress = mac.String()
|
||||
}
|
||||
if ip := iface.Address(); ip != nil && len(ip.IP) > 0 {
|
||||
er.IPv4Address = ip.String()
|
||||
}
|
||||
if ip := iface.AddressIPv6(); ip != nil && len(ip.IP) > 0 {
|
||||
er.IPv6Address = ip.String()
|
||||
}
|
||||
er.MacAddress = iface.MacAddress().String()
|
||||
er.IPv4Address = netiputil.Unmap(iface.Addr())
|
||||
er.IPv6Address = iface.AddrIPv6()
|
||||
}
|
||||
return er
|
||||
}
|
||||
@@ -946,25 +907,22 @@ func buildCreateEndpointOptions(c *container.Container, n *libnetwork.Network, e
|
||||
if epConfig != nil {
|
||||
if ipam := epConfig.IPAMConfig; ipam != nil {
|
||||
var ipList []net.IP
|
||||
for _, ips := range ipam.LinkLocalIPs {
|
||||
linkIP := net.ParseIP(ips)
|
||||
if linkIP == nil && ips != "" {
|
||||
for _, linkIP := range ipam.LinkLocalIPs {
|
||||
if !linkIP.IsValid() {
|
||||
return nil, fmt.Errorf("invalid link-local IP address: %s", ipam.LinkLocalIPs)
|
||||
}
|
||||
ipList = append(ipList, linkIP)
|
||||
ipList = append(ipList, linkIP.AsSlice())
|
||||
}
|
||||
|
||||
ip := net.ParseIP(ipam.IPv4Address)
|
||||
if ip == nil && ipam.IPv4Address != "" {
|
||||
if ipam.IPv4Address.IsValid() && !ipam.IPv4Address.Is4() && !ipam.IPv4Address.Is4In6() {
|
||||
return nil, fmt.Errorf("invalid IPv4 address: %s", ipam.IPv4Address)
|
||||
}
|
||||
|
||||
ip6 := net.ParseIP(ipam.IPv6Address)
|
||||
if ip6 == nil && ipam.IPv6Address != "" {
|
||||
if ipam.IPv6Address.IsValid() && !ipam.IPv6Address.Is6() {
|
||||
return nil, fmt.Errorf("invalid IPv6 address: %s", ipam.IPv6Address)
|
||||
}
|
||||
|
||||
createOptions = append(createOptions, libnetwork.CreateOptionIPAM(ip, ip6, ipList))
|
||||
createOptions = append(createOptions, libnetwork.CreateOptionIPAM(ipam.IPv4Address.AsSlice(), ipam.IPv6Address.AsSlice(), ipList))
|
||||
}
|
||||
|
||||
createOptions = append(createOptions, libnetwork.CreateOptionDNSNames(epConfig.DNSNames))
|
||||
@@ -1197,17 +1155,18 @@ func buildEndpointInfo(networkSettings *network.Settings, n *libnetwork.Network,
|
||||
|
||||
if iface.Address() != nil {
|
||||
ones, _ := iface.Address().Mask.Size()
|
||||
networkSettings.Networks[nwName].IPAddress = iface.Address().IP.String()
|
||||
addr, _ := netip.AddrFromSlice(iface.Address().IP)
|
||||
networkSettings.Networks[nwName].IPAddress = addr.Unmap()
|
||||
networkSettings.Networks[nwName].IPPrefixLen = ones
|
||||
}
|
||||
|
||||
if iface.AddressIPv6() != nil && iface.AddressIPv6().IP.To16() != nil {
|
||||
onesv6, _ := iface.AddressIPv6().Mask.Size()
|
||||
networkSettings.Networks[nwName].GlobalIPv6Address = iface.AddressIPv6().IP.String()
|
||||
networkSettings.Networks[nwName].GlobalIPv6Address, _ = netip.AddrFromSlice(iface.AddressIPv6().IP)
|
||||
networkSettings.Networks[nwName].GlobalIPv6PrefixLen = onesv6
|
||||
} else {
|
||||
// If IPv6 was disabled on the interface, and its address was removed, remove it here too.
|
||||
networkSettings.Networks[nwName].GlobalIPv6Address = ""
|
||||
networkSettings.Networks[nwName].GlobalIPv6Address = netip.Addr{}
|
||||
networkSettings.Networks[nwName].GlobalIPv6PrefixLen = 0
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package daemon
|
||||
|
||||
import (
|
||||
"net/netip"
|
||||
"testing"
|
||||
|
||||
"github.com/moby/moby/api/types/network"
|
||||
@@ -18,10 +19,10 @@ func TestValidateIPAM(t *testing.T) {
|
||||
{
|
||||
name: "IP version mismatch",
|
||||
ipam: []network.IPAMConfig{{
|
||||
Subnet: "10.10.10.0/24",
|
||||
IPRange: "2001:db8::/32",
|
||||
Gateway: "2001:db8::1",
|
||||
AuxAddress: map[string]string{"DefaultGatewayIPv4": "2001:db8::1"},
|
||||
Subnet: netip.MustParsePrefix("10.10.10.0/24"),
|
||||
IPRange: netip.MustParsePrefix("2001:db8::/32"),
|
||||
Gateway: netip.MustParseAddr("2001:db8::1"),
|
||||
AuxAddress: map[string]netip.Addr{"DefaultGatewayIPv4": netip.MustParseAddr("2001:db8::1")},
|
||||
}},
|
||||
expectedErrors: []string{
|
||||
"invalid ip-range 2001:db8::/32: parent subnet is an IPv4 block",
|
||||
@@ -32,34 +33,13 @@ func TestValidateIPAM(t *testing.T) {
|
||||
{
|
||||
// Regression test for https://github.com/moby/moby/issues/47202
|
||||
name: "IPv6 subnet is discarded with no error when IPv6 is disabled",
|
||||
ipam: []network.IPAMConfig{{Subnet: "2001:db8::/32"}},
|
||||
ipam: []network.IPAMConfig{{Subnet: netip.MustParsePrefix("2001:db8::/32")}},
|
||||
ipv6: false,
|
||||
},
|
||||
{
|
||||
name: "Invalid data - Subnet",
|
||||
ipam: []network.IPAMConfig{{Subnet: "foobar"}},
|
||||
expectedErrors: []string{
|
||||
`invalid subnet foobar: invalid CIDR block notation`,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Invalid data",
|
||||
ipam: []network.IPAMConfig{{
|
||||
Subnet: "10.10.10.0/24",
|
||||
IPRange: "foobar",
|
||||
Gateway: "1001.10.5.3",
|
||||
AuxAddress: map[string]string{"DefaultGatewayIPv4": "dummy"},
|
||||
}},
|
||||
expectedErrors: []string{
|
||||
"invalid ip-range foobar: invalid CIDR block notation",
|
||||
"invalid gateway 1001.10.5.3: invalid address",
|
||||
"invalid auxiliary address DefaultGatewayIPv4: invalid address",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "IPRange bigger than its subnet",
|
||||
ipam: []network.IPAMConfig{
|
||||
{Subnet: "10.10.10.0/24", IPRange: "10.0.0.0/8"},
|
||||
{Subnet: netip.MustParsePrefix("10.10.10.0/24"), IPRange: netip.MustParsePrefix("10.0.0.0/8")},
|
||||
},
|
||||
expectedErrors: []string{
|
||||
"invalid ip-range 10.0.0.0/8: CIDR block is bigger than its parent subnet 10.10.10.0/24",
|
||||
@@ -68,10 +48,10 @@ func TestValidateIPAM(t *testing.T) {
|
||||
{
|
||||
name: "Out of range prefix & addresses",
|
||||
ipam: []network.IPAMConfig{{
|
||||
Subnet: "10.0.0.0/8",
|
||||
IPRange: "192.168.0.1/24",
|
||||
Gateway: "192.168.0.1",
|
||||
AuxAddress: map[string]string{"DefaultGatewayIPv4": "192.168.0.1"},
|
||||
Subnet: netip.MustParsePrefix("10.0.0.0/8"),
|
||||
IPRange: netip.MustParsePrefix("192.168.0.1/24"),
|
||||
Gateway: netip.MustParseAddr("192.168.0.1"),
|
||||
AuxAddress: map[string]netip.Addr{"DefaultGatewayIPv4": netip.MustParseAddr("192.168.0.1")},
|
||||
}},
|
||||
expectedErrors: []string{
|
||||
"invalid ip-range 192.168.0.1/24: it should be 192.168.0.0/24",
|
||||
@@ -83,15 +63,15 @@ func TestValidateIPAM(t *testing.T) {
|
||||
{
|
||||
name: "Subnet with host fragment set",
|
||||
ipam: []network.IPAMConfig{{
|
||||
Subnet: "10.10.10.0/8",
|
||||
Subnet: netip.MustParsePrefix("10.10.10.0/8"),
|
||||
}},
|
||||
expectedErrors: []string{"invalid subnet 10.10.10.0/8: it should be 10.0.0.0/8"},
|
||||
},
|
||||
{
|
||||
name: "IPRange with host fragment set",
|
||||
ipam: []network.IPAMConfig{{
|
||||
Subnet: "10.0.0.0/8",
|
||||
IPRange: "10.10.10.0/16",
|
||||
Subnet: netip.MustParsePrefix("10.0.0.0/8"),
|
||||
IPRange: netip.MustParsePrefix("10.10.10.0/16"),
|
||||
}},
|
||||
expectedErrors: []string{"invalid ip-range 10.10.10.0/16: it should be 10.10.0.0/16"},
|
||||
},
|
||||
@@ -101,10 +81,10 @@ func TestValidateIPAM(t *testing.T) {
|
||||
{
|
||||
name: "Valid IPAM",
|
||||
ipam: []network.IPAMConfig{{
|
||||
Subnet: "10.0.0.0/8",
|
||||
IPRange: "10.10.0.0/16",
|
||||
Gateway: "10.10.0.1",
|
||||
AuxAddress: map[string]string{"DefaultGatewayIPv4": "10.10.0.1"},
|
||||
Subnet: netip.MustParsePrefix("10.0.0.0/8"),
|
||||
IPRange: netip.MustParsePrefix("10.10.0.0/16"),
|
||||
Gateway: netip.MustParseAddr("10.10.0.1"),
|
||||
AuxAddress: map[string]netip.Addr{"DefaultGatewayIPv4": netip.MustParseAddr("10.10.0.1")},
|
||||
}},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -71,6 +71,7 @@ generate_model types/network --keep-spec-order --additional-initialism=IPAM <<-
|
||||
NetworkSummary
|
||||
NetworkTaskInfo
|
||||
PeerInfo
|
||||
ServiceInfo
|
||||
SubnetStatus
|
||||
EOT
|
||||
|
||||
|
||||
@@ -110,5 +110,5 @@ func (s *DockerAPISuite) TestInspectAPIBridgeNetworkSettings121(c *testing.T) {
|
||||
|
||||
settings := inspectJSON.NetworkSettings
|
||||
assert.Assert(c, settings.Networks["bridge"] != nil)
|
||||
assert.Assert(c, settings.Networks["bridge"].IPAddress != "")
|
||||
assert.Assert(c, settings.Networks["bridge"].IPAddress.IsValid())
|
||||
}
|
||||
|
||||
@@ -3,8 +3,8 @@ package main
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/netip"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
@@ -37,9 +37,7 @@ func (s *DockerAPISuite) TestAPINetworkInspectBridge(c *testing.T) {
|
||||
_, ok := nr.Containers[containerID]
|
||||
assert.Assert(c, ok)
|
||||
|
||||
ip, _, err := net.ParseCIDR(nr.Containers[containerID].IPv4Address)
|
||||
assert.NilError(c, err)
|
||||
assert.Equal(c, ip.String(), containerIP)
|
||||
assert.Equal(c, nr.Containers[containerID].IPv4Address.Addr().String(), containerIP)
|
||||
}
|
||||
|
||||
func (s *DockerAPISuite) TestAPINetworkInspectUserDefinedNetwork(c *testing.T) {
|
||||
@@ -47,7 +45,7 @@ func (s *DockerAPISuite) TestAPINetworkInspectUserDefinedNetwork(c *testing.T) {
|
||||
// IPAM configuration inspect
|
||||
ipam := &network.IPAM{
|
||||
Driver: "default",
|
||||
Config: []network.IPAMConfig{{Subnet: "172.28.0.0/16", IPRange: "172.28.5.0/24", Gateway: "172.28.5.254"}},
|
||||
Config: []network.IPAMConfig{{Subnet: netip.MustParsePrefix("172.28.0.0/16"), IPRange: netip.MustParsePrefix("172.28.5.0/24"), Gateway: netip.MustParseAddr("172.28.5.254")}},
|
||||
}
|
||||
config := network.CreateRequest{
|
||||
Name: "br0",
|
||||
@@ -60,9 +58,9 @@ func (s *DockerAPISuite) TestAPINetworkInspectUserDefinedNetwork(c *testing.T) {
|
||||
|
||||
nr := getNetworkResource(c, id0)
|
||||
assert.Equal(c, len(nr.IPAM.Config), 1)
|
||||
assert.Equal(c, nr.IPAM.Config[0].Subnet, "172.28.0.0/16")
|
||||
assert.Equal(c, nr.IPAM.Config[0].IPRange, "172.28.5.0/24")
|
||||
assert.Equal(c, nr.IPAM.Config[0].Gateway, "172.28.5.254")
|
||||
assert.Equal(c, nr.IPAM.Config[0].Subnet, netip.MustParsePrefix("172.28.0.0/16"))
|
||||
assert.Equal(c, nr.IPAM.Config[0].IPRange, netip.MustParsePrefix("172.28.5.0/24"))
|
||||
assert.Equal(c, nr.IPAM.Config[0].Gateway, netip.MustParseAddr("172.28.5.254"))
|
||||
assert.Equal(c, nr.Options["foo"], "bar")
|
||||
assert.Equal(c, nr.Options["opts"], "dopts")
|
||||
|
||||
@@ -98,10 +96,8 @@ func (s *DockerAPISuite) TestAPINetworkConnectDisconnect(c *testing.T) {
|
||||
assert.Assert(c, ok)
|
||||
|
||||
// check if container IP matches network inspect
|
||||
ip, _, err := net.ParseCIDR(nr.Containers[containerID].IPv4Address)
|
||||
assert.NilError(c, err)
|
||||
containerIP := findContainerIP(c, "test", "testnetwork")
|
||||
assert.Equal(c, ip.String(), containerIP)
|
||||
assert.Equal(c, nr.Containers[containerID].IPv4Address.Addr().String(), containerIP)
|
||||
|
||||
// disconnect container from the network
|
||||
disconnectNetwork(c, nr.ID, containerID)
|
||||
@@ -118,7 +114,7 @@ func (s *DockerAPISuite) TestAPINetworkIPAMMultipleBridgeNetworks(c *testing.T)
|
||||
// test0 bridge network
|
||||
ipam0 := &network.IPAM{
|
||||
Driver: "default",
|
||||
Config: []network.IPAMConfig{{Subnet: "192.178.0.0/16", IPRange: "192.178.128.0/17", Gateway: "192.178.138.100"}},
|
||||
Config: []network.IPAMConfig{{Subnet: netip.MustParsePrefix("192.178.0.0/16"), IPRange: netip.MustParsePrefix("192.178.128.0/17"), Gateway: netip.MustParseAddr("192.178.138.100")}},
|
||||
}
|
||||
config0 := network.CreateRequest{
|
||||
Name: "test0",
|
||||
@@ -130,7 +126,7 @@ func (s *DockerAPISuite) TestAPINetworkIPAMMultipleBridgeNetworks(c *testing.T)
|
||||
|
||||
ipam1 := &network.IPAM{
|
||||
Driver: "default",
|
||||
Config: []network.IPAMConfig{{Subnet: "192.178.128.0/17", Gateway: "192.178.128.1"}},
|
||||
Config: []network.IPAMConfig{{Subnet: netip.MustParsePrefix("192.178.128.0/17"), Gateway: netip.MustParseAddr("192.178.128.1")}},
|
||||
}
|
||||
// test1 bridge network overlaps with test0
|
||||
config1 := network.CreateRequest{
|
||||
@@ -143,7 +139,7 @@ func (s *DockerAPISuite) TestAPINetworkIPAMMultipleBridgeNetworks(c *testing.T)
|
||||
|
||||
ipam2 := &network.IPAM{
|
||||
Driver: "default",
|
||||
Config: []network.IPAMConfig{{Subnet: "192.169.0.0/16", Gateway: "192.169.100.100"}},
|
||||
Config: []network.IPAMConfig{{Subnet: netip.MustParsePrefix("192.169.0.0/16"), Gateway: netip.MustParseAddr("192.169.100.100")}},
|
||||
}
|
||||
// test2 bridge network does not overlap
|
||||
config2 := network.CreateRequest{
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"net"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"net/netip"
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
@@ -573,10 +574,8 @@ func (s *DockerNetworkSuite) TestDockerNetworkConnectDisconnect(c *testing.T) {
|
||||
assert.Equal(c, len(nr.Containers), 1)
|
||||
|
||||
// check if container IP matches network inspect
|
||||
ip, _, err := net.ParseCIDR(nr.Containers[containerID].IPv4Address)
|
||||
assert.NilError(c, err)
|
||||
containerIP := findContainerIP(c, "test", "test")
|
||||
assert.Equal(c, ip.String(), containerIP)
|
||||
assert.Equal(c, nr.Containers[containerID].IPv4Address.Addr().String(), containerIP)
|
||||
|
||||
// disconnect container from the network
|
||||
cli.DockerCmd(c, "network", "disconnect", "test", containerID)
|
||||
@@ -686,8 +685,8 @@ func (s *DockerNetworkSuite) TestDockerNetworkNullIPAMDriver(c *testing.T) {
|
||||
nr := getNetworkResource(c, "test000")
|
||||
assert.Equal(c, nr.IPAM.Driver, "null")
|
||||
assert.Equal(c, len(nr.IPAM.Config), 1)
|
||||
assert.Equal(c, nr.IPAM.Config[0].Subnet, "0.0.0.0/0")
|
||||
assert.Equal(c, nr.IPAM.Config[0].Gateway, "")
|
||||
assert.Equal(c, nr.IPAM.Config[0].Subnet, netip.MustParsePrefix("0.0.0.0/0"))
|
||||
assert.Assert(c, !nr.IPAM.Config[0].Gateway.IsValid())
|
||||
}
|
||||
|
||||
func (s *DockerNetworkSuite) TestDockerNetworkInspectDefault(c *testing.T) {
|
||||
@@ -744,9 +743,9 @@ func (s *DockerNetworkSuite) TestDockerNetworkInspectCustomSpecified(c *testing.
|
||||
assert.Equal(c, nr.EnableIPv6, true)
|
||||
assert.Equal(c, nr.IPAM.Driver, "default")
|
||||
assert.Equal(c, len(nr.IPAM.Config), 2)
|
||||
assert.Equal(c, nr.IPAM.Config[0].Subnet, "172.28.0.0/16")
|
||||
assert.Equal(c, nr.IPAM.Config[0].IPRange, "172.28.5.0/24")
|
||||
assert.Equal(c, nr.IPAM.Config[0].Gateway, "172.28.5.254")
|
||||
assert.Equal(c, nr.IPAM.Config[0].Subnet, netip.MustParsePrefix("172.28.0.0/16"))
|
||||
assert.Equal(c, nr.IPAM.Config[0].IPRange, netip.MustParsePrefix("172.28.5.0/24"))
|
||||
assert.Equal(c, nr.IPAM.Config[0].Gateway, netip.MustParseAddr("172.28.5.254"))
|
||||
assert.Equal(c, nr.Internal, false)
|
||||
cli.DockerCmd(c, "network", "rm", "br0")
|
||||
assertNwNotAvailable(c, "br0")
|
||||
@@ -1733,9 +1732,9 @@ func (s *DockerNetworkSuite) TestDockerNetworkValidateIP(c *testing.T) {
|
||||
verifyIPAddresses(c, "mynet0", "mynet", "172.28.99.88", "2001:db8:1234::9988")
|
||||
|
||||
_, _, err = dockerCmdWithError("run", "--net=mynet", "--ip", "mynet_ip", "--ip6", "2001:db8:1234::9999", "busybox", "top")
|
||||
assert.ErrorContains(c, err, "invalid IPv4 address")
|
||||
assert.ErrorContains(c, err, "unable to parse IP")
|
||||
_, _, err = dockerCmdWithError("run", "--net=mynet", "--ip", "172.28.99.99", "--ip6", "mynet_ip6", "busybox", "top")
|
||||
assert.ErrorContains(c, err, "invalid IPv6 address")
|
||||
assert.ErrorContains(c, err, "unable to parse IP")
|
||||
|
||||
// This is a case of IPv4 address to `--ip6`
|
||||
_, _, err = dockerCmdWithError("run", "--net=mynet", "--ip6", "172.28.99.99", "busybox", "top")
|
||||
|
||||
@@ -155,7 +155,7 @@ func TestDaemonHostGatewayIP(t *testing.T) {
|
||||
assert.Equal(t, 0, res.ExitCode)
|
||||
inspect, err := c.NetworkInspect(ctx, "bridge", client.NetworkInspectOptions{})
|
||||
assert.NilError(t, err)
|
||||
assert.Check(t, is.Contains(res.Stdout(), inspect.IPAM.Config[0].Gateway))
|
||||
assert.Check(t, is.Contains(res.Stdout(), inspect.IPAM.Config[0].Gateway.String()))
|
||||
c.ContainerRemove(ctx, cID, client.ContainerRemoveOptions{Force: true})
|
||||
d.Stop(t)
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"slices"
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp/cmpopts"
|
||||
"github.com/moby/moby/api/types/network"
|
||||
swarmtypes "github.com/moby/moby/api/types/swarm"
|
||||
"github.com/moby/moby/client"
|
||||
@@ -58,8 +59,8 @@ func TestDaemonDefaultBridgeIPAM_Docker0(t *testing.T) {
|
||||
"--default-address-pool", `base=fdd1:8161:2d2c::/56,size=64`,
|
||||
},
|
||||
expIPAMConfig: []network.IPAMConfig{
|
||||
{Subnet: "192.168.176.0/24", Gateway: "192.168.176.1"},
|
||||
{Subnet: "fdd1:8161:2d2c::/64", Gateway: "fdd1:8161:2d2c::1"},
|
||||
{Subnet: netip.MustParsePrefix("192.168.176.0/24"), Gateway: netip.MustParseAddr("192.168.176.1")},
|
||||
{Subnet: netip.MustParsePrefix("fdd1:8161:2d2c::/64"), Gateway: netip.MustParseAddr("fdd1:8161:2d2c::1")},
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -69,8 +70,8 @@ func TestDaemonDefaultBridgeIPAM_Docker0(t *testing.T) {
|
||||
"--fixed-cidr-v6", "fdd1:8161:2d2c::/64",
|
||||
},
|
||||
expIPAMConfig: []network.IPAMConfig{
|
||||
{Subnet: "192.168.176.0/24", IPRange: "192.168.176.0/24"},
|
||||
{Subnet: "fdd1:8161:2d2c::/64", IPRange: "fdd1:8161:2d2c::/64"},
|
||||
{Subnet: netip.MustParsePrefix("192.168.176.0/24"), IPRange: netip.MustParsePrefix("192.168.176.0/24")},
|
||||
{Subnet: netip.MustParsePrefix("fdd1:8161:2d2c::/64"), IPRange: netip.MustParsePrefix("fdd1:8161:2d2c::/64")},
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -80,16 +81,16 @@ func TestDaemonDefaultBridgeIPAM_Docker0(t *testing.T) {
|
||||
"--bip6", "fdd1:8161:2d2c::8888/64",
|
||||
},
|
||||
expIPAMConfig: []network.IPAMConfig{
|
||||
{Subnet: "192.168.176.0/24", Gateway: "192.168.176.88"},
|
||||
{Subnet: "fdd1:8161:2d2c::/64", Gateway: "fdd1:8161:2d2c::8888"},
|
||||
{Subnet: netip.MustParsePrefix("192.168.176.0/24"), Gateway: netip.MustParseAddr("192.168.176.88")},
|
||||
{Subnet: netip.MustParsePrefix("fdd1:8161:2d2c::/64"), Gateway: netip.MustParseAddr("fdd1:8161:2d2c::8888")},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "existing bridge address only",
|
||||
initialBridgeAddrs: []string{"192.168.176.88/24", "fdd1:8161:2d2c::8888/64"},
|
||||
expIPAMConfig: []network.IPAMConfig{
|
||||
{Subnet: "192.168.176.0/24", Gateway: "192.168.176.88"},
|
||||
{Subnet: "fdd1:8161:2d2c::/64", Gateway: "fdd1:8161:2d2c::8888"},
|
||||
{Subnet: netip.MustParsePrefix("192.168.176.0/24"), Gateway: netip.MustParseAddr("192.168.176.88")},
|
||||
{Subnet: netip.MustParsePrefix("fdd1:8161:2d2c::/64"), Gateway: netip.MustParseAddr("fdd1:8161:2d2c::8888")},
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -111,8 +112,8 @@ func TestDaemonDefaultBridgeIPAM_Docker0(t *testing.T) {
|
||||
// and it'd be a breaking change for anyone relying on the existing
|
||||
// behaviour.
|
||||
expIPAMConfig: []network.IPAMConfig{
|
||||
{Subnet: "192.168.176.0/20", IPRange: "192.168.176.0/24", Gateway: "192.168.176.88"},
|
||||
{Subnet: "fdd1:8161:2d2c::/56", IPRange: "fdd1:8161:2d2c::/64", Gateway: "fdd1:8161:2d2c::8888"},
|
||||
{Subnet: netip.MustParsePrefix("192.168.176.0/20"), IPRange: netip.MustParsePrefix("192.168.176.0/24"), Gateway: netip.MustParseAddr("192.168.176.88")},
|
||||
{Subnet: netip.MustParsePrefix("fdd1:8161:2d2c::/56"), IPRange: netip.MustParsePrefix("fdd1:8161:2d2c::/64"), Gateway: netip.MustParseAddr("fdd1:8161:2d2c::8888")},
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -122,8 +123,8 @@ func TestDaemonDefaultBridgeIPAM_Docker0(t *testing.T) {
|
||||
"--fixed-cidr-v6", "fe80::/64",
|
||||
},
|
||||
expIPAMConfig: []network.IPAMConfig{
|
||||
{Subnet: "192.168.176.0/24", IPRange: "192.168.176.0/24"},
|
||||
{Subnet: "fe80::/64", IPRange: "fe80::/64", Gateway: llGwPlaceholder},
|
||||
{Subnet: netip.MustParsePrefix("192.168.176.0/24"), IPRange: netip.MustParsePrefix("192.168.176.0/24")},
|
||||
{Subnet: netip.MustParsePrefix("fe80::/64"), IPRange: netip.MustParsePrefix("fe80::/64"), Gateway: llGwPlaceholder},
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -134,8 +135,8 @@ func TestDaemonDefaultBridgeIPAM_Docker0(t *testing.T) {
|
||||
"--fixed-cidr-v6", "fe80:1234::/64",
|
||||
},
|
||||
expIPAMConfig: []network.IPAMConfig{
|
||||
{Subnet: "192.168.176.0/20", IPRange: "192.168.176.0/24", Gateway: "192.168.176.88"},
|
||||
{Subnet: "fe80:1234::/56", IPRange: "fe80:1234::/64", Gateway: "fe80:1234::88"},
|
||||
{Subnet: netip.MustParsePrefix("192.168.176.0/20"), IPRange: netip.MustParsePrefix("192.168.176.0/24"), Gateway: netip.MustParseAddr("192.168.176.88")},
|
||||
{Subnet: netip.MustParsePrefix("fe80:1234::/56"), IPRange: netip.MustParsePrefix("fe80:1234::/64"), Gateway: netip.MustParseAddr("fe80:1234::88")},
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -146,8 +147,8 @@ func TestDaemonDefaultBridgeIPAM_Docker0(t *testing.T) {
|
||||
"--fixed-cidr-v6", "fdd1:8161:2d2c::/64", "--bip6", "fdd1:8161:2d2c::9999/64",
|
||||
},
|
||||
expIPAMConfig: []network.IPAMConfig{
|
||||
{Subnet: "192.168.176.0/24", IPRange: "192.168.176.0/24", Gateway: "192.168.176.99"},
|
||||
{Subnet: "fdd1:8161:2d2c::/64", IPRange: "fdd1:8161:2d2c::/64", Gateway: "fdd1:8161:2d2c::9999"},
|
||||
{Subnet: netip.MustParsePrefix("192.168.176.0/24"), IPRange: netip.MustParsePrefix("192.168.176.0/24"), Gateway: netip.MustParseAddr("192.168.176.99")},
|
||||
{Subnet: netip.MustParsePrefix("fdd1:8161:2d2c::/64"), IPRange: netip.MustParsePrefix("fdd1:8161:2d2c::/64"), Gateway: netip.MustParseAddr("fdd1:8161:2d2c::9999")},
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -158,8 +159,8 @@ func TestDaemonDefaultBridgeIPAM_Docker0(t *testing.T) {
|
||||
"--fixed-cidr-v6", "fdd1:8161:2d2c::/56",
|
||||
},
|
||||
expIPAMConfig: []network.IPAMConfig{
|
||||
{Subnet: "192.168.176.0/20", IPRange: "192.168.176.0/20", Gateway: "192.168.176.88"},
|
||||
{Subnet: "fdd1:8161:2d2c::/56", IPRange: "fdd1:8161:2d2c::/56", Gateway: "fdd1:8161:2d2c::8888"},
|
||||
{Subnet: netip.MustParsePrefix("192.168.176.0/20"), IPRange: netip.MustParsePrefix("192.168.176.0/20"), Gateway: netip.MustParseAddr("192.168.176.88")},
|
||||
{Subnet: netip.MustParsePrefix("fdd1:8161:2d2c::/56"), IPRange: netip.MustParsePrefix("fdd1:8161:2d2c::/56"), Gateway: netip.MustParseAddr("fdd1:8161:2d2c::8888")},
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -172,8 +173,8 @@ func TestDaemonDefaultBridgeIPAM_Docker0(t *testing.T) {
|
||||
// The bridge's address/subnet should be ignored, this is a change
|
||||
// of fixed-cidr.
|
||||
expIPAMConfig: []network.IPAMConfig{
|
||||
{Subnet: "192.168.177.0/24", IPRange: "192.168.177.0/24"},
|
||||
{Subnet: "fdd1:8161:2d2c:1::/64", IPRange: "fdd1:8161:2d2c:1::/64"},
|
||||
{Subnet: netip.MustParsePrefix("192.168.177.0/24"), IPRange: netip.MustParsePrefix("192.168.177.0/24")},
|
||||
{Subnet: netip.MustParsePrefix("fdd1:8161:2d2c:1::/64"), IPRange: netip.MustParsePrefix("fdd1:8161:2d2c:1::/64")},
|
||||
// No Gateway is configured, because the address could not be learnt from the
|
||||
// bridge. An address will have been allocated but, because there's config (the
|
||||
// fixed-cidr), inspect shows just the config. (Surprisingly, when there's no
|
||||
@@ -191,8 +192,8 @@ func TestDaemonDefaultBridgeIPAM_Docker0(t *testing.T) {
|
||||
"--fixed-cidr-v6", "fdd1:8161:2d2c:1::/64", "--bip6", "fdd1:8161:2d2c:1::9999/64",
|
||||
},
|
||||
expIPAMConfig: []network.IPAMConfig{
|
||||
{Subnet: "192.168.177.0/24", IPRange: "192.168.177.0/24", Gateway: "192.168.177.99"},
|
||||
{Subnet: "fdd1:8161:2d2c:1::/64", IPRange: "fdd1:8161:2d2c:1::/64", Gateway: "fdd1:8161:2d2c:1::9999"},
|
||||
{Subnet: netip.MustParsePrefix("192.168.177.0/24"), IPRange: netip.MustParsePrefix("192.168.177.0/24"), Gateway: netip.MustParseAddr("192.168.177.99")},
|
||||
{Subnet: netip.MustParsePrefix("fdd1:8161:2d2c:1::/64"), IPRange: netip.MustParsePrefix("fdd1:8161:2d2c:1::/64"), Gateway: netip.MustParseAddr("fdd1:8161:2d2c:1::9999")},
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -213,8 +214,8 @@ func TestDaemonDefaultBridgeIPAM_UserBr(t *testing.T) {
|
||||
name: "bridge only",
|
||||
initialBridgeAddrs: []string{"192.168.176.88/20", "fdd1:8161:2d2c::8888/64"},
|
||||
expIPAMConfig: []network.IPAMConfig{
|
||||
{Subnet: "192.168.176.0/20", Gateway: "192.168.176.88"},
|
||||
{Subnet: "fdd1:8161:2d2c::/64", Gateway: "fdd1:8161:2d2c::8888"},
|
||||
{Subnet: netip.MustParsePrefix("192.168.176.0/20"), Gateway: netip.MustParseAddr("192.168.176.88")},
|
||||
{Subnet: netip.MustParsePrefix("fdd1:8161:2d2c::/64"), Gateway: netip.MustParseAddr("fdd1:8161:2d2c::8888")},
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -224,8 +225,8 @@ func TestDaemonDefaultBridgeIPAM_UserBr(t *testing.T) {
|
||||
"--fixed-cidr-v6", "fdd1:8161:2d2c::/64",
|
||||
},
|
||||
expIPAMConfig: []network.IPAMConfig{
|
||||
{Subnet: "192.168.176.0/24", IPRange: "192.168.176.0/24"},
|
||||
{Subnet: "fdd1:8161:2d2c::/64", IPRange: "fdd1:8161:2d2c::/64"},
|
||||
{Subnet: netip.MustParsePrefix("192.168.176.0/24"), IPRange: netip.MustParsePrefix("192.168.176.0/24")},
|
||||
{Subnet: netip.MustParsePrefix("fdd1:8161:2d2c::/64"), IPRange: netip.MustParsePrefix("fdd1:8161:2d2c::/64")},
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -240,8 +241,8 @@ func TestDaemonDefaultBridgeIPAM_UserBr(t *testing.T) {
|
||||
},
|
||||
// Selected bip should be the one within fixed-cidr
|
||||
expIPAMConfig: []network.IPAMConfig{
|
||||
{Subnet: "192.168.176.0/20", IPRange: "192.168.176.0/24", Gateway: "192.168.176.88"},
|
||||
{Subnet: "fdd1:8161:2d2c:10::/60", IPRange: "fdd1:8161:2d2c:10::/64", Gateway: "fdd1:8161:2d2c:10::8888"},
|
||||
{Subnet: netip.MustParsePrefix("192.168.176.0/20"), IPRange: netip.MustParsePrefix("192.168.176.0/24"), Gateway: netip.MustParseAddr("192.168.176.88")},
|
||||
{Subnet: netip.MustParsePrefix("fdd1:8161:2d2c:10::/60"), IPRange: netip.MustParsePrefix("fdd1:8161:2d2c:10::/64"), Gateway: netip.MustParseAddr("fdd1:8161:2d2c:10::8888")},
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -256,8 +257,8 @@ func TestDaemonDefaultBridgeIPAM_UserBr(t *testing.T) {
|
||||
},
|
||||
// Selected bridge subnet should be the one that encompasses fixed-cidr.
|
||||
expIPAMConfig: []network.IPAMConfig{
|
||||
{Subnet: "192.168.176.0/20", IPRange: "192.168.177.0/24", Gateway: "192.168.176.88"},
|
||||
{Subnet: "fdd1:8161:2d2c:10::/60", IPRange: "fdd1:8161:2d2c:11::/64", Gateway: "fdd1:8161:2d2c:10::8888"},
|
||||
{Subnet: netip.MustParsePrefix("192.168.176.0/20"), IPRange: netip.MustParsePrefix("192.168.177.0/24"), Gateway: netip.MustParseAddr("192.168.176.88")},
|
||||
{Subnet: netip.MustParsePrefix("fdd1:8161:2d2c:10::/60"), IPRange: netip.MustParsePrefix("fdd1:8161:2d2c:11::/64"), Gateway: netip.MustParseAddr("fdd1:8161:2d2c:10::8888")},
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -268,8 +269,8 @@ func TestDaemonDefaultBridgeIPAM_UserBr(t *testing.T) {
|
||||
"--fixed-cidr-v6", "fe80::/64",
|
||||
},
|
||||
expIPAMConfig: []network.IPAMConfig{
|
||||
{Subnet: "192.168.176.0/20", IPRange: "192.168.176.0/24", Gateway: "192.168.176.88"},
|
||||
{Subnet: "fe80::/64", IPRange: "fe80::/64", Gateway: llGwPlaceholder},
|
||||
{Subnet: netip.MustParsePrefix("192.168.176.0/20"), IPRange: netip.MustParsePrefix("192.168.176.0/24"), Gateway: netip.MustParseAddr("192.168.176.88")},
|
||||
{Subnet: netip.MustParsePrefix("fe80::/64"), IPRange: netip.MustParsePrefix("fe80::/64"), Gateway: llGwPlaceholder},
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -280,8 +281,8 @@ func TestDaemonDefaultBridgeIPAM_UserBr(t *testing.T) {
|
||||
"--fixed-cidr-v6", "fe80:1234::/64",
|
||||
},
|
||||
expIPAMConfig: []network.IPAMConfig{
|
||||
{Subnet: "192.168.176.0/20", IPRange: "192.168.176.0/24", Gateway: "192.168.176.88"},
|
||||
{Subnet: "fe80:1234::/56", IPRange: "fe80:1234::/64", Gateway: "fe80:1234::88"},
|
||||
{Subnet: netip.MustParsePrefix("192.168.176.0/20"), IPRange: netip.MustParsePrefix("192.168.176.0/24"), Gateway: netip.MustParseAddr("192.168.176.88")},
|
||||
{Subnet: netip.MustParsePrefix("fe80:1234::/56"), IPRange: netip.MustParsePrefix("fe80:1234::/64"), Gateway: netip.MustParseAddr("fe80:1234::88")},
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -295,7 +296,7 @@ func TestDaemonDefaultBridgeIPAM_UserBr(t *testing.T) {
|
||||
// would normally result in a docker network that allocated addresses
|
||||
// within the selected subnet. So, fixed-cidr is dropped, making the
|
||||
// whole subnet allocatable.
|
||||
expIPAMConfig: []network.IPAMConfig{{Subnet: "192.168.176.0/24", Gateway: "192.168.176.88"}},
|
||||
expIPAMConfig: []network.IPAMConfig{{Subnet: netip.MustParsePrefix("192.168.176.0/24"), Gateway: netip.MustParseAddr("192.168.176.88")}},
|
||||
},
|
||||
{
|
||||
name: "no bridge ip within fixed-cidr",
|
||||
@@ -308,7 +309,7 @@ func TestDaemonDefaultBridgeIPAM_UserBr(t *testing.T) {
|
||||
// would normally result in a docker network that allocated addresses
|
||||
// within the selected subnet. So, fixed-cidr is dropped, making the
|
||||
// whole subnet allocatable.
|
||||
expIPAMConfig: []network.IPAMConfig{{Subnet: "192.168.160.0/20", Gateway: "192.168.160.88"}},
|
||||
expIPAMConfig: []network.IPAMConfig{{Subnet: netip.MustParsePrefix("192.168.160.0/20"), Gateway: netip.MustParseAddr("192.168.160.88")}},
|
||||
},
|
||||
{
|
||||
name: "fixed-cidr contains bridge subnet",
|
||||
@@ -322,7 +323,7 @@ func TestDaemonDefaultBridgeIPAM_UserBr(t *testing.T) {
|
||||
// within the selected subnet. So, fixed-cidr is dropped, making the
|
||||
// whole subnet allocatable.
|
||||
ipv4Only: true,
|
||||
expIPAMConfig: []network.IPAMConfig{{Subnet: "192.168.177.0/24", Gateway: "192.168.177.1"}},
|
||||
expIPAMConfig: []network.IPAMConfig{{Subnet: netip.MustParsePrefix("192.168.177.0/24"), Gateway: netip.MustParseAddr("192.168.177.1")}},
|
||||
},
|
||||
|
||||
{
|
||||
@@ -364,7 +365,7 @@ func TestDaemonDefaultBridgeIPAM_UserBr(t *testing.T) {
|
||||
// llGwPlaceholder can be used as a value for "Gateway" in expected IPAM config,
|
||||
// before comparison with actual results it'll be replaced by the kernel assigned
|
||||
// link local IPv6 address for the bridge.
|
||||
const llGwPlaceholder = "ll-gateway-placeholder"
|
||||
var llGwPlaceholder = netip.MustParseAddr("fe80::1").WithZone("ll-gateway-placeholder")
|
||||
|
||||
type defaultBridgeIPAMTestCase struct {
|
||||
name string
|
||||
@@ -389,7 +390,7 @@ func testDefaultBridgeIPAM(ctx context.Context, t *testing.T, tc defaultBridgeIP
|
||||
defer cleanup()
|
||||
|
||||
host.Do(t, func() {
|
||||
llAddr := createBridge(t, tc.bridgeName, tc.initialBridgeAddrs)
|
||||
llAddr, _ := netip.AddrFromSlice(createBridge(t, tc.bridgeName, tc.initialBridgeAddrs))
|
||||
|
||||
var dArgs []string
|
||||
if !tc.ipv4Only {
|
||||
@@ -424,10 +425,10 @@ func testDefaultBridgeIPAM(ctx context.Context, t *testing.T, tc defaultBridgeIP
|
||||
expIPAMConfig := slices.Clone(tc.expIPAMConfig)
|
||||
for i := range expIPAMConfig {
|
||||
if expIPAMConfig[i].Gateway == llGwPlaceholder {
|
||||
expIPAMConfig[i].Gateway = llAddr.String()
|
||||
expIPAMConfig[i].Gateway = llAddr
|
||||
}
|
||||
}
|
||||
assert.Check(t, is.DeepEqual(insp.IPAM.Config, expIPAMConfig))
|
||||
assert.Check(t, is.DeepEqual(insp.IPAM.Config, expIPAMConfig, cmpopts.EquateComparable(netip.Addr{}, netip.Prefix{})))
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package container
|
||||
|
||||
import (
|
||||
"maps"
|
||||
"net/netip"
|
||||
"slices"
|
||||
"strings"
|
||||
|
||||
@@ -173,7 +174,7 @@ func WithIPv4(networkName, ip string) func(*TestContainerConfig) {
|
||||
if c.NetworkingConfig.EndpointsConfig[networkName].IPAMConfig == nil {
|
||||
c.NetworkingConfig.EndpointsConfig[networkName].IPAMConfig = &network.EndpointIPAMConfig{}
|
||||
}
|
||||
c.NetworkingConfig.EndpointsConfig[networkName].IPAMConfig.IPv4Address = ip
|
||||
c.NetworkingConfig.EndpointsConfig[networkName].IPAMConfig.IPv4Address = netip.MustParseAddr(ip)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -189,7 +190,7 @@ func WithIPv6(networkName, ip string) func(*TestContainerConfig) {
|
||||
if c.NetworkingConfig.EndpointsConfig[networkName].IPAMConfig == nil {
|
||||
c.NetworkingConfig.EndpointsConfig[networkName].IPAMConfig = &network.EndpointIPAMConfig{}
|
||||
}
|
||||
c.NetworkingConfig.EndpointsConfig[networkName].IPAMConfig.IPv6Address = ip
|
||||
c.NetworkingConfig.EndpointsConfig[networkName].IPAMConfig.IPv6Address = netip.MustParseAddr(ip)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package network
|
||||
|
||||
import (
|
||||
"net/netip"
|
||||
|
||||
"github.com/moby/moby/api/types/network"
|
||||
"github.com/moby/moby/client"
|
||||
)
|
||||
@@ -137,12 +139,19 @@ func WithIPAM(subnet, gateway string) func(*client.NetworkCreateOptions) {
|
||||
|
||||
// WithIPAMRange adds an IPAM with the specified Subnet, IPRange and Gateway to the network
|
||||
func WithIPAMRange(subnet, iprange, gateway string) func(*client.NetworkCreateOptions) {
|
||||
return WithIPAMConfig(network.IPAMConfig{
|
||||
Subnet: subnet,
|
||||
IPRange: iprange,
|
||||
Gateway: gateway,
|
||||
AuxAddress: map[string]string{},
|
||||
})
|
||||
c := network.IPAMConfig{
|
||||
AuxAddress: map[string]netip.Addr{},
|
||||
}
|
||||
if subnet != "" {
|
||||
c.Subnet = netip.MustParsePrefix(subnet)
|
||||
}
|
||||
if iprange != "" {
|
||||
c.IPRange = netip.MustParsePrefix(iprange)
|
||||
}
|
||||
if gateway != "" {
|
||||
c.Gateway = netip.MustParseAddr(gateway)
|
||||
}
|
||||
return WithIPAMConfig(c)
|
||||
}
|
||||
|
||||
// WithIPAMConfig adds the provided IPAM configurations to the network
|
||||
|
||||
@@ -71,8 +71,7 @@ func TestCreateWithIPv6DefaultsToULAPrefix(t *testing.T) {
|
||||
assert.NilError(t, err)
|
||||
|
||||
for _, ipam := range nw.IPAM.Config {
|
||||
ipr := netip.MustParsePrefix(ipam.Subnet)
|
||||
if netip.MustParsePrefix("fd00::/8").Overlaps(ipr) {
|
||||
if netip.MustParsePrefix("fd00::/8").Overlaps(ipam.Subnet) {
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -98,8 +97,7 @@ func TestCreateWithIPv6WithoutEnableIPv6Flag(t *testing.T) {
|
||||
assert.NilError(t, err)
|
||||
|
||||
for _, ipam := range nw.IPAM.Config {
|
||||
ipr := netip.MustParsePrefix(ipam.Subnet)
|
||||
if netip.MustParsePrefix("fd00::/8").Overlaps(ipr) {
|
||||
if netip.MustParsePrefix("fd00::/8").Overlaps(ipam.Subnet) {
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -680,13 +678,13 @@ func TestLegacyLink(t *testing.T) {
|
||||
}{
|
||||
{
|
||||
name: "no link",
|
||||
host: svrAddr,
|
||||
host: svrAddr.String(),
|
||||
expect: "download timed out",
|
||||
},
|
||||
{
|
||||
name: "access by address",
|
||||
links: []string{svrName},
|
||||
host: svrAddr,
|
||||
host: svrAddr.String(),
|
||||
expect: "404 Not Found", // Got a response, but the server has nothing to serve.
|
||||
},
|
||||
{
|
||||
@@ -773,7 +771,7 @@ func TestRemoveLegacyLink(t *testing.T) {
|
||||
|
||||
// Check the icc=false rules now block access by address.
|
||||
svrAddr := inspSvr.NetworkSettings.Networks["bridge"].IPAddress
|
||||
res = ctr.ExecT(ctx, t, c, clientId, []string{"wget", "-T3", "http://" + svrAddr})
|
||||
res = ctr.ExecT(ctx, t, c, clientId, []string{"wget", "-T3", "http://" + svrAddr.String()})
|
||||
assert.Check(t, is.Contains(res.Stderr(), "download timed out"))
|
||||
}
|
||||
|
||||
@@ -1109,22 +1107,22 @@ func TestBridgeIPAMStatus(t *testing.T) {
|
||||
network.CreateNoError(ctx, t, c, netName,
|
||||
network.WithIPv4(true),
|
||||
network.WithIPAMConfig(networktypes.IPAMConfig{
|
||||
Subnet: cidrv4.String(),
|
||||
IPRange: ipv4Range,
|
||||
Gateway: ipv4gw,
|
||||
AuxAddress: map[string]string{
|
||||
"reserved": auxIPv4FromRange,
|
||||
"reserved_1": auxIPv4OutOfRange,
|
||||
Subnet: cidrv4,
|
||||
IPRange: netip.MustParsePrefix(ipv4Range),
|
||||
Gateway: netip.MustParseAddr(ipv4gw),
|
||||
AuxAddress: map[string]netip.Addr{
|
||||
"reserved": netip.MustParseAddr(auxIPv4FromRange),
|
||||
"reserved_1": netip.MustParseAddr(auxIPv4OutOfRange),
|
||||
},
|
||||
}),
|
||||
network.WithIPv6(),
|
||||
network.WithIPAMConfig(networktypes.IPAMConfig{
|
||||
Subnet: cidrv6.String(),
|
||||
IPRange: ipv6Range,
|
||||
Gateway: ipv6gw,
|
||||
AuxAddress: map[string]string{
|
||||
"reserved1": auxIPv6FromRange,
|
||||
"reserved2": auxIPv6OutOfRange,
|
||||
Subnet: cidrv6,
|
||||
IPRange: netip.MustParsePrefix(ipv6Range),
|
||||
Gateway: netip.MustParseAddr(ipv6gw),
|
||||
AuxAddress: map[string]netip.Addr{
|
||||
"reserved1": netip.MustParseAddr(auxIPv6FromRange),
|
||||
"reserved2": netip.MustParseAddr(auxIPv6OutOfRange),
|
||||
},
|
||||
}),
|
||||
)
|
||||
@@ -1205,7 +1203,7 @@ func TestBridgeIPAMStatus(t *testing.T) {
|
||||
network.WithIPv4(false),
|
||||
network.WithIPv6(),
|
||||
network.WithIPAMConfig(networktypes.IPAMConfig{
|
||||
Subnet: cidr.String(),
|
||||
Subnet: cidr,
|
||||
}),
|
||||
)
|
||||
defer c.NetworkRemove(ctx, netName)
|
||||
|
||||
@@ -49,12 +49,12 @@ func TestInspectNetwork(t *testing.T) {
|
||||
|
||||
networkName := "Overlay" + t.Name()
|
||||
cidrv4 := netip.MustParsePrefix("192.168.0.0/24")
|
||||
const ipv4Range = "192.168.0.0/25"
|
||||
ipv4Range := netip.MustParsePrefix("192.168.0.0/25")
|
||||
|
||||
overlayID := network.CreateNoError(ctx, t, c1, networkName,
|
||||
network.WithDriver("overlay"),
|
||||
network.WithIPAMConfig(networktypes.IPAMConfig{
|
||||
Subnet: cidrv4.String(),
|
||||
Subnet: cidrv4,
|
||||
IPRange: ipv4Range,
|
||||
}),
|
||||
)
|
||||
@@ -133,8 +133,8 @@ func TestInspectNetwork(t *testing.T) {
|
||||
|
||||
assert.Check(t, nw.IPAM.Config != nil)
|
||||
for _, cfg := range nw.IPAM.Config {
|
||||
assert.Assert(t, cfg.Gateway != "")
|
||||
assert.Assert(t, cfg.Subnet != "")
|
||||
assert.Assert(t, cfg.Gateway.IsValid())
|
||||
assert.Assert(t, cfg.Subnet.IsValid())
|
||||
}
|
||||
|
||||
if d.CachedInfo.Swarm.ControlAvailable {
|
||||
|
||||
@@ -271,15 +271,15 @@ func testIpvlanL2MultiSubnet(t *testing.T, ctx context.Context, client dclient.A
|
||||
c1, err := client.ContainerInspect(ctx, id1)
|
||||
assert.NilError(t, err)
|
||||
// Inspect the v4 gateway to ensure no default GW was assigned
|
||||
assert.Check(t, is.Equal(c1.NetworkSettings.Networks[netName].Gateway, ""))
|
||||
assert.Check(t, !c1.NetworkSettings.Networks[netName].Gateway.IsValid())
|
||||
// Inspect the v6 gateway to ensure no default GW was assigned
|
||||
assert.Check(t, is.Equal(c1.NetworkSettings.Networks[netName].IPv6Gateway, ""))
|
||||
assert.Check(t, !c1.NetworkSettings.Networks[netName].IPv6Gateway.IsValid())
|
||||
|
||||
// verify ipv4 connectivity to the explicit --ip address second to first
|
||||
_, err = container.Exec(ctx, client, id2, []string{"ping", "-c", "1", c1.NetworkSettings.Networks[netName].IPAddress})
|
||||
_, err = container.Exec(ctx, client, id2, []string{"ping", "-c", "1", c1.NetworkSettings.Networks[netName].IPAddress.String()})
|
||||
assert.NilError(t, err)
|
||||
// verify ipv6 connectivity to the explicit --ip6 address second to first
|
||||
_, err = container.Exec(ctx, client, id2, []string{"ping6", "-c", "1", c1.NetworkSettings.Networks[netName].GlobalIPv6Address})
|
||||
_, err = container.Exec(ctx, client, id2, []string{"ping6", "-c", "1", c1.NetworkSettings.Networks[netName].GlobalIPv6Address.String()})
|
||||
assert.NilError(t, err)
|
||||
|
||||
// start dual stack containers and verify the user specified --ip and --ip6 addresses on subnets 172.28.102.0/24 and 2001:db8:abc4::/64
|
||||
@@ -297,21 +297,21 @@ func testIpvlanL2MultiSubnet(t *testing.T, ctx context.Context, client dclient.A
|
||||
assert.NilError(t, err)
|
||||
if parent == "" {
|
||||
// Inspect the v4 gateway to ensure no default GW was assigned
|
||||
assert.Check(t, is.Equal(c3.NetworkSettings.Networks[netName].Gateway, ""))
|
||||
assert.Check(t, !c3.NetworkSettings.Networks[netName].Gateway.IsValid())
|
||||
// Inspect the v6 gateway to ensure no default GW was assigned
|
||||
assert.Check(t, is.Equal(c3.NetworkSettings.Networks[netName].IPv6Gateway, ""))
|
||||
assert.Check(t, !c3.NetworkSettings.Networks[netName].IPv6Gateway.IsValid())
|
||||
} else {
|
||||
// Inspect the v4 gateway to ensure the proper explicitly assigned default GW was assigned
|
||||
assert.Check(t, is.Equal(c3.NetworkSettings.Networks[netName].Gateway, "172.28.202.254"))
|
||||
assert.Check(t, is.Equal(c3.NetworkSettings.Networks[netName].Gateway, netip.MustParseAddr("172.28.202.254")))
|
||||
// Inspect the v6 gateway to ensure the proper explicitly assigned default GW was assigned
|
||||
assert.Check(t, is.Equal(c3.NetworkSettings.Networks[netName].IPv6Gateway, "2001:db8:abc6::254"))
|
||||
assert.Check(t, is.Equal(c3.NetworkSettings.Networks[netName].IPv6Gateway, netip.MustParseAddr("2001:db8:abc6::254")))
|
||||
}
|
||||
|
||||
// verify ipv4 connectivity to the explicit --ip address from third to fourth
|
||||
_, err = container.Exec(ctx, client, id4, []string{"ping", "-c", "1", c3.NetworkSettings.Networks[netName].IPAddress})
|
||||
_, err = container.Exec(ctx, client, id4, []string{"ping", "-c", "1", c3.NetworkSettings.Networks[netName].IPAddress.String()})
|
||||
assert.NilError(t, err)
|
||||
// verify ipv6 connectivity to the explicit --ip6 address from third to fourth
|
||||
_, err = container.Exec(ctx, client, id4, []string{"ping6", "-c", "1", c3.NetworkSettings.Networks[netName].GlobalIPv6Address})
|
||||
_, err = container.Exec(ctx, client, id4, []string{"ping6", "-c", "1", c3.NetworkSettings.Networks[netName].GlobalIPv6Address.String()})
|
||||
assert.NilError(t, err)
|
||||
}
|
||||
|
||||
@@ -342,10 +342,10 @@ func testIpvlanL3MultiSubnet(t *testing.T, ctx context.Context, client dclient.A
|
||||
assert.NilError(t, err)
|
||||
|
||||
// verify ipv4 connectivity to the explicit --ipv address second to first
|
||||
_, err = container.Exec(ctx, client, id2, []string{"ping", "-c", "1", c1.NetworkSettings.Networks[netName].IPAddress})
|
||||
_, err = container.Exec(ctx, client, id2, []string{"ping", "-c", "1", c1.NetworkSettings.Networks[netName].IPAddress.String()})
|
||||
assert.NilError(t, err)
|
||||
// verify ipv6 connectivity to the explicit --ipv6 address second to first
|
||||
_, err = container.Exec(ctx, client, id2, []string{"ping6", "-c", "1", c1.NetworkSettings.Networks[netName].GlobalIPv6Address})
|
||||
_, err = container.Exec(ctx, client, id2, []string{"ping6", "-c", "1", c1.NetworkSettings.Networks[netName].GlobalIPv6Address.String()})
|
||||
assert.NilError(t, err)
|
||||
|
||||
// start dual stack containers and verify the user specified --ip and --ip6 addresses on subnets 172.28.102.0/24 and 2001:db8:abc4::/64
|
||||
@@ -363,20 +363,20 @@ func testIpvlanL3MultiSubnet(t *testing.T, ctx context.Context, client dclient.A
|
||||
assert.NilError(t, err)
|
||||
|
||||
// verify ipv4 connectivity to the explicit --ipv address from third to fourth
|
||||
_, err = container.Exec(ctx, client, id4, []string{"ping", "-c", "1", c3.NetworkSettings.Networks[netName].IPAddress})
|
||||
_, err = container.Exec(ctx, client, id4, []string{"ping", "-c", "1", c3.NetworkSettings.Networks[netName].IPAddress.String()})
|
||||
assert.NilError(t, err)
|
||||
// verify ipv6 connectivity to the explicit --ipv6 address from third to fourth
|
||||
_, err = container.Exec(ctx, client, id4, []string{"ping6", "-c", "1", c3.NetworkSettings.Networks[netName].GlobalIPv6Address})
|
||||
_, err = container.Exec(ctx, client, id4, []string{"ping6", "-c", "1", c3.NetworkSettings.Networks[netName].GlobalIPv6Address.String()})
|
||||
assert.NilError(t, err)
|
||||
|
||||
// Inspect the v4 gateway to ensure no next hop is assigned in L3 mode
|
||||
assert.Equal(t, c1.NetworkSettings.Networks[netName].Gateway, "")
|
||||
assert.Check(t, !c1.NetworkSettings.Networks[netName].Gateway.IsValid())
|
||||
// Inspect the v6 gateway to ensure the explicitly specified default GW is ignored per L3 mode enabled
|
||||
assert.Equal(t, c1.NetworkSettings.Networks[netName].IPv6Gateway, "")
|
||||
assert.Check(t, !c1.NetworkSettings.Networks[netName].IPv6Gateway.IsValid())
|
||||
// Inspect the v4 gateway to ensure no next hop is assigned in L3 mode
|
||||
assert.Equal(t, c3.NetworkSettings.Networks[netName].Gateway, "")
|
||||
assert.Check(t, !c3.NetworkSettings.Networks[netName].Gateway.IsValid())
|
||||
// Inspect the v6 gateway to ensure the explicitly specified default GW is ignored per L3 mode enabled
|
||||
assert.Equal(t, c3.NetworkSettings.Networks[netName].IPv6Gateway, "")
|
||||
assert.Check(t, !c3.NetworkSettings.Networks[netName].IPv6Gateway.IsValid())
|
||||
}
|
||||
|
||||
// Verify ipvlan l2 mode sets the proper default gateway routes via netlink
|
||||
@@ -495,11 +495,11 @@ func TestIpvlanIPAM(t *testing.T) {
|
||||
net.WithIPv4(tc.enableIPv4),
|
||||
net.WithIPAMConfig(
|
||||
network.IPAMConfig{
|
||||
Subnet: subnetv4.String(),
|
||||
Subnet: subnetv4,
|
||||
},
|
||||
network.IPAMConfig{
|
||||
Subnet: subnetv6.String(),
|
||||
IPRange: "2001:db8:abcd::100/120",
|
||||
Subnet: subnetv6,
|
||||
IPRange: netip.MustParsePrefix("2001:db8:abcd::100/120"),
|
||||
},
|
||||
),
|
||||
}
|
||||
@@ -608,16 +608,16 @@ func TestIpvlanIPAMOverlap(t *testing.T) {
|
||||
net.WithIPv6(),
|
||||
net.WithIPAMConfig(
|
||||
network.IPAMConfig{
|
||||
Subnet: cidrv4.String(),
|
||||
IPRange: "192.168.0.0/25",
|
||||
Gateway: "192.168.0.1",
|
||||
AuxAddress: map[string]string{
|
||||
"reserved": "192.168.0.100",
|
||||
Subnet: cidrv4,
|
||||
IPRange: netip.MustParsePrefix("192.168.0.0/25"),
|
||||
Gateway: netip.MustParseAddr("192.168.0.1"),
|
||||
AuxAddress: map[string]netip.Addr{
|
||||
"reserved": netip.MustParseAddr("192.168.0.100"),
|
||||
},
|
||||
},
|
||||
network.IPAMConfig{
|
||||
Subnet: cidrv6.String(),
|
||||
IPRange: "2001:db8:abcd::/124",
|
||||
Subnet: cidrv6,
|
||||
IPRange: netip.MustParsePrefix("2001:db8:abcd::/124"),
|
||||
},
|
||||
),
|
||||
)
|
||||
@@ -640,12 +640,12 @@ func TestIpvlanIPAMOverlap(t *testing.T) {
|
||||
net.WithIPv6(),
|
||||
net.WithIPAMConfig(
|
||||
network.IPAMConfig{
|
||||
Subnet: cidrv4.String(),
|
||||
IPRange: "192.168.0.0/24",
|
||||
Subnet: cidrv4,
|
||||
IPRange: netip.MustParsePrefix("192.168.0.0/24"),
|
||||
},
|
||||
network.IPAMConfig{
|
||||
Subnet: cidrv6.String(),
|
||||
IPRange: "2001:db8:abcd::/120",
|
||||
Subnet: cidrv6,
|
||||
IPRange: netip.MustParsePrefix("2001:db8:abcd::/120"),
|
||||
},
|
||||
),
|
||||
)
|
||||
@@ -669,12 +669,12 @@ func TestIpvlanIPAMOverlap(t *testing.T) {
|
||||
net.WithIPv6(),
|
||||
net.WithIPAMConfig(
|
||||
network.IPAMConfig{
|
||||
Subnet: cidrv4.String(),
|
||||
IPRange: "192.168.0.128/25",
|
||||
Subnet: cidrv4,
|
||||
IPRange: netip.MustParsePrefix("192.168.0.128/25"),
|
||||
},
|
||||
network.IPAMConfig{
|
||||
Subnet: cidrv6.String(),
|
||||
IPRange: "2001:db8:abcd::80/124",
|
||||
Subnet: cidrv6,
|
||||
IPRange: netip.MustParsePrefix("2001:db8:abcd::80/124"),
|
||||
},
|
||||
),
|
||||
)
|
||||
|
||||
@@ -359,15 +359,15 @@ func testMacvlanMultiSubnet(t *testing.T, ctx context.Context, client client.API
|
||||
c1, err := client.ContainerInspect(ctx, id1)
|
||||
assert.NilError(t, err)
|
||||
// Inspect the v4 gateway to ensure no default GW was assigned
|
||||
assert.Check(t, is.Equal(c1.NetworkSettings.Networks["dualstackbridge"].Gateway, ""))
|
||||
assert.Check(t, !c1.NetworkSettings.Networks["dualstackbridge"].Gateway.IsValid())
|
||||
// Inspect the v6 gateway to ensure no default GW was assigned
|
||||
assert.Check(t, is.Equal(c1.NetworkSettings.Networks["dualstackbridge"].IPv6Gateway, ""))
|
||||
assert.Check(t, !c1.NetworkSettings.Networks["dualstackbridge"].IPv6Gateway.IsValid())
|
||||
|
||||
// verify ipv4 connectivity to the explicit --ip address second to first
|
||||
_, err = container.Exec(ctx, client, id2, []string{"ping", "-c", "1", c1.NetworkSettings.Networks["dualstackbridge"].IPAddress})
|
||||
_, err = container.Exec(ctx, client, id2, []string{"ping", "-c", "1", c1.NetworkSettings.Networks["dualstackbridge"].IPAddress.String()})
|
||||
assert.NilError(t, err)
|
||||
// verify ipv6 connectivity to the explicit --ip6 address second to first
|
||||
_, err = container.Exec(ctx, client, id2, []string{"ping6", "-c", "1", c1.NetworkSettings.Networks["dualstackbridge"].GlobalIPv6Address})
|
||||
_, err = container.Exec(ctx, client, id2, []string{"ping6", "-c", "1", c1.NetworkSettings.Networks["dualstackbridge"].GlobalIPv6Address.String()})
|
||||
assert.NilError(t, err)
|
||||
|
||||
// start dual stack containers and verify the user specified --ip and --ip6 addresses on subnets 172.28.102.0/24 and 2001:db8:abc4::/64
|
||||
@@ -385,21 +385,21 @@ func testMacvlanMultiSubnet(t *testing.T, ctx context.Context, client client.API
|
||||
assert.NilError(t, err)
|
||||
if parent == "" {
|
||||
// Inspect the v4 gateway to ensure no default GW was assigned
|
||||
assert.Check(t, is.Equal(c3.NetworkSettings.Networks["dualstackbridge"].Gateway, ""))
|
||||
assert.Check(t, !c3.NetworkSettings.Networks["dualstackbridge"].Gateway.IsValid())
|
||||
// Inspect the v6 gateway to ensure no default GW was assigned
|
||||
assert.Check(t, is.Equal(c3.NetworkSettings.Networks["dualstackbridge"].IPv6Gateway, ""))
|
||||
assert.Check(t, !c3.NetworkSettings.Networks["dualstackbridge"].IPv6Gateway.IsValid())
|
||||
} else {
|
||||
// Inspect the v4 gateway to ensure the proper explicitly assigned default GW was assigned
|
||||
assert.Check(t, is.Equal(c3.NetworkSettings.Networks["dualstackbridge"].Gateway, "172.28.102.254"))
|
||||
assert.Check(t, is.Equal(c3.NetworkSettings.Networks["dualstackbridge"].Gateway, netip.MustParseAddr("172.28.102.254")))
|
||||
// Inspect the v6 gateway to ensure the proper explicitly assigned default GW was assigned
|
||||
assert.Check(t, is.Equal(c3.NetworkSettings.Networks["dualstackbridge"].IPv6Gateway, "2001:db8:abc4::254"))
|
||||
assert.Check(t, is.Equal(c3.NetworkSettings.Networks["dualstackbridge"].IPv6Gateway, netip.MustParseAddr("2001:db8:abc4::254")))
|
||||
}
|
||||
|
||||
// verify ipv4 connectivity to the explicit --ip address from third to fourth
|
||||
_, err = container.Exec(ctx, client, id4, []string{"ping", "-c", "1", c3.NetworkSettings.Networks["dualstackbridge"].IPAddress})
|
||||
_, err = container.Exec(ctx, client, id4, []string{"ping", "-c", "1", c3.NetworkSettings.Networks["dualstackbridge"].IPAddress.String()})
|
||||
assert.NilError(t, err)
|
||||
// verify ipv6 connectivity to the explicit --ip6 address from third to fourth
|
||||
_, err = container.Exec(ctx, client, id4, []string{"ping6", "-c", "1", c3.NetworkSettings.Networks["dualstackbridge"].GlobalIPv6Address})
|
||||
_, err = container.Exec(ctx, client, id4, []string{"ping6", "-c", "1", c3.NetworkSettings.Networks["dualstackbridge"].GlobalIPv6Address.String()})
|
||||
assert.NilError(t, err)
|
||||
}
|
||||
|
||||
@@ -492,17 +492,17 @@ func TestMacvlanIPAM(t *testing.T) {
|
||||
net.WithIPv4(tc.enableIPv4),
|
||||
net.WithIPAMConfig(
|
||||
network.IPAMConfig{
|
||||
Subnet: subnetv4.String(),
|
||||
IPRange: "10.66.77.64/30",
|
||||
Gateway: "10.66.77.1",
|
||||
AuxAddress: map[string]string{
|
||||
"inrange": "10.66.77.65",
|
||||
"outofrange": "10.66.77.128",
|
||||
Subnet: subnetv4,
|
||||
IPRange: netip.MustParsePrefix("10.66.77.64/30"),
|
||||
Gateway: netip.MustParseAddr("10.66.77.1"),
|
||||
AuxAddress: map[string]netip.Addr{
|
||||
"inrange": netip.MustParseAddr("10.66.77.65"),
|
||||
"outofrange": netip.MustParseAddr("10.66.77.128"),
|
||||
},
|
||||
},
|
||||
network.IPAMConfig{
|
||||
Subnet: subnetv6.String(),
|
||||
IPRange: "2001:db8:abcd::/120",
|
||||
Subnet: subnetv6,
|
||||
IPRange: netip.MustParsePrefix("2001:db8:abcd::/120"),
|
||||
},
|
||||
),
|
||||
}
|
||||
@@ -611,16 +611,16 @@ func TestMacvlanIPAMOverlap(t *testing.T) {
|
||||
net.WithIPv6(),
|
||||
net.WithIPAMConfig(
|
||||
network.IPAMConfig{
|
||||
Subnet: cidrv4.String(),
|
||||
IPRange: "192.168.0.0/25",
|
||||
Gateway: "192.168.0.1",
|
||||
AuxAddress: map[string]string{
|
||||
"reserved": "192.168.0.100",
|
||||
Subnet: cidrv4,
|
||||
IPRange: netip.MustParsePrefix("192.168.0.0/25"),
|
||||
Gateway: netip.MustParseAddr("192.168.0.1"),
|
||||
AuxAddress: map[string]netip.Addr{
|
||||
"reserved": netip.MustParseAddr("192.168.0.100"),
|
||||
},
|
||||
},
|
||||
network.IPAMConfig{
|
||||
Subnet: cidrv6.String(),
|
||||
IPRange: "2001:db8:abcd::/124",
|
||||
Subnet: cidrv6,
|
||||
IPRange: netip.MustParsePrefix("2001:db8:abcd::/124"),
|
||||
},
|
||||
),
|
||||
)
|
||||
@@ -643,12 +643,12 @@ func TestMacvlanIPAMOverlap(t *testing.T) {
|
||||
net.WithIPv6(),
|
||||
net.WithIPAMConfig(
|
||||
network.IPAMConfig{
|
||||
Subnet: cidrv4.String(),
|
||||
IPRange: "192.168.0.0/24",
|
||||
Subnet: cidrv4,
|
||||
IPRange: netip.MustParsePrefix("192.168.0.0/24"),
|
||||
},
|
||||
network.IPAMConfig{
|
||||
Subnet: cidrv6.String(),
|
||||
IPRange: "2001:db8:abcd::/120",
|
||||
Subnet: cidrv6,
|
||||
IPRange: netip.MustParsePrefix("2001:db8:abcd::/120"),
|
||||
},
|
||||
),
|
||||
)
|
||||
@@ -672,12 +672,12 @@ func TestMacvlanIPAMOverlap(t *testing.T) {
|
||||
net.WithIPv6(),
|
||||
net.WithIPAMConfig(
|
||||
network.IPAMConfig{
|
||||
Subnet: cidrv4.String(),
|
||||
IPRange: "192.168.0.128/25",
|
||||
Subnet: cidrv4,
|
||||
IPRange: netip.MustParsePrefix("192.168.0.128/25"),
|
||||
},
|
||||
network.IPAMConfig{
|
||||
Subnet: cidrv6.String(),
|
||||
IPRange: "2001:db8:abcd::80/124",
|
||||
Subnet: cidrv6,
|
||||
IPRange: netip.MustParsePrefix("2001:db8:abcd::80/124"),
|
||||
},
|
||||
),
|
||||
)
|
||||
|
||||
@@ -98,7 +98,7 @@ func TestHostIPv4BridgeLabel(t *testing.T) {
|
||||
out.IPAM.Config[0].Subnet, ipv4SNATAddr)
|
||||
assert.Check(t, is.Contains(chain, exp))
|
||||
} else {
|
||||
testutil.RunCommand(ctx, "iptables", "-t", "nat", "-C", "POSTROUTING", "-s", out.IPAM.Config[0].Subnet, "!", "-o", bridgeName, "-j", "SNAT", "--to-source", ipv4SNATAddr).Assert(t, icmd.Success)
|
||||
testutil.RunCommand(ctx, "iptables", "-t", "nat", "-C", "POSTROUTING", "-s", out.IPAM.Config[0].Subnet.String(), "!", "-o", bridgeName, "-j", "SNAT", "--to-source", ipv4SNATAddr).Assert(t, icmd.Success)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -84,7 +84,7 @@ func TestDaemonDefaultNetworkPools(t *testing.T) {
|
||||
// Verify bridge network's subnet
|
||||
out, err := c.NetworkInspect(ctx, "bridge", client.NetworkInspectOptions{})
|
||||
assert.NilError(t, err)
|
||||
assert.Equal(t, out.IPAM.Config[0].Subnet, "175.30.0.0/16")
|
||||
assert.Equal(t, out.IPAM.Config[0].Subnet, netip.MustParsePrefix("175.30.0.0/16"))
|
||||
|
||||
// Create a bridge network and verify its subnet is the second default pool
|
||||
name := "elango" + t.Name()
|
||||
@@ -94,7 +94,7 @@ func TestDaemonDefaultNetworkPools(t *testing.T) {
|
||||
defer network.RemoveNoError(ctx, t, c, name)
|
||||
out, err = c.NetworkInspect(ctx, name, client.NetworkInspectOptions{})
|
||||
assert.NilError(t, err)
|
||||
assert.Check(t, is.Equal(out.IPAM.Config[0].Subnet, "175.33.0.0/24"))
|
||||
assert.Check(t, is.Equal(out.IPAM.Config[0].Subnet, netip.MustParsePrefix("175.33.0.0/24")))
|
||||
|
||||
// Create a bridge network and verify its subnet is the third default pool
|
||||
name = "saanvi" + t.Name()
|
||||
@@ -104,7 +104,7 @@ func TestDaemonDefaultNetworkPools(t *testing.T) {
|
||||
defer network.RemoveNoError(ctx, t, c, name)
|
||||
out, err = c.NetworkInspect(ctx, name, client.NetworkInspectOptions{})
|
||||
assert.NilError(t, err)
|
||||
assert.Check(t, is.Equal(out.IPAM.Config[0].Subnet, "175.33.1.0/24"))
|
||||
assert.Check(t, is.Equal(out.IPAM.Config[0].Subnet, netip.MustParsePrefix("175.33.1.0/24")))
|
||||
}
|
||||
|
||||
func TestDaemonRestartWithExistingNetwork(t *testing.T) {
|
||||
@@ -220,7 +220,7 @@ func TestDaemonWithBipAndDefaultNetworkPool(t *testing.T) {
|
||||
out, err := c.NetworkInspect(ctx, "bridge", client.NetworkInspectOptions{})
|
||||
assert.NilError(t, err)
|
||||
// Make sure BIP IP doesn't get override with new default address pool .
|
||||
assert.Equal(t, out.IPAM.Config[0].Subnet, "172.60.0.0/16")
|
||||
assert.Equal(t, out.IPAM.Config[0].Subnet, netip.MustParsePrefix("172.60.0.0/16"))
|
||||
}
|
||||
|
||||
func TestServiceWithPredefinedNetwork(t *testing.T) {
|
||||
@@ -451,13 +451,13 @@ func TestServiceWithDefaultAddressPoolInit(t *testing.T) {
|
||||
// pool (whereas before, the subnet for the ingress network was hard-coded.
|
||||
// This means that the ingress network gets the subnet 20.20.0.0/24, and
|
||||
// the network we just created gets subnet 20.20.1.0/24.
|
||||
assert.Equal(t, out.IPAM.Config[0].Subnet, "20.20.1.0/24")
|
||||
assert.Equal(t, out.IPAM.Config[0].Subnet, netip.MustParsePrefix("20.20.1.0/24"))
|
||||
|
||||
// Also inspect ingress network and make sure its in the same subnet
|
||||
out, err = cli.NetworkInspect(ctx, "ingress", client.NetworkInspectOptions{Verbose: true})
|
||||
assert.NilError(t, err)
|
||||
assert.Assert(t, len(out.IPAM.Config) > 0)
|
||||
assert.Equal(t, out.IPAM.Config[0].Subnet, "20.20.0.0/24")
|
||||
assert.Equal(t, out.IPAM.Config[0].Subnet, netip.MustParsePrefix("20.20.0.0/24"))
|
||||
|
||||
err = cli.ServiceRemove(ctx, serviceID)
|
||||
poll.WaitOn(t, noServices(ctx, cli), swarm.ServicePoll)
|
||||
|
||||
@@ -191,7 +191,7 @@ func TestBridgeICC(t *testing.T) {
|
||||
if pingHost == "" {
|
||||
if tc.isLinkLocal {
|
||||
inspect := container.Inspect(ctx, t, c, id1)
|
||||
pingHost = inspect.NetworkSettings.Networks[bridgeName].GlobalIPv6Address + "%eth0"
|
||||
pingHost = inspect.NetworkSettings.Networks[bridgeName].GlobalIPv6Address.WithZone("eth0").String()
|
||||
} else {
|
||||
pingHost = ctr1Name
|
||||
}
|
||||
@@ -329,7 +329,7 @@ func TestBridgeINC(t *testing.T) {
|
||||
targetAddr = ctr1Info.NetworkSettings.Networks[bridge1].GlobalIPv6Address
|
||||
}
|
||||
|
||||
pingCmd := []string{"ping", "-c1", "-W3", targetAddr}
|
||||
pingCmd := []string{"ping", "-c1", "-W3", targetAddr.String()}
|
||||
|
||||
ctr2Name := sanitizeCtrName(t.Name() + "-ctr2")
|
||||
attachCtx, cancel := context.WithTimeout(ctx, 5*time.Second)
|
||||
@@ -400,8 +400,8 @@ func TestBridgeINCRouted(t *testing.T) {
|
||||
insp := container.Inspect(ctx, t, c, ctrId)
|
||||
return ctrDesc{
|
||||
id: ctrId,
|
||||
ipv4: insp.NetworkSettings.Networks[netName].IPAddress,
|
||||
ipv6: insp.NetworkSettings.Networks[netName].GlobalIPv6Address,
|
||||
ipv4: insp.NetworkSettings.Networks[netName].IPAddress.String(),
|
||||
ipv6: insp.NetworkSettings.Networks[netName].GlobalIPv6Address.String(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -589,11 +589,11 @@ func TestAccessToPublishedPort(t *testing.T) {
|
||||
assert.NilError(t, err)
|
||||
for _, ipamCfg := range insp.IPAM.Config {
|
||||
ipv := "ipv4"
|
||||
if strings.Contains(ipamCfg.Gateway, ":") {
|
||||
if ipamCfg.Gateway.Is6() {
|
||||
ipv = "ipv6"
|
||||
}
|
||||
t.Run(ipv, func(t *testing.T) {
|
||||
url := "http://" + net.JoinHostPort(ipamCfg.Gateway, "8080")
|
||||
url := "http://" + net.JoinHostPort(ipamCfg.Gateway.String(), "8080")
|
||||
res := container.RunAttach(ctx, t, c,
|
||||
container.WithNetworkMode(clientNetName),
|
||||
container.WithCmd("wget", "-O-", "-T3", url),
|
||||
@@ -732,10 +732,10 @@ func TestInterNetworkDirectRouting(t *testing.T) {
|
||||
}
|
||||
}
|
||||
t.Run("w", func(t *testing.T) { // Wait for the parallel tests to complete.
|
||||
t.Run("ipv4/pub", checkHTTP(pub4, tc.expPubResp))
|
||||
t.Run("ipv6/pub", checkHTTP(pub6, tc.expPubResp))
|
||||
t.Run("ipv4/unpub", checkHTTP(unpub4, tc.expUnpubResp))
|
||||
t.Run("ipv6/unpub", checkHTTP(unpub6, tc.expUnpubResp))
|
||||
t.Run("ipv4/pub", checkHTTP(pub4.String(), tc.expPubResp))
|
||||
t.Run("ipv6/pub", checkHTTP(pub6.String(), tc.expPubResp))
|
||||
t.Run("ipv4/unpub", checkHTTP(unpub4.String(), tc.expUnpubResp))
|
||||
t.Run("ipv6/unpub", checkHTTP(unpub6.String(), tc.expUnpubResp))
|
||||
})
|
||||
})
|
||||
}
|
||||
@@ -796,7 +796,7 @@ func TestDefaultBridgeIPv6(t *testing.T) {
|
||||
defer cancel()
|
||||
res := container.RunAttach(attachCtx, t, c,
|
||||
container.WithImage("busybox:latest"),
|
||||
container.WithCmd("ping", "-c1", "-W3", gIPv6),
|
||||
container.WithCmd("ping", "-c1", "-W3", gIPv6.String()),
|
||||
)
|
||||
defer c.ContainerRemove(ctx, res.ContainerID, client.ContainerRemoveOptions{
|
||||
Force: true,
|
||||
@@ -1079,7 +1079,7 @@ func TestDisableIPv6OnInterface(t *testing.T) {
|
||||
|
||||
// Inspect should not show an IPv6 container address.
|
||||
inspRes2 := container.Inspect(ctx, t, c, ctrId)
|
||||
assert.Check(t, is.Equal("", inspRes2.NetworkSettings.Networks[tc.netName].GlobalIPv6Address))
|
||||
assert.Check(t, is.Equal(netip.Addr{}, inspRes2.NetworkSettings.Networks[tc.netName].GlobalIPv6Address))
|
||||
assert.Check(t, is.Equal(0, inspRes2.NetworkSettings.Networks[tc.netName].GlobalIPv6PrefixLen))
|
||||
|
||||
// Port mappings should be IPv4-only - but can't see the proxy processes in the rootless netns.
|
||||
@@ -1410,11 +1410,12 @@ func TestContainerDisabledIPv6(t *testing.T) {
|
||||
defer c.Close()
|
||||
|
||||
const netName = "ipv6br"
|
||||
subnet6 := netip.MustParsePrefix("fd64:40cd:7fb4:8971::/64")
|
||||
network.CreateNoError(ctx, t, c, netName,
|
||||
network.WithDriver("bridge"),
|
||||
network.WithOption(bridge.BridgeName, netName),
|
||||
network.WithIPv6(),
|
||||
network.WithIPAM("fd64:40cd:7fb4:8971::/64", "fd64:40cd:7fb4:8971::1"),
|
||||
network.WithIPAM(subnet6.String(), "fd64:40cd:7fb4:8971::1"),
|
||||
)
|
||||
defer network.RemoveNoError(ctx, t, c, netName)
|
||||
|
||||
@@ -1425,7 +1426,7 @@ func TestContainerDisabledIPv6(t *testing.T) {
|
||||
defer c.ContainerRemove(ctx, ctrWith6, client.ContainerRemoveOptions{Force: true})
|
||||
inspect := container.Inspect(ctx, t, c, ctrWith6)
|
||||
addr := inspect.NetworkSettings.Networks[netName].GlobalIPv6Address
|
||||
assert.Check(t, is.Contains(addr, "fd64:40cd:7fb4:8971"))
|
||||
assert.Check(t, subnet6.Contains(addr))
|
||||
|
||||
// Run a container with IPv6 disabled.
|
||||
const ctrNo6Name = "ctrNo6"
|
||||
@@ -1437,7 +1438,7 @@ func TestContainerDisabledIPv6(t *testing.T) {
|
||||
defer c.ContainerRemove(ctx, ctrNo6, client.ContainerRemoveOptions{Force: true})
|
||||
inspect = container.Inspect(ctx, t, c, ctrNo6)
|
||||
addr = inspect.NetworkSettings.Networks[netName].GlobalIPv6Address
|
||||
assert.Check(t, is.Equal(addr, ""))
|
||||
assert.Check(t, !addr.IsValid())
|
||||
|
||||
execCtx, cancel := context.WithTimeout(ctx, 5*time.Second)
|
||||
defer cancel()
|
||||
@@ -1574,7 +1575,7 @@ func checkProxies(ctx context.Context, t *testing.T, c *client.Client, daemonPid
|
||||
if e.ctrIPv4 {
|
||||
ctrIP = nw.IPAddress
|
||||
}
|
||||
wantProxies = append(wantProxies, makeExpStr(e.proto, e.hostIP, e.hostPort, ctrIP, e.ctrPort))
|
||||
wantProxies = append(wantProxies, makeExpStr(e.proto, e.hostIP, e.hostPort, ctrIP.String(), e.ctrPort))
|
||||
}
|
||||
|
||||
gotProxies := make([]string, 0, len(exp))
|
||||
@@ -1903,8 +1904,7 @@ func TestNetworkInspectGateway(t *testing.T) {
|
||||
insp, err := c.NetworkInspect(ctx, nid, client.NetworkInspectOptions{})
|
||||
assert.NilError(t, err)
|
||||
for _, ipamCfg := range insp.IPAM.Config {
|
||||
_, err := netip.ParseAddr(ipamCfg.Gateway)
|
||||
assert.Check(t, err)
|
||||
assert.Check(t, ipamCfg.Gateway.IsValid())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -98,9 +98,9 @@ ff02::2 ip6-allrouters
|
||||
// Append the container's own addresses/name to the expected hosts file content.
|
||||
inspect := container.Inspect(ctx, t, c, ctrId)
|
||||
bridgeEp := inspect.NetworkSettings.Networks["bridge"]
|
||||
exp := tc.expEtcHosts + bridgeEp.IPAddress + "\t" + inspect.Config.Hostname + "\n"
|
||||
exp := tc.expEtcHosts + bridgeEp.IPAddress.String() + "\t" + inspect.Config.Hostname + "\n"
|
||||
if tc.expIPv6Enabled {
|
||||
exp += bridgeEp.GlobalIPv6Address + "\t" + inspect.Config.Hostname + "\n"
|
||||
exp += bridgeEp.GlobalIPv6Address.String() + "\t" + inspect.Config.Hostname + "\n"
|
||||
}
|
||||
assert.Check(t, is.Equal(stdout, exp))
|
||||
})
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package networking
|
||||
|
||||
import (
|
||||
"net/netip"
|
||||
"testing"
|
||||
|
||||
"github.com/moby/moby/client"
|
||||
@@ -276,6 +277,6 @@ func TestWatchtowerCreate(t *testing.T) {
|
||||
// Check that the container got the expected addresses.
|
||||
inspect := container.Inspect(ctx, t, c, ctrName)
|
||||
netSettings := inspect.NetworkSettings.Networks[netName]
|
||||
assert.Check(t, is.Equal(netSettings.IPAddress, ctrIP))
|
||||
assert.Check(t, is.Equal(netSettings.IPAddress, netip.MustParseAddr(ctrIP)))
|
||||
assert.Check(t, is.Equal(netSettings.MacAddress, ctrMAC))
|
||||
}
|
||||
|
||||
@@ -716,8 +716,8 @@ func TestDirectRoutingOpenPorts(t *testing.T) {
|
||||
insp := container.Inspect(ctx, t, c, ctrId)
|
||||
return ctrDesc{
|
||||
id: ctrId,
|
||||
ipv4: insp.NetworkSettings.Networks[netName].IPAddress,
|
||||
ipv6: insp.NetworkSettings.Networks[netName].GlobalIPv6Address,
|
||||
ipv4: insp.NetworkSettings.Networks[netName].IPAddress.String(),
|
||||
ipv6: insp.NetworkSettings.Networks[netName].GlobalIPv6Address.String(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -894,16 +894,16 @@ func TestAcceptFwMark(t *testing.T) {
|
||||
test := func(name string, expPing int, expHttp string) {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
t.Run("v4/ping", func(t *testing.T) {
|
||||
testPing(t, "ping", ctrIPv4, expPing)
|
||||
testPing(t, "ping", ctrIPv4.String(), expPing)
|
||||
})
|
||||
t.Run("v6/ping", func(t *testing.T) {
|
||||
testPing(t, "ping6", ctrIPv6, expPing)
|
||||
testPing(t, "ping6", ctrIPv6.String(), expPing)
|
||||
})
|
||||
t.Run("v4/http", func(t *testing.T) {
|
||||
testHttp(t, ctrIPv4, "80", expHttp)
|
||||
testHttp(t, ctrIPv4.String(), "80", expHttp)
|
||||
})
|
||||
t.Run("v6/http", func(t *testing.T) {
|
||||
testHttp(t, ctrIPv6, "80", expHttp)
|
||||
testHttp(t, ctrIPv6.String(), "80", expHttp)
|
||||
})
|
||||
})
|
||||
}
|
||||
@@ -1029,25 +1029,25 @@ func TestRoutedNonGateway(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "nat/direct/v4",
|
||||
addr: insp.NetworkSettings.Networks[natNetName].IPAddress,
|
||||
addr: insp.NetworkSettings.Networks[natNetName].IPAddress.String(),
|
||||
port: "80",
|
||||
expHttp: httpFail,
|
||||
},
|
||||
{
|
||||
name: "nat/direct/v6",
|
||||
addr: insp.NetworkSettings.Networks[natNetName].GlobalIPv6Address,
|
||||
addr: insp.NetworkSettings.Networks[natNetName].GlobalIPv6Address.String(),
|
||||
port: "80",
|
||||
expHttp: httpFail,
|
||||
},
|
||||
{
|
||||
name: "routed/direct/v4",
|
||||
addr: insp.NetworkSettings.Networks[routedNetName].IPAddress,
|
||||
addr: insp.NetworkSettings.Networks[routedNetName].IPAddress.String(),
|
||||
port: "80",
|
||||
expHttp: httpSuccess,
|
||||
},
|
||||
{
|
||||
name: "routed/direct/v6",
|
||||
addr: insp.NetworkSettings.Networks[routedNetName].GlobalIPv6Address,
|
||||
addr: insp.NetworkSettings.Networks[routedNetName].GlobalIPv6Address.String(),
|
||||
port: "80",
|
||||
expHttp: httpSuccess,
|
||||
},
|
||||
@@ -1342,7 +1342,7 @@ func testDirectRemoteAccessOnExposedPort(t *testing.T, ctx context.Context, d *d
|
||||
}),
|
||||
container.WithNetworkMode(bridgeName),
|
||||
container.WithEndpointSettings(bridgeName, &networktypes.EndpointSettings{
|
||||
IPAddress: ctrIP.String(),
|
||||
IPAddress: ctrIP,
|
||||
IPPrefixLen: ctrIP.BitLen(),
|
||||
}))
|
||||
defer c.ContainerRemove(ctx, serverID, client.ContainerRemoveOptions{Force: true})
|
||||
|
||||
@@ -180,7 +180,7 @@ func TestInternalNetworkLocalDNS(t *testing.T) {
|
||||
// Query the internal network's DNS server (via the daemon's internal DNS server).
|
||||
res := container.RunAttach(ctx, t, c,
|
||||
container.WithNetworkMode(intNetName),
|
||||
container.WithDNS([]string{serverIP}),
|
||||
container.WithDNS([]string{serverIP.String()}),
|
||||
container.WithCmd("nslookup", "-type=A", "foo.example"),
|
||||
)
|
||||
defer c.ContainerRemove(ctx, res.ContainerID, client.ContainerRemoveOptions{Force: true})
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/google/go-cmp/cmp/cmpopts"
|
||||
"github.com/moby/moby/api/types/network"
|
||||
swarmtypes "github.com/moby/moby/api/types/swarm"
|
||||
"github.com/moby/moby/client"
|
||||
@@ -121,7 +122,7 @@ func TestDockerNetworkReConnect(t *testing.T) {
|
||||
|
||||
n2, err := apiClient.ContainerInspect(ctx, c1)
|
||||
assert.NilError(t, err)
|
||||
assert.Check(t, is.DeepEqual(n1, n2))
|
||||
assert.Check(t, is.DeepEqual(n1, n2, cmpopts.EquateComparable(netip.Addr{}, netip.Prefix{})))
|
||||
}
|
||||
|
||||
// Check that a swarm-scoped network can't have EnableIPv4=false.
|
||||
|
||||
@@ -53,3 +53,26 @@ func Chain2[K, V any](iters ...iter.Seq2[K, V]) iter.Seq2[K, V] {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Map applies a function to each element of the input sequence.
|
||||
func Map[T, U any](s iter.Seq[T], fn func(T) U) iter.Seq[U] {
|
||||
return func(yield func(U) bool) {
|
||||
for v := range s {
|
||||
if !yield(fn(v)) {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Map2 applies a function to each element of the input sequence.
|
||||
func Map2[K1, V1, K2, V2 any](s iter.Seq2[K1, V1], fn func(K1, V1) (K2, V2)) iter.Seq2[K2, V2] {
|
||||
return func(yield func(K2, V2) bool) {
|
||||
for k1, v1 := range s {
|
||||
k2, v2 := fn(k1, v1)
|
||||
if !yield(k2, v2) {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,8 @@ package iterutil
|
||||
import (
|
||||
"maps"
|
||||
"slices"
|
||||
"strconv"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"gotest.tools/v3/assert"
|
||||
@@ -66,3 +68,17 @@ func TestChain2(t *testing.T) {
|
||||
assert.DeepEqual(t, maps.Collect(ab), expab)
|
||||
assert.DeepEqual(t, maps.Collect(abc), expabc)
|
||||
}
|
||||
|
||||
func TestMap(t *testing.T) {
|
||||
a := []int{1, 2, 3}
|
||||
b := slices.Collect(Map(slices.Values(a), strconv.Itoa))
|
||||
assert.DeepEqual(t, b, []string{"1", "2", "3"})
|
||||
}
|
||||
|
||||
func TestMap2(t *testing.T) {
|
||||
a := map[string]int{"a": 1, "b": 2, "c": 3}
|
||||
b := maps.Collect(Map2(maps.All(a), func(k string, v int) (string, string) {
|
||||
return strings.ToUpper(k), strconv.Itoa(v)
|
||||
}))
|
||||
assert.DeepEqual(t, b, map[string]string{"A": "1", "B": "2", "C": "3"})
|
||||
}
|
||||
|
||||
15
vendor/github.com/moby/moby/api/types/network/endpoint.go
generated
vendored
15
vendor/github.com/moby/moby/api/types/network/endpoint.go
generated
vendored
@@ -2,6 +2,7 @@ package network
|
||||
|
||||
import (
|
||||
"maps"
|
||||
"net/netip"
|
||||
"slices"
|
||||
)
|
||||
|
||||
@@ -25,11 +26,11 @@ type EndpointSettings struct {
|
||||
// Operational data
|
||||
NetworkID string
|
||||
EndpointID string
|
||||
Gateway string
|
||||
IPAddress string
|
||||
Gateway netip.Addr
|
||||
IPAddress netip.Addr
|
||||
IPPrefixLen int
|
||||
IPv6Gateway string
|
||||
GlobalIPv6Address string
|
||||
IPv6Gateway netip.Addr
|
||||
GlobalIPv6Address netip.Addr
|
||||
GlobalIPv6PrefixLen int
|
||||
// DNSNames holds all the (non fully qualified) DNS names associated to this endpoint. First entry is used to
|
||||
// generate PTR records.
|
||||
@@ -54,9 +55,9 @@ func (es *EndpointSettings) Copy() *EndpointSettings {
|
||||
|
||||
// EndpointIPAMConfig represents IPAM configurations for the endpoint
|
||||
type EndpointIPAMConfig struct {
|
||||
IPv4Address string `json:",omitempty"`
|
||||
IPv6Address string `json:",omitempty"`
|
||||
LinkLocalIPs []string `json:",omitempty"`
|
||||
IPv4Address netip.Addr `json:",omitempty"`
|
||||
IPv6Address netip.Addr `json:",omitempty"`
|
||||
LinkLocalIPs []netip.Addr `json:",omitempty"`
|
||||
}
|
||||
|
||||
// Copy makes a copy of the endpoint ipam config
|
||||
|
||||
8
vendor/github.com/moby/moby/api/types/network/endpoint_resource.go
generated
vendored
8
vendor/github.com/moby/moby/api/types/network/endpoint_resource.go
generated
vendored
@@ -5,6 +5,10 @@ package network
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the swagger generate command
|
||||
|
||||
import (
|
||||
"net/netip"
|
||||
)
|
||||
|
||||
// EndpointResource contains network resources allocated and used for a container in a network.
|
||||
//
|
||||
// swagger:model EndpointResource
|
||||
@@ -24,8 +28,8 @@ type EndpointResource struct {
|
||||
|
||||
// IPv4 address
|
||||
// Example: 172.19.0.2/16
|
||||
IPv4Address string `json:"IPv4Address"`
|
||||
IPv4Address netip.Prefix `json:"IPv4Address"`
|
||||
|
||||
// IPv6 address
|
||||
IPv6Address string `json:"IPv6Address"`
|
||||
IPv6Address netip.Prefix `json:"IPv6Address"`
|
||||
}
|
||||
|
||||
8
vendor/github.com/moby/moby/api/types/network/ipam.go
generated
vendored
8
vendor/github.com/moby/moby/api/types/network/ipam.go
generated
vendored
@@ -13,10 +13,10 @@ type IPAM struct {
|
||||
|
||||
// IPAMConfig represents IPAM configurations
|
||||
type IPAMConfig struct {
|
||||
Subnet string `json:",omitempty"`
|
||||
IPRange string `json:",omitempty"`
|
||||
Gateway string `json:",omitempty"`
|
||||
AuxAddress map[string]string `json:"AuxiliaryAddresses,omitempty"`
|
||||
Subnet netip.Prefix `json:",omitempty"`
|
||||
IPRange netip.Prefix `json:",omitempty"`
|
||||
Gateway netip.Addr `json:",omitempty"`
|
||||
AuxAddress map[string]netip.Addr `json:"AuxiliaryAddresses,omitempty"`
|
||||
}
|
||||
|
||||
type SubnetStatuses = map[netip.Prefix]SubnetStatus
|
||||
|
||||
8
vendor/github.com/moby/moby/api/types/network/network_types.go
generated
vendored
8
vendor/github.com/moby/moby/api/types/network/network_types.go
generated
vendored
@@ -30,14 +30,6 @@ type CreateRequest struct {
|
||||
Labels map[string]string // Labels holds metadata specific to the network being created.
|
||||
}
|
||||
|
||||
// ServiceInfo represents service parameters with the list of service's tasks
|
||||
type ServiceInfo struct {
|
||||
VIP string
|
||||
Ports []string
|
||||
LocalLBIndex int
|
||||
Tasks []Task
|
||||
}
|
||||
|
||||
// NetworkingConfig represents the container's networking configuration for each of its interfaces
|
||||
// Carries the networking configs specified in the `docker run` and `docker network connect` commands
|
||||
type NetworkingConfig struct {
|
||||
|
||||
6
vendor/github.com/moby/moby/api/types/network/peer_info.go
generated
vendored
6
vendor/github.com/moby/moby/api/types/network/peer_info.go
generated
vendored
@@ -5,6 +5,10 @@ package network
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the swagger generate command
|
||||
|
||||
import (
|
||||
"net/netip"
|
||||
)
|
||||
|
||||
// PeerInfo represents one peer of an overlay network.
|
||||
//
|
||||
// swagger:model PeerInfo
|
||||
@@ -16,5 +20,5 @@ type PeerInfo struct {
|
||||
|
||||
// IP-address of the peer-node in the Swarm cluster.
|
||||
// Example: 10.133.77.91
|
||||
IP string `json:"IP"`
|
||||
IP netip.Addr `json:"IP"`
|
||||
}
|
||||
|
||||
28
vendor/github.com/moby/moby/api/types/network/service_info.go
generated
vendored
Normal file
28
vendor/github.com/moby/moby/api/types/network/service_info.go
generated
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
package network
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the swagger generate command
|
||||
|
||||
import (
|
||||
"net/netip"
|
||||
)
|
||||
|
||||
// ServiceInfo represents service parameters with the list of service's tasks
|
||||
//
|
||||
// swagger:model ServiceInfo
|
||||
type ServiceInfo struct {
|
||||
|
||||
// v IP
|
||||
VIP netip.Addr `json:"VIP"`
|
||||
|
||||
// ports
|
||||
Ports []string `json:"Ports"`
|
||||
|
||||
// local l b index
|
||||
LocalLBIndex int `json:"LocalLBIndex"`
|
||||
|
||||
// tasks
|
||||
Tasks []Task `json:"Tasks"`
|
||||
}
|
||||
6
vendor/github.com/moby/moby/api/types/network/task.go
generated
vendored
6
vendor/github.com/moby/moby/api/types/network/task.go
generated
vendored
@@ -5,6 +5,10 @@ package network
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the swagger generate command
|
||||
|
||||
import (
|
||||
"net/netip"
|
||||
)
|
||||
|
||||
// Task carries the information about one backend task
|
||||
//
|
||||
// swagger:model Task
|
||||
@@ -17,7 +21,7 @@ type Task struct {
|
||||
EndpointID string `json:"EndpointID"`
|
||||
|
||||
// endpoint IP
|
||||
EndpointIP string `json:"EndpointIP"`
|
||||
EndpointIP netip.Addr `json:"EndpointIP"`
|
||||
|
||||
// info
|
||||
Info map[string]string `json:"Info"`
|
||||
|
||||
Reference in New Issue
Block a user