Jeff Bonhag df7c11a3a7 Fix issues with Windows SSH provisioner
Windows commands that run over SSH are wrapped in a script that writes a
special marker to the two output streams (stdout and stderr).  This
allows Vagrant to consume the output streams.

Unfortunately, this leads to a sort of chicken-and-egg problem where no
commands can be run before a wrapper script exists. For example, you
can't make a destination directory to upload the wrapper script without
first creating a wrapper script to make the directory. :)

This commit changes the behavior of the WinSSH communicator to assume
that the destination directory already exists for provisioning scripts.

It also moves the default `upload_path` from the shell provisioner
config so we can have OS-specific defaults.

Finally, it introduces a Windows-specific #upload method which will
properly use a Windows path separator on a non-Windows host.
2020-03-04 15:08:03 -08:00

144 lines
5.0 KiB
Ruby

require 'uri'
module VagrantPlugins
module Shell
class Config < Vagrant.plugin("2", :config)
attr_accessor :inline
attr_accessor :path
attr_accessor :md5
attr_accessor :sha1
attr_accessor :sha256
attr_accessor :sha384
attr_accessor :sha512
attr_accessor :env
attr_accessor :upload_path
attr_accessor :args
attr_accessor :privileged
attr_accessor :binary
attr_accessor :keep_color
attr_accessor :name
attr_accessor :sensitive
attr_accessor :powershell_args
attr_accessor :powershell_elevated_interactive
attr_accessor :reboot
attr_accessor :reset
def initialize
@args = UNSET_VALUE
@inline = UNSET_VALUE
@path = UNSET_VALUE
@md5 = UNSET_VALUE
@sha1 = UNSET_VALUE
@sha256 = UNSET_VALUE
@sha384 = UNSET_VALUE
@sha512 = UNSET_VALUE
@env = UNSET_VALUE
@upload_path = UNSET_VALUE
@privileged = UNSET_VALUE
@binary = UNSET_VALUE
@keep_color = UNSET_VALUE
@name = UNSET_VALUE
@sensitive = UNSET_VALUE
@reboot = UNSET_VALUE
@reset = UNSET_VALUE
@powershell_args = UNSET_VALUE
@powershell_elevated_interactive = UNSET_VALUE
end
def finalize!
@args = nil if @args == UNSET_VALUE
@inline = nil if @inline == UNSET_VALUE
@path = nil if @path == UNSET_VALUE
@md5 = nil if @md5 == UNSET_VALUE
@sha1 = nil if @sha1 == UNSET_VALUE
@sha256 = nil if @sha256 == UNSET_VALUE
@sha384 = nil if @sha384 == UNSET_VALUE
@sha512 = nil if @sha512 == UNSET_VALUE
@env = {} if @env == UNSET_VALUE
@upload_path = nil if @upload_path == UNSET_VALUE
@privileged = true if @privileged == UNSET_VALUE
@binary = false if @binary == UNSET_VALUE
@keep_color = false if @keep_color == UNSET_VALUE
@name = nil if @name == UNSET_VALUE
@sensitive = false if @sensitive == UNSET_VALUE
@reboot = false if @reboot == UNSET_VALUE
@reset = false if @reset == UNSET_VALUE
@powershell_args = "-ExecutionPolicy Bypass" if @powershell_args == UNSET_VALUE
@powershell_elevated_interactive = false if @powershell_elevated_interactive == UNSET_VALUE
if @args && args_valid?
@args = @args.is_a?(Array) ? @args.map { |a| a.to_s } : @args.to_s
end
if @sensitive
@env.each do |_, v|
Vagrant::Util::CredentialScrubber.sensitive(v)
end
end
end
def validate(machine)
errors = _detected_errors
# Validate that the parameters are properly set
if path && inline
errors << I18n.t("vagrant.provisioners.shell.path_and_inline_set")
elsif !path && !inline && !reset && !reboot
errors << I18n.t("vagrant.provisioners.shell.no_path_or_inline")
end
# If it is not an URL, we validate the existence of a script to upload
if path && !remote?
expanded_path = Pathname.new(path).expand_path(machine.env.root_path)
if !expanded_path.file?
errors << I18n.t("vagrant.provisioners.shell.path_invalid",
path: expanded_path)
else
data = expanded_path.read(16)
if data && !data.valid_encoding?
errors << I18n.t(
"vagrant.provisioners.shell.invalid_encoding",
actual: data.encoding.to_s,
default: Encoding.default_external.to_s,
path: expanded_path.to_s)
end
end
end
if !env.is_a?(Hash)
errors << I18n.t("vagrant.provisioners.shell.env_must_be_a_hash")
end
if !args_valid?
errors << I18n.t("vagrant.provisioners.shell.args_bad_type")
end
if powershell_elevated_interactive && !privileged
errors << I18n.t("vagrant.provisioners.shell.interactive_not_elevated")
end
{ "shell provisioner" => errors }
end
# Args are optional, but if they're provided we only support them as a
# string or as an array.
def args_valid?
return true if !args
return true if args.is_a?(String)
return true if args.is_a?(Integer)
if args.is_a?(Array)
args.each do |a|
return false if !a.kind_of?(String) && !a.kind_of?(Integer)
end
return true
end
end
def remote?
path =~ URI.regexp(["ftp", "http", "https"])
end
end
end
end