delayed_job_monitor 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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
+ })