building from deployment template

This commit is contained in:
David Justice 2016-03-27 00:36:45 -07:00
Родитель 8d7dd2f7de
Коммит e2b639beb9
9 изменённых файлов: 257 добавлений и 114 удалений

29
example_box/Vagrantfile поставляемый
Просмотреть файл

@ -10,34 +10,5 @@ Vagrant.configure('2') do |config|
config.vm.box = 'azure'
config.vm.provider :azure do |azure, override|
# Mandatory Settings
azure.mgmt_certificate = '/Users/alexvinyar/Documents/Projects/Azure/managementCertificate.pem'
azure.mgmt_endpoint = 'https://management.core.windows.net'
azure.subscription_id = '12345678-abcd-1234-5678-abcdefghijk'
# azure.vm_image = 'b39f27a8b8c64d52b05eac6a62ebad85__Ubuntu-14_04_1-LTS-amd64-server-20140927-en-us-30GB'
azure.vm_image = 'a699494373c04fc0bc8f2bb1389d6106__Windows-Server-2012-R2-20150916-en.us-127GB.vhd'
azure.vm_name = 'martymcfly1' # max 15 characters. contains letters, number and hyphens. can start with letters and can end with letters and numbers
# vm_password is optional when specifying the private_key_file with Linux VMs
# When building a Windows VM and using WinRM this setting is used to authenticate via WinRM (PowerShell Remoting)
azure.vm_password = 'Not##RealPass' # min 8 characters. should contain a lower case letter, an uppercase letter, a number and a special character
azure.vm_size = 'Small'
# # Optional Settings
azure.storage_acct_name = 'martymcfly' # optional. A new one will be generated if not provided.
# azure.vm_user = 'PROVIDE A USERNAME' # defaults to 'vagrant' if not provided
# azure.cloud_service_name = 'PROVIDE A NAME FOR YOUR CLOUD SERVICE' # same as vm_name. leave blank to auto-generate
# azure.deployment_name = 'PROVIDE A NAME FOR YOUR DEPLOYMENT' # defaults to cloud_service_name
# azure.vm_location = 'PROVIDE A LOCATION FOR VM' # e.g., West US
# # Optional *Nix Settings
# azure.ssh_port = 'A VALID PUBLIC PORT' # defaults to 22
# azure.private_key_file = 'Path to your ssh private key file (~/.ssh/id_rsa) to use for passwordless auth. If the id_rsa file is password protected, you will be prompted for the password.'
# # Optional Windows Settings
azure.winrm_transport = ['http', 'https'] # this will open up winrm ports on both http (5985) and http (5986) ports
# azure.winrm_https_port = 'A VALID PUBLIC PORT' # customize the winrm https port, instead of 5986
# azure.winrm_http_port = 'A VALID PUBLIC PORT' # customize the winrm http port, insted of 5985
azure.tcp_endpoints = '3389:53389' # opens the Remote Desktop internal port that listens on public port 53389. Without this, you cannot RDP to a Windows VM.
end
end

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

