sprinkle 0.4.2 → 0.5.0.rc1

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 (87) hide show
  1. data/.gitignore +8 -0
  2. data/Gemfile +5 -0
  3. data/Gemfile.lock +54 -0
  4. data/README.markdown +178 -166
  5. data/Rakefile +4 -28
  6. data/bin/sprinkle +14 -1
  7. data/lib/sprinkle.rb +5 -1
  8. data/lib/sprinkle/actors/actors.rb +20 -5
  9. data/lib/sprinkle/actors/capistrano.rb +62 -36
  10. data/lib/sprinkle/actors/dummy.rb +127 -0
  11. data/lib/sprinkle/actors/local.rb +59 -17
  12. data/lib/sprinkle/actors/ssh.rb +189 -107
  13. data/lib/sprinkle/actors/vlad.rb +51 -32
  14. data/lib/sprinkle/configurable.rb +2 -1
  15. data/lib/sprinkle/deployment.rb +22 -2
  16. data/lib/sprinkle/errors/pretty_failure.rb +41 -0
  17. data/lib/sprinkle/errors/remote_command_failure.rb +24 -0
  18. data/lib/sprinkle/errors/transfer_failure.rb +28 -0
  19. data/lib/sprinkle/installers/apt.rb +17 -16
  20. data/lib/sprinkle/installers/binary.rb +23 -8
  21. data/lib/sprinkle/installers/brew.rb +17 -10
  22. data/lib/sprinkle/installers/bsd_port.rb +10 -6
  23. data/lib/sprinkle/installers/deb.rb +3 -10
  24. data/lib/sprinkle/installers/freebsd_pkg.rb +5 -11
  25. data/lib/sprinkle/installers/freebsd_portinstall.rb +8 -2
  26. data/lib/sprinkle/installers/gem.rb +9 -3
  27. data/lib/sprinkle/installers/group.rb +28 -4
  28. data/lib/sprinkle/installers/installer.rb +58 -7
  29. data/lib/sprinkle/installers/mac_port.rb +13 -6
  30. data/lib/sprinkle/installers/npm.rb +42 -0
  31. data/lib/sprinkle/installers/openbsd_pkg.rb +4 -11
  32. data/lib/sprinkle/installers/opensolaris_pkg.rb +7 -13
  33. data/lib/sprinkle/installers/package_installer.rb +33 -0
  34. data/lib/sprinkle/installers/pacman.rb +5 -13
  35. data/lib/sprinkle/installers/pear.rb +40 -0
  36. data/lib/sprinkle/installers/push_text.rb +18 -5
  37. data/lib/sprinkle/installers/rake.rb +7 -2
  38. data/lib/sprinkle/installers/reconnect.rb +29 -0
  39. data/lib/sprinkle/installers/replace_text.rb +11 -2
  40. data/lib/sprinkle/installers/rpm.rb +8 -6
  41. data/lib/sprinkle/installers/runner.rb +41 -16
  42. data/lib/sprinkle/installers/smart.rb +6 -17
  43. data/lib/sprinkle/installers/source.rb +22 -10
  44. data/lib/sprinkle/installers/thor.rb +7 -0
  45. data/lib/sprinkle/installers/transfer.rb +62 -41
  46. data/lib/sprinkle/installers/user.rb +34 -4
  47. data/lib/sprinkle/installers/yum.rb +10 -10
  48. data/lib/sprinkle/installers/zypper.rb +4 -15
  49. data/lib/sprinkle/package.rb +81 -98
  50. data/lib/sprinkle/policy.rb +11 -4
  51. data/lib/sprinkle/utility/log_recorder.rb +33 -0
  52. data/lib/sprinkle/verifiers/directory.rb +1 -1
  53. data/lib/sprinkle/verifiers/executable.rb +1 -1
  54. data/lib/sprinkle/verifiers/file.rb +11 -2
  55. data/lib/sprinkle/verifiers/package.rb +2 -14
  56. data/lib/sprinkle/verifiers/permission.rb +40 -0
  57. data/lib/sprinkle/verifiers/symlink.rb +2 -2
  58. data/lib/sprinkle/verifiers/test.rb +21 -0
  59. data/lib/sprinkle/verify.rb +3 -3
  60. data/lib/sprinkle/version.rb +3 -0
  61. data/spec/fixtures/my_file.txt +1 -0
  62. data/spec/sprinkle/actors/capistrano_spec.rb +16 -3
  63. data/spec/sprinkle/actors/local_spec.rb +24 -6
  64. data/spec/sprinkle/actors/ssh_spec.rb +38 -0
  65. data/spec/sprinkle/installers/apt_spec.rb +23 -2
  66. data/spec/sprinkle/installers/binary_spec.rb +22 -14
  67. data/spec/sprinkle/installers/brew_spec.rb +4 -4
  68. data/spec/sprinkle/installers/installer_spec.rb +36 -7
  69. data/spec/sprinkle/installers/npm_spec.rb +16 -0
  70. data/spec/sprinkle/installers/pear_spec.rb +16 -0
  71. data/spec/sprinkle/installers/push_text_spec.rb +23 -1
  72. data/spec/sprinkle/installers/rpm_spec.rb +5 -0
  73. data/spec/sprinkle/installers/runner_spec.rb +27 -11
  74. data/spec/sprinkle/installers/smart_spec.rb +60 -0
  75. data/spec/sprinkle/installers/source_spec.rb +4 -4
  76. data/spec/sprinkle/installers/transfer_spec.rb +31 -16
  77. data/spec/sprinkle/package_spec.rb +10 -2
  78. data/spec/sprinkle/policy_spec.rb +6 -0
  79. data/spec/sprinkle/verify_spec.rb +18 -4
  80. data/sprinkle.gemspec +22 -158
  81. metadata +178 -96
  82. data/TODO +0 -56
  83. data/VERSION +0 -1
  84. data/lib/sprinkle/verifiers/apt.rb +0 -21
  85. data/lib/sprinkle/verifiers/brew.rb +0 -21
  86. data/lib/sprinkle/verifiers/rpm.rb +0 -21
  87. data/lib/sprinkle/verifiers/users_groups.rb +0 -33
