torquebox-backstage 0.2.1 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG.md CHANGED
@@ -1,6 +1,11 @@
1
+ * 0.3.0 - 2011-04-29
2
+ * you can now tail application and jboss logs
3
+ * added a dashboard
4
+ * updated to use the TorqueBox 1.0.0.Final gems
5
+
1
6
  * 0.2.1 - 2011-04-27
2
7
  * The deployment descriptor name was changed from backstage-knob.yml to torquebox-backstage-knob.yml
3
-
8
+
4
9
  * 0.2.0 - 2011-04-26
5
10
  * if the queue isn't available via jndi, just log an error instead of raising
6
11
  * updated to use the TorqueBox 1.0.0.CR2 gems
data/Gemfile CHANGED
@@ -3,9 +3,10 @@ source :rubygems
3
3
  gem "sinatra", "1.1.2"
4
4
  gem "rack-flash"
5
5
  gem 'haml', '~>3.0'
6
- gem "jmx", '0.7'
6
+ gem 'sass', '~>3.0'
7
+ gem "tobias-jmx", '0.8'
7
8
  gem 'json'
8
- gem 'torquebox', '1.0.0.CR2'
9
+ gem 'torquebox', '1.0.0'
9
10
  gem 'tobias-sinatra-url-for'
10
11
  gem 'rack-accept'
11
12
 
data/Gemfile.lock CHANGED
@@ -3,12 +3,11 @@ GEM
3
3
  specs:
4
4
  diff-lcs (1.1.2)
5
5
  git (1.2.5)
6
- haml (3.0.25)
6
+ haml (3.1.1)
7
7
  jeweler (1.5.2)
8
8
  bundler (~> 1.0.0)
9
9
  git (>= 1.2.5)
10
10
  rake
11
- jmx (0.7)
12
11
  json (1.5.1-java)
13
12
  rack (1.2.2)
14
13
  rack-accept (0.4.3)
@@ -26,28 +25,30 @@ GEM
26
25
  rspec-expectations (2.5.0)
27
26
  diff-lcs (~> 1.1.2)
28
27
  rspec-mocks (2.5.0)
28
+ sass (3.1.1)
29
29
  sinatra (1.1.2)
30
30
  rack (~> 1.1)
31
31
  tilt (~> 1.2)
32
32
  thor (0.14.6)
33
- tilt (1.2.2)
33
+ tilt (1.3)
34
+ tobias-jmx (0.8)
34
35
  tobias-sinatra-url-for (0.2.1)
35
36
  sinatra (>= 0.9.1.1)
36
- torquebox (1.0.0.CR2)
37
- torquebox-base (= 1.0.0.CR2)
38
- torquebox-messaging (= 1.0.0.CR2)
39
- torquebox-naming (= 1.0.0.CR2)
40
- torquebox-rake-support (= 1.0.0.CR2)
41
- torquebox-vfs (= 1.0.0.CR2)
42
- torquebox-web (= 1.0.0.CR2)
43
- torquebox-base (1.0.0.CR2-java)
44
- torquebox-messaging (1.0.0.CR2-java)
45
- torquebox-base (= 1.0.0.CR2)
46
- torquebox-naming (= 1.0.0.CR2)
47
- torquebox-naming (1.0.0.CR2-java)
48
- torquebox-rake-support (1.0.0.CR2)
49
- torquebox-vfs (1.0.0.CR2-java)
50
- torquebox-web (1.0.0.CR2-java)
37
+ torquebox (1.0.0)
38
+ torquebox-base (= 1.0.0)
39
+ torquebox-messaging (= 1.0.0)
40
+ torquebox-naming (= 1.0.0)
41
+ torquebox-rake-support (= 1.0.0)
42
+ torquebox-vfs (= 1.0.0)
43
+ torquebox-web (= 1.0.0)
44
+ torquebox-base (1.0.0-java)
45
+ torquebox-messaging (1.0.0-java)
46
+ torquebox-base (= 1.0.0)
47
+ torquebox-naming (= 1.0.0)
48
+ torquebox-naming (1.0.0-java)
49
+ torquebox-rake-support (1.0.0)
50
+ torquebox-vfs (1.0.0-java)
51
+ torquebox-web (1.0.0-java)
51
52
  watchr (0.7)
52
53
 
