qb 0.4.3 → 0.4.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (73) hide show
  1. checksums.yaml +4 -4
  2. data/.vscode/.gitignore +2 -0
  3. data/.vscode/settings.json +3 -0
  4. data/VERSION +1 -1
  5. data/exe/qb +2 -0
  6. data/lib/python/qb/ansible/display_handler.py +136 -0
  7. data/lib/python/qb/ipc/rpc/__init__.py +0 -0
  8. data/lib/python/qb/ipc/rpc/client.py +170 -0
  9. data/lib/python/qb/ipc/stdio/__init__.py +17 -0
  10. data/lib/python/qb/ipc/stdio/logging.py +5 -21
  11. data/lib/python/qb/logging.py +97 -0
  12. data/lib/python/qb/strings.py +32 -3
  13. data/lib/qb.rb +10 -1
  14. data/lib/qb/ansible/cmd/playbook.rb +16 -10
  15. data/lib/qb/ansible/plugins/filters.rb +55 -0
  16. data/lib/qb/cli.rb +1 -0
  17. data/lib/qb/cli/dev.rb +124 -0
  18. data/lib/qb/cli/run.rb +283 -279
  19. data/lib/qb/cli/setup.rb +97 -74
  20. data/lib/qb/ipc/rpc.rb +42 -0
  21. data/lib/qb/ipc/rpc/server.rb +281 -0
  22. data/lib/qb/ipc/stdio/server.rb +1 -1
  23. data/lib/qb/role.rb +24 -18
  24. data/lib/qb/util.rb +173 -94
  25. data/lib/qb/version.rb +12 -0
  26. data/plugins/filter/path_filters.py +27 -10
  27. data/plugins/filter/rpc_filters.py +112 -0
  28. data/plugins/filter/string_filters.py +15 -0
  29. data/plugins/filter/version_filters.py +2 -4
  30. data/plugins/lookup/version_lookups.py +2 -2
  31. data/qb.gemspec +16 -5
  32. data/requirements.txt +3 -2
  33. data/roles/qb/dev/ref/archive/defaults/main.yml +24 -0
  34. data/roles/qb/dev/ref/archive/meta/main.yml +23 -0
  35. data/roles/qb/dev/ref/archive/meta/qb.yml +58 -0
  36. data/roles/qb/dev/ref/archive/tasks/main.yml +64 -0
  37. data/roles/qb/git/check/clean/tasks/main.yml +1 -1
  38. data/roles/qb/nodejs/yarn/setup/tasks/main.yml +1 -1
  39. data/roles/qb/python/config/defaults/main.yml +11 -0
  40. data/roles/qb/python/config/meta/main.yml +23 -0
  41. data/roles/qb/python/config/meta/qb.yml +58 -0
  42. data/roles/qb/python/config/tasks/main.yml +10 -0
  43. data/roles/qb/python/pip/defaults/main.yml +9 -0
  44. data/roles/qb/python/pip/meta/main.yml +23 -0
  45. data/roles/qb/python/pip/meta/qb.yml +55 -0
  46. data/roles/qb/python/pip/tasks/main.yml +10 -0
  47. data/roles/qb/python/sphinx/setup/defaults/main.yml +7 -0
  48. data/roles/qb/python/sphinx/setup/doc/notes/quickstart.md +81 -0
  49. data/roles/qb/python/sphinx/setup/meta/main.yml +23 -0
  50. data/roles/qb/python/sphinx/setup/meta/qb.yml +55 -0
  51. data/roles/qb/python/sphinx/setup/tasks/main.yml +10 -0
  52. data/roles/qb/ruby/gem/release/tasks/main.yml +5 -0
  53. metadata +73 -43
  54. data/exe/.qb_interop_receive +0 -32
  55. data/lib/python/qb/interop.py +0 -90
  56. data/lib/qb/util/interop.rb +0 -107
  57. data/plugins/filter/ruby_interop_filters.py +0 -30
  58. data/roles/qb/ruby/nrser/rspex/generate/defaults/main.yml +0 -10
  59. data/roles/qb/ruby/nrser/rspex/generate/meta/main.yml +0 -8
  60. data/roles/qb/ruby/nrser/rspex/generate/meta/qb.yml +0 -91
  61. data/roles/qb/ruby/nrser/rspex/generate/tasks/class.yml +0 -22
  62. data/roles/qb/ruby/nrser/rspex/generate/tasks/helpers.yml +0 -96
  63. data/roles/qb/ruby/nrser/rspex/generate/tasks/main.yml +0 -9
  64. data/roles/qb/ruby/nrser/rspex/generate/templates/class_spec.rb.j2 +0 -10
  65. data/roles/qb/ruby/nrser/rspex/issue/defaults/main.yml +0 -2
  66. data/roles/qb/ruby/nrser/rspex/issue/meta/main.yml +0 -8
  67. data/roles/qb/ruby/nrser/rspex/issue/meta/qb.yml +0 -69
  68. data/roles/qb/ruby/nrser/rspex/issue/tasks/main.yml +0 -21
  69. data/roles/qb/setup/meta/main.yml +0 -8
  70. data/roles/qb/setup/meta/qb.yml +0 -69
  71. data/roles/qb/setup/tasks/main.yml +0 -6
  72. data/roles/qb/setup/tasks/packages.yml +0 -3
  73. data/roles/qb/setup/tasks/packages/pip.yml +0 -6
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 023cb0a8d6ea38e59c77d21236f4c95944843093
4
- data.tar.gz: 7d307bb9c37772b4dd06a0d9f1f3af694c5425d4
3
+ metadata.gz: 1adb8b34388a2f7459a5acff24162cb7c3959401
4
+ data.tar.gz: 16174eacdd3a1b2bdd19084d674d2834284c7dfc
5
5
  SHA512:
