lib/sprinkle/package.rb in crafterm-sprinkle-0.1.5 vs lib/sprinkle/package.rb in crafterm-sprinkle-0.1.6

- old
+ new

@@ -1,6 +1,98 @@ module Sprinkle + # = Packages + # + # A package defines one or more things to provision onto the server. + # There is a lot of flexibility in a way a package is defined but + # let me give you a basic example: + # + # package :ruby do + # description 'Ruby MRI' + # version '1.8.6' + # apt 'ruby' + # + # verify { has_executable 'ruby' } + # end + # + # The above would define a package named 'ruby' and give it a description + # and explicitly say its version. It is installed via apt and to verify + # the installation was successful sprinkle will check for the executable + # 'ruby' being availble. Pretty simple, right? + # + # <b>Note:</b> Defining a package does not INSTALL it. To install a + # package, you must require it in a Sprinkle::Policy block. + # + # == Pre-Requirements + # + # Most packages have some sort of pre-requisites in order to be installed. + # Sprinkle allows you to define the requirements of the package, which + # will be installed before the package itself. An example below: + # + # package :rubygems do + # source 'http://rubyforge.org/rubygems.tgz' + # requires :ruby + # end + # + # In this case, when rubygems is being installed, Sprinkle will first + # provision the server with Ruby to make sure the requirements are met. + # In turn, if ruby has requirements, it installs those first, and so on. + # + # == Verifications + # + # Most of the time its important to know whether the software you're + # attempting to install was installed successfully or not. For this, + # Sprinkle provides verifications. Verifications are one or more blocks + # which define rules with which Sprinkle can check if it installed + # the package successfully. If these verification blocks fail, then + # Sprinkle will gracefully stop the entire process. An example below: + # + # package :rubygems do + # source 'http://rubyforge.org/rubygems.tgz' + # requires :ruby + # + # verify { has_executable 'gem' } + # end + # + # In addition to verifying an installation was successfully, by default + # Sprinkle runs these verifications <em>before</em> the installation to + # check if the package is already installed. If the verifications pass + # before installing the package, it skips the package. To override this + # behavior, set the -f flag on the sprinkle script or set the + # :force option to true in Sprinkle::OPTIONS + # + # For more information on verifications and to see all the available + # verifications, see Sprinkle::Verify + # + # == Virtual Packages + # + # Sometimes, there are multiple packages available for a single task. An + # example is a database package. It can contain mySQL, postgres, or sqlite! + # This is where virtual packages come in handy. They are defined as follows: + # + # package :sqlite3, :provides => :database do + # apt 'sqlite3' + # end + # + # The :provides option allows you to reference this package either by :sqlite3 + # or by :database. But whereas the package name is unique, multiple packages may + # share the same provision. If this is the case, when running Sprinkle, the + # script will ask you which provision you want to install. At this time, you + # can only install one. + # + # == Meta-Packages + # + # A package doesn't require an installer. If you want to define a package which + # merely encompasses other packages, that is fine too. Example: + # + # package :meta do + # requires :magic_beans + # requires :magic_sauce + # end + # + #-- + # FIXME: Should probably document recommendations. + #++ module Package PACKAGES = {} def package(name, metadata = {}, &block) package = Package.new(name, metadata, &block) @@ -11,21 +103,22 @@ end package end - class Package + class Package #:nodoc: include ArbitraryOptions - attr_accessor :name, :provides, :installer, :dependencies, :recommends + attr_accessor :name, :provides, :installer, :dependencies, :recommends, :verifications def initialize(name, metadata = {}, &block) raise 'No package name supplied' unless name @name = name @provides = metadata[:provides] @dependencies = [] @recommends = [] + @verifications = [] self.instance_eval &block end def apt(*names, &block) @installer = Sprinkle::Installers::Apt.new(self, *names, &block) @@ -42,15 +135,47 @@ def source(source, options = {}, &block) @recommends << :build_essential # Ubuntu/Debian @installer = Sprinkle::Installers::Source.new(self, source, options, &block) end + + def verify(description = '', &block) + @verifications << Sprinkle::Verify.new(self, description, &block) + end def process(deployment, roles) return if meta_package? + + # Run a pre-test to see if the software is already installed. If so, + # we can skip it, unless we have the force option turned on! + unless @verifications.empty? || Sprinkle::OPTIONS[:force] + begin + process_verifications(deployment, roles, true) + + logger.info "--> #{self.name} already installed for roles: #{roles}" + return + rescue Sprinkle::VerificationFailed => e + # Continue + end + end @installer.defaults(deployment) @installer.process(roles) + + process_verifications(deployment, roles) + end + + def process_verifications(deployment, roles, pre = false) + if pre + logger.info "--> Checking if #{self.name} is already installed for roles: #{roles}" + else + logger.info "--> Verifying #{self.name} was properly installed for roles: #{roles}" + end + + @verifications.each do |v| + v.defaults(deployment) + v.process(roles) + end end def requires(*packages) @dependencies << packages @dependencies.flatten!