cloulu 0.0.0 → 0.1.1

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.
Files changed (152) hide show
  1. data/bin/{cloulu → cl} +0 -0
  2. data/lib/cc_api_stub/applications.rb +53 -0
  3. data/lib/cc_api_stub/domains.rb +16 -0
  4. data/lib/cc_api_stub/frameworks.rb +22 -0
  5. data/lib/cc_api_stub/helper.rb +131 -0
  6. data/lib/cc_api_stub/login.rb +21 -0
  7. data/lib/cc_api_stub/organization_users.rb +21 -0
  8. data/lib/cc_api_stub/organizations.rb +70 -0
  9. data/lib/cc_api_stub/routes.rb +26 -0
  10. data/lib/cc_api_stub/runtimes.rb +22 -0
  11. data/lib/cc_api_stub/service_bindings.rb +22 -0
  12. data/lib/cc_api_stub/service_instances.rb +22 -0
  13. data/lib/cc_api_stub/services.rb +25 -0
  14. data/lib/cc_api_stub/spaces.rb +49 -0
  15. data/lib/cc_api_stub/users.rb +84 -0
  16. data/lib/cc_api_stub.rb +17 -0
  17. data/lib/cfoundry/auth_token.rb +63 -0
  18. data/lib/cfoundry/baseclient.rb +201 -0
  19. data/lib/cfoundry/chatty_hash.rb +46 -0
  20. data/lib/cfoundry/client.rb +46 -0
  21. data/lib/cfoundry/concerns/login_helpers.rb +13 -0
  22. data/lib/cfoundry/errors.rb +160 -0
  23. data/lib/cfoundry/rest_client.rb +299 -0
  24. data/lib/cfoundry/test_support.rb +3 -0
  25. data/lib/cfoundry/trace_helpers.rb +40 -0
  26. data/lib/cfoundry/uaaclient.rb +112 -0
  27. data/lib/cfoundry/upload_helpers.rb +187 -0
  28. data/lib/cfoundry/v1/app.rb +363 -0
  29. data/lib/cfoundry/v1/base.rb +72 -0
  30. data/lib/cfoundry/v1/client.rb +193 -0
  31. data/lib/cfoundry/v1/framework.rb +21 -0
  32. data/lib/cfoundry/v1/model.rb +178 -0
  33. data/lib/cfoundry/v1/model_magic.rb +129 -0
  34. data/lib/cfoundry/v1/runtime.rb +24 -0
  35. data/lib/cfoundry/v1/service.rb +39 -0
  36. data/lib/cfoundry/v1/service_instance.rb +32 -0
  37. data/lib/cfoundry/v1/service_plan.rb +19 -0
  38. data/lib/cfoundry/v1/user.rb +22 -0
  39. data/lib/cfoundry/v2/app.rb +392 -0
  40. data/lib/cfoundry/v2/base.rb +83 -0
  41. data/lib/cfoundry/v2/client.rb +138 -0
  42. data/lib/cfoundry/v2/domain.rb +11 -0
  43. data/lib/cfoundry/v2/framework.rb +14 -0
  44. data/lib/cfoundry/v2/model.rb +148 -0
  45. data/lib/cfoundry/v2/model_magic.rb +449 -0
  46. data/lib/cfoundry/v2/organization.rb +16 -0
  47. data/lib/cfoundry/v2/route.rb +15 -0
  48. data/lib/cfoundry/v2/runtime.rb +12 -0
  49. data/lib/cfoundry/v2/service.rb +19 -0
  50. data/lib/cfoundry/v2/service_auth_token.rb +9 -0
  51. data/lib/cfoundry/v2/service_binding.rb +10 -0
  52. data/lib/cfoundry/v2/service_instance.rb +14 -0
  53. data/lib/cfoundry/v2/service_plan.rb +12 -0
  54. data/lib/cfoundry/v2/space.rb +18 -0
  55. data/lib/cfoundry/v2/user.rb +64 -0
  56. data/lib/cfoundry/validator.rb +39 -0
  57. data/lib/cfoundry/version.rb +4 -0
  58. data/lib/cfoundry/zip.rb +56 -0
  59. data/lib/cfoundry.rb +2 -0
  60. data/lib/manifests-vmc-plugin/errors.rb +21 -0
  61. data/lib/manifests-vmc-plugin/loader/builder.rb +34 -0
  62. data/lib/manifests-vmc-plugin/loader/normalizer.rb +149 -0
  63. data/lib/manifests-vmc-plugin/loader/resolver.rb +79 -0
  64. data/lib/manifests-vmc-plugin/loader.rb +31 -0
  65. data/lib/manifests-vmc-plugin/plugin.rb +145 -0
  66. data/lib/manifests-vmc-plugin/version.rb +3 -0
  67. data/lib/manifests-vmc-plugin.rb +313 -0
  68. data/lib/mothership/base.rb +99 -0
  69. data/lib/mothership/callbacks.rb +85 -0
  70. data/lib/mothership/command.rb +146 -0
  71. data/lib/mothership/errors.rb +38 -0
  72. data/lib/mothership/help/commands.rb +53 -0
  73. data/lib/mothership/help/printer.rb +170 -0
  74. data/lib/mothership/help.rb +64 -0
  75. data/lib/mothership/inputs.rb +189 -0
  76. data/lib/mothership/parser.rb +182 -0
  77. data/lib/mothership/version.rb +3 -0
  78. data/lib/mothership.rb +64 -0
  79. data/lib/tunnel-vmc-plugin/plugin.rb +178 -0
  80. data/lib/tunnel-vmc-plugin/tunnel.rb +308 -0
  81. data/lib/tunnel-vmc-plugin/version.rb +3 -0
  82. data/lib/uaa/http.rb +168 -0
  83. data/lib/uaa/misc.rb +121 -0
  84. data/lib/uaa/scim.rb +292 -0
  85. data/lib/uaa/token_coder.rb +196 -0
  86. data/lib/uaa/token_issuer.rb +255 -0
  87. data/lib/uaa/util.rb +235 -0
  88. data/lib/uaa/version.rb +19 -0
  89. data/lib/uaa.rb +18 -0
  90. data/lib/vmc/cli/app/app.rb +45 -0
  91. data/lib/vmc/cli/app/apps.rb +99 -0
  92. data/lib/vmc/cli/app/base.rb +90 -0
  93. data/lib/vmc/cli/app/crashes.rb +42 -0
  94. data/lib/vmc/cli/app/delete.rb +95 -0
  95. data/lib/vmc/cli/app/deprecated.rb +11 -0
  96. data/lib/vmc/cli/app/env.rb +78 -0
  97. data/lib/vmc/cli/app/files.rb +137 -0
  98. data/lib/vmc/cli/app/health.rb +26 -0
  99. data/lib/vmc/cli/app/instances.rb +53 -0
  100. data/lib/vmc/cli/app/logs.rb +76 -0
  101. data/lib/vmc/cli/app/push/create.rb +165 -0
  102. data/lib/vmc/cli/app/push/interactions.rb +94 -0
  103. data/lib/vmc/cli/app/push/sync.rb +64 -0
  104. data/lib/vmc/cli/app/push.rb +109 -0
  105. data/lib/vmc/cli/app/rename.rb +35 -0
  106. data/lib/vmc/cli/app/restart.rb +20 -0
  107. data/lib/vmc/cli/app/scale.rb +71 -0
  108. data/lib/vmc/cli/app/start.rb +143 -0
  109. data/lib/vmc/cli/app/stats.rb +67 -0
  110. data/lib/vmc/cli/app/stop.rb +27 -0
  111. data/lib/vmc/cli/help.rb +11 -0
  112. data/lib/vmc/cli/interactive.rb +105 -0
  113. data/lib/vmc/cli/route/base.rb +12 -0
  114. data/lib/vmc/cli/route/map.rb +82 -0
  115. data/lib/vmc/cli/route/routes.rb +25 -0
  116. data/lib/vmc/cli/route/unmap.rb +94 -0
  117. data/lib/vmc/cli/service/base.rb +8 -0
  118. data/lib/vmc/cli/service/bind.rb +44 -0
  119. data/lib/vmc/cli/service/create.rb +126 -0
  120. data/lib/vmc/cli/service/delete.rb +86 -0
  121. data/lib/vmc/cli/service/rename.rb +35 -0
  122. data/lib/vmc/cli/service/service.rb +42 -0
  123. data/lib/vmc/cli/service/services.rb +114 -0
  124. data/lib/vmc/cli/service/unbind.rb +38 -0
  125. data/lib/vmc/cli/start/base.rb +94 -0
  126. data/lib/vmc/cli/start/colors.rb +13 -0
  127. data/lib/vmc/cli/start/info.rb +126 -0
  128. data/lib/vmc/cli/start/login.rb +97 -0
  129. data/lib/vmc/cli/start/logout.rb +17 -0
  130. data/lib/vmc/cli/start/target.rb +60 -0
  131. data/lib/vmc/cli/start/target_interactions.rb +37 -0
  132. data/lib/vmc/cli/start/targets.rb +16 -0
  133. data/lib/vmc/cli/user/base.rb +29 -0
  134. data/lib/vmc/cli/user/create.rb +39 -0
  135. data/lib/vmc/cli/user/delete.rb +27 -0
  136. data/lib/vmc/cli/user/passwd.rb +50 -0
  137. data/lib/vmc/cli/user/register.rb +42 -0
  138. data/lib/vmc/cli/user/users.rb +32 -0
  139. data/lib/vmc/cli/v2_check_cli.rb +16 -0
  140. data/lib/vmc/cli.rb +474 -0
  141. data/lib/vmc/constants.rb +13 -0
  142. data/lib/vmc/detect.rb +129 -0
  143. data/lib/vmc/errors.rb +19 -0
  144. data/lib/vmc/plugin.rb +56 -0
  145. data/lib/vmc/spacing.rb +89 -0
  146. data/lib/vmc/spec_helper.rb +1 -0
  147. data/lib/vmc/test_support.rb +6 -0
  148. data/lib/vmc/version.rb +3 -0
  149. data/lib/vmc.rb +8 -0
  150. data/vendor/errors/v1.yml +189 -0
  151. data/vendor/errors/v2.yml +360 -0
  152. metadata +303 -190
