Skip to content

Commit 1c93fae

Browse files
committed
Add AC finalizer management
Signed-off-by: Veronika Fisarova <vfisarov@redhat.com>
1 parent 3e1d979 commit 1c93fae

6 files changed

Lines changed: 250 additions & 0 deletions

File tree

api/bases/barbican.openstack.org_barbicans.yaml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -840,6 +840,13 @@ spec:
840840
status:
841841
description: BarbicanStatus defines the observed state of Barbican
842842
properties:
843+
applicationCredentialSecret:
844+
description: |-
845+
ApplicationCredentialSecret - the AC secret barbican is currently
846+
consuming and protecting with the openstack.org/barbican-ac-consumer
847+
finalizer. Tracked so the controller can remove its finalizer from the
848+
old secret when the openstack-operator rotates the reference.
849+
type: string
843850
barbicanAPIReadyCount:
844851
description: ReadyCount of Barbican API instances
845852
format: int32

api/v1beta1/barbican_types.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,12 @@ type BarbicanStatus struct {
155155
// Barbican Database Hostname
156156
DatabaseHostname string `json:"databaseHostname,omitempty"`
157157

158+
// ApplicationCredentialSecret - the AC secret barbican is currently
159+
// consuming and protecting with the openstack.org/barbican-ac-consumer
160+
// finalizer. Tracked so the controller can remove its finalizer from the
161+
// old secret when the openstack-operator rotates the reference.
162+
ApplicationCredentialSecret string `json:"applicationCredentialSecret,omitempty"`
163+
158164
// ObservedGeneration - the most recent generation observed for this
159165
// service. If the observed generation is less than the spec generation,
160166
// then the controller has not processed the latest changes injected by

config/crd/bases/barbican.openstack.org_barbicans.yaml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -840,6 +840,13 @@ spec:
840840
status:
841841
description: BarbicanStatus defines the observed state of Barbican
842842
properties:
843+
applicationCredentialSecret:
844+
description: |-
845+
ApplicationCredentialSecret - the AC secret barbican is currently
846+
consuming and protecting with the openstack.org/barbican-ac-consumer
847+
finalizer. Tracked so the controller can remove its finalizer from the
848+
old secret when the openstack-operator rotates the reference.
849+
type: string
843850
barbicanAPIReadyCount:
844851
description: ReadyCount of Barbican API instances
845852
format: int32

internal/barbican/const.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,4 +70,6 @@ const (
7070
BarbicanUID int64 = 42403
7171
// BarbicanGID - based on https://github.com/openstack-k8s-operators/tcib/blob/main/container-images/kolla/base/uid_gid_manage.sh
7272
BarbicanGID int64 = 42403
73+
// ACConsumerFinalizer is added to AC secrets that barbican is actively consuming
74+
ACConsumerFinalizer = "openstack.org/barbican-ac-consumer"
7375
)

internal/controller/barbican_controller.go

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -391,6 +391,11 @@ func (r *BarbicanReconciler) reconcileNormal(ctx context.Context, instance *barb
391391

392392
instance.Status.Conditions.MarkTrue(condition.ServiceConfigReadyCondition, condition.ServiceConfigReadyMessage)
393393

394+
// Manage consumer finalizer, the AC data was already read and rendered to the service config secret
395+
if err := r.manageACSecretFinalizer(ctx, helper, instance); err != nil {
396+
return ctrl.Result{}, err
397+
}
398+
394399
// networks to attach to
395400
nadList := []networkv1.NetworkAttachmentDefinition{}
396401
for _, netAtt := range instance.Spec.BarbicanAPI.NetworkAttachments {
@@ -611,13 +616,78 @@ func (r *BarbicanReconciler) reconcileDelete(ctx context.Context, instance *barb
611616
}
612617
}
613618

619+
// Remove consumer finalizer from the AC secret barbican was consuming.
620+
if instance.Status.ApplicationCredentialSecret != "" {
621+
acSecret := &corev1.Secret{}
622+
acKey := types.NamespacedName{
623+
Name: instance.Status.ApplicationCredentialSecret,
624+
Namespace: instance.Namespace,
625+
}
626+
if err := r.Get(ctx, acKey, acSecret); err != nil {
627+
if !k8s_errors.IsNotFound(err) {
628+
return ctrl.Result{}, err
629+
}
630+
Log.Info("AC secret already deleted, skipping consumer finalizer removal",
631+
"secret", instance.Status.ApplicationCredentialSecret)
632+
} else if err := object.RemoveConsumerFinalizer(ctx, helper, acSecret, barbican.ACConsumerFinalizer); err != nil {
633+
return ctrl.Result{}, err
634+
}
635+
}
636+
614637
// Service is deleted so remove the finalizer.
615638
controllerutil.RemoveFinalizer(instance, helper.GetFinalizer())
616639
Log.Info(fmt.Sprintf("Reconciled Service '%s' delete successfully", instance.Name))
617640

618641
return ctrl.Result{}, nil
619642
}
620643

644+
// manageACSecretFinalizer ensures barbican's consumer finalizer is present on
645+
// the AC secret it is currently consuming and absent from any previously-used
646+
// AC secret. Status.ApplicationCredentialSecret tracks which secret currently
647+
// carries the finalizer.
648+
func (r *BarbicanReconciler) manageACSecretFinalizer(
649+
ctx context.Context,
650+
h *helper.Helper,
651+
instance *barbicanv1beta1.Barbican,
652+
) error {
653+
newSecretName := instance.Spec.Auth.ApplicationCredentialSecret
654+
oldSecretName := instance.Status.ApplicationCredentialSecret
655+
656+
if newSecretName == oldSecretName {
657+
return nil
658+
}
659+
660+
var newObj, oldObj client.Object
661+
662+
if newSecretName != "" {
663+
secret := &corev1.Secret{}
664+
key := types.NamespacedName{Name: newSecretName, Namespace: instance.Namespace}
665+
if err := h.GetClient().Get(ctx, key, secret); err != nil {
666+
return fmt.Errorf("failed to get new AC secret %s: %w", newSecretName, err)
667+
}
668+
newObj = secret
669+
}
670+
671+
if oldSecretName != "" {
672+
secret := &corev1.Secret{}
673+
key := types.NamespacedName{Name: oldSecretName, Namespace: instance.Namespace}
674+
if err := h.GetClient().Get(ctx, key, secret); err != nil {
675+
if !k8s_errors.IsNotFound(err) {
676+
return fmt.Errorf("failed to get old AC secret %s: %w", oldSecretName, err)
677+
}
678+
} else {
679+
oldObj = secret
680+
}
681+
}
682+
683+
if err := object.ManageConsumerFinalizer(ctx, h, newObj, oldObj, barbican.ACConsumerFinalizer); err != nil {
684+
return err
685+
}
686+
687+
instance.Status.ApplicationCredentialSecret = newSecretName
688+
return nil
689+
}
690+
621691
// fields to index to reconcile when change
622692
const (
623693
passwordSecretField = ".spec.secret"

test/functional/barbican_controller_test.go

Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1803,6 +1803,164 @@ var _ = Describe("Barbican controller", func() {
18031803
})
18041804
})
18051805

