Skip to content

Commit 01d7345

Browse files
committed
Add affinity group test
1 parent b3dcbdb commit 01d7345

File tree

8 files changed

+261
-0
lines changed

8 files changed

+261
-0
lines changed

test/e2e/affinity_group.go

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
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"
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+
// AffinityGroupSpec implements a test that verifies that an app deployed to the workload cluster works.
35+
func AffinityGroupSpec(ctx context.Context, inputGetter func() CommonSpecInput) {
36+
var (
37+
specName = "affinity-group"
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+
Expect(input.E2EConfig).ToNot(BeNil(), "Invalid argument. input.E2EConfig can't be nil when calling %s spec", specName)
48+
Expect(input.ClusterctlConfigPath).To(BeAnExistingFile(), "Invalid argument. input.ClusterctlConfigPath must be an existing file when calling %s spec", specName)
49+
Expect(input.BootstrapClusterProxy).ToNot(BeNil(), "Invalid argument. input.BootstrapClusterProxy can't be nil when calling %s spec", specName)
50+
Expect(os.MkdirAll(input.ArtifactFolder, 0750)).To(Succeed(), "Invalid argument. input.ArtifactFolder can't be created for %s spec", specName)
51+
52+
Expect(input.E2EConfig.Variables).To(HaveKey(KubernetesVersion))
53+
54+
// Setup a Namespace where to host objects for this spec and create a watcher for the namespace events.
55+
namespace, cancelWatches = setupSpecNamespace(ctx, specName, input.BootstrapClusterProxy, input.ArtifactFolder)
56+
clusterResources = new(clusterctl.ApplyClusterTemplateAndWaitResult)
57+
})
58+
59+
It("Should have host affinity group when affinity is pro", func() {
60+
clusterctl.ApplyClusterTemplateAndWait(ctx, clusterctl.ApplyClusterTemplateAndWaitInput{
61+
ClusterProxy: input.BootstrapClusterProxy,
62+
CNIManifestPath: input.E2EConfig.GetVariable(CNIPath),
63+
ConfigCluster: clusterctl.ConfigClusterInput{
64+
LogFolder: filepath.Join(input.ArtifactFolder, "clusters", input.BootstrapClusterProxy.GetName()),
65+
ClusterctlConfigPath: input.ClusterctlConfigPath,
66+
KubeconfigPath: input.BootstrapClusterProxy.GetKubeconfigPath(),
67+
InfrastructureProvider: clusterctl.DefaultInfrastructureProvider,
68+
Flavor: "affinity-group-" + "pro",
69+
Namespace: namespace.Name,
70+
ClusterName: fmt.Sprintf("%s-%s", specName, util.RandomString(6)),
71+
KubernetesVersion: input.E2EConfig.GetVariable(KubernetesVersion),
72+
ControlPlaneMachineCount: pointer.Int64Ptr(1),
73+
WorkerMachineCount: pointer.Int64Ptr(1),
74+
},
75+
WaitForClusterIntervals: input.E2EConfig.GetIntervals(specName, "wait-cluster"),
76+
WaitForControlPlaneIntervals: input.E2EConfig.GetIntervals(specName, "wait-control-plane"),
77+
WaitForMachineDeployments: input.E2EConfig.GetIntervals(specName, "wait-worker-nodes"),
78+
}, clusterResources)
79+
80+
CheckAffinityGroup(clusterResources.Cluster.Name, "pro")
81+
82+
By("PASSED!")
83+
})
84+
85+
It("Should have host affinity group when affinity is anti", func() {
86+
clusterctl.ApplyClusterTemplateAndWait(ctx, clusterctl.ApplyClusterTemplateAndWaitInput{
87+
ClusterProxy: input.BootstrapClusterProxy,
88+
CNIManifestPath: input.E2EConfig.GetVariable(CNIPath),
89+
ConfigCluster: clusterctl.ConfigClusterInput{
90+
LogFolder: filepath.Join(input.ArtifactFolder, "clusters", input.BootstrapClusterProxy.GetName()),
91+
ClusterctlConfigPath: input.ClusterctlConfigPath,
92+
KubeconfigPath: input.BootstrapClusterProxy.GetKubeconfigPath(),
93+
InfrastructureProvider: clusterctl.DefaultInfrastructureProvider,
94+
Flavor: "affinity-group-" + "anti",
95+
Namespace: namespace.Name,
96+
ClusterName: fmt.Sprintf("%s-%s", specName, util.RandomString(6)),
97+
KubernetesVersion: input.E2EConfig.GetVariable(KubernetesVersion),
98+
ControlPlaneMachineCount: pointer.Int64Ptr(1),
99+
WorkerMachineCount: pointer.Int64Ptr(1),
100+
},
101+
WaitForClusterIntervals: input.E2EConfig.GetIntervals(specName, "wait-cluster"),
102+
WaitForControlPlaneIntervals: input.E2EConfig.GetIntervals(specName, "wait-control-plane"),
103+
WaitForMachineDeployments: input.E2EConfig.GetIntervals(specName, "wait-worker-nodes"),
104+
}, clusterResources)
105+
106+
CheckAffinityGroup(clusterResources.Cluster.Name, "anti")
107+
108+
By("PASSED!")
109+
})
110+
111+
AfterEach(func() {
112+
// Dumps all the resources in the spec namespace, then cleanups the cluster object and the spec namespace itself.
113+
dumpSpecResourcesAndCleanup(ctx, specName, input.BootstrapClusterProxy, input.ArtifactFolder, namespace, cancelWatches, clusterResources.Cluster, input.E2EConfig.GetIntervals, input.SkipCleanup)
114+
})
115+
}

