Skip to content

Add application credential finalizer management#897

Open
Deydra71 wants to merge 1 commit into
openstack-k8s-operators:mainfrom
Deydra71:appcred-finalizer
Open

Add application credential finalizer management#897
Deydra71 wants to merge 1 commit into
openstack-k8s-operators:mainfrom
Deydra71:appcred-finalizer

Conversation

@Deydra71
Copy link
Copy Markdown
Contributor

@Deydra71 Deydra71 commented Apr 8, 2026

Jira: OSPRH-27509

Application Credential dev-doc: https://github.com/openstack-k8s-operators/dev-docs/blob/main/application_credentials.md

  • Tracks the active AC secret name in Status.ApplicationCredentialSecret
  • Add openstack.org/glance-ac-consumer finalizer to the AC secret after service config is rendered
  • On AC rotation, move the finalizer from the old secret to the new one
  • On CR deletion, remove the consumer finalizer from the AC secret before cleaning up the CR

This ensures that the keystone-operator cannot revoke a rotated AC secret while Glance is still consuming it.

2026-04-28T11:49:19Z	INFO	Controllers.GlanceAPI	Added consumer finalizer	{"controller": "glanceapi", "controllerGroup": "glance.openstack.org", "controllerKind": "GlanceAPI", "GlanceAPI": {"name":"glance-default-external","namespace":"openstack"}, "namespace": "openstack", "name": "glance-default-external", "reconcileID": "69f308d9-956f-455e-953a-f87848e4a2e9", "object": "ac-glance-cec48-secret", "finalizer": "openstack.org/glanceapi-default-ac-consumer"}
2026-04-28T11:49:19Z	INFO	Controllers.GlanceAPI	Removed consumer finalizer	{"controller": "glanceapi", "controllerGroup": "glance.openstack.org", "controllerKind": "GlanceAPI", "GlanceAPI": {"name":"glance-default-external","namespace":"openstack"}, "namespace": "openstack", "name": "glance-default-external", "reconcileID": "69f308d9-956f-455e-953a-f87848e4a2e9", "object": "ac-glance-79649-secret", "finalizer": "openstack.org/glanceapi-default-ac-consumer"}

Depends-On: openstack-k8s-operators/keystone-operator#685

Assisted-by: Claude Opus 4.6 noreply@anthropic.com

@softwarefactory-project-zuul
Copy link
Copy Markdown

Merge Failed.

This change or one of its cross-repo dependencies was unable to be automatically merged with the current state of its repository. Please rebase the change and upload a new patchset.
Warning:
Error merging github.com/openstack-k8s-operators/glance-operator for 897,5b62de205bac9f3f02708987c7274689942bef1a

@openshift-ci openshift-ci Bot requested review from fultonj and stuggi April 8, 2026 07:05
@Deydra71 Deydra71 requested a review from fmount April 8, 2026 07:05
@Deydra71 Deydra71 force-pushed the appcred-finalizer branch from 5b62de2 to ddd3396 Compare April 8, 2026 07:07
@softwarefactory-project-zuul
Copy link
Copy Markdown

Build failed (check pipeline). Post recheck (without leading slash)
to rerun all jobs. Make sure the failure cause has been resolved before
you rerun jobs.

https://softwarefactory-project.io/zuul/t/rdoproject.org/buildset/3f8e7ca5c69048048521ae38bacccecb

openstack-k8s-operators-content-provider FAILURE in 19m 23s
⚠️ glance-operator-kuttl SKIPPED Skipped due to failed job openstack-k8s-operators-content-provider
⚠️ glance-operator-tempest SKIPPED Skipped due to failed job openstack-k8s-operators-content-provider

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

// Manage AC consumer finalizer, the AC data was already read and rendered to the service config
if err := keystonev1.ManageACSecretFinalizer(ctx, helper, instance.Namespace,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should we call this block conditionally? if instance.Spec.Auth.ApplicationCredentialSecret is not set, we could skip this block (until L1005) entirely.
I'm also wondering if we should detect issues when err != nil and reflect it int the .Status through a dedicated condition.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Deydra71 after a better look into this, I think we can save us from setting up a new condition for the finalizer, but we could reuse condition.ServiceConfigReadyCondition.
The finalizer management runs after ServiceConfigReadyCondition is marked to true (L996). If ManageACSecretFinalizer fails, we technically marked the condition as true but the finalizer wasn't applied. This might generate a problem, so I was thinking about moving L996 after L1005, and setting ServiceConfigReady to false if there's a problem at this stage (L1003).

What do you think?

}