1806+
When("ApplicationCredential consumer finalizer is managed", func() {
1807+
var (
1808+
acSecretName string
1809+
servicePasswordSecret string
1810+
)
1811+
1812+
BeforeEach(func() {
1813+
servicePasswordSecret = "ac-test-osp-secret" //nolint:gosec // G101
1814+
1815+
DeferCleanup(k8sClient.Delete, ctx,
1816+
CreateBarbicanMessageBusSecret(
1817+
barbicanTest.Instance.Namespace,
1818+
barbicanTest.RabbitmqSecretName,
1819+
),
1820+
)
1821+
DeferCleanup(k8sClient.Delete, ctx,
1822+
CreateBarbicanSecret(
1823+
barbicanTest.Instance.Namespace, servicePasswordSecret))
1824+
1825+
acSecretName = "ac-barbican-a1b2c-secret" //nolint:gosec // G101
1826+
secret := &corev1.Secret{
1827+
ObjectMeta: metav1.ObjectMeta{
1828+
Namespace: barbicanTest.Instance.Namespace,
1829+
Name: acSecretName,
1830+
},
1831+
Data: map[string][]byte{
1832+
keystonev1.ACIDSecretKey: []byte("a1b2ctest-ac-id"),
1833+
keystonev1.ACSecretSecretKey: []byte("test-ac-secret"),
1834+
},
1835+
}
1836+
DeferCleanup(k8sClient.Delete, ctx, secret)
1837+
Expect(k8sClient.Create(ctx, secret)).To(Succeed())
1838+
1839+
spec := GetDefaultBarbicanSpec()
1840+
spec["secret"] = servicePasswordSecret
1841+
spec["simpleCryptoBackendSecret"] = servicePasswordSecret
1842+
spec["auth"] = map[string]any{
1843+
"applicationCredentialSecret": acSecretName,
1844+
}
1845+
DeferCleanup(th.DeleteInstance,
1846+
CreateBarbican(barbicanTest.Instance, spec))
1847+
DeferCleanup(
1848+
mariadb.DeleteDBService,
1849+
mariadb.CreateDBService(
1850+
barbicanTest.Instance.Namespace,
1851+
GetBarbican(barbicanTest.Instance).Spec.DatabaseInstance,
1852+
corev1.ServiceSpec{
1853+
Ports: []corev1.ServicePort{{Port: 3306}}}))
1854+
1855+
DeferCleanup(keystone.DeleteKeystoneAPI,
1856+
keystone.CreateKeystoneAPI(barbicanTest.Instance.Namespace))
1857+
1858+
infra.SimulateTransportURLReady(barbicanTest.BarbicanTransportURL)
1859+
mariadb.SimulateMariaDBAccountCompleted(barbicanTest.BarbicanDatabaseAccount)
1860+
mariadb.SimulateMariaDBDatabaseCompleted(barbicanTest.BarbicanDatabaseName)
1861+
th.SimulateJobSuccess(barbicanTest.BarbicanDBSync)
1862+
keystone.SimulateKeystoneEndpointReady(barbicanTest.BarbicanKeystoneEndpoint)
1863+
})
1864+
1865+
It("should add the consumer finalizer to the AC secret", func() {
1866+
Eventually(func(g Gomega) {
1867+
secret := th.GetSecret(types.NamespacedName{
1868+
Namespace: barbicanTest.Instance.Namespace,
1869+
Name: acSecretName,
1870+
})
1871+
g.Expect(secret.Finalizers).To(
1872+
ContainElement(barbican.ACConsumerFinalizer))
1873+
}, timeout, interval).Should(Succeed())
1874+
})
1875+
1876+
It("should track the consumed AC secret in status", func() {
1877+
Eventually(func(g Gomega) {
1878+
b := GetBarbican(barbicanTest.Instance)
1879+
g.Expect(b.Status.ApplicationCredentialSecret).To(Equal(acSecretName))
1880+
}, timeout, interval).Should(Succeed())
1881+
})
1882+
1883+
It("should move the finalizer from the old to the new secret on rotation", func() {
1884+
// Wait for the initial finalizer to appear
1885+
Eventually(func(g Gomega) {
1886+
secret := th.GetSecret(types.NamespacedName{
1887+
Namespace: barbicanTest.Instance.Namespace,
1888+
Name: acSecretName,
1889+
})
1890+
g.Expect(secret.Finalizers).To(
1891+
ContainElement(barbican.ACConsumerFinalizer))
1892+
}, timeout, interval).Should(Succeed())
1893+
1894+
// Create a new AC secret
1895+
newACSecretName := "ac-barbican-x9y8z-secret" //nolint:gosec // G101
1896+
newSecret := &corev1.Secret{
1897+
ObjectMeta: metav1.ObjectMeta{
1898+
Namespace: barbicanTest.Instance.Namespace,
1899+
Name: newACSecretName,
1900+
},
1901+
Data: map[string][]byte{
1902+
keystonev1.ACIDSecretKey: []byte("x9y8zrotated-ac-id"),
1903+
keystonev1.ACSecretSecretKey: []byte("rotated-ac-secret"),
1904+
},
1905+
}
1906+
DeferCleanup(k8sClient.Delete, ctx, newSecret)
1907+
Expect(k8sClient.Create(ctx, newSecret)).To(Succeed())
1908+
1909+
// Update the Barbican CR to reference the new AC secret
1910+
Eventually(func(g Gomega) {
1911+
b := GetBarbican(barbicanTest.Instance)
1912+
b.Spec.Auth.ApplicationCredentialSecret = newACSecretName
1913+
g.Expect(k8sClient.Update(ctx, b)).Should(Succeed())
1914+
}, timeout, interval).Should(Succeed())
1915+
1916+
// New secret should gain the consumer finalizer
1917+
Eventually(func(g Gomega) {
1918+
secret := th.GetSecret(types.NamespacedName{
1919+
Namespace: barbicanTest.Instance.Namespace,
1920+
Name: newACSecretName,
1921+
})
1922+
g.Expect(secret.Finalizers).To(
1923+
ContainElement(barbican.ACConsumerFinalizer))
1924+
}, timeout, interval).Should(Succeed())
1925+
1926+
// Old secret should lose the consumer finalizer
1927+
Eventually(func(g Gomega) {
1928+
secret := th.GetSecret(types.NamespacedName{
1929+
Namespace: barbicanTest.Instance.Namespace,
1930+
Name: acSecretName,
1931+
})
1932+
g.Expect(secret.Finalizers).NotTo(
1933+
ContainElement(barbican.ACConsumerFinalizer))
1934+
}, timeout, interval).Should(Succeed())
1935+
1936+
// Status should reflect the new secret
1937+
Eventually(func(g Gomega) {
1938+
b := GetBarbican(barbicanTest.Instance)
1939+
g.Expect(b.Status.ApplicationCredentialSecret).To(Equal(newACSecretName))
1940+
}, timeout, interval).Should(Succeed())
1941+
})
1942+
1943+
It("should remove the consumer finalizer from AC secret on CR deletion", func() {
1944+
Eventually(func(g Gomega) {
1945+
secret := th.GetSecret(types.NamespacedName{
1946+
Namespace: barbicanTest.Instance.Namespace,
1947+
Name: acSecretName,
1948+
})
1949+
g.Expect(secret.Finalizers).To(
1950+
ContainElement(barbican.ACConsumerFinalizer))
1951+
}, timeout, interval).Should(Succeed())
1952+
1953+
th.DeleteInstance(GetBarbican(barbicanTest.Instance))
1954+
1955+
secret := th.GetSecret(types.NamespacedName{
1956+
Namespace: barbicanTest.Instance.Namespace,
1957+
Name: acSecretName,
1958+
})
1959+
Expect(secret.Finalizers).NotTo(
1960+
ContainElement(barbican.ACConsumerFinalizer))
1961+
})
1962+
})
1963+
18061964
// Run MariaDBAccount suite tests. these are pre-packaged ginkgo tests
18071965
// that exercise standard account create / update patterns that should be
18081966
// common to all controllers that ensure MariaDBAccount CRs.

0 commit comments

Comments
 (0)