chef-provisioning-azure 0.2.1 → 0.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 26141bfd34a60cbcca1f67c7ad5e812a680460a2
4
- data.tar.gz: 83399cb5d0df2d81245787aaf2815b61dcd9a5c0
3
+ metadata.gz: ea6f8fc59f775e2ca0dbca8498954dfdad8dab5a
4
+ data.tar.gz: 8d3d5954e16f150c15396d05b20c04e82b541224
5
5
  SHA512:
6
- metadata.gz: 563c5947d6cf351e69ef06591dba95d8d0b1ade40ad438a1347b9c228f8950096a57c526a8d08466a2bc245b8ec912e994e0694b9f2a46ec48bccf1303ea89ed
7
- data.tar.gz: 1ac137365d67e200a3bee5a4594e78d855adb5085997e0bff8fbbf2414f746cdd01f5218ba86dbeef5fde2e911b3df4707250bd9ea8cf431c40917636ed46eef
6
+ metadata.gz: 6e4facf681246035a6aa86fcf36307601610435efe26d4fea7c3dfb2927832cef1b58952d548b14cff719b2d10873ab681522b295e64ad1c023b3ba75fd49a09
7
+ data.tar.gz: 02636ab1ff97415cade4db45f5dbc8700e137b25de192e97bd0f53c57b76f6e9535454a666052c88f9a2f8381617e782899772e61c55ae2bc7a6e125555b40e4
data/README.md CHANGED
@@ -1,39 +1,108 @@
1
- [![Gitter](https://badges.gitter.im/Join Chat.svg)](https://gitter.im/opscode/chef-provisioning?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
1
+ [![Gitter](https://badges.gitter.im/Join Chat.svg)](https://gitter.im/chef/chef-provisioning?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
2
2
 
3
3
  # chef-provisioning-azure
4
4
 
5
- Implementation of an Azure driver that relies on the Azure SDK.
6
-
5
+ This is an implementation of an Microsoft Azure driver for [chef-provisioning](/chef/chef-provisioning) that relies on [azure-sdk-for-ruby](https://github.com/stuartpreston/stuartpreston-azure-sdk-for-ruby) and the Azure Service Management API.
7
6
 
8
7
  ## What does it do?
9
8
 
10
9
  It can provision and converge a host on Azure with a recipe like the following:
11
10
 
11
+ ### Linux
12
+
12
13
  ```ruby
13
14
  require 'chef/provisioning/azure_driver'
14
15
  with_driver 'azure'
15
16
 
16
17
  machine_options = {
17
18
  :bootstrap_options => {
18
- :cloud_service_name => 'chefprovisioning',
19
- :storage_account_name => 'chefprovisioning',
20
- #:vm_size => "A7"
21
- :location => 'West US'
19
+ :cloud_service_name => 'chefprovisioning', #required
20
+ :storage_account_name => 'chefprovisioning', #required
21
+ :vm_size => "Standard_D1", #required
22
+ :location => 'West US', #required
23
+ :tcp_endpoints => '80:80' #optional
22
24
  },
23
- #:image_id => 'foobar'
25
+ :image_id => 'b39f27a8b8c64d52b05eac6a62ebad85__Ubuntu-14_04_1-LTS-amd64-server-20140927-en-us-30GB', #required
24
26
  # Until SSH keys are supported (soon)
25
- :password => "chefm3t4l\\m/"
27
+ :password => "chefm3t4l\\m/" #required
26
28
  }
27
29
 
28
30
  machine 'toad' do
29
31
  machine_options machine_options
30
32
  end
31
33
  ```
32
-
33
- That's it. No images, nothing else. Do not expect much just yet. Currently you have to specify the
34
- password you want the initial user to have in your recipe. No, this will not be for very long.
35
34
 
36
- ### Setting your credentials
35
+ ### Windows
36
+
37
+ The following example creates a Windows Server 2012 R2 VM from the public OS image gallery, then the uses the public WinRM/s port to bootstrap the server.
38
+
39
+ ```ruby
40
+ require 'chef/provisioning/azure_driver'
41
+ with_driver 'azure'
42
+
43
+ machine_options = {
44
+ :bootstrap_options => {
45
+ :vm_user => 'localadmin', #required if Windows
46
+ :cloud_service_name => 'chefprovisioning', #required
47
+ :storage_account_name => 'chefprovisioning', #required
48
+ :vm_size => 'Standard_D1', #optional
49
+ :location => 'West US', #optional
50
+ :tcp_endpoints => '3389:3389', #optional
51
+ :winrm_transport => { #optional
52
+ 'https' => { #required (valid values: 'http', 'https')
53
+ :disable_sspi => false, #optional, (default: false)
54
+ :basic_auth_only => false, #optional, (default: false)
55
+ :no_ssl_peer_verification => true #optional, (default: false)
56
+ }
57
+ }
58
+ },
59
+ :password => 'P2ssw0rd', #required
60
+ :image_id => 'a699494373c04fc0bc8f2bb1389d6106__Windows-Server-2012-R2-201502.01-en.us-127GB.vhd' #required
61
+ }
62
+
63
+ machine 'toad' do
64
+ machine_options machine_options
65
+ end
66
+ ```
67
+
68
+ ## Supported Features
69
+ * Automatic creation and teardown of Cloud Services
70
+ * Public (OS) images and captured User (VM) images
71
+ * Up to date (March 2015) VM sizes including 'D', 'DS', 'G', A10/A11 sizes.
72
+ * Custom TCP/UDP endpoints per VM role
73
+ * Linux VMs, SSH external bootstrap via cloud service endpoint
74
+ * Windows VMs, WinRM bootstrap via cloud service endpoint
75
+
76
+ ## Currently untested/Known issues
77
+ * Load-balanced sets
78
+ * Availability sets/Fault domains
79
+ * Cloud Service autoscaling
80
+ * Endpoint monitoring
81
+ * Additional disk volumes
82
+ * Affinity groups
83
+ * Direct server return IP addresses
84
+ * Reserved/Static IP addresses
85
+ * Virtual network allocation
86
+ * Bootstrap via internal (private) addresses
87
+ * Non-IaaS Azure services (e.g CDN/TrafficManager, Service Bus, Azure SQL Database, Media Services, Redis Cache)
88
+
89
+ Currently you have to specify the password you want the initial user to have in your recipe. No, this will not be for very long.
90
+
91
+ ## Getting started
92
+
93
+ The gem is installed into Chef's default Ruby via RubyGems:
94
+
95
+ ```
96
+ chef gem install chef-provisioning-azure
97
+ ```
98
+
99
+ ### Setting your credentials (v0.3 and above)
100
+
101
+ * If you have previously connected to your Azure subscription using the [azure-cli](http://azure.microsoft.com/en-us/documentation/articles/virtual-machines-command-line-tools/) tools and imported your publishsettings, **you do not need to do anything else** the driver will read your profile information and certificates from ~/.azure/azureProfile.json
102
+ * Alternatively, we support any of the methods listed in [configuration](docs/configuration.md) to set the driver up with access to your subscription
103
+ * Note that the use of ~/.azure/config to configure the driver is **no longer supported**.
104
+
105
+ ### Setting your credentials (v0.2.1 and below)
37
106
 
38
107
  Put the right values in ~/.azure/config so that it looks like the following:
39
108
 
@@ -45,7 +114,7 @@ subscription_id = "YOUR_SUBSCRIPTION_ID"
45
114
 
46
115
  If you need to generate a certificate for Azure on OSX / Linux you can do it with the following:
47
116
 
48
- ```shell
117
+ ```shell
49
118
  openssl req \
50
119
  -x509 -nodes -days 365 \
51
120
  -newkey rsa:1024 -keyout azure.pem -out azure.pem
@@ -2,14 +2,16 @@ require 'chef/mixin/shell_out'
2
2
  require 'chef/provisioning/driver'
3
3
  require 'chef/provisioning/convergence_strategy/install_cached'
4
4
  require 'chef/provisioning/convergence_strategy/install_sh'
5
+ require 'chef/provisioning/convergence_strategy/install_msi'
5
6
  require 'chef/provisioning/convergence_strategy/no_converge'
6
7
  require 'chef/provisioning/transport/ssh'
8
+ require 'chef/provisioning/transport/winrm'
7
9
  require 'chef/provisioning/machine/windows_machine'
8
10
  require 'chef/provisioning/machine/unix_machine'
9
11
  require 'chef/provisioning/machine_spec'
10
12
 
11
13
  require 'chef/provisioning/azure_driver/version'
12
- require 'chef/provisioning/azure_driver/credentials'
14
+ require 'chef/provisioning/azure_driver/subscriptions'
13
15
 
14
16
  require 'yaml'
15
17
  require 'azure'
@@ -24,31 +26,46 @@ module AzureDriver
24
26
  # Construct an AzureDriver object from a URL - used to parse existing URL
25
27
  # data to hydrate a driver object.
26
28
  # URL scheme:
27
- # azure:account_id:region
29
+ # azure:subscription_id
28
30
  # @return [AzureDriver] A chef-provisioning Azure driver object for the given URL
29
31
  def self.from_url(driver_url, config)
30
32
  Driver.new(driver_url, config)
31
33
  end
32
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
+
33
51
  def initialize(driver_url, config)
34
52
  super
35
- # TODO: load a specific one if requested by driver config
36
- credentials = azure_credentials.default
37
- default_endpoint = 'https://management.core.windows.net'
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 ......
38
60
  Azure.configure do |azure|
39
61
  # Configure these 3 properties to use Storage
40
- azure.management_certificate = credentials[:management_certificate]
41
- azure.subscription_id = credentials[:subscription_id]
42
- azure.management_endpoint = credentials[:management_endpoint] || default_endpoint
62
+ azure.management_certificate = subscription[:management_certificate]
63
+ azure.subscription_id = subscription[:subscription_id]
64
+ azure.management_endpoint = subscription[:management_endpoint]
43
65
  end
44
66
  end
45
67
 
46
- # Take a URL and split it into the appropriate parts of [URL, config]
47
- # @return [String, Hash] A 2-element array of [URL, config]
48
- def self.canonicalize_url(driver_url, config)
49
- url = driver_url.split(':')[0]
50
- ["azure:#{url}", config]
51
- end
68
+ attr_reader :subscription
52
69
 
53
70
  # -- Machine methods --
54
71
 
@@ -83,39 +100,33 @@ module AzureDriver
83
100
  image_id = machine_options[:image_id] || default_image_for_location(location)
84
101
 
85
102
  Chef::Log.debug "Azure bootstrap options: #{bootstrap_options.inspect}"
86
-
87
- # If the cloud service exists already, need to add a role to it - otherwise create virtual machine (including cloud service)
88
- cloud_service = azure_cloud_service_service.get_cloud_service(bootstrap_options[:cloud_service_name])
89
103
 
90
- if cloud_service
91
- action_handler.report_progress "Cloud Service #{bootstrap_options[:cloud_service_name]} already exists, adding role."
92
- params = {
104
+ params = {
93
105
  vm_name: machine_spec.name,
94
- vm_user: default_ssh_username,
106
+ vm_user: bootstrap_options[:vm_user] || default_ssh_username,
95
107
  image: image_id,
96
108
  # This is only until SSH keys are added
97
109
  password: machine_options[:password],
110
+ location: location,
98
111
  cloud_service_name: bootstrap_options[:cloud_service_name]
99
- }
112
+ }
100
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."
101
120
  action_handler.report_progress "Creating #{machine_spec.name} with image #{image_id} in #{bootstrap_options[:cloud_service_name]}..."
102
121
  vm = azure_vm_service.add_role(params, bootstrap_options)
103
122
  else
104
- params = {
105
- vm_name: machine_spec.name,
106
- 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
- }
112
-
113
123
  action_handler.report_progress "Creating #{machine_spec.name} with image #{image_id} in #{location}..."
114
124
  vm = azure_vm_service.create_virtual_machine(params, bootstrap_options)
115
125
  end
116
126
 
117
127
  machine_spec.location['vm_name'] = vm.vm_name
118
- action_handler.report_progress "Created #{vm.vm_name} in #{location}..."
128
+ machine_spec.location['is_windows'] = (true if vm.os_type == 'Windows') || false
129
+ action_handler.report_progress "Created #{vm.vm_name} in #{location}..."
119
130
  end
120
131
 
121
132
  # (see Chef::Provisioning::Driver#ready_machine)
@@ -196,23 +207,11 @@ module AzureDriver
196
207
  end
197
208
 
198
209
  def transport_for(machine_spec, machine_options, vm)
199
- # TODO winrm
200
- create_ssh_transport(machine_spec, machine_options, vm)
201
- end
202
-
203
- def azure_credentials
204
- # Grab the list of possible credentials
205
- @azure_credentials ||= if driver_options[:azure_credentials]
206
- driver_options[:azure_credentials]
207
- else
208
- credentials = Credentials.new
209
- if driver_options[:azure_config_file]
210
- credentials.load_ini(driver_options.delete(:azure_config_file))
211
- else
212
- credentials.load_default
213
- end
214
- credentials
215
- end
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
216
215
  end
217
216
 
218
217
  def default_image_for_location(location)
@@ -234,7 +233,7 @@ module AzureDriver
234
233
  remote_host = tcp_endpoint[:vip]
235
234
 
236
235
  # TODO: not this... replace with SSH key ASAP, only for getting this thing going...
237
- ssh_options = {
236
+ ssh_options = {
238
237
  password: machine_options[:password],
239
238
  port: tcp_endpoint[:public_port] # use public port from Cloud Service endpoint
240
239
  }
@@ -250,6 +249,50 @@ module AzureDriver
250
249
  Chef::Provisioning::Transport::SSH.new(remote_host, username, ssh_options, options, config)
251
250
  end
252
251
 
252
+ def create_winrm_transport(machine_spec, machine_options, instance)
253
+ winrm_transport_options = machine_options[:bootstrap_options][:winrm_transport]
254
+ shared_winrm_options = {
255
+ :user => machine_options[:vm_user] || 'localadmin',
256
+ :pass => machine_options[:password] # TODO: Replace with encryption
257
+ }
258
+
259
+ if(winrm_transport_options['https'])
260
+ tcp_endpoint = instance.tcp_endpoints.select { |tcp| tcp[:name] == 'PowerShell' }.first
261
+ remote_host = tcp_endpoint[:vip]
262
+ port = tcp_endpoint[:public_port] || default_winrm_https_port
263
+ endpoint = "https://#{remote_host}:#{port}/wsman"
264
+ type = :ssl
265
+ winrm_options = {
266
+ :disable_sspi => winrm_transport_options['https'][:disable_sspi] || false, # default to Negotiate
267
+ :basic_auth_only => winrm_transport_options['https'][:basic_auth_only] || false, # disallow Basic auth by default
268
+ :no_ssl_peer_verification => winrm_transport_options['https'][:no_ssl_peer_verification] || false #disallow MITM potential by default
269
+ }
270
+ end
271
+
272
+ if(winrm_transport_options['http'])
273
+ tcp_endpoint = instance.tcp_endpoints.select { |tcp| tcp[:name] == 'WinRm-Http' }.first
274
+ remote_host = tcp_endpoint[:vip]
275
+ port = tcp_endpoint[:public_port] || default_winrm_http_port
276
+ endpoint = "http://#{remote_host}:#{port}/wsman"
277
+ type = :plaintext
278
+ winrm_options = {
279
+ :disable_sspi => winrm_transport_options['http']['disable_sspi'] || false, # default to Negotiate
280
+ :basic_auth_only => winrm_transport_options['http']['basic_auth_only'] || false # disallow Basic auth by default
281
+ }
282
+ end
283
+
284
+ merged_winrm_options = winrm_options.merge(shared_winrm_options)
285
+ Chef::Provisioning::Transport::WinRM.new("#{endpoint}", type, merged_winrm_options, {})
286
+ end
287
+
288
+ def default_winrm_http_port
289
+ 5985
290
+ end
291
+
292
+ def default_winrm_https_port
293
+ 5986
294
+ end
295
+
253
296
  def convergence_strategy_for(machine_spec, machine_options)
254
297
  convergence_options = machine_options[:convergence_options]
255
298
  # Defaults
@@ -0,0 +1,222 @@
1
+ require 'inifile'
2
+ require 'json'
3
+ require 'nokogiri'
4
+
5
+ class Chef
6
+ module Provisioning
7
+ module AzureDriver
8
+ module Subscriptions
9
+
10
+ #
11
+ # Get the subscription with the given subscription_id or subscription_name
12
+ #
13
+ # Returns `nil` if nothing found.
14
+ #
15
+ def self.get_subscription(config, subscription_id_or_name)
16
+ subscription_count = 0
17
+ subscription = all_subscriptions(config).select do |s|
18
+ subscription_count += 1
19
+ s[:subscription_id] == subscription_id_or_name ||
20
+ s[:subscription_name] == subscription_id_or_name
21
+ end.first
22
+ if !subscription
23
+ Chef::Log.info("Subscription #{subscription_id_or_name} not found out of #{subscription_count} subscriptions read.")
24
+ end
25
+ subscription
26
+ end
27
+
28
+ #
29
+ # Get the default subscription for the given config.
30
+ #
31
+ # The default subscription is either the first .azureProfile subscription with isDefault: true
32
+ #
33
+ def self.default_subscription(config)
34
+ first_subscription = nil
35
+ all_subscriptions(config).each do |subscription|
36
+ if subscription[:is_default]
37
+ Chef::Log.info("Picked default subscription: #{subscription[:subscription_name]} (#{subscription[:subscription_id]}) from #{subscription[:source]}")
38
+ return subscription
39
+ end
40
+
41
+ first_subscription ||= subscription;
42
+ end
43
+ if first_subscription
44
+ Chef::Log.info("Picked first subscription found as default: #{first_subscription[:subscription_name]} (#{first_subscription[:subscription_id]}) from #{first_subscription[:source]}")
45
+ else
46
+ Chef::Log.info("No subscriptions found.")
47
+ end
48
+ first_subscription
49
+ end
50
+
51
+ #
52
+ # We search for subscription in the order specified by Chef::Config.azure_subscriptions,
53
+ # which includes:
54
+ #
55
+ # Key | Description
56
+ # ------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------
57
+ # `subscription_id` | The GUID of the subscription.
58
+ # `subscription_name` | The name of the subscription.
59
+ # `management_certificate` | The path to the actual credentials, a proc, or an IO object (optional; if this is not set, the keychain will be searched).
60
+ # `management_endpoint` | The management endpoint URL (optional; if not set, the default Azure endpoint will be used).
61
+ # `is_default` | If `true`, this should be considered the default subscription.
62
+ # `publishsettings` | The path/glob to one or more `.publishsettings` formatted files, an IO object, or a hash with one or more { type: <path|io> } keys, where type=:pem, :pdx or :cert.
63
+ # `azure_profile` | The path/glob to one or more `azureProfile.json` formatted files, an IO object, or a Hash representing the parsed data.
64
+ # `allow_missing_file` | If `true`, provisioning will skip the publishsettings or azure_profile if it does not exist; otherwise it will raise an error on missing file. Defaults to `false`.
65
+ #
66
+ def self.all_subscriptions(config)
67
+ Enumerator.new do |y|
68
+ subscriptions = config[:azure_subscriptions]
69
+ subscriptions ||= default_subscriptions(config)
70
+ subscriptions = [ subscriptions ].flatten
71
+
72
+ subscriptions.each do |subscription_spec|
73
+ allow_missing_file = subscription_spec[:allow_missing_file]
74
+ if subscription_spec[:subscription_id]
75
+ subscription = {
76
+ management_endpoint: default_management_endpoint(config),
77
+ source: "Chef configuration"
78
+ }
79
+ y.yield subscription.merge(subscription_spec)
80
+ end
81
+ if subscription_spec[:publishsettings]
82
+ load_publishsettings_subscriptions(config, subscription_spec[:publishsettings], allow_missing_file) do |subscription|
83
+ y.yield subscription.merge(subscription_spec)
84
+ end
85
+ end
86
+ if subscription_spec[:azure_profile]
87
+ load_azure_profile_subscriptions(config, subscription_spec[:azure_profile], allow_missing_file) do |subscription|
88
+ y.yield subscription.merge(subscription_spec)
89
+ end
90
+ end
91
+ end
92
+ end
93
+ end
94
+
95
+ def self.load_publishsettings_subscriptions(config, data, allow_missing_file, filename=nil, &block)
96
+ case data
97
+ when String
98
+ found = false
99
+ Dir.glob(data) do |filename|
100
+ found = true
101
+ Chef::Log.info("Reading publishsettings file #{filename}")
102
+ File.open(filename) do |f|
103
+ load_publishsettings_subscriptions(config, f, allow_missing_file, filename, &block)
104
+ end
105
+ end
106
+ if !found
107
+ if allow_missing_file
108
+ Chef::Log.info("Skipping missing publishsettings file #{data}.")
109
+ else
110
+ raise "Missing publishsettings file #{data}!"
111
+ end
112
+ end
113
+
114
+ when IO
115
+ xml = Nokogiri::XML(data)
116
+
117
+ # Parse publishsettings content
118
+ xml.xpath("//PublishData/PublishProfile/Subscription").each do |subscription|
119
+ Chef::Log.debug("- Read subscription #{subscription['Name']} (#{subscription['Id']})")
120
+ result = {
121
+ subscription_id: subscription['Id'],
122
+ subscription_name: subscription['Name'],
123
+ management_endpoint: subscription['ServiceManagementUrl'] || default_management_endpoint(config),
124
+ source: "publishsettings #{filename ? "file #{filename}" : " IO object"}"
125
+ }
126
+ result[:publishsettings] = filename if filename
127
+ if subscription['ManagementCertificate']
128
+ result[:management_certificate] = {
129
+ type: :pdx,
130
+ data: subscription['ManagementCertificate']
131
+ }
132
+ end
133
+ yield result
134
+ end
135
+ else
136
+ raise "Unexpected value #{data.inspect} for publishsettings!"
137
+ end
138
+ end
139
+
140
+ def self.load_azure_profile_subscriptions(config, data, allow_missing_file, filename=nil, &block)
141
+ case data
142
+ when String
143
+ found = false
144
+ Dir.glob(data) do |filename|
145
+ found = true
146
+ Chef::Log.info("Reading azure profile file #{filename}")
147
+ File.open(filename) do |f|
148
+ load_azure_profile_subscriptions(config, f, allow_missing_file, filename, &block)
149
+ end
150
+ end
151
+ if !found
152
+ if allow_missing_file
153
+ Chef::Log.info("Skipping missing azure profile file #{data}.")
154
+ else
155
+ raise "Missing azure profile file #{data}!"
156
+ end
157
+ end
158
+
159
+ when IO
160
+ profile = JSON.parse(data.read, create_additions: false)
161
+ if profile["subscriptions"]
162
+ profile["subscriptions"].each do |subscription|
163
+ Chef::Log.debug("- Read#{subscription['isDefault'] ? " default" : ""} subscription #{subscription['name']} (#{subscription['id']})")
164
+
165
+ result = {
166
+ subscription_id: subscription['id'],
167
+ subscription_name: subscription['name'],
168
+ management_endpoint: subscription['managementEndpointUrl'] || default_management_endpoint(config),
169
+ source: "azure profile #{filename ? "file #{filename}" : " IO object"}"
170
+ }
171
+ subscription[:azure_profile] = filename
172
+ if subscription['isDefault']
173
+ result[:is_default] = true
174
+ end
175
+ if subscription['managementCertificate'] && subscription['managementCertificate']['key']
176
+ # Concatenate the key and cert to one .pem so the SDK will be OK with it
177
+ result[:management_certificate] = {
178
+ type: :pem,
179
+ data: "#{subscription['managementCertificate']['key']}#{subscription['managementCertificate']['cert']}",
180
+ }
181
+ end
182
+ yield result
183
+ end
184
+ else
185
+ Chef::Log.warn("Azure profile #{filename ? "file #{filename}" : data} has no subscriptions")
186
+ end
187
+
188
+ else
189
+ raise "Unexpected value #{data.inspect} for azure_profile!"
190
+ end
191
+ end
192
+
193
+ def self.default_subscriptions(config)
194
+ default_azure_profile = self.default_azure_profile(config)
195
+ azure_publish_settings_file = Chef::Config.knife[:azure_publish_settings_file] if Chef::Config.knife
196
+ Chef::Log.info("No Chef::Config[:driver_options][:subscriptions] found, reading environment variables AZURE_SUBSCRIPTION_ID, AZURE_MANAGEMENT_CERTIFICATE, and AZURE_MANAGEMENT_ENDPOINT,#{azure_publish_settings_file ? " then #{azure_publish_settings_file}," : ""} and then reading #{default_azure_profile}")
197
+ result = []
198
+ result << {
199
+ subscription_id: ENV["AZURE_SUBSCRIPTION_ID"],
200
+ management_certificate: ENV["AZURE_MANAGEMENT_CERTIFICATE"],
201
+ management_endpoint: ENV["AZURE_MANAGEMENT_ENDPOINT"],
202
+ source: "environment variables"
203
+ }
204
+ result << { publishsettings: azure_publish_settings_file } if azure_publish_settings_file
205
+ result << {
206
+ azure_profile: default_azure_profile,
207
+ allow_missing_file: true
208
+ }
209
+ result
210
+ end
211
+
212
+ def self.default_management_endpoint(config)
213
+ 'https://management.core.windows.net'
214
+ end
215
+
216
+ def self.default_azure_profile(config)
217
+ File.join(config[:home_dir] || File.expand_path("~"), ".azure", "azureProfile.json")
218
+ end
219
+ end
220
+ end
221
+ end
222
+ end
@@ -2,7 +2,7 @@
2
2
  class Chef
3
3
  module Provisioning
4
4
  module AzureDriver
5
- VERSION = '0.2.1'
5
+ VERSION = '0.3'
6
6
  end
7
7
  end
8
8
  end
metadata CHANGED
@@ -1,100 +1,101 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: chef-provisioning-azure
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.1
4
+ version: '0.3'
5
5
  platform: ruby
6
6
  authors:
7
7
  - John Ewart
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-02-26 00:00:00.000000000 Z
11
+ date: 2015-04-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: chef
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - ">="
17
+ - - '>='
18
18
  - !ruby/object:Gem::Version
19
19
  version: '0'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - ">="
24
+ - - '>='
25
25
  - !ruby/object:Gem::Version
26
26
  version: '0'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: chef-provisioning
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
- - - "~>"
31
+ - - ~>
32
32
  - !ruby/object:Gem::Version
33
33
  version: '0.9'
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
- - - "~>"
38
+ - - ~>
39
39
  - !ruby/object:Gem::Version
40
40
  version: '0.9'
41
41
  - !ruby/object:Gem::Dependency
42
- name: azure
42
+ name: stuartpreston-azure-sdk-for-ruby
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
- - - ">="
45
+ - - ~>
46
46
  - !ruby/object:Gem::Version
47
- version: '0'
47
+ version: 0.6.9
48
48
  type: :runtime
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
- - - ">="
52
+ - - ~>
53
53
  - !ruby/object:Gem::Version
54
- version: '0'
54
+ version: 0.6.9
55
55
  - !ruby/object:Gem::Dependency
56
56
  name: rspec
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
- - - ">="
59
+ - - '>='
60
60
  - !ruby/object:Gem::Version
61
61
  version: '0'
62
62
  type: :development
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
- - - ">="
66
+ - - '>='
67
67
  - !ruby/object:Gem::Version
68
68
  version: '0'
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: rake
71
71
  requirement: !ruby/object:Gem::Requirement
72
72
  requirements:
73
- - - ">="
73
+ - - '>='
74
74
  - !ruby/object:Gem::Version
75
75
  version: '0'
76
76
  type: :development
77
77
  prerelease: false
78
78
  version_requirements: !ruby/object:Gem::Requirement
79
79
  requirements:
80
- - - ">="
80
+ - - '>='
81
81
  - !ruby/object:Gem::Version
82
82
  version: '0'
83
83
  - !ruby/object:Gem::Dependency
84
84
  name: yard
85
85
  requirement: !ruby/object:Gem::Requirement
86
86
  requirements:
87
- - - ">="
87
+ - - '>='
88
88
  - !ruby/object:Gem::Version
89
89
  version: '0'
90
90
  type: :development
91
91
  prerelease: false
92
92
  version_requirements: !ruby/object:Gem::Requirement
93
93
  requirements:
94
- - - ">="
94
+ - - '>='
95
95
  - !ruby/object:Gem::Version
96
96
  version: '0'
97
- description: Provisioner for creating Azure things in Chef Provisioning.
97
+ description: This is a driver that works with chef-provisioning thatallows Chef Provisioning
98
+ to manage objects in Microsoft Azure.
98
99
  email: jewart@getchef.com
99
100
  executables: []
100
101
  extensions: []
@@ -102,20 +103,21 @@ extra_rdoc_files:
102
103
  - README.md
103
104
  - LICENSE
104
105
  files:
106
+ - Rakefile
105
107
  - LICENSE
106
108
  - README.md
107
- - Rakefile
108
- - lib/chef/provisioning/azure_driver.rb
109
109
  - lib/chef/provisioning/azure_driver/bootstrap_options.rb
110
110
  - lib/chef/provisioning/azure_driver/constants.rb
111
- - lib/chef/provisioning/azure_driver/credentials.rb
112
111
  - lib/chef/provisioning/azure_driver/driver.rb
113
112
  - lib/chef/provisioning/azure_driver/machine_options.rb
114
113
  - lib/chef/provisioning/azure_driver/resources.rb
114
+ - lib/chef/provisioning/azure_driver/subscriptions.rb
115
115
  - lib/chef/provisioning/azure_driver/version.rb
116
+ - lib/chef/provisioning/azure_driver.rb
116
117
  - lib/chef/provisioning/driver_init/azure.rb
117
- homepage: https://github.com/opscode/chef-provisioning-azure
118
- licenses: []
118
+ homepage: https://github.com/chef/chef-provisioning-azure
119
+ licenses:
120
+ - Apache-2.0
119
121
  metadata: {}
120
122
  post_install_message:
121
123
  rdoc_options: []
@@ -123,19 +125,18 @@ require_paths:
123
125
  - lib
124
126
  required_ruby_version: !ruby/object:Gem::Requirement
125
127
  requirements:
126
- - - ">="
128
+ - - '>='
127
129
  - !ruby/object:Gem::Version
128
130
  version: '0'
129
131
  required_rubygems_version: !ruby/object:Gem::Requirement
130
132
  requirements:
131
- - - ">="
133
+ - - '>='
132
134
  - !ruby/object:Gem::Version
133
135
  version: '0'
134
136
  requirements: []
135
137
  rubyforge_project:
136
- rubygems_version: 2.2.2
138
+ rubygems_version: 2.0.14
137
139
  signing_key:
138
140
  specification_version: 4
139
- summary: Provisioner for creating Azure things in Chef Provisioning.
141
+ summary: Provisioner for creating Microsoft Azure resources using Chef Provisioning.
140
142
  test_files: []
141
- has_rdoc:
@@ -1,73 +0,0 @@
1
- require 'inifile'
2
-
3
- class Chef
4
- module Provisioning
5
- module AzureDriver
6
- # Reads in a credentials file from a config file and presents them
7
- class Credentials
8
- CONFIG_PATH = "#{ENV['HOME']}/.azure/config"
9
-
10
- def initialize
11
- @credentials = {}
12
- load_default
13
- end
14
-
15
- include Enumerable
16
-
17
- def default
18
- if @credentials.size == 0
19
- fail "No credentials loaded! Do you have a #{CONFIG_PATH}?"
20
- end
21
- default_key = ENV['AZURE_DEFAULT_PROFILE'] || 'default'
22
- @credentials[default_key] || @credentials.first[1]
23
- end
24
-
25
- def keys
26
- @credentials.keys
27
- end
28
-
29
- def [](name)
30
- @credentials[name]
31
- end
32
-
33
- def each(&block)
34
- @credentials.each(&block)
35
- end
36
-
37
- def load_ini(credentials_ini_file)
38
- inifile = IniFile.load(File.expand_path(credentials_ini_file))
39
- if inifile
40
- inifile.each_section do |section|
41
- if section =~ /^\s*profile\s+(.+)$/ || section =~ /^\s*(default)\s*/
42
- profile_name = $1.strip
43
- profile = inifile[section].inject({}) do |result, pair|
44
- result[pair[0].to_sym] = pair[1]
45
- result
46
- end
47
- profile[:name] = profile_name
48
- @credentials[profile_name] = profile
49
- end
50
- end
51
- else
52
- # Get it to throw an error
53
- File.open(File.expand_path(credentials_ini_file)) do
54
- end
55
- end
56
- end
57
-
58
- def load_default
59
- config_file = ENV['AZURE_CONFIG_FILE'] || File.expand_path(CONFIG_PATH)
60
- load_ini(config_file) if File.file?(config_file)
61
- end
62
-
63
- def self.method_missing(name, *args, &block)
64
- singleton.send(name, *args, &block)
65
- end
66
-
67
- def self.singleton
68
- @azure_credentials ||= Credentials.new
69
- end
70
- end
71
- end
72
- end
73
- end