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
@@ -24,8 +24,9 @@ module Sprinkle
24
24
  @options[sym] || @package.send(sym, *args, &block) # try the parents options if unknown
25
25
  end
26
26
 
27
+ # both nil and false should return false
27
28
  def option?(sym)
28
- !@options[sym].nil?
29
+ !!@options[sym]
29
30
  end
30
31
  end
31
32
  end
@@ -52,7 +52,9 @@ module Sprinkle
52
52
  # the actor. For more information on what configuration options are
53
53
  # available, view the corresponding Sprinkle::Actors page.
54
54
  def delivery(type, &block) #:doc:
55
- @style = ("Sprinkle::Actors::" + type.to_s.titleize).constantize.new &block
55
+ type=type.to_s.titleize
56
+ type="SSH" if type=="Ssh"
57
+ @style = ("Sprinkle::Actors::" + type).constantize.new &block
56
58
  end
57
59
 
58
60
  def method_missing(sym, *args, &block) #:nodoc:
@@ -62,11 +64,29 @@ module Sprinkle
62
64
  def respond_to?(sym) #:nodoc:
63
65
  !!@defaults[sym]
64
66
  end
67
+
68
+ def active_policies
69
+ if role=Sprinkle::OPTIONS[:only_role]
70
+ role=role.to_sym
71
+ POLICIES.select {|x| [x.roles].flatten.include?(role) }
72
+ else
73
+ POLICIES
74
+ end
75
+ end
65
76
 
66
77
  def process #:nodoc:
67
- POLICIES.each do |policy|
78
+ active_policies.each do |policy|
68
79
  policy.process(self)
69
80
  end
81
+ rescue Sprinkle::Errors::RemoteCommandFailure => e
82
+ e.print_summary
83
+ exit
84
+ rescue Sprinkle::Errors::TransferFailure => e
85
+ e.print_summary
86
+ exit
87
+ ensure
88
+ # do any cleanup our actor may need to close network sockets, etc
89
+ @style.teardown if @style.respond_to?(:teardown)
70
90
  end
71
91
  end
72
92
  end
@@ -0,0 +1,41 @@
1
+ module Sprinkle
2
+ module Errors
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
@@ -0,0 +1,24 @@
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
@@ -0,0 +1,28 @@
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
@@ -1,7 +1,5 @@
1
1
  module Sprinkle
2
2
  module Installers
3
- # = Apt Package Installer
4
- #
5
3
  # The Apt package installer uses the +apt-get+ command to install
6
4
  # packages. The apt installer has only one option which can be
7
5
  # modified which is the +dependencies_only+ option. When this is
@@ -13,38 +11,41 @@ module Sprinkle
13
11
  # First, a simple installation of the magic_beans package:
14
12
  #
15
13
  # package :magic_beans do
16
- # description "Beans beans they're good for your heart..."
17
14
  # apt 'magic_beans_package'
15
+ # verify { has_apt 'magic_beans_package' }
18
16
  # end
19
17
  #
20
18
  # Second, only build the magic_beans dependencies:
21
19
  #
22
20
  # package :magic_beans_depends do
23
- # apt 'magic_beans_package' { dependencies_only true }
21
+ # apt 'magic_beans_package' do
22
+ # dependencies_only true
23
+ # end
24
24
  # end
25
25
  #
26
26
  # As you can see, setting options is as simple as creating a
27
27
  # block and calling the option as a method with the value as
28
28
  # its parameter.
29
- class Apt < Installer
30
- attr_accessor :packages #:nodoc:
31
-
29
+ class Apt < PackageInstaller
32
30
  def initialize(parent, *packages, &block) #:nodoc:
33
- packages.flatten!
34
-
35
- options = { :dependencies_only => false }
36
- options.update(packages.pop) if packages.last.is_a?(Hash)
37
-
38
- super parent, options, &block
39
-
40
- @packages = packages
31
+ super parent, *packages, &block
32
+ @options.reverse_merge!(:dependencies_only => false)
33
+ end
34
+
35
+ auto_api
36
+
37
+ verify_api do
38
+ def has_apt(package)
39
+ @commands << "dpkg --status #{package} | grep \"ok installed\""
40
+ end
41
41
  end
42
42
 
43
43
  protected
44
44
 
45
45
  def install_commands #:nodoc:
46
46
  command = @options[:dependencies_only] ? 'build-dep' : 'install'
47
- "env DEBCONF_TERSE='yes' DEBIAN_PRIORITY='critical' DEBIAN_FRONTEND=noninteractive apt-get --force-yes -qyu #{command} #{@packages.join(' ')}"
47
+ noninteractive = "env DEBCONF_TERSE='yes' DEBIAN_PRIORITY='critical' DEBIAN_FRONTEND=noninteractive"
48
+ "#{noninteractive} #{sudo_cmd}apt-get --force-yes -qyu #{command} #{@packages.join(' ')}"
48
49
  end
