sprinkle 0.4.2 → 0.5.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
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