knife-azure 1.9.0 → 2.0.1
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.
- checksums.yaml +4 -4
- data/lib/azure/azure_interface.rb +2 -3
- data/lib/azure/custom_errors.rb +1 -1
- data/lib/azure/helpers.rb +1 -1
- data/lib/azure/resource_management/ARM_deployment_template.rb +157 -162
- data/lib/azure/resource_management/ARM_interface.rb +72 -73
- data/lib/azure/resource_management/vnet_config.rb +11 -10
- data/lib/azure/resource_management/windows_credentials.rb +19 -19
- data/lib/azure/service_management/ASM_interface.rb +6 -5
- data/lib/azure/service_management/ag.rb +11 -11
- data/lib/azure/service_management/certificate.rb +7 -5
- data/lib/azure/service_management/connection.rb +10 -10
- data/lib/azure/service_management/deploy.rb +12 -14
- data/lib/azure/service_management/disk.rb +4 -2
- data/lib/azure/service_management/host.rb +7 -4
- data/lib/azure/service_management/image.rb +4 -4
- data/lib/azure/service_management/loadbalancer.rb +2 -2
- data/lib/azure/service_management/rest.rb +9 -8
- data/lib/azure/service_management/role.rb +67 -70
- data/lib/azure/service_management/storageaccount.rb +5 -3
- data/lib/azure/service_management/utility.rb +1 -1
- data/lib/azure/service_management/vnet.rb +1 -1
- data/lib/chef/knife/azure_ag_create.rb +13 -13
- data/lib/chef/knife/azure_ag_list.rb +1 -1
- data/lib/chef/knife/azure_base.rb +49 -66
- data/lib/chef/knife/azure_image_list.rb +6 -6
- data/lib/chef/knife/azure_internal-lb_create.rb +14 -14
- data/lib/chef/knife/azure_internal-lb_list.rb +1 -1
- data/lib/chef/knife/azure_server_create.rb +233 -268
- data/lib/chef/knife/azure_server_delete.rb +31 -31
- data/lib/chef/knife/azure_server_list.rb +1 -1
- data/lib/chef/knife/azure_server_show.rb +1 -1
- data/lib/chef/knife/azure_vnet_create.rb +15 -19
- data/lib/chef/knife/azure_vnet_list.rb +1 -1
- data/lib/chef/knife/azurerm_base.rb +39 -28
- data/lib/chef/knife/azurerm_server_create.rb +112 -177
- data/lib/chef/knife/azurerm_server_delete.rb +13 -13
- data/lib/chef/knife/azurerm_server_list.rb +1 -1
- data/lib/chef/knife/azurerm_server_show.rb +1 -1
- data/lib/chef/knife/bootstrap/bootstrapper.rb +34 -238
- data/lib/chef/knife/bootstrap/common_bootstrap_options.rb +77 -76
- data/lib/chef/knife/bootstrap_azure.rb +56 -33
- data/lib/chef/knife/bootstrap_azurerm.rb +46 -29
- data/lib/knife-azure/version.rb +18 -1
- metadata +28 -16
- data/lib/chef/knife/bootstrap/bootstrap_options.rb +0 -105
@@ -1,6 +1,6 @@
|
|
1
1
|
#
|
2
2
|
# Author:: Aiman Alsari (aiman.alsari@gmail.com)
|
3
|
-
# Copyright:: Copyright
|
3
|
+
# Copyright:: Copyright 2010-2019, Chef Software Inc.
|
4
4
|
# License:: Apache License, Version 2.0
|
5
5
|
#
|
6
6
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
@@ -2,7 +2,7 @@
|
|
2
2
|
# Author:: Barry Davis (barryd@jetstreamsoftware.com)
|
3
3
|
# Author:: Adam Jacob (<adam@chef.io>)
|
4
4
|
# Author:: Seth Chisamore (<schisamo@chef.io>)
|
5
|
-
# Copyright:: Copyright 2010-
|
5
|
+
# Copyright:: Copyright 2010-2019, Chef Software Inc.
|
6
6
|
# License:: Apache License, Version 2.0
|
7
7
|
#
|
8
8
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
@@ -19,216 +19,211 @@
|
|
19
19
|
#
|
20
20
|
|
21
21
|
require "chef/knife/azure_base"
|
22
|
-
require "chef/knife/winrm_base"
|
23
22
|
require "securerandom"
|
24
|
-
require "chef/knife/bootstrap
|
23
|
+
require "chef/knife/bootstrap"
|
24
|
+
require "chef/knife/bootstrap/client_builder"
|
25
|
+
require "chef/knife/bootstrap/common_bootstrap_options"
|
25
26
|
require "chef/knife/bootstrap/bootstrapper"
|
26
27
|
|
27
28
|
class Chef
|
28
29
|
class Knife
|
29
|
-
class AzureServerCreate < Knife
|
30
|
-
|
30
|
+
class AzureServerCreate < Knife::Bootstrap
|
31
31
|
include Knife::AzureBase
|
32
|
-
include Knife::
|
33
|
-
include Knife::Bootstrap::BootstrapOptions
|
32
|
+
include Knife::Bootstrap::CommonBootstrapOptions
|
34
33
|
include Knife::Bootstrap::Bootstrapper
|
35
34
|
|
36
35
|
deps do
|
37
36
|
require "readline"
|
38
37
|
require "chef/json_compat"
|
39
38
|
require "chef/knife/bootstrap"
|
40
|
-
require "chef/knife/bootstrap_windows_ssh"
|
41
39
|
require "chef/knife/core/windows_bootstrap_context"
|
42
40
|
Chef::Knife::Bootstrap.load_deps
|
43
41
|
end
|
44
42
|
|
45
43
|
banner "knife azure server create (options)"
|
46
44
|
|
47
|
-
|
45
|
+
SUPPORTED_CONNECTION_PROTOCOLS = %w{ssh winrm cloud-api}.freeze
|
48
46
|
|
49
|
-
|
50
|
-
:long => "--bootstrap-protocol protocol",
|
51
|
-
:description => "Protocol to bootstrap windows servers. options: 'winrm' or 'ssh' or 'cloud-api'.",
|
52
|
-
:default => "winrm"
|
53
|
-
|
54
|
-
option :ssh_user,
|
55
|
-
:short => "-x USERNAME",
|
56
|
-
:long => "--ssh-user USERNAME",
|
57
|
-
:description => "The ssh username",
|
58
|
-
:default => "root"
|
59
|
-
|
60
|
-
option :ssh_password,
|
61
|
-
:short => "-P PASSWORD",
|
62
|
-
:long => "--ssh-password PASSWORD",
|
63
|
-
:description => "The ssh password"
|
64
|
-
|
65
|
-
option :ssh_port,
|
66
|
-
:long => "--ssh-port PORT",
|
67
|
-
:description => "The ssh port. Default is 22. If --azure-connect-to-existing-dns set then default SSH port is random"
|
68
|
-
|
69
|
-
option :node_ssl_verify_mode,
|
70
|
-
:long => "--node-ssl-verify-mode [peer|none]",
|
71
|
-
:description => "Whether or not to verify the SSL cert for all HTTPS requests.",
|
72
|
-
:proc => Proc.new { |v|
|
73
|
-
valid_values = %w{none peer}
|
74
|
-
unless valid_values.include?(v)
|
75
|
-
raise "Invalid value '#{v}' for --node-ssl-verify-mode. Valid values are: #{valid_values.join(", ")}"
|
76
|
-
end
|
77
|
-
}
|
78
|
-
|
79
|
-
option :node_verify_api_cert,
|
80
|
-
:long => "--[no-]node-verify-api-cert",
|
81
|
-
:description => "Verify the SSL cert for HTTPS requests to the Chef server API.",
|
82
|
-
:boolean => true
|
83
|
-
|
84
|
-
option :azure_storage_account,
|
85
|
-
:short => "-a NAME",
|
86
|
-
:long => "--azure-storage-account NAME",
|
87
|
-
:description => "Required for advanced server-create option.
|
88
|
-
A name for the storage account that is unique within Windows Azure. Storage account names must be
|
89
|
-
between 3 and 24 characters in length and use numbers and lower-case letters only.
|
90
|
-
This name is the DNS prefix name and can be used to access blobs, queues, and tables in the storage account.
|
91
|
-
For example: http://ServiceName.blob.core.windows.net/mycontainer/"
|
92
|
-
|
93
|
-
option :azure_vm_name,
|
94
|
-
:long => "--azure-vm-name NAME",
|
95
|
-
:description => "Required for advanced server-create option.
|
96
|
-
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"
|
97
|
-
|
98
|
-
option :azure_service_location,
|
99
|
-
:short => "-m LOCATION",
|
100
|
-
:long => "--azure-service-location LOCATION",
|
101
|
-
: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.
|
102
|
-
Eg: West US, East US, East Asia, Southeast Asia, North Europe, West Europe",
|
103
|
-
:proc => Proc.new { |lo| Chef::Config[:knife][:azure_service_location] = lo }
|
47
|
+
attr_accessor :initial_sleep_delay
|
104
48
|
|
105
49
|
option :azure_affinity_group,
|
106
|
-
:
|
107
|
-
:
|
108
|
-
:
|
50
|
+
short: "-a GROUP",
|
51
|
+
long: "--azure-affinity-group GROUP",
|
52
|
+
description: "Required if not using a Service Location. Specifies Affinity Group the VM should belong to."
|
109
53
|
|
110
54
|
option :azure_dns_name,
|
111
|
-
:
|
112
|
-
:
|
113
|
-
:
|
55
|
+
short: "-d DNS_NAME",
|
56
|
+
long: "--azure-dns-name DNS_NAME",
|
57
|
+
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).
|
114
58
|
If you want to add new VM to an existing service/deployment, specify an exiting dns-name,
|
115
59
|
along with --azure-connect-to-existing-dns option.
|
116
60
|
Otherwise a new deployment is created. For example, if the DNS of cloud service is MyService you could access the cloud service
|
117
61
|
by calling: http://DNS_NAME.cloudapp.net"
|
118
62
|
|
119
|
-
option :azure_os_disk_name,
|
120
|
-
:short => "-o DISKNAME",
|
121
|
-
:long => "--azure-os-disk-name DISKNAME",
|
122
|
-
:description => "Optional. Specifies the friendly name of the disk containing the guest OS image in the image repository."
|
123
|
-
|
124
63
|
option :azure_source_image,
|
125
|
-
:
|
126
|
-
:
|
127
|
-
:
|
64
|
+
short: "-I IMAGE",
|
65
|
+
long: "--azure-source-image IMAGE",
|
66
|
+
description: "Required. Specifies the name of the disk image to use to create the virtual machine.
|
128
67
|
Do a \"knife azure image list\" to see a list of available images."
|
129
68
|
|
130
|
-
option :azure_vm_size,
|
131
|
-
:short => "-z SIZE",
|
132
|
-
:long => "--azure-vm-size SIZE",
|
133
|
-
:description => "Optional. Size of virtual machine. Default is Standard_A1_v2.
|
134
|
-
Eg: Standard_A1_v2, Standard_F2, Standard_G1 etc.",
|
135
|
-
:default => "Standard_A1_v2",
|
136
|
-
:proc => Proc.new { |si| Chef::Config[:knife][:azure_vm_size] = si }
|
137
|
-
|
138
|
-
option :azure_availability_set,
|
139
|
-
:long => "--azure-availability-set NAME",
|
140
|
-
:description => "Optional. Name of availability set to add virtual machine into."
|
141
|
-
|
142
|
-
option :tcp_endpoints,
|
143
|
-
:short => "-t PORT_LIST",
|
144
|
-
:long => "--tcp-endpoints PORT_LIST",
|
145
|
-
:description => "Comma-separated list of TCP local and public ports to open e.g. '80:80,433:5000'"
|
146
|
-
|
147
69
|
option :udp_endpoints,
|
148
|
-
:
|
149
|
-
:
|
150
|
-
:
|
70
|
+
short: "-u PORT_LIST",
|
71
|
+
long: "--udp-endpoints PORT_LIST",
|
72
|
+
description: "Comma-separated list of UDP local and public ports to open e.g. '80:80,433:5000'"
|
151
73
|
|
152
74
|
option :azure_connect_to_existing_dns,
|
153
|
-
:
|
154
|
-
:
|
155
|
-
:
|
156
|
-
:
|
157
|
-
:
|
75
|
+
short: "-c",
|
76
|
+
long: "--azure-connect-to-existing-dns",
|
77
|
+
boolean: true,
|
78
|
+
default: false,
|
79
|
+
description: "Set this flag to add the new VM to an existing deployment/service. Must give the name of the existing
|
158
80
|
DNS correctly in the --dns-name option"
|
159
81
|
|
160
82
|
option :azure_network_name,
|
161
|
-
:
|
162
|
-
:
|
83
|
+
long: "--azure-network-name NETWORK_NAME",
|
84
|
+
description: "Optional. Specifies the network of virtual machine"
|
163
85
|
|
164
86
|
option :azure_subnet_name,
|
165
|
-
:
|
166
|
-
:
|
87
|
+
long: "--azure-subnet-name SUBNET_NAME",
|
88
|
+
description: "Optional. Specifies the subnet of virtual machine"
|
167
89
|
|
168
90
|
option :azure_vm_startup_timeout,
|
169
|
-
:
|
170
|
-
:
|
171
|
-
:
|
91
|
+
long: "--azure-vm-startup-timeout TIMEOUT",
|
92
|
+
description: "The number of minutes that knife-azure will wait for the virtual machine to reach the 'provisioning' state. Default is 10.",
|
93
|
+
default: 10
|
172
94
|
|
173
95
|
option :azure_vm_ready_timeout,
|
174
|
-
:
|
175
|
-
:
|
176
|
-
:
|
96
|
+
long: "--azure-vm-ready-timeout TIMEOUT",
|
97
|
+
description: "The number of minutes that knife-azure will wait for the virtual machine state to transition from 'provisioning' to 'ready'. Default is 15.",
|
98
|
+
default: 15
|
177
99
|
|
178
100
|
option :auth_timeout,
|
179
|
-
:
|
180
|
-
:
|
181
|
-
:
|
182
|
-
|
183
|
-
option :identity_file,
|
184
|
-
:long => "--identity-file FILENAME",
|
185
|
-
:description => "SSH identity file for authentication, optional. It is the RSA private key path. Specify either ssh-password or identity-file"
|
101
|
+
long: "--windows-auth-timeout MINUTES",
|
102
|
+
description: "The maximum time in minutes to wait to for authentication over the transport to the node to succeed. The default value is 25 minutes.",
|
103
|
+
default: 25
|
186
104
|
|
187
105
|
option :identity_file_passphrase,
|
188
|
-
:
|
189
|
-
:
|
190
|
-
|
191
|
-
option :thumbprint,
|
192
|
-
:long => "--thumbprint THUMBPRINT",
|
193
|
-
:description => "The thumprint of the ssl certificate"
|
194
|
-
|
195
|
-
option :cert_passphrase,
|
196
|
-
:long => "--cert-passphrase PASSWORD",
|
197
|
-
:description => "SSL Certificate Password"
|
198
|
-
|
199
|
-
option :cert_path,
|
200
|
-
:long => "--cert-path PATH",
|
201
|
-
:description => "SSL Certificate Path"
|
106
|
+
long: "--identity-file-passphrase PASSWORD",
|
107
|
+
description: "SSH key passphrase. Optional, specify if passphrase for identity-file exists"
|
202
108
|
|
203
109
|
option :winrm_max_timeout,
|
204
|
-
:
|
205
|
-
:
|
110
|
+
long: "--winrm-max-timeout MINUTES",
|
111
|
+
description: "Set winrm maximum command timeout in minutes, useful for long bootstraps"
|
206
112
|
|
207
|
-
option :
|
208
|
-
:
|
209
|
-
:
|
113
|
+
option :winrm_max_memory_per_shell,
|
114
|
+
long: "--winrm-max-memory-per-shell",
|
115
|
+
description: "Set winrm max memory per shell in MB"
|
210
116
|
|
211
117
|
option :azure_domain_name,
|
212
|
-
:
|
213
|
-
:
|
118
|
+
long: "--azure-domain-name DOMAIN_NAME",
|
119
|
+
description: 'Optional. Specifies the domain name to join. If the domains name is not specified, --azure-domain-user must specify the user principal name (UPN) format (user@fully-qualified-DNS-domain) or the fully-qualified-DNS-domain\\username format'
|
214
120
|
|
215
121
|
option :azure_domain_ou_dn,
|
216
|
-
:
|
217
|
-
:
|
122
|
+
long: "--azure-domain-ou-dn DOMAIN_OU_DN",
|
123
|
+
description: "Optional. Specifies the (LDAP) X 500-distinguished name of the organizational unit (OU) in which the computer account is created. This account is in Active Directory on a domain controller in the domain to which the computer is being joined. Example: OU=HR,dc=opscode,dc=com"
|
218
124
|
|
219
125
|
option :azure_domain_user,
|
220
|
-
:
|
221
|
-
:
|
126
|
+
long: "--azure-domain-user DOMAIN_USER_NAME",
|
127
|
+
description: 'Optional. Specifies the username who has access to join the domain.
|
222
128
|
Supported format: username(if domain is already specified in --azure-domain-name option),
|
223
129
|
fully-qualified-DNS-domain\username, user@fully-qualified-DNS-domain'
|
224
130
|
|
225
131
|
option :azure_domain_passwd,
|
226
|
-
:
|
227
|
-
:
|
132
|
+
long: "--azure-domain-passwd DOMAIN_PASSWD",
|
133
|
+
description: "Optional. Specifies the password for domain user who has access to join the domain."
|
134
|
+
|
135
|
+
# Overriding this option to provide "cloud-api" in SUPPORTED_CONNECTION_PROTOCOLS
|
136
|
+
option :connection_protocol,
|
137
|
+
short: "-o PROTOCOL",
|
138
|
+
long: "--connection-protocol PROTOCOL",
|
139
|
+
description: "The protocol to use to connect to the target node.",
|
140
|
+
in: SUPPORTED_CONNECTION_PROTOCOLS
|
141
|
+
|
142
|
+
# run() would be executing from parent class
|
143
|
+
# Chef::Knife::Bootstrap, defined in core.
|
144
|
+
# Required methods have been overridden here
|
145
|
+
#### run() execution begins ####
|
146
|
+
|
147
|
+
def plugin_setup!; end
|
148
|
+
|
149
|
+
def validate_name_args!; end
|
150
|
+
|
151
|
+
# Ensure a valid protocol is provided for target host connection
|
152
|
+
#
|
153
|
+
# The method call will cause the program to exit(1) if:
|
154
|
+
# * Conflicting protocols are given via the target URI and the --protocol option
|
155
|
+
# * The protocol is not a supported protocol
|
156
|
+
#
|
157
|
+
# @note we are overriding this method here to consider "cloud-api" as valid protocol
|
158
|
+
#
|
159
|
+
# @return [TrueClass] If options are valid.
|
160
|
+
def validate_protocol!
|
161
|
+
from_cli = config[:connection_protocol]
|
162
|
+
if from_cli && connection_protocol != from_cli
|
163
|
+
# Hanging indent to align with the ERROR: prefix
|
164
|
+
ui.error <<~EOM
|
165
|
+
The URL '#{host_descriptor}' indicates protocol is '#{connection_protocol}'
|
166
|
+
while the --protocol flag specifies '#{from_cli}'. Please include
|
167
|
+
only one or the other.
|
168
|
+
EOM
|
169
|
+
exit 1
|
170
|
+
end
|
171
|
+
|
172
|
+
unless SUPPORTED_CONNECTION_PROTOCOLS.include?(connection_protocol)
|
173
|
+
ui.error <<~EOM
|
174
|
+
Unsupported protocol '#{connection_protocol}'.
|
175
|
+
|
176
|
+
Supported protocols are: #{SUPPORTED_CONNECTION_PROTOCOLS.join(" ")}
|
177
|
+
EOM
|
178
|
+
exit 1
|
179
|
+
end
|
180
|
+
true
|
181
|
+
end
|
182
|
+
|
183
|
+
def plugin_validate_options!
|
184
|
+
Chef::Log.info("Validating...")
|
185
|
+
validate_asm_keys!(:azure_source_image)
|
186
|
+
validate_params!
|
187
|
+
end
|
188
|
+
|
189
|
+
def plugin_create_instance!
|
190
|
+
Chef::Log.info("Creating...")
|
191
|
+
set_defaults
|
192
|
+
server_def = create_server_def
|
193
|
+
vm_details = service.create_server(server_def)
|
194
|
+
|
195
|
+
wait_until_virtual_machine_ready
|
196
|
+
|
197
|
+
config[:connection_port] = server_def[:port]
|
198
|
+
config[:connection_protocol] = server_def[:connection_protocol]
|
199
|
+
config[:chef_node_name] = locate_config_value(:chef_node_name) || server_name
|
200
|
+
rescue => error
|
201
|
+
ui.error("Something went wrong. Please use -VV option for more details.")
|
202
|
+
Chef::Log.debug(error.backtrace.join("\n").to_s)
|
203
|
+
exit 1
|
204
|
+
end
|
205
|
+
|
206
|
+
def server_name
|
207
|
+
@server_name ||= if @server.nil?
|
208
|
+
nil
|
209
|
+
elsif !@server.hostedservicename.nil?
|
210
|
+
@server.hostedservicename + ".cloudapp.net"
|
211
|
+
else
|
212
|
+
@server.ipaddress
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
216
|
+
alias host_descriptor server_name
|
228
217
|
|
229
|
-
|
230
|
-
:
|
231
|
-
|
218
|
+
def plugin_finalize
|
219
|
+
if locate_config_value(:connection_protocol) == "cloud-api" && locate_config_value(:extended_logs)
|
220
|
+
print "\nWaiting for the first chef-client run"
|
221
|
+
fetch_chef_client_logs(Time.now, 30)
|
222
|
+
end
|
223
|
+
msg_server_summary(@server)
|
224
|
+
end
|
225
|
+
|
226
|
+
#### run() execution ends ####
|
232
227
|
|
233
228
|
def wait_until_virtual_machine_ready(retry_interval_in_seconds = 30)
|
234
229
|
vm_status = nil
|
@@ -245,9 +240,9 @@ class Chef
|
|
245
240
|
end
|
246
241
|
end
|
247
242
|
|
248
|
-
msg_server_summary(
|
243
|
+
msg_server_summary(@server)
|
249
244
|
|
250
|
-
if locate_config_value(:
|
245
|
+
if locate_config_value(:connection_protocol) == "cloud-api"
|
251
246
|
extension_status = wait_for_resource_extension_state(:wagent_provisioning, 5, retry_interval_in_seconds)
|
252
247
|
|
253
248
|
if extension_status != :extension_installing
|
@@ -269,10 +264,10 @@ class Chef
|
|
269
264
|
end
|
270
265
|
|
271
266
|
def wait_for_virtual_machine_state(vm_status_goal, total_wait_time_in_minutes, retry_interval_in_seconds)
|
272
|
-
vm_status_ordering = { :
|
273
|
-
vm_status_description = { :
|
267
|
+
vm_status_ordering = { vm_status_not_detected: 0, vm_status_provisioning: 1, vm_status_ready: 2 }
|
268
|
+
vm_status_description = { vm_status_not_detected: "any", vm_status_provisioning: "provisioning", vm_status_ready: "ready" }
|
274
269
|
|
275
|
-
print ui.color("
|
270
|
+
print ui.color("\nWaiting for virtual machine to reach status '#{vm_status_description[vm_status_goal]}'\n", :magenta)
|
276
271
|
|
277
272
|
total_wait_time_in_seconds = total_wait_time_in_minutes * 60
|
278
273
|
max_polling_attempts = total_wait_time_in_seconds / retry_interval_in_seconds
|
@@ -281,28 +276,28 @@ class Chef
|
|
281
276
|
wait_start_time = Time.now
|
282
277
|
|
283
278
|
begin
|
284
|
-
vm_status = get_virtual_machine_status
|
279
|
+
vm_status = get_virtual_machine_status
|
285
280
|
vm_ready = vm_status_ordering[vm_status] >= vm_status_ordering[vm_status_goal]
|
286
281
|
print "."
|
287
|
-
sleep retry_interval_in_seconds
|
282
|
+
sleep retry_interval_in_seconds unless vm_ready
|
288
283
|
polling_attempts += 1
|
289
284
|
end until vm_ready || polling_attempts >= max_polling_attempts
|
290
285
|
|
291
|
-
|
286
|
+
unless vm_ready
|
292
287
|
raise Chef::Exceptions::CommandTimeout, "Virtual machine state '#{vm_status_description[vm_status_goal]}' not reached after #{total_wait_time_in_minutes} minutes."
|
293
288
|
end
|
294
289
|
|
295
290
|
elapsed_time_in_minutes = ((Time.now - wait_start_time) / 60).round(2)
|
296
|
-
print ui.color("
|
291
|
+
print ui.color("\nvm state '#{vm_status_description[vm_status_goal]}' reached after #{elapsed_time_in_minutes} minutes.\n", :cyan)
|
297
292
|
vm_status
|
298
293
|
end
|
299
294
|
|
300
295
|
def wait_for_resource_extension_state(extension_status_goal, total_wait_time_in_minutes, retry_interval_in_seconds)
|
301
|
-
extension_status_ordering = { :
|
296
|
+
extension_status_ordering = { extension_status_not_detected: 0, wagent_provisioning: 1, extension_installing: 2, extension_provisioning: 3, extension_ready: 4 }
|
302
297
|
|
303
|
-
status_description = { :
|
298
|
+
status_description = { extension_status_not_detected: "any", wagent_provisioning: "wagent provisioning", extension_installing: "installing", extension_provisioning: "provisioning", extension_ready: "ready" }
|
304
299
|
|
305
|
-
print ui.color("
|
300
|
+
print ui.color("\nWaiting for Resource Extension to reach status '#{status_description[extension_status_goal]}'\n", :magenta)
|
306
301
|
|
307
302
|
max_polling_attempts = (total_wait_time_in_minutes * 60) / retry_interval_in_seconds
|
308
303
|
polling_attempts = 0
|
@@ -310,42 +305,44 @@ class Chef
|
|
310
305
|
wait_start_time = Time.now
|
311
306
|
|
312
307
|
begin
|
313
|
-
extension_status = get_extension_status
|
308
|
+
extension_status = get_extension_status
|
314
309
|
extension_ready = extension_status_ordering[extension_status[:status]] >= extension_status_ordering[extension_status_goal]
|
315
310
|
print "."
|
316
|
-
sleep retry_interval_in_seconds
|
311
|
+
sleep retry_interval_in_seconds unless extension_ready
|
317
312
|
polling_attempts += 1
|
318
313
|
end until extension_ready || polling_attempts >= max_polling_attempts
|
319
314
|
|
320
|
-
|
315
|
+
unless extension_ready
|
321
316
|
raise Chef::Exceptions::CommandTimeout, "Resource extension state '#{status_description[extension_status_goal]}' not reached after #{total_wait_time_in_minutes} minutes. #{extension_status[:message]}"
|
322
317
|
end
|
323
318
|
|
324
319
|
elapsed_time_in_minutes = ((Time.now - wait_start_time) / 60).round(2)
|
325
|
-
print ui.color("
|
320
|
+
print ui.color("\nResource extension state '#{status_description[extension_status_goal]}' reached after #{elapsed_time_in_minutes} minutes.\n", :cyan)
|
326
321
|
|
327
322
|
extension_status[:status]
|
328
323
|
end
|
329
324
|
|
330
325
|
def get_virtual_machine_status
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
326
|
+
@server = service.get_role_server(locate_config_value(:azure_dns_name), locate_config_value(:azure_vm_name))
|
327
|
+
if @server.nil?
|
328
|
+
:vm_status_not_detected
|
329
|
+
else
|
330
|
+
Chef::Log.debug("Role status is #{@server.status}")
|
331
|
+
case @server.status.to_s
|
332
|
+
when "ReadyRole"
|
333
|
+
:vm_status_ready
|
334
|
+
when "Provisioning"
|
335
|
+
:vm_status_provisioning
|
338
336
|
else
|
339
|
-
|
337
|
+
:vm_status_not_detected
|
340
338
|
end
|
341
339
|
end
|
342
|
-
:vm_status_not_detected
|
343
340
|
end
|
344
341
|
|
345
342
|
def get_extension_status
|
346
343
|
deployment_name = service.deployment_name(locate_config_value(:azure_dns_name))
|
347
344
|
deployment = service.deployment("hostedservices/#{locate_config_value(:azure_dns_name)}/deployments/#{deployment_name}")
|
348
|
-
extension_status =
|
345
|
+
extension_status = {}
|
349
346
|
|
350
347
|
if deployment.at_css("Deployment Name") != nil
|
351
348
|
role_list_xml = deployment.css("RoleInstanceList RoleInstance")
|
@@ -385,57 +382,34 @@ class Chef
|
|
385
382
|
extension_status
|
386
383
|
end
|
387
384
|
|
388
|
-
def run
|
389
|
-
$stdout.sync = true
|
390
|
-
storage = nil
|
391
|
-
Chef::Log.info("validating...")
|
392
|
-
validate_asm_keys!(:azure_source_image)
|
393
|
-
validate_params!
|
394
|
-
ssh_override_winrm if !is_image_windows?
|
395
|
-
Chef::Log.info("creating...")
|
396
|
-
config[:azure_dns_name] = get_dns_name(locate_config_value(:azure_dns_name))
|
397
|
-
config[:azure_vm_name] = locate_config_value(:azure_dns_name) unless locate_config_value(:azure_vm_name)
|
398
|
-
config[:chef_node_name] = locate_config_value(:azure_vm_name) unless locate_config_value(:chef_node_name)
|
399
|
-
service.create_server(create_server_def)
|
400
|
-
wait_until_virtual_machine_ready()
|
401
|
-
if locate_config_value(:bootstrap_protocol) == "cloud-api" && locate_config_value(:extended_logs)
|
402
|
-
print "\n\nWaiting for the first chef-client run"
|
403
|
-
fetch_chef_client_logs(Time.now, 30)
|
404
|
-
end
|
405
|
-
server = service.get_role_server(locate_config_value(:azure_dns_name), locate_config_value(:azure_vm_name))
|
406
|
-
msg_server_summary(server)
|
407
|
-
|
408
|
-
bootstrap_exec(server) unless locate_config_value(:bootstrap_protocol) == "cloud-api"
|
409
|
-
end
|
410
|
-
|
411
385
|
def create_server_def
|
412
386
|
server_def = {
|
413
|
-
:
|
414
|
-
:
|
415
|
-
:
|
416
|
-
:
|
417
|
-
:
|
418
|
-
:
|
419
|
-
:
|
420
|
-
:
|
421
|
-
:
|
422
|
-
:
|
423
|
-
:
|
424
|
-
:
|
425
|
-
:
|
426
|
-
:
|
427
|
-
:
|
428
|
-
:
|
429
|
-
:
|
430
|
-
:
|
431
|
-
:
|
432
|
-
:
|
433
|
-
:
|
434
|
-
:
|
435
|
-
:
|
387
|
+
azure_storage_account: locate_config_value(:azure_storage_account),
|
388
|
+
azure_api_host_name: locate_config_value(:azure_api_host_name),
|
389
|
+
azure_dns_name: locate_config_value(:azure_dns_name),
|
390
|
+
azure_vm_name: locate_config_value(:azure_vm_name),
|
391
|
+
azure_service_location: locate_config_value(:azure_service_location),
|
392
|
+
azure_os_disk_name: locate_config_value(:azure_os_disk_name),
|
393
|
+
azure_source_image: locate_config_value(:azure_source_image),
|
394
|
+
azure_vm_size: locate_config_value(:azure_vm_size),
|
395
|
+
tcp_endpoints: locate_config_value(:tcp_endpoints),
|
396
|
+
udp_endpoints: locate_config_value(:udp_endpoints),
|
397
|
+
connection_protocol: locate_config_value(:connection_protocol),
|
398
|
+
azure_connect_to_existing_dns: locate_config_value(:azure_connect_to_existing_dns),
|
399
|
+
connection_user: locate_config_value(:connection_user),
|
400
|
+
azure_availability_set: locate_config_value(:azure_availability_set),
|
401
|
+
azure_affinity_group: locate_config_value(:azure_affinity_group),
|
402
|
+
azure_network_name: locate_config_value(:azure_network_name),
|
403
|
+
azure_subnet_name: locate_config_value(:azure_subnet_name),
|
404
|
+
ssl_cert_fingerprint: locate_config_value(:thumbprint),
|
405
|
+
cert_path: locate_config_value(:cert_path),
|
406
|
+
cert_password: locate_config_value(:cert_passphrase),
|
407
|
+
winrm_ssl: locate_config_value(:winrm_ssl),
|
408
|
+
winrm_max_timeout: locate_config_value(:winrm_max_timeout).to_i * 60 * 1000, # converting minutes to milliseconds
|
409
|
+
winrm_max_memory_per_shell: locate_config_value(:winrm_max_memory_per_shell),
|
436
410
|
}
|
437
411
|
|
438
|
-
if locate_config_value(:
|
412
|
+
if locate_config_value(:connection_protocol) == "cloud-api"
|
439
413
|
server_def[:chef_extension] = get_chef_extension_name
|
440
414
|
server_def[:chef_extension_publisher] = get_chef_extension_publisher
|
441
415
|
server_def[:chef_extension_version] = get_chef_extension_version
|
@@ -443,28 +417,23 @@ class Chef
|
|
443
417
|
server_def[:chef_extension_private_param] = get_chef_extension_private_params
|
444
418
|
else
|
445
419
|
if is_image_windows?
|
446
|
-
if (not locate_config_value(:winrm_password)) || (not locate_config_value(:bootstrap_protocol))
|
447
|
-
ui.error("WinRM Password and Bootstrapping Protocol are compulsory parameters")
|
448
|
-
exit 1
|
449
|
-
end
|
450
420
|
# We can specify the AdminUsername after API version 2013-03-01. However, in this API version,
|
451
421
|
# the AdminUsername is a required parameter.
|
452
422
|
# Also, the user name cannot be Administrator, Admin, Admin1 etc, for enhanced security (provided by Azure)
|
453
|
-
if locate_config_value(:
|
454
|
-
ui.error("
|
423
|
+
if locate_config_value(:connection_user).nil? || locate_config_value(:connection_user).downcase =~ /admin*/
|
424
|
+
ui.error("Connection User is compulsory parameter and it cannot be named 'admin*'")
|
455
425
|
exit 1
|
456
|
-
end
|
457
426
|
# take cares of when user name contains domain
|
458
427
|
# azure add role api doesn't support '\\' in user name
|
459
|
-
|
460
|
-
server_def[:
|
428
|
+
elsif locate_config_value(:connection_user).split('\\').length.eql?(2)
|
429
|
+
server_def[:connection_user] = locate_config_value(:connection_user).split('\\')[1]
|
461
430
|
end
|
462
431
|
else
|
463
|
-
|
464
|
-
ui.error("
|
432
|
+
unless locate_config_value(:connection_user)
|
433
|
+
ui.error("Connection User is compulsory parameter")
|
465
434
|
exit 1
|
466
435
|
end
|
467
|
-
unless locate_config_value(:
|
436
|
+
unless locate_config_value(:connection_password) || locate_config_value(:ssh_identity_file)
|
468
437
|
ui.error("Specify either SSH Key or SSH Password")
|
469
438
|
exit 1
|
470
439
|
end
|
@@ -473,24 +442,24 @@ class Chef
|
|
473
442
|
|
474
443
|
if is_image_windows?
|
475
444
|
server_def[:os_type] = "Windows"
|
476
|
-
server_def[:admin_password] = locate_config_value(:
|
477
|
-
server_def[:
|
445
|
+
server_def[:admin_password] = locate_config_value(:connection_password)
|
446
|
+
server_def[:connection_protocol] = locate_config_value(:connection_protocol) || "winrm"
|
478
447
|
else
|
479
448
|
server_def[:os_type] = "Linux"
|
480
|
-
server_def[:
|
481
|
-
server_def[:
|
482
|
-
server_def[:
|
483
|
-
server_def[:
|
449
|
+
server_def[:connection_protocol] = locate_config_value(:connection_protocol).nil? || locate_config_value(:connection_protocol) == "winrm" ? "ssh" : locate_config_value(:connection_protocol)
|
450
|
+
server_def[:connection_user] = locate_config_value(:connection_user)
|
451
|
+
server_def[:connection_password] = locate_config_value(:connection_password)
|
452
|
+
server_def[:ssh_identity_file] = locate_config_value(:ssh_identity_file)
|
484
453
|
server_def[:identity_file_passphrase] = locate_config_value(:identity_file_passphrase)
|
485
454
|
end
|
486
455
|
|
487
456
|
azure_connect_to_existing_dns = locate_config_value(:azure_connect_to_existing_dns)
|
488
|
-
if is_image_windows? && server_def[:
|
489
|
-
port = locate_config_value(:
|
490
|
-
port = locate_config_value(:
|
491
|
-
elsif server_def[:
|
492
|
-
port = locate_config_value(:
|
493
|
-
port = locate_config_value(:
|
457
|
+
if is_image_windows? && server_def[:connection_protocol] == "winrm"
|
458
|
+
port = locate_config_value(:connection_port) || "5985"
|
459
|
+
port = locate_config_value(:connection_port) || Random.rand(64000) + 1000 if azure_connect_to_existing_dns
|
460
|
+
elsif server_def[:connection_protocol] == "ssh"
|
461
|
+
port = locate_config_value(:connection_port) || "22"
|
462
|
+
port = locate_config_value(:connection_port) || Random.rand(64000) + 1000 if azure_connect_to_existing_dns
|
494
463
|
end
|
495
464
|
|
496
465
|
server_def[:port] = port
|
@@ -525,27 +494,22 @@ class Chef
|
|
525
494
|
|
526
495
|
private
|
527
496
|
|
528
|
-
def
|
529
|
-
|
530
|
-
|
531
|
-
|
532
|
-
|
533
|
-
|
534
|
-
|
535
|
-
if locate_config_value(:ssh_port).eql?(options[:ssh_port][:default]) &&
|
536
|
-
!locate_config_value(:winrm_port).eql?(options[:winrm_port][:default])
|
537
|
-
config[:ssh_port] = locate_config_value(:winrm_port)
|
538
|
-
end
|
539
|
-
# unset ssh_password and set winrm_password, override ssh_password
|
540
|
-
if locate_config_value(:ssh_password).nil? &&
|
541
|
-
!locate_config_value(:winrm_password).nil?
|
542
|
-
config[:ssh_password] = locate_config_value(:winrm_password)
|
497
|
+
def set_defaults
|
498
|
+
set_configs
|
499
|
+
end
|
500
|
+
|
501
|
+
def set_configs
|
502
|
+
unless locate_config_value(:connection_user).nil?
|
503
|
+
config[:connection_user] = locate_config_value(:connection_user)
|
543
504
|
end
|
544
|
-
|
545
|
-
|
546
|
-
|
547
|
-
config[:identity_file] = locate_config_value(:kerberos_keytab_file)
|
505
|
+
|
506
|
+
unless locate_config_value(:connection_password).nil?
|
507
|
+
config[:connection_password] = locate_config_value(:connection_password)
|
548
508
|
end
|
509
|
+
|
510
|
+
config[:azure_dns_name] = get_dns_name(locate_config_value(:azure_dns_name))
|
511
|
+
config[:azure_vm_name] = locate_config_value(:azure_dns_name) unless locate_config_value(:azure_vm_name)
|
512
|
+
config[:chef_node_name] = locate_config_value(:azure_vm_name) unless locate_config_value(:chef_node_name)
|
549
513
|
end
|
550
514
|
|
551
515
|
# This is related to Windows VM's specifically and computer name
|
@@ -555,10 +519,11 @@ class Chef
|
|
555
519
|
# generate a random dns_name if azure_dns_name is empty
|
556
520
|
def get_dns_name(azure_dns_name, prefix = "az-")
|
557
521
|
return azure_dns_name unless azure_dns_name.nil?
|
522
|
+
|
558
523
|
if locate_config_value(:azure_vm_name).nil?
|
559
|
-
|
524
|
+
(prefix + SecureRandom.hex((MAX_VM_NAME_CHARACTERS - prefix.length) / 2))
|
560
525
|
else
|
561
|
-
|
526
|
+
locate_config_value(:azure_vm_name)
|
562
527
|
end
|
563
528
|
end
|
564
529
|
end
|