easy-serve 0.9 → 0.10
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +13 -0
- data/examples/tunnel/client.rb +33 -0
- data/examples/tunnel/server.rb +43 -0
- data/lib/easy-serve.rb +85 -3
- data/lib/easy-serve/remote.rb +3 -3
- data/lib/easy-serve/{remote-drb.rb → remote/drb.rb} +0 -0
- data/lib/easy-serve/{remote-eval-mgr.rb → remote/eval-mgr.rb} +0 -0
- data/lib/easy-serve/{remote-eval.rb → remote/eval.rb} +1 -2
- data/lib/easy-serve/{remote-run-mgr.rb → remote/run-mgr.rb} +0 -0
- data/lib/easy-serve/{remote-run.rb → remote/run.rb} +1 -2
- data/lib/easy-serve/service.rb +23 -22
- data/lib/easy-serve/service/accessible.rb +81 -0
- data/lib/easy-serve/service/tunnelled.rb +53 -0
- metadata +18 -15
- data/lib/easy-serve/accessible-services.rb +0 -51
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d74d8eda371150176555b9637f8e20b237edf068
|
4
|
+
data.tar.gz: ae3c58392fe3da0cb5e316f814a2a3b60e171087
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 299b6abc59f540f9dd71f53d933ff5638cba9757a07fc9290c3bae5599868a3a9414e59c988d126582bd2f87a414fcff3dfcd7bfa3497f209bac8a894ae24919
|
7
|
+
data.tar.gz: fa63d61b24de5644289ac3711d15a25f1b869c547b46bbc733c5da17ca4849a2d280d7d44625f10648aadac6befda81fa35519c1d4a0bd5af6fd9489ce3c5a4d
|
data/README.md
CHANGED
@@ -2,3 +2,16 @@ easy-serve
|
|
2
2
|
==========
|
3
3
|
|
4
4
|
Framework for starting tcp/unix services and connected clients under one parent process and on remote hosts.
|
5
|
+
|
6
|
+
use cases
|
7
|
+
---------
|
8
|
+
|
9
|
+
1. start some procs with unix sockets established among them and
|
10
|
+
clean up afterwards [simple](examples/simple.rb) [multi](examples/multi.rb)
|
11
|
+
|
12
|
+
2. ditto but with tcp and possibly [remote](examples/remote-eval.rb)
|
13
|
+
|
14
|
+
3. ditto but through ssh [tunnels](examples/remote-eval.rb)
|
15
|
+
|
16
|
+
4. ditto but where the tunnel is set up by the remote client, without
|
17
|
+
special assistance from the server [examples/tunnel](examples/tunnel)
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'easy-serve/remote'
|
2
|
+
|
3
|
+
services_file = ARGV.shift
|
4
|
+
|
5
|
+
unless services_file
|
6
|
+
abort <<-END
|
7
|
+
|
8
|
+
Usage: #$0 services.yaml
|
9
|
+
|
10
|
+
Reads the yaml file and tunnels to the service. Note that
|
11
|
+
the filename may be remote, like host:path, so the lazy way
|
12
|
+
to run the example is:
|
13
|
+
|
14
|
+
host1$ ruby server.rb sv
|
15
|
+
|
16
|
+
host2$ ruby client.rb host1:path/to/sv
|
17
|
+
|
18
|
+
END
|
19
|
+
end
|
20
|
+
|
21
|
+
EasyServe.start services_file: services_file do |ez|
|
22
|
+
log = ez.log
|
23
|
+
log.level = Logger::INFO
|
24
|
+
log.formatter = nil if $VERBOSE
|
25
|
+
|
26
|
+
ez.tunnel_to_remote_services
|
27
|
+
|
28
|
+
ez.child "hello-service" do |conn|
|
29
|
+
log.progname = "client 1"
|
30
|
+
log.info conn.read
|
31
|
+
conn.write "hello from #{log.progname}"
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'easy-serve'
|
2
|
+
|
3
|
+
services_file = ARGV.shift
|
4
|
+
|
5
|
+
unless services_file
|
6
|
+
abort <<-END
|
7
|
+
|
8
|
+
Usage: #$0 services.yaml
|
9
|
+
|
10
|
+
Creates the yaml file and sets up the service. The service is
|
11
|
+
listening only on localhost, so remote clients must use tunnels.
|
12
|
+
Does not set up any tunnels or remote clients. See client.rb.
|
13
|
+
|
14
|
+
END
|
15
|
+
end
|
16
|
+
|
17
|
+
EasyServe.start services_file: services_file do |ez|
|
18
|
+
log = ez.log
|
19
|
+
log.level = Logger::INFO
|
20
|
+
log.formatter = nil if $VERBOSE
|
21
|
+
|
22
|
+
ez.start_services do
|
23
|
+
host = "localhost" # no remote access, except by tunnel
|
24
|
+
ez.service "hello-service", :tcp, bind_host: host do |svr|
|
25
|
+
Thread.new do
|
26
|
+
loop do
|
27
|
+
Thread.new(svr.accept) do |conn|
|
28
|
+
log.info "accepted connection from #{conn.inspect}"
|
29
|
+
conn.write "hello from #{log.progname}"
|
30
|
+
log.info "wrote greeting"
|
31
|
+
conn.close_write
|
32
|
+
log.info "trying to read from #{conn.inspect}"
|
33
|
+
log.info "received: #{conn.read}"
|
34
|
+
conn.close
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
puts "PRESS RETURN TO STOP"
|
42
|
+
gets
|
43
|
+
end
|
data/lib/easy-serve.rb
CHANGED
@@ -6,7 +6,7 @@ require 'fileutils'
|
|
6
6
|
require 'easy-serve/service'
|
7
7
|
|
8
8
|
class EasyServe
|
9
|
-
VERSION = "0.
|
9
|
+
VERSION = "0.10"
|
10
10
|
|
11
11
|
class EasyFormatter < Logger::Formatter
|
12
12
|
Format = "%s: %s: %s\n"
|
@@ -35,6 +35,11 @@ class EasyServe
|
|
35
35
|
attr_reader :services_file
|
36
36
|
attr_reader :interactive
|
37
37
|
|
38
|
+
# Is this a sibling process, started by the same parent process that
|
39
|
+
# started the services, even if started remotely?
|
40
|
+
# Implies not owner, but not conversely.
|
41
|
+
attr_reader :sibling
|
42
|
+
|
38
43
|
def self.start(log: default_logger, **opts)
|
39
44
|
ez = new(**opts, log: log)
|
40
45
|
yield ez
|
@@ -45,6 +50,24 @@ class EasyServe
|
|
45
50
|
ez.cleanup if ez
|
46
51
|
end
|
47
52
|
|
53
|
+
# Options:
|
54
|
+
#
|
55
|
+
# services_file: filename
|
56
|
+
#
|
57
|
+
# name of file that server addresses are written to (if this process
|
58
|
+
# is creating them) or read from (if this process is accessing them).
|
59
|
+
# If not specified, services will be available to child processes,
|
60
|
+
# but harder to access from other processes.
|
61
|
+
#
|
62
|
+
# If the filename has a ':' in it, we assume that it is a remote
|
63
|
+
# file, specified as [user@]host:path/to/file as in scp and rsync,
|
64
|
+
# and attempt to read its contents over an ssh connection.
|
65
|
+
#
|
66
|
+
# interactive: true|false
|
67
|
+
#
|
68
|
+
# true means do not propagate ^C to child processes.
|
69
|
+
# This is useful primarily when running in irb.
|
70
|
+
#
|
48
71
|
def initialize **opts
|
49
72
|
@services_file = opts[:services_file]
|
50
73
|
@interactive = opts[:interactive]
|
@@ -52,6 +75,8 @@ class EasyServe
|
|
52
75
|
@children = [] # pid
|
53
76
|
@passive_children = [] # pid
|
54
77
|
@owner = false
|
78
|
+
@sibling = true
|
79
|
+
@ssh_sessions = []
|
55
80
|
@tmpdir = nil
|
56
81
|
@services = opts[:services] # name => service
|
57
82
|
|
@@ -70,10 +95,21 @@ class EasyServe
|
|
70
95
|
end
|
71
96
|
|
72
97
|
def load_service_table
|
73
|
-
|
74
|
-
|
98
|
+
case services_file
|
99
|
+
when /\A(\S*):(.*)/
|
100
|
+
IO.popen ["ssh", $1, "cat #$2"], "r" do |f|
|
101
|
+
load_service_table_from_io f
|
102
|
+
end
|
103
|
+
else
|
104
|
+
File.open(services_file) do |f|
|
105
|
+
load_service_table_from_io f
|
106
|
+
end
|
75
107
|
end
|
76
108
|
end
|
109
|
+
|
110
|
+
def load_service_table_from_io io
|
111
|
+
YAML.load(io).tap {@sibling = false}
|
112
|
+
end
|
77
113
|
|
78
114
|
def init_service_table
|
79
115
|
@services ||= begin
|
@@ -161,6 +197,10 @@ class EasyServe
|
|
161
197
|
end
|
162
198
|
|
163
199
|
def host_name
|
200
|
+
EasyServe.host_name
|
201
|
+
end
|
202
|
+
|
203
|
+
def EasyServe.host_name
|
164
204
|
@host_name ||= begin
|
165
205
|
hn = Socket.gethostname
|
166
206
|
begin
|
@@ -175,6 +215,10 @@ class EasyServe
|
|
175
215
|
end
|
176
216
|
end
|
177
217
|
end
|
218
|
+
|
219
|
+
def EasyServe.ssh_supports_dynamic_ports_forwards
|
220
|
+
@ssh_6 ||= (Integer(`ssh -V 2>&1`[/OpenSSH_(\d)/i, 1]) >= 6 rescue false)
|
221
|
+
end
|
178
222
|
|
179
223
|
MAX_TRIES = 10
|
180
224
|
|
@@ -246,4 +290,42 @@ class EasyServe
|
|
246
290
|
def no_interrupt_if_interactive
|
247
291
|
trap("INT") {} if interactive
|
248
292
|
end
|
293
|
+
|
294
|
+
# Returns list of services that are accessible from +host+, setting
|
295
|
+
# up an ssh tunnel if specified. This is for the 'ssh -R' type of tunneling:
|
296
|
+
# a process, started remotely by some main process, needs to connect back to
|
297
|
+
# its siblings, other children of that main process. OpenSSH 6.0 or later is
|
298
|
+
# advised, but not necessary, for the tunnel option.
|
299
|
+
def accessible_services host, tunnel: false
|
300
|
+
tcp_svs = services.values.grep(TCPService)
|
301
|
+
return tcp_svs unless tunnel and host != "localhost" and host != "127.0.0.1"
|
302
|
+
|
303
|
+
require 'easy-serve/service/accessible'
|
304
|
+
|
305
|
+
tcp_svs.map do |service|
|
306
|
+
service, ssh_session = service.accessible(host, log)
|
307
|
+
@ssh_sessions << ssh_session # let GC close them
|
308
|
+
service
|
309
|
+
end
|
310
|
+
end
|
311
|
+
|
312
|
+
# Set up tunnels as needed and modify the service list so that connections
|
313
|
+
# will go to local endpoints in those cases. Call this method in non-sibling
|
314
|
+
# invocations, such as when the server file has been copied to a remote
|
315
|
+
# host and used to start a new client. This is for the 'ssh -L' type of
|
316
|
+
# tunneling: a process needs to connect to a cluster of remote EasyServe
|
317
|
+
# processes that already exist and do not know about this process.
|
318
|
+
def tunnel_to_remote_services
|
319
|
+
return if sibling
|
320
|
+
|
321
|
+
require 'easy-serve/service/tunnelled'
|
322
|
+
|
323
|
+
tunnelled_services = {}
|
324
|
+
services.each do |service_name, service|
|
325
|
+
service, ssh_session = service.tunnelled
|
326
|
+
tunnelled_services[service_name] = service
|
327
|
+
@ssh_sessions << ssh_session if ssh_session # let GC close them
|
328
|
+
end
|
329
|
+
@services = tunnelled_services
|
330
|
+
end
|
249
331
|
end
|
data/lib/easy-serve/remote.rb
CHANGED
@@ -7,15 +7,15 @@ class EasyServe
|
|
7
7
|
raise ArgumentError, "no host specified" unless host
|
8
8
|
|
9
9
|
if opts[:eval]
|
10
|
-
require 'easy-serve/remote
|
10
|
+
require 'easy-serve/remote/eval'
|
11
11
|
remote_eval(*service_names, host: host, **opts)
|
12
12
|
|
13
13
|
elsif opts[:file]
|
14
|
-
require 'easy-serve/remote
|
14
|
+
require 'easy-serve/remote/run'
|
15
15
|
remote_run(*service_names, host: host, **opts)
|
16
16
|
|
17
17
|
elsif block_given?
|
18
|
-
require 'easy-serve/remote
|
18
|
+
require 'easy-serve/remote/drb'
|
19
19
|
remote_drb(*service_names, host: host, **opts, &Proc.new)
|
20
20
|
|
21
21
|
else
|
File without changes
|
File without changes
|
@@ -1,5 +1,4 @@
|
|
1
1
|
require 'msgpack'
|
2
|
-
require 'easy-serve/accessible-services'
|
3
2
|
|
4
3
|
class EasyServe
|
5
4
|
# useful simple cases in testing and in production, but long eval strings
|
@@ -20,7 +19,7 @@ class EasyServe
|
|
20
19
|
|
21
20
|
IO.popen [
|
22
21
|
"ssh", host, "ruby",
|
23
|
-
"-r", "easy-serve/remote
|
22
|
+
"-r", "easy-serve/remote/eval-mgr",
|
24
23
|
"-e", "EasyServe.handle_remote_eval_messages"
|
25
24
|
],
|
26
25
|
"w+" do |ssh|
|
File without changes
|
@@ -1,5 +1,4 @@
|
|
1
1
|
require 'msgpack'
|
2
|
-
require 'easy-serve/accessible-services'
|
3
2
|
|
4
3
|
class EasyServe
|
5
4
|
# useful in production, though it requires remote lib files to be set up.
|
@@ -18,7 +17,7 @@ class EasyServe
|
|
18
17
|
|
19
18
|
IO.popen [
|
20
19
|
"ssh", host, "ruby",
|
21
|
-
"-r", "easy-serve/remote
|
20
|
+
"-r", "easy-serve/remote/run-mgr",
|
22
21
|
"-e", "EasyServe.handle_remote_run_messages"
|
23
22
|
],
|
24
23
|
"w+" do |ssh|
|
data/lib/easy-serve/service.rb
CHANGED
@@ -3,25 +3,6 @@ class EasyServe
|
|
3
3
|
# Encapsulates current location and identity, including pid and address. A
|
4
4
|
# Service object can be serialized to a remote process so it can #connect to
|
5
5
|
# the service.
|
6
|
-
#
|
7
|
-
# The scheme for referencing hosts is as follows:
|
8
|
-
#
|
9
|
-
# bind host | connect host
|
10
|
-
# +------------------------------------------------------
|
11
|
-
# | local remote TCP SSH tunnel
|
12
|
-
# -----------+------------------------------------------------------
|
13
|
-
#
|
14
|
-
# localhost 'localhost' X 'localhost'
|
15
|
-
#
|
16
|
-
# 0.0.0.0 'localhost' hostname(*) 'localhost'
|
17
|
-
#
|
18
|
-
# hostname hostname hostname 'localhost'(**)
|
19
|
-
#
|
20
|
-
# * use hostname as best guess, can override; append ".local" if
|
21
|
-
# hostname not qualified
|
22
|
-
#
|
23
|
-
# ** forwarding set up to hostname[.local] instead of localhost
|
24
|
-
#
|
25
6
|
class Service
|
26
7
|
attr_reader :name
|
27
8
|
attr_reader :pid
|
@@ -111,14 +92,33 @@ class EasyServe
|
|
111
92
|
end
|
112
93
|
end
|
113
94
|
|
95
|
+
# The scheme for referencing TCP hosts is as follows:
|
96
|
+
#
|
97
|
+
# bind host | connect host
|
98
|
+
# +------------------------------------------------------
|
99
|
+
# | local remote TCP SSH tunnel
|
100
|
+
# -----------+------------------------------------------------------
|
101
|
+
#
|
102
|
+
# localhost 'localhost' X 'localhost'
|
103
|
+
#
|
104
|
+
# 0.0.0.0 'localhost' hostname(*) 'localhost'
|
105
|
+
#
|
106
|
+
# hostname hostname hostname 'localhost'(**)
|
107
|
+
#
|
108
|
+
# * use hostname as best guess, can override; append ".local" if
|
109
|
+
# hostname not qualified
|
110
|
+
#
|
111
|
+
# ** forwarding set up to hostname[.local] instead of localhost
|
112
|
+
#
|
114
113
|
class TCPService < Service
|
115
114
|
SERVICE_CLASS[:tcp] = self
|
116
115
|
|
117
|
-
attr_reader :bind_host, :connect_host, :port
|
116
|
+
attr_reader :bind_host, :connect_host, :host, :port
|
118
117
|
|
119
|
-
def initialize name, bind_host: nil, connect_host: nil, port: 0
|
118
|
+
def initialize name, bind_host: nil, connect_host: nil, host: nil, port: 0
|
120
119
|
super name
|
121
120
|
@bind_host, @connect_host, @port = bind_host, connect_host, port
|
121
|
+
@host ||= EasyServe.host_name
|
122
122
|
end
|
123
123
|
|
124
124
|
def serve max_tries: 1, log: log
|
@@ -126,6 +126,7 @@ class EasyServe
|
|
126
126
|
found_addr = svr.addr(false).values_at(2,1)
|
127
127
|
log.debug "#{inspect} is listening at #{found_addr.join(":")}"
|
128
128
|
@port = found_addr[1]
|
129
|
+
@bind_host ||= found_addr[0]
|
129
130
|
end
|
130
131
|
end
|
131
132
|
|
@@ -134,7 +135,7 @@ class EasyServe
|
|
134
135
|
end
|
135
136
|
|
136
137
|
def try_serve
|
137
|
-
TCPServer.new(bind_host, port)
|
138
|
+
TCPServer.new(bind_host, port || 0) # new(nil, nil) ==> error
|
138
139
|
end
|
139
140
|
|
140
141
|
def bump!
|
@@ -0,0 +1,81 @@
|
|
1
|
+
require 'easy-serve/service'
|
2
|
+
|
3
|
+
class EasyServe::TCPService
|
4
|
+
# Returns [service, ssh_session]. The service is modified based on self
|
5
|
+
# with tunneling from remote_host and ssh_session is the associated ssh pipe.
|
6
|
+
def accessible remote_host, log
|
7
|
+
service_host =
|
8
|
+
case bind_host
|
9
|
+
when nil, "localhost", "127.0.0.1", "0.0.0.0", /\A<any>\z/i
|
10
|
+
"localhost"
|
11
|
+
else
|
12
|
+
bind_host
|
13
|
+
end
|
14
|
+
|
15
|
+
fwd = "0:#{service_host}:#{port}"
|
16
|
+
remote_port = nil
|
17
|
+
ssh = nil
|
18
|
+
tries = 10
|
19
|
+
|
20
|
+
1.times do
|
21
|
+
if EasyServe.ssh_supports_dynamic_ports_forwards
|
22
|
+
remote_port = Integer(`ssh -O forward -R #{fwd} #{remote_host}`)
|
23
|
+
else
|
24
|
+
log.warn "Unable to set up dynamic ssh port forwarding. " +
|
25
|
+
"Please check if ssh -v is at least 6.0. " +
|
26
|
+
"Falling back to new ssh session."
|
27
|
+
|
28
|
+
code = <<-CODE
|
29
|
+
require 'socket'
|
30
|
+
svr = TCPServer.new "localhost", 0 # no rescue; error here is fatal
|
31
|
+
puts svr.addr[1]
|
32
|
+
svr.close
|
33
|
+
CODE
|
34
|
+
|
35
|
+
remote_port =
|
36
|
+
IO.popen ["ssh", remote_host, "ruby"], "w+" do |ruby|
|
37
|
+
ruby.puts code
|
38
|
+
ruby.close_write
|
39
|
+
Integer(ruby.gets)
|
40
|
+
end
|
41
|
+
|
42
|
+
cmd = [
|
43
|
+
"ssh", remote_host,
|
44
|
+
"-R", "#{remote_port}:#{service_host}:#{port}",
|
45
|
+
"echo ok && cat"
|
46
|
+
]
|
47
|
+
ssh = IO.popen cmd, "w+"
|
48
|
+
## how to tell if port in use and retry? ssh doesn't seem to fail,
|
49
|
+
## or maybe it fails by printing a message on the remote side
|
50
|
+
|
51
|
+
ssh.sync = true
|
52
|
+
line = ssh.gets
|
53
|
+
unless line and line.chomp == "ok" # wait for forwarding
|
54
|
+
raise "Could not start ssh forwarding: #{cmd.join(" ")}"
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
if remote_port == 0
|
59
|
+
log.warn "race condition in ssh selection of remote_port"
|
60
|
+
tries -= 1
|
61
|
+
if tries > 0
|
62
|
+
sleep 0.1
|
63
|
+
log.info "retrying ssh selection of remote_port"
|
64
|
+
redo
|
65
|
+
end
|
66
|
+
raise "ssh did not assign remote_port"
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
# This breaks with multiple forward requests, and it would be too hard
|
71
|
+
# to coordinate among all requesting processes, so let's leave the
|
72
|
+
# forwarding open:
|
73
|
+
#at_exit {system "ssh -O cancel -R #{fwd} #{remote_host}"}
|
74
|
+
|
75
|
+
service =
|
76
|
+
self.class.new name, host: host,
|
77
|
+
bind_host: bind_host, connect_host: "localhost", port: remote_port
|
78
|
+
|
79
|
+
return [service, ssh]
|
80
|
+
end
|
81
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
require 'easy-serve/service'
|
2
|
+
|
3
|
+
class EasyServe
|
4
|
+
class Service
|
5
|
+
def tunnelled(*)
|
6
|
+
[self, nil]
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
class TCPService
|
11
|
+
# Returns [service, ssh_session|nil]. The service is self and ssh_session
|
12
|
+
# is nil, unless tunneling is appropriate, in which case the returned
|
13
|
+
# service is the tunnelled one, and the ssh_session is the associated ssh
|
14
|
+
# pipe. This is for the 'ssh -L' type of tunneling: a process needs to
|
15
|
+
# connect to a cluster of remote EasyServe processes.
|
16
|
+
def tunnelled
|
17
|
+
return [self, nil] if
|
18
|
+
["localhost", "127.0.0.1", EasyServe.host_name].include? host
|
19
|
+
|
20
|
+
if ["localhost", "127.0.0.1", "0.0.0.0"].include? bind_host
|
21
|
+
rhost = "localhost"
|
22
|
+
else
|
23
|
+
rhost = bind_host
|
24
|
+
end
|
25
|
+
|
26
|
+
svr = TCPServer.new "localhost", 0 # no rescue; error here is fatal
|
27
|
+
lport = svr.addr[1]
|
28
|
+
svr.close
|
29
|
+
## why doesn't `ssh -L 0:host:port` work?
|
30
|
+
|
31
|
+
# possible alternative: ssh -f -N -o ExitOnForwardFailure: yes
|
32
|
+
cmd = [
|
33
|
+
"ssh", host,
|
34
|
+
"-L", "#{lport}:#{rhost}:#{port}",
|
35
|
+
"echo ok && cat"
|
36
|
+
]
|
37
|
+
ssh = IO.popen cmd, "w+"
|
38
|
+
## how to tell if lport in use and retry? ssh doesn't seem to fail,
|
39
|
+
## or maybe it fails by printing a message on the remote side
|
40
|
+
|
41
|
+
ssh.sync = true
|
42
|
+
line = ssh.gets
|
43
|
+
unless line and line.chomp == "ok" # wait for forwarding
|
44
|
+
raise "Could not start ssh forwarding: #{cmd.join(" ")}"
|
45
|
+
end
|
46
|
+
|
47
|
+
service = TCPService.new name,
|
48
|
+
bind_host: bind_host, connect_host: 'localhost', host: host, port: lport
|
49
|
+
|
50
|
+
return [service, ssh]
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: easy-serve
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: '0.
|
4
|
+
version: '0.10'
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Joel VanderWerf
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2013-12-
|
11
|
+
date: 2013-12-22 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: msgpack
|
@@ -35,25 +35,28 @@ extra_rdoc_files:
|
|
35
35
|
files:
|
36
36
|
- README.md
|
37
37
|
- COPYING
|
38
|
-
- lib/easy-serve.rb
|
39
|
-
- lib/easy-serve/remote-eval-mgr.rb
|
40
|
-
- lib/easy-serve/remote-run.rb
|
41
|
-
- lib/easy-serve/remote-drb.rb
|
42
38
|
- lib/easy-serve/remote.rb
|
43
|
-
- lib/easy-serve/accessible
|
44
|
-
- lib/easy-serve/
|
45
|
-
- lib/easy-serve/remote-run-mgr.rb
|
39
|
+
- lib/easy-serve/service/accessible.rb
|
40
|
+
- lib/easy-serve/service/tunnelled.rb
|
46
41
|
- lib/easy-serve/service.rb
|
47
|
-
-
|
48
|
-
-
|
49
|
-
-
|
50
|
-
-
|
42
|
+
- lib/easy-serve/remote/eval.rb
|
43
|
+
- lib/easy-serve/remote/run-mgr.rb
|
44
|
+
- lib/easy-serve/remote/run.rb
|
45
|
+
- lib/easy-serve/remote/drb.rb
|
46
|
+
- lib/easy-serve/remote/eval-mgr.rb
|
47
|
+
- lib/easy-serve.rb
|
51
48
|
- examples/remote-run.rb
|
52
|
-
- examples/remote-drb.rb
|
53
49
|
- examples/remote-multi-server.rb
|
50
|
+
- examples/passive.rb
|
51
|
+
- examples/remote-eval-passive.rb
|
52
|
+
- examples/remote-drb.rb
|
54
53
|
- examples/remote-run-script.rb
|
54
|
+
- examples/multi.rb
|
55
|
+
- examples/remote-manual.rb
|
56
|
+
- examples/simple.rb
|
57
|
+
- examples/tunnel/client.rb
|
58
|
+
- examples/tunnel/server.rb
|
55
59
|
- examples/remote-eval.rb
|
56
|
-
- examples/passive.rb
|
57
60
|
homepage: https://github.com/vjoel/easy-serve
|
58
61
|
licenses:
|
59
62
|
- BSD
|
@@ -1,51 +0,0 @@
|
|
1
|
-
class EasyServe
|
2
|
-
# Returns list of services that are accessible from +host+, setting
|
3
|
-
# up an ssh tunnel if specified. Note that OpenSSH 6.0 or later is required
|
4
|
-
# for the tunnel option.
|
5
|
-
def accessible_services host, tunnel: false
|
6
|
-
tcp_svs = services.values.grep(TCPService)
|
7
|
-
return tcp_svs unless tunnel and host != "localhost" and host != "127.0.0.1"
|
8
|
-
|
9
|
-
tcp_svs.map do |service|
|
10
|
-
service_host =
|
11
|
-
case service.bind_host
|
12
|
-
when nil, "localhost", "127.0.0.1", "0.0.0.0", /\A<any>\z/i
|
13
|
-
"localhost"
|
14
|
-
else
|
15
|
-
service.bind_host
|
16
|
-
end
|
17
|
-
|
18
|
-
fwd = "0:#{service_host}:#{service.port}"
|
19
|
-
remote_port = nil
|
20
|
-
tries = 10
|
21
|
-
1.times do
|
22
|
-
out = `ssh -O forward -R #{fwd} #{host}`
|
23
|
-
begin
|
24
|
-
remote_port = Integer(out)
|
25
|
-
rescue
|
26
|
-
log.error "Unable to set up dynamic ssh port forwarding. " +
|
27
|
-
"Please check if ssh -v is at least 6.0."
|
28
|
-
raise
|
29
|
-
end
|
30
|
-
|
31
|
-
if remote_port == 0
|
32
|
-
log.warn "race condition in ssh selection of remote_port"
|
33
|
-
tries -= 1
|
34
|
-
if tries > 0
|
35
|
-
sleep 0.1
|
36
|
-
log.info "retrying ssh selection of remote_port"
|
37
|
-
redo
|
38
|
-
end
|
39
|
-
raise "ssh did not assign remote_port"
|
40
|
-
end
|
41
|
-
end
|
42
|
-
|
43
|
-
# This breaks with multiple forward requests, and it would be too hard
|
44
|
-
# to coordinate among all requesting processes, so let's leave the
|
45
|
-
# forwarding open:
|
46
|
-
#at_exit {system "ssh -O cancel -R #{fwd} #{host}"}
|
47
|
-
|
48
|
-
TCPService.new service.name, connect_host: "localhost", port: remote_port
|
49
|
-
end
|
50
|
-
end
|
51
|
-
end
|