Skip to content

Commit 31fbbd1

Browse files
committed
openstack: Add e2e tests
Signed-off-by: Stephen Finucane <[email protected]>
1 parent e6beba5 commit 31fbbd1

File tree

3 files changed

+162
-0
lines changed

3 files changed

+162
-0
lines changed

e2e/go.mod

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ require (
2121
sigs.k8s.io/cluster-api-provider-azure v1.20.2
2222
sigs.k8s.io/cluster-api-provider-gcp v1.10.0
2323
sigs.k8s.io/cluster-api-provider-ibmcloud v0.11.0
24+
sigs.k8s.io/cluster-api-provider-openstack v0.12.4
2425
sigs.k8s.io/cluster-api-provider-vsphere v1.13.0
2526
sigs.k8s.io/controller-runtime v0.20.4
2627
sigs.k8s.io/yaml v1.4.0
@@ -63,6 +64,7 @@ require (
6364
github.com/google/gnostic-models v0.6.9 // indirect
6465
github.com/google/pprof v0.0.0-20250403155104-27863c87afa6 // indirect
6566
github.com/google/uuid v1.6.0 // indirect
67+
github.com/gophercloud/gophercloud/v2 v2.7.0 // indirect
6668
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
6769
github.com/hashicorp/go-retryablehttp v0.7.7 // indirect
6870
github.com/jmespath/go-jmespath v0.4.1-0.20220621161143-b0104c826a24 // indirect

e2e/go.sum

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,8 @@ github.com/google/pprof v0.0.0-20250403155104-27863c87afa6 h1:BHT72Gu3keYf3ZEu2J
117117
github.com/google/pprof v0.0.0-20250403155104-27863c87afa6/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA=
118118
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
119119
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
120+
github.com/gophercloud/gophercloud/v2 v2.7.0 h1:o0m4kgVcPgHlcXiWAjoVxGd8QCmvM5VU+YM71pFbn0E=
121+
github.com/gophercloud/gophercloud/v2 v2.7.0/go.mod h1:Ki/ILhYZr/5EPebrPL9Ej+tUg4lqx71/YH2JWVeU+Qk=
120122
github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ=
121123
github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48=
122124
github.com/hashicorp/go-hclog v1.6.3 h1:Qr2kF+eVWjTiYmU7Y31tYlP1h0q/X3Nl3tPGdaB11/k=
@@ -373,6 +375,8 @@ sigs.k8s.io/cluster-api-provider-gcp v1.10.0 h1:gngUxo9bz8l+otpdw9v3ULEXwAvijyqi
373375
sigs.k8s.io/cluster-api-provider-gcp v1.10.0/go.mod h1:VkmOqBgi3Jj+VR+YTZEHAlMMmyjqcKl4lXhpLsYnYok=
374376
sigs.k8s.io/cluster-api-provider-ibmcloud v0.11.0 h1:aunR3nnDzQ5x1Qj1hJbR3Xq4SCZth4XyTWAyUCN46kE=
375377
sigs.k8s.io/cluster-api-provider-ibmcloud v0.11.0/go.mod h1:9yPLATyiqLx4crMzbX11YQU+GuR5txjtiCt8z9sxDfM=
378+
sigs.k8s.io/cluster-api-provider-openstack v0.12.4 h1:zTg4UrFbK8GwUm+7983mm0Omz/w4BLB+VSmbELqGimc=
379+
sigs.k8s.io/cluster-api-provider-openstack v0.12.4/go.mod h1:tYFVtlu1NqU9H5edUaz2QkXDhucn6mAY9mHeK9pEKzU=
376380
sigs.k8s.io/cluster-api-provider-vsphere v1.13.0 h1:tvJX0p/LfBvOvF1EWNu4+9EJALBTAHU1US7Z1SX/24Q=
377381
sigs.k8s.io/cluster-api-provider-vsphere v1.13.0/go.mod h1:TgNq4vlGPmUhCmzT2hOptOZuNbyj0JfOXFLufr91tTY=
378382
sigs.k8s.io/controller-runtime v0.20.4 h1:X3c+Odnxz+iPTRobG4tp092+CvBU9UK0t/bRf+n0DGU=

e2e/openstack_test.go

Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
package e2e
2+
3+
import (
4+
"context"
5+
6+
"github.com/onsi/gomega/format"
7+
configv1 "github.com/openshift/api/config/v1"
8+
mapiv1alpha1 "github.com/openshift/api/machine/v1alpha1"
9+
mapiv1beta1 "github.com/openshift/api/machine/v1beta1"
10+
"github.com/openshift/cluster-capi-operator/e2e/framework"
11+
corev1 "k8s.io/api/core/v1"
12+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
13+
"k8s.io/utils/ptr"
14+
"sigs.k8s.io/controller-runtime/pkg/client"
15+
yaml "sigs.k8s.io/yaml"
16+
17+
openstackv1 "sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1"
18+
19+
. "github.com/onsi/ginkgo/v2"
20+
. "github.com/onsi/gomega"
21+
)
22+
23+
const (
24+
openStackMachineTemplateName = "openstack-machine-template"
25+
)
26+
27+
var _ = Describe("Cluster API OpenStack MachineSet", Ordered, func() {
28+
var mapiMachineSpec *mapiv1alpha1.OpenstackProviderSpec
29+
30+
BeforeAll(func() {
31+
if platform != configv1.OpenStackPlatformType {
32+
Skip("Skipping OpenStack E2E tests")
33+
}
34+
mapiMachineSpec = getOpenStackMAPIProviderSpec(cl)
35+
})
36+
37+
It("should be able to run a machine with implicit cluster default network", func() {
38+
openStackMachineTemplate := createOpenStackMachineTemplate(ctx, cl, mapiMachineSpec)
39+
40+
machineSet := framework.CreateMachineSet(ctx, cl, framework.NewMachineSetParams(
41+
"openstack-machineset",
42+
clusterName,
43+
"",
44+
1,
45+
corev1.ObjectReference{
46+
Kind: "OpenStackMachineTemplate",
47+
APIVersion: infraAPIVersion,
48+
Name: openStackMachineTemplate.Name,
49+
},
50+
"worker-user-data",
51+
))
52+
DeferCleanup(func() {
53+
By("Deleting machineset " + machineSet.Name)
54+
Expect(cl.Delete(ctx, machineSet)).To(Succeed())
55+
framework.WaitForMachineSetsDeleted(cl, machineSet)
56+
})
57+
58+
framework.WaitForMachineSet(cl, machineSet.Name, machineSet.Namespace)
59+
})
60+
})
61+
62+
func getOpenStackMAPIProviderSpec(cl client.Client) *mapiv1alpha1.OpenstackProviderSpec {
63+
machineSetList := &mapiv1beta1.MachineSetList{}
64+
Expect(cl.List(ctx, machineSetList, client.InNamespace(framework.MAPINamespace))).To(Succeed())
65+
66+
Expect(machineSetList.Items).ToNot(HaveLen(0))
67+
machineSet := machineSetList.Items[0]
68+
Expect(machineSet.Spec.Template.Spec.ProviderSpec.Value).ToNot(BeNil())
69+
70+
providerSpec := &mapiv1alpha1.OpenstackProviderSpec{}
71+
Expect(yaml.Unmarshal(machineSet.Spec.Template.Spec.ProviderSpec.Value.Raw, providerSpec)).To(Succeed())
72+
73+
return providerSpec
74+
}
75+
76+
func createOpenStackMachineTemplate(ctx context.Context, cl client.Client, mapiProviderSpec *mapiv1alpha1.OpenstackProviderSpec) *openstackv1.OpenStackMachineTemplate {
77+
By("Creating OpenStack machine template")
78+
79+
Expect(mapiProviderSpec).ToNot(BeNil())
80+
Expect(mapiProviderSpec.Flavor).ToNot(BeEmpty())
81+
// NOTE(stephenfin): Installer does not populate ps.Image when ps.RootVolume is set and will
82+
// instead populate ps.RootVolume.SourceUUID. Moreover, according to the ClusterOSImage option
83+
// definition this is always the name of the image and never the UUID. We should allow UUID
84+
// at some point and this will need an update.
85+
if mapiProviderSpec.RootVolume != nil {
86+
Expect(mapiProviderSpec.RootVolume.SourceUUID).ToNot(BeEmpty())
87+
} else {
88+
Expect(mapiProviderSpec.Image).ToNot(BeEmpty())
89+
}
90+
Expect(len(mapiProviderSpec.Networks)).To(BeNumerically(">", 0))
91+
Expect(len(mapiProviderSpec.Networks[0].Subnets)).To(BeNumerically(">", 0))
92+
Expect(mapiProviderSpec.Tags).ToNot(BeNil())
93+
Expect(len(mapiProviderSpec.Tags)).To(BeNumerically(">", 0))
94+
95+
var image string
96+
var rootVolume *openstackv1.RootVolume
97+
98+
if mapiProviderSpec.RootVolume != nil {
99+
rootVolume = &openstackv1.RootVolume{
100+
SizeGiB: mapiProviderSpec.RootVolume.Size,
101+
BlockDeviceVolume: openstackv1.BlockDeviceVolume{
102+
Type: mapiProviderSpec.RootVolume.VolumeType,
103+
AvailabilityZone: &openstackv1.VolumeAvailabilityZone{
104+
From: openstackv1.VolumeAZFromName,
105+
Name: ptr.To(openstackv1.VolumeAZName(mapiProviderSpec.RootVolume.Zone)),
106+
},
107+
},
108+
}
109+
} else {
110+
image = mapiProviderSpec.Image
111+
}
112+
113+
// NOTE(stephenfin): We intentionally ignore additional security for now.
114+
var securityGroupParam openstackv1.SecurityGroupParam
115+
securityGroup := mapiProviderSpec.SecurityGroups[0]
116+
if securityGroup.UUID != "" {
117+
securityGroupParam = openstackv1.SecurityGroupParam{ID: &securityGroup.UUID}
118+
} else {
119+
securityGroupParam = openstackv1.SecurityGroupParam{Filter: &openstackv1.SecurityGroupFilter{Name: securityGroup.Name}}
120+
}
121+
securityGroups := []openstackv1.SecurityGroupParam{
122+
securityGroupParam,
123+
}
124+
125+
// We intentionally omit ports so the machine will default its network
126+
// from the OpenStackCluster created by the infracluster controller.
127+
openStackMachineSpec := openstackv1.OpenStackMachineSpec{
128+
Flavor: ptr.To(mapiProviderSpec.Flavor),
129+
IdentityRef: &openstackv1.OpenStackIdentityReference{
130+
CloudName: "openstack",
131+
Name: "openstack-cloud-credentials",
132+
},
133+
Image: openstackv1.ImageParam{Filter: &openstackv1.ImageFilter{Name: &image}},
134+
RootVolume: rootVolume,
135+
SecurityGroups: securityGroups,
136+
}
137+
138+
openStackMachineTemplate := &openstackv1.OpenStackMachineTemplate{
139+
ObjectMeta: metav1.ObjectMeta{
140+
GenerateName: openStackMachineTemplateName + "-",
141+
Namespace: framework.CAPINamespace,
142+
},
143+
Spec: openstackv1.OpenStackMachineTemplateSpec{
144+
Template: openstackv1.OpenStackMachineTemplateResource{
145+
Spec: openStackMachineSpec,
146+
},
147+
},
148+
}
149+
150+
Expect(cl.Create(ctx, openStackMachineTemplate)).To(Succeed(), format.Object(openStackMachineTemplate, 1))
151+
// DeferCleanup(func() error {
152+
// return cl.Delete(ctx, openStackMachineTemplate)
153+
// })
154+
155+
return openStackMachineTemplate
156+
}

0 commit comments

Comments
 (0)