Fix: Allow service accounts for foundational flows

What does this MR do and why?

  • Service accounts were not being created for foundational flows when beta features were disabled, even though foundational flows are GA (generally available) and should work regardless of beta feature settings.
  • Root Cause: ee/app/services/ai/catalog/item_consumers/create_service.rb was checking Ability.allowed?(current_user, :create_ai_catalog_flow_item_consumer, container) for all flows, not just custom flows. This ability check is prevented when a call to ::Gitlab::Llm::StageCheck.available?(@subject, :ai_catalog_flows) fails. That calls ee/lib/gitlab/llm/utils/ai_features_catalogue.rb, which has ai_catalog_flows as beta. So beta features must be enabled for it to be true.
  • The new ability check calls StageCheck for foundational_flows, which is GA.

References

Screenshots or screen recordings

Before After
Enabling a foundational flow doesn't do anything if beta features are disabled Enabling a foundational flow works with beta features disabled

How to set up and validate locally

  1. Visit group settings (http://gdk.test:3000/groups/gitlab-duo/-/settings/gitlab_duo/configuration) and disable beta features and all foundational flows
  2. Perform data cleanup to ensure you are starting from a clean slate with no existing service accounts for foundational flows you enabled in the past (Save this as cleanup.rb:)
     # to use:
     # gdk rails console
     # group_id = 1000000
     # load 'cleanup.rb'
     # preview_cleanup(group_id)
     # cleanup_ai_catalog(group_id)
    
     def preview_cleanup(group_id)
       group = Group.find(group_id)
    
       puts "Preview cleanup for group: #{group.full_path}"
       puts "Would delete:"
       puts "- #{Ai::Catalog::ItemConsumer.where(group_id: group_id).count} group item consumers"
       puts "- #{Ai::Catalog::ItemConsumer.where(project_id: group.projects.pluck(:id)).count} project item consumers"
       puts "- #{Ai::FlowTrigger.where(project_id: group.projects.pluck(:id)).count} flow triggers"
    
       sa_count = User.service_account.joins(:user_detail)
         .where(user_details: { provisioned_by_group_id: group_id }).count
       puts "- #{sa_count} service accounts"
    
       User.service_account.joins(:user_detail)
         .where(user_details: { provisioned_by_group_id: group_id }).each do |sa|
         puts "  - #{sa.username} (used by #{sa.ai_catalog_item_consumers.count} consumers)"
       end
     end
    
     def cleanup_ai_catalog(group_id)
       group = Group.find(group_id)
    
       puts "Cleaning up AI catalog data for group: #{group.full_path}"
    
       # Delete all item consumers for this group
       group_consumers = Ai::Catalog::ItemConsumer.where(group_id: group_id)
       puts "Deleting #{group_consumers.count} group item consumers"
       group_consumers.destroy_all
    
       # Delete all item consumers for group's projects
       project_consumers = Ai::Catalog::ItemConsumer.where(project_id: group.projects.pluck(:id))
       puts "Deleting #{project_consumers.count} project item consumers"
       project_consumers.destroy_all
    
       # Delete flow triggers for all group projects
       triggers = Ai::FlowTrigger.where(project_id: group.projects.pluck(:id))
       puts "Deleting #{triggers.count} flow triggers"
       triggers.destroy_all
    
       # Remove service account project members
       group.projects.each do |project|
         sa_members = project.members.joins(:user).where(users: { user_type: :service_account })
         puts "Removing #{sa_members.count} service account members from #{project.path}"
         sa_members.destroy_all
       end
    
       # Delete service accounts (only unused ones)
       service_accounts = User.service_account.joins(:user_detail)
         .where(user_details: { provisioned_by_group_id: group_id })
    
       service_accounts.each do |sa|
         if sa.ai_catalog_item_consumers.none?
           puts "Deleting unused service account: #{sa.username}"
           sa.destroy
         else
           puts "Keeping service account #{sa.username} (still in use by #{sa.ai_catalog_item_consumers.count} consumers)"
         end
       end
    
       puts "Cleanup complete!"
     end
  3. Once data is cleaned up, go back to Duo settings and enable at least one foundational flow. Ensure that beta features remain disabled.
  4. You should now see a service account for the flow at http://gdk.test:3000/groups/gitlab-duo/-/settings/service_accounts and be able to trigger it.

MR acceptance checklist

Evaluate this MR against the MR acceptance checklist. It helps you analyze changes to reduce risks in quality, performance, reliability, security, and maintainability.

Edited by Jessie Young

Merge request reports

Loading