ironfan 3.1.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (59) hide show
  1. data/.gitignore +51 -0
  2. data/.rspec +3 -0
  3. data/CHANGELOG.md +130 -0
  4. data/Gemfile +26 -0
  5. data/LICENSE.md +201 -0
  6. data/README.md +328 -0
  7. data/Rakefile +104 -0
  8. data/TODO.md +16 -0
  9. data/VERSION +1 -0
  10. data/chefignore +41 -0
  11. data/cluster_chef-knife.gemspec +123 -0
  12. data/cluster_chef.gemspec +111 -0
  13. data/config/client.rb +59 -0
  14. data/config/proxy.pac +12 -0
  15. data/config/ubuntu10.04-ironfan.erb +157 -0
  16. data/config/ubuntu11.10-ironfan.erb +145 -0
  17. data/ironfan.gemspec +121 -0
  18. data/lib/chef/knife/bootstrap/ubuntu10.04-ironfan.erb +157 -0
  19. data/lib/chef/knife/bootstrap/ubuntu11.10-ironfan.erb +145 -0
  20. data/lib/chef/knife/cluster_bootstrap.rb +74 -0
  21. data/lib/chef/knife/cluster_kick.rb +94 -0
  22. data/lib/chef/knife/cluster_kill.rb +73 -0
  23. data/lib/chef/knife/cluster_launch.rb +164 -0
  24. data/lib/chef/knife/cluster_list.rb +50 -0
  25. data/lib/chef/knife/cluster_proxy.rb +126 -0
  26. data/lib/chef/knife/cluster_show.rb +61 -0
  27. data/lib/chef/knife/cluster_ssh.rb +141 -0
  28. data/lib/chef/knife/cluster_start.rb +40 -0
  29. data/lib/chef/knife/cluster_stop.rb +43 -0
  30. data/lib/chef/knife/cluster_sync.rb +77 -0
  31. data/lib/chef/knife/generic_command.rb +66 -0
  32. data/lib/chef/knife/knife_common.rb +195 -0
  33. data/lib/ironfan.rb +143 -0
  34. data/lib/ironfan/chef_layer.rb +299 -0
  35. data/lib/ironfan/cloud.rb +412 -0
  36. data/lib/ironfan/cluster.rb +118 -0
  37. data/lib/ironfan/compute.rb +153 -0
  38. data/lib/ironfan/deprecated.rb +33 -0
  39. data/lib/ironfan/discovery.rb +177 -0
  40. data/lib/ironfan/dsl_object.rb +124 -0
  41. data/lib/ironfan/facet.rb +144 -0
  42. data/lib/ironfan/fog_layer.rb +150 -0
  43. data/lib/ironfan/private_key.rb +130 -0
  44. data/lib/ironfan/role_implications.rb +58 -0
  45. data/lib/ironfan/security_group.rb +119 -0
  46. data/lib/ironfan/server.rb +281 -0
  47. data/lib/ironfan/server_slice.rb +260 -0
  48. data/lib/ironfan/volume.rb +157 -0
  49. data/spec/ironfan/cluster_spec.rb +13 -0
  50. data/spec/ironfan/facet_spec.rb +69 -0
  51. data/spec/ironfan/server_slice_spec.rb +19 -0
  52. data/spec/ironfan/server_spec.rb +112 -0
  53. data/spec/ironfan_spec.rb +193 -0
  54. data/spec/spec_helper.rb +50 -0
  55. data/spec/spec_helper/dummy_chef.rb +25 -0
  56. data/spec/test_config.rb +20 -0
  57. data/tasks/chef_config.rake +38 -0
  58. data/tasks/jeweler_use_alt_branch.rake +53 -0
  59. metadata +217 -0
