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.
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