vmc 0.3.15 → 0.3.16.beta.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -9,6 +9,7 @@ module VMC::Cli::Command
9
9
 
10
10
  class Apps < Base
11
11
  include VMC::Cli::ServicesHelper
12
+ include VMC::Cli::ManifestHelper
12
13
 
13
14
  def list
14
15
  apps = client.apps
@@ -37,105 +38,37 @@ module VMC::Cli::Command
37
38
  HEALTH_TICKS = 5/SLEEP_TIME
38
39
  TAIL_TICKS = 45/SLEEP_TIME
39
40
  GIVEUP_TICKS = 120/SLEEP_TIME
40
- YES_SET = Set.new(["y", "Y", "yes", "YES"])
41
41
 
42
- def start(appname, push = false)
43
- app = client.app_info(appname)
44
-
45
- return display "Application '#{appname}' could not be found".red if app.nil?
46
- return display "Application '#{appname}' already started".yellow if app[:state] == 'STARTED'
47
-
48
- if @options[:debug]
49
- runtimes = client.runtimes_info
50
- return display "Cannot get runtime information." unless runtimes
51
-
52
- runtime = runtimes[app[:staging][:stack].to_sym]
53
- return display "Unknown runtime." unless runtime
54
-
55
- unless runtime[:debug_modes] and runtime[:debug_modes].include? @options[:debug]
56
- modes = runtime[:debug_modes] || []
57
-
58
- display "\nApplication '#{appname}' cannot start in '#{@options[:debug]}' mode"
59
-
60
- if push
61
- display "Try `vmc start' with one of the following modes: #{modes.inspect}"
62
- else
63
- display "Available modes: #{modes.inspect}"
64
- end
42
+ def info(what, default=nil)
43
+ @options[what] || (@app_info && @app_info[what.to_s]) || default
44
+ end
65
45
 
66
- return
46
+ def start(appname=nil, push=false)
47
+ if appname
48
+ do_start(appname, push)
49
+ else
50
+ each_app do |name|
51
+ do_start(name, push)
67
52
  end
68
53
  end
54
+ end
69
55
 
70
- banner = 'Staging Application: '
71
- display banner, false
72
-
73
- t = Thread.new do
74
- count = 0
75
- while count < TAIL_TICKS do
76
- display '.', false
77
- sleep SLEEP_TIME
78
- count += 1
56
+ def stop(appname=nil)
57
+ if appname
58
+ do_stop(appname)
59
+ else
60
+ reversed = []
61
+ each_app do |name|
62
+ reversed.unshift name
79
63
  end
80
- end
81
-
82
- app[:state] = 'STARTED'
83
- app[:debug] = @options[:debug]
84
- client.update_app(appname, app)
85
-
86
- Thread.kill(t)
87
- clear(LINE_LENGTH)
88
- display "#{banner}#{'OK'.green}"
89
-
90
- banner = 'Starting Application: '
91
- display banner, false
92
-
93
- count = log_lines_displayed = 0
94
- failed = false
95
- start_time = Time.now.to_i
96
64
 
97
- loop do
98
- display '.', false unless count > TICKER_TICKS
99
- sleep SLEEP_TIME
100
- begin
101
- break if app_started_properly(appname, count > HEALTH_TICKS)
102
- if !crashes(appname, false, start_time).empty?
103
- # Check for the existance of crashes
104
- display "\nError: Application [#{appname}] failed to start, logs information below.\n".red
105
- grab_crash_logs(appname, '0', true)
106
- if push and !no_prompt
107
- display "\n"
108
- delete_app(appname, false) if ask "Delete the application?", :default => true
109
- end
110
- failed = true
111
- break
112
- elsif count > TAIL_TICKS
113
- log_lines_displayed = grab_startup_tail(appname, log_lines_displayed)
114
- end
115
- rescue => e
116
- err(e.message, '')
117
- end
118
- count += 1
119
- if count > GIVEUP_TICKS # 2 minutes
120
- display "\nApplication is taking too long to start, check your logs".yellow
121
- break
65
+ reversed.each do |name|
66
+ do_stop(name)
122
67
  end
123
68
  end
