rhc 1.30.3 → 1.31.5

Sign up to get free protection for your applications and to get access to all the features.
@@ -10,9 +10,9 @@ _rhc()
10
10
  if [[ "$cur" == -* ]]; then
11
11
  opts="--always-prefix --clean --config --debug --header --insecure --limit --mock --noprompt --password --raw --rhlogin --server --ssl-ca-file --ssl-client-cert-file --ssl-client-key-file --ssl-version --timeout --token"
12
12
  elif [ -z $cur ]; then
13
- opts="account alias alias-add alias-delete-cert alias-list alias-remove alias-update-cert app app-configure app-create app-delete app-deploy app-force-stop app-reload app-restart app-scale-down app-scale-up app-show app-start app-stop app-tidy apps authorization authorization-add authorization-delete authorization-delete-all authorization-list cartridge cartridge-add cartridge-list cartridge-reload cartridge-remove cartridge-restart cartridge-scale cartridge-show cartridge-start cartridge-status cartridge-stop cartridge-storage deployment deployment-activate deployment-list deployment-show domain domain-configure domain-create domain-delete domain-leave domain-list domain-rename domain-show env env-list env-set env-show env-unset git-clone help logout member member-add member-list member-remove member-update port-forward region region-list scp server server-add server-configure server-list server-remove server-show server-status server-use setup snapshot snapshot-restore snapshot-save ssh sshkey sshkey-add sshkey-list sshkey-remove sshkey-show tail team team-create team-delete team-leave team-list team-show threaddump"
13
+ opts="account alias alias-add alias-delete-cert alias-list alias-remove alias-update-cert app app-configure app-create app-delete app-deploy app-enable-ha app-force-stop app-reload app-restart app-scale-down app-scale-up app-show app-start app-stop app-tidy apps authorization authorization-add authorization-delete authorization-delete-all authorization-list cartridge cartridge-add cartridge-list cartridge-reload cartridge-remove cartridge-restart cartridge-scale cartridge-show cartridge-start cartridge-status cartridge-stop cartridge-storage deployment deployment-activate deployment-list deployment-show domain domain-configure domain-create domain-delete domain-leave domain-list domain-rename domain-show env env-list env-set env-show env-unset git-clone help logout member member-add member-list member-remove member-update port-forward region region-list scp server server-add server-configure server-list server-remove server-show server-status server-use setup snapshot snapshot-restore snapshot-save ssh sshkey sshkey-add sshkey-list sshkey-remove sshkey-show tail team team-create team-delete team-leave team-list team-show threaddump"
14
14
  else
15
- opts="account account-logout activate-deployment add-alias add-authorization add-cartridge add-member add-server add-sshkey alias alias-add alias-delete-cert alias-list alias-remove alias-update-cert aliases app app-configure app-create app-delete app-deploy app-env app-force-stop app-reload app-restart app-scale-down app-scale-up app-scp app-show app-snapshot app-ssh app-start app-stop app-tidy apps authorization authorization-add authorization-delete authorization-delete-all authorization-list authorizations cartridge cartridge-add cartridge-list cartridge-reload cartridge-remove cartridge-restart cartridge-scale cartridge-show cartridge-start cartridge-status cartridge-stop cartridge-storage cartridges configure-app configure-domain configure-server create-app create-domain create-team delete-all-authorization delete-app delete-authorization delete-cert-alias delete-domain delete-team deploy deploy-app deployment deployment-activate deployment-list deployment-show deployments domain domain-configure domain-create domain-delete domain-leave domain-list domain-rename domain-show domains env env-add env-list env-remove env-set env-show env-unset envs force-stop-app git-clone help leave-domain leave-team list-alias list-authorization list-cartridge list-deployment list-domain list-env list-member list-region list-server list-sshkey list-team logout member member-add member-list member-remove member-update members port-forward region region-list regions reload-app reload-cartridge remove-alias remove-cartridge remove-member remove-server remove-sshkey rename-domain restart-app restart-cartridge restore-snapshot save-snapshot scale-cartridge scale-down-app scale-up-app scp server server-add server-configure server-list server-remove server-show server-status server-use servers set-env setup show-app show-cartridge show-deployment show-domain show-env show-server show-sshkey show-team snapshot snapshot-restore snapshot-save ssh sshkey sshkey-add sshkey-list sshkey-remove sshkey-show sshkeys start-app start-cartridge status-cartridge status-server stop-app stop-cartridge storage-cartridge tail team team-create team-delete team-leave team-list team-show teams threaddump tidy-app unset-env update-cert-alias update-member use-server"
15
+ opts="account account-logout activate-deployment add-alias add-authorization add-cartridge add-member add-server add-sshkey alias alias-add alias-delete-cert alias-list alias-remove alias-update-cert aliases app app-configure app-create app-delete app-deploy app-enable-ha app-env app-force-stop app-reload app-restart app-scale-down app-scale-up app-scp app-show app-snapshot app-ssh app-start app-stop app-tidy apps authorization authorization-add authorization-delete authorization-delete-all authorization-list authorizations cartridge cartridge-add cartridge-list cartridge-reload cartridge-remove cartridge-restart cartridge-scale cartridge-show cartridge-start cartridge-status cartridge-stop cartridge-storage cartridges configure-app configure-domain configure-server create-app create-domain create-team delete-all-authorization delete-app delete-authorization delete-cert-alias delete-domain delete-team deploy deploy-app deployment deployment-activate deployment-list deployment-show deployments domain domain-configure domain-create domain-delete domain-leave domain-list domain-rename domain-show domains enable-ha-app env env-add env-list env-remove env-set env-show env-unset envs force-stop-app git-clone help leave-domain leave-team list-alias list-authorization list-cartridge list-deployment list-domain list-env list-member list-region list-server list-sshkey list-team logout member member-add member-list member-remove member-update members port-forward region region-list regions reload-app reload-cartridge remove-alias remove-cartridge remove-member remove-server remove-sshkey rename-domain restart-app restart-cartridge restore-snapshot save-snapshot scale-cartridge scale-down-app scale-up-app scp server server-add server-configure server-list server-remove server-show server-status server-use servers set-env setup show-app show-cartridge show-deployment show-domain show-env show-server show-sshkey show-team snapshot snapshot-restore snapshot-save ssh sshkey sshkey-add sshkey-list sshkey-remove sshkey-show sshkeys start-app start-cartridge status-cartridge status-server stop-app stop-cartridge storage-cartridge tail team team-create team-delete team-leave team-list team-show teams threaddump tidy-app unset-env update-cert-alias update-member use-server"
16
16
  fi
17
17
  else
18
18
  prev="${COMP_WORDS[@]:0:COMP_CWORD}"
@@ -200,7 +200,7 @@ _rhc()
200
200
  if [[ "$cur" == -* ]]; then
201
201
  opts=""
202
202
  else
203
- opts="configure create delete deploy env force-stop reload restart scale-down scale-up show snapshot start stop tidy"
203
+ opts="configure create delete deploy enable-ha env force-stop reload restart scale-down scale-up show snapshot start stop tidy"
204
204
  fi
205
205
  ;;
206
206
 
@@ -236,6 +236,14 @@ _rhc()
236
236
  fi
237
237
  ;;
238
238
 
239
+ "rhc app enable-ha")
240
+ if [[ "$cur" == -* ]]; then
241
+ opts="--app --application-id --namespace"
242
+ else
243
+ opts=""
244
+ fi
245
+ ;;
246
+
239
247
  "rhc app env")
240
248
  if [[ "$cur" == -* ]]; then
241
249
  opts="--app --application-id --confirm --env --namespace"
@@ -372,6 +380,14 @@ _rhc()
372
380
  fi
373
381
  ;;
374
382
 
383
+ "rhc app-enable-ha")
384
+ if [[ "$cur" == -* ]]; then
385
+ opts="--app --application-id --namespace"
386
+ else
387
+ opts=""
388
+ fi
389
+ ;;
390
+
375
391
  "rhc app-env")
376
392
  if [[ "$cur" == -* ]]; then
377
393
  opts="--app --application-id --confirm --env --namespace"
@@ -1060,6 +1076,14 @@ _rhc()
1060
1076
  fi
1061
1077
  ;;
1062
1078
 
1079
+ "rhc enable-ha-app")
1080
+ if [[ "$cur" == -* ]]; then
1081
+ opts="--app --application-id --namespace"
1082
+ else
1083
+ opts=""
1084
+ fi
1085
+ ;;
1086
+
1063
1087
  "rhc env")
1064
1088
  if [[ "$cur" == -* ]]; then
1065
1089
  opts=""
@@ -1192,7 +1216,7 @@ _rhc()
1192
1216
  if [[ "$cur" == -* ]]; then
1193
1217
  opts=""
1194
1218
  else
