chef-provisioning-azure 0.3.2 → 0.3.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
 - data/LICENSE +201 -201
 - data/README.md +108 -127
 - data/Rakefile +6 -6
 - data/lib/chef/provider/azure_cloud_service.rb +21 -0
 - data/lib/chef/provider/azure_sql_server.rb +36 -0
 - data/lib/chef/provider/azure_storage_account.rb +21 -0
 - data/lib/chef/provisioning/azure_driver.rb +3 -3
 - data/lib/chef/provisioning/azure_driver/azure_provider.rb +33 -0
 - data/lib/chef/provisioning/azure_driver/azure_resource.rb +51 -0
 - data/lib/chef/provisioning/azure_driver/bootstrap_options.rb +25 -25
 - data/lib/chef/provisioning/azure_driver/constants.rb +34 -34
 - data/lib/chef/provisioning/azure_driver/driver.rb +360 -359
 - data/lib/chef/provisioning/azure_driver/machine_options.rb +62 -62
 - data/lib/chef/provisioning/azure_driver/resources.rb +7 -7
 - data/lib/chef/provisioning/azure_driver/subscriptions.rb +222 -222
 - data/lib/chef/provisioning/azure_driver/version.rb +8 -8
 - data/lib/chef/provisioning/driver_init/azure.rb +3 -3
 - data/lib/chef/resource/azure_cloud_service.rb +13 -0
 - data/lib/chef/resource/azure_sql_server.rb +13 -0
 - data/lib/chef/resource/azure_storage_account.rb +13 -0
 - metadata +28 -19
 
    
        data/Rakefile
    CHANGED
    
    | 
         @@ -1,6 +1,6 @@ 
     | 
|
| 
       1 
     | 
    
         
            -
            require 'bundler'
         
     | 
| 
       2 
     | 
    
         
            -
            require 'bundler/gem_tasks'
         
     | 
| 
       3 
     | 
    
         
            -
             
     | 
| 
       4 
     | 
    
         
            -
            task :spec do
         
     | 
| 
       5 
     | 
    
         
            -
              require File.expand_path('spec/run')
         
     | 
| 
       6 
     | 
    
         
            -
            end
         
     | 
| 
      
 1 
     | 
    
         
            +
            require 'bundler'
         
     | 
| 
      
 2 
     | 
    
         
            +
            require 'bundler/gem_tasks'
         
     | 
| 
      
 3 
     | 
    
         
            +
             
     | 
| 
      
 4 
     | 
    
         
            +
            task :spec do
         
     | 
| 
      
 5 
     | 
    
         
            +
              require File.expand_path('spec/run')
         
     | 
| 
      
 6 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,21 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'chef/provisioning/azure_driver/azure_provider'
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            class Chef
         
     | 
| 
      
 4 
     | 
    
         
            +
              class Provider
         
     | 
| 
      
 5 
     | 
    
         
            +
                class AzureCloudService < Chef::Provisioning::AzureDriver::AzureProvider
         
     | 
| 
      
 6 
     | 
    
         
            +
                  action :create do
         
     | 
| 
      
 7 
     | 
    
         
            +
                    Chef::Log.info("Creating AzureCloudService: #{new_resource.name}")
         
     | 
| 
      
 8 
     | 
    
         
            +
                    csms = Azure::CloudServiceManagementService.new
         
     | 
| 
      
 9 
     | 
    
         
            +
                    csms.create_cloud_service(new_resource.name, new_resource.options)
         
     | 
| 
      
 10 
     | 
    
         
            +
                    properties = csms.get_cloud_service_properties(new_resource.name)
         
     | 
| 
      
 11 
     | 
    
         
            +
                    Chef::Log.debug("Properties of #{new_resource.name}: #{properties.inspect}")
         
     | 
| 
      
 12 
     | 
    
         
            +
                  end
         
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
      
 14 
     | 
    
         
            +
                  action :destroy do
         
     | 
| 
      
 15 
     | 
    
         
            +
                    Chef::Log.info("Destroying AzureCloudService: #{new_resource.name}")
         
     | 
| 
      
 16 
     | 
    
         
            +
                    csms = Azure::CloudServiceManagementService.new
         
     | 
| 
      
 17 
     | 
    
         
            +
                    csms.delete_cloud_service(new_resource.name)
         
     | 
| 
      
 18 
     | 
    
         
            +
                  end
         
     | 
| 
      
 19 
     | 
    
         
            +
                end
         
     | 
| 
      
 20 
     | 
    
         
            +
              end
         
     | 
| 
      
 21 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,36 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'chef/provisioning/azure_driver/azure_provider'
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            class Chef
         
     | 
| 
      
 4 
     | 
    
         
            +
              class Provider
         
     | 
| 
      
 5 
     | 
    
         
            +
                class AzureSqlServer < Chef::Provisioning::AzureDriver::AzureProvider
         
     | 
| 
      
 6 
     | 
    
         
            +
                  action :create do
         
     | 
| 
      
 7 
     | 
    
         
            +
                    restore = Azure.config.management_endpoint
         
     | 
| 
      
 8 
     | 
    
         
            +
                    Azure.config.management_endpoint = azure_sql_management_endpoint
         
     | 
| 
      
 9 
     | 
    
         
            +
                    Chef::Log.info("Creating AzureSqlServer: #{new_resource.name}")
         
     | 
| 
      
 10 
     | 
    
         
            +
                    csql = Azure::SqlDatabaseManagementService.new
         
     | 
| 
      
 11 
     | 
    
         
            +
                    Chef::Log.info("#{new_resource.options.inspect}")
         
     | 
| 
      
 12 
     | 
    
         
            +
                    properties = csql.create_server("#{new_resource.options[:login]}", \
         
     | 
| 
      
 13 
     | 
    
         
            +
                                                    "#{new_resource.options[:password]}", \
         
     | 
| 
      
 14 
     | 
    
         
            +
                                                    "#{new_resource.options[:location]}")
         
     | 
| 
      
 15 
     | 
    
         
            +
                    server = properties.name
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
      
 17 
     | 
    
         
            +
                    new_resource.options[:firewall_rules].each do | rule |
         
     | 
| 
      
 18 
     | 
    
         
            +
                      rule_name = URI::encode(rule[:name])
         
     | 
| 
      
 19 
     | 
    
         
            +
                      range = {
         
     | 
| 
      
 20 
     | 
    
         
            +
                        :start_ip_address => rule[:start_ip_address],
         
     | 
| 
      
 21 
     | 
    
         
            +
                        :end_ip_address => rule[:end_ip_address]
         
     | 
| 
      
 22 
     | 
    
         
            +
                      }
         
     | 
| 
      
 23 
     | 
    
         
            +
                      csql.set_sql_server_firewall_rule(server, rule_name, range)
         
     | 
| 
      
 24 
     | 
    
         
            +
                    end
         
     | 
| 
      
 25 
     | 
    
         
            +
             
     | 
| 
      
 26 
     | 
    
         
            +
                    Chef::Log.info("Properties of #{new_resource.name}: #{properties.inspect}")
         
     | 
| 
      
 27 
     | 
    
         
            +
                    Azure.config.management_endpoint = restore
         
     | 
| 
      
 28 
     | 
    
         
            +
                  end
         
     | 
| 
      
 29 
     | 
    
         
            +
             
     | 
| 
      
 30 
     | 
    
         
            +
                  action :destroy do
         
     | 
| 
      
 31 
     | 
    
         
            +
                    # not supported
         
     | 
| 
      
 32 
     | 
    
         
            +
                    fail "Destroy not yet implemented on Azure SQL Server using ASM."
         
     | 
| 
      
 33 
     | 
    
         
            +
                  end
         
     | 
| 
      
 34 
     | 
    
         
            +
                end
         
     | 
| 
      
 35 
     | 
    
         
            +
              end
         
     | 
| 
      
 36 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,21 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'chef/provisioning/azure_driver/azure_provider'
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            class Chef
         
     | 
| 
      
 4 
     | 
    
         
            +
              class Provider
         
     | 
| 
      
 5 
     | 
    
         
            +
                class AzureStorageAccount < Chef::Provisioning::AzureDriver::AzureProvider
         
     | 
| 
      
 6 
     | 
    
         
            +
                  action :create do
         
     | 
| 
      
 7 
     | 
    
         
            +
                    Chef::Log.info("Creating AzureStorageAccount: #{new_resource.name}")
         
     | 
| 
      
 8 
     | 
    
         
            +
                    sms = Azure::StorageManagementService.new
         
     | 
| 
      
 9 
     | 
    
         
            +
                    sms.create_storage_account(new_resource.name, new_resource.options)
         
     | 
| 
      
 10 
     | 
    
         
            +
                    properties = sms.get_storage_account_properties(new_resource.name)
         
     | 
| 
      
 11 
     | 
    
         
            +
                    Chef::Log.debug("Properties of #{new_resource.name}: #{properties.inspect}")
         
     | 
| 
      
 12 
     | 
    
         
            +
                  end
         
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
      
 14 
     | 
    
         
            +
                  action :destroy do
         
     | 
| 
      
 15 
     | 
    
         
            +
                    Chef::Log.info("Destroying AzureStorageAccount: #{new_resource.name}")
         
     | 
| 
      
 16 
     | 
    
         
            +
                    sms = Azure::StorageManagementService.new
         
     | 
| 
      
 17 
     | 
    
         
            +
                    sms.delete_storage_account(new_resource.name)
         
     | 
| 
      
 18 
     | 
    
         
            +
                  end
         
     | 
| 
      
 19 
     | 
    
         
            +
                end
         
     | 
| 
      
 20 
     | 
    
         
            +
              end
         
     | 
| 
      
 21 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -1,3 +1,3 @@ 
     | 
|
| 
       1 
     | 
    
         
            -
            require 'chef/provisioning'
         
     | 
| 
       2 
     | 
    
         
            -
            require 'chef/provisioning/azure_driver/driver'
         
     | 
| 
       3 
     | 
    
         
            -
            require 'chef/provisioning/azure_driver/resources'
         
     | 
| 
      
 1 
     | 
    
         
            +
            require 'chef/provisioning'
         
     | 
| 
      
 2 
     | 
    
         
            +
            require 'chef/provisioning/azure_driver/driver'
         
     | 
| 
      
 3 
     | 
    
         
            +
            require 'chef/provisioning/azure_driver/resources'
         
     | 
| 
         @@ -0,0 +1,33 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'chef/provider/lwrp_base'
         
     | 
| 
      
 2 
     | 
    
         
            +
            require 'chef/provisioning/azure_driver/azure_resource'
         
     | 
| 
      
 3 
     | 
    
         
            +
            require 'chef/provisioning/chef_provider_action_handler'
         
     | 
| 
      
 4 
     | 
    
         
            +
            require 'azure'
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
            class Chef
         
     | 
| 
      
 7 
     | 
    
         
            +
              module Provisioning
         
     | 
| 
      
 8 
     | 
    
         
            +
                module AzureDriver
         
     | 
| 
      
 9 
     | 
    
         
            +
                  class AzureProvider < Chef::Provider::LWRPBase
         
     | 
| 
      
 10 
     | 
    
         
            +
                    use_inline_resources
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
                    AzureResource = Chef::Provisioning::AzureDriver::AzureResource
         
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
      
 14 
     | 
    
         
            +
                    def azure_sql_management_endpoint
         
     | 
| 
      
 15 
     | 
    
         
            +
                      'https://management.database.windows.net:8443'
         
     | 
| 
      
 16 
     | 
    
         
            +
                    end
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
                    def action_handler
         
     | 
| 
      
 19 
     | 
    
         
            +
                      @action_handler ||= Chef::Provisioning::ChefProviderActionHandler.new(self)
         
     | 
| 
      
 20 
     | 
    
         
            +
                    end
         
     | 
