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
@@ -25,6 +25,12 @@ module Sprinkle
25
25
  #
26
26
  class ReplaceText < Installer
27
27
  attr_accessor :regex, :text, :path #:nodoc:
28
+
29
+ api do
30
+ def replace_text(regex, text, path, options={}, &block)
31
+ install Sprinkle::Installers::ReplaceText.new(self, regex, text, path, options, &block)
32
+ end
33
+ end
28
34
 
29
35
  def initialize(parent, regex, text, path, options={}, &block) #:nodoc:
30
36
  super parent, options, &block
@@ -32,11 +38,14 @@ module Sprinkle
32
38
  @text = text
33
39
  @path = path
34
40
  end
41
+
42
+ def announce
43
+ log "--> Replace '#{@regex}' with '#{@text}' in file #{@path}"
44
+ end
35
45
 
36
46
  protected
37
-
47
+
38
48
  def install_commands #:nodoc:
39
- logger.info "--> Replace '#{@regex}' with '#{@text}' in file #{@path}"
40
49
  "#{'sudo ' if option?(:sudo)}sed -i 's/#{@regex.gsub("'", "'\\\\''").gsub("/", "\\\\/").gsub("\n", '\n')}/#{@text.gsub("'", "'\\\\''").gsub("/", "\\\\/").gsub("\n", '\n')}/g' #{@path}"
41
50
  end
42
51
 
@@ -10,6 +10,7 @@ module Sprinkle
10
10
  #
11
11
  # package :magic_beans do
12
12
  # rpm 'magic_beans'
13
+ # verify { has_rpm 'magic_beans' }
13
14
  # end
14
15
  #
15
16
  # You may also specify multiple rpms as an array:
@@ -17,13 +18,14 @@ module Sprinkle
17
18
  # package :magic_beans do
18
19
  # rpm %w(magic_beans magic_sauce)
19
20
  # end
20
- class Rpm < Installer
21
- attr_accessor :packages #:nodoc:
21
+ class Rpm < PackageInstaller
22
22
 
23
- def initialize(parent, packages, &block) #:nodoc:
24
- super parent, &block
25
- packages = [packages] unless packages.is_a? Array
26
- @packages = packages
23
+ auto_api
24
+
25
+ verify_api do
26
+ def has_rpm(package)
27
+ @commands << "rpm -qa | grep #{package}"
28
+ end
27
29
  end
28
30
 
29
31
  protected
@@ -1,18 +1,43 @@
1
1
  module Sprinkle
2
- module Installers
3
- class Runner < Installer
4
- attr_accessor :cmd #:nodoc:
5
-
6
- def initialize(parent, cmd, &block) #:nodoc:
7
- super parent, {}, &block
8
- @cmd = cmd
9
- end
10
-
11
- protected
12
-
13
- def install_commands #:nodoc:
14
- @cmd
15
- end
16
- end
17
- end
2
+ module Installers
3
+ # The runner installer is great for running a simple command.
4
+ #
5
+ # == Example Usage
6
+ #
7
+ # package :magic_beans do
8
+ # runner "make world"
9
+ # end
10
+ #
11
+ # You can also pass multiple commands as arguments or an array.
12
+ #
13
+ # package :magic_beans do
14
+ # runner "make world", "destroy world"
15
+ # runner [ "make world", "destroy world" ]
16
+ # end
17
+ #
18
+ class Runner < Installer
19
+
20
+ api do
21
+ def runner(*cmds, &block)
22
+ options = cmds.extract_options!
23
+ install Sprinkle::Installers::Runner.new(self, cmds, options, &block)
24
+ end
25
+ end
26
+
27
+ attr_accessor :cmds #:nodoc:
28
+ def initialize(parent, cmds, options = {}, &block) #:nodoc:
29
+ super parent, options, &block
30
+ @cmds = [*cmds].flatten
31
+ raise "you need to specify a command" if cmds.nil?
32
+ end
33
+
34
+ protected
35
+
36
+ def install_commands #:nodoc:
37
+ sudo_cmd ?
38
+ @cmds.map { |cmd| "#{sudo_cmd}#{cmd}"} :
39
+ @cmds
40
+ end
41
+ end
42
+ end
18
43
  end