1195
- opts="account account-logout activate-deployment add-alias add-authorization add-cartridge add-member add-server add-sshkey alias alias-add alias-delete-cert alias-list alias-remove alias-update-cert aliases app app-configure app-create app-delete app-deploy app-env app-force-stop app-reload app-restart app-scale-down app-scale-up app-scp app-show app-snapshot app-ssh app-start app-stop app-tidy apps authorization authorization-add authorization-delete authorization-delete-all authorization-list authorizations cartridge cartridge-add cartridge-list cartridge-reload cartridge-remove cartridge-restart cartridge-scale cartridge-show cartridge-start cartridge-status cartridge-stop cartridge-storage cartridges configure-app configure-domain configure-server create-app create-domain create-team delete-all-authorization delete-app delete-authorization delete-cert-alias delete-domain delete-team deploy deploy-app deployment deployment-activate deployment-list deployment-show deployments domain domain-configure domain-create domain-delete domain-leave domain-list domain-rename domain-show domains env env-add env-list env-remove env-set env-show env-unset envs force-stop-app git-clone leave-domain leave-team list-alias list-authorization list-cartridge list-deployment list-domain list-env list-member list-region list-server list-sshkey list-team logout member member-add member-list member-remove member-update members port-forward region region-list regions reload-app reload-cartridge remove-alias remove-cartridge remove-member remove-server remove-sshkey rename-domain restart-app restart-cartridge restore-snapshot save-snapshot scale-cartridge scale-down-app scale-up-app scp server server-add server-configure server-list server-remove server-show server-status server-use servers set-env setup show-app show-cartridge show-deployment show-domain show-env show-server show-sshkey show-team snapshot snapshot-restore snapshot-save ssh sshkey sshkey-add sshkey-list sshkey-remove sshkey-show sshkeys start-app start-cartridge status-cartridge status-server stop-app stop-cartridge storage-cartridge tail team team-create team-delete team-leave team-list team-show teams threaddump tidy-app unset-env update-cert-alias update-member use-server"
1219
+ opts="account account-logout activate-deployment add-alias add-authorization add-cartridge add-member add-server add-sshkey alias alias-add alias-delete-cert alias-list alias-remove alias-update-cert aliases app app-configure app-create app-delete app-deploy app-enable-ha app-env app-force-stop app-reload app-restart app-scale-down app-scale-up app-scp app-show app-snapshot app-ssh app-start app-stop app-tidy apps authorization authorization-add authorization-delete authorization-delete-all authorization-list authorizations cartridge cartridge-add cartridge-list cartridge-reload cartridge-remove cartridge-restart cartridge-scale cartridge-show cartridge-start cartridge-status cartridge-stop cartridge-storage cartridges configure-app configure-domain configure-server create-app create-domain create-team delete-all-authorization delete-app delete-authorization delete-cert-alias delete-domain delete-team deploy deploy-app deployment deployment-activate deployment-list deployment-show deployments domain domain-configure domain-create domain-delete domain-leave domain-list domain-rename domain-show domains enable-ha-app env env-add env-list env-remove env-set env-show env-unset envs force-stop-app git-clone leave-domain leave-team list-alias list-authorization list-cartridge list-deployment list-domain list-env list-member list-region list-server list-sshkey list-team logout member member-add member-list member-remove member-update members port-forward region region-list regions reload-app reload-cartridge remove-alias remove-cartridge remove-member remove-server remove-sshkey rename-domain restart-app restart-cartridge restore-snapshot save-snapshot scale-cartridge scale-down-app scale-up-app scp server server-add server-configure server-list server-remove server-show server-status server-use servers set-env setup show-app show-cartridge show-deployment show-domain show-env show-server show-sshkey show-team snapshot snapshot-restore snapshot-save ssh sshkey sshkey-add sshkey-list sshkey-remove sshkey-show sshkeys start-app start-cartridge status-cartridge status-server stop-app stop-cartridge storage-cartridge tail team team-create team-delete team-leave team-list team-show teams threaddump tidy-app unset-env update-cert-alias update-member use-server"
1196
1220
  fi
1197
1221
  ;;
1198
1222
 
@@ -115,6 +115,11 @@ module RHC::Commands
115
115
  scaling = from_app.scalable if scaling.nil?
116
116
  region = from_app.region if region.nil?
117
117
 
118
+ if region.present? && !rest_client.allows_region_selection?
119
+ region = nil
120
+ warn 'Server does not allow selecting regions. Region is being ignored.'
121
+ end
122
+
118
123
  cartridges = from_app.cartridges.reject{|c| c.tags.include?('web_proxy')}.collect do |cartridge|
