guests/linux: Properly escape and retry vbox shared folder mounting
This commit is contained in:
parent
4aaa600bd6
commit
7e88266999
|
@ -780,6 +780,10 @@ module Vagrant
|
||||||
error_key(:virtualbox_no_name)
|
error_key(:virtualbox_no_name)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
class VirtualBoxMountFailed < VagrantError
|
||||||
|
error_key(:virtualbox_mount_failed)
|
||||||
|
end
|
||||||
|
|
||||||
class VirtualBoxNameExists < VagrantError
|
class VirtualBoxNameExists < VagrantError
|
||||||
error_key(:virtualbox_name_exists)
|
error_key(:virtualbox_name_exists)
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,12 +1,17 @@
|
||||||
|
require "shellwords"
|
||||||
|
|
||||||
|
require "vagrant/util/retryable"
|
||||||
|
|
||||||
module VagrantPlugins
|
module VagrantPlugins
|
||||||
module GuestLinux
|
module GuestLinux
|
||||||
module Cap
|
module Cap
|
||||||
class MountVirtualBoxSharedFolder
|
class MountVirtualBoxSharedFolder
|
||||||
def self.mount_virtualbox_shared_folder(machine, name, guestpath, options)
|
extend Vagrant::Util::Retryable
|
||||||
expanded_guest_path = machine.guest.capability(
|
|
||||||
:shell_expand_guest_path, guestpath)
|
|
||||||
|
|
||||||
mount_commands = []
|
def self.mount_virtualbox_shared_folder(machine, name, guestpath, options)
|
||||||
|
guest_path = Shellwords.escape(guestpath)
|
||||||
|
|
||||||
|
mount_commands = ["set -e"]
|
||||||
|
|
||||||
if options[:owner].is_a? Integer
|
if options[:owner].is_a? Integer
|
||||||
mount_uid = options[:owner]
|
mount_uid = options[:owner]
|
||||||
|
@ -25,73 +30,54 @@ module VagrantPlugins
|
||||||
# First mount command uses getent to get the group
|
# First mount command uses getent to get the group
|
||||||
mount_options = "-o uid=#{mount_uid},gid=#{mount_gid}"
|
mount_options = "-o uid=#{mount_uid},gid=#{mount_gid}"
|
||||||
mount_options += ",#{options[:mount_options].join(",")}" if options[:mount_options]
|
mount_options += ",#{options[:mount_options].join(",")}" if options[:mount_options]
|
||||||
mount_commands << "mount -t vboxsf #{mount_options} #{name} #{expanded_guest_path}"
|
mount_commands << "mount -t vboxsf #{mount_options} #{name} #{guest_path}"
|
||||||
|
|
||||||
# Second mount command uses the old style `id -g`
|
# Second mount command uses the old style `id -g`
|
||||||
mount_options = "-o uid=#{mount_uid},gid=#{mount_gid_old}"
|
mount_options = "-o uid=#{mount_uid},gid=#{mount_gid_old}"
|
||||||
mount_options += ",#{options[:mount_options].join(",")}" if options[:mount_options]
|
mount_options += ",#{options[:mount_options].join(",")}" if options[:mount_options]
|
||||||
mount_commands << "mount -t vboxsf #{mount_options} #{name} #{expanded_guest_path}"
|
mount_commands << "mount -t vboxsf #{mount_options} #{name} #{guest_path}"
|
||||||
|
|
||||||
# Create the guest path if it doesn't exist
|
# Create the guest path if it doesn't exist
|
||||||
machine.communicate.sudo("mkdir -p #{expanded_guest_path}")
|
machine.communicate.sudo("mkdir -p #{guest_path}")
|
||||||
|
|
||||||
# Attempt to mount the folder. We retry here a few times because
|
# Attempt to mount the folder. We retry here a few times because
|
||||||
# it can fail early on.
|
# it can fail early on.
|
||||||
attempts = 0
|
command = mount_commands.join("\n")
|
||||||
while true
|
stderr = ""
|
||||||
success = true
|
retryable(on: Vagrant::Errors::VirtualBoxMountFailed, tries: 3, sleep: 5) do
|
||||||
|
machine.communicate.sudo(command,
|
||||||
stderr = ""
|
error_class: Vagrant::Errors::VirtualBoxMountFailed,
|
||||||
mount_commands.each do |command|
|
error_key: :virtualbox_mount_failed,
|
||||||
no_such_device = false
|
command: command,
|
||||||
stderr = ""
|
output: stderr,
|
||||||
status = machine.communicate.sudo(command, error_check: false) do |type, data|
|
) { |type, data| stderr = data if type == :stderr }
|
||||||
if type == :stderr
|
|
||||||
no_such_device = true if data =~ /No such device/i
|
|
||||||
stderr += data.to_s
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
success = status == 0 && !no_such_device
|
|
||||||
break if success
|
|
||||||
end
|
|
||||||
|
|
||||||
break if success
|
|
||||||
|
|
||||||
attempts += 1
|
|
||||||
if attempts > 10
|
|
||||||
raise Vagrant::Errors::LinuxMountFailed,
|
|
||||||
command: mount_commands.join("\n"),
|
|
||||||
output: stderr
|
|
||||||
end
|
|
||||||
|
|
||||||
sleep(2*attempts)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# Chown the directory to the proper user. We skip this if the
|
# Chown the directory to the proper user. We skip this if the
|
||||||
# mount options contained a readonly flag, because it won't work.
|
# mount options contained a readonly flag, because it won't work.
|
||||||
if !options[:mount_options] || !options[:mount_options].include?("ro")
|
if !options[:mount_options] || !options[:mount_options].include?("ro")
|
||||||
chown_commands = []
|
chown_commands = []
|
||||||
chown_commands << "chown #{mount_uid}:#{mount_gid} #{expanded_guest_path}"
|
chown_commands << "chown #{mount_uid}:#{mount_gid} #{guest_path}"
|
||||||
chown_commands << "chown #{mount_uid}:#{mount_gid_old} #{expanded_guest_path}"
|
chown_commands << "chown #{mount_uid}:#{mount_gid_old} #{guest_path}"
|
||||||
|
|
||||||
exit_status = machine.communicate.sudo(chown_commands[0], error_check: false)
|
exit_status = machine.communicate.sudo(chown_commands[0], error_check: false)
|
||||||
machine.communicate.sudo(chown_commands[1]) if exit_status != 0
|
machine.communicate.sudo(chown_commands[1]) if exit_status != 0
|
||||||
end
|
end
|
||||||
|
|
||||||
# Emit an upstart event if we can
|
# Emit an upstart event if we can
|
||||||
machine.communicate.sudo <<-SCRIPT
|
machine.communicate.sudo <<-EOH.gsub(/^ {12}/, "")
|
||||||
if command -v /sbin/init && /sbin/init --version | grep upstart; then
|
if command -v /sbin/init && /sbin/init --version | grep upstart; then
|
||||||
/sbin/initctl emit --no-wait vagrant-mounted MOUNTPOINT='#{expanded_guest_path}'
|
/sbin/initctl emit --no-wait vagrant-mounted MOUNTPOINT=#{guest_path}
|
||||||
fi
|
fi
|
||||||
SCRIPT
|
EOH
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.unmount_virtualbox_shared_folder(machine, guestpath, options)
|
def self.unmount_virtualbox_shared_folder(machine, guestpath, options)
|
||||||
result = machine.communicate.sudo(
|
guest_path = Shellwords.escape(guestpath)
|
||||||
"umount #{guestpath}", error_check: false)
|
|
||||||
|
result = machine.communicate.sudo("umount #{guest_path}", error_check: false)
|
||||||
if result == 0
|
if result == 0
|
||||||
machine.communicate.sudo("rmdir #{guestpath}", error_check: false)
|
machine.communicate.sudo("rmdir #{guest_path}", error_check: false)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1342,6 +1342,19 @@ en:
|
||||||
VirtualBox is complaining that the installation is incomplete. Please
|
VirtualBox is complaining that the installation is incomplete. Please
|
||||||
run `VBoxManage --version` to see the error message which should contain
|
run `VBoxManage --version` to see the error message which should contain
|
||||||
instructions on how to fix this error.
|
instructions on how to fix this error.
|
||||||
|
virtualbox_mount_failed: |-
|
||||||
|
Vagrant was unable to mount VirtualBox shared folders. This is usually
|
||||||
|
because the filesystem "vboxsf" is not available. This filesystem is
|
||||||
|
made available via the VirtualBox Guest Additions and kernel module.
|
||||||
|
Please verify that these guest additions are properly installed in the
|
||||||
|
guest. This is not a bug in Vagrant and is usually caused by a faulty
|
||||||
|
Vagrant box. For context, the command attemped was:
|
||||||
|
|
||||||
|
%{command}
|
||||||
|
|
||||||
|
The error output from the command was:
|
||||||
|
|
||||||
|
%{output}
|
||||||
virtualbox_name_exists: |-
|
virtualbox_name_exists: |-
|
||||||
The name of your virtual machine couldn't be set because VirtualBox
|
The name of your virtual machine couldn't be set because VirtualBox
|
||||||
is reporting another VM with that name already exists. Most of the
|
is reporting another VM with that name already exists. Most of the
|
||||||
|
|
Loading…
Reference in New Issue