vmc 0.3.15 → 0.3.16.beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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