Class: Furnish::Provisioner::EC2
- Defined in:
- lib/furnish/provisioners/ec2.rb
Overview
EC2 Instance Provisioner. See attributes and #new for argument descriptions.
If security groups are supplied to this provisioner as a part of a provisioner group (in #startup’s argument list), they are appended to any list of security group id’s already provided. This allows you to provision a security group with the instances without knowing about either beforehand.
On startup, IP addresses of the instances are returned to the next provisioner in the provisioner group.
It would be in your best interest to familiarize yourself with some of aws-sdk’s API when trying to understand some of these parameters. Here is some documentation:
Constant Summary collapse
- PASSTHROUGH_ATTRS =
Arguments that are passed through straight to AWS::EC2::InstanceCollection#create.
%w[ key_name availability_zone image_id kernel_id ramdisk_id count block_device_mappings instance_type user_data security_group_ids monitoring_enabled ]
Instance Attribute Summary collapse
-
#instance_ids ⇒ Object
readonly
After provisioning, instance identifiers managed by this provisioner will be set here.
Instance Method Summary collapse
-
#availability_zone ⇒ Object
:attr: availability_zone.
-
#block_device_mappings ⇒ Object
:attr: block_device_mappings.
-
#check_ec2_args ⇒ Object
Validate additional required EC2-specific arguments like #key_name and #image_id.
-
#check_region ⇒ Object
Overload of Furnish::Provisioner::AWS#check_region that also deals with availability zones.
-
#coerce_instances(instances) ⇒ Object
puts the return value from ec2.instances.create into a normal array no matter what it returns.
-
#count ⇒ Object
:attr: count.
-
#image_id ⇒ Object
:attr: image_id.
-
#initialize(args) ⇒ EC2
constructor
Create a new EC2 Provisioner.
-
#instance_type ⇒ Object
:attr: instance_type.
-
#kernel_id ⇒ Object
:attr: kernel_id.
-
#key_name ⇒ Object
:attr: key_name.
-
#launch_options ⇒ Object
generates options to pass to AWS::EC2::InstanceCollection#create from our attributes.
-
#monitoring_enabled ⇒ Object
:attr: monitoring_enabled.
-
#poll_interval ⇒ Object
:attr: poll_interval.
-
#provision_timeout ⇒ Object
:attr: provision_timeout.
-
#ramdisk_id ⇒ Object
:attr: ramdisk_id.
-
#report ⇒ Object
Furnish reporter – includes image id, number of servers, and instance id’s.
-
#security_group_ids ⇒ Object
:attr: security_group_ids.
-
#shutdown(args = {}) ⇒ Object
All instances are told to terminate, the method then waits for all of them to enter the terminated state, then returns true.
-
#startup(args = {}) ⇒ Object
Provision instance(s).
-
#user_data ⇒ Object
:attr: user_data.
-
#wait_for_instances(instances, state) ⇒ Object
Polls the EC2 API waiting for instances to enter the state passed.
Methods inherited from AWS
#access_key, #check_aws_settings, #ec2, #region, #secret_key
Constructor Details
#initialize(args) ⇒ EC2
Create a new EC2 Provisioner. Inherits the initializer from Furnish::Provisioner::AWS.new, see the documentation there for more information.
Additionally, see the attribute listing for argument documentation and their defaults.
Required arguments that do not have defaults. ArgumentError will be raised if they are not supplied at construction time:
-
#access_key (from Furnish::Provisioner::AWS)
-
#secret_key (from Furnish::Provisioner::AWS)
-
#instance_type
-
#image_id
-
#region (also see #availability_zone)
-
#key_name
Additionally see #security_group_ids, #poll_interval and #provision_timeout for some detail on special logic surrounding the operation of this provisioner.
211 212 213 214 215 216 217 218 219 220 221 222 223 |
# File 'lib/furnish/provisioners/ec2.rb', line 211 def initialize(args) super check_region check_ec2_args @count ||= 1 @instance_type ||= "c1.medium" @security_group_ids ||= [] @provision_wait ||= 300 @poll_interval ||= 1 @instance_ids = [] end |
Instance Attribute Details
#instance_ids ⇒ Object (readonly)
After provisioning, instance identifiers managed by this provisioner will be set here.
168 169 170 |
# File 'lib/furnish/provisioners/ec2.rb', line 168 def instance_ids @instance_ids end |
Instance Method Details
#availability_zone ⇒ Object
:attr: availability_zone
EC2 availability zone. If not provided, EC2 will pick based on the region. If provided, region will be determined from this value.
69 70 71 |
# File 'lib/furnish/provisioners/ec2.rb', line 69 furnish_property :availability_zone, "EC2 Availability Zone. If provided and no region supplied, region will be determined from this. If this is not supplied, AZ will be EC2's choosing based on the region.", String |
#block_device_mappings ⇒ Object
:attr: block_device_mappings
Mapping for block device information. Passed directly to AWS::EC2::InstanceCollection#create – see its documentation for more information.
139 140 |
# File 'lib/furnish/provisioners/ec2.rb', line 139 furnish_property :block_device_mappings, "block device information. Passed directly to AWS::EC2::InstanceCollection#create's argument list. Not required, no default." |
#check_ec2_args ⇒ Object
Validate additional required EC2-specific arguments like #key_name and #image_id.
245 246 247 248 249 250 251 252 253 |
# File 'lib/furnish/provisioners/ec2.rb', line 245 def check_ec2_args unless key_name raise ArgumentError, "key_name is required for instance provision" end unless image_id raise ArgumentError, "an AMI image_id must be provided" end end |
#check_region ⇒ Object
Overload of Furnish::Provisioner::AWS#check_region that also deals with availability zones.
228 229 230 231 232 233 234 235 236 237 238 239 |
# File 'lib/furnish/provisioners/ec2.rb', line 228 def check_region if availability_zone and !region @region = availability_zone.sub(/\D+$/, '') end if availability_zone and region and !availability_zone.start_with?(region) raise ArgumentError, "region and availability zone are both supplied and do not match: [r: #{region}, a: #{availability_zone}]" end super end |
#coerce_instances(instances) ⇒ Object
puts the return value from ec2.instances.create into a normal array no matter what it returns. See the method comments for more information.
271 272 273 274 275 276 277 278 279 280 281 |
# File 'lib/furnish/provisioners/ec2.rb', line 271 def coerce_instances(instances) # if count == 1, returns a single instance id. instances = [instances] unless instances.kind_of?(Array) # XXX there are versions of aws-sdk that respond to #kind_of?(Array) # but aren't actually arrays. This will always give us an array. tmp_instances = [] instances.each { |i| tmp_instances.push(i) } return tmp_instances end |
#count ⇒ Object
:attr: count
The number of instances to provision. Default is 1.
107 108 109 |
# File 'lib/furnish/provisioners/ec2.rb', line 107 furnish_property :count, "Number of instances to allocate. Required, Default is 1.", Integer |
#image_id ⇒ Object
:attr: image_id
The AMI identifier. Required. No default.
78 79 80 |
# File 'lib/furnish/provisioners/ec2.rb', line 78 furnish_property :image_id, "AMI Identifier. Required. No Default.", String |
#instance_type ⇒ Object
:attr: instance_type
The size or “flavor” of instance(s). Required, default is ‘c1.medium’.
116 117 |
# File 'lib/furnish/provisioners/ec2.rb', line 116 furnish_property :instance_type, "Size or 'flavor' of instance(s). Required, default is 'c1.medium'." |
#kernel_id ⇒ Object
:attr: kernel_id
The AKI identifier. No default, EC2 picks based on the AMI if not provided.
88 89 90 |
# File 'lib/furnish/provisioners/ec2.rb', line 88 furnish_property :kernel_id, "AKI Identifier. If not supplied, left to EC2 to sort out.", String |
#key_name ⇒ Object
:attr: key_name
The SSH key name for EC2. Must already exists. Required. No Default.
59 60 61 |
# File 'lib/furnish/provisioners/ec2.rb', line 59 furnish_property :key_name, "EC2 SSH Key Name, must already exist. Required. No Default.", String |
#launch_options ⇒ Object
generates options to pass to AWS::EC2::InstanceCollection#create from our attributes.
259 260 261 262 263 264 265 |
# File 'lib/furnish/provisioners/ec2.rb', line 259 def = { } PASSTHROUGH_ATTRS.each { |x| [x.to_sym] = send(x) if send(x) } return end |
#monitoring_enabled ⇒ Object
:attr: monitoring_enabled
Mapping for configuring monitoring. Passed directly to AWS::EC2::InstanceCollection#create – see its documentation for more information.
159 160 |
# File 'lib/furnish/provisioners/ec2.rb', line 159 furnish_property :monitoring_enabled, "enable montioring. Passed directly to AWS::EC2::InstanceCollection#create's argument list. Not required, no default." |
#poll_interval ⇒ Object
:attr: poll_interval
Wait this long between instance status requests. Default is one second. Fractional values are OK.
50 51 52 |
# File 'lib/furnish/provisioners/ec2.rb', line 50 furnish_property :poll_interval, "Wait this long between instance status requests. Default is 1 second, fractional values OK.", Numeric |
#provision_timeout ⇒ Object
:attr: provision_timeout
Give up after this long for the requested instances to be in a running state. Default is 300 seconds.
40 41 42 |
# File 'lib/furnish/provisioners/ec2.rb', line 40 furnish_property :provision_timeout, "Give up after this long for the instance to come alive after requesting from the API. Default is 300 seconds.", Integer |
#ramdisk_id ⇒ Object
:attr: ramdisk_id
The ARI identifier. No default, EC2 picks based on the AMI and possibly the AKI if not provided.
98 99 100 |
# File 'lib/furnish/provisioners/ec2.rb', line 98 furnish_property :ramdisk_id, "ARI Identifier. If not supplied, left to EC2 to sort out.", String |
#report ⇒ Object
Furnish reporter – includes image id, number of servers, and instance id’s.
367 368 369 |
# File 'lib/furnish/provisioners/ec2.rb', line 367 def report ["ami #{image_id}; #{count} servers; instance_ids: #{instance_ids.inspect}"] end |
#security_group_ids ⇒ Object
:attr: security_group_ids
Array of security group identifiers (not *group names*) that these instances will be bound to. Appends any incoming group id’s from previous provisioners. At least one must exist at provisioning time, or the provision will fail.
128 129 130 |
# File 'lib/furnish/provisioners/ec2.rb', line 128 furnish_property :security_group_ids, "list of group identifiers (not names) that these instances will be bound to. Appends any incoming group id's from previous provisioners. At least one must exist at provisioning time.", Array |
#shutdown(args = {}) ⇒ Object
All instances are told to terminate, the method then waits for all of them to enter the terminated state, then returns true.
356 357 358 359 360 361 362 |
# File 'lib/furnish/provisioners/ec2.rb', line 356 def shutdown(args={}) instances = instance_ids.map { |i| ec2.instances[i] } instances.each { |i| i.terminate rescue nil } wait_for_instances(instances, :terminated) return { } end |
#startup(args = {}) ⇒ Object
Provision instance(s).
If a security group id is supplied to this method, it will be permanently appended to #security_group_ids and used during instance creation.
Regardless of the above behavior, if no security group ids exist, will raise RuntimeError (EC2 requires instances to be in at least one security group).
Records the instance id’s, waits for them to all enter the ‘:running` state, and then returns a list of their public IP addresses to the next provisioner.
336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 |
# File 'lib/furnish/provisioners/ec2.rb', line 336 def startup(args={}) if args[:security_group_ids] @security_group_ids += args[:security_group_ids] end if security_group_ids.empty? raise "no security groups supplied either at construction or provisioning time, cannot request instances." end instances = coerce_instances(ec2.instances.create()) @instance_ids = instances.map(&:id) wait_for_instances(instances, :running) return({ :security_group_ids => @security_group_ids, :ips => Set[*instances.map(&:ip_address)], :ec2_instance_ids => @instance_ids }) end |
#user_data ⇒ Object
:attr: user_data
Mapping for user data. Passed directly to AWS::EC2::InstanceCollection#create – see its documentation for more information.
149 150 |
# File 'lib/furnish/provisioners/ec2.rb', line 149 furnish_property :user_data, "user data. Passed directly to AWS::EC2::InstanceCollection#create's argument list. Not required, no default." |
#wait_for_instances(instances, state) ⇒ Object
Polls the EC2 API waiting for instances to enter the state passed. Uses #poll_interval to determine how long to wait between status requests, and #provision_timeout to determine how long to wait before giving up.
289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 |
# File 'lib/furnish/provisioners/ec2.rb', line 289 def wait_for_instances(instances, state) Timeout.timeout(provision_timeout) do not_running = instances.dup until not_running.empty? instance = not_running.shift status = instance.status rescue nil if status unless status == state if_debug(3) do puts "instance #{instance.id} is not in #{state} state yet." end not_running.push(instance) end else if_debug(3) do puts "API server doesn't think #{instance.id} exists yet." end not_running.push(instance) end sleep poll_interval end end rescue TimeoutError raise "instances timed out waiting for ec2 API" end |