qb 0.3.3 → 0.3.4
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.
- checksums.yaml +4 -4
- data/exe/qb +10 -91
- data/lib/qb/ansible.rb +2 -0
- data/lib/qb/ansible/cmds/playbook.rb +193 -0
- data/lib/qb/ansible/config_file.rb +10 -5
- data/lib/qb/ansible/env.rb +63 -3
- data/lib/qb/ansible/module.rb +194 -0
- data/lib/qb/ansible_module.rb +2 -182
- data/lib/qb/cli/play.rb +28 -2
- data/lib/qb/cli/run.rb +0 -0
- data/lib/qb/role.rb +1 -1
- data/lib/qb/util.rb +21 -5
- data/lib/qb/util/stdio.rb +52 -0
- data/lib/qb/version.rb +1 -1
- data/library/path_facts +1 -1
- data/library/stream +1 -1
- data/node_modules/.bin/semver +1 -1
- data/qb.gemspec +2 -2
- data/roles/nrser.blockinfile/tests/expected/test-follow/link2.txt +1 -1
- data/roles/nrser.blockinfile/tests/fixtures/test-follow/link0.txt +1 -1
- data/roles/nrser.blockinfile/tests/fixtures/test-follow/link1.txt +1 -1
- data/roles/nrser.blockinfile/tests/fixtures/test-follow/link2.txt +1 -1
- data/roles/nrser.blockinfile/tests/roles/yaegashi.blockinfile +1 -1
- data/roles/qb.bump/library/bump +2 -2
- data/roles/qb.git_submodule_update/library/git_submodule_update +1 -1
- data/roles/qb.gitignore/files/gitignore/Clojure.gitignore +1 -1
- data/roles/qb.gitignore/files/gitignore/Fortran.gitignore +1 -1
- data/roles/qb.role/meta/qb.yml +1 -1
- data/roles/qb.role/templates/library/module.rb.j2 +1 -1
- data/roles/qb/gem/release/tasks/main.yml +98 -39
- metadata +10 -8
- data/lib/qb/ansible/playbook.rb +0 -4
@@ -0,0 +1,194 @@
|
|
1
|
+
require 'json'
|
2
|
+
require 'pp'
|
3
|
+
|
4
|
+
|
5
|
+
# Declarations
|
6
|
+
# =====================================================================
|
7
|
+
|
8
|
+
module QB; end
|
9
|
+
module QB::Ansible; end
|
10
|
+
|
11
|
+
|
12
|
+
# Definitions
|
13
|
+
# =====================================================================
|
14
|
+
|
15
|
+
class QB::Ansible::Module
|
16
|
+
|
17
|
+
# Class Variables
|
18
|
+
# =====================================================================
|
19
|
+
|
20
|
+
@@arg_types = {}
|
21
|
+
|
22
|
+
|
23
|
+
# Class Methods
|
24
|
+
# =====================================================================
|
25
|
+
|
26
|
+
def self.stringify_keys hash
|
27
|
+
hash.map {|k, v| [k.to_s, v]}.to_h
|
28
|
+
end
|
29
|
+
|
30
|
+
|
31
|
+
def self.arg name, type
|
32
|
+
@@arg_types[name.to_sym] = type
|
33
|
+
end
|
34
|
+
|
35
|
+
|
36
|
+
# Constructor
|
37
|
+
# =====================================================================
|
38
|
+
|
39
|
+
def initialize
|
40
|
+
@changed = false
|
41
|
+
@input_file = ARGV[0]
|
42
|
+
@input = File.read @input_file
|
43
|
+
@args = JSON.load @input
|
44
|
+
@facts = {}
|
45
|
+
@warnings = []
|
46
|
+
|
47
|
+
@qb_stdio_out = nil
|
48
|
+
@qb_stdio_err = nil
|
49
|
+
@qb_stdio_in = nil
|
50
|
+
|
51
|
+
# debug "HERE!"
|
52
|
+
# debug ENV
|
53
|
+
|
54
|
+
# if QB_STDIO_ env vars are set send stdout and stderr
|
55
|
+
# to those sockets to print in the parent process
|
56
|
+
|
57
|
+
if ENV['QB_STDIO_ERR']
|
58
|
+
@qb_stdio_err = $stderr = UNIXSocket.new ENV['QB_STDIO_ERR']
|
59
|
+
|
60
|
+
debug "Connected to QB stderr stream at #{ ENV['QB_STDIO_ERR'] } #{ @qb_stdio_err.path }."
|
61
|
+
end
|
62
|
+
|
63
|
+
if ENV['QB_STDIO_OUT']
|
64
|
+
@qb_stdio_out = $stdout = UNIXSocket.new ENV['QB_STDIO_OUT']
|
65
|
+
|
66
|
+
debug "Connected to QB stdout stream at #{ ENV['QB_STDIO_OUT'] }."
|
67
|
+
end
|
68
|
+
|
69
|
+
if ENV['QB_STDIO_IN']
|
70
|
+
@qb_stdio_in = UNIXSocket.new ENV['QB_STDIO_IN']
|
71
|
+
|
72
|
+
debug "Connected to QB stdin stream at #{ ENV['QB_STDIO_IN'] }."
|
73
|
+
end
|
74
|
+
|
75
|
+
@@arg_types.each {|key, type|
|
76
|
+
var_name = "@#{ key.to_s }"
|
77
|
+
|
78
|
+
unless instance_variable_get(var_name).nil?
|
79
|
+
raise ArgumentError.new NRSER.squish <<-END
|
80
|
+
an instance variable named #{ var_name } exists
|
81
|
+
with value #{ instance_variable_get(var_name).inspect }
|
82
|
+
END
|
83
|
+
end
|
84
|
+
|
85
|
+
instance_variable_set var_name,
|
86
|
+
type.check(@args.fetch(key.to_s))
|
87
|
+
}
|
88
|
+
end
|
89
|
+
|
90
|
+
|
91
|
+
|
92
|
+
# Instance Methods
|
93
|
+
# =====================================================================
|
94
|
+
|
95
|
+
# Logging
|
96
|
+
# ---------------------------------------------------------------------
|
97
|
+
#
|
98
|
+
# Logging is a little weird in Ansible modules... Ansible has facilities
|
99
|
+
# for notifying the user about warnings and depreciations, which we will
|
100
|
+
# make accessible, but it doesn't seem to have facilities for notices and
|
101
|
+
# debugging, which I find very useful.
|
102
|
+
#
|
103
|
+
# When run inside of QB (targeting localhost only at the moment, sadly)
|
104
|
+
# we expose additional IO channels for STDIN, STDOUT and STDERR through
|
105
|
+
# opening unix socket files that the main QB process spawns threads to
|
106
|
+
# listen to, and we provide those file paths via environment variables
|
107
|
+
# so modules can pick those up and interact with those streams, allowing
|
108
|
+
# them to act like regular scripts inside Ansible-world (see
|
109
|
+
# QB::Util::STDIO for details and implementation).
|
110
|
+
#
|
111
|
+
# We use those channels if present to provide logging mechanisms.
|
112
|
+
#
|
113
|
+
|
114
|
+
# Forward args to {QB.debug} if we are connected to a QB STDERR stream
|
115
|
+
# (write to STDERR).
|
116
|
+
#
|
117
|
+
# @param args see QB.debug
|
118
|
+
#
|
119
|
+
def debug *args
|
120
|
+
if @qb_stdio_err
|
121
|
+
header = "<QB::Ansible::Module #{ self.class.name }>"
|
122
|
+
|
123
|
+
if args[0].is_a? String
|
124
|
+
header += " " + args.shift
|
125
|
+
end
|
126
|
+
|
127
|
+
QB.debug header, *args
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
def info msg
|
132
|
+
if @qb_stdio_err
|
133
|
+
$stderr.puts msg
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
# Append a warning message to @warnings.
|
138
|
+
def warn msg
|
139
|
+
@warnings << msg
|
140
|
+
end
|
141
|
+
|
142
|
+
|
143
|
+
def run
|
144
|
+
result = main
|
145
|
+
|
146
|
+
case result
|
147
|
+
when nil
|
148
|
+
# pass
|
149
|
+
when Hash
|
150
|
+
@facts.merge! result
|
151
|
+
else
|
152
|
+
raise "result of #main should be nil or Hash, found #{ result.inspect }"
|
153
|
+
end
|
154
|
+
|
155
|
+
done
|
156
|
+
end
|
157
|
+
|
158
|
+
def changed! facts = {}
|
159
|
+
@changed = true
|
160
|
+
@facts.merge! facts
|
161
|
+
done
|
162
|
+
end
|
163
|
+
|
164
|
+
def done
|
165
|
+
exit_json changed: @changed,
|
166
|
+
ansible_facts: self.class.stringify_keys(@facts),
|
167
|
+
warnings: @warnings
|
168
|
+
end
|
169
|
+
|
170
|
+
def exit_json hash
|
171
|
+
# print JSON response to process' actual STDOUT (instead of $stdout,
|
172
|
+
# which may be pointing to the qb parent process)
|
173
|
+
STDOUT.print JSON.dump(self.class.stringify_keys(hash))
|
174
|
+
|
175
|
+
[
|
176
|
+
[:stdin, @qb_stdio_in],
|
177
|
+
[:stdout, @qb_stdio_out],
|
178
|
+
[:stderr, @qb_stdio_err],
|
179
|
+
].each do |name, socket|
|
180
|
+
if socket
|
181
|
+
debug "Flushing socket #{ name }."
|
182
|
+
socket.flush
|
183
|
+
debug "Closing #{ name } socket at #{ socket.path.to_s }."
|
184
|
+
socket.close
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
exit 0
|
189
|
+
end
|
190
|
+
|
191
|
+
def fail msg
|
192
|
+
exit_json failed: true, msg: msg, warnings: @warnings
|
193
|
+
end
|
194
|
+
end # class QB::Ansible::Module
|
data/lib/qb/ansible_module.rb
CHANGED
@@ -1,185 +1,5 @@
|
|
1
|
-
|
2
|
-
require 'pp'
|
1
|
+
require_relative './ansible/module'
|
3
2
|
|
4
3
|
module QB
|
5
|
-
|
6
|
-
|
7
|
-
# Class Variables
|
8
|
-
# =====================================================================
|
9
|
-
|
10
|
-
@@arg_types = {}
|
11
|
-
|
12
|
-
|
13
|
-
# Class Methods
|
14
|
-
# =====================================================================
|
15
|
-
|
16
|
-
def self.stringify_keys hash
|
17
|
-
hash.map {|k, v| [k.to_s, v]}.to_h
|
18
|
-
end
|
19
|
-
|
20
|
-
|
21
|
-
def self.arg name, type
|
22
|
-
@@arg_types[name.to_sym] = type
|
23
|
-
end
|
24
|
-
|
25
|
-
|
26
|
-
# Constructor
|
27
|
-
# =====================================================================
|
28
|
-
|
29
|
-
def initialize
|
30
|
-
@changed = false
|
31
|
-
@input_file = ARGV[0]
|
32
|
-
@input = File.read @input_file
|
33
|
-
@args = JSON.load @input
|
34
|
-
@facts = {}
|
35
|
-
@warnings = []
|
36
|
-
|
37
|
-
@qb_stdio_out = nil
|
38
|
-
@qb_stdio_err = nil
|
39
|
-
@qb_stdio_in = nil
|
40
|
-
|
41
|
-
# debug "HERE!"
|
42
|
-
# debug ENV
|
43
|
-
|
44
|
-
# if QB_STDIO_ env vars are set send stdout and stderr
|
45
|
-
# to those sockets to print in the parent process
|
46
|
-
|
47
|
-
if ENV['QB_STDIO_ERR']
|
48
|
-
@qb_stdio_err = $stderr = UNIXSocket.new ENV['QB_STDIO_ERR']
|
49
|
-
|
50
|
-
debug "Connected to QB stderr stream at #{ ENV['QB_STDIO_ERR'] } #{ @qb_stdio_err.path }."
|
51
|
-
end
|
52
|
-
|
53
|
-
if ENV['QB_STDIO_OUT']
|
54
|
-
@qb_stdio_out = $stdout = UNIXSocket.new ENV['QB_STDIO_OUT']
|
55
|
-
|
56
|
-
debug "Connected to QB stdout stream at #{ ENV['QB_STDIO_OUT'] }."
|
57
|
-
end
|
58
|
-
|
59
|
-
if ENV['QB_STDIO_IN']
|
60
|
-
@qb_stdio_in = UNIXSocket.new ENV['QB_STDIO_IN']
|
61
|
-
|
62
|
-
debug "Connected to QB stdin stream at #{ ENV['QB_STDIO_IN'] }."
|
63
|
-
end
|
64
|
-
|
65
|
-
@@arg_types.each {|key, type|
|
66
|
-
var_name = "@#{ key.to_s }"
|
67
|
-
|
68
|
-
unless instance_variable_get(var_name).nil?
|
69
|
-
raise ArgumentError.new NRSER.squish <<-END
|
70
|
-
an instance variable named #{ var_name } exists
|
71
|
-
with value #{ instance_variable_get(var_name).inspect }
|
72
|
-
END
|
73
|
-
end
|
74
|
-
|
75
|
-
instance_variable_set var_name,
|
76
|
-
type.check(@args.fetch(key.to_s))
|
77
|
-
}
|
78
|
-
end
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
# Instance Methods
|
83
|
-
# =====================================================================
|
84
|
-
|
85
|
-
# Logging
|
86
|
-
# ---------------------------------------------------------------------
|
87
|
-
#
|
88
|
-
# Logging is a little weird in Ansible modules... Ansible has facilities
|
89
|
-
# for notifying the user about warnings and depreciations, which we will
|
90
|
-
# make accessible, but it doesn't seem to have facilities for notices and
|
91
|
-
# debugging, which I find very useful.
|
92
|
-
#
|
93
|
-
# When run inside of QB (targeting localhost only at the moment, sadly)
|
94
|
-
# we expose additional IO channels for STDIN, STDOUT and STDERR through
|
95
|
-
# opening unix socket files that the main QB process spawns threads to
|
96
|
-
# listen to, and we provide those file paths via environment variables
|
97
|
-
# so modules can pick those up and interact with those streams, allowing
|
98
|
-
# them to act like regular scripts inside Ansible-world (see
|
99
|
-
# QB::Util::STDIO for details and implementation).
|
100
|
-
#
|
101
|
-
# We use those channels if present to provide logging mechanisms.
|
102
|
-
#
|
103
|
-
|
104
|
-
# Forward args to {QB.debug} if we are connected to a QB STDERR stream
|
105
|
-
# (write to STDERR).
|
106
|
-
#
|
107
|
-
# @param args see QB.debug
|
108
|
-
#
|
109
|
-
def debug *args
|
110
|
-
if @qb_stdio_err
|
111
|
-
header = "<QB::AnsibleModule #{ self.class.name }>"
|
112
|
-
|
113
|
-
if args[0].is_a? String
|
114
|
-
header += " " + args.shift
|
115
|
-
end
|
116
|
-
|
117
|
-
QB.debug header, *args
|
118
|
-
end
|
119
|
-
end
|
120
|
-
|
121
|
-
def info msg
|
122
|
-
if @qb_stdio_err
|
123
|
-
$stderr.puts msg
|
124
|
-
end
|
125
|
-
end
|
126
|
-
|
127
|
-
# Append a warning message to @warnings.
|
128
|
-
def warn msg
|
129
|
-
@warnings << msg
|
130
|
-
end
|
131
|
-
|
132
|
-
|
133
|
-
def run
|
134
|
-
result = main
|
135
|
-
|
136
|
-
case result
|
137
|
-
when nil
|
138
|
-
# pass
|
139
|
-
when Hash
|
140
|
-
@facts.merge! result
|
141
|
-
else
|
142
|
-
raise "result of #main should be nil or Hash, found #{ result.inspect }"
|
143
|
-
end
|
144
|
-
|
145
|
-
done
|
146
|
-
end
|
147
|
-
|
148
|
-
def changed! facts = {}
|
149
|
-
@changed = true
|
150
|
-
@facts.merge! facts
|
151
|
-
done
|
152
|
-
end
|
153
|
-
|
154
|
-
def done
|
155
|
-
exit_json changed: @changed,
|
156
|
-
ansible_facts: self.class.stringify_keys(@facts),
|
157
|
-
warnings: @warnings
|
158
|
-
end
|
159
|
-
|
160
|
-
def exit_json hash
|
161
|
-
# print JSON response to process' actual STDOUT (instead of $stdout,
|
162
|
-
# which may be pointing to the qb parent process)
|
163
|
-
STDOUT.print JSON.dump(self.class.stringify_keys(hash))
|
164
|
-
|
165
|
-
[
|
166
|
-
[:stdin, @qb_stdio_in],
|
167
|
-
[:stdout, @qb_stdio_out],
|
168
|
-
[:stderr, @qb_stdio_err],
|
169
|
-
].each do |name, socket|
|
170
|
-
if socket
|
171
|
-
debug "Flushing socket #{ name }."
|
172
|
-
socket.flush
|
173
|
-
debug "Closing #{ name } socket at #{ socket.path.to_s }."
|
174
|
-
socket.close
|
175
|
-
end
|
176
|
-
end
|
177
|
-
|
178
|
-
exit 0
|
179
|
-
end
|
180
|
-
|
181
|
-
def fail msg
|
182
|
-
exit_json failed: true, msg: msg, warnings: @warnings
|
183
|
-
end
|
184
|
-
end
|
4
|
+
AnsibleModule = QB::Ansible::Module
|
185
5
|
end # QB
|
data/lib/qb/cli/play.rb
CHANGED
@@ -1,3 +1,9 @@
|
|
1
|
+
# Requirements
|
2
|
+
# =====================================================================
|
3
|
+
|
4
|
+
# package
|
5
|
+
require 'qb/ansible/cmds/playbook'
|
6
|
+
|
1
7
|
|
2
8
|
module QB; end
|
3
9
|
|
@@ -18,13 +24,33 @@ module QB::CLI
|
|
18
24
|
raise "Need path to playbook in first arg."
|
19
25
|
end
|
20
26
|
|
21
|
-
|
27
|
+
playbook_path = QB::Util.resolve args[0]
|
22
28
|
|
23
|
-
unless
|
29
|
+
unless playbook_path.file?
|
24
30
|
raise "Can't find Ansible playbook at #{ path.to_s }"
|
25
31
|
end
|
26
32
|
|
33
|
+
# By default, we won't change directories to run the command.
|
34
|
+
chdir = nil
|
35
|
+
|
36
|
+
# See if there is an Ansible config in the parent directories
|
37
|
+
ansible_cfg_path = QB::Util.find_up \
|
38
|
+
QB::Ansible::ConfigFile::FILE_NAME,
|
39
|
+
playbook_path.dirname,
|
40
|
+
raise_on_not_found: false
|
41
|
+
|
42
|
+
# If we did find an Ansible config, we're going to want to run in that
|
43
|
+
# directory and add it to the role search path so that we merge it's
|
44
|
+
# values into our env vars (otherwise they would override the config
|
45
|
+
# values).
|
46
|
+
unless ansible_cfg_path.nil?
|
47
|
+
QB::Role::PATH.unshift ansible_cfg_path.dirname
|
48
|
+
chdir = ansible_cfg_path.dirname
|
49
|
+
end
|
27
50
|
|
51
|
+
cmd = QB::Ansible::Cmds::Playbook.new \
|
52
|
+
playbook_path: playbook_path,
|
53
|
+
chdir: chdir
|
28
54
|
|
29
55
|
end # .play
|
30
56
|
|
data/lib/qb/cli/run.rb
ADDED
File without changes
|
data/lib/qb/role.rb
CHANGED
@@ -204,7 +204,7 @@ module QB
|
|
204
204
|
def self.search_path
|
205
205
|
QB::Role::PATH.
|
206
206
|
map { |path|
|
207
|
-
if QB::Ansible::ConfigFile.
|
207
|
+
if QB::Ansible::ConfigFile.end_with_config_file?(path)
|
208
208
|
if File.file?(path)
|
209
209
|
QB::Ansible::ConfigFile.new(path).defaults.roles_path
|
210
210
|
end
|
data/lib/qb/util.rb
CHANGED
@@ -95,13 +95,25 @@ module QB
|
|
95
95
|
# @param [Pathname] from (Pathname.pwd)
|
96
96
|
# directory to start from.
|
97
97
|
#
|
98
|
+
# @param [Boolean] raise_on_not_found:
|
99
|
+
# When `true`, a {QB::FSStateError} will be raised if no file is found
|
100
|
+
# (default behavior).
|
101
|
+
#
|
102
|
+
# This is something of a legacy behavior - I think it would be better
|
103
|
+
# to have {find_up} return `nil` in that case and add a `find_up!`
|
104
|
+
# method that raises on not found. But I'm not going to do it right now.
|
105
|
+
#
|
98
106
|
# @return [Pathname]
|
99
107
|
# Pathname of found file.
|
100
108
|
#
|
101
|
-
# @
|
102
|
-
#
|
109
|
+
# @return [nil]
|
110
|
+
# If no file is found and the `raise_on_not_found` option is `false`.
|
111
|
+
#
|
112
|
+
# @raise [QB::FSStateError]
|
113
|
+
# If file is not found in `from` or any of it's parent directories
|
114
|
+
# and the `raise_on_not_found` option is `true` (default behavior).
|
103
115
|
#
|
104
|
-
def self.find_up filename, from = Pathname.pwd
|
116
|
+
def self.find_up filename, from = Pathname.pwd, raise_on_not_found: true
|
105
117
|
path = from + filename
|
106
118
|
|
107
119
|
return from if path.exist?
|
@@ -109,10 +121,14 @@ module QB
|
|
109
121
|
parent = from.parent
|
110
122
|
|
111
123
|
if from == parent
|
112
|
-
|
124
|
+
if raise_on_not_found
|
125
|
+
raise "not found in current or any parent directories: #{ filename }"
|
126
|
+
else
|
127
|
+
return nil
|
128
|
+
end
|
113
129
|
end
|
114
130
|
|
115
|
-
return find_up filename, parent
|
131
|
+
return find_up filename, parent, raise_on_not_found: raise_on_not_found
|
116
132
|
end # .find_up
|
117
133
|
end # Util
|
118
134
|
end # QB
|