vmc-stic 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (43) hide show
  1. data/LICENSE +24 -0
  2. data/README.md +102 -0
  3. data/Rakefile +99 -0
  4. data/bin/vmc +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 +46 -0
  13. data/lib/cli/commands/admin.rb +80 -0
  14. data/lib/cli/commands/apps.rb +1103 -0
  15. data/lib/cli/commands/base.rb +227 -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 +180 -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 +157 -0
  23. data/lib/cli/core_ext.rb +122 -0
  24. data/lib/cli/errors.rb +19 -0
  25. data/lib/cli/frameworks.rb +142 -0
  26. data/lib/cli/manifest_helper.rb +262 -0
  27. data/lib/cli/runner.rb +532 -0
  28. data/lib/cli/services_helper.rb +84 -0
  29. data/lib/cli/tunnel_helper.rb +332 -0
  30. data/lib/cli/usage.rb +115 -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 +471 -0
  35. data/lib/vmc/const.rb +22 -0
  36. data/lib/vmc/micro.rb +56 -0
  37. data/lib/vmc/micro/switcher/base.rb +97 -0
  38. data/lib/vmc/micro/switcher/darwin.rb +19 -0
  39. data/lib/vmc/micro/switcher/dummy.rb +15 -0
  40. data/lib/vmc/micro/switcher/linux.rb +16 -0
  41. data/lib/vmc/micro/switcher/windows.rb +31 -0
  42. data/lib/vmc/micro/vmrun.rb +158 -0
  43. metadata +207 -0
