inprovise 0.2.2
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 +15 -0
- data/.gitignore +4 -0
- data/.travis.yml +28 -0
- data/Gemfile +9 -0
- data/LICENSE +8 -0
- data/README.md +197 -0
- data/Rakefile.rb +9 -0
- data/bin/rig +5 -0
- data/inprovise.gemspec +22 -0
- data/lib/inprovise/channel/ssh.rb +202 -0
- data/lib/inprovise/cli/group.rb +86 -0
- data/lib/inprovise/cli/node.rb +95 -0
- data/lib/inprovise/cli/provision.rb +84 -0
- data/lib/inprovise/cli.rb +105 -0
- data/lib/inprovise/cmd_channel.rb +100 -0
- data/lib/inprovise/cmd_helper.rb +150 -0
- data/lib/inprovise/control.rb +326 -0
- data/lib/inprovise/execution_context.rb +277 -0
- data/lib/inprovise/group.rb +67 -0
- data/lib/inprovise/helper/cygwin.rb +43 -0
- data/lib/inprovise/helper/linux.rb +181 -0
- data/lib/inprovise/helper/windows.rb +123 -0
- data/lib/inprovise/infra.rb +122 -0
- data/lib/inprovise/local_file.rb +120 -0
- data/lib/inprovise/logger.rb +79 -0
- data/lib/inprovise/node.rb +271 -0
- data/lib/inprovise/remote_file.rb +128 -0
- data/lib/inprovise/resolver.rb +36 -0
- data/lib/inprovise/script.rb +175 -0
- data/lib/inprovise/script_index.rb +46 -0
- data/lib/inprovise/script_runner.rb +110 -0
- data/lib/inprovise/sniff.rb +46 -0
- data/lib/inprovise/sniffer/linux.rb +64 -0
- data/lib/inprovise/sniffer/platform.rb +46 -0
- data/lib/inprovise/sniffer/unknown.rb +11 -0
- data/lib/inprovise/sniffer/windows.rb +32 -0
- data/lib/inprovise/template/inprovise.rb.erb +92 -0
- data/lib/inprovise/template.rb +38 -0
- data/lib/inprovise/trigger_runner.rb +36 -0
- data/lib/inprovise/version.rb +10 -0
- data/lib/inprovise.rb +145 -0
- data/test/cli_test.rb +314 -0
- data/test/cli_test_helper.rb +19 -0
- data/test/dsl_test.rb +43 -0
- data/test/fixtures/example.txt +1 -0
- data/test/fixtures/include.rb +4 -0
- data/test/fixtures/inprovise.rb +1 -0
- data/test/fixtures/myscheme.rb +1 -0
- data/test/infra_test.rb +189 -0
- data/test/local_file_test.rb +64 -0
- data/test/remote_file_test.rb +106 -0
- data/test/resolver_test.rb +66 -0
- data/test/script_index_test.rb +53 -0
- data/test/script_test.rb +56 -0
- data/test/test_helper.rb +237 -0
- metadata +182 -0
@@ -0,0 +1,326 @@
|
|
1
|
+
# Controller for Inprovise
|
2
|
+
#
|
3
|
+
# Author:: Martin Corino
|
4
|
+
# License:: Distributes under the same license as Ruby
|
5
|
+
|
6
|
+
require 'monitor'
|
7
|
+
|
8
|
+
class Inprovise::Controller
|
9
|
+
|
10
|
+
class << self
|
11
|
+
|
12
|
+
def controllers
|
13
|
+
@controllers ||= Array.new.extend(MonitorMixin)
|
14
|
+
end
|
15
|
+
private :controllers
|
16
|
+
|
17
|
+
def add(ctrl)
|
18
|
+
controllers.synchronize do
|
19
|
+
controllers << ctrl if ctrl
|
20
|
+
end
|
21
|
+
ctrl
|
22
|
+
end
|
23
|
+
private :add
|
24
|
+
|
25
|
+
def head
|
26
|
+
controllers.synchronize do
|
27
|
+
return controllers.first
|
28
|
+
end
|
29
|
+
end
|
30
|
+
private :head
|
31
|
+
|
32
|
+
def shift
|
33
|
+
controllers.synchronize do
|
34
|
+
return controllers.shift
|
35
|
+
end
|
36
|
+
end
|
37
|
+
private :shift
|
38
|
+
|
39
|
+
def empty?
|
40
|
+
controllers.synchronize do
|
41
|
+
return controllers.empty?
|
42
|
+
end
|
43
|
+
end
|
44
|
+
private :empty?
|
45
|
+
|
46
|
+
def get_value(v)
|
47
|
+
begin
|
48
|
+
Module.new { def self.eval(s); binding.eval(s); end }.eval(v)
|
49
|
+
rescue Exception
|
50
|
+
v
|
51
|
+
end
|
52
|
+
end
|
53
|
+
private :get_value
|
54
|
+
|
55
|
+
def list_scripts(options)
|
56
|
+
# load all specified schemes
|
57
|
+
(Array === options[:scheme] ? options[:scheme] : [options[:scheme]]).each {|s| Inprovise::DSL.include(s) }
|
58
|
+
$stdout.puts
|
59
|
+
$stdout.puts " PROVISIONING SCRIPTS"
|
60
|
+
$stdout.puts " ===================="
|
61
|
+
Inprovise::ScriptIndex.default.scripts.sort.each do |scrname|
|
62
|
+
script = Inprovise::ScriptIndex.default.get(scrname)
|
63
|
+
if script.description || options[:all]
|
64
|
+
script.describe.each {|l| $stdout.puts " #{l}" }
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def parse_config(cfg, opts = {})
|
70
|
+
cfg.inject(opts) do |rc,cfg|
|
71
|
+
k,v = cfg.split('=')
|
72
|
+
k = k.split('.')
|
73
|
+
h = rc
|
74
|
+
while k.size > 1
|
75
|
+
hk = k.shift.to_sym
|
76
|
+
raise ArgumentError, "Conflicting config category #{hk}" unless !h.has_key?(hk) || Hash === h[hk]
|
77
|
+
h = (h[hk] ||= {})
|
78
|
+
end
|
79
|
+
h.store(k.shift.to_sym, get_value(v))
|
80
|
+
rc
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
def run(command, options, *args)
|
85
|
+
begin
|
86
|
+
case command
|
87
|
+
when :add, :remove, :update
|
88
|
+
case args.shift
|
89
|
+
when :node
|
90
|
+
run_node_command(command, options, *args)
|
91
|
+
when :group
|
92
|
+
run_group_command(command, options, *args)
|
93
|
+
end
|
94
|
+
else # :apply, :revert, :validate or :trigger
|
95
|
+
# load all specified schemes
|
96
|
+
(Array === options[:scheme] ? options[:scheme] : [options[:scheme]]).each {|s| Inprovise::DSL.include(s) }
|
97
|
+
# extract config
|
98
|
+
cfg = parse_config(options[:config])
|
99
|
+
# get script/action
|
100
|
+
sca = args.shift
|
101
|
+
run_provisioning_command(command, sca, cfg, *args)
|
102
|
+
end
|
103
|
+
rescue Exception => e
|
104
|
+
cleanup!
|
105
|
+
raise e
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
def wait!
|
110
|
+
ex = nil
|
111
|
+
begin
|
112
|
+
while !empty?
|
113
|
+
head.wait
|
114
|
+
shift
|
115
|
+
end
|
116
|
+
rescue Exception => e
|
117
|
+
ex = e
|
118
|
+
ensure
|
119
|
+
cleanup!
|
120
|
+
raise ex if ex
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
def cleanup!
|
125
|
+
while !empty?
|
126
|
+
head.cleanup rescue Exception $stderr.puts $!.backtrace
|
127
|
+
shift
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
def run_node_command(cmd, options, *names)
|
132
|
+
add(Inprovise::Controller.new).run_node_command(cmd, options, *names)
|
133
|
+
end
|
134
|
+
|
135
|
+
def run_group_command(cmd, options, *names)
|
136
|
+
add(Inprovise::Controller.new).run_group_command(cmd, options, *names)
|
137
|
+
end
|
138
|
+
|
139
|
+
def run_provisioning_command(command, script, opts, *targets)
|
140
|
+
add(Inprovise::Controller.new).run_provisioning_command(command, script, opts, *targets)
|
141
|
+
end
|
142
|
+
|
143
|
+
end
|
144
|
+
|
145
|
+
def initialize
|
146
|
+
@targets = []
|
147
|
+
@threads = []
|
148
|
+
end
|
149
|
+
|
150
|
+
def wait
|
151
|
+
return if @threads.empty?
|
152
|
+
Inprovise.log.local('Waiting for controller threads...') if Inprovise.verbosity > 0
|
153
|
+
@threads.each { |t| t.join }
|
154
|
+
end
|
155
|
+
|
156
|
+
def cleanup
|
157
|
+
return if @targets.empty?
|
158
|
+
Inprovise.log.local('Disconnecting...') if Inprovise.verbosity > 0
|
159
|
+
@targets.each {|tgt| tgt.disconnect! }
|
160
|
+
Inprovise.log.local('Done!') if Inprovise.verbosity > 0
|
161
|
+
end
|
162
|
+
|
163
|
+
def run_provisioning_command(command, cmdtgt, opts, *names)
|
164
|
+
# get intended infrastructure targets/config tuples
|
165
|
+
targets = get_targets(*names)
|
166
|
+
# create runner/config for each target/config
|
167
|
+
runners = targets.map do |tgt, cfg|
|
168
|
+
@targets << tgt
|
169
|
+
[
|
170
|
+
if command == :trigger
|
171
|
+
Inprovise::TriggerRunner.new(tgt, cmdtgt)
|
172
|
+
else
|
173
|
+
Inprovise::ScriptRunner.new(tgt, Inprovise::ScriptIndex.default.get(cmdtgt), Inprovise.skip_dependencies)
|
174
|
+
end,
|
175
|
+
cfg
|
176
|
+
]
|
177
|
+
end
|
178
|
+
# execute runners
|
179
|
+
if Inprovise.sequential
|
180
|
+
runners.each {|runner, cfg| exec(runner, command, cfg.merge(opts)) }
|
181
|
+
else
|
182
|
+
@threads = runners.map {|runner, cfg| Thread.new { exec(runner, command, cfg.merge(opts)) } }
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
def run_node_command(command, options, *names)
|
187
|
+
case command
|
188
|
+
when :add
|
189
|
+
opts = self.class.parse_config(options[:config], { host: options[:address] })
|
190
|
+
opts[:credentials] = self.class.parse_config(options[:credential])
|
191
|
+
@targets << (node = Inprovise::Infrastructure::Node.new(names.first, opts))
|
192
|
+
|
193
|
+
Inprovise.log.local("Adding #{node.to_s}")
|
194
|
+
|
195
|
+
Inprovise::Sniffer.run_sniffers_for(node) if options[:sniff]
|
196
|
+
|
197
|
+
options[:group].each do |g|
|
198
|
+
grp = Inprovise::Infrastructure.find(g)
|
199
|
+
raise ArgumentError, "Unknown group #{g}" unless grp
|
200
|
+
node.add_to(grp)
|
201
|
+
end
|
202
|
+
when :remove
|
203
|
+
names.each do |name|
|
204
|
+
node = Inprovise::Infrastructure.find(name)
|
205
|
+
raise ArgumentError, "Invalid node #{name}" unless node && node.is_a?(Inprovise::Infrastructure::Node)
|
206
|
+
|
207
|
+
Inprovise.log.local("Removing #{node.to_s}")
|
208
|
+
|
209
|
+
Inprovise::Infrastructure.deregister(name)
|
210
|
+
end
|
211
|
+
when :update
|
212
|
+
@targets = names.collect do |name|
|
213
|
+
tgt = Inprovise::Infrastructure.find(name)
|
214
|
+
raise ArgumentError, "Unknown target [#{name}]" unless tgt
|
215
|
+
tgt.targets
|
216
|
+
end.flatten.uniq
|
217
|
+
opts = self.class.parse_config(options[:config])
|
218
|
+
opts[:credentials] = self.class.parse_config(options[:credential])
|
219
|
+
if Inprovise.sequential || (!options[:sniff]) || @targets.size == 1
|
220
|
+
@targets.each {|tgt| run_target_update(tgt, opts.dup, options) }
|
221
|
+
else
|
222
|
+
threads = @targets.map {|tgt| Thread.new { run_target_update(tgt, opts.dup, options) } }
|
223
|
+
threads.each {|t| t.join }
|
224
|
+
end
|
225
|
+
end
|
226
|
+
Inprovise::Infrastructure.save
|
227
|
+
end
|
228
|
+
|
229
|
+
def run_group_command(command, options, *names)
|
230
|
+
case command
|
231
|
+
when :add
|
232
|
+
options[:target].each {|t| raise ArgumentError, "Unknown target [#{t}]" unless Inprovise::Infrastructure.find(t) }
|
233
|
+
opts = self.class.parse_config(options[:config])
|
234
|
+
grp = Inprovise::Infrastructure::Group.new(names.first, opts, options[:target])
|
235
|
+
|
236
|
+
Inprovise.log.local("Adding #{grp.to_s}")
|
237
|
+
|
238
|
+
options[:target].each do |t|
|
239
|
+
tgt = Inprovise::Infrastructure.find(t)
|
240
|
+
raise ArgumentError, "Unknown target #{t}" unless tgt
|
241
|
+
tgt.add_to(grp)
|
242
|
+
end
|
243
|
+
when :remove
|
244
|
+
names.each do |name|
|
245
|
+
grp = Inprovise::Infrastructure.find(name)
|
246
|
+
raise ArgumentError, "Invalid group #{name}" unless grp && grp.is_a?(Inprovise::Infrastructure::Group)
|
247
|
+
|
248
|
+
Inprovise.log.local("Removing #{grp.to_s}")
|
249
|
+
|
250
|
+
Inprovise::Infrastructure.deregister(name)
|
251
|
+
end
|
252
|
+
when :update
|
253
|
+
groups = names.collect do |name|
|
254
|
+
grp = Inprovise::Infrastructure.find(name)
|
255
|
+
raise ArgumentError, "Invalid group #{name}" unless grp && grp.is_a?(Inprovise::Infrastructure::Group)
|
256
|
+
grp
|
257
|
+
end
|
258
|
+
opts = self.class.parse_config(options[:config])
|
259
|
+
grp_tgts = options[:target].collect do |t|
|
260
|
+
tgt = Inprovise::Infrastructure.find(t)
|
261
|
+
raise ArgumentError, "Unknown target #{t}" unless tgt
|
262
|
+
tgt
|
263
|
+
end
|
264
|
+
groups.each do |grp|
|
265
|
+
Inprovise.log.local("Updating #{grp.to_s}")
|
266
|
+
|
267
|
+
grp.config.clear if options[:reset]
|
268
|
+
grp.config.merge!(opts)
|
269
|
+
grp_tgts.each {|tgt| tgt.add_to(grp) }
|
270
|
+
end
|
271
|
+
end
|
272
|
+
Inprovise::Infrastructure.save
|
273
|
+
end
|
274
|
+
|
275
|
+
private
|
276
|
+
|
277
|
+
def get_targets(*names)
|
278
|
+
names.inject({}) do |hsh, name|
|
279
|
+
tgt = Inprovise::Infrastructure.find(name)
|
280
|
+
raise ArgumentError, "Unknown target [#{name}]" unless tgt
|
281
|
+
tgt.targets_with_config.each do |tgt_, cfg|
|
282
|
+
if hsh.has_key?(tgt_)
|
283
|
+
hsh[tgt_].merge!(cfg)
|
284
|
+
else
|
285
|
+
hsh[tgt_] = cfg
|
286
|
+
end
|
287
|
+
end
|
288
|
+
hsh
|
289
|
+
end
|
290
|
+
end
|
291
|
+
|
292
|
+
def exec(runner, command, opts)
|
293
|
+
if Inprovise.demonstrate
|
294
|
+
runner.demonstrate(command, opts)
|
295
|
+
else
|
296
|
+
runner.execute(command, opts)
|
297
|
+
end
|
298
|
+
end
|
299
|
+
|
300
|
+
def run_target_update(tgt, tgt_opts, options)
|
301
|
+
Inprovise.log.local("Updating #{tgt.to_s}")
|
302
|
+
|
303
|
+
if options[:reset]
|
304
|
+
# preserve :host
|
305
|
+
tgt_opts[:host] = tgt.get(:host) if tgt.get(:host)
|
306
|
+
# preserve :user if no new user specified
|
307
|
+
tgt_opts[:user] = tgt.get(:user) if tgt.get(:user) && !tgt_opts.has_key?(:user)
|
308
|
+
# preserve sniffed attributes when not running sniffers now
|
309
|
+
unless options[:sniff]
|
310
|
+
tgt_opts[:attributes] = tgt.get(:attributes)
|
311
|
+
end
|
312
|
+
# clear the target config
|
313
|
+
tgt.config.clear
|
314
|
+
end
|
315
|
+
tgt.config.merge!(tgt_opts) # merge new + preserved config
|
316
|
+
# force update of user if specified
|
317
|
+
tgt.prepare_connection_for_user!(tgt_opts[:user]) if tgt_opts[:user]
|
318
|
+
Inprovise::Sniffer.run_sniffers_for(tgt) if options[:sniff]
|
319
|
+
options[:group].each do |g|
|
320
|
+
grp = Inprovise::Infrastructure.find(g)
|
321
|
+
raise ArgumentError, "Unknown group #{g}" unless grp
|
322
|
+
tgt.add_to(grp)
|
323
|
+
end
|
324
|
+
end
|
325
|
+
|
326
|
+
end
|
@@ -0,0 +1,277 @@
|
|
1
|
+
# Execution context for Inprovise
|
2
|
+
#
|
3
|
+
# Author:: Martin Corino
|
4
|
+
# License:: Distributes under the same license as Ruby
|
5
|
+
|
6
|
+
require 'open3'
|
7
|
+
require 'ostruct'
|
8
|
+
|
9
|
+
class Inprovise::ExecutionContext
|
10
|
+
|
11
|
+
class DSL
|
12
|
+
def initialize(context)
|
13
|
+
@context = context
|
14
|
+
end
|
15
|
+
|
16
|
+
def method_missing(meth, *args)
|
17
|
+
@context.config.send(meth, *args)
|
18
|
+
end
|
19
|
+
|
20
|
+
def node
|
21
|
+
@context.node
|
22
|
+
end
|
23
|
+
|
24
|
+
def config
|
25
|
+
@context.config
|
26
|
+
end
|
27
|
+
|
28
|
+
def as(user, &blk)
|
29
|
+
@context.as(user, &blk)
|
30
|
+
end
|
31
|
+
|
32
|
+
def in_dir(path, &blk)
|
33
|
+
@context.in_dir(path, &blk)
|
34
|
+
end
|
35
|
+
|
36
|
+
def run_local(cmd)
|
37
|
+
@context.run_local(cmd)
|
38
|
+
end
|
39
|
+
|
40
|
+
def run(cmd, opts={})
|
41
|
+
@context.run(cmd, opts)
|
42
|
+
end
|
43
|
+
|
44
|
+
def sudo(cmd, opts={})
|
45
|
+
@context.sudo(cmd, opts)
|
46
|
+
end
|
47
|
+
|
48
|
+
def env(var)
|
49
|
+
@context.env(var)
|
50
|
+
end
|
51
|
+
|
52
|
+
def log(msg=nil)
|
53
|
+
@context.log(msg)
|
54
|
+
end
|
55
|
+
|
56
|
+
def upload(from, to)
|
57
|
+
@context.upload(from, to)
|
58
|
+
end
|
59
|
+
|
60
|
+
def download(from, to)
|
61
|
+
@context.download(from, to)
|
62
|
+
end
|
63
|
+
|
64
|
+
def mkdir(path)
|
65
|
+
@context.mkdir(path)
|
66
|
+
end
|
67
|
+
|
68
|
+
def remove(path)
|
69
|
+
@context.delete(path)
|
70
|
+
end
|
71
|
+
|
72
|
+
def local(path)
|
73
|
+
@context.local(path)
|
74
|
+
end
|
75
|
+
|
76
|
+
def remote(path)
|
77
|
+
@context.remote(path)
|
78
|
+
end
|
79
|
+
|
80
|
+
def template(path)
|
81
|
+
@context.template(path)
|
82
|
+
end
|
83
|
+
|
84
|
+
def trigger(action_ref, *args)
|
85
|
+
@context.trigger(action_ref, *args)
|
86
|
+
end
|
87
|
+
|
88
|
+
def binary_exists?(binary)
|
89
|
+
@context.binary_exists?(binary)
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
attr_reader :node, :config
|
94
|
+
attr_accessor :script
|
95
|
+
|
96
|
+
def initialize(node, log, index, config=nil)
|
97
|
+
@node = node
|
98
|
+
@log = log
|
99
|
+
@node.log_to(@log)
|
100
|
+
@config = init_config(config || @node.config)
|
101
|
+
@index = index
|
102
|
+
@script = nil
|
103
|
+
end
|
104
|
+
|
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
|
+
def exec(blk, *args)
|
113
|
+
if args.empty?
|
114
|
+
DSL.new(self).instance_eval(&blk)
|
115
|
+
else
|
116
|
+
DSL.new(self).instance_exec(*args, &blk)
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
def as(user, &blk)
|
121
|
+
for_user(user).exec(blk)
|
122
|
+
end
|
123
|
+
|
124
|
+
def in_dir(path, &blk)
|
125
|
+
rc = nil
|
126
|
+
old_cwd = @node.helper.set_cwd(path)
|
127
|
+
begin
|
128
|
+
rc = exec(blk)
|
129
|
+
ensure
|
130
|
+
@node.helper.set_cwd(old_cwd)
|
131
|
+
end
|
132
|
+
rc
|
133
|
+
end
|
134
|
+
|
135
|
+
def for_user(user)
|
136
|
+
return self if user.nil? || user == node.user
|
137
|
+
new_node = @node.for_user(user)
|
138
|
+
new_log = @log.clone_for_node(new_node)
|
139
|
+
self.class.new(new_node, new_log, @index, @config)
|
140
|
+
end
|
141
|
+
|
142
|
+
def run_local(cmd)
|
143
|
+
@log.local(cmd)
|
144
|
+
stdout, stderr, status = Open3.capture3(cmd)
|
145
|
+
@log.stdout(stdout)
|
146
|
+
@log.stderr(stderr)
|
147
|
+
end
|
148
|
+
|
149
|
+
def run(cmd, opts={})
|
150
|
+
@node.run(cmd, opts)
|
151
|
+
end
|
152
|
+
|
153
|
+
def sudo(cmd, opts={})
|
154
|
+
@node.sudo(cmd, opts)
|
155
|
+
end
|
156
|
+
|
157
|
+
def env(var)
|
158
|
+
@node.env(var)
|
159
|
+
end
|
160
|
+
|
161
|
+
def log(msg=nil)
|
162
|
+
@log.log(msg) if msg
|
163
|
+
@log
|
164
|
+
end
|
165
|
+
|
166
|
+
def upload(from, to)
|
167
|
+
@node.upload(from, to)
|
168
|
+
end
|
169
|
+
|
170
|
+
def download(from, to)
|
171
|
+
@node.download(from, to)
|
172
|
+
end
|
173
|
+
|
174
|
+
def mkdir(path)
|
175
|
+
@node.mkdir(path)
|
176
|
+
end
|
177
|
+
|
178
|
+
def remove(path)
|
179
|
+
@node.delete(path)
|
180
|
+
end
|
181
|
+
|
182
|
+
def copy(from, to)
|
183
|
+
@node.copy(from, to)
|
184
|
+
end
|
185
|
+
|
186
|
+
def local(path)
|
187
|
+
Inprovise::LocalFile.new(path)
|
188
|
+
end
|
189
|
+
|
190
|
+
def remote(path)
|
191
|
+
Inprovise::RemoteFile.new(self, path)
|
192
|
+
end
|
193
|
+
|
194
|
+
def set_permissions(path, mask)
|
195
|
+
@node.set_permissions(path, mask)
|
196
|
+
end
|
197
|
+
|
198
|
+
def set_owner(path, user, group=nil)
|
199
|
+
@node.set_owner(path, user, group)
|
200
|
+
end
|
201
|
+
|
202
|
+
def template(path)
|
203
|
+
Inprovise::Template.new(path, self)
|
204
|
+
end
|
205
|
+
|
206
|
+
def trigger(action_ref, *args)
|
207
|
+
action_name, pkg_name = *action_ref.split(':', 2).reverse
|
208
|
+
pkg = @script
|
209
|
+
pkg = @index.get(pkg_name) if pkg_name
|
210
|
+
action = pkg.actions[action_name] if pkg
|
211
|
+
raise Inprovise::MissingActionError.new(action_ref) unless action
|
212
|
+
curtask = @node.log.set_task(action_ref)
|
213
|
+
curscript = @script
|
214
|
+
@script = pkg
|
215
|
+
@script.merge_configuration(self.config)
|
216
|
+
begin
|
217
|
+
exec(action, *args)
|
218
|
+
ensure
|
219
|
+
@script = curscript
|
220
|
+
@node.log.set_task(curtask)
|
221
|
+
end
|
222
|
+
end
|
223
|
+
|
224
|
+
def binary_exists?(binary)
|
225
|
+
@node.binary_exists?(binary)
|
226
|
+
end
|
227
|
+
end
|
228
|
+
|
229
|
+
class Inprovise::MissingActionError < StandardError
|
230
|
+
def initialize(action_ref)
|
231
|
+
@action_ref = action_ref
|
232
|
+
end
|
233
|
+
|
234
|
+
def message
|
235
|
+
"Action '#{@action_ref}' could not be found."
|
236
|
+
end
|
237
|
+
end
|
238
|
+
|
239
|
+
class Inprovise::MockExecutionContext < Inprovise::ExecutionContext
|
240
|
+
def run(cmd)
|
241
|
+
@log.mock_execute(cmd)
|
242
|
+
''
|
243
|
+
end
|
244
|
+
|
245
|
+
def sudo(cmd)
|
246
|
+
@log.mock_execute "sudo #{cmd}"
|
247
|
+
''
|
248
|
+
end
|
249
|
+
|
250
|
+
def upload(from, to)
|
251
|
+
@log.mock_execute("UPLOAD: #{from} => #{to}")
|
252
|
+
end
|
253
|
+
|
254
|
+
def download(from, to)
|
255
|
+
@log.mock_execute("DOWLOAD: #{to} <= #{from}")
|
256
|
+
end
|
257
|
+
|
258
|
+
def mkdir(path)
|
259
|
+
@log.mock_execute("MKDIR: #{path}")
|
260
|
+
end
|
261
|
+
|
262
|
+
def remove(path)
|
263
|
+
@log.mock_execute("REMOVE: #{path}")
|
264
|
+
end
|
265
|
+
|
266
|
+
def copy(from, to)
|
267
|
+
@log.mock_execute("COPY: #{from} #{to}")
|
268
|
+
end
|
269
|
+
|
270
|
+
def set_permissions(path, mask)
|
271
|
+
@log.mock_execute("SET_PERMISSIONS: #{path} #{'%o' % mask}")
|
272
|
+
end
|
273
|
+
|
274
|
+
def set_owner(path, user, group=nil)
|
275
|
+
@log.mock_execute("SET_OWNER: #{path} #{user} #{group ? " #{group}" : ''}")
|
276
|
+
end
|
277
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
# Infrastructure group for Inprovise
|
2
|
+
#
|
3
|
+
# Author:: Martin Corino
|
4
|
+
# License:: Distributes under the same license as Ruby
|
5
|
+
|
6
|
+
require 'json'
|
7
|
+
|
8
|
+
class Inprovise::Infrastructure::Group < Inprovise::Infrastructure::Target
|
9
|
+
|
10
|
+
def initialize(name, config={}, targets=[])
|
11
|
+
@targets = targets.collect {|t| Inprovise::Infrastructure::Target === t ? t.name : t.to_s }
|
12
|
+
super(name, config)
|
13
|
+
end
|
14
|
+
|
15
|
+
def add_target(tgt)
|
16
|
+
tgt = Inprovise::Infrastructure::Target === tgt ? tgt : Inprovise::Infrastructure.find(tgt.to_s)
|
17
|
+
raise ArgumentError, "Circular reference detected in [#{tgt.to_s}] to [#{self.to_s}]" if tgt.includes?(self)
|
18
|
+
@targets << (Inprovise::Infrastructure::Target === tgt ? tgt.name : tgt.to_s)
|
19
|
+
end
|
20
|
+
|
21
|
+
def remove_target(tgt)
|
22
|
+
@targets.delete(Inprovise::Infrastructure::Target === tgt ? tgt.name : tgt.to_s)
|
23
|
+
end
|
24
|
+
|
25
|
+
def targets
|
26
|
+
@targets.collect {|t| Inprovise::Infrastructure.find(t).targets }.flatten.uniq
|
27
|
+
end
|
28
|
+
|
29
|
+
def targets_with_config
|
30
|
+
@targets.inject({}) do |hsh, t|
|
31
|
+
Inprovise::Infrastructure.find(t).targets_with_config.each do |tgt, cfg|
|
32
|
+
if hsh.has_key?(tgt)
|
33
|
+
hsh[tgt].merge!(cfg)
|
34
|
+
else
|
35
|
+
hsh[tgt] = cfg
|
36
|
+
end
|
37
|
+
hsh[tgt].merge!(config)
|
38
|
+
end
|
39
|
+
hsh
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def includes?(tgt)
|
44
|
+
tgtname = Inprovise::Infrastructure::Target === tgt ? tgt.name : tgt.to_s
|
45
|
+
@targets.include?(tgtname) || @targets.any? {|t| Inprovise::Infrastructure.find(t).includes?(tgtname) }
|
46
|
+
end
|
47
|
+
|
48
|
+
def to_s
|
49
|
+
"Group:#{name}"
|
50
|
+
end
|
51
|
+
|
52
|
+
def to_json(*a)
|
53
|
+
{
|
54
|
+
JSON.create_id => self.class.name,
|
55
|
+
:data => {
|
56
|
+
:name => name,
|
57
|
+
:config => config,
|
58
|
+
:targets => @targets
|
59
|
+
}
|
60
|
+
}.to_json(*a)
|
61
|
+
end
|
62
|
+
|
63
|
+
def self.json_create(o)
|
64
|
+
data = o[:data]
|
65
|
+
new(data[:name], data[:config], data[:targets])
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
# Windows Cygwin Command helper for Inprovise
|
2
|
+
#
|
3
|
+
# Author:: Martin Corino
|
4
|
+
# License:: Distributes under the same license as Ruby
|
5
|
+
|
6
|
+
Inprovise::CmdHelper.define('cygwin', Inprovise::CmdHelper.implementations['linux']) do
|
7
|
+
|
8
|
+
def initialize(channel)
|
9
|
+
super(channel)
|
10
|
+
# only if this is *NOT* a sudo helper
|
11
|
+
unless channel.node.user == admin_user
|
12
|
+
if channel.node.config.has_key?(:credentials) && channel.node.config[:credentials].has_key?(:'public-key')
|
13
|
+
# trigger sudo channel creation to have pubkey installed for admin as well
|
14
|
+
sudo
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
# platform properties
|
20
|
+
|
21
|
+
def admin_user
|
22
|
+
'administrator'
|
23
|
+
end
|
24
|
+
|
25
|
+
def env_reference(varname)
|
26
|
+
"\$#{varname}"
|
27
|
+
end
|
28
|
+
|
29
|
+
# generic command execution
|
30
|
+
|
31
|
+
def sudo
|
32
|
+
return self if channel.node.user == admin_user
|
33
|
+
unless @sudo
|
34
|
+
@sudo = channel.node.for_user(admin_user, "sudo:#{channel.node.user}").helper
|
35
|
+
end
|
36
|
+
@sudo.set_cwd(self.cwd)
|
37
|
+
@sudo.channel.node.log_to(channel.node.log.clone_for_node(@sudo.channel.node))
|
38
|
+
@sudo
|
39
|
+
end
|
40
|
+
|
41
|
+
# basic commands
|
42
|
+
|
43
|
+
end
|