data/lib/vmc/cli.rb ADDED
@@ -0,0 +1,474 @@
1
+ require "yaml"
2
+ require "socket"
3
+ require "net/http"
4
+ require "multi_json"
5
+ require "fileutils"
6
+
7
+ require "mothership"
8
+
9
+ require "cfoundry"
10
+
11
+ require "vmc/constants"
12
+ require "vmc/errors"
13
+ require "vmc/spacing"
14
+
15
+ require "vmc/cli/help"
16
+ require "vmc/cli/interactive"
17
+
18
+
19
+ $vmc_asked_auth = false
20
+
21
+ module VMC
22
+ class CLI < Mothership
23
+ include VMC::Interactive
24
+ include VMC::Spacing
25
+
26
+ option :help, :desc => "Show command usage", :alias => "-h",
27
+ :default => false
28
+
29
+ option :version, :desc => "Print version number", :alias => "-v",
30
+ :default => false
31
+
32
+ option :verbose, :desc => "Print extra information", :alias => "-V",
33
+ :default => false
34
+
35
+ option :force, :desc => "Skip interaction when possible", :alias => "-f",
36
+ :type => :boolean, :default => proc { input[:script] }
37
+
38
+ option :debug, :desc => "Print full stack trace (instead of crash log)",
39
+ :type => :boolean, :default => false
40
+
41
+ option :quiet, :desc => "Simplify output format", :alias => "-q",
42
+ :type => :boolean, :default => proc { input[:script] }
43
+
44
+ option :script, :desc => "Shortcut for --quiet and --force",
45
+ :type => :boolean, :default => proc { !$stdout.tty? }
46
+
47
+ option :trace, :desc => "Show API traffic", :alias => "-t",
48
+ :default => false
49
+
50
+ option :color, :desc => "Use colorful output",
51
+ :type => :boolean, :default => proc { !input[:quiet] }
52
+
53
+ def default_action
54
+ if input[:version]
55
+ line "cloulu #{VERSION}"
56
+ else
57
+ super
58
+ end
59
+ end
60
+
61
+ def check_target
62
+ unless client && client.target
63
+ fail "Please select a target with 'cl target'."
64
+ end
65
+ end
66
+
67
+ def check_logged_in
68
+ unless client.logged_in?
69
+ if force?
70
+ fail "Please log in with 'cl login'."
71
+ else
72
+ line c("Please log in first to proceed.", :warning)
73
+ line
74
+ invoke :login
75
+ invalidate_client
76
+ end
77
+ end
78
+ end
79
+
80
+ def precondition
81
+ check_target
82
+ check_logged_in
83
+
84
+ return unless v2?
85
+
86
+ unless client.current_organization
87
+ fail "Please select an organization with 'cl target --ask-org'."
88
+ end
89
+
90
+ unless client.current_space
91
+ fail "Please select a space with 'cl target --ask-space'."
92
+ end
93
+ end
94
+
95
+ def wrap_errors
96
+ yield
97
+ rescue CFoundry::Timeout => e
98
+ err(e.message)
99
+ rescue Interrupt
100
+ exit_status 130
101
+ rescue Mothership::Error
102
+ raise
103
+ rescue UserError => e
104
+ log_error(e)
105
+ err e.message
106
+ rescue SystemExit
107
+ raise
108
+ rescue UserFriendlyError => e
109
+ err e.message
110
+ rescue CFoundry::Forbidden, CFoundry::InvalidAuthToken => e
111
+ if !$vmc_asked_auth
112
+ $vmc_asked_auth = true
113
+
114
+ line
115
+ line c("Not authenticated! Try logging in:", :warning)
116
+
117
+ # TODO: there's no color here; global flags not being passed
118
+ # through (mothership bug?)
119
+ invoke :login
120
+
121
+ retry
122
+ end
123
+
124
+ log_error(e)
125
+
126
+ err "Denied: #{e.description}"
127
+
128
+ rescue Exception => e
129
+ log_error(e)
130
+
131
+ msg = e.class.name
132
+ msg << ": #{e}" unless e.to_s.empty?
133
+ msg << "\nFor more information, see #{VMC::CRASH_FILE}"
134
+ err msg
135
+
136
+ raise if debug?
137
+ end
138
+
139
+ def execute(cmd, argv, global = {})
140
+ if input[:help]
141
+ invoke :help, :command => cmd.name.to_s
142
+ else
143
+ wrap_errors do
144
+ @command = cmd
145
+ precondition
146
+
147
+ save_token_if_it_changes do
148
+ super
149
+ end
150
+ end
151
+ end
152
+ end
153
+
154
+ def save_token_if_it_changes
155
+ return yield unless client && client.token
156
+
157
+ before_token = client.token
158
+
159
+ yield
160
+
161
+ after_token = client.token
162
+
163
+ return unless after_token
164
+
165
+ if before_token != after_token
166
+ info = target_info
167
+ info[:token] = after_token.auth_header
168
+ save_target_info(info)
169
+ end
170
+ end
171
+
172
+ def log_error(e)
173
+ ensure_config_dir
174
+
175
+ msg = e.class.name
176
+ msg << ": #{e}" unless e.to_s.empty?
177
+
178
+ crash_file = File.expand_path(VMC::CRASH_FILE)
179
+
180
+ FileUtils.mkdir_p(File.dirname(crash_file))
181
+
182
+ File.open(crash_file, "w") do |f|
183
+ f.puts "Time of crash:"
184
+ f.puts " #{Time.now}"
185
+ f.puts ""
186
+ f.puts msg
187
+ f.puts ""
188
+
189
+ if e.respond_to?(:request_trace)
190
+ f.puts "<<<"
191
+ f.puts e.request_trace
192
+ end
193
+
194
+ if e.respond_to?(:response_trace)
195
+ f.puts e.response_trace
196
+ f.puts ">>>"
197
+ f.puts ""
198
+ end
199
+
200
+ vmc_dir = File.expand_path("../../../..", __FILE__) + "/"
201
+ e.backtrace.each do |loc|
202
+ if loc =~ /\/gems\//
203
+ f.puts loc.sub(/.*\/gems\//, "")
204
+ else
205
+ f.puts loc.sub(vmc_dir, "")
206
+ end
207
+ end
208
+ end
209
+ end
210
+
211
+ def quiet?
212
+ input[:quiet]
213
+ end
214
+
215
+ def force?
216
+ input[:force]
217
+ end
218
+
219
+ def debug?
220
+ !!input[:debug]
221
+ end
222
+
223
+ def color_enabled?
224
+ input[:color]
225
+ end
226
+
227
+ def verbose?
228
+ input[:verbose]
229
+ end
230
+
231
+ def user_colors
232
+ return @user_colors if @user_colors
233
+
234
+ colors = File.expand_path(COLORS_FILE)
235
+
236
+ @user_colors = super.dup
237
+
238
+ # most terminal schemes are stupid, so use cyan instead
239
+ @user_colors.each do |k, v|
240
+ if v == :blue
241
+ @user_colors[k] = :cyan
242
+ end
243
+ end
244
+
245
+ if File.exists?(colors)
246
+ YAML.load_file(colors).each do |k, v|
247
+ @user_colors[k.to_sym] = v.to_sym
248
+ end
249
+ end
250
+
251
+ @user_colors
252
+ end
253
+
254
+ def err(msg, status = 1)
255
+ $stderr.puts c(msg, :error)
256
+ exit_status status
257
+ end
258
+
259
+ def fail(msg)
260
+ raise UserError, msg
261
+ end
262
+
263
+ def table(headers, rows)
264
+ tabular(
265
+ !quiet? && headers.collect { |h| h && b(h) },
266
+ *rows)
267
+ end
268
+
269
+ def name_list(xs)
270
+ if xs.empty?
271
+ d("none")
272
+ else
273
+ xs.collect { |x| c(x.name, :name) }.join(", ")
274
+ end
275
+ end
276
+
277
+ def sane_target_url(url)
278
+ unless url =~ /^https?:\/\//
279
+ begin
280
+ TCPSocket.new(url, Net::HTTP.https_default_port)
281
+ url = "https://#{url}"
282
+ rescue Errno::ECONNREFUSED, SocketError, Timeout::Error
283
+ url = "http://#{url}"
284
+ end
285
+ end
286
+
287
+ url.gsub(/\/$/, "")
288
+ end
289
+
290
+ def target_file
291
+ one_of(VMC::TARGET_FILE, VMC::OLD_TARGET_FILE)
292
+ end
293
+
294
+ def tokens_file
295
+ one_of(VMC::TOKENS_FILE, VMC::OLD_TOKENS_FILE)
296
+ end
297
+
298
+ def one_of(*paths)
299
+ paths.each do |p|
300
+ exp = File.expand_path(p)
301
+ return exp if File.exist? exp
302
+ end
303
+
304
+ File.expand_path(paths.first)
305
+ end
306
+
307
+ def client_target
308
+ if File.exists?(target_file)
309
+ File.read(target_file).chomp
310
+ end
311
+ end
312
+
313
+ def ensure_config_dir
314
+ config = File.expand_path(VMC::CONFIG_DIR)
315
+ FileUtils.mkdir_p(config) unless File.exist? config
316
+ end
317
+
318
+ def set_target(url)
319
+ ensure_config_dir
320
+
321
+ File.open(File.expand_path(VMC::TARGET_FILE), "w") do |f|
322
+ f.write(sane_target_url(url))
323
+ end
324
+
325
+ invalidate_client
326
+ end
327
+
328
+ def targets_info
329
+ new_toks = File.expand_path(VMC::TOKENS_FILE)
330
+ old_toks = File.expand_path(VMC::OLD_TOKENS_FILE)
331
+
332
+ info =
333
+ if File.exist? new_toks
334
+ YAML.load_file(new_toks)
335
+ elsif File.exist? old_toks
336
+ MultiJson.load(File.read(old_toks))
337
+ end
338
+
339
+ info ||= {}
340
+
341
+ normalize_targets_info(info)
342
+ end
343
+
344
+ def normalize_targets_info(info_by_url)
345
+ info_by_url.reduce({}) do |hash, pair|
346
+ key, value = pair
347
+ hash[key] = value.is_a?(String) ? { :token => value } : value
348
+ hash
349
+ end
350
+ end
351
+
352
+ def target_info(target = client_target)
353
+ targets_info[target] || {}
354
+ end
355
+
356
+ def save_targets(ts)
357
+ ensure_config_dir
358
+
359
+ File.open(File.expand_path(VMC::TOKENS_FILE), "w") do |io|
360
+ YAML.dump(ts, io)
361
+ end
362
+ end
363
+
364
+ def save_target_info(info, target = client_target)
365
+ ts = targets_info
366
+ ts[target] = info
367
+ save_targets(ts)
368
+ end
369
+
370
+ def remove_target_info(target = client_target)
371
+ ts = targets_info
372
+ ts.delete target
373
+ save_targets(ts)
374
+ end
375
+
376
+ def no_v2
377
+ fail "Not implemented for v2." if v2?
378
+ end
379
+
380
+ def v2?
381
+ client.version == 2
382
+ end
383
+
384
+ def invalidate_client
385
+ @@client = nil
386
+ client
387
+ end
388
+
389
+ def client(target = client_target)
390
+ return @@client if defined?(@@client) && @@client
391
+ return unless target
392
+
393
+ info = target_info(target)
394
+ token = info[:token] && CFoundry::AuthToken.from_hash(info)
395
+
396
+ @@client =
397
+ case info[:version]
398
+ when 2
399
+ fail "User switching not implemented for v2." if input[:proxy]
400
+ CFoundry::V2::Client.new(target, token)
401
+ when 1
402
+ CFoundry::V1::Client.new(target, token)
403
+ else
404
+ CFoundry::Client.new(target, token)
405
+ end
406
+
407
+ @@client.proxy = input[:proxy]
408
+ #@@client.http_proxy = input[:http_proxy] || ENV['HTTP_PROXY'] || ENV['http_proxy'] || nil
409
+ @@client.trace = input[:trace]
410
+
411
+ uri = URI.parse(target)
412
+ @@client.log = File.expand_path("#{LOGS_DIR}/#{uri.host}.log")
413
+
414
+ unless info.key? :version
415
+ info[:version] = @@client.version
416
+ save_target_info(info, target)
417
+ end
418
+
419
+ if org = info[:organization]
420
+ @@client.current_organization = @@client.organization(org)
421
+ end
422
+
423
+ if space = info[:space]
424
+ @@client.current_space = @@client.space(space)
425
+ end
426
+
427
+ @@client
428
+ rescue CFoundry::InvalidTarget
429
+ end
430
+
431
+ def fail_unknown(display, name)
432
+ fail("Unknown #{display} '#{name}'.")
433
+ end
434
+
435
+ class << self
436
+ def client
437
+ @@client
438
+ end
439
+
440
+ def client=(c)
441
+ @@client = c
442
+ end
443
+
444
+ private
445
+
446
+ def find_by_name(display, &blk)
447
+ proc { |name, *args|
448
+ choices, _ = args
449
+ choices ||= instance_exec(&blk) if block_given?
450
+
451
+ choices.find { |c| c.name == name } ||
452
+ fail_unknown(display, name)
453
+ }
454
+ end
455
+
456
+ def by_name(what, display = what)
457
+ proc { |name, *_|
458
+ client.send(:"#{what}_by_name", name) ||
459
+ fail_unknown(display, name)
460
+ }
461
+ end
462
+
463
+ def find_by_name_insensitive(display, &blk)
464
+ proc { |name, *args|
465
+ choices, _ = args
466
+ choices ||= instance_exec(&blk) if block_given?
467
+
468
+ choices.find { |c| c.name.upcase == name.upcase } ||
469
+ fail_unknown(display, name)
470
+ }
471
+ end
472
+ end
473
+ end
474
+ end
@@ -0,0 +1,13 @@
1
+ module VMC
2
+ OLD_TARGET_FILE = "~/.cloulu_target".freeze
3
+ OLD_TOKENS_FILE = "~/.cloulu_token".freeze
4
+
5
+ CONFIG_DIR = "~/.cloulu".freeze
6
+
7
+ LOGS_DIR = "#{CONFIG_DIR}/logs".freeze
8
+ PLUGINS_FILE = "#{CONFIG_DIR}/plugins.yml".freeze
9
+ TARGET_FILE = "#{CONFIG_DIR}/target".freeze
10
+ TOKENS_FILE = "#{CONFIG_DIR}/tokens.yml".freeze
11
+ COLORS_FILE = "#{CONFIG_DIR}/colors.yml".freeze
12
+ CRASH_FILE = "#{CONFIG_DIR}/crash".freeze
13
+ end
data/lib/vmc/detect.rb ADDED
@@ -0,0 +1,129 @@
1
+ require "set"
2
+
3
+ require "clouseau"
4
+
5
+ module VMC
6
+ class Detector
7
+ # "Basic" framework names for a detected language
8
+ PSEUDO_FRAMEWORKS = {
9
+ :node => "node",
10
+ :python => "wsgi",
11
+ :java => "java_web",
12
+ :php => "php",
13
+ :erlang => "otp_rebar",
14
+ :dotnet => "dotNet"
15
+ }
16
+
17
+ # Mismatched framework names
18
+ FRAMEWORK_NAMES = {
19
+ :rails => "rails3"
20
+ }
21
+
22
+ # Clouseau language symbol => matching runtime names
23
+ LANGUAGE_RUNTIMES = {
24
+ :ruby => /^ruby.*/,
25
+ :java => /^java.*/,
26
+ :node => /^node.*/,
27
+ :erlang => /^erlang.*/,
28
+ :dotnet => /^dotNet.*/,
29
+ :python => /^python.*/,
30
+ :php => /^php.*/
31
+ }
32
+
33
+ def initialize(client, path)
34
+ @client = client
35
+ @path = path
36
+ end
37
+
38
+ # detect the framework
39
+ def detect_framework
40
+ detected && frameworks[detected]
41
+ end
42
+
43
+ # detect the language and return the appropriate runtimes
44
+ def detect_runtimes
45
+ if detected && lang = detected.language_name
46
+ runtimes_for(lang)
47
+ else
48
+ []
49
+ end
50
+ end
51
+
52
+ # determine runtimes for a given framework based on the language its
53
+ # detector reports itself as
54
+ def runtimes(framework)
55
+ if detector = detectors[framework]
56
+ runtimes_for(detector.language_name)
57
+ else
58
+ []
59
+ end
60
+ end
61
+
62
+ # determine suitable memory allocation via the framework's detector
63
+ def suggested_memory(framework)
64
+ if detector = detectors[framework]
65
+ detector.memory_suggestion
66
+ end
67
+ end
68
+
69
+ # helper so that this is cached somewhere
70
+ def all_runtimes
71
+ @all_runtimes ||= @client.runtimes
72
+ end
73
+
74
+ # helper so that this is cached somewhere
75
+ def all_frameworks
76
+ @all_frameworks ||= @client.frameworks
77
+ end
78
+
79
+ def detected
80
+ @detected ||= Clouseau.detect(@path)
81
+ end
82
+
83
+ private
84
+
85
+ def map_detectors!
86
+ @framework_detectors = {}
87
+ @detector_frameworks = {}
88
+
89
+ Clouseau.detectors.each do |d|
90
+ name = d.framework_name
91
+ lang = d.language_name
92
+
93
+ framework = all_frameworks.find { |f|
94
+ f.name == name.to_s ||
95
+ f.name == FRAMEWORK_NAMES[name]
96
+ }
97
+
98
+ framework ||= all_frameworks.find { |f|
99
+ f.name == PSEUDO_FRAMEWORKS[lang]
100
+ }
101
+
102
+ next unless framework
103
+
104
+ @framework_detectors[framework] = d
105
+ @detector_frameworks[d] = framework
106
+ end
107
+
108
+ nil
109
+ end
110
+
111
+ # Framework -> Detector
112
+ def detectors
113
+ map_detectors! unless @framework_detectors
114
+ @framework_detectors
115
+ end
116
+
117
+ # Detector -> Framework
118
+ def frameworks
119
+ map_detectors! unless @detector_frameworks
120
+ @detector_frameworks
121
+ end
122
+
123
+ def runtimes_for(language)
124
+ all_runtimes.select do |r|
125
+ LANGUAGE_RUNTIMES[language] === r.name
126
+ end
127
+ end
128
+ end
129
+ end
data/lib/vmc/errors.rb ADDED
@@ -0,0 +1,19 @@
1
+ module VMC
2
+ class UserFriendlyError < RuntimeError
3
+ def initialize(msg)
4
+ @message = msg
5
+ end
6
+
7
+ def to_s
8
+ @message
9
+ end
10
+ end
11
+
12
+ class UserError < UserFriendlyError; end
13
+
14
+ class NotAuthorized < UserError
15
+ def initialize
16
+ @message = "Not authorized."
17
+ end
18
+ end
19
+ end
data/lib/vmc/plugin.rb ADDED
@@ -0,0 +1,56 @@
1
+ require "set"
2
+ require "yaml"
3
+
4
+ require "vmc/constants"
5
+ require "vmc/cli"
6
+
7
+ module VMC
8
+ module Plugin
9
+ @@plugins = []
10
+
11
+ def self.load_all
12
+ # auto-load gems with 'vmc-plugin' in their name
13
+ matching =
14
+ if Gem::Specification.respond_to? :find_all
15
+ Gem::Specification.find_all do |s|
16
+ s.name =~ /vmc-plugin/
17
+ end
18
+ else
19
+ Gem.source_index.find_name(/vmc-plugin/)
20
+ end
21
+
22
+ enabled = Set.new(matching.collect(&:name))
23
+
24
+ vmc_gems = Gem.loaded_specs["vmc"]
25
+ ((vmc_gems && vmc_gems.dependencies) || Gem.loaded_specs.values).each do |dep|
26
+ if dep.name =~ /vmc-plugin/
27
+ require "#{dep.name}/plugin"
28
+ enabled.delete dep.name
29
+ end
30
+ end
31
+
32
+ # allow explicit enabling/disabling of gems via config
33
+ plugins = File.expand_path(VMC::PLUGINS_FILE)
34
+ if File.exists?(plugins) && yaml = YAML.load_file(plugins)
35
+ enabled += yaml["enabled"] if yaml["enabled"]
36
+ enabled -= yaml["disabled"] if yaml["disabled"]
37
+ end
38
+
39
+ # load up each gem's 'plugin' file
40
+ #
41
+ # we require this file specifically so people can require the gem
42
+ # without it plugging into VMC
43
+ enabled.each do |gemname|
44
+ begin
45
+ require "#{gemname}/plugin"
46
+ rescue Gem::LoadError => e
47
+ puts "Failed to load #{gemname}:"
48
+ puts " #{e}"
49
+ puts
50
+ puts "You may need to update or remove this plugin."
51
+ puts
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end