sprinkle 0.7.1.1 → 0.7.2

Sign up to get free protection for your applications and to get access to all the features.
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