delayed_job_monitor 0.0.1

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.
Files changed (37) hide show
  1. data/.DS_Store +0 -0
  2. data/.gitignore +18 -0
  3. data/Gemfile +23 -0
  4. data/LICENSE.txt +22 -0
  5. data/README.md +57 -0
  6. data/Rakefile +1 -0
  7. data/config.yml +29 -0
  8. data/delayed_job_monitor.gemspec +18 -0
  9. data/delayed_job_monitor_app.rb +7 -0
  10. data/lib/.DS_Store +0 -0
  11. data/lib/delayed_job_monitor/.DS_Store +0 -0
  12. data/lib/delayed_job_monitor/application/.DS_Store +0 -0
  13. data/lib/delayed_job_monitor/application/app.rb +187 -0
  14. data/lib/delayed_job_monitor/application/public/images/poll.png +0 -0
  15. data/lib/delayed_job_monitor/application/public/javascripts/application.js +64 -0
  16. data/lib/delayed_job_monitor/application/public/javascripts/jquery-1.7.1.min.js +4 -0
  17. data/lib/delayed_job_monitor/application/public/javascripts/jquery.relatize_date.js +117 -0
  18. data/lib/delayed_job_monitor/application/public/stylesheets/reset.css +44 -0
  19. data/lib/delayed_job_monitor/application/public/stylesheets/style.css +150 -0
  20. data/lib/delayed_job_monitor/application/views/enqueued.haml +10 -0
  21. data/lib/delayed_job_monitor/application/views/error.haml +2 -0
  22. data/lib/delayed_job_monitor/application/views/failed.haml +14 -0
  23. data/lib/delayed_job_monitor/application/views/job.haml +43 -0
  24. data/lib/delayed_job_monitor/application/views/layout.haml +23 -0
  25. data/lib/delayed_job_monitor/application/views/next_more.haml +6 -0
  26. data/lib/delayed_job_monitor/application/views/overview.haml +110 -0
  27. data/lib/delayed_job_monitor/application/views/pending.haml +9 -0
  28. data/lib/delayed_job_monitor/application/views/queue.haml +13 -0
  29. data/lib/delayed_job_monitor/application/views/servers.haml +16 -0
  30. data/lib/delayed_job_monitor/application/views/workers.haml +15 -0
  31. data/lib/delayed_job_monitor/application/views/working.haml +9 -0
  32. data/lib/delayed_job_monitor/job.rb +31 -0
  33. data/lib/delayed_job_monitor/server.rb +77 -0
  34. data/lib/delayed_job_monitor/version.rb +3 -0
  35. data/lib/delayed_job_monitor/worker.rb +10 -0
  36. data/lib/delayed_job_monitor.rb +85 -0
  37. metadata +81 -0
