chef-provisioning-azure 0.2.1 → 0.3

Sign up to get free protection for your applications and to get access to all the features.
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