119
124
  {
120
125
  :name => (cartridge.name if !cartridge.custom?),
@@ -356,6 +361,16 @@ module RHC::Commands
356
361
  0
357
362
  end
358
363
 
364
+ summary "Make the application highly available"
365
+ syntax "<app> [--namespace NAME]"
366
+ takes_application :argument => true
367
+ def enable_ha(app)
368
+ app_action :enable_ha
369
+
370
+ results { say "#{app} is now highly available" }
371
+ 0
372
+ end
373
+
359
374
  summary "Show information about an application"
360
375
  description <<-DESC
361
376
  Display the properties of an application, including its URL, the SSH
@@ -23,7 +23,7 @@ module RHC::Commands
23
23
 
24
24
  0
25
25
  else
26
- error "You do not have git installed. In order to fully interact with OpenShift you will need to install and configure a git client."
26
+ error "You do not have git installed. In order to fully interact with OpenShift you will need to install and configure a git client.#{RHC::Helpers.windows? ? ' We recommend this free application: Git for Windows - a basic git command line and GUI client http://msysgit.github.io/.' : ''}"
27
27
  2
28
28
  end
29
29
  end
@@ -39,7 +39,7 @@ module RHC::Commands
39
39
 
40
40
  debug "Using user specified SSH: #{options.ssh}" if options.ssh
41
41
 
42
- command_line = [ ssh.split, ('-vvv' if debug?), rest_app.ssh_string.to_s, command ].flatten.compact
42
+ command_line = [RHC::Helpers.split_path(ssh), ('-vvv' if debug?), rest_app.ssh_string.to_s, command].flatten.compact
43
43
 
44
44
  debug "Invoking Kernel.exec with #{command_line.inspect}"
45
45
  Kernel.send(:exec, *command_line)
@@ -313,4 +313,10 @@ module RHC
313
313
  super message, 1
314
314
  end
315
315
  end
316
+
317
+ class HighAvailabilityNotSupportedException < Exception
318
+ def initialize(message="The server does not support high availability")
319
+ super message, 135
320
+ end
321
+ end
316
322
  end
@@ -7,18 +7,41 @@ module RHC
7
7
  "git"
8
8
  end
9
9
 
10
- def git_version
11
- @git_version ||= `#{git_cmd} --version 2>&1`.strip #:nocov:
10
+ def git_version(cmd=discover_git_executable)
11
+ `"#{cmd}" --version 2>&1`.strip
12
12
  end
13
13
 
14
14
  def has_git?
15
- @has_git ||= begin
16
- @git_version = nil
17
- git_version
18
- $?.success?
19
- rescue
20
- false
15
+ discover_git_executable.present?
16
+ end
17
+
18
+ # try my best to discover a git executable
19
+ def discover_git_executable
20
+ @git_executable ||= begin
21
+ guessing_locations = [git_cmd]
22
+
23
+ #:nocov:
24
+ if RHC::Helpers.windows?
25
+ guessing_locations <<
26
+ discover_windows_executables do |base|
27
+ [
28
+ "git.exe",
29
+ "#{base}\\Git\\bin\\git.exe",
30
+ "#{base}\\git.exe",
31
+ ]
32
+ end
21
33
  end
34
+
35
+ # make sure commands can be executed and finally pick the first one
36
+ guessing_locations.flatten.uniq.select do |cmd|
37
+ ((File.exist?(cmd) && File.executable?(cmd)) || exe?(cmd)) &&
38
+ (begin
39
+ git_version(cmd)
40
+ $?.success?
41
+ rescue ; false ; end)
42
+ end.collect{|cmd| cmd =~ / / ? '"' + cmd + '"' : cmd}.first
43
+ #:nocov:
44
+ end
22
45
  end
23
46
 
24
47
  def git_clone_deploy_hooks(repo_dir)
@@ -50,18 +73,18 @@ module RHC
50
73
  dir
51
74
  end
52
75
 
76
+ # :nocov: These all call external binaries so test them in cucumber
53
77
  def git_remote_add(remote_name, remote_url)
54
- cmd = "#{git_cmd} remote add upstream \"#{remote_url}\""
78
+ cmd = "#{discover_git_executable} remote add upstream \"#{remote_url}\""
55
79
  debug "Running #{cmd} 2>&1"
56
80
  output = %x[#{cmd} 2>&1]
57
81
  raise RHC::GitException, "Error while adding upstream remote - #{output}" unless output.empty?
58
82
  end
59
83
 
60
- # :nocov: These all call external binaries so test them in cucumber
61
84
  def git_config_get(key)
62
85
  return nil unless has_git?
63
86
 
64
- config_get_cmd = "#{git_cmd} config --get #{key}"
87
+ config_get_cmd = "#{discover_git_executable} config --get #{key}"
65
88
  value = %x[#{config_get_cmd}].strip
66
89
  debug "Git config '#{config_get_cmd}' returned '#{value}'"
67
90
  value = nil if $?.exitstatus != 0 or value.empty?
@@ -70,8 +93,8 @@ module RHC
70
93
  end
71
94
 
72
95
  def git_config_set(key, value)
73
- unset_cmd = "#{git_cmd} config --unset-all #{key}"
74
- config_cmd = "#{git_cmd} config --add #{key} #{value}"
96
+ unset_cmd = "#{discover_git_executable} config --unset-all #{key}"
97
+ config_cmd = "#{discover_git_executable} config --add #{key} #{value}"
75
98
  debug "Adding #{key} = #{value} to git config"
76
99
  commands = [unset_cmd, config_cmd]
77
100
  commands.each do |cmd|
@@ -85,7 +108,7 @@ module RHC
85
108
  def git_clone_repo(git_url, repo_dir)
86
109
  # quote the repo to avoid input injection risk
87
110
  destination = (repo_dir ? " \"#{repo_dir}\"" : "")
88
- cmd = "#{git_cmd} clone #{git_url}#{destination}"
111
+ cmd = "#{discover_git_executable} clone #{git_url}#{destination}"
89
112
  debug "Running #{cmd}"
90
113
 
91
114
  status, stdout, stderr = run_with_tee(cmd)
@@ -4,7 +4,7 @@ require 'rhc/config'
4
4
  require 'rhc/output_helpers'
5
5
  require 'rhc/server_helpers'
6
6
  require 'rbconfig'
7
-
7
+ require 'csv'
8
8
  require 'resolv'
9
9
 
10
10
  OptionParser.accept(URI) {|s,| URI.parse(s) if s}
@@ -517,6 +517,68 @@ module RHC
517
517
  s.is_a?(String) ? !!(s =~ /^(true|t|yes|y|1)$/i) : s
518
518
  end
519
519
 
520
+ # split spaces but preserve sentences between quotes
521
+ def split_path(s, keep_quotes=false)
522
+ keep_quotes ? s.split(/\s(?=(?:[^"]|"[^"]*")*$)/) : CSV::parse_line(s, :col_sep => ' ')
523
+ end
524
+
525
+ def discover_windows_executables(&block)
526
+ #:nocov:
527
+ if RHC::Helpers.windows?
528
+
529
+ base_path = []
530
+ guessing_locations = []
531
+
532
+ # Look for the base path int the ProgramFiles env var...
533
+ if program_files = ENV['ProgramFiles']; program_files.present? && File.exists?(program_files)
534
+ base_path << program_files
535
+ end
536
+
537
+ # ... and in win32ole drives (drive root or Program Files)
538
+ begin
539
+ require 'win32ole'
540
+ WIN32OLE.new("Scripting.FileSystemObject").tap do |file_system|
541
+ file_system.Drives.each do |drive|
542
+ base_path << [
543
+ "#{drive.DriveLetter}:\\Program Files (x86)",
544
+ "#{drive.DriveLetter}:\\Program Files",
545
+ "#{drive.DriveLetter}:\\Progra~1",
546
+ "#{drive.DriveLetter}:"
547
+ ]
548
+ end
549
+ end
550
+ rescue
551
+ end
552
+
553
+ # executables array from block
554
+ executable_groups = []
555
+ base_path.flatten.uniq.each do |base|
556
+ executable_groups << yield(base)
557
+ end
558
+
559
+ # give proper priorities
560
+ unless executable_groups.empty?
561
+ length = executable_groups.first.length
562
+ for i in 1..length do
563
+ executable_groups.each do |group|
564
+ guessing_locations << group[i - 1]
565
+ end
566
+ end
567
+ end
568
+
569
+ guessing_locations.flatten.uniq.select {|cmd| File.exist?(cmd) && File.executable?(cmd)}
570
+ else
571
+ []
572
+ end
573
+ #:nocov:
574
+ end
575
+
576
+ def exe?(executable)
577
+ ENV['PATH'].split(File::PATH_SEPARATOR).any? do |directory|
578
+ File.executable?(File.join(directory, executable.to_s))
579
+ end
580
+ end
581
+
520
582
  BOUND_WARNING = self.method(:warn).to_proc
521
583
  end
522
584
  end
@@ -110,6 +110,11 @@ module RHC
110
110
  rest_method 'TIDY', :event => "tidy"
111
111
  end
112
112
 
113
+ def enable_ha
114
+ raise RHC::HighAvailabilityNotSupportedException.new unless supports? "MAKE_HA"
115
+ rest_method 'MAKE_HA', :event => "make-ha"
116
+ end
117
+
113
118
  def scale_up
114
119
  rest_method 'SCALE_UP', :event => "scale-up"
115
120
  end
@@ -285,6 +285,16 @@ module RHC
285
285
  api.supports? :LIST_REGIONS
286
286
  end
287
287
 
288
+ #
289
+ # allows_region_selection? determines if the broker will allow a client
290
+ # to specify the region for an app
291
+ # @returns true if allowed; nil otherwise
292
+ #
293
+ def allows_region_selection?
294
+ supported_regions = regions rescue []
295
+ supported_regions.any? { |region| region.allow_selection == true }
296
+ end
297
+
288
298
  def authorizations
289
299
  raise AuthorizationsNotSupported unless supports_sessions?
290
300
  api.rest_method 'LIST_AUTHORIZATIONS'
@@ -426,7 +426,8 @@ module RHC::Rest::Mock
426
426
  ['LIST_DEPLOYMENTS', "broker/rest/domain/#{domain_id}/application/#{app_id}/deployments", 'get' ],
427
427
  ['UPDATE_DEPLOYMENTS', "broker/rest/domain/#{domain_id}/application/#{app_id}/deployments", 'post' ],
428
428
  ['ACTIVATE', "broker/rest/domain/#{domain_id}/application/#{app_id}/events", 'post'],
429
- ['DEPLOY', "broker/rest/domain/#{domain_id}/application/#{app_id}/deployments", 'post']
429
+ ['DEPLOY', "broker/rest/domain/#{domain_id}/application/#{app_id}/deployments", 'post'],
430
+ ['MAKE_HA', "broker/rest/application/#{app_id}/events", 'make-ha']
430
431
  ].compact
431
432
  end
432
433
 
@@ -764,15 +765,16 @@ module RHC::Rest::Mock
764
765
  @applications = nil
765
766
  end
766
767
 
767
- def add_application(name, type=nil, scale=nil, gear_profile='default', git_url=nil)
768
+ def add_application(name, type=nil, scale=nil, gear_profile='default', git_url=nil, region=nil)
768
769
  if type.is_a?(Hash)
769
770
  scale = type[:scale]
770
771
  gear_profile = type[:gear_profile]
771
772
  git_url = type[:initial_git_url]
772
773
  tags = type[:tags]
774
+ region = type[:region]
773
775
  type = Array(type[:cartridges] || type[:cartridge])
774
776
  end
775
- a = MockRestApplication.new(client, name, type, self, scale, gear_profile, git_url)
777
+ a = MockRestApplication.new(client, name, type, self, scale, gear_profile, git_url, nil, region)
776
778
  builder = @applications.find{ |app| app.cartridges.map(&:name).any?{ |s| s =~ /^jenkins-[\d\.]+$/ } }
777
779
  a.building_app = builder.name if builder
778
780
  @applications << a
@@ -847,11 +849,13 @@ module RHC::Rest::Mock
847
849
 
848
850
  class MockRestApplication < RHC::Rest::Application
849
851
  include Helpers
852
+ attr_writer :region
853
+
850
854
  def fakeuuid
851
855
  "fakeuuidfortests#{@name}"
852
856
  end
853
857
 
854
- def initialize(client, name, type, domain, scale=nil, gear_profile='default', initial_git_url=nil, environment_variables=nil)
858
+ def initialize(client, name, type, domain, scale=nil, gear_profile='default', initial_git_url=nil, environment_variables=nil, region=nil)
855
859
  super({}, client)
856
860
  @name = name
857
861
  @domain = domain
@@ -870,7 +874,7 @@ module RHC::Rest::Mock
870
874
  if scale
871
875
  @scalable = true
872
876
  end
873
- @region = nil
877
+ @region = region
874
878
  self.attributes = {:links => mock_response_links(mock_app_links('mock_domain_0', 'mock_app_0')), :messages => []}
875
879
  self.gear_count = 5
876
880
  types = Array(type)
@@ -954,6 +958,14 @@ module RHC::Rest::Mock
954
958
  @app
955
959
  end
956
960
 
961
+ def enable_ha
962
+ if supports? "MAKE_HA"
963
+ @app
964
+ else
965
+ raise RHC::HighAvailabilityNotSupportedException.new
966
+ end
967
+ end
968
+
957
969
  def scale_up
958
970
  @app
959
971
  end
@@ -21,7 +21,6 @@ require 'httpclient'
21
21
 
22
22
  module RHC
23
23
  module SSHHelpers
24
-
25
24
  class MultipleGearTask
26
25
  def initialize(command, over, opts={})
27
26
  requires_ssh_multi!
@@ -373,12 +372,6 @@ module RHC
373
372
  pub_key
374
373
  end
375
374
 
376
- def exe?(executable)
377
- ENV['PATH'].split(File::PATH_SEPARATOR).any? do |directory|
378
- File.executable?(File.join(directory, executable.to_s))
379
- end
380
- end
381
-
382
375
  # For Net::SSH versions (< 2.0.11) that does not have
383
376
  # Net::SSH::KeyFactory.load_public_key, we drop to shell to get
384
377
  # the key's fingerprint
@@ -426,18 +419,51 @@ module RHC
426
419
  end
427
420
 
428
421
  # check the version of SSH that is installed
429
- def ssh_version
430
- @ssh_version ||= `ssh -V 2>&1`.strip
422
+ def ssh_version(cmd=discover_ssh_executable)
423
+ `"#{cmd}" -V 2>&1`.strip
431
424
  end
432
425
 
433
426
  # return whether or not SSH is installed
434
427
  def has_ssh?
435
- @has_ssh ||= begin
436
- @ssh_version = nil
437
- ssh_version
438
- $?.success?
439
- rescue
440
- false
428
+ discover_ssh_executable.present?
429
+ end
430
+
431
+ # try my best to discover a ssh executable
432
+ def discover_ssh_executable
433
+ @ssh_executable ||= begin
434
+ guessing_locations = ['ssh']
435
+
436
+ #:nocov:
437
+ if RHC::Helpers.windows?
438
+ # looks for ssh.exe from msysgit or plink.exe from PuTTY, either on path or specific locations
439
+ guessing_locations <<
440
+ discover_windows_executables do |base|
441
+ [
442
+ 'ssh.exe',
443
+ "#{base}\\Git\\bin\\ssh.exe",
444
+ "#{base}\\ssh.exe",
445
+ 'plink.exe',
446
+ "#{base}\\PuTTY\\plink.exe",
447
+ "#{base}\\plink.exe",
448
+ 'putty.exe',
449
+ "#{base}\\PuTTY\\putty.exe",
450
+ "#{base}\\putty.exe"
451
+ ]
452
+ end
453
+ end
454
+ #:nocov:
455
+
456
+ # make sure commands can be executed and finally pick the first one
457
+ guessing_locations.flatten.uniq.select do |cmd|
458
+ (check_ssh_executable!(cmd).present? rescue false) &&
459
+ (begin
460
+ # putty -V exit as 1
461
+ cmd =~ /plink\.exe/i || cmd =~ /putty\.exe/i || (begin
462
+ ssh_version(cmd)
463
+ $?.success?
464
+ end)
465
+ rescue ; false ; end)
466
+ end.collect{|cmd| cmd =~ / / ? '"' + cmd + '"' : cmd}.first
441
467
  end
442
468
  end
443
469
 
@@ -445,13 +471,14 @@ module RHC
445
471
  # if none was supplied, return installed ssh, if any.
446
472
  def check_ssh_executable!(path)
447
473
  if not path
448
- raise RHC::InvalidSSHExecutableException.new("No system SSH available. Please use the --ssh option to specify the path to your SSH executable, or install SSH.") unless has_ssh?
449
- 'ssh'
474
+ discover_ssh_executable.tap do |ssh_cmd|
475
+ raise RHC::InvalidSSHExecutableException.new("No system SSH available. Please use the --ssh option to specify the path to your SSH executable, or install SSH.#{windows? ? ' We recommend this free application: Git for Windows - a basic git command line and GUI client http://msysgit.github.io/.' : ''}") unless ssh_cmd or has_ssh?
476
+ end
450
477
  else
451
478
  bin_path = path.split(' ').first
452
- raise RHC::InvalidSSHExecutableException.new("SSH executable '#{bin_path}' does not exist.") unless File.exist?(bin_path) or exe?(bin_path)
453
- raise RHC::InvalidSSHExecutableException.new("SSH executable '#{bin_path}' is not executable.") unless File.executable?(bin_path) or exe?(bin_path)
454
- path
479
+ raise RHC::InvalidSSHExecutableException.new("SSH executable '#{bin_path}' does not exist.") unless File.exist?(bin_path) or File.exist?(path) or exe?(bin_path)
480
+ raise RHC::InvalidSSHExecutableException.new("SSH executable '#{bin_path}' is not executable.") unless File.executable?(path) or File.executable?(bin_path) or exe?(bin_path)
481
+ path =~ / / ? '"' + path + '"' : path
455
482
  end
456
483
  end
457
484
 
@@ -182,7 +182,7 @@ module RHC
182
182
  end
183
183
 
184
184
  def server_stage
185
- paragraph do
185
+ paragraph do
186
186
  unless options.__explicit__[:server]
187
187
  say "If you have your own OpenShift server, you can specify it now. Just hit enter to use#{openshift_online_server? ? ' the server for OpenShift Online' : ''}: #{openshift_server}."
188
188
  options.server = ask "Enter the server hostname: " do |q|
@@ -236,7 +236,7 @@ module RHC
236
236
  elsif rest_client.supports_sessions? && !options.token
237
237
  paragraph do
238
238
  info "OpenShift can create and store a token on disk which allows to you to access the server without using your password. The key is stored in your home directory and should be kept secret. You can delete the key at any time by running 'rhc logout'."
239
- if agree "Generate a token now? (yes|no) "
239
+ if options.create_token or agree "Generate a token now? (yes|no) "
240
240
  say "Generating an authorization token for this client ... "
241
241
  token = rest_client.new_session
242
242
  options.token = token.token
@@ -287,8 +287,8 @@ module RHC
287
287
  if write_servers_yml
288
288
  say "Saving server configuration to #{system_path(servers.path)} ... "
289
289
  servers.backup
290
- servers.add_or_update(options.server,
291
- :login => options.rhlogin,
290
+ servers.add_or_update(options.server,
291
+ :login => options.rhlogin,
292
292
  :use_authorization_tokens => options.use_authorization_tokens,
293
293
  :insecure => options.insecure,
294
294
  :timeout => options.timeout,
@@ -605,7 +605,7 @@ module RHC
605
605
  end
606
606
 
607
607
  def generic_unix_install_check
608
- paragraph do
608
+ paragraph do
609
609
  say "Checking for git ... "
610
610
 
611
611
  if has_git?
@@ -623,21 +623,18 @@ module RHC
623
623
  end
624
624
 
625
625
  def windows_install
626
- # Finding windows executables is hard since they can get installed
627
- # in non standard directories. Punt on this for now and simply
628
- # print out urls and some instructions
629
- warn <<EOF
630
-
631
- In order to fully interact with OpenShift you will need to install and configure a git client if you have not already done so.
626
+ unless discover_ssh_executable.present? && discover_git_executable.present?
627
+ warn <<EOF
632
628
 
633
- Documentation for installing other tools you will need for OpenShift can be found at https://www.openshift.com/developers/install-the-client-tools
629
+ In order to fully interact with OpenShift you will need to install and configure a git client if you have not already done so. Documentation for installing other tools you will need for OpenShift can be found at https://www.openshift.com/developers/install-the-client-tools
634
630
 
635
631
  We recommend these free applications:
636
632
 
637
- * Git for Windows - a basic git command line and GUI client https://github.com/msysgit/msysgit/wiki/InstallMSysGit
633
+ * Git for Windows - a basic git command line and GUI client http://msysgit.github.io/
638
634
  * TortoiseGit - git client that integrates into the file explorer http://code.google.com/p/tortoisegit/
639
-
635
+
640
636
  EOF
637
+ end
641
638
  end
642
639
  end
643
640
 
@@ -163,6 +163,7 @@ describe RHC::Commands::App do
163
163
  let!(:config){ base_config }
164
164
  before{ RHC::Config.any_instance.stub(:has_local_config?).and_return(false) }
165
165
  before{ described_class.any_instance.stub(:interactive?).and_return(true) }
166
+ before{ described_class.any_instance.stub(:discover_git_executable).and_return('git') }
166
167
  before{ rest_client.domains.clear }
167
168
  before{ rest_client.sshkeys.delete_if {|k| !k.is_ssh? } }
168
169
  let(:arguments) { ['app', 'create', 'app1', 'mock_standalone_cart-1'] }
@@ -406,6 +407,7 @@ describe RHC::Commands::App do
406
407
  @domain = rest_client.add_domain("mockdomain")
407
408
  @instance.stub(:git_clone_application) { raise RHC::GitException }
408
409
  @instance.stub(:check_sshkeys!)
410
+ @instance.stub(:discover_git_executable).and_return('git')
409
411
  end
410
412
 
411
413
  context 'when run with error in git clone' do
@@ -428,6 +430,7 @@ describe RHC::Commands::App do
428
430
  RHC::Helpers.stub(:windows?) { true }
429
431
  @instance.stub(:run_nslookup) { true }
430
432
  @instance.stub(:run_ping) { true }
433
+ @instance.stub(:has_git?) { true }
431
434
  end
432
435
  it "should print out git warning" do
433
436
  run_output.should match(" We were unable to clone your application's git repo")
@@ -439,6 +442,7 @@ describe RHC::Commands::App do
439
442
  RHC::Helpers.stub(:windows?) { true }
440
443
  @instance.stub(:run_nslookup) { true }
441
444
  @instance.stub(:run_ping) { false }
445
+ @instance.stub(:has_git?) { true }
442
446
  end
443
447
  it "should print out windows warning" do
444
448
  run_output.should match("This may also be related to an issue with Winsock on Windows")
@@ -473,26 +477,66 @@ describe RHC::Commands::App do
473
477
  before(:each) do
474
478
  FakeFS.deactivate!
475
479
  @domain = rest_client.add_domain("mockdomain")
476
- @app = @domain.add_application("app1", "mock_standalone_cart-1")
480
+ @app = @domain.add_application("app1", :cartridge=>"mock_standalone_cart-1")
477
481
  @app.add_alias('myfoo.com')
478
482
  @cart1 = @app.add_cartridge('mock_cart-1')
479
483
  @cart2 = @app.add_cartridge('mock_cart-2')
480
484
  @cart2.gear_profile = 'medium'
481
485
  @instance.stub(:save_snapshot)
482
486
  @instance.stub(:restore_snapshot)
487
+ @app.stub(:region){'aRegion'}
488
+ rest_client.api.stub(:supports?).with(:LIST_REGIONS){true}
483
489
  end
484
490
 
485
- context 'when run' do
491
+ context 'when run and the broker allows region selection' do
492
+ before do
493
+ rest_client.stub(:regions) do
494
+ region = double()
495
+ region.stub(:allow_selection){true}
496
+ [region]
497
+ end
498
+ end
499
+
486
500
  let(:arguments) { ['app', 'create', 'clone', '--from-app', 'app1', '--no-git'] }
487
501
  it { expect { run }.to exit_with_code(0) }
488
502
  it "should clone successfully" do
489
503
  run_output.should match(/Cartridges:\s+mock_standalone_cart-1, mock_cart-1, mock_cart-2/)
490
504
  run_output.should match(/Gear Size:\s+Copied from 'app1'/)
491
505
  run_output.should match(/Setting deployment configuration/)
506
+ run_output.should match(/Region:\s+aRegion\s+\(copied from 'app1'/)
492
507
  run_output.should match(/done/)
493
508
  end
494
509
  end
495
510
 
511
+ context 'when source app has a region and the broker does not allow region selection' do
512
+
513
+ context 'and region flag is not specified' do
514
+ let(:arguments) { ['app', 'create', 'app2', '--from-app', 'app1', '--no-git'] }
515
+ it "should clone successfully" do
516
+ expect { run }.to exit_with_code(0)
517
+ run_output.should match(/Server does not allow selecting regions. Region is being ignored./)
518
+ run_output.should match(/Cartridges:\s+mock_standalone_cart-1, mock_cart-1, mock_cart-2/)
519
+ run_output.should match(/Gear Size:\s+Copied from 'app1'/)
520
+ run_output.should_not match(/Region:\s+aRegion\s+\(copied from 'app1'/)
521
+ run_output.should match(/Setting deployment configuration/)
522
+ run_output.should match(/done/)
523
+ end
524
+ end
525
+
526
+ context 'and region flag is specified' do
527
+ let(:arguments) { ['app', 'create', 'app2', '--from-app', 'app1', '--region','foo','--no-git'] }
528
+ it "should clone successfully" do
529
+ run_output.should match(/Server does not allow selecting regions. Region is being ignored./)
530
+ run_output.should match(/Cartridges:\s+mock_standalone_cart-1, mock_cart-1, mock_cart-2/)
531
+ run_output.should match(/Gear Size:\s+Copied from 'app1'/)
532
+ run_output.should_not match(/Region:\s+aRegion\s+\(copied from 'app1'/)
533
+ run_output.should match(/Setting deployment configuration/)
534
+ run_output.should match(/done/)
535
+ expect { run }.to exit_with_code(0)
536
+ end
537
+ end
538
+ end
539
+
496
540
  context 'when cloning a scalable app as not scalable' do
497
541
  before do
498
542
  @scaled = @domain.add_application("scaled", "mock_standalone_cart-1", true)
@@ -848,6 +892,29 @@ describe RHC::Commands::App do
848
892
  it { run_output.should match('cleaned') }
849
893
  it { expect{ run }.to exit_with_code(0) }
850
894
  end
895
+
896
+ context 'app enable-ha' do
897
+ let(:arguments) { ['app', 'enable-ha', 'app1'] }
898
+ it { run_output.should match('is now highly available') }
899
+ it { expect{ run }.to exit_with_code(0) }
900
+ end
901
+
902
+ end
903
+
904
+ describe 'app enable-ha' do
905
+ before do
906
+ @domain = rest_client.add_domain("mockdomain")
907
+ @app = @domain.add_application("app1", "mock_type")
908
+ @app.add_cartridge('mock_cart-1')
909
+ @app.links.delete 'MAKE_HA'
910
+ end
911
+
912
+ let(:arguments) { ['app', 'enable-ha', 'app1'] }
913
+
914
+ it "should raise ha not supported exception" do
915
+ run_output.should match(/The server does not support high availability/)
916
+ expect{ run }.to exit_with_code(135)
917
+ end
851
918
  end
852
919
 
853
920
  describe "#create_app" do
@@ -34,7 +34,7 @@ describe RHC::Commands::GitClone do
34
34
 
35
35
  context 'when run without git installed' do
36
36
  before do
37
- @instance.stub(:has_git?) { false }
37
+ @instance.stub(:discover_git_executable){ nil }
38
38
  end
39
39
  it "should print out git warning" do
40
40
  run_output.should match("You do not have git installed")
@@ -49,6 +49,7 @@ describe RHC::Commands::GitClone do
49
49
  say "Cloned"
50
50
  true
51
51
  end
52
+ @instance.stub(:discover_git_executable){ 'git' }
52
53
  end
53
54
 
54
55
  it { expect { run }.to exit_with_code(0) }
@@ -65,7 +66,35 @@ describe RHC::Commands::GitClone do
65
66
  let(:arguments) { ['git-clone', 'app2'] }
66
67
  it { run_output.should match("Added remote upstream pointing to git://test") }
67
68
  end
69
+ end
70
+
71
+ context "reports success successfully on windows" do
72
+ before do
73
+ RHC::Helpers.stub(:windows?) do ; true; end
74
+ RHC::Helpers.stub(:jruby?) do ; false ; end
75
+ RHC::Helpers.stub(:linux?) do ; false ; end
76
+ @instance.stub(:git_clone_repo) do |git_url, repo_dir|
77
+ Dir::mkdir(repo_dir)
78
+ say "Cloned"
79
+ true
80
+ end
81
+ @instance.stub(:discover_git_executable){ 'git.exe' }
82
+ end
83
+
84
+ it { expect { run }.to exit_with_code(0) }
85
+ it { run_output.should match("Cloned") }
68
86
 
87
+ context 'when app has an initial git url' do
88
+ before do
89
+ @app2 = @domain.add_application("app2", "mock_unique_standalone_cart", nil, "default", "git://test")
90
+ @instance.stub(:git_remote_add) do |remote_name, remote_url|
91
+ say "Added remote #{remote_name} pointing to #{remote_url}"
92
+ true
93
+ end
94
+ end
95
+ let(:arguments) { ['git-clone', 'app2'] }
96
+ it { run_output.should match("Added remote upstream pointing to git://test") }
97
+ end
69
98
  end
70
99
 
71
100
  context "testing git_clone_deploy_hooks" do
@@ -78,6 +107,7 @@ describe RHC::Commands::GitClone do
78
107
  say "Copied" if File.exists?("#{repo_dir}/.git/hooks/pre_commit")
79
108
  true
80
109
  end
110
+ @instance.stub(:discover_git_executable){ 'git' }
81
111
 
82
112
  # Get around the FakeFS bug (defunkt/fakefs#177) by
83
113
  # stubbing the #cp call to inject a expected fs entry
@@ -92,7 +122,10 @@ describe RHC::Commands::GitClone do
92
122
  end
93
123
 
94
124
  context "reports failure" do
95
- before{ @instance.stub(:git_clone_repo).and_raise(RHC::GitException) }
125
+ before do
126
+ @instance.stub(:git_clone_repo).and_raise(RHC::GitException)
127
+ @instance.stub(:discover_git_executable){ 'git' }
128
+ end
96
129
 
97
130
  it { expect { run }.to exit_with_code(216) }
98
131
  it { run_output.should match("Git returned an error") }
@@ -35,6 +35,7 @@ describe RHC::Commands::Scp do
35
35
  @domain = rest_client.add_domain("mockdomain")
36
36
  @domain.add_application("app1", "mock_type")
37
37
  File.should_receive(:exist?).with("file.txt").once.and_return(false)
38
+ File.should_receive(:exist?).with("git").at_least(1).and_return(true)
38
39
  end
39
40
  it { run_output.should match("Local file, file_path, or directory could not be found.") }
40
41
  end
@@ -48,6 +49,7 @@ describe RHC::Commands::Scp do
48
49
  @domain = rest_client.add_domain("mockdomain")
49
50
  @domain.add_application("app1", "mock_type")
50
51
  File.should_receive(:exist?).with("file.txt").once.and_return(true)
52
+ File.should_receive(:exist?).with("git").at_least(1).and_return(true)
51
53
  Net::SCP.should_receive("upload!".to_sym).with("127.0.0.1", "fakeuuidfortestsapp1","file.txt","app-root/data").and_raise(Errno::ECONNREFUSED)
52
54
  end
53
55
  it { run_output.should match("The server fakeuuidfortestsapp1 refused a connection with user 127.0.0.1. The application may be unavailable.") }
@@ -58,6 +60,7 @@ describe RHC::Commands::Scp do
58
60
  @domain = rest_client.add_domain("mockdomain")
59
61
  @domain.add_application("app1", "mock_type")
60
62
  File.should_receive(:exist?).with("file.txt").once.and_return(true)
63
+ File.should_receive(:exist?).with("git").at_least(1).and_return(true)
61
64
  Net::SCP.should_receive("upload!".to_sym).with("127.0.0.1", "fakeuuidfortestsapp1","file.txt","app-root/data").and_raise(SocketError)
62
65
  end
63
66
  it { run_output.should match("The connection to 127.0.0.1 failed: SocketError") }
@@ -69,6 +72,7 @@ describe RHC::Commands::Scp do
69
72
  @domain = rest_client.add_domain("mockdomain")
70
73
  @domain.add_application("app1", "mock_type")
71
74
  File.should_receive(:exist?).with("file.txt").once.and_return(true)
75
+ File.should_receive(:exist?).with("git").at_least(1).and_return(true)
72
76
  Net::SCP.should_receive("upload!".to_sym).with("127.0.0.1", "fakeuuidfortestsapp1","file.txt","app-root/data").and_raise(Net::SSH::AuthenticationFailed)
73
77
  end
74
78
  it { run_output.should match("Authentication to server 127.0.0.1 with user fakeuuidfortestsapp1 failed") }
@@ -79,6 +83,7 @@ describe RHC::Commands::Scp do
79
83
  @domain = rest_client.add_domain("mockdomain")
80
84
  @domain.add_application("app1", "mock_type")
81
85
  File.should_receive(:exist?).with("file.txt").once.and_return(true)
86
+ File.should_receive(:exist?).with("git").at_least(1).and_return(true)
82
87
  Net::SCP.should_receive("upload!".to_sym).with("127.0.0.1", "fakeuuidfortestsapp1","file.txt","app-root/data").and_raise(Net::SCP::Error.new("SCP error message"))
83
88
  end
84
89
  it { run_output.should match("An unknown error occurred: SCP error message") }
@@ -41,6 +41,7 @@ describe RHC::Commands::Setup do
41
41
  it{ command_for('setup', '--clean').options.clean.should be_true }
42
42
 
43
43
  it{ command_for('setup').options.server.should == 'openshift.redhat.com' }
44
+ it{ command_for('setup').options.create_token.should be_nil }
44
45
  it{ command_for('setup', '--server', 'foo.com').options.server.should == 'foo.com' }
45
46
  it{ command_for('setup', '--no-create-token').options.create_token.should == false }
46
47
  it{ command_for('setup', '--create-token').options.create_token.should == true }
@@ -51,7 +52,7 @@ describe RHC::Commands::Setup do
51
52
  end
52
53
  context "when config has use_authorization_tokens=true" do
53
54
  let!(:config){ base_config{ |c, d| d.add('use_authorization_tokens', 'true') } }
54
- it{ command_for('setup').options.use_authorization_tokens.should == true }
55
+ it{ command_for('setup').options.use_authorization_tokens.should be_true }
55
56
  end
56
57
 
57
58
  =begin context 'when libra_server is set' do
@@ -40,7 +40,6 @@ describe RHC::Commands::Snapshot do
40
40
 
41
41
  context 'when failing to save a snapshot' do
42
42
  before do
43
- subject.class.any_instance.should_receive(:has_ssh?).and_return(true)
44
43
  subject.class.any_instance.should_receive(:exec).with("ssh #{@ssh_uri.user}@#{@ssh_uri.host} 'snapshot' > #{@app.name}.tar.gz").and_return([1, 'some save failures'])
45
44
  end
46
45
  it { expect { run }.to exit_with_code(130) }
@@ -54,6 +53,8 @@ describe RHC::Commands::Snapshot do
54
53
  RHC::Helpers.stub(:linux?) do ; false ; end
55
54
  ssh = double(Net::SSH)
56
55
  Net::SSH.should_receive(:start).with(@ssh_uri.host, @ssh_uri.user).and_yield(ssh)
56
+ subject.class.any_instance.stub(:discover_ssh_executable){ 'ssh' }
57
+ subject.class.any_instance.stub(:discover_git_executable){ 'git' }
57
58
  ssh.should_receive(:exec!).with("snapshot").and_yield(nil, :stdout, 'foo').and_yield(nil, :stderr, 'foo')
58
59
  end
59
60
  it { expect { run }.to exit_with_code(0) }
@@ -67,6 +68,8 @@ describe RHC::Commands::Snapshot do
67
68
  RHC::Helpers.stub(:linux?) do ; false ; end
68
69
  ssh = double(Net::SSH)
69
70
  Net::SSH.should_receive(:start).with(@ssh_uri.host, @ssh_uri.user).and_raise(Timeout::Error)
71
+ subject.class.any_instance.stub(:discover_ssh_executable){ 'ssh' }
72
+ subject.class.any_instance.stub(:discover_git_executable){ 'git' }
70
73
  end
71
74
  it { expect { run }.to exit_with_code(130) }
72
75
  end
@@ -112,7 +115,6 @@ describe RHC::Commands::Snapshot do
112
115
  before do
113
116
  File.stub(:exists?).and_return(true)
114
117
  RHC::TarGz.stub(:contains).and_return(true)
115
- subject.class.any_instance.should_receive(:has_ssh?).and_return(true)
116
118
  subject.class.any_instance.should_receive(:exec).with("cat '#{@app.name}.tar.gz' | ssh #{@ssh_uri.user}@#{@ssh_uri.host} 'restore INCLUDE_GIT'").and_return([1, 'some restore failures'])
117
119
  end
118
120
  it { expect { run }.to exit_with_code(130) }
@@ -134,6 +136,8 @@ describe RHC::Commands::Snapshot do
134
136
  channel.should_receive(:on_extended_data).and_yield(nil, nil, 'foo')
135
137
  channel.should_receive(:on_close).and_yield(nil)
136
138
  lines = ''
139
+ subject.class.any_instance.stub(:discover_ssh_executable){ 'ssh' }
140
+ subject.class.any_instance.stub(:discover_git_executable){ 'git' }
137
141
  File.open(File.expand_path('../../assets/targz_sample.tar.gz', __FILE__), 'rb') do |file|
138
142
  file.chunk(1024) do |chunk|
139
143
  lines << chunk
@@ -153,6 +157,8 @@ describe RHC::Commands::Snapshot do
153
157
  RHC::Helpers.stub(:linux?) do ; false ; end
154
158
  ssh = double(Net::SSH)
155
159
  Net::SSH.should_receive(:start).with(@ssh_uri.host, @ssh_uri.user).and_raise(Timeout::Error)
160
+ subject.class.any_instance.stub(:discover_ssh_executable){ 'ssh' }
161
+ subject.class.any_instance.stub(:discover_git_executable){ 'git' }
156
162
  end
157
163
  it { expect { run }.to exit_with_code(130) }
158
164
  end
@@ -120,7 +120,7 @@ describe RHC::Commands::Ssh do
120
120
  before(:each) do
121
121
  @domain = rest_client.add_domain("mockdomain")
122
122
  @domain.add_application("app1", "mock_type")
123
- RHC::Commands::Ssh.any_instance.should_receive(:has_ssh?).and_return(false)
123
+ subject.class.any_instance.stub(:discover_ssh_executable).and_return(nil)
124
124
  end
125
125
  it { run_output.should match("No system SSH available. Please use the --ssh option to specify the path to your SSH executable, or install SSH.") }
126
126
  it { expect { run }.to exit_with_code(1) }
@@ -135,7 +135,9 @@ describe RHC::Commands::Ssh do
135
135
  @domain = rest_client.add_domain("mockdomain")
136
136
  @domain.add_application("app1", "mock_type")
137
137
  RHC::Commands::Ssh.any_instance.should_not_receive(:has_ssh?)
138
- File.should_receive(:exist?).with("path_to_ssh").once.and_return(false)
138
+ File.should_receive(:exist?).with("path_to_ssh").at_least(1).and_return(false)
139
+ File.should_receive(:executable?).with(/.*path_to_ssh/).at_least(1).and_return(false)
140
+ subject.class.any_instance.stub(:discover_git_executable).and_return('git')
139
141
  end
140
142
  it { run_output.should match("SSH executable 'path_to_ssh' does not exist.") }
141
143
  it { expect { run }.to exit_with_code(1) }
@@ -148,6 +150,7 @@ describe RHC::Commands::Ssh do
148
150
  RHC::Commands::Ssh.any_instance.should_not_receive(:has_ssh?)
149
151
  File.should_receive(:exist?).with("path_to_ssh").once.and_return(true)
150
152
  File.should_receive(:executable?).with(/.*path_to_ssh/).at_least(1).and_return(false)
153
+ subject.class.any_instance.stub(:discover_git_executable).and_return('git')
151
154
  end
152
155
  it { run_output.should match("SSH executable 'path_to_ssh' is not executable.") }
153
156
  it { expect { run }.to exit_with_code(1) }
@@ -161,6 +164,7 @@ describe RHC::Commands::Ssh do
161
164
  File.should_receive(:exist?).with("path_to_ssh").once.and_return(true)
162
165
  File.should_receive(:executable?).with("path_to_ssh").once.and_return(true)
163
166
  Kernel.should_receive(:exec).with("path_to_ssh", "fakeuuidfortestsapp1@127.0.0.1").once.times.and_return(0)
167
+ subject.class.any_instance.stub(:discover_git_executable).and_return('git')
164
168
  end
165
169
  it { run_output.should match("Connecting to fakeuuidfortestsapp") }
166
170
  it { expect { run }.to exit_with_code(0) }
@@ -172,6 +176,7 @@ describe RHC::Commands::Ssh do
172
176
 
173
177
  context 'has_ssh?' do
174
178
  before{ RHC::Commands::Ssh.any_instance.stub(:ssh_version){ raise "Fake Exception" } }
179
+ before{ RHC::Commands::Ssh.any_instance.stub(:discover_ssh_executable){ nil } }
175
180
  its(:has_ssh?) { should be_false }
176
181
  end
177
182
  end
@@ -87,7 +87,7 @@ describe RHC::Wizard do
87
87
  subject.should_receive(:applications).and_return([app])
88
88
  Net::SSH.should_receive(:start).with("foo.com", "uuid", {:timeout => 60}).and_return(ssh)
89
89
  subject.send(:test_ssh_connectivity).should be_true
90
- end
90
+ end
91
91
  it "should handle a failed connection" do
92
92
  subject.should_receive(:ssh_key_uploaded?).and_return(true)
93
93
  subject.should_receive(:applications).and_return([app])
@@ -100,7 +100,7 @@ describe RHC::Wizard do
100
100
  subject.should_receive(:debug_error).with(interrupt)
101
101
  Net::SSH.should_receive(:start).and_raise(interrupt)
102
102
  expect{ subject.send(:test_ssh_connectivity) }.to raise_error(RuntimeError, /Connection attempt to foo.com was interrupted/)
103
- end
103
+ end
104
104
  end
105
105
 
106
106
  describe "#core_auth" do
@@ -121,79 +121,79 @@ describe RHC::Wizard do
121
121
  let(:auth){ subject.send(:auth) }
122
122
  let(:user_obj){ double(:login => user) }
123
123
 
124
- subject{ described_class.new(config, options) }
124
+ let(:wizard){RHC::Wizard.new(config, options) }
125
125
 
126
126
  def expect_client_test(with_sessions=false)
127
- subject.should_receive(:new_client_for_options).ordered.and_return(rest_client)
127
+ wizard.should_receive(:new_client_for_options).ordered.and_return(rest_client)
128
128
  rest_client.should_receive(:api).ordered
129
129
  rest_client.should_receive(:user).ordered.and_return(user_obj)
130
130
  rest_client.should_receive(:supports_sessions?).ordered.and_return(with_sessions)
131
131
  end
132
132
  def expect_raise_from_api(error)
133
- subject.should_receive(:new_client_for_options).ordered.and_return(rest_client)
133
+ wizard.should_receive(:new_client_for_options).ordered.and_return(rest_client)
134
134
  rest_client.should_receive(:api).ordered.and_raise(error)
135
135
  end
136
136
 
137
137
  it "should prompt for user and password" do
138
138
  expect_client_test
139
- subject.send(:login_stage).should be_true
140
- subject.send(:options).rhlogin.should == user
139
+ wizard.send(:login_stage).should be_true
140
+ wizard.send(:options).rhlogin.should == user
141
141
  end
142
142
 
143
143
  context "with token" do
144
144
  let(:token){ 'a_test_value' }
145
145
  let(:default_options){ {:token => token, :rhlogin => user} }
146
- before{ subject.should_receive(:say).with(/Using an existing token for #{user} to login to /).ordered }
146
+ before{ wizard.should_receive(:say).with(/Using an existing token for #{user} to login to /).ordered }
147
147
 
148
148
  it "should continue without prompt" do
149
149
  expect_client_test
150
- subject.send(:login_stage).should be_true
150
+ wizard.send(:login_stage).should be_true
151
151
  end
152
152
  end
153
153
 
154
154
  context "with credentials" do
155
155
  let(:server){ mock_uri }
156
156
  let(:default_options){ {:rhlogin => user, :password => password, :server => server} }
157
- before{ subject.should_receive(:say).with(/Using #{user} to login to /).ordered }
157
+ before{ wizard.should_receive(:say).with(/Using #{user} to login to /).ordered }
158
158
 
159
159
  it "should warn about a self signed cert error" do
160
160
  expect_raise_from_api(RHC::Rest::SelfSignedCertificate.new('reason', 'message'))
161
- subject.should_receive(:warn).with(/server's certificate is self-signed/).ordered
162
- subject.should_receive(:openshift_online_server?).ordered.and_return(true)
163
- subject.should_receive(:warn).with(/server between you and OpenShift/).ordered
161
+ wizard.should_receive(:warn).with(/server's certificate is self-signed/).ordered
162
+ wizard.should_receive(:openshift_online_server?).ordered.and_return(true)
163
+ wizard.should_receive(:warn).with(/server between you and OpenShift/).ordered
164
164
 
165
- subject.send(:login_stage).should be_nil
165
+ wizard.send(:login_stage).should be_nil
166
166
  end
167
167
 
168
168
  it "should warn about a cert error for Online" do
169
169
  expect_raise_from_api(RHC::Rest::CertificateVerificationFailed.new('reason', 'message'))
170
- subject.should_receive(:warn).with(/server's certificate could not be verified/).ordered
171
- subject.should_receive(:openshift_online_server?).ordered.and_return(true)
172
- subject.should_receive(:warn).with(/server between you and OpenShift/).ordered
170
+ wizard.should_receive(:warn).with(/server's certificate could not be verified/).ordered
171
+ wizard.should_receive(:openshift_online_server?).ordered.and_return(true)
172
+ wizard.should_receive(:warn).with(/server between you and OpenShift/).ordered
173
173
 
174
- subject.send(:login_stage).should be_nil
174
+ wizard.send(:login_stage).should be_nil
175
175
  end
176
176
 
177
177
  it "should warn about a cert error for custom server and continue" do
178
178
  expect_raise_from_api(RHC::Rest::CertificateVerificationFailed.new('reason', 'message'))
179
- subject.should_receive(:warn).with(/server's certificate could not be verified/).ordered
180
- subject.should_receive(:openshift_online_server?).ordered.and_return(false)
181
- subject.should_receive(:warn).with(/bypass this check/).ordered
182
- subject.should_receive(:agree).with(/Connect without checking/).ordered.and_return(true)
179
+ wizard.should_receive(:warn).with(/server's certificate could not be verified/).ordered
180
+ wizard.should_receive(:openshift_online_server?).ordered.and_return(false)
181
+ wizard.should_receive(:warn).with(/bypass this check/).ordered
182
+ wizard.should_receive(:agree).with(/Connect without checking/).ordered.and_return(true)
183
183
  expect_client_test
184
184
 
185
- subject.send(:login_stage).should be_true
185
+ wizard.send(:login_stage).should be_true
186
186
  options.insecure.should be_true
187
187
  end
188
188
 
189
189
  it "should warn about a cert error for custom server and be cancelled" do
190
190
  expect_raise_from_api(RHC::Rest::CertificateVerificationFailed.new('reason', 'message'))
191
- subject.should_receive(:warn).with(/server's certificate could not be verified/).ordered
192
- subject.should_receive(:openshift_online_server?).ordered.and_return(false)
193
- subject.should_receive(:warn).with(/bypass this check/).ordered
194
- subject.should_receive(:agree).with(/Connect without checking/).ordered.and_return(false)
191
+ wizard.should_receive(:warn).with(/server's certificate could not be verified/).ordered
192
+ wizard.should_receive(:openshift_online_server?).ordered.and_return(false)
193
+ wizard.should_receive(:warn).with(/bypass this check/).ordered
194
+ wizard.should_receive(:agree).with(/Connect without checking/).ordered.and_return(false)
195
195
 
196
- subject.send(:login_stage).should be_nil
196
+ wizard.send(:login_stage).should be_nil
197
197
  options.insecure.should be_false
198
198
  end
199
199
 
@@ -206,10 +206,10 @@ describe RHC::Wizard do
206
206
  it "should check for an existing token" do
207
207
  store.should_receive(:get).and_return(nil)
208
208
 
209
- subject.should_receive(:info).with(/OpenShift can create and store a token on disk/).ordered
210
- subject.should_receive(:agree).with(/Generate a token now?/).ordered.and_return(false)
209
+ wizard.should_receive(:info).with(/OpenShift can create and store a token on disk/).ordered
210
+ wizard.should_receive(:agree).with(/Generate a token now?/).ordered.and_return(false)
211
211
 
212
- subject.send(:login_stage).should be_true
212
+ wizard.send(:login_stage).should be_true
213
213
  options.token.should be_nil
214
214
  end
215
215
  end
@@ -223,42 +223,79 @@ describe RHC::Wizard do
223
223
 
224
224
  it "should not generate a token if the user does not request it" do
225
225
  store.should_not_receive(:get)
226
- subject.should_receive(:info).with(/OpenShift can create and store a token on disk/).ordered
227
- subject.should_receive(:agree).with(/Generate a token now?/).ordered.and_return(false)
226
+ wizard.should_receive(:info).with(/OpenShift can create and store a token on disk/).ordered
227
+ wizard.should_receive(:agree).with(/Generate a token now?/).ordered.and_return(false)
228
228
 
229
- subject.send(:login_stage).should be_true
229
+ wizard.send(:login_stage).should be_true
230
230
  options.token.should be_nil
231
231
  end
232
232
 
233
+ context "when the option to create tokens is passed" do
234
+
235
+ shared_examples "a wizard creating tokens without prompts" do
236
+ it "should create the token without prompting" do
237
+ store.should_not_receive(:get)
238
+ wizard.should_receive(:info).with(/OpenShift can create and store a token on disk/).ordered
239
+ wizard.should_receive(:agree).never
240
+ wizard.should_receive(:say).with(/Generating an authorization token for this client /).ordered
241
+ rest_client.should_receive(:new_session).ordered.and_return(auth_token)
242
+ store.should_receive(:put).with(user, server, token).ordered.and_return(true)
243
+ wizard.should_receive(:new_client_for_options).ordered.and_return(rest_client)
244
+ rest_client.should_receive(:user).ordered.and_return(true)
245
+ wizard.should_receive(:success).with(/lasts 1 minute/).ordered
246
+
247
+ wizard.send(:login_stage).should be_true
248
+ options.token.should == token
249
+ end
250
+ end
251
+
252
+ describe '--create-token' do
253
+ it_behaves_like 'a wizard creating tokens without prompts' do
254
+ let(:default_options){ {:rhlogin => user, :password => password, :server => server, :create_token => true} }
255
+ end
256
+ end
257
+
258
+ describe '--use-authorization-tokens' do
259
+ before { store.should_receive(:get) }
260
+
261
+ it_behaves_like 'a wizard creating tokens without prompts' do
262
+ #create token would normally be set in rhc/commands/server if not specified
263
+ #and running from the command line
264
+ let(:default_options){ {:rhlogin => user, :password => password, :server => server, :use_authorization_tokens => true, :create_token =>true} }
265
+ end
266
+ end
267
+ end
268
+
233
269
  it "should generate a token if the user requests it" do
234
270
  store.should_not_receive(:get)
235
- subject.should_receive(:info).with(/OpenShift can create and store a token on disk/).ordered
236
- subject.should_receive(:agree).with(/Generate a token now?/).ordered.and_return(true)
237
- subject.should_receive(:say).with(/Generating an authorization token for this client /).ordered
271
+ wizard.should_receive(:info).with(/OpenShift can create and store a token on disk/).ordered
272
+ wizard.should_receive(:agree).with(/Generate a token now?/).ordered.and_return(true)
273
+ wizard.should_receive(:say).with(/Generating an authorization token for this client /).ordered
238
274
  rest_client.should_receive(:new_session).ordered.and_return(auth_token)
239
275
  store.should_receive(:put).with(user, server, token).ordered.and_return(true)
240
- subject.should_receive(:new_client_for_options).ordered.and_return(rest_client)
276
+ wizard.should_receive(:new_client_for_options).ordered.and_return(rest_client)
241
277
  rest_client.should_receive(:user).ordered.and_return(true)
242
- subject.should_receive(:success).with(/lasts 1 minute/).ordered
278
+ wizard.should_receive(:success).with(/lasts 1 minute/).ordered
243
279
 
244
- subject.send(:login_stage).should be_true
280
+ wizard.send(:login_stage).should be_true
245
281
  options.token.should == token
246
282
  end
247
283
  end
248
284
 
285
+
249
286
  context "when the user doesn't want to use tokens" do
250
287
  let(:default_options){ {:rhlogin => user, :password => password, :server => server, :use_authorization_tokens => false, :create_token => false} }
251
288
  before do
252
- subject.should_receive(:new_client_for_options).ordered.and_return(rest_client)
289
+ wizard.should_receive(:new_client_for_options).ordered.and_return(rest_client)
253
290
  rest_client.should_receive(:api).ordered
254
291
  rest_client.should_receive(:user).ordered.and_return(user_obj)
255
292
  end
256
293
 
257
294
  it "should skip token generation" do
258
- subject.should_receive(:say).with(/Skipping token generation/)
259
- subject.should_receive(:agree).never
295
+ wizard.should_receive(:say).with(/Skipping token generation/)
296
+ wizard.should_receive(:agree).never
260
297
 
261
- subject.send(:login_stage).should be_true
298
+ wizard.send(:login_stage).should be_true
262
299
  options.token.should be_nil
263
300
  end
264
301
  end
@@ -407,7 +444,7 @@ describe RHC::Wizard do
407
444
 
408
445
  context "when a multiple keys exist but is not the same" do
409
446
  before{ setup_mock_ssh(true) }
410
- before do
447
+ before do
411
448
  stub_one_key('a_key')
412
449
  stub_add_key_error('invalid```--', 'Invalid key name')
413
450
  stub_add_key('another_key')
@@ -3,7 +3,10 @@ require 'webmock/rspec'
3
3
  require 'fakefs/safe'
4
4
  require 'rbconfig'
5
5
 
6
- require 'pry' if ENV['PRY']
6
+ if ENV['PRY']
7
+ require 'pry'
8
+ require 'pry-debugger'
9
+ end
7
10
 
8
11
  # Environment reset
9
12
  ENV['http_proxy'] = nil
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rhc
3
3
  version: !ruby/object:Gem::Version
4
- hash: 105
4
+ hash: 97
5
5
  prerelease:
6
6
  segments:
7
7
  - 1
8
- - 30
9
- - 3
10
- version: 1.30.3
8
+ - 31
9
+ - 5
10
+ version: 1.31.5
11
11
  platform: ruby
12
12
  authors:
13
13
  - Red Hat
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2014-10-15 00:00:00 Z
18
+ date: 2014-11-04 00:00:00 Z
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency
21
21
  name: net-ssh
@@ -207,6 +207,14 @@ dependencies:
207
207
  - 0
208
208
  - 4
209
209
  version: "0.4"
210
+ - - <
211
+ - !ruby/object:Gem::Version
212
+ hash: 7
213
+ segments:
214
+ - 0
215
+ - 6
216
+ - 0
217
+ version: 0.6.0
210
218
  type: :development
211
219
  version_requirements: *id012
212
220
  - !ruby/object:Gem::Dependency