start, stop, terminate, read_state and auth good

This commit is contained in:
David Justice 2016-03-27 15:09:37 -07:00
Родитель e2b639beb9
Коммит 9411b2da4d
13 изменённых файлов: 268 добавлений и 81 удалений

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

@ -10,7 +10,7 @@ module VagrantPlugins
end end
def call(env) def call(env)
env[:result] = env[:machine].state.id != :stopped env[:result] = env[:machine].state.id == :stopped
@app.call(env) @app.call(env)
end end
end end

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

@ -2,11 +2,14 @@
# Copyright (c) Microsoft Corporation. All rights reserved. # Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License. See License in the project root for license information. # Licensed under the MIT License. See License in the project root for license information.
require 'log4r' require 'log4r'
require 'vagrant-azure/util/machine_id_helper'
module VagrantPlugins module VagrantPlugins
module Azure module Azure
module Action module Action
class ReadSSHInfo class ReadSSHInfo
include VagrantPlugins::Azure::Util::MachineIdHelper
def initialize(app, env, port = 22) def initialize(app, env, port = 22)
@app = app @app = app
@port = port @port = port
@ -14,45 +17,16 @@ module VagrantPlugins
end end
def call(env) def call(env)
env[:ui].detail "Looking for local port #{@port}" env[:machine_ssh_info] = read_ssh_info(env[:azure_arm_service], env)
env[:machine_ssh_info] = read_ssh_info(
env[:azure_arm_service],
env
)
env[:ui].detail "Found port mapping #{env[:machine_ssh_info][:port]} --> #{@port}"
@app.call(env) @app.call(env)
end end
def read_ssh_info(azure, env) def read_ssh_info(azure, env)
return nil if env[:machine].id.nil? return nil if env[:machine].id.nil?
resource_group_name, vm_name = env[:machine].id.split(':') parsed = parse_machine_id(env[:machine].id)
vm = azure.compute.virtual_machines.get(resource_group_name, vm_name, 'instanceView').value!.body public_ip = azure.network.public_ipaddresses.get(parsed[:group], "#{parsed[:name]}-vagrantPublicIP").value!.body
if vm.nil? {:host => public_ip.properties.dns_settings.fqdn, :port => 22}
# Machine cannot be found
@logger.info 'Machine not found. Assuming it was destroyed and cleaning up environment'
terminate(env)
return nil
end
# vm.tcp_endpoints.each do |endpoint|
# if endpoint[:local_port] == "#{@port}"
# return { :host => "#{vm.cloud_service_name}.cloudapp.net", :port => endpoint[:public_port] }
# end
# end
return nil
end
def terminate(env)
destroy_env = env.dup
destroy_env.delete(:interrupted)
destroy_env[:config_validate] = false
destroy_env[:force_confirm_destroy] = true
env[:action_runner].run(Action.action_destroy, destroy_env)
end end
end end
end end

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

@ -2,32 +2,36 @@
# Copyright (c) Microsoft Corporation. All rights reserved. # Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License. See License in the project root for license information. # Licensed under the MIT License. See License in the project root for license information.
require 'log4r' require 'log4r'
require 'vagrant-azure/util/vm_status_translator'
require 'vagrant-azure/util/machine_id_helper'
module VagrantPlugins module VagrantPlugins
module Azure module Azure
module Action module Action
class ReadState class ReadState
include VagrantPlugins::Azure::Util::VMStatusTranslator
include VagrantPlugins::Azure::Util::MachineIdHelper
def initialize(app, env) def initialize(app, env)
@app = app @app = app
@logger = Log4r::Logger.new('vagrant_azure::action::read_state') @logger = Log4r::Logger.new('vagrant_azure::action::read_state')
end end
def call(env) def call(env)
env[:machine_state_id] = read_state(env[:azure_arm_service], env) env[:machine_state_id] = read_state(env[:azure_arm_service], env[:machine])
@app.call(env) @app.call(env)
end end
def read_state(azure, env) def read_state(azure, machine)
machine = env[:machine]
return :not_created if machine.id.nil? return :not_created if machine.id.nil?
# Find the machine # Find the machine
rg_name, vm_name = machine.id.split(':') parsed = parse_machine_id(machine.id)
vm = nil vm = nil
begin begin
vm = azure.compute.virtual_machines.get(rg_name, vm_name, 'instanceView').value!.body vm = azure.compute.virtual_machines.get(parsed[:group], parsed[:name], 'instanceView').value!.body
rescue MsRestAzure::AzureOperationError => ex rescue MsRestAzure::AzureOperationError => ex
if vm.nil? || [:'shutting-down', :terminated].include?(vm.state.to_sym) if vm.nil? || tearing_down?(vm.properties.instance_view.statuses)
# The machine can't be found # The machine can't be found
@logger.info('Machine not found or terminated, assuming it got destroyed.') @logger.info('Machine not found or terminated, assuming it got destroyed.')
machine.id = nil machine.id = nil
@ -36,7 +40,7 @@ module VagrantPlugins
end end
# Return the state # Return the state
vm.state.to_sym power_state(vm.properties.instance_view.statuses)
end end
end end

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

