2014-01-15 19:30:32 +00:00
|
|
|
require "vagrant/util/platform"
|
2014-01-13 19:01:50 +00:00
|
|
|
require "vagrant/util/subprocess"
|
|
|
|
|
|
|
|
module VagrantPlugins
|
|
|
|
module SyncedFolderRSync
|
|
|
|
# This is a helper that abstracts out the functionality of rsyncing
|
|
|
|
# folders so that it can be called from anywhere.
|
|
|
|
class RsyncHelper
|
2014-03-13 02:40:18 +00:00
|
|
|
# This converts an rsync exclude pattern to a regular expression
|
|
|
|
# we can send to Listen.
|
|
|
|
def self.exclude_to_regexp(path, exclude)
|
|
|
|
start_anchor = false
|
|
|
|
|
|
|
|
if exclude.start_with?("/")
|
|
|
|
start_anchor = true
|
|
|
|
exclude = exclude[1..-1]
|
|
|
|
end
|
|
|
|
|
|
|
|
path = "#{path}/" if !path.end_with?("/")
|
|
|
|
regexp = "^#{Regexp.escape(path)}"
|
|
|
|
regexp += ".*" if !start_anchor
|
|
|
|
|
|
|
|
# This is REALLY ghetto, but its a start. We can improve and
|
|
|
|
# keep unit tests passing in the future.
|
|
|
|
exclude = exclude.gsub("**", "|||GLOBAL|||")
|
|
|
|
exclude = exclude.gsub("*", "|||PATH|||")
|
|
|
|
exclude = exclude.gsub("|||PATH|||", "[^/]*")
|
|
|
|
exclude = exclude.gsub("|||GLOBAL|||", ".*")
|
|
|
|
regexp += exclude
|
|
|
|
|
|
|
|
Regexp.new(regexp)
|
|
|
|
end
|
|
|
|
|
2014-01-13 19:01:50 +00:00
|
|
|
def self.rsync_single(machine, ssh_info, opts)
|
|
|
|
# Folder info
|
|
|
|
guestpath = opts[:guestpath]
|
|
|
|
hostpath = opts[:hostpath]
|
2014-01-13 19:34:49 +00:00
|
|
|
hostpath = File.expand_path(hostpath, machine.env.root_path)
|
2014-01-15 19:30:32 +00:00
|
|
|
hostpath = Vagrant::Util::Platform.fs_real_path(hostpath).to_s
|
2014-01-13 19:01:50 +00:00
|
|
|
|
2014-01-16 05:13:08 +00:00
|
|
|
if Vagrant::Util::Platform.windows?
|
2014-03-10 01:21:30 +00:00
|
|
|
# rsync for Windows expects cygwin style paths, always.
|
|
|
|
hostpath = Vagrant::Util::Platform.cygwin_path(hostpath)
|
2014-01-16 05:13:08 +00:00
|
|
|
end
|
|
|
|
|
2014-01-16 05:22:37 +00:00
|
|
|
# Make sure the host path ends with a "/" to avoid creating
|
|
|
|
# a nested directory...
|
|
|
|
if !hostpath.end_with?("/")
|
|
|
|
hostpath += "/"
|
|
|
|
end
|
|
|
|
|
2014-03-13 01:43:59 +00:00
|
|
|
# Folder options
|
|
|
|
opts[:owner] ||= ssh_info[:username]
|
|
|
|
opts[:group] ||= ssh_info[:username]
|
|
|
|
|
2014-01-13 19:01:50 +00:00
|
|
|
# Connection information
|
|
|
|
username = ssh_info[:username]
|
|
|
|
host = ssh_info[:host]
|
2014-04-27 22:53:31 +00:00
|
|
|
proxy_command = ""
|
|
|
|
if ssh_info[:proxy_command]
|
|
|
|
proxy_command = "-o ProxyCommand='#{ssh_info[:proxy_command]}' "
|
|
|
|
end
|
|
|
|
|
2014-01-13 19:01:50 +00:00
|
|
|
rsh = [
|
2014-04-22 03:39:48 +00:00
|
|
|
"ssh -p #{ssh_info[:port]} " +
|
2014-04-26 17:52:39 +00:00
|
|
|
proxy_command +
|
2014-04-22 03:39:48 +00:00
|
|
|
"-o StrictHostKeyChecking=no " +
|
2015-01-12 14:19:43 +00:00
|
|
|
"-o IdentitiesOnly=true " +
|
2014-04-22 03:39:48 +00:00
|
|
|
"-o UserKnownHostsFile=/dev/null",
|
2014-01-13 19:01:50 +00:00
|
|
|
ssh_info[:private_key_path].map { |p| "-i '#{p}'" },
|
|
|
|
].flatten.join(" ")
|
|
|
|
|
|
|
|
# Exclude some files by default, and any that might be configured
|
|
|
|
# by the user.
|
|
|
|
excludes = ['.vagrant/']
|
|
|
|
excludes += Array(opts[:exclude]).map(&:to_s) if opts[:exclude]
|
|
|
|
excludes.uniq!
|
|
|
|
|
2014-03-06 19:27:58 +00:00
|
|
|
# Get the command-line arguments
|
|
|
|
args = nil
|
2014-05-21 02:23:39 +00:00
|
|
|
args = Array(opts[:args]).dup if opts[:args]
|
2014-05-09 16:31:31 +00:00
|
|
|
args ||= ["--verbose", "--archive", "--delete", "-z", "--copy-links"]
|
2014-03-06 19:27:58 +00:00
|
|
|
|
2014-04-05 09:01:09 +00:00
|
|
|
# On Windows, we have to set a default chmod flag to avoid permission issues
|
|
|
|
if Vagrant::Util::Platform.windows? && !args.any? { |arg| arg.start_with?("--chmod=") }
|
2014-04-05 16:05:39 +00:00
|
|
|
# Ensures that all non-masked bits get enabled
|
2014-04-02 16:07:00 +00:00
|
|
|
args << "--chmod=ugo=rwX"
|
2014-04-05 16:05:39 +00:00
|
|
|
|
|
|
|
# Remove the -p option if --archive is enabled (--archive equals -rlptgoD)
|
|
|
|
# otherwise new files will not have the destination-default permissions
|
|
|
|
args << "--no-perms" if args.include?("--archive") || args.include?("-a")
|
2014-04-02 16:07:00 +00:00
|
|
|
end
|
|
|
|
|
2014-04-25 19:00:12 +00:00
|
|
|
# Disable rsync's owner/group preservation (implied by --archive) unless
|
|
|
|
# specifically requested, since we adjust owner/group to match shared
|
|
|
|
# folder setting ourselves.
|
|
|
|
args << "--no-owner" unless args.include?("--owner") || args.include?("-o")
|
|
|
|
args << "--no-group" unless args.include?("--group") || args.include?("-g")
|
|
|
|
|
|
|
|
# Tell local rsync how to invoke remote rsync with sudo
|
|
|
|
if machine.guest.capability?(:rsync_command)
|
|
|
|
args << "--rsync-path"<< machine.guest.capability(:rsync_command)
|
|
|
|
end
|
|
|
|
|
2014-01-13 19:01:50 +00:00
|
|
|
# Build up the actual command to execute
|
|
|
|
command = [
|
|
|
|
"rsync",
|
2014-03-06 19:27:58 +00:00
|
|
|
args,
|
2014-01-13 19:01:50 +00:00
|
|
|
"-e", rsh,
|
2014-03-06 19:27:58 +00:00
|
|
|
excludes.map { |e| ["--exclude", e] },
|
2014-01-13 19:01:50 +00:00
|
|
|
hostpath,
|
2014-03-06 19:27:58 +00:00
|
|
|
"#{username}@#{host}:#{guestpath}",
|
2014-01-13 19:01:50 +00:00
|
|
|
].flatten
|
|
|
|
|
|
|
|
# The working directory should be the root path
|
2014-03-06 19:27:58 +00:00
|
|
|
command_opts = {}
|
2014-01-13 19:01:50 +00:00
|
|
|
command_opts[:workdir] = machine.env.root_path.to_s
|
|
|
|
|
|
|
|
machine.ui.info(I18n.t(
|
|
|
|
"vagrant.rsync_folder", guestpath: guestpath, hostpath: hostpath))
|
|
|
|
if excludes.length > 1
|
|
|
|
machine.ui.info(I18n.t(
|
|
|
|
"vagrant.rsync_folder_excludes", excludes: excludes.inspect))
|
|
|
|
end
|
2015-01-05 20:36:00 +00:00
|
|
|
if opts.include?(:verbose)
|
2014-11-25 20:37:59 +00:00
|
|
|
machine.ui.info(I18n.t("vagrant.rsync_showing_output"));
|
2014-11-25 18:38:41 +00:00
|
|
|
end
|
2014-01-13 19:01:50 +00:00
|
|
|
|
|
|
|
# If we have tasks to do before rsyncing, do those.
|
|
|
|
if machine.guest.capability?(:rsync_pre)
|
|
|
|
machine.guest.capability(:rsync_pre, opts)
|
|
|
|
end
|
|
|
|
|
2015-01-05 20:36:00 +00:00
|
|
|
if opts.include?(:verbose)
|
2014-11-25 18:38:41 +00:00
|
|
|
command_opts[:notify] = [ :stdout, :stderr ];
|
2014-11-25 20:37:59 +00:00
|
|
|
r = Vagrant::Util::Subprocess.execute(*(command + [command_opts])) {
|
|
|
|
|io_name,data| data.each_line { |line| machine.ui.info("rsync[#{io_name}] -> #{line}") }
|
|
|
|
}
|
2014-11-25 18:38:41 +00:00
|
|
|
else
|
|
|
|
r = Vagrant::Util::Subprocess.execute(*(command + [command_opts]))
|
|
|
|
end
|
|
|
|
|
2014-01-13 19:01:50 +00:00
|
|
|
if r.exit_code != 0
|
|
|
|
raise Vagrant::Errors::RSyncError,
|
|
|
|
command: command.join(" "),
|
|
|
|
guestpath: guestpath,
|
|
|
|
hostpath: hostpath,
|
|
|
|
stderr: r.stderr
|
|
|
|
end
|
2014-03-13 01:43:59 +00:00
|
|
|
|
|
|
|
# If we have tasks to do after rsyncing, do those.
|
|
|
|
if machine.guest.capability?(:rsync_post)
|
|
|
|
machine.guest.capability(:rsync_post, opts)
|
|
|
|
end
|
2014-01-13 19:01:50 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|