proby 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +3 -0
- data/Gemfile +3 -0
- data/Gemfile.lock +36 -0
- data/README.md +95 -0
- data/Rakefile +38 -0
- data/lib/proby/exceptions.rb +13 -0
- data/lib/proby/notifier.rb +28 -0
- data/lib/proby/proby_http_api.rb +28 -0
- data/lib/proby/proby_task.rb +278 -0
- data/lib/proby/resque_plugin.rb +65 -0
- data/lib/proby/version.rb +3 -0
- data/lib/proby.rb +75 -0
- data/proby.gemspec +35 -0
- data/test/notifier_test.rb +60 -0
- data/test/proby_task_test.rb +375 -0
- data/test/test_helper.rb +16 -0
- metadata +246 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
proby (2.0.0)
|
5
|
+
chronic (~> 0.6.7)
|
6
|
+
httparty (~> 0.8.1)
|
7
|
+
multi_json (~> 1.2.0)
|
8
|
+
|
9
|
+
GEM
|
10
|
+
remote: http://rubygems.org/
|
11
|
+
specs:
|
12
|
+
bluecloth (2.1.0)
|
13
|
+
chronic (0.6.7)
|
14
|
+
fakeweb (1.3.0)
|
15
|
+
httparty (0.8.3)
|
16
|
+
multi_json (~> 1.0)
|
17
|
+
multi_xml
|
18
|
+
json (1.6.6)
|
19
|
+
multi_json (1.2.0)
|
20
|
+
multi_xml (0.4.4)
|
21
|
+
rake (0.9.2.2)
|
22
|
+
shoulda (2.11.3)
|
23
|
+
yard (0.6.8)
|
24
|
+
|
25
|
+
PLATFORMS
|
26
|
+
ruby
|
27
|
+
|
28
|
+
DEPENDENCIES
|
29
|
+
bluecloth (~> 2.1.0)
|
30
|
+
bundler (>= 1.0.0)
|
31
|
+
fakeweb (~> 1.3.0)
|
32
|
+
json (~> 1.6.6)
|
33
|
+
proby!
|
34
|
+
rake (~> 0.9.0)
|
35
|
+
shoulda (~> 2.11.3)
|
36
|
+
yard (~> 0.6.4)
|
data/README.md
ADDED
@@ -0,0 +1,95 @@
|
|
1
|
+
# Proby
|
2
|
+
A simple library for working with the Proby task monitoring application.
|
3
|
+
|
4
|
+
|
5
|
+
Installation
|
6
|
+
------------
|
7
|
+
|
8
|
+
### RubyGems ###
|
9
|
+
Proby can be installed using RubyGems
|
10
|
+
|
11
|
+
gem install proby
|
12
|
+
|
13
|
+
Inside your script, be sure to
|
14
|
+
|
15
|
+
require "rubygems"
|
16
|
+
require "proby"
|
17
|
+
|
18
|
+
### Bundler ###
|
19
|
+
If you're using Bundler, add the following to your Gemfile
|
20
|
+
|
21
|
+
gem "proby"
|
22
|
+
|
23
|
+
and then run
|
24
|
+
|
25
|
+
bundle install
|
26
|
+
|
27
|
+
|
28
|
+
Setup
|
29
|
+
-----
|
30
|
+
Before notifications can be sent, you must tell Proby your API key. This only needs to be done once,
|
31
|
+
and should ideally be done inside your apps initialization code.
|
32
|
+
|
33
|
+
Proby.api_key = "b4fe1200c105012efde3482a1411a947"
|
34
|
+
|
35
|
+
In addition, you can optionally give Proby a logger to use.
|
36
|
+
|
37
|
+
Proby.logger = Rails.logger
|
38
|
+
|
39
|
+
|
40
|
+
Sending Notifications
|
41
|
+
---------------------
|
42
|
+
To send a start notification
|
43
|
+
|
44
|
+
Proby.send_start_notification(task_api_id)
|
45
|
+
|
46
|
+
To send a finish notification
|
47
|
+
|
48
|
+
Proby.send_finish_notification(task_api_id)
|
49
|
+
|
50
|
+
Specifying the `task_api_id` when calling the notification methods is optional. If it is not provided,
|
51
|
+
Proby will use the value of the `PROBY_TASK_ID` environment variable. If no task id is specified
|
52
|
+
in the method call, and no value is set in the `PROBY_TASK_ID` environment variable, then no notification
|
53
|
+
will be sent.
|
54
|
+
|
55
|
+
|
56
|
+
The Resque Plugin
|
57
|
+
-----------------
|
58
|
+
The Resque plugin will automatically send start and finish notifications to Proby when your job
|
59
|
+
starts and finishes. Simply `extend Proby::ResquePlugin` in your Resque job. The task id
|
60
|
+
can either be pulled from the `PROBY_TASK_ID` environment variable, or specified in the job itself
|
61
|
+
by setting the `@proby_id` attribute to the task id.
|
62
|
+
|
63
|
+
class SomeJob
|
64
|
+
extend Proby::ResquePlugin
|
65
|
+
@proby_id = 'abc123' # Or simply let it use the value in the PROBY_TASK_ID environment variable
|
66
|
+
|
67
|
+
self.perform
|
68
|
+
do_stuff
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
|
73
|
+
Managing Tasks
|
74
|
+
--------------
|
75
|
+
The Proby::ProbyTask class can be used to create, read, update, delete, pause, and unpause your
|
76
|
+
tasks on Proby.
|
77
|
+
|
78
|
+
my_tasks = Proby::ProbyTask.find(:all)
|
79
|
+
a_specific_task = Proby::ProbyTask.find("the_proby_task_id")
|
80
|
+
|
81
|
+
task = Proby::ProbyTask.create(:name => 'Task name', :crontab => '* * * * *')
|
82
|
+
|
83
|
+
task.name = "New name"
|
84
|
+
task.save
|
85
|
+
|
86
|
+
task.pause
|
87
|
+
task.unpause
|
88
|
+
|
89
|
+
task.delete
|
90
|
+
|
91
|
+
|
92
|
+
API Doc
|
93
|
+
-------
|
94
|
+
[http://rdoc.info/github/signal/proby-ruby/master/frames](http://rdoc.info/github/signal/proby-ruby/master/frames)
|
95
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'bundler'
|
3
|
+
|
4
|
+
Bundler::GemHelper.install_tasks
|
5
|
+
|
6
|
+
begin
|
7
|
+
Bundler.setup(:default, :development)
|
8
|
+
rescue Bundler::BundlerError => e
|
9
|
+
$stderr.puts e.message
|
10
|
+
$stderr.puts "Run `bundle install` to install missing gems"
|
11
|
+
exit e.status_code
|
12
|
+
end
|
13
|
+
|
14
|
+
require 'rake'
|
15
|
+
require 'rake/testtask'
|
16
|
+
require 'yard'
|
17
|
+
|
18
|
+
task :default => :test
|
19
|
+
|
20
|
+
Rake::TestTask.new(:test) do |test|
|
21
|
+
test.libs << 'lib' << 'test'
|
22
|
+
test.pattern = ENV['TEST'] || "test/**/*_test.rb"
|
23
|
+
test.verbose = true
|
24
|
+
end
|
25
|
+
|
26
|
+
YARD::Rake::YardocTask.new do |t|
|
27
|
+
t.files = ['lib/**/*.rb']
|
28
|
+
end
|
29
|
+
|
30
|
+
desc 'Delete yard, and other generated files'
|
31
|
+
task :clobber => [:clobber_yard]
|
32
|
+
|
33
|
+
desc 'Delete yard generated files'
|
34
|
+
task :clobber_yard do
|
35
|
+
puts 'rm -rf doc .yardoc'
|
36
|
+
FileUtils.rm_rf ['doc', '.yardoc']
|
37
|
+
end
|
38
|
+
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module Proby
|
2
|
+
# Exception raised when a request to Proby fails
|
3
|
+
class ApiException < StandardError; end
|
4
|
+
|
5
|
+
# Exception raised when the api key is not properly set
|
6
|
+
class InvalidApiKeyException < StandardError; end
|
7
|
+
|
8
|
+
# Authentication to Proby failed. Make sure your API key is correct.
|
9
|
+
class AuthFailedException < StandardError; end
|
10
|
+
|
11
|
+
# An invalid parameter was passed to the given method
|
12
|
+
class InvalidParameterException < StandardError; end
|
13
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module Proby
|
2
|
+
class Notifier < ProbyHttpApi
|
3
|
+
|
4
|
+
def self.send_notification(type, proby_task_id, options={})
|
5
|
+
if Proby.api_key.nil?
|
6
|
+
Proby.logger.warn "Proby: No notification sent because API key is not set"
|
7
|
+
return nil
|
8
|
+
end
|
9
|
+
|
10
|
+
proby_task_id = ENV['PROBY_TASK_ID'] if blank?(proby_task_id)
|
11
|
+
if blank?(proby_task_id)
|
12
|
+
Proby.logger.warn "Proby: No notification sent because task ID was not specified"
|
13
|
+
return nil
|
14
|
+
end
|
15
|
+
|
16
|
+
response = post("/api/v1/tasks/#{proby_task_id}/#{type}.json",
|
17
|
+
:body => MultiJson.encode(options),
|
18
|
+
:format => :json,
|
19
|
+
:headers => default_headers)
|
20
|
+
response.code
|
21
|
+
rescue Exception => e
|
22
|
+
Proby.logger.error "Proby: Proby notification failed: #{e.message}"
|
23
|
+
Proby.logger.error e.backtrace
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module Proby
|
2
|
+
class ProbyHttpApi
|
3
|
+
include HTTParty
|
4
|
+
base_uri "https://proby.signalhq.com"
|
5
|
+
default_timeout 5
|
6
|
+
|
7
|
+
protected
|
8
|
+
|
9
|
+
def self.handle_api_failure(response)
|
10
|
+
if response.code == 401
|
11
|
+
raise AuthFailedException.new("Authentication to Proby failed. Make sure your API key is correct.")
|
12
|
+
else
|
13
|
+
message = "API request failed with a response code of #{response.code}. Respone body: #{response.body}"
|
14
|
+
Proby.logger.error message
|
15
|
+
raise ApiException.new(message)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.default_headers
|
20
|
+
{ 'api_key' => Proby.api_key, 'Content-Type' => 'application/json' }
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.blank?(s)
|
24
|
+
s.nil? || s.strip.empty?
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,278 @@
|
|
1
|
+
module Proby
|
2
|
+
|
3
|
+
# Represents the status of a Proby task
|
4
|
+
class ProbyTaskStatus
|
5
|
+
# The description of the task (OK, ERROR, PAUSED)
|
6
|
+
attr_reader :description
|
7
|
+
|
8
|
+
# Any details (why the task is in the ERROR state)
|
9
|
+
attr_reader :details
|
10
|
+
|
11
|
+
def initialize(attributes={})
|
12
|
+
@description = attributes['description']
|
13
|
+
@details = attributes['details']
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
# Represents a task in Proby
|
18
|
+
class ProbyTask < ProbyHttpApi
|
19
|
+
# The name of the task
|
20
|
+
attr_accessor :name
|
21
|
+
|
22
|
+
# The schedule for the task, specified in crontab format
|
23
|
+
attr_accessor :crontab
|
24
|
+
|
25
|
+
# The time zone of the machine executing the task
|
26
|
+
attr_accessor :time_zone
|
27
|
+
|
28
|
+
# The name of the machine that is responsible for running this task
|
29
|
+
attr_accessor :machine
|
30
|
+
|
31
|
+
# Should finish alarms be sent when the task runs longer than expected?
|
32
|
+
attr_accessor :finish_alarms_enabled
|
33
|
+
|
34
|
+
# The maximum amount of time the task is allowed to run before Proby sends
|
35
|
+
# a finish alarm. If not specified, Proby will determine when an alarm should
|
36
|
+
# be sent based on past run times
|
37
|
+
attr_accessor :maximum_run_time
|
38
|
+
|
39
|
+
# The number of minutes to wait for a task to send its start notification after it
|
40
|
+
# should have started before sending an alarm
|
41
|
+
attr_accessor :start_notification_grace_period
|
42
|
+
|
43
|
+
# The number of consecutive tasks that must fail before an alarm is sent
|
44
|
+
attr_accessor :consecutive_alarmed_tasks_required_to_trigger_alarm
|
45
|
+
|
46
|
+
# The API Task ID of the task
|
47
|
+
attr_reader :api_id
|
48
|
+
|
49
|
+
# Is the task currently paused?
|
50
|
+
attr_reader :paused
|
51
|
+
|
52
|
+
# The number of consecutive times this task has triggered an alarm
|
53
|
+
attr_reader :consecutive_alarmed_tasks
|
54
|
+
|
55
|
+
# The date and time this task was created
|
56
|
+
attr_reader :created_at
|
57
|
+
|
58
|
+
# The date and time this task was updated
|
59
|
+
attr_reader :updated_at
|
60
|
+
|
61
|
+
# The current status of the task, represented as a ProbyTaskStatus
|
62
|
+
attr_reader :status
|
63
|
+
|
64
|
+
# <b>Should not be called directly</b>
|
65
|
+
def initialize(attributes={})
|
66
|
+
@name = attributes['name']
|
67
|
+
@api_id = attributes['api_id']
|
68
|
+
@crontab = attributes['crontab']
|
69
|
+
@paused = attributes['paused']
|
70
|
+
@time_zone = attributes['time_zone']
|
71
|
+
@machine = attributes['machine']
|
72
|
+
@finish_alarms_enabled = attributes['finish_alarms_enabled']
|
73
|
+
@maximum_run_time = attributes['maximum_run_time']
|
74
|
+
@start_notification_grace_period = attributes['start_notification_grace_period']
|
75
|
+
@consecutive_alarmed_tasks = attributes['consecutive_alarmed_tasks']
|
76
|
+
@consecutive_alarmed_tasks_required_to_trigger_alarm = attributes['consecutive_alarmed_tasks_required_to_trigger_alarm']
|
77
|
+
@created_at = Chronic.parse attributes['created_at']
|
78
|
+
@updated_at = Chronic.parse attributes['updated_at']
|
79
|
+
@status = ProbyTaskStatus.new(attributes['status']) if attributes['status']
|
80
|
+
end
|
81
|
+
|
82
|
+
# Get a single, or all tasks from Proby.
|
83
|
+
#
|
84
|
+
# @param [Object] param :all if you are fetching all tasks, or the api_id of the Proby task you would like to fetch.
|
85
|
+
#
|
86
|
+
# @return If requesting all tasks, an [Array<ProbyTask>] will be returned. If an api_id was provided, the
|
87
|
+
# ProbyTask with that api_id will be returned if it exists, or nil if it could not be found.
|
88
|
+
#
|
89
|
+
# @example
|
90
|
+
# all_of_my_tasks = ProbyTask.find(:all)
|
91
|
+
# my_task = ProbyTask.find('my_proby_task_api_id')
|
92
|
+
def self.find(param)
|
93
|
+
ensure_api_key_set
|
94
|
+
param == :all ? list : fetch(param)
|
95
|
+
end
|
96
|
+
|
97
|
+
# Create a new Proby task.
|
98
|
+
#
|
99
|
+
# @param [Hash] attributes The attributes for your task.
|
100
|
+
# @option attributes [String] :name A name for your task.
|
101
|
+
# @option attributes [String] :crontab The schedule of the task, specified in cron format.
|
102
|
+
# @option attributes [String] :time_zone <b>(Optional)</b> The time zone of the machine executing the task.
|
103
|
+
# @option attributes [String] :machine <b>(Optional)</b> The name of the machine that is responsible for running this task.
|
104
|
+
# Will default to the default time zone configured in Proby if not specified.
|
105
|
+
# @option attributes [Boolean] :finish_alarms_enabled <b>(Optional)</b> true if you would like to receive finish alarms for
|
106
|
+
# this task, false otherwise (default: true).
|
107
|
+
# @option attributes [Fixnum] :maximum_run_time <b>(Optional)</b> The maximum amount of time the task is allowed to run before
|
108
|
+
# Proby sends a finish alarm. If not specified, Proby will determine when an alarm should be
|
109
|
+
# sent based on past run times.
|
110
|
+
# @option attributes [Fixnum] :start_notification_grace_period <b>(Optional)</b> The number of minutes to wait for a task to
|
111
|
+
# send its start notification after it should have started before sending an alarm.
|
112
|
+
# @option attributes [Fixnum] :consecutive_alarmed_tasks_required_to_trigger_alarm <b>(Optional)</b> The number of consecutive
|
113
|
+
# tasks that must fail before an alarm is sent.
|
114
|
+
#
|
115
|
+
# @return [ProbyTask] The task that was created.
|
116
|
+
#
|
117
|
+
# @example
|
118
|
+
# proby_task = ProbyTask.create(:name => "My new task", :crontab => "* * * * *")
|
119
|
+
def self.create(attributes={})
|
120
|
+
ensure_api_key_set
|
121
|
+
raise InvalidParameterException.new("attributes are required") if attributes.nil? || attributes.empty?
|
122
|
+
raise InvalidParameterException.new("name is required") unless !blank?(attributes[:name]) || !blank?(attributes['name'])
|
123
|
+
raise InvalidParameterException.new("crontab is required") unless !blank?(attributes[:crontab]) || !blank?(attributes['crontab'])
|
124
|
+
|
125
|
+
Proby.logger.info "Creating task with attributes: #{attributes.inspect}"
|
126
|
+
response = post("/api/v1/tasks.json",
|
127
|
+
:format => :json,
|
128
|
+
:body => MultiJson.encode(:task => attributes),
|
129
|
+
:headers => default_headers)
|
130
|
+
|
131
|
+
if response.code == 201
|
132
|
+
new(response.parsed_response['task'])
|
133
|
+
else
|
134
|
+
handle_api_failure(response)
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
# Saves the task in Proby, updating all attributes to the values stored in the object. Only the attributes specified in
|
139
|
+
# the ProbyTask.create documentation can be updated.
|
140
|
+
#
|
141
|
+
# @example
|
142
|
+
# proby_task = ProbyTask.get('my_proby_task_api_id')
|
143
|
+
# proby_task.name = "Some other name"
|
144
|
+
# proby_task.crontab = "1 2 3 4 5"
|
145
|
+
# proby_task.save
|
146
|
+
def save
|
147
|
+
self.class.ensure_api_key_set
|
148
|
+
raise InvalidParameterException.new("name is required") if self.class.blank?(@name)
|
149
|
+
raise InvalidParameterException.new("crontab is required") if self.class.blank?(@crontab)
|
150
|
+
|
151
|
+
attributes = {
|
152
|
+
:name => @name,
|
153
|
+
:crontab => @crontab,
|
154
|
+
:time_zone => @time_zone,
|
155
|
+
:machine => @machine,
|
156
|
+
:finish_alarms_enabled => @finish_alarms_enabled,
|
157
|
+
:maximum_run_time => @maximum_run_time,
|
158
|
+
:start_notification_grace_period => @start_notification_grace_period,
|
159
|
+
:consecutive_alarmed_tasks_required_to_trigger_alarm => @consecutive_alarmed_tasks_required_to_trigger_alarm
|
160
|
+
}
|
161
|
+
|
162
|
+
Proby.logger.info "Updating task #{@api_id} with attributes: #{attributes.inspect}"
|
163
|
+
response = self.class.put("/api/v1/tasks/#{@api_id}.json",
|
164
|
+
:format => :json,
|
165
|
+
:body => MultiJson.encode(:task => attributes),
|
166
|
+
:headers => self.class.default_headers)
|
167
|
+
|
168
|
+
if response.code == 200
|
169
|
+
true
|
170
|
+
else
|
171
|
+
self.class.handle_api_failure(response)
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
# Delete a Proby task. The object will be frozen after the delete.
|
176
|
+
#
|
177
|
+
# @example
|
178
|
+
# proby_task = ProbyTask.get('my_proby_task_api_id')
|
179
|
+
# proby_task.delete
|
180
|
+
def delete
|
181
|
+
self.class.ensure_api_key_set
|
182
|
+
|
183
|
+
Proby.logger.info "Deleting task #{@api_id}"
|
184
|
+
response = self.class.delete("/api/v1/tasks/#{@api_id}.json",
|
185
|
+
:format => :json,
|
186
|
+
:headers => self.class.default_headers)
|
187
|
+
|
188
|
+
if response.code == 200
|
189
|
+
self.freeze
|
190
|
+
true
|
191
|
+
else
|
192
|
+
self.class.handle_api_failure(response)
|
193
|
+
end
|
194
|
+
end
|
195
|
+
|
196
|
+
# Pause a Proby task.
|
197
|
+
#
|
198
|
+
# @example
|
199
|
+
# proby_task = ProbyTask.get('my_proby_task_api_id')
|
200
|
+
# proby_task.pause
|
201
|
+
def pause
|
202
|
+
self.class.ensure_api_key_set
|
203
|
+
|
204
|
+
Proby.logger.info "Pausing task #{@api_id}"
|
205
|
+
response = self.class.post("/api/v1/tasks/#{@api_id}/pause.json",
|
206
|
+
:format => :json,
|
207
|
+
:headers => self.class.default_headers)
|
208
|
+
|
209
|
+
if response.code == 200
|
210
|
+
@paused = true
|
211
|
+
true
|
212
|
+
else
|
213
|
+
self.class.handle_api_failure(response)
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
217
|
+
# Unpause a Proby task.
|
218
|
+
#
|
219
|
+
# @example
|
220
|
+
# proby_task = ProbyTask.get('my_proby_task_api_id')
|
221
|
+
# proby_task.unpause
|
222
|
+
def unpause
|
223
|
+
self.class.ensure_api_key_set
|
224
|
+
|
225
|
+
Proby.logger.info "Unpausing task #{@api_id}"
|
226
|
+
response = self.class.post("/api/v1/tasks/#{@api_id}/unpause.json",
|
227
|
+
:format => :json,
|
228
|
+
:headers => self.class.default_headers)
|
229
|
+
|
230
|
+
if response.code == 200
|
231
|
+
@paused = false
|
232
|
+
true
|
233
|
+
else
|
234
|
+
self.class.handle_api_failure(response)
|
235
|
+
end
|
236
|
+
end
|
237
|
+
|
238
|
+
private
|
239
|
+
|
240
|
+
def self.list
|
241
|
+
Proby.logger.info "Getting the list of tasks"
|
242
|
+
response = get('/api/v1/tasks.json',
|
243
|
+
:format => :json,
|
244
|
+
:headers => default_headers)
|
245
|
+
|
246
|
+
if response.code == 200
|
247
|
+
data = response.parsed_response['tasks']
|
248
|
+
data.map { |task_data| new(task_data) }
|
249
|
+
else
|
250
|
+
handle_api_failure(response)
|
251
|
+
end
|
252
|
+
end
|
253
|
+
|
254
|
+
def self.fetch(api_id)
|
255
|
+
raise InvalidParameterException.new("api_id is required") if api_id.nil? || api_id.strip.empty?
|
256
|
+
|
257
|
+
Proby.logger.info "Fetching task from Proby: #{api_id}"
|
258
|
+
response = get("/api/v1/tasks/#{api_id}.json",
|
259
|
+
:format => :json,
|
260
|
+
:headers => default_headers)
|
261
|
+
|
262
|
+
if response.code == 200
|
263
|
+
new(response.parsed_response['task'])
|
264
|
+
elsif response.code == 404
|
265
|
+
nil
|
266
|
+
else
|
267
|
+
handle_api_failure(response)
|
268
|
+
end
|
269
|
+
end
|
270
|
+
|
271
|
+
def self.ensure_api_key_set
|
272
|
+
if Proby.api_key.nil? || Proby.api_key.strip.empty?
|
273
|
+
raise InvalidApiKeyException.new("Your Proby API key has not been set. Set it using Proby.api_key = 'my_api_key'")
|
274
|
+
end
|
275
|
+
end
|
276
|
+
|
277
|
+
end
|
278
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
module Proby
|
2
|
+
# Automatically notifies Proby when this job starts and finishes.
|
3
|
+
#
|
4
|
+
# class SomeJob
|
5
|
+
# extend Proby::ResquePlugin
|
6
|
+
#
|
7
|
+
# self.perform
|
8
|
+
# do_stuff
|
9
|
+
# end
|
10
|
+
# end
|
11
|
+
#
|
12
|
+
# The Proby Task ID can be set in one of two ways. The most common way is to
|
13
|
+
# put the ID in an ENV variable that is set in your crontab. This ID will be
|
14
|
+
# transparently passed to the Resque job via Redis.
|
15
|
+
#
|
16
|
+
# 0 0 * * * PROBY_TASK_ID=abc123 ./queue_some_job
|
17
|
+
#
|
18
|
+
# Alternatively, if you're not using cron and therefore don't want that
|
19
|
+
# support, you can just set the @proby_id ivar in the class, like so.
|
20
|
+
#
|
21
|
+
# class SomeJob
|
22
|
+
# extend Proby::ResquePlugin
|
23
|
+
# @proby_id = 'abc123'
|
24
|
+
#
|
25
|
+
# self.perform
|
26
|
+
# do_stuff
|
27
|
+
# end
|
28
|
+
# end
|
29
|
+
#
|
30
|
+
# Setting the @proby_id variable will take precendence over the ENV variable.
|
31
|
+
#
|
32
|
+
module ResquePlugin
|
33
|
+
def proby_id_bucket(*args)
|
34
|
+
"proby_id:#{name}-#{args.to_s}"
|
35
|
+
end
|
36
|
+
|
37
|
+
def before_enqueue_proby(*args)
|
38
|
+
return true if @proby_id
|
39
|
+
|
40
|
+
env_proby_id = ENV['PROBY_TASK_ID']
|
41
|
+
Resque.redis.setex(proby_id_bucket(*args), 24.hours, env_proby_id)
|
42
|
+
return true
|
43
|
+
end
|
44
|
+
|
45
|
+
def proby_id(*args)
|
46
|
+
@proby_id || Resque.redis.get(proby_id_bucket(*args))
|
47
|
+
end
|
48
|
+
|
49
|
+
def around_perform_proby(*args)
|
50
|
+
failed = false
|
51
|
+
error_message = nil
|
52
|
+
_proby_id = proby_id(*args)
|
53
|
+
Proby.send_start_notification(_proby_id)
|
54
|
+
yield
|
55
|
+
rescue Exception => e
|
56
|
+
failed = true
|
57
|
+
error_message = "#{e.class.name}: #{e.message}"
|
58
|
+
error_message << "\n#{e.backtrace.join("\n")}" if e.backtrace
|
59
|
+
raise e
|
60
|
+
ensure
|
61
|
+
Proby.send_finish_notification(_proby_id, :failed => failed, :error_message => error_message)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
data/lib/proby.rb
ADDED
@@ -0,0 +1,75 @@
|
|
1
|
+
require 'logger'
|
2
|
+
require 'httparty'
|
3
|
+
require 'chronic'
|
4
|
+
|
5
|
+
require 'proby/exceptions'
|
6
|
+
require 'proby/proby_http_api'
|
7
|
+
require 'proby/proby_task'
|
8
|
+
require 'proby/notifier'
|
9
|
+
require 'proby/resque_plugin'
|
10
|
+
|
11
|
+
module Proby
|
12
|
+
|
13
|
+
# A simple library for working with the Proby task monitoring application.
|
14
|
+
class << self
|
15
|
+
|
16
|
+
# Set your Proby API key.
|
17
|
+
#
|
18
|
+
# @param [String] api_key Your Proby API key
|
19
|
+
#
|
20
|
+
# @example
|
21
|
+
# Proby.api_key = '1234567890abcdefg'
|
22
|
+
def api_key=(api_key)
|
23
|
+
@api_key = api_key
|
24
|
+
end
|
25
|
+
|
26
|
+
# Get the api key.
|
27
|
+
def api_key
|
28
|
+
@api_key
|
29
|
+
end
|
30
|
+
|
31
|
+
# Set the logger to be used by Proby.
|
32
|
+
#
|
33
|
+
# @param [Logger] logger The logger you would like Proby to use
|
34
|
+
#
|
35
|
+
# @example
|
36
|
+
# Proby.logger = Rails.logger
|
37
|
+
# Proby.logger = Logger.new(STDERR)
|
38
|
+
def logger=(logger)
|
39
|
+
@logger = logger
|
40
|
+
end
|
41
|
+
|
42
|
+
# Get the logger used by Proby.
|
43
|
+
def logger
|
44
|
+
@logger ||= Logger.new("/dev/null")
|
45
|
+
end
|
46
|
+
|
47
|
+
# Send a start notification for this task to Proby.
|
48
|
+
#
|
49
|
+
# @param [String] proby_task_id The id of the task to be notified. If nil, the
|
50
|
+
# value of the +PROBY_TASK_ID+ environment variable will be used.
|
51
|
+
#
|
52
|
+
# @return [Fixnum] The HTTP status code that was returned from Proby.
|
53
|
+
def send_start_notification(proby_task_id=nil)
|
54
|
+
Notifier.send_notification('start', proby_task_id)
|
55
|
+
end
|
56
|
+
|
57
|
+
# Send a finish notification for this task to Proby
|
58
|
+
#
|
59
|
+
# @param [String] proby_task_id The id of the task to be notified. If nil, the
|
60
|
+
# value of the +PROBY_TASK_ID+ environment variable will be used.
|
61
|
+
# @param [Hash] options The options for the finish notification
|
62
|
+
# @option options [Boolean] :failed true if this task run resulted in some sort of failure. Setting
|
63
|
+
# this parameter to true will trigger a notification to be sent to
|
64
|
+
# the alarms configured for the given task. Defaults to false.
|
65
|
+
# @option options [String] :error_message A string message describing the failure that occurred.
|
66
|
+
# 1,000 character limit.
|
67
|
+
#
|
68
|
+
# @return [Fixnum] The HTTP status code that was returned from Proby.
|
69
|
+
def send_finish_notification(proby_task_id=nil, options={})
|
70
|
+
Notifier.send_notification('finish', proby_task_id, options)
|
71
|
+
end
|
72
|
+
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|