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
@@ -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