| 
      
 21 
     | 
    
         
            +
             
     | 
| 
      
 22 
     | 
    
         
            +
                    # All these need to implement whyrun
         
     | 
| 
      
 23 
     | 
    
         
            +
                    def whyrun_supported?
         
     | 
| 
      
 24 
     | 
    
         
            +
                      true
         
     | 
| 
      
 25 
     | 
    
         
            +
                    end
         
     | 
| 
      
 26 
     | 
    
         
            +
             
     | 
| 
      
 27 
     | 
    
         
            +
                    def driver
         
     | 
| 
      
 28 
     | 
    
         
            +
                      new_resource.driver
         
     | 
| 
      
 29 
     | 
    
         
            +
                    end
         
     | 
| 
      
 30 
     | 
    
         
            +
                  end
         
     | 
| 
      
 31 
     | 
    
         
            +
                end
         
     | 
| 
      
 32 
     | 
    
         
            +
              end
         
     | 
| 
      
 33 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,51 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'chef/resource/lwrp_base'
         
     | 
| 
      
 2 
     | 
    
         
            +
            require 'chef/provisioning/azure_driver/subscriptions'
         
     | 
| 
      
 3 
     | 
    
         
            +
             
     | 
| 
      
 4 
     | 
    
         
            +
            class Chef
         
     | 
| 
      
 5 
     | 
    
         
            +
              module Provisioning
         
     | 
| 
      
 6 
     | 
    
         
            +
                module AzureDriver
         
     | 
| 
      
 7 
     | 
    
         
            +
                  class AzureResource < Chef::Resource::LWRPBase
         
     | 
| 
      
 8 
     | 
    
         
            +
                    def initialize(*args)
         
     | 
| 
      
 9 
     | 
    
         
            +
                      super
         
     | 
| 
      
 10 
     | 
    
         
            +
                      if run_context
         
     | 
| 
      
 11 
     | 
    
         
            +
                        @chef_environment = run_context.cheffish.current_environment
         
     | 
| 
      
 12 
     | 
    
         
            +
                        @chef_server = run_context.cheffish.current_chef_server
         
     | 
| 
      
 13 
     | 
    
         
            +
                        @driver = run_context.chef_provisioning.current_driver
         
     | 
| 
      
 14 
     | 
    
         
            +
                      end
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
                      config = run_context.chef_provisioning.config
         
     | 
| 
      
 17 
     | 
    
         
            +
                      scheme, account_id = driver.split(':', 2)
         
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
      
 19 
     | 
    
         
            +
                      if account_id.nil? || account_id.empty?
         
     | 
| 
      
 20 
     | 
    
         
            +
                        subscription = Subscriptions.default_subscription(config)
         
     | 
| 
      
 21 
     | 
    
         
            +
                        config = Cheffish::MergedConfig.new({ azure_subscriptions: subscription }, config)
         
     | 
| 
      
 22 
     | 
    
         
            +
                        if !subscription
         
     | 
| 
      
 23 
     | 
    
         
            +
                          raise "Driver #{driver} 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."
         
     | 
| 
      
 24 
     | 
    
         
            +
                        end
         
     | 
| 
      
 25 
     | 
    
         
            +
                      else
         
     | 
| 
      
 26 
     | 
    
         
            +
                        subscription_id = account_id || subscription[:subscription_id]
         
     | 
| 
      
 27 
     | 
    
         
            +
                        subscription = Subscriptions.get_subscription(config, subscription_id)
         
     | 
| 
      
 28 
     | 
    
         
            +
                      end
         
     | 
| 
      
 29 
     | 
    
         
            +
             
     | 
| 
      
 30 
     | 
    
         
            +
                      if !subscription
         
     | 
| 
      
 31 
     | 
    
         
            +
                        raise "Driver #{driver} has a subscription ID (#{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."
         
     | 
| 
      
 32 
     | 
    
         
            +
                      else
         
     | 
| 
      
 33 
     | 
    
         
            +
                        Chef::Log.debug("Using subscription: #{subscription.inspect}")
         
     | 
| 
      
 34 
     | 
    
         
            +
                      end
         
     | 
| 
      
 35 
     | 
    
         
            +
             
     | 
| 
      
 36 
     | 
    
         
            +
                      Azure.configure do |azure|
         
     | 
| 
      
 37 
     | 
    
         
            +
                        azure.management_certificate = subscription[:management_certificate]
         
     | 
| 
      
 38 
     | 
    
         
            +
                        azure.subscription_id        = subscription[:subscription_id]
         
     | 
| 
      
 39 
     | 
    
         
            +
                        azure.management_endpoint    = subscription[:management_endpoint]
         
     | 
| 
      
 40 
     | 
    
         
            +
                      end
         
     | 
| 
      
 41 
     | 
    
         
            +
                    end
         
     | 
| 
      
 42 
     | 
    
         
            +
             
     | 
| 
      
 43 
     | 
    
         
            +
                    attribute :driver
         
     | 
| 
      
 44 
     | 
    
         
            +
                    attribute :chef_server, kind_of: Hash
         
     | 
| 
      
 45 
     | 
    
         
            +
                    attribute :managed_entry_store, kind_of: Chef::Provisioning::ManagedEntryStore,
         
     | 
| 
      
 46 
     | 
    
         
            +
                                              lazy_default: proc { Chef::Provisioning::ChefManagedEntryStore.new(chef_server) }
         
     | 
| 
      
 47 
     | 
    
         
            +
                    attribute :subscription
         
     | 
| 
      
 48 
     | 
    
         
            +
                  end
         
     | 
| 
      
 49 
     | 
    
         
            +
                end
         
     | 
| 
      
 50 
     | 
    
         
            +
              end
         
     | 
| 
      
 51 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -1,25 +1,25 @@ 
     | 
|
| 
       1 
     | 
    
         
            -
            class Chef
         
     | 
| 
       2 
     | 
    
         
            -
            module Provisioning
         
     | 
| 
       3 
     | 
    
         
            -
            module AzureDriver
         
     | 
| 
       4 
     | 
    
         
            -
              # Represents available options when bootstrapping a host on Azure
         
     | 
| 
       5 
     | 
    
         
            -
              # These are used to tell Azure some initial pieces of information
         
     | 
| 
       6 
     | 
    
         
            -
              # for building a new VM.
         
     | 
| 
       7 
     | 
    
         
            -
              class BootstrapOptions < Chef::Provisioning::BootstrapOptions
         
     | 
| 
       8 
     | 
    
         
            -
                # @return [String] The name of the VM
         
     | 
| 
       9 
     | 
    
         
            -
                attr_accessor :vm_name
         
     | 
| 
       10 
     | 
    
         
            -
             
     | 
| 
       11 
     | 
    
         
            -
                # @return [String] The VM user
         
     | 
| 
       12 
     | 
    
         
            -
                attr_accessor :vm_user
         
     | 
| 
       13 
     | 
    
         
            -
             
     | 
| 
       14 
     | 
    
         
            -
                # @return [String] The identifier for the VM image to use
         
     | 
| 
       15 
     | 
    
         
            -
                attr_accessor :image
         
     | 
| 
       16 
     | 
    
         
            -
             
     | 
| 
       17 
     | 
    
         
            -
                # @return [String] the password to use
         
     | 
| 
       18 
     | 
    
         
            -
                attr_accessor :password
         
     | 
| 
       19 
     | 
    
         
            -
             
     | 
| 
       20 
     | 
    
         
            -
                # @return [String] The Azure location to store this in
         
     | 
| 
       21 
     | 
    
         
            -
                attr_accessor :location
         
     | 
| 
       22 
     | 
    
         
            -
              end
         
     | 
| 
       23 
     | 
    
         
            -
            end
         
     | 
| 
       24 
     | 
    
         
            -
            end
         
     | 
| 
       25 
     | 
    
         
            -
            end
         
     | 
| 
      
 1 
     | 
    
         
            +
            class Chef
         
     | 
| 
      
 2 
     | 
    
         
            +
            module Provisioning
         
     | 
| 
      
 3 
     | 
    
         
            +
            module AzureDriver
         
     | 
| 
      
 4 
     | 
    
         
            +
              # Represents available options when bootstrapping a host on Azure
         
     | 
| 
      
 5 
     | 
    
         
            +
              # These are used to tell Azure some initial pieces of information
         
     | 
| 
      
 6 
     | 
    
         
            +
              # for building a new VM.
         
     | 
| 
      
 7 
     | 
    
         
            +
              class BootstrapOptions < Chef::Provisioning::BootstrapOptions
         
     | 
| 
      
 8 
     | 
    
         
            +
                # @return [String] The name of the VM
         
     | 
| 
      
 9 
     | 
    
         
            +
                attr_accessor :vm_name
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
                # @return [String] The VM user
         
     | 
| 
      
 12 
     | 
    
         
            +
                attr_accessor :vm_user
         
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
      
 14 
     | 
    
         
            +
                # @return [String] The identifier for the VM image to use
         
     | 
| 
      
 15 
     | 
    
         
            +
                attr_accessor :image
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
      
 17 
     | 
    
         
            +
                # @return [String] the password to use
         
     | 
| 
      
 18 
     | 
    
         
            +
                attr_accessor :password
         
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
      
 20 
     | 
    
         
            +
                # @return [String] The Azure location to store this in
         
     | 
| 
      
 21 
     | 
    
         
            +
                attr_accessor :location
         
     | 
| 
      
 22 
     | 
    
         
            +
              end
         
     | 
| 
      
 23 
     | 
    
         
            +
            end
         
     | 
| 
      
 24 
     | 
    
         
            +
            end
         
     | 
| 
      
 25 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -1,34 +1,34 @@ 
     | 
|
| 
       1 
     | 
    
         
            -
            class Chef
         
     | 
| 
       2 
     | 
    
         
            -
            module Provisioning
         
     | 
| 
       3 
     | 
    
         
            -
            module AzureDriver
         
     | 
| 
       4 
     | 
    
         
            -
              # A collection of useful Azure-specific constants
         
     | 
| 
       5 
     | 
    
         
            -
              class Constants
         
     | 
| 
       6 
     | 
    
         
            -
                # Constants around transport mechanisms available
         
     | 
| 
       7 
     | 
    
         
            -
                class Transport
         
     | 
| 
       8 
     | 
    
         
            -
                  HTTP = 'http'
         
     | 
| 
       9 
     | 
    
         
            -
                  HTTPS = 'https'
         
     | 
| 
       10 
     | 
    
         
            -
                end
         
     | 
| 
       11 
     | 
    
         
            -
             
     | 
| 
       12 
     | 
    
         
            -
                # Constants for describing VM sizes in Azure
         
     | 
| 
       13 
     | 
    
         
            -
                class MachineSize
         
     | 
| 
       14 
     | 
    
         
            -
                  # Put in machine specs here...
         
     | 
| 
       15 
     | 
    
         
            -
                  EXTRASMALL = 'ExtraSmall'
         
     | 
| 
       16 
     | 
    
         
            -
                  # What is this?
         
     | 
| 
       17 
     | 
    
         
            -
                  SMALL = 'Small'
         
     | 
| 
       18 
     | 
    
         
            -
                  # Are these now A2?
         
     | 
| 
       19 
     | 
    
         
            -
                  MEDIUM = 'Medium'
         
     | 
| 
       20 
     | 
    
         
            -
                  LARGE = 'Large'
         
     | 
| 
       21 
     | 
    
         
            -
                  XLARGE = 'ExtraLarge'
         
     | 
| 
       22 
     | 
    
         
            -
                  A5 = 'A5'
         
     | 
| 
       23 
     | 
    
         
            -
                  A6 = 'A6'
         
     | 
| 
       24 
     | 
    
         
            -
                  A7 = 'A7'
         
     | 
