From 185a7dfc2e89e7f72dbd33f548b60e4b08fa7a64 Mon Sep 17 00:00:00 2001 From: Chris Roberts Date: Tue, 16 Aug 2016 15:08:57 -0700 Subject: [PATCH] guests/rsync do not chown files excluded from sync --- plugins/guests/darwin/cap/rsync.rb | 27 +----- plugins/guests/freebsd/cap/rsync.rb | 29 +------ plugins/guests/linux/cap/rsync.rb | 29 +------ plugins/guests/netbsd/cap/rsync.rb | 22 +---- plugins/guests/openbsd/cap/rsync.rb | 24 +----- plugins/guests/smartos/cap/rsync.rb | 11 ++- plugins/guests/solaris/cap/rsync.rb | 11 ++- plugins/guests/solaris11/cap/rsync.rb | 13 +-- .../synced_folders/rsync/default_unix_cap.rb | 45 ++++++++++ .../plugins/guests/linux/cap/rsync_test.rb | 85 +++++++++++++++++++ .../plugins/guests/smartos/cap/rsync_test.rb | 3 +- 11 files changed, 169 insertions(+), 130 deletions(-) create mode 100644 plugins/synced_folders/rsync/default_unix_cap.rb create mode 100644 test/unit/plugins/guests/linux/cap/rsync_test.rb diff --git a/plugins/guests/darwin/cap/rsync.rb b/plugins/guests/darwin/cap/rsync.rb index e1c594a19..6ab88a898 100644 --- a/plugins/guests/darwin/cap/rsync.rb +++ b/plugins/guests/darwin/cap/rsync.rb @@ -1,33 +1,10 @@ -require "shellwords" +require_relative "../../../synced_folders/rsync/default_unix_cap" module VagrantPlugins module GuestDarwin module Cap class RSync - def self.rsync_installed(machine) - machine.communicate.test("which rsync") - end - - def self.rsync_command(machine) - "sudo rsync" - end - - def self.rsync_pre(machine, opts) - guest_path = Shellwords.escape(opts[:guestpath]) - machine.communicate.sudo("mkdir -p #{guest_path}") - end - - def self.rsync_post(machine, opts) - if opts.key?(:chown) && !opts[:chown] - return - end - - guest_path = Shellwords.escape(opts[:guestpath]) - - machine.communicate.sudo( - "find #{guest_path} '(' ! -user #{opts[:owner]} -or ! -group #{opts[:group]} ')' -print0 | " + - "xargs -0 chown #{opts[:owner]}:#{opts[:group]}") - end + extend VagrantPlugins::SyncedFolderRSync::DefaultUnixCap end end end diff --git a/plugins/guests/freebsd/cap/rsync.rb b/plugins/guests/freebsd/cap/rsync.rb index 6064a8a1d..7766d64a2 100644 --- a/plugins/guests/freebsd/cap/rsync.rb +++ b/plugins/guests/freebsd/cap/rsync.rb @@ -1,37 +1,14 @@ -require "shellwords" +require_relative "../../../synced_folders/rsync/default_unix_cap" module VagrantPlugins module GuestFreeBSD module Cap class RSync + extend VagrantPlugins::SyncedFolderRSync::DefaultUnixCap + def self.rsync_install(machine) machine.communicate.sudo("pkg install -y rsync") end - - def self.rsync_installed(machine) - machine.communicate.test("which rsync") - end - - def self.rsync_command(machine) - "sudo rsync" - end - - def self.rsync_pre(machine, opts) - guest_path = Shellwords.escape(opts[:guestpath]) - machine.communicate.sudo("mkdir -p #{guest_path}") - end - - def self.rsync_post(machine, opts) - if opts.key?(:chown) && !opts[:chown] - return - end - - guest_path = Shellwords.escape(opts[:guestpath]) - - machine.communicate.sudo( - "find #{guest_path} '(' ! -user #{opts[:owner]} -or ! -group #{opts[:group]} ')' -print0 | " + - "xargs -0 -r chown #{opts[:owner]}:#{opts[:group]}") - end end end end diff --git a/plugins/guests/linux/cap/rsync.rb b/plugins/guests/linux/cap/rsync.rb index bd3d6f9b1..17ff0bbaa 100644 --- a/plugins/guests/linux/cap/rsync.rb +++ b/plugins/guests/linux/cap/rsync.rb @@ -1,35 +1,10 @@ -require "shellwords" +require_relative "../../../synced_folders/rsync/default_unix_cap" module VagrantPlugins module GuestLinux module Cap class RSync - def self.rsync_installed(machine) - machine.communicate.test("which rsync") - end - - def self.rsync_command(machine) - "sudo rsync" - end - - def self.rsync_pre(machine, opts) - guest_path = Shellwords.escape(opts[:guestpath]) - machine.communicate.sudo("mkdir -p #{guest_path}") - end - - def self.rsync_post(machine, opts) - if opts.key?(:chown) && !opts[:chown] - return - end - - guest_path = Shellwords.escape(opts[:guestpath]) - - machine.communicate.sudo( - "find #{guest_path} " + - "'!' -type l -a " + - "'(' ! -user #{opts[:owner]} -or ! -group #{opts[:group]} ')' -print0 | " + - "xargs -0 -r chown #{opts[:owner]}:#{opts[:group]}") - end + extend VagrantPlugins::SyncedFolderRSync::DefaultUnixCap end end end diff --git a/plugins/guests/netbsd/cap/rsync.rb b/plugins/guests/netbsd/cap/rsync.rb index 5e40a2742..e88a0ceb4 100644 --- a/plugins/guests/netbsd/cap/rsync.rb +++ b/plugins/guests/netbsd/cap/rsync.rb @@ -1,10 +1,10 @@ +require_relative "../../../synced_folders/rsync/default_unix_cap" + module VagrantPlugins module GuestNetBSD module Cap class RSync - def self.rsync_installed(machine) - machine.communicate.test("which rsync") - end + extend VagrantPlugins::SyncedFolderRSync::DefaultUnixCap def self.rsync_install(machine) machine.communicate.sudo( @@ -12,22 +12,6 @@ module VagrantPlugins '`uname -m`/`uname -r | cut -d. -f1-2`/All" ' \ 'pkg_add rsync') end - - def self.rsync_command(machine) - "sudo rsync" - end - - def self.rsync_pre(machine, opts) - machine.communicate.tap do |comm| - comm.sudo("mkdir -p '#{opts[:guestpath]}'") - end - end - - def self.rsync_post(machine, opts) - machine.communicate.sudo( - "find '#{opts[:guestpath]}' '(' ! -user #{opts[:owner]} -or ! -group #{opts[:group]} ')' -print0 | " + - "xargs -0 -r chown #{opts[:owner]}:#{opts[:group]}") - end end end end diff --git a/plugins/guests/openbsd/cap/rsync.rb b/plugins/guests/openbsd/cap/rsync.rb index 5467ecd67..54663ef6a 100644 --- a/plugins/guests/openbsd/cap/rsync.rb +++ b/plugins/guests/openbsd/cap/rsync.rb @@ -1,33 +1,17 @@ +require_relative "../../../synced_folders/rsync/default_unix_cap" + module VagrantPlugins module GuestOpenBSD module Cap class RSync + extend VagrantPlugins::SyncedFolderRSync::DefaultUnixCap + def self.rsync_install(machine) machine.communicate.sudo( 'PKG_PATH="http://ftp.openbsd.org/pub/OpenBSD/' \ '`uname -r`/packages/`arch -s`/" ' \ 'pkg_add -I rsync--') end - - def self.rsync_installed(machine) - machine.communicate.test("which rsync") - end - - def self.rsync_command(machine) - "sudo rsync" - end - - def self.rsync_pre(machine, opts) - machine.communicate.tap do |comm| - comm.sudo("mkdir -p '#{opts[:guestpath]}'") - end - end - - def self.rsync_post(machine, opts) - machine.communicate.sudo( - "find '#{opts[:guestpath]}' '(' ! -user #{opts[:owner]} -or ! -group #{opts[:group]} ')' -print0 | " + - "xargs -0 -r chown #{opts[:owner]}:#{opts[:group]}") - end end end end diff --git a/plugins/guests/smartos/cap/rsync.rb b/plugins/guests/smartos/cap/rsync.rb index 29f517596..df447db3b 100644 --- a/plugins/guests/smartos/cap/rsync.rb +++ b/plugins/guests/smartos/cap/rsync.rb @@ -1,7 +1,11 @@ +require_relative "../../../synced_folders/rsync/default_unix_cap" + module VagrantPlugins module GuestSmartos module Cap class RSync + extend VagrantPlugins::SyncedFolderRSync::DefaultUnixCap + def self.rsync_installed(machine) machine.communicate.test("which rsync") end @@ -17,8 +21,11 @@ module VagrantPlugins end def self.rsync_post(machine, opts) - machine.communicate.execute("#{machine.config.smartos.suexec_cmd} find '#{opts[:guestpath]}' '(' ! -user #{opts[:owner]} -or ! -group #{opts[:group]} ')' -print0 | " + - "#{machine.config.smartos.suexec_cmd} xargs -0 chown #{opts[:owner]}:#{opts[:group]}") + if opts.key?(:chown) && !opts[:chown] + return + end + suexec_cmd = machine.config.smartos.suexec_cmd + machine.communicate.execute("#{suexec_cmd} #{build_rsync_chown(opts)}") end end end diff --git a/plugins/guests/solaris/cap/rsync.rb b/plugins/guests/solaris/cap/rsync.rb index 0d0220f40..e72c8095d 100644 --- a/plugins/guests/solaris/cap/rsync.rb +++ b/plugins/guests/solaris/cap/rsync.rb @@ -1,7 +1,11 @@ +require_relative "../../../synced_folders/rsync/default_unix_cap" + module VagrantPlugins module GuestSolaris module Cap class RSync + extend VagrantPlugins::SyncedFolderRSync::DefaultUnixCap + def self.rsync_installed(machine) machine.communicate.test("which rsync") end @@ -17,10 +21,11 @@ module VagrantPlugins end def self.rsync_post(machine, opts) + if opts.key?(:chown) && !opts[:chown] + return + end suexec_cmd = machine.config.solaris.suexec_cmd - machine.communicate.execute( - "#{suexec_cmd} find '#{opts[:guestpath]}' '(' ! -user #{opts[:owner]} -or ! -group #{opts[:group]} ')' -print0 | " + - "xargs -0 chown #{opts[:owner]}:#{opts[:group]}") + machine.communicate.execute("#{suexec_cmd} #{build_rsync_chown(opts)}") end end end diff --git a/plugins/guests/solaris11/cap/rsync.rb b/plugins/guests/solaris11/cap/rsync.rb index 95df046c9..f86f39a10 100644 --- a/plugins/guests/solaris11/cap/rsync.rb +++ b/plugins/guests/solaris11/cap/rsync.rb @@ -1,10 +1,10 @@ +require_relative "../../../synced_folders/rsync/default_unix_cap" + module VagrantPlugins module GuestSolaris11 module Cap class RSync - def self.rsync_installed(machine) - machine.communicate.test("which rsync") - end + extend VagrantPlugins::SyncedFolderRSync::DefaultUnixCap def self.rsync_command(machine) "#{machine.config.solaris11.suexec_cmd} rsync" @@ -17,10 +17,11 @@ module VagrantPlugins end def self.rsync_post(machine, opts) + if opts.key?(:chown) && !opts[:chown] + return + end suexec_cmd = machine.config.solaris11.suexec_cmd - machine.communicate.execute( - "#{suexec_cmd} '#{opts[:guestpath]}' '(' ! -user #{opts[:owner]} -or ! -group #{opts[:group]} ')' -print0 | " + - "xargs -0 chown #{opts[:owner]}:#{opts[:group]}") + machine.communicate.execute("#{suexec_cmd} #{build_rsync_chown(opts)}") end end end diff --git a/plugins/synced_folders/rsync/default_unix_cap.rb b/plugins/synced_folders/rsync/default_unix_cap.rb new file mode 100644 index 000000000..6e073fde5 --- /dev/null +++ b/plugins/synced_folders/rsync/default_unix_cap.rb @@ -0,0 +1,45 @@ +require "shellwords" + +module VagrantPlugins + module SyncedFolderRSync + # This module provides default rsync capabilities for + # unix type operating systems. + module DefaultUnixCap + + def rsync_installed(machine) + machine.communicate.test("which rsync") + end + + def rsync_command(machine) + "sudo rsync" + end + + def rsync_pre(machine, opts) + guest_path = Shellwords.escape(opts[:guestpath]) + machine.communicate.sudo("mkdir -p #{guest_path}") + end + + def rsync_post(machine, opts) + if opts.key?(:chown) && !opts[:chown] + return + end + machine.communicate.sudo(build_rsync_chown(opts)) + end + + def build_rsync_chown(opts) + guest_path = Shellwords.escape(opts[:guestpath]) + if(opts[:exclude]) + exclude_base = Pathname.new(opts[:guestpath]) + exclusions = Array(opts[:exclude]).map do |ex_path| + ex_path = ex_path.slice(1, ex_path.size) if ex_path.start_with?(File::SEPARATOR) + "-path #{exclude_base.join(ex_path)} -prune" + end.join(" -o ") + " -o " + end + "find #{guest_path} #{exclusions}" \ + "'!' -type l -a " \ + "'(' ! -user #{opts[:owner]} -or ! -group #{opts[:group]} ')' -exec " \ + "chown #{opts[:owner]}:#{opts[:group]} '{}' +" + end + end + end +end diff --git a/test/unit/plugins/guests/linux/cap/rsync_test.rb b/test/unit/plugins/guests/linux/cap/rsync_test.rb new file mode 100644 index 000000000..4676cdf04 --- /dev/null +++ b/test/unit/plugins/guests/linux/cap/rsync_test.rb @@ -0,0 +1,85 @@ +require_relative "../../../../base" + +describe "VagrantPlugins::GuestLinux::Cap::Rsync" do + let(:caps) do + VagrantPlugins::GuestLinux::Plugin + .components + .guest_capabilities[:linux] + end + + let(:machine) { double("machine") } + let(:comm) { VagrantTests::DummyCommunicator::Communicator.new(machine) } + let(:guest_directory){ "/guest/directory/path" } + + before do + allow(machine).to receive(:communicate).and_return(comm) + end + + after do + comm.verify_expectations! + end + + describe ".rsync_installed" do + let(:cap) { caps.get(:rsync_installed) } + + it "checks if the command is installed" do + comm.expect_command("which rsync") + cap.rsync_installed(machine) + end + end + + describe ".rsync_command" do + let(:cap) { caps.get(:rsync_command) } + + it "provides the rsync command to use" do + expect(cap.rsync_command(machine)).to eq("sudo rsync") + end + end + + describe ".rsync_pre" do + let(:cap) { caps.get(:rsync_pre) } + + it "creates target directory on guest" do + comm.expect_command("mkdir -p #{guest_directory}") + cap.rsync_pre(machine, :guestpath => guest_directory) + end + end + + describe ".rsync_post" do + let(:cap) { caps.get(:rsync_post) } + let(:host_directory){ '.' } + let(:owner) { "vagrant-user" } + let(:group) { "vagrant-group" } + let(:excludes) { false } + let(:options) do + { + hostpath: host_directory, + guestpath: guest_directory, + owner: owner, + group: group, + exclude: excludes + } + end + + it "chowns files within the guest directory" do + comm.expect_command( + "find #{guest_directory} '!' -type l -a '(' ! -user #{owner} -or " \ + "! -group #{group} ')' -exec chown #{owner}:#{group} '{}' +" + ) + cap.rsync_post(machine, options) + end + + context "with excludes provided" do + let(:excludes){ ["tmp", "state/*"] } + + it "ignores files that are excluded" do + comm.expect_command( + "find #{guest_directory} -path #{File.join(guest_directory, excludes.first)} -prune -o " \ + "-path #{File.join(guest_directory, excludes.last)} -prune -o '!' -type l -a '(' ! -user " \ + "#{owner} -or ! -group #{group} ')' -exec chown #{owner}:#{group} '{}' +" + ) + cap.rsync_post(machine, options) + end + end + end +end diff --git a/test/unit/plugins/guests/smartos/cap/rsync_test.rb b/test/unit/plugins/guests/smartos/cap/rsync_test.rb index 1c3deb716..f5e82d9f8 100644 --- a/test/unit/plugins/guests/smartos/cap/rsync_test.rb +++ b/test/unit/plugins/guests/smartos/cap/rsync_test.rb @@ -40,9 +40,8 @@ describe "VagrantPlugins::VagrantPlugins::Cap::Rsync" do describe ".rsync_post" do it 'chowns incorrectly owned files in sync dir' do - communicator.expect_command("pfexec find '/sync_dir' '(' ! -user somebody -or ! -group somegroup ')' -print0 | pfexec xargs -0 chown somebody:somegroup") + communicator.expect_command("pfexec find /sync_dir '!' -type l -a '(' ! -user somebody -or ! -group somegroup ')' -exec chown somebody:somegroup '{}' +") plugin.rsync_post(machine, guestpath: '/sync_dir', owner: 'somebody', group: 'somegroup') end end end -