49
50
 
50
51
  end
@@ -1,16 +1,27 @@
1
1
  module Sprinkle
2
2
  module Installers
3
- # = Binary Installer
3
+ # The Binary installer will download a binary archive and then extract
4
+ # it in the directory specified by the prefix option.
4
5
  #
5
- # binary "http://some.url.com/archive.tar.gz" do
6
- # prefix "/home/user/local"
7
- # archives "/home/user/sources"
8
- # end
6
+ # == Example Usage
9
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.
10
15
  class Binary < Installer
16
+
17
+ api do
18
+ def binary(source, options = {}, &block)
19
+ install Sprinkle::Installers::Binary.new(self, source, options, &block)
20
+ end
21
+ end
22
+
11
23
  def initialize(parent, binary_archive, options = {}, &block) #:nodoc:
12
24
  @binary_archive = binary_archive
13
- @options = options
14
25
  super parent, options, &block
15
26
  end
16
27
 
@@ -24,10 +35,14 @@ module Sprinkle
24
35
 
25
36
  def install_commands #:nodoc:
26
37
  commands = [ "bash -c 'wget -cq --directory-prefix=#{@options[:archives].first} #{@binary_archive}'" ]
27
- commands << "bash -c 'cd #{@options[:prefix].first} && #{extract_command} #{@options[:archives].first}/#{@binary_archive.split("/").last}'"
38
+ commands << "bash -c \"cd #{@options[:prefix].first} && #{extract_command} '#{@options[:archives].first}/#{archive_name}'\""
39
+ end
40
+
41
+ def archive_name #:nodoc:
42
+ @archive_name ||= @binary_archive.split("/").last.gsub('%20', ' ')
28
43
  end
29
44
 
30
- def extract_command(archive_name = @binary_archive.split("/").last)
45
+ def extract_command #:nodoc:
31
46
  case archive_name
32
47
  when /(tar.gz)|(tgz)$/
33
48
  'tar xzf'
@@ -9,24 +9,31 @@ module Sprinkle
9
9
  #
10
10
  # package :magic_beans do
11
11
  # description "Beans beans they're good for your heart..."
12
- # brew 'magic_beans_package'
12
+ # brew 'ntp'
13
+ #
14
+ # verify { has_brew 'ntp' }
15
+ #
13
16
  # end
14
17
  #
15
- class Brew < Installer
16
- attr_accessor :formulas #:nodoc:
18
+ class Brew < PackageInstaller
17
19
 
18
- def initialize(parent, *formulas, &block) #:nodoc:
19
- formulas.flatten!
20
-
21
- super parent, &block
22
-
23
- @formulas = formulas
20
+ api do
21
+ def brew(*names, &block)
22
+ @recommends << :homebrew
23
+ install_package(*names, &block)
24
+ end
25
+ end
26
+
27
+ verify_api do
28
+ def has_brew(package)
29
+ @commands << "brew list | grep #{package}"
30
+ end
24
31
  end
25
32
 
26
33
  protected
27
34
 
28
35
  def install_commands #:nodoc:
29
- "brew install #{@formulas.join(' ')}"
36
+ "brew install #{@packages.join(' ')}"
30
37
  end
31
38
 
32
39
  end
@@ -1,10 +1,8 @@
1
1
  module Sprinkle
2
2
  module Installers
3
- # = OpenBSD and FreeBSD Port Installer
4
- #
5
- # The Port installer installs OpenBSD and FreeBSD ports.
3
+ # The BSD Port installer installs OpenBSD and FreeBSD ports.
6
4
  # Before usage, the ports sytem must be installed and
7
- # read on the target operating system.
5
+ # ready on the target operating system.
8
6
  #
9
7
  # == Example Usage
10
8
  #
@@ -16,9 +14,15 @@ module Sprinkle
16
14
  #
17
15
  class BsdPort < Installer
18
16
  attr_accessor :port #:nodoc:
17
+
18
+ api do
19
+ def bsd_port(port, options ={}, &block)
20
+ install Sprinkle::Installers::BsdPort.new(self, port, options, &block)
21
+ end
22
+ end
19
23
 
20
- def initialize(parent, port, &block) #:nodoc:
21
- super parent, &block
24
+ def initialize(parent, port, options={}, &block) #:nodoc:
25
+ super parent, options, &block
22
26
  @port = port
23
27
  end
24
28
 
@@ -1,7 +1,5 @@
1
1
  module Sprinkle
