ironfan 3.1.0.rc1

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 (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