| 
       25 
     | 
    
         
            -
                  BASIC_A0 = 'Basic_A0'
         
     | 
| 
       26 
     | 
    
         
            -
                  BASIC_A1 = 'Basic_A1'
         
     | 
| 
       27 
     | 
    
         
            -
                  BASIC_A2 = 'Basic_A2'
         
     | 
| 
       28 
     | 
    
         
            -
                  BASIC_A3 = 'Basic_A3'
         
     | 
| 
       29 
     | 
    
         
            -
                  BASIC_A4 = 'Basic_A4'
         
     | 
| 
       30 
     | 
    
         
            -
                end
         
     | 
| 
       31 
     | 
    
         
            -
              end
         
     | 
| 
       32 
     | 
    
         
            -
            end
         
     | 
| 
       33 
     | 
    
         
            -
            end
         
     | 
| 
       34 
     | 
    
         
            -
            end
         
     | 
| 
      
 1 
     | 
    
         
            +
            class Chef
         
     | 
| 
      
 2 
     | 
    
         
            +
            module Provisioning
         
     | 
| 
      
 3 
     | 
    
         
            +
            module AzureDriver
         
     | 
| 
      
 4 
     | 
    
         
            +
              # A collection of useful Azure-specific constants
         
     | 
| 
      
 5 
     | 
    
         
            +
              class Constants
         
     | 
| 
      
 6 
     | 
    
         
            +
                # Constants around transport mechanisms available
         
     | 
| 
      
 7 
     | 
    
         
            +
                class Transport
         
     | 
| 
      
 8 
     | 
    
         
            +
                  HTTP = 'http'
         
     | 
| 
      
 9 
     | 
    
         
            +
                  HTTPS = 'https'
         
     | 
| 
      
 10 
     | 
    
         
            +
                end
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
                # Constants for describing VM sizes in Azure
         
     | 
| 
      
 13 
     | 
    
         
            +
                class MachineSize
         
     | 
| 
      
 14 
     | 
    
         
            +
                  # Put in machine specs here...
         
     | 
| 
      
 15 
     | 
    
         
            +
                  EXTRASMALL = 'ExtraSmall'
         
     | 
| 
      
 16 
     | 
    
         
            +
                  # What is this?
         
     | 
| 
      
 17 
     | 
    
         
            +
                  SMALL = 'Small'
         
     | 
| 
      
 18 
     | 
    
         
            +
                  # Are these now A2?
         
     | 
| 
      
 19 
     | 
    
         
            +
                  MEDIUM = 'Medium'
         
     | 
| 
      
 20 
     | 
    
         
            +
                  LARGE = 'Large'
         
     | 
| 
      
 21 
     | 
    
         
            +
                  XLARGE = 'ExtraLarge'
         
     | 
| 
      
 22 
     | 
    
         
            +
                  A5 = 'A5'
         
     | 
| 
      
 23 
     | 
    
         
            +
                  A6 = 'A6'
         
     | 
| 
      
 24 
     | 
    
         
            +
                  A7 = 'A7'
         
     | 
| 
      
 25 
     | 
    
         
            +
                  BASIC_A0 = 'Basic_A0'
         
     | 
| 
      
 26 
     | 
    
         
            +
                  BASIC_A1 = 'Basic_A1'
         
     | 
| 
      
 27 
     | 
    
         
            +
                  BASIC_A2 = 'Basic_A2'
         
     | 
| 
      
 28 
     | 
    
         
            +
                  BASIC_A3 = 'Basic_A3'
         
     | 
| 
      
 29 
     | 
    
         
            +
                  BASIC_A4 = 'Basic_A4'
         
     | 
| 
      
 30 
     | 
    
         
            +
                end
         
     | 
| 
      
 31 
     | 
    
         
            +
              end
         
     | 
| 
      
 32 
     | 
    
         
            +
            end
         
     | 
| 
      
 33 
     | 
    
         
            +
            end
         
     | 
| 
      
 34 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -1,359 +1,360 @@ 
     | 
|
| 
       1 
     | 
    
         
            -
            require 'chef/mixin/shell_out'
         
     | 
| 
       2 
     | 
    
         
            -
            require 'chef/provisioning/driver'
         
     | 
| 
       3 
     | 
    
         
            -
            require 'chef/provisioning/convergence_strategy/install_cached'
         
     | 
| 
       4 
     | 
    
         
            -
            require 'chef/provisioning/convergence_strategy/install_sh'
         
     | 
| 
       5 
     | 
    
         
            -
            require 'chef/provisioning/convergence_strategy/install_msi'
         
     | 
| 
       6 
     | 
    
         
            -
            require 'chef/provisioning/convergence_strategy/no_converge'
         
     | 
| 
       7 
     | 
    
         
            -
            require 'chef/provisioning/transport/ssh'
         
     | 
| 
       8 
     | 
    
         
            -
            require 'chef/provisioning/transport/winrm'
         
     | 
| 
       9 
     | 
    
         
            -
            require 'chef/provisioning/machine/windows_machine'
         
     | 
| 
       10 
     | 
    
         
            -
            require 'chef/provisioning/machine/unix_machine'
         
     | 
| 
       11 
     | 
    
         
            -
            require 'chef/provisioning/machine_spec'
         
     | 
| 
       12 
     | 
    
         
            -
             
     | 
| 
       13 
     | 
    
         
            -
            require 'chef/provisioning/azure_driver/version'
         
     | 
| 
       14 
     | 
    
         
            -
            require 'chef/provisioning/azure_driver/subscriptions'
         
     | 
| 
       15 
     | 
    
         
            -
             
     | 
| 
       16 
     | 
    
         
            -
            require 'yaml'
         
     | 
| 
       17 
     | 
    
         
            -
            require 'azure'
         
     | 
| 
       18 
     | 
    
         
            -
             
     | 
| 
       19 
     | 
    
         
            -
            class Chef
         
     | 
| 
       20 
     | 
    
         
            -
            module Provisioning
         
     | 
| 
       21 
     | 
    
         
            -
            module AzureDriver
         
     | 
| 
       22 
     | 
    
         
            -
              # Provisions machines using the Azure SDK
         
     | 
| 
       23 
     | 
    
         
            -
              class Driver < Chef::Provisioning::Driver
         
     | 
| 
       24 
     | 
    
         
            -
                attr_reader :region
         
     | 
| 
       25 
     | 
    
         
            -
             
     | 
| 
       26 
     | 
    
         
            -
                # Construct an AzureDriver object from a URL - used to parse existing URL
         
     | 
| 
       27 
     | 
    
         
            -
                # data to hydrate a driver object.
         
     | 
| 
       28 
     | 
    
         
            -
                # URL scheme:
         
     | 
| 
       29 
     | 
    
         
            -
                # azure:subscription_id
         
     | 
| 
       30 
     | 
    
         
            -
                # @return [AzureDriver] A chef-provisioning Azure driver object for the given URL
         
     | 
| 
       31 
     | 
    
         
            -
                def self.from_url(driver_url, config)
         
     | 
| 
       32 
     | 
    
         
            -
                  Driver.new(driver_url, config)
         
     | 
| 
       33 
     | 
    
         
            -
                end
         
     | 
| 
       34 
     | 
    
         
            -
             
     | 
| 
       35 
     | 
    
         
            -
                def self.canonicalize_url(driver_url, config)
         
     | 
| 
       36 
     | 
    
         
            -
                  scheme, account_id = driver_url.split(':', 2)
         
     | 
| 
       37 
     | 
    
         
            -
                  if account_id.nil? || account_id.empty?
         
     | 
| 
       38 
     | 
    
         
            -
                    subscription = Subscriptions.default_subscription(config)
         
     | 
| 
       39 
     | 
    
         
            -
                    if !subscription
         
     | 
| 
       40 
     | 
    
         
            -
                      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."
         
     | 
| 
       41 
     | 
    
         
            -
                    end
         
     | 
| 
       42 
     | 
    
         
            -
                    config = Cheffish::MergedConfig.new({ azure_subscriptions: subscription }, config)
         
     | 
| 
       43 
     | 
    
         
            -
                  end
         
     | 
| 
       44 
     | 
    
         
            -
                  if subscription
         
     | 
| 
       45 
     | 
    
         
            -
                    [ "#{scheme}:#{subscription[:subscription_id]}", config ]
         
     | 
| 
       46 
     | 
    
         
            -
                  else
         
     | 
| 
       47 
     | 
    
         
            -
                    [ driver_url, config]
         
     | 
| 
       48 
     | 
    
         
            -
                  end
         
     | 
| 
       49 
     | 
    
         
            -
                end
         
     | 
| 
       50 
     | 
    
         
            -
             
     | 
| 
       51 
     | 
    
         
            -
                def initialize(driver_url, config)
         
     | 
| 
       52 
     | 
    
         
            -
                  super
         
     | 
| 
       53 
     | 
    
         
            -
                  scheme, subscription_id = driver_url.split(':', 2)
         
     | 
| 
       54 
     | 
    
         
            -
                  @subscription = Subscriptions.get_subscription(config, subscription_id)
         
     | 
| 
       55 
     | 
    
         
            -
                  if !subscription
         
     | 
| 
       56 
     | 
    
         
            -
                    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."
         
     | 
| 
       57 
     | 
    
         
            -
                  end
         
     | 
| 
       58 
     | 
    
         
            -
             
     | 
| 
       59 
     | 
    
         
            -
                  # TODO make this instantiable so we can have multiple drivers ......
         
     | 
| 
       60 
     | 
    
         
            -
                  Azure.configure do |azure|
         
     | 
| 
       61 
     | 
    
         
            -
                    # Configure these 3 properties to use Storage
         
     | 
| 
       62 
     | 
    
         
            -
                    azure.management_certificate = subscription[:management_certificate]
         
     | 
| 
       63 
     | 
    
         
            -
                    azure.subscription_id        = subscription[:subscription_id]
         
     | 
| 
       64 
     | 
    
         
            -
                    azure.management_endpoint    = subscription[:management_endpoint]
         
     | 
| 
       65 
     | 
    
         
            -
                  end
         
     | 
| 
       66 
     | 
    
         
            -
                end
         
     | 
| 
       67 
     | 
    
         
            -
             
     | 
| 
       68 
     | 
    
         
            -
                attr_reader :subscription
         
     | 
| 
       69 
     | 
    
         
            -
             
     | 
| 
       70 
     | 
    
         
            -
                # -- Machine methods --
         
     | 
| 
       71 
     | 
    
         
            -
             
     | 
| 
       72 
     | 
    
         
            -
                # Allocate a new machine with the Azure API and start it up, without
         
     | 
| 
       73 
     | 
    
         
            -
                # blocking to wait for it. Creates any needed resources to get a machine
         
     | 
| 
       74 
     | 
    
         
            -
                # up and running.
         
     | 
| 
       75 
     | 
    
         
            -
                # @param (see Chef::Provisioning::Driver#allocate_machine)
         
     | 
| 
       76 
     | 
    
         
            -
                def allocate_machine(action_handler, machine_spec, machine_options)
         
     | 
| 
       77 
     | 
    
         
            -
                  existing_vm = vm_for(machine_spec)
         
     | 
| 
       78 
     | 
    
         
            -
             
     | 
| 
       79 
     | 
    
         
            -
                  # We don't need to do anything if the existing VM is found
         
     | 
| 
       80 
     | 
    
         
            -
                  return if existing_vm
         
     | 
| 
       81 
     | 
    
         
            -
             
     | 
| 
       82 
     | 
    
         
            -
                  bootstrap_options = machine_options[:bootstrap_options] || {}
         
     | 
| 
       83 
     | 
    
         
            -
                  bootstrap_options[:vm_size] ||= 'Small'
         
     | 
