jdc 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,265 @@
1
+ module JDC::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*require[\s\(]*['"]sinatra(\/base)?['"]/))
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
@@ -0,0 +1,302 @@
1
+ require "set"
2
+
3
+ module JDC::Cli::ManifestHelper
4
+ include JDC::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 = JDC::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 = JDC::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 = JDC::Cli::Framework.lookup(
196
+ ask(
197
+ "Select Application Type",
198
+ :indexed => true,
199
+ :default => framework,
200
+ :choices => JDC::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