magent 0.5.4 → 0.6.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +3 -0
- data/Gemfile.lock +15 -2
- data/VERSION +1 -1
- data/bin/magent-web +18 -0
- data/config.ru +5 -0
- data/lib/magent.rb +2 -2
- data/lib/magent/failure.rb +5 -0
- data/lib/magent_web.rb +42 -0
- data/lib/magent_web/app.rb +71 -0
- data/lib/magent_web/mongo_helper.rb +84 -0
- data/lib/magent_web/views/index.haml +14 -0
- data/lib/magent_web/views/layout.haml +15 -0
- data/lib/magent_web/views/queues/failed.haml +36 -0
- data/lib/magent_web/views/queues/show.haml +44 -0
- data/lib/magent_web/views/queues/stats.haml +6 -0
- data/lib/magent_web/views/shared/values.haml +74 -0
- data/lib/magent_web/views/status.haml +72 -0
- data/magent.gemspec +34 -14
- data/public/javascripts/application.js +4 -0
- data/public/javascripts/jquery.mobile-1.0b1pre.min.js +140 -0
- data/public/stylesheets/highlight.css +56 -0
- data/public/stylesheets/images/ajax-loader.png +0 -0
- data/public/stylesheets/images/icon-search-black.png +0 -0
- data/public/stylesheets/images/icons-18-black.png +0 -0
- data/public/stylesheets/images/icons-18-white.png +0 -0
- data/public/stylesheets/images/icons-36-black.png +0 -0
- data/public/stylesheets/images/icons-36-white.png +0 -0
- data/public/stylesheets/jquery.mobile-1.0b1pre.min.css +9 -0
- metadata +83 -22
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
@@ -3,19 +3,32 @@ GEM
|
|
3
3
|
specs:
|
4
4
|
addressable (2.2.1)
|
5
5
|
bson (1.1)
|
6
|
+
configuration (1.2.0)
|
6
7
|
em-websocket (0.1.4)
|
7
8
|
addressable (>= 2.1.1)
|
8
9
|
eventmachine (>= 0.12.9)
|
9
10
|
eventmachine (0.12.10)
|
11
|
+
haml (3.1.1)
|
12
|
+
launchy (0.4.0)
|
13
|
+
configuration (>= 0.0.5)
|
14
|
+
rake (>= 0.8.1)
|
10
15
|
mongo (1.1)
|
11
16
|
bson (>= 1.0.5)
|
17
|
+
rack (1.3.0)
|
18
|
+
rake (0.9.0)
|
19
|
+
sinatra (1.2.6)
|
20
|
+
rack (~> 1.1)
|
21
|
+
tilt (>= 1.2.2, < 2.0)
|
22
|
+
tilt (1.3.2)
|
12
23
|
uuidtools (2.1.1)
|
13
24
|
|
14
25
|
PLATFORMS
|
15
26
|
ruby
|
16
27
|
|
17
28
|
DEPENDENCIES
|
18
|
-
bson
|
19
29
|
em-websocket
|
20
|
-
|
30
|
+
haml (~> 3.1.1)
|
31
|
+
launchy (~> 0.4.0)
|
32
|
+
mongo (~> 1.0)
|
33
|
+
sinatra (~> 1.2.6)
|
21
34
|
uuidtools
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.6.0
|
data/bin/magent-web
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
$:.unshift << File.expand_path("../../lib", __FILE__)
|
4
|
+
require 'magent'
|
5
|
+
require 'magent_web'
|
6
|
+
require 'launchy'
|
7
|
+
|
8
|
+
Thread.start do
|
9
|
+
sleep 2.5
|
10
|
+
Launchy.open("http://localhost:9876")
|
11
|
+
end
|
12
|
+
|
13
|
+
Rack::Server.new(:Port => "9876",
|
14
|
+
:config => File.dirname(__FILE__)+"/../config.ru",
|
15
|
+
:AccessLog => [],
|
16
|
+
:pid => nil,
|
17
|
+
:Host => "0.0.0.0").start
|
18
|
+
|
data/config.ru
ADDED
data/lib/magent.rb
CHANGED
@@ -62,7 +62,7 @@ module Magent
|
|
62
62
|
end
|
63
63
|
|
64
64
|
def self.connect(environment, options={})
|
65
|
-
raise 'Set config before connecting. Magent.config = {...}' if config.
|
65
|
+
raise 'Set config before connecting. Magent.config = {...}' if config.nil? || config.empty?
|
66
66
|
|
67
67
|
env = config_for_environment(environment)
|
68
68
|
Magent.connection = Mongo::Connection.new(env['host'], env['port'], options)
|
@@ -97,7 +97,7 @@ module Magent
|
|
97
97
|
def self.config_for_environment(environment)
|
98
98
|
env = environment ? config[environment] : config
|
99
99
|
|
100
|
-
return env if env['uri'].blank?
|
100
|
+
return env if env['uri'].nil? || env['uri'].blank?
|
101
101
|
|
102
102
|
uri = URI.parse(env['uri'])
|
103
103
|
raise InvalidScheme.new('must be mongodb') unless uri.scheme == 'mongodb'
|
data/lib/magent/failure.rb
CHANGED
@@ -26,6 +26,11 @@ module Magent
|
|
26
26
|
remove_error(error["_id"])
|
27
27
|
end
|
28
28
|
|
29
|
+
def enqueue_error(error)
|
30
|
+
enqueue(error["message"], 1)
|
31
|
+
remove_error(error["_id"])
|
32
|
+
end
|
33
|
+
|
29
34
|
def error_collection
|
30
35
|
@error_collection ||= Magent.database.collection("#{@name}.errors")
|
31
36
|
end
|
data/lib/magent_web.rb
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
|
3
|
+
require 'json'
|
4
|
+
require 'magent'
|
5
|
+
|
6
|
+
require 'benchmark'
|
7
|
+
require 'sinatra'
|
8
|
+
require 'haml'
|
9
|
+
require 'yaml'
|
10
|
+
|
11
|
+
require 'net/http'
|
12
|
+
require 'uri'
|
13
|
+
require 'cgi'
|
14
|
+
|
15
|
+
require 'magent_web/mongo_helper'
|
16
|
+
require 'magent_web/app'
|
17
|
+
|
18
|
+
module MagentWeb
|
19
|
+
def self.app
|
20
|
+
MagentWeb.connect
|
21
|
+
|
22
|
+
MagentWeb::App
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.connect
|
26
|
+
ENV["MAGENT_ENV"] ||= ENV["RACK_ENV"] || ENV["RAILS_ENV"]
|
27
|
+
if !ENV["MAGENT_ENV"]
|
28
|
+
raise ArgumentError, "please define the env var MAGENT_ENV"
|
29
|
+
end
|
30
|
+
|
31
|
+
if File.exist?("/etc/magent.yml")
|
32
|
+
Magent.setup(YAML.load_file("/etc/magent.yml"), ENV["MAGENT_ENV"], {})
|
33
|
+
elsif File.exist?("config/magent.yml")
|
34
|
+
Magent.setup(YAML.load_file("config/magent.yml"), ENV["MAGENT_ENV"], {})
|
35
|
+
elsif File.exist?("magent.yml")
|
36
|
+
Magent.setup(YAML.load_file("magent.yml"), ENV["MAGENT_ENV"], {})
|
37
|
+
else
|
38
|
+
raise ArgumentError, "/etc/magent.yml, ./config/magent.yml or ./magent.yml were not found"
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
@@ -0,0 +1,71 @@
|
|
1
|
+
module MagentWeb
|
2
|
+
class App < Sinatra::Base
|
3
|
+
include MagentWeb::MongoHelper
|
4
|
+
|
5
|
+
helpers do
|
6
|
+
include Rack::Utils
|
7
|
+
alias_method :h, :escape_html
|
8
|
+
end
|
9
|
+
|
10
|
+
set :public, File.expand_path("../../../public", __FILE__)
|
11
|
+
set :views, File.expand_path("../../../lib/magent_web/views", __FILE__)
|
12
|
+
|
13
|
+
before do
|
14
|
+
@database = Magent.database
|
15
|
+
end
|
16
|
+
|
17
|
+
get "/" do
|
18
|
+
@queues = self.queues
|
19
|
+
|
20
|
+
haml :index
|
21
|
+
end
|
22
|
+
|
23
|
+
get "/status" do
|
24
|
+
haml :status
|
25
|
+
end
|
26
|
+
|
27
|
+
get "/queues/:id" do
|
28
|
+
@queue = @database.collection(params[:id])
|
29
|
+
@messages = document_list(@queue)
|
30
|
+
|
31
|
+
haml :"queues/show"
|
32
|
+
end
|
33
|
+
|
34
|
+
get "/queues/:id/failed" do
|
35
|
+
@queue = @database.collection(params[:id])
|
36
|
+
@errors_queue = @database.collection(params[:id]+".errors")
|
37
|
+
@errors = document_list(@errors_queue)
|
38
|
+
|
39
|
+
haml :"queues/failed"
|
40
|
+
end
|
41
|
+
|
42
|
+
get "/queues/:id/stats" do
|
43
|
+
@queue = @database.collection(params[:id])
|
44
|
+
|
45
|
+
haml :"queues/stats"
|
46
|
+
end
|
47
|
+
|
48
|
+
get "/queues/:queue_id/retry/:id" do
|
49
|
+
@errors_queue = @database.collection(params[:queue_id]+".errors")
|
50
|
+
@channel_name = channel_name_for(params[:queue_id])
|
51
|
+
|
52
|
+
channel = Magent::AsyncChannel.new(@channel_name)
|
53
|
+
|
54
|
+
doc = @errors_queue.find({:_id => params[:id]}).next_document
|
55
|
+
channel.enqueue_error(doc)
|
56
|
+
|
57
|
+
redirect "/queues/#{params[:queue_id]}/failed"
|
58
|
+
end
|
59
|
+
|
60
|
+
get "/queues/:queue_id/delete/:id" do
|
61
|
+
@errors_queue = @database.collection(params[:queue_id]+".errors")
|
62
|
+
@errors_queue.remove(:_id => params[:id])
|
63
|
+
redirect "/queues/#{params[:queue_id]}/failed"
|
64
|
+
end
|
65
|
+
|
66
|
+
private
|
67
|
+
def error_not_found
|
68
|
+
status 404
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
module MagentWeb
|
2
|
+
module MongoHelper
|
3
|
+
def queues
|
4
|
+
q = []
|
5
|
+
Magent.database.collections.each do |collection|
|
6
|
+
if collection.name =~ /^magent\./ && collection.name !~ /errors$/
|
7
|
+
q << collection
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
q
|
12
|
+
end
|
13
|
+
|
14
|
+
def query_options
|
15
|
+
options = {}
|
16
|
+
options[:limit] = params[:limit].nil? ? 25 : params[:limit].to_i
|
17
|
+
options[:sort] = [["_id", (params[:descending] == "true" ? 'descending' : 'ascending')]]
|
18
|
+
options[:skip] = params[:skip].nil? ? 0 : params[:skip].to_i
|
19
|
+
|
20
|
+
options
|
21
|
+
end
|
22
|
+
|
23
|
+
def document_list(collection, query = {})
|
24
|
+
collection.find(query, query_options)
|
25
|
+
end
|
26
|
+
|
27
|
+
def file_size(database)
|
28
|
+
"%0.2f MB" % (database.stats["fileSize"] / 1024**2)
|
29
|
+
end
|
30
|
+
|
31
|
+
def normalize_stats(stats)
|
32
|
+
r={}
|
33
|
+
stats.each do |k,v|
|
34
|
+
if k =~ /size/i
|
35
|
+
if v.kind_of?(Hash)
|
36
|
+
v.each do |ki,vi|
|
37
|
+
v[ki]="%0.2f MB" % (vi.to_f/1024**2)
|
38
|
+
end
|
39
|
+
r[k] = v
|
40
|
+
else
|
41
|
+
r[k]="%0.2f MB" % (v.to_f/1024**2)
|
42
|
+
end
|
43
|
+
else
|
44
|
+
r[k]=v
|
45
|
+
end
|
46
|
+
end
|
47
|
+
r
|
48
|
+
end
|
49
|
+
|
50
|
+
def server_status
|
51
|
+
Magent.database.command(:serverStatus => 1)
|
52
|
+
end
|
53
|
+
|
54
|
+
def humanize(v, quote = true)
|
55
|
+
if v.nil? && quote
|
56
|
+
"null"
|
57
|
+
elsif v.kind_of?(Hash)
|
58
|
+
JSON.pretty_generate(v)
|
59
|
+
elsif v.kind_of?(Array)
|
60
|
+
JSON.pretty_generate(v)
|
61
|
+
elsif v.kind_of?(Time)
|
62
|
+
v.strftime("%d %B %Y %H:%M:%S").inspect
|
63
|
+
elsif quote
|
64
|
+
v.inspect
|
65
|
+
else
|
66
|
+
v
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def humanize_messages(messages)
|
71
|
+
return "" if !messages.kind_of?(Array)
|
72
|
+
|
73
|
+
messages.map do |e|
|
74
|
+
name = e.first
|
75
|
+
args = e.last.join(", ")
|
76
|
+
"#{name}(#{args})"
|
77
|
+
end.join(" -> ")
|
78
|
+
end
|
79
|
+
|
80
|
+
def channel_name_for(queue_id)
|
81
|
+
queue_id.to_s.match("magent\.([^\.]+)")[1]
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
%div(data-role="page" data-add-back-btn="true")
|
2
|
+
%div(data-role="header")
|
3
|
+
%h1
|
4
|
+
= @title = "All Magent Queues"
|
5
|
+
%div(data-role="content")
|
6
|
+
%ul(data-role="listview" data-filter="true")
|
7
|
+
-@queues.each do |queue|
|
8
|
+
%li
|
9
|
+
%a(href="/queues/#{queue.name}")
|
10
|
+
&=queue.name
|
11
|
+
%span{:class=>"ui-li-count"}
|
12
|
+
=0
|
13
|
+
|
14
|
+
%div(data-role="footer")
|
@@ -0,0 +1,15 @@
|
|
1
|
+
!!! 5
|
2
|
+
%html
|
3
|
+
%head
|
4
|
+
%title
|
5
|
+
=@title||"Magent Web"
|
6
|
+
%link(rel="stylesheet" href="/stylesheets/jquery.mobile-1.0b1pre.min.css")
|
7
|
+
%link(rel="stylesheet" href="/stylesheets/highlight.css")
|
8
|
+
|
9
|
+
%script(src="http://code.jquery.com/jquery-1.5.2.min.js")
|
10
|
+
%script(src="/javascripts/jquery.mobile-1.0b1pre.min.js")
|
11
|
+
|
12
|
+
%script(src="/javascripts/application.js")
|
13
|
+
%body
|
14
|
+
= yield
|
15
|
+
|
@@ -0,0 +1,36 @@
|
|
1
|
+
-count=0
|
2
|
+
-skip = params[:skip].to_i
|
3
|
+
%div(data-role="page" data-add-back-btn="true")
|
4
|
+
%div(data-role="header")
|
5
|
+
%h1
|
6
|
+
Failed Jobs for
|
7
|
+
&= channel_name_for(@queue.name)
|
8
|
+
%div(data-role="content")
|
9
|
+
%h1
|
10
|
+
Failed Jobs
|
11
|
+
==(#{@errors.count})
|
12
|
+
-@errors.each do |document|
|
13
|
+
-message = document["message"]
|
14
|
+
-count+=1
|
15
|
+
%div(data-role="collapsible" data-collapsed=true)
|
16
|
+
%h1
|
17
|
+
&= "#{humanize_messages(document["message"].last)}: #{document["error"]}"
|
18
|
+
=haml :"shared/values", :layout => false, :locals => {:hash => document}
|
19
|
+
%div(data-role="navbar" data-iconpos="bottom")
|
20
|
+
%ul
|
21
|
+
%li
|
22
|
+
%a(href="/queues/#{@queue.name}/retry/#{document["_id"]}" data-icon="refresh")
|
23
|
+
Retry
|
24
|
+
%li
|
25
|
+
%a(href="/queues/#{@queue.name}/delete/#{document["_id"]}" data-icon="delete" data-theme="e")
|
26
|
+
Delete
|
27
|
+
|
28
|
+
|
29
|
+
%div(data-role="footer")
|
30
|
+
-if skip > 0
|
31
|
+
%a(href="/queues/#{@queue.name}/failed?skip=#{params[:skip].to_i-25}")
|
32
|
+
Previous
|
33
|
+
-if count >= 25
|
34
|
+
%a(href="/queues/#{@queue.name}/failed?skip=#{params[:skip].to_i+25}")
|
35
|
+
Next
|
36
|
+
|
@@ -0,0 +1,44 @@
|
|
1
|
+
-count=0
|
2
|
+
-skip = params[:skip].to_i
|
3
|
+
%div(data-role="page" data-add-back-btn="true")
|
4
|
+
%div(data-role="header")
|
5
|
+
%h1
|
6
|
+
Queue:
|
7
|
+
&=@title=@database.name+" / #{channel_name_for(@queue.name)}"
|
8
|
+
%div{:"data-role" => "navbar"}
|
9
|
+
%ul
|
10
|
+
%li
|
11
|
+
%a(href="/queues/#{@queue.name}/failed" data-icon="alert")
|
12
|
+
Failed
|
13
|
+
%li
|
14
|
+
%a(href="/queues/#{@queue.name}/stats" data-icon="grid")
|
15
|
+
Stats
|
16
|
+
|
17
|
+
|
18
|
+
%div(data-role="content")
|
19
|
+
-if skip == 0
|
20
|
+
%h1
|
21
|
+
Information
|
22
|
+
-normalize_stats(@queue.stats).each do |k,v|
|
23
|
+
%b
|
24
|
+
&="#{k}="
|
25
|
+
&=humanize v
|
26
|
+
%br
|
27
|
+
%h1
|
28
|
+
Pending Jobs
|
29
|
+
==(#{@messages.count})
|
30
|
+
-@messages.each do |document|
|
31
|
+
-message = document["message"]
|
32
|
+
-count+=1
|
33
|
+
%div(data-role="collapsible" data-collapsed=true)
|
34
|
+
%h1
|
35
|
+
&= "[#{document["priority"]}] #{message.first}: #{humanize_messages(message.last)}"
|
36
|
+
=haml :"shared/values", :layout => false, :locals => {:hash => document}
|
37
|
+
|
38
|
+
%div(data-role="footer")
|
39
|
+
-if skip > 0
|
40
|
+
%a(href="/queues/#{@queue.name}?skip=#{params[:skip].to_i-25}")
|
41
|
+
Previous
|
42
|
+
-if count >= 25
|
43
|
+
%a(href="/queues/#{@queue.name}?skip=#{params[:skip].to_i+25}")
|
44
|
+
Next
|
@@ -0,0 +1,74 @@
|
|
1
|
+
-hash.each do |key,value|
|
2
|
+
-k = humanize(key)
|
3
|
+
-v = humanize(value)
|
4
|
+
|
5
|
+
-if value.kind_of?(Hash) && value.size > 5
|
6
|
+
%div(data-role="collapsible" data-collapsed=true data-theme="b")
|
7
|
+
%h2
|
8
|
+
&=k
|
9
|
+
= haml :"shared/values", :layout => false, :locals => {:hash => value}
|
10
|
+
-elsif value.kind_of?(Array) && value.size > 5
|
11
|
+
%div(data-role="collapsible" data-collapsed=true data-theme="b")
|
12
|
+
%h2
|
13
|
+
&=k
|
14
|
+
-tmp = []
|
15
|
+
-value.each_with_index do |element, index|
|
16
|
+
-if element.kind_of?(Hash)
|
17
|
+
%div(data-role="collapsible" data-collapsed=true data-theme="e")
|
18
|
+
%h2
|
19
|
+
&=index
|
20
|
+
= haml :"shared/values", :layout => false, :locals => {:hash => element}
|
21
|
+
-else
|
22
|
+
-tmp << element
|
23
|
+
- if !tmp.empty?
|
24
|
+
=humanize(tmp)
|
25
|
+
%br
|
26
|
+
|
27
|
+
-else
|
28
|
+
%b
|
29
|
+
-if key == "_type"
|
30
|
+
%span.type_key
|
31
|
+
&=k
|
32
|
+
-elsif key == "_id"
|
33
|
+
%span.pk_key
|
34
|
+
&=k
|
35
|
+
-elsif key =~ /_id$/
|
36
|
+
%span.fk_key
|
37
|
+
&=k
|
38
|
+
-elsif key =~ /_at$/
|
39
|
+
%span.date_key
|
40
|
+
&=k
|
41
|
+
-elsif ["name", "title", "error", "message"].include?(key)
|
42
|
+
%span.name_key
|
43
|
+
&=k
|
44
|
+
-elsif key =~ /^_/
|
45
|
+
%span.private_key
|
46
|
+
&=k
|
47
|
+
-else
|
48
|
+
%span.normal_key
|
49
|
+
&=k
|
50
|
+
\=
|
51
|
+
|
52
|
+
-if key == "_type"
|
53
|
+
%span.type_value
|
54
|
+
&=v
|
55
|
+
-elsif key == "_id"
|
56
|
+
%span.pk_value
|
57
|
+
&=v
|
58
|
+
-elsif key =~ /_id$/
|
59
|
+
%span.fk_value
|
60
|
+
&=v
|
61
|
+
-elsif key =~ /_at$/
|
62
|
+
%span.date_value
|
63
|
+
&=v
|
64
|
+
-elsif ["name", "title", "error", "message"].include?(key)
|
65
|
+
%span.name_value
|
66
|
+
&=v
|
67
|
+
-elsif key =~ /^_/
|
68
|
+
%span.private_value
|
69
|
+
&=v
|
70
|
+
-else
|
71
|
+
%span.normal_value
|
72
|
+
&=v
|
73
|
+
%br<
|
74
|
+
|