vmcu 0.3.17

Sign up to get free protection for your applications and to get access to all the features.
Files changed (43) hide show
  1. data/LICENSE +24 -0
  2. data/README.md +160 -0
  3. data/Rakefile +101 -0
  4. data/bin/vmcu +6 -0
  5. data/caldecott_helper/Gemfile +10 -0
  6. data/caldecott_helper/Gemfile.lock +48 -0
  7. data/caldecott_helper/server.rb +43 -0
  8. data/config/clients.yml +17 -0
  9. data/config/micro/offline.conf +2 -0
  10. data/config/micro/paths.yml +22 -0
  11. data/config/micro/refresh_ip.rb +20 -0
  12. data/lib/cli.rb +47 -0
  13. data/lib/cli/commands/admin.rb +80 -0
  14. data/lib/cli/commands/apps.rb +1128 -0
  15. data/lib/cli/commands/base.rb +238 -0
  16. data/lib/cli/commands/manifest.rb +56 -0
  17. data/lib/cli/commands/micro.rb +115 -0
  18. data/lib/cli/commands/misc.rb +277 -0
  19. data/lib/cli/commands/services.rb +180 -0
  20. data/lib/cli/commands/user.rb +96 -0
  21. data/lib/cli/config.rb +192 -0
  22. data/lib/cli/console_helper.rb +157 -0
  23. data/lib/cli/core_ext.rb +122 -0
  24. data/lib/cli/errors.rb +19 -0
  25. data/lib/cli/frameworks.rb +244 -0
  26. data/lib/cli/manifest_helper.rb +302 -0
  27. data/lib/cli/runner.rb +543 -0
  28. data/lib/cli/services_helper.rb +84 -0
  29. data/lib/cli/tunnel_helper.rb +332 -0
  30. data/lib/cli/usage.rb +118 -0
  31. data/lib/cli/version.rb +7 -0
  32. data/lib/cli/zip_util.rb +77 -0
  33. data/lib/vmc.rb +3 -0
  34. data/lib/vmc/client.rb +591 -0
  35. data/lib/vmc/const.rb +22 -0
  36. data/lib/vmc/micro.rb +56 -0
  37. data/lib/vmc/micro/switcher/base.rb +97 -0
  38. data/lib/vmc/micro/switcher/darwin.rb +19 -0
  39. data/lib/vmc/micro/switcher/dummy.rb +15 -0
  40. data/lib/vmc/micro/switcher/linux.rb +16 -0
  41. data/lib/vmc/micro/switcher/windows.rb +31 -0
  42. data/lib/vmc/micro/vmrun.rb +158 -0
  43. metadata +263 -0
