mitchellh-sprinkle 0.1.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (70) hide show
  1. data/CREDITS +16 -0
  2. data/History.txt +4 -0
  3. data/MIT-LICENSE +20 -0
  4. data/Manifest.txt +68 -0
  5. data/README.txt +224 -0
  6. data/Rakefile +4 -0
  7. data/bin/sprinkle +86 -0
  8. data/config/hoe.rb +70 -0
  9. data/config/requirements.rb +17 -0
  10. data/examples/merb/deploy.rb +5 -0
  11. data/examples/rails/README +15 -0
  12. data/examples/rails/deploy.rb +2 -0
  13. data/examples/rails/packages/database.rb +9 -0
  14. data/examples/rails/packages/essential.rb +6 -0
  15. data/examples/rails/packages/rails.rb +28 -0
  16. data/examples/rails/packages/search.rb +11 -0
  17. data/examples/rails/packages/server.rb +28 -0
  18. data/examples/rails/rails.rb +71 -0
  19. data/examples/sprinkle/sprinkle.rb +38 -0
  20. data/lib/sprinkle/actors/actors.rb +17 -0
  21. data/lib/sprinkle/actors/capistrano.rb +117 -0
  22. data/lib/sprinkle/actors/vlad.rb +65 -0
  23. data/lib/sprinkle/configurable.rb +27 -0
  24. data/lib/sprinkle/deployment.rb +73 -0
  25. data/lib/sprinkle/extensions/arbitrary_options.rb +10 -0
  26. data/lib/sprinkle/extensions/array.rb +5 -0
  27. data/lib/sprinkle/extensions/blank_slate.rb +5 -0
  28. data/lib/sprinkle/extensions/dsl_accessor.rb +15 -0
  29. data/lib/sprinkle/extensions/string.rb +10 -0
  30. data/lib/sprinkle/extensions/symbol.rb +7 -0
  31. data/lib/sprinkle/installers/apt.rb +51 -0
  32. data/lib/sprinkle/installers/gem.rb +61 -0
  33. data/lib/sprinkle/installers/installer.rb +120 -0
  34. data/lib/sprinkle/installers/rake.rb +30 -0
  35. data/lib/sprinkle/installers/rpm.rb +37 -0
  36. data/lib/sprinkle/installers/source.rb +179 -0
  37. data/lib/sprinkle/package.rb +127 -0
  38. data/lib/sprinkle/policy.rb +85 -0
  39. data/lib/sprinkle/script.rb +13 -0
  40. data/lib/sprinkle/verifiers/directory.rb +11 -0
  41. data/lib/sprinkle/verifiers/executable.rb +17 -0
  42. data/lib/sprinkle/verifiers/file.rb +11 -0
  43. data/lib/sprinkle/verifiers/symlink.rb +15 -0
  44. data/lib/sprinkle/verify.rb +55 -0
  45. data/lib/sprinkle/version.rb +9 -0
  46. data/lib/sprinkle.rb +32 -0
  47. data/script/destroy +14 -0
  48. data/script/generate +14 -0
  49. data/spec/spec.opts +1 -0
  50. data/spec/spec_helper.rb +17 -0
  51. data/spec/sprinkle/actors/capistrano_spec.rb +170 -0
  52. data/spec/sprinkle/configurable_spec.rb +46 -0
  53. data/spec/sprinkle/deployment_spec.rb +80 -0
  54. data/spec/sprinkle/extensions/array_spec.rb +19 -0
  55. data/spec/sprinkle/extensions/string_spec.rb +21 -0
  56. data/spec/sprinkle/installers/apt_spec.rb +74 -0
  57. data/spec/sprinkle/installers/gem_spec.rb +75 -0
  58. data/spec/sprinkle/installers/installer_spec.rb +151 -0
  59. data/spec/sprinkle/installers/rpm_spec.rb +50 -0
  60. data/spec/sprinkle/installers/source_spec.rb +331 -0
  61. data/spec/sprinkle/package_spec.rb +422 -0
  62. data/spec/sprinkle/policy_spec.rb +126 -0
  63. data/spec/sprinkle/script_spec.rb +51 -0
  64. data/spec/sprinkle/sprinkle_spec.rb +25 -0
  65. data/spec/sprinkle/verify_spec.rb +137 -0
  66. data/sprinkle.gemspec +43 -0
  67. data/tasks/deployment.rake +34 -0
  68. data/tasks/environment.rake +7 -0
  69. data/tasks/rspec.rake +21 -0
  70. metadata +160 -0