53
54
  PLATFORMS
@@ -56,14 +57,15 @@ PLATFORMS
56
57
  DEPENDENCIES
57
58
  haml (~> 3.0)
58
59
  jeweler
59
- jmx (= 0.7)
60
60
  json
61
61
  rack-accept
62
62
  rack-flash
63
63
  rack-test
64
64
  rspec
65
+ sass (~> 3.0)
65
66
  sinatra (= 1.1.2)
66
67
  thor
68
+ tobias-jmx (= 0.8)
67
69
  tobias-sinatra-url-for
68
- torquebox (= 1.0.0.CR2)
70
+ torquebox (= 1.0.0)
69
71
  watchr
data/README.md CHANGED
@@ -100,7 +100,9 @@ Returns:
100
100
  "topics":"http://localhost:8080/backstage/topics?format=json",
101
101
  "message_processors":"http://localhost:8080/backstage/message_processors?format=json",
102
102
  "jobs":"http://localhost:8080/backstage/jobs?format=json",
103
- "services":"http://localhost:8080/backstage/services?format=json"
103
+ "services":"http://localhost:8080/backstage/services?format=json",
104
+ "pools":"http://localhost:8080/backstage/pools?format=json",
105
+ "logs":"http://localhost:8080/backstage/logs?format=json"
104
106
  }
105
107
  }
106
108
 
data/Rakefile CHANGED
@@ -16,8 +16,8 @@ Jeweler::Tasks.new do |gem|
16
16
  gem.name = "torquebox-backstage"
17
17
  gem.homepage = "http://github.com/torquebox/backstage"
18
18
  gem.license = "MIT"
19
- gem.summary = %Q{BackStage - Queue/Topic/Job viewer for TorqueBox}
20
- gem.description = %Q{BackStage - Queue/Topic/Job viewer for TorqueBox}
19
+ gem.summary = %Q{BackStage - Queue/Topic/Job/etc viewer for TorqueBox}
20
+ gem.description = %Q{BackStage allows you to look behind the TorqueBox curtain, and view information about all of the components you have running. It includes support for remote code execution and log tailing to aid in debugging.}
21
21
  gem.email = "tcrawley@redhat.com"
22
22
  gem.authors = ["Tobias Crawley"]
23
23
  gem.files = FileList["[A-Z]*", 'backstage.rb', 'config.ru', 'bin/*.rb', "{config,lib,spec,views}/**/*"]
data/TODO CHANGED
@@ -7,4 +7,4 @@ TODO
7
7
  * improve test coverage
8
8
  * add stats collection, with memory usage and other bits? ruby runtime stats?
9
9
  * link to app context (requires TB changes)
10
- * allow log tailing/download
10
+ * allow log download
data/TORQUEBOX_VERSION CHANGED
@@ -1 +1 @@
1
- 1.0.0.CR2
1
+ 1.0.0
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.2.1
1
+ 0.3.0
data/backstage.rb CHANGED
@@ -34,24 +34,26 @@ require 'apps'
34
34
  require 'destinations'
35
35
  require 'message_processors'
36
36
  require 'jobs'
37
+ require 'logs'
37
38
  require 'services'
38
39
 
39
40
 
40
- puts "ENV['REQUIRE_AUTHENTICATION'] is not set, *disabling* authentication" unless ENV['REQUIRE_AUTHENTICATION']
41
+ Backstage.logger.warn "ENV['REQUIRE_AUTHENTICATION'] is not set, *disabling* authentication" unless ENV['REQUIRE_AUTHENTICATION']
41
42
 
42
43
  module Backstage
43
44
  BACKSTAGE_VERSION = File.readlines( File.join( File.dirname( __FILE__ ), 'VERSION' ) ).first.strip
44
45
  TORQUEBOX_VERSION = File.readlines( File.join( File.dirname( __FILE__ ), 'TORQUEBOX_VERSION' ) ).first.strip
45
46
 
46
47
  class Application < Sinatra::Base
47
- enable :logging, :sessions
48
+ enable :sessions
48
49
  use Rack::Accept
49
50
  use Rack::Flash
50
-
51
+ use Rack::CommonLogger, Backstage.logger
52
+
51
53
  include Backstage::Authentication
52
54
 
