knife-azure 1.5.1.rc.1 → 1.5.1.rc.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +28 -0
- data/lib/azure/connection.rb +6 -4
- data/lib/azure/deploy.rb +36 -1
- data/lib/azure/host.rb +1 -1
- data/lib/azure/loadbalancer.rb +92 -0
- data/lib/azure/rest.rb +1 -1
- data/lib/azure/role.rb +114 -35
- data/lib/chef/knife/azure_internal-lb_create.rb +77 -0
- data/lib/chef/knife/azure_internal-lb_list.rb +51 -0
- data/lib/chef/knife/azure_server_create.rb +206 -45
- data/lib/knife-azure/version.rb +1 -1
- metadata +17 -14
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 77a080a603467387d888d6063ecd0ac4602ab8b4
|
4
|
+
data.tar.gz: 91e38b9592db815f0119cc346e0e169713719df4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c796c6ce3df502f4825a3c516201cac9603854f149dace38cc771c1b7264c41738a7a7eed5356b16c61c6152207c7659b999f5514a04f64636ea2c4fa2e3f392
|
7
|
+
data.tar.gz: 56f16f843a22a121c2fba17567e8baf886978057a7b219fd651d3b8d0408a2bf280c33b09841fdc966880d7f8b3c1d5441e9a717190bc304613149e12bc4f4b0
|
data/README.md
CHANGED
@@ -189,6 +189,20 @@ These options may also be configured from knife.rb, as in this example:
|
|
189
189
|
knife[:tcp-endpoints]='80:80,3389:5678'
|
190
190
|
knife[:udp-endpoints]='123:123'
|
191
191
|
|
192
|
+
#### Endpoint configuration
|
193
|
+
|
194
|
+
Endpoints are configured using tcp-endpoints and udp-endpoints. This is a string in the form:
|
195
|
+
{localPort}:{publicPort}:{load_balancer_set_name}:{load_balancer_probe_path}
|
196
|
+
|
197
|
+
Examples:
|
198
|
+
|
199
|
+
knife[:tcp-endpoints]='80' # Allow Port 80 inbound
|
200
|
+
knife[:tcp-endpoints]='80:8080' # Allow Port 80 inbound and map it to local port 8080
|
201
|
+
knife[:tcp-endpoints]='80:8080:web-set' # Allow Port 80 and add it to the load balancing set called 'web-set'
|
202
|
+
knife[:tcp-endpoints]='80:8080:web-set:/healthcheck' # Allow Port 80, add it to the load balancing set, and use an HTTP probe at path "/healthcheck"
|
203
|
+
|
204
|
+
Note that the load balancing set will be created if it does not exist. If it exists within another VM in the cloud service, it will re-use those values for the probe.
|
205
|
+
|
192
206
|
#### Options for Bootstrapping a Windows Node in Azure
|
193
207
|
|
194
208
|
:bootstrap_protocol Default is winrm for a windows image
|
@@ -294,6 +308,20 @@ Knife options:
|
|
294
308
|
:azure_service_location Specifies the geographic location.
|
295
309
|
:azure_ag_desc Optional. Description for new affinity group.
|
296
310
|
|
311
|
+
### Azure Internal LB List Subcommand
|
312
|
+
Outputs a lit of defined load balancers for all cloud services. Public facing load balancers are not shown here.
|
313
|
+
|
314
|
+
### Azure Internal LB Create Subcommand
|
315
|
+
Creates a new Internal Load Balancer within a cloud service.
|
316
|
+
|
317
|
+
knife azure internal lb create -n 'my_lb' --azure-lb-static-vip '10.0.0.123' --azure-subnet_name 'Subnet_1' --azure-dns-name 'service_name'
|
318
|
+
|
319
|
+
Knife options:
|
320
|
+
:azure_load_balancer Required. Specifies the name of the Load Balancer.
|
321
|
+
:azure_lb_static_vip Optional. Allows you to set a static IP for the VIP.
|
322
|
+
:azure_subnet_name Required ONLY IF azure_lb_static_ip is set. Specifies the subnet that the static IP resides in.
|
323
|
+
:azure_dns_name Required. The cloud service that this internal Load Balancer will be added to.
|
324
|
+
|
297
325
|
### Azure Vnet List Subcommand
|
298
326
|
Outputs a list of defined virtual networks in the azure subscription.
|
299
327
|
|
data/lib/azure/connection.rb
CHANGED
@@ -25,6 +25,7 @@ require File.expand_path('../disk', __FILE__)
|
|
25
25
|
require File.expand_path('../image', __FILE__)
|
26
26
|
require File.expand_path('../certificate', __FILE__)
|
27
27
|
require File.expand_path('../ag', __FILE__)
|
28
|
+
require File.expand_path('../loadbalancer', __FILE__)
|
28
29
|
require File.expand_path('../vnet', __FILE__)
|
29
30
|
|
30
31
|
class Azure
|
@@ -32,7 +33,7 @@ class Azure
|
|
32
33
|
include AzureAPI
|
33
34
|
include AzureUtility
|
34
35
|
attr_accessor :hosts, :rest, :images, :deploys, :roles,
|
35
|
-
:disks, :storageaccounts, :certificates, :ags, :vnets
|
36
|
+
:disks, :storageaccounts, :certificates, :ags, :vnets, :lbs
|
36
37
|
def initialize(params={})
|
37
38
|
@rest = Rest.new(params)
|
38
39
|
@hosts = Hosts.new(self)
|
@@ -44,6 +45,7 @@ class Azure
|
|
44
45
|
@certificates = Certificates.new(self)
|
45
46
|
@ags = AGs.new(self)
|
46
47
|
@vnets = Vnets.new(self)
|
48
|
+
@lbs = Loadbalancers.new(self)
|
47
49
|
end
|
48
50
|
|
49
51
|
def query_azure(service_name,
|
@@ -67,7 +69,7 @@ class Azure
|
|
67
69
|
ret_val = Nokogiri::XML response.body
|
68
70
|
Chef::Log.debug ret_val.to_xml
|
69
71
|
error_code, error_message = error_from_response_xml(ret_val)
|
70
|
-
Chef::Log.
|
72
|
+
Chef::Log.debug error_code + ' : ' + error_message if error_code.length > 0
|
71
73
|
else
|
72
74
|
Chef::Log.warn 'http error: ' + response.code
|
73
75
|
end
|
@@ -78,7 +80,7 @@ class Azure
|
|
78
80
|
status = 'InProgress'
|
79
81
|
Chef::Log.info 'Waiting while status returns InProgress'
|
80
82
|
while status == 'InProgress'
|
81
|
-
response = @rest.query_for_completion()
|
83
|
+
response = @rest.query_for_completion()
|
82
84
|
ret_val = Nokogiri::XML response.body
|
83
85
|
status = xml_content(ret_val,'Status')
|
84
86
|
if status == 'InProgress'
|
@@ -88,7 +90,7 @@ class Azure
|
|
88
90
|
Chef::Log.debug 'not InProgress : ' + ret_val.to_xml
|
89
91
|
else
|
90
92
|
error_code, error_message = error_from_response_xml(ret_val)
|
91
|
-
Chef::Log.
|
93
|
+
Chef::Log.debug status + error_code + ' : ' + error_message if error_code.length > 0
|
92
94
|
end
|
93
95
|
end
|
94
96
|
ret_val
|
data/lib/azure/deploy.rb
CHANGED
@@ -116,7 +116,7 @@ class Azure
|
|
116
116
|
|
117
117
|
class Deploy
|
118
118
|
include AzureUtility
|
119
|
-
attr_accessor :connection, :name, :status, :url, :hostedservicename
|
119
|
+
attr_accessor :connection, :name, :status, :url, :hostedservicename, :input_endpoints, :loadbalancers
|
120
120
|
|
121
121
|
def initialize(connection)
|
122
122
|
@connection = connection
|
@@ -135,8 +135,21 @@ class Azure
|
|
135
135
|
role.parse(roleXML, hostedservicename, @name)
|
136
136
|
@roles[role.name] = role
|
137
137
|
end
|
138
|
+
@input_endpoints = Array.new
|
139
|
+
endpointsXML = deployXML.css('InputEndpoint')
|
140
|
+
endpointsXML.each do |endpointXML|
|
141
|
+
@input_endpoints << parse_endpoint(endpointXML)
|
142
|
+
end
|
143
|
+
@loadbalancers = Hash.new
|
144
|
+
lbsXML = deployXML.css('Deployment LoadBalancers LoadBalancer')
|
145
|
+
lbsXML.each do |lbXML|
|
146
|
+
loadbalancer = Loadbalancer.new(@connection)
|
147
|
+
loadbalancer.parse(lbXML, hostedservicename)
|
148
|
+
@loadbalancers[loadbalancer.name] = loadbalancer
|
149
|
+
end
|
138
150
|
end
|
139
151
|
end
|
152
|
+
|
140
153
|
def setup(params)
|
141
154
|
role = Role.new(@connection)
|
142
155
|
roleXML = role.setup(params)
|
@@ -158,11 +171,33 @@ class Azure
|
|
158
171
|
builder.doc.at_css('Role') << roleXML.at_css('PersistentVMRole').children.to_s
|
159
172
|
builder.doc
|
160
173
|
end
|
174
|
+
|
161
175
|
def create(params, deployXML)
|
162
176
|
servicecall = "hostedservices/#{params[:azure_dns_name]}/deployments"
|
163
177
|
@connection.query_azure(servicecall, "post", deployXML.to_xml)
|
164
178
|
end
|
165
179
|
|
180
|
+
# This parses endpoints from a RoleList-Role-InputEndpoint, NOT a RoleInstanceList-RoleInstance-InstanceEndpoint
|
181
|
+
# Refactor: make this an object rather than a hash..?
|
182
|
+
def parse_endpoint(inputendpoint_xml)
|
183
|
+
hash = Hash.new
|
184
|
+
%w{LoadBalancedEndpointSetName LocalPort Name Port Protocol EnableDirectServerReturn LoadBalancerName IdleTimeoutInMinutes}.each do |key|
|
185
|
+
hash[key] = xml_content(inputendpoint_xml, key, nil)
|
186
|
+
end
|
187
|
+
# Protocol could be in there twice... If we have two, pick the second one as the first is for the probe.
|
188
|
+
if inputendpoint_xml.css('Protocol').count > 1
|
189
|
+
hash['Protocol'] = inputendpoint_xml.css('Protocol')[1].content
|
190
|
+
end
|
191
|
+
probe = inputendpoint_xml.css('LoadBalancerProbe')
|
192
|
+
if probe
|
193
|
+
hash['LoadBalancerProbe'] = Hash.new
|
194
|
+
%w{Path Port Protocol IntervalInSeconds TimeoutInSeconds}.each do |key|
|
195
|
+
hash['LoadBalancerProbe'][key] = xml_content(probe, key, nil)
|
196
|
+
end
|
197
|
+
end
|
198
|
+
hash
|
199
|
+
end
|
200
|
+
|
166
201
|
def roles
|
167
202
|
@roles.values if @roles
|
168
203
|
end
|
data/lib/azure/host.rb
CHANGED
@@ -56,7 +56,7 @@ class Azure
|
|
56
56
|
ret_val = @connection.query_azure("hostedservices/#{name}")
|
57
57
|
error_code, error_message = error_from_response_xml(ret_val) if ret_val
|
58
58
|
if ret_val.nil? || error_code.length > 0
|
59
|
-
Chef::Log.
|
59
|
+
Chef::Log.debug('Unable to find hosted(cloud) service:' + error_code + ' : ' + error_message) if ret_val
|
60
60
|
false
|
61
61
|
else
|
62
62
|
true
|
@@ -0,0 +1,92 @@
|
|
1
|
+
#
|
2
|
+
# Author:: Aiman Alsari (aiman.alsari@gmail.com)
|
3
|
+
# Copyright:: Copyright (c) 2013 Opscode, Inc.
|
4
|
+
# License:: Apache License, Version 2.0
|
5
|
+
#
|
6
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
7
|
+
# you may not use this file except in compliance with the License.
|
8
|
+
# You may obtain a copy of the License at
|
9
|
+
#
|
10
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
11
|
+
#
|
12
|
+
# Unless required by applicable law or agreed to in writing, software
|
13
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
14
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
15
|
+
# See the License for the specific language governing permissions and
|
16
|
+
# limitations under the License.
|
17
|
+
#
|
18
|
+
|
19
|
+
class Azure
|
20
|
+
class Loadbalancers
|
21
|
+
|
22
|
+
def initialize(connection)
|
23
|
+
@connection = connection
|
24
|
+
end
|
25
|
+
|
26
|
+
def load
|
27
|
+
@lbs ||= begin
|
28
|
+
@lbs = Hash.new
|
29
|
+
@connection.deploys.all.each do |deploy|
|
30
|
+
@lbs.merge!(deploy.loadbalancers)
|
31
|
+
end
|
32
|
+
@lbs
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def all
|
37
|
+
load.values
|
38
|
+
end
|
39
|
+
|
40
|
+
def exists?(name)
|
41
|
+
load.key?(name)
|
42
|
+
end
|
43
|
+
|
44
|
+
def find(name)
|
45
|
+
load[name]
|
46
|
+
end
|
47
|
+
|
48
|
+
def create(params)
|
49
|
+
lb = Loadbalancer.new(@connection)
|
50
|
+
lb.create(params)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
class Azure
|
56
|
+
class Loadbalancer
|
57
|
+
include AzureUtility
|
58
|
+
attr_accessor :name, :service, :subnet, :vip
|
59
|
+
|
60
|
+
def initialize(connection)
|
61
|
+
@connection = connection
|
62
|
+
end
|
63
|
+
|
64
|
+
def parse(lbXML, hostedservicename)
|
65
|
+
@name = xml_content(lbXML, 'Name')
|
66
|
+
ip_configXML = lbXML.css('FrontendIpConfiguration')
|
67
|
+
@subnet = xml_content(ip_configXML, 'SubnetName')
|
68
|
+
@vip = xml_content(ip_configXML, 'StaticVirtualNetworkIPAddress')
|
69
|
+
@service = hostedservicename
|
70
|
+
self
|
71
|
+
end
|
72
|
+
|
73
|
+
def create(params)
|
74
|
+
if params[:azure_lb_static_vip] && !params[:azure_subnet_name]
|
75
|
+
Chef::Log.fatal 'Unable to create Loadbalancer, :azure_subnet_name needs to be set if :azure_lb_static_vip is set'
|
76
|
+
end
|
77
|
+
builder = Nokogiri::XML::Builder.new(encoding: 'utf-8') do |xml|
|
78
|
+
xml.LoadBalancer(xmlns: 'http://schemas.microsoft.com/windowsazure') {
|
79
|
+
xml.Name params[:azure_load_balancer]
|
80
|
+
xml.FrontendIpConfiguration {
|
81
|
+
xml.Type 'Private'
|
82
|
+
xml.SubnetName params[:azure_subnet_name] if params[:azure_subnet_name]
|
83
|
+
xml.StaticVirtualNetworkIPAddress params[:azure_lb_static_vip] if params[:azure_lb_static_vip]
|
84
|
+
}
|
85
|
+
}
|
86
|
+
end
|
87
|
+
deploy_name = @connection.deploys.get_deploy_name_for_hostedservice(params[:azure_dns_name])
|
88
|
+
servicecall = "hostedservices/#{params[:azure_dns_name]}/deployments/#{deploy_name}/loadbalancers"
|
89
|
+
@connection.query_azure(servicecall, "post", builder.doc.to_xml)
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
data/lib/azure/rest.rb
CHANGED
@@ -97,7 +97,7 @@ module AzureAPI
|
|
97
97
|
request = Net::HTTP::Put.new(uri.request_uri)
|
98
98
|
end
|
99
99
|
text = verb == 'put'
|
100
|
-
request["x-ms-version"] = "2014-
|
100
|
+
request["x-ms-version"] = "2014-05-01"
|
101
101
|
request["content-type"] = text ? "text/plain" : "application/xml"
|
102
102
|
request["accept"] = "application/xml"
|
103
103
|
request["accept-charset"] = "utf-8"
|
data/lib/azure/role.rb
CHANGED
@@ -176,9 +176,28 @@ class Azure
|
|
176
176
|
attr_accessor :winrmport
|
177
177
|
attr_accessor :hostname, :tcpports, :udpports
|
178
178
|
|
179
|
+
TCP_ENDPOINTS_MAPPING = { '3389' => 'Remote Desktop',
|
180
|
+
'5986' => 'PowerShell',
|
181
|
+
'22' => 'SSH',
|
182
|
+
'21' => 'FTP',
|
183
|
+
'25' => 'SMTP',
|
184
|
+
'53' => 'DNS',
|
185
|
+
'80' => 'HTTP',
|
186
|
+
'110' => 'POP3',
|
187
|
+
'143' => 'IMAP',
|
188
|
+
'389' => 'LDAP',
|
189
|
+
'443' => 'HTTPs',
|
190
|
+
'587' => 'SMTPS',
|
191
|
+
'995' => 'POP3S',
|
192
|
+
'993' => 'IMAPS',
|
193
|
+
'1433' => 'MSSQL',
|
194
|
+
'3306' => 'MySQL'
|
195
|
+
}
|
196
|
+
|
179
197
|
def initialize(connection)
|
180
198
|
@connection = connection
|
181
199
|
end
|
200
|
+
|
182
201
|
def parse(roleXML, hostedservicename, deployname)
|
183
202
|
@name = xml_content(roleXML, 'RoleName')
|
184
203
|
@status = xml_content(roleXML, 'InstanceStatus')
|
@@ -204,6 +223,7 @@ class Azure
|
|
204
223
|
hash['Vip'] = xml_content(endpoint, 'Vip')
|
205
224
|
hash['PublicPort'] = xml_content(endpoint, 'PublicPort')
|
206
225
|
hash['LocalPort'] = xml_content(endpoint, 'LocalPort')
|
226
|
+
|
207
227
|
if xml_content(endpoint, 'Protocol') == 'tcp'
|
208
228
|
@tcpports << hash
|
209
229
|
else # == 'udp'
|
@@ -213,6 +233,72 @@ class Azure
|
|
213
233
|
end
|
214
234
|
end
|
215
235
|
|
236
|
+
# Expects endpoint_param_string to be in the form {localport}:{publicport}:{lb_set_name}:{lb_probe_path}
|
237
|
+
# Only localport is mandatory.
|
238
|
+
def parse_endpoint_from_params(protocol, azure_vm_name, endpoint_param_string)
|
239
|
+
fields = endpoint_param_string.split(':').map(&:strip)
|
240
|
+
hash = {}
|
241
|
+
hash['LocalPort'] = fields[0]
|
242
|
+
hash['Port'] = fields[1] || fields[0]
|
243
|
+
hash['LoadBalancerName'] = fields[2] if fields[2] != 'EXTERNAL' # TODO: hackity hack.. Shouldn't use magic words.
|
244
|
+
hash['LoadBalancedEndpointSetName'] = fields[3]
|
245
|
+
hash['Protocol'] = protocol
|
246
|
+
if TCP_ENDPOINTS_MAPPING.include?(hash['Port']) && protocol == 'TCP'
|
247
|
+
hash['Name'] = TCP_ENDPOINTS_MAPPING[hash['Port']]
|
248
|
+
else
|
249
|
+
hash['Name'] = "#{protocol}Endpoint_chef_#{fields[0]}"
|
250
|
+
end
|
251
|
+
if fields[2]
|
252
|
+
hash['LoadBalancerProbe'] = {}
|
253
|
+
hash['LoadBalancerProbe']['Path'] = fields[4]
|
254
|
+
hash['LoadBalancerProbe']['Port'] = fields[0]
|
255
|
+
hash['LoadBalancerProbe']['Protocol'] = fields[4] ? 'HTTP' : protocol
|
256
|
+
end
|
257
|
+
hash
|
258
|
+
end
|
259
|
+
|
260
|
+
def find_deploy(params)
|
261
|
+
@connection.hosts.find(params[:azure_dns_name]).deploys[0] # TODO this relies on the 'production only' bug.
|
262
|
+
end
|
263
|
+
|
264
|
+
def add_endpoints_to_xml(xml, endpoints, params)
|
265
|
+
existing_endpoints = find_deploy(params).input_endpoints
|
266
|
+
|
267
|
+
endpoints.each do |ep|
|
268
|
+
|
269
|
+
if existing_endpoints
|
270
|
+
existing_endpoints.each do |eep|
|
271
|
+
ep = eep if eep['LoadBalancedEndpointSetName'] && ep['LoadBalancedEndpointSetName'] && ( eep['LoadBalancedEndpointSetName'] == ep['LoadBalancedEndpointSetName'] )
|
272
|
+
end
|
273
|
+
end
|
274
|
+
|
275
|
+
if ep['Port'] == params[:port] && ep['Protocol'].downcase == 'tcp'
|
276
|
+
puts("Skipping tcp-endpoints: #{ep['LocalPort']} because this port is already in use by ssh/winrm endpoint in current VM.")
|
277
|
+
next
|
278
|
+
end
|
279
|
+
|
280
|
+
xml.InputEndpoint {
|
281
|
+
xml.LoadBalancedEndpointSetName ep['LoadBalancedEndpointSetName'] if ep['LoadBalancedEndpointSetName']
|
282
|
+
xml.LocalPort ep['LocalPort']
|
283
|
+
xml.Name ep['Name']
|
284
|
+
xml.Port ep['Port']
|
285
|
+
if ep['LoadBalancerProbe']
|
286
|
+
xml.LoadBalancerProbe {
|
287
|
+
xml.Path ep['LoadBalancerProbe']['Path'] if ep['LoadBalancerProbe']['Path']
|
288
|
+
xml.Port ep['LoadBalancerProbe']['Port']
|
289
|
+
xml.Protocol ep['LoadBalancerProbe']['Protocol']
|
290
|
+
xml.IntervalInSeconds ep['LoadBalancerProbe']['IntervalInSeconds'] if ep['LoadBalancerProbe']['IntervalInSeconds']
|
291
|
+
xml.TimeoutInSeconds ep['LoadBalancerProbe']['TimeoutInSeconds'] if ep['LoadBalancerProbe']['TimeoutInSeconds']
|
292
|
+
}
|
293
|
+
end
|
294
|
+
xml.Protocol ep['Protocol']
|
295
|
+
xml.EnableDirectServerReturn ep['EnableDirectServerReturn'] if ep['EnableDirectServerReturn']
|
296
|
+
xml.LoadBalancerName ep['LoadBalancerName'] if ep['LoadBalancerName']
|
297
|
+
xml.IdleTimeoutInMinutes ep['IdleTimeoutInMinutes'] if ep['IdleTimeoutInMinutes']
|
298
|
+
}
|
299
|
+
end
|
300
|
+
end
|
301
|
+
|
216
302
|
def fetch_thumbprint
|
217
303
|
query_result = connection.query_azure("hostedservices/#{@hostedservicename}/deployments/#{@hostedservicename}/roles/#{@name}")
|
218
304
|
query_result.at_css("DefaultWinRmCertificateThumbprint").nil? ? '' : query_result.at_css("DefaultWinRmCertificateThumbprint").text
|
@@ -227,7 +313,6 @@ class Azure
|
|
227
313
|
xml.RoleName {xml.text params[:azure_vm_name]}
|
228
314
|
xml.OsVersion('i:nil' => 'true')
|
229
315
|
xml.RoleType 'PersistentVMRole'
|
230
|
-
xml.VMImageName params[:azure_source_image] if params[:is_vm_image]
|
231
316
|
|
232
317
|
xml.ConfigurationSets {
|
233
318
|
if params[:os_type] == 'Linux'
|
@@ -257,6 +342,17 @@ class Azure
|
|
257
342
|
xml.AdminPassword params[:admin_password]
|
258
343
|
xml.ResetPasswordOnFirstLogon 'false'
|
259
344
|
xml.EnableAutomaticUpdates 'false'
|
345
|
+
if params[:azure_domain_name]
|
346
|
+
xml.DomainJoin {
|
347
|
+
xml.Credentials {
|
348
|
+
xml.Domain params[:azure_domain_name]
|
349
|
+
xml.Username params[:azure_domain_user]
|
350
|
+
xml.Password params[:azure_domain_passwd]
|
351
|
+
}
|
352
|
+
xml.JoinDomain params[:azure_domain_name]
|
353
|
+
xml.MachineObjectOU params[:azure_domain_ou_dn] if params[:azure_domain_ou_dn]
|
354
|
+
}
|
355
|
+
end
|
260
356
|
if params[:bootstrap_proto].downcase == 'winrm'
|
261
357
|
if params[:ssl_cert_fingerprint]
|
262
358
|
xml.StoredCertificateSettings {
|
@@ -269,9 +365,9 @@ class Azure
|
|
269
365
|
end
|
270
366
|
xml.WinRM {
|
271
367
|
xml.Listeners {
|
272
|
-
if params[:ssl_cert_fingerprint]
|
368
|
+
if params[:winrm_transport] == "ssl" || params[:ssl_cert_fingerprint]
|
273
369
|
xml.Listener {
|
274
|
-
xml.CertificateThumbprint params[:ssl_cert_fingerprint]
|
370
|
+
xml.CertificateThumbprint params[:ssl_cert_fingerprint] if params[:ssl_cert_fingerprint]
|
275
371
|
xml.Protocol 'Https'
|
276
372
|
}
|
277
373
|
else
|
@@ -346,6 +442,9 @@ class Azure
|
|
346
442
|
xml.ConfigurationSetType 'NetworkConfiguration'
|
347
443
|
xml.InputEndpoints {
|
348
444
|
|
445
|
+
#1. bootstrap_proto = 'winrm' for windows => Set winrm port
|
446
|
+
#2. bootstrap_proto = 'ssh' for windows and linux => Set ssh port
|
447
|
+
#3. bootstrap_proto = 'cloud-api' for windows and linux => Set no port
|
349
448
|
if params[:os_type] == 'Windows' and params[:bootstrap_proto].downcase == 'winrm'
|
350
449
|
xml.InputEndpoint {
|
351
450
|
if params[:winrm_transport] == "ssl"
|
@@ -357,7 +456,7 @@ class Azure
|
|
357
456
|
xml.Port params[:port]
|
358
457
|
xml.Protocol 'TCP'
|
359
458
|
}
|
360
|
-
elsif
|
459
|
+
elsif(params[:bootstrap_proto].downcase == 'ssh')
|
361
460
|
xml.InputEndpoint {
|
362
461
|
xml.LocalPort '22'
|
363
462
|
xml.Name 'SSH'
|
@@ -365,43 +464,19 @@ class Azure
|
|
365
464
|
xml.Protocol 'TCP'
|
366
465
|
}
|
367
466
|
end
|
467
|
+
all_endpoints = Array.new
|
368
468
|
|
369
469
|
if params[:tcp_endpoints]
|
370
|
-
params[:tcp_endpoints].split(',').each do |endpoint|
|
371
|
-
|
372
|
-
if !(ports.length > 1 && ports[1] == params[:port] || ports.length == 1 && ports[0] == params[:port])
|
373
|
-
xml.InputEndpoint {
|
374
|
-
xml.LocalPort ports[0]
|
375
|
-
xml.Name 'tcpport_' + ports[0] + '_' + params[:azure_vm_name]
|
376
|
-
if ports.length > 1
|
377
|
-
xml.Port ports[1]
|
378
|
-
else
|
379
|
-
xml.Port ports[0]
|
380
|
-
end
|
381
|
-
xml.Protocol 'TCP'
|
382
|
-
}
|
383
|
-
else
|
384
|
-
warn_message = ports.length > 1 ? "#{ports.join(':')} because this ports are" : "#{ports[0]} because this port is"
|
385
|
-
puts("Skipping tcp-endpoints: #{warn_message} already in use by ssh/winrm endpoint in current VM.")
|
386
|
-
end
|
470
|
+
params[:tcp_endpoints].split(',').map(&:strip).each do |endpoint|
|
471
|
+
all_endpoints << parse_endpoint_from_params('TCP', params[:azure_vm_name], endpoint)
|
387
472
|
end
|
388
473
|
end
|
389
|
-
|
390
474
|
if params[:udp_endpoints]
|
391
|
-
params[:udp_endpoints].split(',').each do |endpoint|
|
392
|
-
|
393
|
-
xml.InputEndpoint {
|
394
|
-
xml.LocalPort ports[0]
|
395
|
-
xml.Name 'udpport_' + ports[0] + '_' + params[:azure_vm_name]
|
396
|
-
if ports.length > 1
|
397
|
-
xml.Port ports[1]
|
398
|
-
else
|
399
|
-
xml.Port ports[0]
|
400
|
-
end
|
401
|
-
xml.Protocol 'UDP'
|
402
|
-
}
|
475
|
+
params[:udp_endpoints].split(',').map(&:strip).each do |endpoint|
|
476
|
+
all_endpoints << parse_endpoint_from_params('UDP', params[:azure_vm_name], endpoint)
|
403
477
|
end
|
404
478
|
end
|
479
|
+
add_endpoints_to_xml(xml, all_endpoints, params) if all_endpoints.any?
|
405
480
|
}
|
406
481
|
if params[:azure_subnet_name]
|
407
482
|
xml.SubnetNames {
|
@@ -444,6 +519,8 @@ class Azure
|
|
444
519
|
xml.AvailabilitySetName params[:azure_availability_set]
|
445
520
|
end
|
446
521
|
|
522
|
+
xml.VMImageName params[:azure_source_image] if params[:is_vm_image]
|
523
|
+
|
447
524
|
xml.Label Base64.encode64(params[:azure_vm_name]).strip
|
448
525
|
|
449
526
|
#OSVirtualHardDisk not required in case azure_source_image is a VMImage
|
@@ -451,7 +528,8 @@ class Azure
|
|
451
528
|
xml.OSVirtualHardDisk {
|
452
529
|
disk_name = params[:azure_os_disk_name] || "disk_" + SecureRandom.uuid
|
453
530
|
xml.DiskName disk_name
|
454
|
-
|
531
|
+
domain_suffix = params[:azure_api_host_name].scan(/core.*/)[0]
|
532
|
+
xml.MediaLink 'http://' + params[:azure_storage_account] + '.blob.' + domain_suffix + '/vhds/' + disk_name + '.vhd'
|
455
533
|
xml.SourceImageName params[:azure_source_image]
|
456
534
|
}
|
457
535
|
end
|
@@ -462,6 +540,7 @@ class Azure
|
|
462
540
|
end
|
463
541
|
builder.doc
|
464
542
|
end
|
543
|
+
|
465
544
|
def create(params, roleXML)
|
466
545
|
servicecall = "hostedservices/#{params[:azure_dns_name]}/deployments" +
|
467
546
|
"/#{params['deploy_name']}/roles"
|
@@ -0,0 +1,77 @@
|
|
1
|
+
#
|
2
|
+
# Author:: Aiman Alsari (aiman.alsari@gmail.com)
|
3
|
+
# Copyright:: Copyright (c) 2013 Opscode, Inc.
|
4
|
+
# License:: Apache License, Version 2.0
|
5
|
+
#
|
6
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
7
|
+
# you may not use this file except in compliance with the License.
|
8
|
+
# You may obtain a copy of the License at
|
9
|
+
#
|
10
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
11
|
+
#
|
12
|
+
# Unless required by applicable law or agreed to in writing, software
|
13
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
14
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
15
|
+
# See the License for the specific language governing permissions and
|
16
|
+
# limitations under the License.
|
17
|
+
#
|
18
|
+
|
19
|
+
require File.expand_path('../azure_base', __FILE__)
|
20
|
+
|
21
|
+
class Chef
|
22
|
+
class Knife
|
23
|
+
class AzureInternalLbCreate < Knife
|
24
|
+
include Knife::AzureBase
|
25
|
+
|
26
|
+
banner 'knife azure internal lb create (options)'
|
27
|
+
|
28
|
+
option :azure_load_balancer,
|
29
|
+
:short => '-n NAME',
|
30
|
+
:long => '--azure-load-balancer NAME',
|
31
|
+
:description => 'Required. Specifies new load balancer name.'
|
32
|
+
|
33
|
+
option :azure_lb_static_vip,
|
34
|
+
:long => '--azure-lb-static-vip VIP',
|
35
|
+
:description => 'Optional. The Virtual IP that will be used for the load balancer.'
|
36
|
+
|
37
|
+
option :azure_subnet_name,
|
38
|
+
:long => '--azure-subnet-name SUBNET_NAME',
|
39
|
+
:description => 'Required if static VIP is set. Specifies the subnet name '\
|
40
|
+
'the load balancer is located in.'
|
41
|
+
|
42
|
+
option :azure_dns_name,
|
43
|
+
:long => "--azure-dns-name DNS_NAME",
|
44
|
+
:description => "The DNS prefix name that will be used to add this load balancer to. This must be an existing service/deployment."
|
45
|
+
|
46
|
+
def run
|
47
|
+
$stdout.sync = true
|
48
|
+
|
49
|
+
Chef::Log.info('validating...')
|
50
|
+
validate!([:azure_subscription_id,
|
51
|
+
:azure_mgmt_cert,
|
52
|
+
:azure_api_host_name,
|
53
|
+
:azure_load_balancer])
|
54
|
+
|
55
|
+
params = {
|
56
|
+
azure_load_balancer: locate_config_value(:azure_load_balancer),
|
57
|
+
azure_lb_static_vip: locate_config_value(:azure_lb_static_vip),
|
58
|
+
azure_subnet_name: locate_config_value(:azure_subnet_name),
|
59
|
+
azure_dns_name: locate_config_value(:azure_dns_name)
|
60
|
+
}
|
61
|
+
|
62
|
+
rsp = connection.lbs.create(params)
|
63
|
+
print "\n"
|
64
|
+
if rsp.at_css('Status').nil?
|
65
|
+
if rsp.at_css('Code').nil? || rsp.at_css('Message').nil?
|
66
|
+
puts 'Unknown Error. try -VV'
|
67
|
+
else
|
68
|
+
puts "#{rsp.at_css('Code').content}: "\
|
69
|
+
"#{rsp.at_css('Message').content}"
|
70
|
+
end
|
71
|
+
else
|
72
|
+
puts "Creation status: #{rsp.at_css('Status').content}"
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
#
|
2
|
+
# Author:: Aiman Alsari (aiman.alsari@gmail.com)
|
3
|
+
# Copyright:: Copyright (c) 2013 Opscode, Inc.
|
4
|
+
# License:: Apache License, Version 2.0
|
5
|
+
#
|
6
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
7
|
+
# you may not use this file except in compliance with the License.
|
8
|
+
# You may obtain a copy of the License at
|
9
|
+
#
|
10
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
11
|
+
#
|
12
|
+
# Unless required by applicable law or agreed to in writing, software
|
13
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
14
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
15
|
+
# See the License for the specific language governing permissions and
|
16
|
+
# limitations under the License.
|
17
|
+
#
|
18
|
+
|
19
|
+
require File.expand_path('../azure_base', __FILE__)
|
20
|
+
|
21
|
+
class Chef
|
22
|
+
class Knife
|
23
|
+
class AzureInternalLbList < Knife
|
24
|
+
include Knife::AzureBase
|
25
|
+
|
26
|
+
deps { require 'highline' }
|
27
|
+
|
28
|
+
banner 'knife azure internal lb list (options)'
|
29
|
+
|
30
|
+
def hl
|
31
|
+
@highline ||= HighLine.new
|
32
|
+
end
|
33
|
+
|
34
|
+
def run
|
35
|
+
$stdout.sync = true
|
36
|
+
|
37
|
+
validate!
|
38
|
+
|
39
|
+
cols = %w{Name Service Subnet VIP}
|
40
|
+
|
41
|
+
the_list = cols.map { |col| ui.color(col, :bold) }
|
42
|
+
connection.lbs.all.each do |lb|
|
43
|
+
cols.each { |attr| the_list << lb.send(attr.downcase).to_s }
|
44
|
+
end
|
45
|
+
|
46
|
+
puts "\n"
|
47
|
+
puts hl.list(the_list, :uneven_columns_across, cols.size)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -20,6 +20,7 @@
|
|
20
20
|
|
21
21
|
require 'chef/knife/azure_base'
|
22
22
|
require 'chef/knife/winrm_base'
|
23
|
+
require 'chef/knife/bootstrap_windows_base'
|
23
24
|
require 'securerandom'
|
24
25
|
|
25
26
|
class Chef
|
@@ -28,6 +29,7 @@ class Chef
|
|
28
29
|
|
29
30
|
include Knife::AzureBase
|
30
31
|
include Knife::WinrmBase
|
32
|
+
include Knife::BootstrapWindowsBase
|
31
33
|
|
32
34
|
deps do
|
33
35
|
require 'readline'
|
@@ -40,7 +42,6 @@ class Chef
|
|
40
42
|
|
41
43
|
def load_winrm_deps
|
42
44
|
require 'winrm'
|
43
|
-
require 'em-winrm'
|
44
45
|
require 'chef/knife/winrm'
|
45
46
|
require 'chef/knife/bootstrap_windows_winrm'
|
46
47
|
end
|
@@ -49,6 +50,12 @@ class Chef
|
|
49
50
|
|
50
51
|
attr_accessor :initial_sleep_delay
|
51
52
|
|
53
|
+
option :forward_agent,
|
54
|
+
:short => "-A",
|
55
|
+
:long => "--forward-agent",
|
56
|
+
:description => "Enable SSH agent forwarding",
|
57
|
+
:boolean => true
|
58
|
+
|
52
59
|
option :bootstrap_protocol,
|
53
60
|
:long => "--bootstrap-protocol protocol",
|
54
61
|
:description => "Protocol to bootstrap windows servers. options: 'winrm' or 'ssh' or 'cloud-api'.",
|
@@ -86,15 +93,23 @@ class Chef
|
|
86
93
|
option :distro,
|
87
94
|
:short => "-d DISTRO",
|
88
95
|
:long => "--distro DISTRO",
|
89
|
-
:description => "Bootstrap a distro using a template",
|
90
|
-
:proc
|
91
|
-
|
96
|
+
:description => "Bootstrap a distro using a template. [DEPRECATED] Use --bootstrap-template option instead.",
|
97
|
+
:proc => Proc.new { |v|
|
98
|
+
Chef::Log.warn("[DEPRECATED] -d / --distro option is deprecated. Use --bootstrap-template option instead.")
|
99
|
+
v
|
100
|
+
}
|
92
101
|
|
93
102
|
option :template_file,
|
94
103
|
:long => "--template-file TEMPLATE",
|
95
|
-
:description => "Full path to location of template to use",
|
96
|
-
:proc
|
97
|
-
|
104
|
+
:description => "Full path to location of template to use. [DEPRECATED] Use -t / --bootstrap-template option instead.",
|
105
|
+
:proc => Proc.new { |v|
|
106
|
+
Chef::Log.warn("[DEPRECATED] --template-file option is deprecated. Use -t / --bootstrap-template option instead.")
|
107
|
+
v
|
108
|
+
}
|
109
|
+
|
110
|
+
option :bootstrap_template,
|
111
|
+
:long => "--bootstrap-template TEMPLATE",
|
112
|
+
:description => "Bootstrap Chef using a built-in or custom template. Set to the full path of an erb template or use one of the built-in templates."
|
98
113
|
|
99
114
|
option :run_list,
|
100
115
|
:short => "-r RUN_LIST",
|
@@ -109,6 +124,70 @@ class Chef
|
|
109
124
|
:boolean => true,
|
110
125
|
:default => true
|
111
126
|
|
127
|
+
option :node_ssl_verify_mode,
|
128
|
+
:long => "--node-ssl-verify-mode [peer|none]",
|
129
|
+
:description => "Whether or not to verify the SSL cert for all HTTPS requests.",
|
130
|
+
:proc => Proc.new { |v|
|
131
|
+
valid_values = ["none", "peer"]
|
132
|
+
unless valid_values.include?(v)
|
133
|
+
raise "Invalid value '#{v}' for --node-ssl-verify-mode. Valid values are: #{valid_values.join(", ")}"
|
134
|
+
end
|
135
|
+
}
|
136
|
+
|
137
|
+
option :node_verify_api_cert,
|
138
|
+
:long => "--[no-]node-verify-api-cert",
|
139
|
+
:description => "Verify the SSL cert for HTTPS requests to the Chef server API.",
|
140
|
+
:boolean => true
|
141
|
+
|
142
|
+
option :bootstrap_no_proxy,
|
143
|
+
:long => "--bootstrap-no-proxy [NO_PROXY_URL|NO_PROXY_IP]",
|
144
|
+
:description => "Do not proxy locations for the node being bootstrapped; this option is used internally by Opscode",
|
145
|
+
:proc => Proc.new { |np| Chef::Config[:knife][:bootstrap_no_proxy] = np }
|
146
|
+
|
147
|
+
option :bootstrap_url,
|
148
|
+
:long => "--bootstrap-url URL",
|
149
|
+
:description => "URL to a custom installation script",
|
150
|
+
:proc => Proc.new { |u| Chef::Config[:knife][:bootstrap_url] = u }
|
151
|
+
|
152
|
+
option :bootstrap_install_command,
|
153
|
+
:long => "--bootstrap-install-command COMMANDS",
|
154
|
+
:description => "Custom command to install chef-client",
|
155
|
+
:proc => Proc.new { |ic| Chef::Config[:knife][:bootstrap_install_command] = ic }
|
156
|
+
|
157
|
+
option :bootstrap_wget_options,
|
158
|
+
:long => "--bootstrap-wget-options OPTIONS",
|
159
|
+
:description => "Add options to wget when installing chef-client",
|
160
|
+
:proc => Proc.new { |wo| Chef::Config[:knife][:bootstrap_wget_options] = wo }
|
161
|
+
|
162
|
+
option :bootstrap_curl_options,
|
163
|
+
:long => "--bootstrap-curl-options OPTIONS",
|
164
|
+
:description => "Add options to curl when install chef-client",
|
165
|
+
:proc => Proc.new { |co| Chef::Config[:knife][:bootstrap_curl_options] = co }
|
166
|
+
|
167
|
+
option :bootstrap_vault_file,
|
168
|
+
:long => '--bootstrap-vault-file VAULT_FILE',
|
169
|
+
:description => 'A JSON file with a list of vault(s) and item(s) to be updated'
|
170
|
+
|
171
|
+
option :bootstrap_vault_json,
|
172
|
+
:long => '--bootstrap-vault-json VAULT_JSON',
|
173
|
+
:description => 'A JSON string with the vault(s) and item(s) to be updated'
|
174
|
+
|
175
|
+
option :bootstrap_vault_item,
|
176
|
+
:long => '--bootstrap-vault-item VAULT_ITEM',
|
177
|
+
:description => 'A single vault and item to update as "vault:item"',
|
178
|
+
:proc => Proc.new { |i|
|
179
|
+
(vault, item) = i.split(/:/)
|
180
|
+
Chef::Config[:knife][:bootstrap_vault_item] ||= {}
|
181
|
+
Chef::Config[:knife][:bootstrap_vault_item][vault] ||= []
|
182
|
+
Chef::Config[:knife][:bootstrap_vault_item][vault].push(item)
|
183
|
+
Chef::Config[:knife][:bootstrap_vault_item]
|
184
|
+
}
|
185
|
+
|
186
|
+
option :use_sudo_password,
|
187
|
+
:long => "--use-sudo-password",
|
188
|
+
:description => "Execute the bootstrap via sudo with password",
|
189
|
+
:boolean => false
|
190
|
+
|
112
191
|
option :azure_storage_account,
|
113
192
|
:short => "-a NAME",
|
114
193
|
:long => "--azure-storage-account NAME",
|
@@ -248,19 +327,33 @@ class Chef
|
|
248
327
|
|
249
328
|
option :winrm_max_timeout,
|
250
329
|
:long => "--winrm-max-timeout MINUTES",
|
251
|
-
:description => "Set winrm
|
330
|
+
:description => "Set winrm maximum command timeout in minutes, useful for long bootstraps"
|
252
331
|
|
253
|
-
option :
|
254
|
-
:long => "--winrm-max-
|
332
|
+
option :winrm_max_memorypershell,
|
333
|
+
:long => "--winrm-max-memory-per-shell",
|
255
334
|
:description => "Set winrm max memory per shell in MB"
|
256
335
|
|
257
|
-
|
258
336
|
option :delete_chef_extension_config,
|
259
337
|
:long => "--delete-chef-extension-config",
|
260
338
|
:boolean => true,
|
261
339
|
:default => false,
|
262
340
|
: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."
|
263
341
|
|
342
|
+
option :azure_domain_name,
|
343
|
+
:long => "--azure-domain-name DOMAIN_NAME",
|
344
|
+
: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"
|
345
|
+
|
346
|
+
option :azure_domain_ou_dn,
|
347
|
+
:long => "--azure-domain-ou-dn DOMAIN_OU_DN",
|
348
|
+
: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"
|
349
|
+
|
350
|
+
option :azure_domain_user,
|
351
|
+
:long => "--azure-domain-user DOMAIN_USER_NAME",
|
352
|
+
:description => "Optional. Specifies the username who has access to join the domain."
|
353
|
+
|
354
|
+
option :azure_domain_passwd,
|
355
|
+
:long => "--azure-domain-passwd DOMAIN_PASSWD",
|
356
|
+
:description => "Optional. Specifies the password for domain user who has access to join the domain."
|
264
357
|
|
265
358
|
def strip_non_ascii(string)
|
266
359
|
string.gsub(/[^0-9a-z ]/i, '')
|
@@ -417,7 +510,7 @@ class Chef
|
|
417
510
|
extension_status[:status] = :extension_status_not_detected
|
418
511
|
end
|
419
512
|
# This fix is for linux waagent issue: api unable to deserialize the waagent status.
|
420
|
-
elsif role.at_css(
|
513
|
+
elsif role.at_css('GuestAgentStatus Status').text == 'NotReady' and waagent_status_msg == lnx_waagent_fail_msg
|
421
514
|
extension_status[:status] = :extension_ready
|
422
515
|
else
|
423
516
|
extension_status[:status] = :wagent_provisioning
|
@@ -434,31 +527,31 @@ class Chef
|
|
434
527
|
return extension_status
|
435
528
|
end
|
436
529
|
|
437
|
-
def get_role_server
|
530
|
+
def get_role_server
|
438
531
|
deploy = connection.deploys.queryDeploy(locate_config_value(:azure_dns_name))
|
439
532
|
deploy.find_role(locate_config_value(:azure_vm_name))
|
440
533
|
end
|
441
534
|
|
442
535
|
def tcp_test_winrm(ip_addr, port)
|
443
|
-
|
536
|
+
hostname = ip_addr
|
444
537
|
socket = TCPSocket.new(hostname, port)
|
445
|
-
|
446
|
-
|
447
|
-
|
448
|
-
|
449
|
-
|
450
|
-
|
451
|
-
|
452
|
-
|
453
|
-
|
454
|
-
|
455
|
-
|
456
|
-
|
457
|
-
|
458
|
-
|
459
|
-
|
460
|
-
|
461
|
-
|
538
|
+
return true
|
539
|
+
rescue SocketError
|
540
|
+
sleep 2
|
541
|
+
false
|
542
|
+
rescue Errno::ETIMEDOUT
|
543
|
+
false
|
544
|
+
rescue Errno::EPERM
|
545
|
+
false
|
546
|
+
rescue Errno::ECONNREFUSED
|
547
|
+
sleep 2
|
548
|
+
false
|
549
|
+
rescue Errno::EHOSTUNREACH
|
550
|
+
sleep 2
|
551
|
+
false
|
552
|
+
rescue Errno::ENETUNREACH
|
553
|
+
sleep 2
|
554
|
+
false
|
462
555
|
end
|
463
556
|
|
464
557
|
def tcp_test_ssh(fqdn, sshport)
|
@@ -550,13 +643,14 @@ class Chef
|
|
550
643
|
bootstrap_exec(server) unless locate_config_value(:bootstrap_protocol) == 'cloud-api'
|
551
644
|
end
|
552
645
|
|
646
|
+
def default_bootstrap_template
|
647
|
+
is_image_windows? ? 'windows-chef-client-msi' : 'chef-full'
|
648
|
+
end
|
649
|
+
|
553
650
|
def bootstrap_exec(server)
|
554
651
|
fqdn = server.publicipaddress
|
555
652
|
|
556
653
|
if is_image_windows?
|
557
|
-
# Set distro to windows-chef-client-msi
|
558
|
-
config[:distro] = "windows-chef-client-msi" if (config[:distro].nil? || config[:distro] == "chef-full")
|
559
|
-
|
560
654
|
if locate_config_value(:bootstrap_protocol) == 'ssh'
|
561
655
|
port = server.sshport
|
562
656
|
print "#{ui.color("Waiting for sshd on #{fqdn}:#{port}", :magenta)}"
|
@@ -587,7 +681,7 @@ class Chef
|
|
587
681
|
|
588
682
|
port = server.sshport
|
589
683
|
|
590
|
-
print
|
684
|
+
print ui.color("Waiting for sshd on #{fqdn}:#{port}", :magenta)
|
591
685
|
|
592
686
|
print(".") until tcp_test_ssh(fqdn,port) {
|
593
687
|
sleep @initial_sleep_delay ||= 10
|
@@ -622,8 +716,17 @@ class Chef
|
|
622
716
|
bootstrap.config[:prerelease] = config[:prerelease]
|
623
717
|
bootstrap.config[:first_boot_attributes] = locate_config_value(:json_attributes) || {}
|
624
718
|
bootstrap.config[:bootstrap_version] = locate_config_value(:bootstrap_version)
|
625
|
-
bootstrap.config[:distro] = locate_config_value(:distro)
|
626
|
-
|
719
|
+
bootstrap.config[:distro] = locate_config_value(:distro) || default_bootstrap_template
|
720
|
+
# setting bootstrap_template value to template_file for backward
|
721
|
+
bootstrap.config[:template_file] = locate_config_value(:template_file) || locate_config_value(:bootstrap_template)
|
722
|
+
bootstrap.config[:node_ssl_verify_mode] = locate_config_value(:node_ssl_verify_mode)
|
723
|
+
bootstrap.config[:node_verify_api_cert] = locate_config_value(:node_verify_api_cert)
|
724
|
+
bootstrap.config[:bootstrap_no_proxy] = locate_config_value(:bootstrap_no_proxy)
|
725
|
+
bootstrap.config[:bootstrap_url] = locate_config_value(:bootstrap_url)
|
726
|
+
bootstrap.config[:bootstrap_vault_file] = locate_config_value(:bootstrap_vault_file)
|
727
|
+
bootstrap.config[:bootstrap_vault_json] = locate_config_value(:bootstrap_vault_json)
|
728
|
+
bootstrap.config[:bootstrap_vault_item] = locate_config_value(:bootstrap_vault_item)
|
729
|
+
|
627
730
|
load_cloud_attributes_in_hints(server)
|
628
731
|
bootstrap
|
629
732
|
end
|
@@ -646,11 +749,11 @@ class Chef
|
|
646
749
|
bootstrap.config[:auth_timeout] = locate_config_value(:auth_timeout)
|
647
750
|
# Todo: we should skip cert generate in case when winrm_ssl_verify_mode=verify_none
|
648
751
|
bootstrap.config[:winrm_ssl_verify_mode] = locate_config_value(:winrm_ssl_verify_mode)
|
649
|
-
|
650
752
|
elsif locate_config_value(:bootstrap_protocol) == 'ssh'
|
651
753
|
bootstrap = Chef::Knife::BootstrapWindowsSsh.new
|
652
754
|
bootstrap.config[:ssh_user] = locate_config_value(:ssh_user)
|
653
755
|
bootstrap.config[:ssh_password] = locate_config_value(:ssh_password)
|
756
|
+
bootstrap.config[:forward_agent] = locate_config_value(:forward_agent)
|
654
757
|
bootstrap.config[:ssh_port] = port
|
655
758
|
bootstrap.config[:identity_file] = locate_config_value(:identity_file)
|
656
759
|
bootstrap.config[:host_key_verify] = locate_config_value(:host_key_verify)
|
@@ -662,6 +765,8 @@ class Chef
|
|
662
765
|
bootstrap.config[:chef_node_name] = config[:chef_node_name] || server.name
|
663
766
|
bootstrap.config[:encrypted_data_bag_secret] = locate_config_value(:encrypted_data_bag_secret)
|
664
767
|
bootstrap.config[:encrypted_data_bag_secret_file] = locate_config_value(:encrypted_data_bag_secret_file)
|
768
|
+
bootstrap.config[:msi_url] = locate_config_value(:msi_url)
|
769
|
+
bootstrap.config[:install_as_service] = locate_config_value(:install_as_service)
|
665
770
|
bootstrap_common_params(bootstrap, server)
|
666
771
|
end
|
667
772
|
|
@@ -680,7 +785,9 @@ class Chef
|
|
680
785
|
bootstrap.config[:host_key_verify] = config[:host_key_verify]
|
681
786
|
bootstrap.config[:secret] = locate_config_value(:secret)
|
682
787
|
bootstrap.config[:secret_file] = locate_config_value(:secret_file)
|
683
|
-
|
788
|
+
bootstrap.config[:bootstrap_install_command] = locate_config_value(:bootstrap_install_command)
|
789
|
+
bootstrap.config[:bootstrap_wget_options] = locate_config_value(:bootstrap_wget_options)
|
790
|
+
bootstrap.config[:bootstrap_curl_options] = locate_config_value(:bootstrap_curl_options)
|
684
791
|
bootstrap_common_params(bootstrap, server)
|
685
792
|
end
|
686
793
|
|
@@ -725,11 +832,25 @@ class Chef
|
|
725
832
|
ui.error("Image provided is invalid")
|
726
833
|
exit 1
|
727
834
|
end
|
835
|
+
|
836
|
+
# Validate join domain requirements.
|
837
|
+
if locate_config_value(:azure_domain_name) or locate_config_value(:azure_domain_user)
|
838
|
+
if locate_config_value(:azure_domain_user).nil? or locate_config_value(:azure_domain_passwd).nil?
|
839
|
+
ui.error("Must specify both --azure-domain-user and --azure-domain-passwd.")
|
840
|
+
exit 1
|
841
|
+
end
|
842
|
+
end
|
843
|
+
|
844
|
+
if locate_config_value(:winrm_transport) == "ssl" && locate_config_value(:thumbprint).nil? && ( locate_config_value(:winrm_ssl_verify_mode).nil? || locate_config_value(:winrm_ssl_verify_mode) == :verify_peer )
|
845
|
+
ui.error("The SSL transport was specified without the --thumbprint option. Specify a thumbprint, or alternatively set the --winrm-ssl-verify-mode option to 'verify_none' to skip verification.")
|
846
|
+
exit 1
|
847
|
+
end
|
728
848
|
end
|
729
849
|
|
730
850
|
def create_server_def
|
731
851
|
server_def = {
|
732
852
|
:azure_storage_account => locate_config_value(:azure_storage_account),
|
853
|
+
:azure_api_host_name => locate_config_value(:azure_api_host_name),
|
733
854
|
:azure_dns_name => locate_config_value(:azure_dns_name),
|
734
855
|
:azure_vm_name => locate_config_value(:azure_vm_name),
|
735
856
|
:azure_service_location => locate_config_value(:azure_service_location),
|
@@ -750,7 +871,7 @@ class Chef
|
|
750
871
|
:cert_password => locate_config_value(:cert_passphrase),
|
751
872
|
:winrm_transport => locate_config_value(:winrm_transport),
|
752
873
|
:winrm_max_timeout => locate_config_value(:winrm_max_timeout).to_i * 60 * 1000, #converting minutes to milliseconds
|
753
|
-
:winrm_max_memoryPerShell => locate_config_value(:
|
874
|
+
:winrm_max_memoryPerShell => locate_config_value(:winrm_max_memory_per_shell)
|
754
875
|
}
|
755
876
|
# If user is connecting a new VM to an existing dns, then
|
756
877
|
# the VM needs to have a unique public port. Logic below takes care of this.
|
@@ -767,7 +888,6 @@ class Chef
|
|
767
888
|
port = locate_config_value(:winrm_port) || '5985'
|
768
889
|
end
|
769
890
|
end
|
770
|
-
|
771
891
|
server_def[:port] = port
|
772
892
|
|
773
893
|
if locate_config_value(:bootstrap_protocol) == 'cloud-api'
|
@@ -811,7 +931,7 @@ class Chef
|
|
811
931
|
server_def[:bootstrap_proto] = locate_config_value(:bootstrap_protocol)
|
812
932
|
else
|
813
933
|
server_def[:os_type] = 'Linux'
|
814
|
-
server_def[:bootstrap_proto] =
|
934
|
+
server_def[:bootstrap_proto] = (locate_config_value(:bootstrap_protocol) == 'winrm') ? 'ssh' : locate_config_value(:bootstrap_protocol)
|
815
935
|
server_def[:ssh_user] = locate_config_value(:ssh_user)
|
816
936
|
server_def[:ssh_password] = locate_config_value(:ssh_password)
|
817
937
|
server_def[:identity_file] = locate_config_value(:identity_file)
|
@@ -819,6 +939,27 @@ class Chef
|
|
819
939
|
end
|
820
940
|
|
821
941
|
server_def[:is_vm_image] = connection.images.is_vm_image(locate_config_value(:azure_source_image))
|
942
|
+
|
943
|
+
if locate_config_value(:azure_domain_name)
|
944
|
+
server_def[:azure_domain_name] = locate_config_value(:azure_domain_name)
|
945
|
+
server_def[:azure_domain_user] = locate_config_value(:azure_domain_user)
|
946
|
+
elsif locate_config_value(:azure_domain_user)
|
947
|
+
# extract domain name since it should be part of username
|
948
|
+
case locate_config_value(:azure_domain_user)
|
949
|
+
when /(\S+)\\(.+)/ # format - fully-qualified-DNS-domain\username
|
950
|
+
server_def[:azure_domain_name] = $1
|
951
|
+
server_def[:azure_domain_user] = $2
|
952
|
+
when /(.+)@(\S+)/ # format - user@fully-qualified-DNS-domain
|
953
|
+
server_def[:azure_domain_name] = $2
|
954
|
+
server_def[:azure_domain_user] = $1
|
955
|
+
else
|
956
|
+
# Format error.
|
957
|
+
ui.error("Format error for --azure-domain-user option. Supported format are user principal name (UPN) format (user@fully-qualified-DNS-domain) or the fully-qualified-DNS-domain\\username format")
|
958
|
+
exit 1
|
959
|
+
end
|
960
|
+
end
|
961
|
+
server_def[:azure_domain_passwd] = locate_config_value(:azure_domain_passwd)
|
962
|
+
server_def[:azure_domain_ou_dn] = locate_config_value(:azure_domain_ou_dn)
|
822
963
|
server_def
|
823
964
|
end
|
824
965
|
|
@@ -855,13 +996,33 @@ class Chef
|
|
855
996
|
pub_config[:bootstrap_options][:encrypted_data_bag_secret] = locate_config_value(:encrypted_data_bag_secret) if locate_config_value(:encrypted_data_bag_secret)
|
856
997
|
pub_config[:bootstrap_options][:chef_server_url] = Chef::Config[:chef_server_url] if Chef::Config[:chef_server_url]
|
857
998
|
pub_config[:bootstrap_options][:validation_client_name] = Chef::Config[:validation_client_name] if Chef::Config[:validation_client_name]
|
858
|
-
|
999
|
+
pub_config[:bootstrap_options][:node_verify_api_cert] = locate_config_value(:node_verify_api_cert) ? "true" : "false" if config.key?(:node_verify_api_cert)
|
859
1000
|
Base64.encode64(pub_config.to_json)
|
860
1001
|
end
|
861
1002
|
|
862
1003
|
def get_chef_extension_private_params
|
863
1004
|
pri_config = Hash.new
|
864
|
-
|
1005
|
+
|
1006
|
+
# validator less bootstrap support for bootstrap protocol cloud-api
|
1007
|
+
if (Chef::Config[:validation_key] && !File.exist?(File.expand_path(Chef::Config[:validation_key])))
|
1008
|
+
|
1009
|
+
if Chef::VERSION.split('.').first.to_i == 11
|
1010
|
+
ui.error('Unable to find validation key. Please verify your configuration file for validation_key config value.')
|
1011
|
+
exit 1
|
1012
|
+
end
|
1013
|
+
|
1014
|
+
client_builder = Chef::Knife::Bootstrap::ClientBuilder.new(
|
1015
|
+
chef_config: Chef::Config,
|
1016
|
+
knife_config: config,
|
1017
|
+
ui: ui,
|
1018
|
+
)
|
1019
|
+
|
1020
|
+
client_builder.run
|
1021
|
+
key_path = client_builder.client_path
|
1022
|
+
pri_config[:client_pem] = File.read(key_path)
|
1023
|
+
else
|
1024
|
+
pri_config[:validation_key] = File.read(Chef::Config[:validation_key])
|
1025
|
+
end
|
865
1026
|
Base64.encode64(pri_config.to_json)
|
866
1027
|
end
|
867
1028
|
|
@@ -898,7 +1059,7 @@ class Chef
|
|
898
1059
|
!locate_config_value(:winrm_password).nil?
|
899
1060
|
config[:ssh_password] = locate_config_value(:winrm_password)
|
900
1061
|
end
|
901
|
-
# unset identity_file and set
|
1062
|
+
# unset identity_file and set _file, override identity_file
|
902
1063
|
if locate_config_value(:identity_file).nil? &&
|
903
1064
|
!locate_config_value(:kerberos_keytab_file).nil?
|
904
1065
|
config[:identity_file] = locate_config_value(:kerberos_keytab_file)
|
data/lib/knife-azure/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: knife-azure
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.5.1.rc.
|
4
|
+
version: 1.5.1.rc.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Barry Davis
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2015-
|
12
|
+
date: 2015-09-17 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: nokogiri
|
@@ -29,36 +29,36 @@ dependencies:
|
|
29
29
|
name: knife-windows
|
30
30
|
requirement: !ruby/object:Gem::Requirement
|
31
31
|
requirements:
|
32
|
-
- - "
|
32
|
+
- - "~>"
|
33
33
|
- !ruby/object:Gem::Version
|
34
|
-
version: 0.
|
34
|
+
version: 1.0.0.rc.1
|
35
35
|
type: :runtime
|
36
36
|
prerelease: false
|
37
37
|
version_requirements: !ruby/object:Gem::Requirement
|
38
38
|
requirements:
|
39
|
-
- - "
|
39
|
+
- - "~>"
|
40
40
|
- !ruby/object:Gem::Version
|
41
|
-
version: 0.
|
41
|
+
version: 1.0.0.rc.1
|
42
42
|
- !ruby/object:Gem::Dependency
|
43
43
|
name: chef
|
44
44
|
requirement: !ruby/object:Gem::Requirement
|
45
45
|
requirements:
|
46
|
-
- - "
|
46
|
+
- - "~>"
|
47
47
|
- !ruby/object:Gem::Version
|
48
|
-
version:
|
49
|
-
- - "
|
48
|
+
version: '12.0'
|
49
|
+
- - ">="
|
50
50
|
- !ruby/object:Gem::Version
|
51
|
-
version:
|
51
|
+
version: 12.2.1
|
52
52
|
type: :development
|
53
53
|
prerelease: false
|
54
54
|
version_requirements: !ruby/object:Gem::Requirement
|
55
55
|
requirements:
|
56
|
-
- - "
|
56
|
+
- - "~>"
|
57
57
|
- !ruby/object:Gem::Version
|
58
|
-
version:
|
59
|
-
- - "
|
58
|
+
version: '12.0'
|
59
|
+
- - ">="
|
60
60
|
- !ruby/object:Gem::Version
|
61
|
-
version:
|
61
|
+
version: 12.2.1
|
62
62
|
- !ruby/object:Gem::Dependency
|
63
63
|
name: mixlib-config
|
64
64
|
requirement: !ruby/object:Gem::Requirement
|
@@ -118,6 +118,7 @@ files:
|
|
118
118
|
- lib/azure/disk.rb
|
119
119
|
- lib/azure/host.rb
|
120
120
|
- lib/azure/image.rb
|
121
|
+
- lib/azure/loadbalancer.rb
|
121
122
|
- lib/azure/rest.rb
|
122
123
|
- lib/azure/role.rb
|
123
124
|
- lib/azure/storageaccount.rb
|
@@ -127,6 +128,8 @@ files:
|
|
127
128
|
- lib/chef/knife/azure_ag_list.rb
|
128
129
|
- lib/chef/knife/azure_base.rb
|
129
130
|
- lib/chef/knife/azure_image_list.rb
|
131
|
+
- lib/chef/knife/azure_internal-lb_create.rb
|
132
|
+
- lib/chef/knife/azure_internal-lb_list.rb
|
130
133
|
- lib/chef/knife/azure_server_create.rb
|
131
134
|
- lib/chef/knife/azure_server_delete.rb
|
132
135
|
- lib/chef/knife/azure_server_list.rb
|