knife-azure 3.0.6 → 4.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (37) 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 +4 -6
  5. data/lib/azure/resource_management/windows_credentials.rb +2 -2
  6. data/lib/chef/knife/azurerm_server_create.rb +1 -1
  7. data/lib/chef/knife/bootstrap/bootstrapper.rb +5 -10
  8. data/lib/chef/knife/helpers/azurerm_base.rb +4 -4
  9. data/lib/knife-azure/version.rb +1 -1
  10. metadata +30 -43
  11. data/lib/azure/service_management/ASM_interface.rb +0 -310
  12. data/lib/azure/service_management/ag.rb +0 -99
  13. data/lib/azure/service_management/certificate.rb +0 -235
  14. data/lib/azure/service_management/connection.rb +0 -102
  15. data/lib/azure/service_management/deploy.rb +0 -221
  16. data/lib/azure/service_management/disk.rb +0 -68
  17. data/lib/azure/service_management/host.rb +0 -184
  18. data/lib/azure/service_management/image.rb +0 -94
  19. data/lib/azure/service_management/loadbalancer.rb +0 -78
  20. data/lib/azure/service_management/rest.rb +0 -126
  21. data/lib/azure/service_management/role.rb +0 -717
  22. data/lib/azure/service_management/storageaccount.rb +0 -127
  23. data/lib/azure/service_management/utility.rb +0 -40
  24. data/lib/azure/service_management/vnet.rb +0 -134
  25. data/lib/chef/knife/azure_ag_create.rb +0 -73
  26. data/lib/chef/knife/azure_ag_list.rb +0 -35
  27. data/lib/chef/knife/azure_image_list.rb +0 -56
  28. data/lib/chef/knife/azure_internal-lb_create.rb +0 -74
  29. data/lib/chef/knife/azure_internal-lb_list.rb +0 -35
  30. data/lib/chef/knife/azure_server_create.rb +0 -531
  31. data/lib/chef/knife/azure_server_delete.rb +0 -136
  32. data/lib/chef/knife/azure_server_list.rb +0 -38
  33. data/lib/chef/knife/azure_server_show.rb +0 -41
  34. data/lib/chef/knife/azure_vnet_create.rb +0 -74
  35. data/lib/chef/knife/azure_vnet_list.rb +0 -35
  36. data/lib/chef/knife/bootstrap_azure.rb +0 -191
  37. data/lib/chef/knife/helpers/azure_base.rb +0 -392
@@ -1,392 +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" unless defined?(Nokogiri)
208
- require "base64" unless defined?(Base64)
209
- require "openssl" unless defined?(OpenSSL)
210
- require "uri" unless defined?(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" unless defined?(OpenSSL)
239
- require "uri" unless defined?(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
- service.deployment("hostedservices/#{config[:azure_dns_name]}/deployments/#{deployment_name}")
296
- end
297
-
298
- def fetch_role
299
- deployment = fetch_deployment
300
-
301
- if deployment.at_css("Deployment Name") != nil
302
- role_list_xml = deployment.css("RoleInstanceList RoleInstance")
303
- role_list_xml.each do |role|
304
- if role.at_css("RoleName").text == (config[:azure_vm_name] || @name_args[0])
305
- return role
306
- end
307
- end
308
- end
309
- nil
310
- end
311
-
312
- def fetch_extension(role)
313
- ext_list_xml = role.css("ResourceExtensionStatusList ResourceExtensionStatus")
314
- return nil if ext_list_xml.nil?
315
-
316
- ext_list_xml.each do |ext|
317
- if ext.at_css("HandlerName").text == "Chef.Bootstrap.WindowsAzure.LinuxChefClient" || ext.at_css("HandlerName").text == "Chef.Bootstrap.WindowsAzure.ChefClient"
318
- return ext
319
- end
320
- end
321
- nil
322
- end
323
-
324
- def fetch_substatus(extension)
325
- return nil if extension.at_css("ExtensionSettingStatus SubStatusList SubStatus").nil?
326
-
327
- substatus_list_xml = extension.css("ExtensionSettingStatus SubStatusList SubStatus")
328
- substatus_list_xml.each do |substatus|
329
- if substatus.at_css("Name").text == "Chef Client run logs"
330
- return substatus
331
- end
332
- end
333
- nil
334
- end
335
-
336
- def fetch_chef_client_logs(fetch_process_start_time, fetch_process_wait_timeout)
337
- ## fetch server details ##
338
- role = fetch_role
339
- if !role.nil?
340
- ## fetch Chef Extension details deployed on the server ##
341
- ext = fetch_extension(role)
342
- if !ext.nil?
343
- ## fetch substatus field which contains the chef-client run logs ##
344
- substatus = fetch_substatus(ext)
345
- if !substatus.nil?
346
- ## chef-client run logs becomes available ##
347
- name = substatus.at_css("Name").text
348
- status = substatus.at_css("Status").text
349
- message = substatus.at_css("Message").text
350
-
351
- ## printing the logs ##
352
- puts "\n\n******** Please find the chef-client run details below ********\n\n"
353
- print "----> chef-client run status: "
354
- case status
355
- when "Success"
356
- ## chef-client run succeeded ##
357
- color = :green
358
- when "Error"
359
- ## chef-client run failed ##
360
- color = :red
361
- when "Transitioning"
362
- ## chef-client run did not complete within maximum timeout of 30 minutes ##
363
- ## fetch whatever logs available under the chef-client.log file ##
364
- color = :yellow
365
- end
366
- puts "#{ui.color(status, color, :bold)}"
367
- puts "----> chef-client run logs: "
368
- puts "\n#{message}\n" ## message field of substatus contains the chef-client run logs ##
369
- else
370
- ## unavailability of the substatus field indicates that chef-client run is not completed yet on the server ##
371
- fetch_process_wait_time = ((Time.now - fetch_process_start_time) / 60).round
372
- if fetch_process_wait_time <= fetch_process_wait_timeout ## wait for maximum 30 minutes until chef-client run logs becomes available ##
373
- print "#{ui.color(".", :bold)}"
374
- sleep 30
375
- fetch_chef_client_logs(fetch_process_start_time, fetch_process_wait_timeout)
376
- else
377
- ## wait time exceeded maximum threshold set for the wait timeout ##
378
- ui.error "\nchef-client run logs could not be fetched since fetch process exceeded wait timeout of #{fetch_process_wait_timeout} minutes.\n"
379
- end
380
- end
381
- else
382
- ## Chef Extension could not be found ##
383
- ui.error("Unable to find Chef extension under role #{config[:azure_vm_name] || @name_args[0]}.")
384
- end
385
- else
386
- ## server could not be found ##
387
- ui.error("chef-client run logs could not be fetched since role #{config[:azure_vm_name] || @name_args[0]} could not be found.")
388
- end
389
- end
390
- end
391
- end
392
- end