@@ -1,4 +1,8 @@
1
1
  module Sprinkle
2
+ # Installers are where the bulk of the work in Sprinkle happens. Installers are
3
+ # the building blocks of packages. Typically each unique type of install
4
+ # command has it's own installer class.
5
+ #
2
6
  module Installers
3
7
  # The base class which all installers must subclass, this class makes
4
8
  # sure all installers share some general features, which are outlined
@@ -8,9 +12,16 @@ module Sprinkle
8
12
  #
9
13
  # With all installation methods you have the ability to specify multiple
10
14
  # pre/post installation hooks. This gives you the ability to specify
11
- # commands to run before and after an installation takes place. All
12
- # commands by default are sudo'd so there is no need to include "sudo"
13
- # in the command itself. There are three ways to specify a pre/post hook.
15
+ # commands to run before and after an installation takes place.
16
+ # There are three ways to specify a pre/post hook.
17
+
18
+ # Note about sudo:
19
+ # When using the Capistrano actor all commands by default are run using
20
+ # sudo (unless your Capfile includes "set :use_sudo, false"). If you wish
21
+ # to use sudo periodically with "set :user_sudo, false" or with an actor
22
+ # other than Capistrano then you can just append it to your command. Some
23
+ # installers (transfer) also support a :sudo option, so check each
24
+ # installer for details.
14
25
  #
15
26
  # First, a single command:
16
27
  #
@@ -45,11 +56,37 @@ module Sprinkle
45
56
 
46
57
  def initialize(package, options = {}, &block) #:nodoc:
47
58
  @package = package
48
- @options = options
59
+ @options = options || {}
49
60
  @pre = {}; @post = {}
50
61
  self.instance_eval(&block) if block
51
62
  end
63
+
64
+ class << self
65
+ def subclasses
66
+ @subclasses ||= []
67
+ end
68
+
69
+ def api(&block)
70
+ Sprinkle::Package::Package.class_eval &block
71
+ end
72
+
73
+ def verify_api(&block)
74
+ Sprinkle::Verify.class_eval &block
75
+ end
52
76
 
77
+ def inherited(base)
78
+ subclasses << base
79
+ end
80
+ end
81
+
82
+ def sudo_cmd
83
+ "sudo " if sudo?
84
+ end
85
+
86
+ def sudo?
87
+ options[:sudo] or package.sudo?
88
+ end
89
+
53
90
  def pre(stage, *commands)
54
91
  @pre[stage] ||= []
55
92
  @pre[stage] += commands
@@ -61,6 +98,15 @@ module Sprinkle
61
98
  @post[stage] += commands
62
99
  @post[stage] += [yield] if block_given?
63
100
  end
101
+
102
+ def per_host?
103
+ return false
104
+ @per_host
105
+ end
106
+
107
+ # Called right before an installer is exected, can be used for logging
108
+ # and announcing what is about to happen
109
+ def announce; end
64
110
 
65
111
  def process(roles) #:nodoc:
66
112
  assert_delivery
@@ -71,12 +117,11 @@ module Sprinkle
71
117
  end
72
118
 
73
119
  unless Sprinkle::OPTIONS[:testing]
74
- logger.info "--> Installing #{package.name} for roles: #{roles}"
75
- @delivery.process(@package.name, install_sequence, roles)
120
+ logger.debug " --> Running #{self.class.name} for roles: #{roles}"
121
+ @delivery.install(self, roles, :per_host => per_host?)
76
122
  end
