rhc 1.2.7 → 1.3.8
Sign up to get free protection for your applications and to get access to all the features.
- data/bin/rhc +6 -8
- data/bin/rhc-chk +23 -10
- data/features/domain.feature +1 -1
- data/features/lib/rhc_helper.rb +3 -2
- data/features/lib/rhc_helper/api.rb +7 -0
- data/features/lib/rhc_helper/app.rb +8 -10
- data/features/lib/rhc_helper/domain.rb +2 -1
- data/features/lib/rhc_helper/runnable.rb +2 -24
- data/features/sshkey.feature +3 -3
- data/features/step_definitions/cartridge_steps.rb +6 -6
- data/features/step_definitions/client_steps.rb +0 -1
- data/features/step_definitions/sshkey_steps.rb +2 -2
- data/features/support/before_hooks.rb +0 -1
- data/features/support/env.rb +5 -3
- data/lib/rhc-common.rb +1 -1
- data/lib/rhc.rb +9 -8
- data/lib/rhc/auth.rb +3 -0
- data/lib/rhc/auth/basic.rb +54 -0
- data/lib/rhc/cartridge_helpers.rb +11 -5
- data/lib/rhc/cli.rb +4 -2
- data/lib/rhc/command_runner.rb +35 -30
- data/lib/rhc/commands.rb +127 -18
- data/lib/rhc/commands/account.rb +24 -0
- data/lib/rhc/commands/alias.rb +1 -1
- data/lib/rhc/commands/app.rb +210 -209
- data/lib/rhc/commands/apps.rb +22 -0
- data/lib/rhc/commands/base.rb +10 -77
- data/lib/rhc/commands/cartridge.rb +35 -35
- data/lib/rhc/commands/domain.rb +20 -13
- data/lib/rhc/commands/git_clone.rb +30 -0
- data/lib/rhc/commands/{port-forward.rb → port_forward.rb} +3 -3
- data/lib/rhc/commands/server.rb +28 -16
- data/lib/rhc/commands/setup.rb +18 -1
- data/lib/rhc/commands/snapshot.rb +4 -4
- data/lib/rhc/commands/sshkey.rb +4 -18
- data/lib/rhc/commands/tail.rb +32 -9
- data/lib/rhc/config.rb +168 -99
- data/lib/rhc/context_helper.rb +22 -9
- data/lib/rhc/core_ext.rb +41 -1
- data/lib/rhc/exceptions.rb +21 -5
- data/lib/rhc/git_helpers.rb +81 -0
- data/lib/rhc/help_formatter.rb +21 -1
- data/lib/rhc/helpers.rb +222 -87
- data/lib/rhc/output_helpers.rb +94 -110
- data/lib/rhc/rest.rb +15 -198
- data/lib/rhc/rest/api.rb +88 -0
- data/lib/rhc/rest/application.rb +29 -30
- data/lib/rhc/rest/attributes.rb +27 -0
- data/lib/rhc/rest/base.rb +29 -33
- data/lib/rhc/rest/cartridge.rb +42 -20
- data/lib/rhc/rest/client.rb +351 -89
- data/lib/rhc/rest/domain.rb +7 -13
- data/lib/rhc/rest/gear_group.rb +1 -1
- data/lib/rhc/rest/key.rb +7 -2
- data/lib/rhc/rest/mock.rb +609 -0
- data/lib/rhc/rest/user.rb +6 -2
- data/lib/rhc/{ssh_key_helpers.rb → ssh_helpers.rb} +58 -28
- data/lib/rhc/{targz.rb → tar_gz.rb} +0 -0
- data/lib/rhc/usage_templates/command_help.erb +4 -1
- data/lib/rhc/usage_templates/help.erb +24 -11
- data/lib/rhc/usage_templates/options_help.erb +14 -0
- data/lib/rhc/wizard.rb +283 -213
- data/spec/keys/example.pem +23 -0
- data/spec/keys/example_private.pem +27 -0
- data/spec/keys/server.pem +19 -0
- data/spec/rest_spec_helper.rb +3 -371
- data/spec/rhc/auth_spec.rb +226 -0
- data/spec/rhc/cli_spec.rb +41 -14
- data/spec/rhc/command_spec.rb +44 -15
- data/spec/rhc/commands/account_spec.rb +41 -0
- data/spec/rhc/commands/alias_spec.rb +16 -15
- data/spec/rhc/commands/app_spec.rb +115 -92
- data/spec/rhc/commands/apps_spec.rb +39 -0
- data/spec/rhc/commands/cartridge_spec.rb +134 -112
- data/spec/rhc/commands/domain_spec.rb +31 -86
- data/spec/rhc/commands/git_clone_spec.rb +56 -0
- data/spec/rhc/commands/{port-forward_spec.rb → port_forward_spec.rb} +27 -32
- data/spec/rhc/commands/server_spec.rb +28 -3
- data/spec/rhc/commands/setup_spec.rb +29 -11
- data/spec/rhc/commands/snapshot_spec.rb +4 -3
- data/spec/rhc/commands/sshkey_spec.rb +24 -56
- data/spec/rhc/commands/tail_spec.rb +26 -9
- data/spec/rhc/commands/threaddump_spec.rb +12 -11
- data/spec/rhc/config_spec.rb +211 -164
- data/spec/rhc/context_spec.rb +2 -0
- data/spec/rhc/helpers_spec.rb +242 -46
- data/spec/rhc/rest_application_spec.rb +42 -28
- data/spec/rhc/rest_client_spec.rb +110 -93
- data/spec/rhc/rest_spec.rb +220 -131
- data/spec/rhc/targz_spec.rb +1 -1
- data/spec/rhc/wizard_spec.rb +435 -624
- data/spec/spec.opts +1 -1
- data/spec/spec_helper.rb +140 -6
- data/spec/wizard_spec_helper.rb +326 -0
- metadata +163 -143
- data/lib/rhc/client.rb +0 -17
- data/lib/rhc/git_helper.rb +0 -59
data/lib/rhc/rest/user.rb
CHANGED
@@ -1,9 +1,9 @@
|
|
1
|
-
require '
|
1
|
+
require 'ostruct'
|
2
2
|
|
3
3
|
module RHC
|
4
4
|
module Rest
|
5
5
|
class User < Base
|
6
|
-
|
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
|
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 =
|
41
|
-
|
42
|
-
|
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(
|
50
|
-
File.chmod(0600,
|
51
|
-
File.open(
|
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
|
92
|
+
ssh_add
|
54
93
|
end
|
55
|
-
|
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
|
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
|
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
|
-
|
124
|
-
|
125
|
-
|
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:
|
1
|
+
Usage: rhc [--help] [--version] [--debug] <command> [<args>]
|
2
|
+
|
2
3
|
<%= Array(program :description).first %>
|
3
4
|
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
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
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
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 <
|
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 -%>
|
data/lib/rhc/wizard.rb
CHANGED
@@ -1,16 +1,15 @@
|
|
1
|
-
require 'rhc-common'
|
2
1
|
require 'rhc/helpers'
|
3
|
-
require 'rhc/
|
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
|
-
|
13
|
-
|
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
|
-
|
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
|
-
@
|
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
|
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
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
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
|
-
|
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
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
126
|
-
paragraph
|
127
|
-
|
128
|
-
|
129
|
-
ssh_pub_key_file_path
|
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
|
-
|
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
|
146
|
-
|
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
|
188
|
+
if ssh_keys.empty?
|
161
189
|
paragraph do
|
162
|
-
|
163
|
-
|
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
|
-
|
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
|
174
|
-
|
175
|
-
|
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
|
-
|
183
|
-
pubkey_base_name = "#{
|
184
|
-
|
185
|
-
: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 =>
|
217
|
+
:max_length => DEFAULT_MAX_LENGTH
|
188
218
|
)
|
189
|
-
|
219
|
+
|
190
220
|
paragraph do
|
191
|
-
key_name =
|
192
|
-
q.default =
|
193
|
-
q.validate =
|
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] ||
|
235
|
+
keys = opts[:keys] || ssh_keys
|
205
236
|
base = opts[:base] || 'default'
|
206
|
-
max = opts[:max_length] ||
|
237
|
+
max = opts[:max_length] || DEFAULT_MAX_LENGTH
|
207
238
|
key_name_suffix = 1
|
208
239
|
candidate = base
|
209
|
-
while
|
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
|
-
|
253
|
+
indent do
|
254
|
+
say table([['Type:', type], ['Fingerprint:', fingerprint_for_default_key]])
|
255
|
+
end
|
223
256
|
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
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 =
|
240
|
-
|
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
|
-
|
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
|
280
|
-
domains =
|
314
|
+
say "Checking your namespace ... "
|
315
|
+
domains = rest_client.domains
|
281
316
|
if domains.length == 0
|
282
|
-
|
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
|
-
|
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
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
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
|
-
|
317
|
-
|
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
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
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
|
-
|
348
|
-
|
449
|
+
begin
|
450
|
+
domain = rest_client.add_domain(namespace)
|
349
451
|
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
|
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
|
371
|
-
|
372
|
-
|
373
|
-
|
374
|
-
|
375
|
-
|
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
|
385
|
-
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
|
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
|
-
|
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
|
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
|
-
|
452
|
-
|
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
|
-
|
468
|
-
super
|
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
|