sprinkle 0.7.1.1 → 0.7.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.
Files changed (62) hide show
  1. data/.tm_properties +7 -0
  2. data/CHANGELOG.md +8 -0
  3. data/Gemfile +3 -0
  4. data/Gemfile.lock +46 -4
  5. data/Rakefile +38 -1
  6. data/examples/rails/packages/database.rb +7 -0
  7. data/examples/rails/templates/mysql.cnf.erb +3 -0
  8. data/lib/sprinkle.rb +8 -13
  9. data/lib/sprinkle/actors/actor.rb +17 -17
  10. data/lib/sprinkle/actors/capistrano.rb +3 -3
  11. data/lib/sprinkle/actors/local.rb +10 -6
  12. data/lib/sprinkle/actors/ssh.rb +8 -8
  13. data/lib/sprinkle/actors/vlad.rb +7 -7
  14. data/lib/sprinkle/core.rb +16 -0
  15. data/lib/sprinkle/deployment.rb +8 -6
  16. data/lib/sprinkle/errors/pretty_failure.rb +1 -1
  17. data/lib/sprinkle/errors/template_error.rb +1 -1
  18. data/lib/sprinkle/installers/brew.rb +0 -2
  19. data/lib/sprinkle/installers/deb.rb +6 -2
  20. data/lib/sprinkle/installers/file.rb +38 -39
  21. data/lib/sprinkle/installers/freebsd_pkg.rb +4 -0
  22. data/lib/sprinkle/installers/group.rb +11 -12
  23. data/lib/sprinkle/installers/install_package.rb +5 -5
  24. data/lib/sprinkle/installers/openbsd_pkg.rb +5 -1
  25. data/lib/sprinkle/installers/opensolaris_pkg.rb +5 -1
  26. data/lib/sprinkle/installers/package_installer.rb +7 -3
  27. data/lib/sprinkle/installers/pacman.rb +4 -0
  28. data/lib/sprinkle/installers/push_text.rb +1 -1
  29. data/lib/sprinkle/installers/rake.rb +14 -6
  30. data/lib/sprinkle/installers/rpm.rb +5 -0
  31. data/lib/sprinkle/installers/runner.rb +3 -2
  32. data/lib/sprinkle/installers/thor.rb +14 -15
  33. data/lib/sprinkle/installers/transfer.rb +0 -1
  34. data/lib/sprinkle/installers/user.rb +2 -2
  35. data/lib/sprinkle/installers/yum.rb +9 -8
  36. data/lib/sprinkle/installers/zypper.rb +5 -1
  37. data/lib/sprinkle/package.rb +31 -8
  38. data/lib/sprinkle/package/chooser.rb +1 -1
  39. data/lib/sprinkle/package/package_repository.rb +1 -1
  40. data/lib/sprinkle/package/rendering.rb +13 -10
  41. data/lib/sprinkle/policy.rb +71 -74
  42. data/lib/sprinkle/script.rb +2 -2
  43. data/lib/sprinkle/utility/log_recorder.rb +1 -1
  44. data/lib/sprinkle/verifiers/file.rb +17 -1
  45. data/lib/sprinkle/verifiers/package.rb +2 -2
  46. data/lib/sprinkle/verify.rb +2 -2
  47. data/lib/sprinkle/version.rb +1 -1
  48. data/spec/sprinkle/deployment_spec.rb +5 -3
  49. data/spec/sprinkle/extensions/rendering_spec.rb +29 -14
  50. data/spec/sprinkle/installers/file_spec.rb +1 -1
  51. data/spec/sprinkle/installers/installer_spec.rb +1 -1
  52. data/spec/sprinkle/installers/opensolaris_pkg_spec.rb +0 -4
  53. data/spec/sprinkle/installers/transfer_spec.rb +1 -1
  54. data/spec/sprinkle/package_spec.rb +31 -0
  55. data/spec/sprinkle/policy_spec.rb +5 -5
  56. data/spec/sprinkle/verify_spec.rb +0 -7
  57. data/spec/templates/locals.erb +1 -0
  58. data/spec/templates/test.erb +1 -0
  59. data/templates/test.erb +1 -0
  60. metadata +10 -4
  61. data/lib/sprinkle/verifiers/directory.rb +0 -16
  62. data/lib/sprinkle/verifiers/symlink.rb +0 -30
