Class: Chef::Provisioning::AzureDriver::Driver

Inherits:
Driver
  • Object
show all
Defined in:
lib/chef/provisioning/azure_driver/driver.rb

Overview

Provisions machines using the Azure SDK

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(driver_url, config) ⇒ Driver

Returns a new instance of Driver.



51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
# File 'lib/chef/provisioning/azure_driver/driver.rb', line 51

def initialize(driver_url, config)
  super
  scheme, subscription_id = driver_url.split(':', 2)
  @subscription = Subscriptions.get_subscription(config, subscription_id)
  if !subscription
    raise "Driver #{driver_url} has a subscription ID, but the system has no credentials configured for it!  If you have access to this subscription, you can use `azure account download` and `azure account import` in the Azure CLI to get the credentials, or set azure_subscriptions to [ { subscription_id: '...', management_credentials: ... }] in your Chef configuration."
  end

  # TODO make this instantiable so we can have multiple drivers ......
  Azure.configure do |azure|
    # Configure these 3 properties to use Storage
    azure.management_certificate = subscription[:management_certificate]
    azure.subscription_id        = subscription[:subscription_id]
    azure.management_endpoint    = subscription[:management_endpoint]
  end
end

Instance Attribute Details

#regionObject (readonly)

Returns the value of attribute region.



24
25
26
# File 'lib/chef/provisioning/azure_driver/driver.rb', line 24

def region
  @region
end

#subscriptionObject (readonly)

Returns the value of attribute subscription.



68
69
70
# File 'lib/chef/provisioning/azure_driver/driver.rb', line 68

def subscription
  @subscription
end

Class Method Details

.canonicalize_url(driver_url, config) ⇒ Object



35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
# File 'lib/chef/provisioning/azure_driver/driver.rb', line 35

def self.canonicalize_url(driver_url, config)
  scheme,  = driver_url.split(':', 2)
  if .nil? || .empty?
    subscription = Subscriptions.default_subscription(config)
    if !subscription
      raise "Driver #{driver_url} did not specify a subscription ID, and no default subscription was found.  Have you downloaded the Azure CLI and used `azure account download` and `azure account import` to set up Azure?  Alternately, you can set azure_subscriptions to [ { subscription_id: '...', management_credentials: ... }] in your Chef configuration."
    end
    config = Cheffish::MergedConfig.new({ azure_subscriptions: subscription }, config)
  end
  if subscription
    [ "#{scheme}:#{subscription[:subscription_id]}", config ]
  else
    [ driver_url, config]
  end
end

.from_url(driver_url, config) ⇒ AzureDriver

Construct an AzureDriver object from a URL - used to parse existing URL data to hydrate a driver object. URL scheme: azure:subscription_id

Returns:

  • (AzureDriver)

    A chef-provisioning Azure driver object for the given URL



31
32
33
# File 'lib/chef/provisioning/azure_driver/driver.rb', line 31

def self.from_url(driver_url, config)
  Driver.new(driver_url, config)
end

Instance Method Details

#allocate_machine(action_handler, machine_spec, machine_options) ⇒ Object

Allocate a new machine with the Azure API and start it up, without blocking to wait for it. Creates any needed resources to get a machine up and running.



76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
# File 'lib/chef/provisioning/azure_driver/driver.rb', line 76

def allocate_machine(action_handler, machine_spec, machine_options)
  existing_vm = vm_for(machine_spec)

  # We don't need to do anything if the existing VM is found
  return if existing_vm

  bootstrap_options = machine_options[:bootstrap_options] || {}
  bootstrap_options[:vm_size] ||= 'Small'
  bootstrap_options[:cloud_service_name] ||= 'chefprovisioning'
  bootstrap_options[:storage_account_name] ||=  'chefprovisioning'
  bootstrap_options[:location] ||=  'West US'

  location = bootstrap_options[:location]

  machine_spec.location = {
    'driver_url' => driver_url,
    'driver_version' => Chef::Provisioning::AzureDriver::VERSION,
    'allocated_at' => Time.now.utc.to_s,
    'host_node' => action_handler.host_node,
    'image_id' => machine_options[:image_id],
    'location' => location,
    'cloud_service' => bootstrap_options[:cloud_service_name]
  }

  image_id = machine_options[:image_id] || default_image_for_location(location)

  Chef::Log.debug "Azure bootstrap options: #{bootstrap_options.inspect}"

  params = {
      vm_name: machine_spec.name,
      vm_user: bootstrap_options[:vm_user] || default_ssh_username,
      image: image_id,
      # This is only until SSH keys are added
      password: machine_options[:password],
      location: location,
      cloud_service_name: bootstrap_options[:cloud_service_name]
  }

  # If the cloud service exists already, need to add a role to it - otherwise create virtual machine (including cloud service)
  cloud_service = azure_cloud_service_service.get_cloud_service(bootstrap_options[:cloud_service_name])
  existing_deployment = azure_vm_service.list_virtual_machines(bootstrap_options[:cloud_service_name]).any?

  if cloud_service and existing_deployment
    action_handler.report_progress "Cloud Service #{bootstrap_options[:cloud_service_name]} already exists, adding role."
    action_handler.report_progress "Creating #{machine_spec.name} with image #{image_id} in #{bootstrap_options[:cloud_service_name]}..."
    vm = azure_vm_service.add_role(params, bootstrap_options)
  else
    action_handler.report_progress "Creating #{machine_spec.name} with image #{image_id} in #{location}..."
    vm = azure_vm_service.create_virtual_machine(params, bootstrap_options)
  end

  machine_spec.location['vm_name'] = vm.vm_name
  machine_spec.location['is_windows'] = (true if vm.os_type == 'Windows') || false
  action_handler.report_progress "Created #{vm.vm_name} in #{location}..."
end

#destroy_machine(action_handler, machine_spec, machine_options) ⇒ Object



154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
# File 'lib/chef/provisioning/azure_driver/driver.rb', line 154

def destroy_machine(action_handler, machine_spec, machine_options)
  vm = vm_for(machine_spec)
  vm_name = machine_spec.name
  cloud_service = machine_spec.location['cloud_service']

  # Check if we need to proceed
  return if vm.nil? || vm_name.nil? || cloud_service.nil?

  # Skip if we don't actually need to do anything
  return unless action_handler.should_perform_actions

  # TODO: action_handler.do |block| ?
  action_handler.report_progress "Destroying VM #{machine_spec.name}!"
  azure_vm_service.delete_virtual_machine(vm_name, cloud_service)
  action_handler.report_progress "Destroyed VM #{machine_spec.name}!"
end

#ready_machine(action_handler, machine_spec, machine_options) ⇒ Object



133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
# File 'lib/chef/provisioning/azure_driver/driver.rb', line 133

def ready_machine(action_handler, machine_spec, machine_options)
  vm = vm_for(machine_spec)
  location = machine_spec.location['location']

  if vm.nil?
    fail "Machine #{machine_spec.name} does not have a VM associated with it, or the VM does not exist."
  end

  # TODO: Not sure if this is the right thing to check
  if vm.status != 'ReadyRole'
    action_handler.report_progress "Readying #{machine_spec.name} in #{location}..."
    wait_until_ready(action_handler, machine_spec)
    wait_for_transport(action_handler, machine_spec, machine_options)
  else
    action_handler.report_progress "#{machine_spec.name} already ready in #{location}!"
  end

  machine_for(machine_spec, machine_options, vm)
end