rhc 0.92.11 → 0.93.18

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 (55) hide show
  1. data/README.md +1 -9
  2. data/Rakefile +1 -55
  3. data/bin/rhc +31 -1
  4. data/bin/rhc-app +62 -127
  5. data/bin/rhc-chk +6 -7
  6. data/bin/rhc-create-app +8 -8
  7. data/bin/rhc-create-domain +6 -86
  8. data/bin/rhc-ctl-app +3 -3
  9. data/bin/rhc-ctl-domain +4 -4
  10. data/bin/rhc-domain +16 -18
  11. data/bin/rhc-domain-info +3 -3
  12. data/bin/rhc-port-forward +127 -24
  13. data/bin/rhc-snapshot +7 -46
  14. data/bin/rhc-sshkey +13 -79
  15. data/bin/rhc-tail-files +16 -8
  16. data/lib/rhc-common.rb +406 -230
  17. data/lib/rhc-rest.rb +3 -3
  18. data/lib/rhc-rest/client.rb +1 -1
  19. data/lib/rhc-rest/domain.rb +1 -1
  20. data/lib/rhc.rb +20 -0
  21. data/lib/rhc/cli.rb +38 -0
  22. data/lib/rhc/client.rb +15 -0
  23. data/lib/rhc/commands.rb +32 -0
  24. data/lib/rhc/commands/base.rb +67 -0
  25. data/lib/rhc/commands/server.rb +24 -0
  26. data/lib/rhc/config.rb +141 -0
  27. data/lib/rhc/core_ext.rb +15 -0
  28. data/lib/rhc/helpers.rb +142 -0
  29. data/lib/rhc/json.rb +52 -0
  30. data/lib/rhc/targz.rb +46 -0
  31. data/lib/rhc/vendor/okjson.rb +600 -0
  32. data/lib/rhc/vendor/zliby.rb +628 -0
  33. data/lib/rhc/wizard.rb +579 -0
  34. data/spec/rhc/assets/foo.txt +1 -0
  35. data/spec/rhc/assets/targz_corrupted.tar.gz +1 -0
  36. data/spec/rhc/assets/targz_sample.tar.gz +0 -0
  37. data/spec/rhc/cli_spec.rb +24 -0
  38. data/spec/rhc/command_spec.rb +88 -0
  39. data/spec/rhc/commands/server_spec.rb +39 -0
  40. data/spec/rhc/helpers_spec.rb +171 -0
  41. data/spec/rhc/json_spec.rb +30 -0
  42. data/spec/rhc/targz_spec.rb +42 -0
  43. data/spec/rhc/wizard_spec.rb +426 -0
  44. data/spec/spec_helper.rb +192 -0
  45. data/test/functional/application_test.rb +71 -0
  46. data/test/functional/domain_test.rb +123 -0
  47. data/test/functional/test_credentials.rb +5 -0
  48. data/test/sample-usage.rb +122 -0
  49. data/test/support/server.rb +14 -0
  50. data/test/support/testcase.rb +3 -0
  51. data/test/test_helper.rb +4 -0
  52. data/test/unit/command_test.rb +19 -0
  53. metadata +181 -29
  54. data/ext/mkrf_conf.rb +0 -58
  55. data/lib/rhc +0 -115
data/bin/rhc-snapshot CHANGED
@@ -46,13 +46,8 @@ rescue Exception => e
46
46
  p_usage
47
47
  end
48
48
 
49
- # check for gnutar - gnu tar is called tar on linux and cygwin but gnutar on
50
- # BSD based systems like MacOS X
51
- TAR_BIN = 'tar'
52
- TAR_BIN = '/usr/bin/gnutar' if File.executable?('/usr/bin/gnutar')
53
-
54
49
  # If provided a config path, check it
55
- check_cpath(opt)
50
+ RHC::Config.check_cpath(opt)
56
51
 
57
52
  # Pull in configs from files
58
53
  libra_server = get_var('libra_server')
@@ -80,7 +75,7 @@ if !password
80
75
  password = RHC::get_password
81
76
  end
82
77
 
83
- user_info = RHC::get_user_info(libra_server, opt['rhlogin'], password, @http, debug, false)
78
+ user_info = RHC::get_user_info(libra_server, opt['rhlogin'], password, RHC::Config.default_proxy, debug, false)
84
79
 
85
80
  app = opt['app']
86
81
  opt['save'] = "#{opt['app']}.tar.gz" unless opt['save'] || opt['restore']
@@ -96,47 +91,13 @@ end
96
91
  app_uuid = user_info['app_info'][app]['uuid']
97
92
  namespace = user_info['user_info']['domains'][0]['namespace']
98
93
  rhc_domain = user_info['user_info']['rhc_domain']
99
- if opt['save']
100
- ssh_cmd = "ssh #{app_uuid}@#{app}-#{namespace}.#{rhc_domain} 'snapshot' > #{opt['save']}"
101
- puts "Pulling down a snapshot to #{opt['save']}"
102
- else
103
- if File.exists? opt['restore']
104
- `#{TAR_BIN} --wildcards -tf #{opt['restore']} './*/#{app}'`
105
- if $?.exitstatus != 0
106
- puts "Archive at #{opt['restore']} does not contain the target application: ./*/#{app}"
107
- puts "If you created this archive rather than exported with rhc-snapshot, be sure"
108
- puts "the directory structure inside the archive starts with ./<app_uuid>/"
109
- puts "i.e.: tar -czvf <app_name>.tar.gz ./<app_uuid>/"
110
- exit 255
111
- else
112
- `#{TAR_BIN} --wildcards -tf #{opt['restore']} './*/git'`
113
- include_git = $?.exitstatus == 0
114
- ssh_cmd = "cat #{opt['restore']} | ssh #{app_uuid}@#{app}-#{namespace}.#{rhc_domain} 'restore#{include_git ? ' INCLUDE_GIT' : ''}'"
115
- puts "Restoring from snapshot #{opt['restore']}"
116
- end
117
- else
118
- puts "Archive not found: #{opt['restore']}"
119
- exit 255
120
- end
121
- end
122
-
123
94
 
