Skip to content

Commit cbfbb7c

Browse files
committed
Remove rabbitmqv1.RabbitMQUserCleanupBlockedFinalizer on cleanup
1 parent 17dd638 commit cbfbb7c

4 files changed

Lines changed: 201 additions & 20 deletions

File tree

internal/controller/dataplane/manage_service_finalizers.go

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -276,5 +276,27 @@ func (r *OpenStackDataPlaneNodeSetReconciler) manageServiceFinalizers(
276276
"updatedNodes", len(tracking.UpdatedNodes),
277277
"totalNodes", len(allNodeNames))
278278
}
279+
280+
// Remove the temporary cleanup-blocked finalizer if present
281+
// This finalizer was added by infra-operator as a temporary safeguard during
282+
// credential rotation, but should be removed now that proper finalizer management is in place
283+
if slices.Contains(rabbitmqUser.Finalizers, rabbitmqv1.RabbitMQUserCleanupBlockedFinalizer) {
284+
Log.Info("Removing temporary cleanup-blocked finalizer from RabbitMqUser",
285+
"user", rabbitmqUser.Name,
286+
"finalizer", rabbitmqv1.RabbitMQUserCleanupBlockedFinalizer)
287+
var newFinalizers []string
288+
for _, f := range rabbitmqUser.Finalizers {
289+
if f != rabbitmqv1.RabbitMQUserCleanupBlockedFinalizer {
290+
newFinalizers = append(newFinalizers, f)
291+
}
292+
}
293+
rabbitmqUser.Finalizers = newFinalizers
294+
err = r.Update(ctx, rabbitmqUser)
295+
if err != nil {
296+
Log.Error(err, "Failed to remove cleanup-blocked finalizer from RabbitMQUser",
297+
"user", rabbitmqUser.Name)
298+
// Don't fail reconciliation, just log the error
299+
}
300+
}
279301
}
280302
}

