Skip to content

Conversation

@Deydra71
Copy link
Contributor

@Deydra71 Deydra71 commented May 7, 2025

OSPRH-14738

This PR add ApplicationCredential support enabling both global defaults and service-specific overrides in OpenStackControlPlane.

CRD updates:

  • New top-level spec.applicationCredential section with enabled, expirationDays and gracePeriodDays
  • Added applicationCredential field to each service section that has a corresponding Keystone user
  • Defaults guarantee visible enabled:false in every supported service, while expirationDays and gracePeriodDays are hidden unless specified directly (in that case global values are used).

Controller logic:

  • To enable AppCred both global and service specific must be enable: true
  • For each enabled service, controller creates AC CR
  • Deletes service AC CRs when either global or service enabled is turned off

Example:

 spec:
  applicationCredential:
    enabled: true
    expirationDays: 14
    gracePeriodDays: 7
  barbican:
    applicationCredential:
      enabled: true
      expirationDays: 2
      gracePeriodDays: 1
  cinder:
    applicationCredential:
      enabled: true

In the example barbican is using days overrides, while cinder is using default values.

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

@openshift-ci
Copy link
Contributor

openshift-ci bot commented May 7, 2025

Skipping CI for Draft Pull Request.
If you want CI signal for your change, please convert it to an actual PR.
You can still manually trigger a test run with /test all

@softwarefactory-project-zuul
Copy link

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/openstack-operator for 1430,edb1584a0e480eb9b099e45478bca2c880c67006

@softwarefactory-project-zuul
Copy link

Unable to freeze job graph: Job adoption-standalone-to-crc-ceph-provider depends on openstack-k8s-operators-content-provider which was not run.

@danpawlik
Copy link
Contributor

recheck

@softwarefactory-project-zuul
Copy link

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/fb95ce639e164bb190aa3b41fcda82da

✔️ openstack-k8s-operators-content-provider SUCCESS in 1h 59m 19s
podified-multinode-edpm-deployment-crc FAILURE in 1h 38m 01s
✔️ cifmw-crc-podified-edpm-baremetal SUCCESS in 1h 31m 54s
adoption-standalone-to-crc-ceph-provider FAILURE in 1h 39m 09s
✔️ openstack-operator-tempest-multinode SUCCESS in 1h 31m 34s
openstack-operator-kuttl FAILURE in 28m 08s (non-voting)

@softwarefactory-project-zuul
Copy link

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/64ab1660f5ff460cb0bdb2682d3b4149

openstack-k8s-operators-content-provider FAILURE in 15m 15s
⚠️ podified-multinode-edpm-deployment-crc SKIPPED Skipped due to failed job openstack-k8s-operators-content-provider
⚠️ cifmw-crc-podified-edpm-baremetal SKIPPED Skipped due to failed job openstack-k8s-operators-content-provider
⚠️ adoption-standalone-to-crc-ceph-provider SKIPPED Skipped due to failed job openstack-k8s-operators-content-provider
⚠️ openstack-operator-tempest-multinode SKIPPED Skipped due to failed job openstack-k8s-operators-content-provider
⚠️ openstack-operator-kuttl SKIPPED Skipped due to failed job openstack-k8s-operators-content-provider (non-voting)

@softwarefactory-project-zuul
Copy link

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/5ebbd01a8bc549b2956a87c3507363e2

openstack-k8s-operators-content-provider FAILURE in 13m 33s
⚠️ podified-multinode-edpm-deployment-crc SKIPPED Skipped due to failed job openstack-k8s-operators-content-provider
⚠️ cifmw-crc-podified-edpm-baremetal SKIPPED Skipped due to failed job openstack-k8s-operators-content-provider
⚠️ adoption-standalone-to-crc-ceph-provider SKIPPED Skipped due to failed job openstack-k8s-operators-content-provider
⚠️ openstack-operator-tempest-multinode SKIPPED Skipped due to failed job openstack-k8s-operators-content-provider
⚠️ openstack-operator-kuttl SKIPPED Skipped due to failed job openstack-k8s-operators-content-provider (non-voting)