@@ -79,7 +79,6 @@ module Sprinkle
79
79
 
80
80
  def render_template(template, context, prefix)
81
81
  require 'tempfile'
82
- require 'erubis'
83
82
 
84
83
  output = @package.template(template, context)
85
84
 
@@ -1,6 +1,6 @@
1
1
  module Sprinkle
2
2
  module Installers
3
- # The user installer helps add users. You may pass flags as an option.
3
+ # The user installer add users. You may pass :flags as an option.
4
4
  #
5
5
  # == Example Usage
6
6
  #
@@ -8,7 +8,7 @@ module Sprinkle
8
8
  # add_user 'admin', :flags => "--disabled-password"
9
9
  #
10
10
  # verify do
11
- # has_user 'admin', :in_group = "root"
11
+ # has_user 'admin', :in_group => "root"
12
12
  # end
13
13
  # end
14
14
 
@@ -7,14 +7,11 @@ module Sprinkle
7
7
  # Installing the magic_beans RPM via Yum. Its all the craze these days.
8
8
  #
9
9
  # package :magic_beans do
10
- # yum 'magic_beans'
11
- # verify { has_yum 'magic_beans' }
12
- # end
13
- #
14
- # You may also specify multiple rpms as arguments or an array:
15
- #
16
- # package :magic_beans do
17
- # yum "magic_beans", "magic_sauce"
10
+ # yum 'magic_beans', 'magic_corn'
11
+ # verify do
12
+ # has_yum 'magic_beans'
13
+ # has_yum 'magic_corn'
14
+ # end
18
15
  # end
19
16
  #
20
17
  # To install a specific version just add that version after the name
@@ -24,6 +21,10 @@ module Sprinkle
24
21
  # end
25
22
  class Yum < PackageInstaller
26
23
 
24
+ ##
25
+ # installs the RPM packages passed
26
+ # :method: yum
27
+ # :call-seq: yum(*packages)
27
28
  auto_api
28
29
 
29
30
  verify_api do
@@ -19,7 +19,11 @@ module Sprinkle
19
19
  # zypper "magic_beans", "magic_sauce"
20
20
  # end
21
21
  class Zypper < PackageInstaller
22
-
22
+
23
+ ##
24
+ # installs the ZYpp packages passed
25
+ # :method: zypper
26
+ # :call-seq: zypper(*packages)
23
27
  auto_api
24
28
 
25
29
  protected
@@ -1,3 +1,4 @@
1
+ # encoding: utf-8
1
2
  module Sprinkle
2
3
  # = Packages
3
4
  #
@@ -36,6 +37,20 @@ module Sprinkle
36
37
  # provision the server with Ruby to make sure the requirements are met.
37
38
  # In turn, if ruby has requirements, it installs those first, and so on.
38
39
  #
40
+ # == Defaults
41
+ #
42
+ # Packages can be given defaults.
43
+ # These default options are available as a hash in opts.
44
+ #
45
+ # package :deploy_user do
46
+ # defaults :username => 'deploy'
47
+ # add_user opts[:username]
48
+ # end
49
+ #
50
+ # Options given when requiring a package are merged over the defaults
51
+ #
52
+ # requires :deploy_user, :username => 'deployer'
53
+ #
39
54
  # == Verifications
40
55
  #
41
56
  # Most of the time its important to know whether the software you're
@@ -142,7 +157,7 @@ module Sprinkle
142
157
 
143
158
  def instance(*args)
144
159
  p=Package.new(name, @metadata) {}
145
- p.opts = args.extract_options!
160
+ p.opts = defaults.merge(args.extract_options!)
146
161
  p.args = args
147
162
  p.instance_variable_set("@block", @block)
148
163
  p.instance_eval &@block
@@ -156,13 +171,17 @@ module Sprinkle
156
171
  def use_sudo(flag=true)
157
172
  @use_sudo = flag
158
173
  end
159
-
174
+
175
+ def defaults(s=nil)
176
+ s ? @defaults = s : @defaults ||= Hash.new
177
+ end
178
+
160
179
  def args