53
55
  set :views, Proc.new { File.join( File.dirname( __FILE__ ), "views" ) }
54
-
56
+
55
57
  before do
56
58
  require_authentication if ENV['REQUIRE_AUTHENTICATION']
57
59
  end
@@ -60,7 +62,7 @@ module Backstage
60
62
  content_type :json
61
63
 
62
64
  {
63
- :collections => [:pools, :apps, :queues, :topics, :message_processors, :jobs, :services].inject({}) do |collections, collection|
65
+ :collections => [:pools, :apps, :queues, :topics, :message_processors, :jobs, :services, :logs].inject({}) do |collections, collection|
64
66
  collections[collection] = json_url_for( collection_path( collection ) )
65
67
  collections
66
68
  end
@@ -68,7 +70,11 @@ module Backstage
68
70
  end
69
71
 
70
72
  get '/' do
71
- redirect_to collection_path( :apps )
73
+ if html_requested?
74
+ haml :'dashboard/index'
75
+ else
76
+ redirect_to '/url'
77
+ end
72
78
  end
73
79
 
74
80
  get '/css/style.css' do
data/config/torquebox.yml CHANGED
@@ -1,5 +1,6 @@
1
1
  web:
2
2
  context: /backstage
3
+ static: public
3
4
 
4
5
  tasks:
5
6
  Backgroundable:
@@ -27,7 +27,24 @@ module Backstage
27
27
  def self.to_hash_attributes
28
28
  super + [:name, :environment_name, :root_path, :deployed_at]
29
29
  end
30
+
31
+ def has_logs?
32
+ File.directory?( log_dir )
33
+ end
30
34
 
35
+ def logs
36
+ logs = Log.all( log_dir )
37
+ logs.each { |log| log.parent = self }
38
+ logs
39
+ end
40
+
41
+ def log_dir
42
+ log_dir = File.join( root_path, 'logs' )
43
+ log_dir = File.join( Backstage.jboss_log_dir, name ) if !File.directory?( log_dir ) && Backstage.jboss_log_dir
44
+ log_dir = File.join( root_path, 'log' ) unless File.directory?( log_dir )
45
+
46
+ log_dir
47
+ end
31
48
  end
32
49
  end
33
50
 
data/lib/apps/routes.rb CHANGED
@@ -14,5 +14,33 @@
14
14
  # limitations under the License.
15
15
  #
16
16
 
17
- Backstage::Application.resource :app
17
+ module Backstage
18
+ class Application < Sinatra::Base
19
+ resource :app
20
+
21
+ get "/app/:name/logs" do
22
+ @parent = App.find( Util.decode_name( params[:name] ) )
23
+ @collection = @parent.logs
24
+ if html_requested?
25
+ haml :'logs/index'
26
+ else
27
+ content_type :json
28
+ collection_to_json( @collection )
29
+ end
30
+ end
31
+
32
+ get "/app/:name/log/:id" do
33
+ @parent = App.find( Util.decode_name( params[:name] ) )
34
+ @object = Log.find( Util.decode_name( params[:id] ) )
35
+ @object.parent = @parent
36
+
37
+ if html_requested?
38
+ haml :'logs/show'
39
+ else
40
+ object_to_json( @object )
41
+ end
42
+ end
43
+
44
+ end
45
+ end
18
46
 
@@ -43,8 +43,8 @@ module Backstage
43
43
  yield message
44
44
  end
45
45
  rescue Exception => ex
46
- puts "WARNING - failed to access messages for queue #{jndi_name}: #{ex}"
47
- puts ex.backtrace.join( "\n" )
46
+ Backstage.logger "WARNING - failed to access messages for queue #{jndi_name}: #{ex}"
47
+ Backstage.logger ex
48
48
  end
49
49
 
50
50
  def display_name
@@ -72,6 +72,16 @@ module Backstage
72
72
  name =~ %r{/queues/torquebox/(.*)} ? $1 : 'n/a'
73
73
  end
74
74
 
75
+ def pause
76
+ super
77
+ self
78
+ end
79
+
80
+ def resume
81
+ super
82
+ self
83
+ end
84
+
75
85
  def status
76
86
  mbean.paused ? 'Paused' : 'Running'
77
87
  end
@@ -38,7 +38,19 @@ module Backstage
38
38
  object_to_json( @object )
39
39
  end
40
40
  end
41
-
41
+
42
+ # we can't implement move until we figure out how to get the
43
+ # actual HQ internal message id on the client side
44
+ # post "/queue/:name/message/:id/move" do
45
+ # @destination = Queue.find( Util.decode_name( params[:name] ) )
46
+ # @destination.mbean.move_message( params[:id], params[:queue])
47
+ # if html_requested?
48
+ # haml :'messages/show'
49
+ # else
50
+ # object_to_json( @destination )
51
+ # end
52
+ # end
53
+
42
54
  end
43
55
  end
44
56
 
data/lib/has_mbean.rb CHANGED
@@ -16,6 +16,7 @@
16
16
 
17
17
  module Backstage
18
18
  module HasMBean
19
+ include Comparable
19
20
  java_import javax.management.ObjectName
20
21
 
21
22
  def self.included(base)
@@ -27,6 +28,10 @@ module Backstage
27
28
  self.mbean_name = mbean_name
28
29
  self.mbean = mbean
29
30
  end
31
+
32
+ def <=>(other)
33
+ full_name <=> other.full_name
34
+ end
30
35
 
31
36
  def full_name
32
37
  mbean_name.to_s
@@ -44,7 +49,7 @@ module Backstage
44
49
  end
45
50
 
46
51
  def all(filter_string = filter)
47
- jmx_server.query_names( filter_string ).collect { |name| new( name, jmx_server[name] ) }
52
+ jmx_server.query_names( filter_string ).collect { |name| new( name, jmx_server[name] ) }.sort
48
53
  end
49
54
 
50
55
  def find(name)
data/lib/helpers.rb CHANGED
@@ -13,14 +13,37 @@
13
13
  # See the License for the specific language governing permissions and
14
14
  # limitations under the License.
15
15
  #
16
-
16
+ require 'logger'
17
17
  require 'util'
18
18
  require 'authentication'
19
19
  require 'sinatra/url_for'
20
20
 
21
21
  module Backstage
22
- class Application < Sinatra::Base
22
+ def self.jboss_log_dir
23
+ java.lang.System.get_property( 'jboss.server.log.dir' )
24
+ end
25
+
26
+ def self.jboss_app_name
27
+ App.find( "torquebox.apps:app=torquebox-backstage" ) ? 'torquebox-backstage' : 'backstage'
28
+ end
29
+
30
+ def self.logger
31
+ return @logger if @logger
23
32
 
33
+ # log to <jboss log dir>/torquebox/<RACK_ENV>.log if you can,
34
+ # otherwise do <app dir>/log/<RACK_ENV>.log
35
+ if Backstage.jboss_log_dir && File.directory?( Backstage.jboss_log_dir )
36
+ log_dir = File.join( Backstage.jboss_log_dir, Backstage.jboss_app_name )
37
+ else
38
+ log_dir = File.join( File.dirname( __FILE__ ), '..', 'log' )
39
+ end
40
+ FileUtils.mkdir_p( log_dir )
41
+
42
+ @logger = Logger.new( File.join( log_dir, "#{ENV['RACK_ENV']}.log" ) )
43
+ end
44
+
45
+ class Application < Sinatra::Base
46
+
24
47
  helpers do
25
48
  include Backstage::Authentication
26
49
  include Sinatra::UrlForHelper
@@ -29,7 +52,7 @@ module Backstage
29
52
  options[:format] = 'json'
30
53
  url_for( fragment, :full, options )
31
54
  end
32
-
55
+
33
56
  def object_path(object)
34
57
  object_action_or_collection_path(*(object.association_chain << nil))
35
58
  end
@@ -45,7 +68,7 @@ module Backstage
45
68
  end
46
69
  alias_method :object_action_path, :object_action_or_collection_path
47
70
  alias_method :collection_path, :object_action_or_collection_path
48
-
71
+
49
72
  def redirect_to(location)
50
73
  redirect url_for(location, :full)
51
74
  end
@@ -59,15 +82,15 @@ module Backstage
59
82
  dom_class << 'status' << value.downcase if name.to_s.downcase == 'status' # hack
60
83
  "<tr class='data-row'><td class='label'>#{name}</td><td class='#{dom_class.join(' ')}'>#{value}</td></tr>"
61
84
  end
62
-
85
+
63
86
  def simple_class_name(object)
64
87
  object.class.name.split( "::" ).last.underscore
65
88
  end
66
-
89
+
67
90
  def truncate(text, length = 30)
68
91
  text.length > length ? text[0...length] + '...' : text
69
92
  end
70
-
93
+
71
94
  def class_for_body
72
95
  klass = request.path_info.split('/').reverse.select { |part| part =~ /^[A-Za-z_]*$/ }
73
96
  klass.empty? ? 'root' : klass
@@ -85,21 +108,24 @@ module Backstage
85
108
  def html_requested?
86
109
  params[:format] != 'json' && env['rack-accept.request'].media_type?( 'text/html' )
87
110
  end
88
-
111
+
89
112
  def collection_to_json( collection )
90
113
  JSON.generate( collection.collect { |object| object_to_hash( object ) } )
91
114
  end
92
115
 
93
116
  def object_to_json(object)
94
- JSON.generate( object_to_hash( object ) )
117
+ JSON.generate( object_to_hash( object ) )
95
118
  end
96
-
119
+
97
120
  def object_to_hash(object)
98
121
  response = object.to_hash
99
- response[:actions] = object.available_actions.inject({}) do |actions, action|
100
- actions[action] = json_url_for( object_action_path( response[:resource], action ) )
101
- actions
122
+ if object.respond_to?( :available_actions )
123
+ response[:actions] = object.available_actions.inject({}) do |actions, action|
124
+ actions[action] = json_url_for( object_action_path( response[:resource], action ) )
125
+ actions
126
+ end
102
127
  end
128
+
103
129
  response.each do |key, value|
104
130
  if value.kind_of?( Resource )
105
131
  response[key] = json_url_for( object_path( value ) )
@@ -115,10 +141,57 @@ module Backstage
115
141
  response
116
142
  end
117
143
 
144
+ def human_size(size)
145
+ if size > 1024
146
+ size = size.to_f
147
+ if size > 1024*1024
148
+ size /= 1024*1024
149
+ suffix = 'Mb'
150
+ elsif size > 1024
151
+ size /= 1024
152
+ suffix = 'kb'
153
+ end
154
+ size = size.round( 2 )
155
+ else
156
+ suffix = 'b'
157
+ end
158
+
159
+ "#{size} #{suffix}"
160
+ end
161
+
162
+ def torquebox_version_info
163
+ versions = []
164
+ torquebox = JMX::MBeanServer.new[javax.management.ObjectName.new( 'torquebox:type=system' )]
165
+ versions << ['Version', torquebox.version]
166
+ versions << ['Build Number', torquebox.build_number]
167
+ versions << ['Revision', torquebox.revision]
168
+ versions
169
+ end
170
+
171
+ def hornetq_version_info
172
+ versions = []
173
+ hornetq = JMX::MBeanServer.new[javax.management.ObjectName.new( 'org.hornetq:module=Core,type=Server' )]
174
+ versions << ['Version', hornetq.version]
175
+ versions << ['Clustered', hornetq.clustered]
176
+ versions
177
+ end
178
+
179
+ def jboss_version_info
180
+ versions = []
181
+ jboss = JMX::MBeanServer.new[javax.management.ObjectName.new( 'jboss.system:type=Server' )]
182
+ versions << ['Version', jboss.version]
183
+ versions
184
+ end
118
185
  end
119
186
  end
120
187
  end
121
188
 
189
+ class Object
190
+ def blank?
191
+ nil? or (respond_to?( :empty? ) and empty?)
192
+ end
193
+ end
194
+
122
195
  class String
123
196
  def classify
124
197
  if self =~ %r{/}
@@ -133,11 +206,11 @@ class String
133
206
  def constantize
134
207
  eval( classify )
135
208
  end
136
-
209
+
137
210
  def underscore
138
211
  gsub(/([a-zA-Z])([A-Z])/, '\1_\2').downcase
139
212
  end
140
-
213
+
141
214
  def humanize
142
215
  split( '_' ).collect( &:capitalize ).join( ' ' )
143
216
  end
@@ -147,3 +220,7 @@ class String
147
220
  "#{self}s"
148
221
  end
149
222
  end
223
+
224
+ class Logger
225
+ alias_method :write, :<<
226
+ end