vmcu 0.3.17

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