@ -17,8 +17,8 @@ module VagrantPlugins
env[:ui].detail "Looking for local port #{@port}"
env[:machine_ssh_info] = read_ssh_info(
env[:azure_vm_service],
env[:machine]
env[:azure_arm_service],
env
)
env[:ui].detail "Found port mapping #{env[:machine_ssh_info][:port]} --> #{@port}"
@ -26,26 +26,34 @@ module VagrantPlugins
@app.call(env)
end
def read_ssh_info(azure, machine)
return nil if machine.id.nil?
machine.id =~ /@/
vm = azure.get_virtual_machine($`, $')
def read_ssh_info(azure, env)
return nil if env[:machine].id.nil?
resource_group_name, vm_name = env[:machine].id.split(':')
vm = azure.compute.virtual_machines.get(resource_group_name, vm_name, 'instanceView').value!.body
if vm.nil? || !vm.instance_of?(Azure::VirtualMachineManagement::VirtualMachine)
if vm.nil?
# Machine cannot be found
@logger.info 'Machine not found. Assuming it was destroyed'
machine.id = nil
@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
# 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

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

@ -13,49 +13,32 @@ module VagrantPlugins
end
def call(env)
env[:machine_state_id] = read_state(env[:azure_arm_service], env[:machine])
env[:machine_state_id] = read_state(env[:azure_arm_service], env)
@app.call(env)
end
# def read_state(azure, machine)
# env[:machine].id = "#{env[:machine].provider_config.vm_name}@#{env[:machine].provider_config.cloud_service_name}" unless env[:machine].id
#
# env[:machine].id =~ /@/
#
# env[:ui].info "Attempting to read state for #{$`} in #{$'}"
#
# vm = env[:azure_vm_service].get_virtual_machine($`, $')
#
# if vm.nil? || \
# !vm.instance_of?(Azure::VirtualMachineManagement::VirtualMachine) || \
# [ :DeletingVM ].include?(vm.status.to_sym)
# # Machine can't be found
# @logger.info 'Machine cannot be found'
# env[:machine].id = nil
# return :NotCreated
# end
#
# env[:ui].info "VM Status: #{vm.status.to_sym}"
# return vm.status.to_sym
# end
def read_state(azure, machine)
def read_state(azure, env)
machine = env[:machine]
return :not_created if machine.id.nil?
# Find the machine
config = machine.provider_config
server = azure.compute.virtual_machines.get(config.resource_group_name, machine.name, true).value!
if server.nil? || [:'shutting-down', :terminated].include?(server.state.to_sym)
# The machine can't be found
@logger.info('Machine not found or terminated, assuming it got destroyed.')
machine.id = nil
return :not_created
rg_name, vm_name = machine.id.split(':')
vm = nil
begin
vm = azure.compute.virtual_machines.get(rg_name, vm_name, 'instanceView').value!.body
rescue MsRestAzure::AzureOperationError => ex
if vm.nil? || [:'shutting-down', :terminated].include?(vm.state.to_sym)
# The machine can't be found
@logger.info('Machine not found or terminated, assuming it got destroyed.')
machine.id = nil
return :not_created
end
end
# Return the state
server.state.to_sym
vm.state.to_sym
end
end
end
end

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

