inprovise 0.2.7 → 0.2.8
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 +8 -8
- data/lib/inprovise/channel/ssh.rb +9 -5
- data/lib/inprovise/cmd_channel.rb +2 -0
- data/lib/inprovise/config.rb +94 -0
- data/lib/inprovise/control.rb +42 -50
- data/lib/inprovise/execution_context.rb +12 -14
- data/lib/inprovise/infra.rb +2 -2
- data/lib/inprovise/logger.rb +141 -17
- data/lib/inprovise/node.rb +2 -2
- data/lib/inprovise/script.rb +12 -42
- data/lib/inprovise/script_runner.rb +11 -4
- data/lib/inprovise/version.rb +1 -1
- data/lib/inprovise.rb +1 -0
- data/test/config_test.rb +124 -0
- data/test/script_runner_test.rb +4 -4
- metadata +5 -2
checksums.yaml
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
---
|
2
2
|
!binary "U0hBMQ==":
|
3
3
|
metadata.gz: !binary |-
|
4
|
-
|
4
|
+
ZWIzZmMxNTQzOTY1MjAxNTZiYjE2ZjQwNzZjZDg1ZDQwMDA3MWMxMg==
|
5
5
|
data.tar.gz: !binary |-
|
6
|
-
|
6
|
+
YzYzYjJlYzc2MmYxOTFkYTJjY2Q0ZmY3Njg5MWFlYzc0NDU0MGUzNw==
|
7
7
|
SHA512:
|
8
8
|
metadata.gz: !binary |-
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
MDljNzdlYTgwNTEyM2I4YmNkMzE0Y2NlYTJhMWRhMjJiYzUyNGRiMjQ4YmEx
|
10
|
+
NmViZmRmMTUzNGJiMWRkNDg4YmEzYjNjMDAyMzI1OWVhMmNiMGU1MjBiYmJl
|
11
|
+
ZjU1YTZkYzUzZTk0ZjdlNDMzNzExMGM1MTYwNWI4MjZiZTI1ZTA=
|
12
12
|
data.tar.gz: !binary |-
|
13
|
-
|
14
|
-
|
15
|
-
|
13
|
+
YTc2YjdjYzc3MGFkMGM3NDgwNGRjOWI2OGE0MTA4NjVkMDNjOTA0NDgyMDZi
|
14
|
+
ZWQ0NWMyMjcyMWYyNzliM2JlMzk0ZmFlZDhkMjMzNDQzMWFiMzk0MjI5YzVj
|
15
|
+
YmI4MGRiYWJmMDY2ZGJkNmE3ZTA1OGZjMWRiNjc2MWNhYWI2MWU=
|
@@ -191,11 +191,15 @@ Inprovise::CmdChannel.define('ssh') do
|
|
191
191
|
def execute(cmd, forcelog=false)
|
192
192
|
@node.log.remote("SSH: #{cmd}") if Inprovise.verbosity > 1 || forcelog
|
193
193
|
output = ''
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
@node.log.send(stream,
|
198
|
-
end
|
194
|
+
begin
|
195
|
+
connection.exec! cmd do |_channel, stream, data|
|
196
|
+
output << data if stream == :stdout
|
197
|
+
@node.log.send(stream, data, forcelog) if Inprovise.verbosity > 1 || forcelog
|
198
|
+
end
|
199
|
+
rescue Net::SSH::Exception => ex
|
200
|
+
raise Inprovise::CmdChannel::Exception, "#{ex.message}"
|
201
|
+
ensure
|
202
|
+
@node.log.flush_all if Inprovise.verbosity > 1 || forcelog
|
199
203
|
end
|
200
204
|
output
|
201
205
|
end
|
@@ -0,0 +1,94 @@
|
|
1
|
+
# Config class for Inprovise
|
2
|
+
#
|
3
|
+
# Author:: Martin Corino
|
4
|
+
# License:: Distributes under the same license as Ruby
|
5
|
+
|
6
|
+
class Inprovise::Config
|
7
|
+
|
8
|
+
def initialize(other=nil)
|
9
|
+
@table = {}
|
10
|
+
copy!(other.to_h) if other
|
11
|
+
end
|
12
|
+
|
13
|
+
def _k_(key)
|
14
|
+
Symbol === key ? key : key.to_s.to_sym
|
15
|
+
end
|
16
|
+
private :_k_
|
17
|
+
|
18
|
+
def _v_(val)
|
19
|
+
Hash === val ? self.class.new.merge!(val) : val
|
20
|
+
end
|
21
|
+
private :_v_
|
22
|
+
|
23
|
+
def [](key)
|
24
|
+
@table[_k_(key)]
|
25
|
+
end
|
26
|
+
|
27
|
+
def []=(key, val)
|
28
|
+
@table[_k_(key)] = _v_(val)
|
29
|
+
end
|
30
|
+
|
31
|
+
def has_key?(key)
|
32
|
+
@table.has_key?(_k_(key))
|
33
|
+
end
|
34
|
+
|
35
|
+
def empty?
|
36
|
+
@table.empty?
|
37
|
+
end
|
38
|
+
|
39
|
+
def merge!(other)
|
40
|
+
other.to_h.each do |k,v|
|
41
|
+
case self[k]
|
42
|
+
when self.class
|
43
|
+
self[k].merge!(v)
|
44
|
+
else
|
45
|
+
self[k] = v
|
46
|
+
end
|
47
|
+
end
|
48
|
+
self
|
49
|
+
end
|
50
|
+
|
51
|
+
def copy!(other)
|
52
|
+
other.to_h.each do |k,v|
|
53
|
+
case self[k]
|
54
|
+
when self.class
|
55
|
+
self[k].copy!(v)
|
56
|
+
else
|
57
|
+
self[k] = v.is_a?(Hash) ? v : (v.dup rescue v)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
self
|
61
|
+
end
|
62
|
+
|
63
|
+
def update!(other)
|
64
|
+
other.to_h.each do |k,v|
|
65
|
+
if self.has_key?(k)
|
66
|
+
self[k].update!(v) if self.class === self[k]
|
67
|
+
else
|
68
|
+
self[k] = v.is_a?(Hash) ? v : (v.dup rescue v)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
self
|
72
|
+
end
|
73
|
+
|
74
|
+
def each(&block)
|
75
|
+
@table.each { |k,v| block.call(k,v) }
|
76
|
+
end
|
77
|
+
|
78
|
+
def dup
|
79
|
+
self.class.new(@table)
|
80
|
+
end
|
81
|
+
|
82
|
+
def to_h
|
83
|
+
@table
|
84
|
+
end
|
85
|
+
|
86
|
+
def method_missing(method, *args)
|
87
|
+
if /(.*)=$/ =~ method.to_s
|
88
|
+
self[$1] = (args.size > 1 ? args : args.shift)
|
89
|
+
else
|
90
|
+
self[method]
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
end
|
data/lib/inprovise/control.rb
CHANGED
@@ -87,11 +87,13 @@ class Inprovise::Controller
|
|
87
87
|
begin
|
88
88
|
case command
|
89
89
|
when :add, :remove, :update
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
90
|
+
target = args.shift
|
91
|
+
if command == :remove
|
92
|
+
run_infra_command(command, target, *args)
|
93
|
+
else
|
94
|
+
tgtcfg = parse_config(options[:config])
|
95
|
+
tgtcfg[:credentials] = parse_config(options[:credential]) if target == :node && options.has_key?(:credential)
|
96
|
+
run_infra_command(command, target, options, tgtcfg, *args)
|
95
97
|
end
|
96
98
|
else # :apply, :revert, :validate or :trigger
|
97
99
|
load_schemes(options)
|
@@ -129,18 +131,13 @@ class Inprovise::Controller
|
|
129
131
|
end
|
130
132
|
end
|
131
133
|
|
132
|
-
def
|
133
|
-
add(Inprovise::Controller.new).send(:"#{cmd}
|
134
|
+
def run_infra_command(cmd, target, *args)
|
135
|
+
add(Inprovise::Controller.new).send(:"#{cmd}_#{target}", *args)
|
134
136
|
Inprovise::Infrastructure.save
|
135
137
|
end
|
136
138
|
|
137
|
-
def
|
138
|
-
add(Inprovise::Controller.new).
|
139
|
-
Inprovise::Infrastructure.save
|
140
|
-
end
|
141
|
-
|
142
|
-
def run_provisioning_command(command, script, opts, *targets)
|
143
|
-
add(Inprovise::Controller.new).run_provisioning_command(command, script, opts, *targets)
|
139
|
+
def run_provisioning_command(command, script, cfg, *targets)
|
140
|
+
add(Inprovise::Controller.new).run_provisioning_command(command, script, cfg, *targets)
|
144
141
|
end
|
145
142
|
|
146
143
|
end
|
@@ -163,11 +160,11 @@ class Inprovise::Controller
|
|
163
160
|
Inprovise.log.local('Done!') if Inprovise.verbosity > 0
|
164
161
|
end
|
165
162
|
|
166
|
-
def run_provisioning_command(command, cmdtgt,
|
163
|
+
def run_provisioning_command(command, cmdtgt, cmdcfg, *names)
|
167
164
|
# get intended infrastructure targets/config tuples
|
168
165
|
targets = get_targets(*names)
|
169
166
|
# create runner/config for each target/config
|
170
|
-
runners = targets.map do |tgt,
|
167
|
+
runners = targets.map do |tgt, tgtcfg|
|
171
168
|
@targets << tgt
|
172
169
|
[
|
173
170
|
if command == :trigger
|
@@ -175,21 +172,20 @@ class Inprovise::Controller
|
|
175
172
|
else
|
176
173
|
Inprovise::ScriptRunner.new(tgt, Inprovise::ScriptIndex.default.get(cmdtgt), Inprovise.skip_dependencies)
|
177
174
|
end,
|
178
|
-
|
175
|
+
tgtcfg
|
179
176
|
]
|
180
177
|
end
|
181
178
|
# execute runners
|
182
179
|
if Inprovise.sequential
|
183
|
-
runners.each {|runner,
|
180
|
+
runners.each {|runner, tgtcfg| exec(runner, command, tgtcfg.merge(cmdcfg)) }
|
184
181
|
else
|
185
|
-
@threads = runners.map {|runner,
|
182
|
+
@threads = runners.map {|runner, tgtcfg| Thread.new { exec(runner, command, tgtcfg.merge(cmdcfg)) } }
|
186
183
|
end
|
187
184
|
end
|
188
185
|
|
189
|
-
def add_node(options,
|
190
|
-
|
191
|
-
|
192
|
-
@targets << (node = Inprovise::Infrastructure::Node.new(names.first, opts))
|
186
|
+
def add_node(options, nodecfg, name)
|
187
|
+
nodecfg.merge!({ host: options[:address] })
|
188
|
+
@targets << (node = Inprovise::Infrastructure::Node.new(name, nodecfg))
|
193
189
|
|
194
190
|
Inprovise.log.local("Adding #{node}")
|
195
191
|
|
@@ -202,7 +198,7 @@ class Inprovise::Controller
|
|
202
198
|
end
|
203
199
|
end
|
204
200
|
|
205
|
-
def remove_node(
|
201
|
+
def remove_node(*names)
|
206
202
|
names.each do |name|
|
207
203
|
node = Inprovise::Infrastructure.find(name)
|
208
204
|
raise ArgumentError, "Invalid node #{name}" unless node && node.is_a?(Inprovise::Infrastructure::Node)
|
@@ -213,26 +209,23 @@ class Inprovise::Controller
|
|
213
209
|
end
|
214
210
|
end
|
215
211
|
|
216
|
-
def update_node(options, *names)
|
212
|
+
def update_node(options, nodecfg, *names)
|
217
213
|
@targets = names.collect do |name|
|
218
214
|
tgt = Inprovise::Infrastructure.find(name)
|
219
215
|
raise ArgumentError, "Unknown target [#{name}]" unless tgt
|
220
216
|
tgt.targets
|
221
217
|
end.flatten.uniq
|
222
|
-
opts = self.class.parse_config(options[:config])
|
223
|
-
opts[:credentials] = self.class.parse_config(options[:credential])
|
224
218
|
if Inprovise.sequential || (!options[:sniff]) || @targets.size == 1
|
225
|
-
@targets.each {|tgt|
|
219
|
+
@targets.each {|tgt| run_node_update(tgt, nodecfg.dup, options) }
|
226
220
|
else
|
227
|
-
threads = @targets.map {|tgt| Thread.new {
|
221
|
+
threads = @targets.map {|tgt| Thread.new { run_node_update(tgt, nodecfg.dup, options) } }
|
228
222
|
threads.each {|t| t.join }
|
229
223
|
end
|
230
224
|
end
|
231
225
|
|
232
|
-
def add_group(options,
|
226
|
+
def add_group(options, grpcfg, name)
|
233
227
|
options[:target].each {|t| raise ArgumentError, "Unknown target [#{t}]" unless Inprovise::Infrastructure.find(t) }
|
234
|
-
|
235
|
-
grp = Inprovise::Infrastructure::Group.new(names.first, opts, options[:target])
|
228
|
+
grp = Inprovise::Infrastructure::Group.new(name, grpcfg, options[:target])
|
236
229
|
|
237
230
|
Inprovise.log.local("Adding #{grp}")
|
238
231
|
|
@@ -243,7 +236,7 @@ class Inprovise::Controller
|
|
243
236
|
end
|
244
237
|
end
|
245
238
|
|
246
|
-
def remove_group(
|
239
|
+
def remove_group(*names)
|
247
240
|
names.each do |name|
|
248
241
|
grp = Inprovise::Infrastructure.find(name)
|
249
242
|
raise ArgumentError, "Invalid group #{name}" unless grp && grp.is_a?(Inprovise::Infrastructure::Group)
|
@@ -254,13 +247,12 @@ class Inprovise::Controller
|
|
254
247
|
end
|
255
248
|
end
|
256
249
|
|
257
|
-
def update_group(options, *names)
|
250
|
+
def update_group(options, grpcfg, *names)
|
258
251
|
groups = names.collect do |name|
|
259
252
|
tgt = Inprovise::Infrastructure.find(name)
|
260
253
|
raise ArgumentError, "Invalid group #{name}" unless tgt && tgt.is_a?(Inprovise::Infrastructure::Group)
|
261
254
|
tgt
|
262
255
|
end
|
263
|
-
opts = self.class.parse_config(options[:config])
|
264
256
|
grp_tgts = options[:target].collect do |tnm|
|
265
257
|
tgt = Inprovise::Infrastructure.find(tnm)
|
266
258
|
raise ArgumentError, "Unknown target #{tnm}" unless tgt
|
@@ -270,7 +262,7 @@ class Inprovise::Controller
|
|
270
262
|
Inprovise.log.local("Updating #{grp}")
|
271
263
|
|
272
264
|
grp.config.clear if options[:reset]
|
273
|
-
grp.config.merge!(
|
265
|
+
grp.config.merge!(grpcfg)
|
274
266
|
grp_tgts.each {|gt| gt.add_to(grp) }
|
275
267
|
end
|
276
268
|
end
|
@@ -292,37 +284,37 @@ class Inprovise::Controller
|
|
292
284
|
end
|
293
285
|
end
|
294
286
|
|
295
|
-
def exec(runner, command,
|
287
|
+
def exec(runner, command, cfg)
|
296
288
|
if Inprovise.demonstrate
|
297
|
-
runner.demonstrate(command,
|
289
|
+
runner.demonstrate(command, cfg)
|
298
290
|
else
|
299
|
-
runner.execute(command,
|
291
|
+
runner.execute(command, cfg)
|
300
292
|
end
|
301
293
|
end
|
302
294
|
|
303
|
-
def
|
304
|
-
Inprovise.log.local("Updating #{
|
295
|
+
def run_node_update(node, nodecfg, options)
|
296
|
+
Inprovise.log.local("Updating #{node}")
|
305
297
|
|
306
298
|
if options[:reset]
|
307
299
|
# preserve :host
|
308
|
-
|
300
|
+
nodecfg[:host] = node.get(:host) if node.get(:host)
|
309
301
|
# preserve :user if no new user specified
|
310
|
-
|
302
|
+
nodecfg[:user] = node.get(:user) if node.get(:user) && !nodecfg.has_key?(:user)
|
311
303
|
# preserve sniffed attributes when not running sniffers now
|
312
304
|
unless options[:sniff]
|
313
|
-
|
305
|
+
nodecfg[:attributes] = node.get(:attributes)
|
314
306
|
end
|
315
|
-
# clear the
|
316
|
-
|
307
|
+
# clear the node config
|
308
|
+
node.config.clear
|
317
309
|
end
|
318
|
-
|
310
|
+
node.config.merge!(nodecfg) # merge new + preserved config
|
319
311
|
# force update of user if specified
|
320
|
-
|
321
|
-
Inprovise::Sniffer.run_sniffers_for(
|
312
|
+
node.prepare_connection_for_user!(nodecfg[:user]) if nodecfg[:user]
|
313
|
+
Inprovise::Sniffer.run_sniffers_for(node) if options[:sniff]
|
322
314
|
options[:group].each do |g|
|
323
315
|
grp = Inprovise::Infrastructure.find(g)
|
324
316
|
raise ArgumentError, "Unknown group #{g}" unless grp
|
325
|
-
|
317
|
+
node.add_to(grp)
|
326
318
|
end
|
327
319
|
end
|
328
320
|
|
@@ -4,7 +4,6 @@
|
|
4
4
|
# License:: Distributes under the same license as Ruby
|
5
5
|
|
6
6
|
require 'open3'
|
7
|
-
require 'ostruct'
|
8
7
|
|
9
8
|
class Inprovise::ExecutionContext
|
10
9
|
|
@@ -49,7 +48,7 @@ class Inprovise::ExecutionContext
|
|
49
48
|
@context.env(var)
|
50
49
|
end
|
51
50
|
|
52
|
-
def log(msg=nil)
|
51
|
+
def log(msg=nil, color=nil)
|
53
52
|
@context.log(msg)
|
54
53
|
end
|
55
54
|
|
@@ -97,18 +96,11 @@ class Inprovise::ExecutionContext
|
|
97
96
|
@node = node
|
98
97
|
@log = log
|
99
98
|
@node.log_to(@log)
|
100
|
-
@config =
|
99
|
+
@config = Inprovise::Config.new(config || @node.config)
|
101
100
|
@index = index
|
102
101
|
@script = nil
|
103
102
|
end
|
104
103
|
|
105
|
-
def init_config(hash)
|
106
|
-
hash.to_h.reduce(OpenStruct.new(hash)) do |os,(k,v)|
|
107
|
-
os[k] = init_config(v) if Hash === v
|
108
|
-
os
|
109
|
-
end
|
110
|
-
end
|
111
|
-
|
112
104
|
def exec(blk, *args)
|
113
105
|
if args.empty?
|
114
106
|
DSL.new(self).instance_eval(&blk)
|
@@ -136,7 +128,13 @@ class Inprovise::ExecutionContext
|
|
136
128
|
return self if user.nil? || user == node.user
|
137
129
|
new_node = @node.for_user(user)
|
138
130
|
new_log = @log.clone_for_node(new_node)
|
139
|
-
self.
|
131
|
+
self.dup.setup_for_node!(new_node, new_log)
|
132
|
+
end
|
133
|
+
|
134
|
+
def setup_for_node!(node, log)
|
135
|
+
@node = node
|
136
|
+
@log = log
|
137
|
+
self
|
140
138
|
end
|
141
139
|
|
142
140
|
def run_local(cmd)
|
@@ -158,8 +156,8 @@ class Inprovise::ExecutionContext
|
|
158
156
|
@node.env(var)
|
159
157
|
end
|
160
158
|
|
161
|
-
def log(msg=nil)
|
162
|
-
@log.log(msg) if msg
|
159
|
+
def log(msg=nil, color=nil)
|
160
|
+
@log.log(msg, color) if msg
|
163
161
|
@log
|
164
162
|
end
|
165
163
|
|
@@ -216,7 +214,7 @@ class Inprovise::ExecutionContext
|
|
216
214
|
curtask = @node.log.set_task(action_ref)
|
217
215
|
curscript = @script
|
218
216
|
@script = pkg
|
219
|
-
@script.
|
217
|
+
@script.update_configuration(self)
|
220
218
|
begin
|
221
219
|
exec(action, *args)
|
222
220
|
ensure
|
data/lib/inprovise/infra.rb
CHANGED
@@ -12,13 +12,13 @@ module Inprovise::Infrastructure
|
|
12
12
|
JSON.create_id = 'json_class'
|
13
13
|
|
14
14
|
def self.symbolize_keys(hsh)
|
15
|
-
return hsh unless Hash === hsh
|
15
|
+
return hsh unless ::Hash === hsh
|
16
16
|
hsh.reduce({}) {|h, (k,v)| h[k.to_sym] = symbolize_keys(v); h }
|
17
17
|
end
|
18
18
|
|
19
19
|
class << self
|
20
20
|
def targets
|
21
|
-
@targets ||= Hash.new.extend(MonitorMixin)
|
21
|
+
@targets ||= ::Hash.new.extend(::MonitorMixin)
|
22
22
|
end
|
23
23
|
private :targets
|
24
24
|
|
data/lib/inprovise/logger.rb
CHANGED
@@ -3,13 +3,27 @@
|
|
3
3
|
# Author:: Martin Corino
|
4
4
|
# License:: Distributes under the same license as Ruby
|
5
5
|
|
6
|
+
require 'monitor'
|
7
|
+
|
6
8
|
class Inprovise::Logger
|
7
9
|
attr_accessor :node
|
8
10
|
attr_reader :task
|
9
11
|
|
12
|
+
class << self
|
13
|
+
def streams
|
14
|
+
@streams ||= ::Hash.new.extend(::MonitorMixin).merge!({
|
15
|
+
:stdout => { :ios => $stdout, :buffer => [{col: nil, ln: '', cr: false}] },
|
16
|
+
:stderr => { :ios => $stderr, :buffer => [{col: nil, ln: '', cr: false}] }
|
17
|
+
})
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
10
21
|
def initialize(node, task)
|
22
|
+
@streams = {
|
23
|
+
:stdout => { :ios => $stdout, :buffer => [{col: nil, ln: '', cr: false}] },
|
24
|
+
:stderr => { :ios => $stderr, :buffer => [{col: nil, ln: '', cr: false}] }
|
25
|
+
}
|
11
26
|
@node = node
|
12
|
-
@nl = true
|
13
27
|
set_task(task)
|
14
28
|
end
|
15
29
|
|
@@ -49,35 +63,145 @@ class Inprovise::Logger
|
|
49
63
|
say(cmd, :blue)
|
50
64
|
end
|
51
65
|
|
52
|
-
def log(msg)
|
53
|
-
say(msg)
|
66
|
+
def log(msg, color=nil)
|
67
|
+
say(msg, color)
|
68
|
+
end
|
69
|
+
|
70
|
+
def synchronize(&block)
|
71
|
+
self.class.streams.synchronize do
|
72
|
+
block.call
|
73
|
+
end if block_given?
|
74
|
+
end
|
75
|
+
private :synchronize
|
76
|
+
|
77
|
+
def ios(stream=:stdout)
|
78
|
+
self.class.streams[stream][:ios]
|
79
|
+
end
|
80
|
+
private :ios
|
81
|
+
|
82
|
+
def buffer(stream=:stdout)
|
83
|
+
self.class.streams[stream][:buffer]
|
84
|
+
end
|
85
|
+
private :buffer
|
86
|
+
|
87
|
+
def put(msg, color=nil, stream=:stdout)
|
88
|
+
streambuf = buffer(stream)
|
89
|
+
streambuf.last[:col] ||= color
|
90
|
+
streambuf.last[:ln] << msg
|
91
|
+
streambuf
|
92
|
+
end
|
93
|
+
private :put
|
94
|
+
|
95
|
+
def puts(msg, color=nil, stream=:stdout)
|
96
|
+
put(msg, color, stream) << {col:nil, ln:'',cr:false}
|
97
|
+
end
|
98
|
+
private :puts
|
99
|
+
|
100
|
+
def do_print(stream=:stdout)
|
101
|
+
streambuf = buffer(stream)
|
102
|
+
while lnbuf = streambuf.shift
|
103
|
+
clear_to_eol = lnbuf[:cr]
|
104
|
+
lnbuf[:ln].scan(/([^\r]*)(\r)?/) do |txt, cr|
|
105
|
+
# do we have a (full) line to print?
|
106
|
+
if cr || !streambuf.empty?
|
107
|
+
out = lnbuf[:col] ? txt.to_s.send(lnbuf[:col]) : txt
|
108
|
+
unless txt.empty?
|
109
|
+
ios(stream).print "\r".to_eol if clear_to_eol
|
110
|
+
ios(stream).print "#{@node.to_s} [#{@task.bold}] #{out}"
|
111
|
+
end
|
112
|
+
ios(stream).flush if cr
|
113
|
+
ios(stream).puts unless cr || (txt.empty? && !clear_to_eol)
|
114
|
+
clear_to_eol = cr ? true : false
|
115
|
+
break unless cr # next line or cr?
|
116
|
+
else
|
117
|
+
streambuf << if txt.empty?
|
118
|
+
# restart with empty line
|
119
|
+
{col:nil,ln:'',cr:clear_to_eol}
|
120
|
+
else
|
121
|
+
# stuff the remaining text back for a next round
|
122
|
+
{col:lnbuf[:col],ln:txt,cr:clear_to_eol}
|
123
|
+
end
|
124
|
+
return
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
private :do_print
|
130
|
+
|
131
|
+
def do_flush(stream)
|
132
|
+
lnbuf = buffer(stream).last
|
133
|
+
unless lnbuf[:ln].empty? && !lnbuf[:cr]
|
134
|
+
# add an empty line buffer to force output of current buffered contents
|
135
|
+
buffer(stream) << {col:nil, ln:'',cr:false}
|
136
|
+
do_print(stream)
|
137
|
+
end
|
138
|
+
end
|
139
|
+
private :do_flush
|
140
|
+
|
141
|
+
def flush(stream=:stdout)
|
142
|
+
synchronize do
|
143
|
+
do_flush(stream)
|
144
|
+
end
|
145
|
+
self
|
54
146
|
end
|
55
147
|
|
56
|
-
def
|
57
|
-
|
58
|
-
|
59
|
-
$stdout.print msg.sub("\r", "\r".to_eol << "#{@node.to_s} [#{@task.bold}] ")
|
60
|
-
@nl = false
|
148
|
+
def flush_all
|
149
|
+
synchronize do
|
150
|
+
[:stderr, :stdout].each { |stream| do_flush(stream) }
|
61
151
|
end
|
152
|
+
self
|
62
153
|
end
|
63
154
|
|
64
|
-
def
|
65
|
-
|
66
|
-
|
155
|
+
def print(msg, color=nil, stream=:stdout)
|
156
|
+
synchronize do
|
157
|
+
put(msg, color, stream)
|
158
|
+
do_print(stream)
|
159
|
+
end
|
160
|
+
self
|
161
|
+
end
|
162
|
+
|
163
|
+
def println(msg, color=nil, stream=:stdout)
|
164
|
+
synchronize do
|
165
|
+
puts(msg, color, stream)
|
166
|
+
do_print(stream)
|
167
|
+
end
|
168
|
+
self
|
169
|
+
end
|
170
|
+
|
171
|
+
def redirect(msg, color, stream)
|
172
|
+
synchronize do
|
173
|
+
msg.to_s.scan(/([^\n]*)(\n\r|\n)?/) do |txt,sep|
|
174
|
+
if sep
|
175
|
+
puts(txt, color, stream)
|
176
|
+
else
|
177
|
+
put(txt, color, stream)
|
178
|
+
end
|
179
|
+
break unless sep
|
180
|
+
end
|
181
|
+
do_print(stream)
|
182
|
+
end
|
183
|
+
self
|
67
184
|
end
|
185
|
+
private :redirect
|
68
186
|
|
69
187
|
def stdout(msg, force=false)
|
70
|
-
|
188
|
+
redirect(msg, :green, :stdout) if force || Inprovise.verbosity>0
|
71
189
|
end
|
72
190
|
|
73
191
|
def stderr(msg, force=false)
|
74
|
-
|
192
|
+
redirect(msg, :red, :stderr) if force || Inprovise.verbosity>0
|
75
193
|
end
|
76
194
|
|
77
|
-
def say(msg, color=nil, stream
|
78
|
-
|
79
|
-
|
80
|
-
|
195
|
+
def say(msg, color=nil, stream=:stdout)
|
196
|
+
synchronize do
|
197
|
+
[:stderr, :stdout].each { |stream| do_flush(stream) }
|
198
|
+
streambuf = buffer(stream)
|
199
|
+
msg.to_s.scan(/([^\n]*)(\n\r|\n)?/) do |txt,sep|
|
200
|
+
puts(txt)
|
201
|
+
break unless sep
|
202
|
+
end
|
203
|
+
do_print(stream)
|
81
204
|
end
|
205
|
+
self
|
82
206
|
end
|
83
207
|
end
|
data/lib/inprovise/node.rb
CHANGED
@@ -252,8 +252,8 @@ class Inprovise::Infrastructure::Node < Inprovise::Infrastructure::Target
|
|
252
252
|
output = exec.run(cmd, opts[:log])
|
253
253
|
@history << {cmd:cmd, output:output}
|
254
254
|
output
|
255
|
-
rescue Exception
|
256
|
-
raise RuntimeError, "Failed to communicate with [#{self}]"
|
255
|
+
rescue Inprovise::CmdChannel::Exception => ex
|
256
|
+
raise RuntimeError, "Failed to communicate with [#{self}] : #{ex.message}"
|
257
257
|
end
|
258
258
|
end
|
259
259
|
|
data/lib/inprovise/script.rb
CHANGED
@@ -3,10 +3,8 @@
|
|
3
3
|
# Author:: Martin Corino
|
4
4
|
# License:: Distributes under the same license as Ruby
|
5
5
|
|
6
|
-
require 'ostruct'
|
7
|
-
|
8
6
|
class Inprovise::Script
|
9
|
-
attr_reader :name, :dependencies, :actions, :children, :user
|
7
|
+
attr_reader :name, :dependencies, :actions, :children, :user, :configuration
|
10
8
|
|
11
9
|
class DSL
|
12
10
|
def initialize(script)
|
@@ -16,10 +14,12 @@ class Inprovise::Script
|
|
16
14
|
def description(desc)
|
17
15
|
@script.description(desc)
|
18
16
|
end
|
17
|
+
alias :describe :description
|
19
18
|
|
20
|
-
def
|
21
|
-
@script.
|
19
|
+
def configure(cfg=nil, &block)
|
20
|
+
@script.configure(cfg, &block)
|
22
21
|
end
|
22
|
+
alias :configuration :configure
|
23
23
|
|
24
24
|
def depends_on(*scr_names)
|
25
25
|
@script.depends_on(*scr_names)
|
@@ -73,48 +73,18 @@ class Inprovise::Script
|
|
73
73
|
self.description.split("\n").collect {|ld| "#{"%-25s" % nm.shift.to_s}\t#{ld.strip}"}
|
74
74
|
end
|
75
75
|
|
76
|
-
def
|
77
|
-
@configuration = cfg if cfg
|
76
|
+
def configure(cfg=nil, &definition)
|
77
|
+
@configuration = Inprovise::Config.new.merge!(cfg) if cfg
|
78
|
+
command(:configure, &definition)
|
78
79
|
@configuration
|
79
80
|
end
|
80
81
|
|
81
|
-
def
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
when Array
|
86
|
-
cfg.collect { |e| copy_config(e) }
|
87
|
-
else
|
88
|
-
cfg.dup rescue cfg
|
82
|
+
def update_configuration(context)
|
83
|
+
if @configuration
|
84
|
+
context.config[self.name.to_sym] ||= Inprovise::Config.new
|
85
|
+
context.config[self.name.to_sym].update!(@configuration)
|
89
86
|
end
|
90
87
|
end
|
91
|
-
private :copy_config
|
92
|
-
|
93
|
-
def merge_config(runcfg, scrcfg)
|
94
|
-
return scrcfg unless runcfg
|
95
|
-
case runcfg
|
96
|
-
when Hash, OpenStruct
|
97
|
-
return runcfg unless scrcfg.respond_to?(:to_h)
|
98
|
-
return scrcfg.to_h.reduce(runcfg) do |rc, (k,v)|
|
99
|
-
case rc[k]
|
100
|
-
when Hash,OpenStruct
|
101
|
-
rc[k] = merge_config(rc[k], v)
|
102
|
-
else
|
103
|
-
rc[k] = v unless rc[k]
|
104
|
-
end
|
105
|
-
rc
|
106
|
-
end
|
107
|
-
else
|
108
|
-
return runcfg
|
109
|
-
end
|
110
|
-
end
|
111
|
-
private :merge_config
|
112
|
-
|
113
|
-
def merge_configuration(config)
|
114
|
-
return unless self.configuration
|
115
|
-
script_cfg = copy_config(self.configuration)
|
116
|
-
config[self.name.to_sym] = merge_config(config[self.name.to_sym], script_cfg)
|
117
|
-
end
|
118
88
|
|
119
89
|
def depends_on(*scr_names)
|
120
90
|
scr_names.each do |scr_name|
|
@@ -33,11 +33,13 @@ class Inprovise::ScriptRunner
|
|
33
33
|
def execute(command_name, config=nil)
|
34
34
|
Inprovise.log.local("#{COMMANDS[command_name].first} #{script.name} #{COMMANDS[command_name].last} #{@node.to_s}")
|
35
35
|
scrs = scripts
|
36
|
-
scrs.reverse! if command_name.to_sym == :revert
|
37
|
-
@log.say scrs.map(&:name).join(', ').yellow if Inprovise.verbosity > 0
|
38
36
|
context = @perform ? Inprovise::ExecutionContext.new(@node, @log, @index, config) : Inprovise::MockExecutionContext.new(@node, @log, @index, config)
|
39
|
-
context.config.command = command_name
|
40
|
-
scrs.each
|
37
|
+
context.config.command = command_name.to_sym
|
38
|
+
scrs.each do |script|
|
39
|
+
execute_configuration(script, context)
|
40
|
+
end
|
41
|
+
scrs.reverse! if command_name.to_sym == :revert
|
42
|
+
@log.say(scrs.map(&:name).join(', '), :yellow) if Inprovise.verbosity > 0
|
41
43
|
scrs.each do |script|
|
42
44
|
send(:"execute_#{command_name}", script, context)
|
43
45
|
end
|
@@ -49,6 +51,11 @@ class Inprovise::ScriptRunner
|
|
49
51
|
@perform = true
|
50
52
|
end
|
51
53
|
|
54
|
+
def execute_configuration(script, context)
|
55
|
+
script.update_configuration(context)
|
56
|
+
exec(script, :configure, context)
|
57
|
+
end
|
58
|
+
|
52
59
|
def execute_apply(script, context)
|
53
60
|
return unless should_run?(script, :apply, context)
|
54
61
|
exec(script, :apply, context)
|
data/lib/inprovise/version.rb
CHANGED
data/lib/inprovise.rb
CHANGED
data/test/config_test.rb
ADDED
@@ -0,0 +1,124 @@
|
|
1
|
+
# Config tests for Inprovise
|
2
|
+
#
|
3
|
+
# Author:: Martin Corino
|
4
|
+
# License:: Distributes under the same license as Ruby
|
5
|
+
|
6
|
+
require_relative 'test_helper'
|
7
|
+
|
8
|
+
describe Inprovise::Config do
|
9
|
+
|
10
|
+
it 'creates an empty instance' do
|
11
|
+
@config = Inprovise::Config.new
|
12
|
+
@config.empty?.must_equal true
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'creates an instance from Hash' do
|
16
|
+
@config = Inprovise::Config.new({ :key => 'value'})
|
17
|
+
@config.empty?.must_equal false
|
18
|
+
@config.has_key?(:key).must_equal true
|
19
|
+
@config[:key].must_equal 'value'
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'creates an instance from Config' do
|
23
|
+
tmp = Inprovise::Config.new({ :key => 'value'})
|
24
|
+
@config = Inprovise::Config.new(tmp)
|
25
|
+
@config.empty?.must_equal false
|
26
|
+
@config.has_key?(:key).must_equal true
|
27
|
+
@config[:key].must_equal 'value'
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'copies initialization values' do
|
31
|
+
tmp = Inprovise::Config.new({ :key => %w{value1 value2}, :key2 => { :another => 999} })
|
32
|
+
@config = Inprovise::Config.new(tmp)
|
33
|
+
@config.empty?.must_equal false
|
34
|
+
@config.has_key?(:key).must_equal true
|
35
|
+
tmp[:key] << 'value3'
|
36
|
+
tmp[:key].size.must_equal 3
|
37
|
+
@config[:key].size.must_equal 2
|
38
|
+
tmp[:key].first << 'X'
|
39
|
+
# but not recursively (except for hashes/config)
|
40
|
+
tmp[:key].first.must_equal 'value1X'
|
41
|
+
@config[:key].first.must_equal 'value1X'
|
42
|
+
tmp[:key2][:another] = 100
|
43
|
+
tmp[:key2][:another].must_equal 100
|
44
|
+
@config[:key2][:another].must_equal 999
|
45
|
+
end
|
46
|
+
|
47
|
+
it 'merges configurations' do
|
48
|
+
tmp = Inprovise::Config.new({ :key => 'value1', :key2 => 'value2'})
|
49
|
+
@config = Inprovise::Config.new({ :key => 'value'})
|
50
|
+
@config.merge!(tmp)
|
51
|
+
@config.empty?.must_equal false
|
52
|
+
@config.has_key?(:key).must_equal true
|
53
|
+
@config[:key].must_equal 'value1'
|
54
|
+
@config[:key2].must_equal 'value2'
|
55
|
+
end
|
56
|
+
|
57
|
+
it 'copies configurations' do
|
58
|
+
tmp = Inprovise::Config.new({ :key => 'value1', :key2 => 'value2'})
|
59
|
+
@config = Inprovise::Config.new({ :key => 'value'})
|
60
|
+
@config.copy!(tmp)
|
61
|
+
@config.empty?.must_equal false
|
62
|
+
@config.has_key?(:key).must_equal true
|
63
|
+
tmp[:key].tr!('1', '')
|
64
|
+
tmp[:key].must_equal 'value'
|
65
|
+
@config[:key].must_equal 'value1'
|
66
|
+
@config[:key2].must_equal 'value2'
|
67
|
+
end
|
68
|
+
|
69
|
+
it 'updates configurations' do
|
70
|
+
tmp = Inprovise::Config.new({ :key => 'value1', :key2 => 'value2'})
|
71
|
+
@config = Inprovise::Config.new({ :key => 'value'})
|
72
|
+
@config.update!(tmp)
|
73
|
+
@config.empty?.must_equal false
|
74
|
+
@config.has_key?(:key).must_equal true
|
75
|
+
tmp[:key].must_equal 'value1'
|
76
|
+
@config[:key].must_equal 'value'
|
77
|
+
@config[:key2].must_equal 'value2'
|
78
|
+
end
|
79
|
+
|
80
|
+
it 'iterates content' do
|
81
|
+
@config = Inprovise::Config.new({ :key => 'value1', :key2 => 'value2'})
|
82
|
+
@config.each do |k,v|
|
83
|
+
[:key, :key2].must_include k
|
84
|
+
v.must_equal 'value1' if k == :key
|
85
|
+
v.must_equal 'value2' if k == :key2
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
it 'returns Hash' do
|
90
|
+
@config = Inprovise::Config.new({ :key => 'value1', :key2 => 'value2'})
|
91
|
+
@config.to_h.must_be_kind_of Hash
|
92
|
+
@config.to_h[:key].must_equal 'value1'
|
93
|
+
end
|
94
|
+
|
95
|
+
it 'duplicates Config' do
|
96
|
+
tmp = Inprovise::Config.new({ :key => 'value1', :key2 => 'value2'})
|
97
|
+
@config = tmp.dup
|
98
|
+
@config.empty?.must_equal false
|
99
|
+
@config.has_key?(:key).must_equal true
|
100
|
+
tmp[:key].tr!('1', '')
|
101
|
+
tmp[:key].must_equal 'value'
|
102
|
+
@config[:key].must_equal 'value1'
|
103
|
+
@config[:key2].must_equal 'value2'
|
104
|
+
end
|
105
|
+
|
106
|
+
it 'supports method_missing access to members' do
|
107
|
+
@config = Inprovise::Config.new({ :key => 'value1', :key2 => 'value2'})
|
108
|
+
@config.key.must_equal 'value1'
|
109
|
+
@config[:key2].must_equal 'value2'
|
110
|
+
@config.key2 = 'value3'
|
111
|
+
@config.key2.must_equal 'value3'
|
112
|
+
@config[:key2].must_equal 'value3'
|
113
|
+
end
|
114
|
+
|
115
|
+
it 'converts hashes to Config' do
|
116
|
+
@config = Inprovise::Config.new({ :key => 'value1', :key2 => 'value2'})
|
117
|
+
@config[:key3] = {}
|
118
|
+
@config[:key3].must_be_kind_of Inprovise::Config
|
119
|
+
@config.key4 = {}
|
120
|
+
@config.key4.must_be_kind_of Inprovise::Config
|
121
|
+
@config.key5 ||= {}
|
122
|
+
@config.key5.must_be_kind_of Inprovise::Config
|
123
|
+
end
|
124
|
+
end
|
data/test/script_runner_test.rb
CHANGED
@@ -100,8 +100,8 @@ describe Inprovise::ScriptRunner do
|
|
100
100
|
|
101
101
|
it 'validates before and after applying a script' do
|
102
102
|
@runner = Inprovise::ScriptRunner.new(@node, 'validate')
|
103
|
-
@runner.expects(:exec).
|
104
|
-
.with() { |script, cmd, context| script.name.must_equal('validate') && Inprovise::ExecutionContext === context && cmd == :apply }
|
103
|
+
@runner.expects(:exec).twice
|
104
|
+
.with() { |script, cmd, context| script.name.must_equal('validate') && Inprovise::ExecutionContext === context && (cmd == :configure || cmd == :apply) }
|
105
105
|
@runner.expects(:is_valid?).twice
|
106
106
|
.with() { |script, context| script.name.must_equal('validate') && Inprovise::ExecutionContext === context }
|
107
107
|
.returns(false, true)
|
@@ -135,8 +135,8 @@ describe Inprovise::ScriptRunner do
|
|
135
135
|
|
136
136
|
it 'validates before reverting a script' do
|
137
137
|
@runner = Inprovise::ScriptRunner.new(@node, 'validate')
|
138
|
-
@runner.expects(:exec).
|
139
|
-
.with() { |script, cmd, context| script.name.must_equal('validate') && Inprovise::ExecutionContext === context && cmd == :revert }
|
138
|
+
@runner.expects(:exec).twice
|
139
|
+
.with() { |script, cmd, context| script.name.must_equal('validate') && Inprovise::ExecutionContext === context && (cmd == :configure || cmd == :revert) }
|
140
140
|
@runner.expects(:is_valid?).once
|
141
141
|
.with() { |script, context| script.name.must_equal('validate') && Inprovise::ExecutionContext === context }
|
142
142
|
.returns(true)
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: inprovise
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.8
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Martin Corino
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-09-
|
11
|
+
date: 2016-09-11 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: colored
|
@@ -108,6 +108,7 @@ files:
|
|
108
108
|
- lib/inprovise/cli/provision.rb
|
109
109
|
- lib/inprovise/cmd_channel.rb
|
110
110
|
- lib/inprovise/cmd_helper.rb
|
111
|
+
- lib/inprovise/config.rb
|
111
112
|
- lib/inprovise/control.rb
|
112
113
|
- lib/inprovise/execution_context.rb
|
113
114
|
- lib/inprovise/group.rb
|
@@ -134,6 +135,7 @@ files:
|
|
134
135
|
- lib/inprovise/version.rb
|
135
136
|
- test/cli_test.rb
|
136
137
|
- test/cli_test_helper.rb
|
138
|
+
- test/config_test.rb
|
137
139
|
- test/dsl_test.rb
|
138
140
|
- test/fixtures/example.txt
|
139
141
|
- test/fixtures/include.rb
|
@@ -175,6 +177,7 @@ summary: Simple, easy and intuitive infrastructure provisioning
|
|
175
177
|
test_files:
|
176
178
|
- test/cli_test.rb
|
177
179
|
- test/cli_test_helper.rb
|
180
|
+
- test/config_test.rb
|
178
181
|
- test/dsl_test.rb
|
179
182
|
- test/fixtures/example.txt
|
180
183
|
- test/fixtures/include.rb
|