6
- metadata.gz: 25520e571a52e90950dfff2263d4e57154900adb42f63fea546fa1967b175f45bdebd276145a5741f2001ebfbf1791b4065e28cda4b63588a70eeabc81a1cf53
7
- data.tar.gz: 3de536eadddf790d77ea0d3e97af2a1d6c03c39d179e45bc96b259f3302ccf3d6eda933731d89f7f370558d02b7166744127712013af220ca40b305c428274e9
6
+ metadata.gz: a214e3865fd351527b73637e87e1a53358c88362c24966fa64ced70868241ae4db773bb35d62289dc726c7fe66e8dfa607be3620a6340fd4c04d5a6aa8e48c8b
7
+ data.tar.gz: 99358c88001aa83cfdebdc9bc6b4af4fcc03468715c637f152cba56edb6e52b2fd73303a6198ffb02cd5883fd921b0904a30ac562ee5660de4a5aeacb0ecd6ad
@@ -0,0 +1,2 @@
1
+ # It seems like the `tags` file should not be checked in..?
2
+ /tags
@@ -0,0 +1,3 @@
1
+ {
2
+ "javascript.validate.enable": false,
3
+ }
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.4.3
1
+ 0.4.4
data/exe/qb CHANGED
@@ -62,6 +62,8 @@ def main *args
62
62
  [:setup, args.rest]
63
63
  when 'list', 'ls'
64
64
  [:list, *args.rest]
65
+ when '.dev'
66
+ [:dev, *args.rest]
65
67
  when 'root'
66
68
  puts QB::ROOT.to_s
67
69
  exit true
