From ad5bc23088f0e9a34f9edacc38d9ab0ce8aeb950 Mon Sep 17 00:00:00 2001 From: Chris Roberts Date: Wed, 26 Jul 2017 09:08:51 -0700 Subject: [PATCH] Validate powershell prior to powershell use Adds powershell validation to ensure powershell is available on the PATH and checks powershell version to ensure meets the defined minimum powershell version. --- lib/vagrant/errors.rb | 8 +++++ lib/vagrant/util/powershell.rb | 55 ++++++++++++++++++++++++++-------- templates/locales/en.yml | 11 +++++++ 3 files changed, 61 insertions(+), 13 deletions(-) diff --git a/lib/vagrant/errors.rb b/lib/vagrant/errors.rb index 42b732c42..ab05f27ab 100644 --- a/lib/vagrant/errors.rb +++ b/lib/vagrant/errors.rb @@ -536,6 +536,14 @@ module Vagrant error_key(:requires_directory, "vagrant.actions.general.package") end + class PowerShellNotFound < VagrantError + error_key(:powershell_not_found) + end + + class PowerShellInvalidVersion < VagrantError + error_key(:powershell_invalid_version) + end + class ProviderCantInstall < VagrantError error_key(:provider_cant_install) end diff --git a/lib/vagrant/util/powershell.rb b/lib/vagrant/util/powershell.rb index ce65c6631..b5d474cec 100644 --- a/lib/vagrant/util/powershell.rb +++ b/lib/vagrant/util/powershell.rb @@ -8,8 +8,15 @@ module Vagrant # This is primarily a convenience wrapper around Subprocess that # properly sets powershell flags for you. class PowerShell + # NOTE: Version checks are only on Major + MINIMUM_REQUIRED_VERSION = 3 + + # @return [Boolean] powershell executable available on PATH def self.available? - !!Which.which("powershell") + if !defined?(@_powershell_available) + @_powershell_available = !!Which.which("powershell") + end + @_powershell_available end # Execute a powershell script. @@ -17,6 +24,7 @@ module Vagrant # @param [String] path Path to the PowerShell script to execute. # @return [Subprocess::Result] def self.execute(path, *args, **opts, &block) + validate_install! command = [ "powershell", "-NoLogo", @@ -39,6 +47,7 @@ module Vagrant # @param [String] command PowerShell command to execute. # @return [Subprocess::Result] def self.execute_cmd(command) + validate_install! c = [ "powershell", "-NoLogo", @@ -58,19 +67,39 @@ module Vagrant # # @return [String] def self.version - command = [ - "powershell", - "-NoLogo", - "-NoProfile", - "-NonInteractive", - "-ExecutionPolicy", "Bypass", - "-Command", - "$PSVersionTable.PSVersion.Major" - ].flatten + if !defined?(@_powershell_version) + command = [ + "powershell", + "-NoLogo", + "-NoProfile", + "-NonInteractive", + "-ExecutionPolicy", "Bypass", + "-Command", + "$PSVersionTable.PSVersion.Major" + ].flatten - r = Subprocess.execute(*command) - return nil if r.exit_code != 0 - return r.stdout.chomp + r = Subprocess.execute(*command) + @_powershell_version = r.exit_code != 0 ? nil : r.stdout.chomp + end + @_powershell_version + end + + # Validates that powershell is installed, available, and + # at or above minimum required version + # + # @return [Boolean] + # @raises [] + def self.validate_install! + if !defined?(@_powershell_validation) + raise Errors::PowerShellNotFound if !available? + if version.to_i < MINIMUM_REQUIRED_VERSION + raise Errors::PowerShellInvalidVersion, + minimum_version: MINIMUM_REQUIRED_VERSION, + installed_version: version ? version : "N/A" + end + @_powershell_validation = true + end + @_powershell_validation end end end diff --git a/templates/locales/en.yml b/templates/locales/en.yml index 6513577b7..26e2eaff6 100644 --- a/templates/locales/en.yml +++ b/templates/locales/en.yml @@ -1051,6 +1051,17 @@ en: %{error_msg} Source: %{source} + powershell_not_found: |- + Failed to locate the powershell executable on the available PATH. Please + ensure powershell is installed and available on the local PATH, then + run the command again. + powershell_invalid_version: |- + The version of powershell currently installed on this host is less than + the required minimum version. Please upgrade the installed version of + powershell to the minimum required version and run the command again. + + Installed version: %{installed_version} + Minimum required version: %{minimum_version} pushes_not_defined: |- The Vagrantfile does not define any 'push' strategies. In order to use `vagrant push`, you must define at least one push strategy: