diff --git a/lib/vagrant/errors.rb b/lib/vagrant/errors.rb index 057d6a823..ebdf23516 100644 --- a/lib/vagrant/errors.rb +++ b/lib/vagrant/errors.rb @@ -324,10 +324,6 @@ module Vagrant error_key(:darwin_mount_failed) end - class DarwinNFSMountFailed < VagrantError - error_key(:darwin_nfs_mount_failed) - end - class DestroyRequiresForce < VagrantError error_key(:destroy_requires_force) end @@ -404,10 +400,6 @@ module Vagrant error_key(:linux_mount_failed) end - class LinuxNFSMountFailed < VagrantError - error_key(:linux_nfs_mount_failed) - end - class LinuxRDPClientNotFound < VagrantError error_key(:linux_rdp_client_not_found) end @@ -464,6 +456,10 @@ module Vagrant error_key(:nfs_cant_read_exports) end + class NFSMountFailed < VagrantError + error_key(:nfs_mount_failed) + end + class NFSNoGuestIP < VagrantError error_key(:nfs_no_guest_ip) end diff --git a/plugins/guests/bsd/cap/nfs.rb b/plugins/guests/bsd/cap/nfs.rb new file mode 100644 index 000000000..a54835e89 --- /dev/null +++ b/plugins/guests/bsd/cap/nfs.rb @@ -0,0 +1,49 @@ +require "shellwords" +require "vagrant/util/retryable" + +module VagrantPlugins + module GuestBSD + module Cap + class NFS + extend Vagrant::Util::Retryable + + # Mount the given NFS folder. + def self.mount_nfs_folder(machine, ip, folders) + comm = machine.communicate + + folders.each do |name, opts| + # Mount each folder separately so we can retry. + commands = ["set -e"] + + # Shellescape the paths in case they do not have special characters. + guest_path = Shellwords.escape(opts[:guestpath]) + host_path = Shellwords.escape(opts[:hostpath]) + + # Build the list of mount options. + mount_opts = [] + mount_opts << "nfsv#{opts[:nfs_version]}" if opts[:nfs_version] + mount_opts << "mntudp" if opts[:nfs_udp] + if opts[:mount_options] + mount_opts = mount_opts + opts[:mount_options].dup + end + mount_opts = mount_opts.join(",") + + # Make the directory on the guest. + commands << "mkdir -p #{guest_path}" + + # Perform the mount operation. + commands << "/sbin/mount -t nfs -o '#{mount_opts}' #{ip}:#{host_path} #{guest_path}" + + # Run the command, raising a specific error. + retryable(on: Vagrant::Errors::NFSMountFailed, tries: 3, sleep: 5) do + machine.communicate.sudo(commands.join("\n"), + error_class: Vagrant::Errors::NFSMountFailed, + shell: "sh", + ) + end + end + end + end + end + end +end diff --git a/plugins/guests/bsd/plugin.rb b/plugins/guests/bsd/plugin.rb index a6d82742f..2da50eaf7 100644 --- a/plugins/guests/bsd/plugin.rb +++ b/plugins/guests/bsd/plugin.rb @@ -15,6 +15,11 @@ module VagrantPlugins require_relative "cap/insert_public_key" Cap::InsertPublicKey end + + guest_capability(:bsd, :mount_nfs_folder) do + require_relative "cap/nfs" + Cap::NFS + end end end end diff --git a/plugins/guests/darwin/cap/mount_nfs_folder.rb b/plugins/guests/darwin/cap/mount_nfs_folder.rb deleted file mode 100644 index 85c68e8e7..000000000 --- a/plugins/guests/darwin/cap/mount_nfs_folder.rb +++ /dev/null @@ -1,37 +0,0 @@ -require "vagrant/util/retryable" - -module VagrantPlugins - module GuestDarwin - module Cap - class MountNFSFolder - extend Vagrant::Util::Retryable - def self.mount_nfs_folder(machine, ip, folders) - folders.each do |name, opts| - # Expand the guest path so we can handle things like "~/vagrant" - expanded_guest_path = machine.guest.capability( - :shell_expand_guest_path, opts[:guestpath]) - - # Create the folder - machine.communicate.sudo("mkdir -p #{expanded_guest_path}") - - # Figure out any options - mount_opts = ["vers=#{opts[:nfs_version]}"] - mount_opts << "udp" if opts[:nfs_udp] - if opts[:mount_options] - mount_opts = opts[:mount_options].dup - end - - mount_command = "mount -t nfs " + - "-o '#{mount_opts.join(",")}' " + - "'#{ip}:#{opts[:hostpath]}' '#{expanded_guest_path}'" - retryable(on: Vagrant::Errors::DarwinNFSMountFailed, tries: 10, sleep: 5) do - machine.communicate.sudo( - mount_command, - error_class: Vagrant::Errors::DarwinNFSMountFailed) - end - end - end - end - end - end -end diff --git a/plugins/guests/darwin/plugin.rb b/plugins/guests/darwin/plugin.rb index 5f1ce453c..c3c264bd2 100644 --- a/plugins/guests/darwin/plugin.rb +++ b/plugins/guests/darwin/plugin.rb @@ -31,11 +31,6 @@ module VagrantPlugins Cap::Halt end - guest_capability(:darwin, :mount_nfs_folder) do - require_relative "cap/mount_nfs_folder" - Cap::MountNFSFolder - end - guest_capability(:darwin, :mount_smb_shared_folder) do require_relative "cap/mount_smb_shared_folder" Cap::MountSMBSharedFolder diff --git a/plugins/guests/esxi/cap/mount_nfs_folder.rb b/plugins/guests/esxi/cap/mount_nfs_folder.rb index 1c2f80820..3fe7e2a3b 100644 --- a/plugins/guests/esxi/cap/mount_nfs_folder.rb +++ b/plugins/guests/esxi/cap/mount_nfs_folder.rb @@ -13,9 +13,9 @@ module VagrantPlugins comm.execute("localcli storage nfs remove -v #{volume}") end mount_command = "localcli storage nfs add -H #{ip} -s '#{opts[:hostpath]}' -v '#{volume}'" - retryable(on: Vagrant::Errors::LinuxNFSMountFailed, tries: 5, sleep: 2) do + retryable(on: Vagrant::Errors::NFSMountFailed, tries: 5, sleep: 2) do comm.execute(mount_command, - error_class: Vagrant::Errors::LinuxNFSMountFailed) + error_class: Vagrant::Errors::NFSMountFailed) end # symlink vmfs volume to :guestpath diff --git a/plugins/guests/freebsd/cap/mount_nfs_folder.rb b/plugins/guests/freebsd/cap/mount_nfs_folder.rb deleted file mode 100644 index 1541eff33..000000000 --- a/plugins/guests/freebsd/cap/mount_nfs_folder.rb +++ /dev/null @@ -1,24 +0,0 @@ -module VagrantPlugins - module GuestFreeBSD - module Cap - class MountNFSFolder - def self.mount_nfs_folder(machine, ip, folders) - comm = machine.communicate - - commands = [] - - folders.each do |_, opts| - if opts[:nfs_version] - mount_opts = "-o nfsv#{opts[:nfs_version]}" - end - - commands << "mkdir -p '#{opts[:guestpath]}'" - commands << "mount -t nfs #{mount_opts} '#{ip}:#{opts[:hostpath]}' '#{opts[:guestpath]}'" - end - - comm.sudo(commands.join("\n"), { shell: "sh" }) - end - end - end - end -end diff --git a/plugins/guests/freebsd/plugin.rb b/plugins/guests/freebsd/plugin.rb index 17d2f30b2..000c67021 100644 --- a/plugins/guests/freebsd/plugin.rb +++ b/plugins/guests/freebsd/plugin.rb @@ -26,11 +26,6 @@ module VagrantPlugins Cap::Halt end - guest_capability(:freebsd, :mount_nfs_folder) do - require_relative "cap/mount_nfs_folder" - Cap::MountNFSFolder - end - guest_capability(:freebsd, :remove_public_key) do require_relative "cap/remove_public_key" Cap::RemovePublicKey diff --git a/plugins/guests/linux/cap/mount_nfs.rb b/plugins/guests/linux/cap/mount_nfs.rb index 83731f563..27be70a33 100644 --- a/plugins/guests/linux/cap/mount_nfs.rb +++ b/plugins/guests/linux/cap/mount_nfs.rb @@ -40,8 +40,8 @@ module VagrantPlugins EOH end - retryable(on: Vagrant::Errors::LinuxNFSMountFailed, tries: 8, sleep: 3) do - comm.sudo(commands.join("\n"), error_class: Vagrant::Errors::LinuxNFSMountFailed) + retryable(on: Vagrant::Errors::NFSMountFailed, tries: 8, sleep: 3) do + comm.sudo(commands.join("\n"), error_class: Vagrant::Errors::NFSMountFailed) end end end diff --git a/plugins/guests/netbsd/cap/mount_nfs_folder.rb b/plugins/guests/netbsd/cap/mount_nfs_folder.rb deleted file mode 100644 index 5facd3780..000000000 --- a/plugins/guests/netbsd/cap/mount_nfs_folder.rb +++ /dev/null @@ -1,17 +0,0 @@ -module VagrantPlugins - module GuestNetBSD - module Cap - class MountNFSFolder - def self.mount_nfs_folder(machine, ip, folders) - folders.each do |name, opts| - machine.communicate.sudo(< { + guestpath: "/guest", + hostpath: "/host", + } + } + cap.mount_nfs_folder(machine, ip, folders) + + expect(comm.received_commands[0]).to match(/set -e/) + expect(comm.received_commands[0]).to match(/mkdir -p \/guest/) + expect(comm.received_commands[0]).to match(/mount -t nfs/) + expect(comm.received_commands[0]).to match(/1.2.3.4:\/host \/guest/) + end + + it "mounts with options" do + folders = { + "/vagrant-nfs" => { + guestpath: "/guest", + hostpath: "/host", + nfs_version: 2, + nfs_udp: true, + mount_options: ["banana"] + } + } + cap.mount_nfs_folder(machine, ip, folders) + + expect(comm.received_commands[0]).to match(/mount -t nfs -o 'nfsv2,mntudp,banana'/) + end + + it "escapes host and guest paths" do + folders = { + "/vagrant-nfs" => { + guestpath: "/guest with spaces", + hostpath: "/host's", + } + } + cap.mount_nfs_folder(machine, ip, folders) + + expect(comm.received_commands[0]).to match(/host\\\'s/) + expect(comm.received_commands[0]).to match(/guest\\\ with\\\ spaces/) + end + end +end diff --git a/test/unit/plugins/guests/freebsd/cap/mount_nfs_folder_test.rb b/test/unit/plugins/guests/freebsd/cap/mount_nfs_folder_test.rb deleted file mode 100644 index b86ad26df..000000000 --- a/test/unit/plugins/guests/freebsd/cap/mount_nfs_folder_test.rb +++ /dev/null @@ -1,53 +0,0 @@ -require_relative "../../../../base" - -describe "VagrantPlugins::GuestFreeBSD::Cap::MountNFSFolder" do - let(:described_class) do - VagrantPlugins::GuestFreeBSD::Plugin - .components - .guest_capabilities[:freebsd] - .get(:mount_nfs_folder) - end - - let(:machine) { double("machine") } - let(:comm) { VagrantTests::DummyCommunicator::Communicator.new(machine) } - - before do - allow(machine).to receive(:communicate).and_return(comm) - end - - after do - comm.verify_expectations! - end - - describe ".mount_nfs_folder" do - let(:ip) { "1.2.3.4" } - - it "mounts the folder" do - folders = { - "/vagrant-nfs" => { - type: :nfs, - guestpath: "/guest", - hostpath: "/host", - } - } - described_class.mount_nfs_folder(machine, ip, folders) - - expect(comm.received_commands[0]).to match(/mkdir -p '\/guest'/) - expect(comm.received_commands[0]).to match(/'1.2.3.4:\/host' '\/guest'/) - end - - it "mounts with options" do - folders = { - "/vagrant-nfs" => { - type: :nfs, - guestpath: "/guest", - hostpath: "/host", - nfs_version: 2, - } - } - described_class.mount_nfs_folder(machine, ip, folders) - - expect(comm.received_commands[0]).to match(/mount -t nfs -o nfsv2/) - end - end -end