@@ -0,0 +1,136 @@
1
+ from __future__ import absolute_import, division, print_function
2
+ __metaclass__ = type
3
+
4
+ import logging
5
+ import qb.logging
6
+
7
+ # Find a Display if possible
8
+ try:
9
+ from __main__ import display
10
+ except ImportError:
11
+ try:
12
+ from ansible.utils.display import Display
13
+ except ImportError:
14
+ display = None
15
+ else:
16
+ display = Display()
17
+
18
+
19
+ class DisplayHandler(logging.Handler):
20
+ '''
21
+ A handler class that writes messages to Ansible's
22
+ `ansible.utils.display.Display`, which then writes them to the user output.
23
+
24
+ Includes static methods that let it act as a sort of a singleton, with
25
+ a single instance created on-demand.
26
+ '''
27
+
28
+
29
+ # Singleton instance
30
+ _instance = None
31
+
32
+
33
+ @staticmethod
34
+ def getDisplay():
35
+ '''
36
+ Get the display instance, if we were able to import or create one.
37
+
38
+ :rtype: None
39
+ :return: No display could be found or created.
40
+
41
+ :rtype: ansible.util.display.Display
42
+ :return: The display we're using.
43
+ '''
44
+ return display
45
+ # .getDisplay
46
+
47
+
48
+ @staticmethod
49
+ def getInstance():
50
+ '''
51
+ :rtype: DisplayHandler
52
+ :return: The singleton instance.
53
+ '''
54
+ if DisplayHandler._instance is None:
55
+ DisplayHandler._instance = DisplayHandler()
56
+ return DisplayHandler._instance
57
+ # .getInstance
58
+
59
+
60
+ @staticmethod
61
+ def enable():
62
+ '''
63
+ Enable logging to Ansible's display by sending {.getInstance()} to
64
+ {qb.logging.addHandler()}.
65
+
66
+ :raises:
67
+ '''
68
+ instance = DisplayHandler.getInstance()
69
+
70
+ if instance.display is None:
71
+ raise RuntimeError("No display available")
72
+
73
+ return qb.logging.addHandler(instance)
74
+ # .enable
75
+
76
+
77
+ def disable():
78
+ '''
79
+ Disable logging to Ansible's display be sending {.getInstance()} to
80
+ {qb.logging.removeHandler()}.
81
+ '''
82
+ return qb.logging.removeHandler(DisplayHandler.getInstance())
83
+ # .disable
84
+
85
+
86
+ def is_enabled():
87
+ return qb.logging.hasHandler(DisplayHandler.getInstance())
88
+ # .is_enabled
89
+
90
+
91
+ def __init__(self, display=None):
92
+ logging.Handler.__init__(self)
93
+
94
+ if display is None:
95
+ display = DisplayHandler.getDisplay()
96
+
97
+ self.display = display
98
+ # #__init__
99
+
100
+
101
+ def emit(self, record):
102
+ '''
103
+ Overridden to send log records to Ansible's display.
104
+ '''
105
+
106
+ if self.display is None:
107
+ # Nothing we can do, drop it
108
+ return
109
+
110
+ try:
111
+ self.format(record)
112
+
113
+ if record.levelname == 'DEBUG':
114
+ self.display.debug(record.message)
115
+
116
+ elif record.levelname == 'INFO':
117
+ # `verbose` I guess?
118
+ self.display.verbose(record.message)
119
+
120
+ elif record.levelname == 'WARNING':
121
+ self.display.warning(record.message)
122
+
123
+ elif record.levelname == 'ERROR':
124
+ self.display.error(record.message)
125
+
126
+ elif record.levelname == 'CRITICAL':
127
+ self.display.error("(CRITICAL) {}".format(record.message))
128
+
129
+ else:
130
+ pass
131
+ except (KeyboardInterrupt, SystemExit):
132
+ raise
133
+ except:
134
+ raise
135
+ # self.handleError(record)
136
+ # #emit
File without changes
@@ -0,0 +1,170 @@
1
+ ##############################################################################
2
+ #
3
+ ##############################################################################
4
+
5
+ # Imports
6
+ # ============================================================================
7
+
8
+ # Make Python 3-ish
9
+ from __future__ import (absolute_import, division, print_function)
10
+ __metaclass__ = type
11
+
12
+ # Stdlib
13
+ # ----------------------------------------------------------------------------
14
+
15
+ # Need {os.environ}, {os.path.join}
16
+ import os
17
+
18
+ # Need {urllib.quote_plus} to URL-encode socket paths for the
19
+ # {requests_unixsocket} package.
20
+ import urllib
21
+
22
+ # Deps
23
+ # ----------------------------------------------------------------------------
24
+
25
+ # Facilities making requests to UNIX domain sockets with the {requests}
26
+ # package.
27
+ import requests_unixsocket
28
+
29
+
30
+ # Constants
31
+ # ============================================================================
32
+
33
+ # The name of the ENV var that holds the socket path, which is created in a
34
+ # temp dir that only exists while the main QB process is running and is only
35
+ # accessible to the user that ran it.
36
+ #
37
+ RPC_SOCKET_ENV_VAR_NAME = 'QB_RPC_SOCKET'
38
+
39
+
40
+ # Module Variables
41
+ # ============================================================================
42
+
43
+ # A "module-level global" (yeah, weird they're called "globals") that holds
44
+ # the default {Client}, which is created on demand.
45
+ #
46
+ # Need to preface it's use with
47
+ #
48
+ # global _client
49
+ #
50
+ _client = None
51
+
52
+
53
+ # Module Functions
54
+ # ============================================================================
55
+ #
56
+ # Static helpers and functions that operate on the default {Client}, which is
57
+ # created on demand using the ENV var set by the QB master process that hosts
58
+ # the server (during normal execution... things are set up flexibly because I'm
59
+ # sure we'd want to do things differently during testing).
60
+ #
61
+ # NOTE Due to the way Python's files<->imports system works, this seems like
62
+ # the least annoying way to create a decent API without sticking stuff in
63
+ # the `__init__.py` file, which I *hate* because it's really hard to
64
+ # remember which ones have shit in them.
65
+ #
66
+
67
+ def client_from_env():
68
+ return Client(socket_path=os.environ[RPC_SOCKET_ENV_VAR_NAME])
69
+
70
+
71
+ def requests_path_for(socket_path):
72
+ '''
73
+ URL-quotes the socket file path and protocol prefixes it with `http+unix://`
74
+
75
+ The {requests} module - as extended by {requests_unixsocket} - requires that
76
+ the actual file path to the socket by URL-quoted, probably because it uses
77
+ some split-by-/ logic to parse it, which would normally consider the socket
78
+ path part of the HTTP path.
79
+
80
+ :rtype: str
81
+ :return: Path ready for use with {requests_unixsocket.Session}.
82
+ '''
83
+ return "http+unix://{}".format(urllib.quote_plus(socket_path))
84
+
85
+
86
+ def init_from_env(force=False):
87
+ global _client
88
+
89
+ if _client is None or force is True:
90
+ _client = client_from_env()
91
+ return True
92
+ else:
93
+ return False
94
+
95
+
96
+ def get_client():
97
+ global _client
98
+ init_from_env()
99
+ return _client
100
+
101
+
102
+ def set_client(client):
103
+ global _client
104
+ _client = client
105
+
106
+
107
+ def get(path):
108
+ return get_client().get(path)
109
+
110
+
111
+ def post(path, **payload):
112
+ return get_client().post(path, **payload)
113
+
114
+
115
+ def send(receiver, method, *args, **kwds):
116
+ return get_client().send(receiver, method, *args, **kwds)
117
+
118
+
119
+ class Client:
120
+ '''
121
+ RPC client for making calls to the QB master Ruby process (HTTP over
122
+ a UNIX domain socket).
123
+ '''
124
+
125
+ def __init__(self, socket_path):
126
+ self.socket_path = socket_path
127
+ self.session = requests_unixsocket.Session()
128
+ self.requests_path = requests_path_for(self.socket_path)
129
+
130
+
131
+ def full_path_for(self, path):
132
+ if path[0] == '/':
133
+ path = path[1:]
134
+ return os.path.join(self.requests_path, path)
135
+
136
+
137
+ def handle_response(self, response):
138
+ return response.json()['data']
139
+
140
+
141
+ def get(self, path):
142
+ return self.handle_response(
143
+ self.session.get(
144
+ self.full_path_for(path)
145
+ )
146
+ )
147
+
148
+
149
+ def post(self, path, **payload):
150
+ return self.handle_response(
151
+ self.session.post(
152
+ self.full_path_for(path),
153
+ json = payload,
154
+ )
155
+ )
156
+
157
+
158
+ def send(self, receiver, method, *args, **kwds):
159
+ return self.handle_response(
160
+ self.session.post(
161
+ self.full_path_for('/send'),
162
+ json = dict(
163
+ receiver = receiver,
164
+ method = method,
165
+ args = args,
166
+ kwds = kwds,
167
+ )
168
+ )
169
+ )
170
+
@@ -4,7 +4,24 @@ __metaclass__ = type
4
4
  import os
