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,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