@@ -0,0 +1,302 @@
1
+ require "set"
2
+
3
+ module VMC::Cli::ManifestHelper
4
+ include VMC::Cli::ServicesHelper
5
+
6
+ DEFAULTS = {
7
+ "url" => "${name}.${target-base}",
8
+ "mem" => "128M",
9
+ "instances" => 1
10
+ }
11
+
12
+ MANIFEST = "manifest.yml"
13
+
14
+ YES_SET = Set.new(["y", "Y", "yes", "YES"])
15
+
16
+ # take a block and call it once for each app to push/update.
17
+ # with @application and @app_info set appropriately
18
+ def each_app(panic=true)
19
+ if @manifest and all_apps = @manifest["applications"]
20
+ where = File.expand_path(@path)
21
+ single = false
22
+
23
+ all_apps.each do |path, info|
24
+ app = File.expand_path("../" + path, manifest_file)
25
+ if where.start_with?(app)
26
+ @application = app
27
+ @app_info = info
28
+ yield info["name"]
29
+ single = true
30
+ break
31
+ end
32
+ end
33
+
34
+ unless single
35
+ if where == File.expand_path("../", manifest_file)
36
+ ordered_by_deps(all_apps).each do |path, info|
37
+ app = File.expand_path("../" + path, manifest_file)
38
+ @application = app
39
+ @app_info = info
40
+ yield info["name"]
41
+ end
42
+ else
43
+ err "Path '#{@path}' is not known to manifest '#{manifest_file}'."
44
+ end
45
+ end
46
+ else
47
+ @application = @path
48
+ @app_info = @manifest
49
+ if @app_info
50
+ yield @app_info["name"]
51
+ elsif panic
52
+ err "No applications."
53
+ end
54
+ end
55
+
56
+ nil
57
+ ensure
58
+ @application = nil
59
+ @app_info = nil
60
+ end
61
+
62
+ def interact(many=false)
63
+ @manifest ||= {}
64
+ configure_app(many)
65
+ end
66
+
67
+ def target_manifest
68
+ @options[:manifest] || MANIFEST
69
+ end
70
+
71
+ def save_manifest(save_to = nil)
72
+ save_to ||= target_manifest
73
+
74
+ File.open(save_to, "w") do |f|
75
+ f.write @manifest.to_yaml
76
+ end
77
+
78
+ say "Manifest written to #{save_to}."
79
+ end
80
+
81
+ def configure_app(many=false)
82
+ name = manifest("name") ||
83
+ set(ask("Application Name", :default => manifest("name")), "name")
84
+
85
+
86
+
87
+ if manifest "framework"
88
+ framework = VMC::Cli::Framework.lookup_by_framework manifest("framework","name")
89
+ else
90
+ framework = detect_framework
91
+ set framework.name, "framework", "name"
92
+ set(
93
+ { "mem" => framework.mem,
94
+ "description" => framework.description,
95
+ "exec" => framework.exec
96
+ },
97
+ "framework",
98
+ "info"
99
+ )
100
+ end
101
+
102
+ default_runtime = manifest "runtime"
103
+ if not default_runtime
104
+ default_runtime = framework.default_runtime(@application)
105
+ set(detect_runtime(default_runtime), "runtime") if framework.prompt_for_runtime?
106
+ end
107
+ default_command = manifest "command"
108
+ set ask("Start Command", :default => default_command), "command" if framework.require_start_command?
109
+
110
+ url_template = manifest("url") || DEFAULTS["url"]
111
+ url_resolved = url_template.dup
112
+ resolve_lexically(url_resolved)
113
+
114
+ if !framework.require_url?
115
+ url_resolved = "None"
116
+ end
117
+ url = ask("Application Deployed URL", :default => url_resolved)
118
+
119
+ if url == url_resolved && url != "None"
120
+ url = url_template
121
+ end
122
+
123
+ # common error case is for prompted users to answer y or Y or yes or
124
+ # YES to this ask() resulting in an unintended URL of y. Special
125
+ # case this common error
126
+ url = url_resolved if YES_SET.member? url
127
+
128
+ if(url == "None")
129
+ url = nil
130
+ end
131
+
132
+ set url, "url"
133
+
134
+ default_mem = manifest("mem")
135
+ default_mem = framework.memory(manifest("runtime")) if not default_mem
136
+ set ask(
137
+ "Memory reservation",
138
+ :default =>
139
+ default_mem ||
140
+ DEFAULTS["mem"],
141
+ :choices => ["128M", "256M", "512M", "1G", "2G"]
142
+ ), "mem"
143
+
144
+ set ask(
145
+ "How many instances?",
146
+ :default => manifest("instances") || DEFAULTS["instances"]
147
+ ), "instances"
148
+
149
+ unless manifest "services"
150
+ user_services = client.services
151
+ user_services.sort! {|a, b| a[:name] <=> b[:name] }
152
+
153
+ unless user_services.empty?
154
+ if ask "Bind existing services to '#{name}'?", :default => false
155
+ bind_services(user_services)
156
+ end
157
+ end
158
+
159
+ services = client.services_info
160
+ unless services.empty?
161
+ if ask "Create services to bind to '#{name}'?", :default => false
162
+ create_services(services.values.collect(&:keys).flatten)
163
+ end
164
+ end
165
+ end
166
+
167
+ if many and ask("Configure for another application?", :default => false)
168
+ @application = ask "Application path?"
169
+ configure_app
170
+ end
171
+ end
172
+
173
+ def set(what, *where)
174
+ where.unshift "applications", @application
175
+
176
+ which = @manifest
177
+ where.each_with_index do |k, i|
178
+ if i + 1 == where.size
179
+ which[k] = what
180
+ else
181
+ which = (which[k] ||= {})
182
+ end
183
+ end
184
+
185
+ what
186
+ end
187
+
188
+ # Detect the appropriate framework.
189
+ def detect_framework(prompt_ok = true)
190
+ framework = VMC::Cli::Framework.detect(@application, frameworks_info)
191
+ framework_correct = ask("Detected a #{framework}, is this correct?", :default => true) if prompt_ok && framework
192
+ if prompt_ok && (framework.nil? || !framework_correct)
193
+ display "#{"[WARNING]".yellow} Can't determine the Application Type." unless framework
194
+ framework = nil if !framework_correct
195
+ framework = VMC::Cli::Framework.lookup(
196
+ ask(
197
+ "Select Application Type",
198
+ :indexed => true,
199
+ :default => framework,
200
+ :choices => VMC::Cli::Framework.known_frameworks(frameworks_info)
201
+ )
202
+ )
203
+ display "Selected #{framework}"
204
+ end
205
+
206
+ framework
207
+ end
208
+
209
+ # Detect the appropriate runtime.
210
+ def detect_runtime(default, prompt_ok=true)
211
+ runtime = nil
212
+ runtime_keys=[]
213
+ runtimes_info.keys.each {|runtime_key| runtime_keys << runtime_key.dup }
214
+ runtime_keys.sort!
215
+ if prompt_ok
216
+ runtime = ask(
217
+ "Select Runtime",
218
+ :indexed => true,
219
+ :default => default,
220
+ :choices => runtime_keys
221
+ )
222
+ display "Selected #{runtime}"
223
+ end
224
+ runtime
225
+ end
226
+
227
+ def bind_services(user_services, chosen = 0)
228
+ svcname = ask(
229
+ "Which one?",
230
+ :indexed => true,
231
+ :choices => user_services.collect { |p| p[:name] })
232
+
233
+ svc = user_services.find { |p| p[:name] == svcname }
234
+
235
+ set svc[:vendor], "services", svcname, "type"
236
+
237
+ if chosen + 1 < user_services.size && ask("Bind another?", :default => false)
238
+ bind_services(user_services, chosen + 1)
239
+ end
240
+ end
241
+
242
+ def create_services(services)
243
+ svcs = services.collect(&:to_s).sort!
244
+
245
+ configure_service(
246
+ ask(
247
+ "What kind of service?",
248
+ :indexed => true,
249
+ :choices => svcs
250
+ )
251
+ )
252
+
253
+ if ask "Create another?", :default => false
254
+ create_services(services)
255
+ end
256
+ end
257
+
258
+ def configure_service(vendor)
259
+ default_name = random_service_name(vendor)
260
+ name = ask "Specify the name of the service", :default => default_name
261
+
262
+ set vendor, "services", name, "type"
263
+ end
264
+
265
+ private
266
+ def ordered_by_deps(apps, abspaths = nil, processed = Set[])
267
+ unless abspaths
268
+ abspaths = {}
269
+ apps.each do |p, i|
270
+ ep = File.expand_path("../" + p, manifest_file)
271
+ abspaths[ep] = i
272
+ end
273
+ end
274
+
275
+ ordered = []
276
+ apps.each do |path, info|
277
+ epath = File.expand_path("../" + path, manifest_file)
278
+
279
+ if deps = info["depends-on"]
280
+ dep_apps = {}
281
+ deps.each do |dep|
282
+ edep = File.expand_path("../" + dep, manifest_file)
283
+
284
+ err "Circular dependency detected." if processed.include? edep
285
+
286
+ dep_apps[dep] = abspaths[edep]
287
+ end
288
+
289
+ processed.add(epath)
290
+
291
+ ordered += ordered_by_deps(dep_apps, abspaths, processed)
292
+ ordered << [path, info]
293
+ elsif not processed.include? epath
294
+ ordered << [path, info]
295
+ processed.add(epath)
296
+ end
297
+ end
298
+
299
+ ordered
300
+ end
301
+
302
+ end
@@ -0,0 +1,543 @@
1
+
2
+ require 'optparse'
3
+
4
+ require File.dirname(__FILE__) + '/usage'
5
+
6
+ class VMC::Cli::Runner
7
+
8
+ attr_reader :namespace
9
+ attr_reader :action
10
+ attr_reader :args
11
+ attr_reader :options
12
+
13
+ def self.run(args)
14
+ new(args).run
15
+ end
16
+
17
+ def initialize(args=[])
18
+ @args = args
19
+ @options = { :colorize => true }
20
+ @exit_status = true
21
+ end
22
+
23
+ # Collect all the available options for all commands
24
+ # Some duplicates exists to capture all scenarios
25
+ def parse_options!
26
+ opts_parser = OptionParser.new do |opts|
27
+ opts.banner = "\nAvailable options:\n\n"
28
+
29
+ opts.on('--email EMAIL') { |email| @options[:email] = email }
30
+ opts.on('--user EMAIL') { |email| @options[:email] = email }
31
+ opts.on('--passwd PASS') { |pass| @options[:password] = pass }
32
+ opts.on('--pass PASS') { |pass| @options[:password] = pass }
33
+ opts.on('--password PASS') { |pass| @options[:password] = pass }
34
+ opts.on('--token-file TOKEN_FILE') { |token_file| @options[:token_file] = token_file }
35
+ opts.on('--app NAME') { |name| @options[:name] = name }
36
+ opts.on('--name NAME') { |name| @options[:name] = name }
37
+ opts.on('--bind BIND') { |bind| @options[:bind] = bind }
38
+ opts.on('--instance INST') { |inst| @options[:instance] = inst }
39
+ opts.on('--instances INST') { |inst| @options[:instances] = inst }
40
+ opts.on('--url URL') { |url| @options[:url] = url }
41
+ opts.on('--mem MEM') { |mem| @options[:mem] = mem }
42
+ opts.on('--path PATH') { |path| @options[:path] = path }
43
+ opts.on('--no-start') { @options[:nostart] = true }
44
+ opts.on('--nostart') { @options[:nostart] = true }
45
+ opts.on('--force') { @options[:force] = true }
46
+ opts.on('--all') { @options[:all] = true }
47
+
48
+ # generic tracing and debugging
49
+ opts.on('-t [TKEY]') { |tkey| @options[:trace] = tkey || true }
50
+ opts.on('--trace [TKEY]') { |tkey| @options[:trace] = tkey || true }
51
+
52
+ # start application in debug mode
53
+ opts.on('-d [MODE]') { |mode| @options[:debug] = mode || "run" }
54
+ opts.on('--debug [MODE]') { |mode| @options[:debug] = mode || "run" }
55
+
56
+ # override manifest file
57
+ opts.on('-m FILE') { |file| @options[:manifest] = file }
58
+ opts.on('--manifest FILE') { |file| @options[:manifest] = file }
59
+
60
+ opts.on('-q', '--quiet') { @options[:quiet] = true }
61
+
62
+ # micro cloud options
63
+ opts.on('--vmx FILE') { |file| @options[:vmx] = file }
64
+ opts.on('--vmrun FILE') { |file| @options[:vmrun] = file }
65
+ opts.on('--save') { @options[:save] = true }
66
+
67
+ # Don't use builtin zip
68
+ opts.on('--no-zip') { @options[:nozip] = true }
69
+ opts.on('--nozip') { @options[:nozip] = true }
70
+
71
+ opts.on('--no-resources') { @options[:noresources] = true }
72
+ opts.on('--noresources') { @options[:noresources] = true }
73
+
74
+ opts.on('--no-color') { @options[:colorize] = false }
75
+ opts.on('--verbose') { @options[:verbose] = true }
76
+
77
+ opts.on('-n','--no-prompt') { @options[:noprompts] = true }
78
+ opts.on('--noprompt') { @options[:noprompts] = true }
79
+ opts.on('--non-interactive') { @options[:noprompts] = true }
80
+
81
+ opts.on('--prefix') { @options[:prefixlogs] = true }
82
+ opts.on('--prefix-logs') { @options[:prefixlogs] = true }
83
+ opts.on('--prefixlogs') { @options[:prefixlogs] = true }
84
+
85
+ opts.on('--json') { @options[:json] = true }
86
+
87
+ opts.on('-v', '--version') { set_cmd(:misc, :version) }
88
+ opts.on('-h', '--help') { puts "#{command_usage}\n"; exit }
89
+
90
+ opts.on('--port PORT') { |port| @options[:port] = port }
91
+
92
+ opts.on('--runtime RUNTIME') { |rt| @options[:runtime] = rt }
93
+
94
+ # deprecated
95
+ opts.on('--exec EXEC') { |exec| @options[:exec] = exec }
96
+ opts.on('--noframework') { @options[:noframework] = true }
97
+ opts.on('--canary') { @options[:canary] = true }
98
+
99
+ # Proxying for another user, requires admin privileges
100
+ opts.on('-u PROXY') { |proxy| @options[:proxy] = proxy }
101
+
102
+ opts.on('--token TOKEN') { |token| @options[:token] = token }
103
+ opts.on('--realm REALM') { |realm| @options[:realm] = realm }
104
+
105
+ opts.on_tail('--options') { puts "#{opts}\n"; exit }
106
+ end
107
+ instances_delta_arg = check_instances_delta!
108
+ @args = opts_parser.parse!(@args)
109
+ @args.concat instances_delta_arg
110
+ convert_options!
111
+ self
112
+ end
113
+
114
+ def check_instances_delta!
115
+ return unless @args
116
+ instance_args = @args.select { |arg| /^[-]\d+$/ =~ arg } || []
117
+ @args.delete_if { |arg| instance_args.include? arg}
118
+ instance_args
119
+ end
120
+
121
+ def display_help
122
+ puts command_usage
123
+ exit
124
+ end
125
+
126
+ def convert_options!
127
+ # make sure certain options are valid and in correct form.
128
+ @options[:instances] = Integer(@options[:instances]) if @options[:instances]
129
+ end
130
+
131
+ def set_cmd(namespace, action, args_range=0)
132
+ return if @help_only
133
+ unless args_range == "*" || args_range.is_a?(Range)
134
+ args_range = (args_range.to_i..args_range.to_i)
135
+ end
136
+
137
+ if args_range == "*" || args_range.include?(@args.size)
138
+ @namespace = namespace
139
+ @action = action
140
+ else
141
+ @exit_status = false
142
+ if @args.size > args_range.last
143
+ usage_error("Too many arguments for [#{action}]: %s" % [ @args[args_range.last..-1].map{|a| "'#{a}'"}.join(', ') ])
144
+ else
145
+ usage_error("Not enough arguments for [#{action}]")
146
+ end
147
+ end
148
+ end
149
+
150
+ def parse_command!
151
+ # just return if already set, happends with -v, -h
152
+ return if @namespace && @action
153
+
154
+ verb = @args.shift
155
+ case verb
156
+
157
+ when 'version'
158
+ usage('vmcu version')
159
+ set_cmd(:misc, :version)
160
+
161
+ when 'target'
162
+ usage('vmcu target [url] [--url]')
163
+ if @args.size == 1
164
+ set_cmd(:misc, :set_target, 1)
165
+ else
166
+ set_cmd(:misc, :target)
167
+ end
168
+
169
+ when 'cloud-team'
170
+ usage('vmcu cloud-team [name or id] [--realm REALM]')
171
+ set_cmd(:misc, :set_cloud_team, 0) if @args.size == 0
172
+ set_cmd(:misc, :set_cloud_team, 1) if @args.size == 1
173
+
174
+ when 'import-uhuru'
175
+ usage('vmcu import-uhuru')
176
+ set_cmd(:misc, :import_from_registry, 0) if @args.size == 0
177
+
178
+ when 'targets'
179
+ usage('vmcu targets')
180
+ set_cmd(:misc, :targets)
181
+
182
+ when 'tokens'
183
+ usage('vmcu tokens')
184
+ set_cmd(:misc, :tokens)
185
+
186
+ when 'info'
187
+ usage('vmcu info')
188
+ set_cmd(:misc, :info)
189
+
190
+ when 'runtimes'
191
+ usage('vmcu runtimes')
192
+ set_cmd(:misc, :runtimes)
193
+
194
+ when 'frameworks'
195
+ usage('vmcu frameworks')
196
+ set_cmd(:misc, :frameworks)
197
+
198
+ when 'user'
199
+ usage('vmcu user')
200
+ set_cmd(:user, :info)
201
+
202
+ when 'login'
203
+ usage('vmcu login [email] [--email EMAIL] [--passwd PASS] [--token UHURU_TOKEN]')
204
+ if @args.size == 1
205
+ set_cmd(:user, :login, 1)
206
+ else
207
+ set_cmd(:user, :login)
208
+ end
209
+
210
+ when 'logout'
211
+ usage('vmcu logout')
212
+ set_cmd(:user, :logout)
213
+
214
+ when 'passwd'
215
+ usage('vmcu passwd')
216
+ if @args.size == 1
217
+ set_cmd(:user, :change_password, 1)
218
+ else
219
+ set_cmd(:user, :change_password)
220
+ end
221
+
222
+ when 'add-user', 'add_user', 'create_user', 'create-user', 'register'
223
+ usage('vmcu add-user [user] [--email EMAIL] [--passwd PASS]')
224
+ if @args.size == 1
225
+ set_cmd(:admin, :add_user, 1)
226
+ else
227
+ set_cmd(:admin, :add_user)
228
+ end
229
+
230
+ when 'delete-user', 'delete_user', 'unregister'
231
+ usage('vmcu delete-user <user>')
232
+ set_cmd(:admin, :delete_user, 1)
233
+
234
+ when 'users'
235
+ usage('vmcu users')
236
+ set_cmd(:admin, :users)
237
+
238
+ when 'apps'
239
+ usage('vmcu apps')
240
+ set_cmd(:apps, :apps)
241
+
242
+ when 'list'
243
+ usage('vmcu list')
244
+ set_cmd(:apps, :list)
245
+
246
+ when 'start'
247
+ usage('vmcu start <appname>')
248
+ set_cmd(:apps, :start, @args.size == 1 ? 1 : 0)
249
+
250
+ when 'stop'
251
+ usage('vmcu stop <appname>')
252
+ set_cmd(:apps, :stop, @args.size == 1 ? 1 : 0)
253
+
254
+ when 'restart'
255
+ usage('vmcu restart <appname>')
256
+ set_cmd(:apps, :restart, @args.size == 1 ? 1 : 0)
257
+
258
+ when 'mem'
259
+ usage('vmcu mem <appname> [memsize]')
260
+ if @args.size == 2
261
+ set_cmd(:apps, :mem, 2)
262
+ else
263
+ set_cmd(:apps, :mem, 1)
264
+ end
265
+
266
+ when 'stats'
267
+ usage('vmcu stats <appname>')
268
+ set_cmd(:apps, :stats, @args.size == 1 ? 1 : 0)
269
+
270
+ when 'map'
271
+ usage('vmcu map <appname> <url>')
272
+ set_cmd(:apps, :map, 2)
273
+
274
+ when 'unmap'
275
+ usage('vmcu unmap <appname> <url>')
276
+ set_cmd(:apps, :unmap, 2)
277
+
278
+ when 'delete'
279
+ usage('vmcu delete <appname>')
280
+ if @options[:all] && @args.size == 0
281
+ set_cmd(:apps, :delete)
282
+ else
283
+ set_cmd(:apps, :delete, 1)
284
+ end
285
+
286
+ when 'files'
287
+ usage('vmcu files <appname> [path] [--instance N] [--all] [--prefix]')
288
+ if @args.size == 1
289
+ set_cmd(:apps, :files, 1)
290
+ else
291
+ set_cmd(:apps, :files, 2)
292
+ end
293
+
294
+ when 'logs'
295
+ usage('vmcu logs <appname> [--instance N] [--all] [--prefix]')
296
+ set_cmd(:apps, :logs, 1)
297
+
298
+ when 'instances', 'scale'
299
+ if @args.size > 1
300
+ usage('vmcu instances <appname> <num|delta>')
301
+ set_cmd(:apps, :instances, 2)
302
+ else
303
+ usage('vmcu instances <appname>')
304
+ set_cmd(:apps, :instances, 1)
305
+ end
306
+
307
+ when 'crashes'
308
+ usage('vmcu crashes <appname>')
309
+ set_cmd(:apps, :crashes, 1)
310
+
311
+ when 'crashlogs'
312
+ usage('vmcu crashlogs <appname>')
313
+ set_cmd(:apps, :crashlogs, 1)
314
+
315
+ when 'push'
316
+ usage('vmcu push [appname] [--path PATH] [--url URL] [--instances N] [--mem] [--runtime RUNTIME] [--no-start]')
317
+ if @args.size == 1
318
+ set_cmd(:apps, :push, 1)
319
+ else
320
+ set_cmd(:apps, :push, 0)
321
+ end
322
+
323
+ when 'update'
324
+ usage('vmcu update <appname> [--path PATH]')
325
+ set_cmd(:apps, :update, @args.size == 1 ? 1 : 0)
326
+
327
+ when 'services'
328
+ usage('vmcu services')
329
+ set_cmd(:services, :services)
330
+
331
+ when 'env'
332
+ usage('vmcu env <appname>')
333
+ set_cmd(:apps, :environment, 1)
334
+
335
+ when 'env-add'
336
+ usage('vmcu env-add <appname> <variable[=]value>')
337
+ if @args.size == 2
338
+ set_cmd(:apps, :environment_add, 2)
339
+ elsif @args.size == 3
340
+ set_cmd(:apps, :environment_add, 3)
341
+ end
342
+
343
+ when 'env-del'
344
+ usage('vmcu env-del <appname> <variable>')
345
+ set_cmd(:apps, :environment_del, 2)
346
+
347
+ when 'create-service', 'create_service'
348
+ usage('vmcu create-service [service] [servicename] [appname] [--name servicename] [--bind appname]')
349
+ set_cmd(:services, :create_service) if @args.size == 0
350
+ set_cmd(:services, :create_service, 1) if @args.size == 1
351
+ set_cmd(:services, :create_service, 2) if @args.size == 2
352
+ set_cmd(:services, :create_service, 3) if @args.size == 3
353
+
354
+ when 'delete-service', 'delete_service'
355
+ usage('vmcu delete-service <service>')
356
+ if @args.size == 1
357
+ set_cmd(:services, :delete_service, 1)
358
+ else
359
+ set_cmd(:services, :delete_service)
360
+ end
361
+
362
+ when 'bind-service', 'bind_service'
363
+ usage('vmcu bind-service <servicename> <appname>')
364
+ set_cmd(:services, :bind_service, 2)
365
+
366
+ when 'unbind-service', 'unbind_service'
367
+ usage('vmcu unbind-service <servicename> <appname>')
368
+ set_cmd(:services, :unbind_service, 2)
369
+
370
+ when 'clone-services'
371
+ usage('vmcu clone-services <src-app> <dest-app>')
372
+ set_cmd(:services, :clone_services, 2)
373
+
374
+ when 'aliases'
375
+ usage('vmcu aliases')
376
+ set_cmd(:misc, :aliases)
377
+
378
+ when 'alias'
379
+ usage('vmcu alias <alias[=]command>')
380
+ if @args.size == 1
381
+ set_cmd(:misc, :alias, 1)
382
+ elsif @args.size == 2
383
+ set_cmd(:misc, :alias, 2)
384
+ end
385
+
386
+ when 'unalias'
387
+ usage('vmcu unalias <alias>')
388
+ set_cmd(:misc, :unalias, 1)
389
+
390
+ when 'tunnel'
391
+ usage('vmcu tunnel [servicename] [clientcmd] [--port port]')
392
+ set_cmd(:services, :tunnel, 0) if @args.size == 0
393
+ set_cmd(:services, :tunnel, 1) if @args.size == 1
394
+ set_cmd(:services, :tunnel, 2) if @args.size == 2
395
+
396
+ when 'rails-console'
397
+ usage('vmcu rails-console <appname>')
398
+ set_cmd(:apps, :console, 1)
399
+
400
+ when 'micro'
401
+ usage('vmcu micro <online|offline|status> [--password password] [--save] [--vmx file] [--vmrun executable]')
402
+ if %w[online offline status].include?(@args[0])
403
+ set_cmd(:micro, @args[0].to_sym, 1)
404
+ end
405
+
406
+ when 'help'
407
+ display_help if @args.size == 0
408
+ @help_only = true
409
+ parse_command!
410
+
411
+ when 'usage'
412
+ display basic_usage
413
+ exit(true)
414
+
415
+ when 'options'
416
+ # Simulate --options
417
+ @args = @args.unshift('--options')
418
+ parse_options!
419
+
420
+ when 'manifest'
421
+ usage('vmcu manifest')
422
+ set_cmd(:manifest, :edit)
423
+
424
+ when 'extend-manifest'
425
+ usage('vmcu extend-manifest')
426
+ set_cmd(:manifest, :extend, 1)
427
+
428
+ else
429
+ if verb
430
+ display "vmcu: Unknown command [#{verb}]"
431
+ display basic_usage
432
+ exit(false)
433
+ end
434
+ end
435
+ end
436
+
437
+ def process_aliases!
438
+ return if @args.empty?
439
+ aliases = VMC::Cli::Config.aliases
440
+ aliases.each_pair do |k,v|
441
+ if @args[0] == k
442
+ display "[#{@args[0]} aliased to #{aliases.invert[key]}]" if @options[:verbose]
443
+ @args[0] = v
444
+ break;
445
+ end
446
+ end
447
+ end
448
+
449
+ def usage(msg = nil)
450
+ @usage = msg if msg
451
+ @usage
452
+ end
453
+
454
+ def usage_error(msg = nil)
455
+ @usage_error = msg if msg
456
+ @usage_error
457
+ end
458
+
459
+ def run
460
+
461
+ trap('TERM') { print "\nTerminated\n"; exit(false)}
462
+
463
+ parse_options!
464
+
465
+ @options[:colorize] = false unless STDOUT.tty?
466
+
467
+ VMC::Cli::Config.colorize = @options.delete(:colorize)
468
+ VMC::Cli::Config.nozip = @options.delete(:nozip)
469
+ VMC::Cli::Config.trace = @options.delete(:trace)
470
+ VMC::Cli::Config.output ||= STDOUT unless @options[:quiet]
471
+
472
+ process_aliases!
473
+ parse_command!
474
+
475
+ if @namespace && @action
476
+ cmd = VMC::Cli::Command.const_get(@namespace.to_s.capitalize)
477
+ cmd.new(@options).send(@action, *@args.collect(&:dup))
478
+ elsif @help_only || @usage
479
+ display_usage
480
+ else
481
+ display basic_usage
482
+ exit(false)
483
+ end
484
+
485
+ rescue OptionParser::InvalidOption => e
486
+ puts(e.message.red)
487
+ puts("\n")
488
+ puts(basic_usage)
489
+ @exit_status = false
490
+ rescue OptionParser::AmbiguousOption => e
491
+ puts(e.message.red)
492
+ puts("\n")
493
+ puts(basic_usage)
494
+ @exit_status = false
495
+ rescue VMC::Client::AuthError => e
496
+ if VMC::Cli::Config.auth_token.nil?
497
+ puts "Login Required".red
498
+ else
499
+ puts "Not Authorized".red
500
+ end
501
+ @exit_status = false
502
+ rescue VMC::Client::TargetError, VMC::Client::NotFound, VMC::Client::BadTarget => e
503
+ puts e.message.red
504
+ @exit_status = false
505
+ rescue VMC::Client::HTTPException => e
506
+ puts e.message.red
507
+ @exit_status = false
508
+ rescue VMC::Cli::GracefulExit => e
509
+ # Redirected commands end up generating this exception (kind of goto)
510
+ rescue VMC::Cli::CliExit => e
511
+ puts e.message.red
512
+ @exit_status = false
513
+ rescue VMC::Cli::CliError => e
514
+ say("Error #{e.error_code}: #{e.message}".red)
515
+ @exit_status = false
516
+ rescue SystemExit => e
517
+ @exit_status = e.success?
518
+ rescue SyntaxError => e
519
+ puts e.message.red
520
+ puts e.backtrace
521
+ @exit_status = false
522
+ rescue Interrupt => e
523
+ say("\nInterrupted".red)
524
+ @exit_status = false
525
+ rescue Exception => e
526
+ puts e.message.red
527
+ puts e.backtrace
528
+ @exit_status = false
529
+ ensure
530
+ say("\n")
531
+ @exit_status == true if @exit_status.nil?
532
+ if @options[:verbose]
533
+ if @exit_status
534
+ puts "[#{@namespace}:#{@action}] SUCCEEDED".green
535
+ else
536
+ puts "[#{@namespace}:#{@action}] FAILED".red
537
+ end
538
+ say("\n")
539
+ end
540
+ exit(@exit_status)
541
+ end
542
+
543
+ end