knife-azure 1.6.0 → 1.7.0

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