124
- puts
125
- puts ssh_cmd if debug
126
- output = `#{ssh_cmd}`
127
- puts
128
- if $?.exitstatus != 0
129
- puts output
130
- puts
131
95
  if opt['save']
132
- puts "Error in trying to save snapshot. You can try to save manually by running:"
96
+ status = RHC.snapshot_create rhc_domain, namespace, app, app_uuid, opt['save'], debug
97
+ exit status if ! status
133
98
  else
134
- puts "Error in trying to restore snapshot. You can try to restore manually by running:"
99
+ status = RHC.snapshot_restore rhc_domain, namespace, app, app_uuid, opt['restore'], debug
100
+ exit status if ! status
135
101
  end
136
- puts
137
- puts ssh_cmd
138
- puts
139
- exit 1
140
- end
141
- puts output if opt['restore'] && debug
102
+
142
103
  exit 0
data/bin/rhc-sshkey CHANGED
@@ -34,7 +34,7 @@ end
34
34
 
35
35
  def validate_args(val_id=true)
36
36
  # If provided a config path, check it
37
- check_cpath($opt)
37
+ RHC::Config.check_cpath($opt)
38
38
 
39
39
  # Pull in configs from files
40
40
  $libra_server = get_var('libra_server')
@@ -53,52 +53,6 @@ def validate_args(val_id=true)
53
53
  $password = $opt['password'] ? $opt['password'] : RHC::get_password
54
54
  end
55
55
 
56
- def add_or_update_key(command)
57
- validate_args(true)
58
-
59
- # Read user public ssh key
60
- if $opt['ssh']
61
- if File.readable?($opt['ssh'])
62
- begin
63
- ssh_keyfile_contents = File.open($opt['ssh']).gets.chomp.split(' ')
64
- ssh_key = ssh_keyfile_contents[1]
65
- ssh_key_type = ssh_keyfile_contents[0]
66
- rescue Exception => e
67
- puts "Invalid public keyfile format! Please specify a valid user public keyfile."
68
- exit 1
69
- end
70
- else
71
- puts "Unable to read user public keyfile #{$opt['ssh']}"
72
- exit 1
73
- end
74
- else # create key
75
- key_name = $opt['identifier']
76
- puts "Generating ssh key pair for user '#{key_name}' in the dir '#{Dir.pwd}/'"
77
- # Use system for interaction
78
- system("ssh-keygen -t rsa -f '#{key_name}'")
79
- ssh_pub_key_file = Dir.pwd + '/' + key_name + '.pub'
80
- ssh_keyfile_contents = File.open(ssh_pub_key_file).gets.chomp.split(' ')
81
- ssh_key = ssh_keyfile_contents[1]
82
- ssh_key_type = ssh_keyfile_contents[0]
83
- end
84
-
85
- data = {}
86
- data[:rhlogin] = $opt['rhlogin']
87
- data[:key_name] = $opt['identifier']
88
- data[:ssh] = ssh_key
89
- data[:action] = 'add-key'
90
- data[:key_type] = ssh_key_type
91
-
92
- if command == 'add'
93
- data[:action] = 'add-key'
94
- elsif command == 'update'
95
- data[:action] = 'update-key'
96
- end
97
-
98
- url = URI.parse("https://#{$libra_server}/broker/ssh_keys")
99
- handle_key_mgmt_response(url, data, $password)
100
- end
101
-
102
56
  def remove_key
103
57
  validate_args(true)
104
58
 
@@ -114,7 +68,7 @@ end
114
68
  def show_key_list
115
69
  validate_args(false)
116
70
 
117
- ssh_keys = RHC::get_ssh_keys($libra_server, $opt['rhlogin'], $password, @http)
71
+ ssh_keys = RHC::get_ssh_keys($libra_server, $opt['rhlogin'], $password, RHC::Config.default_proxy)
118
72
  additional_ssh_keys = ssh_keys['keys']
119
73
 
120
74
  puts ""
@@ -122,46 +76,24 @@ def show_key_list
122
76
  puts "========"
123
77
 
124
78
  # first list the primary key
125
- puts "Name: default"
126
- puts "Type: #{ssh_keys['ssh_type']}"
127
- puts " Key: #{ssh_keys['ssh_key']}"
79
+ puts " Name: default"
80
+ puts " Type: #{ssh_keys['ssh_type']}"
81
+ puts "Fingerprint: #{ssh_keys['fingerprint']}"
82
+ #puts " Key: #{ssh_keys['ssh_key']}"
128
83
  puts ""
129
84
 
130
85
  # now list the additional keys
131
86
  if additional_ssh_keys && additional_ssh_keys.kind_of?(Hash)
132
87
  additional_ssh_keys.each do |name, keyval|
133
- puts "Name: #{name}"
134
- puts "Type: #{keyval['type']}"
135
- puts " Key: #{keyval['key']}"
88
+ puts " Name: #{name}"
89
+ puts " Type: #{keyval['type']}"
90
+ puts "Fingerprint: #{keyval['fingerprint']}"
91
+ #puts " Key: #{keyval['key']}"
136
92
  puts ""
137
93
  end
138
94
  end
139
95
  end
140
96
 
141
- def handle_key_mgmt_response(url, data, password)
142
- RHC::print_post_data(data)
143
- json_data = RHC::generate_json(data)
144
-
145
- response = RHC::http_post(@http, url, json_data, password)
146
-
147
- if response.code == '200'
148
- begin
149
- json_resp = JSON.parse(response.body)
150
- RHC::update_server_api_v(json_resp)
151
- RHC::print_response_success(json_resp)
152
- puts "Success"
153
- exit 0
154
- rescue JSON::ParserError
155
- RHC::print_response_err(response)
156
- end
157
- else
158
- RHC::print_response_err(response)
159
- end
160
- puts "Failure"
161
- exit 1
162
- end
163
-
164
-
165
97
  begin
166
98
  argv_c = ARGV.clone
167
99
 
@@ -223,7 +155,9 @@ p_usage 0 if $opt["help"]
223
155
 
224
156
  case argv_c[0]
225
157
  when "add", "update"
226
- add_or_update_key(argv_c[0])
158
+ validate_args(true)
159
+ add_or_update_key(argv_c[0], $opt['identifier'], $opt['ssh'],
160
+ $opt['rhlogin'], $password)
227
161
  when "remove"
