rhc 1.2.7 → 1.3.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|