chef-provisioning-azure 0.4.0 → 0.5.0

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