From 3cf6c99c04e8fa76a73d959344cd5c70bfa7c6c2 Mon Sep 17 00:00:00 2001 From: Jeff Mendoza Date: Fri, 11 Apr 2014 15:56:10 -0700 Subject: [PATCH 01/27] Synced Folders, Wait for SSH. --- lib/vagrant-azure/action.rb | 12 +- lib/vagrant-azure/action/connect_azure.rb | 8 +- lib/vagrant-azure/action/sync_folders.rb | 118 ++++++++++++++++++ .../action/wait_for_communicate.rb | 38 ++++++ locales/en.yml | 9 ++ 5 files changed, 177 insertions(+), 8 deletions(-) create mode 100644 lib/vagrant-azure/action/sync_folders.rb create mode 100644 lib/vagrant-azure/action/wait_for_communicate.rb diff --git a/lib/vagrant-azure/action.rb b/lib/vagrant-azure/action.rb index 74cdb65..f4f6c2c 100644 --- a/lib/vagrant-azure/action.rb +++ b/lib/vagrant-azure/action.rb @@ -67,7 +67,7 @@ module VagrantPlugins end b2.use Provision - # b2.use SyncFolders + b2.use SyncFolders end end end @@ -159,8 +159,7 @@ module VagrantPlugins def self.action_prepare_boot Vagrant::Action::Builder.new.tap do |b| b.use Provision - # b.use SyncFolders - # b.use WarnNetworks + b.use SyncFolders end end @@ -170,6 +169,7 @@ module VagrantPlugins b.use HandleBoxUrl b.use ConfigValidate b.use ConnectAzure + b.use Call, IsState, :NotCreated do |env1, b1| if !env1[:result] b1.use Call, IsState, :StoppedDeallocated do |env2, b2| @@ -182,6 +182,7 @@ module VagrantPlugins b3.use Message, I18n.t( 'vagrant_azure.vm_started', :name => $` ) + b3.use WaitForCommunicate end end else @@ -191,6 +192,7 @@ module VagrantPlugins end end else + b1.use action_prepare_boot b1.use RunInstance # Launch a new instance b1.use Call, WaitForState, :ReadyRole do |env2, b2| if env2[:result] @@ -198,6 +200,7 @@ module VagrantPlugins b2.use Message, I18n.t( 'vagrant_azure.vm_started', :name => $` ) + b2.use WaitForCommunicate end end end @@ -240,11 +243,12 @@ module VagrantPlugins autoload :RunInstance, action_root.join('run_instance') autoload :StartInstance, action_root.join('start_instance') autoload :StopInstance, action_root.join('stop_instance') - # autoload :SyncFolders, action_root.join('sync_folders') + autoload :SyncFolders, action_root.join('sync_folders') autoload :TerminateInstance, action_root.join('terminate_instance') # autoload :TimedProvision, action_root.join('timed_provision') # autoload :WarnNetworks, action_root.join('warn_networks') autoload :WaitForState, action_root.join('wait_for_state') + autoload :WaitForCommunicate, action_root.join('wait_for_communicate') end end end diff --git a/lib/vagrant-azure/action/connect_azure.rb b/lib/vagrant-azure/action/connect_azure.rb index a1ea1a1..b551900 100644 --- a/lib/vagrant-azure/action/connect_azure.rb +++ b/lib/vagrant-azure/action/connect_azure.rb @@ -17,10 +17,10 @@ module VagrantPlugins def call (env) config = env[:machine].provider_config - env[:ui].warn "Subscription ID: [#{config.subscription_id}]" - env[:ui].warn "Mangement Certificate: [#{config.mgmt_certificate}]" - env[:ui].warn "Mangement Endpoint: [#{config.mgmt_endpoint}]" - env[:ui].warn "Storage Account Name: [#{config.storage_acct_name}]" + env[:ui].info "Subscription ID: [#{config.subscription_id}]" + env[:ui].info "Mangement Certificate: [#{config.mgmt_certificate}]" + env[:ui].info "Mangement Endpoint: [#{config.mgmt_endpoint}]" + env[:ui].info "Storage Account Name: [#{config.storage_acct_name}]" Azure.configure do |c| c.subscription_id = config.subscription_id diff --git a/lib/vagrant-azure/action/sync_folders.rb b/lib/vagrant-azure/action/sync_folders.rb new file mode 100644 index 0000000..4236042 --- /dev/null +++ b/lib/vagrant-azure/action/sync_folders.rb @@ -0,0 +1,118 @@ +# Copyright (c) 2014 Mitchell Hashimoto +# Under The MIT License (MIT) +#--------------------------------------------------------------------------- +# Copyright (c) Microsoft Open Technologies, Inc. +# All Rights Reserved. Licensed under the Apache 2.0 License. +#-------------------------------------------------------------------------- +require "log4r" +require "vagrant/util/subprocess" +require "vagrant/util/scoped_hash_override" +require "vagrant/util/which" + +module VagrantPlugins + module WinAzure + module Action + # This middleware uses `rsync` to sync the folders + class SyncFolders + include Vagrant::Util::ScopedHashOverride + + def initialize(app, env) + @app = app + @logger = Log4r::Logger.new("vagrant_azure::action::sync_folders") + end + + def call(env) + puts 'entered sync folders, call app' + @app.call(env) + puts 'called app' + + ssh_info = env[:machine].ssh_info + + unless Vagrant::Util::Which.which('rsync') + env[:ui].warn(I18n.t('vagrant_azure.rsync_not_found_warning', :side => "host")) + return + end + + puts 'running which on target' + if env[:machine].communicate.execute('which rsync', :error_check => false) != 0 + env[:ui].warn(I18n.t('vagrant_azure.rsync_not_found_warning', :side => "guest")) + return + end + puts 'done running which on target' + + env[:machine].config.vm.synced_folders.each do |id, data| + data = scoped_hash_override(data, :azure) + + # Ignore disabled shared folders + next if data[:disabled] + + hostpath = File.expand_path(data[:hostpath], env[:root_path]) + guestpath = data[:guestpath] + + # Make sure there is a trailing slash on the host path to + # avoid creating an additional directory with rsync + hostpath = "#{hostpath}/" if hostpath !~ /\/$/ + + # on windows rsync.exe requires cygdrive-style paths + if Vagrant::Util::Platform.windows? + hostpath = hostpath.gsub(/^(\w):/) { "/cygdrive/#{$1}" } + end + + env[:ui].info(I18n.t("vagrant_azure.rsync_folder", + :hostpath => hostpath, + :guestpath => guestpath)) + + # Create the host path if it doesn't exist and option flag is set + if data[:create] + begin + FileUtils::mkdir_p(hostpath) + rescue => err + raise Errors::MkdirError, + :hostpath => hostpath, + :err => err + end + end + + # Create the guest path + env[:machine].communicate.sudo("mkdir -p '#{guestpath}'") + env[:machine].communicate.sudo( + "chown -R #{ssh_info[:username]} '#{guestpath}'") + + #collect rsync excludes specified :rsync_excludes=>['path1',...] in synced_folder options + excludes = ['.vagrant/', 'Vagrantfile', *Array(data[:rsync_excludes])].uniq + + # Rsync over to the guest path using the SSH info + command = [ + "rsync", "--verbose", "--archive", "-z", + *excludes.map{|e|['--exclude', e]}.flatten, + "-e", "ssh -p #{ssh_info[:port]} -o StrictHostKeyChecking=no #{ssh_key_options(ssh_info)}", + hostpath, + "#{ssh_info[:username]}@#{ssh_info[:host]}:#{guestpath}"] + + # we need to fix permissions when using rsync.exe on windows, see + # http://stackoverflow.com/questions/5798807/rsync-permission-denied-created-directories-have-no-permissions + if Vagrant::Util::Platform.windows? + command.insert(1, "--chmod", "ugo=rwX") + end + + puts 'executing rsync' + r = Vagrant::Util::Subprocess.execute(*command) + if r.exit_code != 0 + raise Errors::RsyncError, + :guestpath => guestpath, + :hostpath => hostpath, + :stderr => r.stderr + end + end + end + + private + + def ssh_key_options(ssh_info) + # Ensure that `private_key_path` is an Array (for Vagrant < 1.4) + Array(ssh_info[:private_key_path]).map { |path| "-i '#{path}' " }.join + end + end + end + end +end diff --git a/lib/vagrant-azure/action/wait_for_communicate.rb b/lib/vagrant-azure/action/wait_for_communicate.rb new file mode 100644 index 0000000..8cb06bc --- /dev/null +++ b/lib/vagrant-azure/action/wait_for_communicate.rb @@ -0,0 +1,38 @@ +#-------------------------------------------------------------------------- +# Copyright (c) Microsoft Open Technologies, Inc. +# All Rights Reserved. Licensed under the Apache 2.0 License. +#-------------------------------------------------------------------------- +require 'log4r' +require 'timeout' + +module VagrantPlugins + module WinAzure + module Action + class WaitForCommunicate + def initialize(app, env) + @app = app + @logger = Log4r::Logger.new("vagrant_azure::action::wait_for_communicate") + end + + def call(env) + + if !env[:interrupted] + # Wait for SSH to be ready. + env[:ui].info(I18n.t("vagrant_azure.waiting_for_ssh")) + while true + # If we're interrupted then just back out + break if env[:interrupted] + break if env[:machine].communicate.ready? + sleep 5 + end + + # Ready and booted! + env[:ui].info(I18n.t("vagrant_azure.ssh_ready")) + end + + @app.call(env) + end + end + end + end +end diff --git a/locales/en.yml b/locales/en.yml index 9cc10b9..9b3cd11 100644 --- a/locales/en.yml +++ b/locales/en.yml @@ -14,3 +14,12 @@ en: VM '%{name}' has been started vm_stopped: |- VM '%{name}' has been stopped + rsync_folder: |- + Rsyncing folder: %{hostpath} => %{guestpath} + rsync_not_found_warning: |- + Warning! Folder sync disabled because the rsync binary is missing in the %{side}. + Make sure rsync is installed and the binary can be found in the PATH. + waiting_for_ssh: |- + Waiting for SSH + ssh_ready: |- + SSH Ready From 5ddab35add6d9af6ecba64147c8d850e47620704 Mon Sep 17 00:00:00 2001 From: Jeff Mendoza Date: Sat, 12 Apr 2014 00:32:20 -0700 Subject: [PATCH 02/27] Remove debug. Fix HandleBoxUrl. Remove some verbosity. Bump version to .pre. --- lib/vagrant-azure/action.rb | 2 +- lib/vagrant-azure/action/connect_azure.rb | 5 ----- lib/vagrant-azure/action/sync_folders.rb | 5 ----- lib/vagrant-azure/version.rb | 2 +- 4 files changed, 2 insertions(+), 12 deletions(-) diff --git a/lib/vagrant-azure/action.rb b/lib/vagrant-azure/action.rb index f4f6c2c..cdedf08 100644 --- a/lib/vagrant-azure/action.rb +++ b/lib/vagrant-azure/action.rb @@ -166,7 +166,7 @@ module VagrantPlugins # This action is called to bring the box up from nothing def self.action_up Vagrant::Action::Builder.new.tap do |b| - b.use HandleBoxUrl + b.use HandleBox b.use ConfigValidate b.use ConnectAzure diff --git a/lib/vagrant-azure/action/connect_azure.rb b/lib/vagrant-azure/action/connect_azure.rb index b551900..644290a 100644 --- a/lib/vagrant-azure/action/connect_azure.rb +++ b/lib/vagrant-azure/action/connect_azure.rb @@ -17,11 +17,6 @@ module VagrantPlugins def call (env) config = env[:machine].provider_config - env[:ui].info "Subscription ID: [#{config.subscription_id}]" - env[:ui].info "Mangement Certificate: [#{config.mgmt_certificate}]" - env[:ui].info "Mangement Endpoint: [#{config.mgmt_endpoint}]" - env[:ui].info "Storage Account Name: [#{config.storage_acct_name}]" - Azure.configure do |c| c.subscription_id = config.subscription_id c.management_certificate = config.mgmt_certificate diff --git a/lib/vagrant-azure/action/sync_folders.rb b/lib/vagrant-azure/action/sync_folders.rb index 4236042..9fa3f57 100644 --- a/lib/vagrant-azure/action/sync_folders.rb +++ b/lib/vagrant-azure/action/sync_folders.rb @@ -22,9 +22,7 @@ module VagrantPlugins end def call(env) - puts 'entered sync folders, call app' @app.call(env) - puts 'called app' ssh_info = env[:machine].ssh_info @@ -33,12 +31,10 @@ module VagrantPlugins return end - puts 'running which on target' if env[:machine].communicate.execute('which rsync', :error_check => false) != 0 env[:ui].warn(I18n.t('vagrant_azure.rsync_not_found_warning', :side => "guest")) return end - puts 'done running which on target' env[:machine].config.vm.synced_folders.each do |id, data| data = scoped_hash_override(data, :azure) @@ -95,7 +91,6 @@ module VagrantPlugins command.insert(1, "--chmod", "ugo=rwX") end - puts 'executing rsync' r = Vagrant::Util::Subprocess.execute(*command) if r.exit_code != 0 raise Errors::RsyncError, diff --git a/lib/vagrant-azure/version.rb b/lib/vagrant-azure/version.rb index bd191ec..30f0974 100644 --- a/lib/vagrant-azure/version.rb +++ b/lib/vagrant-azure/version.rb @@ -5,6 +5,6 @@ module VagrantPlugins module WinAzure - VERSION = '1.0.4' + VERSION = '1.0.5.pre' end end From b5d9a16264a206a41253f002c66980acc49fbdcf Mon Sep 17 00:00:00 2001 From: Ramakrishnan Date: Mon, 14 Apr 2014 20:53:13 +0530 Subject: [PATCH 03/27] General sync folders. Using rsync to sync linux VM and powershell copy for windows VM. Added few error handling. --- lib/vagrant-azure.rb | 9 +- lib/vagrant-azure/action.rb | 3 +- lib/vagrant-azure/action/read_ssh_info.rb | 15 ++- lib/vagrant-azure/action/run_instance.rb | 3 +- lib/vagrant-azure/action/sync_folders.rb | 104 +++++------------- lib/vagrant-azure/communication/powershell.rb | 37 +++++++ lib/vagrant-azure/driver.rb | 5 + lib/vagrant-azure/errors.rb | 23 ++++ lib/vagrant-azure/monkey_patch/machine.rb | 22 ++++ lib/vagrant-azure/scripts/check_winrm.ps1 | 2 +- locales/en.yml | 13 ++- 11 files changed, 150 insertions(+), 86 deletions(-) create mode 100644 lib/vagrant-azure/communication/powershell.rb create mode 100644 lib/vagrant-azure/errors.rb create mode 100644 lib/vagrant-azure/monkey_patch/machine.rb diff --git a/lib/vagrant-azure.rb b/lib/vagrant-azure.rb index fd99df9..33958aa 100644 --- a/lib/vagrant-azure.rb +++ b/lib/vagrant-azure.rb @@ -9,13 +9,20 @@ module VagrantPlugins module WinAzure lib_path = Pathname.new(File.expand_path('../vagrant-azure', __FILE__)) autoload :Action, lib_path.join('action') - autoload :Error, lib_path.join('errors') + autoload :Errors, lib_path.join('errors') autoload :Driver, lib_path.join('driver') + # Load a communicator for Windows guest + require lib_path.join("communication/powershell") + require lib_path.join('provisioner/puppet') require lib_path.join('provisioner/chef-solo') require lib_path.join('provisioner/shell') + monkey_patch = Pathname.new(File.expand_path("../vagrant-azure/monkey_patch", __FILE__)) + # Monkey Patch the core Hyper-V vagrant with the following + require monkey_patch.join("machine") + # This returns the path to the source of this plugin. # # @return [Pathname] diff --git a/lib/vagrant-azure/action.rb b/lib/vagrant-azure/action.rb index cdedf08..47212cd 100644 --- a/lib/vagrant-azure/action.rb +++ b/lib/vagrant-azure/action.rb @@ -67,12 +67,11 @@ module VagrantPlugins end b2.use Provision - b2.use SyncFolders end end end - # This action is called to read the SSH info of the machine. The + # This action is called to read the SSH info of the machine. The # resulting state is expected to be put into the `:machine_ssh_info` key. def self.action_read_ssh_info Vagrant::Action::Builder.new.tap do |b| diff --git a/lib/vagrant-azure/action/read_ssh_info.rb b/lib/vagrant-azure/action/read_ssh_info.rb index 6dcd837..d1c52dd 100644 --- a/lib/vagrant-azure/action/read_ssh_info.rb +++ b/lib/vagrant-azure/action/read_ssh_info.rb @@ -28,7 +28,20 @@ module VagrantPlugins def read_ssh_info(azure, machine) return nil if machine.id.nil? machine.id =~ /@/ - vm = azure.get_virtual_machine($`, $') + vm = nil + attempt = 0 + while true + begin + vm = azure.get_virtual_machine($`, $') + rescue SocketError + attempt = attempt + 1 + env[:ui].info(I18n.t("vagrant_azure.read_attempt", + :attempt => attempt)) + sleep 5 + next if attempt < 3 + end + break + end if vm.nil? || !vm.instance_of?(Azure::VirtualMachineManagement::VirtualMachine) # Machine cannot be found diff --git a/lib/vagrant-azure/action/run_instance.rb b/lib/vagrant-azure/action/run_instance.rb index aa093a9..4faee77 100644 --- a/lib/vagrant-azure/action/run_instance.rb +++ b/lib/vagrant-azure/action/run_instance.rb @@ -97,8 +97,7 @@ module VagrantPlugins # TODO: Exception/Error Handling if server.instance_of? String - env[:ui].info "Server not created. Error is: #{server}" - raise "#{server}" + raise Errors::ServerNotCreated, message: server end env[:machine].id = "#{server.vm_name}@#{server.cloud_service_name}" diff --git a/lib/vagrant-azure/action/sync_folders.rb b/lib/vagrant-azure/action/sync_folders.rb index 9fa3f57..b30b619 100644 --- a/lib/vagrant-azure/action/sync_folders.rb +++ b/lib/vagrant-azure/action/sync_folders.rb @@ -8,12 +8,13 @@ require "log4r" require "vagrant/util/subprocess" require "vagrant/util/scoped_hash_override" require "vagrant/util/which" +require "#{Vagrant::source_root}/lib/vagrant/action/builtin/synced_folders" module VagrantPlugins module WinAzure module Action # This middleware uses `rsync` to sync the folders - class SyncFolders + class SyncFolders < Vagrant::Action::Builtin::SyncedFolders include Vagrant::Util::ScopedHashOverride def initialize(app, env) @@ -22,91 +23,40 @@ module VagrantPlugins end def call(env) - @app.call(env) + if env[:machine].config.vm.guest != :windows + super + else + env[:machine].config.vm.synced_folders.each do |id, data| + data = scoped_hash_override(data, :azure) - ssh_info = env[:machine].ssh_info + # Ignore disabled shared folders + next if data[:disabled] - unless Vagrant::Util::Which.which('rsync') - env[:ui].warn(I18n.t('vagrant_azure.rsync_not_found_warning', :side => "host")) - return - end + hostpath = File.expand_path(data[:hostpath], env[:root_path]) + guestpath = data[:guestpath] - if env[:machine].communicate.execute('which rsync', :error_check => false) != 0 - env[:ui].warn(I18n.t('vagrant_azure.rsync_not_found_warning', :side => "guest")) - return - end + env[:ui].info(I18n.t("vagrant_azure.copy_folder", + :hostpath => hostpath, + :guestpath => guestpath)) - env[:machine].config.vm.synced_folders.each do |id, data| - data = scoped_hash_override(data, :azure) - - # Ignore disabled shared folders - next if data[:disabled] - - hostpath = File.expand_path(data[:hostpath], env[:root_path]) - guestpath = data[:guestpath] - - # Make sure there is a trailing slash on the host path to - # avoid creating an additional directory with rsync - hostpath = "#{hostpath}/" if hostpath !~ /\/$/ - - # on windows rsync.exe requires cygdrive-style paths - if Vagrant::Util::Platform.windows? - hostpath = hostpath.gsub(/^(\w):/) { "/cygdrive/#{$1}" } - end - - env[:ui].info(I18n.t("vagrant_azure.rsync_folder", - :hostpath => hostpath, - :guestpath => guestpath)) - - # Create the host path if it doesn't exist and option flag is set - if data[:create] - begin - FileUtils::mkdir_p(hostpath) - rescue => err - raise Errors::MkdirError, - :hostpath => hostpath, - :err => err + # Create the host path if it doesn't exist and option flag is set + if data[:create] + begin + FileUtils::mkdir_p(hostpath) + rescue => err + raise Errors::MkdirError, + :hostpath => hostpath, + :err => err + end end + + env[:machine].provider.driver.upload(hostpath, guestpath) + end - - # Create the guest path - env[:machine].communicate.sudo("mkdir -p '#{guestpath}'") - env[:machine].communicate.sudo( - "chown -R #{ssh_info[:username]} '#{guestpath}'") - - #collect rsync excludes specified :rsync_excludes=>['path1',...] in synced_folder options - excludes = ['.vagrant/', 'Vagrantfile', *Array(data[:rsync_excludes])].uniq - - # Rsync over to the guest path using the SSH info - command = [ - "rsync", "--verbose", "--archive", "-z", - *excludes.map{|e|['--exclude', e]}.flatten, - "-e", "ssh -p #{ssh_info[:port]} -o StrictHostKeyChecking=no #{ssh_key_options(ssh_info)}", - hostpath, - "#{ssh_info[:username]}@#{ssh_info[:host]}:#{guestpath}"] - - # we need to fix permissions when using rsync.exe on windows, see - # http://stackoverflow.com/questions/5798807/rsync-permission-denied-created-directories-have-no-permissions - if Vagrant::Util::Platform.windows? - command.insert(1, "--chmod", "ugo=rwX") - end - - r = Vagrant::Util::Subprocess.execute(*command) - if r.exit_code != 0 - raise Errors::RsyncError, - :guestpath => guestpath, - :hostpath => hostpath, - :stderr => r.stderr - end + @app.call(env) end end - private - - def ssh_key_options(ssh_info) - # Ensure that `private_key_path` is an Array (for Vagrant < 1.4) - Array(ssh_info[:private_key_path]).map { |path| "-i '#{path}' " }.join - end end end end diff --git a/lib/vagrant-azure/communication/powershell.rb b/lib/vagrant-azure/communication/powershell.rb new file mode 100644 index 0000000..d95cd88 --- /dev/null +++ b/lib/vagrant-azure/communication/powershell.rb @@ -0,0 +1,37 @@ +#------------------------------------------------------------------------- +# Copyright (c) Microsoft Open Technologies, Inc. +# All Rights Reserved. Licensed under the Apache 2.0 License. +#-------------------------------------------------------------------------- + +module VagrantPlugins + module WinAzure + module Communicator + class PowerShell < Vagrant.plugin("2", :communicator) + def initialize(machine) + @machine = machine + end + + def wait_for_ready(timeout) + ready? + end + + def ready? + # Return True when the guest has enabled WinRM + # In this case we can try any remote PowerShell commands to see if + # further vagrant can be carried out using this communication + if !@winrm_status + status = false + response = @machine.provider.driver.check_winrm + @winrm_status = response && response["message"] == "Running" + raise Errors::WinRMNotReady if !@winrm_status + end + @winrm_status + end + + def test(command, opts=nil) + true + end + end + end + end +end diff --git a/lib/vagrant-azure/driver.rb b/lib/vagrant-azure/driver.rb index 2384c63..80cc184 100644 --- a/lib/vagrant-azure/driver.rb +++ b/lib/vagrant-azure/driver.rb @@ -60,6 +60,11 @@ module VagrantPlugins execute(script_path, options) end + def check_winrm + script_path = local_script_path('check_winrm.ps1') + execute(script_path, remote_credentials) + end + protected def local_script_path(path) diff --git a/lib/vagrant-azure/errors.rb b/lib/vagrant-azure/errors.rb new file mode 100644 index 0000000..a19a85e --- /dev/null +++ b/lib/vagrant-azure/errors.rb @@ -0,0 +1,23 @@ +#------------------------------------------------------------------------- +# Copyright (c) Microsoft Open Technologies, Inc. +# All Rights Reserved. Licensed under the Apache 2.0 License. +#-------------------------------------------------------------------------- + +module VagrantPlugins + module WinAzure + module Errors + class WinAzureError < Vagrant::Errors::VagrantError + error_namespace("vagrant_azure.errors") + end + + class WinRMNotReady < WinAzureError + error_key(:win_rm_not_ready) + end + + class ServerNotCreated < WinAzureError + error_key(:server_not_created) + end + + end + end +end diff --git a/lib/vagrant-azure/monkey_patch/machine.rb b/lib/vagrant-azure/monkey_patch/machine.rb new file mode 100644 index 0000000..b0be839 --- /dev/null +++ b/lib/vagrant-azure/monkey_patch/machine.rb @@ -0,0 +1,22 @@ +#------------------------------------------------------------------------- +# Copyright (c) Microsoft Open Technologies, Inc. +# All Rights Reserved. Licensed under the Apache 2.0 License. +#-------------------------------------------------------------------------- + +module Vagrant + class Machine + + alias_method :original_communicate, :communicate + + def communicate + unless @communicator + if @config.vm.guest == :windows + @communicator = VagrantPlugins::VagrantHyperV::Communicator::PowerShell.new(self) + else + @communicator = original_communicate + end + end + @communicator + end + end +end diff --git a/lib/vagrant-azure/scripts/check_winrm.ps1 b/lib/vagrant-azure/scripts/check_winrm.ps1 index 703ef75..d76699a 100644 --- a/lib/vagrant-azure/scripts/check_winrm.ps1 +++ b/lib/vagrant-azure/scripts/check_winrm.ps1 @@ -34,7 +34,7 @@ try { } catch { $errortHash = @{ type = "PowerShellError" - error ="Failed to copy file $_" + error ="$_" } Write-Error-Message $errortHash return diff --git a/locales/en.yml b/locales/en.yml index 9b3cd11..be38a7b 100644 --- a/locales/en.yml +++ b/locales/en.yml @@ -14,8 +14,9 @@ en: VM '%{name}' has been started vm_stopped: |- VM '%{name}' has been stopped - rsync_folder: |- - Rsyncing folder: %{hostpath} => %{guestpath} + copy_folder: |- + Copying folder: %{hostpath} ==> + %{guestpath} rsync_not_found_warning: |- Warning! Folder sync disabled because the rsync binary is missing in the %{side}. Make sure rsync is installed and the binary can be found in the PATH. @@ -23,3 +24,11 @@ en: Waiting for SSH ssh_ready: |- SSH Ready + read_attempt: |- + Failed to connect to Virtual Machine. Re-Connecting %{attempt} + errors: + win_rm_not_ready: |- + WinRM is not enabled in the remot guest. Vagrant-Windows-HyperV uses remote PowerShell scripts + to communicate to the remote machine. Please check the same and try again. + server_not_created: |- + Server not created. Error is: %{message} From 4de68d31247a38510958420e1e71758d85c9ae93 Mon Sep 17 00:00:00 2001 From: Ramakrishnan Date: Wed, 23 Apr 2014 17:41:50 +0530 Subject: [PATCH 04/27] Few minor fixes. --- lib/vagrant-azure/action.rb | 9 ++++++--- lib/vagrant-azure/monkey_patch/machine.rb | 2 +- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/lib/vagrant-azure/action.rb b/lib/vagrant-azure/action.rb index 47212cd..e14b5c9 100644 --- a/lib/vagrant-azure/action.rb +++ b/lib/vagrant-azure/action.rb @@ -158,7 +158,7 @@ module VagrantPlugins def self.action_prepare_boot Vagrant::Action::Builder.new.tap do |b| b.use Provision - b.use SyncFolders + # b.use SyncFolders end end @@ -173,7 +173,8 @@ module VagrantPlugins if !env1[:result] b1.use Call, IsState, :StoppedDeallocated do |env2, b2| if env2[:result] - b2.use action_prepare_boot + # b2.use action_prepare_boot + b2.use Provision b2.use StartInstance # start this instance again b2.use Call, WaitForState, :ReadyRole do |env3, b3| if env3[:result] @@ -182,6 +183,7 @@ module VagrantPlugins 'vagrant_azure.vm_started', :name => $` ) b3.use WaitForCommunicate + b3.use SyncFolders end end else @@ -191,8 +193,9 @@ module VagrantPlugins end end else - b1.use action_prepare_boot + b1.use Provision b1.use RunInstance # Launch a new instance + b1.use SyncFolders b1.use Call, WaitForState, :ReadyRole do |env2, b2| if env2[:result] env2[:machine].id =~ /@/ diff --git a/lib/vagrant-azure/monkey_patch/machine.rb b/lib/vagrant-azure/monkey_patch/machine.rb index b0be839..5a3a7d0 100644 --- a/lib/vagrant-azure/monkey_patch/machine.rb +++ b/lib/vagrant-azure/monkey_patch/machine.rb @@ -11,7 +11,7 @@ module Vagrant def communicate unless @communicator if @config.vm.guest == :windows - @communicator = VagrantPlugins::VagrantHyperV::Communicator::PowerShell.new(self) + @communicator = VagrantPlugins::WinAzure::Communicator::PowerShell.new(self) else @communicator = original_communicate end From 4597e0bd5812e95e8c9585a9a6ec6d731a41f542 Mon Sep 17 00:00:00 2001 From: Ramakrishnan Date: Thu, 24 Apr 2014 07:09:02 +0530 Subject: [PATCH 05/27] Fix check winrm running. Add guest port to create new ps session. --- lib/vagrant-azure/communication/powershell.rb | 1 - lib/vagrant-azure/scripts/check_winrm.ps1 | 6 ++++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/lib/vagrant-azure/communication/powershell.rb b/lib/vagrant-azure/communication/powershell.rb index d95cd88..177a5d6 100644 --- a/lib/vagrant-azure/communication/powershell.rb +++ b/lib/vagrant-azure/communication/powershell.rb @@ -2,7 +2,6 @@ # Copyright (c) Microsoft Open Technologies, Inc. # All Rights Reserved. Licensed under the Apache 2.0 License. #-------------------------------------------------------------------------- - module VagrantPlugins module WinAzure module Communicator diff --git a/lib/vagrant-azure/scripts/check_winrm.ps1 b/lib/vagrant-azure/scripts/check_winrm.ps1 index d76699a..dc716f9 100644 --- a/lib/vagrant-azure/scripts/check_winrm.ps1 +++ b/lib/vagrant-azure/scripts/check_winrm.ps1 @@ -2,10 +2,12 @@ # Copyright (c) Microsoft Open Technologies, Inc. # All Rights Reserved. Licensed under the Apache 2.0 License. #-------------------------------------------------------------------------- + param ( [string]$guest_ip = $(throw "-guest_ip is required."), [string]$username = $(throw "-guest_username is required."), - [string]$password = $(throw "-guest_password is required.") + [string]$password = $(throw "-guest_password is required."), + [string]$guest_port = $(throw "-guest_port is required") ) # Include the following modules @@ -14,7 +16,7 @@ $presentDir = Split-Path -parent $PSCommandPath . ([System.IO.Path]::Combine($presentDir, "utils\create_session.ps1")) try { - $response = Create-Remote-Session $guest_ip $username $password + $response = Create-Remote-Session $guest_ip $guest_port $username $password if (!$response["session"] -and $response["error"]) { Write-Host $response["error"] return From 74d70ea868dc566b7dafbc3fe38aae654cf99e13 Mon Sep 17 00:00:00 2001 From: Ramakrishnan Date: Thu, 24 Apr 2014 18:20:23 +0530 Subject: [PATCH 06/27] Fix action ordering. Rearranged the action ordering for up and reload command. This also fixes the sync action getting called before the VM in up and running. --- lib/vagrant-azure/action.rb | 39 +++++++++--------------- lib/vagrant-azure/action/sync_folders.rb | 2 +- 2 files changed, 15 insertions(+), 26 deletions(-) diff --git a/lib/vagrant-azure/action.rb b/lib/vagrant-azure/action.rb index e14b5c9..36e59b6 100644 --- a/lib/vagrant-azure/action.rb +++ b/lib/vagrant-azure/action.rb @@ -65,7 +65,6 @@ module VagrantPlugins b2.use Message, I18n.t('vagrant_azure.not_created') next end - b2.use Provision end end @@ -158,7 +157,16 @@ module VagrantPlugins def self.action_prepare_boot Vagrant::Action::Builder.new.tap do |b| b.use Provision - # b.use SyncFolders + b.use SyncFolders + b.use Call, WaitForState, :ReadyRole do |env, b1| + if env[:result] + env[:machine].id =~ /@/ + b1.use Message, I18n.t( + 'vagrant_azure.vm_started', :name => $` + ) + b1.use WaitForCommunicate + end + end end end @@ -173,19 +181,9 @@ module VagrantPlugins if !env1[:result] b1.use Call, IsState, :StoppedDeallocated do |env2, b2| if env2[:result] - # b2.use action_prepare_boot - b2.use Provision + b2.use action_prepare_boot b2.use StartInstance # start this instance again - b2.use Call, WaitForState, :ReadyRole do |env3, b3| - if env3[:result] - env3[:machine].id =~ /@/ - b3.use Message, I18n.t( - 'vagrant_azure.vm_started', :name => $` - ) - b3.use WaitForCommunicate - b3.use SyncFolders - end - end + # b2.use action_boot_ready else b2.use Message, I18n.t( 'vagrant_azure.already_status', :status => 'created' @@ -193,18 +191,9 @@ module VagrantPlugins end end else - b1.use Provision b1.use RunInstance # Launch a new instance - b1.use SyncFolders - b1.use Call, WaitForState, :ReadyRole do |env2, b2| - if env2[:result] - env2[:machine].id =~ /@/ - b2.use Message, I18n.t( - 'vagrant_azure.vm_started', :name => $` - ) - b2.use WaitForCommunicate - end - end + b1.use action_prepare_boot + # b1.use action_boot_ready end end end diff --git a/lib/vagrant-azure/action/sync_folders.rb b/lib/vagrant-azure/action/sync_folders.rb index b30b619..5a2a257 100644 --- a/lib/vagrant-azure/action/sync_folders.rb +++ b/lib/vagrant-azure/action/sync_folders.rb @@ -26,6 +26,7 @@ module VagrantPlugins if env[:machine].config.vm.guest != :windows super else + @app.call(env) env[:machine].config.vm.synced_folders.each do |id, data| data = scoped_hash_override(data, :azure) @@ -53,7 +54,6 @@ module VagrantPlugins env[:machine].provider.driver.upload(hostpath, guestpath) end - @app.call(env) end end From f40b531694fb9b4ab74c7a5f17e1b6b7521caad2 Mon Sep 17 00:00:00 2001 From: Jeff Mendoza Date: Fri, 11 Apr 2014 15:56:10 -0700 Subject: [PATCH 07/27] Synced Folders, Wait for SSH. --- lib/vagrant-azure/action.rb | 12 +- lib/vagrant-azure/action/connect_azure.rb | 8 +- lib/vagrant-azure/action/sync_folders.rb | 118 ++++++++++++++++++ .../action/wait_for_communicate.rb | 38 ++++++ locales/en.yml | 9 ++ 5 files changed, 177 insertions(+), 8 deletions(-) create mode 100644 lib/vagrant-azure/action/sync_folders.rb create mode 100644 lib/vagrant-azure/action/wait_for_communicate.rb diff --git a/lib/vagrant-azure/action.rb b/lib/vagrant-azure/action.rb index 74cdb65..f4f6c2c 100644 --- a/lib/vagrant-azure/action.rb +++ b/lib/vagrant-azure/action.rb @@ -67,7 +67,7 @@ module VagrantPlugins end b2.use Provision - # b2.use SyncFolders + b2.use SyncFolders end end end @@ -159,8 +159,7 @@ module VagrantPlugins def self.action_prepare_boot Vagrant::Action::Builder.new.tap do |b| b.use Provision - # b.use SyncFolders - # b.use WarnNetworks + b.use SyncFolders end end @@ -170,6 +169,7 @@ module VagrantPlugins b.use HandleBoxUrl b.use ConfigValidate b.use ConnectAzure + b.use Call, IsState, :NotCreated do |env1, b1| if !env1[:result] b1.use Call, IsState, :StoppedDeallocated do |env2, b2| @@ -182,6 +182,7 @@ module VagrantPlugins b3.use Message, I18n.t( 'vagrant_azure.vm_started', :name => $` ) + b3.use WaitForCommunicate end end else @@ -191,6 +192,7 @@ module VagrantPlugins end end else + b1.use action_prepare_boot b1.use RunInstance # Launch a new instance b1.use Call, WaitForState, :ReadyRole do |env2, b2| if env2[:result] @@ -198,6 +200,7 @@ module VagrantPlugins b2.use Message, I18n.t( 'vagrant_azure.vm_started', :name => $` ) + b2.use WaitForCommunicate end end end @@ -240,11 +243,12 @@ module VagrantPlugins autoload :RunInstance, action_root.join('run_instance') autoload :StartInstance, action_root.join('start_instance') autoload :StopInstance, action_root.join('stop_instance') - # autoload :SyncFolders, action_root.join('sync_folders') + autoload :SyncFolders, action_root.join('sync_folders') autoload :TerminateInstance, action_root.join('terminate_instance') # autoload :TimedProvision, action_root.join('timed_provision') # autoload :WarnNetworks, action_root.join('warn_networks') autoload :WaitForState, action_root.join('wait_for_state') + autoload :WaitForCommunicate, action_root.join('wait_for_communicate') end end end diff --git a/lib/vagrant-azure/action/connect_azure.rb b/lib/vagrant-azure/action/connect_azure.rb index a1ea1a1..b551900 100644 --- a/lib/vagrant-azure/action/connect_azure.rb +++ b/lib/vagrant-azure/action/connect_azure.rb @@ -17,10 +17,10 @@ module VagrantPlugins def call (env) config = env[:machine].provider_config - env[:ui].warn "Subscription ID: [#{config.subscription_id}]" - env[:ui].warn "Mangement Certificate: [#{config.mgmt_certificate}]" - env[:ui].warn "Mangement Endpoint: [#{config.mgmt_endpoint}]" - env[:ui].warn "Storage Account Name: [#{config.storage_acct_name}]" + env[:ui].info "Subscription ID: [#{config.subscription_id}]" + env[:ui].info "Mangement Certificate: [#{config.mgmt_certificate}]" + env[:ui].info "Mangement Endpoint: [#{config.mgmt_endpoint}]" + env[:ui].info "Storage Account Name: [#{config.storage_acct_name}]" Azure.configure do |c| c.subscription_id = config.subscription_id diff --git a/lib/vagrant-azure/action/sync_folders.rb b/lib/vagrant-azure/action/sync_folders.rb new file mode 100644 index 0000000..4236042 --- /dev/null +++ b/lib/vagrant-azure/action/sync_folders.rb @@ -0,0 +1,118 @@ +# Copyright (c) 2014 Mitchell Hashimoto +# Under The MIT License (MIT) +#--------------------------------------------------------------------------- +# Copyright (c) Microsoft Open Technologies, Inc. +# All Rights Reserved. Licensed under the Apache 2.0 License. +#-------------------------------------------------------------------------- +require "log4r" +require "vagrant/util/subprocess" +require "vagrant/util/scoped_hash_override" +require "vagrant/util/which" + +module VagrantPlugins + module WinAzure + module Action + # This middleware uses `rsync` to sync the folders + class SyncFolders + include Vagrant::Util::ScopedHashOverride + + def initialize(app, env) + @app = app + @logger = Log4r::Logger.new("vagrant_azure::action::sync_folders") + end + + def call(env) + puts 'entered sync folders, call app' + @app.call(env) + puts 'called app' + + ssh_info = env[:machine].ssh_info + + unless Vagrant::Util::Which.which('rsync') + env[:ui].warn(I18n.t('vagrant_azure.rsync_not_found_warning', :side => "host")) + return + end + + puts 'running which on target' + if env[:machine].communicate.execute('which rsync', :error_check => false) != 0 + env[:ui].warn(I18n.t('vagrant_azure.rsync_not_found_warning', :side => "guest")) + return + end + puts 'done running which on target' + + env[:machine].config.vm.synced_folders.each do |id, data| + data = scoped_hash_override(data, :azure) + + # Ignore disabled shared folders + next if data[:disabled] + + hostpath = File.expand_path(data[:hostpath], env[:root_path]) + guestpath = data[:guestpath] + + # Make sure there is a trailing slash on the host path to + # avoid creating an additional directory with rsync + hostpath = "#{hostpath}/" if hostpath !~ /\/$/ + + # on windows rsync.exe requires cygdrive-style paths + if Vagrant::Util::Platform.windows? + hostpath = hostpath.gsub(/^(\w):/) { "/cygdrive/#{$1}" } + end + + env[:ui].info(I18n.t("vagrant_azure.rsync_folder", + :hostpath => hostpath, + :guestpath => guestpath)) + + # Create the host path if it doesn't exist and option flag is set + if data[:create] + begin + FileUtils::mkdir_p(hostpath) + rescue => err + raise Errors::MkdirError, + :hostpath => hostpath, + :err => err + end + end + + # Create the guest path + env[:machine].communicate.sudo("mkdir -p '#{guestpath}'") + env[:machine].communicate.sudo( + "chown -R #{ssh_info[:username]} '#{guestpath}'") + + #collect rsync excludes specified :rsync_excludes=>['path1',...] in synced_folder options + excludes = ['.vagrant/', 'Vagrantfile', *Array(data[:rsync_excludes])].uniq + + # Rsync over to the guest path using the SSH info + command = [ + "rsync", "--verbose", "--archive", "-z", + *excludes.map{|e|['--exclude', e]}.flatten, + "-e", "ssh -p #{ssh_info[:port]} -o StrictHostKeyChecking=no #{ssh_key_options(ssh_info)}", + hostpath, + "#{ssh_info[:username]}@#{ssh_info[:host]}:#{guestpath}"] + + # we need to fix permissions when using rsync.exe on windows, see + # http://stackoverflow.com/questions/5798807/rsync-permission-denied-created-directories-have-no-permissions + if Vagrant::Util::Platform.windows? + command.insert(1, "--chmod", "ugo=rwX") + end + + puts 'executing rsync' + r = Vagrant::Util::Subprocess.execute(*command) + if r.exit_code != 0 + raise Errors::RsyncError, + :guestpath => guestpath, + :hostpath => hostpath, + :stderr => r.stderr + end + end + end + + private + + def ssh_key_options(ssh_info) + # Ensure that `private_key_path` is an Array (for Vagrant < 1.4) + Array(ssh_info[:private_key_path]).map { |path| "-i '#{path}' " }.join + end + end + end + end +end diff --git a/lib/vagrant-azure/action/wait_for_communicate.rb b/lib/vagrant-azure/action/wait_for_communicate.rb new file mode 100644 index 0000000..8cb06bc --- /dev/null +++ b/lib/vagrant-azure/action/wait_for_communicate.rb @@ -0,0 +1,38 @@ +#-------------------------------------------------------------------------- +# Copyright (c) Microsoft Open Technologies, Inc. +# All Rights Reserved. Licensed under the Apache 2.0 License. +#-------------------------------------------------------------------------- +require 'log4r' +require 'timeout' + +module VagrantPlugins + module WinAzure + module Action + class WaitForCommunicate + def initialize(app, env) + @app = app + @logger = Log4r::Logger.new("vagrant_azure::action::wait_for_communicate") + end + + def call(env) + + if !env[:interrupted] + # Wait for SSH to be ready. + env[:ui].info(I18n.t("vagrant_azure.waiting_for_ssh")) + while true + # If we're interrupted then just back out + break if env[:interrupted] + break if env[:machine].communicate.ready? + sleep 5 + end + + # Ready and booted! + env[:ui].info(I18n.t("vagrant_azure.ssh_ready")) + end + + @app.call(env) + end + end + end + end +end diff --git a/locales/en.yml b/locales/en.yml index a61ded1..a33ffaa 100644 --- a/locales/en.yml +++ b/locales/en.yml @@ -14,6 +14,15 @@ en: VM '%{name}' has been started vm_stopped: |- VM '%{name}' has been stopped + rsync_folder: |- + Rsyncing folder: %{hostpath} => %{guestpath} + rsync_not_found_warning: |- + Warning! Folder sync disabled because the rsync binary is missing in the %{side}. + Make sure rsync is installed and the binary can be found in the PATH. + waiting_for_ssh: |- + Waiting for SSH + ssh_ready: |- + SSH Ready errors: create_vm_failure: |- There was some error in creating the VM, this could be due to some network latency. From 3eec951e7a6e20d8ecb23bed668d18eca4e3c608 Mon Sep 17 00:00:00 2001 From: Jeff Mendoza Date: Sat, 12 Apr 2014 00:32:20 -0700 Subject: [PATCH 08/27] Remove debug. Fix HandleBoxUrl. Remove some verbosity. Bump version to .pre. --- lib/vagrant-azure/action.rb | 2 +- lib/vagrant-azure/action/connect_azure.rb | 5 ----- lib/vagrant-azure/action/sync_folders.rb | 5 ----- lib/vagrant-azure/version.rb | 2 +- 4 files changed, 2 insertions(+), 12 deletions(-) diff --git a/lib/vagrant-azure/action.rb b/lib/vagrant-azure/action.rb index f4f6c2c..cdedf08 100644 --- a/lib/vagrant-azure/action.rb +++ b/lib/vagrant-azure/action.rb @@ -166,7 +166,7 @@ module VagrantPlugins # This action is called to bring the box up from nothing def self.action_up Vagrant::Action::Builder.new.tap do |b| - b.use HandleBoxUrl + b.use HandleBox b.use ConfigValidate b.use ConnectAzure diff --git a/lib/vagrant-azure/action/connect_azure.rb b/lib/vagrant-azure/action/connect_azure.rb index b551900..644290a 100644 --- a/lib/vagrant-azure/action/connect_azure.rb +++ b/lib/vagrant-azure/action/connect_azure.rb @@ -17,11 +17,6 @@ module VagrantPlugins def call (env) config = env[:machine].provider_config - env[:ui].info "Subscription ID: [#{config.subscription_id}]" - env[:ui].info "Mangement Certificate: [#{config.mgmt_certificate}]" - env[:ui].info "Mangement Endpoint: [#{config.mgmt_endpoint}]" - env[:ui].info "Storage Account Name: [#{config.storage_acct_name}]" - Azure.configure do |c| c.subscription_id = config.subscription_id c.management_certificate = config.mgmt_certificate diff --git a/lib/vagrant-azure/action/sync_folders.rb b/lib/vagrant-azure/action/sync_folders.rb index 4236042..9fa3f57 100644 --- a/lib/vagrant-azure/action/sync_folders.rb +++ b/lib/vagrant-azure/action/sync_folders.rb @@ -22,9 +22,7 @@ module VagrantPlugins end def call(env) - puts 'entered sync folders, call app' @app.call(env) - puts 'called app' ssh_info = env[:machine].ssh_info @@ -33,12 +31,10 @@ module VagrantPlugins return end - puts 'running which on target' if env[:machine].communicate.execute('which rsync', :error_check => false) != 0 env[:ui].warn(I18n.t('vagrant_azure.rsync_not_found_warning', :side => "guest")) return end - puts 'done running which on target' env[:machine].config.vm.synced_folders.each do |id, data| data = scoped_hash_override(data, :azure) @@ -95,7 +91,6 @@ module VagrantPlugins command.insert(1, "--chmod", "ugo=rwX") end - puts 'executing rsync' r = Vagrant::Util::Subprocess.execute(*command) if r.exit_code != 0 raise Errors::RsyncError, diff --git a/lib/vagrant-azure/version.rb b/lib/vagrant-azure/version.rb index bd191ec..30f0974 100644 --- a/lib/vagrant-azure/version.rb +++ b/lib/vagrant-azure/version.rb @@ -5,6 +5,6 @@ module VagrantPlugins module WinAzure - VERSION = '1.0.4' + VERSION = '1.0.5.pre' end end From 837ff9fe7d4a2615205f0f612f1d911bb4ab2d8f Mon Sep 17 00:00:00 2001 From: Ramakrishnan Date: Mon, 14 Apr 2014 20:53:13 +0530 Subject: [PATCH 09/27] General sync folders. Using rsync to sync linux VM and powershell copy for windows VM. Added few error handling. --- lib/vagrant-azure.rb | 7 ++ lib/vagrant-azure/action.rb | 3 +- lib/vagrant-azure/action/read_ssh_info.rb | 15 ++- lib/vagrant-azure/action/run_instance.rb | 3 +- lib/vagrant-azure/action/sync_folders.rb | 104 +++++------------- lib/vagrant-azure/communication/powershell.rb | 37 +++++++ lib/vagrant-azure/driver.rb | 5 + lib/vagrant-azure/errors.rb | 14 ++- lib/vagrant-azure/monkey_patch/machine.rb | 22 ++++ lib/vagrant-azure/scripts/check_winrm.ps1 | 2 +- locales/en.yml | 15 ++- 11 files changed, 135 insertions(+), 92 deletions(-) create mode 100644 lib/vagrant-azure/communication/powershell.rb create mode 100644 lib/vagrant-azure/monkey_patch/machine.rb diff --git a/lib/vagrant-azure.rb b/lib/vagrant-azure.rb index a6562fe..33958aa 100644 --- a/lib/vagrant-azure.rb +++ b/lib/vagrant-azure.rb @@ -12,10 +12,17 @@ module VagrantPlugins autoload :Errors, lib_path.join('errors') autoload :Driver, lib_path.join('driver') + # Load a communicator for Windows guest + require lib_path.join("communication/powershell") + require lib_path.join('provisioner/puppet') require lib_path.join('provisioner/chef-solo') require lib_path.join('provisioner/shell') + monkey_patch = Pathname.new(File.expand_path("../vagrant-azure/monkey_patch", __FILE__)) + # Monkey Patch the core Hyper-V vagrant with the following + require monkey_patch.join("machine") + # This returns the path to the source of this plugin. # # @return [Pathname] diff --git a/lib/vagrant-azure/action.rb b/lib/vagrant-azure/action.rb index cdedf08..47212cd 100644 --- a/lib/vagrant-azure/action.rb +++ b/lib/vagrant-azure/action.rb @@ -67,12 +67,11 @@ module VagrantPlugins end b2.use Provision - b2.use SyncFolders end end end - # This action is called to read the SSH info of the machine. The + # This action is called to read the SSH info of the machine. The # resulting state is expected to be put into the `:machine_ssh_info` key. def self.action_read_ssh_info Vagrant::Action::Builder.new.tap do |b| diff --git a/lib/vagrant-azure/action/read_ssh_info.rb b/lib/vagrant-azure/action/read_ssh_info.rb index 6dcd837..d1c52dd 100644 --- a/lib/vagrant-azure/action/read_ssh_info.rb +++ b/lib/vagrant-azure/action/read_ssh_info.rb @@ -28,7 +28,20 @@ module VagrantPlugins def read_ssh_info(azure, machine) return nil if machine.id.nil? machine.id =~ /@/ - vm = azure.get_virtual_machine($`, $') + vm = nil + attempt = 0 + while true + begin + vm = azure.get_virtual_machine($`, $') + rescue SocketError + attempt = attempt + 1 + env[:ui].info(I18n.t("vagrant_azure.read_attempt", + :attempt => attempt)) + sleep 5 + next if attempt < 3 + end + break + end if vm.nil? || !vm.instance_of?(Azure::VirtualMachineManagement::VirtualMachine) # Machine cannot be found diff --git a/lib/vagrant-azure/action/run_instance.rb b/lib/vagrant-azure/action/run_instance.rb index a417e27..9d8fd82 100644 --- a/lib/vagrant-azure/action/run_instance.rb +++ b/lib/vagrant-azure/action/run_instance.rb @@ -102,8 +102,7 @@ module VagrantPlugins # machine as a string. if server.instance_of? String - env[:ui].info "Server not created. Error is: #{server}" - raise Errors::CreateVMError, message: "#{server}" + raise Errors::ServerNotCreated, message: server end env[:machine].id = "#{server.vm_name}@#{server.cloud_service_name}" diff --git a/lib/vagrant-azure/action/sync_folders.rb b/lib/vagrant-azure/action/sync_folders.rb index 9fa3f57..b30b619 100644 --- a/lib/vagrant-azure/action/sync_folders.rb +++ b/lib/vagrant-azure/action/sync_folders.rb @@ -8,12 +8,13 @@ require "log4r" require "vagrant/util/subprocess" require "vagrant/util/scoped_hash_override" require "vagrant/util/which" +require "#{Vagrant::source_root}/lib/vagrant/action/builtin/synced_folders" module VagrantPlugins module WinAzure module Action # This middleware uses `rsync` to sync the folders - class SyncFolders + class SyncFolders < Vagrant::Action::Builtin::SyncedFolders include Vagrant::Util::ScopedHashOverride def initialize(app, env) @@ -22,91 +23,40 @@ module VagrantPlugins end def call(env) - @app.call(env) + if env[:machine].config.vm.guest != :windows + super + else + env[:machine].config.vm.synced_folders.each do |id, data| + data = scoped_hash_override(data, :azure) - ssh_info = env[:machine].ssh_info + # Ignore disabled shared folders + next if data[:disabled] - unless Vagrant::Util::Which.which('rsync') - env[:ui].warn(I18n.t('vagrant_azure.rsync_not_found_warning', :side => "host")) - return - end + hostpath = File.expand_path(data[:hostpath], env[:root_path]) + guestpath = data[:guestpath] - if env[:machine].communicate.execute('which rsync', :error_check => false) != 0 - env[:ui].warn(I18n.t('vagrant_azure.rsync_not_found_warning', :side => "guest")) - return - end + env[:ui].info(I18n.t("vagrant_azure.copy_folder", + :hostpath => hostpath, + :guestpath => guestpath)) - env[:machine].config.vm.synced_folders.each do |id, data| - data = scoped_hash_override(data, :azure) - - # Ignore disabled shared folders - next if data[:disabled] - - hostpath = File.expand_path(data[:hostpath], env[:root_path]) - guestpath = data[:guestpath] - - # Make sure there is a trailing slash on the host path to - # avoid creating an additional directory with rsync - hostpath = "#{hostpath}/" if hostpath !~ /\/$/ - - # on windows rsync.exe requires cygdrive-style paths - if Vagrant::Util::Platform.windows? - hostpath = hostpath.gsub(/^(\w):/) { "/cygdrive/#{$1}" } - end - - env[:ui].info(I18n.t("vagrant_azure.rsync_folder", - :hostpath => hostpath, - :guestpath => guestpath)) - - # Create the host path if it doesn't exist and option flag is set - if data[:create] - begin - FileUtils::mkdir_p(hostpath) - rescue => err - raise Errors::MkdirError, - :hostpath => hostpath, - :err => err + # Create the host path if it doesn't exist and option flag is set + if data[:create] + begin + FileUtils::mkdir_p(hostpath) + rescue => err + raise Errors::MkdirError, + :hostpath => hostpath, + :err => err + end end + + env[:machine].provider.driver.upload(hostpath, guestpath) + end - - # Create the guest path - env[:machine].communicate.sudo("mkdir -p '#{guestpath}'") - env[:machine].communicate.sudo( - "chown -R #{ssh_info[:username]} '#{guestpath}'") - - #collect rsync excludes specified :rsync_excludes=>['path1',...] in synced_folder options - excludes = ['.vagrant/', 'Vagrantfile', *Array(data[:rsync_excludes])].uniq - - # Rsync over to the guest path using the SSH info - command = [ - "rsync", "--verbose", "--archive", "-z", - *excludes.map{|e|['--exclude', e]}.flatten, - "-e", "ssh -p #{ssh_info[:port]} -o StrictHostKeyChecking=no #{ssh_key_options(ssh_info)}", - hostpath, - "#{ssh_info[:username]}@#{ssh_info[:host]}:#{guestpath}"] - - # we need to fix permissions when using rsync.exe on windows, see - # http://stackoverflow.com/questions/5798807/rsync-permission-denied-created-directories-have-no-permissions - if Vagrant::Util::Platform.windows? - command.insert(1, "--chmod", "ugo=rwX") - end - - r = Vagrant::Util::Subprocess.execute(*command) - if r.exit_code != 0 - raise Errors::RsyncError, - :guestpath => guestpath, - :hostpath => hostpath, - :stderr => r.stderr - end + @app.call(env) end end - private - - def ssh_key_options(ssh_info) - # Ensure that `private_key_path` is an Array (for Vagrant < 1.4) - Array(ssh_info[:private_key_path]).map { |path| "-i '#{path}' " }.join - end end end end diff --git a/lib/vagrant-azure/communication/powershell.rb b/lib/vagrant-azure/communication/powershell.rb new file mode 100644 index 0000000..d95cd88 --- /dev/null +++ b/lib/vagrant-azure/communication/powershell.rb @@ -0,0 +1,37 @@ +#------------------------------------------------------------------------- +# Copyright (c) Microsoft Open Technologies, Inc. +# All Rights Reserved. Licensed under the Apache 2.0 License. +#-------------------------------------------------------------------------- + +module VagrantPlugins + module WinAzure + module Communicator + class PowerShell < Vagrant.plugin("2", :communicator) + def initialize(machine) + @machine = machine + end + + def wait_for_ready(timeout) + ready? + end + + def ready? + # Return True when the guest has enabled WinRM + # In this case we can try any remote PowerShell commands to see if + # further vagrant can be carried out using this communication + if !@winrm_status + status = false + response = @machine.provider.driver.check_winrm + @winrm_status = response && response["message"] == "Running" + raise Errors::WinRMNotReady if !@winrm_status + end + @winrm_status + end + + def test(command, opts=nil) + true + end + end + end + end +end diff --git a/lib/vagrant-azure/driver.rb b/lib/vagrant-azure/driver.rb index 2384c63..80cc184 100644 --- a/lib/vagrant-azure/driver.rb +++ b/lib/vagrant-azure/driver.rb @@ -60,6 +60,11 @@ module VagrantPlugins execute(script_path, options) end + def check_winrm + script_path = local_script_path('check_winrm.ps1') + execute(script_path, remote_credentials) + end + protected def local_script_path(path) diff --git a/lib/vagrant-azure/errors.rb b/lib/vagrant-azure/errors.rb index de5f34c..6d76a15 100644 --- a/lib/vagrant-azure/errors.rb +++ b/lib/vagrant-azure/errors.rb @@ -1,21 +1,27 @@ -#-------------------------------------------------------------------------- +#------------------------------------------------------------------------- # Copyright (c) Microsoft Open Technologies, Inc. # All Rights Reserved. Licensed under the Apache 2.0 License. #-------------------------------------------------------------------------- + module VagrantPlugins module WinAzure module Errors - class VagrantAzureError < Vagrant::Errors::VagrantError + class WinAzureError < Vagrant::Errors::VagrantError error_namespace("vagrant_azure.errors") end - class CreateVMFailure < VagrantAzureError - error_key(:create_vm_failure) + class WinRMNotReady < WinAzureError + error_key(:win_rm_not_ready) + end + + class ServerNotCreated < WinAzureError + error_key(:server_not_created) end class CreateVMError < VagrantAzureError error_key(:create_vm_error) end + end end end diff --git a/lib/vagrant-azure/monkey_patch/machine.rb b/lib/vagrant-azure/monkey_patch/machine.rb new file mode 100644 index 0000000..b0be839 --- /dev/null +++ b/lib/vagrant-azure/monkey_patch/machine.rb @@ -0,0 +1,22 @@ +#------------------------------------------------------------------------- +# Copyright (c) Microsoft Open Technologies, Inc. +# All Rights Reserved. Licensed under the Apache 2.0 License. +#-------------------------------------------------------------------------- + +module Vagrant + class Machine + + alias_method :original_communicate, :communicate + + def communicate + unless @communicator + if @config.vm.guest == :windows + @communicator = VagrantPlugins::VagrantHyperV::Communicator::PowerShell.new(self) + else + @communicator = original_communicate + end + end + @communicator + end + end +end diff --git a/lib/vagrant-azure/scripts/check_winrm.ps1 b/lib/vagrant-azure/scripts/check_winrm.ps1 index 703ef75..d76699a 100644 --- a/lib/vagrant-azure/scripts/check_winrm.ps1 +++ b/lib/vagrant-azure/scripts/check_winrm.ps1 @@ -34,7 +34,7 @@ try { } catch { $errortHash = @{ type = "PowerShellError" - error ="Failed to copy file $_" + error ="$_" } Write-Error-Message $errortHash return diff --git a/locales/en.yml b/locales/en.yml index a33ffaa..374eb75 100644 --- a/locales/en.yml +++ b/locales/en.yml @@ -14,8 +14,9 @@ en: VM '%{name}' has been started vm_stopped: |- VM '%{name}' has been stopped - rsync_folder: |- - Rsyncing folder: %{hostpath} => %{guestpath} + copy_folder: |- + Copying folder: %{hostpath} ==> + %{guestpath} rsync_not_found_warning: |- Warning! Folder sync disabled because the rsync binary is missing in the %{side}. Make sure rsync is installed and the binary can be found in the PATH. @@ -23,10 +24,14 @@ en: Waiting for SSH ssh_ready: |- SSH Ready + read_attempt: |- + Failed to connect to Virtual Machine. Re-Connecting %{attempt} errors: - create_vm_failure: |- - There was some error in creating the VM, this could be due to some network latency. - Generally the next vagrant up command should fix this error. + win_rm_not_ready: |- + WinRM is not enabled in the remot guest. Vagrant-Windows-HyperV uses remote PowerShell scripts + to communicate to the remote machine. Please check the same and try again. + server_not_created: |- + Server not created. Error is: %{message} create_vm_error: |- Failed to create a VM in azure cloud with the following message. %{message} From 87a57056c7441635b88304a6d4b08fce482bc5e9 Mon Sep 17 00:00:00 2001 From: Ramakrishnan Date: Wed, 23 Apr 2014 17:41:50 +0530 Subject: [PATCH 10/27] Few minor fixes. --- lib/vagrant-azure/action.rb | 9 ++++++--- lib/vagrant-azure/monkey_patch/machine.rb | 2 +- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/lib/vagrant-azure/action.rb b/lib/vagrant-azure/action.rb index 47212cd..e14b5c9 100644 --- a/lib/vagrant-azure/action.rb +++ b/lib/vagrant-azure/action.rb @@ -158,7 +158,7 @@ module VagrantPlugins def self.action_prepare_boot Vagrant::Action::Builder.new.tap do |b| b.use Provision - b.use SyncFolders + # b.use SyncFolders end end @@ -173,7 +173,8 @@ module VagrantPlugins if !env1[:result] b1.use Call, IsState, :StoppedDeallocated do |env2, b2| if env2[:result] - b2.use action_prepare_boot + # b2.use action_prepare_boot + b2.use Provision b2.use StartInstance # start this instance again b2.use Call, WaitForState, :ReadyRole do |env3, b3| if env3[:result] @@ -182,6 +183,7 @@ module VagrantPlugins 'vagrant_azure.vm_started', :name => $` ) b3.use WaitForCommunicate + b3.use SyncFolders end end else @@ -191,8 +193,9 @@ module VagrantPlugins end end else - b1.use action_prepare_boot + b1.use Provision b1.use RunInstance # Launch a new instance + b1.use SyncFolders b1.use Call, WaitForState, :ReadyRole do |env2, b2| if env2[:result] env2[:machine].id =~ /@/ diff --git a/lib/vagrant-azure/monkey_patch/machine.rb b/lib/vagrant-azure/monkey_patch/machine.rb index b0be839..5a3a7d0 100644 --- a/lib/vagrant-azure/monkey_patch/machine.rb +++ b/lib/vagrant-azure/monkey_patch/machine.rb @@ -11,7 +11,7 @@ module Vagrant def communicate unless @communicator if @config.vm.guest == :windows - @communicator = VagrantPlugins::VagrantHyperV::Communicator::PowerShell.new(self) + @communicator = VagrantPlugins::WinAzure::Communicator::PowerShell.new(self) else @communicator = original_communicate end From e1324a46099f5a3a5fa70ca7ada565c5f59e1510 Mon Sep 17 00:00:00 2001 From: Ramakrishnan Date: Thu, 24 Apr 2014 07:09:02 +0530 Subject: [PATCH 11/27] Fix check winrm running. Add guest port to create new ps session. --- lib/vagrant-azure/communication/powershell.rb | 1 - lib/vagrant-azure/scripts/check_winrm.ps1 | 6 ++++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/lib/vagrant-azure/communication/powershell.rb b/lib/vagrant-azure/communication/powershell.rb index d95cd88..177a5d6 100644 --- a/lib/vagrant-azure/communication/powershell.rb +++ b/lib/vagrant-azure/communication/powershell.rb @@ -2,7 +2,6 @@ # Copyright (c) Microsoft Open Technologies, Inc. # All Rights Reserved. Licensed under the Apache 2.0 License. #-------------------------------------------------------------------------- - module VagrantPlugins module WinAzure module Communicator diff --git a/lib/vagrant-azure/scripts/check_winrm.ps1 b/lib/vagrant-azure/scripts/check_winrm.ps1 index d76699a..dc716f9 100644 --- a/lib/vagrant-azure/scripts/check_winrm.ps1 +++ b/lib/vagrant-azure/scripts/check_winrm.ps1 @@ -2,10 +2,12 @@ # Copyright (c) Microsoft Open Technologies, Inc. # All Rights Reserved. Licensed under the Apache 2.0 License. #-------------------------------------------------------------------------- + param ( [string]$guest_ip = $(throw "-guest_ip is required."), [string]$username = $(throw "-guest_username is required."), - [string]$password = $(throw "-guest_password is required.") + [string]$password = $(throw "-guest_password is required."), + [string]$guest_port = $(throw "-guest_port is required") ) # Include the following modules @@ -14,7 +16,7 @@ $presentDir = Split-Path -parent $PSCommandPath . ([System.IO.Path]::Combine($presentDir, "utils\create_session.ps1")) try { - $response = Create-Remote-Session $guest_ip $username $password + $response = Create-Remote-Session $guest_ip $guest_port $username $password if (!$response["session"] -and $response["error"]) { Write-Host $response["error"] return From eec35a05cc1df9da21644f91772f12983f6b115a Mon Sep 17 00:00:00 2001 From: Ramakrishnan Date: Thu, 24 Apr 2014 18:20:23 +0530 Subject: [PATCH 12/27] Fix action ordering. Rearranged the action ordering for up and reload command. This also fixes the sync action getting called before the VM in up and running. --- lib/vagrant-azure/action.rb | 39 +++++++++--------------- lib/vagrant-azure/action/sync_folders.rb | 2 +- 2 files changed, 15 insertions(+), 26 deletions(-) diff --git a/lib/vagrant-azure/action.rb b/lib/vagrant-azure/action.rb index e14b5c9..36e59b6 100644 --- a/lib/vagrant-azure/action.rb +++ b/lib/vagrant-azure/action.rb @@ -65,7 +65,6 @@ module VagrantPlugins b2.use Message, I18n.t('vagrant_azure.not_created') next end - b2.use Provision end end @@ -158,7 +157,16 @@ module VagrantPlugins def self.action_prepare_boot Vagrant::Action::Builder.new.tap do |b| b.use Provision - # b.use SyncFolders + b.use SyncFolders + b.use Call, WaitForState, :ReadyRole do |env, b1| + if env[:result] + env[:machine].id =~ /@/ + b1.use Message, I18n.t( + 'vagrant_azure.vm_started', :name => $` + ) + b1.use WaitForCommunicate + end + end end end @@ -173,19 +181,9 @@ module VagrantPlugins if !env1[:result] b1.use Call, IsState, :StoppedDeallocated do |env2, b2| if env2[:result] - # b2.use action_prepare_boot - b2.use Provision + b2.use action_prepare_boot b2.use StartInstance # start this instance again - b2.use Call, WaitForState, :ReadyRole do |env3, b3| - if env3[:result] - env3[:machine].id =~ /@/ - b3.use Message, I18n.t( - 'vagrant_azure.vm_started', :name => $` - ) - b3.use WaitForCommunicate - b3.use SyncFolders - end - end + # b2.use action_boot_ready else b2.use Message, I18n.t( 'vagrant_azure.already_status', :status => 'created' @@ -193,18 +191,9 @@ module VagrantPlugins end end else - b1.use Provision b1.use RunInstance # Launch a new instance - b1.use SyncFolders - b1.use Call, WaitForState, :ReadyRole do |env2, b2| - if env2[:result] - env2[:machine].id =~ /@/ - b2.use Message, I18n.t( - 'vagrant_azure.vm_started', :name => $` - ) - b2.use WaitForCommunicate - end - end + b1.use action_prepare_boot + # b1.use action_boot_ready end end end diff --git a/lib/vagrant-azure/action/sync_folders.rb b/lib/vagrant-azure/action/sync_folders.rb index b30b619..5a2a257 100644 --- a/lib/vagrant-azure/action/sync_folders.rb +++ b/lib/vagrant-azure/action/sync_folders.rb @@ -26,6 +26,7 @@ module VagrantPlugins if env[:machine].config.vm.guest != :windows super else + @app.call(env) env[:machine].config.vm.synced_folders.each do |id, data| data = scoped_hash_override(data, :azure) @@ -53,7 +54,6 @@ module VagrantPlugins env[:machine].provider.driver.upload(hostpath, guestpath) end - @app.call(env) end end From e5664c0938a38022853b52aabbecaa5cec0cb7e8 Mon Sep 17 00:00:00 2001 From: Ramakrishnan Date: Fri, 25 Apr 2014 09:59:49 +0530 Subject: [PATCH 13/27] nil check for server. There is a bug in azure Ruby SDK which is not able to check the VM name when the name is in upper case and returns a nil. This check will raise an exception when the server object is nil. --- lib/vagrant-azure/errors.rb | 4 ++-- locales/en.yml | 5 ++--- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/lib/vagrant-azure/errors.rb b/lib/vagrant-azure/errors.rb index 6d76a15..d272df8 100644 --- a/lib/vagrant-azure/errors.rb +++ b/lib/vagrant-azure/errors.rb @@ -18,8 +18,8 @@ module VagrantPlugins error_key(:server_not_created) end - class CreateVMError < VagrantAzureError - error_key(:create_vm_error) + class CreateVMFailure < VagrantAzureError + error_key(:create_vm_failure) end end diff --git a/locales/en.yml b/locales/en.yml index 374eb75..56ae76f 100644 --- a/locales/en.yml +++ b/locales/en.yml @@ -32,6 +32,5 @@ en: to communicate to the remote machine. Please check the same and try again. server_not_created: |- Server not created. Error is: %{message} - create_vm_error: |- - Failed to create a VM in azure cloud with the following message. - %{message} + create_vm_failure: |- + There was some error in creating the VM. From dcb813e70aaf57f4f078840c1792a87bcc5e6687 Mon Sep 17 00:00:00 2001 From: Ramakrishnan Date: Fri, 25 Apr 2014 12:05:14 +0530 Subject: [PATCH 14/27] Add few exception handling Exception handling for azure SocketError on slow network. --- lib/vagrant-azure/action/connect_azure.rb | 13 +++++- lib/vagrant-azure/action/read_ssh_info.rb | 15 +----- .../action/vagrant_azure_service.rb | 46 +++++++++++++++++++ lib/vagrant-azure/errors.rb | 2 +- 4 files changed, 60 insertions(+), 16 deletions(-) create mode 100644 lib/vagrant-azure/action/vagrant_azure_service.rb diff --git a/lib/vagrant-azure/action/connect_azure.rb b/lib/vagrant-azure/action/connect_azure.rb index 644290a..37deda0 100644 --- a/lib/vagrant-azure/action/connect_azure.rb +++ b/lib/vagrant-azure/action/connect_azure.rb @@ -5,6 +5,11 @@ require 'azure' require 'log4r' +# FIXME: +# This is a required to patch few exception handling which are not done in +# Azure Ruby SDK +require_relative "vagrant_azure_service" + module VagrantPlugins module WinAzure module Action @@ -25,7 +30,13 @@ module VagrantPlugins c.storage_access_key = config.storage_access_key end - env[:azure_vm_service] = Azure::VirtualMachineManagementService.new + # FIXME: + # Defining a new class VagrantAzureService + # Here we call the native azure virtual machine management service method + # and add some exception handling. + # Remove this once the Azure SDK adds the exception handling for the + # methods defined in VagrantAzureService + env[:azure_vm_service] = VagrantAzureService.new(Azure::VirtualMachineManagementService.new, env) @app.call(env) end diff --git a/lib/vagrant-azure/action/read_ssh_info.rb b/lib/vagrant-azure/action/read_ssh_info.rb index d1c52dd..6dcd837 100644 --- a/lib/vagrant-azure/action/read_ssh_info.rb +++ b/lib/vagrant-azure/action/read_ssh_info.rb @@ -28,20 +28,7 @@ module VagrantPlugins def read_ssh_info(azure, machine) return nil if machine.id.nil? machine.id =~ /@/ - vm = nil - attempt = 0 - while true - begin - vm = azure.get_virtual_machine($`, $') - rescue SocketError - attempt = attempt + 1 - env[:ui].info(I18n.t("vagrant_azure.read_attempt", - :attempt => attempt)) - sleep 5 - next if attempt < 3 - end - break - end + vm = azure.get_virtual_machine($`, $') if vm.nil? || !vm.instance_of?(Azure::VirtualMachineManagement::VirtualMachine) # Machine cannot be found diff --git a/lib/vagrant-azure/action/vagrant_azure_service.rb b/lib/vagrant-azure/action/vagrant_azure_service.rb new file mode 100644 index 0000000..2d6af5a --- /dev/null +++ b/lib/vagrant-azure/action/vagrant_azure_service.rb @@ -0,0 +1,46 @@ +#--------------------------------------------------------------------------- +# Copyright (c) Microsoft Open Technologies, Inc. +# All Rights Reserved. Licensed under the Apache 2.0 License. +#-------------------------------------------------------------------------- +# FIXME: +# This is a stop gap arrangement until the azure ruby SDK fixes the exceptions +# and gracefully fails. + +module VagrantPlugins + module WinAzure + module Action + class VagrantAzureService + attr_reader :azure + def initialize(azure, env) + @azure = azure + @env = env + end + + # At times due to network latency the SDK raises SocketError, this can + # be rescued and re-try for three attempts. + def get_virtual_machine(*args) + vm = nil + attempt = 0 + while true + begin + vm = azure.get_virtual_machine(*args) + rescue SocketError + attempt = attempt + 1 + @env[:ui].info (I18n.t("vagrant_azure.read_attempt", + :attempt => attempt)) + sleep 5 + next if attempt < 3 + end + break + end + vm + end + + def method_missing(method, *args, &block) + azure.send(method, *args, &block) + end + + end + end + end +end diff --git a/lib/vagrant-azure/errors.rb b/lib/vagrant-azure/errors.rb index d272df8..8544705 100644 --- a/lib/vagrant-azure/errors.rb +++ b/lib/vagrant-azure/errors.rb @@ -18,7 +18,7 @@ module VagrantPlugins error_key(:server_not_created) end - class CreateVMFailure < VagrantAzureError + class CreateVMFailure < WinAzureError error_key(:create_vm_failure) end From 062b1b3e2bf03935ae448c0ae4cd9ce3b172a277 Mon Sep 17 00:00:00 2001 From: Ramakrishnan Date: Fri, 25 Apr 2014 14:27:50 +0530 Subject: [PATCH 15/27] WinRm error handling. Better exception handling for WinRM errors. --- lib/vagrant-azure/action.rb | 8 +++----- lib/vagrant-azure/action/connect_azure.rb | 2 +- lib/vagrant-azure/action/vagrant_azure_service.rb | 5 +---- lib/vagrant-azure/communication/powershell.rb | 9 +++++++-- lib/vagrant-azure/scripts/check_winrm.ps1 | 8 ++++++-- locales/en.yml | 5 +++-- 6 files changed, 21 insertions(+), 16 deletions(-) diff --git a/lib/vagrant-azure/action.rb b/lib/vagrant-azure/action.rb index 36e59b6..792cedc 100644 --- a/lib/vagrant-azure/action.rb +++ b/lib/vagrant-azure/action.rb @@ -156,8 +156,6 @@ module VagrantPlugins def self.action_prepare_boot Vagrant::Action::Builder.new.tap do |b| - b.use Provision - b.use SyncFolders b.use Call, WaitForState, :ReadyRole do |env, b1| if env[:result] env[:machine].id =~ /@/ @@ -165,6 +163,8 @@ module VagrantPlugins 'vagrant_azure.vm_started', :name => $` ) b1.use WaitForCommunicate + b1.use Provision + b1.use SyncFolders end end end @@ -181,9 +181,8 @@ module VagrantPlugins if !env1[:result] b1.use Call, IsState, :StoppedDeallocated do |env2, b2| if env2[:result] - b2.use action_prepare_boot b2.use StartInstance # start this instance again - # b2.use action_boot_ready + b2.use action_prepare_boot else b2.use Message, I18n.t( 'vagrant_azure.already_status', :status => 'created' @@ -193,7 +192,6 @@ module VagrantPlugins else b1.use RunInstance # Launch a new instance b1.use action_prepare_boot - # b1.use action_boot_ready end end end diff --git a/lib/vagrant-azure/action/connect_azure.rb b/lib/vagrant-azure/action/connect_azure.rb index 37deda0..40cad7c 100644 --- a/lib/vagrant-azure/action/connect_azure.rb +++ b/lib/vagrant-azure/action/connect_azure.rb @@ -36,7 +36,7 @@ module VagrantPlugins # and add some exception handling. # Remove this once the Azure SDK adds the exception handling for the # methods defined in VagrantAzureService - env[:azure_vm_service] = VagrantAzureService.new(Azure::VirtualMachineManagementService.new, env) + env[:azure_vm_service] = VagrantAzureService.new(Azure::VirtualMachineManagementService.new) @app.call(env) end diff --git a/lib/vagrant-azure/action/vagrant_azure_service.rb b/lib/vagrant-azure/action/vagrant_azure_service.rb index 2d6af5a..5423c6c 100644 --- a/lib/vagrant-azure/action/vagrant_azure_service.rb +++ b/lib/vagrant-azure/action/vagrant_azure_service.rb @@ -11,9 +11,8 @@ module VagrantPlugins module Action class VagrantAzureService attr_reader :azure - def initialize(azure, env) + def initialize(azure) @azure = azure - @env = env end # At times due to network latency the SDK raises SocketError, this can @@ -26,8 +25,6 @@ module VagrantPlugins vm = azure.get_virtual_machine(*args) rescue SocketError attempt = attempt + 1 - @env[:ui].info (I18n.t("vagrant_azure.read_attempt", - :attempt => attempt)) sleep 5 next if attempt < 3 end diff --git a/lib/vagrant-azure/communication/powershell.rb b/lib/vagrant-azure/communication/powershell.rb index 177a5d6..d9dd381 100644 --- a/lib/vagrant-azure/communication/powershell.rb +++ b/lib/vagrant-azure/communication/powershell.rb @@ -2,6 +2,7 @@ # Copyright (c) Microsoft Open Technologies, Inc. # All Rights Reserved. Licensed under the Apache 2.0 License. #-------------------------------------------------------------------------- + module VagrantPlugins module WinAzure module Communicator @@ -21,8 +22,12 @@ module VagrantPlugins if !@winrm_status status = false response = @machine.provider.driver.check_winrm - @winrm_status = response && response["message"] == "Running" - raise Errors::WinRMNotReady if !@winrm_status + message = nil + if response && response["message"] + message = response["message"] + @winrm_status = message == "Running" + end + raise Errors::WinRMNotReady, message: message if !@winrm_status end @winrm_status end diff --git a/lib/vagrant-azure/scripts/check_winrm.ps1 b/lib/vagrant-azure/scripts/check_winrm.ps1 index dc716f9..8bb1804 100644 --- a/lib/vagrant-azure/scripts/check_winrm.ps1 +++ b/lib/vagrant-azure/scripts/check_winrm.ps1 @@ -18,8 +18,12 @@ $presentDir = Split-Path -parent $PSCommandPath try { $response = Create-Remote-Session $guest_ip $guest_port $username $password if (!$response["session"] -and $response["error"]) { - Write-Host $response["error"] - return + $session_message = $response['error'] + $resultHash = @{ + message = "$session_message" + } + Write-Output-Message $resultHash + return } function Remote-Execute() { $winrm_state = "" diff --git a/locales/en.yml b/locales/en.yml index 56ae76f..e65c5f6 100644 --- a/locales/en.yml +++ b/locales/en.yml @@ -28,8 +28,9 @@ en: Failed to connect to Virtual Machine. Re-Connecting %{attempt} errors: win_rm_not_ready: |- - WinRM is not enabled in the remot guest. Vagrant-Windows-HyperV uses remote PowerShell scripts - to communicate to the remote machine. Please check the same and try again. + Vagrant failed to communicate to the VM using powershell. The operation failed + with the following message. + %{message} server_not_created: |- Server not created. Error is: %{message} create_vm_failure: |- From 2b935f1af3e8b5cf46a37a1715dfd38b37597ac7 Mon Sep 17 00:00:00 2001 From: Ramakrishnan Date: Fri, 25 Apr 2014 14:29:43 +0530 Subject: [PATCH 16/27] New CHANGELOG file. --- CHANGELOG.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 CHANGELOG.md diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..cb73d9f --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,16 @@ +## 1.0.5 (Unreleased) + +FEATURES + + - Provision for windows VM. Windows VM has to be specifically mentioned in the + Vagrantfile with `config.vm.guest = :windows` + - SyncedFolders for VM, `rsync` for Linux VM and `powershelll` file copy from Windows. + - Chef, Puppet and Shell provision for Linux and Windows VM. + +IMPROVEMENTS + + - Cleaned up few typo in README + - Better exception handling when VM fails to get created in cloud. + +# Previous +See git commits From 1809242f5bdead123b58c5dd692a3bf0c0b5f7a8 Mon Sep 17 00:00:00 2001 From: Ramakrishnan Date: Fri, 25 Apr 2014 14:33:04 +0530 Subject: [PATCH 17/27] Update Changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index cb73d9f..144276d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ IMPROVEMENTS - Cleaned up few typo in README - Better exception handling when VM fails to get created in cloud. + - Better exception handling for WinRM session errors. # Previous See git commits From 2758fb4569b79f44088adada9cb9a57316384512 Mon Sep 17 00:00:00 2001 From: Ramakrishnan Date: Fri, 25 Apr 2014 15:12:44 +0530 Subject: [PATCH 18/27] Remove plugin group. --- Gemfile | 4 ---- 1 file changed, 4 deletions(-) diff --git a/Gemfile b/Gemfile index c5d5e33..5ed8e32 100644 --- a/Gemfile +++ b/Gemfile @@ -13,7 +13,3 @@ group :development do # Vagrant environment itself using `vagrant plugin`. gem "vagrant", :git => "git://github.com/mitchellh/vagrant.git" end - -group :plugins do - gem 'vagrant-azure', path: '.' -end From 535a3972cebc8e9c29aeb379e40d1c3967defa37 Mon Sep 17 00:00:00 2001 From: Ramakrishnan Date: Mon, 28 Apr 2014 11:11:47 +0530 Subject: [PATCH 19/27] Update CHANGELOG.md --- CHANGELOG.md | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 144276d..fa58900 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,16 +2,22 @@ FEATURES - - Provision for windows VM. Windows VM has to be specifically mentioned in the - Vagrantfile with `config.vm.guest = :windows` - - SyncedFolders for VM, `rsync` for Linux VM and `powershelll` file copy from Windows. - - Chef, Puppet and Shell provision for Linux and Windows VM. +- Provision for windows VM. +- Windows VM has to be specifically mentioned in the Vagrantfile with + `config.vm.guest = :windows` +- Chef, Puppet and Shell provision for Linux and Windows VM. +- **SyncedFolders** +- Linux VM uses `rsync` and has be mentioned in the VagrantFile. +- Windows VM will default to use PowerShell to copy files. IMPROVEMENTS - - Cleaned up few typo in README - Better exception handling when VM fails to get created in cloud. - Better exception handling for WinRM session errors. -# Previous +BUGFIXES + + - Cleaned up few typo in README + +## Previous See git commits From b5e487cd3ffc4f8fea794a943d3c567b2525df8e Mon Sep 17 00:00:00 2001 From: Ramakrishnan Date: Mon, 28 Apr 2014 14:29:00 +0530 Subject: [PATCH 20/27] Fix machine.rb define ssh communicate method for non windows vm. --- lib/vagrant-azure/monkey_patch/machine.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/vagrant-azure/monkey_patch/machine.rb b/lib/vagrant-azure/monkey_patch/machine.rb index 5a3a7d0..c076c21 100644 --- a/lib/vagrant-azure/monkey_patch/machine.rb +++ b/lib/vagrant-azure/monkey_patch/machine.rb @@ -6,9 +6,9 @@ module Vagrant class Machine - alias_method :original_communicate, :communicate + ssh_communicate = instance_method(:communicate) - def communicate + define_method(:communicate) do unless @communicator if @config.vm.guest == :windows @communicator = VagrantPlugins::WinAzure::Communicator::PowerShell.new(self) From 88c93fcdf10a1dba1f8d23dda6e255b8c7f4247c Mon Sep 17 00:00:00 2001 From: Ramakrishnan Date: Mon, 28 Apr 2014 15:07:39 +0530 Subject: [PATCH 21/27] Call original ssh_communicate for non windows vm --- lib/vagrant-azure/monkey_patch/machine.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/vagrant-azure/monkey_patch/machine.rb b/lib/vagrant-azure/monkey_patch/machine.rb index c076c21..d60aa0f 100644 --- a/lib/vagrant-azure/monkey_patch/machine.rb +++ b/lib/vagrant-azure/monkey_patch/machine.rb @@ -13,7 +13,7 @@ module Vagrant if @config.vm.guest == :windows @communicator = VagrantPlugins::WinAzure::Communicator::PowerShell.new(self) else - @communicator = original_communicate + @communicator = ssh_communicate.bind(self).() end end @communicator From 0f532ba01c1b77567d9bb16f3834b14358e6c178 Mon Sep 17 00:00:00 2001 From: Ramakrishnan Date: Mon, 28 Apr 2014 15:08:21 +0530 Subject: [PATCH 22/27] Use vagrant from tag 1.5.4 for development. --- Gemfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile b/Gemfile index 5ed8e32..79ae4b0 100644 --- a/Gemfile +++ b/Gemfile @@ -11,5 +11,5 @@ group :development do # We depend on Vagrant for development, but we don't add it as a # gem dependency because we expect to be installed within the # Vagrant environment itself using `vagrant plugin`. - gem "vagrant", :git => "git://github.com/mitchellh/vagrant.git" + gem "vagrant", :git => "git://github.com/mitchellh/vagrant.git", :tag => "V1.5.4" end From 4672904d8c4edd8a438516e3e0f3c4bce50a2eb4 Mon Sep 17 00:00:00 2001 From: Ramakrishnan Date: Thu, 8 May 2014 15:42:54 +0530 Subject: [PATCH 23/27] Fix typo in config --- lib/vagrant-azure/config.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/vagrant-azure/config.rb b/lib/vagrant-azure/config.rb index 9cde641..a1d54ef 100644 --- a/lib/vagrant-azure/config.rb +++ b/lib/vagrant-azure/config.rb @@ -96,7 +96,7 @@ module VagrantPlugins @state_read_timeout = 360 if @state_read_timeout == UNSET_VALUE - # This done due to a bug in Ruby SDK - it doesn't generate a storage + # This done due to a bug in Ruby SDK - it doesn't generate a storage # account name if add_role = true if @storage_acct_name.nil? || @storage_acct_name.empty? @storage_acct_name = Azure::Core::Utility.random_string( @@ -119,7 +119,7 @@ module VagrantPlugins self.mgmt_endpoint result.subscription_id = other.subscription_id || \ self.subscription_id - result.storage_account_name = other.storage_acct_name || \ + result.storage_acct_name = other.storage_acct_name || \ self.storage_acct_name result.storage_access_key = other.storage_access_key || \ self.storage_access_key From 8d41c5b9d48a227072a17cfb0b736ad72e49f341 Mon Sep 17 00:00:00 2001 From: Ramakrishnan Date: Thu, 8 May 2014 15:43:33 +0530 Subject: [PATCH 24/27] Use vagrant from master for development. --- Gemfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile b/Gemfile index 79ae4b0..e8468e3 100644 --- a/Gemfile +++ b/Gemfile @@ -11,5 +11,5 @@ group :development do # We depend on Vagrant for development, but we don't add it as a # gem dependency because we expect to be installed within the # Vagrant environment itself using `vagrant plugin`. - gem "vagrant", :git => "git://github.com/mitchellh/vagrant.git", :tag => "V1.5.4" + gem "vagrant", :git => "git://github.com/mitchellh/vagrant.git", end From 67266e7ff4706298d857da8e14b91224e0eb840a Mon Sep 17 00:00:00 2001 From: Ramakrishnan Date: Fri, 9 May 2014 10:23:36 +0530 Subject: [PATCH 25/27] Update CHANGELOG --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index fa58900..3e18133 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,7 @@ IMPROVEMENTS BUGFIXES - Cleaned up few typo in README + - Compatible with Vagrant 1.6 [GH-15] ## Previous See git commits From d2db08438794131ccf5b962b269e12c80717e90a Mon Sep 17 00:00:00 2001 From: Ramakrishnan Date: Fri, 9 May 2014 10:37:41 +0530 Subject: [PATCH 26/27] Typo fix in Gemfile --- Gemfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile b/Gemfile index e8468e3..5ed8e32 100644 --- a/Gemfile +++ b/Gemfile @@ -11,5 +11,5 @@ group :development do # We depend on Vagrant for development, but we don't add it as a # gem dependency because we expect to be installed within the # Vagrant environment itself using `vagrant plugin`. - gem "vagrant", :git => "git://github.com/mitchellh/vagrant.git", + gem "vagrant", :git => "git://github.com/mitchellh/vagrant.git" end From a17334d98559cc0c776a5ad397f4b48d71eebbac Mon Sep 17 00:00:00 2001 From: Ramakrishnan Date: Fri, 9 May 2014 12:09:37 +0530 Subject: [PATCH 27/27] Bump version --- lib/vagrant-azure/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/vagrant-azure/version.rb b/lib/vagrant-azure/version.rb index 30f0974..612a84c 100644 --- a/lib/vagrant-azure/version.rb +++ b/lib/vagrant-azure/version.rb @@ -5,6 +5,6 @@ module VagrantPlugins module WinAzure - VERSION = '1.0.5.pre' + VERSION = '1.0.5' end end