Class: EasyServe
- Inherits:
-
Object
- Object
- EasyServe
- Defined in:
- lib/easy-serve.rb,
lib/easy-serve/remote.rb,
lib/easy-serve/service.rb,
lib/easy-serve/remote/drb.rb,
lib/easy-serve/remote/run.rb,
lib/easy-serve/remote/eval.rb,
lib/easy-serve/service/tunnelled.rb
Defined Under Namespace
Classes: EasyFormatter, RemoteError, Service, ServicesExistError, TCPService, UNIXService
Constant Summary collapse
- VERSION =
"0.13"
- MAX_TRIES =
10
Instance Attribute Summary collapse
-
#children ⇒ Object
readonly
Returns the value of attribute children.
-
#interactive ⇒ Object
readonly
True means do not propagate ^C to child processes.
-
#log ⇒ Object
Returns the value of attribute log.
-
#passive_children ⇒ Object
readonly
Returns the value of attribute passive_children.
-
#services ⇒ Object
Returns the value of attribute services.
-
#services_file ⇒ Object
readonly
Returns the value of attribute services_file.
-
#sibling ⇒ Object
readonly
Is this a sibling process, started by the same parent process that started the services, even if started remotely? Implies not owner, but not conversely.
Class Method Summary collapse
- .bump_socket_filename(name) ⇒ Object
- .default_logger ⇒ Object
- .handle_remote_eval_messages ⇒ Object
- .handle_remote_run_messages ⇒ Object
- .host_name ⇒ Object
- .manage_remote_eval_client(msg) ⇒ Object
- .manage_remote_run_client(msg) ⇒ Object
- .null_logger ⇒ Object
- .ssh_supports_dynamic_ports_forwards ⇒ Object
- .start(log: default_logger, **opts) ⇒ Object
Instance Method Summary collapse
-
#accessible_services(host, tunnel: false) ⇒ Object
Returns list of services that are accessible from
host
, setting up an ssh tunnel if specified. -
#child(*service_names, passive: false) ⇒ Object
A passive client child may be stopped after all active clients exit.
- #choose_socket_filename(name, base: nil) ⇒ Object
- #clean_tmpdir ⇒ Object
- #cleanup ⇒ Object
- #host_name ⇒ Object
- #init_service_table ⇒ Object
-
#initialize(**opts) ⇒ EasyServe
constructor
Options:.
- #load_service_table ⇒ Object
- #load_service_table_from_io(io) ⇒ Object
-
#local(*service_names) ⇒ Object
A local client runs in the same process, not a child process.
-
#no_interrupt_if_interactive ⇒ Object
^C in the irb session (parent process) should not kill the service (child process).
- #remote(*service_names, host: nil, **opts) ⇒ Object
-
#remote_drb(*service_names, host: nil) ⇒ Object
useful for testing only – use _eval or _run for production.
-
#remote_eval(*service_names, host: nil, passive: false, tunnel: false, **opts) ⇒ Object
useful simple cases in testing and in production, but long eval strings can be hard to debug – use _run instead.
-
#remote_run(*service_names, host: nil, passive: false, tunnel: false, **opts) ⇒ Object
useful in production, though it requires remote lib files to be set up.
-
#service(name, proto = nil, **opts) ⇒ Object
Start a service named
name
. - #start_services ⇒ Object
- #tmpdir ⇒ Object
-
#tunnel_to_remote_services ⇒ Object
Set up tunnels as needed and modify the service list so that connections will go to local endpoints in those cases.
-
#with_lock_file(dir, base) ⇒ Object
Returns true if this process got the lock.
Constructor Details
#initialize(**opts) ⇒ EasyServe
Options:
services_file: filename
name of file that server addresses are written to (if this process
is creating them) or read from (if this process is accessing them).
If not specified, services will be available to child processes,
but harder to access from other processes.
If the filename has a ':' in it, we assume that it is a remote
file, specified as [user@]host:path/to/file as in scp and rsync,
and attempt to read its contents over an ssh connection.
interactive: true|false
true means do not propagate ^C to child processes.
This is useful primarily when running in irb.
75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 |
# File 'lib/easy-serve.rb', line 75 def initialize **opts @services_file = opts[:services_file] @created_services_file = false @interactive = opts[:interactive] @log = opts[:log] || self.class.null_logger @children = [] # pid @passive_children = [] # pid @owner = false @sibling = true @ssh_sessions = [] @tmpdir = nil @services = opts[:services] # name => service unless services if services_file @services = begin load_service_table rescue Errno::ENOENT init_service_table end else init_service_table end end end |
Instance Attribute Details
#children ⇒ Object (readonly)
Returns the value of attribute children.
35 36 37 |
# File 'lib/easy-serve.rb', line 35 def children @children end |
#interactive ⇒ Object (readonly)
True means do not propagate ^C to child processes.
40 41 42 |
# File 'lib/easy-serve.rb', line 40 def interactive @interactive end |
#log ⇒ Object
Returns the value of attribute log.
33 34 35 |
# File 'lib/easy-serve.rb', line 33 def log @log end |
#passive_children ⇒ Object (readonly)
Returns the value of attribute passive_children.
36 37 38 |
# File 'lib/easy-serve.rb', line 36 def passive_children @passive_children end |
#services ⇒ Object
Returns the value of attribute services.
34 35 36 |
# File 'lib/easy-serve.rb', line 34 def services @services end |
#services_file ⇒ Object (readonly)
Returns the value of attribute services_file.
37 38 39 |
# File 'lib/easy-serve.rb', line 37 def services_file @services_file end |
#sibling ⇒ Object (readonly)
Is this a sibling process, started by the same parent process that started the services, even if started remotely? Implies not owner, but not conversely.
45 46 47 |
# File 'lib/easy-serve.rb', line 45 def sibling @sibling end |
Class Method Details
.bump_socket_filename(name) ⇒ Object
241 242 243 |
# File 'lib/easy-serve.rb', line 241 def self.bump_socket_filename name name =~ /-\d+\z/ ? name.succ : name + "-0" end |
.default_logger ⇒ Object
21 22 23 24 25 |
# File 'lib/easy-serve.rb', line 21 def self.default_logger log = Logger.new($stderr) log.formatter = EasyFormatter.new log end |
.handle_remote_eval_messages ⇒ Object
55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 |
# File 'lib/easy-serve/remote/eval-mgr.rb', line 55 def EasyServe. unpacker = MessagePack::Unpacker.new($stdin) unpacker.each do |msg| case when msg["service_names"] Thread.new {manage_remote_eval_client(msg); exit} when msg["exit"] puts "exiting" exit when msg["request"] response = self.send(*msg["command"]) puts "response: #{response.inspect}" else puts "unhandled: #{msg.inspect}" end end rescue => ex puts "ez error", ex, ex.backtrace end |
.handle_remote_run_messages ⇒ Object
57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 |
# File 'lib/easy-serve/remote/run-mgr.rb', line 57 def EasyServe. unpacker = MessagePack::Unpacker.new($stdin) unpacker.each do |msg| case when msg["service_names"] Thread.new {manage_remote_run_client(msg); exit} when msg["exit"] puts "exiting" exit when msg["request"] response = self.send(*msg["command"]) puts "response: #{response.inspect}" else puts "unhandled: #{msg.inspect}" end end rescue => ex puts "ez error", ex, ex.backtrace end |
.host_name ⇒ Object
249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 |
# File 'lib/easy-serve.rb', line 249 def EasyServe.host_name @host_name ||= begin hn = Socket.gethostname begin official_hostname = Socket.gethostbyname(hn)[0] if /\./ =~ official_hostname official_hostname else official_hostname + ".local" end rescue 'localhost' end end end |
.manage_remote_eval_client(msg) ⇒ Object
4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 |
# File 'lib/easy-serve/remote/eval-mgr.rb', line 4 def EasyServe.manage_remote_eval_client msg $VERBOSE = msg["verbose"] service_names, services_list, log_level, eval_string, host = msg.values_at(*%w{service_names services_list log_level eval_string host}) services = {} service_names = Marshal.load(service_names) services_list = Marshal.load(services_list) services_list.each do |service| services[service.name] = service end log_args = msg["log"] log = case log_args when Array Logger.new(*log_args) when true EasyServe.default_logger when nil, false EasyServe.null_logger end EasyServe.start services: services, log: log do |ez| log = ez.log log.level = log_level log.formatter = nil if $VERBOSE ez.local *service_names do |*conns| begin pr = eval "proc do |conns, host, log| #{eval_string}; end" pr[conns, host, log] rescue => ex puts "ez error", ex.inspect lineno = (Integer(ex.backtrace[0][/(\d+):/, 1]) rescue nil) if lineno lines = eval_string.lines puts " #{lineno-1} --> " + lines[lineno-1] end puts ex.backtrace end end log.info "done" end rescue LoadError, ScriptError, StandardError => ex puts "ez error", ex, ex.backtrace end |
.manage_remote_run_client(msg) ⇒ Object
4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 |
# File 'lib/easy-serve/remote/run-mgr.rb', line 4 def EasyServe.manage_remote_run_client msg $VERBOSE = msg["verbose"] service_names, services_list, log_level, host, dir, file, class_name, args = msg.values_at(*%w{ service_names services_list log_level host dir file class_name args }) services = {} service_names = Marshal.load(service_names) services_list = Marshal.load(services_list) services_list.each do |service| services[service.name] = service end ### opt for tmpdir and send files to it via ssh Dir.chdir(dir) if dir load file log_args = msg["log"] log = case log_args when Array Logger.new(*log_args) when true, :default EasyServe.default_logger when nil, false EasyServe.null_logger end EasyServe.start services: services, log: log do |ez| log = ez.log log.level = log_level log.formatter = nil if $VERBOSE ez.local *service_names do |*conns| begin cl = Object.const_get(class_name) ro = cl.new(conns, host, log, *args) ro.run rescue => ex puts "ez error", ex.inspect, ex.backtrace end end log.info "done" end rescue LoadError, ScriptError, StandardError => ex puts "ez error", ex, ex.backtrace end |
.null_logger ⇒ Object
27 28 29 30 31 |
# File 'lib/easy-serve.rb', line 27 def self.null_logger log = Logger.new('/dev/null') log.level = Logger::FATAL log end |
.ssh_supports_dynamic_ports_forwards ⇒ Object
265 266 267 |
# File 'lib/easy-serve.rb', line 265 def EasyServe.ssh_supports_dynamic_ports_forwards @ssh_6 ||= (Integer(`ssh -V 2>&1`[/OpenSSH_(\d)/i, 1]) >= 6 rescue false) end |
.start(log: default_logger, **opts) ⇒ Object
47 48 49 50 51 52 53 54 55 |
# File 'lib/easy-serve.rb', line 47 def self.start(log: default_logger, **opts) ez = new(**opts, log: log) yield ez rescue => ex log.error ex raise ensure ez.cleanup if ez end |
Instance Method Details
#accessible_services(host, tunnel: false) ⇒ Object
Returns list of services that are accessible from host
, setting up an ssh tunnel if specified. This is for the ‘ssh -R’ type of tunneling: a process, started remotely by some main process, needs to connect back to its siblings, other children of that main process. OpenSSH 6.0 or later is advised, but not necessary, for the tunnel option.
371 372 373 374 375 376 377 378 379 380 381 382 |
# File 'lib/easy-serve.rb', line 371 def accessible_services host, tunnel: false tcp_svs = services.values.grep(TCPService) return tcp_svs unless tunnel and host != "localhost" and host != "127.0.0.1" require 'easy-serve/service/accessible' tcp_svs.map do |service| service, ssh_session = service.accessible(host, log) @ssh_sessions << ssh_session # let GC close them service end end |
#child(*service_names, passive: false) ⇒ Object
A passive client child may be stopped after all active clients exit.
339 340 341 342 343 344 345 346 347 |
# File 'lib/easy-serve.rb', line 339 def child *service_names, passive: false c = fork do conns = service_names.map {|sn| services[sn].connect} yield(*conns) if block_given? no_interrupt_if_interactive end (passive ? passive_children : children) << c c end |
#choose_socket_filename(name, base: nil) ⇒ Object
233 234 235 236 237 238 239 |
# File 'lib/easy-serve.rb', line 233 def choose_socket_filename name, base: nil if base "#{base}-#{name}" else File.join(tmpdir, "sock-#{name}") ## permissions? end end |
#clean_tmpdir ⇒ Object
229 230 231 |
# File 'lib/easy-serve.rb', line 229 def clean_tmpdir FileUtils.remove_entry @tmpdir if @tmpdir end |
#cleanup ⇒ Object
126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 |
# File 'lib/easy-serve.rb', line 126 def cleanup handler = trap("INT") do trap("INT", handler) end children.each do |pid| log.debug {"waiting for client pid=#{pid} to stop"} begin Process.waitpid pid rescue Errno::ECHILD log.debug {"client pid=#{pid} was already waited for"} end end passive_children.each do |pid| log.debug {"stopping client pid=#{pid}"} Process.kill("TERM", pid) begin Process.waitpid pid rescue Errno::ECHILD log.debug {"client pid=#{pid} was already waited for"} end end if @owner services.each do |name, service| log.info "stopping #{name}" service.cleanup end if @created_services_file begin FileUtils.rm services_file rescue Errno::ENOENT log.warn "services file #{services_file.inspect} was deleted already" end end end clean_tmpdir end |
#host_name ⇒ Object
245 246 247 |
# File 'lib/easy-serve.rb', line 245 def host_name EasyServe.host_name end |
#init_service_table ⇒ Object
119 120 121 122 123 124 |
# File 'lib/easy-serve.rb', line 119 def init_service_table @services ||= begin @owner = true {} end end |
#load_service_table ⇒ Object
102 103 104 105 106 107 108 109 110 111 112 113 |
# File 'lib/easy-serve.rb', line 102 def load_service_table case services_file when /\A(\S*):(.*)/ IO.popen ["ssh", $1, "cat #$2"], "r" do |f| load_service_table_from_io f end else File.open(services_file) do |f| load_service_table_from_io f end end end |
#load_service_table_from_io(io) ⇒ Object
115 116 117 |
# File 'lib/easy-serve.rb', line 115 def load_service_table_from_io io YAML.load(io).tap {@sibling = false} end |
#local(*service_names) ⇒ Object
A local client runs in the same process, not a child process.
350 351 352 353 354 355 356 357 358 |
# File 'lib/easy-serve.rb', line 350 def local *service_names conns = service_names.map {|sn| services[sn].connect} yield(*conns) if block_given? ensure conns and conns.each do |conn| conn.close unless conn.closed? end log.info "stopped local client" end |
#no_interrupt_if_interactive ⇒ Object
^C in the irb session (parent process) should not kill the service (child process)
362 363 364 |
# File 'lib/easy-serve.rb', line 362 def no_interrupt_if_interactive trap("INT") {} if interactive end |
#remote(*service_names, host: nil, **opts) ⇒ Object
6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
# File 'lib/easy-serve/remote.rb', line 6 def remote *service_names, host: nil, **opts raise ArgumentError, "no host specified" unless host if opts[:eval] require 'easy-serve/remote/eval' remote_eval(*service_names, host: host, **opts) elsif opts[:file] require 'easy-serve/remote/run' remote_run(*service_names, host: host, **opts) elsif block_given? require 'easy-serve/remote/drb' remote_drb(*service_names, host: host, **opts, &Proc.new) else raise ArgumentError, "cannot select remote mode based on arguments" end end |
#remote_drb(*service_names, host: nil) ⇒ Object
useful for testing only – use _eval or _run for production. Note: as with #local, the code block runs in the main thread, by default. It’s up to you to start another thread inside the code block if you want more concurrency. This is for convenience when testing (cases in which concurrency needs to be controlled explicitly).
9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 |
# File 'lib/easy-serve/remote/drb.rb', line 9 def remote_drb *service_names, host: nil ## remote logfile option? DRb.start_service("druby://#{host_name}:0", nil) hostname = host.sub(/.*@/,"") host_uri = "druby://#{hostname}:0" log.progname = "remote_drb #{host}" IO.popen ["ssh", host, "ruby"], "w+" do |ssh| ssh.puts %Q{ $stdout.sync = true begin require 'drb' require 'yaml' require 'easy-serve' service_names = #{service_names.inspect} services = YAML.load(#{YAML.dump(services).inspect}) log_level = #{log.level} host_uri = #{host_uri.inspect} EasyServe.start services: services do |ez| log = ez.log log.level = log_level log.formatter = nil if $VERBOSE ez.local *service_names do |*conns| begin DRb.start_service(host_uri, {conns: conns}) puts DRb.uri Thread.new do loop do sleep 1 begin puts "." rescue exit end end end DRb.thread.join rescue => ex puts "ez error", ex, ex.backtrace end end end rescue => ex puts "ez error", ex, ex.backtrace end } ssh.close_write result = ssh.gets if !result raise RemoteError, "problem with ssh connection to remote" else error = result[/ez error/] if error raise RemoteError, "error raised in remote: #{ssh.read}" else uri = result[/druby:\/\/\S+/] if uri Thread.new do loop do ssh.gets # consume the "." end end log.debug "remote is at #{uri}" ro = DRbObject.new_with_uri(uri) conns = ro[:conns] conns_ary = [] conns.each {|c| conns_ary << c} # needed because it's a DRbObject yield(*conns_ary) if block_given? else raise RemoteError, "no druby uri in string from remote: #{result.inspect}" end end end end end |
#remote_eval(*service_names, host: nil, passive: false, tunnel: false, **opts) ⇒ Object
useful simple cases in testing and in production, but long eval strings can be hard to debug – use _run instead. Returns pid of child managing the ssh connection.
Note, unlike #local and #child, by default logging goes to the null logger. If you want to see logs from the remote, you need to choose:
-
Log to remote file: pass log: [args…] with args as in Logger.new
-
Log back over ssh: pass log: true.
15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 |
# File 'lib/easy-serve/remote/eval.rb', line 15 def remote_eval *service_names, host: nil, passive: false, tunnel: false, **opts child_pid = fork do log.progname = "remote_eval #{host}" IO.popen [ "ssh", host, "ruby", "-r", "easy-serve/remote/eval-mgr", "-e", "EasyServe.handle_remote_eval_messages" ], "w+" do |ssh| ssh.sync = true services_list = accessible_services(host, tunnel: tunnel) MessagePack.pack( { verbose: $VERBOSE, service_names: Marshal.dump(service_names), services_list: Marshal.dump(services_list), log_level: log.level, eval_string: opts[:eval], host: host, log: opts[:log] }, ssh) while s = ssh.gets case s when /^ez error/ raise RemoteError, "error raised in remote: #{ssh.read}" else puts s end end end end (passive ? passive_children : children) << child_pid child_pid end |
#remote_run(*service_names, host: nil, passive: false, tunnel: false, **opts) ⇒ Object
useful in production, though it requires remote lib files to be set up. Returns pid of child managing the ssh connection.
Note, unlike #local and #child, by default logging goes to the null logger. If you want to see logs from the remote, you need to choose:
-
Log to remote file: pass log: [args…] with args as in Logger.new
-
Log back over ssh: pass log: true.
14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 |
# File 'lib/easy-serve/remote/run.rb', line 14 def remote_run *service_names, host: nil, passive: false, tunnel: false, **opts child_pid = fork do log.progname = "remote_run #{host}" IO.popen [ "ssh", host, "ruby", "-r", "easy-serve/remote/run-mgr", "-e", "EasyServe.handle_remote_run_messages" ], "w+" do |ssh| ssh.sync = true services_list = accessible_services(host, tunnel: tunnel) MessagePack.pack( { verbose: $VERBOSE, service_names: Marshal.dump(service_names), services_list: Marshal.dump(services_list), log_level: log.level, host: host, dir: opts[:dir], file: opts[:file], class_name: opts[:class_name], args: opts[:args], log: opts[:log] }, ssh) while s = ssh.gets case s when /^ez error/ raise RemoteError, "error raised in remote: #{ssh.read}" else puts s end end end end (passive ? passive_children : children) << child_pid child_pid end |
#service(name, proto = nil, **opts) ⇒ Object
Start a service named name
. The name is referenced in #child, #local, and #remote to connect a new process to this service.
The proto
can be either :unix (the default) or :tcp; the value can also be specifed with the proto: key-value argument.
Other key-value arguments are:
- :path
-
for unix sockets, path to the socket file to be created
- :base
-
for unix sockets, a base string for constructing the socket filename, if :path option is not provided; if neither :path nor :base specified, socket is in a tmp dir with filename based on
name
. - :bind_host
-
interface this service listens on, such as:
"0.0.0.0", "<any>" (same)
"localhost", "127.0.0.1" (same)
or a specific hostname.
- :connect_host
-
host specifier used by remote clients to connect. By default, this is constructed from the bind_host. For example, with bind_host: “<any>”, the default connect_host is the current hostname (see #host_name).
- :port
-
port this service listens on; defaults to 0 to choose a free port
301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 |
# File 'lib/easy-serve.rb', line 301 def service name, proto = nil, **opts proto ||= opts.delete(:proto) || :unix case proto when :unix opts[:path] ||= choose_socket_filename(name, base: opts[:base]) when :tcp opts[:connect_host] ||= case opts[:bind_host] when nil, "0.0.0.0", /\A<any>\z/i host_name when "localhost", "127.0.0.1" "localhost" end end service = Service.for(name, proto, **opts) rd, wr = IO.pipe pid = fork do rd.close log.progname = name log.info "starting" svr = service.serve(max_tries: MAX_TRIES, log: log) yield svr if block_given? no_interrupt_if_interactive Marshal.dump service, wr wr.close sleep end wr.close services[name] = Marshal.load rd rd.close end |
#start_services ⇒ Object
168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 |
# File 'lib/easy-serve.rb', line 168 def start_services return unless @owner if not services_file log.debug {"starting services without services_file"} yield return end lock_success = with_lock_file *File.split(services_file) do # Successful creation of the lock file gives this process the # right to check and create the services_file itself. if File.exist? services_file raise ServicesExistError, "Services at #{services_file.inspect} already exist." end log.debug {"starting services stored in #{services_file.inspect}"} yield tmp = services_file + ".tmp" File.open(tmp, "w") do |f| YAML.dump(services, f) end FileUtils.mv(tmp, services_file) @created_services_file = true end unless lock_success raise ServicesExistError, "Services at #{services_file.inspect} are being created." end end |
#tmpdir ⇒ Object
222 223 224 225 226 227 |
# File 'lib/easy-serve.rb', line 222 def tmpdir @tmpdir ||= begin require 'tmpdir' Dir.mktmpdir "easy-serve-" end end |
#tunnel_to_remote_services ⇒ Object
Set up tunnels as needed and modify the service list so that connections will go to local endpoints in those cases. Call this method in non-sibling invocations, such as when the server file has been copied to a remote host and used to start a new client. This is for the ‘ssh -L’ type of tunneling: a process needs to connect to a cluster of remote EasyServe processes that already exist and do not know about this process.
390 391 392 393 394 395 396 397 398 399 400 401 402 |
# File 'lib/easy-serve.rb', line 390 def tunnel_to_remote_services return if sibling require 'easy-serve/service/tunnelled' tunnelled_services = {} services.each do |service_name, service| service, ssh_session = service.tunnelled tunnelled_services[service_name] = service @ssh_sessions << ssh_session if ssh_session # let GC close them end @services = tunnelled_services end |
#with_lock_file(dir, base) ⇒ Object
Returns true if this process got the lock.
204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 |
# File 'lib/easy-serve.rb', line 204 def with_lock_file dir, base lock_file = File.join(dir, ".lock.#{base}") begin FileUtils.ln_s ".#{Process.pid}.#{base}", lock_file rescue Errno::EEXIST return false end begin yield ensure FileUtils.rm_f lock_file end true end |