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