ironfan 3.2.2 → 4.0.0
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.
- 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'
|