@softwarefactory-project-zuul
Copy link

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/67ea6f17ebce4477b46d077171537d98

openstack-k8s-operators-content-provider FAILURE in 13m 49s
⚠️ podified-multinode-edpm-deployment-crc SKIPPED Skipped due to failed job openstack-k8s-operators-content-provider
⚠️ cifmw-crc-podified-edpm-baremetal SKIPPED Skipped due to failed job openstack-k8s-operators-content-provider
⚠️ adoption-standalone-to-crc-ceph-provider SKIPPED Skipped due to failed job openstack-k8s-operators-content-provider
⚠️ openstack-operator-tempest-multinode SKIPPED Skipped due to failed job openstack-k8s-operators-content-provider
⚠️ openstack-operator-kuttl SKIPPED Skipped due to failed job openstack-k8s-operators-content-provider (non-voting)

@softwarefactory-project-zuul
Copy link

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/65697c077ffe4630a698b4a94657a08a

openstack-k8s-operators-content-provider FAILURE in 16m 43s
⚠️ podified-multinode-edpm-deployment-crc SKIPPED Skipped due to failed job openstack-k8s-operators-content-provider
⚠️ cifmw-crc-podified-edpm-baremetal SKIPPED Skipped due to failed job openstack-k8s-operators-content-provider
⚠️ adoption-standalone-to-crc-ceph-provider SKIPPED Skipped due to failed job openstack-k8s-operators-content-provider
⚠️ openstack-operator-tempest-multinode SKIPPED Skipped due to failed job openstack-k8s-operators-content-provider
⚠️ openstack-operator-kuttl SKIPPED Skipped due to failed job openstack-k8s-operators-content-provider (non-voting)

@softwarefactory-project-zuul
Copy link

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/8b21d132d2c944f4bb8faccfff711e47

openstack-k8s-operators-content-provider FAILURE in 14m 09s
⚠️ podified-multinode-edpm-deployment-crc SKIPPED Skipped due to failed job openstack-k8s-operators-content-provider
⚠️ cifmw-crc-podified-edpm-baremetal SKIPPED Skipped due to failed job openstack-k8s-operators-content-provider
⚠️ adoption-standalone-to-crc-ceph-provider SKIPPED Skipped due to failed job openstack-k8s-operators-content-provider
⚠️ openstack-operator-tempest-multinode SKIPPED Skipped due to failed job openstack-k8s-operators-content-provider
⚠️ openstack-operator-kuttl SKIPPED Skipped due to failed job openstack-k8s-operators-content-provider (non-voting)

@Deydra71 Deydra71 force-pushed the appcred-support branch 2 times, most recently from 62fd3e5 to 27db2bd Compare May 21, 2025 09:16
@softwarefactory-project-zuul
Copy link

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/openstack-operator for 1430,27db2bd11155d687612f5ae130e8cb42336647e1

@Deydra71 Deydra71 force-pushed the appcred-support branch 2 times, most recently from e9e9a01 to 7e74505 Compare November 11, 2025 08:40
@softwarefactory-project-zuul
Copy link

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/dd42cbd419dc474287f25fb88d135e5e

openstack-k8s-operators-content-provider FAILURE in 15m 35s
⚠️ podified-multinode-edpm-deployment-crc SKIPPED Skipped due to failed job openstack-k8s-operators-content-provider
⚠️ cifmw-crc-podified-edpm-baremetal SKIPPED Skipped due to failed job openstack-k8s-operators-content-provider
⚠️ adoption-standalone-to-crc-ceph-provider SKIPPED Skipped due to failed job openstack-k8s-operators-content-provider
⚠️ openstack-operator-tempest-multinode SKIPPED Skipped due to failed job openstack-k8s-operators-content-provider

@softwarefactory-project-zuul
Copy link

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/60f380b19b72420684a8b9ba527bb1db

