Skip to content

Commit

Permalink
localnet, tests: add e2e tests to share bridge mappings
Browse files Browse the repository at this point in the history
Adds an e2e tests that asserts the same bridge mapping can be shared
between multiple networks.

Signed-off-by: Miguel Duarte Barroso <[email protected]>
  • Loading branch information
maiqueb committed Oct 2, 2024
1 parent a565353 commit 5edf806
Show file tree
Hide file tree
Showing 3 changed files with 144 additions and 14 deletions.
37 changes: 37 additions & 0 deletions test/e2e/localnet-underlay.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ package e2e
import (
"context"
"fmt"
"os"
"os/exec"
"strings"

v1 "k8s.io/api/core/v1"
Expand Down Expand Up @@ -161,3 +163,38 @@ func bridgeMapping(physnet, ovsBridge string) BridgeMapping {
ovsBridge: ovsBridge,
}
}

func createVLANInterface(deviceName string, vlanID string, ipAddress *string) error {
vlanName := fmt.Sprintf("%s.%s", deviceName, vlanID)
cmd := exec.Command("sudo", "ip", "link", "add", "link", deviceName, "name", vlanName, "type", "vlan", "id", vlanID)
cmd.Stderr = os.Stderr
if err := cmd.Run(); err != nil {
return fmt.Errorf("failed to create vlan interface %s: %v", vlanName, err)
}

cmd = exec.Command("sudo", "ip", "link", "set", "dev", vlanName, "up")
cmd.Stderr = os.Stderr
if err := cmd.Run(); err != nil {
return fmt.Errorf("failed to enable vlan interface %s: %v", vlanName, err)
}

if ipAddress != nil {
cmd = exec.Command("sudo", "ip", "addr", "add", *ipAddress, "dev", vlanName)
cmd.Stderr = os.Stderr

if err := cmd.Run(); err != nil {
return fmt.Errorf("failed to define the vlan interface %q IP Address %s: %v", vlanName, *ipAddress, err)
}
}
return nil
}

