jemmyw-sprinkle 0.2.3

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 (105) hide show
  1. data/CREDITS +21 -0
  2. data/History.txt +4 -0
  3. data/MIT-LICENSE +20 -0
  4. data/Manifest.txt +104 -0
  5. data/README.txt +241 -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/actors/actors.rb +17 -0
  31. data/lib/sprinkle/actors/capistrano.rb +147 -0
  32. data/lib/sprinkle/actors/local.rb +50 -0
  33. data/lib/sprinkle/actors/ssh.rb +81 -0
  34. data/lib/sprinkle/actors/vlad.rb +65 -0
  35. data/lib/sprinkle/configurable.rb +31 -0
  36. data/lib/sprinkle/deployment.rb +77 -0
  37. data/lib/sprinkle/extensions/arbitrary_options.rb +10 -0
  38. data/lib/sprinkle/extensions/array.rb +5 -0
  39. data/lib/sprinkle/extensions/blank_slate.rb +5 -0
  40. data/lib/sprinkle/extensions/dsl_accessor.rb +15 -0
  41. data/lib/sprinkle/extensions/string.rb +10 -0
  42. data/lib/sprinkle/extensions/symbol.rb +7 -0
  43. data/lib/sprinkle/installers/apt.rb +52 -0
  44. data/lib/sprinkle/installers/bsd_port.rb +33 -0
  45. data/lib/sprinkle/installers/custom.rb +28 -0
  46. data/lib/sprinkle/installers/deb.rb +38 -0
  47. data/lib/sprinkle/installers/freebsd_pkg.rb +37 -0
  48. data/lib/sprinkle/installers/gem.rb +63 -0
  49. data/lib/sprinkle/installers/installer.rb +120 -0
  50. data/lib/sprinkle/installers/mac_port.rb +38 -0
  51. data/lib/sprinkle/installers/openbsd_pkg.rb +47 -0
  52. data/lib/sprinkle/installers/opensolaris_pkg.rb +43 -0
  53. data/lib/sprinkle/installers/push_text.rb +45 -0
  54. data/lib/sprinkle/installers/rake.rb +37 -0
  55. data/lib/sprinkle/installers/rpm.rb +37 -0
  56. data/lib/sprinkle/installers/source.rb +179 -0
  57. data/lib/sprinkle/installers/yum.rb +37 -0
  58. data/lib/sprinkle/package.rb +293 -0
  59. data/lib/sprinkle/policy.rb +126 -0
  60. data/lib/sprinkle/script.rb +23 -0
  61. data/lib/sprinkle/verifiers/directory.rb +16 -0
  62. data/lib/sprinkle/verifiers/dpkg.rb +14 -0
  63. data/lib/sprinkle/verifiers/executable.rb +36 -0
  64. data/lib/sprinkle/verifiers/file.rb +26 -0
  65. data/lib/sprinkle/verifiers/process.rb +21 -0
  66. data/lib/sprinkle/verifiers/ruby.rb +25 -0
  67. data/lib/sprinkle/verifiers/symlink.rb +30 -0
  68. data/lib/sprinkle/verifiers/user.rb +11 -0
  69. data/lib/sprinkle/verify.rb +114 -0
  70. data/lib/sprinkle/version.rb +9 -0
  71. data/lib/sprinkle.rb +32 -0
  72. data/script/destroy +14 -0
  73. data/script/generate +14 -0
  74. data/spec/spec.opts +1 -0
  75. data/spec/spec_helper.rb +17 -0
  76. data/spec/sprinkle/actors/capistrano_spec.rb +194 -0
  77. data/spec/sprinkle/actors/local_spec.rb +29 -0
  78. data/spec/sprinkle/configurable_spec.rb +46 -0
  79. data/spec/sprinkle/deployment_spec.rb +80 -0
  80. data/spec/sprinkle/extensions/array_spec.rb +19 -0
  81. data/spec/sprinkle/extensions/string_spec.rb +21 -0
  82. data/spec/sprinkle/installers/apt_spec.rb +70 -0
  83. data/spec/sprinkle/installers/bsd_port_spec.rb +42 -0
  84. data/spec/sprinkle/installers/custom_spec.rb +29 -0
  85. data/spec/sprinkle/installers/freebsd_pkg_spec.rb +49 -0
  86. data/spec/sprinkle/installers/gem_spec.rb +91 -0
  87. data/spec/sprinkle/installers/installer_spec.rb +151 -0
  88. data/spec/sprinkle/installers/mac_port_spec.rb +42 -0
  89. data/spec/sprinkle/installers/openbsd_pkg_spec.rb +49 -0
  90. data/spec/sprinkle/installers/opensolaris_pkg_spec.rb +49 -0
  91. data/spec/sprinkle/installers/push_text_spec.rb +55 -0
  92. data/spec/sprinkle/installers/rake_spec.rb +29 -0
  93. data/spec/sprinkle/installers/rpm_spec.rb +50 -0
  94. data/spec/sprinkle/installers/source_spec.rb +331 -0
  95. data/spec/sprinkle/installers/yum_spec.rb +49 -0
  96. data/spec/sprinkle/package_spec.rb +452 -0
  97. data/spec/sprinkle/policy_spec.rb +133 -0
  98. data/spec/sprinkle/script_spec.rb +51 -0
  99. data/spec/sprinkle/sprinkle_spec.rb +25 -0
  100. data/spec/sprinkle/verify_spec.rb +185 -0
  101. data/sprinkle.gemspec +45 -0
  102. data/tasks/deployment.rake +34 -0
  103. data/tasks/environment.rake +7 -0
  104. data/tasks/rspec.rake +21 -0
  105. metadata +199 -0
