rhc 1.2.7 → 1.3.8

Sign up to get free protection for your applications and to get access to all the features.
Files changed (97) hide show
  1. data/bin/rhc +6 -8
  2. data/bin/rhc-chk +23 -10
  3. data/features/domain.feature +1 -1
  4. data/features/lib/rhc_helper.rb +3 -2
  5. data/features/lib/rhc_helper/api.rb +7 -0
  6. data/features/lib/rhc_helper/app.rb +8 -10
  7. data/features/lib/rhc_helper/domain.rb +2 -1
  8. data/features/lib/rhc_helper/runnable.rb +2 -24
  9. data/features/sshkey.feature +3 -3
  10. data/features/step_definitions/cartridge_steps.rb +6 -6
  11. data/features/step_definitions/client_steps.rb +0 -1
  12. data/features/step_definitions/sshkey_steps.rb +2 -2
  13. data/features/support/before_hooks.rb +0 -1
  14. data/features/support/env.rb +5 -3
  15. data/lib/rhc-common.rb +1 -1
  16. data/lib/rhc.rb +9 -8
  17. data/lib/rhc/auth.rb +3 -0
  18. data/lib/rhc/auth/basic.rb +54 -0
  19. data/lib/rhc/cartridge_helpers.rb +11 -5
  20. data/lib/rhc/cli.rb +4 -2
  21. data/lib/rhc/command_runner.rb +35 -30
  22. data/lib/rhc/commands.rb +127 -18
  23. data/lib/rhc/commands/account.rb +24 -0
  24. data/lib/rhc/commands/alias.rb +1 -1
  25. data/lib/rhc/commands/app.rb +210 -209
  26. data/lib/rhc/commands/apps.rb +22 -0
  27. data/lib/rhc/commands/base.rb +10 -77
  28. data/lib/rhc/commands/cartridge.rb +35 -35
  29. data/lib/rhc/commands/domain.rb +20 -13
  30. data/lib/rhc/commands/git_clone.rb +30 -0
  31. data/lib/rhc/commands/{port-forward.rb → port_forward.rb} +3 -3
  32. data/lib/rhc/commands/server.rb +28 -16
  33. data/lib/rhc/commands/setup.rb +18 -1
  34. data/lib/rhc/commands/snapshot.rb +4 -4
  35. data/lib/rhc/commands/sshkey.rb +4 -18
  36. data/lib/rhc/commands/tail.rb +32 -9
  37. data/lib/rhc/config.rb +168 -99
  38. data/lib/rhc/context_helper.rb +22 -9
  39. data/lib/rhc/core_ext.rb +41 -1
  40. data/lib/rhc/exceptions.rb +21 -5
  41. data/lib/rhc/git_helpers.rb +81 -0
  42. data/lib/rhc/help_formatter.rb +21 -1
  43. data/lib/rhc/helpers.rb +222 -87
  44. data/lib/rhc/output_helpers.rb +94 -110
  45. data/lib/rhc/rest.rb +15 -198
  46. data/lib/rhc/rest/api.rb +88 -0
  47. data/lib/rhc/rest/application.rb +29 -30
  48. data/lib/rhc/rest/attributes.rb +27 -0
  49. data/lib/rhc/rest/base.rb +29 -33
  50. data/lib/rhc/rest/cartridge.rb +42 -20
  51. data/lib/rhc/rest/client.rb +351 -89
  52. data/lib/rhc/rest/domain.rb +7 -13
  53. data/lib/rhc/rest/gear_group.rb +1 -1
  54. data/lib/rhc/rest/key.rb +7 -2
  55. data/lib/rhc/rest/mock.rb +609 -0
  56. data/lib/rhc/rest/user.rb +6 -2
  57. data/lib/rhc/{ssh_key_helpers.rb → ssh_helpers.rb} +58 -28
  58. data/lib/rhc/{targz.rb → tar_gz.rb} +0 -0
  59. data/lib/rhc/usage_templates/command_help.erb +4 -1
  60. data/lib/rhc/usage_templates/help.erb +24 -11
  61. data/lib/rhc/usage_templates/options_help.erb +14 -0
  62. data/lib/rhc/wizard.rb +283 -213
  63. data/spec/keys/example.pem +23 -0
  64. data/spec/keys/example_private.pem +27 -0
  65. data/spec/keys/server.pem +19 -0
  66. data/spec/rest_spec_helper.rb +3 -371
  67. data/spec/rhc/auth_spec.rb +226 -0
  68. data/spec/rhc/cli_spec.rb +41 -14
  69. data/spec/rhc/command_spec.rb +44 -15
  70. data/spec/rhc/commands/account_spec.rb +41 -0
  71. data/spec/rhc/commands/alias_spec.rb +16 -15
  72. data/spec/rhc/commands/app_spec.rb +115 -92
  73. data/spec/rhc/commands/apps_spec.rb +39 -0
  74. data/spec/rhc/commands/cartridge_spec.rb +134 -112
  75. data/spec/rhc/commands/domain_spec.rb +31 -86
  76. data/spec/rhc/commands/git_clone_spec.rb +56 -0
  77. data/spec/rhc/commands/{port-forward_spec.rb → port_forward_spec.rb} +27 -32
  78. data/spec/rhc/commands/server_spec.rb +28 -3
  79. data/spec/rhc/commands/setup_spec.rb +29 -11
  80. data/spec/rhc/commands/snapshot_spec.rb +4 -3
  81. data/spec/rhc/commands/sshkey_spec.rb +24 -56
  82. data/spec/rhc/commands/tail_spec.rb +26 -9
  83. data/spec/rhc/commands/threaddump_spec.rb +12 -11
  84. data/spec/rhc/config_spec.rb +211 -164
  85. data/spec/rhc/context_spec.rb +2 -0
  86. data/spec/rhc/helpers_spec.rb +242 -46
  87. data/spec/rhc/rest_application_spec.rb +42 -28
  88. data/spec/rhc/rest_client_spec.rb +110 -93
  89. data/spec/rhc/rest_spec.rb +220 -131
  90. data/spec/rhc/targz_spec.rb +1 -1
  91. data/spec/rhc/wizard_spec.rb +435 -624
  92. data/spec/spec.opts +1 -1
  93. data/spec/spec_helper.rb +140 -6
  94. data/spec/wizard_spec_helper.rb +326 -0
  95. metadata +163 -143
  96. data/lib/rhc/client.rb +0 -17
  97. data/lib/rhc/git_helper.rb +0 -59
