vmc 0.5.0.beta.12 → 0.5.0.rc1

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.
@@ -63,7 +63,7 @@ module VMC
63
63
  end
64
64
 
65
65
  def check_target
66
- unless File.exists? target_file
66
+ unless client && client.target
67
67
  fail "Please select a target with 'vmc target'."
68
68
  end
69
69
  end
@@ -96,14 +96,8 @@ module VMC
96
96
  end
97
97
  end
98
98
 
99
- def execute(cmd, argv, global = {})
100
- if input[:help]
101
- invoke :help, :command => cmd.name.to_s
102
- else
103
- @command = cmd
104
- precondition
105
- super
106
- end
99
+ def wrap_errors
100
+ yield
107
101
  rescue CFoundry::Timeout => e
108
102
  err(e.message)
109
103
  rescue Interrupt
@@ -122,6 +116,8 @@ module VMC
122
116
  line
123
117
  line c("Not authenticated! Try logging in:", :warning)
124
118
 
119
+ # TODO: there's no color here; global flags not being passed
120
+ # through (mothership bug?)
125
121
  invoke :login
126
122
 
127
123
  retry
@@ -132,18 +128,52 @@ module VMC
132
128
  err "Denied: #{e.description}"
133
129
 
134
130
  rescue Exception => e
135
- ensure_config_dir
136
-
137
131
  log_error(e)
138
132
 
139
133
  msg = e.class.name
140
134
  msg << ": #{e}" unless e.to_s.empty?
141
135
  msg << "\nFor more information, see #{VMC::CRASH_FILE}"
142
136
  err msg
137
+
143
138
  raise if debug?
144
139
  end
145
140
 
141
+ def execute(cmd, argv, global = {})
142
+ if input[:help]
143
+ invoke :help, :command => cmd.name.to_s
144
+ else
145
+ wrap_errors do
146
+ @command = cmd
147
+ precondition
148
+
149
+ save_token_if_it_changes do
150
+ super
151
+ end
152
+ end
153
+ end
154
+ end
155
+
156
+ def save_token_if_it_changes
157
+ return yield unless client && client.token
158
+
159
+ before_token = client.token
160
+
161
+ yield
162
+
163
+ after_token = client.token
164
+
165
+ return unless after_token
166
+
167
+ if before_token != after_token
168
+ info = target_info
169
+ info[:token] = after_token.auth_header
170
+ save_target_info(info)
171
+ end
172
+ end
173
+
146
174
  def log_error(e)
175
+ ensure_config_dir
176
+
147
177
  msg = e.class.name
148
178
  msg << ": #{e}" unless e.to_s.empty?
149
179
 
@@ -277,8 +307,9 @@ module VMC
277
307
  end
278
308
 
279
309
  def client_target
280
- check_target
281
- File.read(target_file).chomp
310
+ if File.exists?(target_file)
311
+ File.read(target_file).chomp
312
+ end
282
313
  end
283
314
 
284
315
  def ensure_config_dir
@@ -300,13 +331,14 @@ module VMC
300
331
  new_toks = File.expand_path(VMC::TOKENS_FILE)
301
332
  old_toks = File.expand_path(VMC::OLD_TOKENS_FILE)
302
333
 
303
- info = if File.exist? new_toks
304
- YAML.load_file(new_toks)
305
- elsif File.exist? old_toks
306
- MultiJson.load(File.read(old_toks))
307
- else
308
- {}
309
- end
334
+ info =
335
+ if File.exist? new_toks
336
+ YAML.load_file(new_toks)
337
+ elsif File.exist? old_toks
338
+ MultiJson.load(File.read(old_toks))
339
+ end
340
+
341
+ info ||= {}
310
342
 
311
343
  normalize_targets_info(info)
312
344
  end
@@ -314,7 +346,7 @@ module VMC
314
346
  def normalize_targets_info(info_by_url)
315
347
  info_by_url.reduce({}) do |hash, pair|
316
348
  key, value = pair
317
- hash[key] = value.is_a?(String) ? {:token => value } : value
349
+ hash[key] = value.is_a?(String) ? { :token => value } : value
318
350
  hash
319
351
  end
320
352
  end
@@ -358,6 +390,7 @@ module VMC
358
390
 
359
391
  def client(target = client_target)
360
392
  return @@client if defined?(@@client) && @@client
393
+ return unless target
361
394
 
362
395
  info = target_info(target)
363
396
  token = info[:token] && CFoundry::AuthToken.from_hash(info)
@@ -392,6 +425,11 @@ module VMC
392
425
  end
393
426
 
394
427
  @@client
428
+ rescue CFoundry::InvalidTarget
429
+ end
430
+
431
+ def fail_unknown(display, name)
432
+ fail("Unknown #{display} '#{name}'.")
395
433
  end
396
434
 
397
435
  class << self
@@ -411,14 +449,14 @@ module VMC
411
449
  choices ||= instance_exec(&blk) if block_given?
412
450
 
413
451
  choices.find { |c| c.name == name } ||
414
- fail("Unknown #{display} '#{name}'.")
452
+ fail_unknown(display, name)
415
453
  }
