rhc 1.1.11 → 1.2.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. data/features/cartridge.feature +14 -1
  2. data/features/domain.feature +1 -1
  3. data/features/lib/rhc_helper.rb +3 -3
  4. data/features/lib/rhc_helper/app.rb +11 -3
  5. data/features/lib/rhc_helper/cartridge.rb +8 -0
  6. data/features/lib/rhc_helper/domain.rb +8 -15
  7. data/features/lib/rhc_helper/httpify.rb +11 -6
  8. data/features/lib/rhc_helper/runnable.rb +43 -7
  9. data/features/sshkey.feature +3 -4
  10. data/features/step_definitions/application_steps.rb +5 -5
  11. data/features/step_definitions/cartridge_steps.rb +12 -0
  12. data/features/step_definitions/client_steps.rb +3 -2
  13. data/features/step_definitions/sshkey_steps.rb +3 -3
  14. data/features/support/assumptions.rb +11 -11
  15. data/features/support/before_hooks.rb +23 -5
  16. data/features/support/env.rb +14 -4
  17. data/lib/rhc-common.rb +5 -2
  18. data/lib/rhc/cartridge_helpers.rb +7 -1
  19. data/lib/rhc/command_runner.rb +8 -4
  20. data/lib/rhc/commands.rb +6 -0
  21. data/lib/rhc/commands/app.rb +15 -7
  22. data/lib/rhc/commands/base.rb +3 -3
  23. data/lib/rhc/commands/cartridge.rb +78 -2
  24. data/lib/rhc/commands/port-forward.rb +137 -24
  25. data/lib/rhc/exceptions.rb +23 -8
  26. data/lib/rhc/helpers.rb +25 -4
  27. data/lib/rhc/output_helpers.rb +23 -0
  28. data/lib/rhc/rest.rb +38 -19
  29. data/lib/rhc/rest/base.rb +7 -3
  30. data/lib/rhc/rest/cartridge.rb +10 -1
  31. data/lib/rhc/usage_templates/command_help.erb +12 -12
  32. data/lib/rhc/usage_templates/command_syntax_help.erb +1 -1
  33. data/lib/rhc/usage_templates/help.erb +3 -3
  34. data/lib/rhc/usage_templates/missing_help.erb +1 -1
  35. data/lib/rhc/version.rb +1 -5
  36. data/lib/rhc/wizard.rb +4 -32
  37. data/spec/rest_spec_helper.rb +18 -4
  38. data/spec/rhc/commands/cartridge_spec.rb +91 -0
  39. data/spec/rhc/commands/domain_spec.rb +6 -2
  40. data/spec/rhc/commands/port-forward_spec.rb +95 -54
  41. data/spec/rhc/commands/snapshot_spec.rb +5 -0
  42. data/spec/rhc/rest_spec.rb +23 -2
  43. data/spec/rhc/wizard_spec.rb +9 -12
  44. data/spec/spec_helper.rb +5 -0
  45. metadata +228 -224
@@ -1,4 +1,5 @@
1
1
  Before('@clean') do
2
+ puts "Cleaning applications and keys now"
2
3
  clean_applications(true)
3
4
  end
4
5
 
@@ -8,24 +9,41 @@ Before('@sshkey') do
8
9
  end
9
10
 
10
11
  Before('@sshkey','@key1') do
11
- Given 'a new SSH key "key1.pub" is added as "key1"'
12
+ step 'a new SSH key "key1.pub" is added as "key1"'
12
13
  end
13
14
 
14
15
  # Defined the required hooks first so we make sure we have everything we need
15
16
  Before('@geared_user_required') do
17
+ $old_username = $username
16
18
  $username = "user_with_multiple_gear_sizes@test.com"
19
+ $namespace = nil
20
+ end
21
+
22
+ After do
23
+ if $old_username
24
+ $username = $old_username
25
+ $namespace = nil
26
+ $old_username = nil
27
+ $old_namespace = nil
28
+ end
29
+ end
30
+
31
+ Before('@cartridge_storage_user_required') do
32
+ $old_username = $username
33
+ $username = "user_with_extra_storage@test.com"
34
+ $namespace = nil
17
35
  end
