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
@@ -5,13 +5,7 @@
5
5
  # -----------------------------------------------------------------------
6
6
 
7
7
  require 'nrser'
8
-
9
-
10
- # Refinements
11
- # =======================================================================
12
-
13
- require 'nrser/refinements'
14
- using NRSER
8
+ require 'nrser/props/immutable/instance_variables'
15
9
 
16
10
 
17
11
  # Declarations
@@ -27,10 +21,15 @@ module QB::Util; end
27
21
  # Base class for QB "resources" - object representations of outside structures
28
22
  # and concepts: things that live on disk, in remote systems or other runtimes.
29
23
  #
30
- # At the moment, it's simply an extension of {NRSER::Meta::Props::Base}, but
31
- # it is here to serve as a centralized point to implement common behaviors,
32
- # some of which would be lifted up to the {NRSER} properties system if they
33
- # prove sufficiently useful and general.
34
- #
35
- class QB::Util::Resource < NRSER::Meta::Props::Base
24
+ class QB::Util::Resource
25
+
26
+ # Mixins
27
+ # =====================================================================
28
+
29
+ include NRSER::Props::Immutable::InstanceVariables
30
+
31
+
32
+ def initialize values = {}
33
+ initialize_props values
34
+ end # #initialize
36
35
  end # class QB::Util::Resource
@@ -34,6 +34,16 @@ module QB
34
34
  # Class Methods
35
35
  # =====================================================================
36
36
 
37
+ # Are we running in local development? Looks for the `//dev` directory's
38
+ # presence.
39
+ #
40
+ # @return [Boolean]
41
+ #
42
+ def self.local_dev?
43
+ (QB::ROOT / 'dev').directory?
44
+ end
45
+
46
+
37
47
  def self.gemspec
38
48
  Gem.loaded_specs[GEM_NAME]
39
49
  end
@@ -1,19 +1,14 @@
1
1
  #!/usr/bin/env ruby
2
2
  # WANT_JSON
3
3
 
4
- # init bundler in dev env
5
- if ENV['QB_DEV_ENV']
6
- ENV.each {|k, v|
7
- if k.start_with? 'QB_DEV_ENV_'
8
- ENV[k.sub('QB_DEV_ENV_', '')] = v
9
- end
10
- }
11
- require 'bundler/setup'
12
- end
4
+ # Reinstate Bundler ENV vars if they have been moved
5
+ load ENV['QB_REBUNDLE_PATH'] if ENV['QB_REBUNDLE_PATH']
6
+
13
7
 
14
8
  require 'ostruct'
15
9
 
16
10
  require 'qb'
11
+ require 'qb/ansible/module'
17
12
  require 'qb/package/version'
18
13
  require 'cmds'
19
14
  require 'pathname'
@@ -283,4 +278,4 @@ class PathFacts < QB::Ansible::Module
283
278
  end
284
279
  end
285
280
 
