rhc 0.94.8 → 0.95.13

Sign up to get free protection for your applications and to get access to all the features.
Files changed (59) hide show
  1. data/README.md +27 -1
  2. data/bin/rhc +15 -23
  3. data/bin/rhc-app +4 -1
  4. data/bin/rhc-chk +3 -0
  5. data/bin/rhc-create-app +3 -0
  6. data/bin/rhc-create-domain +3 -0
  7. data/bin/rhc-ctl-app +3 -0
  8. data/bin/rhc-ctl-domain +3 -0
  9. data/bin/rhc-domain +5 -2
  10. data/bin/rhc-domain-info +3 -0
  11. data/bin/rhc-port-forward +16 -18
  12. data/bin/rhc-snapshot +3 -0
  13. data/bin/rhc-sshkey +3 -0
  14. data/bin/rhc-tail-files +3 -0
  15. data/bin/rhc-user-info +1 -0
  16. data/features/README.md +70 -0
  17. data/features/lib/rhc_helper/app.rb +124 -0
  18. data/features/lib/rhc_helper/cartridge.rb +72 -0
  19. data/features/lib/rhc_helper/commandify.rb +154 -0
  20. data/features/lib/rhc_helper/domain.rb +50 -0
  21. data/features/lib/rhc_helper/httpify.rb +107 -0
  22. data/features/lib/rhc_helper/loggable.rb +39 -0
  23. data/features/lib/rhc_helper/persistable.rb +38 -0
  24. data/features/lib/rhc_helper/runnable.rb +41 -0
  25. data/features/lib/rhc_helper.rb +7 -0
  26. data/features/step_definitions/application_steps.rb +99 -0
  27. data/features/step_definitions/cartridge_steps.rb +42 -0
  28. data/features/step_definitions/client_steps.rb +32 -0
  29. data/features/step_definitions/domain_steps.rb +19 -0
  30. data/features/support/env.rb +99 -0
  31. data/features/verify.feature +123 -0
  32. data/lib/rhc/cli.rb +4 -1
  33. data/lib/rhc/commands/base.rb +28 -6
  34. data/lib/rhc/commands/server.rb +4 -1
  35. data/lib/rhc/commands/setup.rb +24 -0
  36. data/lib/rhc/commands.rb +10 -5
  37. data/lib/rhc/config.rb +90 -21
  38. data/lib/rhc/core_ext.rb +11 -2
  39. data/lib/rhc/coverage_helper.rb +35 -0
  40. data/lib/rhc/help_formatter.rb +30 -0
  41. data/lib/rhc/helpers.rb +41 -5
  42. data/lib/rhc/ssh_key_helpers.rb +72 -0
  43. data/lib/rhc/targz.rb +2 -8
  44. data/lib/rhc/wizard.rb +75 -58
  45. data/lib/rhc-common.rb +20 -13
  46. data/lib/rhc-rest.rb +3 -11
  47. data/spec/coverage_helper.rb +51 -0
  48. data/spec/rest_spec_helper.rb +86 -0
  49. data/spec/rhc/cli_spec.rb +19 -3
  50. data/spec/rhc/commands/server_spec.rb +2 -2
  51. data/spec/rhc/common_spec.rb +49 -0
  52. data/spec/rhc/config_spec.rb +328 -0
  53. data/spec/rhc/helpers_spec.rb +74 -1
  54. data/spec/rhc/rest_client_spec.rb +402 -0
  55. data/spec/rhc/rest_spec.rb +454 -0
  56. data/spec/rhc/targz_spec.rb +13 -0
  57. data/spec/rhc/wizard_spec.rb +305 -43
  58. data/spec/spec_helper.rb +30 -25
  59. metadata +124 -5
data/README.md CHANGED
@@ -19,7 +19,6 @@ DEPENDENCIES:
19
19
  * openssh-clients
20
20
  * ruby (1.8.7 or later)
21
21
  * rubygems
22
- * parseconfig gem
23
22
 
24
23
  Step 1: Create a domain to under which your applications will live:
25
24
 
@@ -65,3 +64,30 @@ Installing git from MacPorts/HomeBrew/Fink/etc requires Xcode.
65
64
  Now obtain the client code, either via 'git clone' as above
66
65
  or via the rhc gem.
67
66
 
