easy-serve 0.9 → 0.10
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.
- 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
|