chef-provisioning-azure 0.4.0 → 0.5.0

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.
@@ -1,62 +1,62 @@
1
- require 'chef/provisioning/machine_options'
2
- require 'chef/provisioning/azure_driver/constants'
3
-
4
- # Chef Provisioning Azure driver
5
- class Chef
6
- module Provisioning
7
- module AzureDriver
8
- # Represents available machine provisioning options for Azure
9
- # These are used to tell Azure how to construct a new VM
10
- class MachineOptions < Chef::Provisioning::MachineOptions
11
- # @return [String] Storage account name.
12
- attr_accessor :storage_account_name
13
-
14
- # @return [String] WinRM transport mechanism ("http", or "https").
15
- # Defaults to "http".
16
- attr_accessor :winrm_transport
17
-
18
- # @return [String] Cloud service name.
19
- attr_accessor :cloud_service_name
20
-
21
- # @return [String] Deployment name.
22
- attr_accessor :deployment_name
23
-
24
- # @return [Array] Array of ports to enable.
25
- # Can be in +port+ or +src:dest+ format.
26
- attr_accessor :tcp_endpoints
27
-
28
- # @return [Pathname] Path to the private key.
29
- attr_accessor :private_key_file
30
-
31
- # @return [Pathname] Path to the certificate file.
32
- attr_accessor :certificate_file
33
-
34
- # @return [Integer] The SSH port to listen on.
35
- # Defaults to 22
36
- attr_accessor :ssh_port
37
-
38
- # @return [Chef::Provisioning::AzureDriver::Constants::MachineSize] The Azure machine size.
39
- attr_accessor :vm_size
40
-
41
- # @return [String] Name of the affinity group being used.
42
- attr_accessor :affinity_group_name
43
-
44
- # @return [String] Virtual network name.
45
- attr_accessor :virtual_network_name
46
-
47
- # @return [String] Subnet name.
48
- attr_accessor :subnet_name
49
-
50
- # @return [String] Availability set name.
51
- attr_accessor :availability_set_name
52
-
53
- def initialize
54
- # Set defaults
55
- self.winrm_transport = 'http'
56
- self.ssh_port = 22
57
- end
58
-
59
- end
60
- end
61
- end
62
- end
1
+ require 'chef/provisioning/machine_options'
2
+ require 'chef/provisioning/azure_driver/constants'
3
+
4
+ # Chef Provisioning Azure driver
5
+ class Chef
6
+ module Provisioning
7
+ module AzureDriver
8
+ # Represents available machine provisioning options for Azure
9
+ # These are used to tell Azure how to construct a new VM
10
+ class MachineOptions < Chef::Provisioning::MachineOptions
11
+ # @return [String] Storage account name.
12
+ attr_accessor :storage_account_name
13
+
14
+ # @return [String] WinRM transport mechanism ("http", or "https").
15
+ # Defaults to "http".
16
+ attr_accessor :winrm_transport
17
+
18
+ # @return [String] Cloud service name.
19
+ attr_accessor :cloud_service_name
20
+
21
+ # @return [String] Deployment name.
22
+ attr_accessor :deployment_name
23
+
24
+ # @return [Array] Array of ports to enable.
25
+ # Can be in +port+ or +src:dest+ format.
26
+ attr_accessor :tcp_endpoints
27
+
28
+ # @return [Pathname] Path to the private key.
29
+ attr_accessor :private_key_file
30
+
31
+ # @return [Pathname] Path to the certificate file.
32
+ attr_accessor :certificate_file
33
+
34
+ # @return [Integer] The SSH port to listen on.
35
+ # Defaults to 22
36
+ attr_accessor :ssh_port
37
+
38
+ # @return [Chef::Provisioning::AzureDriver::Constants::MachineSize] The Azure machine size.
39
+ attr_accessor :vm_size
40
+
41
+ # @return [String] Name of the affinity group being used.
42
+ attr_accessor :affinity_group_name
43
+
44
+ # @return [String] Virtual network name.
45
+ attr_accessor :virtual_network_name
46
+
47
+ # @return [String] Subnet name.
48
+ attr_accessor :subnet_name
49
+
50
+ # @return [String] Availability set name.
51
+ attr_accessor :availability_set_name
52
+
53
+ def initialize
54
+ # Set defaults
55
+ self.winrm_transport = 'http'
56
+ self.ssh_port = 22
57
+ end
58
+
59
+ end
60
+ end
61
+ end
62
+ end
@@ -1,7 +1,7 @@
1
- resources = %w(storage_account cloud_service sql_server)
2
-
3
- resources.each do |r|
4
- Chef::Log.debug("Loading resource: #{r}")
5
- require "chef/resource/azure_#{r}"
6
- require "chef/provider/azure_#{r}"
7
- end
1
+ resources = %w(storage_account cloud_service sql_server)
2
+
3
+ resources.each do |r|
4
+ Chef::Log.debug("Loading resource: #{r}")
5
+ require "chef/resource/azure_#{r}"
6
+ require "chef/provider/azure_#{r}"
7
+ end
@@ -1,222 +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.debug("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
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.debug("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