@@ -1,9 +1,9 @@
1
- require 'rhc/rest/base'
1
+ require 'ostruct'
2
2
 
3
3
  module RHC
4
4
  module Rest
5
5
  class User < Base
6
- attr_reader :login
6
+ define_attr :login, :plan_id, :max_gears, :consumed_gears
7
7
 
8
8
  def add_key(name, content, type)
9
9
  debug "Add key #{name} of type #{type} for user #{login}"
@@ -20,6 +20,10 @@ module RHC
20
20
  #TODO do a regex caomparison
21
21
  keys.detect { |key| key.name == name }
22
22
  end
23
+
24
+ def capabilities
25
+ @capabilities ||= OpenStruct.new attribute('capabilities')
26
+ end
23
27
  end
24
28
  end
25
29
  end
@@ -18,9 +18,45 @@
18
18
  require 'net/ssh'
19
19
  require 'rhc/vendor/sshkey'
20
20
 
21
-
22
21
  module RHC
23
- module SSHKeyHelpers
22
+ module SSHHelpers
23
+ # Public: Run ssh command on remote host
24
+ #
25
+ # host - The String of the remote hostname to ssh to.
26
+ # username - The String username of the remote user to ssh as.
27
+ # command - The String command to run on the remote host.
28
+ #
29
+ # Examples
30
+ #
31
+ # ssh_ruby('myapp-t.rhcloud.com',
32
+ # '109745632b514e9590aa802ec015b074',
33
+ # 'rhcsh tail -f $OPENSHIFT_LOG_DIR/*"')
34
+ # # => true
35
+ #
36
+ # Returns true on success
37
+ def ssh_ruby(host, username, command)
38
+ debug "Opening Net::SSH connection to #{host}, #{username}, #{command}"
39
+ Net::SSH.start(host, username) do |session|
40
+ #:nocov:
41
+ session.open_channel do |channel|
42
+ channel.request_pty do |ch, success|
43
+ say "pty could not be obtained" unless success
44
+ end
45
+
46
+ channel.on_data do |ch, data|
47
+ puts data
48
+ end
49
+ channel.exec command
50
+ end
51
+ session.loop
52
+ #:nocov:
53
+ end
54
+ rescue Errno::ECONNREFUSED => e
55
+ raise RHC::SSHConnectionRefused.new(host, username)
56
+ rescue SocketError => e
57
+ raise RHC::ConnectionFailed, "The connection to #{host} failed: #{e.message}"
58
+ end
59
+
24
60
  # Public: Generate an SSH key and store it in ~/.ssh/id_rsa
25
61
  #
26
62
  # type - The String type RSA or DSS.
@@ -29,7 +65,7 @@ module RHC
29
65
  #
30
66
  # Examples
31
67
  #
32
- # generate_ssh_key_ruby()
68
+ # generate_ssh_key_ruby
33
69
  # # => /home/user/.ssh/id_rsa.pub
34
70
  #
35
71
  # Returns nil on failure or public key location as a String on success
@@ -37,22 +73,25 @@ module RHC
37
73
  key = RHC::Vendor::SSHKey.generate(:type => type,
38
74
  :bits => bits,
39
75
  :comment => comment)
40
- ssh_dir = "#{RHC::Config.home_dir}/.ssh"
41
- if File.exists?("#{ssh_dir}/id_rsa")
42
- say "SSH key already exists: #{ssh_dir}/id_rsa. Reusing..."
76
+ ssh_dir = RHC::Config.ssh_dir
77
+ priv_key = RHC::Config.ssh_priv_key_file_path
78
+ pub_key = RHC::Config.ssh_pub_key_file_path
79
+
80
+ if File.exists?(priv_key)
81
+ say "SSH key already exists: #{priv_key}. Reusing..."
43
82
  return nil
44
83
  else
45
84
  unless File.exists?(ssh_dir)
46
85
  FileUtils.mkdir_p(ssh_dir)
47
86
  File.chmod(0700, ssh_dir)
48
87
  end
49
- File.open("#{ssh_dir}/id_rsa", 'w') {|f| f.write(key.private_key)}
50
- File.chmod(0600, "#{ssh_dir}/id_rsa")
51
- File.open("#{ssh_dir}/id_rsa.pub", 'w') {|f| f.write(key.ssh_public_key)}
88
+ File.open(priv_key, 'w') {|f| f.write(key.private_key)}
89
+ File.chmod(0600, priv_key)
90
+ File.open(pub_key, 'w') {|f| f.write(key.ssh_public_key)}
52
91
 
53
- ssh_add if exe?('ssh-add')
92
+ ssh_add
54
93
  end
