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.
- data/features/cartridge.feature +14 -1
- data/features/domain.feature +1 -1
- data/features/lib/rhc_helper.rb +3 -3
- data/features/lib/rhc_helper/app.rb +11 -3
- data/features/lib/rhc_helper/cartridge.rb +8 -0
- data/features/lib/rhc_helper/domain.rb +8 -15
- data/features/lib/rhc_helper/httpify.rb +11 -6
- data/features/lib/rhc_helper/runnable.rb +43 -7
- data/features/sshkey.feature +3 -4
- data/features/step_definitions/application_steps.rb +5 -5
- data/features/step_definitions/cartridge_steps.rb +12 -0
- data/features/step_definitions/client_steps.rb +3 -2
- data/features/step_definitions/sshkey_steps.rb +3 -3
- data/features/support/assumptions.rb +11 -11
- data/features/support/before_hooks.rb +23 -5
- data/features/support/env.rb +14 -4
- data/lib/rhc-common.rb +5 -2
- data/lib/rhc/cartridge_helpers.rb +7 -1
- data/lib/rhc/command_runner.rb +8 -4
- data/lib/rhc/commands.rb +6 -0
- data/lib/rhc/commands/app.rb +15 -7
- data/lib/rhc/commands/base.rb +3 -3
- data/lib/rhc/commands/cartridge.rb +78 -2
- data/lib/rhc/commands/port-forward.rb +137 -24
- data/lib/rhc/exceptions.rb +23 -8
- data/lib/rhc/helpers.rb +25 -4
- data/lib/rhc/output_helpers.rb +23 -0
- data/lib/rhc/rest.rb +38 -19
- data/lib/rhc/rest/base.rb +7 -3
- data/lib/rhc/rest/cartridge.rb +10 -1
- data/lib/rhc/usage_templates/command_help.erb +12 -12
- data/lib/rhc/usage_templates/command_syntax_help.erb +1 -1
- data/lib/rhc/usage_templates/help.erb +3 -3
- data/lib/rhc/usage_templates/missing_help.erb +1 -1
- data/lib/rhc/version.rb +1 -5
- data/lib/rhc/wizard.rb +4 -32
- data/spec/rest_spec_helper.rb +18 -4
- data/spec/rhc/commands/cartridge_spec.rb +91 -0
- data/spec/rhc/commands/domain_spec.rb +6 -2
- data/spec/rhc/commands/port-forward_spec.rb +95 -54
- data/spec/rhc/commands/snapshot_spec.rb +5 -0
- data/spec/rhc/rest_spec.rb +23 -2
- data/spec/rhc/wizard_spec.rb +9 -12
- data/spec/spec_helper.rb +5 -0
- 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
|
-
|
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
|
-
|
38
|
+
step 'we have an existing domain'
|
21
39
|
end
|
22
40
|
|
23
41
|
Before('@client_tools_required') do
|
24
|
-
|
42
|
+
step 'we have the client tools setup'
|
25
43
|
end
|
26
44
|
|
27
45
|
Before('@single_cartridge','@init') do
|
28
|
-
|
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
|
-
|
59
|
+
step assumption
|
42
60
|
end
|
43
61
|
end
|
data/features/support/env.rb
CHANGED
@@ -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, "
|
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)
|
data/lib/rhc-common.rb
CHANGED
@@ -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
|
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|
|
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
|
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
|
data/lib/rhc/command_runner.rb
CHANGED
@@ -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,
|
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,
|
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,
|
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 =
|
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
|
data/lib/rhc/commands.rb
CHANGED
@@ -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)
|
data/lib/rhc/commands/app.rb
CHANGED
@@ -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
|
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
|
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
|
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
|
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
|
data/lib/rhc/commands/base.rb
CHANGED
@@ -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 "
|
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
|
-
|
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> [--
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
53
|
-
|
54
|
-
|
55
|
-
|
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
|
68
|
-
|
69
|
-
ssh_cmd
|
70
|
-
|
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
|