286
- PathFacts.new.run
281
+ PathFacts.run!
@@ -0,0 +1,105 @@
1
+ #!/usr/bin/env ruby
2
+ # WANT_JSON
3
+
4
+ # Reinstate Bundler ENV vars if they have been moved
5
+ load ENV['QB_REBUNDLE_PATH'] if ENV['QB_REBUNDLE_PATH']
6
+
7
+ require 'qb/ansible/module'
8
+
9
+ require 'nrser'
10
+ require 'nrser/core_ext/string'
11
+ require 'nrser/types'
12
+
13
+ def t; NRSER::Types; end
14
+
15
+ QB::Ansible::Module.setup_io!
16
+
17
+ class QB_Ansible_Module_Runner < QB::Ansible::Module
18
+
19
+
20
+ # @!attribute [r] name
21
+ # Name of module to run (relative path from `//lib/qb/ansible/modules`)
22
+ #
23
+ # @return [PropRubyType]
24
+ #
25
+ arg :module_name,
26
+ type: t.non_empty_str,
27
+ aliases: [ :name ],
28
+ reader: { name: false }
29
+
30
+ arg :module_require,
31
+ type: (
32
+ t.nil | # Guess and try the require path
33
+ t.false | # Don't require anything; module class must already be loaded
34
+ t.non_empty_str | # String to require
35
+ t.list( t.non_empty_str ) # List of strings to require
36
+ ),
37
+ aliases: [ :require ],
38
+ reader: { require: false }
39
+
40
+ arg :module_args,
41
+ type: t.map,
42
+ aliases: [ :args ],
43
+ reader: { args: false },
44
+ default: ->{ {} }
45
+
46
+ def main
47
+ class_name = module_name.camelize
48
+ class_path = module_name.underscore
49
+
50
+ logger.trace "Received module argument",
51
+ module_name: module_name,
52
+ class_name: class_name,
53
+ class_path: class_path,
54
+ module_require: module_require
55
+
56
+ unless class_name.start_with? '::'
57
+ class_name = "QB::Ansible::Modules::#{ class_name }"
58
+ end
59
+
60
+ require_paths = case module_require
61
+ when nil
62
+ if class_path.start_with? '/'
63
+ [ class_path[1..-1] ]
64
+ else
65
+ [ "qb/ansible/modules/#{ class_path }" ]
66
+ end
67
+ when false
68
+ []
69
+ when String
70
+ [ module_require ]
71
+ when Array
72
+ module_require
73
+ end
74
+
75
+ if require_paths.empty?
76
+ logger.trace "No requiring any paths"
77
+ else
78
+ logger.trace "Requiring paths...",
79
+ require_paths: require_paths
80
+
81
+ require_paths.each do |require_path|
82
+ begin
83
+ require require_path
84
+ rescue LoadError => error
85
+ logger.warn "Failed to require #{ require_path.inspect }", error
86
+ end
87
+ end
88
+ end
89
+
90
+ module_class = class_name.to_const!
91
+
92
+ logger.trace "Loaded class",
93
+ module_class: module_class.to_s
94
+
95
+ instance = module_class.from_data module_args
96
+ instance.args_source = args_source
97
+ instance.args = args['args'] || args['module_args'] || {}
98
+
99
+ instance.run!
100
+ end
101
+
102
+ end # class QB_Ansible_Module_Runner
103
+
104
+
105
+ QB_Ansible_Module_Runner.run!
@@ -1,29 +1,14 @@
1
1
  #!/usr/bin/env ruby
2
2
  # WANT_JSON
3
3
 
4
- # init bundler in dev env
5
-
6
- # HACK Keep track of the ENV vars we overwrite so we can swap them back in
7
- # later when we need to run the command.
8
- #
9
- $replaced_env_vars = {}
10
-
11
- if ENV['QB_DEV_ENV']
12
- ENV.each {|k, v|
13
- if k.start_with? 'QB_DEV_ENV_'
14
- key = k.sub('QB_DEV_ENV_', '')
15
- $replaced_env_vars[key] = [ENV[key], v]
16
- ENV[key] = v
17
- end
18
- }
19
- require 'bundler/setup'
20
- end
4
+ # Reinstate Bundler ENV vars if they have been moved
5
+ load ENV['QB_REBUNDLE_PATH'] if ENV['QB_REBUNDLE_PATH']
21
6
 
7
+ require 'nrser'
22
8
  require 'qb'
9
+ require 'qb/ansible/module'
23
10
  require 'cmds'
24
11
 
25
- using NRSER
26
-
27
12
  class Stream < QB::Ansible::Module
28
13
  def main
29
14
  template = @args['template'] || @args['cmd']
@@ -71,15 +56,10 @@ class Stream < QB::Ansible::Module
71
56
  END
72
57
  end
73
58
 
74
- # HACK Swap any ENV vars we replaced at the top back in
75
- $replaced_env_vars.each do |key, (original, replacement)|
76
- ENV[key] = original
77
- end
78
-
79
- cmd.stream!
59
+ qb_unbundle! { cmd.stream! }
80
60
 
