sys 0.1

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 (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,163 @@
1
+ require 'net/telnet'
2
+ require 'readline'
3
+
4
+ module VMC::Cli
5
+ module ConsoleHelper
6
+
7
+ def console_connection_info(appname)
8
+ app = client.app_info(appname)
9
+ fw = VMC::Cli::Framework.lookup_by_framework(app[:staging][:model])
10
+ if !fw.console
11
+ err "'#{appname}' is a #{fw.name} application. " +
12
+ "Console access is not supported for #{fw.name} applications."
13
+ end
14
+ instances_info_envelope = client.app_instances(appname)
15
+ instances_info_envelope = {} if instances_info_envelope.is_a?(Array)
16
+
17
+ instances_info = instances_info_envelope[:instances] || []
18
+ err "No running instances for [#{appname}]" if instances_info.empty?
19
+
20
+ entry = instances_info[0]
21
+ if !entry[:console_port]
22
+ begin
23
+ client.app_files(appname, '/app/cf-rails-console')
24
+ err "Console port not provided for [#{appname}]. Try restarting the app."
25
+ rescue VMC::Client::TargetError, VMC::Client::NotFound
26
+ err "Console access not supported for [#{appname}]. " +
27
+ "Please redeploy your app to enable support."
28
+ end
29
+ end
30
+ conn_info = {
31
+ 'hostname' => entry[:console_ip],
32
+ 'port' => entry[:console_port]
33
+ }
34
+ end
35
+
36
+ def start_local_console(port, appname)
37
+ auth_info = console_credentials(appname)
38
+ display "Connecting to '#{appname}' console: ", false
39
+ prompt = console_login(auth_info, port)
40
+ display "OK".green
41
+ display "\n"
42
+ initialize_readline
43
+ run_console prompt
44
+ end
45
+
46
+ def console_login(auth_info, port)
47
+ if !auth_info["username"] || !auth_info["password"]
48
+ err "Unable to verify console credentials."
49
+ end
50
+ @telnet_client = telnet_client(port)
51
+ prompt = nil
52
+ err_msg = "Login attempt timed out."
53
+ 5.times do
54
+ begin
55
+ results = @telnet_client.login("Name"=>auth_info["username"],
56
+ "Password"=>auth_info["password"])
57
+ lines = results.sub("Login: Password: ", "").split("\n")
58
+ last_line = lines.pop
59
+ if last_line =~ /[$%#>] \z/n
60
+ prompt = last_line
61
+ elsif last_line =~ /Login failed/
62
+ err_msg = last_line
63
+ end
64
+ break
65
+ rescue TimeoutError
66
+ sleep 1
67
+ rescue EOFError
68
+ #This may happen if we login right syster app starts
69
+ close_console
70
+ sleep 5
71
+ @telnet_client = telnet_client(port)
72
+ end
73
+ display ".", false
74
+ end
75
+ unless prompt
76
+ close_console
77
+ err err_msg
78
+ end
79
+ prompt
80
+ end
81
+
82
+ def send_console_command(cmd)
83
+ results = @telnet_client.cmd(cmd)
84
+ results.split("\n")
85
+ end
86
+
87
+ def console_credentials(appname)
88
+ content = client.app_files(appname, '/app/cf-rails-console/.consoleaccess', '0')
89
+ YAML.load(content)
90
+ end
91
+
92
+ def close_console
93
+ @telnet_client.close
94
+ end
95
+
96
+ def console_tab_completion_data(cmd)
97
+ begin
98
+ results = @telnet_client.cmd("String"=> cmd + "\t", "Match"=>/\S*\n$/, "Timeout"=>10)
99
+ results.chomp.split(",")
100
+ rescue TimeoutError
101
+ [] #Just return empty results if timeout occurred on tab completion
102
+ end
103
+ end
104
+
105
+ private
106
+ def telnet_client(port)
107
+ Net::Telnet.new({"Port"=>port, "Prompt"=>/[$%#>] \z|Login failed/n, "Timeout"=>30, "FailEOF"=>true})
108
+ end
109
+
110
+ def readline_with_history(prompt)
111
+ line = Readline::readline(prompt)
112
+ return nil if line == nil || line == 'quit' || line == 'exit'
113
+ Readline::HISTORY.push(line) if not line =~ /^\s*$/ and Readline::HISTORY.to_a[-1] != line
114
+ line
115
+ end
116
+
117
+ def run_console(prompt)
118
+ prev = trap("INT") { |x| exit_console; prev.call(x); exit }
119
+ prev = trap("TERM") { |x| exit_console; prev.call(x); exit }
120
+ loop do
121
+ cmd = readline_with_history(prompt)
122
+ if(cmd == nil)
123
+ exit_console
124
+ break
125
+ end
126
+ prompt = send_console_command_display_results(cmd, prompt)
127
+ end
128
+ end
129
+
130
+ def exit_console
131
+ #TimeoutError expected, as exit doesn't return anything
132
+ @telnet_client.cmd("String"=>"exit","Timeout"=>1) rescue TimeoutError
133
+ close_console
134
+ end
135
+
136
+ def send_console_command_display_results(cmd, prompt)
137
+ begin
138
+ lines = send_console_command cmd
139
+ #Assumes the last line is a prompt
140
+ prompt = lines.pop
141
+ lines.each {|line| display line if line != cmd}
142
+ rescue TimeoutError
143
+ display "Timed out sending command to server.".red
144
+ rescue EOFError
145
+ err "The console connection has been terminated. Perhaps the app was stopped or deleted?"
146
+ end
147
+ prompt
148
+ end
149
+
150
+ def initialize_readline
151
+ if Readline.respond_to?("basic_word_break_characters=")
152
+ Readline.basic_word_break_characters= " \t\n`><=;|&{("
153
+ end
154
+ Readline.completion_append_character = nil
155
+ #Assumes that sending a String ending with tab will return a non-empty
156
+ #String of comma-separated completion options, terminated by a new line
157
+ #For example, "app.\t" might result in "to_s,nil?,etc\n"
158
+ Readline.completion_proc = proc {|s|
159
+ console_tab_completion_data s
160
+ }
161
+ end
162
+ end
163
+ end
@@ -0,0 +1,122 @@
1
+ module VMCExtensions
2
+
3
+ def say(message)
4
+ VMC::Cli::Config.output.puts(message) if VMC::Cli::Config.output
5
+ end
6
+
7
+ def header(message, filler = '-')
8
+ say "\n"
9
+ say message
10
+ say filler.to_s * message.size
11
+ end
12
+
13
+ def banner(message)
14
+ say "\n"
15
+ say message
16
+ end
17
+
18
+ def display(message, nl=true)
19
+ if nl
20
+ say message
21
+ else
22
+ if VMC::Cli::Config.output
23
+ VMC::Cli::Config.output.print(message)
24
+ VMC::Cli::Config.output.flush
25
+ end
26
+ end
27
+ end
28
+
29
+ def clear(size=80)
30
+ return unless VMC::Cli::Config.output
31
+ VMC::Cli::Config.output.print("\r")
32
+ VMC::Cli::Config.output.print(" " * size)
33
+ VMC::Cli::Config.output.print("\r")
34
+ #VMC::Cli::Config.output.flush
35
+ end
36
+
37
+ def err(message, prefix='Error: ')
38
+ raise VMC::Cli::CliExit, "#{prefix}#{message}"
39
+ end
40
+
41
+ def warn(msg)
42
+ say "#{"[WARNING]".yellow} #{msg}"
43
+ end
44
+
45
+ def quit(message = nil)
46
+ raise VMC::Cli::GracefulExit, message
47
+ end
48
+
49
+ def blank?
50
+ self.to_s.blank?
51
+ end
52
+
53
+ def uptime_string(delta)
54
+ num_seconds = delta.to_i
55
+ days = num_seconds / (60 * 60 * 24);
56
+ num_seconds -= days * (60 * 60 * 24);
57
+ hours = num_seconds / (60 * 60);
58
+ num_seconds -= hours * (60 * 60);
59
+ minutes = num_seconds / 60;
60
+ num_seconds -= minutes * 60;
61
+ "#{days}d:#{hours}h:#{minutes}m:#{num_seconds}s"
62
+ end
63
+
64
+ def pretty_size(size, prec=1)
65
+ return 'NA' unless size
66
+ return "#{size}B" if size < 1024
67
+ return sprintf("%.#{prec}fK", size/1024.0) if size < (1024*1024)
68
+ return sprintf("%.#{prec}fM", size/(1024.0*1024.0)) if size < (1024*1024*1024)
69
+ return sprintf("%.#{prec}fG", size/(1024.0*1024.0*1024.0))
70
+ end
71
+ end
72
+
73
+ module VMCStringExtensions
74
+
75
+ def red
76
+ colorize("\e[0m\e[31m")
77
+ end
78
+
79
+ def green
80
+ colorize("\e[0m\e[32m")
81
+ end
82
+
83
+ def yellow
84
+ colorize("\e[0m\e[33m")
85
+ end
86
+
87
+ def bold
88
+ colorize("\e[0m\e[1m")
89
+ end
90
+
91
+ def colorize(color_code)
92
+ if VMC::Cli::Config.colorize
93
+ "#{color_code}#{self}\e[0m"
94
+ else
95
+ self
96
+ end
97
+ end
98
+
99
+ def blank?
100
+ self =~ /^\s*$/
101
+ end
102
+
103
+ def truncate(limit = 30)
104
+ return "" if self.blank?
105
+ etc = "..."
106
+ stripped = self.strip[0..limit]
107
+ if stripped.length > limit
108
+ stripped.gsub(/\s+?(\S+)?$/, "") + etc
109
+ else
110
+ stripped
111
+ end
112
+ end
113
+
114
+ end
115
+
116
+ class Object
117
+ include VMCExtensions
118
+ end
119
+
120
+ class String
121
+ include VMCStringExtensions
122
+ end
data/lib/cli/errors.rb ADDED
@@ -0,0 +1,19 @@
1
+ module VMC::Cli
2
+
3
+ class CliError < StandardError
4
+ def self.error_code(code = nil)
5
+ define_method(:error_code) { code }
6
+ end
7
+ end
8
+
9
+ class UnknownCommand < CliError; error_code(100); end
10
+ class TargetMissing < CliError; error_code(102); end
11
+ class TargetInaccessible < CliError; error_code(103); end
12
+
13
+ class TargetError < CliError; error_code(201); end
14
+ class AuthError < TargetError; error_code(202); end
15
+
16
+ class CliExit < CliError; error_code(400); end
17
+ class GracefulExit < CliExit; error_code(401); end
18
+
19
+ end
@@ -0,0 +1,123 @@
1
+ module VMC::Cli
2
+ module FileHelper
3
+
4
+ class SYSIgnore
5
+
6
+ def initialize(patterns,project_root = "")
7
+ @patterns = patterns + [ ".git/" ]
8
+ @project_root = project_root
9
+ end
10
+
11
+ def included_files(filenames)
12
+ exclude_dots_only(filenames).reject do |filename|
13
+ exclude = false
14
+ @patterns.each do |pattern|
15
+ if is_negative_pattern?(pattern)
16
+ exclude = false if negative_match(pattern,filename)
17
+ else
18
+ exclude ||= match(pattern,filename)
19
+ end
20
+ end
21
+ exclude
22
+ end
23
+ end
24
+
25
+ def exclude_dots_only(filenames)
26
+ filenames.reject do |filename|
27
+ base = File.basename(filename)
28
+ base == "." || base == ".."
29
+ end
30
+ end
31
+
32
+
33
+
34
+ def excluded_files(filenames)
35
+ filenames - included_files(filenames)
36
+ end
37
+
38
+ def self.from_file(project_root)
39
+ f = "#{project_root}/.sysignore"
40
+ if File.exists?(f)
41
+ contents = File.read(f).split("\n")
42
+ SYSIgnore.new(contents,project_root)
43
+ else
44
+ SYSIgnore.new([],project_root)
45
+ end
46
+ end
47
+
48
+ def match(pattern,filename)
49
+
50
+ filename = filename.sub(/^#{@project_root}\//,'') # remove any project directory prefix
51
+
52
+ return false if pattern =~ /^\s*$/ # ignore blank lines
53
+
54
+ return false if pattern =~ /^#/ # lines starting with # are comments
55
+
56
+ return false if pattern =~ /^!/ # lines starting with ! are negated
57
+
58
+ if pattern =~ /\/$/
59
+ # pattern ending in a slash should ignore directory and all its children
60
+ dirname = pattern.sub(/\/$/,'')
61
+ return filename == dirname || filename =~ /^#{dirname}\/.*$/
62
+ end
63
+
64
+ if pattern =~ /^\//
65
+ parts = filename.split('/')
66
+ return File.fnmatch(pattern.sub(/^\//,''),parts[0])
67
+ end
68
+
69
+ if pattern.include? '/'
70
+ return File.fnmatch(pattern,filename)
71
+ end
72
+
73
+ File.fnmatch(pattern,filename,File::FNM_PATHNAME)
74
+ end
75
+
76
+ def is_negative_pattern?(pattern)
77
+ pattern =~ /^!/
78
+ end
79
+
80
+ def negative_match(pattern,filename)
81
+ return false unless pattern =~ /^!/
82
+ match(pattern.sub(/^!/,''),filename)
83
+ end
84
+
85
+ end
86
+
87
+ def ignore_sockets(files)
88
+ files.reject { |f| File.socket? f }
89
+ end
90
+
91
+ def check_unreachable_links(path,files)
92
+ pwd = Pathname.new(path)
93
+ abspath = pwd.realpath.to_s
94
+ unreachable = []
95
+ files.each do |f|
96
+ file = Pathname.new(f)
97
+ if file.symlink? && !file.realpath.to_s.start_with?(abspath)
98
+ unreachable << file.relative_path_from(pwd).to_s
99
+ end
100
+ end
101
+
102
+ unless unreachable.empty?
103
+ root = pwd.relative_path_from(pwd).to_s
104
+ err "Can't deploy application containing links '#{unreachable.join(",")}' that reach outside its root '#{root}'"
105
+ end
106
+ end
107
+
108
+ def copy_files(project_root,files,dest_dir)
109
+ project_root = Pathname.new(project_root)
110
+ files.each do |f|
111
+ dest = Pathname.new(f).relative_path_from(project_root)
112
+ if File.symlink?(f)
113
+ FileUtils.copy_entry(f,"#{dest_dir}/#{dest}")
114
+ elsif File.directory?(f)
115
+ FileUtils.mkdir_p("#{dest_dir}/#{dest}")
116
+ else
117
+ FileUtils.cp(f,"#{dest_dir}/#{dest}")
118
+ end
119
+ end
120
+ end
121
+
122
+ end
123
+ end
@@ -0,0 +1,265 @@
1
+ module VMC::Cli
2
+
3
+ class Framework
4
+
5
+ DEFAULT_FRAMEWORK = "http://b20nine.com/unknown"
6
+ DEFAULT_MEM = '256M'
7
+
8
+ FRAMEWORKS = {
9
+ 'Rails' => ['rails3', { :mem => '256M', :description => 'Rails Application', :console=>true}],
10
+ 'Spring' => ['spring', { :mem => '512M', :description => 'Java SpringSource Spring Application'}],
11
+ 'Grails' => ['grails', { :mem => '512M', :description => 'Java SpringSource Grails Application'}],
12
+ 'Lift' => ['lift', { :mem => '512M', :description => 'Scala Lift Application'}],
13
+ 'JavaWeb' => ['java_web',{ :mem => '512M', :description => 'Java Web Application'}],
14
+ 'Standalone' => ['standalone', { :mem => '64M', :description => 'Standalone Application'}],
15
+ 'Sinatra' => ['sinatra', { :mem => '128M', :description => 'Sinatra Application'}],
16
+ 'Node' => ['node', { :mem => '64M', :description => 'Node.js Application'}],
17
+ 'PHP' => ['php', { :mem => '128M', :description => 'PHP Application'}],
18
+ 'Erlang/OTP Rebar' => ['otp_rebar', { :mem => '64M', :description => 'Erlang/OTP Rebar Application'}],
19
+ 'WSGI' => ['wsgi', { :mem => '64M', :description => 'Python WSGI Application'}],
20
+ 'Django' => ['django', { :mem => '128M', :description => 'Python Django Application'}],
21
+ 'dotNet' => ['dotNet', { :mem => '128M', :description => '.Net Web Application'}],
22
+ 'Rack' => ['rack', { :mem => '128M', :description => 'Rack Application'}],
23
+ 'Play' => ['play', { :mem => '256M', :description => 'Play Framework Application'}]
24
+ }
25
+
26
+ class << self
27
+
28
+ def known_frameworks(available_frameworks)
29
+ frameworks = []
30
+ FRAMEWORKS.each do |key,fw|
31
+ frameworks << key if available_frameworks.include? [fw[0]]
32
+ end
33
+ frameworks
34
+ end
35
+
36
+ def lookup(name)
37
+ return create(*FRAMEWORKS[name])
38
+ end
39
+
40
+ def lookup_by_framework(name)
41
+ FRAMEWORKS.each do |key,fw|
42
+ return create(fw[0],fw[1]) if fw[0] == name
43
+ end
44
+ end
45
+
46
+ def create(name,opts)
47
+ if name == "standalone"
48
+ return StandaloneFramework.new(name, opts)
49
+ else
50
+ return Framework.new(name,opts)
51
+ end
52
+ end
53
+
54
+ def detect(path, available_frameworks)
55
+ if !File.directory? path
56
+ if path.end_with?('.war')
57
+ return detect_framework_from_war path
58
+ elsif path.end_with?('.zip')
59
+ return detect_framework_from_zip path, available_frameworks
60
+ elsif available_frameworks.include?(["standalone"])
61
+ return Framework.lookup('Standalone')
62
+ else
63
+ return nil
64
+ end
65
+ end
66
+ Dir.chdir(path) do
67
+ # Rails
68
+ if File.exist?('config/environment.rb')
69
+ return Framework.lookup('Rails')
70
+
71
+ # Rack
72
+ elsif File.exist?('config.ru') && available_frameworks.include?(["rack"])
73
+ return Framework.lookup('Rack')
74
+
75
+ # Java Web Apps
76
+ elsif Dir.glob('*.war').first
77
+ return detect_framework_from_war(Dir.glob('*.war').first)
78
+
79
+ elsif File.exist?('WEB-INF/web.xml')
80
+ return detect_framework_from_war
81
+
82
+ # Simple Ruby Apps
83
+ elsif !Dir.glob('*.rb').empty?
84
+ matched_file = nil
85
+ Dir.glob('*.rb').each do |fname|
86
+ next if matched_file
87
+ File.open(fname, 'r') do |f|
88
+ str = f.read # This might want to be limited
89
+ matched_file = fname if (str && str.match(/^\s*\#?\s*require\s*\(?\s*['"]sinatra['"]/))
90
+ end
91
+ end
92
+ if matched_file
93
+ # Sinatra apps
94
+ f = Framework.lookup('Sinatra')
95
+ f.exec = "ruby #{matched_file}"
96
+ return f
97
+ end
98
+
99
+ # PHP
100
+ elsif !Dir.glob('*.php').empty?
101
+ return Framework.lookup('PHP')
102
+
103
+ # Erlang/OTP using Rebar
104
+ elsif !Dir.glob('releases/*/*.rel').empty? && !Dir.glob('releases/*/*.boot').empty?
105
+ return Framework.lookup('Erlang/OTP Rebar')
106
+
107
+ # Python Django
108
+ # XXX: not all django projects keep settings.py in top-level directory
109
+ elsif File.exist?('manage.py') && File.exist?('settings.py')
110
+ return Framework.lookup('Django')
111
+
112
+ # Python
113
+ elsif !Dir.glob('wsgi.py').empty?
114
+ return Framework.lookup('WSGI')
115
+
116
+ # .Net
117
+ elsif !Dir.glob('web.config').empty?
118
+ return Framework.lookup('dotNet')
119
+
120
+ # Node.js
121
+ elsif !Dir.glob('*.js').empty?
122
+ if File.exist?('server.js') || File.exist?('app.js') || File.exist?('index.js') || File.exist?('main.js')
123
+ return Framework.lookup('Node')
124
+ end
125
+
126
+ # Play or Standalone Apps
127
+ elsif Dir.glob('*.zip').first
128
+ zip_file = Dir.glob('*.zip').first
129
+ return detect_framework_from_zip zip_file, available_frameworks
130
+ end
131
+
132
+ # Default to Standalone if no other match was made
133
+ return Framework.lookup('Standalone') if available_frameworks.include?(["standalone"])
134
+ end
135
+ end
136
+
137
+ def detect_framework_from_war(war_file=nil)
138
+ if war_file
139
+ contents = ZipUtil.entry_lines(war_file)
140
+ else
141
+ #assume we are working with current dir
142
+ contents = Dir['**/*'].join("\n")
143
+ end
144
+
145
+ # Spring/Lift Variations
146
+ if contents =~ /WEB-INF\/lib\/grails-web.*\.jar/
147
+ return Framework.lookup('Grails')
148
+ elsif contents =~ /WEB-INF\/lib\/lift-webkit.*\.jar/
149
+ return Framework.lookup('Lift')
150
+ elsif contents =~ /WEB-INF\/classes\/org\/springframework/
151
+ return Framework.lookup('Spring')
152
+ elsif contents =~ /WEB-INF\/lib\/spring-core.*\.jar/
153
+ return Framework.lookup('Spring')
154
+ elsif contents =~ /WEB-INF\/lib\/org\.springframework\.core.*\.jar/
155
+ return Framework.lookup('Spring')
156
+ else
157
+ return Framework.lookup('JavaWeb')
158
+ end
159
+ end
160
+
161
+ def detect_framework_from_zip(zip_file, available_frameworks)
162
+ contents = ZipUtil.entry_lines(zip_file)
163
+ detect_framework_from_zip_contents(contents, available_frameworks)
164
+ end
165
+
166
+ def detect_framework_from_zip_contents(contents, available_frameworks)
167
+ if available_frameworks.include?(["play"]) && contents =~ /lib\/play\..*\.jar/
168
+ return Framework.lookup('Play')
169
+ elsif available_frameworks.include?(["standalone"])
170
+ return Framework.lookup('Standalone')
171
+ end
172
+ end
173
+ end
174
+
175
+ attr_reader :name, :description, :console
176
+ attr_accessor :exec
177
+
178
+ def initialize(framework=nil, opts={})
179
+ @name = framework || DEFAULT_FRAMEWORK
180
+ @memory = opts[:mem] || DEFAULT_MEM
181
+ @description = opts[:description] || 'Unknown Application Type'
182
+ @exec = opts[:exec]
183
+ @console = opts[:console] || false
184
+ end
185
+
186
+ def to_s
187
+ description
188
+ end
189
+
190
+ def require_url?
191
+ true
192
+ end
193
+
194
+ def require_start_command?
195
+ false
196
+ end
197
+
198
+ def prompt_for_runtime?
199
+ false
200
+ end
201
+
202
+ def default_runtime(path)
203
+ nil
204
+ end
205
+
206
+ def memory(runtime=nil)
207
+ @memory
208
+ end
209
+
210
+ alias :mem :memory
211
+ end
212
+
213
+ class StandaloneFramework < Framework
214
+ def require_url?
215
+ false
216
+ end
217
+
218
+ def require_start_command?
219
+ true
220
+ end
221
+
222
+ def prompt_for_runtime?
223
+ true
224
+ end
225
+
226
+ def default_runtime(path)
227
+ if !File.directory? path
228
+ if path =~ /\.(jar|class)$/
229
+ return "java"
230
+ elsif path =~ /\.(rb)$/
231
+ return "ruby18"
232
+ elsif path =~ /\.(zip)$/
233
+ return detect_runtime_from_zip path
234
+ end
235
+ else
236
+ Dir.chdir(path) do
237
+ return "ruby18" if not Dir.glob('**/*.rb').empty?
238
+ if !Dir.glob('**/*.class').empty? || !Dir.glob('**/*.jar').empty?
239
+ return "java"
240
+ elsif Dir.glob('*.zip').first
241
+ zip_file = Dir.glob('*.zip').first
242
+ return detect_runtime_from_zip zip_file
243
+ end
244
+ end
245
+ end
246
+ return nil
247
+ end
248
+
249
+ def memory(runtime=nil)
250
+ default_mem = @memory
251
+ default_mem = '128M' if runtime =~ /\Aruby/ || runtime == "php"
252
+ default_mem = '512M' if runtime == "java" || runtime == "java7"
253
+ default_mem
254
+ end
255
+
256
+ private
257
+ def detect_runtime_from_zip(zip_file)
258
+ contents = ZipUtil.entry_lines(zip_file)
259
+ if contents =~ /\.(jar)$/
260
+ return "java"
261
+ end
262
+ end
263
+ end
264
+
265
+ end