vmc 0.3.13.beta.2 → 0.3.13.beta.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. data/Rakefile +84 -2
  2. data/lib/cli.rb +2 -3
  3. data/lib/cli/commands/admin.rb +9 -6
  4. data/lib/cli/commands/apps.rb +144 -111
  5. data/lib/cli/commands/base.rb +8 -15
  6. data/lib/cli/commands/misc.rb +3 -2
  7. data/lib/cli/commands/services.rb +15 -16
  8. data/lib/cli/commands/user.rb +11 -2
  9. data/lib/cli/config.rb +5 -1
  10. data/lib/cli/core_ext.rb +331 -0
  11. data/lib/cli/frameworks.rb +10 -4
  12. data/lib/cli/runner.rb +4 -0
  13. data/lib/cli/services_helper.rb +8 -2
  14. data/lib/cli/usage.rb +4 -3
  15. data/lib/cli/version.rb +1 -1
  16. data/lib/vmc/client.rb +4 -0
  17. data/lib/vmc/const.rb +1 -0
  18. metadata +13 -46
  19. data/spec/assets/app_info.txt +0 -9
  20. data/spec/assets/app_listings.txt +0 -9
  21. data/spec/assets/bad_create_app.txt +0 -9
  22. data/spec/assets/delete_app.txt +0 -9
  23. data/spec/assets/global_service_listings.txt +0 -9
  24. data/spec/assets/good_create_app.txt +0 -9
  25. data/spec/assets/good_create_service.txt +0 -9
  26. data/spec/assets/info_authenticated.txt +0 -27
  27. data/spec/assets/info_return.txt +0 -15
  28. data/spec/assets/info_return_bad.txt +0 -16
  29. data/spec/assets/list_users.txt +0 -13
  30. data/spec/assets/login_fail.txt +0 -9
  31. data/spec/assets/login_success.txt +0 -9
  32. data/spec/assets/sample_token.txt +0 -1
  33. data/spec/assets/service_already_exists.txt +0 -9
  34. data/spec/assets/service_gateway_fail.txt +0 -9
  35. data/spec/assets/service_listings.txt +0 -9
  36. data/spec/assets/service_not_found.txt +0 -9
  37. data/spec/assets/user_info.txt +0 -9
  38. data/spec/spec_helper.rb +0 -11
  39. data/spec/unit/cli_opts_spec.rb +0 -68
  40. data/spec/unit/client_spec.rb +0 -345
data/Rakefile CHANGED
@@ -2,8 +2,7 @@ require 'rake'
2
2
  require 'spec/rake/spectask'
3
3
 
4
4
  desc "Run specs"
5
- task :spec do
6
- sh('bundle install')
5
+ task :spec => :build do
7
6
  Spec::Rake::SpecTask.new('spec') do |t|
8
7
  t.spec_opts = %w(-fs -c)
9
8
  t.spec_files = FileList['spec/**/*_spec.rb']
@@ -15,3 +14,86 @@ task :test => :spec
15
14
  desc "Synonym for spec"
16
15
  task :tests => :spec
17
16
  task :default => :spec
