@@ -48,6 +48,7 @@ import (
4848 ironicv1 "github.com/openstack-k8s-operators/ironic-operator/api/v1beta1"
4949 ironic "github.com/openstack-k8s-operators/ironic-operator/internal/ironic"
5050 ironicconductor "github.com/openstack-k8s-operators/ironic-operator/internal/ironicconductor"
51+ keystonev1 "github.com/openstack-k8s-operators/keystone-operator/api/v1beta1"
5152 "github.com/openstack-k8s-operators/lib-common/modules/common"
5253 "github.com/openstack-k8s-operators/lib-common/modules/common/condition"
5354 "github.com/openstack-k8s-operators/lib-common/modules/common/endpoint"
@@ -256,6 +257,42 @@ func (r *IronicConductorReconciler) SetupWithManager(ctx context.Context, mgr ct
256257 return err
257258 }
258259
260+ // Application Credential secret watching function
261+ acSecretFn := func (_ context.Context , o client.Object ) []reconcile.Request {
262+ name := o .GetName ()
263+ ns := o .GetNamespace ()
264+ result := []reconcile.Request {}
265+
266+ // Only handle Secret objects
267+ if _ , isSecret := o .(* corev1.Secret ); ! isSecret {
268+ return nil
269+ }
270+
271+ // Check if this is an ironic AC secret by name pattern (ac-ironic-secret)
272+ expectedSecretName := keystonev1 .GetACSecretName ("ironic" )
273+ if name == expectedSecretName {
274+ // get all IronicConductor CRs in this namespace
275+ ironicConductors := & ironicv1.IronicConductorList {}
276+ listOpts := []client.ListOption {
277+ client .InNamespace (ns ),
278+ }
279+ if err := r .List (context .Background (), ironicConductors , listOpts ... ); err != nil {
280+ return nil
281+ }
282+
283+ // Enqueue reconcile for all ironic conductor instances
284+ for _ , cr := range ironicConductors .Items {
285+ objKey := client.ObjectKey {
286+ Namespace : ns ,
287+ Name : cr .Name ,
288+ }
289+ result = append (result , reconcile.Request {NamespacedName : objKey })
290+ }
291+ }
292+
293+ return result
294+ }
295+
259296 return ctrl .NewControllerManagedBy (mgr ).
260297 For (& ironicv1.IronicConductor {}).
261298 Owns (& appsv1.StatefulSet {}).
@@ -272,6 +309,8 @@ func (r *IronicConductorReconciler) SetupWithManager(ctx context.Context, mgr ct
272309 handler .EnqueueRequestsFromMapFunc (r .findObjectsForSrc ),
273310 builder .WithPredicates (predicate.ResourceVersionChangedPredicate {}),
274311 ).
312+ Watches (& corev1.Secret {},
313+ handler .EnqueueRequestsFromMapFunc (acSecretFn )).
275314 Watches (
276315 & topologyv1.Topology {},
277316 handler .EnqueueRequestsFromMapFunc (r .findObjectsForSrc ),
@@ -594,6 +633,19 @@ func (r *IronicConductorReconciler) reconcileNormal(ctx context.Context, instanc
594633
595634 instance .Status .Conditions .MarkTrue (condition .InputReadyCondition , condition .InputReadyMessage )
596635
636+ // Verify Application Credentials if available
637+ ctrlResult , err := keystonev1 .VerifyApplicationCredentialsForService (
638+ ctx ,
639+ r .Client ,
640+ instance .Namespace ,
641+ ironic .ServiceName ,
642+ & configMapVars ,
643+ 10 * time .Second ,
644+ )
645+ if (err != nil || ctrlResult != ctrl.Result {}) {
646+ return ctrlResult , err
647+ }
648+
597649 //
598650 // TLS input validation
599651 //
@@ -760,7 +812,7 @@ func (r *IronicConductorReconciler) reconcileNormal(ctx context.Context, instanc
760812 time .Duration (5 )* time .Second ,
761813 )
762814
763- ctrlResult , err : = ss .CreateOrPatch (ctx , helper )
815+ ctrlResult , err = ss .CreateOrPatch (ctx , helper )
764816 if err != nil {
765817 instance .Status .Conditions .Set (condition .FalseCondition (
766818 condition .DeploymentReadyCondition ,
@@ -885,6 +937,18 @@ func (r *IronicConductorReconciler) generateServiceConfigMaps(
885937 templateParameters ["KeystonePublicURL" ] = instance .Spec .KeystoneEndpoints .Public
886938 templateParameters ["ServiceUser" ] = instance .Spec .ServiceUser
887939 templateParameters ["ServicePassword" ] = servicePassword
940+
941+ // Try to get Application Credential for this service
942+ templateParameters ["UseApplicationCredentials" ] = false
943+ if acData , err := keystonev1 .GetApplicationCredentialFromSecret (ctx , r .Client , instance .Namespace , ironic .ServiceName ); err != nil {
944+ Log .Error (err , "Failed to get ApplicationCredential for service" , "service" , ironic .ServiceName )
945+ return err
946+ } else if acData != nil {
947+ templateParameters ["UseApplicationCredentials" ] = true
948+ templateParameters ["ACID" ] = acData .ID
949+ templateParameters ["ACSecret" ] = acData .Secret
950+ Log .Info ("Using ApplicationCredentials auth" , "service" , ironic .ServiceName )
951+ }
888952 } else {
889953 ironicAPI , err := ironicv1 .GetIronicAPI (
890954 ctx , h , instance .Namespace , map [string ]string {})
0 commit comments