containernetworking-plugins/pkg/ip/link_linux_test.go
Matthieu MOREL d12b81dec5 ci(lint): setup golangci-lint
Signed-off-by: Matthieu MOREL <matthieu.morel35@gmail.com>
2023-03-01 06:55:40 +00:00

291 lines
7.8 KiB
Go

// Copyright 2016 CNI authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package ip_test
import (
"bytes"
"crypto/rand"
"fmt"
"net"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
"github.com/vishvananda/netlink"
"github.com/containernetworking/plugins/pkg/ip"
"github.com/containernetworking/plugins/pkg/ns"
"github.com/containernetworking/plugins/pkg/testutils"
)
var _ = Describe("Link", func() {
const (
ifaceFormatString string = "i%d"
mtu int = 1400
ip4onehwaddr = "0a:58:01:01:01:01"
)
var (
hostNetNS ns.NetNS
containerNetNS ns.NetNS
ifaceCounter int = 0
hostVeth net.Interface
containerVeth net.Interface
hostVethName string
containerVethName string
originalRandReader = rand.Reader
)
BeforeEach(func() {
var err error
hostNetNS, err = testutils.NewNS()
Expect(err).NotTo(HaveOccurred())
containerNetNS, err = testutils.NewNS()
Expect(err).NotTo(HaveOccurred())
fakeBytes := make([]byte, 20)
// to be reset in AfterEach block
rand.Reader = bytes.NewReader(fakeBytes)
_ = containerNetNS.Do(func(ns.NetNS) error {
defer GinkgoRecover()
hostVeth, containerVeth, err = ip.SetupVeth(fmt.Sprintf(ifaceFormatString, ifaceCounter), mtu, "", hostNetNS)
if err != nil {
return err
}
Expect(err).NotTo(HaveOccurred())
hostVethName = hostVeth.Name
containerVethName = containerVeth.Name
return nil
})
})
AfterEach(func() {
Expect(containerNetNS.Close()).To(Succeed())
Expect(hostNetNS.Close()).To(Succeed())
ifaceCounter++
rand.Reader = originalRandReader
})
Describe("GetVethPeerIfindex", func() {
It("returns the link and peer index of the named interface", func() {
By("looking up the container veth index using the host veth name")
_ = hostNetNS.Do(func(ns.NetNS) error {
defer GinkgoRecover()
gotHostLink, gotContainerIndex, err := ip.GetVethPeerIfindex(hostVethName)
Expect(err).NotTo(HaveOccurred())
By("checking we got back the host link")
attrs := gotHostLink.Attrs()
Expect(attrs.Index).To(Equal(hostVeth.Index))
Expect(attrs.Name).To(Equal(hostVeth.Name))
By("checking we got back the container veth index")
Expect(gotContainerIndex).To(Equal(containerVeth.Index))
return nil
})
By("looking up the host veth index using the container veth name")
_ = containerNetNS.Do(func(ns.NetNS) error {
defer GinkgoRecover()
gotContainerLink, gotHostIndex, err := ip.GetVethPeerIfindex(containerVethName)
Expect(err).NotTo(HaveOccurred())
By("checking we got back the container link")
attrs := gotContainerLink.Attrs()
Expect(attrs.Index).To(Equal(containerVeth.Index))
Expect(attrs.Name).To(Equal(containerVeth.Name))
By("checking we got back the host veth index")
Expect(gotHostIndex).To(Equal(hostVeth.Index))
return nil
})
})
})
It("SetupVeth must put the veth endpoints into the separate namespaces", func() {
_ = containerNetNS.Do(func(ns.NetNS) error {
defer GinkgoRecover()
containerVethFromName, err := netlink.LinkByName(containerVethName)
Expect(err).NotTo(HaveOccurred())
Expect(containerVethFromName.Attrs().Index).To(Equal(containerVeth.Index))
return nil
})
_ = hostNetNS.Do(func(ns.NetNS) error {
defer GinkgoRecover()
hostVethFromName, err := netlink.LinkByName(hostVethName)
Expect(err).NotTo(HaveOccurred())
Expect(hostVethFromName.Attrs().Index).To(Equal(hostVeth.Index))
return nil
})
})
Context("when container already has an interface with the same name", func() {
It("returns useful error", func() {
_ = containerNetNS.Do(func(ns.NetNS) error {
defer GinkgoRecover()
_, _, err := ip.SetupVeth(containerVethName, mtu, "", hostNetNS)
Expect(err.Error()).To(Equal(fmt.Sprintf("container veth name provided (%s) already exists", containerVethName)))
return nil
})
})
})
Context("deleting an non-existent device", func() {
It("returns known error", func() {
_ = containerNetNS.Do(func(ns.NetNS) error {
defer GinkgoRecover()
// This string should match the expected error codes in the cmdDel functions of some of the plugins
_, err := ip.DelLinkByNameAddr("THIS_DONT_EXIST")
Expect(err).To(Equal(ip.ErrLinkNotFound))
return nil
})
})
})
Context("when there is no name available for the host-side", func() {
BeforeEach(func() {
// adding different interface to container ns
containerVethName += "0"
})
It("returns useful error", func() {
_ = containerNetNS.Do(func(ns.NetNS) error {
defer GinkgoRecover()
_, _, err := ip.SetupVeth(containerVethName, mtu, "", hostNetNS)
Expect(err.Error()).To(HavePrefix("container veth name provided"))
Expect(err.Error()).To(HaveSuffix("already exists"))
return nil
})
})
})
Context("when there is no name conflict for the host or container interfaces", func() {
BeforeEach(func() {
// adding different interface to container and host ns
containerVethName += "0"
rand.Reader = originalRandReader
})
It("successfully creates the second veth pair", func() {
_ = containerNetNS.Do(func(ns.NetNS) error {
defer GinkgoRecover()
hostVeth, _, err := ip.SetupVeth(containerVethName, mtu, "", hostNetNS)
Expect(err).NotTo(HaveOccurred())
hostVethName = hostVeth.Name
return nil
})
// verify veths are in different namespaces
_ = containerNetNS.Do(func(ns.NetNS) error {
defer GinkgoRecover()
_, err := netlink.LinkByName(containerVethName)
Expect(err).NotTo(HaveOccurred())
return nil
})
_ = hostNetNS.Do(func(ns.NetNS) error {
defer GinkgoRecover()
_, err := netlink.LinkByName(hostVethName)
Expect(err).NotTo(HaveOccurred())
return nil
})
})
It("successfully creates a veth pair with an explicit mac", func() {
const mac = "02:00:00:00:01:23"
_ = containerNetNS.Do(func(ns.NetNS) error {
defer GinkgoRecover()
hostVeth, _, err := ip.SetupVeth(containerVethName, mtu, mac, hostNetNS)
Expect(err).NotTo(HaveOccurred())
hostVethName = hostVeth.Name
link, err := netlink.LinkByName(containerVethName)
Expect(err).NotTo(HaveOccurred())
Expect(link.Attrs().HardwareAddr.String()).To(Equal(mac))
return nil
})
_ = hostNetNS.Do(func(ns.NetNS) error {
defer GinkgoRecover()
link, err := netlink.LinkByName(hostVethName)
Expect(err).NotTo(HaveOccurred())
Expect(link.Attrs().HardwareAddr.String()).NotTo(Equal(mac))
return nil
})
})
})
It("DelLinkByName must delete the veth endpoints", func() {
_ = containerNetNS.Do(func(ns.NetNS) error {
defer GinkgoRecover()
// this will delete the host endpoint too
err := ip.DelLinkByName(containerVethName)
Expect(err).NotTo(HaveOccurred())
_, err = netlink.LinkByName(containerVethName)
Expect(err).To(HaveOccurred())
return nil
})
_ = hostNetNS.Do(func(ns.NetNS) error {
defer GinkgoRecover()
_, err := netlink.LinkByName(hostVethName)
Expect(err).To(HaveOccurred())
return nil
})
})
It("DelLinkByNameAddr should return no IPs when no IPs are configured", func() {
_ = containerNetNS.Do(func(ns.NetNS) error {
defer GinkgoRecover()
// this will delete the host endpoint too
addr, err := ip.DelLinkByNameAddr(containerVethName)
Expect(err).NotTo(HaveOccurred())
Expect(addr).To(HaveLen(0))
return nil
})
})
})