77
123
  end
78
124
 
79
- protected
80
125
  # More complicated installers that have different stages, and require pre/post commands
81
126
  # within stages can override install_sequence and take complete control of the install
82
127
  # command sequence construction (eg. source based installer).
@@ -84,6 +129,12 @@ module Sprinkle
84
129
  commands = pre_commands(:install) + [ install_commands ] + post_commands(:install)
85
130
  commands.flatten
86
131
  end
132
+
133
+ protected
134
+
135
+ def log(t, level=:info)
136
+ logger.send(level, t)
137
+ end
87
138
 
88
139
  # A concrete installer (subclass of this virtual class) must override this method
89
140
  # and return the commands it needs to run as either a string or an array.
@@ -1,8 +1,6 @@
1
1
  module Sprinkle
2
2
  module Installers
3
- # = Mac OS X Port Installer (macports)
4
- #
5
- # The Port installer installs macports ports.
3
+ # The MacPort installer installs macports ports.
6
4
  #
7
5
  # == Example Usage
8
6
  #
@@ -13,17 +11,26 @@ module Sprinkle
13
11
  # end
14
12
  #
15
13
  # == Notes
14
+ #
16
15
  # Before MacPorts packages can be installed, the PATH
17
16
  # environment variable probably has to be changed so
18
- # capistrano can find the /opt/local/bin/port executable
17
+ # Sprinkle can find the /opt/local/bin/port executable
19
18
  #
20
19
  # You must set PATH in ~/.ssh/environment on the remote
21
20
  # system and enable 'PermitUserEnvironment yes' in /etc/sshd_config
21
+ #
22
22
  class MacPort < Installer
23
+
24
+ api do
25
+ def mac_port(port, options={}, &block)
26
+ install Sprinkle::Installers::MacPort.new(self, port, options, &block)
27
+ end
28
+ end
29
+
23
30
  attr_accessor :port #:nodoc:
24
31
 
25
- def initialize(parent, port, &block) #:nodoc:
26
- super parent, &block
32
+ def initialize(parent, port, options = {}, &block) #:nodoc:
33
+ super parent, options, &block
27
34
  @port = port
28
35
  end
29
36
 
@@ -0,0 +1,42 @@
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 Sprinkle::Installers::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
+ def install_commands #:nodoc:
37
+ "npm install --global #{@package_name}"
38
+ end
39
+
40
+ end
41
+ end
42
+ end
@@ -1,8 +1,6 @@
1
1
  module Sprinkle
2
2
  module Installers
3
- # = OpenBSD Package Installer
4
- #
5
- # The Pkg package installer installs OpenBSD packages.
3
+ # The OpenBSD package installer installs OpenBSD packages.
6
4
  #
7
5
  # == Example Usage
8
6
  #
@@ -27,14 +25,9 @@ module Sprinkle
27
25
  #
28
26
  # For help on PKG_PATH see section 15.2.2 of the OpenBSD FAQ
29
27
  # (http://www.openbsd.org/faq/faq15.html)
30
- class OpenbsdPkg < Installer
31
- attr_accessor :packages #:nodoc:
32
-
33
- def initialize(parent, packages, &block) #:nodoc:
34
- super parent, &block
35
- packages = [packages] unless packages.is_a? Array
36
- @packages = packages
37
- end
28
+ class OpenbsdPkg < PackageInstaller
29
+
30
+ auto_api
38
31
 
39
32
  protected
40
33
 
@@ -1,8 +1,6 @@
1
1
  module Sprinkle
2
2
  module Installers
3
- # = OpenSolaris Package Installer
4
- #
5
- # The Pkg package installer installs OpenSolaris packages.
3
+ # The OpenSolaris package installer installs OpenSolaris packages.
6
4
  #
7
5
  # == Example Usage
8
6
  #
@@ -21,16 +19,12 @@ module Sprinkle
21
19
  # == Note
22
20
  # If you are using capistrano as the deployment method
23
21
  # you will need to add the following lines to your deploy.rb
24
- # set :sudo, 'pfexec'
25
- # set :sudo_prompt, ''
26
- class OpensolarisPkg < Installer
27
- attr_accessor :packages #:nodoc:
28
-
29
- def initialize(parent, packages, &block) #:nodoc:
30
- super parent, &block
31
- packages = [packages] unless packages.is_a? Array
32
- @packages = packages
33
- end
22
+ #
23
+ # set :sudo, 'pfexec'
24
+ # set :sudo_prompt, ''
25
+ class OpensolarisPkg < PackageInstaller
26
+
27
+ auto_api
34
28
 
35
29
  protected
36
30
 