5
5
  import socket
6
6
 
7
+
7
8
  def path_env_var_name(name):
9
+ '''
10
+ Get the ENV var name whose value (if any) will be the file system path
11
+ to the UNIX socket file for that IO stream.
12
+
13
+ The names in current use:
14
+
15
+ >>> path_env_var_name('out')
16
+ 'QB_STDIO_OUT'
17
+
18
+ >>> path_env_var_name('err')
19
+ 'QB_STDIO_ERR'
20
+
21
+ >>> path_env_var_name('log')
22
+ 'QB_STDIO_LOG'
23
+ '''
24
+
8
25
  return "QB_STDIO_{}".format(name.upper())
9
26
 
10
27
 
@@ -5,9 +5,11 @@ import logging
5
5
  import threading
6
6
  import json
7
7
 
8
+ import qb.logging
8
9
  import qb.ipc.stdio
9
10
 
10
11
 
12
+ # FIXME After adding central logging stuff
11
13
  def getLogger(name, level=logging.DEBUG, io_client=qb.ipc.stdio.client):
12
14
  logger = logging.getLogger(name)
13
15
  if level is not None:
@@ -16,27 +18,6 @@ def getLogger(name, level=logging.DEBUG, io_client=qb.ipc.stdio.client):
16
18
  return Adapter(logger, {})