55
- "#{ssh_dir}/id_rsa.pub"
94
+ pub_key
56
95
  end
57
96
 
58
97
  def exe?(executable)
@@ -60,15 +99,6 @@ module RHC
60
99
  File.executable?(File.join(directory, executable.to_s))
61
100
  end
62
101
  end
63
-
64
- # Public: Format SSH key's core attributes (name, type, fingerprint)
65
- # in a given ERB template
66
- #
67
- # key - an object to be formatted
68
- # template - ERB template
69
- def format(key, template)
70
- template.result(binding)
71
- end
72
102
 
73
103
 
74
104
  # For Net::SSH versions (< 2.0.11) that does not have
@@ -88,15 +118,13 @@ module RHC
88
118
  rescue NoMethodError, NotImplementedError => e
89
119
  ssh_keygen_fallback key
90
120
  return nil
91
- # :nocov: no reason to cover this case
92
121
  rescue OpenSSL::PKey::PKeyError, Net::SSH::Exception => e
93
122
  error e.message
94
123
  return nil
95
- # :nocov:
96
124
  end
97
-
125
+
98
126
  def fingerprint_for_default_key
99
- fingerprint_for_local_key RHC::Config::ssh_pub_key_file_path
127
+ fingerprint_for_local_key RHC::Config.ssh_pub_key_file_path
100
128
  end
101
129
 
102
130
  # for an SSH public key specified by 'key', return a triple
@@ -114,15 +142,17 @@ module RHC
114
142
  end
115
143
 
116
144
  def ssh_key_triple_for_default_key
117
- ssh_key_triple_for RHC::Config::ssh_pub_key_file_path
145
+ ssh_key_triple_for RHC::Config.ssh_pub_key_file_path
118
146
  end
119
147
 
120
148
  private
121
149
 
122
150
  def ssh_add
123
- #:nocov:
124
- `ssh-add 2&>1`
125
- #:nocov:
151
+ if exe?('ssh-add')
152
+ #:nocov:
153
+ `ssh-add 2&>1`
154
+ #:nocov:
155
+ end
126
156
  end
127
157
  end
128
158
  end
File without changes
@@ -11,9 +11,12 @@ Options for <%= @command.name %>
11
11
  <% end -%>
12
12
 
13
13
  Global Options
14
- <% for option in @global_options -%><% next if option[:hide] -%>
14
+ <% for option in @global_options -%><% next if option[:hide] || @command.options.any?{ |o| (o[:switches] & option[:switches]).present? } -%>
15
15
  <%= "%-25s %s\n" % [option[:switches].join(', '), option[:description]] -%>
16
16
  <% end -%>
17
+
18
+ See 'rhc help options' for a full list of command-line options.
19
+
17
20
  <% else -%>
18
21
 
19
22
  List of Actions
@@ -1,19 +1,32 @@
1
- Usage: <%= Array(program :name).first %> <resource> [<action>] [--help] [<args>]
1
+ Usage: rhc [--help] [--version] [--debug] <command> [<args>]
2
+
2
3
  <%= Array(program :description).first %>
3
4
 
4
- List of Commands and Resources
5
- <% for name, command in @commands.sort -%>
6
- <%- unless alias? name or name == "help" or name.include?(" ") or command.summary.nil? -%> <%="%-18s %s\n" % [command.name, command.summary || command.description] %><%- end -%>
7
- <%- end -%>
5
+ <%
6
+ remaining = Hash[@commands.dup.select{ |name, command| not alias?(name) and not command.summary.blank? }]
7
+ basic = remaining.slice!('setup', 'app create', 'apps', 'cartridge list', 'cartridge add', 'server')
8
+ begin -%>
9
+ Getting started:
10
+ <% for name, command in basic -%>
11
+ <%="%-18s %s\n" % [command.name, command.summary || command.description] -%>
12
+ <% end %>
13
+ <% end unless basic.empty?
8
14
 
9
- Global Options
10
- <%- unless @options.nil? or @options.empty? -%>
11
- <% for option in @options -%><% next if option[:hide] -%>
12
- <%= "%-25s %s\n" % [option[:switches].join(', '), option[:description]] -%>
13
- <% end -%>
15
+ debugging = remaining.slice!('app restart', 'app show', 'tail', 'port-forward', 'threaddump', 'snapshot', 'git-clone')
16
+ begin -%>
17
+ Working with apps:
18
+ <% for name, command in debugging -%>
19
+ <%="%-18s %s\n" % [command.name, command.summary || command.description] -%>
20
+ <% end
21
+ end unless debugging.empty?
22
+ %>
23
+ Management commands:
24
+ <% for name, command in remaining.sort -%>
25
+ <%- unless alias? name or name.include?(' ') or command.summary.nil? -%> <%="%-18s %s\n" % [command.name, command.summary || command.description] %><%- end -%>
14
26
  <%- end -%>
15
27
 
16
- See '<%= Array(program :name).first %> help <resource>' for more specifics on each resource or command.
28
+ See '<%= Array(program :name).first %> help <command>' for more information on a specific command. See 'rhc help options' for a list of global command-line options and information about the config file.
29
+
17
30
  <% if program :help -%>
18
31
  <% for title, body in program(:help) %>
19
32
  <%= $terminal.color title.to_s.upcase, :bold %>:
