chef-metal 0.5 → 0.6

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,18 +0,0 @@
1
- require 'chef/resource/lwrp_base'
2
- require 'chef_metal/provisioner/vagrant_provisioner'
3
-
4
- class Chef::Resource::VagrantBox < Chef::Resource::LWRPBase
5
- self.resource_name = 'vagrant_box'
6
-
7
- actions :create, :delete, :nothing
8
- default_action :create
9
-
10
- attribute :name, :kind_of => String, :name_attribute => true
11
- attribute :url, :kind_of => String
12
- attribute :provisioner_options, :kind_of => Hash
13
-
14
- def after_created
15
- super
16
- ChefMetal.with_vagrant_box self
17
- end
18
- end
@@ -1,16 +0,0 @@
1
- require 'chef/resource/lwrp_base'
2
- require 'chef_metal'
3
-
4
- class Chef::Resource::VagrantCluster < Chef::Resource::LWRPBase
5
- self.resource_name = 'vagrant_cluster'
6
-
7
- actions :create, :delete, :nothing
8
- default_action :create
9
-
10
- attribute :path, :kind_of => String, :name_attribute => true
11
-
12
- def after_created
13
- super
14
- ChefMetal.with_vagrant_cluster path
15
- end
16
- end
@@ -1,20 +0,0 @@
1
- require 'chef_metal'
2
- require 'chef/resource/fog_key_pair'
3
- require 'chef/provider/fog_key_pair'
4
- require 'chef_metal/provisioner/fog_provisioner'
5
-
6
- class Chef
7
- class Recipe
8
- def with_fog_provisioner(options = {}, &block)
9
- ChefMetal.with_provisioner(ChefMetal::Provisioner::FogProvisioner.new(options), &block)
10
- end
11
-
12
- def with_fog_ec2_provisioner(options = {}, &block)
13
- with_fog_provisioner({ :provider => 'AWS' }.merge(options), &block)
14
- end
15
-
16
- def with_fog_openstack_provisioner(options = {}, &block)
17
- with_fog_provisioner({ :provider => 'OpenStack' }.merge(options), &block)
18
- end
19
- end
20
- end
@@ -1,550 +0,0 @@
1
- require 'chef_metal/provisioner'
2
- require 'chef_metal/aws_credentials'
3
- require 'chef_metal/openstack_credentials'
4
- require 'chef_metal/version'
5
- require 'fog/compute'
6
- require 'fog'
7
-
8
- module ChefMetal
9
- class Provisioner
10
-
11
- # Provisions machines in vagrant.
12
- class FogProvisioner < Provisioner
13
-
14
- include Chef::Mixin::ShellOut
15
-
16
- DEFAULT_OPTIONS = {
17
- :create_timeout => 600,
18
- :start_timeout => 600,
19
- :ssh_timeout => 20
20
- }
21
-
22
- def self.inflate(node)
23
- url = node['normal']['provisioner_output']['provisioner_url']
24
- scheme, provider, id = url.split(':', 3)
25
- FogProvisioner.new({ :provider => provider }, id)
26
- end
27
-
28
- # Create a new fog provisioner.
29
- #
30
- # ## Parameters
31
- # compute_options - hash of options to be passed to Fog::Compute.new
32
- # Special options:
33
- # - :base_bootstrap_options is merged with bootstrap_options in acquire_machine
34
- # to present the full set of bootstrap options. Write down any bootstrap_options
35
- # you intend to apply universally here.
36
- # - :aws_credentials is an AWS CSV file (created with Download Credentials)
37
- # containing your aws key information. If you do not specify aws_access_key_id
38
- # and aws_secret_access_key explicitly, the first line from this file
39
- # will be used. You may pass a Cheffish::AWSCredentials object.
40
- # - :create_timeout - the time to wait for the instance to boot to ssh (defaults to 600)
41
- # - :start_timeout - the time to wait for the instance to start (defaults to 600)
42
- # - :ssh_timeout - the time to wait for ssh to be available if the instance is detected as up (defaults to 20)
43
- # id - the ID in the provisioner_url (fog:PROVIDER:ID)
44
- def initialize(compute_options, id=nil)
45
- @compute_options = compute_options
46
- @base_bootstrap_options = compute_options.delete(:base_bootstrap_options) || {}
47
-
48
- case compute_options[:provider]
49
- when 'AWS'
50
- aws_credentials = compute_options.delete(:aws_credentials)
51
- if aws_credentials
52
- @aws_credentials = aws_credentials
53
- else
54
- @aws_credentials = ChefMetal::AWSCredentials.new
55
- @aws_credentials.load_default
56
- end
57
- compute_options[:aws_access_key_id] ||= @aws_credentials.default[:access_key_id]
58
- compute_options[:aws_secret_access_key] ||= @aws_credentials.default[:secret_access_key]
59
- # TODO actually find a key with the proper id
60
- # TODO let the user specify credentials and provider profiles that we can use
61
- if id && aws_login_info[0] != id
62
- raise "Default AWS credentials point at AWS account #{aws_login_info[0]}, but inflating from URL #{id}"
63
- end
64
- when 'OpenStack'
65
- openstack_credentials = compute_options.delete(:openstack_credentials)
66
- if openstack_credentials
67
- @openstack_credentials = openstack_credentials
68
- else
69
- @openstack_credentials = ChefMetal::OpenstackCredentials.new
70
- @openstack_credentials.load_default
71
- end
72
-
73
- compute_options[:openstack_username] ||= @openstack_credentials.default[:openstack_username]
74
- compute_options[:openstack_api_key] ||= @openstack_credentials.default[:openstack_api_key]
75
- compute_options[:openstack_auth_url] ||= @openstack_credentials.default[:openstack_auth_url]
76
- compute_options[:openstack_tenant] ||= @openstack_credentials.default[:openstack_tenant]
77
- end
78
- @key_pairs = {}
79
- @base_bootstrap_options_for = {}
80
- end
81
-
82
- attr_reader :compute_options
83
- attr_reader :aws_credentials
84
- attr_reader :openstack_credentials
85
- attr_reader :key_pairs
86
-
87
- def current_base_bootstrap_options
88
- result = @base_bootstrap_options.dup
89
- if key_pairs.size > 0
90
- last_pair_name = key_pairs.keys.last
91
- last_pair = key_pairs[last_pair_name]
92
- result[:key_name] ||= last_pair_name
93
- result[:private_key_path] ||= last_pair.private_key_path
94
- result[:public_key_path] ||= last_pair.public_key_path
95
- end
96
- result
97
- end
98
-
99
- # Inflate a provisioner from node information; we don't want to force the
100
- # driver to figure out what the provisioner really needs, since it varies
101
- # from provisioner to provisioner.
102
- #
103
- # ## Parameters
104
- # node - node to inflate the provisioner for
105
- #
106
- # returns a FogProvisioner
107
- # TODO: def self.inflate(node)
108
- # right now, not implemented, will raise error from base class until overridden
109
-
110
- # Acquire a machine, generally by provisioning it. Returns a Machine
111
- # object pointing at the machine, allowing useful actions like setup,
112
- # converge, execute, file and directory. The Machine object will have a
113
- # "node" property which must be saved to the server (if it is any
114
- # different from the original node object).
115
- #
116
- # ## Parameters
117
- # action_handler - the action_handler object that is calling this method; this
118
- # is generally a action_handler, but could be anything that can support the
119
- # ChefMetal::ActionHandler interface (i.e., in the case of the test
120
- # kitchen metal driver for acquiring and destroying VMs; see the base
121
- # class for what needs providing).
122
- # node - node object (deserialized json) representing this machine. If
123
- # the node has a provisioner_options hash in it, these will be used
124
- # instead of options provided by the provisioner. TODO compare and
125
- # fail if different?
126
- # node will have node['normal']['provisioner_options'] in it with any options.
127
- # It is a hash with this format:
128
- #
129
- # -- provisioner_url: fog:<relevant_fog_options>
130
- # -- bootstrap_options: hash of options to pass to compute.servers.create
131
- # -- is_windows: true if windows. TODO detect this from ami?
132
- # -- create_timeout - the time to wait for the instance to boot to ssh (defaults to 600)
133
- # -- start_timeout - the time to wait for the instance to start (defaults to 600)
134
- # -- ssh_timeout - the time to wait for ssh to be available if the instance is detected as up (defaults to 20)
135
- #
136
- # Example bootstrap_options for ec2:
137
- # :image_id =>'ami-311f2b45',
138
- # :flavor_id =>'t1.micro',
139
- # :key_name => 'key-pair-name'
140
- #
141
- # node['normal']['provisioner_output'] will be populated with information
142
- # about the created machine. For vagrant, it is a hash with this
143
- # format:
144
- #
145
- # -- provisioner_url: fog:<relevant_fog_options>
146
- # -- server_id: the ID of the server so it can be found again
147
- #
148
- def acquire_machine(action_handler, node)
149
- # Set up the modified node data
150
- provisioner_output = node['normal']['provisioner_output'] || {
151
- 'provisioner_url' => provisioner_url,
152
- 'provisioner_version' => ChefMetal::VERSION,
153
- 'creator' => aws_login_info[1]
154
- }
155
-
156
- if provisioner_output['provisioner_url'] != provisioner_url
157
- if (provisioner_output['provisioner_version'].to_f <= 0.3) && provisioner_output['provisioner_url'].start_with?('fog:AWS:') && compute_options[:provider] == 'AWS'
158
- Chef::Log.warn "The upgrade from chef-metal 0.3 to 0.4 changed the provisioner URL format! Metal will assume you are in fact using the same AWS account, and modify the provisioner URL to match."
159
- provisioner_output['provisioner_url'] = provisioner_url
160
- provisioner_output['provisioner_version'] ||= ChefMetal::VERSION
161
- provisioner_output['creator'] ||= aws_login_info[1]
162
- else
163
- raise "Switching providers for a machine is not currently supported! Use machine :destroy and then re-create the machine on the new action_handler."
164
- end
165
- end
166
-
167
- node['normal']['provisioner_output'] = provisioner_output
168
-
169
- if provisioner_output['server_id']
170
-
171
- # If the server already exists, make sure it is up
172
-
173
- # TODO verify that the server info matches the specification (ami, etc.)\
174
- server = server_for(node)
175
- if !server
176
- Chef::Log.warn "Machine #{node['name']} (#{provisioner_output['server_id']} on #{provisioner_url}) is not associated with the ec2 account. Recreating ..."
177
- need_to_create = true
178
- elsif %w(terminated archive).include?(server.state) # Can't come back from that
179
- Chef::Log.warn "Machine #{node['name']} (#{server.id} on #{provisioner_url}) is terminated. Recreating ..."
180
- need_to_create = true
181
- else
182
- need_to_create = false
183
- if !server.ready?
184
- action_handler.perform_action "start machine #{node['name']} (#{server.id} on #{provisioner_url})" do
185
- server.start
186
- end
187
- action_handler.perform_action "wait for machine #{node['name']} (#{server.id} on #{provisioner_url}) to be ready" do
188
- wait_until_ready(server, option_for(node, :start_timeout))
189
- end
190
- else
191
- wait_until_ready(server, option_for(node, :ssh_timeout))
192
- end
193
- end
194
- else
195
- need_to_create = true
196
- end
197
-
198
- if need_to_create
199
- # If the server does not exist, create it
200
- bootstrap_options = bootstrap_options_for(action_handler.new_resource, node)
201
- bootstrap_options.merge(:name => action_handler.new_resource.name)
202
-
203
- start_time = Time.now
204
- timeout = option_for(node, :create_timeout)
205
-
206
- description = [ "create machine #{node['name']} on #{provisioner_url}" ]
207
- bootstrap_options.each_pair { |key,value| description << " #{key}: #{value.inspect}" }
208
- server = nil
209
- action_handler.perform_action description do
210
- server = compute.servers.create(bootstrap_options)
211
- provisioner_output['server_id'] = server.id
212
- # Save quickly in case something goes wrong
213
- save_node(action_handler, node, action_handler.new_resource.chef_server)
214
- end
215
-
216
- if server
217
- @@ip_pool_lock = Mutex.new
218
- # Re-retrieve the server in a more malleable form and wait for it to be ready
219
- server = compute.servers.get(server.id)
220
- if bootstrap_options[:floating_ip_pool]
221
- Chef::Log.info 'Attaching IP from pool'
222
- server.wait_for { ready? }
223
- action_handler.perform_action "attach floating IP from #{bootstrap_options[:floating_ip_pool]} pool" do
224
- attach_ip_from_pool(server, bootstrap_options[:floating_ip_pool])
225
- end
226
- elsif bootstrap_options[:floating_ip]
227
- Chef::Log.info 'Attaching given IP'
228
- server.wait_for { ready? }
229
- action_handler.perform_action "attach floating IP #{bootstrap_options[:floating_ip]}" do
230
- attach_ip(server, bootstrap_options[:floating_ip])
231
- end
232
- end
233
- action_handler.perform_action "machine #{node['name']} created as #{server.id} on #{provisioner_url}" do
234
- end
235
- # Wait for the machine to come up and for ssh to start listening
236
- transport = nil
237
- _self = self
238
- action_handler.perform_action "wait for machine #{node['name']} to boot" do
239
- server.wait_for(timeout - (Time.now - start_time)) do
240
- if ready?
241
- transport ||= _self.transport_for(server)
242
- begin
243
- transport.execute('pwd')
244
- true
245
- rescue Errno::ECONNREFUSED, Net::SSH::Disconnect
246
- false
247
- rescue
248
- true
249
- end
250
- else
251
- false
252
- end
253
- end
254
- end
255
-
256
- # If there is some other error, we just wait patiently for SSH
257
- begin
258
- server.wait_for(option_for(node, :ssh_timeout)) { transport.available? }
259
- rescue Fog::Errors::TimeoutError
260
- # Sometimes (on EC2) the machine comes up but gets stuck or has
261
- # some other problem. If this is the case, we restart the server
262
- # to unstick it. Reboot covers a multitude of sins.
263
- Chef::Log.warn "Machine #{node['name']} (#{server.id} on #{provisioner_url}) was started but SSH did not come up. Rebooting machine in an attempt to unstick it ..."
264
- action_handler.perform_action "reboot machine #{node['name']} to try to unstick it" do
265
- server.reboot
266
- end
267
- action_handler.perform_action "wait for machine #{node['name']} to be ready after reboot" do
268
- wait_until_ready(server, option_for(node, :start_timeout))
269
- end
270
- end
271
- end
272
- end
273
-
274
- # Create machine object for callers to use
275
- machine_for(node, server)
276
- end
277
-
278
- # Attach IP to machine from IP pool
279
- # Code taken from kitchen-openstack driver
280
- # https://github.com/test-kitchen/kitchen-openstack/blob/master/lib/kitchen/driver/openstack.rb#L196-L207
281
- def attach_ip_from_pool(server, pool)
282
- @@ip_pool_lock.synchronize do
283
- Chef::Log.info "Attaching floating IP from <#{pool}> pool"
284
- free_addrs = compute.addresses.collect do |i|
285
- i.ip if i.fixed_ip.nil? and i.instance_id.nil? and i.pool == pool
286
- end.compact
287
- if free_addrs.empty?
288
- raise ActionFailed, "No available IPs in pool <#{pool}>"
289
- end
290
- attach_ip(server, free_addrs[0])
291
- end
292
- end
293
-
294
- # Attach given IP to machine
295
- # Code taken from kitchen-openstack driver
296
- # https://github.com/test-kitchen/kitchen-openstack/blob/master/lib/kitchen/driver/openstack.rb#L209-L213
297
- def attach_ip(server, ip)
298
- Chef::Log.info "Attaching floating IP <#{ip}>"
299
- server.associate_address ip
300
- (server.addresses['public'] ||= []) << { 'version' => 4, 'addr' => ip }
301
- end
302
-
303
- # Connect to machine without acquiring it
304
- def connect_to_machine(node)
305
- machine_for(node)
306
- end
307
-
308
- def delete_machine(action_handler, node)
309
- if node['normal']['provisioner_output'] && node['normal']['provisioner_output']['server_id']
310
- server = compute.servers.get(node['normal']['provisioner_output']['server_id'])
311
- action_handler.perform_action "destroy machine #{node['name']} (#{node['normal']['provisioner_output']['server_id']} at #{provisioner_url})" do
312
- server.destroy
313
- end
314
- convergence_strategy_for(node).cleanup_convergence(action_handler, node)
315
- end
316
- end
317
-
318
- def stop_machine(action_handler, node)
319
- # If the machine doesn't exist, we silently do nothing
320
- if node['normal']['provisioner_output'] && node['normal']['provisioner_output']['server_id']
321
- server = compute.servers.get(node['normal']['provisioner_output']['server_id'])
322
- action_handler.perform_action "stop machine #{node['name']} (#{server.id} at #{provisioner_url})" do
323
- server.stop
324
- end
325
- end
326
- end
327
-
328
- def resource_created(machine)
329
- @base_bootstrap_options_for[machine] = current_base_bootstrap_options
330
- end
331
-
332
-
333
- def compute
334
- @compute ||= Fog::Compute.new(compute_options)
335
- end
336
-
337
- def provisioner_url
338
- provider_identifier = case compute_options[:provider]
339
- when 'AWS'
340
- aws_login_info[0]
341
- when 'DigitalOcean'
342
- compute_options[:digitalocean_client_id]
343
- when 'OpenStack'
344
- compute_options[:openstack_auth_url]
345
- else
346
- '???'
347
- end
348
- "fog:#{compute_options[:provider]}:#{provider_identifier}"
349
- end
350
-
351
- # Not meant to be part of public interface
352
- def transport_for(server)
353
- # TODO winrm
354
- create_ssh_transport(server)
355
- end
356
-
357
- protected
358
-
359
- def option_for(node, key)
360
- if node['normal']['provisioner_options'] && node['normal']['provisioner_options'][key.to_s]
361
- node['normal']['provisioner_options'][key.to_s]
362
- elsif compute_options[key]
363
- compute_options[key]
364
- else
365
- DEFAULT_OPTIONS[key]
366
- end
367
- end
368
-
369
- # Returns [ Account ID, User ]
370
- # Account ID is the 12 digit identifier on your Manage Account page in AWS Console. It is used as part of all ARNs identifying resources.
371
- # User is an identifier like "root" or "user/username" or "federated-user/username"
372
- def aws_login_info
373
- @aws_login_info ||= begin
374
- iam = Fog::AWS::IAM.new(:aws_access_key_id => compute_options[:aws_access_key_id], :aws_secret_access_key => compute_options[:aws_secret_access_key])
375
- arn = begin
376
- # TODO it would be nice if Fog let you do this normally ...
377
- iam.send(:request, {
378
- 'Action' => 'GetUser',
379
- :parser => Fog::Parsers::AWS::IAM::GetUser.new
380
- }).body['User']['Arn']
381
- rescue Fog::AWS::IAM::Error
382
- # TODO Someone tell me there is a better way to find out your current
383
- # user ID than this! This is what happens when you use an IAM user
384
- # with default privileges.
385
- if $!.message =~ /AccessDenied.+(arn:aws:iam::\d+:\S+)/
386
- arn = $1
387
- else
388
- raise
389
- end
390
- end
391
- arn.split(':')[4..5]
392
- end
393
- end
394
-
395
- def symbolize_keys(options)
396
- options.inject({}) { |result,(key,value)| result[key.to_sym] = value; result }
397
- end
398
-
399
- def server_for(node)
400
- if node['normal']['provisioner_output'] && node['normal']['provisioner_output']['server_id']
401
- compute.servers.get(node['normal']['provisioner_output']['server_id'])
402
- else
403
- nil
404
- end
405
- end
406
-
407
- def bootstrap_options_for(machine, node)
408
- provisioner_options = node['normal']['provisioner_options'] || {}
409
- bootstrap_options = @base_bootstrap_options_for[machine] || current_base_bootstrap_options
410
- bootstrap_options = bootstrap_options.merge(symbolize_keys(provisioner_options['bootstrap_options'] || {}))
411
- require 'socket'
412
- require 'etc'
413
- tags = {
414
- 'Name' => node['name'],
415
- 'BootstrapChefServer' => machine.chef_server[:chef_server_url],
416
- 'BootstrapHost' => Socket.gethostname,
417
- 'BootstrapUser' => Etc.getlogin,
418
- 'BootstrapNodeName' => node['name']
419
- }
420
- if machine.chef_server[:options] && machine.chef_server[:options][:data_store]
421
- tags['ChefLocalRepository'] = machine.chef_server[:options][:data_store].chef_fs.fs_description
422
- end
423
- # User-defined tags override the ones we set
424
- tags.merge!(bootstrap_options[:tags]) if bootstrap_options[:tags]
425
- bootstrap_options.merge!({ :tags => tags })
426
-
427
- # Provide reasonable defaults for DigitalOcean
428
- if compute_options[:provider] == 'DigitalOcean'
429
- if !bootstrap_options[:image_id]
430
- bootstrap_options[:image_name] ||= 'CentOS 6.4 x32'
431
- bootstrap_options[:image_id] = compute.images.select { |image| image.name == bootstrap_options[:image_name] }.first.id
432
- end
433
- if !bootstrap_options[:flavor_id]
434
- bootstrap_options[:flavor_name] ||= '512MB'
435
- bootstrap_options[:flavor_id] = compute.flavors.select { |flavor| flavor.name == bootstrap_options[:flavor_name] }.first.id
436
- end
437
- if !bootstrap_options[:region_id]
438
- bootstrap_options[:region_name] ||= 'San Francisco 1'
439
- bootstrap_options[:region_id] = compute.regions.select { |region| region.name == bootstrap_options[:region_name] }.first.id
440
- end
441
- bootstrap_options[:ssh_key_ids] ||= [ compute.ssh_keys.select { |k| k.name == bootstrap_options[:key_name] }.first.id ]
442
-
443
- # You don't get to specify name yourself
444
- bootstrap_options[:name] = node['name']
445
- end
446
-
447
- bootstrap_options
448
- end
449
-
450
- def machine_for(node, server = nil)
451
- server ||= server_for(node)
452
- if !server
453
- raise "Server for node #{node['name']} has not been created!"
454
- end
455
-
456
- if node['normal']['provisioner_options'] && node['normal']['provisioner_options']['is_windows']
457
- require 'chef_metal/machine/windows_machine'
458
- ChefMetal::Machine::WindowsMachine.new(node, transport_for(server), convergence_strategy_for(node))
459
- else
460
- require 'chef_metal/machine/unix_machine'
461
- ChefMetal::Machine::UnixMachine.new(node, transport_for(server), convergence_strategy_for(node))
462
- end
463
- end
464
-
465
- def convergence_strategy_for(node)
466
- if node['normal']['provisioner_options'] && node['normal']['provisioner_options']['is_windows']
467
- @windows_convergence_strategy ||= begin
468
- require 'chef_metal/convergence_strategy/install_msi'
469
- ChefMetal::ConvergenceStrategy::InstallMsi.new
470
- end
471
- else
472
- @unix_convergence_strategy ||= begin
473
- require 'chef_metal/convergence_strategy/install_cached'
474
- ChefMetal::ConvergenceStrategy::InstallCached.new
475
- end
476
- end
477
- end
478
-
479
- def ssh_options_for(server)
480
- result = {
481
- # TODO create a user known hosts file
482
- # :user_known_hosts_file => vagrant_ssh_config['UserKnownHostsFile'],
483
- # :paranoid => true,
484
- :auth_methods => [ 'publickey' ],
485
- :keys_only => true,
486
- :host_key_alias => "#{server.id}.#{compute_options[:provider]}"
487
- }
488
- if server.respond_to?(:private_key) && server.private_key
489
- result[:keys] = [ server.private_key ]
490
- elsif server.respond_to?(:key_name) && key_pairs[server.key_name]
491
- # TODO generalize for others?
492
- result[:keys] ||= [ key_pairs[server.key_name].private_key_path ]
493
- else
494
- # TODO need a way to know which key if there were multiple
495
- result[:keys] = [ key_pairs.first[1].private_key_path ]
496
- end
497
- result
498
- end
499
-
500
- def create_ssh_transport(server)
501
- require 'chef_metal/transport/ssh'
502
-
503
- ssh_options = ssh_options_for(server)
504
- # If we're on AWS, the default is to use ubuntu, not root
505
- if compute_options[:provider] == 'AWS'
506
- username = compute_options[:ssh_username] || 'ubuntu'
507
- else
508
- username = compute_options[:ssh_username] || 'root'
509
- end
510
- options = {}
511
- if compute_options[:sudo] || (!compute_options.has_key?(:sudo) && username != 'root')
512
- options[:prefix] = 'sudo '
513
- end
514
-
515
- remote_host = nil
516
- if compute_options[:use_private_ip_for_ssh]
517
- remote_host = server.private_ip_address
518
- elsif !server.public_ip_address
519
- Chef::Log.warn("Server has no public ip address. Using private ip '#{server.private_ip_address}'. Set provisioner option 'use_private_ip_for_ssh' => true if this will always be the case ...")
520
- remote_host = server.private_ip_address
521
- elsif server.public_ip_address
522
- remote_host = server.public_ip_address
523
- else
524
- raise "Server #{server.id} has no private or public IP address!"
525
- end
526
-
527
- #Enable pty by default
528
- options[:ssh_pty_enable] = true
529
-
530
- ChefMetal::Transport::SSH.new(remote_host, username, ssh_options, options)
531
- end
532
-
533
- def wait_until_ready(server, timeout)
534
- transport = nil
535
- _self = self
536
- server.wait_for(timeout) do
537
- if transport
538
- transport.available?
539
- elsif ready?
540
- # Don't create the transport until the machine is ready (we won't have the host till then)
541
- transport = _self.transport_for(server)
542
- transport.available?
543
- else
544
- false
545
- end
546
- end
547
- end
548
- end
549
- end
550
- end