chef-metal 0.5 → 0.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +29 -0
- data/lib/chef_metal/convergence_strategy/precreate_chef_objects.rb +14 -0
- data/lib/chef_metal/version.rb +1 -1
- metadata +31 -13
- data/lib/chef/provider/fog_key_pair.rb +0 -169
- data/lib/chef/provider/vagrant_box.rb +0 -44
- data/lib/chef/provider/vagrant_cluster.rb +0 -42
- data/lib/chef/resource/fog_key_pair.rb +0 -34
- data/lib/chef/resource/vagrant_box.rb +0 -18
- data/lib/chef/resource/vagrant_cluster.rb +0 -16
- data/lib/chef_metal/fog.rb +0 -20
- data/lib/chef_metal/provisioner/fog_provisioner.rb +0 -550
- data/lib/chef_metal/provisioner/vagrant_provisioner.rb +0 -348
- data/lib/chef_metal/provisioner_init/fog_init.rb +0 -4
- data/lib/chef_metal/provisioner_init/vagrant_cluster_init.rb +0 -4
- data/lib/chef_metal/vagrant.rb +0 -39
@@ -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
|
data/lib/chef_metal/fog.rb
DELETED
@@ -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
|