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

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