trucker-cli 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (294) hide show
  1. checksums.yaml +15 -0
  2. data/LICENSE +1277 -0
  3. data/Rakefile +13 -0
  4. data/bin/truck +18 -0
  5. data/lib/admin/README.md +30 -0
  6. data/lib/admin/curl.rb +59 -0
  7. data/lib/admin/guid.rb +89 -0
  8. data/lib/admin/plugin.rb +8 -0
  9. data/lib/admin/service_auth_token.rb +93 -0
  10. data/lib/admin/service_broker/add.rb +54 -0
  11. data/lib/admin/service_broker/remove.rb +33 -0
  12. data/lib/admin/service_broker/service_brokers.rb +29 -0
  13. data/lib/admin/service_broker/update.rb +60 -0
  14. data/lib/admin/set_quota.rb +44 -0
  15. data/lib/cf.rb +14 -0
  16. data/lib/cf/cli.rb +495 -0
  17. data/lib/cf/cli/app/app.rb +43 -0
  18. data/lib/cf/cli/app/apps.rb +87 -0
  19. data/lib/cf/cli/app/base.rb +72 -0
  20. data/lib/cf/cli/app/delete.rb +95 -0
  21. data/lib/cf/cli/app/deprecated.rb +11 -0
  22. data/lib/cf/cli/app/env.rb +99 -0
  23. data/lib/cf/cli/app/events.rb +45 -0
  24. data/lib/cf/cli/app/files.rb +137 -0
  25. data/lib/cf/cli/app/health.rb +26 -0
  26. data/lib/cf/cli/app/instances.rb +53 -0
  27. data/lib/cf/cli/app/logs.rb +76 -0
  28. data/lib/cf/cli/app/push.rb +104 -0
  29. data/lib/cf/cli/app/push/create.rb +108 -0
  30. data/lib/cf/cli/app/push/interactions.rb +90 -0
  31. data/lib/cf/cli/app/push/sync.rb +57 -0
  32. data/lib/cf/cli/app/rename.rb +35 -0
  33. data/lib/cf/cli/app/restart.rb +31 -0
  34. data/lib/cf/cli/app/scale.rb +63 -0
  35. data/lib/cf/cli/app/start.rb +175 -0
  36. data/lib/cf/cli/app/stats.rb +66 -0
  37. data/lib/cf/cli/app/stop.rb +27 -0
  38. data/lib/cf/cli/domain/base.rb +9 -0
  39. data/lib/cf/cli/domain/domains.rb +40 -0
  40. data/lib/cf/cli/domain/map.rb +55 -0
  41. data/lib/cf/cli/domain/unmap.rb +56 -0
  42. data/lib/cf/cli/help.rb +15 -0
  43. data/lib/cf/cli/interactive.rb +104 -0
  44. data/lib/cf/cli/login_requirements.rb +13 -0
  45. data/lib/cf/cli/organization/base.rb +12 -0
  46. data/lib/cf/cli/organization/create.rb +36 -0
  47. data/lib/cf/cli/organization/delete.rb +61 -0
  48. data/lib/cf/cli/organization/org.rb +45 -0
  49. data/lib/cf/cli/organization/orgs.rb +30 -0
  50. data/lib/cf/cli/organization/rename.rb +36 -0
  51. data/lib/cf/cli/populators/base.rb +16 -0
  52. data/lib/cf/cli/populators/organization.rb +32 -0
  53. data/lib/cf/cli/populators/populator_methods.rb +64 -0
  54. data/lib/cf/cli/populators/space.rb +33 -0
  55. data/lib/cf/cli/populators/target.rb +14 -0
  56. data/lib/cf/cli/route/base.rb +9 -0
  57. data/lib/cf/cli/route/delete.rb +28 -0
  58. data/lib/cf/cli/route/map.rb +68 -0
  59. data/lib/cf/cli/route/routes.rb +26 -0
  60. data/lib/cf/cli/route/unmap.rb +56 -0
  61. data/lib/cf/cli/service/base.rb +9 -0
  62. data/lib/cf/cli/service/bind.rb +43 -0
  63. data/lib/cf/cli/service/create.rb +158 -0
  64. data/lib/cf/cli/service/delete.rb +82 -0
  65. data/lib/cf/cli/service/rename.rb +35 -0
  66. data/lib/cf/cli/service/service.rb +40 -0
  67. data/lib/cf/cli/service/service_helper.rb +25 -0
  68. data/lib/cf/cli/service/service_instance_helper.rb +100 -0
  69. data/lib/cf/cli/service/services.rb +110 -0
  70. data/lib/cf/cli/service/unbind.rb +36 -0
  71. data/lib/cf/cli/space/base.rb +27 -0
  72. data/lib/cf/cli/space/create.rb +66 -0
  73. data/lib/cf/cli/space/delete.rb +55 -0
  74. data/lib/cf/cli/space/rename.rb +38 -0
  75. data/lib/cf/cli/space/space.rb +66 -0
  76. data/lib/cf/cli/space/spaces.rb +57 -0
  77. data/lib/cf/cli/space/switch.rb +18 -0
  78. data/lib/cf/cli/start/base.rb +35 -0
  79. data/lib/cf/cli/start/colors.rb +13 -0
  80. data/lib/cf/cli/start/login.rb +104 -0
  81. data/lib/cf/cli/start/logout.rb +17 -0
  82. data/lib/cf/cli/start/target.rb +50 -0
  83. data/lib/cf/cli/start/target_prettifier.rb +35 -0
  84. data/lib/cf/cli/start/targets.rb +16 -0
  85. data/lib/cf/cli/user/base.rb +29 -0
  86. data/lib/cf/cli/user/create.rb +51 -0
  87. data/lib/cf/cli/user/passwd.rb +36 -0
  88. data/lib/cf/cli/user/register.rb +42 -0
  89. data/lib/cf/cli/user/users.rb +32 -0
  90. data/lib/cf/constants.rb +10 -0
  91. data/lib/cf/errors.rb +19 -0
  92. data/lib/cf/plugin.rb +56 -0
  93. data/lib/cf/spacing.rb +89 -0
  94. data/lib/cf/spec_helper.rb +1 -0
  95. data/lib/cf/test_support.rb +6 -0
  96. data/lib/cf/version.rb +3 -0
  97. data/lib/console/README.md +16 -0
  98. data/lib/console/console.rb +187 -0
  99. data/lib/console/plugin.rb +32 -0
  100. data/lib/manifests/errors.rb +35 -0
  101. data/lib/manifests/loader.rb +31 -0
  102. data/lib/manifests/loader/builder.rb +39 -0
  103. data/lib/manifests/loader/normalizer.rb +145 -0
  104. data/lib/manifests/loader/resolver.rb +79 -0
  105. data/lib/manifests/manifests.rb +343 -0
  106. data/lib/manifests/plugin.rb +140 -0
  107. data/lib/micro/README.md +25 -0
  108. data/lib/micro/errors.rb +4 -0
  109. data/lib/micro/micro.rb +56 -0
  110. data/lib/micro/plugin.rb +197 -0
  111. data/lib/micro/switcher/base.rb +79 -0
  112. data/lib/micro/switcher/darwin.rb +21 -0
  113. data/lib/micro/switcher/dummy.rb +15 -0
  114. data/lib/micro/switcher/linux.rb +16 -0
  115. data/lib/micro/switcher/windows.rb +31 -0
  116. data/lib/micro/vmrun.rb +175 -0
  117. data/lib/tasks/gem_release.rake +56 -0
  118. data/lib/tasks/windows_build.rake +14 -0
  119. data/lib/tunnel/README.md +29 -0
  120. data/lib/tunnel/config/clients.yml +17 -0
  121. data/lib/tunnel/helper-app/Gemfile +10 -0
  122. data/lib/tunnel/helper-app/Gemfile.lock +48 -0
  123. data/lib/tunnel/helper-app/server.rb +43 -0
  124. data/lib/tunnel/plugin.rb +183 -0
  125. data/lib/tunnel/tunnel.rb +295 -0
  126. data/spec/admin/curl_spec.rb +52 -0
  127. data/spec/admin/guid_spec.rb +85 -0
  128. data/spec/admin/service_broker/add_spec.rb +64 -0
  129. data/spec/admin/service_broker/remove_spec.rb +46 -0
  130. data/spec/admin/service_broker/service_brokers_spec.rb +34 -0
  131. data/spec/admin/service_broker/update_spec.rb +51 -0
  132. data/spec/admin/set_quota_spec.rb +89 -0
  133. data/spec/assets/env/Gemfile +4 -0
  134. data/spec/assets/env/Gemfile.lock +20 -0
  135. data/spec/assets/env/env_test.rb +58 -0
  136. data/spec/assets/hello-sinatra/Gemfile +3 -0
  137. data/spec/assets/hello-sinatra/Gemfile.lock +17 -0
  138. data/spec/assets/hello-sinatra/config.ru +3 -0
  139. data/spec/assets/hello-sinatra/fat-cat-makes-app-larger.png +0 -0
  140. data/spec/assets/hello-sinatra/main.rb +8 -0
  141. data/spec/assets/rails328_ruby187_app/Gemfile +39 -0
  142. data/spec/assets/rails328_ruby187_app/README.rdoc +261 -0
  143. data/spec/assets/rails328_ruby187_app/Rakefile +7 -0
  144. data/spec/assets/rails328_ruby187_app/app/assets/images/rails.png +0 -0
  145. data/spec/assets/rails328_ruby187_app/app/assets/javascripts/application.js +15 -0
  146. data/spec/assets/rails328_ruby187_app/app/assets/stylesheets/application.css +13 -0
  147. data/spec/assets/rails328_ruby187_app/app/controllers/application_controller.rb +3 -0
  148. data/spec/assets/rails328_ruby187_app/app/helpers/application_helper.rb +2 -0
  149. data/spec/assets/rails328_ruby187_app/app/views/layouts/application.html.erb +14 -0
  150. data/spec/assets/rails328_ruby187_app/config.ru +4 -0
  151. data/spec/assets/rails328_ruby187_app/config/application.rb +62 -0
  152. data/spec/assets/rails328_ruby187_app/config/boot.rb +6 -0
  153. data/spec/assets/rails328_ruby187_app/config/database.yml +25 -0
  154. data/spec/assets/rails328_ruby187_app/config/environment.rb +5 -0
  155. data/spec/assets/rails328_ruby187_app/config/environments/development.rb +37 -0
  156. data/spec/assets/rails328_ruby187_app/config/environments/production.rb +67 -0
  157. data/spec/assets/rails328_ruby187_app/config/environments/test.rb +37 -0
  158. data/spec/assets/rails328_ruby187_app/config/initializers/backtrace_silencers.rb +7 -0
  159. data/spec/assets/rails328_ruby187_app/config/initializers/inflections.rb +15 -0
  160. data/spec/assets/rails328_ruby187_app/config/initializers/mime_types.rb +5 -0
  161. data/spec/assets/rails328_ruby187_app/config/initializers/secret_token.rb +7 -0
  162. data/spec/assets/rails328_ruby187_app/config/initializers/session_store.rb +8 -0
  163. data/spec/assets/rails328_ruby187_app/config/initializers/wrap_parameters.rb +14 -0
  164. data/spec/assets/rails328_ruby187_app/config/locales/en.yml +5 -0
  165. data/spec/assets/rails328_ruby187_app/config/routes.rb +58 -0
  166. data/spec/assets/rails328_ruby187_app/db/seeds.rb +7 -0
  167. data/spec/assets/rails328_ruby187_app/doc/README_FOR_APP +2 -0
  168. data/spec/assets/rails328_ruby187_app/manifest.yml +7 -0
  169. data/spec/assets/rails328_ruby187_app/public/404.html +26 -0
  170. data/spec/assets/rails328_ruby187_app/public/422.html +26 -0
  171. data/spec/assets/rails328_ruby187_app/public/500.html +25 -0
  172. data/spec/assets/rails328_ruby187_app/public/assets/application-7270767b2a9e9fff880aa5de378ca791.css +0 -0
  173. data/spec/assets/rails328_ruby187_app/public/assets/application-7270767b2a9e9fff880aa5de378ca791.css.gz +0 -0
  174. data/spec/assets/rails328_ruby187_app/public/assets/application-ccab98dc1abdf097c0af693e20aed861.js +17 -0
  175. data/spec/assets/rails328_ruby187_app/public/assets/application-ccab98dc1abdf097c0af693e20aed861.js.gz +0 -0
  176. data/spec/assets/rails328_ruby187_app/public/assets/application.css +0 -0
  177. data/spec/assets/rails328_ruby187_app/public/assets/application.css.gz +0 -0
  178. data/spec/assets/rails328_ruby187_app/public/assets/application.js +17 -0
  179. data/spec/assets/rails328_ruby187_app/public/assets/application.js.gz +0 -0
  180. data/spec/assets/rails328_ruby187_app/public/assets/manifest.yml +4 -0
  181. data/spec/assets/rails328_ruby187_app/public/assets/rails-be8732dac73d845ac5b142c8fb5f9fb0.png +0 -0
  182. data/spec/assets/rails328_ruby187_app/public/assets/rails.png +0 -0
  183. data/spec/assets/rails328_ruby187_app/public/favicon.ico +0 -0
  184. data/spec/assets/rails328_ruby187_app/public/index.html +241 -0
  185. data/spec/assets/rails328_ruby187_app/public/robots.txt +5 -0
  186. data/spec/assets/rails328_ruby187_app/script/rails +6 -0
  187. data/spec/assets/rails328_ruby187_app/test/performance/browsing_test.rb +12 -0
  188. data/spec/assets/rails328_ruby187_app/test/test_helper.rb +13 -0
  189. data/spec/cf/cli/app/base_spec.rb +17 -0
  190. data/spec/cf/cli/app/delete_spec.rb +197 -0
  191. data/spec/cf/cli/app/env_spec.rb +261 -0
  192. data/spec/cf/cli/app/events_spec.rb +72 -0
  193. data/spec/cf/cli/app/help_spec.rb +39 -0
  194. data/spec/cf/cli/app/instances_spec.rb +69 -0
  195. data/spec/cf/cli/app/push/create_spec.rb +444 -0
  196. data/spec/cf/cli/app/push/interactions_spec.rb +32 -0
  197. data/spec/cf/cli/app/push_spec.rb +332 -0
  198. data/spec/cf/cli/app/rename_spec.rb +108 -0
  199. data/spec/cf/cli/app/restart_spec.rb +47 -0
  200. data/spec/cf/cli/app/scale_spec.rb +63 -0
  201. data/spec/cf/cli/app/start_spec.rb +255 -0
  202. data/spec/cf/cli/app/stats_spec.rb +66 -0
  203. data/spec/cf/cli/domain/help_spec.rb +39 -0
  204. data/spec/cf/cli/domain/map_spec.rb +140 -0
  205. data/spec/cf/cli/domain/unmap_spec.rb +77 -0
  206. data/spec/cf/cli/help_spec.rb +21 -0
  207. data/spec/cf/cli/organization/create_spec.rb +121 -0
  208. data/spec/cf/cli/organization/delete_spec.rb +105 -0
  209. data/spec/cf/cli/organization/help_spec.rb +39 -0
  210. data/spec/cf/cli/organization/orgs_spec.rb +116 -0
  211. data/spec/cf/cli/organization/rename_spec.rb +116 -0
  212. data/spec/cf/cli/populators/organization_spec.rb +179 -0
  213. data/spec/cf/cli/populators/space_spec.rb +156 -0
  214. data/spec/cf/cli/populators/target_spec.rb +39 -0
  215. data/spec/cf/cli/route/delete_spec.rb +92 -0
  216. data/spec/cf/cli/route/help_spec.rb +32 -0
  217. data/spec/cf/cli/route/map_spec.rb +148 -0
  218. data/spec/cf/cli/route/unmap_spec.rb +114 -0
  219. data/spec/cf/cli/service/bind_spec.rb +25 -0
  220. data/spec/cf/cli/service/create_spec.rb +147 -0
  221. data/spec/cf/cli/service/delete_spec.rb +22 -0
  222. data/spec/cf/cli/service/help_spec.rb +39 -0
  223. data/spec/cf/cli/service/rename_spec.rb +103 -0
  224. data/spec/cf/cli/service/service_helper_spec.rb +66 -0
  225. data/spec/cf/cli/service/service_instance_helper_spec.rb +155 -0
  226. data/spec/cf/cli/service/service_spec.rb +23 -0
  227. data/spec/cf/cli/service/services_spec.rb +156 -0
  228. data/spec/cf/cli/service/unbind_spec.rb +25 -0
  229. data/spec/cf/cli/space/base_spec.rb +49 -0
  230. data/spec/cf/cli/space/create_spec.rb +144 -0
  231. data/spec/cf/cli/space/delete_spec.rb +109 -0
  232. data/spec/cf/cli/space/help_spec.rb +39 -0
  233. data/spec/cf/cli/space/rename_spec.rb +110 -0
  234. data/spec/cf/cli/space/space_spec.rb +80 -0
  235. data/spec/cf/cli/space/spaces_spec.rb +107 -0
  236. data/spec/cf/cli/space/switch_space_spec.rb +57 -0
  237. data/spec/cf/cli/start/help_spec.rb +39 -0
  238. data/spec/cf/cli/start/login_spec.rb +142 -0
  239. data/spec/cf/cli/start/logout_spec.rb +49 -0
  240. data/spec/cf/cli/start/target_prettifier_spec.rb +78 -0
  241. data/spec/cf/cli/start/target_spec.rb +123 -0
  242. data/spec/cf/cli/user/create_spec.rb +98 -0
  243. data/spec/cf/cli/user/help_spec.rb +32 -0
  244. data/spec/cf/cli/user/passwd_spec.rb +94 -0
  245. data/spec/cf/cli/user/register_spec.rb +145 -0
  246. data/spec/cf/cli_spec.rb +474 -0
  247. data/spec/console/console_spec.rb +189 -0
  248. data/spec/factories/cfoundry/v2/apps_factory.rb +12 -0
  249. data/spec/factories/cfoundry/v2/clients_factory.rb +7 -0
  250. data/spec/factories/cfoundry/v2/domains_factory.rb +8 -0
  251. data/spec/factories/cfoundry/v2/managed_service_instances_factory.rb +13 -0
  252. data/spec/factories/cfoundry/v2/organizations_factory.rb +14 -0
  253. data/spec/factories/cfoundry/v2/quota_definitions.rb +13 -0
  254. data/spec/factories/cfoundry/v2/routes_factory.rb +8 -0
  255. data/spec/factories/cfoundry/v2/service_bindings_factory.rb +11 -0
  256. data/spec/factories/cfoundry/v2/service_brokers_factory.rb +11 -0
  257. data/spec/factories/cfoundry/v2/service_plans_factory.rb +17 -0
  258. data/spec/factories/cfoundry/v2/services_factory.rb +14 -0
  259. data/spec/factories/cfoundry/v2/spaces_factory.rb +13 -0
  260. data/spec/factories/cfoundry/v2/stacks_factory.rb +11 -0
  261. data/spec/factories/cfoundry/v2/user_provided_service_instances_factory.rb +12 -0
  262. data/spec/factories/cfoundry/v2/users_factory.rb +7 -0
  263. data/spec/features/backspace_spec.rb +46 -0
  264. data/spec/features/create_user_spec.rb +74 -0
  265. data/spec/features/delete_orphaned_service_spec.rb +64 -0
  266. data/spec/features/login_spec.rb +65 -0
  267. data/spec/features/manifests_spec.rb +86 -0
  268. data/spec/features/org_spec.rb +50 -0
  269. data/spec/features/push_flow_spec.rb +169 -0
  270. data/spec/features/service_brokers_spec.rb +173 -0
  271. data/spec/features/services_spec.rb +122 -0
  272. data/spec/features/space_spec.rb +107 -0
  273. data/spec/features/switching_targets_spec.rb +70 -0
  274. data/spec/manifests/errors_spec.rb +43 -0
  275. data/spec/manifests/loader/builder_spec.rb +80 -0
  276. data/spec/manifests/loader/normalizer_spec.rb +158 -0
  277. data/spec/manifests/manifests_spec.rb +335 -0
  278. data/spec/manifests/plugin_spec.rb +410 -0
  279. data/spec/micro/plugin_spec.rb +64 -0
  280. data/spec/spec_helper.rb +69 -0
  281. data/spec/support/cli_helper.rb +87 -0
  282. data/spec/support/config_helper.rb +15 -0
  283. data/spec/support/factory_girl.rb +6 -0
  284. data/spec/support/fake_home_dir.rb +55 -0
  285. data/spec/support/features_helper.rb +99 -0
  286. data/spec/support/interact_helper.rb +27 -0
  287. data/spec/support/matchers.rb +16 -0
  288. data/spec/support/mock_commands/mock_restart.rb +16 -0
  289. data/spec/support/mock_commands/mock_start.rb +17 -0
  290. data/spec/support/shared_examples/errors.rb +40 -0
  291. data/spec/support/shared_examples/input.rb +14 -0
  292. data/spec/tunnel/plugin_spec.rb +31 -0
  293. data/spec/tunnel/tunnel_spec.rb +54 -0
  294. metadata +814 -0
