Merge lp:~logan/ubuntu/quantal/ruby-vmc/new-upstream into lp:ubuntu/quantal/ruby-vmc
- Quantal (12.10)
- new-upstream
- Merge into quantal
Proposed by
Logan Rosen
Status: | Merged |
---|---|
Merged at revision: | 6 |
Proposed branch: | lp:~logan/ubuntu/quantal/ruby-vmc/new-upstream |
Merge into: | lp:ubuntu/quantal/ruby-vmc |
Diff against target: |
5823 lines (+3220/-1281) 63 files modified
.pc/applied-patches (+0/-1) .pc/modify-include-path/bin/vmc (+0/-6) README.md (+12/-2) Rakefile (+86/-2) bin/vmc (+2/-2) caldecott_helper/Gemfile (+10/-0) caldecott_helper/Gemfile.lock (+48/-0) caldecott_helper/server.rb (+43/-0) config/clients.yml (+17/-0) config/micro/offline.conf (+2/-0) config/micro/paths.yml (+22/-0) config/micro/refresh_ip.rb (+20/-0) debian/changelog (+6/-0) lib/cli.rb (+19/-2) lib/cli/commands/admin.rb (+31/-8) lib/cli/commands/apps.rb (+658/-469) lib/cli/commands/base.rb (+169/-21) lib/cli/commands/manifest.rb (+56/-0) lib/cli/commands/micro.rb (+115/-0) lib/cli/commands/misc.rb (+5/-4) lib/cli/commands/services.rb (+112/-16) lib/cli/commands/user.rb (+13/-8) lib/cli/config.rb (+85/-22) lib/cli/console_helper.rb (+160/-0) lib/cli/core_ext.rb (+4/-1) lib/cli/frameworks.rb (+201/-33) lib/cli/manifest_helper.rb (+302/-0) lib/cli/runner.rb (+65/-19) lib/cli/services_helper.rb (+19/-9) lib/cli/tunnel_helper.rb (+332/-0) lib/cli/usage.rb (+16/-4) lib/cli/version.rb (+1/-1) lib/cli/zip_util.rb (+1/-1) lib/vmc/client.rb (+62/-26) lib/vmc/const.rb (+8/-7) lib/vmc/micro.rb (+56/-0) lib/vmc/micro/switcher/base.rb (+97/-0) lib/vmc/micro/switcher/darwin.rb (+19/-0) lib/vmc/micro/switcher/dummy.rb (+15/-0) lib/vmc/micro/switcher/linux.rb (+16/-0) lib/vmc/micro/switcher/windows.rb (+31/-0) lib/vmc/micro/vmrun.rb (+158/-0) metadata.yml (+126/-73) spec/assets/app_info.txt (+0/-9) spec/assets/app_listings.txt (+0/-9) spec/assets/bad_create_app.txt (+0/-9) spec/assets/delete_app.txt (+0/-9) spec/assets/global_service_listings.txt (+0/-9) spec/assets/good_create_app.txt (+0/-9) spec/assets/good_create_service.txt (+0/-9) spec/assets/info_authenticated.txt (+0/-27) spec/assets/info_return.txt (+0/-15) spec/assets/info_return_bad.txt (+0/-16) spec/assets/login_fail.txt (+0/-9) spec/assets/login_success.txt (+0/-9) spec/assets/sample_token.txt (+0/-1) spec/assets/service_already_exists.txt (+0/-9) spec/assets/service_listings.txt (+0/-9) spec/assets/service_not_found.txt (+0/-9) spec/assets/user_info.txt (+0/-9) spec/spec_helper.rb (+0/-11) spec/unit/cli_opts_spec.rb (+0/-73) spec/unit/client_spec.rb (+0/-284) |
To merge this branch: | bzr merge lp:~logan/ubuntu/quantal/ruby-vmc/new-upstream |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Michael Terry | Approve | ||
Ubuntu branches | Pending | ||
Review via email: mp+109967@code.launchpad.net |
Commit message
Description of the change
To post a comment you must log in.
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === removed file '.pc/applied-patches' | |||
2 | --- .pc/applied-patches 2011-06-17 13:38:56 +0000 | |||
3 | +++ .pc/applied-patches 1970-01-01 00:00:00 +0000 | |||
4 | @@ -1,1 +0,0 @@ | |||
5 | 1 | modify-include-path | ||
6 | 2 | 0 | ||
7 | === removed directory '.pc/modify-include-path' | |||
8 | === removed directory '.pc/modify-include-path/bin' | |||
9 | === removed file '.pc/modify-include-path/bin/vmc' | |||
10 | --- .pc/modify-include-path/bin/vmc 2011-06-17 13:38:56 +0000 | |||
11 | +++ .pc/modify-include-path/bin/vmc 1970-01-01 00:00:00 +0000 | |||
12 | @@ -1,6 +0,0 @@ | |||
13 | 1 | #!/usr/bin/env ruby | ||
14 | 2 | |||
15 | 3 | require File.expand_path('../../lib/cli', __FILE__) | ||
16 | 4 | |||
17 | 5 | VMC::Cli::Runner.run(ARGV.dup) | ||
18 | 6 | |||
19 | 7 | 0 | ||
20 | === modified file 'README.md' | |||
21 | --- README.md 2011-06-17 13:38:56 +0000 | |||
22 | +++ README.md 2012-06-13 01:37:18 +0000 | |||
23 | @@ -31,7 +31,6 @@ | |||
24 | 31 | stop <appname> Stop the application | 31 | stop <appname> Stop the application |
25 | 32 | restart <appname> Restart the application | 32 | restart <appname> Restart the application |
26 | 33 | delete <appname> Delete the application | 33 | delete <appname> Delete the application |
27 | 34 | rename <appname> <newname> Rename the application | ||
28 | 35 | 34 | ||
29 | 36 | Application Updates | 35 | Application Updates |
30 | 37 | update <appname> [--path] Update the application bits | 36 | update <appname> [--path] Update the application bits |
31 | @@ -62,6 +61,8 @@ | |||
32 | 62 | bind-service <servicename> <appname> Bind a service to an application | 61 | bind-service <servicename> <appname> Bind a service to an application |
33 | 63 | unbind-service <servicename> <appname> Unbind service from the application | 62 | unbind-service <servicename> <appname> Unbind service from the application |
34 | 64 | clone-services <src-app> <dest-app> Clone service bindings from <src-app> application to <dest-app> | 63 | clone-services <src-app> <dest-app> Clone service bindings from <src-app> application to <dest-app> |
35 | 64 | tunnel <servicename> [--port] Create a local tunnel to a service | ||
36 | 65 | tunnel <servicename> <clientcmd> Create a local tunnel to a service and start a local client | ||
37 | 65 | 66 | ||
38 | 66 | Administration | 67 | Administration |
39 | 67 | user Display user account information | 68 | user Display user account information |
40 | @@ -74,6 +75,15 @@ | |||
41 | 74 | runtimes Display the supported runtimes of the target system | 75 | runtimes Display the supported runtimes of the target system |
42 | 75 | frameworks Display the recognized frameworks of the target system | 76 | frameworks Display the recognized frameworks of the target system |
43 | 76 | 77 | ||
44 | 78 | Micro Cloud Foundry | ||
45 | 79 | micro status Display Micro Cloud Foundry VM status | ||
46 | 80 | mciro offline Configure Micro Cloud Foundry VM for offline mode | ||
47 | 81 | micro online Configure Micro Cloud Foundry VM for online mode | ||
48 | 82 | [--vmx file] Path to micro.vmx | ||
49 | 83 | [--vmrun executable] Path to vmrun executable | ||
50 | 84 | [--password cleartext] Cleartext password for guest VM vcap user | ||
51 | 85 | [--save] Save cleartext password in ~/.vmc_micro | ||
52 | 86 | |||
53 | 77 | Misc | 87 | Misc |
54 | 78 | aliases List aliases | 88 | aliases List aliases |
55 | 79 | alias <alias[=]command> Create an alias for a command | 89 | alias <alias[=]command> Create an alias for a command |
56 | @@ -86,7 +96,7 @@ | |||
57 | 86 | 96 | ||
58 | 87 | ## Simple Story (for Ruby apps) | 97 | ## Simple Story (for Ruby apps) |
59 | 88 | 98 | ||
61 | 89 | vmc target api.vcloudlabs.com | 99 | vmc target api.cloudfoundry.com |
62 | 90 | vmc login | 100 | vmc login |
63 | 91 | bundle package | 101 | bundle package |
64 | 92 | vmc push | 102 | vmc push |
65 | 93 | 103 | ||
66 | === modified file 'Rakefile' | |||
67 | --- Rakefile 2011-06-17 13:38:56 +0000 | |||
68 | +++ Rakefile 2012-06-13 01:37:18 +0000 | |||
69 | @@ -2,8 +2,7 @@ | |||
70 | 2 | require 'spec/rake/spectask' | 2 | require 'spec/rake/spectask' |
71 | 3 | 3 | ||
72 | 4 | desc "Run specs" | 4 | desc "Run specs" |
75 | 5 | task :spec do | 5 | task :spec => :build do |
74 | 6 | sh('bundle install') | ||
76 | 7 | Spec::Rake::SpecTask.new('spec') do |t| | 6 | Spec::Rake::SpecTask.new('spec') do |t| |
77 | 8 | t.spec_opts = %w(-fs -c) | 7 | t.spec_opts = %w(-fs -c) |
78 | 9 | t.spec_files = FileList['spec/**/*_spec.rb'] | 8 | t.spec_files = FileList['spec/**/*_spec.rb'] |
79 | @@ -15,3 +14,88 @@ | |||
80 | 15 | desc "Synonym for spec" | 14 | desc "Synonym for spec" |
81 | 16 | task :tests => :spec | 15 | task :tests => :spec |
82 | 17 | task :default => :spec | 16 | task :default => :spec |
83 | 17 | |||
84 | 18 | def tests_path | ||
85 | 19 | if @tests_path == nil | ||
86 | 20 | @tests_path = File.join(Dir.pwd, "spec/assets/tests") | ||
87 | 21 | end | ||
88 | 22 | @tests_path | ||
89 | 23 | end | ||
90 | 24 | TESTS_PATH = tests_path | ||
91 | 25 | |||
92 | 26 | BUILD_ARTIFACT = File.join(Dir.pwd, "spec/assets/.build") | ||
93 | 27 | |||
94 | 28 | TESTS_TO_BUILD = ["#{TESTS_PATH}/java_web/java_tiny_app", | ||
95 | 29 | # "#{TESTS_PATH}/grails/guestbook", | ||
96 | 30 | "#{TESTS_PATH}/lift/hello_lift", | ||
97 | 31 | "#{TESTS_PATH}/spring/roo-guestbook", | ||
98 | 32 | "#{TESTS_PATH}/spring/spring-osgi-hello", | ||
99 | 33 | "#{TESTS_PATH}/standalone/java_app", | ||
100 | 34 | "#{TESTS_PATH}/standalone/python_app" | ||
101 | 35 | ] | ||
102 | 36 | |||
103 | 37 | desc "Build the tests. If the git hash associated with the test assets has not changed, nothing is built. To force a build, invoke 'rake build[--force]'" | ||
104 | 38 | task :build, [:force] do |t, args| | ||
105 | 39 | sh('bundle install') | ||
106 | 40 | sh('git submodule update --init') | ||
107 | 41 | puts "\nBuilding tests" | ||
108 | 42 | if build_required? args.force | ||
109 | 43 | ENV['MAVEN_OPTS']="-XX:MaxPermSize=256M" | ||
110 | 44 | TESTS_TO_BUILD.each do |test| | ||
111 | 45 | puts "\tBuilding '#{test}'" | ||
112 | 46 | Dir.chdir test do | ||
113 | 47 | sh('mvn package -DskipTests') do |success, exit_code| | ||
114 | 48 | unless success | ||
115 | 49 | clear_build_artifact | ||
116 | 50 | do_mvn_clean('-q') | ||
117 | 51 | fail "\tFailed to build #{test} - aborting build" | ||
118 | 52 | end | ||
119 | 53 | end | ||
120 | 54 | end | ||
121 | 55 | puts "\tCompleted building '#{test}'" | ||
122 | 56 | end | ||
123 | 57 | save_git_hash | ||
124 | 58 | else | ||
125 | 59 | puts "Built artifacts in sync with test assets - no build required" | ||
126 | 60 | end | ||
127 | 61 | end | ||
128 | 62 | |||
129 | 63 | desc "Clean the build artifacts" | ||
130 | 64 | task :clean do | ||
131 | 65 | puts "\nCleaning tests" | ||
132 | 66 | clear_build_artifact | ||
133 | 67 | TESTS_TO_BUILD.each do |test| | ||
134 | 68 | puts "\tCleaning '#{test}'" | ||
135 | 69 | Dir.chdir test do | ||
136 | 70 | do_mvn_clean | ||
137 | 71 | end | ||
138 | 72 | puts "\tCompleted cleaning '#{test}'" | ||
139 | 73 | end | ||
140 | 74 | end | ||
141 | 75 | |||
142 | 76 | def build_required? (force_build=nil) | ||
143 | 77 | if File.exists?(BUILD_ARTIFACT) == false or (force_build and force_build == "--force") | ||
144 | 78 | return true | ||
145 | 79 | end | ||
146 | 80 | Dir.chdir(tests_path) do | ||
147 | 81 | saved_git_hash = IO.readlines(BUILD_ARTIFACT)[0].split[0] | ||
148 | 82 | git_hash = `git rev-parse --short=8 --verify HEAD` | ||
149 | 83 | saved_git_hash.to_s.strip != git_hash.to_s.strip | ||
150 | 84 | end | ||
151 | 85 | end | ||
152 | 86 | |||
153 | 87 | def save_git_hash | ||
154 | 88 | Dir.chdir(tests_path) do | ||
155 | 89 | git_hash = `git rev-parse --short=8 --verify HEAD` | ||
156 | 90 | File.open(BUILD_ARTIFACT, 'w') {|f| f.puts("#{git_hash}")} | ||
157 | 91 | end | ||
158 | 92 | end | ||
159 | 93 | |||
160 | 94 | def clear_build_artifact | ||
161 | 95 | puts "\tClearing build artifact #{BUILD_ARTIFACT}" | ||
162 | 96 | File.unlink BUILD_ARTIFACT if File.exists? BUILD_ARTIFACT | ||
163 | 97 | end | ||
164 | 98 | |||
165 | 99 | def do_mvn_clean options=nil | ||
166 | 100 | sh("mvn clean #{options}") | ||
167 | 101 | end | ||
168 | 18 | 102 | ||
169 | === modified file 'bin/vmc' | |||
170 | --- bin/vmc 2011-06-17 13:38:56 +0000 | |||
171 | +++ bin/vmc 2012-06-13 01:37:18 +0000 | |||
172 | @@ -1,6 +1,6 @@ | |||
174 | 1 | #!/usr/bin/ruby1.9.1 | 1 | #!/usr/bin/env ruby |
175 | 2 | 2 | ||
177 | 3 | require File.expand_path('/usr/lib/ruby/vendor_ruby/cli', __FILE__) | 3 | require File.expand_path('../../lib/cli', __FILE__) |
178 | 4 | 4 | ||
179 | 5 | VMC::Cli::Runner.run(ARGV.dup) | 5 | VMC::Cli::Runner.run(ARGV.dup) |
180 | 6 | 6 | ||
181 | 7 | 7 | ||
182 | === added directory 'caldecott_helper' | |||
183 | === added file 'caldecott_helper/Gemfile' | |||
184 | --- caldecott_helper/Gemfile 1970-01-01 00:00:00 +0000 | |||
185 | +++ caldecott_helper/Gemfile 2012-06-13 01:37:18 +0000 | |||
186 | @@ -0,0 +1,10 @@ | |||
187 | 1 | source "http://rubygems.org" | ||
188 | 2 | |||
189 | 3 | gem 'rack', '~> 1.2.0' | ||
190 | 4 | gem 'caldecott', '= 0.0.3' | ||
191 | 5 | gem 'bundler' | ||
192 | 6 | gem 'em-websocket' | ||
193 | 7 | gem 'async_sinatra' | ||
194 | 8 | gem 'thin' | ||
195 | 9 | gem 'json' | ||
196 | 10 | gem 'uuidtools' | ||
197 | 0 | 11 | ||
198 | === added file 'caldecott_helper/Gemfile.lock' | |||
199 | --- caldecott_helper/Gemfile.lock 1970-01-01 00:00:00 +0000 | |||
200 | +++ caldecott_helper/Gemfile.lock 2012-06-13 01:37:18 +0000 | |||
201 | @@ -0,0 +1,48 @@ | |||
202 | 1 | GEM | ||
203 | 2 | remote: http://rubygems.org/ | ||
204 | 3 | specs: | ||
205 | 4 | addressable (2.2.6) | ||
206 | 5 | async_sinatra (0.5.0) | ||
207 | 6 | rack (>= 1.2.1) | ||
208 | 7 | sinatra (>= 1.0) | ||
209 | 8 | caldecott (0.0.3) | ||
210 | 9 | addressable (= 2.2.6) | ||
211 | 10 | async_sinatra (= 0.5.0) | ||
212 | 11 | em-http-request (= 0.3.0) | ||
213 | 12 | em-websocket (= 0.3.1) | ||
214 | 13 | json (= 1.6.1) | ||
215 | 14 | uuidtools (= 2.1.2) | ||
216 | 15 | daemons (1.1.4) | ||
217 | 16 | em-http-request (0.3.0) | ||
218 | 17 | addressable (>= 2.0.0) | ||
219 | 18 | escape_utils | ||
220 | 19 | eventmachine (>= 0.12.9) | ||
221 | 20 | em-websocket (0.3.1) | ||
222 | 21 | addressable (>= 2.1.1) | ||
223 | 22 | eventmachine (>= 0.12.9) | ||
224 | 23 | escape_utils (0.2.4) | ||
225 | 24 | eventmachine (0.12.10) | ||
226 | 25 | json (1.6.1) | ||
227 | 26 | rack (1.2.4) | ||
228 | 27 | sinatra (1.2.7) | ||
229 | 28 | rack (~> 1.1) | ||
230 | 29 | tilt (>= 1.2.2, < 2.0) | ||
231 | 30 | thin (1.2.11) | ||
232 | 31 | daemons (>= 1.0.9) | ||
233 | 32 | eventmachine (>= 0.12.6) | ||
234 | 33 | rack (>= 1.0.0) | ||
235 | 34 | tilt (1.3.3) | ||
236 | 35 | uuidtools (2.1.2) | ||
237 | 36 | |||
238 | 37 | PLATFORMS | ||
239 | 38 | ruby | ||
240 | 39 | |||
241 | 40 | DEPENDENCIES | ||
242 | 41 | async_sinatra | ||
243 | 42 | bundler | ||
244 | 43 | caldecott (= 0.0.3) | ||
245 | 44 | em-websocket | ||
246 | 45 | json | ||
247 | 46 | rack (~> 1.2.0) | ||
248 | 47 | thin | ||
249 | 48 | uuidtools | ||
250 | 0 | 49 | ||
251 | === added file 'caldecott_helper/server.rb' | |||
252 | --- caldecott_helper/server.rb 1970-01-01 00:00:00 +0000 | |||
253 | +++ caldecott_helper/server.rb 2012-06-13 01:37:18 +0000 | |||
254 | @@ -0,0 +1,43 @@ | |||
255 | 1 | #!/usr/bin/env ruby | ||
256 | 2 | # Copyright (c) 2009-2011 VMware, Inc. | ||
257 | 3 | $:.unshift(File.dirname(__FILE__) + '/lib') | ||
258 | 4 | |||
259 | 5 | require 'rubygems' | ||
260 | 6 | require 'bundler/setup' | ||
261 | 7 | |||
262 | 8 | require 'caldecott' | ||
263 | 9 | require 'sinatra' | ||
264 | 10 | require 'json' | ||
265 | 11 | require 'eventmachine' | ||
266 | 12 | |||
267 | 13 | port = ENV['VMC_APP_PORT'] | ||
268 | 14 | port ||= 8081 | ||
269 | 15 | |||
270 | 16 | # add vcap specific stuff to Caldecott | ||
271 | 17 | class VcapHttpTunnel < Caldecott::Server::HttpTunnel | ||
272 | 18 | get '/info' do | ||
273 | 19 | { "version" => '0.0.4' }.to_json | ||
274 | 20 | end | ||
275 | 21 | |||
276 | 22 | def self.get_tunnels | ||
277 | 23 | super | ||
278 | 24 | end | ||
279 | 25 | |||
280 | 26 | get '/services' do | ||
281 | 27 | services_env = ENV['VMC_SERVICES'] | ||
282 | 28 | return "no services env" if services_env.nil? or services_env.empty? | ||
283 | 29 | services_env | ||
284 | 30 | end | ||
285 | 31 | |||
286 | 32 | get '/services/:service' do |service_name| | ||
287 | 33 | services_env = ENV['VMC_SERVICES'] | ||
288 | 34 | not_found if services_env.nil? | ||
289 | 35 | |||
290 | 36 | services = JSON.parse(services_env) | ||
291 | 37 | service = services.find { |s| s["name"] == service_name } | ||
292 | 38 | not_found if service.nil? | ||
293 | 39 | service["options"].to_json | ||
294 | 40 | end | ||
295 | 41 | end | ||
296 | 42 | |||
297 | 43 | VcapHttpTunnel.run!(:port => port, :auth_token => ENV["CALDECOTT_AUTH"]) | ||
298 | 0 | 44 | ||
299 | === added directory 'config' | |||
300 | === added file 'config/clients.yml' | |||
301 | --- config/clients.yml 1970-01-01 00:00:00 +0000 | |||
302 | +++ config/clients.yml 2012-06-13 01:37:18 +0000 | |||
303 | @@ -0,0 +1,17 @@ | |||
304 | 1 | redis: | ||
305 | 2 | redis-cli: -h ${host} -p ${port} -a ${password} | ||
306 | 3 | |||
307 | 4 | mysql: | ||
308 | 5 | mysql: --protocol=TCP --host=${host} --port=${port} --user=${user} --password=${password} ${name} | ||
309 | 6 | mysqldump: --protocol=TCP --host=${host} --port=${port} --user=${user} --password=${password} ${name} > ${Output file} | ||
310 | 7 | |||
311 | 8 | mongodb: | ||
312 | 9 | mongo: --host ${host} --port ${port} -u ${user} -p ${password} ${name} | ||
313 | 10 | mongodump: --host ${host} --port ${port} -u ${user} -p ${password} --db ${name} | ||
314 | 11 | mongorestore: --host ${host} --port ${port} -u ${user} -p ${password} --db ${name} ${Directory or filename to restore from} | ||
315 | 12 | |||
316 | 13 | postgresql: | ||
317 | 14 | psql: | ||
318 | 15 | command: -h ${host} -p ${port} -d ${name} -U ${user} -w | ||
319 | 16 | environment: | ||
320 | 17 | - PGPASSWORD='${password}' | ||
321 | 0 | 18 | ||
322 | === added directory 'config/micro' | |||
323 | === added file 'config/micro/offline.conf' | |||
324 | --- config/micro/offline.conf 1970-01-01 00:00:00 +0000 | |||
325 | +++ config/micro/offline.conf 2012-06-13 01:37:18 +0000 | |||
326 | @@ -0,0 +1,2 @@ | |||
327 | 1 | no-resolv | ||
328 | 2 | log-queries | ||
329 | 0 | 3 | ||
330 | === added file 'config/micro/paths.yml' | |||
331 | --- config/micro/paths.yml 1970-01-01 00:00:00 +0000 | |||
332 | +++ config/micro/paths.yml 2012-06-13 01:37:18 +0000 | |||
333 | @@ -0,0 +1,22 @@ | |||
334 | 1 | darwin: | ||
335 | 2 | vmrun: | ||
336 | 3 | - "/Applications/VMware Fusion.app/Contents/Library/" | ||
337 | 4 | - "/Applications/Fusion.app/Contents/Library/" | ||
338 | 5 | vmx: | ||
339 | 6 | - "~/Documents/Virtual Machines.localized/" | ||
340 | 7 | - "~/Documents/Virtual Machines/" | ||
341 | 8 | - "~/Desktop/" | ||
342 | 9 | |||
343 | 10 | linux: | ||
344 | 11 | vmrun: | ||
345 | 12 | - "/usr/bin/" | ||
346 | 13 | vmx: | ||
347 | 14 | - "~/" | ||
348 | 15 | |||
349 | 16 | windows: | ||
350 | 17 | vmrun: | ||
351 | 18 | - "c:\\Program Files (x86)\\" | ||
352 | 19 | - "c:\\Program Files\\" | ||
353 | 20 | vmx: | ||
354 | 21 | - "~\\Documents\\" | ||
355 | 22 | - "~\\Desktop\\" | ||
356 | 0 | 23 | ||
357 | === added file 'config/micro/refresh_ip.rb' | |||
358 | --- config/micro/refresh_ip.rb 1970-01-01 00:00:00 +0000 | |||
359 | +++ config/micro/refresh_ip.rb 2012-06-13 01:37:18 +0000 | |||
360 | @@ -0,0 +1,20 @@ | |||
361 | 1 | #!/var/vcap/bosh/bin/ruby | ||
362 | 2 | require 'socket' | ||
363 | 3 | |||
364 | 4 | A_ROOT_SERVER = '198.41.0.4' | ||
365 | 5 | |||
366 | 6 | begin | ||
367 | 7 | retries ||= 0 | ||
368 | 8 | route ||= A_ROOT_SERVER | ||
369 | 9 | orig, Socket.do_not_reverse_lookup = Socket.do_not_reverse_lookup, true | ||
370 | 10 | ip_address = UDPSocket.open {|s| s.connect(route, 1); s.addr.last } | ||
371 | 11 | rescue Errno::ENETUNREACH | ||
372 | 12 | # happens on boot when dhcp hasn't completed when we get here | ||
373 | 13 | sleep 3 | ||
374 | 14 | retries += 1 | ||
375 | 15 | retry if retries < 10 | ||
376 | 16 | ensure | ||
377 | 17 | Socket.do_not_reverse_lookup = orig | ||
378 | 18 | end | ||
379 | 19 | |||
380 | 20 | File.open("/tmp/ip.txt", 'w') { |file| file.write(ip_address) } | ||
381 | 0 | 21 | ||
382 | === modified file 'debian/changelog' | |||
383 | --- debian/changelog 2011-08-09 14:45:42 +0000 | |||
384 | +++ debian/changelog 2012-06-13 01:37:18 +0000 | |||
385 | @@ -1,3 +1,9 @@ | |||
386 | 1 | ruby-vmc (0.3.18-0ubuntu1) UNRELEASED; urgency=low | ||
387 | 2 | |||
388 | 3 | * New upstream release (LP: #998111). | ||
389 | 4 | |||
390 | 5 | -- Logan Rosen <logatronico@gmail.com> Tue, 12 Jun 2012 21:31:33 -0400 | ||
391 | 6 | |||
392 | 1 | ruby-vmc (0.3.10-0ubuntu10) oneiric; urgency=low | 7 | ruby-vmc (0.3.10-0ubuntu10) oneiric; urgency=low |
393 | 2 | 8 | ||
394 | 3 | [ Marc Cluet ] | 9 | [ Marc Cluet ] |
395 | 4 | 10 | ||
396 | === modified file 'lib/cli.rb' | |||
397 | --- lib/cli.rb 2011-06-17 13:38:56 +0000 | |||
398 | +++ lib/cli.rb 2012-06-13 01:37:18 +0000 | |||
399 | @@ -1,25 +1,42 @@ | |||
400 | 1 | require "rbconfig" | ||
401 | 1 | 2 | ||
402 | 2 | ROOT = File.expand_path(File.dirname(__FILE__)) | 3 | ROOT = File.expand_path(File.dirname(__FILE__)) |
403 | 4 | WINDOWS = !!(RbConfig::CONFIG['host_os'] =~ /mingw|mswin32|cygwin/) | ||
404 | 3 | 5 | ||
405 | 4 | module VMC | 6 | module VMC |
406 | 5 | |||
407 | 6 | autoload :Client, "#{ROOT}/vmc/client" | 7 | autoload :Client, "#{ROOT}/vmc/client" |
408 | 8 | autoload :Micro, "#{ROOT}/vmc/micro" | ||
409 | 9 | |||
410 | 10 | module Micro | ||
411 | 11 | module Switcher | ||
412 | 12 | autoload :Base, "#{ROOT}/vmc/micro/switcher/base" | ||
413 | 13 | autoload :Darwin, "#{ROOT}/vmc/micro/switcher/darwin" | ||
414 | 14 | autoload :Dummy, "#{ROOT}/vmc/micro/switcher/dummy" | ||
415 | 15 | autoload :Linux, "#{ROOT}/vmc/micro/switcher/linux" | ||
416 | 16 | autoload :Windows, "#{ROOT}/vmc/micro/switcher/windows" | ||
417 | 17 | end | ||
418 | 18 | autoload :VMrun, "#{ROOT}/vmc/micro/vmrun" | ||
419 | 19 | end | ||
420 | 7 | 20 | ||
421 | 8 | module Cli | 21 | module Cli |
422 | 9 | |||
423 | 10 | autoload :Config, "#{ROOT}/cli/config" | 22 | autoload :Config, "#{ROOT}/cli/config" |
424 | 11 | autoload :Framework, "#{ROOT}/cli/frameworks" | 23 | autoload :Framework, "#{ROOT}/cli/frameworks" |
425 | 12 | autoload :Runner, "#{ROOT}/cli/runner" | 24 | autoload :Runner, "#{ROOT}/cli/runner" |
426 | 13 | autoload :ZipUtil, "#{ROOT}/cli/zip_util" | 25 | autoload :ZipUtil, "#{ROOT}/cli/zip_util" |
427 | 14 | autoload :ServicesHelper, "#{ROOT}/cli/services_helper" | 26 | autoload :ServicesHelper, "#{ROOT}/cli/services_helper" |
428 | 27 | autoload :TunnelHelper, "#{ROOT}/cli/tunnel_helper" | ||
429 | 28 | autoload :ManifestHelper, "#{ROOT}/cli/manifest_helper" | ||
430 | 29 | autoload :ConsoleHelper, "#{ROOT}/cli/console_helper" | ||
431 | 15 | 30 | ||
432 | 16 | module Command | 31 | module Command |
433 | 17 | autoload :Base, "#{ROOT}/cli/commands/base" | 32 | autoload :Base, "#{ROOT}/cli/commands/base" |
434 | 18 | autoload :Admin, "#{ROOT}/cli/commands/admin" | 33 | autoload :Admin, "#{ROOT}/cli/commands/admin" |
435 | 19 | autoload :Apps, "#{ROOT}/cli/commands/apps" | 34 | autoload :Apps, "#{ROOT}/cli/commands/apps" |
436 | 35 | autoload :Micro, "#{ROOT}/cli/commands/micro" | ||
437 | 20 | autoload :Misc, "#{ROOT}/cli/commands/misc" | 36 | autoload :Misc, "#{ROOT}/cli/commands/misc" |
438 | 21 | autoload :Services, "#{ROOT}/cli/commands/services" | 37 | autoload :Services, "#{ROOT}/cli/commands/services" |
439 | 22 | autoload :User, "#{ROOT}/cli/commands/user" | 38 | autoload :User, "#{ROOT}/cli/commands/user" |
440 | 39 | autoload :Manifest, "#{ROOT}/cli/commands/manifest" | ||
441 | 23 | end | 40 | end |
442 | 24 | 41 | ||
443 | 25 | end | 42 | end |
444 | 26 | 43 | ||
445 | === modified file 'lib/cli/commands/admin.rb' | |||
446 | --- lib/cli/commands/admin.rb 2011-06-17 13:38:56 +0000 | |||
447 | +++ lib/cli/commands/admin.rb 2012-06-13 01:37:18 +0000 | |||
448 | @@ -2,13 +2,32 @@ | |||
449 | 2 | 2 | ||
450 | 3 | class Admin < Base | 3 | class Admin < Base |
451 | 4 | 4 | ||
452 | 5 | def list_users | ||
453 | 6 | users = client.users | ||
454 | 7 | users.sort! {|a, b| a[:email] <=> b[:email] } | ||
455 | 8 | return display JSON.pretty_generate(users || []) if @options[:json] | ||
456 | 9 | |||
457 | 10 | display "\n" | ||
458 | 11 | return display "No Users" if users.nil? || users.empty? | ||
459 | 12 | |||
460 | 13 | users_table = table do |t| | ||
461 | 14 | t.headings = 'Email', 'Admin', 'Apps' | ||
462 | 15 | users.each do |user| | ||
463 | 16 | t << [user[:email], user[:admin], user[:apps].map {|x| x[:name]}.join(', ')] | ||
464 | 17 | end | ||
465 | 18 | end | ||
466 | 19 | display users_table | ||
467 | 20 | end | ||
468 | 21 | |||
469 | 22 | alias :users :list_users | ||
470 | 23 | |||
471 | 5 | def add_user(email=nil) | 24 | def add_user(email=nil) |
473 | 6 | email = @options[:email] unless email | 25 | email ||= @options[:email] |
474 | 26 | email ||= ask("Email") unless no_prompt | ||
475 | 7 | password = @options[:password] | 27 | password = @options[:password] |
476 | 8 | email = ask("Email: ") unless no_prompt || email | ||
477 | 9 | unless no_prompt || password | 28 | unless no_prompt || password |
480 | 10 | password = ask("Password: ") {|q| q.echo = '*'} | 29 | password = ask("Password", :echo => "*") |
481 | 11 | password2 = ask("Verify Password: ") {|q| q.echo = '*'} | 30 | password2 = ask("Verify Password", :echo => "*") |
482 | 12 | err "Passwords did not match, try again" if password != password2 | 31 | err "Passwords did not match, try again" if password != password2 |
483 | 13 | end | 32 | end |
484 | 14 | err "Need a valid email" unless email | 33 | err "Need a valid email" unless email |
485 | @@ -34,11 +53,14 @@ | |||
486 | 34 | 53 | ||
487 | 35 | if (apps && !apps.empty?) | 54 | if (apps && !apps.empty?) |
488 | 36 | unless no_prompt | 55 | unless no_prompt |
491 | 37 | proceed = ask("\nDeployed applications and associated services will be DELETED, continue? [yN]: ") | 56 | proceed = ask( |
492 | 38 | err "Aborted" if proceed.upcase != 'Y' | 57 | "\nDeployed applications and associated services will be DELETED, continue?", |
493 | 58 | :default => false | ||
494 | 59 | ) | ||
495 | 60 | err "Aborted" unless proceed | ||
496 | 39 | end | 61 | end |
499 | 40 | cmd = Apps.new(@options) | 62 | cmd = Apps.new(@options.merge({ :force => true })) |
500 | 41 | apps.each { |app| cmd.delete_app(app[:name], true) } | 63 | apps.each { |app| cmd.delete(app[:name]) } |
501 | 42 | end | 64 | end |
502 | 43 | 65 | ||
503 | 44 | services = client.services | 66 | services = client.services |
504 | @@ -48,6 +70,7 @@ | |||
505 | 48 | end | 70 | end |
506 | 49 | 71 | ||
507 | 50 | display 'Deleting User: ', false | 72 | display 'Deleting User: ', false |
508 | 73 | client.proxy = nil | ||
509 | 51 | client.delete_user(user_email) | 74 | client.delete_user(user_email) |
510 | 52 | display 'OK'.green | 75 | display 'OK'.green |
511 | 53 | end | 76 | end |
512 | 54 | 77 | ||
513 | === modified file 'lib/cli/commands/apps.rb' | |||
514 | --- lib/cli/commands/apps.rb 2011-06-17 13:38:56 +0000 | |||
515 | +++ lib/cli/commands/apps.rb 2012-06-13 01:37:18 +0000 | |||
516 | @@ -1,16 +1,23 @@ | |||
517 | 1 | require 'digest/sha1' | 1 | require 'digest/sha1' |
518 | 2 | require 'fileutils' | 2 | require 'fileutils' |
519 | 3 | require 'pathname' | ||
520 | 3 | require 'tempfile' | 4 | require 'tempfile' |
521 | 4 | require 'tmpdir' | 5 | require 'tmpdir' |
522 | 5 | require 'set' | 6 | require 'set' |
523 | 7 | require "uuidtools" | ||
524 | 8 | require 'socket' | ||
525 | 6 | 9 | ||
526 | 7 | module VMC::Cli::Command | 10 | module VMC::Cli::Command |
527 | 8 | 11 | ||
528 | 9 | class Apps < Base | 12 | class Apps < Base |
529 | 10 | include VMC::Cli::ServicesHelper | 13 | include VMC::Cli::ServicesHelper |
530 | 14 | include VMC::Cli::ManifestHelper | ||
531 | 15 | include VMC::Cli::TunnelHelper | ||
532 | 16 | include VMC::Cli::ConsoleHelper | ||
533 | 11 | 17 | ||
534 | 12 | def list | 18 | def list |
535 | 13 | apps = client.apps | 19 | apps = client.apps |
536 | 20 | apps.sort! {|a, b| a[:name] <=> b[:name] } | ||
537 | 14 | return display JSON.pretty_generate(apps || []) if @options[:json] | 21 | return display JSON.pretty_generate(apps || []) if @options[:json] |
538 | 15 | 22 | ||
539 | 16 | display "\n" | 23 | display "\n" |
540 | @@ -35,108 +42,97 @@ | |||
541 | 35 | HEALTH_TICKS = 5/SLEEP_TIME | 42 | HEALTH_TICKS = 5/SLEEP_TIME |
542 | 36 | TAIL_TICKS = 45/SLEEP_TIME | 43 | TAIL_TICKS = 45/SLEEP_TIME |
543 | 37 | GIVEUP_TICKS = 120/SLEEP_TIME | 44 | GIVEUP_TICKS = 120/SLEEP_TIME |
621 | 38 | YES_SET = Set.new(["y", "Y", "yes", "YES"]) | 45 | |
622 | 39 | 46 | def info(what, default=nil) | |
623 | 40 | def start(appname, push = false) | 47 | @options[what] || (@app_info && @app_info[what.to_s]) || default |
624 | 41 | app = client.app_info(appname) | 48 | end |
625 | 42 | 49 | ||
626 | 43 | return display "Application '#{appname}' could not be found".red if app.nil? | 50 | def console(appname, interactive=true) |
627 | 44 | return display "Application '#{appname}' already started".yellow if app[:state] == 'STARTED' | 51 | unless defined? Caldecott |
628 | 45 | 52 | display "To use `vmc rails-console', you must first install Caldecott:" | |
629 | 46 | banner = 'Staging Application: ' | 53 | display "" |
630 | 47 | display banner, false | 54 | display "\tgem install caldecott" |
631 | 48 | 55 | display "" | |
632 | 49 | t = Thread.new do | 56 | display "Note that you'll need a C compiler. If you're on OS X, Xcode" |
633 | 50 | count = 0 | 57 | display "will provide one. If you're on Windows, try DevKit." |
634 | 51 | while count < TAIL_TICKS do | 58 | display "" |
635 | 52 | display '.', false | 59 | display "This manual step will be removed in the future." |
636 | 53 | sleep SLEEP_TIME | 60 | display "" |
637 | 54 | count += 1 | 61 | err "Caldecott is not installed." |
638 | 55 | end | 62 | end |
639 | 56 | end | 63 | |
640 | 57 | 64 | #Make sure there is a console we can connect to first | |
641 | 58 | app[:state] = 'STARTED' | 65 | conn_info = console_connection_info appname |
642 | 59 | client.update_app(appname, app) | 66 | |
643 | 60 | 67 | port = pick_tunnel_port(@options[:port] || 20000) | |
644 | 61 | Thread.kill(t) | 68 | |
645 | 62 | clear(LINE_LENGTH) | 69 | raise VMC::Client::AuthError unless client.logged_in? |
646 | 63 | display "#{banner}#{'OK'.green}" | 70 | |
647 | 64 | 71 | if not tunnel_pushed? | |
648 | 65 | banner = 'Starting Application: ' | 72 | display "Deploying tunnel application '#{tunnel_appname}'." |
649 | 66 | display banner, false | 73 | auth = UUIDTools::UUID.random_create.to_s |
650 | 67 | 74 | push_caldecott(auth) | |
651 | 68 | count = log_lines_displayed = 0 | 75 | start_caldecott |
652 | 69 | failed = false | 76 | else |
653 | 70 | start_time = Time.now.to_i | 77 | auth = tunnel_auth |
654 | 71 | 78 | end | |
655 | 72 | loop do | 79 | |
656 | 73 | display '.', false unless count > TICKER_TICKS | 80 | if not tunnel_healthy?(auth) |
657 | 74 | sleep SLEEP_TIME | 81 | display "Redeploying tunnel application '#{tunnel_appname}'." |
658 | 75 | begin | 82 | # We don't expect caldecott not to be running, so take the |
659 | 76 | break if app_started_properly(appname, count > HEALTH_TICKS) | 83 | # most aggressive restart method.. delete/re-push |
660 | 77 | if !crashes(appname, false, start_time).empty? | 84 | client.delete_app(tunnel_appname) |
661 | 78 | # Check for the existance of crashes | 85 | invalidate_tunnel_app_info |
662 | 79 | display "\nError: Application [#{appname}] failed to start, logs information below.\n".red | 86 | push_caldecott(auth) |
663 | 80 | grab_crash_logs(appname, '0', true) | 87 | start_caldecott |
664 | 81 | if push | 88 | end |
665 | 82 | display "\n" | 89 | |
666 | 83 | should_delete = ask 'Should I delete the application? (Y/n)? ' unless no_prompt | 90 | start_tunnel(port, conn_info, auth) |
667 | 84 | delete_app(appname, false) unless no_prompt || should_delete.upcase == 'N' | 91 | wait_for_tunnel_start(port) |
668 | 85 | end | 92 | start_local_console(port, appname) if interactive |
669 | 86 | failed = true | 93 | port |
670 | 87 | break | 94 | end |
671 | 88 | elsif count > TAIL_TICKS | 95 | |
672 | 89 | log_lines_displayed = grab_startup_tail(appname, log_lines_displayed) | 96 | def start(appname=nil, push=false) |
673 | 90 | end | 97 | if appname |
674 | 91 | rescue => e | 98 | do_start(appname, push) |
675 | 92 | err(e.message, '') | 99 | else |
676 | 93 | end | 100 | each_app do |name| |
677 | 94 | count += 1 | 101 | do_start(name, push) |
678 | 95 | if count > GIVEUP_TICKS # 2 minutes | 102 | end |
679 | 96 | display "\nApplication is taking too long to start, check your logs".yellow | 103 | end |
680 | 97 | break | 104 | end |
681 | 98 | end | 105 | |
682 | 99 | end | 106 | def stop(appname=nil) |
683 | 100 | exit(false) if failed | 107 | if appname |
684 | 101 | clear(LINE_LENGTH) | 108 | do_stop(appname) |
685 | 102 | display "#{banner}#{'OK'.green}" | 109 | else |
686 | 103 | end | 110 | reversed = [] |
687 | 104 | 111 | each_app do |name| | |
688 | 105 | def stop(appname) | 112 | reversed.unshift name |
689 | 106 | app = client.app_info(appname) | 113 | end |
690 | 107 | return display "Application '#{appname}' already stopped".yellow if app[:state] == 'STOPPED' | 114 | |
691 | 108 | display 'Stopping Application: ', false | 115 | reversed.each do |name| |
692 | 109 | app[:state] = 'STOPPED' | 116 | do_stop(name) |
693 | 110 | client.update_app(appname, app) | 117 | end |
694 | 111 | display 'OK'.green | 118 | end |
695 | 112 | end | 119 | end |
696 | 113 | 120 | ||
697 | 114 | def restart(appname) | 121 | def restart(appname=nil) |
698 | 115 | stop(appname) | 122 | stop(appname) |
699 | 116 | start(appname) | 123 | start(appname) |
700 | 117 | end | 124 | end |
701 | 118 | 125 | ||
702 | 119 | def rename(appname, newname) | ||
703 | 120 | app = client.app_info(appname) | ||
704 | 121 | app[:name] = newname | ||
705 | 122 | display 'Renaming Appliction: ' | ||
706 | 123 | client.update_app(newname, app) | ||
707 | 124 | display 'OK'.green | ||
708 | 125 | end | ||
709 | 126 | |||
710 | 127 | def mem(appname, memsize=nil) | 126 | def mem(appname, memsize=nil) |
711 | 128 | app = client.app_info(appname) | 127 | app = client.app_info(appname) |
712 | 129 | mem = current_mem = mem_quota_to_choice(app[:resources][:memory]) | 128 | mem = current_mem = mem_quota_to_choice(app[:resources][:memory]) |
713 | 130 | memsize = normalize_mem(memsize) if memsize | 129 | memsize = normalize_mem(memsize) if memsize |
714 | 131 | 130 | ||
723 | 132 | unless memsize | 131 | memsize ||= ask( |
724 | 133 | choose do |menu| | 132 | "Update Memory Reservation?", |
725 | 134 | menu.layout = :one_line | 133 | :default => current_mem, |
726 | 135 | menu.prompt = "Update Memory Reservation? [Current:#{current_mem}] " | 134 | :choices => mem_choices |
727 | 136 | menu.default = current_mem | 135 | ) |
720 | 137 | mem_choices.each { |choice| menu.choice(choice) { memsize = choice } } | ||
721 | 138 | end | ||
722 | 139 | end | ||
728 | 140 | 136 | ||
729 | 141 | mem = mem_choice_to_quota(mem) | 137 | mem = mem_choice_to_quota(mem) |
730 | 142 | memsize = mem_choice_to_quota(memsize) | 138 | memsize = mem_choice_to_quota(memsize) |
731 | @@ -165,7 +161,7 @@ | |||
732 | 165 | uris << url | 161 | uris << url |
733 | 166 | app[:uris] = uris | 162 | app[:uris] = uris |
734 | 167 | client.update_app(appname, app) | 163 | client.update_app(appname, app) |
736 | 168 | display "Succesfully mapped url".green | 164 | display "Successfully mapped url".green |
737 | 169 | end | 165 | end |
738 | 170 | 166 | ||
739 | 171 | def unmap(appname, url) | 167 | def unmap(appname, url) |
740 | @@ -176,18 +172,13 @@ | |||
741 | 176 | err "Invalid url" unless deleted | 172 | err "Invalid url" unless deleted |
742 | 177 | app[:uris] = uris | 173 | app[:uris] = uris |
743 | 178 | client.update_app(appname, app) | 174 | client.update_app(appname, app) |
746 | 179 | display "Succesfully unmapped url".green | 175 | display "Successfully unmapped url".green |
745 | 180 | |||
747 | 181 | end | 176 | end |
748 | 182 | 177 | ||
749 | 183 | def delete(appname=nil) | 178 | def delete(appname=nil) |
750 | 184 | force = @options[:force] | 179 | force = @options[:force] |
751 | 185 | if @options[:all] | 180 | if @options[:all] |
757 | 186 | should_delete = force && no_prompt ? 'Y' : 'N' | 181 | if no_prompt || force || ask("Delete ALL applications?", :default => false) |
753 | 187 | unless no_prompt || force | ||
754 | 188 | should_delete = ask 'Delete ALL Applications and Services? (y/N)? ' | ||
755 | 189 | end | ||
756 | 190 | if should_delete.upcase == 'Y' | ||
758 | 191 | apps = client.apps | 182 | apps = client.apps |
759 | 192 | apps.each { |app| delete_app(app[:name], force) } | 183 | apps.each { |app| delete_app(app[:name], force) } |
760 | 193 | end | 184 | end |
761 | @@ -197,48 +188,18 @@ | |||
762 | 197 | end | 188 | end |
763 | 198 | end | 189 | end |
764 | 199 | 190 | ||
765 | 200 | def delete_app(appname, force) | ||
766 | 201 | app = client.app_info(appname) | ||
767 | 202 | services_to_delete = [] | ||
768 | 203 | app_services = app[:services] | ||
769 | 204 | app_services.each { |service| | ||
770 | 205 | del_service = force && no_prompt ? 'Y' : 'N' | ||
771 | 206 | unless no_prompt || force | ||
772 | 207 | del_service = ask("Provisioned service [#{service}] detected, would you like to delete it? [yN]: ") | ||
773 | 208 | end | ||
774 | 209 | services_to_delete << service if del_service.upcase == 'Y' | ||
775 | 210 | } | ||
776 | 211 | display "Deleting application [#{appname}]: ", false | ||
777 | 212 | client.delete_app(appname) | ||
778 | 213 | display 'OK'.green | ||
779 | 214 | |||
780 | 215 | services_to_delete.each do |s| | ||
781 | 216 | display "Deleting service [#{s}]: ", false | ||
782 | 217 | client.delete_service(s) | ||
783 | 218 | display 'OK'.green | ||
784 | 219 | end | ||
785 | 220 | end | ||
786 | 221 | |||
787 | 222 | def all_files(appname, path) | ||
788 | 223 | instances_info_envelope = client.app_instances(appname) | ||
789 | 224 | return if instances_info_envelope.is_a?(Array) | ||
790 | 225 | instances_info = instances_info_envelope[:instances] || [] | ||
791 | 226 | instances_info.each do |entry| | ||
792 | 227 | content = client.app_files(appname, path, entry[:index]) | ||
793 | 228 | display_logfile(path, content, entry[:index], "====> [#{entry[:index]}: #{path}] <====\n".bold) | ||
794 | 229 | end | ||
795 | 230 | end | ||
796 | 231 | |||
797 | 232 | def files(appname, path='/') | 191 | def files(appname, path='/') |
798 | 233 | return all_files(appname, path) if @options[:all] && !@options[:instance] | 192 | return all_files(appname, path) if @options[:all] && !@options[:instance] |
799 | 234 | instance = @options[:instance] || '0' | 193 | instance = @options[:instance] || '0' |
800 | 235 | content = client.app_files(appname, path, instance) | 194 | content = client.app_files(appname, path, instance) |
801 | 236 | display content | 195 | display content |
803 | 237 | rescue VMC::Client::NotFound => e | 196 | rescue VMC::Client::NotFound, VMC::Client::TargetError |
804 | 238 | err 'No such file or directory' | 197 | err 'No such file or directory' |
805 | 239 | end | 198 | end |
806 | 240 | 199 | ||
807 | 241 | def logs(appname) | 200 | def logs(appname) |
808 | 201 | # Check if we have an app before progressing further | ||
809 | 202 | client.app_info(appname) | ||
810 | 242 | return grab_all_logs(appname) if @options[:all] && !@options[:instance] | 203 | return grab_all_logs(appname) if @options[:all] && !@options[:instance] |
811 | 243 | instance = @options[:instance] || '0' | 204 | instance = @options[:instance] || '0' |
812 | 244 | grab_logs(appname, instance) | 205 | grab_logs(appname, instance) |
813 | @@ -285,194 +246,67 @@ | |||
814 | 285 | end | 246 | end |
815 | 286 | 247 | ||
816 | 287 | def instances(appname, num=nil) | 248 | def instances(appname, num=nil) |
818 | 288 | if (num) | 249 | if num |
819 | 289 | change_instances(appname, num) | 250 | change_instances(appname, num) |
820 | 290 | else | 251 | else |
821 | 291 | get_instances(appname) | 252 | get_instances(appname) |
822 | 292 | end | 253 | end |
823 | 293 | end | 254 | end |
824 | 294 | 255 | ||
849 | 295 | def stats(appname) | 256 | def stats(appname=nil) |
850 | 296 | stats = client.app_stats(appname) | 257 | if appname |
851 | 297 | return display JSON.pretty_generate(stats) if @options[:json] | 258 | display "\n", false |
852 | 298 | 259 | do_stats(appname) | |
853 | 299 | stats_table = table do |t| | 260 | else |
854 | 300 | t.headings = 'Instance', 'CPU (Cores)', 'Memory (limit)', 'Disk (limit)', 'Uptime' | 261 | each_app do |n| |
855 | 301 | stats.each do |entry| | 262 | display "\n#{n}:" |
856 | 302 | index = entry[:instance] | 263 | do_stats(n) |
833 | 303 | stat = entry[:stats] | ||
834 | 304 | hp = "#{stat[:host]}:#{stat[:port]}" | ||
835 | 305 | uptime = uptime_string(stat[:uptime]) | ||
836 | 306 | usage = stat[:usage] | ||
837 | 307 | if usage | ||
838 | 308 | cpu = usage[:cpu] | ||
839 | 309 | mem = (usage[:mem] * 1024) # mem comes in K's | ||
840 | 310 | disk = usage[:disk] | ||
841 | 311 | end | ||
842 | 312 | mem_quota = stat[:mem_quota] | ||
843 | 313 | disk_quota = stat[:disk_quota] | ||
844 | 314 | mem = "#{pretty_size(mem)} (#{pretty_size(mem_quota, 0)})" | ||
845 | 315 | disk = "#{pretty_size(disk)} (#{pretty_size(disk_quota, 0)})" | ||
846 | 316 | cpu = cpu ? cpu.to_s : 'NA' | ||
847 | 317 | cpu = "#{cpu}% (#{stat[:cores]})" | ||
848 | 318 | t << [index, cpu, mem, disk, uptime] | ||
857 | 319 | end | 264 | end |
858 | 320 | end | 265 | end |
859 | 321 | display "\n" | ||
860 | 322 | if stats.empty? | ||
861 | 323 | display "No running instances for [#{appname}]".yellow | ||
862 | 324 | else | ||
863 | 325 | display stats_table | ||
864 | 326 | end | ||
865 | 327 | end | 266 | end |
866 | 328 | 267 | ||
871 | 329 | def update(appname) | 268 | def update(appname=nil) |
872 | 330 | app = client.app_info(appname) | 269 | if appname |
873 | 331 | if @options[:canary] | 270 | app = client.app_info(appname) |
874 | 332 | display "[--canary] is deprecated and will be removed in a future version".yellow | 271 | if @options[:canary] |
875 | 272 | display "[--canary] is deprecated and will be removed in a future version".yellow | ||
876 | 273 | end | ||
877 | 274 | upload_app_bits(appname, @path) | ||
878 | 275 | restart appname if app[:state] == 'STARTED' | ||
879 | 276 | else | ||
880 | 277 | each_app do |name| | ||
881 | 278 | display "Updating application '#{name}'..." | ||
882 | 279 | |||
883 | 280 | app = client.app_info(name) | ||
884 | 281 | upload_app_bits(name, @application) | ||
885 | 282 | restart name if app[:state] == 'STARTED' | ||
886 | 283 | end | ||
887 | 333 | end | 284 | end |
888 | 334 | path = @options[:path] || '.' | ||
889 | 335 | upload_app_bits(appname, path) | ||
890 | 336 | restart appname if app[:state] == 'STARTED' | ||
891 | 337 | end | 285 | end |
892 | 338 | 286 | ||
893 | 339 | def push(appname=nil) | 287 | def push(appname=nil) |
894 | 340 | instances = @options[:instances] || 1 | ||
895 | 341 | exec = @options[:exec] || 'thin start' | ||
896 | 342 | ignore_framework = @options[:noframework] | ||
897 | 343 | no_start = @options[:nostart] | ||
898 | 344 | |||
899 | 345 | path = @options[:path] || '.' | ||
900 | 346 | appname = @options[:name] unless appname | ||
901 | 347 | url = @options[:url] | ||
902 | 348 | mem, memswitch = nil, @options[:mem] | ||
903 | 349 | memswitch = normalize_mem(memswitch) if memswitch | ||
904 | 350 | |||
905 | 351 | # Check app existing upfront if we have appname | ||
906 | 352 | app_checked = false | ||
907 | 353 | if appname | ||
908 | 354 | err "Application '#{appname}' already exists, use update" if app_exists?(appname) | ||
909 | 355 | app_checked = true | ||
910 | 356 | else | ||
911 | 357 | raise VMC::Client::AuthError unless client.logged_in? | ||
912 | 358 | end | ||
913 | 359 | |||
914 | 360 | # check if we have hit our app limit | ||
915 | 361 | check_app_limit | ||
916 | 362 | |||
917 | 363 | # check memsize here for capacity | ||
918 | 364 | if memswitch && !no_start | ||
919 | 365 | check_has_capacity_for(mem_choice_to_quota(memswitch) * instances) | ||
920 | 366 | end | ||
921 | 367 | |||
922 | 368 | unless no_prompt || @options[:path] | 288 | unless no_prompt || @options[:path] |
1030 | 369 | proceed = ask('Would you like to deploy from the current directory? [Yn]: ') | 289 | proceed = ask( |
1031 | 370 | if proceed.upcase == 'N' | 290 | 'Would you like to deploy from the current directory?', |
1032 | 371 | path = ask('Please enter in the deployment path: ') | 291 | :default => true |
1033 | 372 | end | 292 | ) |
1034 | 373 | end | 293 | |
1035 | 374 | 294 | unless proceed | |
1036 | 375 | path = File.expand_path(path) | 295 | @path = ask('Deployment path') |
1037 | 376 | check_deploy_directory(path) | 296 | end |
1038 | 377 | 297 | end | |
1039 | 378 | appname = ask("Application Name: ") unless no_prompt || appname | 298 | |
1040 | 379 | err "Application Name required." if appname.nil? || appname.empty? | 299 | pushed = false |
1041 | 380 | 300 | each_app(false) do |name| | |
1042 | 381 | unless app_checked | 301 | display "Pushing application '#{name}'..." if name |
1043 | 382 | err "Application '#{appname}' already exists, use update or delete." if app_exists?(appname) | 302 | do_push(name) |
1044 | 383 | end | 303 | pushed = true |
1045 | 384 | 304 | end | |
1046 | 385 | unless no_prompt || url | 305 | |
1047 | 386 | url = ask("Application Deployed URL: '#{appname}.#{VMC::Cli::Config.suggest_url}'? ") | 306 | unless pushed |
1048 | 387 | 307 | @application = @path | |
1049 | 388 | # common error case is for prompted users to answer y or Y or yes or YES to this ask() resulting in an | 308 | do_push(appname) |
1050 | 389 | # unintended URL of y. Special case this common error | 309 | end |
944 | 390 | if YES_SET.member?(url) | ||
945 | 391 | #silently revert to the stock url | ||
946 | 392 | url = "#{appname}.#{VMC::Cli::Config.suggest_url}" | ||
947 | 393 | end | ||
948 | 394 | end | ||
949 | 395 | |||
950 | 396 | url = "#{appname}.#{VMC::Cli::Config.suggest_url}" if url.nil? || url.empty? | ||
951 | 397 | |||
952 | 398 | # Detect the appropriate framework. | ||
953 | 399 | framework = nil | ||
954 | 400 | unless ignore_framework | ||
955 | 401 | framework = VMC::Cli::Framework.detect(path) | ||
956 | 402 | framework_correct = ask("Detected a #{framework}, is this correct? [Yn]: ") if prompt_ok && framework | ||
957 | 403 | framework_correct ||= 'y' | ||
958 | 404 | if prompt_ok && (framework.nil? || framework_correct.upcase == 'N') | ||
959 | 405 | display "#{"[WARNING]".yellow} Can't determine the Application Type." unless framework | ||
960 | 406 | framework = nil if framework_correct.upcase == 'N' | ||
961 | 407 | choose do |menu| | ||
962 | 408 | menu.layout = :one_line | ||
963 | 409 | menu.prompt = "Select Application Type: " | ||
964 | 410 | menu.default = framework | ||
965 | 411 | VMC::Cli::Framework.known_frameworks.each do |f| | ||
966 | 412 | menu.choice(f) { framework = VMC::Cli::Framework.lookup(f) } | ||
967 | 413 | end | ||
968 | 414 | end | ||
969 | 415 | display "Selected #{framework}" | ||
970 | 416 | end | ||
971 | 417 | # Framework override, deprecated | ||
972 | 418 | exec = framework.exec if framework && framework.exec | ||
973 | 419 | else | ||
974 | 420 | framework = VMC::Cli::Framework.new | ||
975 | 421 | end | ||
976 | 422 | |||
977 | 423 | err "Application Type undetermined for path '#{path}'" unless framework | ||
978 | 424 | unless memswitch | ||
979 | 425 | mem = framework.memory | ||
980 | 426 | if prompt_ok | ||
981 | 427 | choose do |menu| | ||
982 | 428 | menu.layout = :one_line | ||
983 | 429 | menu.prompt = "Memory Reservation [Default:#{mem}] " | ||
984 | 430 | menu.default = mem | ||
985 | 431 | mem_choices.each { |choice| menu.choice(choice) { mem = choice } } | ||
986 | 432 | end | ||
987 | 433 | end | ||
988 | 434 | else | ||
989 | 435 | mem = memswitch | ||
990 | 436 | end | ||
991 | 437 | |||
992 | 438 | # Set to MB number | ||
993 | 439 | mem_quota = mem_choice_to_quota(mem) | ||
994 | 440 | |||
995 | 441 | # check memsize here for capacity | ||
996 | 442 | check_has_capacity_for(mem_quota * instances) unless no_start | ||
997 | 443 | |||
998 | 444 | display 'Creating Application: ', false | ||
999 | 445 | |||
1000 | 446 | manifest = { | ||
1001 | 447 | :name => "#{appname}", | ||
1002 | 448 | :staging => { | ||
1003 | 449 | :framework => framework.name, | ||
1004 | 450 | :runtime => @options[:runtime] | ||
1005 | 451 | }, | ||
1006 | 452 | :uris => [url], | ||
1007 | 453 | :instances => instances, | ||
1008 | 454 | :resources => { | ||
1009 | 455 | :memory => mem_quota | ||
1010 | 456 | }, | ||
1011 | 457 | } | ||
1012 | 458 | |||
1013 | 459 | # Send the manifest to the cloud controller | ||
1014 | 460 | client.create_app(appname, manifest) | ||
1015 | 461 | display 'OK'.green | ||
1016 | 462 | |||
1017 | 463 | # Services check | ||
1018 | 464 | unless no_prompt || @options[:noservices] | ||
1019 | 465 | services = client.services_info | ||
1020 | 466 | unless services.empty? | ||
1021 | 467 | proceed = ask("Would you like to bind any services to '#{appname}'? [yN]: ") | ||
1022 | 468 | bind_services(appname, services) if proceed.upcase == 'Y' | ||
1023 | 469 | end | ||
1024 | 470 | end | ||
1025 | 471 | |||
1026 | 472 | # Stage and upload the app bits. | ||
1027 | 473 | upload_app_bits(appname, path) | ||
1028 | 474 | |||
1029 | 475 | start(appname, true) unless no_start | ||
1051 | 476 | end | 310 | end |
1052 | 477 | 311 | ||
1053 | 478 | def environment(appname) | 312 | def environment(appname) |
1054 | @@ -483,7 +317,7 @@ | |||
1055 | 483 | etable = table do |t| | 317 | etable = table do |t| |
1056 | 484 | t.headings = 'Variable', 'Value' | 318 | t.headings = 'Variable', 'Value' |
1057 | 485 | env.each do |e| | 319 | env.each do |e| |
1059 | 486 | k,v = e.split('=') | 320 | k,v = e.split('=', 2) |
1060 | 487 | t << [k, v] | 321 | t << [k, v] |
1061 | 488 | end | 322 | end |
1062 | 489 | end | 323 | end |
1063 | @@ -494,7 +328,7 @@ | |||
1064 | 494 | def environment_add(appname, k, v=nil) | 328 | def environment_add(appname, k, v=nil) |
1065 | 495 | app = client.app_info(appname) | 329 | app = client.app_info(appname) |
1066 | 496 | env = app[:env] || [] | 330 | env = app[:env] || [] |
1068 | 497 | k,v = k.split('=') unless v | 331 | k,v = k.split('=', 2) unless v |
1069 | 498 | env << "#{k}=#{v}" | 332 | env << "#{k}=#{v}" |
1070 | 499 | display "Adding Environment Variable [#{k}=#{v}]: ", false | 333 | display "Adding Environment Variable [#{k}=#{v}]: ", false |
1071 | 500 | app[:env] = env | 334 | app[:env] = env |
1072 | @@ -537,11 +371,35 @@ | |||
1073 | 537 | 371 | ||
1074 | 538 | def check_deploy_directory(path) | 372 | def check_deploy_directory(path) |
1075 | 539 | err 'Deployment path does not exist' unless File.exists? path | 373 | err 'Deployment path does not exist' unless File.exists? path |
1076 | 540 | err 'Deployment path is not a directory' unless File.directory? path | ||
1077 | 541 | return if File.expand_path(Dir.tmpdir) != File.expand_path(path) | 374 | return if File.expand_path(Dir.tmpdir) != File.expand_path(path) |
1078 | 542 | err "Can't deploy applications from staging directory: [#{Dir.tmpdir}]" | 375 | err "Can't deploy applications from staging directory: [#{Dir.tmpdir}]" |
1079 | 543 | end | 376 | end |
1080 | 544 | 377 | ||
1081 | 378 | def check_unreachable_links(path) | ||
1082 | 379 | files = Dir.glob("#{path}/**/*", File::FNM_DOTMATCH) | ||
1083 | 380 | |||
1084 | 381 | pwd = Pathname.pwd | ||
1085 | 382 | |||
1086 | 383 | abspath = File.expand_path(path) | ||
1087 | 384 | unreachable = [] | ||
1088 | 385 | files.each do |f| | ||
1089 | 386 | file = Pathname.new(f) | ||
1090 | 387 | if file.symlink? && !file.realpath.to_s.start_with?(abspath) | ||
1091 | 388 | unreachable << file.relative_path_from(pwd) | ||
1092 | 389 | end | ||
1093 | 390 | end | ||
1094 | 391 | |||
1095 | 392 | unless unreachable.empty? | ||
1096 | 393 | root = Pathname.new(path).relative_path_from(pwd) | ||
1097 | 394 | err "Can't deploy application containing links '#{unreachable}' that reach outside its root '#{root}'" | ||
1098 | 395 | end | ||
1099 | 396 | end | ||
1100 | 397 | |||
1101 | 398 | def find_sockets(path) | ||
1102 | 399 | files = Dir.glob("#{path}/**/*", File::FNM_DOTMATCH) | ||
1103 | 400 | files && files.select { |f| File.socket? f } | ||
1104 | 401 | end | ||
1105 | 402 | |||
1106 | 545 | def upload_app_bits(appname, path) | 403 | def upload_app_bits(appname, path) |
1107 | 546 | display 'Uploading Application:' | 404 | display 'Uploading Application:' |
1108 | 547 | 405 | ||
1109 | @@ -551,147 +409,112 @@ | |||
1110 | 551 | explode_dir = "#{Dir.tmpdir}/.vmc_#{appname}_files" | 409 | explode_dir = "#{Dir.tmpdir}/.vmc_#{appname}_files" |
1111 | 552 | FileUtils.rm_rf(explode_dir) # Make sure we didn't have anything left over.. | 410 | FileUtils.rm_rf(explode_dir) # Make sure we didn't have anything left over.. |
1112 | 553 | 411 | ||
1153 | 554 | Dir.chdir(path) do | 412 | if path =~ /\.(war|zip)$/ |
1154 | 555 | # Stage the app appropriately and do the appropriate fingerprinting, etc. | 413 | #single file that needs unpacking |
1155 | 556 | if war_file = Dir.glob('*.war').first | 414 | VMC::Cli::ZipUtil.unpack(path, explode_dir) |
1156 | 557 | VMC::Cli::ZipUtil.unpack(war_file, explode_dir) | 415 | elsif !File.directory? path |
1157 | 558 | else | 416 | #single file that doesn't need unpacking |
1158 | 559 | FileUtils.mkdir(explode_dir) | 417 | FileUtils.mkdir(explode_dir) |
1159 | 560 | files = Dir.glob('{*,.[^\.]*}') | 418 | FileUtils.cp(path,explode_dir) |
1160 | 561 | FileUtils.cp_r(files, explode_dir) | 419 | else |
1161 | 562 | end | 420 | Dir.chdir(path) do |
1162 | 563 | 421 | # Stage the app appropriately and do the appropriate fingerprinting, etc. | |
1163 | 564 | # Send the resource list to the cloudcontroller, the response will tell us what it already has.. | 422 | if war_file = Dir.glob('*.war').first |
1164 | 565 | unless @options[:noresources] | 423 | VMC::Cli::ZipUtil.unpack(war_file, explode_dir) |
1165 | 566 | display ' Checking for available resources: ', false | 424 | elsif zip_file = Dir.glob('*.zip').first |
1166 | 567 | fingerprints = [] | 425 | VMC::Cli::ZipUtil.unpack(zip_file, explode_dir) |
1167 | 568 | total_size = 0 | 426 | else |
1168 | 569 | resource_files = Dir.glob("#{explode_dir}/**/*", File::FNM_DOTMATCH) | 427 | check_unreachable_links(path) |
1169 | 570 | resource_files.each do |filename| | 428 | FileUtils.mkdir(explode_dir) |
1170 | 571 | next if (File.directory?(filename) || !File.exists?(filename)) | 429 | |
1171 | 572 | fingerprints << { | 430 | files = Dir.glob('{*,.[^\.]*}') |
1172 | 573 | :size => File.size(filename), | 431 | |
1173 | 574 | :sha1 => Digest::SHA1.file(filename).hexdigest, | 432 | # Do not process .git files |
1174 | 575 | :fn => filename | 433 | files.delete('.git') if files |
1175 | 576 | } | 434 | |
1176 | 577 | total_size += File.size(filename) | 435 | FileUtils.cp_r(files, explode_dir) |
1177 | 578 | end | 436 | |
1178 | 579 | 437 | find_sockets(explode_dir).each do |s| | |
1179 | 580 | # Check to see if the resource check is worth the round trip | 438 | File.delete s |
1140 | 581 | if (total_size > (64*1024)) # 64k for now | ||
1141 | 582 | # Send resource fingerprints to the cloud controller | ||
1142 | 583 | appcloud_resources = client.check_resources(fingerprints) | ||
1143 | 584 | end | ||
1144 | 585 | display 'OK'.green | ||
1145 | 586 | |||
1146 | 587 | if appcloud_resources | ||
1147 | 588 | display ' Processing resources: ', false | ||
1148 | 589 | # We can then delete what we do not need to send. | ||
1149 | 590 | appcloud_resources.each do |resource| | ||
1150 | 591 | FileUtils.rm_f resource[:fn] | ||
1151 | 592 | # adjust filenames sans the explode_dir prefix | ||
1152 | 593 | resource[:fn].sub!("#{explode_dir}/", '') | ||
1180 | 594 | end | 439 | end |
1215 | 595 | display 'OK'.green | 440 | end |
1216 | 596 | end | 441 | end |
1217 | 597 | 442 | end | |
1218 | 598 | end | 443 | |
1219 | 599 | 444 | # Send the resource list to the cloudcontroller, the response will tell us what it already has.. | |
1220 | 600 | # Perform Packing of the upload bits here. | 445 | unless @options[:noresources] |
1221 | 601 | unless VMC::Cli::ZipUtil.get_files_to_pack(explode_dir).empty? | 446 | display ' Checking for available resources: ', false |
1222 | 602 | display ' Packing application: ', false | 447 | fingerprints = [] |
1223 | 603 | VMC::Cli::ZipUtil.pack(explode_dir, upload_file) | 448 | total_size = 0 |
1224 | 604 | display 'OK'.green | 449 | resource_files = Dir.glob("#{explode_dir}/**/*", File::FNM_DOTMATCH) |
1225 | 605 | 450 | resource_files.each do |filename| | |
1226 | 606 | upload_size = File.size(upload_file); | 451 | next if (File.directory?(filename) || !File.exists?(filename)) |
1227 | 607 | if upload_size > 1024*1024 | 452 | fingerprints << { |
1228 | 608 | upload_size = (upload_size/(1024.0*1024.0)).round.to_s + 'M' | 453 | :size => File.size(filename), |
1229 | 609 | elsif upload_size > 0 | 454 | :sha1 => Digest::SHA1.file(filename).hexdigest, |
1230 | 610 | upload_size = (upload_size/1024.0).round.to_s + 'K' | 455 | :fn => filename |
1231 | 611 | end | 456 | } |
1232 | 612 | else | 457 | total_size += File.size(filename) |
1233 | 613 | upload_size = '0K' | 458 | end |
1234 | 614 | end | 459 | |
1235 | 615 | 460 | # Check to see if the resource check is worth the round trip | |
1236 | 616 | upload_str = " Uploading (#{upload_size}): " | 461 | if (total_size > (64*1024)) # 64k for now |
1237 | 617 | display upload_str, false | 462 | # Send resource fingerprints to the cloud controller |
1238 | 618 | 463 | appcloud_resources = client.check_resources(fingerprints) | |
1239 | 619 | unless VMC::Cli::ZipUtil.get_files_to_pack(explode_dir).empty? | 464 | end |
1206 | 620 | FileWithPercentOutput.display_str = upload_str | ||
1207 | 621 | FileWithPercentOutput.upload_size = File.size(upload_file); | ||
1208 | 622 | file = FileWithPercentOutput.open(upload_file, 'rb') | ||
1209 | 623 | end | ||
1210 | 624 | |||
1211 | 625 | client.upload_app(appname, file, appcloud_resources) | ||
1212 | 626 | display 'OK'.green if VMC::Cli::ZipUtil.get_files_to_pack(explode_dir).empty? | ||
1213 | 627 | |||
1214 | 628 | display 'Push Status: ', false | ||
1240 | 629 | display 'OK'.green | 465 | display 'OK'.green |
1306 | 630 | end | 466 | |
1307 | 631 | 467 | if appcloud_resources | |
1308 | 632 | ensure | 468 | display ' Processing resources: ', false |
1309 | 633 | # Cleanup if we created an exploded directory. | 469 | # We can then delete what we do not need to send. |
1310 | 634 | FileUtils.rm_f(upload_file) if upload_file | 470 | appcloud_resources.each do |resource| |
1311 | 635 | FileUtils.rm_rf(explode_dir) if explode_dir | 471 | FileUtils.rm_f resource[:fn] |
1312 | 636 | end | 472 | # adjust filenames sans the explode_dir prefix |
1313 | 637 | 473 | resource[:fn].sub!("#{explode_dir}/", '') | |
1314 | 638 | def choose_existing_service(appname, user_services) | 474 | end |
1315 | 639 | return unless prompt_ok | 475 | display 'OK'.green |
1316 | 640 | selected = false | 476 | end |
1317 | 641 | choose do |menu| | 477 | |
1318 | 642 | menu.header = "The following provisioned services are available:" | 478 | end |
1319 | 643 | menu.prompt = 'Please select one you wish to provision: ' | 479 | |
1320 | 644 | menu.select_by = :index_or_name | 480 | # If no resource needs to be sent, add an empty file to ensure we have |
1321 | 645 | user_services.each do |s| | 481 | # a multi-part request that is expected by nginx fronting the CC. |
1322 | 646 | menu.choice(s[:name]) do | 482 | if VMC::Cli::ZipUtil.get_files_to_pack(explode_dir).empty? |
1323 | 647 | display "Binding Service: ", false | 483 | Dir.chdir(explode_dir) do |
1324 | 648 | client.bind_service(s[:name], appname) | 484 | File.new(".__empty__", "w") |
1325 | 649 | display 'OK'.green | 485 | end |
1326 | 650 | selected = true | 486 | end |
1327 | 651 | end | 487 | # Perform Packing of the upload bits here. |
1328 | 652 | end | 488 | display ' Packing application: ', false |
1329 | 653 | end | 489 | VMC::Cli::ZipUtil.pack(explode_dir, upload_file) |
1330 | 654 | selected | 490 | display 'OK'.green |
1331 | 655 | end | 491 | |
1332 | 656 | 492 | upload_size = File.size(upload_file); | |
1333 | 657 | def choose_new_service(appname, services) | 493 | if upload_size > 1024*1024 |
1334 | 658 | return unless prompt_ok | 494 | upload_size = (upload_size/(1024.0*1024.0)).round.to_s + 'M' |
1335 | 659 | choose do |menu| | 495 | elsif upload_size > 0 |
1336 | 660 | menu.header = "The following system services are available:" | 496 | upload_size = (upload_size/1024.0).round.to_s + 'K' |
1337 | 661 | menu.prompt = 'Please select one you wish to provision: ' | 497 | else |
1338 | 662 | menu.select_by = :index_or_name | 498 | upload_size = '0K' |
1339 | 663 | service_choices = [] | 499 | end |
1340 | 664 | services.each do |service_type, value| | 500 | |
1341 | 665 | value.each do |vendor, version| | 501 | upload_str = " Uploading (#{upload_size}): " |
1342 | 666 | service_choices << vendor | 502 | display upload_str, false |
1343 | 667 | end | 503 | |
1344 | 668 | end | 504 | FileWithPercentOutput.display_str = upload_str |
1345 | 669 | service_choices.sort! {|a, b| a.to_s <=> b.to_s } | 505 | FileWithPercentOutput.upload_size = File.size(upload_file); |
1346 | 670 | service_choices.each do |vendor| | 506 | file = FileWithPercentOutput.open(upload_file, 'rb') |
1347 | 671 | menu.choice(vendor) do | 507 | |
1348 | 672 | default_name = random_service_name(vendor) | 508 | client.upload_app(appname, file, appcloud_resources) |
1349 | 673 | service_name = ask("Specify the name of the service [#{default_name}]: ") | 509 | display 'OK'.green if VMC::Cli::ZipUtil.get_files_to_pack(explode_dir).empty? |
1350 | 674 | service_name = default_name if service_name.empty? | 510 | |
1351 | 675 | create_service_banner(vendor, service_name) | 511 | display 'Push Status: ', false |
1352 | 676 | bind_service_banner(service_name, appname) | 512 | display 'OK'.green |
1353 | 677 | end | 513 | |
1354 | 678 | end | 514 | ensure |
1355 | 679 | end | 515 | # Cleanup if we created an exploded directory. |
1356 | 680 | end | 516 | FileUtils.rm_f(upload_file) if upload_file |
1357 | 681 | 517 | FileUtils.rm_rf(explode_dir) if explode_dir | |
1293 | 682 | def bind_services(appname, services) | ||
1294 | 683 | user_services = client.services | ||
1295 | 684 | selected_existing = false | ||
1296 | 685 | unless no_prompt || user_services.empty? | ||
1297 | 686 | use_existing = ask "Would you like to use an existing provisioned service [yN]? " | ||
1298 | 687 | if use_existing.upcase == 'Y' | ||
1299 | 688 | selected_existing = choose_existing_service(appname, user_services) | ||
1300 | 689 | end | ||
1301 | 690 | end | ||
1302 | 691 | # Create a new service and bind it here | ||
1303 | 692 | unless selected_existing | ||
1304 | 693 | choose_new_service(appname, services) | ||
1305 | 694 | end | ||
1358 | 695 | end | 518 | end |
1359 | 696 | 519 | ||
1360 | 697 | def check_app_limit | 520 | def check_app_limit |
1361 | @@ -768,9 +591,19 @@ | |||
1362 | 768 | return display "No running instances for [#{appname}]".yellow if instances_info.empty? | 591 | return display "No running instances for [#{appname}]".yellow if instances_info.empty? |
1363 | 769 | 592 | ||
1364 | 770 | instances_table = table do |t| | 593 | instances_table = table do |t| |
1366 | 771 | t.headings = 'Index', 'State', 'Start Time' | 594 | show_debug = instances_info.any? { |e| e[:debug_port] } |
1367 | 595 | |||
1368 | 596 | headings = ['Index', 'State', 'Start Time'] | ||
1369 | 597 | headings << 'Debug IP' if show_debug | ||
1370 | 598 | headings << 'Debug Port' if show_debug | ||
1371 | 599 | |||
1372 | 600 | t.headings = headings | ||
1373 | 601 | |||
1374 | 772 | instances_info.each do |entry| | 602 | instances_info.each do |entry| |
1376 | 773 | t << [entry[:index], entry[:state], Time.at(entry[:since]).strftime("%m/%d/%Y %I:%M%p")] | 603 | row = [entry[:index], entry[:state], Time.at(entry[:since]).strftime("%m/%d/%Y %I:%M%p")] |
1377 | 604 | row << entry[:debug_ip] if show_debug | ||
1378 | 605 | row << entry[:debug_port] if show_debug | ||
1379 | 606 | t << row | ||
1380 | 774 | end | 607 | end |
1381 | 775 | end | 608 | end |
1382 | 776 | display "\n" | 609 | display "\n" |
1383 | @@ -822,18 +655,24 @@ | |||
1384 | 822 | case health(app) | 655 | case health(app) |
1385 | 823 | when 'N/A' | 656 | when 'N/A' |
1386 | 824 | # Health manager not running. | 657 | # Health manager not running. |
1388 | 825 | err "\Application '#{appname}'s state is undetermined, not enough information available." if error_on_health | 658 | err "\nApplication '#{appname}'s state is undetermined, not enough information available." if error_on_health |
1389 | 826 | return false | 659 | return false |
1390 | 827 | when 'RUNNING' | 660 | when 'RUNNING' |
1391 | 828 | return true | 661 | return true |
1392 | 829 | else | 662 | else |
1394 | 830 | return false | 663 | if app[:meta][:debug] == "suspend" |
1395 | 664 | display "\nApplication [#{appname}] has started in a mode that is waiting for you to trigger startup." | ||
1396 | 665 | return true | ||
1397 | 666 | else | ||
1398 | 667 | return false | ||
1399 | 668 | end | ||
1400 | 831 | end | 669 | end |
1401 | 832 | end | 670 | end |
1402 | 833 | 671 | ||
1403 | 834 | def display_logfile(path, content, instance='0', banner=nil) | 672 | def display_logfile(path, content, instance='0', banner=nil) |
1404 | 835 | banner ||= "====> #{path} <====\n\n" | 673 | banner ||= "====> #{path} <====\n\n" |
1406 | 836 | if content && !content.empty? | 674 | |
1407 | 675 | unless content.empty? | ||
1408 | 837 | display banner | 676 | display banner |
1409 | 838 | prefix = "[#{instance}: #{path}] -".bold if @options[:prefixlogs] | 677 | prefix = "[#{instance}: #{path}] -".bold if @options[:prefixlogs] |
1410 | 839 | unless prefix | 678 | unless prefix |
1411 | @@ -846,10 +685,6 @@ | |||
1412 | 846 | end | 685 | end |
1413 | 847 | end | 686 | end |
1414 | 848 | 687 | ||
1415 | 849 | def log_file_paths | ||
1416 | 850 | %w[logs/stderr.log logs/stdout.log logs/startup.log] | ||
1417 | 851 | end | ||
1418 | 852 | |||
1419 | 853 | def grab_all_logs(appname) | 688 | def grab_all_logs(appname) |
1420 | 854 | instances_info_envelope = client.app_instances(appname) | 689 | instances_info_envelope = client.app_instances(appname) |
1421 | 855 | return if instances_info_envelope.is_a?(Array) | 690 | return if instances_info_envelope.is_a?(Array) |
1422 | @@ -860,13 +695,21 @@ | |||
1423 | 860 | end | 695 | end |
1424 | 861 | 696 | ||
1425 | 862 | def grab_logs(appname, instance) | 697 | def grab_logs(appname, instance) |
1427 | 863 | log_file_paths.each do |path| | 698 | files_under(appname, instance, "/logs").each do |path| |
1428 | 864 | begin | 699 | begin |
1429 | 865 | content = client.app_files(appname, path, instance) | 700 | content = client.app_files(appname, path, instance) |
1431 | 866 | rescue | 701 | display_logfile(path, content, instance) |
1432 | 702 | rescue VMC::Client::NotFound, VMC::Client::TargetError | ||
1433 | 867 | end | 703 | end |
1436 | 868 | display_logfile(path, content, instance) | 704 | end |
1437 | 869 | end | 705 | end |
1438 | 706 | |||
1439 | 707 | def files_under(appname, instance, path) | ||
1440 | 708 | client.app_files(appname, path, instance).split("\n").collect do |l| | ||
1441 | 709 | "#{path}/#{l.split[0]}" | ||
1442 | 710 | end | ||
1443 | 711 | rescue VMC::Client::NotFound, VMC::Client::TargetError | ||
1444 | 712 | [] | ||
1445 | 870 | end | 713 | end |
1446 | 871 | 714 | ||
1447 | 872 | def grab_crash_logs(appname, instance, was_staged=false) | 715 | def grab_crash_logs(appname, instance, was_staged=false) |
1448 | @@ -877,11 +720,10 @@ | |||
1449 | 877 | map = VMC::Cli::Config.instances | 720 | map = VMC::Cli::Config.instances |
1450 | 878 | instance = map[instance] if map[instance] | 721 | instance = map[instance] if map[instance] |
1451 | 879 | 722 | ||
1457 | 880 | ['/logs/err.log', '/logs/staging.log', 'logs/stderr.log', 'logs/stdout.log', 'logs/startup.log'].each do |path| | 723 | (files_under(appname, instance, "/logs") + |
1458 | 881 | begin | 724 | files_under(appname, instance, "/app/logs") + |
1459 | 882 | content = client.app_files(appname, path, instance) | 725 | files_under(appname, instance, "/app/log")).each do |path| |
1460 | 883 | rescue | 726 | content = client.app_files(appname, path, instance) |
1456 | 884 | end | ||
1461 | 885 | display_logfile(path, content, instance) | 727 | display_logfile(path, content, instance) |
1462 | 886 | end | 728 | end |
1463 | 887 | end | 729 | end |
1464 | @@ -899,8 +741,355 @@ | |||
1465 | 899 | display tail.join("\n") if new_lines > 0 | 741 | display tail.join("\n") if new_lines > 0 |
1466 | 900 | end | 742 | end |
1467 | 901 | since + new_lines | 743 | since + new_lines |
1470 | 902 | end | 744 | rescue VMC::Client::NotFound, VMC::Client::TargetError |
1471 | 903 | rescue | 745 | 0 |
1472 | 746 | end | ||
1473 | 747 | |||
1474 | 748 | def provisioned_services_apps_hash | ||
1475 | 749 | apps = client.apps | ||
1476 | 750 | services_apps_hash = {} | ||
1477 | 751 | apps.each {|app| | ||
1478 | 752 | app[:services].each { |svc| | ||
1479 | 753 | svc_apps = services_apps_hash[svc] | ||
1480 | 754 | unless svc_apps | ||
1481 | 755 | svc_apps = Set.new | ||
1482 | 756 | services_apps_hash[svc] = svc_apps | ||
1483 | 757 | end | ||
1484 | 758 | svc_apps.add(app[:name]) | ||
1485 | 759 | } unless app[:services] == nil | ||
1486 | 760 | } | ||
1487 | 761 | services_apps_hash | ||
1488 | 762 | end | ||
1489 | 763 | |||
1490 | 764 | def delete_app(appname, force) | ||
1491 | 765 | app = client.app_info(appname) | ||
1492 | 766 | services_to_delete = [] | ||
1493 | 767 | app_services = app[:services] | ||
1494 | 768 | services_apps_hash = provisioned_services_apps_hash | ||
1495 | 769 | app_services.each { |service| | ||
1496 | 770 | del_service = force && no_prompt | ||
1497 | 771 | unless no_prompt || force | ||
1498 | 772 | del_service = ask( | ||
1499 | 773 | "Provisioned service [#{service}] detected, would you like to delete it?", | ||
1500 | 774 | :default => false | ||
1501 | 775 | ) | ||
1502 | 776 | |||
1503 | 777 | if del_service | ||
1504 | 778 | apps_using_service = services_apps_hash[service].reject!{ |app| app == appname} | ||
1505 | 779 | if apps_using_service.size > 0 | ||
1506 | 780 | del_service = ask( | ||
1507 | 781 | "Provisioned service [#{service}] is also used by #{apps_using_service.size == 1 ? "app" : "apps"} #{apps_using_service.entries}, are you sure you want to delete it?", | ||
1508 | 782 | :default => false | ||
1509 | 783 | ) | ||
1510 | 784 | end | ||
1511 | 785 | end | ||
1512 | 786 | end | ||
1513 | 787 | services_to_delete << service if del_service | ||
1514 | 788 | } | ||
1515 | 789 | |||
1516 | 790 | display "Deleting application [#{appname}]: ", false | ||
1517 | 791 | client.delete_app(appname) | ||
1518 | 792 | display 'OK'.green | ||
1519 | 793 | |||
1520 | 794 | services_to_delete.each do |s| | ||
1521 | 795 | delete_service_banner(s) | ||
1522 | 796 | end | ||
1523 | 797 | end | ||
1524 | 798 | |||
1525 | 799 | def do_start(appname, push=false) | ||
1526 | 800 | app = client.app_info(appname) | ||
1527 | 801 | return display "Application '#{appname}' could not be found".red if app.nil? | ||
1528 | 802 | return display "Application '#{appname}' already started".yellow if app[:state] == 'STARTED' | ||
1529 | 803 | |||
1530 | 804 | |||
1531 | 805 | |||
1532 | 806 | if @options[:debug] | ||
1533 | 807 | runtimes = client.runtimes_info | ||
1534 | 808 | return display "Cannot get runtime information." unless runtimes | ||
1535 | 809 | |||
1536 | 810 | runtime = runtimes[app[:staging][:stack].to_sym] | ||
1537 | 811 | return display "Unknown runtime." unless runtime | ||
1538 | 812 | |||
1539 | 813 | unless runtime[:debug_modes] and runtime[:debug_modes].include? @options[:debug] | ||
1540 | 814 | modes = runtime[:debug_modes] || [] | ||
1541 | 815 | |||
1542 | 816 | display "\nApplication '#{appname}' cannot start in '#{@options[:debug]}' mode" | ||
1543 | 817 | |||
1544 | 818 | if push | ||
1545 | 819 | display "Try 'vmc start' with one of the following modes: #{modes.inspect}" | ||
1546 | 820 | else | ||
1547 | 821 | display "Available modes: #{modes.inspect}" | ||
1548 | 822 | end | ||
1549 | 823 | |||
1550 | 824 | return | ||
1551 | 825 | end | ||
1552 | 826 | end | ||
1553 | 827 | |||
1554 | 828 | banner = "Staging Application '#{appname}': " | ||
1555 | 829 | display banner, false | ||
1556 | 830 | |||
1557 | 831 | t = Thread.new do | ||
1558 | 832 | count = 0 | ||
1559 | 833 | while count < TAIL_TICKS do | ||
1560 | 834 | display '.', false | ||
1561 | 835 | sleep SLEEP_TIME | ||
1562 | 836 | count += 1 | ||
1563 | 837 | end | ||
1564 | 838 | end | ||
1565 | 839 | |||
1566 | 840 | app[:state] = 'STARTED' | ||
1567 | 841 | app[:debug] = @options[:debug] | ||
1568 | 842 | app[:console] = VMC::Cli::Framework.lookup_by_framework(app[:staging][:model]).console | ||
1569 | 843 | client.update_app(appname, app) | ||
1570 | 844 | |||
1571 | 845 | Thread.kill(t) | ||
1572 | 846 | clear(LINE_LENGTH) | ||
1573 | 847 | display "#{banner}#{'OK'.green}" | ||
1574 | 848 | |||
1575 | 849 | banner = "Starting Application '#{appname}': " | ||
1576 | 850 | display banner, false | ||
1577 | 851 | |||
1578 | 852 | count = log_lines_displayed = 0 | ||
1579 | 853 | failed = false | ||
1580 | 854 | start_time = Time.now.to_i | ||
1581 | 855 | |||
1582 | 856 | loop do | ||
1583 | 857 | display '.', false unless count > TICKER_TICKS | ||
1584 | 858 | sleep SLEEP_TIME | ||
1585 | 859 | |||
1586 | 860 | break if app_started_properly(appname, count > HEALTH_TICKS) | ||
1587 | 861 | |||
1588 | 862 | if !crashes(appname, false, start_time).empty? | ||
1589 | 863 | # Check for the existance of crashes | ||
1590 | 864 | display "\nError: Application [#{appname}] failed to start, logs information below.\n".red | ||
1591 | 865 | grab_crash_logs(appname, '0', true) | ||
1592 | 866 | if push and !no_prompt | ||
1593 | 867 | display "\n" | ||
1594 | 868 | delete_app(appname, false) if ask "Delete the application?", :default => true | ||
1595 | 869 | end | ||
1596 | 870 | failed = true | ||
1597 | 871 | break | ||
1598 | 872 | elsif count > TAIL_TICKS | ||
1599 | 873 | log_lines_displayed = grab_startup_tail(appname, log_lines_displayed) | ||
1600 | 874 | end | ||
1601 | 875 | |||
1602 | 876 | count += 1 | ||
1603 | 877 | if count > GIVEUP_TICKS # 2 minutes | ||
1604 | 878 | display "\nApplication is taking too long to start, check your logs".yellow | ||
1605 | 879 | break | ||
1606 | 880 | end | ||
1607 | 881 | end | ||
1608 | 882 | exit(false) if failed | ||
1609 | 883 | clear(LINE_LENGTH) | ||
1610 | 884 | display "#{banner}#{'OK'.green}" | ||
1611 | 885 | end | ||
1612 | 886 | |||
1613 | 887 | def do_stop(appname) | ||
1614 | 888 | app = client.app_info(appname) | ||
1615 | 889 | return display "Application '#{appname}' already stopped".yellow if app[:state] == 'STOPPED' | ||
1616 | 890 | display "Stopping Application '#{appname}': ", false | ||
1617 | 891 | app[:state] = 'STOPPED' | ||
1618 | 892 | client.update_app(appname, app) | ||
1619 | 893 | display 'OK'.green | ||
1620 | 894 | end | ||
1621 | 895 | |||
1622 | 896 | def do_push(appname=nil) | ||
1623 | 897 | unless @app_info || no_prompt | ||
1624 | 898 | @manifest = { "applications" => { @path => { "name" => appname } } } | ||
1625 | 899 | |||
1626 | 900 | interact | ||
1627 | 901 | |||
1628 | 902 | if ask("Would you like to save this configuration?", :default => false) | ||
1629 | 903 | save_manifest | ||
1630 | 904 | end | ||
1631 | 905 | |||
1632 | 906 | resolve_manifest(@manifest) | ||
1633 | 907 | |||
1634 | 908 | @app_info = @manifest["applications"][@path] | ||
1635 | 909 | end | ||
1636 | 910 | |||
1637 | 911 | instances = info(:instances, 1) | ||
1638 | 912 | exec = info(:exec, 'thin start') | ||
1639 | 913 | |||
1640 | 914 | ignore_framework = @options[:noframework] | ||
1641 | 915 | no_start = @options[:nostart] | ||
1642 | 916 | |||
1643 | 917 | appname ||= info(:name) | ||
1644 | 918 | url = info(:url) || info(:urls) | ||
1645 | 919 | mem, memswitch = nil, info(:mem) | ||
1646 | 920 | memswitch = normalize_mem(memswitch) if memswitch | ||
1647 | 921 | command = info(:command) | ||
1648 | 922 | runtime = info(:runtime) | ||
1649 | 923 | |||
1650 | 924 | # Check app existing upfront if we have appname | ||
1651 | 925 | app_checked = false | ||
1652 | 926 | if appname | ||
1653 | 927 | err "Application '#{appname}' already exists, use update" if app_exists?(appname) | ||
1654 | 928 | app_checked = true | ||
1655 | 929 | else | ||
1656 | 930 | raise VMC::Client::AuthError unless client.logged_in? | ||
1657 | 931 | end | ||
1658 | 932 | |||
1659 | 933 | # check if we have hit our app limit | ||
1660 | 934 | check_app_limit | ||
1661 | 935 | # check memsize here for capacity | ||
1662 | 936 | if memswitch && !no_start | ||
1663 | 937 | check_has_capacity_for(mem_choice_to_quota(memswitch) * instances) | ||
1664 | 938 | end | ||
1665 | 939 | |||
1666 | 940 | appname ||= ask("Application Name") unless no_prompt | ||
1667 | 941 | err "Application Name required." if appname.nil? || appname.empty? | ||
1668 | 942 | |||
1669 | 943 | check_deploy_directory(@application) | ||
1670 | 944 | |||
1671 | 945 | if !app_checked and app_exists?(appname) | ||
1672 | 946 | err "Application '#{appname}' already exists, use update or delete." | ||
1673 | 947 | end | ||
1674 | 948 | |||
1675 | 949 | if ignore_framework | ||
1676 | 950 | framework = VMC::Cli::Framework.new | ||
1677 | 951 | elsif f = info(:framework) | ||
1678 | 952 | info = Hash[f["info"].collect { |k, v| [k.to_sym, v] }] | ||
1679 | 953 | |||
1680 | 954 | framework = VMC::Cli::Framework.create(f["name"], info) | ||
1681 | 955 | exec = framework.exec if framework && framework.exec | ||
1682 | 956 | else | ||
1683 | 957 | framework = detect_framework(prompt_ok) | ||
1684 | 958 | end | ||
1685 | 959 | |||
1686 | 960 | err "Application Type undetermined for path '#{@application}'" unless framework | ||
1687 | 961 | |||
1688 | 962 | if not runtime | ||
1689 | 963 | default_runtime = framework.default_runtime @application | ||
1690 | 964 | runtime = detect_runtime(default_runtime, !no_prompt) if framework.prompt_for_runtime? | ||
1691 | 965 | end | ||
1692 | 966 | command = ask("Start Command") if !command && framework.require_start_command? | ||
1693 | 967 | |||
1694 | 968 | default_url = "None" | ||
1695 | 969 | default_url = "#{appname}.#{VMC::Cli::Config.suggest_url}" if framework.require_url? | ||
1696 | 970 | |||
1697 | 971 | |||
1698 | 972 | unless no_prompt || url || !framework.require_url? | ||
1699 | 973 | url = ask( | ||
1700 | 974 | "Application Deployed URL", | ||
1701 | 975 | :default => default_url | ||
1702 | 976 | ) | ||
1703 | 977 | |||
1704 | 978 | # common error case is for prompted users to answer y or Y or yes or | ||
1705 | 979 | # YES to this ask() resulting in an unintended URL of y. Special case | ||
1706 | 980 | # this common error | ||
1707 | 981 | url = nil if YES_SET.member? url | ||
1708 | 982 | end | ||
1709 | 983 | url = nil if url == "None" | ||
1710 | 984 | default_url = nil if default_url == "None" | ||
1711 | 985 | url ||= default_url | ||
1712 | 986 | |||
1713 | 987 | if memswitch | ||
1714 | 988 | mem = memswitch | ||
1715 | 989 | elsif prompt_ok | ||
1716 | 990 | mem = ask("Memory Reservation", | ||
1717 | 991 | :default => framework.memory(runtime), | ||
1718 | 992 | :choices => mem_choices) | ||
1719 | 993 | else | ||
1720 | 994 | mem = framework.memory runtime | ||
1721 | 995 | end | ||
1722 | 996 | |||
1723 | 997 | # Set to MB number | ||
1724 | 998 | mem_quota = mem_choice_to_quota(mem) | ||
1725 | 999 | |||
1726 | 1000 | # check memsize here for capacity | ||
1727 | 1001 | check_has_capacity_for(mem_quota * instances) unless no_start | ||
1728 | 1002 | |||
1729 | 1003 | display 'Creating Application: ', false | ||
1730 | 1004 | |||
1731 | 1005 | manifest = { | ||
1732 | 1006 | :name => "#{appname}", | ||
1733 | 1007 | :staging => { | ||
1734 | 1008 | :framework => framework.name, | ||
1735 | 1009 | :runtime => runtime | ||
1736 | 1010 | }, | ||
1737 | 1011 | :uris => Array(url), | ||
1738 | 1012 | :instances => instances, | ||
1739 | 1013 | :resources => { | ||
1740 | 1014 | :memory => mem_quota | ||
1741 | 1015 | } | ||
1742 | 1016 | } | ||
1743 | 1017 | manifest[:staging][:command] = command if command | ||
1744 | 1018 | |||
1745 | 1019 | # Send the manifest to the cloud controller | ||
1746 | 1020 | client.create_app(appname, manifest) | ||
1747 | 1021 | display 'OK'.green | ||
1748 | 1022 | |||
1749 | 1023 | |||
1750 | 1024 | existing = Set.new(client.services.collect { |s| s[:name] }) | ||
1751 | 1025 | |||
1752 | 1026 | if @app_info && services = @app_info["services"] | ||
1753 | 1027 | services.each do |name, info| | ||
1754 | 1028 | unless existing.include? name | ||
1755 | 1029 | create_service_banner(info["type"], name, true) | ||
1756 | 1030 | end | ||
1757 | 1031 | |||
1758 | 1032 | bind_service_banner(name, appname) | ||
1759 | 1033 | end | ||
1760 | 1034 | end | ||
1761 | 1035 | |||
1762 | 1036 | # Stage and upload the app bits. | ||
1763 | 1037 | upload_app_bits(appname, @application) | ||
1764 | 1038 | |||
1765 | 1039 | start(appname, true) unless no_start | ||
1766 | 1040 | end | ||
1767 | 1041 | |||
1768 | 1042 | def do_stats(appname) | ||
1769 | 1043 | stats = client.app_stats(appname) | ||
1770 | 1044 | return display JSON.pretty_generate(stats) if @options[:json] | ||
1771 | 1045 | |||
1772 | 1046 | stats_table = table do |t| | ||
1773 | 1047 | t.headings = 'Instance', 'CPU (Cores)', 'Memory (limit)', 'Disk (limit)', 'Uptime' | ||
1774 | 1048 | stats.each do |entry| | ||
1775 | 1049 | index = entry[:instance] | ||
1776 | 1050 | stat = entry[:stats] | ||
1777 | 1051 | hp = "#{stat[:host]}:#{stat[:port]}" | ||
1778 | 1052 | uptime = uptime_string(stat[:uptime]) | ||
1779 | 1053 | usage = stat[:usage] | ||
1780 | 1054 | if usage | ||
1781 | 1055 | cpu = usage[:cpu] | ||
1782 | 1056 | mem = (usage[:mem] * 1024) # mem comes in K's | ||
1783 | 1057 | disk = usage[:disk] | ||
1784 | 1058 | end | ||
1785 | 1059 | mem_quota = stat[:mem_quota] | ||
1786 | 1060 | disk_quota = stat[:disk_quota] | ||
1787 | 1061 | mem = "#{pretty_size(mem)} (#{pretty_size(mem_quota, 0)})" | ||
1788 | 1062 | disk = "#{pretty_size(disk)} (#{pretty_size(disk_quota, 0)})" | ||
1789 | 1063 | cpu = cpu ? cpu.to_s : 'NA' | ||
1790 | 1064 | cpu = "#{cpu}% (#{stat[:cores]})" | ||
1791 | 1065 | t << [index, cpu, mem, disk, uptime] | ||
1792 | 1066 | end | ||
1793 | 1067 | end | ||
1794 | 1068 | |||
1795 | 1069 | if stats.empty? | ||
1796 | 1070 | display "No running instances for [#{appname}]".yellow | ||
1797 | 1071 | else | ||
1798 | 1072 | display stats_table | ||
1799 | 1073 | end | ||
1800 | 1074 | end | ||
1801 | 1075 | |||
1802 | 1076 | def all_files(appname, path) | ||
1803 | 1077 | instances_info_envelope = client.app_instances(appname) | ||
1804 | 1078 | return if instances_info_envelope.is_a?(Array) | ||
1805 | 1079 | instances_info = instances_info_envelope[:instances] || [] | ||
1806 | 1080 | instances_info.each do |entry| | ||
1807 | 1081 | begin | ||
1808 | 1082 | content = client.app_files(appname, path, entry[:index]) | ||
1809 | 1083 | display_logfile( | ||
1810 | 1084 | path, | ||
1811 | 1085 | content, | ||
1812 | 1086 | entry[:index], | ||
1813 | 1087 | "====> [#{entry[:index]}: #{path}] <====\n".bold | ||
1814 | 1088 | ) | ||
1815 | 1089 | rescue VMC::Client::NotFound, VMC::Client::TargetError | ||
1816 | 1090 | end | ||
1817 | 1091 | end | ||
1818 | 1092 | end | ||
1819 | 904 | end | 1093 | end |
1820 | 905 | 1094 | ||
1821 | 906 | class FileWithPercentOutput < ::File | 1095 | class FileWithPercentOutput < ::File |
1822 | 907 | 1096 | ||
1823 | === modified file 'lib/cli/commands/base.rb' | |||
1824 | --- lib/cli/commands/base.rb 2011-06-17 13:38:56 +0000 | |||
1825 | +++ lib/cli/commands/base.rb 2012-06-13 01:37:18 +0000 | |||
1826 | @@ -1,33 +1,189 @@ | |||
1827 | 1 | |||
1828 | 2 | require 'rubygems' | 1 | require 'rubygems' |
1829 | 2 | require 'interact' | ||
1830 | 3 | require 'terminal-table/import' | 3 | require 'terminal-table/import' |
1831 | 4 | require 'highline/import' | ||
1832 | 5 | 4 | ||
1833 | 6 | module VMC::Cli | 5 | module VMC::Cli |
1834 | 7 | 6 | ||
1835 | 8 | module Command | 7 | module Command |
1836 | 9 | 8 | ||
1837 | 10 | class Base | 9 | class Base |
1838 | 10 | include Interactive | ||
1839 | 11 | |||
1840 | 11 | attr_reader :no_prompt, :prompt_ok | 12 | attr_reader :no_prompt, :prompt_ok |
1841 | 12 | 13 | ||
1842 | 14 | MANIFEST = "manifest.yml" | ||
1843 | 15 | |||
1844 | 13 | def initialize(options={}) | 16 | def initialize(options={}) |
1845 | 14 | @options = options.dup | 17 | @options = options.dup |
1846 | 15 | @no_prompt = @options[:noprompts] | 18 | @no_prompt = @options[:noprompts] |
1847 | 16 | @prompt_ok = !no_prompt | 19 | @prompt_ok = !no_prompt |
1848 | 17 | 20 | ||
1849 | 18 | # Fix for system ruby and Highline (stdin) on MacOSX | ||
1850 | 19 | if RUBY_PLATFORM =~ /darwin/ && RUBY_VERSION == '1.8.7' && RUBY_PATCHLEVEL <= 174 | ||
1851 | 20 | HighLine.track_eof = false | ||
1852 | 21 | end | ||
1853 | 22 | |||
1854 | 23 | # Suppress colorize on Windows systems for now. | 21 | # Suppress colorize on Windows systems for now. |
1856 | 24 | if !!RUBY_PLATFORM['mingw'] || !!RUBY_PLATFORM['mswin32'] || !!RUBY_PLATFORM['cygwin'] | 22 | if WINDOWS |
1857 | 25 | VMC::Cli::Config.colorize = false | 23 | VMC::Cli::Config.colorize = false |
1858 | 26 | end | 24 | end |
1859 | 27 | 25 | ||
1863 | 28 | end | 26 | @path = @options[:path] || '.' |
1864 | 29 | 27 | ||
1865 | 30 | def client | 28 | load_manifest manifest_file if manifest_file |
1866 | 29 | end | ||
1867 | 30 | |||
1868 | 31 | def manifest_file | ||
1869 | 32 | return @options[:manifest] if @options[:manifest] | ||
1870 | 33 | return @manifest_file if @manifest_file | ||
1871 | 34 | |||
1872 | 35 | where = File.expand_path(@path) | ||
1873 | 36 | while true | ||
1874 | 37 | if File.exists?(File.join(where, MANIFEST)) | ||
1875 | 38 | @manifest_file = File.join(where, MANIFEST) | ||
1876 | 39 | break | ||
1877 | 40 | elsif File.basename(where) == "/" | ||
1878 | 41 | @manifest_file = nil | ||
1879 | 42 | break | ||
1880 | 43 | else | ||
1881 | 44 | where = File.expand_path("../", where) | ||
1882 | 45 | end | ||
1883 | 46 | end | ||
1884 | 47 | |||
1885 | 48 | @manifest_file | ||
1886 | 49 | end | ||
1887 | 50 | |||
1888 | 51 | def load_manifest_structure(file) | ||
1889 | 52 | manifest = YAML.load_file file | ||
1890 | 53 | |||
1891 | 54 | Array(manifest["inherit"]).each do |p| | ||
1892 | 55 | manifest = merge_parent(manifest, p) | ||
1893 | 56 | end | ||
1894 | 57 | |||
1895 | 58 | if apps = manifest["applications"] | ||
1896 | 59 | apps.each do |k, v| | ||
1897 | 60 | abs = File.expand_path(k, file) | ||
1898 | 61 | if Dir.pwd.start_with? abs | ||
1899 | 62 | manifest = merge_manifest(manifest, v) | ||
1900 | 63 | end | ||
1901 | 64 | end | ||
1902 | 65 | end | ||
1903 | 66 | |||
1904 | 67 | manifest | ||
1905 | 68 | end | ||
1906 | 69 | |||
1907 | 70 | def resolve_manifest(manifest) | ||
1908 | 71 | if apps = manifest["applications"] | ||
1909 | 72 | apps.each_value do |v| | ||
1910 | 73 | resolve_lexically(v, [manifest]) | ||
1911 | 74 | end | ||
1912 | 75 | end | ||
1913 | 76 | |||
1914 | 77 | resolve_lexically(manifest, [manifest]) | ||
1915 | 78 | end | ||
1916 | 79 | |||
1917 | 80 | def load_manifest(file) | ||
1918 | 81 | @manifest = load_manifest_structure(file) | ||
1919 | 82 | resolve_manifest(@manifest) | ||
1920 | 83 | end | ||
1921 | 84 | |||
1922 | 85 | def merge_parent(child, path) | ||
1923 | 86 | file = File.expand_path("../" + path, manifest_file) | ||
1924 | 87 | merge_manifest(child, load_manifest_structure(file)) | ||
1925 | 88 | end | ||
1926 | 89 | |||
1927 | 90 | def merge_manifest(child, parent) | ||
1928 | 91 | merge = proc do |_, old, new| | ||
1929 | 92 | if new.is_a?(Hash) and old.is_a?(Hash) | ||
1930 | 93 | old.merge(new, &merge) | ||
1931 | 94 | else | ||
1932 | 95 | new | ||
1933 | 96 | end | ||
1934 | 97 | end | ||
1935 | 98 | |||
1936 | 99 | parent.merge(child, &merge) | ||
1937 | 100 | end | ||
1938 | 101 | |||
1939 | 102 | def resolve_lexically(val, ctx = [@manifest]) | ||
1940 | 103 | case val | ||
1941 | 104 | when Hash | ||
1942 | 105 | val.each_value do |v| | ||
1943 | 106 | resolve_lexically(v, [val] + ctx) | ||
1944 | 107 | end | ||
1945 | 108 | when Array | ||
1946 | 109 | val.each do |v| | ||
1947 | 110 | resolve_lexically(v, ctx) | ||
1948 | 111 | end | ||
1949 | 112 | when String | ||
1950 | 113 | val.gsub!(/\$\{([[:alnum:]\-]+)\}/) do | ||
1951 | 114 | resolve_symbol($1, ctx) | ||
1952 | 115 | end | ||
1953 | 116 | end | ||
1954 | 117 | |||
1955 | 118 | nil | ||
1956 | 119 | end | ||
1957 | 120 | |||
1958 | 121 | def resolve_symbol(sym, ctx) | ||
1959 | 122 | case sym | ||
1960 | 123 | when "target-base" | ||
1961 | 124 | target_base(ctx) | ||
1962 | 125 | |||
1963 | 126 | when "target-url" | ||
1964 | 127 | target_url(ctx) | ||
1965 | 128 | |||
1966 | 129 | when "random-word" | ||
1967 | 130 | "%04x" % [rand(0x0100000)] | ||
1968 | 131 | |||
1969 | 132 | else | ||
1970 | 133 | found = find_symbol(sym, ctx) | ||
1971 | 134 | |||
1972 | 135 | if found | ||
1973 | 136 | resolve_lexically(found, ctx) | ||
1974 | 137 | found | ||
1975 | 138 | else | ||
1976 | 139 | err(sym, "Unknown symbol in manifest: ") | ||
1977 | 140 | end | ||
1978 | 141 | end | ||
1979 | 142 | end | ||
1980 | 143 | |||
1981 | 144 | def find_symbol(sym, ctx) | ||
1982 | 145 | ctx.each do |h| | ||
1983 | 146 | if val = resolve_in(h, sym) | ||
1984 | 147 | return val | ||
1985 | 148 | end | ||
1986 | 149 | end | ||
1987 | 150 | |||
1988 | 151 | nil | ||
1989 | 152 | end | ||
1990 | 153 | |||
1991 | 154 | def resolve_in(hash, *where) | ||
1992 | 155 | find_in_hash(hash, ["properties"] + where) || | ||
1993 | 156 | find_in_hash(hash, ["applications", @application] + where) || | ||
1994 | 157 | find_in_hash(hash, where) | ||
1995 | 158 | end | ||
1996 | 159 | |||
1997 | 160 | def manifest(*where) | ||
1998 | 161 | resolve_in(@manifest, *where) | ||
1999 | 162 | end | ||
2000 | 163 | |||
2001 | 164 | def find_in_hash(hash, where) | ||
2002 | 165 | what = hash | ||
2003 | 166 | where.each do |x| | ||
2004 | 167 | return nil unless what.is_a?(Hash) | ||
2005 | 168 | what = what[x] | ||
2006 | 169 | end | ||
2007 | 170 | |||
2008 | 171 | what | ||
2009 | 172 | end | ||
2010 | 173 | |||
2011 | 174 | def target_url(ctx = []) | ||
2012 | 175 | find_symbol("target", ctx) || | ||
2013 | 176 | (@client && @client.target) || | ||
2014 | 177 | VMC::Cli::Config.target_url | ||
2015 | 178 | end | ||
2016 | 179 | |||
2017 | 180 | def target_base(ctx = []) | ||
2018 | 181 | VMC::Cli::Config.base_of(find_symbol("target", ctx) || target_url) | ||
2019 | 182 | end | ||
2020 | 183 | |||
2021 | 184 | # Inject a client to help in testing. | ||
2022 | 185 | def client(cli=nil) | ||
2023 | 186 | @client ||= cli | ||
2024 | 31 | return @client if @client | 187 | return @client if @client |
2025 | 32 | @client = VMC::Client.new(target_url, auth_token) | 188 | @client = VMC::Client.new(target_url, auth_token) |
2026 | 33 | @client.trace = VMC::Cli::Config.trace if VMC::Cli::Config.trace | 189 | @client.trace = VMC::Cli::Config.trace if VMC::Cli::Config.trace |
2027 | @@ -36,18 +192,11 @@ | |||
2028 | 36 | end | 192 | end |
2029 | 37 | 193 | ||
2030 | 38 | def client_info | 194 | def client_info |
2038 | 39 | return @client_info if @client_info | 195 | @client_info ||= client.info |
2032 | 40 | @client_info = client.info | ||
2033 | 41 | end | ||
2034 | 42 | |||
2035 | 43 | def target_url | ||
2036 | 44 | return @target_url if @target_url | ||
2037 | 45 | @target_url = VMC::Cli::Config.target_url | ||
2039 | 46 | end | 196 | end |
2040 | 47 | 197 | ||
2041 | 48 | def auth_token | 198 | def auth_token |
2044 | 49 | return @auth_token if @auth_token | 199 | @auth_token = VMC::Cli::Config.auth_token(@options[:token_file]) |
2043 | 50 | @auth_token = VMC::Cli::Config.auth_token | ||
2045 | 51 | end | 200 | end |
2046 | 52 | 201 | ||
2047 | 53 | def runtimes_info | 202 | def runtimes_info |
2048 | @@ -72,7 +221,6 @@ | |||
2049 | 72 | end | 221 | end |
2050 | 73 | @frameworks | 222 | @frameworks |
2051 | 74 | end | 223 | end |
2052 | 75 | |||
2053 | 76 | end | 224 | end |
2054 | 77 | end | 225 | end |
2055 | 78 | end | 226 | end |
2056 | 79 | 227 | ||
2057 | === added file 'lib/cli/commands/manifest.rb' | |||
2058 | --- lib/cli/commands/manifest.rb 1970-01-01 00:00:00 +0000 | |||
2059 | +++ lib/cli/commands/manifest.rb 2012-06-13 01:37:18 +0000 | |||
2060 | @@ -0,0 +1,56 @@ | |||
2061 | 1 | module VMC::Cli::Command | ||
2062 | 2 | class Manifest < Base | ||
2063 | 3 | include VMC::Cli::ManifestHelper | ||
2064 | 4 | |||
2065 | 5 | def initialize(options) | ||
2066 | 6 | super | ||
2067 | 7 | |||
2068 | 8 | # don't resolve any of the manifest template stuff | ||
2069 | 9 | if manifest_file | ||
2070 | 10 | @manifest = load_manifest_structure manifest_file | ||
2071 | 11 | else | ||
2072 | 12 | @manifest = {} | ||
2073 | 13 | end | ||
2074 | 14 | end | ||
2075 | 15 | |||
2076 | 16 | def edit | ||
2077 | 17 | build_manifest | ||
2078 | 18 | save_manifest | ||
2079 | 19 | end | ||
2080 | 20 | |||
2081 | 21 | def extend(which) | ||
2082 | 22 | parent = load_manifest_structure which | ||
2083 | 23 | @manifest = load_manifest_structure which | ||
2084 | 24 | |||
2085 | 25 | build_manifest | ||
2086 | 26 | |||
2087 | 27 | simplify(@manifest, parent) | ||
2088 | 28 | |||
2089 | 29 | @manifest["inherit"] ||= [] | ||
2090 | 30 | @manifest["inherit"] << which | ||
2091 | 31 | |||
2092 | 32 | save_manifest(ask("Save where?")) | ||
2093 | 33 | end | ||
2094 | 34 | |||
2095 | 35 | private | ||
2096 | 36 | |||
2097 | 37 | def simplify(child, parent) | ||
2098 | 38 | return unless child.is_a?(Hash) and parent.is_a?(Hash) | ||
2099 | 39 | |||
2100 | 40 | child.reject! do |k, v| | ||
2101 | 41 | if v == parent[k] | ||
2102 | 42 | puts "rejecting #{k}" | ||
2103 | 43 | true | ||
2104 | 44 | else | ||
2105 | 45 | simplify(v, parent[k]) | ||
2106 | 46 | false | ||
2107 | 47 | end | ||
2108 | 48 | end | ||
2109 | 49 | end | ||
2110 | 50 | |||
2111 | 51 | def build_manifest | ||
2112 | 52 | @application = ask("Configure for which application?", :default => ".") | ||
2113 | 53 | interact true | ||
2114 | 54 | end | ||
2115 | 55 | end | ||
2116 | 56 | end | ||
2117 | 0 | 57 | ||
2118 | === added file 'lib/cli/commands/micro.rb' | |||
2119 | --- lib/cli/commands/micro.rb 1970-01-01 00:00:00 +0000 | |||
2120 | +++ lib/cli/commands/micro.rb 2012-06-13 01:37:18 +0000 | |||
2121 | @@ -0,0 +1,115 @@ | |||
2122 | 1 | module VMC::Cli::Command | ||
2123 | 2 | class Micro < Base | ||
2124 | 3 | |||
2125 | 4 | def initialize(args) | ||
2126 | 5 | super(args) | ||
2127 | 6 | end | ||
2128 | 7 | |||
2129 | 8 | def offline(mode) | ||
2130 | 9 | command('offline') | ||
2131 | 10 | end | ||
2132 | 11 | |||
2133 | 12 | def online(mode) | ||
2134 | 13 | command('online') | ||
2135 | 14 | end | ||
2136 | 15 | |||
2137 | 16 | def status(mode) | ||
2138 | 17 | command('status') | ||
2139 | 18 | end | ||
2140 | 19 | |||
2141 | 20 | def command(cmd) | ||
2142 | 21 | config = build_config | ||
2143 | 22 | switcher(config).send(cmd) | ||
2144 | 23 | store_config(config) | ||
2145 | 24 | end | ||
2146 | 25 | |||
2147 | 26 | def switcher(config) | ||
2148 | 27 | case Micro.platform | ||
2149 | 28 | when :darwin | ||
2150 | 29 | switcher = VMC::Micro::Switcher::Darwin.new(config) | ||
2151 | 30 | when :linux | ||
2152 | 31 | switcher = VMC::Micro::Switcher::Linux.new(config) | ||
2153 | 32 | when :windows | ||
2154 | 33 | switcher = VMC::Micro::Switcher::Windows.new(config) | ||
2155 | 34 | when :dummy # for testing only | ||
2156 | 35 | switcher = VMC::Micro::Switcher::Dummy.new(config) | ||
2157 | 36 | else | ||
2158 | 37 | err "unsupported platform: #{Micro.platform}" | ||
2159 | 38 | end | ||
2160 | 39 | end | ||
2161 | 40 | |||
2162 | 41 | # Returns the configuration needed to run the micro related subcommands. | ||
2163 | 42 | # First loads saved config from file (if there is any), then overrides | ||
2164 | 43 | # loaded values with command line arguments, and finally tries to guess | ||
2165 | 44 | # in case neither was used: | ||
2166 | 45 | # vmx location of micro.vmx file | ||
2167 | 46 | # vmrun location of vmrun command | ||
2168 | 47 | # password password for vcap user (in the guest vm) | ||
2169 | 48 | # platform current platform | ||
2170 | 49 | def build_config | ||
2171 | 50 | conf = VMC::Cli::Config.micro # returns {} if there isn't a saved config | ||
2172 | 51 | |||
2173 | 52 | override(conf, 'vmx', true) do | ||
2174 | 53 | locate_vmx(Micro.platform) | ||
2175 | 54 | end | ||
2176 | 55 | |||
2177 | 56 | override(conf, 'vmrun', true) do | ||
2178 | 57 | VMC::Micro::VMrun.locate(Micro.platform) | ||
2179 | 58 | end | ||
2180 | 59 | |||
2181 | 60 | override(conf, 'password') do | ||
2182 | 61 | @password = ask("Please enter your Micro Cloud Foundry VM password (vcap user) password", :echo => "*") | ||
2183 | 62 | end | ||
2184 | 63 | |||
2185 | 64 | conf['platform'] = Micro.platform | ||
2186 | 65 | |||
2187 | 66 | conf | ||
2188 | 67 | end | ||
2189 | 68 | |||
2190 | 69 | # Save the cleartext password if --save is supplied. | ||
2191 | 70 | # Note: it is due to vix we have to use a cleartext password :( | ||
2192 | 71 | # Only if --password is used and not --save is the password deleted from the | ||
2193 | 72 | # config file before it is stored to disk. | ||
2194 | 73 | def store_config(config) | ||
2195 | 74 | if @options[:save] | ||
2196 | 75 | warn("cleartext password saved in: #{VMC::Cli::Config::MICRO_FILE}") | ||
2197 | 76 | elsif @options[:password] || @password | ||
2198 | 77 | config.delete('password') | ||
2199 | 78 | end | ||
2200 | 79 | |||
2201 | 80 | VMC::Cli::Config.store_micro(config) | ||
2202 | 81 | end | ||
2203 | 82 | |||
2204 | 83 | # override with command line arguments and yield the block in case the option isn't set | ||
2205 | 84 | def override(config, option, escape=false, &blk) | ||
2206 | 85 | # override if given on the command line | ||
2207 | 86 | if opt = @options[option.to_sym] | ||
2208 | 87 | opt = VMC::Micro.escape_path(opt) if escape | ||
2209 | 88 | config[option] = opt | ||
2210 | 89 | end | ||
2211 | 90 | config[option] = yield unless config[option] | ||
2212 | 91 | end | ||
2213 | 92 | |||
2214 | 93 | def locate_vmx(platform) | ||
2215 | 94 | paths = YAML.load_file(VMC::Micro.config_file('paths.yml')) | ||
2216 | 95 | vmx_paths = paths[platform.to_s]['vmx'] | ||
2217 | 96 | vmx = VMC::Micro.locate_file('micro.vmx', 'micro', vmx_paths) | ||
2218 | 97 | err "Unable to locate micro.vmx, please supply --vmx option" unless vmx | ||
2219 | 98 | vmx | ||
2220 | 99 | end | ||
2221 | 100 | |||
2222 | 101 | def self.platform | ||
2223 | 102 | case RUBY_PLATFORM | ||
2224 | 103 | when /darwin/ # x86_64-darwin11.2.0 | ||
2225 | 104 | :darwin | ||
2226 | 105 | when /linux/ # x86_64-linux | ||
2227 | 106 | :linux | ||
2228 | 107 | when /mingw|mswin32|cygwin/ # i386-mingw32 | ||
2229 | 108 | :windows | ||
2230 | 109 | else | ||
2231 | 110 | RUBY_PLATFORM | ||
2232 | 111 | end | ||
2233 | 112 | end | ||
2234 | 113 | |||
2235 | 114 | end | ||
2236 | 115 | end | ||
2237 | 0 | 116 | ||
2238 | === modified file 'lib/cli/commands/misc.rb' | |||
2239 | --- lib/cli/commands/misc.rb 2011-06-17 13:38:56 +0000 | |||
2240 | +++ lib/cli/commands/misc.rb 2012-06-13 01:37:18 +0000 | |||
2241 | @@ -30,14 +30,15 @@ | |||
2242 | 30 | client = VMC::Client.new(target_url) | 30 | client = VMC::Client.new(target_url) |
2243 | 31 | unless client.target_valid? | 31 | unless client.target_valid? |
2244 | 32 | if prompt_ok | 32 | if prompt_ok |
2248 | 33 | display "Host is not valid: '#{target_url}'".red | 33 | display "Host is not available or is not valid: '#{target_url}'".red |
2249 | 34 | show_response = ask "Would you like see the response [yN]? " | 34 | show_response = ask "Would you like see the response?", |
2250 | 35 | display "\n<<<\n#{client.raw_info}\n>>>\n" if show_response.upcase == 'Y' | 35 | :default => false |
2251 | 36 | display "\n<<<\n#{client.raw_info}\n>>>\n" if show_response | ||
2252 | 36 | end | 37 | end |
2253 | 37 | exit(false) | 38 | exit(false) |
2254 | 38 | else | 39 | else |
2255 | 39 | VMC::Cli::Config.store_target(target_url) | 40 | VMC::Cli::Config.store_target(target_url) |
2257 | 40 | say "Succesfully targeted to [#{target_url}]".green | 41 | say "Successfully targeted to [#{target_url}]".green |
2258 | 41 | end | 42 | end |
2259 | 42 | end | 43 | end |
2260 | 43 | 44 | ||
2261 | 44 | 45 | ||
2262 | === modified file 'lib/cli/commands/services.rb' | |||
2263 | --- lib/cli/commands/services.rb 2011-06-17 13:38:56 +0000 | |||
2264 | +++ lib/cli/commands/services.rb 2012-06-13 01:37:18 +0000 | |||
2265 | @@ -1,11 +1,16 @@ | |||
2266 | 1 | require "uuidtools" | ||
2267 | 2 | |||
2268 | 1 | module VMC::Cli::Command | 3 | module VMC::Cli::Command |
2269 | 2 | 4 | ||
2270 | 3 | class Services < Base | 5 | class Services < Base |
2271 | 4 | include VMC::Cli::ServicesHelper | 6 | include VMC::Cli::ServicesHelper |
2272 | 7 | include VMC::Cli::TunnelHelper | ||
2273 | 5 | 8 | ||
2274 | 6 | def services | 9 | def services |
2275 | 7 | ss = client.services_info | 10 | ss = client.services_info |
2276 | 8 | ps = client.services | 11 | ps = client.services |
2277 | 12 | ps.sort! {|a, b| a[:name] <=> b[:name] } | ||
2278 | 13 | |||
2279 | 9 | if @options[:json] | 14 | if @options[:json] |
2280 | 10 | services = { :system => ss, :provisioned => ps } | 15 | services = { :system => ss, :provisioned => ps } |
2281 | 11 | return display JSON.pretty_generate(services) | 16 | return display JSON.pretty_generate(services) |
2282 | @@ -18,15 +23,15 @@ | |||
2283 | 18 | unless no_prompt || service | 23 | unless no_prompt || service |
2284 | 19 | services = client.services_info | 24 | services = client.services_info |
2285 | 20 | err 'No services available to provision' if services.empty? | 25 | err 'No services available to provision' if services.empty? |
2295 | 21 | choose do |menu| | 26 | service = ask( |
2296 | 22 | menu.prompt = 'Please select one you wish to provision: ' | 27 | "Which service would you like to provision?", |
2297 | 23 | menu.select_by = :index_or_name | 28 | { :indexed => true, |
2298 | 24 | services.each do |service_type, value| | 29 | :choices => |
2299 | 25 | value.each do |vendor, version| | 30 | services.values.collect { |type| |
2300 | 26 | menu.choice(vendor.to_s) { service = vendor.to_s } | 31 | type.keys.collect(&:to_s) |
2301 | 27 | end | 32 | }.flatten |
2302 | 28 | end | 33 | } |
2303 | 29 | end | 34 | ) |
2304 | 30 | end | 35 | end |
2305 | 31 | name = @options[:name] unless name | 36 | name = @options[:name] unless name |
2306 | 32 | unless name | 37 | unless name |
2307 | @@ -42,13 +47,12 @@ | |||
2308 | 42 | unless no_prompt || service | 47 | unless no_prompt || service |
2309 | 43 | user_services = client.services | 48 | user_services = client.services |
2310 | 44 | err 'No services available to delete' if user_services.empty? | 49 | err 'No services available to delete' if user_services.empty? |
2318 | 45 | choose do |menu| | 50 | service = ask( |
2319 | 46 | menu.prompt = 'Please select one you wish to delete: ' | 51 | "Which service would you like to delete?", |
2320 | 47 | menu.select_by = :index_or_name | 52 | { :indexed => true, |
2321 | 48 | user_services.each do |s| | 53 | :choices => user_services.collect { |s| s[:name] } |
2322 | 49 | menu.choice(s[:name]) { service = s[:name] } | 54 | } |
2323 | 50 | end | 55 | ) |
2317 | 51 | end | ||
2324 | 52 | end | 56 | end |
2325 | 53 | err "Service name required." unless service | 57 | err "Service name required." unless service |
2326 | 54 | display "Deleting service [#{service}]: ", false | 58 | display "Deleting service [#{service}]: ", false |
2327 | @@ -80,5 +84,97 @@ | |||
2328 | 80 | check_app_for_restart(dest_app) | 84 | check_app_for_restart(dest_app) |
2329 | 81 | end | 85 | end |
2330 | 82 | 86 | ||
2331 | 87 | def tunnel(service=nil, client_name=nil) | ||
2332 | 88 | unless defined? Caldecott | ||
2333 | 89 | display "To use `vmc tunnel', you must first install Caldecott:" | ||
2334 | 90 | display "" | ||
2335 | 91 | display "\tgem install caldecott" | ||
2336 | 92 | display "" | ||
2337 | 93 | display "Note that you'll need a C compiler. If you're on OS X, Xcode" | ||
2338 | 94 | display "will provide one. If you're on Windows, try DevKit." | ||
2339 | 95 | display "" | ||
2340 | 96 | display "This manual step will be removed in the future." | ||
2341 | 97 | display "" | ||
2342 | 98 | err "Caldecott is not installed." | ||
2343 | 99 | end | ||
2344 | 100 | |||
2345 | 101 | ps = client.services | ||
2346 | 102 | err "No services available to tunnel to" if ps.empty? | ||
2347 | 103 | |||
2348 | 104 | unless service | ||
2349 | 105 | choices = ps.collect { |s| s[:name] }.sort | ||
2350 | 106 | service = ask( | ||
2351 | 107 | "Which service to tunnel to?", | ||
2352 | 108 | :choices => choices, | ||
2353 | 109 | :indexed => true | ||
2354 | 110 | ) | ||
2355 | 111 | end | ||
2356 | 112 | |||
2357 | 113 | info = ps.select { |s| s[:name] == service }.first | ||
2358 | 114 | |||
2359 | 115 | err "Unknown service '#{service}'" unless info | ||
2360 | 116 | |||
2361 | 117 | port = pick_tunnel_port(@options[:port] || 10000) | ||
2362 | 118 | |||
2363 | 119 | raise VMC::Client::AuthError unless client.logged_in? | ||
2364 | 120 | |||
2365 | 121 | if not tunnel_pushed? | ||
2366 | 122 | display "Deploying tunnel application '#{tunnel_appname}'." | ||
2367 | 123 | auth = UUIDTools::UUID.random_create.to_s | ||
2368 | 124 | push_caldecott(auth) | ||
2369 | 125 | bind_service_banner(service, tunnel_appname, false) | ||
2370 | 126 | start_caldecott | ||
2371 | 127 | else | ||
2372 | 128 | auth = tunnel_auth | ||
2373 | 129 | end | ||
2374 | 130 | |||
2375 | 131 | if not tunnel_healthy?(auth) | ||
2376 | 132 | display "Redeploying tunnel application '#{tunnel_appname}'." | ||
2377 | 133 | |||
2378 | 134 | # We don't expect caldecott not to be running, so take the | ||
2379 | 135 | # most aggressive restart method.. delete/re-push | ||
2380 | 136 | client.delete_app(tunnel_appname) | ||
2381 | 137 | invalidate_tunnel_app_info | ||
2382 | 138 | |||
2383 | 139 | push_caldecott(auth) | ||
2384 | 140 | bind_service_banner(service, tunnel_appname, false) | ||
2385 | 141 | start_caldecott | ||
2386 | 142 | end | ||
2387 | 143 | |||
2388 | 144 | if not tunnel_bound?(service) | ||
2389 | 145 | bind_service_banner(service, tunnel_appname) | ||
2390 | 146 | end | ||
2391 | 147 | |||
2392 | 148 | conn_info = tunnel_connection_info info[:vendor], service, auth | ||
2393 | 149 | display_tunnel_connection_info(conn_info) | ||
2394 | 150 | display "Starting tunnel to #{service.bold} on port #{port.to_s.bold}." | ||
2395 | 151 | start_tunnel(port, conn_info, auth) | ||
2396 | 152 | |||
2397 | 153 | clients = get_clients_for(info[:vendor]) | ||
2398 | 154 | |||
2399 | 155 | if clients.empty? | ||
2400 | 156 | client_name ||= "none" | ||
2401 | 157 | else | ||
2402 | 158 | client_name ||= ask( | ||
2403 | 159 | "Which client would you like to start?", | ||
2404 | 160 | :choices => ["none"] + clients.keys, | ||
2405 | 161 | :indexed => true | ||
2406 | 162 | ) | ||
2407 | 163 | end | ||
2408 | 164 | |||
2409 | 165 | if client_name == "none" | ||
2410 | 166 | wait_for_tunnel_end | ||
2411 | 167 | else | ||
2412 | 168 | wait_for_tunnel_start(port) | ||
2413 | 169 | unless start_local_prog(clients, client_name, conn_info, port) | ||
2414 | 170 | err "'#{client_name}' execution failed; is it in your $PATH?" | ||
2415 | 171 | end | ||
2416 | 172 | end | ||
2417 | 173 | end | ||
2418 | 174 | |||
2419 | 175 | def get_clients_for(type) | ||
2420 | 176 | conf = VMC::Cli::Config.clients | ||
2421 | 177 | conf[type] || {} | ||
2422 | 178 | end | ||
2423 | 83 | end | 179 | end |
2424 | 84 | end | 180 | end |
2425 | 85 | 181 | ||
2426 | === modified file 'lib/cli/commands/user.rb' | |||
2427 | --- lib/cli/commands/user.rb 2011-06-17 13:38:56 +0000 | |||
2428 | +++ lib/cli/commands/user.rb 2012-06-13 01:37:18 +0000 | |||
2429 | @@ -12,19 +12,24 @@ | |||
2430 | 12 | def login(email=nil) | 12 | def login(email=nil) |
2431 | 13 | email = @options[:email] unless email | 13 | email = @options[:email] unless email |
2432 | 14 | password = @options[:password] | 14 | password = @options[:password] |
2436 | 15 | tries = 0 | 15 | tries ||= 0 |
2437 | 16 | email = ask("Email: ") unless no_prompt || email | 16 | |
2438 | 17 | password = ask("Password: ") {|q| q.echo = '*'} unless no_prompt || password | 17 | unless no_prompt |
2439 | 18 | display "Attempting login to [#{target_url}]" if target_url | ||
2440 | 19 | email ||= ask("Email") | ||
2441 | 20 | password ||= ask("Password", :echo => "*") | ||
2442 | 21 | end | ||
2443 | 22 | |||
2444 | 18 | err "Need a valid email" unless email | 23 | err "Need a valid email" unless email |
2445 | 19 | err "Need a password" unless password | 24 | err "Need a password" unless password |
2446 | 20 | login_and_save_token(email, password) | 25 | login_and_save_token(email, password) |
2447 | 21 | say "Successfully logged into [#{target_url}]".green | 26 | say "Successfully logged into [#{target_url}]".green |
2448 | 22 | rescue VMC::Client::TargetError | 27 | rescue VMC::Client::TargetError |
2450 | 23 | display "Problem with login, invalid account or password.".red | 28 | display "Problem with login, invalid account or password when attempting to login to '#{target_url}'".red |
2451 | 24 | retry if (tries += 1) < 3 && prompt_ok && !@options[:password] | 29 | retry if (tries += 1) < 3 && prompt_ok && !@options[:password] |
2452 | 25 | exit 1 | 30 | exit 1 |
2453 | 26 | rescue => e | 31 | rescue => e |
2455 | 27 | display "Problem with login, #{e}, try again or register for an account.".red | 32 | display "Problem with login to '#{target_url}', #{e}, try again or register for an account.".red |
2456 | 28 | exit 1 | 33 | exit 1 |
2457 | 29 | end | 34 | end |
2458 | 30 | 35 | ||
2459 | @@ -39,8 +44,8 @@ | |||
2460 | 39 | err "Need to be logged in to change password." unless email | 44 | err "Need to be logged in to change password." unless email |
2461 | 40 | say "Changing password for '#{email}'\n" | 45 | say "Changing password for '#{email}'\n" |
2462 | 41 | unless no_prompt | 46 | unless no_prompt |
2465 | 42 | password = ask("New Password: ") {|q| q.echo = '*'} | 47 | password = ask "New Password", :echo => "*" |
2466 | 43 | password2 = ask("Verify Password: ") {|q| q.echo = '*'} | 48 | password2 = ask "Verify Password", :echo => "*" |
2467 | 44 | err "Passwords did not match, try again" if password != password2 | 49 | err "Passwords did not match, try again" if password != password2 |
2468 | 45 | end | 50 | end |
2469 | 46 | err "Password required" unless password | 51 | err "Password required" unless password |
2470 | @@ -52,7 +57,7 @@ | |||
2471 | 52 | 57 | ||
2472 | 53 | def login_and_save_token(email, password) | 58 | def login_and_save_token(email, password) |
2473 | 54 | token = client.login(email, password) | 59 | token = client.login(email, password) |
2475 | 55 | VMC::Cli::Config.store_token(token) | 60 | VMC::Cli::Config.store_token(token, @options[:token_file]) |
2476 | 56 | end | 61 | end |
2477 | 57 | 62 | ||
2478 | 58 | end | 63 | end |
2479 | 59 | 64 | ||
2480 | === modified file 'lib/cli/config.rb' | |||
2481 | --- lib/cli/config.rb 2011-06-17 13:38:56 +0000 | |||
2482 | +++ lib/cli/config.rb 2012-06-13 01:37:18 +0000 | |||
2483 | @@ -8,56 +8,60 @@ | |||
2484 | 8 | class Config | 8 | class Config |
2485 | 9 | 9 | ||
2486 | 10 | DEFAULT_TARGET = 'api.vcap.me' | 10 | DEFAULT_TARGET = 'api.vcap.me' |
2487 | 11 | DEFAULT_SUGGEST = 'vcap.me' | ||
2488 | 12 | 11 | ||
2489 | 13 | TARGET_FILE = '~/.vmc_target' | 12 | TARGET_FILE = '~/.vmc_target' |
2490 | 14 | TOKEN_FILE = '~/.vmc_token' | 13 | TOKEN_FILE = '~/.vmc_token' |
2491 | 15 | INSTANCES_FILE = '~/.vmc_instances' | 14 | INSTANCES_FILE = '~/.vmc_instances' |
2492 | 16 | ALIASES_FILE = '~/.vmc_aliases' | 15 | ALIASES_FILE = '~/.vmc_aliases' |
2493 | 16 | CLIENTS_FILE = '~/.vmc_clients' | ||
2494 | 17 | MICRO_FILE = '~/.vmc_micro' | ||
2495 | 18 | |||
2496 | 19 | STOCK_CLIENTS = File.expand_path("../../../config/clients.yml", __FILE__) | ||
2497 | 17 | 20 | ||
2498 | 18 | class << self | 21 | class << self |
2499 | 19 | attr_accessor :colorize | 22 | attr_accessor :colorize |
2500 | 20 | attr_accessor :output | 23 | attr_accessor :output |
2501 | 21 | attr_accessor :trace | 24 | attr_accessor :trace |
2502 | 22 | attr_accessor :nozip | 25 | attr_accessor :nozip |
2503 | 23 | attr_reader :suggest_url | ||
2504 | 24 | 26 | ||
2505 | 25 | def target_url | 27 | def target_url |
2506 | 26 | return @target_url if @target_url | 28 | return @target_url if @target_url |
2507 | 27 | target_file = File.expand_path(TARGET_FILE) | 29 | target_file = File.expand_path(TARGET_FILE) |
2508 | 28 | if File.exists? target_file | 30 | if File.exists? target_file |
2514 | 29 | @target_url = File.read(target_file).strip! | 31 | @target_url = lock_and_read(target_file).strip |
2510 | 30 | ha = @target_url.split('.') | ||
2511 | 31 | ha.shift | ||
2512 | 32 | @suggest_url = ha.join('.') | ||
2513 | 33 | @suggest_url = DEFAULT_SUGGEST if @suggest_url.empty? | ||
2515 | 34 | else | 32 | else |
2516 | 35 | @target_url = DEFAULT_TARGET | 33 | @target_url = DEFAULT_TARGET |
2517 | 36 | @suggest_url = DEFAULT_SUGGEST | ||
2518 | 37 | end | 34 | end |
2519 | 38 | @target_url = "http://#{@target_url}" unless /^https?/ =~ @target_url | 35 | @target_url = "http://#{@target_url}" unless /^https?/ =~ @target_url |
2520 | 39 | @target_url = @target_url.gsub(/\/+$/, '') | 36 | @target_url = @target_url.gsub(/\/+$/, '') |
2521 | 40 | @target_url | 37 | @target_url |
2522 | 41 | end | 38 | end |
2523 | 42 | 39 | ||
2524 | 40 | def base_of(url) | ||
2525 | 41 | url.sub(/^[^\.]+\./, "") | ||
2526 | 42 | end | ||
2527 | 43 | |||
2528 | 44 | def suggest_url | ||
2529 | 45 | @suggest_url ||= base_of(target_url) | ||
2530 | 46 | end | ||
2531 | 47 | |||
2532 | 43 | def store_target(target_host) | 48 | def store_target(target_host) |
2533 | 44 | target_file = File.expand_path(TARGET_FILE) | 49 | target_file = File.expand_path(TARGET_FILE) |
2536 | 45 | File.open(target_file, 'w+') { |f| f.puts target_host } | 50 | lock_and_write(target_file, target_host) |
2535 | 46 | FileUtils.chmod 0600, target_file | ||
2537 | 47 | end | 51 | end |
2538 | 48 | 52 | ||
2541 | 49 | def all_tokens | 53 | def all_tokens(token_file_path=nil) |
2542 | 50 | token_file = File.expand_path(TOKEN_FILE) | 54 | token_file = File.expand_path(token_file_path || TOKEN_FILE) |
2543 | 51 | return nil unless File.exists? token_file | 55 | return nil unless File.exists? token_file |
2545 | 52 | contents = File.read(token_file).strip | 56 | contents = lock_and_read(token_file).strip |
2546 | 53 | JSON.parse(contents) | 57 | JSON.parse(contents) |
2547 | 54 | end | 58 | end |
2548 | 55 | 59 | ||
2549 | 56 | alias :targets :all_tokens | 60 | alias :targets :all_tokens |
2550 | 57 | 61 | ||
2552 | 58 | def auth_token | 62 | def auth_token(token_file_path=nil) |
2553 | 59 | return @token if @token | 63 | return @token if @token |
2555 | 60 | tokens = all_tokens | 64 | tokens = all_tokens(token_file_path) |
2556 | 61 | @token = tokens[target_url] if tokens | 65 | @token = tokens[target_url] if tokens |
2557 | 62 | end | 66 | end |
2558 | 63 | 67 | ||
2559 | @@ -65,24 +69,23 @@ | |||
2560 | 65 | FileUtils.rm_f(File.expand_path(TOKEN_FILE)) | 69 | FileUtils.rm_f(File.expand_path(TOKEN_FILE)) |
2561 | 66 | end | 70 | end |
2562 | 67 | 71 | ||
2565 | 68 | def store_token(token) | 72 | def store_token(token, token_file_path=nil) |
2566 | 69 | tokens = all_tokens || {} | 73 | tokens = all_tokens(token_file_path) || {} |
2567 | 70 | tokens[target_url] = token | 74 | tokens[target_url] = token |
2571 | 71 | token_file = File.expand_path(TOKEN_FILE) | 75 | token_file = File.expand_path(token_file_path || TOKEN_FILE) |
2572 | 72 | File.open(token_file, 'w+') { |f| f.write(tokens.to_json) } | 76 | lock_and_write(token_file, tokens.to_json) |
2570 | 73 | FileUtils.chmod 0600, token_file | ||
2573 | 74 | end | 77 | end |
2574 | 75 | 78 | ||
2575 | 76 | def instances | 79 | def instances |
2576 | 77 | instances_file = File.expand_path(INSTANCES_FILE) | 80 | instances_file = File.expand_path(INSTANCES_FILE) |
2577 | 78 | return nil unless File.exists? instances_file | 81 | return nil unless File.exists? instances_file |
2579 | 79 | contents = File.read(instances_file).strip | 82 | contents = lock_and_read(instances_file).strip |
2580 | 80 | JSON.parse(contents) | 83 | JSON.parse(contents) |
2581 | 81 | end | 84 | end |
2582 | 82 | 85 | ||
2583 | 83 | def store_instances(instances) | 86 | def store_instances(instances) |
2584 | 84 | instances_file = File.expand_path(INSTANCES_FILE) | 87 | instances_file = File.expand_path(INSTANCES_FILE) |
2586 | 85 | File.open(instances_file, 'w') { |f| f.write(instances.to_json) } | 88 | lock_and_write(instances_file, instances.to_json) |
2587 | 86 | end | 89 | end |
2588 | 87 | 90 | ||
2589 | 88 | def aliases | 91 | def aliases |
2590 | @@ -100,6 +103,66 @@ | |||
2591 | 100 | File.open(aliases_file, 'wb') {|f| f.write(aliases.to_yaml)} | 103 | File.open(aliases_file, 'wb') {|f| f.write(aliases.to_yaml)} |
2592 | 101 | end | 104 | end |
2593 | 102 | 105 | ||
2594 | 106 | def micro | ||
2595 | 107 | micro_file = File.expand_path(MICRO_FILE) | ||
2596 | 108 | return {} unless File.exists? micro_file | ||
2597 | 109 | contents = lock_and_read(micro_file).strip | ||
2598 | 110 | JSON.parse(contents) | ||
2599 | 111 | end | ||
2600 | 112 | |||
2601 | 113 | def store_micro(micro) | ||
2602 | 114 | micro_file = File.expand_path(MICRO_FILE) | ||
2603 | 115 | lock_and_write(micro_file, micro.to_json) | ||
2604 | 116 | end | ||
2605 | 117 | |||
2606 | 118 | def deep_merge(a, b) | ||
2607 | 119 | merge = proc do |_, old, new| | ||
2608 | 120 | if new.is_a?(Hash) and old.is_a?(Hash) | ||
2609 | 121 | old.merge(new, &merge) | ||
2610 | 122 | else | ||
2611 | 123 | new | ||
2612 | 124 | end | ||
2613 | 125 | end | ||
2614 | 126 | |||
2615 | 127 | a.merge(b, &merge) | ||
2616 | 128 | end | ||
2617 | 129 | |||
2618 | 130 | def clients | ||
2619 | 131 | return @clients if @clients | ||
2620 | 132 | |||
2621 | 133 | stock = YAML.load_file(STOCK_CLIENTS) | ||
2622 | 134 | clients = File.expand_path CLIENTS_FILE | ||
2623 | 135 | if File.exists? clients | ||
2624 | 136 | user = YAML.load_file(clients) | ||
2625 | 137 | @clients = deep_merge(stock, user) | ||
2626 | 138 | else | ||
2627 | 139 | @clients = stock | ||
2628 | 140 | end | ||
2629 | 141 | end | ||
2630 | 142 | |||
2631 | 143 | def lock_and_read(file) | ||
2632 | 144 | File.open(file, File::RDONLY) {|f| | ||
2633 | 145 | if defined? JRUBY_VERSION | ||
2634 | 146 | f.flock(File::LOCK_SH) | ||
2635 | 147 | else | ||
2636 | 148 | f.flock(File::LOCK_EX) | ||
2637 | 149 | end | ||
2638 | 150 | contents = f.read | ||
2639 | 151 | f.flock(File::LOCK_UN) | ||
2640 | 152 | contents | ||
2641 | 153 | } | ||
2642 | 154 | end | ||
2643 | 155 | |||
2644 | 156 | def lock_and_write(file, contents) | ||
2645 | 157 | File.open(file, File::RDWR | File::CREAT, 0600) {|f| | ||
2646 | 158 | f.flock(File::LOCK_EX) | ||
2647 | 159 | f.rewind | ||
2648 | 160 | f.puts contents | ||
2649 | 161 | f.flush | ||
2650 | 162 | f.truncate(f.pos) | ||
2651 | 163 | f.flock(File::LOCK_UN) | ||
2652 | 164 | } | ||
2653 | 165 | end | ||
2654 | 103 | end | 166 | end |
2655 | 104 | 167 | ||
2656 | 105 | def initialize(work_dir = Dir.pwd) | 168 | def initialize(work_dir = Dir.pwd) |
2657 | 106 | 169 | ||
2658 | === added file 'lib/cli/console_helper.rb' | |||
2659 | --- lib/cli/console_helper.rb 1970-01-01 00:00:00 +0000 | |||
2660 | +++ lib/cli/console_helper.rb 2012-06-13 01:37:18 +0000 | |||
2661 | @@ -0,0 +1,160 @@ | |||
2662 | 1 | require 'net/telnet' | ||
2663 | 2 | require 'readline' | ||
2664 | 3 | |||
2665 | 4 | module VMC::Cli | ||
2666 | 5 | module ConsoleHelper | ||
2667 | 6 | |||
2668 | 7 | def console_connection_info(appname) | ||
2669 | 8 | app = client.app_info(appname) | ||
2670 | 9 | fw = VMC::Cli::Framework.lookup_by_framework(app[:staging][:model]) | ||
2671 | 10 | if !fw.console | ||
2672 | 11 | err "'#{appname}' is a #{fw.name} application. " + | ||
2673 | 12 | "Console access is not supported for #{fw.name} applications." | ||
2674 | 13 | end | ||
2675 | 14 | instances_info_envelope = client.app_instances(appname) | ||
2676 | 15 | instances_info_envelope = {} if instances_info_envelope.is_a?(Array) | ||
2677 | 16 | |||
2678 | 17 | instances_info = instances_info_envelope[:instances] || [] | ||
2679 | 18 | err "No running instances for [#{appname}]" if instances_info.empty? | ||
2680 | 19 | |||
2681 | 20 | entry = instances_info[0] | ||
2682 | 21 | if !entry[:console_port] | ||
2683 | 22 | begin | ||
2684 | 23 | client.app_files(appname, '/app/cf-rails-console') | ||
2685 | 24 | err "Console port not provided for [#{appname}]. Try restarting the app." | ||
2686 | 25 | rescue VMC::Client::TargetError, VMC::Client::NotFound | ||
2687 | 26 | err "Console access not supported for [#{appname}]. " + | ||
2688 | 27 | "Please redeploy your app to enable support." | ||
2689 | 28 | end | ||
2690 | 29 | end | ||
2691 | 30 | conn_info = {'hostname' => entry[:console_ip], 'port' => entry[:console_port]} | ||
2692 | 31 | end | ||
2693 | 32 | |||
2694 | 33 | def start_local_console(port, appname) | ||
2695 | 34 | auth_info = console_credentials(appname) | ||
2696 | 35 | display "Connecting to '#{appname}' console: ", false | ||
2697 | 36 | prompt = console_login(auth_info, port) | ||
2698 | 37 | display "OK".green | ||
2699 | 38 | display "\n" | ||
2700 | 39 | initialize_readline | ||
2701 | 40 | run_console prompt | ||
2702 | 41 | end | ||
2703 | 42 | |||
2704 | 43 | def console_login(auth_info, port) | ||
2705 | 44 | if !auth_info["username"] || !auth_info["password"] | ||
2706 | 45 | err "Unable to verify console credentials." | ||
2707 | 46 | end | ||
2708 | 47 | @telnet_client = telnet_client(port) | ||
2709 | 48 | prompt = nil | ||
2710 | 49 | err_msg = "Login attempt timed out." | ||
2711 | 50 | 5.times do | ||
2712 | 51 | begin | ||
2713 | 52 | results = @telnet_client.login("Name"=>auth_info["username"], | ||
2714 | 53 | "Password"=>auth_info["password"]) | ||
2715 | 54 | lines = results.sub("Login: Password: ", "").split("\n") | ||
2716 | 55 | last_line = lines.pop | ||
2717 | 56 | if last_line =~ /[$%#>] \z/n | ||
2718 | 57 | prompt = last_line | ||
2719 | 58 | elsif last_line =~ /Login failed/ | ||
2720 | 59 | err_msg = last_line | ||
2721 | 60 | end | ||
2722 | 61 | break | ||
2723 | 62 | rescue TimeoutError | ||
2724 | 63 | sleep 1 | ||
2725 | 64 | rescue EOFError | ||
2726 | 65 | #This may happen if we login right after app starts | ||
2727 | 66 | close_console | ||
2728 | 67 | sleep 5 | ||
2729 | 68 | @telnet_client = telnet_client(port) | ||
2730 | 69 | end | ||
2731 | 70 | display ".", false | ||
2732 | 71 | end | ||
2733 | 72 | unless prompt | ||
2734 | 73 | close_console | ||
2735 | 74 | err err_msg | ||
2736 | 75 | end | ||
2737 | 76 | prompt | ||
2738 | 77 | end | ||
2739 | 78 | |||
2740 | 79 | def send_console_command(cmd) | ||
2741 | 80 | results = @telnet_client.cmd(cmd) | ||
2742 | 81 | results.split("\n") | ||
2743 | 82 | end | ||
2744 | 83 | |||
2745 | 84 | def console_credentials(appname) | ||
2746 | 85 | content = client.app_files(appname, '/app/cf-rails-console/.consoleaccess', '0') | ||
2747 | 86 | YAML.load(content) | ||
2748 | 87 | end | ||
2749 | 88 | |||
2750 | 89 | def close_console | ||
2751 | 90 | @telnet_client.close | ||
2752 | 91 | end | ||
2753 | 92 | |||
2754 | 93 | def console_tab_completion_data(cmd) | ||
2755 | 94 | begin | ||
2756 | 95 | results = @telnet_client.cmd("String"=> cmd + "\t", "Match"=>/\S*\n$/, "Timeout"=>10) | ||
2757 | 96 | results.chomp.split(",") | ||
2758 | 97 | rescue TimeoutError | ||
2759 | 98 | [] #Just return empty results if timeout occurred on tab completion | ||
2760 | 99 | end | ||
2761 | 100 | end | ||
2762 | 101 | |||
2763 | 102 | private | ||
2764 | 103 | def telnet_client(port) | ||
2765 | 104 | Net::Telnet.new({"Port"=>port, "Prompt"=>/[$%#>] \z|Login failed/n, "Timeout"=>30, "FailEOF"=>true}) | ||
2766 | 105 | end | ||
2767 | 106 | |||
2768 | 107 | def readline_with_history(prompt) | ||
2769 | 108 | line = Readline::readline(prompt) | ||
2770 | 109 | return nil if line == nil || line == 'quit' || line == 'exit' | ||
2771 | 110 | Readline::HISTORY.push(line) if not line =~ /^\s*$/ and Readline::HISTORY.to_a[-1] != line | ||
2772 | 111 | line | ||
2773 | 112 | end | ||
2774 | 113 | |||
2775 | 114 | def run_console(prompt) | ||
2776 | 115 | prev = trap("INT") { |x| exit_console; prev.call(x); exit } | ||
2777 | 116 | prev = trap("TERM") { |x| exit_console; prev.call(x); exit } | ||
2778 | 117 | loop do | ||
2779 | 118 | cmd = readline_with_history(prompt) | ||
2780 | 119 | if(cmd == nil) | ||
2781 | 120 | exit_console | ||
2782 | 121 | break | ||
2783 | 122 | end | ||
2784 | 123 | prompt = send_console_command_display_results(cmd, prompt) | ||
2785 | 124 | end | ||
2786 | 125 | end | ||
2787 | 126 | |||
2788 | 127 | def exit_console | ||
2789 | 128 | #TimeoutError expected, as exit doesn't return anything | ||
2790 | 129 | @telnet_client.cmd("String"=>"exit","Timeout"=>1) rescue TimeoutError | ||
2791 | 130 | close_console | ||
2792 | 131 | end | ||
2793 | 132 | |||
2794 | 133 | def send_console_command_display_results(cmd, prompt) | ||
2795 | 134 | begin | ||
2796 | 135 | lines = send_console_command cmd | ||
2797 | 136 | #Assumes the last line is a prompt | ||
2798 | 137 | prompt = lines.pop | ||
2799 | 138 | lines.each {|line| display line if line != cmd} | ||
2800 | 139 | rescue TimeoutError | ||
2801 | 140 | display "Timed out sending command to server.".red | ||
2802 | 141 | rescue EOFError | ||
2803 | 142 | err "The console connection has been terminated. Perhaps the app was stopped or deleted?" | ||
2804 | 143 | end | ||
2805 | 144 | prompt | ||
2806 | 145 | end | ||
2807 | 146 | |||
2808 | 147 | def initialize_readline | ||
2809 | 148 | if Readline.respond_to?("basic_word_break_characters=") | ||
2810 | 149 | Readline.basic_word_break_characters= " \t\n`><=;|&{(" | ||
2811 | 150 | end | ||
2812 | 151 | Readline.completion_append_character = nil | ||
2813 | 152 | #Assumes that sending a String ending with tab will return a non-empty | ||
2814 | 153 | #String of comma-separated completion options, terminated by a new line | ||
2815 | 154 | #For example, "app.\t" might result in "to_s,nil?,etc\n" | ||
2816 | 155 | Readline.completion_proc = proc {|s| | ||
2817 | 156 | console_tab_completion_data s | ||
2818 | 157 | } | ||
2819 | 158 | end | ||
2820 | 159 | end | ||
2821 | 160 | end | ||
2822 | 0 | 161 | ||
2823 | === modified file 'lib/cli/core_ext.rb' | |||
2824 | --- lib/cli/core_ext.rb 2011-06-17 13:38:56 +0000 | |||
2825 | +++ lib/cli/core_ext.rb 2012-06-13 01:37:18 +0000 | |||
2826 | @@ -38,6 +38,10 @@ | |||
2827 | 38 | raise VMC::Cli::CliExit, "#{prefix}#{message}" | 38 | raise VMC::Cli::CliExit, "#{prefix}#{message}" |
2828 | 39 | end | 39 | end |
2829 | 40 | 40 | ||
2830 | 41 | def warn(msg) | ||
2831 | 42 | say "#{"[WARNING]".yellow} #{msg}" | ||
2832 | 43 | end | ||
2833 | 44 | |||
2834 | 41 | def quit(message = nil) | 45 | def quit(message = nil) |
2835 | 42 | raise VMC::Cli::GracefulExit, message | 46 | raise VMC::Cli::GracefulExit, message |
2836 | 43 | end | 47 | end |
2837 | @@ -64,7 +68,6 @@ | |||
2838 | 64 | return sprintf("%.#{prec}fM", size/(1024.0*1024.0)) if size < (1024*1024*1024) | 68 | return sprintf("%.#{prec}fM", size/(1024.0*1024.0)) if size < (1024*1024*1024) |
2839 | 65 | return sprintf("%.#{prec}fG", size/(1024.0*1024.0*1024.0)) | 69 | return sprintf("%.#{prec}fG", size/(1024.0*1024.0*1024.0)) |
2840 | 66 | end | 70 | end |
2841 | 67 | |||
2842 | 68 | end | 71 | end |
2843 | 69 | 72 | ||
2844 | 70 | module VMCStringExtensions | 73 | module VMCStringExtensions |
2845 | 71 | 74 | ||
2846 | === modified file 'lib/cli/frameworks.rb' | |||
2847 | --- lib/cli/frameworks.rb 2011-06-17 13:38:56 +0000 | |||
2848 | +++ lib/cli/frameworks.rb 2012-06-13 01:37:18 +0000 | |||
2849 | @@ -6,47 +6,78 @@ | |||
2850 | 6 | DEFAULT_MEM = '256M' | 6 | DEFAULT_MEM = '256M' |
2851 | 7 | 7 | ||
2852 | 8 | FRAMEWORKS = { | 8 | FRAMEWORKS = { |
2854 | 9 | 'Rails' => ['rails3', { :mem => '256M', :description => 'Rails Application'}], | 9 | 'Rails' => ['rails3', { :mem => '256M', :description => 'Rails Application', :console=>true}], |
2855 | 10 | 'Spring' => ['spring', { :mem => '512M', :description => 'Java SpringSource Spring Application'}], | 10 | 'Spring' => ['spring', { :mem => '512M', :description => 'Java SpringSource Spring Application'}], |
2856 | 11 | 'Grails' => ['grails', { :mem => '512M', :description => 'Java SpringSource Grails Application'}], | 11 | 'Grails' => ['grails', { :mem => '512M', :description => 'Java SpringSource Grails Application'}], |
2859 | 12 | 'Roo' => ['spring', { :mem => '512M', :description => 'Java SpringSource Roo Application'}], | 12 | 'Lift' => ['lift', { :mem => '512M', :description => 'Scala Lift Application'}], |
2860 | 13 | 'JavaWeb' => ['spring', { :mem => '512M', :description => 'Java Web Application'}], | 13 | 'JavaWeb' => ['java_web',{ :mem => '512M', :description => 'Java Web Application'}], |
2861 | 14 | 'Standalone' => ['standalone', { :mem => '64M', :description => 'Standalone Application'}], | ||
2862 | 14 | 'Sinatra' => ['sinatra', { :mem => '128M', :description => 'Sinatra Application'}], | 15 | 'Sinatra' => ['sinatra', { :mem => '128M', :description => 'Sinatra Application'}], |
2863 | 15 | 'Node' => ['node', { :mem => '64M', :description => 'Node.js Application'}], | 16 | 'Node' => ['node', { :mem => '64M', :description => 'Node.js Application'}], |
2864 | 17 | 'PHP' => ['php', { :mem => '128M', :description => 'PHP Application'}], | ||
2865 | 18 | 'Erlang/OTP Rebar' => ['otp_rebar', { :mem => '64M', :description => 'Erlang/OTP Rebar Application'}], | ||
2866 | 19 | 'WSGI' => ['wsgi', { :mem => '64M', :description => 'Python WSGI Application'}], | ||
2867 | 20 | 'Django' => ['django', { :mem => '128M', :description => 'Python Django Application'}], | ||
2868 | 21 | 'dotNet' => ['dotNet', { :mem => '128M', :description => '.Net Web Application'}], | ||
2869 | 22 | 'Rack' => ['rack', { :mem => '128M', :description => 'Rack Application'}], | ||
2870 | 23 | 'Play' => ['play', { :mem => '256M', :description => 'Play Framework Application'}] | ||
2871 | 16 | } | 24 | } |
2872 | 17 | 25 | ||
2873 | 18 | class << self | 26 | class << self |
2874 | 19 | 27 | ||
2877 | 20 | def known_frameworks | 28 | def known_frameworks(available_frameworks) |
2878 | 21 | FRAMEWORKS.keys | 29 | frameworks = [] |
2879 | 30 | FRAMEWORKS.each do |key,fw| | ||
2880 | 31 | frameworks << key if available_frameworks.include? [fw[0]] | ||
2881 | 32 | end | ||
2882 | 33 | frameworks | ||
2883 | 22 | end | 34 | end |
2884 | 23 | 35 | ||
2885 | 24 | def lookup(name) | 36 | def lookup(name) |
2890 | 25 | return Framework.new(*FRAMEWORKS[name]) | 37 | return create(*FRAMEWORKS[name]) |
2891 | 26 | end | 38 | end |
2892 | 27 | 39 | ||
2893 | 28 | def detect(path) | 40 | def lookup_by_framework(name) |
2894 | 41 | FRAMEWORKS.each do |key,fw| | ||
2895 | 42 | return create(fw[0],fw[1]) if fw[0] == name | ||
2896 | 43 | end | ||
2897 | 44 | end | ||
2898 | 45 | |||
2899 | 46 | def create(name,opts) | ||
2900 | 47 | if name == "standalone" | ||
2901 | 48 | return StandaloneFramework.new(name, opts) | ||
2902 | 49 | else | ||
2903 | 50 | return Framework.new(name,opts) | ||
2904 | 51 | end | ||
2905 | 52 | end | ||
2906 | 53 | |||
2907 | 54 | def detect(path, available_frameworks) | ||
2908 | 55 | if !File.directory? path | ||
2909 | 56 | if path.end_with?('.war') | ||
2910 | 57 | return detect_framework_from_war path | ||
2911 | 58 | elsif path.end_with?('.zip') | ||
2912 | 59 | return detect_framework_from_zip path, available_frameworks | ||
2913 | 60 | elsif available_frameworks.include?(["standalone"]) | ||
2914 | 61 | return Framework.lookup('Standalone') | ||
2915 | 62 | else | ||
2916 | 63 | return nil | ||
2917 | 64 | end | ||
2918 | 65 | end | ||
2919 | 29 | Dir.chdir(path) do | 66 | Dir.chdir(path) do |
2920 | 30 | |||
2921 | 31 | # Rails | 67 | # Rails |
2922 | 32 | if File.exist?('config/environment.rb') | 68 | if File.exist?('config/environment.rb') |
2923 | 33 | return Framework.lookup('Rails') | 69 | return Framework.lookup('Rails') |
2924 | 34 | 70 | ||
2926 | 35 | # Java | 71 | # Rack |
2927 | 72 | elsif File.exist?('config.ru') && available_frameworks.include?(["rack"]) | ||
2928 | 73 | return Framework.lookup('Rack') | ||
2929 | 74 | |||
2930 | 75 | # Java Web Apps | ||
2931 | 36 | elsif Dir.glob('*.war').first | 76 | elsif Dir.glob('*.war').first |
2934 | 37 | war_file = Dir.glob('*.war').first | 77 | return detect_framework_from_war(Dir.glob('*.war').first) |
2933 | 38 | contents = ZipUtil.entry_lines(war_file) | ||
2935 | 39 | 78 | ||
2946 | 40 | # Spring Variations | 79 | elsif File.exist?('WEB-INF/web.xml') |
2947 | 41 | if contents =~ /WEB-INF\/lib\/grails-web.*\.jar/ | 80 | return detect_framework_from_war |
2938 | 42 | return Framework.lookup('Grails') | ||
2939 | 43 | elsif contents =~ /WEB-INF\/classes\/org\/springframework/ | ||
2940 | 44 | return Framework.lookup('Spring') | ||
2941 | 45 | elsif contents =~ /WEB-INF\/lib\/spring-core.*\.jar/ | ||
2942 | 46 | return Framework.lookup('Spring') | ||
2943 | 47 | else | ||
2944 | 48 | return Framework.lookup('JavaWeb') | ||
2945 | 49 | end | ||
2948 | 50 | 81 | ||
2949 | 51 | # Simple Ruby Apps | 82 | # Simple Ruby Apps |
2950 | 52 | elsif !Dir.glob('*.rb').empty? | 83 | elsif !Dir.glob('*.rb').empty? |
2951 | @@ -55,43 +86,180 @@ | |||
2952 | 55 | next if matched_file | 86 | next if matched_file |
2953 | 56 | File.open(fname, 'r') do |f| | 87 | File.open(fname, 'r') do |f| |
2954 | 57 | str = f.read # This might want to be limited | 88 | str = f.read # This might want to be limited |
2956 | 58 | matched_file = fname if (str && str.match(/^\s*require\s*['"]sinatra['"]/)) | 89 | matched_file = fname if (str && str.match(/^\s*require[\s\(]*['"]sinatra['"]/)) |
2957 | 59 | end | 90 | end |
2958 | 60 | end | 91 | end |
2959 | 61 | if matched_file | 92 | if matched_file |
2960 | 93 | # Sinatra apps | ||
2961 | 62 | f = Framework.lookup('Sinatra') | 94 | f = Framework.lookup('Sinatra') |
2962 | 63 | f.exec = "ruby #{matched_file}" | 95 | f.exec = "ruby #{matched_file}" |
2963 | 64 | return f | 96 | return f |
2964 | 65 | end | 97 | end |
2965 | 66 | 98 | ||
2966 | 99 | # PHP | ||
2967 | 100 | elsif !Dir.glob('*.php').empty? | ||
2968 | 101 | return Framework.lookup('PHP') | ||
2969 | 102 | |||
2970 | 103 | # Erlang/OTP using Rebar | ||
2971 | 104 | elsif !Dir.glob('releases/*/*.rel').empty? && !Dir.glob('releases/*/*.boot').empty? | ||
2972 | 105 | return Framework.lookup('Erlang/OTP Rebar') | ||
2973 | 106 | |||
2974 | 107 | # Python Django | ||
2975 | 108 | # XXX: not all django projects keep settings.py in top-level directory | ||
2976 | 109 | elsif File.exist?('manage.py') && File.exist?('settings.py') | ||
2977 | 110 | return Framework.lookup('Django') | ||
2978 | 111 | |||
2979 | 112 | # Python | ||
2980 | 113 | elsif !Dir.glob('wsgi.py').empty? | ||
2981 | 114 | return Framework.lookup('WSGI') | ||
2982 | 115 | |||
2983 | 116 | # .Net | ||
2984 | 117 | elsif !Dir.glob('web.config').empty? | ||
2985 | 118 | return Framework.lookup('dotNet') | ||
2986 | 119 | |||
2987 | 67 | # Node.js | 120 | # Node.js |
2988 | 68 | elsif !Dir.glob('*.js').empty? | 121 | elsif !Dir.glob('*.js').empty? |
2991 | 69 | # Fixme, make other files work too.. | 122 | if File.exist?('server.js') || File.exist?('app.js') || File.exist?('index.js') || File.exist?('main.js') |
2990 | 70 | if File.exist?('app.js') || File.exist?('index.js') || File.exist?('main.js') | ||
2992 | 71 | return Framework.lookup('Node') | 123 | return Framework.lookup('Node') |
2993 | 72 | end | 124 | end |
2994 | 125 | |||
2995 | 126 | # Play or Standalone Apps | ||
2996 | 127 | elsif Dir.glob('*.zip').first | ||
2997 | 128 | zip_file = Dir.glob('*.zip').first | ||
2998 | 129 | return detect_framework_from_zip zip_file, available_frameworks | ||
2999 | 73 | end | 130 | end |
3004 | 74 | end | 131 | |
3005 | 75 | nil | 132 | # Default to Standalone if no other match was made |
3006 | 76 | end | 133 | return Framework.lookup('Standalone') if available_frameworks.include?(["standalone"]) |
3007 | 77 | 134 | end | |
3008 | 135 | end | ||
3009 | 136 | |||
3010 | 137 | def detect_framework_from_war(war_file=nil) | ||
3011 | 138 | if war_file | ||
3012 | 139 | contents = ZipUtil.entry_lines(war_file) | ||
3013 | 140 | else | ||
3014 | 141 | #assume we are working with current dir | ||
3015 | 142 | contents = Dir['**/*'].join("\n") | ||
3016 | 143 | end | ||
3017 | 144 | |||
3018 | 145 | # Spring/Lift Variations | ||
3019 | 146 | if contents =~ /WEB-INF\/lib\/grails-web.*\.jar/ | ||
3020 | 147 | return Framework.lookup('Grails') | ||
3021 | 148 | elsif contents =~ /WEB-INF\/lib\/lift-webkit.*\.jar/ | ||
3022 | 149 | return Framework.lookup('Lift') | ||
3023 | 150 | elsif contents =~ /WEB-INF\/classes\/org\/springframework/ | ||
3024 | 151 | return Framework.lookup('Spring') | ||
3025 | 152 | elsif contents =~ /WEB-INF\/lib\/spring-core.*\.jar/ | ||
3026 | 153 | return Framework.lookup('Spring') | ||
3027 | 154 | elsif contents =~ /WEB-INF\/lib\/org\.springframework\.core.*\.jar/ | ||
3028 | 155 | return Framework.lookup('Spring') | ||
3029 | 156 | else | ||
3030 | 157 | return Framework.lookup('JavaWeb') | ||
3031 | 158 | end | ||
3032 | 159 | end | ||
3033 | 160 | |||
3034 | 161 | def detect_framework_from_zip(zip_file, available_frameworks) | ||
3035 | 162 | contents = ZipUtil.entry_lines(zip_file) | ||
3036 | 163 | detect_framework_from_zip_contents(contents, available_frameworks) | ||
3037 | 164 | end | ||
3038 | 165 | |||
3039 | 166 | def detect_framework_from_zip_contents(contents, available_frameworks) | ||
3040 | 167 | if available_frameworks.include?(["play"]) && contents =~ /lib\/play\..*\.jar/ | ||
3041 | 168 | return Framework.lookup('Play') | ||
3042 | 169 | elsif available_frameworks.include?(["standalone"]) | ||
3043 | 170 | return Framework.lookup('Standalone') | ||
3044 | 171 | end | ||
3045 | 172 | end | ||
3046 | 78 | end | 173 | end |
3047 | 79 | 174 | ||
3049 | 80 | attr_reader :name, :description, :memory | 175 | attr_reader :name, :description, :console |
3050 | 81 | attr_accessor :exec | 176 | attr_accessor :exec |
3051 | 82 | 177 | ||
3052 | 83 | alias :mem :memory | ||
3053 | 84 | |||
3054 | 85 | def initialize(framework=nil, opts={}) | 178 | def initialize(framework=nil, opts={}) |
3055 | 86 | @name = framework || DEFAULT_FRAMEWORK | 179 | @name = framework || DEFAULT_FRAMEWORK |
3056 | 87 | @memory = opts[:mem] || DEFAULT_MEM | 180 | @memory = opts[:mem] || DEFAULT_MEM |
3057 | 88 | @description = opts[:description] || 'Unknown Application Type' | 181 | @description = opts[:description] || 'Unknown Application Type' |
3058 | 89 | @exec = opts[:exec] | 182 | @exec = opts[:exec] |
3059 | 183 | @console = opts[:console] || false | ||
3060 | 90 | end | 184 | end |
3061 | 91 | 185 | ||
3062 | 92 | def to_s | 186 | def to_s |
3063 | 93 | description | 187 | description |
3064 | 94 | end | 188 | end |
3065 | 189 | |||
3066 | 190 | def require_url? | ||
3067 | 191 | true | ||
3068 | 192 | end | ||
3069 | 193 | |||
3070 | 194 | def require_start_command? | ||
3071 | 195 | false | ||
3072 | 196 | end | ||
3073 | 197 | |||
3074 | 198 | def prompt_for_runtime? | ||
3075 | 199 | false | ||
3076 | 200 | end | ||
3077 | 201 | |||
3078 | 202 | def default_runtime(path) | ||
3079 | 203 | nil | ||
3080 | 204 | end | ||
3081 | 205 | |||
3082 | 206 | def memory(runtime=nil) | ||
3083 | 207 | @memory | ||
3084 | 208 | end | ||
3085 | 209 | |||
3086 | 210 | alias :mem :memory | ||
3087 | 211 | end | ||
3088 | 212 | |||
3089 | 213 | class StandaloneFramework < Framework | ||
3090 | 214 | def require_url? | ||
3091 | 215 | false | ||
3092 | 216 | end | ||
3093 | 217 | |||
3094 | 218 | def require_start_command? | ||
3095 | 219 | true | ||
3096 | 220 | end | ||
3097 | 221 | |||
3098 | 222 | def prompt_for_runtime? | ||
3099 | 223 | true | ||
3100 | 224 | end | ||
3101 | 225 | |||
3102 | 226 | def default_runtime(path) | ||
3103 | 227 | if !File.directory? path | ||
3104 | 228 | if path =~ /\.(jar|class)$/ | ||
3105 | 229 | return "java" | ||
3106 | 230 | elsif path =~ /\.(rb)$/ | ||
3107 | 231 | return "ruby18" | ||
3108 | 232 | elsif path =~ /\.(zip)$/ | ||
3109 | 233 | return detect_runtime_from_zip path | ||
3110 | 234 | end | ||
3111 | 235 | else | ||
3112 | 236 | Dir.chdir(path) do | ||
3113 | 237 | return "ruby18" if not Dir.glob('**/*.rb').empty? | ||
3114 | 238 | if !Dir.glob('**/*.class').empty? || !Dir.glob('**/*.jar').empty? | ||
3115 | 239 | return "java" | ||
3116 | 240 | elsif Dir.glob('*.zip').first | ||
3117 | 241 | zip_file = Dir.glob('*.zip').first | ||
3118 | 242 | return detect_runtime_from_zip zip_file | ||
3119 | 243 | end | ||
3120 | 244 | end | ||
3121 | 245 | end | ||
3122 | 246 | return nil | ||
3123 | 247 | end | ||
3124 | 248 | |||
3125 | 249 | def memory(runtime=nil) | ||
3126 | 250 | default_mem = @memory | ||
3127 | 251 | default_mem = '128M' if runtime =~ /\Aruby/ || runtime == "php" | ||
3128 | 252 | default_mem = '512M' if runtime == "java" || runtime == "java7" | ||
3129 | 253 | default_mem | ||
3130 | 254 | end | ||
3131 | 255 | |||
3132 | 256 | private | ||
3133 | 257 | def detect_runtime_from_zip(zip_file) | ||
3134 | 258 | contents = ZipUtil.entry_lines(zip_file) | ||
3135 | 259 | if contents =~ /\.(jar)$/ | ||
3136 | 260 | return "java" | ||
3137 | 261 | end | ||
3138 | 262 | end | ||
3139 | 95 | end | 263 | end |
3140 | 96 | 264 | ||
3141 | 97 | end | 265 | end |
3142 | 98 | 266 | ||
3143 | === added file 'lib/cli/manifest_helper.rb' | |||
3144 | --- lib/cli/manifest_helper.rb 1970-01-01 00:00:00 +0000 | |||
3145 | +++ lib/cli/manifest_helper.rb 2012-06-13 01:37:18 +0000 | |||
3146 | @@ -0,0 +1,302 @@ | |||
3147 | 1 | require "set" | ||
3148 | 2 | |||
3149 | 3 | module VMC::Cli::ManifestHelper | ||
3150 | 4 | include VMC::Cli::ServicesHelper | ||
3151 | 5 | |||
3152 | 6 | DEFAULTS = { | ||
3153 | 7 | "url" => "${name}.${target-base}", | ||
3154 | 8 | "mem" => "128M", | ||
3155 | 9 | "instances" => 1 | ||
3156 | 10 | } | ||
3157 | 11 | |||
3158 | 12 | MANIFEST = "manifest.yml" | ||
3159 | 13 | |||
3160 | 14 | YES_SET = Set.new(["y", "Y", "yes", "YES"]) | ||
3161 | 15 | |||
3162 | 16 | # take a block and call it once for each app to push/update. | ||
3163 | 17 | # with @application and @app_info set appropriately | ||
3164 | 18 | def each_app(panic=true) | ||
3165 | 19 | if @manifest and all_apps = @manifest["applications"] | ||
3166 | 20 | where = File.expand_path(@path) | ||
3167 | 21 | single = false | ||
3168 | 22 | |||
3169 | 23 | all_apps.each do |path, info| | ||
3170 | 24 | app = File.expand_path("../" + path, manifest_file) | ||
3171 | 25 | if where.start_with?(app) | ||
3172 | 26 | @application = app | ||
3173 | 27 | @app_info = info | ||
3174 | 28 | yield info["name"] | ||
3175 | 29 | single = true | ||
3176 | 30 | break | ||
3177 | 31 | end | ||
3178 | 32 | end | ||
3179 | 33 | |||
3180 | 34 | unless single | ||
3181 | 35 | if where == File.expand_path("../", manifest_file) | ||
3182 | 36 | ordered_by_deps(all_apps).each do |path, info| | ||
3183 | 37 | app = File.expand_path("../" + path, manifest_file) | ||
3184 | 38 | @application = app | ||
3185 | 39 | @app_info = info | ||
3186 | 40 | yield info["name"] | ||
3187 | 41 | end | ||
3188 | 42 | else | ||
3189 | 43 | err "Path '#{@path}' is not known to manifest '#{manifest_file}'." | ||
3190 | 44 | end | ||
3191 | 45 | end | ||
3192 | 46 | else | ||
3193 | 47 | @application = @path | ||
3194 | 48 | @app_info = @manifest | ||
3195 | 49 | if @app_info | ||
3196 | 50 | yield @app_info["name"] | ||
3197 | 51 | elsif panic | ||
3198 | 52 | err "No applications." | ||
3199 | 53 | end | ||
3200 | 54 | end | ||
3201 | 55 | |||
3202 | 56 | nil | ||
3203 | 57 | ensure | ||
3204 | 58 | @application = nil | ||
3205 | 59 | @app_info = nil | ||
3206 | 60 | end | ||
3207 | 61 | |||
3208 | 62 | def interact(many=false) | ||
3209 | 63 | @manifest ||= {} | ||
3210 | 64 | configure_app(many) | ||
3211 | 65 | end | ||
3212 | 66 | |||
3213 | 67 | def target_manifest | ||
3214 | 68 | @options[:manifest] || MANIFEST | ||
3215 | 69 | end | ||
3216 | 70 | |||
3217 | 71 | def save_manifest(save_to = nil) | ||
3218 | 72 | save_to ||= target_manifest | ||
3219 | 73 | |||
3220 | 74 | File.open(save_to, "w") do |f| | ||
3221 | 75 | f.write @manifest.to_yaml | ||
3222 | 76 | end | ||
3223 | 77 | |||
3224 | 78 | say "Manifest written to #{save_to}." | ||
3225 | 79 | end | ||
3226 | 80 | |||
3227 | 81 | def configure_app(many=false) | ||
3228 | 82 | name = manifest("name") || | ||
3229 | 83 | set(ask("Application Name", :default => manifest("name")), "name") | ||
3230 | 84 | |||
3231 | 85 | |||
3232 | 86 | |||
3233 | 87 | if manifest "framework" | ||
3234 | 88 | framework = VMC::Cli::Framework.lookup_by_framework manifest("framework","name") | ||
3235 | 89 | else | ||
3236 | 90 | framework = detect_framework | ||
3237 | 91 | set framework.name, "framework", "name" | ||
3238 | 92 | set( | ||
3239 | 93 | { "mem" => framework.mem, | ||
3240 | 94 | "description" => framework.description, | ||
3241 | 95 | "exec" => framework.exec | ||
3242 | 96 | }, | ||
3243 | 97 | "framework", | ||
3244 | 98 | "info" | ||
3245 | 99 | ) | ||
3246 | 100 | end | ||
3247 | 101 | |||
3248 | 102 | default_runtime = manifest "runtime" | ||
3249 | 103 | if not default_runtime | ||
3250 | 104 | default_runtime = framework.default_runtime(@application) | ||
3251 | 105 | set(detect_runtime(default_runtime), "runtime") if framework.prompt_for_runtime? | ||
3252 | 106 | end | ||
3253 | 107 | default_command = manifest "command" | ||
3254 | 108 | set ask("Start Command", :default => default_command), "command" if framework.require_start_command? | ||
3255 | 109 | |||
3256 | 110 | url_template = manifest("url") || DEFAULTS["url"] | ||
3257 | 111 | url_resolved = url_template.dup | ||
3258 | 112 | resolve_lexically(url_resolved) | ||
3259 | 113 | |||
3260 | 114 | if !framework.require_url? | ||
3261 | 115 | url_resolved = "None" | ||
3262 | 116 | end | ||
3263 | 117 | url = ask("Application Deployed URL", :default => url_resolved) | ||
3264 | 118 | |||
3265 | 119 | if url == url_resolved && url != "None" | ||
3266 | 120 | url = url_template | ||
3267 | 121 | end | ||
3268 | 122 | |||
3269 | 123 | # common error case is for prompted users to answer y or Y or yes or | ||
3270 | 124 | # YES to this ask() resulting in an unintended URL of y. Special | ||
3271 | 125 | # case this common error | ||
3272 | 126 | url = url_resolved if YES_SET.member? url | ||
3273 | 127 | |||
3274 | 128 | if(url == "None") | ||
3275 | 129 | url = nil | ||
3276 | 130 | end | ||
3277 | 131 | |||
3278 | 132 | set url, "url" | ||
3279 | 133 | |||
3280 | 134 | default_mem = manifest("mem") | ||
3281 | 135 | default_mem = framework.memory(manifest("runtime")) if not default_mem | ||
3282 | 136 | set ask( | ||
3283 | 137 | "Memory reservation", | ||
3284 | 138 | :default => | ||
3285 | 139 | default_mem || | ||
3286 | 140 | DEFAULTS["mem"], | ||
3287 | 141 | :choices => ["128M", "256M", "512M", "1G", "2G"] | ||
3288 | 142 | ), "mem" | ||
3289 | 143 | |||
3290 | 144 | set ask( | ||
3291 | 145 | "How many instances?", | ||
3292 | 146 | :default => manifest("instances") || DEFAULTS["instances"] | ||
3293 | 147 | ), "instances" | ||
3294 | 148 | |||
3295 | 149 | unless manifest "services" | ||
3296 | 150 | user_services = client.services | ||
3297 | 151 | user_services.sort! {|a, b| a[:name] <=> b[:name] } | ||
3298 | 152 | |||
3299 | 153 | unless user_services.empty? | ||
3300 | 154 | if ask "Bind existing services to '#{name}'?", :default => false | ||
3301 | 155 | bind_services(user_services) | ||
3302 | 156 | end | ||
3303 | 157 | end | ||
3304 | 158 | |||
3305 | 159 | services = client.services_info | ||
3306 | 160 | unless services.empty? | ||
3307 | 161 | if ask "Create services to bind to '#{name}'?", :default => false | ||
3308 | 162 | create_services(services.values.collect(&:keys).flatten) | ||
3309 | 163 | end | ||
3310 | 164 | end | ||
3311 | 165 | end | ||
3312 | 166 | |||
3313 | 167 | if many and ask("Configure for another application?", :default => false) | ||
3314 | 168 | @application = ask "Application path?" | ||
3315 | 169 | configure_app | ||
3316 | 170 | end | ||
3317 | 171 | end | ||
3318 | 172 | |||
3319 | 173 | def set(what, *where) | ||
3320 | 174 | where.unshift "applications", @application | ||
3321 | 175 | |||
3322 | 176 | which = @manifest | ||
3323 | 177 | where.each_with_index do |k, i| | ||
3324 | 178 | if i + 1 == where.size | ||
3325 | 179 | which[k] = what | ||
3326 | 180 | else | ||
3327 | 181 | which = (which[k] ||= {}) | ||
3328 | 182 | end | ||
3329 | 183 | end | ||
3330 | 184 | |||
3331 | 185 | what | ||
3332 | 186 | end | ||
3333 | 187 | |||
3334 | 188 | # Detect the appropriate framework. | ||
3335 | 189 | def detect_framework(prompt_ok = true) | ||
3336 | 190 | framework = VMC::Cli::Framework.detect(@application, frameworks_info) | ||
3337 | 191 | framework_correct = ask("Detected a #{framework}, is this correct?", :default => true) if prompt_ok && framework | ||
3338 | 192 | if prompt_ok && (framework.nil? || !framework_correct) | ||
3339 | 193 | display "#{"[WARNING]".yellow} Can't determine the Application Type." unless framework | ||
3340 | 194 | framework = nil if !framework_correct | ||
3341 | 195 | framework = VMC::Cli::Framework.lookup( | ||
3342 | 196 | ask( | ||
3343 | 197 | "Select Application Type", | ||
3344 | 198 | :indexed => true, | ||
3345 | 199 | :default => framework, | ||
3346 | 200 | :choices => VMC::Cli::Framework.known_frameworks(frameworks_info) | ||
3347 | 201 | ) | ||
3348 | 202 | ) | ||
3349 | 203 | display "Selected #{framework}" | ||
3350 | 204 | end | ||
3351 | 205 | |||
3352 | 206 | framework | ||
3353 | 207 | end | ||
3354 | 208 | |||
3355 | 209 | # Detect the appropriate runtime. | ||
3356 | 210 | def detect_runtime(default, prompt_ok=true) | ||
3357 | 211 | runtime = nil | ||
3358 | 212 | runtime_keys=[] | ||
3359 | 213 | runtimes_info.keys.each {|runtime_key| runtime_keys << runtime_key.dup } | ||
3360 | 214 | runtime_keys.sort! | ||
3361 | 215 | if prompt_ok | ||
3362 | 216 | runtime = ask( | ||
3363 | 217 | "Select Runtime", | ||
3364 | 218 | :indexed => true, | ||
3365 | 219 | :default => default, | ||
3366 | 220 | :choices => runtime_keys | ||
3367 | 221 | ) | ||
3368 | 222 | display "Selected #{runtime}" | ||
3369 | 223 | end | ||
3370 | 224 | runtime | ||
3371 | 225 | end | ||
3372 | 226 | |||
3373 | 227 | def bind_services(user_services, chosen = 0) | ||
3374 | 228 | svcname = ask( | ||
3375 | 229 | "Which one?", | ||
3376 | 230 | :indexed => true, | ||
3377 | 231 | :choices => user_services.collect { |p| p[:name] }) | ||
3378 | 232 | |||
3379 | 233 | svc = user_services.find { |p| p[:name] == svcname } | ||
3380 | 234 | |||
3381 | 235 | set svc[:vendor], "services", svcname, "type" | ||
3382 | 236 | |||
3383 | 237 | if chosen + 1 < user_services.size && ask("Bind another?", :default => false) | ||
3384 | 238 | bind_services(user_services, chosen + 1) | ||
3385 | 239 | end | ||
3386 | 240 | end | ||
3387 | 241 | |||
3388 | 242 | def create_services(services) | ||
3389 | 243 | svcs = services.collect(&:to_s).sort! | ||
3390 | 244 | |||
3391 | 245 | configure_service( | ||
3392 | 246 | ask( | ||
3393 | 247 | "What kind of service?", | ||
3394 | 248 | :indexed => true, | ||
3395 | 249 | :choices => svcs | ||
3396 | 250 | ) | ||
3397 | 251 | ) | ||
3398 | 252 | |||
3399 | 253 | if ask "Create another?", :default => false | ||
3400 | 254 | create_services(services) | ||
3401 | 255 | end | ||
3402 | 256 | end | ||
3403 | 257 | |||
3404 | 258 | def configure_service(vendor) | ||
3405 | 259 | default_name = random_service_name(vendor) | ||
3406 | 260 | name = ask "Specify the name of the service", :default => default_name | ||
3407 | 261 | |||
3408 | 262 | set vendor, "services", name, "type" | ||
3409 | 263 | end | ||
3410 | 264 | |||
3411 | 265 | private | ||
3412 | 266 | def ordered_by_deps(apps, abspaths = nil, processed = Set[]) | ||
3413 | 267 | unless abspaths | ||
3414 | 268 | abspaths = {} | ||
3415 | 269 | apps.each do |p, i| | ||
3416 | 270 | ep = File.expand_path("../" + p, manifest_file) | ||
3417 | 271 | abspaths[ep] = i | ||
3418 | 272 | end | ||
3419 | 273 | end | ||
3420 | 274 | |||
3421 | 275 | ordered = [] | ||
3422 | 276 | apps.each do |path, info| | ||
3423 | 277 | epath = File.expand_path("../" + path, manifest_file) | ||
3424 | 278 | |||
3425 | 279 | if deps = info["depends-on"] | ||
3426 | 280 | dep_apps = {} | ||
3427 | 281 | deps.each do |dep| | ||
3428 | 282 | edep = File.expand_path("../" + dep, manifest_file) | ||
3429 | 283 | |||
3430 | 284 | err "Circular dependency detected." if processed.include? edep | ||
3431 | 285 | |||
3432 | 286 | dep_apps[dep] = abspaths[edep] | ||
3433 | 287 | end | ||
3434 | 288 | |||
3435 | 289 | processed.add(epath) | ||
3436 | 290 | |||
3437 | 291 | ordered += ordered_by_deps(dep_apps, abspaths, processed) | ||
3438 | 292 | ordered << [path, info] | ||
3439 | 293 | elsif not processed.include? epath | ||
3440 | 294 | ordered << [path, info] | ||
3441 | 295 | processed.add(epath) | ||
3442 | 296 | end | ||
3443 | 297 | end | ||
3444 | 298 | |||
3445 | 299 | ordered | ||
3446 | 300 | end | ||
3447 | 301 | |||
3448 | 302 | end | ||
3449 | 0 | 303 | ||
3450 | === modified file 'lib/cli/runner.rb' | |||
3451 | --- lib/cli/runner.rb 2011-06-17 13:38:56 +0000 | |||
3452 | +++ lib/cli/runner.rb 2012-06-13 01:37:18 +0000 | |||
3453 | @@ -31,6 +31,7 @@ | |||
3454 | 31 | opts.on('--passwd PASS') { |pass| @options[:password] = pass } | 31 | opts.on('--passwd PASS') { |pass| @options[:password] = pass } |
3455 | 32 | opts.on('--pass PASS') { |pass| @options[:password] = pass } | 32 | opts.on('--pass PASS') { |pass| @options[:password] = pass } |
3456 | 33 | opts.on('--password PASS') { |pass| @options[:password] = pass } | 33 | opts.on('--password PASS') { |pass| @options[:password] = pass } |
3457 | 34 | opts.on('--token-file TOKEN_FILE') { |token_file| @options[:token_file] = token_file } | ||
3458 | 34 | opts.on('--app NAME') { |name| @options[:name] = name } | 35 | opts.on('--app NAME') { |name| @options[:name] = name } |
3459 | 35 | opts.on('--name NAME') { |name| @options[:name] = name } | 36 | opts.on('--name NAME') { |name| @options[:name] = name } |
3460 | 36 | opts.on('--bind BIND') { |bind| @options[:bind] = bind } | 37 | opts.on('--bind BIND') { |bind| @options[:bind] = bind } |
3461 | @@ -48,8 +49,21 @@ | |||
3462 | 48 | opts.on('-t [TKEY]') { |tkey| @options[:trace] = tkey || true } | 49 | opts.on('-t [TKEY]') { |tkey| @options[:trace] = tkey || true } |
3463 | 49 | opts.on('--trace [TKEY]') { |tkey| @options[:trace] = tkey || true } | 50 | opts.on('--trace [TKEY]') { |tkey| @options[:trace] = tkey || true } |
3464 | 50 | 51 | ||
3465 | 52 | # start application in debug mode | ||
3466 | 53 | opts.on('-d [MODE]') { |mode| @options[:debug] = mode || "run" } | ||
3467 | 54 | opts.on('--debug [MODE]') { |mode| @options[:debug] = mode || "run" } | ||
3468 | 55 | |||
3469 | 56 | # override manifest file | ||
3470 | 57 | opts.on('-m FILE') { |file| @options[:manifest] = file } | ||
3471 | 58 | opts.on('--manifest FILE') { |file| @options[:manifest] = file } | ||
3472 | 59 | |||
3473 | 51 | opts.on('-q', '--quiet') { @options[:quiet] = true } | 60 | opts.on('-q', '--quiet') { @options[:quiet] = true } |
3474 | 52 | 61 | ||
3475 | 62 | # micro cloud options | ||
3476 | 63 | opts.on('--vmx FILE') { |file| @options[:vmx] = file } | ||
3477 | 64 | opts.on('--vmrun FILE') { |file| @options[:vmrun] = file } | ||
3478 | 65 | opts.on('--save') { @options[:save] = true } | ||
3479 | 66 | |||
3480 | 53 | # Don't use builtin zip | 67 | # Don't use builtin zip |
3481 | 54 | opts.on('--no-zip') { @options[:nozip] = true } | 68 | opts.on('--no-zip') { @options[:nozip] = true } |
3482 | 55 | opts.on('--nozip') { @options[:nozip] = true } | 69 | opts.on('--nozip') { @options[:nozip] = true } |
3483 | @@ -73,6 +87,8 @@ | |||
3484 | 73 | opts.on('-v', '--version') { set_cmd(:misc, :version) } | 87 | opts.on('-v', '--version') { set_cmd(:misc, :version) } |
3485 | 74 | opts.on('-h', '--help') { puts "#{command_usage}\n"; exit } | 88 | opts.on('-h', '--help') { puts "#{command_usage}\n"; exit } |
3486 | 75 | 89 | ||
3487 | 90 | opts.on('--port PORT') { |port| @options[:port] = port } | ||
3488 | 91 | |||
3489 | 76 | opts.on('--runtime RUNTIME') { |rt| @options[:runtime] = rt } | 92 | opts.on('--runtime RUNTIME') { |rt| @options[:runtime] = rt } |
3490 | 77 | 93 | ||
3491 | 78 | # deprecated | 94 | # deprecated |
3492 | @@ -107,7 +123,6 @@ | |||
3493 | 107 | def convert_options! | 123 | def convert_options! |
3494 | 108 | # make sure certain options are valid and in correct form. | 124 | # make sure certain options are valid and in correct form. |
3495 | 109 | @options[:instances] = Integer(@options[:instances]) if @options[:instances] | 125 | @options[:instances] = Integer(@options[:instances]) if @options[:instances] |
3496 | 110 | @options[:instance] = Integer(@options[:instance]) if @options[:instance] | ||
3497 | 111 | end | 126 | end |
3498 | 112 | 127 | ||
3499 | 113 | def set_cmd(namespace, action, args_range=0) | 128 | def set_cmd(namespace, action, args_range=0) |
3500 | @@ -204,6 +219,10 @@ | |||
3501 | 204 | usage('vmc delete-user <user>') | 219 | usage('vmc delete-user <user>') |
3502 | 205 | set_cmd(:admin, :delete_user, 1) | 220 | set_cmd(:admin, :delete_user, 1) |
3503 | 206 | 221 | ||
3504 | 222 | when 'users' | ||
3505 | 223 | usage('vmc users') | ||
3506 | 224 | set_cmd(:admin, :users) | ||
3507 | 225 | |||
3508 | 207 | when 'apps' | 226 | when 'apps' |
3509 | 208 | usage('vmc apps') | 227 | usage('vmc apps') |
3510 | 209 | set_cmd(:apps, :apps) | 228 | set_cmd(:apps, :apps) |
3511 | @@ -214,19 +233,15 @@ | |||
3512 | 214 | 233 | ||
3513 | 215 | when 'start' | 234 | when 'start' |
3514 | 216 | usage('vmc start <appname>') | 235 | usage('vmc start <appname>') |
3516 | 217 | set_cmd(:apps, :start, 1) | 236 | set_cmd(:apps, :start, @args.size == 1 ? 1 : 0) |
3517 | 218 | 237 | ||
3518 | 219 | when 'stop' | 238 | when 'stop' |
3519 | 220 | usage('vmc stop <appname>') | 239 | usage('vmc stop <appname>') |
3521 | 221 | set_cmd(:apps, :stop, 1) | 240 | set_cmd(:apps, :stop, @args.size == 1 ? 1 : 0) |
3522 | 222 | 241 | ||
3523 | 223 | when 'restart' | 242 | when 'restart' |
3524 | 224 | usage('vmc restart <appname>') | 243 | usage('vmc restart <appname>') |
3530 | 225 | set_cmd(:apps, :restart, 1) | 244 | set_cmd(:apps, :restart, @args.size == 1 ? 1 : 0) |
3526 | 226 | |||
3527 | 227 | when 'rename' | ||
3528 | 228 | usage('vmc rename <appname> <newname>') | ||
3529 | 229 | set_cmd(:apps, :rename, 2) | ||
3531 | 230 | 245 | ||
3532 | 231 | when 'mem' | 246 | when 'mem' |
3533 | 232 | usage('vmc mem <appname> [memsize]') | 247 | usage('vmc mem <appname> [memsize]') |
3534 | @@ -238,7 +253,7 @@ | |||
3535 | 238 | 253 | ||
3536 | 239 | when 'stats' | 254 | when 'stats' |
3537 | 240 | usage('vmc stats <appname>') | 255 | usage('vmc stats <appname>') |
3539 | 241 | set_cmd(:apps, :stats, 1) | 256 | set_cmd(:apps, :stats, @args.size == 1 ? 1 : 0) |
3540 | 242 | 257 | ||
3541 | 243 | when 'map' | 258 | when 'map' |
3542 | 244 | usage('vmc map <appname> <url>') | 259 | usage('vmc map <appname> <url>') |
3543 | @@ -269,12 +284,12 @@ | |||
3544 | 269 | set_cmd(:apps, :logs, 1) | 284 | set_cmd(:apps, :logs, 1) |
3545 | 270 | 285 | ||
3546 | 271 | when 'instances', 'scale' | 286 | when 'instances', 'scale' |
3548 | 272 | if @args.size == 1 | 287 | if @args.size > 1 |
3549 | 288 | usage('vmc instances <appname> <num|delta>') | ||
3550 | 289 | set_cmd(:apps, :instances, 2) | ||
3551 | 290 | else | ||
3552 | 273 | usage('vmc instances <appname>') | 291 | usage('vmc instances <appname>') |
3553 | 274 | set_cmd(:apps, :instances, 1) | 292 | set_cmd(:apps, :instances, 1) |
3554 | 275 | else | ||
3555 | 276 | usage('vmc instances <appname> <num|delta>') | ||
3556 | 277 | set_cmd(:apps, :instances, 2) | ||
3557 | 278 | end | 293 | end |
3558 | 279 | 294 | ||
3559 | 280 | when 'crashes' | 295 | when 'crashes' |
3560 | @@ -286,7 +301,7 @@ | |||
3561 | 286 | set_cmd(:apps, :crashlogs, 1) | 301 | set_cmd(:apps, :crashlogs, 1) |
3562 | 287 | 302 | ||
3563 | 288 | when 'push' | 303 | when 'push' |
3565 | 289 | usage('vmc push [appname] [--path PATH] [--url URL] [--instances N] [--mem] [--no-start]') | 304 | usage('vmc push [appname] [--path PATH] [--url URL] [--instances N] [--mem] [--runtime RUNTIME] [--no-start]') |
3566 | 290 | if @args.size == 1 | 305 | if @args.size == 1 |
3567 | 291 | set_cmd(:apps, :push, 1) | 306 | set_cmd(:apps, :push, 1) |
3568 | 292 | else | 307 | else |
3569 | @@ -295,7 +310,7 @@ | |||
3570 | 295 | 310 | ||
3571 | 296 | when 'update' | 311 | when 'update' |
3572 | 297 | usage('vmc update <appname> [--path PATH]') | 312 | usage('vmc update <appname> [--path PATH]') |
3574 | 298 | set_cmd(:apps, :update, 1) | 313 | set_cmd(:apps, :update, @args.size == 1 ? 1 : 0) |
3575 | 299 | 314 | ||
3576 | 300 | when 'services' | 315 | when 'services' |
3577 | 301 | usage('vmc services') | 316 | usage('vmc services') |
3578 | @@ -360,6 +375,22 @@ | |||
3579 | 360 | usage('vmc unalias <alias>') | 375 | usage('vmc unalias <alias>') |
3580 | 361 | set_cmd(:misc, :unalias, 1) | 376 | set_cmd(:misc, :unalias, 1) |
3581 | 362 | 377 | ||
3582 | 378 | when 'tunnel' | ||
3583 | 379 | usage('vmc tunnel [servicename] [clientcmd] [--port port]') | ||
3584 | 380 | set_cmd(:services, :tunnel, 0) if @args.size == 0 | ||
3585 | 381 | set_cmd(:services, :tunnel, 1) if @args.size == 1 | ||
3586 | 382 | set_cmd(:services, :tunnel, 2) if @args.size == 2 | ||
3587 | 383 | |||
3588 | 384 | when 'rails-console' | ||
3589 | 385 | usage('vmc rails-console <appname>') | ||
3590 | 386 | set_cmd(:apps, :console, 1) | ||
3591 | 387 | |||
3592 | 388 | when 'micro' | ||
3593 | 389 | usage('vmc micro <online|offline|status> [--password password] [--save] [--vmx file] [--vmrun executable]') | ||
3594 | 390 | if %w[online offline status].include?(@args[0]) | ||
3595 | 391 | set_cmd(:micro, @args[0].to_sym, 1) | ||
3596 | 392 | end | ||
3597 | 393 | |||
3598 | 363 | when 'help' | 394 | when 'help' |
3599 | 364 | display_help if @args.size == 0 | 395 | display_help if @args.size == 0 |
3600 | 365 | @help_only = true | 396 | @help_only = true |
3601 | @@ -374,6 +405,14 @@ | |||
3602 | 374 | @args = @args.unshift('--options') | 405 | @args = @args.unshift('--options') |
3603 | 375 | parse_options! | 406 | parse_options! |
3604 | 376 | 407 | ||
3605 | 408 | when 'manifest' | ||
3606 | 409 | usage('vmc manifest') | ||
3607 | 410 | set_cmd(:manifest, :edit) | ||
3608 | 411 | |||
3609 | 412 | when 'extend-manifest' | ||
3610 | 413 | usage('vmc extend-manifest') | ||
3611 | 414 | set_cmd(:manifest, :extend, 1) | ||
3612 | 415 | |||
3613 | 377 | else | 416 | else |
3614 | 378 | if verb | 417 | if verb |
3615 | 379 | display "vmc: Unknown command [#{verb}]" | 418 | display "vmc: Unknown command [#{verb}]" |
3616 | @@ -407,8 +446,7 @@ | |||
3617 | 407 | 446 | ||
3618 | 408 | def run | 447 | def run |
3619 | 409 | 448 | ||
3622 | 410 | trap('TERM') { print "\nInterrupted\n"; exit(false)} | 449 | trap('TERM') { print "\nTerminated\n"; exit(false)} |
3621 | 411 | trap('INT') { print "\nInterrupted\n"; exit(false)} | ||
3623 | 412 | 450 | ||
3624 | 413 | parse_options! | 451 | parse_options! |
3625 | 414 | 452 | ||
3626 | @@ -423,7 +461,8 @@ | |||
3627 | 423 | parse_command! | 461 | parse_command! |
3628 | 424 | 462 | ||
3629 | 425 | if @namespace && @action | 463 | if @namespace && @action |
3631 | 426 | eval("VMC::Cli::Command::#{@namespace.to_s.capitalize}").new(@options).send(@action.to_sym, *@args) | 464 | cmd = VMC::Cli::Command.const_get(@namespace.to_s.capitalize) |
3632 | 465 | cmd.new(@options).send(@action, *@args.collect(&:dup)) | ||
3633 | 427 | elsif @help_only || @usage | 466 | elsif @help_only || @usage |
3634 | 428 | display_usage | 467 | display_usage |
3635 | 429 | else | 468 | else |
3636 | @@ -432,6 +471,10 @@ | |||
3637 | 432 | end | 471 | end |
3638 | 433 | 472 | ||
3639 | 434 | rescue OptionParser::InvalidOption => e | 473 | rescue OptionParser::InvalidOption => e |
3640 | 474 | puts(e.message.red) | ||
3641 | 475 | puts("\n") | ||
3642 | 476 | puts(basic_usage) | ||
3643 | 477 | @exit_status = false | ||
3644 | 435 | rescue OptionParser::AmbiguousOption => e | 478 | rescue OptionParser::AmbiguousOption => e |
3645 | 436 | puts(e.message.red) | 479 | puts(e.message.red) |
3646 | 437 | puts("\n") | 480 | puts("\n") |
3647 | @@ -464,7 +507,10 @@ | |||
3648 | 464 | puts e.message.red | 507 | puts e.message.red |
3649 | 465 | puts e.backtrace | 508 | puts e.backtrace |
3650 | 466 | @exit_status = false | 509 | @exit_status = false |
3652 | 467 | rescue => e | 510 | rescue Interrupt => e |
3653 | 511 | say("\nInterrupted".red) | ||
3654 | 512 | @exit_status = false | ||
3655 | 513 | rescue Exception => e | ||
3656 | 468 | puts e.message.red | 514 | puts e.message.red |
3657 | 469 | puts e.backtrace | 515 | puts e.backtrace |
3658 | 470 | @exit_status = false | 516 | @exit_status = false |
3659 | 471 | 517 | ||
3660 | === modified file 'lib/cli/services_helper.rb' | |||
3661 | --- lib/cli/services_helper.rb 2011-06-17 13:38:56 +0000 | |||
3662 | +++ lib/cli/services_helper.rb 2012-06-13 01:37:18 +0000 | |||
3663 | @@ -8,15 +8,19 @@ | |||
3664 | 8 | 8 | ||
3665 | 9 | return display "No system services available" if services.empty? | 9 | return display "No system services available" if services.empty? |
3666 | 10 | 10 | ||
3667 | 11 | displayed_services = [] | ||
3668 | 12 | services.each do |service_type, value| | ||
3669 | 13 | value.each do |vendor, version| | ||
3670 | 14 | version.each do |version_str, service| | ||
3671 | 15 | displayed_services << [ vendor, version_str, service[:description] ] | ||
3672 | 16 | end | ||
3673 | 17 | end | ||
3674 | 18 | end | ||
3675 | 19 | displayed_services.sort! { |a, b| a.first.to_s <=> b.first.to_s} | ||
3676 | 20 | |||
3677 | 11 | services_table = table do |t| | 21 | services_table = table do |t| |
3678 | 12 | t.headings = 'Service', 'Version', 'Description' | 22 | t.headings = 'Service', 'Version', 'Description' |
3686 | 13 | services.each do |service_type, value| | 23 | displayed_services.each { |s| t << s } |
3680 | 14 | value.each do |vendor, version| | ||
3681 | 15 | version.each do |version_str, service| | ||
3682 | 16 | t << [ vendor, version_str, service[:description] ] | ||
3683 | 17 | end | ||
3684 | 18 | end | ||
3685 | 19 | end | ||
3687 | 20 | end | 24 | end |
3688 | 21 | display services_table | 25 | display services_table |
3689 | 22 | end | 26 | end |
3690 | @@ -46,19 +50,25 @@ | |||
3691 | 46 | end | 50 | end |
3692 | 47 | 51 | ||
3693 | 48 | def bind_service_banner(service, appname, check_restart=true) | 52 | def bind_service_banner(service, appname, check_restart=true) |
3695 | 49 | display "Binding Service: ", false | 53 | display "Binding Service [#{service}]: ", false |
3696 | 50 | client.bind_service(service, appname) | 54 | client.bind_service(service, appname) |
3697 | 51 | display 'OK'.green | 55 | display 'OK'.green |
3698 | 52 | check_app_for_restart(appname) if check_restart | 56 | check_app_for_restart(appname) if check_restart |
3699 | 53 | end | 57 | end |
3700 | 54 | 58 | ||
3701 | 55 | def unbind_service_banner(service, appname, check_restart=true) | 59 | def unbind_service_banner(service, appname, check_restart=true) |
3703 | 56 | display "Unbinding Service: ", false | 60 | display "Unbinding Service [#{service}]: ", false |
3704 | 57 | client.unbind_service(service, appname) | 61 | client.unbind_service(service, appname) |
3705 | 58 | display 'OK'.green | 62 | display 'OK'.green |
3706 | 59 | check_app_for_restart(appname) if check_restart | 63 | check_app_for_restart(appname) if check_restart |
3707 | 60 | end | 64 | end |
3708 | 61 | 65 | ||
3709 | 66 | def delete_service_banner(service) | ||
3710 | 67 | display "Deleting service [#{service}]: ", false | ||
3711 | 68 | client.delete_service(service) | ||
3712 | 69 | display 'OK'.green | ||
3713 | 70 | end | ||
3714 | 71 | |||
3715 | 62 | def random_service_name(service) | 72 | def random_service_name(service) |
3716 | 63 | r = "%04x" % [rand(0x0100000)] | 73 | r = "%04x" % [rand(0x0100000)] |
3717 | 64 | "#{service.to_s}-#{r}" | 74 | "#{service.to_s}-#{r}" |
3718 | 65 | 75 | ||
3719 | === added file 'lib/cli/tunnel_helper.rb' | |||
3720 | --- lib/cli/tunnel_helper.rb 1970-01-01 00:00:00 +0000 | |||
3721 | +++ lib/cli/tunnel_helper.rb 2012-06-13 01:37:18 +0000 | |||
3722 | @@ -0,0 +1,332 @@ | |||
3723 | 1 | # Copyright (c) 2009-2011 VMware, Inc. | ||
3724 | 2 | |||
3725 | 3 | require 'addressable/uri' | ||
3726 | 4 | |||
3727 | 5 | begin | ||
3728 | 6 | require 'caldecott' | ||
3729 | 7 | rescue LoadError | ||
3730 | 8 | end | ||
3731 | 9 | |||
3732 | 10 | module VMC::Cli | ||
3733 | 11 | module TunnelHelper | ||
3734 | 12 | PORT_RANGE = 10 | ||
3735 | 13 | |||
3736 | 14 | HELPER_APP = File.expand_path("../../../caldecott_helper", __FILE__) | ||
3737 | 15 | |||
3738 | 16 | # bump this AND the version info reported by HELPER_APP/server.rb | ||
3739 | 17 | # this is to keep the helper in sync with any updates here | ||
3740 | 18 | HELPER_VERSION = '0.0.4' | ||
3741 | 19 | |||
3742 | 20 | def tunnel_uniquename | ||
3743 | 21 | random_service_name(tunnel_appname) | ||
3744 | 22 | end | ||
3745 | 23 | |||
3746 | 24 | def tunnel_appname | ||
3747 | 25 | "caldecott" | ||
3748 | 26 | end | ||
3749 | 27 | |||
3750 | 28 | def tunnel_app_info | ||
3751 | 29 | return @tun_app_info if @tunnel_app_info | ||
3752 | 30 | begin | ||
3753 | 31 | @tun_app_info = client.app_info(tunnel_appname) | ||
3754 | 32 | rescue => e | ||
3755 | 33 | @tun_app_info = nil | ||
3756 | 34 | end | ||
3757 | 35 | end | ||
3758 | 36 | |||
3759 | 37 | def tunnel_auth | ||
3760 | 38 | tunnel_app_info[:env].each do |e| | ||
3761 | 39 | name, val = e.split("=", 2) | ||
3762 | 40 | return val if name == "CALDECOTT_AUTH" | ||
3763 | 41 | end | ||
3764 | 42 | nil | ||
3765 | 43 | end | ||
3766 | 44 | |||
3767 | 45 | def tunnel_url | ||
3768 | 46 | return @tunnel_url if @tunnel_url | ||
3769 | 47 | |||
3770 | 48 | tun_url = tunnel_app_info[:uris][0] | ||
3771 | 49 | |||
3772 | 50 | ["https", "http"].each do |scheme| | ||
3773 | 51 | url = "#{scheme}://#{tun_url}" | ||
3774 | 52 | begin | ||
3775 | 53 | RestClient.get(url) | ||
3776 | 54 | |||
3777 | 55 | # https failed | ||
3778 | 56 | rescue Errno::ECONNREFUSED | ||
3779 | 57 | |||
3780 | 58 | # we expect a 404 since this request isn't auth'd | ||
3781 | 59 | rescue RestClient::ResourceNotFound | ||
3782 | 60 | return @tunnel_url = url | ||
3783 | 61 | end | ||
3784 | 62 | end | ||
3785 | 63 | |||
3786 | 64 | err "Cannot determine URL for #{tun_url}" | ||
3787 | 65 | end | ||
3788 | 66 | |||
3789 | 67 | def invalidate_tunnel_app_info | ||
3790 | 68 | @tunnel_url = nil | ||
3791 | 69 | @tunnel_app_info = nil | ||
3792 | 70 | end | ||
3793 | 71 | |||
3794 | 72 | def tunnel_pushed? | ||
3795 | 73 | not tunnel_app_info.nil? | ||
3796 | 74 | end | ||
3797 | 75 | |||
3798 | 76 | def tunnel_healthy?(token) | ||
3799 | 77 | return false unless tunnel_app_info[:state] == 'STARTED' | ||
3800 | 78 | |||
3801 | 79 | begin | ||
3802 | 80 | response = RestClient.get( | ||
3803 | 81 | "#{tunnel_url}/info", | ||
3804 | 82 | "Auth-Token" => token | ||
3805 | 83 | ) | ||
3806 | 84 | |||
3807 | 85 | info = JSON.parse(response) | ||
3808 | 86 | if info["version"] == HELPER_VERSION | ||
3809 | 87 | true | ||
3810 | 88 | else | ||
3811 | 89 | stop_caldecott | ||
3812 | 90 | false | ||
3813 | 91 | end | ||
3814 | 92 | rescue RestClient::Exception | ||
3815 | 93 | stop_caldecott | ||
3816 | 94 | false | ||
3817 | 95 | end | ||
3818 | 96 | end | ||
3819 | 97 | |||
3820 | 98 | def tunnel_bound?(service) | ||
3821 | 99 | tunnel_app_info[:services].include?(service) | ||
3822 | 100 | end | ||
3823 | 101 | |||
3824 | 102 | def tunnel_connection_info(type, service, token) | ||
3825 | 103 | display "Getting tunnel connection info: ", false | ||
3826 | 104 | response = nil | ||
3827 | 105 | 10.times do | ||
3828 | 106 | begin | ||
3829 | 107 | response = RestClient.get(tunnel_url + "/" + VMC::Client.path("services", service), "Auth-Token" => token) | ||
3830 | 108 | break | ||
3831 | 109 | rescue RestClient::Exception | ||
3832 | 110 | sleep 1 | ||
3833 | 111 | end | ||
3834 | 112 | |||
3835 | 113 | display ".", false | ||
3836 | 114 | end | ||
3837 | 115 | |||
3838 | 116 | unless response | ||
3839 | 117 | err "Expected remote tunnel to know about #{service}, but it doesn't" | ||
3840 | 118 | end | ||
3841 | 119 | |||
3842 | 120 | display "OK".green | ||
3843 | 121 | |||
3844 | 122 | info = JSON.parse(response) | ||
3845 | 123 | case type | ||
3846 | 124 | when "rabbitmq" | ||
3847 | 125 | uri = Addressable::URI.parse info["url"] | ||
3848 | 126 | info["hostname"] = uri.host | ||
3849 | 127 | info["port"] = uri.port | ||
3850 | 128 | info["vhost"] = uri.path[1..-1] | ||
3851 | 129 | info["user"] = uri.user | ||
3852 | 130 | info["password"] = uri.password | ||
3853 | 131 | info.delete "url" | ||
3854 | 132 | |||
3855 | 133 | # we use "db" as the "name" for mongo | ||
3856 | 134 | # existing "name" is junk | ||
3857 | 135 | when "mongodb" | ||
3858 | 136 | info["name"] = info["db"] | ||
3859 | 137 | info.delete "db" | ||
3860 | 138 | |||
3861 | 139 | # our "name" is irrelevant for redis | ||
3862 | 140 | when "redis" | ||
3863 | 141 | info.delete "name" | ||
3864 | 142 | end | ||
3865 | 143 | |||
3866 | 144 | ['hostname', 'port', 'password'].each do |k| | ||
3867 | 145 | err "Could not determine #{k} for #{service}" if info[k].nil? | ||
3868 | 146 | end | ||
3869 | 147 | |||
3870 | 148 | info | ||
3871 | 149 | end | ||
3872 | 150 | |||
3873 | 151 | def display_tunnel_connection_info(info) | ||
3874 | 152 | display '' | ||
3875 | 153 | display "Service connection info: " | ||
3876 | 154 | |||
3877 | 155 | to_show = [nil, nil, nil] # reserved for user, pass, db name | ||
3878 | 156 | info.keys.each do |k| | ||
3879 | 157 | case k | ||
3880 | 158 | when "host", "hostname", "port", "node_id" | ||
3881 | 159 | # skip | ||
3882 | 160 | when "user", "username" | ||
3883 | 161 | # prefer "username" over "user" | ||
3884 | 162 | to_show[0] = k unless to_show[0] == "username" | ||
3885 | 163 | when "password" | ||
3886 | 164 | to_show[1] = k | ||
3887 | 165 | when "name" | ||
3888 | 166 | to_show[2] = k | ||
3889 | 167 | else | ||
3890 | 168 | to_show << k | ||
3891 | 169 | end | ||
3892 | 170 | end | ||
3893 | 171 | to_show.compact! | ||
3894 | 172 | |||
3895 | 173 | align_len = to_show.collect(&:size).max + 1 | ||
3896 | 174 | |||
3897 | 175 | to_show.each do |k| | ||
3898 | 176 | # TODO: modify the server services rest call to have explicit knowledge | ||
3899 | 177 | # about the items to return. It should return all of them if | ||
3900 | 178 | # the service is unknown so that we don't have to do this weird | ||
3901 | 179 | # filtering. | ||
3902 | 180 | display " #{k.ljust align_len}: ", false | ||
3903 | 181 | display "#{info[k]}".yellow | ||
3904 | 182 | end | ||
3905 | 183 | display '' | ||
3906 | 184 | end | ||
3907 | 185 | |||
3908 | 186 | def start_tunnel(local_port, conn_info, auth) | ||
3909 | 187 | @local_tunnel_thread = Thread.new do | ||
3910 | 188 | Caldecott::Client.start({ | ||
3911 | 189 | :local_port => local_port, | ||
3912 | 190 | :tun_url => tunnel_url, | ||
3913 | 191 | :dst_host => conn_info['hostname'], | ||
3914 | 192 | :dst_port => conn_info['port'], | ||
3915 | 193 | :log_file => STDOUT, | ||
3916 | 194 | :log_level => ENV["VMC_TUNNEL_DEBUG"] || "ERROR", | ||
3917 | 195 | :auth_token => auth, | ||
3918 | 196 | :quiet => true | ||
3919 | 197 | }) | ||
3920 | 198 | end | ||
3921 | 199 | |||
3922 | 200 | at_exit { @local_tunnel_thread.kill } | ||
3923 | 201 | end | ||
3924 | 202 | |||
3925 | 203 | |||
3926 | 204 | |||
3927 | 205 | def pick_tunnel_port(port) | ||
3928 | 206 | original = port | ||
3929 | 207 | |||
3930 | 208 | PORT_RANGE.times do |n| | ||
3931 | 209 | begin | ||
3932 | 210 | TCPSocket.open('localhost', port) | ||
3933 | 211 | port += 1 | ||
3934 | 212 | rescue | ||
3935 | 213 | return port | ||
3936 | 214 | end | ||
3937 | 215 | end | ||
3938 | 216 | |||
3939 | 217 | grab_ephemeral_port | ||
3940 | 218 | end | ||
3941 | 219 | |||
3942 | 220 | def grab_ephemeral_port | ||
3943 | 221 | socket = TCPServer.new('0.0.0.0', 0) | ||
3944 | 222 | socket.setsockopt(Socket::SOL_SOCKET, Socket::SO_REUSEADDR, true) | ||
3945 | 223 | Socket.do_not_reverse_lookup = true | ||
3946 | 224 | port = socket.addr[1] | ||
3947 | 225 | socket.close | ||
3948 | 226 | return port | ||
3949 | 227 | end | ||
3950 | 228 | |||
3951 | 229 | def wait_for_tunnel_start(port) | ||
3952 | 230 | 10.times do |n| | ||
3953 | 231 | begin | ||
3954 | 232 | client = TCPSocket.open('localhost', port) | ||
3955 | 233 | display '' if n > 0 | ||
3956 | 234 | client.close | ||
3957 | 235 | return true | ||
3958 | 236 | rescue => e | ||
3959 | 237 | display "Waiting for local tunnel to become available", false if n == 0 | ||
3960 | 238 | display '.', false | ||
3961 | 239 | sleep 1 | ||
3962 | 240 | end | ||
3963 | 241 | end | ||
3964 | 242 | err "Could not connect to local tunnel." | ||
3965 | 243 | end | ||
3966 | 244 | |||
3967 | 245 | def wait_for_tunnel_end | ||
3968 | 246 | display "Open another shell to run command-line clients or" | ||
3969 | 247 | display "use a UI tool to connect using the displayed information." | ||
3970 | 248 | display "Press Ctrl-C to exit..." | ||
3971 | 249 | @local_tunnel_thread.join | ||
3972 | 250 | end | ||
3973 | 251 | |||
3974 | 252 | def resolve_symbols(str, info, local_port) | ||
3975 | 253 | str.gsub(/\$\{\s*([^\}]+)\s*\}/) do | ||
3976 | 254 | case $1 | ||
3977 | 255 | when "host" | ||
3978 | 256 | # TODO: determine proper host | ||
3979 | 257 | "localhost" | ||
3980 | 258 | when "port" | ||
3981 | 259 | local_port | ||
3982 | 260 | when "user", "username" | ||
3983 | 261 | info["username"] | ||
3984 | 262 | else | ||
3985 | 263 | info[$1] || ask($1) | ||
3986 | 264 | end | ||
3987 | 265 | end | ||
3988 | 266 | end | ||
3989 | 267 | |||
3990 | 268 | def start_local_prog(clients, command, info, port) | ||
3991 | 269 | client = clients[File.basename(command)] | ||
3992 | 270 | |||
3993 | 271 | cmdline = "#{command} " | ||
3994 | 272 | |||
3995 | 273 | case client | ||
3996 | 274 | when Hash | ||
3997 | 275 | cmdline << resolve_symbols(client["command"], info, port) | ||
3998 | 276 | client["environment"].each do |e| | ||
3999 | 277 | if e =~ /([^=]+)=(["']?)([^"']*)\2/ | ||
4000 | 278 | ENV[$1] = resolve_symbols($3, info, port) | ||
4001 | 279 | else | ||
4002 | 280 | err "Invalid environment variable: #{e}" | ||
4003 | 281 | end | ||
4004 | 282 | end | ||
4005 | 283 | when String | ||
4006 | 284 | cmdline << resolve_symbols(client, info, port) | ||
4007 | 285 | else | ||
4008 | 286 | err "Unknown client info: #{client.inspect}." | ||
4009 | 287 | end | ||
4010 | 288 | |||
4011 | 289 | display "Launching '#{cmdline}'" | ||
4012 | 290 | display '' | ||
4013 | 291 | |||
4014 | 292 | system(cmdline) | ||
4015 | 293 | end | ||
4016 | 294 | |||
4017 | 295 | def push_caldecott(token) | ||
4018 | 296 | client.create_app( | ||
4019 | 297 | tunnel_appname, | ||
4020 | 298 | { :name => tunnel_appname, | ||
4021 | 299 | :staging => {:framework => "sinatra"}, | ||
4022 | 300 | :uris => ["#{tunnel_uniquename}.#{target_base}"], | ||
4023 | 301 | :instances => 1, | ||
4024 | 302 | :resources => {:memory => 64}, | ||
4025 | 303 | :env => ["CALDECOTT_AUTH=#{token}"] | ||
4026 | 304 | } | ||
4027 | 305 | ) | ||
4028 | 306 | |||
4029 | 307 | apps_cmd.send(:upload_app_bits, tunnel_appname, HELPER_APP) | ||
4030 | 308 | |||
4031 | 309 | invalidate_tunnel_app_info | ||
4032 | 310 | end | ||
4033 | 311 | |||
4034 | 312 | def stop_caldecott | ||
4035 | 313 | apps_cmd.stop(tunnel_appname) | ||
4036 | 314 | |||
4037 | 315 | invalidate_tunnel_app_info | ||
4038 | 316 | end | ||
4039 | 317 | |||
4040 | 318 | def start_caldecott | ||
4041 | 319 | apps_cmd.start(tunnel_appname) | ||
4042 | 320 | |||
4043 | 321 | invalidate_tunnel_app_info | ||
4044 | 322 | end | ||
4045 | 323 | |||
4046 | 324 | private | ||
4047 | 325 | |||
4048 | 326 | def apps_cmd | ||
4049 | 327 | a = Command::Apps.new(@options) | ||
4050 | 328 | a.client client | ||
4051 | 329 | a | ||
4052 | 330 | end | ||
4053 | 331 | end | ||
4054 | 332 | end | ||
4055 | 0 | 333 | ||
4056 | === modified file 'lib/cli/usage.rb' | |||
4057 | --- lib/cli/usage.rb 2011-06-17 13:38:56 +0000 | |||
4058 | +++ lib/cli/usage.rb 2012-06-13 01:37:18 +0000 | |||
4059 | @@ -38,17 +38,18 @@ | |||
4060 | 38 | push [appname] --url Set the url for the application | 38 | push [appname] --url Set the url for the application |
4061 | 39 | push [appname] --instances <N> Set the expected number <N> of instances | 39 | push [appname] --instances <N> Set the expected number <N> of instances |
4062 | 40 | push [appname] --mem M Set the memory reservation for the application | 40 | push [appname] --mem M Set the memory reservation for the application |
4063 | 41 | push [appname] --runtime RUNTIME Set the runtime to use for the application | ||
4064 | 42 | push [appname] --debug [MODE] Push application and start in a debug mode | ||
4065 | 41 | push [appname] --no-start Do not auto-start the application | 43 | push [appname] --no-start Do not auto-start the application |
4066 | 42 | 44 | ||
4067 | 43 | Application Operations | 45 | Application Operations |
4069 | 44 | start <appname> Start the application | 46 | start <appname> [--debug [MODE]] Start the application |
4070 | 45 | stop <appname> Stop the application | 47 | stop <appname> Stop the application |
4072 | 46 | restart <appname> Restart the application | 48 | restart <appname> [--debug [MODE]] Restart the application |
4073 | 47 | delete <appname> Delete the application | 49 | delete <appname> Delete the application |
4074 | 48 | rename <appname> <newname> Rename the application | ||
4075 | 49 | 50 | ||
4076 | 50 | Application Updates | 51 | Application Updates |
4078 | 51 | update <appname> [--path] Update the application bits | 52 | update <appname> [--path,--debug [MODE]] Update the application bits |
4079 | 52 | mem <appname> [memsize] Update the memory reservation for an application | 53 | mem <appname> [memsize] Update the memory reservation for an application |
4080 | 53 | map <appname> <url> Register the application to the url | 54 | map <appname> <url> Register the application to the url |
4081 | 54 | unmap <appname> <url> Unregister the application from the url | 55 | unmap <appname> <url> Unregister the application from the url |
4082 | @@ -76,6 +77,8 @@ | |||
4083 | 76 | bind-service <servicename> <appname> Bind a service to an application | 77 | bind-service <servicename> <appname> Bind a service to an application |
4084 | 77 | unbind-service <servicename> <appname> Unbind service from the application | 78 | unbind-service <servicename> <appname> Unbind service from the application |
4085 | 78 | clone-services <src-app> <dest-app> Clone service bindings from <src-app> application to <dest-app> | 79 | clone-services <src-app> <dest-app> Clone service bindings from <src-app> application to <dest-app> |
4086 | 80 | tunnel <servicename> [--port] Create a local tunnel to a service | ||
4087 | 81 | tunnel <servicename> <clientcmd> Create a local tunnel to a service and start a local client | ||
4088 | 79 | 82 | ||
4089 | 80 | Administration | 83 | Administration |
4090 | 81 | user Display user account information | 84 | user Display user account information |
4091 | @@ -88,6 +91,15 @@ | |||
4092 | 88 | runtimes Display the supported runtimes of the target system | 91 | runtimes Display the supported runtimes of the target system |
4093 | 89 | frameworks Display the recognized frameworks of the target system | 92 | frameworks Display the recognized frameworks of the target system |
4094 | 90 | 93 | ||
4095 | 94 | Micro Cloud Foundry | ||
4096 | 95 | micro status Display Micro Cloud Foundry VM status | ||
4097 | 96 | micro offline Configure Micro Cloud Foundry VM for offline mode | ||
4098 | 97 | micro online Configure Micro Cloud Foundry VM for online mode | ||
4099 | 98 | [--vmx file] Path to micro.vmx | ||
4100 | 99 | [--vmrun executable] Path to vmrun executable | ||
4101 | 100 | [--password cleartext] Cleartext password for guest VM vcap user | ||
4102 | 101 | [--save] Save cleartext password in ~/.vmc_micro | ||
4103 | 102 | |||
4104 | 91 | Misc | 103 | Misc |
4105 | 92 | aliases List aliases | 104 | aliases List aliases |
4106 | 93 | alias <alias[=]command> Create an alias for a command | 105 | alias <alias[=]command> Create an alias for a command |
4107 | 94 | 106 | ||
4108 | === modified file 'lib/cli/version.rb' | |||
4109 | --- lib/cli/version.rb 2011-06-17 13:38:56 +0000 | |||
4110 | +++ lib/cli/version.rb 2012-06-13 01:37:18 +0000 | |||
4111 | @@ -2,6 +2,6 @@ | |||
4112 | 2 | module Cli | 2 | module Cli |
4113 | 3 | # This version number is used as the RubyGem release version. | 3 | # This version number is used as the RubyGem release version. |
4114 | 4 | # The internal VMC version number is VMC::VERSION. | 4 | # The internal VMC version number is VMC::VERSION. |
4116 | 5 | VERSION = '0.3.10' | 5 | VERSION = '0.3.18' |
4117 | 6 | end | 6 | end |
4118 | 7 | end | 7 | end |
4119 | 8 | 8 | ||
4120 | === modified file 'lib/cli/zip_util.rb' | |||
4121 | --- lib/cli/zip_util.rb 2011-06-17 13:38:56 +0000 | |||
4122 | +++ lib/cli/zip_util.rb 2012-06-13 01:37:18 +0000 | |||
4123 | @@ -10,7 +10,7 @@ | |||
4124 | 10 | class << self | 10 | class << self |
4125 | 11 | 11 | ||
4126 | 12 | def to_dev_null | 12 | def to_dev_null |
4128 | 13 | if !!RUBY_PLATFORM['mingw'] || !!RUBY_PLATFORM['mswin32'] || !!RUBY_PLATFORM['cygwin'] | 13 | if WINDOWS |
4129 | 14 | 'nul' | 14 | 'nul' |
4130 | 15 | else | 15 | else |
4131 | 16 | '/dev/null' | 16 | '/dev/null' |
4132 | 17 | 17 | ||
4133 | === modified file 'lib/vmc/client.rb' | |||
4134 | --- lib/vmc/client.rb 2011-06-17 13:38:56 +0000 | |||
4135 | +++ lib/vmc/client.rb 2012-06-13 01:37:18 +0000 | |||
4136 | @@ -11,6 +11,7 @@ | |||
4137 | 11 | 11 | ||
4138 | 12 | require 'rubygems' | 12 | require 'rubygems' |
4139 | 13 | require 'json/pure' | 13 | require 'json/pure' |
4140 | 14 | require 'open-uri' | ||
4141 | 14 | 15 | ||
4142 | 15 | require File.expand_path('../const', __FILE__) | 16 | require File.expand_path('../const', __FILE__) |
4143 | 16 | 17 | ||
4144 | @@ -24,7 +25,7 @@ | |||
4145 | 24 | attr_accessor :trace | 25 | attr_accessor :trace |
4146 | 25 | 26 | ||
4147 | 26 | # Error codes | 27 | # Error codes |
4149 | 27 | VMC_HTTP_ERROR_CODES = [ 400, 403, 404, 500 ] | 28 | VMC_HTTP_ERROR_CODES = [ 400, 500 ] |
4150 | 28 | 29 | ||
4151 | 29 | # Errors | 30 | # Errors |
4152 | 30 | class BadTarget < RuntimeError; end | 31 | class BadTarget < RuntimeError; end |
4153 | @@ -38,7 +39,7 @@ | |||
4154 | 38 | def initialize(target_url=VMC::DEFAULT_TARGET, auth_token=nil) | 39 | def initialize(target_url=VMC::DEFAULT_TARGET, auth_token=nil) |
4155 | 39 | target_url = "http://#{target_url}" unless /^https?/ =~ target_url | 40 | target_url = "http://#{target_url}" unless /^https?/ =~ target_url |
4156 | 40 | target_url = target_url.gsub(/\/+$/, '') | 41 | target_url = target_url.gsub(/\/+$/, '') |
4158 | 41 | @target = target_url | 42 | @target = target_url |
4159 | 42 | @auth_token = auth_token | 43 | @auth_token = auth_token |
4160 | 43 | end | 44 | end |
4161 | 44 | 45 | ||
4162 | @@ -59,7 +60,11 @@ | |||
4163 | 59 | # Global listing of services that are available on the target system | 60 | # Global listing of services that are available on the target system |
4164 | 60 | def services_info | 61 | def services_info |
4165 | 61 | check_login_status | 62 | check_login_status |
4167 | 62 | json_get(VMC::GLOBAL_SERVICES_PATH) | 63 | json_get(path(VMC::GLOBAL_SERVICES_PATH)) |
4168 | 64 | end | ||
4169 | 65 | |||
4170 | 66 | def runtimes_info | ||
4171 | 67 | json_get(path(VMC::GLOBAL_RUNTIMES_PATH)) | ||
4172 | 63 | end | 68 | end |
4173 | 64 | 69 | ||
4174 | 65 | ###################################################### | 70 | ###################################################### |
4175 | @@ -81,7 +86,7 @@ | |||
4176 | 81 | 86 | ||
4177 | 82 | def update_app(name, manifest) | 87 | def update_app(name, manifest) |
4178 | 83 | check_login_status | 88 | check_login_status |
4180 | 84 | json_put("#{VMC::APPS_PATH}/#{name}", manifest) | 89 | json_put(path(VMC::APPS_PATH, name), manifest) |
4181 | 85 | end | 90 | end |
4182 | 86 | 91 | ||
4183 | 87 | def upload_app(name, zipfile, resource_manifest=nil) | 92 | def upload_app(name, zipfile, resource_manifest=nil) |
4184 | @@ -98,27 +103,29 @@ | |||
4185 | 98 | upload_data[:application] = file | 103 | upload_data[:application] = file |
4186 | 99 | end | 104 | end |
4187 | 100 | upload_data[:resources] = resource_manifest.to_json if resource_manifest | 105 | upload_data[:resources] = resource_manifest.to_json if resource_manifest |
4189 | 101 | http_post("#{VMC::APPS_PATH}/#{name}/application", upload_data) | 106 | http_post(path(VMC::APPS_PATH, name, "application"), upload_data) |
4190 | 107 | rescue RestClient::ServerBrokeConnection | ||
4191 | 108 | retry | ||
4192 | 102 | end | 109 | end |
4193 | 103 | 110 | ||
4194 | 104 | def delete_app(name) | 111 | def delete_app(name) |
4195 | 105 | check_login_status | 112 | check_login_status |
4197 | 106 | http_delete("#{VMC::APPS_PATH}/#{name}") | 113 | http_delete(path(VMC::APPS_PATH, name)) |
4198 | 107 | end | 114 | end |
4199 | 108 | 115 | ||
4200 | 109 | def app_info(name) | 116 | def app_info(name) |
4201 | 110 | check_login_status | 117 | check_login_status |
4203 | 111 | json_get("#{VMC::APPS_PATH}/#{name}") | 118 | json_get(path(VMC::APPS_PATH, name)) |
4204 | 112 | end | 119 | end |
4205 | 113 | 120 | ||
4206 | 114 | def app_update_info(name) | 121 | def app_update_info(name) |
4207 | 115 | check_login_status | 122 | check_login_status |
4209 | 116 | json_get("#{VMC::APPS_PATH}/#{name}/update") | 123 | json_get(path(VMC::APPS_PATH, name, "update")) |
4210 | 117 | end | 124 | end |
4211 | 118 | 125 | ||
4212 | 119 | def app_stats(name) | 126 | def app_stats(name) |
4213 | 120 | check_login_status | 127 | check_login_status |
4215 | 121 | stats_raw = json_get("#{VMC::APPS_PATH}/#{name}/stats") | 128 | stats_raw = json_get(path(VMC::APPS_PATH, name, "stats")) |
4216 | 122 | stats = [] | 129 | stats = [] |
4217 | 123 | stats_raw.each_pair do |k, entry| | 130 | stats_raw.each_pair do |k, entry| |
4218 | 124 | # Skip entries with no stats | 131 | # Skip entries with no stats |
4219 | @@ -132,20 +139,20 @@ | |||
4220 | 132 | 139 | ||
4221 | 133 | def app_instances(name) | 140 | def app_instances(name) |
4222 | 134 | check_login_status | 141 | check_login_status |
4224 | 135 | json_get("#{VMC::APPS_PATH}/#{name}/instances") | 142 | json_get(path(VMC::APPS_PATH, name, "instances")) |
4225 | 136 | end | 143 | end |
4226 | 137 | 144 | ||
4227 | 138 | def app_crashes(name) | 145 | def app_crashes(name) |
4228 | 139 | check_login_status | 146 | check_login_status |
4230 | 140 | json_get("#{VMC::APPS_PATH}/#{name}/crashes") | 147 | json_get(path(VMC::APPS_PATH, name, "crashes")) |
4231 | 141 | end | 148 | end |
4232 | 142 | 149 | ||
4233 | 143 | # List the directory or download the actual file indicated by | 150 | # List the directory or download the actual file indicated by |
4234 | 144 | # the path. | 151 | # the path. |
4236 | 145 | def app_files(name, path, instance=0) | 152 | def app_files(name, path, instance='0') |
4237 | 146 | check_login_status | 153 | check_login_status |
4240 | 147 | url = "#{VMC::APPS_PATH}/#{name}/instances/#{instance}/files/#{path}" | 154 | path = path.gsub('//', '/') |
4241 | 148 | url.gsub!('//', '/') | 155 | url = path(VMC::APPS_PATH, name, "instances", instance, "files", path) |
4242 | 149 | _, body, headers = http_get(url) | 156 | _, body, headers = http_get(url) |
4243 | 150 | body | 157 | body |
4244 | 151 | end | 158 | end |
4245 | @@ -185,7 +192,7 @@ | |||
4246 | 185 | 192 | ||
4247 | 186 | raise TargetError, "Service [#{service}] is not a valid service choice" unless service_hash | 193 | raise TargetError, "Service [#{service}] is not a valid service choice" unless service_hash |
4248 | 187 | service_hash[:name] = name | 194 | service_hash[:name] = name |
4250 | 188 | json_post(VMC::SERVICES_PATH, service_hash) | 195 | json_post(path(VMC::SERVICES_PATH), service_hash) |
4251 | 189 | end | 196 | end |
4252 | 190 | 197 | ||
4253 | 191 | def delete_service(name) | 198 | def delete_service(name) |
4254 | @@ -193,7 +200,7 @@ | |||
4255 | 193 | svcs = services || [] | 200 | svcs = services || [] |
4256 | 194 | names = svcs.collect { |s| s[:name] } | 201 | names = svcs.collect { |s| s[:name] } |
4257 | 195 | raise TargetError, "Service [#{name}] not a valid service" unless names.include? name | 202 | raise TargetError, "Service [#{name}] not a valid service" unless names.include? name |
4259 | 196 | http_delete("#{VMC::SERVICES_PATH}/#{name}") | 203 | http_delete(path(VMC::SERVICES_PATH, name)) |
4260 | 197 | end | 204 | end |
4261 | 198 | 205 | ||
4262 | 199 | def bind_service(service, appname) | 206 | def bind_service(service, appname) |
4263 | @@ -263,7 +270,7 @@ | |||
4264 | 263 | # Auth token can be retained and used in creating | 270 | # Auth token can be retained and used in creating |
4265 | 264 | # new clients, avoiding login. | 271 | # new clients, avoiding login. |
4266 | 265 | def login(user, password) | 272 | def login(user, password) |
4268 | 266 | status, body, headers = json_post("#{VMC::USERS_PATH}/#{user}/tokens", {:password => password}) | 273 | status, body, headers = json_post(path(VMC::USERS_PATH, user, "tokens"), {:password => password}) |
4269 | 267 | response_info = json_parse(body) | 274 | response_info = json_parse(body) |
4270 | 268 | if response_info | 275 | if response_info |
4271 | 269 | @user = user | 276 | @user = user |
4272 | @@ -274,10 +281,10 @@ | |||
4273 | 274 | # sets the password for the current logged user | 281 | # sets the password for the current logged user |
4274 | 275 | def change_password(new_password) | 282 | def change_password(new_password) |
4275 | 276 | check_login_status | 283 | check_login_status |
4277 | 277 | user_info = json_get("#{VMC::USERS_PATH}/#{@user}") | 284 | user_info = json_get(path(VMC::USERS_PATH, @user)) |
4278 | 278 | if user_info | 285 | if user_info |
4279 | 279 | user_info[:password] = new_password | 286 | user_info[:password] = new_password |
4281 | 280 | json_put("#{VMC::USERS_PATH}/#{@user}", user_info) | 287 | json_put(path(VMC::USERS_PATH, @user), user_info) |
4282 | 281 | end | 288 | end |
4283 | 282 | end | 289 | end |
4284 | 283 | 290 | ||
4285 | @@ -293,18 +300,34 @@ | |||
4286 | 293 | @proxy = proxy | 300 | @proxy = proxy |
4287 | 294 | end | 301 | end |
4288 | 295 | 302 | ||
4289 | 303 | def users | ||
4290 | 304 | check_login_status | ||
4291 | 305 | json_get(VMC::USERS_PATH) | ||
4292 | 306 | end | ||
4293 | 307 | |||
4294 | 296 | def add_user(user_email, password) | 308 | def add_user(user_email, password) |
4295 | 297 | json_post(VMC::USERS_PATH, { :email => user_email, :password => password }) | 309 | json_post(VMC::USERS_PATH, { :email => user_email, :password => password }) |
4296 | 298 | end | 310 | end |
4297 | 299 | 311 | ||
4298 | 300 | def delete_user(user_email) | 312 | def delete_user(user_email) |
4300 | 301 | http_delete("#{VMC::USERS_PATH}/#{user_email}") | 313 | check_login_status |
4301 | 314 | http_delete(path(VMC::USERS_PATH, user_email)) | ||
4302 | 302 | end | 315 | end |
4303 | 303 | 316 | ||
4304 | 304 | ###################################################### | 317 | ###################################################### |
4305 | 305 | 318 | ||
4306 | 319 | def self.path(*path) | ||
4307 | 320 | path.flatten.collect { |x| | ||
4308 | 321 | URI.encode x.to_s, Regexp.new("[^#{URI::PATTERN::UNRESERVED}]") | ||
4309 | 322 | }.join("/") | ||
4310 | 323 | end | ||
4311 | 324 | |||
4312 | 306 | private | 325 | private |
4313 | 307 | 326 | ||
4314 | 327 | def path(*args, &blk) | ||
4315 | 328 | self.class.path(*args, &blk) | ||
4316 | 329 | end | ||
4317 | 330 | |||
4318 | 308 | def json_get(url) | 331 | def json_get(url) |
4319 | 309 | status, body, headers = http_get(url, 'application/json') | 332 | status, body, headers = http_get(url, 'application/json') |
4320 | 310 | json_parse(body) | 333 | json_parse(body) |
4321 | @@ -357,12 +380,12 @@ | |||
4322 | 357 | end | 380 | end |
4323 | 358 | 381 | ||
4324 | 359 | req = { | 382 | req = { |
4327 | 360 | :method => method, :url => "#{@target}#{path}", | 383 | :method => method, :url => "#{@target}/#{path}", |
4328 | 361 | :payload => payload, :headers => headers | 384 | :payload => payload, :headers => headers, :multipart => true |
4329 | 362 | } | 385 | } |
4330 | 363 | status, body, response_headers = perform_http_request(req) | 386 | status, body, response_headers = perform_http_request(req) |
4331 | 364 | 387 | ||
4333 | 365 | if VMC_HTTP_ERROR_CODES.include?(status) | 388 | if request_failed?(status) |
4334 | 366 | # FIXME, old cc returned 400 on not found for file access | 389 | # FIXME, old cc returned 400 on not found for file access |
4335 | 367 | err = (status == 404 || status == 400) ? NotFound : TargetError | 390 | err = (status == 404 || status == 400) ? NotFound : TargetError |
4336 | 368 | raise err, parse_error_message(status, body) | 391 | raise err, parse_error_message(status, body) |
4337 | @@ -373,8 +396,13 @@ | |||
4338 | 373 | raise BadTarget, "Cannot access target (%s)" % [ e.message ] | 396 | raise BadTarget, "Cannot access target (%s)" % [ e.message ] |
4339 | 374 | end | 397 | end |
4340 | 375 | 398 | ||
4341 | 399 | def request_failed?(status) | ||
4342 | 400 | VMC_HTTP_ERROR_CODES.detect{|error_code| status >= error_code} | ||
4343 | 401 | end | ||
4344 | 402 | |||
4345 | 376 | def perform_http_request(req) | 403 | def perform_http_request(req) |
4347 | 377 | RestClient.proxy = ENV['https_proxy'] || ENV['http_proxy'] | 404 | proxy_uri = URI.parse(req[:url]).find_proxy() |
4348 | 405 | RestClient.proxy = proxy_uri.to_s if proxy_uri | ||
4349 | 378 | 406 | ||
4350 | 379 | # Setup tracing if needed | 407 | # Setup tracing if needed |
4351 | 380 | unless trace.nil? | 408 | unless trace.nil? |
4352 | @@ -388,9 +416,17 @@ | |||
4353 | 388 | puts '>>>' | 416 | puts '>>>' |
4354 | 389 | puts "PROXY: #{RestClient.proxy}" if RestClient.proxy | 417 | puts "PROXY: #{RestClient.proxy}" if RestClient.proxy |
4355 | 390 | puts "REQUEST: #{req[:method]} #{req[:url]}" | 418 | puts "REQUEST: #{req[:method]} #{req[:url]}" |
4357 | 391 | puts "RESPONSE_HEADERS: #{response.headers}" | 419 | puts "RESPONSE_HEADERS:" |
4358 | 420 | response.headers.each do |key, value| | ||
4359 | 421 | puts " #{key} : #{value}" | ||
4360 | 422 | end | ||
4361 | 392 | puts "REQUEST_BODY: #{req[:payload]}" if req[:payload] | 423 | puts "REQUEST_BODY: #{req[:payload]}" if req[:payload] |
4363 | 393 | puts "RESPONSE: [#{response.code}] #{response.body}" | 424 | puts "RESPONSE: [#{response.code}]" |
4364 | 425 | begin | ||
4365 | 426 | puts JSON.pretty_generate(JSON.parse(response.body)) | ||
4366 | 427 | rescue | ||
4367 | 428 | puts "#{response.body}" | ||
4368 | 429 | end | ||
4369 | 394 | puts '<<<' | 430 | puts '<<<' |
4370 | 395 | end | 431 | end |
4371 | 396 | end | 432 | end |
4372 | 397 | 433 | ||
4373 | === modified file 'lib/vmc/const.rb' | |||
4374 | --- lib/vmc/const.rb 2011-06-17 13:38:56 +0000 | |||
4375 | +++ lib/vmc/const.rb 2012-06-13 01:37:18 +0000 | |||
4376 | @@ -5,17 +5,18 @@ | |||
4377 | 5 | VERSION = '0.3.2' | 5 | VERSION = '0.3.2' |
4378 | 6 | 6 | ||
4379 | 7 | # Targets | 7 | # Targets |
4381 | 8 | DEFAULT_TARGET = 'http://api.cloudfoundry.com' | 8 | DEFAULT_TARGET = 'https://api.cloudfoundry.com' |
4382 | 9 | DEFAULT_LOCAL_TARGET = 'http://api.vcap.me' | 9 | DEFAULT_LOCAL_TARGET = 'http://api.vcap.me' |
4383 | 10 | 10 | ||
4384 | 11 | # General Paths | 11 | # General Paths |
4388 | 12 | INFO_PATH = '/info' | 12 | INFO_PATH = 'info' |
4389 | 13 | GLOBAL_SERVICES_PATH = '/info/services' | 13 | GLOBAL_SERVICES_PATH = ['info', 'services'] |
4390 | 14 | RESOURCES_PATH = '/resources' | 14 | GLOBAL_RUNTIMES_PATH = ['info', 'runtimes'] |
4391 | 15 | RESOURCES_PATH = 'resources' | ||
4392 | 15 | 16 | ||
4393 | 16 | # User specific paths | 17 | # User specific paths |
4397 | 17 | APPS_PATH = '/apps' | 18 | APPS_PATH = 'apps' |
4398 | 18 | SERVICES_PATH = '/services' | 19 | SERVICES_PATH = 'services' |
4399 | 19 | USERS_PATH = '/users' | 20 | USERS_PATH = 'users' |
4400 | 20 | 21 | ||
4401 | 21 | end | 22 | end |
4402 | 22 | 23 | ||
4403 | === added directory 'lib/vmc/micro' | |||
4404 | === added file 'lib/vmc/micro.rb' | |||
4405 | --- lib/vmc/micro.rb 1970-01-01 00:00:00 +0000 | |||
4406 | +++ lib/vmc/micro.rb 2012-06-13 01:37:18 +0000 | |||
4407 | @@ -0,0 +1,56 @@ | |||
4408 | 1 | require 'find' | ||
4409 | 2 | |||
4410 | 3 | module VMC::Micro | ||
4411 | 4 | def config_file(file) | ||
4412 | 5 | File.join(File.dirname(__FILE__), '..', '..', 'config', 'micro', file) | ||
4413 | 6 | end | ||
4414 | 7 | |||
4415 | 8 | def escape_path(path) | ||
4416 | 9 | path = File.expand_path(path) | ||
4417 | 10 | if RUBY_PLATFORM =~ /mingw|mswin32|cygwin/ | ||
4418 | 11 | if path.include?(' ') | ||
4419 | 12 | return '"' + path + '"' | ||
4420 | 13 | else | ||
4421 | 14 | return path | ||
4422 | 15 | end | ||
4423 | 16 | else | ||
4424 | 17 | return path.gsub(' ', '\ ') | ||
4425 | 18 | end | ||
4426 | 19 | end | ||
4427 | 20 | |||
4428 | 21 | def locate_file(file, directory, search_paths) | ||
4429 | 22 | search_paths.each do |path| | ||
4430 | 23 | expanded_path = File.expand_path(path) | ||
4431 | 24 | if File.exists?(expanded_path) | ||
4432 | 25 | Find.find(expanded_path) do |current| | ||
4433 | 26 | if File.directory?(current) && current.include?(directory) | ||
4434 | 27 | full_path = File.join(current, file) | ||
4435 | 28 | return self.escape_path(full_path) if File.exists?(full_path) | ||
4436 | 29 | end | ||
4437 | 30 | end | ||
4438 | 31 | end | ||
4439 | 32 | end | ||
4440 | 33 | |||
4441 | 34 | false | ||
4442 | 35 | end | ||
4443 | 36 | |||
4444 | 37 | def run_command(command, args=nil) | ||
4445 | 38 | # TODO switch to using posix-spawn instead | ||
4446 | 39 | result = %x{#{command} #{args} 2>&1} | ||
4447 | 40 | unless $?.exitstatus == 0 | ||
4448 | 41 | if block_given? | ||
4449 | 42 | yield | ||
4450 | 43 | else | ||
4451 | 44 | raise "failed to execute #{command} #{args}:\n#{result}" | ||
4452 | 45 | end | ||
4453 | 46 | else | ||
4454 | 47 | result.split(/\n/) | ||
4455 | 48 | end | ||
4456 | 49 | end | ||
4457 | 50 | |||
4458 | 51 | module_function :config_file | ||
4459 | 52 | module_function :escape_path | ||
4460 | 53 | module_function :locate_file | ||
4461 | 54 | module_function :run_command | ||
4462 | 55 | |||
4463 | 56 | end | ||
4464 | 0 | 57 | ||
4465 | === added directory 'lib/vmc/micro/switcher' | |||
4466 | === added file 'lib/vmc/micro/switcher/base.rb' | |||
4467 | --- lib/vmc/micro/switcher/base.rb 1970-01-01 00:00:00 +0000 | |||
4468 | +++ lib/vmc/micro/switcher/base.rb 2012-06-13 01:37:18 +0000 | |||
4469 | @@ -0,0 +1,97 @@ | |||
4470 | 1 | require 'interact' | ||
4471 | 2 | |||
4472 | 3 | module VMC::Micro::Switcher | ||
4473 | 4 | class Base | ||
4474 | 5 | include Interactive | ||
4475 | 6 | |||
4476 | 7 | def initialize(config) | ||
4477 | 8 | @config = config | ||
4478 | 9 | |||
4479 | 10 | @vmrun = VMC::Micro::VMrun.new(config) | ||
4480 | 11 | unless @vmrun.running? | ||
4481 | 12 | if ask("Micro Cloud Foundry VM is not running. Do you want to start it?", :choices => ['y', 'n']) == 'y' | ||
4482 | 13 | display "Starting Micro Cloud Foundry VM: ", false | ||
4483 | 14 | @vmrun.start | ||
4484 | 15 | say "done".green | ||
4485 | 16 | else | ||
4486 | 17 | err "Micro Cloud Foundry VM needs to be running." | ||
4487 | 18 | end | ||
4488 | 19 | end | ||
4489 | 20 | |||
4490 | 21 | err "Micro Cloud Foundry VM initial setup needs to be completed before using 'vmc micro'" unless @vmrun.ready? | ||
4491 | 22 | end | ||
4492 | 23 | |||
4493 | 24 | def offline | ||
4494 | 25 | unless @vmrun.offline? | ||
4495 | 26 | # save online connection type so we can restore it later | ||
4496 | 27 | @config['online_connection_type'] = @vmrun.connection_type | ||
4497 | 28 | |||
4498 | 29 | if (@config['online_connection_type'] != 'nat') | ||
4499 | 30 | if ask("Reconfigure Micro Cloud Foundry VM network to nat mode and reboot?", :choices => ['y', 'n']) == 'y' | ||
4500 | 31 | display "Rebooting Micro Cloud Foundry VM: ", false | ||
4501 | 32 | @vmrun.connection_type = 'nat' | ||
4502 | 33 | @vmrun.reset | ||
4503 | 34 | say "done".green | ||
4504 | 35 | else | ||
4505 | 36 | err "Aborted" | ||
4506 | 37 | end | ||
4507 | 38 | end | ||
4508 | 39 | |||
4509 | 40 | display "Setting Micro Cloud Foundry VM to offline mode: ", false | ||
4510 | 41 | @vmrun.offline! | ||
4511 | 42 | say "done".green | ||
4512 | 43 | display "Setting host DNS server: ", false | ||
4513 | 44 | |||
4514 | 45 | @config['domain'] = @vmrun.domain | ||
4515 | 46 | @config['ip'] = @vmrun.ip | ||
4516 | 47 | set_nameserver(@config['domain'], @config['ip']) | ||
4517 | 48 | say "done".green | ||
4518 | 49 | else | ||
4519 | 50 | say "Micro Cloud Foundry VM already in offline mode".yellow | ||
4520 | 51 | end | ||
4521 | 52 | end | ||
4522 | 53 | |||
4523 | 54 | def online | ||
4524 | 55 | if @vmrun.offline? | ||
4525 | 56 | current_connection_type = @vmrun.connection_type | ||
4526 | 57 | @config['online_connection_type'] ||= current_connection_type | ||
4527 | 58 | |||
4528 | 59 | if (@config['online_connection_type'] != current_connection_type) | ||
4529 | 60 | # TODO handle missing connection type in saved config | ||
4530 | 61 | question = "Reconfigure Micro Cloud Foundry VM network to #{@config['online_connection_type']} mode and reboot?" | ||
4531 | 62 | if ask(question, :choices => ['y', 'n']) == 'y' | ||
4532 | 63 | display "Rebooting Micro Cloud Foundry VM: ", false | ||
4533 | 64 | @vmrun.connection_type = @config['online_connection_type'] | ||
4534 | 65 | @vmrun.reset | ||
4535 | 66 | say "done".green | ||
4536 | 67 | else | ||
4537 | 68 | err "Aborted" | ||
4538 | 69 | end | ||
4539 | 70 | end | ||
4540 | 71 | |||
4541 | 72 | display "Unsetting host DNS server: ", false | ||
4542 | 73 | # TODO handle missing domain and ip in saved config (look at the VM) | ||
4543 | 74 | @config['domain'] ||= @vmrun.domain | ||
4544 | 75 | @config['ip'] ||= @vmrun.ip | ||
4545 | 76 | unset_nameserver(@config['domain'], @config['ip']) | ||
4546 | 77 | say "done".green | ||
4547 | 78 | |||
4548 | 79 | display "Setting Micro Cloud Foundry VM to online mode: ", false | ||
4549 | 80 | @vmrun.online! | ||
4550 | 81 | say "done".green | ||
4551 | 82 | else | ||
4552 | 83 | say "Micro Cloud Foundry already in online mode".yellow | ||
4553 | 84 | end | ||
4554 | 85 | end | ||
4555 | 86 | |||
4556 | 87 | def status | ||
4557 | 88 | mode = @vmrun.offline? ? 'offline' : 'online' | ||
4558 | 89 | say "Micro Cloud Foundry VM currently in #{mode.green} mode" | ||
4559 | 90 | # should the VMX path be unescaped? | ||
4560 | 91 | say "VMX Path: #{@vmrun.vmx}" | ||
4561 | 92 | say "Domain: #{@vmrun.domain.green}" | ||
4562 | 93 | say "IP Address: #{@vmrun.ip.green}" | ||
4563 | 94 | end | ||
4564 | 95 | end | ||
4565 | 96 | |||
4566 | 97 | end | ||
4567 | 0 | 98 | ||
4568 | === added file 'lib/vmc/micro/switcher/darwin.rb' | |||
4569 | --- lib/vmc/micro/switcher/darwin.rb 1970-01-01 00:00:00 +0000 | |||
4570 | +++ lib/vmc/micro/switcher/darwin.rb 2012-06-13 01:37:18 +0000 | |||
4571 | @@ -0,0 +1,19 @@ | |||
4572 | 1 | module VMC::Micro::Switcher | ||
4573 | 2 | |||
4574 | 3 | class Darwin < Base | ||
4575 | 4 | def adminrun(command) | ||
4576 | 5 | VMC::Micro.run_command("osascript", "-e 'do shell script \"#{command}\" with administrator privileges'") | ||
4577 | 6 | end | ||
4578 | 7 | |||
4579 | 8 | def set_nameserver(domain, ip) | ||
4580 | 9 | File.open("/tmp/#{domain}", 'w') { |file| file.write("nameserver #{ip}") } | ||
4581 | 10 | adminrun("mkdir -p /etc/resolver;mv /tmp/#{domain} /etc/resolver/") | ||
4582 | 11 | end | ||
4583 | 12 | |||
4584 | 13 | def unset_nameserver(domain, ip) | ||
4585 | 14 | err "domain missing" unless domain | ||
4586 | 15 | adminrun("rm -f /etc/resolver/#{domain}") | ||
4587 | 16 | end | ||
4588 | 17 | end | ||
4589 | 18 | |||
4590 | 19 | end | ||
4591 | 0 | 20 | ||
4592 | === added file 'lib/vmc/micro/switcher/dummy.rb' | |||
4593 | --- lib/vmc/micro/switcher/dummy.rb 1970-01-01 00:00:00 +0000 | |||
4594 | +++ lib/vmc/micro/switcher/dummy.rb 2012-06-13 01:37:18 +0000 | |||
4595 | @@ -0,0 +1,15 @@ | |||
4596 | 1 | # only used for testing | ||
4597 | 2 | module VMC::Micro::Switcher | ||
4598 | 3 | |||
4599 | 4 | class Dummy < Base | ||
4600 | 5 | def adminrun(command) | ||
4601 | 6 | end | ||
4602 | 7 | |||
4603 | 8 | def set_nameserver(domain, ip) | ||
4604 | 9 | end | ||
4605 | 10 | |||
4606 | 11 | def unset_nameserver(domain, ip) | ||
4607 | 12 | end | ||
4608 | 13 | end | ||
4609 | 14 | |||
4610 | 15 | end | ||
4611 | 0 | 16 | ||
4612 | === added file 'lib/vmc/micro/switcher/linux.rb' | |||
4613 | --- lib/vmc/micro/switcher/linux.rb 1970-01-01 00:00:00 +0000 | |||
4614 | +++ lib/vmc/micro/switcher/linux.rb 2012-06-13 01:37:18 +0000 | |||
4615 | @@ -0,0 +1,16 @@ | |||
4616 | 1 | module VMC::Micro::Switcher | ||
4617 | 2 | |||
4618 | 3 | class Linux < Base | ||
4619 | 4 | def set_nameserver(domain, ip) | ||
4620 | 5 | VMC::Micro.run_command("sudo", "sed -i'.backup' '1 i nameserver #{ip}' /etc/resolv.conf") | ||
4621 | 6 | # lock resolv.conf so Network Manager doesn't clear out the file when offline | ||
4622 | 7 | VMC::Micro.run_command("sudo", "chattr +i /etc/resolv.conf") | ||
4623 | 8 | end | ||
4624 | 9 | |||
4625 | 10 | def unset_nameserver(domain, ip) | ||
4626 | 11 | VMC::Micro.run_command("sudo", "chattr -i /etc/resolv.conf") | ||
4627 | 12 | VMC::Micro.run_command("sudo", "sed -i'.backup' '/#{ip}/d' /etc/resolv.conf") | ||
4628 | 13 | end | ||
4629 | 14 | end | ||
4630 | 15 | |||
4631 | 16 | end | ||
4632 | 0 | 17 | ||
4633 | === added file 'lib/vmc/micro/switcher/windows.rb' | |||
4634 | --- lib/vmc/micro/switcher/windows.rb 1970-01-01 00:00:00 +0000 | |||
4635 | +++ lib/vmc/micro/switcher/windows.rb 2012-06-13 01:37:18 +0000 | |||
4636 | @@ -0,0 +1,31 @@ | |||
4637 | 1 | module VMC::Micro::Switcher | ||
4638 | 2 | |||
4639 | 3 | class Windows < Base | ||
4640 | 4 | def version? | ||
4641 | 5 | VMC::Micro.run_command("cmd", "/c ver").to_s.scan(/\d+\.\d+/).first.to_f | ||
4642 | 6 | end | ||
4643 | 7 | |||
4644 | 8 | def adminrun(command, args=nil) | ||
4645 | 9 | if version? > 5.2 | ||
4646 | 10 | require 'win32ole' | ||
4647 | 11 | shell = WIN32OLE.new("Shell.Application") | ||
4648 | 12 | shell.ShellExecute(command, args, nil, "runas", 0) | ||
4649 | 13 | else | ||
4650 | 14 | # on older version this will try to run the command, and if you don't have | ||
4651 | 15 | # admin privilges it will tell you so and exit | ||
4652 | 16 | VMC::Micro.run_command(command, args) | ||
4653 | 17 | end | ||
4654 | 18 | end | ||
4655 | 19 | |||
4656 | 20 | # TODO better method to figure out the interface name is to get the NAT ip and find the | ||
4657 | 21 | # interface with the correct subnet | ||
4658 | 22 | def set_nameserver(domain, ip) | ||
4659 | 23 | adminrun("netsh", "interface ip set dns \"VMware Network Adapter VMnet8\" static #{ip}") | ||
4660 | 24 | end | ||
4661 | 25 | |||
4662 | 26 | def unset_nameserver(domain, ip) | ||
4663 | 27 | adminrun("netsh", "interface ip set dns \"VMware Network Adapter VMnet8\" static none") | ||
4664 | 28 | end | ||
4665 | 29 | end | ||
4666 | 30 | |||
4667 | 31 | end | ||
4668 | 0 | 32 | ||
4669 | === added file 'lib/vmc/micro/vmrun.rb' | |||
4670 | --- lib/vmc/micro/vmrun.rb 1970-01-01 00:00:00 +0000 | |||
4671 | +++ lib/vmc/micro/vmrun.rb 2012-06-13 01:37:18 +0000 | |||
4672 | @@ -0,0 +1,158 @@ | |||
4673 | 1 | module VMC::Micro | ||
4674 | 2 | class VMrun | ||
4675 | 3 | attr_reader :vmx, :vmrun | ||
4676 | 4 | |||
4677 | 5 | def initialize(config) | ||
4678 | 6 | @platform = config['platform'] | ||
4679 | 7 | @user = 'root' # must use root as we muck around with system settings | ||
4680 | 8 | @password = config['password'] | ||
4681 | 9 | @vmrun = config['vmrun'] | ||
4682 | 10 | @vmx = config['vmx'] | ||
4683 | 11 | |||
4684 | 12 | # TODO honor TMPDIR | ||
4685 | 13 | if @platform == :windows | ||
4686 | 14 | @temp_dir = ENV['temp'] | ||
4687 | 15 | else | ||
4688 | 16 | @temp_dir = '/tmp' | ||
4689 | 17 | end | ||
4690 | 18 | end | ||
4691 | 19 | |||
4692 | 20 | def connection_type | ||
4693 | 21 | read_variable('ethernet0.connectionType') | ||
4694 | 22 | end | ||
4695 | 23 | |||
4696 | 24 | def connection_type=(type) | ||
4697 | 25 | write_variable("ethernet0.connectionType", type) | ||
4698 | 26 | end | ||
4699 | 27 | |||
4700 | 28 | def nat? | ||
4701 | 29 | connection_type == "nat" | ||
4702 | 30 | end | ||
4703 | 31 | |||
4704 | 32 | def bridged? | ||
4705 | 33 | connection_type == "bridged" | ||
4706 | 34 | end | ||
4707 | 35 | |||
4708 | 36 | def domain | ||
4709 | 37 | # switch to Dir.mktmpdir | ||
4710 | 38 | state_config = VMC::Micro.escape_path(File.join(@temp_dir, 'state.yml')) | ||
4711 | 39 | run('CopyFileFromGuestToHost', "/var/vcap/bosh/state.yml #{state_config}") | ||
4712 | 40 | bosh_config = YAML.load_file(state_config) | ||
4713 | 41 | bosh_config['properties']['domain'] | ||
4714 | 42 | end | ||
4715 | 43 | |||
4716 | 44 | def ip | ||
4717 | 45 | # switch to Dir.mktmpdir | ||
4718 | 46 | path = VMC::Micro.escape_path(VMC::Micro.config_file('refresh_ip.rb')) | ||
4719 | 47 | ip_file = VMC::Micro.escape_path(File.join(@temp_dir, 'ip.txt')) | ||
4720 | 48 | run('CopyFileFromHostToGuest', "#{path} /tmp/refresh_ip.rb") | ||
4721 | 49 | run('runProgramInGuest', '/tmp/refresh_ip.rb') | ||
4722 | 50 | run('CopyFileFromGuestToHost', "/tmp/ip.txt #{ip_file}") | ||
4723 | 51 | File.open(ip_file, 'r') { |file| file.read } | ||
4724 | 52 | end | ||
4725 | 53 | |||
4726 | 54 | def list | ||
4727 | 55 | vms = run("list") | ||
4728 | 56 | vms.delete_if { |line| line =~ /^Total/ } | ||
4729 | 57 | vms.map { |line| VMC::Micro.escape_path(File.expand_path(line)) } | ||
4730 | 58 | end | ||
4731 | 59 | |||
4732 | 60 | def offline? | ||
4733 | 61 | command = "-gu #{@user} -gp #{@password} runProgramInGuest" | ||
4734 | 62 | args = '/usr/bin/test -e /var/vcap/micro/offline' | ||
4735 | 63 | # why not use run_command? | ||
4736 | 64 | result = %x{#{@vmrun} #{command} #{@vmx} #{args}} | ||
4737 | 65 | |||
4738 | 66 | if result.include?('Guest program exited with non-zero exit code: 1') | ||
4739 | 67 | return false | ||
4740 | 68 | elsif $?.exitstatus == 0 | ||
4741 | 69 | return true | ||
4742 | 70 | else | ||
4743 | 71 | raise "failed to execute vmrun:\n#{result}" | ||
4744 | 72 | end | ||
4745 | 73 | end | ||
4746 | 74 | |||
4747 | 75 | def offline! | ||
4748 | 76 | path = VMC::Micro.escape_path(VMC::Micro.config_file('offline.conf')) | ||
4749 | 77 | run('CopyFileFromHostToGuest', "#{path} /etc/dnsmasq.d/offline.conf") | ||
4750 | 78 | run('runProgramInGuest', '/usr/bin/touch /var/vcap/micro/offline') | ||
4751 | 79 | restart_dnsmasq | ||
4752 | 80 | end | ||
4753 | 81 | |||
4754 | 82 | def online! | ||
4755 | 83 | run('runProgramInGuest', '/bin/rm -f /etc/dnsmasq.d/offline.conf') | ||
4756 | 84 | run('runProgramInGuest', '/bin/rm -f /var/vcap/micro/offline') | ||
4757 | 85 | restart_dnsmasq | ||
4758 | 86 | end | ||
4759 | 87 | |||
4760 | 88 | # check to see if the micro cloud has been configured | ||
4761 | 89 | # uses default password to check | ||
4762 | 90 | def ready? | ||
4763 | 91 | command = "-gu root -gp 'ca$hc0w' runProgramInGuest" | ||
4764 | 92 | args = '/usr/bin/test -e /var/vcap/micro/micro.json' | ||
4765 | 93 | result = %x{#{@vmrun} #{command} #{@vmx} #{args}} | ||
4766 | 94 | |||
4767 | 95 | if result.include?('Invalid user name or password for the guest OS') || $?.exitstatus == 0 | ||
4768 | 96 | return true | ||
4769 | 97 | elsif $?.exitstatus == 1 | ||
4770 | 98 | return false | ||
4771 | 99 | else | ||
4772 | 100 | raise "failed to execute vmrun:\n#{result}" | ||
4773 | 101 | end | ||
4774 | 102 | end | ||
4775 | 103 | |||
4776 | 104 | def read_variable(var) | ||
4777 | 105 | # TODO deal with non-ok return | ||
4778 | 106 | run("readVariable", "runtimeConfig #{var}").first | ||
4779 | 107 | end | ||
4780 | 108 | |||
4781 | 109 | def write_variable(var, value) | ||
4782 | 110 | run('writeVariable', "runtimeConfig #{var} #{value}") | ||
4783 | 111 | end | ||
4784 | 112 | |||
4785 | 113 | def reset | ||
4786 | 114 | run('reset', 'soft') | ||
4787 | 115 | end | ||
4788 | 116 | |||
4789 | 117 | def restart_dnsmasq | ||
4790 | 118 | # restart command doesn't always work, start and stop seems to be more reliable | ||
4791 | 119 | run('runProgramInGuest', '/etc/init.d/dnsmasq stop') | ||
4792 | 120 | run('runProgramInGuest', '/etc/init.d/dnsmasq start') | ||
4793 | 121 | end | ||
4794 | 122 | |||
4795 | 123 | def run(command, args=nil) | ||
4796 | 124 | if command.include?('Guest') | ||
4797 | 125 | command = "-gu #{@user} -gp #{@password} #{command}" | ||
4798 | 126 | end | ||
4799 | 127 | VMC::Micro.run_command(@vmrun, "#{command} #{@vmx} #{args}") | ||
4800 | 128 | end | ||
4801 | 129 | |||
4802 | 130 | def running? | ||
4803 | 131 | vms = list | ||
4804 | 132 | if @platform == :windows | ||
4805 | 133 | vms.map! { |x| x.downcase } | ||
4806 | 134 | vms.include?(@vmx.downcase) | ||
4807 | 135 | else | ||
4808 | 136 | vms.include?(@vmx) | ||
4809 | 137 | end | ||
4810 | 138 | end | ||
4811 | 139 | |||
4812 | 140 | def start | ||
4813 | 141 | run('start') unless running? | ||
4814 | 142 | end | ||
4815 | 143 | |||
4816 | 144 | def stop | ||
4817 | 145 | run('stop') if running? | ||
4818 | 146 | end | ||
4819 | 147 | |||
4820 | 148 | def self.locate(platform) | ||
4821 | 149 | paths = YAML.load_file(VMC::Micro.config_file('paths.yml')) | ||
4822 | 150 | vmrun_paths = paths[platform.to_s]['vmrun'] | ||
4823 | 151 | vmrun_exe = @platform == :windows ? 'vmrun.exe' : 'vmrun' | ||
4824 | 152 | vmrun = VMC::Micro.locate_file(vmrun_exe, "VMware", vmrun_paths) | ||
4825 | 153 | err "Unable to locate vmrun, please supply --vmrun option" unless vmrun | ||
4826 | 154 | vmrun | ||
4827 | 155 | end | ||
4828 | 156 | end | ||
4829 | 157 | |||
4830 | 158 | end | ||
4831 | 0 | 159 | ||
4832 | === modified file 'metadata.yml' | |||
4833 | --- metadata.yml 2011-06-17 13:38:56 +0000 | |||
4834 | +++ metadata.yml 2012-06-13 01:37:18 +0000 | |||
4835 | @@ -1,13 +1,13 @@ | |||
4836 | 1 | --- !ruby/object:Gem::Specification | 1 | --- !ruby/object:Gem::Specification |
4837 | 2 | name: vmc | 2 | name: vmc |
4838 | 3 | version: !ruby/object:Gem::Version | 3 | version: !ruby/object:Gem::Version |
4840 | 4 | hash: 7 | 4 | hash: 55 |
4841 | 5 | prerelease: | 5 | prerelease: |
4842 | 6 | segments: | 6 | segments: |
4843 | 7 | - 0 | 7 | - 0 |
4844 | 8 | - 3 | 8 | - 3 |
4847 | 9 | - 10 | 9 | - 18 |
4848 | 10 | version: 0.3.10 | 10 | version: 0.3.18 |
4849 | 11 | platform: ruby | 11 | platform: ruby |
4850 | 12 | authors: | 12 | authors: |
4851 | 13 | - VMware | 13 | - VMware |
4852 | @@ -15,8 +15,7 @@ | |||
4853 | 15 | bindir: bin | 15 | bindir: bin |
4854 | 16 | cert_chain: [] | 16 | cert_chain: [] |
4855 | 17 | 17 | ||
4858 | 18 | date: 2011-04-11 00:00:00 -07:00 | 18 | date: 2012-05-30 00:00:00 Z |
4857 | 19 | default_executable: | ||
4859 | 20 | dependencies: | 19 | dependencies: |
4860 | 21 | - !ruby/object:Gem::Dependency | 20 | - !ruby/object:Gem::Dependency |
4861 | 22 | name: json_pure | 21 | name: json_pure |
4862 | @@ -24,7 +23,7 @@ | |||
4863 | 24 | requirement: &id001 !ruby/object:Gem::Requirement | 23 | requirement: &id001 !ruby/object:Gem::Requirement |
4864 | 25 | none: false | 24 | none: false |
4865 | 26 | requirements: | 25 | requirements: |
4867 | 27 | - - ~> | 26 | - - ">=" |
4868 | 28 | - !ruby/object:Gem::Version | 27 | - !ruby/object:Gem::Version |
4869 | 29 | hash: 1 | 28 | hash: 1 |
4870 | 30 | segments: | 29 | segments: |
4871 | @@ -32,31 +31,39 @@ | |||
4872 | 32 | - 5 | 31 | - 5 |
4873 | 33 | - 1 | 32 | - 1 |
4874 | 34 | version: 1.5.1 | 33 | version: 1.5.1 |
4875 | 34 | - - < | ||
4876 | 35 | - !ruby/object:Gem::Version | ||
4877 | 36 | hash: 11 | ||
4878 | 37 | segments: | ||
4879 | 38 | - 1 | ||
4880 | 39 | - 7 | ||
4881 | 40 | - 0 | ||
4882 | 41 | version: 1.7.0 | ||
4883 | 35 | type: :runtime | 42 | type: :runtime |
4884 | 36 | version_requirements: *id001 | 43 | version_requirements: *id001 |
4885 | 37 | - !ruby/object:Gem::Dependency | 44 | - !ruby/object:Gem::Dependency |
4887 | 38 | name: rubyzip2 | 45 | name: rubyzip |
4888 | 39 | prerelease: false | 46 | prerelease: false |
4889 | 40 | requirement: &id002 !ruby/object:Gem::Requirement | 47 | requirement: &id002 !ruby/object:Gem::Requirement |
4890 | 41 | none: false | 48 | none: false |
4891 | 42 | requirements: | 49 | requirements: |
4892 | 43 | - - ~> | 50 | - - ~> |
4893 | 44 | - !ruby/object:Gem::Version | 51 | - !ruby/object:Gem::Version |
4895 | 45 | hash: 13 | 52 | hash: 51 |
4896 | 46 | segments: | 53 | segments: |
4897 | 47 | - 2 | ||
4898 | 48 | - 0 | 54 | - 0 |
4901 | 49 | - 1 | 55 | - 9 |
4902 | 50 | version: 2.0.1 | 56 | - 4 |
4903 | 57 | version: 0.9.4 | ||
4904 | 51 | type: :runtime | 58 | type: :runtime |
4905 | 52 | version_requirements: *id002 | 59 | version_requirements: *id002 |
4906 | 53 | - !ruby/object:Gem::Dependency | 60 | - !ruby/object:Gem::Dependency |
4908 | 54 | name: highline | 61 | name: rest-client |
4909 | 55 | prerelease: false | 62 | prerelease: false |
4910 | 56 | requirement: &id003 !ruby/object:Gem::Requirement | 63 | requirement: &id003 !ruby/object:Gem::Requirement |
4911 | 57 | none: false | 64 | none: false |
4912 | 58 | requirements: | 65 | requirements: |
4914 | 59 | - - ~> | 66 | - - ">=" |
4915 | 60 | - !ruby/object:Gem::Version | 67 | - !ruby/object:Gem::Version |
4916 | 61 | hash: 13 | 68 | hash: 13 |
4917 | 62 | segments: | 69 | segments: |
4918 | @@ -64,52 +71,100 @@ | |||
4919 | 64 | - 6 | 71 | - 6 |
4920 | 65 | - 1 | 72 | - 1 |
4921 | 66 | version: 1.6.1 | 73 | version: 1.6.1 |
4922 | 74 | - - < | ||
4923 | 75 | - !ruby/object:Gem::Version | ||
4924 | 76 | hash: 11 | ||
4925 | 77 | segments: | ||
4926 | 78 | - 1 | ||
4927 | 79 | - 7 | ||
4928 | 80 | - 0 | ||
4929 | 81 | version: 1.7.0 | ||
4930 | 67 | type: :runtime | 82 | type: :runtime |
4931 | 68 | version_requirements: *id003 | 83 | version_requirements: *id003 |
4932 | 69 | - !ruby/object:Gem::Dependency | 84 | - !ruby/object:Gem::Dependency |
4934 | 70 | name: rest-client | 85 | name: terminal-table |
4935 | 71 | prerelease: false | 86 | prerelease: false |
4936 | 72 | requirement: &id004 !ruby/object:Gem::Requirement | 87 | requirement: &id004 !ruby/object:Gem::Requirement |
4937 | 73 | none: false | 88 | none: false |
4938 | 74 | requirements: | 89 | requirements: |
4955 | 75 | - - ">=" | 90 | - - ~> |
4956 | 76 | - !ruby/object:Gem::Version | 91 | - !ruby/object:Gem::Version |
4957 | 77 | hash: 13 | 92 | hash: 3 |
4958 | 78 | segments: | 93 | segments: |
4959 | 79 | - 1 | 94 | - 1 |
4960 | 80 | - 6 | 95 | - 4 |
4961 | 81 | - 1 | 96 | - 2 |
4962 | 82 | version: 1.6.1 | 97 | version: 1.4.2 |
4947 | 83 | - - < | ||
4948 | 84 | - !ruby/object:Gem::Version | ||
4949 | 85 | hash: 11 | ||
4950 | 86 | segments: | ||
4951 | 87 | - 1 | ||
4952 | 88 | - 7 | ||
4953 | 89 | - 0 | ||
4954 | 90 | version: 1.7.0 | ||
4963 | 91 | type: :runtime | 98 | type: :runtime |
4964 | 92 | version_requirements: *id004 | 99 | version_requirements: *id004 |
4965 | 93 | - !ruby/object:Gem::Dependency | 100 | - !ruby/object:Gem::Dependency |
4967 | 94 | name: terminal-table | 101 | name: interact |
4968 | 95 | prerelease: false | 102 | prerelease: false |
4969 | 96 | requirement: &id005 !ruby/object:Gem::Requirement | 103 | requirement: &id005 !ruby/object:Gem::Requirement |
4970 | 97 | none: false | 104 | none: false |
4971 | 98 | requirements: | 105 | requirements: |
4972 | 99 | - - ~> | 106 | - - ~> |
4973 | 100 | - !ruby/object:Gem::Version | 107 | - !ruby/object:Gem::Version |
4975 | 101 | hash: 3 | 108 | hash: 15 |
4976 | 102 | segments: | 109 | segments: |
4978 | 103 | - 1 | 110 | - 0 |
4979 | 104 | - 4 | 111 | - 4 |
4982 | 105 | - 2 | 112 | - 0 |
4983 | 106 | version: 1.4.2 | 113 | version: 0.4.0 |
4984 | 107 | type: :runtime | 114 | type: :runtime |
4985 | 108 | version_requirements: *id005 | 115 | version_requirements: *id005 |
4986 | 109 | - !ruby/object:Gem::Dependency | 116 | - !ruby/object:Gem::Dependency |
4987 | 117 | name: addressable | ||
4988 | 118 | prerelease: false | ||
4989 | 119 | requirement: &id006 !ruby/object:Gem::Requirement | ||
4990 | 120 | none: false | ||
4991 | 121 | requirements: | ||
4992 | 122 | - - ~> | ||
4993 | 123 | - !ruby/object:Gem::Version | ||
4994 | 124 | hash: 11 | ||
4995 | 125 | segments: | ||
4996 | 126 | - 2 | ||
4997 | 127 | - 2 | ||
4998 | 128 | - 6 | ||
4999 | 129 | version: 2.2.6 | ||
5000 | 130 | type: :runtime |
The diff has been truncated for viewing.
Looks fine, thanks! Again, UNRELEASED- >quantal.