17
+
18
+ def tests_path
19
+ if @tests_path == nil
20
+ @tests_path = File.join(Dir.pwd, "spec/assets/tests")
21
+ end
22
+ @tests_path
23
+ end
24
+ TESTS_PATH = tests_path
25
+
26
+ BUILD_ARTIFACT = File.join(Dir.pwd, "spec/assets/.build")
27
+
28
+ TESTS_TO_BUILD = ["#{TESTS_PATH}/java_web/java_tiny_app",
29
+ # "#{TESTS_PATH}/grails/guestbook",
30
+ "#{TESTS_PATH}/lift/hello_lift",
31
+ "#{TESTS_PATH}/spring/roo-guestbook",
32
+ "#{TESTS_PATH}/spring/spring-osgi-hello",
33
+ ]
34
+
35
+ desc "Build the tests. If the git hash associated with the test assets has not changed, nothing is built. To force a build, invoke 'rake build[--force]'"
36
+ task :build, [:force] do |t, args|
37
+ sh('bundle install')
38
+ sh('git submodule update --init')
39
+ puts "\nBuilding tests"
40
+ if build_required? args.force
41
+ ENV['MAVEN_OPTS']="-XX:MaxPermSize=256M"
42
+ TESTS_TO_BUILD.each do |test|
43
+ puts "\tBuilding '#{test}'"
44
+ Dir.chdir test do
45
+ sh('mvn package -DskipTests') do |success, exit_code|
46
+ unless success
47
+ clear_build_artifact
48
+ do_mvn_clean('-q')
49
+ fail "\tFailed to build #{test} - aborting build"
50
+ end
51
+ end
52
+ end
53
+ puts "\tCompleted building '#{test}'"
54
+ end
55
+ save_git_hash
56
+ else
57
+ puts "Built artifacts in sync with test assets - no build required"
58
+ end
59
+ end
60
+
61
+ desc "Clean the build artifacts"
62
+ task :clean do
63
+ puts "\nCleaning tests"
64
+ clear_build_artifact
65
+ TESTS_TO_BUILD.each do |test|
66
+ puts "\tCleaning '#{test}'"
67
+ Dir.chdir test do
68
+ do_mvn_clean
69
+ end
70
+ puts "\tCompleted cleaning '#{test}'"
71
+ end
72
+ end
73
+
74
+ def build_required? (force_build=nil)
75
+ if File.exists?(BUILD_ARTIFACT) == false or (force_build and force_build == "--force")
76
+ return true
77
+ end
78
+ Dir.chdir(tests_path) do
79
+ saved_git_hash = IO.readlines(BUILD_ARTIFACT)[0].split[0]
80
+ git_hash = `git rev-parse --short=8 --verify HEAD`
81
+ saved_git_hash.to_s.strip != git_hash.to_s.strip
82
+ end
83
+ end
84
+
85
+ def save_git_hash
86
+ Dir.chdir(tests_path) do
87
+ git_hash = `git rev-parse --short=8 --verify HEAD`
88
+ File.open(BUILD_ARTIFACT, 'w') {|f| f.puts("#{git_hash}")}
89
+ end
90
+ end
91
+
92
+ def clear_build_artifact
93
+ puts "\tClearing build artifact #{BUILD_ARTIFACT}"
94
+ File.unlink BUILD_ARTIFACT if File.exists? BUILD_ARTIFACT
95
+ end
96
+
97
+ def do_mvn_clean options=nil
98
+ sh("mvn clean #{options}")
99
+ end
data/lib/cli.rb CHANGED
@@ -1,12 +1,11 @@
1
-
2
1
  ROOT = File.expand_path(File.dirname(__FILE__))
2
+ WINDOWS = !!(RUBY_PLATFORM =~ /mingw|mswin32|cygwin/)
3
3
 
4
- module VMC
5
4
 
5
+ module VMC
6
6
  autoload :Client, "#{ROOT}/vmc/client"
7
7
 
8
8
  module Cli
9
-
10
9
  autoload :Config, "#{ROOT}/cli/config"
11
10
  autoload :Framework, "#{ROOT}/cli/frameworks"
12
11
  autoload :Runner, "#{ROOT}/cli/runner"
@@ -22,12 +22,12 @@ module VMC::Cli::Command
22
22
  alias :users :list_users
23
23
 
24
24
  def add_user(email=nil)
25
- email = @options[:email] unless email
25
+ email ||= @options[:email]
26
+ email ||= ask("Email") unless no_prompt
26
27
  password = @options[:password]
27
- email = ask("Email: ") unless no_prompt || email
28
28
  unless no_prompt || password
29
- password = ask("Password: ") {|q| q.echo = '*'}
30
- password2 = ask("Verify Password: ") {|q| q.echo = '*'}
29
+ password = ask("Password", :echo => "*")
30
+ password2 = ask("Verify Password", :echo => "*")
31
31
  err "Passwords did not match, try again" if password != password2
32
32
  end
33
33
  err "Need a valid email" unless email
@@ -53,8 +53,11 @@ module VMC::Cli::Command
53
53
 
54
54
  if (apps && !apps.empty?)
