knife-azure 1.6.0 → 1.7.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 (35) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE +201 -201
  3. data/README.md +37 -654
  4. data/lib/azure/resource_management/ARM_deployment_template.rb +87 -50
  5. data/lib/azure/resource_management/ARM_interface.rb +236 -516
  6. data/lib/azure/resource_management/vnet_config.rb +254 -0
  7. data/lib/azure/resource_management/windows_credentials.rb +109 -61
  8. data/lib/azure/service_management/ASM_interface.rb +17 -1
  9. data/lib/azure/service_management/certificate.rb +37 -13
  10. data/lib/azure/service_management/connection.rb +0 -0
  11. data/lib/azure/service_management/deploy.rb +0 -0
  12. data/lib/azure/service_management/disk.rb +0 -0
  13. data/lib/azure/service_management/host.rb +0 -0
  14. data/lib/azure/service_management/image.rb +0 -0
  15. data/lib/azure/service_management/rest.rb +0 -0
  16. data/lib/azure/service_management/role.rb +0 -0
  17. data/lib/azure/service_management/utility.rb +0 -0
  18. data/lib/chef/knife/azure_base.rb +100 -0
  19. data/lib/chef/knife/azure_image_list.rb +0 -0
  20. data/lib/chef/knife/azure_server_create.rb +0 -98
  21. data/lib/chef/knife/azure_server_delete.rb +0 -0
  22. data/lib/chef/knife/azure_server_list.rb +0 -0
  23. data/lib/chef/knife/azure_server_show.rb +0 -0
  24. data/lib/chef/knife/azurerm_base.rb +42 -9
  25. data/lib/chef/knife/azurerm_server_create.rb +31 -24
  26. data/lib/chef/knife/azurerm_server_delete.rb +1 -10
  27. data/lib/chef/knife/azurerm_server_list.rb +5 -1
  28. data/lib/chef/knife/azurerm_server_show.rb +5 -1
  29. data/lib/chef/knife/bootstrap/bootstrap_options.rb +12 -18
  30. data/lib/chef/knife/bootstrap/bootstrapper.rb +34 -15
  31. data/lib/chef/knife/bootstrap/common_bootstrap_options.rb +21 -24
  32. data/lib/chef/knife/bootstrap_azure.rb +58 -0
  33. data/lib/chef/knife/bootstrap_azurerm.rb +40 -50
  34. data/lib/knife-azure/version.rb +1 -1
  35. metadata +27 -12
@@ -132,17 +132,24 @@ class Chef
132
132
 
133
133
  option :azure_vnet_name,
134
134
  :long => "--azure-vnet-name VNET_NAME",
135
- :description => "Optional. Specifies the virtual network name
136
- If this is an existing vnet then it must exists under the current resource group identified by resource-group
137
- If this is an existing vnet then vnet-subnet-name is required"
135
+ :description => "Optional. Specifies the virtual network name.
136
+ This may be the name of an existing vnet present under the given resource group
137
+ or this may be the name of a new vnet to be added in the given resource group.
138
+ If not specified then azure-vm-name will be taken as the default name for vnet name as well.
139
+ Along with this option azure-vnet-subnet-name option can also be specified or it can also be skipped."
138
140
 
139
141
  option :azure_vnet_subnet_name,
140
142
  :long => "--azure-vnet-subnet-name VNET_SUBNET_NAME",
141
- :description => "Optional. Specifies the virtual network subnet name."
143
+ :description => "Optional. Specifies the virtual network subnet name.
144
+ Must be specified only with azure-vnet-name option.
145
+ This may be the name of an existing subnet present under the given virtual network
146
+ or this may be the name of a new subnet to be added in the given virtual network.
147
+ If not specified then azure-vm-name will be taken as the default name for subnet name as well.
148
+ Value as 'GatewaySubnet' cannot be used as the name for the --azure-vnet-subnet-name option."
142
149
 
143
- option :identity_file,
144
- :long => "--identity-file FILENAME",
145
- :description => "SSH identity file for authentication, optional. It is the RSA private key path. Specify either ssh-password or identity-file"
150
+ option :ssh_public_key,
151
+ :long => "--ssh-public-key FILENAME",
152
+ :description => "It is the ssh-rsa public key path. Specify either ssh-password or ssh-public-key"
146
153
 