internal/controller/dataplane/openstackdataplanenodeset_controller.go

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ import (
5151

5252
"github.com/go-logr/logr"
5353
infranetworkv1 "github.com/openstack-k8s-operators/infra-operator/apis/network/v1beta1"
54+
rabbitmqv1 "github.com/openstack-k8s-operators/infra-operator/apis/rabbitmq/v1beta1"
5455
condition "github.com/openstack-k8s-operators/lib-common/modules/common/condition"
5556
"github.com/openstack-k8s-operators/lib-common/modules/common/helper"
5657
"github.com/openstack-k8s-operators/lib-common/modules/common/rolebinding"
@@ -1095,7 +1096,7 @@ func (r *OpenStackDataPlaneNodeSetReconciler) isNodesetFullyUpdated(
10951096
helper *helper.Helper,
10961097
nodeset *dataplanev1.OpenStackDataPlaneNodeSet,
10971098
serviceName string,
1098-
secretsLastModified map[string]time.Time,
1099+
_ map[string]time.Time,
10991100
) (bool, error) {
11001101
Log := r.GetLogger(ctx)
11011102

@@ -1269,13 +1270,18 @@ func (r *OpenStackDataPlaneNodeSetReconciler) reconcileDelete(
12691270
updatedFinalizers := []string{}
12701271

12711272
// Keep only finalizers that don't belong to this nodeset
1273+
// Also remove the temporary cleanup-blocked finalizer if present
12721274
for _, f := range originalFinalizers {
1273-
if !strings.HasPrefix(f, finalizerPrefix) {
1274-
updatedFinalizers = append(updatedFinalizers, f)
1275-
} else {
1275+
if strings.HasPrefix(f, finalizerPrefix) {
12761276
Log.Info("Removing finalizer from RabbitMQ user during nodeset deletion",
12771277
"user", user.GetName(),
12781278
"finalizer", f)
1279+
} else if f == rabbitmqv1.RabbitMQUserCleanupBlockedFinalizer {
1280+
Log.Info("Removing temporary cleanup-blocked finalizer from RabbitMQ user during nodeset deletion",
1281+
"user", user.GetName(),
1282+
"finalizer", f)
1283+
} else {
1284+
updatedFinalizers = append(updatedFinalizers, f)
12791285
}
12801286
}
12811287

internal/dataplane/rabbitmq_test.go

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ package deployment
1818

1919
import (
2020
"context"
21-
"fmt"
2221
"testing"
2322

2423
"github.com/openstack-k8s-operators/lib-common/modules/common/helper"
@@ -432,11 +431,11 @@ func TestGetNovaCellRabbitMqUserFromSecret_EdgeCases(t *testing.T) {
432431
Namespace: namespace,
433432
},
434433
Data: map[string][]byte{
435-
"01-nova.conf": []byte(fmt.Sprintf(`[DEFAULT]
434+
"01-nova.conf": []byte(`[DEFAULT]
436435
debug = true
437436
transport_url = rabbit://complex-user:p@ssw0rd@host1:5672,host2:5672,host3:5672/cell1
438437
log_dir = /var/log/nova
439-
`)),
438+
`),
440439
},
441440
}
442441

test/functional/dataplane/openstackdataplanenodeset_rabbitmq_finalizer_test.go

Lines changed: 167 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,7 @@ var _ = Describe("Dataplane NodeSet RabbitMQ Finalizer Management", func() {
136136
}
137137

138138
// Helper to simulate deployment completion
139-
SimulateDeploymentComplete := func(deploymentName types.NamespacedName, nodesetName string, ansibleLimit []string) {
139+
SimulateDeploymentComplete := func(deploymentName types.NamespacedName, nodesetName string, _ []string) {
140140
// First, complete the AnsibleEE jobs for each service
141141
deployment := GetDataplaneDeployment(deploymentName)
142142
nodeset := GetDataplaneNodeSet(types.NamespacedName{
@@ -273,7 +273,7 @@ var _ = Describe("Dataplane NodeSet RabbitMQ Finalizer Management", func() {
273273
})
274274

275275
AfterEach(func() {
276-
k8sClient.Delete(ctx, novaUser)
276+
_ = k8sClient.Delete(ctx, novaUser)
277277
})
278278

279279
It("Should add finalizer only after ALL nodes are updated in rolling deployment", func() {
@@ -444,7 +444,7 @@ var _ = Describe("Dataplane NodeSet RabbitMQ Finalizer Management", func() {
444444
})
445445

446446
AfterEach(func() {
447-
k8sClient.Delete(ctx, novaUser)
447+
_ = k8sClient.Delete(ctx, novaUser)
448448
})
449449

450450
It("Should add independent finalizers from each nodeset to shared user", func() {
@@ -618,8 +618,8 @@ var _ = Describe("Dataplane NodeSet RabbitMQ Finalizer Management", func() {
618618
})
619619

620620
AfterEach(func() {
621-
k8sClient.Delete(ctx, oldUser)
622-
k8sClient.Delete(ctx, newUser)
621+
_ = k8sClient.Delete(ctx, oldUser)
622+
_ = k8sClient.Delete(ctx, newUser)
623623
})
624624

625625
It("Should switch finalizer from old user to new user after rotation completes", func() {
@@ -792,9 +792,9 @@ var _ = Describe("Dataplane NodeSet RabbitMQ Finalizer Management", func() {
792792
})
793793

794794
AfterEach(func() {
795-
k8sClient.Delete(ctx, novaUser)
796-
k8sClient.Delete(ctx, neutronUser)
797-
k8sClient.Delete(ctx, ironicUser)
795+
_ = k8sClient.Delete(ctx, novaUser)
796+
_ = k8sClient.Delete(ctx, neutronUser)
797+
_ = k8sClient.Delete(ctx, ironicUser)
798798
})
799799

800800
It("Should manage service-specific finalizers independently across different clusters", func() {
@@ -945,7 +945,7 @@ var _ = Describe("Dataplane NodeSet RabbitMQ Finalizer Management", func() {
945945
})
946946

947947
AfterEach(func() {
948-
k8sClient.Delete(ctx, novaUser)
948+
_ = k8sClient.Delete(ctx, novaUser)
949949
})
950950

951951
It("Should use deployment completion time not creation time", func() {
@@ -1002,7 +1002,7 @@ var _ = Describe("Dataplane NodeSet RabbitMQ Finalizer Management", func() {
10021002
// Rotate secret (secret modified time = now, AFTER deployment creation)
10031003
UpdateNovaCellConfigSecret("cell1", "nova-user2", "rabbitmq-cell1")
10041004
newUser := CreateRabbitMQUser("nova-user2")
1005-
DeferCleanup(func() { k8sClient.Delete(ctx, newUser) })
1005+
DeferCleanup(func() { _ = k8sClient.Delete(ctx, newUser) })
10061006

10071007
// Wait for secret to propagate
10081008
time.Sleep(time.Second * 2)
@@ -1120,7 +1120,7 @@ var _ = Describe("Dataplane NodeSet RabbitMQ Finalizer Management", func() {
11201120
// Change secret (this triggers credential rotation)
11211121
UpdateNovaCellConfigSecret("cell1", "nova-user2", "rabbitmq-cell1")
11221122
newUser := CreateRabbitMQUser("nova-user2")
1123-
DeferCleanup(func() { k8sClient.Delete(ctx, newUser) })
1123+
DeferCleanup(func() { _ = k8sClient.Delete(ctx, newUser) })
11241124

11251125
// Wait for secret to propagate
11261126
time.Sleep(time.Second * 2)
@@ -1237,7 +1237,7 @@ var _ = Describe("Dataplane NodeSet RabbitMQ Finalizer Management", func() {
12371237
// Change secret to user2
12381238
UpdateNovaCellConfigSecret("cell1", "nova-user2", "rabbitmq-cell1")
12391239
user2 := CreateRabbitMQUser("nova-user2")
1240-
DeferCleanup(func() { k8sClient.Delete(ctx, user2) })
1240+
DeferCleanup(func() { _ = k8sClient.Delete(ctx, user2) })
12411241

12421242
// Wait for secret to propagate
12431243
time.Sleep(time.Second * 2)
@@ -1425,8 +1425,162 @@ var _ = Describe("Dataplane NodeSet RabbitMQ Finalizer Management", func() {
14251425
Eventually(func(g Gomega) {
14261426
nodeset := GetDataplaneNodeSet(dataplaneNodeSetName)
14271427
// Should have exactly 2 nodes defined (not 4 with hostName and ansibleHost)
1428-
g.Expect(len(nodeset.Spec.Nodes)).Should(Equal(2))
1428+
g.Expect(nodeset.Spec.Nodes).Should(HaveLen(2))
14291429
}, th.Timeout, th.Interval).Should(Succeed())
14301430
})
14311431
})
1432+
1433+
Context("When RabbitMQ users have cleanup-blocked finalizer", func() {
1434+
var novaUser *rabbitmqv1.RabbitMQUser
1435+
1436+
BeforeEach(func() {
1437+
// Create Nova cell config secret
1438+
CreateNovaCellConfigSecret("cell1", "nova-user1", "rabbitmq-cell1")
1439+
1440+
// Create RabbitMQ user with cleanup-blocked finalizer
1441+
novaUser = CreateRabbitMQUser("nova-user1")
1442+
1443+
// Manually add the cleanup-blocked finalizer to simulate migration scenario
1444+
Eventually(func(g Gomega) {
1445+
user := GetRabbitMQUser("nova-user1")
1446+
user.Finalizers = append(user.Finalizers, rabbitmqv1.RabbitMQUserCleanupBlockedFinalizer)
1447+
g.Expect(k8sClient.Update(ctx, user)).Should(Succeed())
1448+
}, timeout, interval).Should(Succeed())
1449+
1450+
// Setup infrastructure
1451+
netConfigName := types.NamespacedName{Namespace: namespace, Name: "dataplane-netconfig-cleanup"}
1452+
DeferCleanup(th.DeleteInstance, CreateNetConfig(netConfigName, DefaultNetConfigSpec()))
1453+
1454+
dnsMasqName := types.NamespacedName{Namespace: namespace, Name: "dnsmasq-cleanup"}
1455+
DeferCleanup(th.DeleteInstance, CreateDNSMasq(dnsMasqName, DefaultDNSMasqSpec()))
1456+
SimulateDNSMasqComplete(dnsMasqName)
1457+
1458+
CreateSSHSecret(types.NamespacedName{
1459+
Namespace: namespace,
1460+
Name: "nova-migration-ssh-key",
1461+
})
1462+
})
1463+
1464+
AfterEach(func() {
1465+
_ = k8sClient.Delete(ctx, novaUser)
1466+
})
1467+
1468+
It("Should remove cleanup-blocked finalizer during normal reconciliation", func() {
1469+
// Verify cleanup-blocked finalizer is present initially
1470+
Eventually(func(g Gomega) {
1471+
user := GetRabbitMQUser("nova-user1")
1472+
g.Expect(user.Finalizers).Should(ContainElement(rabbitmqv1.RabbitMQUserCleanupBlockedFinalizer))
1473+
}, timeout, interval).Should(Succeed())
1474+
1475+
// Create nodeset with Nova service
1476+
nodeSetSpec := map[string]interface{}{
1477+
"preProvisioned": true,
1478+
"services": []string{"nova"},
1479+
"nodes": map[string]dataplanev1.NodeSection{
1480+
"compute-0": {
1481+
HostName: "compute-0",
1482+
Ansible: dataplanev1.AnsibleOpts{
1483+
AnsibleHost: "192.168.122.100",
1484+
},
1485+
},
1486+
},
1487+
"nodeTemplate": map[string]interface{}{
1488+
"ansibleSSHPrivateKeySecret": "dataplane-ansible-ssh-private-key-secret",
1489+
"managementNetwork": "ctlplane",
1490+
"ansible": map[string]interface{}{
1491+
"ansibleUser": "cloud-admin",
1492+
},
1493+
},
1494+
}
1495+
dataplaneNodeSetName := types.NamespacedName{
1496+
Name: "edpm-nodeset-cleanup-test",
1497+
Namespace: namespace,
1498+
}
1499+
DeferCleanup(th.DeleteInstance, CreateDataplaneNodeSet(dataplaneNodeSetName, nodeSetSpec))
1500+
1501+
CreateSSHSecret(types.NamespacedName{
1502+
Namespace: namespace,
1503+
Name: "nova-migration-ssh-key",
1504+
})
1505+
1506+
SimulateIPSetComplete(types.NamespacedName{Namespace: namespace, Name: "compute-0"})
1507+
SimulateDNSDataComplete(dataplaneNodeSetName)
1508+
1509+
// Deploy the nodeset
1510+
deploymentName := types.NamespacedName{
1511+
Name: "cleanup-deployment",
1512+
Namespace: namespace,
1513+
}
1514+
deploymentSpec := map[string]interface{}{
1515+
"nodeSets": []string{dataplaneNodeSetName.Name},
1516+
}
1517+
DeferCleanup(th.DeleteInstance, CreateDataplaneDeployment(deploymentName, deploymentSpec))
1518+
SimulateDeploymentComplete(deploymentName, dataplaneNodeSetName.Name, []string{"compute-0"})
1519+
1520+
// Verify the cleanup-blocked finalizer is removed
1521+
Eventually(func(g Gomega) {
1522+
user := GetRabbitMQUser("nova-user1")
1523+
g.Expect(user.Finalizers).ShouldNot(ContainElement(rabbitmqv1.RabbitMQUserCleanupBlockedFinalizer),
1524+
"cleanup-blocked finalizer should be automatically removed during reconciliation")
1525+
}, timeout, interval).Should(Succeed())
1526+
1527+
// Verify our nodeset finalizer is still added
1528+
Eventually(func(g Gomega) {
1529+
user := GetRabbitMQUser("nova-user1")
1530+
hasNodesetFinalizer := false
1531+
for _, f := range user.Finalizers {
1532+
if len(f) > 11 && f[:11] == "nodeset.os/" {
1533+
hasNodesetFinalizer = true
1534+
break
1535+
}
1536+
}
1537+
g.Expect(hasNodesetFinalizer).Should(BeTrue(),
1538+
"nodeset finalizer should still be present")
1539+
}, timeout, interval).Should(Succeed())
1540+
})
1541+
1542+
It("Should remove cleanup-blocked finalizer during nodeset deletion", func() {
1543+
// Create a simple nodeset
1544+
nodeSetSpec := map[string]interface{}{
1545+
"preProvisioned": true,
1546+
"services": []string{"nova"},
1547+
"nodes": map[string]dataplanev1.NodeSection{
1548+
"compute-0": {
1549+
HostName: "compute-0",
1550+
},
1551+
},
1552+
"nodeTemplate": map[string]interface{}{
1553+
"ansibleSSHPrivateKeySecret": "dataplane-ansible-ssh-private-key-secret",
1554+
},
1555+
}
1556+
dataplaneNodeSetName := types.NamespacedName{
1557+
Name: "edpm-nodeset-deletion-cleanup",
1558+
Namespace: namespace,
1559+
}
1560+
CreateDataplaneNodeSet(dataplaneNodeSetName, nodeSetSpec)
1561+
1562+
// Verify cleanup-blocked finalizer is present
1563+
Eventually(func(g Gomega) {
1564+
user := GetRabbitMQUser("nova-user1")
1565+
g.Expect(user.Finalizers).Should(ContainElement(rabbitmqv1.RabbitMQUserCleanupBlockedFinalizer))
1566+
}, timeout, interval).Should(Succeed())
1567+
1568+
// Delete the nodeset
1569+
nodeset := GetDataplaneNodeSet(dataplaneNodeSetName)
1570+
Expect(k8sClient.Delete(ctx, nodeset)).Should(Succeed())
1571+
1572+
// Wait for nodeset to be deleted
1573+
Eventually(func(g Gomega) {
1574+
err := k8sClient.Get(ctx, dataplaneNodeSetName, nodeset)
1575+
g.Expect(err).Should(HaveOccurred())
1576+
}, timeout, interval).Should(Succeed())
1577+
1578+
// Verify cleanup-blocked finalizer is removed from the user
1579+
Eventually(func(g Gomega) {
1580+
user := GetRabbitMQUser("nova-user1")
1581+
g.Expect(user.Finalizers).ShouldNot(ContainElement(rabbitmqv1.RabbitMQUserCleanupBlockedFinalizer),
1582+
"cleanup-blocked finalizer should be removed during nodeset deletion")
1583+
}, timeout, interval).Should(Succeed())
1584+
})
1585+
})
14321586
})

0 commit comments

Comments
 (0)