228
162
  remove_key
229
163
  when "list", nil
data/bin/rhc-tail-files CHANGED
@@ -48,7 +48,7 @@ rescue Exception => e
48
48
  end
49
49
 
50
50
  # If provided a config path, check it
51
- check_cpath(opt)
51
+ RHC::Config.check_cpath(opt)
52
52
 
53
53
  # Pull in configs from files
54
54
  libra_server = get_var('libra_server')
@@ -76,7 +76,7 @@ if !password
76
76
  password = RHC::get_password
77
77
  end
78
78
 
79
- user_info = RHC::get_user_info(libra_server, opt['rhlogin'], password, @http, false)
79
+ user_info = RHC::get_user_info(libra_server, opt['rhlogin'], password, RHC::Config.default_proxy, false)
80
80
 
81
81
  app = opt['app']
82
82
 
@@ -98,21 +98,29 @@ rhc_domain = user_info['user_info']['rhc_domain']
98
98
  # Red Hat OpenShift: https://bugzilla.redhat.com/show_bug.cgi?id=726646
99
99
  # OpenSSH https://bugzilla.mindrot.org/show_bug.cgi?id=396
100
100
 
101
- ssh_cmd = "ssh -t #{app_uuid}@#{app}-#{namespace}.#{rhc_domain} 'tail#{opt['opts'] ? ' --opts ' + Base64::encode64(opt['opts']).chomp : ''} #{file_glob}'"
101
+ remote_cmd = "tail#{opt['opts'] ? ' --opts ' + Base64::encode64(opt['opts']).chomp : ''} #{file_glob}"
102
+ ssh_cmd = "ssh -t #{app_uuid}@#{app}-#{namespace}.#{rhc_domain} '#{remote_cmd}'"
102
103
 
103
104
  puts "Attempting to tail files: #{file_glob}"
104
105
  puts "Use ctl + c to stop"
105
- puts
106
106
  puts ssh_cmd if debug
107
107
  begin
108
108
  if opt['files'] == 'system-messages'
109
- RHC::ctl_app(libra_server, @http, opt['app'], opt['rhlogin'], password, 'system-messages', false, nil, nil)
109
+ RHC::ctl_app(libra_server, RHC::Config.default_proxy, opt['app'], opt['rhlogin'], password,
110
+ 'system-messages', false, nil, nil)
110
111
  else
111
- exec ssh_cmd
112
+ ssh_ruby("#{app}-#{namespace}.#{rhc_domain}", app_uuid, remote_cmd)
112
113
  end
113
- rescue SystemCallError
114
+ rescue Interrupt
115
+ puts
116
+ puts "Terminating..."
117
+ exit 0
118
+ rescue SocketError => e
119
+ puts
120
+ puts "Could not connect: #{e.message}"
114
121
  puts
115
- puts "Error in trying to tail files. You can tail manually by running:"
122
+ puts "DEBUG: #{e.debug}" if debug
123
+ puts "You can try to run this manually if you have ssh installed: "
116
124
  puts
117
125
  puts ssh_cmd
118
126
  puts
data/lib/rhc-common.rb CHANGED
@@ -1,15 +1,20 @@
1
1
  require 'rubygems'
2
2
  require 'fileutils'
3
3
  require 'getoptlong'
4
- require 'json'
5
4
  require 'net/http'
6
5
  require 'net/https'
7
- #require 'net/ssh'
8
- require 'open3'
6
+ require 'net/ssh'
7
+ require 'sshkey'
9
8
  require 'parseconfig'
10
9
  require 'resolv'
11
10
  require 'uri'
11
+ require 'highline/import'
12
12
  require 'rhc-rest'
13
+ require 'rhc/helpers'
14
+ require 'rhc/config'
15
+ require 'rhc/wizard'
16
+ require 'rhc/targz'
17
+ require 'rhc/json'
13
18
 
14
19
  module RHC
15
20
 
@@ -68,7 +73,7 @@ module RHC
68
73
 
69
74
  def self.debug(bool)
70
75
  @mydebug = bool
71
- end
76
+ end
72
77
 
73
78
  def self.update_server_api_v(dict)
74
79
  if !dict['api'].nil? && (dict['api'] =~ PATTERN_VERSION)
@@ -87,10 +92,18 @@ module RHC
87
92
  def self.delay(time, adj=DEFAULT_DELAY)
88
93
  (time*=adj).to_int
89
94
  end
95
+
96
+ def self.json_encode(data)
97
+ RHC::Json.encode(data)
98
+ end
99
+
100
+ def self.json_decode(json)
101
+ RHC::Json.decode(json)
102
+ end
90
103
 
91
104
  def self.generate_json(data)
92
105
  data['api'] = API
93
- json = JSON.generate(data)
106
+ json = json_encode(data)
94
107
  json
95
108
  end
96
109
 
@@ -111,8 +124,8 @@ module RHC
111
124
  return []
112
125
  end
113
126
  begin
114
- json_resp = JSON.parse(response.body)
115
- rescue JSON::ParserError
127
+ json_resp = json_decode(response.body)
128
+ rescue RHC::JsonError
116
129
  exit 1
117
130
  end
118
131
  update_server_api_v(json_resp)
@@ -120,8 +133,8 @@ module RHC
120
133
  print_response_success(json_resp)
121
134
  end
122
135
  begin
123
- carts = (JSON.parse(json_resp['data']))['carts']
124
- rescue JSON::ParserError
136
+ carts = (json_decode(json_resp['data']))['carts']
137
+ rescue RHC::JsonError
125
138
  exit 1
126
139
  end
127
140
  carts
@@ -156,8 +169,8 @@ module RHC
156
169
  end
157
170
 
158
171
  def self.check_key(keyname)
159
- check_field(keyname, 'key name', DEFAULT_MAX_LENGTH, /[^0-9_a-zA-Z]/,
160
- 'contains invalid characters! Only alpha-numeric characters and underscores allowed.')
172
+ check_field(keyname, 'key name', DEFAULT_MAX_LENGTH, /[^0-9a-zA-Z]/,
173
+ 'contains invalid characters! Only alpha-numeric characters allowed.')
161
174
  end
162
175
 