124
- exit(false) if failed
125
- clear(LINE_LENGTH)
126
- display "#{banner}#{'OK'.green}"
127
- end
128
-
129
- def stop(appname)
130
- app = client.app_info(appname)
131
- return display "Application '#{appname}' already stopped".yellow if app[:state] == 'STOPPED'
132
- display 'Stopping Application: ', false
133
- app[:state] = 'STOPPED'
134
- client.update_app(appname, app)
135
- display 'OK'.green
136
69
  end
137
70
 
138
- def restart(appname)
71
+ def restart(appname=nil)
139
72
  stop(appname)
140
73
  start(appname)
141
74
  end
@@ -198,7 +131,6 @@ module VMC::Cli::Command
198
131
  app[:uris] = uris
199
132
  client.update_app(appname, app)
200
133
  display "Successfully unmapped url".green
201
-
202
134
  end
203
135
 
204
136
  def delete(appname=nil)
@@ -214,51 +146,6 @@ module VMC::Cli::Command
214
146
  end
215
147
  end
216
148
 
217
- def delete_app(appname, force)
218
- app = client.app_info(appname)
219
- services_to_delete = []
220
- app_services = app[:services]
221
- services_apps_hash = provisioned_services_apps_hash
222
- app_services.each { |service|
223
- del_service = force && no_prompt
224
- unless no_prompt || force
225
- del_service = ask(
226
- "Provisioned service [#{service}] detected, would you like to delete it?",
227
- :default => false
228
- )
229
-
230
- if del_service
231
- apps_using_service = services_apps_hash[service].reject!{ |app| app == appname}
232
- if apps_using_service.size > 0
233
- del_service = ask(
234
- "Provisioned service [#{service}] is also used by #{apps_using_service.size == 1 ? "app" : "apps"} #{apps_using_service.entries}, are you sure you want to delete it?",
235
- :default => false
236
- )
237
- end
238
- end
239
- end
240
- services_to_delete << service if del_service
241
- }
242
-
243
- display "Deleting application [#{appname}]: ", false
244
- client.delete_app(appname)
245
- display 'OK'.green
246
-
247
- services_to_delete.each do |s|
248
- delete_service_banner(s)
249
- end
250
- end
251
-
252
- def all_files(appname, path)
253
- instances_info_envelope = client.app_instances(appname)
254
- return if instances_info_envelope.is_a?(Array)
255
- instances_info = instances_info_envelope[:instances] || []
256
- instances_info.each do |entry|
257
- content = client.app_files(appname, path, entry[:index])
258
- display_logfile(path, content, entry[:index], "====> [#{entry[:index]}: #{path}] <====\n".bold)
259
- end
260
- end
261
-
262
149
  def files(appname, path='/')
263
150
  return all_files(appname, path) if @options[:all] && !@options[:instance]
264
151
  instance = @options[:instance] || '0'
@@ -317,194 +204,67 @@ module VMC::Cli::Command
317
204
  end
318
205
 
319
206
  def instances(appname, num=nil)
320
- if (num)
207
+ if num
321
208
  change_instances(appname, num)
322
209
  else
323
210
  get_instances(appname)
324
211
  end
325
212
  end
326
213
 
327
- def stats(appname)
328
- stats = client.app_stats(appname)
329
- return display JSON.pretty_generate(stats) if @options[:json]
330
-
331
- stats_table = table do |t|
332
- t.headings = 'Instance', 'CPU (Cores)', 'Memory (limit)', 'Disk (limit)', 'Uptime'
333
- stats.each do |entry|
334
- index = entry[:instance]
335
- stat = entry[:stats]
336
- hp = "#{stat[:host]}:#{stat[:port]}"
337
- uptime = uptime_string(stat[:uptime])
338
- usage = stat[:usage]
339
- if usage
340
- cpu = usage[:cpu]
341
- mem = (usage[:mem] * 1024) # mem comes in K's
342
- disk = usage[:disk]
343
- end
344
- mem_quota = stat[:mem_quota]
345
- disk_quota = stat[:disk_quota]
346
- mem = "#{pretty_size(mem)} (#{pretty_size(mem_quota, 0)})"
347
- disk = "#{pretty_size(disk)} (#{pretty_size(disk_quota, 0)})"
348
- cpu = cpu ? cpu.to_s : 'NA'
349
- cpu = "#{cpu}% (#{stat[:cores]})"
350
- t << [index, cpu, mem, disk, uptime]
351
- end
352
- end
353
- display "\n"
354
- if stats.empty?
355
- display "No running instances for [#{appname}]".yellow
214
+ def stats(appname=nil)
215
+ if appname
216
+ display "\n", false
217
+ do_stats(appname)
356
218
  else