18
36
 
19
37
  Before('@domain_required') do
20
- When 'we have an existing domain'
38
+ step 'we have an existing domain'
21
39
  end
22
40
 
23
41
  Before('@client_tools_required') do
24
- When 'we have the client tools setup'
42
+ step 'we have the client tools setup'
25
43
  end
26
44
 
27
45
  Before('@single_cartridge','@init') do
28
- When 'an existing or new php-5.3 application without an embedded cartridge'
46
+ step 'an existing or new php-5.3 application without an embedded cartridge'
29
47
  end
30
48
 
31
49
  # These assumptions help to ensure any steps that are run independently have the same state as after the @init step
@@ -38,6 +56,6 @@ end
38
56
  :multiple_cartridge => 'an existing or new php-5.3 application with embedded mysql-5.1 and phpmyadmin-3.4 cartridges',
39
57
  }.each do |tag,assumption|
40
58
  Before("@#{tag}",'~@init') do
41
- When assumption
59
+ step assumption
42
60
  end
43
61
  end
@@ -7,6 +7,7 @@ require 'rhc_helper'
7
7
  require 'rhc/rest'
8
8
  require 'rhc/config'
9
9
  require 'rhc/helpers'
10
+ require 'rhc/commands'
10
11
 
11
12
  def set_path
12
13
  ENV["PATH"] = "#{ENV['RHC_LOCAL_PATH']}:#{ENV['PATH']}" if ENV['RHC_LOCAL_PATH']
@@ -87,10 +88,13 @@ _log "\n\n"
87
88
 
88
89
  def clean_applications(leave_domain = false)
89
90
  return if ENV['NO_CLEAN']
90
- users = [$username,'user_with_multiple_gear_sizes@test.com']
91
+ users = [$username,'user_with_multiple_gear_sizes@test.com','user_with_extra_storage@test.com']
91
92
 
92
93
  _log " Cleaning up test applications..."
93
94
 
95
+ $namespace = nil unless leave_domain
96
+ $keyed_users = []
97
+
94
98
  users.each do |user|
95
99
  _log "\tUser: #{user}"
96
100
  client = RHC::Rest::Client.new($end_point, user, $password)
@@ -104,6 +108,10 @@ def clean_applications(leave_domain = false)
104
108
  end
105
109
  domain.delete unless leave_domain
106
110
  end
111
+ client.sshkeys.each do |key|
112
+ _log "\t\tKey: #{key.name}"
113
+ key.delete
114
+ end
107
115
  end
108
116
  end
109
117
 
@@ -118,6 +126,7 @@ unless ENV['NO_CLEAN']
118
126
  # Start with a clean config
119
127
  _log " Replacing express.conf with the specified libra_server"
120
128
  File.open(RHC::Config::local_config_path, 'w') {|f| f.write("libra_server=#{URI.parse($end_point).host}") }
129
+ RHC::Config.initialize
121
130
 
122
131
  # Clean up temp dir
123
132
  FileUtils.rm_rf RHCHelper::TEMP_DIR
@@ -138,6 +147,8 @@ AfterConfiguration do |config|
138
147
 
139
148
  # Modify the .ssh/config so the git and ssh commands can succeed
140
149
  keyfile = RHCHelper::Sshkey.keyfile_path('key1')
150
+ File.chmod(0600,keyfile)
151
+
141
152
  begin
142
153
  File.open('/root/.ssh/config','w',0600) do |f|
143
154
  f.puts "Host *"
@@ -147,10 +158,9 @@ AfterConfiguration do |config|
147
158
  end
148
159
  rescue Errno::ENOENT, Errno::EACCES
149
160
  end
150
- File.chmod(0600,keyfile)
151
161
 
152
162
  # Setup the logger
153
- logger = Logger.new(File.join(RHCHelper::TEMP_DIR, "cucumber.log"))
163
+ logger = Logger.new(File.join(RHCHelper::TEMP_DIR, "rhc_cucumber.log"))
154
164
  logger.level = Logger::DEBUG