@ -2,22 +2,24 @@
# Copyright (c) Microsoft Corporation. All rights reserved. # Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License. See License in the project root for license information. # Licensed under the MIT License. See License in the project root for license information.
require 'log4r' require 'log4r'
require 'vagrant-azure/util/machine_id_helper'
module VagrantPlugins module VagrantPlugins
module Azure module Azure
module Action module Action
class RestartVM class RestartVM
include VagrantPlugins::Azure::Util::MachineIdHelper
def initialize(app, env) def initialize(app, env)
@app = app @app = app
@logger = Log4r::Logger.new('vagrant_azure::action::restart_vm') @logger = Log4r::Logger.new('vagrant_azure::action::restart_vm')
end end
def call(env) def call(env)
env[:machine].id =~ /@/ parsed = parse_machine_id(env[:machine].id)
env[:ui].info(I18n.t('vagrant_azure.restarting', parsed))
env[:ui].info "Restarting #{$`} in #{$'}" env[:azure_arm_service].compute.virtual_machines.restart(parsed[:group], parsed[:name])
env[:azure_vm_service].restart_virtual_machine($`, $') env[:ui].info(I18n.t('vagrant_azure.restarted', parsed))
@app.call(env) @app.call(env)
end end
end end

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

@ -6,6 +6,7 @@ require 'json'
require 'azure_mgmt_resources' require 'azure_mgmt_resources'
require 'vagrant/util/template_renderer' require 'vagrant/util/template_renderer'
require 'vagrant-azure/util/timer' require 'vagrant-azure/util/timer'
require 'vagrant-azure/util/machine_id_helper'
require 'haikunator' require 'haikunator'
module VagrantPlugins module VagrantPlugins
@ -13,6 +14,7 @@ module VagrantPlugins
module Action module Action
class RunInstance class RunInstance
include Vagrant::Util::Retryable include Vagrant::Util::Retryable
include VagrantPlugins::Azure::Util::MachineIdHelper
def initialize(app, env) def initialize(app, env)
@app = app @app = app
@ -23,8 +25,10 @@ module VagrantPlugins
# Initialize metrics if they haven't been # Initialize metrics if they haven't been
env[:metrics] ||= {} env[:metrics] ||= {}
machine = env[:machine]
# Get the configs # Get the configs
config = env[:machine].provider_config config = machine.provider_config
endpoint = config.endpoint endpoint = config.endpoint
resource_group_name = config.resource_group_name resource_group_name = config.resource_group_name
location = config.location location = config.location
@ -61,7 +65,6 @@ module VagrantPlugins
@logger.info("Time to fetch os image details: #{env[:metrics]['get_image_details']}") @logger.info("Time to fetch os image details: #{env[:metrics]['get_image_details']}")
deployment_params = { deployment_params = {
sshKeyData: File.read(File.expand_path('~/.ssh/id_rsa.pub')),
dnsLabelPrefix: Haikunator.haikunate(100), dnsLabelPrefix: Haikunator.haikunate(100),
vmSize: vm_size, vmSize: vm_size,
vmName: vm_name, vmName: vm_name,
@ -73,11 +76,22 @@ module VagrantPlugins
virtualNetworkName: virtual_network_name virtualNetworkName: virtual_network_name
} }
if get_image_os(image_details) != 'Windows'
private_key_paths = machine.config.ssh.private_key_path
if private_key_paths.empty?
raise I18n.t('vagrant_azure.private_key_not_specified')
end
paths_to_pub = private_key_paths.map{ |k| File.expand_path( k + '.pub') }.select{ |p| File.exists?(p) }
raise I18n.t('vagrant_azure.public_key_path_private_key', private_key_paths.join(', ')) if paths_to_pub.empty?
deployment_params.merge!(sshKeyData: File.read(paths_to_pub.first))
end
template_params = { template_params = {
operating_system: get_image_os(image_details) operating_system: get_image_os(image_details)
} }
env[:ui].info(" -- Putting Resource Group: #{resource_group_name}") env[:ui].info(" -- Create or Update of Resource Group: #{resource_group_name}")
env[:metrics]['put_resource_group'] = Util::Timer.time do env[:metrics]['put_resource_group'] = Util::Timer.time do
put_resource_group(azure, resource_group_name, location) put_resource_group(azure, resource_group_name, location)
end end
@ -92,7 +106,7 @@ module VagrantPlugins
env[:ui].info('Finished deploying') env[:ui].info('Finished deploying')
# Immediately save the ID since it is created at this point. # Immediately save the ID since it is created at this point.
env[:machine].id = "#{resource_group_name}:#{vm_name}" env[:machine].id = serialize_machine_id(resource_group_name, vm_name, location)
@logger.info("Time to deploy: #{env[:metrics]['deployment_time']}") @logger.info("Time to deploy: #{env[:metrics]['deployment_time']}")
unless env[:interrupted] unless env[:interrupted]

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

@ -2,29 +2,50 @@
# Copyright (c) Microsoft Corporation. All rights reserved. # Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License. See License in the project root for license information. # Licensed under the MIT License. See License in the project root for license information.
require 'log4r' require 'log4r'
require 'vagrant-azure/util/machine_id_helper'
require 'vagrant-azure/util/vm_status_translator'
require 'vagrant/util/retryable'
require 'vagrant-azure/util/timer'
require 'vagrant-azure/util/vm_await'
# require 'vagrant/util/retryable'
# Bare bones basic implementation. This a work in progress in very early stages
module VagrantPlugins module VagrantPlugins
module Azure module Azure
module Action module Action
# This starts a stopped instance # This starts a stopped instance
class StartInstance class StartInstance
include VagrantPlugins::Azure::Util::MachineIdHelper
include VagrantPlugins::Azure::Util::VMStatusTranslator
include VagrantPlugins::Azure::Util::VMAwait
def initialize(app, env) def initialize(app, env)
@app = app @app = app
@logger = Log4r::Logger.new('vagrant_azure:action::start_instance') @logger = Log4r::Logger.new('vagrant_azure::action::start_instance')
end end
def call(env) def call(env)
env[:machine].id = "#{env[:machine].provider_config.vm_name}@#{env[:machine].provider_config.cloud_service_name}" unless env[:machine].id env[:metrics] ||= {}
env[:machine].id =~ /@/
VagrantPlugins::Azure::CLOUD_SERVICE_SEMAPHORE.synchronize do parsed = parse_machine_id(env[:machine].id)
env[:ui].info "Attempting to start '#{$`}' in '#{$'}'" azure = env[:azure_arm_service]
env[:azure_vm_service].start_virtual_machine($`, $') env[:ui].info(I18n.t('vagrant_azure.starting', parsed))
azure.compute.virtual_machines.start(parsed[:group], parsed[:name])
# Wait for the instance to be ready first
env[:metrics]['instance_ready_time'] = Util::Timer.time do
env[:ui].info(I18n.t('vagrant_azure.waiting_for_ready'))
task = await_true(env) do |vm|
running?(vm.properties.instance_view.statuses)
end
if task.value
env[:ui].info(I18n.t('vagrant_azure.started', parsed))
else
raise I18n.t('vagrant_azure.errors.failed_starting', parsed) unless env[:interrupted]
end
end end
@app.call(env) @app.call(env)
end end
end end

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

@ -2,12 +2,18 @@
# Copyright (c) Microsoft Corporation. All rights reserved. # Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License. See License in the project root for license information. # Licensed under the MIT License. See License in the project root for license information.
require 'log4r' require 'log4r'
require 'vagrant-azure/util/machine_id_helper'
require 'vagrant-azure/util/vm_await'
require 'vagrant-azure/util/vm_status_translator'
require 'vagrant-azure/util/timer'
# Bare bones basic implementation. This a work in progress in very early stages
module VagrantPlugins module VagrantPlugins
module Azure module Azure
module Action module Action
class StopInstance class StopInstance
include VagrantPlugins::Azure::Util::MachineIdHelper
include VagrantPlugins::Azure::Util::VMAwait
include VagrantPlugins::Azure::Util::VMStatusTranslator
def initialize(app, env) def initialize(app, env)
@app = app @app = app
@ -15,23 +21,34 @@ module VagrantPlugins
end end
def call(env) def call(env)
if env[:machine].state.id == :StoppedDeallocated env[:metrics] ||= {}
env[:ui].info(
I18n.t('vagrant_azure.already_status', :status => 'stopped.') parsed = parse_machine_id(env[:machine].id)
) if env[:machine].state.id == :stopped
env[:ui].info(I18n.t('vagrant_azure.already_status', :status => 'stopped.'))
else else
env[:machine].id =~ /@/ env[:ui].info(I18n.t('vagrant_azure.stopping', parsed))
VagrantPlugins::Azure::CLOUD_SERVICE_SEMAPHORE.synchronize do env[:azure_arm_service].compute.virtual_machines.power_off(parsed[:group], parsed[:name])
env[:ui].info(
I18n.t( # Wait for the instance to be ready first
'vagrant_azure.stopping', env[:metrics]['instance_stop_time'] = Util::Timer.time do
:vm_name => $`,
:cloud_service_name => $' env[:ui].info(I18n.t('vagrant_azure.waiting_for_stop'))
)
) task = await_true(env) do |vm|
env[:azure_vm_service].shutdown_virtual_machine($`, $') stopped?(vm.properties.instance_view.statuses)
end
if task.value
env[:ui].info(I18n.t('vagrant_azure.stopped', parsed))
else
raise I18n.t('vagrant_azure.errors.failed_starting', parsed) unless env[:interrupted]
end
end end
env[:ui].info(I18n.t('vagrant_azure.stopped', parsed))
end end
@app.call(env) @app.call(env)
end end
end end

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

@ -2,20 +2,32 @@
# Copyright (c) Microsoft Corporation. All rights reserved. # Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License. See License in the project root for license information. # Licensed under the MIT License. See License in the project root for license information.
require 'log4r' require 'log4r'
require 'vagrant-azure/util/machine_id_helper'
module VagrantPlugins module VagrantPlugins
module Azure module Azure
module Action module Action
class TerminateInstance class TerminateInstance
include VagrantPlugins::Azure::Util::MachineIdHelper
def initialize(app, env) def initialize(app, env)
@app = app @app = app
@logger = Log4r::Logger.new('vagrant_azure::action::terminate_instance') @logger = Log4r::Logger.new('vagrant_azure::action::terminate_instance')
end end
def call(env) def call(env)
rg_name, vm_name = env[:machine].id.split(':') parsed = parse_machine_id(env[:machine].id)
begin
env[:ui].info(I18n.t('vagrant_azure.terminating', parsed))
env[:azure_arm_service].resources.resource_groups.delete(parsed[:group]).value!.body
rescue MsRestAzure::AzureOperationError => ex
unless ex.response.status == 404
raise ex
end
end
env[:ui].info(I18n.t('vagrant_azure.terminated', parsed))
env[:azure_arm_service].compute.virtual_machines.delete(rg_name, vm_name).value!.body
env[:machine].id = nil env[:machine].id = nil
@app.call(env) @app.call(env)

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

@ -28,7 +28,7 @@ module VagrantPlugins
# @return [String] # @return [String]
attr_accessor :subscription_id attr_accessor :subscription_id
# (Optional) Name of the resource group to use. WARNING: the resource group will be removed upon destroy!!! # (Optional) Name of the resource group to use.
# #
# @return [String] # @return [String]
attr_accessor :resource_group_name attr_accessor :resource_group_name

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

@ -0,0 +1,20 @@
module VagrantPlugins
module Azure
module Util
module MachineIdHelper
def parse_machine_id(id)
parts = id.split(':')
{
group: parts[0],
name: parts[1],
location: parts[2]
}
end
def serialize_machine_id(resource_group, vm_name, location)
[resource_group, vm_name, location].join(':')
end
end
end
end
end

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

@ -0,0 +1,36 @@
module VagrantPlugins
module Azure
module Util
module VMAwait
def await_true(env)
config = env[:machine].provider_config
parsed = parse_machine_id(env[:machine].id)
azure = env[:azure_arm_service]
tries = config.instance_ready_timeout / 2
count = 0
task = Concurrent::TimerTask.new(execution_interval: config.instance_check_interval ) do
task.shutdown if env[:interrupted]
if count > tries
task.shutdown
false
end
count += 1
vm = azure.compute.virtual_machines.get(parsed[:group], parsed[:name], 'instanceView').value!.body
if yield(vm)
task.shutdown
true
end
end
task.execute
task.wait_for_termination
task
end
end
end
end
end

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

@ -0,0 +1,59 @@
module VagrantPlugins
module Azure
module Util
module VMStatusTranslator
PROVISIONING_STATES = [:provisioned, :deleting]
POWER_STATES = [:running, :starting, :deallocating, :deallocated]
def vm_status_to_state(status)
code = status.code
case
when code == 'ProvisioningState/succeeded'
:provisioned
when code == 'ProvisioningState/deleting'
:deleting
when code == 'PowerState/running'
:running
when code == 'PowerState/stopping'
:stopping
when code == 'PowerState/stopped'
:stopped
when code == 'PowerState/starting'
:starting
when code == 'PowerState/deallocating'
:deallocating
when code == 'PowerState/deallocated'
:deallocated
else
:unknown
end
end
def power_state(statuses)
vm_status_to_state(statuses.select{ |s| s.code.match(/PowerState/) }.last)
end
def running?(statuses)
statuses.any?{ |s| vm_status_to_state(s) == :running }
end
def built?(statuses)
statuses.any?{ |s| vm_status_to_state(s) == :provisioned }
end
def stopped?(statuses)
statuses.any?{ |s| vm_status_to_state(s) == :stopped }
end
def stopping?(statuses)
statuses.any?{ |s| vm_status_to_state(s) == :stopping }
end
def tearing_down?(statuses)
statuses.any?{ |s| vm_status_to_state(s) == :deleting }
end
end
end
end
end

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

@ -9,13 +9,25 @@ en:
already_status: |- already_status: |-
The machine is already %{status}. The machine is already %{status}.
stopping: |- stopping: |-
Stopping '%{vm_name}' in '%{cloud_service_name}' Stopping '%{name}' in '%{group}'
terminating: |-
Terminating '%{name}'
terminated: |-
Terminated '%{name}'
rdp_not_ready: |- rdp_not_ready: |-
RDP not ready RDP not ready
vm_started: |- starting: |-
VM '%{name}' is starting
started: |-
VM '%{name}' has been started VM '%{name}' has been started
vm_stopped: |- stopping: |-
VM '%{name}' is stopping
stopped: |-
VM '%{name}' has been stopped VM '%{name}' has been stopped
restarting: |-
VM '%{name}' is restarting
restarted: |-
VM '%{name}' has been restarted
copy_folder: |- copy_folder: |-
Copying folder: %{hostpath} ==> Copying folder: %{hostpath} ==>
%{guestpath} %{guestpath}
@ -37,6 +49,10 @@ en:
Server not created. Error is: %{message} Server not created. Error is: %{message}
create_vm_failure: |- create_vm_failure: |-
There was some error in creating the VM. There was some error in creating the VM.
failed_starting: |-
Failed to start VM '%{name}'!!
failed_stopping: |-
Failed to stopping VM '%{name}'!!
subscription_id: subscription_id:
required: |- required: |-
You must provide an Azure Subscription Id either through ENV['AZURE_SUBSCRIPTION_ID'] or via Vagrantfile. You must provide an Azure Subscription Id either through ENV['AZURE_SUBSCRIPTION_ID'] or via Vagrantfile.
@ -78,3 +94,15 @@ en:
Waiting for SSH to become available... Waiting for SSH to become available...
ready: |- ready: |-
Machine is booted and ready for use! Machine is booted and ready for use!
public_key_path_private_key: |-
We expect the public key to be added to the Azure VM to be located at the
same place as the config.ssh.private_key_path + '.pub'. We couldn't find
any public keys for these private key paths:
private_key_not_specified: |-
Please specify a secure key to use with config.ssh.private_key_path
(see: https://www.vagrantup.com/docs/vagrantfile/ssh_settings.html).
If not, you publicly accessible Azure VM will be extremely insecure.
waiting_for_ready: |-
Waiting for instance to become "ready"...
waiting_for_stop: |-
Waiting for instance to become "stopped"...