sys 0.1

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