161
- @args || []
180
+ @args ||= []
162
181
  end
163
182
 
164
183
  def opts
165
- @opts || {}
184
+ @opts ||= defaults.clone
166
185
  end
167
186
 
168
187
  class ContextError < StandardError #:nodoc:
@@ -172,9 +191,9 @@ module Sprinkle
172
191
  raise ContextError, "Cannot call get inside a package, must be inside an Installer block"
173
192
  end
174
193
 
175
- # meta installer
176
- # TODO - fix to be atomic
194
+ # TODO - remove
177
195
  def push_file(file, options ={}, &block)
196
+ ActiveSupport::Deprecation.warn("push_file is depreciated and will be removed in v0.9. Use the new `file` installer instead.")
178
197
  raise "need content" unless options[:content]
179
198
  runner "#{"sudo " if sudo?}rm -f #{file}"
180
199
  push_text options[:content], file, options, &block
@@ -182,11 +201,15 @@ module Sprinkle
182
201
 
183
202
  def verify(description = '', &block)
184
203
  @verifications << Sprinkle::Verify.new(self, description, &block)
185
- end
204
+ end
186
205
 
187
206
  def process(deployment, roles)
188
207
  logger.info " * #{name}"
189
208
  return if meta_package?
209
+ opts.each_with_index do |(k, v), index|
210
+ branch = (index == opts.size - 1) ? "└" : "├"
211
+ logger.debug " #{branch}─ #{k}: #{v}"
212
+ end
190
213
 
191
214
  # Run a pre-test to see if the software is already installed. If so,
192
215
  # we can skip it, unless we have the force option turned on!
@@ -224,7 +247,7 @@ module Sprinkle
224
247
  v.process(roles)
225
248
  end
226
249
  end
227
-
250
+
228
251
  def requires(*packages)
229
252
  add_dependencies packages, :dependencies
230
253
  end
@@ -1,5 +1,5 @@
1
1
  module Sprinkle::Package
2
- class Chooser
2
+ class Chooser #:nodoc:
3
3
  def self.select_package(name, packages)
4
4
  if packages.size <= 1
5
5
  package = packages.first
@@ -1,5 +1,5 @@
1
1
  module Sprinkle::Package
2
- class PackageRepository
2
+ class PackageRepository #:nodoc:
3
3
 
4
4
  # sets up an empty repository
5
5
  def initialize
@@ -4,24 +4,27 @@ require 'digest/md5'
4
4
  module Sprinkle::Package
5
5
  module Rendering
6
6
  extend ActiveSupport::Concern
7
-
7
+
8
8
  included do
9
9
  self.send :include, Helpers
10
10
  end
11
-
12
- def template(src, bound=binding)
11
+
12
+ def template(src, context=binding)
13
13
  eruby = Erubis::Eruby.new(src)
14
- output = eruby.result(bound)
14
+ output = eruby.result(context)
15
15
  rescue Object => e
16
- raise Sprinkle::Errors::TemplateError.new(e, src, bound)
16
+ raise Sprinkle::Errors::TemplateError.new(e, src, context)
17
17
  end
18
18
 
19
- def render(file)
20
- contents=File.read(expand_filename(file))
21
- template(contents)
19
+ def render(filename, context=binding)
20
+ contents=File.read(expand_filename(filename))
21
+ template(contents, context)
22
22
  end
23
23
 
24
+ # Helper methods can be called from inside your package and
25
+ # verification code
24
26
  module Helpers
27
+ # return the md5 of a string (as a hex string)
25
28
  def md5(s)
26
29
  Digest::MD5.hexdigest(s)
27
30
  end
@@ -29,13 +32,13 @@ module Sprinkle::Package
29
32
 
30
33
  private
31
34
 
32
- def expand_filename(n)
35
+ def expand_filename(n) #:nodoc:
33
36
  return n.to_s if n.to_s.starts_with? "/"
34
37
  ["./templates/#{n}","./templates/#{n}.erb"].each do |f|
35
38
  return f if File.exist?(f)
36
39
  end
37
40
  raise "template file not found"
38
41
  end
39
-
42
+
40
43
  end
41
44
  end
@@ -1,20 +1,31 @@
1
1
  require 'highline/import'
2
2
 
