@@ -21,24 +21,31 @@ package hcp
21
21
22
22
import (
23
23
"context"
24
+ "fmt"
25
+ "time"
24
26
25
27
. "github.com/onsi/ginkgo/v2"
26
28
. "github.com/onsi/gomega"
27
29
30
+ "github.com/gophercloud/gophercloud/openstack/compute/v2/servers"
28
31
corev1 "k8s.io/api/core/v1"
29
32
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
30
33
"k8s.io/apimachinery/pkg/types"
34
+ "k8s.io/utils/ptr"
31
35
"sigs.k8s.io/controller-runtime/pkg/client"
32
36
33
37
infrav1 "sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1"
34
38
"sigs.k8s.io/cluster-api-provider-openstack/test/e2e/shared"
39
+ clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
40
+ "sigs.k8s.io/cluster-api/test/framework"
35
41
)
36
42
37
43
// NetworkValidationInput contains the input for network validation tests
38
44
type NetworkValidationInput struct {
39
45
WorkloadClusterProxy * shared.ClusterProxy
40
46
Namespace string
41
47
ClusterName string
48
+ E2EContext * shared.E2EContext
42
49
}
43
50
44
51
// ValidateNetworkConfiguration tests the specific network edge cases fixed in hcp-2380
@@ -54,6 +61,9 @@ func ValidateNetworkConfiguration(ctx context.Context, input NetworkValidationIn
54
61
By ("Testing security group precedence" )
55
62
validateSecurityGroupPrecedence (ctx , input )
56
63
64
+ By ("Testing security group precedence with a live client" )
65
+ validateSecurityGroupPrecedenceWithClient (ctx , input )
66
+
57
67
By ("Testing port configuration edge cases" )
58
68
validatePortConfigurationEdgeCases (ctx , input )
59
69
@@ -179,6 +189,116 @@ func validateSecurityGroupPrecedence(ctx context.Context, input NetworkValidatio
179
189
}
180
190
}
181
191
192
+ // validateSecurityGroupPrecedenceWithClient creates a machine with a specific security group
193
+ // and verifies with the OpenStack client that it's the only one applied.
194
+ func validateSecurityGroupPrecedenceWithClient (ctx context.Context , input NetworkValidationInput ) {
195
+ shared .Logf ("Validating security group precedence with a live client" )
196
+
197
+ // Get a compute client
198
+ computeClient , err := shared .NewComputeClient (input .E2EContext )
199
+ Expect (err ).ToNot (HaveOccurred (), "Failed to create compute client" )
200
+
201
+ // Define a new security group to be used exclusively for this test
202
+ openStackCluster := & infrav1.OpenStackCluster {}
203
+ err = input .WorkloadClusterProxy .GetClient ().Get (ctx , types.NamespacedName {
204
+ Namespace : input .Namespace ,
205
+ Name : input .ClusterName ,
206
+ }, openStackCluster )
207
+ Expect (err ).ToNot (HaveOccurred (), "Failed to get OpenStackCluster" )
208
+
209
+ sgName := "e2e-sg-override-test"
210
+ sg , err := shared .CreateSecurityGroup (input .E2EContext , sgName )
211
+ Expect (err ).ToNot (HaveOccurred (), "Failed to create security group for test" )
212
+ defer func () {
213
+ Expect (shared .DeleteSecurityGroup (input .E2EContext , sg .ID )).To (Succeed ())
214
+ }()
215
+
216
+ // Create a new Machine and OpenStackMachine with the override security group
217
+ machineName := fmt .Sprintf ("%s-sg-override" , input .ClusterName )
218
+ shared .Logf ("Creating machine %s with security group %s" , machineName , sg .Name )
219
+
220
+ machine := & clusterv1.Machine {
221
+ ObjectMeta : metav1.ObjectMeta {
222
+ Name : machineName ,
223
+ Namespace : input .Namespace ,
224
+ Labels : map [string ]string {
225
+ clusterv1 .ClusterNameLabel : input .ClusterName ,
226
+ },
227
+ },
228
+ Spec : clusterv1.MachineSpec {
229
+ ClusterName : input .ClusterName ,
230
+ Version : ptr .To (e2eCtx .E2EConfig .Variables [shared .KubernetesVersion ]),
231
+ Bootstrap : clusterv1.Bootstrap {
232
+ ConfigRef : & corev1.ObjectReference {
233
+ APIVersion : "bootstrap.cluster.x-k8s.io/v1beta1" ,
234
+ Kind : "KubeadmConfigTemplate" ,
235
+ Name : fmt .Sprintf ("%s-md-0" , input .ClusterName ),
236
+ },
237
+ },
238
+ InfrastructureRef : corev1.ObjectReference {
239
+ APIVersion : "infrastructure.cluster.x-k8s.io/v1beta1" ,
240
+ Kind : "OpenStackMachine" ,
241
+ Name : machineName ,
242
+ },
243
+ },
244
+ }
245
+
246
+ openStackMachine := & infrav1.OpenStackMachine {
247
+ ObjectMeta : metav1.ObjectMeta {
248
+ Name : machineName ,
249
+ Namespace : input .Namespace ,
250
+ },
251
+ Spec : infrav1.OpenStackMachineSpec {
252
+ // Explicitly set the security group, overriding any cluster defaults
253
+ SecurityGroups : []infrav1.SecurityGroupParam {
254
+ {ID : & sg .ID },
255
+ },
256
+ Flavor : e2eCtx .E2EConfig .Variables [shared .OpenstackNodeMachineFlavor ],
257
+ Image : infrav1.ImageParam {Filter : & infrav1.ImageFilter {Name : & e2eCtx .E2EConfig .Variables [shared .OpenstackImageName ]}},
258
+ SSHKeyName : e2eCtx .E2EConfig .Variables [shared .OpenstackSSHKeyName ],
259
+ },
260
+ }
261
+
262
+ // Create the resources
263
+ err = input .WorkloadClusterProxy .GetClient ().Create (ctx , openStackMachine )
264
+ Expect (err ).ToNot (HaveOccurred (), "Failed to create OpenStackMachine" )
265
+ err = input .WorkloadClusterProxy .GetClient ().Create (ctx , machine )
266
+ Expect (err ).ToNot (HaveOccurred (), "Failed to create Machine" )
267
+
268
+ // Wait for the machine to get an instance ID and become ready
269
+ shared .Logf ("Waiting for machine %s to become ready" , machineName )
270
+ framework .WaitForMachineReady (ctx , framework.WaitForMachineReadyInput {
271
+ Getter : input .WorkloadClusterProxy .GetClient (),
272
+ Cluster : & clusterv1.Cluster {ObjectMeta : metav1.ObjectMeta {Name : input .ClusterName , Namespace : input .Namespace }},
273
+ Machine : machine ,
274
+ Timeout : 20 * time .Minute ,
275
+ Intervals : e2eCtx .E2EConfig .GetIntervals (specName , "wait-worker-nodes" ),
276
+ })
277
+
278
+ // Get the updated OpenStackMachine to find its instance ID
279
+ err = input .WorkloadClusterProxy .GetClient ().Get (ctx , client .ObjectKeyFromObject (openStackMachine ), openStackMachine )
280
+ Expect (err ).ToNot (HaveOccurred (), "Failed to get updated OpenStackMachine" )
281
+ Expect (openStackMachine .Status .InstanceID ).ToNot (BeNil (), "InstanceID should not be nil" )
282
+
283
+ // Use the compute client to verify the security groups on the live instance
284
+ shared .Logf ("Verifying security groups on instance %s" , * openStackMachine .Status .InstanceID )
285
+ server , err := servers .Get (computeClient , * openStackMachine .Status .InstanceID ).Extract ()
286
+ Expect (err ).ToNot (HaveOccurred (), "Failed to get server details from OpenStack" )
287
+
288
+ // Assert that the ONLY security group is the one we specified
289
+ Expect (server .SecurityGroups ).To (HaveLen (1 ), "Should only have one security group" )
290
+ Expect (server .SecurityGroups [0 ].(map [string ]interface {})["name" ]).To (Equal (sg .Name ), "The applied security group should be the override one" )
291
+
292
+ shared .Logf ("Successfully verified security group override" )
293
+
294
+ // Clean up the machine
295
+ shared .Logf ("Deleting machine %s" , machineName )
296
+ err = input .WorkloadClusterProxy .GetClient ().Delete (ctx , machine )
297
+ Expect (err ).ToNot (HaveOccurred (), "Failed to delete machine" )
298
+ err = input .WorkloadClusterProxy .GetClient ().Delete (ctx , openStackMachine )
299
+ Expect (err ).ToNot (HaveOccurred (), "Failed to delete OpenStackMachine" )
300
+ }
301
+
182
302
// validatePortConfigurationEdgeCases tests edge cases in port configuration
183
303
func validatePortConfigurationEdgeCases (ctx context.Context , input NetworkValidationInput ) {
184
304
shared .Logf ("Validating port configuration edge cases" )
0 commit comments