@@ -1,12 +1,11 @@
1
1
  module Sprinkle
2
2
  module Installers
3
- class Smart < Installer
4
- attr_accessor :packages #:nodoc:
5
-
6
- def initialize(parent, packages, &block) #:nodoc:
7
- super parent, &block
8
- packages = [packages] unless packages.is_a? Array
9
- @packages = packages
3
+ class Smart < PackageInstaller
4
+
5
+ api do
6
+ def smart(*names, &block)
7
+ install Sprinkle::Installers::Smart.new(self, *names, &block)
8
+ end
10
9
  end
11
10
 
12
11
  protected
@@ -16,14 +15,4 @@ module Sprinkle
16
15
  end
17
16
  end
18
17
  end
19
- end
20
-
21
- module Sprinkle
22
- module Package
23
- class Package
24
- def smart(*names, &block)
25
- @installer = Sprinkle::Installers::Smart.new(self, *names, &block)
26
- end
27
- end
28
- end
29
18
  end
@@ -62,24 +62,40 @@ module Sprinkle
62
62
  # end
63
63
  # end
64
64
  #
65
+ # Fifth, specifying a custom directory where the archive actually is extracted to:
66
+ #
67
+ # package :ruby_build do
68
+ # source 'https://github.com/sstephenson/ruby-build/archive/v20130227.tar.gz' do
69
+ # custom_dir 'ruby-build-20130227'
70
+ # custom_install './install.sh'
71
+ # end
72
+ # end
73
+ #
65
74
  # As you can see, setting options is as simple as creating a
66
75
  # block and calling the option as a method with the value as
67
76
  # its parameter.
68
77
 
69
78
  class Source < Installer
70
79
  attr_accessor :source #:nodoc:
80
+
81
+ api do
82
+ def source(source, options = {}, &block)
83
+ @recommends << :build_essential # Ubuntu/Debian
84
+ install Sprinkle::Installers::Source.new(self, source, options, &block)
85
+ end
86
+ end
71
87
 
72
88
  def initialize(parent, source, options = {}, &block) #:nodoc:
73
- @source = source
74
89
  super parent, options, &block
90
+ @source = source
91
+ end
92
+
93
+ def install_sequence #:nodoc:
94
+ prepare + download + extract + configure + build + install
75
95
  end
76
96
 
77
97
  protected
78
98
 
79
- def install_sequence #:nodoc:
80
- prepare + download + extract + configure + build + install
81
- end
82
-
83
99
  %w( prepare download extract configure build install ).each do |stage|
84
100
  define_method stage do
85
101
  pre_commands(stage.to_sym) + self.send("#{stage}_commands") + post_commands(stage.to_sym)
@@ -97,11 +113,7 @@ module Sprinkle
97
113
  end
98
114
 
99
115
  def download_commands #:nodoc:
100
- if File.exist? @source
101
- [ "cp #{@source} #{@options[:archives].first}/#{archive_name}" ]
102
- else
103
- [ "wget -cq -O '#{@options[:archives].first}/#{archive_name}' #{@source}" ]
104
- end
116
+ [ "wget -cq -O '#{@options[:archives].first}/#{archive_name}' #{@source}" ]
105
117
  end
106
118
 
107
119
  def extract_commands #:nodoc:
@@ -20,6 +20,13 @@ module Sprinkle
20
20
  # end
21
21
 
22
22
  class Thor < Installer
23
+
24
+ api do
25
+ def thor(name, options = {}, &block)
26
+ install Sprinkle::Installers::Thor.new(self, name, options, &block)
27
+ end
28
+ end
29
+
23
30
  def initialize(parent, commands, options = {}, &block) #:nodoc:
24
31
  super parent, options, &block
25
32
  @commands = commands
@@ -84,16 +84,46 @@ module Sprinkle
84
84
  # directories or changing permissions), you can use the pre/post :install directives
