ironfan 3.2.2 → 4.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.md +5 -0
- data/VERSION +1 -1
- data/ironfan.gemspec +33 -20
- data/lib/chef/knife/cluster_kick.rb +17 -17
- data/lib/chef/knife/cluster_kill.rb +13 -7
- data/lib/chef/knife/cluster_launch.rb +60 -66
- data/lib/chef/knife/cluster_pry.rb +2 -2
- data/lib/chef/knife/cluster_show.rb +3 -6
- data/lib/chef/knife/cluster_ssh.rb +5 -11
- data/lib/chef/knife/cluster_start.rb +2 -4
- data/lib/chef/knife/cluster_stop.rb +1 -3
- data/lib/chef/knife/cluster_sync.rb +13 -21
- data/lib/chef/knife/ironfan_knife_common.rb +11 -9
- data/lib/chef/knife/ironfan_script.rb +2 -1
- data/lib/gorillib/resolution.rb +119 -0
- data/lib/ironfan/broker/computer.rb +316 -0
- data/lib/ironfan/broker/drive.rb +21 -0
- data/lib/ironfan/broker.rb +37 -0
- data/lib/ironfan/builder.rb +14 -0
- data/lib/ironfan/deprecated.rb +16 -58
- data/lib/ironfan/dsl/cloud.rb +21 -0
- data/lib/ironfan/dsl/cluster.rb +27 -0
- data/lib/ironfan/dsl/compute.rb +84 -0
- data/lib/ironfan/dsl/ec2.rb +260 -0
- data/lib/ironfan/dsl/facet.rb +25 -0
- data/lib/ironfan/dsl/role.rb +19 -0
- data/lib/ironfan/dsl/server.rb +31 -0
- data/lib/ironfan/dsl/virtualbox.rb +8 -0
- data/lib/ironfan/dsl/volume.rb +45 -0
- data/lib/ironfan/dsl.rb +7 -0
- data/lib/ironfan/headers.rb +58 -0
- data/lib/ironfan/provider/chef/client.rb +77 -0
- data/lib/ironfan/provider/chef/node.rb +133 -0
- data/lib/ironfan/provider/chef/role.rb +69 -0
- data/lib/ironfan/provider/chef.rb +28 -0
- data/lib/ironfan/provider/ec2/ebs_volume.rb +137 -0
- data/lib/ironfan/provider/ec2/elastic_ip.rb +10 -0
- data/lib/ironfan/provider/ec2/key_pair.rb +65 -0
- data/lib/ironfan/provider/ec2/machine.rb +258 -0
- data/lib/ironfan/provider/ec2/placement_group.rb +24 -0
- data/lib/ironfan/provider/ec2/security_group.rb +118 -0
- data/lib/ironfan/provider/ec2.rb +47 -0
- data/lib/ironfan/provider/virtualbox/machine.rb +10 -0
- data/lib/ironfan/provider/virtualbox.rb +8 -0
- data/lib/ironfan/provider.rb +139 -0
- data/lib/ironfan/requirements.rb +52 -0
- data/lib/ironfan.rb +44 -33
- metadata +34 -21
- data/lib/chef/knife/cluster_vagrant.rb +0 -144
- data/lib/chef/knife/vagrant/ironfan_environment.rb +0 -18
- data/lib/chef/knife/vagrant/ironfan_provisioners.rb +0 -27
- data/lib/chef/knife/vagrant/skeleton_vagrantfile.rb +0 -119
- data/lib/ironfan/chef_layer.rb +0 -300
- data/lib/ironfan/cloud.rb +0 -323
- data/lib/ironfan/cluster.rb +0 -118
- data/lib/ironfan/compute.rb +0 -139
- data/lib/ironfan/discovery.rb +0 -190
- data/lib/ironfan/dsl_builder.rb +0 -99
- data/lib/ironfan/facet.rb +0 -143
- data/lib/ironfan/fog_layer.rb +0 -196
- data/lib/ironfan/private_key.rb +0 -130
- data/lib/ironfan/role_implications.rb +0 -58
- data/lib/ironfan/security_group.rb +0 -133
- data/lib/ironfan/server.rb +0 -291
- data/lib/ironfan/server_slice.rb +0 -265
- data/lib/ironfan/volume.rb +0 -146
@@ -0,0 +1,258 @@
|
|
1
|
+
module Ironfan
|
2
|
+
class Provider
|
3
|
+
class Ec2
|
4
|
+
|
5
|
+
class Machine < Ironfan::IaasProvider::Machine
|
6
|
+
delegate :_dump, :addresses, :ami_launch_index, :ami_launch_index=,
|
7
|
+
:architecture, :architecture=, :availability_zone,
|
8
|
+
:availability_zone=, :block_device_mapping, :block_device_mapping=,
|
9
|
+
:client_token, :client_token=, :collection, :collection=,
|
10
|
+
:connection, :connection=, :console_output, :created_at,
|
11
|
+
:created_at=, :destroy, :dns_name, :dns_name=, :flavor, :flavor=,
|
12
|
+
:flavor_id, :flavor_id=, :groups, :groups=, :iam_instance_profile,
|
13
|
+
:iam_instance_profile=, :iam_instance_profile_arn=,
|
14
|
+
:iam_instance_profile_name=, :id, :id=, :identity, :identity=,
|
15
|
+
:image_id, :image_id=, :instance_initiated_shutdown_behavior,
|
16
|
+
:instance_initiated_shutdown_behavior=, :ip_address, :kernel_id,
|
17
|
+
:kernel_id=, :key_name, :key_name=, :key_pair, :key_pair=,
|
18
|
+
:monitor=, :monitoring, :monitoring=, :network_interfaces,
|
19
|
+
:network_interfaces=, :new_record?, :password, :password=,
|
20
|
+
:placement_group, :placement_group=, :platform, :platform=,
|
21
|
+
:private_dns_name, :private_dns_name=, :private_ip_address,
|
22
|
+
:private_ip_address=, :private_key, :private_key=,
|
23
|
+
:private_key_path, :private_key_path=, :product_codes,
|
24
|
+
:product_codes=, :public_ip_address, :public_ip_address=,
|
25
|
+
:public_key, :public_key=, :public_key_path, :public_key_path=,
|
26
|
+
:ramdisk_id, :ramdisk_id=, :ready?, :reason, :reason=, :reboot,
|
27
|
+
:reload, :requires, :requires_one, :root_device_name,
|
28
|
+
:root_device_name=, :root_device_type, :root_device_type=, :save,
|
29
|
+
:scp, :scp_download, :scp_upload, :security_group_ids,
|
30
|
+
:security_group_ids=, :setup, :ssh, :ssh_port, :sshable?, :start,
|
31
|
+
:state, :state=, :state_reason, :state_reason=, :stop, :subnet_id,
|
32
|
+
:subnet_id=, :symbolize_keys, :tags, :tags=, :tenancy, :tenancy=,
|
33
|
+
:user_data, :user_data=, :username, :username=, :volumes, :vpc_id,
|
34
|
+
:vpc_id=, :wait_for,
|
35
|
+
:to => :adaptee
|
36
|
+
|
37
|
+
|
38
|
+
def self.shared?() false; end
|
39
|
+
def self.multiple?() false; end
|
40
|
+
# def self.resource_type() Ironfan::IaasProvider::Machine; end
|
41
|
+
def self.resource_type() :machine; end
|
42
|
+
def self.expected_ids(computer) [computer.server.fullname]; end
|
43
|
+
|
44
|
+
def name
|
45
|
+
return id if tags.empty?
|
46
|
+
tags["Name"] || tags["name"] || id
|
47
|
+
end
|
48
|
+
|
49
|
+
def public_hostname() dns_name; end
|
50
|
+
|
51
|
+
def created?
|
52
|
+
not ['terminated', 'shutting-down'].include? state
|
53
|
+
end
|
54
|
+
def running?
|
55
|
+
state == "running"
|
56
|
+
end
|
57
|
+
def stopped?
|
58
|
+
state == "stopped"
|
59
|
+
end
|
60
|
+
|
61
|
+
def start
|
62
|
+
adaptee.start
|
63
|
+
adaptee.wait_for{ state == 'pending' }
|
64
|
+
end
|
65
|
+
|
66
|
+
def stop
|
67
|
+
adaptee.stop
|
68
|
+
adaptee.wait_for{ state == 'stopping' }
|
69
|
+
end
|
70
|
+
|
71
|
+
def to_display(style,values={})
|
72
|
+
# style == :minimal
|
73
|
+
values["State"] = state.to_sym
|
74
|
+
values["MachineID"] = id
|
75
|
+
values["Public IP"] = public_ip_address
|
76
|
+
values["Private IP"] = private_ip_address
|
77
|
+
values["Created On"] = created_at.to_date
|
78
|
+
return values if style == :minimal
|
79
|
+
|
80
|
+
# style == :default
|
81
|
+
values["Flavor"] = flavor_id
|
82
|
+
values["AZ"] = availability_zone
|
83
|
+
return values if style == :default
|
84
|
+
|
85
|
+
# style == :expanded
|
86
|
+
values["Image"] = image_id
|
87
|
+
values["Volumes"] = volumes.map(&:id).join(', ')
|
88
|
+
values["SSH Key"] = key_name
|
89
|
+
values
|
90
|
+
end
|
91
|
+
|
92
|
+
#
|
93
|
+
# Discovery
|
94
|
+
#
|
95
|
+
def self.load!(cluster=nil)
|
96
|
+
Ec2.connection.servers.each do |fs|
|
97
|
+
machine = new(:adaptee => fs)
|
98
|
+
if recall? machine.name
|
99
|
+
raise 'duplicate'
|
100
|
+
machine.bogus << :duplicate_machines
|
101
|
+
recall(machine.name).bogus << :duplicate_machines
|
102
|
+
remember machine, :append_id => "duplicate:#{machine.id}"
|
103
|
+
elsif machine.created?
|
104
|
+
remember machine
|
105
|
+
else
|
106
|
+
remember machine, :append_id => "terminated:#{machine.id}"
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
# Find active machines that haven't matched, but should have,
|
112
|
+
# make sure all bogus machines have a computer to attach to
|
113
|
+
# for display purposes
|
114
|
+
def self.validate_resources!(computers)
|
115
|
+
recall.each_value do |machine|
|
116
|
+
next unless machine.users.empty? and machine.name
|
117
|
+
if machine.name.match("^#{computers.cluster.name}")
|
118
|
+
machine.bogus << :unexpected_machine
|
119
|
+
end
|
120
|
+
next unless machine.bogus?
|
121
|
+
fake = Ironfan::Broker::Computer.new
|
122
|
+
fake[:machine] = machine
|
123
|
+
fake.name = machine.name
|
124
|
+
machine.users << fake
|
125
|
+
computers << fake
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
#
|
130
|
+
# Manipulation
|
131
|
+
#
|
132
|
+
def self.create!(computer)
|
133
|
+
Chef::Log.warn("CODE SMELL: overly large method: #{caller}")
|
134
|
+
return if computer.machine? and computer.machine.created?
|
135
|
+
Ironfan.step(computer.name,"creating cloud machine", :green)
|
136
|
+
# lint_fog
|
137
|
+
launch_desc = fog_launch_description(computer)
|
138
|
+
Chef::Log.debug(JSON.pretty_generate(launch_desc))
|
139
|
+
|
140
|
+
Ironfan.safely do
|
141
|
+
fog_server = Ec2.connection.servers.create(launch_desc)
|
142
|
+
machine = Machine.new(:adaptee => fog_server)
|
143
|
+
computer.machine = machine
|
144
|
+
remember machine, :id => computer.name
|
145
|
+
|
146
|
+
fog_server.wait_for { ready? }
|
147
|
+
end
|
148
|
+
|
149
|
+
# tag the computer correctly
|
150
|
+
tags = {
|
151
|
+
'cluster' => computer.server.cluster_name,
|
152
|
+
'facet' => computer.server.facet_name,
|
153
|
+
'index' => computer.server.index,
|
154
|
+
'name' => computer.name,
|
155
|
+
'Name' => computer.name,
|
156
|
+
}
|
157
|
+
Ec2.ensure_tags(tags,computer.machine)
|
158
|
+
|
159
|
+
# register the new volumes for later save!, and tag appropriately
|
160
|
+
computer.machine.volumes.each do |v|
|
161
|
+
ebs_vol = Ec2::EbsVolume.register v
|
162
|
+
drive = computer.drives.values.select do |drive|
|
163
|
+
drive.volume.device == ebs_vol.device
|
164
|
+
end.first
|
165
|
+
drive.disk = ebs_vol
|
166
|
+
|
167
|
+
vol_name = "#{computer.name}-#{drive.volume.name}"
|
168
|
+
tags['name'] = vol_name
|
169
|
+
tags['Name'] = vol_name
|
170
|
+
Ec2.ensure_tags(tags,ebs_vol)
|
171
|
+
end
|
172
|
+
end
|
173
|
+
def self.fog_launch_description(computer)
|
174
|
+
cloud = computer.server.cloud(:ec2)
|
175
|
+
user_data_hsh = {
|
176
|
+
:chef_server => Chef::Config[:chef_server_url],
|
177
|
+
#:validation_client_name => Chef::Config[:validation_client_name],
|
178
|
+
#
|
179
|
+
:node_name => computer.name,
|
180
|
+
:organization => Chef::Config[:organization],
|
181
|
+
:cluster_name => computer.server.cluster_name,
|
182
|
+
:facet_name => computer.server.facet_name,
|
183
|
+
:facet_index => computer.server.index,
|
184
|
+
:client_key => computer[:client].private_key
|
185
|
+
}
|
186
|
+
|
187
|
+
# Fog does not actually create tags when it creates a server;
|
188
|
+
# they and permanence are applied during sync
|
189
|
+
keypair = cloud.keypair || computer.server.cluster_name
|
190
|
+
description = {
|
191
|
+
:image_id => cloud.image_id,
|
192
|
+
:flavor_id => cloud.flavor,
|
193
|
+
:vpc_id => cloud.vpc,
|
194
|
+
:subnet_id => cloud.subnet,
|
195
|
+
:groups => cloud.security_groups.keys,
|
196
|
+
:key_name => keypair.to_s,
|
197
|
+
:user_data => JSON.pretty_generate(user_data_hsh),
|
198
|
+
:block_device_mapping => block_device_mapping(computer),
|
199
|
+
:availability_zone => cloud.default_availability_zone,
|
200
|
+
:monitoring => cloud.monitoring,
|
201
|
+
}
|
202
|
+
|
203
|
+
if cloud.flavor_info[:placement_groupable]
|
204
|
+
ui.warn "1.3.1 and earlier versions of Fog don't correctly support placement groups, so your nodes will land willy-nilly. We're working on a fix"
|
205
|
+
description[:placement] = { 'groupName' => cloud.placement_group.to_s }
|
206
|
+
end
|
207
|
+
description
|
208
|
+
end
|
209
|
+
# An array of hashes with dorky-looking keys, just like Fog wants it.
|
210
|
+
def self.block_device_mapping(computer)
|
211
|
+
computer.drives.values.map do |drive|
|
212
|
+
next if drive.disk # Don't create any disc already satisfied
|
213
|
+
volume = drive.volume or next
|
214
|
+
hsh = { 'DeviceName' => volume.device }
|
215
|
+
if volume.attachable == 'ephemeral'
|
216
|
+
hsh['VirtualName'] = drive.name
|
217
|
+
# if set for creation at launch (and not already created)
|
218
|
+
elsif drive.node[:volume_id].blank? && volume.create_at_launch
|
219
|
+
if volume.snapshot_id.blank? && volume.size.blank?
|
220
|
+
raise "Must specify a size or a snapshot ID for #{volume}"
|
221
|
+
end
|
222
|
+
hsh['Ebs.SnapshotId'] = volume.snapshot_id if volume.snapshot_id.present?
|
223
|
+
hsh['Ebs.VolumeSize'] = volume.size.to_s if volume.size.present?
|
224
|
+
hsh['Ebs.DeleteOnTermination'] = (not volume.keep).to_s
|
225
|
+
else
|
226
|
+
next
|
227
|
+
end
|
228
|
+
hsh
|
229
|
+
end.compact
|
230
|
+
end
|
231
|
+
|
232
|
+
def self.destroy!(computer)
|
233
|
+
return unless computer.machine?
|
234
|
+
forget computer.machine.name
|
235
|
+
computer.machine.destroy
|
236
|
+
computer.machine.reload # show the node as shutting down
|
237
|
+
end
|
238
|
+
|
239
|
+
def self.save!(computer)
|
240
|
+
return unless computer.machine?
|
241
|
+
# the EC2 API does not surface disable_api_termination as a value, so we
|
242
|
+
# have to set it every time.
|
243
|
+
permanent = computer.server.cloud(:ec2).permanent
|
244
|
+
return unless computer.created?
|
245
|
+
Ironfan.step(computer.name, "setting termination flag #{permanent}", :blue)
|
246
|
+
Ironfan.unless_dry_run do
|
247
|
+
Ironfan.safely do
|
248
|
+
Ec2.connection.modify_instance_attribute( computer.machine.id,
|
249
|
+
{'DisableApiTermination.Value' => computer.permanent?, })
|
250
|
+
end
|
251
|
+
end
|
252
|
+
end
|
253
|
+
end
|
254
|
+
|
255
|
+
end
|
256
|
+
end
|
257
|
+
end
|
258
|
+
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Ironfan
|
2
|
+
class Provider
|
3
|
+
class Ec2
|
4
|
+
|
5
|
+
# Fog::AWS doesn't seem to have native models for PlacementGroup,
|
6
|
+
# using Hash semantics instead
|
7
|
+
class PlacementGroup < Ironfan::Provider::Resource
|
8
|
+
delegate :[],:[]=, :to => :adaptee
|
9
|
+
|
10
|
+
def name()
|
11
|
+
self["groupName"]
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.load!(cluster)
|
15
|
+
result = Ec2.connection.describe_placement_groups
|
16
|
+
result.body["placementGroupSet"].each do |group|
|
17
|
+
register group unless group.blank?
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,118 @@
|
|
1
|
+
module Ironfan
|
2
|
+
class Provider
|
3
|
+
class Ec2
|
4
|
+
|
5
|
+
class SecurityGroup < Ironfan::Provider::Resource
|
6
|
+
delegate :_dump, :authorize_group_and_owner, :authorize_port_range,
|
7
|
+
:collection, :collection=, :connection, :connection=, :description,
|
8
|
+
:description=, :destroy, :group_id, :group_id=, :identity,
|
9
|
+
:identity=, :ip_permissions, :ip_permissions=, :name, :name=,
|
10
|
+
:new_record?, :owner_id, :owner_id=, :reload, :requires,
|
11
|
+
:requires_one, :revoke_group_and_owner, :revoke_port_range, :save,
|
12
|
+
:symbolize_keys, :vpc_id, :vpc_id=, :wait_for,
|
13
|
+
:to => :adaptee
|
14
|
+
field :ensured, :boolean, :default => false
|
15
|
+
|
16
|
+
def self.shared?() true; end
|
17
|
+
def self.multiple?() true; end
|
18
|
+
def self.resource_type() :security_group; end
|
19
|
+
def self.expected_ids(computer)
|
20
|
+
computer.server.cloud(:ec2).security_groups.keys.map{|k| k.to_s}.uniq
|
21
|
+
end
|
22
|
+
|
23
|
+
#
|
24
|
+
# Discovery
|
25
|
+
#
|
26
|
+
def self.load!(cluster=nil)
|
27
|
+
Ec2.connection.security_groups.each do |sg|
|
28
|
+
remember SecurityGroup.new(:adaptee => sg) unless sg.blank?
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
#
|
33
|
+
# Manipulation
|
34
|
+
#
|
35
|
+
|
36
|
+
def self.create!(computer)
|
37
|
+
return unless Ec2.applicable computer
|
38
|
+
|
39
|
+
ensure_groups(computer)
|
40
|
+
groups = computer.server.cloud(:ec2).security_groups.keys.map{|k| k.to_s}.uniq
|
41
|
+
# Only handle groups that don't already exist
|
42
|
+
groups.delete_if {|group| recall? group.to_s }
|
43
|
+
return if groups.empty?
|
44
|
+
|
45
|
+
Ironfan.step(computer.server.cluster_name, "creating security groups", :blue)
|
46
|
+
groups.each do |group|
|
47
|
+
Ironfan.step(group, " creating #{group} security group", :blue)
|
48
|
+
Ec2.connection.create_security_group(group.to_s,"Ironfan created group #{group}")
|
49
|
+
end
|
50
|
+
load! # Get the native groups via reload
|
51
|
+
end
|
52
|
+
|
53
|
+
def self.save!(computer)
|
54
|
+
return unless Ec2.applicable computer
|
55
|
+
|
56
|
+
create!(computer) # Make sure the security groups exist
|
57
|
+
security_groups = computer.server.cloud(:ec2).security_groups.values
|
58
|
+
dsl_groups = security_groups.select do |dsl_group|
|
59
|
+
not (recall? dsl_group or recall(dsl_group.name).ensured) and \
|
60
|
+
not (dsl_group.range_authorizations + dsl_group.group_authorized_by).empty?
|
61
|
+
end.compact
|
62
|
+
return if dsl_groups.empty?
|
63
|
+
|
64
|
+
Ironfan.step(computer.server.cluster_name, "ensuring security group permissions", :blue)
|
65
|
+
dsl_groups.each do |dsl_group|
|
66
|
+
dsl_group.group_authorized_by.each do |other_group|
|
67
|
+
Ironfan.step(dsl_group.name, " ensuring access to #{other_group}", :blue)
|
68
|
+
options = {:group => "#{Ec2.aws_account_id}:#{dsl_group.name}"}
|
69
|
+
safely_authorize(other_group,1..65535,options)
|
70
|
+
end
|
71
|
+
|
72
|
+
dsl_group.range_authorizations.each do |range_auth|
|
73
|
+
range, cidr, protocol = range_auth
|
74
|
+
step_message = " ensuring #{protocol} access from #{cidr} to #{range}"
|
75
|
+
Ironfan.step(dsl_group.name, step_message, :blue)
|
76
|
+
options = {:cidr_ip => cidr, :ip_protocol => protocol}
|
77
|
+
safely_authorize(dsl_group.name,range,options)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
#
|
83
|
+
# Utility
|
84
|
+
#
|
85
|
+
def self.ensure_groups(computer)
|
86
|
+
return unless Ec2.applicable computer
|
87
|
+
# Ensure the security_groups include those for cluster & facet
|
88
|
+
# FIXME: This violates the DSL's immutability; it should be
|
89
|
+
# something calculated from within the DSL construction
|
90
|
+
Chef::Log.warn("CODE SMELL: violation of DSL immutability: #{caller}")
|
91
|
+
cloud = computer.server.cloud(:ec2)
|
92
|
+
c_group = cloud.security_group(computer.server.cluster_name)
|
93
|
+
c_group.authorized_by_group(c_group.name)
|
94
|
+
facet_name = "#{computer.server.cluster_name}-#{computer.server.facet_name}"
|
95
|
+
cloud.security_group(facet_name)
|
96
|
+
end
|
97
|
+
|
98
|
+
# Try an authorization, ignoring duplicates (this is easier than correlating).
|
99
|
+
# Do so for both TCP and UDP, unless only one is specified
|
100
|
+
def self.safely_authorize(group_name,range,options)
|
101
|
+
unless options[:ip_protocol]
|
102
|
+
safely_authorize(group_name,range,options.merge(:ip_protocol => 'tcp'))
|
103
|
+
safely_authorize(group_name,range,options.merge(:ip_protocol => 'udp'))
|
104
|
+
return
|
105
|
+
end
|
106
|
+
|
107
|
+
fog_group = recall(group_name) or raise "unrecognized group: #{group_name}"
|
108
|
+
begin
|
109
|
+
fog_group.authorize_port_range(range,options)
|
110
|
+
rescue Fog::Compute::AWS::Error => e # InvalidPermission.Duplicate
|
111
|
+
Chef::Log.debug("ignoring #{e}")
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
module Ironfan
|
2
|
+
class Provider
|
3
|
+
|
4
|
+
class Ec2 < Ironfan::IaasProvider
|
5
|
+
|
6
|
+
def self.resources
|
7
|
+
[ Machine, EbsVolume, KeyPair, SecurityGroup ]
|
8
|
+
end
|
9
|
+
|
10
|
+
#
|
11
|
+
# Utility functions
|
12
|
+
#
|
13
|
+
def self.connection
|
14
|
+
@@connection ||= Fog::Compute.new({
|
15
|
+
:provider => 'AWS',
|
16
|
+
:aws_access_key_id => Chef::Config[:knife][:aws_access_key_id],
|
17
|
+
:aws_secret_access_key => Chef::Config[:knife][:aws_secret_access_key],
|
18
|
+
:region => Chef::Config[:knife][:region]
|
19
|
+
})
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.aws_account_id()
|
23
|
+
Chef::Config[:knife][:aws_account_id]
|
24
|
+
end
|
25
|
+
|
26
|
+
# Ensure that a fog object (machine, volume, etc.) has the proper tags on it
|
27
|
+
def self.ensure_tags(tags,fog)
|
28
|
+
tags.delete_if {|k, v| fog.tags[k] == v.to_s rescue false }
|
29
|
+
return if tags.empty?
|
30
|
+
|
31
|
+
Ironfan.step(fog.name,"tagging with #{tags.inspect}", :green)
|
32
|
+
tags.each do |k, v|
|
33
|
+
Chef::Log.debug( "tagging #{fog.name} with #{k} = #{v}" )
|
34
|
+
Ironfan.safely do
|
35
|
+
config = {:key => k, :value => v.to_s, :resource_id => fog.id }
|
36
|
+
connection.tags.create(config)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def self.applicable(computer)
|
42
|
+
computer.server and computer.server.clouds.include?(:ec2)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,139 @@
|
|
1
|
+
# Providers present a lightweight wrapper for various third-party services,
|
2
|
+
# such as Chef's node and client APIs, and Amazon's EC2 APIs. This allows
|
3
|
+
# Ironfan ask specialized questions (such as whether a given resource
|
4
|
+
# matches
|
5
|
+
module Ironfan
|
6
|
+
class Provider < Builder
|
7
|
+
|
8
|
+
def self.receive(obj,&block)
|
9
|
+
obj[:_type] = case obj[:name]
|
10
|
+
when :chef; Chef
|
11
|
+
when :ec2; Ec2
|
12
|
+
when :virtualbox; VirtualBox
|
13
|
+
else; raise "Unsupported provider #{obj[:name]}"
|
14
|
+
end unless native?(obj)
|
15
|
+
super
|
16
|
+
end
|
17
|
+
|
18
|
+
def resources() self.class.resources; end
|
19
|
+
def self.resources
|
20
|
+
raise "missing #{self.class}.resources declaration"
|
21
|
+
end
|
22
|
+
|
23
|
+
#
|
24
|
+
# Discovery
|
25
|
+
#
|
26
|
+
def self.load(cluster)
|
27
|
+
Ironfan.delegate_to(resources) { load! cluster }
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.validate(computers)
|
31
|
+
Ironfan.delegate_to(resources) { validate_resources! computers }
|
32
|
+
end
|
33
|
+
|
34
|
+
|
35
|
+
class Resource < Builder
|
36
|
+
@@known = {}
|
37
|
+
field :adaptee, Whatever
|
38
|
+
field :bogus, Array, :default => []
|
39
|
+
attr_accessor :owner
|
40
|
+
attr_accessor :users
|
41
|
+
def users() @users ||= []; end;
|
42
|
+
|
43
|
+
def bogus?() !bogus.empty?; end
|
44
|
+
|
45
|
+
#
|
46
|
+
# Flags
|
47
|
+
#
|
48
|
+
# Non-shared resources live and die with the computer
|
49
|
+
def self.shared?() true; end
|
50
|
+
# Can multiple instances of this resource be associated with the computer?
|
51
|
+
def self.multiple?() false; end
|
52
|
+
|
53
|
+
#
|
54
|
+
# Discovery
|
55
|
+
#
|
56
|
+
def self.load!(*p) Ironfan.noop(self,__method__,*p); end
|
57
|
+
def self.correlate!(*p) Ironfan.noop(self,__method__,*p); end
|
58
|
+
def self.validate_computer!(*p) Ironfan.noop(self,__method__,*p); end
|
59
|
+
def self.validate_resources!(*p) Ironfan.noop(self,__method__,*p); end
|
60
|
+
|
61
|
+
def on_correlate(*p) Ironfan.noop(self,__method__,*p); end
|
62
|
+
|
63
|
+
#
|
64
|
+
# Manipulation
|
65
|
+
#
|
66
|
+
def self.create!(*p) Ironfan.noop(self,__method__,*p); end
|
67
|
+
def self.save!(*p) Ironfan.noop(self,__method__,*p); end
|
68
|
+
def self.destroy!(*p) Ironfan.noop(self,__method__,*p); end
|
69
|
+
|
70
|
+
#
|
71
|
+
# Utilities
|
72
|
+
#
|
73
|
+
[:shared?, :multiple?, :load!,:correlate!,:validate_computer!,
|
74
|
+
:validate_resources!,:create!,:save!,:destroy!].each do |method_name|
|
75
|
+
define_method(method_name) {|*p| self.class.send(method_name,*p) }
|
76
|
+
end
|
77
|
+
|
78
|
+
def self.remember(resource,options={})
|
79
|
+
index = options[:id] || resource.name
|
80
|
+
index += options[:append_id] if options[:append_id]
|
81
|
+
self.known[index] = resource
|
82
|
+
end
|
83
|
+
|
84
|
+
# Register and return the (adapted) object with the collection
|
85
|
+
def self.register(native)
|
86
|
+
result = new(:adaptee => native)
|
87
|
+
remember result unless result.nil?
|
88
|
+
end
|
89
|
+
|
90
|
+
def self.recall?(id)
|
91
|
+
self.known.include? id
|
92
|
+
end
|
93
|
+
|
94
|
+
def self.recall(id=nil)
|
95
|
+
return self.known if id.nil?
|
96
|
+
self.known[id]
|
97
|
+
end
|
98
|
+
|
99
|
+
def self.forget(id)
|
100
|
+
self.known.delete(id)
|
101
|
+
end
|
102
|
+
|
103
|
+
# Provide a separate namespace in @@known for each subclass
|
104
|
+
def self.known
|
105
|
+
@@known[self.name] ||= {}
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
end
|
110
|
+
|
111
|
+
class IaasProvider < Provider
|
112
|
+
def self.machine_class
|
113
|
+
self.const_get(:Machine)
|
114
|
+
end
|
115
|
+
|
116
|
+
#
|
117
|
+
# Manipulation
|
118
|
+
#
|
119
|
+
def ensure_prerequisites!(computers)
|
120
|
+
# Create all things that aren't machines
|
121
|
+
targets = resources.reject {|type| type < IaasProvider::Machine}
|
122
|
+
computers.each do |computer|
|
123
|
+
delegate_to(targets) { create! computer }
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
def save!(computers)
|
128
|
+
computers.each do |computer|
|
129
|
+
delegate_to(resources) { save! computer }
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
class Machine < Resource
|
134
|
+
# A Machine lives and dies with its Computer
|
135
|
+
def self.shared?() false; end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
# Gorillib core classes
|
2
|
+
require 'gorillib/builder'
|
3
|
+
require 'gorillib/resolution'
|
4
|
+
|
5
|
+
|
6
|
+
# Pre-declaration of class hierarchy
|
7
|
+
require 'ironfan/headers'
|
8
|
+
|
9
|
+
|
10
|
+
# DSL for cluster descriptions
|
11
|
+
require 'ironfan/dsl'
|
12
|
+
require 'ironfan/builder'
|
13
|
+
|
14
|
+
require 'ironfan/dsl/compute'
|
15
|
+
require 'ironfan/dsl/server'
|
16
|
+
require 'ironfan/dsl/facet'
|
17
|
+
require 'ironfan/dsl/cluster'
|
18
|
+
|
19
|
+
require 'ironfan/dsl/role'
|
20
|
+
require 'ironfan/dsl/volume'
|
21
|
+
|
22
|
+
require 'ironfan/dsl/cloud'
|
23
|
+
require 'ironfan/dsl/ec2'
|
24
|
+
|
25
|
+
|
26
|
+
# Providers for specific resources
|
27
|
+
require 'ironfan/provider'
|
28
|
+
|
29
|
+
require 'ironfan/provider/chef'
|
30
|
+
require 'ironfan/provider/chef/client'
|
31
|
+
require 'ironfan/provider/chef/node'
|
32
|
+
require 'ironfan/provider/chef/role'
|
33
|
+
|
34
|
+
require 'ironfan/provider/ec2'
|
35
|
+
require 'ironfan/provider/ec2/ebs_volume'
|
36
|
+
require 'ironfan/provider/ec2/machine'
|
37
|
+
require 'ironfan/provider/ec2/key_pair'
|
38
|
+
require 'ironfan/provider/ec2/placement_group'
|
39
|
+
require 'ironfan/provider/ec2/security_group'
|
40
|
+
|
41
|
+
require 'ironfan/provider/virtualbox'
|
42
|
+
require 'ironfan/provider/virtualbox/machine'
|
43
|
+
|
44
|
+
|
45
|
+
# Broker classes to coordinate DSL expectations and provider resources
|
46
|
+
require 'ironfan/broker'
|
47
|
+
require 'ironfan/broker/computer'
|
48
|
+
require 'ironfan/broker/drive'
|
49
|
+
|
50
|
+
|
51
|
+
# Calls that are slated to go away
|
52
|
+
require 'ironfan/deprecated'
|