@ -3,6 +3,10 @@
# Licensed under the MIT License. See License in the project root for license information.
require 'log4r'
require 'json'
require 'azure_mgmt_resources'
require 'vagrant/util/template_renderer'
require 'vagrant-azure/util/timer'
require 'haikunator'
module VagrantPlugins
module Azure
@ -16,15 +20,170 @@ module VagrantPlugins
end
def call(env)
config = env[:machine].provider_config
# Initialize metrics if they haven't been
env[:metrics] ||= {}
# Get the configs
config = env[:machine].provider_config
endpoint = config.endpoint
resource_group_name = config.resource_group_name
location = config.location
vm_name = config.vm_name
vm_password = config.vm_password
vm_size = config.vm_size
vm_image_urn = config.vm_image_urn
virtual_network_name = config.virtual_network_name
subnet_name = config.subnet_name
tcp_endpoints = config.tcp_endpoints
availability_set_name = config.availability_set_name
# Launch!
env[:ui].info(I18n.t('vagrant_azure.launching_instance'))
env[:ui].info(" -- Management Endpoint: #{endpoint}")
env[:ui].info(" -- Subscription Id: #{config.subscription_id}")
env[:ui].info(" -- Resource Group Name: #{resource_group_name}")
env[:ui].info(" -- Location: #{location}")
env[:ui].info(" -- VM Name: #{vm_name}")
env[:ui].info(" -- VM Size: #{vm_size}")
env[:ui].info(" -- Image URN: #{vm_image_urn}")
env[:ui].info(" -- Virtual Network Name: #{virtual_network_name}") if virtual_network_name
env[:ui].info(" -- Subnet Name: #{subnet_name}") if subnet_name
env[:ui].info(" -- TCP Endpoints: #{tcp_endpoints}") if tcp_endpoints
env[:ui].info(" -- Availability Set Name: #{availability_set_name}") if availability_set_name
env[:machine].id = "#{server.vm_name}@#{server.cloud_service_name}"
image_publisher, image_offer, image_sku, image_version = vm_image_urn.split(':')
azure = env[:azure_arm_service]
image_details = nil
env[:metrics]['get_image_details'] = Util::Timer.time do
image_details = get_image_details(azure, location, image_publisher, image_offer, image_sku, image_version)
end
@logger.info("Time to fetch os image details: #{env[:metrics]['get_image_details']}")
deployment_params = {
sshKeyData: File.read(File.expand_path('~/.ssh/id_rsa.pub')),
dnsLabelPrefix: Haikunator.haikunate(100),
vmSize: vm_size,
vmName: vm_name,
imagePublisher: image_publisher,
imageOffer: image_offer,
imageSku: image_sku,
imageVersion: image_version,
subnetName: subnet_name,
virtualNetworkName: virtual_network_name
}
template_params = {
operating_system: get_image_os(image_details)
}
env[:ui].info(" -- Putting Resource Group: #{resource_group_name}")
env[:metrics]['put_resource_group'] = Util::Timer.time do
put_resource_group(azure, resource_group_name, location)
end
@logger.info("Time to create resource group: #{env[:metrics]['put_resource_group']}")
deployment_params = build_deployment_params(template_params, deployment_params.reject{|_,v| v.nil?})
env[:ui].info('Starting deployment')
env[:metrics]['deployment_time'] = Util::Timer.time do
put_deployment(azure, resource_group_name, deployment_params)
end
env[:ui].info('Finished deploying')
# Immediately save the ID since it is created at this point.
env[:machine].id = "#{resource_group_name}:#{vm_name}"
@logger.info("Time to deploy: #{env[:metrics]['deployment_time']}")
unless env[:interrupted]
env[:metrics]['instance_ssh_time'] = Util::Timer.time do
# Wait for SSH to be ready.
env[:ui].info(I18n.t('vagrant_azure.waiting_for_ssh'))
network_ready_retries = 0
network_ready_retries_max = 10
while true
break if env[:interrupted]
begin
break if env[:machine].communicate.ready?
rescue Exception => e
if network_ready_retries < network_ready_retries_max
network_ready_retries += 1
@logger.warn(I18n.t('vagrant_azure.waiting_for_ssh, retrying'))
else
raise e
end
end
sleep 2
end
end
@logger.info("Time for SSH ready: #{env[:metrics]['instance_ssh_time']}")
# Ready and booted!
env[:ui].info(I18n.t('vagrant_azure.ready'))
end
# Terminate the instance if we were interrupted
terminate(env) if env[:interrupted]
@app.call(env)
end
def get_image_os(image_details)
image_details.properties.os_disk_image.operating_system
end
def get_image_details(azure, location, publisher, offer, sku, version)
if version == 'latest'
latest = azure.compute.virtual_machine_images.list(location, publisher, offer, sku).value!.body.last
azure.compute.virtual_machine_images.get(location, publisher, offer, sku, latest.name).value!.body
else
azure.compute.virtual_machine_images.get(location, publisher, offer, sku, version).value!.body
end
end
def put_deployment(azure, rg_name, params)
azure.resources.deployments.create_or_update(rg_name, 'vagrant', params).value!.body
end
def put_resource_group(azure, name, location)
params = ::Azure::ARM::Resources::Models::ResourceGroup.new.tap do |rg|
rg.location = location
end
azure.resources.resource_groups.create_or_update(name, params).value!.body
end
# This method generates the deployment template
def render_deployment_template(options)
Vagrant::Util::TemplateRenderer.render('arm/deployment.json', options.merge(template_root: template_root))
end
def build_deployment_params(template_params, deployment_params)
params = ::Azure::ARM::Resources::Models::Deployment.new
params.properties = ::Azure::ARM::Resources::Models::DeploymentProperties.new
params.properties.template = JSON.parse(render_deployment_template(template_params))
params.properties.mode = ::Azure::ARM::Resources::Models::DeploymentMode::Incremental
params.properties.parameters = build_parameters(deployment_params)
params
end
def build_parameters(options)
Hash[*options.map{ |k, v| [k, {value: v}] }.flatten]
end
# Used to find the base location of aws-vagrant templates
def template_root
Azure.source_root.join('templates')
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

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

