rhc 1.3.8 → 1.4.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.
@@ -66,7 +66,9 @@ module RHC::Commands
66
66
 
67
67
  UP_TO_256 = /25[0-5]|2[0-4][0-9]|[01]?(?:[0-9][0-9]?)/
68
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/
69
+ # 'host' part is a bit lax; we rely on 'rhc-list-ports' to hand us a reasonable output
70
+ # about the host information, be it numeric or FQDN in IPv4 or IPv6.
71
+ HOST_AND_PORT = /(.+):(#{UP_TO_65535})\b/
70
72
 
71
73
  summary "Forward remote ports to the workstation"
72
74
  syntax "<application>"
@@ -96,7 +98,7 @@ module RHC::Commands
96
98
  raise RHC::PermissionDeniedException.new "Permission denied." if line =~ /permission denied/i
97
99
  # ...and also which services are available for the application
98
100
  # for us to forward ports for.
99
- if line =~ /\A\s*(\S+) -> #{IP_AND_PORT}/
101
+ if line =~ /\A\s*(\S+) -> #{HOST_AND_PORT}\z/
100
102
  debug fs = ForwardingSpec.new($1, $2, $3.to_i)
101
103
  forwarding_specs << fs
102
104
  else
@@ -18,14 +18,12 @@ if RUBY_VERSION >= '1.9' and ENV['RHC_FEATURE_COVERAGE']
18
18
  # Groups - general categories of test areas
19
19
  add_group('Commands') { |src_file| src_file.filename.include?(File.join(%w[lib rhc commands])) }
20
20
  add_group('RHC Lib') { |src_file| src_file.filename.include?(File.join(%w[lib rhc])) }
21
- add_group('REST') { |src_file| src_file.filename.include?(File.join(%w[lib rhc/rest])) }
22
- add_group('Legacy') { |src_file| src_file.filename.include?(File.join(%w[bin])) or
23
- src_file.filename.include?(File.join(%w[lib rhc-common.rb])) }
21
+ add_group('REST') { |src_file| src_file.filename.include?(File.join(%w[lib rhc/rest])) }
24
22
  add_group('Test') { |src_file| src_file.filename.include?(File.join(%w[features])) or
25
23
  src_file.filename.include?(File.join(%w[spec])) }
26
24
 
27
25
  use_merging = true
28
- # Note, the #:nocov: coverage exclusion should only be used on external functions
26
+ # Note, the #:nocov: coverage exclusion should only be used on external functions
29
27
  # that cannot be nondestructively tested in a developer environment.
30
28
  end
31
29
 
@@ -123,8 +123,6 @@ module RHC
123
123
  end
124
124
  end
125
125
 
126
- =begin
127
- # Commenting this out for US2438
128
126
  class AdditionalStorageArgumentsException < Exception
129
127
  def initialize(message="Only one storage action can be performed at a time.")
130
128
  super message, 1
@@ -142,5 +140,4 @@ module RHC
142
140
  super message, 1
143
141
  end
144
142
  end
145
- =end
146
143
  end
@@ -1,4 +1,4 @@
1
- require 'open4'
1
+ require 'open4'
2
2
 
3
3
  module RHC
4
4
  module GitHelpers
@@ -254,14 +254,6 @@ module RHC
254
254
  count == 1 ? "#{count} #{s}" : "#{count} #{s}s"
255
255
  end
256
256
 
257
- ## report a result (true/false) and return result
258
- ## if the result is false, msg is displayed
259
- def report_result(result, msg, fatal = true)
260
- result ? $terminal.instance_variable_get(:@output).print('.') : error(msg)
261
- # ignore the result if non-fatal
262
- fatal ? result : true
263
- end
264
-
265
257
  # given an array of arrays "items", construct an array of strings that can
266
258
  # be used to print in tabular form.
267
259
  def table(items, opts={}, &block)
@@ -457,10 +449,32 @@ module RHC
457
449
  end
458
450
 
459
451
  def hosts_file_contains?(host)
452
+ with_tolerant_encoding do
453
+ begin
454
+ resolver = Resolv::Hosts.new
455
+ resolver.getaddress host
456
+ rescue => e
457
+ debug "Error while resolving with Resolv::Hosts: #{e.message}(#{e.class})\n #{e.backtrace.join("\n ")}"
458
+ end
459
+ end
460
+ end
461
+
462
+ def with_tolerant_encoding(&block)
460
463
  # :nocov:
461
- resolver = Resolv::Hosts.new
462
- resolver.getaddress host
463
- rescue Resolv::ResolvError
464
+ if RUBY_VERSION.to_f >= 1.9
465
+ orig_default_internal = Encoding.default_internal
466
+ Encoding.default_internal = 'ISO-8859-1'
467
+ else
468
+ orig_default_kcode = $KCODE
469
+ $KCODE = 'N'
470
+ end
471
+ yield
472
+ ensure
473
+ if RUBY_VERSION.to_f >= 1.9
474
+ Encoding.default_internal = orig_default_internal
475
+ else
476
+ $KCODE = orig_default_kcode
477
+ end
464
478
  # :nocov:
465
479
  end
466
480
 
@@ -69,7 +69,7 @@ module RHC
69
69
 
70
70
  def format_cart_header(cart)
71
71
  [
72
- cart.name,
72
+ cart.name,
73
73
  cart.name != cart.display_name ? "(#{cart.display_name})" : nil,
74
74
  ].compact.join(' ')
75
75
  end
@@ -127,8 +127,6 @@ module RHC
127
127
  ].compact.join(' ')
