vmc 0.5.0.beta.7 → 0.5.0.beta.10

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 (59) hide show
  1. data/LICENSE +1277 -746
  2. data/Rakefile +22 -23
  3. data/lib/vmc/cli.rb +21 -27
  4. data/lib/vmc/cli/app/push.rb +1 -0
  5. data/lib/vmc/cli/app/push/create.rb +3 -2
  6. data/lib/vmc/cli/app/push/sync.rb +11 -8
  7. data/lib/vmc/cli/app/scale.rb +19 -17
  8. data/lib/vmc/cli/domain/map.rb +55 -0
  9. data/lib/vmc/cli/domain/unmap.rb +56 -0
  10. data/lib/vmc/cli/route/map.rb +74 -0
  11. data/lib/vmc/cli/route/unmap.rb +94 -0
  12. data/lib/vmc/cli/service/create.rb +4 -1
  13. data/lib/vmc/cli/start/base.rb +2 -2
  14. data/lib/vmc/cli/start/login.rb +5 -2
  15. data/lib/vmc/cli/start/target.rb +4 -3
  16. data/lib/vmc/cli/user/base.rb +19 -0
  17. data/lib/vmc/cli/user/passwd.rb +7 -19
  18. data/lib/vmc/cli/{start → user}/register.rb +12 -23
  19. data/lib/vmc/constants.rb +1 -1
  20. data/lib/vmc/test_support.rb +4 -0
  21. data/lib/vmc/test_support/command_helper.rb +38 -0
  22. data/{spec/support → lib/vmc/test_support}/common_input_examples.rb +0 -0
  23. data/lib/vmc/test_support/fake_home_dir.rb +16 -0
  24. data/lib/vmc/test_support/interact_helper.rb +29 -0
  25. data/lib/vmc/version.rb +1 -1
  26. data/spec/features/new_user_flow_spec.rb +43 -51
  27. data/spec/spec_helper.rb +24 -12
  28. data/spec/vmc/cli/app/instances_spec.rb +3 -8
  29. data/spec/vmc/cli/app/push/create_spec.rb +10 -7
  30. data/spec/vmc/cli/app/push_spec.rb +1 -1
  31. data/spec/vmc/cli/app/rename_spec.rb +1 -1
  32. data/spec/vmc/cli/app/scale_spec.rb +81 -0
  33. data/spec/vmc/cli/app/stats_spec.rb +3 -7
  34. data/spec/vmc/cli/domain/map_spec.rb +140 -0
  35. data/spec/vmc/cli/domain/unmap_spec.rb +73 -0
  36. data/spec/vmc/cli/organization/orgs_spec.rb +13 -16
  37. data/spec/vmc/cli/organization/rename_spec.rb +1 -1
  38. data/spec/vmc/cli/route/map_spec.rb +142 -0
  39. data/spec/vmc/cli/route/unmap_spec.rb +215 -0
  40. data/spec/vmc/cli/service/rename_spec.rb +1 -1
  41. data/spec/vmc/cli/space/rename_spec.rb +15 -18
  42. data/spec/vmc/cli/space/spaces_spec.rb +18 -25
  43. data/spec/vmc/cli/start/info_spec.rb +44 -46
  44. data/spec/vmc/cli/start/login_spec.rb +40 -0
  45. data/spec/vmc/cli/user/create_spec.rb +54 -0
  46. data/spec/vmc/cli/user/passwd_spec.rb +7 -14
  47. data/spec/vmc/cli/{start → user}/register_spec.rb +26 -22
  48. data/spec/vmc/cli_spec.rb +164 -6
  49. metadata +46 -39
  50. data/lib/vmc/cli/app/routes.rb +0 -100
  51. data/lib/vmc/cli/domain/add_domain.rb +0 -25
  52. data/lib/vmc/cli/domain/create_domain.rb +0 -28
  53. data/lib/vmc/cli/domain/delete_domain.rb +0 -56
  54. data/lib/vmc/cli/domain/remove_domain.rb +0 -28
  55. data/lib/vmc/cli/route/create_route.rb +0 -49
  56. data/lib/vmc/cli/route/delete.rb +0 -47
  57. data/spec/support/feature_helpers.rb +0 -16
  58. data/spec/support/interact_helpers.rb +0 -27
  59. data/spec/vmc/cli/route/delete_route_spec.rb +0 -162
data/Rakefile CHANGED
@@ -1,39 +1,38 @@
1
1
  require "rake"
2
+ require "rspec/core/rake_task"
2
3
 
3
4
  $LOAD_PATH.unshift File.expand_path("../lib", __FILE__)
4
5
  require "vmc/version"
5
6
 
7
+ RSpec::Core::RakeTask.new(:spec)
6
8
  task :default => :spec
7
9
 
8
- desc "Run specs"
9
- task :spec => "bundler:install" do
10
- sh("rspec")
11
- end
12
-
13
- namespace :bundler do
14
- desc "Install bundler and gems"
15
- task "install" do
16
- sh("(gem list --local bundler | grep bundler || gem install bundler) && (bundle check || bundle install)")
10
+ namespace :deploy do
11
+ def last_staging_sha
12
+ `git rev-parse latest-staging`.strip
17
13
  end
18
- end
19
14
 
20
- namespace :gem do
21
- desc "Build Gem"
22
- task :build do
23
- sh "gem build vmc.gemspec"
15
+ def last_release_sha
16
+ `git rev-parse latest-release`.strip
24
17
  end
25
18
 
26
- desc "Install Gem"
27
- task :install => :build do
28
- sh "gem install --local vmc-#{VMC::VERSION}"
29
- sh "rm vmc-#{VMC::VERSION}.gem"
19
+ def last_staging_ref_was_released?
20
+ last_staging_sha == last_release_sha
30
21
  end
31
22
 
32
- desc "Uninstall Gem"
33
- task :uninstall do
34
- sh "gem uninstall vmc"
23
+ task :staging, :version do |_, args|
24
+ sh "gem bump --push #{"--version #{args.version}" if args.version}" if last_staging_ref_was_released?
25
+ sh "git tag -f latest-staging"
26
+ sh "git push origin :latest-staging"
27
+ sh "git push origin latest-staging"
35
28
  end
36
29
 
37
- desc "Reinstall Gem"
38
- task :reinstall => [:uninstall, :install]
30
+ task :gem do
31
+ sh "git fetch"
32
+ sh "git checkout #{last_staging_sha}"
33
+ sh "gem release --tag"
34
+ sh "git tag -f latest-release"
35
+ sh "git push origin :latest-release"
36
+ sh "git push origin latest-release"
37
+ end
39
38
  end
@@ -73,7 +73,7 @@ module VMC
73
73
  if force?
74
74
  fail "Please log in with 'vmc login'."
75
75
  else
76
- line c("Please log in with 'vmc login'.", :warning)
76
+ line c("Please log in first to proceed.", :warning)
77
77
  line
78
78
  invoke :login
79
79
  invalidate_client
@@ -224,12 +224,7 @@ module VMC
224
224
  end
225
225
 
226
226
  def err(msg, status = 1)
227
- if quiet?
228
- $stderr.puts(msg)
229
- else
230
- puts c(msg, :error)
231
- end
232
-
227
+ $stderr.puts c(msg, :error)
233
228
  exit_status status
234
229
  end
235
230
 
@@ -282,6 +277,7 @@ module VMC
282
277
  end
283
278
 
284
279
  def client_target
280
+ check_target
285
281
  File.read(target_file).chomp
286
282
  end
287
283
 
@@ -304,25 +300,29 @@ module VMC
304
300
  new_toks = File.expand_path(VMC::TOKENS_FILE)
305
301
  old_toks = File.expand_path(VMC::OLD_TOKENS_FILE)
306
302
 
307
- if File.exist? new_toks
303
+ info = if File.exist? new_toks
308
304
  YAML.load_file(new_toks)
309
305
  elsif File.exist? old_toks
310
306
  MultiJson.load(File.read(old_toks))
311
307
  else
312
308
  {}
313
309
  end
314
- end
315
310
 
316
- def target_info(target = client_target)
317
- info = targets_info[target]
311
+ normalize_targets_info(info)
312
+ end
318
313
 
319
- if info.is_a? String
320
- { :token => info }
321
- else
322
- info || {}
314
+ def normalize_targets_info(info_by_url)
315
+ info_by_url.reduce({}) do |hash, pair|
316
+ key, value = pair
317
+ hash[key] = value.is_a?(String) ? {:token => value } : value
318
+ hash
323
319
  end
324
320
  end
325
321
 
322
+ def target_info(target = client_target)
323
+ targets_info[target] || {}
324
+ end
325
+
326
326
  def save_targets(ts)
327
327
  ensure_config_dir
328
328
 
@@ -348,7 +348,7 @@ module VMC
348
348
  end
349
349
 
350
350
  def v2?
351
- client.is_a?(CFoundry::V2::Client)
351
+ client.version == 2
352
352
  end
353
353
 
354
354
  def invalidate_client
@@ -360,15 +360,16 @@ module VMC
360
360
  return @@client if defined?(@@client) && @@client
361
361
 
362
362
  info = target_info(target)
363
+ token = info[:token] && CFoundry::AuthToken.from_hash(info)
363
364
 
364
365
  @@client =
365
366
  case info[:version]
366
367
  when 2
367
- CFoundry::V2::Client.new(target, info[:token])
368
+ CFoundry::V2::Client.new(target, token)
368
369
  when 1
369
- CFoundry::V1::Client.new(target, info[:token])
370
+ CFoundry::V1::Client.new(target, token)
370
371
  else
371
- CFoundry::Client.new(target, info[:token])
372
+ CFoundry::Client.new(target, token)
372
373
  end
373
374
 
374
375
  @@client.proxy = input[:proxy]
@@ -378,14 +379,7 @@ module VMC
378
379
  @@client.log = File.expand_path("#{LOGS_DIR}/#{uri.host}.log")
379
380
 
380
381
  unless info.key? :version
381
- info[:version] =
382
- case @@client
383
- when CFoundry::V2::Client
384
- 2
385
- else
386
- 1
387
- end
388
-
382
+ info[:version] = @@client.version
389
383
  save_target_info(info, target)
390
384
  end
391
385
 
@@ -21,6 +21,7 @@ module VMC::App
21
21
  input :plan, :desc => "Application plan", :default => "D100"
22
22
  input :start, :desc => "Start app after pushing?", :default => true
23
23
  input :restart, :desc => "Restart app after updating?", :default => true
24
+ input :buildpack, :desc => "Custom buildpack URL", :default => nil
24
25
  input :create_services, :desc => "Interactively create services?",
25
26
  :type => :boolean, :default => proc { force? ? false : interact }
26
27
  input :bind_services, :desc => "Interactively bind services?",
@@ -14,6 +14,7 @@ module VMC::App
14
14
  inputs[:framework] = framework = determine_framework
15
15
  inputs[:command] = input[:command] if can_have_custom_start_command?(framework)
16
16
  inputs[:runtime] = determine_runtime(framework)
17
+ inputs[:buildpack] = input[:buildpack] if v2?
17
18
 
18
19
  human_mb = human_mb(detector.suggested_memory(framework) || 64)
19
20
  inputs[:memory] = megabytes(input[:memory, human_mb])
@@ -22,7 +23,7 @@ module VMC::App
22
23
  end
23
24
 
24
25
  def determine_framework
25
- return input[:framework] if input.given?(:framework)
26
+ return input[:framework] if input.has?(:framework)
26
27
 
27
28
  if (detected_framework = detector.detect_framework)
28
29
  input[:framework, [detected_framework], detected_framework, :other]
@@ -32,7 +33,7 @@ module VMC::App
32
33
  end
33
34
 
34
35
  def determine_runtime(framework)
35
- return input[:runtime] if input.given?(:runtime)
36
+ return input[:runtime] if input.has?(:runtime)
36
37
 
37
38
  detected_runtimes =
38
39
  if framework.name == "standalone"
@@ -1,12 +1,13 @@
1
1
  module VMC::App
2
2
  module Sync
3
3
  def apply_changes(app)
4
- app.memory = megabytes(input[:memory]) if input.given?(:memory)
5
- app.total_instances = input[:instances] if input.given?(:instances)
6
- app.command = input[:command] if input.given?(:command)
7
- app.production = input[:plan].upcase.start_with?("P") if input.given?(:plan)
8
- app.framework = input[:framework] if input.given?(:framework)
9
- app.runtime = input[:runtime] if input.given?(:runtime)
4
+ app.memory = megabytes(input[:memory]) if input.has?(:memory)
5
+ app.total_instances = input[:instances] if input.has?(:instances)
6
+ app.command = input[:command] if input.has?(:command)
7
+ app.production = input[:plan].upcase.start_with?("P") if input.has?(:plan)
8
+ app.framework = input[:framework] if input.has?(:framework)
9
+ app.runtime = input[:runtime] if input.has?(:runtime)
10
+ app.buildpack = input[:buildpack] if input.has?(:buildpack)
10
11
  end
11
12
 
12
13
  def display_changes(app)
@@ -14,8 +15,10 @@ module VMC::App
14
15
 
15
16
  line "Changes:"
16
17
 
17
- app.changes.each do |attr, (old, new)|
18
- line "#{c(attr, :name)}: #{diff_str(attr, old)} -> #{diff_str(attr, new)}"
18
+ indented do
19
+ app.changes.each do |attr, (old, new)|
20
+ line "#{c(attr, :name)}: #{diff_str(attr, old)} -> #{diff_str(attr, new)}"
21
+ end
19
22
  end
20
23
  end
21
24
 
@@ -6,51 +6,53 @@ module VMC::App
6
6
  group :apps, :info, :hidden => true
7
7
  input :app, :desc => "Application to update", :argument => true,
8
8
  :from_given => by_name(:app)
9
- input :instances, :desc => "Number of instances to run", :type => :numeric
9
+ input :instances, :desc => "Number of instances to run",
10
+ :type => :numeric
10
11
  input :memory, :desc => "Memory limit"
12
+ input :disk, :desc => "Disk quota"
11
13
  input :plan, :desc => "Application plan", :default => "D100"
12
14
  input :restart, :desc => "Restart app after updating?", :default => true
13
15
  def scale
14
16
  app = input[:app]
15
17
 
16
- if input.given?(:instances)
18
+ if input.has?(:instances)
17
19
  instances = input[:instances, app.total_instances]
18
20
  end
19
21
 
20
- if input.given?(:memory)
22
+ if input.has?(:memory)
21
23
  memory = input[:memory, app.memory]
22
24
  end
23
25
 
24
- if input.given?(:plan)
26
+ if input.has?(:disk)
27
+ disk = input[:disk, human_mb(app.disk_quota)]
28
+ end
29
+
30
+ if input.has?(:plan)
25
31
  fail "Plans not supported on target cloud." unless v2?
26
32
 
27
33
  plan_name = input[:plan]
28
34
  production = !!(plan_name =~ /^p/i)
29
35
  end
30
36
 
31
- unless instances || memory || plan_name
37
+ unless instances || memory || disk || plan_name
32
38
  instances = input[:instances, app.total_instances]
33
39
  memory = input[:memory, app.memory]
34
40
  end
35
41
 
36
- memory = megabytes(memory) if memory
42
+ app.total_instances = instances if input.has?(:instances)
43
+ app.memory = megabytes(memory) if input.has?(:memory)
44
+ app.disk_quota = megabytes(disk) if input.has?(:disk)
45
+ app.production = production if input.has?(:plan)
37
46
 
38
- instances_changed = instances && instances != app.total_instances
39
- memory_changed = memory && memory != app.memory
40
- plan_changed = plan_name && production != app.production
41
-
42
- unless memory_changed || instances_changed || plan_changed
43
- fail "No changes!"
44
- end
47
+ fail "No changes!" unless app.changed?
45
48
 
46
49
  with_progress("Scaling #{c(app.name, :name)}") do
47
- app.total_instances = instances if instances_changed
48
- app.memory = memory if memory_changed
49
- app.production = production if plan_changed
50
50
  app.update!
51
51
  end
52
52
 
53
- if memory_changed && app.started? && input[:restart]
53
+ needs_restart = app.changes.key?(:memory) || app.changes.key?(:disk_quota)
54
+
55
+ if needs_restart && app.started? && input[:restart]
54
56
  invoke :restart, :app => app
55
57
  end
56
58
  end
@@ -0,0 +1,55 @@
1
+ require "vmc/cli/domain/base"
2
+
3
+ module VMC::Domain
4
+ class Map < Base
5
+ desc "Map a domain to an organization or space"
6
+ group :domains
7
+ input :name, :desc => "Domain to map", :argument => :required
8
+ input :organization, :desc => "Organization to map the domain to",
9
+ :aliases => %w{--org -o},
10
+ :default => proc { client.current_organization },
11
+ :from_given => by_name(:organization)
12
+ input :space, :desc => "Space to map the domain to",
13
+ :default => proc { client.current_space },
14
+ :from_given => by_name(:space)
15
+ input :shared, :desc => "Create a shared domain", :default => false
16
+ def map_domain
17
+ domain = client.domain_by_name(input[:name])
18
+
19
+ given_org = input.has?(:organization)
20
+ given_space = input.has?(:space)
21
+
22
+ org = input[:organization]
23
+ space = input[:space]
24
+
25
+ given_space = true unless given_org || given_space
26
+
27
+ unless domain
28
+ domain = client.domain
29
+ domain.name = input[:name]
30
+ domain.owning_organization = org unless input[:shared]
31
+ domain.wildcard = true
32
+
33
+ with_progress("Creating domain #{c(domain.name, :name)}") do
34
+ domain.create!
35
+ org.add_domain(domain) if org && !given_org && !given_space
36
+ end
37
+ end
38
+
39
+ if given_space
40
+ add_domain(domain, space.organization)
41
+ add_domain(domain, space)
42
+ elsif given_org
43
+ add_domain(domain, org)
44
+ end
45
+ end
46
+
47
+ private
48
+
49
+ def add_domain(domain, target)
50
+ with_progress("Mapping #{c(domain.name, :name)} to #{c(target.name, :name)}") do
51
+ target.add_domain(domain)
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,56 @@
1
+ require "vmc/cli/domain/base"
2
+
3
+ module VMC::Domain
4
+ class Unmap < Base
5
+ desc "Unmap a domain from an organization or space"
6
+ group :domains
7
+ input :domain, :desc => "Domain to unmap", :argument => :required,
8
+ :from_given => by_name("domain")
9
+ input :organization, :desc => "Organization to unmap the domain from",
10
+ :aliases => %w{--org -o},
11
+ :default => proc { client.current_organization },
12
+ :from_given => by_name(:organization)
13
+ input :space, :desc => "Space to unmap the domain from",
14
+ :default => proc { client.current_space },
15
+ :from_given => by_name(:space)
16
+ input :delete, :desc => "Delete domain", :type => :boolean
17
+ input :really, :type => :boolean, :forget => true, :hidden => true,
18
+ :default => proc { force? || interact }
19
+ def unmap_domain
20
+ domain = input[:domain]
21
+
22
+ given_org = input.has?(:organization)
23
+ given_space = input.has?(:space)
24
+
25
+ org = input[:organization]
26
+ space = input[:space]
27
+
28
+ if input[:delete]
29
+ return unless input[:really, domain.name, :name]
30
+
31
+ with_progress("Deleting domain #{c(domain.name, :name)}") do
32
+ domain.delete!
33
+ end
34
+
35
+ return
36
+ end
37
+
38
+ given_space = true unless given_org || given_space
39
+
40
+ remove_domain(domain, space) if given_space
41
+ remove_domain(domain, org) if given_org
42
+ end
43
+
44
+ private
45
+
46
+ def remove_domain(domain, target)
47
+ with_progress("Unmapping #{c(domain.name, :name)} from #{c(target.name, :name)}") do
48
+ target.remove_domain(domain)
49
+ end
50
+ end
51
+
52
+ def ask_really(name, color)
53
+ ask("Really delete #{c(name, color)}?", :default => false)
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,74 @@
1
+ require "vmc/cli/route/base"
2
+
3
+ module VMC::Route
4
+ class Map < Base
5
+ def precondition; end
6
+
7
+ desc "Add a URL mapping"
8
+ group :apps, :info, :hidden => true
9
+ input :url, :desc => "URL to map", :argument => true
10
+ input :app, :desc => "Application to add the URL to", :argument => :optional,
11
+ :from_given => by_name(:app)
12
+ input :space, :desc => "Space to add the URL to",
13
+ :from_given => by_name(:space)
14
+ def map
15
+ if input.has?(:space)
16
+ space = input[:space]
17
+ else
18
+ app = input[:app]
19
+ space = app.space if v2?
20
+ end
21
+
22
+ url = input[:url].sub(/^https?:\/\/(.*)\/?/i, '\1')
23
+
24
+ if v2?
25
+ host, domain_name = url.split(".", 2)
26
+ domain = find_domain(space, domain_name)
27
+ route = find_or_create_route(domain, host, space)
28
+ bind_route(route, app) if app
29
+ else
30
+ with_progress("Updating #{c(app.name, :name)}") do
31
+ app.urls << url
32
+ app.update!
33
+ end
34
+ end
35
+ end
36
+
37
+ private
38
+
39
+ def bind_route(route, app)
40
+ with_progress("Binding #{c(route.name, :name)} to #{c(app.name, :name)}") do
41
+ app.add_route(route)
42
+ end
43
+ end
44
+
45
+ def find_or_create_route(domain, host, space)
46
+ find_route(domain, host) || create_route(domain, host, space)
47
+ end
48
+
49
+ def find_route(domain, host)
50
+ client.routes_by_host(host, :depth => 0).find { |r| r.domain == domain }
51
+ end
52
+
53
+ def create_route(domain, host, space)
54
+ route = client.route
55
+ route.host = host
56
+ route.domain = domain
57
+ route.space = space
58
+
59
+ with_progress("Creating route #{c(route.name, :name)}") { route.create! }
60
+
61
+ route
62
+ end
63
+
64
+ def find_domain(space, name)
65
+ domain = space.domain_by_name(name, :depth => 0)
66
+ fail "Invalid domain '#{name}'" unless domain
67
+ domain
68
+ end
69
+
70
+ def ask_app
71
+ ask("Which application?", :choices => client.apps, :display => proc(&:name))
72
+ end
73
+ end
74
+ end