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

Sign up to get free protection for your applications and to get access to all the features.
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