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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: b155513dd741133385af7bca60fdd5453f127747
4
- data.tar.gz: 7e8bf7e30c23627796a11fc8edd9e6e1f02c4d92
3
+ metadata.gz: 77a080a603467387d888d6063ecd0ac4602ab8b4
4
+ data.tar.gz: 91e38b9592db815f0119cc346e0e169713719df4
5
5
  SHA512:
6
- metadata.gz: 50bb073dc94f302d783ba9ab342456a9a820e1872242279b1572b16e4ef4706a7d4f26bd048a2275704a5d838ba312e3e5268384cd6c85ab6fefd449eae07255
7
- data.tar.gz: f8de73b32d14eda508ab58a4322f9a2d06d12e5291c5f0233765f163da9808b3778e37bf3aa3ea0b4e8f59708f4e40a9ad3aae6472ff161ea499f5f0a9607210
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
 
@@ -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.warn error_code + ' : ' + error_message if error_code.length > 0
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.warn status + error_code + ' : ' + error_message if error_code.length > 0
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.warn('Unable to find hosted(cloud) service:' + error_code + ' : ' + error_message) if ret_val
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-04-01"
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 params[:os_type] == 'Linux' and params[:bootstrap_proto].downcase == 'ssh'
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
- ports = endpoint.split(':')
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
- ports = endpoint.split(':')
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
- xml.MediaLink 'http://' + params[:azure_storage_account] + '.blob.core.windows.net/vhds/' + disk_name + '.vhd'
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 => Proc.new { |d| Chef::Config[:knife][:distro] = d },
91
- :default => "chef-full"
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 => Proc.new { |t| Chef::Config[:knife][:template_file] = t },
97
- :default => false
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 max timeout in minutes"
330
+ :description => "Set winrm maximum command timeout in minutes, useful for long bootstraps"
252
331
 
253
- option :winrm_max_memoryPerShell,
254
- :long => "--winrm-max-memoryPerShell MB",
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("GuestAgentStatus Status").text == "NotReady" and waagent_status_msg == lnx_waagent_fail_msg
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
- hostname = ip_addr
536
+ hostname = ip_addr
444
537
  socket = TCPSocket.new(hostname, port)
445
- return true
446
- rescue SocketError
447
- sleep 2
448
- false
449
- rescue Errno::ETIMEDOUT
450
- false
451
- rescue Errno::EPERM
452
- false
453
- rescue Errno::ECONNREFUSED
454
- sleep 2
455
- false
456
- rescue Errno::EHOSTUNREACH
457
- sleep 2
458
- false
459
- rescue Errno::ENETUNREACH
460
- sleep 2
461
- false
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 "#{ui.color("Waiting for sshd on #{fqdn}:#{port}", :magenta)}"
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
- bootstrap.config[:template_file] = locate_config_value(:template_file)
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(:winrm_max_memoryPerShell)
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] = locate_config_value(:bootstrap_protocol) || 'ssh'
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
- pri_config[:validation_key] = File.read(Chef::Config[:validation_key])
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 kerberos_keytab_file, override identity_file
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)
@@ -1,6 +1,6 @@
1
1
  module Knife
2
2
  module Azure
3
- VERSION = "1.5.1.rc.1"
3
+ VERSION = "1.5.1.rc.2"
4
4
  MAJOR, MINOR, TINY = VERSION.split('.')
5
5
  end
6
6
  end
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.1
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-06-03 00:00:00.000000000 Z
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.8.2
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.8.2
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: 11.8.2
49
- - - "<"
48
+ version: '12.0'
49
+ - - ">="
50
50
  - !ruby/object:Gem::Version
51
- version: '12'
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: 11.8.2
59
- - - "<"
58
+ version: '12.0'
59
+ - - ">="
60
60
  - !ruby/object:Gem::Version
61
- version: '12'
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