proby 2.0.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.
- 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
|
+
|