vmc 0.5.0.beta.12 → 0.5.0.rc1
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/vmc/cli.rb +62 -24
- data/lib/vmc/cli/app/push.rb +23 -2
- data/lib/vmc/cli/app/push/create.rb +14 -7
- data/lib/vmc/cli/app/push/interactions.rb +10 -3
- data/lib/vmc/cli/app/push/sync.rb +4 -2
- data/lib/vmc/cli/app/stats.rb +1 -1
- data/lib/vmc/cli/route/map.rb +22 -16
- data/lib/vmc/cli/service/create.rb +19 -4
- data/lib/vmc/cli/start/info.rb +4 -0
- data/lib/vmc/cli/start/login.rb +4 -0
- data/lib/vmc/cli/start/logout.rb +4 -0
- data/lib/vmc/cli/user/base.rb +1 -1
- data/lib/vmc/test_support/command_helper.rb +7 -13
- data/lib/vmc/version.rb +1 -1
- data/spec/assets/specker_runner/specker_runner_input.rb +6 -0
- data/spec/assets/specker_runner/specker_runner_pause.rb +5 -0
- data/spec/console_app_specker/console_app_specker_matchers_spec.rb +152 -0
- data/spec/console_app_specker/specker_runner_spec.rb +157 -0
- data/spec/features/new_user_flow_spec.rb +83 -45
- data/spec/spec_helper.rb +16 -0
- data/spec/support/console_app_specker_matchers.rb +75 -0
- data/spec/support/specker_runner.rb +123 -0
- data/spec/vmc/cli/app/push/create_spec.rb +82 -21
- data/spec/vmc/cli/app/push_spec.rb +49 -4
- data/spec/vmc/cli/app/stats_spec.rb +1 -1
- data/spec/vmc/cli/route/map_spec.rb +38 -42
- data/spec/vmc/cli/start/info_spec.rb +21 -1
- data/spec/vmc/cli/start/login_spec.rb +36 -10
- data/spec/vmc/cli/start/logout_spec.rb +63 -0
- data/spec/vmc/cli/user/passwd_spec.rb +1 -1
- data/spec/vmc/cli/user/register_spec.rb +1 -1
- data/spec/vmc/cli_spec.rb +243 -33
- metadata +68 -36
data/lib/vmc/cli.rb
CHANGED
@@ -63,7 +63,7 @@ module VMC
|
|
63
63
|
end
|
64
64
|
|
65
65
|
def check_target
|
66
|
-
unless
|
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
|
100
|
-
|
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
|
-
|
281
|
-
|
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 =
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
469
|
+
fail_unknown(display, name)
|
432
470
|
}
|
433
471
|
end
|
434
472
|
end
|
data/lib/vmc/cli/app/push.rb
CHANGED
@@ -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 :
|
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
|
-
|
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
|
-
|
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
|
70
|
+
def map_route(app)
|
69
71
|
line unless quiet?
|
70
72
|
|
71
|
-
|
73
|
+
host = input[:host, app.name] if v2?
|
74
|
+
domain = input[:domain, app]
|
72
75
|
|
73
76
|
mapped_url = false
|
74
|
-
until
|
77
|
+
until domain == "none" || !domain || mapped_url
|
75
78
|
begin
|
76
|
-
|
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(:
|
85
|
-
|
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
|
8
|
-
choices
|
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 "
|
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
|
-
|
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)
|
data/lib/vmc/cli/app/stats.rb
CHANGED
data/lib/vmc/cli/route/map.rb
CHANGED
@@ -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 :
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
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
|
-
|
16
|
-
|
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
|
-
|
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 <<
|
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)}")
|
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 =
|
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?",
|
107
|
-
:
|
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
|
data/lib/vmc/cli/start/info.rb
CHANGED
@@ -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",
|
data/lib/vmc/cli/start/login.rb
CHANGED