Skip to content

Commit 7521b14

Browse files
authored
Merge pull request #455 from shapeblue/testsForMultiNicSupport
Added e2e test for multiple network support
2 parents ee61587 + 14cb7cb commit 7521b14

File tree

6 files changed

+270
-0
lines changed

6 files changed

+270
-0
lines changed

test/e2e/common.go

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import (
2121
"encoding/json"
2222
"errors"
2323
"fmt"
24+
"maps"
2425
"path/filepath"
2526
"strconv"
2627
"strings"
@@ -575,6 +576,95 @@ func IsClusterReady(ctx context.Context, mgmtClient client.Client, cluster *clus
575576
return c.Status.ControlPlaneReady && c.Status.InfrastructureReady
576577
}
577578

579+
func EnsureSecondaryNetworkExists(client *cloudstack.CloudStackClient, input CommonSpecInput) (*cloudstack.Network, error) {
580+
secondaryNetName := input.E2EConfig.GetVariable("CLOUDSTACK_NEW_NETWORK_NAME")
581+
582+
By("Fetching secondary network details")
583+
// Try fetching secondary network
584+
secondaryNet, _, err := client.Network.GetNetworkByName(secondaryNetName)
585+
if err == nil && secondaryNet != nil {
586+
By(fmt.Sprintf("Network %q already exists", secondaryNetName))
587+
return secondaryNet, nil
588+
}
589+
590+
By("Listing Zone")
591+
zoneName := input.E2EConfig.GetVariable("CLOUDSTACK_ZONE_NAME")
592+
pz := client.Zone.NewListZonesParams()
593+
pz.SetName(zoneName)
594+
listZonesResponse, err := client.Zone.ListZones(pz)
595+
Expect(err).To(BeNil(), "error listing zones")
596+
Expect(listZonesResponse.Count).To(Equal(1), "no zones, or more than one zone resolve to zone name %s", zoneName)
597+
zoneId := listZonesResponse.Zones[0].Id
598+
599+
By("Listing network offerings")
600+
networkOffering, _, err := client.NetworkOffering.GetNetworkOfferingByName(DefaultNetworkOffering)
601+
Expect(err).To(BeNil(), "error fetching network offering %q", DefaultNetworkOffering)
602+
Expect(networkOffering).ToNot(BeNil(), "network offering %q not found", DefaultNetworkOffering)
603+
604+
// Create new network using zone and offering from primary
605+
By("Create secondary network")
606+
createParams := client.Network.NewCreateNetworkParams(
607+
secondaryNetName,
608+
networkOffering.Id,
609+
zoneId,
610+
)
611+
612+
newNetResp, err := client.Network.CreateNetwork(createParams)
613+
if err != nil {
614+
return nil, fmt.Errorf("failed to create network %q: %w", secondaryNetName, err)
615+
}
616+
617+
newNet, _, err := client.Network.GetNetworkByID(newNetResp.Id)
618+
if err != nil {
619+
return nil, fmt.Errorf("failed to fetch created network %q by ID: %w", newNetResp.Id, err)
620+
}
621+
622+
By("Created secondary network")
623+
By(fmt.Sprintf("Created secondary network %q", secondaryNetName))
624+
return newNet, nil
625+
}
626+
627+
func CheckIfNodesHaveTwoNICs(client *cloudstack.CloudStackClient, clusterName string, input CommonSpecInput) {
628+
requiredNetworks := map[string]bool{
629+
input.E2EConfig.GetVariable("CLOUDSTACK_NETWORK_NAME"): false,
630+
input.E2EConfig.GetVariable("CLOUDSTACK_NEW_NETWORK_NAME"): false,
631+
}
632+
633+
Byf("Listing machines with name containing %q", clusterName)
634+
listResp, err := client.VirtualMachine.ListVirtualMachines(client.VirtualMachine.NewListVirtualMachinesParams())
635+
Expect(err).NotTo(HaveOccurred(), "Failed to list virtual machines from CloudStack")
636+
for _, vm := range listResp.VirtualMachines {
637+
if !strings.Contains(vm.Name, clusterName) {
638+
continue
639+
}
640+
641+
if len(vm.Nic) < 2 {
642+
Fail(fmt.Sprintf("VM %q has fewer than 2 NICs. Found: %d", vm.Name, len(vm.Nic)))
643+
}
644+
645+
foundNetworks := make(map[string]bool)
646+
for _, nic := range vm.Nic {
647+
foundNetworks[nic.Networkname] = true
648+
}
649+
650+
for required := range requiredNetworks {
651+
if !foundNetworks[required] {
652+
Fail(fmt.Sprintf("VM %q is missing required network %q", vm.Name, required))
653+
}
654+
}
655+
656+
By(fmt.Sprintf("VM %q has required NICs: %v", vm.Name, maps.Keys(foundNetworks)))
657+
}
658+
}
659+
660+
func keys(m map[string]bool) []string {
661+
var list []string
662+
for k := range m {
663+
list = append(list, k)
664+
}
665+
return list
666+
}
667+
578668
func CheckDiskOfferingOfVmInstances(client *cloudstack.CloudStackClient, clusterName string, diskOfferingName string) {
579669
Byf("Listing machines with %q", clusterName)
580670
listResp, err := client.VirtualMachine.ListVirtualMachines(client.VirtualMachine.NewListVirtualMachinesParams())

test/e2e/config/cloudstack.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,7 @@ providers:
102102
- sourcePath: "../data/infrastructure-cloudstack/v1beta3/cluster-template-kubernetes-version-upgrade-before.yaml"
103103
- sourcePath: "../data/infrastructure-cloudstack/v1beta3/cluster-template-kubernetes-version-upgrade-after.yaml"
104104
- sourcePath: "../data/infrastructure-cloudstack/v1beta3/cluster-template-k8s-cks.yaml"
105+
- sourcePath: "../data/infrastructure-cloudstack/v1beta3/cluster-template-multiple-networks.yaml"
105106
- sourcePath: "../data/shared/v1beta1_provider/metadata.yaml"
106107
versions:
107108
- name: v1.0.0
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
bases:
2+
- ../bases/cluster-with-kcp.yaml
3+
- ../bases/md.yaml
4+
5+
patchesStrategicMerge:
6+
- ./multiple-networks.yaml
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
---
2+
apiVersion: infrastructure.cluster.x-k8s.io/v1beta3
3+
kind: CloudStackMachineTemplate
4+
metadata:
5+
name: ${CLUSTER_NAME}-control-plane
6+
spec:
7+
template:
8+
spec:
9+
offering:
10+
name: ${CLOUDSTACK_CONTROL_PLANE_MACHINE_OFFERING}
11+
template:
12+
name: ${CLOUDSTACK_TEMPLATE_NAME}
13+
sshKey: ${CLOUDSTACK_SSH_KEY_NAME}
14+
affinity: pro
15+
networks:
16+
- name: ${CLOUDSTACK_NETWORK_NAME}
17+
- name: ${CLOUDSTACK_NEW_NETWORK_NAME}
18+
---
19+
apiVersion: infrastructure.cluster.x-k8s.io/v1beta3
20+
kind: CloudStackMachineTemplate
21+
metadata:
22+
name: ${CLUSTER_NAME}-md-0
23+
spec:
24+
template:
25+
spec:
26+
offering:
27+
name: ${CLOUDSTACK_WORKER_MACHINE_OFFERING}
28+
template:
29+
name: ${CLOUDSTACK_TEMPLATE_NAME}
30+
sshKey: ${CLOUDSTACK_SSH_KEY_NAME}
31+
networks:
32+
- name: ${CLOUDSTACK_NETWORK_NAME}
33+
- name: ${CLOUDSTACK_NEW_NETWORK_NAME}

test/e2e/multiple_networks.go

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
/*
2+
Copyright 2020 The Kubernetes Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package e2e
18+
19+
import (
20+
"context"
21+
"fmt"
22+
"os"
23+
"path/filepath"
24+
25+
. "github.com/onsi/ginkgo/v2"
26+
. "github.com/onsi/gomega"
27+
corev1 "k8s.io/api/core/v1"
28+
"k8s.io/utils/pointer"
29+
30+
"sigs.k8s.io/cluster-api/test/framework/clusterctl"
31+
"sigs.k8s.io/cluster-api/util"
32+
)
33+
34+
// MultipleNetworksSpec implements a spec that creates a cluster with nodes having multiple NICs.
35+
func MultipleNetworksSpec(ctx context.Context, inputGetter func() CommonSpecInput) {
36+
var (
37+
specName = "multiple-networks"
38+
input CommonSpecInput
39+
namespace *corev1.Namespace
40+
cancelWatches context.CancelFunc
41+
clusterResources *clusterctl.ApplyClusterTemplateAndWaitResult
42+
)
43+
44+
BeforeEach(func() {
45+
Expect(ctx).NotTo(BeNil(), "ctx is required for %s spec", specName)
46+
input = inputGetter()
47+
48+
Expect(input.E2EConfig).ToNot(BeNil(), "Invalid argument. input.E2EConfig can't be nil when calling %s spec", specName)
49+
Expect(input.ClusterctlConfigPath).To(BeAnExistingFile(), "Invalid argument. input.ClusterctlConfigPath must be an existing file when calling %s spec", specName)
50+
Expect(input.BootstrapClusterProxy).ToNot(BeNil(), "Invalid argument. input.BootstrapClusterProxy can't be nil when calling %s spec", specName)
51+
Expect(os.MkdirAll(input.ArtifactFolder, 0750)).To(Succeed(), "Invalid argument. input.ArtifactFolder can't be created for %s spec", specName)
52+
53+
Expect(input.E2EConfig.Variables).To(HaveKey(KubernetesVersion))
54+
55+
// Setup a Namespace where to host objects for this spec and create a watcher for the namespace events.
56+
namespace, cancelWatches = setupSpecNamespace(ctx, specName, input.BootstrapClusterProxy, input.ArtifactFolder)
57+
clusterResources = new(clusterctl.ApplyClusterTemplateAndWaitResult)
58+
})
59+
60+
It("Should create a workload cluster", func() {
61+
By("Creating a workload cluster")
62+
63+
clusterName := fmt.Sprintf("%s-%s", specName, util.RandomString(6))
64+
65+
// Get details from ACS and ensure secondary network exists
66+
csClient := CreateCloudStackClient(ctx, input.BootstrapClusterProxy.GetKubeconfigPath())
67+
_, err := EnsureSecondaryNetworkExists(csClient, input)
68+
Expect(err).ToNot(HaveOccurred(), "Failed to ensure secondary network exists")
69+
70+
clusterctl.ApplyClusterTemplateAndWait(ctx, clusterctl.ApplyClusterTemplateAndWaitInput{
71+
ClusterProxy: input.BootstrapClusterProxy,
72+
CNIManifestPath: input.E2EConfig.GetVariable(CNIPath),
73+
ConfigCluster: clusterctl.ConfigClusterInput{
74+
LogFolder: filepath.Join(input.ArtifactFolder, "clusters", input.BootstrapClusterProxy.GetName()),
75+
ClusterctlConfigPath: input.ClusterctlConfigPath,
76+
KubeconfigPath: input.BootstrapClusterProxy.GetKubeconfigPath(),
77+
InfrastructureProvider: clusterctl.DefaultInfrastructureProvider,
78+
Flavor: specName,
79+
Namespace: namespace.Name,
80+
ClusterName: clusterName,
81+
KubernetesVersion: input.E2EConfig.GetVariable(KubernetesVersion),
82+
ControlPlaneMachineCount: pointer.Int64Ptr(1),
83+
WorkerMachineCount: pointer.Int64Ptr(1),
84+
},
85+
WaitForClusterIntervals: input.E2EConfig.GetIntervals(specName, "wait-cluster"),
86+
WaitForControlPlaneIntervals: input.E2EConfig.GetIntervals(specName, "wait-control-plane"),
87+
WaitForMachineDeployments: input.E2EConfig.GetIntervals(specName, "wait-worker-nodes"),
88+
}, clusterResources)
89+
90+
By("Verifying that each VM has two NICs")
91+
CheckIfNodesHaveTwoNICs(csClient, clusterName, input)
92+
93+
By("PASSED!")
94+
})
95+
96+
AfterEach(func() {
97+
// Dumps all the resources in the spec namespace, then cleanups the cluster object and the spec namespace itself.
98+
dumpSpecResourcesAndCleanup(ctx, specName, input.BootstrapClusterProxy, input.ArtifactFolder, namespace, cancelWatches, clusterResources.Cluster, input.E2EConfig.GetIntervals, input.SkipCleanup)
99+
})
100+
}

test/e2e/multiple_networks_test.go

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
//go:build e2e
2+
// +build e2e
3+
4+
/*
5+
Copyright 2021 The Kubernetes Authors.
6+
7+
Licensed under the Apache License, Version 2.0 (the "License");
8+
you may not use this file except in compliance with the License.
9+
You may obtain a copy of the License at
10+
11+
http://www.apache.org/licenses/LICENSE-2.0
12+
13+
Unless required by applicable law or agreed to in writing, software
14+
distributed under the License is distributed on an "AS IS" BASIS,
15+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16+
See the License for the specific language governing permissions and
17+
limitations under the License.
18+
*/
19+
20+
package e2e
21+
22+
import (
23+
"context"
24+
25+
. "github.com/onsi/ginkgo/v2"
26+
)
27+
28+
var _ = Describe("Test with multiple networks for nodes", func() {
29+
30+
MultipleNetworksSpec(context.TODO(), func() CommonSpecInput {
31+
return CommonSpecInput{
32+
E2EConfig: e2eConfig,
33+
ClusterctlConfigPath: clusterctlConfigPath,
34+
BootstrapClusterProxy: bootstrapClusterProxy,
35+
ArtifactFolder: artifactFolder,
36+
SkipCleanup: skipCleanup,
37+
}
38+
})
39+
40+
})

0 commit comments

Comments
 (0)