@@ -0,0 +1,14 @@
1
+ The following options can be passed to any command:
2
+
3
+ <% for option in options -%>
4
+ <%= "%-25s %s\n" % [option[:switches].join(', '), option[:description]] -%>
5
+ <% end -%>
6
+
7
+ The following configuration options are stored in the <%= RHC::Helpers.system_path('.openshift/express.conf') %> file in your home directory. Passing any of these options to 'rhc setup' will save that value to the file:
8
+
9
+ <%= "%-25s %s\n" % ["Option", "In Config"] -%>
10
+
11
+ <% for key, value in RHC::Config::OPTIONS -%>
12
+ <% next if value.nil? -%>
13
+ <%= "%-25s %s\n" % ["--#{key.to_s.gsub('_','-')}", value[0] || key] -%>
14
+ <% end -%>
@@ -1,16 +1,15 @@
1
- require 'rhc-common'
2
1
  require 'rhc/helpers'
3
- require 'rhc/ssh_key_helpers'
2
+ require 'rhc/ssh_helpers'
3
+ require 'rhc/git_helpers'
4
4
  require 'highline/system_extensions'
5
- require 'net/ssh'
6
5
  require 'fileutils'
7
6
  require 'socket'
8
7
 
9
8
  module RHC
10
9
  class Wizard
11
10
  include HighLine::SystemExtensions
12
- include RHC::Helpers
13
- include RHC::SSHKeyHelpers
11
+
12
+ DEFAULT_MAX_LENGTH = 16
14
13
 
15
14
  STAGES = [:greeting_stage,
16
15
  :login_stage,
@@ -18,6 +17,7 @@ module RHC
18
17
  :config_ssh_key_stage,
19
18
  :upload_ssh_key_stage,
20
19
  :install_client_tools_stage,
20
+ :setup_test_stage,
21
21
  :config_namespace_stage,
22
22
  :show_app_info_stage,
23
23
  :finalize_stage]
@@ -25,15 +25,15 @@ module RHC
25
25
  STAGES
26
26
  end
27
27
 
28
- def initialize(config, opts=nil)
28
+ attr_reader :rest_client
29
+
30
+ #
31
+ # Running the setup wizard may change the contents of opts and config if
32
+ # the create_config_stage completes successfully.
33
+ #
34
+ def initialize(config=RHC::Config.new, opts=Commander::Command::Options.new)
29
35
  @config = config
30
- @config_path = config.config_path
31
- if @libra_server.nil?
32
- @libra_server = config['libra_server']
33
- # if not set, set to default
34
- @libra_server = @libra_server ? @libra_server : "openshift.redhat.com"
35
- end
36
- @config.config_user opts.rhlogin if opts && opts.rhlogin
36
+ @options = opts
37
37
  @debug = opts.debug if opts
38
38
  end
39
39
 
@@ -47,16 +47,53 @@ module RHC
47
47
  # Returns nil on failure or true on success
48
48
  def run
49
49
  stages.each do |stage|
50
- # FIXME: cleanup if we fail
51
50
  debug "Running #{stage}"
52
- if (self.send stage).nil?
51
+ if self.send(stage).nil?
53
52
  return nil
54
53
  end
55
54
  end
56
55
  true
57
56
  end
58
57
 
58
+ protected
59
+ include RHC::Helpers
60
+ include RHC::SSHHelpers
61
+ include RHC::GitHelpers
62
+ attr_reader :config, :options
63
+ attr_accessor :auth, :user
64
+
65
+ def openshift_server
66
+ options.server || config['libra_server'] || "openshift.redhat.com"
67
+ end
68
+
69
+ def new_client_for_options
70
+ client_from_options({
71
+ :auth => auth,
72
+ })
73
+ end
74
+
75
+ def auth
76
+ @auth ||= RHC::Auth::Basic.new(options)
77
+ end
78
+
79
+ def username
80
+ auth.send(:username)
81
+ end
82
+ def password
83
+ auth.send(:password)
84
+ end
85
+
59
86
  private
87
+
88
+ # cache SSH keys from the REST client
89
+ def ssh_keys
90
+ @ssh_keys ||= rest_client.sshkeys
91
+ end
92
+
93
+ # clear SSH key cache
94
+ def clear_ssh_keys_cache
95
+ @ssh_keys = nil
96
+ end
60
97
 
61
98
  def greeting_stage
62
99
  info "OpenShift Client Tools (RHC) Setup Wizard"
@@ -69,67 +106,67 @@ module RHC
69
106
  end
70
107
 
71
108
  def login_stage
72
- # get_password adds an extra untracked newline so set :bottom to -1
73
- section(:top => 1, :bottom => -1) do
74
- if @config.has_opts? && @config.opts_login
75
- @username = @config.opts_login
76
- say "Using #{@username}"
77
- else
78
- @username = ask("Login to #{@libra_server}: ") do |q|
79
- q.default = RHC::Config.default_rhlogin
109
+ say "Using #{options.rhlogin} to login to #{openshift_server}" if options.rhlogin
110
+
111
+ self.rest_client = new_client_for_options
112
+
113
+ begin
114
+ rest_client.api
115
+ rescue RHC::Rest::CertificateVerificationFailed => e
116
+ debug "Certificate validation failed: #{e.reason}"
117
+ unless options.insecure
118
+ if RHC::Rest::SelfSignedCertificate === e
119
+ warn "The server's certificate is self-signed, which means that a secure connection can't be established to '#{openshift_server}'."
120
+ else
121
+ warn "The server's certificate could not be verified, which means that a secure connection can't be established to '#{openshift_server}'."
122
+ end
123
+ if openshift_online_server?
124
+ paragraph{ warn "This may mean that a server between you and OpenShift is capable of accessing information sent from this client. If you wish to continue without checking the certificate, please pass the -k (or --insecure) option to this command." }
125
+ return
126
+ else
127
+ paragraph{ warn "You may bypass this check, but any data you send to the server could be intercepted by others." }
128
+ return unless agree "Connect without checking the certificate? (yes|no): "
129
+ options.insecure = true
130
+ self.rest_client = new_client_for_options
131
+ retry
80
132
  end
