knife-azure 1.5.1.rc.1 → 1.5.1.rc.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml 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