openstack-k8s-operators-content-provider FAILURE in 18m 52s
⚠️ podified-multinode-edpm-deployment-crc SKIPPED Skipped due to failed job openstack-k8s-operators-content-provider
⚠️ cifmw-crc-podified-edpm-baremetal SKIPPED Skipped due to failed job openstack-k8s-operators-content-provider
⚠️ adoption-standalone-to-crc-ceph-provider SKIPPED Skipped due to failed job openstack-k8s-operators-content-provider
⚠️ openstack-operator-tempest-multinode SKIPPED Skipped due to failed job openstack-k8s-operators-content-provider

@softwarefactory-project-zuul
Copy link

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/650f861e266649e684e94e8e38236ca9

openstack-k8s-operators-content-provider FAILURE in 13m 53s
⚠️ podified-multinode-edpm-deployment-crc SKIPPED Skipped due to failed job openstack-k8s-operators-content-provider
⚠️ cifmw-crc-podified-edpm-baremetal SKIPPED Skipped due to failed job openstack-k8s-operators-content-provider
⚠️ adoption-standalone-to-crc-ceph-provider SKIPPED Skipped due to failed job openstack-k8s-operators-content-provider
⚠️ openstack-operator-tempest-multinode SKIPPED Skipped due to failed job openstack-k8s-operators-content-provider

@softwarefactory-project-zuul
Copy link

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/fe0e210d9f7d44429cfde38f83a800be

openstack-k8s-operators-content-provider FAILURE in 18m 57s
⚠️ podified-multinode-edpm-deployment-crc SKIPPED Skipped due to failed job openstack-k8s-operators-content-provider
⚠️ cifmw-crc-podified-edpm-baremetal SKIPPED Skipped due to failed job openstack-k8s-operators-content-provider
⚠️ adoption-standalone-to-crc-ceph-provider SKIPPED Skipped due to failed job openstack-k8s-operators-content-provider
⚠️ openstack-operator-tempest-multinode SKIPPED Skipped due to failed job openstack-k8s-operators-content-provider

Copy link
Contributor

@vyzigold vyzigold left a comment

Choose a reason for hiding this comment

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

In telemetry, we have an enabled switch for the whole telemetry similarly to other services to disable / enable the whole telemetry. But we also allow to enable / disable each individual part of telemetry, so we have additional enabled switches for aodh, ceilometer and cloudkitty, which I think should be also used to determine whether to create the application credentials. See my suggestions.

{"glance", instance.Spec.Glance.Enabled, instance.Spec.Glance.ApplicationCredential},
{"nova", instance.Spec.Nova.Enabled, instance.Spec.Nova.ApplicationCredential},
{"swift", instance.Spec.Swift.Enabled, instance.Spec.Swift.ApplicationCredential},
{"ceilometer", instance.Spec.Telemetry.Enabled, instance.Spec.Telemetry.ApplicationCredentialCeilometer},
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
{"ceilometer", instance.Spec.Telemetry.Enabled, instance.Spec.Telemetry.ApplicationCredentialCeilometer},
{"ceilometer", instance.Spec.Telemetry.Enabled && instance.Spec.Telemetry.Template.Ceilometer.Enabled, instance.Spec.Telemetry.ApplicationCredentialCeilometer},

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, good catch. We don't want to rely on solely "disabled implies empty template fields"

Copy link
Contributor Author

@Deydra71 Deydra71 Jan 15, 2026

Choose a reason for hiding this comment

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

Note: I can't accept suggestions directly, because we need dereference the pointer:

{"ceilometer",
			instance.Spec.Telemetry.Enabled &&
				instance.Spec.Telemetry.Template.Ceilometer.Enabled != nil &&
				*instance.Spec.Telemetry.Template.Ceilometer.Enabled,
			instance.Spec.Telemetry.ApplicationCredentialCeilometer,
		},

