@@ -53,6 +53,7 @@ import (
5353 "github.com/openstack-k8s-operators/lib-common/modules/common/util"
5454
5555 assistantv1 "github.com/openstack-k8s-operators/openstack-operator/api/assistant/v1beta1"
56+ clientv1 "github.com/openstack-k8s-operators/openstack-operator/api/client/v1beta1"
5657 "github.com/openstack-k8s-operators/openstack-operator/internal/openstackassistant"
5758)
5859
@@ -81,6 +82,7 @@ func (r *OpenStackAssistantReconciler) GetLogger(ctx context.Context) logr.Logge
8182// +kubebuilder:rbac:groups="rbac.authorization.k8s.io",resources=rolebindings,verbs=get;list;watch;create;update;patch
8283// +kubebuilder:rbac:groups="rbac.authorization.k8s.io",resources=clusterroles,verbs=get;list;watch;create;update;patch;delete
8384// +kubebuilder:rbac:groups="rbac.authorization.k8s.io",resources=clusterrolebindings,verbs=get;list;watch;create;update;patch;delete
85+ // +kubebuilder:rbac:groups=client.openstack.org,resources=openstackclients,verbs=get;list;watch
8486
8587// Reconcile -
8688func (r * OpenStackAssistantReconciler ) Reconcile (ctx context.Context , req ctrl.Request ) (result ctrl.Result , _err error ) {
@@ -223,7 +225,7 @@ func (r *OpenStackAssistantReconciler) Reconcile(ctx context.Context, req ctrl.R
223225 condition .ErrorReason ,
224226 condition .SeverityWarning ,
225227 assistantv1 .OpenStackAssistantReadyErrorMessage ,
226- fmt . Sprintf ( "CA bundle secret %s not found" , instance .Spec .LightspeedStack .CaBundleSecretName ) ))
228+ "CA bundle secret " + instance .Spec .LightspeedStack .CaBundleSecretName ))
227229 return ctrl.Result {}, nil
228230 }
229231 return ctrl.Result {}, err
@@ -288,8 +290,74 @@ func (r *OpenStackAssistantReconciler) Reconcile(ctx context.Context, req ctrl.R
288290 return ctrl.Result {}, err
289291 }
290292
293+ // Resolve MCP servers (auto-discover from OpenStackClientRef or use manual URL)
294+ resolvedMCPServers := make (map [string ]string )
295+ mcpCaBundleSecretName := ""
296+ if instance .Spec .Goose != nil {
297+ for _ , mcp := range instance .Spec .Goose .MCPServers {
298+ if mcp .OpenStackClientRef != "" {
299+ osclient := & clientv1.OpenStackClient {}
300+ err := r .Get (ctx , types.NamespacedName {
301+ Name : mcp .OpenStackClientRef ,
302+ Namespace : instance .Namespace ,
303+ }, osclient )
304+ if err != nil {
305+ if k8s_errors .IsNotFound (err ) {
306+ instance .Status .Conditions .Set (condition .FalseCondition (
307+ assistantv1 .OpenStackAssistantReadyCondition ,
308+ condition .RequestedReason ,
309+ condition .SeverityInfo ,
310+ "Waiting for OpenStackClient %s" , mcp .OpenStackClientRef ))
311+ return ctrl.Result {RequeueAfter : time .Duration (10 ) * time .Second }, nil
312+ }
313+ return ctrl.Result {}, fmt .Errorf ("error looking up OpenStackClient %s: %w" , mcp .OpenStackClientRef , err )
314+ }
315+
316+ if osclient .Spec .MCP == nil || ! osclient .Spec .MCP .Enabled {
317+ instance .Status .Conditions .Set (condition .FalseCondition (
318+ assistantv1 .OpenStackAssistantReadyCondition ,
319+ condition .ErrorReason ,
320+ condition .SeverityWarning ,
321+ assistantv1 .OpenStackAssistantReadyErrorMessage ,
322+ "OpenStackClient " + mcp .OpenStackClientRef + " does not have MCP enabled" ))
323+ return ctrl.Result {}, nil
324+ }
325+
326+ mcpSvcName := mcp .OpenStackClientRef + "-mcp"
327+ scheme := "http"
328+ if osclient .Spec .CaBundleSecretName != "" {
329+ scheme = "https"
330+ mcpCaBundleSecretName = osclient .Spec .CaBundleSecretName
331+ }
332+ mcpURL := fmt .Sprintf ("%s://%s.%s.svc:8080/openstack/" , scheme , mcpSvcName , instance .Namespace )
333+ resolvedMCPServers [mcp .Name ] = mcpURL
334+ Log .Info ("Auto-resolved MCP server" , "name" , mcp .Name , "url" , mcpURL , "openstackClientRef" , mcp .OpenStackClientRef )
335+ } else if mcp .URL != "" {
336+ resolvedMCPServers [mcp .Name ] = mcp .URL
337+ }
338+ }
339+ }
340+
341+ // Validate MCP CA bundle secret if auto-discovered
342+ if mcpCaBundleSecretName != "" && mcpCaBundleSecretName != instance .Spec .LightspeedStack .CaBundleSecretName {
343+ _ , mcpCaHash , err := secret .GetSecret (ctx , helper , mcpCaBundleSecretName , instance .Namespace )
344+ if err != nil {
345+ if k8s_errors .IsNotFound (err ) {
346+ instance .Status .Conditions .Set (condition .FalseCondition (
347+ assistantv1 .OpenStackAssistantReadyCondition ,
348+ condition .ErrorReason ,
349+ condition .SeverityWarning ,
350+ assistantv1 .OpenStackAssistantReadyErrorMessage ,
351+ "MCP CA bundle secret " + mcpCaBundleSecretName + " not found" ))
352+ return ctrl.Result {}, nil
353+ }
354+ return ctrl.Result {}, err
355+ }
356+ configVars ["mcp-ca-bundle" ] = env .SetValue (mcpCaHash )
357+ }
358+
291359 // Build PodSpec
292- spec := openstackassistant .AssistantPodSpec (instance , configVarsHash )
360+ spec := openstackassistant .AssistantPodSpec (instance , configVarsHash , resolvedMCPServers , mcpCaBundleSecretName )
293361
294362 podSpecHash , err := util .ObjectHash (spec )
295363 if err != nil {
@@ -623,9 +691,47 @@ func (r *OpenStackAssistantReconciler) SetupWithManager(
623691 handler .EnqueueRequestsFromMapFunc (r .findObjectsForSrc ),
624692 builder .WithPredicates (predicate.ResourceVersionChangedPredicate {}),
625693 ).
694+ Watches (
695+ & clientv1.OpenStackClient {},
696+ handler .EnqueueRequestsFromMapFunc (r .findAssistantsForOpenStackClient ),
697+ builder .WithPredicates (predicate.ResourceVersionChangedPredicate {}),
698+ ).
626699 Complete (r )
627700}
628701
702+ func (r * OpenStackAssistantReconciler ) findAssistantsForOpenStackClient (ctx context.Context , src client.Object ) []reconcile.Request {
703+ Log := r .GetLogger (ctx )
704+ requests := []reconcile.Request {}
705+
706+ crList := & assistantv1.OpenStackAssistantList {}
707+ if err := r .List (ctx , crList , client .InNamespace (src .GetNamespace ())); err != nil {
708+ Log .Error (err , "listing OpenStackAssistants for OpenStackClient change" )
709+ return requests
710+ }
711+
712+ for _ , item := range crList .Items {
713+ if item .Spec .Goose == nil {
714+ continue
715+ }
716+ for _ , mcp := range item .Spec .Goose .MCPServers {
717+ if mcp .OpenStackClientRef == src .GetName () {
718+ Log .Info ("OpenStackClient changed, reconciling assistant" ,
719+ "openstackClient" , src .GetName (),
720+ "assistant" , item .GetName ())
721+ requests = append (requests , reconcile.Request {
722+ NamespacedName : types.NamespacedName {
723+ Name : item .GetName (),
724+ Namespace : item .GetNamespace (),
725+ },
726+ })
727+ break
728+ }
729+ }
730+ }
731+
732+ return requests
733+ }
734+
629735func (r * OpenStackAssistantReconciler ) findObjectsForSrc (ctx context.Context , src client.Object ) []reconcile.Request {
630736 requests := []reconcile.Request {}
631737
0 commit comments