startapp 0.1.6
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.
- checksums.yaml +7 -0
- data/COPYRIGHT +1 -0
- data/LICENSE +11 -0
- data/README.md +95 -0
- data/Rakefile +6 -0
- data/autocomplete/rhc_bash +1672 -0
- data/bin/app +37 -0
- data/conf/express.conf +8 -0
- data/features/assets/deploy.tar.gz +0 -0
- data/features/core_feature.rb +191 -0
- data/features/deployments_feature.rb +129 -0
- data/features/domains_feature.rb +58 -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 +185 -0
- data/lib/rhc/commands/account.rb +25 -0
- data/lib/rhc/commands/alias.rb +124 -0
- data/lib/rhc/commands/app.rb +726 -0
- data/lib/rhc/commands/apps.rb +20 -0
- data/lib/rhc/commands/authorization.rb +115 -0
- data/lib/rhc/commands/base.rb +174 -0
- data/lib/rhc/commands/cartridge.rb +329 -0
- data/lib/rhc/commands/clone.rb +66 -0
- data/lib/rhc/commands/configure.rb +20 -0
- data/lib/rhc/commands/create.rb +100 -0
- data/lib/rhc/commands/delete.rb +19 -0
- data/lib/rhc/commands/deploy.rb +32 -0
- data/lib/rhc/commands/deployment.rb +82 -0
- data/lib/rhc/commands/domain.rb +172 -0
- data/lib/rhc/commands/env.rb +142 -0
- data/lib/rhc/commands/force_stop.rb +17 -0
- data/lib/rhc/commands/git_clone.rb +34 -0
- data/lib/rhc/commands/logout.rb +51 -0
- data/lib/rhc/commands/logs.rb +21 -0
- data/lib/rhc/commands/member.rb +148 -0
- data/lib/rhc/commands/port_forward.rb +197 -0
- data/lib/rhc/commands/reload.rb +17 -0
- data/lib/rhc/commands/restart.rb +17 -0
- data/lib/rhc/commands/scp.rb +54 -0
- data/lib/rhc/commands/server.rb +40 -0
- data/lib/rhc/commands/setup.rb +60 -0
- data/lib/rhc/commands/show.rb +43 -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/start.rb +17 -0
- data/lib/rhc/commands/stop.rb +17 -0
- data/lib/rhc/commands/tail.rb +47 -0
- data/lib/rhc/commands/threaddump.rb +14 -0
- data/lib/rhc/commands/tidy.rb +17 -0
- data/lib/rhc/commands.rb +396 -0
- data/lib/rhc/config.rb +321 -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 +111 -0
- data/lib/rhc/exceptions.rb +256 -0
- data/lib/rhc/git_helpers.rb +106 -0
- data/lib/rhc/help_formatter.rb +55 -0
- data/lib/rhc/helpers.rb +481 -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 +348 -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 +162 -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 +1042 -0
- data/lib/rhc/rest/user.rb +32 -0
- data/lib/rhc/rest.rb +148 -0
- data/lib/rhc/scp_helpers.rb +27 -0
- data/lib/rhc/ssh_helpers.rb +380 -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 +61 -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 +637 -0
- data/lib/rhc.rb +34 -0
- data/spec/coverage_helper.rb +82 -0
- data/spec/direct_execution_helper.rb +339 -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 +186 -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 +777 -0
- data/spec/rhc/commands/apps_spec.rb +39 -0
- data/spec/rhc/commands/authorization_spec.rb +157 -0
- data/spec/rhc/commands/cartridge_spec.rb +665 -0
- data/spec/rhc/commands/clone_spec.rb +41 -0
- data/spec/rhc/commands/deployment_spec.rb +327 -0
- data/spec/rhc/commands/domain_spec.rb +401 -0
- data/spec/rhc/commands/env_spec.rb +493 -0
- data/spec/rhc/commands/git_clone_spec.rb +102 -0
- data/spec/rhc/commands/logout_spec.rb +86 -0
- data/spec/rhc/commands/member_spec.rb +247 -0
- data/spec/rhc/commands/port_forward_spec.rb +217 -0
- data/spec/rhc/commands/scp_spec.rb +77 -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 +531 -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 +258 -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 +469 -0
data/lib/rhc/helpers.rb
ADDED
|
@@ -0,0 +1,481 @@
|
|
|
1
|
+
require 'commander/user_interaction'
|
|
2
|
+
require 'rhc/version'
|
|
3
|
+
require 'rhc/config'
|
|
4
|
+
require 'rhc/output_helpers'
|
|
5
|
+
require 'rbconfig'
|
|
6
|
+
|
|
7
|
+
require 'resolv'
|
|
8
|
+
|
|
9
|
+
OptionParser.accept(URI) {|s,| URI.parse(s) if s}
|
|
10
|
+
|
|
11
|
+
module RHC
|
|
12
|
+
|
|
13
|
+
module Helpers
|
|
14
|
+
private
|
|
15
|
+
def self.global_option(*args, &block)
|
|
16
|
+
RHC::Commands.global_option *args, &block
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
module Helpers
|
|
21
|
+
|
|
22
|
+
# helpers always have Commander UI available
|
|
23
|
+
include Commander::UI
|
|
24
|
+
include Commander::UI::AskForClass
|
|
25
|
+
include RHC::OutputHelpers
|
|
26
|
+
|
|
27
|
+
extend self
|
|
28
|
+
|
|
29
|
+
def decode_json(s)
|
|
30
|
+
RHC::Vendor::OkJson.decode(s)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def system_path(path)
|
|
34
|
+
return path.gsub(File::SEPARATOR, File::ALT_SEPARATOR) if File.const_defined?('ALT_SEPARATOR') and File::ALT_SEPARATOR.present?
|
|
35
|
+
path
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
PREFIX = %W(TB GB MB KB B).freeze
|
|
39
|
+
|
|
40
|
+
def human_size( s )
|
|
41
|
+
return "unknown" unless s
|
|
42
|
+
s = s.to_f
|
|
43
|
+
i = PREFIX.length - 1
|
|
44
|
+
while s > 500 && i > 0
|
|
45
|
+
i -= 1
|
|
46
|
+
s /= 1000
|
|
47
|
+
end
|
|
48
|
+
((s > 9 || s.modulo(1) < 0.1 ? '%d' : '%.1f') % s) + ' ' + PREFIX[i]
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def date(s)
|
|
52
|
+
return nil unless s.present?
|
|
53
|
+
now = Date.today
|
|
54
|
+
d = datetime_rfc3339(s).to_time
|
|
55
|
+
if now.year == d.year
|
|
56
|
+
return d.strftime('%l:%M %p').strip if now.yday == d.yday
|
|
57
|
+
d.strftime('%b %d %l:%M %p')
|
|
58
|
+
else
|
|
59
|
+
d.strftime('%b %d, %Y %l:%M %p')
|
|
60
|
+
end
|
|
61
|
+
rescue ArgumentError
|
|
62
|
+
"Unknown date"
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def distance_of_time_in_words(from_time, to_time = 0)
|
|
66
|
+
from_time = from_time.to_time if from_time.respond_to?(:to_time)
|
|
67
|
+
to_time = to_time.to_time if to_time.respond_to?(:to_time)
|
|
68
|
+
distance_in_minutes = (((to_time - from_time).abs)/60).round
|
|
69
|
+
distance_in_seconds = ((to_time - from_time).abs).round
|
|
70
|
+
|
|
71
|
+
case distance_in_minutes
|
|
72
|
+
when 0..1
|
|
73
|
+
return distance_in_minutes == 0 ?
|
|
74
|
+
"less than 1 minute" :
|
|
75
|
+
"#{distance_in_minutes} minute"
|
|
76
|
+
|
|
77
|
+
when 2..44 then "#{distance_in_minutes} minutes"
|
|
78
|
+
when 45..89 then "about 1 hour"
|
|
79
|
+
when 90..1439 then "about #{(distance_in_minutes.to_f / 60.0).round} hours"
|
|
80
|
+
when 1440..2519 then "about 1 day"
|
|
81
|
+
when 2520..43199 then "#{(distance_in_minutes.to_f / 1440.0).round} days"
|
|
82
|
+
when 43200..86399 then "about 1 month"
|
|
83
|
+
else
|
|
84
|
+
"about #{(distance_in_minutes.to_f / 43200.0).round} months"
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def datetime_rfc3339(s)
|
|
89
|
+
DateTime.strptime(s, '%Y-%m-%dT%H:%M:%S%z')
|
|
90
|
+
# Replace with d = DateTime.rfc3339(s)
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
#
|
|
94
|
+
# Web related requests
|
|
95
|
+
#
|
|
96
|
+
|
|
97
|
+
def user_agent
|
|
98
|
+
"app/#{RHC::VERSION::STRING} (ruby #{RUBY_VERSION}; #{RUBY_PLATFORM})#{" (API #{RHC::Rest::Client::CLIENT_API_VERSIONS})"}"
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
#
|
|
102
|
+
# Global config
|
|
103
|
+
#
|
|
104
|
+
|
|
105
|
+
global_option '-l', '--rhlogin LOGIN', "StartApp login"
|
|
106
|
+
global_option '-p', '--password PASSWORD', "StartApp password"
|
|
107
|
+
global_option '--token TOKEN', "An authorization token for accessing your account."
|
|
108
|
+
|
|
109
|
+
global_option '-d', '--debug', "Turn on debugging", :hide => true
|
|
110
|
+
|
|
111
|
+
global_option '--server NAME', String, 'An StartApp server hostname (default: broker.startapp.bg)'
|
|
112
|
+
global_option '-k', '--insecure', "Allow insecure SSL connections. Potential security risk.", :hide => true
|
|
113
|
+
|
|
114
|
+
global_option '--limit INTEGER', Integer, "Maximum number of simultaneous operations to execute.", :hide => true
|
|
115
|
+
global_option '--raw', "Do not format the output from the requested operations.", :hide => true
|
|
116
|
+
global_option '--always-prefix', "Include the gear prefix on all output from the server.", :hide => true
|
|
117
|
+
|
|
118
|
+
OptionParser.accept(SSLVersion = Class.new){ |s| OpenSSL::SSL::SSLContext::METHODS.find{ |m| m.to_s.downcase == s.downcase } or raise OptionParser::InvalidOption.new(nil, "The provided SSL version '#{s}' is not valid. Supported values: #{OpenSSL::SSL::SSLContext::METHODS.map(&:to_s).map(&:downcase).join(', ')}") }
|
|
119
|
+
global_option '--ssl-version VERSION', SSLVersion, "The version of SSL to use", :hide => true do |value|
|
|
120
|
+
raise RHC::Exception, "You are using an older version of the httpclient gem which prevents the use of --ssl-version. Please run 'gem update httpclient' to install a newer version (2.2.6 or newer)." unless HTTPClient::SSLConfig.method_defined? :ssl_version
|
|
121
|
+
end
|
|
122
|
+
global_option '--ssl-ca-file FILE', "An SSL certificate CA file (may contain multiple certs)", :hide => true do |value|
|
|
123
|
+
debug certificate_file(value)
|
|
124
|
+
end
|
|
125
|
+
global_option '--ssl-client-cert-file FILE', "An SSL x509 client certificate file", :hide => true do |value|
|
|
126
|
+
debug certificate_file(value)
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
global_option('--timeout SECONDS', Integer, 'The timeout for operations') do |value|
|
|
130
|
+
raise RHC::Exception, "Timeout must be a positive integer" unless value > 0
|
|
131
|
+
end
|
|
132
|
+
global_option '--noprompt', "Suppress all interactive operations command", :hide => true do
|
|
133
|
+
$terminal.page_at = nil
|
|
134
|
+
end
|
|
135
|
+
global_option '--config FILE', "Path of a different config file (default: #{system_path("~/.startapp/express.conf")})", :hide => true
|
|
136
|
+
global_option '--clean', "Ignore any saved configuration options", :hide => true
|
|
137
|
+
global_option '--mock', "Run in mock mode", :hide => true do
|
|
138
|
+
#:nocov:
|
|
139
|
+
require 'rhc/rest/mock'
|
|
140
|
+
RHC::Rest::Mock.start
|
|
141
|
+
#:nocov:
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
ROLES = {'view' => 'viewer', 'edit' => 'editor', 'admin' => 'administrator'}
|
|
145
|
+
OptionParser.accept(Role = Class.new) do |s|
|
|
146
|
+
s.downcase!
|
|
147
|
+
(ROLES.has_key?(s) && s) or
|
|
148
|
+
raise OptionParser::InvalidOption.new(nil, "The provided role '#{s}' is not valid. Supported values: #{ROLES.keys.join(', ')}")
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
def role_name(s)
|
|
152
|
+
ROLES[s.downcase]
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
def openshift_server
|
|
156
|
+
to_host((options.server rescue nil) || ENV['LIBRA_SERVER'] || "broker.startapp.bg")
|
|
157
|
+
end
|
|
158
|
+
def openshift_online_server?
|
|
159
|
+
openshift_server =~ /broker.startapp.bg$/i
|
|
160
|
+
end
|
|
161
|
+
def openshift_url
|
|
162
|
+
"https://#{openshift_server}"
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
def to_host(s)
|
|
166
|
+
s =~ %r(^http(?:s)?://) ? URI(s).host : s
|
|
167
|
+
end
|
|
168
|
+
def to_uri(s)
|
|
169
|
+
begin
|
|
170
|
+
URI(s =~ %r(^http(?:s)?://) ? s : "https://#{s}")
|
|
171
|
+
rescue URI::InvalidURIError
|
|
172
|
+
raise RHC::InvalidURIException.new(s)
|
|
173
|
+
end
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
def ssh_string(ssh_url)
|
|
177
|
+
return nil if ssh_url.blank?
|
|
178
|
+
uri = URI.parse(ssh_url)
|
|
179
|
+
"#{uri.user}@#{uri.host}"
|
|
180
|
+
rescue => e
|
|
181
|
+
RHC::Helpers.debug_error(e)
|
|
182
|
+
ssh_url
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
def ssh_string_parts(ssh_url)
|
|
186
|
+
uri = URI.parse(ssh_url)
|
|
187
|
+
[uri.host, uri.user]
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
def openshift_rest_endpoint
|
|
191
|
+
uri = to_uri((options.server rescue nil) || ENV['LIBRA_SERVER'] || "broker.startapp.bg")
|
|
192
|
+
uri.path = '/broker/rest/api' if uri.path.blank? || uri.path == '/'
|
|
193
|
+
uri
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
def token_for_user
|
|
197
|
+
options.token or (token_store.get(options.rhlogin, options.server) if options.rhlogin && options.use_authorization_tokens)
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
def client_from_options(opts)
|
|
201
|
+
RHC::Rest::Client.new({
|
|
202
|
+
:url => openshift_rest_endpoint.to_s,
|
|
203
|
+
:debug => options.debug,
|
|
204
|
+
:timeout => options.timeout,
|
|
205
|
+
:warn => BOUND_WARNING,
|
|
206
|
+
}.merge!(ssl_options).merge!(opts))
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
def ssl_options
|
|
210
|
+
{
|
|
211
|
+
:ssl_version => options.ssl_version,
|
|
212
|
+
:client_cert => certificate_file(options.ssl_client_cert),
|
|
213
|
+
:ca_file => options.ssl_ca_file && File.expand_path(options.ssl_ca_file),
|
|
214
|
+
:verify_mode => options.insecure ? OpenSSL::SSL::VERIFY_NONE : nil,
|
|
215
|
+
}.delete_if{ |k,v| v.nil? }
|
|
216
|
+
end
|
|
217
|
+
|
|
218
|
+
def certificate_file(file)
|
|
219
|
+
file && OpenSSL::X509::Certificate.new(IO.read(File.expand_path(file)))
|
|
220
|
+
rescue => e
|
|
221
|
+
debug e
|
|
222
|
+
raise OptionParser::InvalidOption.new(nil, "The certificate '#{file}' cannot be loaded: #{e.message} (#{e.class})")
|
|
223
|
+
end
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
#
|
|
227
|
+
# Output helpers
|
|
228
|
+
#
|
|
229
|
+
|
|
230
|
+
def interactive?
|
|
231
|
+
$stdin.tty? and $stdout.tty? and not options.noprompt
|
|
232
|
+
end
|
|
233
|
+
|
|
234
|
+
def debug(*args)
|
|
235
|
+
$terminal.debug(*args)
|
|
236
|
+
end
|
|
237
|
+
def debug_error(*args)
|
|
238
|
+
$terminal.debug_error(*args)
|
|
239
|
+
end
|
|
240
|
+
def debug?
|
|
241
|
+
$terminal.debug?
|
|
242
|
+
end
|
|
243
|
+
|
|
244
|
+
def exec(cmd)
|
|
245
|
+
output = Kernel.send(:`, cmd)
|
|
246
|
+
[$?.exitstatus, output]
|
|
247
|
+
end
|
|
248
|
+
|
|
249
|
+
def disable_deprecated?
|
|
250
|
+
ENV['DISABLE_DEPRECATED'] == '1'
|
|
251
|
+
end
|
|
252
|
+
|
|
253
|
+
def deprecated_command(correct, short=false)
|
|
254
|
+
deprecated("This command is deprecated. Please use '#{correct}' instead.", short)
|
|
255
|
+
end
|
|
256
|
+
|
|
257
|
+
def deprecated(msg,short = false)
|
|
258
|
+
raise DeprecatedError.new(msg % ['an error','a warning',0]) if disable_deprecated?
|
|
259
|
+
warn "Warning: #{msg}\n" % ['a warning','an error',1]
|
|
260
|
+
end
|
|
261
|
+
|
|
262
|
+
#
|
|
263
|
+
# By default, agree should take a single character in interactive
|
|
264
|
+
#
|
|
265
|
+
def agree(*args, &block)
|
|
266
|
+
#args.push(interactive?.presence) if args.length == 1
|
|
267
|
+
block = lambda do |q|
|
|
268
|
+
q.validate = /\A(?:y|yes|n|no)\Z/i
|
|
269
|
+
end unless block_given?
|
|
270
|
+
super *args, &block
|
|
271
|
+
end
|
|
272
|
+
|
|
273
|
+
def confirm_action(question)
|
|
274
|
+
return if options.confirm
|
|
275
|
+
return if !options.noprompt && paragraph{ agree "#{question} (yes|no): " }
|
|
276
|
+
raise RHC::ConfirmationError
|
|
277
|
+
end
|
|
278
|
+
|
|
279
|
+
def success(msg, *args)
|
|
280
|
+
say color(msg, :green), *args
|
|
281
|
+
end
|
|
282
|
+
|
|
283
|
+
def info(msg, *args)
|
|
284
|
+
say color(msg, :cyan), *args
|
|
285
|
+
end
|
|
286
|
+
|
|
287
|
+
def warn(msg, *args)
|
|
288
|
+
say color(msg, :yellow), *args
|
|
289
|
+
end
|
|
290
|
+
|
|
291
|
+
def error(msg, *args)
|
|
292
|
+
say color(msg, :red), *args
|
|
293
|
+
end
|
|
294
|
+
|
|
295
|
+
# OVERRIDE: Replaces default commander behavior
|
|
296
|
+
def color(item, *args)
|
|
297
|
+
if item.is_a? Array
|
|
298
|
+
item.map{ |i| $terminal.color(i, *args) }
|
|
299
|
+
else
|
|
300
|
+
$terminal.color(item, *args)
|
|
301
|
+
end
|
|
302
|
+
end
|
|
303
|
+
|
|
304
|
+
[:pager, :indent, :paragraph, :section, :header, :table, :table_args].each do |sym|
|
|
305
|
+
define_method(sym) do |*args, &block|
|
|
306
|
+
$terminal.send(sym, *args, &block)
|
|
307
|
+
end
|
|
308
|
+
end
|
|
309
|
+
|
|
310
|
+
def pluralize(count, s)
|
|
311
|
+
count == 1 ? "#{count} #{s}" : "#{count} #{s}s"
|
|
312
|
+
end
|
|
313
|
+
|
|
314
|
+
# This will format table headings for a consistent look and feel
|
|
315
|
+
# If a heading isn't explicitly defined, it will attempt to look up the parts
|
|
316
|
+
# If those aren't found, it will capitalize the string
|
|
317
|
+
def table_heading(value)
|
|
318
|
+
# Set the default proc to look up undefined values
|
|
319
|
+
headings = Hash.new do |hash,key|
|
|
320
|
+
items = key.to_s.split('_')
|
|
321
|
+
# Look up each piece individually
|
|
322
|
+
hash[key] = items.length > 1 ?
|
|
323
|
+
# Recusively look up the heading for the parts
|
|
324
|
+
items.map{|x| headings[x.to_sym]}.join(' ') :
|
|
325
|
+
# Capitalize if this part isn't defined
|
|
326
|
+
items.first.capitalize
|
|
327
|
+
end
|
|
328
|
+
|
|
329
|
+
# Predefined headings (or parts of headings)
|
|
330
|
+
headings.merge!({
|
|
331
|
+
:creation_time => "Created",
|
|
332
|
+
:expires_in_seconds => "Expires In",
|
|
333
|
+
:uuid => "ID",
|
|
334
|
+
:id => 'ID',
|
|
335
|
+
:current_scale => "Current",
|
|
336
|
+
:scales_from => "Minimum",
|
|
337
|
+
:scales_to => "Maximum",
|
|
338
|
+
:gear_sizes => "Allowed Gear Sizes",
|
|
339
|
+
:consumed_gears => "Gears Used",
|
|
340
|
+
:max_gears => "Gears Allowed",
|
|
341
|
+
:max_domains => "Domains Allowed",
|
|
342
|
+
:compact_members => "Members",
|
|
343
|
+
:gear_info => "Gears",
|
|
344
|
+
:plan_id => "Plan",
|
|
345
|
+
:url => "URL",
|
|
346
|
+
:ssh_string => "SSH",
|
|
347
|
+
:connection_info => "Connection URL",
|
|
348
|
+
:gear_profile => "Gear Size",
|
|
349
|
+
:visible_to_ssh? => 'Available',
|
|
350
|
+
:downloaded_cartridge_url => 'From',
|
|
351
|
+
:auto_deploy => 'Deployment',
|
|
352
|
+
:sha1 => 'SHA1',
|
|
353
|
+
:ref => 'Git Reference'
|
|
354
|
+
})
|
|
355
|
+
|
|
356
|
+
headings[value]
|
|
357
|
+
end
|
|
358
|
+
|
|
359
|
+
class StringTee < StringIO
|
|
360
|
+
attr_reader :tee
|
|
361
|
+
def initialize(other)
|
|
362
|
+
@tee = other
|
|
363
|
+
super()
|
|
364
|
+
end
|
|
365
|
+
def <<(buf)
|
|
366
|
+
tee << buf
|
|
367
|
+
super
|
|
368
|
+
end
|
|
369
|
+
end
|
|
370
|
+
|
|
371
|
+
##
|
|
372
|
+
# results
|
|
373
|
+
#
|
|
374
|
+
# highline helper which creates a paragraph with a header
|
|
375
|
+
# to distinguish the final results of a command from other output
|
|
376
|
+
#
|
|
377
|
+
def results(&block)
|
|
378
|
+
section(:top => 1, :bottom => 0) do
|
|
379
|
+
say "RESULT:"
|
|
380
|
+
yield
|
|
381
|
+
end
|
|
382
|
+
end
|
|
383
|
+
|
|
384
|
+
# Platform helpers
|
|
385
|
+
def jruby? ; RUBY_PLATFORM =~ /java/i end
|
|
386
|
+
def windows? ; RUBY_PLATFORM =~ /win(32|dows|ce)|djgpp|(ms|cyg|bcc)win|mingw32/i end
|
|
387
|
+
def unix? ; !jruby? && !windows? end
|
|
388
|
+
def mac? ; RbConfig::CONFIG['host_os'] =~ /^darwin/ end
|
|
389
|
+
|
|
390
|
+
#
|
|
391
|
+
# Check if host exists
|
|
392
|
+
#
|
|
393
|
+
def host_exists?(host)
|
|
394
|
+
# :nocov:
|
|
395
|
+
# Patch for BZ840938 to support Ruby 1.8 on machines without /etc/resolv.conf
|
|
396
|
+
dns = Resolv::DNS.new((Resolv::DNS::Config.default_config_hash || {}))
|
|
397
|
+
resources = dns.getresources(host, Resolv::DNS::Resource::IN::A)
|
|
398
|
+
debug("Checking for #{host} from Resolv::DNS: #{resources.inspect}") if debug?
|
|
399
|
+
resources.present?
|
|
400
|
+
# :nocov:
|
|
401
|
+
end
|
|
402
|
+
|
|
403
|
+
def hosts_file_contains?(host)
|
|
404
|
+
with_tolerant_encoding do
|
|
405
|
+
begin
|
|
406
|
+
resolver = Resolv::Hosts.new
|
|
407
|
+
result = resolver.getaddress host
|
|
408
|
+
debug("Checking for #{host} from Resolv::Hosts: #{result.inspect}") if debug?
|
|
409
|
+
result
|
|
410
|
+
rescue => e
|
|
411
|
+
debug "Error while resolving with Resolv::Hosts: #{e.message}(#{e.class})\n #{e.backtrace.join("\n ")}"
|
|
412
|
+
end
|
|
413
|
+
end
|
|
414
|
+
end
|
|
415
|
+
|
|
416
|
+
def with_tolerant_encoding(&block)
|
|
417
|
+
# :nocov:
|
|
418
|
+
if RUBY_VERSION.to_f >= 1.9
|
|
419
|
+
orig_default_internal = Encoding.default_internal
|
|
420
|
+
Encoding.default_internal = 'ISO-8859-1'
|
|
421
|
+
else
|
|
422
|
+
orig_default_kcode = $KCODE
|
|
423
|
+
$KCODE = 'N'
|
|
424
|
+
end
|
|
425
|
+
yield
|
|
426
|
+
ensure
|
|
427
|
+
if RUBY_VERSION.to_f >= 1.9
|
|
428
|
+
Encoding.default_internal = orig_default_internal
|
|
429
|
+
else
|
|
430
|
+
$KCODE = orig_default_kcode
|
|
431
|
+
end
|
|
432
|
+
# :nocov:
|
|
433
|
+
end
|
|
434
|
+
|
|
435
|
+
# Run a command and export its output to the user. Output is not capturable
|
|
436
|
+
# on all platforms.
|
|
437
|
+
def run_with_tee(cmd)
|
|
438
|
+
status, stdout, stderr = nil
|
|
439
|
+
|
|
440
|
+
if windows?
|
|
441
|
+
#:nocov: TODO: Test block
|
|
442
|
+
system(cmd)
|
|
443
|
+
status = $?.exitstatus
|
|
444
|
+
#:nocov:
|
|
445
|
+
else
|
|
446
|
+
stdout, stderr = [$stdout, $stderr].map{ |t| StringTee.new(t) }
|
|
447
|
+
status = Open4.spawn(cmd, 'stdout' => stdout, 'stderr' => stderr, 'quiet' => true)
|
|
448
|
+
stdout, stderr = [stdout, stderr].map(&:string)
|
|
449
|
+
end
|
|
450
|
+
|
|
451
|
+
[status, stdout, stderr]
|
|
452
|
+
end
|
|
453
|
+
|
|
454
|
+
def env_var_regex_pattern
|
|
455
|
+
/^([a-zA-Z_][\w]*)=(.*)$/
|
|
456
|
+
end
|
|
457
|
+
|
|
458
|
+
def collect_env_vars(items)
|
|
459
|
+
return nil if items.blank?
|
|
460
|
+
|
|
461
|
+
env_vars = []
|
|
462
|
+
|
|
463
|
+
Array(items).each do |item|
|
|
464
|
+
if match = item.match(env_var_regex_pattern)
|
|
465
|
+
name, value = match.captures
|
|
466
|
+
env_vars << RHC::Rest::EnvironmentVariable.new({ :name => name, :value => value })
|
|
467
|
+
elsif File.file? item
|
|
468
|
+
File.readlines(item).each do |line|
|
|
469
|
+
if match = line.match(env_var_regex_pattern)
|
|
470
|
+
name, value = match.captures
|
|
471
|
+
env_vars << RHC::Rest::EnvironmentVariable.new({ :name => name, :value => value })
|
|
472
|
+
end
|
|
473
|
+
end
|
|
474
|
+
end
|
|
475
|
+
end
|
|
476
|
+
env_vars
|
|
477
|
+
end
|
|
478
|
+
|
|
479
|
+
BOUND_WARNING = self.method(:warn).to_proc
|
|
480
|
+
end
|
|
481
|
+
end
|