class QB::IPC::STDIO::Server
Server
functionality to make the master QB
process' STDIO streams available to external processes, specifically Ansible modules.
Ansible's handling of STDIO in modules is really not suitable for our use case - we want to see what modules and other external process commands are doing in real time, much like invoking them in a Bash script.
This thing is far from perfect, but it's been incredibly helpful for a simple solution.
Basically, {OutService} instances are created for `STDOUT` and `STDERR`, which each create a {UNIXServer} on a local socket file and spawn a {Thread} to listen to it. The socket's path is then made available to the Ansible child process via ENV vars, and that process in turn carries those ENV vars to it's module child processes, who can then use an instance of the corresponding {QB::IPC::STDIO::Client} class to connect to those sockets and write output that is passed through to the master QB
process' output streams.
The protocol is simply text line-based, and modules - or any other process - written in other languages can easily connect and write as well.
@note
This feature only works for `localhost`. I have no idea what it will do in other cases. It doesn't seem like it should break anything, but remotely executing modules definitely won't be able to connect to the sockets on the host.
@todo
There is also a {InService} for `STDIN`, but it's is pretty experimental / broken at this point. That would be nice to fix in the future so that programs that make use of user interaction work seamlessly through QB. This will probably require using pseudo-TTY streams or whatever mess.
Attributes
Where the UNIX socket files get put.
@return [Pathname]
Public Class Methods
Clean up resources for an instance. Broken out because I was trying to make it run as a finalizer to remove the directory in all cases, but that does not seem to be triggering. Whatever man…
@param [Fixnum] object_id:
The instance's `#object_id`, just for logging purposes.
@param [Array<Service>]
The instance's services, which we will {Service#close!}.
@param [Pathname] socket_dir
:
The tmpdir created for the sockets, which we will remove.
@return [nil]
# File lib/qb/ipc/stdio/server.rb, line 106 def self.clean_up_for object_id:, services:, socket_dir: logger.debug "Cleaning up...", object_id: object_id, socket_dir: socket_dir services.each do |service| logger.catch.warn( "Unable to close service", service: service, ) { service.close! } end FileUtils.rm_rf( socket_dir ) if socket_dir.exist? logger.debug "Clean!", object_id: object_id, socket_dir: socket_dir nil end
Make a {Proc} to use for finalization.
Needs to be done outside instance scope to doesn't close over the instance.
@param **kwds
Passed to {.clean_up_for}.
@return [Proc<() => nil>]
@todo Document return value.
# File lib/qb/ipc/stdio/server.rb, line 139 def self.finalizer_for **kwds -> { logger.debug "Finalizing...", **kwds clean_up_for **kwds logger.debug "Finalized", **kwds } end
Instantiate a new `QB::IPC::STDIO::Server`.
# File lib/qb/ipc/stdio/server.rb, line 163 def initialize @socket_dir = Dir.mktmpdir( 'qb-ipc-stdio' ).to_pn @in_service = QB::IPC::STDIO::Server::InService.new \ name: :in, socket_dir: socket_dir, src: $stdin @out_service = QB::IPC::STDIO::Server::OutService.new \ name: :out, socket_dir: socket_dir, dest: $stdout @err_service = QB::IPC::STDIO::Server::OutService.new \ name: :err, socket_dir: socket_dir, dest: $stderr @log_service = QB::IPC::STDIO::Server::LogService.new \ name: :log, socket_dir: socket_dir ObjectSpace.define_finalizer \ self, self.class.finalizer_for( object_id: object_id, services: services, socket_dir: socket_dir ) end
Public Instance Methods
@return [Array<(InService
, OutService
, OutService
)>]
Array of in, out and err services.
# File lib/qb/ipc/stdio/server.rb, line 201 def services [ @in_service, @out_service, @err_service, @log_service ] end
Start all the {#services} by calling {Service#open!} on them.
@return [self]
# File lib/qb/ipc/stdio/server.rb, line 210 def start! services.each &:open! self end
Stop all {#services} by calling {Service#close!} on them and clean up the resources.
@return [self]]
# File lib/qb/ipc/stdio/server.rb, line 221 def stop! self.class.clean_up_for \ object_id: object_id, services: services, socket_dir: socket_dir self end