55
55
  unless no_prompt
56
- proceed = ask("\nDeployed applications and associated services will be DELETED, continue? [yN]: ")
57
- err "Aborted" if proceed.upcase != 'Y'
56
+ proceed = ask(
57
+ "\nDeployed applications and associated services will be DELETED, continue?",
58
+ :default => false
59
+ )
60
+ err "Aborted" unless proceed
58
61
  end
59
62
  cmd = Apps.new(@options)
60
63
  apps.each { |app| cmd.delete_app(app[:name], true) }
@@ -44,6 +44,28 @@ module VMC::Cli::Command
44
44
  return display "Application '#{appname}' could not be found".red if app.nil?
45
45
  return display "Application '#{appname}' already started".yellow if app[:state] == 'STARTED'
46
46
 
47
+ if @options[:debug]
48
+ runtimes = client.runtimes_info
49
+ return display "Cannot get runtime information." unless runtimes
50
+
51
+ runtime = runtimes[app[:staging][:stack].to_sym]
52
+ return display "Unknown runtime." unless runtime
53
+
54
+ unless runtime[:debug_modes] and runtime[:debug_modes].include? @options[:debug]
55
+ modes = runtime[:debug_modes] || []
56
+
57
+ display "\nApplication '#{appname}' cannot start in '#{@options[:debug]}' mode"
58
+
59
+ if push
60
+ display "Try `vmc start' with one of the following modes: #{modes.inspect}"
61
+ else
62
+ display "Available modes: #{modes.inspect}"
63
+ end
64
+
65
+ return
66
+ end
67
+ end
68
+
47
69
  banner = 'Staging Application: '
48
70
  display banner, false
49
71
 
@@ -57,6 +79,7 @@ module VMC::Cli::Command
57
79
  end
58
80
 
59
81
  app[:state] = 'STARTED'
82
+ app[:debug] = @options[:debug]
60
83
  client.update_app(appname, app)
61
84
 
62
85
  Thread.kill(t)
@@ -79,10 +102,9 @@ module VMC::Cli::Command
79
102
  # Check for the existance of crashes
80
103
  display "\nError: Application [#{appname}] failed to start, logs information below.\n".red
81
104
  grab_crash_logs(appname, '0', true)
82
- if push
105
+ if push and !no_prompt
83
106
  display "\n"
84
- should_delete = ask 'Should I delete the application? (Y/n)? ' unless no_prompt
85
- delete_app(appname, false) unless no_prompt || should_delete.upcase == 'N'
107
+ delete_app(appname, false) if ask "Delete the application?", :default => true
86
108
  end
87
109
  failed = true
88
110
  break
@@ -130,14 +152,11 @@ module VMC::Cli::Command
130
152
  mem = current_mem = mem_quota_to_choice(app[:resources][:memory])
131
153
  memsize = normalize_mem(memsize) if memsize
132
154
 
133
- unless memsize
134
- choose do |menu|
135
- menu.layout = :one_line
136
- menu.prompt = "Update Memory Reservation? [Current:#{current_mem}] "
137
- menu.default = current_mem
138
- mem_choices.each { |choice| menu.choice(choice) { memsize = choice } }
139
- end
140
- end
155
+ memsize ||= ask(
156
+ "Update Memory Reservation?",
157
+ :default => current_mem,
158
+ :choices => mem_choices
159
+ )
141
160
 
142
161
  mem = mem_choice_to_quota(mem)
143
162
  memsize = mem_choice_to_quota(memsize)
@@ -184,11 +203,7 @@ module VMC::Cli::Command
184
203
  def delete(appname=nil)
185
204
  force = @options[:force]
186
205
  if @options[:all]
187
- should_delete = force && no_prompt ? 'Y' : 'N'
188
- unless no_prompt || force
189
- should_delete = ask 'Delete ALL Applications and Services? (y/N)? '
190
- end
191
- if should_delete.upcase == 'Y'
206
+ if no_prompt || force || ask("Delete ALL applications and services?", :default => false)
192
207
  apps = client.apps
193
208
  apps.each { |app| delete_app(app[:name], force) }
