vmc 0.4.0.beta.93 → 0.4.0.beta.94

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 (65) hide show
  1. data/vmc-ng/Rakefile +21 -30
  2. data/vmc-ng/lib/vmc.rb +4 -3
  3. data/vmc-ng/lib/vmc/cli.rb +10 -9
  4. data/vmc-ng/lib/vmc/cli/app/app.rb +45 -0
  5. data/vmc-ng/lib/vmc/cli/app/apps.rb +97 -0
  6. data/vmc-ng/lib/vmc/cli/app/base.rb +82 -0
  7. data/vmc-ng/lib/vmc/cli/app/crashes.rb +41 -0
  8. data/vmc-ng/lib/vmc/cli/app/delete.rb +90 -0
  9. data/vmc-ng/lib/vmc/cli/app/deprecated.rb +11 -0
  10. data/vmc-ng/lib/vmc/cli/app/env.rb +86 -0
  11. data/vmc-ng/lib/vmc/cli/app/files.rb +85 -0
  12. data/vmc-ng/lib/vmc/cli/app/health.rb +27 -0
  13. data/vmc-ng/lib/vmc/cli/app/instances.rb +49 -0
  14. data/vmc-ng/lib/vmc/cli/app/logs.rb +80 -0
  15. data/vmc-ng/lib/vmc/cli/app/push.rb +336 -0
  16. data/vmc-ng/lib/vmc/cli/app/rename.rb +31 -0
  17. data/vmc-ng/lib/vmc/cli/app/restart.rb +23 -0
  18. data/vmc-ng/lib/vmc/cli/app/routes.rb +97 -0
  19. data/vmc-ng/lib/vmc/cli/app/scale.rb +67 -0
  20. data/vmc-ng/lib/vmc/cli/app/start.rb +96 -0
  21. data/vmc-ng/lib/vmc/cli/app/stats.rb +68 -0
  22. data/vmc-ng/lib/vmc/cli/app/stop.rb +29 -0
  23. data/vmc-ng/lib/vmc/cli/domain/add_domain.rb +27 -0
  24. data/vmc-ng/lib/vmc/cli/domain/base.rb +12 -0
  25. data/vmc-ng/lib/vmc/cli/domain/create_domain.rb +31 -0
  26. data/vmc-ng/lib/vmc/cli/domain/delete_domain.rb +51 -0
  27. data/vmc-ng/lib/vmc/cli/domain/domains.rb +43 -0
  28. data/vmc-ng/lib/vmc/cli/domain/remove_domain.rb +26 -0
  29. data/vmc-ng/lib/vmc/cli/help.rb +0 -1
  30. data/vmc-ng/lib/vmc/cli/interactive.rb +4 -0
  31. data/vmc-ng/lib/vmc/cli/route/base.rb +12 -0
  32. data/vmc-ng/lib/vmc/cli/route/create_route.rb +42 -0
  33. data/vmc-ng/lib/vmc/cli/route/delete_route.rb +42 -0
  34. data/vmc-ng/lib/vmc/cli/route/routes.rb +26 -0
  35. data/vmc-ng/lib/vmc/detect.rb +2 -2
  36. data/vmc-ng/lib/vmc/spec_helper.rb +1 -0
  37. data/vmc-ng/lib/vmc/version.rb +1 -1
  38. data/vmc-ng/spec/cli/app/push_spec.rb +34 -0
  39. data/vmc-ng/spec/cli/app/rename_spec.rb +108 -0
  40. data/vmc-ng/spec/cli/route/delete_route_spec.rb +160 -0
  41. data/vmc-ng/spec/detect_spec.rb +54 -0
  42. data/vmc-ng/spec/factories/app_factory.rb +9 -0
  43. data/vmc-ng/spec/factories/client_factory.rb +16 -0
  44. data/vmc-ng/spec/factories/domain_factory.rb +9 -0
  45. data/vmc-ng/spec/factories/factory.rb +3 -0
  46. data/vmc-ng/spec/factories/framework_factory.rb +9 -0
  47. data/vmc-ng/spec/factories/route_factory.rb +10 -0
  48. data/vmc-ng/spec/spec_helper.rb +17 -0
  49. data/vmc-ng/spec/support/interact_helpers.rb +23 -0
  50. metadata +135 -62
  51. data/vmc-ng/lib/vmc/cli/app.rb +0 -1333
  52. data/vmc-ng/lib/vmc/cli/domain.rb +0 -164
  53. data/vmc-ng/lib/vmc/cli/route.rb +0 -106
  54. data/vmc-ng/lib/vmc/spec_helpers.rb +0 -431
  55. data/vmc-ng/lib/vmc/spec_helpers/eventlog.rb +0 -277
  56. data/vmc-ng/lib/vmc/spec_helpers/patches.rb +0 -94
  57. data/vmc-ng/spec/Rakefile +0 -13
  58. data/vmc-ng/spec/app/app_spec.rb +0 -19
  59. data/vmc-ng/spec/app/apps_spec.rb +0 -79
  60. data/vmc-ng/spec/app/push_spec.rb +0 -74
  61. data/vmc-ng/spec/assets/hello-sinatra/Gemfile +0 -2
  62. data/vmc-ng/spec/assets/hello-sinatra/main.rb +0 -5
  63. data/vmc-ng/spec/assets/hello-sinatra/manifest.yml +0 -9
  64. data/vmc-ng/spec/helpers.rb +0 -7
  65. data/vmc-ng/spec/start/target_spec.rb +0 -60