81
133
  end
82
-
83
- @password = RHC::Config.password
84
- @password = RHC::get_password if @password.nil?
85
134
  end
86
135
 
87
- # instantiate a REST client that stages can use
88
- end_point = "https://#{@libra_server}/broker/rest/api"
89
- @rest_client = RHC::Rest::Client.new(end_point, @username, @password, @debug)
90
-
91
- # confirm that the REST client can connect
92
- return false unless @rest_client.user
93
-
136
+ self.user = rest_client.user
94
137
  true
95
138
  end
96
139
 
97
140
  def create_config_stage
98
- if !File.exists? @config_path
99
- FileUtils.mkdir_p File.dirname(@config_path)
100
- File.open(@config_path, 'w') do |file|
101
- file.puts <<EOF
102
- # Default user login
103
- default_rhlogin='#{@username}'
104
-
105
- # Server API
106
- libra_server = '#{@libra_server}'
107
- EOF
108
-
141
+ paragraph do
142
+ if File.exists? config.path
143
+ backup = "#{@config.path}.bak"
144
+ FileUtils.cp(config.path, backup)
145
+ FileUtils.rm(config.path)
109
146
  end
110
147
 
111
- paragraph do
112
- say "Created local config file: " + @config_path
113
- say "The #{File.basename(@config_path)} file contains user configuration, and can be transferred to different computers."
114
- end
148
+ say "Saving configuration to #{system_path(config.path)} ... "
115
149
 
116
- true
150
+ changed = Commander::Command::Options.new(options)
151
+ changed.rhlogin = username
152
+ changed.password = password
153
+
154
+ FileUtils.mkdir_p File.dirname(config.path)
155
+ config.save!(changed)
156
+ options.__replace__(changed)
157
+
158
+ success "done"
117
159
  end
118
160
 
119
- # Read in @config_path now that it exists (was skipped before because it did
120
- # not exist
121
- RHC::Config.set_local_config(@config_path)
161
+ true
122
162
  end
123
163
 
124
164
  def config_ssh_key_stage
125
- if RHC::Config.should_run_ssh_wizard?
126
- paragraph do
127
- say "No SSH keys were found. We will generate a pair of keys for you."
128
- end
129
- ssh_pub_key_file_path = generate_ssh_key_ruby()
130
- paragraph do
131
- say " Created: #{ssh_pub_key_file_path}\n\n"
132
- end
165
+ if config.should_run_ssh_wizard?
166
+ paragraph{ say "No SSH keys were found. We will generate a pair of keys for you." }
167
+
168
+ ssh_pub_key_file_path = generate_ssh_key_ruby
169
+ paragraph{ say " Created: #{ssh_pub_key_file_path}" }
133
170
  end
134
171
  true
135
172
  end
@@ -137,60 +174,54 @@ EOF
137
174
  # return true if the account has the public key defined by
138
175
  # RHC::Config::ssh_pub_key_file_path
139
176
  def ssh_key_uploaded?
140
- @ssh_keys ||= @rest_client.sshkeys
141
- @ssh_keys.any? { |k| k.fingerprint == fingerprint_for_default_key }
177
+ ssh_keys.any? { |k| k.fingerprint == fingerprint_for_default_key }
142
178
  end
143
179
 
144
180
  def existing_keys_info
145
- return unless @ssh_keys
146
- # TODO: This ERB format is shared with RHC::Commands::Sshkey; should be refactored
147
- @ssh_keys.inject("Current Keys: \n") do |result, key|
148
- erb = ::RHC::Helpers.ssh_key_display_format
149
- result += format(key, erb)
150
- end
181
+ return unless ssh_keys
182
+ indent{ ssh_keys.each{ |key| paragraph{ display_key(key) } } }
151
183
  end
152
184
 
153
185
  def get_preferred_key_name
154
- paragraph do
155
- say "You can enter a name for your key, or leave it blank to use the default name. " \
156
- "Using the same name as an existing key will overwrite the old key."
157
- end
158
186
  key_name = 'default'
159
187
 
160
- if @ssh_keys.empty?
188
+ if ssh_keys.empty?
161
189
  paragraph do
162
- say <<-DEFAULT_KEY_UPLOAD_MSG
163
- Since you do not have any keys associated with your OpenShift account,
164
- your new key will be uploaded as the 'default' key
165
- DEFAULT_KEY_UPLOAD_MSG
190
+ info "Since you do not have any keys associated with your OpenShift account, "\
191
+ "your new key will be uploaded as the 'default' key."
166
192
  end
167
193
  else
168
- section(:top => 1) { say existing_keys_info }
194
+ paragraph do
195
+ say "You can enter a name for your key, or leave it blank to use the default name. " \
196
+ "Using the same name as an existing key will overwrite the old key."
197
+ end
198
+
199
+ paragraph { existing_keys_info }
169
200
 
170
201
  key_fingerprint = fingerprint_for_default_key
171
202
  unless key_fingerprint
172
203
  paragraph do
