qb 0.3.3 → 0.3.4
Sign up to get free protection for your applications and to get access to all the features.
- 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
|