├── .gitignore ├── .tm_properties ├── .travis.yml ├── CHANGELOG.md ├── CREDITS ├── Gemfile ├── Gemfile.lock ├── MIT-LICENSE ├── README.md ├── Rakefile ├── bin └── sprinkle ├── examples ├── packages │ ├── build_essential.rb │ ├── databases │ │ ├── mysql.rb │ │ ├── mysql_source.rb │ │ └── sqlite3.rb │ ├── phusion.rb │ ├── ruby │ │ ├── rails.rb │ │ ├── ruby.rb │ │ └── rubygems.rb │ ├── scm │ │ ├── git.rb │ │ └── subversion.rb │ └── servers │ │ └── apache.rb ├── rails │ ├── README │ ├── deploy.rb │ ├── packages │ │ ├── database.rb │ │ ├── essential.rb │ │ ├── rails.rb │ │ ├── scm.rb │ │ ├── search.rb │ │ └── server.rb │ ├── rails.rb │ └── templates │ │ └── mysql.cnf.erb └── sprinkle │ └── sprinkle.rb ├── lib ├── sprinkle.rb └── sprinkle │ ├── actors │ ├── actor.rb │ ├── capistrano.rb │ ├── dummy.rb │ ├── local.rb │ ├── ssh.rb │ ├── ssh │ │ └── connection_cache.rb │ └── vlad.rb │ ├── commands │ ├── command.rb │ ├── reconnect.rb │ └── transfer.rb │ ├── core.rb │ ├── deployment.rb │ ├── errors │ ├── pretty_failure.rb │ ├── remote_command_failure.rb │ ├── template_error.rb │ └── transfer_failure.rb │ ├── extensions │ ├── array.rb │ ├── attributes.rb │ ├── blank_slate.rb │ ├── string.rb │ ├── sudo.rb │ └── symbol.rb │ ├── installers │ ├── apt.rb │ ├── binary.rb │ ├── brew.rb │ ├── bsd_port.rb │ ├── deb.rb │ ├── file.rb │ ├── freebsd_pkg.rb │ ├── freebsd_portinstall.rb │ ├── gem.rb │ ├── group.rb │ ├── install_package.rb │ ├── installer.rb │ ├── mac_port.rb │ ├── npm.rb │ ├── openbsd_pkg.rb │ ├── opensolaris_pkg.rb │ ├── package_installer.rb │ ├── pacman.rb │ ├── pear.rb │ ├── pecl.rb │ ├── push_text.rb │ ├── rake.rb │ ├── reconnect.rb │ ├── replace_text.rb │ ├── rpm.rb │ ├── runner.rb │ ├── smart.rb │ ├── source.rb │ ├── thor.rb │ ├── transfer.rb │ ├── user.rb │ ├── yum.rb │ └── zypper.rb │ ├── package.rb │ ├── package │ ├── chooser.rb │ ├── package_repository.rb │ └── rendering.rb │ ├── policy.rb │ ├── script.rb │ ├── utility │ └── log_recorder.rb │ ├── verifiers │ ├── executable.rb │ ├── file.rb │ ├── package.rb │ ├── permission.rb │ ├── process.rb │ ├── ruby.rb │ └── test.rb │ ├── verify.rb │ └── version.rb ├── script ├── console ├── destroy └── generate ├── spec ├── fixtures │ └── my_file.txt ├── spec.opts ├── spec_helper.rb ├── sprinkle │ ├── actors │ │ ├── capistrano_spec.rb │ │ ├── local_spec.rb │ │ └── ssh_spec.rb │ ├── deployment_spec.rb │ ├── extensions │ │ ├── array_spec.rb │ │ ├── rendering_spec.rb │ │ └── string_spec.rb │ ├── installers │ │ ├── apt_spec.rb │ │ ├── binary_spec.rb │ │ ├── brew_spec.rb │ │ ├── bsd_port_spec.rb │ │ ├── file_spec.rb │ │ ├── freebsd_pkg_spec.rb │ │ ├── freebsd_portinstall_spec.rb │ │ ├── gem_spec.rb │ │ ├── installer_spec.rb │ │ ├── mac_port_spec.rb │ │ ├── npm_spec.rb │ │ ├── openbsd_pkg_spec.rb │ │ ├── opensolaris_pkg_spec.rb │ │ ├── pear_spec.rb │ │ ├── pecl_spec.rb │ │ ├── push_text_spec.rb │ │ ├── rake_spec.rb │ │ ├── replace_text_spec.rb │ │ ├── rpm_spec.rb │ │ ├── runner_spec.rb │ │ ├── smart_spec.rb │ │ ├── source_spec.rb │ │ ├── thor_spec.rb │ │ ├── transfer_spec.rb │ │ ├── user_spec.rb │ │ ├── yum_spec.rb │ │ └── zypper_spec.rb │ ├── package │ │ └── package_repository_spec.rb │ ├── package_spec.rb │ ├── policy_spec.rb │ ├── script_spec.rb │ ├── sprinkle_spec.rb │ └── verify_spec.rb └── templates │ ├── locals.erb │ └── test.erb ├── sprinkle.gemspec └── templates └── test.erb /.gitignore: -------------------------------------------------------------------------------- 1 | *.gem 2 | pkg 3 | .DS_Store 4 | .idea 5 | .*.swp 6 | coverage/* 7 | rdoc 8 | .ruby-version 9 | 10 | work 11 | config 12 | tmp 13 | -------------------------------------------------------------------------------- /.tm_properties: -------------------------------------------------------------------------------- 1 | projectDirectory = "$CWD" 2 | 3 | stockFiles = "{Gemfile.lock,test.rb}" 4 | 5 | excludeDirectories = "{rdoc,work}" 6 | # excludeInFolderSearch = "${excludeDirectories}" 7 | excludeFiles = "{*.gem,$stockFiles}" -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: ruby 2 | rvm: 3 | - 2.0.0 4 | - 1.9.3 5 | - 1.9.2 6 | - 1.8.7 7 | - ree 8 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | * Lots of sudo fixes 2 | 3 | *Various people* 4 | 5 | * Default package options (see Package docs) 6 | 7 | *Koen Punt* 8 | 9 | * Add the file installer so we can stop doing templates with `transfer` 10 | 11 | *Josh Goebel* 12 | 13 | * Officially depreciate transfer :render and the ability to render just by passing 14 | a multi-line string as the transfer source. If you want to render templates see the 15 | new `render()` and `template()` (rendering.rb) helpers and the `file` installer. 16 | 17 | *Josh Goebel* 18 | 19 | * A users own post :install hooks should happen after a file has completely been moved 20 | (when using sudo this was not the case) 21 | 22 | *Koen Punt* 23 | 24 | * Remove the Deployment module from Object. 25 | 26 | If anyone is relying on the behavior of placing their deployment block in a required 27 | file then they will first need to manually add the module back to the Object class 28 | themselves. Polluting Object is generally bad. 29 | 30 | *Josh Goebel* 31 | 32 | * Add support for specifying the Net::SSH keys property 33 | 34 | *Chris Kimpton* 35 | 36 | * push_text was escaping & and / when it should not be 37 | 38 | *Stefano Diem Benatti* 39 | 40 | * Sprinkle `sudo_cmd` and Capistrino should work together instead of getting in each others way 41 | 42 | When using the capistrano actor `sudo_cmd` will now use the capistrano 43 | generated sudo command and therefore automatically deal with password 44 | prompts, etc. This should fix hangs when installers try to call `sudo` on 45 | the other side of a pipe operation and capistrano can not recognize the 46 | password prompt. 47 | 48 | * Sprinkle executable should return an error code if there was a failure 49 | 50 | *Michael Nigh* 51 | 52 | * verify of local actor was never returning false so installers would never be executed 53 | 54 | *Edgars Beigarts* 55 | 56 | * Capistrano actor now defaults to loading "Capfile" not "deploy" when no block is given. 57 | If for some reason you just have a 'deploy' file in your root folder you 58 | should `capify .` your setup and move your deploy.rb file into the config 59 | folder. Or you can provide a block with `recipe 'deploy'` to force the 60 | old behavior. 61 | 62 | *Josh Goebel* 63 | 64 | * Capistrano actor now uses the configured setting of `run_method`, instead of always sudo. 65 | The default Capistrano setup prefers sudo, so nothing should change for 66 | most users. If you want to NOT use sudo to run commands you can set 67 | `use_sudo` or `run_method` accordingly in your capistrano recipes: 68 | `set :use_sudo, false` or `set :run_method, :run` 69 | 70 | *Michael Nigh* 71 | -------------------------------------------------------------------------------- /CREDITS: -------------------------------------------------------------------------------- 1 | = CREDITS 2 | 3 | Many thanks to the following people who have submitted ideas, patches, helped with 4 | testing and/or generally provided support to Sprinkle, I really appreciate your help: 5 | 6 | Koen Punt (http://github.com/koenpunt) 7 | Manuel Meurer (http://github.com/manuelmeurer) 8 | Kristin Baumann (http://crafterm.net/kristin/blog/) 9 | Ben Schwarz (http://germanforblack.com/) 10 | Jim Freeze (http://www.artima.com/rubycs/articles/ruby_as_dslP.html) 11 | Matthew Tanase (http://www.slicehost.com) 12 | Jared Kuolt (http://www.slicehost.com) 13 | Jamis Buck (http://www.capify.org) 14 | Matt Allen (http://blog.allen.com.au) 15 | Eric Hodel (http://blog.segment7.net) 16 | Pete Yandell (http://notahat.com) 17 | Adam Meehan (http://duckpunching.com) 18 | Mitchell Hashimoto (http://mitchellhashimoto.com) 19 | Ari Lerner (http://www.citrusbyte.com) 20 | Jorgen Orehøj Erichsen (http://blog.erichsen.net) 21 | Joshua Sierles (http://diluvia.net) 22 | Julian Russell (http://github.com/plusplus) 23 | Dave (Gassto) (http://github.com/gassto) 24 | Bodaniel Jeanes (http://bjeanes.github.com) 25 | Jacob Harris (http://open.nytimes.com) 26 | Justin Pease (http://jit.nuance9.com) 27 | Tobias Lütke (http://blog.leetsoft.com) 28 | Josh Reynolds (http://github.com/jreynolds) 29 | Jan Ulbrich (http://www.ulbrich.net) 30 | Chris Gaffney (http://github.com/gaffneyc) 31 | Maxmpz (http://github.com/maxmpz) 32 | Geoff Garside (http://geoffgarside.co.uk/) 33 | Oliver Kiessler (http://inceedo.com) 34 | Nick Plante (http://blog.zerosum.org) 35 | 36 | The transfer installer contains a piece of exception reporting code copied from 37 | the Chef library (http://wiki.opscode.com/display/chef/Home) 38 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | # to make travis CI happy 2 | #source :rubygems 3 | source "https://rubygems.org" 4 | 5 | gemspec 6 | 7 | group :development do 8 | gem 'railties' 9 | gem 'rdoc', "3.10" 10 | gem 'sdoc' 11 | gem 'pry' 12 | gem 'pry_debug' 13 | gem 'pry-rescue' 14 | end -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | PATH 2 | remote: . 3 | specs: 4 | sprinkle (0.7.6.2) 5 | activesupport (>= 2.0.2) 6 | capistrano (>= 2.5.5, < 3) 7 | erubis (>= 2.7.0) 8 | highline (>= 1.4.0) 9 | open4 (>= 1.1.0) 10 | 11 | GEM 12 | remote: https://rubygems.org/ 13 | specs: 14 | actionpack (3.2.13) 15 | activemodel (= 3.2.13) 16 | activesupport (= 3.2.13) 17 | builder (~> 3.0.0) 18 | erubis (~> 2.7.0) 19 | journey (~> 1.0.4) 20 | rack (~> 1.4.5) 21 | rack-cache (~> 1.2) 22 | rack-test (~> 0.6.1) 23 | sprockets (~> 2.2.1) 24 | activemodel (3.2.13) 25 | activesupport (= 3.2.13) 26 | builder (~> 3.0.0) 27 | activesupport (3.2.13) 28 | i18n (= 0.6.1) 29 | multi_json (~> 1.0) 30 | builder (3.0.4) 31 | capistrano (2.15.5) 32 | highline 33 | net-scp (>= 1.0.0) 34 | net-sftp (>= 2.0.0) 35 | net-ssh (>= 2.0.14) 36 | net-ssh-gateway (>= 1.1.0) 37 | coderay (1.0.9) 38 | diff-lcs (1.2.4) 39 | erubis (2.7.0) 40 | highline (1.6.19) 41 | hike (1.2.3) 42 | i18n (0.6.1) 43 | interception (0.3) 44 | journey (1.0.4) 45 | json (1.8.0) 46 | method_source (0.8.1) 47 | multi_json (1.8.2) 48 | net-scp (1.1.2) 49 | net-ssh (>= 2.6.5) 50 | net-sftp (2.1.2) 51 | net-ssh (>= 2.6.5) 52 | net-ssh (2.7.0) 53 | net-ssh-gateway (1.2.0) 54 | net-ssh (>= 2.6.5) 55 | open4 (1.3.0) 56 | pry (0.9.12.2) 57 | coderay (~> 1.0.5) 58 | method_source (~> 0.8) 59 | slop (~> 3.4) 60 | pry-rescue (1.1.1) 61 | interception (>= 0.3) 62 | pry 63 | pry_debug (0.1.0) 64 | pry (~> 0.9.0) 65 | rack (1.4.5) 66 | rack-cache (1.2) 67 | rack (>= 0.4) 68 | rack-ssl (1.3.3) 69 | rack 70 | rack-test (0.6.2) 71 | rack (>= 1.0) 72 | railties (3.2.13) 73 | actionpack (= 3.2.13) 74 | activesupport (= 3.2.13) 75 | rack-ssl (~> 1.3.2) 76 | rake (>= 0.8.7) 77 | rdoc (~> 3.4) 78 | thor (>= 0.14.6, < 2.0) 79 | rake (10.1.0) 80 | rdoc (3.10) 81 | json (~> 1.4) 82 | rspec (2.14.1) 83 | rspec-core (~> 2.14.0) 84 | rspec-expectations (~> 2.14.0) 85 | rspec-mocks (~> 2.14.0) 86 | rspec-core (2.14.5) 87 | rspec-expectations (2.14.1) 88 | diff-lcs (>= 1.1.3, < 2.0) 89 | rspec-mocks (2.14.3) 90 | sdoc (0.3.20) 91 | json (>= 1.1.3) 92 | rdoc (~> 3.10) 93 | slop (3.4.5) 94 | sprockets (2.2.2) 95 | hike (~> 1.2) 96 | multi_json (~> 1.0) 97 | rack (~> 1.0) 98 | tilt (~> 1.1, != 1.3.0) 99 | thor (0.18.1) 100 | tilt (1.4.1) 101 | 102 | PLATFORMS 103 | ruby 104 | 105 | DEPENDENCIES 106 | pry 107 | pry-rescue 108 | pry_debug 109 | railties 110 | rake (>= 0.8.7) 111 | rdoc (= 3.10) 112 | rspec (>= 2.5) 113 | sdoc 114 | sprinkle! 115 | -------------------------------------------------------------------------------- /MIT-LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2008-2013 Marcus Crafter 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require 'rubygems' 2 | require 'bundler/setup' 3 | 4 | require 'rake' 5 | require 'rspec/core/rake_task' 6 | require 'sdoc' 7 | require 'rdoc/task' 8 | require './lib/sprinkle/version' 9 | 10 | task "inst" => [:clobber, :build] do 11 | puts `gem install pkg/sprinkle-*.gem` 12 | end 13 | 14 | desc 'Default: run specs.' 15 | task :default => :spec 16 | 17 | desc "Run specs" 18 | RSpec::Core::RakeTask.new do |t| 19 | t.pattern = "./spec/**/*_spec.rb" # don't need this, it's default. 20 | # Put spec opts in a file named .rspec in root 21 | end 22 | 23 | desc "Generate code coverage" 24 | RSpec::Core::RakeTask.new(:coverage) do |t| 25 | t.pattern = "./spec/**/*_spec.rb" # don't need this, it's default. 26 | t.rcov = true 27 | t.rcov_opts = ['--exclude', 'spec'] 28 | end 29 | 30 | class RDoc::Comment 31 | def gsub(*args) 32 | @text.gsub(*args) 33 | end 34 | end 35 | 36 | RDoc::Task.new do |rdoc| 37 | version = Sprinkle::Version 38 | 39 | rdoc.options << '-e' << 'UTF-8' 40 | rdoc.options << '-f' << 'sdoc' 41 | # rdoc.options << "-T" << "rails" 42 | 43 | rdoc.rdoc_dir = 'rdoc' 44 | rdoc.title = "sprinkle #{version}" 45 | rdoc.rdoc_files.include('README*') 46 | rdoc.rdoc_files.include('lib/**/*.rb') 47 | end 48 | 49 | STATS_DIRECTORIES = [ 50 | %w(Library lib/sprinkle/), 51 | %w(Specs spec), 52 | ].collect { |name, dir| [ name, "./#{dir}" ] }.select { |name, dir| File.directory?(dir) } 53 | BROKEN = [ 54 | %w(Actors lib/sprinkle/actors), 55 | # %w(Errors lib/sprinkle/errors), 56 | # %w(Extensions lib/sprinkle/extensions), 57 | %w(Installers lib/sprinkle/installers), 58 | # %w(Utility lib/sprinkle/utility), 59 | %w(Package lib/sprinkle/package), 60 | %w(Verifiers lib/sprinkle/verifiers), 61 | ].collect { |name, dir| [ name, "./#{dir}" ] }.select { |name, dir| File.directory?(dir) } 62 | 63 | desc "Report code statistics (KLOCs, etc) from the application" 64 | task :stats do 65 | require 'rails/code_statistics' 66 | CodeStatistics::TEST_TYPES << "Specs" 67 | cs=CodeStatistics.new(*BROKEN) 68 | cs.instance_variable_set("@total",nil) 69 | cs.to_s 70 | CodeStatistics.new(*STATS_DIRECTORIES).to_s 71 | end 72 | -------------------------------------------------------------------------------- /bin/sprinkle: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # 3 | # Created on 2008-3-11. 4 | # Copyright (c) 2008. All rights reserved. 5 | 6 | begin 7 | require 'rubygems' 8 | rescue LoadError 9 | # no rubygems to load, so we fail silently 10 | end 11 | 12 | require 'optparse' 13 | 14 | # NOTE: the option -p/--path= is given as an example, and should probably be replaced in your application. 15 | 16 | OPTIONS = {} 17 | MANDATORY_OPTIONS = %w( path ) 18 | 19 | ARGV.each do |arg| 20 | ENV[$1] = $2 if arg =~ /^(\w+)=(.*)$/ 21 | end 22 | 23 | require File.dirname(__FILE__) + '/../lib/sprinkle/version' 24 | 25 | parser = OptionParser.new do |opts| 26 | opts.version = Sprinkle::Version 27 | opts.banner = < #{File.basename($0)} [options] 44 | 45 | Options are: 46 | BANNER 47 | opts.separator "" 48 | opts.on("-s", "--script=PATH", String, 49 | "path to a sprinkle script to run") { |v| OPTIONS[:path] = v } 50 | opts.on("--only [ROLE]", String, 51 | "only run sprinkle policies for given role") { |v| OPTIONS[:only_role] = v } 52 | opts.on("-t", "--test", 53 | "process but don't perform any actions","(this does not connect to any servers)") { |v| OPTIONS[:testing] = v } 54 | opts.on("-c", "--cloud", 55 | "show powder cloud, package hierarchy","and installation order") { |v| OPTIONS[:cloud] = v } 56 | opts.on("-f", "--force", 57 | "force installation of all packages","by skipping pre-verify checks.") { |v| OPTIONS[:force] = v } 58 | opts.on("-v", "--verbose", 59 | "verbose output") { |v| OPTIONS[:verbose] = v } 60 | opts.on("-h", "--help", 61 | "show this help message") { puts opts; exit } 62 | opts.parse!(ARGV) 63 | 64 | if MANDATORY_OPTIONS && MANDATORY_OPTIONS.find { |option| OPTIONS[option.to_sym].nil? } 65 | puts opts; exit 66 | end 67 | end 68 | 69 | def only_role(options) 70 | role=OPTIONS[:only_role] 71 | return if role.blank? 72 | Sprinkle::OPTIONS[:only_role] = role 73 | puts "Only running policies for :#{role}" 74 | end 75 | 76 | def force_mode(options) 77 | Sprinkle::OPTIONS[:force] = OPTIONS[:force] || false 78 | end 79 | 80 | def operation_mode(options) 81 | Sprinkle::OPTIONS[:testing] = OPTIONS[:testing] || false 82 | end 83 | 84 | def powder_cloud(options) 85 | Sprinkle::OPTIONS[:cloud] = OPTIONS[:cloud] || false 86 | end 87 | 88 | def verbosity(options) 89 | Sprinkle::OPTIONS[:verbose] = OPTIONS[:verbose] || false 90 | end 91 | 92 | def log_level(options) 93 | Object.logger.level = OPTIONS[:verbose] ? Logger::Severity::DEBUG : Logger::Severity::INFO 94 | end 95 | 96 | require File.dirname(__FILE__) + '/../lib/sprinkle' 97 | 98 | powder = OPTIONS[:path] 99 | raise "Sprinkle script is not readable: #{powder}" unless File.readable?(powder) 100 | 101 | force_mode(OPTIONS) 102 | operation_mode(OPTIONS) 103 | powder_cloud(OPTIONS) 104 | log_level(OPTIONS) 105 | verbosity(OPTIONS) 106 | only_role(OPTIONS) 107 | 108 | Sprinkle::Script.sprinkle File.read(powder), powder 109 | -------------------------------------------------------------------------------- /examples/packages/build_essential.rb: -------------------------------------------------------------------------------- 1 | ## Special package, anything that defines a 'source' package means build-essential should be installed for Ubuntu 2 | 3 | package :build_essential do 4 | description 'Build tools' 5 | 6 | apt 'build-essential' do 7 | # Update the sources and upgrade the lists before we build essentials 8 | pre :install, ['aptitude update', 'aptitude safe-upgrade', 'aptitude full-upgrade'] 9 | end 10 | end -------------------------------------------------------------------------------- /examples/packages/databases/mysql.rb: -------------------------------------------------------------------------------- 1 | package :mysql, :provides => :database do 2 | description 'MySQL Database' 3 | 4 | apt %w( mysql-server mysql-client libmysqlclient15-dev ) 5 | end 6 | 7 | package :mysql_ruby_driver do 8 | description 'Ruby MySQL database driver' 9 | 10 | gem 'mysql' # :build_flags => "—with-mysql-config=/usr/local/mysql/bin/mysql_config" 11 | 12 | verify do 13 | ruby_can_load 'mysql' 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /examples/packages/databases/mysql_source.rb: -------------------------------------------------------------------------------- 1 | # Install the latest MySQL database from source 2 | package :mysql do 3 | requires :mysql_dependencies, :mysql_user_group, :mysql_user, :mysql_core 4 | end 5 | 6 | package :mysql_dependencies do 7 | description 'MySQL dependencies' 8 | apt 'cmake' 9 | end 10 | 11 | package :mysql_user_group do 12 | description 'MySQL user group' 13 | group 'mysql' 14 | verify do 15 | has_group 'mysql' 16 | end 17 | end 18 | 19 | package :mysql_user do 20 | description 'MySQL user' 21 | requires :mysql_user_group 22 | runner 'useradd -r -g mysql mysql' 23 | verify do 24 | has_user 'mysql' 25 | end 26 | end 27 | 28 | package :mysql_core do 29 | description 'MySQL database' 30 | version '5.5.25a' 31 | requires :mysql_dependencies, :mysql_user_group, :mysql_user 32 | source "http://dev.mysql.com/get/Downloads/MySQL-5.5/mysql-#{version}.tar.gz/from/http://cdn.mysql.com/" do 33 | custom_archive "mysql-#{version}.tar.gz" 34 | configure_command 'cmake .' 35 | post :install, '/usr/local/mysql/scripts/mysql_install_db --user=mysql --basedir=/usr/local/mysql' 36 | post :install, 'chown -R mysql:mysql /usr/local/mysql/data' 37 | end 38 | verify do 39 | has_executable '/usr/local/mysql/bin/mysql' 40 | end 41 | end 42 | -------------------------------------------------------------------------------- /examples/packages/databases/sqlite3.rb: -------------------------------------------------------------------------------- 1 | # Packages to install sqlite3 and the sqlite3 ruby driver. 2 | package :sqlite3, :provides => :database do 3 | description 'SQLite3 database' 4 | 5 | apt 'sqlite3' 6 | end 7 | 8 | package :sqlite3_ruby_driver do 9 | description 'Ruby SQLite3 library.' 10 | requires :rubygems 11 | 12 | apt 'libsqlite3-dev libsqlite3-ruby1.8' 13 | verify do 14 | ruby_can_load 'sqlite3' 15 | end 16 | end -------------------------------------------------------------------------------- /examples/packages/phusion.rb: -------------------------------------------------------------------------------- 1 | # Contains software created by Phusion.nl which is Ruby Enterprise Edition 2 | # and mod_rails 3 | 4 | package :ruby_enterprise do 5 | description 'Ruby Enterprise Edition' 6 | version '1.8.6-20080810' 7 | requires :apache 8 | requires :passenger 9 | 10 | source 'http://rubyforge.org/frs/download.php/41040/ruby-enterprise-1.8.6-20080810.tar.gz' do 11 | custom_install 'echo -en "\n\n\n\n" | ./installer' 12 | 13 | # Modify the passenger conf file to point to REE 14 | post :install, 'sed -i "s|^PassengerRuby [/a-zA-Z0-9.]*$|PassengerRuby /opt/ruby-enterprise-1.8.6-20080810/bin/ruby|" /etc/apache2/extras/passenger.conf' 15 | 16 | # Restart apache 17 | post :install, '/etc/init.d/apache2 restart' 18 | end 19 | 20 | verify do 21 | has_directory '/opt/ruby-enterprise-1.8.6-20080810' 22 | has_executable '/opt/ruby-enterprise-1.8.6-20080810/bin/ruby' 23 | end 24 | end 25 | 26 | package :passenger, :provides => :appserver do 27 | description 'Phusion Passenger (mod_rails)' 28 | requires :apache 29 | requires :apache2_prefork_dev 30 | 31 | gem 'passenger' do 32 | post :install, 'echo -en "\n\n\n\n" | passenger-install-apache2-module' 33 | 34 | # Create the passenger conf file 35 | post :install, 'mkdir /etc/apache2/extras' 36 | post :install, 'touch /etc/apache2/extras/passenger.conf' 37 | post :install, "echo 'Include /etc/apache2/extras/passenger.conf' >> /etc/apache2/apache2.conf" 38 | 39 | [%q(LoadModule passenger_module /usr/lib/ruby/gems/1.8/gems/passenger-2.0.3/ext/apache2/mod_passenger.so), 40 | %q(PassengerRoot /usr/lib/ruby/gems/1.8/gems/passenger-2.0.3), 41 | %q(PassengerRuby /usr/bin/ruby1.8), 42 | %q(RailsEnv development)].each do |line| 43 | post :install, "echo '#{line}' >> /etc/apache2/extras/passenger.conf" 44 | end 45 | 46 | # Restart apache to note changes 47 | post :install, '/etc/init.d/apache2 restart' 48 | end 49 | 50 | verify do 51 | has_file '/etc/apache2/extras/passenger.conf' 52 | has_file '/usr/lib/ruby/gems/1.8/gems/passenger-2.0.3/ext/apache2/mod_passenger.so' 53 | has_directory '/usr/lib/ruby/gems/1.8/gems/passenger-2.0.3' 54 | end 55 | end -------------------------------------------------------------------------------- /examples/packages/ruby/rails.rb: -------------------------------------------------------------------------------- 1 | package :rails do 2 | description 'Ruby on Rails' 3 | version '3.2' 4 | 5 | gem 'rails' 6 | 7 | verify do 8 | has_executable 'rails' 9 | end 10 | end -------------------------------------------------------------------------------- /examples/packages/ruby/ruby.rb: -------------------------------------------------------------------------------- 1 | package :ruby do 2 | description 'Ruby Virtual Machine' 3 | version '1.8.6' 4 | 5 | apt %q(ruby1.8-dev ruby1.8 ri1.8 rdoc1.8 irb1.8 libreadline-ruby1.8 libruby1.8 libopenssl-ruby) do 6 | post :install, [%q(ln -s /usr/bin/ruby1.8 /usr/bin/ruby), 7 | %q(ln -s /usr/bin/ri1.8 /usr/bin/ri), 8 | %q(ln -s /usr/bin/rdoc1.8 /usr/bin/rdoc), 9 | %q(ln -s /usr/bin/irb1.8 /usr/bin/irb)] 10 | end 11 | 12 | verify 'binaries' do 13 | has_file '/usr/bin/ruby1.8' 14 | has_file '/usr/bin/ri1.8' 15 | has_file '/usr/bin/rdoc1.8' 16 | has_file '/usr/bin/irb1.8' 17 | end 18 | end -------------------------------------------------------------------------------- /examples/packages/ruby/rubygems.rb: -------------------------------------------------------------------------------- 1 | package :rubygems do 2 | description 'Ruby Gems Package Management System' 3 | version '1.8.23' 4 | requires :ruby 5 | 6 | source "http://rubyforge.org/frs/download.php/38646/rubygems-#{version}.tgz" do 7 | custom_install 'ruby setup.rb' 8 | post :install, 'ln -s /usr/bin/gem1.8 /usr/bin/gem' 9 | post :install, 'gem update' 10 | post :install, 'gem update --system' 11 | end 12 | 13 | verify 'binary' do 14 | has_file '/usr/bin/gem1.8' 15 | has_symlink '/usr/bin/gem', '/usr/bin/gem1.8' 16 | end 17 | end -------------------------------------------------------------------------------- /examples/packages/scm/git.rb: -------------------------------------------------------------------------------- 1 | package :git, :provides => :scm do 2 | description 'Git Distributed Version Control' 3 | version '1.5.6.3' 4 | requires :git_dependencies 5 | 6 | source "http://kernel.org/pub/software/scm/git/git-#{version}.tar.gz" 7 | end 8 | 9 | package :git_dependencies do 10 | description 'Git Build Dependencies' 11 | 12 | apt 'git', :dependencies_only => true 13 | end 14 | -------------------------------------------------------------------------------- /examples/packages/scm/subversion.rb: -------------------------------------------------------------------------------- 1 | package :subversion, :provides => :scm do 2 | description 'Subversion Version Control' 3 | 4 | apt 'subversion' 5 | end 6 | -------------------------------------------------------------------------------- /examples/packages/servers/apache.rb: -------------------------------------------------------------------------------- 1 | package :apache, :provides => :webserver do 2 | description 'Apache2 web server.' 3 | 4 | apt 'apache2 apache2.2-common apache2-mpm-prefork apache2-utils libexpat1 ssl-cert' do 5 | post :install, 'a2enmod rewrite' 6 | end 7 | 8 | verify do 9 | has_process 'apache2' 10 | end 11 | end 12 | 13 | package :apache2_prefork_dev do 14 | description 'A dependency required by some packages.' 15 | 16 | apt 'apache2-prefork-dev' 17 | end -------------------------------------------------------------------------------- /examples/rails/README: -------------------------------------------------------------------------------- 1 | = Example Rails Sprinkle Deployment Script 2 | 3 | The following example shows how you can provision Rails and associated packages onto a remote server (or set of servers). 4 | 5 | == Usage: 6 | 7 | $> sprinkle -s rails.rb 8 | 9 | or in test mode: 10 | 11 | $> sprinkle -t -s rails.rb 12 | 13 | == Information 14 | 15 | For more information, please see: http://github.com/crafterm/sprinkle/tree/master/README.markdown -------------------------------------------------------------------------------- /examples/rails/deploy.rb: -------------------------------------------------------------------------------- 1 | set :user, 'root' 2 | role :app, 'yourhost.com', :primary => true 3 | -------------------------------------------------------------------------------- /examples/rails/packages/database.rb: -------------------------------------------------------------------------------- 1 | package :mysql, :provides => :database do 2 | description 'MySQL Database' 3 | 4 | defaults :innodb_file_per_table => true, 5 | :innodb_buffer_pool_size => "512MB" 6 | 7 | file "/etc/my.cnf", 8 | :contents => render("mysql.cnf"), 9 | :sudo => true 10 | 11 | apt %w( mysql-server mysql-client ) 12 | end 13 | 14 | package :ruby_mysql_driver do 15 | description 'Ruby MySQL database driver' 16 | requires :mysql 17 | 18 | gem 'mysql2' 19 | end 20 | -------------------------------------------------------------------------------- /examples/rails/packages/essential.rb: -------------------------------------------------------------------------------- 1 | ## Special package, anything that defines a 'source' package means build-essential should be installed for Ubuntu 2 | 3 | package :build_essential do 4 | description 'Build tools' 5 | 6 | apt 'build-essential' do 7 | # Update the sources and upgrade the lists before we build essentials 8 | pre :install, 'apt-get update' 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /examples/rails/packages/rails.rb: -------------------------------------------------------------------------------- 1 | package :ruby_dependencies do 2 | description 'Ruby Virtual Machine Build Dependencies' 3 | 4 | apt %w( bison zlib1g-dev libssl-dev libreadline5-dev libncurses5-dev file ) 5 | end 6 | 7 | package :ruby do 8 | description 'Ruby Virtual Machine' 9 | version '1.8.6' 10 | patchlevel = '369' 11 | requires :ruby_dependencies 12 | 13 | source "ftp://ftp.ruby-lang.org/pub/ruby/1.8/ruby-#{version}-p#{patchlevel}.tar.gz" # implicit :style => :gnu 14 | end 15 | 16 | package :rubygems do 17 | description 'Ruby Gems Package Management System' 18 | version '1.8.23' 19 | requires :ruby 20 | 21 | source "http://rubyforge.org/frs/download.php/60718/rubygems-#{version}.tgz" do 22 | custom_install 'ruby setup.rb' 23 | end 24 | end 25 | 26 | package :rails do 27 | description 'Ruby on Rails' 28 | version '3.2' 29 | 30 | gem 'rails' 31 | end 32 | -------------------------------------------------------------------------------- /examples/rails/packages/scm.rb: -------------------------------------------------------------------------------- 1 | package :git_dependencies do 2 | description 'Git Build Dependencies' 3 | 4 | apt 'git', :dependencies_only => true 5 | end 6 | 7 | package :git, :provides => :scm do 8 | description 'Git Distributed Version Control' 9 | version '1.6.3.3' 10 | requires :git_dependencies 11 | 12 | source "http://kernel.org/pub/software/scm/git/git-#{version}.tar.gz" 13 | end -------------------------------------------------------------------------------- /examples/rails/packages/search.rb: -------------------------------------------------------------------------------- 1 | package :sphinx, :provides => :search do 2 | description 'MySQL full text search engine' 3 | version '0.9.8.1' 4 | requires :mysql_dev 5 | 6 | source "http://www.sphinxsearch.com/downloads/sphinx-#{version}.tar.gz" 7 | end 8 | 9 | package :mysql_dev do 10 | description 'MySQL Database development package' 11 | 12 | apt %w( libmysqlclient15-dev ) 13 | end 14 | -------------------------------------------------------------------------------- /examples/rails/packages/server.rb: -------------------------------------------------------------------------------- 1 | package :mongrel do 2 | description 'Mongrel Application Server' 3 | version '1.1.5' 4 | 5 | gem 'mongrel' 6 | end 7 | 8 | package :mongrel_cluster, :provides => :appserver do 9 | description 'Cluster Management for Mongrel' 10 | version '1.0.5' 11 | requires :mongrel 12 | 13 | gem 'mongrel_cluster' 14 | end 15 | 16 | package :apache, :provides => :webserver do 17 | description 'Apache 2 HTTP Server' 18 | version '2.2.11' 19 | requires :apache_dependencies 20 | 21 | source "http://www.apache.org/dist/httpd/httpd-#{version}.tar.bz2" do 22 | enable %w( mods-shared=all proxy proxy-balancer proxy-http rewrite cache headers ssl deflate so ) 23 | prefix "/opt/local/apache2-#{version}" 24 | post :install, 'install -m 755 support/apachectl /etc/init.d/apache2', 'update-rc.d -f apache2 defaults' 25 | end 26 | end 27 | 28 | package :apache_dependencies do 29 | description 'Apache 2 HTTP Server Build Dependencies' 30 | 31 | apt %w( openssl libtool mawk zlib1g-dev libssl-dev ) 32 | end 33 | -------------------------------------------------------------------------------- /examples/rails/rails.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sprinkle -s 2 | 3 | # Annotated Example Sprinkle Rails deployment script 4 | # 5 | # This is an example Sprinkle script configured to install Rails from gems, Apache, Ruby, 6 | # Sphinx and Git from source, and mysql and Git dependencies from apt on an Ubuntu system. 7 | # 8 | # Installation is configured to run via capistrano (and an accompanying deploy.rb recipe script). 9 | # Source based packages are downloaded and built into /usr/local on the remote system. 10 | # 11 | # A sprinkle script is separated into 3 different sections. Packages, policies and deployment: 12 | 13 | 14 | # Packages (separate files for brevity) 15 | # 16 | # Defines the world of packages as we know it. Each package has a name and 17 | # set of metadata including its installer type (eg. apt, source, gem, etc). Packages can have 18 | # relationships to each other via dependencies. 19 | 20 | require './packages/essential' 21 | require './packages/rails' 22 | require './packages/database' 23 | require './packages/server' 24 | require './packages/search' 25 | require './packages/scm' 26 | 27 | 28 | # Policies 29 | # 30 | # Names a group of packages (optionally with versions) that apply to a particular set of roles: 31 | # 32 | # Associates the rails policy to the application servers. Contains rails, and surrounding 33 | # packages. Note, appserver, database, webserver and search are all virtual packages defined above. 34 | # If there's only one implementation of a virtual package, it's selected automatically, otherwise 35 | # the user is requested to select which one to use. 36 | 37 | policy :rails, :roles => :app do 38 | requires :rails 39 | requires :appserver 40 | requires :database 41 | requires :webserver 42 | requires :search 43 | requires :scm 44 | end 45 | 46 | 47 | # Deployment 48 | # 49 | # Defines script wide settings such as a delivery mechanism for executing commands on the target 50 | # system (eg. capistrano), and installer defaults (eg. build locations, etc): 51 | # 52 | # Configures spinkle to use capistrano for delivery of commands to the remote machines (via 53 | # the named 'deploy' recipe). Also configures 'source' installer defaults to put package gear 54 | # in /usr/local 55 | 56 | deployment do 57 | 58 | # mechanism for deployment 59 | # delivery :capistrano do 60 | # recipes 'deploy' 61 | # end 62 | delivery :dummy do 63 | role :app, "test" 64 | end 65 | 66 | # source based package installer defaults 67 | source do 68 | prefix '/usr/local' 69 | archives '/usr/local/sources' 70 | builds '/usr/local/build' 71 | end 72 | 73 | end 74 | 75 | # End of script, given the above information, Spinkle will apply the defined policy on all roles using the 76 | # deployment settings specified. 77 | -------------------------------------------------------------------------------- /examples/rails/templates/mysql.cnf.erb: -------------------------------------------------------------------------------- 1 | [mysqld] 2 | <%= "innodb_file_per_table" if opts[:innodb_file_per_table] %> 3 | innodb_buffer_pool_size = <%= opts[:innodb_buffer_pool_size] %> -------------------------------------------------------------------------------- /examples/sprinkle/sprinkle.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sprinkle -c -s 2 | 3 | # Example of the simplest Sprinkle script to install a single gem on a remote host. This 4 | # particular script assumes that rubygems (and ruby, etc) are already installed on the remote 5 | # host. To see a larger example of installing an entire ruby, rubygems, gem stack from source, 6 | # please see the rails example. 7 | 8 | # Packages, only sprinkle is defined in this world 9 | 10 | package :sprinkle do 11 | description 'Sprinkle Provisioning Tool' 12 | gem 'crafterm-sprinkle' do 13 | source 'http://gems.github.com' # use alternate gem server 14 | #repository '/opt/local/gems' # specify an alternate local gem repository 15 | end 16 | end 17 | 18 | 19 | # Policies, sprinkle policy requires only the sprinkle gem 20 | 21 | policy :sprinkle, :roles => :app do 22 | requires :sprinkle 23 | end 24 | 25 | 26 | # Deployment settings 27 | 28 | deployment do 29 | 30 | # use vlad for deployment 31 | delivery :vlad do 32 | role :app, 'yourhost.com' 33 | end 34 | 35 | end 36 | 37 | # End of script, given the above information, Spinkle will apply the defined policy on all roles using the 38 | # deployment settings specified. 39 | -------------------------------------------------------------------------------- /lib/sprinkle.rb: -------------------------------------------------------------------------------- 1 | require 'rubygems' 2 | require 'active_support/all' 3 | 4 | if ActiveSupport::VERSION::MAJOR > 2 5 | require 'active_support/dependencies' 6 | ActiveSupport::Dependencies.autoload_paths << File.dirname(__FILE__) 7 | else 8 | ActiveSupport::Dependencies.load_paths << File.dirname(__FILE__) 9 | end 10 | 11 | # Configure active support to log auto-loading of dependencies 12 | #ActiveSupport::Dependencies::RAILS_DEFAULT_LOGGER = Logger.new($stdout) 13 | #ActiveSupport::Dependencies.log_activity = true 14 | 15 | def require_all(*args) # :nodoc: 16 | args.each { |f| 17 | Dir[File.dirname(__FILE__) + "/sprinkle/#{f}"].each { |e| require e } } 18 | end 19 | 20 | $LOAD_PATH << File.dirname(__FILE__) 21 | 22 | require 'sprinkle/version' 23 | 24 | require_all "extensions/*.rb" 25 | 26 | require 'sprinkle/package' 27 | require 'sprinkle/verify' 28 | require 'sprinkle/installers/installer' 29 | require 'sprinkle/installers/package_installer' 30 | require_all "verifiers/*.rb", "installers/*.rb" 31 | 32 | module Sprinkle 33 | # Configuration options 34 | OPTIONS = { :testing => false, :verbose => false, :force => false } 35 | 36 | module Logger 37 | def logger # :nodoc: 38 | # ActiveSupport::BufferedLogger was deprecated and replaced by ActiveSupport::Logger in Rails 4. 39 | # Use ActiveSupport::Logger if available. 40 | active_support_logger = defined?(ActiveSupport::Logger) ? ActiveSupport::Logger : ActiveSupport::BufferedLogger 41 | @@__log__ ||= active_support_logger.new($stdout, active_support_logger::Severity::INFO) 42 | end 43 | end 44 | end 45 | 46 | # Object is extended with a few helper methods. Please see Sprinkle::Core. 47 | #-- 48 | # Define a logging target and understand packages, policies and deployment DSL 49 | #++ 50 | class Object 51 | include Sprinkle::Package, Sprinkle::Core, Sprinkle::Logger 52 | end 53 | -------------------------------------------------------------------------------- /lib/sprinkle/actors/actor.rb: -------------------------------------------------------------------------------- 1 | module Sprinkle 2 | # = Actors 3 | # 4 | # An actor is a method of command delivery to a remote machine. Actors are the 5 | # layer setting between Sprinkle and the systems you and wanting to apply 6 | # policies to. 7 | # 8 | # Sprinkle ships with actors for Capistrano, Vlad, localhost and pure SSH. 9 | module Actors 10 | # == Writing an actor 11 | # 12 | # All actors should inherit from Sprinkle::Actors::Actor. Actors must provide the 13 | # following methods: 14 | # 15 | # * #install (installer, roles, options) 16 | # * #verify (verifier, roles, options) 17 | # * #servers_for_role? (roles) 18 | # * #sudo? 19 | # * #sudo_command 20 | # 21 | # Hopefully these methods are kind of fairly obvious. They should return true 22 | # to indicate success and false to indicate failure. 23 | # The actual commands you need to execute can be retrived from 24 | # +installer.install_sequence+ and +verifier.commands+. 25 | class Actor 26 | 27 | # an actor must define this method so that each policy can ask the actor 28 | # if there are any servers with that policy's roles so the policy knows 29 | # whether it should execute or not 30 | # 31 | # input: a single role or array of roles 32 | def servers_for_role?(role) 33 | raise "please define servers_for_role?" 34 | end 35 | 36 | def install(installer, roles, options) 37 | raise "you must define install" 38 | end 39 | 40 | # an actor must define this and let the installers know if it plans 41 | # to try and add sudo to all of their commands or not since some 42 | # installers might need to handle sudo their own special way 43 | def sudo? 44 | raise "you must define sudo?" 45 | end 46 | 47 | # if an installer needs to call sudo this is the command the actor 48 | # would prefer the installers to use 49 | def sudo_command 50 | raise "you must define sudo_command" 51 | end 52 | 53 | def verify(verifier, roles, options) 54 | raise "you must define verify" 55 | end 56 | 57 | end 58 | end 59 | end -------------------------------------------------------------------------------- /lib/sprinkle/actors/capistrano.rb: -------------------------------------------------------------------------------- 1 | require 'capistrano/cli' 2 | 3 | module Sprinkle 4 | module Actors 5 | # The Capistrano actor uses Capistrano to define your roles and deliver 6 | # commands to your remote servers. You'll need the capistrano gem installed. 7 | # 8 | # The only configuration option is to specify a recipe. 9 | # 10 | # deployment do 11 | # delivery :capistrano do 12 | # recipe 'deploy' 13 | # recipe 'more' 14 | # end 15 | # end 16 | # 17 | # Recipes is given a list of files which capistrano will include and load. 18 | # These recipes are mainly to set variables such as :user, :password, and to 19 | # set the app domain which will be sprinkled. 20 | class Capistrano < Actor 21 | attr_accessor :config, :loaded_recipes #:nodoc: 22 | 23 | def initialize(&block) #:nodoc: 24 | @installer = nil 25 | @config = ::Capistrano::Configuration.new 26 | @config.logger.level = Sprinkle::OPTIONS[:verbose] ? ::Capistrano::Logger::INFO : ::Capistrano::Logger::IMPORTANT 27 | @config.set(:password) { ::Capistrano::CLI.password_prompt } 28 | # default sudo to false, we must turn it on 29 | @config.set(:run_method) { @config.fetch(:use_sudo, false) ? :sudo : :run } 30 | 31 | @config.set(:_sprinkle_actor, self) 32 | 33 | def @config.recipes(script) 34 | _sprinkle_actor.recipes(script) 35 | end 36 | 37 | if block 38 | @config.instance_eval(&block) 39 | else 40 | @config.load "Capfile" if File.exist?("Capfile") 41 | end 42 | end 43 | 44 | def sudo? #:nodoc: 45 | @config.fetch(:run_method) == :sudo 46 | end 47 | 48 | def sudo_command #:nodoc: 49 | @config.sudo 50 | end 51 | 52 | # Determines if there are any servers for the given roles 53 | def servers_for_role?(roles) #:nodoc: 54 | roles=Array(roles) 55 | roles.any? { |r| @config.roles.keys.include?(r) } 56 | end 57 | 58 | 59 | # Defines a recipe file which will be included by capistrano. Use these 60 | # recipe files to set capistrano specific configurations. Default recipe 61 | # included is "deploy." But if any other recipe is specified, it will 62 | # include that instead. Multiple recipes may be specified through multiple 63 | # recipes calls, an example: 64 | # 65 | # deployment do 66 | # delivery :capistrano do 67 | # recipe 'deploy' 68 | # recipes 'magic_beans', 'normal_beans' 69 | # end 70 | # end 71 | def recipe(scripts) 72 | @loaded_recipes ||= [] 73 | Array(scripts).each do |script| 74 | @config.load script 75 | @loaded_recipes << script 76 | end 77 | end 78 | 79 | def recipes(scripts) #:nodoc: 80 | recipe(scripts) 81 | end 82 | 83 | def install(installer, roles, opts = {}) #:nodoc: 84 | @installer = installer 85 | process(installer.package.name, installer.install_sequence, roles, opts) 86 | rescue ::Capistrano::CommandError => e 87 | raise_error(e) 88 | ensure 89 | @installer = nil 90 | end 91 | 92 | def verify(verifier, roles, opts = {}) #:nodoc: 93 | process(verifier.package.name, verifier.commands, roles) 94 | rescue ::Capistrano::CommandError 95 | return false 96 | end 97 | 98 | def process(name, commands, roles, opts = {}) #:nodoc: 99 | inst=@installer 100 | @log_recorder = log_recorder = Sprinkle::Utility::LogRecorder.new 101 | commands = commands.map {|x| rewrite_command(x)} 102 | define_task(name, roles) do 103 | via = fetch(:run_method) 104 | commands.each do |command| 105 | if command.is_a? Commands::Transfer 106 | upload command.source, command.destination, :via => :scp, 107 | :recursive => command.recursive? 108 | elsif command.is_a? Commands::Reconnect 109 | teardown_connections_to(sessions.keys) 110 | else 111 | # this reset the log 112 | log_recorder.reset command 113 | invoke_command(command, {:via => via}) do |ch, stream, out| 114 | ::Capistrano::Configuration.default_io_proc.call(ch, stream, out) if Sprinkle::OPTIONS[:verbose] 115 | log_recorder.log(stream, out) 116 | end 117 | end 118 | end 119 | end 120 | run_task(name, opts) 121 | end 122 | 123 | private 124 | 125 | # rip out any double sudos from the beginning of the command 126 | def rewrite_command(cmd) 127 | return cmd if cmd.is_a?(Symbol) 128 | via = @config.fetch(:run_method) 129 | if via == :sudo and cmd =~ /^#{sudo_command}/ 130 | cmd.gsub(/^#{sudo_command}\s?/,"") 131 | else 132 | cmd 133 | end 134 | end 135 | 136 | def raise_error(e) 137 | details={:command => @log_recorder.command, :code => "??", 138 | :message => e.message, 139 | :hosts => e.hosts, 140 | :error => @log_recorder.err, :stdout => @log_recorder.out} 141 | raise Sprinkle::Errors::RemoteCommandFailure.new(@installer, details, e) 142 | end 143 | 144 | def run_task(task, opts={}) 145 | run(task) 146 | true 147 | end 148 | 149 | # REVISIT: can we set the description somehow? 150 | def define_task(name, roles, &block) 151 | @config.task task_sym(name), :roles => roles, &block 152 | end 153 | 154 | def run(task) 155 | @config.send task_sym(task) 156 | end 157 | 158 | def task_sym(name) 159 | "install_#{name.to_task_name}".to_sym 160 | end 161 | end 162 | end 163 | end 164 | 165 | 166 | =begin 167 | 168 | # channel: the SSH channel object used for this response 169 | # stream: either :err or :out, for stderr or stdout responses 170 | # output: the text that the server is sending, might be in chunks 171 | run "apt-get update" do |channel, stream, output| 172 | if output =~ /Are you sure?/ 173 | answer = Capistrano::CLI.ui.ask("Are you sure: ") 174 | channel.send_data(answer + "\n") 175 | else 176 | # allow the default callback to be processed 177 | Capistrano::Configuration.default_io_proc.call[channel, stream, output] 178 | end 179 | end 180 | 181 | 182 | 183 | You can tell subversion to use a different username+password by 184 | setting a couple variables: 185 | set :svn_username, "my svn username" 186 | set :svn_password, "my svn password" 187 | If you don't want to set the password explicitly in your recipe like 188 | that, you can make capistrano prompt you for it like this: 189 | set(:svn_password) { Capistrano::CLI.password_prompt("Subversion 190 | password: ") } 191 | - Jamis 192 | =end 193 | -------------------------------------------------------------------------------- /lib/sprinkle/actors/dummy.rb: -------------------------------------------------------------------------------- 1 | require 'capistrano/cli' 2 | require 'pp' 3 | 4 | module Sprinkle 5 | module Actors 6 | class Dummy < Actor #:nodoc: 7 | 8 | attr_accessor :per_host 9 | 10 | def initialize(&block) #:nodoc: 11 | # @config.set(:_sprinkle_actor, self) 12 | @roles={} 13 | self.instance_eval(&block) 14 | end 15 | 16 | def role(role, server, opts={}) 17 | @roles[role]||=[] 18 | @roles[role] << [ server, opts ] 19 | end 20 | 21 | # Determines if there are any servers for the given roles 22 | def servers_for_role?(roles) 23 | roles=Array(roles) 24 | roles.any? { |r| @roles.keys.include?(r) } 25 | end 26 | 27 | def install(installer, roles, opts={}) 28 | @installer = installer 29 | if self.per_host=opts.delete(:per_host) 30 | servers_per_role(roles).each do |server| 31 | installer.reconfigure_for(server) 32 | installer.announce 33 | process(installer.package.name, installer.install_sequence, server, opts) 34 | end 35 | else 36 | process(installer.package, installer.install_sequence, roles, opts) 37 | end 38 | end 39 | 40 | def sudo? 41 | false 42 | end 43 | 44 | def verify(verifier, roles, opts = {}) 45 | process(verifier.package.name, verifier.commands, roles, opts = {}) 46 | end 47 | 48 | def servers_per_role(role) 49 | @roles[role] 50 | end 51 | 52 | def print_command(cmd) 53 | puts cmd.inspect 54 | end 55 | 56 | def process(name, commands, roles, opts = {}) #:nodoc: 57 | # puts "PROCESS: #{name} on #{roles}" 58 | commands.each do |cmd| 59 | print_command(cmd) 60 | end 61 | # return false if suppress_and_return_failures 62 | true 63 | end 64 | end 65 | end 66 | end 67 | -------------------------------------------------------------------------------- /lib/sprinkle/actors/local.rb: -------------------------------------------------------------------------------- 1 | require 'open4' 2 | 3 | module Sprinkle 4 | module Actors 5 | # The local actor executes all commands on your local system, as opposed to other 6 | # implementations that generally run commands on a remote system over the 7 | # network. 8 | # 9 | # This could be useful if you'd like to use Sprinkle to provision your 10 | # local machine. To enable this actor, in your Sprinkle script specify 11 | # the :local delivery mechanism. 12 | # 13 | # deployment do 14 | # delivery :local 15 | # end 16 | # 17 | # Note: The local actor completely ignores roles and behaves as if your 18 | # local system was a member of all roles defined. 19 | class Local < Actor 20 | 21 | 22 | class LocalCommandError < StandardError #:nodoc: 23 | end 24 | 25 | def servers_for_role?(role) #:nodoc: 26 | true 27 | end 28 | 29 | def sudo? #:nodoc:; 30 | false; end 31 | def sudo_command #:nodoc:; 32 | nil; end 33 | 34 | def install(installer, roles, opts = {}) #:nodoc: 35 | # all local installer cares about is the commands 36 | @installer = installer 37 | process(installer.package.name, installer.install_sequence, roles) 38 | rescue LocalCommandError => e 39 | raise_error(e) 40 | ensure 41 | @installer = nil 42 | end 43 | 44 | def verify(verifier, roles, opts = {}) #:nodoc: 45 | process(verifier.package.name, verifier.commands, roles) 46 | true 47 | rescue LocalCommandError 48 | false 49 | end 50 | 51 | protected 52 | 53 | def process(name, commands, roles, opts = {}) #:nodoc: 54 | @log_recorder = Sprinkle::Utility::LogRecorder.new 55 | commands.each do |command| 56 | if command.is_a?(Commands::Reconnect) 57 | res = 0 58 | elsif command.is_a?(Commands::Transfer) 59 | res = transfer(command.source, command.destination, roles, 60 | :recursive => command.recursive?) 61 | else 62 | res = run_command command 63 | end 64 | raise LocalCommandError if res != 0 65 | end 66 | return true 67 | end 68 | 69 | def run_command(cmd) #:nodoc: 70 | @log_recorder.reset cmd 71 | pid, _, out, err = Open4.popen4(cmd) 72 | _, status = Process::waitpid2 pid 73 | @log_recorder.log :err, err.read 74 | @log_recorder.log :out, out.read 75 | @log_recorder.code = status.to_i 76 | end 77 | 78 | def raise_error(e) #:nodoc: 79 | raise Sprinkle::Errors::RemoteCommandFailure.new(@installer, @log_recorder.hash, e) 80 | end 81 | 82 | def transfer(source, destination, roles, opts ={}) #:nodoc: 83 | opts.reverse_merge!(:recursive => true) 84 | flags = "-R " if opts[:recursive] 85 | 86 | run_command "cp #{flags}#{source} #{destination}" 87 | end 88 | end 89 | end 90 | end 91 | -------------------------------------------------------------------------------- /lib/sprinkle/actors/ssh/connection_cache.rb: -------------------------------------------------------------------------------- 1 | module Sprinkle 2 | module Actors 3 | class SSHConnectionCache #:nodoc: 4 | def initialize(options={}) 5 | @cache = {} 6 | @gateway = Net::SSH::Gateway.new(options[:gateway], options[:user]) if options[:gateway] 7 | end 8 | def start(host, user, opts={}) 9 | key="#{host}/#{user}#{opts.to_s}" 10 | if @gateway 11 | @cache[key] ||= @gateway.ssh(host, user, opts) 12 | else 13 | @cache[key] ||= Net::SSH.start(host, user, opts) 14 | end 15 | end 16 | def reconnect(host) 17 | @cache.delete_if do |k,v| 18 | (v.close; true) if k =~ /^#{host}\// 19 | end 20 | end 21 | def shutdown! 22 | @gateway.shutdown! if @gateway 23 | end 24 | end 25 | end 26 | end -------------------------------------------------------------------------------- /lib/sprinkle/actors/vlad.rb: -------------------------------------------------------------------------------- 1 | require 'vlad' 2 | 3 | module Sprinkle 4 | module Actors 5 | # The Vlad actor is one of the delivery method options available out of the 6 | # box with Sprinkle. If you have the vlad the deployer gem installed, you 7 | # may use this delivery. The only configuration option available, and 8 | # which is mandatory to include is +script+. An example: 9 | # 10 | # deployment do 11 | # delivery :vlad do 12 | # script 'deploy' 13 | # end 14 | # end 15 | # 16 | # script is given a list of files which vlad will include and load. 17 | # These recipes are mainly to set variables such as :user, :password, and to 18 | # set the app domain which will be sprinkled. 19 | class Vlad < Actor 20 | attr_accessor :loaded_recipes #:nodoc: 21 | 22 | def initialize(&block) #:nodoc: 23 | self.instance_eval &block if block 24 | end 25 | 26 | def servers_for_role? #:nodoc: 27 | raise "The vlad actor needs a maintainer. "+ 28 | "Please file an issue on github.com/sprinkle-tool/sprinkle if you can help." 29 | end 30 | 31 | def sudo? #:nodoc: 32 | # TODO 33 | raise 34 | end 35 | 36 | def sudo_command #:nodoc: 37 | "sudo" 38 | end 39 | 40 | # Defines a script file which will be included by vlad. Use these 41 | # script files to set vlad specific configurations. Multiple scripts 42 | # may be specified through multiple script calls, an example: 43 | # 44 | # deployment do 45 | # delivery :vlad do 46 | # script 'deploy' 47 | # script 'magic_beans' 48 | # end 49 | # end 50 | def script(name) 51 | @loaded_recipes ||= [] 52 | require name 53 | @loaded_recipes << name 54 | end 55 | 56 | def install(installer, roles, opts={}) #:nodoc: 57 | @installer=installer 58 | if installer.install_sequence.include?(:TRANSFER) 59 | process_with_transfer(installer.package.name, installer.install_sequence, roles, opts) 60 | else 61 | process(installer.package.name, installer.install_sequence, roles, opts) 62 | end 63 | # recast our rake error to the common sprinkle error type 64 | rescue ::Rake::CommandFailedError => e 65 | raise Sprinkle::Errors::RemoteCommandFailure.new(installer, {}, e) 66 | ensure 67 | @installer = nil 68 | end 69 | 70 | def verify(verifier, roles, opts={}) #:nodoc: 71 | process(verifier.package.name, commands, roles, 72 | :suppress_and_return_failures => true) 73 | end 74 | 75 | protected 76 | 77 | def process(name, commands, roles, opts ={}) #:nodoc: 78 | commands = commands.map{|x| "#{sudo_command} #{x}"} if sudo? 79 | commands = commands.join(' && ') 80 | puts "executing #{commands}" 81 | task = remote_task(task_sym(name), :roles => roles) { run commands } 82 | invoke(task) 83 | end 84 | 85 | def process_with_transfer(name, commands, roles, opts ={}) #:nodoc: 86 | raise "cant do non recursive file transfers, sorry" if opts[:recursive] == false 87 | commands = commands.map{|x| x == :TRANSFER ? x : "sudo #{x}" } if sudo? 88 | i = commands.index(:TRANSFER) 89 | before = commands.first(i).join(" && ") 90 | after = commands.last(commands.size-i+1).join(" && ") 91 | inst = @installer 92 | task = remote_task(task_sym(name), :roles => roles) do 93 | run before unless before.empty? 94 | rsync inst.sourcepath, inst.destination 95 | run after unless after.empty? 96 | end 97 | invoke(task) 98 | end 99 | 100 | def invoke(t) #:nodoc: 101 | t.invoke 102 | return true 103 | rescue ::Rake::CommandFailedError => e 104 | return false if opts[:suppress_and_return_failures] 105 | # Reraise error if we're not suppressing it 106 | raise e 107 | end 108 | 109 | private 110 | 111 | def task_sym(name) #:nodoc: 112 | "install_#{name.to_task_name}".to_sym 113 | end 114 | end 115 | end 116 | end 117 | -------------------------------------------------------------------------------- /lib/sprinkle/commands/command.rb: -------------------------------------------------------------------------------- 1 | module Sprinkle 2 | module Commands 3 | class Command 4 | 5 | def initialize(str, opts = {}) 6 | @sudo = opts[:sudo] 7 | @str = str 8 | # this is a dummy class for now, not intended to be used directly 9 | raise 10 | end 11 | 12 | def sudo? 13 | @sudo 14 | end 15 | 16 | def string 17 | # TODO: sudo 18 | @str 19 | end 20 | 21 | end 22 | end 23 | end -------------------------------------------------------------------------------- /lib/sprinkle/commands/reconnect.rb: -------------------------------------------------------------------------------- 1 | module Sprinkle 2 | module Commands 3 | class Reconnect < Command 4 | 5 | def initialize() 6 | end 7 | 8 | def inspect 9 | ":RECONNECT" 10 | end 11 | 12 | end 13 | end 14 | end -------------------------------------------------------------------------------- /lib/sprinkle/commands/transfer.rb: -------------------------------------------------------------------------------- 1 | module Sprinkle 2 | module Commands 3 | class Transfer < Command 4 | 5 | attr_reader :source, :destination, :opts 6 | 7 | def initialize(source, destination, opts={}) 8 | @source = source 9 | @destination = destination 10 | @opts = opts 11 | end 12 | 13 | def recursive? 14 | !!@opts[:recursive] 15 | end 16 | 17 | def inspect 18 | ":TRANSFER, src: #{source}, dest: #{destination}, opts: #{@opts.inspect}" 19 | end 20 | 21 | def eql?(a,b) 22 | a.source == b.source && 23 | a.destionation == b.destination && 24 | a.opts == b.opts 25 | end 26 | 27 | end 28 | end 29 | end -------------------------------------------------------------------------------- /lib/sprinkle/core.rb: -------------------------------------------------------------------------------- 1 | module Sprinkle 2 | # stores the global list of policies as they are defined 3 | POLICIES = [] 4 | 5 | module Core 6 | # Defines a single policy. Currently the only option, which is also 7 | # required, is :roles, which defines which servers a policy is 8 | # used on. 9 | def policy(name, options = {}, &block) 10 | p = Sprinkle::Policy.new(name, options, &block) 11 | POLICIES << p 12 | p 13 | end 14 | 15 | end 16 | end -------------------------------------------------------------------------------- /lib/sprinkle/deployment.rb: -------------------------------------------------------------------------------- 1 | module Sprinkle 2 | # = Deployments 3 | # 4 | # Deployment blocks specify deployment specific information about a 5 | # sprinkle script. An example: 6 | # 7 | # deployment do 8 | # # mechanism for deployment 9 | # delivery :capistrano do 10 | # recipes 'deploy' 11 | # end 12 | # 13 | # # source based package installer defaults 14 | # source do 15 | # prefix '/usr/local' 16 | # archives '/usr/local/sources' 17 | # builds '/usr/local/build' 18 | # end 19 | # end 20 | # 21 | # What the above example does is tell sprinkle that we will be using 22 | # *capistrano* (Sprinkle::Actors::Capistrano) for deployment and 23 | # everything within the block is capistrano specific configuration. 24 | # For more information on what options are available, check the corresponding 25 | # Sprinkle::Actors doc page. 26 | # 27 | # In addition to what delivery mechanism we're using, we specify some 28 | # configuration options for the "source" command. The only things 29 | # configurable, at this time, in the deployment block other than 30 | # the delivery method are installers. If installers are configurable, 31 | # they will say so on their corresponding documentation page. See 32 | # Sprinkle::Installers 33 | # 34 | # The deployment block must be included in the script file passed to the 35 | # sprinkle executable. It may not be loaded from a required file unless you 36 | # first manually include the Sprinkle::Deployment module in the Object class. 37 | # 38 | # Only one deployment block is on any given sprinkle script 39 | module Deployment 40 | # The method outlined above which specifies deployment specific information 41 | # for a sprinkle script. For more information, read the header of this module. 42 | def deployment(&block) 43 | @deployment = Deployment.new(&block) 44 | end 45 | 46 | class Deployment 47 | attr_accessor :style, :defaults #:nodoc: 48 | 49 | def initialize(&block) #:nodoc: 50 | @defaults = {} 51 | @style = nil 52 | self.instance_eval(&block) 53 | raise 'No delivery mechanism defined' unless @style 54 | end 55 | 56 | # Specifies which Sprinkle::Actors to use for delivery. Although all 57 | # actors jobs are the same: to run remote commands on a server, you 58 | # may have a personal preference. The block you pass is used to configure 59 | # the actor. For more information on what configuration options are 60 | # available, view the corresponding Sprinkle::Actors page. 61 | def delivery(type, &block) #:doc: 62 | type=type.to_s.titleize 63 | type="SSH" if type=="Ssh" 64 | @style = ("Sprinkle::Actors::" + type).constantize.new(&block) 65 | end 66 | 67 | def method_missing(sym, *args, &block) #:nodoc: 68 | if Sprinkle::Package::Package.installer_methods.include?(sym) 69 | @defaults[sym] = block 70 | else 71 | super sym, *args, &block 72 | end 73 | end 74 | 75 | def active_policies #:nodoc: 76 | if role=Sprinkle::OPTIONS[:only_role] 77 | role=role.to_sym 78 | POLICIES.select {|x| [x.roles].flatten.include?(role) } 79 | else 80 | POLICIES 81 | end 82 | end 83 | 84 | def process #:nodoc: 85 | active_policies.each do |policy| 86 | policy.process(self) 87 | end 88 | rescue Sprinkle::Errors::RemoteCommandFailure => e 89 | e.print_summary 90 | exit 1 91 | rescue Sprinkle::Errors::TransferFailure => e 92 | e.print_summary 93 | exit 2 94 | ensure 95 | # do any cleanup our actor may need to close network sockets, etc 96 | @style.teardown if @style.respond_to?(:teardown) 97 | end 98 | end 99 | end 100 | end 101 | -------------------------------------------------------------------------------- /lib/sprinkle/errors/pretty_failure.rb: -------------------------------------------------------------------------------- 1 | module Sprinkle 2 | module Errors #:nodoc: 3 | 4 | class PrettyFailure < StandardError #:nodoc: 5 | 6 | attr_accessor :details 7 | 8 | def initialize(installer, details={}, previous_error=nil) 9 | @installer = installer 10 | @details = details 11 | @previous_error = previous_error 12 | end 13 | 14 | def log(s, o) 15 | puts s 16 | puts "-" * (s.length+2) 17 | puts o 18 | puts 19 | end 20 | 21 | def boxed(s) 22 | puts red("-"*54) 23 | puts red("| #{s.center(50)} |") 24 | puts red("-"*54) 25 | puts 26 | end 27 | 28 | private 29 | 30 | def color(code, s) 31 | "\033[%sm%s\033[0m"%[code,s] 32 | end 33 | 34 | def red(s) 35 | color(31, s) 36 | end 37 | 38 | end 39 | 40 | end 41 | end -------------------------------------------------------------------------------- /lib/sprinkle/errors/remote_command_failure.rb: -------------------------------------------------------------------------------- 1 | module Sprinkle 2 | module Errors 3 | 4 | class RemoteCommandFailure < PrettyFailure #:nodoc: 5 | 6 | def print_summary 7 | summary 8 | log "Command", @details[:command] 9 | # capistrano returns this 10 | log "Hosts", @details[:hosts] if @details[:hosts] 11 | # ssh actor returns error and stdout outputs 12 | log "STDERR", @details[:error] unless @details[:error].blank? 13 | log "STDOUT", @details[:stdout] unless @details[:stdout].blank? 14 | log "Actor error message", @details[:message] if @details[:message] 15 | end 16 | 17 | def summary 18 | boxed("Package '#{@installer.package.name}' returned error code #{@details[:code]}.") 19 | end 20 | 21 | end 22 | 23 | end 24 | end -------------------------------------------------------------------------------- /lib/sprinkle/errors/template_error.rb: -------------------------------------------------------------------------------- 1 | module Sprinkle::Errors 2 | # Blatantly stole this from Chef 3 | class TemplateError < RuntimeError #:nodoc: 4 | attr_reader :original_exception, :context 5 | SOURCE_CONTEXT_WINDOW = 2 unless defined? SOURCE_CONTEXT_WINDOW 6 | 7 | def initialize(original_exception, template, context) 8 | @original_exception, @template, @context = original_exception, template, context 9 | end 10 | 11 | def message 12 | @original_exception.message 13 | end 14 | 15 | def line_number 16 | @line_number ||= $1.to_i if original_exception.backtrace.find {|line| line =~ /\(erubis\):(\d+)/ } 17 | end 18 | 19 | def source_location 20 | "on line ##{line_number}" 21 | end 22 | 23 | def source_listing 24 | return nil if line_number.nil? 25 | 26 | @source_listing ||= begin 27 | line_index = line_number - 1 28 | beginning_line = line_index <= SOURCE_CONTEXT_WINDOW ? 0 : line_index - SOURCE_CONTEXT_WINDOW 29 | source_size = SOURCE_CONTEXT_WINDOW * 2 + 1 30 | lines = @template.split(/\n/) 31 | contextual_lines = lines[beginning_line, source_size] 32 | output = [] 33 | contextual_lines.each_with_index do |line, index| 34 | line_number = (index+beginning_line+1).to_s.rjust(3) 35 | output << "#{line_number}: #{line}" 36 | end 37 | output.join("\n") 38 | end 39 | end 40 | 41 | def to_s 42 | "\n\n#{self.class} (#{message}) #{source_location}:\n\n" + 43 | "#{source_listing}\n\n #{original_exception.backtrace.join("\n ")}\n\n" 44 | end 45 | end 46 | end -------------------------------------------------------------------------------- /lib/sprinkle/errors/transfer_failure.rb: -------------------------------------------------------------------------------- 1 | module Sprinkle 2 | module Errors 3 | 4 | class TransferFailure < PrettyFailure #:nodoc: 5 | 6 | def self.no_permission(installer,e) 7 | tf=TransferFailure.new(installer, {}, e) 8 | tf.details[:error]=e.message 9 | tf 10 | end 11 | 12 | def print_summary 13 | summary 14 | # log "Command", @details[:command] 15 | log "ERROR", @details[:error] 16 | if details[:error] =~ /Permission denied/ 17 | log "HINTS", "You may want to try passing the :sudo option to transfer." 18 | end 19 | end 20 | 21 | def summary 22 | boxed("Package '#{@installer.package.name}' could not transfer #{@installer.source}") 23 | end 24 | 25 | end 26 | 27 | end 28 | end -------------------------------------------------------------------------------- /lib/sprinkle/extensions/array.rb: -------------------------------------------------------------------------------- 1 | class Array #:nodoc: 2 | def to_task_name 3 | collect(&:to_task_name).join('_') 4 | end 5 | end -------------------------------------------------------------------------------- /lib/sprinkle/extensions/attributes.rb: -------------------------------------------------------------------------------- 1 | module Sprinkle 2 | module Attributes 3 | extend ActiveSupport::Concern 4 | 5 | included do 6 | attr_accessor :delivery 7 | end 8 | 9 | def defaults(deployment) 10 | defaults = deployment.defaults[self.class.name.split(/::/).last.downcase.to_sym] 11 | self.set_defaults(&defaults) if defaults 12 | @delivery = deployment.style 13 | end 14 | 15 | def set_defaults(&block) 16 | before = @options 17 | @options = {} 18 | self.instance_eval(&block) if block 19 | @options = before.reverse_merge(@options) 20 | end 21 | 22 | private 23 | 24 | def read_from_package(m) 25 | @package.send(m) if @package.respond_to?(m) and @package.method(m).arity.abs < 2 26 | end 27 | 28 | def option?(sym) 29 | !!@options[sym] 30 | end 31 | 32 | module ClassMethods 33 | 34 | def attributes(*list) 35 | list.each do |a| 36 | define_method a do |*val| 37 | val=nil if val.empty? 38 | val ? @options[a] = val.first : @options[a] || read_from_package(a) 39 | end 40 | end 41 | end 42 | 43 | def multi_attributes(*list) 44 | list.each do |a| 45 | define_method a do |*val| 46 | val = val.try(:first) 47 | return @options[a] unless val 48 | @options[a]||=[] 49 | val.is_a?(Array) ? @options[a] += val : @options[a] << val 50 | end 51 | end 52 | end 53 | end 54 | 55 | end 56 | end -------------------------------------------------------------------------------- /lib/sprinkle/extensions/blank_slate.rb: -------------------------------------------------------------------------------- 1 | class BlankSlate #:nodoc: 2 | instance_methods.each do |m| 3 | undef_method(m) unless %w( __send__ __id__ send class inspect instance_eval instance_variables object_id ).include?(m.to_s) 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /lib/sprinkle/extensions/string.rb: -------------------------------------------------------------------------------- 1 | class String #:nodoc: 2 | 3 | # REVISIT: what chars shall we allow in task names? 4 | def to_task_name 5 | s = downcase 6 | s.gsub!(/-/, '_') # all - to _ chars 7 | s 8 | end 9 | 10 | end -------------------------------------------------------------------------------- /lib/sprinkle/extensions/sudo.rb: -------------------------------------------------------------------------------- 1 | module Sprinkle 2 | module Sudo 3 | 4 | def sudo_cmd 5 | return "#{@delivery.try(:sudo_command) || "sudo"} " if sudo? 6 | end 7 | 8 | def sudo? 9 | sudo_stack.detect { |x| x==true or x==false } 10 | end 11 | 12 | def sudo_stack 13 | [ options[:sudo], package.sudo?, @delivery.try(:sudo?) ] 14 | end 15 | 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /lib/sprinkle/extensions/symbol.rb: -------------------------------------------------------------------------------- 1 | class Symbol #:nodoc: 2 | 3 | def to_task_name 4 | to_s.to_task_name 5 | end 6 | 7 | end -------------------------------------------------------------------------------- /lib/sprinkle/installers/apt.rb: -------------------------------------------------------------------------------- 1 | module Sprinkle 2 | module Installers 3 | # The Apt package installer uses the +apt-get+ command to install 4 | # packages. The apt installer has only one option which can be 5 | # modified which is the +dependencies_only+ option. When this is 6 | # set to true, the installer uses +build-dep+ instead of +install+ 7 | # to only build the dependencies. 8 | # 9 | # == Example Usage 10 | # 11 | # First, a simple installation of the magic_beans package: 12 | # 13 | # package :magic_beans do 14 | # apt 'magic_beans_package' 15 | # verify { has_apt 'magic_beans_package' } 16 | # end 17 | # 18 | # Second, only build the magic_beans dependencies: 19 | # 20 | # package :magic_beans_depends do 21 | # apt 'magic_beans_package' do 22 | # dependencies_only true 23 | # end 24 | # end 25 | # 26 | # As you can see, setting options is as simple as creating a 27 | # block and calling the option as a method with the value as 28 | # its parameter. 29 | class Apt < PackageInstaller 30 | def initialize(parent, *packages, &block) #:nodoc: 31 | super parent, *packages, &block 32 | @options.reverse_merge!(:dependencies_only => false) 33 | end 34 | 35 | attributes :dependencies_only 36 | 37 | auto_api 38 | 39 | verify_api do 40 | def has_apt(package) 41 | @commands << "dpkg --status #{package} | grep \"ok installed\"" 42 | end 43 | end 44 | 45 | protected 46 | 47 | def install_commands #:nodoc: 48 | command = @options[:dependencies_only] ? 'build-dep' : 'install' 49 | noninteractive = "#{sudo_cmd}env DEBCONF_TERSE='yes' DEBIAN_PRIORITY='critical' DEBIAN_FRONTEND=noninteractive" 50 | "#{noninteractive} apt-get --force-yes -qyu #{command} #{@packages.join(' ')}" 51 | end 52 | 53 | end 54 | end 55 | end 56 | -------------------------------------------------------------------------------- /lib/sprinkle/installers/binary.rb: -------------------------------------------------------------------------------- 1 | module Sprinkle 2 | module Installers 3 | # The Binary installer will download a binary archive and then extract 4 | # it in the directory specified by the prefix option. 5 | # 6 | # == Example Usage 7 | # 8 | # binary "http://some.url.com/archive.tar.gz" do 9 | # prefix "/home/user/local" 10 | # archives "/home/user/sources" 11 | # end 12 | # 13 | # This example will download archive.tar.gz to /home/user/sources and then 14 | # extract it into /home/user/local. 15 | class Binary < Installer 16 | 17 | api do 18 | def binary(source, options = {}, &block) 19 | install Binary.new(self, source, options, &block) 20 | end 21 | end 22 | 23 | def initialize(parent, binary_archive, options = {}, &block) #:nodoc: 24 | @binary_archive = binary_archive 25 | super parent, options, &block 26 | end 27 | 28 | def prepare_commands #:nodoc: 29 | raise 'No installation area defined' unless @options[:prefix] 30 | raise 'No archive download area defined' unless @options[:archives] 31 | 32 | [ "mkdir -p #{@options[:prefix]}", 33 | "mkdir -p #{@options[:archives]}" ] 34 | end 35 | 36 | def install_commands #:nodoc: 37 | commands = [ "bash -c 'wget -cq --directory-prefix=#{@options[:archives]} #{@binary_archive}'" ] 38 | commands << "bash -c \"cd #{@options[:prefix]} && #{sudo_cmd} #{extract_command} '#{@options[:archives]}/#{archive_name}'\"" 39 | end 40 | 41 | def archive_name #:nodoc: 42 | @archive_name ||= @binary_archive.split("/").last.gsub('%20', ' ') 43 | end 44 | 45 | def extract_command #:nodoc: 46 | case archive_name 47 | when /(tar.gz)|(tgz)$/ 48 | 'tar xzf' 49 | when /(tar.bz2)|(tb2)$/ 50 | 'tar xjf' 51 | when /tar$/ 52 | 'tar xf' 53 | when /zip$/ 54 | 'unzip -o' 55 | else 56 | raise "Unknown binary archive format: #{archive_name}" 57 | end 58 | end 59 | end 60 | end 61 | end 62 | -------------------------------------------------------------------------------- /lib/sprinkle/installers/brew.rb: -------------------------------------------------------------------------------- 1 | module Sprinkle 2 | module Installers 3 | # The Homebrew package installer uses the +brew+ command to install 4 | # packages on OSX. 5 | # 6 | # == Example Usage 7 | # 8 | # package :magic_beans do 9 | # description "Beans beans they're good for your heart..." 10 | # brew 'ntp' 11 | # 12 | # verify { has_brew 'ntp' } 13 | # 14 | # end 15 | # 16 | class Brew < PackageInstaller 17 | 18 | api do 19 | def brew(*names, &block) 20 | recommends :homebrew 21 | install_package(*names, &block) 22 | end 23 | end 24 | 25 | verify_api do 26 | def has_brew(package) 27 | @commands << "brew list | grep #{package}" 28 | end 29 | end 30 | 31 | protected 32 | 33 | def install_commands #:nodoc: 34 | "brew install #{@packages.join(' ')}" 35 | end 36 | 37 | end 38 | end 39 | end 40 | -------------------------------------------------------------------------------- /lib/sprinkle/installers/bsd_port.rb: -------------------------------------------------------------------------------- 1 | module Sprinkle 2 | module Installers 3 | # The BSD Port installer installs OpenBSD and FreeBSD ports. 4 | # Before usage, the ports sytem must be installed and 5 | # ready on the target operating system. 6 | # 7 | # == Example Usage 8 | # 9 | # Installing the magic_beans port. 10 | # 11 | # package :magic_beans do 12 | # bsd_port 'magic/magic_beans' 13 | # end 14 | # 15 | class BsdPort < Installer 16 | attr_accessor :port #:nodoc: 17 | 18 | api do 19 | def bsd_port(port, options ={}, &block) 20 | install BsdPort.new(self, port, options, &block) 21 | end 22 | end 23 | 24 | def initialize(parent, port, options={}, &block) #:nodoc: 25 | super parent, options, &block 26 | @port = port 27 | end 28 | 29 | protected 30 | 31 | def install_commands #:nodoc: 32 | "sh -c 'cd /usr/ports/#{@port} && make BATCH=yes install clean'" 33 | end 34 | 35 | end 36 | end 37 | end -------------------------------------------------------------------------------- /lib/sprinkle/installers/deb.rb: -------------------------------------------------------------------------------- 1 | module Sprinkle 2 | module Installers 3 | # The Deb installer installs deb packages sourced from a remote URL 4 | # 5 | # == Example Usage 6 | # 7 | # Installing the magic_beans deb. 8 | # 9 | # package :magic_beans do 10 | # deb 'http://debs.example.com/magic_beans.deb' 11 | # end 12 | # 13 | class Deb < PackageInstaller 14 | 15 | ## 16 | # install deb packages from an external URL 17 | # :call-seq: 18 | # deb(*package_urls) 19 | auto_api :deb 20 | 21 | protected 22 | 23 | def install_commands #:nodoc: 24 | [ 25 | "wget -cq --directory-prefix=/tmp #{@packages.join(' ')}", 26 | "dpkg -i #{@packages.collect{|p| "/tmp/#{package_name(p)}"}.join(" ")}" 27 | ] 28 | end 29 | 30 | private 31 | 32 | def package_name(url) 33 | url.split('/').last 34 | end 35 | 36 | end 37 | end 38 | end 39 | -------------------------------------------------------------------------------- /lib/sprinkle/installers/file.rb: -------------------------------------------------------------------------------- 1 | require 'tempfile' 2 | 3 | module Sprinkle 4 | module Installers 5 | # = File installer 6 | # 7 | # This installer creates a file on the remote server. 8 | # 9 | # == Example Usage 10 | # 11 | # Installing a nginx.conf onto remote servers 12 | # 13 | # package :nginx_conf do 14 | # file '/etc/nginx.conf', :content => File.read('files/nginx.conf'), 15 | # :sudo => true 16 | # end 17 | # 18 | # Sudo is only necessary when the user your sprinkle is running as does 19 | # not have necessarily permissions to create the file on its own. 20 | # Such as when the file is in /etc. 21 | # 22 | # Should you need to run commands before or after the file transfer (making 23 | # directories or changing permissions), you can use the pre/post :install directives. 24 | # 25 | # == Rendering templates 26 | # 27 | # Use the template render helper to render an ERB template to a remote file (you 28 | # can use variables in your templates by setting them as instance variables inside 29 | # your package. Templates have access to package methods such as opts, args, etc. 30 | # 31 | # package :nginx_conf do 32 | # @nginx_port = 8080 33 | # file '/etc/nginx.conf', 34 | # :contents => render("nginx.conf") 35 | # # where [cwd] is the current working dir you're running sprinkle from 36 | # # [cwd]/templates/nginx.conf.erb or 37 | # # [cwd]/templates/nginx.conf should contain the erb template 38 | # end 39 | # 40 | # You can also tell the package where to look for templates, so that if you have 41 | # a complex package hierarchy such as: 42 | # 43 | # .../packages/p/postfix.rb 44 | # .../packages/p/postfix/templates/main.cf.erb 45 | # 46 | # package :postfix do 47 | # template_search_path File.dirname(__FILE__) 48 | # file '/etc/postfix/main.cf', :contents => render("main.cf") 49 | # # searches for: 50 | # # ../packages/p/main.cf[.erb] 51 | # # ../packages/p/templates/main.cf[.erb] 52 | # end 53 | class FileInstaller < Installer 54 | attr_reader :sourcepath, :destination, :contents #:nodoc: 55 | 56 | api do 57 | def file(destination, options = {}, &block) #:nodoc: 58 | # options.merge!(:binding => binding()) 59 | install FileInstaller.new(self, destination, options, &block) 60 | end 61 | end 62 | 63 | def initialize(parent, destination, options={}, &block) #:nodoc: 64 | @destination = destination 65 | @contents = options[:content] || options[:contents] 66 | raise "need :contents key for file" unless @contents 67 | super parent, options, &block 68 | 69 | # setup file attributes 70 | owner options[:owner] if options[:owner] 71 | mode options[:mode] if options[:mode] 72 | 73 | post_move_if_sudo 74 | setup_source 75 | end 76 | 77 | def install_commands #:nodoc: 78 | Commands::Transfer.new(sourcepath, destination) 79 | end 80 | 81 | # calls chown own to set the file ownership 82 | def owner(owner) 83 | @owner = owner 84 | post :install, "#{sudo_cmd}chown #{owner} #{@destination}" 85 | end 86 | 87 | # calls chmod to set the files permissions 88 | def mode(mode) 89 | @mode = mode 90 | post :install, "#{sudo_cmd}chmod #{mode} #{@destination}" 91 | end 92 | 93 | private 94 | 95 | def post_move_if_sudo 96 | return unless sudo? # perform the file copy in two steps if we're using sudo 97 | final = @destination 98 | @destination = "/tmp/sprinkle_#{File.basename(@destination)}" 99 | # make sure we push the move ahead of any other post install tasks 100 | # a user may have requested 101 | post(:install).unshift ["#{sudo_cmd}mv #{@destination} #{final}"] 102 | end 103 | 104 | def setup_source 105 | @file = Tempfile.new(@package.name.to_s) 106 | @file.print @contents 107 | @file.close 108 | @sourcepath = @file.path 109 | end 110 | 111 | def post_process 112 | @file.unlink 113 | end 114 | 115 | end 116 | end 117 | end 118 | -------------------------------------------------------------------------------- /lib/sprinkle/installers/freebsd_pkg.rb: -------------------------------------------------------------------------------- 1 | module Sprinkle 2 | module Installers 3 | # The FreeBSDPkg installer installs FreeBSD packages. 4 | # 5 | # == Example Usage 6 | # 7 | # Installing the magic_beans package. 8 | # 9 | # package :magic_beans do 10 | # freebsd_pkg 'magic_beans' 11 | # end 12 | # 13 | # You may also specify multiple packages as an array: 14 | # 15 | # package :magic_beans do 16 | # freebsd_pkg %w(magic_beans magic_sauce) 17 | # end 18 | # 19 | class FreebsdPkg < PackageInstaller 20 | 21 | ## 22 | # installs the FreeBSD packages passed 23 | # :method: freebsd_pkg 24 | # :call-seq: freebsd_pkg(*packages) 25 | auto_api 26 | 27 | protected 28 | 29 | def install_commands #:nodoc: 30 | "pkg_add -r #{@packages.join(' ')}" 31 | end 32 | 33 | end 34 | end 35 | end 36 | -------------------------------------------------------------------------------- /lib/sprinkle/installers/freebsd_portinstall.rb: -------------------------------------------------------------------------------- 1 | module Sprinkle 2 | module Installers 3 | # = FreeBSD Portinstall Installer 4 | # 5 | # The Portinstall installer installs FreeBSD ports. 6 | # It uses the ports-mgmt/portupgrade port to install. 7 | # Before usage, the ports system must be installed and 8 | # read on the target operating system. 9 | # It is recommended to use `portsnap fetch extract` to 10 | # install the ports system. 11 | # 12 | # == Example Usage 13 | # 14 | # Installing the magic_beans port. 15 | # 16 | # package :magic_beans do 17 | # freebsd_portinstall 'magic/magic_beans' 18 | # end 19 | # 20 | class FreebsdPortinstall < Installer 21 | attr_accessor :port #:nodoc: 22 | 23 | api do 24 | def freebsd_portinstall(port, options={}, &block) 25 | install FreebsdPortinstall.new(self, port, options, &block) 26 | end 27 | end 28 | 29 | def initialize(parent, port, options={}, &block) #:nodoc: 30 | super parent, options, &block 31 | @port = port 32 | end 33 | 34 | protected 35 | 36 | def install_commands #:nodoc: 37 | "portinstall --batch #{@port}" 38 | end 39 | 40 | end 41 | end 42 | end 43 | -------------------------------------------------------------------------------- /lib/sprinkle/installers/gem.rb: -------------------------------------------------------------------------------- 1 | 2 | module Sprinkle 3 | module Installers 4 | # The gem package installer installs Ruby gems. 5 | # 6 | # The installer has a optional configuration: source. 7 | # By changing source you can specify a given ruby gems 8 | # repository from which to install. 9 | # 10 | # Besides `source`, this installer also supports these options: 11 | # 12 | # - `repository`: install directory 13 | # - `http_proxy` 14 | # - `build_docs`: by default, no `rdoc` and `ri` 15 | # - `build_flags`: additional build flags 16 | # 17 | # == Example Usage 18 | # 19 | # First, a simple installation of the magic_beans gem: 20 | # 21 | # package :magic_beans do 22 | # description "Beans beans they're good for your heart..." 23 | # gem 'magic_beans' 24 | # end 25 | # 26 | # Second, install magic_beans gem from github: 27 | # 28 | # package :magic_beans do 29 | # gem 'magic_beans_package' do 30 | # source 'http://gems.github.com' 31 | # end 32 | # end 33 | # 34 | # As you can see, setting options is as simple as creating a 35 | # block and calling the option as a method with the value as 36 | # its parameter. 37 | class Gem < Installer 38 | 39 | api do 40 | def gem(name, options = {}, &block) 41 | recommends :rubygems 42 | install Gem.new(self, name, options, &block) 43 | end 44 | end 45 | 46 | attr_accessor :gem #:nodoc: 47 | 48 | def initialize(parent, gem, options = {}, &block) #:nodoc: 49 | super parent, options, &block 50 | @gem = gem 51 | end 52 | 53 | attributes :source, :repository, :http_proxy, :build_docs, :build_flags, :version 54 | 55 | protected 56 | 57 | # rubygems 0.9.5+ installs dependencies by default, and does platform selection 58 | 59 | def install_commands #:nodoc: 60 | cmd = "#{sudo_cmd}gem install #{gem}" 61 | cmd << " --version '#{version}'" if version 62 | cmd << " --source #{source}" if source 63 | cmd << " --install-dir #{repository}" if option?(:repository) 64 | cmd << " --no-rdoc --no-ri" unless option?(:build_docs) 65 | cmd << " --http-proxy #{http_proxy}" if option?(:http_proxy) 66 | cmd << " -- #{build_flags}" if option?(:build_flags) 67 | cmd 68 | end 69 | 70 | end 71 | end 72 | end 73 | -------------------------------------------------------------------------------- /lib/sprinkle/installers/group.rb: -------------------------------------------------------------------------------- 1 | module Sprinkle 2 | module Installers 3 | # The user installer helps add groups. You may pass flags as an option. 4 | # 5 | # == Example Usage 6 | # 7 | # package :users do 8 | # add_group 'webguys', :flags => "--shell /usr/bin/zsh" 9 | # 10 | # verify do 11 | # has_group 'webguys' 12 | # end 13 | # end 14 | class Group < Installer 15 | api do 16 | def add_group(group, options={}, &block) 17 | install Group.new(self, group, options, &block) 18 | end 19 | end 20 | 21 | verify_api do 22 | def has_group(group) 23 | @commands << "egrep -i \"^#{group}:\" /etc/group" 24 | end 25 | end 26 | 27 | def initialize(package, groupname, options, &block) #:nodoc: 28 | super package, options, &block 29 | @groupname = groupname 30 | end 31 | 32 | protected 33 | def install_commands #:nodoc: 34 | "addgroup #{@options[:flags]} #{@groupname}" 35 | end 36 | end 37 | end 38 | end 39 | -------------------------------------------------------------------------------- /lib/sprinkle/installers/install_package.rb: -------------------------------------------------------------------------------- 1 | module Sprinkle 2 | module Installers 3 | class InstallPackage < Installer #:nodoc: 4 | cattr_accessor :installer 5 | attr_accessor :packages #:nodoc: 6 | 7 | def initialize(parent, packages, &block) #:nodoc: 8 | super parent, &block 9 | if packages.is_a?(Array) && packages.first.is_a?(Array) 10 | packages = packages.first 11 | else 12 | packages = [packages] unless packages.is_a? Array 13 | end 14 | 15 | @packages = packages 16 | end 17 | 18 | protected 19 | 20 | def install_commands #:nodoc: 21 | case installer 22 | when :smart 23 | "smart install #{@packages.join(' ')} -y 2>&1 | tee -a /var/log/smart-sprinkle" 24 | when :yum 25 | "yum install #{@packages.join(' ')} -y 2>&1 | tee -a /var/log/yum-sprinkle" 26 | else 27 | raise "Unknown InstallPackage.installer" 28 | end 29 | end 30 | end 31 | 32 | class UninstallPackage < Installer 33 | attr_accessor :packages #:nodoc: 34 | 35 | def initialize(parent, packages, &block) #:nodoc: 36 | super parent, &block 37 | if packages.is_a?(Array) && packages.first.is_a?(Array) 38 | packages = packages.first 39 | else 40 | packages = [packages] unless packages.is_a? Array 41 | end 42 | 43 | @packages = packages 44 | end 45 | 46 | protected 47 | 48 | def install_commands #:nodoc: 49 | case Sprinkle::Installers::InstallPackage.installer 50 | when :smart 51 | "smart remove #{@packages.join(' ')} -y 2>&1 | tee -a /var/log/smart-sprinkle" 52 | when :yum 53 | "yum erase #{@packages.join(' ')} -y 2>&1 | tee -a /var/log/yum-sprinkle" 54 | else 55 | raise "Unknown InstallPackage.installer" 56 | end 57 | end 58 | end 59 | end 60 | end 61 | 62 | module Sprinkle 63 | module Package 64 | class Package 65 | def install_package(*names, &block) #:nodoc: 66 | ActiveSupport::Deprecation.warn("install_package will be removed from sprinkle 0.8, please use yum or smart installers instead.") 67 | @installers << Sprinkle::Installers::InstallPackage.new(self, names, &block) 68 | end 69 | 70 | def uninstall_package(*names, &block) #:nodoc: 71 | ActiveSupport::Deprecation.warn("uninstall_package will be removed from sprinkle 0.8, please use yum or smart installers instead.") 72 | @installers << Sprinkle::Installers::UninstallPackage.new(self, names, &block) 73 | end 74 | 75 | alias_method :install_packages, :install_package #:nodoc: 76 | alias_method :uninstall_packages, :uninstall_package #:nodoc: 77 | end 78 | end 79 | end 80 | 81 | -------------------------------------------------------------------------------- /lib/sprinkle/installers/mac_port.rb: -------------------------------------------------------------------------------- 1 | module Sprinkle 2 | module Installers 3 | # The MacPort installer installs macports ports. 4 | # 5 | # == Example Usage 6 | # 7 | # Installing the magic_beans port. 8 | # 9 | # package :magic_beans do 10 | # mac_port 'magic/magic_beans' 11 | # end 12 | # 13 | # == Notes 14 | # 15 | # Before MacPorts packages can be installed, the PATH 16 | # environment variable probably has to be changed so 17 | # Sprinkle can find the /opt/local/bin/port executable 18 | # 19 | # You must set PATH in ~/.ssh/environment on the remote 20 | # system and enable 'PermitUserEnvironment yes' in /etc/sshd_config 21 | # 22 | class MacPort < Installer 23 | 24 | api do 25 | def mac_port(port, options={}, &block) 26 | install MacPort.new(self, port, options, &block) 27 | end 28 | end 29 | 30 | attr_accessor :port #:nodoc: 31 | 32 | def initialize(parent, port, options = {}, &block) #:nodoc: 33 | super parent, options, &block 34 | @port = port 35 | end 36 | 37 | protected 38 | 39 | def install_commands #:nodoc: 40 | "port install #{@port}" 41 | end 42 | 43 | end 44 | end 45 | end -------------------------------------------------------------------------------- /lib/sprinkle/installers/npm.rb: -------------------------------------------------------------------------------- 1 | module Sprinkle 2 | module Installers 3 | # = Npm package Installed 4 | # 5 | # Installs an npm module 6 | # 7 | # == Example Usage 8 | # 9 | # package :magic_beans do 10 | # npm 'grunt' 11 | # end 12 | # 13 | # verify { has_npm 'grunt' } 14 | class Npm < Installer 15 | 16 | attr_accessor :package_name 17 | 18 | api do 19 | def npm(package, &block) 20 | install Npm.new(self, package, &block) 21 | end 22 | end 23 | 24 | verify_api do 25 | def has_npm(package) 26 | @commands << "npm --global list | grep \"#{package}@\"" 27 | end 28 | end 29 | 30 | def initialize(parent, package_name, &block) #:nodoc: 31 | super parent, &block 32 | @package_name = package_name 33 | end 34 | 35 | protected 36 | 37 | def install_commands #:nodoc: 38 | "npm install --global #{@package_name}" 39 | end 40 | 41 | end 42 | end 43 | end 44 | -------------------------------------------------------------------------------- /lib/sprinkle/installers/openbsd_pkg.rb: -------------------------------------------------------------------------------- 1 | module Sprinkle 2 | module Installers 3 | # The OpenBSD package installer installs OpenBSD packages. 4 | # 5 | # == Example Usage 6 | # 7 | # Installing the magic_beans package. 8 | # 9 | # package :magic_beans do 10 | # openbsd_pkg 'magic_beans' 11 | # end 12 | # 13 | # You may also specify multiple packages as an array: 14 | # 15 | # package :magic_beans do 16 | # openbsd_pkg %w(magic_beans magic_sauce) 17 | # end 18 | # 19 | # == Notes 20 | # Before OpenBSD packages can be installed, the PKG_PATH 21 | # environment variable must be set. 22 | # 23 | # You must set PKG_PATH in ~/.ssh/environment on the remote 24 | # system and enable 'PermitUserEnvironment yes' in /etc/ssh/sshd_config 25 | # 26 | # For help on PKG_PATH see section 15.2.2 of the OpenBSD FAQ 27 | # (http://www.openbsd.org/faq/faq15.html) 28 | class OpenbsdPkg < PackageInstaller 29 | 30 | ## 31 | # installs the OpenBSD packages passed 32 | # :method: openbsd_pkg 33 | # :call-seq: openbsd_pkg(*packages) 34 | auto_api 35 | 36 | protected 37 | 38 | def install_commands #:nodoc: 39 | "pkg_add #{@packages.join(' ')}" 40 | end 41 | 42 | end 43 | end 44 | end 45 | -------------------------------------------------------------------------------- /lib/sprinkle/installers/opensolaris_pkg.rb: -------------------------------------------------------------------------------- 1 | module Sprinkle 2 | module Installers 3 | # The OpenSolaris package installer installs OpenSolaris packages. 4 | # 5 | # == Example Usage 6 | # 7 | # Installing the magic_beans package. 8 | # 9 | # package :magic_beans do 10 | # opensolaris_pkg 'magic_beans' 11 | # end 12 | # 13 | # You may also specify multiple packages as an array: 14 | # 15 | # package :magic_beans do 16 | # opensolaris_pkg 'magic_beans', 'magic_sauce' 17 | # end 18 | # 19 | # == Note 20 | # If you are using capistrano as the deployment method 21 | # you will need to add the following lines to your deploy.rb 22 | # 23 | # set :sudo, 'pfexec' 24 | # set :sudo_prompt, '' 25 | class OpensolarisPkg < PackageInstaller 26 | 27 | ## 28 | # installs the OpenSolaris packages passed 29 | # :method: opensolaris_pkg 30 | # :call-seq: opensolaris_pkg(*packages) 31 | auto_api 32 | 33 | protected 34 | 35 | def install_commands #:nodoc: 36 | "pkg install #{@packages.join(' ')}" 37 | end 38 | 39 | end 40 | end 41 | end 42 | -------------------------------------------------------------------------------- /lib/sprinkle/installers/package_installer.rb: -------------------------------------------------------------------------------- 1 | module Sprinkle 2 | module Installers 3 | # This is a abstract class installer that most all the package installers 4 | # inherit from (deb, *BSD pkg, rpm, etc) 5 | class PackageInstaller < Installer 6 | 7 | # holds the list of packages to be installed 8 | attr_accessor :packages 9 | 10 | def initialize(parent, *packages, &block) #:nodoc: 11 | options = packages.extract_options! 12 | super parent, options, &block 13 | @packages = [*packages].flatten 14 | end 15 | 16 | # automatically sets up the api for package installation based on the class name 17 | # 18 | # Apt becomes the method `apt`, etc 19 | def self.auto_api(*args) 20 | method_name = args.first || self.to_s.underscore.split("/").last 21 | class_name = self.to_s 22 | api do 23 | method="def #{method_name}(*names, &block) 24 | install #{class_name}.new(self, *names, &block) 25 | end" 26 | eval(method) 27 | end 28 | end 29 | 30 | # called by subclasses of PackageInstaller 31 | def install_package(*names, &block) #:nodoc: 32 | install Sprinkle::Installers::class.new(self, *names, &block) 33 | end 34 | 35 | end 36 | end 37 | end 38 | -------------------------------------------------------------------------------- /lib/sprinkle/installers/pacman.rb: -------------------------------------------------------------------------------- 1 | module Sprinkle 2 | module Installers 3 | # The pacman installer installs Pacman packages 4 | class Pacman < PackageInstaller 5 | 6 | ## 7 | # installs the Pacman packages passed 8 | # :method: pacman 9 | # :call-seq: pacman(*packages) 10 | auto_api 11 | 12 | protected 13 | 14 | def install_commands #:nodoc: 15 | "pacman -Sy #{@packages.join(' ')} --no-confirm --needed" 16 | end 17 | end 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /lib/sprinkle/installers/pear.rb: -------------------------------------------------------------------------------- 1 | module Sprinkle 2 | module Installers 3 | # = Pear package installed 4 | # 5 | # Installs the specified pear package 6 | # 7 | # == Example Usage 8 | # 9 | # package :php_stuff do 10 | # pear 'PHP_Compat' 11 | # verify { has_pear 'PHP_Compat' } 12 | # end 13 | class Pear < Installer 14 | attr_accessor :package_name 15 | 16 | api do 17 | def pear(package, &block) 18 | install Pear.new(self, package, &block) 19 | end 20 | end 21 | 22 | verify_api do 23 | def has_pear(package) 24 | @commands << "pear list | grep \"#{package}\" | grep \"stable\"" 25 | end 26 | end 27 | 28 | def initialize(parent, package_name, &block) #:nodoc: 29 | super parent, &block 30 | @package_name = package_name 31 | end 32 | 33 | protected 34 | def install_commands #:nodoc: 35 | "pear install --alldeps #{@package_name}" 36 | end 37 | end 38 | end 39 | end 40 | -------------------------------------------------------------------------------- /lib/sprinkle/installers/pecl.rb: -------------------------------------------------------------------------------- 1 | module Sprinkle 2 | module Installers 3 | # = Pecl extension installed 4 | # 5 | # Installs the specified pecl extension 6 | # 7 | # == Example Usage 8 | # 9 | # package :php_stuff do 10 | # pecl 'mongo' 11 | # verify { has_pecl 'mongo' } 12 | # end 13 | # 14 | # You can optionally pass a version number to both `pecl` and `has_pecl`: 15 | # 16 | # package :php_stuff do 17 | # pecl 'mongo', :version => "1.4.3" 18 | # verify { has_pecl 'mongo', :version => "1.4.3" } 19 | # end 20 | # 21 | # Some extensions need an ini file. You can have that generated, by passing the `:ini_file` option: 22 | # 23 | # package :php_stuff do 24 | # pecl 'mongo', :ini_file => true 25 | # end 26 | # 27 | # If you need more fine grained control of the location or contents of the ini file, use: 28 | # 29 | # package :php_stuff do 30 | # pecl 'mongo', :ini_file => { :path => "/etc/php5/apache2/php.ini", 31 | # :content => "extension=mongo.so", 32 | # :sudo => true } 33 | # end 34 | # 35 | class Pecl < Installer 36 | attr_accessor :package_name, :package_version 37 | 38 | api do 39 | def pecl(package_name, options = {}, &block) 40 | install Pecl.new(self, package_name, options, &block) 41 | end 42 | end 43 | 44 | verify_api do 45 | def has_pecl(package_name, options = {}) 46 | @commands = "TERM= pecl list | grep '^#{package_name}\\\\s*" + (options[:version] ? options[:version].to_s : "") + "'" 47 | end 48 | end 49 | 50 | def initialize(parent, package_name, options = {}, &block) #:nodoc: 51 | super parent, &block 52 | @package_name = package_name 53 | @package_version = options[:version] 54 | @ini_file = options[:ini_file] 55 | setup_ini if @ini_file 56 | end 57 | 58 | def setup_ini 59 | @ini_file = to_ini_file_hash(@ini_file) 60 | text = @ini_file[:content] || "extension=#{@package_name}.so" 61 | path = @ini_file[:path] || "/etc/php5/conf.d/#{@package_name}.ini" 62 | use_sudo = @ini_file[:sudo]===false ? false : true 63 | post(:install) do 64 | file(path, :content => text, :sudo => use_sudo) 65 | end 66 | end 67 | 68 | def to_ini_file_hash(s) 69 | return {:content => s} if s.is_a? String 70 | return {} if s===true 71 | s 72 | end 73 | 74 | protected 75 | def install_commands #:nodoc: 76 | cmd = "TERM= pecl install --alldeps #{@package_name}" 77 | cmd << "-#{@package_version}" if @package_version 78 | cmd 79 | end 80 | end 81 | end 82 | end 83 | -------------------------------------------------------------------------------- /lib/sprinkle/installers/push_text.rb: -------------------------------------------------------------------------------- 1 | module Sprinkle 2 | module Installers 3 | # Beware, strange "installer" coming your way. 4 | # 5 | # This push text installer pushes simple configuration into a file. 6 | # 7 | # == Example Usage 8 | # 9 | # Installing magic_beans into apache2.conf 10 | # 11 | # package :magic_beans do 12 | # push_text 'magic_beans', '/etc/apache2/apache2.conf' 13 | # end 14 | # 15 | # If you user has access to 'sudo' and theres a file that requires 16 | # priveledges, you can pass :sudo => true 17 | # 18 | # package :magic_beans do 19 | # push_text 'magic_beans', '/etc/apache2/apache2.conf', :sudo => true 20 | # end 21 | # 22 | # A special verify step exists for this very installer 23 | # its known as +file_contains+, it will test that a file indeed 24 | # contains a substring that you send it. 25 | # 26 | # package :magic_beans do 27 | # push_text 'magic_beans', '/etc/apache2/apache2.conf' 28 | # verify do 29 | # file_contains '/etc/apache2/apache2.conf', 'magic_beans' 30 | # end 31 | # end 32 | # 33 | class PushText < Installer 34 | attr_accessor :text, :path #:nodoc: 35 | 36 | api do 37 | def push_text(text, path, options = {}, &block) 38 | install PushText.new(self, text, path, options, &block) 39 | end 40 | end 41 | 42 | def initialize(parent, text, path, options={}, &block) #:nodoc: 43 | super parent, options, &block 44 | # by default we would not want to push the same thing over and over 45 | options.reverse_merge!(:idempotent => true) 46 | @text = text 47 | @path = path 48 | end 49 | 50 | def announce #:nodoc: 51 | log "--> Append '#{@text}' to file #{@path}" 52 | end 53 | 54 | protected 55 | 56 | def install_commands #:nodoc: 57 | escaped_text = escape_shell_arg(@text) 58 | escaped_regex = Regexp.escape(@text) 59 | command = "" 60 | command << "#{sudo_cmd}grep -qPzo '^#{escaped_regex}$' #{@path} || " if option?(:idempotent) 61 | command << "/bin/echo -e '#{escaped_text}' |#{sudo_cmd}tee -a #{@path}" 62 | command 63 | end 64 | end 65 | end 66 | end 67 | -------------------------------------------------------------------------------- /lib/sprinkle/installers/rake.rb: -------------------------------------------------------------------------------- 1 | module Sprinkle 2 | module Installers 3 | # This installer runs a rake task. 4 | # 5 | # == Example Usage 6 | # 7 | # The following example runs the command "rake spec" on 8 | # the remote server. Specify an optional Rakefile with 9 | # the :rakefile option. 10 | # 11 | # package :spec do 12 | # rake 'spec', :file => "/var/setup/Rakefile" 13 | # end 14 | class Rake < Installer 15 | 16 | api do 17 | def rake(task, options = {}, &block) 18 | install Rake.new(self, task, options, &block) 19 | end 20 | end 21 | 22 | def initialize(parent, commands, options = {}, &block) #:nodoc: 23 | super parent, options, &block 24 | @commands = commands 25 | end 26 | 27 | protected 28 | 29 | def install_commands #:nodoc: 30 | "#{executable} #{taskfile}#{@commands}" 31 | end 32 | 33 | def executable #:nodoc: 34 | "rake" 35 | end 36 | 37 | def taskfile #:nodoc: 38 | file = @options[:rakefile] || @options[:file] 39 | file ? "-f #{file} " : "" 40 | end 41 | 42 | end 43 | end 44 | end 45 | -------------------------------------------------------------------------------- /lib/sprinkle/installers/reconnect.rb: -------------------------------------------------------------------------------- 1 | module Sprinkle 2 | module Installers 3 | # Disconnects and reconnects the remote SSH session, you might want to do this 4 | # after pushing a file that would affect the local shell environment 5 | # 6 | # == Example Usage 7 | # 8 | # package :download_with_proxy do 9 | # push_text proxy_config, "/etc/environment", :sudo => true 10 | # reconnect 11 | # source "http://someurlthatneedstheproxy.com/installer.tar.gz" 12 | # end 13 | class Reconnect < Installer 14 | 15 | api do 16 | def reconnect(options={}, &block) 17 | install Sprinkle::Installers::Reconnect.new(self, options, &block) 18 | end 19 | end 20 | 21 | # :RECONNECT is a symbol that the actors understand to mean to drop 22 | # and reestablish any SSH conncetions they have open 23 | def install_commands #:nodoc: 24 | Commands::Reconnect.new() 25 | end 26 | 27 | end 28 | end 29 | end -------------------------------------------------------------------------------- /lib/sprinkle/installers/replace_text.rb: -------------------------------------------------------------------------------- 1 | module Sprinkle 2 | module Installers 3 | # = Replace text installer 4 | # 5 | # This installer replaces a text with another one in a file. 6 | # 7 | # == Example Usage 8 | # 9 | # Change ssh port in /etc/ssh/sshd_config 10 | # 11 | # package :magic_beans do 12 | # replace_text 'Port 22', 'Port 2500', '/etc/ssh/sshd_config' 13 | # end 14 | # 15 | # If you user has access to 'sudo' and theres a file that requires 16 | # privileges, you can pass :sudo => true 17 | # 18 | # package :magic_beans do 19 | # replace_text 'Port 22', 'Port 2500', '/etc/ssh/sshd_config', :sudo => true 20 | # end 21 | # 22 | # A special verify step exists for this very installer 23 | # its known as file_contains, it will test that a file indeed 24 | # contains a substring that you send it. 25 | # 26 | class ReplaceText < Installer 27 | attr_accessor :regex, :text, :path #:nodoc: 28 | 29 | api do 30 | def replace_text(regex, text, path, options={}, &block) 31 | install ReplaceText.new(self, regex, text, path, options, &block) 32 | end 33 | end 34 | 35 | def initialize(parent, regex, text, path, options={}, &block) #:nodoc: 36 | super parent, options, &block 37 | @regex = regex 38 | @text = text 39 | @path = path 40 | end 41 | 42 | def announce 43 | log "--> Replace '#{@regex}' with '#{@text}' in file #{@path}" 44 | end 45 | 46 | protected 47 | 48 | def escape_sed_arg(s) 49 | escape_shell_arg(s).gsub("/", "\\\\/").gsub('&', '\\\&') 50 | end 51 | 52 | def install_commands #:nodoc: 53 | "#{sudo_cmd}sed -i 's/#{escape_sed_arg(@regex)}/#{escape_sed_arg(@text)}/g' #{@path}" 54 | end 55 | 56 | end 57 | end 58 | end 59 | -------------------------------------------------------------------------------- /lib/sprinkle/installers/rpm.rb: -------------------------------------------------------------------------------- 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 | # verify { has_rpm 'magic_beans' } 14 | # end 15 | # 16 | # You may also specify multiple rpms as an array: 17 | # 18 | # package :magic_beans do 19 | # rpm %w(magic_beans magic_sauce) 20 | # end 21 | class Rpm < PackageInstaller 22 | 23 | ## 24 | # install RPM packages 25 | # :method: rpm 26 | # :call-seq: 27 | # rpm(*packages) 28 | auto_api 29 | 30 | verify_api do 31 | def has_rpm(package) 32 | @commands << "rpm -qa | grep #{package}" 33 | end 34 | end 35 | 36 | protected 37 | 38 | def install_commands #:nodoc: 39 | "rpm -Uvh #{@packages.join(' ')}" 40 | end 41 | 42 | end 43 | end 44 | end 45 | -------------------------------------------------------------------------------- /lib/sprinkle/installers/runner.rb: -------------------------------------------------------------------------------- 1 | module Sprinkle 2 | module Installers 3 | # The runner installer is great for running a simple command. 4 | # 5 | # == Example Usage 6 | # 7 | # package :magic_beans do 8 | # runner "make world" 9 | # end 10 | # 11 | # You can also pass multiple commands as arguments or an array. 12 | # 13 | # package :magic_beans do 14 | # runner "make world", "destroy world" 15 | # runner [ "make world", "destroy world" ] 16 | # end 17 | # 18 | # Environment variables can be supplied throught the :env option. 19 | # 20 | # package :magic_beans do 21 | # runner "make world", :env => { 22 | # :PATH => '/this/is/my/path:$PATH' 23 | # } 24 | # end 25 | # 26 | class Runner < Installer 27 | 28 | api do 29 | def runner(*cmds, &block) 30 | options = cmds.extract_options! 31 | install Runner.new(self, cmds, options, &block) 32 | end 33 | 34 | # runs 'echo noop' on the remote host 35 | def noop 36 | install Runner.new(self, "echo noop") 37 | end 38 | end 39 | 40 | attr_accessor :cmds #:nodoc: 41 | def initialize(parent, cmds, options = {}, &block) #:nodoc: 42 | super parent, options, &block 43 | @env = options.delete(:env) 44 | @cmds = [*cmds].flatten 45 | raise "you need to specify a command" if cmds.nil? 46 | end 47 | 48 | protected 49 | 50 | def env_str #:nodoc: 51 | @env_str ||= @env.inject("env ") do |s, (k,v)| 52 | s << "#{k.to_s.upcase}=#{v} " 53 | end 54 | end 55 | 56 | def install_commands #:nodoc: 57 | cmds = @env ? @cmds.map { |cmd| "#{env_str}#{cmd}"} : @cmds 58 | 59 | sudo? ? 60 | cmds.map { |cmd| "#{sudo_cmd}#{cmd}"} : 61 | cmds 62 | end 63 | end 64 | end 65 | end 66 | -------------------------------------------------------------------------------- /lib/sprinkle/installers/smart.rb: -------------------------------------------------------------------------------- 1 | module Sprinkle 2 | module Installers 3 | class Smart < PackageInstaller 4 | 5 | api do 6 | def smart(*names, &block) 7 | install Smart.new(self, *names, &block) 8 | end 9 | end 10 | 11 | protected 12 | 13 | def install_commands #:nodoc: 14 | "smart install #{@packages.join(' ')} -y 2>&1 | tee -a /var/log/smart-sprinkle" 15 | end 16 | end 17 | end 18 | end -------------------------------------------------------------------------------- /lib/sprinkle/installers/thor.rb: -------------------------------------------------------------------------------- 1 | module Sprinkle 2 | module Installers 3 | # = Thor Installer 4 | # 5 | # This installer runs a thor task. 6 | # 7 | # == Example Usage 8 | # 9 | # The following example runs the command "thor spec" on 10 | # the remote server. 11 | # 12 | # package :spec do 13 | # thor 'spec' 14 | # end 15 | # 16 | # Specify a Thorfile with the :thorfile option. 17 | # 18 | # package :spec do 19 | # thor 'spec', :file => "/var/setup/Thorfile" 20 | # end 21 | class Thor < Sprinkle::Installers::Rake 22 | 23 | api do 24 | def thor(task, options = {}, &block) 25 | install Thor.new(self, task, options, &block) 26 | end 27 | end 28 | 29 | protected 30 | 31 | def executable #:nodoc: 32 | "thor" 33 | end 34 | 35 | def taskfile #:nodoc: 36 | file = @options[:thorfile] || @options[:file] 37 | file ? "-f #{file} " : "" 38 | end 39 | 40 | end 41 | end 42 | end 43 | -------------------------------------------------------------------------------- /lib/sprinkle/installers/user.rb: -------------------------------------------------------------------------------- 1 | module Sprinkle 2 | module Installers 3 | # The user installer add users. You may pass :flags as an option. 4 | # 5 | # == Example Usage 6 | # 7 | # package :users do 8 | # add_user 'admin', :flags => "--disabled-password" 9 | # 10 | # verify do 11 | # has_user 'admin', :in_group => "root" 12 | # end 13 | # end 14 | class User < Installer 15 | 16 | api do 17 | def add_user(username, options={}, &block) 18 | install User.new(self, username, options, &block) 19 | end 20 | end 21 | 22 | verify_api do 23 | def has_user(user, opts = {}) 24 | if opts[:in_group] 25 | @commands << "id -nG #{user} | xargs -n1 echo | grep #{opts[:in_group]}" 26 | else 27 | @commands << "id #{user}" 28 | end 29 | end 30 | end 31 | 32 | def initialize(package, username, options = {}, &block) #:nodoc: 33 | super package, options, &block 34 | @username = username 35 | end 36 | 37 | protected 38 | 39 | def install_commands #:nodoc: 40 | noninteractive = " --gecos ,,," 41 | flags = @options[:flags] || "" 42 | flags << noninteractive unless flags =~ /--gecos/ 43 | "#{sudo_cmd}adduser #{flags.strip} #{@username}" 44 | end 45 | 46 | end 47 | end 48 | end 49 | -------------------------------------------------------------------------------- /lib/sprinkle/installers/yum.rb: -------------------------------------------------------------------------------- 1 | module Sprinkle 2 | module Installers 3 | # The Yum package installer installs RPM packages. 4 | # 5 | # == Example Usage 6 | # 7 | # Installing the magic_beans RPM via Yum. Its all the craze these days. 8 | # 9 | # package :magic_beans do 10 | # yum 'magic_beans', 'magic_corn' 11 | # verify do 12 | # has_yum 'magic_beans' 13 | # has_yum 'magic_corn' 14 | # end 15 | # end 16 | # 17 | # To install a specific version just add that version after the name 18 | # 19 | # package :magic_beans do 20 | # yum "magic_beans-3.0" 21 | # end 22 | class Yum < PackageInstaller 23 | 24 | ## 25 | # installs the RPM packages passed 26 | # :method: yum 27 | # :call-seq: yum(*packages) 28 | auto_api 29 | 30 | verify_api do 31 | def has_yum(package) 32 | @commands << "yum list installed #{package} | grep ^#{package}" 33 | end 34 | end 35 | 36 | protected 37 | 38 | def install_commands #:nodoc: 39 | "yum install #{@packages.join(' ')} -y" 40 | end 41 | 42 | end 43 | end 44 | end 45 | -------------------------------------------------------------------------------- /lib/sprinkle/installers/zypper.rb: -------------------------------------------------------------------------------- 1 | module Sprinkle 2 | module Installers 3 | # = Zypper Installer 4 | # 5 | # Zypper is a command-line interface to ZYpp system management library. 6 | # It mostly be used on Suse or OpenSuse. 7 | # 8 | # == Example Usage 9 | # 10 | # Installing the magic_beans package via Zypper. Its all the craze these days. 11 | # 12 | # package :magic_beans do 13 | # zypper 'magic_beans' 14 | # end 15 | # 16 | # You may also specify multiple packages as an argument list or array: 17 | # 18 | # package :magic_beans do 19 | # zypper "magic_beans", "magic_sauce" 20 | # end 21 | class Zypper < PackageInstaller 22 | 23 | ## 24 | # installs the ZYpp packages passed 25 | # :method: zypper 26 | # :call-seq: zypper(*packages) 27 | auto_api 28 | 29 | protected 30 | 31 | def install_commands #:nodoc: 32 | "zypper -n install -l -R #{@packages.join(' ')}" 33 | end 34 | end 35 | end 36 | end 37 | -------------------------------------------------------------------------------- /lib/sprinkle/package/chooser.rb: -------------------------------------------------------------------------------- 1 | module Sprinkle::Package 2 | class Chooser #:nodoc: 3 | 4 | def self.select_package(name, packages) 5 | if packages.size <= 1 6 | package = packages.first 7 | else 8 | package = choose do |menu| 9 | menu.prompt = "Multiple choices exist for virtual package #{name}" 10 | packages.each do |pkg| 11 | menu.choice(pkg.to_s) { pkg; } 12 | end 13 | end 14 | end 15 | cloud_info "Selecting #{package.to_s} for virtual package #{name}" 16 | package 17 | end 18 | 19 | def self.cloud_info(message) 20 | logger.info(message) if Sprinkle::OPTIONS[:cloud] or logger.debug? 21 | end 22 | 23 | end 24 | end 25 | -------------------------------------------------------------------------------- /lib/sprinkle/package/package_repository.rb: -------------------------------------------------------------------------------- 1 | module Sprinkle::Package 2 | class PackageRepository #:nodoc: 3 | 4 | # sets up an empty repository 5 | def initialize 6 | clear 7 | end 8 | 9 | def clear 10 | @packages = [] 11 | end 12 | 13 | # adds a single package to the repository 14 | def add(package) 15 | @packages << package 16 | end 17 | def <<(package); add(package); end 18 | 19 | # returns the first package matching the name and options given 20 | def first(name, opts={}) 21 | find_all(name, opts).try(:first) 22 | end 23 | 24 | # returns all packages matching the name and options given (including via provides) 25 | def find_all(name, opts={}) 26 | # opts ||= {} 27 | all = [@packages.select {|x| x.name.to_s == name.to_s }, 28 | find_all_by_provides(name, opts)].flatten.compact 29 | filter(all, opts) 30 | end 31 | 32 | def count 33 | @packages.size 34 | end 35 | 36 | private 37 | 38 | def find_all_by_provides(name, opts={}) 39 | @packages.select {|x| x.provides and x.provides.to_s == name.to_s } 40 | end 41 | 42 | def filter(all, opts) 43 | all = all.select {|x| "#{x.version}" == opts[:version].to_s} if opts[:version] 44 | all 45 | end 46 | 47 | end 48 | end -------------------------------------------------------------------------------- /lib/sprinkle/package/rendering.rb: -------------------------------------------------------------------------------- 1 | require 'pp' 2 | require 'erubis' 3 | require 'digest/md5' 4 | 5 | module Sprinkle::Package 6 | # For help on rendering, see the Sprinkle::Installers::FileInstaller. 7 | module Rendering 8 | extend ActiveSupport::Concern 9 | 10 | included do 11 | self.send :include, Helpers 12 | end 13 | 14 | # render src as ERB 15 | def template(src, context=binding) 16 | eruby = Erubis::Eruby.new(src) 17 | eruby.result(context) 18 | rescue Object => e 19 | raise Sprinkle::Errors::TemplateError.new(e, src, context) 20 | end 21 | 22 | # read in filename and render it as ERB 23 | def render(filename, context=binding) 24 | contents=File.read(expand_filename(filename)) 25 | template(contents, context) 26 | end 27 | 28 | # Helper methods can be called from inside your package and 29 | # verification code 30 | module Helpers 31 | # return the md5 of a string (as a hex string) 32 | def md5(s) 33 | Digest::MD5.hexdigest(s) 34 | end 35 | end 36 | 37 | # sets the path a package should use to search for templates 38 | def template_search_path(path) 39 | @template_search_path = path 40 | end 41 | 42 | private 43 | 44 | def search_paths(n) #:nodoc: 45 | # if we are given an absolute path, return just that path 46 | return [File.dirname(n)] if n.starts_with? "/" 47 | 48 | pwd = Dir.pwd 49 | package_dir = @template_search_path 50 | 51 | p = [] 52 | # if ./ is used assume the path is relative to the package 53 | if package_dir 54 | p << File.expand_path(File.join(package_dir,"templates")) 55 | p << File.expand_path(package_dir) 56 | else 57 | # otherwise search template folders relate to cwd 58 | p << File.expand_path(File.join(pwd,"templates")) 59 | p << File.expand_path(pwd) 60 | end 61 | 62 | p.uniq 63 | end 64 | 65 | def expand_filename(n) #:nodoc: 66 | name = File.basename(n) 67 | paths = search_paths(n).map do |p| 68 | [File.join(p,name), File.join(p,"#{name}.erb")] 69 | end.flatten 70 | 71 | paths.each do |f| 72 | return f if File.exist?(f) 73 | end 74 | 75 | puts "RESOLVED SEARCH PATHS" 76 | pp paths 77 | 78 | raise "template not found: #{n}" 79 | 80 | end 81 | 82 | end 83 | end 84 | -------------------------------------------------------------------------------- /lib/sprinkle/policy.rb: -------------------------------------------------------------------------------- 1 | require 'highline/import' 2 | 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 | 15 | class MissingPackageError < StandardError #:nodoc: 16 | def initialize(name) 17 | @name = name 18 | end 19 | 20 | def to_s 21 | "Package definition not found for key: #{@name}" 22 | end 23 | end 24 | 25 | # = Policies 26 | # 27 | # Policies define a set of packages which are required for a certain 28 | # role (app, database, etc.). All policies defined will be run and all 29 | # packages required by the policy will be installed. So whereas defining 30 | # a Sprinkle::Package merely defines it, defining a Sprinkle::Policy 31 | # actually causes those packages to install. 32 | # 33 | # == Example 34 | # 35 | # policy :blog, :roles => :app do 36 | # requires :webserver 37 | # requires :database 38 | # requires :rails 39 | # end 40 | # 41 | # This says that for the blog on the app role, it requires certain 42 | # packages. The :roles option is exactly the same as a capistrano 43 | # or vlad role. A role merely defines what server the commands are run 44 | # on. This way, a single Sprinkle script can provision an entire group 45 | # of servers. 46 | # 47 | # To define a role, put in your actor specific configuration file (recipe or 48 | # script file): 49 | # 50 | # role :app, "208.28.38.44" 51 | # 52 | # The capistrano and vlad syntax is the same for that. If you're using a 53 | # custom actor, you may have to do it differently. 54 | # 55 | # == Requiring a package more than once with different options 56 | # 57 | # This works exactly as you might expect: 58 | # 59 | # policy :bootstrap, :roles => :app do 60 | # require :user_settings, :for => "john" 61 | # require :user_settings, :for => "suzy" 62 | # require :user_settings, :for => "dorothy" 63 | # end 64 | # 65 | # Multiple requires for a package with no options will be 66 | # collapsed; that package will be installed once. 67 | # 68 | # policy :apache, :roles => :app do 69 | # require :devtools 70 | # ... 71 | # end 72 | # policy :git, :roles => :app do 73 | # require :devtools 74 | # ... 75 | # end 76 | # 77 | # In this example devtools will only be installed once, prior to 78 | # apache and git. 79 | # 80 | # == Multiple Policies 81 | # 82 | # You may specify as many policies as you'd like. If the packages you're 83 | # requiring are properly defined with verification blocks, then 84 | # no software will be installed twice, so you may require a webserver on 85 | # multiple packages within the same role without having to wait for 86 | # that package to install repeatedly. 87 | class Policy 88 | attr_reader :name 89 | # roles for which a policy should be installed [required] 90 | attr_reader :roles 91 | 92 | # creates a new policy, 93 | # although policies are typically not created directly but 94 | # rather via the Core#policy helper. 95 | def initialize(name, metadata = {}, &block) 96 | raise 'No name provided' unless name 97 | raise 'No roles provided' unless metadata[:roles] 98 | 99 | @name = name 100 | @roles = metadata[:roles] 101 | @packages = [] 102 | self.instance_eval(&block) 103 | end 104 | 105 | # tell a policy which packages are required 106 | def requires(package, *args) 107 | @packages << [package, args] 108 | end 109 | 110 | def packages #:nodoc: 111 | @packages.map {|x| x.first } 112 | end 113 | 114 | def to_s #:nodoc: 115 | name; end 116 | 117 | def process(deployment) #:nodoc: 118 | raise NoMatchingServersError.new(@name, @roles) unless deployment.style.servers_for_role?(@roles) 119 | 120 | logger.info "[#{name}]" 121 | 122 | package_install_tree.each do |package| 123 | package.process(deployment, @roles) 124 | end 125 | end 126 | 127 | def package_install_tree 128 | @install_tree ||= normalize(tree) 129 | end 130 | 131 | private 132 | 133 | def tree() 134 | all = [] 135 | 136 | cloud_info "--> Cloud hierarchy for policy #{@name}" 137 | 138 | @packages.each do |p, args| 139 | cloud_info " * requires package #{p}" 140 | 141 | opts = args.clone.extract_options! 142 | package = Sprinkle::Package::PACKAGES.find_all(p, opts) 143 | raise MissingPackageError.new(p) unless package.any? 144 | package = Sprinkle::Package::Chooser.select_package(p, package) if package.is_a? Array # handle virtual package selection 145 | # get an instance of the package and pass our config options 146 | package = package.instance(*args) 147 | tree = package.tree do |parent, child, depth| 148 | indent = "\t" * depth; cloud_info "#{indent}Package #{parent.name} requires #{child.name}" 149 | end 150 | 151 | all << tree 152 | end 153 | all 154 | 155 | end 156 | 157 | def normalize(all, &block) 158 | all = all.flatten.uniq {|x| [x.name, x.version, x.opts] } 159 | cloud_info "--> Normalized installation order for all packages: #{all.collect(&:name).join(', ')}\n" 160 | all 161 | end 162 | 163 | def cloud_info(message) 164 | logger.info(message) if Sprinkle::OPTIONS[:cloud] or logger.debug? 165 | end 166 | 167 | end 168 | end 169 | -------------------------------------------------------------------------------- /lib/sprinkle/script.rb: -------------------------------------------------------------------------------- 1 | module Sprinkle 2 | # = Scripting 3 | # 4 | # Script gives you a way to programatically run a given 5 | # sprinkle script. 6 | class Script 7 | include Sprinkle::Deployment 8 | 9 | def initialize 10 | @deployment = nil 11 | end 12 | 13 | # Run a given sprinkle script. This method is blocking so 14 | # it will not return until the sprinkling is complete or fails. 15 | #-- 16 | # FIXME: Improve documentation, possibly notify user how to tell 17 | # if a sprinkling failed. 18 | #++ 19 | def self.sprinkle(script, filename = '__SCRIPT__') 20 | powder = new 21 | powder.instance_eval script, filename 22 | powder.sprinkle 23 | end 24 | 25 | def sprinkle #:nodoc: 26 | @deployment.process if @deployment 27 | end 28 | end 29 | end 30 | -------------------------------------------------------------------------------- /lib/sprinkle/utility/log_recorder.rb: -------------------------------------------------------------------------------- 1 | module Sprinkle 2 | module Utility #:nodoc: 3 | class LogRecorder #:nodoc: 4 | 5 | attr_accessor :err, :out, :command, :code 6 | 7 | def initialize(cmd=nil) 8 | reset(cmd) 9 | end 10 | 11 | def log(stream, data) 12 | case stream 13 | when :err then @err << data 14 | when :out then @out << data 15 | end 16 | end 17 | 18 | # hash suitable to pass into a pretty failure details hash 19 | def hash 20 | {:error => err, :stdout => out, :command => command, :code => code} 21 | end 22 | 23 | def reset(cmd=nil) 24 | @command=cmd 25 | @code=nil 26 | @err="" 27 | @out="" 28 | end 29 | 30 | end 31 | 32 | end 33 | end -------------------------------------------------------------------------------- /lib/sprinkle/verifiers/executable.rb: -------------------------------------------------------------------------------- 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 path is an executable script using which 22 | # - accepts both absolute paths and binary names with no path 23 | def has_executable(path) 24 | @commands << "which #{path}" 25 | end 26 | 27 | # Same as has_executable but with checking for e certain version number. 28 | # Last option is the parameter to append for getting the version (which 29 | # defaults to "-v"). 30 | def has_executable_with_version(path, version, get_version = '-v') 31 | if path.include?('/') 32 | @commands << "[ -x #{path} -a -n \"`#{path} #{get_version} 2>&1 | egrep -e \\\"#{version}\\\"`\" ]" 33 | else 34 | @commands << "[ -n \"`echo \\`which #{path}\\``\" -a -n \"`\\`which #{path}\\` #{get_version} 2>&1 | egrep -e \\\"#{version}\\\"`\" ]" 35 | end 36 | end 37 | 38 | # Same as has_executable but checking output of a certain command 39 | # with grep. 40 | def has_version_in_grep(cmd, version) 41 | @commands << "[ -n \"`#{cmd} 2> /dev/null | egrep -e \\\"#{version}\\\"`\" ]" 42 | end 43 | end 44 | end 45 | end -------------------------------------------------------------------------------- /lib/sprinkle/verifiers/file.rb: -------------------------------------------------------------------------------- 1 | module Sprinkle 2 | module Verifiers 3 | # = File Verifier 4 | # 5 | # Contains a verifier to check the existance of a file. 6 | # 7 | # == Example Usage 8 | # 9 | # verify { has_file '/etc/apache2/apache2.conf' } 10 | # 11 | # verify { file_contains '/etc/apache2/apache2.conf', 'mod_gzip'} 12 | # 13 | module File 14 | Sprinkle::Verify.register(Sprinkle::Verifiers::File) 15 | 16 | # tests that the file path exists 17 | def has_file(path) 18 | test "-f #{path}" 19 | end 20 | 21 | # Tests that the directory dir exists. 22 | def has_directory(dir) 23 | test "-d #{dir}" 24 | end 25 | 26 | # Checks that symlink is a symbolic link. If file is 27 | # given, it checks that symlink points to file 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 | 36 | def no_file(path) 37 | test "! -f #{path}" 38 | end 39 | 40 | def md5_of_file(path, md5) 41 | test "\"`#{sudo_cmd}md5sum #{path} | cut -f1 -d' '`\" = \"#{md5}\"" 42 | end 43 | 44 | def sha1_of_file(path, sha1) 45 | test "\"`#{sudo_cmd}sha1sum #{path} | cut -f1 -d' '`\" = \"#{sha1}\"" 46 | end 47 | 48 | def file_contains(path, text) 49 | @commands << "grep '#{text}' #{path}" 50 | end 51 | 52 | # TODO: remove 0.9 53 | def user_present(username) 54 | ActiveSupport::Deprecation.warn("user_present is depreciated. Use has_user instead.") 55 | has_user username 56 | end 57 | 58 | def matches_local(localfile, remotefile, mode=nil) 59 | raise "Couldn't find local file #{localfile}" unless ::File.exists?(localfile) 60 | require 'digest/md5' 61 | local = Digest::MD5.hexdigest(::File.read(localfile)) 62 | md5_of_file remotefile, local 63 | end 64 | end 65 | end 66 | end 67 | -------------------------------------------------------------------------------- /lib/sprinkle/verifiers/package.rb: -------------------------------------------------------------------------------- 1 | # TODO: remove 2 | module Sprinkle 3 | module Verifiers 4 | module Package #:nodoc: 5 | Sprinkle::Verify.register(Sprinkle::Verifiers::Package) 6 | 7 | def has_package(*packages) 8 | puts "has_package and has_packages are depreciated" 9 | raise "please use has_yum and friends instead" 10 | end 11 | 12 | alias_method :has_packages, :has_package 13 | end 14 | end 15 | end -------------------------------------------------------------------------------- /lib/sprinkle/verifiers/permission.rb: -------------------------------------------------------------------------------- 1 | module Sprinkle 2 | module Verifiers 3 | # = Permission and ownership Verifier 4 | # 5 | # Contains a verifier to check the permissions and ownership of a file or directory. 6 | # 7 | # == Example Usage 8 | # 9 | # verify { has_permission '/etc/apache2/apache2.conf', 0644 } 10 | # 11 | # verify { belongs_to_user '/etc/apache2/apache2.conf', 'noop' } 12 | # 13 | # verify { belongs_to_user '/etc/apache2/apache2.conf', 1000 } 14 | # 15 | module Permission 16 | Sprinkle::Verify.register(Sprinkle::Verifiers::Permission) 17 | 18 | def has_permission(path, permission) 19 | @commands << "find #{path} -maxdepth 0 -perm #{permission} | egrep '.*'" 20 | end 21 | 22 | def belongs_to_user(path, user) 23 | arg = user.is_a?(Integer) ? "-uid" : "-user" 24 | @commands << "find #{path} -maxdepth 0 #{arg} #{user} | egrep '.*'" 25 | end 26 | 27 | def belongs_to_group(path, group) 28 | arg = group.is_a?(Integer) ? "-gid" : "-group" 29 | @commands << "find #{path} -maxdepth 0 #{arg} #{group} | egrep '.*'" 30 | end 31 | 32 | end 33 | end 34 | end 35 | -------------------------------------------------------------------------------- /lib/sprinkle/verifiers/process.rb: -------------------------------------------------------------------------------- 1 | module Sprinkle 2 | module Verifiers 3 | # = Process Verifier 4 | # 5 | # Contains a verifier to check that a process is running. 6 | # 7 | # == Example Usage 8 | # 9 | # verify { has_process 'httpd' } 10 | # 11 | module Process 12 | Sprinkle::Verify.register(Sprinkle::Verifiers::Process) 13 | 14 | # Checks to make sure process is a process running 15 | # on the remote server. 16 | def has_process(process) 17 | @commands << "ps -C #{process}" 18 | end 19 | end 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /lib/sprinkle/verifiers/ruby.rb: -------------------------------------------------------------------------------- 1 | module Sprinkle 2 | module Verifiers 3 | # = Ruby Verifiers 4 | # 5 | # The verifiers in this module are ruby specific. 6 | module Ruby 7 | Sprinkle::Verify.register(Sprinkle::Verifiers::Ruby) 8 | 9 | # Checks if ruby can require the files given. rubygems 10 | # is always included first. 11 | def ruby_can_load(*files) 12 | # Always include rubygems first 13 | files = files.unshift('rubygems').collect { |x| "require '#{x}'" } 14 | 15 | @commands << "ruby -e \"#{files.join(';')}\"" 16 | end 17 | 18 | # Checks if a gem exists by calling "gem list" and grepping against it. 19 | def has_gem(name, version = nil) 20 | version = version ? "--version '#{version}'" : '' 21 | @commands << "gem list '#{name}' --installed #{version} > /dev/null" 22 | end 23 | end 24 | end 25 | end -------------------------------------------------------------------------------- /lib/sprinkle/verifiers/test.rb: -------------------------------------------------------------------------------- 1 | module Sprinkle 2 | module Verifiers 3 | # = Test Verifier 4 | # 5 | # Checks that a specific test runs successfully (using the unix test command) 6 | # 7 | # == Example Usage 8 | # 9 | # verify { test '-f /some_file' } 10 | # 11 | module Test 12 | Sprinkle::Verify.register(Sprinkle::Verifiers::Test) 13 | 14 | # Checks to make sure a test runs successfully on the remote server 15 | def test(args) 16 | @commands << "test #{args}" 17 | end 18 | 19 | end 20 | end 21 | end -------------------------------------------------------------------------------- /lib/sprinkle/verify.rb: -------------------------------------------------------------------------------- 1 | module Sprinkle 2 | # = Verify Blocks 3 | # 4 | # As documented in Sprinkle::Package, you may define a block on a package 5 | # which verifies that a package was installed correctly. If this verification 6 | # block fails, Sprinkle will stop the script gracefully, raising the error. 7 | # 8 | # In addition to checking post install if it was successfully, verification 9 | # blocks are also run before an install to see if a package is already 10 | # installed. If this is the case, the package is skipped and Sprinkle continues 11 | # with the next package. This behavior can be overriden by setting the -f flag on 12 | # the sprinkle script or setting Sprinkle::OPTIONS[:force] to true if you're 13 | # using sprinkle programmatically. 14 | # 15 | # == An Example 16 | # 17 | # The following verifies that rails was installed correctly be checking to see 18 | # if the 'rails' command is available on the command line: 19 | # 20 | # package :rails do 21 | # gem 'rails' 22 | # 23 | # verify do 24 | # has_executable 'rails' 25 | # end 26 | # end 27 | # 28 | # == Available Verifiers 29 | # 30 | # There are a variety of available methods for use in the verification block. 31 | # The standard methods are defined in the Sprinkle::Verifiers module, so see 32 | # their corresponding documentation. 33 | # 34 | # == Custom Verifiers 35 | # 36 | # If you feel that the built-in verifiers do not offer a certain aspect of 37 | # verification which you need, you may create your own verifier! Simply wrap 38 | # any method in a module which you want to use: 39 | # 40 | # module MagicBeansVerifier 41 | # def has_magic_beans(sauce) 42 | # @commands << '[ -z "`echo $' + sauce + '`"]' 43 | # end 44 | # end 45 | # 46 | # The method can append as many commands as it wishes to the @commands array. 47 | # These commands will be run on the remote server and MUST give an 48 | # exit status of 0 if successful or other if unsuccessful. 49 | # 50 | # To register your verifier, call the register method on Sprinkle::Verify: 51 | # 52 | # Sprinkle::Verify.register(MagicBeansVerifier) 53 | # 54 | # And now you may use it like any other verifier: 55 | # 56 | # package :magic_beans do 57 | # gem 'magic_beans' 58 | # 59 | # verify { has_magic_beans('ranch') } 60 | # end 61 | class Verify 62 | include Sprinkle::Attributes 63 | include Sprinkle::Package::Rendering::Helpers 64 | include Sprinkle::Sudo 65 | attr_accessor :package, :description, :options #:nodoc: 66 | 67 | delegate :opts, :to => :package 68 | delegate :args, :to => :package 69 | delegate :version, :to => :package 70 | delegate :description, :to => :package 71 | 72 | class < Verifying #{description}..." 128 | 129 | unless @delivery.verify(self, roles) 130 | # Verification failed, halt sprinkling gracefully. 131 | raise Sprinkle::VerificationFailed.new(@package, description) 132 | end 133 | end 134 | end 135 | end 136 | 137 | class VerificationFailed < Exception #:nodoc: 138 | attr_accessor :package, :description 139 | 140 | def initialize(package, description) 141 | super("Verifying #{package.name}#{description} failed.") 142 | 143 | @package = package 144 | @description = description 145 | end 146 | end 147 | end 148 | -------------------------------------------------------------------------------- /lib/sprinkle/version.rb: -------------------------------------------------------------------------------- 1 | module Sprinkle 2 | Version = "0.7.7" 3 | end -------------------------------------------------------------------------------- /script/console: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # File: script/console 3 | irb = RUBY_PLATFORM =~ /(:?mswin|mingw)/ ? 'irb.bat' : 'irb' 4 | 5 | libs = " -r irb/completion" 6 | libs << " -r #{File.dirname(__FILE__) + '/../lib/sprinkle.rb'}" 7 | puts "Loading sprinkle gem" 8 | exec "#{irb} #{libs} --simple-prompt" -------------------------------------------------------------------------------- /script/destroy: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | APP_ROOT = File.expand_path(File.join(File.dirname(__FILE__), '..')) 3 | 4 | begin 5 | require 'rubigen' 6 | rescue LoadError 7 | require 'rubygems' 8 | require 'rubigen' 9 | end 10 | require 'rubigen/scripts/destroy' 11 | 12 | ARGV.shift if ['--help', '-h'].include?(ARGV[0]) 13 | RubiGen::Base.use_component_sources! [:rubygems, :newgem, :newgem_theme, :test_unit] 14 | RubiGen::Scripts::Destroy.new.run(ARGV) 15 | -------------------------------------------------------------------------------- /script/generate: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | APP_ROOT = File.expand_path(File.join(File.dirname(__FILE__), '..')) 3 | 4 | begin 5 | require 'rubigen' 6 | rescue LoadError 7 | require 'rubygems' 8 | require 'rubigen' 9 | end 10 | require 'rubigen/scripts/generate' 11 | 12 | ARGV.shift if ['--help', '-h'].include?(ARGV[0]) 13 | RubiGen::Base.use_component_sources! [:rubygems, :newgem, :newgem_theme, :test_unit] 14 | RubiGen::Scripts::Generate.new.run(ARGV) 15 | -------------------------------------------------------------------------------- /spec/fixtures/my_file.txt: -------------------------------------------------------------------------------- 1 | ... 2 | -------------------------------------------------------------------------------- /spec/spec.opts: -------------------------------------------------------------------------------- 1 | --colour -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | $:.unshift(File.dirname(__FILE__) + '/../lib') 2 | require 'sprinkle' 3 | 4 | module Sprinkle 5 | module TestLogger 6 | def logger 7 | # ActiveSupport::BufferedLogger was deprecated and replaced by ActiveSupport::Logger in Rails 4. 8 | # Use ActiveSupport::Logger if available. 9 | active_support_logger = defined?(ActiveSupport::Logger) ? ActiveSupport::Logger : ActiveSupport::BufferedLogger 10 | @@__log_file__ ||= StringIO.new 11 | @@__log__ = active_support_logger.new @@__log_file__, active_support_logger::Severity::INFO 12 | end 13 | end 14 | end 15 | 16 | class Object 17 | include Sprinkle::TestLogger 18 | end 19 | 20 | ActiveSupport::Deprecation.silenced = true 21 | -------------------------------------------------------------------------------- /spec/sprinkle/actors/local_spec.rb: -------------------------------------------------------------------------------- 1 | require File.expand_path("../../spec_helper", File.dirname(__FILE__)) 2 | 3 | describe Sprinkle::Actors::Local do 4 | 5 | before do 6 | @local = Sprinkle::Actors::Local.new 7 | 8 | @package = Package.new("super") {} 9 | end 10 | 11 | describe 'when installing' do 12 | 13 | before do 14 | @installer = Sprinkle::Installers::Runner.new(@package, "echo hi") 15 | @commands = %w( op1 op2 ) 16 | @roles = %w( app ) 17 | @name = 'name' 18 | 19 | @local.stub(:run_command).and_return(0) 20 | end 21 | 22 | it 'should run the commands on the local system' do 23 | @local.should_receive(:run_command).once.and_return(0) 24 | @local.install @installer, @roles 25 | end 26 | 27 | end 28 | 29 | describe 'when verifying' do 30 | 31 | before do 32 | @verifier = Sprinkle::Verify::new(@package) {} 33 | @verifier.commands.concat ["test","test"] 34 | @roles = %w( app ) 35 | @name = 'name' 36 | end 37 | 38 | it 'should return false when verification fails' do 39 | @local.stub(:run_command).and_return(1) 40 | res = @local.verify @verifier, @roles 41 | res.should == false 42 | end 43 | 44 | it 'should run the commands on the local system' do 45 | @local.stub(:run_command).and_return(0) 46 | res = @local.verify @verifier, @roles 47 | res.should == true 48 | end 49 | 50 | end 51 | 52 | end 53 | -------------------------------------------------------------------------------- /spec/sprinkle/actors/ssh_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe Sprinkle::Actors::SSH do 4 | describe 'process' do 5 | before do 6 | subject.stub(:gateway_defined?).and_return(false) 7 | end 8 | 9 | subject do 10 | Sprinkle::Actors::SSH.new do 11 | role :app, "booger.com" 12 | end 13 | end 14 | 15 | let(:commands) { %w[one two three] } 16 | let(:roles) { %w[app] } 17 | 18 | describe 'when use_sudo is true' do 19 | before do 20 | subject.use_sudo(true) 21 | end 22 | 23 | it 'prepends "sudo" to each command' do 24 | subject.send(:prepare_commands,commands).should == ['sudo one', 'sudo two', 'sudo three'] 25 | end 26 | end 27 | 28 | describe 'when use_sudo is false' do 29 | before do 30 | subject.use_sudo(false) 31 | end 32 | 33 | it 'does not prepend "sudo" to each command' do 34 | subject.send(:prepare_commands,commands).should == commands 35 | end 36 | end 37 | end 38 | end 39 | -------------------------------------------------------------------------------- /spec/sprinkle/deployment_spec.rb: -------------------------------------------------------------------------------- 1 | require File.expand_path("../spec_helper", File.dirname(__FILE__)) 2 | 3 | describe Sprinkle::Deployment do 4 | include Sprinkle::Deployment 5 | 6 | def create_deployment(&block) 7 | deployment do 8 | delivery :capistrano, &block 9 | 10 | source do 11 | prefix '/usr/local' 12 | end 13 | end 14 | end 15 | 16 | describe 'when created' do 17 | 18 | it 'should be invalid without a block descriptor' do 19 | lambda { deployment }.should raise_error 20 | end 21 | 22 | it 'should be invalid without a delivery method' do 23 | lambda { @deployment = deployment do; end }.should raise_error 24 | end 25 | 26 | it 'should optionally accept installer defaults' do 27 | @deployment = create_deployment 28 | @deployment.source do; end 29 | @deployment.defaults.keys.should == [:source] 30 | end 31 | 32 | it 'should provide installer defaults as a proc when requested' do 33 | @deployment = create_deployment 34 | @deployment.defaults[:source].class.should == Proc 35 | end 36 | 37 | end 38 | 39 | describe 'delivery specification' do 40 | 41 | before do 42 | @actor = double(Sprinkle::Actors::Capistrano) 43 | Sprinkle::Actors::Capistrano.stub(:new).and_return(@actor) 44 | end 45 | 46 | it 'should automatically instantiate the delivery type' do 47 | @deployment = create_deployment 48 | @deployment.style.should == @actor 49 | end 50 | 51 | it 'should optionally accept a block to pass to the actor' do 52 | lambda { @deployment = create_deployment }.should_not raise_error 53 | end 54 | 55 | describe 'with a block' do 56 | 57 | it 'should pass the block to the actor for configuration' do 58 | @deployment = create_deployment do; recipes 'deploy'; end 59 | end 60 | 61 | end 62 | end 63 | 64 | describe 'when processing policies' do 65 | 66 | before do 67 | @policy = double(Sprinkle::Policy, :process => true) 68 | Sprinkle::POLICIES.clear 69 | Sprinkle::POLICIES << @policy 70 | @deployment = create_deployment 71 | end 72 | 73 | it 'should apply all policies, passing itself as the deployment context' do 74 | @policy.should_receive(:process).with(@deployment).and_return 75 | end 76 | 77 | after do 78 | @deployment.process 79 | end 80 | end 81 | 82 | end 83 | -------------------------------------------------------------------------------- /spec/sprinkle/extensions/array_spec.rb: -------------------------------------------------------------------------------- 1 | require File.expand_path("../../spec_helper", File.dirname(__FILE__)) 2 | 3 | describe Array, 'task name conversions' do 4 | 5 | it 'should be able to deliver a task name' do 6 | ['build_essential'].to_task_name.should == 'build_essential' 7 | end 8 | 9 | it 'should join multiple elements together with a _ char' do 10 | ['gdb', 'gcc', 'g++'].to_task_name.should == 'gdb_gcc_g++' 11 | end 12 | 13 | it 'should use the task name of the underlying array element' do 14 | string = 'build-essential' 15 | string.should_receive(:to_task_name).and_return('build_essential') 16 | [string].to_task_name.should == 'build_essential' 17 | end 18 | 19 | end 20 | -------------------------------------------------------------------------------- /spec/sprinkle/extensions/rendering_spec.rb: -------------------------------------------------------------------------------- 1 | require File.expand_path("../../spec_helper", File.dirname(__FILE__)) 2 | 3 | describe Sprinkle::Package::Rendering, 'rendering' do 4 | 5 | before do 6 | @root = File.expand_path(File.join(File.dirname(__FILE__), "../..")) 7 | @package = package :something do 8 | end 9 | end 10 | 11 | describe "path expansion" do 12 | 13 | it "should know / is root" do 14 | dirs = @package.send :search_paths, "/test/file" 15 | dirs.should eq ["/test"] 16 | end 17 | 18 | it "./ is local to where we tell it to be" do 19 | Dir.stub(:pwd).and_return("/path/is/") 20 | @package.template_search_path "/my/super/package/" 21 | dirs = @package.send :search_paths, "./test/file" 22 | dirs.should include("/my/super/package") 23 | dirs.should include("/my/super/package/templates") 24 | dirs.should_not include("/path/is") 25 | dirs.should_not include("/path/is/templates") 26 | end 27 | 28 | it "should search pwd when amgiguous" do 29 | Dir.stub(:pwd).and_return("/path/is/") 30 | dirs = @package.send :search_paths, "test/file" 31 | dirs.should include("/path/is") 32 | dirs.should include("/path/is/templates") 33 | dirs.size.should eq 2 34 | end 35 | 36 | end 37 | 38 | it "should be able to calculate md5s" do 39 | @package.md5("test").should == "098f6bcd4621d373cade4e832627b4f6" 40 | end 41 | 42 | it "should allow passing locals to template" do 43 | t = @package.template("hello <%= world %>", :world => "world") 44 | t.should == "hello world" 45 | end 46 | 47 | it "should allow access to the package context by default" do 48 | @package = package :new do 49 | @wowser = "wowser" 50 | end.instance 51 | @package.opts[:world]="world" 52 | t=@package.template("hello <%= opts[:world] %> <%= @wowser %>") 53 | t.should == "hello world wowser" 54 | end 55 | 56 | it "should be able to render a file from templates" do 57 | Dir.chdir(@root) do 58 | t = @package.render("test") 59 | t.should == "hello " 60 | end 61 | end 62 | 63 | it "should be able to render a file from absolute path" do 64 | path = File.join(@root, "templates/test.erb") 65 | t = @package.render(path) 66 | t.should == "hello " 67 | end 68 | 69 | it "should accept binding as second argument" do 70 | path = File.join(@root, "templates/locals.erb") 71 | t = @package.render(path, :world => "world") 72 | t.should == "hello world" 73 | end 74 | 75 | end 76 | -------------------------------------------------------------------------------- /spec/sprinkle/extensions/string_spec.rb: -------------------------------------------------------------------------------- 1 | require File.expand_path("../../spec_helper", File.dirname(__FILE__)) 2 | 3 | describe String, 'task name conversions' do 4 | 5 | it 'should be able to deliver a task name' do 6 | 'build_essential'.to_task_name.should == 'build_essential' 7 | end 8 | 9 | it 'should convert all - chars to _ in the task name' do 10 | 'build-essential'.to_task_name.should == 'build_essential' 11 | end 12 | 13 | it 'should convert multiple - chars to _ chars in the task name' do 14 | 'build--essential'.to_task_name.should == 'build__essential' 15 | end 16 | 17 | it 'should lowercase the task name' do 18 | 'BUILD-ESSENTIAL'.to_task_name.should == 'build_essential' 19 | end 20 | 21 | end -------------------------------------------------------------------------------- /spec/sprinkle/installers/apt_spec.rb: -------------------------------------------------------------------------------- 1 | require File.expand_path("../../spec_helper", File.dirname(__FILE__)) 2 | 3 | describe Sprinkle::Installers::Apt do 4 | 5 | before do 6 | @package = create_pkg "name", :use_sudo => false 7 | end 8 | 9 | def create_pkg(name="name", opts={}) 10 | @package = Sprinkle::Package::Package.new(name) {} 11 | @package.use_sudo opts[:use_sudo] 12 | @package 13 | end 14 | 15 | def create_apt(*debs, &block) 16 | @package.apt(*debs, &block) 17 | end 18 | 19 | describe 'when created' do 20 | 21 | it 'should accept a single package to install' do 22 | @installer = create_apt 'ruby' 23 | @installer.packages.should == [ 'ruby' ] 24 | end 25 | 26 | it 'should accept an array of packages to install' do 27 | @installer = create_apt %w( gcc gdb g++ ) 28 | @installer.packages.should == ['gcc', 'gdb', 'g++'] 29 | end 30 | 31 | it 'should remove options from packages list' do 32 | @installer = create_apt 'ruby', :dependencies_only => true 33 | @installer.packages.should == [ 'ruby' ] 34 | end 35 | 36 | end 37 | 38 | 39 | describe 'during installation' do 40 | 41 | before do 42 | @installer = create_apt 'ruby' do 43 | pre :install, 'op1' 44 | post :install, 'op2' 45 | end 46 | @install_commands = @installer.send :install_commands 47 | end 48 | 49 | it 'should use sudo if package specifies' do 50 | @package = create_pkg "name", :use_sudo => true 51 | @installer = create_apt 'ruby' 52 | @install_commands = @installer.send :install_commands 53 | @install_commands.should =~ /sudo env/ 54 | end 55 | 56 | it 'should use sudo if installer specifies' do 57 | @package = create_pkg "name", :use_sudo => false 58 | @installer = create_apt 'ruby', :sudo => true 59 | @install_commands = @installer.send :install_commands 60 | @install_commands.should =~ /sudo env/ 61 | end 62 | 63 | it 'should invoke the apt installer for all specified packages' do 64 | @install_commands.should =~ /apt-get --force-yes -qyu install ruby/ 65 | end 66 | 67 | it 'should specify a non interactive mode to the apt installer' do 68 | @install_commands.should =~ /env DEBCONF_TERSE='yes' DEBIAN_PRIORITY='critical' DEBIAN_FRONTEND=noninteractive/ 69 | end 70 | 71 | it 'should automatically insert pre/post commands for the specified package' do 72 | @installer.send(:install_sequence).should == [ 'op1', %(env DEBCONF_TERSE='yes' DEBIAN_PRIORITY='critical' DEBIAN_FRONTEND=noninteractive apt-get --force-yes -qyu install ruby), 'op2' ] 73 | end 74 | 75 | it 'should install a specific version if defined' do 76 | @installer = create_apt 'ruby=2' 77 | @installer.send(:install_sequence).should == [ %(env DEBCONF_TERSE='yes' DEBIAN_PRIORITY='critical' DEBIAN_FRONTEND=noninteractive apt-get --force-yes -qyu install ruby=2)] 78 | end 79 | 80 | end 81 | 82 | describe 'during dependencies only installation' do 83 | 84 | before do 85 | @installer = create_apt('ruby') { dependencies_only true } 86 | @install_commands = @installer.send :install_commands 87 | end 88 | 89 | it 'should invoke the apt installer with build-dep command for all specified packages' do 90 | @install_commands.should =~ /apt-get --force-yes -qyu build-dep ruby/ 91 | end 92 | 93 | end 94 | 95 | end 96 | -------------------------------------------------------------------------------- /spec/sprinkle/installers/binary_spec.rb: -------------------------------------------------------------------------------- 1 | require File.expand_path("../../spec_helper", File.dirname(__FILE__)) 2 | 3 | describe Sprinkle::Installers::Binary do 4 | include Sprinkle::Deployment 5 | 6 | def create_context(source = 'http://www.example.com/archive.tar.gz', sudo = false) 7 | deployment = deployment do 8 | delivery :capistrano 9 | binary source do 10 | prefix '/prefix/directory' 11 | archives '/archives/directory' 12 | end 13 | end 14 | 15 | installer = create_binary source, sudo do 16 | prefix '/prefix/directory' 17 | archives '/archives/directory' 18 | end 19 | 20 | installer.defaults(@deployment) 21 | 22 | [source, deployment, installer] 23 | end 24 | 25 | def create_binary(binary, sudo, version = nil, &block) 26 | @package ||= double(Sprinkle::Package, :name => 'package', :version => version, :sudo? => sudo) 27 | Sprinkle::Installers::Binary.new(@package, binary, &block) 28 | end 29 | 30 | describe "binary#prepare_commands" do 31 | before do 32 | @binary, @deployment, @installer = create_context 33 | end 34 | 35 | it "should return mkdir command to create the prefix directory" do 36 | @installer.send(:prepare_commands)[0].should == 'mkdir -p /prefix/directory' 37 | end 38 | it "should return mkdir command to create the archives directory" do 39 | @installer.send(:prepare_commands)[1].should == 'mkdir -p /archives/directory' 40 | end 41 | end 42 | 43 | 44 | describe "binary#install_commands" do 45 | before do 46 | @binary, @deployment, @installer = create_context 47 | end 48 | 49 | it "should return a commands to place the binary in the correct archive directory" do 50 | @installer.send(:install_commands)[0].should =~ /--directory-prefix=\/archives\/directory/ 51 | end 52 | 53 | it "should return a command to extract to the correct prefix folder" do 54 | @installer.send(:install_commands)[1].should =~ /cd \/prefix\/directory/ 55 | end 56 | 57 | it "should return a command to extract the right file in the right directory" do 58 | @installer.send(:install_commands)[1].should =~ / '\/archives\/directory\/archive.tar.gz'/ 59 | end 60 | end 61 | 62 | describe "binary#install_commands", "with sudo support" do 63 | before do 64 | @binary, @deployment, @installer = create_context 'http://www.example.com/archive.tar.gz', true 65 | end 66 | 67 | it "should sudo the command when required" do 68 | @installer.send(:install_commands)[1].should =~ /sudo/ 69 | end 70 | end 71 | 72 | describe "when source contains spaces (%20's) in path" do 73 | before do 74 | _, _, @installer = create_context('http://c758482.r82.cf2.rackcdn.com/Sublime%20Text%202.0.1%20x64.tar.bz2') 75 | end 76 | 77 | it "should correctly interpret the archive filename as it gets extracted downloaded to file system" do 78 | @installer.send(:install_commands)[1].should =~ / '\/archives\/directory\/Sublime Text 2.0.1 x64.tar.bz2'/ 79 | end 80 | end 81 | 82 | end 83 | -------------------------------------------------------------------------------- /spec/sprinkle/installers/brew_spec.rb: -------------------------------------------------------------------------------- 1 | require File.dirname(__FILE__) + '/../../spec_helper' 2 | 3 | describe Sprinkle::Installers::Brew do 4 | 5 | before do 6 | @formula = double(Sprinkle::Package, :name => 'formula', :sudo? => false) 7 | end 8 | 9 | def create_brew(*formulas, &block) 10 | Sprinkle::Installers::Brew.new(@formula, *formulas, &block) 11 | end 12 | 13 | describe 'when created' do 14 | 15 | it 'should accept a single package to install' do 16 | @installer = create_brew 'ruby' 17 | @installer.packages.should == [ 'ruby' ] 18 | end 19 | 20 | it 'should accept an array of packages to install' do 21 | @installer = create_brew %w( gcc gdb g++ ) 22 | @installer.packages.should == ['gcc', 'gdb', 'g++'] 23 | end 24 | 25 | it 'should remove options from packages list' do 26 | @installer = create_brew 'ruby' 27 | @installer.packages.should == [ 'ruby' ] 28 | end 29 | 30 | end 31 | 32 | describe 'during installation' do 33 | 34 | before do 35 | @installer = create_brew 'ruby' do 36 | pre :install, 'op1' 37 | post :install, 'op2' 38 | end 39 | @install_commands = @installer.send :install_commands 40 | end 41 | 42 | it 'should invoke the apt installer for all specified packages' do 43 | @install_commands.should =~ /brew install ruby/ 44 | end 45 | 46 | it 'should automatically insert pre/post commands for the specified package' do 47 | @installer.send(:install_sequence).should == [ 'op1', %(brew install ruby), 'op2' ] 48 | end 49 | 50 | end 51 | 52 | # describe 'during dependencies only installation' do 53 | # 54 | # before do 55 | # @installer = create_apt('ruby') { dependencies_only true } 56 | # @install_commands = @installer.send :install_commands 57 | # end 58 | # 59 | # it 'should invoke the apt installer with build-dep command for all specified packages' do 60 | # @install_commands.should =~ /apt-get --force-yes -qyu build-dep ruby/ 61 | # end 62 | # 63 | # end 64 | end -------------------------------------------------------------------------------- /spec/sprinkle/installers/bsd_port_spec.rb: -------------------------------------------------------------------------------- 1 | require File.expand_path("../../spec_helper", File.dirname(__FILE__)) 2 | 3 | describe Sprinkle::Installers::BsdPort do 4 | 5 | before do 6 | @package = double(Sprinkle::Package, :name => 'package') 7 | end 8 | 9 | def create_port(ports, &block) 10 | Sprinkle::Installers::BsdPort.new(@package, ports, &block) 11 | end 12 | 13 | describe 'when created' do 14 | 15 | it 'should accept a single package to install' do 16 | @installer = create_port 'lang/ruby' 17 | @installer.port.should == 'lang/ruby' 18 | end 19 | 20 | end 21 | 22 | describe 'during installation' do 23 | 24 | before do 25 | @installer = create_port 'lang/ruby' do 26 | pre :install, 'op1' 27 | post :install, 'op2' 28 | end 29 | @install_commands = @installer.send :install_commands 30 | end 31 | 32 | it 'should invoke the port installer for all specified packages' do 33 | @install_commands.should =~ /cd \/usr\/ports\/lang\/ruby && make BATCH=yes install clean/ 34 | end 35 | 36 | it 'should automatically insert pre/post commands for the specified package' do 37 | @installer.send(:install_sequence).should == [ 'op1', 'sh -c \'cd /usr/ports/lang/ruby && make BATCH=yes install clean\'', 'op2' ] 38 | end 39 | 40 | end 41 | 42 | end 43 | -------------------------------------------------------------------------------- /spec/sprinkle/installers/file_spec.rb: -------------------------------------------------------------------------------- 1 | require File.expand_path("../../spec_helper", File.dirname(__FILE__)) 2 | 3 | describe Sprinkle::Installers::FileInstaller do 4 | include Sprinkle::Deployment 5 | 6 | before do 7 | @package = double(Sprinkle::Package, :name => 'package', :sudo? => false) 8 | @empty = Proc.new { } 9 | @delivery = double(Sprinkle::Deployment, :install => true) 10 | @destination = 'destination' 11 | @contents = "hi" 12 | @installer = create_file_installer(@destination, :contents => @contents) 13 | @roles = [] 14 | @deployment = deployment do 15 | delivery :capistrano 16 | source do; prefix '/usr/bin'; end 17 | end 18 | end 19 | 20 | def simplify(seq) 21 | seq.map do |cmd| 22 | cmd.is_a?(Sprinkle::Commands::Transfer) ? :TRANSFER : cmd 23 | end 24 | end 25 | 26 | def create_file_installer(dest, options={}, &block) 27 | i = Sprinkle::Installers::FileInstaller.new(@package, dest, options, &block) 28 | i.delivery = @delivery 29 | i 30 | end 31 | 32 | describe 'when created' do 33 | before do 34 | @source = "/tmp/file" 35 | Tempfile.any_instance.stub(:path).and_return(@source) 36 | @installer = create_file_installer(@destination, :contents => @contents) 37 | @transfer = @installer.install_sequence.detect {|x| x.is_a? Sprinkle::Commands::Transfer } 38 | end 39 | 40 | it 'should accept a source and destination to install' do 41 | @installer.contents.should eq @contents 42 | @installer.destination.should eq @destination 43 | end 44 | 45 | it 'should create a transfer command' do 46 | @transfer.source.should eq @source 47 | @transfer.destination.should eq @destination 48 | end 49 | 50 | it 'should not be using recursive' do 51 | @transfer.recursive?.should eq false 52 | end 53 | end 54 | 55 | describe 'during installation' do 56 | 57 | context "post process hooks" do 58 | 59 | it "should remove its temporary file" do 60 | @tmp = double(:print => true, :close => true, :path => "/file") 61 | Tempfile.stub(:new).and_return(@tmp) 62 | @installer = create_file_installer(@destination, :contents => @contents) 63 | @tmp.should_receive(:unlink) 64 | end 65 | 66 | end 67 | 68 | context "setting mode and owner" do 69 | before do 70 | @installer = create_file_installer @destination, :content => @contents do 71 | mode "744" 72 | owner "root" 73 | end 74 | @installer_commands = @installer.install_sequence 75 | end 76 | 77 | it "should include command to set owner" do 78 | @installer_commands.should include("chmod 744 #{@destination}") 79 | end 80 | 81 | it "should include command to set mode" do 82 | @installer_commands.should include("chown root #{@destination}") 83 | end 84 | 85 | end 86 | 87 | context "setting mode and owner with sudo" do 88 | before do 89 | @installer = create_file_installer @destination, :content => @contents do 90 | @options[:sudo]= true 91 | mode "744" 92 | owner "root" 93 | end 94 | @installer_commands = simplify @installer.install_sequence 95 | end 96 | 97 | it "should run commands in correct order" do 98 | @installer_commands.should eq [ 99 | :TRANSFER, 100 | "sudo mv /tmp/sprinkle_#{@destination} #{@destination}", 101 | "sudo chmod 744 #{@destination}", 102 | "sudo chown root #{@destination}" 103 | ] 104 | end 105 | end 106 | 107 | context "setting mode and owner with sudo as options" do 108 | before do 109 | @installer = create_file_installer @destination, :content => @contents, 110 | :mode => "744", :owner => "root" do 111 | @options[:sudo]= true 112 | end 113 | @installer_commands = simplify @installer.install_sequence 114 | end 115 | 116 | it "should run commands in correct order" do 117 | @installer_commands.should eq [ 118 | :TRANSFER, 119 | "sudo mv /tmp/sprinkle_#{@destination} #{@destination}", 120 | "sudo chown root #{@destination}", 121 | "sudo chmod 744 #{@destination}" 122 | ] 123 | end 124 | 125 | end 126 | 127 | 128 | context 'single pre/post commands' do 129 | before do 130 | @installer = create_file_installer @destination, :content => @contents do 131 | pre :install, 'op1' 132 | post :install, 'op2' 133 | end 134 | @installer_commands = simplify @installer.install_sequence 135 | @delivery = @installer.delivery 136 | end 137 | 138 | it "should call the pre and post install commands around the file transfer" do 139 | @installer_commands.should eq ["op1",:TRANSFER, "op2"] 140 | end 141 | 142 | end 143 | 144 | context 'pre/post with sudo' do 145 | before do 146 | @installer = create_file_installer @destination, :content => @contents do 147 | @options[:sudo]= true 148 | pre :install, 'op1' 149 | post :install, 'op2' 150 | end 151 | @installer_commands = simplify @installer.install_sequence 152 | @delivery = @installer.delivery 153 | end 154 | 155 | it "should call the pre and post install commands around the file transfer" do 156 | @installer_commands.should eq ["op1",:TRANSFER, 157 | "sudo mv /tmp/sprinkle_destination destination", "op2"] 158 | end 159 | end 160 | 161 | context 'multiple pre/post commands' do 162 | before do 163 | @installer = create_file_installer @destination, :content => @contents do 164 | pre :install, 'op1', 'op1-1' 165 | post :install, 'op2', 'op2-1' 166 | end 167 | @installer_commands = simplify @installer.install_sequence 168 | @delivery = @installer.delivery 169 | end 170 | 171 | it "should call the pre and post install commands around the file transfer" do 172 | @installer_commands.should eq ["op1","op1-1",:TRANSFER, "op2","op2-1"] 173 | end 174 | 175 | end 176 | 177 | after do 178 | @installer.process @roles 179 | end 180 | end 181 | 182 | end 183 | -------------------------------------------------------------------------------- /spec/sprinkle/installers/freebsd_pkg_spec.rb: -------------------------------------------------------------------------------- 1 | require File.expand_path("../../spec_helper", File.dirname(__FILE__)) 2 | 3 | describe Sprinkle::Installers::FreebsdPkg do 4 | 5 | before do 6 | @package = double(Sprinkle::Package, :name => 'package') 7 | end 8 | 9 | def create_pkg(pkgs, &block) 10 | Sprinkle::Installers::FreebsdPkg.new(@package, pkgs, &block) 11 | end 12 | 13 | describe 'when created' do 14 | 15 | it 'should accept a single package to install' do 16 | @installer = create_pkg 'ruby' 17 | @installer.packages.should == [ 'ruby' ] 18 | end 19 | 20 | it 'should accept an array of packages to install' do 21 | @installer = create_pkg %w( gcc gdb g++ ) 22 | @installer.packages.should == ['gcc', 'gdb', 'g++'] 23 | end 24 | 25 | end 26 | 27 | describe 'during installation' do 28 | 29 | before do 30 | @installer = create_pkg 'ruby' do 31 | pre :install, 'op1' 32 | post :install, 'op2' 33 | end 34 | @install_commands = @installer.send :install_commands 35 | end 36 | 37 | it 'should invoke the freebsd_pkg installer for all specified packages' do 38 | @install_commands.should =~ /pkg_add -r ruby/ 39 | end 40 | 41 | it 'should automatically insert pre/post commands for the specified package' do 42 | @installer.send(:install_sequence).should == [ 'op1', 'pkg_add -r ruby', 'op2' ] 43 | end 44 | 45 | end 46 | 47 | end 48 | -------------------------------------------------------------------------------- /spec/sprinkle/installers/freebsd_portinstall_spec.rb: -------------------------------------------------------------------------------- 1 | require File.expand_path("../../spec_helper", File.dirname(__FILE__)) 2 | 3 | describe Sprinkle::Installers::FreebsdPortinstall do 4 | 5 | before do 6 | @package = double(Sprinkle::Package, :name => 'package') 7 | end 8 | 9 | def create_port(ports, &block) 10 | Sprinkle::Installers::FreebsdPortinstall.new(@package, ports, &block) 11 | end 12 | 13 | describe 'when created' do 14 | 15 | it 'should accept a single package to install' do 16 | @installer = create_port 'lang/ruby' 17 | @installer.port.should == 'lang/ruby' 18 | end 19 | 20 | end 21 | 22 | describe 'during installation' do 23 | 24 | before do 25 | @installer = create_port 'lang/ruby' do 26 | pre :install, 'op1' 27 | post :install, 'op2' 28 | end 29 | @install_commands = @installer.send :install_commands 30 | end 31 | 32 | it 'should invoke the port installer for all specified packages' do 33 | @install_commands.should =~ /portinstall --batch lang\/ruby/ 34 | end 35 | 36 | it 'should automatically insert pre/post commands for the specified package' do 37 | @installer.send(:install_sequence).should == [ 'op1', 'portinstall --batch lang/ruby', 'op2' ] 38 | end 39 | 40 | end 41 | 42 | end 43 | -------------------------------------------------------------------------------- /spec/sprinkle/installers/gem_spec.rb: -------------------------------------------------------------------------------- 1 | require File.expand_path("../../spec_helper", File.dirname(__FILE__)) 2 | 3 | describe Sprinkle::Installers::Gem do 4 | 5 | before do 6 | @gem = 'rails' 7 | @version = '2.0.2' 8 | @options = { :source => 'http://gems.github.com/', :repository => '/tmp/gems', :build_flags => '--build_flag=foo', :http_proxy => 'http://proxy:8080' } 9 | end 10 | 11 | def create_gem(gem, version = nil, options = {}, &block) 12 | # @package = double(Sprinkle::Package, :name => gem, :version => version) 13 | @package = Package.new "test" do; end 14 | @package.version version 15 | Sprinkle::Installers::Gem.new(@package, gem, options, &block) 16 | end 17 | 18 | describe 'when created' do 19 | 20 | before do 21 | @installer = create_gem @gem, @version, @options 22 | end 23 | 24 | it "should return nil if no source is not configured" do 25 | @options.delete(:source) 26 | @installer = create_gem @gem, @version, @options 27 | @installer.source.should == nil 28 | end 29 | 30 | it 'should accept a single package to install' do 31 | @installer.gem.should == @gem 32 | end 33 | 34 | it 'should optionally store a version of the gem to install' do 35 | @installer.version.should == '2.0.2' 36 | end 37 | 38 | it 'should optionally store a source location of the gem to install' do 39 | @installer.source.should == 'http://gems.github.com/' 40 | end 41 | 42 | it 'should optionally store the repository location where gems are to be installed' do 43 | @installer.repository.should == @options[:repository] 44 | end 45 | 46 | it 'should optionally store the build flags' do 47 | @installer.build_flags.should == @options[:build_flags] 48 | end 49 | 50 | it 'should optionally store the http proxy' do 51 | @installer.http_proxy.should == @options[:http_proxy] 52 | end 53 | 54 | end 55 | 56 | describe 'during installation' do 57 | 58 | describe 'without a version' do 59 | 60 | before do 61 | @installer = create_gem @gem do 62 | pre :install, 'op1' 63 | post :install, 'op2' 64 | end 65 | end 66 | 67 | it 'should invoke the gem installer for the specified package' do 68 | @installer.send(:install_commands).should == "gem install #{@gem} --no-rdoc --no-ri" 69 | end 70 | 71 | it 'should automatically insert pre/post commands for the specified package' do 72 | @installer.send(:install_sequence).should == [ 'op1', "gem install #{@gem} --no-rdoc --no-ri", 'op2'] 73 | end 74 | 75 | end 76 | 77 | describe 'with a specific version' do 78 | 79 | before do 80 | @installer = create_gem @gem, @version, :build_docs => true 81 | end 82 | 83 | it 'should install a specific version if defined, and with docs' do 84 | @installer.send(:install_commands).should == "gem install #{@gem} --version '#{@version}'" 85 | end 86 | 87 | end 88 | 89 | describe "with sudo" do 90 | before do 91 | @installer = create_gem @gem, nil, :sudo => true 92 | end 93 | 94 | it 'should call sudo' do 95 | @installer.send(:install_commands).should == "sudo gem install #{@gem} --no-rdoc --no-ri" 96 | end 97 | 98 | end 99 | 100 | describe 'with build flags' do 101 | 102 | before do 103 | @installer = create_gem @gem, nil, :build_flags => '--option=foo' 104 | end 105 | 106 | it 'should install with defined build flags' do 107 | @installer.send(:install_commands).should == "gem install #{@gem} --no-rdoc --no-ri -- --option=foo" 108 | end 109 | 110 | end 111 | 112 | describe 'with http proxy' do 113 | 114 | before do 115 | @installer = create_gem @gem, nil, :http_proxy => 'http://proxy:8080' 116 | end 117 | 118 | it 'should install with defined build flags' do 119 | @installer.send(:install_commands).should == "gem install #{@gem} --no-rdoc --no-ri --http-proxy http://proxy:8080" 120 | end 121 | 122 | end 123 | 124 | end 125 | 126 | end 127 | -------------------------------------------------------------------------------- /spec/sprinkle/installers/mac_port_spec.rb: -------------------------------------------------------------------------------- 1 | require File.expand_path("../../spec_helper", File.dirname(__FILE__)) 2 | 3 | describe Sprinkle::Installers::MacPort do 4 | 5 | before do 6 | @package = double(Sprinkle::Package, :name => 'package') 7 | end 8 | 9 | def create_port(ports, &block) 10 | Sprinkle::Installers::MacPort.new(@package, ports, &block) 11 | end 12 | 13 | describe 'when created' do 14 | 15 | it 'should accept a single package to install' do 16 | @installer = create_port 'ruby' 17 | @installer.port.should == 'ruby' 18 | end 19 | 20 | end 21 | 22 | describe 'during installation' do 23 | 24 | before do 25 | @installer = create_port 'ruby' do 26 | pre :install, 'op1' 27 | post :install, 'op2' 28 | end 29 | @install_commands = @installer.send :install_commands 30 | end 31 | 32 | it 'should invoke the port installer for all specified packages' do 33 | @install_commands.should =~ /port install ruby/ 34 | end 35 | 36 | it 'should automatically insert pre/post commands for the specified package' do 37 | @installer.send(:install_sequence).should == [ 'op1', 'port install ruby', 'op2' ] 38 | end 39 | 40 | end 41 | 42 | end 43 | -------------------------------------------------------------------------------- /spec/sprinkle/installers/npm_spec.rb: -------------------------------------------------------------------------------- 1 | require File.expand_path("../../spec_helper", File.dirname(__FILE__)) 2 | 3 | describe Sprinkle::Installers::Npm do 4 | 5 | before do 6 | @package = double(Sprinkle::Package, :name => 'spec') 7 | @installer = Sprinkle::Installers::Npm.new(@package, 'spec') 8 | end 9 | 10 | describe 'during installation' do 11 | it 'should invoke the npm executer for all specified tasks' do 12 | @install_commands = @installer.send :install_commands 13 | @install_commands.should == "npm install --global spec" 14 | end 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /spec/sprinkle/installers/openbsd_pkg_spec.rb: -------------------------------------------------------------------------------- 1 | require File.expand_path("../../spec_helper", File.dirname(__FILE__)) 2 | 3 | describe Sprinkle::Installers::OpenbsdPkg do 4 | 5 | before do 6 | @package = double(Sprinkle::Package, :name => 'package') 7 | end 8 | 9 | def create_pkg(pkgs, &block) 10 | Sprinkle::Installers::OpenbsdPkg.new(@package, pkgs, &block) 11 | end 12 | 13 | describe 'when created' do 14 | 15 | it 'should accept a single package to install' do 16 | @installer = create_pkg 'ruby' 17 | @installer.packages.should == [ 'ruby' ] 18 | end 19 | 20 | it 'should accept an array of packages to install' do 21 | @installer = create_pkg %w( gcc gdb g++ ) 22 | @installer.packages.should == ['gcc', 'gdb', 'g++'] 23 | end 24 | 25 | end 26 | 27 | describe 'during installation' do 28 | 29 | before do 30 | @installer = create_pkg 'ruby' do 31 | pre :install, 'op1' 32 | post :install, 'op2' 33 | end 34 | @install_commands = @installer.send :install_commands 35 | end 36 | 37 | it 'should invoke the freebsd_pkg installer for all specified packages' do 38 | @install_commands.should =~ /pkg_add ruby/ 39 | end 40 | 41 | it 'should automatically insert pre/post commands for the specified package' do 42 | @installer.send(:install_sequence).should == [ 'op1', 'pkg_add ruby', 'op2' ] 43 | end 44 | 45 | end 46 | 47 | end 48 | -------------------------------------------------------------------------------- /spec/sprinkle/installers/opensolaris_pkg_spec.rb: -------------------------------------------------------------------------------- 1 | require File.expand_path("../../spec_helper", File.dirname(__FILE__)) 2 | 3 | describe Sprinkle::Installers::OpensolarisPkg do 4 | 5 | before do 6 | @package = double(Sprinkle::Package, :name => 'package') 7 | end 8 | 9 | def create_pkg(pkgs, &block) 10 | Sprinkle::Installers::OpensolarisPkg.new(@package, pkgs, &block) 11 | end 12 | 13 | describe 'when created' do 14 | 15 | it 'should accept a single package to install' do 16 | @installer = create_pkg 'ruby' 17 | @installer.packages.should == [ 'ruby' ] 18 | end 19 | 20 | it 'should accept an array of packages to install' do 21 | @installer = create_pkg %w( gcc gdb g++ ) 22 | @installer.packages.should == ['gcc', 'gdb', 'g++'] 23 | end 24 | 25 | end 26 | 27 | describe 'during installation' do 28 | 29 | before do 30 | @installer = create_pkg 'ruby' do 31 | pre :install, 'op1' 32 | post :install, 'op2' 33 | end 34 | @install_commands = @installer.send :install_commands 35 | end 36 | 37 | it 'should invoke the freebsd_pkg installer for all specified packages' do 38 | @install_commands.should =~ /pkg install ruby/ 39 | end 40 | 41 | it 'should automatically insert pre/post commands for the specified package' do 42 | @installer.send(:install_sequence).should == [ 'op1', 'pkg install ruby', 'op2' ] 43 | end 44 | 45 | end 46 | 47 | end 48 | -------------------------------------------------------------------------------- /spec/sprinkle/installers/pear_spec.rb: -------------------------------------------------------------------------------- 1 | require File.expand_path("../../spec_helper", File.dirname(__FILE__)) 2 | 3 | describe Sprinkle::Installers::Pear do 4 | 5 | before do 6 | @package = double(Sprinkle::Package, :name => 'spec') 7 | @installer = Sprinkle::Installers::Pear.new(@package, 'spec') 8 | end 9 | 10 | describe 'during installation' do 11 | it 'should invoke the pear executer for all specified tasks' do 12 | @install_commands = @installer.send :install_commands 13 | @install_commands.should == "pear install --alldeps spec" 14 | end 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /spec/sprinkle/installers/pecl_spec.rb: -------------------------------------------------------------------------------- 1 | require File.expand_path("../../spec_helper", File.dirname(__FILE__)) 2 | 3 | describe Sprinkle::Installers::Pecl do 4 | before do 5 | # @package = double(Sprinkle::Package, :name => 'spec', :class => double(Sprinkle::Package, :installer_methods => [])) 6 | @package = Package.new("test") {} 7 | end 8 | 9 | describe 'providing just package name' do 10 | before do 11 | @installer = Sprinkle::Installers::Pecl.new(@package, 'spec') 12 | end 13 | 14 | describe 'during installation' do 15 | it 'should invoke the pecl installer' do 16 | @install_commands = @installer.send :install_commands 17 | @install_commands.should == "TERM= pecl install --alldeps spec" 18 | end 19 | end 20 | end 21 | 22 | describe 'providing explicit version' do 23 | before do 24 | @installer = Sprinkle::Installers::Pecl.new(@package, 'spec', :version => '1.1.1') 25 | end 26 | 27 | describe 'during installation' do 28 | it 'should invoke the pecl installer' do 29 | @install_commands = @installer.send :install_commands 30 | @install_commands.should == "TERM= pecl install --alldeps spec-1.1.1" 31 | end 32 | end 33 | end 34 | 35 | describe 'providing ini_file option' do 36 | describe 'during installation' do 37 | it 'should transfer file with default arguments in post install' do 38 | @installer = Sprinkle::Installers::Pecl.new(@package, 'spec', :ini_file => true) do 39 | self.stub(:file) do |path,options| 40 | path.should == "/etc/php5/conf.d/spec.ini" 41 | options[:content].should == "extension=spec.so" 42 | options[:sudo].should == true 43 | "post install file transfer" 44 | end 45 | end 46 | @install_sequence = @installer.install_sequence 47 | @install_sequence.should include("TERM= pecl install --alldeps spec") 48 | @install_sequence.should include("post install file transfer") 49 | end 50 | 51 | it 'should use custom path and content if provided' do 52 | @installer = Sprinkle::Installers::Pecl.new(@package, 'spec', :ini_file => {:path => "/custom/path", :content => "hello"}) do 53 | self.stub(:file) do |path,options| 54 | path.should == "/custom/path" 55 | options[:content].should == "hello" 56 | end 57 | end 58 | end 59 | 60 | it 'should use custom content if string passed' do 61 | @installer = Sprinkle::Installers::Pecl.new(@package, 'spec', :ini_file => "hello") do 62 | self.stub(:file) do |path,options| 63 | options[:content].should == "hello" 64 | end 65 | end 66 | end 67 | 68 | it 'should NOT use sudo if explicitly denied' do 69 | @installer = Sprinkle::Installers::Pecl.new(@package, 'spec', :ini_file => {:sudo => false}) do 70 | self.stub(:file) do |path,options| 71 | options[:sudo].should == false 72 | end 73 | end 74 | end 75 | end 76 | end 77 | 78 | end 79 | -------------------------------------------------------------------------------- /spec/sprinkle/installers/push_text_spec.rb: -------------------------------------------------------------------------------- 1 | require File.expand_path("../../spec_helper", File.dirname(__FILE__)) 2 | 3 | describe Sprinkle::Installers::PushText do 4 | 5 | before do 6 | @package = double(Sprinkle::Package, :name => 'package', :sudo? => false) 7 | @options = {:sudo => true} 8 | end 9 | 10 | def create_text(text, path, options={}, &block) 11 | # the old default 12 | options.reverse_merge!(:idempotent => false) 13 | Sprinkle::Installers::PushText.new(@package, text, path, options, &block) 14 | end 15 | 16 | describe 'when created' do 17 | 18 | it 'should accept a single package to install' do 19 | @installer = create_text 'crazy-configuration-methods', '/etc/doomed/file.conf' 20 | @installer.text.should eq 'crazy-configuration-methods' 21 | @installer.path.should eq '/etc/doomed/file.conf' 22 | end 23 | 24 | end 25 | 26 | describe 'during installation' do 27 | 28 | before do 29 | @installer = create_text 'another-hair-brained-idea', '/dev/mind/late-night' do 30 | pre :install, 'op1' 31 | post :install, 'op2' 32 | end 33 | @install_commands = @installer.send :install_commands 34 | end 35 | 36 | describe 'with idempotent' do 37 | before do 38 | @installer = create_text 'another-hair-brained-idea', '/dev/mind/late-night', :idempotent => true 39 | @install_commands = @installer.send :install_commands 40 | end 41 | it "should grep for existing of the string" do 42 | @install_commands.should eq %q 43 | end 44 | end 45 | 46 | describe 'with multiline idempotent' do 47 | before do 48 | mline = <<-MULTI 49 | ^search( [adnor]{2,3} rescue)?$ 50 | ^fries( [adnor]{2,3} barbecue)? 51 | ^songs( [adnor]{2,3} autocue)? 52 | MULTI 53 | @installer = create_text mline.strip, '/dev/mind/late-night', :idempotent => true 54 | @install_commands = @installer.send :install_commands 55 | end 56 | it "should grep for existence of the string" do 57 | @install_commands.should eq %q 58 | end 59 | end 60 | 61 | describe 'with sudo' do 62 | before do 63 | @installer = create_text 'another-hair-brained-idea', '/dev/mind/late-night', :sudo => true 64 | @install_commands = @installer.send :install_commands 65 | end 66 | it 'should invoke sudo if sudo option passed' do 67 | @install_commands.should eq %q[/bin/echo -e 'another-hair-brained-idea' |sudo tee -a /dev/mind/late-night] 68 | end 69 | end 70 | 71 | it 'should invoke the push text installer for all specified packages' do 72 | @install_commands.should eq %q[/bin/echo -e 'another-hair-brained-idea' |tee -a /dev/mind/late-night] 73 | end 74 | 75 | it 'should automatically insert pre/post commands for the specified package' do 76 | @installer.send(:install_sequence).should eq [ 'op1', "/bin/echo -e 'another-hair-brained-idea' |tee -a /dev/mind/late-night", 'op2' ] 77 | end 78 | 79 | end 80 | 81 | describe 'running with sudo' do 82 | before do 83 | @installer = create_text "a special user", "/dev/mind/the-day-after", :sudo => true 84 | @install_commands = @installer.send :install_commands 85 | end 86 | 87 | it "should invoke the push installer with sudo" do 88 | @install_commands.should eq %q[/bin/echo -e 'a special user' |sudo tee -a /dev/mind/the-day-after] 89 | end 90 | end 91 | 92 | describe 'sending a string with special characters' do 93 | 94 | it "should not escape an ampersand" do 95 | @installer = create_text "bob & lucy", "/dev/mind/the-day-after" 96 | @install_commands = @installer.send :install_commands 97 | @install_commands.should eq %q[/bin/echo -e 'bob & lucy' |tee -a /dev/mind/the-day-after] 98 | end 99 | 100 | it "should not escape a slash" do 101 | @installer = create_text "bob/lucy", "/dev/mind/the-day-after" 102 | @install_commands = @installer.send :install_commands 103 | @install_commands.should eq %q[/bin/echo -e 'bob/lucy' |tee -a /dev/mind/the-day-after] 104 | end 105 | end 106 | 107 | describe 'sending a string with single quotes' do 108 | before do 109 | @installer = create_text "I'm a string with a single quote", "/dev/mind/the-day-after" 110 | @install_commands = @installer.send :install_commands 111 | end 112 | 113 | it "should correctly encode the single quote character" do 114 | @install_commands.should eq %q[/bin/echo -e 'I'\''m a string with a single quote' |tee -a /dev/mind/the-day-after] 115 | end 116 | end 117 | 118 | end 119 | -------------------------------------------------------------------------------- /spec/sprinkle/installers/rake_spec.rb: -------------------------------------------------------------------------------- 1 | require File.expand_path("../../spec_helper", File.dirname(__FILE__)) 2 | 3 | describe Sprinkle::Installers::Rake do 4 | 5 | before do 6 | @package = double(Sprinkle::Package, :name => 'spec') 7 | end 8 | 9 | def create_rake(names, options = {}, &block) 10 | Sprinkle::Installers::Rake.new(@package, names, options, &block) 11 | end 12 | 13 | describe 'during installation' do 14 | 15 | it 'should invoke the rake executer for all specified tasks' do 16 | @installer = create_rake 'spec' 17 | @install_commands = @installer.send :install_commands 18 | @install_commands.should =~ /rake spec/ 19 | end 20 | 21 | it 'should invoke the rake executer for all specified tasks' do 22 | @installer = create_rake 'spec', :rakefile => '/some/Rakefile' 23 | @install_commands = @installer.send :install_commands 24 | @install_commands.should == "rake -f /some/Rakefile spec" 25 | end 26 | 27 | end 28 | 29 | end 30 | -------------------------------------------------------------------------------- /spec/sprinkle/installers/replace_text_spec.rb: -------------------------------------------------------------------------------- 1 | require File.expand_path("../../spec_helper", File.dirname(__FILE__)) 2 | 3 | describe Sprinkle::Installers::ReplaceText do 4 | 5 | before do 6 | @package = double(Sprinkle::Package, :name => 'package', :sudo? => false) 7 | end 8 | 9 | def create_replacement_text(regex, text, path, options={}, &block) 10 | Sprinkle::Installers::ReplaceText.new(@package, regex, text, path, options, &block) 11 | end 12 | 13 | describe 'when created' do 14 | 15 | it 'should accept text to replace, replacement, and path' do 16 | @installer = create_replacement_text 'text_to_replace', 'new_text', '/etc/example/foo.conf' 17 | @installer.regex.should eq 'text_to_replace' 18 | @installer.text.should eq 'new_text' 19 | @installer.path.should eq '/etc/example/foo.conf' 20 | end 21 | 22 | it 'should not escape original search string and replacement' do 23 | @installer = create_replacement_text "some option with 'quotes' & ampersand", "other 'quotes' & ampersand", '/etc/example/foo.conf' 24 | @installer.regex.should eq %q[some option with 'quotes' & ampersand] 25 | @installer.text.should eq %q[other 'quotes' & ampersand] 26 | @installer.path.should eq '/etc/example/foo.conf' 27 | end 28 | 29 | end 30 | 31 | describe 'during installation' do 32 | 33 | before do 34 | @installer = create_replacement_text 'bad option', 'super option', '/etc/brand/new.conf' do 35 | pre :install, 'op1' 36 | post :install, 'op2' 37 | end 38 | @install_commands = @installer.send :install_commands 39 | end 40 | 41 | it 'should invoke the replace text installer for all specified packages' do 42 | @install_commands.should eq %q[sed -i 's/bad option/super option/g' /etc/brand/new.conf] 43 | end 44 | 45 | it 'should automatically insert pre/post commands for the specified package' do 46 | @installer.send(:install_sequence).should eq [ 'op1', "sed -i 's/bad option/super option/g' /etc/brand/new.conf", 'op2' ] 47 | end 48 | 49 | it 'should correctly escape search string and replacement' do 50 | installer = create_replacement_text "some option with 'quotes' & ampersand", "other 'quotes' & ampersand", '/etc/example/foo.conf' 51 | installer.send(:install_commands).should eq "sed -i 's/some option with '\\''quotes'\\'' \\& ampersand/other '\\''quotes'\\'' \\& ampersand/g' /etc/example/foo.conf" 52 | end 53 | end 54 | 55 | end 56 | -------------------------------------------------------------------------------- /spec/sprinkle/installers/rpm_spec.rb: -------------------------------------------------------------------------------- 1 | require File.expand_path("../../spec_helper", File.dirname(__FILE__)) 2 | 3 | describe Sprinkle::Installers::Rpm do 4 | 5 | before do 6 | @package = double(Sprinkle::Package, :name => 'package') 7 | end 8 | 9 | def create_rpm(debs, &block) 10 | Sprinkle::Installers::Rpm.new(@package, debs, &block) 11 | end 12 | 13 | describe 'when created' do 14 | 15 | it 'should accept a single package to install' do 16 | @installer = create_rpm 'ruby' 17 | @installer.packages.should == [ 'ruby' ] 18 | end 19 | 20 | it 'should accept an array of packages to install' do 21 | @installer = create_rpm %w( gcc gdb g++ ) 22 | @installer.packages.should == ['gcc', 'gdb', 'g++'] 23 | end 24 | 25 | it 'should accept a list of packages to install' do 26 | @installer = Sprinkle::Installers::Rpm.new(@package, "gcc", "gdb", "g++") 27 | @installer.packages.should == ['gcc', 'gdb', 'g++'] 28 | end 29 | 30 | end 31 | 32 | describe 'during installation' do 33 | 34 | before do 35 | @installer = create_rpm 'ruby' do 36 | pre :install, 'op1' 37 | post :install, 'op2' 38 | end 39 | @install_commands = @installer.send :install_commands 40 | end 41 | 42 | it 'should invoke the rpm installer for all specified packages' do 43 | @install_commands.should =~ /rpm -Uvh ruby/ 44 | end 45 | 46 | it 'should automatically insert pre/post commands for the specified package' do 47 | @installer.send(:install_sequence).should == [ 'op1', 'rpm -Uvh ruby', 'op2' ] 48 | end 49 | 50 | end 51 | 52 | end 53 | -------------------------------------------------------------------------------- /spec/sprinkle/installers/runner_spec.rb: -------------------------------------------------------------------------------- 1 | require File.expand_path("../../spec_helper", File.dirname(__FILE__)) 2 | 3 | describe Sprinkle::Installers::Runner do 4 | 5 | before do 6 | @package = double(Sprinkle::Package, :name => 'package', :sudo? => false) 7 | end 8 | 9 | def create_runner(*cmds) 10 | options=cmds.extract_options! 11 | Sprinkle::Installers::Runner.new(@package, cmds, options) 12 | end 13 | 14 | describe 'when created' do 15 | it 'should accept a single cmd to run' do 16 | @installer = create_runner 'teste' 17 | @installer.cmds.should eq ['teste'] 18 | end 19 | 20 | it 'should accept an array of commands to run' do 21 | @installer = create_runner ['teste', 'world'] 22 | @installer.cmds.should eq ['teste', 'world'] 23 | @installer.install_sequence.should eq ['teste', 'world'] 24 | end 25 | end 26 | 27 | describe 'during installation' do 28 | 29 | it 'should use sudo if specified locally' do 30 | @installer = create_runner 'teste', :sudo => true 31 | @install_commands = @installer.send :install_commands 32 | @install_commands.should eq ['sudo teste'] 33 | end 34 | 35 | it "should accept env options and convert to uppercase" do 36 | @installer = create_runner 'command1', :env => { 37 | :z => 'foo', 38 | :PATH => '/some/path', 39 | :user => 'deploy', 40 | :a => 'bar' 41 | } 42 | @install_commands = @installer.send :install_commands 43 | command_parts = @install_commands.first.split(/ /) 44 | 45 | command_parts.shift.should eq 'env' 46 | command_parts.pop.should eq 'command1' 47 | 48 | command_parts.should =~ ['PATH=/some/path', 'USER=deploy', 'Z=foo', 'A=bar'] 49 | 50 | end 51 | 52 | it "should accept multiple commands" do 53 | @installer = create_runner 'teste', 'test2' 54 | @install_commands = @installer.send :install_commands 55 | @install_commands.should eq ['teste','test2'] 56 | end 57 | 58 | it 'should run the given command for all specified packages' do 59 | @installer = create_runner 'teste' 60 | @install_commands = @installer.send :install_commands 61 | @install_commands.should eq ['teste'] 62 | end 63 | end 64 | end 65 | -------------------------------------------------------------------------------- /spec/sprinkle/installers/smart_spec.rb: -------------------------------------------------------------------------------- 1 | require File.expand_path("../../spec_helper", File.dirname(__FILE__)) 2 | 3 | describe Sprinkle::Installers::Smart do 4 | 5 | before do 6 | @package = double(Sprinkle::Package, :name => 'package') 7 | end 8 | 9 | def create_smart(pkgs, &block) 10 | Sprinkle::Installers::Smart.new(@package, pkgs, &block) 11 | end 12 | 13 | describe 'when created' do 14 | 15 | it 'should accept a single package to install' do 16 | @installer = create_smart 'ruby' 17 | @installer.packages.should == [ 'ruby' ] 18 | end 19 | 20 | it 'should accept an array of packages to install' do 21 | @installer = create_smart %w( gcc gdb g++ ) 22 | @installer.packages.should == ['gcc', 'gdb', 'g++'] 23 | end 24 | 25 | it 'should accept a list of packages to install' do 26 | @installer = Sprinkle::Installers::Smart.new(@package, "gcc", "gdb", "g++") 27 | @installer.packages.should == ['gcc', 'gdb', 'g++'] 28 | end 29 | 30 | end 31 | 32 | describe 'during installation' do 33 | 34 | before do 35 | @installer = create_smart 'ruby' do 36 | pre :install, 'op1' 37 | post :install, 'op2' 38 | end 39 | @install_commands = @installer.send :install_commands 40 | end 41 | 42 | it 'should invoke the rpm installer for all specified packages' do 43 | @install_commands.should == "smart install ruby -y 2>&1 | tee -a /var/log/smart-sprinkle" 44 | end 45 | 46 | it 'should automatically insert pre/post commands for the specified package' do 47 | @installer.send(:install_sequence).should == [ 'op1', 48 | 'smart install ruby -y 2>&1 | tee -a /var/log/smart-sprinkle', 'op2' ] 49 | end 50 | 51 | end 52 | 53 | end 54 | -------------------------------------------------------------------------------- /spec/sprinkle/installers/thor_spec.rb: -------------------------------------------------------------------------------- 1 | require File.expand_path("../../spec_helper", File.dirname(__FILE__)) 2 | 3 | describe Sprinkle::Installers::Thor do 4 | 5 | before do 6 | @package = double(Sprinkle::Package, :name => 'spec') 7 | end 8 | 9 | def create_thor(names, options = {}, &block) 10 | Sprinkle::Installers::Thor.new(@package, names, options, &block) 11 | end 12 | 13 | describe 'during installation' do 14 | 15 | it 'should invoke the thor executer for all specified tasks' do 16 | @installer = create_thor 'spec' 17 | @install_commands = @installer.send :install_commands 18 | @install_commands.should =~ /thor spec/ 19 | end 20 | 21 | it 'should invoke the thor executer for all specified tasks' do 22 | @installer = create_thor 'spec', :thorfile => '/some/Thorfile' 23 | @install_commands = @installer.send :install_commands 24 | @install_commands.should == "thor -f /some/Thorfile spec" 25 | end 26 | 27 | end 28 | 29 | end -------------------------------------------------------------------------------- /spec/sprinkle/installers/user_spec.rb: -------------------------------------------------------------------------------- 1 | require File.expand_path("../../spec_helper", File.dirname(__FILE__)) 2 | 3 | describe Sprinkle::Installers::User do 4 | 5 | before do 6 | @package = double(Sprinkle::Package, :name => 'spec', :sudo? => false) 7 | @user = "bob" 8 | end 9 | 10 | def create_user(name, options = {}, &block) 11 | Sprinkle::Installers::User.new @package, name, options, &block 12 | end 13 | 14 | describe 'during installation' do 15 | 16 | it "should invoke add user" do 17 | @installer = create_user 'bob' 18 | @install_commands = @installer.send :install_commands 19 | @install_commands.should == "adduser --gecos ,,, bob" 20 | end 21 | 22 | it "should merge flags" do 23 | @installer = create_user 'bob', :flags => "-x" 24 | @install_commands = @installer.send :install_commands 25 | @install_commands.should == "adduser -x --gecos ,,, bob" 26 | end 27 | 28 | it "should use actual gecos options if passed" do 29 | @installer = create_user 'bob', :flags => "--gecos bob,,," 30 | @install_commands = @installer.send :install_commands 31 | @install_commands.should == "adduser --gecos bob,,, bob" 32 | end 33 | 34 | it "should use sudo if sudo specified" do 35 | @installer = create_user 'bob', :sudo => true 36 | @install_commands = @installer.send :install_commands 37 | @install_commands.should == "sudo adduser --gecos ,,, bob" 38 | end 39 | 40 | end 41 | 42 | end 43 | -------------------------------------------------------------------------------- /spec/sprinkle/installers/yum_spec.rb: -------------------------------------------------------------------------------- 1 | require File.expand_path("../../spec_helper", File.dirname(__FILE__)) 2 | 3 | describe Sprinkle::Installers::Yum do 4 | 5 | before do 6 | @package = double(Sprinkle::Package, :name => 'package') 7 | end 8 | 9 | def create_rpm(rpms, &block) 10 | Sprinkle::Installers::Yum.new(@package, rpms, &block) 11 | end 12 | 13 | describe 'when created' do 14 | 15 | it 'should accept a single package to install' do 16 | @installer = create_rpm 'ruby' 17 | @installer.packages.should == [ 'ruby' ] 18 | end 19 | 20 | it 'should accept an array of packages to install' do 21 | @installer = create_rpm %w( gcc gdb g++ ) 22 | @installer.packages.should == ['gcc', 'gdb', 'g++'] 23 | end 24 | 25 | end 26 | 27 | describe 'during installation' do 28 | 29 | before do 30 | @installer = create_rpm 'ruby' do 31 | pre :install, 'op1' 32 | post :install, 'op2' 33 | end 34 | @install_commands = @installer.send :install_commands 35 | end 36 | 37 | it 'should invoke the rpm installer for all specified packages' do 38 | @install_commands.should =~ /yum install ruby -y/ 39 | end 40 | 41 | it 'should automatically insert pre/post commands for the specified package' do 42 | @installer.send(:install_sequence).should == [ 'op1', 'yum install ruby -y', 'op2' ] 43 | end 44 | 45 | it 'should install a specific version if defined' do 46 | @installer = create_rpm 'ruby-2.0' 47 | @installer.send(:install_sequence).should == [ 'yum install ruby-2.0 -y' ] 48 | end 49 | 50 | end 51 | 52 | end 53 | -------------------------------------------------------------------------------- /spec/sprinkle/installers/zypper_spec.rb: -------------------------------------------------------------------------------- 1 | require File.expand_path("../../spec_helper", File.dirname(__FILE__)) 2 | 3 | describe Sprinkle::Installers::Zypper do 4 | 5 | before do 6 | @package = double(Sprinkle::Package, :name => 'package') 7 | end 8 | 9 | def create_zypper(*packages, &block) 10 | Sprinkle::Installers::Zypper.new(@package, *packages, &block) 11 | end 12 | 13 | describe 'when created' do 14 | 15 | it 'should accept a single package to install' do 16 | @installer = create_zypper 'ruby' 17 | @installer.packages.should == [ 'ruby' ] 18 | end 19 | 20 | it 'should accept an array of packages to install' do 21 | @installer = create_zypper %w( gcc gdb g++ ) 22 | @installer.packages.should == ['gcc', 'gdb', 'g++'] 23 | end 24 | 25 | it 'should accept an argument list of packages to install' do 26 | @installer = create_zypper "gcc", "gdb", "g++" 27 | @installer.packages.should == ['gcc', 'gdb', 'g++'] 28 | end 29 | end 30 | 31 | describe 'during installation' do 32 | 33 | before do 34 | @installer = create_zypper 'ruby', 'rubygems' do 35 | pre :install, 'op1' 36 | post :install, 'op2' 37 | end 38 | @install_commands = @installer.send :install_commands 39 | end 40 | 41 | it 'should invoke the zypper installer for all specified packages' do 42 | @install_commands.should =~ /zypper -n install -l -R ruby rubygems/ 43 | end 44 | 45 | it 'should automatically insert pre/post commands for the specified package' do 46 | @installer.send(:install_sequence).should == [ 'op1', 'zypper -n install -l -R ruby rubygems', 'op2' ] 47 | end 48 | end 49 | end 50 | -------------------------------------------------------------------------------- /spec/sprinkle/package/package_repository_spec.rb: -------------------------------------------------------------------------------- 1 | require File.expand_path("../../spec_helper", File.dirname(__FILE__)) 2 | 3 | describe Sprinkle::Package::PackageRepository do 4 | 5 | before do 6 | @repository = PackageRepository.new {} 7 | @test_package = Package.new(:test) {} 8 | @mysql_package = Package.new(:mysql, :provides => :db) {} 9 | @test_v2_package = Package.new(:test) do 10 | version "2" 11 | end 12 | @another_package = Package.new(:another) {} 13 | end 14 | 15 | it 'should allow adding a package' do 16 | @repository.add @test_package 17 | @repository.count.should eq 1 18 | end 19 | 20 | it 'should allow clearing' do 21 | @repository.add @test_package 22 | @repository.clear 23 | @repository.count.should eq 0 24 | end 25 | 26 | it "should find by provides" do 27 | @repository.add @mysql_package 28 | @repository.find_all("db").should eq [ @mysql_package ] 29 | end 30 | 31 | it "should find by name" do 32 | @repository.add @test_package 33 | @repository.find_all("test").should eq [ @test_package ] 34 | end 35 | 36 | it "should filter by version" do 37 | @repository.add @test_package 38 | @repository.add @test_v2_package 39 | @repository.find_all("test").size.should eq 2 40 | @repository.first("test", :version => "2").should eq @test_v2_package 41 | end 42 | 43 | after do 44 | end 45 | 46 | end 47 | -------------------------------------------------------------------------------- /spec/sprinkle/policy_spec.rb: -------------------------------------------------------------------------------- 1 | require File.expand_path("../spec_helper", File.dirname(__FILE__)) 2 | 3 | describe Sprinkle::Policy do 4 | include Sprinkle::Core 5 | 6 | before do 7 | @name = 'a policy' 8 | end 9 | 10 | describe 'with a role with no matching servers' do 11 | before do 12 | @policy = policy @name, :roles => :app do; end 13 | end 14 | 15 | it "should raise an error" do 16 | @deployment = double(:style => Sprinkle::Actors::Dummy.new {}) 17 | lambda { @policy.process(@deployment) }.should raise_error(Sprinkle::NoMatchingServersError) 18 | end 19 | end 20 | 21 | describe 'when created' do 22 | 23 | it 'should be invalid without a name' do 24 | lambda { policy nil }.should raise_error 25 | end 26 | 27 | it 'should be invalid without role definitions' do 28 | lambda { policy @name do; end }.should raise_error 29 | lambda { policy @name, :roles => :app do; end }.should_not raise_error 30 | end 31 | 32 | it 'should optionally accept package dependencies' do 33 | p = policy @name, :roles => :app do; end 34 | p.should respond_to(:requires) 35 | p.requires :appserver 36 | p.packages.should eq [ :appserver ] 37 | end 38 | 39 | it 'should optionally accept package dependencies with versions' do 40 | p = policy @name, :roles => :app do; end 41 | p.requires :appserver, :version => 2 42 | p.packages.should eq [ :appserver ] 43 | # pending 'requires version checking implementation' 44 | end 45 | 46 | it 'should add itself to the global policy list' do 47 | sz = Sprinkle::POLICIES.size 48 | p = policy @name, :roles => :app do; end 49 | Sprinkle::POLICIES.size.should eq sz + 1 50 | Sprinkle::POLICIES.last.should eq p 51 | end 52 | 53 | end 54 | 55 | describe 'with the same package multiple times' do 56 | include Sprinkle::Package 57 | 58 | before do 59 | @deployment = double(Sprinkle::Deployment) 60 | actor = double(:servers_for_role? => true) 61 | @deployment.stub(:style).and_return(actor) 62 | Sprinkle::Package::PACKAGES.clear # reset full package list before each spec is run 63 | 64 | @user = package :user do; end 65 | 66 | @policy = policy :test, :roles => :app do 67 | requires :user, :name => "josh" 68 | requires :user, :name => "bill" 69 | end 70 | end 71 | 72 | it "should call process on both users" do 73 | (all=@policy.package_install_tree).size.should == 2 74 | all.each do |p| 75 | p.should_receive(:process).and_return 76 | end 77 | end 78 | 79 | after do 80 | @policy.process(@deployment) 81 | end 82 | end 83 | 84 | describe 'with packages' do 85 | include Sprinkle::Package 86 | 87 | before do 88 | @deployment = double(Sprinkle::Deployment) 89 | actor = double(:servers_for_role? => true) 90 | @deployment.stub(:style).and_return(actor) 91 | Sprinkle::Package::PACKAGES.clear # reset full package list before each spec is run 92 | 93 | @a = package :a do; requires :b; requires :c; end 94 | @b = package :b, :provides => :xyz do; end 95 | @c = package :c, :provides => :abc do; end 96 | @d = package :d, :provides => :abc do; end 97 | 98 | @a.stub(:instance).and_return(@a) 99 | @b.stub(:instance).and_return(@b) 100 | @c.stub(:instance).and_return(@c) 101 | @d.stub(:instance).and_return(@d) 102 | 103 | @policy = policy :test, :roles => :app do; requires :a; end 104 | $terminal.stub(:choose).and_return(@c) # stub out highline asking questions 105 | end 106 | 107 | describe 'when applying' do 108 | include Sprinkle::Package 109 | 110 | it 'should determine the packages to install via the hierarchy dependency tree of each package in the policy' do 111 | @a.should_receive(:process).and_return 112 | @b.should_receive(:process).and_return 113 | @c.should_receive(:process).and_return 114 | @d.should_not_receive(:process) 115 | end 116 | 117 | it 'should normalize (ie remove duplicates from) the installation order of all packages including dependencies' do 118 | @e = package :e do; requires :b; end 119 | @policy.requires :e 120 | @e.stub(:instance).and_return(@e) 121 | 122 | @a.should_receive(:process).once.and_return 123 | @b.should_receive(:process).once.and_return 124 | @c.should_receive(:process).once.and_return 125 | @d.should_not_receive(:process) 126 | @e.should_receive(:process).once.and_return 127 | end 128 | end 129 | 130 | describe 'containing package dependencies with versions' do 131 | 132 | it 'should select the correct package version when applying' do 133 | @my3 = package :mysql do; version 3; end 134 | @my4 = package :mysql do; version 4; end 135 | @my5 = package :mysql do; version 5; end 136 | @e = package :e do; requires :mysql, :version => "4"; end 137 | @policy.requires :e 138 | @e.stub(:instance).and_return @e 139 | @my4.stub(:instance).and_return @my4 140 | @my3.should_not_receive(:process) 141 | @my5.should_not_receive(:process) 142 | @my4.should_receive(:process) 143 | end 144 | end 145 | 146 | describe 'containing virtual packages' do 147 | 148 | it 'should automatically select a concrete package implementation for a virtual one when there exists only one possible selection' do 149 | @policy = policy :virtual, :roles => :app do; requires :xyz; end 150 | @b.should_receive(:process) 151 | end 152 | 153 | it 'should ask the user for the concrete package implementation to use for a virtual one when more than one possible choice exists' do 154 | @policy = policy :virtual, :roles => :app do; requires :abc; end 155 | $terminal.should_receive(:choose).and_return(@c) 156 | @c.should_receive(:process) 157 | end 158 | 159 | end 160 | 161 | after do 162 | @policy.process(@deployment) 163 | end 164 | end 165 | end 166 | 167 | describe Sprinkle::Policy, 'with missing packages' do 168 | 169 | before do 170 | @deployment = double(Sprinkle::Deployment) 171 | actor = double(:servers_for_role? => true) 172 | @deployment.stub(:style).and_return(actor) 173 | Sprinkle::Package::PACKAGES.clear # reset full package list before each spec is run 174 | 175 | @policy = policy :test, :roles => :app do; requires :z; end 176 | $terminal.stub(:choose).and_return(:c) # stub out highline asking questions 177 | end 178 | 179 | it 'should raise an error if a package is missing' do 180 | lambda { @policy.process(@deployment) }.should raise_error 181 | end 182 | 183 | end 184 | -------------------------------------------------------------------------------- /spec/sprinkle/script_spec.rb: -------------------------------------------------------------------------------- 1 | require File.expand_path("../spec_helper", File.dirname(__FILE__)) 2 | 3 | describe Sprinkle::Script, 'class' do 4 | 5 | it 'should define a entry point into the system' do 6 | Sprinkle::Script.should respond_to(:sprinkle) 7 | end 8 | 9 | it 'should respond to deployment' do 10 | Sprinkle::Script.new.should respond_to(:deployment) 11 | end 12 | 13 | end 14 | 15 | describe Sprinkle::Script, 'when given a script' do 16 | 17 | before do 18 | @script = 'script' 19 | @filename = 'filename' 20 | 21 | @sprinkle = Sprinkle::Script.new 22 | Sprinkle::Script.stub(:new).and_return(@sprinkle) 23 | end 24 | 25 | it 'should create a new sprinkle instance' do 26 | Sprinkle::Script.should_receive(:new).and_return(@sprinkle) 27 | Sprinkle::Script.sprinkle @script 28 | end 29 | 30 | it 'should evaulate the sprinkle script against the instance' do 31 | @sprinkle.should_receive(:instance_eval).and_return 32 | Sprinkle::Script.sprinkle @script 33 | end 34 | 35 | it 'should specify the filename if given for line number errors' do 36 | @sprinkle.should_receive(:instance_eval).with(@script, @filename).and_return 37 | Sprinkle::Script.sprinkle @script, @filename 38 | end 39 | 40 | it 'should specify a filename of __SCRIPT__ by default if none is provided' do 41 | @sprinkle.should_receive(:instance_eval).with(@script, '__SCRIPT__').and_return 42 | Sprinkle::Script.sprinkle @script 43 | end 44 | 45 | it 'should automatically run in production mode by default' do 46 | @sprinkle.should_receive(:instance_eval).with(@script, '__SCRIPT__').and_return 47 | Sprinkle::Script.sprinkle @script 48 | end 49 | 50 | it 'should ask the Sprinkle instance to process the data from the script' do 51 | @sprinkle.should_receive(:sprinkle) 52 | Sprinkle::Script.sprinkle @script 53 | end 54 | 55 | end 56 | -------------------------------------------------------------------------------- /spec/sprinkle/sprinkle_spec.rb: -------------------------------------------------------------------------------- 1 | require File.expand_path("../spec_helper", File.dirname(__FILE__)) 2 | 3 | describe Sprinkle do 4 | 5 | it 'should automatically extend Object to support package, policy and deployment DSL keywords' do 6 | %w( package policy ).each do |keyword| 7 | Object.should respond_to(keyword.to_sym) 8 | end 9 | end 10 | 11 | it 'should default to production mode' do 12 | Sprinkle::OPTIONS[:testing].should be_false 13 | end 14 | 15 | it 'should automatically create a logger object on Kernel' do 16 | Object.should respond_to(:logger) 17 | logger.should_not be_nil 18 | # ActiveSupport::BufferedLogger was deprecated and replaced by ActiveSupport::Logger in Rails 4. 19 | # Use ActiveSupport::Logger if available 20 | active_support_logger = defined?(ActiveSupport::Logger) ? ActiveSupport::Logger : ActiveSupport::BufferedLogger 21 | logger.class.should == active_support_logger 22 | end 23 | 24 | it 'should create a logger of level INFO' do 25 | logger.level.should == Logger::Severity::INFO 26 | end 27 | 28 | end 29 | -------------------------------------------------------------------------------- /spec/templates/locals.erb: -------------------------------------------------------------------------------- 1 | hello <%= world %> -------------------------------------------------------------------------------- /spec/templates/test.erb: -------------------------------------------------------------------------------- 1 | hello <%= @world %> -------------------------------------------------------------------------------- /sprinkle.gemspec: -------------------------------------------------------------------------------- 1 | require "./lib/sprinkle/version" 2 | require 'date' 3 | 4 | Gem::Specification.new do |s| 5 | s.name = "sprinkle" 6 | s.version = Sprinkle::Version 7 | 8 | s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version= 9 | s.authors = ["Marcus Crafter", "Josh Goebel"] 10 | s.date = Date.today 11 | s.description = "Ruby DSL based software provisioning tool" 12 | s.email = "crafterm@redartisan.com" 13 | s.executables = ["sprinkle"] 14 | s.extra_rdoc_files = [ 15 | "README.md" 16 | ] 17 | 18 | s.license = "MIT" 19 | 20 | s.files = `git ls-files`.split("\n") 21 | s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n") 22 | s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) } 23 | 24 | s.homepage = "https://github.com/sprinkle-tool/sprinkle" 25 | s.require_paths = ["lib"] 26 | s.rubyforge_project = "sprinkle" 27 | s.rubygems_version = "1.8.15" 28 | s.summary = "Ruby DSL based software provisioning tool" 29 | 30 | s.add_development_dependency(%q, [">= 2.5"]) 31 | s.add_development_dependency(%q, [">= 0.8.7"]) 32 | s.add_development_dependency(%q, [">= 3.12"]) 33 | s.add_runtime_dependency(%q, [">= 2.7.0"]) 34 | s.add_runtime_dependency(%q, [">= 1.1.0"]) 35 | s.add_runtime_dependency(%q, [">= 2.0.2"]) 36 | s.add_runtime_dependency(%q, [">= 1.4.0"]) 37 | s.add_runtime_dependency(%q, [">= 2.5.5", '< 3']) 38 | 39 | end 40 | 41 | -------------------------------------------------------------------------------- /templates/test.erb: -------------------------------------------------------------------------------- 1 | hello <%= @world %> --------------------------------------------------------------------------------