Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Merge pull request #57 from seomoz/spot_instances

Spot instances (wip)
  • Loading branch information...
commit 8e9c4f74d08067c60a665ccc9fef7236462da508 2 parents ba1ccc7 + d7f9d3b
@delano delano authored
View
3  .gitignore
@@ -1,3 +1,4 @@
.rudy
pkg
-doc
+doc
+Gemfile.lock
View
2  Gemfile
@@ -0,0 +1,2 @@
+source "http://rubygems.org"
+gemspec
View
2  bin/rudy
@@ -1,4 +1,4 @@
-#!/usr/bin/ruby
+#!/usr/bin/env ruby
# = Rudy
View
2  bin/rudy-ec2
@@ -1,4 +1,4 @@
-#!/usr/bin/ruby
+#!/usr/bin/env ruby
# = Rudy EC2
#
View
2  bin/rudy-s3
@@ -1,4 +1,4 @@
-#!/usr/bin/ruby
+#!/usr/bin/env ruby
# = Rudy S3
#
View
2  bin/rudy-sdb
@@ -1,4 +1,4 @@
-#!/usr/bin/ruby
+#!/usr/bin/env ruby
# = Rudy SDB
#
View
2  lib/rudy/aws/ec2.rb
@@ -114,5 +114,7 @@ class NoDevice < Rudy::Error; end
autoload :Volumes, 'rudy/aws/ec2/volume'
autoload :Zone, 'rudy/aws/ec2/zone'
autoload :Zones, 'rudy/aws/ec2/zone'
+ autoload :SpotRequest, 'rudy/aws/ec2/spot_request'
+ autoload :SpotRequests, 'rudy/aws/ec2/spot_request'
end
end; end
View
107 lib/rudy/aws/ec2/spot_request.rb
@@ -0,0 +1,107 @@
+
+module Rudy::AWS
+ module EC2
+
+ class SpotRequest < Storable
+ field :id => String
+ field :price => Float
+ field :type => String
+ field :state => String
+ field :ami => String
+ field :keyname => String
+ field :groups => Array
+ field :size => String
+ field :zone => String
+ field :create_time => Time
+ field :instid => String
+
+ def fulfilled?
+ instid && !instid.empty?
+ end
+ end
+
+
+ module SpotRequests
+ include Rudy::AWS::EC2 # important! include,
+ extend self # then extend
+
+ # Return an Array of SpotRequest objects.
+ #
+ # +opts+ supports the following parameters:
+ #
+ # * +:price+
+ # * +:count+
+ # * +:zone+
+ # * +:ami+
+ # * +:group+
+ # * +:size+
+ # * +:keypair+
+ # * +:private+ true or false (default)
+ def create(opts)
+ raise NoAMI unless opts[:ami]
+ raise NoGroup unless opts[:group]
+
+ opts = { :size => 'm1.small', :count => 1}.merge(opts)
+
+ old_opts = {
+ :spot_price => opts[:price].to_s,
+ :instance_count => opts[:count].to_i,
+ :availability_zone_group => (opts[:zone] || @@global.zone).to_s,
+ :image_id => opts[:ami].to_s,
+ :key_name => opts[:keypair].to_s,
+ :security_group => opts[:group].to_s,
+ :instance_type => opts[:size]
+ }
+
+ response = Rudy::AWS::EC2.execute_request({}) { @@ec2.request_spot_instances(old_opts) }
+ self.from_request_set(response['spotInstanceRequestSet'])
+ end
+
+ def list(requests = nil)
+ opts = requests ? {:spot_instance_request_id => requests.map(&:id)} : {}
+
+ response = Rudy::AWS::EC2.execute_request({}) do
+ @@ec2.describe_spot_instance_requests(opts)
+ end
+
+ self.from_request_set(response['spotInstanceRequestSet'])
+ end
+
+ def fulfilled?(requests)
+ list(requests).all? { |r| r.fulfilled? }
+ end
+
+ def self.from_hash(hash)
+ Rudy::AWS::EC2::SpotRequest.new.tap do |request|
+ request.id = hash['spotInstanceRequestId']
+ request.price = hash['spotPrice']
+ request.type = hash['type']
+ request.state = hash['state']
+ request.ami = hash['launchSpecification']['imageId']
+ request.keyname = hash['launchSpecification']['keyName']
+ request.size = hash['launchSpecification']['instanceType']
+ request.zone = hash['availabilityZoneGroup']
+ request.create_time = hash['createTime']
+ request.instid = hash['instanceId']
+
+ request.groups = hash['launchSpecification']['groupSet']['item'].map do |item|
+ item['groupId']
+ end
+ end
+ end
+
+ private
+
+ def self.from_request_set(request_set)
+ return unless request_set.is_a?(Hash)
+ request_set['item'].collect do |request|
+ self.from_hash(request)
+ end
+ end
+
+
+ end
+
+ end
+
+end
View
12 lib/rudy/huxtable.rb
@@ -178,6 +178,14 @@ def current_machine_count
fetch_machine_param(:positions) || 1
end
+ def current_machine_positions
+ if @@global.position
+ Array(@@global.position)
+ else
+ Array.new(current_machine_count.to_i || 1) { |i| i + 1 }
+ end
+ end
+
def current_machine_hostname
# NOTE: There is an issue with Caesars that a keyword that has been
# defined as forced_array (or forced_hash, etc...) is like that for
@@ -203,6 +211,10 @@ def current_machine_size
fetch_machine_param(:size) || 'm1.small'
end
+ def current_machine_pricing
+ fetch_machine_param(:pricing) || fetch_machine_param(:spot) || :on_demand
+ end
+
def current_machine_address(position='01')
#raise NoConfig unless @@config
#raise NoMachinesConfig unless @@config.machines
View
14 lib/rudy/machines.rb
@@ -63,6 +63,20 @@ def create(size=nil)
group
end
+ def from_spot_request(request)
+ instances = Rudy::AWS::EC2::Instances.list(:any, request.map(&:instid))
+
+ current_machine_positions.zip(instances).map do |position, instance|
+ Rudy::Machine.new(position).tap do |machine|
+ machine.instid = instance.awsid
+ machine.created = machine.started = Time.now
+ machine.state = instance.state
+ machine.save
+ sleep 1 # Eventual consistency in SimpleDB
+ end
+ end
+ end
+
def restart
group = list
raise MachineGroupNotRunning, current_machine_group if group.nil?
View
1  lib/rudy/routines.rb
@@ -150,6 +150,7 @@ module Handlers
autoload :Keypair, 'rudy/routines/handlers/keypair'
autoload :Machines, 'rudy/routines/handlers/machines'
autoload :RyeTools, 'rudy/routines/handlers/rye'
+ autoload :SpotRequest, 'rudy/routines/handlers/spot_request'
# The following can't be autoloaded because they call
# Rudy::Routines.add_handler when they're loaded.
require 'rudy/routines/handlers/depends'
View
34 lib/rudy/routines/handlers/spot_request.rb
@@ -0,0 +1,34 @@
+module Rudy; module Routines; module Handlers;
+ module SpotRequest
+ include Rudy::Routines::Handlers::Base
+ extend self
+
+ def needed?
+ current_machine_pricing.is_a?(Hash) || current_machine_pricing.to_sym == :spot
+ end
+
+ def create
+ opts = {
+ :price => current_machine_pricing[:bid],
+ :count => current_machine_positions.length,
+ :size => current_machine_size,
+ :os => current_machine_os,
+ :ami => current_machine_image,
+ :group => current_group_name,
+ :keypair => root_keypairname
+ }
+
+ request = Rudy::AWS::EC2::SpotRequests.create(opts)
+ raise NoMachines unless wait_for_fulfillment_of(request)
+ Rudy::AWS::EC2::SpotRequests.list(request)
+ end
+
+ def wait_for_fulfillment_of(spot_requests)
+ msg = "Waiting for #{spot_requests.length} spot requests to be fulfilled"
+ Rudy::Utils.waiter(2, 180, Rudy::Huxtable.logger, msg, 2) {
+ Rudy::AWS::EC2::SpotRequests.fulfilled?(spot_requests)
+ }
+ end
+
+ end
+end; end; end
View
10 lib/rudy/routines/startup.rb
@@ -58,7 +58,15 @@ def execute
# we'll just grab the list of machines in this group.
# NOTE: Expect errors if there are no machines.
Rudy::Routines.rescue {
- @machines = run? ? Rudy::Machines.create : Rudy::Machines.list
+ @machines = if !run?
+ Rudy::Machines.list
+ elsif Rudy::Routines::Handlers::SpotRequest.needed?
+ request = Rudy::Routines::Handlers::SpotRequest.create
+ Rudy::Machines.from_spot_request(request)
+ else
+ Rudy::Machines.create
+ end
+
@@rset = Rudy::Routines::Handlers::RyeTools.create_set @machines
}
View
2  tryouts/exploration/machine.rb
@@ -1,4 +1,4 @@
-#!/usr/bin/ruby
+#!/usr/bin/env ruby
# Tryout - A basic use-case
#
Please sign in to comment.
Something went wrong with that request. Please try again.