knife-azure 3.0.0 → 4.0.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.
Files changed (39) hide show
  1. checksums.yaml +4 -4
  2. data/lib/azure/custom_errors.rb +1 -1
  3. data/lib/azure/resource_management/ARM_deployment_template.rb +5 -5
  4. data/lib/azure/resource_management/ARM_interface.rb +8 -12
  5. data/lib/azure/resource_management/windows_credentials.rb +7 -8
  6. data/lib/chef/knife/azurerm_server_create.rb +2 -2
  7. data/lib/chef/knife/azurerm_server_delete.rb +1 -1
  8. data/lib/chef/knife/bootstrap/bootstrapper.rb +10 -11
  9. data/lib/chef/knife/bootstrap_azurerm.rb +1 -1
  10. data/lib/chef/knife/helpers/azurerm_base.rb +17 -19
  11. data/lib/knife-azure/version.rb +1 -1
  12. metadata +30 -43
  13. data/lib/azure/service_management/ASM_interface.rb +0 -310
  14. data/lib/azure/service_management/ag.rb +0 -99
  15. data/lib/azure/service_management/certificate.rb +0 -235
  16. data/lib/azure/service_management/connection.rb +0 -102
  17. data/lib/azure/service_management/deploy.rb +0 -221
  18. data/lib/azure/service_management/disk.rb +0 -68
  19. data/lib/azure/service_management/host.rb +0 -184
  20. data/lib/azure/service_management/image.rb +0 -94
  21. data/lib/azure/service_management/loadbalancer.rb +0 -78
  22. data/lib/azure/service_management/rest.rb +0 -125
  23. data/lib/azure/service_management/role.rb +0 -717
  24. data/lib/azure/service_management/storageaccount.rb +0 -127
  25. data/lib/azure/service_management/utility.rb +0 -40
  26. data/lib/azure/service_management/vnet.rb +0 -134
  27. data/lib/chef/knife/azure_ag_create.rb +0 -73
  28. data/lib/chef/knife/azure_ag_list.rb +0 -35
  29. data/lib/chef/knife/azure_image_list.rb +0 -56
  30. data/lib/chef/knife/azure_internal-lb_create.rb +0 -74
  31. data/lib/chef/knife/azure_internal-lb_list.rb +0 -35
  32. data/lib/chef/knife/azure_server_create.rb +0 -531
  33. data/lib/chef/knife/azure_server_delete.rb +0 -136
  34. data/lib/chef/knife/azure_server_list.rb +0 -38
  35. data/lib/chef/knife/azure_server_show.rb +0 -41
  36. data/lib/chef/knife/azure_vnet_create.rb +0 -74
  37. data/lib/chef/knife/azure_vnet_list.rb +0 -35
  38. data/lib/chef/knife/bootstrap_azure.rb +0 -191
  39. data/lib/chef/knife/helpers/azure_base.rb +0 -394