data/.DS_Store ADDED
Binary file
data/.gitignore ADDED
@@ -0,0 +1,18 @@
1
+ doc/
2
+ *.gem
3
+ *.rbc
4
+ .bundle
5
+ .config
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ coverage
9
+ lib/bundler/man
10
+ pkg
11
+ rdoc
12
+ spec/reports
13
+ test/tmp
14
+ test/version_tmp
15
+ tmp
16
+ .yardoc
17
+ _yardoc
18
+ configbak.yml
data/Gemfile ADDED
@@ -0,0 +1,23 @@
1
+ source "http://rubygems.org"
2
+ #CORE GEMS
3
+ gem "sinatra"
4
+ gem "haml"
5
+ gem 'activerecord'
6
+ gem 'delayed_job'
7
+ gem 'rdoc'
8
+ gem 'hashie'
9
+ #gem 'request-log-analyzer'
10
+ #DATABASE ADAPTERS
11
+ gem 'pg'
12
+ gem 'sqlite3'
13
+ #gem 'mongoid' #not yet supported
14
+
15
+ group :development do
16
+ gem "pry"
17
+ gem 'rspec'
18
+ gem "shoulda"
19
+ gem "bundler"
20
+ gem "jeweler"
21
+ gem "simplecov"
22
+ gem "rack-test"
23
+ end
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 Dan Barrett
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,57 @@
1
+ ========
2
+ DelayedJobMonitor
3
+ ========
4
+
5
+ **WARNING!!!!**
6
+ * This gem is extremely rough. There are no tests of any kind. Use at your own risk!
7
+
8
+ Delayed Jobs Administration for the Masses!
9
+
10
+ Built for/by [Contently](http://www.contently.com) by [thoughpunch](http://www.about.me/thoughtpunch)
11
+
12
+ ## Description
13
+
14
+ DelayedJobMonitor is a Sinatra-based web front-end for managing [Delayed Jobs](https://github.com/collectiveidea/delayed_job) heavily inspired by the excellent [delayed_job_web](https://github.com/ejschmitt/delayed_job_web) gem.
15
+
16
+ What makes DelayedJobMonitor unique is that it can be run in one of two modes. 'Gem' mode runs the mounted Sinatra app inside your Rails app via a route. 'Stand Alone' mode can be run from the same server or even a different location, provided that you configure the database settings to connect to the instance that is storing the delayed jobs. It's a Sinatra app, it's a Gem, it's a Sinatra app inside a gem!
17
+
18
+ ## Installation
19
+
20
+ gem install 'delayed_job_monitor'
21
+
22
+ ## Usage
23
+
24
+ **Gem Mode**
25
+ * Mount the Sinatra app in your route.rb file like so:
26
+ * ```match "/jobs" => DelayedJobMonitor::App, :anchor => false```
27
+
28
+ **Stand Alone Mode**
29
+ * Cloning the entire repo into a directory and run the following command will start the stand-alone Sinatra app.
30
+ * ``ruby delayed_job_monitor_app.rb```
31
+
32
+ ## RoadMap
33
+
34
+ The overall goal is to allow full management of Delayed Jobs workers and tasks from both inside and outside a Rails App.
35
+ To do so *might* require stubbing enough Rails functionality that Delayed::Worker and Delayed::Command can run without error.
36
+
37
+ 1. Make fully functional as stand alone app
38
+ 1. Ensure can use any ActiveRecord-supported database
39
+ 2. Add PID/Daemon monitoring for Delayed Job Deamons
40
+ 1. Ability to restart jobs if not running from outside Rails app
41
+ 3. Delayed Job log parsing/tailing
42
+ 1. Tail all delayed_job logs in the view via JS [like so](http://dojo4.com/blog/easy-cheasy-realtime-log-tailing-in-a-rails-admin-view)
43
+ 4. Ability to edit jobs on the fly, individually or in bulk
44
+ 1. Change PID, queue, run_at
45
+ 5. Delayed Jobs statistics and processing numbers
46
+ 1. Store jobs total and jobs per second while app is running, maybe store in YAML?
47
+ 2. Use request-log-analyzer for past dj stats (Note: Current version can not parse logs from Delayed Job v3.0 and later)
48
+ 3. Realtime Graphs and charts
49
+ 6. Write Tests
50
+ 1. Integration tests for Sinatra App, unit tests for all modules and classes.
51
+ 2. Version lock all gems after testing
52
+
53
+ ## Contributing
54
+
55
+ 1. Fork it
56
+ 2. Writer tests or extend functionality from the roadmap
57
+ 3. Submit a pull request
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
data/config.yml ADDED
@@ -0,0 +1,29 @@
1
+ # DELAYED JOB ADMIN CONFIGURATION
2
+
3
+ #DELAYED JOB WORKER MACHINES
4
+ # - Configure SSH connection info for the seperates hosts that are working delayed jobs
5
+ # - To add additional host connections, simply add another nested entry below
6
+ # - The DelayedJobMonitor::Worker module will establish SSH connections to each
7
+ # one to monitor, start, and stop delayed_job workers.
8
+ #DELAYED JOB SERVER
9
+ # - Configure an ActiveRecord connection to the database that is hosting the delay_jobs database
10
+ # - Only 1 DB connection is supported at this time.
11
+
12
+ application:
13
+ name: #used to parse Rails path, log paths, etc
14
+ email: #email upon failures,changes,etc
15
+ hosts:
16
+ -
17
+ name: # 'nickname' of host
18
+ rails_path: # path of the Rails app on host machine, assumes '/var/www/RAILS_APP' by default
19
+ host: # domain name of host for SSH connection
20
+ username: # SSH Username
21
+ password: # SSH Password
22
+ database:
23
+ host:
24
+ port:
25
+ adapter:
26
+ database:
27
+ username:
28
+ password:
29
+ template:
@@ -0,0 +1,18 @@
1
+ # -*- encoding: utf-8 -*-
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'delayed_job_monitor/version'
5
+
6
+ Gem::Specification.new do |gem|
7
+ gem.name = "delayed_job_monitor"
8
+ gem.version = DelayedJobMonitor::VERSION
9
+ gem.authors = ["Dan Barrett"]
10
+ gem.email = ["dbarrett83@gmail.com"]
11
+ gem.description = "An Adminstrative tool for Delayed Jobs"
12
+ gem.summary = "Manage all your delayed jobs"
13
+ gem.homepage = "http://www.contently.com"
14
+ gem.files = `git ls-files`.split($/)
15
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
16
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
17
+ gem.require_paths = ["lib"]
18
+ end
@@ -0,0 +1,7 @@
1
+ require './lib/delayed_job_monitor.rb'
2
+
3
+ if YAML.load_file("config.yml")["database"]["database"]
4
+ DelayedJobMonitor.start!
5
+ else
6
+ p "Please configure your database connection inside the config.yml file"
7
+ end
data/lib/.DS_Store ADDED
Binary file
Binary file
@@ -0,0 +1,187 @@
1
+ require 'sinatra'
2
+ require 'sinatra/base'
3
+ require 'haml'
4
+
5
+ module DelayedJobMonitor
6
+ class App < Sinatra::Base
7
+ set :root, File.dirname(__FILE__)
8
+ set :static, true
9
+ set :public_folder, File.expand_path('../public', __FILE__)
10
+ set :views, File.expand_path('../views', __FILE__)
11
+ set :haml, { :format => :html5 }
12
+ set :port, 4567
13
+
14
+ def delayed_job
15
+ begin
16
+ DelayedJobMonitor::Job
17
+ rescue
18
+ false
19
+ end
20
+ end
21
+
22
+ ############################## SINTRA APP ################################
23
+ def current_page
24
+ url_path request.path_info.sub('/','')
25
+ end
26
+
27
+ def start
28
+ params[:start].to_i
29
+ end
30
+
31
+ def per_page
32
+ 25
33
+ end
34
+
35
+ def url_path(*path_parts)
36
+ [ path_prefix, path_parts ].join("/").squeeze('/')
37
+ end
38
+ alias_method :u, :url_path
39
+
40
+ def path_prefix
41
+ request.env['SCRIPT_NAME']
42
+ end
43
+
44
+ def delayed_jobs(type)
45
+ delayed_job.where(delayed_job_sql(type))
46
+ end
47
+
48
+ def delayed_job_sql(type)
49
+ case type
50
+ when :enqueued
51
+ ''
52
+ when :working
53
+ 'locked_at is not null'
54
+ when :failed
55
+ 'last_error is not null'
56
+ when :pending
57
+ 'attempts = 0'
58
+ end
59
+ end
60
+
61
+ def partial(template, local_vars = {})
62
+ @partial = true
63
+ haml(template.to_sym, {:layout => false}, local_vars)
64
+ ensure
65
+ @partial = false
66
+ end
67
+
68
+ def poll
69
+ if @polling
70
+ text = "Last Updated: #{Time.now.strftime("%H:%M:%S")}"
71
+ else
72
+ text = "<a href='#{u(request.path_info)}.poll' rel='poll'>Live Poll</a>"
73
+ end
74
+ "<p class='poll'>#{text}</p>"
75
+ end
76
+
77
+ def show_for_polling(page)
78
+ content_type "text/html"
79
+ @polling = true
80
+ # show(page.to_sym, false).gsub(/\s{1,}/, ' ')
81
+ @jobs = delayed_jobs(page.to_sym)
82
+ haml(page.to_sym, {:layout => false})
83
+ end
84
+
85
+ ####################### SINATRA ROUTES/ACTIONS ##########################
86
+ def tabs
87
+ [
88
+ {:name => 'Overview', :path => '/overview'},
89
+ {:name => 'Enqueued', :path => '/enqueued'},
90
+ {:name => 'Working', :path => '/working'},
91
+ {:name => 'Pending', :path => '/pending'},
92
+ {:name => 'Failed', :path => '/failed'}
93
+ # {:name => 'Servers', :path => '/servers'},
94
+ # {:name => 'Workers', :path => '/workers'}
95
+ ]
96
+ end
97
+
98
+ get "/?" do
99
+ redirect u(:overview)
100
+ end
101
+
102
+ #Static Page Rendering
103
+ %w(enqueued working pending failed).each do |page|
104
+ get "/#{page}" do
105
+ @jobs = delayed_jobs(page.to_sym).order('created_at desc, id desc').offset(start).limit(per_page)
106
+ @all_jobs = delayed_jobs(page.to_sym)
107
+ haml page.to_sym
108
+ end
109
+ end
110
+
111
+ #Polling Page Rendering
112
+ %w(overview enqueued working pending failed stats) .each do |page|
113
+ get "/#{page}.poll" do
114
+ show_for_polling(page)
115
+ end
116
+
117
+ get "/#{page}/:id.poll" do
118
+ show_for_polling(page)
119
+ end
120
+ end
121
+
122
+ get '/overview' do
123
+ if delayed_job
124
+ haml :overview
125
+ else
126
+ @message = "Unable to connected to Delayed::Job database"
127
+ haml :error
128
+ end
129
+ end
130
+
131
+ # get "/servers" do
132
+ # @servers = delayed_job_monitor::Server.servers
133
+ # p @servers.class
134
+ # p @servers.count
135
+ # p @servers.first
136
+ # end
137
+
138
+ # get "/workers" do
139
+ # @workers = delayed_job_monitor::Server.servers.map{|x| x.delayed_job_workers}
140
+ # end
141
+
142
+ get "/queue/:queue" do
143
+ @jobs = delayed_job.where(:queue=>params[:queue]).order('created_at desc, id desc').offset(start).limit(per_page)
144
+ @all_jobs = delayed_job.where(:queue=>params[:queue]).count
145
+ haml :queue
146
+ end
147
+
148
+ get "/remove/:id" do
149
+ delayed_job.find(params[:id]).delete
150
+ redirect back
151
+ end
152
+
153
+ get "/requeue/:id" do
154
+ job = delayed_job.find(params[:id])
155
+ job.update_attributes(:run_at =>Time.now,:failed_at=>nil,:locked_at=>nil,:attempts=>0)
156
+ redirect back
157
+ end
158
+
159
+ post "/failed/clear" do
160
+ delayed_job.where("last_error IS NOT NULL").destroy_all
161
+ redirect back
162
+ end
163
+
164
+ post "/requeue/all" do
165
+ delayed_job.where("last_error IS NOT NULL").update_all(
166
+ :run_at=>Time.now,
167
+ :failed_at => nil,
168
+ :attempts=>0,
169
+ :last_error=>nil,
170
+ :locked_at=>nil)
171
+ redirect back
172
+ end
173
+
174
+ get '/update/:id' do
175
+ "#{params[:id]}"
176
+ end
177
+
178
+ post '/update/all' do
179
+ "#{params}"
180
+ end
181
+
182
+ post '/kill/:pid' do
183
+ delayed_job_monitor::Worker.kill_worker(params[:pid])
184
+ redirect back
185
+ end
186
+ end
187
+ end
@@ -0,0 +1,64 @@
1
+ $(function() {
2
+ var poll_interval = 3;
3
+
4
+ var relatizer = function(){
5
+ var dt = $(this).text(), relatized = $.relatizeDate(this)
6
+ if ($(this).parents("a").length > 0 || $(this).is("a")) {
7
+ $(this).relatizeDate()
8
+ if (!$(this).attr('title')) {
9
+ $(this).attr('title', dt)
10
+ }
11
+ } else {
12
+ $(this)
13
+ .text('')
14
+ .append( $('<a href="#" class="toggle_format" title="' + dt + '" />')
15
+ .append('<span class="date_time">' + dt +
16
+ '</span><span class="relatized_time">' +
17
+ relatized + '</span>') )
18
+ }
19
+ };
20
+
21
+ $('.time').each(relatizer);
22
+
23
+ $('.time a.toggle_format .date_time').hide();
24
+
25
+ var format_toggler = function(){
26
+ $('.time a.toggle_format span').toggle();
27
+ $(this).attr('title', $('span:hidden',this).text());
28
+ return false;
29
+ };
30
+
31
+ $('.time a.toggle_format').click(format_toggler);
32
+
33
+ $('ul li.job').hover(function() {
34
+ $(this).addClass('hover');
35
+ }, function() {
36
+ $(this).removeClass('hover');
37
+ })
38
+
39
+ $('a.backtrace').click(function (e) {
40
+ e.preventDefault();
41
+ if($(this).prev('div.backtrace:visible').length > 0) {
42
+ $(this).next('div.backtrace').show();
43
+ $(this).prev('div.backtrace').hide();
44
+ } else {
45
+ $(this).next('div.backtrace').hide();
46
+ $(this).prev('div.backtrace').show();
47
+ }
48
+ });
49
+
50
+ $('a[rel=poll]').click(function(e) {
51
+ e.preventDefault();
52
+ var href = $(this).attr('href')
53
+ $(this).parent().text('Starting...')
54
+ $("#main").addClass('polling')
55
+
56
+ setInterval(function() {
57
+ $.ajax({dataType: 'text', type: 'get', url: href, success: function(data) {
58
+ $('#main').html(data);
59
+ }})
60
+ }, poll_interval * 1000)
61
+
62
+ return false
63
+ })
64
+ })