Skip to content

Commit b0a496b

Browse files
committed
Create e2e for vSphere vm-host zonal feature
the vSphere vm-host feature. The two new checks are: - Does the virtual machines created belong in the correct vm-host group of type VirtualMachine - Does the nodes have the correct topology labels for their location within vSphere based on tags
1 parent c0c9364 commit b0a496b

File tree

1 file changed

+315
-0
lines changed

1 file changed

+315
-0
lines changed

test/e2e/vsphere/hostzonal.go

Lines changed: 315 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,315 @@
1+
package vsphere
2+
3+
import (
4+
"context"
5+
_ "embed"
6+
"fmt"
7+
"strings"
8+
9+
"github.com/vmware/govmomi/find"
10+
"github.com/vmware/govmomi/object"
11+
vapirest "github.com/vmware/govmomi/vapi/rest"
12+
"github.com/vmware/govmomi/vapi/tags"
13+
"github.com/vmware/govmomi/vim25"
14+
"github.com/vmware/govmomi/vim25/mo"
15+
"github.com/vmware/govmomi/vim25/types"
16+
corev1 "k8s.io/api/core/v1"
17+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
18+
"k8s.io/client-go/kubernetes"
19+
"k8s.io/client-go/rest"
20+
e2e "k8s.io/kubernetes/test/e2e/framework"
21+
"k8s.io/utils/ptr"
22+
23+
. "github.com/onsi/ginkgo/v2"
24+
. "github.com/onsi/gomega"
25+
26+
configv1 "github.com/openshift/api/config/v1"
27+
configclient "github.com/openshift/client-go/config/clientset/versioned/typed/config/v1"
28+
29+
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
30+
)
31+
32+
var _ = Describe("[sig-cluster-lifecycle][OCPFeatureGate:VSphereHostVMGroupZonal][platform:vsphere] A Machine in a managed cluster", Label("Conformance"), Label("Parallel"), func() {
33+
ctx := context.Background()
34+
35+
var (
36+
cfg *rest.Config
37+
c *kubernetes.Clientset
38+
cc *configclient.ConfigV1Client
39+
40+
vsphereCreds *corev1.Secret
41+
err error
42+
infra *configv1.Infrastructure
43+
nodes *corev1.NodeList
44+
)
45+
46+
BeforeEach(func() {
47+
cfg, err = e2e.LoadConfig()
48+
Expect(err).NotTo(HaveOccurred(), "expected LoadConfig() to succeed")
49+
c, err = e2e.LoadClientset()
50+
Expect(err).NotTo(HaveOccurred(), "expected LoadClientset() to succeed")
51+
cc, err = configclient.NewForConfig(cfg)
52+
Expect(err).NotTo(HaveOccurred(), "expected configclient.NewForConfig() to succeed")
53+
By("Get Infrastructure spec")
54+
infra, err = cc.Infrastructures().Get(ctx, "cluster", metav1.GetOptions{})
55+
Expect(err).NotTo(HaveOccurred(), "expected Infrastructures().Get() to succeed")
56+
57+
if !isVmHostZonal(infra.Spec.PlatformSpec.VSphere) {
58+
Skip("skipping test since cluster does not support vSphere host zones")
59+
}
60+
61+
By("Get vSphere Credentials")
62+
vsphereCreds, err = c.CoreV1().Secrets("kube-system").Get(ctx, "vsphere-creds", v1.GetOptions{})
63+
Expect(err).NotTo(HaveOccurred(), "expected vSphere creds secret to exist")
64+
65+
By("Expect Failure Domains to be greater than one")
66+
Expect(len(infra.Spec.PlatformSpec.VSphere.FailureDomains) >= 1, "expected more than one failure domain")
67+
68+
nodes, err = c.CoreV1().Nodes().List(ctx, v1.ListOptions{})
69+
Expect(err).NotTo(HaveOccurred(), "expected nodes List() to succeed")
70+
})
71+
72+
It("should be placed in the correct vm-host group [apigroup:machine.openshift.io][Suite:openshift/conformance/parallel]", func() {
73+
failIfMachineIsNotInCorrectVMGroup(ctx, nodes, infra.Spec.PlatformSpec.VSphere, vsphereCreds)
74+
})
75+
76+
It("associated with a vm-host group should have the correct topology labels [apigroup:machine.openshift.io][Suite:openshift/conformance/parallel]", func() {
77+
failIfMachineIsNotInCorrectRegionZone(ctx, nodes, infra.Spec.PlatformSpec.VSphere, vsphereCreds)
78+
})
79+
80+
})
81+
82+
func getClusterVmGroups(ctx context.Context, vim25Client *vim25.Client, computeCluster string) ([]*types.ClusterVmGroup, error) {
83+
By("get cluster vm groups")
84+
finder := find.NewFinder(vim25Client, true)
85+
86+
ccr, err := finder.ClusterComputeResource(ctx, computeCluster)
87+
if err != nil {
88+
return nil, err
89+
}
90+
91+
clusterConfig, err := ccr.Configuration(ctx)
92+
if err != nil {
93+
return nil, err
94+
}
95+
96+
var clusterVmGroup []*types.ClusterVmGroup
97+
98+
for _, g := range clusterConfig.Group {
99+
if vmg, ok := g.(*types.ClusterVmGroup); ok {
100+
clusterVmGroup = append(clusterVmGroup, vmg)
101+
}
102+
}
103+
return clusterVmGroup, nil
104+
}
105+
106+
func getAttachedObjects(ctx context.Context, restClient *vapirest.Client) ([]tags.AttachedObjects, []tags.AttachedObjects) {
107+
tmgr := tags.NewManager(restClient)
108+
109+
regionTags, err := tmgr.GetTagsForCategory(ctx, "openshift-region")
110+
Expect(err).NotTo(HaveOccurred(), "expected to get openshift-region tags")
111+
zoneTags, err := tmgr.GetTagsForCategory(ctx, "openshift-zone")
112+
Expect(err).NotTo(HaveOccurred(), "expected to get openshift-zone tags")
113+
114+
regionAttached, err := tmgr.GetAttachedObjectsOnTags(ctx, func(tags []tags.Tag) []string {
115+
var tagsID []string
116+
for _, tag := range tags {
117+
tagsID = append(tagsID, tag.ID)
118+
}
119+
return tagsID
120+
}(regionTags))
121+
122+
Expect(err).NotTo(HaveOccurred(), "expected to get attached objects to be in region tags")
123+
zoneAttached, err := tmgr.GetAttachedObjectsOnTags(ctx, func(tags []tags.Tag) []string {
124+
var tagsID []string
125+
for _, tag := range tags {
126+
tagsID = append(tagsID, tag.ID)
127+
}
128+
return tagsID
129+
}(zoneTags))
130+
Expect(err).NotTo(HaveOccurred(), "expected to get attached objects to be in zone tags")
131+
132+
return regionAttached, zoneAttached
133+
}
134+
135+
func getVSphereClientsFromClusterCreds(ctx context.Context, platform *configv1.VSpherePlatformSpec, vsphereCreds *corev1.Secret) (*vim25.Client, *vapirest.Client, ClientLogout, error) {
136+
vcenter := platform.VCenters[0]
137+
By("get vsphere credentials")
138+
user, pass, err := getCredentialsForVCenter(ctx, vsphereCreds, vcenter)
139+
Expect(err).NotTo(HaveOccurred(), "expected to get vsphere credentials")
140+
return CreateVSphereClients(ctx, vcenter.Server, user, pass)
141+
}
142+
143+
func failIfMachineIsNotInCorrectRegionZone(ctx context.Context,
144+
nodes *corev1.NodeList,
145+
platform *configv1.VSpherePlatformSpec,
146+
vsphereCreds *corev1.Secret) {
147+
148+
vim25Client, restClient, logout, err := getVSphereClientsFromClusterCreds(ctx, platform, vsphereCreds)
149+
defer logout()
150+
Expect(err).NotTo(HaveOccurred(), "expected to get vSphere clients from cluster credentials")
151+
152+
// vm-host zonal will only ever have one vcenter
153+
Expect(platform.VCenters).To(HaveLen(1), "Expected only one vCenter to be configured, but found %d", len(platform.VCenters))
154+
155+
regionAttached, zoneAttached := getAttachedObjects(ctx, restClient)
156+
157+
for _, fd := range platform.FailureDomains {
158+
fdNodes, err := getNodesInFailureDomain(platform, fd, nodes)
159+
Expect(err).NotTo(HaveOccurred(), "expected to get nodes in failure domain")
160+
161+
searchIndex := object.NewSearchIndex(vim25Client)
162+
163+
By("get moref via node uuid")
164+
for _, n := range fdNodes {
165+
166+
parts := strings.Split(n.Spec.ProviderID, "vsphere://")
167+
Expect(len(parts)).Should(BeIdenticalTo(2))
168+
169+
ref, err := searchIndex.FindAllByUuid(ctx, nil, parts[1], true, ptr.To(false))
170+
171+
Expect(err).NotTo(HaveOccurred(), "expected to FindAllByUuid to succeed")
172+
173+
pc := vim25Client.ServiceContent.PropertyCollector
174+
175+
for _, r := range ref {
176+
var vm *object.VirtualMachine
177+
var ok bool
178+
if vm, ok = r.(*object.VirtualMachine); !ok {
179+
Expect(vm).NotTo(BeNil())
180+
}
181+
182+
host, err := vm.HostSystem(ctx)
183+
Expect(err).NotTo(HaveOccurred(), "expected HostSystem to succeed")
184+
185+
foundRegion := false
186+
foundZone := false
187+
me, err := mo.Ancestors(ctx, vim25Client, pc, host.Reference())
188+
189+
Expect(err).NotTo(HaveOccurred(), "expected Ancestor to succeed")
190+
191+
// for vm-host zonal we only care about tags attached to the
192+
// ClusterComputeResource and HostSystem (ESXi Hosts)
193+
for _, m := range me {
194+
switch m.Self.Type {
195+
case "ClusterComputeResource":
196+
for _, r := range regionAttached {
197+
if r.Tag.Name == fd.Region {
198+
for _, attachedRef := range r.ObjectIDs {
199+
if attachedRef.Reference().Value == m.Self.Value {
200+
foundRegion = true
201+
break
202+
}
203+
}
204+
}
205+
if foundRegion {
206+
break
207+
}
208+
}
209+
case "HostSystem":
210+
for _, z := range zoneAttached {
211+
if z.Tag.Name == fd.Zone {
212+
for _, attachedRef := range z.ObjectIDs {
213+
if attachedRef.Reference().Value == m.Self.Value {
214+
foundZone = true
215+
break
216+
}
217+
}
218+
}
219+
if foundZone {
220+
break
221+
}
222+
}
223+
}
224+
}
225+
226+
if !foundRegion || !foundZone {
227+
Expect(fmt.Errorf("node %s missing expected region '%s' or zone '%s' tags", n.Name, fd.Region, fd.Zone)).NotTo(HaveOccurred())
228+
}
229+
}
230+
}
231+
}
232+
}
233+
234+
func failIfMachineIsNotInCorrectVMGroup(ctx context.Context,
235+
nodes *corev1.NodeList,
236+
platform *configv1.VSpherePlatformSpec,
237+
vsphereCreds *corev1.Secret) {
238+
239+
// vm-host zonal will only ever have one vcenter
240+
Expect(platform.VCenters).To(HaveLen(1), "Expected only one vCenter to be configured, but found %d", len(platform.VCenters))
241+
242+
vim25Client, _, logout, err := getVSphereClientsFromClusterCreds(ctx, platform, vsphereCreds)
243+
defer logout()
244+
Expect(err).NotTo(HaveOccurred(), "expected to get vSphere clients from cluster credentials")
245+
246+
for _, fd := range platform.FailureDomains {
247+
clusterVmGroups, err := getClusterVmGroups(ctx, vim25Client, fd.Topology.ComputeCluster)
248+
Expect(err).NotTo(HaveOccurred(), "expected cluster vm groups to be available")
249+
250+
fdNodes, err := getNodesInFailureDomain(platform, fd, nodes)
251+
Expect(err).NotTo(HaveOccurred(), "expected to be able to get nodes in failure domain")
252+
253+
var moRefs []types.ManagedObjectReference
254+
searchIndex := object.NewSearchIndex(vim25Client)
255+
256+
By("get moref via node uuid")
257+
for _, n := range fdNodes {
258+
parts := strings.Split(n.Spec.ProviderID, "vsphere://")
259+
Expect(parts).Should(HaveLen(2), "Expected to find 2 parts in the provider ID, but found %d", len(parts))
260+
261+
ref, err := searchIndex.FindAllByUuid(ctx, nil, parts[1], true, ptr.To(false))
262+
263+
Expect(err).NotTo(HaveOccurred(), "expected FindAllByUuid to succeed")
264+
265+
for _, r := range ref {
266+
moRefs = append(moRefs, r.Reference())
267+
}
268+
}
269+
270+
foundMoRef := make(map[string]bool)
271+
272+
var clusterVmGroup *types.ClusterVmGroup
273+
for _, group := range clusterVmGroups {
274+
if fd.ZoneAffinity.HostGroup.VMGroup == group.Name {
275+
clusterVmGroup = group
276+
}
277+
}
278+
279+
By("check to make sure vm groups are in the correct location")
280+
for _, nMoRef := range moRefs {
281+
foundMoRef[nMoRef.Value] = false
282+
if clusterVmGroup != nil {
283+
for _, gMoRef := range clusterVmGroup.Vm {
284+
if gMoRef.Value == nMoRef.Value {
285+
foundMoRef[nMoRef.Value] = true
286+
break
287+
}
288+
}
289+
if foundMoRef[nMoRef.Value] {
290+
continue
291+
}
292+
}
293+
}
294+
295+
for moRef, v := range foundMoRef {
296+
if !v {
297+
Expect(fmt.Errorf("virtual machine id %s was not in vm group %s", moRef, fd.ZoneAffinity.HostGroup.VMGroup)).NotTo(HaveOccurred())
298+
}
299+
}
300+
}
301+
}
302+
303+
func isVmHostZonal(platform *configv1.VSpherePlatformSpec) bool {
304+
By("check to make sure installed cluster is vm-host zonal")
305+
for _, fd := range platform.FailureDomains {
306+
if fd.ZoneAffinity != nil {
307+
if fd.ZoneAffinity.Type == "HostGroup" {
308+
if fd.ZoneAffinity.HostGroup.VMGroup != "" {
309+
return true
310+
}
311+
}
312+
}
313+
}
314+
return false
315+
}

0 commit comments

Comments
 (0)