knife-azure 1.5.1.rc.1 → 1.5.1.rc.2
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/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
|