194
209
  end
@@ -206,25 +221,30 @@ module VMC::Cli::Command
206
221
  app_services.each { |service|
207
222
  del_service = force && no_prompt ? 'Y' : 'N'
208
223
  unless no_prompt || force
209
- apps_using_service = services_apps_hash[service].reject!{ |app| app == appname}
210
- if apps_using_service.size > 0
211
- del_service = ask("Provisioned service [#{service}] is being used by #{apps_using_service.entries}, would you still like to delete it? [yN]: ")
212
- else
213
- del_service = ask("Provisioned service [#{service}] detected, would you like to delete it? [yN]: ")
224
+ del_service = ask(
225
+ "Provisioned service [#{service}] detected, would you like to delete it?",
226
+ :default => false
227
+ )
228
+
229
+ if del_service
230
+ apps_using_service = services_apps_hash[service].reject!{ |app| app == appname}
231
+ if apps_using_service.size > 0
232
+ del_service = ask(
233
+ "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?",
234
+ :default => false
235
+ )
236
+ end
214
237
  end
215
238
  end
216
- services_to_delete << service if del_service.upcase == 'Y'
239
+ services_to_delete << service if del_service
217
240
  }
241
+
218
242
  display "Deleting application [#{appname}]: ", false
219
243
  client.delete_app(appname)
220
244
  display 'OK'.green
221
245
 
222
- unless services_to_delete.length == 0
223
- services_to_delete.each do |s|
224
- display "Deleting service [#{s}]: ", false
225
- client.delete_service(s)
226
- display 'OK'.green
227
- end
246
+ services_to_delete.each do |s|
247
+ delete_service_banner(s)
228
248
  end
229
249
  end
230
250
 
@@ -352,10 +372,10 @@ module VMC::Cli::Command
352
372
  no_start = @options[:nostart]
353
373
 
354
374
  path = @options[:path] || '.'
355
- appname = @options[:name] unless appname
356
- url = @options[:url]
375
+ appname ||= @options[:name]
357
376
  mem, memswitch = nil, @options[:mem]
358
377
  memswitch = normalize_mem(memswitch) if memswitch
378
+ url = @options[:url]
359
379
 
360
380
  # Check app existing upfront if we have appname
361
381
  app_checked = false
@@ -375,52 +395,57 @@ module VMC::Cli::Command
375
395
  end
376
396
 
377
397
  unless no_prompt || @options[:path]
378
- proceed = ask('Would you like to deploy from the current directory? [Yn]: ')
379
- if proceed.upcase == 'N'
380
- path = ask('Please enter in the deployment path: ')
398
+ unless ask('Would you like to deploy from the current directory?', :default => true)
399
+ path = ask('Please enter in the deployment path')
381
400
  end
382
401
  end
383
402
 
384
403
  path = File.expand_path(path)
385
404
  check_deploy_directory(path)
386
405
 
387
- appname = ask("Application Name: ") unless no_prompt || appname
406
+ appname ||= ask("Application Name") unless no_prompt
388
407
  err "Application Name required." if appname.nil? || appname.empty?
389
408
 
390
- unless app_checked
391
- err "Application '#{appname}' already exists, use update or delete." if app_exists?(appname)
409
+ if !app_checked and app_exists?(appname)
410
+ err "Application '#{appname}' already exists, use update or delete."
392
411
  end
393
412
 
413
+ default_url = "#{appname}.#{VMC::Cli::Config.suggest_url}"
414
+
394
415
  unless no_prompt || url
395
- url = ask("Application Deployed URL: '#{appname}.#{VMC::Cli::Config.suggest_url}'? ")
416
+ url = ask(
417
+ "Application Deployed URL",
418
+ :default => default_url
419
+ )
396
420
 
397
- # common error case is for prompted users to answer y or Y or yes or YES to this ask() resulting in an
398
- # unintended URL of y. Special case this common error
399
- if YES_SET.member?(url)
400
- #silently revert to the stock url
401
- url = "#{appname}.#{VMC::Cli::Config.suggest_url}"
402
- end
421
+ # common error case is for prompted users to answer y or Y or yes or
422
+ # YES to this ask() resulting in an unintended URL of y. Special case
423
+ # this common error
424
+ url = nil if YES_SET.member? url
403
425
  end