163
176
  def self.check_field(field, type, max=0, val_regex=/[^0-9a-zA-Z]/,
@@ -175,7 +188,7 @@ module RHC
175
188
  puts "#{type} is required"
176
189
  return false
177
190
  end
178
- true
191
+ field
179
192
  end
180
193
 
181
194
  def self.print_post_data(h)
@@ -223,8 +236,8 @@ module RHC
223
236
  exit 1
224
237
  end
225
238
  begin
226
- json_resp = JSON.parse(response.body)
227
- rescue JSON::ParserError
239
+ json_resp = json_decode(response.body)
240
+ rescue RHC::JsonError
228
241
  exit 1
229
242
  end
230
243
  update_server_api_v(json_resp)
@@ -232,13 +245,34 @@ module RHC
232
245
  print_response_success(json_resp)
233
246
  end
234
247
  begin
235
- user_info = JSON.parse(json_resp['data'].to_s)
236
- rescue JSON::ParserError
248
+ user_info = json_decode(json_resp['data'].to_s)
249
+ rescue RHC::JsonError
237
250
  exit 1
238
251
  end
239
252
  user_info
240
253
  end
241
254
 
255
+ # Public: Get a list of ssh keys
256
+ #
257
+ # type - The String type RSA or DSS.
258
+ # libra_server - The String DNS for the broker
259
+ # rhlogin - The String login name
260
+ # password - The String password for login
261
+ # net_http - The NET::HTTP Object to use
262
+ #
263
+ # Examples
264
+ #
265
+ # RHC::get_ssh_keys('openshift.redhat.com',
266
+ # 'mylogin@example.com',
267
+ # 'mypassword',
268
+ # RHC::Config.default_proxy)
269
+ # # => { "ssh_type" => "ssh-rsa",
270
+ # "ssh_key" => "AAAAB3NzaC1yc2EAAAADAQAB....",
271
+ # "fingerprint" => "ea:08:e3:c7:e3:c3:8e:6a:66:34:65:e4:56:f4:3e:ff"}
272
+ #
273
+ # FIXME! Exits on failure! Should return something instead
274
+ #
275
+ # Returns Hash on success or exits on failure
242
276
  def self.get_ssh_keys(libra_server, rhlogin, password, net_http)
243
277
  data = {'rhlogin' => rhlogin, 'action' => 'list-keys'}
244
278
  if @mydebug
@@ -260,30 +294,59 @@ module RHC
260
294
  exit 1
261
295
  end
262
296
  begin
263
- json_resp = JSON.parse(response.body)
264
- rescue JSON::ParserError
297
+ json_resp = json_decode(response.body)
298
+ rescue RHC::JsonError
265
299
  exit 1
266
300
  end
267
301
  update_server_api_v(json_resp)
268
302
  begin
269
- ssh_keys = (JSON.parse(json_resp['data'].to_s))
270
- rescue JSON::ParserError
303
+ ssh_keys = (json_decode(json_resp['data'].to_s))
304
+ rescue RHC::JsonError
271
305
  exit 1
272
306
  end
307
+
308
+ # Inject public fingerprint into key.
309
+ begin
310
+ ssh_keys['fingerprint'] = \
311
+ Net::SSH::KeyFactory.load_data_public_key(
312
+ "#{ssh_keys['ssh_type']} #{ssh_keys['ssh_key']}").fingerprint
313
+ rescue NoMethodError
314
+ #older net/ssh (mac for example)
315
+ tempfile = `mktemp /tmp/openshift.XXXXXXXX`
316
+ `echo "#{ssh_keys['ssh_type']} #{ssh_keys['ssh_key']}" > #{tempfile}`
317
+ ssh_keys['fingerprint'] = `ssh-keygen -lf #{tempfile}`.split(' ')[1]
318
+ rescue Net::SSH::Exception
319
+ rescue NotImplementedError
320
+ # key invalid, do nothing
321
+ # this happens if the user does not have a default key
322
+ end
323
+
324
+ if ssh_keys['keys'] && ssh_keys['keys'].kind_of?(Hash)
325
+ ssh_keys['keys'].each do |name, keyval|
326
+ type = keyval['type']
327
+ key = keyval['key']
328
+ begin
329
+ ssh_keys['keys'][name]['fingerprint'] = \
330
+ Net::SSH::KeyFactory.load_data_public_key(
331
+ "#{type} #{key}").fingerprint
332
+ rescue NoMethodError
333
+ #older net/ssh (mac for example)
334
+ tempfile = `mktemp /tmp/openshift.XXXXXXXX`
335
+ `echo "#{type} #{key}" > #{tempfile}`
336
+ ssh_keys['keys'][name]['fingerprint'] = `ssh-keygen -lf #{tempfile}`.split(' ')[1]
337
+ end
338
+ end
339
+ end
273
340
  ssh_keys
274
341
  end
275
342
 
276
343
  def self.get_password
277
344
  password = nil
278
345
  begin
279
- print "Password: "
280
- system "stty -echo"
281
- password = gets.chomp
346
+ password = ask_password
282
347
  rescue Interrupt
283
348
  puts "\n"
284
349
  exit 1
285
- ensure
286
- system "stty echo"
287
350
  end
288
351
  puts "\n"
289
352
  password
@@ -328,9 +391,9 @@ module RHC
328
391
  print_response_message(response.body)
329
392
  elsif response.content_type == 'application/json'
330
393
  begin
331
- json_resp = JSON.parse(response.body)
394
+ json_resp = json_decode(response.body)
332
395
  exit_code = print_json_body(json_resp)
333
- rescue JSON::ParserError
396
+ rescue RHC::JsonError
334
397
  exit_code = 1
335
398
  end
336
399
  elsif @mydebug
@@ -408,7 +471,13 @@ module RHC
408
471
  def self.create_app(libra_server, net_http, user_info, app_name, app_type, rhlogin, password, repo_dir=nil, no_dns=false, no_git=false, is_embedded_jenkins=false, gear_size='small',scale=false)
409
472
 
410
473
  # Need to have a fake HTTPResponse object for passing to print_reponse_err
411
- Struct.new('FakeResponse',:body,:code,:content_type)
474
+ # May already be initialized if called from another piece of code
475
+ # FIXME: remove this requirement when refactoring rhc
476
+ begin
477
+ Struct::FakeResponse
478
+ rescue NameError
479
+ Struct.new('FakeResponse',:body,:code,:content_type)
480
+ end
412
481
 
413
482
  domains = user_info['user_info']['domains']
414
483
  if domains.empty?
@@ -476,9 +545,9 @@ module RHC
476
545
  response = http_post(net_http, url, json_data, password)
477
546
 
478
547
  if response.code == '200'
479
- json_resp = JSON.parse(response.body)
548
+ json_resp = json_decode(response.body)
480
549
  print_response_success(json_resp)
481
- json_data = JSON.parse(json_resp['data'])
550
+ json_data = json_decode(json_resp['data'])
482
551
  health_check_path = json_data['health_check_path']
483
552
  app_uuid = json_data['uuid']
484
553
  result = json_resp['result']
@@ -697,116 +766,6 @@ LOOKSGOOD
697
766
  http_post(net_http, url, json_data, password)
698
767
  end
699
768
 
700
- # Runs rhc-list-ports on server to check available ports
701
- # :stderr return user-friendly port name, :stdout returns 127.0.0.1:8080 format
702
- def self.list_ports(rhc_domain, namespace, app_name, app_uuid, debug=true)
703
-
704
- ip_and_port_simple_regex = /[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\:[0-9]{1,5}/
705
-
706
- ssh_host = "#{app_name}-#{namespace}.#{rhc_domain}"
707
-
708
- ssh_cmd = "ssh -t #{app_uuid}@#{ssh_host} 'rhc-list-ports'"
709
-
710
- hosts_and_ports = []
711
- hosts_and_ports_descriptions = []
712
- scaled_uuids = []
713
-
714
- puts ssh_cmd if debug
715
-
716
- Open3.popen3(ssh_cmd) { |stdin, stdout, stderr|
717
-
718
- stdout.each { |line|
719
- line = line.chomp
720
- if line.downcase =~ /scale/
721
- scaled_uuid = line[5..-1]
722
- scaled_uuids << scaled_uuid
723
- else
724
- if ip_and_port_simple_regex.match(line)
725
- hosts_and_ports << line
726
- end
727
- end
728
- }
729
-
730
- stderr.each { |line|
731
- line = line.chomp
732
- if line.downcase =~ /permission denied/
733
- puts line
734
- exit 1
735
- end
736
-
737
-
738
- if line.index(ip_and_port_simple_regex)
739
- hosts_and_ports_descriptions << line
740
- end
741
- }
742
-
743
- }
744
-
745
- scaled_uuids.each { |uuid|
746
- list_scaled_ports(rhc_domain, namespace, app_name, uuid, hosts_and_ports, hosts_and_ports_descriptions, debug)
747
- }
748
-
749
- #hosts_and_ports_descriptions = stderr.gets.chomp.split(/\n/)
750
- #hosts_and_ports = stdout.gets.chomp.split(/\n/)
751
-
752
- # Net::SSH.start(ssh_host, app_uuid) do |ssh|
753
-
754
- # ssh.exec!("rhc-list-ports") do |channel, stream, data|
755
-
756
- # array = data.split(/\n/)
757
-
758
- # if stream == :stderr
759
- # hosts_and_ports_descriptions = array
760
- # elsif stream == :stdout
761
- # hosts_and_ports = array
762
- # end
763
-
764
- # end
765
-
766
- # end
767
-
768
- return hosts_and_ports, hosts_and_ports_descriptions
769
-
770
- end
771
-
772
- # Runs rhc-list-ports on server to check available ports
773
- # :stderr return user-friendly port name, :stdout returns 127.0.0.1:8080 format
774
- def self.list_scaled_ports(rhc_domain, namespace, app_name, app_uuid, hosts_and_ports, hosts_and_ports_descriptions, debug=true)
775
-
776
- ip_and_port_simple_regex = /[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\:[0-9]{1,5}/
777
-
778
- ssh_host = "#{app_name}-#{namespace}.#{rhc_domain}"
779
-
780
- ssh_cmd = "ssh -t #{app_uuid}@#{ssh_host} 'rhc-list-ports'"
781
-
782
- puts ssh_cmd if debug
783
-
784
- Open3.popen3(ssh_cmd) { |stdin, stdout, stderr|
785
-
786
- stdout.each { |line|
787
- line = line.chomp
788
-
789
- if ip_and_port_simple_regex.match(line)
790
- hosts_and_ports << line
791
- end
792
- }
793
-
794
- stderr.each { |line|
795
- line = line.chomp
796
-
797
- if line.downcase =~ /permission denied/
798
- puts line
799
- exit 1
800
- end
801
-
802
- if line.index(ip_and_port_simple_regex)
803
- hosts_and_ports_descriptions << line
804
- end
805
- }
806
- }
807
-
808
- end
809
-
810
769
  def self.ctl_app(libra_server, net_http, app_name, rhlogin, password, action, embedded=false, framework=nil, server_alias=nil, print_result=true)
811
770
  data = {:action => action,
812
771
  :app_name => app_name,
@@ -833,13 +792,128 @@ LOOKSGOOD
833
792
  response = http_post(net_http, url, json_data, password)
834
793
 
835
794
  if response.code == '200'
836
- json_resp = JSON.parse(response.body)
795
+ json_resp = json_decode(response.body)
837
796
  print_response_success(json_resp, print_result || @mydebug)
838
797
  else
839
798
  print_response_err(response)
840
799
  end
841
- JSON.parse(response.body)
800
+ json_decode(response.body)
801
+ end
802
+
803
+ def self.snapshot_create(rhc_domain, namespace, app_name, app_uuid, filename, debug=false)
804
+
805
+ ssh_cmd = "ssh #{app_uuid}@#{app_name}-#{namespace}.#{rhc_domain} 'snapshot' > #{filename}"
806
+ puts "Pulling down a snapshot to #{filename}..."
807
+ puts ssh_cmd if debug
808
+ puts
809
+
810
+ begin
811
+
812
+ if ! RHC::Helpers.windows?
813
+ output = `#{ssh_cmd}`
814
+ if $?.exitstatus != 0
815
+ puts output
816
+ puts "Error in trying to save snapshot. You can try to save manually by running:"
817
+ puts
818
+ puts ssh_cmd
819
+ puts
820
+ return 1
821
+ end
822
+ else
823
+ Net::SSH.start("#{app_name}-#{namespace}.#{rhc_domain}", app_uuid) do |ssh|
824
+ File.open(filename, 'wb') do |file|
825
+ ssh.exec! "snapshot" do |channel, stream, data|
826
+ if stream == :stdout
827
+ file.write(data)
828
+ else
829
+ puts data if debug
830
+ end
831
+ end
832
+ end
833
+ end
834
+ end
835
+ rescue Exception => e
836
+ puts e.message
837
+ puts "Error in trying to save snapshot. You can try to save manually by running:"
838
+ puts
839
+ puts ssh_cmd
840
+ puts
841
+ return 1
842
+ end
843
+ true
842
844
  end
845
+
846
+ def self.snapshot_restore(rhc_domain, namespace, app_name, app_uuid, filename, debug=false)
847
+ if File.exists? filename
848
+
849
+ if ! RHC::TarGz.contains filename, './*/' + app_name
850
+
851
+ puts "Archive at #{filename} does not contain the target application: ./*/#{app_name}"
852
+ puts "If you created this archive rather than exported with rhc-snapshot, be sure"
853
+ puts "the directory structure inside the archive starts with ./<app_uuid>/"
854
+ puts "i.e.: tar -czvf <app_name>.tar.gz ./<app_uuid>/"
855
+ return 255
856
+
857
+ else
858
+
859
+ include_git = RHC::TarGz.contains filename, './*/git'
860
+
861
+ ssh_cmd = "cat #{filename} | ssh #{app_uuid}@#{app_name}-#{namespace}.#{rhc_domain} 'restore#{include_git ? ' INCLUDE_GIT' : ''}'"
862
+ puts "Restoring from snapshot #{filename}..."
863
+ puts ssh_cmd if debug
864
+ puts
865
+
866
+ begin
867
+ if ! RHC::Helpers.windows?
868
+ output = `#{ssh_cmd}`
869
+ if $?.exitstatus != 0
870
+ puts output
871
+ puts "Error in trying to restore snapshot. You can try to restore manually by running:"
872
+ puts
873
+ puts ssh_cmd
874
+ puts
875
+ return 1
876
+ end
877
+ else
878
+ ssh = Net::SSH.start("#{app_name}-#{namespace}.#{rhc_domain}", app_uuid)
879
+ ssh.open_channel do |channel|
880
+ channel.exec("restore#{include_git ? ' INCLUDE_GIT' : ''}") do |ch, success|
881
+ channel.on_data do |ch, data|
882
+ puts data
883
+ end
884
+ channel.on_extended_data do |ch, type, data|
885
+ puts data
886
+ end
887
+ channel.on_close do |ch|
888
+ puts "Terminating..."
889
+ end
890
+ File.open(filename, 'rb') do |file|
891
+ while (line = file.gets)
892
+ channel.send_data line
893
+ end
894
+ end
895
+ channel.eof!
896
+ end
897
+ end
898
+ ssh.loop
899
+ end
900
+ rescue Exception => e
901
+ puts e.message if debug
902
+ puts "Error in trying to restore snapshot. You can try to restore manually by running:"
903
+ puts
904
+ puts ssh_cmd
905
+ puts
906
+ return 1
907
+ end
908
+
909
+ end
910
+ else
911
+ puts "Archive not found: #{filename}"
912
+ return 255
913
+ end
914
+ true
915
+ end
916
+
843
917
  end
844
918
 
845
919
  # provide a hook for performing actions before rhc-* commands exit
@@ -856,32 +930,17 @@ at_exit {
856
930
  @conf_name = 'express.conf'
857
931
  _linux_cfg = '/etc/openshift/' + @conf_name
858
932
  _gem_cfg = File.join(File.expand_path(File.dirname(__FILE__) + "/../conf"), @conf_name)
859
- _home_conf = File.expand_path('~/.openshift')
860
- @local_config_path = File.join(_home_conf, @conf_name)
933
+ @home_conf = File.expand_path('~/.openshift')
934
+ @local_config_path = File.join(@home_conf, @conf_name)
861
935
  @config_path = File.exists?(_linux_cfg) ? _linux_cfg : _gem_cfg
936
+ @home_dir=File.expand_path("~")
862
937
 
863
- FileUtils.mkdir_p _home_conf unless File.directory?(_home_conf)
864
938
  local_config_path = File.expand_path(@local_config_path)
865
- if !File.exists? local_config_path
866
- file = File.open(local_config_path, 'w')
867
- begin
868
- file.puts <<EOF
869
- # SSH key file
870
- #ssh_key_file = 'libra_id_rsa'
871
- EOF
872
-
873
- ensure
874
- file.close
875
- end
876
- puts ""
877
- puts "Created local config file: " + local_config_path
878
- puts "express.conf contains user configuration and can be transferred across clients."
879
- puts ""
880
- end
881
939
 
882
940
  begin
883
941
  @global_config = ParseConfig.new(@config_path)
884
- @local_config = ParseConfig.new(File.expand_path(@local_config_path))
942
+ @local_config = ParseConfig.new(File.expand_path(@local_config_path)) if \
943
+ File.exists?(@local_config_path)
885
944
  rescue Errno::EACCES => e
886
945
  puts "Could not open config file: #{e.message}"
887
946
  exit 253
@@ -946,6 +1005,10 @@ def get_var(var)
946
1005
  v
947
1006
  end
948
1007
 
1008
+ def ask_password
1009
+ return ask("Password: ") { |q| q.echo = '*' }
1010
+ end
1011
+
949
1012
  def kfile_not_found
950
1013
  puts <<KFILE_NOT_FOUND
951
1014
  Your SSH keys are created either by running ssh-keygen (password optional)
@@ -953,7 +1016,7 @@ or by having the 'rhc domain create' command do it for you. If you created
953
1016
  them on your own (or want to use an existing keypair), be sure to paste
954
1017
  your public key into the express console at http://www.openshift.com.
955
1018
  The client tools use the value of 'ssh_key_file' in express.conf to find
956
- your key followed by the defaults of libra_id_rsa[.pub] and then
1019
+ your key followed by the defaults of id_rsa[.pub] and then
957
1020
  id_rsa[.pub].
958
1021
  KFILE_NOT_FOUND
959
1022
 
@@ -969,7 +1032,7 @@ def get_kfile(check_exists=true)
969
1032
  kfile = File.expand_path(ssh_key_file)
970
1033
  end
971
1034
  else
972
- kfile = "#{ENV['HOME']}/.ssh/libra_id_rsa"
1035
+ kfile = "#{ENV['HOME']}/.ssh/id_rsa"
973
1036
  end
974
1037
  if check_exists && !File.exists?(kfile)
975
1038
  if ssh_key_file
@@ -997,8 +1060,9 @@ end
997
1060
 
998
1061
  # Add a new namespace to configs
999
1062
  def self.add_rhlogin_config(rhlogin, uuid)
1063
+ config_path = RHC::Config.local_config_path
1000
1064
  f = open(File.expand_path(config_path), 'a')
1001
- unless config.get_value('default_rhlogin')
1065
+ unless RHC::Config.get_value('default_rhlogin')
1002
1066
  f.puts("# Default rhlogin to use if none is specified")
1003
1067
  f.puts("default_rhlogin=#{rhlogin}")
1004
1068
  f.puts("")
@@ -1006,77 +1070,189 @@ def self.add_rhlogin_config(rhlogin, uuid)
1006
1070
  f.close
1007
1071
  end
1008
1072
 
1009
- # Check / add new host to ~/.ssh/config
1010
- def self.add_ssh_config_host(rhc_domain, ssh_key_file_path, ssh_config, ssh_config_d)
1011
-
1012
- puts "Checking ~/.ssh/config"
1013
- ssh_key_file_name = File.basename(ssh_key_file_path)
1014
- if ssh_key_file_path =~ /^#{ENV['HOME']}/
1015
- ssh_key_file_path = ssh_key_file_path[ENV['HOME'].length..-1]
1016
- if ssh_key_file_path =~ /^\// || ssh_key_file_path =~ /^\\/
1017
- ssh_key_file_path = '~' + ssh_key_file_path
1073
+ # Public: Handle response message when updating keys
1074
+ #
1075
+ # url - The Object URI::HTTPS
1076
+ # data - The Hash representation of the data response
1077
+ # password - The String password for the user
1078
+ #
1079
+ # Examples
1080
+ #
1081
+ # handle_key_mgmt_response(
1082
+ # URI.parse('https://openshift.redhat.com/broker/ssh_keys'),
1083
+ # {
1084
+ # :rhlogin=>"rhnlogin@example.com",
1085
+ # :key_name=>"default",
1086
+ # :action=>"update-key",
1087
+ # :ssh=>"AAAAB3NzaC1yc2EAAAADAQABAAAAgQCrXG5c.....",
1088
+ # :key_type=>"ssh-rsa"},
1089
+ # 'mypass')
1090
+ # # => nil
1091
+ #
1092
+ # Returns nil on Success and RHC::http object on failure
1093
+ def handle_key_mgmt_response(url, data, password)
1094
+ RHC::print_post_data(data)
1095
+ json_data = RHC::generate_json(data)
1096
+
1097
+ response = RHC::http_post(RHC::Config.default_proxy, url, json_data, password)
1098
+
1099
+ if response.code == '200'
1100
+ begin
1101
+ json_resp = RHC::json_decode(response.body)
1102
+ RHC::update_server_api_v(json_resp)
1103
+ RHC::print_response_success(json_resp)
1104
+ puts "Success"
1105
+ return
1106
+ rescue RHC::JsonError
1107
+ RHC::print_response_err(response)
1108
+ end
1109
+ else
1110
+ RHC::print_response_err(response)
1111
+ end
1112
+ puts "Failure"
1113
+ return response
1114
+ end
1115
+
1116
+ # Public: Add or upload an ssh key
1117
+ #
1118
+ # type - The String type RSA or DSS.
1119
+ # command - The String value 'add' or 'update'
1120
+ # identifier - The String value to identify the key
1121
+ # pub_key_file_path - The String file path of the public key
1122
+ # rhlogin - The String login to the broker
1123
+ # password- The String password for the user
1124
+ #
1125
+ # Examples
1126
+ #
1127
+ # generate_ssh_key_ruby('add', 'newkeyname', '~/.ssh/id_rsa',
1128
+ # 'mylogin', 'mypass')
1129
+ # # => /home/user/.ssh/id_rsa.pub
1130
+ #
1131
+ # Returns nil on success or HTTP object on failure
1132
+ def add_or_update_key(command, identifier, pub_key_file_path, rhlogin, password)
1133
+
1134
+ # Read user public ssh key
1135
+ if pub_key_file_path
1136
+ if File.readable?(pub_key_file_path)
1137
+ begin
1138
+ ssh_keyfile_contents = File.open(pub_key_file_path).gets.chomp.split(' ')
1139
+ ssh_key = ssh_keyfile_contents[1]
1140
+ ssh_key_type = ssh_keyfile_contents[0]
1141
+ rescue Exception => e
1142
+ puts "Invalid public keyfile format! Please specify a valid user public keyfile."
1143
+ exit 1
1144
+ end
1018
1145
  else
1019
- ssh_key_file_path = '~/' + ssh_key_file_path
1146
+ puts "Unable to read user public keyfile #{pub_key_file_path}"
1147
+ exit 1
1020
1148
  end
1149
+ else # create key
1150
+ key_name = identifier
1151
+ puts "Generating ssh key pair for user '#{key_name}' in the dir '#{Dir.pwd}/'"
1152
+ # REMOVED in favor of generate_ssh_key_ruby: system("ssh-keygen -t rsa -f '#{key_name}'")
1153
+ ssh_pub_key_file = generate_ssh_key_ruby()
1154
+ ssh_keyfile_contents = File.open(ssh_pub_key_file).gets.chomp.split(' ')
1155
+ ssh_key = ssh_keyfile_contents[1]
1156
+ ssh_key_type = ssh_keyfile_contents[0]
1021
1157
  end
1022
- if (ssh_key_file_name != 'id_rsa')
1023
- found = false
1024
1158
 
1025
- begin
1026
- File.open(ssh_config, "r") do |sline|
1027
- while(line = sline.gets)
1028
- if line.to_s.index("Host *.#{rhc_domain}") == 0
1029
- found = true
1030
- break
1031
- end
1032
- end
1033
- end
1034
- rescue Errno::EACCES
1035
- puts "Could not read from #{ssh_config}"
1036
- puts "Reason: " + $!
1037
- puts
1038
- puts "Please correct this first. Then run rerun."
1039
- puts
1040
- exit 213
1041
- rescue Errno::ENOENT
1042
- puts "Could not find #{ssh_config}. This is ok, continuing"
1159
+ data = {}
1160
+ data[:rhlogin] = rhlogin
1161
+ data[:key_name] = identifier
1162
+ data[:ssh] = ssh_key
1163
+ data[:action] = 'add-key'
1164
+ data[:key_type] = ssh_key_type
1165
+
1166
+ if command == 'add'
1167
+ data[:action] = 'add-key'
1168
+ elsif command == 'update'
1169
+ data[:action] = 'update-key'
1170
+ end
1171
+
1172
+ url = URI.parse("https://#{RHC::Config.get_value('libra_server')}/broker/ssh_keys")
1173
+ handle_key_mgmt_response(url, data, password)
1174
+ end
1175
+
1176
+
1177
+ # Public: Generate an SSH key and store it in ~/.ssh/id_rsa
1178
+ #
1179
+ # type - The String type RSA or DSS.
1180
+ # bits - The Integer value for number of bits.
1181
+ # comment - The String comment for the key
1182
+ #
1183
+ # Examples
1184
+ #
1185
+ # generate_ssh_key_ruby()
1186
+ # # => /home/user/.ssh/id_rsa.pub
1187
+ #
1188
+ # Returns nil on failure or public key location as a String on success
1189
+ def generate_ssh_key_ruby(type="RSA", bits = 1024, comment = "OpenShift-Key")
1190
+ key = SSHKey.generate(:type => type,
1191
+ :bits => bits,
1192
+ :comment => comment)
1193
+ ssh_dir = "#{RHC::Config.home_dir}/.ssh"
1194
+ if File.exists?("#{ssh_dir}/id_rsa")
1195
+ puts "SSH key already exists: #{ssh_dir}/id_rsa. Reusing..."
1196
+ return nil
1197
+ else
1198
+ unless File.exists?(ssh_dir)
1199
+ FileUtils.mkdir_p(ssh_dir)
1200
+ File.chmod(0700, ssh_dir)
1043
1201
  end
1044
- if found
1045
- puts "Found #{rhc_domain} in ~/.ssh/config... No need to adjust"
1046
- else
1047
- puts " Adding #{rhc_domain} to ~/.ssh/config"
1048
- begin
1049
- f = File.open(ssh_config, "a")
1050
- f.puts <<SSH
1051
-
1052
- # Added by 'rhc domain create' on #{`date`}
1053
- Host *.#{rhc_domain}
1054
- IdentityFile #{ssh_key_file_path}
1055
- VerifyHostKeyDNS yes
1056
- StrictHostKeyChecking no
1057
- UserKnownHostsFile ~/.ssh/libra_known_hosts
1058
-
1059
- SSH
1060
- f.close
1061
- rescue Errno::EACCES
1062
- puts "Could not write to #{ssh_config}"
1063
- puts "Reason: " + $!
1064
- puts
1065
- puts "Please correct this first. Then run rerun."
1066
- puts
1067
- exit 214
1068
- rescue Errno::ENOENT
1069
- # Make directory and config if they do not exist
1070
- puts "Could not find directory: " + $!
1071
- puts "creating"
1072
- FileUtils.mkdir_p ssh_config_d
1073
- file = File.open(ssh_config, 'w')
1074
- file.close
1075
- retry
1076
- end
1202
+ File.open("#{ssh_dir}/id_rsa", 'w') {|f| f.write(key.private_key)}
1203
+ File.chmod(0600, "#{ssh_dir}/id_rsa")
1204
+ File.open("#{ssh_dir}/id_rsa.pub", 'w') {|f| f.write(key.ssh_public_key)}
1205
+ end
1206
+ "#{ssh_dir}/id_rsa.pub"
1207
+ end
1208
+
1209
+ # Public: Run ssh command on remote host
1210
+ #
1211
+ # host - The String of the remote hostname to ssh to.
1212
+ # username - The String username of the remote user to ssh as.
1213
+ # command - The String command to run on the remote host.
1214
+ #
1215
+ # Examples
1216
+ #
1217
+ # ssh_ruby('myapp-t.rhcloud.com',
1218
+ # '109745632b514e9590aa802ec015b074',
1219
+ # 'rhcsh tail -f $OPENSHIFT_LOG_DIR/*"')
1220
+ # # => true
1221
+ #
1222
+ # Returns true on success
1223
+ def ssh_ruby(host, username, command)
1224
+ Net::SSH.start(host, username) do |session|
1225
+ session.open_channel do |channel|
1226
+ channel.request_pty do |ch, success|
1227
+ puts "pty could not be obtained" unless success
1228
+ end
1229
+
1230
+ channel.on_data do |ch, data|
1231
+ #puts "[#{file}] -> #{data}"
1232
+ puts data
1233
+ end
1234
+ channel.exec command
1077
1235
  end
1236
+ session.loop
1237
+ end
1238
+ end
1078
1239
 
1079
- File.chmod(0700, ssh_config_d)
1080
- File.chmod(0600, ssh_config)
1240
+ # Public: legacy convinience function for getting config keys
1241
+ def get_var(key)
1242
+ RHC::Config.get_value(key)
1243
+ end
1244
+
1245
+ # Public: convinience function for running the wizard
1246
+ #
1247
+ # Returns: false if wizard did not need to run
1248
+ # true if wizard ran successfuly
1249
+ # nil of there was an error
1250
+ #
1251
+ def default_setup_wizard
1252
+ if RHC::Config.should_run_wizard?
1253
+ w = RHC::Wizard.new(RHC::Config.local_config_path)
1254
+ return w.run
1081
1255
  end
1256
+
1257
+ false
1082
1258
  end