startapp 0.1.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (156) hide show
  1. checksums.yaml +7 -0
  2. data/COPYRIGHT +1 -0
  3. data/LICENSE +11 -0
  4. data/README.md +95 -0
  5. data/Rakefile +6 -0
  6. data/autocomplete/rhc_bash +1672 -0
  7. data/bin/app +37 -0
  8. data/conf/express.conf +8 -0
  9. data/features/assets/deploy.tar.gz +0 -0
  10. data/features/core_feature.rb +191 -0
  11. data/features/deployments_feature.rb +129 -0
  12. data/features/domains_feature.rb +58 -0
  13. data/features/keys_feature.rb +37 -0
  14. data/features/members_feature.rb +166 -0
  15. data/lib/rhc/auth/basic.rb +64 -0
  16. data/lib/rhc/auth/token.rb +102 -0
  17. data/lib/rhc/auth/token_store.rb +53 -0
  18. data/lib/rhc/auth.rb +5 -0
  19. data/lib/rhc/autocomplete.rb +66 -0
  20. data/lib/rhc/autocomplete_templates/bash.erb +39 -0
  21. data/lib/rhc/cartridge_helpers.rb +118 -0
  22. data/lib/rhc/cli.rb +40 -0
  23. data/lib/rhc/command_runner.rb +185 -0
  24. data/lib/rhc/commands/account.rb +25 -0
  25. data/lib/rhc/commands/alias.rb +124 -0
  26. data/lib/rhc/commands/app.rb +726 -0
  27. data/lib/rhc/commands/apps.rb +20 -0
  28. data/lib/rhc/commands/authorization.rb +115 -0
  29. data/lib/rhc/commands/base.rb +174 -0
  30. data/lib/rhc/commands/cartridge.rb +329 -0
  31. data/lib/rhc/commands/clone.rb +66 -0
  32. data/lib/rhc/commands/configure.rb +20 -0
  33. data/lib/rhc/commands/create.rb +100 -0
  34. data/lib/rhc/commands/delete.rb +19 -0
  35. data/lib/rhc/commands/deploy.rb +32 -0
  36. data/lib/rhc/commands/deployment.rb +82 -0
  37. data/lib/rhc/commands/domain.rb +172 -0
  38. data/lib/rhc/commands/env.rb +142 -0
  39. data/lib/rhc/commands/force_stop.rb +17 -0
  40. data/lib/rhc/commands/git_clone.rb +34 -0
  41. data/lib/rhc/commands/logout.rb +51 -0
  42. data/lib/rhc/commands/logs.rb +21 -0
  43. data/lib/rhc/commands/member.rb +148 -0
  44. data/lib/rhc/commands/port_forward.rb +197 -0
  45. data/lib/rhc/commands/reload.rb +17 -0
  46. data/lib/rhc/commands/restart.rb +17 -0
  47. data/lib/rhc/commands/scp.rb +54 -0
  48. data/lib/rhc/commands/server.rb +40 -0
  49. data/lib/rhc/commands/setup.rb +60 -0
  50. data/lib/rhc/commands/show.rb +43 -0
  51. data/lib/rhc/commands/snapshot.rb +137 -0
  52. data/lib/rhc/commands/ssh.rb +51 -0
  53. data/lib/rhc/commands/sshkey.rb +97 -0
  54. data/lib/rhc/commands/start.rb +17 -0
  55. data/lib/rhc/commands/stop.rb +17 -0
  56. data/lib/rhc/commands/tail.rb +47 -0
  57. data/lib/rhc/commands/threaddump.rb +14 -0
  58. data/lib/rhc/commands/tidy.rb +17 -0
  59. data/lib/rhc/commands.rb +396 -0
  60. data/lib/rhc/config.rb +321 -0
  61. data/lib/rhc/context_helper.rb +121 -0
  62. data/lib/rhc/core_ext.rb +202 -0
  63. data/lib/rhc/coverage_helper.rb +33 -0
  64. data/lib/rhc/deployment_helpers.rb +111 -0
  65. data/lib/rhc/exceptions.rb +256 -0
  66. data/lib/rhc/git_helpers.rb +106 -0
  67. data/lib/rhc/help_formatter.rb +55 -0
  68. data/lib/rhc/helpers.rb +481 -0
  69. data/lib/rhc/highline_extensions.rb +479 -0
  70. data/lib/rhc/json.rb +51 -0
  71. data/lib/rhc/output_helpers.rb +260 -0
  72. data/lib/rhc/rest/activation.rb +11 -0
  73. data/lib/rhc/rest/alias.rb +42 -0
  74. data/lib/rhc/rest/api.rb +87 -0
  75. data/lib/rhc/rest/application.rb +348 -0
  76. data/lib/rhc/rest/attributes.rb +36 -0
  77. data/lib/rhc/rest/authorization.rb +8 -0
  78. data/lib/rhc/rest/base.rb +79 -0
  79. data/lib/rhc/rest/cartridge.rb +162 -0
  80. data/lib/rhc/rest/client.rb +650 -0
  81. data/lib/rhc/rest/deployment.rb +18 -0
  82. data/lib/rhc/rest/domain.rb +98 -0
  83. data/lib/rhc/rest/environment_variable.rb +15 -0
  84. data/lib/rhc/rest/gear_group.rb +16 -0
  85. data/lib/rhc/rest/httpclient.rb +145 -0
  86. data/lib/rhc/rest/key.rb +44 -0
  87. data/lib/rhc/rest/membership.rb +105 -0
  88. data/lib/rhc/rest/mock.rb +1042 -0
  89. data/lib/rhc/rest/user.rb +32 -0
  90. data/lib/rhc/rest.rb +148 -0
  91. data/lib/rhc/scp_helpers.rb +27 -0
  92. data/lib/rhc/ssh_helpers.rb +380 -0
  93. data/lib/rhc/tar_gz.rb +51 -0
  94. data/lib/rhc/usage_templates/command_help.erb +51 -0
  95. data/lib/rhc/usage_templates/command_syntax_help.erb +11 -0
  96. data/lib/rhc/usage_templates/help.erb +61 -0
  97. data/lib/rhc/usage_templates/missing_help.erb +1 -0
  98. data/lib/rhc/usage_templates/options_help.erb +12 -0
  99. data/lib/rhc/vendor/okjson.rb +600 -0
  100. data/lib/rhc/vendor/parseconfig.rb +178 -0
  101. data/lib/rhc/vendor/sshkey.rb +253 -0
  102. data/lib/rhc/vendor/zliby.rb +628 -0
  103. data/lib/rhc/version.rb +5 -0
  104. data/lib/rhc/wizard.rb +637 -0
  105. data/lib/rhc.rb +34 -0
  106. data/spec/coverage_helper.rb +82 -0
  107. data/spec/direct_execution_helper.rb +339 -0
  108. data/spec/keys/example.pem +23 -0
  109. data/spec/keys/example_private.pem +27 -0
  110. data/spec/keys/server.pem +19 -0
  111. data/spec/rest_spec_helper.rb +31 -0
  112. data/spec/rhc/assets/cert.crt +22 -0
  113. data/spec/rhc/assets/cert_key_rsa +27 -0
  114. data/spec/rhc/assets/empty.txt +0 -0
  115. data/spec/rhc/assets/env_vars.txt +7 -0
  116. data/spec/rhc/assets/env_vars_2.txt +1 -0
  117. data/spec/rhc/assets/foo.txt +1 -0
  118. data/spec/rhc/assets/targz_corrupted.tar.gz +1 -0
  119. data/spec/rhc/assets/targz_sample.tar.gz +0 -0
  120. data/spec/rhc/auth_spec.rb +442 -0
  121. data/spec/rhc/cli_spec.rb +186 -0
  122. data/spec/rhc/command_spec.rb +435 -0
  123. data/spec/rhc/commands/account_spec.rb +42 -0
  124. data/spec/rhc/commands/alias_spec.rb +333 -0
  125. data/spec/rhc/commands/app_spec.rb +777 -0
  126. data/spec/rhc/commands/apps_spec.rb +39 -0
  127. data/spec/rhc/commands/authorization_spec.rb +157 -0
  128. data/spec/rhc/commands/cartridge_spec.rb +665 -0
  129. data/spec/rhc/commands/clone_spec.rb +41 -0
  130. data/spec/rhc/commands/deployment_spec.rb +327 -0
  131. data/spec/rhc/commands/domain_spec.rb +401 -0
  132. data/spec/rhc/commands/env_spec.rb +493 -0
  133. data/spec/rhc/commands/git_clone_spec.rb +102 -0
  134. data/spec/rhc/commands/logout_spec.rb +86 -0
  135. data/spec/rhc/commands/member_spec.rb +247 -0
  136. data/spec/rhc/commands/port_forward_spec.rb +217 -0
  137. data/spec/rhc/commands/scp_spec.rb +77 -0
  138. data/spec/rhc/commands/server_spec.rb +69 -0
  139. data/spec/rhc/commands/setup_spec.rb +118 -0
  140. data/spec/rhc/commands/snapshot_spec.rb +179 -0
  141. data/spec/rhc/commands/ssh_spec.rb +163 -0
  142. data/spec/rhc/commands/sshkey_spec.rb +188 -0
  143. data/spec/rhc/commands/tail_spec.rb +81 -0
  144. data/spec/rhc/commands/threaddump_spec.rb +84 -0
  145. data/spec/rhc/config_spec.rb +407 -0
  146. data/spec/rhc/helpers_spec.rb +531 -0
  147. data/spec/rhc/highline_extensions_spec.rb +314 -0
  148. data/spec/rhc/json_spec.rb +30 -0
  149. data/spec/rhc/rest_application_spec.rb +258 -0
  150. data/spec/rhc/rest_client_spec.rb +752 -0
  151. data/spec/rhc/rest_spec.rb +740 -0
  152. data/spec/rhc/targz_spec.rb +55 -0
  153. data/spec/rhc/wizard_spec.rb +756 -0
  154. data/spec/spec_helper.rb +575 -0
  155. data/spec/wizard_spec_helper.rb +330 -0
  156. metadata +469 -0
@@ -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