{"manila", instance.Spec.Manila.Enabled, instance.Spec.Manila.ApplicationCredential},
{"designate", instance.Spec.Designate.Enabled, instance.Spec.Designate.ApplicationCredential},
{"watcher", instance.Spec.Watcher.Enabled, instance.Spec.Watcher.ApplicationCredential},
{"aodh", instance.Spec.Telemetry.Enabled, instance.Spec.Telemetry.ApplicationCredentialAodh},
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
{"aodh", instance.Spec.Telemetry.Enabled, instance.Spec.Telemetry.ApplicationCredentialAodh},
{"aodh", instance.Spec.Telemetry.Enabled && instance.Spec.Telemetry.Template.Autoscaling.Enabled, instance.Spec.Telemetry.ApplicationCredentialAodh},

{"designate", instance.Spec.Designate.Enabled, instance.Spec.Designate.ApplicationCredential},
{"watcher", instance.Spec.Watcher.Enabled, instance.Spec.Watcher.ApplicationCredential},
{"aodh", instance.Spec.Telemetry.Enabled, instance.Spec.Telemetry.ApplicationCredentialAodh},
{"cloudkitty", instance.Spec.Telemetry.Enabled, instance.Spec.Telemetry.ApplicationCredentialCloudKitty},
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
{"cloudkitty", instance.Spec.Telemetry.Enabled, instance.Spec.Telemetry.ApplicationCredentialCloudKitty},
{"cloudkitty", instance.Spec.Telemetry.Enabled && instance.Spec.Telemetry.Template.CloudKitty.Enabled, instance.Spec.Telemetry.ApplicationCredentialCloudKitty},

@softwarefactory-project-zuul
Copy link

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/097e3f645c1a4f4094a7fcfe49b94d04

openstack-k8s-operators-content-provider FAILURE in 11m 20s
⚠️ podified-multinode-edpm-deployment-crc SKIPPED Skipped due to failed job openstack-k8s-operators-content-provider
⚠️ cifmw-crc-podified-edpm-baremetal SKIPPED Skipped due to failed job openstack-k8s-operators-content-provider
⚠️ adoption-standalone-to-crc-ceph-provider SKIPPED Skipped due to failed job openstack-k8s-operators-content-provider
⚠️ openstack-operator-tempest-multinode SKIPPED Skipped due to failed job openstack-k8s-operators-content-provider

@softwarefactory-project-zuul
Copy link

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/c0c013624c3444e9a20465996192328f

openstack-k8s-operators-content-provider FAILURE in 12m 20s
⚠️ podified-multinode-edpm-deployment-crc SKIPPED Skipped due to failed job openstack-k8s-operators-content-provider
⚠️ cifmw-crc-podified-edpm-baremetal SKIPPED Skipped due to failed job openstack-k8s-operators-content-provider
⚠️ adoption-standalone-to-crc-ceph-provider SKIPPED Skipped due to failed job openstack-k8s-operators-content-provider
⚠️ openstack-operator-tempest-multinode SKIPPED Skipped due to failed job openstack-k8s-operators-content-provider

@softwarefactory-project-zuul
Copy link

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/a7e544c73217452b93106c7807a5aea4

openstack-k8s-operators-content-provider FAILURE in 11m 40s
⚠️ podified-multinode-edpm-deployment-crc SKIPPED Skipped due to failed job openstack-k8s-operators-content-provider
⚠️ cifmw-crc-podified-edpm-baremetal SKIPPED Skipped due to failed job openstack-k8s-operators-content-provider
⚠️ adoption-standalone-to-crc-ceph-provider SKIPPED Skipped due to failed job openstack-k8s-operators-content-provider
⚠️ openstack-operator-tempest-multinode SKIPPED Skipped due to failed job openstack-k8s-operators-content-provider

Comment on lines +961 to +964
// Enabled indicates whether an ApplicationCredential should be created
// +kubebuilder:validation:Optional
// +kubebuilder:default=false
Enabled bool `json:"enabled"`
Copy link
Contributor

Choose a reason for hiding this comment

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

