All Files (56.22% covered at 1.11 hits/line)
26 files in total.
731 relevant lines.
411 lines covered and
320 lines missed
- 1
require "pathname"
- 1
module VagrantWindows
- 1
def self.vagrant_windows_root
- 3
@vagrant_windows_root ||= Pathname.new(File.expand_path("../../", __FILE__))
end
- 1
def self.load_script(script_file_name)
File.read(expand_script_path(script_file_name))
end
- 1
def self.load_script_template(script_file_name, options)
- 2
Vagrant::Util::TemplateRenderer.render(expand_script_path(script_file_name), options)
end
- 1
def self.expand_script_path(script_file_name)
- 2
File.expand_path("lib/vagrant-windows/scripts/#{script_file_name}", VagrantWindows.vagrant_windows_root)
end
end
- 1
require "vagrant-windows/plugin"
- 1
require 'log4r'
- 1
require_relative '../../vagrant-windows'
- 1
require_relative '../communication/winrmshell'
- 1
require_relative '../errors'
- 1
module VagrantWindows
- 1
module Communication
# Manages the remote Windows guest network
- 1
class GuestNetwork
- 1
PS_GET_WSMAN_VER = '((test-wsman).productversion.split(" ") | select -last 1).split("\.")[0]'
- 1
WQL_NET_ADAPTERS_V2 = 'SELECT * FROM Win32_NetworkAdapter WHERE MACAddress IS NOT NULL'
- 1
attr_reader :logger
- 1
attr_reader :winrmshell
- 1
def initialize(winrmshell)
@logger = Log4r::Logger.new("vagrant_windows::communication::winrmshell")
@logger.debug("initializing WinRMShell")
@winrmshell = winrmshell
end
# Returns an array of all NICs on the guest. Each array entry is a
# Hash of the NICs properties.
#
# @return [Array]
- 1
def network_adapters()
wsman_version() == 2? network_adapters_v2_winrm() : network_adapters_v3_winrm()
end
# Checks to see if the specified NIC is currently configured for DHCP.
#
# @return [Boolean]
- 1
def is_dhcp_enabled(nic_index)
has_dhcp_enabled = false
cmd = "Get-WmiObject -Class Win32_NetworkAdapterConfiguration -Filter \"Index=#{nic_index} and DHCPEnabled=True\""
@winrmshell.powershell(cmd) do |type, line|
has_dhcp_enabled = !line.nil?
end
@logger.debug("NIC #{nic_index} has DHCP enabled: #{has_dhcp_enabled}")
has_dhcp_enabled
end
# Configures the specified interface for DHCP
#
# @param [Integer] The interface index.
# @param [String] The unique name of the NIC, such as 'Local Area Connection'.
- 1
def configure_dhcp_interface(nic_index, net_connection_id)
@logger.info("Configuring NIC #{net_connection_id} for DHCP")
if !is_dhcp_enabled(nic_index)
netsh = "netsh interface ip set address \"#{net_connection_id}\" dhcp"
@winrmshell.powershell(netsh)
end
end
# Configures the specified interface using a static address
#
# @param [Integer] The interface index.
# @param [String] The unique name of the NIC, such as 'Local Area Connection'.
# @param [String] The static IP address to assign to the specified NIC.
# @param [String] The network mask to use with the static IP.
- 1
def configure_static_interface(nic_index, net_connection_id, ip, netmask)
@logger.info("Configuring NIC #{net_connection_id} using static ip #{ip}")
#netsh interface ip set address "Local Area Connection 2" static 192.168.33.10 255.255.255.0
netsh = "netsh interface ip set address \"#{net_connection_id}\" static #{ip} #{netmask}"
@winrmshell.powershell(netsh)
end
# Sets all networks on the guest to 'Work Network' mode. This is
# to allow guest access from the host via a private IP on Win7
# https://github.com/WinRb/vagrant-windows/issues/63
- 1
def set_all_networks_to_work()
@logger.info("Setting all networks to 'Work Network'")
command = VagrantWindows.load_script("set_work_network.ps1")
@winrmshell.powershell(command)
end
- 1
protected
# Checks the WinRS version on the guest. Usually 2 on Windows 7/2008
# and 3 on Windows 8/2012.
#
# @return [Integer]
- 1
def wsman_version()
@logger.debug("querying WSMan version")
version = ''
@winrmshell.powershell(PS_GET_WSMAN_VER) do |type, line|
version = version + "#{line}" if type == :stdout && !line.nil?
end
@logger.debug("wsman version: #{version}")
Integer(version)
end
# Returns an array of all NICs on the guest. Each array entry is a
# Hash of the NICs properties. This method should only be used on
# guests that have WinRS version 2.
#
# @return [Array]
- 1
def network_adapters_v2_winrm()
@logger.debug("querying network adapters")
# Get all NICs that have a MAC address
# http://msdn.microsoft.com/en-us/library/windows/desktop/aa394216(v=vs.85).aspx
adapters = @winrmshell.wql(WQL_NET_ADAPTERS_V2)[:win32_network_adapter]
@logger.debug("#{adapters.inspect}")
return adapters
end
# Returns an array of all NICs on the guest. Each array entry is a
# Hash of the NICs properties. This method should only be used on
# guests that have WinRS version 3.
#
# This method is a workaround until the WinRM gem supports WinRS version 3.
#
# @return [Array]
- 1
def network_adapters_v3_winrm()
winrs_v3_get_adapters_ps1 = VagrantWindows.load_script("winrs_v3_get_adapters.ps1")
output = ''
@winrmshell.powershell(winrs_v3_get_adapters_ps1) do |type, line|
output = output + "#{line}" if type == :stdout && !line.nil?
end
adapters = []
JSON.parse(output).each do |nic|
adapters << nic.inject({}){ |memo,(k,v)| memo[k.to_sym] = v; memo }
end
@logger.debug("#{adapters.inspect}")
return adapters
end
end #GuestNetwork class
end
end
- 1
require 'timeout'
- 1
require 'log4r'
- 1
require_relative '../errors'
- 1
require_relative 'winrmshell'
- 1
require_relative 'winrmfinder'
- 1
module VagrantWindows
- 1
module Communication
# Provides communication with the VM via WinRM.
- 1
class WinRMCommunicator < Vagrant.plugin("2", :communicator)
- 1
attr_reader :logger
- 1
attr_reader :machine
- 1
attr_reader :winrm_finder
- 1
def self.match?(machine)
machine.config.vm.guest.eql? :windows
end
- 1
def initialize(machine)
@machine = machine
@logger = Log4r::Logger.new("vagrant_windows::communication::winrmcommunicator")
@logger.debug("initializing WinRMCommunicator")
@winrm_finder = WinRMFinder.new(machine)
end
- 1
def ready?
logger.debug("Checking whether WinRM is ready...")
Timeout.timeout(@machine.config.winrm.timeout) do
session.powershell("hostname")
end
logger.info("WinRM is ready!")
return true
rescue Vagrant::Errors::VagrantError => e
# We catch a `VagrantError` which would signal that something went
# wrong expectedly in the `connect`, which means we didn't connect.
@logger.info("WinRM not up: #{e.inspect}")
return false
end
- 1
def execute(command, opts={}, &block)
opts = {
:error_check => true,
:error_class => VagrantWindows::Errors::WinRMExecutionError,
:error_key => :winrm_execution_error,
:command => command,
:shell => :powershell
}.merge(opts || {})
exit_status = do_execute(command, opts[:shell], &block)
if opts[:error_check] && exit_status != 0
raise_execution_error(opts, exit_status)
end
exit_status
end
- 1
alias_method :sudo, :execute
- 1
def test(command, opts=nil)
@logger.debug("Testing: #{command}")
# HACK: to speed up Vagrant 1.2 OS detection, skip checking for *nix OS
return false unless (command =~ /^uname|^cat \/etc|^cat \/proc|grep 'Fedora/).nil?
opts = { :error_check => false }.merge(opts || {})
execute(command, opts) == 0
end
- 1
def upload(from, to)
@logger.debug("Uploading: #{from} to #{to}")
file = "winrm-upload-#{rand()}"
file_name = (session.cmd("echo %TEMP%\\#{file}"))[:data][0][:stdout].chomp
session.powershell <<-EOH
if(Test-Path #{to})
{
rm #{to}
}
EOH
Base64.encode64(IO.binread(from)).gsub("\n",'').chars.to_a.each_slice(8000-file_name.size) do |chunk|
out = session.cmd("echo #{chunk.join} >> \"#{file_name}\"")
end
session.powershell("mkdir $([System.IO.Path]::GetDirectoryName(\"#{to}\"))")
session.powershell <<-EOH
$base64_string = Get-Content \"#{file_name}\"
$bytes = [System.Convert]::FromBase64String($base64_string)
$new_file = [System.IO.Path]::GetFullPath(\"#{to}\")
[System.IO.File]::WriteAllBytes($new_file,$bytes)
EOH
end
- 1
def download(from, to=nil)
@logger.warn("Downloading: #{from} to #{to} not supported on Windows guests")
end
# Runs a remote WQL query against the VM
#
# Note: This is not part of the standard Vagrant communicator interface, but
# guest capabilities may need to use this.
- 1
def wql(query)
session.wql(query)
end
- 1
def set_winrmshell(winrmshell)
@session = winrmshell
end
- 1
def session
@session ||= new_session
end
- 1
alias_method :winrmshell, :session
- 1
protected
- 1
def do_execute(command, shell, &block)
if shell.eql? :cmd
session.cmd(command, &block)[:exitcode]
else
command = VagrantWindows.load_script("command_alias.ps1") << "\r\n" << command
session.powershell(command, &block)[:exitcode]
end
end
- 1
def raise_execution_error(opts, exit_code)
# The error classes expect the translation key to be _key, but that makes for an ugly
# configuration parameter, so we set it here from `error_key`
msg = "Command execution failed with an exit code of #{exit_code}"
error_opts = opts.merge(:_key => opts[:error_key], :message => msg)
raise opts[:error_class], error_opts
end
- 1
def new_session
WinRMShell.new(
@winrm_finder.winrm_host_address(),
@machine.config.winrm.username,
@machine.config.winrm.password,
{
:port => @winrm_finder.winrm_host_port(),
:timeout_in_seconds => @machine.config.winrm.timeout,
:max_tries => @machine.config.winrm.max_tries
})
end
end #WinRM class
end
end
- 1
require 'log4r'
- 1
require_relative '../errors'
- 1
module VagrantWindows
- 1
module Communication
- 1
class WinRMFinder
- 1
attr_reader :logger
- 1
attr_reader :machine
- 1
def initialize(machine)
- 3
@machine = machine
- 3
@logger = Log4r::Logger.new("vagrant_windows::communication::winrmfinder")
end
- 1
def winrm_host_address
# Get the SSH info for the machine, raise an exception if the
# provider is saying that the machine is not ready.
- 3
ssh_info = @machine.ssh_info
- 3
raise VagrantWindows::Errors::WinRMNotReady if ssh_info.nil?
# if the configuration has a host value, that takes precedence
- 2
host = @machine.config.winrm.host || ssh_info[:host]
- 2
@logger.info("WinRM host: #{host}")
- 2
host
end
- 1
def winrm_host_port
expected_guest_port = @machine.config.winrm.guest_port
@logger.debug("Searching for WinRM port: #{expected_guest_port.inspect}")
# Look for the forwarded port only by comparing the guest port
begin
@machine.provider.driver.read_forwarded_ports.each do |_, _, hostport, guestport|
return hostport if guestport == expected_guest_port
end
rescue NoMethodError => e
# VMWare provider doesn't support read_forwarded_ports
@logger.debug(e.message)
end
# We tried, give up and use the configured port as-is
@machine.config.winrm.port
end
end
end
end
- 1
require 'timeout'
- 1
require 'log4r'
- 1
require 'winrm'
- 1
require 'vagrant/util/retryable'
- 1
require_relative '../errors'
- 1
module VagrantWindows
- 1
module Communication
- 1
class WinRMShell
- 1
include Vagrant::Util::Retryable
# These are the exceptions that we retry because they represent
# errors that are generally fixed from a retry and don't
# necessarily represent immediate failure cases.
- 1
@@exceptions_to_retry_on = [
HTTPClient::KeepAliveDisconnected,
WinRM::WinRMHTTPTransportError,
Errno::EACCES,
Errno::EADDRINUSE,
Errno::ECONNREFUSED,
Errno::ECONNRESET,
Errno::ENETUNREACH,
Errno::EHOSTUNREACH,
Timeout::Error
]
- 1
attr_reader :logger
- 1
attr_reader :username
- 1
attr_reader :password
- 1
attr_reader :host
- 1
attr_reader :port
- 1
attr_reader :timeout_in_seconds
- 1
attr_reader :max_tries
- 1
def initialize(host, username, password, options = {})
@logger = Log4r::Logger.new("vagrant_windows::communication::winrmshell")
@logger.debug("initializing WinRMShell")
@host = host
@port = options[:port] || 5985
@username = username
@password = password
@timeout_in_seconds = options[:timeout_in_seconds] || 60
@max_tries = options[:max_tries] || 20
end
- 1
def powershell(command, &block)
execute_shell(command, :powershell, &block)
end
- 1
def cmd(command, &block)
execute_shell(command, :cmd, &block)
end
- 1
def wql(query)
execute_wql(query)
end
- 1
protected
- 1
def execute_shell(command, shell=:powershell, &block)
raise Errors::WinRMInvalidShell, :shell => shell unless shell == :cmd || shell == :powershell
begin
execute_shell_with_retry(command, shell, &block)
rescue => e
raise_winrm_exception(e, shell, command)
end
end
- 1
def execute_shell_with_retry(command, shell, &block)
retryable(:tries => @max_tries, :on => @@exceptions_to_retry_on, :sleep => 10) do
@logger.debug("#{shell} executing:\n#{command}")
output = session.send(shell, command) do |out, err|
block.call(:stdout, out) if block_given? && out
block.call(:stderr, err) if block_given? && err
end
@logger.debug("Exit status: #{output[:exitcode].inspect}")
return output
end
end
- 1
def execute_wql(query)
retryable(:tries => @max_tries, :on => @@exceptions_to_retry_on, :sleep => 10) do
@logger.debug("#executing wql: #{query}")
output = session.wql(query)
@logger.debug("wql result: #{output.inspect}")
return output
end
rescue => e
raise_winrm_exception(e, :wql, query)
end
- 1
def raise_winrm_exception(winrm_exception, shell, command)
if winrm_exception.message.include?("401") # return a more specific auth error for 401 errors
raise Errors::WinRMAuthorizationError,
:user => @username,
:password => @password,
:endpoint => endpoint,
:message => winrm_exception.message
end
raise Errors::WinRMExecutionError,
:shell => shell,
:command => command,
:message => winrm_exception.message
end
- 1
def new_session
@logger.info("Attempting to connect to WinRM...")
@logger.info(" - Host: #{@host}")
@logger.info(" - Port: #{@port}")
@logger.info(" - Username: #{@username}")
client = ::WinRM::WinRMWebService.new(endpoint, :plaintext, endpoint_options)
client.set_timeout(@timeout_in_seconds)
client.toggle_nori_type_casting(:off) #we don't want coersion of types
client
end
- 1
def session
@session ||= new_session
end
- 1
def endpoint
"http://#{@host}:#{@port}/wsman"
end
- 1
def endpoint_options
{ :user => @username,
:pass => @password,
:host => @host,
:port => @port,
:operation_timeout => @timeout_in_seconds,
:basic_auth_only => true }
end
end #WinShell class
end
end
- 1
require "vagrant"
- 1
module VagrantWindows
- 1
module Config
- 1
class Windows < Vagrant.plugin("2", :config)
- 1
attr_accessor :halt_timeout
- 1
attr_accessor :halt_check_interval
- 1
attr_accessor :set_work_network
- 1
def initialize
- 4
@halt_timeout = UNSET_VALUE
- 4
@halt_check_interval = UNSET_VALUE
- 4
@set_work_network = UNSET_VALUE
end
- 1
def validate(machine)
errors = []
errors << "windows.halt_timeout cannot be nil." if machine.config.windows.halt_timeout.nil?
errors << "windows.halt_check_interval cannot be nil." if machine.config.windows.halt_check_interval.nil?
errors << "windows.set_work_network cannot be nil." if machine.config.windows.set_work_network.nil?
{ "Windows Guest" => errors }
end
- 1
def finalize!
- 4
@halt_timeout = 30 if @halt_timeout == UNSET_VALUE
- 4
@halt_check_interval = 1 if @halt_check_interval == UNSET_VALUE
- 4
@set_work_network = false if @set_work_network == UNSET_VALUE
end
end
end
end
- 1
require "vagrant"
- 1
module VagrantWindows
- 1
module Config
- 1
class WinRM < Vagrant.plugin("2", :config)
- 1
attr_accessor :username
- 1
attr_accessor :password
- 1
attr_accessor :host
- 1
attr_accessor :port
- 1
attr_accessor :guest_port
- 1
attr_accessor :max_tries
- 1
attr_accessor :timeout
- 1
def initialize
- 16
@username = UNSET_VALUE
- 16
@password = UNSET_VALUE
- 16
@host = UNSET_VALUE
- 16
@port = UNSET_VALUE
- 16
@guest_port = UNSET_VALUE
- 16
@max_tries = UNSET_VALUE
- 16
@timeout = UNSET_VALUE
end
- 1
def validate(machine)
errors = []
errors << "winrm.username cannot be nil." if machine.config.winrm.username.nil?
errors << "winrm.password cannot be nil." if machine.config.winrm.password.nil?
errors << "winrm.port cannot be nil." if machine.config.winrm.port.nil?
errors << "winrm.guest_port cannot be nil." if machine.config.winrm.guest_port.nil?
errors << "winrm.max_tries cannot be nil." if machine.config.winrm.max_tries.nil?
errors << "winrm.timeout cannot be nil." if machine.config.winrm.timeout.nil?
{ "WinRM" => errors }
end
- 1
def finalize!
- 16
@username = "vagrant" if @username == UNSET_VALUE
- 16
@password = "vagrant" if @password == UNSET_VALUE
- 16
@host = nil if @host == UNSET_VALUE
- 16
@port = 5985 if @port == UNSET_VALUE
- 16
@guest_port = 5985 if @guest_port == UNSET_VALUE
- 16
@max_tries = 20 if @max_tries == UNSET_VALUE
- 16
@timeout = 1800 if @timeout == UNSET_VALUE
end
end
end
end
- 1
require 'vagrant/errors'
- 1
module VagrantWindows
- 1
module Errors
- 1
class VagrantWindowsError < ::Vagrant::Errors::VagrantError
- 1
error_namespace("vagrant_windows.errors")
end
- 1
class WinRMNotReady < VagrantWindowsError
- 1
error_key(:winrm_not_ready)
end
- 1
class WinRMInvalidShell < VagrantWindowsError
- 1
error_key(:winrm_invalid_shell)
end
- 1
class WinRMExecutionError < VagrantWindowsError
- 1
error_key(:winrm_execution_error)
end
- 1
class WinRMAuthorizationError < VagrantWindowsError
- 1
error_key(:winrm_auth_error)
end
end
end
- 1
require_relative '../../helper'
- 1
module VagrantWindows
- 1
module Guest
- 1
module Cap
- 1
class MountSharedFolder
- 1
def self.mount_virtualbox_shared_folder(machine, name, guestpath, options)
- 1
mount_shared_folder(machine, name, guestpath, "\\\\vboxsrv\\")
end
- 1
def self.mount_vmware_shared_folder(machine, name, guestpath, options)
- 1
mount_shared_folder(machine, name, guestpath, "\\\\vmware-host\\Shared Folders\\")
end
- 1
protected
- 1
def self.mount_shared_folder(machine, name, guestpath, vm_provider_unc_base)
- 2
share_name = VagrantWindows::Helper.win_friendly_share_id(name)
- 2
options = {
:mount_point => guestpath,
:share_name => share_name,
:vm_provider_unc_path => vm_provider_unc_base + share_name}
- 2
mount_script = VagrantWindows.load_script_template("mount_volume.ps1", :options => options)
- 2
machine.communicate.execute(mount_script, {:shell => :powershell})
end
end
end
end
end
- 1
module VagrantWindows
- 1
module Helper
- 1
extend self
# Makes a path Windows guest friendly.
# Turns '/vagrant' into 'c:\vagrant'
#
# @return [String]
- 1
def win_friendly_path(path)
- 3
if path
- 2
new_path = path.gsub('/', '\\')
- 2
new_path = "c:#{new_path}" if new_path =~ /^\\/
end
- 3
new_path
end
# Makes Vagrant share names Windows guest friendly.
# Turns '/vagrant' into 'vagrant' or turns ''/a/b/c/d/e' into 'a_b_c_d_e'
#
# @return [String]
- 1
def win_friendly_share_id(shared_folder_name)
- 4
return shared_folder_name.gsub(/[\/\/]/,'_').sub(/^_/, '')
end
# Checks to see if the specified machine is using VMWare Fusion or Workstation.
#
# @return [Boolean]
- 1
def is_vmware(machine)
- 3
machine.provider_name.to_s().start_with?('vmware')
end
end
end
- 1
require_relative '../../../helper'
- 1
require_relative '../../../communication/winrmcommunicator'
- 1
module Vagrant
- 1
class Machine
- 1
ssh_communicate = instance_method(:communicate)
# This patch is needed until Vagrant supports a configurable communication channel
- 1
define_method(:communicate) do
unless @communicator
if @config.vm.guest.eql? :windows
@logger.info("guest is #{@config.vm.guest}, using WinRM for communication channel")
@communicator = ::VagrantWindows::Communication::WinRMCommunicator.new(self)
else
@logger.info("guest is #{@config.vm.guest}, using SSH for communication channel")
@communicator = ssh_communicate.bind(self).()
end
end
@communicator
end
- 1
def winrm
@winrm ||= WinRM.new(self)
end
- 1
def is_windows?
return @config.vm.guest.eql? :windows
end
end
end
- 1
require "#{Vagrant::source_root}/plugins/providers/virtualbox/action/share_folders"
- 1
require_relative '../../../../../helper'
- 1
module VagrantPlugins
- 1
module ProviderVirtualBox
- 1
module Action
- 1
class ShareFolders
- 1
include VagrantWindows::Helper
- 1
alias_method :original_create_metadata, :create_metadata
- 1
def create_metadata
unless @env[:machine].is_windows?
# don't change the shared folder name for linux guests. We don't want the shared folder name of linux guests to be different
# depending on whether the vagrant-windows plugin is installed or not.
original_create_metadata
else
@env[:ui].info I18n.t("vagrant.actions.vm.share_folders.creating")
folders = []
shared_folders.each do |id, data|
hostpath = File.expand_path(data[:hostpath], @env[:root_path])
hostpath = Vagrant::Util::Platform.cygwin_windows_path(hostpath)
folder_name = win_friendly_share_id(id.gsub(/[\/\/]/,'_').sub(/^_/, ''))
folders << {
:name => folder_name,
:hostpath => hostpath,
:transient => data[:transient]
}
end
@env[:machine].provider.driver.share_folders(folders)
end
end
end
end
end
end
- 1
module VagrantPlugins
- 1
module ProviderVirtualBox
- 1
module Driver
- 1
class Version_4_2
- 1
def read_mac_addresses
macs = {}
info = execute("showvminfo", @uuid, "--machinereadable", :retryable => true)
info.split("\n").each do |line|
if matcher = /^macaddress(\d+)="(.+?)"$/.match(line)
adapter = matcher[1].to_i
mac = matcher[2].to_s
macs[adapter] = mac
end
end
macs
end
end
end
end
end
# TODO: support Chef client
- 1
require 'tempfile'
- 1
require "#{Vagrant::source_root}/plugins/provisioners/chef/provisioner/chef_solo"
- 1
require_relative '../../../../../helper'
- 1
module VagrantPlugins
- 1
module Chef
- 1
module Provisioner
- 1
class ChefSolo < Base
- 1
include VagrantWindows::Helper
- 1
provision_on_linux = instance_method(:provision)
- 1
run_chef_solo_on_linux = instance_method(:run_chef_solo)
# This patch is needed until Vagrant supports chef on Windows guests
- 1
define_method(:run_chef_solo) do
is_windows ? run_chef_solo_on_windows() : run_chef_solo_on_linux.bind(self).()
end
- 1
define_method(:provision) do
wait_if_rebooting() if is_windows
provision_on_linux.bind(self).()
end
- 1
def wait_if_rebooting
# Check to see if the guest is rebooting, if its rebooting then wait until its ready
@logger.info('Checking guest reboot status')
reboot_detect_script = VagrantWindows.load_script('reboot_detect.ps1')
exit_status = @machine.communicate.execute(reboot_detect_script, :error_check => false)
if exit_status != 0
begin
@logger.debug('Guest is rebooting, waiting 10 seconds...')
sleep(10)
end until @machine.communicate.ready?
end
end
- 1
def run_chef_solo_on_windows
# This re-establishes our symbolic links if they were created between now and a reboot
# Fixes issue #119
@machine.communicate.execute('& net use a-non-existant-share', :error_check => false)
# create cheftaskrun.ps1 that the scheduled task will invoke when run
render_file_and_upload("cheftaskrun.ps1", chef_script_options[:chef_task_run_ps1], :options => chef_script_options)
# create cheftask.xml that the scheduled task will be created with
render_file_and_upload("cheftask.xml", chef_script_options[:chef_task_xml], :options => chef_script_options)
# create cheftask.ps1 that will immediately invoke the scheduled task and wait for completion
render_file_and_upload("cheftask.ps1", chef_script_options[:chef_task_ps1], :options => chef_script_options)
command = <<-EOH
$old = Get-ExecutionPolicy;
Set-ExecutionPolicy Unrestricted -force;
#{chef_script_options[:chef_task_ps1]};
Set-ExecutionPolicy $old -force
exit $LASTEXITCODE
EOH
@config.attempts.times do |attempt|
if attempt == 0
@machine.env.ui.info I18n.t("vagrant.provisioners.chef.running_solo")
else
@machine.env.ui.info I18n.t("vagrant.provisioners.chef.running_solo_again")
end
exit_status = @machine.communicate.execute(command, :error_check => false) do |type, data|
# Output the data with the proper color based on the stream.
color = type == :stdout ? :green : :red
@machine.env.ui.info(
data, :color => color, :new_line => false, :prefix => false)
end
# There is no need to run Chef again if it converges
return if exit_status == 0
end
# If we reached this point then Chef never converged! Error.
raise ChefError, :no_convergence
end
- 1
def render_file_and_upload(script_name, dest_file, options)
script_contents = VagrantWindows.load_script_template(script_name, options)
# render cheftaskrun.ps1 to local temp file
script_local = Tempfile.new(script_name)
IO.write(script_local, script_contents)
# upload cheftaskrun.ps1 file
@machine.communicate.upload(script_local, dest_file)
end
- 1
def chef_script_options
if @chef_script_options.nil?
command_env = @config.binary_env ? "#{@config.binary_env} " : ""
command_args = @config.arguments ? " #{@config.arguments}" : ""
chef_solo_path = win_friendly_path(File.join(@config.provisioning_path, 'solo.rb'))
chef_dna_path = win_friendly_path(File.join(@config.provisioning_path, 'dna.json'))
chef_arguments = "-c #{chef_solo_path} "
chef_arguments << "-j #{chef_dna_path} "
chef_arguments << "#{command_args}"
@chef_script_options = {
:user => @machine.config.winrm.username,
:pass => @machine.config.winrm.password,
:chef_arguments => chef_arguments,
:chef_task_xml => win_friendly_path("#{@config.provisioning_path}/cheftask.xml"),
:chef_task_running => win_friendly_path("#{@config.provisioning_path}/cheftask.running"),
:chef_task_exitcode => win_friendly_path("#{@config.provisioning_path}/cheftask.exitcode"),
:chef_task_ps1 => win_friendly_path("#{@config.provisioning_path}/cheftask.ps1"),
:chef_task_run_ps1 => win_friendly_path("#{@config.provisioning_path}/cheftaskrun.ps1"),
:chef_stdout_log => win_friendly_path("#{@config.provisioning_path}/chef-solo.log"),
:chef_stderr_log => win_friendly_path("#{@config.provisioning_path}/chef-solo.err.log"),
:chef_binary_path => win_friendly_path("#{command_env}#{chef_binary_path("chef-solo")}")
}
end
@chef_script_options
end
- 1
def is_windows
@machine.config.vm.guest.eql? :windows
end
end # ChefSolo class
end
end
end
- 1
require "#{Vagrant::source_root}/plugins/provisioners/puppet/provisioner/puppet"
- 1
module VagrantPlugins
- 1
module Puppet
- 1
module Provisioner
- 1
class Puppet < Vagrant.plugin("2", :provisioner)
# This patch is needed until Vagrant supports Puppet on Windows guests
- 1
run_puppet_apply_on_linux = instance_method(:run_puppet_apply)
- 1
configure_on_linux = instance_method(:configure)
- 1
define_method(:run_puppet_apply) do
is_windows ? run_puppet_apply_on_windows() : run_puppet_apply_on_linux.bind(self).()
end
- 1
define_method(:configure) do |root_config|
is_windows ? configure_on_windows(root_config) : configure_on_linux.bind(self).(root_config)
end
- 1
def run_puppet_apply_on_windows
# This re-establishes our symbolic links if they were created between now and a reboot
@machine.communicate.execute('& net use a-non-existant-share', :error_check => false)
options = [config.options].flatten
module_paths = @module_paths.map { |_, to| to }
if !@module_paths.empty?
# Prepend the default module path
module_paths.unshift("/ProgramData/PuppetLabs/puppet/etc/modules")
# Add the command line switch to add the module path
options << "--modulepath '#{module_paths.join(';')}'"
end
if @hiera_config_path
options << "--hiera_config=#{@hiera_config_path}"
end
if !@machine.env.ui.is_a?(Vagrant::UI::Colored)
options << "--color=false"
end
options << "--manifestdir #{manifests_guest_path}"
options << "--detailed-exitcodes"
options << @manifest_file
options = options.join(" ")
# Build up the custom facts if we have any
facter = ""
if !config.facter.empty?
facts = []
config.facter.each do |key, value|
facts << "$env:FACTER_#{key}='#{value}';"
end
facter = "#{facts.join(" ")} "
end
command = "#{facter} puppet apply #{options}"
if config.working_directory
command = "cd #{config.working_directory}; if($?) \{ #{command} \}"
end
@machine.env.ui.info I18n.t("vagrant.provisioners.puppet.running_puppet",
:manifest => @manifest_file)
@machine.communicate.sudo(command) do |type, data|
if !data.empty?
@machine.env.ui.info(data, :new_line => false, :prefix => false)
end
end
end
- 1
def configure_on_windows(root_config)
# Calculate the paths we're going to use based on the environment
root_path = @machine.env.root_path
@expanded_manifests_path = @config.expanded_manifests_path(root_path)
@expanded_module_paths = @config.expanded_module_paths(root_path)
@manifest_file = File.join(manifests_guest_path, @config.manifest_file)
# Setup the module paths
@module_paths = []
@expanded_module_paths.each_with_index do |path, i|
@module_paths << [path, File.join(config.temp_dir, "modules-#{i}")]
end
@logger.debug("Syncing folders from puppet configure")
@logger.debug("manifests_guest_path = #{manifests_guest_path}")
@logger.debug("expanded_manifests_path = #{@expanded_manifests_path}")
# Windows guest volume mounting fails without an "id" specified
# This hacks around that problem and allows the PS mount script to work
root_config.vm.synced_folder(
@expanded_manifests_path, manifests_guest_path,
:id => "v-manifests-1")
# Share the manifests directory with the guest
#root_config.vm.synced_folder(
# @expanded_manifests_path, manifests_guest_path)
# Share the module paths
count = 0
@module_paths.each do |from, to|
# Sorry for the cryptic key here, but VirtualBox has a strange limit on
# maximum size for it and its something small (around 10)
root_config.vm.synced_folder(from, to, :id => "v-modules-#{count}")
count += 1
end
end
- 1
def is_windows
@machine.config.vm.guest.eql? :windows
end
end # Puppet class
end
end
end
#TODO: Support Puppet server
- 1
require "#{Vagrant::source_root}/plugins/provisioners/shell/provisioner"
- 1
require_relative '../../../../helper'
- 1
module VagrantPlugins
- 1
module Shell
- 1
class Provisioner < Vagrant.plugin("2", :provisioner)
- 1
include VagrantWindows::Helper
# This patch is needed until Vagrant supports Puppet on Windows guests
- 1
provision_on_linux = instance_method(:provision)
- 1
define_method(:provision) do
is_windows ? provision_on_windows() : provision_on_linux.bind(self).()
end
- 1
def provision_on_windows
args = ""
args = " #{config.args}" if config.args
with_script_file do |path|
# Upload the script to the machine
@machine.communicate.tap do |comm|
# Ensure the uploaded script has a file extension, by default
# config.upload_path from vagrant core does not
fixed_upload_path = if File.extname(config.upload_path) == ""
"#{config.upload_path}#{File.extname(path.to_s)}"
else
config.upload_path
end
comm.upload(path.to_s, fixed_upload_path)
command = <<-EOH
$old = Get-ExecutionPolicy;
Set-ExecutionPolicy Unrestricted -force;
#{win_friendly_path(fixed_upload_path)}#{args};
Set-ExecutionPolicy $old -force
EOH
# Execute it with sudo
comm.sudo(command) do |type, data|
if [:stderr, :stdout].include?(type)
# Output the data with the proper color based on the stream.
color = type == :stdout ? :green : :red
@machine.ui.info(
data,
:color => color, :new_line => false, :prefix => false)
end
end
end
end
end
- 1
protected
# This method yields the path to a script to upload and execute
# on the remote server. This method will properly clean up the
# script file if needed.
- 1
def with_script_file
if config.path
# Just yield the path to that file...
yield config.path
else
# Otherwise we have an inline script, we need to Tempfile it,
# and handle it specially...
file = Tempfile.new(['vagrant-powershell', '.ps1'])
begin
file.write(config.inline)
file.fsync
file.close
yield file.path
ensure
file.close
file.unlink
end
end
end
- 1
def is_windows
@machine.config.vm.guest.eql? :windows
end
end # Provisioner class
end
end
- 1
begin
- 1
require "vagrant"
rescue LoadError
raise "The Vagrant Windows plugin must be run within Vagrant."
end
# This is a sanity check to make sure no one is attempting to install
# this into an early Vagrant version.
- 1
if Vagrant::VERSION < "1.1.0"
raise "The Vagrant Windows plugin is only compatible with Vagrant 1.1+"
end
- 1
if Vagrant::VERSION >= "1.2.0"
# Monkey Patch the virtualbox share_folders action to make valid share names on windows
- 1
require_relative "monkey_patches/plugins/providers/virtualbox/action/share_folders"
end
# Monkey patch the vbox42 driver to support read mac addresses
- 1
require_relative "monkey_patches/plugins/providers/virtualbox/driver/version_4_2"
# Monkey Patch the VM object to support multiple channels, i.e. WinRM
- 1
require_relative "monkey_patches/lib/vagrant/machine"
# Monkey patch the Puppet provisioners to support PowerShell/Windows
- 1
require_relative "monkey_patches/plugins/provisioners/puppet/provisioner/puppet"
- 1
require_relative "monkey_patches/plugins/provisioners/puppet/provisioner/puppet_server"
# Monkey patch the Chef provisioners to support PowerShell/Windows
- 1
require_relative "monkey_patches/plugins/provisioners/chef/provisioner/chef_solo"
- 1
require_relative "monkey_patches/plugins/provisioners/chef/provisioner/chef_client"
# Monkey patch the shell provisioner to support PowerShell/batch/exe/Windows/etc
- 1
require_relative "monkey_patches/plugins/provisioners/shell/provisioner"
- 1
module VagrantWindows
- 1
class Plugin < Vagrant.plugin("2")
- 1
name "Windows guest"
description <<-DESC
This plugin installs a provider that allows Vagrant to manage
Windows machines as guests.
- 1
DESC
- 1
config(:windows) do
require_relative "config/windows"
VagrantWindows::Config::Windows
end
- 1
config(:winrm) do
require_relative "config/winrm"
VagrantWindows::Config::WinRM
end
- 1
guest(:windows) do
require_relative "guest/windows"
VagrantWindows::Guest::Windows
end
# Vagrant 1.2 introduced the concept of capabilities instead of implementing
# an interface on the guest.
- 1
if Vagrant::VERSION >= "1.2.0"
- 1
guest_capability(:windows, :change_host_name) do
require_relative "guest/cap/change_host_name"
VagrantWindows::Guest::Cap::ChangeHostName
end
- 1
guest_capability(:windows, :configure_networks) do
require_relative "guest/cap/configure_networks"
VagrantWindows::Guest::Cap::ConfigureNetworks
end
- 1
guest_capability(:windows, :halt) do
require_relative "guest/cap/halt"
VagrantWindows::Guest::Cap::Halt
end
- 1
guest_capability(:windows, :mount_virtualbox_shared_folder) do
require_relative "guest/cap/mount_shared_folder"
VagrantWindows::Guest::Cap::MountSharedFolder
end
- 1
guest_capability(:windows, :mount_vmware_shared_folder) do
require_relative "guest/cap/mount_shared_folder"
VagrantWindows::Guest::Cap::MountSharedFolder
end
end
# This initializes the internationalization strings.
- 1
def self.setup_i18n
- 1
I18n.load_path << File.expand_path("locales/en.yml", VagrantWindows.vagrant_windows_root)
- 1
I18n.reload!
end
# This sets up our log level to be whatever VAGRANT_LOG is.
- 1
def self.setup_logging
- 1
require "log4r"
- 1
level = nil
- 1
begin
- 1
level = Log4r.const_get(ENV["VAGRANT_LOG"].upcase)
rescue NameError
# This means that the logging constant wasn't found,
# which is fine. We just keep `level` as `nil`. But
# we tell the user.
- 1
level = nil
end
# Some constants, such as "true" resolve to booleans, so the
# above error checking doesn't catch it. This will check to make
# sure that the log level is an integer, as Log4r requires.
- 1
level = nil if !level.is_a?(Integer)
# Set the logging level on all "vagrant" namespaced
# logs as long as we have a valid level.
- 1
if level
logger = Log4r::Logger.new("vagrant_windows")
logger.outputters = Log4r::Outputter.stderr
logger.level = level
logger = nil
end
end
end
end
- 1
VagrantWindows::Plugin.setup_logging()
- 1
VagrantWindows::Plugin.setup_i18n()
- 1
require 'spec_helper'
- 1
require 'mocha/api'
- 1
describe VagrantWindows::Helper , :unit => true do
- 1
class DummyHelper
- 1
include VagrantWindows::Helper
end
- 1
before(:all) do
- 1
@dummy = DummyHelper.new
end
- 1
describe "win_friendly_path" do
- 1
it "should replace slashes with backslashes" do
- 1
@dummy.win_friendly_path('c:/tmp/dir').should eq('c:\\tmp\\dir')
end
- 1
it "should prepend c: drive if not drive specified" do
- 1
@dummy.win_friendly_path('/tmp/dir').should eq('c:\\tmp\\dir')
end
- 1
it "should return nil if no path specified" do
- 1
@dummy.win_friendly_path(nil).should be_nil
end
end
- 1
describe "win_friendly_share_id" do
- 1
it "should use share id if present" do
- 1
@dummy.win_friendly_share_id('sharename').should eq('sharename')
end
- 1
it "should use last folder name in guest_path" do
- 1
@dummy.win_friendly_share_id('/tmp/folder/sharename').should eq('tmp_folder_sharename')
end
end
- 1
describe "is_vmware" do
- 1
it "should be true for vmware_fusion" do
- 1
machine = stub(:provider_name => :vmware_fusion)
- 1
expect(@dummy.is_vmware(machine)).to be_true
end
- 1
it "should be true for vmware_workstation" do
- 1
machine = stub(:provider_name => :vmware_workstation)
- 1
expect(@dummy.is_vmware(machine)).to be_true
end
- 1
it "should be false for virtual_box" do
- 1
machine = stub(:provider_name => :virtual_box)
- 1
expect(@dummy.is_vmware(machine)).to be_false
end
end
end
- 1
require 'spec_helper'
- 1
require 'mocha/api'
- 1
require 'vagrant-windows/guest/cap/mount_shared_folder'
- 1
describe VagrantWindows::Guest::Cap::MountSharedFolder, :unit => true do
- 1
before(:each) do
- 2
@communicator = mock()
- 2
@machine = stub(:communicate => @communicator)
end
- 1
describe "mount_virtualbox_shared_folder" do
- 1
it "should run script with vbox paths" do
- 1
@communicator.expects(:execute).with do |script, options|
- 1
expect(script).to include("$VmProviderUncPath = \"\\\\vboxsrv\\vagrant\"")
end
- 1
VagrantWindows::Guest::Cap::MountSharedFolder.mount_virtualbox_shared_folder(
@machine, "vagrant", "/tmp/vagrant", {})
end
end
- 1
describe "mount_vmware_shared_folder" do
- 1
it "should run script with vmware paths" do
- 1
@communicator.expects(:execute).with do |script, options|
- 1
expect(script).to include("$VmProviderUncPath = \"\\\\vmware-host\\Shared Folders\\vagrant\"")
end
- 1
VagrantWindows::Guest::Cap::MountSharedFolder.mount_vmware_shared_folder(
@machine, "vagrant", "/tmp/vagrant", {})
end
end
end
- 1
require 'spec_helper'
- 1
describe VagrantWindows::Config::Windows , :unit => true do
- 5
let(:instance) { described_class.new }
- 1
describe "defaults" do
- 1
subject do
- 2
instance.tap do |o|
- 2
o.finalize!
end
end
- 2
its("halt_timeout") { should == 30 }
- 2
its("halt_check_interval") { should == 1 }
end
- 1
describe "overriding defaults" do
- 1
[:halt_timeout, :halt_check_interval].each do |attribute|
- 2
it "should not default #{attribute} if overridden" do
- 2
instance.send("#{attribute}=".to_sym, 10)
- 2
instance.finalize!
- 2
instance.send(attribute).should == 10
end
end
end
end
- 1
require 'spec_helper'
- 1
describe VagrantWindows::Config::WinRM, :unit => true do
- 15
let(:instance) { described_class.new }
- 1
describe "defaults" do
- 1
subject do
- 7
instance.tap do |o|
- 7
o.finalize!
end
end
- 2
its("username") { should == "vagrant" }
- 2
its("password") { should == "vagrant" }
- 2
its("host") { should == nil }
- 2
its("port") { should == 5985 }
- 2
its("guest_port") { should == 5985 }
- 2
its("max_tries") { should == 20 }
- 2
its("timeout") { should == 1800 }
end
- 1
describe "overriding defaults" do
- 1
[:username, :password, :host, :port, :guest_port, :max_tries, :timeout].each do |attribute|
- 7
it "should not default #{attribute} if overridden" do
- 7
instance.send("#{attribute}=".to_sym, 10)
- 7
instance.finalize!
- 7
instance.send(attribute).should == 10
end
end
end
end
- 1
require 'spec_helper'
- 1
describe VagrantWindows::Communication::WinRMCommunicator, :integration => true do
- 1
before(:all) do
# This test requires you already have a running Windows Server 2008 R2 Vagrant VM
# Not ideal, but you have to start somewhere
@shell = VagrantWindows::Communication::WinRMShell.new("localhost", "vagrant", "vagrant")
@communicator = VagrantWindows::Communication::WinRMCommunicator.new({})
@communicator.set_winrmshell(@shell)
end
- 1
describe "execute" do
- 1
it "should return 1 when error_check is false" do
expect(@communicator.execute("exit 1", { :error_check => false })).to eq(1)
end
- 1
it "should raise WinRMExecutionError when error_check is true" do
expect { @communicator.execute("exit 1") }.to raise_error(VagrantWindows::Errors::WinRMExecutionError)
end
- 1
it "should raise specified error type when specified and error_check is true" do
opts = { :error_class => VagrantWindows::Errors::WinRMInvalidShell }
expect { @communicator.execute("exit 1", opts) }.to raise_error(VagrantWindows::Errors::WinRMInvalidShell)
end
end
end
- 1
require 'spec_helper'
- 1
describe VagrantWindows::Communication::WinRMFinder, :unit => true do
- 1
before(:each) do
- 3
@machine = stub()
- 3
@winrmfinder = VagrantWindows::Communication::WinRMFinder.new(@machine)
end
- 1
describe 'winrm_host_address' do
- 1
it 'should raise WinRMNotReady exception when ssh_info is nil' do
- 1
@machine.stubs(:ssh_info).returns(nil)
- 2
expect { @winrmfinder.winrm_host_address() }.to raise_error(VagrantWindows::Errors::WinRMNotReady)
end
- 1
it 'should return ssh_info host if config host has no value' do
# setup the winrm config to return nil for the host (i.e. the default)
- 1
winrm_config = VagrantWindows::Config::WinRM.new()
- 1
winrm_config.finalize!()
- 1
machine_config = stub(:winrm => winrm_config)
- 1
@machine.stubs(:config).returns(machine_config)
# setup the machine ssh_info to return a 10.0.0.1
- 1
@machine.stubs(:ssh_info).returns({ :host => '10.0.0.1' })
- 1
expect(@winrmfinder.winrm_host_address()).to eq('10.0.0.1')
end
- 1
it 'should return host config if set (issue 104)' do
# setup the winrm config to return nil for the host (i.e. the default)
- 1
winrm_config = VagrantWindows::Config::WinRM.new()
- 1
winrm_config.host = '10.0.0.1'
- 1
winrm_config.finalize!()
- 1
machine_config = stub(:winrm => winrm_config)
- 1
@machine.stubs(:config).returns(machine_config)
# setup the machine ssh_info to return a 10.0.0.1
- 1
@machine.stubs(:ssh_info).returns({ :host => '127.0.0.1' })
- 1
expect(@winrmfinder.winrm_host_address()).to eq('10.0.0.1')
end
end
end
- 1
require 'spec_helper'
- 1
describe VagrantWindows::Communication::WinRMShell, :integration => true do
- 1
before(:all) do
# This test requires you already have a running Windows Server 2008 R2 Vagrant VM
# Not ideal, but you have to start somewhere
@shell = VagrantWindows::Communication::WinRMShell.new("localhost", "vagrant", "vagrant")
end
- 1
describe "powershell" do
- 1
it "should return exit code of 0" do
expect(@shell.powershell("exit 0")[:exitcode]).to eq(0)
end
- 1
it "should return exit code greater than 0" do
expect(@shell.powershell("exit 1")[:exitcode]).to eq(1)
end
- 1
it "should return stdout" do
result = @shell.powershell("dir") do |type, line|
expect(type).to eq(:stdout)
expect(line.length).to be > 1
end
expect(result[:exitcode]).to eq(0)
end
end
- 1
describe "cmd" do
- 1
it "should return stdout" do
result = @shell.cmd("dir") do |type, line|
expect(type).to eq(:stdout)
expect(line.length).to be > 1
end
expect(result[:exitcode]).to eq(0)
end
end
end