2
2
  module Installers
3
- # = Deb Package Installer
4
- #
5
3
  # The Deb installer installs deb packages sourced from a remote URL
6
4
  #
7
5
  # == Example Usage
@@ -12,14 +10,9 @@ module Sprinkle
12
10
  # deb 'http://debs.example.com/magic_beans.deb'
13
11
  # end
14
12
  #
15
- class Deb < Installer
16
- attr_accessor :packages #:nodoc:
17
-
18
- def initialize(parent, packages, &block) #:nodoc:
19
- super parent, &block
20
- packages = [packages] unless packages.is_a? Array
21
- @packages = packages
22
- end
13
+ class Deb < PackageInstaller
14
+
15
+ auto_api
23
16
 
24
17
  protected
25
18
 
@@ -1,8 +1,6 @@
1
1
  module Sprinkle
2
2
  module Installers
3
- # = FreeBSD Package Installer
4
- #
5
- # The Pkg package installer installs FreeBSD packages.
3
+ # The FreeBSDPkg installer installs FreeBSD packages.
6
4
  #
7
5
  # == Example Usage
8
6
  #
@@ -17,14 +15,10 @@ module Sprinkle
17
15
  # package :magic_beans do
18
16
  # freebsd_pkg %w(magic_beans magic_sauce)
19
17
  # end
20
- class FreebsdPkg < Installer
21
- attr_accessor :packages #:nodoc:
22
-
23
- def initialize(parent, packages, &block) #:nodoc:
24
- super parent, &block
25
- packages = [packages] unless packages.is_a? Array
26
- @packages = packages
27
- end
18
+ #
19
+ class FreebsdPkg < PackageInstaller
20
+
21
+ auto_api
28
22
 
29
23
  protected
30
24
 
@@ -19,9 +19,15 @@ module Sprinkle
19
19
  #
20
20
  class FreebsdPortinstall < Installer
21
21
  attr_accessor :port #:nodoc:
22
+
23
+ api do
24
+ def freebsd_portinstall(port, options={}, &block)
25
+ install Sprinkle::Installers::FreebsdPortinstall.new(self, port, options, &block)
26
+ end
27
+ end
22
28
 
23
- def initialize(parent, port, &block) #:nodoc:
24
- super parent, &block
29
+ def initialize(parent, port, options={}, &block) #:nodoc:
30
+ super parent, options, &block
25
31
  @port = port
26
32
  end
27
33
 
@@ -1,8 +1,6 @@
1
1
  module Sprinkle
2
2
  module Installers
3
- # = Ruby Gem Package Installer
4
- #
5
- # The gem package installer installs ruby gems.
3
+ # The gem package installer installs Ruby gems.
6
4
  #
7
5
  # The installer has a single optional configuration: source.
8
6
  # By changing source you can specify a given ruby gems
@@ -29,6 +27,14 @@ module Sprinkle
29
27
  # block and calling the option as a method with the value as
30
28
  # its parameter.
31
29
  class Gem < Installer
30
+
31
+ api do
32
+ def gem(name, options = {}, &block)
33
+ @recommends << :rubygems
34
+ install Sprinkle::Installers::Gem.new(self, name, options, &block)
35
+ end
36
+ end
37
+
32
38
  attr_accessor :gem #:nodoc:
33
39
 
34
40
  def initialize(parent, gem, options = {}, &block) #:nodoc:
@@ -1,13 +1,37 @@
1
1
  module Sprinkle
2
2
  module Installers
3
3
  class Group < Installer
4
- def initialize(package, groupname, options, &block)
5
- super package, &block
4
+ # The user installer helps add groups. You may pass flags as an option.
5
+ #
6
+ # == Example Usage
7
+ #
8
+ # package :users do
9
+ # add_group 'webguys', :flags => "--shell /usr/bin/zsh"
10
+ #
11
+ # verify do
12
+ # has_group 'webguys'
13
+ # end
14
+ # end
15
+
16
+ api do
17
+ def add_group(group, options={}, &block)
18
+ install Sprinkle::Installers::Group.new(self, group, options, &block)
19
+ end
20
+ end
21
+
22
+ verify_api do
23
+ def has_group(group)
24
+ @commands << "id -g #{group}"
25
+ end
26
+ end
27
+
28
+ def initialize(package, groupname, options, &block) #:nodoc:
29
+ super package, options, &block
6
30
  @groupname = groupname
7
- @options = options
8
31
  end
32
+
9
33
  protected
10
- def install_commands
34
+ def install_commands #:nodoc:
11
35
  "addgroup #{@options[:flags]} #{@groupname}"
12
36
  end
13
37
  end