3
3
  module Sprinkle
4
+ class NoMatchingServersError < StandardError #:nodoc:
5
+ def initialize(name, roles)
6
+ @name = name
7
+ @roles = roles
8
+ end
9
+
10
+ def to_s
11
+ "Policy #{@name} is to be installed on #{@roles.inspect} but no server has such a role."
12
+ end
13
+ end
14
+
4
15
  # = Policies
5
16
  #
6
- # A policy defines a set of packages which are required for a certain
17
+ # Policies define a set of packages which are required for a certain
7
18
  # role (app, database, etc.). All policies defined will be run and all
8
19
  # packages required by the policy will be installed. So whereas defining
9
20
  # a Sprinkle::Package merely defines it, defining a Sprinkle::Policy
10
21
  # actually causes those packages to install.
11
22
  #
12
- # == A Basic Example
23
+ # == Example
13
24
  #
14
25
  # policy :blog, :roles => :app do
15
- # require :webserver
16
- # require :database
17
- # require :rails
26
+ # requires :webserver
27
+ # requires :database
28
+ # requires :rails
18
29
  # end
19
30
  #
20
31
  # This says that for the blog on the app role, it requires certain
@@ -38,91 +49,77 @@ module Sprinkle
38
49
  # no software will be installed twice, so you may require a webserver on
39
50
  # multiple packages within the same role without having to wait for
40
51
  # that package to install repeatedly.
41
- module Policy
42
- POLICIES = [] #:nodoc:
43
-
44
- # Defines a single policy. Currently the only option, which is also
45
- # required, is :roles, which defines which servers a policy is
46
- # used on.
47
- def policy(name, options = {}, &block)
48
- p = Policy.new(name, options, &block)
49
- POLICIES << p
50
- p
52
+ class Policy
53
+ attr_reader :name
54
+ # roles for which a policy should be installed [required]
55
+ attr_reader :roles
56
+
57
+ # creates a new policy,
58
+ # although policies are typically not created directly but
59
+ # rather via the Core#policy helper.
60
+ def initialize(name, metadata = {}, &block)
61
+ raise 'No name provided' unless name
62
+ raise 'No roles provided' unless metadata[:roles]
63
+
64
+ @name = name
65
+ @roles = metadata[:roles]
66
+ @packages = []
67
+ self.instance_eval(&block)
51
68
  end
52
-
53
- class NoMatchingServersError < StandardError #:nodoc:
54
- def initialize(name, roles)
55
- @name = name
56
- @roles = roles
57
- end
58
-
59
- def to_s
60
- "Policy #{@name} is to be installed on #{@roles.inspect} but no server has such a role."
61
- end
69
+
70
+ # tell a policy which packages are required
71
+ def requires(package, opts={})
72
+ @packages << [package, opts]
62
73
  end
63
74
 
64
- class Policy #:nodoc:
65
- attr_reader :name, :roles
75
+ def packages #:nodoc:
76
+ @packages.map {|x| x.first }
77
+ end
66
78
 
67
- def initialize(name, metadata = {}, &block)
68
- raise 'No name provided' unless name
69
- raise 'No roles provided' unless metadata[:roles]
79
+ def to_s #:nodoc:
80
+ name; end
70
81
 
71
- @name = name
72
- @roles = metadata[:roles]
73
- @packages = []
74
- self.instance_eval(&block)
75
- end
76
-
77
- def requires(package, opts={})
78
- @packages << [package, opts]
79
- end
82
+ def process(deployment) #:nodoc:
83
+ raise NoMatchingServersError.new(@name, @roles) unless deployment.style.servers_for_role?(@roles)
80
84
 
81
- def packages; @packages.map {|x| x.first }; end
85
+ all = []
86
+
87
+ logger.info "[#{name}]"
82
88
 
83
- def to_s; name; end
89
+ cloud_info "--> Cloud hierarchy for policy #{@name}"
84
90
 
85
- def process(deployment)
86
- raise NoMatchingServersError.new(@name, @roles) unless deployment.style.servers_for_role?(@roles)
87
-
88
- all = []
89
-
90
- logger.info "[#{name}]"
91
+ @packages.each do |p, args|
92
+ cloud_info " * requires package #{p}"
91
93
 