85
85
  # and they will be run.
86
86
  class Transfer < Installer
87
- attr_accessor :source, :destination #:nodoc:
87
+ attr_accessor :source, :destination, :sourcepath #:nodoc:
88
+
89
+ api do
90
+ def transfer(source, destination, options = {}, &block)
91
+ options.merge!(:binding => binding())
92
+ install Sprinkle::Installers::Transfer.new(self, source, destination, options, &block)
93
+ end
94
+ end
88
95
 
89
96
  def initialize(parent, source, destination, options={}, &block) #:nodoc:
90
- super parent, options, &block
91
97
  @source = source
92
98
  @destination = destination
99
+ @orig_destination = destination
100
+ super parent, options, &block
101
+ @binding = options[:binding]
102
+ # perform the transfer in two steps if we're using sudo
103
+ if sudo?
104
+ final = @destination
105
+ @destination = "/tmp/sprinkle_#{File.basename(@destination)}"
106
+ post :install, "#{sudo_cmd}mv #{@destination} #{final}"
107
+ end
108
+ owner(options[:owner]) if options[:owner]
109
+ mode(options[:mode]) if options[:mode]
110
+
111
+ options[:render]=true if source_is_template?
112
+ options[:recursive]=false if options[:render]
113
+ end
114
+
115
+ def owner(owner)
116
+ @owner = owner
117
+ post :install, "#{sudo_cmd}chown #{owner} #{@orig_destination}"
118
+ end
119
+
120
+ def mode(mode)
121
+ @mode = mode
122
+ post :install, "#{sudo_cmd}chmod #{mode} #{@orig_destination}"
93
123
  end
94
124
 
95
125
  def install_commands
96
- nil
126
+ :TRANSFER
97
127
  end
98
128
 
99
129
  def self.render_template(template, context, prefix)
@@ -118,60 +148,51 @@ module Sprinkle
118
148
  end
119
149
 
120
150
  def render_template_file(path, context, prefix)
121
- template = File.read(path)
151
+ template = source_is_template? ? path : File.read(path)
122
152
  tempfile = render_template(template, context, @package.name)
123
153
  tempfile
124
154
  end
155
+
156
+ def source_is_template?
157
+ @source.split("\n").size>1
158
+ end
125
159
 
126
160
  def process(roles) #:nodoc:
127
161
  assert_delivery
128
162
 
129
- if logger.debug?
130
- logger.debug "transfer: #{@source} -> #{@destination} for roles: #{roles}\n"
131
- end
163
+ logger.debug "transfer: #{@source} -> #{@destination} for roles: #{roles}\n"
132
164
 
133
- unless Sprinkle::OPTIONS[:testing]
134
- pre = pre_commands(:install)
135
- unless pre.empty?
136
- sequence = pre; sequence = sequence.join('; ') if sequence.is_a? Array
137
- logger.info "#{@package.name} pre-transfer commands: #{sequence} for roles: #{roles}\n"
138
- @delivery.process @package.name, [pre].flatten, roles
139
- end
165
+ return if Sprinkle::OPTIONS[:testing]
140
166
 
141
- recursive = @options[:recursive]
142
-
143
- if options[:render]
144
- if options[:locals]
145
- context = {}
146
- options[:locals].each_pair do |k,v|
147
- if v.respond_to?(:call)
148
- context[k] = v.call
149
- else
150
- context[k] = v
151
- end
167
+ if options[:render]
168
+ if options[:locals]
169
+ context = {}
170
+ options[:locals].each_pair do |k,v|
171
+ if v.respond_to?(:call)
172
+ context[k] = v.call
173
+ else
174
+ context[k] = v
152
175
  end
153
- else
154
- context = binding()
155
176
  end
156
-
157
- tempfile = render_template_file(@source, context, @package.name)
158
- sourcepath = tempfile.path
159
- logger.info "Rendering template #{@source} to temporary file #{sourcepath}"
160
- recursive = false
161
177
  else
162
- sourcepath = @source
178
+ # context = binding()
179
+ context = @binding
163
180
  end
