Skip to content

Commit 36c5d78

Browse files
Merge pull request #1369 from jcpowermac/vm-host-zonal-test-ext
SPLAT-2082: Create e2e for vSphere vm-host zonal feature
2 parents 7975408 + 0f4a8d8 commit 36c5d78

File tree

3 files changed

+322
-7
lines changed

3 files changed

+322
-7
lines changed

test/e2e/util.go

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,6 @@ import (
66
"time"
77

88
. "github.com/onsi/gomega"
9-
configv1 "github.com/openshift/api/config/v1"
10-
"github.com/openshift/api/machine/v1beta1"
11-
configclient "github.com/openshift/client-go/config/clientset/versioned"
12-
machinesetclient "github.com/openshift/client-go/machine/clientset/versioned/typed/machine/v1beta1"
139
v1 "k8s.io/api/core/v1"
1410
"k8s.io/apimachinery/pkg/api/errors"
1511
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -24,6 +20,11 @@ import (
2420
"k8s.io/client-go/scale"
2521
e2e "k8s.io/kubernetes/test/e2e/framework"
2622
e2eskipper "k8s.io/kubernetes/test/e2e/framework/skipper"
23+
24+
configv1 "github.com/openshift/api/config/v1"
25+
"github.com/openshift/api/machine/v1beta1"
26+
configclient "github.com/openshift/client-go/config/clientset/versioned"
27+
machinesetclient "github.com/openshift/client-go/machine/clientset/versioned/typed/machine/v1beta1"
2728
)
2829

2930
const (

test/e2e/vsphere/hostzonal.go

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

test/e2e/vsphere/util.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,6 @@ import (
1212

1313
. "github.com/onsi/ginkgo/v2"
1414
. "github.com/onsi/gomega"
15-
configv1 "github.com/openshift/api/config/v1"
16-
"github.com/openshift/api/machine/v1beta1"
17-
machinev1beta1 "github.com/openshift/api/machine/v1beta1"
1815
"github.com/pkg/errors"
1916
"github.com/vmware/govmomi"
2017
"github.com/vmware/govmomi/find"
@@ -28,6 +25,9 @@ import (
2825
corev1 "k8s.io/api/core/v1"
2926
"k8s.io/client-go/rest"
3027

28+
configv1 "github.com/openshift/api/config/v1"
29+
"github.com/openshift/api/machine/v1beta1"
30+
machinev1beta1 "github.com/openshift/api/machine/v1beta1"
3131
"github.com/openshift/machine-api-operator/pkg/controller/vsphere"
3232
util "github.com/openshift/machine-api-operator/test/e2e"
3333
)

0 commit comments

Comments
 (0)