@@ -1,1333 +0,0 @@
1
- require "set"
2
-
3
- require "vmc/cli"
4
- require "vmc/detect"
5
-
6
- module VMC
7
- class App < CLI
8
- desc "List your applications"
9
- group :apps
10
- input :space, :from_given => by_name("space"),
11
- :default => proc { client.current_space },
12
- :desc => "Show apps in given space"
13
- input :name, :desc => "Filter by name regexp"
14
- input :runtime, :desc => "Filter by runtime regexp"
15
- input :framework, :desc => "Filter by framework regexp"
16
- input :url, :desc => "Filter by url regexp"
17
- input :full, :type => :boolean, :default => false,
18
- :desc => "Verbose output format"
19
- def apps
20
- if space = input[:space]
21
- space.summarize! rescue CFoundry::APIError
22
-
23
- apps =
24
- with_progress("Getting applications in #{c(space.name, :name)}") do
25
- space.apps
26
- end
27
- else
28
- apps =
29
- with_progress("Getting applications") do
30
- client.apps(:depth => 2)
31
- end
32
- end
33
-
34
- line unless quiet?
35
-
36
- if apps.empty? and !quiet?
37
- line "No applications."
38
- return
39
- end
40
-
41
- apps.reject! do |a|
42
- !app_matches(a, input)
43
- end
44
-
45
- apps = apps.sort_by(&:name)
46
-
47
- if input[:full]
48
- spaced(apps) do |a|
49
- display_app(a)
50
- end
51
- elsif quiet?
52
- apps.each do |a|
53
- line a.name
54
- end
55
- else
56
- table(
57
- ["name", "status", "usage", v2? && "plan", "runtime", "url"],
58
- apps.collect { |a|
59
- [ c(a.name, :name),
60
- app_status(a),
61
- "#{a.total_instances} x #{human_mb(a.memory)}",
62
- v2? && (a.production ? "prod" : "dev"),
63
- a.runtime.name,
64
- if a.urls.empty?
65
- d("none")
66
- elsif a.urls.size == 1
67
- a.url
68
- else
69
- "#{a.url}, ..."
70
- end
71
- ]
72
- })
73
- end
74
- end
75
-
76
-
77
- desc "Show app information"
78
- group :apps
79
- input :app, :argument => :required, :from_given => by_name("app"),
80
- :desc => "App to show"
81
- def app
82
- app = input[:app]
83
-
84
- if quiet?
85
- line app.name
86
- else
87
- display_app(app)
88
- end
89
- end
90
-
91
- desc "Push an application, syncing changes if it exists"
92
- group :apps, :manage
93
- input(:name, :argument => true, :desc => "Application name") {
94
- ask("Name")
95
- }
96
- input :path, :default => ".",
97
- :desc => "Path containing the application"
98
- input(:url, :desc => "URL bound to app") { |name|
99
- choices = url_choices(name)
100
-
101
- options = {
102
- :choices => choices + ["none"],
103
- :allow_other => true
104
- }
105
-
106
- options[:default] = choices.first if choices.size == 1
107
-
108
- url = ask "URL", options
109
-
110
- unless url == "none"
111
- url
112
- end
113
- }
114
- input(:memory, :desc => "Memory limit") { |default|
115
- ask("Memory Limit",
116
- :choices => memory_choices,
117
- :allow_other => true,
118
- :default => default || "64M")
119
- }
120
- input(:instances, :type => :integer,
121
- :desc => "Number of instances to run") {
122
- ask("Instances", :default => 1)
123
- }
124
- input(:framework, :from_given => find_by_name("framework"),
125
- :desc => "Framework to use") { |all, choices, default, other|
126
- ask_with_other("Framework", all, choices, default, other)
127
- }
128
- input(:runtime, :from_given => find_by_name("runtime"),
129
- :desc => "Runtime to use") { |all, choices, default, other|
130
- ask_with_other("Runtime", all, choices, default, other)
131
- }
132
- input(:command, :desc => "Startup command for standalone app") {
133
- ask("Startup command")
134
- }
135
- input :plan, :default => "D100",
136
- :desc => "Application plan (e.g. D100, P200)"
137
- input :start, :type => :boolean, :default => true,
138
- :desc => "Start app after pushing?"
139
- input :restart, :type => :boolean, :default => true,
140
- :desc => "Restart app after updating?"
141
- input(:create_services, :type => :boolean,
142
- :desc => "Interactively create services?") {
143
- line unless quiet?
144
- ask "Create services for application?", :default => false
145
- }
146
- input(:bind_services, :type => :boolean,
147
- :desc => "Interactively bind services?") {
148
- ask "Bind other services to application?", :default => false
149
- }
150
- def push
151
- name = input[:name]
152
- path = File.expand_path(input[:path])
153
-
154
- if app = client.app_by_name(name)
155
- sync_app(app, path)
156
- else
157
- create_app(name, path)
158
- end
159
- end
160
-
161
-
162
- desc "Start an application"
163
- group :apps, :manage
164
- input :apps, :argument => :splat, :singular => :app,
165
- :desc => "Applications to start",
166
- :from_given => by_name("app")
167
- input :debug_mode, :aliases => "-d",
168
- :desc => "Debug mode to start in"
169
- input :all, :type => :boolean, :default => false,
170
- :desc => "Start all applications"
171
- def start
172
- apps = input[:all] ? client.apps : input[:apps]
173
- fail "No applications given." if apps.empty?
174
-
175
- spaced(apps) do |app|
176
- app = filter(:start_app, app)
177
-
178
- switch_mode(app, input[:debug_mode])
179
-
180
- with_progress("Starting #{c(app.name, :name)}") do |s|
181
- if app.started?
182
- s.skip do
183
- err "Already started."
184
- end
185
- end
186
-
187
- app.start!
188
- end
189
-
190
- check_application(app)
191
-
192
- if app.debug_mode && !quiet?
193
- line
194
- invoke :instances, :app => app
195
- end
196
- end
197
- end
198
-
199
-
200
- desc "Stop an application"
201
- group :apps, :manage
202
- input :apps, :argument => :splat, :singular => :app,
203
- :desc => "Applications to start",
204
- :from_given => by_name("app")
205
- input :all, :type => :boolean, :default => false,
206
- :desc => "Stop all applications"
207
- def stop
208
- apps = input[:all] ? client.apps : input[:apps]
209
- fail "No applications given." if apps.empty?
210
-
211
- apps.each do |app|
212
- with_progress("Stopping #{c(app.name, :name)}") do |s|
213
- if app.stopped?
214
- s.skip do
215
- err "Application is not running."
216
- end
217
- end
218
-
219
- app.stop!
220
- end
221
- end
222
- end
223
-
224
-
225
- desc "Stop and start an application"
226
- group :apps, :manage
227
- input :apps, :argument => :splat, :singular => :app,
228
- :desc => "Applications to start",
229
- :from_given => by_name("app")
230
- input :debug_mode, :aliases => "-d",
231
- :desc => "Debug mode to start in"
232
- input :all, :type => :boolean, :default => false,
233
- :desc => "Restart all applications"
234
- def restart
235
- invoke :stop, :all => input[:all], :apps => input[:apps]
236
-
237
- line unless quiet?
238
-
239
- invoke :start, :all => input[:all], :apps => input[:apps],
240
- :debug_mode => input[:debug_mode]
241
- end
242
-
243
-
244
- desc "Delete an application"
245
- group :apps, :manage
246
- input(:apps, :argument => :splat, :singular => :app,
247
- :desc => "Applications to delete",
248
- :from_given => by_name("app")) {
249
- apps = client.apps
250
- fail "No applications." if apps.empty?
251
-
252
- [ask("Delete which application?", :choices => apps.sort_by(&:name),
253
- :display => proc(&:name))]
254
- }
255
- input(:really, :type => :boolean, :forget => true,
256
- :default => proc { force? || interact }) { |name, color|
257
- ask("Really delete #{c(name, color)}?", :default => false)
258
- }
259
- input :routes, :type => :boolean, :default => false,
260
- :desc => "Delete associated routes"
261
- input :orphaned, :aliases => "-o", :type => :boolean,
262
- :desc => "Delete orphaned instances"
263
- input :all, :type => :boolean, :default => false,
264
- :desc => "Delete all applications"
265
- def delete
266
- apps = client.apps
267
-
268
- if input[:all]
269
- return unless input[:really, "ALL APPS", :bad]
270
-
271
- to_delete = apps
272
- others = []
273
- else
274
- to_delete = input[:apps]
275
- others = apps - to_delete
276
- end
277
-
278
- orphaned = find_orphaned_services(to_delete, others)
279
-
280
- deleted = []
281
- spaced(to_delete) do |app|
282
- really = input[:all] || input[:really, app.name, :name]
283
- next unless really
284
-
285
- deleted << app
286
-
287
- with_progress("Deleting #{c(app.name, :name)}") do
288
- app.routes.collect(&:delete!) if input[:routes]
289
- app.delete!
290
- end
291
- end
292
-
293
- delete_orphaned_services(orphaned, input[:orphaned])
294
-
295
- to_delete
296
- end
297
-
298
-
299
- desc "List an app's instances"
300
- group :apps, :info, :hidden => true
301
- input :apps, :argument => :splat, :singular => :app,
302
- :desc => "Applications whose instances to list",
303
- :from_given => by_name("app")
304
- def instances
305
- apps = input[:apps]
306
- fail "No applications given." if apps.empty?
307
-
308
- spaced(apps) do |app|
309
- instances =
310
- with_progress("Getting instances for #{c(app.name, :name)}") do
311
- app.instances
312
- end
313
-
314
- spaced(instances) do |i|
315
- if quiet?
316
- line i.id
317
- else
318
- display_instance(i)
319
- end
320
- end
321
- end
322
- end
323
-
324
-
325
- desc "List an app's crashed instances"
326
- group :apps, :info, :hidden => true
327
- input :apps, :argument => :splat, :singular => :app,
328
- :desc => "Applications whose crashed instances to list",
329
- :from_given => by_name("app")
330
- def crashes
331
- apps = input[:apps]
332
- fail "No applications given." if apps.empty?
333
-
334
- spaced(apps) do |app|
335
- instances =
336
- with_progress("Getting crashed instances for #{c(app.name, :name)}") do
337
- app.crashes
338
- end
339
-
340
- spaced(instances) do |i|
341
- if quiet?
342
- line i.id
343
- else
344
- display_crashed_instance(i)
345
- end
346
- end
347
- end
348
- end
349
-
350
-
351
- desc "Update the instances/memory limit for an application"
352
- group :apps, :info, :hidden => true
353
- input :app, :argument => true, :desc => "Application to update",
354
- :from_given => by_name("app")
355
- input(:instances, :type => :numeric,
356
- :desc => "Number of instances to run") { |default|
357
- ask("Instances", :default => default)
358
- }
359
- input(:memory, :desc => "Memory limit") { |default|
360
- ask("Memory Limit", :choices => memory_choices(default),
361
- :allow_other => true,
362
- :default => human_mb(default))
363
- }
364
- input :plan, :default => "D100",
365
- :desc => "Application plan (e.g. D100, P200)"
366
- input :restart, :type => :boolean, :default => true,
367
- :desc => "Restart app after updating?"
368
- def scale
369
- app = input[:app]
370
-
371
- if input.given?(:instances)
372
- instances = input[:instances, app.total_instances]
373
- end
374
-
375
- if input.given?(:memory)
376
- memory = input[:memory, app.memory]
377
- end
378
-
379
- if input.given?(:plan)
380
- fail "Plans not supported on target cloud." unless v2?
381
-
382
- plan_name = input[:plan]
383
- production = !!(plan_name =~ /^p/i)
384
- end
385
-
386
- unless instances || memory || plan_name
387
- instances = input[:instances, app.total_instances]
388
- memory = input[:memory, app.memory]
389
- end
390
-
391
- memory = megabytes(memory) if memory
392
-
393
- instances_changed = instances && instances != app.total_instances
394
- memory_changed = memory && memory != app.memory
395
- plan_changed = plan_name && production != app.production
396
-
397
- unless memory_changed || instances_changed || plan_changed
398
- fail "No changes!"
399
- end
400
-
401
- with_progress("Scaling #{c(app.name, :name)}") do
402
- app.total_instances = instances if instances_changed
403
- app.memory = memory if memory_changed
404
- app.production = production if plan_changed
405
- app.update!
406
- end
407
-
408
- if memory_changed && app.started? && input[:restart]
409
- invoke :restart, :app => app
410
- end
411
- end
412
-
413
-
414
- desc "Print out an app's logs"
415
- group :apps, :info, :hidden => true
416
- input :app, :argument => true,
417
- :desc => "Application to get the logs of",
418
- :from_given => by_name("app")
419
- input :instance, :default => "0",
420
- :desc => "Instance of application to get the logs of"
421
- input :all, :type => :boolean, :default => false,
422
- :desc => "Get logs for every instance"
423
- def logs
424
- app = input[:app]
425
-
426
- instances =
427
- if input[:all] || input[:instance] == "all"
428
- app.instances
429
- else
430
- app.instances.select { |i| i.id == input[:instance] }
431
- end
432
-
433
- if instances.empty?
434
- if input[:all]
435
- fail "No instances found."
436
- else
437
- fail "Instance #{app.name} \##{input[:instance]} not found."
438
- end
439
- end
440
-
441
- spaced(instances) do |i|
442
- show_instance_logs(app, i)
443
- end
444
- end
445
-
446
-
447
- desc "Print out the logs for an app's crashed instances"
448
- group :apps, :info, :hidden => true
449
- input :app, :argument => true,
450
- :desc => "Application to get the logs of",
451
- :from_given => by_name("app")
452
- def crashlogs
453
- app = input[:app]
454
-
455
- crashes = app.crashes
456
-
457
- fail "No crashed instances found." if crashes.empty?
458
-
459
- spaced(crashes) do |i|
460
- show_instance_logs(app, i)
461
- end
462
- end
463
-
464
-
465
- desc "Print out an app's file contents"
466
- group :apps, :info, :hidden => true
467
- input :app, :argument => true,
468
- :desc => "Application to inspect the files of",
469
- :from_given => by_name("app")
470
- input :path, :argument => true, :default => "/",
471
- :desc => "Path of file to read"
472
- def file
473
- app = input[:app]
474
- path = input[:path]
475
-
476
- file =
477
- with_progress("Getting file contents") do
478
- app.file(*path.split("/"))
479
- end
480
-
481
- if quiet?
482
- print file
483
- else
484
- line
485
-
486
- file.split("\n").each do |l|
487
- line l
488
- end
489
- end
490
- rescue CFoundry::NotFound
491
- fail "Invalid path #{b(path)} for app #{b(app.name)}"
492
- rescue CFoundry::FileError => e
493
- fail e.description
494
- end
495
-
496
- desc "Examine an app's files"
497
- group :apps, :info, :hidden => true
498
- input :app, :argument => true,
499
- :desc => "Application to inspect the files of",
500
- :from_given => by_name("app")
501
- input :path, :argument => :optional, :default => "/",
502
- :desc => "Path of directory to list"
503
- def files
504
- app = input[:app]
505
- path = input[:path]
506
-
507
- if quiet?
508
- files =
509
- with_progress("Getting file listing") do
510
- app.files(*path.split("/"))
511
- end
512
-
513
- files.each do |file|
514
- line file.join("/")
515
- end
516
- else
517
- invoke :file, :app => app, :path => path
518
- end
519
- rescue CFoundry::NotFound
520
- fail "Invalid path #{b(path)} for app #{b(app.name)}"
521
- rescue CFoundry::FileError => e
522
- fail e.description
523
- end
524
-
525
- desc "Stream an app's file contents"
526
- group :apps, :info, :hidden => true
527
- input :app, :argument => true,
528
- :desc => "Application to inspect the file of",
529
- :from_given => by_name("app")
530
- input :path, :argument => true, :default => "/",
531
- :desc => "Path of file to stream"
532
- def tail
533
- app = input[:app]
534
- path = input[:path]
535
-
536
- app.stream_file(*path.split("/")) do |contents|
537
- print contents
538
- end
539
- rescue CFoundry::NotFound
540
- fail "Invalid path #{b(path)} for app #{b(app.name)}"
541
- rescue CFoundry::FileError => e
542
- fail e.description
543
- end
544
-
545
-
546
- desc "Get application health"
547
- group :apps, :info, :hidden => true
548
- input :apps, :argument => :splat, :singular => :app,
549
- :desc => "Applications to start",
550
- :from_given => by_name("app")
551
- def health
552
- apps = input[:apps]
553
- fail "No applications given." if apps.empty?
554
-
555
- health =
556
- with_progress("Getting health status") do
557
- apps.collect { |a| [a, app_status(a)] }
558
- end
559
-
560
- line unless quiet?
561
-
562
- spaced(health) do |app, status|
563
- start_line "#{c(app.name, :name)}: " unless quiet?
564
- puts status
565
- end
566
- end
567
-
568
-
569
- desc "Display application instance status"
570
- group :apps, :info, :hidden => true
571
- input :app, :argument => true,
572
- :desc => "Application to get the stats for",
573
- :from_given => by_name("app")
574
- def stats
575
- app = input[:app]
576
-
577
- stats =
578
- with_progress("Getting stats for #{c(app.name, :name)}") do |s|
579
- begin
580
- app.stats
581
- rescue CFoundry::StatsError
582
- s.fail do
583
- err "Application #{b(app.name)} is not running."
584
- return
585
- end
586
- end
587
- end
588
-
589
- line unless quiet?
590
-
591
- table(
592
- %w{instance cpu memory disk},
593
- stats.sort_by { |idx, _| idx.to_i }.collect { |idx, info|
594
- idx = c("\##{idx}", :instance)
595
-
596
- if info[:state] == "DOWN"
597
- [idx, c("down", :bad)]
598
- else
599
- stats = info[:stats]
600
- usage = stats[:usage]
601
-
602
- if usage
603
- [ idx,
604
- "#{percentage(usage[:cpu])} of #{b(stats[:cores])} cores",
605
- "#{usage(usage[:mem] * 1024, stats[:mem_quota])}",
606
- "#{usage(usage[:disk], stats[:disk_quota])}"
607
- ]
608
- else
609
- [idx, c("n/a", :neutral)]
610
- end
611
- end
612
- })
613
- end
614
-
615
-
616
- desc "Add a URL mapping for an app"
617
- group :apps, :info, :hidden => true
618
- input :app, :argument => true,
619
- :desc => "Application to add the URL to",
620
- :from_given => by_name("app")
621
- input :url, :argument => true,
622
- :desc => "URL to map to the application"
623
- def map
624
- app = input[:app]
625
-
626
- simple = input[:url].sub(/^https?:\/\/(.*)\/?/i, '\1')
627
-
628
- if v2?
629
- host, domain_name = simple.split(".", 2)
630
-
631
- domain =
632
- client.current_space.domain_by_name(domain_name, :depth => 0)
633
-
634
- fail "Invalid domain '#{domain_name}'" unless domain
635
-
636
- route = client.routes_by_host(host, :depth => 0).find do |r|
637
- r.domain == domain
638
- end
639
-
640
- unless route
641
- route = client.route
642
-
643
- with_progress("Creating route #{c(simple, :name)}") do
644
- route.host = host
645
- route.domain = domain
646
- route.space = app.space
647
- route.create!
648
- end
649
- end
650
-
651
- with_progress("Binding #{c(simple, :name)} to #{c(app.name, :name)}") do
652
- app.add_route(route)
653
- end
654
- else
655
- with_progress("Updating #{c(app.name, :name)}") do
656
- app.urls << simple
657
- app.update!
658
- end
659
- end
660
- end
661
-
662
-
663
- desc "Remove a URL mapping from an app"
664
- group :apps, :info, :hidden => true
665
- input :app, :argument => true,
666
- :desc => "Application to remove the URL from",
667
- :from_given => by_name("app")
668
- input(:url, :argument => true, :desc => "URL to unmap") { |choices|
669
- ask("Which URL?", :choices => choices)
670
- }
671
- def unmap
672
- app = input[:app]
673
- url = input[:url, app.urls]
674
-
675
- simple = url.sub(/^https?:\/\/(.*)\/?/i, '\1')
676
-
677
- if v2?
678
- host, domain_name = simple.split(".", 2)
679
-
680
- domain =
681
- client.current_space.domain_by_name(domain_name, :depth => 0)
682
-
683
- fail "Invalid domain '#{domain_name}'" unless domain
684
-
685
- route = app.routes_by_host(host, :depth => 0).find do |r|
686
- r.domain == domain
687
- end
688
-
689
- fail "Invalid route '#{simple}'" unless route
690
-
691
- with_progress("Removing route #{c(simple, :name)}") do
692
- app.remove_route(route)
693
- end
694
- else
695
- with_progress("Updating #{c(app.name, :name)}") do |s|
696
- unless app.urls.delete(simple)
697
- s.fail do
698
- err "URL #{url} is not mapped to this application."
699
- return
700
- end
701
- end
702
-
703
- app.update!
704
- end
705
- end
706
- end
707
-
708
-
709
- desc "Show all environment variables set for an app"
710
- group :apps, :info, :hidden => true
711
- input :app, :argument => true,
712
- :desc => "Application to inspect the environment of",
713
- :from_given => by_name("app")
714
- def env
715
- app = input[:app]
716
-
717
- vars =
718
- with_progress("Getting env for #{c(app.name, :name)}") do |s|
719
- app.env
720
- end
721
-
722
- line unless quiet?
723
-
724
- vars.each do |name, val|
725
- line "#{c(name, :name)}: #{val}"
726
- end
727
- end
728
-
729
-
730
- VALID_ENV_VAR = /^[a-zA-Za-z_][[:alnum:]_]*$/
731
-
732
- desc "Set an environment variable"
733
- group :apps, :info, :hidden => true
734
- input :app, :argument => true,
735
- :desc => "Application to set the variable for",
736
- :from_given => by_name("app")
737
- input :name, :argument => true,
738
- :desc => "Environment variable name"
739
- input :value, :argument => :optional,
740
- :desc => "Environment variable value"
741
- input :restart, :type => :boolean, :default => true,
742
- :desc => "Restart app after updating?"
743
- def set_env
744
- app = input[:app]
745
- name = input[:name]
746
-
747
- if value = input[:value]
748
- name = input[:name]
749
- elsif name["="]
750
- name, value = name.split("=")
751
- end
752
-
753
- unless name =~ VALID_ENV_VAR
754
- fail "Invalid variable name; must match #{VALID_ENV_VAR.inspect}"
755
- end
756
-
757
- with_progress("Updating #{c(app.name, :name)}") do
758
- app.env[name] = value
759
- app.update!
760
- end
761
-
762
- if app.started? && input[:restart]
763
- invoke :restart, :app => app
764
- end
765
- end
766
-
767
-
768
- desc "Remove an environment variable"
769
- group :apps, :info, :hidden => true
770
- input :app, :argument => true,
771
- :desc => "Application to set the variable for",
772
- :from_given => by_name("app")
773
- input :name, :argument => true,
774
- :desc => "Environment variable name"
775
- input :restart, :type => :boolean, :default => true,
776
- :desc => "Restart app after updating?"
777
- def unset_env
778
- app = input[:app]
779
- name = input[:name]
780
-
781
- with_progress("Updating #{c(app.name, :name)}") do
782
- app.env.delete(name)
783
- app.update!
784
- end
785
-
786
- if app.started? && input[:restart]
787
- invoke :restart, :app => app
788
- end
789
- end
790
-
791
-
792
- desc "DEPRECATED. Use 'push' instead."
793
- input :app, :argument => :optional
794
- def update
795
- fail "The 'update' command is no longer needed; use 'push' instead."
796
- end
797
-
798
- private
799
-
800
- def app_matches(a, options)
801
- if name = options[:name]
802
- return false if a.name !~ /#{name}/
803
- end
804
-
805
- if runtime = options[:runtime]
806
- return false if a.runtime.name !~ /#{runtime}/
807
- end
808
-
809
- if framework = options[:framework]
810
- return false if a.framework.name !~ /#{framework}/
811
- end
812
-
813
- if url = options[:url]
814
- return false if a.urls.none? { |u| u =~ /#{url}/ }
815
- end
816
-
817
- true
818
- end
819
-
820
- IS_UTF8 = !!(ENV["LC_ALL"] || ENV["LC_CTYPE"] || ENV["LANG"] || "")["UTF-8"]
821
-
822
- def display_app(a)
823
- status = app_status(a)
824
-
825
- line "#{c(a.name, :name)}: #{status}"
826
-
827
- indented do
828
- line "platform: #{b(a.framework.name)} on #{b(a.runtime.name)}"
829
-
830
- start_line "usage: #{b(human_mb(a.memory))}"
831
- print " #{d(IS_UTF8 ? "\xc3\x97" : "x")} #{b(a.total_instances)}"
832
- print " instance#{a.total_instances == 1 ? "" : "s"}"
833
-
834
- line
835
-
836
- unless a.urls.empty?
837
- line "urls: #{a.urls.collect { |u| b(u) }.join(", ")}"
838
- end
839
-
840
- unless a.services.empty?
841
- line "services: #{a.services.collect { |s| b(s.name) }.join(", ")}"
842
- end
843
- end
844
- end
845
-
846
- def sync_app(app, path)
847
- upload_app(app, path)
848
-
849
- diff = {}
850
-
851
- if input.given?(:memory)
852
- mem = megabytes(input[:memory])
853
-
854
- if mem != app.memory
855
- diff[:memory] = [app.memory, mem]
856
- app.memory = mem
857
- end
858
- end
859
-
860
- if input.given?(:instances)
861
- instances = input[:instances]
862
-
863
- if instances != app.total_instances
864
- diff[:instances] = [app.total_instances, instances]
865
- app.total_instances = instances
866
- end
867
- end
868
-
869
- if input.given?(:framework)
870
- all_frameworks = client.frameworks
871
-
872
- framework = input[:framework, all_frameworks, all_frameworks]
873
-
874
- if framework != app.framework
875
- diff[:framework] = [app.framework.name, framework.name]
876
- app.framework = framework
877
- end
878
- end
879
-
880
- if input.given?(:runtime)
881
- all_runtimes = client.runtimes
882
-
883
- runtime = input[:runtime, all_runtimes, all_runtimes]
884
-
885
- if runtime != app.runtime
886
- diff[:runtime] = [app.runtime.name, runtime.name]
887
- app.runtime = runtime
888
- end
889
- end
890
-
891
- if input.given?(:command) && input[:command] != app.command
892
- command = input[:command]
893
-
894
- if command != app.command
895
- diff[:command] = [app.command, command]
896
- app.command = command
897
- end
898
- end
899
-
900
- if input.given?(:plan) && v2?
901
- production = !!(input[:plan] =~ /^p/i)
902
-
903
- if production != app.production
904
- diff[:production] = [bool(app.production), bool(production)]
905
- app.production = production
906
- end
907
- end
908
-
909
- unless diff.empty?
910
- line "Changes:"
911
-
912
- indented do
913
- diff.each do |name, change|
914
- old, new = change
915
- line "#{c(name, :name)}: #{old} #{c("->", :dim)} #{new}"
916
- end
917
- end
918
-
919
- with_progress("Updating #{c(app.name, :name)}") do
920
- app.update!
921
- end
922
- end
923
-
924
- if input[:restart] && app.started?
925
- invoke :restart, :app => app
926
- end
927
- end
928
-
929
- def create_app(name, path)
930
- app = client.app
931
- app.name = name
932
- app.space = client.current_space if client.current_space
933
- app.total_instances = input[:instances]
934
- app.production = !!(input[:plan] =~ /^p/i) if v2?
935
-
936
- detector = Detector.new(client, path)
937
- all_frameworks = detector.all_frameworks
938
- all_runtimes = detector.all_runtimes
939
-
940
- if detected_framework = detector.detect_framework
941
- framework = input[
942
- :framework,
943
- all_frameworks,
944
- [detected_framework],
945
- detected_framework,
946
- :other
947
- ]
948
- else
949
- framework = input[:framework, all_frameworks, all_frameworks]
950
- end
951
-
952
-
953
- if framework.name == "standalone"
954
- detected_runtimes = detector.detect_runtimes
955
- else
956
- detected_runtimes = detector.runtimes(framework)
957
- end
958
-
959
- if detected_runtimes.size == 1
960
- default_runtime = detected_runtimes.first
961
- end
962
-
963
- if detected_runtimes.empty?
964
- runtime = input[:runtime, all_runtimes, all_runtimes]
965
- else
966
- runtime = input[
967
- :runtime,
968
- all_runtimes,
969
- detected_runtimes,
970
- default_runtime,
971
- :other
972
- ]
973
- end
974
-
975
-
976
- fail "Invalid framework '#{input[:framework]}'" unless framework
977
- fail "Invalid runtime '#{input[:runtime]}'" unless runtime
978
-
979
- app.framework = framework
980
- app.runtime = runtime
981
-
982
- app.command = input[:command] if framework.name == "standalone"
983
-
984
- default_memory = detector.suggested_memory(framework) || 64
985
- app.memory = megabytes(input[:memory, human_mb(default_memory)])
986
-
987
- app = filter(:create_app, app)
988
-
989
- with_progress("Creating #{c(app.name, :name)}") do
990
- app.create!
991
- end
992
-
993
- line unless quiet?
994
-
995
- url = input[:url, name]
996
-
997
- mapped_url = false
998
- until !url || mapped_url
999
- begin
1000
- invoke :map, :app => app, :url => url
1001
- mapped_url = true
1002
- rescue CFoundry::RouteHostTaken, CFoundry::UriAlreadyTaken => e
1003
- line c(e.description, :bad)
1004
- line
1005
-
1006
- input.forget(:url)
1007
- url = input[:url, name]
1008
-
1009
- # version bumps on v1 even though mapping fails
1010
- app.invalidate! unless v2?
1011
- end
1012
- end
1013
-
1014
- bindings = []
1015
-
1016
- if input[:create_services] && !force?
1017
- while true
1018
- invoke :create_service, { :app => app }, :plan => :interact
1019
- break unless ask "Create another service?", :default => false
1020
- end
1021
- end
1022
-
1023
- if input[:bind_services] && !force?
1024
- instances = client.service_instances
1025
-
1026
- while true
1027
- invoke :bind_service, :app => app
1028
-
1029
- break if (instances - app.services).empty?
1030
-
1031
- break unless ask("Bind another service?", :default => false)
1032
- end
1033
- end
1034
-
1035
- app = filter(:push_app, app)
1036
-
1037
- begin
1038
- upload_app(app, path)
1039
- rescue
1040
- err "Upload failed. Try again with 'vmc push'."
1041
- raise
1042
- end
1043
-
1044
- invoke :start, :app => app if input[:start]
1045
- end
1046
-
1047
- def upload_app(app, path)
1048
- with_progress("Uploading #{c(app.name, :name)}") do
1049
- app.upload(path)
1050
- end
1051
- end
1052
-
1053
- # set app debug mode, ensuring it's valid, and shutting it down
1054
- def switch_mode(app, mode)
1055
- mode = nil if mode == "none"
1056
- mode = "run" if mode == "debug_mode" # no value given
1057
-
1058
- return false if app.debug_mode == mode
1059
-
1060
- if mode.nil?
1061
- with_progress("Removing debug mode") do
1062
- app.debug_mode = nil
1063
- app.stop! if app.started?
1064
- end
1065
-
1066
- return true
1067
- end
1068
-
1069
- with_progress("Switching mode to #{c(mode, :name)}") do |s|
1070
- runtime = client.runtimes.find { |r| r.name == app.runtime.name }
1071
- modes = runtime.debug_modes
1072
-
1073
- if modes.include?(mode)
1074
- app.debug_mode = mode
1075
- app.stop! if app.started?
1076
- else
1077
- fail "Unknown mode '#{mode}'; available: #{modes.join ", "}"
1078
- end
1079
- end
1080
- end
1081
-
1082
- APP_CHECK_LIMIT = 60
1083
-
1084
- def check_application(app)
1085
- with_progress("Checking #{c(app.name, :name)}") do |s|
1086
- if app.debug_mode == "suspend"
1087
- s.skip do
1088
- line "Application is in suspended debugging mode."
1089
- line "It will wait for you to attach to it before starting."
1090
- end
1091
- end
1092
-
1093
- seconds = 0
1094
- until app.healthy?
1095
- sleep 1
1096
- seconds += 1
1097
- if seconds == APP_CHECK_LIMIT
1098
- s.give_up do
1099
- err "Application failed to start."
1100
- # TODO: print logs
1101
- end
1102
- end
1103
- end
1104
- end
1105
- end
1106
-
1107
- # choose the right color for app/instance state
1108
- def state_color(s)
1109
- case s
1110
- when "STARTING"
1111
- :neutral
1112
- when "STARTED", "RUNNING"
1113
- :good
1114
- when "DOWN"
1115
- :bad
1116
- when "FLAPPING"
1117
- :error
1118
- when "N/A"
1119
- :unknown
1120
- else
1121
- :warning
1122
- end
1123
- end
1124
-
1125
- def app_status(a)
1126
- health = a.health
1127
-
1128
- if a.debug_mode == "suspend" && health == "0%"
1129
- c("suspended", :neutral)
1130
- else
1131
- c(health.downcase, state_color(health))
1132
- end
1133
- end
1134
-
1135
- def display_instance(i)
1136
- start_line "instance #{c("\##{i.id}", :instance)}: "
1137
- puts "#{b(c(i.state.downcase, state_color(i.state)))} "
1138
-
1139
- indented do
1140
- if s = i.since
1141
- line "started: #{c(s.strftime("%F %r"), :neutral)}"
1142
- end
1143
-
1144
- if d = i.debugger
1145
- line "debugger: port #{b(d[:port])} at #{b(d[:ip])}"
1146
- end
1147
-
1148
- if c = i.console
1149
- line "console: port #{b(c[:port])} at #{b(c[:ip])}"
1150
- end
1151
- end
1152
- end
1153
-
1154
- def display_crashed_instance(i)
1155
- start_line "instance #{c("\##{i.id}", :instance)}: "
1156
- puts "#{b(c("crashed", :error))} "
1157
-
1158
- indented do
1159
- if s = i.since
1160
- line "since: #{c(s.strftime("%F %r"), :neutral)}"
1161
- end
1162
- end
1163
- end
1164
-
1165
- def show_instance_logs(app, i)
1166
- return unless i.id
1167
-
1168
- logs =
1169
- with_progress(
1170
- "Getting logs for #{c(app.name, :name)} " +
1171
- c("\##{i.id}", :instance)) do
1172
- i.files("logs")
1173
- end
1174
-
1175
- line unless quiet?
1176
-
1177
- spaced(logs) do |log|
1178
- begin
1179
- body =
1180
- with_progress("Reading " + b(log.join("/"))) do |s|
1181
- i.file(*log)
1182
- end
1183
-
1184
- lines body
1185
- line unless body.empty?
1186
- rescue CFoundry::NotFound
1187
- end
1188
- end
1189
- end
1190
-
1191
- def find_orphaned_services(apps, others = [])
1192
- orphaned = Set.new
1193
-
1194
- apps.each do |a|
1195
- a.services.each do |i|
1196
- if others.none? { |x| x.binds?(i) }
1197
- orphaned << i
1198
- end
1199
- end
1200
- end
1201
-
1202
- orphaned.each(&:invalidate!)
1203
- end
1204
-
1205
- def delete_orphaned_services(instances, orphaned)
1206
- return if instances.empty?
1207
-
1208
- line unless quiet? || force?
1209
-
1210
- instances.select { |i|
1211
- orphaned ||
1212
- ask("Delete orphaned service instance #{c(i.name, :name)}?",
1213
- :default => false)
1214
- }.each do |instance|
1215
- # TODO: splat
1216
- invoke :delete_service, :instance => instance, :really => true
1217
- end
1218
- end
1219
-
1220
- def ask_with_other(message, all, choices, default, other)
1221
- choices = choices.sort_by(&:name)
1222
- choices << other if other
1223
-
1224
- opts = {
1225
- :choices => choices,
1226
- :display => proc { |x|
1227
- if other && x == other
1228
- "other"
1229
- else
1230
- x.name
1231
- end
1232
- }
1233
- }
1234
-
1235
- opts[:default] = default if default
1236
-
1237
- res = ask(message, opts)
1238
-
1239
- if other && res == other
1240
- opts[:choices] = all
1241
- res = ask(message, opts)
1242
- end
1243
-
1244
- res
1245
- end
1246
- def usage(used, limit)
1247
- "#{b(human_size(used))} of #{b(human_size(limit, 0))}"
1248
- end
1249
-
1250
- def percentage(num, low = 50, mid = 70)
1251
- color =
1252
- if num <= low
1253
- :good
1254
- elsif num <= mid
1255
- :warning
1256
- else
1257
- :bad
1258
- end
1259
-
1260
- c(format("%.1f\%", num), color)
1261
- end
1262
-
1263
- def megabytes(str)
1264
- if str =~ /T$/i
1265
- str.to_i * 1024 * 1024
1266
- elsif str =~ /G$/i
1267
- str.to_i * 1024
1268
- elsif str =~ /M$/i
1269
- str.to_i
1270
- elsif str =~ /K$/i
1271
- str.to_i / 1024
1272
- else # assume megabytes
1273
- str.to_i
1274
- end
1275
- end
1276
-
1277
- def human_size(num, precision = 1)
1278
- sizes = ["G", "M", "K"]
1279
- sizes.each.with_index do |suf, i|
1280
- pow = sizes.size - i
1281
- unit = 1024 ** pow
1282
- if num >= unit
1283
- return format("%.#{precision}f%s", num / unit, suf)
1284
- end
1285
- end
1286
-
1287
- format("%.#{precision}fB", num)
1288
- end
1289
-
1290
- def human_mb(num)
1291
- human_size(num * 1024 * 1024, 0)
1292
- end
1293
-
1294
- def target_base
1295
- client.target.sub(/^https?:\/\/([^\.]+\.)?(.+)\/?/, '\2')
1296
- end
1297
-
1298
- def url_choices(name)
1299
- if v2?
1300
- client.current_space.domains.sort_by(&:name).collect do |d|
1301
- # TODO: check availability
1302
- "#{name}.#{d.name}"
1303
- end
1304
- else
1305
- ["#{name}.#{target_base}"]
1306
- end
1307
- end
1308
-
1309
- def memory_choices(exclude = 0)
1310
- info = client.info
1311
- used = info[:usage][:memory]
1312
- limit = info[:limits][:memory]
1313
- available = limit - used + exclude
1314
-
1315
- mem = 64
1316
- choices = []
1317
- until mem > available
1318
- choices << human_mb(mem)
1319
- mem *= 2
1320
- end
1321
-
1322
- choices
1323
- end
1324
-
1325
- def bool(b)
1326
- if b
1327
- c("true", :yes)
1328
- else
1329
- c("false", :no)
1330
- end
1331
- end
1332
- end
1333
- end