Changeset af8813eb9aca5c38c2d1715935d0917036042d5c
Dimitrij Denissenko
over 1 year ago
Changeset af8813e
Improved TaskRunner, changed config storage frmo YAML to DB
Affected files:
db/migrate/20090419112736_create_tasks.rb
lib/retrospectiva/task_manager.rb
lib/retrospectiva/task_manager/parser.rb
lib/retrospectiva/task_manager/task.rb
config/runtime/tasks.yml.default
lib/retrospectiva/tasks.rb
app/controllers/admin/tasks_controller.rb (Quick Diff)
app/controllers/admin/tasks_controller.rb
| 841111f | af8813e |
|---|
1 | #-- | 1 | #-- |
2 | # Copyright (C) 2008 Dimitrij Denissenko | 2 | # Copyright (C) 2009 Dimitrij Denissenko |
3 | # Please read LICENSE document for more information. | 3 | # Please read LICENSE document for more information. |
4 | #++ | 4 | #++ |
5 | class Admin::TasksController < AdminAreaController | 5 | class Admin::TasksController < AdminAreaController |
6 | verify :params => :tasks, :only => :save | 6 | verify :params => :tasks, :only => :save |
7 | | 7 | |
8 | def index | 8 | def index |
9 | @tasks = Retrospectiva::Tasks.tasks | 9 | @tasks = Retrospectiva::TaskManager::Parser.new.tasks |
10 | end | 10 | end |
11 | | 11 | |
12 | def save | 12 | def save |
13 | configuration = params[:tasks].inject({}) do |result, (name, interval)| | 13 | params[:tasks].each do |name, interval| |
14 | seconds = TimeInterval.in_seconds(interval[:count], interval[:units]) rescue 0 | 14 | seconds = TimeInterval.in_seconds(interval[:count], interval[:units]) rescue 0 |
15 | result.merge name => seconds | 15 | Retrospectiva::TaskManager::Task.create_or_update(name, seconds) |
16 | end | 16 | end if params[:tasks].is_a?(Hash) |
17 | Retrospectiva::Tasks.update(configuration) | | |
18 | flash[:notice] = _('Task configuration was successfully updated.') | 17 | flash[:notice] = _('Task configuration was successfully updated.') |
19 | redirect_to admin_tasks_path | 18 | redirect_to admin_tasks_path |
20 | end | 19 | end |
|---|
app/models/queued_mail.rb (Quick Diff)
app/models/queued_mail.rb
| 66c1706 | af8813e |
|---|
1 | #-- | 1 | #-- |
2 | # Copyright (C) 2008 Dimitrij Denissenko | 2 | # Copyright (C) 2009 Dimitrij Denissenko |
3 | # Please read LICENSE document for more information. | 3 | # Please read LICENSE document for more information. |
4 | #++ | 4 | #++ |
5 | class QueuedMail < ActiveRecord::Base | 5 | class QueuedMail < ActiveRecord::Base |
|---|
... | | ... | |
|---|
9 | named_scope :pending, :conditions => ['delivered_at IS NULL'], :order => 'created_at' | 9 | named_scope :pending, :conditions => ['delivered_at IS NULL'], :order => 'created_at' |
10 | | 10 | |
11 | def mailer_class | 11 | def mailer_class |
12 | @mailer_class ||= mailer_class_name.constantize rescue nil | 12 | mailer_class_name.constantize rescue nil |
13 | end | 13 | end |
14 | | 14 | |
15 | def deliver! | 15 | def deliver! |
16 | mailer_class ? mailer_class.deliver(object) && deactivate! : false | 16 | if mailer_class |
| | 17 | mailer_class.deliver(object) |
| | 18 | deactivate! |
| | 19 | else |
| | 20 | false |
| | 21 | end |
17 | end | 22 | end |
18 | | 23 | |
19 | def deactivate! | 24 | def deactivate! |
20 | t = self.class.default_timezone == :utc ? Time.now.utc : Time.now | 25 | update_attribute :delivered_at, Time.now.utc |
21 | update_attribute :delivered_at, t | | |
22 | end | 26 | end |
23 | | 27 | |
24 | end | 28 | end |
|---|
app/views/admin/tasks/_task.html.erb (Quick Diff)
app/views/admin/tasks/_task.html.erb
| 66c1706 | af8813e |
|---|
2 | <td class="strong"><%=h task.name.humanize %></td> | 2 | <td class="strong"><%=h task.name.humanize %></td> |
3 | <td><%=h task.description %></td> | 3 | <td><%=h task.description %></td> |
4 | <td> | 4 | <td> |
5 | <% if task.started -%> | 5 | <% if task.running? -%> |
6 | <%= _('Started') %> <%=h time_interval_in_words(task.started) %> | 6 | <%= _('Started') %> <%=h time_interval_in_words(task.started_at) %> |
7 | <% elsif task.last_run -%> | 7 | <% elsif task.finished_at > 1.year.ago -%> |
8 | <%= _('Last run') %> <%=h time_interval_in_words(task.last_run) %> | 8 | <%= _('Finished') %> <%=h time_interval_in_words(task.finished_at) %> |
9 | <% else -%> | 9 | <% else -%> |
10 | – | 10 | – |
11 | <% end -%> | 11 | <% end -%> |
|---|
config/environments/development.rb (Quick Diff)
config/environments/development.rb
| 66c1706 | af8813e |
|---|
14 | config.action_controller.perform_caching = false | 14 | config.action_controller.perform_caching = false |
15 | | 15 | |
16 | # Don't care if the mailer can't send | 16 | # Don't care if the mailer can't send |
17 | config.action_mailer.raise_delivery_errors = false | 17 | config.action_mailer.raise_delivery_errors = false |
| | 18 | config.action_mailer.perform_deliveries = false |
|---|
db/default_content.rb (Quick Diff)
db/default_content.rb
| de482d6 | af8813e |
|---|
32 | creator.create_group unless Group.exists?(:name => 'Default') | 32 | creator.create_group unless Group.exists?(:name => 'Default') |
33 | creator.create_public unless User.exists?(:name => 'Public') | 33 | creator.create_public unless User.exists?(:name => 'Public') |
34 | creator.create_admin unless User.exists?(:admin => true, :active => true) | 34 | creator.create_admin unless User.exists?(:admin => true, :active => true) |
| | 35 | creator.create_tasks if Retrospectiva::TaskManager::Task.count.zero? |
35 | end | 36 | end |
36 | end | 37 | end |
37 | | 38 | |
|---|
... | | ... | |
|---|
49 | end | 50 | end |
50 | end | 51 | end |
51 | | 52 | |
| | 53 | def create_tasks |
| | 54 | puts 'Creating default tasks' |
| | 55 | Retrospectiva::TaskManager::Task.create :name => 'sync_repositories', :interval => 600 |
| | 56 | Retrospectiva::TaskManager::Task.create :name => 'process_mails', :interval => 300 |
| | 57 | end |
| | 58 | |
52 | def create_group | 59 | def create_group |
53 | puts 'Creating default group' | 60 | puts 'Creating default group' |
54 | Group.create!(:name => 'Default') | 61 | Group.create!(:name => 'Default') |
|---|
db/schema.core.rb (Quick Diff)
db/schema.core.rb
| 4f2ff01 | af8813e |
|---|
9 | # | 9 | # |
10 | # It's strongly recommended to check this file into your version control system. | 10 | # It's strongly recommended to check this file into your version control system. |
11 | | 11 | |
12 | ActiveRecord::Schema.define(:version => 20090124095048) do | 12 | ActiveRecord::Schema.define(:version => 20090419112736) do |
13 | | 13 | |
14 | create_table "attachments", :force => true do |t| | 14 | create_table "attachments", :force => true do |t| |
15 | t.string "file_name" | 15 | t.string "file_name" |
|---|
... | | ... | |
|---|
186 | | 186 | |
187 | add_index "tags", ["name"], :name => "i_tags_on_name" | 187 | add_index "tags", ["name"], :name => "i_tags_on_name" |
188 | | 188 | |
| | 189 | create_table "tasks", :force => true do |t| |
| | 190 | t.string "name", :limit => 60 |
| | 191 | t.datetime "started_at", :default => '1970-01-01 00:00:00', :null => false |
| | 192 | t.datetime "finished_at", :default => '1970-01-01 00:00:00', :null => false |
| | 193 | t.integer "interval", :default => 0, :null => false |
| | 194 | end |
| | 195 | |
| | 196 | add_index "tasks", ["name"], :name => "i_tasks_name" |
| | 197 | |
189 | create_table "ticket_changes", :force => true do |t| | 198 | create_table "ticket_changes", :force => true do |t| |
190 | t.integer "ticket_id" | 199 | t.integer "ticket_id" |
191 | t.string "author", :limit => 75 | 200 | t.string "author", :limit => 75 |
|---|
lib/retrospectiva.rb (Quick Diff)
lib/retrospectiva.rb
| 66c1706 | af8813e |
|---|
1 | require 'retrospectiva/core_ext' | 1 | require 'retrospectiva/core_ext' |
2 | require 'retrospectiva/session' | 2 | require 'retrospectiva/session' |
3 | require 'retrospectiva/tasks' | | |
4 | require 'retrospectiva/misc' | 3 | require 'retrospectiva/misc' |
5 | | 4 | |
6 | require 'retrospectiva/extension_manager' | 5 | require 'retrospectiva/extension_manager' |
7 | require 'retrospectiva/configuration_manager' | 6 | require 'retrospectiva/configuration_manager' |
8 | require 'retrospectiva/access_manager' | 7 | require 'retrospectiva/access_manager' |
9 | require 'retrospectiva/previewable' | 8 | require 'retrospectiva/previewable' |
| | 9 | require 'retrospectiva/task_manager' |
|---|
lib/tasks/rspec.rake (Quick Diff)
lib/tasks/rspec.rake
| b9c19e3 | af8813e |
|---|
1 | gem 'test-unit', '1.2.3' if RUBY_VERSION.to_f >= 1.9 | | |
2 | | | |
3 | # Don't load rspec if running "rake gems:*" | 1 | # Don't load rspec if running "rake gems:*" |
4 | unless ARGV.any? {|a| a =~ /^gems/} | 2 | unless ARGV.any? {|a| a =~ /^gems/} |
| | 3 | gem 'test-unit', '1.2.3' if RUBY_VERSION.to_f >= 1.9 |
5 | | 4 | |
6 | begin | 5 | begin |
7 | require 'spec/rake/spectask' | 6 | require 'spec/rake/spectask' |
|---|
script/retro_tasks (Quick Diff)
script/retro_tasks
| 66c1706 | af8813e |
|---|
1 | #!/usr/bin/env ruby | 1 | #!/usr/bin/env ruby |
2 | | 2 | |
3 | #-- | 3 | require File.expand_path(File.join(File.dirname(__FILE__), '..', 'config', 'boot')) |
4 | # Copyright (C) 2007 Dimitrij Denissenko | | |
5 | # Please read LICENSE document for more information. | | |
6 | #++ | | |
7 | | 4 | |
| | 5 | require 'active_record' |
| | 6 | require 'active_support' |
| | 7 | require 'erb' |
8 | require 'yaml' | 8 | require 'yaml' |
9 | require 'logger' | | |
10 | | 9 | |
11 | class RetroTasks | 10 | Time.zone_default = Time.__send__(:get_zone, 'UTC') |
12 | | 11 | ActiveRecord::Base.time_zone_aware_attributes = true |
13 | def self.run | 12 | ActiveRecord::Base.default_timezone = :utc |
14 | new.run | | |
15 | end | | |
16 | | 13 | |
17 | attr_reader :logger | 14 | load 'retrospectiva/task_manager.rb' |
18 | | | |
19 | def initialize | | |
20 | @logger = Logger.new(log_file) | | |
21 | @logger.level = Logger::INFO | | |
22 | end | | |
23 | | 15 | |
24 | def run | 16 | manager = Retrospectiva::TaskManager.new |
25 | return if tasks_to_run.empty? | 17 | if ARGV.include?('tasks') |
26 | | 18 | puts "Tasks: " + manager.tasks.map(&:name).inspect |
27 | require 'rubygems' | 19 | elsif ARGV.include?('pending') |
28 | load File.join(RAILS_ROOT, 'Rakefile') | 20 | puts "Pending: " + manager.pending.map(&:name).inspect |
29 | | 21 | else |
30 | tasks_to_run.each do |task_name| | 22 | manager.run |
31 | if task = Rake.application.lookup("retro:#{task_name}") | | |
32 | update_configuration(task_name, :started => current_minute) | | |
33 | begin | | |
34 | task.invoke | | |
35 | rescue Exception => ex | | |
36 | log_exception(ex) | | |
37 | ensure | | |
38 | update_configuration(task_name, :last_run => current_minute, :started => nil) | | |
39 | end | | |
40 | else | | |
41 | delete_configuration(task_name) | | |
42 | end | | |
43 | end | | |
44 | end | | |
45 | | | |
46 | protected | | |
47 | | | |
48 | def config | | |
49 | file = File.exist?(config_file) ? config_file : "#{config_file}.default" | | |
50 | (YAML.load_file(file) rescue {}) || {} | | |
51 | end | | |
52 | | | |
53 | def config_file | | |
54 | File.join(RAILS_ROOT, 'config', 'runtime', 'tasks.yml') | | |
55 | end | | |
56 | | | |
57 | def log_file | | |
58 | File.join(RAILS_ROOT, 'log', 'tasks.log') | | |
59 | end | | |
60 | | | |
61 | def log_exception(exception) | | |
62 | clean_trace = exception.backtrace.collect { |line| line.gsub(RAILS_ROOT, '') } | | |
63 | self.logger.fatal( | | |
64 | "\n\n#{exception.class} (#{exception.message}):\n " + | | |
65 | clean_trace.join("\n ") + | | |
66 | "\n\n" | | |
67 | ) | | |
68 | end | | |
69 | | | |
70 | def current_minute | | |
71 | time = Time.now.utc | | |
72 | time -= time.sec | | |
73 | end | | |
74 | | | |
75 | def tasks_to_run | | |
76 | @tasks_to_run ||= self.config.inject([]) do |result, (task, task_conf)| | | |
77 | if task_conf.is_a?(Hash) | | |
78 | interval = Kernel.Integer(task_conf[:interval]) rescue 0 | | |
79 | if interval > 0 && !task_conf[:started] | | |
80 | last_run = task_conf[:last_run] || Time.at(0).utc | | |
81 | if (last_run + interval).to_i <= current_minute.to_i | | |
82 | result << task | | |
83 | end | | |
84 | end | | |
85 | end | | |
86 | result | | |
87 | end | | |
88 | end | | |
89 | | | |
90 | def update_configuration(task_name, attributes) | | |
91 | retries = 0 | | |
92 | begin | | |
93 | task_conf = self.config[task_name].merge(attributes) | | |
94 | write_configuration( self.config.merge(task_name => task_conf) ) | | |
95 | rescue | | |
96 | if (retries += 1) < 3 | | |
97 | sleep 1 | | |
98 | retry | | |
99 | else | | |
100 | raise | | |
101 | end | | |
102 | end | | |
103 | end | | |
104 | | | |
105 | def delete_configuration(task_name) | | |
106 | write_configuration( self.config.delete_if{|k,v| k == task_name} ) | | |
107 | end | | |
108 | | | |
109 | def write_configuration(hash) | | |
110 | File.open(config_file, 'w') do |f| | | |
111 | YAML.dump(hash, f) | | |
112 | f.close | | |
113 | end | | |
114 | end | | |
115 | | | |
116 | end | 23 | end |
117 | | | |
118 | RAILS_ROOT = File.expand_path(File.join(File.dirname(__FILE__), '..')) | | |
119 | RetroTasks.run | | |
|---|
spec/controllers/admin/tasks_controller_spec.rb (Quick Diff)
spec/controllers/admin/tasks_controller_spec.rb
| 66c1706 | af8813e |
|---|
5 | | 5 | |
6 | before do | 6 | before do |
7 | permit_access! | 7 | permit_access! |
8 | @tasks = [mock('Task1')] | 8 | @task = mock('Task1') |
9 | Retrospectiva::Tasks.stub!(:tasks).and_return(@tasks) | 9 | @tasks = [@task] |
| | 10 | @parser = mock(Retrospectiva::TaskManager::Parser, :tasks => @tasks) |
| | 11 | Retrospectiva::TaskManager::Parser.stub!(:new).and_return(@parser) |
10 | end | 12 | end |
11 | | 13 | |
12 | describe "handling GET /admin/tasks" do | 14 | describe "handling GET /admin/tasks" do |
|---|
... | | ... | |
|---|
17 | it_should_successfully_render_template('index') | 19 | it_should_successfully_render_template('index') |
18 | | 20 | |
19 | it "should query the tasks" do | 21 | it "should query the tasks" do |
20 | Retrospectiva::Tasks.should_receive(:tasks).and_return(@tasks) | 22 | Retrospectiva::TaskManager::Parser.should_receive(:new).and_return(@parser) |
| | 23 | @parser.should_receive(:tasks).and_return(@tasks) |
21 | do_get | 24 | do_get |
22 | end | 25 | end |
23 | | 26 | |
|---|
... | | ... | |
|---|
32 | describe "handling PUT /admin/tasks/save" do | 35 | describe "handling PUT /admin/tasks/save" do |
33 | | 36 | |
34 | before do | 37 | before do |
35 | Retrospectiva::Tasks.stub!(:update).and_return true | 38 | Retrospectiva::TaskManager::Task.stub!(:update_or_create).and_return @task |
36 | end | 39 | end |
37 | | 40 | |
38 | def do_put | 41 | def do_put |
|---|
... | | ... | |
|---|
43 | get :save | 46 | get :save |
44 | response.code.should == '400' | 47 | response.code.should == '400' |
45 | end | 48 | end |
46 | | 49 | |
47 | it "should update the configuration" do | 50 | it "should find or create the affected task" do |
48 | Retrospectiva::Tasks.should_receive(:update).with('task_a' => 600).and_return true | 51 | Retrospectiva::TaskManager::Task.should_receive(:create_or_update).with('task_a', 600).and_return @task |
49 | do_put | 52 | do_put |
50 | end | 53 | end |
51 | | 54 | |
52 | it "should redirect to task overview" do | 55 | it "should redirect to task overview" do |
53 | do_put | 56 | do_put |
54 | response.should be_redirect | 57 | response.should be_redirect |
|---|