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.
- data/.gitignore +8 -0
- data/Gemfile +5 -0
- data/Gemfile.lock +54 -0
- data/README.markdown +178 -166
- data/Rakefile +4 -28
- data/bin/sprinkle +14 -1
- data/lib/sprinkle.rb +5 -1
- data/lib/sprinkle/actors/actors.rb +20 -5
- data/lib/sprinkle/actors/capistrano.rb +62 -36
- data/lib/sprinkle/actors/dummy.rb +127 -0
- data/lib/sprinkle/actors/local.rb +59 -17
- data/lib/sprinkle/actors/ssh.rb +189 -107
- data/lib/sprinkle/actors/vlad.rb +51 -32
- data/lib/sprinkle/configurable.rb +2 -1
- data/lib/sprinkle/deployment.rb +22 -2
- data/lib/sprinkle/errors/pretty_failure.rb +41 -0
- data/lib/sprinkle/errors/remote_command_failure.rb +24 -0
- data/lib/sprinkle/errors/transfer_failure.rb +28 -0
- data/lib/sprinkle/installers/apt.rb +17 -16
- data/lib/sprinkle/installers/binary.rb +23 -8
- data/lib/sprinkle/installers/brew.rb +17 -10
- data/lib/sprinkle/installers/bsd_port.rb +10 -6
- data/lib/sprinkle/installers/deb.rb +3 -10
- data/lib/sprinkle/installers/freebsd_pkg.rb +5 -11
- data/lib/sprinkle/installers/freebsd_portinstall.rb +8 -2
- data/lib/sprinkle/installers/gem.rb +9 -3
- data/lib/sprinkle/installers/group.rb +28 -4
- data/lib/sprinkle/installers/installer.rb +58 -7
- data/lib/sprinkle/installers/mac_port.rb +13 -6
- data/lib/sprinkle/installers/npm.rb +42 -0
- data/lib/sprinkle/installers/openbsd_pkg.rb +4 -11
- data/lib/sprinkle/installers/opensolaris_pkg.rb +7 -13
- data/lib/sprinkle/installers/package_installer.rb +33 -0
- data/lib/sprinkle/installers/pacman.rb +5 -13
- data/lib/sprinkle/installers/pear.rb +40 -0
- data/lib/sprinkle/installers/push_text.rb +18 -5
- data/lib/sprinkle/installers/rake.rb +7 -2
- data/lib/sprinkle/installers/reconnect.rb +29 -0
- data/lib/sprinkle/installers/replace_text.rb +11 -2
- data/lib/sprinkle/installers/rpm.rb +8 -6
- data/lib/sprinkle/installers/runner.rb +41 -16
- data/lib/sprinkle/installers/smart.rb +6 -17
- data/lib/sprinkle/installers/source.rb +22 -10
- data/lib/sprinkle/installers/thor.rb +7 -0
- data/lib/sprinkle/installers/transfer.rb +62 -41
- data/lib/sprinkle/installers/user.rb +34 -4
- data/lib/sprinkle/installers/yum.rb +10 -10
- data/lib/sprinkle/installers/zypper.rb +4 -15
- data/lib/sprinkle/package.rb +81 -98
- data/lib/sprinkle/policy.rb +11 -4
- data/lib/sprinkle/utility/log_recorder.rb +33 -0
- data/lib/sprinkle/verifiers/directory.rb +1 -1
- data/lib/sprinkle/verifiers/executable.rb +1 -1
- data/lib/sprinkle/verifiers/file.rb +11 -2
- data/lib/sprinkle/verifiers/package.rb +2 -14
- data/lib/sprinkle/verifiers/permission.rb +40 -0
- data/lib/sprinkle/verifiers/symlink.rb +2 -2
- data/lib/sprinkle/verifiers/test.rb +21 -0
- data/lib/sprinkle/verify.rb +3 -3
- data/lib/sprinkle/version.rb +3 -0
- data/spec/fixtures/my_file.txt +1 -0
- data/spec/sprinkle/actors/capistrano_spec.rb +16 -3
- data/spec/sprinkle/actors/local_spec.rb +24 -6
- data/spec/sprinkle/actors/ssh_spec.rb +38 -0
- data/spec/sprinkle/installers/apt_spec.rb +23 -2
- data/spec/sprinkle/installers/binary_spec.rb +22 -14
- data/spec/sprinkle/installers/brew_spec.rb +4 -4
- data/spec/sprinkle/installers/installer_spec.rb +36 -7
- data/spec/sprinkle/installers/npm_spec.rb +16 -0
- data/spec/sprinkle/installers/pear_spec.rb +16 -0
- data/spec/sprinkle/installers/push_text_spec.rb +23 -1
- data/spec/sprinkle/installers/rpm_spec.rb +5 -0
- data/spec/sprinkle/installers/runner_spec.rb +27 -11
- data/spec/sprinkle/installers/smart_spec.rb +60 -0
- data/spec/sprinkle/installers/source_spec.rb +4 -4
- data/spec/sprinkle/installers/transfer_spec.rb +31 -16
- data/spec/sprinkle/package_spec.rb +10 -2
- data/spec/sprinkle/policy_spec.rb +6 -0
- data/spec/sprinkle/verify_spec.rb +18 -4
- data/sprinkle.gemspec +22 -158
- metadata +178 -96
- data/TODO +0 -56
- data/VERSION +0 -1
- data/lib/sprinkle/verifiers/apt.rb +0 -21
- data/lib/sprinkle/verifiers/brew.rb +0 -21
- data/lib/sprinkle/verifiers/rpm.rb +0 -21
- data/lib/sprinkle/verifiers/users_groups.rb +0 -33
data/bin/sprinkle
CHANGED
@@ -20,7 +20,10 @@ ARGV.each do |arg|
|
|
20
20
|
ENV[$1] = $2 if arg =~ /^(\w+)=(.*)$/
|
21
21
|
end
|
22
22
|
|
23
|
+
require File.dirname(__FILE__) + '/../lib/sprinkle/version'
|
24
|
+
|
23
25
|
parser = OptionParser.new do |opts|
|
26
|
+
opts.version = Sprinkle::Version
|
24
27
|
opts.banner = <<BANNER
|
25
28
|
Sprinkle
|
26
29
|
========
|
@@ -44,6 +47,8 @@ BANNER
|
|
44
47
|
opts.separator ""
|
45
48
|
opts.on("-s", "--script=PATH", String,
|
46
49
|
"Path to a sprinkle script to run") { |v| OPTIONS[:path] = v }
|
50
|
+
opts.on("--only [ROLE]", String,
|
51
|
+
"Only run sprinkle policies for the specified role") { |v| OPTIONS[:only_role] = v }
|
47
52
|
opts.on("-t", "--test",
|
48
53
|
"Process but don't perform any actions") { |v| OPTIONS[:testing] = v }
|
49
54
|
opts.on("-v", "--verbose",
|
@@ -61,6 +66,12 @@ BANNER
|
|
61
66
|
end
|
62
67
|
end
|
63
68
|
|
69
|
+
def only_role(options)
|
70
|
+
role=OPTIONS[:only_role]
|
71
|
+
Sprinkle::OPTIONS[:only_role] = role
|
72
|
+
puts "Only running policies for :#{role}"
|
73
|
+
end
|
74
|
+
|
64
75
|
def force_mode(options)
|
65
76
|
Sprinkle::OPTIONS[:force] = OPTIONS[:force] || false
|
66
77
|
end
|
@@ -78,7 +89,8 @@ def verbosity(options)
|
|
78
89
|
end
|
79
90
|
|
80
91
|
def log_level(options)
|
81
|
-
|
92
|
+
severity = ActiveSupport::BufferedLogger::Severity
|
93
|
+
Object.logger.level = options[:verbose] ? severity::DEBUG : severity::INFO
|
82
94
|
end
|
83
95
|
|
84
96
|
require File.dirname(__FILE__) + '/../lib/sprinkle'
|
@@ -91,5 +103,6 @@ operation_mode(OPTIONS)
|
|
91
103
|
powder_cloud(OPTIONS)
|
92
104
|
log_level(OPTIONS)
|
93
105
|
verbosity(OPTIONS)
|
106
|
+
only_role(OPTIONS)
|
94
107
|
|
95
108
|
Sprinkle::Script.sprinkle File.read(powder), powder
|
data/lib/sprinkle.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
require 'rubygems'
|
2
|
-
require 'active_support'
|
2
|
+
require 'active_support/all'
|
3
3
|
|
4
4
|
# Use active supports auto load mechanism
|
5
5
|
require 'active_support/version'
|
@@ -14,10 +14,14 @@ end
|
|
14
14
|
#ActiveSupport::Dependencies::RAILS_DEFAULT_LOGGER = Logger.new($stdout)
|
15
15
|
#ActiveSupport::Dependencies.log_activity = true
|
16
16
|
|
17
|
+
require File.dirname(__FILE__) + "/sprinkle/version.rb"
|
18
|
+
|
17
19
|
# Load up extensions to existing classes
|
18
20
|
Dir[File.dirname(__FILE__) + '/sprinkle/extensions/*.rb'].each { |e| require e }
|
19
21
|
# Load up the verifiers so they can register themselves
|
20
22
|
Dir[File.dirname(__FILE__) + '/sprinkle/verifiers/*.rb'].each { |e| require e }
|
23
|
+
# Load up the installers so they can register themselves
|
24
|
+
Dir[File.dirname(__FILE__) + '/sprinkle/installers/*.rb'].each { |e| require e }
|
21
25
|
|
22
26
|
# Configuration options
|
23
27
|
module Sprinkle
|
@@ -6,12 +6,27 @@
|
|
6
6
|
#++
|
7
7
|
|
8
8
|
module Sprinkle
|
9
|
-
# An actor is a method of command delivery to a remote machine.
|
10
|
-
# layer between
|
11
|
-
#
|
9
|
+
# An actor is a method of command delivery to a remote machine. Actors are the
|
10
|
+
# layer setting between Sprinkle and the systems you and wanting to apply
|
11
|
+
# policies to.
|
12
12
|
#
|
13
|
-
#
|
14
|
-
#
|
13
|
+
# Sprinkle ships with actors for Capistrano, Vlad, localhost and pure SSH.
|
14
|
+
# 99% of the time these should be sufficient but you can always write your
|
15
|
+
# own actor otherwise.
|
16
|
+
#
|
17
|
+
# == Writing an actor
|
18
|
+
#
|
19
|
+
# Actors must provide only 3 methods:
|
20
|
+
#
|
21
|
+
# * install (installer, roles, options)
|
22
|
+
# * verify (verifier, roles, options)
|
23
|
+
# * transfer (source, destination, roles, options)
|
24
|
+
#
|
25
|
+
# Hopefully these methods are kind of fairly obvious. They should return true
|
26
|
+
# to indicate success and false to indicate failure.
|
27
|
+
# The actual commands you need to execute can be retrived from
|
28
|
+
# +installer.install_sequence+ and +verifier.commands+.
|
29
|
+
|
15
30
|
module Actors
|
16
31
|
end
|
17
32
|
end
|
@@ -2,16 +2,15 @@ require 'capistrano/cli'
|
|
2
2
|
|
3
3
|
module Sprinkle
|
4
4
|
module Actors
|
5
|
-
#
|
5
|
+
# The Capistrano actor uses Capistrano to define your roles and deliver
|
6
|
+
# commands to your remote servers. You'll need the capistrano gem installed.
|
6
7
|
#
|
7
|
-
#
|
8
|
-
# box with Sprinkle. If you have the capistrano gem install, you may use
|
9
|
-
# this delivery. The only configuration option available, and which is
|
10
|
-
# mandatory to include is +recipes+. An example:
|
8
|
+
# The only configuration option is to specify a recipe.
|
11
9
|
#
|
12
10
|
# deployment do
|
13
11
|
# delivery :capistrano do
|
14
|
-
#
|
12
|
+
# recipe 'deploy'
|
13
|
+
# recipe 'more'
|
15
14
|
# end
|
16
15
|
# end
|
17
16
|
#
|
@@ -47,52 +46,79 @@ module Sprinkle
|
|
47
46
|
#
|
48
47
|
# deployment do
|
49
48
|
# delivery :capistrano do
|
50
|
-
#
|
51
|
-
# recipes 'magic_beans'
|
49
|
+
# recipe 'deploy'
|
50
|
+
# recipes 'magic_beans', 'normal_beans'
|
52
51
|
# end
|
53
52
|
# end
|
54
|
-
def
|
53
|
+
def recipe(scripts)
|
55
54
|
@loaded_recipes ||= []
|
56
|
-
|
57
|
-
|
55
|
+
Array(scripts).each do |script|
|
56
|
+
@config.load script
|
57
|
+
@loaded_recipes << script
|
58
|
+
end
|
58
59
|
end
|
59
|
-
|
60
|
-
def
|
60
|
+
|
61
|
+
def recipes(scripts) #:nodoc:
|
62
|
+
recipe(scripts)
|
63
|
+
end
|
64
|
+
|
65
|
+
def install(installer, roles, opts = {}) #:nodoc:
|
66
|
+
@installer = installer
|
67
|
+
process(installer.package.name, installer.install_sequence, roles, opts)
|
68
|
+
rescue ::Capistrano::CommandError => e
|
69
|
+
raise_error(e)
|
70
|
+
ensure
|
71
|
+
@installer = nil
|
72
|
+
end
|
73
|
+
|
74
|
+
def verify(verifier, roles, opts = {}) #:nodoc:
|
75
|
+
process(verifier.package.name, verifier.commands, roles,
|
76
|
+
:suppress_and_return_failures => true)
|
77
|
+
end
|
78
|
+
|
79
|
+
def process(name, commands, roles, opts = {}) #:nodoc:
|
80
|
+
inst=@installer
|
81
|
+
@log_recorder = log_recorder = Sprinkle::Utility::LogRecorder.new
|
61
82
|
define_task(name, roles) do
|
62
83
|
via = fetch(:run_method, :sudo)
|
63
84
|
commands.each do |command|
|
64
|
-
|
85
|
+
if command == :TRANSFER
|
86
|
+
opts.reverse_merge!(:recursive => true)
|
87
|
+
upload inst.sourcepath, inst.destination, :via => :scp,
|
88
|
+
:recursive => opts[:recursive]
|
89
|
+
elsif command == :RECONNECT
|
90
|
+
teardown_connections_to(sessions.keys)
|
91
|
+
else
|
92
|
+
# this reset the log
|
93
|
+
log_recorder.reset command
|
94
|
+
invoke_command(command, {:via => via}) do |c,s,d|
|
95
|
+
# record the stream and data
|
96
|
+
log_recorder.log(s, d)
|
97
|
+
end
|
98
|
+
end
|
65
99
|
end
|
66
100
|
end
|
67
|
-
|
68
|
-
begin
|
69
|
-
run(name)
|
70
|
-
return true
|
71
|
-
rescue ::Capistrano::CommandError => e
|
72
|
-
return false if suppress_and_return_failures
|
73
|
-
|
74
|
-
# Reraise error if we're not suppressing it
|
75
|
-
raise
|
76
|
-
end
|
101
|
+
run_task(name, opts)
|
77
102
|
end
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
103
|
+
|
104
|
+
private
|
105
|
+
|
106
|
+
def raise_error(e)
|
107
|
+
details={:command => @log_recorder.command, :code => "??",
|
108
|
+
:message => e.message,
|
109
|
+
:hosts => e.hosts,
|
110
|
+
:error => @log_recorder.err, :stdout => @log_recorder.out}
|
111
|
+
raise Sprinkle::Errors::RemoteCommandFailure.new(@installer, details, e)
|
82
112
|
end
|
83
|
-
|
84
|
-
|
85
|
-
run(
|
113
|
+
|
114
|
+
def run_task(task, opts={})
|
115
|
+
run(task)
|
86
116
|
return true
|
87
117
|
rescue ::Capistrano::CommandError => e
|
88
|
-
return false if suppress_and_return_failures
|
89
|
-
|
118
|
+
return false if opts[:suppress_and_return_failures]
|
90
119
|
# Reraise error if we're not suppressing it
|
91
120
|
raise
|
92
121
|
end
|
93
|
-
end
|
94
|
-
|
95
|
-
private
|
96
122
|
|
97
123
|
# REVISIT: can we set the description somehow?
|
98
124
|
def define_task(name, roles, &block)
|
@@ -0,0 +1,127 @@
|
|
1
|
+
require 'capistrano/cli'
|
2
|
+
require 'pp'
|
3
|
+
|
4
|
+
module Sprinkle
|
5
|
+
module Actors
|
6
|
+
# = Capistrano Delivery Method
|
7
|
+
#
|
8
|
+
# Capistrano is one of the delivery method options available out of the
|
9
|
+
# box with Sprinkle. If you have the capistrano gem install, you may use
|
10
|
+
# this delivery. The only configuration option available, and which is
|
11
|
+
# mandatory to include is +recipes+. An example:
|
12
|
+
#
|
13
|
+
# deployment do
|
14
|
+
# delivery :capistrano do
|
15
|
+
# recipes 'deploy'
|
16
|
+
# end
|
17
|
+
# end
|
18
|
+
#
|
19
|
+
# Recipes is given a list of files which capistrano will include and load.
|
20
|
+
# These recipes are mainly to set variables such as :user, :password, and to
|
21
|
+
# set the app domain which will be sprinkled.
|
22
|
+
class Dummy #:nodoc:
|
23
|
+
attr_accessor :config, :loaded_recipes #:nodoc:
|
24
|
+
|
25
|
+
def initialize(&block) #:nodoc:
|
26
|
+
# @config.set(:_sprinkle_actor, self)
|
27
|
+
@roles={}
|
28
|
+
self.instance_eval &block
|
29
|
+
end
|
30
|
+
|
31
|
+
# Defines a recipe file which will be included by capistrano. Use these
|
32
|
+
# recipe files to set capistrano specific configurations. Default recipe
|
33
|
+
# included is "deploy." But if any other recipe is specified, it will
|
34
|
+
# include that instead. Multiple recipes may be specified through multiple
|
35
|
+
# recipes calls, an example:
|
36
|
+
#
|
37
|
+
# deployment do
|
38
|
+
# delivery :capistrano do
|
39
|
+
# recipes 'deploy'
|
40
|
+
# recipes 'magic_beans'
|
41
|
+
# end
|
42
|
+
# end
|
43
|
+
# def recipes(script)
|
44
|
+
# end
|
45
|
+
|
46
|
+
def role(role, server, opts={})
|
47
|
+
@roles[role]||=[]
|
48
|
+
@roles[role] << [ server, opts ]
|
49
|
+
end
|
50
|
+
|
51
|
+
def install(installer, roles, opts={})
|
52
|
+
if per_host=opts.delete(:per_host)
|
53
|
+
servers_per_role(roles).each do |server|
|
54
|
+
installer.reconfigure_for(server)
|
55
|
+
installer.announce
|
56
|
+
process(installer.package.name, installer.install_sequence, server, opts)
|
57
|
+
end
|
58
|
+
else
|
59
|
+
process(installer.package, installer.install_sequence, roles, opts)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def verify(verifier, roles, opts = {})
|
64
|
+
process(verifier.package.name, verifier.commands, roles, opts = {})
|
65
|
+
end
|
66
|
+
|
67
|
+
def servers_per_role(role)
|
68
|
+
@roles[role]
|
69
|
+
end
|
70
|
+
|
71
|
+
def process(name, commands, roles, opts = {}) #:nodoc:
|
72
|
+
# puts "PROCESS: #{name} on #{roles}"
|
73
|
+
pp commands
|
74
|
+
# return false if suppress_and_return_failures
|
75
|
+
true
|
76
|
+
end
|
77
|
+
|
78
|
+
def transfer(name, source, destination, roles, recursive = true, suppress_and_return_failures = false)
|
79
|
+
|
80
|
+
end
|
81
|
+
|
82
|
+
private
|
83
|
+
|
84
|
+
# REVISIT: can we set the description somehow?
|
85
|
+
def define_task(name, roles, &block)
|
86
|
+
@config.task task_sym(name), :roles => roles, &block
|
87
|
+
end
|
88
|
+
|
89
|
+
def run(task)
|
90
|
+
@config.send task_sym(task)
|
91
|
+
end
|
92
|
+
|
93
|
+
def task_sym(name)
|
94
|
+
"install_#{name.to_task_name}".to_sym
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
|
101
|
+
=begin
|
102
|
+
|
103
|
+
# channel: the SSH channel object used for this response
|
104
|
+
# stream: either :err or :out, for stderr or stdout responses
|
105
|
+
# output: the text that the server is sending, might be in chunks
|
106
|
+
run "apt-get update" do |channel, stream, output|
|
107
|
+
if output =~ /Are you sure?/
|
108
|
+
answer = Capistrano::CLI.ui.ask("Are you sure: ")
|
109
|
+
channel.send_data(answer + "\n")
|
110
|
+
else
|
111
|
+
# allow the default callback to be processed
|
112
|
+
Capistrano::Configuration.default_io_proc.call[channel, stream, output]
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
|
117
|
+
|
118
|
+
You can tell subversion to use a different username+password by
|
119
|
+
setting a couple variables:
|
120
|
+
set :svn_username, "my svn username"
|
121
|
+
set :svn_password, "my svn password"
|
122
|
+
If you don't want to set the password explicitly in your recipe like
|
123
|
+
that, you can make capistrano prompt you for it like this:
|
124
|
+
set(:svn_password) { Capistrano::CLI.password_prompt("Subversion
|
125
|
+
password: ") }
|
126
|
+
- Jamis
|
127
|
+
=end
|
@@ -1,37 +1,79 @@
|
|
1
|
+
require 'open4'
|
2
|
+
|
1
3
|
module Sprinkle
|
2
4
|
module Actors
|
3
|
-
#
|
5
|
+
# The local actor executes all commands on your local system, as opposed to other
|
6
|
+
# implementations that generally run commands on a remote system over the
|
7
|
+
# network.
|
4
8
|
#
|
5
|
-
# This
|
6
|
-
#
|
7
|
-
#
|
8
|
-
#
|
9
|
-
# This is useful if you'd like to use Sprinkle to provision your local machine.
|
10
|
-
# To enable this actor, in your Sprinkle script specify the :local delivery mechanism.
|
9
|
+
# This could be useful if you'd like to use Sprinkle to provision your
|
10
|
+
# local machine. To enable this actor, in your Sprinkle script specify
|
11
|
+
# the :local delivery mechanism.
|
11
12
|
#
|
12
13
|
# deployment do
|
13
14
|
# delivery :local
|
14
15
|
# end
|
15
16
|
#
|
16
|
-
# Note
|
17
|
-
#
|
17
|
+
# Note: The local actor completely ignores roles and behaves as if your
|
18
|
+
# local system was a member of all roles defined.
|
18
19
|
class Local
|
19
20
|
|
20
|
-
|
21
|
+
class LocalCommandError < StandardError; end
|
22
|
+
|
23
|
+
def install(installer, roles, opts = {}) #:nodoc:
|
24
|
+
# all local installer cares about is the commands
|
25
|
+
@installer = installer
|
26
|
+
process(installer.package.name, installer.install_sequence, roles)
|
27
|
+
rescue LocalCommandError => e
|
28
|
+
raise_error(e)
|
29
|
+
ensure
|
30
|
+
@installer = nil
|
31
|
+
end
|
32
|
+
|
33
|
+
def verify(verifier, roles, opts = {}) #:nodoc:
|
34
|
+
process(verifier.package.name, verifier.commands, roles, :suppress_and_return_failures => true)
|
35
|
+
end
|
36
|
+
|
37
|
+
protected
|
38
|
+
|
39
|
+
def process(name, commands, roles, opts = {}) #:nodoc:
|
40
|
+
@log_recorder = Sprinkle::Utility::LogRecorder.new
|
21
41
|
commands.each do |command|
|
22
|
-
|
23
|
-
|
42
|
+
if command == :RECONNECT
|
43
|
+
return true
|
44
|
+
elsif command == :TRANSFER
|
45
|
+
res = transfer(@installer.sourcepath, @installer.destination, roles,
|
46
|
+
:recursive => @installer.options[:recursive])
|
47
|
+
raise LocalCommandError if res != 0
|
48
|
+
else
|
49
|
+
res = run_command command
|
50
|
+
raise LocalCommandError if res != 0 and not opts[:suppress_and_return_failures]
|
51
|
+
end
|
24
52
|
end
|
25
53
|
return true
|
26
54
|
end
|
27
55
|
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
56
|
+
def run_command(cmd)
|
57
|
+
@log_recorder.reset cmd
|
58
|
+
pid, stdin, out, err = Open4.popen4(cmd)
|
59
|
+
ignored, status = Process::waitpid2 pid
|
60
|
+
@log_recorder.log :err, err.read
|
61
|
+
@log_recorder.log :out, out.read
|
62
|
+
@log_recorder.code = status.to_i
|
63
|
+
end
|
64
|
+
|
65
|
+
def raise_error(e)
|
66
|
+
raise Sprinkle::Errors::RemoteCommandFailure.new(@installer, @log_recorder.hash, e)
|
67
|
+
end
|
68
|
+
|
69
|
+
def transfer(source, destination, roles, opts ={}) #:nodoc:
|
70
|
+
opts.reverse_merge!(:recursive => true)
|
71
|
+
flags = "-R " if opts[:recursive]
|
32
72
|
|
33
|
-
|
73
|
+
run_command "cp #{flags}#{source} #{destination}"
|
34
74
|
end
|
75
|
+
|
76
|
+
|
35
77
|
end
|
36
78
|
end
|
37
79
|
end
|