pbox 1.17.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/COPYRIGHT +1 -0
- data/LICENSE +11 -0
- data/README.md +40 -0
- data/Rakefile +6 -0
- data/autocomplete/pbox_bash +1639 -0
- data/bin/pbox +37 -0
- data/conf/protonbox.conf +8 -0
- data/features/assets/deploy.tar.gz +0 -0
- data/features/core_feature.rb +178 -0
- data/features/deployments_feature.rb +127 -0
- data/features/domains_feature.rb +49 -0
- data/features/keys_feature.rb +37 -0
- data/features/members_feature.rb +166 -0
- data/lib/rhc/auth/basic.rb +64 -0
- data/lib/rhc/auth/token.rb +102 -0
- data/lib/rhc/auth/token_store.rb +53 -0
- data/lib/rhc/auth.rb +5 -0
- data/lib/rhc/autocomplete.rb +66 -0
- data/lib/rhc/autocomplete_templates/bash.erb +39 -0
- data/lib/rhc/cartridge_helpers.rb +118 -0
- data/lib/rhc/cli.rb +40 -0
- data/lib/rhc/command_runner.rb +186 -0
- data/lib/rhc/commands/account.rb +25 -0
- data/lib/rhc/commands/alias.rb +124 -0
- data/lib/rhc/commands/app.rb +701 -0
- data/lib/rhc/commands/apps.rb +20 -0
- data/lib/rhc/commands/authorization.rb +96 -0
- data/lib/rhc/commands/base.rb +174 -0
- data/lib/rhc/commands/cartridge.rb +326 -0
- data/lib/rhc/commands/deployment.rb +82 -0
- data/lib/rhc/commands/domain.rb +167 -0
- data/lib/rhc/commands/env.rb +142 -0
- data/lib/rhc/commands/git_clone.rb +29 -0
- data/lib/rhc/commands/logout.rb +51 -0
- data/lib/rhc/commands/member.rb +148 -0
- data/lib/rhc/commands/port_forward.rb +197 -0
- data/lib/rhc/commands/server.rb +40 -0
- data/lib/rhc/commands/setup.rb +60 -0
- data/lib/rhc/commands/snapshot.rb +137 -0
- data/lib/rhc/commands/ssh.rb +51 -0
- data/lib/rhc/commands/sshkey.rb +97 -0
- data/lib/rhc/commands/tail.rb +47 -0
- data/lib/rhc/commands/threaddump.rb +14 -0
- data/lib/rhc/commands.rb +396 -0
- data/lib/rhc/config.rb +320 -0
- data/lib/rhc/context_helper.rb +121 -0
- data/lib/rhc/core_ext.rb +202 -0
- data/lib/rhc/coverage_helper.rb +33 -0
- data/lib/rhc/deployment_helpers.rb +88 -0
- data/lib/rhc/exceptions.rb +232 -0
- data/lib/rhc/git_helpers.rb +91 -0
- data/lib/rhc/help_formatter.rb +55 -0
- data/lib/rhc/helpers.rb +477 -0
- data/lib/rhc/highline_extensions.rb +479 -0
- data/lib/rhc/json.rb +51 -0
- data/lib/rhc/output_helpers.rb +260 -0
- data/lib/rhc/rest/activation.rb +11 -0
- data/lib/rhc/rest/alias.rb +42 -0
- data/lib/rhc/rest/api.rb +87 -0
- data/lib/rhc/rest/application.rb +332 -0
- data/lib/rhc/rest/attributes.rb +36 -0
- data/lib/rhc/rest/authorization.rb +8 -0
- data/lib/rhc/rest/base.rb +79 -0
- data/lib/rhc/rest/cartridge.rb +154 -0
- data/lib/rhc/rest/client.rb +650 -0
- data/lib/rhc/rest/deployment.rb +18 -0
- data/lib/rhc/rest/domain.rb +98 -0
- data/lib/rhc/rest/environment_variable.rb +15 -0
- data/lib/rhc/rest/gear_group.rb +16 -0
- data/lib/rhc/rest/httpclient.rb +145 -0
- data/lib/rhc/rest/key.rb +44 -0
- data/lib/rhc/rest/membership.rb +105 -0
- data/lib/rhc/rest/mock.rb +1024 -0
- data/lib/rhc/rest/user.rb +32 -0
- data/lib/rhc/rest.rb +148 -0
- data/lib/rhc/ssh_helpers.rb +378 -0
- data/lib/rhc/tar_gz.rb +51 -0
- data/lib/rhc/usage_templates/command_help.erb +51 -0
- data/lib/rhc/usage_templates/command_syntax_help.erb +11 -0
- data/lib/rhc/usage_templates/help.erb +35 -0
- data/lib/rhc/usage_templates/missing_help.erb +1 -0
- data/lib/rhc/usage_templates/options_help.erb +12 -0
- data/lib/rhc/vendor/okjson.rb +600 -0
- data/lib/rhc/vendor/parseconfig.rb +178 -0
- data/lib/rhc/vendor/sshkey.rb +253 -0
- data/lib/rhc/vendor/zliby.rb +628 -0
- data/lib/rhc/version.rb +5 -0
- data/lib/rhc/wizard.rb +633 -0
- data/lib/rhc.rb +34 -0
- data/spec/coverage_helper.rb +89 -0
- data/spec/direct_execution_helper.rb +338 -0
- 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 +31 -0
- data/spec/rhc/assets/cert.crt +22 -0
- data/spec/rhc/assets/cert_key_rsa +27 -0
- data/spec/rhc/assets/empty.txt +0 -0
- data/spec/rhc/assets/env_vars.txt +7 -0
- data/spec/rhc/assets/env_vars_2.txt +1 -0
- data/spec/rhc/assets/foo.txt +1 -0
- data/spec/rhc/assets/targz_corrupted.tar.gz +1 -0
- data/spec/rhc/assets/targz_sample.tar.gz +0 -0
- data/spec/rhc/auth_spec.rb +442 -0
- data/spec/rhc/cli_spec.rb +188 -0
- data/spec/rhc/command_spec.rb +435 -0
- data/spec/rhc/commands/account_spec.rb +42 -0
- data/spec/rhc/commands/alias_spec.rb +333 -0
- data/spec/rhc/commands/app_spec.rb +754 -0
- data/spec/rhc/commands/apps_spec.rb +39 -0
- data/spec/rhc/commands/authorization_spec.rb +145 -0
- data/spec/rhc/commands/cartridge_spec.rb +641 -0
- data/spec/rhc/commands/deployment_spec.rb +286 -0
- data/spec/rhc/commands/domain_spec.rb +383 -0
- data/spec/rhc/commands/env_spec.rb +493 -0
- data/spec/rhc/commands/git_clone_spec.rb +80 -0
- data/spec/rhc/commands/logout_spec.rb +86 -0
- data/spec/rhc/commands/member_spec.rb +228 -0
- data/spec/rhc/commands/port_forward_spec.rb +217 -0
- data/spec/rhc/commands/server_spec.rb +69 -0
- data/spec/rhc/commands/setup_spec.rb +118 -0
- data/spec/rhc/commands/snapshot_spec.rb +179 -0
- data/spec/rhc/commands/ssh_spec.rb +163 -0
- data/spec/rhc/commands/sshkey_spec.rb +188 -0
- data/spec/rhc/commands/tail_spec.rb +81 -0
- data/spec/rhc/commands/threaddump_spec.rb +84 -0
- data/spec/rhc/config_spec.rb +407 -0
- data/spec/rhc/helpers_spec.rb +524 -0
- data/spec/rhc/highline_extensions_spec.rb +314 -0
- data/spec/rhc/json_spec.rb +30 -0
- data/spec/rhc/rest_application_spec.rb +248 -0
- data/spec/rhc/rest_client_spec.rb +752 -0
- data/spec/rhc/rest_spec.rb +740 -0
- data/spec/rhc/targz_spec.rb +55 -0
- data/spec/rhc/wizard_spec.rb +756 -0
- data/spec/spec_helper.rb +575 -0
- data/spec/wizard_spec_helper.rb +330 -0
- metadata +435 -0
data/lib/rhc/wizard.rb
ADDED
@@ -0,0 +1,633 @@
|
|
1
|
+
require 'rhc'
|
2
|
+
require 'fileutils'
|
3
|
+
require 'socket'
|
4
|
+
|
5
|
+
module RHC
|
6
|
+
class Wizard
|
7
|
+
include HighLine::SystemExtensions
|
8
|
+
|
9
|
+
def self.has_configuration?
|
10
|
+
File.exists? RHC::Config.local_config_path
|
11
|
+
end
|
12
|
+
|
13
|
+
DEFAULT_MAX_LENGTH = 16
|
14
|
+
|
15
|
+
CONFIG_STAGES = [
|
16
|
+
:login_stage,
|
17
|
+
:create_config_stage,
|
18
|
+
]
|
19
|
+
KEY_STAGES = [
|
20
|
+
:config_ssh_key_stage,
|
21
|
+
:upload_ssh_key_stage,
|
22
|
+
]
|
23
|
+
TEST_STAGES = [
|
24
|
+
:install_client_tools_stage,
|
25
|
+
:setup_test_stage,
|
26
|
+
]
|
27
|
+
NAMESPACE_STAGES = [
|
28
|
+
:config_namespace_stage,
|
29
|
+
]
|
30
|
+
APP_STAGES = [
|
31
|
+
:show_app_info_stage,
|
32
|
+
]
|
33
|
+
STAGES = [:greeting_stage] + CONFIG_STAGES + KEY_STAGES + TEST_STAGES + NAMESPACE_STAGES + APP_STAGES + [:finalize_stage]
|
34
|
+
|
35
|
+
def stages
|
36
|
+
STAGES
|
37
|
+
end
|
38
|
+
|
39
|
+
attr_reader :rest_client
|
40
|
+
|
41
|
+
#
|
42
|
+
# Running the setup wizard may change the contents of opts and config if
|
43
|
+
# the create_config_stage completes successfully.
|
44
|
+
#
|
45
|
+
def initialize(config=RHC::Config.new, opts=Commander::Command::Options.new)
|
46
|
+
@config = config
|
47
|
+
@options = opts
|
48
|
+
end
|
49
|
+
|
50
|
+
# Public: Runs the setup wizard to make sure ~/.protonbox and ~/.ssh are correct
|
51
|
+
#
|
52
|
+
# Examples
|
53
|
+
#
|
54
|
+
# wizard.run()
|
55
|
+
# # => true
|
56
|
+
#
|
57
|
+
# Returns nil on failure or true on success
|
58
|
+
def run
|
59
|
+
stages.each do |stage|
|
60
|
+
debug "Running #{stage}"
|
61
|
+
if self.send(stage).nil?
|
62
|
+
return nil
|
63
|
+
end
|
64
|
+
end
|
65
|
+
true
|
66
|
+
end
|
67
|
+
|
68
|
+
protected
|
69
|
+
|
70
|
+
include RHC::Helpers
|
71
|
+
include RHC::SSHHelpers
|
72
|
+
include RHC::GitHelpers
|
73
|
+
include RHC::CartridgeHelpers
|
74
|
+
attr_reader :config, :options
|
75
|
+
attr_accessor :auth, :user
|
76
|
+
attr_writer :rest_client
|
77
|
+
|
78
|
+
def hostname
|
79
|
+
Socket.gethostname
|
80
|
+
end
|
81
|
+
|
82
|
+
def protonbox_server
|
83
|
+
options.server || config['protonbox_server'] || "api.protonbox.com"
|
84
|
+
end
|
85
|
+
|
86
|
+
def new_client_for_options
|
87
|
+
client_from_options({
|
88
|
+
:auth => auth,
|
89
|
+
})
|
90
|
+
end
|
91
|
+
|
92
|
+
def core_auth
|
93
|
+
@core_auth ||= RHC::Auth::Basic.new(options)
|
94
|
+
end
|
95
|
+
|
96
|
+
def token_auth
|
97
|
+
RHC::Auth::Token.new(options, core_auth, token_store)
|
98
|
+
end
|
99
|
+
|
100
|
+
def auth(reset=false)
|
101
|
+
@auth = nil if reset
|
102
|
+
@auth ||= begin
|
103
|
+
if options.token
|
104
|
+
token_auth
|
105
|
+
else
|
106
|
+
core_auth
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
def token_store
|
112
|
+
@token_store ||= RHC::Auth::TokenStore.new(config.home_conf_path)
|
113
|
+
end
|
114
|
+
|
115
|
+
def username
|
116
|
+
options.pblogin || (auth.username if auth.respond_to?(:username))
|
117
|
+
end
|
118
|
+
|
119
|
+
def print_dot
|
120
|
+
$terminal.instance_variable_get(:@output).print('.')
|
121
|
+
end
|
122
|
+
|
123
|
+
|
124
|
+
# cache SSH keys from the REST client
|
125
|
+
def ssh_keys
|
126
|
+
@ssh_keys ||= rest_client.sshkeys
|
127
|
+
end
|
128
|
+
|
129
|
+
# clear SSH key cache
|
130
|
+
def clear_ssh_keys_cache
|
131
|
+
@ssh_keys = nil
|
132
|
+
end
|
133
|
+
|
134
|
+
# return true if the account has the public key defined by
|
135
|
+
# RHC::Config::ssh_pub_key_file_path
|
136
|
+
def ssh_key_uploaded?
|
137
|
+
ssh_keys.present? && ssh_keys.any? { |k| k.fingerprint.present? && k.fingerprint == fingerprint_for_default_key }
|
138
|
+
end
|
139
|
+
|
140
|
+
def existing_keys_info
|
141
|
+
return unless ssh_keys
|
142
|
+
indent{ ssh_keys.each{ |key| paragraph{ display_key(key) } } }
|
143
|
+
end
|
144
|
+
|
145
|
+
def applications
|
146
|
+
@applications ||= rest_client.domains.map(&:applications).flatten
|
147
|
+
end
|
148
|
+
|
149
|
+
def namespace_optional?
|
150
|
+
true
|
151
|
+
end
|
152
|
+
|
153
|
+
#
|
154
|
+
# Stages
|
155
|
+
#
|
156
|
+
|
157
|
+
def greeting_stage
|
158
|
+
info "ProtonBox Client Tools (PBOX) Setup Wizard"
|
159
|
+
|
160
|
+
paragraph do
|
161
|
+
say "This wizard will help you upload your SSH keys, set your application namespace, and check that other programs like Git are properly installed."
|
162
|
+
end
|
163
|
+
|
164
|
+
true
|
165
|
+
end
|
166
|
+
|
167
|
+
def login_stage
|
168
|
+
if token_for_user
|
169
|
+
options.token = token_for_user
|
170
|
+
say "Using an existing token for #{options.pblogin} to login to #{protonbox_server}"
|
171
|
+
elsif options.pblogin
|
172
|
+
say "Using #{options.pblogin} to login to #{protonbox_server}"
|
173
|
+
end
|
174
|
+
|
175
|
+
self.rest_client = new_client_for_options
|
176
|
+
|
177
|
+
begin
|
178
|
+
rest_client.api
|
179
|
+
rescue RHC::Rest::CertificateVerificationFailed => e
|
180
|
+
debug "Certificate validation failed: #{e.reason}"
|
181
|
+
unless options.insecure
|
182
|
+
if RHC::Rest::SelfSignedCertificate === e
|
183
|
+
warn "The server's certificate is self-signed, which means that a secure connection can't be established to '#{protonbox_server}'."
|
184
|
+
else
|
185
|
+
warn "The server's certificate could not be verified, which means that a secure connection can't be established to '#{protonbox_server}'."
|
186
|
+
end
|
187
|
+
if protonbox_online_server?
|
188
|
+
paragraph{ warn "This may mean that a server between you and ProtonBox 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." }
|
189
|
+
return
|
190
|
+
else
|
191
|
+
paragraph{ warn "You may bypass this check, but any data you send to the server could be intercepted by others." }
|
192
|
+
return unless agree "Connect without checking the certificate? (yes|no): "
|
193
|
+
options.insecure = true
|
194
|
+
self.rest_client = new_client_for_options
|
195
|
+
retry
|
196
|
+
end
|
197
|
+
end
|
198
|
+
end
|
199
|
+
|
200
|
+
self.user = rest_client.user
|
201
|
+
options.pblogin = self.user.login unless username
|
202
|
+
|
203
|
+
if rest_client.supports_sessions? && !options.token && options.create_token != false
|
204
|
+
paragraph do
|
205
|
+
info "ProtonBox can create and store a token on disk which allows to you to access the server without using your password. The key is stored in your home directory and should be kept secret. You can delete the key at any time by running 'pbox logout'."
|
206
|
+
if options.create_token or agree "Generate a token now? (yes|no) "
|
207
|
+
say "Generating an authorization token for this client ... "
|
208
|
+
token = rest_client.new_session
|
209
|
+
options.token = token.token
|
210
|
+
self.auth(true).save(token.token)
|
211
|
+
self.rest_client = new_client_for_options
|
212
|
+
self.user = rest_client.user
|
213
|
+
|
214
|
+
success "lasts #{distance_of_time_in_words(token.expires_in_seconds)}"
|
215
|
+
end
|
216
|
+
end
|
217
|
+
end
|
218
|
+
true
|
219
|
+
end
|
220
|
+
|
221
|
+
def create_config_stage
|
222
|
+
paragraph do
|
223
|
+
if File.exists? config.path
|
224
|
+
backup = "#{@config.path}.bak"
|
225
|
+
FileUtils.cp(config.path, backup)
|
226
|
+
FileUtils.rm(config.path)
|
227
|
+
end
|
228
|
+
|
229
|
+
say "Saving configuration to #{system_path(config.path)} ... "
|
230
|
+
|
231
|
+
changed = Commander::Command::Options.new(options)
|
232
|
+
changed.pblogin = username
|
233
|
+
changed.password = nil
|
234
|
+
changed.use_authorization_tokens = options.create_token != false && !changed.token.nil?
|
235
|
+
|
236
|
+
FileUtils.mkdir_p File.dirname(config.path)
|
237
|
+
config.save!(changed)
|
238
|
+
options.__replace__(changed)
|
239
|
+
|
240
|
+
success "done"
|
241
|
+
end
|
242
|
+
|
243
|
+
true
|
244
|
+
end
|
245
|
+
|
246
|
+
def config_ssh_key_stage
|
247
|
+
if config.should_run_ssh_wizard?
|
248
|
+
paragraph{ say "No SSH keys were found. We will generate a pair of keys for you." }
|
249
|
+
|
250
|
+
ssh_pub_key_file_path = generate_ssh_key_ruby
|
251
|
+
paragraph{ say " Created: #{ssh_pub_key_file_path}" }
|
252
|
+
end
|
253
|
+
true
|
254
|
+
end
|
255
|
+
|
256
|
+
def upload_ssh_key_stage
|
257
|
+
return true if ssh_key_uploaded?
|
258
|
+
|
259
|
+
upload = paragraph do
|
260
|
+
agree "Your public SSH key must be uploaded to the ProtonBox server to access code. Upload now? (yes|no) "
|
261
|
+
end
|
262
|
+
|
263
|
+
if upload
|
264
|
+
if ssh_keys.empty?
|
265
|
+
paragraph do
|
266
|
+
info "Since you do not have any keys associated with your ProtonBox account, "\
|
267
|
+
"your new key will be uploaded as the 'default' key."
|
268
|
+
upload_ssh_key('default')
|
269
|
+
end
|
270
|
+
else
|
271
|
+
paragraph { existing_keys_info }
|
272
|
+
|
273
|
+
key_fingerprint = fingerprint_for_default_key
|
274
|
+
unless key_fingerprint
|
275
|
+
paragraph do
|
276
|
+
warn "Your public SSH key at #{system_path(RHC::Config.ssh_pub_key_file_path)} is invalid or unreadable. "\
|
277
|
+
"Setup can not continue until you manually remove or fix your "\
|
278
|
+
"public and private keys id_rsa keys."
|
279
|
+
end
|
280
|
+
return false
|
281
|
+
end
|
282
|
+
|
283
|
+
paragraph do
|
284
|
+
say "You can enter a name for your key, or leave it blank to use the default name. " \
|
285
|
+
"Using the same name as an existing key will overwrite the old key."
|
286
|
+
end
|
287
|
+
ask_for_key_name
|
288
|
+
end
|
289
|
+
else
|
290
|
+
paragraph do
|
291
|
+
info "You can upload your public SSH key at a later time using the 'pbox sshkey' command"
|
292
|
+
end
|
293
|
+
end
|
294
|
+
|
295
|
+
true
|
296
|
+
end
|
297
|
+
|
298
|
+
def ask_for_key_name(default_name=get_preferred_key_name)
|
299
|
+
key_name = nil
|
300
|
+
paragraph do
|
301
|
+
begin
|
302
|
+
key_name = ask "Provide a name for this key: " do |q|
|
303
|
+
q.default = default_name
|
304
|
+
q.responses[:ask_on_error] = ''
|
305
|
+
end
|
306
|
+
end while !upload_ssh_key(key_name)
|
307
|
+
end
|
308
|
+
end
|
309
|
+
|
310
|
+
def get_preferred_key_name
|
311
|
+
userkey = username ? username.gsub(/@.*/, '') : ''
|
312
|
+
pubkey_base_name = "#{userkey}#{hostname.gsub(/\..*\z/,'')}".gsub(/[^A-Za-z0-9]/,'').slice(0,16)
|
313
|
+
find_unique_key_name(pubkey_base_name)
|
314
|
+
end
|
315
|
+
|
316
|
+
# given the base name and the maximum length,
|
317
|
+
# find a name that does not clash with what is in opts[:keys]
|
318
|
+
def find_unique_key_name(base='default')
|
319
|
+
max = DEFAULT_MAX_LENGTH
|
320
|
+
key_name_suffix = 1
|
321
|
+
candidate = base
|
322
|
+
while ssh_keys.detect { |k| k.name == candidate }
|
323
|
+
candidate = base.slice(0, max - key_name_suffix.to_s.length) + key_name_suffix.to_s
|
324
|
+
key_name_suffix += 1
|
325
|
+
end
|
326
|
+
candidate
|
327
|
+
end
|
328
|
+
|
329
|
+
def upload_ssh_key(key_name)
|
330
|
+
return false unless key_name.present?
|
331
|
+
|
332
|
+
type, content, comment = ssh_key_triple_for_default_key
|
333
|
+
|
334
|
+
if ssh_keys.present? && ssh_keys.any? { |k| k.name == key_name }
|
335
|
+
clear_ssh_keys_cache
|
336
|
+
paragraph do
|
337
|
+
say "Key with the name '#{key_name}' already exists. Updating ... "
|
338
|
+
key = rest_client.find_key(key_name)
|
339
|
+
key.update(type, content)
|
340
|
+
success "done"
|
341
|
+
end
|
342
|
+
else
|
343
|
+
clear_ssh_keys_cache
|
344
|
+
begin
|
345
|
+
rest_client.add_key(key_name, content, type)
|
346
|
+
paragraph{ say "Uploading key '#{key_name}' ... #{color('done', :green)}" }
|
347
|
+
rescue RHC::Rest::ValidationException => e
|
348
|
+
error e.message || "Unknown error during key upload."
|
349
|
+
return false
|
350
|
+
end
|
351
|
+
end
|
352
|
+
|
353
|
+
true
|
354
|
+
end
|
355
|
+
|
356
|
+
|
357
|
+
##
|
358
|
+
# Alert the user that they should manually install tools if they are not
|
359
|
+
# currently available
|
360
|
+
#
|
361
|
+
# Unix Tools:
|
362
|
+
# git
|
363
|
+
#
|
364
|
+
# Windows Tools:
|
365
|
+
# msysgit (Git for Windows)
|
366
|
+
# TortoiseGIT (Windows Explorer integration)
|
367
|
+
#
|
368
|
+
def install_client_tools_stage
|
369
|
+
if windows?
|
370
|
+
windows_install
|
371
|
+
else
|
372
|
+
generic_unix_install_check
|
373
|
+
end
|
374
|
+
true
|
375
|
+
end
|
376
|
+
|
377
|
+
def config_namespace_stage
|
378
|
+
paragraph do
|
379
|
+
say "Checking for a domain ... "
|
380
|
+
domains = rest_client.domains
|
381
|
+
if domains.length == 0
|
382
|
+
warn "none"
|
383
|
+
|
384
|
+
paragraph do
|
385
|
+
say [
|
386
|
+
"Applications are grouped into domains - each domain has a unique name (called a namespace) that becomes part of your public application URL.",
|
387
|
+
("You may create your first domain here or leave it blank and use 'pbox create-domain' later." if namespace_optional?),
|
388
|
+
"You will not be able to create an application without completing this step.",
|
389
|
+
].compact.join(' ')
|
390
|
+
end
|
391
|
+
|
392
|
+
ask_for_namespace
|
393
|
+
else
|
394
|
+
success domains.map(&:name).join(', ')
|
395
|
+
end
|
396
|
+
end
|
397
|
+
|
398
|
+
true
|
399
|
+
end
|
400
|
+
|
401
|
+
def show_app_info_stage
|
402
|
+
section do
|
403
|
+
say "Checking for applications ... "
|
404
|
+
|
405
|
+
if !applications.nil? and !applications.empty?
|
406
|
+
success "found #{applications.length}"
|
407
|
+
|
408
|
+
paragraph do
|
409
|
+
indent do
|
410
|
+
say table(applications.map do |app|
|
411
|
+
[app.name, app.app_url]
|
412
|
+
end)
|
413
|
+
end
|
414
|
+
end
|
415
|
+
else
|
416
|
+
info "none"
|
417
|
+
|
418
|
+
paragraph{ say "Run 'pbox create-app' to create your first application." }
|
419
|
+
paragraph do
|
420
|
+
say table(standalone_cartridges.sort {|a,b| a.display_name <=> b.display_name }.map do |cart|
|
421
|
+
[' ', cart.display_name, "pbox create-app <app name> #{cart.name}"]
|
422
|
+
end)
|
423
|
+
end
|
424
|
+
end
|
425
|
+
paragraph do
|
426
|
+
indent do
|
427
|
+
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
|
428
|
+
say "The following gear sizes are available to you: #{self.user.capabilities.gear_sizes.join(', ')}" if user.capabilities.gear_sizes.present?
|
429
|
+
end
|
430
|
+
end
|
431
|
+
end
|
432
|
+
true
|
433
|
+
end
|
434
|
+
|
435
|
+
# Perform basic tests to ensure that setup is sane
|
436
|
+
# search for private methods starting with "test_" and execute them
|
437
|
+
# in alphabetical order
|
438
|
+
# NOTE: The order itself is not important--the tests should be independent.
|
439
|
+
# However, the hash order is unpredictable in Ruby 1.8, and is preserved in
|
440
|
+
# 1.9. There are two similar tests that can fail under the same conditions,
|
441
|
+
# and this makes the spec results inconsistent between 1.8 and 1.9.
|
442
|
+
# Thus, we force an order with #sort to ensure spec passage on both.
|
443
|
+
def setup_test_stage
|
444
|
+
say "Checking common problems "
|
445
|
+
failed = false
|
446
|
+
all_test_methods.sort.each do |test|
|
447
|
+
begin
|
448
|
+
send(test)
|
449
|
+
print_dot unless failed
|
450
|
+
true
|
451
|
+
rescue => e
|
452
|
+
say "\n" unless failed
|
453
|
+
failed = true
|
454
|
+
paragraph{ error e.message }
|
455
|
+
false
|
456
|
+
end
|
457
|
+
end.tap do |pass|
|
458
|
+
success(' done') if !failed
|
459
|
+
end
|
460
|
+
|
461
|
+
true
|
462
|
+
end
|
463
|
+
|
464
|
+
def all_test_methods
|
465
|
+
(protected_methods + private_methods).select {|m| m.to_s.start_with? 'test_'}
|
466
|
+
end
|
467
|
+
|
468
|
+
###
|
469
|
+
# tests for specific user errors
|
470
|
+
|
471
|
+
def test_private_key_mode
|
472
|
+
pub_key_file = RHC::Config.ssh_pub_key_file_path
|
473
|
+
private_key_file = RHC::Config.ssh_priv_key_file_path
|
474
|
+
if File.exist?(private_key_file)
|
475
|
+
unless File.stat(private_key_file).mode.to_s(8) =~ /[4-7]00$/
|
476
|
+
raise "Your private SSH key file should be set as readable only to yourself. Please run 'chmod 600 #{system_path(private_key_file)}'"
|
477
|
+
end
|
478
|
+
end
|
479
|
+
true
|
480
|
+
end
|
481
|
+
|
482
|
+
# test connectivity an app
|
483
|
+
def test_ssh_connectivity
|
484
|
+
return true unless ssh_key_uploaded?
|
485
|
+
|
486
|
+
applications.take(1).each do |app|
|
487
|
+
begin
|
488
|
+
host, user = RHC::Helpers.ssh_string_parts(app.ssh_url)
|
489
|
+
ssh = Net::SSH.start(host, user, :timeout => 60)
|
490
|
+
rescue Interrupt => e
|
491
|
+
debug_error(e)
|
492
|
+
raise "Connection attempt to #{app.host} was interrupted"
|
493
|
+
rescue ::Exception => e
|
494
|
+
debug_error(e)
|
495
|
+
raise "An SSH connection could not be established to #{app.host}. Your SSH configuration may not be correct, or the application may not be responding. #{e.message} (#{e.class})"
|
496
|
+
ensure
|
497
|
+
ssh.close if ssh
|
498
|
+
end
|
499
|
+
end
|
500
|
+
true
|
501
|
+
end
|
502
|
+
|
503
|
+
###
|
504
|
+
def finalize_stage
|
505
|
+
paragraph do
|
506
|
+
say "The ProtonBox client tools have been configured on your computer. " \
|
507
|
+
"You can run this setup wizard at any time by using the command 'pbox setup' " \
|
508
|
+
"We will now execute your original " \
|
509
|
+
"command (pbox #{ARGV.join(" ")})"
|
510
|
+
end
|
511
|
+
true
|
512
|
+
end
|
513
|
+
|
514
|
+
def config_namespace(namespace)
|
515
|
+
# skip if string is empty
|
516
|
+
if namespace_optional? and (namespace.nil? or namespace.chomp.blank?)
|
517
|
+
paragraph{ info "You may create a domain later through 'pbox create-domain'" }
|
518
|
+
return true
|
519
|
+
end
|
520
|
+
|
521
|
+
begin
|
522
|
+
domain = rest_client.add_domain(namespace)
|
523
|
+
options.namespace = namespace
|
524
|
+
|
525
|
+
success "Your domain '#{domain.name}' has been successfully created"
|
526
|
+
rescue RHC::Rest::ValidationException => e
|
527
|
+
error e.message || "Unknown error during domain creation."
|
528
|
+
return false
|
529
|
+
end
|
530
|
+
true
|
531
|
+
end
|
532
|
+
|
533
|
+
def ask_for_namespace
|
534
|
+
# Ask for a namespace at least once, configure the namespace if a valid,
|
535
|
+
# non-blank string is provided.
|
536
|
+
namespace = nil
|
537
|
+
paragraph do
|
538
|
+
begin
|
539
|
+
namespace = ask "Please enter a namespace (letters and numbers only)#{namespace_optional? ? " |<none>|" : ""}: " do |q|
|
540
|
+
q.responses[:ask_on_error] = ''
|
541
|
+
end
|
542
|
+
end while !config_namespace(namespace)
|
543
|
+
end
|
544
|
+
end
|
545
|
+
|
546
|
+
def generic_unix_install_check
|
547
|
+
paragraph do
|
548
|
+
say "Checking for git ... "
|
549
|
+
|
550
|
+
if has_git?
|
551
|
+
success("found #{git_version}") rescue success('found')
|
552
|
+
else
|
553
|
+
warn "needs to be installed"
|
554
|
+
|
555
|
+
paragraph do
|
556
|
+
say "Automated installation of client tools is not supported for " \
|
557
|
+
"your platform. You will need to manually install git for full " \
|
558
|
+
"ProtonBox functionality."
|
559
|
+
end
|
560
|
+
end
|
561
|
+
end
|
562
|
+
end
|
563
|
+
|
564
|
+
def windows_install
|
565
|
+
# Finding windows executables is hard since they can get installed
|
566
|
+
# in non standard directories. Punt on this for now and simply
|
567
|
+
# print out urls and some instructions
|
568
|
+
warn <<EOF
|
569
|
+
|
570
|
+
In order to fully interact with ProtonBox you will need to install and configure a git client if you have not already done so.
|
571
|
+
|
572
|
+
Documentation for installing other tools you will need for ProtonBox can be found at http://devcenter.protonbox.com/getting-started
|
573
|
+
|
574
|
+
We recommend these free applications:
|
575
|
+
|
576
|
+
* Git for Windows - a basic git command line and GUI client http://git-scm.com/download/win
|
577
|
+
|
578
|
+
EOF
|
579
|
+
end
|
580
|
+
end
|
581
|
+
|
582
|
+
class RerunWizard < Wizard
|
583
|
+
def finalize_stage
|
584
|
+
section :top => 1 do
|
585
|
+
success "Your client tools are now configured."
|
586
|
+
end
|
587
|
+
true
|
588
|
+
end
|
589
|
+
end
|
590
|
+
|
591
|
+
class EmbeddedWizard < Wizard
|
592
|
+
def stages
|
593
|
+
super - APP_STAGES - KEY_STAGES - [:setup_test_stage]
|
594
|
+
end
|
595
|
+
|
596
|
+
def finalize_stage
|
597
|
+
true
|
598
|
+
end
|
599
|
+
|
600
|
+
protected
|
601
|
+
def namespace_optional?
|
602
|
+
false
|
603
|
+
end
|
604
|
+
end
|
605
|
+
|
606
|
+
class DomainWizard < Wizard
|
607
|
+
def initialize(*args)
|
608
|
+
client = args.length == 3 ? args.pop : nil
|
609
|
+
super *args
|
610
|
+
self.rest_client = client || new_client_for_options
|
611
|
+
end
|
612
|
+
|
613
|
+
def stages
|
614
|
+
[:config_namespace_stage]
|
615
|
+
end
|
616
|
+
|
617
|
+
protected
|
618
|
+
def namespace_optional?
|
619
|
+
false
|
620
|
+
end
|
621
|
+
end
|
622
|
+
|
623
|
+
class SSHWizard < Wizard
|
624
|
+
def stages
|
625
|
+
KEY_STAGES
|
626
|
+
end
|
627
|
+
|
628
|
+
def initialize(rest_client, config, options)
|
629
|
+
self.rest_client = rest_client
|
630
|
+
super config, options
|
631
|
+
end
|
632
|
+
end
|
633
|
+
end
|
data/lib/rhc.rb
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
# require 'rhc/version' #FIXME gem should know version
|
2
|
+
|
3
|
+
# Only require external gem dependencies here
|
4
|
+
require 'logger'
|
5
|
+
require 'pp'
|
6
|
+
|
7
|
+
require 'pry' if ENV['PRY']
|
8
|
+
|
9
|
+
# Extend core methods
|
10
|
+
require 'rhc/core_ext'
|
11
|
+
|
12
|
+
module RHC
|
13
|
+
autoload :AutoComplete, 'rhc/autocomplete'
|
14
|
+
autoload :Auth, 'rhc/auth'
|
15
|
+
autoload :CartridgeHelpers, 'rhc/cartridge_helpers'
|
16
|
+
autoload :CommandRunner, 'rhc/command_runner'
|
17
|
+
autoload :Commands, 'rhc/commands'
|
18
|
+
autoload :Config, 'rhc/config'
|
19
|
+
autoload :GitHelpers, 'rhc/git_helpers'
|
20
|
+
autoload :Helpers, 'rhc/helpers'
|
21
|
+
autoload :HelpFormatter, 'rhc/help_formatter'
|
22
|
+
autoload :Rest, 'rhc/rest'
|
23
|
+
autoload :SSHHelpers, 'rhc/ssh_helpers'
|
24
|
+
autoload :TarGz, 'rhc/tar_gz'
|
25
|
+
autoload :VERSION, 'rhc/version'
|
26
|
+
end
|
27
|
+
|
28
|
+
require 'rhc/exceptions'
|
29
|
+
|
30
|
+
require 'commander'
|
31
|
+
require 'commander/delegates'
|
32
|
+
require 'highline/system_extensions'
|
33
|
+
|
34
|
+
require 'rhc/highline_extensions'
|