From 83b0c87f52d7be7edfc73b9e65a8c841c32d705b Mon Sep 17 00:00:00 2001 From: Chris Roberts Date: Thu, 4 May 2017 19:02:15 -0700 Subject: [PATCH] Support running Vagrant within the Windows Subsystem for Linux --- bin/vagrant | 7 ++ lib/vagrant/errors.rb | 4 + lib/vagrant/util/platform.rb | 87 +++++++++++++++++++++ lib/vagrant/util/ssh.rb | 2 +- plugins/providers/virtualbox/driver/base.rb | 6 ++ plugins/providers/virtualbox/provider.rb | 14 ++-- templates/locales/en.yml | 10 +++ 7 files changed, 124 insertions(+), 6 deletions(-) diff --git a/bin/vagrant b/bin/vagrant index 080b7a7c8..42c239f40 100755 --- a/bin/vagrant +++ b/bin/vagrant @@ -112,6 +112,13 @@ begin argv += argv_extra end + # If we are running with the Windows Subsystem for Linux do + # some extra setup to allow access to Vagrant managed machines + # outside the subsystem + if Vagrant::Util::Platform.wsl? + Vagrant::Util::Platform.wsl_init(logger) + end + # Create the environment, which is the cwd of wherever the # `vagrant` command was invoked from logger.debug("Creating Vagrant environment") diff --git a/lib/vagrant/errors.rb b/lib/vagrant/errors.rb index 94bbe6371..6fb07803b 100644 --- a/lib/vagrant/errors.rb +++ b/lib/vagrant/errors.rb @@ -780,6 +780,10 @@ module Vagrant error_key(:vboxmanage_not_found_error) end + class VBoxManageNotFoundWSLError < VagrantError + error_key(:vboxmanage_not_found_wsl_error) + end + class VirtualBoxBrokenVersion040214 < VagrantError error_key(:virtualbox_broken_version_040214) end diff --git a/lib/vagrant/util/platform.rb b/lib/vagrant/util/platform.rb index 09dcbbc16..24b315987 100644 --- a/lib/vagrant/util/platform.rb +++ b/lib/vagrant/util/platform.rb @@ -28,6 +28,27 @@ module Vagrant return @_cygwin end + def wsl? + if !defined?(@_wsl) + @_wsl = false + original_verbose = $VERBOSE + begin + $VERBOSE = nil + # Use PATH values to check for `/mnt/c` path indicative of WSL + if ENV.fetch("PATH", "").downcase.include?("/mnt/c") + # Validate WSL via uname output + uname = Subprocess.execute("uname", "-r") + if uname.exit_code == 0 && uname.stdout.downcase.include?("microsoft") + @_wsl = true + end + end + ensure + $VERBOSE = original_verbose + end + end + @_wsl + end + [:darwin, :bsd, :freebsd, :linux, :solaris].each do |type| define_method("#{type}?") do platform.include?(type.to_s) @@ -223,6 +244,72 @@ module Vagrant return @_platform end + # Determine if given path is within the WSL rootfs. Returns + # true if within the subsystem, or false if outside the subsystem. + # + # @param [String] path Path to check + # @return [Boolean] path is within subsystem + def wsl_path?(path) + wsl? && !path.to_s.downcase.start_with?("/mnt/") + end + + # Allow Vagrant to access Vagrant managed machines outside the + # Windows Subsystem for Linux + # + # @return [Boolean] + def wsl_windows_access? + if !defined?(@_wsl_windows_access) + @_wsl_windows_access = wsl? && ENV["VAGRANT_WSL_ACCESS_WINDOWS_USER"] + end + @_wsl_windows_access + end + + # The allowed windows system path Vagrant can manage from the Windows + # Subsystem for Linux + # + # @return [Pathname] + def wsl_windows_accessible_path + if !defined?(@_wsl_windows_accessible_path) + access_path = ENV.fetch("VAGRANT_WSL_ACCESS_WINDOWS_USER_HOME_PATH", + "/mnt/c/Users/#{ENV["VAGRANT_WSL_ACCESS_WINDOWS_USER"]}") + @_wsl_windows_accessible_path = Pathname.new(access_path) + end + @_wsl_windows_accessible_path + end + + # Checks given path to determine if Vagrant is allowed to bypass checks + # + # @param [String] path Path to check + # @return [Boolean] Vagrant is allowed to bypass checks + def wsl_windows_access_bypass?(path) + wsl? && wsl_windows_access? && + path.to_s.start_with?(wsl_windows_accessible_path.to_s) + end + + # If running within the Windows Subsystem for Linux, this will provide + # simple setup to allow sharing of the user's VAGRANT_HOME directory + # within the subsystem + # + # @param [Logger] logger Optional logger to display information + def wsl_init(logger=nil) + if wsl? && ENV["VAGRANT_WSL_ACCESS_WINDOWS_USER"] + shared_user = ENV["VAGRANT_WSL_ACCESS_WINDOWS_USER"] + if logger + logger.warn("Windows Subsystem for Linux detected. Allowing access to user: #{shared_user}") + logger.warn("Vagrant will be allowed to control Vagrant managed machines within the user's home path.") + end + if ENV["VAGRANT_HOME"] || ENV["VAGRANT_WSL_DISABLE_VAGRANT_HOME"] + logger.warn("VAGRANT_HOME environment variable already set. Not overriding!") if logger + else + home_path = wsl_windows_accessible_path + ENV["VAGRANT_HOME"] = File.join(home_path, ".vagrant.d") + if logger + logger.info("Overriding VAGRANT_HOME environment variable to configured windows user. (#{ENV["VAGRANT_HOME"]})") + end + end + end + end + # @private # Reset the cached values for platform. This is not considered a public # API and should only be used for testing. diff --git a/lib/vagrant/util/ssh.rb b/lib/vagrant/util/ssh.rb index faa965c26..77cc1e911 100644 --- a/lib/vagrant/util/ssh.rb +++ b/lib/vagrant/util/ssh.rb @@ -28,7 +28,7 @@ module Vagrant def self.check_key_permissions(key_path) # Don't do anything if we're on Windows, since Windows doesn't worry # about key permissions. - return if Platform.windows? + return if Platform.windows? || Platform.wsl_windows_access_bypass?(key_path) LOGGER.debug("Checking key permissions: #{key_path}") stat = key_path.stat diff --git a/plugins/providers/virtualbox/driver/base.rb b/plugins/providers/virtualbox/driver/base.rb index c304d3efb..12c01eebe 100644 --- a/plugins/providers/virtualbox/driver/base.rb +++ b/plugins/providers/virtualbox/driver/base.rb @@ -66,6 +66,12 @@ module VagrantPlugins break end end + elsif Vagrant::Util::Platform.wsl? + @logger.debug("Linux platform detected but executing within WSL. Locating VBoxManage.") + @vboxmanage_path = Vagrant::Util::Which.which("VBoxManage") || Vagrant::Util::Which.which("VBoxManage.exe") + if !@vboxmanage_path + raise Vagrant::Errors::VBoxManageNotFoundWSLError + end end # Fall back to hoping for the PATH to work out diff --git a/plugins/providers/virtualbox/provider.rb b/plugins/providers/virtualbox/provider.rb index 6bde344d2..12170936a 100644 --- a/plugins/providers/virtualbox/provider.rb +++ b/plugins/providers/virtualbox/provider.rb @@ -84,11 +84,15 @@ module VagrantPlugins def state # We have to check if the UID matches to avoid issues with # VirtualBox. - uid = @machine.uid - if uid && uid.to_s != Process.uid.to_s - raise Vagrant::Errors::VirtualBoxUserMismatch, - original_uid: uid.to_s, - uid: Process.uid.to_s + if Vagrant::Util::Platform.wsl_windows_access_bypass?(@machine.data_dir) + @logger.warn("Skipping UID check on machine by user request for WSL Windows access.") + else + uid = @machine.uid + if uid && uid.to_s != Process.uid.to_s + raise Vagrant::Errors::VirtualBoxUserMismatch, + original_uid: uid.to_s, + uid: Process.uid.to_s + end end # Determine the ID of the state here. diff --git a/templates/locales/en.yml b/templates/locales/en.yml index 2bb312910..00ba09371 100644 --- a/templates/locales/en.yml +++ b/templates/locales/en.yml @@ -1381,6 +1381,16 @@ en: log out and log back in for the new environmental variables to take effect. If you're on Linux or Mac, verify your PATH contains the folder that has VBoxManage in it. + vboxmanage_not_found_wsl_error: |- + The "VBoxManage.exe" command or one of its dependencies could not + be found. Please verify VirtualBox is properly installed. You can verify + everything is okay by running "VBoxManage.exe --version" and verifying + that the VirtualBox version is outputted. + + If you just installed VirtualBox, you have to log out and log back in for + the new environmental variables to take effect. Using the VirtualBox + provider within the WSL requires VirtualBox executables to be available + on the system PATH. virtualbox_broken_version_040214: |- Vagrant detected you have VirtualBox 4.2.14 installed. VirtualBox 4.2.14 contains a critical bug which prevents it from working with