vmc 0.4.0.beta.12 → 0.4.0.beta.13

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.
@@ -2,13 +2,10 @@
2
2
  # vim: ft=ruby
3
3
 
4
4
  require "rubygems"
5
- require "thor"
6
5
 
7
6
  require "vmc"
8
7
  require "vmc/plugin"
9
8
 
10
9
  VMC::Plugin.load_all
11
10
 
12
- $exit_status = 0
13
11
  VMC::CLI.start(ARGV)
14
- exit($exit_status)
@@ -1,2 +1,7 @@
1
1
  require "vmc/version"
2
+
2
3
  require "vmc/cli"
4
+ require "vmc/cli/start"
5
+ require "vmc/cli/app"
6
+ require "vmc/cli/service"
7
+ require "vmc/cli/user"
@@ -1,428 +1,276 @@
1
- require "vmc/cli/command"
2
-
3
- VMC::Command.groups(
4
- [:start, "Getting Started"],
5
- [:apps, "Applications",
6
- [:manage, "Management"],
7
- [:info, "Information"]],
8
- [:services, "Services",
9
- [:manage, "Management"]],
10
- [:admin, "Administration",
11
- [:user, "User Management"]])
12
-
13
- require "vmc/cli/app"
14
- require "vmc/cli/service"
15
- require "vmc/cli/user"
1
+ require "yaml"
16
2
 
17
- module VMC
18
- class CLI < App # subclass App since we operate on Apps by default
19
- desc "service SUBCOMMAND ...ARGS", "Service management"
20
- subcommand "service", Service
21
-
22
- desc "user SUBCOMMAND ...ARGS", "User management"
23
- subcommand "user", User
24
-
25
- desc "info", "Display information on the current target, user, etc."
26
- group :start
27
- flag :runtimes, :default => false
28
- flag :services, :default => false
29
- flag :frameworks, :default => false
30
- def info
31
- info =
32
- with_progress("Getting target information") do
33
- client.info
34
- end
3
+ require "mothership"
4
+ require "mothership/pretty"
5
+ require "mothership/progress"
35
6
 
36
- authorized = !!info["frameworks"]
7
+ require "cfoundry"
37
8
 
38
- if input(:runtimes)
39
- raise NotAuthorized unless authorized
9
+ require "vmc/constants"
10
+ require "vmc/errors"
40
11
 
41
- runtimes = {}
42
- info["frameworks"].each do |_, f|
43
- f["runtimes"].each do |r|
44
- runtimes[r["name"]] = r
45
- end
46
- end
12
+ require "vmc/cli/help"
13
+ require "vmc/cli/interactive"
47
14
 
48
- runtimes = runtimes.values.sort_by { |x| x["name"] }
49
15
 
50
- if simple_output?
51
- runtimes.each do |r|
52
- puts r["name"]
53
- end
54
- return
55
- end
16
+ $vmc_asked_auth = false
56
17
 
57
- runtimes.each do |r|
58
- puts ""
59
- puts "#{c(r["name"], :name)}:"
60
- puts " version: #{b(r["version"])}"
61
- puts " description: #{b(r["description"])}"
62
- end
63
-
64
- return
65
- end
66
-
67
- if input(:services)
68
- raise NotAuthorized unless authorized
69
-
70
- services = client.system_services
71
-
72
- if simple_output?
73
- services.each do |name, _|
74
- puts name
75
- end
18
+ module VMC
19
+ class CLI < Mothership
20
+ include VMC::Interactive
21
+ include Mothership::Pretty
22
+ include Mothership::Progress
76
23
 
77
- return
78
- end
24
+ option :help, :alias => "-h", :type => :boolean,
25
+ :desc => "Show command usage & instructions"
79
26
 
80
- services.each do |name, meta|
81
- puts ""
82
- puts "#{c(name, :name)}:"
83
- puts " versions: #{meta[:versions].join ", "}"
84
- puts " description: #{meta[:description]}"
85
- puts " type: #{meta[:type]}"
86
- end
27
+ option :proxy, :alias => "-u", :value => :email,
28
+ :desc => "Act as another user (admin only)"
87
29
 