67
+ ## Developing / Contributing
68
+ We expect code contributions to follow these standards:
69
+
70
+ 1. Ensure code matches the [GitHub Ruby styleguide](https://github.com/styleguide/ruby), except where the file establishes a different standard.
71
+ 2. We use RSpec for functional testing and Cucumber for our high level
72
+ integration tests. Specs are in 'spec/' and can be run with <code>bundle
73
+ exec rake spec</code>. Features are in 'features/' and can be run with
74
+ <code>bundle exec rake features</code> (although these tests runs
75
+ against the gem installed locally so you will need to gem install
76
+ first). See [README.md](https://github.com/openshift/os-client-tools/blob/master/features/README.md) in the features dir for more info.
77
+ 3. We maintain 100% line coverage of all newly added code via spec
78
+ testing. The build will fail if new code is added and it does not
79
+ have full line coverage. Some old code is currently excluded until it
80
+ can be refactored. Run <code>bundle exec rake spec</code> on Ruby 1.9+
81
+ to see your code coverage level.
82
+
83
+ Once you've made your changes:
84
+
85
+ 1. [Fork](http://help.github.com/forking/) the code
86
+ 2. Create a topic branch - `git checkout -b my_branch`
87
+ 3. Push to your branch - `git push origin my_branch`
88
+ 4. Create a [Pull Request](http://help.github.com/pull-requests/) from your branch
89
+ 5. That's it!
90
+
91
+ If you use vim, we've included a .vimrc in the root of this project.
92
+ In order to use it, install https://github.com/MarcWeber/vim-addon-local-vimrc
93
+
data/bin/rhc CHANGED
@@ -2,6 +2,7 @@
2
2
  #
3
3
  # print help
4
4
  #
5
+ require 'rhc/coverage_helper'
5
6
 
6
7
  # Require rhc-common for wizard invocation
7
8
  require 'rhc-common'
@@ -38,7 +39,7 @@ def get_args
38
39
  end
39
40
 
40
41
  def run_setup_wizard_if_needed
41
- default_setup_wizard unless ARGV.include?('--noprompt')
42
+ default_setup_wizard unless ARGV.delete('--noprompt')
42
43
  end
43
44
 
44
45
  begin
@@ -59,31 +60,22 @@ begin
59
60
  run_setup_wizard_if_needed
60
61
  system("rhc-port-forward #{get_args} 2>&1")
61
62
  retcode = $?.exitstatus
62
- when "server"
63
- run_setup_wizard_if_needed
64
- begin
65
- require 'rhc/cli'
66
- RHC::CLI.start(ARGV)
67
- retcode = 0
68
- rescue SystemExit => e
69
- retcode = e.status
70
- end
71
- when "setup"
72
- if ARGV.include?('--help') or ARGV.include?('-h') or ARGV.include?('help')
73
- puts "Usage: rhc setup"
74
- puts "Runs the setup wizard to configure your account"
75
- exit 0
76
- end
77
-
78
- w = RHC::RerunWizard.new(RHC::Config.local_config_path)
79
- success = w.run
80
- retcode = 0
81
- retcode = 1 unless success
82
63
  when "-h", "--help", "help", nil
83
64
  p_usage 0
84
65
  else
85
- puts "Invalid rhc command: #{ARGV[0]}"
86
- p_usage
66
+ if ["server", "setup"].include?(ARGV[0])
67
+ run_setup_wizard_if_needed if ARGV[0] != "setup"
68
+ begin
69
+ require 'rhc/cli'
70
+ RHC::CLI.start(ARGV)
71
+ retcode = 0
72
+ rescue SystemExit => e
73
+ retcode = e.status
74
+ end
75
+ else
76
+ puts "Invalid rhc command: #{ARGV[0]}"
77
+ p_usage
78
+ end
87
79
  end
88
80
 
89
81
  if retcode == nil
data/bin/rhc-app CHANGED
@@ -1,4 +1,7 @@
1
1
  #!/usr/bin/env ruby
2
+
3
+ require 'rhc/coverage_helper'
4
+
2
5
  require 'rhc-common'
3
6
  require 'base64'
4
7
 
@@ -93,7 +96,7 @@ def validate_args(val_type=true, val_cartridge=false, val_timeout=true)
93
96
  p_usage
94
97
  end
95
98
 
96
- debug = $opt.has_key? 'debug'
99
+ debug = true if $opt.has_key? 'debug'
97
100
  RHC::debug(debug)
98
101
 
99
102
  RHC::timeout($opt["timeout"], get_var('timeout')) if val_timeout
data/bin/rhc-chk CHANGED
@@ -1,4 +1,7 @@
1
1
  #!/usr/bin/env ruby
2
+
3
+ require 'rhc/coverage_helper'
4
+
2
5
  require 'rhc-common'
3
6
  require 'net/http'
4
7
  require 'net/https'
data/bin/rhc-create-app CHANGED
@@ -1,4 +1,7 @@
1
1
  #!/usr/bin/env ruby
2
+
3
+ require 'rhc/coverage_helper'
4
+
2
5
  require 'rhc-common'
3
6
 
4
7
  def p_usage(error_code = 255)
@@ -1,4 +1,7 @@
1
1
  #!/usr/bin/env ruby
2
+
3
+ require 'rhc/coverage_helper'
4
+
2
5
  require 'rhc-common'
3
6
 
4
7
  #
data/bin/rhc-ctl-app CHANGED
@@ -1,4 +1,7 @@
1
1
  #!/usr/bin/env ruby
2
+
3
+ require 'rhc/coverage_helper'
4
+
2
5
  require 'rhc-common'
3
6
 
4
7
  embed_mapper = { 'add' => 'configure', 'remove' => 'deconfigure' }
data/bin/rhc-ctl-domain CHANGED
@@ -1,4 +1,7 @@
1
1
  #!/usr/bin/env ruby
2
+
3
+ require 'rhc/coverage_helper'
4
+
2
5
  require 'rhc-common'
3
6
 
4
7
  #
data/bin/rhc-domain CHANGED
@@ -1,4 +1,7 @@
1
1
  #!/usr/bin/env ruby
2
+
3
+ require 'rhc/coverage_helper'
4
+
2
5
  require 'rhc-common'
3
6
 
4
7
  #
@@ -77,8 +80,8 @@ def create_or_alter_domain(alter=false)
77
80
  puts "OpenShift key found at #{ssh_key_file_path}. Reusing..."
78
81
  else
79
82
  puts "Generating OpenShift ssh key to #{ssh_key_file_path}"
80
- # Use system for interaction
81
- system("ssh-keygen -t rsa -f '#{ssh_key_file_path}'")
83
+ w = RHC::SSHWizard.new($opt['rhlogin'], $password)
84
+ w.run
82
85
  end
83
86
 
84
87
  ssh_keyfile_contents = File.open(ssh_pub_key_file_path).gets.chomp.split(' ')
data/bin/rhc-domain-info CHANGED
@@ -1,4 +1,7 @@
1
1
  #!/usr/bin/env ruby
2
+
3
+ require 'rhc/coverage_helper'
4
+
2
5
  require 'rhc-common'
3
6
 
4
7
  #
data/bin/rhc-port-forward CHANGED
@@ -1,4 +1,7 @@
1
1
  #!/usr/bin/env ruby
2
+
3
+ require 'rhc/coverage_helper'
4
+
2
5
  require 'rhc-common'
3
6
 
4
7
  #
@@ -86,7 +89,7 @@ end
86
89
 
87
90
  # mock for windows
88
91
  if defined?(UNIXServer) != 'constant' or UNIXServer.class != Class
89
- class UNIXServer; end
92
+ class UNIXServer; end
90
93
  end
91
94
 
92
95
  app_uuid = user_info['app_info'][app_name]['uuid']
@@ -108,21 +111,17 @@ Net::SSH.start(ssh_host, app_uuid) do |ssh|
108
111
 
109
112
  ssh.exec! "rhc-list-ports" do |channel, stream, data|
110
113
 
111
- # data comes from a linux server so it is a one-line string for windows, we need to split \n manually
112
- if RHC::Helpers.windows?
113
- data = data.split /\n/
114
- end
115
-
116
114
  if stream == :stderr
117
115
 
118
- data.each { |line|
116
+ data.lines { |line|
117
+
119
118
  line = line.chomp
120
119
 
121
120
  if line.downcase =~ /permission denied/
122
121
  puts line
123
122
  exit 1
124
123
  end
125
-
124
+
126
125
  if line.index(ip_and_port_simple_regex)
127
126
  hosts_and_ports_descriptions << line
128
127
  end
@@ -130,7 +129,8 @@ Net::SSH.start(ssh_host, app_uuid) do |ssh|
130
129
 
131
130
  else
132
131
 
133
- data.each { |line|
132
+ data.lines { |line|
133
+
134
134
  line = line.chomp
135
135
 
136
136
  if line.downcase =~ /scale/
@@ -149,18 +149,15 @@ Net::SSH.start(ssh_host, app_uuid) do |ssh|
149
149
  end
150
150
 
151
151
  scaled_uuids.each { |scaled_uuid|
152
-
152
+
153
153
  puts "Using #{scaled_uuid}@#{ssh_host} (scaled instance)..." if debug
154
154
 
155
155
  Net::SSH.start(ssh_host, scaled_uuid) do |ssh|
156
156
 
157
157
  ssh.exec! "rhc-list-ports" do |channel, stream, data|
158
- # data comes from a linux server so it is a one-line string for windows, we need to split \n manually
159
- if RHC::Helpers.windows?
160
- data = data.split /\n/
161
- end
162
158
  if stream == :stderr
163
- data.each { |line|
159
+ data.lines { |line|
160
+
164
161
  line = line.chomp
165
162
  if line.downcase =~ /permission denied/
166
163
  puts line
@@ -171,7 +168,8 @@ Net::SSH.start(ssh_host, app_uuid) do |ssh|
171
168
  end
172
169
  }
173
170
  else
174
- data.each { |line|
171
+ data.lines { |line|
172
+
175
173
  line = line.chomp
176
174
  if ip_and_port_simple_regex.match(line)
177
175
  hosts_and_ports << line
@@ -202,12 +200,12 @@ Net::SSH.start(ssh_host, app_uuid) do |ssh|
202
200
  end
203
201
  ssh.loop { true }
204
202
  end
205
-
203
+
206
204
  rescue Interrupt
207
205
  puts
208
206
  puts "Terminating..."
209
207
  exit 0
210
-
208
+
211
209
  rescue Exception => e
212
210
  puts
213
211
  puts e.message if debug
data/bin/rhc-snapshot CHANGED
@@ -1,4 +1,7 @@
1
1
  #!/usr/bin/env ruby
2
+
3
+ require 'rhc/coverage_helper'
4
+
2
5
  require 'rhc-common'
3
6
 
4
7
  #
data/bin/rhc-sshkey CHANGED
@@ -1,4 +1,7 @@
1
1
  #!/usr/bin/env ruby
2
+
3
+ require 'rhc/coverage_helper'
4
+
2
5
  require 'rhc-common'
3
6
 
4
7
  #
data/bin/rhc-tail-files CHANGED
@@ -1,4 +1,7 @@
1
1
  #!/usr/bin/env ruby
2
+
3
+ require 'rhc/coverage_helper'
4
+
2
5
  require 'rhc-common'
3
6
  require 'base64'
4
7
 
data/bin/rhc-user-info CHANGED
@@ -1,5 +1,6 @@
1
1
  #!/usr/bin/env ruby
2
2
  # print deprecation warning
3
+
3
4
  puts "Warning: This command is deprecated. Please use rhc-domain-info instead."
4
5
  puts""
5
6
 
@@ -0,0 +1,70 @@
1
+ Overview
2
+ ==============
3
+
4
+ These tests can be run against a production or OpenShift Origin instance for
5
+ verification of basic functionality. These tests should be operating system
6
+ independent and will shell out to execute the 'rhc *' commands to emulate a
7
+ user as closely as possible. These tests exercise both the commandline
8
+ client and the underlying infrastructure and serve as integration level
9
+ verification tests for the entire stack.
10
+
11
+ Usage
12
+ =============
13
+
14
+ Run from the base directory with
15
+
16
+ <env variables> bundle exec rake features
17
+
18
+ 'features' requires RHC_USERNAME+RHC_PASSWORD+RHC_NAMESPACE or
19
+ RHC_ENDPOINT to be set in the environment.
20
+
21
+ Using a proxy
22
+ --------------
23
+
24
+ You can use a proxy by setting the http_proxy environment variable. For example
25
+
26
+ http_proxy='http://proxyserver:proxyport/' bundle exec rake features
27
+
28
+ Pre-defined users
29
+ -----------------
30
+
31
+ In many cases, these tests will be run with an existing, pre-created user. The
32
+ tests should keep the resource needs of that user to a minimum, but in some
33
+ cases, the user might need to have an increased number of gears added to
34
+ support certain tests.
35
+
36
+ You use environment variables to notify the tests of the well defined user,
37
+ password and namespace. This can be done by passing the values in before the
38
+ command:
39
+
40
+ RHC_USERNAME='mylogin@example.com' RHC_PASSWORD='supersecretpassword' RHC_NAMESPACE='mynamespace' bundle exec rake features
41
+
42
+ Development Usage
43
+ =================
44
+
45
+ In development, you probably aren't going to be running against production systems.
46
+ You will most likely be running against your own OpenShift Origin system. To be
47
+ able to point to a custom system, you can configure the REST endpoint that is used.
48
+ If not specified, it will default to the OpenShift Production REST Endpoint:
49
+
50
+ RHC_ENDPOINT='https://myserver/rest/api' bundle exec rake features
51
+
52
+
53
+ Developing tests
54
+ ----------------
55
+
56
+ Often when you are developing new tests, you don't want to run the entire suite
57
+ each time. However, the tests by default automatically clean up the test
58
+ applications that were created on the previous run. You can quickly develop
59
+ and interate on a single test by doing the following:
60
+
61
+ * Run the initialization portion of the test suite
62
+
63
+ RHC_ENDPOINT='https://yourserver/rest/api' bundle exec cucumber -t @init
64
+
65
+ * Run the tests on your specific feature without reset state and using the
66
+ created username and namespace from the previous run. For example, if the
67
+ cucumber feature you wanted to test started on line 17, in your .feature file,
68
+ you would run
69
+
70
+ RHC_USERNAME=`cat /tmp/rhc/username` RHC_NAMESPACE=`cat /tmp/rhc/namespace` RHC_ENDPOINT='https://yourserver/rest/api' NO_CLEAN=1 bundle exec cucumber features/verify.feature:20
@@ -0,0 +1,124 @@
1
+ require 'tmpdir'
2
+ require 'rhc-rest'
3
+
4
+ module RHCHelper
5
+ #
6
+ # Constant Definitions
7
+ #
8
+ TEMP_DIR = File.join(Dir.tmpdir, "rhc")
9
+
10
+ #
11
+ # A class to help maintain the state from rhc calls and helper
12
+ # methods around application management.
13
+ #
14
+ class App
15
+ extend Persistable
16
+ extend Runnable
17
+ include Loggable
18
+ include Commandify
19
+ include Runnable
20
+ include Persistify
21
+ include Httpify
22
+
23
+ # attributes to represent the general information of the application
24
+ attr_accessor :name, :type, :hostname, :repo, :embed, :snapshot, :uid, :alias
25
+
26
+ # mysql connection information
27
+ attr_accessor :mysql_hostname, :mysql_user, :mysql_password, :mysql_database
28
+
29
+ # Create the data structure for a test application
30
+ def initialize(type, name)
31
+ @name, @type = name, type
32
+ @hostname = "#{name}-#{$namespace}.#{$domain}"
33
+ @repo = "#{TEMP_DIR}/#{$namespace}_#{name}_repo"
34
+ @file = "#{TEMP_DIR}/#{$namespace}.json"
35
+ @embed = []
36
+ @embed_helpers = {}
37
+ end
38
+
39
+ def self.rhc_setup
40
+ # Setup questions asked by wizard which are passed in below:
41
+ # 1 - username
42
+ # 2 - password
43
+ # 3 - upload SSH keys
44
+ # 4 - if no namespace is found, create namespace? (blank is no)
45
+ if $namespace
46
+ # Namespace is already created, so don't pass anything in
47
+ logger.info("Namespace (#{$namespace}) should be found by the wizard")
48
+ run("rhc setup", nil, [$username, $password, 'yes', ""])
49
+ else
50
+ # Pass in a blank value for namespace to create in the next step
51
+ logger.info("Skipping namespace creation")
52
+ run("rhc setup", nil, [$username, $password, 'yes', "", ""])
53
+ end
54
+ end
55
+
56
+ def self.create_unique(type, prefix="test")
57
+ # Get a REST client to verify the application name
58
+ client = Rhc::Rest::Client.new($end_point, $username, $password)
59
+
60
+ # Cleanup all test applications
61
+ test_names = []
62
+ client.domains.each do |domain|
63
+ domain.applications.each do |app|
64
+ test_names << app.name if app.name.start_with?(prefix)
65
+ end
66
+ end
67
+
68
+ loop do
69
+ # Generate a random application name
70
+ chars = ("1".."9").to_a
71
+ name = prefix + Array.new(8, '').collect{chars[rand(chars.size)]}.join
72
+
73
+ # If the test name exists, try again
74
+ next if test_names.index(name)
75
+
76
+ # Create the app
77
+ app = App.new(type, name)
78
+ app.persist
79
+ return app
80
+ end
81
+ end
82
+
83
+ def add_cartridge(name)
84
+ exitcode = 0
85
+ cartridge(name).add do |ec|
86
+ exitcode = ec
87
+ embed << name unless embed.include?(name) or ec != 0
88
+ end
89
+
90
+ persist
91
+ exitcode
92
+ end
93
+
94
+ def remove_cartridge(name)
95
+ cartridge(name).remove
96
+ embed.reject! { |s| s == name }
97
+
98
+ persist
99
+ end
100
+
101
+ def cartridge(name)
102
+ @embed_helpers[name] = RHCHelper::Cartridge.new(self, name) unless @embed_helpers.include?(name)
103
+ @embed_helpers[name]
104
+ end
105
+
106
+ def get_index_file
107
+ case @type
108
+ when "php-5.3" then "php/index.php"
109
+ when "ruby-1.8" then "config.ru"
110
+ when "python-2.6" then "wsgi/application"
111
+ when "perl-5.10" then "perl/index.pl"
112
+ when "jbossas-7" then "src/main/webapp/index.html"
113
+ when "jbosseap-6.0" then "src/main/webapp/index.html"
114
+ when "nodejs-0.6" then "index.html"
115
+ end
116
+ end
117
+
118
+ def get_mysql_file
119
+ case @type
120
+ when "php-5.3" then File.expand_path("../misc/php/db_test.php", File.expand_path(File.dirname(__FILE__)))
121
+ end
122
+ end
123
+ end
124
+ end
@@ -0,0 +1,72 @@
1
+ require 'tmpdir'
2
+ require 'rhc-rest'
3
+
4
+ module RHCHelper
5
+ #
6
+ # Constant Definitions
7
+ #
8
+ TEMP_DIR = File.join(Dir.tmpdir, "rhc")
9
+
10
+ #
11
+ # A class to help maintain the state from rhc calls and helper
12
+ # methods around cartridge management.
13
+ #
14
+ class Cartridge
15
+ extend Runnable
16
+ extend Persistable
17
+ include Loggable
18
+ include Runnable
19
+ include Httpify
20
+ include Persistify
21
+
22
+ # attributes to represent the general information of the cartridge
23
+ attr_accessor :name
24
+
25
+ # Create the data structure for a test cartridge
26
+ def initialize(app, name)
27
+ @name = name
28
+ @app_name = app.name
29
+ @hostname = "#{@app_name}-#{$namespace}.#{$domain}"
30
+ @file = "#{TEMP_DIR}/#{$namespace}.json"
31
+ end
32
+
33
+ def rhc_app_cartridge(cmd)
34
+ full_cmd = "rhc app cartridge #{cmd} -l #{$username} -p #{$password} -a #{@app_name}"
35
+ full_cmd += " -c #{@name}" if cmd != "list"
36
+ run(full_cmd, nil) do |exitstatus, out, err, arg|
37
+ yield exitstatus, out, err, arg if block_given?
38
+ end
39
+ end
40
+
41
+ def add
42
+ rhc_app_cartridge('add') do |exitstatus, out, err, arg|
43
+ yield exitstatus, out, err, arg if block_given?
44
+ end
45
+ end
46
+
47
+ def status
48
+ result = ""
49
+ rhc_app_cartridge('status') do |exitstatus, out, err, arg|
50
+ result = out
51
+ end
52
+
53
+ result
54
+ end
55
+
56
+ def start
57
+ rhc_app_cartridge('start')
58
+ end
59
+
60
+ def stop
61
+ rhc_app_cartridge('stop')
62
+ end
63
+
64
+ def restart
65
+ rhc_app_cartridge('restart')
66
+ end
67
+
68
+ def remove
69
+ rhc_app_cartridge('remove')
70
+ end
71
+ end
72
+ end