Skip to content

Commit 9e1b42e

Browse files
committed
Add missing opts to --network advanced syntax
The new advanced --network syntax introduced in #1767 is lacking support for `link-local-ip` and `mac-address` fields. This commit adds both. Signed-off-by: Albin Kerouanton <albinker@gmail.com>
1 parent 58f4260 commit 9e1b42e

4 files changed

Lines changed: 92 additions & 9 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,
@@ -773,8 +779,7 @@ func parseNetworkOpts(copts *containerOptions) (map[string]*networktypes.Endpoin
773779
return endpoints, nil
774780
}
775781

776-
func applyContainerOptions(n *opts.NetworkAttachmentOpts, copts *containerOptions) error {
777-
// TODO should copts.MacAddress actually be set on the first network? (currently it's not)
782+
func applyContainerOptions(n *opts.NetworkAttachmentOpts, copts *containerOptions) error { //nolint:gocyclo
778783
// 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`)?
779784
if len(n.Aliases) > 0 && copts.aliases.Len() > 0 {
780785
return errdefs.InvalidParameter(errors.New("conflicting options: cannot specify both --network-alias and per-network alias"))
@@ -788,6 +793,12 @@ func applyContainerOptions(n *opts.NetworkAttachmentOpts, copts *containerOption
788793
if n.IPv6Address != "" && copts.ipv6Address != "" {
789794
return errdefs.InvalidParameter(errors.New("conflicting options: cannot specify both --ip6 and per-network IPv6 address"))
790795
}
796+
if n.MacAddress != "" && copts.macAddress != "" {
797+
return errdefs.InvalidParameter(errors.New("conflicting options: cannot specify both --mac-address and per-network MAC address"))
798+
}
799+
if len(n.LinkLocalIPs) > 0 && copts.linkLocalIPs.Len() > 0 {
800+
return errdefs.InvalidParameter(errors.New("conflicting options: cannot specify both --link-local-ip and per-network link-local IP addresses"))
801+
}
791802
if copts.aliases.Len() > 0 {
792803
n.Aliases = make([]string, copts.aliases.Len())
793804
copy(n.Aliases, copts.aliases.GetAll())
@@ -802,8 +813,9 @@ func applyContainerOptions(n *opts.NetworkAttachmentOpts, copts *containerOption
802813
if copts.ipv6Address != "" {
803814
n.IPv6Address = copts.ipv6Address
804815
}
805-
806-
// TODO should linkLocalIPs be added to the _first_ network only, or to _all_ networks? (should this be a per-network option as well?)
816+
if copts.macAddress != "" {
817+
n.MacAddress = copts.macAddress
818+
}
807819
if copts.linkLocalIPs.Len() > 0 {
808820
n.LinkLocalIPs = make([]string, copts.linkLocalIPs.Len())
809821
copy(n.LinkLocalIPs, copts.linkLocalIPs.GetAll())
@@ -840,6 +852,12 @@ func parseNetworkAttachmentOpt(ep opts.NetworkAttachmentOpts) (*networktypes.End
840852
LinkLocalIPs: ep.LinkLocalIPs,
841853
}
842854
}
855+
if ep.MacAddress != "" {
856+
if _, err := opts.ValidateMACAddress(ep.MacAddress); err != nil {
857+
return nil, errors.Errorf("%s is not a valid mac address", ep.MacAddress)
858+
}
859+
epConfig.MacAddress = ep.MacAddress
860+
}
843861
return epConfig, nil
844862
}
845863

cli/command/container/opts_test.go

Lines changed: 41 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -510,6 +510,7 @@ func TestParseNetworkConfig(t *testing.T) {
510510
name string
511511
flags []string
512512
expected map[string]*networktypes.EndpointSettings
513+
expectedCfg container.Config
513514
expectedHostCfg container.HostConfig
514515
expectedErr string
515516
}{
@@ -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
},
590598
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,9 +610,11 @@ 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
},
617+
expectedCfg: container.Config{MacAddress: "02:32:1c:23:00:04"},
608618
expectedHostCfg: container.HostConfig{NetworkMode: "net1"},
609619
},
610620
{
@@ -613,6 +623,18 @@ func TestParseNetworkConfig(t *testing.T) {
613623
expected: map[string]*networktypes.EndpointSettings{"net1": {}, "net2": {}},
614624
expectedHostCfg: container.HostConfig{NetworkMode: "net1"},
615625
},
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"},
637+
},
616638
{
617639
name: "conflict-network",
618640
flags: []string{"--network", "duplicate", "--network", "name=duplicate"},
@@ -638,18 +660,34 @@ 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)
690+
assert.DeepEqual(t, config.MacAddress, tc.expectedCfg.MacAddress)
653691
assert.DeepEqual(t, hConfig.NetworkMode, tc.expectedHostCfg.NetworkMode)
654692
assert.DeepEqual(t, nwConfig.EndpointsConfig, tc.expected)
655693
})

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)