88
- return
89
- end
30
+ option :version, :alias => "-v", :type => :boolean,
31
+ :desc => "Print version number"
90
32
 
91
- if input(:frameworks)
92
- raise NotAuthorized unless authorized
33
+ option(:force, :alias => "-f", :type => :boolean,
34
+ :desc => "Skip interaction when possible") {
35
+ option(:script)
36
+ }
93
37
 
94
- puts "" unless simple_output?
38
+ option(:quiet, :alias => "-q", :type => :boolean,
39
+ :desc => "Simplify output format") {
40
+ option(:script)
41
+ }
95
42
 
96
- info["frameworks"].each do |name, _|
97
- puts name
98
- end
43
+ option(:script, :alias => "-s", :type => :boolean,
44
+ :desc => "Shortcut for --quiet and --force") {
45
+ !$stdout.tty?
46
+ }
99
47
 
100
- return
101
- end
48
+ option(:color, :type => :boolean, :default => true,
49
+ :desc => "Use colorful output") {
50
+ !option(:quiet)
51
+ }
102
52
 
103
- puts ""
53
+ option :trace, :alias => "-t", :type => :boolean,
54
+ :desc => "Show API requests and responses"
104
55
 
105
- puts info["description"]
106
- puts ""
107
- puts "target: #{b(client.target)}"
108
- puts " version: #{info["version"]}"
109
- puts " support: #{info["support"]}"
110
56
 
111
- if info["user"]
112
- puts ""
113
- puts "user: #{b(info["user"])}"
114
- puts " usage:"
115
-
116
- limits = info["limits"]
117
- info["usage"].each do |k, v|
118
- m = limits[k]
119
- if k == "memory"
120
- puts " #{k}: #{usage(v * 1024 * 1024, m * 1024 * 1024)}"
121
- else
122
- puts " #{k}: #{b(v)} of #{b(m)} limit"
123
- end
124
- end
57
+ def default_action
58
+ if option(:version)
59
+ puts "vmc #{VERSION}"
60
+ else
61
+ super
125
62
  end
126
63
  end
127
64
 
128
- desc "target [URL]", "Set or display the current target cloud"
129
- group :start
130
- def target(url = nil)
131
- if url.nil?
132
- display_target
133
- return
134
- end
65
+ def execute(cmd, argv)
66
+ if option(:help)
67
+ invoke :help, :command => cmd.name.to_s
68
+ else
69
+ super
70
+ end
71
+ rescue Interrupt
72
+ exit_status 130
73
+ rescue Mothership::Error
74
+ raise
75
+ rescue UserError => e
76
+ err e.message
77
+ rescue CFoundry::Denied => e
78
+ if !$vmc_asked_auth && e.error_code == 200
79
+ $vmc_asked_auth = true
135
80
 
