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.
- 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