qb 0.3.25 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
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