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
@@ -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
+