| 
       84 
     | 
    
         
            -
                  bootstrap_options[:cloud_service_name] ||= 'chefprovisioning'
         
     | 
| 
       85 
     | 
    
         
            -
                  bootstrap_options[:storage_account_name] ||=  'chefprovisioning'
         
     | 
| 
       86 
     | 
    
         
            -
                  bootstrap_options[:location] ||=  'West US'
         
     | 
| 
       87 
     | 
    
         
            -
             
     | 
| 
       88 
     | 
    
         
            -
                  location = bootstrap_options[:location]
         
     | 
| 
       89 
     | 
    
         
            -
             
     | 
| 
       90 
     | 
    
         
            -
                  machine_spec.location = {
         
     | 
| 
       91 
     | 
    
         
            -
                    'driver_url' => driver_url,
         
     | 
| 
       92 
     | 
    
         
            -
                    'driver_version' => Chef::Provisioning::AzureDriver::VERSION,
         
     | 
| 
       93 
     | 
    
         
            -
                    'allocated_at' => Time.now.utc.to_s,
         
     | 
| 
       94 
     | 
    
         
            -
                    'host_node' => action_handler.host_node,
         
     | 
| 
       95 
     | 
    
         
            -
                    'image_id' => machine_options[:image_id],
         
     | 
| 
       96 
     | 
    
         
            -
                    'location' => location,
         
     | 
| 
       97 
     | 
    
         
            -
                    'cloud_service' => bootstrap_options[:cloud_service_name]
         
     | 
| 
       98 
     | 
    
         
            -
                  }
         
     | 
| 
       99 
     | 
    
         
            -
             
     | 
| 
       100 
     | 
    
         
            -
                  image_id = machine_options[:image_id] || default_image_for_location(location)
         
     | 
| 
       101 
     | 
    
         
            -
             
     | 
| 
       102 
     | 
    
         
            -
                  Chef::Log.debug "Azure bootstrap options: #{bootstrap_options.inspect}"
         
     | 
| 
       103 
     | 
    
         
            -
             
     | 
| 
       104 
     | 
    
         
            -
                  params = {
         
     | 
| 
       105 
     | 
    
         
            -
                      vm_name: machine_spec.name,
         
     | 
| 
       106 
     | 
    
         
            -
                      vm_user: bootstrap_options[:vm_user] || default_ssh_username,
         
     | 
| 
       107 
     | 
    
         
            -
                      image: image_id,
         
     | 
| 
       108 
     | 
    
         
            -
                      # This is only until SSH keys are added
         
     | 
| 
       109 
     | 
    
         
            -
                      password: machine_options[:password],
         
     | 
| 
       110 
     | 
    
         
            -
                      location: location,
         
     | 
| 
       111 
     | 
    
         
            -
                      cloud_service_name: bootstrap_options[:cloud_service_name]
         
     | 
| 
       112 
     | 
    
         
            -
                  }
         
     | 
| 
       113 
     | 
    
         
            -
             
     | 
| 
       114 
     | 
    
         
            -
                  # If the cloud service exists already, need to add a role to it - otherwise create virtual machine (including cloud service)
         
     | 
| 
       115 
     | 
    
         
            -
                  cloud_service = azure_cloud_service_service.get_cloud_service(bootstrap_options[:cloud_service_name])
         
     | 
| 
       116 
     | 
    
         
            -
                  existing_deployment = azure_vm_service.list_virtual_machines(bootstrap_options[:cloud_service_name]).any?
         
     | 
| 
       117 
     | 
    
         
            -
             
     | 
| 
       118 
     | 
    
         
            -
                  if cloud_service and existing_deployment
         
     | 
| 
       119 
     | 
    
         
            -
                    action_handler.report_progress "Cloud Service #{bootstrap_options[:cloud_service_name]} already exists, adding role."
         
     | 
| 
       120 
     | 
    
         
            -
                    action_handler.report_progress "Creating #{machine_spec.name} with image #{image_id} in #{bootstrap_options[:cloud_service_name]}..."
         
     | 
| 
       121 
     | 
    
         
            -
                    vm = azure_vm_service.add_role(params, bootstrap_options)
         
     | 
| 
       122 
     | 
    
         
            -
                  else
         
     | 
| 
       123 
     | 
    
         
            -
                    action_handler.report_progress "Creating #{machine_spec.name} with image #{image_id} in #{location}..."
         
     | 
| 
       124 
     | 
    
         
            -
                    vm = azure_vm_service.create_virtual_machine(params, bootstrap_options)
         
     | 
| 
       125 
     | 
    
         
            -
                  end
         
     | 
| 
       126 
     | 
    
         
            -
             
     | 
| 
       127 
     | 
    
         
            -
                  machine_spec.location['vm_name'] = vm.vm_name
         
     | 
| 
       128 
     | 
    
         
            -
                  machine_spec.location['is_windows'] = (true if vm.os_type == 'Windows') || false
         
     | 
| 
       129 
     | 
    
         
            -
                  action_handler.report_progress "Created #{vm.vm_name} in #{location}..."
         
     | 
| 
       130 
     | 
    
         
            -
                end
         
     | 
| 
       131 
     | 
    
         
            -
             
     | 
| 
       132 
     | 
    
         
            -
                # (see Chef::Provisioning::Driver#ready_machine)
         
     | 
| 
       133 
     | 
    
         
            -
                def ready_machine(action_handler, machine_spec, machine_options)
         
     | 
| 
       134 
     | 
    
         
            -
                  vm = vm_for(machine_spec)
         
     | 
| 
       135 
     | 
    
         
            -
                  location = machine_spec.location['location']
         
     | 
| 
       136 
     | 
    
         
            -
             
     | 
| 
       137 
     | 
    
         
            -
                  if vm.nil?
         
     | 
| 
       138 
     | 
    
         
            -
                    fail "Machine #{machine_spec.name} does not have a VM associated with it, or the VM does not exist."
         
     | 
| 
       139 
     | 
    
         
            -
                  end
         
     | 
| 
       140 
     | 
    
         
            -
             
     | 
| 
       141 
     | 
    
         
            -
                  # TODO: Not sure if this is the right thing to check
         
     | 
| 
       142 
     | 
    
         
            -
                  if vm.status != 'ReadyRole'
         
     | 
| 
       143 
     | 
    
         
            -
                    action_handler.report_progress "Readying #{machine_spec.name} in #{location}..."
         
     | 
| 
       144 
     | 
    
         
            -
                    wait_until_ready(action_handler, machine_spec)
         
     | 
| 
       145 
     | 
    
         
            -
                    wait_for_transport(action_handler, machine_spec, machine_options)
         
     | 
| 
       146 
     | 
    
         
            -
                  else
         
     | 
| 
       147 
     | 
    
         
            -
                    action_handler.report_progress "#{machine_spec.name} already ready in #{location}!"
         
     | 
| 
       148 
     | 
    
         
            -
                  end
         
     | 
| 
       149 
     | 
    
         
            -
             
     | 
| 
       150 
     | 
    
         
            -
                  machine_for(machine_spec, machine_options, vm)
         
     | 
| 
       151 
     | 
    
         
            -
                end
         
     | 
| 
       152 
     | 
    
         
            -
             
     | 
| 
       153 
     | 
    
         
            -
                # (see Chef::Provisioning::Driver#destroy_machine)
         
     | 
| 
       154 
     | 
    
         
            -
                def destroy_machine(action_handler, machine_spec, machine_options)
         
     | 
| 
       155 
     | 
    
         
            -
                  vm = vm_for(machine_spec)
         
     | 
| 
       156 
     | 
    
         
            -
                  vm_name = machine_spec.name
         
     | 
| 
       157 
     | 
    
         
            -
                  cloud_service = machine_spec.location['cloud_service']
         
     | 
| 
       158 
     | 
    
         
            -
             
     | 
| 
       159 
     | 
    
         
            -
                  # Check if we need to proceed
         
     | 
| 
       160 
     | 
    
         
            -
                  return if vm.nil? || vm_name.nil? || cloud_service.nil?
         
     | 
| 
       161 
     | 
    
         
            -
             
     | 
| 
       162 
     | 
    
         
            -
                  # Skip if we don't actually need to do anything
         
     | 
| 
       163 
     | 
    
         
            -
                  return unless action_handler.should_perform_actions
         
     | 
| 
       164 
     | 
    
         
            -
             
     | 
| 
       165 
     | 
    
         
            -
                  # TODO: action_handler.do |block| ?
         
     | 
| 
       166 
     | 
    
         
            -
                  action_handler.report_progress "Destroying VM #{machine_spec.name}!"
         
     | 
| 
       167 
     | 
    
         
            -
                  azure_vm_service.delete_virtual_machine(vm_name, cloud_service)
         
     | 
| 
       168 
     | 
    
         
            -
                  action_handler.report_progress "Destroyed VM #{machine_spec.name}!"
         
     | 
| 
       169 
     | 
    
         
            -
                end
         
     | 
| 
       170 
     | 
    
         
            -
             
     | 
| 
       171 
     | 
    
         
            -
                private
         
     | 
| 
       172 
     | 
    
         
            -
             
     | 
| 
       173 
     | 
    
         
            -
                def machine_for(machine_spec, machine_options, vm = nil)
         
     | 
| 
       174 
     | 
    
         
            -
                  vm ||= vm_for(machine_spec)
         
     | 
| 
       175 
     | 
    
         
            -
             
     | 
| 
       176 
     | 
    
         
            -
                  fail "VM for node #{machine_spec.name} has not been created!" unless vm
         
     | 
| 
       177 
     | 
    
         
            -
             
     | 
| 
       178 
     | 
    
         
            -
                  transport =  transport_for(machine_spec, machine_options, vm)
         
     | 
| 
       179 
     | 
    
         
            -
                  convergence_strategy = convergence_strategy_for(machine_spec, machine_options)
         
     | 
| 
       180 
     | 
    
         
            -
             
     | 
| 
       181 
     | 
    
         
            -
                  if machine_spec.location['is_windows']
         
     | 
| 
       182 
     | 
    
         
            -
                    Chef::Provisioning::Machine::WindowsMachine.new(machine_spec, transport, convergence_strategy)
         
     | 
| 
       183 
     | 
    
         
            -
                  else
         
     | 
| 
       184 
     | 
    
         
            -
                    Chef::Provisioning::Machine::UnixMachine.new(machine_spec, transport, convergence_strategy)
         
     | 
| 
       185 
     | 
    
         
            -
                  end
         
     | 
| 
       186 
     | 
    
         
            -
                end
         
     | 
| 
       187 
     | 
    
         
            -
             
     | 
| 
       188 
     | 
    
         
            -
                def azure_vm_service
         
     | 
| 
       189 
     | 
    
         
            -
                  @vm_service ||= Azure::VirtualMachineManagementService.new
         
     | 
| 
       190 
     | 
    
         
            -
                end
         
     | 
| 
       191 
     | 
    
         
            -
             
     | 
| 
       192 
     | 
    
         
            -
                def azure_cloud_service_service
         
     | 
| 
       193 
     | 
    
         
            -
                  @cloud_service_service ||= Azure::CloudServiceManagementService.new
         
     | 
| 
       194 
     | 
    
         
            -
                end
         
     | 
| 
       195 
     | 
    
         
            -
             
     | 
| 
       196 
     | 
    
         
            -
                def default_ssh_username
         
     | 
| 
       197 
     | 
    
         
            -
                  'ubuntu'
         
     | 
| 
       198 
     | 
    
         
            -
                end
         
     | 
| 
       199 
     | 
    
         
            -
             
     | 
| 
       200 
     | 
    
         
            -
                def vm_for(machine_spec)
         
     | 
| 
       201 
     | 
    
         
            -
                  if machine_spec.location && machine_spec.name
         
     | 
