Skip to content

Commit 4ed6860

Browse files
authored
Merge pull request #3104 from larainema/fix/test-sg-failure-clears-ready-condition
🌱 Add regression test for security group failure on previously ready cluster
2 parents 10e5009 + 313c884 commit 4ed6860

File tree

1 file changed

+104
-0
lines changed

1 file changed

+104
-0
lines changed

controllers/openstackcluster_controller_test.go

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1161,6 +1161,110 @@ var _ = Describe("OpenStackCluster controller", func() {
11611161
Expect(conditions.IsTrue(testCluster, infrav1.NetworkReadyCondition)).To(BeTrue())
11621162
})
11631163

1164+
It("should clear ReadyCondition when security group reconciliation fails on a previously ready cluster", func() {
1165+
const clusterNetworkID = "6c90b532-7ba0-418a-a276-5ae55060b5b0"
1166+
const clusterSubnetID = "cad5a91a-36de-4388-823b-b0cc82cadfdc"
1167+
1168+
testCluster.SetName("sg-failure-previously-ready")
1169+
testCluster.Spec = infrav1.OpenStackClusterSpec{
1170+
IdentityRef: infrav1.OpenStackIdentityReference{
1171+
Name: "test-creds",
1172+
CloudName: "openstack",
1173+
},
1174+
Network: &infrav1.NetworkParam{
1175+
ID: ptr.To(clusterNetworkID),
1176+
},
1177+
DisableExternalNetwork: ptr.To(true),
1178+
DisableAPIServerFloatingIP: ptr.To(true),
1179+
APIServerFixedIP: ptr.To("192.168.0.10"),
1180+
ManagedSecurityGroups: &infrav1.ManagedSecurityGroups{
1181+
AllNodesSecurityGroupRules: []infrav1.SecurityGroupRuleSpec{
1182+
{
1183+
Direction: "ingress",
1184+
Protocol: ptr.To("tcp"),
1185+
RemoteManagedGroups: []infrav1.ManagedSecurityGroupName{
1186+
"worker",
1187+
},
1188+
},
1189+
},
1190+
},
1191+
}
1192+
err := k8sClient.Create(ctx, testCluster)
1193+
Expect(err).To(BeNil())
1194+
err = k8sClient.Create(ctx, capiCluster)
1195+
Expect(err).To(BeNil())
1196+
1197+
// Simulate a previously successful reconcile by pre-setting conditions to True.
1198+
// This is the scenario from https://github.com/kubernetes-sigs/cluster-api-provider-openstack/issues/2993
1199+
// where the cluster was already Ready and a subsequent security group update fails.
1200+
conditions.Set(testCluster, metav1.Condition{
1201+
Type: clusterv1.ReadyCondition,
1202+
Status: metav1.ConditionTrue,
1203+
Reason: infrav1.ReadyConditionReason,
1204+
})
1205+
conditions.Set(testCluster, metav1.Condition{
1206+
Type: infrav1.SecurityGroupsReadyCondition,
1207+
Status: metav1.ConditionTrue,
1208+
Reason: infrav1.ReadyConditionReason,
1209+
})
1210+
conditions.Set(testCluster, metav1.Condition{
1211+
Type: infrav1.NetworkReadyCondition,
1212+
Status: metav1.ConditionTrue,
1213+
Reason: infrav1.ReadyConditionReason,
1214+
})
1215+
1216+
// Verify preconditions: cluster appears Ready
1217+
Expect(conditions.IsTrue(testCluster, clusterv1.ReadyCondition)).To(BeTrue())
1218+
Expect(conditions.IsTrue(testCluster, infrav1.SecurityGroupsReadyCondition)).To(BeTrue())
1219+
1220+
log := GinkgoLogr
1221+
clientScope, err := mockScopeFactory.NewClientScopeFromObject(ctx, k8sClient, nil, log, testCluster)
1222+
Expect(err).To(BeNil())
1223+
scope := scope.NewWithLogger(clientScope, log)
1224+
1225+
networkClientRecorder := mockScopeFactory.NetworkClient.EXPECT()
1226+
1227+
// Network lookup succeeds
1228+
networkClientRecorder.GetNetwork(clusterNetworkID).Return(&networks.Network{
1229+
ID: clusterNetworkID,
1230+
Name: "cluster-network",
1231+
}, nil)
1232+
1233+
// Subnet lookup succeeds
1234+
networkClientRecorder.ListSubnet(subnets.ListOpts{
1235+
NetworkID: clusterNetworkID,
1236+
}).Return([]subnets.Subnet{
1237+
{
1238+
ID: clusterSubnetID,
1239+
Name: "cluster-subnet",
1240+
CIDR: "192.168.0.0/24",
1241+
},
1242+
}, nil)
1243+
1244+
// Security group reconciliation fails (e.g. rule conflict as reported in #2993)
1245+
networkClientRecorder.ListSecGroup(gomock.Any()).Return([]groups.SecGroup{}, nil).AnyTimes()
1246+
networkClientRecorder.CreateSecGroup(gomock.Any()).Return(nil, fmt.Errorf("SecurityGroupRuleExists")).AnyTimes()
1247+
1248+
err = reconcileNetworkComponents(scope, capiCluster, testCluster)
1249+
Expect(err).ToNot(BeNil())
1250+
Expect(err.Error()).To(ContainSubstring("failed to reconcile security groups"))
1251+
1252+
// Verify ReadyCondition is now False (was True before)
1253+
Expect(conditions.IsFalse(testCluster, clusterv1.ReadyCondition)).To(BeTrue())
1254+
readyCondition := conditions.Get(testCluster, clusterv1.ReadyCondition)
1255+
Expect(readyCondition).ToNot(BeNil())
1256+
Expect(readyCondition.Reason).To(Equal(infrav1.OpenStackErrorReason))
1257+
1258+
// Verify SecurityGroupsReadyCondition is now False (was True before)
1259+
Expect(conditions.IsFalse(testCluster, infrav1.SecurityGroupsReadyCondition)).To(BeTrue())
1260+
sgCondition := conditions.Get(testCluster, infrav1.SecurityGroupsReadyCondition)
1261+
Expect(sgCondition).ToNot(BeNil())
1262+
Expect(sgCondition.Reason).To(Equal(infrav1.SecurityGroupReconcileFailedReason))
1263+
1264+
// NetworkReadyCondition should remain True since network reconciliation succeeded
1265+
Expect(conditions.IsTrue(testCluster, infrav1.NetworkReadyCondition)).To(BeTrue())
1266+
})
1267+
11641268
It("should set APIEndpointReadyCondition to False when floating IP creation fails", func() {
11651269
const externalNetworkID = "a42211a2-4d2c-426f-9413-830e4b4abbbc"
11661270
const clusterNetworkID = "6c90b532-7ba0-418a-a276-5ae55060b5b0"

0 commit comments

Comments
 (0)