128
128
  end
129
129
 
130
- =begin
131
- # Commenting this out for US2438
132
130
  def display_cart_storage_info(cart, title="Storage Info")
133
131
  say_table \
134
132
  title,
@@ -141,7 +139,6 @@ module RHC
141
139
  display_cart_storage_info(cart, cart.display_name)
142
140
  end
143
141
  end
144
- =end
145
142
 
146
143
  #---------------------------
147
144
  # Misc information
@@ -204,11 +201,8 @@ module RHC
204
201
  (value == -1 ? "available" : value)
205
202
  when :gear_info
206
203
  format_gear_info(value)
207
- =begin
208
- # Commenting this out for US2438
209
204
  when :base_gear_storage,:additional_gear_storage
210
205
  ((value.nil? or value == 0) ? "None" : "#{value}GB")
211
- =end
212
206
  when :aliases
213
207
  value.join ', '
214
208
  else
@@ -3,7 +3,7 @@ module RHC
3
3
  class Cartridge < Base
4
4
  HIDDEN_TAGS = [:framework, :web_framework, :cartridge].map(&:to_s)
5
5
 
6
- define_attr :type, :name, :display_name, :properties, :gear_profile, :status_messages, :scales_to, :scales_from, :scales_with, :current_scale, :supported_scales_to, :supported_scales_from, :tags, :description, :collocated_with
6
+ define_attr :type, :name, :display_name, :properties, :gear_profile, :status_messages, :scales_to, :scales_from, :scales_with, :current_scale, :supported_scales_to, :supported_scales_from, :tags, :description, :collocated_with, :base_gear_storage, :additional_gear_storage
7
7
 
8
8
  def scalable?
9
9
  supported_scales_to != supported_scales_from
@@ -24,7 +24,7 @@ module RHC
24
24
  else
25
25
  [
26
26
  options.delete(:url) ||
27
- (options[:server] && "https://#{options.delete(:server)}/broker/rest/api"),
27
+ (options[:server] && "https://#{options.delete(:server)}/broker/rest/api"),
28
28
  options.delete(:debug),
29
29
  options.delete(:preferred_api_versions)
30
30
  ]
@@ -118,10 +118,10 @@ module RHC
118
118
  "A secure connection could not be established to the server (#{e.message}). You may disable secure connections to your server with the -k (or --insecure) option '#{args[1]}'.\n\n"\
119
119
  "If your server is using a self-signed certificate, you may disable certificate checks with the -k (or --insecure) option. Using this option means that your data is potentially visible to third parties.")
120
120
  end