| 
       202 
     | 
    
         
            -
                    existing_vms = azure_vm_service.list_virtual_machines
         
     | 
| 
       203 
     | 
    
         
            -
                    existing_vms.select { |vm| vm.vm_name == machine_spec.name }.first
         
     | 
| 
       204 
     | 
    
         
            -
                  else
         
     | 
| 
       205 
     | 
    
         
            -
                    nil
         
     | 
| 
       206 
     | 
    
         
            -
                  end
         
     | 
| 
       207 
     | 
    
         
            -
                end
         
     | 
| 
       208 
     | 
    
         
            -
             
     | 
| 
       209 
     | 
    
         
            -
                def transport_for(machine_spec, machine_options, vm)
         
     | 
| 
       210 
     | 
    
         
            -
                  if machine_spec.location['is_windows']
         
     | 
| 
       211 
     | 
    
         
            -
                    create_winrm_transport(machine_spec, machine_options, vm)
         
     | 
| 
       212 
     | 
    
         
            -
                  else
         
     | 
| 
       213 
     | 
    
         
            -
                    create_ssh_transport(machine_spec, machine_options, vm)
         
     | 
| 
       214 
     | 
    
         
            -
                  end
         
     | 
| 
       215 
     | 
    
         
            -
                end
         
     | 
| 
       216 
     | 
    
         
            -
             
     | 
| 
       217 
     | 
    
         
            -
                def default_image_for_location(location)
         
     | 
| 
       218 
     | 
    
         
            -
                  Chef::Log.debug("Choosing default image for region '#{location}'")
         
     | 
| 
       219 
     | 
    
         
            -
             
     | 
| 
       220 
     | 
    
         
            -
                  case location
         
     | 
| 
       221 
     | 
    
         
            -
                  when 'East US'
         
     | 
| 
       222 
     | 
    
         
            -
                  when 'Southeast Asia'
         
     | 
| 
       223 
     | 
    
         
            -
                  when 'West US'
         
     | 
| 
       224 
     | 
    
         
            -
                    'b39f27a8b8c64d52b05eac6a62ebad85__Ubuntu-14_04_1-LTS-amd64-server-20140927-en-us-30GB'
         
     | 
| 
       225 
     | 
    
         
            -
                  else
         
     | 
| 
       226 
     | 
    
         
            -
                    raise 'Unsupported location!'
         
     | 
| 
       227 
     | 
    
         
            -
                  end
         
     | 
| 
       228 
     | 
    
         
            -
                end
         
     | 
| 
       229 
     | 
    
         
            -
             
     | 
| 
       230 
     | 
    
         
            -
                def create_ssh_transport(machine_spec, machine_options, vm)
         
     | 
| 
       231 
     | 
    
         
            -
                   
     | 
| 
       232 
     | 
    
         
            -
                   
     | 
| 
       233 
     | 
    
         
            -
                   
     | 
| 
       234 
     | 
    
         
            -
             
     | 
| 
       235 
     | 
    
         
            -
             
     | 
| 
       236 
     | 
    
         
            -
                   
     | 
| 
       237 
     | 
    
         
            -
             
     | 
| 
       238 
     | 
    
         
            -
                     
     | 
| 
       239 
     | 
    
         
            -
             
     | 
| 
       240 
     | 
    
         
            -
             
     | 
| 
       241 
     | 
    
         
            -
             
     | 
| 
       242 
     | 
    
         
            -
                  options 
     | 
| 
       243 
     | 
    
         
            -
             
     | 
| 
       244 
     | 
    
         
            -
             
     | 
| 
       245 
     | 
    
         
            -
                  #  
     | 
| 
       246 
     | 
    
         
            -
                   
     | 