155
165
  RHCHelper::Loggable.logger = logger
156
166
  $logger = logger
@@ -163,7 +173,7 @@ end
163
173
 
164
174
  After do |s|
165
175
  # Tell Cucumber to quit after this scenario is done - if it failed.
166
- Cucumber.wants_to_quit = true if s.failed?
176
+ Cucumber.wants_to_quit = true if s.failed? && !ENV['QUIET']
167
177
  end
168
178
 
169
179
  World(RHCHelper)
@@ -384,7 +384,7 @@ end
384
384
  rescue Exception => e
385
385
  puts "There was a problem communicating with the server. Response message: #{e.message}"
386
386
  puts "If you were disconnected it is possible the operation finished without being able to report success."
387
- puts "You can use 'rhc domain show' and 'rhc app status' to learn about the status of your user and application(s)."
387
+ puts "You can use 'rhc domain show' and 'rhc app show --state' to learn about the status of your user and application(s)."
388
388
  exit 219
389
389
  end
390
390
  end
@@ -965,7 +965,10 @@ def config
965
965
  end
966
966
 
967
967
  def ask_password
968
- return ask("Password: ") { |q| q.echo = '*' }
968
+ return ask("Password: ") { |q|
969
+ q.echo = '*'
970
+ q.whitespace = :chomp
971
+ }
969
972
  end
970
973
 
971
974
  def kfile_not_found
@@ -2,7 +2,7 @@ module RHC
2
2
  module CartridgeHelpers
3
3
 
4
4
  def find_cartridge(rest_obj, cartridge_name, type="embedded")
5
- carts = rest_obj.find_cartridges :regex => cart_regex(cartridge_name), :type => type
5
+ carts = find_cartridges(rest_obj, [cartridge_name], type)
6
6
 
7
7
  if carts.length == 0
8
8
  valid_carts = rest_obj.cartridges.collect { |c| c.name if c.type == type }.compact
@@ -21,6 +21,12 @@ module RHC
21
21
  carts[0]
22
22
  end
23
23
 
24
+ def find_cartridges(rest_obj, cartridge_list, type='embedded')
25
+ rest_obj.find_cartridges :regex => cartridge_list.collect { |c| cart_regex c }.join('|'), :type => type
26
+ end
27
+
28
+ private
29
+
24
30
  def cart_regex(cart)
25
31
  "^#{cart.rstrip}(-[0-9\.]+){0,1}$"
26
32
  end
@@ -66,7 +66,7 @@ module RHC
66
66
  OptionParser::InvalidArgument,
67
67
  OptionParser::MissingArgument => e
68
68
 
69
- help_bindings = CommandHelpBindings.new(active_command, commands, Commander::Runner.instance.options)
69
+ help_bindings = CommandHelpBindings.new(active_command, commands, self)
70
70
  usage = RHC::HelpFormatter.new(self).render_command_syntax(help_bindings)
71
71
  RHC::Helpers.error e.message
72
72
  say "#{usage}"
@@ -106,7 +106,7 @@ module RHC
106
106
  next
107
107
  else
108
108
  command = command(cmd)
109
- help_bindings = CommandHelpBindings.new command, commands, Commander::Runner.instance.options
109
+ help_bindings = CommandHelpBindings.new command, commands, self
110
110
  say help_formatter.render_command help_bindings
111
111
  end
112
112
  end
@@ -115,7 +115,7 @@ module RHC
115
115
  end
116
116
 
117
117
  class CommandHelpBindings
118
- def initialize(command, instance_commands, global_options)
118
+ def initialize(command, instance_commands, runner)
119
119
  @command = command
120
120
  @actions = instance_commands.collect do |command_name, command_class|
121
121
  next if command_class.summary.nil?
@@ -124,7 +124,11 @@ module RHC
124
124
  m and command_name == command_class.name ? {:name => m[1], :summary => command_class.summary || ""} : nil
125
125
  end
126
126
  @actions.compact!