404
426
 
405
- url = "#{appname}.#{VMC::Cli::Config.suggest_url}" if url.nil? || url.empty?
427
+ url ||= default_url
406
428
 
407
429
  # Detect the appropriate framework.
408
430
  framework = nil
409
431
  unless ignore_framework
410
432
  framework = VMC::Cli::Framework.detect(path)
411
- framework_correct = ask("Detected a #{framework}, is this correct? [Yn]: ") if prompt_ok && framework
412
- framework_correct ||= 'y'
413
- if prompt_ok && (framework.nil? || framework_correct.upcase == 'N')
433
+
434
+ if prompt_ok and framework
435
+ framework_correct =
436
+ ask("Detected a #{framework}, is this correct?", :default => true)
437
+ end
438
+
439
+ if prompt_ok && (framework.nil? || !framework_correct)
414
440
  display "#{"[WARNING]".yellow} Can't determine the Application Type." unless framework
415
- framework = nil if framework_correct.upcase == 'N'
416
- choose do |menu|
417
- menu.layout = :one_line
418
- menu.prompt = "Select Application Type: "
419
- menu.default = framework
420
- VMC::Cli::Framework.known_frameworks.each do |f|
421
- menu.choice(f) { framework = VMC::Cli::Framework.lookup(f) }
422
- end
423
- end
441
+ framework = VMC::Cli::Framework.lookup(
442
+ ask(
443
+ "Select Application Type",
444
+ { :indexed => true,
445
+ :choices => VMC::Cli::Framework.known_frameworks
446
+ }
447
+ )
448
+ )
424
449
  display "Selected #{framework}"
425
450
  end
426
451
  # Framework override, deprecated
@@ -430,18 +455,14 @@ module VMC::Cli::Command
430
455
  end
431
456
 
432
457
  err "Application Type undetermined for path '#{path}'" unless framework
433
- unless memswitch
434
- mem = framework.memory
435
- if prompt_ok
436
- choose do |menu|
437
- menu.layout = :one_line
438
- menu.prompt = "Memory Reservation [Default:#{mem}] "
439
- menu.default = mem
440
- mem_choices.each { |choice| menu.choice(choice) { mem = choice } }
441
- end
442
- end
443
- else
458
+
459
+ if memswitch
444
460
  mem = memswitch
461
+ elsif prompt_ok
462
+ mem = ask("Memory Reservation",
463
+ :default => framework.memory, :choices => mem_choices)
464
+ else
465
+ mem = framework.memory
445
466
  end
446
467
 
447
468
  # Set to MB number
@@ -473,8 +494,8 @@ module VMC::Cli::Command
473
494
  unless no_prompt || @options[:noservices]
474
495
  services = client.services_info
475
496
  unless services.empty?
476
- proceed = ask("Would you like to bind any services to '#{appname}'? [yN]: ")
477
- bind_services(appname, services) if proceed.upcase == 'Y'
497
+ proceed = ask("Would you like to bind any services to '#{appname}'?", :default => false)
498
+ bind_services(appname, services) if proceed
478
499
  end
479
500
  end
480
501
 
@@ -648,57 +669,54 @@ module VMC::Cli::Command
648
669
 
649
670
  def choose_existing_service(appname, user_services)
650
671
  return unless prompt_ok
651
- selected = false
652
- choose do |menu|
653
- menu.header = "The following provisioned services are available"
654
- menu.prompt = 'Please select one you wish to provision: '
655
- menu.select_by = :index_or_name
656
- user_services.each do |s|
657
- menu.choice(s[:name]) do
658
- display "Binding Service: ", false
659
- client.bind_service(s[:name], appname)
660
- display 'OK'.green
661
- selected = true
662
- end
663
- end
664
- end
665
- selected
672
+
673
+ display "The following provisioned services are available"
674
+ name = ask(
675
+ "Please select one you which to prevision",
676
+ { :indexed => true,
677
+ :choices => user_services.collect { |s| s[:name] }
678
+ }
679
+ )
680
+
681
+ bind_service_banner(name, appname, false)
682
+
683
+ true
666
684
  end
