jsierles-sprinkle 0.1.9

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 (89) hide show
  1. data/CREDITS +19 -0
  2. data/History.txt +4 -0
  3. data/MIT-LICENSE +20 -0
  4. data/Manifest.txt +87 -0
  5. data/README.txt +238 -0
  6. data/Rakefile +4 -0
  7. data/bin/sprinkle +86 -0
  8. data/config/hoe.rb +70 -0
  9. data/config/requirements.rb +17 -0
  10. data/examples/packages/build_essential.rb +9 -0
  11. data/examples/packages/databases/mysql.rb +13 -0
  12. data/examples/packages/databases/sqlite3.rb +16 -0
  13. data/examples/packages/phusion.rb +55 -0
  14. data/examples/packages/ruby/rails.rb +9 -0
  15. data/examples/packages/ruby/ruby.rb +17 -0
  16. data/examples/packages/ruby/rubygems.rb +17 -0
  17. data/examples/packages/scm/git.rb +11 -0
  18. data/examples/packages/scm/subversion.rb +4 -0
  19. data/examples/packages/servers/apache.rb +15 -0
  20. data/examples/rails/README +15 -0
  21. data/examples/rails/deploy.rb +2 -0
  22. data/examples/rails/packages/database.rb +9 -0
  23. data/examples/rails/packages/essential.rb +9 -0
  24. data/examples/rails/packages/rails.rb +28 -0
  25. data/examples/rails/packages/scm.rb +11 -0
  26. data/examples/rails/packages/search.rb +11 -0
  27. data/examples/rails/packages/server.rb +28 -0
  28. data/examples/rails/rails.rb +73 -0
  29. data/examples/sprinkle/sprinkle.rb +38 -0
  30. data/lib/sprinkle.rb +32 -0
  31. data/lib/sprinkle/actors/actors.rb +17 -0
  32. data/lib/sprinkle/actors/capistrano.rb +117 -0
  33. data/lib/sprinkle/actors/local.rb +26 -0
  34. data/lib/sprinkle/actors/ssh.rb +81 -0
  35. data/lib/sprinkle/actors/vlad.rb +65 -0
  36. data/lib/sprinkle/configurable.rb +31 -0
  37. data/lib/sprinkle/deployment.rb +73 -0
  38. data/lib/sprinkle/extensions/arbitrary_options.rb +10 -0
  39. data/lib/sprinkle/extensions/array.rb +5 -0
  40. data/lib/sprinkle/extensions/blank_slate.rb +5 -0
  41. data/lib/sprinkle/extensions/dsl_accessor.rb +15 -0
  42. data/lib/sprinkle/extensions/string.rb +10 -0
  43. data/lib/sprinkle/extensions/symbol.rb +7 -0
  44. data/lib/sprinkle/installers/apt.rb +52 -0
  45. data/lib/sprinkle/installers/deb.rb +38 -0
  46. data/lib/sprinkle/installers/gem.rb +62 -0
  47. data/lib/sprinkle/installers/installer.rb +120 -0
  48. data/lib/sprinkle/installers/rake.rb +37 -0
  49. data/lib/sprinkle/installers/rpm.rb +37 -0
  50. data/lib/sprinkle/installers/source.rb +179 -0
  51. data/lib/sprinkle/installers/yum.rb +37 -0
  52. data/lib/sprinkle/package.rb +233 -0
  53. data/lib/sprinkle/policy.rb +125 -0
  54. data/lib/sprinkle/script.rb +23 -0
  55. data/lib/sprinkle/verifiers/directory.rb +16 -0
  56. data/lib/sprinkle/verifiers/executable.rb +36 -0
  57. data/lib/sprinkle/verifiers/file.rb +20 -0
  58. data/lib/sprinkle/verifiers/process.rb +21 -0
  59. data/lib/sprinkle/verifiers/ruby.rb +25 -0
  60. data/lib/sprinkle/verifiers/symlink.rb +30 -0
  61. data/lib/sprinkle/verify.rb +114 -0
  62. data/lib/sprinkle/version.rb +9 -0
  63. data/script/destroy +14 -0
  64. data/script/generate +14 -0
  65. data/spec/spec.opts +1 -0
  66. data/spec/spec_helper.rb +17 -0
  67. data/spec/sprinkle/actors/capistrano_spec.rb +170 -0
  68. data/spec/sprinkle/actors/local_spec.rb +29 -0
  69. data/spec/sprinkle/configurable_spec.rb +46 -0
  70. data/spec/sprinkle/deployment_spec.rb +80 -0
  71. data/spec/sprinkle/extensions/array_spec.rb +19 -0
  72. data/spec/sprinkle/extensions/string_spec.rb +21 -0
  73. data/spec/sprinkle/installers/apt_spec.rb +70 -0
  74. data/spec/sprinkle/installers/gem_spec.rb +75 -0
  75. data/spec/sprinkle/installers/installer_spec.rb +151 -0
  76. data/spec/sprinkle/installers/rake_spec.rb +29 -0
  77. data/spec/sprinkle/installers/rpm_spec.rb +50 -0
  78. data/spec/sprinkle/installers/source_spec.rb +331 -0
  79. data/spec/sprinkle/installers/yum_spec.rb +49 -0
  80. data/spec/sprinkle/package_spec.rb +422 -0
  81. data/spec/sprinkle/policy_spec.rb +126 -0
  82. data/spec/sprinkle/script_spec.rb +51 -0
  83. data/spec/sprinkle/sprinkle_spec.rb +25 -0
  84. data/spec/sprinkle/verify_spec.rb +160 -0
  85. data/sprinkle.gemspec +70 -0
  86. data/tasks/deployment.rake +34 -0
  87. data/tasks/environment.rake +7 -0
  88. data/tasks/rspec.rake +21 -0
  89. metadata +180 -0
@@ -0,0 +1,37 @@
1
+ module Sprinkle
2
+ module Installers
3
+ # = Yum Package Installer
4
+ #
5
+ # The Yum package installer installs RPM packages.
6
+ #
7
+ # == Example Usage
8
+ #
9
+ # Installing the magic_beans RPM via Yum. Its all the craze these days.
10
+ #
11
+ # package :magic_beans do
12
+ # yum 'magic_beans'
13
+ # end
14
+ #
15
+ # You may also specify multiple rpms as an array:
16
+ #
17
+ # package :magic_beans do
18
+ # yum %w(magic_beans magic_sauce)
19
+ # end
20
+ class Yum < Installer
21
+ attr_accessor :packages #:nodoc:
22
+
23
+ def initialize(parent, packages, &block) #:nodoc:
24
+ super parent, &block
25
+ packages = [packages] unless packages.is_a? Array
26
+ @packages = packages
27
+ end
28
+
29
+ protected
30
+
31
+ def install_commands #:nodoc:
32
+ "yum #{@packages.join(' ')} -y"
33
+ end
34
+
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,233 @@
1
+ module Sprinkle
2
+ # = Packages
3
+ #
4
+ # A package defines one or more things to provision onto the server.
5
+ # There is a lot of flexibility in a way a package is defined but
6
+ # let me give you a basic example:
7
+ #
8
+ # package :ruby do
9
+ # description 'Ruby MRI'
10
+ # version '1.8.6'
11
+ # apt 'ruby'
12
+ #
13
+ # verify { has_executable 'ruby' }
14
+ # end
15
+ #
16
+ # The above would define a package named 'ruby' and give it a description
17
+ # and explicitly say its version. It is installed via apt and to verify
18
+ # the installation was successful sprinkle will check for the executable
19
+ # 'ruby' being availble. Pretty simple, right?
20
+ #
21
+ # <b>Note:</b> Defining a package does not INSTALL it. To install a
22
+ # package, you must require it in a Sprinkle::Policy block.
23
+ #
24
+ # == Pre-Requirements
25
+ #
26
+ # Most packages have some sort of pre-requisites in order to be installed.
27
+ # Sprinkle allows you to define the requirements of the package, which
28
+ # will be installed before the package itself. An example below:
29
+ #
30
+ # package :rubygems do
31
+ # source 'http://rubyforge.org/rubygems.tgz'
32
+ # requires :ruby
33
+ # end
34
+ #
35
+ # In this case, when rubygems is being installed, Sprinkle will first
36
+ # provision the server with Ruby to make sure the requirements are met.
37
+ # In turn, if ruby has requirements, it installs those first, and so on.
38
+ #
39
+ # == Verifications
40
+ #
41
+ # Most of the time its important to know whether the software you're
42
+ # attempting to install was installed successfully or not. For this,
43
+ # Sprinkle provides verifications. Verifications are one or more blocks
44
+ # which define rules with which Sprinkle can check if it installed
45
+ # the package successfully. If these verification blocks fail, then
46
+ # Sprinkle will gracefully stop the entire process. An example below:
47
+ #
48
+ # package :rubygems do
49
+ # source 'http://rubyforge.org/rubygems.tgz'
50
+ # requires :ruby
51
+ #
52
+ # verify { has_executable 'gem' }
53
+ # end
54
+ #
55
+ # In addition to verifying an installation was successfully, by default
56
+ # Sprinkle runs these verifications <em>before</em> the installation to
57
+ # check if the package is already installed. If the verifications pass
58
+ # before installing the package, it skips the package. To override this
59
+ # behavior, set the -f flag on the sprinkle script or set the
60
+ # :force option to true in Sprinkle::OPTIONS
61
+ #
62
+ # For more information on verifications and to see all the available
63
+ # verifications, see Sprinkle::Verify
64
+ #
65
+ # == Virtual Packages
66
+ #
67
+ # Sometimes, there are multiple packages available for a single task. An
68
+ # example is a database package. It can contain mySQL, postgres, or sqlite!
69
+ # This is where virtual packages come in handy. They are defined as follows:
70
+ #
71
+ # package :sqlite3, :provides => :database do
72
+ # apt 'sqlite3'
73
+ # end
74
+ #
75
+ # The :provides option allows you to reference this package either by :sqlite3
76
+ # or by :database. But whereas the package name is unique, multiple packages may
77
+ # share the same provision. If this is the case, when running Sprinkle, the
78
+ # script will ask you which provision you want to install. At this time, you
79
+ # can only install one.
80
+ #
81
+ # == Meta-Packages
82
+ #
83
+ # A package doesn't require an installer. If you want to define a package which
84
+ # merely encompasses other packages, that is fine too. Example:
85
+ #
86
+ # package :meta do
87
+ # requires :magic_beans
88
+ # requires :magic_sauce
89
+ # end
90
+ #
91
+ #--
92
+ # FIXME: Should probably document recommendations.
93
+ #++
94
+ module Package
95
+ PACKAGES = {}
96
+
97
+ def package(name, metadata = {}, &block)
98
+ package = Package.new(name, metadata, &block)
99
+ PACKAGES[name] = package
100
+
101
+ if package.provides
102
+ (PACKAGES[package.provides] ||= []) << package
103
+ end
104
+
105
+ package
106
+ end
107
+
108
+ class Package #:nodoc:
109
+ include ArbitraryOptions
110
+ attr_accessor :name, :provides, :installer, :dependencies, :recommends, :verifications
111
+
112
+ def initialize(name, metadata = {}, &block)
113
+ raise 'No package name supplied' unless name
114
+
115
+ @name = name
116
+ @provides = metadata[:provides]
117
+ @dependencies = []
118
+ @recommends = []
119
+ @verifications = []
120
+ self.instance_eval &block
121
+ end
122
+
123
+ def apt(*names, &block)
124
+ @installer = Sprinkle::Installers::Apt.new(self, *names, &block)
125
+ end
126
+
127
+ def deb(*names, &block)
128
+ @installer = Sprinkle::Installers::Deb.new(self, *names, &block)
129
+ end
130
+
131
+ def rpm(*names, &block)
132
+ @installer = Sprinkle::Installers::Rpm.new(self, *names, &block)
133
+ end
134
+
135
+ def yum(*names, &block)
136
+ @installer = Sprinkle::Installers::Yum.new(self, *names, &block)
137
+ end
138
+
139
+ def gem(name, options = {}, &block)
140
+ @recommends << :rubygems
141
+ @installer = Sprinkle::Installers::Gem.new(self, name, options, &block)
142
+ end
143
+
144
+ def source(source, options = {}, &block)
145
+ @recommends << :build_essential # Ubuntu/Debian
146
+ @installer = Sprinkle::Installers::Source.new(self, source, options, &block)
147
+ end
148
+
149
+ def rake(*names, &block)
150
+ @installer = Sprinkle::Installers::Rake.new(self, options, *names, &block)
151
+ end
152
+
153
+ def verify(description = '', &block)
154
+ @verifications << Sprinkle::Verify.new(self, description, &block)
155
+ end
156
+
157
+ def process(deployment, roles)
158
+ return if meta_package?
159
+
160
+ # Run a pre-test to see if the software is already installed. If so,
161
+ # we can skip it, unless we have the force option turned on!
162
+ unless @verifications.empty? || Sprinkle::OPTIONS[:force]
163
+ begin
164
+ process_verifications(deployment, roles, true)
165
+
166
+ logger.info "--> #{self.name} already installed for roles: #{roles}"
167
+ return
168
+ rescue Sprinkle::VerificationFailed => e
169
+ # Continue
170
+ end
171
+ end
172
+
173
+ @installer.defaults(deployment)
174
+ @installer.process(roles)
175
+
176
+ process_verifications(deployment, roles)
177
+ end
178
+
179
+ def process_verifications(deployment, roles, pre = false)
180
+ return if @verifications.blank?
181
+
182
+ if pre
183
+ logger.info "--> Checking if #{self.name} is already installed for roles: #{roles}"
184
+ else
185
+ logger.info "--> Verifying #{self.name} was properly installed for roles: #{roles}"
186
+ end
187
+
188
+ @verifications.each do |v|
189
+ v.defaults(deployment)
190
+ v.process(roles)
191
+ end
192
+ end
193
+
194
+ def requires(*packages)
195
+ @dependencies << packages
196
+ @dependencies.flatten!
197
+ end
198
+
199
+ def recommends(*packages)
200
+ @recommends << packages
201
+ @recommends.flatten!
202
+ end
203
+
204
+ def tree(depth = 1, &block)
205
+ packages = []
206
+
207
+ @recommends.each do |dep|
208
+ package = PACKAGES[dep]
209
+ next unless package # skip missing recommended packages as they can be optional
210
+ block.call(self, package, depth) if block
211
+ packages << package.tree(depth + 1, &block)
212
+ end
213
+
214
+ @dependencies.each do |dep|
215
+ package = PACKAGES[dep]
216
+ raise "Package definition not found for key: #{dep}" unless package
217
+ block.call(self, package, depth) if block
218
+ packages << package.tree(depth + 1, &block)
219
+ end
220
+
221
+ packages << self
222
+ end
223
+
224
+ def to_s; @name; end
225
+
226
+ private
227
+
228
+ def meta_package?
229
+ @installer == nil
230
+ end
231
+ end
232
+ end
233
+ end
@@ -0,0 +1,125 @@
1
+ require 'highline/import'
2
+
3
+ module Sprinkle
4
+ # = Policies
5
+ #
6
+ # A policy defines a set of packages which are required for a certain
7
+ # role (app, database, etc.). All policies defined will be run and all
8
+ # packages required by the policy will be installed. So whereas defining
9
+ # a Sprinkle::Package merely defines it, defining a Sprinkle::Policy
10
+ # actually causes those packages to install.
11
+ #
12
+ # == A Basic Example
13
+ #
14
+ # policy :blog, :roles => :app do
15
+ # require :webserver
16
+ # require :database
17
+ # require :rails
18
+ # end
19
+ #
20
+ # This says that for the blog on the app role, it requires certain
21
+ # packages. The :roles option is <em>exactly</em> the same as a capistrano
22
+ # or vlad role. A role merely defines what server the commands are run
23
+ # on. This way, a single Sprinkle script can provision an entire group
24
+ # of servers.
25
+ #
26
+ # To define a role, put in your actor specific configuration file (recipe or
27
+ # script file):
28
+ #
29
+ # role :app, "208.28.38.44"
30
+ #
31
+ # The capistrano and vlad syntax is the same for that. If you're using a
32
+ # custom actor, you may have to do it differently.
33
+ #
34
+ # == Multiple Policies
35
+ #
36
+ # You may specify as many policies as you'd like. If the packages you're
37
+ # requiring are properly defined with verification blocks, then
38
+ # no software will be installed twice, so you may require a webserver on
39
+ # multiple packages within the same role without having to wait for
40
+ # 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
51
+ end
52
+
53
+ class Policy #:nodoc:
54
+ attr_reader :name, :packages
55
+
56
+ def initialize(name, metadata = {}, &block)
57
+ raise 'No name provided' unless name
58
+ raise 'No roles provided' unless metadata[:roles]
59
+
60
+ @name = name
61
+ @roles = metadata[:roles]
62
+ @packages = []
63
+ self.instance_eval(&block)
64
+ end
65
+
66
+ def requires(package, options = {})
67
+ @packages << package
68
+ end
69
+
70
+ def to_s; name; end
71
+
72
+ def process(deployment)
73
+ all = []
74
+
75
+ cloud_info "--> Cloud hierarchy for policy #{@name}"
76
+
77
+ @packages.each do |p|
78
+ cloud_info "\nPolicy #{@name} requires package #{p}"
79
+
80
+ package = Sprinkle::Package::PACKAGES[p]
81
+ raise "Package definition not found for key: #{p}" unless package
82
+ package = select_package(p, package) if package.is_a? Array # handle virtual package selection
83
+
84
+ tree = package.tree do |parent, child, depth|
85
+ indent = "\t" * depth; cloud_info "#{indent}Package #{parent.name} requires #{child.name}"
86
+ end
87
+
88
+ all << tree
89
+ end
90
+
91
+ normalize(all) do |package|
92
+ package.process(deployment, @roles)
93
+ end
94
+ end
95
+
96
+ private
97
+
98
+ def cloud_info(message)
99
+ logger.info(message) if Sprinkle::OPTIONS[:cloud] or logger.debug?
100
+ end
101
+
102
+ def select_package(name, packages)
103
+ if packages.size <= 1
104
+ package = packages.first
105
+ else
106
+ package = choose do |menu|
107
+ menu.prompt = "Multiple choices exist for virtual package #{name}"
108
+ menu.choices *packages.collect(&:to_s)
109
+ end
110
+ package = Sprinkle::Package::PACKAGES[package]
111
+ end
112
+
113
+ cloud_info "Selecting #{package.to_s} for virtual package #{name}"
114
+
115
+ package
116
+ end
117
+
118
+ def normalize(all, &block)
119
+ all = all.flatten.uniq
120
+ cloud_info "\n--> Normalized installation order for all packages: #{all.collect(&:name).join(', ')}"
121
+ all.each &block
122
+ end
123
+ end
124
+ end
125
+ end
@@ -0,0 +1,23 @@
1
+ module Sprinkle
2
+ # = Programmatically Run Sprinkle
3
+ #
4
+ # Sprinkle::Script gives you a way to programatically run a given
5
+ # sprinkle script.
6
+ class Script
7
+ # Run a given sprinkle script. This method is <b>blocking</b> so
8
+ # it will not return until the sprinkling is complete or fails.
9
+ #--
10
+ # FIXME: Improve documentation, possibly notify user how to tell
11
+ # if a sprinkling failed.
12
+ #++
13
+ def self.sprinkle(script, filename = '__SCRIPT__')
14
+ powder = new
15
+ powder.instance_eval script, filename
16
+ powder.sprinkle
17
+ end
18
+
19
+ def sprinkle #:nodoc:
20
+ @deployment.process if @deployment
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,16 @@
1
+ module Sprinkle
2
+ module Verifiers
3
+ # = Directory Verifier
4
+ #
5
+ # Defines a verify which can be used to test the existence of a
6
+ # directory.
7
+ module Directory
8
+ Sprinkle::Verify.register(Sprinkle::Verifiers::Directory)
9
+
10
+ # Tests that the directory <tt>dir</tt> exists.
11
+ def has_directory(dir)
12
+ @commands << "test -d #{dir}"
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,36 @@
1
+ module Sprinkle
2
+ module Verifiers
3
+ # = Executable Verifier
4
+ #
5
+ # Contains a verifier to check the existance of an executable
6
+ # script on your server.
7
+ #
8
+ # == Example Usage
9
+ #
10
+ # First, absolute path to an executable:
11
+ #
12
+ # verify { has_executable '/usr/special/secret/bin/scipt' }
13
+ #
14
+ # Second, a global executable which would be available anywhere on the
15
+ # command line:
16
+ #
17
+ # verify { has_executable 'grep' }
18
+ module Executable
19
+ Sprinkle::Verify.register(Sprinkle::Verifiers::Executable)
20
+
21
+ # Checks if <tt>path</tt> is an executable script. This verifier is "smart" because
22
+ # if the path contains a forward slash '/' then it assumes you're checking an
23
+ # absolute path to an executable. If no '/' is in the path, it assumes you're
24
+ # checking for a global executable that would be available anywhere on the command line.
25
+ def has_executable(path)
26
+ # Be smart: If the path includes a forward slash, we're checking
27
+ # an absolute path. Otherwise, we're checking a global executable
28
+ if path.include?('/')
29
+ @commands << "test -x #{path}"
30
+ else
31
+ @commands << "[ -n \"`echo \\`which #{path}\\``\" ]"
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end