| 
       247 
     | 
    
         
            -
                  options[: 
     | 
| 
       248 
     | 
    
         
            -
             
     | 
| 
       249 
     | 
    
         
            -
             
     | 
| 
       250 
     | 
    
         
            -
             
     | 
| 
       251 
     | 
    
         
            -
             
     | 
| 
       252 
     | 
    
         
            -
             
     | 
| 
       253 
     | 
    
         
            -
             
     | 
| 
       254 
     | 
    
         
            -
                   
     | 
| 
       255 
     | 
    
         
            -
             
     | 
| 
       256 
     | 
    
         
            -
                      : 
     | 
| 
       257 
     | 
    
         
            -
             
     | 
| 
       258 
     | 
    
         
            -
             
     | 
| 
       259 
     | 
    
         
            -
             
     | 
| 
       260 
     | 
    
         
            -
             
     | 
| 
       261 
     | 
    
         
            -
                     
     | 
| 
       262 
     | 
    
         
            -
                     
     | 
| 
       263 
     | 
    
         
            -
                     
     | 
| 
       264 
     | 
    
         
            -
                     
     | 
| 
       265 
     | 
    
         
            -
                     
     | 
| 
       266 
     | 
    
         
            -
             
     | 
| 
       267 
     | 
    
         
            -
                      : 
     | 
| 
       268 
     | 
    
         
            -
                      : 
     | 
| 
       269 
     | 
    
         
            -
             
     | 
| 
       270 
     | 
    
         
            -
             
     | 
| 
       271 
     | 
    
         
            -
             
     | 
| 
       272 
     | 
    
         
            -
             
     | 
| 
       273 
     | 
    
         
            -
             
     | 
| 
       274 
     | 
    
         
            -
                     
     | 
| 
       275 
     | 
    
         
            -
                     
     | 
| 
       276 
     | 
    
         
            -
                     
     | 
| 
       277 
     | 
    
         
            -
                     
     | 
| 
       278 
     | 
    
         
            -
                     
     | 
| 
       279 
     | 
    
         
            -
             
     | 
| 
       280 
     | 
    
         
            -
                      : 
     | 
| 
       281 
     | 
    
         
            -
             
     | 
| 
       282 
     | 
    
         
            -
             
     | 
| 
       283 
     | 
    
         
            -
             
     | 
| 
       284 
     | 
    
         
            -
             
     | 
| 
       285 
     | 
    
         
            -
                   
     | 
| 
       286 
     | 
    
         
            -
             
     | 
| 
       287 
     | 
    
         
            -
             
     | 
| 
       288 
     | 
    
         
            -
             
     | 
| 
       289 
     | 
    
         
            -
             
     | 
| 
       290 
     | 
    
         
            -
             
     | 
| 
       291 
     | 
    
         
            -
             
     | 
| 
       292 
     | 
    
         
            -
             
     | 
| 
       293 
     | 
    
         
            -
             
     | 
| 
       294 
     | 
    
         
            -
             
     | 
| 
       295 
     | 
    
         
            -
             
     | 
| 
       296 
     | 
    
         
            -
             
     | 
| 
       297 
     | 
    
         
            -
             
     | 
| 
       298 
     | 
    
         
            -
                   
     | 
| 
       299 
     | 
    
         
            -
                   
     | 
| 
       300 
     | 
    
         
            -
             
     | 
| 
       301 
     | 
    
         
            -
             
     | 
| 
       302 
     | 
    
         
            -
             
     | 
| 
       303 
     | 
    
         
            -
             
     | 
| 
       304 
     | 
    
         
            -
             
     | 
| 
       305 
     | 
    
         
            -
             
     | 
| 
       306 
     | 
    
         
            -
             
     | 
| 
       307 
     | 
    
         
            -
             
     | 
| 
       308 
     | 
    
         
            -
             
     | 
| 
       309 
     | 
    
         
            -
             
     | 
| 
       310 
     | 
    
         
            -
             
     | 
| 
       311 
     | 
    
         
            -
             
     | 
| 
       312 
     | 
    
         
            -
             
     | 
| 
       313 
     | 
    
         
            -
             
     | 
| 
       314 
     | 
    
         
            -
             
     | 
| 
       315 
     | 
    
         
            -
             
     | 
| 
       316 
     | 
    
         
            -
                   
     | 
| 
       317 
     | 
    
         
            -
             
     | 
| 
       318 
     | 
    
         
            -
             
     | 
| 
       319 
     | 
    
         
            -
                   
     | 
| 
       320 
     | 
    
         
            -
             
     | 
| 
       321 
     | 
    
         
            -
             
     | 
| 
       322 
     | 
    
         
            -
                   
     | 
| 
       323 
     | 
    
         
            -
                   
     | 
| 
       324 
     | 
    
         
            -
             
     | 
| 
       325 
     | 
    
         
            -
             
     | 
| 
       326 
     | 
    
         
            -
                   
     | 
| 
       327 
     | 
    
         
            -
             
     | 
| 
       328 
     | 
    
         
            -
                     
     | 
| 
       329 
     | 
    
         
            -
                     
     | 
| 
       330 
     | 
    
         
            -
                     
     | 
| 
       331 
     | 
    
         
            -
                     
     | 
| 
       332 
     | 
    
         
            -
             
     | 
| 
       333 
     | 
    
         
            -
                   
     | 
| 
       334 
     | 
    
         
            -
             
     | 
| 
       335 
     | 
    
         
            -
             
     | 
| 
       336 
     | 
    
         
            -
             
     | 
| 
       337 
     | 
    
         
            -
             
     | 
| 
       338 
     | 
    
         
            -
                   
     | 
| 
       339 
     | 
    
         
            -
             
     | 
| 
       340 
     | 
    
         
            -
             
     | 
| 
       341 
     | 
    
         
            -
                  return  
     | 
| 
       342 
     | 
    
         
            -
             
     | 
| 
       343 
     | 
    
         
            -
             
     | 
| 
       344 
     | 
    
         
            -
                   
     | 
| 
       345 
     | 
    
         
            -
                   
     | 
| 
       346 
     | 
    
         
            -
             
     | 
| 
       347 
     | 
    
         
            -
             
     | 
| 
       348 
     | 
    
         
            -
                   
     | 
| 
       349 
     | 
    
         
            -
             
     | 
| 
       350 
     | 
    
         
            -
                     
     | 
| 
       351 
     | 
    
         
            -
                     
     | 
| 
       352 
     | 
    
         
            -
             
     | 
| 
       353 
     | 
    
         
            -
                   
     | 
| 
       354 
     | 
    
         
            -
             
     | 
| 
       355 
     | 
    
         
            -
             
     | 
| 
       356 
     | 
    
         
            -
             
     | 
| 
       357 
     | 
    
         
            -
            end
         
     | 
| 
       358 
     | 
    
         
            -
            end
         
     | 
| 
       359 
     | 
    
         
            -
            end
         
     | 
| 
      
 1 
     | 
    
         
            +
            require 'chef/mixin/shell_out'
         
     | 
| 
      
 2 
     | 
    
         
            +
            require 'chef/provisioning/driver'
         
     | 
| 
      
 3 
     | 
    
         
            +
            require 'chef/provisioning/convergence_strategy/install_cached'
         
     | 
| 
      
 4 
     | 
    
         
            +
            require 'chef/provisioning/convergence_strategy/install_sh'
         
     | 
| 
      
 5 
     | 
    
         
            +
            require 'chef/provisioning/convergence_strategy/install_msi'
         
     | 
| 
      
 6 
     | 
    
         
            +
            require 'chef/provisioning/convergence_strategy/no_converge'
         
     | 
| 
      
 7 
     | 
    
         
            +
            require 'chef/provisioning/transport/ssh'
         
     | 
| 
      
 8 
     | 
    
         
            +
            require 'chef/provisioning/transport/winrm'
         
     | 
| 
      
 9 
     | 
    
         
            +
            require 'chef/provisioning/machine/windows_machine'
         
     | 
| 
      
 10 
     | 
    
         
            +
            require 'chef/provisioning/machine/unix_machine'
         
     | 
| 
      
 11 
     | 
    
         
            +
            require 'chef/provisioning/machine_spec'
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
      
 13 
     | 
    
         
            +
            require 'chef/provisioning/azure_driver/version'
         
     | 
| 
      
 14 
     | 
    
         
            +
            require 'chef/provisioning/azure_driver/subscriptions'
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
            require 'yaml'
         
     | 
| 
      
 17 
     | 
    
         
            +
            require 'azure'
         
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
      
 19 
     | 
    
         
            +
            class Chef
         
     | 
| 
      
 20 
     | 
    
         
            +
            module Provisioning
         
     | 
| 
      
 21 
     | 
    
         
            +
            module AzureDriver
         
     | 
| 
      
 22 
     | 
    
         
            +
              # Provisions machines using the Azure SDK
         
     | 
| 
      
 23 
     | 
    
         
            +
              class Driver < Chef::Provisioning::Driver
         
     | 
| 
      
 24 
     | 
    
         
            +
                attr_reader :region
         
     | 
| 
      
 25 
     | 
    
         
            +
             
     | 
| 
      
 26 
     | 
    
         
            +
                # Construct an AzureDriver object from a URL - used to parse existing URL
         
     | 
| 
      
 27 
     | 
    
         
            +
                # data to hydrate a driver object.
         
     | 
| 
      
 28 
     | 
    
         
            +
                # URL scheme:
         
     | 
| 
      
 29 
     | 
    
         
            +
                # azure:subscription_id
         
     | 
| 
      
 30 
     | 
    
         
            +
                # @return [AzureDriver] A chef-provisioning Azure driver object for the given URL
         
     | 
| 
      
 31 
     | 
    
         
            +
                def self.from_url(driver_url, config)
         
     | 
| 
      
 32 
     | 
    
         
            +
                  Driver.new(driver_url, config)
         
     | 
| 
      
 33 
     | 
    
         
            +
                end
         
     | 
| 
      
 34 
     | 
    
         
            +
             
     | 
| 
      
 35 
     | 
    
         
            +
                def self.canonicalize_url(driver_url, config)
         
     | 
| 
      
 36 
     | 
    
         
            +
                  scheme, account_id = driver_url.split(':', 2)
         
     | 
| 
      
 37 
     | 
    
         
            +
                  if account_id.nil? || account_id.empty?
         
     | 
| 
      
 38 
     | 
    
         
            +
                    subscription = Subscriptions.default_subscription(config)
         
     | 
| 
      
 39 
     | 
    
         
            +
                    if !subscription
         
     | 
| 
      
 40 
     | 
    
         
            +
                      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."
         
     | 
| 
      
 41 
     | 
    
         
            +
                    end
         
     | 
| 
      
 42 
     | 
    
         
            +
                    config = Cheffish::MergedConfig.new({ azure_subscriptions: subscription }, config)
         
     | 
| 
      
 43 
     | 
    
         
            +
                  end
         
     | 
| 
      
 44 
     | 
    
         
            +
                  if subscription
         
     | 
| 
      
 45 
     | 
    
         
            +
                    [ "#{scheme}:#{subscription[:subscription_id]}", config ]
         
     | 
| 
      
 46 
     | 
    
         
            +
                  else
         
     | 
| 
      
 47 
     | 
    
         
            +
                    [ driver_url, config]
         
     | 
| 
      
 48 
     | 
    
         
            +
                  end
         
     | 
| 
      
 49 
     | 
    
         
            +
                end
         
     | 
| 
      
 50 
     | 
    
         
            +
             
     | 
| 
      
 51 
     | 
    
         
            +
                def initialize(driver_url, config)
         
     | 
| 
      
 52 
     | 
    
         
            +
                  super
         
     | 
| 
      
 53 
     | 
    
         
            +
                  scheme, subscription_id = driver_url.split(':', 2)
         
     | 
| 
      
 54 
     | 
    
         
            +
                  @subscription = Subscriptions.get_subscription(config, subscription_id)
         
     | 
| 
      
 55 
     | 
    
         
            +
                  if !subscription
         
     | 
| 
      
 56 
     | 
    
         
            +
                    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."
         
     | 
| 
      
 57 
     | 
    
         
            +
                  end
         
     | 
| 
      
 58 
     | 
    
         
            +
             
     | 
| 
      
 59 
     | 
    
         
            +
                  # TODO make this instantiable so we can have multiple drivers ......
         
     | 
| 
      
 60 
     | 
    
         
            +
                  Azure.configure do |azure|
         
     | 
| 
      
 61 
     | 
    
         
            +
                    # Configure these 3 properties to use Storage
         
     | 
| 
      
 62 
     | 
    
         
            +
                    azure.management_certificate = subscription[:management_certificate]
         
     | 
| 
      
 63 
     | 
    
         
            +
                    azure.subscription_id        = subscription[:subscription_id]
         
     | 
| 
      
 64 
     | 
    
         
            +
                    azure.management_endpoint    = subscription[:management_endpoint]
         
     | 
| 
      
 65 
     | 
    
         
            +
                  end
         
     | 
| 
      
 66 
     | 
    
         
            +
                end
         
     | 
| 
      
 67 
     | 
    
         
            +
             
     | 
| 
      
 68 
     | 
    
         
            +
                attr_reader :subscription
         
     | 
| 
      
 69 
     | 
    
         
            +
             
     | 
| 
      
 70 
     | 
    
         
            +
                # -- Machine methods --
         
     | 
| 
      
 71 
     | 
    
         
            +
             
     | 
| 
      
 72 
     | 
    
         
            +
                # Allocate a new machine with the Azure API and start it up, without
         
     | 
| 
      
 73 
     | 
    
         
            +
                # blocking to wait for it. Creates any needed resources to get a machine
         
     | 
| 
      
 74 
     | 
    
         
            +
                # up and running.
         
     | 
| 
      
 75 
     | 
    
         
            +
                # @param (see Chef::Provisioning::Driver#allocate_machine)
         
     | 
| 
      
 76 
     | 
    
         
            +
                def allocate_machine(action_handler, machine_spec, machine_options)
         
     | 
| 
      
 77 
     | 
    
         
            +
                  existing_vm = vm_for(machine_spec)
         
     | 
| 
      
 78 
     | 
    
         
            +
             
     | 
| 
      
 79 
     | 
    
         
            +
                  # We don't need to do anything if the existing VM is found
         
     | 
| 
      
 80 
     | 
    
         
            +
                  return if existing_vm
         
     | 
| 
      
 81 
     | 
    
         
            +
             
     | 
| 
      
 82 
     | 
    
         
            +
                  bootstrap_options = machine_options[:bootstrap_options] || {}
         
     | 
| 
      
 83 
     | 
    
         
            +
                  bootstrap_options[:vm_size] ||= 'Small'
         
     | 
| 
      
 84 
     | 
    
         
            +
                  bootstrap_options[:cloud_service_name] ||= 'chefprovisioning'
         
     | 
| 
      
 85 
     | 
    
         
            +
                  bootstrap_options[:storage_account_name] ||=  'chefprovisioning'
         
     | 
| 
      
 86 
     | 
    
         
            +
                  bootstrap_options[:location] ||=  'West US'
         
     | 
| 
      
 87 
     | 
    
         
            +
             
     | 
| 
      
 88 
     | 
    
         
            +
                  location = bootstrap_options[:location]
         
     | 
| 
      
 89 
     | 
    
         
            +
             
     | 
| 
      
 90 
     | 
    
         
            +
                  machine_spec.location = {
         
     | 
| 
      
 91 
     | 
    
         
            +
                    'driver_url' => driver_url,
         
     | 
| 
      
 92 
     | 
    
         
            +
                    'driver_version' => Chef::Provisioning::AzureDriver::VERSION,
         
     | 
| 
      
 93 
     | 
    
         
            +
                    'allocated_at' => Time.now.utc.to_s,
         
     | 
| 
      
 94 
     | 
    
         
            +
                    'host_node' => action_handler.host_node,
         
     | 
| 
      
 95 
     | 
    
         
            +
                    'image_id' => machine_options[:image_id],
         
     | 
| 
      
 96 
     | 
    
         
            +
                    'location' => location,
         
     | 
| 
      
 97 
     | 
    
         
            +
                    'cloud_service' => bootstrap_options[:cloud_service_name]
         
     | 
| 
      
 98 
     | 
    
         
            +
                  }
         
     | 
| 
      
 99 
     | 
    
         
            +
             
     | 
| 
      
 100 
     | 
    
         
            +
                  image_id = machine_options[:image_id] || default_image_for_location(location)
         
     | 
| 
      
 101 
     | 
    
         
            +
             
     | 
| 
      
 102 
     | 
    
         
            +
                  Chef::Log.debug "Azure bootstrap options: #{bootstrap_options.inspect}"
         
     | 
| 
      
 103 
     | 
    
         
            +
             
     | 
| 
      
 104 
     | 
    
         
            +
                  params = {
         
     | 
| 
      
 105 
     | 
    
         
            +
                      vm_name: machine_spec.name,
         
     | 
| 
      
 106 
     | 
    
         
            +
                      vm_user: bootstrap_options[:vm_user] || default_ssh_username,
         
     | 
| 
      
 107 
     | 
    
         
            +
                      image: image_id,
         
     | 
| 
      
 108 
     | 
    
         
            +
                      # This is only until SSH keys are added
         
     | 
| 
      
 109 
     | 
    
         
            +
                      password: machine_options[:password],
         
     | 
| 
      
 110 
     | 
    
         
            +
                      location: location,
         
     | 
| 
      
 111 
     | 
    
         
            +
                      cloud_service_name: bootstrap_options[:cloud_service_name]
         
     | 
| 
      
 112 
     | 
    
         
            +
                  }
         
     | 
| 
      
 113 
     | 
    
         
            +
             
     | 
| 
      
 114 
     | 
    
         
            +
                  # If the cloud service exists already, need to add a role to it - otherwise create virtual machine (including cloud service)
         
     | 
| 
      
 115 
     | 
    
         
            +
                  cloud_service = azure_cloud_service_service.get_cloud_service(bootstrap_options[:cloud_service_name])
         
     | 
| 
      
 116 
     | 
    
         
            +
                  existing_deployment = azure_vm_service.list_virtual_machines(bootstrap_options[:cloud_service_name]).any?
         
     | 
| 
      
 117 
     | 
    
         
            +
             
     | 
| 
      
 118 
     | 
    
         
            +
                  if cloud_service and existing_deployment
         
     | 
| 
      
 119 
     | 
    
         
            +
                    action_handler.report_progress "Cloud Service #{bootstrap_options[:cloud_service_name]} already exists, adding role."
         
     | 
| 
      
 120 
     | 
    
         
            +
                    action_handler.report_progress "Creating #{machine_spec.name} with image #{image_id} in #{bootstrap_options[:cloud_service_name]}..."
         
     | 
| 
      
 121 
     | 
    
         
            +
                    vm = azure_vm_service.add_role(params, bootstrap_options)
         
     | 
| 
      
 122 
     | 
    
         
            +
                  else
         
     | 
| 
      
 123 
     | 
    
         
            +
                    action_handler.report_progress "Creating #{machine_spec.name} with image #{image_id} in #{location}..."
         
     | 
| 
      
 124 
     | 
    
         
            +
                    vm = azure_vm_service.create_virtual_machine(params, bootstrap_options)
         
     | 
| 
      
 125 
     | 
    
         
            +
                  end
         
     | 
| 
      
 126 
     | 
    
         
            +
             
     | 
| 
      
 127 
     | 
    
         
            +
                  machine_spec.location['vm_name'] = vm.vm_name
         
     | 
| 
      
 128 
     | 
    
         
            +
                  machine_spec.location['is_windows'] = (true if vm.os_type == 'Windows') || false
         
     | 
| 
      
 129 
     | 
    
         
            +
                  action_handler.report_progress "Created #{vm.vm_name} in #{location}..."
         
     | 
| 
      
 130 
     | 
    
         
            +
                end
         
     | 
| 
      
 131 
     | 
    
         
            +
             
     | 
| 
      
 132 
     | 
    
         
            +
                # (see Chef::Provisioning::Driver#ready_machine)
         
     | 
| 
      
 133 
     | 
    
         
            +
                def ready_machine(action_handler, machine_spec, machine_options)
         
     | 
| 
      
 134 
     | 
    
         
            +
                  vm = vm_for(machine_spec)
         
     | 
| 
      
 135 
     | 
    
         
            +
                  location = machine_spec.location['location']
         
     | 
| 
      
 136 
     | 
    
         
            +
             
     | 
| 
      
 137 
     | 
    
         
            +
                  if vm.nil?
         
     | 
| 
      
 138 
     | 
    
         
            +
                    fail "Machine #{machine_spec.name} does not have a VM associated with it, or the VM does not exist."
         
     | 
| 
      
 139 
     | 
    
         
            +
                  end
         
     | 
| 
      
 140 
     | 
    
         
            +
             
     | 
| 
      
 141 
     | 
    
         
            +
                  # TODO: Not sure if this is the right thing to check
         
     | 
| 
      
 142 
     | 
    
         
            +
                  if vm.status != 'ReadyRole'
         
     | 
| 
      
 143 
     | 
    
         
            +
                    action_handler.report_progress "Readying #{machine_spec.name} in #{location}..."
         
     | 
| 
      
 144 
     | 
    
         
            +
                    wait_until_ready(action_handler, machine_spec)
         
     | 
| 
      
 145 
     | 
    
         
            +
                    wait_for_transport(action_handler, machine_spec, machine_options)
         
     | 
| 
      
 146 
     | 
    
         
            +
                  else
         
     | 
| 
      
 147 
     | 
    
         
            +
                    action_handler.report_progress "#{machine_spec.name} already ready in #{location}!"
         
     | 
| 
      
 148 
     | 
    
         
            +
                  end
         
     | 
| 
      
 149 
     | 
    
         
            +
             
     | 
| 
      
 150 
     | 
    
         
            +
                  machine_for(machine_spec, machine_options, vm)
         
     | 
| 
      
 151 
     | 
    
         
            +
                end
         
     | 
| 
      
 152 
     | 
    
         
            +
             
     | 
| 
      
 153 
     | 
    
         
            +
                # (see Chef::Provisioning::Driver#destroy_machine)
         
     | 
| 
      
 154 
     | 
    
         
            +
                def destroy_machine(action_handler, machine_spec, machine_options)
         
     | 
| 
      
 155 
     | 
    
         
            +
                  vm = vm_for(machine_spec)
         
     | 
| 
      
 156 
     | 
    
         
            +
                  vm_name = machine_spec.name
         
     | 
| 
      
 157 
     | 
    
         
            +
                  cloud_service = machine_spec.location['cloud_service']
         
     | 
| 
      
 158 
     | 
    
         
            +
             
     | 
| 
      
 159 
     | 
    
         
            +
                  # Check if we need to proceed
         
     | 
| 
      
 160 
     | 
    
         
            +
                  return if vm.nil? || vm_name.nil? || cloud_service.nil?
         
     | 
| 
      
 161 
     | 
    
         
            +
             
     | 
| 
      
 162 
     | 
    
         
            +
                  # Skip if we don't actually need to do anything
         
     | 
| 
      
 163 
     | 
    
         
            +
                  return unless action_handler.should_perform_actions
         
     | 
| 
      
 164 
     | 
    
         
            +
             
     | 
| 
      
 165 
     | 
    
         
            +
                  # TODO: action_handler.do |block| ?
         
     | 
| 
      
 166 
     | 
    
         
            +
                  action_handler.report_progress "Destroying VM #{machine_spec.name}!"
         
     | 
| 
      
 167 
     | 
    
         
            +
                  azure_vm_service.delete_virtual_machine(vm_name, cloud_service)
         
     | 
| 
      
 168 
     | 
    
         
            +
                  action_handler.report_progress "Destroyed VM #{machine_spec.name}!"
         
     | 
| 
      
 169 
     | 
    
         
            +
                end
         
     | 
| 
      
 170 
     | 
    
         
            +
             
     | 
| 
      
 171 
     | 
    
         
            +
                private
         
     | 
| 
      
 172 
     | 
    
         
            +
             
     | 
| 
      
 173 
     | 
    
         
            +
                def machine_for(machine_spec, machine_options, vm = nil)
         
     | 
| 
      
 174 
     | 
    
         
            +
                  vm ||= vm_for(machine_spec)
         
     | 
| 
      
 175 
     | 
    
         
            +
             
     | 
| 
      
 176 
     | 
    
         
            +
                  fail "VM for node #{machine_spec.name} has not been created!" unless vm
         
     | 
| 
      
 177 
     | 
    
         
            +
             
     | 
| 
      
 178 
     | 
    
         
            +
                  transport =  transport_for(machine_spec, machine_options, vm)
         
     | 
| 
      
 179 
     | 
    
         
            +
                  convergence_strategy = convergence_strategy_for(machine_spec, machine_options)
         
     | 
| 
      
 180 
     | 
    
         
            +
             
     | 
| 
      
 181 
     | 
    
         
            +
                  if machine_spec.location['is_windows']
         
     | 
| 
      
 182 
     | 
    
         
            +
                    Chef::Provisioning::Machine::WindowsMachine.new(machine_spec, transport, convergence_strategy)
         
     | 
| 
      
 183 
     | 
    
         
            +
                  else
         
     | 
| 
      
 184 
     | 
    
         
            +
                    Chef::Provisioning::Machine::UnixMachine.new(machine_spec, transport, convergence_strategy)
         
     | 
| 
      
 185 
     | 
    
         
            +
                  end
         
     | 
| 
      
 186 
     | 
    
         
            +
                end
         
     | 
| 
      
 187 
     | 
    
         
            +
             
     | 
| 
      
 188 
     | 
    
         
            +
                def azure_vm_service
         
     | 
| 
      
 189 
     | 
    
         
            +
                  @vm_service ||= Azure::VirtualMachineManagementService.new
         
     | 
| 
      
 190 
     | 
    
         
            +
                end
         
     | 
| 
      
 191 
     | 
    
         
            +
             
     | 
| 
      
 192 
     | 
    
         
            +
                def azure_cloud_service_service
         
     | 
| 
      
 193 
     | 
    
         
            +
                  @cloud_service_service ||= Azure::CloudServiceManagementService.new
         
     | 
| 
      
 194 
     | 
    
         
            +
                end
         
     | 
| 
      
 195 
     | 
    
         
            +
             
     | 
| 
      
 196 
     | 
    
         
            +
                def default_ssh_username
         
     | 
| 
      
 197 
     | 
    
         
            +
                  'ubuntu'
         
     | 
| 
      
 198 
     | 
    
         
            +
                end
         
     | 
| 
      
 199 
     | 
    
         
            +
             
     | 
| 
      
 200 
     | 
    
         
            +
                def vm_for(machine_spec)
         
     | 
| 
      
 201 
     | 
    
         
            +
                  if machine_spec.location && machine_spec.name
         
     | 
| 
      
 202 
     | 
    
         
            +
                    existing_vms = azure_vm_service.list_virtual_machines
         
     | 
| 
      
 203 
     | 
    
         
            +
                    existing_vms.select { |vm| vm.vm_name == machine_spec.name }.first
         
     | 
| 
      
 204 
     | 
    
         
            +
                  else
         
     | 
| 
      
 205 
     | 
    
         
            +
                    nil
         
     | 
| 
      
 206 
     | 
    
         
            +
                  end
         
     | 
| 
      
 207 
     | 
    
         
            +
                end
         
     | 
| 
      
 208 
     | 
    
         
            +
             
     | 
| 
      
 209 
     | 
    
         
            +
                def transport_for(machine_spec, machine_options, vm)
         
     | 
| 
      
 210 
     | 
    
         
            +
                  if machine_spec.location['is_windows']
         
     | 
| 
      
 211 
     | 
    
         
            +
                    create_winrm_transport(machine_spec, machine_options, vm)
         
     | 
| 
      
 212 
     | 
    
         
            +
                  else
         
     | 
| 
      
 213 
     | 
    
         
            +
                    create_ssh_transport(machine_spec, machine_options, vm)
         
     | 
| 
      
 214 
     | 
    
         
            +
                  end
         
     | 
| 
      
 215 
     | 
    
         
            +
                end
         
     | 
| 
      
 216 
     | 
    
         
            +
             
     | 
| 
      
 217 
     | 
    
         
            +
                def default_image_for_location(location)
         
     | 
| 
      
 218 
     | 
    
         
            +
                  Chef::Log.debug("Choosing default image for region '#{location}'")
         
     | 
| 
      
 219 
     | 
    
         
            +
             
     | 
| 
      
 220 
     | 
    
         
            +
                  case location
         
     | 
| 
      
 221 
     | 
    
         
            +
                  when 'East US'
         
     | 
| 
      
 222 
     | 
    
         
            +
                  when 'Southeast Asia'
         
     | 
| 
      
 223 
     | 
    
         
            +
                  when 'West US'
         
     | 
| 
      
 224 
     | 
    
         
            +
                    'b39f27a8b8c64d52b05eac6a62ebad85__Ubuntu-14_04_1-LTS-amd64-server-20140927-en-us-30GB'
         
     | 
| 
      
 225 
     | 
    
         
            +
                  else
         
     | 
| 
      
 226 
     | 
    
         
            +
                    raise 'Unsupported location!'
         
     | 
| 
      
 227 
     | 
    
         
            +
                  end
         
     | 
| 
      
 228 
     | 
    
         
            +
                end
         
     | 
| 
      
 229 
     | 
    
         
            +
             
     | 
| 
      
 230 
     | 
    
         
            +
                def create_ssh_transport(machine_spec, machine_options, vm)
         
     | 
| 
      
 231 
     | 
    
         
            +
                  bootstrap_options = machine_options[:bootstrap_options] || {}
         
     | 
| 
      
 232 
     | 
    
         
            +
                  username = bootstrap_options[:vm_user] || default_ssh_username
         
     | 
| 
      
 233 
     | 
    
         
            +
                  tcp_endpoint = vm.tcp_endpoints.select { |tcp| tcp[:name] == 'SSH' }.first
         
     | 
| 
      
 234 
     | 
    
         
            +
                  remote_host = tcp_endpoint[:vip]
         
     | 
| 
      
 235 
     | 
    
         
            +
             
     | 
| 
      
 236 
     | 
    
         
            +
                  # TODO: not this... replace with SSH key ASAP, only for getting this thing going...
         
     | 
| 
      
 237 
     | 
    
         
            +
                  ssh_options = {
         
     | 
| 
      
 238 
     | 
    
         
            +
                    password: machine_options[:password],
         
     | 
| 
      
 239 
     | 
    
         
            +
                    port: tcp_endpoint[:public_port] # use public port from Cloud Service endpoint
         
     | 
| 
      
 240 
     | 
    
         
            +
                  }
         
     | 
| 
      
 241 
     | 
    
         
            +
             
     | 
| 
      
 242 
     | 
    
         
            +
                  options = {}
         
     | 
| 
      
 243 
     | 
    
         
            +
                  options[:prefix] = 'sudo ' if machine_spec.location[:sudo] || username != 'root'
         
     | 
| 
      
 244 
     | 
    
         
            +
             
     | 
| 
      
 245 
     | 
    
         
            +
                  # Enable pty by default
         
     | 
| 
      
 246 
     | 
    
         
            +
                  # TODO: why?
         
     | 
| 
      
 247 
     | 
    
         
            +
                  options[:ssh_pty_enable] = true
         
     | 
| 
      
 248 
     | 
    
         
            +
                  options[:ssh_gateway] ||= machine_spec.location['ssh_gateway']
         
     | 
| 
      
 249 
     | 
    
         
            +
             
     | 
| 
      
 250 
     | 
    
         
            +
                  Chef::Provisioning::Transport::SSH.new(remote_host, username, ssh_options, options, config)
         
     | 
| 
      
 251 
     | 
    
         
            +
                end
         
     | 
| 
      
 252 
     | 
    
         
            +
             
     | 
| 
      
 253 
     | 
    
         
            +
                def create_winrm_transport(machine_spec, machine_options, instance)
         
     | 
| 
      
 254 
     | 
    
         
            +
                  winrm_transport_options = machine_options[:bootstrap_options][:winrm_transport]
         
     | 
| 
      
 255 
     | 
    
         
            +
                  shared_winrm_options = {
         
     | 
| 
      
 256 
     | 
    
         
            +
                      :user => machine_options[:vm_user] || 'localadmin',
         
     | 
| 
      
 257 
     | 
    
         
            +
                      :pass => machine_options[:password] # TODO: Replace with encryption
         
     | 
| 
      
 258 
     | 
    
         
            +
                  }
         
     | 
| 
      
 259 
     | 
    
         
            +
             
     | 
| 
      
 260 
     | 
    
         
            +
                  if(winrm_transport_options['https'])
         
     | 
| 
      
 261 
     | 
    
         
            +
                    tcp_endpoint = instance.tcp_endpoints.select { |tcp| tcp[:name] == 'PowerShell' }.first
         
     | 
| 
      
 262 
     | 
    
         
            +
                    remote_host = tcp_endpoint[:vip]
         
     | 
| 
      
 263 
     | 
    
         
            +
                    port = tcp_endpoint[:public_port] || default_winrm_https_port
         
     | 
| 
      
 264 
     | 
    
         
            +
                    endpoint = "https://#{remote_host}:#{port}/wsman"
         
     | 
| 
      
 265 
     | 
    
         
            +
                    type = :ssl
         
     | 
| 
      
 266 
     | 
    
         
            +
                    winrm_options = {
         
     | 
| 
      
 267 
     | 
    
         
            +
                      :disable_sspi => winrm_transport_options['https'][:disable_sspi] || false, # default to Negotiate
         
     | 
| 
      
 268 
     | 
    
         
            +
                      :basic_auth_only => winrm_transport_options['https'][:basic_auth_only] || false, # disallow Basic auth by default
         
     | 
| 
      
 269 
     | 
    
         
            +
                      :no_ssl_peer_verification => winrm_transport_options['https'][:no_ssl_peer_verification] || false #disallow MITM potential by default
         
     | 
| 
      
 270 
     | 
    
         
            +
                    }
         
     | 
| 
      
 271 
     | 
    
         
            +
                  end
         
     | 
| 
      
 272 
     | 
    
         
            +
             
     | 
| 
      
 273 
     | 
    
         
            +
                  if(winrm_transport_options['http'])
         
     | 
| 
      
 274 
     | 
    
         
            +
                    tcp_endpoint = instance.tcp_endpoints.select { |tcp| tcp[:name] == 'WinRm-Http' }.first
         
     | 
| 
      
 275 
     | 
    
         
            +
                    remote_host = tcp_endpoint[:vip]
         
     | 
| 
      
 276 
     | 
    
         
            +
                    port = tcp_endpoint[:public_port] || default_winrm_http_port
         
     | 
| 
      
 277 
     | 
    
         
            +
                    endpoint = "http://#{remote_host}:#{port}/wsman"
         
     | 
| 
      
 278 
     | 
    
         
            +
                    type = :plaintext
         
     | 
| 
      
 279 
     | 
    
         
            +
                    winrm_options = {
         
     | 
| 
      
 280 
     | 
    
         
            +
                      :disable_sspi => winrm_transport_options['http']['disable_sspi'] || false, # default to Negotiate
         
     | 
| 
      
 281 
     | 
    
         
            +
                      :basic_auth_only => winrm_transport_options['http']['basic_auth_only'] || false # disallow Basic auth by default
         
     | 
| 
      
 282 
     | 
    
         
            +
                    }
         
     | 
| 
      
 283 
     | 
    
         
            +
                  end
         
     | 
| 
      
 284 
     | 
    
         
            +
             
     | 
| 
      
 285 
     | 
    
         
            +
                  merged_winrm_options = winrm_options.merge(shared_winrm_options)
         
     | 
| 
      
 286 
     | 
    
         
            +
                  Chef::Provisioning::Transport::WinRM.new("#{endpoint}", type, merged_winrm_options, {})
         
     | 
| 
      
 287 
     | 
    
         
            +
                end
         
     | 
| 
      
 288 
     | 
    
         
            +
             
     | 
| 
      
 289 
     | 
    
         
            +
                def default_winrm_http_port
         
     | 
| 
      
 290 
     | 
    
         
            +
                  5985
         
     | 
| 
      
 291 
     | 
    
         
            +
                end
         
     | 
| 
      
 292 
     | 
    
         
            +
             
     | 
| 
      
 293 
     | 
    
         
            +
                def default_winrm_https_port
         
     | 
| 
      
 294 
     | 
    
         
            +
                  5986
         
     | 
| 
      
 295 
     | 
    
         
            +
                end
         
     | 
| 
      
 296 
     | 
    
         
            +
             
     | 
| 
      
 297 
     | 
    
         
            +
                def convergence_strategy_for(machine_spec, machine_options)
         
     | 
| 
      
 298 
     | 
    
         
            +
                  convergence_options = machine_options[:convergence_options]
         
     | 
| 
      
 299 
     | 
    
         
            +
                  # Defaults
         
     | 
| 
      
 300 
     | 
    
         
            +
                  unless machine_spec.location
         
     | 
| 
      
 301 
     | 
    
         
            +
                    return Chef::Provisioning::ConvergenceStrategy::NoConverge.new(convergence_options, config)
         
     | 
| 
      
 302 
     | 
    
         
            +
                  end
         
     | 
| 
      
 303 
     | 
    
         
            +
             
     | 
| 
      
 304 
     | 
    
         
            +
                  if machine_spec.location['is_windows']
         
     | 
| 
      
 305 
     | 
    
         
            +
                    Chef::Provisioning::ConvergenceStrategy::InstallMsi.new(convergence_options, config)
         
     | 
| 
      
 306 
     | 
    
         
            +
                  elsif machine_options[:cached_installer]
         
     | 
| 
      
 307 
     | 
    
         
            +
                    Chef::Provisioning::ConvergenceStrategy::InstallCached.new(convergence_options, config)
         
     | 
| 
      
 308 
     | 
    
         
            +
                  else
         
     | 
| 
      
 309 
     | 
    
         
            +
                    Chef::Provisioning::ConvergenceStrategy::InstallSh.new(convergence_options, config)
         
     | 
| 
      
 310 
     | 
    
         
            +
                  end
         
     | 
| 
      
 311 
     | 
    
         
            +
                end
         
     | 
| 
      
 312 
     | 
    
         
            +
             
     | 
| 
      
 313 
     | 
    
         
            +
                def wait_until_ready(action_handler, machine_spec)
         
     | 
| 
      
 314 
     | 
    
         
            +
                  vm = vm_for(machine_spec)
         
     | 
| 
      
 315 
     | 
    
         
            +
             
     | 
| 
      
 316 
     | 
    
         
            +
                  # If the machine is ready, nothing to do
         
     | 
| 
      
 317 
     | 
    
         
            +
                  return if vm.status == 'ReadyRole'
         
     | 
| 
      
 318 
     | 
    
         
            +
             
     | 
| 
      
 319 
     | 
    
         
            +
                  # Skip if we don't actually need to do anything
         
     | 
| 
      
 320 
     | 
    
         
            +
                  return unless action_handler.should_perform_actions
         
     | 
| 
      
 321 
     | 
    
         
            +
             
     | 
| 
      
 322 
     | 
    
         
            +
                  time_elapsed = 0
         
     | 
| 
      
 323 
     | 
    
         
            +
                  sleep_time = 10
         
     | 
| 
      
 324 
     | 
    
         
            +
                  max_wait_time = 120
         
     | 
| 
      
 325 
     | 
    
         
            +
             
     | 
| 
      
 326 
     | 
    
         
            +
                  action_handler.report_progress "waiting for #{machine_spec.name} to be ready ..."
         
     | 
| 
      
 327 
     | 
    
         
            +
                  while time_elapsed < 120 && vm.status != 'ReadyRole'
         
     | 
| 
      
 328 
     | 
    
         
            +
                    action_handler.report_progress "#{time_elapsed}/#{max_wait_time}s..."
         
     | 
| 
      
 329 
     | 
    
         
            +
                    sleep(sleep_time)
         
     | 
| 
      
 330 
     | 
    
         
            +
                    time_elapsed += sleep_time
         
     | 
| 
      
 331 
     | 
    
         
            +
                    # Azure caches results
         
     | 
| 
      
 332 
     | 
    
         
            +
                    vm = vm_for(machine_spec)
         
     | 
| 
      
 333 
     | 
    
         
            +
                  end
         
     | 
| 
      
 334 
     | 
    
         
            +
                  action_handler.report_progress "#{machine_spec.name} is now ready"
         
     | 
| 
      
 335 
     | 
    
         
            +
                end
         
     | 
| 
      
 336 
     | 
    
         
            +
             
     | 
| 
      
 337 
     | 
    
         
            +
                def wait_for_transport(action_handler, machine_spec, machine_options)
         
     | 
| 
      
 338 
     | 
    
         
            +
                  vm = vm_for(machine_spec)
         
     | 
| 
      
 339 
     | 
    
         
            +
                  transport = transport_for(machine_spec, machine_options, vm)
         
     | 
| 
      
 340 
     | 
    
         
            +
             
     | 
| 
      
 341 
     | 
    
         
            +
                  return if transport.available?
         
     | 
| 
      
 342 
     | 
    
         
            +
                  return unless action_handler.should_perform_actions
         
     | 
| 
      
 343 
     | 
    
         
            +
             
     | 
| 
      
 344 
     | 
    
         
            +
                  time_elapsed = 0
         
     | 
| 
      
 345 
     | 
    
         
            +
                  sleep_time = 10
         
     | 
| 
      
 346 
     | 
    
         
            +
                  max_wait_time = 120
         
     | 
| 
      
 347 
     | 
    
         
            +
             
     | 
| 
      
 348 
     | 
    
         
            +
                  action_handler.report_progress "Waiting for transport on #{machine_spec.name} ..."
         
     | 
| 
      
 349 
     | 
    
         
            +
                  while time_elapsed < 120 && !transport.available?
         
     | 
| 
      
 350 
     | 
    
         
            +
                    action_handler.report_progress "#{time_elapsed}/#{max_wait_time}s..."
         
     | 
| 
      
 351 
     | 
    
         
            +
                    sleep(sleep_time)
         
     | 
| 
      
 352 
     | 
    
         
            +
                    time_elapsed += sleep_time
         
     | 
| 
      
 353 
     | 
    
         
            +
                  end
         
     | 
| 
      
 354 
     | 
    
         
            +
                  action_handler.report_progress "Transport to #{machine_spec.name} is now up!"
         
     | 
| 
      
 355 
     | 
    
         
            +
                end
         
     | 
| 
      
 356 
     | 
    
         
            +
             
     | 
| 
      
 357 
     | 
    
         
            +
              end
         
     | 
| 
      
 358 
     | 
    
         
            +
            end
         
     | 
| 
      
 359 
     | 
    
         
            +
            end
         
     | 
| 
      
 360 
     | 
    
         
            +
            end
         
     |