173
- say <<-CONFIG_KEY_INVALID
174
- Your ssh public key at #{RHC::Config.ssh_pub_key_file_path} is invalid or unreadable.
175
- The setup can not continue until you manually remove or fix both of your
176
- public and private keys id_rsa keys.
177
- CONFIG_KEY_INVALID
204
+ say "Your ssh public key at #{system_path(RHC::Config.ssh_pub_key_file_path)} is invalid or unreadable. "\
205
+ "Setup can not continue until you manually remove or fix your "\
206
+ "public and private keys id_rsa keys."
178
207
  end
179
208
  return nil
180
209
  end
210
+
181
211
  hostname = Socket.gethostname.gsub(/\..*\z/,'')
182
- username = @username ? @username.gsub(/@.*/, '') : ''
183
- pubkey_base_name = "#{username}#{hostname}".gsub(/[^A-Za-z0-9]/,'').slice(0,16)
184
- pubkey_default_name = find_unique_key_name(
185
- :keys => @ssh_keys,
212
+ userkey = username ? username.gsub(/@.*/, '') : ''
213
+ pubkey_base_name = "#{userkey}#{hostname}".gsub(/[^A-Za-z0-9]/,'').slice(0,16)
214
+ default_name = find_unique_key_name(
215
+ :keys => ssh_keys,
186
216
  :base => pubkey_base_name,
187
- :max_length => RHC::DEFAULT_MAX_LENGTH
217
+ :max_length => DEFAULT_MAX_LENGTH
188
218
  )
189
-
219
+
190
220
  paragraph do
191
- key_name = ask("Provide a name for this key: ") do |q|
192
- q.default = pubkey_default_name
193
- q.validate = lambda { |p| RHC::check_key(p) }
221
+ key_name = ask("Provide a name for this key: ") do |q|
222
+ q.default = default_name
223
+ q.validate = /^[0-9a-zA-Z]*$/
224
+ q.responses[:not_valid] = 'Your key name must be letters and numbers only.'
194
225
  end
195
226
  end
196
227
  end
@@ -201,12 +232,12 @@ public and private keys id_rsa keys.
201
232
  # given the base name and the maximum length,
202
233
  # find a name that does not clash with what is in opts[:keys]
203
234
  def find_unique_key_name(opts)
204
- keys = opts[:keys] || @ssh_keys
235
+ keys = opts[:keys] || ssh_keys
205
236
  base = opts[:base] || 'default'
206
- max = opts[:max_length] || RHC::DEFAULT_MAX_LENGTH # in rhc-common.rb
237
+ max = opts[:max_length] || DEFAULT_MAX_LENGTH
207
238
  key_name_suffix = 1
208
239
  candidate = base
209
- while @ssh_keys.detect { |k| k.name == candidate }
240
+ while ssh_keys.detect { |k| k.name == candidate }
210
241
  candidate = base.slice(0, max - key_name_suffix.to_s.length) +
211
242
  key_name_suffix.to_s
212
243
  key_name_suffix += 1
@@ -219,35 +250,42 @@ public and private keys id_rsa keys.
219
250
  return false unless key_name
220
251
 
221
252
  type, content, comment = ssh_key_triple_for_default_key
222
- say "type: %s\ncontent: %s\nfingerprint: %s" % [type, content, fingerprint_for_default_key]
253
+ indent do
254
+ say table([['Type:', type], ['Fingerprint:', fingerprint_for_default_key]])
255
+ end
223
256
 
224
- if !@ssh_keys.empty? && @ssh_keys.any? { |k| k.name == key_name }
225
- say "Key with the name #{key_name} already exists. Updating... "
226
- key = @rest_client.find_key(key_name)
227
- key.update(type, content)
228
- else
229
- say "Uploading key '#{key_name}' from #{RHC::Config::ssh_pub_key_file_path}"
230
- @rest_client.add_key key_name, content, type
257
+ paragraph do
258
+ if !ssh_keys.empty? && ssh_keys.any? { |k| k.name == key_name }
259
+ clear_ssh_keys_cache
260
+ say "Key with the name #{key_name} already exists. Updating ... "
261
+ key = rest_client.find_key(key_name)
262
+ key.update(type, content)
263
+ else
264
+ clear_ssh_keys_cache
265
+ say "Uploading key '#{key_name}' from #{system_path(RHC::Config::ssh_pub_key_file_path)} ... "
266
+ rest_client.add_key key_name, content, type
267
+ end
268
+ success "done"
231
269
  end
232
-
270
+
233
271
  true
234
272
  end
235
273
 
236
274
  def upload_ssh_key_stage
237
275
  return true if ssh_key_uploaded?
238
276
 
239
- upload = false
240
- section do
241
- upload = agree "Your public ssh key must be uploaded to the OpenShift server. Would you like us to upload it for you? (yes/no) "
277
+ upload = paragraph do
278
+ agree "Your public SSH key must be uploaded to the OpenShift server to access code. Upload now? (yes|no) "
242
279
  end
243
280
 
244
281
  if upload
245
282
  upload_ssh_key
246
283
  else
247
284
  paragraph do
248
- say "You can upload your ssh key at a later time using the 'rhc sshkey' command"
285
+ info "You can upload your SSH key at a later time using the 'rhc sshkey' command"
249
286
  end
250
287
  end
288
+
251
289
  true
252
290
  end
253
291
 
@@ -266,9 +304,6 @@ public and private keys id_rsa keys.
266
304
  if windows?
267
305
  windows_install
268
306
  else
269
- paragraph do
270
- say "We will now check to see if you have the necessary client tools installed."
271
- end
272
307
  generic_unix_install_check
273
308
  end
274
309
  true
@@ -276,14 +311,22 @@ public and private keys id_rsa keys.
276
311
 
277
312
  def config_namespace_stage
278
313
  paragraph do
279
- say "Checking for your namespace ... "
280
- domains = @rest_client.domains
314
+ say "Checking your namespace ... "
315
+ domains = rest_client.domains
281
316
  if domains.length == 0
282
- say "not found"
317
+ warn "none"
318
+
319
+ paragraph do
320
+ say "Your namespace is unique to your account and is the suffix of the " \
321
+ "public URLs we assign to your applications. You may configure your " \
322
+ "namespace here or leave it blank and use 'rhc domain create' to " \
323
+ "create a namespace later. You will not be able to create " \
324
+ "applications without first creating a namespace."
325
+ end
326
+
283
327
  ask_for_namespace
284
328
  else
285
- say "found namespace:"
286
- domains.each { |d| say " #{d.id}" }
329
+ success domains.map(&:id).join(', ')
287
330
  end
288
331
  end
289
332
 
@@ -293,39 +336,99 @@ public and private keys id_rsa keys.
293
336
  def show_app_info_stage
294
337
  section do
295
338
  say "Checking for applications ... "
296
- end
297
-
298
- apps = @rest_client.domains.inject([]) do |list, domain|
299
- list += domain.applications
300
- end
301
-
302
- if !apps.nil? and !apps.empty?
303
- section(:bottom => 1) do
304
- say "found"
305
- apps.each do |app|
306
- if app.app_url.nil? && app.u
307
- say " * #{app.name} - no public url (you need to add a namespace)"
308
- else
309
- say " * #{app.name} - #{app.app_url}"
339
+
340
+ if !applications.nil? and !applications.empty?
341
+ success "found #{applications.length}"
342
+
343
+ paragraph do
344
+ indent do
345
+ say table(applications.map do |app|
346
+ [app.name, app.app_url]
347
+ end)
310
348
  end
311
349
  end
350
+ else
351
+ info "none"
352
+
353
+ paragraph{ say "Run 'rhc app create' to create your first application." }
354
+ 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|
357
+ [' ', cart.display_name, "rhc app create <app name> #{cart.name}"]
358
+ end).join("\n")
359
+ end
312
360
  end