@@ -0,0 +1,33 @@
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
+ attr_accessor :packages #:nodoc:
8
+
9
+ def initialize(parent, *packages, &block) #:nodoc:
10
+ options = packages.extract_options!
11
+ super parent, options, &block
12
+ @packages = [*packages].flatten
13
+ end
14
+
15
+ def self.auto_api
16
+ method_name = self.to_s.underscore.split("/").last
17
+ class_name = self.to_s
18
+ api do
19
+ method="def #{method_name}(*names, &block)
20
+ install #{class_name}.new(self, *names, &block)
21
+ end"
22
+ eval(method)
23
+ end
24
+ end
25
+
26
+ # called by subclasses of PackageInstaller
27
+ def install_package(*names, &block) #:nodoc:
28
+ install Sprinkle::Installers::class.new(self, *names, &block)
29
+ end
30
+
31
+ end
32
+ end
33
+ end
@@ -1,21 +1,13 @@
1
1
  module Sprinkle
2
2
  module Installers
3
- class Pacman < Installer
4
- attr_accessor :packages
5
-
6
- def initialize(parent, *packages, &block)
7
- super parent, options, &block
8
-
9
- packages = [packages] unless packages.is_a?(Array)
10
- packages.flatten!
11
-
12
-
13
- @packages = packages
14
- end
3
+ # The pacman installer installs Pacman packages
4
+ class Pacman < PackageInstaller
5
+
6
+ auto_api
15
7
 
16
8
  protected
17
9
 
18
- def install_commands
10
+ def install_commands #:nodoc:
19
11
  "pacman -Sy #{@packages.join(' ')} --no-confirm --needed"
20
12
  end
21
13
  end
@@ -0,0 +1,40 @@
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 Sprinkle::Installers::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
+
38
+ end
39
+ end
40
+ end
@@ -2,9 +2,7 @@ module Sprinkle
2
2
  module Installers
3
3
  # Beware, strange "installer" coming your way.
4
4
  #
5
- # = Text configuration installer
6
- #
7
- # This installer pushes simple configuration into a file.
5
+ # This push text installer pushes simple configuration into a file.
8
6
  #
9
7
  # == Example Usage
10
8
  #
@@ -22,14 +20,29 @@ module Sprinkle
22
20
  # end
23
21
  #
24
22
  # A special verify step exists for this very installer
25
- # its known as file_contains, it will test that a file indeed
23
+ # its known as +file_contains+, it will test that a file indeed
26
24
  # contains a substring that you send it.
27
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
+ #
28
33
  class PushText < Installer
29
34
  attr_accessor :text, :path #:nodoc:
35
+
36
+ api do
37
+ def push_text(text, path, options = {}, &block)
38
+ install Sprinkle::Installers::PushText.new(self, text, path, options, &block)
39
+ end
40
+ end
30
41
 
31
42
  def initialize(parent, text, path, options={}, &block) #:nodoc:
32
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)
33
46
  @text = text
34
47
  @path = path
35
48
  end
@@ -37,7 +50,7 @@ module Sprinkle
37
50
  protected
38
51
 
39
52
  def install_commands #:nodoc:
40
- "#{"#{'sudo ' if option?(:sudo)}grep \"^#{@text.gsub("'", "'\\\\''").gsub("\n", '\n')}$\" #{@path} ||" if option?(:idempotent) }/bin/echo -e '#{@text.gsub("'", "'\\\\''").gsub("\n", '\n')}' |#{'sudo ' if option?(:sudo)}tee -a #{@path}"
53
+ "#{"#{sudo_cmd}grep \"^#{@text.gsub("'", "'\\\\''").gsub("\n", '\n')}$\" #{@path} || " if option?(:idempotent) }/bin/echo -e '#{@text.gsub("'", "'\\\\''").gsub("\n", '\n')}' |#{sudo_cmd}tee -a #{@path}"
41
54
  end
42
55
 
43
56
  end
@@ -1,7 +1,5 @@
1
1
  module Sprinkle
2
2
  module Installers
3
- # = Rake Installer
4
- #
5
3
  # This installer runs a rake command.
6
4
  #
7
5
  # == Example Usage
@@ -20,6 +18,13 @@ module Sprinkle
20
18
  # end
21
19
 
22
20
  class Rake < Installer
21
+
22
+ api do
23
+ def rake(name, options = {}, &block)
24
+ install Sprinkle::Installers::Rake.new(self, name, options, &block)
25
+ end
26
+ end
27
+
23
28
  def initialize(parent, commands, options = {}, &block) #:nodoc:
24
29
  super parent, options, &block
25
30
  @commands = commands
@@ -0,0 +1,29 @@
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
+ :RECONNECT
25
+ end
26
+
27
+ end
28
+ end
29
+ end