func deleteVLANInterface(deviceName string, vlanID string) error {
vlanName := fmt.Sprintf("%s.%s", deviceName, vlanID)
cmd := exec.Command("sudo", "ip", "link", "del", deviceName)
cmd.Stderr = os.Stderr
if err := cmd.Run(); err != nil {
return fmt.Errorf("failed to create vlan interface %s: %v", vlanName, err)
}
return nil
}
100 changes: 95 additions & 5 deletions test/e2e/multihoming.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package e2e
import (
"context"
"fmt"
"net"
"os"
"os/exec"
"strconv"
Expand Down Expand Up @@ -656,6 +657,7 @@ var _ = Describe("Multi Homing", func() {
var underlayBridgeName string
var cmdWebServer *exec.Cmd

underlayIP := underlayServiceIP + "/24"
Context("with a service running on the underlay", func() {
BeforeEach(func() {
netConfig = newNetworkAttachmentConfig(
Expand Down Expand Up @@ -685,18 +687,17 @@ var _ = Describe("Multi Homing", func() {
underlayBridgeName, err = findInterfaceByIP(gatewayIP)
Expect(err).NotTo(HaveOccurred())

cmd := exec.Command("sudo", "ip", "addr", "add", underlayServiceIP+"/24", "dev", underlayBridgeName)
cmd := exec.Command("sudo", "ip", "addr", "add", underlayIP, "dev", underlayBridgeName)
cmd.Stderr = os.Stderr
err = cmd.Run()
Expect(err).NotTo(HaveOccurred())
})

BeforeEach(func() {
By("starting a service, connected to the underlay")
cmdWebServer = exec.Command("python3", "-m", "http.server", "--bind", underlayServiceIP, strconv.Itoa(servicePort))
cmdWebServer = exec.Command("python3", "-m", "http.server", "--bind", underlayServiceIP, strconv.Itoa(port))
cmdWebServer.Stderr = os.Stderr
err := cmdWebServer.Start()
Expect(err).NotTo(HaveOccurred(), "failed to create web server, port might be busy")
Expect(cmdWebServer.Start()).NotTo(HaveOccurred(), "failed to create web server, port might be busy")
})

BeforeEach(func() {
Expand All @@ -715,7 +716,7 @@ var _ = Describe("Multi Homing", func() {
})

AfterEach(func() {
cmd := exec.Command("sudo", "ip", "addr", "del", underlayServiceIP+"/24", "dev", underlayBridgeName)
cmd := exec.Command("sudo", "ip", "addr", "del", underlayIP, "dev", underlayBridgeName)
cmd.Stderr = os.Stderr
err := cmd.Run()
Expect(err).NotTo(HaveOccurred())
Expand Down Expand Up @@ -899,6 +900,83 @@ var _ = Describe("Multi Homing", func() {
)
})
})

Context("with a trunked configuration", func() {
const vlanID = 20
BeforeEach(func() {
nodes := ovsPods(cs)
Expect(nodes).NotTo(BeEmpty())

netConfig = newNetworkAttachmentConfig(
networkAttachmentConfigParams{
name: secondaryNetworkName,
namespace: f.Namespace.Name,
topology: "localnet",
cidr: secondaryLocalnetNetworkCIDR,
excludeCIDRs: []string{underlayServiceIP + "/32"},
})

By("setting up the localnet underlay with a trunked configuration")
Expect(setupUnderlay(nodes, secondaryInterfaceName, netConfig)).To(Succeed(), "configuring the OVS bridge")

By(fmt.Sprintf("creating a VLAN interface on top of the bridge connecting the cluster nodes with IP: %s", underlayIP))
cli, err := client.NewClientWithOpts(client.FromEnv)
Expect(err).NotTo(HaveOccurred())

gatewayIP, err := getNetworkGateway(cli, dockerNetworkName)
Expect(err).NotTo(HaveOccurred())

underlayBridgeName, err = findInterfaceByIP(gatewayIP)
Expect(err).NotTo(HaveOccurred())
Expect(createVLANInterface(underlayBridgeName, strconv.Itoa(vlanID), &underlayIP)).To(
Succeed(),
"create a VLAN interface on the bridge interconnecting the cluster nodes",
)

By("starting a service, connected to the underlay")
cmdWebServer = exec.Command("python3", "-m", "http.server", "--bind", underlayServiceIP, strconv.Itoa(port))
cmdWebServer.Stderr = os.Stderr
Expect(cmdWebServer.Start()).NotTo(HaveOccurred(), "failed to create web server, port might be busy")
})

AfterEach(func() {
Expect(cmdWebServer.Process.Kill()).NotTo(HaveOccurred(), "kill the python webserver")
Expect(deleteVLANInterface(underlayBridgeName, strconv.Itoa(vlanID))).NotTo(HaveOccurred(), "remove the underlay physical configuration")
Expect(teardownUnderlay(nodes)).To(Succeed(), "tear down the localnet underlay")
})

It("the same localnet network mapping can be shared on a separate VLAN by using the localnet alias attribute", func() {
const otherNetworkName = "different-network"
vlan20NetConfig := newNetworkAttachmentConfig(
networkAttachmentConfigParams{
name: otherNetworkName,
localnetPortNameAlias: netConfig.networkName,
namespace: f.Namespace.Name,
vlanID: vlanID,
topology: "localnet",
cidr: secondaryLocalnetNetworkCIDR,
excludeCIDRs: []string{underlayServiceIP + "/32"},
})

By("creating the attachment configuration for a separate VLAN")
_, err := nadClient.NetworkAttachmentDefinitions(f.Namespace.Name).Create(
context.Background(),
generateNAD(vlan20NetConfig),
metav1.CreateOptions{},
)
Expect(err).NotTo(HaveOccurred())

clientPodConfig := podConfiguration{
name: clientPodName,
namespace: f.Namespace.Name,
attachments: []nadapi.NetworkSelectionElement{{Name: otherNetworkName}},
}
kickstartPod(cs, clientPodConfig)

By(fmt.Sprintf("asserting the *client* pod can contact the underlay service with IP %q on the separate vlan", underlayIP))
Expect(connectToServer(clientPodConfig, underlayServiceIP, servicePort)).To(Succeed())
})
})
})

Context("multi-network policies", func() {
Expand Down Expand Up @@ -1666,3 +1744,15 @@ func createMultiNetworkPolicy(mnpClient mnpclient.K8sCniCncfIoV1beta1Interface,
)
return err
}

func MustParseIPNet(cidrStr string) *net.IPNet {
ip, ipNet, err := net.ParseCIDR(cidrStr)
if err != nil {
panic(fmt.Sprintf("Could not parse %q as a CIDR: %v", cidrStr, err))
}

if !ipNet.IP.Equal(ip) {
ipNet.IP = ip
}
return ipNet
}
21 changes: 12 additions & 9 deletions test/e2e/multihoming_utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,15 +52,16 @@ func getNetCIDRSubnet(netCIDR string) (string, error) {
}

type networkAttachmentConfigParams struct {
cidr string
excludeCIDRs []string
namespace string
name string
topology string
networkName string
vlanID int
allowPersistentIPs bool
role string
cidr string
excludeCIDRs []string
namespace string
name string
topology string
networkName string
vlanID int
allowPersistentIPs bool
role string
localnetPortNameAlias string
}

type networkAttachmentConfig struct {
Expand Down Expand Up @@ -96,6 +97,7 @@ func generateNAD(config networkAttachmentConfig) *nadapi.NetworkAttachmentDefini
"netAttachDefName": %q,
"vlanID": %d,
"allowPersistentIPs": %t,
"localnetPortAlias": %q,
"role": %q
}
`,
Expand All @@ -106,6 +108,7 @@ func generateNAD(config networkAttachmentConfig) *nadapi.NetworkAttachmentDefini
namespacedName(config.namespace, config.name),
config.vlanID,
config.allowPersistentIPs,
config.localnetPortNameAlias,
config.role,
)
return generateNetAttachDef(config.namespace, config.name, nadSpec)
Expand Down

0 comments on commit 5edf806

Please sign in to comment.