udn 0.3.23.0.pre

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