@ -13,17 +13,10 @@ module VagrantPlugins
end
def call(env)
env[:machine].id =~ /@/
rg_name, vm_name = env[:machine].id.split(':')
vm = env[:azure_vm_service].get_virtual_machine($`, $')
if vm.nil?
# machine not found. assuming it was not created or destroyed
env[:ui].info (I18n.t('vagrant_azure.not_created'))
else
env[:azure_vm_service].delete_virtual_machine($`, $')
env[:machine].id = nil
end
env[:azure_arm_service].compute.virtual_machines.delete(rg_name, vm_name).value!.body
env[:machine].id = nil
@app.call(env)
end

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

@ -38,17 +38,12 @@ module VagrantPlugins
# @return [String]
attr_accessor :location
# (Optional) Name of the storage account to use -- ENV['AZURE_STORAGE_ACCOUNT']
#
# @return [String]
attr_accessor :storage_acct_name
# (Optional) Name of the virtual machine
#
# @return [String]
attr_accessor :vm_name
# (Optional) Password for the VM
# Password for the VM -- This is not recommended for *nix deployments
#
# @return [String]
attr_accessor :vm_password
@ -96,17 +91,16 @@ module VagrantPlugins
# (Optional) The Azure Management API endpoint -- default 'https://management.azure.com' seconds -- ENV['AZURE_MANAGEMENT_ENDPOINT'].
#
# @return [String]
attr_accessor :management_endpoint
attr_accessor :endpoint
def initialize
@tenant_id = UNSET_VALUE
@client_id = UNSET_VALUE
@client_secret = UNSET_VALUE
@management_endpoint = UNSET_VALUE
@endpoint = UNSET_VALUE
@subscription_id = UNSET_VALUE
@resource_group_name = UNSET_VALUE
@location = UNSET_VALUE
@storage_acct_name = UNSET_VALUE
@vm_name = UNSET_VALUE
@vm_password = UNSET_VALUE
@vm_image_urn = UNSET_VALUE
@ -120,8 +114,7 @@ module VagrantPlugins
end
def finalize!
@storage_acct_name = ENV['AZURE_STORAGE_ACCOUNT'] if @storage_acct_name == UNSET_VALUE
@management_endpoint = (ENV['AZURE_MANAGEMENT_ENDPOINT'] || 'https://management.azure.com') if @management_endpoint == UNSET_VALUE
@endpoint = (ENV['AZURE_MANAGEMENT_ENDPOINT'] || 'https://management.azure.com') if @endpoint == UNSET_VALUE
@subscription_id = ENV['AZURE_SUBSCRIPTION_ID'] if @subscription_id == UNSET_VALUE
@tenant_id = ENV['AZURE_TENANT_ID'] if @tenant_id == UNSET_VALUE
@client_id = ENV['AZURE_CLIENT_ID'] if @client_id == UNSET_VALUE
@ -146,9 +139,9 @@ module VagrantPlugins
errors = _detected_errors
# Azure connection properties related validation.
errors << 'vagrant_azure.subscription_id.required' if @subscription_id.nil?
errors << 'vagrant_azure.mgmt_endpoint.required' if @management_endpoint.nil?
errors << 'vagrant_azure.auth.required' if @tenant_id.nil? || @client_secret.nil? || @client_id.nil?
errors << I18n.t('vagrant_azure.subscription_id.required') if @subscription_id.nil?
errors << I18n.t('vagrant_azure.mgmt_endpoint.required') if @endpoint.nil?
errors << I18n.t('vagrant_azure.auth.required') if @tenant_id.nil? || @client_secret.nil? || @client_id.nil?
{ 'Microsoft Azure Provider' => errors }
end

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

@ -0,0 +1,15 @@
module VagrantPlugins
module Azure
module Util
class Timer
def self.time
start_time = Time.now.to_f
yield
end_time = Time.now.to_f
end_time - start_time
end
end
end
end
end

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

@ -1,5 +1,7 @@
en:
vagrant_azure:
launching_instance: |-
Launching an instance with the following settings...
not_created: |-
The machine does not exist. Please run `vagrant up` first.
will_not_destroy: |-
@ -72,3 +74,7 @@ en:
long_pending: |-
The Azure instance is still being initialized. To destroy this machine,
you can run `vagrant destroy`.
waiting_for_ssh: |-
Waiting for SSH to become available...
ready: |-
Machine is booted and ready for use!

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

@ -9,6 +9,14 @@
"description": "User name for the Virtual Machine."
}
},
<% if operating_system == 'Windows' %>
"adminPassword": {
"type": "string",
"metadata": {
"description": "Password for the Virtual Machine (only used on Windows)"
}
},
<% end %>
"sshKeyData": {
"type": "string",
"metadata": {
@ -55,9 +63,16 @@
"description": "Name of the image sku"
}
},
"subnet1Name": {
"imageVersion": {
"type": "string",
"defaultValue": "vagrant-subnet-1",
"defaultValue": "latest",
"metadata": {
"description": "Name of the image sku"
}
},
"subnetName": {
"type": "string",
"defaultValue": "vagrant-subnet",
"metadata": {
"description": "Name of the subnet"
}
@ -71,20 +86,20 @@
}
},
"variables": {
"storageAccountName": "[concat(uniquestring(resourceGroup().id), 'sasshvm')]",
"storageAccountName": "[concat(uniquestring(resourceGroup().id), 'vagrant')]",
"location": "[resourceGroup().location]",
"osDiskName": "osDisk1",
"addressPrefix": "10.0.0.0/16",
"subnet1Prefix": "10.0.0.0/24",
"subnetPrefix": "10.0.0.0/24",
"vmStorageAccountContainerName": "vagrant-vhds",
"nicName": "SshNIC",
"publicIPAddressName": "vagrantPublicIP",
"nicName": "[concat(parameters('vmName'), '-vagrantNIC')]",
"publicIPAddressName": "[concat(parameters('vmName'), '-vagrantPublicIP')]",
"publicIPAddressType": "Dynamic",
"storageAccountType": "Standard_LRS",
"networkSecurityGroupName": "vagrantNetworkSecurityGroup1",
"networkSecurityGroupName": "[concat(parameters('vmName'), '-vagrantNSG')]",
"sshKeyPath": "[concat('/home/',parameters('adminUsername'),'/.ssh/authorized_keys')]",
"vnetID": "[resourceId('Microsoft.Network/virtualNetworks', parameters('virtualNetworkName'))]",
"subnet1Ref": "[concat(variables('vnetID'),'/subnets/',parameters('subnet1Name'))]",
"subnetRef": "[concat(variables('vnetID'),'/subnets/',parameters('subnetName'))]",
"apiVersion": "2015-06-15"
},
"resources": [
@ -149,9 +164,9 @@
},
"subnets": [
{
"name": "[parameters('subnet1Name')]",
"name": "[parameters('subnetName')]",
"properties": {
"addressPrefix": "[variables('subnet1Prefix')]",
"addressPrefix": "[variables('subnetPrefix')]",
"networkSecurityGroup": {
"id": "[resourceId('Microsoft.Network/networkSecurityGroups', variables('networkSecurityGroupName'))]"
}
@ -179,7 +194,7 @@
"id": "[resourceId('Microsoft.Network/publicIPAddresses', variables('publicIPAddressName'))]"
},
"subnet": {
"id": "[variables('subnet1Ref')]"
"id": "[variables('subnetRef')]"
}
}
}