wondering if we need this global switch, if you always have to enable it also on the per service level. if there is the req to always do it in each service do we need a global switch? wouldn't a global switch only make sense if we default the per service to true. like:

  • switch global to true ac is enable for all services
  • if you want to disable it for some services switch them to off

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I know having two switches might feel confusing, but I was thinking about them as two different layers of control. - per-service enablement should stay explicit so the operator isn’t surprised by authentication changing in a service they didn’t mean to migrate yet. The global switch is mainly there for ops convenience and safety: if we ever need to disable AppCred everywhere, we can flip one flag instead of going service-by-service again. If we made per-service default to true, it could be missed and then users would wonder why auth changed unexpectedly. With opt-in per service and global gate, rollout stays controlled and predictable, and we can still turn it off fast at once.

But this is just my idea, I'm open to suggestion based on better practices.

Copy link
Contributor

Choose a reason for hiding this comment

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

I was inclined towards having a single switch too, but double knob makes sense now.

do you think we can enable a logs for same, so suppose operator only enabled in respective service but they wont see AC's until the global is enabled too.
to debug they will start with operator logs of respective service - will it make sense to add an warning or error log saying global one might not be enabled.

// ServiceAppCredSection allows service-specific overrides of the global AC configuration
type ServiceAppCredSection struct {
// +kubebuilder:validation:Optional
// +kubebuilder:default=false
Copy link
Contributor

Choose a reason for hiding this comment

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

see my comment on the global parameter, should this one default to true?

// ACRule describes a single access rule for an ApplicationCredential
// +k8s:openapi-gen=true
type ACRule struct {
// Service is the name of the service to target (e.g. "identity").
Copy link
Contributor

Choose a reason for hiding this comment

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

from where to get the service name? can we link a reference or will it be in product doc or how to use ac rules?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

These access rules are a keystone upstream feature for app creds. It is documented directly in the OpenStackClient CLI docs and it will be documented in the product doc how to get the necessary fields (service, path, method) from the cluster.

@vakwetu Can you please provide better answer than me, I remember that I added the AccessRules based on your suggesitons.

// +kubebuilder:validation:Required
// +kubebuilder:validation:MinLength=1
Service string `json:"service"`
// Path is the HTTP path (e.g. "/v3/auth/tokens").
Copy link
Contributor

Choose a reason for hiding this comment

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

from where you get this? you have to know or is there some doc? or do we document this as part of the AC feature in product doc?

// Whether the AC should be unrestricted
Unrestricted *bool `json:"unrestricted,omitempty"`

// AccessRules lets the service override either the global rules
Copy link
Contributor

Choose a reason for hiding this comment

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

is there an or missing?

}
r.Spec.Cinder.Template.Default()
// Default Auth fields for Application Credentials
if r.Spec.Cinder.Template.CinderAPI.Auth.ApplicationCredentialSecret == "" {
Copy link
Contributor

Choose a reason for hiding this comment

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

we are setting it to a string of the secret name in the webhook, even the secret might not be there, not sure if I understand the intend of this. shouldn't we only set the ApplicationCredentialSecret in the controller if AC is enabled and the secret got created?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

YEs, we should set this only when app cred are enabled. The original intent was to declare where the secret is, not if it exists, because service operators are designed to handle this, eg in glance-operator/pull/812

But I will change it to appear only when app creds are enabled to not raise confusion.

Copy link
Contributor

Choose a reason for hiding this comment

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

I think we should remove any defaulting via webhooks. if e.g. for glance AC gets enabled in its ReconcileGlance https://github.com/openstack-k8s-operators/openstack-operator/blob/main/internal/openstack/glance.go#L68 the operator should request the AC and check/wait until the AC is ready. if ready, it sets the parameter in the template. if it gets disabled, it removes the parameter in the template, or nils it. not sure what it is.

api/go.mod Outdated
@@ -1,34 +1,34 @@
module github.com/openstack-k8s-operators/openstack-operator/api
module github.com/openstack-k8s-operators/openstack-operator/apis
Copy link
Contributor

Choose a reason for hiding this comment

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

is this correct?


// ReconcileApplicationCredentials ensures an AC CR per enabled service,
// propagating its secret name, passwordSelector, and serviceUser fields.
func ReconcileApplicationCredentials(
Copy link
Contributor

Choose a reason for hiding this comment

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

can you explain the process when AC get enabled. I was expecting that the creation of the AC for each service happens in each of the service reconciler logics to have it there as a pre-step if enabled from beginning or when enabled later. seems right now we have a dedicated ReconcileApplicationCredentials logic which then patches each service template with a lot of hard coded stuff. I don't think this is how we want to do it, or is there a need to do it that way?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

So, based on the agreed ZDPR design - the AC CR creation should happen in the opesntack-operator and service operators act only as consumers of the AC Secret. However what I didn't realize is that right now we don't take the actual service info from the actual service CR, btu from OpenStackControlPlane.Spec templates.

Would it be sufficient to get the service info from the actual service CR, would eliminate the hard-coded switch statement that reaches into template structures, eg:

// In openstack-operator
ffunc ReconcileApplicationCredentials(ctx, instance, helper) error {
    // ... global enabled check ...
    
    // Glance
    if instance.Spec.Glance.Enabled && isACEnabledForService(instance, "glance") {
        glanceCR := &glancev1.Glance{}
        err := helper.GetClient().Get(ctx, types.NamespacedName{
            Name: "glance",
            Namespace: instance.Namespace,
        }, glanceCR)
        
        if err != nil {
            if errors.IsNotFound(err) {
                log.Info("Glance CR not found yet, skipping AC creation")
                // AC will be created on next reconcile
            } else {
                return fmt.Errorf("failed to get Glance CR: %w", err)
            }
        } else {            
            acConfig := mergeAppCred(
                instance.Spec.ApplicationCredential,
                instance.Spec.Glance.ApplicationCredential,
            )
            
            err := reconcileApplicationCredential(
                ctx, helper, instance,
                "ac-glance",  // One AC name
                glanceCR.Spec.ServiceUser,  // One service user
                glanceCR.Spec.Secret,
                glanceCR.Spec.PasswordSelectors.Service,
                acConfig,
            )
            if err != nil {
                return fmt.Errorf("failed to reconcile AC for glance: %w", err)
            }
        }
    }
    
    // other services
   // ironic will be different, because it has two keystone service users
    
    return nil
}

Of course we need to add some check if the service is not created yet, so it doesn't fail on the first reconcile. And the structure of each service might be little different.

Copy link
Contributor

Choose a reason for hiding this comment

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

check #1430 (comment) . thats I think how we should do it. we can have some helpers in internal/openstack/applicationcredential.go, so that it is simple func call from each Reconcile func. which this we do not have to maintain any list of possible service, or have to add something to a template outside of where the template gets created and updated.

Copy link
Contributor

Choose a reason for hiding this comment

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

this func could now be deleted?

@softwarefactory-project-zuul
Copy link

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/openstack-operator for 1430,22dd45d7d0ac5a01b8420137d21943e09c259afe

@softwarefactory-project-zuul
Copy link

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/f8ab8a85899342ce9ccc33f474348db3

openstack-k8s-operators-content-provider FAILURE in 14m 12s
⚠️ podified-multinode-edpm-deployment-crc SKIPPED Skipped due to failed job openstack-k8s-operators-content-provider
⚠️ cifmw-crc-podified-edpm-baremetal SKIPPED Skipped due to failed job openstack-k8s-operators-content-provider
⚠️ adoption-standalone-to-crc-ceph-provider SKIPPED Skipped due to failed job openstack-k8s-operators-content-provider
⚠️ openstack-operator-tempest-multinode SKIPPED Skipped due to failed job openstack-k8s-operators-content-provider

Comment on lines 860 to 861
// Update the glanceAPI with Auth defaults
r.Spec.Glance.Template.GlanceAPIs[name] = glanceAPI
Copy link
Contributor

Choose a reason for hiding this comment

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

is this needed?

Comment on lines 1091 to 1096
// Initialize ApplicationCredential (watcher specific) field to avoid null value
// This ensures consistent behavior with other services.
if r.Spec.Watcher.ApplicationCredential == nil {
r.Spec.Watcher.ApplicationCredential = &ServiceAppCredSection{Enabled: false}
}

Copy link
Contributor

Choose a reason for hiding this comment

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

same, is this needed? seems to be the default from the kubebuilder annotation?

Comment on lines 28 to 34
name: openstack-operator-controller-operator
namespace: system
- patch: '[{"op": "replace", "path": "/spec/template/spec/containers/0/env/0", "value":
{"name": "OPENSTACK_RELEASE_VERSION", "value": "0.5.0-1767944881"}}]'
target:
kind: Deployment
name: openstack-operator-controller-operator
Copy link
Contributor

Choose a reason for hiding this comment

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

I think we don't want to include the file config/operator/deployment/kustomization.yaml in the PR.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, this is a generated remnant when I built the image, I will remove the changes here.

Comment on lines 201 to 237
switch serviceKey {
case "glance":
acSection = instance.Spec.Glance.ApplicationCredential
case "cinder":
acSection = instance.Spec.Cinder.ApplicationCredential
case "swift":
acSection = instance.Spec.Swift.ApplicationCredential
case "ironic":
acSection = instance.Spec.Ironic.ApplicationCredential
case "ironic-inspector":
// ironic-inspector shares the same AC configuration as ironic
acSection = instance.Spec.Ironic.ApplicationCredential
case "heat":
acSection = instance.Spec.Heat.ApplicationCredential
case "manila":
acSection = instance.Spec.Manila.ApplicationCredential
case "neutron":
acSection = instance.Spec.Neutron.ApplicationCredential
case "nova":
acSection = instance.Spec.Nova.ApplicationCredential
case "placement":
acSection = instance.Spec.Placement.ApplicationCredential
case "octavia":
acSection = instance.Spec.Octavia.ApplicationCredential
case "barbican":
acSection = instance.Spec.Barbican.ApplicationCredential
case "designate":
acSection = instance.Spec.Designate.ApplicationCredential
case "watcher":
acSection = instance.Spec.Watcher.ApplicationCredential
case "ceilometer":
acSection = instance.Spec.Telemetry.ApplicationCredentialCeilometer
case "aodh":
acSection = instance.Spec.Telemetry.ApplicationCredentialAodh
case "cloudkitty":
acSection = instance.Spec.Telemetry.ApplicationCredentialCloudKitty
}
Copy link
Contributor

Choose a reason for hiding this comment

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

I think we should not have service specific things in here. it should be just a generic func. if the services passes in its ApplicationCredential I think we can get rid of all these?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

yes, definitely

acExists := err == nil

// Check if AC is enabled for this service
if !shouldEnableACForService(serviceKey, instance) {
Copy link
Contributor

Choose a reason for hiding this comment

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

as mentioned bellow, if we pass in the ApplicationCredential I don't think we need that complex shouldEnableACForService with different service names.
could be just return ac != nil && ac.Enabled, right?


// servicesWithMultipleUsers defines services that have multiple Keystone users
// The function returns nil if the service is not enabled or template is not initialized
var servicesWithMultipleUsers = map[string]func(*corev1beta1.OpenStackControlPlane) []ServiceUserConfig{
Copy link
Contributor

Choose a reason for hiding this comment

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

is this still needed?


// Application Credential Management (Day-2 operation)
barbicanReady := barbican.Status.ObservedGeneration == barbican.Generation && barbican.IsReady()
acEnabled := shouldEnableACForService("barbican", instance)
Copy link
Contributor

Choose a reason for hiding this comment

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

if we already check it here, which is basically Template.Auth.ApplicationCredentialSecret != nil && Template.Auth.ApplicationCredentialSecret.Enabled lets pass it to EnsureApplicationCredentialForService. add a simple func where each service can pass in its Auth and get back the bool.

if !acEnabled {
instance.Spec.Barbican.Template.Auth.ApplicationCredentialSecret = ""
} else if acReady {
instance.Spec.Barbican.Template.Auth.ApplicationCredentialSecret = keystonev1.GetACSecretName("barbican")
Copy link
Contributor

Choose a reason for hiding this comment

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

I think EnsureApplicationCredentialForService should return the AC and we then can just use the secret from the AC.

ctx,
helper,
instance,
"barbican",
Copy link
Contributor

Choose a reason for hiding this comment

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

should or could this be barbican.Name, could remove the need for another static string.

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

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/5c42e7939c0a40fa92ed0b1ba8dfae74

openstack-k8s-operators-content-provider FAILURE in 12m 38s
⚠️ podified-multinode-edpm-deployment-crc SKIPPED Skipped due to failed job openstack-k8s-operators-content-provider
⚠️ cifmw-crc-podified-edpm-baremetal SKIPPED Skipped due to failed job openstack-k8s-operators-content-provider
⚠️ adoption-standalone-to-crc-ceph-provider SKIPPED Skipped due to failed job openstack-k8s-operators-content-provider
⚠️ openstack-operator-tempest-multinode SKIPPED Skipped due to failed job openstack-k8s-operators-content-provider

@openshift-ci
Copy link
Contributor

openshift-ci bot commented Jan 23, 2026

@Deydra71: The following tests failed, say /retest to rerun all failed tests or /retest-required to rerun all mandatory failed tests:

Test name Commit Details Required Rerun command
ci/prow/precommit-check bf826ed link true /test precommit-check
ci/prow/openstack-operator-build-deploy-kuttl bf826ed link true /test openstack-operator-build-deploy-kuttl
ci/prow/openstack-operator-build-deploy-kuttl-4-18 bf826ed link true /test openstack-operator-build-deploy-kuttl-4-18

Full PR test history. Your PR dashboard.

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. I understand the commands that are listed here.

if barbicanSecret == "" {
barbicanSecret = instance.Spec.Secret
}

Copy link
Contributor

Choose a reason for hiding this comment

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

I think to prevent the bellow mentioned flapping we'd have to do something like we do for certs to fetch the current auth from the running barbican

	if isACEnabled(instance.Spec.ApplicationCredential, instance.Spec.Barbican.ApplicationCredential) {
		instance.Spec.Barbican.Template.Auth = barbican.Spec.Barbican.Template.Auth
    }

Comment on lines +88 to +96
// Set or clear ApplicationCredentialSecret
// - If AC disabled: use password
// - If AC enabled AND ready: use AC
// - If AC enabled BUT not ready: leave unchanged to avoid flapping
if acSecretName == "" && !isACEnabled(instance.Spec.ApplicationCredential, instance.Spec.Barbican.ApplicationCredential) {
instance.Spec.Barbican.Template.Auth.ApplicationCredentialSecret = ""
} else if acSecretName != "" {
instance.Spec.Barbican.Template.Auth.ApplicationCredentialSecret = acSecretName
}
Copy link
Contributor

Choose a reason for hiding this comment

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

with the above comment, it might be ok to just assign what EnsureApplicationCredentialForService returns as the ApplicationCredentialSecret

instance.Spec.Barbican.Template.Auth.ApplicationCredentialSecret = acSecretName
  • If AC disabled: use password, it returns ""
  • If AC enabled AND ready. it returns the name

)
if err != nil {
return ctrl.Result{}, err
}
Copy link
Contributor

Choose a reason for hiding this comment

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

do we have to return for the Application Credential not ready yet case when we return with the RequeueAfter?

if (acResult != ctrl.Result{}) {
		return acResult, nil
}

barbicanSecret = instance.Spec.Secret
}

acSecretName, acResult, err := EnsureApplicationCredentialForService(
Copy link
Contributor

Choose a reason for hiding this comment

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

the default for AC is that they are disabled. wondering if we don't have to call it if ac is disabled and there is no current barbican.Spec.Barbican.Template.Auth.ApplicationCredentialSecret configured?

Comment on lines +221 to +223
if barbicanReady && (acResult != ctrl.Result{}) {
return acResult, nil
}
Copy link
Contributor

Choose a reason for hiding this comment

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

as commented above, wondering why we do it at the bottom and not right after the ensure func?

@stuggi
Copy link
Contributor

stuggi commented Jan 23, 2026

this looks a lot cleaner!! just some questions for clarification, and maybe improving things

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.

6 participants