// Remove consumer finalizer from the AC secret GlanceAPI was consuming.
if err := keystonev1.RemoveACSecretConsumerFinalizer(ctx, helper, instance.Namespace,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The concern here is that you might have a misaligned .Spec.Auth.ApplicationCredentials with .Status.ApplicationCredentialSecret. On a rotation, if the reconciliation stops before being able to assign the LastAppliedCredentialSecret, and you delete the CR (e.g. oc delete glance glance), the finalizer might not be removed from the last created secret.
To be honest this is just theory, I didn't test it, but I'm wondering if we should take a note here for a follow up where we introduce or check ObservedGeneration in the reconciliation.

Comment thread internal/glance/const.go Outdated
// GlanceCachePruner -
GlanceCachePruner = "/usr/bin/glance-cache-pruner"
// ACConsumerFinalizer is added to AC secrets that GlanceAPI is actively consuming
ACConsumerFinalizer = "openstack.org/glanceapi-ac-consumer"
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is weird to me, and I have to check it more.
In the past I used to add/remove/update finalizers using controllerutil (see [1] as an example).
That way you can simply pass to the function exposed by keystone an helper and instance.APIName(), and you don't need to define a constant because the group is inferred by controllerutil.
However I didn't see the keystone function exposed here, so I might be totally wrong.
I'll check that part.

[1] https://github.com/openstack-k8s-operators/infra-operator/blob/main/apis/topology/v1beta1/topology_types.go#L166

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Deydra71 also using a constant in the form glanceapi-ac-consumer might be problematic for two reasons:

  1. we use to have / pattern, and we're breaking it
  2. assuming you have multiple glance APIs (which is pretty common in DCN, DZ and other custom topologies), you can technically use the same appCred for all of them by referencing the same secret. This can create a problem because you lose track of which api is using that secret from a finalizer perspective, and if I remove one of these APIs, I remove this constant finalizer from the secret for all of them, defeating the purpose of this patch.

Scenario#2 seems a blocker to me, and this is why we should use instance.APIName() as a finalizer for a given secret.

Comment thread internal/glance/const.go Outdated
// GlanceCachePruner -
GlanceCachePruner = "/usr/bin/glance-cache-pruner"
// ACConsumerFinalizer is added to AC secrets that GlanceAPI is actively consuming
ACConsumerFinalizer = "openstack.org/glanceapi-ac-consumer"
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Deydra71 also using a constant in the form glanceapi-ac-consumer might be problematic for two reasons:

  1. we use to have / pattern, and we're breaking it
  2. assuming you have multiple glance APIs (which is pretty common in DCN, DZ and other custom topologies), you can technically use the same appCred for all of them by referencing the same secret. This can create a problem because you lose track of which api is using that secret from a finalizer perspective, and if I remove one of these APIs, I remove this constant finalizer from the secret for all of them, defeating the purpose of this patch.

Scenario#2 seems a blocker to me, and this is why we should use instance.APIName() as a finalizer for a given secret.

@openshift-ci
Copy link
Copy Markdown
Contributor

openshift-ci Bot commented Apr 22, 2026

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by: Deydra71
Once this PR has been reviewed and has the lgtm label, please ask for approval from fmount. For more information see the Code Review Process.

The full list of commands accepted by this bot can be found here.

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@Deydra71
Copy link
Copy Markdown
Contributor Author

Hi @fmount ! Thank you the revieww.

I didn't initially realize that Glance creates multiple GlanceAPI instances per CR, so the original static ACConsumerFinalizer was incorrect. Now it's replaced with ACConsumerFinalizerName(instance.APIName()) which generates openstack.org/glanceapi-<apiName>-ac-consumer

As for the guard finalizer block - ManageACSecretFinalizer is now wrapped in:

if  instance.Spec.Auth.ApplicationCredentialSecret != "" || instance.Status.ApplicationCredentialSecret != "" 

so we skip the call when there's no AC secret.

Reordered ServiceConfigReadyCondition - moved MarkTrue(ServiceConfigReadyCondition) to after the finalizer block. If ManageACSecretFinalizer fails, we now explicitly set the condition to False with the error.

Removed finalizer from both spec and status secrets on delete - reconcileDelete now removes the consumer finalizer from both Status.ApplicationCredentialSecret and Spec.Auth.ApplicationCredentialSecret, handling the crash edge case. I t hink this is sufficient fix for the delete path for now, without the ObservedGeneration check.

I will also incorporate last three mentioned fixes to barbican, manila, cinder and swift operators since it's affecting them the same way.

@softwarefactory-project-zuul
Copy link
Copy Markdown

Build failed (check pipeline). Post recheck (without leading slash)
to rerun all jobs. Make sure the failure cause has been resolved before
you rerun jobs.

https://softwarefactory-project.io/zuul/t/rdoproject.org/buildset/9b0f066666f14a84b25869004f53cd53

openstack-k8s-operators-content-provider FAILURE in 15m 53s
⚠️ glance-operator-kuttl SKIPPED Skipped due to failed job openstack-k8s-operators-content-provider
⚠️ glance-operator-tempest SKIPPED Skipped due to failed job openstack-k8s-operators-content-provider

Signed-off-by: Veronika Fisarova <vfisarov@redhat.com>
@softwarefactory-project-zuul
Copy link
Copy Markdown

Build failed (check pipeline). Post recheck (without leading slash)
to rerun all jobs. Make sure the failure cause has been resolved before
you rerun jobs.

https://softwarefactory-project.io/zuul/t/rdoproject.org/buildset/6f0aed477bf2409dabb1d23ee21929ca

openstack-k8s-operators-content-provider FAILURE in 14m 15s
⚠️ glance-operator-kuttl SKIPPED Skipped due to failed job openstack-k8s-operators-content-provider
⚠️ glance-operator-tempest SKIPPED Skipped due to failed job openstack-k8s-operators-content-provider

@openshift-ci
Copy link
Copy Markdown
Contributor

openshift-ci Bot commented Apr 27, 2026

PR needs rebase.

Details

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants