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

Sign up to get free protection for your applications and to get access to all the features.
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