121
- rescue SocketError => e
121
+ rescue SocketError, Errno::ECONNREFUSED => e
122
122
  raise ConnectionException.new(
123
123
  "Unable to connect to the server (#{e.message})."\
124
- "#{client.proxy.present? ? " Check that you have correctly specified your proxy server '#{client.proxy}' as well as your OpenShift server '#{args[1]}'." : " Check that you have correctly specified your OpenShift server '#{args[0]}'."}")
124
+ "#{client.proxy.present? ? " Check that you have correctly specified your proxy server '#{client.proxy}' as well as your OpenShift server '#{args[1]}'." : " Check that you have correctly specified your OpenShift server '#{args[1]}'."}")
125
125
  rescue RHC::Rest::Exception
126
126
  raise
127
127
  rescue => e
@@ -34,7 +34,7 @@ module RHC::Rest::Mock
34
34
  :body => {
35
35
  :data => mock_response_links(mock_real_client_links),
36
36
  :supported_api_versions => [1.0, 1.1, 1.2, 1.3],
37
- }.to_json
37
+ }.to_json
38
38
  })
39
39
  end
40
40
  def stub_user(auth=mock_user_auth)
@@ -127,7 +127,7 @@ module RHC::Rest::Mock
127
127
  def stub_simple_carts
128
128
  stub_api_request(:get, 'broker/rest/cartridges', mock_user_auth).to_return(simple_carts)
129
129
  end
130
-
130
+
131
131
  def define_exceptional_test_on_wizard
132
132
  RHC::Wizard.module_eval <<-EOM
133
133
  private
@@ -559,6 +559,7 @@ module RHC::Rest::Mock
559
559
  @scales_to = 1
560
560
  @current_scale = 1
561
561
  @gear_profile = 'small'
562
+ @additional_gear_storage = 5
562
563
  end
563
564
 
564
565
  def destroy
@@ -594,6 +595,11 @@ module RHC::Rest::Mock
594
595
  @scales_to = values[:scales_to] if values[:scales_to]
595
596
  self
596
597
  end
598
+
599
+ def set_storage(values)
600
+ @additional_gear_storage = values[:additional_gear_storage] if values[:additional_gear_storage]
601
+ self
602
+ end
597
603
  end
598
604
 
599
605
  class MockRestKey < RHC::Rest::Key
@@ -1,6 +1,4 @@
1
- require 'rhc/helpers'
2
- require 'rhc/ssh_helpers'
3
- require 'rhc/git_helpers'
1
+ require 'rhc'
4
2
  require 'highline/system_extensions'
5
3
  require 'fileutils'
6
4
  require 'socket'
@@ -59,6 +57,7 @@ module RHC
59
57
  include RHC::Helpers
60
58
  include RHC::SSHHelpers
61
59
  include RHC::GitHelpers
60
+ include RHC::CartridgeHelpers
62
61
  attr_reader :config, :options
63
62
  attr_accessor :auth, :user
64
63
 
@@ -83,13 +82,17 @@ module RHC
83
82
  auth.send(:password)
84
83
  end
85
84
 
85
+ def print_dot
86
+ $terminal.instance_variable_get(:@output).print('.')
87
+ end
88
+
86
89
  private
87
-
90
+
88
91
  # cache SSH keys from the REST client
89
92
  def ssh_keys
90
93
  @ssh_keys ||= rest_client.sshkeys
91
94
  end
92
-
95
+
93
96
  # clear SSH key cache
94
97
  def clear_ssh_keys_cache
95
98
  @ssh_keys = nil
@@ -352,8 +355,7 @@ module RHC
352
355
 
353
356
  paragraph{ say "Run 'rhc app create' to create your first application." }
354
357
  paragraph do