416
454
  end
417
455
 
418
456
  def by_name(what, display = what)
419
457
  proc { |name, *_|
420
458
  client.send(:"#{what}_by_name", name) ||
421
- fail("Unknown #{display} '#{name}'.")
459
+ fail_unknown(display, name)
422
460
  }
423
461
  end
424
462
 
@@ -428,7 +466,7 @@ module VMC
428
466
  choices ||= instance_exec(&blk) if block_given?
429
467
 
430
468
  choices.find { |c| c.name.upcase == name.upcase } ||
431
- fail("Unknown #{display} '#{name}'.")
469
+ fail_unknown(display, name)
432
470
  }
433
471
  end
434
472
  end
@@ -12,7 +12,16 @@ module VMC::App
12
12
  group :apps, :manage
13
13
  input :name, :desc => "Application name", :argument => :optional
14
14
  input :path, :desc => "Path containing the bits", :default => "."
15
- input :url, :desc => "URL to bind to app"
15
+ input :host, :desc => "Subdomain for the app's URL"
16
+ input :domain, :desc => "Domain for the app",
17
+ :from_given => proc { |given, app|
18
+ if !v2? || given == "none"
19
+ given
20
+ else
21
+ app.space.domain_by_name(given) ||
22
+ fail_unknown("domain", given)
23
+ end
24
+ }
16
25
  input :memory, :desc => "Memory limit"
17
26
  input :instances, :desc => "Number of instances to run", :type => :integer
18
27
  input :framework, :desc => "Framework to use", :from_given => by_name(:framework)
@@ -50,7 +59,7 @@ module VMC::App
50
59
  def setup_new_app(path)
51
60
  self.path = path
52
61
  app = create_app(get_inputs)
53
- map_url(app)
62
+ map_route(app)
54
63
  create_services(app)
55
64
  bind_services(app)
56
65
  upload_app(app, path)
@@ -80,5 +89,17 @@ module VMC::App
80
89
  err "Upload failed. Try again with 'vmc push'."
81
90
  raise
82
91
  end
92
+
93
+ def wrap_message_format_errors
94
+ yield
95
+ rescue CFoundry::MessageParseError => e
96
+ md = e.description.match /Field: ([^,]+)/
97
+ field = md[1]
98
+
99
+ case field
100
+ when "buildpack"
101
+ fail "Buildpack must be a public git repository URI."
102
+ end
103
+ end
83
104
  end
84
105
  end
@@ -59,21 +59,25 @@ module VMC::App
59
59
  app = filter(:create_app, app)
60
60
 
61
61
  with_progress("Creating #{c(app.name, :name)}") do
62
- app.create!
62
+ wrap_message_format_errors do
63
+ app.create!
64
+ end
63
65
  end
64
66
 
65
67
  app
66
68
  end
67
69
 
68
- def map_url(app)
70
+ def map_route(app)
69
71
  line unless quiet?
70
72
 
71
- url = input[:url, app.name]
73
+ host = input[:host, app.name] if v2?
74
+ domain = input[:domain, app]
72
75
 
73
76
  mapped_url = false
74
- until url == "none" || !url || mapped_url
77
+ until domain == "none" || !domain || mapped_url
75
78
  begin
76
- invoke :map, :app => app, :url => url
79
+ host = "" if host == "none"
80
+ invoke :map, :app => app, :host => host, :domain => domain
77
81
  mapped_url = true
78
82
  rescue CFoundry::RouteHostTaken, CFoundry::UriAlreadyTaken => e
79
83
  raise if force?
@@ -81,8 +85,11 @@ module VMC::App
81
85
  line c(e.description, :bad)
82
86
  line
83
87
 
84
- input.forget(:url)
85
- url = input[:url, app.name]
88
+ input.forget(:host) if v2?
89
+ input.forget(:domain)
90
+
91
+ host = input[:host, app.name] if v2?
92
+ domain = input[:domain, app]
86
93
 
87
94
  # version bumps on v1 even though mapping fails
88
95
  app.invalidate! unless v2?
@@ -4,17 +4,24 @@ module VMC::App
4
4
  ask("Name")
5
5
  end
6
6
 
7
- def ask_url(name)
8
- choices = url_choices(name)
7
+ def ask_host(name)
8
+ ask "Subdomain", :choices => [name, "none"],
9
+ :default => name,
10
+ :allow_other => true
11
+ end
12
+
13
+ def ask_domain(app)
14
+ choices = v2? ? app.space.domains : ["#{app.name}.#{target_base}"]
9
15
 
10
16
  options = {
11
17
  :choices => choices + ["none"],
18
+ :display => proc { |d| d.is_a?(String) ? d : d.name },
12
19
  :allow_other => true
13
20
  }
14
21
 
15
22
  options[:default] = choices.first if choices.size == 1
16
23
 
17
- ask "URL", options
24
+ ask "Domain", options
18
25
  end
19
26
 
20
27
  def ask_memory(default)
@@ -25,7 +25,9 @@ module VMC::App
25
25
  def commit_changes(app)
26
26
  if app.changed?
27
27
  with_progress("Updating #{c(app.name, :name)}") do
