Skip to content

Commit cd6467b

Browse files
authored
Merge pull request #4419 from akerouanton/missing-nw-advanced-options
Add missing opts to --network advanced syntax
2 parents 86329b6 + 9e1b42e commit cd6467b

4 files changed

Lines changed: 113 additions & 30 deletions

File tree

cli/command/container/opts.go

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -712,6 +712,12 @@ func parse(flags *pflag.FlagSet, copts *containerOptions, serverOS string) (*con
712712
return nil, err
713713
}
714714

715+
// Put the endpoint-specific MacAddress of the "main" network attachment into the container Config for backward
716+
// compatibility with older daemons.
717+
if nw, ok := networkingConfig.EndpointsConfig[hostConfig.NetworkMode.NetworkName()]; ok {
718+
config.MacAddress = nw.MacAddress
719+
}
720+
715721
return &containerConfig{
716722
Config: config,
717723
HostConfig: hostConfig,
@@ -787,8 +793,7 @@ func parseNetworkOpts(copts *containerOptions) (map[string]*networktypes.Endpoin
787793
return endpoints, nil
788794
}
789795

790-
func applyContainerOptions(n *opts.NetworkAttachmentOpts, copts *containerOptions) error {
791-
// TODO should copts.MacAddress actually be set on the first network? (currently it's not)
796+
func applyContainerOptions(n *opts.NetworkAttachmentOpts, copts *containerOptions) error { //nolint:gocyclo
792797
// 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`)?
793798
if len(n.Aliases) > 0 && copts.aliases.Len() > 0 {
794799
return errdefs.InvalidParameter(errors.New("conflicting options: cannot specify both --network-alias and per-network alias"))
@@ -802,6 +807,12 @@ func applyContainerOptions(n *opts.NetworkAttachmentOpts, copts *containerOption
802807
if n.IPv6Address != "" && copts.ipv6Address != "" {
803808
return errdefs.InvalidParameter(errors.New("conflicting options: cannot specify both --ip6 and per-network IPv6 address"))
804809
}
810+
if n.MacAddress != "" && copts.macAddress != "" {
811+
return errdefs.InvalidParameter(errors.New("conflicting options: cannot specify both --mac-address and per-network MAC address"))
812+
}
813+
if len(n.LinkLocalIPs) > 0 && copts.linkLocalIPs.Len() > 0 {
814+
return errdefs.InvalidParameter(errors.New("conflicting options: cannot specify both --link-local-ip and per-network link-local IP addresses"))
815+
}
805816
if copts.aliases.Len() > 0 {
806817
n.Aliases = make([]string, copts.aliases.Len())
807818
copy(n.Aliases, copts.aliases.GetAll())
@@ -816,8 +827,9 @@ func applyContainerOptions(n *opts.NetworkAttachmentOpts, copts *containerOption
816827
if copts.ipv6Address != "" {
817828
n.IPv6Address = copts.ipv6Address
818829
}
819-
820-
// TODO should linkLocalIPs be added to the _first_ network only, or to _all_ networks? (should this be a per-network option as well?)
830+
if copts.macAddress != "" {
831+
n.MacAddress = copts.macAddress
832+
}
821833
if copts.linkLocalIPs.Len() > 0 {
822834
n.LinkLocalIPs = make([]string, copts.linkLocalIPs.Len())
823835
copy(n.LinkLocalIPs, copts.linkLocalIPs.GetAll())
@@ -854,6 +866,12 @@ func parseNetworkAttachmentOpt(ep opts.NetworkAttachmentOpts) (*networktypes.End
854866
LinkLocalIPs: ep.LinkLocalIPs,
855867
}
856868
}
869+
if ep.MacAddress != "" {
870+
if _, err := opts.ValidateMACAddress(ep.MacAddress); err != nil {
871+
return nil, errors.Errorf("%s is not a valid mac address", ep.MacAddress)
872+
}
873+
epConfig.MacAddress = ep.MacAddress
874+
}
857875
return epConfig, nil
858876
}
859877

cli/command/container/opts_test.go

Lines changed: 62 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -507,23 +507,24 @@ func TestParseDevice(t *testing.T) {
507507

508508
func TestParseNetworkConfig(t *testing.T) {
509509
tests := []struct {
510-
name string
511-
flags []string
512-
expected map[string]*networktypes.EndpointSettings
513-
expectedCfg container.HostConfig
514-
expectedErr string
510+
name string
511+
flags []string
512+
expected map[string]*networktypes.EndpointSettings
513+
expectedCfg container.Config
514+
expectedHostCfg container.HostConfig
515+
expectedErr string
515516
}{
516517
{
517-
name: "single-network-legacy",
518-
flags: []string{"--network", "net1"},
519-
expected: map[string]*networktypes.EndpointSettings{},
520-
expectedCfg: container.HostConfig{NetworkMode: "net1"},
518+
name: "single-network-legacy",
519+
flags: []string{"--network", "net1"},
520+
expected: map[string]*networktypes.EndpointSettings{},
521+
expectedHostCfg: container.HostConfig{NetworkMode: "net1"},
521522
},
522523
{
523-
name: "single-network-advanced",
524-
flags: []string{"--network", "name=net1"},
525-
expected: map[string]*networktypes.EndpointSettings{},
526-
expectedCfg: container.HostConfig{NetworkMode: "net1"},
524+
name: "single-network-advanced",
525+
flags: []string{"--network", "name=net1"},
526+
expected: map[string]*networktypes.EndpointSettings{},
527+
expectedHostCfg: container.HostConfig{NetworkMode: "net1"},
527528
},
528529
{
529530
name: "single-network-legacy-with-options",
@@ -549,7 +550,7 @@ func TestParseNetworkConfig(t *testing.T) {
549550
Aliases: []string{"web1", "web2"},
550551
},
551552
},
552-
expectedCfg: container.HostConfig{NetworkMode: "net1"},
553+
expectedHostCfg: container.HostConfig{NetworkMode: "net1"},
553554
},
554555
{
555556
name: "multiple-network-advanced-mixed",
@@ -565,6 +566,7 @@ func TestParseNetworkConfig(t *testing.T) {
565566
"--network-alias", "web2",
566567
"--network", "net2",
567568
"--network", "name=net3,alias=web3,driver-opt=field3=value3,ip=172.20.88.22,ip6=2001:db8::8822",
569+
"--network", "name=net4,mac-address=02:32:1c:23:00:04,link-local-ip=169.254.169.254",
568570
},
569571
expected: map[string]*networktypes.EndpointSettings{
570572
"net1": {
@@ -586,12 +588,18 @@ func TestParseNetworkConfig(t *testing.T) {
586588
},
587589
Aliases: []string{"web3"},
588590
},
591+
"net4": {
592+
MacAddress: "02:32:1c:23:00:04",
593+
IPAMConfig: &networktypes.EndpointIPAMConfig{
594+
LinkLocalIPs: []string{"169.254.169.254"},
595+
},
596+
},
589597
},
590-
expectedCfg: container.HostConfig{NetworkMode: "net1"},
598+
expectedHostCfg: container.HostConfig{NetworkMode: "net1"},
591599
},
592600
{
593601
name: "single-network-advanced-with-options",
594-
flags: []string{"--network", "name=net1,alias=web1,alias=web2,driver-opt=field1=value1,driver-opt=field2=value2,ip=172.20.88.22,ip6=2001:db8::8822"},
602+
flags: []string{"--network", "name=net1,alias=web1,alias=web2,driver-opt=field1=value1,driver-opt=field2=value2,ip=172.20.88.22,ip6=2001:db8::8822,mac-address=02:32:1c:23:00:04"},
595603
expected: map[string]*networktypes.EndpointSettings{
596604
"net1": {
597605
DriverOpts: map[string]string{
@@ -602,16 +610,30 @@ func TestParseNetworkConfig(t *testing.T) {
602610
IPv4Address: "172.20.88.22",
603611
IPv6Address: "2001:db8::8822",
604612
},
605-
Aliases: []string{"web1", "web2"},
613+
Aliases: []string{"web1", "web2"},
614+
MacAddress: "02:32:1c:23:00:04",
606615
},
607616
},
608-
expectedCfg: container.HostConfig{NetworkMode: "net1"},
617+
expectedCfg: container.Config{MacAddress: "02:32:1c:23:00:04"},
618+
expectedHostCfg: container.HostConfig{NetworkMode: "net1"},
609619
},
610620
{
611-
name: "multiple-networks",
612-
flags: []string{"--network", "net1", "--network", "name=net2"},
613-
expected: map[string]*networktypes.EndpointSettings{"net1": {}, "net2": {}},
614-
expectedCfg: container.HostConfig{NetworkMode: "net1"},
621+
name: "multiple-networks",
622+
flags: []string{"--network", "net1", "--network", "name=net2"},
623+
expected: map[string]*networktypes.EndpointSettings{"net1": {}, "net2": {}},
624+
expectedHostCfg: container.HostConfig{NetworkMode: "net1"},
625+
},
626+
{
627+
name: "advanced-options-with-standalone-mac-address-flag",
628+
flags: []string{"--network=name=net1,alias=foobar", "--mac-address", "52:0f:f3:dc:50:10"},
629+
expected: map[string]*networktypes.EndpointSettings{
630+
"net1": {
631+
Aliases: []string{"foobar"},
632+
MacAddress: "52:0f:f3:dc:50:10",
633+
},
634+
},
635+
expectedCfg: container.Config{MacAddress: "52:0f:f3:dc:50:10"},
636+
expectedHostCfg: container.HostConfig{NetworkMode: "net1"},
615637
},
616638
{
617639
name: "conflict-network",
@@ -638,19 +660,35 @@ func TestParseNetworkConfig(t *testing.T) {
638660
flags: []string{"--network", "name=host", "--network", "net1"},
639661
expectedErr: `conflicting options: cannot attach both user-defined and non-user-defined network-modes`,
640662
},
663+
{
664+
name: "conflict-options-link-local-ip",
665+
flags: []string{"--network", "name=net1,link-local-ip=169.254.169.254", "--link-local-ip", "169.254.10.8"},
666+
expectedErr: `conflicting options: cannot specify both --link-local-ip and per-network link-local IP addresses`,
667+
},
668+
{
669+
name: "conflict-options-mac-address",
670+
flags: []string{"--network", "name=net1,mac-address=02:32:1c:23:00:04", "--mac-address", "02:32:1c:23:00:04"},
671+
expectedErr: `conflicting options: cannot specify both --mac-address and per-network MAC address`,
672+
},
673+
{
674+
name: "invalid-mac-address",
675+
flags: []string{"--network", "name=net1,mac-address=foobar"},
676+
expectedErr: "foobar is not a valid mac address",
677+
},
641678
}
642679

643680
for _, tc := range tests {
644681
t.Run(tc.name, func(t *testing.T) {
645-
_, hConfig, nwConfig, err := parseRun(tc.flags)
682+
config, hConfig, nwConfig, err := parseRun(tc.flags)
646683

647684
if tc.expectedErr != "" {
648685
assert.Error(t, err, tc.expectedErr)
649686
return
650687
}
651688

652689
assert.NilError(t, err)
653-
assert.DeepEqual(t, hConfig.NetworkMode, tc.expectedCfg.NetworkMode)
690+
assert.DeepEqual(t, config.MacAddress, tc.expectedCfg.MacAddress)
691+
assert.DeepEqual(t, hConfig.NetworkMode, tc.expectedHostCfg.NetworkMode)
654692
assert.DeepEqual(t, nwConfig.EndpointsConfig, tc.expected)
655693
})
656694
}

opts/network.go

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ const (
1212
networkOptAlias = "alias"
1313
networkOptIPv4Address = "ip"
1414
networkOptIPv6Address = "ip6"
15+
networkOptMacAddress = "mac-address"
16+
networkOptLinkLocalIP = "link-local-ip"
1517
driverOpt = "driver-opt"
1618
)
1719

@@ -23,7 +25,8 @@ type NetworkAttachmentOpts struct {
2325
Links []string // TODO add support for links in the csv notation of `--network`
2426
IPv4Address string
2527
IPv6Address string
26-
LinkLocalIPs []string // TODO add support for LinkLocalIPs in the csv notation of `--network` ?
28+
LinkLocalIPs []string
29+
MacAddress string
2730
}
2831

2932
// NetworkOpt represents a network config in swarm mode.
@@ -32,7 +35,7 @@ type NetworkOpt struct {
3235
}
3336

3437
// Set networkopts value
35-
func (n *NetworkOpt) Set(value string) error {
38+
func (n *NetworkOpt) Set(value string) error { //nolint:gocyclo
3639
longSyntax, err := regexp.MatchString(`\w+=\w+(,\w+=\w+)*`, value)
3740
if err != nil {
3841
return err
@@ -66,6 +69,10 @@ func (n *NetworkOpt) Set(value string) error {
6669
netOpt.IPv4Address = val
6770
case networkOptIPv6Address:
6871
netOpt.IPv6Address = val
72+
case networkOptMacAddress:
73+
netOpt.MacAddress = val
74+
case networkOptLinkLocalIP:
75+
netOpt.LinkLocalIPs = append(netOpt.LinkLocalIPs, val)
6976
case driverOpt:
7077
key, val, err = parseDriverOpt(val)
7178
if err != nil {

opts/network_test.go

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,26 @@ func TestNetworkOptAdvancedSyntax(t *testing.T) {
7878
},
7979
},
8080
},
81+
{
82+
value: "name=docknet1,mac-address=52:0f:f3:dc:50:10",
83+
expected: []NetworkAttachmentOpts{
84+
{
85+
Target: "docknet1",
86+
Aliases: []string{},
87+
MacAddress: "52:0f:f3:dc:50:10",
88+
},
89+
},
90+
},
91+
{
92+
value: "name=docknet1,link-local-ip=169.254.169.254,link-local-ip=169.254.10.10",
93+
expected: []NetworkAttachmentOpts{
94+
{
95+
Target: "docknet1",
96+
Aliases: []string{},
97+
LinkLocalIPs: []string{"169.254.169.254", "169.254.10.10"},
98+
},
99+
},
100+
},
81101
}
82102
for _, tc := range testCases {
83103
tc := tc

0 commit comments

Comments
 (0)