plugins/synced_folders/rsync/helper.rb in vagrant-unbundled-2.2.2.0 vs plugins/synced_folders/rsync/helper.rb in vagrant-unbundled-2.2.3.0

- old
+ new

@@ -8,10 +8,13 @@ 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 + # rsync version requirement to support chown argument + RSYNC_CHOWN_REQUIREMENT = Gem::Requirement.new(">= 3.1.0").freeze + # 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 @@ -22,11 +25,11 @@ 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 + # This is not an ideal solution, but it's 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|||", ".*") @@ -75,10 +78,15 @@ proxy_command = "" if ssh_info[:proxy_command] proxy_command = "-o ProxyCommand='#{ssh_info[:proxy_command]}' " end + ssh_config_file = "" + if ssh_info[:config] + ssh_config_file = "-F #{ssh_info[:config]}" + end + # Create the path for the control sockets. We used to do this # in the machine data dir but this can result in paths that are # too long for unix domain sockets. control_options = "" unless Vagrant::Util::Platform.windows? @@ -89,10 +97,11 @@ # rsh cmd option rsh = [ "ssh", "-p", "#{ssh_info[:port]}", "-o", "LogLevel=#{log_level}", proxy_command, + ssh_config_file, control_options, ] # Solaris/OpenSolaris/Illumos uses SunSSH which doesn't support the # IdentitiesOnly option. Also, we don't enable it if keys_only is false @@ -132,15 +141,22 @@ # 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") end - # 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") + if opts[:rsync_ownership] && rsync_chown_support?(machine) + # Allow rsync to map ownership + args << "--chown=#{opts[:owner]}:#{opts[:group]}" + # Notify rsync post capability not to chown + opts[:chown] = false + else + # 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") + end # Tell local rsync how to invoke remote rsync with sudo rsync_path = opts[:rsync_path] if !rsync_path && machine.guest.capability?(:rsync_command) rsync_path = machine.guest.capability(:rsync_command) @@ -205,11 +221,67 @@ stderr: r.stderr end # If we have tasks to do after rsyncing, do those. if machine.guest.capability?(:rsync_post) - machine.guest.capability(:rsync_post, opts) + begin + machine.guest.capability(:rsync_post, opts) + rescue Vagrant::Errors::VagrantError => err + raise Vagrant::Errors::RSyncPostCommandError, + guestpath: guestpath, + hostpath: hostpath, + message: err.to_s + end end + end + + # Check if rsync versions support using chown option + # + # @param [Vagrant::Machine] machine The remote machine + # @return [Boolean] + def self.rsync_chown_support?(machine) + if !RSYNC_CHOWN_REQUIREMENT.satisfied_by?(Gem::Version.new(local_rsync_version)) + return false + end + mrv = machine_rsync_version(machine) + if mrv && !RSYNC_CHOWN_REQUIREMENT.satisfied_by?(Gem::Version.new(mrv)) + return false + end + true + end + + # @return [String, nil] version of remote rsync + def self.machine_rsync_version(machine) + if machine.guest.capability?(:rsync_command) + rsync_path = machine.guest.capability(:rsync_command) + else + rsync_path = "rsync" + end + output = "" + machine.communicate.execute(rsync_path + " --version") { |_, data| output << data } + vmatch = output.match(/version\s+(?<version>[\d.]+)\s/) + if vmatch + vmatch[:version] + end + end + + # @return [String, nil] version of local rsync + def self.local_rsync_version + if !@_rsync_version + r = Vagrant::Util::Subprocess.execute("rsync", "--version") + vmatch = r.stdout.to_s.match(/version\s+(?<version>[\d.]+)\s/) + if vmatch + @_rsync_version = vmatch[:version] + end + end + @_rsync_version + end + + # @private + # Reset the cached values for helper. This is not considered a public + # API and should only be used for testing. + def self.reset! + instance_variables.each(&method(:remove_instance_variable)) end end end end