127
- @global_options = global_options
127
+ @global_options = runner.options
128
+ @runner = runner
129
+ end
130
+ def program(*args)
131
+ @runner.program *args
128
132
  end
129
133
  end
130
134
  end
@@ -5,6 +5,11 @@ require 'rhc/helpers'
5
5
  # to avoid conflicts and side effects of similar short switches
6
6
  module Commander
7
7
  class Command
8
+ attr_accessor :default_action
9
+ def default_action?
10
+ default_action.present?
11
+ end
12
+
8
13
  def parse_options_and_call_procs *args
9
14
  return args if args.empty?
10
15
  opts = OptionParser.new
@@ -84,6 +89,7 @@ module RHC
84
89
  c.description = opts[:description]
85
90
  c.summary = opts[:summary]
86
91
  c.syntax = opts[:syntax]
92
+ c.default_action = opts[:default]
87
93
 
88
94
  (options_metadata = opts[:options] || []).each do |o|
89
95
  option_data = [o[:switches], o[:description]].flatten(1)
@@ -41,12 +41,15 @@ module RHC::Commands
41
41
  ).each { |s| say " #{s}" }
42
42
  end
43
43
 
44
+ raise ArgumentError, "You have named both your main application and your Jenkins application '#{name}'. In order to continue you'll need to specify a different name with --enable-jenkins or choose a different application name." if jenkins_app_name == name && enable_jenkins?
45
+
46
+ rest_app, rest_domain = nil
44
47
  raise RHC::DomainNotFoundException.new("No domains found. Please create a domain with 'rhc domain create <namespace>' before creating applications.") if rest_client.domains.empty?
45
48
 
46
49
  rest_domain = rest_client.find_domain(options.namespace)
47
50
 
48
51
  # check to make sure the right options are set for enabling jenkins
49
- jenkins_rest_app = check_jenkins(name, rest_domain) if options.enable_jenkins
52
+ jenkins_rest_app = check_jenkins(name, rest_domain) if enable_jenkins?
50
53
 
51
54
  # create the main app