@@ -0,0 +1,299 @@
1
+ #
2
+ # OK so things get a little fishy here, and it's all Opscode's fault ;-)
3
+ #
4
+ # There's currently no API for setting ACLs. However, if the *client the
5
+ # node will run as* is the *client that creates the node*, it is granted the
6
+ # correct permissions.
7
+ #
8
+ # * client exists, node exists: don't need to do anything. We trust that permissions are correct.
9
+ # * client absent, node exists: client created, node is fine. We trust that permissions are correct.
10
+ # * client absent, node absent: client created, so have key; client creates node, so it has write permissions.
11
+ # * client exists, node absent: FAIL.
12
+ #
13
+ # The current implementation persists the client keys locally to your
14
+ # Chef::Config[:client_key_dir]. This is insecure and unmanageable; and the
15
+ # node will shortly re-register the key, making it invalide anyway.
16
+ #
17
+ # If the client's private_key is empty/wrong and the node is absent, it will
18
+ # cause an error. in that case, you can:
19
+ #
20
+ # * create the node yourself in the management console, and
21
+ # grant access to its eponymous client; OR
22
+ # * nuke the client key from orbit (it's the only way to be sure) and re-run,
23
+ # taking all responsibility for the catastrophic results of an errant nuke; OR
24
+ # * wait for opscode to open API access for ACLs.
25
+ #
26
+ #
27
+
28
+ module Ironfan
29
+ module DryRunnable
30
+ # Run given block unless in dry_run mode (Ironfan.chef_config[:dry_run]
31
+ # is true)
32
+ def unless_dry_run
33
+ if Ironfan.chef_config[:dry_run]
34
+ ui.info(" ... but not really (#{ui.color("dry run", :bold, :yellow)} for server #{name})")
35
+ else
36
+ yield
37
+ end
38
+ end
39
+ end
40
+
41
+ ComputeBuilder.class_eval do
42
+ def new_chef_role(role_name, cluster, facet=nil)
43
+ chef_role = Chef::Role.new
44
+ chef_role.name role_name
45
+ chef_role.description "Ironfan generated role for #{[cluster_name, facet_name].compact.join('-')}" unless chef_role.description
46
+ chef_role.instance_eval{ @cluster = cluster; @facet = facet; }
47
+ @chef_roles << chef_role
48
+ chef_role
49
+ end
50
+ end
51
+
52
+ ServerSlice.class_eval do
53
+ include DryRunnable
54
+ def sync_roles
55
+ step(" syncing cluster and facet roles")
56
+ unless_dry_run do
57
+ chef_roles.each(&:save)
58
+ end
59
+ end
60
+ end
61
+
62
+ #
63
+ # Ironfan::Server methods that handle chef actions
64
+ #
65
+ Server.class_eval do
66
+ include DryRunnable
67
+
68
+ # The chef client, if it already exists in the server.
69
+ # Use the 'ensure' method to create/update it.
70
+ def chef_client
71
+ return @chef_client unless @chef_client.nil?
72
+ @chef_client = cluster.find_client(fullname) || false
73
+ end
74
+
75
+ # The chef node, if it already exists in the server.
76
+ # Use the 'ensure' method to create/update it.
77
+ def chef_node
78
+ return @chef_node unless @chef_node.nil?
79
+ @chef_node = cluster.find_node(fullname) || false
80
+ end
81
+
82
+ # true if chef client is created and discovered
83
+ def chef_client?
84
+ chef_client.present?
85
+ end
86
+
87
+ # true if chef node is created and discovered
88
+ def chef_node?
89
+ chef_node.present?
90
+ end
91
+
92
+ def delete_chef
93
+ if chef_node then
94
+ step(" deleting chef node", :red)
95
+ unless_dry_run do
96
+ chef_node.destroy
97
+ end
98
+ @chef_node = nil
99
+ end
100
+ if chef_client
101
+ step(" deleting chef client", :red)
102
+ unless_dry_run do
103
+ chef_client.destroy
104
+ end
105
+ @chef_client = nil
106
+ end
107
+ end
108
+
109
+ # creates or updates the chef node.
110
+ #
111
+ # See notes at top of file for why all this jiggery-fuckery
112
+ #
113
+ # * client exists, node exists: assume client can update, weep later when
114
+ # the initial chef run fails. Not much we can do here -- holler at opscode.
115
+ # * client exists, node absent: see if client can create, fail otherwise
116
+ # * client absent, node absent: see if client can create both, fail otherwise
117
+ # * client absent, node exists: fail (we can't get permissions)
118
+ def sync_chef_node
119
+ step(" syncing chef node using the server's key")
120
+ # force-fetch the node so that we have its full attributes (the discovery
121
+ # does not pull all of it back)
122
+ @chef_node = handle_chef_response('404'){ Chef::Node.load( fullname ) }
123
+ # sets @chef_client if it exists
124
+ chef_client
125
+ #
126
+ case
127
+ when @chef_client && @chef_node then _update_chef_node # this will fail later if the chef client is in a bad state but whaddayagonnado
128
+ when @chef_client && (! @chef_node) then _create_chef_node
129
+ when (! @chef_client) && (! @chef_node) then # create both
130
+ ensure_chef_client
131
+ _create_chef_node
132
+ when (! @chef_client) && @chef_node
133
+ raise("The #{fullname} node exists, but its client does not.\nDue to limitations in the Opscode API, if we create a client, it will lack write permissions to the node. Small sadness now avoids much sadness later\nYou must either create a client manually, fix its permissions in the Chef console, and drop its client key where we can find it; or (if you are aware of the consequences) do \nknife node delete #{fullname}")
134
+ end
135
+ @chef_node
136
+ end
137
+
138
+ def client_key
139
+ @client_key ||= Ironfan::ChefClientKey.new("client-#{fullname}", chef_client) do |body|
140
+ chef_client.private_key(body) if chef_client.present? && body.present?
141
+ cloud.user_data(:client_key => body)
142
+ end
143
+ end
144
+
145
+ def chef_client_script_content
146
+ return @chef_client_script_content if @chef_client_script_content
147
+ return unless cloud.chef_client_script
148
+ script_filename = File.expand_path("../../config/#{cloud.chef_client_script}", File.dirname(__FILE__))
149
+ @chef_client_script_content = safely{ File.read(script_filename) }
150
+ end
151
+
152
+ def announce_state state
153
+ @chef_node.set[:state] = state
154
+ end
155
+
156
+ protected
157
+
158
+ # Create the chef client on the server. Do not call this directly -- go
159
+ # through sync_chef_node.
160
+ #
161
+ # this is done as the eponymous client, ensuring that the client does in
162
+ # fact have permissions on the node
163
+ #
164
+ # preconditions: @chef_node is set
165
+ def _create_chef_node(&block)
166
+ step(" creating chef node", :green)
167
+ @chef_node = Chef::Node.new
168
+ @chef_node.name(fullname)
169
+ set_chef_node_attributes
170
+ set_chef_node_environment
171
+ sync_volume_attributes
172
+ unless_dry_run do
173
+ chef_api_server_as_client.post_rest('nodes', @chef_node)
174
+ end
175
+ end
176
+
177
+ # Update the chef client on the server. Do not call this directly -- go
178
+ # through create_or_update_chef_node.
179
+ #
180
+ # this is done as the eponymous client, ensuring that the client does in
181
+ # fact have permissions on the node.
182
+ #
183
+ # preconditions: @chef_node is set
184
+ def _update_chef_node
185
+ step(" updating chef node", :blue)
186
+ set_chef_node_attributes
187
+ set_chef_node_environment
188
+ sync_volume_attributes
189
+ unless_dry_run do
190
+ chef_api_server_as_admin.put_rest("nodes/#{@chef_node.name}", @chef_node)
191
+ end
192
+ end
193
+
194
+
195
+ def sync_volume_attributes
196
+ composite_volumes.each do |vol_name, vol|
197
+ chef_node.normal[:volumes] ||= Mash.new
198
+ chef_node.normal[:volumes][vol_name] = vol.to_mash.compact
199
+ end
200
+ end
201
+
202
+ def set_chef_node_attributes
203
+ step(" setting node runlist and essential attributes")
204
+ @chef_node.run_list = Chef::RunList.new(*@settings[:run_list])
205
+ @chef_node.normal[ :organization] = Chef::Config[:organization] if Chef::Config[:organization]
206
+ @chef_node.override[:cluster_name] = cluster_name
207
+ @chef_node.override[:facet_name] = facet_name
208
+ @chef_node.override[:facet_index] = facet_index
209
+ end
210
+
211
+ def set_chef_node_environment
212
+ @chef_node.chef_environment(environment.to_s) if environment.present?
213
+ end
214
+
215
+ #
216
+ # Don't call this directly -- only through ensure_chef_node_and_client
217
+ #
218
+ def ensure_chef_client
219
+ step(" ensuring chef client exists")
220
+ return @chef_client if chef_client
221
+ step( " creating chef client", :green)
222
+ @chef_client = Chef::ApiClient.new
223
+ @chef_client.name(fullname)
224
+ @chef_client.admin(false)
225
+ #
226
+ # ApiClient#create sends extra params that fail -- we'll do it ourselves
227
+ # purposefully *not* catching the 'but it already exists' error: if it
228
+ # didn't show up in the discovery process, we're in an inconsistent state
229
+ unless_dry_run do
230
+ response = chef_api_server_as_admin.post_rest("clients", { 'name' => fullname, 'admin' => false, 'private_key' => true })
231
+ client_key.body = response['private_key']
232
+ end
233
+ client_key.save
234
+ @chef_client
235
+ end
236
+
237
+ def chef_api_server_as_client
238
+ return @chef_api_server_as_client if @chef_api_server_as_client
239
+ unless File.exists?(client_key.filename)
240
+ raise("Cannot create chef node #{fullname} -- client #{@chef_client} exists but no client key found in #{client_key.filename}.")
241
+ end
242
+ @chef_api_server_as_client = Chef::REST.new(Chef::Config[:chef_server_url], fullname, client_key.filename)
243
+ end
244
+
245
+ def chef_api_server_as_admin
246
+ @chef_api_server_as_admin ||= Chef::REST.new(Chef::Config[:chef_server_url])
247
+ end
248
+
249
+ # Execute the given chef call, but don't explode if the given http status
250
+ # code comes back
251
+ #
252
+ # @return chef object, or false if the server returned a recoverable response
253
+ def handle_chef_response(recoverable_responses, &block)
254
+ begin
255
+ block.call
256
+ rescue Net::HTTPServerException => e
257
+ raise unless Array(recoverable_responses).include?(e.response.code)
258
+ Chef::Log.debug("Swallowing a #{e.response.code} response in #{self.fullname}: #{e}")
259
+ return false
260
+ end
261
+ end
262
+
263
+ #
264
+ # The below *was* present but was pulled from the API by opscode for some reason (2011/10/20)
265
+ #
266
+
267
+ # # The client is required to have these permissions on its eponymous node
268
+ # REQUIRED_PERMISSIONS = %w[read create update]
269
+ #
270
+ # #
271
+ # # Verify that the client has required _acl's on the node.
272
+ # #
273
+ # # We don't raise an error, just a very noisy warning.
274
+ # #
275
+ # def check_node_permissions
276
+ # step(" ensuring chef node permissions are correct")
277
+ # chef_server_rest = Chef::REST.new(Chef::Config[:chef_server_url])
278
+ # handle_chef_response('404') do
279
+ # perms = chef_server_rest.get_rest("nodes/#{fullname}/_acl")
280
+ # perms_valid = {}
281
+ # REQUIRED_PERMISSIONS.each{|perm| perms_valid[perm] = perms[perm] && perms[perm]['actors'].include?(fullname) }
282
+ # Chef::Log.debug("Checking permissions: #{perms_valid.inspect} -- #{ perms_valid.values.all? ? 'correct' : 'BADNESS' }")
283
+ # unless perms_valid.values.all?
284
+ # ui.info(" ************************ ")
285
+ # ui.info(" ")
286
+ # ui.info(" INCONSISTENT PERMISSIONS for node #{fullname}:")
287
+ # ui.info(" The client[#{fullname}] should have permissions for #{REQUIRED_PERMISSIONS.join(', ')}")
288
+ # ui.info(" Instead, they are #{perms_valid.inspect}")
289
+ # ui.info(" You should create the node #{fullname} as client[#{fullname}], not as yourself.")
290
+ # ui.info(" ")
291
+ # ui.info(" Please adjust the permissions on the Opscode console, at")
292
+ # ui.info(" https://manage.opscode.com/nodes/#{fullname}/_acl")
293
+ # ui.info(" ")
294
+ # ui.info(" ************************ ")
295
+ # end
296
+ # end
297
+ # end
298
+ end
299
+ end
@@ -0,0 +1,412 @@
1
+ module Ironfan
2
+ module Cloud
3
+
4
+ #
5
+ # Right now only one cloud provider is implemented, so the separation
6
+ # between `cloud` and `cloud(:ec2)` is muddy.
7
+ #
8
+ # The goal though is to allow
9
+ #
10
+ # * cloud with no predicate -- definitions that apply to all cloud
11
+ # providers. If you only use one provider ever nothing stops you from
12
+ # always saying `cloud`.
13
+ # * Declarations irrelevant to other providers are acceptable and will be ignored
14
+ # * Declarations that are wrong in the context of other providers (a `public_ip`
15
+ # that is not available) will presumably cause a downstream error -- it's
16
+ # your responsibility to overlay with provider-correct values.
17
+ # * There are several declarations that *could* be sensibly abstracted, but
18
+ # are not. Rather than specifying `flavor 'm1.xlarge'`, I could ask for
19
+ # :ram => 15, :cores => 4 or storage => 1500 and get the cheapest machine
20
+ # that met or exceeded each constraint -- the default of `:price =>
21
+ # :smallest` would get me a t1.micro on EC2, a 256MB on
22
+ # Rackspace. Availability zones could also plausibly be parameterized.
23
+ #
24
+ # @example
25
+ # # these apply regardless of cloud provider
26
+ # cloud do
27
+ # # this makes sense everywhere
28
+ # image_name 'maverick'
29
+ #
30
+ # # this is not offered by many providers, and its value is non-portable;
31
+ # # but if you only run in one cloud there's harm in putting it here
32
+ # # or overriding it.
33
+ # public_ip '1.2.3.4'
34
+ #
35
+ # # Implemented differently across providers but its meaning is clear
36
+ # security_group :nagios
37
+ #
38
+ # # This is harmless for the other clouds
39
+ # availability_zones ['us-east-1d']
40
+ # end
41
+ #
42
+ # # these only apply to ec2 launches.
43
+ # # `ec2` is sugar for `cloud(:ec2)`.
44
+ # ec2 do
45
+ # spot_price_fraction 0.4
46
+ # end
47
+ #
48
+ class Base < Ironfan::DslObject
49
+ has_keys(
50
+ :name, :flavor, :image_name, :image_id, :keypair,
51
+ :chef_client_script, :public_ip, :permanent )
52
+ attr_accessor :owner
53
+
54
+ def initialize(owner, *args)
55
+ self.owner = owner
56
+ super(*args)
57
+ end
58
+
59
+ # default values to apply where no value was set
60
+ # @returns [Hash] hash of defaults
61
+ def defaults
62
+ reverse_merge!({
63
+ :image_name => 'natty',
64
+ })
65
+ end
66
+
67
+ # The username to ssh with.
68
+ # @return the ssh_user if set explicitly; otherwise, the user implied by the image name, if any; or else 'root'
69
+ def ssh_user(val=nil)
70
+ from_setting_or_image_info :ssh_user, val, 'root'
71
+ end
72
+
73
+ # Location of ssh private keys
74
+ def ssh_identity_dir(val=nil)
75
+ set :ssh_identity_dir, File.expand_path(val) unless val.nil?
76
+ @settings.include?(:ssh_identity_dir) ? @settings[:ssh_identity_dir] : Chef::Config.ec2_key_dir
77
+ end
78
+
79
+ # SSH identity file used for knife ssh, knife boostrap and such
80
+ def ssh_identity_file(val=nil)
81
+ set :ssh_identity_file, File.expand_path(val) unless val.nil?
82
+ @settings.include?(:ssh_identity_file) ? @settings[:ssh_identity_file] : File.join(ssh_identity_dir, "#{keypair}.pem")
83
+ end
84
+
85
+ # ID of the machine image to use.
86
+ # @return the image_id if set explicitly; otherwise, the id implied by the image name
87
+ def image_id(val=nil)
88
+ from_setting_or_image_info :image_id, val
89
+ end
90
+
91
+ # Distribution knife should target when bootstrapping an instance
92
+ # @return the bootstrap_distro if set explicitly; otherwise, the bootstrap_distro implied by the image name
93
+ def bootstrap_distro(val=nil)
94
+ from_setting_or_image_info :bootstrap_distro, val, "ubuntu10.04-gems"
95
+ end
96
+
97
+ def validation_key
98
+ IO.read(Chef::Config.validation_key) rescue ''
99
+ end
100
+
101
+ # The instance price, drawn from the compute flavor's info
102
+ def price
103
+ flavor_info[:price]
104
+ end
105
+
106
+ # The instance bitness, drawn from the compute flavor's info
107
+ def bits
108
+ flavor_info[:bits]
109
+ end
110
+
111
+ protected
112
+ # If value was explicitly set, use that; if the Chef::Config[:ec2_image_info] implies a value use that; otherwise use the default
113
+ def from_setting_or_image_info(key, val=nil, default=nil)
114
+ @settings[key] = val unless val.nil?
115
+ return @settings[key] if @settings.include?(key)
116
+ return image_info[key] unless image_info.nil?
117
+ return default # otherwise
118
+ end
119
+ end
120
+
121
+ class Ec2 < Base
122
+ has_keys(
123
+ :region, :availability_zones, :backing,
124
+ :spot_price, :spot_price_fraction,
125
+ :user_data, :security_groups,
126
+ :monitoring
127
+ )
128
+
129
+ def initialize(*args)
130
+ super *args
131
+ @settings[:security_groups] ||= Mash.new
132
+ @settings[:user_data] ||= Mash.new
133
+ end
134
+
135
+ #
136
+ # Sets some defaults for amazon cloud usage, and registers the root volume
137
+ #
138
+ def defaults
139
+ owner.volume(:root).reverse_merge!({
140
+ :device => '/dev/sda1',
141
+ :mount_point => '/',
142
+ :mountable => false,
143
+ })
144
+ self.reverse_merge!({
145
+ :availability_zones => ['us-east-1d'],
146
+ :backing => 'ebs',
147
+ :flavor => 't1.micro',
148
+ })
149
+ super
150
+ end
151
+
152
+ # adds a security group to the cloud instance
153
+ def security_group(sg_name, hsh={}, &block)
154
+ sg_name = sg_name.to_s
155
+ security_groups[sg_name] ||= Ironfan::Cloud::SecurityGroup.new(self, sg_name)
156
+ security_groups[sg_name].configure(hsh, &block)
157
+ security_groups[sg_name]
158
+ end
159
+
160
+ # With a value, sets the spot price to the given fraction of the
161
+ # instance's full price (as found in Ironfan::Cloud::Aws::FLAVOR_INFO)
162
+ # With no value, returns the spot price as a fraction of the full instance price.
163
+ def spot_price_fraction(val=nil)
164
+ if val
165
+ spot_price( price.to_f * val )
166
+ else
167
+ spot_price / price rescue 0
168
+ end
169
+ end
170
+
171
+ # EC2 User data -- DNA typically used to bootstrap the machine.
172
+ # @param [Hash] value -- when present, merged with the existing user data (overriding it)
173
+ # @return the user_data hash
174
+ def user_data(hsh={})
175
+ @settings[:user_data].merge!(hsh.to_hash) unless hsh.empty?
176
+ @settings[:user_data]
177
+ end
178
+
179
+ def reverse_merge!(hsh)
180
+ super(hsh.to_mash.compact)
181
+ @settings[:security_groups].reverse_merge!(hsh.security_groups) if hsh.respond_to?(:security_groups)
182
+ @settings[:user_data ].reverse_merge!(hsh.user_data) if hsh.respond_to?(:user_data)
183
+ self
184
+ end
185
+
186
+ def region(val=nil)
187
+ set(:region, val)
188
+ if @settings[:region] then @settings[:region]
189
+ elsif default_availability_zone then default_availability_zone.gsub(/^(\w+-\w+-\d)[a-z]/, '\1')
190
+ else nil
191
+ end
192
+ end
193
+
194
+ def default_availability_zone
195
+ availability_zones.first if availability_zones
196
+ end
197
+
198
+ # Bring the ephemeral storage (local scratch disks) online
199
+ def mount_ephemerals(attrs={})
200
+ owner.volume(:ephemeral0, attrs){ device '/dev/sdb'; volume_id 'ephemeral0' ; mount_point '/mnt' ; tags( :bulk => true, :local => true, :fallback => true) } if flavor_info[:ephemeral_volumes] > 0
201
+ owner.volume(:ephemeral1, attrs){ device '/dev/sdc'; volume_id 'ephemeral1' ; mount_point '/mnt2'; tags( :bulk => true, :local => true, :fallback => true) } if flavor_info[:ephemeral_volumes] > 1
202
+ owner.volume(:ephemeral2, attrs){ device '/dev/sdd'; volume_id 'ephemeral2' ; mount_point '/mnt3'; tags( :bulk => true, :local => true, :fallback => true) } if flavor_info[:ephemeral_volumes] > 2
203
+ owner.volume(:ephemeral3, attrs){ device '/dev/sde'; volume_id 'ephemeral3' ; mount_point '/mnt4'; tags( :bulk => true, :local => true, :fallback => true) } if flavor_info[:ephemeral_volumes] > 3
204
+ end
205
+
206
+ # Utility methods
207
+
208
+ def image_info
209
+ Chef::Config[:ec2_image_info][ [region, bits, backing, image_name] ] or ( ui.warn "Make sure to define the machine's region, bits, backing and image_name. (Have #{[region, bits, backing, image_name].inspect})" ; {} )
210
+ end
211
+
212
+ def list_images
213
+ ui.info("Available images:")
214
+ Chef::Config[:ec2_image_info].each do |flavor_name, flavor|
215
+ ui.info(" #{flavor_name}\t#{flavor.inspect}")
216
+ end
217
+ end
218
+
219
+ def flavor(val=nil)
220
+ if val && (not FLAVOR_INFO.has_key?(val.to_s))
221
+ ui.warn("Unknown machine image flavor '#{val}'")
222
+ list_flavors
223
+ end
224
+ set :flavor, val
225
+ end
226
+
227
+ def flavor_info
228
+ FLAVOR_INFO[flavor] or ( ui.warn "Please define the machine's flavor: have #{self.inspect}" ; {} )
229
+ end
230
+
231
+ def list_flavors
232
+ ui.info("Available flavors:")
233
+ FLAVOR_INFO.each do |flavor_name, flavor|
234
+ ui.info(" #{flavor_name}\t#{flavor.inspect}")
235
+ end
236
+ end
237
+
238
+ # code $/hr $/mo $/day CPU/$ Mem/$ mem cpu cores cpcore storage bits IO type name
239
+ # t1.micro $0.02 14 0.48 10.00 33.50 0.67 0.2 1 0.2 0 64 Low Micro Micro
240
+ # m1.small $0.085 61 2.04 11.76 20.00 1.7 1 1 1 160 32 Moderate Standard Small
241
+ # c1.medium $0.17 123 4.08 29.41 10.00 1.7 5 2 2.5 350 32 Moderate High-CPU Medium
242
+ # m1.large $0.34 246 8.16 11.76 22.06 7.5 4 2 2 850 64 High Standard Large
243
+ # m2.xlarge $0.50 363 12.00 13.00 35.40 17.7 6.5 2 3.25 420 64 Moderate High-Memory Extra Large
244
+ # c1.xlarge $0.68 493 16.32 29.41 10.29 7 20 8 2.5 1690 64 High High-CPU Extra Large
245
+ # m1.xlarge $0.68 493 16.32 11.76 22.06 15 8 4 2 1690 64 High Standard Extra Large
246
+ # m2.2xlarge $1.00 726 24.00 13.00 34.20 34.2 13 4 3.25 850 64 High High-Memory Double Extra Large
247
+ # m2.4xlarge $2.00 1452 48.00 13.00 34.20 68.4 26 8 3.25 1690 64 High High-Memory Quadruple Extra Large
248
+ # cc1.4xlarge $1.60 1161 38.40 20.94 14.38 23 33.5 2 16.75 1690 64 Very High 10GB Compute Quadruple Extra Large
249
+ # cg1.4xlarge $2.10 1524 50.40 15.95 10.48 22 33.5 2 16.75 1690 64 Very High 10GB Cluster GPU Quadruple Extra Large
250
+
251
+ FLAVOR_INFO = {
252
+ 't1.micro' => { :price => 0.02, :bits => '64-bit', :ram => 686, :cores => 1, :core_size => 0.25, :inst_disks => 0, :inst_disk_size => 0, :ephemeral_volumes => 0 },
253
+ 'm1.small' => { :price => 0.085, :bits => '32-bit', :ram => 1740, :cores => 1, :core_size => 1, :inst_disks => 1, :inst_disk_size => 160, :ephemeral_volumes => 1 },
254
+ 'c1.medium' => { :price => 0.17, :bits => '32-bit', :ram => 1740, :cores => 2, :core_size => 2.5, :inst_disks => 1, :inst_disk_size => 350, :ephemeral_volumes => 1 },
255
+ 'm1.large' => { :price => 0.34, :bits => '64-bit', :ram => 7680, :cores => 2, :core_size => 2, :inst_disks => 2, :inst_disk_size => 850, :ephemeral_volumes => 2 },
256
+ 'm2.xlarge' => { :price => 0.50, :bits => '64-bit', :ram => 18124, :cores => 2, :core_size => 3.25, :inst_disks => 1, :inst_disk_size => 420, :ephemeral_volumes => 1 },
257
+ 'c1.xlarge' => { :price => 0.68, :bits => '64-bit', :ram => 7168, :cores => 8, :core_size => 2.5, :inst_disks => 4, :inst_disk_size => 1690, :ephemeral_volumes => 4 },
258
+ 'm1.xlarge' => { :price => 0.68, :bits => '64-bit', :ram => 15360, :cores => 4, :core_size => 2, :inst_disks => 4, :inst_disk_size => 1690, :ephemeral_volumes => 4 },
259
+ 'm2.2xlarge' => { :price => 1.00, :bits => '64-bit', :ram => 35020, :cores => 4, :core_size => 3.25, :inst_disks => 2, :inst_disk_size => 850, :ephemeral_volumes => 2 },
260
+ 'm2.4xlarge' => { :price => 2.00, :bits => '64-bit', :ram => 70041, :cores => 8, :core_size => 3.25, :inst_disks => 4, :inst_disk_size => 1690, :ephemeral_volumes => 4 },
261
+ 'cc1.4xlarge' => { :price => 1.60, :bits => '64-bit', :ram => 23552, :cores => 2, :core_size =>16.75, :inst_disks => 4, :inst_disk_size => 1690, :ephemeral_volumes => 2 },
262
+ 'cg1.4xlarge' => { :price => 2.10, :bits => '64-bit', :ram => 22528, :cores => 2, :core_size =>16.75, :inst_disks => 4, :inst_disk_size => 1690, :ephemeral_volumes => 2 },
263
+ }
264
+
265
+ #
266
+ # To add to this list, use this snippet:
267
+ #
268
+ # Chef::Config[:ec2_image_info] ||= {}
269
+ # Chef::Config[:ec2_image_info].merge!({
270
+ # # ... lines like the below
271
+ # })
272
+ #
273
+ # in your knife.rb or whereever. We'll notice that it exists and add to it, rather than clobbering it.
274
+ #
275
+ Chef::Config[:ec2_image_info] ||= {}
276
+ Chef::Config[:ec2_image_info].merge!({
277
+
278
+ #
279
+ # Lucid (Ubuntu 9.10)
280
+ #
281
+ %w[us-east-1 64-bit instance karmic ] => { :image_id => 'ami-55739e3c', :ssh_user => 'ubuntu', :bootstrap_distro => "ubuntu10.04-gems", },
282
+ %w[us-east-1 32-bit instance karmic ] => { :image_id => 'ami-bb709dd2', :ssh_user => 'ubuntu', :bootstrap_distro => "ubuntu10.04-gems", },
283
+ %w[us-west-1 64-bit instance karmic ] => { :image_id => 'ami-cb2e7f8e', :ssh_user => 'ubuntu', :bootstrap_distro => "ubuntu10.04-gems", },
284
+ %w[us-west-1 32-bit instance karmic ] => { :image_id => 'ami-c32e7f86', :ssh_user => 'ubuntu', :bootstrap_distro => "ubuntu10.04-gems", },
285
+ %w[eu-west-1 64-bit instance karmic ] => { :image_id => 'ami-05c2e971', :ssh_user => 'ubuntu', :bootstrap_distro => "ubuntu10.04-gems", },
286
+ %w[eu-west-1 32-bit instance karmic ] => { :image_id => 'ami-2fc2e95b', :ssh_user => 'ubuntu', :bootstrap_distro => "ubuntu10.04-gems", },
287
+
288
+ #
289
+ # Lucid (Ubuntu 10.04.3)
290
+ #
291
+ %w[ap-southeast-1 64-bit ebs lucid ] => { :image_id => 'ami-77f28d25', :ssh_user => 'ubuntu', :bootstrap_distro => "ubuntu10.04-gems", },
292
+ %w[ap-southeast-1 32-bit ebs lucid ] => { :image_id => 'ami-4df28d1f', :ssh_user => 'ubuntu', :bootstrap_distro => "ubuntu10.04-gems", },
293
+ %w[ap-southeast-1 64-bit instance lucid ] => { :image_id => 'ami-57f28d05', :ssh_user => 'ubuntu', :bootstrap_distro => "ubuntu10.04-gems", },
294
+ %w[ap-southeast-1 32-bit instance lucid ] => { :image_id => 'ami-a5f38cf7', :ssh_user => 'ubuntu', :bootstrap_distro => "ubuntu10.04-gems", },
295
+ %w[eu-west-1 64-bit ebs lucid ] => { :image_id => 'ami-ab4d67df', :ssh_user => 'ubuntu', :bootstrap_distro => "ubuntu10.04-gems", },
296
+ %w[eu-west-1 32-bit ebs lucid ] => { :image_id => 'ami-a94d67dd', :ssh_user => 'ubuntu', :bootstrap_distro => "ubuntu10.04-gems", },
297
+ %w[eu-west-1 64-bit instance lucid ] => { :image_id => 'ami-a54d67d1', :ssh_user => 'ubuntu', :bootstrap_distro => "ubuntu10.04-gems", },
298
+ %w[eu-west-1 32-bit instance lucid ] => { :image_id => 'ami-cf4d67bb', :ssh_user => 'ubuntu', :bootstrap_distro => "ubuntu10.04-gems", },
299
+ #
300
+ %w[us-east-1 64-bit ebs lucid ] => { :image_id => 'ami-4b4ba522', :ssh_user => 'ubuntu', :bootstrap_distro => "ubuntu10.04-gems", },
301
+ %w[us-east-1 32-bit ebs lucid ] => { :image_id => 'ami-714ba518', :ssh_user => 'ubuntu', :bootstrap_distro => "ubuntu10.04-gems", },
302
+ %w[us-east-1 64-bit instance lucid ] => { :image_id => 'ami-fd4aa494', :ssh_user => 'ubuntu', :bootstrap_distro => "ubuntu10.04-gems", },
303
+ %w[us-east-1 32-bit instance lucid ] => { :image_id => 'ami-2d4aa444', :ssh_user => 'ubuntu', :bootstrap_distro => "ubuntu10.04-gems", },
304
+ #
305
+ %w[us-west-1 64-bit ebs lucid ] => { :image_id => 'ami-d197c694', :ssh_user => 'ubuntu', :bootstrap_distro => "ubuntu10.04-gems", },
306
+ %w[us-west-1 32-bit ebs lucid ] => { :image_id => 'ami-cb97c68e', :ssh_user => 'ubuntu', :bootstrap_distro => "ubuntu10.04-gems", },
307
+ %w[us-west-1 64-bit instance lucid ] => { :image_id => 'ami-c997c68c', :ssh_user => 'ubuntu', :bootstrap_distro => "ubuntu10.04-gems", },
308
+ %w[us-west-1 32-bit instance lucid ] => { :image_id => 'ami-c597c680', :ssh_user => 'ubuntu', :bootstrap_distro => "ubuntu10.04-gems", },
309
+
310
+ #
311
+ # Maverick (Ubuntu 10.10)
312
+ #
313
+ %w[ ap-southeast-1 64-bit ebs maverick ] => { :image_id => 'ami-32423c60', :ssh_user => 'ubuntu', :bootstrap_distro => "ubuntu10.04-gems", },
314
+ %w[ ap-southeast-1 64-bit instance maverick ] => { :image_id => 'ami-12423c40', :ssh_user => 'ubuntu', :bootstrap_distro => "ubuntu10.04-gems", },
315
+ %w[ ap-southeast-1 32-bit ebs maverick ] => { :image_id => 'ami-0c423c5e', :ssh_user => 'ubuntu', :bootstrap_distro => "ubuntu10.04-gems", },
316
+ %w[ ap-southeast-1 32-bit instance maverick ] => { :image_id => 'ami-7c423c2e', :ssh_user => 'ubuntu', :bootstrap_distro => "ubuntu10.04-gems", },
317
+ #
318
+ %w[ eu-west-1 64-bit ebs maverick ] => { :image_id => 'ami-e59ca991', :ssh_user => 'ubuntu', :bootstrap_distro => "ubuntu10.04-gems", },
319
+ %w[ eu-west-1 64-bit instance maverick ] => { :image_id => 'ami-1b9ca96f', :ssh_user => 'ubuntu', :bootstrap_distro => "ubuntu10.04-gems", },
320
+ %w[ eu-west-1 32-bit ebs maverick ] => { :image_id => 'ami-fb9ca98f', :ssh_user => 'ubuntu', :bootstrap_distro => "ubuntu10.04-gems", },
321
+ %w[ eu-west-1 32-bit instance maverick ] => { :image_id => 'ami-339ca947', :ssh_user => 'ubuntu', :bootstrap_distro => "ubuntu10.04-gems", },
322
+ #
323
+ %w[ us-east-1 64-bit ebs maverick ] => { :image_id => 'ami-cef405a7', :ssh_user => 'ubuntu', :bootstrap_distro => "ubuntu10.04-gems", },
324
+ %w[ us-east-1 64-bit instance maverick ] => { :image_id => 'ami-08f40561', :ssh_user => 'ubuntu', :bootstrap_distro => "ubuntu10.04-gems", },
325
+ %w[ us-east-1 32-bit ebs maverick ] => { :image_id => 'ami-ccf405a5', :ssh_user => 'ubuntu', :bootstrap_distro => "ubuntu10.04-gems", },
326
+ %w[ us-east-1 32-bit instance maverick ] => { :image_id => 'ami-a6f504cf', :ssh_user => 'ubuntu', :bootstrap_distro => "ubuntu10.04-gems", },
327
+ #
328
+ %w[ us-west-1 64-bit ebs maverick ] => { :image_id => 'ami-af7e2eea', :ssh_user => 'ubuntu', :bootstrap_distro => "ubuntu10.04-gems", },
329
+ %w[ us-west-1 64-bit instance maverick ] => { :image_id => 'ami-a17e2ee4', :ssh_user => 'ubuntu', :bootstrap_distro => "ubuntu10.04-gems", },
330
+ %w[ us-west-1 32-bit ebs maverick ] => { :image_id => 'ami-ad7e2ee8', :ssh_user => 'ubuntu', :bootstrap_distro => "ubuntu10.04-gems", },
331
+ %w[ us-west-1 32-bit instance maverick ] => { :image_id => 'ami-957e2ed0', :ssh_user => 'ubuntu', :bootstrap_distro => "ubuntu10.04-gems", },
332
+
333
+ #
334
+ # Natty (Ubuntu 11.04)
335
+ #
336
+ %w[ ap-northeast-1 32-bit ebs natty ] => { :image_id => 'ami-00b10501', :ssh_user => 'ubuntu', :bootstrap_distro => "ubuntu10.04-gems", },
337
+ %w[ ap-northeast-1 32-bit instance natty ] => { :image_id => 'ami-f0b004f1', :ssh_user => 'ubuntu', :bootstrap_distro => "ubuntu10.04-gems", },
338
+ %w[ ap-northeast-1 64-bit ebs natty ] => { :image_id => 'ami-02b10503', :ssh_user => 'ubuntu', :bootstrap_distro => "ubuntu10.04-gems", },
339
+ %w[ ap-northeast-1 64-bit instance natty ] => { :image_id => 'ami-fab004fb', :ssh_user => 'ubuntu', :bootstrap_distro => "ubuntu10.04-gems", },
340
+ #
341
+ %w[ ap-southeast-1 32-bit ebs natty ] => { :image_id => 'ami-06255f54', :ssh_user => 'ubuntu', :bootstrap_distro => "ubuntu10.04-gems", },
342
+ %w[ ap-southeast-1 32-bit instance natty ] => { :image_id => 'ami-72255f20', :ssh_user => 'ubuntu', :bootstrap_distro => "ubuntu10.04-gems", },
343
+ %w[ ap-southeast-1 64-bit ebs natty ] => { :image_id => 'ami-04255f56', :ssh_user => 'ubuntu', :bootstrap_distro => "ubuntu10.04-gems", },
344
+ %w[ ap-southeast-1 64-bit instance natty ] => { :image_id => 'ami-7a255f28', :ssh_user => 'ubuntu', :bootstrap_distro => "ubuntu10.04-gems", },
345
+ #
346
+ %w[ eu-west-1 32-bit ebs natty ] => { :image_id => 'ami-a4f7c5d0', :ssh_user => 'ubuntu', :bootstrap_distro => "ubuntu10.04-gems", },
347
+ %w[ eu-west-1 32-bit instance natty ] => { :image_id => 'ami-fef7c58a', :ssh_user => 'ubuntu', :bootstrap_distro => "ubuntu10.04-gems", },
348
+ %w[ eu-west-1 64-bit ebs natty ] => { :image_id => 'ami-a6f7c5d2', :ssh_user => 'ubuntu', :bootstrap_distro => "ubuntu10.04-gems", },
349
+ %w[ eu-west-1 64-bit instance natty ] => { :image_id => 'ami-c0f7c5b4', :ssh_user => 'ubuntu', :bootstrap_distro => "ubuntu10.04-gems", },
350
+ #
351
+ %w[ us-east-1 32-bit ebs natty ] => { :image_id => 'ami-e358958a', :ssh_user => 'ubuntu', :bootstrap_distro => "ubuntu10.04-gems", },
352
+ %w[ us-east-1 32-bit instance natty ] => { :image_id => 'ami-c15994a8', :ssh_user => 'ubuntu', :bootstrap_distro => "ubuntu10.04-gems", },
353
+ %w[ us-east-1 64-bit ebs natty ] => { :image_id => 'ami-fd589594', :ssh_user => 'ubuntu', :bootstrap_distro => "ubuntu10.04-gems", },
354
+ %w[ us-east-1 64-bit instance natty ] => { :image_id => 'ami-71589518', :ssh_user => 'ubuntu', :bootstrap_distro => "ubuntu10.04-gems", },
355
+ #
356
+ %w[ us-west-1 32-bit ebs natty ] => { :image_id => 'ami-43580406', :ssh_user => 'ubuntu', :bootstrap_distro => "ubuntu10.04-gems", },
357
+ %w[ us-west-1 32-bit instance natty ] => { :image_id => 'ami-e95f03ac', :ssh_user => 'ubuntu', :bootstrap_distro => "ubuntu10.04-gems", },
358
+ %w[ us-west-1 64-bit ebs natty ] => { :image_id => 'ami-4d580408', :ssh_user => 'ubuntu', :bootstrap_distro => "ubuntu10.04-gems", },
359
+ %w[ us-west-1 64-bit instance natty ] => { :image_id => 'ami-a15f03e4', :ssh_user => 'ubuntu', :bootstrap_distro => "ubuntu10.04-gems", },
360
+
361
+ #
362
+ # Oneric (Ubuntu 11.10)
363
+ #
364
+ %w[ ap-northeast-1 32-bit ebs oneric ] => { :image_id => 'ami-84902785', :ssh_user => 'ubuntu', :bootstrap_distro => "ubuntu10.04-gems", },
365
+ %w[ ap-northeast-1 32-bit instance oneric ] => { :image_id => 'ami-5e90275f', :ssh_user => 'ubuntu', :bootstrap_distro => "ubuntu10.04-gems", },
366
+ %w[ ap-northeast-1 64-bit ebs oneric ] => { :image_id => 'ami-88902789', :ssh_user => 'ubuntu', :bootstrap_distro => "ubuntu10.04-gems", },
367
+ %w[ ap-northeast-1 64-bit instance oneric ] => { :image_id => 'ami-7c90277d', :ssh_user => 'ubuntu', :bootstrap_distro => "ubuntu10.04-gems", },
368
+ #
369
+ %w[ ap-southeast-1 32-bit ebs oneric ] => { :image_id => 'ami-0a327758', :ssh_user => 'ubuntu', :bootstrap_distro => "ubuntu10.04-gems", },
370
+ %w[ ap-southeast-1 32-bit instance oneric ] => { :image_id => 'ami-00327752', :ssh_user => 'ubuntu', :bootstrap_distro => "ubuntu10.04-gems", },
371
+ %w[ ap-southeast-1 64-bit ebs oneric ] => { :image_id => 'ami-0832775a', :ssh_user => 'ubuntu', :bootstrap_distro => "ubuntu10.04-gems", },
372
+ %w[ ap-southeast-1 64-bit instance oneric ] => { :image_id => 'ami-04327756', :ssh_user => 'ubuntu', :bootstrap_distro => "ubuntu10.04-gems", },
373
+ #
374
+ %w[ eu-west-1 32-bit ebs oneric ] => { :image_id => 'ami-11f0cc65', :ssh_user => 'ubuntu', :bootstrap_distro => "ubuntu10.04-gems", },
375
+ %w[ eu-west-1 32-bit instance oneric ] => { :image_id => 'ami-4ff0cc3b', :ssh_user => 'ubuntu', :bootstrap_distro => "ubuntu10.04-gems", },
376
+ %w[ eu-west-1 64-bit ebs oneric ] => { :image_id => 'ami-1df0cc69', :ssh_user => 'ubuntu', :bootstrap_distro => "ubuntu10.04-gems", },
377
+ %w[ eu-west-1 64-bit instance oneric ] => { :image_id => 'ami-23f0cc57', :ssh_user => 'ubuntu', :bootstrap_distro => "ubuntu10.04-gems", },
378
+ #
379
+ %w[ us-east-1 32-bit ebs oneric ] => { :image_id => 'ami-a562a9cc', :ssh_user => 'ubuntu', :bootstrap_distro => "ubuntu10.04-gems", },
380
+ %w[ us-east-1 32-bit instance oneric ] => { :image_id => 'ami-3962a950', :ssh_user => 'ubuntu', :bootstrap_distro => "ubuntu10.04-gems", },
381
+ %w[ us-east-1 64-bit ebs oneric ] => { :image_id => 'ami-bf62a9d6', :ssh_user => 'ubuntu', :bootstrap_distro => "ubuntu10.04-gems", },
382
+ %w[ us-east-1 64-bit instance oneric ] => { :image_id => 'ami-c162a9a8', :ssh_user => 'ubuntu', :bootstrap_distro => "ubuntu10.04-gems", },
383
+ #
384
+ %w[ us-west-1 32-bit ebs oneric ] => { :image_id => 'ami-c9a1fe8c', :ssh_user => 'ubuntu', :bootstrap_distro => "ubuntu10.04-gems", },
385
+ %w[ us-west-1 32-bit instance oneric ] => { :image_id => 'ami-21a1fe64', :ssh_user => 'ubuntu', :bootstrap_distro => "ubuntu10.04-gems", },
386
+ %w[ us-west-1 64-bit ebs oneric ] => { :image_id => 'ami-cba1fe8e', :ssh_user => 'ubuntu', :bootstrap_distro => "ubuntu10.04-gems", },
387
+ %w[ us-west-1 64-bit instance oneric ] => { :image_id => 'ami-3fa1fe7a', :ssh_user => 'ubuntu', :bootstrap_distro => "ubuntu10.04-gems", },
388
+ #
389
+ %w[ us-west-2 32-bit ebs oneric ] => { :image_id => 'ami-ea9a17da', :ssh_user => 'ubuntu', :bootstrap_distro => "ubuntu10.04-gems", },
390
+ %w[ us-west-2 32-bit instance oneric ] => { :image_id => 'ami-f49a17c4', :ssh_user => 'ubuntu', :bootstrap_distro => "ubuntu10.04-gems", },
391
+ %w[ us-west-2 64-bit ebs oneric ] => { :image_id => 'ami-ec9a17dc', :ssh_user => 'ubuntu', :bootstrap_distro => "ubuntu10.04-gems", },
392
+ %w[ us-west-2 64-bit instance oneric ] => { :image_id => 'ami-fe9a17ce', :ssh_user => 'ubuntu', :bootstrap_distro => "ubuntu10.04-gems", },
393
+ })
394
+ end
395
+
396
+ class Slicehost < Base
397
+ # server_name
398
+ # slicehost_password
399
+ # Proc.new { |password| Chef::Config[:knife][:slicehost_password] = password }
400
+
401
+ # personality
402
+ end
403
+
404
+ class Rackspace < Base
405
+ # api_key, api_username, server_name
406
+ end
407
+
408
+ class Terremark < Base
409
+ # password, username, service
410
+ end
411
+ end
412
+ end