vmc 0.3.13 → 0.3.14.beta.3
Sign up to get free protection for your applications and to get access to all the features.
- 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:
|