ironfan 3.2.2 → 4.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (66) hide show
  1. data/CHANGELOG.md +5 -0
  2. data/VERSION +1 -1
  3. data/ironfan.gemspec +33 -20
  4. data/lib/chef/knife/cluster_kick.rb +17 -17
  5. data/lib/chef/knife/cluster_kill.rb +13 -7
  6. data/lib/chef/knife/cluster_launch.rb +60 -66
  7. data/lib/chef/knife/cluster_pry.rb +2 -2
  8. data/lib/chef/knife/cluster_show.rb +3 -6
  9. data/lib/chef/knife/cluster_ssh.rb +5 -11
  10. data/lib/chef/knife/cluster_start.rb +2 -4
  11. data/lib/chef/knife/cluster_stop.rb +1 -3
  12. data/lib/chef/knife/cluster_sync.rb +13 -21
  13. data/lib/chef/knife/ironfan_knife_common.rb +11 -9
  14. data/lib/chef/knife/ironfan_script.rb +2 -1
  15. data/lib/gorillib/resolution.rb +119 -0
  16. data/lib/ironfan/broker/computer.rb +316 -0
  17. data/lib/ironfan/broker/drive.rb +21 -0
  18. data/lib/ironfan/broker.rb +37 -0
  19. data/lib/ironfan/builder.rb +14 -0
  20. data/lib/ironfan/deprecated.rb +16 -58
  21. data/lib/ironfan/dsl/cloud.rb +21 -0
  22. data/lib/ironfan/dsl/cluster.rb +27 -0
  23. data/lib/ironfan/dsl/compute.rb +84 -0
  24. data/lib/ironfan/dsl/ec2.rb +260 -0
  25. data/lib/ironfan/dsl/facet.rb +25 -0
  26. data/lib/ironfan/dsl/role.rb +19 -0
  27. data/lib/ironfan/dsl/server.rb +31 -0
  28. data/lib/ironfan/dsl/virtualbox.rb +8 -0
  29. data/lib/ironfan/dsl/volume.rb +45 -0
  30. data/lib/ironfan/dsl.rb +7 -0
  31. data/lib/ironfan/headers.rb +58 -0
  32. data/lib/ironfan/provider/chef/client.rb +77 -0
  33. data/lib/ironfan/provider/chef/node.rb +133 -0
  34. data/lib/ironfan/provider/chef/role.rb +69 -0
  35. data/lib/ironfan/provider/chef.rb +28 -0
  36. data/lib/ironfan/provider/ec2/ebs_volume.rb +137 -0
  37. data/lib/ironfan/provider/ec2/elastic_ip.rb +10 -0
  38. data/lib/ironfan/provider/ec2/key_pair.rb +65 -0
  39. data/lib/ironfan/provider/ec2/machine.rb +258 -0
  40. data/lib/ironfan/provider/ec2/placement_group.rb +24 -0
  41. data/lib/ironfan/provider/ec2/security_group.rb +118 -0
  42. data/lib/ironfan/provider/ec2.rb +47 -0
  43. data/lib/ironfan/provider/virtualbox/machine.rb +10 -0
  44. data/lib/ironfan/provider/virtualbox.rb +8 -0
  45. data/lib/ironfan/provider.rb +139 -0
  46. data/lib/ironfan/requirements.rb +52 -0
  47. data/lib/ironfan.rb +44 -33
  48. metadata +34 -21
  49. data/lib/chef/knife/cluster_vagrant.rb +0 -144
  50. data/lib/chef/knife/vagrant/ironfan_environment.rb +0 -18
  51. data/lib/chef/knife/vagrant/ironfan_provisioners.rb +0 -27
  52. data/lib/chef/knife/vagrant/skeleton_vagrantfile.rb +0 -119
  53. data/lib/ironfan/chef_layer.rb +0 -300
  54. data/lib/ironfan/cloud.rb +0 -323
  55. data/lib/ironfan/cluster.rb +0 -118
  56. data/lib/ironfan/compute.rb +0 -139
  57. data/lib/ironfan/discovery.rb +0 -190
  58. data/lib/ironfan/dsl_builder.rb +0 -99
  59. data/lib/ironfan/facet.rb +0 -143
  60. data/lib/ironfan/fog_layer.rb +0 -196
  61. data/lib/ironfan/private_key.rb +0 -130
  62. data/lib/ironfan/role_implications.rb +0 -58
  63. data/lib/ironfan/security_group.rb +0 -133
  64. data/lib/ironfan/server.rb +0 -291
  65. data/lib/ironfan/server_slice.rb +0 -265
  66. 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,10 @@
1
+ module Ironfan
2
+ class Provider
3
+ class VirtualBox
4
+
5
+ class Machine < Ironfan::IaasProvider::Machine
6
+ end
7
+
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,8 @@
1
+ module Ironfan
2
+ class Provider
3
+
4
+ class VirtualBox < Ironfan::IaasProvider
5
+ end
6
+
7
+ end
8
+ 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'