vmc 0.3.13 → 0.3.14.beta.3
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.
- data/README.md +2 -0
- data/caldecott_helper/Gemfile +10 -0
- data/caldecott_helper/Gemfile.lock +48 -0
- data/caldecott_helper/server.rb +43 -0
- data/config/clients.yml +11 -0
- data/lib/cli.rb +1 -0
- data/lib/cli/commands/apps.rb +5 -3
- data/lib/cli/commands/misc.rb +1 -1
- data/lib/cli/commands/services.rb +92 -0
- data/lib/cli/commands/user.rb +1 -0
- data/lib/cli/config.rb +27 -0
- data/lib/cli/runner.rb +8 -0
- data/lib/cli/tunnel_helper.rb +289 -0
- data/lib/cli/usage.rb +2 -0
- data/lib/cli/version.rb +1 -1
- metadata +28 -12
data/README.md
CHANGED
@@ -62,6 +62,8 @@ MIT license, please see the LICENSE file. All rights reserved._
|
|
62
62
|
bind-service <servicename> <appname> Bind a service to an application
|
63
63
|
unbind-service <servicename> <appname> Unbind service from the application
|
64
64
|
clone-services <src-app> <dest-app> Clone service bindings from <src-app> application to <dest-app>
|
65
|
+
tunnel <servicename> [--port] Create a local tunnel to a service
|
66
|
+
tunnel <servicename> <clientcmd> Create a local tunnel to a service and start a local client
|
65
67
|
|
66
68
|
Administration
|
67
69
|
user Display user account information
|
@@ -0,0 +1,48 @@
|
|
1
|
+
GEM
|
2
|
+
remote: http://rubygems.org/
|
3
|
+
specs:
|
4
|
+
addressable (2.2.6)
|
5
|
+
async_sinatra (0.5.0)
|
6
|
+
rack (>= 1.2.1)
|
7
|
+
sinatra (>= 1.0)
|
8
|
+
caldecott (0.0.3)
|
9
|
+
addressable (= 2.2.6)
|
10
|
+
async_sinatra (= 0.5.0)
|
11
|
+
em-http-request (= 0.3.0)
|
12
|
+
em-websocket (= 0.3.1)
|
13
|
+
json (= 1.6.1)
|
14
|
+
uuidtools (= 2.1.2)
|
15
|
+
daemons (1.1.4)
|
16
|
+
em-http-request (0.3.0)
|
17
|
+
addressable (>= 2.0.0)
|
18
|
+
escape_utils
|
19
|
+
eventmachine (>= 0.12.9)
|
20
|
+
em-websocket (0.3.1)
|
21
|
+
addressable (>= 2.1.1)
|
22
|
+
eventmachine (>= 0.12.9)
|
23
|
+
escape_utils (0.2.4)
|
24
|
+
eventmachine (0.12.10)
|
25
|
+
json (1.6.1)
|
26
|
+
rack (1.2.4)
|
27
|
+
sinatra (1.2.7)
|
28
|
+
rack (~> 1.1)
|
29
|
+
tilt (>= 1.2.2, < 2.0)
|
30
|
+
thin (1.2.11)
|
31
|
+
daemons (>= 1.0.9)
|
32
|
+
eventmachine (>= 0.12.6)
|
33
|
+
rack (>= 1.0.0)
|
34
|
+
tilt (1.3.3)
|
35
|
+
uuidtools (2.1.2)
|
36
|
+
|
37
|
+
PLATFORMS
|
38
|
+
ruby
|
39
|
+
|
40
|
+
DEPENDENCIES
|
41
|
+
async_sinatra
|
42
|
+
bundler
|
43
|
+
caldecott (= 0.0.3)
|
44
|
+
em-websocket
|
45
|
+
json
|
46
|
+
rack (~> 1.2.0)
|
47
|
+
thin
|
48
|
+
uuidtools
|
@@ -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['VMC_APP_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['VMC_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['VMC_SERVICES']
|
34
|
+
not_found if services_env.nil?
|
35
|
+
|
36
|
+
services = JSON.parse(services_env)
|
37
|
+
service = services.find { |s| s["name"] == service_name }
|
38
|
+
not_found if service.nil?
|
39
|
+
service["options"].to_json
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
VcapHttpTunnel.run!(:port => port, :auth_token => ENV["CALDECOTT_AUTH"])
|
data/config/clients.yml
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
redis:
|
2
|
+
redis-cli: redis-cli -h ${host} -p ${port} -a ${password}
|
3
|
+
|
4
|
+
mysql:
|
5
|
+
mysql: mysql --protocol=TCP --host=${host} --port=${port} --user=${user} --password=${password} ${name}
|
6
|
+
|
7
|
+
mongodb:
|
8
|
+
mongo: mongo --host ${host} --port ${port} -u ${user} -p ${password} ${name}
|
9
|
+
|
10
|
+
postgresql:
|
11
|
+
psql: PGPASSWORD='${password}' psql -h ${host} -p ${port} -d ${name} -U ${user} -w
|
data/lib/cli.rb
CHANGED
@@ -11,6 +11,7 @@ module VMC
|
|
11
11
|
autoload :Runner, "#{ROOT}/cli/runner"
|
12
12
|
autoload :ZipUtil, "#{ROOT}/cli/zip_util"
|
13
13
|
autoload :ServicesHelper, "#{ROOT}/cli/services_helper"
|
14
|
+
autoload :TunnelHelper, "#{ROOT}/cli/tunnel_helper"
|
14
15
|
|
15
16
|
module Command
|
16
17
|
autoload :Base, "#{ROOT}/cli/commands/base"
|
data/lib/cli/commands/apps.rb
CHANGED
@@ -185,7 +185,7 @@ module VMC::Cli::Command
|
|
185
185
|
uris << url
|
186
186
|
app[:uris] = uris
|
187
187
|
client.update_app(appname, app)
|
188
|
-
display "
|
188
|
+
display "Successfully mapped url".green
|
189
189
|
end
|
190
190
|
|
191
191
|
def unmap(appname, url)
|
@@ -196,7 +196,7 @@ module VMC::Cli::Command
|
|
196
196
|
err "Invalid url" unless deleted
|
197
197
|
app[:uris] = uris
|
198
198
|
client.update_app(appname, app)
|
199
|
-
display "
|
199
|
+
display "Successfully unmapped url".green
|
200
200
|
|
201
201
|
end
|
202
202
|
|
@@ -268,6 +268,8 @@ module VMC::Cli::Command
|
|
268
268
|
end
|
269
269
|
|
270
270
|
def logs(appname)
|
271
|
+
# Check if we have an app before progressing further
|
272
|
+
client.app_info(appname)
|
271
273
|
return grab_all_logs(appname) if @options[:all] && !@options[:instance]
|
272
274
|
instance = @options[:instance] || '0'
|
273
275
|
grab_logs(appname, instance)
|
@@ -686,7 +688,7 @@ module VMC::Cli::Command
|
|
686
688
|
|
687
689
|
display "The following provisioned services are available"
|
688
690
|
name = ask(
|
689
|
-
"Please select one you
|
691
|
+
"Please select one you wish to use",
|
690
692
|
{ :indexed => true,
|
691
693
|
:choices => user_services.collect { |s| s[:name] }
|
692
694
|
}
|
data/lib/cli/commands/misc.rb
CHANGED
@@ -2,6 +2,7 @@ module VMC::Cli::Command
|
|
2
2
|
|
3
3
|
class Services < Base
|
4
4
|
include VMC::Cli::ServicesHelper
|
5
|
+
include VMC::Cli::TunnelHelper
|
5
6
|
|
6
7
|
def services
|
7
8
|
ss = client.services_info
|
@@ -81,5 +82,96 @@ module VMC::Cli::Command
|
|
81
82
|
check_app_for_restart(dest_app)
|
82
83
|
end
|
83
84
|
|
85
|
+
def tunnel(service=nil, client_name=nil)
|
86
|
+
unless defined? Caldecott
|
87
|
+
display "To use `vmc tunnel', you must first install Caldecott:"
|
88
|
+
display ""
|
89
|
+
display "\tgem install caldecott"
|
90
|
+
display ""
|
91
|
+
display "Note that you'll need a C compiler. If you're on OS X, Xcode"
|
92
|
+
display "will provide one. If you're on Windows, try DevKit."
|
93
|
+
display ""
|
94
|
+
display "This manual step will be removed in the future."
|
95
|
+
display ""
|
96
|
+
err "Caldecott is not installed."
|
97
|
+
end
|
98
|
+
|
99
|
+
ps = client.services
|
100
|
+
err "No services available to tunnel to" if ps.empty?
|
101
|
+
|
102
|
+
unless service
|
103
|
+
choices = ps.collect { |s| s[:name] }.sort
|
104
|
+
service = ask(
|
105
|
+
"Which service to tunnel to?",
|
106
|
+
:choices => choices,
|
107
|
+
:indexed => true
|
108
|
+
)
|
109
|
+
end
|
110
|
+
|
111
|
+
info = ps.select { |s| s[:name] == service }.first
|
112
|
+
|
113
|
+
err "Unknown service '#{service}'" unless info
|
114
|
+
|
115
|
+
# TODO: rather than default to a particular port, we should get
|
116
|
+
# the defaults based on the service name.. i.e. known services should
|
117
|
+
# have known local default ports for this side of the tunnel.
|
118
|
+
port = pick_tunnel_port(@options[:port] || 10000)
|
119
|
+
|
120
|
+
raise VMC::Client::AuthError unless client.logged_in?
|
121
|
+
|
122
|
+
if not tunnel_pushed?
|
123
|
+
display "Deploying tunnel application '#{tunnel_appname}'."
|
124
|
+
auth = ask("Create a password", :echo => "*")
|
125
|
+
push_caldecott(auth)
|
126
|
+
bind_service_banner(service, tunnel_appname, false)
|
127
|
+
start_caldecott
|
128
|
+
else
|
129
|
+
auth = ask("Password", :echo => "*")
|
130
|
+
end
|
131
|
+
|
132
|
+
if not tunnel_healthy?(auth)
|
133
|
+
display "Redeploying tunnel application '#{tunnel_appname}'."
|
134
|
+
|
135
|
+
# We don't expect caldecott not to be running, so take the
|
136
|
+
# most aggressive restart method.. delete/re-push
|
137
|
+
client.delete_app(tunnel_appname)
|
138
|
+
invalidate_tunnel_app_info
|
139
|
+
|
140
|
+
push_caldecott(auth)
|
141
|
+
bind_service_banner(service, tunnel_appname, false)
|
142
|
+
start_caldecott
|
143
|
+
end
|
144
|
+
|
145
|
+
if not tunnel_bound?(service)
|
146
|
+
bind_service_banner(service, tunnel_appname)
|
147
|
+
end
|
148
|
+
|
149
|
+
conn_info = tunnel_connection_info info[:vendor], service, auth
|
150
|
+
display_tunnel_connection_info(conn_info)
|
151
|
+
start_tunnel(service, port, conn_info, auth)
|
152
|
+
|
153
|
+
clients = get_clients_for(info[:vendor])
|
154
|
+
if clients.empty?
|
155
|
+
which = "none"
|
156
|
+
else
|
157
|
+
which = client_name || ask(
|
158
|
+
"Which client would you like to start?",
|
159
|
+
:choices => ["none"] + clients.keys,
|
160
|
+
:indexed => true
|
161
|
+
)
|
162
|
+
end
|
163
|
+
|
164
|
+
if which == "none"
|
165
|
+
wait_for_tunnel_end
|
166
|
+
else
|
167
|
+
wait_for_tunnel_start(port)
|
168
|
+
start_local_prog(which, local_prog_cmdline(clients[which], port, conn_info))
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
def get_clients_for(type)
|
173
|
+
conf = VMC::Cli::Config.clients
|
174
|
+
conf[type] || {}
|
175
|
+
end
|
84
176
|
end
|
85
177
|
end
|
data/lib/cli/commands/user.rb
CHANGED
data/lib/cli/config.rb
CHANGED
@@ -14,6 +14,9 @@ module VMC::Cli
|
|
14
14
|
TOKEN_FILE = '~/.vmc_token'
|
15
15
|
INSTANCES_FILE = '~/.vmc_instances'
|
16
16
|
ALIASES_FILE = '~/.vmc_aliases'
|
17
|
+
CLIENTS_FILE = '~/.vmc_clients'
|
18
|
+
|
19
|
+
STOCK_CLIENTS = File.expand_path("../../../config/clients.yml", __FILE__)
|
17
20
|
|
18
21
|
class << self
|
19
22
|
attr_accessor :colorize
|
@@ -102,6 +105,30 @@ module VMC::Cli
|
|
102
105
|
File.open(aliases_file, 'wb') {|f| f.write(aliases.to_yaml)}
|
103
106
|
end
|
104
107
|
|
108
|
+
def deep_merge(a, b)
|
109
|
+
merge = proc do |_, old, new|
|
110
|
+
if new.is_a?(Hash) and old.is_a?(Hash)
|
111
|
+
old.merge(new, &merge)
|
112
|
+
else
|
113
|
+
new
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
a.merge(b, &merge)
|
118
|
+
end
|
119
|
+
|
120
|
+
def clients
|
121
|
+
return @clients if @clients
|
122
|
+
|
123
|
+
stock = YAML.load_file(STOCK_CLIENTS)
|
124
|
+
if File.exists? CLIENTS_FILE
|
125
|
+
user = YAML.load_file(CLIENTS_FILE)
|
126
|
+
@clients = deep_merge(stock, user)
|
127
|
+
else
|
128
|
+
@clients = stock
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
105
132
|
def lock_and_read(file)
|
106
133
|
File.open(file, "r") {|f|
|
107
134
|
f.flock(File::LOCK_EX)
|
data/lib/cli/runner.rb
CHANGED
@@ -77,6 +77,8 @@ class VMC::Cli::Runner
|
|
77
77
|
opts.on('-v', '--version') { set_cmd(:misc, :version) }
|
78
78
|
opts.on('-h', '--help') { puts "#{command_usage}\n"; exit }
|
79
79
|
|
80
|
+
opts.on('--port PORT') { |port| @options[:port] = port }
|
81
|
+
|
80
82
|
opts.on('--runtime RUNTIME') { |rt| @options[:runtime] = rt }
|
81
83
|
|
82
84
|
# deprecated
|
@@ -367,6 +369,12 @@ class VMC::Cli::Runner
|
|
367
369
|
usage('vmc unalias <alias>')
|
368
370
|
set_cmd(:misc, :unalias, 1)
|
369
371
|
|
372
|
+
when 'tunnel'
|
373
|
+
usage('vmc tunnel [servicename] [clientcmd] [--port port]')
|
374
|
+
set_cmd(:services, :tunnel, 0) if @args.size == 0
|
375
|
+
set_cmd(:services, :tunnel, 1) if @args.size == 1
|
376
|
+
set_cmd(:services, :tunnel, 2) if @args.size == 2
|
377
|
+
|
370
378
|
when 'help'
|
371
379
|
display_help if @args.size == 0
|
372
380
|
@help_only = true
|
@@ -0,0 +1,289 @@
|
|
1
|
+
# Copyright (c) 2009-2011 VMware, Inc.
|
2
|
+
|
3
|
+
require 'addressable/uri'
|
4
|
+
|
5
|
+
begin
|
6
|
+
require 'caldecott'
|
7
|
+
rescue LoadError
|
8
|
+
end
|
9
|
+
|
10
|
+
module VMC::Cli
|
11
|
+
module TunnelHelper
|
12
|
+
PORT_RANGE = 10
|
13
|
+
|
14
|
+
HELPER_APP = File.expand_path("../../../caldecott_helper", __FILE__)
|
15
|
+
|
16
|
+
# bump this AND the version info reported by HELPER_APP/server.rb
|
17
|
+
# this is to keep the helper in sync with any updates here
|
18
|
+
HELPER_VERSION = '0.0.4'
|
19
|
+
|
20
|
+
def tunnel_uniquename
|
21
|
+
random_service_name(tunnel_appname)
|
22
|
+
end
|
23
|
+
|
24
|
+
def tunnel_appname
|
25
|
+
"caldecott"
|
26
|
+
end
|
27
|
+
|
28
|
+
def tunnel_app_info
|
29
|
+
return @tun_app_info if @tunnel_app_info
|
30
|
+
begin
|
31
|
+
@tun_app_info = client.app_info(tunnel_appname)
|
32
|
+
rescue => e
|
33
|
+
@tun_app_info = nil
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def tunnel_url
|
38
|
+
return @tunnel_url if @tunnel_url
|
39
|
+
|
40
|
+
tun_url = tunnel_app_info[:uris][0]
|
41
|
+
|
42
|
+
["https", "http"].each do |scheme|
|
43
|
+
url = "#{scheme}://#{tun_url}"
|
44
|
+
begin
|
45
|
+
RestClient.get(url)
|
46
|
+
|
47
|
+
# https failed
|
48
|
+
rescue Errno::ECONNREFUSED
|
49
|
+
|
50
|
+
# we expect a 404 since this request isn't auth'd
|
51
|
+
rescue RestClient::ResourceNotFound
|
52
|
+
return @tunnel_url = url
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
err "Cannot determine URL for #{tun_url}"
|
57
|
+
end
|
58
|
+
|
59
|
+
def invalidate_tunnel_app_info
|
60
|
+
@tunnel_url = nil
|
61
|
+
@tunnel_app_info = nil
|
62
|
+
end
|
63
|
+
|
64
|
+
def tunnel_pushed?
|
65
|
+
not tunnel_app_info.nil?
|
66
|
+
end
|
67
|
+
|
68
|
+
def tunnel_healthy?(token)
|
69
|
+
return false unless tunnel_app_info[:state] == 'STARTED'
|
70
|
+
|
71
|
+
begin
|
72
|
+
response = RestClient.get(
|
73
|
+
"#{tunnel_url}/info",
|
74
|
+
"Auth-Token" => token
|
75
|
+
)
|
76
|
+
|
77
|
+
info = JSON.parse(response)
|
78
|
+
if info["version"] == HELPER_VERSION
|
79
|
+
true
|
80
|
+
else
|
81
|
+
stop_caldecott
|
82
|
+
false
|
83
|
+
end
|
84
|
+
rescue RestClient::Exception
|
85
|
+
stop_caldecott
|
86
|
+
false
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def tunnel_bound?(service)
|
91
|
+
tunnel_app_info[:services].include?(service)
|
92
|
+
end
|
93
|
+
|
94
|
+
def tunnel_connection_info(type, service, token)
|
95
|
+
display "Getting tunnel connection info: ", false
|
96
|
+
response = nil
|
97
|
+
10.times do
|
98
|
+
begin
|
99
|
+
response = RestClient.get("#{tunnel_url}/services/#{service}", "Auth-Token" => token)
|
100
|
+
break
|
101
|
+
rescue RestClient::Exception
|
102
|
+
sleep 1
|
103
|
+
end
|
104
|
+
|
105
|
+
display ".", false
|
106
|
+
end
|
107
|
+
|
108
|
+
unless response
|
109
|
+
err "Expected remote tunnel to know about #{service}, but it doesn't"
|
110
|
+
end
|
111
|
+
|
112
|
+
display "OK".green
|
113
|
+
|
114
|
+
info = JSON.parse(response)
|
115
|
+
case type
|
116
|
+
when "rabbitmq"
|
117
|
+
uri = Addressable::URI.parse info["url"]
|
118
|
+
info["hostname"] = uri.host
|
119
|
+
info["port"] = uri.port
|
120
|
+
info["vhost"] = uri.path[1..-1]
|
121
|
+
info["user"] = uri.user
|
122
|
+
info["password"] = uri.password
|
123
|
+
info.delete "url"
|
124
|
+
|
125
|
+
# we use "db" as the "name" for mongo
|
126
|
+
# existing "name" is junk
|
127
|
+
when "mongodb"
|
128
|
+
info["name"] = info["db"]
|
129
|
+
info.delete "db"
|
130
|
+
|
131
|
+
# our "name" is irrelevant for redis
|
132
|
+
when "redis"
|
133
|
+
info.delete "name"
|
134
|
+
end
|
135
|
+
|
136
|
+
['hostname', 'port', 'password'].each do |k|
|
137
|
+
err "Could not determine #{k} for #{service}" if info[k].nil?
|
138
|
+
end
|
139
|
+
|
140
|
+
info
|
141
|
+
end
|
142
|
+
|
143
|
+
def display_tunnel_connection_info(info)
|
144
|
+
display ''
|
145
|
+
display "Service connection info: "
|
146
|
+
|
147
|
+
to_show = [nil, nil, nil] # reserved for user, pass, db name
|
148
|
+
info.keys.each do |k|
|
149
|
+
case k
|
150
|
+
when "host", "hostname", "port", "node_id"
|
151
|
+
# skip
|
152
|
+
when "user", "username"
|
153
|
+
# prefer "username" over "user"
|
154
|
+
to_show[0] = k unless to_show[0] == "username"
|
155
|
+
when "password"
|
156
|
+
to_show[1] = k
|
157
|
+
when "name"
|
158
|
+
to_show[2] = k
|
159
|
+
else
|
160
|
+
to_show << k
|
161
|
+
end
|
162
|
+
end
|
163
|
+
to_show.compact!
|
164
|
+
|
165
|
+
align_len = to_show.collect(&:size).max + 1
|
166
|
+
|
167
|
+
to_show.each do |k|
|
168
|
+
# TODO: modify the server services rest call to have explicit knowledge
|
169
|
+
# about the items to return. It should return all of them if
|
170
|
+
# the service is unknown so that we don't have to do this weird
|
171
|
+
# filtering.
|
172
|
+
display " #{k.ljust align_len}: ", false
|
173
|
+
display "#{info[k]}".yellow
|
174
|
+
end
|
175
|
+
display ''
|
176
|
+
end
|
177
|
+
|
178
|
+
def start_tunnel(service, local_port, conn_info, auth)
|
179
|
+
display "Starting tunnel to #{service.bold} on port #{local_port.to_s.bold}."
|
180
|
+
|
181
|
+
@local_tunnel_thread = Thread.new do
|
182
|
+
Caldecott::Client.start({
|
183
|
+
:local_port => local_port,
|
184
|
+
:tun_url => tunnel_url,
|
185
|
+
:dst_host => conn_info['hostname'],
|
186
|
+
:dst_port => conn_info['port'],
|
187
|
+
:log_file => STDOUT,
|
188
|
+
:log_level => "ERROR",
|
189
|
+
:auth_token => auth,
|
190
|
+
:quiet => true
|
191
|
+
})
|
192
|
+
end
|
193
|
+
|
194
|
+
at_exit { @local_tunnel_thread.kill }
|
195
|
+
end
|
196
|
+
|
197
|
+
def pick_tunnel_port(port)
|
198
|
+
original = port
|
199
|
+
|
200
|
+
PORT_RANGE.times do |n|
|
201
|
+
begin
|
202
|
+
TCPSocket.open('localhost', port)
|
203
|
+
port += 1
|
204
|
+
rescue => e
|
205
|
+
return port
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
209
|
+
err "Could not pick a port between #{original} and #{original + PORT_RANGE - 1}"
|
210
|
+
end
|
211
|
+
|
212
|
+
def wait_for_tunnel_start(port)
|
213
|
+
10.times do |n|
|
214
|
+
begin
|
215
|
+
TCPSocket.open('localhost', port)
|
216
|
+
display '' if n > 0
|
217
|
+
return true
|
218
|
+
rescue => e
|
219
|
+
display "Waiting for local tunnel to become available", false if n == 0
|
220
|
+
display '.', false
|
221
|
+
sleep 1
|
222
|
+
end
|
223
|
+
end
|
224
|
+
err "Could not connect to local tunnel."
|
225
|
+
end
|
226
|
+
|
227
|
+
def wait_for_tunnel_end
|
228
|
+
display "Open another shell to run command-line clients or"
|
229
|
+
display "use a UI tool to connect using the displayed information."
|
230
|
+
display "Press Ctrl-C to exit..."
|
231
|
+
@local_tunnel_thread.join
|
232
|
+
end
|
233
|
+
|
234
|
+
def local_prog_cmdline(command, local_port, tunnel_info)
|
235
|
+
cmd = command.dup
|
236
|
+
cmd.gsub!(/\$\{\s*([^\}]+)\s*\}/) do
|
237
|
+
case $1
|
238
|
+
when "host"
|
239
|
+
# TODO: determine proper host
|
240
|
+
"localhost"
|
241
|
+
when "port"
|
242
|
+
local_port
|
243
|
+
when "user", "username"
|
244
|
+
tunnel_info["username"]
|
245
|
+
else
|
246
|
+
tunnel_info[$1] || err("Unknown symbol '#{$1}'")
|
247
|
+
end
|
248
|
+
end
|
249
|
+
cmd
|
250
|
+
end
|
251
|
+
|
252
|
+
def start_local_prog(which, cmdline)
|
253
|
+
display "Launching '#{cmdline}'"
|
254
|
+
display ''
|
255
|
+
unless system(cmdline)
|
256
|
+
err "Failed to start '#{which}' client; is it in your $PATH?"
|
257
|
+
end
|
258
|
+
end
|
259
|
+
|
260
|
+
def push_caldecott(token)
|
261
|
+
client.create_app(
|
262
|
+
tunnel_appname,
|
263
|
+
{ :name => tunnel_appname,
|
264
|
+
:staging => {:framework => "sinatra"},
|
265
|
+
:uris => ["#{tunnel_uniquename}.#{VMC::Cli::Config.suggest_url}"],
|
266
|
+
:instances => 1,
|
267
|
+
:resources => {:memory => 64},
|
268
|
+
:env => ["CALDECOTT_AUTH=#{token}"]
|
269
|
+
}
|
270
|
+
)
|
271
|
+
|
272
|
+
Command::Apps.new({}).send(:upload_app_bits, tunnel_appname, HELPER_APP)
|
273
|
+
|
274
|
+
invalidate_tunnel_app_info
|
275
|
+
end
|
276
|
+
|
277
|
+
def stop_caldecott
|
278
|
+
Command::Apps.new({}).stop(tunnel_appname)
|
279
|
+
|
280
|
+
invalidate_tunnel_app_info
|
281
|
+
end
|
282
|
+
|
283
|
+
def start_caldecott
|
284
|
+
Command::Apps.new({}).start(tunnel_appname)
|
285
|
+
|
286
|
+
invalidate_tunnel_app_info
|
287
|
+
end
|
288
|
+
end
|
289
|
+
end
|
data/lib/cli/usage.rb
CHANGED
@@ -78,6 +78,8 @@ Currently available vmc commands are:
|
|
78
78
|
bind-service <servicename> <appname> Bind a service to an application
|
79
79
|
unbind-service <servicename> <appname> Unbind service from the application
|
80
80
|
clone-services <src-app> <dest-app> Clone service bindings from <src-app> application to <dest-app>
|
81
|
+
tunnel <servicename> [--port] Create a local tunnel to a service
|
82
|
+
tunnel <servicename> <clientcmd> Create a local tunnel to a service and start a local client
|
81
83
|
|
82
84
|
Administration
|
83
85
|
user Display user account information
|
data/lib/cli/version.rb
CHANGED
metadata
CHANGED
@@ -1,8 +1,8 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: vmc
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
prerelease:
|
5
|
-
version: 0.3.
|
4
|
+
prerelease: 7
|
5
|
+
version: 0.3.14.beta.3
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
8
8
|
- VMware
|
@@ -10,7 +10,7 @@ autorequire:
|
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
12
|
|
13
|
-
date: 2011-11-
|
13
|
+
date: 2011-11-14 00:00:00 -08:00
|
14
14
|
default_executable:
|
15
15
|
dependencies:
|
16
16
|
- !ruby/object:Gem::Dependency
|
@@ -68,42 +68,53 @@ dependencies:
|
|
68
68
|
requirements:
|
69
69
|
- - ~>
|
70
70
|
- !ruby/object:Gem::Version
|
71
|
-
version: 0.
|
71
|
+
version: 0.3.0
|
72
72
|
type: :runtime
|
73
73
|
version_requirements: *id005
|
74
74
|
- !ruby/object:Gem::Dependency
|
75
|
-
name:
|
75
|
+
name: addressable
|
76
76
|
prerelease: false
|
77
77
|
requirement: &id006 !ruby/object:Gem::Requirement
|
78
|
+
none: false
|
79
|
+
requirements:
|
80
|
+
- - ~>
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: 2.2.6
|
83
|
+
type: :runtime
|
84
|
+
version_requirements: *id006
|
85
|
+
- !ruby/object:Gem::Dependency
|
86
|
+
name: rake
|
87
|
+
prerelease: false
|
88
|
+
requirement: &id007 !ruby/object:Gem::Requirement
|
78
89
|
none: false
|
79
90
|
requirements:
|
80
91
|
- - ">="
|
81
92
|
- !ruby/object:Gem::Version
|
82
93
|
version: "0"
|
83
94
|
type: :development
|
84
|
-
version_requirements: *
|
95
|
+
version_requirements: *id007
|
85
96
|
- !ruby/object:Gem::Dependency
|
86
97
|
name: rspec
|
87
98
|
prerelease: false
|
88
|
-
requirement: &
|
99
|
+
requirement: &id008 !ruby/object:Gem::Requirement
|
89
100
|
none: false
|
90
101
|
requirements:
|
91
102
|
- - ~>
|
92
103
|
- !ruby/object:Gem::Version
|
93
104
|
version: 1.3.0
|
94
105
|
type: :development
|
95
|
-
version_requirements: *
|
106
|
+
version_requirements: *id008
|
96
107
|
- !ruby/object:Gem::Dependency
|
97
108
|
name: webmock
|
98
109
|
prerelease: false
|
99
|
-
requirement: &
|
110
|
+
requirement: &id009 !ruby/object:Gem::Requirement
|
100
111
|
none: false
|
101
112
|
requirements:
|
102
113
|
- - "="
|
103
114
|
- !ruby/object:Gem::Version
|
104
115
|
version: 1.5.0
|
105
116
|
type: :development
|
106
|
-
version_requirements: *
|
117
|
+
version_requirements: *id009
|
107
118
|
description: Client library and CLI that provides access to the VMware Cloud Application Platform.
|
108
119
|
email: support@vmware.com
|
109
120
|
executables:
|
@@ -117,6 +128,7 @@ files:
|
|
117
128
|
- LICENSE
|
118
129
|
- README.md
|
119
130
|
- Rakefile
|
131
|
+
- config/clients.yml
|
120
132
|
- lib/cli/commands/admin.rb
|
121
133
|
- lib/cli/commands/apps.rb
|
122
134
|
- lib/cli/commands/base.rb
|
@@ -129,6 +141,7 @@ files:
|
|
129
141
|
- lib/cli/frameworks.rb
|
130
142
|
- lib/cli/runner.rb
|
131
143
|
- lib/cli/services_helper.rb
|
144
|
+
- lib/cli/tunnel_helper.rb
|
132
145
|
- lib/cli/usage.rb
|
133
146
|
- lib/cli/version.rb
|
134
147
|
- lib/cli/zip_util.rb
|
@@ -136,6 +149,9 @@ files:
|
|
136
149
|
- lib/vmc/client.rb
|
137
150
|
- lib/vmc/const.rb
|
138
151
|
- lib/vmc.rb
|
152
|
+
- caldecott_helper/Gemfile
|
153
|
+
- caldecott_helper/Gemfile.lock
|
154
|
+
- caldecott_helper/server.rb
|
139
155
|
- bin/vmc
|
140
156
|
has_rdoc: true
|
141
157
|
homepage: http://vmware.com
|
@@ -155,9 +171,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
155
171
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
156
172
|
none: false
|
157
173
|
requirements:
|
158
|
-
- - "
|
174
|
+
- - ">"
|
159
175
|
- !ruby/object:Gem::Version
|
160
|
-
version:
|
176
|
+
version: 1.3.1
|
161
177
|
requirements: []
|
162
178
|
|
163
179
|
rubyforge_project:
|