bridge: Enable disabling bridge interface
The new `disableContainerInterface` parameter is added to the bridge plugin to enable setting the container interface state down. When the parameter is enabled, the container interface (veth peer that is placed at the container ns) remain down (i.e: disabled). The bridge and host peer interfaces state are not affected by the parameter. Since IPAM logic involve various configurations including waiting for addresses to be realized and setting the interface state UP, the new parameter cannot work with IPAM. In case both IPAM and DisableContainerInterface parameters are set, the bridge plugin will raise an error. Signed-off-by: Or Mergi <ormergi@redhat.com>
This commit is contained in:
parent
b6a0e0bc96
commit
7e131a0076
@ -47,19 +47,20 @@ const defaultBrName = "cni0"
|
|||||||
|
|
||||||
type NetConf struct {
|
type NetConf struct {
|
||||||
types.NetConf
|
types.NetConf
|
||||||
BrName string `json:"bridge"`
|
BrName string `json:"bridge"`
|
||||||
IsGW bool `json:"isGateway"`
|
IsGW bool `json:"isGateway"`
|
||||||
IsDefaultGW bool `json:"isDefaultGateway"`
|
IsDefaultGW bool `json:"isDefaultGateway"`
|
||||||
ForceAddress bool `json:"forceAddress"`
|
ForceAddress bool `json:"forceAddress"`
|
||||||
IPMasq bool `json:"ipMasq"`
|
IPMasq bool `json:"ipMasq"`
|
||||||
MTU int `json:"mtu"`
|
MTU int `json:"mtu"`
|
||||||
HairpinMode bool `json:"hairpinMode"`
|
HairpinMode bool `json:"hairpinMode"`
|
||||||
PromiscMode bool `json:"promiscMode"`
|
PromiscMode bool `json:"promiscMode"`
|
||||||
Vlan int `json:"vlan"`
|
Vlan int `json:"vlan"`
|
||||||
VlanTrunk []*VlanTrunk `json:"vlanTrunk,omitempty"`
|
VlanTrunk []*VlanTrunk `json:"vlanTrunk,omitempty"`
|
||||||
PreserveDefaultVlan bool `json:"preserveDefaultVlan"`
|
PreserveDefaultVlan bool `json:"preserveDefaultVlan"`
|
||||||
MacSpoofChk bool `json:"macspoofchk,omitempty"`
|
MacSpoofChk bool `json:"macspoofchk,omitempty"`
|
||||||
EnableDad bool `json:"enabledad,omitempty"`
|
EnableDad bool `json:"enabledad,omitempty"`
|
||||||
|
DisableContainerInterface bool `json:"disableContainerInterface,omitempty"`
|
||||||
|
|
||||||
Args struct {
|
Args struct {
|
||||||
Cni BridgeArgs `json:"cni,omitempty"`
|
Cni BridgeArgs `json:"cni,omitempty"`
|
||||||
@ -530,6 +531,10 @@ func cmdAdd(args *skel.CmdArgs) error {
|
|||||||
|
|
||||||
isLayer3 := n.IPAM.Type != ""
|
isLayer3 := n.IPAM.Type != ""
|
||||||
|
|
||||||
|
if isLayer3 && n.DisableContainerInterface {
|
||||||
|
return fmt.Errorf("cannot use IPAM when DisableContainerInterface flag is set")
|
||||||
|
}
|
||||||
|
|
||||||
if n.IsDefaultGW {
|
if n.IsDefaultGW {
|
||||||
n.IsGW = true
|
n.IsGW = true
|
||||||
}
|
}
|
||||||
@ -676,12 +681,13 @@ func cmdAdd(args *skel.CmdArgs) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else if !n.DisableContainerInterface {
|
||||||
if err := netns.Do(func(_ ns.NetNS) error {
|
if err := netns.Do(func(_ ns.NetNS) error {
|
||||||
link, err := netlink.LinkByName(args.IfName)
|
link, err := netlink.LinkByName(args.IfName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to retrieve link: %v", err)
|
return fmt.Errorf("failed to retrieve link: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// If layer 2 we still need to set the container veth to up
|
// If layer 2 we still need to set the container veth to up
|
||||||
if err = netlink.LinkSetUp(link); err != nil {
|
if err = netlink.LinkSetUp(link); err != nil {
|
||||||
return fmt.Errorf("failed to set %q up: %v", args.IfName, err)
|
return fmt.Errorf("failed to set %q up: %v", args.IfName, err)
|
||||||
@ -692,23 +698,28 @@ func cmdAdd(args *skel.CmdArgs) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var hostVeth netlink.Link
|
hostVeth, err := netlink.LinkByName(hostInterface.Name)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
// check bridge port state
|
if !n.DisableContainerInterface {
|
||||||
retries := []int{0, 50, 500, 1000, 1000}
|
// check bridge port state
|
||||||
for idx, sleep := range retries {
|
retries := []int{0, 50, 500, 1000, 1000}
|
||||||
time.Sleep(time.Duration(sleep) * time.Millisecond)
|
for idx, sleep := range retries {
|
||||||
|
time.Sleep(time.Duration(sleep) * time.Millisecond)
|
||||||
|
|
||||||
hostVeth, err = netlink.LinkByName(hostInterface.Name)
|
hostVeth, err = netlink.LinkByName(hostInterface.Name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if hostVeth.Attrs().OperState == netlink.OperUp {
|
if hostVeth.Attrs().OperState == netlink.OperUp {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
if idx == len(retries)-1 {
|
if idx == len(retries)-1 {
|
||||||
return fmt.Errorf("bridge port in error state: %s", hostVeth.Attrs().OperState)
|
return fmt.Errorf("bridge port in error state: %s", hostVeth.Attrs().OperState)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -78,10 +78,12 @@ type testCase struct {
|
|||||||
removeDefaultVlan bool
|
removeDefaultVlan bool
|
||||||
ipMasq bool
|
ipMasq bool
|
||||||
macspoofchk bool
|
macspoofchk bool
|
||||||
AddErr020 string
|
disableContIface bool
|
||||||
DelErr020 string
|
|
||||||
AddErr010 string
|
AddErr020 string
|
||||||
DelErr010 string
|
DelErr020 string
|
||||||
|
AddErr010 string
|
||||||
|
DelErr010 string
|
||||||
|
|
||||||
envArgs string // CNI_ARGS
|
envArgs string // CNI_ARGS
|
||||||
runtimeConfig struct {
|
runtimeConfig struct {
|
||||||
@ -154,6 +156,9 @@ const (
|
|||||||
netDefault = `,
|
netDefault = `,
|
||||||
"isDefaultGateway": true`
|
"isDefaultGateway": true`
|
||||||
|
|
||||||
|
disableContainerInterface = `,
|
||||||
|
"disableContainerInterface": true`
|
||||||
|
|
||||||
ipamStartStr = `,
|
ipamStartStr = `,
|
||||||
"ipam": {
|
"ipam": {
|
||||||
"type": "host-local"`
|
"type": "host-local"`
|
||||||
@ -248,6 +253,10 @@ func (tc testCase) netConfJSON(dataDir string) string {
|
|||||||
conf += fmt.Sprintf(macspoofchkFormat, tc.macspoofchk)
|
conf += fmt.Sprintf(macspoofchkFormat, tc.macspoofchk)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if tc.disableContIface {
|
||||||
|
conf += disableContainerInterface
|
||||||
|
}
|
||||||
|
|
||||||
if !tc.isLayer2 {
|
if !tc.isLayer2 {
|
||||||
conf += netDefault
|
conf += netDefault
|
||||||
if tc.subnet != "" || tc.ranges != nil {
|
if tc.subnet != "" || tc.ranges != nil {
|
||||||
@ -677,14 +686,16 @@ func (tester *testerV10x) cmdAddTest(tc testCase, dataDir string) (types.Result,
|
|||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
Expect(link.Attrs().Name).To(Equal(IFNAME))
|
Expect(link.Attrs().Name).To(Equal(IFNAME))
|
||||||
Expect(link).To(BeAssignableToTypeOf(&netlink.Veth{}))
|
Expect(link).To(BeAssignableToTypeOf(&netlink.Veth{}))
|
||||||
|
assertContainerInterfaceLinkState(&tc, link)
|
||||||
|
|
||||||
expCIDRsV4, expCIDRsV6 := tc.expectedCIDRs()
|
expCIDRsV4, expCIDRsV6 := tc.expectedCIDRs()
|
||||||
addrs, err := netlink.AddrList(link, netlink.FAMILY_V4)
|
addrs, err := netlink.AddrList(link, netlink.FAMILY_V4)
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
Expect(addrs).To(HaveLen(len(expCIDRsV4)))
|
Expect(addrs).To(HaveLen(len(expCIDRsV4)))
|
||||||
addrs, err = netlink.AddrList(link, netlink.FAMILY_V6)
|
addrs, err = netlink.AddrList(link, netlink.FAMILY_V6)
|
||||||
Expect(addrs).To(HaveLen(len(expCIDRsV6) + 1)) // add one for the link-local
|
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
assertIPv6Addresses(&tc, addrs, expCIDRsV6)
|
||||||
|
|
||||||
// Ignore link local address which may or may not be
|
// Ignore link local address which may or may not be
|
||||||
// ready when we read addresses.
|
// ready when we read addresses.
|
||||||
var foundAddrs int
|
var foundAddrs int
|
||||||
@ -728,6 +739,15 @@ func (tester *testerV10x) cmdAddTest(tc testCase, dataDir string) (types.Result,
|
|||||||
return result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func assertContainerInterfaceLinkState(tc *testCase, link netlink.Link) {
|
||||||
|
linkState := int(link.Attrs().OperState)
|
||||||
|
if tc.disableContIface {
|
||||||
|
Expect(linkState).ToNot(Equal(netlink.OperUp))
|
||||||
|
} else {
|
||||||
|
Expect(linkState).To(Equal(netlink.OperUp))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (tester *testerV10x) cmdCheckTest(tc testCase, conf *Net, _ string) {
|
func (tester *testerV10x) cmdCheckTest(tc testCase, conf *Net, _ string) {
|
||||||
// Generate network config and command arguments
|
// Generate network config and command arguments
|
||||||
tester.args = tc.createCheckCmdArgs(tester.targetNS, conf)
|
tester.args = tc.createCheckCmdArgs(tester.targetNS, conf)
|
||||||
@ -1008,8 +1028,9 @@ func (tester *testerV04x) cmdAddTest(tc testCase, dataDir string) (types.Result,
|
|||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
Expect(addrs).To(HaveLen(len(expCIDRsV4)))
|
Expect(addrs).To(HaveLen(len(expCIDRsV4)))
|
||||||
addrs, err = netlink.AddrList(link, netlink.FAMILY_V6)
|
addrs, err = netlink.AddrList(link, netlink.FAMILY_V6)
|
||||||
Expect(addrs).To(HaveLen(len(expCIDRsV6) + 1)) // add one for the link-local
|
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
assertIPv6Addresses(&tc, addrs, expCIDRsV6)
|
||||||
|
|
||||||
// Ignore link local address which may or may not be
|
// Ignore link local address which may or may not be
|
||||||
// ready when we read addresses.
|
// ready when we read addresses.
|
||||||
var foundAddrs int
|
var foundAddrs int
|
||||||
@ -1053,6 +1074,14 @@ func (tester *testerV04x) cmdAddTest(tc testCase, dataDir string) (types.Result,
|
|||||||
return result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func assertIPv6Addresses(tc *testCase, addrs []netlink.Addr, expCIDRsV6 []*net.IPNet) {
|
||||||
|
if tc.disableContIface {
|
||||||
|
Expect(addrs).To(BeEmpty())
|
||||||
|
} else {
|
||||||
|
Expect(addrs).To(HaveLen(len(expCIDRsV6) + 1)) // add one for the link-local
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (tester *testerV04x) cmdCheckTest(tc testCase, conf *Net, _ string) {
|
func (tester *testerV04x) cmdCheckTest(tc testCase, conf *Net, _ string) {
|
||||||
// Generate network config and command arguments
|
// Generate network config and command arguments
|
||||||
tester.args = tc.createCheckCmdArgs(tester.targetNS, conf)
|
tester.args = tc.createCheckCmdArgs(tester.targetNS, conf)
|
||||||
@ -2461,6 +2490,36 @@ var _ = Describe("bridge Operations", func() {
|
|||||||
return nil
|
return nil
|
||||||
})).To(Succeed())
|
})).To(Succeed())
|
||||||
})
|
})
|
||||||
|
|
||||||
|
It(fmt.Sprintf("[%s] should fail when both IPAM and DisableContainerInterface are set", ver), func() {
|
||||||
|
Expect(originalNS.Do(func(ns.NetNS) error {
|
||||||
|
defer GinkgoRecover()
|
||||||
|
tc := testCase{
|
||||||
|
cniVersion: ver,
|
||||||
|
subnet: "10.1.2.0/24",
|
||||||
|
disableContIface: true,
|
||||||
|
}
|
||||||
|
args := tc.createCmdArgs(targetNS, dataDir)
|
||||||
|
Expect(cmdAdd(args)).To(HaveOccurred())
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})).To(Succeed())
|
||||||
|
})
|
||||||
|
|
||||||
|
It(fmt.Sprintf("[%s] should set the container veth peer state down", ver), func() {
|
||||||
|
Expect(originalNS.Do(func(ns.NetNS) error {
|
||||||
|
defer GinkgoRecover()
|
||||||
|
tc := testCase{
|
||||||
|
cniVersion: ver,
|
||||||
|
disableContIface: true,
|
||||||
|
isLayer2: true,
|
||||||
|
AddErr020: "cannot convert: no valid IP addresses",
|
||||||
|
AddErr010: "cannot convert: no valid IP addresses",
|
||||||
|
}
|
||||||
|
cmdAddDelTest(originalNS, targetNS, tc, dataDir)
|
||||||
|
return nil
|
||||||
|
})).To(Succeed())
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
It("check vlan id when loading net conf", func() {
|
It("check vlan id when loading net conf", func() {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user