81
61
  changed!
82
62
  end
83
63
  end
84
64
 
85
- Stream.new.run
65
+ Stream.run!
@@ -0,0 +1,25 @@
1
+
2
+ # Reinstate Bundler ENV vars if they have been moved
3
+ load ENV['QB_REBUNDLE_PATH'] if ENV['QB_REBUNDLE_PATH']
4
+
5
+ require 'active_support/core_ext/class/subclasses'
6
+ require 'qb/ansible/module'
7
+
8
+ require 'nrser'
9
+ require 'nrser/types'
10
+
11
+ def t; NRSER::Types; end
12
+
13
+ QB::Ansible::Module.setup_io!
14
+
15
+ at_exit do
16
+ if $!
17
+ QB::Ansible::Module.logger.fatal "Error raised pre-execution", $!
18
+ else
19
+ QB::Ansible::Module.descendants.find_only { |klass|
20
+ begin
21
+ klass.instance_method( :main ).source_location[0] == $0
22
+ rescue; end
23
+ }.run!
24
+ end
25
+ end
@@ -0,0 +1,123 @@
1
+
2
+ # Reinstate Bundler ENV vars if they have been moved
3
+ load ENV['QB_REBUNDLE_PATH'] if ENV['QB_REBUNDLE_PATH']
4
+
5
+ require 'qb/ansible/module'
6
+ require 'nrser/types'
7
+ require 'nrser/core_ext/exception'
8
+
9
+
10
+ # Constants
11
+ # ========================================================================
12
+
13
+ MAIN = self
14
+ NAME = File.basename( $0 ).gsub( '.', '/' ).camelize
15
+ LOGGER = NRSER::Log[ NAME ]
16
+ RAW_ARGS, ARGS_SOURCE = QB::Ansible::Module.load_args
17
+ ARG_DEFS = {}
18
+ ARGS = {}
19
+
20
+
21
+ # Global Variables
22
+ # ============================================================================
23
+
24
+ $response = QB::Ansible::Module::Response.new
25
+
26
+
27
+ # Global Methods
28
+ # ============================================================================
29
+
30
+ # Constant Readers
31
+ # ----------------------------------------------------------------------------
32
+ #
33
+ # Just to make 'em feel familiar...
34
+ #
35
+
36
+ def logger; LOGGER; end
37
+ def t; NRSER::Types; end
38
+ def name; NAME; end
39
+ def raw_args; RAW_ARGS; end
40
+ def arg_defs; ARG_DEFS; end
41
+ def args; ARGS; end
42
+ def response; $response; end
43
+
44
+
45
+ def arg name, **opts
46
+ t.non_empty_sym.check! name
47
+
48
+ prop = NRSER::Props::Prop.new \
49
+ MAIN,
50
+ name,
51
+ reader: true,
52
+ writer: false, **opts
53
+
54
+ arg_defs[name] = prop
55
+
56
+ args[prop.name] = if raw_args.key? prop.name
57
+ prop.check! raw_args[prop.name]
58
+ else
59
+ prop.default **args
60
+ end
61
+
62
+ prop.names.each do |name|
63
+ MAIN.send :define_method, name do
64
+ args[prop.name]
65
+ end
66
+ end
67
+ end
68
+
69
+
70
+ def fail! msg, **values
71
+ $response = response.to_failure \
72
+ msg: msg,
73
+ **values
74
+
75
+ exit false
76
+ end
77
+
78
+
79
+ def response_sent?
80
+ !!$response_sent
81
+ end
82
+
83
+
84
+ def send_response!
85
+ if response_sent?
86
+ logger.warn "Response has already been sent, ignoring..."
87
+ return
88
+ end
89
+
90
+ # NOTE *Need* to assign here or it disappears after the `logger.fatal`
91
+ # call!
92
+ if (error = $!) && !error.is_a?( SystemExit )
93
+ # We failed!
94
+
95
+ # Log it
96
+ logger.fatal error
97
+
98
+ # Create a new response; don't want to carry whatever the current one
99
+ # has in it back? Maybe we do? Ans'y doesn't set facts when modules fail,
100
+ # it seems, so might not hurt...
101
+ $response = response.to_failure \
102
+ msg: error.to_s,
103
+ exception: error.format
104
+ end
105
+
106
+ response_data = response.to_data( add_class: false ).compact
107
+
108
+ logger.debug "Responding", response_data
109
+ STDOUT.print JSON.pretty_generate( response_data )
110
+
111
+ exit !response.failed
112
+ rescue SystemExit
113
+ raise
114
+ rescue Exception => error
115
+ logger.error "Internal QB Error: Failed to send response", error
116
+ exit false
117
+ end
118
+
119
+
120
+ at_exit { send_response! unless response_sent? }
121
+
122
+
123
+ QB::Ansible::Module.setup_io!
@@ -0,0 +1,39 @@
1
+ # Restores Bundler ENV vars that were moved when spawning the Ansible process.
2
+ #
3
+ # See `//lib/qb/util/bundler.rb` for documentation on how and why we do this.
4
+ #
5
+
6
+ # Keep track of the ENV vars we overwrite so we can swap them back in
7
+ # later when we need to do things like shell out.
8
+ #
9
+ $qb_replaced_env_vars = {}
10
+
11
+
12
+ def qb_rebundle!
13
+ ENV.each do |k, v|
14
+ if k.start_with? 'QB_BUNDLER_ENV_'
15
+ key = k.sub 'QB_BUNDLER_ENV_', ''
16
+ $qb_replaced_env_vars[key] = [ENV[key], v]
17
+ ENV[key] = v
18
+ end
19
+ end
20
+ end
21
+
22
+
23
+ def qb_unbundle! &block
24
+ $qb_replaced_env_vars.each do |key, (original, replacement)|
25
+ ENV[key] = original
26
+ end
27
+
28
+ $qb_replaced_env_vars = {}
29
+
30
+ if block
31
+ block.call.tap { qb_rebundle! }
32
+ end
33
+ end
34
+
35
+
36
+ qb_rebundle!
37
+
38
+
39
+ require 'bundler/setup'
@@ -0,0 +1,56 @@
1
+ # Imports
2
+ # ============================================================================
3
+
4
+ from __future__ import (absolute_import, division, print_function)
5
+ __metaclass__ = type
6
+
7
+ import sys
8
+ import re
9
+ import os
10
+
11
+ from ansible.errors import AnsibleError
12
+ from jinja2.filters import contextfilter
13
+
14
+ def to_dict( value ):
15
+ '''Call Python's built-in `dict()` on the value.
16
+
17
+ Can't believe this shit isn't built-in to Jinja/Ansible :/
18
+ '''
19
+
20
+ return dict( value )
21
+
22
+
23
+ @contextfilter
24
+ def select_by_keys( context, dct, test_name, *args, **kwds ):
25
+ # raise StandardError(
26
+ # "args: {}, kwds: {}".format( args, kwds )
27
+ # )
28
+
29
+ test = lambda key: context.environment.call_test(
30
+ test_name, key, args, kwds
31
+ )
32
+
33
+ new_dct = {}
34
+
35
+ for key, value in dct.iteritems():
36
+ if test( key ):
37
+ new_dct[key] = value
38
+
39
+ return new_dct
40
+
41
+
42
+ class FilterModule(object):
43
+ '''Some dict filters'''
44
+
45
+ def filters(self):
46
+ return {
47
+ 'dict': to_dict,
48
+ 'select_by_keys': select_by_keys,
49
+ }
50
+
51
+
52
+ # testing - call camel_case on first cli arg and print result
53
+ if __name__ == '__main__':
54
+ import doctest
55
+ doctest.testmod()
56
+