313
- else
314
- section(:bottom => 1) { say "none found" }
315
361
  paragraph do
316
- say "Run 'rhc app create' to create your first application.\n\n"
317
- say "Below is a list of the types of application you can create: \n"
318
-
319
- application_types = @rest_client.find_cartridges :type => "standalone"
320
- application_types.sort {|a,b| a.name <=> b.name }.each do |cart|
321
- say " * #{cart.name} - rhc app create <app name> #{cart.name}"
362
+ indent do
363
+ say "You are using #{color(self.user.consumed_gears.to_s, :green)} of #{color(self.user.max_gears.to_s, :green)} total gears" if user.max_gears.is_a? Fixnum
364
+ say "The following gear sizes are available to you: #{self.user.capabilities.gear_sizes.join(', ')}" if user.capabilities.gear_sizes.present?
322
365
  end
323
366
  end
324
367
  end
368
+ true
369
+ end
370
+
371
+ # Perform basic tests to ensure that setup is sane
372
+ # search for private methods starting with "test_" and execute them
373
+ # in alphabetical order
374
+ # NOTE: The order itself is not important--the tests should be independent.
375
+ # However, the hash order is unpredictable in Ruby 1.8, and is preserved in
376
+ # 1.9. There are two similar tests that can fail under the same conditions,
377
+ # and this makes the spec results inconsistent between 1.8 and 1.9.
378
+ # Thus, we force an order with #sort to ensure spec passage on both.
379
+ def setup_test_stage
380
+ say "Checking common problems "
381
+ tests = private_methods.select {|m| m.to_s.start_with? 'test_'}
382
+ tests.sort.each do |test|
383
+ send(test)
384
+ end.tap do |pass|
385
+ success(' done') if pass
386
+ end
325
387
 
326
388
  true
327
389
  end
328
390
 
391
+ # cached list of applications needed for test stage
392
+ def applications
393
+ @applications ||= rest_client.domains.map(&:applications).flatten
394
+ end
395
+
396
+ ###
397
+ # tests for specific user errors
398
+
399
+ def test_private_key_mode
400
+ pub_key_file = RHC::Config.ssh_pub_key_file_path
401
+ private_key_file = RHC::Config.ssh_priv_key_file_path
402
+ 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:
409
+ end
410
+ end
411
+
412
+ # test connectivity an app
413
+ def test_ssh_connectivity
414
+ unless ssh_key_uploaded?
415
+ return true
416
+ end
417
+
418
+ applications.take(1).each do |app|
419
+ begin
420
+ ssh = Net::SSH.start(app.host, app.uuid, :timeout => 60)
421
+ return true
422
+ 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
425
+ ensure
426
+ ssh.close if ssh
427
+ end
428
+ end
429
+ end
430
+
431
+ ###
329
432
  def finalize_stage
330
433
  paragraph do
331
434
  say "The OpenShift client tools have been configured on your computer. " \
@@ -338,59 +441,51 @@ public and private keys id_rsa keys.
338
441
 
339
442
  def config_namespace(namespace)
340
443
  # skip if string is empty
341
- paragraph do
342
- if namespace.nil? or namespace.chomp.length == 0
343
- say "Skipping! You may create a namespace using 'rhc domain create'"
344
- return true
345
- end
444
+ if namespace.nil? or namespace.chomp.length == 0
445
+ paragraph{ info "You may create a namespace later through 'rhc domain create'" }
446
+ return true
447
+ end
346
448
 
