sprinkle 0.4.2 → 0.5.0.rc1

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.
Files changed (87) hide show
  1. data/.gitignore +8 -0
  2. data/Gemfile +5 -0
  3. data/Gemfile.lock +54 -0
  4. data/README.markdown +178 -166
  5. data/Rakefile +4 -28
  6. data/bin/sprinkle +14 -1
  7. data/lib/sprinkle.rb +5 -1
  8. data/lib/sprinkle/actors/actors.rb +20 -5
  9. data/lib/sprinkle/actors/capistrano.rb +62 -36
  10. data/lib/sprinkle/actors/dummy.rb +127 -0
  11. data/lib/sprinkle/actors/local.rb +59 -17
  12. data/lib/sprinkle/actors/ssh.rb +189 -107
  13. data/lib/sprinkle/actors/vlad.rb +51 -32
  14. data/lib/sprinkle/configurable.rb +2 -1
  15. data/lib/sprinkle/deployment.rb +22 -2
  16. data/lib/sprinkle/errors/pretty_failure.rb +41 -0
  17. data/lib/sprinkle/errors/remote_command_failure.rb +24 -0
  18. data/lib/sprinkle/errors/transfer_failure.rb +28 -0
  19. data/lib/sprinkle/installers/apt.rb +17 -16
  20. data/lib/sprinkle/installers/binary.rb +23 -8
  21. data/lib/sprinkle/installers/brew.rb +17 -10
  22. data/lib/sprinkle/installers/bsd_port.rb +10 -6
  23. data/lib/sprinkle/installers/deb.rb +3 -10
  24. data/lib/sprinkle/installers/freebsd_pkg.rb +5 -11
  25. data/lib/sprinkle/installers/freebsd_portinstall.rb +8 -2
  26. data/lib/sprinkle/installers/gem.rb +9 -3
  27. data/lib/sprinkle/installers/group.rb +28 -4
  28. data/lib/sprinkle/installers/installer.rb +58 -7
  29. data/lib/sprinkle/installers/mac_port.rb +13 -6
  30. data/lib/sprinkle/installers/npm.rb +42 -0
  31. data/lib/sprinkle/installers/openbsd_pkg.rb +4 -11
  32. data/lib/sprinkle/installers/opensolaris_pkg.rb +7 -13
  33. data/lib/sprinkle/installers/package_installer.rb +33 -0
  34. data/lib/sprinkle/installers/pacman.rb +5 -13
  35. data/lib/sprinkle/installers/pear.rb +40 -0
  36. data/lib/sprinkle/installers/push_text.rb +18 -5
  37. data/lib/sprinkle/installers/rake.rb +7 -2
  38. data/lib/sprinkle/installers/reconnect.rb +29 -0
  39. data/lib/sprinkle/installers/replace_text.rb +11 -2
  40. data/lib/sprinkle/installers/rpm.rb +8 -6
  41. data/lib/sprinkle/installers/runner.rb +41 -16
  42. data/lib/sprinkle/installers/smart.rb +6 -17
  43. data/lib/sprinkle/installers/source.rb +22 -10
  44. data/lib/sprinkle/installers/thor.rb +7 -0
  45. data/lib/sprinkle/installers/transfer.rb +62 -41
  46. data/lib/sprinkle/installers/user.rb +34 -4
  47. data/lib/sprinkle/installers/yum.rb +10 -10
  48. data/lib/sprinkle/installers/zypper.rb +4 -15
  49. data/lib/sprinkle/package.rb +81 -98
  50. data/lib/sprinkle/policy.rb +11 -4
  51. data/lib/sprinkle/utility/log_recorder.rb +33 -0
  52. data/lib/sprinkle/verifiers/directory.rb +1 -1
  53. data/lib/sprinkle/verifiers/executable.rb +1 -1
  54. data/lib/sprinkle/verifiers/file.rb +11 -2
  55. data/lib/sprinkle/verifiers/package.rb +2 -14
  56. data/lib/sprinkle/verifiers/permission.rb +40 -0
  57. data/lib/sprinkle/verifiers/symlink.rb +2 -2
  58. data/lib/sprinkle/verifiers/test.rb +21 -0
  59. data/lib/sprinkle/verify.rb +3 -3
  60. data/lib/sprinkle/version.rb +3 -0
  61. data/spec/fixtures/my_file.txt +1 -0
  62. data/spec/sprinkle/actors/capistrano_spec.rb +16 -3
  63. data/spec/sprinkle/actors/local_spec.rb +24 -6
  64. data/spec/sprinkle/actors/ssh_spec.rb +38 -0
  65. data/spec/sprinkle/installers/apt_spec.rb +23 -2
  66. data/spec/sprinkle/installers/binary_spec.rb +22 -14
  67. data/spec/sprinkle/installers/brew_spec.rb +4 -4
  68. data/spec/sprinkle/installers/installer_spec.rb +36 -7
  69. data/spec/sprinkle/installers/npm_spec.rb +16 -0
  70. data/spec/sprinkle/installers/pear_spec.rb +16 -0
  71. data/spec/sprinkle/installers/push_text_spec.rb +23 -1
  72. data/spec/sprinkle/installers/rpm_spec.rb +5 -0
  73. data/spec/sprinkle/installers/runner_spec.rb +27 -11
  74. data/spec/sprinkle/installers/smart_spec.rb +60 -0
  75. data/spec/sprinkle/installers/source_spec.rb +4 -4
  76. data/spec/sprinkle/installers/transfer_spec.rb +31 -16
  77. data/spec/sprinkle/package_spec.rb +10 -2
  78. data/spec/sprinkle/policy_spec.rb +6 -0
  79. data/spec/sprinkle/verify_spec.rb +18 -4
  80. data/sprinkle.gemspec +22 -158
  81. metadata +178 -96
  82. data/TODO +0 -56
  83. data/VERSION +0 -1
  84. data/lib/sprinkle/verifiers/apt.rb +0 -21
  85. data/lib/sprinkle/verifiers/brew.rb +0 -21
  86. data/lib/sprinkle/verifiers/rpm.rb +0 -21
  87. 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 < Installer