28
- app.update!
28
+ wrap_message_format_errors do
29
+ app.update!
30
+ end
29
31
  end
30
32
  end
31
33
 
@@ -42,7 +44,7 @@ module VMC::App
42
44
  human_mb(val)
43
45
  when :framework, :runtime
44
46
  val.name
45
- when :command
47
+ when :command, :buildpack
46
48
  "'#{val}'"
47
49
  when :production
48
50
  bool(val)
@@ -36,7 +36,7 @@ module VMC::App
36
36
 
37
37
  if usage
38
38
  [ idx,
39
- "#{percentage(usage[:cpu])} of #{b(stats[:cores])} cores",
39
+ "#{percentage(usage[:cpu])}",
40
40
  "#{usage(usage[:mem], stats[:mem_quota])}",
41
41
  "#{usage(usage[:disk], stats[:disk_quota])}"
42
42
  ]
@@ -6,29 +6,33 @@ module VMC::Route
6
6
 
7
7
  desc "Add a URL mapping"
8
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)
9
+ input :app, :desc => "Application to add the URL to",
10
+ :argument => :optional, :from_given => by_name(:app)
11
+ input :host, :desc => "Host name for the route",
12
+ :argument => :optional, :default => ""
13
+ input :domain, :desc => "Domain to add the route to",
14
+ :argument => true,
15
+ :from_given => proc { |name, space|
16
+ if v2?
17
+ space.domain_by_name(name) ||
18
+ fail_unknown("domain", name)
19
+ else
20
+ name
21
+ end
22
+ }
14
23
  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
24
+ app = input[:app]
25
+ space = app.space if v2?
21
26
 
22
- url = input[:url].sub(/^https?:\/\/(.*)\/?/i, '\1')
27
+ host = input[:host]
28
+ domain = input[:domain, space]
23
29
 
24
30
  if v2?
25
- host, domain_name = url.split(".", 2)
26
- domain = find_domain(space, domain_name)
27
31
  route = find_or_create_route(domain, host, space)
28
32
  bind_route(route, app) if app
29
33
  else
30
34
  with_progress("Updating #{c(app.name, :name)}") do
31
- app.urls << url
35
+ app.urls << domain
32
36
  app.update!
33
37
  end
34
38
  end
@@ -56,7 +60,9 @@ module VMC::Route
56
60
  route.domain = domain
57
61
  route.space = space
58
62
 
59
- with_progress("Creating route #{c(route.name, :name)}") { route.create! }
63
+ with_progress("Creating route #{c(route.name, :name)}") do
64
+ route.create!
65
+ end
60
66
 
61
67
  route
62
68
  end
@@ -69,7 +69,7 @@ module VMC::Service
69
69
  service.type = offering.type
70
70
  service.vendor = offering.label
71
71
  service.version = offering.version
72
- service.tier = "free"
72
+ service.tier = v1_service_tier(offering)
73
73
  end
74
74
 
75
75
  with_progress("Creating service #{c(service.name, :name)}") do
@@ -102,10 +102,25 @@ module VMC::Service
102
102
  ask "Name?", :default => "#{offering.label}-#{random}"
103
103
  end
104
104
 
105
- def ask_plan(plans)
106
- ask "Which plan?", :choices => plans.sort_by(&:name),
107
- :display => proc { |p| "#{p.name}: #{p.description}" },
105
+ def ask_plan(plans, default_plan = nil)
106
+ ask "Which plan?",
107
+ :choices => plans.sort_by(&:name),
108
+ :indexed => true,
109
+ :display => proc { |p| "#{p.name}: #{p.description || 'No description'}" },
110
+ :default => default_plan,
108
111
  :complete => proc(&:name)
109
112
  end
113
+
114
+ def v1_service_tier(service)
115
+ plans = service.service_plans
116
+ fail "No service plans" if plans.empty?
117
+ if plans.length == 1
118
+ plan = plans[0]
119
+ else
120
+ plan = ask_plan(plans, service.default_service_plan)
121
+ end
122
+ plan.name
123
+ end
124
+
110
125
  end
111
126
  end
@@ -2,6 +2,10 @@ require "vmc/cli/start/base"
2
2
 
3
3
  module VMC::Start
4
4
  class Info < Base
5
+ def precondition
6
+ check_target
7
+ end
8
+
5
9
  desc "Display information on the current target, user, etc."
6
10
  group :start
7
11
  input :runtimes, :desc => "List supported runtimes", :alias => "-r",
@@ -3,6 +3,10 @@ require "vmc/cli/start/target_interactions"
3
3
 
4
4
  module VMC::Start
5
5
  class Login < Base
6
+ def precondition
7
+ check_target
8
+ end
9
+
6
10
  desc "Authenticate with the target"
7
11
  group :start
8
12
  input :username, :value => :email, :desc => "Account email",
@@ -2,6 +2,10 @@ require "vmc/cli/start/base"
2
2
 
3
3
  module VMC::Start
4
4
  class Logout < Base
5
+ def precondition
6
+ check_target
7
+ end
8
+
5
9
  desc "Log out from the target"
6
10
  group :start
7
11
  def logout