667
685
 
668
686
  def choose_new_service(appname, services)
669
687
  return unless prompt_ok
670
- choose do |menu|
671
- menu.header = "The following system services are available"
672
- menu.prompt = 'Please select one you wish to provision: '
673
- menu.select_by = :index_or_name
674
- service_choices = []
675
- services.each do |service_type, value|
676
- value.each do |vendor, version|
677
- service_choices << vendor
678
- end
679
- end
680
- service_choices.sort! {|a, b| a.to_s <=> b.to_s }
681
- service_choices.each do |vendor|
682
- menu.choice(vendor) do
683
- default_name = random_service_name(vendor)
684
- service_name = ask("Specify the name of the service [#{default_name}]: ")
685
- service_name = default_name if service_name.empty?
686
- create_service_banner(vendor, service_name)
687
- bind_service_banner(service_name, appname)
688
- end
689
- end
690
- end
688
+
689
+ display "The following system services are available"
690
+
691
+ vendor = ask(
692
+ "Please select one you wish to provision",
693
+ { :indexed => true,
694
+ :choices =>
695
+ services.values.collect { |type|
696
+ type.keys.collect(&:to_s)
697
+ }.flatten.sort!
698
+ }
699
+ )
700
+
701
+ default_name = random_service_name(vendor)
702
+ service_name = ask("Specify the name of the service",
703
+ :default => default_name)
704
+
705
+ create_service_banner(vendor, service_name)
706
+ bind_service_banner(service_name, appname)
691
707
  end
692
708
 
693
709
  def bind_services(appname, services)
694
710
  user_services = client.services
711
+
695
712
  selected_existing = false
696
713
  unless no_prompt || user_services.empty?
697
- use_existing = ask "Would you like to use an existing provisioned service [yN]? "
698
- if use_existing.upcase == 'Y'
714
+ if ask("Would you like to use an existing provisioned service?",
715
+ :default => false)
699
716
  selected_existing = choose_existing_service(appname, user_services)
700
717
  end
701
718
  end
719
+
702
720
  # Create a new service and bind it here
703
721
  unless selected_existing
704
722
  choose_new_service(appname, services)
@@ -795,9 +813,19 @@ module VMC::Cli::Command
795
813
  return display "No running instances for [#{appname}]".yellow if instances_info.empty?
796
814
 
797
815
  instances_table = table do |t|
798
- t.headings = 'Index', 'State', 'Start Time'
816
+ show_debug = instances_info.any? { |e| e[:debug_port] }
817
+
818
+ headings = ['Index', 'State', 'Start Time']
819
+ headings << 'Debug IP' if show_debug
820
+ headings << 'Debug Port' if show_debug
821
+
822
+ t.headings = headings
823
+
799
824
  instances_info.each do |entry|
800
- t << [entry[:index], entry[:state], Time.at(entry[:since]).strftime("%m/%d/%Y %I:%M%p")]
825
+ row = [entry[:index], entry[:state], Time.at(entry[:since]).strftime("%m/%d/%Y %I:%M%p")]
826
+ row << entry[:debug_ip] if show_debug
827
+ row << entry[:debug_port] if show_debug
828
+ t << row
801
829
  end
802
830
  end
803
831
  display "\n"
@@ -849,12 +877,17 @@ module VMC::Cli::Command
849
877
  case health(app)
850
878
  when 'N/A'
851
879
  # Health manager not running.
852
- err "\Application '#{appname}'s state is undetermined, not enough information available." if error_on_health
880
+ err "\nApplication '#{appname}'s state is undetermined, not enough information available." if error_on_health
853
881
  return false
854
882
  when 'RUNNING'
855
883
  return true
856
884
  else
857
- return false
885
+ if app[:meta][:debug] == "suspend"
886
+ display "\nApplication [#{appname}] has started in a mode that is waiting for you to trigger startup."
887
+ return true
888
+ else
889
+ return false
890
+ end
858
891
  end
859
892
  end
860
893