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,207 @@
1
+ # encoding: UTF-8
2
+ # frozen_string_literal: true
3
+
4
+ # Requirements
5
+ # =======================================================================
6
+
7
+ # Stdlib
8
+ # -----------------------------------------------------------------------
9
+
10
+ # Deps
11
+ # -----------------------------------------------------------------------
12
+
13
+ # Project / Package
14
+ # -----------------------------------------------------------------------
15
+
16
+ require 'qb/data'
17
+
18
+ require_relative './image/name'
19
+ require_relative './image/tag'
20
+
21
+
22
+ # Refinements
23
+ # =======================================================================
24
+
25
+ require 'nrser/refinements/types'
26
+ using NRSER::Types
27
+
28
+
29
+ # Definitions
30
+ # =======================================================================
31
+
32
+ # @todo document Docker::Image class.
33
+ module QB
34
+ module Docker
35
+ class Image < QB::Data::Immutable
36
+
37
+ include NRSER::Log::Mixin
38
+
39
+ # Constants
40
+ # ======================================================================
41
+
42
+
43
+ # Class Methods
44
+ # ======================================================================
45
+
46
+
47
+
48
+
49
+ def self.with_name name, &block
50
+ block.call \
51
+ case name
52
+ when QB::Docker::Image::Name
53
+ name
54
+ when String
55
+ QB::Docker::Image::Name.from_s name
56
+ when Hash
57
+ QB::Docker::Image::Name.from_data name
58
+ else
59
+ raise NRSER::TypeError.new \
60
+ "Not sure what to do with ", name,
61
+ name: name
62
+ end
63
+ end
64
+
65
+
66
+ def self.names **opts
67
+ QB::Docker::CLI.image_names **opts
68
+ end
69
+
70
+
71
+ def self.run_cmd cmd, stream: true, raise_on_fail: false
72
+ if stream
73
+ if raise_on_fail
74
+ cmd.stream!
75
+ else
76
+ cmd.stream
77
+ end
78
+ else
79
+ cmd.capture.tap { |result|
80
+ if raise_on_fail && result.error?
81
+ raise QB::Docker::CLI::Error.from_result result
82
+ end
83
+ }
84
+ end
85
+ end
86
+
87
+
88
+ def self.run_cmd! cmd, stream: true
89
+ run_cmd cmd, stream: stream, raise_on_fail: true
90
+ end
91
+
92
+
93
+ def self.build! name:,
94
+ path:,
95
+ push: false,
96
+ tags: [],
97
+ _cmd_stream: true,
98
+ **build_opts
99
+
100
+ cmd = QB::Docker::CLI.build_cmd path, tag: name.to_s, **build_opts
101
+
102
+ logger.info "building...",
103
+ name: name.to_s,
104
+ path: path,
105
+ push: push,
106
+ _cmd_stream: _cmd_stream,
107
+ cmd: cmd.prepare
108
+
109
+ result = run_cmd! cmd, stream: _cmd_stream
110
+
111
+ tags.each do |tag_arg|
112
+ cmd = QB::Docker::CLI.tag_cmd name, tag_arg
113
+ tag = cmd.args[1]
114
+ logger.debug "Tagging #{ name } as #{ tag }",
115
+ tag_arg: tag_arg
116
+
117
+ result = run_cmd cmd, stream: _cmd_stream
118
+
119
+ if result.ok?
120
+ logger.info "Tagged #{ name } as #{ tag }.",
121
+ cmd: cmd.last_prepared_cmd
122
+ else
123
+ logger.error "Failed to tag #{ name } as #{ tag }",
124
+ tag_arg: tag_arg,
125
+ cmd: cmd.last_prepared_cmd
126
+ end
127
+ end
128
+
129
+ QB::Docker::CLI.push( name ) if push
130
+
131
+ result
132
+ end
133
+
134
+
135
+ def self.ensure_present! name:,
136
+ pull: nil,
137
+ build: nil,
138
+ force: false,
139
+ push: false
140
+
141
+ name = QB::Docker::Image::Name.from name
142
+
143
+ if pull.nil? &&
144
+ name.tag &&
145
+ name.tag.version.is_a?( QB::Package::Version::Leveled )
146
+ pull = !name.tag.version.dev?
147
+ end
148
+
149
+ logger.info "Ensuring image is present...",
150
+ name: name.to_s,
151
+ pull: pull,
152
+ push: push,
153
+ force: force
154
+
155
+ if force
156
+ logger.info "Forcing build...",
157
+ name: name.to_s
158
+
159
+ build! name: name, **build
160
+
161
+ QB::Docker::CLI.push?( name: name ) if push
162
+
163
+ return
164
+ end
165
+
166
+ if name.exists?
167
+ logger.info "Image exists", name: name.to_s
168
+ return
169
+ end
170
+
171
+ logger.info "Name does NOT exist",
172
+ name: name.to_s
173
+
174
+ if pull
175
+ logger.info "Attempting to pull...",
176
+ name: name.to_s
177
+
178
+ return if QB::Docker::CLI.pull?( name )
179
+ end
180
+
181
+
182
+ logger.info "Resorting to building...",
183
+ name: name.to_s,
184
+ path: build[:path]
185
+
186
+ build! name: name, **build
187
+
188
+ QB::Docker::CLI.push( name: name ) if push
189
+
190
+ end # .ensure_present!
191
+
192
+
193
+ # Properties
194
+ # ======================================================================
195
+
196
+ prop :id, type: t.non_empty_str
197
+
198
+ # prop :repo, type: QB::Docker::Repo
199
+ #
200
+ # prop :tag, type: QB::Docker::Image::Tag
201
+
202
+
203
+ # Instance Methods
204
+ # ======================================================================
205
+
206
+
207
+ end; end; end # class QB::Docker::Image
@@ -0,0 +1,309 @@
1
+ # encoding: UTF-8
2
+ # frozen_string_literal: true
3
+
4
+ # Requirements
5
+ # =======================================================================
6
+
7
+ # Stdlib
8
+ # -----------------------------------------------------------------------
9
+
10
+ # Deps
11
+ # -----------------------------------------------------------------------
12
+
13
+ # Project / Package
14
+ # -----------------------------------------------------------------------
15
+
16
+ require_relative './tag'
17
+
18
+
19
+ # Refinements
20
+ # ========================================================================
21
+
22
+ require 'nrser/refinements/types'
23
+ using NRSER::Types
24
+
25
+
26
+ # Definitions
27
+ # =======================================================================
28
+
29
+ # Image name.
30
+ #
31
+ # Input format will be one of:
32
+ #
33
+ # 1. name[:tag]
34
+ # 2. repository/name[:tag]
35
+ # 3. registry_server:port/name[:tag]
36
+ #
37
+ module QB
38
+ module Docker
39
+ class Image < QB::Data::Immutable
40
+ class Name < QB::Data::Immutable
41
+
42
+ # Mixins
43
+ # ========================================================================
44
+
45
+ extend ::MethodDecorators
46
+
47
+ include NRSER::Log::Mixin
48
+
49
+
50
+ # Class Methods
51
+ # ======================================================================
52
+
53
+ # Load from a {String}.
54
+ #
55
+ # @param [String] string
56
+ # @return [self]
57
+ #
58
+ def self.from_s string
59
+ strings = {}
60
+ segments = string.split '/'
61
+
62
+ if segments[-1].include? ':'
63
+ rest, _, tag = segments[-1].rpartition ':'
64
+ strings[:tag] = tag
65
+ segments[-1] = rest
66
+ else
67
+ rest = string
68
+ end
69
+
70
+ case segments.length
71
+ when 0
72
+ # Pass - construction will error
73
+ when 1
74
+ # Just a name
75
+ strings[:name] = segments[0]
76
+ else
77
+ if segments[0].include? ':'
78
+ # segments = [s_0, s_1, ... s_n]
79
+ # => repository = s_0
80
+ # segments = [s_1, s_2, ... s_n]
81
+ #
82
+ # like
83
+ #
84
+ # segments = ['docker.beiarea.com:8888', 'beiarea', 'wall']
85
+ # => registry_server = 'docker.beiarea.com'
86
+ # port = '8888'
87
+ # segments = ['beiarea', 'wall']
88
+ #
89
+ registry_server, _, port = segments.shift.rpartition ':'
90
+ strings[:registry_server] = registry_server
91
+ strings[:port] = port
92
+ end
93
+
94
+ if segments.length > 1
95
+ # segments = [s_0, s_1, ... s_m]
96
+ # => repository = s_0
97
+ # segments = [s_1, s_2, ... s_m]
98
+ #
99
+ # like
100
+ #
101
+ # segments = ['beiarea', 'wall']
102
+ # => repository = 'beiarea'
103
+ # segments = ['wall']
104
+ #
105
+ repository = segments.shift
106
+ strings[:repository] = repository
107
+ end
108
+
109
+ # I think Docker image names *can* have more than just a repo and name
110
+ # segment, though it's poorly supported from what I recall... though
111
+ # we will handle it by just re-joining whatever's left into the name.
112
+ #
113
+ # segments = [s_0, s_1, ... s_p]
114
+ # => name = "s_0/s_1/.../s_p"
115
+ #
116
+ # like
117
+ #
118
+ # segments = ['wall']
119
+ # => name = 'wall'
120
+ #
121
+ # or
122
+ #
123
+ # segments = ['www_rails', 'web']
124
+ # => name = 'www_rails/web'
125
+ #
126
+ strings[:name] = segments.join '/'
127
+ end
128
+
129
+ logger.debug "strings", strings
130
+
131
+ # Now turn them into value using their prop types
132
+ values = strings.transform_values_with_keys { |name, string|
133
+ prop = metadata[name]
134
+
135
+ if prop.type.respond_to? :from_s
136
+ prop.type.from_s string
137
+ else
138
+ string
139
+ end
140
+ }
141
+
142
+ logger.debug "values", values
143
+
144
+ # And construct!
145
+ new source: string, **values
146
+ end # .from_s
147
+
148
+
149
+ # Get an instance from a source.
150
+ #
151
+ # @param [self | String | Hash] source
152
+ # @return [self]
153
+ #
154
+ def self.from source
155
+ t.match source,
156
+ self, source,
157
+ t.str, method( :from_s ),
158
+ t.hash_, method( :from_data )
159
+ end # .from
160
+
161
+
162
+ # Queries
163
+ # --------------------------------------------------------------------------
164
+
165
+ # @see QB::Docker::CLI.image_named?
166
+ #
167
+ def self.exists? name
168
+ QB::Docker::CLI.image_named? name
169
+ end # .exists?
170
+
171
+
172
+ # @see QB::Docker::CLI.image_names
173
+ #
174
+ +QB::Util::Decorators::NoPropsInKwds
175
+ def self.list *args, **opts
176
+ QB::Docker::CLI.image_names *args, **opts
177
+ end # .list
178
+
179
+ singleton_class.send :alias_method, :all, :list
180
+
181
+
182
+ # Props
183
+ # ======================================================================
184
+
185
+ # @!attribute [r] source
186
+ # Source string this name was loaded from, if any.
187
+ #
188
+ # @return [String?]
189
+ #
190
+ prop :source,
191
+ type: t.non_empty_str?
192
+
193
+
194
+ # @!attribute [r] name
195
+ # For lack of a better name, the part of the name that's not anything
196
+ # else. It's also the only required part.
197
+ #
198
+ # @return [String]
199
+ # Can't be empty.
200
+ #
201
+ prop :name,
202
+ type: t.non_empty_str
203
+
204
+
205
+ # @!attribute [r] repository
206
+ # The repository name, if any.
207
+ #
208
+ # @return [String?]
209
+ # String is non-empty.
210
+ prop :repository,
211
+ type: t.non_empty_str?
212
+
213
+
214
+ # @!attribute [r] registry_server
215
+ # Registry server, if any.
216
+ #
217
+ # @return [String?]
218
+ # String is non-empty.
219
+ prop :registry_server,
220
+ type: t.non_empty_str?
221
+
222
+
223
+ # @!attribute [r] port
224
+ # Registry server port, if any.
225
+ #
226
+ # @return [Integer?]
227
+ # In range [1, 2**16 - 1]
228
+ prop :port,
229
+ type: t.port?
230
+
231
+
232
+ prop :tag,
233
+ type: t.maybe( QB::Docker::Image::Tag )
234
+
235
+
236
+ prop :string,
237
+ type: t.non_empty_str,
238
+ source: :to_s
239
+
240
+
241
+ invariant t.attrs( registry_server: t.nil, port: t.nil ) |
242
+ t.attrs( registry_server: ~t.nil, port: ~t.nil )
243
+
244
+
245
+ # Instance Methods
246
+ # ======================================================================
247
+
248
+ # Does the name exist in the local daemon?
249
+ #
250
+ # @see QB::Docker::CLI.image_named?
251
+ #
252
+ # @return [Boolean]
253
+ #
254
+ def exists?
255
+ QB::Docker::CLI.image_named? self
256
+ end # #exist?
257
+
258
+ alias_method :exist?, :exists?
259
+
260
+
261
+ # @todo Document dirty? method.
262
+ #
263
+ # @param [type] arg_name
264
+ # @todo Add name param description.
265
+ #
266
+ # @return [return_type]
267
+ # @todo Document return value.
268
+ #
269
+ def dirty?
270
+ !!tag.try( :dirty? )
271
+ end # #dirty?
272
+
273
+
274
+ def host
275
+ "#{ registry_server }:#{ port }" if registry_server && port
276
+ end
277
+
278
+
279
+ def formatted
280
+ [
281
+ host,
282
+ repository,
283
+ name,
284
+ ].compact.join( '/' ).thru { |without_tag|
285
+ if tag
286
+ "#{ without_tag }:#{ tag }"
287
+ else
288
+ without_tag
289
+ end
290
+ }
291
+ end
292
+
293
+
294
+ def to_s
295
+ formatted
296
+ end
297
+
298
+
299
+ def inspect
300
+ "#<#{ self.class.safe_name } #{ to_s }>"
301
+ end
302
+
303
+
304
+ def pretty_print q
305
+ q.text inspect
306
+ end
307
+
308
+
309
+ end; end; end; end # class QB::Docker::Image::Name