knife-azure 1.1.2 → 1.1.4.rc.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +3 -1
- data/lib/azure/connection.rb +2 -2
- data/lib/azure/deploy.rb +10 -2
- data/lib/azure/host.rb +6 -1
- data/lib/azure/image.rb +2 -1
- data/lib/azure/rest.rb +3 -2
- data/lib/azure/role.rb +31 -12
- data/lib/azure/storageaccount.rb +12 -1
- data/lib/chef/knife/azure_base.rb +9 -0
- data/lib/chef/knife/azure_image_list.rb +2 -2
- data/lib/chef/knife/azure_server_create.rb +150 -27
- data/lib/chef/knife/azure_server_delete.rb +7 -0
- data/lib/knife-azure/version.rb +1 -1
- metadata +22 -6
data/README.md
CHANGED
@@ -119,6 +119,7 @@ https://github.com/opscode/knife-windows#nodes
|
|
119
119
|
:azure_connect_to_existing_dns Set this flag to add the new VM to an existing
|
120
120
|
deployment/service. Must give the name of the existing
|
121
121
|
DNS correctly in the --azure-dns-name option
|
122
|
+
:azure_availability_set Optional. Name of availability set to add virtual machine into.
|
122
123
|
|
123
124
|
#### Azure VM Quick Create
|
124
125
|
You can create a server with minimal configuration. On the Azure Management Portal, this corresponds to a "Quick Create - VM". Sample command for quick create (for an Ubuntu instance):
|
@@ -219,11 +220,12 @@ Sample knife.rb for bootstrapping Windows Node with basic authentication
|
|
219
220
|
### Azure Server Delete Subcommand
|
220
221
|
Deletes an existing server in the currently configured Azure account. By
|
221
222
|
default, this does not delete the associated node and client objects from the
|
222
|
-
Chef server. To do so, add the --purge flag. Also by default, the DNS name, also called "cloud service", is deleted if you are deleting the last VM from that service. By default, the OS disk is also deleted. If you want to retain them add the --preserve flag as shown below. To delete the storage account, add the --delete-azure-storage-account flag since by default the storage account is not deleted.
|
223
|
+
Chef server. To do so, add the --purge flag. Also by default, the DNS name, also called "cloud service", is deleted if you are deleting the last VM from that service. By default, the OS disk is also deleted. The underlying VHD blob is also deleted by default. If you want to retain them add the --preserve flag as shown below. To delete the storage account, add the --delete-azure-storage-account flag since by default the storage account is not deleted.
|
223
224
|
|
224
225
|
knife azure server delete "myvm01"
|
225
226
|
knife azure server delete "myvm01" --purge #purge chef node
|
226
227
|
knife azure server delete "myvm01" --preserve-azure-os-disk
|
228
|
+
knife azure server delete "myvm01" --preserve-azure-vhd
|
227
229
|
knife azure server delete "myvm01" --preserve-azure-dns-name
|
228
230
|
knife azure server delete "myvm01" --delete-azure-storage-account
|
229
231
|
|
data/lib/azure/connection.rb
CHANGED
@@ -39,10 +39,10 @@ class Azure
|
|
39
39
|
@disks = Disks.new(self)
|
40
40
|
@certificates = Certificates.new(self)
|
41
41
|
end
|
42
|
-
def query_azure(service_name, verb = 'get', body = '')
|
42
|
+
def query_azure(service_name, verb = 'get', body = '', params = '')
|
43
43
|
Chef::Log.info 'calling ' + verb + ' ' + service_name
|
44
44
|
Chef::Log.debug body unless body == ''
|
45
|
-
response = @rest.query_azure(service_name, verb, body)
|
45
|
+
response = @rest.query_azure(service_name, verb, body, params)
|
46
46
|
if response.code.to_i == 200
|
47
47
|
ret_val = Nokogiri::XML response.body
|
48
48
|
elsif response.code.to_i >= 201 && response.code.to_i <= 299
|
data/lib/azure/deploy.rb
CHANGED
@@ -89,13 +89,18 @@ class Azure
|
|
89
89
|
ret_val = deploy.create(params, deployXML)
|
90
90
|
end
|
91
91
|
if ret_val.css('Error Code').length > 0
|
92
|
-
|
93
|
-
exit 1
|
92
|
+
raise Chef::Log.fatal 'Unable to create role:' + ret_val.at_css('Error Code').content + ' : ' + ret_val.at_css('Error Message').content
|
94
93
|
end
|
95
94
|
@connection.roles.find_in_hosted_service(params[:azure_vm_name], params[:azure_dns_name])
|
96
95
|
end
|
97
96
|
def delete(rolename)
|
98
97
|
end
|
98
|
+
|
99
|
+
def queryDeploy(hostedservicename)
|
100
|
+
deploy = Deploy.new(@connection)
|
101
|
+
deploy.retrieve(hostedservicename)
|
102
|
+
deploy
|
103
|
+
end
|
99
104
|
end
|
100
105
|
|
101
106
|
class Deploy
|
@@ -134,6 +139,9 @@ class Azure
|
|
134
139
|
xml.DeploymentSlot 'Production'
|
135
140
|
xml.Label Base64.encode64(params['deploy_name']).strip
|
136
141
|
xml.RoleList { xml.Role('i:type'=>'PersistentVMRole') }
|
142
|
+
if params[:azure_network_name]
|
143
|
+
xml.VirtualNetworkName params[:azure_network_name]
|
144
|
+
end
|
137
145
|
}
|
138
146
|
end
|
139
147
|
builder.doc.at_css('Role') << roleXML.at_css('PersistentVMRole').children.to_s
|
data/lib/azure/host.rb
CHANGED
@@ -120,7 +120,12 @@ class Azure
|
|
120
120
|
xml.ServiceName params[:azure_dns_name]
|
121
121
|
xml.Label Base64.encode64(params[:azure_dns_name])
|
122
122
|
xml.Description 'Explicitly created hosted service'
|
123
|
-
|
123
|
+
unless params[:azure_service_location].nil?
|
124
|
+
xml.Location params[:azure_service_location]
|
125
|
+
end
|
126
|
+
unless params[:azure_affinity_group].nil?
|
127
|
+
xml.AffinityGroup params[:azure_affinity_group]
|
128
|
+
end
|
124
129
|
}
|
125
130
|
end
|
126
131
|
@connection.query_azure("hostedservices", "post", builder.to_xml)
|
data/lib/azure/image.rb
CHANGED
@@ -51,12 +51,13 @@ end
|
|
51
51
|
class Azure
|
52
52
|
class Image
|
53
53
|
attr_accessor :category, :label
|
54
|
-
attr_accessor :name, :os, :eula, :description
|
54
|
+
attr_accessor :name, :os, :eula, :description, :location
|
55
55
|
def initialize(image)
|
56
56
|
@category = image.at_css('Category').content
|
57
57
|
@label = image.at_css('Label').content
|
58
58
|
@name = image.at_css('Name').content
|
59
59
|
@os = image.at_css('OS').content
|
60
|
+
@location = image.at_css('Location').content.gsub(";", ", ") if image.at_css('Location')
|
60
61
|
@eula = image.at_css('Eula').content if image.at_css('Eula')
|
61
62
|
@description = image.at_css('Description').content if image.at_css('Description')
|
62
63
|
end
|
data/lib/azure/rest.rb
CHANGED
@@ -28,10 +28,11 @@ module AzureAPI
|
|
28
28
|
@host_name = params[:azure_api_host_name]
|
29
29
|
@verify_ssl = params[:verify_ssl_cert]
|
30
30
|
end
|
31
|
-
def query_azure(service_name, verb = 'get', body = '')
|
31
|
+
def query_azure(service_name, verb = 'get', body = '', params = '')
|
32
32
|
request_url = "https://#{@host_name}/#{@subscription_id}/services/#{service_name}"
|
33
33
|
print '.'
|
34
34
|
uri = URI.parse(request_url)
|
35
|
+
uri.query = params
|
35
36
|
http = http_setup(uri)
|
36
37
|
request = request_setup(uri, verb, body)
|
37
38
|
response = http.request(request)
|
@@ -72,7 +73,7 @@ module AzureAPI
|
|
72
73
|
elsif verb == 'delete'
|
73
74
|
request = Net::HTTP::Delete.new(uri.request_uri)
|
74
75
|
end
|
75
|
-
request["x-ms-version"] = "
|
76
|
+
request["x-ms-version"] = "2013-03-01"
|
76
77
|
request["content-type"] = "application/xml"
|
77
78
|
request["accept"] = "application/xml"
|
78
79
|
request["accept-charset"] = "utf-8"
|
data/lib/azure/role.rb
CHANGED
@@ -119,7 +119,12 @@ class Azure
|
|
119
119
|
break if @connection.query_azure(servicecall, "get").search("AttachedTo").text == ""
|
120
120
|
if attempt == 12 then puts "The associated disk could not be deleted due to time out." else sleep 25 end
|
121
121
|
end
|
122
|
-
|
122
|
+
|
123
|
+
unless params[:preserve_azure_vhd]
|
124
|
+
@connection.query_azure(servicecall, 'delete', '', 'comp=media')
|
125
|
+
else
|
126
|
+
@connection.query_azure(servicecall, 'delete')
|
127
|
+
end
|
123
128
|
|
124
129
|
if params[:delete_azure_storage_account]
|
125
130
|
storage_account_name = xml_content(storage_account, "MediaLink")
|
@@ -222,7 +227,7 @@ class Azure
|
|
222
227
|
xml.AdminPassword params[:admin_password]
|
223
228
|
xml.ResetPasswordOnFirstLogon 'false'
|
224
229
|
xml.EnableAutomaticUpdates 'false'
|
225
|
-
|
230
|
+
xml.AdminUsername params[:winrm_user]
|
226
231
|
}
|
227
232
|
end
|
228
233
|
|
@@ -248,18 +253,24 @@ class Azure
|
|
248
253
|
if params[:tcp_endpoints]
|
249
254
|
params[:tcp_endpoints].split(',').each do |endpoint|
|
250
255
|
ports = endpoint.split(':')
|
251
|
-
|
252
|
-
xml.
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
256
|
+
if !(ports.length > 1 && ports[1] == params[:port] || ports.length == 1 && ports[0] == params[:port])
|
257
|
+
xml.InputEndpoint {
|
258
|
+
xml.LocalPort ports[0]
|
259
|
+
xml.Name 'tcpport_' + ports[0] + '_' + params[:azure_vm_name]
|
260
|
+
if ports.length > 1
|
261
|
+
xml.Port ports[1]
|
262
|
+
else
|
263
|
+
xml.Port ports[0]
|
264
|
+
end
|
265
|
+
xml.Protocol 'TCP'
|
266
|
+
}
|
267
|
+
else
|
268
|
+
warn_message = ports.length > 1 ? "#{ports.join(':')} because this ports are" : "#{ports[0]} because this port is"
|
269
|
+
puts("Skipping tcp-endpoints: #{warn_message} already in use by ssh/winrm endpoint in current VM.")
|
270
|
+
end
|
261
271
|
end
|
262
272
|
end
|
273
|
+
|
263
274
|
if params[:udp_endpoints]
|
264
275
|
params[:udp_endpoints].split(',').each do |endpoint|
|
265
276
|
ports = endpoint.split(':')
|
@@ -276,8 +287,16 @@ class Azure
|
|
276
287
|
end
|
277
288
|
end
|
278
289
|
}
|
290
|
+
if params[:azure_subnet_name]
|
291
|
+
xml.SubnetNames {
|
292
|
+
xml.SubnetName params[:azure_subnet_name]
|
293
|
+
}
|
294
|
+
end
|
279
295
|
}
|
280
296
|
}
|
297
|
+
if params[:azure_availability_set]
|
298
|
+
xml.AvailabilitySetName params[:azure_availability_set]
|
299
|
+
end
|
281
300
|
xml.Label Base64.encode64(params[:azure_vm_name]).strip
|
282
301
|
xml.OSVirtualHardDisk {
|
283
302
|
disk_name = params[:azure_os_disk_name] || "disk_" + SecureRandom.uuid
|
data/lib/azure/storageaccount.rb
CHANGED
@@ -70,6 +70,13 @@ class Azure
|
|
70
70
|
@connection.query_azure('storageaccounts/' + storage.name, 'delete')
|
71
71
|
end
|
72
72
|
end
|
73
|
+
|
74
|
+
def delete(name)
|
75
|
+
if self.exists?(name)
|
76
|
+
servicecall = "storageservices/" + name
|
77
|
+
@connection.query_azure(servicecall, "delete")
|
78
|
+
end
|
79
|
+
end
|
73
80
|
end
|
74
81
|
end
|
75
82
|
|
@@ -99,7 +106,11 @@ class Azure
|
|
99
106
|
xml.Label Base64.encode64(params[:azure_storage_account])
|
100
107
|
xml.Description params[:azure_storage_account_description] || 'Explicitly created storage service'
|
101
108
|
# Location defaults to 'West US'
|
102
|
-
|
109
|
+
if params[:azure_affinity_group]
|
110
|
+
xml.AffinityGroup params[:azure_affinity_group]
|
111
|
+
else
|
112
|
+
xml.Location params[:azure_service_location] || 'West US'
|
113
|
+
end
|
103
114
|
}
|
104
115
|
end
|
105
116
|
@connection.query_azure("storageservices", "post", builder.to_xml)
|
@@ -98,6 +98,15 @@ class Chef
|
|
98
98
|
end
|
99
99
|
end
|
100
100
|
|
101
|
+
def msg_server_summary(server)
|
102
|
+
puts "\n"
|
103
|
+
msg_pair('DNS Name', server.hostedservicename + ".cloudapp.net")
|
104
|
+
msg_pair('VM Name', server.name)
|
105
|
+
msg_pair('Size', server.size)
|
106
|
+
msg_pair('Public Ip Address', server.publicipaddress)
|
107
|
+
puts "\n"
|
108
|
+
end
|
109
|
+
|
101
110
|
def validate!(keys=[:azure_subscription_id, :azure_mgmt_cert, :azure_api_host_name])
|
102
111
|
errors = []
|
103
112
|
if(locate_config_value(:azure_mgmt_cert) != nil)
|
@@ -44,7 +44,7 @@ class Chef
|
|
44
44
|
|
45
45
|
validate!
|
46
46
|
|
47
|
-
image_labels = !locate_config_value(:show_all_fields) ? ['Name', 'OS'] : ['Name', 'Category', 'Label', 'OS']
|
47
|
+
image_labels = !locate_config_value(:show_all_fields) ? ['Name', 'OS', 'Location'] : ['Name', 'Category', 'Label', 'OS', 'Location']
|
48
48
|
image_list = image_labels.map {|label| ui.color(label, :bold)}
|
49
49
|
items = connection.images.all
|
50
50
|
|
@@ -54,7 +54,7 @@ class Chef
|
|
54
54
|
end
|
55
55
|
|
56
56
|
puts "\n"
|
57
|
-
puts h.list(image_list, :uneven_columns_across, !locate_config_value(:show_all_fields) ?
|
57
|
+
puts h.list(image_list, :uneven_columns_across, !locate_config_value(:show_all_fields) ? 3 : 5)
|
58
58
|
|
59
59
|
end
|
60
60
|
end
|
@@ -20,6 +20,8 @@
|
|
20
20
|
|
21
21
|
require 'chef/knife/azure_base'
|
22
22
|
require 'chef/knife/winrm_base'
|
23
|
+
require 'securerandom'
|
24
|
+
|
23
25
|
class Chef
|
24
26
|
class Knife
|
25
27
|
class AzureServerCreate < Knife
|
@@ -119,18 +121,23 @@ class Chef
|
|
119
121
|
option :azure_vm_name,
|
120
122
|
:long => "--azure-vm-name NAME",
|
121
123
|
:description => "Required for advanced server-create option.
|
122
|
-
Specifies the name for the virtual machine. The name must be unique within the deployment."
|
124
|
+
Specifies the name for the virtual machine. The name must be unique within the deployment. The azure vm name cannot be more than 15 characters long"
|
123
125
|
|
124
126
|
option :azure_service_location,
|
125
127
|
:short => "-m LOCATION",
|
126
128
|
:long => "--azure-service-location LOCATION",
|
127
|
-
:description => "Required. Specifies the geographic location - the name of the data center location that is valid for your subscription.
|
129
|
+
: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.
|
128
130
|
Eg: West US, East US, East Asia, Southeast Asia, North Europe, West Europe"
|
129
131
|
|
132
|
+
option :azure_affinity_group,
|
133
|
+
:short => "-a GROUP",
|
134
|
+
:long => "--azure-affinity-group GROUP",
|
135
|
+
:description => "Required if not using a Service Location. Specifies Affinity Group the VM should belong to."
|
136
|
+
|
130
137
|
option :azure_dns_name,
|
131
138
|
:short => "-d DNS_NAME",
|
132
139
|
:long => "--azure-dns-name DNS_NAME",
|
133
|
-
:description => "
|
140
|
+
:description => "The DNS prefix name that can be used to access the cloud service which is unique within Windows Azure. Default is 'azure-dns-any_random_text'(e.g: azure-dns-be9b0f6f-7dda-456f-b2bf-4e28a3bc0add).
|
134
141
|
If you want to add new VM to an existing service/deployment, specify an exiting dns-name,
|
135
142
|
along with --azure-connect-to-existing-dns option.
|
136
143
|
Otherwise a new deployment is created. For example, if the DNS of cloud service is MyService you could access the cloud service
|
@@ -153,6 +160,10 @@ class Chef
|
|
153
160
|
:description => "Optional. Size of virtual machine (ExtraSmall, Small, Medium, Large, ExtraLarge)",
|
154
161
|
:default => 'Small'
|
155
162
|
|
163
|
+
option :azure_availability_set,
|
164
|
+
:long => "--azure-availability-set NAME",
|
165
|
+
:description => "Optional. Name of availability set to add virtual machine into."
|
166
|
+
|
156
167
|
option :tcp_endpoints,
|
157
168
|
:short => "-t PORT_LIST",
|
158
169
|
:long => "--tcp-endpoints PORT_LIST",
|
@@ -170,6 +181,15 @@ class Chef
|
|
170
181
|
:default => false,
|
171
182
|
:description => "Set this flag to add the new VM to an existing deployment/service. Must give the name of the existing
|
172
183
|
DNS correctly in the --dns-name option"
|
184
|
+
|
185
|
+
option :azure_network_name,
|
186
|
+
:long => "--azure-network-name NETWORK_NAME",
|
187
|
+
:description => "Optional. Specifies the network of virtual machine"
|
188
|
+
|
189
|
+
option :azure_subnet_name,
|
190
|
+
:long => "--azure-subnet-name SUBNET_NAME",
|
191
|
+
:description => "Optional. Specifies the subnet of virtual machine"
|
192
|
+
|
173
193
|
option :identity_file,
|
174
194
|
:long => "--identity-file FILENAME",
|
175
195
|
:description => "SSH identity file for authentication, optional. It is the RSA private key path. Specify either ssh-password or identity-file"
|
@@ -195,6 +215,43 @@ class Chef
|
|
195
215
|
(0...len).map{65.+(rand(25)).chr}.join
|
196
216
|
end
|
197
217
|
|
218
|
+
def wait_until_virtual_machine_ready(total_wait_time_in_minutes = 15, retry_interval_in_seconds = 30)
|
219
|
+
print "\n#{ui.color('Waiting for virtual machine to be ready.', :magenta)}"
|
220
|
+
total_wait_time_in_seconds = total_wait_time_in_minutes * 60
|
221
|
+
max_polling_attempts = total_wait_time_in_seconds / retry_interval_in_seconds
|
222
|
+
|
223
|
+
wait_start_time = Time.now
|
224
|
+
|
225
|
+
vm_ready = check_if_virtual_machine_ready()
|
226
|
+
polling_attempts = 1
|
227
|
+
until vm_ready || polling_attempts >= max_polling_attempts
|
228
|
+
print '.'
|
229
|
+
sleep retry_interval_in_seconds
|
230
|
+
vm_ready = check_if_virtual_machine_ready()
|
231
|
+
polling_attempts += 1
|
232
|
+
end
|
233
|
+
if vm_ready
|
234
|
+
elapsed_time_in_minutes = ((Time.now - wait_start_time) / 60).round(2)
|
235
|
+
puts("vm ready after #{elapsed_time_in_minutes} minutes.")
|
236
|
+
else
|
237
|
+
raise "Virtual machine not ready after #{total_wait_time_in_minutes} minutes."
|
238
|
+
end
|
239
|
+
end
|
240
|
+
|
241
|
+
def check_if_virtual_machine_ready()
|
242
|
+
deploy = connection.deploys.queryDeploy(locate_config_value(:azure_dns_name))
|
243
|
+
role = deploy.find_role(locate_config_value(:azure_vm_name))
|
244
|
+
if role.nil?
|
245
|
+
raise "Could not find role - status unknown."
|
246
|
+
end
|
247
|
+
Chef::Log.debug("Role status is #{role.status.to_s}")
|
248
|
+
if "ReadyRole".eql? role.status.to_s
|
249
|
+
return true
|
250
|
+
else
|
251
|
+
return false
|
252
|
+
end
|
253
|
+
end
|
254
|
+
|
198
255
|
def tcp_test_winrm(ip_addr, port)
|
199
256
|
hostname = ip_addr
|
200
257
|
socket = TCPSocket.new(hostname, port)
|
@@ -253,66 +310,87 @@ class Chef
|
|
253
310
|
|
254
311
|
Chef::Log.info("creating...")
|
255
312
|
|
313
|
+
config[:azure_dns_name] = get_dns_name(locate_config_value(:azure_dns_name))
|
256
314
|
if not locate_config_value(:azure_vm_name)
|
257
315
|
config[:azure_vm_name] = locate_config_value(:azure_dns_name)
|
258
316
|
end
|
259
317
|
|
318
|
+
remove_hosted_service_on_failure = locate_config_value(:azure_dns_name)
|
319
|
+
if connection.hosts.exists?(locate_config_value(:azure_dns_name))
|
320
|
+
remove_hosted_service_on_failure = nil
|
321
|
+
end
|
322
|
+
remove_storage_service_on_failure = locate_config_value(:azure_storage_account)
|
323
|
+
|
260
324
|
#If Storage Account is not specified, check if the geographic location has one to re-use
|
261
325
|
if not locate_config_value(:azure_storage_account)
|
262
326
|
storage_accts = connection.storageaccounts.all
|
263
327
|
storage = storage_accts.find { |storage_acct| storage_acct.location.to_s == locate_config_value(:azure_service_location) }
|
264
328
|
if not storage
|
265
329
|
config[:azure_storage_account] = [strip_non_ascii(locate_config_value(:azure_vm_name)), random_string].join.downcase
|
330
|
+
remove_storage_service_on_failure = config[:azure_storage_account]
|
266
331
|
else
|
332
|
+
remove_storage_service_on_failure = nil
|
267
333
|
config[:azure_storage_account] = storage.name.to_s
|
268
334
|
end
|
269
335
|
end
|
270
336
|
|
271
|
-
|
272
|
-
|
337
|
+
begin
|
338
|
+
server = connection.deploys.create(create_server_def)
|
339
|
+
fqdn = server.publicipaddress
|
340
|
+
wait_until_virtual_machine_ready()
|
341
|
+
rescue Exception => e
|
342
|
+
Chef::Log.error("Failed to create the server -- exception being rescued: #{e.to_s}")
|
343
|
+
cleanup_and_exit(remove_hosted_service_on_failure, remove_storage_service_on_failure)
|
344
|
+
end
|
345
|
+
|
346
|
+
msg_server_summary(server)
|
273
347
|
|
274
|
-
puts("\n")
|
275
348
|
if is_image_windows?
|
349
|
+
# Set distro to windows-chef-client-msi
|
350
|
+
config[:distro] = "windows-chef-client-msi" if (config[:distro].nil? || config[:distro] == "chef-full")
|
351
|
+
|
276
352
|
if locate_config_value(:bootstrap_protocol) == 'ssh'
|
277
353
|
port = server.sshport
|
278
|
-
print "
|
354
|
+
print "#{ui.color("Waiting for sshd on #{fqdn}:#{port}", :magenta)}"
|
279
355
|
|
280
356
|
print(".") until tcp_test_ssh(fqdn,port) {
|
281
357
|
sleep @initial_sleep_delay ||= 10
|
282
358
|
puts("done")
|
283
|
-
|
359
|
+
}
|
284
360
|
|
285
361
|
elsif locate_config_value(:bootstrap_protocol) == 'winrm'
|
286
362
|
port = server.winrmport
|
287
363
|
|
288
|
-
print "
|
364
|
+
print "#{ui.color("Waiting for winrm on #{fqdn}:#{port}", :magenta)}"
|
289
365
|
|
290
366
|
print(".") until tcp_test_winrm(fqdn,port) {
|
291
367
|
sleep @initial_sleep_delay ||= 10
|
292
368
|
puts("done")
|
293
|
-
|
294
|
-
|
369
|
+
}
|
295
370
|
end
|
296
|
-
|
371
|
+
|
372
|
+
puts("\n")
|
297
373
|
bootstrap_for_windows_node(server,fqdn, port).run
|
298
374
|
else
|
299
375
|
unless server && server.publicipaddress && server.sshport
|
300
376
|
Chef::Log.fatal("server not created")
|
301
|
-
|
302
|
-
|
377
|
+
exit 1
|
378
|
+
end
|
303
379
|
|
304
|
-
|
380
|
+
port = server.sshport
|
305
381
|
|
306
|
-
|
382
|
+
print "#{ui.color("Waiting for sshd on #{fqdn}:#{port}", :magenta)}"
|
307
383
|
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
384
|
+
print(".") until tcp_test_ssh(fqdn,port) {
|
385
|
+
sleep @initial_sleep_delay ||= 10
|
386
|
+
puts("done")
|
387
|
+
}
|
312
388
|
|
313
|
-
|
389
|
+
puts("\n")
|
314
390
|
bootstrap_for_node(server,fqdn,port).run
|
315
391
|
end
|
392
|
+
|
393
|
+
msg_server_summary(server)
|
316
394
|
end
|
317
395
|
|
318
396
|
def load_cloud_attributes_in_hints(server)
|
@@ -371,8 +449,8 @@ class Chef
|
|
371
449
|
end
|
372
450
|
bootstrap.name_args = [fqdn]
|
373
451
|
bootstrap.config[:chef_node_name] = config[:chef_node_name] || server.name
|
374
|
-
bootstrap.config[:encrypted_data_bag_secret] =
|
375
|
-
bootstrap.config[:encrypted_data_bag_secret_file] =
|
452
|
+
bootstrap.config[:encrypted_data_bag_secret] = locate_config_value(:encrypted_data_bag_secret)
|
453
|
+
bootstrap.config[:encrypted_data_bag_secret_file] = locate_config_value(:encrypted_data_bag_secret_file)
|
376
454
|
bootstrap_common_params(bootstrap, server)
|
377
455
|
end
|
378
456
|
|
@@ -394,6 +472,8 @@ class Chef
|
|
394
472
|
bootstrap.config[:environment] = locate_config_value(:environment)
|
395
473
|
# may be needed for vpc_mode
|
396
474
|
bootstrap.config[:host_key_verify] = config[:host_key_verify]
|
475
|
+
bootstrap.config[:secret] = locate_config_value(:secret)
|
476
|
+
bootstrap.config[:secret_file] = locate_config_value(:secret_file)
|
397
477
|
|
398
478
|
# Load cloud attributes.
|
399
479
|
load_cloud_attributes_in_hints(server)
|
@@ -406,8 +486,6 @@ class Chef
|
|
406
486
|
:azure_subscription_id,
|
407
487
|
:azure_mgmt_cert,
|
408
488
|
:azure_api_host_name,
|
409
|
-
:azure_dns_name,
|
410
|
-
:azure_service_location,
|
411
489
|
:azure_source_image,
|
412
490
|
:azure_vm_size,
|
413
491
|
])
|
@@ -415,6 +493,13 @@ class Chef
|
|
415
493
|
ui.error("Specify the VM name using --azure-vm-name option, since you are connecting to existing dns")
|
416
494
|
exit 1
|
417
495
|
end
|
496
|
+
if locate_config_value(:azure_service_location) && locate_config_value(:azure_affinity_group)
|
497
|
+
ui.error("Cannot specify both --azure_service_location and --azure_affinity_group, use one or the other.")
|
498
|
+
exit 1
|
499
|
+
elsif locate_config_value(:azure_service_location).nil? && locate_config_value(:azure_affinity_group).nil?
|
500
|
+
ui.error("Must specify either --azure_service_location or --azure_affinity_group.")
|
501
|
+
exit 1
|
502
|
+
end
|
418
503
|
end
|
419
504
|
|
420
505
|
def create_server_def
|
@@ -429,7 +514,12 @@ class Chef
|
|
429
514
|
:tcp_endpoints => locate_config_value(:tcp_endpoints),
|
430
515
|
:udp_endpoints => locate_config_value(:udp_endpoints),
|
431
516
|
:bootstrap_proto => locate_config_value(:bootstrap_protocol),
|
432
|
-
:azure_connect_to_existing_dns => locate_config_value(:azure_connect_to_existing_dns)
|
517
|
+
:azure_connect_to_existing_dns => locate_config_value(:azure_connect_to_existing_dns),
|
518
|
+
:winrm_user => locate_config_value(:winrm_user),
|
519
|
+
:azure_availability_set => locate_config_value(:azure_availability_set),
|
520
|
+
:azure_affinity_group => locate_config_value(:azure_affinity_group),
|
521
|
+
:azure_network_name => locate_config_value(:azure_network_name),
|
522
|
+
:azure_subnet_name => locate_config_value(:azure_subnet_name)
|
433
523
|
}
|
434
524
|
# If user is connecting a new VM to an existing dns, then
|
435
525
|
# the VM needs to have a unique public port. Logic below takes care of this.
|
@@ -450,6 +540,14 @@ class Chef
|
|
450
540
|
server_def[:os_type] = 'Windows'
|
451
541
|
if not locate_config_value(:winrm_password) or not locate_config_value(:bootstrap_protocol)
|
452
542
|
ui.error("WinRM Password and Bootstrapping Protocol are compulsory parameters")
|
543
|
+
exit 1
|
544
|
+
end
|
545
|
+
# We can specify the AdminUsername after API version 2013-03-01. However, in this API version,
|
546
|
+
# the AdminUsername is a required parameter.
|
547
|
+
# Also, the user name cannot be Administrator, Admin, Admin1 etc, for enhanced security (provided by Azure)
|
548
|
+
if locate_config_value(:winrm_user).nil? || locate_config_value(:winrm_user).downcase =~ /admin*/
|
549
|
+
ui.error("WinRM User is compulsory parameter and it cannot be named 'admin*'")
|
550
|
+
exit
|
453
551
|
end
|
454
552
|
server_def[:admin_password] = locate_config_value(:winrm_password)
|
455
553
|
server_def[:bootstrap_proto] = locate_config_value(:bootstrap_protocol)
|
@@ -472,7 +570,32 @@ class Chef
|
|
472
570
|
end
|
473
571
|
server_def
|
474
572
|
end
|
573
|
+
|
574
|
+
def cleanup_and_exit(remove_hosted_service_on_failure, remove_storage_service_on_failure)
|
575
|
+
if remove_hosted_service_on_failure
|
576
|
+
connection.hosts.delete(remove_hosted_service_on_failure)
|
577
|
+
end
|
578
|
+
if remove_storage_service_on_failure
|
579
|
+
connection.storageaccounts.delete(remove_storage_service_on_failure)
|
580
|
+
end
|
581
|
+
exit 1
|
582
|
+
end
|
583
|
+
|
584
|
+
private
|
585
|
+
# This is related to Windows VM's specifically and computer name
|
586
|
+
# length limits for legacy computer accounts
|
587
|
+
MAX_VM_NAME_CHARACTERS = 15
|
588
|
+
|
589
|
+
# generate a random dns_name if azure_dns_name is empty
|
590
|
+
def get_dns_name(azure_dns_name, prefix = "az-")
|
591
|
+
return azure_dns_name unless azure_dns_name.nil?
|
592
|
+
if locate_config_value(:azure_vm_name).nil?
|
593
|
+
azure_dns_name = prefix + SecureRandom.hex(( MAX_VM_NAME_CHARACTERS - prefix.length)/2)
|
594
|
+
else
|
595
|
+
azure_dns_name = prefix + locate_config_value(:azure_vm_name)
|
596
|
+
end
|
597
|
+
end
|
598
|
+
|
475
599
|
end
|
476
600
|
end
|
477
601
|
end
|
478
|
-
|
@@ -38,6 +38,12 @@ class Chef
|
|
38
38
|
:default => false,
|
39
39
|
:description => "Preserve corresponding OS Disk"
|
40
40
|
|
41
|
+
option :preserve_azure_vhd,
|
42
|
+
:long => "--preserve-azure-vhd",
|
43
|
+
:boolean => true,
|
44
|
+
:default => false,
|
45
|
+
:description => "Preserve underlying VHD"
|
46
|
+
|
41
47
|
option :purge,
|
42
48
|
:short => "-P",
|
43
49
|
:long => "--purge",
|
@@ -118,6 +124,7 @@ class Chef
|
|
118
124
|
end
|
119
125
|
|
120
126
|
connection.roles.delete(name, params = { :preserve_azure_os_disk => locate_config_value(:preserve_azure_os_disk),
|
127
|
+
:preserve_azure_vhd => locate_config_value(:preserve_azure_vhd),
|
121
128
|
:preserve_azure_dns_name => locate_config_value(:preserve_azure_dns_name),
|
122
129
|
:azure_dns_name => server.hostedservicename,
|
123
130
|
:delete_azure_storage_account => locate_config_value(:delete_azure_storage_account) })
|
data/lib/knife-azure/version.rb
CHANGED
metadata
CHANGED
@@ -1,8 +1,8 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: knife-azure
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.1.
|
5
|
-
prerelease:
|
4
|
+
version: 1.1.4.rc.0
|
5
|
+
prerelease: 6
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
8
8
|
- Barry Davis
|
@@ -10,7 +10,7 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date: 2013-
|
13
|
+
date: 2013-11-06 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: chef
|
@@ -97,7 +97,7 @@ dependencies:
|
|
97
97
|
requirement: !ruby/object:Gem::Requirement
|
98
98
|
none: false
|
99
99
|
requirements:
|
100
|
-
- -
|
100
|
+
- - ! '>='
|
101
101
|
- !ruby/object:Gem::Version
|
102
102
|
version: 1.5.5
|
103
103
|
type: :runtime
|
@@ -105,7 +105,7 @@ dependencies:
|
|
105
105
|
version_requirements: !ruby/object:Gem::Requirement
|
106
106
|
none: false
|
107
107
|
requirements:
|
108
|
-
- -
|
108
|
+
- - ! '>='
|
109
109
|
- !ruby/object:Gem::Version
|
110
110
|
version: 1.5.5
|
111
111
|
- !ruby/object:Gem::Dependency
|
@@ -124,6 +124,22 @@ dependencies:
|
|
124
124
|
- - ! '>='
|
125
125
|
- !ruby/object:Gem::Version
|
126
126
|
version: '0'
|
127
|
+
- !ruby/object:Gem::Dependency
|
128
|
+
name: mixlib-config
|
129
|
+
requirement: !ruby/object:Gem::Requirement
|
130
|
+
none: false
|
131
|
+
requirements:
|
132
|
+
- - ~>
|
133
|
+
- !ruby/object:Gem::Version
|
134
|
+
version: '2.0'
|
135
|
+
type: :development
|
136
|
+
prerelease: false
|
137
|
+
version_requirements: !ruby/object:Gem::Requirement
|
138
|
+
none: false
|
139
|
+
requirements:
|
140
|
+
- - ~>
|
141
|
+
- !ruby/object:Gem::Version
|
142
|
+
version: '2.0'
|
127
143
|
description: A plugin to Opscode knife for creating instances on the Microsoft Azure
|
128
144
|
platform
|
129
145
|
email: oss@opscode.com
|
@@ -166,7 +182,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
166
182
|
version: '0'
|
167
183
|
segments:
|
168
184
|
- 0
|
169
|
-
hash:
|
185
|
+
hash: 4192859344861281444
|
170
186
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
171
187
|
none: false
|
172
188
|
requirements:
|