@@ -0,0 +1,43 @@
1
+ #!/usr/bin/env ruby
2
+ # Copyright (c) 2009-2011 VMware, Inc.
3
+ $:.unshift(File.dirname(__FILE__) + '/lib')
4
+
5
+ require 'rubygems'
6
+ require 'bundler/setup'
7
+
8
+ require 'caldecott'
9
+ require 'sinatra'
10
+ require 'json'
11
+ require 'eventmachine'
12
+
13
+ port = ENV['PORT']
14
+ port ||= 8081
15
+
16
+ # add vcap specific stuff to Caldecott
17
+ class VcapHttpTunnel < Caldecott::Server::HttpTunnel
18
+ get '/info' do
19
+ { "version" => '0.0.4' }.to_json
20
+ end
21
+
22
+ def self.get_tunnels
23
+ super
24
+ end
25
+
26
+ get '/services' do
27
+ services_env = ENV['VCAP_SERVICES']
28
+ return "no services env" if services_env.nil? or services_env.empty?
29
+ services_env
30
+ end
31
+
32
+ get '/services/:service' do |service_name|
33
+ services_env = ENV['VCAP_SERVICES']
34
+ not_found if services_env.nil?
35
+
36
+ services = JSON.parse(services_env).values.flatten(1)
37
+ service = services.find { |s| s["name"] == service_name }
38
+ not_found if service.nil?
39
+ service["credentials"].to_json
40
+ end
41
+ end
42
+
43
+ VcapHttpTunnel.run!(:port => port, :auth_token => ENV["CALDECOTT_AUTH"])
@@ -0,0 +1,183 @@
1
+ require "cf/cli"
2
+ require "tunnel/tunnel"
3
+
4
+ module CFTunnelPlugin
5
+ class Tunnel < CF::CLI
6
+ CLIENTS_FILE = "tunnel-clients.yml"
7
+ STOCK_CLIENTS = File.expand_path("../config/clients.yml", __FILE__)
8
+
9
+ desc "Create a local tunnel to a service."
10
+ group :services, :manage
11
+ input(:instance, :argument => :optional,
12
+ :from_given => find_by_name("service instance"),
13
+ :desc => "Service instance to tunnel to") { |instances|
14
+ ask("Which service instance?", :choices => instances,
15
+ :display => proc(&:name))
16
+ }
17
+ input(:client, :argument => :optional,
18
+ :desc => "Client to automatically launch") { |clients|
19
+ if clients.empty?
20
+ "none"
21
+ else
22
+ ask("Which client would you like to start?",
23
+ :choices => clients.keys.unshift("none"))
24
+ end
25
+ }
26
+ input(:port, :default => 10000, :desc => "Port to bind the tunnel to")
27
+ def tunnel
28
+ instances = client.service_instances
29
+ fail "No services available for tunneling." if instances.empty?
30
+
31
+ instance = input[:instance, instances.sort_by(&:name)]
32
+ vendor = instance.service_plan.service.label
33
+ clients = tunnel_clients[vendor] || {}
34
+ client_name = input[:client, clients]
35
+
36
+ tunnel = CFTunnel.new(client, instance)
37
+ port = tunnel.pick_port!(input[:port])
38
+
39
+ conn_info =
40
+ with_progress("Opening tunnel on port #{c(port, :name)}") do
41
+ tunnel.open!
42
+ end
43
+
44
+ if client_name == "none"
45
+ unless quiet?
46
+ line
47
+ display_tunnel_connection_info(conn_info)
48
+
49
+ line
50
+ line "Open another shell to run command-line clients or"
51
+ line "use a UI tool to connect using the displayed information."
52
+ line "Press Ctrl-C to exit..."
53
+ end
54
+
55
+ tunnel.wait_for_end
56
+ else
57
+ with_progress("Waiting for local tunnel to become available") do
58
+ tunnel.wait_for_start
59
+ end
60
+
61
+ unless start_local_prog(clients, client_name, conn_info, port)
62
+ fail "'#{client_name}' execution failed; is it in your $PATH?"
63
+ end
64
+ end
65
+ end
66
+
67
+ def tunnel_clients
68
+ return @tunnel_clients if @tunnel_clients
69
+ stock_config = YAML.load_file(STOCK_CLIENTS)
70
+ custom_config_file = config_file_path
71
+
72
+ if File.exists?(custom_config_file)
73
+ custom_config = YAML.load_file(custom_config_file)
74
+ @tunnel_clients = deep_merge(stock_config, custom_config)
75
+ else
76
+ @tunnel_clients = stock_config
77
+ end
78
+ end
79
+
80
+ private
81
+
82
+ def config_file_path
83
+ File.expand_path("#{CF::CONFIG_DIR}/#{CLIENTS_FILE}")
84
+ end
85
+
86
+ def display_tunnel_connection_info(info)
87
+ line "Service connection info:"
88
+
89
+ to_show = [nil, nil, nil] # reserved for user, pass, db name
90
+ info.keys.each do |k|
91
+ case k
92
+ when "host", "hostname", "port", "node_id"
93
+ # skip
94
+ when "user", "username"
95
+ # prefer "username" over "user"
96
+ to_show[0] = k unless to_show[0] == "username"
97
+ when "password"
98
+ to_show[1] = k
99
+ when "name"
100
+ to_show[2] = k
101
+ else
102
+ to_show << k
103
+ end
104
+ end
105
+ to_show.compact!
106
+
107
+ align_len = to_show.collect(&:size).max + 1
108
+
109
+ indented do
110
+ to_show.each do |k|
111
+ # TODO: modify the server services rest call to have explicit knowledge
112
+ # about the items to return. It should return all of them if
113
+ # the service is unknown so that we don't have to do this weird
114
+ # filtering.
115
+ line "#{k.ljust align_len}: #{b(info[k])}"
116
+ end
117
+ end
118
+
119
+ line
120
+ end
121
+
122
+ def start_local_prog(clients, command, info, port)
123
+ client = clients[File.basename(command)]
124
+
125
+ cmdline = "#{command} "
126
+
127
+ case client
128
+ when Hash
129
+ cmdline << resolve_symbols(client["command"], info, port)
130
+ client["environment"].each do |e|
131
+ if e =~ /([^=]+)=(["']?)([^"']*)\2/
132
+ ENV[$1] = resolve_symbols($3, info, port)
133
+ else
134
+ fail "Invalid environment variable: #{e}"
135
+ end
136
+ end
137
+ when String
138
+ cmdline << resolve_symbols(client, info, port)
139
+ else
140
+ raise "Unknown client info: #{client.inspect}."
141
+ end
142
+
143
+ if verbose?
144
+ line
145
+ line "Launching '#{cmdline}'"
146
+ end
147
+
148
+ system(cmdline)
149
+ end
150
+
151
+ def resolve_symbols(str, info, local_port)
152
+ str.gsub(/\$\{\s*([^\}]+)\s*\}/) do
153
+ sym = $1
154
+
155
+ case sym
156
+ when "host"
157
+ # TODO: determine proper host
158
+ "localhost"
159
+ when "port"
160
+ local_port
161
+ when "user", "username"
162
+ info["username"]
163
+ when /^ask (.+)/
164
+ ask($1)
165
+ else
166
+ info[sym] || raise("Unknown symbol in config: #{sym}")
167
+ end
168
+ end
169
+ end
170
+
171
+ def deep_merge(a, b)
172
+ merge = proc { |_, old, new|
173
+ if old.is_a?(Hash) && new.is_a?(Hash)
174
+ old.merge(new, &merge)
175
+ else
176
+ new
177
+ end
178
+ }
179
+
180
+ a.merge(b, &merge)
181
+ end
182
+ end
183
+ end
@@ -0,0 +1,295 @@
1
+ require "addressable/uri"
2
+ require "restclient"
3
+ require "uuidtools"
4
+
5
+ require "caldecott-client"
6
+
7
+ class CFTunnel
8
+ HELPER_NAME = "caldecott"
9
+ HELPER_APP = File.expand_path("../helper-app", __FILE__)
10
+
11
+ # bump this AND the version info reported by HELPER_APP/server.rb
12
+ # this is to keep the helper in sync with any updates here
13
+ HELPER_VERSION = "0.0.4"
14
+
15
+ def initialize(client, service, port = 10000)
16
+ @client = client
17
+ @service = service
18
+ @port = port
19
+ end
20
+
21
+ def open!
22
+ if helper
23
+ auth = helper_auth
24
+
25
+ unless helper_healthy?(auth)
26
+ delete_helper
27
+ auth = create_helper
28
+ end
29
+ else
30
+ auth = create_helper
31
+ end
32
+
33
+ bind_to_helper if @service && !helper_already_binds?
34
+
35
+ info = get_connection_info(auth)
36
+
37
+ start_tunnel(info, auth)
38
+
39
+ info
40
+ end
41
+
42
+ def wait_for_start
43
+ 10.times do |n|
44
+ begin
45
+ TCPSocket.open("localhost", @port).close
46
+ return true
47
+ rescue => e
48
+ sleep 1
49
+ end
50
+ end
51
+
52
+ raise "Could not connect to local tunnel."
53
+ end
54
+
55
+ def wait_for_end
56
+ if @local_tunnel_thread
57
+ @local_tunnel_thread.join
58
+ else
59
+ raise "Tunnel wasn't started!"
60
+ end
61
+ end
62
+
63
+ PORT_RANGE = 10
64
+ def pick_port!(port = @port)
65
+ original = port
66
+
67
+ PORT_RANGE.times do |n|
68
+ begin
69
+ TCPSocket.open("localhost", port)
70
+ port += 1
71
+ rescue
72
+ return @port = port
73
+ end
74
+ end
75
+
76
+ @port = grab_ephemeral_port
77
+ end
78
+
79
+ private
80
+
81
+ def helper
82
+ @helper ||= @client.app_by_name(HELPER_NAME)
83
+ end
84
+
85
+ def create_helper
86
+ auth = UUIDTools::UUID.random_create.to_s
87
+ push_helper(auth)
88
+ start_helper
89
+ auth
90
+ end
91
+
92
+ def helper_auth
93
+ helper.env["CALDECOTT_AUTH"]
94
+ end
95
+
96
+ def helper_healthy?(token)
97
+ return false unless helper.healthy?
98
+
99
+ begin
100
+ response = RestClient.get(
101
+ "#{helper_url}/info",
102
+ "Auth-Token" => token
103
+ )
104
+
105
+ info = JSON.parse(response)
106
+ if info["version"] == HELPER_VERSION
107
+ true
108
+ else
109
+ stop_helper
110
+ false
111
+ end
112
+ rescue RestClient::Exception
113
+ stop_helper
114
+ false
115
+ end
116
+ end
117
+
118
+ def helper_already_binds?
119
+ helper.binds? @service
120
+ end
121
+
122
+ def push_helper(token)
123
+ app = @client.app
124
+ app.name = HELPER_NAME
125
+ app.command = "bundle exec ruby server.rb"
126
+ app.total_instances = 1
127
+ app.memory = 128
128
+ app.env = {"CALDECOTT_AUTH" => token}
129
+
130
+ space = app.space = @client.current_space
131
+ app.create!
132
+
133
+ app.bind(@service) if @service
134
+
135
+ domain = @client.domains.find { |d| d.owning_organization == nil }
136
+
137
+ app.create_route(:domain => domain, :space => space, :host => random_helper_url)
138
+
139
+ begin
140
+ app.upload(HELPER_APP)
141
+ invalidate_tunnel_app_info
142
+ rescue
143
+ app.delete!
144
+ raise
145
+ end
146
+ end
147
+
148
+ def delete_helper
149
+ helper.delete!
150
+ invalidate_tunnel_app_info
151
+ end
152
+
153
+ def stop_helper
154
+ helper.stop!
155
+ invalidate_tunnel_app_info
156
+ end
157
+
158
+ TUNNEL_CHECK_LIMIT = 60
159
+ def start_helper
160
+ helper.start!
161
+
162
+ seconds = 0
163
+ until helper.healthy?
164
+ sleep 1
165
+ seconds += 1
166
+ if seconds == TUNNEL_CHECK_LIMIT
167
+ raise "Helper application failed to start."
168
+ end
169
+ end
170
+
171
+ invalidate_tunnel_app_info
172
+ end
173
+
174
+ def bind_to_helper
175
+ helper.bind(@service)
176
+ helper.restart!
177
+ end
178
+
179
+ def invalidate_tunnel_app_info
180
+ @helper_url = nil
181
+ @helper = nil
182
+ end
183
+
184
+ def helper_url
185
+ return @helper_url if @helper_url
186
+
187
+ tun_url = helper.url
188
+
189
+ ["https", "http"].each do |scheme|
190
+ url = "#{scheme}://#{tun_url}"
191
+ begin
192
+ RestClient.get(url)
193
+
194
+ # https failed
195
+ rescue Errno::ECONNREFUSED
196
+
197
+ # we expect a 404 since this request isn't auth'd
198
+ rescue RestClient::ResourceNotFound
199
+ return @helper_url = url
200
+ end
201
+ end
202
+
203
+ raise "Cannot determine URL for #{tun_url}"
204
+ end
205
+
206
+ def get_connection_info(token)
207
+ response = nil
208
+ 10.times do
209
+ begin
210
+ response =
211
+ RestClient.get(
212
+ helper_url + "/" + safe_path("services", @service.name),
213
+ "Auth-Token" => token)
214
+
215
+ break
216
+ rescue RestClient::Exception => e
217
+ sleep 1
218
+ end
219
+ end
220
+
221
+ unless response
222
+ raise "Remote tunnel helper is unaware of #{@service.name}!"
223
+ end
224
+
225
+ is_v2 = @client.is_a?(CFoundry::V2::Client)
226
+
227
+ info = JSON.parse(response)
228
+ case (is_v2 ? @service.service_plan.service.label : @service.vendor)
229
+ when "rabbitmq"
230
+ uri = Addressable::URI.parse info["url"]
231
+ info["hostname"] = uri.host
232
+ info["port"] = uri.port
233
+ info["vhost"] = uri.path[1..-1]
234
+ info["user"] = uri.user
235
+ info["password"] = uri.password
236
+ info.delete "url"
237
+
238
+ # we use "db" as the "name" for mongo
239
+ # existing "name" is junk
240
+ when "mongodb"
241
+ info["name"] = info["db"]
242
+ info.delete "db"
243
+
244
+ # our "name" is irrelevant for redis
245
+ when "redis"
246
+ info.delete "name"
247
+
248
+ when "filesystem"
249
+ raise "Tunneling is not supported for this type of service"
250
+ end
251
+
252
+ ["hostname", "port", "password"].each do |k|
253
+ raise "Could not determine #{k} for #{@service.name}" if info[k].nil?
254
+ end
255
+
256
+ info
257
+ end
258
+
259
+ def start_tunnel(conn_info, auth)
260
+ @local_tunnel_thread = Thread.new do
261
+ Caldecott::Client.start({
262
+ :local_port => @port,
263
+ :tun_url => helper_url,
264
+ :dst_host => conn_info["hostname"],
265
+ :dst_port => conn_info["port"],
266
+ :log_file => STDOUT,
267
+ :log_level => ENV["CF_TUNNEL_DEBUG"] || "ERROR",
268
+ :auth_token => auth,
269
+ :quiet => true
270
+ })
271
+ end
272
+
273
+ at_exit { @local_tunnel_thread.kill }
274
+ end
275
+
276
+ def random_helper_url
277
+ random = sprintf("%x", rand(1000000))
278
+ "caldecott-#{random}"
279
+ end
280
+
281
+ def safe_path(*segments)
282
+ segments.flatten.collect { |x|
283
+ URI.encode x.to_s, Regexp.new("[^#{URI::PATTERN::UNRESERVED}]")
284
+ }.join("/")
285
+ end
286
+
287
+ def grab_ephemeral_port
288
+ socket = TCPServer.new("0.0.0.0", 0)
289
+ socket.setsockopt(Socket::SOL_SOCKET, Socket::SO_REUSEADDR, true)
290
+ Socket.do_not_reverse_lookup = true
291
+ socket.addr[1]
292
+ ensure
293
+ socket.close
294
+ end
295
+ end