Skip to content

Commit 550d519

Browse files
authored
Merge pull request #2138 from shiftstack/issue2136
🐛 Fix down-conversion of IdentityRef
2 parents 1f87904 + ade7f77 commit 550d519

8 files changed

+141
-28
lines changed

api/v1alpha6/openstackcluster_conversion.go

+4-1
Original file line numberDiff line numberDiff line change
@@ -364,7 +364,10 @@ func Convert_v1beta1_OpenStackClusterSpec_To_v1alpha6_OpenStackClusterSpec(in *i
364364
}
365365

366366
out.CloudName = in.IdentityRef.CloudName
367-
out.IdentityRef = &OpenStackIdentityReference{Name: in.IdentityRef.Name}
367+
out.IdentityRef = &OpenStackIdentityReference{}
368+
if err := Convert_v1beta1_OpenStackIdentityReference_To_v1alpha6_OpenStackIdentityReference(&in.IdentityRef, out.IdentityRef, s); err != nil {
369+
return err
370+
}
368371

369372
if in.APIServerLoadBalancer != nil {
370373
if err := Convert_v1beta1_APIServerLoadBalancer_To_v1alpha6_APIServerLoadBalancer(in.APIServerLoadBalancer, &out.APIServerLoadBalancer, s); err != nil {

api/v1alpha6/openstackmachine_conversion.go

-1
Original file line numberDiff line numberDiff line change
@@ -373,7 +373,6 @@ func Convert_v1beta1_OpenStackMachineSpec_To_v1alpha6_OpenStackMachineSpec(in *i
373373
}
374374

375375
if in.IdentityRef != nil {
376-
out.IdentityRef = &OpenStackIdentityReference{Name: in.IdentityRef.Name}
377376
out.CloudName = in.IdentityRef.CloudName
378377
}
379378

api/v1alpha6/types_conversion.go

+2
Original file line numberDiff line numberDiff line change
@@ -534,6 +534,8 @@ func Convert_v1alpha6_OpenStackIdentityReference_To_v1beta1_OpenStackIdentityRef
534534

535535
func Convert_v1beta1_OpenStackIdentityReference_To_v1alpha6_OpenStackIdentityReference(in *infrav1.OpenStackIdentityReference, out *OpenStackIdentityReference, _ apiconversion.Scope) error {
536536
out.Name = in.Name
537+
// Kind will be overwritten during restore if it was previously set to some other value, but if not then some value is still required
538+
out.Kind = "Secret"
537539
return nil
538540
}
539541

api/v1alpha7/openstackcluster_conversion.go

+4-1
Original file line numberDiff line numberDiff line change
@@ -361,7 +361,10 @@ func Convert_v1beta1_OpenStackClusterSpec_To_v1alpha7_OpenStackClusterSpec(in *i
361361
}
362362

363363
out.CloudName = in.IdentityRef.CloudName
364-
out.IdentityRef = &OpenStackIdentityReference{Name: in.IdentityRef.Name}
364+
out.IdentityRef = &OpenStackIdentityReference{}
365+
if err := Convert_v1beta1_OpenStackIdentityReference_To_v1alpha7_OpenStackIdentityReference(&in.IdentityRef, out.IdentityRef, s); err != nil {
366+
return err
367+
}
365368

366369
if in.APIServerLoadBalancer != nil {
367370
if err := Convert_v1beta1_APIServerLoadBalancer_To_v1alpha7_APIServerLoadBalancer(in.APIServerLoadBalancer, &out.APIServerLoadBalancer, s); err != nil {

api/v1alpha7/types_conversion.go

+2
Original file line numberDiff line numberDiff line change
@@ -536,6 +536,8 @@ func Convert_v1alpha7_OpenStackIdentityReference_To_v1beta1_OpenStackIdentityRef
536536

537537
func Convert_v1beta1_OpenStackIdentityReference_To_v1alpha7_OpenStackIdentityReference(in *infrav1.OpenStackIdentityReference, out *OpenStackIdentityReference, _ apiconversion.Scope) error {
538538
out.Name = in.Name
539+
// Kind will be overwritten during restore if it was previously set to some other value, but if not then some value is still required
540+
out.Kind = "Secret"
539541
return nil
540542
}
541543

test/e2e/suites/apivalidations/openstackcluster_test.go

+56-25
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ package apivalidations
1919
import (
2020
. "github.com/onsi/ginkgo/v2"
2121
. "github.com/onsi/gomega"
22+
"github.com/onsi/gomega/format"
2223
corev1 "k8s.io/api/core/v1"
2324
"k8s.io/apimachinery/pkg/types"
2425
"k8s.io/utils/ptr"
@@ -33,16 +34,6 @@ import (
3334
var _ = Describe("OpenStackCluster API validations", func() {
3435
var namespace *corev1.Namespace
3536

36-
create := func(obj client.Object) error {
37-
err := k8sClient.Create(ctx, obj)
38-
if err == nil {
39-
DeferCleanup(func() error {
40-
return k8sClient.Delete(ctx, obj)
41-
})
42-
}
43-
return err
44-
}
45-
4637
BeforeEach(func() {
4738
namespace = createNamespace()
4839
})
@@ -58,12 +49,12 @@ var _ = Describe("OpenStackCluster API validations", func() {
5849
})
5950

6051
It("should allow the smallest permissible cluster spec", func() {
61-
Expect(create(cluster)).To(Succeed(), "OpenStackCluster creation should succeed")
52+
Expect(createObj(cluster)).To(Succeed(), "OpenStackCluster creation should succeed")
6253
})
6354

6455
It("should only allow controlPlaneEndpoint to be set once", func() {
6556
By("Creating a bare cluster")
66-
Expect(create(cluster)).To(Succeed(), "OpenStackCluster creation should succeed")
57+
Expect(createObj(cluster)).To(Succeed(), "OpenStackCluster creation should succeed")
6758

6859
By("Setting the control plane endpoint")
6960
cluster.Spec.ControlPlaneEndpoint = &clusterv1.APIEndpoint{
@@ -79,12 +70,12 @@ var _ = Describe("OpenStackCluster API validations", func() {
7970

8071
It("should allow an empty managed security groups definition", func() {
8172
cluster.Spec.ManagedSecurityGroups = &infrav1.ManagedSecurityGroups{}
82-
Expect(create(cluster)).To(Succeed(), "OpenStackCluster creation should succeed")
73+
Expect(createObj(cluster)).To(Succeed(), "OpenStackCluster creation should succeed")
8374
})
8475

8576
It("should default enabled to true if APIServerLoadBalancer is specified without enabled=true", func() {
8677
cluster.Spec.APIServerLoadBalancer = &infrav1.APIServerLoadBalancer{}
87-
Expect(create(cluster)).To(Succeed(), "OpenStackCluster creation should succeed")
78+
Expect(createObj(cluster)).To(Succeed(), "OpenStackCluster creation should succeed")
8879

8980
// Fetch the cluster and check the defaulting
9081
fetchedCluster := &infrav1.OpenStackCluster{}
@@ -95,7 +86,7 @@ var _ = Describe("OpenStackCluster API validations", func() {
9586
})
9687

9788
It("should not default APIServerLoadBalancer if it is not specifid", func() {
98-
Expect(create(cluster)).To(Succeed(), "OpenStackCluster creation should succeed")
89+
Expect(createObj(cluster)).To(Succeed(), "OpenStackCluster creation should succeed")
9990

10091
// Fetch the cluster and check the defaulting
10192
fetchedCluster := &infrav1.OpenStackCluster{}
@@ -116,19 +107,19 @@ var _ = Describe("OpenStackCluster API validations", func() {
116107
},
117108
},
118109
}
119-
Expect(create(cluster)).To(Succeed(), "OpenStackCluster creation should succeed")
110+
Expect(createObj(cluster)).To(Succeed(), "OpenStackCluster creation should succeed")
120111
})
121112

122113
It("should not allow bastion.enabled=true without a spec", func() {
123114
cluster.Spec.Bastion = &infrav1.Bastion{
124115
Enabled: ptr.To(true),
125116
}
126-
Expect(create(cluster)).NotTo(Succeed(), "OpenStackCluster creation should not succeed")
117+
Expect(createObj(cluster)).NotTo(Succeed(), "OpenStackCluster creation should not succeed")
127118
})
128119

129120
It("should not allow an empty Bastion", func() {
130121
cluster.Spec.Bastion = &infrav1.Bastion{}
131-
Expect(create(cluster)).NotTo(Succeed(), "OpenStackCluster creation should not succeed")
122+
Expect(createObj(cluster)).NotTo(Succeed(), "OpenStackCluster creation should not succeed")
132123
})
133124

134125
It("should default bastion.enabled=true", func() {
@@ -141,7 +132,7 @@ var _ = Describe("OpenStackCluster API validations", func() {
141132
},
142133
},
143134
}
144-
Expect(create(cluster)).To(Succeed(), "OpenStackCluster creation should not succeed")
135+
Expect(createObj(cluster)).To(Succeed(), "OpenStackCluster creation should not succeed")
145136

146137
// Fetch the cluster and check the defaulting
147138
fetchedCluster := &infrav1.OpenStackCluster{}
@@ -162,7 +153,7 @@ var _ = Describe("OpenStackCluster API validations", func() {
162153
},
163154
FloatingIP: ptr.To("10.0.0.0"),
164155
}
165-
Expect(create(cluster)).To(Succeed(), "OpenStackCluster creation should succeed")
156+
Expect(createObj(cluster)).To(Succeed(), "OpenStackCluster creation should succeed")
166157
})
167158

168159
It("should not allow non-IPv4 as bastion floating IP", func() {
@@ -176,7 +167,7 @@ var _ = Describe("OpenStackCluster API validations", func() {
176167
},
177168
FloatingIP: ptr.To("foobar"),
178169
}
179-
Expect(create(cluster)).NotTo(Succeed(), "OpenStackCluster creation should not succeed")
170+
Expect(createObj(cluster)).NotTo(Succeed(), "OpenStackCluster creation should not succeed")
180171
})
181172
})
182173

@@ -196,7 +187,7 @@ var _ = Describe("OpenStackCluster API validations", func() {
196187
Kind: "FakeKind",
197188
Name: "identity-ref",
198189
}
199-
Expect(create(cluster)).To(Succeed(), "OpenStackCluster creation should succeed")
190+
Expect(createObj(cluster)).To(Succeed(), "OpenStackCluster creation should succeed")
200191

201192
// Fetch the infrav1 version of the cluster
202193
infrav1Cluster := &infrav1.OpenStackCluster{}
@@ -218,7 +209,7 @@ var _ = Describe("OpenStackCluster API validations", func() {
218209

219210
It("should not enable an explicitly disabled bastion when converting to v1beta1", func() {
220211
cluster.Spec.Bastion = &infrav1alpha7.Bastion{Enabled: false}
221-
Expect(create(cluster)).To(Succeed(), "OpenStackCluster creation should succeed")
212+
Expect(createObj(cluster)).To(Succeed(), "OpenStackCluster creation should succeed")
222213

223214
// Fetch the infrav1 version of the cluster
224215
infrav1Cluster := &infrav1.OpenStackCluster{}
@@ -233,6 +224,26 @@ var _ = Describe("OpenStackCluster API validations", func() {
233224
Expect(infrav1Bastion).ToNot(BeNil(), "Bastion should not have been removed")
234225
Expect(infrav1Bastion.Enabled).To(Equal(ptr.To(false)), "Bastion should remain disabled")
235226
})
227+
228+
It("should downgrade cleanly from infrav1", func() {
229+
infrav1Cluster := &infrav1.OpenStackCluster{}
230+
infrav1Cluster.Namespace = namespace.Name
231+
infrav1Cluster.GenerateName = clusterNamePrefix
232+
infrav1Cluster.Spec.IdentityRef.CloudName = "test-cloud"
233+
infrav1Cluster.Spec.IdentityRef.Name = "test-credentials"
234+
Expect(createObj(infrav1Cluster)).To(Succeed(), "infrav1 OpenStackCluster creation should succeed")
235+
236+
// Just fetching the object as v1alpha6 doesn't trigger
237+
// validation failure, so we first fetch it and then
238+
// patch the object with identical contents. The patch
239+
// triggers a validation failure.
240+
cluster := &infrav1alpha7.OpenStackCluster{}
241+
Expect(k8sClient.Get(ctx, types.NamespacedName{Name: infrav1Cluster.Name, Namespace: infrav1Cluster.Namespace}, cluster)).To(Succeed(), "OpenStackCluster fetch should succeed")
242+
243+
setObjectGVK(cluster)
244+
cluster.ManagedFields = nil
245+
Expect(k8sClient.Patch(ctx, cluster, client.Apply, client.FieldOwner("test"), client.ForceOwnership)).To(Succeed(), format.Object(cluster, 4))
246+
})
236247
})
237248

238249
Context("v1alpha6", func() {
@@ -251,7 +262,7 @@ var _ = Describe("OpenStackCluster API validations", func() {
251262
Kind: "FakeKind",
252263
Name: "identity-ref",
253264
}
254-
Expect(create(cluster)).To(Succeed(), "OpenStackCluster creation should succeed")
265+
Expect(createObj(cluster)).To(Succeed(), "OpenStackCluster creation should succeed")
255266

256267
// Fetch the infrav1 version of the cluster
257268
infrav1Cluster := &infrav1.OpenStackCluster{}
@@ -273,7 +284,7 @@ var _ = Describe("OpenStackCluster API validations", func() {
273284

274285
It("should not enable an explicitly disabled bastion when converting to v1beta1", func() {
275286
cluster.Spec.Bastion = &infrav1alpha6.Bastion{Enabled: false}
276-
Expect(create(cluster)).To(Succeed(), "OpenStackCluster creation should succeed")
287+
Expect(createObj(cluster)).To(Succeed(), "OpenStackCluster creation should succeed")
277288

278289
// Fetch the infrav1 version of the cluster
279290
infrav1Cluster := &infrav1.OpenStackCluster{}
@@ -288,5 +299,25 @@ var _ = Describe("OpenStackCluster API validations", func() {
288299
Expect(infrav1Bastion).ToNot(BeNil(), "Bastion should not have been removed")
289300
Expect(infrav1Bastion.Enabled).To(Equal(ptr.To(false)), "Bastion should remain disabled")
290301
})
302+
303+
It("should downgrade cleanly from infrav1", func() {
304+
infrav1Cluster := &infrav1.OpenStackCluster{}
305+
infrav1Cluster.Namespace = namespace.Name
306+
infrav1Cluster.GenerateName = clusterNamePrefix
307+
infrav1Cluster.Spec.IdentityRef.CloudName = "test-cloud"
308+
infrav1Cluster.Spec.IdentityRef.Name = "test-credentials"
309+
Expect(createObj(infrav1Cluster)).To(Succeed(), "infrav1 OpenStackCluster creation should succeed")
310+
311+
// Just fetching the object as v1alpha6 doesn't trigger
312+
// validation failure, so we first fetch it and then
313+
// patch the object with identical contents. The patch
314+
// triggers a validation failure.
315+
cluster := &infrav1alpha6.OpenStackCluster{} //nolint:staticcheck
316+
Expect(k8sClient.Get(ctx, types.NamespacedName{Name: infrav1Cluster.Name, Namespace: infrav1Cluster.Namespace}, cluster)).To(Succeed(), "OpenStackCluster fetch should succeed")
317+
318+
setObjectGVK(cluster)
319+
cluster.ManagedFields = nil
320+
Expect(k8sClient.Patch(ctx, cluster, client.Apply, client.FieldOwner("test"), client.ForceOwnership)).To(Succeed(), format.Object(cluster, 4))
321+
})
291322
})
292323
})

test/e2e/suites/apivalidations/openstackmachine_test.go

+55
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,14 @@ import (
2121

2222
. "github.com/onsi/ginkgo/v2"
2323
. "github.com/onsi/gomega"
24+
"github.com/onsi/gomega/format"
2425
corev1 "k8s.io/api/core/v1"
26+
"k8s.io/apimachinery/pkg/types"
2527
"k8s.io/utils/ptr"
28+
"sigs.k8s.io/controller-runtime/pkg/client"
2629

30+
infrav1alpha6 "sigs.k8s.io/cluster-api-provider-openstack/api/v1alpha6"
31+
infrav1alpha7 "sigs.k8s.io/cluster-api-provider-openstack/api/v1alpha7"
2732
infrav1 "sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1"
2833
)
2934

@@ -278,4 +283,54 @@ var _ = Describe("OpenStackMachine API validations", func() {
278283
Expect(k8sClient.Create(ctx, machine)).NotTo(Succeed(), "Creating a machine with an additional block device with an empty name availability zone should fail")
279284
})
280285
})
286+
287+
Context("v1alpha7", func() {
288+
It("should downgrade cleanly from infrav1", func() {
289+
infrav1Machine := &infrav1.OpenStackMachine{}
290+
infrav1Machine.Namespace = namespace.Name
291+
infrav1Machine.GenerateName = clusterNamePrefix
292+
infrav1Machine.Spec.IdentityRef = &infrav1.OpenStackIdentityReference{
293+
CloudName: "test-cloud",
294+
Name: "test-credentials",
295+
}
296+
infrav1Machine.Spec.Image.ID = ptr.To("de9872ee-0c2c-44ed-9414-90163c8b0e0d")
297+
Expect(createObj(infrav1Machine)).To(Succeed(), "infrav1 OpenStackMachine creation should succeed")
298+
299+
// Just fetching the object as v1alpha6 doesn't trigger
300+
// validation failure, so we first fetch it and then
301+
// patch the object with identical contents. The patch
302+
// triggers a validation failure.
303+
machine := &infrav1alpha7.OpenStackMachine{}
304+
Expect(k8sClient.Get(ctx, types.NamespacedName{Name: infrav1Machine.Name, Namespace: infrav1Machine.Namespace}, machine)).To(Succeed(), "OpenStackMachine fetch should succeed")
305+
306+
setObjectGVK(machine)
307+
machine.ManagedFields = nil
308+
Expect(k8sClient.Patch(ctx, machine, client.Apply, client.FieldOwner("test"), client.ForceOwnership)).To(Succeed(), format.Object(machine, 4))
309+
})
310+
})
311+
312+
Context("v1alpha6", func() {
313+
It("should downgrade cleanly from infrav1", func() {
314+
infrav1Machine := &infrav1.OpenStackMachine{}
315+
infrav1Machine.Namespace = namespace.Name
316+
infrav1Machine.GenerateName = clusterNamePrefix
317+
infrav1Machine.Spec.IdentityRef = &infrav1.OpenStackIdentityReference{
318+
CloudName: "test-cloud",
319+
Name: "test-credentials",
320+
}
321+
infrav1Machine.Spec.Image.ID = ptr.To("de9872ee-0c2c-44ed-9414-90163c8b0e0d")
322+
Expect(createObj(infrav1Machine)).To(Succeed(), "infrav1 OpenStackMachine creation should succeed")
323+
324+
// Just fetching the object as v1alpha6 doesn't trigger
325+
// validation failure, so we first fetch it and then
326+
// patch the object with identical contents. The patch
327+
// triggers a validation failure.
328+
machine := &infrav1alpha6.OpenStackMachine{} //nolint:staticcheck
329+
Expect(k8sClient.Get(ctx, types.NamespacedName{Name: infrav1Machine.Name, Namespace: infrav1Machine.Namespace}, machine)).To(Succeed(), "OpenStackMachine fetch should succeed")
330+
331+
setObjectGVK(machine)
332+
machine.ManagedFields = nil
333+
Expect(k8sClient.Patch(ctx, machine, client.Apply, client.FieldOwner("test"), client.ForceOwnership)).To(Succeed(), format.Object(machine, 4))
334+
})
335+
})
281336
})

test/e2e/suites/apivalidations/suite_test.go

+18
Original file line numberDiff line numberDiff line change
@@ -165,3 +165,21 @@ func createNamespace() *corev1.Namespace {
165165
By(fmt.Sprintf("Using namespace %s", namespace.Name))
166166
return &namespace
167167
}
168+
169+
func createObj(obj client.Object) error {
170+
err := k8sClient.Create(ctx, obj)
171+
if err == nil {
172+
DeferCleanup(func() error {
173+
return k8sClient.Delete(ctx, obj)
174+
})
175+
}
176+
return err
177+
}
178+
179+
func setObjectGVK(obj runtime.Object) {
180+
gvk, unversioned, err := testScheme.ObjectKinds(obj)
181+
Expect(unversioned).To(BeFalse(), "Object is considered unversioned")
182+
Expect(err).ToNot(HaveOccurred(), "Error fetching gvk for Object")
183+
Expect(gvk).To(HaveLen(1), "Object should have only one gvk")
184+
obj.GetObjectKind().SetGroupVersionKind(gvk[0])
185+
}

0 commit comments

Comments
 (0)