-
Notifications
You must be signed in to change notification settings - Fork 1k
Fixes #38956 - Add cron rake tasks for recurring jobs #10784
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: develop
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,74 @@ | ||
| require 'rake' | ||
|
|
||
| module Foreman | ||
| module Cron | ||
| SUPPORTED_CADENCES = [:hourly, :daily, :weekly, :monthly].freeze | ||
|
|
||
| class << self | ||
| def register(cadence, task_name) | ||
| cadence = cadence.to_sym | ||
|
|
||
| unless SUPPORTED_CADENCES.include?(cadence) | ||
| logger.warn( | ||
| "Foreman::Cron[#{cadence}]: unknown cadence when registering #{task_name}, ignoring" | ||
| ) | ||
| return | ||
| end | ||
|
|
||
| cadence_tasks = tasks_for(cadence) | ||
| cadence_tasks << task_name unless cadence_tasks.include?(task_name) | ||
| end | ||
|
|
||
| def run(cadence) | ||
| cadence = cadence.to_sym | ||
|
|
||
| cadence_tasks = tasks_for(cadence) | ||
| if cadence_tasks.empty? | ||
| logger.debug("Foreman::Cron[#{cadence}]: no tasks configured for this cadence") | ||
| return false | ||
| end | ||
|
|
||
| logger.info("Foreman::Cron[#{cadence}]: running #{cadence_tasks.size} task(s)") | ||
|
|
||
| failed = false | ||
|
|
||
| cadence_tasks.each do |task_name| | ||
| failed ||= !run_task(cadence, task_name) | ||
| end | ||
|
|
||
| failed | ||
| end | ||
|
|
||
| private | ||
|
|
||
| def tasks | ||
| @tasks ||= Hash.new { |h, k| h[k] = [] } | ||
| end | ||
|
|
||
| def tasks_for(cadence) | ||
| tasks[cadence.to_sym] | ||
| end | ||
|
|
||
| def run_task(cadence, task_name) | ||
| logger.info("Foreman::Cron[#{cadence}]: starting #{task_name}") | ||
|
|
||
| task = Rake::Task[task_name] | ||
| task.reenable | ||
| task.invoke | ||
|
|
||
| logger.info("Foreman::Cron[#{cadence}]: finished #{task_name}") | ||
| true | ||
| rescue StandardError => e | ||
| logger.error( | ||
| "Foreman::Cron[#{cadence}]: #{task_name} failed: #{e.class}: #{e.message}" | ||
| ) | ||
| logger.debug(e.backtrace.join("\n")) if e.backtrace | ||
| false | ||
| end | ||
|
|
||
| def logger | ||
| @logger ||= Rails.logger | ||
| end | ||
| end | ||
| end | ||
| end | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,41 @@ | ||
| require 'foreman/cron' | ||
|
|
||
| # Register built-in recurring tasks for each cadence. | ||
| # Plugins can also call Foreman::Cron.register(:daily, 'my_plugin:task'). | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think we should add this to https://github.com/theforeman/foreman/blob/develop/developer_docs/how_to_create_a_plugin.asciidoc Also, where should they do this? I did that in the |
||
| Foreman::Cron.register(:hourly, 'ldap:refresh_usergroups') | ||
|
|
||
| Foreman::Cron.register(:daily, 'reports:daily') | ||
| Foreman::Cron.register(:daily, 'db:sessions:clear') | ||
| Foreman::Cron.register(:daily, 'reports:expire') | ||
| Foreman::Cron.register(:daily, 'audits:expire') | ||
|
|
||
| Foreman::Cron.register(:weekly, 'reports:weekly') | ||
| Foreman::Cron.register(:weekly, 'notifications:clean') | ||
|
|
||
| Foreman::Cron.register(:monthly, 'reports:monthly') | ||
|
|
||
| namespace :cron do | ||
| desc 'Run hourly Foreman cron jobs' | ||
| task hourly: :environment do | ||
| failed = Foreman::Cron.run(:hourly) | ||
| raise "One or more hourly cron tasks failed" if failed | ||
| end | ||
|
|
||
| desc 'Run daily Foreman cron jobs' | ||
| task daily: :environment do | ||
| failed = Foreman::Cron.run(:daily) | ||
| raise "One or more daily cron tasks failed" if failed | ||
| end | ||
|
|
||
| desc 'Run weekly Foreman cron jobs' | ||
| task weekly: :environment do | ||
| failed = Foreman::Cron.run(:weekly) | ||
| raise "One or more weekly cron tasks failed" if failed | ||
| end | ||
|
|
||
| desc 'Run monthly Foreman cron jobs' | ||
| task monthly: :environment do | ||
| failed = Foreman::Cron.run(:monthly) | ||
| raise "One or more monthly cron tasks failed" if failed | ||
| end | ||
| end | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,72 @@ | ||
| require 'test_helper' | ||
| require 'foreman/cron' | ||
| require 'logger' | ||
|
|
||
| class Foreman::CronTest < ActiveSupport::TestCase | ||
| setup do | ||
| Foreman::Cron.instance_variable_set(:@tasks, nil) | ||
| Foreman::Cron.instance_variable_set(:@logger, Logger.new(nil)) | ||
| end | ||
|
|
||
| test 'register adds task for valid cadence' do | ||
| Foreman::Cron.register(:daily, 'test:task') | ||
|
|
||
| tasks = Foreman::Cron.send(:tasks_for, :daily) | ||
| assert_includes tasks, 'test:task' | ||
| end | ||
|
|
||
| test 'register does not add duplicate tasks' do | ||
| Foreman::Cron.register(:daily, 'test:task') | ||
| Foreman::Cron.register(:daily, 'test:task') | ||
|
|
||
| tasks = Foreman::Cron.send(:tasks_for, :daily) | ||
| assert_equal 1, tasks.count('test:task') | ||
| end | ||
|
|
||
| test 'register ignores invalid cadence' do | ||
| Foreman::Cron.register(:invalid, 'test:task') | ||
|
|
||
| tasks = Foreman::Cron.send(:tasks_for, :invalid) | ||
| assert_empty(tasks) | ||
| end | ||
|
|
||
| test 'run returns false when all tasks succeed' do | ||
| Foreman::Cron.instance_variable_set(:@tasks, { daily: ['test:task'] }) | ||
|
|
||
| task = mock('rake_task') | ||
| task.expects(:reenable).once | ||
| task.expects(:invoke).once | ||
| Rake::Task.stubs(:[]).with('test:task').returns(task) | ||
|
|
||
| result = Foreman::Cron.run(:daily) | ||
|
|
||
| assert_equal false, result | ||
| end | ||
|
|
||
| test 'run returns true when a task fails but continues executing others' do | ||
| Foreman::Cron.instance_variable_set(:@tasks, { daily: %w[first second] }) | ||
|
|
||
| failing = mock('failing_task') | ||
| failing.expects(:reenable).once | ||
| failing.expects(:invoke).raises(StandardError.new('boom')) | ||
|
|
||
| succeeding = mock('succeeding_task') | ||
| succeeding.expects(:reenable).once | ||
| succeeding.expects(:invoke).once | ||
|
Check failure on line 55 in test/unit/foreman/cron_test.rb
|
||
|
Comment on lines
+53
to
+55
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The failure is related Seems that the succeeding task is never executed? |
||
|
|
||
| Rake::Task.stubs(:[]).with('first').returns(failing) | ||
| Rake::Task.stubs(:[]).with('second').returns(succeeding) | ||
|
|
||
| result = Foreman::Cron.run(:daily) | ||
|
|
||
| assert_equal true, result | ||
| end | ||
|
|
||
| test 'run returns false when no tasks are configured' do | ||
| Foreman::Cron.instance_variable_set(:@tasks, { hourly: [] }) | ||
|
|
||
| result = Foreman::Cron.run(:hourly) | ||
|
|
||
| assert_equal false, result | ||
| end | ||
| end | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Using the default Rails logger results in the logging to happen to
/var/log/foreman/production.log, not stdout:There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Using
Logger.new($stdout)(orLogger.new($stderr)- but I think stdout is right) instead works: