Merge pull request #9943 from chrisroberts/e-wsl-hyperv

Support Hyper-V provider within WSL
This commit is contained in:
Chris Roberts 2018-06-18 15:29:01 -07:00 committed by GitHub
commit 0134a235e7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 69 additions and 27 deletions

View File

@ -116,7 +116,7 @@ module Vagrant
# Extract the box into a temporary directory. # Extract the box into a temporary directory.
@logger.debug("Unpacking box into temporary directory: #{temp_dir}") @logger.debug("Unpacking box into temporary directory: #{temp_dir}")
result = Util::Subprocess.execute( result = Util::Subprocess.execute(
"bsdtar", "-v", "-x", "-m", "-C", temp_dir.to_s, "-f", path.to_s) "bsdtar", "-v", "-x", "-m", "-s", "|\\\\\|/|", "-C", temp_dir.to_s, "-f", path.to_s)
if result.exit_code != 0 if result.exit_code != 0
raise Errors::BoxUnpackageFailure, raise Errors::BoxUnpackageFailure,
output: result.stderr.to_s output: result.stderr.to_s

View File

@ -393,6 +393,7 @@ module Vagrant
# @return [String] # @return [String]
def wsl_to_windows_path(path) def wsl_to_windows_path(path)
if wsl? && wsl_windows_access? && !path.match(/^[a-zA-Z]:/) if wsl? && wsl_windows_access? && !path.match(/^[a-zA-Z]:/)
path = File.expand_path(path.to_s)
if wsl_path?(path) if wsl_path?(path)
parts = path.split("/") parts = path.split("/")
parts.delete_if(&:empty?) parts.delete_if(&:empty?)
@ -402,17 +403,15 @@ module Vagrant
if root_path.end_with?("lxss") && !(["root", "home"].include?(parts.first)) if root_path.end_with?("lxss") && !(["root", "home"].include?(parts.first))
root_path = "#{root_path}\\rootfs" root_path = "#{root_path}\\rootfs"
end end
[root_path, *parts].join("\\") path = [root_path, *parts].join("\\")
else else
path = path.to_s.sub("/mnt/", "") path = path.to_s.sub("/mnt/", "")
parts = path.split("/") parts = path.split("/")
parts.first << ":" parts.first << ":"
path = parts.join("\\") path = parts.join("\\")
path
end end
else
path
end end
path.to_s
end end
# Takes a windows path and formats it to the # Takes a windows path and formats it to the

View File

@ -27,7 +27,11 @@ module Vagrant
@_powershell_executable += ".exe" @_powershell_executable += ".exe"
if Which.which(@_powershell_executable).nil? if Which.which(@_powershell_executable).nil?
@_powershell_executable = nil @_powershell_executable = "/mnt/c/Windows/System32/WindowsPowerShell/v1.0/powershell.exe"
if Which.which(@_powershell_executable).nil?
@_powershell_executable = nil
end
end end
else else
@_powershell_executable = nil @_powershell_executable = nil

View File

@ -9,6 +9,14 @@ module VagrantPlugins
def call(env) def call(env)
env[:ui].info("Deleting the machine...") env[:ui].info("Deleting the machine...")
env[:machine].provider.driver.delete_vm env[:machine].provider.driver.delete_vm
# NOTE: We remove the data directory and recreate it
# to overcome an issue seen when running within
# the WSL. Hyper-V will successfully remove the
# VM and the files will appear to be gone, but
# on a subsequent up, they will cause collisions.
# This forces them to be gone for real.
FileUtils.rm_rf(env[:machine].data_dir)
FileUtils.mkdir_p(env[:machine].data_dir)
@app.call(env) @app.call(env)
end end
end end

View File

@ -23,7 +23,8 @@ module VagrantPlugins
def export def export
@env[:ui].info I18n.t("vagrant.actions.vm.export.exporting") @env[:ui].info I18n.t("vagrant.actions.vm.export.exporting")
@env[:machine].provider.driver.export(@env["export.temp_dir"]) do |progress| export_tmp_dir = Vagrant::Util::Platform.wsl_to_windows_path(@env["export.temp_dir"])
@env[:machine].provider.driver.export(export_tmp_dir) do |progress|
@env[:ui].clear_line @env[:ui].clear_line
@env[:ui].report_progress(progress.percent, 100, false) @env[:ui].report_progress(progress.percent, 100, false)
end end

View File

@ -61,11 +61,11 @@ module VagrantPlugins
dest_path = env[:machine].data_dir.join("Virtual Hard Disks").join(image_path.basename).to_s dest_path = env[:machine].data_dir.join("Virtual Hard Disks").join(image_path.basename).to_s
options = { options = {
"VMConfigFile" => config_path.to_s.gsub("/", "\\"), "VMConfigFile" => Vagrant::Util::Platform.wsl_to_windows_path(config_path).gsub("/", "\\"),
"DestinationPath" => dest_path.to_s.gsub("/", "\\"), "DestinationPath" => Vagrant::Util::Platform.wsl_to_windows_path(dest_path).gsub("/", "\\"),
"DataPath" => env[:machine].data_dir.to_s.gsub("/", "\\"), "DataPath" => Vagrant::Util::Platform.wsl_to_windows_path(env[:machine].data_dir).gsub("/", "\\"),
"LinkedClone" => !!env[:machine].provider_config.linked_clone, "LinkedClone" => !!env[:machine].provider_config.linked_clone,
"SourcePath" => image_path.to_s.gsub("/", "\\"), "SourcePath" => Vagrant::Util::Platform.wsl_to_windows_path(image_path).gsub("/", "\\"),
"VMName" => env[:machine].provider_config.vmname, "VMName" => env[:machine].provider_config.vmname,
} }

View File

@ -221,8 +221,8 @@ module VagrantPlugins
def execute_powershell(path, options, &block) def execute_powershell(path, options, &block)
lib_path = Pathname.new(File.expand_path("../scripts", __FILE__)) lib_path = Pathname.new(File.expand_path("../scripts", __FILE__))
mod_path = lib_path.join("utils").to_s.gsub("/", "\\") mod_path = Vagrant::Util::Platform.wsl_to_windows_path(lib_path.join("utils")).to_s.gsub("/", "\\")
path = lib_path.join(path).to_s.gsub("/", "\\") path = Vagrant::Util::Platform.wsl_to_windows_path(lib_path.join(path)).to_s.gsub("/", "\\")
options = options || {} options = options || {}
ps_options = [] ps_options = []
options.each do |key, value| options.each do |key, value|
@ -239,8 +239,9 @@ module VagrantPlugins
# Include our module path so we can nicely load helper modules # Include our module path so we can nicely load helper modules
opts = { opts = {
notify: [:stdout, :stderr, :stdin], notify: [:stdout, :stderr, :stdin],
env: {"PSModulePath" => "$env:PSModulePath+';#{mod_path}'"} module_path: mod_path
} }
Vagrant::Util::PowerShell.execute(path, *ps_options, **opts, &block) Vagrant::Util::PowerShell.execute(path, *ps_options, **opts, &block)
end end
end end

View File

@ -12,11 +12,12 @@ module VagrantPlugins
attr_reader :driver attr_reader :driver
def self.usable?(raise_error=false) def self.usable?(raise_error=false)
if !Vagrant::Util::Platform.windows? if !Vagrant::Util::Platform.windows? &&
!Vagrant::Util::Platform.wsl?
raise Errors::WindowsRequired raise Errors::WindowsRequired
end end
if !Vagrant::Util::Platform.windows_admin? and if !Vagrant::Util::Platform.windows_admin? &&
!Vagrant::Util::Platform.windows_hyperv_admin? !Vagrant::Util::Platform.windows_hyperv_admin?
raise Errors::AdminRequired raise Errors::AdminRequired
end end

View File

@ -1,3 +1,6 @@
# Always stop when errors are encountered unless instructed not to
$ErrorActionPreference = "Stop"
# Vagrant VM creation functions # Vagrant VM creation functions
function New-VagrantVM { function New-VagrantVM {
@ -123,7 +126,7 @@ function New-VagrantVMVMCX {
$Path = $Drive.Path $Path = $Drive.Path
Hyper-V\Remove-VMHardDiskDrive $Drive Hyper-V\Remove-VMHardDiskDrive $Drive
Hyper-V\New-VHD -Path $DestinationPath -ParentPath $SourcePath Hyper-V\New-VHD -Path $DestinationPath -ParentPath $SourcePath
Hyper-V\AddVMHardDiskDrive -VM $VM -Path $DestinationPath Hyper-V\Add-VMHardDiskDrive -VM $VM -Path $DestinationPath
break break
} }
} }

View File

@ -1,10 +1,5 @@
en: en:
vagrant_sf_smb: vagrant_sf_smb:
not_supported: |-
It appears your machine doesn't support SMB, has not been
properly configured for SMB, or there is not an adapter to
enable SMB on this machine for Vagrant. Ensure SMB host
functionality is available on this machine and try again.
mounting: |- mounting: |-
Mounting SMB shared folders... Mounting SMB shared folders...
mounting_single: |- mounting_single: |-
@ -26,6 +21,11 @@ en:
Vagrant requires administrator access to create SMB shares and Vagrant requires administrator access to create SMB shares and
may request access to complete setup of configured shares. may request access to complete setup of configured shares.
errors: errors:
not_supported: |-
It appears your machine doesn't support SMB, has not been
properly configured for SMB, or there is not an adapter to
enable SMB on this machine for Vagrant. Ensure SMB host
functionality is available on this machine and try again.
start_failed: |- start_failed: |-
Vagrant failed to automatically start the SMB service. Ensure the Vagrant failed to automatically start the SMB service. Ensure the
required services can be started and try again. required services can be started and try again.

View File

@ -8,13 +8,15 @@ describe VagrantPlugins::HyperV::Action::DeleteVM do
let(:ui){ double("ui") } let(:ui){ double("ui") }
let(:provider){ double("provider", driver: driver) } let(:provider){ double("provider", driver: driver) }
let(:driver){ double("driver") } let(:driver){ double("driver") }
let(:machine){ double("machine", provider: provider) } let(:machine){ double("machine", provider: provider, data_dir: "/dev/null") }
let(:subject){ described_class.new(app, env) } let(:subject){ described_class.new(app, env) }
before do before do
allow(app).to receive(:call) allow(app).to receive(:call)
allow(ui).to receive(:info) allow(ui).to receive(:info)
allow(driver).to receive(:delete_vm) allow(driver).to receive(:delete_vm)
allow(FileUtils).to receive(:rm_rf)
allow(FileUtils).to receive(:mkdir_p)
end end
it "should call the app on success" do it "should call the app on success" do
@ -26,4 +28,14 @@ describe VagrantPlugins::HyperV::Action::DeleteVM do
expect(driver).to receive(:delete_vm) expect(driver).to receive(:delete_vm)
subject.call(env) subject.call(env)
end end
it "should delete the data directory" do
expect(FileUtils).to receive(:rm_rf).with(machine.data_dir)
subject.call(env)
end
it "should recreate the data directory" do
expect(FileUtils).to receive(:mkdir_p).with(machine.data_dir)
subject.call(env)
end
end end

View File

@ -144,8 +144,7 @@ describe VagrantPlugins::HyperV::Driver do
it "should automatically include module path" do it "should automatically include module path" do
expect(Vagrant::Util::PowerShell).to receive(:execute) do |path, *args| expect(Vagrant::Util::PowerShell).to receive(:execute) do |path, *args|
opts = args.detect{|i| i.is_a?(Hash)} opts = args.detect{|i| i.is_a?(Hash)}
expect(opts[:env]).not_to be_nil expect(opts[:module_path]).not_to be_nil
expect(opts[:env]["PSModulePath"]).to include("$env:PSModulePath+")
end end
subject.send(:execute_powershell, "path", {}) subject.send(:execute_powershell, "path", {})
end end

View File

@ -14,6 +14,7 @@ describe VagrantPlugins::HyperV::Provider do
stub_const("Vagrant::Util::PowerShell", powershell) stub_const("Vagrant::Util::PowerShell", powershell)
allow(machine).to receive(:id).and_return("foo") allow(machine).to receive(:id).and_return("foo")
allow(platform).to receive(:windows?).and_return(true) allow(platform).to receive(:windows?).and_return(true)
allow(platform).to receive(:wsl?).and_return(false)
allow(platform).to receive(:windows_admin?).and_return(true) allow(platform).to receive(:windows_admin?).and_return(true)
allow(platform).to receive(:windows_hyperv_admin?).and_return(true) allow(platform).to receive(:windows_hyperv_admin?).and_return(true)
allow(powershell).to receive(:available?).and_return(true) allow(powershell).to receive(:available?).and_return(true)
@ -27,6 +28,12 @@ describe VagrantPlugins::HyperV::Provider do
expect(subject).to_not be_usable expect(subject).to_not be_usable
end end
it "returns true if within WSL" do
expect(platform).to receive(:windows?).and_return(false)
expect(platform).to receive(:wsl?).and_return(true)
expect(subject).to be_usable
end
it "returns false if neither an admin nor a hyper-v admin" do it "returns false if neither an admin nor a hyper-v admin" do
allow(platform).to receive(:windows_admin?).and_return(false) allow(platform).to receive(:windows_admin?).and_return(false)
allow(platform).to receive(:windows_hyperv_admin?).and_return(false) allow(platform).to receive(:windows_hyperv_admin?).and_return(false)

View File

@ -63,7 +63,10 @@ describe Vagrant::Util::PowerShell do
end end
context "when within WSL" do context "when within WSL" do
before{ expect(Vagrant::Util::Platform).to receive(:wsl?).and_return(true) } before do
allow(Vagrant::Util::Which).to receive(:which).with(/powershell/).and_return(nil)
expect(Vagrant::Util::Platform).to receive(:wsl?).and_return(true)
end
it "should check PATH with .exe extension" do it "should check PATH with .exe extension" do
expect(Vagrant::Util::Which).to receive(:which).with("powershell.exe") expect(Vagrant::Util::Which).to receive(:which).with("powershell.exe")
@ -76,9 +79,13 @@ describe Vagrant::Util::PowerShell do
end end
it "should return nil when not found" do it "should return nil when not found" do
expect(Vagrant::Util::Which).to receive(:which).with("powershell.exe").and_return(nil)
expect(described_class.executable).to be_nil expect(described_class.executable).to be_nil
end end
it "should check for powershell with full path" do
expect(Vagrant::Util::Which).to receive(:which).with(/Windows\/System32.+powershell.exe/)
described_class.executable
end
end end
end end
end end