Merge branch 'jlm/sync_folder'

This commit is contained in:
Ramakrishnan 2014-05-09 12:31:51 +05:30
Родитель 3ee7813917 a17334d985
Коммит d15326a4b0
17 изменённых файлов: 319 добавлений и 56 удалений

24
CHANGELOG.md Normal file
Просмотреть файл

@ -0,0 +1,24 @@
## 1.0.5 (Unreleased)
FEATURES
- 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
- Better exception handling when VM fails to get created in cloud.
- Better exception handling for WinRM session errors.
BUGFIXES
- Cleaned up few typo in README
- Compatible with Vagrant 1.6 [GH-15]
## Previous
See git commits

Просмотреть файл

@ -13,7 +13,3 @@ group :development do
# Vagrant environment itself using `vagrant plugin`. # 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 end
group :plugins do
gem 'vagrant-azure', path: '.'
end

Просмотреть файл

@ -12,10 +12,17 @@ module VagrantPlugins
autoload :Errors, lib_path.join('errors') autoload :Errors, lib_path.join('errors')
autoload :Driver, lib_path.join('driver') 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/puppet')
require lib_path.join('provisioner/chef-solo') require lib_path.join('provisioner/chef-solo')
require lib_path.join('provisioner/shell') 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. # This returns the path to the source of this plugin.
# #
# @return [Pathname] # @return [Pathname]

Просмотреть файл

