qb 0.3.25 → 0.4.0

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.
Files changed (113) hide show
  1. checksums.yaml +4 -4
  2. data/VERSION +1 -1
  3. data/ansible.cfg +10 -1
  4. data/exe/.qb_interop_receive +3 -10
  5. data/exe/qb +8 -2
  6. data/lib/python/qb/__init__.py +6 -0
  7. data/{roles/qb/ruby/rspec/setup/tasks/persistence.yml → lib/python/qb/ansible/__init__.py} +0 -0
  8. data/lib/python/qb/ansible/modules/__init__.py +0 -0
  9. data/lib/python/qb/ansible/modules/docker/__init__.py +0 -0
  10. data/lib/python/qb/ansible/modules/docker/client.py +177 -0
  11. data/lib/python/qb/ansible/modules/docker/image_manager.py +754 -0
  12. data/lib/python/qb/ipc/__init__.py +0 -0
  13. data/lib/python/qb/ipc/stdio/__init__.py +99 -0
  14. data/lib/python/qb/ipc/stdio/logging.py +151 -0
  15. data/lib/qb.rb +3 -3
  16. data/lib/qb/ansible/cmds/playbook.rb +5 -14
  17. data/lib/qb/ansible/env.rb +36 -6
  18. data/lib/qb/ansible/module.rb +396 -152
  19. data/lib/qb/ansible/module/response.rb +195 -0
  20. data/lib/qb/ansible/modules.rb +42 -0
  21. data/lib/qb/ansible/modules/docker/image.rb +273 -0
  22. data/lib/qb/cli.rb +5 -18
  23. data/lib/qb/cli/run.rb +2 -2
  24. data/lib/qb/data.rb +22 -0
  25. data/lib/qb/data/immutable.rb +39 -0
  26. data/lib/qb/docker.rb +2 -0
  27. data/lib/qb/docker/cli.rb +430 -0
  28. data/lib/qb/docker/image.rb +207 -0
  29. data/lib/qb/docker/image/name.rb +309 -0
  30. data/lib/qb/docker/image/tag.rb +113 -0
  31. data/lib/qb/docker/repo.rb +0 -0
  32. data/lib/qb/errors.rb +17 -3
  33. data/lib/qb/execution.rb +83 -0
  34. data/lib/qb/ipc.rb +48 -0
  35. data/lib/qb/ipc/stdio.rb +32 -0
  36. data/lib/qb/ipc/stdio/client.rb +267 -0
  37. data/lib/qb/ipc/stdio/server.rb +229 -0
  38. data/lib/qb/ipc/stdio/server/in_service.rb +18 -0
  39. data/lib/qb/ipc/stdio/server/log_service.rb +168 -0
  40. data/lib/qb/ipc/stdio/server/out_service.rb +20 -0
  41. data/lib/qb/ipc/stdio/server/service.rb +229 -0
  42. data/lib/qb/options.rb +360 -502
  43. data/lib/qb/options/option.rb +293 -115
  44. data/lib/qb/options/option/option_parser_concern.rb +228 -0
  45. data/lib/qb/options/types.rb +73 -0
  46. data/lib/qb/package.rb +0 -1
  47. data/lib/qb/package/version.rb +179 -58
  48. data/lib/qb/package/version/from.rb +192 -51
  49. data/lib/qb/package/version/leveled.rb +1 -1
  50. data/lib/qb/path.rb +3 -2
  51. data/lib/qb/repo/git.rb +9 -85
  52. data/lib/qb/role/default_dir.rb +2 -2
  53. data/lib/qb/role/errors.rb +2 -8
  54. data/lib/qb/util.rb +1 -2
  55. data/lib/qb/util/bundler.rb +73 -43
  56. data/lib/qb/util/decorators.rb +99 -0
  57. data/lib/qb/util/interop.rb +7 -8
  58. data/lib/qb/util/resource.rb +12 -13
  59. data/lib/qb/version.rb +10 -0
  60. data/library/path_facts +5 -10
  61. data/library/qb.module.rb +105 -0
  62. data/library/stream +6 -26
  63. data/load/ansible/module/autorun.rb +25 -0
  64. data/load/ansible/module/script.rb +123 -0
  65. data/load/rebundle.rb +39 -0
  66. data/plugins/filter/dict_filters.py +56 -0
  67. data/plugins/{filter_plugins/path_plugins.py → filter/path_filters.py} +0 -0
  68. data/plugins/{filter_plugins/ruby_interop_plugins.py → filter/ruby_interop_filters.py} +1 -17
  69. data/plugins/{filter_plugins/string_plugins.py → filter/string_filters.py} +1 -20
  70. data/plugins/{filter_plugins/version_plugins.py → filter/version_filters.py} +3 -18
  71. data/plugins/{lookup_plugins/every.py → lookup/every_lookups.py} +0 -0
  72. data/plugins/{lookup_plugins/resolve.py → lookup/resolve_lookups.py} +0 -0
  73. data/plugins/{lookup_plugins/version.py → lookup/version_lookups.py} +0 -16
  74. data/plugins/test/dict_tests.py +36 -0
  75. data/plugins/test/string_tests.py +36 -0
  76. data/qb.gemspec +7 -3
  77. data/roles/nrser.rb/library/set_fact_with_ruby.rb +3 -9
  78. data/roles/nrser.state_mate/library/state +3 -17
  79. data/roles/qb/call/meta/qb.yml +1 -1
  80. data/roles/qb/dev/ref/repo/git/meta/qb.yml +1 -1
  81. data/roles/qb/{ruby/rspec/setup → docker/mac/kubernetes}/defaults/main.yml +1 -1
  82. data/roles/qb/{ruby/rspec/setup → docker/mac/kubernetes}/meta/main.yml +3 -2
  83. data/roles/qb/{ruby/rspec/setup → docker/mac/kubernetes}/meta/qb.yml +12 -7
  84. data/roles/qb/docker/mac/kubernetes/tasks/main.yml +45 -0
  85. data/roles/qb/git/check/clean/meta/qb.yml +1 -1
  86. data/roles/qb/git/ignore/meta/qb +10 -3
  87. data/roles/qb/git/submodule/update/library/git_submodule_update +17 -27
  88. data/roles/qb/github/pages/setup/meta/qb.yml +1 -1
  89. data/roles/qb/labs/atom/apm/meta/qb.yml +1 -1
  90. data/roles/qb/osx/git/change_case/meta/qb.yml +1 -1
  91. data/roles/qb/osx/notif/meta/qb.yml +1 -1
  92. data/roles/qb/pkg/bump/library/bump +4 -16
  93. data/roles/qb/role/qb/defaults/main.yml +2 -0
  94. data/roles/qb/role/qb/meta/qb.yml +10 -5
  95. data/roles/qb/role/qb/templates/qb.yml.j2 +7 -2
  96. data/roles/qb/role/templates/library/module.rb.j2 +12 -23
  97. data/roles/qb/role/templates/meta/main.yml.j2 +14 -1
  98. data/roles/qb/ruby/bundler/meta/qb.yml +1 -1
  99. data/roles/qb/ruby/dependency/meta/qb.yml +1 -1
  100. data/roles/qb/ruby/gem/bin_stubs/meta/qb.yml +1 -1
  101. data/roles/qb/ruby/gem/bin_stubs/templates/console +8 -2
  102. data/roles/qb/ruby/gem/build/meta/qb.yml +1 -1
  103. data/roles/qb/ruby/gem/new/meta/qb.yml +1 -1
  104. data/roles/qb/ruby/nrser/rspex/generate/meta/qb.yml +5 -5
  105. data/roles/qb/ruby/nrser/rspex/issue/meta/qb.yml +1 -1
  106. data/roles/qb/ruby/yard/clean/meta/qb.yml +1 -1
  107. data/roles/qb/ruby/yard/config/library/yard.get_output_dir +5 -15
  108. data/roles/qb/ruby/yard/config/meta/qb.yml +1 -1
  109. data/roles/qb/ruby/yard/setup/meta/qb.yml +1 -1
  110. metadata +71 -22
  111. data/lib/qb/ansible_module.rb +0 -5
  112. data/lib/qb/util/stdio.rb +0 -187
  113. data/roles/qb/ruby/rspec/setup/tasks/main.yml +0 -4