355
- application_types = rest_client.find_cartridges :type => "standalone"
356
- say table(application_types.sort {|a,b| a.display_name <=> b.display_name }.map do |cart|
358
+ say table(standalone_cartridges.sort {|a,b| a.display_name <=> b.display_name }.map do |cart|
357
359
  [' ', cart.display_name, "rhc app create <app name> #{cart.name}"]
358
360
  end).join("\n")
359
361
  end
@@ -379,10 +381,20 @@ module RHC
379
381
  def setup_test_stage
380
382
  say "Checking common problems "
381
383
  tests = private_methods.select {|m| m.to_s.start_with? 'test_'}
384
+ failed = false
382
385
  tests.sort.each do |test|
383
- send(test)
386
+ begin
387
+ send(test)
388
+ print_dot unless failed
389
+ true
390
+ rescue => e
391
+ say "\n" unless failed
392
+ failed = true
393
+ paragraph{ error e.message }
394
+ false
395
+ end
384
396
  end.tap do |pass|
385
- success(' done') if pass
397
+ success(' done') if !failed
386
398
  end
387
399
 
388
400
  true
@@ -400,32 +412,27 @@ module RHC
400
412
  pub_key_file = RHC::Config.ssh_pub_key_file_path
401
413
  private_key_file = RHC::Config.ssh_priv_key_file_path
402
414
  if File.exist?(private_key_file)
403
- report_result (File.exist?(private_key_file) and File.stat(private_key_file).mode.to_s(8) =~ /[4-7]00$/),
404
- "#{private_key_file} should not be accessible by anyone but the current user"
405
- else
406
- #:nocov:
407
- true
408
- #:nocov:
415
+ unless File.stat(private_key_file).mode.to_s(8) =~ /[4-7]00$/
416
+ raise "Your private SSH key file should be set as readable only to yourself. Please run 'chmod 600 #{system_path(private_key_file)}'"
417
+ end
409
418
  end
419
+ true
410
420
  end
411
421
 
412
422
  # test connectivity an app
413
423
  def test_ssh_connectivity
414
- unless ssh_key_uploaded?
415
- return true
416
- end
417
-
424
+ return true unless ssh_key_uploaded?
425
+
418
426
  applications.take(1).each do |app|
419
427
  begin
420
428
  ssh = Net::SSH.start(app.host, app.uuid, :timeout => 60)
421
- return true
422
429
  rescue => e
423
- report_result(ssh, "An SSH connection could not be established to #{app.host}. Your SSH configuration may not be correct, or the application may not be responding. #{e.message} (#{e.class})", false)
424
- return false
430
+ raise "An SSH connection could not be established to #{app.host}. Your SSH configuration may not be correct, or the application may not be responding. #{e.message} (#{e.class})"
425
431
  ensure
426
432
  ssh.close if ssh
427
433
  end
428
434
  end
435
+ true
429
436
  end
430
437
 
431
438
  ###
@@ -13,7 +13,7 @@ unless RUBY_VERSION < '1.9'
13
13
  @missed_lines
14
14
  end
15
15
  end
16
-
16
+
17
17
  original_stderr = $stderr # in case helpers don't properly cleanup
18
18
  SimpleCov.at_exit do
19
19
  SimpleCov.result.format!
@@ -22,7 +22,7 @@ unless RUBY_VERSION < '1.9'
22
22
  exit 1
23
23
  end
24
24
  end
25
-
25
+
26
26
  SimpleCov.start do
27
27
  coverage_dir 'coverage/spec/'
28
28
 
@@ -31,7 +31,6 @@ unless RUBY_VERSION < '1.9'
31
31
  # namespaces changed
32
32
  add_filter 'lib/rhc/rest/' # REST coverage is not yet 100%
33
33
  add_filter 'lib/bin/' # This is just for safety; simplecov isn't picking these up.
34
- add_filter 'lib/rhc-common.rb' # deprecated, functionality moved into client or rhc/helpers.rb
35
34
  add_filter 'features/' # Don't report on the files that run the cucumber tests
36
35
  add_filter 'lib/rhc-feature-coverage-helper.rb'
37
36
  add_filter 'spec/' # Don't report on the files that run the spec tests
@@ -39,13 +38,11 @@ unless RUBY_VERSION < '1.9'
39
38
  # Groups - general categories of test areas
40
39
  add_group('Commands') { |src_file| src_file.filename.include?(File.join(%w[lib rhc commands])) }
41
40
  add_group('RHC Lib') { |src_file| src_file.filename.include?(File.join(%w[lib rhc])) }
42
- add_group('REST') { |src_file| src_file.filename.include?(File.join(%w[lib rhc/rest])) }
43
- add_group('Legacy') { |src_file| src_file.filename.include?(File.join(%w[bin])) or
44
- src_file.filename.include?(File.join(%w[lib rhc-common.rb])) }
41
+ add_group('REST') { |src_file| src_file.filename.include?(File.join(%w[lib rhc/rest])) }
45
42
  add_group('Test') { |src_file| src_file.filename.include?(File.join(%w[features])) or
46
43
  src_file.filename.include?(File.join(%w[spec])) }
47
-
48
- # Note, the #:nocov: coverage exclusion should only be used on external functions
44
+
45
+ # Note, the #:nocov: coverage exclusion should only be used on external functions
49
46
  # that cannot be nondestructively tested in a developer environment.
50
47
  end
51
48
  end
@@ -2,6 +2,7 @@ require 'spec_helper'
2
2
  require 'rest_spec_helper'
3
3
  require 'rhc/commands/app'
4
4
  require 'rhc/config'
5
+ require 'resolv'
5
6
 
6
7
  describe RHC::Commands::App do
7
8
  let!(:rest_client){ MockRestClient.new }
@@ -60,6 +61,18 @@ describe RHC::Commands::App do
60
61
  it { run_output.should match("Cartridges: mock_standalone_cart-1\n") }
61
62
  end
62
63
 
64
+ context 'when Hosts resolver raises an Exception' do
65
+ let(:arguments) { ['app', 'create', 'app1', 'mock_standalone_cart-1', '--noprompt', '--timeout', '10', '--config', 'test.conf', '-l', 'test@test.foo', '-p', 'password'] }
66
+ before :each do
67
+ resolver = Object.new
68
+ Resolv::Hosts.should_receive(:new).and_return(resolver)
69
+ resolver.should_receive(:getaddress).with('app1-mockdomain.fake.foo').and_raise(ArgumentError)
70
+ end
71
+
72
+ it { expect { run }.should exit_with_code(0) }
73
+ it { run_output.should match("Success") }
74
+ end
75
+
63
76
  context 'when run with multiple carts' do
64
77
  let(:arguments) { ['app', 'create', 'app1', 'mock_standalone_cart-1', 'mock_cart-1', '--noprompt', '-p', 'password'] }
65
78
  it { expect { run }.should exit_with_code(0) }
@@ -80,8 +93,6 @@ describe RHC::Commands::App do
80
93
  context 'when no cartridges are returned' do
81
94
  before(:each) do
82
95
  domain = rest_client.domains.first
83
- #rest_client.stub(:cartridges) { [] }
84
- # domain.stub(:add_application).and_raise(RHC::Rest::ValidationException.new('Each application needs a web cartridge', '', '109'))
85
96
  end
86
97
  context 'without trace' do
87
98
  let(:arguments) { ['app', 'create', 'app1', 'nomatch_cart', '--noprompt', '--config', 'test.conf', '-l', 'test@test.foo', '-p', 'password'] }
@@ -94,15 +105,39 @@ describe RHC::Commands::App do
94
105
  end
95
106
  end
96
107
 
97
- describe 'app create too many carts found error' do
98
- let(:arguments) { ['app', 'create', 'app1', 'mock_standalone_cart', '--trace', '--noprompt', '--config', 'test.conf', '-l', 'test@test.foo', '-p', 'password'] }
108
+ describe 'cart matching behavior' do
109
+ before(:each) do
110
+ domain = rest_client.add_domain("mockdomain")
111
+ end
99
112
 
100
- context 'when run' do
101
- before(:each) do
102
- domain = rest_client.add_domain("mockdomain")
103
- end
113
+ context 'multiple web matches' do
114
+ let(:arguments) { ['app', 'create', 'app1', 'mock_standalone_cart', '--trace', '--noprompt'] }
104
115
  it { expect { run }.should raise_error(RHC::MultipleCartridgesException) }
105
116
  end
117
+ context 'when only a single cart can match' do
118
+ let(:arguments) { ['app', 'create', 'app1', 'unique', '--trace', '--noprompt'] }
119
+ it('picks the cart') { run_output.should match('Using mock_unique_standalone_cart-1') }
120
+ end
121
+ context 'when I pick a web cart and an ambiguous non web cart' do
122
+ let(:arguments) { ['app', 'create', 'app1', 'mock_standalone_cart-1', 'unique', '--trace', '--noprompt'] }
123
+ it('picks the non web cart') { run_output.should match('Using unique_mock_cart-1') }
124
+ end
125
+ context 'when I pick very ambiguous carts' do
126
+ let(:arguments) { ['app', 'create', 'app1', 'mock', '--noprompt'] }
127
+ it('shows only web carts') { run_output.should_not match('unique_mock_cart-1') }
128
+ end
129
+ context 'when I pick only embedded carts' do
130
+ let(:arguments) { ['app', 'create', 'app1', 'mock_cart', '--trace', '--noprompt'] }
131
+ it { expect { run }.should raise_error(RHC::CartridgeNotFoundException, /Every application needs a web cartridge/) }
132
+ end
133
+ context 'when I pick multiple embedded carts' do
134
+ let(:arguments) { ['app', 'create', 'app1', 'unique_standalone', 'mock_cart', '--trace', '--noprompt'] }
135
+ it { expect { run }.should raise_error(RHC::MultipleCartridgesException, /There are multiple cartridges matching 'mock_cart'/) }
136
+ end
137
+ context 'when I pick multiple standalone carts' do
138
+ let(:arguments) { ['app', 'create', 'app1', 'unique_standalone', 'mock_standalone_cart', '--trace', '--noprompt'] }
139
+ it { expect { run }.should raise_error(RHC::MultipleCartridgesException, /You must select only a single web cart/) }
140
+ end
106
141
  end
107
142
 
108
143
  describe 'app create enable-jenkins' do
@@ -381,6 +416,58 @@ describe RHC::Commands::App do
381
416
  end
382
417
  end
383
418
 
419
+ describe 'app ssh' do
420
+ let(:arguments) { ['app', 'ssh', 'app1'] }
421
+
422
+ context 'when run' do
423
+ before(:each) do
424
+ @domain = rest_client.add_domain("mockdomain")
425
+ @domain.add_application("app1", "mock_type")
426
+ Kernel.should_receive(:system).with("ssh fakeuuidfortestsapp1@127.0.0.1").and_return(0)
427
+ end
428
+ it { run_output.should match("Connecting to fakeuuidfortestsapp") }
429
+ it { expect { run }.should exit_with_code(0) }
430
+ end
431
+ end
432
+
433
+ describe 'app ssh no system ssh' do
434
+ let(:arguments) { ['app', 'ssh', 'app1'] }
435
+
436
+ context 'when run' do
437
+ before(:each) do
438
+ @domain = rest_client.add_domain("mockdomain")
439
+ @domain.add_application("app1", "mock_type")
440
+ @instance.should_receive(:has_ssh?).and_return(false)
441
+ end
442
+ it { run_output.should match("Please use the --ssh option to specify the path to your SSH executable, or install SSH.") }
443
+ it { expect { run }.should exit_with_code(1) }
444
+ end
445
+ end
446
+
447
+ describe 'app ssh can use system exec' do
448
+ let(:arguments) { ['app', 'ssh', 'app1', '--ssh', 'path_to_ssh'] }
449
+
450
+ context 'when run' do
451
+ before(:each) do
452
+ @domain = rest_client.add_domain("mockdomain")
453
+ @domain.add_application("app1", "mock_type")
454
+ @instance.should_not_receive(:has_ssh?)
455
+ Kernel.should_receive(:system).with("path_to_ssh fakeuuidfortestsapp1@127.0.0.1").and_return(1)
456
+ end
457
+ it { run_output.should match("Connecting to fakeuuidfortestsapp") }
458
+ it { expect { run }.should exit_with_code(1) }
459
+ end
460
+ end
461
+
462
+ describe 'ssh tests' do
463
+ let(:arguments) { ['app', 'ssh', 'app1', '-s /bin/blah'] }
464
+
465
+ context 'has_ssh?' do
466
+ before{ @instance.stub(:ssh_version){ raise "Fake Exception" } }
467
+ its(:has_ssh?) { should be_false }
468
+ end
469
+ end
470
+
384
471
  describe 'app status' do
385
472
  let(:arguments) { ['app', 'status', 'app1', '--noprompt', '--config', 'test.conf', '-l', 'test@test.foo', '-p', 'password'] }
386
473