sprinkle 0.5.1.1 → 0.5.2

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.
@@ -0,0 +1,15 @@
1
+ * Sprinkle `sudo_cmd` and Capistrino should work together instead of getting in each others way
2
+
3
+ When using the capistrano actor `sudo_cmd` will now use the capistrano
4
+ generated sudo command and therefore automatically deal with password
5
+ prompts, etc. This should fix hangs when installers try to call `sudo` on
6
+ the other side of a pipe operation and capistrano can not recognize the
7
+ password prompt.
8
+
9
+ * Sprinkle executable should return an error code if there was a failure
10
+
11
+ *Michael Nigh*
12
+
13
+ * verify of local actor was never returning false so installers would never be executed
14
+
15
+ *Edgars Beigarts*
@@ -15,6 +15,8 @@ module Sprinkle
15
15
  # * install (installer, roles, options)
16
16
  # * verify (verifier, roles, options)
17
17
  # * servers_for_role? (roles)
18
+ # * sudo?
19
+ # * sudo_command
18
20
  #
19
21
  # Hopefully these methods are kind of fairly obvious. They should return true
20
22
  # to indicate success and false to indicate failure.
@@ -35,6 +37,19 @@ module Sprinkle
35
37
  raise "you must define install"
36
38
  end
37
39
 
40
+ # an actor must define this and let the installers know if it plans
41
+ # to try and add sudo to all of their commands or not since some
42
+ # installers might need to handle sudo their own special way
43
+ def sudo?
44
+ raise "you must define sudo?"
45
+ end
46
+
47
+ # if an installer needs to call sudo this is the command the actor
48
+ # would prefer the installers to use
49
+ def sudo_command
50
+ raise "you must define sudo_command"
51
+ end
52
+
38
53
  def verify(*args)
39
54
  raise "you must define verify"
40
55
  end
@@ -38,6 +38,14 @@ module Sprinkle
38
38
  end
39
39
  end
40
40
 
41
+ def sudo?
42
+ @config.fetch(:run_method, :sudo) == :sudo
43
+ end
44
+
45
+ def sudo_command
46
+ @config.sudo
47
+ end
48
+
41
49
  # Determines if there are any servers for the given roles
42
50
  def servers_for_role?(roles)
43
51
  roles=Array(roles)
@@ -79,13 +87,15 @@ module Sprinkle
79
87
  end
80
88
 
81
89
  def verify(verifier, roles, opts = {}) #:nodoc:
82
- process(verifier.package.name, verifier.commands, roles,
83
- :suppress_and_return_failures => true)
90
+ process(verifier.package.name, verifier.commands, roles)
91
+ rescue ::Capistrano::CommandError
92
+ return false
84
93
  end
85
94
 
86
95
  def process(name, commands, roles, opts = {}) #:nodoc:
87
96
  inst=@installer
88
97
  @log_recorder = log_recorder = Sprinkle::Utility::LogRecorder.new
98
+ commands = commands.map {|x| rewrite_command(x)}
89
99
  define_task(name, roles) do
90
100
  via = fetch(:run_method, :sudo)
91
101
  commands.each do |command|
@@ -110,6 +120,17 @@ module Sprinkle
110
120
 
111
121
  private
112
122
 