92
- cloud_info "--> Cloud hierarchy for policy #{@name}"
94
+ package = Sprinkle::Package::PACKAGES.find_all(p, args)
95
+ raise "Package definition not found for key: #{p}" unless package
96
+ package = Sprinkle::Package::Chooser.select_package(p, package) if package.is_a? Array # handle virtual package selection
97
+ # get an instance of the package and pass our config options
98
+ package = package.instance(*args)
93
99
 
94
- @packages.each do |p, args|
95
- cloud_info " * requires package #{p}"
100
+ tree = package.tree do |parent, child, depth|
101
+ indent = "\t" * depth; cloud_info "#{indent}Package #{parent.name} requires #{child.name}"
102
+ end
96
103
 
97
- package = Sprinkle::Package::PACKAGES.find_all(p, args)
98
- raise "Package definition not found for key: #{p}" unless package
99
- package = Sprinkle::Package::Chooser.select_package(p, package) if package.is_a? Array # handle virtual package selection
100
- # get an instance of the package and pass our config options
101
- package = package.instance(*args)
104
+ all << tree
105
+ end
102
106
 
103
- tree = package.tree do |parent, child, depth|
104
- indent = "\t" * depth; cloud_info "#{indent}Package #{parent.name} requires #{child.name}"
105
- end
107
+ normalize(all).each do |package|
108
+ package.process(deployment, @roles)
109
+ end
110
+ end
106
111
 
107
- all << tree
108
- end
112
+ private
109
113
 
110
- normalize(all) do |package|
111
- package.process(deployment, @roles)
112
- end
114
+ def normalize(all, &block)
115
+ all = all.flatten.uniq {|x| [x.name, x.version] }
116
+ cloud_info "--> Normalized installation order for all packages: #{all.collect(&:name).join(', ')}\n"
117
+ all
113
118
  end
114
119
 
115
- private
116
-
117
- def cloud_info(message)
118
- logger.info(message) if Sprinkle::OPTIONS[:cloud] or logger.debug?
119
- end
120
+ def cloud_info(message)
121
+ logger.info(message) if Sprinkle::OPTIONS[:cloud] or logger.debug?
122
+ end
120
123
 
121
- def normalize(all, &block)
122
- all = all.flatten.uniq {|x| [x.name, x.version] }
123
- cloud_info "--> Normalized installation order for all packages: #{all.collect(&:name).join(', ')}\n"
124
- all.each &block
125
- end
126
- end
127
124
  end
128
125
  end
@@ -1,7 +1,7 @@
1
1
  module Sprinkle
2
- # = Programmatically Run Sprinkle
2
+ # = Scripting
3
3
  #
4
- # Sprinkle::Script gives you a way to programatically run a given
4
+ # Script gives you a way to programatically run a given
5
5
  # sprinkle script.
6
6
  class Script
7
7
  include Sprinkle::Deployment
@@ -1,5 +1,5 @@
1
1
  module Sprinkle
2
- module Utility
2
+ module Utility #:nodoc:
3
3
  class LogRecorder #:nodoc:
4
4
 
5
5
  attr_accessor :err, :out, :command, :code
@@ -12,11 +12,27 @@ module Sprinkle
12
12
  #
13
13
  module File
14
14
  Sprinkle::Verify.register(Sprinkle::Verifiers::File)
15
-
15
+
16
+ # tests that the file <tt>path</tt> exists
16
17
  def has_file(path)
17
18
  test "-f #{path}"
18
19
  end
19
20
 
21
+ # Tests that the directory <tt>dir</tt> exists.
22
+ def has_directory(dir)
23
+ test "-d #{dir}"
24
+ end
25
+
26
+ # Checks that <tt>symlink</tt> is a symbolic link. If <tt>file</tt> is
27
+ # given, it checks that <tt>symlink</tt> points to <tt>file</tt>
28
+ def has_symlink(symlink, file = nil)
29
+ if file.nil?
30
+ test "-L #{symlink}"
31
+ else
32
+ test "'#{file}' = `readlink #{symlink}`"
33
+ end
34
+ end
35
+
20
36
  def no_file(path)
21
37
  test "! -f #{path}"
22
38
  end