357
- display stats_table
358
- end
359
- end
360
-
361
- def update(appname)
362
- app = client.app_info(appname)
363
- if @options[:canary]
364
- display "[--canary] is deprecated and will be removed in a future version".yellow
219
+ each_app do |n|
220
+ display "\n#{n}:"
221
+ do_stats(n)
222
+ end
365
223
  end
366
- path = @options[:path] || '.'
367
- upload_app_bits(appname, path)
368
- restart appname if app[:state] == 'STARTED'
369
224
  end
370
225
 
371
- def push(appname=nil)
372
- instances = @options[:instances] || 1
373
- exec = @options[:exec] || 'thin start'
374
- ignore_framework = @options[:noframework]
375
- no_start = @options[:nostart]
376
-
377
- path = @options[:path] || '.'
378
- appname ||= @options[:name]
379
- mem, memswitch = nil, @options[:mem]
380
- memswitch = normalize_mem(memswitch) if memswitch
381
- url = @options[:url]
382
-
383
- # Check app existing upfront if we have appname
384
- app_checked = false
226
+ def update(appname=nil)
385
227
  if appname
386
- err "Application '#{appname}' already exists, use update" if app_exists?(appname)
387
- app_checked = true
228
+ app = client.app_info(appname)
229
+ if @options[:canary]
230
+ display "[--canary] is deprecated and will be removed in a future version".yellow
231
+ end
232
+ upload_app_bits(appname, @path)
233
+ restart appname if app[:state] == 'STARTED'
388
234
  else
389
- raise VMC::Client::AuthError unless client.logged_in?
390
- end
391
-
392
- # check if we have hit our app limit
393
- check_app_limit
394
- # check memsize here for capacity
395
- if memswitch && !no_start
396
- check_has_capacity_for(mem_choice_to_quota(memswitch) * instances)
397
- end
235
+ each_app do |name|
236
+ display "Updating application '#{name}'..."
398
237
 
399
- unless no_prompt || @options[:path]
400
- unless ask('Would you like to deploy from the current directory?', :default => true)
401
- path = ask('Please enter in the deployment path')
238
+ app = client.app_info(name)
239
+ upload_app_bits(name, @application)
240
+ restart name if app[:state] == 'STARTED'
402
241
  end
403
242
  end
243
+ end
404
244
 