17
19
 
18
20
 
19
- class Adapter(logging.LoggerAdapter):
20
- def process(self, msg, kwds):
21
- payload = None
22
- if 'payload' in kwds:
23
- payload = kwds['payload']
24
- del kwds['payload']
25
-
26
- if payload:
27
- try:
28
- msg = msg.format(**payload)
29
- except:
30
- pass
31
-
32
- if 'extra' not in kwds:
33
- kwds['extra'] = {}
34
-
35
- kwds['extra']['payload'] = payload
36
-
37
- return msg, kwds
38
-
39
-
40
21
  class Handler(logging.Handler):
41
22
  """
42
23
  A handler class which writes logging records to the QB master process
@@ -103,6 +84,9 @@ class Handler(logging.Handler):
103
84
  def emit(self, record):
104
85
  """
105
86
  Emit a record.
87
+
88
+ Doc coppied in (TOOD re-write):
89
+
106
90
  Pickles the record and writes it to the socket in binary format.
107
91
  If there is an error with the socket, silently drop the packet.
108
92
  If there was a problem with the socket, re-establishes the
@@ -0,0 +1,97 @@
1
+ from __future__ import absolute_import, division, print_function
2
+ __metaclass__ = type
3
+
4
+ import logging
5
+ import threading
6
+ import json
7
+ import weakref
8
+
9
+ import qb.ipc.stdio
10
+
11
+ # Current handlers
12
+ _handlers = []
13
+
14
+
15
+ def hasHandler(handler):
16
+ return handler in _handlers
17
+ # hasHandler
18
+
19
+
20
+ def addHandler(handler):
21
+ '''
22
+ Add a handler if it's not already added.
23
+
24
+ :rtype: Boolean
25
+ :return: `True` if it was added (not already there), `False` if already
26
+ present.
27
+ '''
28
+ if not handler in _handlers:
29
+ _handlers.append(handler)
30
+ return True
31
+ else:
32
+ return False
33
+ # addHandler
34
+
35
+
36
+ def removeHandler(handler):
37
+ '''
38
+ Remove a handler.
39
+
40
+ :rtype: Boolean
41
+ :return: `True` if it was removed, `False` if wasn't there to begin
42
+ with.
43
+ '''
44
+ if handler in _handlers:
45
+ _handlers.remove(handler)
46
+ return True
47
+ else:
48
+ return False
49
+ # removeHandler
50
+
51
+
52
+ def getLogger(name, level=logging.DEBUG):
53
+ logger = logging.getLogger(name)
54
+ if level is not None:
55
+ logger.setLevel(level)
56
+ for handler in _handlers:
57
+ logger.addHandler(handler)
58
+ return Adapter(logger, {})
59
+
60
+
61
+ class Adapter(logging.LoggerAdapter):
62
+ '''
63
+ Adapter to allow Ruby's Semantic Logger (basis of NRSER::Log) style logging,
64
+ which is then easy to translate when sending logs up to the QB master
65
+ process via IPC.
66
+
67
+ Usage:
68
+
69
+ logger.info(
70
+ "Message with payload {value} interpolations",
71
+ payload = dict(
72
+ value = "interpolated into message",
73
+ mote = "values",
74
+ # ...
75
+ )
76
+ )
77
+
78
+ '''
79
+
80
+ def process(self, msg, kwds):
81
+ payload = None
82
+ if 'payload' in kwds:
83
+ payload = kwds['payload']
84
+ del kwds['payload']
85
+
86
+ if payload:
87
+ try:
88
+ msg = msg.format(**payload)
89
+ except:
90
+ pass
91
+
92
+ if 'extra' not in kwds:
93
+ kwds['extra'] = {}
94
+
95
+ kwds['extra']['payload'] = payload
96
+
97
+ return msg, kwds