136
- target = sane_target_url(url)
137
- display = c(target.sub(/https?:\/\//, ""), :name)
138
- with_progress("Setting target to #{display}") do
139
- unless force?
140
- # check that the target is valid
141
- CFoundry::Client.new(target).info
142
- end
81
+ puts ""
82
+ puts c("Not authenticated! Try logging in:", :warning)
143
83
 
144
- set_target(target)
145
- end
146
- end
84
+ invoke :login
85
+ @client = nil
147
86
 
148
- desc "login [EMAIL]", "Authenticate with the target"
149
- group :start
150
- flag(:email) {
151
- ask("Email")
152
- }
153
- flag(:password)
154
- # TODO: implement new authentication scheme
155
- def login(email = nil)
156
- unless simple_output?
157
- display_target
158
- puts ""
87
+ retry
159
88
  end
160
89
 
161
- email ||= input(:email)
162
- password = input(:password)
90
+ err "Denied: #{e.description}"
91
+ rescue Exception => e
92
+ msg = e.class.name
93
+ msg << ": #{e}" unless e.to_s.empty?
94
+ err msg
163
95
 
164
- authenticated = false
165
- failed = false
166
- until authenticated
167
- unless force?
168
- if failed || !password
169
- password = ask("Password", :echo => "*", :forget => true)
170
- end
171
- end
96
+ ensure_config_dir
172
97
 
173
- with_progress("Authenticating") do |s|
174
- begin
175
- save_token(client.login(email, password))
176
- authenticated = true
177
- rescue CFoundry::Denied
178
- return if force?
98
+ File.open(File.expand_path(VMC::CRASH_FILE), "w") do |f|
99
+ f.puts "Time of crash:"
100
+ f.puts " #{Time.now}"
101
+ f.puts ""
102
+ f.puts msg
103
+ f.puts ""
179
104
 
180
- s.fail do
181
- failed = true
182
- end
105
+ vmc_dir = File.expand_path("../../../..", __FILE__) + "/"
106
+ e.backtrace.each do |loc|
107
+ if loc =~ /\/gems\//
108
+ f.puts loc.sub(/.*\/gems\//, "")
109
+ else
110
+ f.puts loc.sub(vmc_dir, "")
183
111
  end
184
112
  end
185
113
  end
186
- ensure
187
- $exit_status = 1 if not authenticated
188
114
  end
189
115
 
190
- desc "logout", "Log out from the target"
191
- group :start
192
- def logout
193
- with_progress("Logging out") do
194
- remove_token
195
- end
116
+ def quiet?
117
+ option(:quiet)
196
118
  end
197
119
 
198
- desc "register [EMAIL]", "Create a user and log in"
199
- group :start, :hidden => true
200
- flag(:email) {
201
- ask("Email")
202
- }
203
- flag(:password) {
204
- ask("Password", :echo => "*", :forget => true)
205
- }
206
- flag(:verify_password) {
207
- ask("Confirm Password", :echo => "*", :forget => true)
208
- }
209
- flag(:no_login, :type => :boolean)
210
- def register(email = nil)
211
- unless simple_output?
212
- puts "Target: #{c(client_target, :name)}"
213
- puts ""
214
- end
215
-
216
- email ||= input(:email)
217
- password ||= input(:password)
218
-
219
- if !force? && password != input(:verify_password)
220
- fail "Passwords do not match."
221
- end
222
-
223
- with_progress("Creating user") do
224
- client.register(email, password)
225
- end
226
-
227
- unless input(:skip_login)
228
- with_progress("Logging in") do
229
- save_token(client.login(email, password))
230
- end
231
- end
120
+ def force?
121
+ option(:force)
232
122
  end
233
123
 
234
- desc "apps", "List your applications"
235
- group :apps
236
- flag :name, :desc => "Filter by name regexp"
237
- flag :runtime, :desc => "Filter by runtime regexp"
238
- flag :framework, :desc => "Filter by framework regexp"
239
- flag :url, :desc => "Filter by url regexp"
240
- def apps
241
- apps =
242
- with_progress("Getting applications") do
243
- client.apps
244
- end
124
+ def color_enabled?
125
+ option(:color)
126
+ end
245
127
 
246
- if apps.empty? and !simple_output?
247
- puts ""
248
- puts "No applications."
249
- return
128
+ def err(msg, exit_status = 1)
129
+ if quiet?
130
+ $stderr.puts(msg)
131
+ else
132
+ puts c(msg, :error)
250
133
  end
251
134
 
252
- apps.each.with_index do |a, num|
253
- display_app(a) if app_matches(a)
254
- end
135
+ exit_status 1
255
136
  end
256
137
 
257
- desc "services", "List your services"
258
- group :services
259
- flag :name, :desc => "Filter by name regexp"
260
- flag :app, :desc => "Filter by bound application regexp"
261
- flag :type, :desc => "Filter by service type regexp"
262
- flag :vendor, :desc => "Filter by service vendor regexp"
263
- flag :tier, :desc => "Filter by service tier regexp"
264
- def services
265
- services =
266
- with_progress("Getting services") do
267
- client.services
268
- end
269
-
270
- puts "" unless simple_output?
271
-
272
- if services.empty? and !simple_output?
273
- puts "No services."
274
- end
138
+ def fail(msg)
139
+ raise UserError, msg
140
+ end
275
141
 
276
- if app = options[:app]
277
- apps = client.apps
278
- services.reject! do |s|
279
- apps.none? { |a| a.services.include? s.name }
280
- end
142
+ def sane_target_url(url)
143
+ unless url =~ /^https?:\/\//
144
+ url = "http://#{url}"
281
145
  end
282
146
 
283
- services.each do |s|
284
- display_service(s) if service_matches(s)
285
- end
147
+ url.gsub(/\/$/, "")
286
148
  end
287
149
 
288
- desc "users", "List all users"
289
- group :admin, :hidden => true
290
- def users
291
- users =
292
- with_progress("Getting users") do
293
- client.users
294
- end
150
+ def target_file
151
+ one_of(VMC::TARGET_FILE, VMC::OLD_TARGET_FILE)
152
+ end
295
153
 
296
- users.each do |u|
297
- display_user(u)
298
- end
154
+ def tokens_file
155
+ one_of(VMC::TOKENS_FILE, VMC::OLD_TOKENS_FILE)
299
156
  end
300
157
 
301
- desc "help [COMMAND]", "Usage instructions"
302
- flag :all, :default => false
303
- group :start
304
- def help(task = nil)
305
- if options[:version]
306
- puts "vmc #{VERSION}"
307
- return
158
+ def one_of(*paths)
159
+ paths.each do |p|
160
+ exp = File.expand_path(p)
161
+ return exp if File.exist? exp
308
162
  end
309
163
 
310
- if task
311
- self.class.task_help(@shell, task)
312
- else
313
- unless input(:all)
314
- puts "Showing basic command set. Pass --all to list all commands."
315
- puts ""
316
- end
164
+ paths.first
165
+ end
317
166
 
318
- self.class.print_help_groups(input(:all))
319
- end
167
+ def client_target
168
+ File.read(target_file).chomp
320
169
  end
321
170
 
322
- desc "colors", "Show color configuration"
323
- group :start, :hidden => true
324
- def colors
325
- user_colors.each do |n, c|
326
- puts "#{n}: #{c(c.to_s, n)}"
327
- end
171
+ def ensure_config_dir
172
+ config = File.expand_path(VMC::CONFIG_DIR)
173
+ Dir.mkdir(config) unless File.exist? config
328
174
  end
329
175
 
330
- private
176
+ def set_target(url)
177
+ ensure_config_dir
331
178
 
332
- def app_matches(a)
333
- if name = options[:name]
334
- return false if a.name !~ /#{name}/
179
+ File.open(File.expand_path(VMC::TARGET_FILE), "w") do |f|
180
+ f.write(sane_target_url(url))
335
181
  end
336
182
 
337
- if runtime = options[:runtime]
338
- return false if a.runtime !~ /#{runtime}/
339
- end
183
+ @client = nil
184
+ end
340
185
 
341
- if framework = options[:framework]
342
- return false if a.framework !~ /#{framework}/
343
- end
186
+ def tokens
187
+ new_toks = File.expand_path(VMC::TOKENS_FILE)
188
+ old_toks = File.expand_path(VMC::OLD_TOKENS_FILE)
344
189
 
345
- if url = options[:url]
346
- return false if a.urls.none? { |u| u =~ /#{url}/ }
190
+ if File.exist? new_toks
191
+ YAML.load_file(new_toks)
192
+ elsif File.exist? old_toks
193
+ JSON.load(File.read(old_toks))
194
+ else
195
+ {}
347
196
  end
197
+ end
348
198
 
349
- true
199
+ def client_token
200
+ tokens[client_target]
350
201
  end
351
202
 
352
- IS_UTF8 = !!(ENV["LC_ALL"] || ENV["LC_CTYPE"] || ENV["LANG"])["UTF-8"]
203
+ def save_tokens(ts)
204
+ ensure_config_dir
353
205
 
354
- def display_app(a)
355
- if simple_output?
356
- puts a.name
357
- return
206
+ File.open(File.expand_path(VMC::TOKENS_FILE), "w") do |io|
207
+ YAML.dump(ts, io)
358
208
  end
209
+ end
359
210
 
360
- puts ""
361
-
362
- status = app_status(a)
363
-
364
- puts "#{c(a.name, :name)}: #{status}"
365
-
366
- puts " platform: #{b(a.framework)} on #{b(a.runtime)}"
367
-
368
- print " usage: #{b(human_size(a.memory * 1024 * 1024, 0))}"
369
- print " #{c(IS_UTF8 ? "\xc3\x97" : "x", :dim)} #{b(a.total_instances)}"
370
- print " instance#{a.total_instances == 1 ? "" : "s"}"
371
- puts ""
372
-
373
- unless a.urls.empty?
374
- puts " urls: #{a.urls.collect { |u| b(u) }.join(", ")}"
375
- end
211
+ def save_token(token)
212
+ ts = tokens
213
+ ts[client_target] = token
214
+ save_tokens(ts)
215
+ end
376
216
 
377
- unless a.services.empty?
378
- puts " services: #{a.services.collect { |s| b(s) }.join(", ")}"
379
- end
217
+ def remove_token
218
+ ts = tokens
219
+ ts.delete client_target
220
+ save_tokens(ts)
380
221
  end
381
222
 
382
- def service_matches(s)
383
- if name = options[:name]
384
- return false if s.name !~ /#{name}/
385
- end
223
+ def client
224
+ return @client if @client
386
225
 
387
- if type = options[:type]
388
- return false if s.type !~ /#{type}/
389
- end
226
+ @client = CFoundry::Client.new(client_target, client_token)
227
+ @client.proxy = option(:proxy)
228
+ @client.trace = option(:trace)
229
+ @client
230
+ end
390
231
 
391
- if vendor = options[:vendor]
392
- return false if s.vendor !~ /#{vendor}/
393
- end
232
+ def usage(used, limit)
233
+ "#{b(human_size(used))} of #{b(human_size(limit, 0))}"
234
+ end
394
235
 
395
- if tier = options[:tier]
396
- return false if s.tier !~ /#{tier}/
397
- end
236
+ def percentage(num, low = 50, mid = 70)
237
+ color =
238
+ if num <= low
239
+ :good
240
+ elsif num <= mid
241
+ :warning
242
+ else
243
+ :bad
244
+ end
398
245
 
399
- true
246
+ c(format("%.1f\%", num), color)
400
247
  end
401
248
 
402
- def display_service(s)
403
- if simple_output?
404
- puts s.name
405
- else
406
- puts "#{c(s.name, :name)}: #{s.vendor} v#{s.version}"
249
+ def megabytes(str)
250
+ if str =~ /T$/i
251
+ str.to_i * 1024 * 1024
252
+ elsif str =~ /G$/i
253
+ str.to_i * 1024
254
+ elsif str =~ /M$/i
255
+ str.to_i
256
+ elsif str =~ /K$/i
257
+ str.to_i / 1024
258
+ else # assume megabytes
259
+ str.to_i
407
260
  end
408
261
  end
409
262
 
410
- def display_user(u)
411
- if simple_output?
412
- puts u.email
413
- else
414
- puts ""
415
- puts "#{c(u.email, :name)}:"
416
- puts " admin?: #{c(u.admin?, u.admin? ? :yes : :no)}"
263
+ def human_size(num, precision = 1)
264
+ sizes = ["G", "M", "K"]
265
+ sizes.each.with_index do |suf, i|
266
+ pow = sizes.size - i
267
+ unit = 1024 ** pow
268
+ if num >= unit
269
+ return format("%.#{precision}f%s", num / unit, suf)
270
+ end
417
271
  end
418
- end
419
272
 
420
- def display_target
421
- if simple_output?
422
- puts client.target
423
- else
424
- puts "Target: #{c(client.target, :name)}"
425
- end
273
+ format("%.#{precision}fB", num)
426
274
  end
427
275
  end
428
276
  end