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.
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'