@@ -0,0 +1,71 @@
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 and
6
+ # Sphinx from source, and mysql 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
+
26
+
27
+ # Policies
28
+ #
29
+ # Names a group of packages (optionally with versions) that apply to a particular set of roles:
30
+ #
31
+ # Associates the rails policy to the application servers. Contains rails, and surrounding
32
+ # packages. Note, appserver, database, webserver and search are all virtual packages defined above.
33
+ # If there's only one implementation of a virtual package, it's selected automatically, otherwise
34
+ # the user is requested to select which one to use.
35
+
36
+ policy :rails, :roles => :app do
37
+ requires :rails, :version => '2.1.0'
38
+ requires :appserver
39
+ requires :database
40
+ requires :webserver
41
+ requires :search
42
+ end
43
+
44
+
45
+ # Deployment
46
+ #
47
+ # Defines script wide settings such as a delivery mechanism for executing commands on the target
48
+ # system (eg. capistrano), and installer defaults (eg. build locations, etc):
49
+ #
50
+ # Configures spinkle to use capistrano for delivery of commands to the remote machines (via
51
+ # the named 'deploy' recipe). Also configures 'source' installer defaults to put package gear
52
+ # in /usr/local
53
+
54
+ deployment do
55
+
56
+ # mechanism for deployment
57
+ delivery :capistrano do
58
+ recipes 'deploy'
59
+ end
60
+
61
+ # source based package installer defaults
62
+ source do
63
+ prefix '/usr/local'
64
+ archives '/usr/local/sources'
65
+ builds '/usr/local/build'
66
+ end
67
+
68
+ end
69
+
70
+ # End of script, given the above information, Spinkle will apply the defined policy on all roles using the
71
+ # deployment settings specified.
@@ -0,0 +1,38 @@
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.
@@ -0,0 +1,17 @@
1
+ #--
2
+ # The only point of this file is to give RDoc a definition for
3
+ # Sprinkle::Actors. This file in production is never actually included
4
+ # since ActiveSupport only on-demand loads classes which are needed
5
+ # and this module is never explicitly needed.
6
+ #++
7
+
8
+ module Sprinkle
9
+ # An actor is a method of command delivery to a remote machine. It is the
10
+ # layer between sprinkle and the SSH connection to run commands. This gives
11
+ # you the flexibility to define custom actors, for whatever purpose you need.
12
+ #
13
+ # 99% of the time, however, the two built-in actors Sprinkle::Actors::Capistrano
14
+ # and Sprinkle::Actors::Vlad will be enough.
15
+ module Actors
16
+ end
17
+ end
@@ -0,0 +1,117 @@
1
+ require 'capistrano/cli'
2
+
3
+ module Sprinkle
4
+ module Actors
5
+ # = Capistrano Delivery Method
6
+ #
7
+ # Capistrano is one of the delivery method options available out of the
8
+ # box with Sprinkle. If you have the capistrano gem install, you may use
9
+ # this delivery. The only configuration option available, and which is
10
+ # mandatory to include is +recipes+. An example:
11
+ #
12
+ # deployment do
13
+ # delivery :capistrano do
14
+ # recipes 'deploy'
15
+ # end
16
+ # end
17
+ #
18
+ # Recipes is given a list of files which capistrano will include and load.
19
+ # These recipes are mainly to set variables such as :user, :password, and to
20
+ # set the app domain which will be sprinkled.
21
+ class Capistrano
22
+ attr_accessor :config, :loaded_recipes #:nodoc:
23
+
24
+ def initialize(&block) #:nodoc:
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
+ if block
29
+ self.instance_eval &block
30
+ else
31
+ @config.load 'deploy' # normally in the config directory for rails
32
+ end
33
+ end
34
+
35
+ # Defines a recipe file which will be included by capistrano. Use these
36
+ # recipe files to set capistrano specific configurations. Default recipe
37
+ # included is "deploy." But if any other recipe is specified, it will
38
+ # include that instead. Multiple recipes may be specified through multiple
39
+ # recipes calls, an example:
40
+ #
41
+ # deployment do
42
+ # delivery :capistrano do
43
+ # recipes 'deploy'
44
+ # recipes 'magic_beans'
45
+ # end
46
+ # end
47
+ def recipes(script)
48
+ @loaded_recipes ||= []
49
+ @config.load script
50
+ @loaded_recipes << script
51
+ end
52
+
53
+ def process(name, commands, roles, suppress_and_return_failures = false) #:nodoc:
54
+ define_task(name, roles) do
55
+ via = fetch(:run_method, :sudo)
56
+ commands.each do |command|
57
+ invoke_command command, :via => via
58
+ end
59
+ end
60
+
61
+ begin
62
+ run(name)
63
+ return true
64
+ rescue ::Capistrano::CommandError => e
65
+ return false if suppress_and_return_failures
66
+
67
+ # Reraise error if we're not suppressing it
68
+ raise
69
+ end
70
+ end
71
+
72
+ private
73
+
74
+ # REVISIT: can we set the description somehow?
75
+ def define_task(name, roles, &block)
76
+ @config.task task_sym(name), :roles => roles, &block
77
+ end
78
+
79
+ def run(task)
80
+ @config.send task_sym(task)
81
+ end
82
+
83
+ def task_sym(name)
84
+ "install_#{name.to_task_name}".to_sym
85
+ end
86
+ end
87
+ end
88
+ end
89
+
90
+
91
+ =begin
92
+
93
+ # channel: the SSH channel object used for this response
94
+ # stream: either :err or :out, for stderr or stdout responses
95
+ # output: the text that the server is sending, might be in chunks
96
+ run "apt-get update" do |channel, stream, output|
97
+ if output =~ /Are you sure?/
98
+ answer = Capistrano::CLI.ui.ask("Are you sure: ")
99
+ channel.send_data(answer + "\n")
100
+ else
101
+ # allow the default callback to be processed
102
+ Capistrano::Configuration.default_io_proc.call[channel, stream, output]
103
+ end
104
+ end
105
+
106
+
107
+
108
+ You can tell subversion to use a different username+password by
109
+ setting a couple variables:
110
+ set :svn_username, "my svn username"
111
+ set :svn_password, "my svn password"
112
+ If you don't want to set the password explicitly in your recipe like
113
+ that, you can make capistrano prompt you for it like this:
114
+ set(:svn_password) { Capistrano::CLI.password_prompt("Subversion
115
+ password: ") }
116
+ - Jamis
117
+ =end
@@ -0,0 +1,65 @@
1
+ module Sprinkle
2
+ module Actors
3
+ # = Vlad Delivery Method
4
+ #
5
+ # Vlad is one of the delivery method options available out of the
6
+ # box with Sprinkle. If you have the vlad the deployer gem install, 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 capistrano 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
20
+ require 'vlad'
21
+ attr_accessor :loaded_recipes #:nodoc:
22
+
23
+ def initialize(&block) #:nodoc:
24
+ self.instance_eval &block if block
25
+ end
26
+
27
+ # Defines a script file which will be included by vlad. Use these
28
+ # script files to set vlad specific configurations. Multiple scripts
29
+ # may be specified through multiple script calls, an example:
30
+ #
31
+ # deployment do
32
+ # delivery :vlad do
33
+ # script 'deploy'
34
+ # script 'magic_beans'
35
+ # end
36
+ # end
37
+ def script(name)
38
+ @loaded_recipes ||= []
39
+ self.load name
40
+ @loaded_recipes << script
41
+ end
42
+
43
+ def process(name, commands, roles, suppress_and_return_failures = false) #:nodoc:
44
+ commands = commands.join ' && ' if commands.is_a? Array
45
+ t = remote_task(task_sym(name), :roles => roles) { run commands }
46
+
47
+ begin
48
+ t.invoke
49
+ return true
50
+ rescue ::Vlad::CommandFailedError => e
51
+ return false if suppress_and_return_failures
52
+
53
+ # Reraise error if we're not suppressing it
54
+ raise
55
+ end
56
+ end
57
+
58
+ private
59
+
60
+ def task_sym(name)
61
+ "install_#{name.to_task_name}".to_sym
62
+ end
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,27 @@
1
+ module Sprinkle
2
+ #--
3
+ # TODO: Possible documentation?
4
+ #++
5
+ module Configurable #:nodoc:
6
+ attr_accessor :delivery
7
+
8
+ def defaults(deployment)
9
+ defaults = deployment.defaults[self.class.name.split(/::/).last.downcase.to_sym]
10
+ self.instance_eval(&defaults) if defaults
11
+ @delivery = deployment.style
12
+ end
13
+
14
+ def assert_delivery
15
+ raise 'Unknown command delivery target' unless @delivery
16
+ end
17
+
18
+ def method_missing(sym, *args, &block)
19
+ unless args.empty? # mutate if not set
20
+ @options ||= {}
21
+ @options[sym] = *args unless @options[sym]
22
+ end
23
+
24
+ @options[sym] || @package.send(sym, *args, &block) # try the parents options if unknown
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,73 @@
1
+ module Sprinkle
2
+ # Deployment blocks specify deployment specific information about a
3
+ # sprinkle script. An example:
4
+ #
5
+ # deployment do
6
+ # # mechanism for deployment
7
+ # delivery :capistrano do
8
+ # recipes 'deploy'
9
+ # end
10
+ #
11
+ # # source based package installer defaults
12
+ # source do
13
+ # prefix '/usr/local'
14
+ # archives '/usr/local/sources'
15
+ # builds '/usr/local/build'
16
+ # end
17
+ # end
18
+ #
19
+ # What the above example does is tell sprinkle that we will be using
20
+ # *capistrano* (Sprinkle::Actors::Capistrano) for deployment and
21
+ # everything within the block is capistrano specific configuration.
22
+ # For more information on what options are available, check the corresponding
23
+ # Sprinkle::Actors doc page.
24
+ #
25
+ # In addition to what delivery mechanism we're using, we specify some
26
+ # configuration options for the "source" command. The only things
27
+ # configurable, at this time, in the deployment block other than
28
+ # the delivery method are installers. If installers are configurable,
29
+ # they will say so on their corresponding documentation page. See
30
+ # Sprinkle::Installers
31
+ #
32
+ # <b>Only one deployment block is on any given sprinkle script</b>
33
+ module Deployment
34
+ # The method outlined above which specifies deployment specific information
35
+ # for a sprinkle script. For more information, read the header of this module.
36
+ def deployment(&block)
37
+ @deployment = Deployment.new(&block)
38
+ end
39
+
40
+ class Deployment
41
+ attr_accessor :style, :defaults #:nodoc:
42
+
43
+ def initialize(&block) #:nodoc:
44
+ @defaults = {}
45
+ self.instance_eval(&block)
46
+ raise 'No delivery mechanism defined' unless @style
47
+ end
48
+
49
+ # Specifies which Sprinkle::Actors to use for delivery. Although all
50
+ # actors jobs are the same: to run remote commands on a server, you
51
+ # may have a personal preference. The block you pass is used to configure
52
+ # the actor. For more information on what configuration options are
53
+ # available, view the corresponding Sprinkle::Actors page.
54
+ def delivery(type, &block) #:doc:
55
+ @style = Actors.const_get(type.to_s.titleize).new &block
56
+ end
57
+
58
+ def method_missing(sym, *args, &block) #:nodoc:
59
+ @defaults[sym] = block
60
+ end
61
+
62
+ def respond_to?(sym) #:nodoc:
63
+ !!@defaults[sym]
64
+ end
65
+
66
+ def process #:nodoc:
67
+ POLICIES.each do |policy|
68
+ policy.process(self)
69
+ end
70
+ end
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,10 @@
1
+ module ArbitraryOptions #:nodoc:
2
+ def self.included(base)
3
+ base.alias_method_chain :method_missing, :arbitrary_options
4
+ end
5
+
6
+ def method_missing_with_arbitrary_options(sym, *args, &block)
7
+ self.class.dsl_accessor sym
8
+ send(sym, *args, &block)
9
+ end
10
+ end
@@ -0,0 +1,5 @@
1
+ class Array #:nodoc:
2
+ def to_task_name
3
+ collect(&:to_task_name).join('_')
4
+ end
5
+ end
@@ -0,0 +1,5 @@
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 ).include?(m)
4
+ end
5
+ end
@@ -0,0 +1,15 @@
1
+ class Module #:nodoc:
2
+ def dsl_accessor(*symbols)
3
+ symbols.each do |sym|
4
+ class_eval %{
5
+ def #{sym}(*val)
6
+ if val.empty?
7
+ @#{sym}
8
+ else
9
+ @#{sym} = val.size == 1 ? val[0] : val
10
+ end
11
+ end
12
+ }
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,10 @@
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
@@ -0,0 +1,7 @@
1
+ class Symbol #:nodoc:
2
+
3
+ def to_task_name
4
+ to_s.to_task_name
5
+ end
6
+
7
+ end
@@ -0,0 +1,51 @@
1
+ module Sprinkle
2
+ module Installers
3
+ # = Apt Package Installer
4
+ #
5
+ # The Apt package installer uses the +apt-get+ command to install
6
+ # packages. The apt installer has only one option which can be
7
+ # modified which is the +dependencies_only+ option. When this is
8
+ # set to true, the installer uses +build-dep+ instead of +install+
9
+ # to only build the dependencies.
10
+ #
11
+ # == Example Usage
12
+ #
13
+ # First, a simple installation of the magic_beans package:
14
+ #
15
+ # package :magic_beans do
16
+ # description "Beans beans they're good for your heart..."
17
+ # apt 'magic_beans_package'
18
+ # end
19
+ #
20
+ # Second, only build the magic_beans dependencies:
21
+ #
22
+ # package :magic_beans_depends do
23
+ # apt 'magic_beans_package' { dependencies_only true }
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 < Installer
30
+ attr_accessor :packages #:nodoc:
31
+
32
+ def initialize(parent, *packages, &block) #:nodoc:
33
+ super parent, &block
34
+ packages.flatten!
35
+
36
+ options = { :dependencies_only => false }
37
+ options.update(packages.pop) if packages.last.is_a?(Hash)
38
+
39
+ @command = options[:dependencies_only] ? 'build-dep' : 'install'
40
+ @packages = packages
41
+ end
42
+
43
+ protected
44
+
45
+ def install_commands #:nodoc:
46
+ "DEBCONF_TERSE='yes' DEBIAN_PRIORITY='critical' DEBIAN_FRONTEND=noninteractive apt-get -qyu #{@command} #{@packages.join(' ')}"
47
+ end
48
+
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,61 @@
1
+ module Sprinkle
2
+ module Installers
3
+ # = Ruby Gem Package Installer
4
+ #
5
+ # The gem package installer installs ruby gems.
6
+ #
7
+ # The installer has a single optional configuration: source.
8
+ # By changing source you can specify a given ruby gems
9
+ # repository from which to install.
10
+ #
11
+ # == Example Usage
12
+ #
13
+ # First, a simple installation of the magic_beans gem:
14
+ #
15
+ # package :magic_beans do
16
+ # description "Beans beans they're good for your heart..."
17
+ # gem 'magic_beans'
18
+ # end
19
+ #
20
+ # Second, install magic_beans gem from github:
21
+ #
22
+ # package :magic_beans do
23
+ # gem 'magic_beans_package' do
24
+ # source 'http://gems.github.com'
25
+ # end
26
+ # end
27
+ #
28
+ # As you can see, setting options is as simple as creating a
29
+ # block and calling the option as a method with the value as
30
+ # its parameter.
31
+ class Gem < Installer
32
+ attr_accessor :gem #:nodoc:
33
+
34
+ def initialize(parent, gem, options = {}, &block) #:nodoc:
35
+ super parent, options, &block
36
+ @gem = gem
37
+ end
38
+
39
+ def source(location = nil) #:nodoc:
40
+ # package defines an installer called source so here we specify a method directly
41
+ # rather than rely on the automatic options processing since packages' method missing
42
+ # won't be run
43
+ return @options[:source] unless location
44
+ @options[:source] = location
45
+ end
46
+
47
+ protected
48
+
49
+ # rubygems 0.9.5+ installs dependencies by default, and does platform selection
50
+
51
+ def install_commands #:nodoc:
52
+ cmd = "gem install #{gem}"
53
+ cmd << " --version '#{version}'" if version
54
+ cmd << " --source #{source}" if source
55
+ cmd << " --install-dir #{repository}" if repository
56
+ cmd
57
+ end
58
+
59
+ end
60
+ end
61
+ end