147
154
  option :thumbprint,
148
155
  :long => "--thumbprint THUMBPRINT",
@@ -156,6 +163,11 @@ class Chef
156
163
  :long => "--cert-path PATH",
157
164
  :description => "SSL Certificate Path"
158
165
 
166
+ option :tcp_endpoints,
167
+ :short => "-t PORT_LIST",
168
+ :long => "--tcp-endpoints PORT_LIST",
169
+ :description => "Comma-separated list of TCP ports to open e.g. '80,433'"
170
+
159
171
  option :server_count,
160
172
  :long => "--server-count COUNT",
161
173
  :description => "Number of servers to create with same configuration.
@@ -188,16 +200,7 @@ class Chef
188
200
 
189
201
  vm_details = service.create_server(create_server_def)
190
202
  rescue => error
191
- if error.class == MsRestAzure::AzureOperationError && error.body
192
- if error.response.body['error']['code'] == 'DeploymentFailed'
193
- ui.error("#{error.body['error']['message']}")
194
- else
195
- ui.error(error.response.body)
196
- end
197
- else
198
- ui.error("#{error.message}")
199
- end
200
- Chef::Log.debug("#{error.backtrace.join("\n")}")
203
+ service.common_arm_rescue_block(error)
201
204
  exit
202
205
  end
203
206
  end
@@ -227,6 +230,10 @@ class Chef
227
230
  :server_count => locate_config_value(:server_count)
228
231
  }
229
232
 
233
+ if locate_config_value(:tcp_endpoints)
234
+ server_def[:tcp_endpoints] = locate_config_value(:tcp_endpoints)
235
+ end
236
+
230
237
  server_def[:azure_storage_account] = locate_config_value(:azure_vm_name) if server_def[:azure_storage_account].nil?
