startapp 0.1.6
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 +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
|