28
- attr_accessor :packages #:nodoc:
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
 
@@ -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, :dependencies, :recommends, :verifications
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 freebsd_portinstall(port, &block)
137
- @installers << Sprinkle::Installers::FreebsdPortinstall.new(self, port, &block)
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 opensolaris_pkg(*names, &block)
145
- @installers << Sprinkle::Installers::OpensolarisPkg.new(self, *names, &block)
133
+ def version(s=nil)
134
+ s ? @version = s : @version
146
135
  end
147
136
 
148
- def bsd_port(port, &block)
149
- @installers << Sprinkle::Installers::BsdPort.new(self, port, &block)
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 mac_port(port, &block)
153
- @installers << Sprinkle::Installers::MacPort.new(self, port, &block)
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 brew(*names, &block)
177
- @installers << Sprinkle::Installers::Brew.new(self, *names, &block)
150
+ def use_sudo(flag=true)
151
+ @use_sudo = flag
178
152
  end
179
-
180
- def gem(name, options = {}, &block)
181
- @recommends << :rubygems
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 binary(source, options = {}, &block)
191
- @installers << Sprinkle::Installers::Binary.new(self, source, options, &block)
158
+ def opts
159
+ @opts || {}
192
160
  end
193
161
 
194
- def rake(name, options = {}, &block)
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 push_text(text, path, options = {}, &block)
207
- @installers << Sprinkle::Installers::PushText.new(self, text, path, options, &block)
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 transfer(source, destination, options = {}, &block)
215
- @installers << Sprinkle::Installers::Transfer.new(self, source, destination, options, &block)
169
+ def noop(&block)
170
+ install Sprinkle::Installers::Runner.new(self, "echo noop", &block)
216
171
  end
217
-
218
- def runner(cmd, &block)
219
- @installers << Sprinkle::Installers::Runner.new(self, cmd, &block)
220
- end
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 "--> #{self.name} already installed for roles: #{roles}"
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.info "--> Checking if #{self.name} is already installed for roles: #{roles}"
215
+ logger.debug "--> Checking if #{self.name} is already installed for roles: #{roles}"
259
216
  else
260
- logger.info "--> Verifying #{self.name} was properly installed for roles: #{roles}"
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
- @dependencies << packages
271
- @dependencies.flatten!
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
- @recommends << packages
276
- @recommends.flatten!
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
- @optional << packages
281
- @optional.flatten!
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
@@ -51,7 +51,7 @@ module Sprinkle
51
51
  end
52
52
 
53
53
  class Policy #:nodoc:
54
- attr_reader :name, :packages
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
- @packages << package
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
@@ -9,7 +9,7 @@ module Sprinkle
9
9
 
10
10
  # Tests that the directory <tt>dir</tt> exists.
11
11
  def has_directory(dir)
12
- @commands << "test -d #{dir}"
12
+ test "-d #{dir}"
13
13
  end
14
14
  end
15
15
  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
- @commands << "test -x #{path}"
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
- @commands << "test -f #{path}"
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
- local = `md5 #{localfile}`.split.last
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
- if packages.is_a?(Array) && packages.first.is_a?(Array)
8
- packages = packages.first
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