123
+ # rip out any double sudos from the beginning of the command
124
+ def rewrite_command(cmd)
125
+ return cmd if cmd.is_a?(Symbol)
126
+ via = @config.fetch(:run_method, :sudo)
127
+ if via == :sudo and cmd =~ /^#{sudo_command}/
128
+ cmd.gsub(/^#{sudo_command}\s?/,"")
129
+ else
130
+ cmd
131
+ end
132
+ end
133
+
113
134
  def raise_error(e)
114
135
  details={:command => @log_recorder.command, :code => "??",
115
136
  :message => e.message,
@@ -120,11 +141,7 @@ module Sprinkle
120
141
 
121
142
  def run_task(task, opts={})
122
143
  run(task)
123
- return true
124
- rescue ::Capistrano::CommandError => e
125
- return false if opts[:suppress_and_return_failures]
126
- # Reraise error if we're not suppressing it
127
- raise
144
+ true
128
145
  end
129
146
 
130
147
  # REVISIT: can we set the description somehow?
@@ -24,6 +24,9 @@ module Sprinkle
24
24
  true
25
25
  end
26
26
 
27
+ def sudo?; false; end
28
+ def sudo_command; nil; end
29
+
27
30
  def install(installer, roles, opts = {}) #:nodoc:
28
31
  # all local installer cares about is the commands
29
32
  @installer = installer
@@ -35,7 +38,10 @@ module Sprinkle
35
38
  end
36
39
 
37
40
  def verify(verifier, roles, opts = {}) #:nodoc:
38
- process(verifier.package.name, verifier.commands, roles, :suppress_and_return_failures => true)
41
+ process(verifier.package.name, verifier.commands, roles)
42
+ true
43
+ rescue LocalCommandError
44
+ false
39
45
  end
40
46
 
41
47
  protected
@@ -44,15 +50,14 @@ module Sprinkle
44
50
  @log_recorder = Sprinkle::Utility::LogRecorder.new
45
51
  commands.each do |command|
46
52
  if command == :RECONNECT
47
- return true
53
+ res = 0
48
54
  elsif command == :TRANSFER
49
55
  res = transfer(@installer.sourcepath, @installer.destination, roles,
50
56
  :recursive => @installer.options[:recursive])
51
- raise LocalCommandError if res != 0
52
57
  else
53
58
  res = run_command command
54
- raise LocalCommandError if res != 0 and not opts[:suppress_and_return_failures]
55
59
  end
60
+ raise LocalCommandError if res != 0
56
61
  end
57
62
  return true
58
63
  end
@@ -88,9 +88,17 @@ module Sprinkle
88
88
  end
89
89
 
90
90
  # Set this to true to prepend 'sudo' to every command.
91
- def use_sudo(value)
91
+ def use_sudo(value=true)
92
92
  @options[:use_sudo] = value
93
93
  end
94
+
95
+ def sudo?
96
+ @options[:use_sudo]
97
+ end
98
+
99
+ def sudo_command
100
+ "sudo"
101
+ end
94
102
 
95
103
  def setup_gateway #:nodoc:
96
104
  @gateway ||= Net::SSH::Gateway.new(@options[:gateway], @options[:user]) if @options[:gateway]
@@ -104,8 +112,9 @@ module Sprinkle
104
112
  @verifier = verifier
105
113
  # issue all the verification steps in a single SSH command
106
114
  commands=[verifier.commands.join(" && ")]
107
- process(verifier.package.name, commands, roles,
108
- :suppress_and_return_failures => true)
115
+ process(verifier.package.name, commands, roles)
116
+ rescue SSHCommandFailure => e
117
+ false
109
118
  ensure
110
119
  @verifier = nil
111
120
  end
@@ -126,9 +135,7 @@ module Sprinkle
126
135
  end
127
136
 
128
137
  def process(name, commands, roles, opts = {}) #:nodoc:
129
- opts.reverse_merge!(:suppress_and_return_failures => false)
130
138
  setup_gateway
131
- @suppress = opts[:suppress_and_return_failures]
132
139
  r=execute_on_role(commands, roles)
133
140
  logger.debug green "process returning #{r}"
134
141
  return r
@@ -143,10 +150,10 @@ module Sprinkle
143
150
  end
144
151
 
145
152
  def prepare_commands(commands)
146
- return commands unless @options[:use_sudo]
153
+ return commands unless sudo?
147
154
  commands.map do |command|
148
155
  next command if command.is_a?(Symbol)
149
- command.match(/^sudo/) ? command : "sudo #{command}"
156
+ command.match(/^#{sudo_command}/) ? command : "#{sudo_command} #{command}"
150
157
  end
151
158
  end
152
159
 
@@ -166,13 +173,9 @@ module Sprinkle
166
173
  @log_recorder.reset cmd
167
174
  res = ssh(session, cmd)
168
175
  if res != 0
169
- if @suppress
170
- return false
171
- else
172
- fail=SSHCommandFailure.new
173
- fail.details = @log_recorder.hash.merge(:hosts => host)
174
- raise fail, "#{cmd} failed with error code #{res[:code]}"
175
- end
176
+ fail=SSHCommandFailure.new
177
+ fail.details = @log_recorder.hash.merge(:hosts => host)
178
+ raise fail, "#{cmd} failed with error code #{res[:code]}"
176
179
  end
177
180
  end
178
181
  true
@@ -27,6 +27,15 @@ module Sprinkle
27
27
  raise "The vlad actor needs a maintainer. "+
28
28
  "Please file an issue on github.com/sprinkle-tool/sprinkle if you can help."
29
29
  end
30
+
31
+ def sudo?
32
+ # TODO
33
+ raise
34
+ end
35
+
36
+ def sudo_command
37
+ "sudo"
38
+ end
30
39
 
31
40
  # Defines a script file which will be included by vlad. Use these
32
41
  # script files to set vlad specific configurations. Multiple scripts
@@ -66,7 +75,7 @@ module Sprinkle
66
75
  protected
67
76
 
68
77
  def process(name, commands, roles, opts ={}) #:nodoc:
69
- commands = commands.map{|x| "sudo #{x}"} if use_sudo
78
+ commands = commands.map{|x| "#{sudo_command} #{x}"} if sudo?
70
79
  commands = commands.join(' && ')
71
80
  puts "executing #{commands}"
72
81
  task = remote_task(task_sym(name), :roles => roles) { run commands }
@@ -75,7 +84,7 @@ module Sprinkle
75
84
 
76
85
  def process_with_transfer(name, commands, roles, opts ={}) #:nodoc:
77
86
  raise "cant do non recursive file transfers, sorry" if opts[:recursive] == false
78
- commands = commands.map{|x| x == :TRANSFER : x ? "sudo #{x}" } if use_sudo
87
+ commands = commands.map{|x| x == :TRANSFER ? x : "sudo #{x}" } if sudo?
79
88
  i = commands.index(:TRANSFER)
80
89
  before = commands.first(i).join(" && ")
81
90
  after = commands.last(commands.size-i+1).join(" && ")
@@ -80,10 +80,10 @@ module Sprinkle
80
80
  end
81
81
  rescue Sprinkle::Errors::RemoteCommandFailure => e
82
82
  e.print_summary
83
- exit
83
+ exit 1
84
84
  rescue Sprinkle::Errors::TransferFailure => e
85
85
  e.print_summary
86
- exit
86
+ exit 2
87
87
  ensure
88
88
  # do any cleanup our actor may need to close network sockets, etc
89
89
  @style.teardown if @style.respond_to?(:teardown)
@@ -80,11 +80,11 @@ module Sprinkle
80
80
  end
81
81
 
82
82
  def sudo_cmd
83
- "sudo " if sudo?
83
+ return "#{@delivery.try(:sudo_command) || "sudo"} " if sudo?
84
84
  end
85
85
 
86
86
  def sudo?
87
- options[:sudo] or package.sudo?
87
+ options[:sudo] or package.sudo? or @delivery.try(:sudo?)
88
88
  end
89
89
 
90
90
  def escape_shell_arg(str)
@@ -34,7 +34,7 @@ module Sprinkle
34
34
  protected
35
35
 
36
36
  def install_commands #:nodoc:
37
- sudo_cmd ?
37
+ sudo? ?
38
38
  @cmds.map { |cmd| "#{sudo_cmd}#{cmd}"} :
39
39
  @cmds
40
40
  end
@@ -16,6 +16,12 @@ module Sprinkle
16
16
  # package :magic_beans do
17
17
  # yum "magic_beans", "magic_sauce"
18
18
  # end
19
+ #
20
+ # To install a specific version just add that version after the name
21
+ #
22
+ # package :magic_beans do
23
+ # yum "magic_beans-3.0"
24
+ # end
19
25
  class Yum < PackageInstaller
20
26
 
21
27
  auto_api
@@ -222,78 +222,66 @@ module Sprinkle
222
222
  v.process(roles)
223
223
  end
224
224
  end
225
-
226
- def dependencies
227
- @dependencies.map {|a,b| a }
228
- end
229
-
225
+
230
226
  def requires(*packages)
231
- opts = packages.extract_options!
232
- packages.each do |pack|
233
- @dependencies << [pack, opts]
234
- end
227
+ add_dependencies packages, :dependencies
235
228
  end
236
229
 
237
230
  def recommends(*packages)
238
- opts = packages.extract_options!
239
- packages.each do |pack|
240
- @recommends << [pack, opts]
241
- end
242
- @recommends.map {|a,b| a }
231
+ add_dependencies packages, :recommends
243
232
  end
244
233
 
245
234
  def optional(*packages)
246
- opts = packages.extract_options!
247
- packages.each do |pack|
248
- @optional << [pack, opts]
249
- end
250
- @optional.map {|a,b| a }
235
+ add_dependencies packages, :optional
251
236
  end
252
-
237
+
238
+ def dependencies
239
+ @dependencies.map {|a,b| a }
240
+ end
241
+
253
242
  def tree(depth = 1, &block)
254
243
  packages = []
255
-
256
- @recommends.each do |dep, config|
257
- package = PACKAGES[dep]
258
- next unless package # skip missing recommended packages as they're allowed to not exist
259
- package=package.instance(config)
260
- block.call(self, package, depth) if block
261
- packages << package.tree(depth + 1, &block)
262
- end
263
-
264
- @dependencies.each do |dep, config|
265
- package = PACKAGES[dep]
266
- package = select_package(dep, package) if package.is_a? Array
267
-
268
- raise "Package definition not found for key: #{dep}" unless package
269
- package = package.instance(config)
270
- block.call(self, package, depth) if block
271
- packages << package.tree(depth + 1, &block)
272
- end
273
-
244
+ packages << tree_for_packages(@recommends, :depth => depth, &block)
245
+ packages << tree_for_packages(@dependencies, :depth => depth, :required => true, &block)
274
246
  packages << self
275
-
276
- @optional.each do |dep, config|
277
- package = PACKAGES[dep]
278
- next unless package # skip missing optional packages as they're allow to not exist
279
- package = package.instance(config)
280
- block.call(self, package, depth) if block
281
- packages << package.tree(depth + 1, &block)
282
- end
283
-
247
+ packages << tree_for_packages(@optional, :depth => depth, &block)
284
248
  packages
285
249
  end
286
250
 
287
251
  def to_s; @name; end
288
252
 
289
- protected
253
+ protected
290
254
 
291
255
  def install(i)
292
256
  @installers << i
293
257
  i
294
258
  end
295
259
 
296
- private
260
+ private
261
+
262
+ def add_dependencies(packages, kind)
263
+ opts = packages.extract_options!
264
+ depends = instance_variable_get("@#{kind}")
265
+ packages.each do |pack|
266
+ depends << [pack, opts]
267
+ end
268
+ depends.map {|a,b| a }
269
+ end
270
+
271
+ def tree_for_packages(packages, opts={}, &block)
272
+ depth = opts[:depth]
273
+ tree = []
274
+ packages.each do |dep, config|
275
+ package = PACKAGES[dep]
276
+ raise "Package definition not found for key: #{dep}" if not package and opts[:required]
277
+ next unless package # skip missing recommended packages as they're allowed to not exist
278
+ package = select_package(dep, package) if package.is_a? Array
279
+ package = package.instance(config)
280
+ block.call(self, package, depth) if block
281
+ tree << package.tree(depth + 1, &block)
282
+ end
283
+ tree
284
+ end
297
285
 
298
286
  def cloud_info(message)
299
287
  logger.info(message) if Sprinkle::OPTIONS[:cloud] or logger.debug?
@@ -1,3 +1,3 @@
1
1
  module Sprinkle
2
- Version = "0.5.1.1"
2
+ Version = "0.5.2"
3
3
  end
@@ -89,6 +89,30 @@ describe Sprinkle::Actors::Capistrano do
89
89
  end
90
90
 
91
91
  end
92
+
93
+ describe 'verifications' do
94
+
95
+ before do
96
+ @commands = %w( op1 op2 )
97
+ @roles = %w( app )
98
+ @package = stub(:name => "name")
99
+ @cap = create_cap
100
+ @verifier = stub(:package => @package, :commands => ["op1", "op2"])
101
+ end
102
+
103
+ it "should return true if successful" do
104
+ @cap.stub!(:run).and_return
105
+ res = @cap.verify(@verifier, @roles)
106
+ res.should == true
107
+ end
108
+
109
+ it "should return false if there was an error" do
110
+ @cap.stub!(:run).and_raise(::Capistrano::CommandError)
111
+ res = @cap.verify(@verifier, @roles)
112
+ res.should == false
113
+ end
114
+
115
+ end
92
116
 
93
117
  describe 'processing commands' do
94
118
 
@@ -102,6 +126,14 @@ describe Sprinkle::Actors::Capistrano do
102
126
 
103
127
  @testing_errors = false
104
128
  end
129
+
130
+ it "should strip excessive occurrences of sudo" do
131
+ # pretend the package or installer has also added sudo
132
+ @commands =["sudo op1"]
133
+ @cap.stub(:sudo_command).and_return("sudo")
134
+ @cap.unstub!(:run)
135
+ @cap.config.should_receive(:invoke_command).with('op1', :via => :sudo).ordered.and_return
136
+ end
105
137
 
106
138
  it 'should dynamically create a capistrano task containing the commands' do
107
139
  @cap.config.should_receive(:task).and_return
@@ -118,17 +150,6 @@ describe Sprinkle::Actors::Capistrano do
118
150
  lambda { @cap.process @name, @commands, @roles }.should raise_error(::Capistrano::CommandError)
119
151
  end
120
152
 
121
- it 'should not raise errors and instead return false when suppressing parameter is set' do
122
- @testing_errors = true
123
-
124
- @cap.should_receive(:run).and_raise(::Capistrano::CommandError)
125
-
126
- value = nil
127
- lambda { value = @cap.process(@name, @commands, @roles, true) }.should_not raise_error(::Capistrano::CommandError)
128
-
129
- value.should_not be
130
- end
131
-
132
153
  after do
133
154
  @cap.process @name, @commands, @roles unless @testing_errors
134
155
  end
@@ -141,6 +162,7 @@ describe Sprinkle::Actors::Capistrano do
141
162
  @source = 'source'
142
163
  @dest = 'dest'
143
164
  @roles = %w( app )
165
+ @commands = [:TRANSFER]
144
166
  @name = 'name'
145
167
 
146
168
  @cap = create_cap do; recipes 'deploy'; end
@@ -167,17 +189,6 @@ describe Sprinkle::Actors::Capistrano do
167
189
  lambda { @cap.process @name, @commands, @roles }.should raise_error(::Capistrano::CommandError)
168
190
  end
169
191
 
170
- it 'should not raise errors and instead return false when suppressing parameter is set' do
171
- @testing_errors = true
172
-
173
- @cap.should_receive(:run).and_raise(::Capistrano::CommandError)
174
-
175
- value = nil
176
- lambda { value = @cap.process(@name, @commands, @roles, true) }.should_not raise_error(::Capistrano::CommandError)
177
-
178
- value.should_not be
179
- end
180
-
181
192
  after do
182
193
  @cap.process @package.name, @installer.install_sequence, @roles
183
194
  end
@@ -191,12 +202,12 @@ describe Sprinkle::Actors::Capistrano do
191
202
  @name = 'name'
192
203
 
193
204
  @cap = create_cap do; recipes 'deploy'; end
194
- @cap.config.stub!(:fetch).and_return(:sudo)
195
205
  @cap.config.stub!(:invoke_command).and_return
196
206
  end
197
207
 
198
208
  it 'should use sudo to invoke commands when so configured' do
199
- @cap.config.should_receive(:fetch).with(:run_method, :sudo).and_return(:sudo)
209
+ @cap.config.should_receive(:invoke_command).with('op1', :via => :sudo).ordered.and_return
210
+ @cap.config.should_receive(:invoke_command).with('op2', :via => :sudo).ordered.and_return
200
211
  end
201
212
 
202
213
  it 'should run the supplied commands' do
@@ -33,13 +33,18 @@ describe Sprinkle::Actors::Local do
33
33
  @verifier.commands += ["test","test"]
34
34
  @roles = %w( app )
35
35
  @name = 'name'
36
-
37
- @local.stub!(:run_command).and_return(0)
36
+ end
37
+
38
+ it 'should return false when verification fails' do
39
+ @local.stub!(:run_command).and_return(1)
40
+ res = @local.verify @verifier, @roles
41
+ res.should == false
38
42
  end
39
43
 
40
44
  it 'should run the commands on the local system' do
41
- @local.should_receive(:run_command).twice.and_return
42
- @local.verify @verifier, @roles
45
+ @local.stub!(:run_command).and_return(0)
46
+ res = @local.verify @verifier, @roles
47
+ res.should == true
43
48
  end
44
49
 
45
50
  end
@@ -73,7 +73,8 @@ describe Sprinkle::Installers::Apt do
73
73
  end
74
74
 
75
75
  it 'should install a specific version if defined' do
76
- pending
76
+ @installer = create_apt 'ruby=2'
77
+ @installer.send(:install_sequence).should == [ %(env DEBCONF_TERSE='yes' DEBIAN_PRIORITY='critical' DEBIAN_FRONTEND=noninteractive apt-get --force-yes -qyu install ruby=2)]
77
78
  end
78
79
 
79
80
  end
@@ -47,8 +47,6 @@ describe Sprinkle::Installers::Brew do
47
47
  @installer.send(:install_sequence).should == [ 'op1', %(brew install ruby), 'op2' ]
48
48
  end
49
49
 
50
- it 'should install a specific version if defined'
51
-
52
50
  end
53
51
 
54
52
  # describe 'during dependencies only installation' do
@@ -7,7 +7,8 @@ describe Sprinkle::Installers::Installer do
7
7
  @package = mock(Sprinkle::Package, :name => 'package')
8
8
  @empty = Proc.new { }
9
9
  @sequence = ['op1', 'op2']
10
- @delivery = mock(Sprinkle::Deployment, :process => true, :install => true)
10
+ @delivery = mock(Sprinkle::Deployment, :process => true, :install => true,
11
+ :sudo_command => "sudo")
11
12
  @installer = create_installer
12
13
  @installer.delivery = @delivery
13
14
  @roles = []
@@ -102,6 +103,12 @@ describe Sprinkle::Installers::Installer do
102
103
  @installer.delivery = @delivery
103
104
  end
104
105
 
106
+ it "should use sudo command from actor" do
107
+ @installer.delivery = mock(Sprinkle::Deployment, :process => true, :install => true,
108
+ :sudo_command => "sudo -p blah")
109
+ @installer.sudo_cmd.should =~ /sudo -p blah /
110
+ end
111
+
105
112
  it "should know it uses sudo" do
106
113
  @installer.sudo?.should == true
107
114
  end
@@ -47,9 +47,6 @@ describe Sprinkle::Installers::Rpm do
47
47
  @installer.send(:install_sequence).should == [ 'op1', 'rpm -Uvh ruby', 'op2' ]
48
48
  end
49
49
 
50
- it 'should specify a non interactive mode to the apt installer' do
51
- pending
52
- end
53
50
  it 'should install a specific version if defined' do
54
51
  pending
55
52
  end
@@ -43,7 +43,8 @@ describe Sprinkle::Installers::Yum do
43
43
  end
44
44
 
45
45
  it 'should install a specific version if defined' do
46
- pending
46
+ @installer = create_rpm 'ruby-2.0'
47
+ @installer.send(:install_sequence).should == [ 'yum install ruby-2.0 -y' ]
47
48
  end
48
49
 
49
50
  end
@@ -1,4 +1,5 @@
1
1
  require "./lib/sprinkle/version"
2
+ require 'date'
2
3
 
3
4
  Gem::Specification.new do |s|
4
5
  s.name = "sprinkle"
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sprinkle
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.1.1
4
+ version: 0.5.2
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2013-04-27 00:00:00.000000000 Z
13
+ date: 2013-05-15 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: rspec
@@ -134,6 +134,7 @@ extra_rdoc_files:
134
134
  files:
135
135
  - .gitignore
136
136
  - .travis.yml
137
+ - CHANGELOG.md
137
138
  - CREDITS
138
139
  - Gemfile
139
140
  - Gemfile.lock