@@ -0,0 +1,229 @@
1
+ # encoding: UTF-8
2
+ # frozen_string_literal: true
3
+
4
+ # Requirements
5
+ # =======================================================================
6
+
7
+ # Stdlib
8
+ # -----------------------------------------------------------------------
9
+
10
+ # Used to generate a random ID to use in socket file names
11
+ require 'securerandom'
12
+
13
+ # Need {FileUtils.rm_rf}
14
+ require 'fileutils'
15
+
16
+ # Deps
17
+ # -----------------------------------------------------------------------
18
+
19
+ require 'nrser'
20
+
21
+ # Project / Package
22
+ # -----------------------------------------------------------------------
23
+
24
+
25
+
26
+ # Declarations
27
+ # ========================================================================
28
+
29
+ module QB::IPC
30
+ module STDIO; end
31
+ end
32
+
33
+
34
+ # Definitions
35
+ # =======================================================================
36
+
37
+ # Server functionality to make the master QB process' STDIO streams available
38
+ # to external processes, specifically Ansible modules.
39
+ #
40
+ # Ansible's handling of STDIO in modules is really not suitable for our use
41
+ # case - we want to see what modules and other external process commands
42
+ # are doing in real time, much like invoking them in a Bash script.
43
+ #
44
+ # This thing is **far** from perfect, but it's been incredibly helpful for a
45
+ # simple solution.
46
+ #
47
+ # Basically, {OutService} instances are created for `STDOUT` and `STDERR`,
48
+ # which each create a {UNIXServer} on a local socket file and spawn a {Thread}
49
+ # to listen to it. The socket's path is then made available to the Ansible
50
+ # child process via ENV vars, and that process in turn carries those ENV vars
51
+ # to it's module child processes, who can then use an instance of the
52
+ # corresponding {QB::IPC::STDIO::Client} class to connect to those sockets and
53
+ # write output that is passed through to the master QB process' output streams.
54
+ #
55
+ # The protocol is simply text line-based, and modules - or any other process -
56
+ # written in other languages can easily connect and write as well.
57
+ #
58
+ # @note
59
+ # This feature only works for `localhost`. I have no idea what it will do
60
+ # in other cases. It doesn't seem like it should break anything, but remotely
61
+ # executing modules definitely won't be able to connect to the sockets on
62
+ # the host.
63
+ #
64
+ # @todo
65
+ # There is also a {InService} for `STDIN`, but it's is pretty experimental /
66
+ # broken at this point. That would be nice to fix in the future so that
67
+ # programs that make use of user interaction work seamlessly through QB.
68
+ #
69
+ # This will probably require using pseudo-TTY streams or whatever mess.
70
+ #
71
+ class QB::IPC::STDIO::Server
72
+
73
+ # Sub-Tree Requirements
74
+ # ========================================================================
75
+
76
+ require_relative './server/in_service'
77
+ require_relative './server/out_service'
78
+ require_relative './server/log_service'
79
+
80
+
81
+ # Mixins
82
+ # ========================================================================
83
+
84
+ # Add {.logger} and {#logger} methods
85
+ include NRSER::Log::Mixin
86
+
87
+
88
+ # Class Methods
89
+ # ========================================================================
90
+
91
+ # Clean up resources for an instance. Broken out because I was trying to
92
+ # make it run as a finalizer to remove the directory in all cases, but that
93
+ # does not seem to be triggering. Whatever man...
94
+ #
95
+ # @param [Fixnum] object_id:
96
+ # The instance's `#object_id`, just for logging purposes.
97
+ #
98
+ # @param [Array<Service>]
99
+ # The instance's services, which we will {Service#close!}.
100
+ #
101
+ # @param [Pathname] socket_dir:
102
+ # The tmpdir created for the sockets, which we will remove.
103
+ #
104
+ # @return [nil]
105
+ #
106
+ def self.clean_up_for object_id:, services:, socket_dir:
107
+ logger.debug "Cleaning up...",
108
+ object_id: object_id,
109
+ socket_dir: socket_dir
110
+
111
+ services.each do |service|
112
+ logger.catch.warn(
113
+ "Unable to close service",
114
+ service: service,
115
+ ) { service.close! }
116
+ end
117
+
118
+ FileUtils.rm_rf( socket_dir ) if socket_dir.exist?
119
+
120
+ logger.debug "Clean!",
121
+ object_id: object_id,
122
+ socket_dir: socket_dir
123
+
124
+ nil
125
+ end # .finalize
126
+
127
+
128
+ # Make a {Proc} to use for finalization.
129
+ #
130
+ # Needs to be done outside instance scope to doesn't close over the
131
+ # instance.
132
+ #
133
+ # @param **kwds
134
+ # Passed to {.clean_up_for}.
135
+ #
136
+ # @return [Proc<() => nil>]
137
+ # @todo Document return value.
138
+ #
139
+ def self.finalizer_for **kwds
140
+ -> {
141
+ logger.debug "Finalizing...", **kwds
142
+ clean_up_for **kwds
143
+ logger.debug "Finalized", **kwds
144
+ }
145
+ end # .finalizer_for
146
+
147
+
148
+ # Attributes
149
+ # ========================================================================
150
+
151
+ # Where the UNIX socket files get put.
152
+ #
153
+ # @return [Pathname]
154
+ #
155
+ attr_reader :socket_dir
156
+
157
+
158
+ # Construction
159
+ # ========================================================================
160
+
161
+ # Instantiate a new `QB::IPC::STDIO::Server`.
162
+ #
163
+ def initialize
164
+ @socket_dir = Dir.mktmpdir( 'qb_ipc_stdio' ).to_pn
165
+
166
+ @in_service = QB::IPC::STDIO::Server::InService.new \
167
+ name: :in,
168
+ socket_dir: socket_dir,
169
+ src: $stdin
170
+
171
+ @out_service = QB::IPC::STDIO::Server::OutService.new \
172
+ name: :out,
173
+ socket_dir: socket_dir,
174
+ dest: $stdout
175
+
176
+ @err_service = QB::IPC::STDIO::Server::OutService.new \
177
+ name: :err,
178
+ socket_dir: socket_dir,
179
+ dest: $stderr
180
+
181
+ @log_service = QB::IPC::STDIO::Server::LogService.new \
182
+ name: :log,
183
+ socket_dir: socket_dir
184
+
185
+ ObjectSpace.define_finalizer \
186
+ self,
187
+ self.class.finalizer_for(
188
+ object_id: object_id,
189
+ services: services,
190
+ socket_dir: socket_dir
191
+ )
192
+ end # #initialize
193
+
194
+
195
+ # Instance Methods
196
+ # ========================================================================
197
+
198
+ # @return [Array<(InService, OutService, OutService)>]
199
+ # Array of in, out and err services.
200
+ #
201
+ def services
202
+ [ @in_service, @out_service, @err_service, @log_service ]
203
+ end # #services
204
+
205
+
206
+ # Start all the {#services} by calling {Service#open!} on them.
207
+ #
208
+ # @return [self]
209
+ #
210
+ def start!
211
+ services.each &:open!
212
+ self
213
+ end # #open!
214
+
215
+
216
+ # Stop all {#services} by calling {Service#close!} on them and clean up
217
+ # the resources.
218
+ #
219
+ # @return [self]]
220
+ #
221
+ def stop!
222
+ self.class.clean_up_for \
223
+ object_id: object_id,
224
+ services: services,
225
+ socket_dir: socket_dir
226
+ self
227
+ end
228
+
229
+ end # class QB::IPC::STDIO::Server
@@ -0,0 +1,18 @@
1
+ require_relative './service'
2
+
3
+ # QB STDIO Service to proxy interactive user input from the main process
4
+ # to modules.
5
+ class QB::IPC::STDIO::Server::InService < QB::IPC::STDIO::Server::Service
6
+ def initialize name:, socket_dir:, src:
7
+ super name: name, socket_dir: socket_dir
8
+ @src = src
9
+ end
10
+
11
+ def work_in_thread
12
+ while (line = @src.gets) do
13
+ @socket.puts line
14
+ end
15
+
16
+ close!
17
+ end
18
+ end # InService
@@ -0,0 +1,168 @@
1
+ require 'nrser/core_ext/hash'
2
+ require_relative './service'
3
+
4
+ # QB STDIO Service to receive log lines in JSON format and forward them
5
+ # on to the logger.
6
+ #
7
+ class QB::IPC::STDIO::Server::LogService < QB::IPC::STDIO::Server::Service
8
+
9
+ class Log < SemanticLogger::Log
10
+
11
+
12
+ # Construction
13
+ # ========================================================================
14
+
15
+ def initialize **kwds
16
+ super *kwds.values_at( :name, :level, :level_index )
17
+
18
+ if kwds.key? :timestamp
19
+ self.time = Time.parse kwds[:timestamp]
20
+ end
21
+
22
+ self.tags = kwds[:tags] || []
23
+ self.named_tags = kwds[:named_tags] || {}
24
+
25
+ @pid = kwds[:pid] || '???'
26
+ @thread = kwds[:thread] || '???'
27
+
28
+ exception = if kwds.key? :exception
29
+ klass = kwds[:exception]["name"].safe_constantize
30
+
31
+ if klass
32
+ # HACK Good God...
33
+ #
34
+ # What we're doing is constructing an instance of the
35
+ # exception class so that SemLog is happy with it... so we
36
+ # take the class name, load that constant, then we *don't*
37
+ # create an instance, because that could require args, and
38
+ # all we need is something that holds the message and
39
+ # backtrace, so we add the message as the response from
40
+ # dynamically-created `#to_s` and `#message` methods added
41
+ # *to that instance only*. Then we set the backtrace using
42
+ # the regular instance API.
43
+ #
44
+ # ...and it kinda seems to work. But I suspect it will fuck
45
+ # me/us/someone at some point if left like this...
46
+ #
47
+
48
+ message = kwds[:exception]["message"] || '(none)'
49
+
50
+ error = klass.allocate
51
+
52
+ metaclass = class << error; self; end
53
+
54
+ [:to_s, :message].each do |name|
55
+ metaclass.send( :define_method, name ){ message }
56
+ end
57
+
58
+ if kwds[:exception]["stack_trace"]
59
+ error.set_backtrace kwds[:exception]["stack_trace"]
60
+ end
61
+
62
+ error
63
+ end
64
+ end
65
+
66
+ assign exception: exception, **kwds.slice(
67
+ :message,
68
+ :payload,
69
+ :min_duration,
70
+ :metric,
71
+ :metric_amount,
72
+ :duration,
73
+ # :backtrace,
74
+ # :log_exception,
75
+ # :on_exception_level,
76
+ :dimension,
77
+ )
78
+ end
79
+
80
+ # Instance Methods
81
+ # ========================================================================
82
+
83
+ def process_info thread_name_length = 30
84
+ "IPC:#{ @pid }:#{"%.#{ thread_name_length }s" % @thread}"
85
+ end
86
+
87
+
88
+ end # class Log
89
+
90
+
91
+ def initialize name:, socket_dir:
92
+ super name: name, socket_dir: socket_dir
93
+ @loggers = {}
94
+ end
95
+
96
+ def work_in_thread
97
+ while (line = @socket.gets) do
98
+ logger.trace "received line",
99
+ line: line
100
+
101
+ load_log_in_thread line
102
+ end
103
+ end
104
+
105
+
106
+ protected
107
+ # ========================================================================
108
+
109
+ # Get a {NRSER::Log::Logger} for a log name, creating them on demand
110
+ # and caching after that.
111
+ #
112
+ # @param [String] name
113
+ # Name from the log message.
114
+ #
115
+ # @return [NRSER::Log::Logger]
116
+ #
117
+ def logger_for name
118
+ @loggers[name] ||= NRSER::Log[name]
119
+ end
120
+
121
+
122
+ # Log a {Log} in it's logger if it should log.
123
+ #
124
+ # @protected
125
+ #
126
+ # @param [Log] log
127
+ # Log instance to dispatch
128
+ #
129
+ # @return [void]
130
+ #
131
+ def write_log log
132
+ logger = logger_for log.name
133
+ # logger.level = :trace
134
+ logger.log( log ) if logger.should_log?( log )
135
+ end
136
+
137
+
138
+ # Try to load the line into a {SemanticLogger::Log} instance.
139
+ #
140
+ def load_log_in_thread line
141
+ # logger.with_level :trace do
142
+ decoded = logger.catch.warn(
143
+ "Unable to decode log message",
144
+ line: line,
145
+ ) { ActiveSupport::JSON.decode line }
146
+
147
+ logger.trace "Decoded log message", decoded
148
+
149
+ return nil unless decoded
150
+
151
+ logger.catch.warn(
152
+ "Unable to process log message",
153
+ line: line,
154
+ decoded: decoded,
155
+ ) do
156
+ log = Log.new **decoded.to_options
157
+
158
+ logger.trace "Constructed {Log}",
159
+ log: log
160
+
161
+ write_log log
162
+ end # logger.catch.warn
163
+ # end # logger.with_level :trace
164
+ end
165
+
166
+ public # end protected ***************************************************
167
+
168
+ end # LogService
@@ -0,0 +1,20 @@
1
+ require_relative './service'
2
+
3
+ # QB STDIO Service to proxy output from modules back to the main user
4
+ # process.
5
+ class QB::IPC::STDIO::Server::OutService < QB::IPC::STDIO::Server::Service
6
+ def initialize name:, socket_dir:, dest:
7
+ super name: name, socket_dir: socket_dir
8
+ @dest = dest
9
+ end
10
+
11
+ def work_in_thread
12
+ while (line = @socket.gets) do
13
+ logger.trace "received line",
14
+ line: line,
15
+ dest: @dest
16
+
17
+ @dest.puts line
18
+ end
19
+ end
20
+ end # InService
@@ -0,0 +1,229 @@
1
+ # Requirements
2
+ # =====================================================================
3
+
4
+ # Stdlib
5
+ # ----------------------------------------------------------------------------
6
+
7
+ require 'thread'
8
+ require 'socket'
9
+ require 'fileutils'
10
+
11
+ # Deps
12
+ # ----------------------------------------------------------------------------
13
+
14
+ require 'nrser'
15
+
16
+ # Project
17
+ # ----------------------------------------------------------------------------
18
+
19
+ # Need {QB::IPC::STDIO.path_env_var_name}
20
+ require 'qb/ipc/stdio'
21
+
22
+
23
+ # Definitions
24
+ # =====================================================================
25
+
26
+ # STDIO as a service exposed on a UNIX socket so that modules can stream
27
+ # their output to it, which is in turn printed to the console `qb` is running
28
+ # in.
29
+ class QB::IPC::STDIO::Server::Service
30
+
31
+ # Mixins
32
+ # ========================================================================
33
+
34
+ # Add {.logger} and {#logger}
35
+ include NRSER::Log::Mixin
36
+
37
+
38
+ # Attributes
39
+ # ========================================================================
40
+
41
+ # The service's name, like `:in`, `:out`, `:err`.
42
+ #
43
+ # @return [Synbol]
44
+ #
45
+ attr_reader :name
46
+
47
+
48
+ # Absolute path to socket file.
49
+ #
50
+ # @return [Pathname]
51
+ #
52
+ attr_reader :path
53
+
54
+
55
+ # TODO document `thread` attribute.
56
+ #
57
+ # @return [attr_type]
58
+ #
59
+ attr_reader :thread
60
+
61
+
62
+ # TODO document `env_var_name` attribute.
63
+ #
64
+ # @return [String]
65
+ #
66
+ attr_reader :env_var_name
67
+
68
+
69
+ # The UNIX socket server.
70
+ #
71
+ # @return [UNIXServer?]
72
+ #
73
+ attr_reader :server
74
+
75
+
76
+ # The socket we accept from the server.
77
+ #
78
+ # @return [UNIXSocket]
79
+ #
80
+ attr_reader :socket
81
+
82
+
83
+ # Construction
84
+ # ========================================================================
85
+
86
+ # Construct an IO service.
87
+ #
88
+ # @param [Symbol] name
89
+ # What this service is for... `:in`, `:out`, `:err`...
90
+ #
91
+ # Used as the thread name.
92
+ #
93
+ def initialize name:, socket_dir:
94
+ @name = name
95
+ @thread = nil
96
+ @server = nil
97
+ @socket = nil
98
+ @env_var_name = QB::IPC::STDIO.path_env_var_name name
99
+
100
+ @path = socket_dir.join "#{ name }.sock"
101
+
102
+ self.logger = create_logger
103
+
104
+ logger.debug "Initialized"
105
+ end
106
+
107
+
108
+ protected
109
+ # ========================================================================
110
+
111
+ # Initialize the {#logger}.
112
+ #
113
+ # @protected
114
+ # @return [nil]
115
+ #
116
+ def create_logger
117
+ logger = NRSER::Log[ self ]
118
+
119
+ # HACK
120
+ #
121
+ # Tracing the IO is *really* noisy and spaghettis up the log output
122
+ # due to the threaded nature of the this beast... which is what you
123
+ # *want* if you're debugging main/module process IO, since it shows
124
+ # you what's happening synchronously, but that's pretty much all you
125
+ # can debug when it's being output.
126
+ #
127
+ # The `debug`-level output is
128
+ #
129
+ # For that reason, I quickly threw
130
+ #
131
+ if ENV['QB_TRACE_STDIO'].truthy?
132
+ logger.level = :trace
133
+ elsif ENV['QB_DEBUG_STDIO'].truthy?
134
+ logger.level = :debug
135
+ else
136
+ logger.level = :info
137
+ end
138
+
139
+ logger
140
+ end
141
+
142
+ public # end private *****************************************************
143
+
144
+
145
+ # Instance Methods
146
+ # ========================================================================
147
+
148
+ # @return [String]
149
+ # a short string describing the instance. Used to set the name for
150
+ # instance loggers.
151
+ def to_s
152
+ "#<#{ self.class.name } name=#{ name.inspect } path=#{ path.to_s }>"
153
+ end # #to_s
154
+
155
+
156
+ def open!
157
+ logger.debug "Opening..."
158
+
159
+ # make sure env var is not already set (basically just prevents you from
160
+ # accidentally opening two instances with the same name)
161
+ if ENV.key? env_var_name
162
+ raise "env already contains key #{ env_var_name }" \
163
+ "with value #{ ENV[env_var_name] }"
164
+ end
165
+
166
+ @thread = Thread.new do
167
+ Thread.current.name = name
168
+ logger.trace "thread started."
169
+
170
+ @server = UNIXServer.new path.to_s
171
+
172
+ while true do
173
+ @socket = server.accept
174
+ work_in_thread
175
+ end
176
+ end
177
+
178
+ # set the env key so children can find the socket path
179
+ ENV[env_var_name] = path.to_s
180
+ logger.debug "Set env var",
181
+ env_var_name => ENV[env_var_name]
182
+
183
+ logger.debug "Service open."
184
+ end # open
185
+
186
+
187
+ # We're done here, clean up!
188
+ #
189
+ # @todo
190
+ # Not sure how correct this is... fucking threading. *Seems* to work...
191
+ #
192
+ # @return [nil]
193
+ #
194
+ def close!
195
+ logger.debug "Closing...",
196
+ socket: socket,
197
+ server: server,
198
+ path_exists: path.exist?,
199
+ thread: thread,
200
+ env_var: {
201
+ env_var_name => ENV[env_var_name],
202
+ }
203
+
204
+ # Remove the path from the ENV so if we do anything after this the
205
+ # old one isn't hanging around
206
+ ENV.delete env_var_name
207
+
208
+ # Kill the thread first so that it can't try to do anything else
209
+ thread.kill if thread && thread.alive?
210
+
211
+ socket.close unless socket.nil?
212
+ @socket = nil
213
+ server.close unless server.nil?
214
+ @server = nil
215
+ FileUtils.rm( path ) if path.exist?
216
+
217
+ logger.debug "Closed.",
218
+ socket: socket,
219
+ server: server,
220
+ path_exists: path.exist?,
221
+ thread: thread,
222
+ env_var: {
223
+ env_var_name => ENV[env_var_name],
224
+ }
225
+
226
+ nil
227
+ end # #close!
228
+
229
+ end # QB::IPC::STDIO::Server::Service