52
55
  rest_app = create_app(name, cartridge, rest_domain,
@@ -55,7 +58,7 @@ module RHC::Commands
55
58
  # create a jenkins app if not available
56
59
  # don't error out if there are issues, setup warnings instead
57
60
  begin
58
- jenkins_rest_app = setup_jenkins_app(rest_domain) if options.enable_jenkins and jenkins_rest_app.nil?
61
+ jenkins_rest_app = setup_jenkins_app(rest_domain) if enable_jenkins? and jenkins_rest_app.nil?
59
62
  rescue Exception => e
60
63
  add_issue("Jenkins failed to install - #{e}",
61
64
  "Installing jenkins and jenkins-client",
@@ -119,9 +122,9 @@ module RHC::Commands
119
122
  if issues?
120
123
  output_issues(rest_app)
121
124
  else
122
- results {
123
- rest_app.messages.each { |msg| say msg }
124
- jenkins_rest_app.messages.each { |msg| say msg } if options.enable_jenkins and jenkins_rest_app
125
+ results {
126
+ rest_app.messages.each { |msg| say msg }
127
+ jenkins_rest_app.messages.each { |msg| say msg } if enable_jenkins? and jenkins_rest_app
125
128
  }
126
129
  end
127
130
 
@@ -346,7 +349,7 @@ module RHC::Commands
346
349
 
347
350
  # Now start checking for DNS
348
351
  for i in 0..MAX_RETRIES-1
349
- found = host_exists?(host)
352
+ found = host_exists?(host) || hosts_file_contains?(host)
350
353
  break if found
351
354
 
352
355
  say " retry # #{i+1} - Waiting for DNS: #{host}"
@@ -386,8 +389,13 @@ module RHC::Commands
386
389
  end
387
390
  end
388
391
 
392
+ def enable_jenkins?
393
+ # legacy issue, commander 4.0.x will place the option in the hash with nil value (BZ878407)
394
+ options.__hash__.has_key?(:enable_jenkins)
395
+ end
396
+
389
397
  def jenkins_app_name
390
- return "jenkins" if options.enable_jenkins == true or options.enable_jenkins == "true"
398
+ return "jenkins" if options.enable_jenkins == true || options.enable_jenkins == "true" || (enable_jenkins? && options.enable_jenkins.nil?)
391
399
  return options.enable_jenkins if options.enable_jenkins.is_a?(String)
392
400
  nil
393
401
  end
@@ -97,7 +97,7 @@ class RHC::Commands::Base
97
97
  @rest_client ||= begin
98
98
  username = config.username
99
99
  unless username
100
- username = ask "To connect to #{openshift_server} enter your OpenShift login (email or Red Hat login id): "
100
+ username = ask "Login to #{openshift_server}: "
101
101
  config.config_user(username)
102
102
  end
103
103
  config.password = config.password || RHC::get_password
@@ -107,8 +107,7 @@ class RHC::Commands::Base
107
107
  end
108
108
 
109
109
  def help(*args)
110
- ac = Commander::Runner.instance.active_command
111
- Commander::Runner.instance.command(:help).run(ac.name, *args)
110
+ raise ArgumentError, "Please specify an action to take"
112
111
  end
113
112
 
114
113
  def debug?
@@ -202,6 +201,7 @@ class RHC::Commands::Base
202
201
  end
203
202
 
204
203
  def self.default_action(action)
204
+ options[:default] = action unless action == :help
205
205
  define_method(:run) { |*args| send(action, *args) }
206
206
  end
207
207
 
@@ -163,11 +163,10 @@ module RHC::Commands
163
163
  end
164
164
 
165
165
  summary "Set the scaling range of a cartridge"
166
- syntax "<cartridge> [--timeout timeout] [--namespace namespace] [--app app] [--min min] [--max max]"
166
+ syntax "<cartridge> [--namespace namespace] [--app app] [--min min] [--max max]"
167
167
  argument :cart_type, "The name of the cartridge you are reloading", ["-c", "--cartridge cartridge"]
168
168
  option ["-n", "--namespace namespace"], "Namespace of the application the cartridge belongs to", :context => :namespace_context, :required => true
169
169
  option ["-a", "--app app"], "Application the cartridge belongs to", :context => :app_context, :required => true
170
- option ["--timeout timeout"], "Timeout, in seconds, for the session"
171
170
  option ["--min min", Integer], "Minimum scaling value"
172
171
  option ["--max max", Integer], "Maximum scaling value"
173
172
  def scale(cartridge)
@@ -192,6 +191,83 @@ module RHC::Commands
192
191
  0
193
192
  end
194
193
 
194
+ =begin
195
+ # Commenting this out for US2438
196
+ summary 'View/manipulate storage on a cartridge'
197
+ syntax '<cartridge> -a app [--show] [--add|--remove|--set amount] [--namespace namespace]'
198
+ argument :cart_type, "The name of the cartridge", ["-c", "--cartridge cart_type"], :arg_type => :list
199
+ option ["-n", "--namespace namespace"], "Namespace of the application the cartridge belongs to", :context => :namespace_context, :required => true
200
+ option ["-a", "--app app"], "Application the cartridge belongs to", :context => :app_context, :required => true
201
+ option ["--show"], "Show the current base and additional storage capacity"
202
+ option ["--add amount"], "Add the indicated amount to the additional storage capacity"
203
+ option ["--remove amount"], "Remove the indicated amount from the additional storage capacity"
204
+ option ["--set amount"], "Set the specified amount of additional storage capacity"
205
+ option ["-f", "--force"], "Force the action"
206
+ def storage(cartridges)
207
+ # Make sure that we are dealing with an array (-c param will only pass in a string)
208
+ # BZ 883658
209
+ cartridges = [cartridges].flatten
210
+
211
+ rest_domain = rest_client.find_domain(options.namespace)
212
+ rest_app = rest_domain.find_application(options.app)
213
+
214
+ # Pull the desired action
215
+ #
216
+ actions = options.__hash__.keys & [:show, :add, :remove, :set]
217
+
218
+ # Ensure that only zero or one action was selected
219
+ raise RHC::AdditionalStorageArgumentsException if actions.length > 1
220
+
221
+ operation = actions.first || :show
222
+ amount = options.__hash__[operation]
223
+
224
+ # Perform a storage change action if requested
225
+ if operation == :show
226
+ results do
227
+ if cartridges.length == 0
228
+ display_cart_storage_list rest_app.cartridges
229
+ else
230
+ cartridges.each do |cartridge_name|
231
+ cart = rest_app.find_cartridge(cartridge_name)
232
+ display_cart_storage_info cart, cart.display_name
233
+ end
234
+ end
235
+ end
236
+ else
237
+ raise RHC::MultipleCartridgesException,
238
+ 'Exactly one cartridge must be specified for this operation' if cartridges.length != 1
239
+
240
+ rest_cartridge = find_cartridge rest_app, cartridges.first, nil
241
+ amount = amount.match(/^(\d+)(GB)?$/i)
242
+ raise RHC::AdditionalStorageValueException if amount.nil?
243
+
244
+ # If the amount is specified, find the regex match and convert to a number
245
+ amount = amount[1].to_i
246
+ total_amount = rest_cartridge.additional_gear_storage
247
+
248
+ if operation == :add
249
+ total_amount += amount
250
+ elsif operation == :remove
251
+ if amount > total_amount && !options.force
252
+ raise RHC::AdditionalStorageRemoveException
253
+ else
254
+ total_amount = [total_amount - amount, 0].max
255
+ end
256
+ else
257
+ total_amount = amount
258
+ end
259
+
260
+ cart = rest_cartridge.set_storage(:additional_gear_storage => total_amount)
261
+ results do
262
+ say "Success: additional storage space set to #{total_amount}GB\n"
263
+ display_cart_storage_info cart
264
+ end
265
+ end
266
+
267
+ 0
268
+ end
269
+ =end
270
+
195
271
  private
196
272
  include RHC::CartridgeHelpers
197
273
 
@@ -2,11 +2,74 @@ require 'rhc/commands/base'
2
2
  require 'uri'
3
3
 
4
4
  module RHC::Commands
5
+ class ForwardingSpec
6
+ include RHC::Helpers
7
+ include Enumerable
8
+ # class to represent how SSH port forwarding should be performed
9
+ attr_accessor :port_from
10
+ attr_reader :remote_host, :port_to, :host_from, :service
11
+ attr_writer :bound
12
+
13
+ def initialize(service, remote_host, port_to, port_from = nil)
14
+ @service = service
15
+ @remote_host = remote_host
16
+ @port_to = port_to
17
+ @host_from = mac? ? "localhost" : remote_host # forward locally on a Mac
18
+ @port_from = port_from || port_to # match ports if possible
19
+ @bound = false
20
+ end
21
+
22
+ def to_cmd_arg
23
+ # string to be used in a direct SSH command
24
+ mac? ? "-L #{port_from}:#{remote_host}:#{port_to}" : "-L #{remote_host}:#{port_from}:#{remote_host}:#{port_to}"
25
+ end
26
+
27
+ def to_fwd_args
28
+ # array of arguments to be passed to Net::SSH::Service::Forward#local
29
+ args = [port_from.to_i, remote_host, port_to.to_i]
30
+ args.unshift(remote_host) unless mac?
31
+ args
32
+ end
33
+
34
+ def bound?
35
+ @bound
36
+ end
37
+
38
+ # :nocov: These are for sorting. No need to test for coverage.
39
+ def <=>(other)
40
+ if bound? && !other.bound?
41
+ -1
42
+ elsif !bound? && other.bound?
43
+ 1
44
+ else
45
+ order_by_attrs(other, :service, :remote_host, :port_from)
46
+ end
47
+ end
48
+
49
+ def order_by_attrs(other, *attrs)
50
+ # compare self and "other" by examining their "attrs" in order
51
+ # attrs should be an array of symbols to which self and "other"
52
+ # respond when sent.
53
+ while attribute = attrs.shift do
54
+ if self.send(attribute) != other.send(attribute)
55
+ return self.send(attribute) <=> other.send(attribute)
56
+ end
57
+ end
58
+ 0
59
+ end
60
+ # :nocov:
61
+
62
+ private :order_by_attrs
63
+ end
64
+
5
65
  class PortForward < Base
6
66
 
7
- IP_AND_PORT = /[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\:[0-9]{1,5}/
67
+ UP_TO_256 = /25[0-5]|2[0-4][0-9]|[01]?(?:[0-9][0-9]?)/
68
+ UP_TO_65535 = /6553[0-5]|655[0-2][0-9]|65[0-4][0-9][0-9]|6[0-4][0-9][0-9][0-9]|[0-5]?(?:[0-9][0-9]{0,3})/
69
+ IP_AND_PORT = /\b(#{UP_TO_256}(?:\.#{UP_TO_256}){3})\:(#{UP_TO_65535})\b/
8
70
 
9
71
  summary "Forward remote ports to the workstation"
72
+ syntax "<application>"
10
73
  option ["-n", "--namespace namespace"], "Namespace of the application you are port forwarding to", :context => :namespace_context, :required => true
11
74
  argument :app, "Application you are port forwarding to (required)", ["-a", "--app app"]
12
75
  def run(app)
@@ -14,45 +77,91 @@ module RHC::Commands
14
77
  rest_domain = rest_client.find_domain options.namespace
15
78
  rest_app = rest_domain.find_application app
16
79
 
17
- raise RHC::ScaledApplicationsNotSupportedException.new "This utility does not currently support scaled applications. You will need to set up port forwarding manually." if (rest_app.embedded.keys.any?{ |k| k =~ /\Ahaproxy/ })
18
-
19
80
  ssh_uri = URI.parse(rest_app.ssh_url)
20
81
  say "Using #{rest_app.ssh_url}..." if options.debug
21
82
 
22
- hosts_and_ports = []
23
- hosts_and_ports_descriptions = []
83
+ forwarding_specs = []
24
84
 
25
85
  begin
26
-
27
86
  say "Checking available ports..."
28
87
 
29
88
  Net::SSH.start(ssh_uri.host, ssh_uri.user) do |ssh|
30
-
31
89
  ssh.exec! "rhc-list-ports" do |channel, stream, data|
32
90
  if stream == :stderr
33
91
  data.each_line do |line|
34
92
  line.chomp!
93
+ # FIXME: This is really brittle; there must be a better way
94
+ # for the server to tell us that permission (what permission?)
95
+ # is denied.
35
96
  raise RHC::PermissionDeniedException.new "Permission denied." if line =~ /permission denied/i
36
- hosts_and_ports_descriptions << line if line.index(IP_AND_PORT)
97
+ # ...and also which services are available for the application
98
+ # for us to forward ports for.
99
+ if line =~ /\A\s*(\S+) -> #{IP_AND_PORT}/
100
+ debug fs = ForwardingSpec.new($1, $2, $3.to_i)
101
+ forwarding_specs << fs
102
+ else
103
+ debug line
104
+ end
105
+
37
106
  end
107
+ end
108
+ end
109
+
110
+ if forwarding_specs.length == 0
111
+ # check if the gears have been stopped
112
+ ggs = rest_app.gear_groups
113
+ if ggs.any? { |gg|
114
+ gears = gg.gears
115
+ true if gears.any? { |g| g["state"] == "stopped" }
116
+ }
117
+ warn "Application #{rest_app.name} is stopped. Please restart the application and try again."
118
+ return 1
38
119
  else
39
- data.each_line do |line|
40
- line.chomp!
41
- hosts_and_ports << line if ((not line =~ /scale/i) and IP_AND_PORT.match(line))
42
- end
120
+ raise RHC::NoPortsToForwardException.new "There are no available ports to forward for this application. Your application may be stopped."
43
121
  end
44
122
  end
45
123
 
46
- raise RHC::NoPortsToForwardException.new "There are no available ports to forward for this application. Your application may be stopped." if hosts_and_ports.length == 0
47
-
48
- hosts_and_ports_descriptions.each { |description| say "Binding #{description}..." }
49
-
50
124
  begin
51
125
  Net::SSH.start(ssh_uri.host, ssh_uri.user) do |ssh|
52
- say "Forwarding ports, use ctl + c to stop"
53
- hosts_and_ports.each do |host_and_port|
54
- host, port = host_and_port.split(/:/)
55
- ssh.forward.local(host, port.to_i, host, port.to_i)
126
+ say "Forwarding ports"
127
+ forwarding_specs.each do |fs|
128
+ given_up = nil
129
+ while !fs.bound? && !given_up
130
+ begin
131
+ args = fs.to_fwd_args
132
+ debug args.inspect
133
+ ssh.forward.local(*args)
134
+ fs.bound = true
135
+ rescue Errno::EADDRINUSE
136
+ debug "trying local port #{fs.port_from}"
137
+ fs.port_from += 1
138
+ rescue Timeout::Error, Errno::EADDRNOTAVAIL, Errno::EHOSTUNREACH, Errno::ECONNREFUSED, Net::SSH::AuthenticationFailed => e
139
+ given_up = true
140
+ end
141
+ end
142
+ end
143
+
144
+ bound_ports = forwarding_specs.select(&:bound?)
145
+ if bound_ports.length > 0
146
+ items = bound_ports.map do |fs|
147
+ [fs.service, "#{fs.host_from}:#{fs.port_from}", " => ", "#{fs.remote_host}:#{fs.port_to.to_s}"]
148
+ end
149
+ table(items, :header => ["Service", "Connect to", " ", "Forward to"]).each { |s| success " #{s}" }
150
+ end
151
+
152
+ # for failed port forwarding attempts
153
+ failed_port_forwards = forwarding_specs.select { |fs| !fs.bound? }
154
+ if failed_port_forwards.length > 0
155
+ ssh_cmd_arg = failed_port_forwards.map { |fs| fs.to_cmd_arg }.join(" ")
156
+ ssh_cmd = "ssh -N #{ssh_cmd_arg} #{ssh_uri.user}@#{ssh_uri.host}"
157
+ warn "Error forwarding some port(s). You can try to forward manually by running:\n#{ssh_cmd}"
158
+ else
159
+ say "Press CTRL-C to terminate port forwarding"
160
+ end
161
+
162
+ unless forwarding_specs.any?(&:bound?)
163
+ warn "No ports have been bound"
164
+ return
56
165
  end
57
166
  ssh.loop { true }
58
167
  end
@@ -64,13 +173,17 @@ module RHC::Commands
64
173
  end
65
174
 
66
175
  rescue Timeout::Error, Errno::EADDRNOTAVAIL, Errno::EADDRINUSE, Errno::EHOSTUNREACH, Errno::ECONNREFUSED, Net::SSH::AuthenticationFailed => e
67
- ssh_cmd = "ssh -N "
68
- hosts_and_ports.each { |port| ssh_cmd << "-L #{port}:#{port} " }
69
- ssh_cmd << "#{ssh_uri.user}@#{ssh_uri.host}"
70
- raise RHC::PortForwardFailedException.new("#{e.message if options.debug}\nError trying to forward ports. You can try to forward manually by running:\n" + ssh_cmd)
176
+ ssh_cmd = ["ssh","-N"]
177
+ unbound_fs = forwarding_specs.select { |fs| !fs.bound? }
178
+ ssh_cmd += unbound_fs.map { |fs| fs.to_cmd_arg }
179
+ ssh_cmd += ["#{ssh_uri.user}@#{ssh_uri.host}"]
180
+ raise RHC::PortForwardFailedException.new("#{e.message + "\n" if options.debug}Error trying to forward ports. You can try to forward manually by running:\n" + ssh_cmd.join(" "))
71
181
  end
72
182
 
73
183
  return 0
184
+ rescue RestClient::Exception => e
185
+ error "Connection to #{openshift_server} failed: #{e.message}"
186
+ return 1
74
187
  end
75
188
  end
76
189
  end