Permalink
Browse files

US2815: Refactor RHC threaddump command

  • Loading branch information...
1 parent fb6d1f1 commit a4c2cfde6beb0bcc39a6478714c5ccabc8ad9927 Chris Alfonso committed Sep 5, 2012
View
4 autocomplete/rhc
@@ -16,7 +16,7 @@ _rhc()
opts="create show alter status destroy"
;;
app)
- opts="create show start stop force-stop restart reload status destroy tidy add-alias remove-alias threaddump snapshot tail cartridge"
+ opts="create show start stop force-stop restart reload status destroy tidy add-alias remove-alias snapshot tail cartridge"
;;
sshkey)
opts="add update remove list"
@@ -48,7 +48,7 @@ _rhc()
create)
opts="--debug --help --rhlogin --password --no-dns --nogit --app --repo --type --enable-jenkins --config --timeout"
;;
- show | start | stop | force-stop | restart | reload | status | destroy | tidy | add-alias | remove-alias | threaddump | destroy )
+ show | start | stop | force-stop | restart | reload | status | destroy | tidy | add-alias | remove-alias | destroy )
opts="--debug --help --rhlogin --password --app --alias --bypass --config --timeout"
;;
tail)
View
1 bin/rhc
@@ -15,6 +15,7 @@ Command line tool for performing operations related to your rhcloud account.
List of resources
domain Manage the namespace for the registered rhcloud user.
+ threaddump Trigger a thread dump for jbossas, jbosseap, and ruby applications.
app Manage applications within the rhcloud account.
sshkey Manage multiple keys for the registered rhcloud user.
port-forward Forward remote ports to the workstation
View
5 bin/rhc-app
@@ -32,7 +32,6 @@ List of commands
tidy Garbage collects the git repo and empties log/tmp dirs
add-alias Add a custom domain name for the application
remove-alias Remove a custom domain name for the application
- threaddump Trigger a thread dump for jbossas, jbosseap, and ruby applications
tail Tail the logs of an application
snapshot [save|restore] Saves/Restores an application snapshot to/from a tarball at the location specified using --filepath (default: ./$APPNAME.tar.gz)
cartridge <action> Manage a cartridge running in this application
@@ -425,7 +424,7 @@ begin
["--config", GetoptLong::REQUIRED_ARGUMENT],
["--timeout", GetoptLong::REQUIRED_ARGUMENT]
)
- elsif ARGV[0] =~ /^(show|start|stop|force-stop|restart|reload|status|destroy|tidy|add-alias|remove-alias|threaddump|destroy)$/
+ elsif ARGV[0] =~ /^(show|start|stop|force-stop|restart|reload|status|destroy|tidy|add-alias|remove-alias|destroy)$/
ARGV.shift
opts = GetoptLong.new(
["--debug", "-d", GetoptLong::NO_ARGUMENT],
@@ -505,7 +504,7 @@ case argv_c[0]
create_app
when "show"
show_app
- when "start", "stop", "force-stop", "restart", "reload", "status", "tidy", "add-alias", "remove-alias", "threaddump", "destroy"
+ when "start", "stop", "force-stop", "restart", "reload", "status", "tidy", "add-alias", "remove-alias", "destroy"
control_app(argv_c[0])
when "tail"
system("rhc-tail-files #{get_args argv_c} 2>&1")
View
6 bin/rhc-ctl-app
@@ -16,7 +16,7 @@ Control an OpenShift express app
-a|--app application Application name (alphanumeric) (required)
-l|--rhlogin rhlogin OpenShift login (#{rhlogin})
-p|--password password Password (optional, will prompt)
- -c|--command command (start|stop|force-stop|restart|reload|status|destroy|tidy|add-alias|remove-alias|threaddump)
+ -c|--command command (start|stop|force-stop|restart|reload|status|destroy|tidy|add-alias|remove-alias)
-L|--embedded-list List supported embedded cartridges
-e|--embed (add|remove|stop|start|restart|status|reload)-$cartridge eg: add-mysql-5.1
-b|--bypass Bypass warnings
@@ -103,8 +103,8 @@ unless opt["embed"] or opt["command"]
end
if opt["command"]
- unless opt["command"] =~ /^(start|stop|force-stop|restart|reload|status|destroy|tidy|add-alias|remove-alias|threaddump)$/
- puts "Invalid command '#{opt["command"]}' specified. Valid commands are (start|stop|force-stop|restart|reload|status|destroy|tidy|add-alias|remove-alias|threaddump)"
+ unless opt["command"] =~ /^(start|stop|force-stop|restart|reload|status|destroy|tidy|add-alias|remove-alias)$/
+ puts "Invalid command '#{opt["command"]}' specified. Valid commands are (start|stop|force-stop|restart|reload|status|destroy|tidy|add-alias|remove-alias)"
p_usage
end
elsif opt["embed"]
View
1 lib/rhc/commands/base.rb
@@ -6,7 +6,6 @@
require 'rhc/commands'
require 'rhc/exceptions'
require 'rhc/context_helper'
-
class RHC::Commands::Base
attr_writer :options, :config
View
13 lib/rhc/commands/threaddump.rb
@@ -0,0 +1,13 @@
+require 'rhc/commands/base'
+module RHC::Commands
+ class Threaddump < Base
+ summary "Trigger a thread dump for JBossAS, JBossEAP, and Ruby applications."
+ syntax "<application>"
+ argument :app, "Name of the application on which to execute the thread dump", []
+ def run(app)
+ reply = rest_client.threaddump(app)[:message]
+ results { say "#{reply}" }
+ 0
+ end
+ end
+end
View
3 lib/rhc/rest.rb
@@ -112,7 +112,8 @@ def parse_response(response)
end
return apps
when 'application'
- return Application.new(data)
+ message = result['messages'].first['text']
+ return Hash[:message => message, :data => Application.new(data)]
when 'cartridges'
carts = Array.new
data.each do |cart_json|
View
15 lib/rhc/rest/application.rb
@@ -1,9 +1,8 @@
-
module RHC
module Rest
class Application
include Rest
- attr_reader :domain_id, :name, :creation_time, :uuid, :aliases, :git_url, :app_url, :gear_profile, :framework,
+ attr_reader :domain_id, :name, :creation_time, :uuid, :aliases, :git_url, :app_url, :gear_profile, :framework,
:scalable, :health_check_path, :embedded, :gear_count, :ssh_url, :scale_min, :scale_max
def initialize(args)
#logger.debug args
@@ -24,7 +23,6 @@ def initialize(args)
@scale_min = args[:scale_min] || args["scale_min"]
@scale_max = args[:scale_max] || args["scale_max"]
@links = args[:links] || args["links"]
-
end
#Add Cartridge
@@ -88,6 +86,17 @@ def destroy
request = new_request(:url => url, :method => method, :headers => @@headers)
return request(request)
end
+
+ #Thread dump
+ def threaddump
+ logger.debug "Running thread dump for #{self.name}" if @mydebug
+ url = @links['THREAD_DUMP']['href']
+ method = @links['THREAD_DUMP']['method']
+ payload = {:event => 'thread-dump'}
+ request = new_request(:url => url, :method => method, :headers => @@headers, :payload => payload)
+ return request(request)
+
+ end
alias :delete :destroy
end
end
View
14 lib/rhc/rest/client.rb
@@ -1,6 +1,5 @@
require 'base64'
require 'rhc/json'
-
module RHC
module Rest
class Client
@@ -128,5 +127,18 @@ def logout
alias :close :logout
end
+ #Application threaddump
+ def threaddump(app)
+ logger.debug "Threaddump in progress for #{app}" if @mydebug
+ url = @links['LIST_DOMAINS']['href']
+ method = @links['LIST_DOMAINS']['method']
+ request = new_request(:url => url, :method => method, :headers => @@headers)
+ response = request(request)
+ domain = response.first if not response.empty?
+ application = domain.find_application(app)
+ return application.threaddump unless application.nil?
+
+ raise RHC::ApplicationNotFoundException.new("Application #{app} does not exist") if application.nil?
+ end
end
end
View
2 man/rhc-app.1
@@ -45,8 +45,6 @@ Garbage collects the git repo and empties log/tmp dirs
Add a custom domain name for the application
.IP remove-alias
Remove a custom domain name for the application
-.IP threaddump
-Trigger a thread dump for jbossas, jbosseap, and ruby applications
.IP tail
Tail the logs of an application
.IP snapshot [save|restore]
View
3 man/rhc-ctl-app.1
@@ -51,7 +51,7 @@ RHLogin password
.IP "\-a|\-\-app application_name"
Name of application to create
.IP "\-c|\-\-command command"
-The command to send to remote server (start|stop|force\-stop|restart|reload|status|destroy|tidy|add\-alias|remove\-alias|threaddump)
+The command to send to remote server (start|stop|force\-stop|restart|reload|status|destroy|tidy|add\-alias|remove\-alias)
.br
start \- Starts the application (includes embedded)
.br
@@ -73,7 +73,6 @@ add\-alias \- Add a custom domain name for the application
.br
remove\-alias \- Remove a custom domain name for the application
.br
-threaddump \- Trigger a thread dump for jbossas, jbosseap, and ruby applications
.IP "\-e|\-\-embed embed_action"
The action to add or remove an embedded cartridge (add|remove|stop|start|restart|status|reload)\-$cartridge. Ex: add\-mysql\-5.1
.br
View
60 man/rhc-threaddump.1
@@ -0,0 +1,60 @@
+.\" Process this file with
+.\" groff -man -Tascii rhc-domain.1
+.\"
+.TH "RHC_THREADDUMP" "1" "JANUARY 2011" "Linux" "User Manuals"
+.SH "NAME"
+rhc\-threaddump \- Trigger a thread dump for jbossas, jbosseap, and ruby applications.
+
+.SH "SYNOPSIS"
+.B rhc threaddump (--help) [<args>]
+
+.SH "DESCRIPTION"
+.B rhc threaddump - Trigger a thread dump for jbossas, jbosseap, and ruby applications.
+
+.SH "OPTIONS"
+.IP "\-l|\-\-rhlogin login"
+Red Hat login (RHN or OpenShift login with OpenShift access)
+.IP "\-p|\-\-password password"
+RHLogin password
+.IP "\-a|\-\-application application"
+Name of the application on which to trigger the thread dump
+.IP \-d|\-\-debug
+Enable debugging / verbose output
+.IP \-h|\-\-help
+Display help menu
+.IP \-\-config
+Alternate full pathname for the config file
+.IP \-\-timeout
+Timeout value, in seconds, for the connection (default is 10)
+
+.SH "FILES"
+.I <ruby_gem_dir>/gems/rhc\-<version>/conf/express.conf
+.RS
+The RubyGem system wide configuration file. See
+.BR express.conf (5)
+for further details.
+.RE
+.I /etc/openshift/express.conf
+.RS
+The system wide configuration file. See
+.BR express.conf (5)
+for further details.
+.RE
+.I ~/.openshift/express.conf
+.RS
+Per user configuration file. See
+.BR express.conf (5)
+for further details.
+.RE
+
+.SH "BUGS"
+Please contact the Red Hat OpenShift team.
+Forums: https://openshift.redhat.com/community/forums/openshift
+IRC: #openshift on irc.freenode.net
+
+.SH "AUTHOR"
+Mike McGrath <mmcgrath@redhat.com>, Jim Jagielski <jimjag@redhat.com>
+
+.SH "SEE ALSO"
+.BR rhc-app (1),
+.BR express.conf (5)
View
4 spec/rest_spec_helper.rb
@@ -67,6 +67,7 @@ def mock_app_links(domain_id='test_domain',app_id='test_app')
['START', "domains/#{domain_id}/apps/#{app_id}/start", 'post'],
['STOP', "domains/#{domain_id}/apps/#{app_id}/stop", 'post'],
['RESTART', "domains/#{domain_id}/apps/#{app_id}/restart", 'post'],
+ ['THREAD_DUMP', "domains/#{domain_id}/apps/#{app_id}/event", 'post'],
['DELETE', "domains/#{domain_id}/apps/#{app_id}/delete", 'post']]
end
@@ -131,6 +132,7 @@ def initialize
MockRestKey.new('mockkey1', 'ssh-rsa', 'AAAAB3NzaC1yc2EAAAADAQABAAABAQDNK8xT3O+kSltmCMsSqBfAgheB3YFJ9Y0ESJnFjFASVxH70AcCQAgdQSD/r31+atYShJdP7f0AMWiQUTw2tK434XSylnZWEyIR0V+j+cyOPdVQlns6D5gPOnOtweFF0o18YulwCOK8Q1H28GK8qyWhLe0FcMmxtKbbQgaVRvQdXZz4ThzutCJOyJm9xVb93+fatvwZW76oLLvfFJcJSOK2sgW7tJM2A83bm4mwixFDF7wO/+C9WA+PgPKJUIjvy1gZjBhRB+3b58vLOnYhPOgMNruJwzB+wJ3pg8tLJEjxSbHyyoi6OqMBs4BVV7LdzvwTDxEjcgtHVvaVNXgO5iRX'),
MockRestKey.new('mockkey2', 'ssh-dsa', 'AAAAB3NzaC1kc3MAAACBAPaaFj6Xjrjd8Dc4AAkJe0HigqaXMxj/87xHoV+nPgerHIceJWhPUWdW40lSASrgpAV9Eq4zzD+L19kgYdbMw0vSX5Cj3XtNOsow9MmMxFsYjTxCv4eSs/rLdGPaYZ5GVRPDu8tN42Bm8lj5o+ky3HzwW+mkQMZwcADQIgqtn6QhAAAAFQCirDfIMf/JoMOFf8CTnsTKWw/0zwAAAIAIQp6t2sLIp1d2TBfd/qLjOJA10rPADcnhBzWB/cd/oFJ8a/2nmxeSPR5Ov18T6itWqbKwvZw2UC0MrXoYbgcfVNP/ym1bCd9rB5hu1sg8WO4JIxA/47PZooT6PwTKVxHuENEzQyJL2o6ZJq+wuV0taLvm6IaM5TAZuEJ2p4TC/gAAAIBpLcVXZREa7XLY55nyidt/+UC+PxpjhPHOHbzL1OvWEaumN4wcJk/JZPppgXX9+WDkTm1SD891U0cXnGMTP0OZOHkOUHF2ZcfUe7p9kX4WjHs0OccoxV0Lny6MC4DjalJyaaEbijJHSUX3QlLcBOlPHJWpEpvWQ9P8AN4PokiGzA==')
]
+ @links = mock_response_links(mock_client_links)
end
def domains
@@ -174,6 +176,7 @@ def initialize(id, client)
@id = id
@client = client
@applications = []
+ @links = mock_response_links(mock_domain_links('mock_domain_0'))
end
def update(id)
@@ -218,6 +221,7 @@ def initialize(name, type, domain, scale=nil)
@scalable = true
@embedded = {"haproxy-1.4" => {:info => ""}}
end
+ @links = mock_response_links(mock_app_links('mock_domain_0', 'mock_app_0'))
add_cartridge(type, false) if type
end
View
83 spec/rhc/commands/threaddump_spec.rb
@@ -0,0 +1,83 @@
+require 'spec_helper'
+require 'rest_spec_helper'
+require 'rhc/commands/threaddump'
+require 'rhc/config'
+describe RHC::Commands::Threaddump do
+ let(:client_links) { mock_response_links(mock_client_links) }
+ let(:domain_0_links) { mock_response_links(mock_domain_links('mock_domain_0')) }
+ let(:domain_1_links) { mock_response_links(mock_domain_links('mock_domain_1')) }
+ let(:app_0_links) { mock_response_links(mock_app_links('mock_domain_0', 'mock_app_0')) }
+ before(:each) do
+ RHC::Config.set_defaults
+ @rc = MockRestClient.new
+ @rc.add_domain("mock_domain_0").add_application("mock_app_0", "ruby-1.8.7")
+ stub_api_request(:any, client_links['LIST_DOMAINS']['relative']).with(:headers => {'Accept'=>'application/json', 'Accept-Encoding'=>'gzip, deflate'}).
+ to_return({ :body => {
+ :type => 'domains',
+ :data =>
+ [{ :id => 'mock_domain_0',
+ :links => mock_response_links(mock_domain_links('mock_domain_0')),
+ },
+ { :id => 'mock_domain_1',
+ :links => mock_response_links(mock_domain_links('mock_domain_1')),
+ }]
+ }.to_json,
+ :status => 200
+ })
+ stub_api_request(:any, domain_0_links['LIST_APPLICATIONS']['relative']).with(:headers => {'Accept'=>'application/json', 'Accept-Encoding'=>'gzip, deflate'}).
+ to_return({ :body => {
+ :type => 'applications',
+ :data =>
+ [{ :domain_id => 'mock_domain_0',
+ :name => 'mock_app_0',
+ :creation_time => Time.new.to_s,
+ :uuid => 1234,
+ :aliases => ['alias_1', 'alias_2'],
+ :server_identity => 'mock_server_identity',
+ :links => mock_response_links(mock_app_links('mock_domain_0','mock_app_0')),
+ }]
+ }.to_json,
+ :status => 200
+ })
+ stub_api_request(:any, app_0_links['THREAD_DUMP']['relative']).with(:body => {:event => 'thread-dump'}, :headers => {'Accept'=>'application/json', 'Accept-Encoding'=>'gzip, deflate', 'Content-Length'=>'17', 'Content-Type'=>'application/x-www-form-urlencoded'}).
+ to_return({ :body => {
+ :type => 'application',
+ :data =>
+ { :domain_id => 'mock_domain_1',
+ :name => 'mock_app_0',
+ :creation_time => Time.new.to_s,
+ :uuid => 1234,
+ :aliases => ['alias_1', 'alias_2'],
+ :server_identity => 'mock_server_identity',
+ :links => mock_response_links(mock_app_links('mock_domain_1','mock_app_0')),
+ },
+ :messages => [{:text => 'Application test thread dump complete.: Success'}]
+ }.to_json,
+ :status => 200
+ })
+ end
+
+ describe 'help' do
+ let(:arguments) { ['threaddump', '--help'] }
+
+ context 'help is run' do
+ it "should display help" do
+ expect { run }.should exit_with_code(0)
+ end
+ it('should output usage') { run_output.should match("Usage: rhc threaddump") }
+ end
+ end
+
+ describe 'threaddump' do
+ let(:arguments) { ['threaddump', '--noprompt', '--config', 'test.conf', '-l', 'test@test.foo', '-p', 'password', 'mock_app_0'] }
+ context 'with no issues' do
+ it { expect { run }.should exit_with_code(0) }
+ end
+ end
+ describe 'threaddump no args' do
+ let(:arguments) { ['threaddump'] }
+ context 'args not supplied' do
+ it { expect { run }.should exit_with_code(1) }
+ end
+ end
+end
View
69 spec/rhc/rest_client_spec.rb
@@ -28,6 +28,7 @@ module Rest
let(:client_links) { mock_response_links(mock_client_links) }
let(:domain_0_links) { mock_response_links(mock_domain_links('mock_domain_0')) }
let(:domain_1_links) { mock_response_links(mock_domain_links('mock_domain_1')) }
+ let(:app_0_links) { mock_response_links(mock_app_links('mock_domain_0', 'mock_app')) }
let(:user_links) { mock_response_links(mock_user_links) }
let(:key_links) { mock_response_links(mock_key_links) }
@@ -163,6 +164,74 @@ module Rest
end
end
+ context "#threadump" do
+ before(:each) do
+ stub_api_request(:any, client_links['LIST_DOMAINS']['relative']).
+ to_return({ :body => {
+ :type => 'domains',
+ :data =>
+ [{ :id => 'mock_domain_0',
+ :links => mock_response_links(mock_domain_links('mock_domain_0')),
+ },
+ { :id => 'mock_domain_1',
+ :links => mock_response_links(mock_domain_links('mock_domain_1')),
+ }]
+ }.to_json,
+ :status => 200
+ })
+ stub_api_request(:any, domain_0_links['LIST_APPLICATIONS']['relative']).
+ to_return({ :body => {
+ :type => 'applications',
+ :data =>
+ [{ :domain_id => 'mock_domain_0',
+ :name => 'mock_app',
+ :creation_time => Time.new.to_s,
+ :uuid => 1234,
+ :aliases => ['alias_1', 'alias_2'],
+ :server_identity => 'mock_server_identity',
+ :links => mock_response_links(mock_app_links('mock_domain_0','mock_app')),
+ }]
+ }.to_json,
+ :status => 200
+ })
+ stub_api_request(:any, domain_1_links['LIST_APPLICATIONS']['relative']).
+ to_return({ :body => {
+ :type => 'applications',
+ :data =>
+ [{ :domain_id => 'mock_domain_1',
+ :name => 'mock_app',
+ :creation_time => Time.new.to_s,
+ :uuid => 1234,
+ :aliases => ['alias_1', 'alias_2'],
+ :server_identity => 'mock_server_identity',
+ :links => mock_response_links(mock_app_links('mock_domain_1','mock_app')),
+ }]
+ }.to_json,
+ :status => 200
+ })
+ stub_api_request(:any, app_0_links['THREAD_DUMP']['relative']).with(:body => {:event => 'thread-dump'}).
+ to_return({ :body => {
+ :type => 'application',
+ :data =>
+ { :domain_id => 'mock_domain_1',
+ :name => 'mock_app',
+ :creation_time => Time.new.to_s,
+ :uuid => 1234,
+ :aliases => ['alias_1', 'alias_2'],
+ :server_identity => 'mock_server_identity',
+ :links => mock_response_links(mock_app_links('mock_domain_1','mock_app')),
+ },
+ :messages => [{:text => 'Application test thread dump complete.: Success'}]
+ }.to_json,
+ :status => 200
+ })
+ end
+ it "returns a domain object for matching domain IDs" do
+ match = nil
+ expect { match = @client.threaddump('mock_app') }.should_not raise_error
+ end
+ end
+
context "#find_application" do
before(:each) do
stub_api_request(:any, client_links['LIST_DOMAINS']['relative']).
View
4 spec/rhc/rest_spec.rb
@@ -55,9 +55,9 @@ module RHC
:links => { :foo => 'bar' }
}}
it "deserializes to an application" do
- json_response = { :type => 'application', :data => object }.to_json
+ json_response = { :type => 'application', :data => object, :messages => [{'text' => 'test message'}]}.to_json
app_obj = RHC::Rest::Application.new(object)
- subject.parse_response(json_response).should have_same_attributes_as(app_obj)
+ subject.parse_response(json_response)[:data].should have_same_attributes_as(app_obj)
end
end

0 comments on commit a4c2cfd

Please sign in to comment.