@@ -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,142 @@
1
+ module VMC::Cli
2
+
3
+ class Framework
4
+
5
+ DEFAULT_FRAMEWORK = "http://b20nine.com/unknown"
6
+ DEFAULT_MEM = '256M'
7
+
8
+ FRAMEWORKS = {
9
+ 'Topaz' => ['topaz', { :mem => '128M', :description => 'Topaz for GemStone/S Application'}],
10
+ 'Rails' => ['rails3', { :mem => '256M', :description => 'Rails Application', :console=>true}],
11
+ 'Spring' => ['spring', { :mem => '512M', :description => 'Java SpringSource Spring Application'}],
12
+ 'Grails' => ['grails', { :mem => '512M', :description => 'Java SpringSource Grails Application'}],
13
+ 'Lift' => ['lift', { :mem => '512M', :description => 'Scala Lift Application'}],
14
+ 'JavaWeb' => ['java_web',{ :mem => '512M', :description => 'Java Web 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
+ 'Rack' => ['rack', { :mem => '128M', :description => 'Rack Application'}]
22
+ }
23
+
24
+ class << self
25
+
26
+ def known_frameworks
27
+ FRAMEWORKS.keys
28
+ end
29
+
30
+ def lookup(name)
31
+ return Framework.new(*FRAMEWORKS[name])
32
+ end
33
+
34
+ def lookup_by_framework(name)
35
+ FRAMEWORKS.each do |key,fw|
36
+ return Framework.new(fw[0], fw[1]) if fw[0] == name
37
+ end
38
+ end
39
+
40
+ def detect(path, available_frameworks)
41
+ Dir.chdir(path) do
42
+ # Rails
43
+ if File.exist?('config/environment.rb')
44
+ return Framework.lookup('Rails')
45
+
46
+ # Topaz
47
+ elsif File.exist?('main.tpz') && available_frameworks.include?(["topaz"])
48
+ return Framework.lookup('Topaz')
49
+
50
+ # Rack
51
+ elsif File.exist?('config.ru') && available_frameworks.include?(["rack"])
52
+ return Framework.lookup('Rack')
53
+
54
+ # Java
55
+ elsif Dir.glob('*.war').first || File.exist?('WEB-INF/web.xml')
56
+ war_file = Dir.glob('*.war').first
57
+
58
+ if war_file
59
+ contents = ZipUtil.entry_lines(war_file)
60
+ else
61
+ contents = Dir['**/*'].join("\n")
62
+ end
63
+
64
+ # Spring/Lift Variations
65
+ if contents =~ /WEB-INF\/lib\/grails-web.*\.jar/
66
+ return Framework.lookup('Grails')
67
+ elsif contents =~ /WEB-INF\/lib\/lift-webkit.*\.jar/
68
+ return Framework.lookup('Lift')
69
+ elsif contents =~ /WEB-INF\/classes\/org\/springframework/
70
+ return Framework.lookup('Spring')
71
+ elsif contents =~ /WEB-INF\/lib\/spring-core.*\.jar/
72
+ return Framework.lookup('Spring')
73
+ elsif contents =~ /WEB-INF\/lib\/org\.springframework\.core.*\.jar/
74
+ return Framework.lookup('Spring')
75
+ else
76
+ return Framework.lookup('JavaWeb')
77
+ end
78
+ # Simple Ruby Apps
79
+ elsif !Dir.glob('*.rb').empty?
80
+ matched_file = nil
81
+ Dir.glob('*.rb').each do |fname|
82
+ next if matched_file
83
+ File.open(fname, 'r') do |f|
84
+ str = f.read # This might want to be limited
85
+ matched_file = fname if (str && str.match(/^\s*require[\s\(]*['"]sinatra['"]/))
86
+ end
87
+ end
88
+ if matched_file
89
+ f = Framework.lookup('Sinatra')
90
+ f.exec = "ruby #{matched_file}"
91
+ return f
92
+ end
93
+
94
+ # Node.js
95
+ elsif !Dir.glob('*.js').empty?
96
+ if File.exist?('server.js') || File.exist?('app.js') || File.exist?('index.js') || File.exist?('main.js')
97
+ return Framework.lookup('Node')
98
+ end
99
+
100
+ # PHP
101
+ elsif !Dir.glob('*.php').empty?
102
+ return Framework.lookup('PHP')
103
+
104
+ # Erlang/OTP using Rebar
105
+ elsif !Dir.glob('releases/*/*.rel').empty? && !Dir.glob('releases/*/*.boot').empty?
106
+ return Framework.lookup('Erlang/OTP Rebar')
107
+
108
+ # Python Django
109
+ # XXX: not all django projects keep settings.py in top-level directory
110
+ elsif File.exist?('manage.py') && File.exist?('settings.py')
111
+ return Framework.lookup('Django')
112
+
113
+ # Python
114
+ elsif !Dir.glob('wsgi.py').empty?
115
+ return Framework.lookup('WSGI')
116
+
117
+ end
118
+ end
119
+ nil
120
+ end
121
+
122
+ end
123
+
124
+ attr_reader :name, :description, :memory, :console
125
+ attr_accessor :exec
126
+
127
+ alias :mem :memory
128
+
129
+ def initialize(framework=nil, opts={})
130
+ @name = framework || DEFAULT_FRAMEWORK
131
+ @memory = opts[:mem] || DEFAULT_MEM
132
+ @description = opts[:description] || 'Unknown Application Type'
133
+ @exec = opts[:exec]
134
+ @console = opts[:console] || false
135
+ end
136
+
137
+ def to_s
138
+ description
139
+ end
140
+ end
141
+
142
+ end
@@ -0,0 +1,262 @@
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
+ url_template = manifest("url") || DEFAULTS["url"]
86
+ url_resolved = url_template.dup
87
+ resolve_lexically(url_resolved)
88
+
89
+ url = ask("Application Deployed URL", :default => url_resolved)
90
+
91
+ url = url_template if url == url_resolved
92
+
93
+ # common error case is for prompted users to answer y or Y or yes or
94
+ # YES to this ask() resulting in an unintended URL of y. Special
95
+ # case this common error
96
+ url = DEFAULTS["url"] if YES_SET.member? url
97
+
98
+ set url, "url"
99
+
100
+ unless manifest "framework"
101
+ framework = detect_framework
102
+ set framework.name, "framework", "name"
103
+ set(
104
+ { "mem" => framework.mem,
105
+ "description" => framework.description,
106
+ "exec" => framework.exec
107
+ },
108
+ "framework",
109
+ "info"
110
+ )
111
+ end
112
+
113
+ set ask(
114
+ "Memory reservation",
115
+ :default =>
116
+ manifest("mem") ||
117
+ manifest("framework", "info", "mem") ||
118
+ DEFAULTS["mem"],
119
+ :choices => ["128M", "256M", "512M", "1G", "2G"]
120
+ ), "mem"
121
+
122
+ set ask(
123
+ "How many instances?",
124
+ :default => manifest("instances") || DEFAULTS["instances"]
125
+ ), "instances"
126
+
127
+ unless manifest "services"
128
+ user_services = client.services
129
+ user_services.sort! {|a, b| a[:name] <=> b[:name] }
130
+
131
+ unless user_services.empty?
132
+ if ask "Bind existing services to '#{name}'?", :default => false
133
+ bind_services(user_services)
134
+ end
135
+ end
136
+
137
+ services = client.services_info
138
+ unless services.empty?
139
+ if ask "Create services to bind to '#{name}'?", :default => false
140
+ create_services(services.values.collect(&:keys).flatten)
141
+ end
142
+ end
143
+ end
144
+
145
+ if many and ask("Configure for another application?", :default => false)
146
+ @application = ask "Application path?"
147
+ configure_app
148
+ end
149
+ end
150
+
151
+ def set(what, *where)
152
+ where.unshift "applications", @application
153
+
154
+ which = @manifest
155
+ where.each_with_index do |k, i|
156
+ if i + 1 == where.size
157
+ which[k] = what
158
+ else
159
+ which = (which[k] ||= {})
160
+ end
161
+ end
162
+
163
+ what
164
+ end
165
+
166
+ # Detect the appropriate framework.
167
+ def detect_framework(prompt_ok = true)
168
+ framework = VMC::Cli::Framework.detect(@application, frameworks_info)
169
+ framework_correct = ask("Detected a #{framework}, is this correct?", :default => true) if prompt_ok && framework
170
+ if prompt_ok && (framework.nil? || !framework_correct)
171
+ display "#{"[WARNING]".yellow} Can't determine the Application Type." unless framework
172
+ framework = nil if !framework_correct
173
+ framework = VMC::Cli::Framework.lookup(
174
+ ask(
175
+ "Select Application Type",
176
+ :indexed => true,
177
+ :default => framework,
178
+ :choices => VMC::Cli::Framework.known_frameworks
179
+ )
180
+ )
181
+ display "Selected #{framework}"
182
+ end
183
+
184
+ framework
185
+ end
186
+
187
+ def bind_services(user_services, chosen = 0)
188
+ svcname = ask(
189
+ "Which one?",
190
+ :indexed => true,
191
+ :choices => user_services.collect { |p| p[:name] })
192
+
193
+ svc = user_services.find { |p| p[:name] == svcname }
194
+
195
+ set svc[:vendor], "services", svcname, "type"
196
+
197
+ if chosen + 1 < user_services.size && ask("Bind another?", :default => false)
198
+ bind_services(user_services, chosen + 1)
199
+ end
200
+ end
201
+
202
+ def create_services(services)
203
+ svcs = services.collect(&:to_s).sort!
204
+
205
+ configure_service(
206
+ ask(
207
+ "What kind of service?",
208
+ :indexed => true,
209
+ :choices => svcs
210
+ )
211
+ )
212
+
213
+ if ask "Create another?", :default => false
214
+ create_services(services)
215
+ end
216
+ end
217
+
218
+ def configure_service(vendor)
219
+ default_name = random_service_name(vendor)
220
+ name = ask "Specify the name of the service", :default => default_name
221
+
222
+ set vendor, "services", name, "type"
223
+ end
224
+
225
+ private
226
+ def ordered_by_deps(apps, abspaths = nil, processed = Set[])
227
+ unless abspaths
228
+ abspaths = {}
229
+ apps.each do |p, i|
230
+ ep = File.expand_path("../" + p, manifest_file)
231
+ abspaths[ep] = i
232
+ end
233
+ end
234
+
235
+ ordered = []
236
+ apps.each do |path, info|
237
+ epath = File.expand_path("../" + path, manifest_file)
238
+
239
+ if deps = info["depends-on"]
240
+ dep_apps = {}
241
+ deps.each do |dep|
242
+ edep = File.expand_path("../" + dep, manifest_file)
243
+
244
+ err "Circular dependency detected." if processed.include? edep
245
+
246
+ dep_apps[dep] = abspaths[edep]
247
+ end
248
+
249
+ processed.add(epath)
250
+
251
+ ordered += ordered_by_deps(dep_apps, abspaths, processed)
252
+ ordered << [path, info]
253
+ elsif not processed.include? epath
254
+ ordered << [path, info]
255
+ processed.add(epath)
256
+ end
257
+ end
258
+
259
+ ordered
260
+ end
261
+
262
+ end