test/e2e/affinity_group_test.go

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
//go:build e2e
2+
// +build e2e
3+
4+
/*
5+
Copyright 2020 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+
. "github.com/onsi/ginkgo"
25+
)
26+
27+
var _ = Describe("When testing affinity group", func() {
28+
29+
AffinityGroupSpec(context.TODO(), func() CommonSpecInput {
30+
return CommonSpecInput{
31+
E2EConfig: e2eConfig,
32+
ClusterctlConfigPath: clusterctlConfigPath,
33+
BootstrapClusterProxy: bootstrapClusterProxy,
34+
ArtifactFolder: artifactFolder,
35+
SkipCleanup: skipCleanup,
36+
}
37+
})
38+
39+
})

test/e2e/common.go

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -241,6 +241,51 @@ func DestroyOneMachine(clusterName string, machineType string) {
241241
}
242242
}
243243

244+
func CheckAffinityGroup(clusterName string, affinityType string) {
245+
encodedSecret := os.Getenv("CLOUDSTACK_B64ENCODED_SECRET")
246+
secret, err := base64.StdEncoding.DecodeString(encodedSecret)
247+
if err != nil {
248+
Fail("Failed ")
249+
}
250+
cfg := &cloudConfig{VerifySSL: true}
251+
if rawCfg, err := ini.Load(secret); err != nil {
252+
Fail("Failed to load INI file")
253+
} else if g := rawCfg.Section("Global"); len(g.Keys()) == 0 {
254+
Fail("Global section not found")
255+
} else if err = rawCfg.Section("Global").StrictMapTo(cfg); err != nil {
256+
Fail("Error encountered while parsing Global section")
257+
}
258+
259+
By("Creating a CloudStack client")
260+
client := cloudstack.NewAsyncClient(cfg.APIURL, cfg.APIKey, cfg.SecretKey, cfg.VerifySSL)
261+
262+
By("Listing all machines")
263+
listResp, err := client.VirtualMachine.ListVirtualMachines(client.VirtualMachine.NewListVirtualMachinesParams())
264+
if err != nil {
265+
Fail("Failed to list machines")
266+
}
267+
affinityTypeString := strings.Title(fmt.Sprintf("%sAffinity", affinityType))
268+
for _, vm := range listResp.VirtualMachines {
269+
if strings.Contains(vm.Name, clusterName) {
270+
for _, affinity := range vm.Affinitygroup {
271+
affinity, _, _ := client.AffinityGroup.GetAffinityGroupByID(affinity.Id)
272+
if err != nil {
273+
Fail("Failed to get affinity group for " + affinity.Id)
274+
}
275+
if !strings.Contains(affinity.Name, affinityTypeString) {
276+
Fail(affinity.Name + " does not contain " + affinityTypeString)
277+
}
278+
if affinityType == "pro" && affinity.Type != "host affinity" {
279+
Fail(affinity.Type + " does not match " + affinityType)
280+
}
281+
if affinityType == "anti" && affinity.Type != "host anti-affinity" {
282+
Fail(affinity.Type + " does not match " + affinityType)
283+
}
284+
}
285+
}
286+
}
287+
}
288+
244289
func WaitForMachineRemediationAfterDestroy(ctx context.Context, proxy framework.ClusterProxy, cluster *clusterv1.Cluster, machineMatcher string, healthyMachineCount int, intervals []interface{}) {
245290
mgmtClusterClient := proxy.GetClient()
246291
workloadClusterClient := proxy.GetWorkloadCluster(ctx, cluster.Namespace, cluster.Name).GetClient()

test/e2e/config/cloudstack.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,8 @@ providers:
8383
- sourcePath: "../data/infrastructure-cloudstack/v1beta1/cluster-template-invalid-worker-offering.yaml"
8484
- sourcePath: "../data/infrastructure-cloudstack/v1beta1/cluster-template-node-drain.yaml"
8585
- sourcePath: "../data/infrastructure-cloudstack/v1beta1/cluster-template-machine-remediation.yaml"
86+
- sourcePath: "../data/infrastructure-cloudstack/v1beta1/cluster-template-affinity-group-pro.yaml"
87+
- sourcePath: "../data/infrastructure-cloudstack/v1beta1/cluster-template-affinity-group-anti.yaml"
8688
- sourcePath: "../data/shared/v1beta1/metadata.yaml"
8789
versions:
8890
- 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+
- ./md.yaml
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
---
2+
apiVersion: infrastructure.cluster.x-k8s.io/v1beta1
3+
kind: CloudStackMachineTemplate
4+
metadata:
5+
name: ${CLUSTER_NAME}-control-plane
6+
spec:
7+
template:
8+
spec:
9+
offering: ${CLOUDSTACK_CONTROL_PLANE_MACHINE_OFFERING}
10+
template: ${CLOUDSTACK_TEMPLATE_NAME}
11+
sshKey: ${CLOUDSTACK_SSH_KEY_NAME}
12+
affinity: anti
13+
---
14+
apiVersion: infrastructure.cluster.x-k8s.io/v1beta1
15+
kind: CloudStackMachineTemplate
16+
metadata:
17+
name: ${CLUSTER_NAME}-md-0
18+
spec:
19+
template:
20+
spec:
21+
offering: ${CLOUDSTACK_WORKER_MACHINE_OFFERING}
22+
template: ${CLOUDSTACK_TEMPLATE_NAME}
23+
sshKey: ${CLOUDSTACK_SSH_KEY_NAME}
24+
affinity: anti
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+
- ./md.yaml
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
---
2+
apiVersion: infrastructure.cluster.x-k8s.io/v1beta1
3+
kind: CloudStackMachineTemplate
4+
metadata:
5+
name: ${CLUSTER_NAME}-control-plane
6+
spec:
7+
template:
8+
spec:
9+
offering: ${CLOUDSTACK_CONTROL_PLANE_MACHINE_OFFERING}
10+
template: ${CLOUDSTACK_TEMPLATE_NAME}
11+
sshKey: ${CLOUDSTACK_SSH_KEY_NAME}
12+
affinity: pro
13+
---
14+
apiVersion: infrastructure.cluster.x-k8s.io/v1beta1
15+
kind: CloudStackMachineTemplate
16+
metadata:
17+
name: ${CLUSTER_NAME}-md-0
18+
spec:
19+
template:
20+
spec:
21+
offering: ${CLOUDSTACK_WORKER_MACHINE_OFFERING}
22+
template: ${CLOUDSTACK_TEMPLATE_NAME}
23+
sshKey: ${CLOUDSTACK_SSH_KEY_NAME}
24+
affinity: pro

0 commit comments

Comments
 (0)