sprinkle 0.4.2 → 0.5.0.rc1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +8 -0
- data/Gemfile +5 -0
- data/Gemfile.lock +54 -0
- data/README.markdown +178 -166
- data/Rakefile +4 -28
- data/bin/sprinkle +14 -1
- data/lib/sprinkle.rb +5 -1
- data/lib/sprinkle/actors/actors.rb +20 -5
- data/lib/sprinkle/actors/capistrano.rb +62 -36
- data/lib/sprinkle/actors/dummy.rb +127 -0
- data/lib/sprinkle/actors/local.rb +59 -17
- data/lib/sprinkle/actors/ssh.rb +189 -107
- data/lib/sprinkle/actors/vlad.rb +51 -32
- data/lib/sprinkle/configurable.rb +2 -1
- data/lib/sprinkle/deployment.rb +22 -2
- data/lib/sprinkle/errors/pretty_failure.rb +41 -0
- data/lib/sprinkle/errors/remote_command_failure.rb +24 -0
- data/lib/sprinkle/errors/transfer_failure.rb +28 -0
- data/lib/sprinkle/installers/apt.rb +17 -16
- data/lib/sprinkle/installers/binary.rb +23 -8
- data/lib/sprinkle/installers/brew.rb +17 -10
- data/lib/sprinkle/installers/bsd_port.rb +10 -6
- data/lib/sprinkle/installers/deb.rb +3 -10
- data/lib/sprinkle/installers/freebsd_pkg.rb +5 -11
- data/lib/sprinkle/installers/freebsd_portinstall.rb +8 -2
- data/lib/sprinkle/installers/gem.rb +9 -3
- data/lib/sprinkle/installers/group.rb +28 -4
- data/lib/sprinkle/installers/installer.rb +58 -7
- data/lib/sprinkle/installers/mac_port.rb +13 -6
- data/lib/sprinkle/installers/npm.rb +42 -0
- data/lib/sprinkle/installers/openbsd_pkg.rb +4 -11
- data/lib/sprinkle/installers/opensolaris_pkg.rb +7 -13
- data/lib/sprinkle/installers/package_installer.rb +33 -0
- data/lib/sprinkle/installers/pacman.rb +5 -13
- data/lib/sprinkle/installers/pear.rb +40 -0
- data/lib/sprinkle/installers/push_text.rb +18 -5
- data/lib/sprinkle/installers/rake.rb +7 -2
- data/lib/sprinkle/installers/reconnect.rb +29 -0
- data/lib/sprinkle/installers/replace_text.rb +11 -2
- data/lib/sprinkle/installers/rpm.rb +8 -6
- data/lib/sprinkle/installers/runner.rb +41 -16
- data/lib/sprinkle/installers/smart.rb +6 -17
- data/lib/sprinkle/installers/source.rb +22 -10
- data/lib/sprinkle/installers/thor.rb +7 -0
- data/lib/sprinkle/installers/transfer.rb +62 -41
- data/lib/sprinkle/installers/user.rb +34 -4
- data/lib/sprinkle/installers/yum.rb +10 -10
- data/lib/sprinkle/installers/zypper.rb +4 -15
- data/lib/sprinkle/package.rb +81 -98
- data/lib/sprinkle/policy.rb +11 -4
- data/lib/sprinkle/utility/log_recorder.rb +33 -0
- data/lib/sprinkle/verifiers/directory.rb +1 -1
- data/lib/sprinkle/verifiers/executable.rb +1 -1
- data/lib/sprinkle/verifiers/file.rb +11 -2
- data/lib/sprinkle/verifiers/package.rb +2 -14
- data/lib/sprinkle/verifiers/permission.rb +40 -0
- data/lib/sprinkle/verifiers/symlink.rb +2 -2
- data/lib/sprinkle/verifiers/test.rb +21 -0
- data/lib/sprinkle/verify.rb +3 -3
- data/lib/sprinkle/version.rb +3 -0
- data/spec/fixtures/my_file.txt +1 -0
- data/spec/sprinkle/actors/capistrano_spec.rb +16 -3
- data/spec/sprinkle/actors/local_spec.rb +24 -6
- data/spec/sprinkle/actors/ssh_spec.rb +38 -0
- data/spec/sprinkle/installers/apt_spec.rb +23 -2
- data/spec/sprinkle/installers/binary_spec.rb +22 -14
- data/spec/sprinkle/installers/brew_spec.rb +4 -4
- data/spec/sprinkle/installers/installer_spec.rb +36 -7
- data/spec/sprinkle/installers/npm_spec.rb +16 -0
- data/spec/sprinkle/installers/pear_spec.rb +16 -0
- data/spec/sprinkle/installers/push_text_spec.rb +23 -1
- data/spec/sprinkle/installers/rpm_spec.rb +5 -0
- data/spec/sprinkle/installers/runner_spec.rb +27 -11
- data/spec/sprinkle/installers/smart_spec.rb +60 -0
- data/spec/sprinkle/installers/source_spec.rb +4 -4
- data/spec/sprinkle/installers/transfer_spec.rb +31 -16
- data/spec/sprinkle/package_spec.rb +10 -2
- data/spec/sprinkle/policy_spec.rb +6 -0
- data/spec/sprinkle/verify_spec.rb +18 -4
- data/sprinkle.gemspec +22 -158
- metadata +178 -96
- data/TODO +0 -56
- data/VERSION +0 -1
- data/lib/sprinkle/verifiers/apt.rb +0 -21
- data/lib/sprinkle/verifiers/brew.rb +0 -21
- data/lib/sprinkle/verifiers/rpm.rb +0 -21
- data/lib/sprinkle/verifiers/users_groups.rb +0 -33
@@ -13,25 +13,14 @@ module Sprinkle
|
|
13
13
|
# zypper 'magic_beans'
|
14
14
|
# end
|
15
15
|
#
|
16
|
-
# You may also specify multiple packages as an array:
|
17
|
-
#
|
18
|
-
# package :magic_beans do
|
19
|
-
# zypper %w(magic_beans magic_sauce)
|
20
|
-
# end
|
21
|
-
#
|
22
|
-
# or an argument list:
|
16
|
+
# You may also specify multiple packages as an argument list or array:
|
23
17
|
#
|
24
18
|
# package :magic_beans do
|
25
19
|
# zypper "magic_beans", "magic_sauce"
|
26
20
|
# end
|
27
|
-
class Zypper <
|
28
|
-
|
29
|
-
|
30
|
-
def initialize(parent, *packages, &block) #:nodoc:
|
31
|
-
packages.flatten!
|
32
|
-
super parent, &block
|
33
|
-
@packages = packages
|
34
|
-
end
|
21
|
+
class Zypper < PackageInstaller
|
22
|
+
|
23
|
+
auto_api
|
35
24
|
|
36
25
|
protected
|
37
26
|
|
data/lib/sprinkle/package.rb
CHANGED
@@ -106,128 +106,84 @@ module Sprinkle
|
|
106
106
|
end
|
107
107
|
|
108
108
|
class Package #:nodoc:
|
109
|
-
include ArbitraryOptions
|
110
|
-
attr_accessor :name, :provides, :installers, :
|
109
|
+
# include ArbitraryOptions
|
110
|
+
attr_accessor :name, :provides, :installers, :verifications
|
111
|
+
attr_accessor :args, :opts
|
111
112
|
|
112
113
|
def initialize(name, metadata = {}, &block)
|
113
114
|
raise 'No package name supplied' unless name
|
114
115
|
|
115
116
|
@name = name
|
117
|
+
@metadata = metadata
|
116
118
|
@provides = metadata[:provides]
|
117
119
|
@dependencies = []
|
118
120
|
@recommends = []
|
119
121
|
@optional = []
|
120
122
|
@verifications = []
|
121
123
|
@installers = []
|
124
|
+
@block = block
|
125
|
+
# this should probably not be done twice
|
122
126
|
self.instance_eval &block
|
123
127
|
end
|
124
|
-
def add_user(username, options={}, &block)
|
125
|
-
@installers << Sprinkle::Installers::User.new(self, username, options, &block)
|
126
|
-
end
|
127
|
-
|
128
|
-
def add_group(group, options={}, &block)
|
129
|
-
@installers << Sprinkle::Installers::Group.new(self, group, options, &block)
|
130
|
-
end
|
131
|
-
|
132
|
-
def freebsd_pkg(*names, &block)
|
133
|
-
@installers << Sprinkle::Installers::FreebsdPkg.new(self, *names, &block)
|
134
|
-
end
|
135
128
|
|
136
|
-
def
|
137
|
-
@
|
138
|
-
end
|
139
|
-
|
140
|
-
def openbsd_pkg(*names, &block)
|
141
|
-
@installers << Sprinkle::Installers::OpenbsdPkg.new(self, *names, &block)
|
129
|
+
def description(s=nil)
|
130
|
+
s ? @description = s : @description
|
142
131
|
end
|
143
132
|
|
144
|
-
def
|
145
|
-
@
|
133
|
+
def version(s=nil)
|
134
|
+
s ? @version = s : @version
|
146
135
|
end
|
147
136
|
|
148
|
-
def
|
149
|
-
|
137
|
+
def instance(*args)
|
138
|
+
p=Package.new(name, @metadata) {}
|
139
|
+
p.opts = args.extract_options!
|
140
|
+
p.args = args
|
141
|
+
p.instance_variable_set("@block", @block)
|
142
|
+
p.instance_eval &@block
|
143
|
+
p
|
150
144
|
end
|
151
145
|
|
152
|
-
def
|
153
|
-
@
|
154
|
-
end
|
155
|
-
|
156
|
-
def apt(*names, &block)
|
157
|
-
@installers << Sprinkle::Installers::Apt.new(self, *names, &block)
|
158
|
-
end
|
159
|
-
|
160
|
-
def deb(*names, &block)
|
161
|
-
@installers << Sprinkle::Installers::Deb.new(self, *names, &block)
|
162
|
-
end
|
163
|
-
|
164
|
-
def rpm(*names, &block)
|
165
|
-
@installers << Sprinkle::Installers::Rpm.new(self, *names, &block)
|
166
|
-
end
|
167
|
-
|
168
|
-
def yum(*names, &block)
|
169
|
-
@installers << Sprinkle::Installers::Yum.new(self, *names, &block)
|
170
|
-
end
|
171
|
-
|
172
|
-
def zypper(*names, &block)
|
173
|
-
@installers << Sprinkle::Installers::Zypper.new(self, *names, &block)
|
146
|
+
def sudo?
|
147
|
+
@use_sudo
|
174
148
|
end
|
175
149
|
|
176
|
-
def
|
177
|
-
@
|
150
|
+
def use_sudo(flag=true)
|
151
|
+
@use_sudo = flag
|
178
152
|
end
|
179
|
-
|
180
|
-
def
|
181
|
-
@
|
182
|
-
@installers << Sprinkle::Installers::Gem.new(self, name, options, &block)
|
183
|
-
end
|
184
|
-
|
185
|
-
def source(source, options = {}, &block)
|
186
|
-
@recommends << :build_essential # Ubuntu/Debian
|
187
|
-
@installers << Sprinkle::Installers::Source.new(self, source, options, &block)
|
153
|
+
|
154
|
+
def args
|
155
|
+
@args || []
|
188
156
|
end
|
189
157
|
|
190
|
-
def
|
191
|
-
@
|
158
|
+
def opts
|
159
|
+
@opts || {}
|
192
160
|
end
|
193
161
|
|
194
|
-
|
195
|
-
@installers << Sprinkle::Installers::Rake.new(self, name, options, &block)
|
196
|
-
end
|
197
|
-
|
198
|
-
def thor(name, options = {}, &block)
|
199
|
-
@installers << Sprinkle::Installers::Thor.new(self, name, options, &block)
|
200
|
-
end
|
201
|
-
|
202
|
-
def noop(&block)
|
203
|
-
@installers << Sprinkle::Installers::Runner.new(self, "echo noop", &block)
|
162
|
+
class ContextError < StandardError #:nodoc:
|
204
163
|
end
|
205
164
|
|
206
|
-
def
|
207
|
-
|
208
|
-
end
|
209
|
-
|
210
|
-
def replace_text(regex, text, path, options={}, &block)
|
211
|
-
@installers << Sprinkle::Installers::ReplaceText.new(self, regex, text, path, options, &block)
|
165
|
+
def get(x)
|
166
|
+
raise ContextError, "Cannot call get inside a package, must be inside an Installer block"
|
212
167
|
end
|
213
168
|
|
214
|
-
def
|
215
|
-
|
169
|
+
def noop(&block)
|
170
|
+
install Sprinkle::Installers::Runner.new(self, "echo noop", &block)
|
216
171
|
end
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
172
|
+
|
173
|
+
# meta installer
|
174
|
+
# TODO - fix to be atomic
|
175
|
+
def push_file(file, options ={}, &block)
|
176
|
+
raise "need content" unless options[:content]
|
177
|
+
runner "#{"sudo " if sudo?}rm -f #{file}"
|
178
|
+
push_text options[:content], file, options, &block
|
179
|
+
end
|
180
|
+
|
222
181
|
def verify(description = '', &block)
|
223
182
|
@verifications << Sprinkle::Verify.new(self, description, &block)
|
224
183
|
end
|
225
|
-
|
226
|
-
def pacman(*names, &block)
|
227
|
-
@installers << Sprinkle::Installers::Pacman.new(self, *names, &block)
|
228
|
-
end
|
229
184
|
|
230
185
|
def process(deployment, roles)
|
186
|
+
logger.info " * #{name}"
|
231
187
|
return if meta_package?
|
232
188
|
|
233
189
|
# Run a pre-test to see if the software is already installed. If so,
|
@@ -236,7 +192,7 @@ module Sprinkle
|
|
236
192
|
begin
|
237
193
|
process_verifications(deployment, roles, true)
|
238
194
|
|
239
|
-
logger.info "-->
|
195
|
+
logger.info " --> already installed for roles: #{roles}"
|
240
196
|
return
|
241
197
|
rescue Sprinkle::VerificationFailed => e
|
242
198
|
# Continue
|
@@ -249,15 +205,16 @@ module Sprinkle
|
|
249
205
|
end
|
250
206
|
|
251
207
|
process_verifications(deployment, roles)
|
208
|
+
logger.info " --> INSTALLED for roles: #{roles}"
|
252
209
|
end
|
253
210
|
|
254
211
|
def process_verifications(deployment, roles, pre = false)
|
255
212
|
return if @verifications.blank?
|
256
213
|
|
257
214
|
if pre
|
258
|
-
logger.
|
215
|
+
logger.debug "--> Checking if #{self.name} is already installed for roles: #{roles}"
|
259
216
|
else
|
260
|
-
logger.
|
217
|
+
logger.debug "--> Verifying #{self.name} was properly installed for roles: #{roles}"
|
261
218
|
end
|
262
219
|
|
263
220
|
@verifications.each do |v|
|
@@ -265,46 +222,61 @@ module Sprinkle
|
|
265
222
|
v.process(roles)
|
266
223
|
end
|
267
224
|
end
|
268
|
-
|
225
|
+
|
226
|
+
def dependencies
|
227
|
+
@dependencies.map {|a,b| a }
|
228
|
+
end
|
229
|
+
|
269
230
|
def requires(*packages)
|
270
|
-
|
271
|
-
|
231
|
+
opts = packages.extract_options!
|
232
|
+
packages.each do |pack|
|
233
|
+
@dependencies << [pack, opts]
|
234
|
+
end
|
272
235
|
end
|
273
236
|
|
274
237
|
def recommends(*packages)
|
275
|
-
|
276
|
-
|
238
|
+
opts = packages.extract_options!
|
239
|
+
packages.each do |pack|
|
240
|
+
@recommends << [pack, opts]
|
241
|
+
end
|
242
|
+
@recommends.map {|a,b| a }
|
277
243
|
end
|
278
244
|
|
279
245
|
def optional(*packages)
|
280
|
-
|
281
|
-
|
246
|
+
opts = packages.extract_options!
|
247
|
+
packages.each do |pack|
|
248
|
+
@optional << [pack, opts]
|
249
|
+
end
|
250
|
+
@optional.map {|a,b| a }
|
282
251
|
end
|
283
252
|
|
284
253
|
def tree(depth = 1, &block)
|
285
254
|
packages = []
|
286
255
|
|
287
|
-
@recommends.each do |dep|
|
256
|
+
@recommends.each do |dep, config|
|
288
257
|
package = PACKAGES[dep]
|
289
258
|
next unless package # skip missing recommended packages as they're allowed to not exist
|
259
|
+
package=package.instance(config)
|
290
260
|
block.call(self, package, depth) if block
|
291
261
|
packages << package.tree(depth + 1, &block)
|
292
262
|
end
|
293
263
|
|
294
|
-
@dependencies.each do |dep|
|
264
|
+
@dependencies.each do |dep, config|
|
295
265
|
package = PACKAGES[dep]
|
296
266
|
package = select_package(dep, package) if package.is_a? Array
|
297
267
|
|
298
268
|
raise "Package definition not found for key: #{dep}" unless package
|
269
|
+
package = package.instance(config)
|
299
270
|
block.call(self, package, depth) if block
|
300
271
|
packages << package.tree(depth + 1, &block)
|
301
272
|
end
|
302
273
|
|
303
274
|
packages << self
|
304
275
|
|
305
|
-
@optional.each do |dep|
|
276
|
+
@optional.each do |dep, config|
|
306
277
|
package = PACKAGES[dep]
|
307
278
|
next unless package # skip missing optional packages as they're allow to not exist
|
279
|
+
package = package.instance(config)
|
308
280
|
block.call(self, package, depth) if block
|
309
281
|
packages << package.tree(depth + 1, &block)
|
310
282
|
end
|
@@ -313,8 +285,19 @@ module Sprinkle
|
|
313
285
|
end
|
314
286
|
|
315
287
|
def to_s; @name; end
|
288
|
+
|
289
|
+
protected
|
290
|
+
|
291
|
+
def install(i)
|
292
|
+
@installers << i
|
293
|
+
i
|
294
|
+
end
|
316
295
|
|
317
296
|
private
|
297
|
+
|
298
|
+
def cloud_info(message)
|
299
|
+
logger.info(message) if Sprinkle::OPTIONS[:cloud] or logger.debug?
|
300
|
+
end
|
318
301
|
|
319
302
|
def select_package(name, packages)
|
320
303
|
if packages.size <= 1
|
data/lib/sprinkle/policy.rb
CHANGED
@@ -51,7 +51,7 @@ module Sprinkle
|
|
51
51
|
end
|
52
52
|
|
53
53
|
class Policy #:nodoc:
|
54
|
-
attr_reader :name, :
|
54
|
+
attr_reader :name, :roles
|
55
55
|
|
56
56
|
def initialize(name, metadata = {}, &block)
|
57
57
|
raise 'No name provided' unless name
|
@@ -63,23 +63,30 @@ module Sprinkle
|
|
63
63
|
self.instance_eval(&block)
|
64
64
|
end
|
65
65
|
|
66
|
-
def requires(package, options = {})
|
67
|
-
|
66
|
+
# def requires(package, options = {})
|
67
|
+
def requires(package, *args)
|
68
|
+
@packages << [package, args]
|
68
69
|
end
|
70
|
+
|
71
|
+
def packages; @packages.map {|x| x.first }; end
|
69
72
|
|
70
73
|
def to_s; name; end
|
71
74
|
|
72
75
|
def process(deployment)
|
73
76
|
all = []
|
77
|
+
|
78
|
+
logger.info "[#{name}]"
|
74
79
|
|
75
80
|
cloud_info "--> Cloud hierarchy for policy #{@name}"
|
76
81
|
|
77
|
-
@packages.each do |p|
|
82
|
+
@packages.each do |p, args|
|
78
83
|
cloud_info "\nPolicy #{@name} requires package #{p}"
|
79
84
|
|
80
85
|
package = Sprinkle::Package::PACKAGES[p]
|
81
86
|
raise "Package definition not found for key: #{p}" unless package
|
82
87
|
package = select_package(p, package) if package.is_a? Array # handle virtual package selection
|
88
|
+
# get an instance of the package and pass our config options
|
89
|
+
package = package.instance(*args)
|
83
90
|
|
84
91
|
tree = package.tree do |parent, child, depth|
|
85
92
|
indent = "\t" * depth; cloud_info "#{indent}Package #{parent.name} requires #{child.name}"
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module Sprinkle
|
2
|
+
module Utility
|
3
|
+
class LogRecorder #:nodoc:
|
4
|
+
|
5
|
+
attr_accessor :err, :out, :command, :code
|
6
|
+
|
7
|
+
def initialize
|
8
|
+
reset
|
9
|
+
end
|
10
|
+
|
11
|
+
def log(stream, data)
|
12
|
+
case stream
|
13
|
+
when :err then @err << data
|
14
|
+
when :out then @out << data
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
# hash suitable to pass into a pretty failure details hash
|
19
|
+
def hash
|
20
|
+
{:error => err, :stdout => out, :command => command, :code => code}
|
21
|
+
end
|
22
|
+
|
23
|
+
def reset(cmd=nil)
|
24
|
+
@command=cmd
|
25
|
+
@code=nil
|
26
|
+
@err=""
|
27
|
+
@out=""
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
end
|
@@ -26,7 +26,7 @@ module Sprinkle
|
|
26
26
|
# Be smart: If the path includes a forward slash, we're checking
|
27
27
|
# an absolute path. Otherwise, we're checking a global executable
|
28
28
|
if path.include?('/')
|
29
|
-
|
29
|
+
test "-x #{path}"
|
30
30
|
else
|
31
31
|
@commands << "[ -n \"`echo \\`which #{path}\\``\" ]"
|
32
32
|
end
|
@@ -15,7 +15,15 @@ module Sprinkle
|
|
15
15
|
|
16
16
|
# Checks to make sure <tt>path</tt> is a file on the remote server.
|
17
17
|
def has_file(path)
|
18
|
-
|
18
|
+
test "-f #{path}"
|
19
|
+
end
|
20
|
+
|
21
|
+
def no_file(path)
|
22
|
+
test "! -f #{path}"
|
23
|
+
end
|
24
|
+
|
25
|
+
def md5_of_file(path, md5)
|
26
|
+
test "\"`md5sum #{path} | cut -f1 -d' '`\" = \"#{md5}\""
|
19
27
|
end
|
20
28
|
|
21
29
|
def file_contains(path, text)
|
@@ -26,7 +34,8 @@ module Sprinkle
|
|
26
34
|
end
|
27
35
|
def matches_local(localfile, remotefile, mode=nil)
|
28
36
|
raise "Couldn't find local file #{localfile}" unless ::File.exists?(localfile)
|
29
|
-
|
37
|
+
require 'digest/md5'
|
38
|
+
local = Digest::MD5.hexdigest(::File.read(localfile))
|
30
39
|
@commands << %{[ "X$(md5sum #{remotefile}|cut -d\\ -f 1)" = "X#{local}" ]}
|
31
40
|
end
|
32
41
|
end
|
@@ -4,20 +4,8 @@ module Sprinkle
|
|
4
4
|
Sprinkle::Verify.register(Sprinkle::Verifiers::Package)
|
5
5
|
|
6
6
|
def has_package(*packages)
|
7
|
-
|
8
|
-
|
9
|
-
else
|
10
|
-
packages = [packages] unless packages.is_a? Array
|
11
|
-
end
|
12
|
-
|
13
|
-
packages.each do |pak|
|
14
|
-
case Sprinkle::Installers::InstallPackage.installer
|
15
|
-
when :yum
|
16
|
-
@commands << "[ -n \"`yum list installed #{pak} 2> /dev/null | egrep -e \\\"#{pak}\\\"`\" ]"
|
17
|
-
else
|
18
|
-
raise "Unknown InstallPackage.installer"
|
19
|
-
end
|
20
|
-
end
|
7
|
+
puts "has_package and has_packages are depreciated"
|
8
|
+
raise "please use has_yum and friends instead"
|
21
9
|
end
|
22
10
|
|
23
11
|
alias_method :has_packages, :has_package
|