231
238
  server_def[:azure_storage_account] = server_def[:azure_storage_account].gsub(/[!@#$%^&*()_-]/,'')
232
239
 
@@ -234,7 +241,7 @@ class Chef
234
241
  server_def[:azure_os_disk_name] = server_def[:azure_os_disk_name].gsub(/[!@#$%^&*()_-]/,'')
235
242
 
236
243
  server_def[:azure_vnet_name] = locate_config_value(:azure_vm_name) if server_def[:azure_vnet_name].nil?
237
- server_def[:azure_vnet_subnet_name] = locate_config_value(:azure_vm_name) if locate_config_value(:azure_vnet_subnet_name).nil? && locate_config_value(:azure_vnet_name).nil?
244
+ server_def[:azure_vnet_subnet_name] = locate_config_value(:azure_vm_name) if locate_config_value(:azure_vnet_subnet_name).nil?
238
245
 
239
246
  server_def[:chef_extension] = get_chef_extension_name
240
247
  server_def[:chef_extension_publisher] = get_chef_extension_publisher
@@ -249,9 +256,9 @@ class Chef
249
256
  server_def[:ssh_user] = locate_config_value(:ssh_user)
250
257
  server_def[:ssh_password] = locate_config_value(:ssh_password)
251
258
  server_def[:disablePasswordAuthentication] = "false"
252
- if locate_config_value(:identity_file)
259
+ if locate_config_value(:ssh_public_key)
253
260
  server_def[:disablePasswordAuthentication] = "true"
254
- server_def[:ssh_key] = File.read(locate_config_value(:identity_file))
261
+ server_def[:ssh_key] = File.read(locate_config_value(:ssh_public_key))
255
262
  end
256
263
  end
257
264
 
@@ -285,14 +292,14 @@ class Chef
285
292
  end
286
293
 
287
294
  def validate_params!
288
- if locate_config_value(:azure_vnet_name) && !locate_config_value(:azure_vnet_subnet_name)
289
- raise ArgumentError, "When a --azure-vnet-name is specified, the --azure-vnet-subnet-name must also be specified."
290
- end
291
-
292
295
  if locate_config_value(:azure_vnet_subnet_name) && !locate_config_value(:azure_vnet_name)
293
296
  raise ArgumentError, "When --azure-vnet-subnet-name is specified, the --azure-vnet-name must also be specified."
294
297
  end
295
298
 
299
+ if locate_config_value(:azure_vnet_subnet_name) == 'GatewaySubnet'
300
+ raise ArgumentError, 'GatewaySubnet cannot be used as the name for --azure-vnet-subnet-name option. GatewaySubnet can only be used for virtual network gateways.'
301
+ end
302
+
296
303
  if is_image_windows?
297
304
  if locate_config_value(:winrm_user).nil? || locate_config_value(:winrm_password).nil?
298
305
  raise ArgumentError, "Please provide --winrm-user and --winrm-password options for Windows option."
@@ -104,16 +104,7 @@ class Chef
104
104
  ui.warn("Corresponding node and client for the #{vm_name} server were not deleted and remain registered with the Chef Server")
105
105
  end
106
106
  rescue => error
107
- if error.class == MsRestAzure::AzureOperationError && error.body
108
- if error.body['error']['code'] == 'ResourceNotFound'
109
- ui.error("#{error.body['error']['message']}")
110
- else
111
- ui.error(error.body)
112
- end
113
- else
114
- ui.error("#{error.message}")
115
- Chef::Log.debug("#{error.backtrace.join("\n")}")
116
- end
107
+ service.common_arm_rescue_block(error)
117
108
  end
118
109
  end
119
110
  end
@@ -11,7 +11,11 @@ class Chef
11
11
  def run
12
12
  $stdout.sync = true
13
13
  validate_arm_keys!
14
- service.list_servers(locate_config_value(:azure_resource_group_name))
14
+ begin
15
+ service.list_servers(locate_config_value(:azure_resource_group_name))
16
+ rescue => error
17
+ service.common_arm_rescue_block(error)
18
+ end
15
19
  end
16
20
  end
17
21
  end
@@ -29,7 +29,11 @@ class Chef
29
29
  def run
30
30
  $stdout.sync = true
31
31
  validate_arm_keys!(:azure_resource_group_name)
32
- service.show_server(@name_args[0], locate_config_value(:azure_resource_group_name))
32
+ begin
33
+ service.show_server(@name_args[0], locate_config_value(:azure_resource_group_name))
34
+ rescue => error
35
+ service.common_arm_rescue_block(error)
36
+ end
33
37
  end
34
38
 
35
39
  end
@@ -16,6 +16,12 @@
16
16
  # See the License for the specific language governing permissions and
17
17
  # limitations under the License.
18
18
  #
19
+ #
20
+ # Bootstrap options listed here are in accordance with the options supported by
21
+ # Chef's bootstrap which bootstraps the target system through protocols like ssh
22
+ # or winrm. In addition it contains additional options which gives the users a
23
+ # choice to bootstrap the target system through cloud-api protocol.
24
+ #
19
25
 
20
26
  require 'chef/knife/winrm_base'
21
27
  require 'chef/knife/bootstrap_windows_base'
@@ -72,30 +78,18 @@ class Chef
72
78
  :description => "Execute the bootstrap via sudo with password",
73
79
  :boolean => false
74
80
 
75
- option :auto_update_client,
76
- :long => "--auto-update-client",
77
- :boolean => true,
78
- :default => false,
79
- :description => "Set this flag to enable auto chef client update in azure chef extension. This flag should be used with cloud-api bootstrap protocol only"
80
-
81
- option :delete_chef_extension_config,
82
- :long => "--delete-chef-extension-config",
83
- :boolean => true,
84
- :default => false,
85
- :description => "Determines whether Chef configuration files removed when Azure removes the Chef resource extension from the VM. This option is only valid for the 'cloud-api' bootstrap protocol. The default is false."
86
-
87
- option :uninstall_chef_client,
88
- :long => "--uninstall-chef-client",
89
- :boolean => true,
90
- :default => false,
91
- :description => "Determines whether Chef Client will be un-installed from the VM or not. This option is only valid for the 'cloud-api' bootstrap protocol. The default value is false."
92
-
93
81
  option :extended_logs,
94
82
  :long => "--extended-logs",
95
83
  :boolean => true,
96
84
  :default => false,
97
85
  :description => "Optional. Provide this option when --bootstrap-protocol is set to 'cloud-api'. It shows chef converge logs in detail."
98
86
 
87
+ option :chef_service_interval,
88
+ :long => "--chef-service-interval INTERVAL",
89
+ :description => "Optional. Provide this option when --bootstrap-protocol is set to 'cloud-api'.
90
+ It specifies the frequency (in minutes) at which the chef-service runs.
91
+ Pass 0 if you don't want the chef-service to be installed on the target machine."
92
+
99
93
  end
100
94
  end
101
95
  end
@@ -217,8 +217,8 @@ class Chef
217
217
  bootstrap.config[:host_key_verify] = config[:host_key_verify]
218
218
  Chef::Config[:knife][:secret] = config[:encrypted_data_bag_secret] if config[:encrypted_data_bag_secret]
219
219
  Chef::Config[:knife][:secret_file] = config[:encrypted_data_bag_secret_file] if config[:encrypted_data_bag_secret_file]
220
- bootstrap.config[:secret] = locate_config_value(:secret) || locate_config_value(:encrypted_data_bag_secret)
221
- bootstrap.config[:secret_file] = locate_config_value(:secret_file) || locate_config_value(:encrypted_data_bag_secret_file)
220
+ bootstrap.config[:secret] = locate_config_value(:encrypted_data_bag_secret)
221
+ bootstrap.config[:secret_file] = locate_config_value(:encrypted_data_bag_secret_file)
222
222
  bootstrap.config[:bootstrap_install_command] = locate_config_value(:bootstrap_install_command)
223
223
  bootstrap.config[:bootstrap_wget_options] = locate_config_value(:bootstrap_wget_options)
224
224
  bootstrap.config[:bootstrap_curl_options] = locate_config_value(:bootstrap_curl_options)
@@ -239,8 +239,16 @@ class Chef
239
239
  Chef::Config[:knife][:azure_chef_extension_version]
240
240
  else
241
241
  chef_extension_name = chef_extension_name.nil? ? get_chef_extension_name : chef_extension_name
242
- extensions = service.get_extension(chef_extension_name, get_chef_extension_publisher)
243
- extensions.css("Version").max.text.split(".").first + ".*"
242
+ if @service.instance_of? Azure::ResourceManagement::ARMInterface
243
+ service.get_latest_chef_extension_version({
244
+ :azure_service_location => locate_config_value(:azure_service_location),
245
+ :chef_extension_publisher => get_chef_extension_publisher,
246
+ :chef_extension => chef_extension_name
247
+ })
248
+ elsif @service.instance_of? Azure::ServiceManagement::ASMInterface
249
+ extensions = service.get_extension(chef_extension_name, get_chef_extension_publisher)
250
+ extensions.css("Version").max.text.split(".").first + ".*"
251
+ end
244
252
  end
245
253
  end
246
254
 
@@ -273,24 +281,15 @@ class Chef
273
281
  end
274
282
 
275
283
  pub_config[:runlist] = locate_config_value(:run_list).empty? ? "" : locate_config_value(:run_list).join(",").to_json
276
- pub_config[:autoUpdateClient] = locate_config_value(:auto_update_client) ? "true" : "false"
277
- pub_config[:deleteChefConfig] = locate_config_value(:delete_chef_extension_config) ? "true" : "false"
278
- pub_config[:uninstallChefClient] = locate_config_value(:uninstall_chef_client) ? "true" : "false"
279
284
  pub_config[:custom_json_attr] = locate_config_value(:json_attributes) || {}
280
285
  pub_config[:extendedLogs] = locate_config_value(:extended_logs) ? "true" : "false"
281
- pub_config[:hints] = ohai_hints if @service.instance_of? Azure::ResourceManagement::ARMInterface
286
+ pub_config[:hints] = ohai_hints if @service.instance_of?(Azure::ResourceManagement::ARMInterface) && !locate_config_value(:ohai_hints).nil?
287
+ pub_config[:chef_service_interval] = locate_config_value(:chef_service_interval) if locate_config_value(:chef_service_interval)
282
288
 
283
289
  # bootstrap attributes
284
290
  pub_config[:bootstrap_options] = {}
285
291
  pub_config[:bootstrap_options][:environment] = locate_config_value(:environment) if locate_config_value(:environment)
286
292
  pub_config[:bootstrap_options][:chef_node_name] = locate_config_value(:chef_node_name) if locate_config_value(:chef_node_name)
287
-
288
- if ( locate_config_value(:secret_file) || locate_config_value(:encrypted_data_bag_secret_file) ) && ( !locate_config_value(:secret) || !locate_config_value(:encrypted_data_bag_secret) )
289
- pub_config[:bootstrap_options][:encrypted_data_bag_secret] = Chef::EncryptedDataBagItem.load_secret(config[:secret_file])
290
- elsif locate_config_value(:encrypted_data_bag_secret) || locate_config_value(:secret)
291
- pub_config[:bootstrap_options][:encrypted_data_bag_secret] = locate_config_value(:encrypted_data_bag_secret) || locate_config_value(:secret)
292
- end
293
-
294
293
  pub_config[:bootstrap_options][:chef_server_url] = Chef::Config[:chef_server_url] if Chef::Config[:chef_server_url]
295
294
  pub_config[:bootstrap_options][:validation_client_name] = Chef::Config[:validation_client_name] if Chef::Config[:validation_client_name]
296
295
  pub_config[:bootstrap_options][:node_verify_api_cert] = locate_config_value(:node_verify_api_cert) ? "true" : "false" if config.key?(:node_verify_api_cert)
@@ -301,6 +300,23 @@ class Chef
301
300
  pub_config
302
301
  end
303
302
 
303
+ def load_correct_secret
304
+ knife_secret_file = Chef::Config[:knife][:encrypted_data_bag_secret_file]
305
+ knife_secret = Chef::Config[:knife][:encrypted_data_bag_secret]
306
+ cli_secret_file = config[:encrypted_data_bag_secret_file]
307
+ cli_secret = config[:encrypted_data_bag_secret]
308
+
309
+ #The value set in knife.rb gets set in config object too
310
+ #That's why setting cli objects to nil if the values are specified in knife.rb
311
+ cli_secret_file = nil if cli_secret_file == knife_secret_file
312
+ cli_secret = nil if cli_secret == knife_secret
313
+
314
+ cli_secret_file = Chef::EncryptedDataBagItem.load_secret(cli_secret_file) if cli_secret_file != nil
315
+ knife_secret_file = Chef::EncryptedDataBagItem.load_secret(knife_secret_file) if knife_secret_file != nil
316
+
317
+ cli_secret_file || cli_secret || knife_secret_file || knife_secret
318
+ end
319
+
304
320
  def get_chef_extension_private_params
305
321
  pri_config = Hash.new
306
322
 
@@ -335,6 +351,9 @@ class Chef
335
351
  end
336
352
  end
337
353
 
354
+ # encrypted_data_bag_secret key for encrypting/decrypting the data bags
355
+ pri_config[:encrypted_data_bag_secret] = load_correct_secret
356
+
338
357
  pri_config
339
358
  end
340
359
  end
@@ -16,6 +16,9 @@
16
16
  # See the License for the specific language governing permissions and
17
17
  # limitations under the License.
18
18
  #
19
+ #
20
+ # Bootstrap options listed here are supported only for cloud-api protocol.
21
+ #
19
22
 
20
23
  class Chef
21
24
  class Knife
@@ -81,36 +84,30 @@ class Chef
81
84
  :long => "--azure-extension-client-config CLIENT_PATH",
82
85
  :description => "Optional. Path to a client.rb file for use by the bootstrapped node."
83
86
 
84
- option :auto_update_client,
85
- :long => "--auto-update-client",
86
- :boolean => true,
87
- :default => false,
88
- :description => "Set this flag to enable auto chef client update in azure chef extension."
89
-
90
- option :delete_chef_extension_config,
91
- :long => "--delete-chef-extension-config",
92
- :boolean => true,
93
- :default => false,
94
- :description => "Determines whether Chef configuration files removed when Azure removes the Chef resource extension from the VM. The default is false."
95
-
96
- option :uninstall_chef_client,
97
- :long => "--uninstall-chef-client",
98
- :boolean => true,
99
- :default => false,
100
- :description => "Determines whether Chef Client will be un-installed from the VM or not. The default value is false."
101
-
102
- option :secret,
87
+ option :encrypted_data_bag_secret,
103
88
  :short => "-s SECRET",
104
89
  :long => "--secret ",
105
90
  :description => "The secret key to use to encrypt data bag item values. Can also be defaulted in your config with the key 'secret'"
106
91
 
107
- option :secret_file,
92
+ option :encrypted_data_bag_secret_file,
108
93
  :long => "--secret-file SECRET_FILE",
109
94
  :description => "A file containing the secret key to use to encrypt data bag item values. Can also be defaulted in your config with the key 'secret_file'"
110
- end
111
- end
112
- end
113
- end
95
+
96
+ option :extended_logs,
97
+ :long => "--extended-logs",
98
+ :boolean => true,
99
+ :default => false,
100
+ :description => "Optional. It shows chef convergence logs in detail."
101
+
102
+ option :chef_service_interval,
103
+ :long => "--chef-service-interval INTERVAL",
104
+ :description => "Optional. It specifies the frequency (in minutes) at which the chef-service runs.
105
+ Pass 0 if you don't want the chef-service to be installed on the target machine."
106
+
107
+ end
108
+ end
109
+ end
110
+ end
114
111
  end
115
112
  end
116
113
 
@@ -42,6 +42,12 @@ class Chef
42
42
  begin
43
43
  if @name_args.length == 1
44
44
  service.add_extension(@name_args[0], set_ext_params)
45
+ if locate_config_value(:extended_logs)
46
+ print "\n\nWaiting for the Chef Extension to become available/ready"
47
+ wait_until_extension_available(Time.now, 10)
48
+ print "\n\nWaiting for the first chef-client run"
49
+ fetch_chef_client_logs(Time.now, 35)
50
+ end
45
51
  else
46
52
  raise ArgumentError, 'Please specify the SERVER name which needs to be bootstrapped via the Chef Extension.' if @name_args.length == 0
47
53
  raise ArgumentError, 'Please specify only one SERVER name which needs to be bootstrapped via the Chef Extension.' if @name_args.length > 1
@@ -61,6 +67,8 @@ class Chef
61
67
  azure_dns_name: locate_config_value(:azure_dns_name)
62
68
  })
63
69
 
70
+ ## if azure_dns_name value not passed by user then set it using the hostedservicename attribute from the retrieved server's object ##
71
+ config[:azure_dns_name] = server.hostedservicename if locate_config_value(:azure_dns_name).nil? && (server.instance_of? Azure::Role)
64
72
  if !server.instance_of? Azure::Role
65
73
  if server.nil?
66
74
  if !locate_config_value(:azure_dns_name).nil?
@@ -105,6 +113,56 @@ class Chef
105
113
 
106
114
  ext_params
107
115
  end
116
+
117
+ def wait_until_extension_available(extension_deploy_start_time, extension_availaibility_wait_timeout)
118
+ extension_availaibility_wait_time = ((Time.now - extension_deploy_start_time) / 60).round
119
+ if extension_availaibility_wait_time <= extension_availaibility_wait_timeout
120
+ ## extension availaibility wait time has not exceeded the maximum threshold set for the wait timeout ##
121
+ my_role = nil
122
+ sleep_and_wait = false
123
+ deployment = fetch_deployment
124
+ if deployment.at_css('Deployment Name') != nil
125
+ role_list_xml = deployment.css('RoleInstanceList RoleInstance')
126
+ ## list of roles found under the deployment ##
127
+ role_list_xml.each do |role|
128
+ ## search in the roles list for the given role ##
129
+ if role.at_css("RoleName").text == @name_args[0]
130
+ my_role = role
131
+ break
132
+ end
133
+ end
134
+
135
+ if my_role && my_role.at_css("GuestAgentStatus Status").text == "Ready"
136
+ ## given role found and also GuestAgent is ready ##
137
+ extension = fetch_extension(my_role)
138
+ ## check if Chef Extension not found (which means it is not available/ready yet) then sleep_and_wait OR
139
+ ## if found (which means it is available/ready now) then proceed further with chef-client run logs fetch process ##
140
+ if extension.nil?
141
+ sleep_and_wait = true
142
+ end
143
+ else
144
+ ## given role not found or GuestAgent not ready yet ##
145
+ sleep_and_wait = true
146
+ end
147
+ else
148
+ ## deployment could not be found ##
149
+ sleep_and_wait = true
150
+ end
151
+
152
+ ## wait for some time and then re-fetch the status ##
153
+ if sleep_and_wait == true
154
+ print "#{ui.color('.', :bold)}"
155
+ sleep 30
156
+ wait_until_extension_available(
157
+ extension_deploy_start_time,
158
+ extension_availaibility_wait_timeout
159
+ )
160
+ end
161
+ else
162
+ ## extension availaibility wait time exceeded maximum threshold set for the wait timeout ##
163
+ raise "\nUnable to fetch chef-client run logs as Chef Extension seems to be unavailable even after #{extension_availaibility_wait_timeout} minutes of its deployment.\n"
164
+ end
165
+ end
108
166
  end
109
167
  end
110
168
  end
@@ -20,6 +20,8 @@
20
20
  require 'chef/knife/azurerm_base'
21
21
  require 'chef/knife/bootstrap/common_bootstrap_options'
22
22
  require 'chef/knife/bootstrap/bootstrapper'
23
+ require 'azure/resource_management/ARM_interface'
24
+ require 'time'
23
25
 
24
26
  class Chef
25
27
  class Knife
@@ -34,7 +36,7 @@ class Chef
34
36
  :short => "-m LOCATION",
35
37
  :long => "--azure-service-location LOCATION",
36
38
  :description => "Required if not using an Affinity Group. Specifies the geographic location - the name of the data center location that is valid for your subscription.
37
- Eg: West US, East US, East Asia, Southeast Asia, North Europe, West Europe",
39
+ Eg: westus, eastus, eastasia, southeastasia, northeurope, westeurope",
38
40
  :proc => Proc.new { |lo| Chef::Config[:knife][:azure_service_location] = lo }
39
41
 
40
42
  def run
@@ -42,70 +44,58 @@ class Chef
42
44
  validate_arm_keys!(:azure_resource_group_name, :azure_service_location)
43
45
 
44
46
  begin
45
- if @name_args.length == 1
47
+ if @name_args.length == 1
46
48
  ui.log("Creating VirtualMachineExtension....")
47
- vm_extension = service.create_vm_extension(set_ext_params)
49
+ ext_params = set_ext_params
50
+ vm_extension = service.create_vm_extension(ext_params)
48
51
  if vm_extension
52
+ if ext_params[:chef_extension_public_param][:extendedLogs] == 'true'
53
+ service.fetch_chef_client_logs(ext_params[:azure_resource_group_name], ext_params[:azure_vm_name], ext_params[:chef_extension], Time.now)
54
+ end
49
55
  ui.log("VirtualMachineExtension creation successfull.")
50
56
  ui.log("Virtual Machine Extension name is: #{vm_extension.name}")
51
57
  ui.log("Virtual Machine Extension ID is: #{vm_extension.id}")
52
58
  end
53
- else
54
- raise ArgumentError, 'Please specify the SERVER name which needs to be bootstrapped via the Chef Extension.' if @name_args.length == 0
55
- raise ArgumentError, 'Please specify only one SERVER name which needs to be bootstrapped via the Chef Extension.' if @name_args.length > 1
56
- end
57
- rescue => error
58
- if error.class == MsRestAzure::AzureOperationError && error.body
59
- if error.body['error']['code'] == 'DeploymentFailed'
60
- ui.error("#{error.body['error']['message']}")
61
- else
62
- ui.error(error.body)
63
- end
64
59
  else
65
- ui.error("#{error.message}")
66
- Chef::Log.debug("#{error.backtrace.join("\n")}")
60
+ raise ArgumentError, 'Please specify the SERVER name which needs to be bootstrapped via the Chef Extension.' if @name_args.length == 0
61
+ raise ArgumentError, 'Please specify only one SERVER name which needs to be bootstrapped via the Chef Extension.' if @name_args.length > 1
67
62
  end
68
- exit
69
- end
63
+ rescue => error
64
+ service.common_arm_rescue_block(error)
65
+ end
70
66
  end
71
67
 
72
68
  def set_ext_params
73
- begin
74
- server = service.find_server(locate_config_value(:azure_resource_group_name), name_args[0])
69
+ server = service.find_server(locate_config_value(:azure_resource_group_name), name_args[0])
75
70
 
76
- if server
77
- if service.extension_already_installed?(server)
78
- raise "Virtual machine #{server.name} already has Chef extension installed on it."
79
- else
80
- ext_params = Hash.new
81
- case server.properties.storage_profile.os_disk.os_type.downcase
82
- when 'windows'
83
- ext_params[:chef_extension] = 'ChefClient'
84
- when 'linux'
85
- if ['ubuntu', 'debian', 'rhel', 'centos'].any? { |platform| server.properties.storage_profile.image_reference.offer.downcase.include? platform }
86
- ext_params[:chef_extension] = 'LinuxChefClient'
87
- else
88
- raise "Offer #{server.properties.storage_profile.image_reference.offer} is not supported in the extension."
89
- end
71
+ if server
72
+ if service.extension_already_installed?(server)
73
+ raise "Virtual machine #{server.name} already has Chef extension installed on it."
74
+ else
75
+ ext_params = Hash.new
76
+ case server.properties.storage_profile.os_disk.os_type.downcase
77
+ when 'windows'
78
+ ext_params[:chef_extension] = 'ChefClient'
79
+ when 'linux'
80
+ if ['ubuntu', 'debian', 'rhel', 'centos'].any? { |platform| server.properties.storage_profile.image_reference.offer.downcase.include? platform }
81
+ ext_params[:chef_extension] = 'LinuxChefClient'
90
82
  else
91
- raise "OS type #{server.properties.storage_profile.os_disk.os_type} is not supported."
83
+ raise "Offer #{server.properties.storage_profile.image_reference.offer} is not supported in the extension."
92
84
  end
93
-
94
- ext_params[:azure_resource_group_name] = locate_config_value(:azure_resource_group_name)
95
- ext_params[:azure_vm_name] = @name_args[0]
96
- ext_params[:azure_service_location] = locate_config_value(:azure_service_location)
97
- ext_params[:chef_extension_publisher] = get_chef_extension_publisher
98
- ext_params[:chef_extension_version] = get_chef_extension_version(ext_params[:chef_extension])
99
- ext_params[:chef_extension_public_param] = get_chef_extension_public_params
100
- ext_params[:chef_extension_private_param] = get_chef_extension_private_params
85
+ else
86
+ raise "OS type #{server.properties.storage_profile.os_disk.os_type} is not supported."
101
87
  end
102
- else
103
- raise "The given server '#{@name_args[0]}' does not exist under resource group '#{locate_config_value(:azure_resource_group_name)}'"
88
+
89
+ ext_params[:azure_resource_group_name] = locate_config_value(:azure_resource_group_name)
90
+ ext_params[:azure_vm_name] = @name_args[0]
91
+ ext_params[:azure_service_location] = locate_config_value(:azure_service_location)
92
+ ext_params[:chef_extension_publisher] = get_chef_extension_publisher
93
+ ext_params[:chef_extension_version] = get_chef_extension_version(ext_params[:chef_extension])
94
+ ext_params[:chef_extension_public_param] = get_chef_extension_public_params
95
+ ext_params[:chef_extension_private_param] = get_chef_extension_private_params
104
96
  end
105
- rescue => error
106
- ui.error("#{error.message}")
107
- Chef::Log.debug("#{error.backtrace.join("\n")}")
108
- exit
97
+ else
98
+ raise "The given server '#{@name_args[0]}' does not exist under resource group '#{locate_config_value(:azure_resource_group_name)}'"
109
99
  end
110
100
 
111
101
  ext_params
@@ -113,4 +103,4 @@ class Chef
113
103
 
114
104
  end
115
105
  end
116
- end
106
+ end