inprovise 0.2.2
Sign up to get free protection for your applications and to get access to all the features.
- 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,271 @@
|
|
1
|
+
# Infrastructure node 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::Node < Inprovise::Infrastructure::Target
|
9
|
+
attr_reader :host, :user
|
10
|
+
|
11
|
+
def initialize(name, config={})
|
12
|
+
@host = config[:host] || name
|
13
|
+
@user = config[:user] || 'root'
|
14
|
+
@channel = nil
|
15
|
+
@helper = nil
|
16
|
+
@history = []
|
17
|
+
@user_nodes = {}
|
18
|
+
super(name, config)
|
19
|
+
end
|
20
|
+
|
21
|
+
def channel
|
22
|
+
@channel ||= Inprovise::CmdChannel.open(self, config[:channel])
|
23
|
+
end
|
24
|
+
|
25
|
+
def helper
|
26
|
+
@helper ||= Inprovise::CmdHelper.get(self, config[:helper])
|
27
|
+
end
|
28
|
+
|
29
|
+
def disconnect!
|
30
|
+
@user_nodes.each_value {|n| n.disconnect! }
|
31
|
+
@channel.close if @channel
|
32
|
+
self
|
33
|
+
end
|
34
|
+
|
35
|
+
# generic command execution
|
36
|
+
|
37
|
+
def run(cmd, opts={})
|
38
|
+
log.execute("RUN: #{cmd}") if Inprovise.verbosity > 0
|
39
|
+
if should_run?(cmd, opts)
|
40
|
+
really_run(cmd, opts)
|
41
|
+
else
|
42
|
+
cached_run(cmd, opts)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def sudo(cmd, opts={})
|
47
|
+
log.execute("SUDO: #{cmd}") if Inprovise.verbosity > 0
|
48
|
+
opts = opts.merge({:sudo => true})
|
49
|
+
if should_run?(cmd, opts)
|
50
|
+
really_run(cmd, opts)
|
51
|
+
else
|
52
|
+
cached_run(cmd, opts)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
# file management
|
57
|
+
|
58
|
+
def upload(from, to)
|
59
|
+
log.execute("UPLOAD: #{from} => #{to}") if Inprovise.verbosity > 0
|
60
|
+
helper.upload(from, to)
|
61
|
+
end
|
62
|
+
|
63
|
+
def download(from, to)
|
64
|
+
log.execute("DOWLOAD: #{to} <= #{from}") if Inprovise.verbosity > 0
|
65
|
+
helper.download(from, to)
|
66
|
+
end
|
67
|
+
|
68
|
+
# basic commands
|
69
|
+
|
70
|
+
def echo(arg)
|
71
|
+
log.execute("ECHO: #{arg}") if Inprovise.verbosity > 0
|
72
|
+
out = helper.echo(arg)
|
73
|
+
log.execute("ECHO: #{out}") if Inprovise.verbosity > 0
|
74
|
+
out
|
75
|
+
end
|
76
|
+
|
77
|
+
def env(var)
|
78
|
+
log.execute("ENV: #{var}") if Inprovise.verbosity > 0
|
79
|
+
val = helper.env(var)
|
80
|
+
log.execute("ENV: #{val}") if Inprovise.verbosity > 0
|
81
|
+
val
|
82
|
+
end
|
83
|
+
|
84
|
+
def cat(path)
|
85
|
+
log.execute("CAT: #{path}") if Inprovise.verbosity > 0
|
86
|
+
out = helper.cat(path)
|
87
|
+
log.execute("CAT: #{out}") if Inprovise.verbosity > 0
|
88
|
+
out
|
89
|
+
end
|
90
|
+
|
91
|
+
def hash_for(path)
|
92
|
+
log.execute("HASH_FOR: #{path}") if Inprovise.verbosity > 0
|
93
|
+
hsh = helper.hash_for(path)
|
94
|
+
log.execute("HASH_FOR: #{hsh}") if Inprovise.verbosity > 0
|
95
|
+
hsh
|
96
|
+
end
|
97
|
+
|
98
|
+
def mkdir(path)
|
99
|
+
log.execute("MKDIR: #{path}") if Inprovise.verbosity > 0
|
100
|
+
helper.mkdir(path)
|
101
|
+
end
|
102
|
+
|
103
|
+
def exists?(path)
|
104
|
+
log.execute("EXISTS?: #{path}") if Inprovise.verbosity > 0
|
105
|
+
rc = helper.exists?(path)
|
106
|
+
log.execute("EXISTS?: #{rc}") if Inprovise.verbosity > 0
|
107
|
+
rc
|
108
|
+
end
|
109
|
+
|
110
|
+
def file?(path)
|
111
|
+
log.execute("FILE?: #{path}") if Inprovise.verbosity > 0
|
112
|
+
rc = helper.file?(path)
|
113
|
+
log.execute("FILE?: #{rc}") if Inprovise.verbosity > 0
|
114
|
+
rc
|
115
|
+
end
|
116
|
+
|
117
|
+
def directory?(path)
|
118
|
+
log.execute("DIRECTORY?: #{path}") if Inprovise.verbosity > 0
|
119
|
+
rc = helper.directory?(path)
|
120
|
+
log.execute("DIRECTORY?: #{rc}") if Inprovise.verbosity > 0
|
121
|
+
rc
|
122
|
+
end
|
123
|
+
|
124
|
+
def copy(from, to)
|
125
|
+
log.execute("COPY: #{from} #{to}") if Inprovise.verbosity > 0
|
126
|
+
helper.copy(from, to)
|
127
|
+
end
|
128
|
+
|
129
|
+
def delete(path)
|
130
|
+
log.execute("DELETE: #{path}") if Inprovise.verbosity > 0
|
131
|
+
helper.delete(path)
|
132
|
+
end
|
133
|
+
|
134
|
+
def permissions(path)
|
135
|
+
log.execute("PERMISSIONS: #{path}") if Inprovise.verbosity > 0
|
136
|
+
perm = helper.permissions(path)
|
137
|
+
log.execute("PERMISSIONS: #{'%o' % perm}") if Inprovise.verbosity > 0
|
138
|
+
perm
|
139
|
+
end
|
140
|
+
|
141
|
+
def set_permissions(path, perm)
|
142
|
+
log.execute("SET_PERMISSIONS: #{path} #{'%o' % perm}") if Inprovise.verbosity > 0
|
143
|
+
helper.set_permissions(path, perm)
|
144
|
+
end
|
145
|
+
|
146
|
+
def owner(path)
|
147
|
+
log.execute("OWNER: #{path}") if Inprovise.verbosity > 0
|
148
|
+
owner = helper.owner(path)
|
149
|
+
log.execute("OWNER: #{owner}") if Inprovise.verbosity > 0
|
150
|
+
owner
|
151
|
+
end
|
152
|
+
|
153
|
+
def group(path)
|
154
|
+
log.execute("GROUP: #{path}") if Inprovise.verbosity > 0
|
155
|
+
group = helper.group(path)
|
156
|
+
log.execute("OWNER: #{group}") if Inprovise.verbosity > 0
|
157
|
+
group
|
158
|
+
end
|
159
|
+
|
160
|
+
def set_owner(path, user, group=nil)
|
161
|
+
log.execute("SET_OWNER: #{path} #{user}#{group ? " #{group}" : ''}") if Inprovise.verbosity > 0
|
162
|
+
helper.set_owner(path, user, group)
|
163
|
+
end
|
164
|
+
|
165
|
+
def binary_exists?(bin)
|
166
|
+
log.execute("BINARY_EXISTS?: #{bin}") if Inprovise.verbosity > 0
|
167
|
+
rc = helper.binary_exists?(bin)
|
168
|
+
log.execute("BINARY_EXISTS?: #{rc}") if Inprovise.verbosity > 0
|
169
|
+
rc
|
170
|
+
end
|
171
|
+
|
172
|
+
def log
|
173
|
+
@log ||= Inprovise::Logger.new(self, nil)
|
174
|
+
end
|
175
|
+
|
176
|
+
def log_to(log)
|
177
|
+
@log = log
|
178
|
+
end
|
179
|
+
|
180
|
+
def for_user(new_user, user_key=nil)
|
181
|
+
new_user = new_user.to_s
|
182
|
+
return self if self.user == new_user
|
183
|
+
user_key ||= new_user
|
184
|
+
return @user_nodes[user_key] if @user_nodes[user_key]
|
185
|
+
new_node = self.dup
|
186
|
+
new_node.prepare_connection_for_user!(new_user)
|
187
|
+
@user_nodes[user_key] = new_node
|
188
|
+
new_node
|
189
|
+
end
|
190
|
+
|
191
|
+
def for_dir(path)
|
192
|
+
user_key = "#{self.user}:#{path}"
|
193
|
+
return @user_nodes[user_key] if @user_nodes[user_key]
|
194
|
+
new_node = self.dup
|
195
|
+
new_node.prepare_connection_for_user!(self.user)
|
196
|
+
end
|
197
|
+
|
198
|
+
def prepare_connection_for_user!(new_user)
|
199
|
+
@user = new_user
|
200
|
+
@channel = nil
|
201
|
+
@helper = nil
|
202
|
+
@user_nodes = {}
|
203
|
+
@history = []
|
204
|
+
@log = Inprovise::Logger.new(self, @log.task) if @log
|
205
|
+
end
|
206
|
+
|
207
|
+
def to_s
|
208
|
+
"#{name}(#{user}@#{host})"
|
209
|
+
end
|
210
|
+
|
211
|
+
def safe_config
|
212
|
+
scfg = config.dup
|
213
|
+
scfg.delete :passphrase
|
214
|
+
scfg.delete :password
|
215
|
+
scfg.delete :credentials
|
216
|
+
scfg
|
217
|
+
end
|
218
|
+
protected :safe_config
|
219
|
+
|
220
|
+
def to_json(*a)
|
221
|
+
{
|
222
|
+
JSON.create_id => self.class.name,
|
223
|
+
:data => {
|
224
|
+
:name => name,
|
225
|
+
:config => safe_config
|
226
|
+
}
|
227
|
+
}.to_json(*a)
|
228
|
+
end
|
229
|
+
|
230
|
+
def self.json_create(o)
|
231
|
+
data = o[:data]
|
232
|
+
new(data[:name], data[:config])
|
233
|
+
end
|
234
|
+
|
235
|
+
private
|
236
|
+
|
237
|
+
def cached_run(cmd, opts={})
|
238
|
+
cmd = "sudo #{cmd}" if opts[:sudo]
|
239
|
+
log.cached(cmd)
|
240
|
+
last_output(cmd)
|
241
|
+
end
|
242
|
+
|
243
|
+
def really_run(cmd, opts={})
|
244
|
+
exec = opts[:sudo] ? helper.sudo : helper
|
245
|
+
cmd = prefixed_command(cmd)
|
246
|
+
begin
|
247
|
+
output = exec.run(cmd, opts[:log])
|
248
|
+
@history << {cmd:cmd, output:output}
|
249
|
+
output
|
250
|
+
rescue Exception
|
251
|
+
raise RuntimeError, "Failed to communicate with [#{self.to_s}]"
|
252
|
+
end
|
253
|
+
end
|
254
|
+
|
255
|
+
def should_run?(cmd, opts)
|
256
|
+
return true unless opts[:once]
|
257
|
+
cmd = "sudo #{cmd}" if opts[:sudo]
|
258
|
+
last_output(cmd).nil?
|
259
|
+
end
|
260
|
+
|
261
|
+
def last_output(cmd)
|
262
|
+
results = @history.select {|h| h[:cmd] == cmd }
|
263
|
+
return nil unless results && results.size > 0
|
264
|
+
results.last[:output]
|
265
|
+
end
|
266
|
+
|
267
|
+
def prefixed_command(cmd)
|
268
|
+
return cmd unless config[:prefix]
|
269
|
+
config[:prefix] + cmd
|
270
|
+
end
|
271
|
+
end
|
@@ -0,0 +1,128 @@
|
|
1
|
+
# RemoteFile support for Inprovise
|
2
|
+
#
|
3
|
+
# Author:: Martin Corino
|
4
|
+
# License:: Distributes under the same license as Ruby
|
5
|
+
|
6
|
+
require 'digest/sha1'
|
7
|
+
require 'fileutils'
|
8
|
+
|
9
|
+
class Inprovise::RemoteFile
|
10
|
+
attr_reader :path
|
11
|
+
|
12
|
+
def initialize(context, path)
|
13
|
+
@context = context
|
14
|
+
@path = path
|
15
|
+
@exists = nil
|
16
|
+
@permissions = nil
|
17
|
+
@owner = nil
|
18
|
+
end
|
19
|
+
|
20
|
+
def hash
|
21
|
+
return nil unless exists?
|
22
|
+
@hash ||= @context.node.hash_for(path)
|
23
|
+
end
|
24
|
+
|
25
|
+
def exists?
|
26
|
+
return @exists unless @exists.nil?
|
27
|
+
@exists = @context.node.exists?(path)
|
28
|
+
end
|
29
|
+
|
30
|
+
def directory?
|
31
|
+
@context.node.directory?(path)
|
32
|
+
end
|
33
|
+
|
34
|
+
def file?
|
35
|
+
@context.node.file?(path)
|
36
|
+
end
|
37
|
+
|
38
|
+
def content
|
39
|
+
@context.node.cat(path)
|
40
|
+
end
|
41
|
+
|
42
|
+
# doesnt check permissions or user. should it?
|
43
|
+
def matches?(other)
|
44
|
+
self.exists? && other.exists? && self.hash == other.hash
|
45
|
+
end
|
46
|
+
|
47
|
+
def copy_to(destination)
|
48
|
+
if destination.is_local?
|
49
|
+
download(destination)
|
50
|
+
else
|
51
|
+
duplicate(destination)
|
52
|
+
end
|
53
|
+
destination
|
54
|
+
end
|
55
|
+
|
56
|
+
def copy_from(destination)
|
57
|
+
destination.copy_to(self)
|
58
|
+
end
|
59
|
+
|
60
|
+
def duplicate(destination)
|
61
|
+
@context.copy(path, destination.path)
|
62
|
+
destination
|
63
|
+
end
|
64
|
+
|
65
|
+
def download(destination)
|
66
|
+
if String === destination || destination.is_local?
|
67
|
+
@context.download(path, String === destination ? destination : destination.path)
|
68
|
+
else
|
69
|
+
@context.copy(path, destination.path)
|
70
|
+
end
|
71
|
+
String === destination ? @context.local(destination) : destination
|
72
|
+
end
|
73
|
+
|
74
|
+
def upload(source)
|
75
|
+
if String === source || source.is_local?
|
76
|
+
@context.upload(String === source ? source : source.path, path)
|
77
|
+
else
|
78
|
+
@context.copy(source.path, path)
|
79
|
+
end
|
80
|
+
self
|
81
|
+
end
|
82
|
+
|
83
|
+
def delete!
|
84
|
+
@context.remove(path) if exists?
|
85
|
+
invalidate!
|
86
|
+
self
|
87
|
+
end
|
88
|
+
|
89
|
+
def set_permissions(mask)
|
90
|
+
@context.set_permissions(path, mask)
|
91
|
+
invalidate!
|
92
|
+
self
|
93
|
+
end
|
94
|
+
|
95
|
+
def permissions
|
96
|
+
@permissions ||= @context.node.permissions(path)
|
97
|
+
end
|
98
|
+
|
99
|
+
def set_owner(user, group=nil)
|
100
|
+
user ||= owner[:user]
|
101
|
+
@context.set_owner(path, user, group)
|
102
|
+
invalidate!
|
103
|
+
self
|
104
|
+
end
|
105
|
+
|
106
|
+
def owner
|
107
|
+
@owner ||= @context.node.owner(path)
|
108
|
+
end
|
109
|
+
|
110
|
+
def user
|
111
|
+
owner[:user]
|
112
|
+
end
|
113
|
+
|
114
|
+
def group
|
115
|
+
owner[:group]
|
116
|
+
end
|
117
|
+
|
118
|
+
def is_local?
|
119
|
+
false
|
120
|
+
end
|
121
|
+
|
122
|
+
private
|
123
|
+
|
124
|
+
def invalidate!
|
125
|
+
@permissions = nil
|
126
|
+
@owner = nil
|
127
|
+
end
|
128
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# Script dependency Resolver for Inprovise
|
2
|
+
#
|
3
|
+
# Author:: Martin Corino
|
4
|
+
# License:: Distributes under the same license as Ruby
|
5
|
+
|
6
|
+
class Inprovise::Resolver
|
7
|
+
attr_reader :scripts
|
8
|
+
def initialize(script,index=nil)
|
9
|
+
@script = script
|
10
|
+
@index = index || Inprovise::ScriptIndex.default
|
11
|
+
@last_seen = script
|
12
|
+
@scripts = [@script]
|
13
|
+
end
|
14
|
+
|
15
|
+
def resolve
|
16
|
+
begin
|
17
|
+
@script.dependencies.reverse.each do |d|
|
18
|
+
@scripts.insert(0, *Inprovise::Resolver.new(@index.get(d), @index).resolve.scripts)
|
19
|
+
end
|
20
|
+
@script.children.each do |c|
|
21
|
+
child = @index.get(c)
|
22
|
+
@scripts.concat(Inprovise::Resolver.new(child, @index).resolve.scripts) unless @scripts.include?(child)
|
23
|
+
end
|
24
|
+
rescue SystemStackError
|
25
|
+
raise CircularDependencyError.new
|
26
|
+
end
|
27
|
+
@scripts.uniq!
|
28
|
+
self
|
29
|
+
end
|
30
|
+
|
31
|
+
class CircularDependencyError < StandardError
|
32
|
+
def initialize
|
33
|
+
super('Circular dependecy detected')
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,175 @@
|
|
1
|
+
# Script base class for Inprovise
|
2
|
+
#
|
3
|
+
# Author:: Martin Corino
|
4
|
+
# License:: Distributes under the same license as Ruby
|
5
|
+
|
6
|
+
require 'ostruct'
|
7
|
+
|
8
|
+
class Inprovise::Script
|
9
|
+
attr_reader :name, :dependencies, :actions, :children, :user
|
10
|
+
|
11
|
+
class DSL
|
12
|
+
def initialize(script)
|
13
|
+
@script = script
|
14
|
+
end
|
15
|
+
|
16
|
+
def description(desc)
|
17
|
+
@script.description(desc)
|
18
|
+
end
|
19
|
+
|
20
|
+
def configuration(cfg)
|
21
|
+
@script.configuration(cfg)
|
22
|
+
end
|
23
|
+
|
24
|
+
def depends_on(*scr_names)
|
25
|
+
@script.depends_on(*scr_names)
|
26
|
+
end
|
27
|
+
|
28
|
+
def triggers(*scr_names)
|
29
|
+
@script.triggers(*scr_names)
|
30
|
+
end
|
31
|
+
|
32
|
+
def validate(&definition)
|
33
|
+
@script.validate(&definition)
|
34
|
+
end
|
35
|
+
|
36
|
+
def apply(&definition)
|
37
|
+
@script.apply(&definition)
|
38
|
+
end
|
39
|
+
|
40
|
+
def revert(&definition)
|
41
|
+
@script.revert(&definition)
|
42
|
+
end
|
43
|
+
|
44
|
+
def as(user)
|
45
|
+
@script.as(user)
|
46
|
+
end
|
47
|
+
|
48
|
+
def action(name, &definition)
|
49
|
+
@script.action(name, &definition)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def initialize(name)
|
54
|
+
@name = name
|
55
|
+
@description = nil
|
56
|
+
@configuration = nil
|
57
|
+
@user = nil
|
58
|
+
@dependencies = []
|
59
|
+
@children = []
|
60
|
+
@actions = {}
|
61
|
+
@commands = {}
|
62
|
+
@remove = nil
|
63
|
+
end
|
64
|
+
|
65
|
+
def description(desc=nil)
|
66
|
+
@description = desc if desc
|
67
|
+
@description
|
68
|
+
end
|
69
|
+
|
70
|
+
def describe
|
71
|
+
return [self.name] unless self.description
|
72
|
+
nm = [self.name]
|
73
|
+
self.description.split("\n").collect {|ld| "#{"%-25s" % nm.shift.to_s}\t#{ld.strip}"}
|
74
|
+
end
|
75
|
+
|
76
|
+
def configuration(cfg=nil)
|
77
|
+
@configuration = cfg if cfg
|
78
|
+
@configuration
|
79
|
+
end
|
80
|
+
|
81
|
+
def copy_config(cfg)
|
82
|
+
case cfg
|
83
|
+
when Hash, OpenStruct
|
84
|
+
cfg.to_h.reduce(OpenStruct.new) { |os, (k,v)| os[k] = copy_config(v); os }
|
85
|
+
when Array
|
86
|
+
cfg.collect { |e| copy_config(e) }
|
87
|
+
else
|
88
|
+
cfg.dup rescue cfg
|
89
|
+
end
|
90
|
+
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
|
+
|
119
|
+
def depends_on(*scr_names)
|
120
|
+
scr_names.each do |scr_name|
|
121
|
+
@dependencies << scr_name
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
def triggers(*scr_names)
|
126
|
+
scr_names.each do |scr_name|
|
127
|
+
@children << scr_name
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
def validate(&definition)
|
132
|
+
command(:validate, &definition)
|
133
|
+
end
|
134
|
+
|
135
|
+
def apply(&definition)
|
136
|
+
command(:apply, &definition)
|
137
|
+
end
|
138
|
+
|
139
|
+
def revert(&definition)
|
140
|
+
command(:revert, &definition)
|
141
|
+
end
|
142
|
+
|
143
|
+
def as(user)
|
144
|
+
@user = user
|
145
|
+
end
|
146
|
+
|
147
|
+
def action(name, &definition)
|
148
|
+
@actions[name] = definition
|
149
|
+
end
|
150
|
+
|
151
|
+
def command(name, &definition)
|
152
|
+
if block_given?
|
153
|
+
(@commands[name.to_sym] ||= []) << definition
|
154
|
+
else
|
155
|
+
@commands[name.to_sym] ||= []
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
def provides_command?(name)
|
160
|
+
@commands.has_key?(name.to_sym)
|
161
|
+
end
|
162
|
+
|
163
|
+
def to_s
|
164
|
+
self.name
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
Inprovise::DSL.dsl_define do
|
169
|
+
def script(name, &definition)
|
170
|
+
Inprovise.log.local("Adding provisioning script #{name}") if Inprovise.verbosity > 1
|
171
|
+
Inprovise.add_script(Inprovise::Script.new(name)) do |script|
|
172
|
+
Inprovise::Script::DSL.new(script).instance_eval(&definition)
|
173
|
+
end
|
174
|
+
end
|
175
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
# Script Index for Inprovise
|
2
|
+
#
|
3
|
+
# Author:: Martin Corino
|
4
|
+
# License:: Distributes under the same license as Ruby
|
5
|
+
|
6
|
+
class Inprovise::ScriptIndex
|
7
|
+
attr_reader :index_name
|
8
|
+
|
9
|
+
def initialize(index_name)
|
10
|
+
@index_name = index_name
|
11
|
+
@scripts = {}
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.default
|
15
|
+
@default ||= new('default')
|
16
|
+
end
|
17
|
+
|
18
|
+
def add(scr)
|
19
|
+
@scripts[scr.name] = scr
|
20
|
+
end
|
21
|
+
|
22
|
+
def get(scr_name)
|
23
|
+
scr = @scripts[scr_name]
|
24
|
+
raise MissingScriptError.new(index_name, scr_name) if scr.nil?
|
25
|
+
scr
|
26
|
+
end
|
27
|
+
|
28
|
+
def scripts
|
29
|
+
@scripts.keys
|
30
|
+
end
|
31
|
+
|
32
|
+
def clear!
|
33
|
+
@scripts = {}
|
34
|
+
end
|
35
|
+
|
36
|
+
class MissingScriptError < StandardError
|
37
|
+
def initialize(index_name, script_name)
|
38
|
+
@index_name = index_name
|
39
|
+
@script_name = script_name
|
40
|
+
end
|
41
|
+
|
42
|
+
def message
|
43
|
+
"script #{@script_name} could not be found in the index #{@index_name}"
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|