@@ -1,394 +0,0 @@
1
- # Author:: Barry Davis (barryd@jetstreamsoftware.com)
2
- # Author:: Seth Chisamore (<schisamo@chef.io>)
3
- # Copyright:: Copyright (c) Chef Software Inc.
4
- # License:: Apache License, Version 2.0
5
- #
6
- # Licensed under the Apache License, Version 2.0 (the "License");
7
- # you may not use this file except in compliance with the License.
8
- # You may obtain a copy of the License at
9
- #
10
- # http://www.apache.org/licenses/LICENSE-2.0
11
- #
12
- # Unless required by applicable law or agreed to in writing, software
13
- # distributed under the License is distributed on an "AS IS" BASIS,
14
- # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
- # See the License for the specific language governing permissions and
16
- # limitations under the License.
17
- #
18
-
19
- require "chef/knife"
20
-
21
- class Chef
22
- class Knife
23
- module AzureBase
24
- # :nodoc:
25
- # Would prefer to do this in a rational way, but can't be done b/c of
26
- # Mixlib::CLI's design :(
27
- def self.included(includer)
28
- includer.class_eval do
29
- deps do
30
- require "readline"
31
- require "chef/json_compat"
32
- require_relative "../../../azure/service_management/ASM_interface"
33
- end
34
-
35
- option :azure_subscription_id,
36
- short: "-S ID",
37
- long: "--azure-subscription-id ID",
38
- description: "Your Azure subscription ID"
39
-
40
- option :azure_mgmt_cert,
41
- short: "-p FILENAME",
42
- long: "--azure-mgmt-cert FILENAME",
43
- description: "Your Azure PEM file name"
44
-
45
- option :azure_api_host_name,
46
- short: "-H HOSTNAME",
47
- long: "--azure-api-host-name HOSTNAME",
48
- description: "Your Azure host name"
49
-
50
- option :verify_ssl_cert,
51
- long: "--verify-ssl-cert",
52
- description: "Verify SSL Certificates for communication over HTTPS",
53
- boolean: true,
54
- default: false
55
-
56
- option :azure_publish_settings_file,
57
- long: "--azure-publish-settings-file FILENAME",
58
- description: "Your Azure Publish Settings File"
59
- end
60
- end
61
-
62
- def is_image_windows?
63
- images = service.list_images
64
- target_image = images.select { |i| i.name == config[:azure_source_image] }
65
- if target_image[0].nil?
66
- ui.error('Invalid image. Use the command "knife azure image list" to verify the image name')
67
- exit 1
68
- else
69
- target_image[0].os == "Windows"
70
- end
71
- end
72
-
73
- def service
74
- @service ||= begin
75
- service = Azure::ServiceManagement::ASMInterface.new(
76
- azure_subscription_id: config[:azure_subscription_id],
77
- azure_mgmt_cert: config[:azure_mgmt_cert],
78
- azure_api_host_name: config[:azure_api_host_name],
79
- verify_ssl_cert: config[:verify_ssl_cert]
80
- )
81
- end
82
- @service.ui = ui
83
- @service
84
- end
85
-
86
- def msg_pair(label, value, color = :cyan)
87
- if value && !value.to_s.empty?
88
- puts "#{ui.color(label, color)}: #{value}"
89
- end
90
- end
91
-
92
- def msg_server_summary(server)
93
- puts "\n"
94
- msg_pair("DNS Name", server.hostedservicename + ".cloudapp.net")
95
- msg_pair("VM Name", server.name)
96
- msg_pair("Size", server.size)
97
- msg_pair("Azure Source Image", config[:azure_source_image])
98
- msg_pair("Azure Service Location", config[:azure_service_location])
99
- msg_pair("Public Ip Address", server.publicipaddress)
100
- msg_pair("Private Ip Address", server.ipaddress)
101
- msg_pair("SSH Port", server.sshport) unless server.sshport.nil?
102
- msg_pair("WinRM Port", server.winrmport) unless server.winrmport.nil?
103
- msg_pair("TCP Ports", server.tcpports) unless server.tcpports.nil? || server.tcpports.empty?
104
- msg_pair("UDP Ports", server.udpports) unless server.udpports.nil? || server.udpports.empty?
105
- msg_pair("Environment", config[:environment] || "_default")
106
- msg_pair("Runlist", config[:run_list]) unless config[:run_list].empty?
107
- puts "\n"
108
- end
109
-
110
- def pretty_key(key)
111
- key.to_s.tr("_", " ").gsub(/\w+/) { |w| w =~ /(ssh)|(aws)/i ? w.upcase : w.capitalize }
112
- end
113
-
114
- # validate command pre-requisites (cli options)
115
- # (config[:connection_password].length <= 6 && config[:connection_password].length >= 72)
116
- def validate_params!
117
- if config[:connection_password] && !config[:connection_password].length.between?(6, 72)
118
- ui.error("The supplied connection password must be 6-72 characters long and meet password complexity requirements")
119
- exit 1
120
- end
121
-
122
- if config[:azure_connect_to_existing_dns] && config[:azure_vm_name].nil?
123
- ui.error("Specify the VM name using --azure-vm-name option, since you are connecting to existing dns")
124
- exit 1
125
- end
126
-
127
- unless !!config[:azure_service_location] ^ !!config[:azure_affinity_group]
128
- ui.error("Specify either --azure-service-location or --azure-affinity-group")
129
- exit 1
130
- end
131
-
132
- unless service.valid_image?(config[:azure_source_image])
133
- ui.error("Image '#{config[:azure_source_image]}' is invalid")
134
- exit 1
135
- end
136
-
137
- # Validate join domain requirements.
138
- if config[:azure_domain_name] || config[:azure_domain_user]
139
- if config[:azure_domain_user].nil? || config[:azure_domain_passwd].nil?
140
- ui.error("Must specify both --azure-domain-user and --azure-domain-passwd.")
141
- exit 1
142
- end
143
- end
144
-
145
- if config[:winrm_ssl] && config[:thumbprint].nil? && config[:winrm_no_verify_cert].nil?
146
- ui.error("The SSL transport was specified without the --thumbprint option. Specify a thumbprint, or alternatively set the --winrm-no-verify-cert option to skip verification.")
147
- exit 1
148
- end
149
-
150
- if config[:extended_logs] && config[:connection_protocol] != "cloud-api"
151
- ui.error("--extended-logs option only works with --bootstrap-protocol cloud-api")
152
- exit 1
153
- end
154
-
155
- if config[:connection_protocol] == "cloud-api" && config[:azure_vm_name].nil? && config[:azure_dns_name].nil?
156
- ui.error("Specifying the DNS name using --azure-dns-name or VM name using --azure-vm-name option is required with --bootstrap-protocol cloud-api")
157
- exit 1
158
- end
159
-
160
- if config[:daemon]
161
- unless is_image_windows?
162
- raise ArgumentError, "The daemon option is only supported for Windows nodes."
163
- end
164
-
165
- unless config[:connection_protocol] == "cloud-api"
166
- raise ArgumentError, "The --daemon option requires the use of --bootstrap-protocol cloud-api"
167
- end
168
-
169
- unless %w{none service task}.include?(config[:daemon].downcase)
170
- raise ArgumentError, "Invalid value for --daemon option. Valid values are 'none', 'service' and 'task'."
171
- end
172
- end
173
- end
174
-
175
- # validates keys
176
- def validate!(keys)
177
- errors = []
178
- keys.each do |k|
179
- if config[k].nil?
180
- errors << "You did not provide a valid '#{pretty_key(k)}' value. Please set knife[:#{k}] in your knife.rb or pass as an option."
181
- end
182
- end
183
- exit 1 if errors.each { |e| ui.error(e) }.any?
184
- end
185
-
186
- # validate ASM mandatory keys
187
- def validate_asm_keys!(*keys)
188
- mandatory_keys = %i{azure_subscription_id azure_mgmt_cert azure_api_host_name}
189
- keys.concat(mandatory_keys)
190
-
191
- unless config[:azure_mgmt_cert].nil?
192
- config[:azure_mgmt_cert] = File.read find_file(config[:azure_mgmt_cert])
193
- end
194
-
195
- if !config[:azure_publish_settings_file].nil?
196
- parse_publish_settings_file(config[:azure_publish_settings_file])
197
- elsif config[:azure_subscription_id].nil? && config[:azure_mgmt_cert].nil? && config[:azure_api_host_name].nil?
198
- azureprofile_file = get_azure_profile_file_path
199
- if File.exist?(File.expand_path(azureprofile_file))
200
- errors = parse_azure_profile(azureprofile_file, errors)
201
- end
202
- end
203
- validate!(keys)
204
- end
205
-
206
- def parse_publish_settings_file(filename)
207
- require "nokogiri"
208
- require "base64"
209
- require "openssl"
210
- require "uri"
211
- begin
212
- doc = Nokogiri::XML(File.open(find_file(filename)))
213
- profile = doc.at_css("PublishProfile")
214
- subscription = profile.at_css("Subscription")
215
- # check given PublishSettings XML file format.Currently PublishSettings file have two different XML format
216
- if profile.attribute("SchemaVersion").nil?
217
- management_cert = OpenSSL::PKCS12.new(Base64.decode64(profile.attribute("ManagementCertificate").value))
218
- config[:azure_api_host_name] = URI(profile.attribute("Url").value).host
219
- elsif profile.attribute("SchemaVersion").value == "2.0"
220
- management_cert = OpenSSL::PKCS12.new(Base64.decode64(subscription.attribute("ManagementCertificate").value))
221
- config[:azure_api_host_name] = URI(subscription.attribute("ServiceManagementUrl").value).host
222
- else
223
- ui.error("Publish settings file Schema not supported - " + filename)
224
- end
225
- config[:azure_mgmt_cert] = management_cert.certificate.to_pem + management_cert.key.to_pem
226
- config[:azure_subscription_id] = doc.at_css("Subscription").attribute("Id").value
227
- rescue
228
- ui.error("Incorrect publish settings file - " + filename)
229
- exit 1
230
- end
231
- end
232
-
233
- def get_azure_profile_file_path
234
- "~/.azure/azureProfile.json"
235
- end
236
-
237
- def parse_azure_profile(filename, errors)
238
- require "openssl"
239
- require "uri"
240
- errors = [] if errors.nil?
241
- azure_profile = File.read(File.expand_path(filename))
242
- azure_profile = JSON.parse(azure_profile)
243
- default_subscription = get_default_subscription(azure_profile)
244
- if default_subscription.key?("id") && default_subscription.key?("managementCertificate") && default_subscription.key?("managementEndpointUrl")
245
-
246
- config[:azure_subscription_id] = default_subscription["id"]
247
- mgmt_key = OpenSSL::PKey::RSA.new(default_subscription["managementCertificate"]["key"]).to_pem
248
- mgmt_cert = OpenSSL::X509::Certificate.new(default_subscription["managementCertificate"]["cert"]).to_pem
249
- config[:azure_mgmt_cert] = mgmt_key + mgmt_cert
250
- config[:azure_api_host_name] = URI(default_subscription["managementEndpointUrl"]).host
251
- else
252
- errors << "Check if values set for 'id', 'managementCertificate', 'managementEndpointUrl' in -> #{filename} for 'defaultSubscription'. \n OR "
253
- end
254
- errors
255
- end
256
-
257
- def get_default_subscription(azure_profile)
258
- first_subscription_as_default = nil
259
- azure_profile["subscriptions"].each do |subscription|
260
- if subscription["isDefault"]
261
- Chef::Log.info("Default subscription \'#{subscription["name"]}\'' selected.")
262
- return subscription
263
- end
264
-
265
- first_subscription_as_default ||= subscription
266
- end
267
-
268
- if first_subscription_as_default
269
- Chef::Log.info("First subscription \'#{subscription["name"]}\' selected as default.")
270
- else
271
- Chef::Log.info("No subscriptions found.")
272
- exit 1
273
- end
274
- first_subscription_as_default
275
- end
276
-
277
- def find_file(name)
278
- name = ::File.expand_path(name)
279
- config_dir = Chef::Knife.chef_config_dir
280
- if File.exist? name
281
- file = name
282
- elsif config_dir && File.exist?(File.join(config_dir, name))
283
- file = File.join(config_dir, name)
284
- elsif File.exist?(File.join(ENV["HOME"], ".chef", name))
285
- file = File.join(ENV["HOME"], ".chef", name)
286
- else
287
- ui.error("Unable to find file - " + name)
288
- exit 1
289
- end
290
- file
291
- end
292
-
293
- def fetch_deployment
294
- deployment_name = service.deployment_name(config[:azure_dns_name])
295
- deployment = service.deployment("hostedservices/#{config[:azure_dns_name]}/deployments/#{deployment_name}")
296
-
297
- deployment
298
- end
299
-
300
- def fetch_role
301
- deployment = fetch_deployment
302
-
303
- if deployment.at_css("Deployment Name") != nil
304
- role_list_xml = deployment.css("RoleInstanceList RoleInstance")
305
- role_list_xml.each do |role|
306
- if role.at_css("RoleName").text == (config[:azure_vm_name] || @name_args[0])
307
- return role
308
- end
309
- end
310
- end
311
- nil
312
- end
313
-
314
- def fetch_extension(role)
315
- ext_list_xml = role.css("ResourceExtensionStatusList ResourceExtensionStatus")
316
- return nil if ext_list_xml.nil?
317
-
318
- ext_list_xml.each do |ext|
319
- if ext.at_css("HandlerName").text == "Chef.Bootstrap.WindowsAzure.LinuxChefClient" || ext.at_css("HandlerName").text == "Chef.Bootstrap.WindowsAzure.ChefClient"
320
- return ext
321
- end
322
- end
323
- nil
324
- end
325
-
326
- def fetch_substatus(extension)
327
- return nil if extension.at_css("ExtensionSettingStatus SubStatusList SubStatus").nil?
328
-
329
- substatus_list_xml = extension.css("ExtensionSettingStatus SubStatusList SubStatus")
330
- substatus_list_xml.each do |substatus|
331
- if substatus.at_css("Name").text == "Chef Client run logs"
332
- return substatus
333
- end
334
- end
335
- nil
336
- end
337
-
338
- def fetch_chef_client_logs(fetch_process_start_time, fetch_process_wait_timeout)
339
- ## fetch server details ##
340
- role = fetch_role
341
- if !role.nil?
342
- ## fetch Chef Extension details deployed on the server ##
343
- ext = fetch_extension(role)
344
- if !ext.nil?
345
- ## fetch substatus field which contains the chef-client run logs ##
346
- substatus = fetch_substatus(ext)
347
- if !substatus.nil?
348
- ## chef-client run logs becomes available ##
349
- name = substatus.at_css("Name").text
350
- status = substatus.at_css("Status").text
351
- message = substatus.at_css("Message").text
352
-
353
- ## printing the logs ##
354
- puts "\n\n******** Please find the chef-client run details below ********\n\n"
355
- print "----> chef-client run status: "
356
- case status
357
- when "Success"
358
- ## chef-client run succeeded ##
359
- color = :green
360
- when "Error"
361
- ## chef-client run failed ##
362
- color = :red
363
- when "Transitioning"
364
- ## chef-client run did not complete within maximum timeout of 30 minutes ##
365
- ## fetch whatever logs available under the chef-client.log file ##
366
- color = :yellow
367
- end
368
- puts "#{ui.color(status, color, :bold)}"
369
- puts "----> chef-client run logs: "
370
- puts "\n#{message}\n" ## message field of substatus contains the chef-client run logs ##
371
- else
372
- ## unavailability of the substatus field indicates that chef-client run is not completed yet on the server ##
373
- fetch_process_wait_time = ((Time.now - fetch_process_start_time) / 60).round
374
- if fetch_process_wait_time <= fetch_process_wait_timeout ## wait for maximum 30 minutes until chef-client run logs becomes available ##
375
- print "#{ui.color(".", :bold)}"
376
- sleep 30
377
- fetch_chef_client_logs(fetch_process_start_time, fetch_process_wait_timeout)
378
- else
379
- ## wait time exceeded maximum threshold set for the wait timeout ##
380
- ui.error "\nchef-client run logs could not be fetched since fetch process exceeded wait timeout of #{fetch_process_wait_timeout} minutes.\n"
381
- end
382
- end
383
- else
384
- ## Chef Extension could not be found ##
385
- ui.error("Unable to find Chef extension under role #{config[:azure_vm_name] || @name_args[0]}.")
386
- end
387
- else
388
- ## server could not be found ##
389
- ui.error("chef-client run logs could not be fetched since role #{config[:azure_vm_name] || @name_args[0]} could not be found.")
390
- end
391
- end
392
- end
393
- end
394
- end