405
- path = File.expand_path(path)
406
- check_deploy_directory(path)
407
-
408
- appname ||= ask("Application Name") unless no_prompt
409
- err "Application Name required." if appname.nil? || appname.empty?
410
-
411
- if !app_checked and app_exists?(appname)
412
- err "Application '#{appname}' already exists, use update or delete."
413
- end
414
-
415
- default_url = "#{appname}.#{VMC::Cli::Config.suggest_url}"
416
-
417
- unless no_prompt || url
418
- url = ask(
419
- "Application Deployed URL",
420
- :default => default_url
245
+ def push(appname=nil)
246
+ unless no_prompt || @options[:path]
247
+ proceed = ask(
248
+ 'Would you like to deploy from the current directory?',
249
+ :default => true
421
250
  )
422
251
 
423
- # common error case is for prompted users to answer y or Y or yes or
424
- # YES to this ask() resulting in an unintended URL of y. Special case
425
- # this common error
426
- url = nil if YES_SET.member? url
427
- end
428
-
429
- url ||= default_url
430
-
431
- # Detect the appropriate framework.
432
- framework = nil
433
- unless ignore_framework
434
- framework = VMC::Cli::Framework.detect(path)
435
-
436
- if prompt_ok and framework
437
- framework_correct =
438
- ask("Detected a #{framework}, is this correct?", :default => true)
439
- end
440
-
441
- if prompt_ok && (framework.nil? || !framework_correct)
442
- display "#{"[WARNING]".yellow} Can't determine the Application Type." unless framework
443
- framework = VMC::Cli::Framework.lookup(
444
- ask(
445
- "Select Application Type",
446
- { :indexed => true,
447
- :choices => VMC::Cli::Framework.known_frameworks
448
- }
449
- )
450
- )
451
- display "Selected #{framework}"
252
+ unless proceed
253
+ @path = ask('Deployment path')
452
254
  end
453
- # Framework override, deprecated
454
- exec = framework.exec if framework && framework.exec
455
- else
456
- framework = VMC::Cli::Framework.new
457
255
  end
458
256
 
459
- err "Application Type undetermined for path '#{path}'" unless framework
460
-
461
- if memswitch
462
- mem = memswitch
463
- elsif prompt_ok
464
- mem = ask("Memory Reservation",
465
- :default => framework.memory, :choices => mem_choices)
466
- else
467
- mem = framework.memory
257
+ pushed = false
258
+ each_app(false) do |name|
259
+ display "Pushing application '#{name}'..." if name
260
+ do_push(name)
261
+ pushed = true
468
262
  end
469
263
 
470
- # Set to MB number
471
- mem_quota = mem_choice_to_quota(mem)
472
-
473
- # check memsize here for capacity
474
- check_has_capacity_for(mem_quota * instances) unless no_start
475
-
476
- display 'Creating Application: ', false
477
-
478
- manifest = {
479
- :name => "#{appname}",
480
- :staging => {
481
- :framework => framework.name,
482
- :runtime => @options[:runtime]
483
- },
484
- :uris => [url],
485
- :instances => instances,
486
- :resources => {
487
- :memory => mem_quota
488
- },
489
- }
490
-
491
- # Send the manifest to the cloud controller
492
- client.create_app(appname, manifest)
493
- display 'OK'.green
494
-
495
- # Services check
496
- unless no_prompt || @options[:noservices]
497
- services = client.services_info
498
- unless services.empty?
499
- proceed = ask("Would you like to bind any services to '#{appname}'?", :default => false)
500
- bind_services(appname, services) if proceed
501
- end
264
+ unless pushed
265
+ @application = @path
266
+ do_push(appname)
502
267
  end
503
-
504
- # Stage and upload the app bits.
505
- upload_app_bits(appname, path)
506
-
507
- start(appname, true) unless no_start
508
268
  end
509
269
 
510
270
  def environment(appname)
@@ -574,8 +334,7 @@ module VMC::Cli::Command
574
334
  err "Can't deploy applications from staging directory: [#{Dir.tmpdir}]"
575
335
  end
576
336
 
577
- def check_unreachable_links
578
- path = Dir.pwd
337
+ def check_unreachable_links(path)
579
338
  files = Dir.glob("#{path}/**/*", File::FNM_DOTMATCH)
580
339
  unreachable_paths = files.select { |f|
581
340
  File.symlink? f and !Pathname.new(f).realpath.to_s.include? path
@@ -585,6 +344,11 @@ module VMC::Cli::Command
585
344
  end
586
345
  end
587
346
 
347
+ def find_sockets(path)
348
+ files = Dir.glob("#{path}/**/*", File::FNM_DOTMATCH)
349
+ files && files.select { |f| File.socket? f }
350
+ end
351
+
588
352
  def upload_app_bits(appname, path)
589
353
  display 'Uploading Application:'
590
354
 
@@ -599,12 +363,19 @@ module VMC::Cli::Command
599
363
  if war_file = Dir.glob('*.war').first
600
364
  VMC::Cli::ZipUtil.unpack(war_file, explode_dir)
601
365
  else
602
- check_unreachable_links
366
+ check_unreachable_links(path)
603
367
  FileUtils.mkdir(explode_dir)
368
+
604
369
  files = Dir.glob('{*,.[^\.]*}')
370
+
605
371
  # Do not process .git files
606
372
  files.delete('.git') if files
373
+
607
374
  FileUtils.cp_r(files, explode_dir)
375
+
376
+ find_sockets(explode_dir).each do |s|
377
+ File.delete s
378
+ end
608
379
  end
609
380
 
610
381
  # Send the resource list to the cloudcontroller, the response will tell us what it already has..
@@ -684,78 +455,6 @@ module VMC::Cli::Command
684
455
  FileUtils.rm_rf(explode_dir) if explode_dir
685
456
  end
686
457
 
687
- def choose_existing_service(appname, user_services)
688
- return unless prompt_ok
689
-
690
- display "The following provisioned services are available"
691
- name = ask(
692
- "Please select one you wish to use",
693
- { :indexed => true,
694
- :choices => user_services.collect { |s| s[:name] }
695
- }
696
- )
697
-
698
- bind_service_banner(name, appname, false)
699
-
700
- true
701
- end
702
-
703
- def choose_new_service(appname, services)
704
- return unless prompt_ok
705
-
706
- display "The following system services are available"
707
-
708
- vendor = ask(
709
- "Please select one you wish to provision",
710
- { :indexed => true,
711
- :choices =>
712
- services.values.collect { |type|
713
- type.keys.collect(&:to_s)
714
- }.flatten.sort!
715
- }
716
- )
717
-
718
- default_name = random_service_name(vendor)
719
- service_name = ask("Specify the name of the service",
720
- :default => default_name)
721
-
722
- create_service_banner(vendor, service_name)
723
- bind_service_banner(service_name, appname)
724
- end
725
-
726
- def bind_services(appname, services)
727
- user_services = client.services
728
-
729
- selected_existing = false
730
- unless no_prompt || user_services.empty?
731
- if ask("Would you like to use an existing provisioned service?",
732
- :default => false)
733
- selected_existing = choose_existing_service(appname, user_services)
734
- end
735
- end
736
-
737
- # Create a new service and bind it here
738
- unless selected_existing
739
- choose_new_service(appname, services)
740
- end
741
- end
742
-
743
- def provisioned_services_apps_hash
744
- apps = client.apps
745
- services_apps_hash = {}
746
- apps.each {|app|
747
- app[:services].each { |svc|
748
- svc_apps = services_apps_hash[svc]
749
- unless svc_apps
750
- svc_apps = Set.new
751
- services_apps_hash[svc] = svc_apps
752
- end
753
- svc_apps.add(app[:name])
754
- } unless app[:services] == nil
755
- }
756
- services_apps_hash
757
- end
758
-
759
458
  def check_app_limit
760
459
  usage = client_info[:usage]
761
460
  limits = client_info[:limits]
@@ -910,7 +609,8 @@ module VMC::Cli::Command
910
609
 
911
610
  def display_logfile(path, content, instance='0', banner=nil)
912
611
  banner ||= "====> #{path} <====\n\n"
913
- if content && !content.empty?
612
+
613
+ unless content.empty?
914
614
  display banner
915
615
  prefix = "[#{instance}: #{path}] -".bold if @options[:prefixlogs]
916
616
  unless prefix
@@ -940,9 +640,9 @@ module VMC::Cli::Command
940
640
  log_file_paths.each do |path|
941
641
  begin
942
642
  content = client.app_files(appname, path, instance)
943
- rescue
643
+ display_logfile(path, content, instance)
644
+ rescue VMC::Client::NotFound
944
645
  end
945
- display_logfile(path, content, instance)
946
646
  end
947
647
  end
948
648
 
@@ -954,12 +654,15 @@ module VMC::Cli::Command
954
654
  map = VMC::Cli::Config.instances
955
655
  instance = map[instance] if map[instance]
956
656
 
957
- ['/logs/err.log', '/logs/staging.log', 'logs/stderr.log', 'logs/stdout.log', 'logs/startup.log'].each do |path|
657
+ %w{
658
+ /logs/err.log /logs/staging.log /app/logs/stderr.log
659
+ /app/logs/stdout.log /app/logs/startup.log /app/logs/migration.log
660
+ }.each do |path|
958
661
  begin
959
662
  content = client.app_files(appname, path, instance)
960
- rescue
663
+ display_logfile(path, content, instance)
664
+ rescue VMC::Client::NotFound
961
665
  end
962
- display_logfile(path, content, instance)
963
666
  end
964
667
  end
965
668
 
@@ -977,7 +680,338 @@ module VMC::Cli::Command
977
680
  end
978
681
  since + new_lines
979
682
  end
980
- rescue
683
+
684
+ def provisioned_services_apps_hash
685
+ apps = client.apps
686
+ services_apps_hash = {}
687
+ apps.each {|app|
688
+ app[:services].each { |svc|
689
+ svc_apps = services_apps_hash[svc]
690
+ unless svc_apps
691
+ svc_apps = Set.new
692
+ services_apps_hash[svc] = svc_apps
693
+ end
694
+ svc_apps.add(app[:name])
695
+ } unless app[:services] == nil
696
+ }
697
+ services_apps_hash
698
+ end
699
+
700
+ def delete_app(appname, force)
701
+ app = client.app_info(appname)
702
+ services_to_delete = []
703
+ app_services = app[:services]
704
+ services_apps_hash = provisioned_services_apps_hash
705
+ app_services.each { |service|
706
+ del_service = force && no_prompt
707
+ unless no_prompt || force
708
+ del_service = ask(
709
+ "Provisioned service [#{service}] detected, would you like to delete it?",
710
+ :default => false
711
+ )
712
+
713
+ if del_service
714
+ apps_using_service = services_apps_hash[service].reject!{ |app| app == appname}
715
+ if apps_using_service.size > 0
716
+ del_service = ask(
717
+ "Provisioned service [#{service}] is also used by #{apps_using_service.size == 1 ? "app" : "apps"} #{apps_using_service.entries}, are you sure you want to delete it?",
718
+ :default => false
719
+ )
720
+ end
721
+ end
722
+ end
723
+ services_to_delete << service if del_service
724
+ }
725
+
726
+ display "Deleting application [#{appname}]: ", false
727
+ client.delete_app(appname)
728
+ display 'OK'.green
729
+
730
+ services_to_delete.each do |s|
731
+ delete_service_banner(s)
732
+ end
733
+ end
734
+
735
+ def do_start(appname, push=false)
736
+ app = client.app_info(appname)
737
+
738
+ return display "Application '#{appname}' could not be found".red if app.nil?
739
+ return display "Application '#{appname}' already started".yellow if app[:state] == 'STARTED'
740
+
741
+ if @options[:debug]
742
+ runtimes = client.runtimes_info
743
+ return display "Cannot get runtime information." unless runtimes
744
+
745
+ runtime = runtimes[app[:staging][:stack].to_sym]
746
+ return display "Unknown runtime." unless runtime
747
+
748
+ unless runtime[:debug_modes] and runtime[:debug_modes].include? @options[:debug]
749
+ modes = runtime[:debug_modes] || []
750
+
751
+ display "\nApplication '#{appname}' cannot start in '#{@options[:debug]}' mode"
752
+
753
+ if push
754
+ display "Try 'vmc start' with one of the following modes: #{modes.inspect}"
755
+ else
756
+ display "Available modes: #{modes.inspect}"
757
+ end
758
+
759
+ return
760
+ end
761
+ end
762
+
763
+ banner = "Staging Application '#{appname}': "
764
+ display banner, false
765
+
766
+ t = Thread.new do
767
+ count = 0
768
+ while count < TAIL_TICKS do
769
+ display '.', false
770
+ sleep SLEEP_TIME
771
+ count += 1
772
+ end
773
+ end
774
+
775
+ app[:state] = 'STARTED'
776
+ app[:debug] = @options[:debug]
777
+ client.update_app(appname, app)
778
+
779
+ Thread.kill(t)
780
+ clear(LINE_LENGTH)
781
+ display "#{banner}#{'OK'.green}"
782
+
783
+ banner = "Starting Application '#{appname}': "
784
+ display banner, false
785
+
786
+ count = log_lines_displayed = 0
787
+ failed = false
788
+ start_time = Time.now.to_i
789
+
790
+ loop do
791
+ display '.', false unless count > TICKER_TICKS
792
+ sleep SLEEP_TIME
793
+ begin
794
+ break if app_started_properly(appname, count > HEALTH_TICKS)
795
+ if !crashes(appname, false, start_time).empty?
796
+ # Check for the existance of crashes
797
+ display "\nError: Application [#{appname}] failed to start, logs information below.\n".red
798
+ grab_crash_logs(appname, '0', true)
799
+ if push and !no_prompt
800
+ display "\n"
801
+ delete_app(appname, false) if ask "Delete the application?", :default => true
802
+ end
803
+ failed = true
804
+ break
805
+ elsif count > TAIL_TICKS
806
+ log_lines_displayed = grab_startup_tail(appname, log_lines_displayed)
807
+ end
808
+ rescue => e
809
+ err(e.message, '')
810
+ end
811
+ count += 1
812
+ if count > GIVEUP_TICKS # 2 minutes
813
+ display "\nApplication is taking too long to start, check your logs".yellow
814
+ break
815
+ end
816
+ end
817
+ exit(false) if failed
818
+ clear(LINE_LENGTH)
819
+ display "#{banner}#{'OK'.green}"
820
+ end
821
+
822
+ def do_stop(appname)
823
+ app = client.app_info(appname)
824
+ return display "Application '#{appname}' already stopped".yellow if app[:state] == 'STOPPED'
825
+ display "Stopping Application '#{appname}': ", false
826
+ app[:state] = 'STOPPED'
827
+ client.update_app(appname, app)
828
+ display 'OK'.green
829
+ end
830
+
831
+ def do_push(appname=nil)
832
+ unless @app_info || no_prompt
833
+ @manifest = { "applications" => { @path => { "name" => appname } } }
834
+
835
+ interact
836
+
837
+ if ask("Would you like to save this configuration?", :default => false)
838
+ save_manifest
839
+ end
840
+
841
+ resolve_manifest(@manifest)
842
+
843
+ @app_info = @manifest["applications"][@path]
844
+ end
845
+
846
+ instances = info(:instances, 1)
847
+ exec = info(:exec, 'thin start')
848
+
849
+ ignore_framework = @options[:noframework]
850
+ no_start = @options[:nostart]
851
+
852
+ appname ||= info(:name)
853
+ url = info(:url) || info(:urls)
854
+ mem, memswitch = nil, info(:mem)
855
+ memswitch = normalize_mem(memswitch) if memswitch
856
+
857
+ # Check app existing upfront if we have appname
858
+ app_checked = false
859
+ if appname
860
+ err "Application '#{appname}' already exists, use update" if app_exists?(appname)
861
+ app_checked = true
862
+ else
863
+ raise VMC::Client::AuthError unless client.logged_in?
864
+ end
865
+
866
+ # check if we have hit our app limit
867
+ check_app_limit
868
+ # check memsize here for capacity
869
+ if memswitch && !no_start
870
+ check_has_capacity_for(mem_choice_to_quota(memswitch) * instances)
871
+ end
872
+
873
+ appname ||= ask("Application Name") unless no_prompt
874
+ err "Application Name required." if appname.nil? || appname.empty?
875
+
876
+ check_deploy_directory(@application)
877
+
878
+ if !app_checked and app_exists?(appname)
879
+ err "Application '#{appname}' already exists, use update or delete."
880
+ end
881
+
882
+ default_url = "#{appname}.#{VMC::Cli::Config.suggest_url}"
883
+
884
+ unless no_prompt || url
885
+ url = ask(
886
+ "Application Deployed URL",
887
+ :default => default_url
888
+ )
889
+
890
+ # common error case is for prompted users to answer y or Y or yes or
891
+ # YES to this ask() resulting in an unintended URL of y. Special case
892
+ # this common error
893
+ url = nil if YES_SET.member? url
894
+ end
895
+
896
+ url ||= default_url
897
+
898
+ if ignore_framework
899
+ framework = VMC::Cli::Framework.new
900
+ elsif f = info(:framework)
901
+ info = Hash[f["info"].collect { |k, v| [k.to_sym, v] }]
902
+
903
+ framework = VMC::Cli::Framework.new(f["name"], info)
904
+ exec = framework.exec if framework && framework.exec
905
+ else
906
+ framework = detect_framework(prompt_ok)
907
+ end
908
+
909
+ err "Application Type undetermined for path '#{@application}'" unless framework
910
+
911
+ if memswitch
912
+ mem = memswitch
913
+ elsif prompt_ok
914
+ mem = ask("Memory Reservation",
915
+ :default => framework.memory, :choices => mem_choices)
916
+ else
917
+ mem = framework.memory
918
+ end
919
+
920
+ # Set to MB number
921
+ mem_quota = mem_choice_to_quota(mem)
922
+
923
+ # check memsize here for capacity
924
+ check_has_capacity_for(mem_quota * instances) unless no_start
925
+
926
+ display 'Creating Application: ', false
927
+
928
+ manifest = {
929
+ :name => "#{appname}",
930
+ :staging => {
931
+ :framework => framework.name,
932
+ :runtime => info(:runtime)
933
+ },
934
+ :uris => Array(url),
935
+ :instances => instances,
936
+ :resources => {
937
+ :memory => mem_quota
938
+ },
939
+ }
940
+
941
+ # Send the manifest to the cloud controller
942
+ client.create_app(appname, manifest)
943
+ display 'OK'.green
944
+
945
+
946
+ existing = Set.new(client.services.collect { |s| s[:name] })
947
+
948
+ if @app_info && services = @app_info["services"]
949
+ services.each do |name, info|
950
+ unless existing.include? name
951
+ create_service_banner(info["type"], name, true)
952
+ end
953
+
954
+ bind_service_banner(name, appname)
955
+ end
956
+ end
957
+
958
+ # Stage and upload the app bits.
959
+ upload_app_bits(appname, @application)
960
+
961
+ start(appname, true) unless no_start
962
+ end
963
+
964
+ def do_stats(appname)
965
+ stats = client.app_stats(appname)
966
+ return display JSON.pretty_generate(stats) if @options[:json]
967
+
968
+ stats_table = table do |t|
969
+ t.headings = 'Instance', 'CPU (Cores)', 'Memory (limit)', 'Disk (limit)', 'Uptime'
970
+ stats.each do |entry|
971
+ index = entry[:instance]
972
+ stat = entry[:stats]
973
+ hp = "#{stat[:host]}:#{stat[:port]}"
974
+ uptime = uptime_string(stat[:uptime])
975
+ usage = stat[:usage]
976
+ if usage
977
+ cpu = usage[:cpu]
978
+ mem = (usage[:mem] * 1024) # mem comes in K's
979
+ disk = usage[:disk]
980
+ end
981
+ mem_quota = stat[:mem_quota]
982
+ disk_quota = stat[:disk_quota]
983
+ mem = "#{pretty_size(mem)} (#{pretty_size(mem_quota, 0)})"
984
+ disk = "#{pretty_size(disk)} (#{pretty_size(disk_quota, 0)})"
985
+ cpu = cpu ? cpu.to_s : 'NA'
986
+ cpu = "#{cpu}% (#{stat[:cores]})"
987
+ t << [index, cpu, mem, disk, uptime]
988
+ end
989
+ end
990
+
991
+ if stats.empty?
992
+ display "No running instances for [#{appname}]".yellow
993
+ else
994
+ display stats_table
995
+ end
996
+ end
997
+
998
+ def all_files(appname, path)
999
+ instances_info_envelope = client.app_instances(appname)
1000
+ return if instances_info_envelope.is_a?(Array)
1001
+ instances_info = instances_info_envelope[:instances] || []
1002
+ instances_info.each do |entry|
1003
+ begin
1004
+ content = client.app_files(appname, path, entry[:index])
1005
+ display_logfile(
1006
+ path,
1007
+ content,
1008
+ entry[:index],
1009
+ "====> [#{entry[:index]}: #{path}] <====\n".bold
1010
+ )
1011
+ rescue VMC::Client::NotFound
1012
+ end
1013
+ end
1014
+ end
981
1015
  end
982
1016
 
983
1017
  class FileWithPercentOutput < ::File