@@ -0,0 +1,45 @@
1
+ module Sprinkle
2
+ module Installers
3
+ # Beware, strange "installer" coming your way.
4
+ #
5
+ # = Text configuration installer
6
+ #
7
+ # This installer pushes simple configuration into a file.
8
+ #
9
+ # == Example Usage
10
+ #
11
+ # Installing magic_beans into apache2.conf
12
+ #
13
+ # package :magic_beans do
14
+ # push_text 'magic_beans', '/etc/apache2/apache2.conf'
15
+ # end
16
+ #
17
+ # If you user has access to 'sudo' and theres a file that requires
18
+ # priveledges, you can pass :sudo => true
19
+ #
20
+ # package :magic_beans do
21
+ # push_text 'magic_beans', '/etc/apache2/apache2.conf', :sudo => true
22
+ # end
23
+ #
24
+ # A special verify step exists for this very installer
25
+ # its known as file_contains, it will test that a file indeed
26
+ # contains a substring that you send it.
27
+ #
28
+ class PushText < Installer
29
+ attr_accessor :text, :path #:nodoc:
30
+
31
+ def initialize(parent, text, path, options={}, &block) #:nodoc:
32
+ super parent, options, &block
33
+ @text = text
34
+ @path = path
35
+ end
36
+
37
+ protected
38
+
39
+ def install_commands #:nodoc:
40
+ "echo '#{@text}' |#{'sudo' if option?(:sudo)} tee -a #{@path}"
41
+ end
42
+
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,37 @@
1
+ module Sprinkle
2
+ module Installers
3
+ # = Rake Installer
4
+ #
5
+ # This installer runs a rake command.
6
+ #
7
+ # == Example Usage
8
+ #
9
+ # The following example runs the command "rake spec" on
10
+ # the remote server.
11
+ #
12
+ # package :spec do
13
+ # rake 'spec'
14
+ # end
15
+ #
16
+ # Specify a Rakefile with the :rakefile option.
17
+ #
18
+ # package :spec, :rakefile => "/var/setup/Rakefile" do
19
+ # rake 'spec'
20
+ # end
21
+
22
+ class Rake < Installer
23
+ def initialize(parent, commands, options = {}, &block) #:nodoc:
24
+ super parent, options, &block
25
+ @commands = commands.to_a
26
+ end
27
+
28
+ protected
29
+
30
+ def install_commands #:nodoc:
31
+ file = @options[:rakefile] ? "-f #{@options[:rakefile]} " : ""
32
+ "rake #{file}#{@commands.join(' ')}"
33
+ end
34
+
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,37 @@
1
+ module Sprinkle
2
+ module Installers
3
+ # = RPM Package Installer
4
+ #
5
+ # The RPM package installer installs RPM packages.
6
+ #
7
+ # == Example Usage
8
+ #
9
+ # Installing the magic_beans RPM. Its all the craze these days.
10
+ #
11
+ # package :magic_beans do
12
+ # rpm 'magic_beans'
13
+ # end
14
+ #
15
+ # You may also specify multiple rpms as an array:
16
+ #
17
+ # package :magic_beans do
18
+ # rpm %w(magic_beans magic_sauce)
19
+ # end
20
+ class Rpm < 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
+ "rpm -Uvh #{@packages.join(' ')}"
33
+ end
34
+
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,179 @@
1
+ module Sprinkle
2
+ module Installers
3
+ # = Source Package Installer
4
+ #
5
+ # The source package installer installs software from source.
6
+ # It handles downloading, extracting, configuring, building,
7
+ # and installing software.
8
+ #
9
+ # == Configuration Options
10
+ #
11
+ # The source installer has many configuration options:
12
+ # * <b>prefix</b> - The prefix directory that is configured to.
13
+ # * <b>archives</b> - The location all the files are downloaded to.
14
+ # * <b>builds</b> - The directory the package is extracted to to configure and install
15
+ #
16
+ # == Pre/Post Hooks
17
+ #
18
+ # The source installer defines a myriad of new stages which can be hooked into:
19
+ # * <b>prepare</b> - Prepare is the stage which all the prefix, archives, and build directories are made.
20
+ # * <b>download</b> - Download is the stage which the software package is downloaded.
21
+ # * <b>extract</b> - Extract is the stage which the software package is extracted.
22
+ # * <b>configure</b> - Configure is the stage which the ./configure script is run.
23
+ # * <b>build</b> - Build is the stage in which `make` is called.
24
+ # * <b>install</b> - Install is the stage which `make install` is called.
25
+ #
26
+ # == Example Usage
27
+ #
28
+ # First, a simple package, no configuration:
29
+ #
30
+ # package :magic_beans do
31
+ # source 'http://magicbeansland.com/latest-1.1.1.tar.gz'
32
+ # end
33
+ #
34
+ # Second, specifying exactly where I want my files:
35
+ #
36
+ # package :magic_beans do
37
+ # source 'http://magicbeansland.com/latest-1.1.1.tar.gz' do
38
+ # prefix '/usr/local'
39
+ # archives '/tmp'
40
+ # builds '/tmp/builds'
41
+ # end
42
+ # end
43
+ #
44
+ # Third, specifying some hooks:
45
+ #
46
+ # package :magic_beans do
47
+ # source 'http://magicbeansland.com/latest-1.1.1.tar.gz' do
48
+ # prefix '/usr/local'
49
+ #
50
+ # pre :prepare { 'echo "Here we go folks."' }
51
+ # post :extract { 'echo "I believe..."' }
52
+ # pre :build { 'echo "Cross your fingers!"' }
53
+ # end
54
+ # end
55
+ #
56
+ # As you can see, setting options is as simple as creating a
57
+ # block and calling the option as a method with the value as
58
+ # its parameter.
59
+ class Source < Installer
60
+ attr_accessor :source #:nodoc:
61
+
62
+ def initialize(parent, source, options = {}, &block) #:nodoc:
63
+ @source = source
64
+ super parent, options, &block
65
+ end
66
+
67
+ protected
68
+
69
+ def install_sequence #:nodoc:
70
+ prepare + download + extract + configure + build + install
71
+ end
72
+
73
+ %w( prepare download extract configure build install ).each do |stage|
74
+ define_method stage do
75
+ pre_commands(stage.to_sym) + self.send("#{stage}_commands") + post_commands(stage.to_sym)
76
+ end
77
+ end
78
+
79
+ def prepare_commands #:nodoc:
80
+ raise 'No installation area defined' unless @options[:prefix]
81
+ raise 'No build area defined' unless @options[:builds]
82
+ raise 'No source download area defined' unless @options[:archives]
83
+
84
+ [ "mkdir -p #{@options[:prefix]}",
85
+ "mkdir -p #{@options[:builds]}",
86
+ "mkdir -p #{@options[:archives]}" ]
87
+ end
88
+
89
+ def download_commands #:nodoc:
90
+ [ "wget -cq --directory-prefix='#{@options[:archives]}' #{@source}" ]
91
+ end
92
+
93
+ def extract_commands #:nodoc:
94
+ [ "bash -c 'cd #{@options[:builds]} && #{extract_command} #{@options[:archives]}/#{archive_name}'" ]
95
+ end
96
+
97
+ def configure_commands #:nodoc:
98
+ return [] if custom_install?
99
+
100
+ command = "bash -c 'cd #{build_dir} && ./configure --prefix=#{@options[:prefix]} "
101
+
102
+ extras = {
103
+ :enable => '--enable', :disable => '--disable',
104
+ :with => '--with', :without => '--without'
105
+ }
106
+
107
+ extras.inject(command) { |m, (k, v)| m << create_options(k, v) if options[k]; m }
108
+
109
+ [ command << " > #{@package.name}-configure.log 2>&1'" ]
110
+ end
111
+
112
+ def build_commands #:nodoc:
113
+ return [] if custom_install?
114
+ [ "bash -c 'cd #{build_dir} && make > #{@package.name}-build.log 2>&1'" ]
115
+ end
116
+
117
+ def install_commands #:nodoc:
118
+ return custom_install_commands if custom_install?
119
+ [ "bash -c 'cd #{build_dir} && make install > #{@package.name}-install.log 2>&1'" ]
120
+ end
121
+
122
+ def custom_install? #:nodoc:
123
+ !! @options[:custom_install]
124
+ end
125
+
126
+ # REVISIT: must be better processing of custom install commands somehow? use splat operator?
127
+ def custom_install_commands #:nodoc:
128
+ dress @options[:custom_install], :install
129
+ end
130
+
131
+ protected
132
+
133
+ # dress is overriden from the base Sprinkle::Installers::Installer class so that the command changes
134
+ # directory to the build directory first. Also, the result of the command is logged.
135
+ def dress(commands, stage)
136
+ commands.collect { |command| "bash -c 'cd #{build_dir} && #{command} >> #{@package.name}-#{stage}.log 2>&1'" }
137
+ end
138
+
139
+ private
140
+
141
+ def create_options(key, prefix) #:nodoc:
142
+ @options[key].inject(' ') { |m, option| m << "#{prefix}-#{option} "; m }
143
+ end
144
+
145
+ def extract_command #:nodoc:
146
+ case @source
147
+ when /(tar.gz)|(tgz)$/
148
+ 'tar xzf'
149
+ when /(tar.bz2)|(tb2)$/
150
+ 'tar xjf'
151
+ when /tar$/
152
+ 'tar xf'
153
+ when /zip$/
154
+ 'unzip'
155
+ else
156
+ raise "Unknown source archive format: #{archive_name}"
157
+ end
158
+ end
159
+
160
+ def archive_name #:nodoc:
161
+ name = @source.split('/').last
162
+ raise "Unable to determine archive name for source: #{source}, please update code knowledge" unless name
163
+ name
164
+ end
165
+
166
+ def build_dir #:nodoc:
167
+ "#{@options[:builds]}/#{options[:custom_dir] || base_dir}"
168
+ end
169
+
170
+ def base_dir #:nodoc:
171
+ if @source.split('/').last =~ /(.*)\.(tar\.gz|tgz|tar\.bz2|tb2)/
172
+ return $1
173
+ end
174
+ raise "Unknown base path for source archive: #{@source}, please update code knowledge"
175
+ end
176
+
177
+ end
178
+ end
179
+ end
@@ -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 install #{@packages.join(' ')} -y"
33
+ end
34
+
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,293 @@
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, :operations, :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
+ @operations = []
121
+ self.instance_eval(&block)
122
+ end
123
+
124
+ def freebsd_pkg(*names, &block)
125
+ @operations << Sprinkle::Installers::FreebsdPkg.new(self, *names, &block)
126
+ end
127
+
128
+ def openbsd_pkg(*names, &block)
129
+ @operations << Sprinkle::Installers::OpenbsdPkg.new(self, *names, &block)
130
+ end
131
+
132
+ def opensolaris_pkg(*names, &block)
133
+ @operations << Sprinkle::Installers::OpensolarisPkg.new(self, *names, &block)
134
+ end
135
+
136
+ def bsd_port(port, &block)
137
+ @operations << Sprinkle::Installers::BsdPort.new(self, port, &block)
138
+ end
139
+
140
+ def mac_port(port, &block)
141
+ @operations << Sprinkle::Installers::MacPort.new(self, port, &block)
142
+ end
143
+
144
+ def apt(*names, &block)
145
+ @operations << Sprinkle::Installers::Apt.new(self, *names, &block)
146
+ end
147
+
148
+ def deb(*names, &block)
149
+ @operations << Sprinkle::Installers::Deb.new(self, *names, &block)
150
+ end
151
+
152
+ def rpm(*names, &block)
153
+ @operations << Sprinkle::Installers::Rpm.new(self, *names, &block)
154
+ end
155
+
156
+ def yum(*names, &block)
157
+ @operations << Sprinkle::Installers::Yum.new(self, *names, &block)
158
+ end
159
+
160
+ def gem(name, options = {}, &block)
161
+ @recommends << :rubygems
162
+ @operations << Sprinkle::Installers::Gem.new(self, name, options, &block)
163
+ end
164
+
165
+ def source(source, options = {}, &block)
166
+ @recommends << :build_essential # Ubuntu/Debian
167
+ @operations << Sprinkle::Installers::Source.new(self, source, options, &block)
168
+ end
169
+
170
+ def rake(name, options = {}, &block)
171
+ @operations << Sprinkle::Installers::Rake.new(self, name, options, &block)
172
+ end
173
+
174
+ def push_text(text, path, options = {}, &block)
175
+ @installer = Sprinkle::Installers::PushText.new(self, text, path, options, &block)
176
+ end
177
+
178
+ def custom(command, &block)
179
+ @installer = Sprinkle::Installers::Custom.new(self, command, &block)
180
+ end
181
+
182
+ def verify(description = '', &block)
183
+ @verifications << Sprinkle::Verify.new(self, description, &block)
184
+ end
185
+
186
+ def verify(description = '', &block)
187
+ @verifications << Sprinkle::Verify.new(self, description, &block)
188
+ end
189
+
190
+
191
+ def process(deployment, roles)
192
+ return if meta_package?
193
+
194
+ roles = [roles].flatten
195
+ roles.each do |role|
196
+ servers = deployment.find_servers(role)
197
+
198
+ servers.each do |server|
199
+ # Run a pre-test to see if the software is already installed. If so,
200
+ # we can skip it, unless we have the force option turned on!
201
+ unless @verifications.empty? || Sprinkle::OPTIONS[:force]
202
+ begin
203
+ process_verifications(deployment, server, true)
204
+ logger.info "--> #{self.name} already installed for server: #{server}"
205
+ next
206
+ rescue Sprinkle::VerificationFailed => e
207
+ # Continue
208
+ end
209
+ end
210
+
211
+ @operations.each do |installer|
212
+ installer.defaults(deployment) if installer.responds_to?(:defaults)
213
+ installer.process(server) if installer.responds_to?(:process)
214
+ end
215
+
216
+ process_verifications(deployment, server)
217
+ end
218
+ end
219
+ end
220
+
221
+ def process_verifications(deployment, server, pre = false)
222
+ return if @verifications.blank?
223
+
224
+ if pre
225
+ logger.info "--> Checking if #{self.name} is already installed for server: #{server}"
226
+ else
227
+ logger.info "--> Verifying #{self.name} was properly installed for server: #{server}"
228
+ end
229
+
230
+ @verifications.each do |v|
231
+ v.defaults(deployment)
232
+ v.process(server)
233
+ end
234
+ end
235
+
236
+ def requires(*packages)
237
+ @dependencies << packages
238
+ @dependencies.flatten!
239
+ end
240
+
241
+ def recommends(*packages)
242
+ @recommends << packages
243
+ @recommends.flatten!
244
+ end
245
+
246
+ def tree(depth = 1, &block)
247
+ packages = []
248
+
249
+ @recommends.each do |dep|
250
+ package = PACKAGES[dep]
251
+ next unless package # skip missing recommended packages as they can be optional
252
+ block.call(self, package, depth) if block
253
+ packages << package.tree(depth + 1, &block)
254
+ end
255
+
256
+ @dependencies.each do |dep|
257
+ package = PACKAGES[dep]
258
+ package = select_package(dep, package) if package.is_a? Array
259
+
260
+ raise "Package definition not found for key: #{dep}" unless package
261
+ block.call(self, package, depth) if block
262
+ packages << package.tree(depth + 1, &block)
263
+ end
264
+
265
+ packages << self
266
+ end
267
+
268
+ def to_s; @name; end
269
+
270
+ private
271
+
272
+ def select_package(name, packages)
273
+ if packages.size <= 1
274
+ package = packages.first
275
+ else
276
+ package = choose do |menu|
277
+ menu.prompt = "Multiple choices exist for virtual package #{name}"
278
+ menu.choices *packages.collect(&:to_s)
279
+ end
280
+ package = Sprinkle::Package::PACKAGES[package]
281
+ end
282
+
283
+ cloud_info "Selecting #{package.to_s} for virtual package #{name}"
284
+
285
+ package
286
+ end
287
+
288
+ def meta_package?
289
+ @operations.empty?
290
+ end
291
+ end
292
+ end
293
+ end