taskr 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,45 @@
1
+ body {
2
+ font-size: 9pt;
3
+ font-family: Verdana;
4
+ }
5
+
6
+ table {
7
+ border-collapse: collapse;
8
+ }
9
+
10
+ th, td {
11
+ font-size: 9pt;
12
+ padding-left: 2pt;
13
+ padding-right: 6pt;
14
+ vertical-align: top;
15
+ }
16
+
17
+ th {
18
+ text-align: right;
19
+ }
20
+
21
+ thead th {
22
+ background: #dde;
23
+ text-align: left;
24
+ font-size: 8pt;
25
+ }
26
+
27
+ td {
28
+
29
+ }
30
+
31
+ label {
32
+ font-weight: bold;
33
+ }
34
+
35
+ tr.expired td {
36
+ color: #aaa;
37
+ }
38
+
39
+ tr.expired td.job-id {
40
+ text-decoration: line-through;
41
+ }
42
+
43
+ tr.error td {
44
+ background-color: #faa;
45
+ }
@@ -0,0 +1,83 @@
1
+ # This file is part of Taskr.
2
+ #
3
+ # Taskr is free software: you can redistribute it and/or modify
4
+ # it under the terms of the GNU General Public License as published by
5
+ # the Free Software Foundation, either version 3 of the License, or
6
+ # (at your option) any later version.
7
+ #
8
+ # Taskr is distributed in the hope that it will be useful,
9
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
10
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
+ # GNU General Public License for more details.
12
+ #
13
+ # You should have received a copy of the GNU General Public License
14
+ # along with Taskr. If not, see <http://www.gnu.org/licenses/>.
15
+
16
+ $: << File.dirname(File.expand_path(__FILE__))
17
+ require 'taskr/environment'
18
+
19
+ Camping.goes :Taskr
20
+ Taskr.picnic!
21
+
22
+ require 'taskr/controllers'
23
+
24
+ module Taskr
25
+ @@scheduler = nil
26
+ def self.scheduler=(scheduler)
27
+ @@scheduler = scheduler
28
+ end
29
+ def self.scheduler
30
+ @@scheduler
31
+ end
32
+ end
33
+
34
+ require 'taskr/actions'
35
+ require 'taskr/models'
36
+ require 'taskr/helpers'
37
+ require 'taskr/views'
38
+ require 'taskr/controllers'
39
+
40
+ module Taskr
41
+ include Taskr::Models
42
+ end
43
+
44
+ include Taskr::Models
45
+
46
+ def Taskr.create
47
+ $LOG.info "Initializing Taskr..."
48
+ Taskr::Models::Base.establish_connection(Taskr::Conf.database)
49
+ Taskr::Models.create_schema
50
+
51
+ if self::Conf[:external_actions]
52
+ if self::Conf[:external_actions].kind_of? Array
53
+ external_actions = self::Conf[:external_actions]
54
+ else
55
+ external_actions = [self::Conf[:external_actions]]
56
+ end
57
+ external_actions.each do |f|
58
+ $LOG.info "Loading additional action definitions from #{self::Conf[:external_actions]}..."
59
+ require f
60
+ end
61
+ end
62
+ end
63
+
64
+ def Taskr.prestart
65
+ $LOG.info "Starting OpenWFE Scheduler..."
66
+
67
+ Taskr.scheduler = OpenWFE::Scheduler.new
68
+ Taskr.scheduler.start
69
+
70
+ $LOG.debug "Scheduler is: #{Taskr.scheduler.inspect}"
71
+
72
+ tasks = Taskr::Models::Task.find(:all)
73
+
74
+ $LOG.info "Scheduling #{tasks.length} persisted tasks..."
75
+
76
+ tasks.each do |t|
77
+ t.schedule! Taskr.scheduler
78
+ end
79
+
80
+ Taskr.scheduler.instance_variable_get(:@scheduler_thread).run
81
+ end
82
+
83
+ Taskr.start_picnic
@@ -0,0 +1,230 @@
1
+ # This file is part of Taskr.
2
+ #
3
+ # Taskr is free software: you can redistribute it and/or modify
4
+ # it under the terms of the GNU General Public License as published by
5
+ # the Free Software Foundation, either version 3 of the License, or
6
+ # (at your option) any later version.
7
+ #
8
+ # Taskr is distributed in the hope that it will be useful,
9
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
10
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
+ # GNU General Public License for more details.
12
+ #
13
+ # You should have received a copy of the GNU General Public License
14
+ # along with Taskr. If not, see <http://www.gnu.org/licenses/>.
15
+
16
+ require 'openwfe/util/scheduler'
17
+
18
+ #require 'active_resource'
19
+ require 'restr'
20
+
21
+
22
+ unless $LOG
23
+ $LOG = Logger.new(STDERR)
24
+ $LOG.level = Logger::ERROR
25
+ end
26
+
27
+ module Taskr
28
+ module Actions
29
+
30
+ def self.list
31
+ actions = []
32
+ Taskr::Actions.constants.each do |m|
33
+ a = Taskr::Actions.const_get(m)
34
+ actions << a if a < Taskr::Actions::Base
35
+ end
36
+ return actions
37
+ end
38
+
39
+ # The base class for all Actions.
40
+ # Extend this to define your own custom Action.
41
+ class Base
42
+ include OpenWFE::Schedulable
43
+
44
+ class_inheritable_accessor :parameters
45
+ class_inheritable_accessor :description
46
+
47
+ attr_accessor :parameters
48
+ attr_accessor :task
49
+
50
+ def initialize(parameters)
51
+ self.parameters = HashWithIndifferentAccess.new(parameters)
52
+ end
53
+
54
+ def execute
55
+ raise NotImplementedError, "Implement me!"
56
+ end
57
+
58
+ def trigger(trigger_args = {})
59
+ begin
60
+ $LOG.info("Executing task #{self.task.name}")
61
+ execute
62
+ task.update_attribute(:last_triggered, Time.now)
63
+ task.update_attribute(:last_triggered_error, nil)
64
+ rescue => e
65
+ $LOG.error(e)
66
+ $LOG.debug(e.backtrace.to_s)
67
+ task.update_attribute(:last_triggered, Time.now)
68
+ task.update_attribute(:last_triggered_error, {:type => e.class.to_s, :message => e.message})
69
+ raise e
70
+ end
71
+ end
72
+ end
73
+
74
+ # Do not extend this class. It is used internally to schedule multiple
75
+ # actions per task.
76
+ #
77
+ # If you want to define your own custom Action, extend Taskr::Actions::Base
78
+ class Multi
79
+ include OpenWFE::Schedulable
80
+
81
+ attr_accessor :actions
82
+ attr_accessor :task
83
+
84
+ def initialize
85
+ self.actions = []
86
+ end
87
+
88
+ def trigger(trigger_args = {})
89
+ begin
90
+ $LOG.info("Executing task #{self.name}")
91
+ actions.each do |a|
92
+ a.execute
93
+ end
94
+ # TODO: maybe we should store last_triggered time on a per-action basis?
95
+ task.update_attribute(:last_triggered, Time.now)
96
+ rescue => e
97
+ $LOG.error(e)
98
+ $LOG.debug("#{e.stacktrace}")
99
+ task.update_attribute(:last_triggered, Time.now)
100
+ task.update_attribute(:last_triggered_error, e)
101
+ raise e
102
+ end
103
+ end
104
+ end
105
+
106
+ class Shell < Base
107
+ self.parameters = ['command', 'as_user']
108
+ self.description = "Execute a shell command (be careful!)"
109
+
110
+ def execute
111
+ if parameters.kind_of? Hash
112
+ user = parameters['as_user']
113
+ cmd = parameters['command']
114
+ else
115
+ user = nil
116
+ end
117
+
118
+ if user
119
+ `sudo -u #{user} #{cmd}`
120
+ else
121
+ `#{cmd}`
122
+ end
123
+
124
+ unless $?.success?
125
+ raise "Shell command failed (#{$?}): #{cmd}"
126
+ end
127
+ end
128
+ end
129
+
130
+ class Ruby < Base
131
+ self.parameters = ['code']
132
+ self.description = "Execute some Ruby code."
133
+
134
+ def execute
135
+ code = parameters['code']
136
+ eval code
137
+ end
138
+ end
139
+
140
+ # class ActiveResource < Base
141
+ # self.parameters = ['site', 'resource', 'action', 'parameters']
142
+ # self.description = "Perform a REST call on a remote service using ActiveResource."
143
+ #
144
+ # def execute
145
+ # $LOG.debug self
146
+ # ::ActiveResource::Base.logger = $LOG
147
+ # ::ActiveResource::Base.logger.progname = (task ? task.to_s : self)
148
+ #
149
+ # eval %{
150
+ # class Proxy < ::ActiveResource::Base
151
+ # self.site = "#{parameters['site']}"
152
+ # self.collection_name = "#{parameters['resource'].pluralize}"
153
+ # end
154
+ # }
155
+ #
156
+ # begin
157
+ # case parameters['action']
158
+ # when 'create'
159
+ # obj = Proxy.new(parameters['parameters'])
160
+ # obj.save
161
+ # when 'update', "'update' action is not implemented"
162
+ # raise NotImplementedError
163
+ # when 'delete'
164
+ # Proxy.delete(parameters['parameters'])
165
+ # when 'find'
166
+ # raise NotImplementedError, "'find' action is not implemented"
167
+ # else
168
+ # raise ArgumentError, "unknown action #{parameters['action'].inspect}"
169
+ # end
170
+ # rescue ::ActiveResource::ServerError => e
171
+ # $LOG.error #{self} ERROR: #{e.methods.inspect}"
172
+ # raise e
173
+ # end
174
+ # end
175
+ # end
176
+ #
177
+ # class Howlr < ActiveResource
178
+ # self.parameters = ['site', 'from', 'recipients', 'subject', 'body']
179
+ # self.description = "Send a message through a Howlr service."
180
+ #
181
+ # def execute
182
+ # parameters['action'] = 'create'
183
+ # parameters['resource'] = 'message'
184
+ # parameters['parameters'] = {
185
+ # 'from' => parameters['from'],
186
+ # 'recipients' => parameters['recipients'],
187
+ # 'subject' => parameters['subject'],
188
+ # 'body' => parameters['body']
189
+ # }
190
+ #
191
+ # super
192
+ # end
193
+ # end
194
+
195
+ class Rest < Base
196
+ self.parameters = ['method', 'url', 'params', 'username', 'password']
197
+ self.description = "Perform a REST call on a remote service."
198
+
199
+ def execute
200
+ auth = nil
201
+ if parameters['username']
202
+ auth = {}
203
+ auth['username'] = parameters['username'] if parameters['username']
204
+ auth['password'] = parameters['password'] if parameters['password']
205
+ end
206
+
207
+ Restr.logger = $LOG
208
+ Restr.do(parameters['method'], parameters['url'], parameters['params'], auth)
209
+ end
210
+ end
211
+
212
+ class Howlr < Rest
213
+ self.parameters = ['url', 'from', 'recipients', 'subject', 'body', 'username', 'password']
214
+ self.description = "Send a message through a Howlr service."
215
+
216
+ def execute
217
+ parameters['method'] = 'post'
218
+ parameters['params'] = {
219
+ 'from' => parameters['from'],
220
+ 'recipients' => parameters['recipients'],
221
+ 'subject' => parameters['subject'],
222
+ 'body' => parameters['body'],
223
+ 'format' => 'XML'
224
+ }
225
+
226
+ super
227
+ end
228
+ end
229
+ end
230
+ end
@@ -0,0 +1,164 @@
1
+ # This file is part of Taskr.
2
+ #
3
+ # Taskr is free software: you can redistribute it and/or modify
4
+ # it under the terms of the GNU General Public License as published by
5
+ # the Free Software Foundation, either version 3 of the License, or
6
+ # (at your option) any later version.
7
+ #
8
+ # Taskr is distributed in the hope that it will be useful,
9
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
10
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
+ # GNU General Public License for more details.
12
+ #
13
+ # You should have received a copy of the GNU General Public License
14
+ # along with Taskr. If not, see <http://www.gnu.org/licenses/>.
15
+
16
+ module Taskr::Controllers
17
+
18
+ class ActionTypes < REST 'action_types'
19
+ def list
20
+ @actions = Taskr::Actions.list
21
+
22
+ render :action_list
23
+ end
24
+ end
25
+
26
+ class Actions < REST 'actions'
27
+ def parameters_form(id)
28
+ @num = @input[:num] || 0
29
+ @action = Taskr::Actions.list.find {|a| a.to_s =~ Regexp.new("#{id}$")}
30
+ if @action
31
+ render :action_parameters_form
32
+ else
33
+ @status = 404
34
+ "Action #{id.inspect} not defined"
35
+ end
36
+ end
37
+
38
+ def new
39
+ @num = @input[:num] || 0
40
+ @actions = Taskr::Actions.list
41
+ render :action_form
42
+ end
43
+ end
44
+
45
+ class Tasks < REST 'tasks'
46
+ include Taskr::Models
47
+
48
+ def list
49
+ @tasks = Task.find(:all, :include => [:task_actions])
50
+
51
+ render :tasks_list
52
+ end
53
+
54
+ def new
55
+ @actions = Taskr::Actions.list
56
+
57
+ render :new_task
58
+ end
59
+
60
+ def read(id)
61
+ @task = Task.find(id, :include => [:task_actions])
62
+
63
+ render :view_task
64
+ end
65
+
66
+ def create
67
+ begin
68
+ puts @input.class
69
+ puts @input.to_xml if @input.kind_of?(XmlSimple)
70
+ puts @input.inspect
71
+
72
+ # the "0" is for compatibility with PHP's Zend_Rest_Client
73
+ task_data = @input[:task] || @input["0"] || @input
74
+
75
+ name = task_data[:name]
76
+ created_by = @env['REMOTE_HOST']
77
+ schedule_method = task_data[:schedule_method]
78
+ schedule_when = task_data[:schedule_when]
79
+
80
+ @task = Task.new(
81
+ :name => name,
82
+ :created_by => created_by,
83
+ :schedule_method => schedule_method,
84
+ :schedule_when => schedule_when
85
+ )
86
+
87
+ # some gymnastics here to provide compatibility for the way various
88
+ # REST client libraries submit data
89
+ actions_data = task_data[:actions] || task_data[:action]
90
+
91
+ raise ArgumentError, "Missing action(s) parameter." if actions_data.blank?
92
+
93
+ if actions_data.kind_of?(Array)
94
+ actions = actions_data
95
+ elsif actions_data["0"]
96
+ actions = []
97
+ actions_data.each do |i,a|
98
+ actions << a
99
+ end
100
+ else
101
+ actions = actions_data[:action]
102
+ end
103
+
104
+ actions = [actions] unless actions.kind_of? Array
105
+ puts actions.inspect
106
+
107
+ i = 0
108
+ actions.each do |a|
109
+ puts a.inspect
110
+ action_class_name = a[:action_class_name]
111
+ action_class_name = "Taskr::Actions::#{action_class_name}" unless action_class_name =~ /^Taskr::Actions::/
112
+
113
+ begin
114
+ action_class = action_class_name.constantize
115
+ unless action_class.include? OpenWFE::Schedulable
116
+ raise ArgumentError,
117
+ "#{a[:action_class_name].inspect} cannot be used as an action because it does not include the OpenWFE::Schedulable module."
118
+ end
119
+ rescue NameError
120
+ raise ArgumentError,
121
+ "#{a[:action_class_name].inspect} is not defined (i.e. there is no such action class)."
122
+ end
123
+
124
+ action = TaskAction.new(:order => a[:order] || i, :action_class_name => action_class_name)
125
+
126
+ action_class.parameters.each do |p|
127
+ action.action_parameters << TaskActionParameter.new(:name => p, :value => a[p])
128
+ end
129
+
130
+ @task.task_actions << action
131
+ i += 1
132
+ end
133
+
134
+
135
+ unless @task.valid?
136
+ @status = 500
137
+ return render(:new_task)
138
+ end
139
+
140
+
141
+ @task.schedule! Taskr.scheduler
142
+
143
+ if @task.save
144
+ location = "/tasks/#{@task.id}?format=#{@format}"
145
+ $LOG.debug "#{@task} saved successfuly. Setting Location header to #{location.inspect}."
146
+ @headers['Location'] = location
147
+ end
148
+
149
+ return render(:view_task)
150
+ rescue => e
151
+ puts e.inspect
152
+ puts e.backtrace
153
+ raise e
154
+ end
155
+ end
156
+
157
+ def destroy(id)
158
+ @task = Task.find(id)
159
+ Taskr.scheduler.unschedule(@task.scheduler_job_id) if @task.scheduler_job_id
160
+ @task.destroy
161
+ return redirect('/tasks')
162
+ end
163
+ end
164
+ end