164
181
 
165
- logger.info "--> Transferring #{sourcepath} to #{@destination} for roles: #{roles}"
166
- @delivery.transfer(@package.name, sourcepath, @destination, roles, recursive)
167
-
168
- post = post_commands(:install)
169
- unless post.empty?
170
- sequence = post; sequence = sequence.join('; ') if sequence.is_a? Array
171
- logger.info "#{@package.name} post-transfer commands: #{sequence} for roles: #{roles}\n"
172
- @delivery.process @package.name, [post].flatten, roles
182
+ tempfile = render_template_file(@source, context, @package.name)
183
+ @sourcepath = tempfile.path
184
+ if source_is_template?
185
+ logger.debug "Rendering inline template to temporary file #{sourcepath}"
186
+ else
187
+ logger.debug "Rendering template #{@source} to temporary file #{sourcepath}"
173
188
  end
189
+ recursive = false
190
+ else
191
+ @sourcepath = @source
174
192
  end
193
+
194
+ logger.debug " --> Transferring #{sourcepath} to #{@orig_destination} for roles: #{roles}"
195
+ @delivery.install(self, roles, :recursive => @options[:recursive])
175
196
  end
176
197
  end
177
198
  end
@@ -1,13 +1,43 @@
1
1
  module Sprinkle
2
2
  module Installers
3
+ # The user installer helps add users. You may pass flags as an option.
4
+ #
5
+ # == Example Usage
6
+ #
7
+ # package :users do
8
+ # add_user 'admin', :flags => "--disabled-password"
9
+ #
10
+ # verify do
11
+ # has_user 'admin', :in_group = "root"
12
+ # end
13
+ # end
14
+
3
15
  class User < Installer
4
- def initialize(package, username, options, &block)
5
- super package, &block
16
+
17
+ api do
18
+ def add_user(username, options={}, &block)
19
+ install Sprinkle::Installers::User.new(self, username, options, &block)
20
+ end
21
+ end
22
+
23
+ verify_api do
24
+ def has_user(user, opts = {})
25
+ if opts[:in_group]
26
+ @commands << "id -nG #{user} | xargs -n1 echo | grep #{opts[:in_group]}"
27
+ else
28
+ @commands << "id #{user}"
29
+ end
30
+ end
31
+ end
32
+
33
+ def initialize(package, username, options = {}, &block) #:nodoc:
34
+ super package, options, &block
6
35
  @username = username
7
- @options = options
8
36
  end
37
+
9
38
  protected
10
- def install_commands
39
+
40
+ def install_commands #:nodoc:
11
41
  "adduser #{@options[:flags]} #{@username}"
12
42
  end
13
43
  end
@@ -1,7 +1,5 @@
1
1
  module Sprinkle
2
2
  module Installers
3
- # = Yum Package Installer
4
- #
5
3
  # The Yum package installer installs RPM packages.
6
4
  #
7
5
  # == Example Usage
@@ -10,20 +8,22 @@ module Sprinkle
10
8
  #
11
9
  # package :magic_beans do
12
10
  # yum 'magic_beans'
11
+ # verify { has_yum 'magic_beans' }
13
12
  # end
14
13
  #
15
- # You may also specify multiple rpms as an array:
14
+ # You may also specify multiple rpms as arguments or an array:
16
15
  #
17
16
  # package :magic_beans do
18
- # yum %w(magic_beans magic_sauce)
17
+ # yum "magic_beans", "magic_sauce"
19
18
  # end
20
- class Yum < Installer
21
- attr_accessor :packages #:nodoc:
19
+ class Yum < PackageInstaller
20
+
21
+ auto_api
22
22
 
23
- def initialize(parent, packages, &block) #:nodoc:
24
- super parent, &block
25
- packages = [packages] unless packages.is_a? Array
26
- @packages = packages
23
+ verify_api do
24
+ def has_yum(package)
25
+ @commands << "yum list installed #{package} | grep ^#{package}"
26
+ end
27
27
  end
28
28
 
29
29
  protected