diff --git a/.rubocop.yml b/.rubocop.yml index f19eac4..e81de53 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -43,3 +43,7 @@ Rails/SkipsModelValidations: Style/FormatStringToken: Enabled: false + +Rails/DynamicFindBy: + Exclude: + - "lib/foreman_resource_quota/engine.rb" diff --git a/lib/foreman_resource_quota/async/refresh_resource_quota_utilization.rb b/lib/foreman_resource_quota/async/refresh_resource_quota_utilization.rb new file mode 100644 index 0000000..3720ba6 --- /dev/null +++ b/lib/foreman_resource_quota/async/refresh_resource_quota_utilization.rb @@ -0,0 +1,25 @@ +# frozen_string_literal: true + +module ForemanResourceQuota + module Async + class RefreshResourceQuotaUtilization < ::Actions::EntryAction + include ::Actions::RecurringAction + + def run + ResourceQuota.all.each do |quota| + quota.determine_utilization + rescue e + logger.error N_(format("An error occured determining the utilization of '%s'-quota: %s", quota.name, e)) + end + end + + def logger + action_logger + end + + def rescue_strategy_for_self + Dynflow::Action::Rescue::Fail + end + end + end +end diff --git a/lib/foreman_resource_quota/engine.rb b/lib/foreman_resource_quota/engine.rb index 0e96649..f73de8a 100644 --- a/lib/foreman_resource_quota/engine.rb +++ b/lib/foreman_resource_quota/engine.rb @@ -1,5 +1,7 @@ # frozen_string_literal: true +require 'foreman_tasks' + module ForemanResourceQuota class Engine < ::Rails::Engine engine_name 'foreman_resource_quota' @@ -42,10 +44,48 @@ class Engine < ::Rails::Engine Rails.logger.warn "ForemanResourceQuota: skipping engine hook (#{e})" end + # Register ForemanTasks-based recurring logic/scheduled tasks + initializer 'foreman_resource_quota.register_scheduled_tasks', before: :finisher_hook do |_app| + action_paths = [ForemanResourceQuota::Engine.root.join('lib/foreman_resource_quota/async')] + ::ForemanTasks.dynflow.config.eager_load_paths.concat(action_paths) + + # Skip object creation if the admin user is not present + # skip database manipulations while tables do not exist, like in migrations + if ActiveRecord::Base.connection.data_source_exists?(ForemanTasks::Task.table_name) && + User.unscoped.find_by_login(User::ANONYMOUS_ADMIN).present? + # Register the scheduled tasks + ::ForemanTasks.dynflow.config.on_init(false) do |_world| + ForemanResourceQuota::Engine.register_scheduled_task( + ForemanResourceQuota::Async::RefreshResourceQuotaUtilization, + '0 1 * * *' + ) + end + end + rescue ActiveRecord::NoDatabaseError => e + Rails.logger.warn "ForemanResourceQuota: skipping ForemanTasks registration hook (#{e})" + end + initializer 'foreman_resource_quota.register_gettext', after: :load_config_initializers do |_app| locale_dir = File.join(File.expand_path('../..', __dir__), 'locale') locale_domain = 'foreman_resource_quota' Foreman::Gettext::Support.add_text_domain locale_domain, locale_dir end + + # Helper to register ForemanTasks + def self.register_scheduled_task(task_class, cronline) + unless ::ForemanTasks::RecurringLogic.joins(:tasks) + .merge(::ForemanTasks::Task.where(label: task_class.name)) + .exists? + ::ForemanTasks::RecurringLogic.transaction(isolation: :serializable) do + User.as_anonymous_admin do + recurring_logic = ::ForemanTasks::RecurringLogic.new_from_cronline(cronline) + recurring_logic.save! + recurring_logic.start(task_class) + end + rescue ActiveRecord::TransactionIsolationError => e + Rails.logger.warn "ForemanResourceQuota: skipping RecurringLogic registration hook (#{e})" + end + end + end end end