diff --git a/cli/command/container/opts.go b/cli/command/container/opts.go index e4db20ee3e47..4b7700319d74 100644 --- a/cli/command/container/opts.go +++ b/cli/command/container/opts.go @@ -741,7 +741,6 @@ func parseNetworkOpts(copts *containerOptions) (map[string]*networktypes.Endpoin } func applyContainerOptions(n *opts.NetworkAttachmentOpts, copts *containerOptions) error { - // TODO should copts.MacAddress actually be set on the first network? (currently it's not) // TODO should we error if _any_ advanced option is used? (i.e. forbid to combine advanced notation with the "old" flags (`--network-alias`, `--link`, `--ip`, `--ip6`)? if len(n.Aliases) > 0 && copts.aliases.Len() > 0 { return errdefs.InvalidParameter(errors.New("conflicting options: cannot specify both --network-alias and per-network alias")) @@ -755,6 +754,12 @@ func applyContainerOptions(n *opts.NetworkAttachmentOpts, copts *containerOption if n.IPv6Address != "" && copts.ipv6Address != "" { return errdefs.InvalidParameter(errors.New("conflicting options: cannot specify both --ip6 and per-network IPv6 address")) } + if n.MacAddress != "" && copts.macAddress != "" { + return errdefs.InvalidParameter(errors.New("conflicting options: cannot specify both --mac-address and per-network MAC address")) + } + if len(n.LinkLocalIPs) > 0 && copts.linkLocalIPs.Len() > 0 { + return errdefs.InvalidParameter(errors.New("conflicting options: cannot specify both --link-local-ip and per-network link-local IP addresses")) + } if copts.aliases.Len() > 0 { n.Aliases = make([]string, copts.aliases.Len()) copy(n.Aliases, copts.aliases.GetAll()) @@ -769,8 +774,9 @@ func applyContainerOptions(n *opts.NetworkAttachmentOpts, copts *containerOption if copts.ipv6Address != "" { n.IPv6Address = copts.ipv6Address } - - // TODO should linkLocalIPs be added to the _first_ network only, or to _all_ networks? (should this be a per-network option as well?) + if copts.macAddress != "" { + n.MacAddress = copts.macAddress + } if copts.linkLocalIPs.Len() > 0 { n.LinkLocalIPs = make([]string, copts.linkLocalIPs.Len()) copy(n.LinkLocalIPs, copts.linkLocalIPs.GetAll()) @@ -807,6 +813,9 @@ func parseNetworkAttachmentOpt(ep opts.NetworkAttachmentOpts) (*networktypes.End LinkLocalIPs: ep.LinkLocalIPs, } } + if ep.MacAddress != "" { + epConfig.MacAddress = ep.MacAddress + } return epConfig, nil } diff --git a/cli/command/container/opts_test.go b/cli/command/container/opts_test.go index 6421f80a703c..a83550532add 100644 --- a/cli/command/container/opts_test.go +++ b/cli/command/container/opts_test.go @@ -513,6 +513,7 @@ func TestParseNetworkConfig(t *testing.T) { "--network-alias", "web2", "--network", "net2", "--network", "name=net3,alias=web3,driver-opt=field3=value3,ip=172.20.88.22,ip6=2001:db8::8822", + "--network", "name=net4,mac-address=02:32:1c:23:00:04,link-local-ip=169.254.169.254", }, expected: map[string]*networktypes.EndpointSettings{ "net1": { @@ -534,6 +535,12 @@ func TestParseNetworkConfig(t *testing.T) { }, Aliases: []string{"web3"}, }, + "net4": { + MacAddress: "02:32:1c:23:00:04", + IPAMConfig: &networktypes.EndpointIPAMConfig{ + LinkLocalIPs: []string{"169.254.169.254"}, + }, + }, }, expectedCfg: container.HostConfig{NetworkMode: "net1"}, }, @@ -586,6 +593,16 @@ func TestParseNetworkConfig(t *testing.T) { flags: []string{"--network", "name=host", "--network", "net1"}, expectedErr: `conflicting options: cannot attach both user-defined and non-user-defined network-modes`, }, + { + name: "conflict-options-link-local-ip", + flags: []string{"--network", "name=net1,link-local-ip=169.254.169.254", "--link-local-ip", "169.254.10.8"}, + expectedErr: `conflicting options: cannot specify both --link-local-ip and per-network link-local IP addresses`, + }, + { + name: "conflict-options-mac-address", + flags: []string{"--network", "name=net1,mac-address=02:32:1c:23:00:04", "--mac-address", "02:32:1c:23:00:04"}, + expectedErr: `conflicting options: cannot specify both --mac-address and per-network MAC address`, + }, } for _, tc := range tests { diff --git a/opts/network.go b/opts/network.go index 12c3977b1bc3..bb02adaa1ca2 100644 --- a/opts/network.go +++ b/opts/network.go @@ -12,6 +12,8 @@ const ( networkOptAlias = "alias" networkOptIPv4Address = "ip" networkOptIPv6Address = "ip6" + networkOptMacAddress = "mac-address" + networkOptLinkLocalIP = "link-local-ip" driverOpt = "driver-opt" ) @@ -23,7 +25,8 @@ type NetworkAttachmentOpts struct { Links []string // TODO add support for links in the csv notation of `--network` IPv4Address string IPv6Address string - LinkLocalIPs []string // TODO add support for LinkLocalIPs in the csv notation of `--network` ? + LinkLocalIPs []string + MacAddress string } // NetworkOpt represents a network config in swarm mode. @@ -66,6 +69,10 @@ func (n *NetworkOpt) Set(value string) error { netOpt.IPv4Address = val case networkOptIPv6Address: netOpt.IPv6Address = val + case networkOptMacAddress: + netOpt.MacAddress = val + case networkOptLinkLocalIP: + netOpt.LinkLocalIPs = append(netOpt.LinkLocalIPs, val) case driverOpt: key, val, err = parseDriverOpt(val) if err != nil { diff --git a/opts/network_test.go b/opts/network_test.go index ca595927e523..005554bfe34f 100644 --- a/opts/network_test.go +++ b/opts/network_test.go @@ -78,6 +78,16 @@ func TestNetworkOptAdvancedSyntax(t *testing.T) { }, }, }, + { + value: "name=docknet1,link-local-ip=169.254.169.254,link-local-ip=169.254.10.10", + expected: []NetworkAttachmentOpts{ + { + Target: "docknet1", + Aliases: []string{}, + LinkLocalIPs: []string{"169.254.169.254", "169.254.10.10"}, + }, + }, + }, } for _, tc := range testCases { tc := tc