@ -65,9 +65,7 @@ module VagrantPlugins
b2.use Message, I18n.t('vagrant_azure.not_created') b2.use Message, I18n.t('vagrant_azure.not_created')
next next
end end
b2.use Provision b2.use Provision
# b2.use SyncFolders
end end
end end
end end
@ -158,32 +156,33 @@ module VagrantPlugins
def self.action_prepare_boot def self.action_prepare_boot
Vagrant::Action::Builder.new.tap do |b| Vagrant::Action::Builder.new.tap do |b|
b.use Provision b.use Call, WaitForState, :ReadyRole do |env, b1|
# b.use SyncFolders if env[:result]
# b.use WarnNetworks env[:machine].id =~ /@/
b1.use Message, I18n.t(
'vagrant_azure.vm_started', :name => $`
)
b1.use WaitForCommunicate
b1.use Provision
b1.use SyncFolders
end
end
end end
end end
# This action is called to bring the box up from nothing # This action is called to bring the box up from nothing
def self.action_up def self.action_up
Vagrant::Action::Builder.new.tap do |b| Vagrant::Action::Builder.new.tap do |b|
b.use HandleBoxUrl b.use HandleBox
b.use ConfigValidate b.use ConfigValidate
b.use ConnectAzure b.use ConnectAzure
b.use Call, IsState, :NotCreated do |env1, b1| b.use Call, IsState, :NotCreated do |env1, b1|
if !env1[:result] if !env1[:result]
b1.use Call, IsState, :StoppedDeallocated do |env2, b2| b1.use Call, IsState, :StoppedDeallocated do |env2, b2|
if env2[:result] if env2[:result]
b2.use action_prepare_boot
b2.use StartInstance # start this instance again b2.use StartInstance # start this instance again
b2.use Call, WaitForState, :ReadyRole do |env3, b3| b2.use action_prepare_boot
if env3[:result]
env3[:machine].id =~ /@/
b3.use Message, I18n.t(
'vagrant_azure.vm_started', :name => $`
)
end
end
else else
b2.use Message, I18n.t( b2.use Message, I18n.t(
'vagrant_azure.already_status', :status => 'created' 'vagrant_azure.already_status', :status => 'created'
@ -192,14 +191,7 @@ module VagrantPlugins
end end
else else
b1.use RunInstance # Launch a new instance b1.use RunInstance # Launch a new instance
b1.use Call, WaitForState, :ReadyRole do |env2, b2| b1.use action_prepare_boot
if env2[:result]
env2[:machine].id =~ /@/
b2.use Message, I18n.t(
'vagrant_azure.vm_started', :name => $`
)
end
end
end end
end end
end end
@ -240,11 +232,12 @@ module VagrantPlugins
autoload :RunInstance, action_root.join('run_instance') autoload :RunInstance, action_root.join('run_instance')
autoload :StartInstance, action_root.join('start_instance') autoload :StartInstance, action_root.join('start_instance')
autoload :StopInstance, action_root.join('stop_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 :TerminateInstance, action_root.join('terminate_instance')
# autoload :TimedProvision, action_root.join('timed_provision') # autoload :TimedProvision, action_root.join('timed_provision')
# autoload :WarnNetworks, action_root.join('warn_networks') # autoload :WarnNetworks, action_root.join('warn_networks')
autoload :WaitForState, action_root.join('wait_for_state') autoload :WaitForState, action_root.join('wait_for_state')
autoload :WaitForCommunicate, action_root.join('wait_for_communicate')
end end
end end
end end

Просмотреть файл

@ -5,6 +5,11 @@
require 'azure' require 'azure'
require 'log4r' 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 VagrantPlugins
module WinAzure module WinAzure
module Action module Action
@ -17,11 +22,6 @@ module VagrantPlugins
def call (env) def call (env)
config = env[:machine].provider_config 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}]"
Azure.configure do |c| Azure.configure do |c|
c.subscription_id = config.subscription_id c.subscription_id = config.subscription_id
c.management_certificate = config.mgmt_certificate c.management_certificate = config.mgmt_certificate
@ -30,7 +30,13 @@ module VagrantPlugins
c.storage_access_key = config.storage_access_key c.storage_access_key = config.storage_access_key
end 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)
@app.call(env) @app.call(env)
end end

Просмотреть файл

@ -102,8 +102,7 @@ module VagrantPlugins
# machine as a string. # machine as a string.
if server.instance_of? String if server.instance_of? String
env[:ui].info "Server not created. Error is: #{server}" raise Errors::ServerNotCreated, message: server
raise Errors::CreateVMError, message: "#{server}"
end end
env[:machine].id = "#{server.vm_name}@#{server.cloud_service_name}" env[:machine].id = "#{server.vm_name}@#{server.cloud_service_name}"

Просмотреть файл

@ -0,0 +1,63 @@
# 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"
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 < Vagrant::Action::Builtin::SyncedFolders
include Vagrant::Util::ScopedHashOverride
def initialize(app, env)
@app = app
@logger = Log4r::Logger.new("vagrant_azure::action::sync_folders")
end
def call(env)
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)
# Ignore disabled shared folders
next if data[:disabled]
hostpath = File.expand_path(data[:hostpath], env[:root_path])
guestpath = data[:guestpath]
env[:ui].info(I18n.t("vagrant_azure.copy_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
env[:machine].provider.driver.upload(hostpath, guestpath)
end
end
end
end
end
end
end

Просмотреть файл

@ -0,0 +1,43 @@
#---------------------------------------------------------------------------
# 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)
@azure = azure
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
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

Просмотреть файл

@ -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

Просмотреть файл

@ -0,0 +1,41 @@
#-------------------------------------------------------------------------
# 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
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
def test(command, opts=nil)
true
end
end
end
end
end

Просмотреть файл

@ -119,7 +119,7 @@ module VagrantPlugins
self.mgmt_endpoint self.mgmt_endpoint
result.subscription_id = other.subscription_id || \ result.subscription_id = other.subscription_id || \
self.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 self.storage_acct_name
result.storage_access_key = other.storage_access_key || \ result.storage_access_key = other.storage_access_key || \
self.storage_access_key self.storage_access_key

Просмотреть файл

@ -60,6 +60,11 @@ module VagrantPlugins
execute(script_path, options) execute(script_path, options)
end end
def check_winrm
script_path = local_script_path('check_winrm.ps1')
execute(script_path, remote_credentials)
end
protected protected
def local_script_path(path) def local_script_path(path)

Просмотреть файл

@ -1,21 +1,27 @@
#-------------------------------------------------------------------------- #-------------------------------------------------------------------------
# Copyright (c) Microsoft Open Technologies, Inc. # Copyright (c) Microsoft Open Technologies, Inc.
# All Rights Reserved. Licensed under the Apache 2.0 License. # All Rights Reserved. Licensed under the Apache 2.0 License.
#-------------------------------------------------------------------------- #--------------------------------------------------------------------------
module VagrantPlugins module VagrantPlugins
module WinAzure module WinAzure
module Errors module Errors
class VagrantAzureError < Vagrant::Errors::VagrantError class WinAzureError < Vagrant::Errors::VagrantError
error_namespace("vagrant_azure.errors") error_namespace("vagrant_azure.errors")
end end
class CreateVMFailure < VagrantAzureError class WinRMNotReady < WinAzureError
error_key(:win_rm_not_ready)
end
class ServerNotCreated < WinAzureError
error_key(:server_not_created)
end
class CreateVMFailure < WinAzureError
error_key(:create_vm_failure) error_key(:create_vm_failure)
end end
class CreateVMError < VagrantAzureError
error_key(:create_vm_error)
end
end end
end end
end end

Просмотреть файл

@ -0,0 +1,22 @@
#-------------------------------------------------------------------------
# Copyright (c) Microsoft Open Technologies, Inc.
# All Rights Reserved. Licensed under the Apache 2.0 License.
#--------------------------------------------------------------------------
module Vagrant
class Machine
ssh_communicate = instance_method(:communicate)
define_method(:communicate) do
unless @communicator
if @config.vm.guest == :windows
@communicator = VagrantPlugins::WinAzure::Communicator::PowerShell.new(self)
else
@communicator = ssh_communicate.bind(self).()
end
end
@communicator
end
end
end

Просмотреть файл

@ -2,10 +2,12 @@
# Copyright (c) Microsoft Open Technologies, Inc. # Copyright (c) Microsoft Open Technologies, Inc.
# All Rights Reserved. Licensed under the Apache 2.0 License. # All Rights Reserved. Licensed under the Apache 2.0 License.
#-------------------------------------------------------------------------- #--------------------------------------------------------------------------
param ( param (
[string]$guest_ip = $(throw "-guest_ip is required."), [string]$guest_ip = $(throw "-guest_ip is required."),
[string]$username = $(throw "-guest_username 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 # Include the following modules
@ -14,9 +16,13 @@ $presentDir = Split-Path -parent $PSCommandPath
. ([System.IO.Path]::Combine($presentDir, "utils\create_session.ps1")) . ([System.IO.Path]::Combine($presentDir, "utils\create_session.ps1"))
try { 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"]) { if (!$response["session"] -and $response["error"]) {
Write-Host $response["error"] $session_message = $response['error']
$resultHash = @{
message = "$session_message"
}
Write-Output-Message $resultHash
return return
} }
function Remote-Execute() { function Remote-Execute() {
@ -34,7 +40,7 @@ try {
} catch { } catch {
$errortHash = @{ $errortHash = @{
type = "PowerShellError" type = "PowerShellError"
error ="Failed to copy file $_" error ="$_"
} }
Write-Error-Message $errortHash Write-Error-Message $errortHash
return return

Просмотреть файл

@ -5,6 +5,6 @@
module VagrantPlugins module VagrantPlugins
module WinAzure module WinAzure
VERSION = '1.0.4' VERSION = '1.0.5'
end end
end end

Просмотреть файл

@ -14,10 +14,24 @@ en:
VM '%{name}' has been started VM '%{name}' has been started
vm_stopped: |- vm_stopped: |-
VM '%{name}' has been stopped VM '%{name}' has been stopped
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.
waiting_for_ssh: |-
Waiting for SSH
ssh_ready: |-
SSH Ready
read_attempt: |-
Failed to connect to Virtual Machine. Re-Connecting %{attempt}
errors: errors:
create_vm_failure: |- win_rm_not_ready: |-
There was some error in creating the VM, this could be due to some network latency. Vagrant failed to communicate to the VM using powershell. The operation failed
Generally the next vagrant up command should fix this error. with the following message.
create_vm_error: |-
Failed to create a VM in azure cloud with the following message.
%{message} %{message}
server_not_created: |-
Server not created. Error is: %{message}
create_vm_failure: |-
There was some error in creating the VM.