347
- begin
348
- domain = @rest_client.add_domain(namespace)
449
+ begin
450
+ domain = rest_client.add_domain(namespace)
349
451
 
350
- say "Your domain name '#{domain.id}' has been successfully created"
351
- rescue RHC::Rest::ValidationException => e
352
- say e.message
353
- return false
354
- end
452
+ success "Your domain name '#{domain.id}' has been successfully created"
453
+ rescue RHC::Rest::ValidationException => e
454
+ error e.message || "Unknown error during namespace creation."
455
+ return false
355
456
  end
356
457
  true
357
458
  end
358
459
 
359
460
  def ask_for_namespace
360
- paragraph do
361
- say "Your namespace is unique to your account and is the suffix of the " \
362
- "public URLs we assign to your applications. You may configure your " \
363
- "namespace here or leave it blank and use 'rhc domain create' to " \
364
- "create a namespace later. You will not be able to create " \
365
- "applications without first creating a namespace."
366
- end
367
-
368
461
  # Ask for a namespace at least once, configure the namespace if a valid,
369
462
  # non-blank string is provided.
370
- namespace = nil
371
- first_pass = true
372
- while first_pass or !config_namespace namespace do
373
- first_pass = false
374
- paragraph do
375
- namespace = ask "Please enter a namespace or leave this blank if you wish to skip this step:" do |q|
376
- q.validate = lambda{ |p| RHC::check_namespace p }
377
- q.responses[:not_valid] = 'The namespace value must contain only letters and/or numbers (A-Za-z0-9):'
463
+ namespace = nil
464
+ paragraph do
465
+ begin
466
+ namespace = ask "Please enter a namespace (letters and numbers only) |<none>|: " do |q|
467
+ #q.validate = lambda{ |p| RHC::check_namespace p }
468
+ #q.responses[:not_valid] = 'The namespace value must contain only letters and/or numbers (A-Za-z0-9):'
378
469
  q.responses[:ask_on_error] = ''
379
470
  end
380
- end
471
+ end while !config_namespace(namespace)
381
472
  end
382
473
  end
383
474
 
384
- def generic_unix_install_check(show_action=true)
385
- section(:top => 1) { say "Checking for git ... " } if show_action
386
- if has_git?
387
- section(:bottom => 1) { say "found" }
388
- else
389
- section(:bottom => 1) { say "needs to be installed" }
390
- paragraph do
391
- say "Automated installation of client tools is not supported for " \
392
- "your platform. You will need to manually install git for full " \
393
- "OpenShift functionality."
475
+ def generic_unix_install_check
476
+ paragraph do
477
+ say "Checking for git ... "
478
+
479
+ if has_git?
480
+ success("found #{git_version}") rescue success('found')
481
+ else
482
+ warn "needs to be installed"
483
+
484
+ paragraph do
485
+ say "Automated installation of client tools is not supported for " \
486
+ "your platform. You will need to manually install git for full " \
487
+ "OpenShift functionality."
488
+ end
394
489
  end
395
490
  end
396
491
  end
@@ -399,10 +494,11 @@ public and private keys id_rsa keys.
399
494
  # Finding windows executables is hard since they can get installed
400
495
  # in non standard directories. Punt on this for now and simply
401
496
  # print out urls and some instructions
402
- say <<EOF
497
+ warn <<EOF
498
+
403
499
  In order to fully interact with OpenShift you will need to install and configure a git client if you have not already done so.
404
500
 
405
- Documentation for installing other tools you will need for OpenShift can be found at https://#{@libra_server}/app/getting_started#install_client_tools
501
+ Documentation for installing other tools you will need for OpenShift can be found at https://openshift.redhat.com/community/developers/install-the-client-tools
406
502
 
407
503
  We recommend these free applications:
408
504
 
@@ -412,45 +508,19 @@ We recommend these free applications:
412
508
  EOF
413
509
  end
414
510
 
415
- def git_version_exec
416
- `git --version 2>&1`
417
- end
418
-
419
- def has_git?
420
- git_version_exec
421
- $?.success?
422
- rescue
423
- false
424
- end
425
-
426
511
  def debug?
427
512
  @debug
428
513
  end
514
+
515
+ protected
516
+ attr_writer :rest_client
429
517
  end
430
518
 
431
519
  class RerunWizard < Wizard
432
- def initialize(config, login=nil)
433
- super(config, login)
434
- end
435
-
436
- def create_config_stage
437
- if File.exists? @config_path
438
- backup = "#{@config_path}.bak"
439
- paragraph do
440
- say "Configuration file #{@config_path} already exists, " \
441
- "backing up to #{backup}"
442
- end
443
- FileUtils.cp(@config_path, backup)
444
- FileUtils.rm(@config_path)
445
- end
446
- super
447
- true
448
- end
449
520
 
450
521
  def finalize_stage
451
- paragraph do
452
- say "Thank you for setting up your system. You can rerun this at any time " \
453
- "by calling 'rhc setup'."
522
+ section :top => 1 do
523
+ success "Your client tools are now configured."
454
524
  end
455
525
  true
456
526
  end
@@ -463,9 +533,9 @@ EOF
463
533
  STAGES
464
534
  end
465
535
 
466
- def initialize(rest_client)
467
- @rest_client = rest_client
468
- super RHC::Config
536
+ def initialize(rest_client, config, options)
537
+ self.rest_client = rest_client
538
+ super config, options
469
539
  end
470
540
  end
471
541
  end