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.
- data/config/clients.yml +7 -5
- data/lib/cli/commands/admin.rb +2 -2
- data/lib/cli/commands/apps.rb +414 -380
- data/lib/cli/commands/base.rb +162 -9
- data/lib/cli/commands/manifest.rb +56 -0
- data/lib/cli/commands/services.rb +5 -6
- data/lib/cli/commands/user.rb +1 -1
- data/lib/cli/config.rb +17 -9
- data/lib/cli/manifest_helper.rb +238 -0
- data/lib/cli/runner.rb +24 -10
- data/lib/cli/tunnel_helper.rb +36 -11
- data/lib/cli/version.rb +1 -1
- data/lib/cli.rb +2 -0
- data/lib/vmc/client.rb +29 -19
- data/lib/vmc/const.rb +7 -7
- metadata +29 -13
data/lib/cli/commands/apps.rb
CHANGED
@@ -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
|
43
|
-
|
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
|
-
|
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
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
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
|
-
|
98
|
-
|
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
|
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
|
-
|
329
|
-
|
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
|
-
|
358
|
-
|
359
|
-
|
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
|
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
|
-
|
387
|
-
|
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
|
-
|
390
|
-
|
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
|
-
|
400
|
-
|
401
|
-
|
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
|
-
|
406
|
-
|
407
|
-
|
408
|
-
|
409
|
-
|
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
|
-
|
424
|
-
|
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
|
-
|
460
|
-
|
461
|
-
|
462
|
-
|
463
|
-
|
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
|
-
|
471
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|