Browse files

Rob: Made load_balancer.reload work properly. Refactored store_app_st…

…ack_metadata in application stack. Added load balancers to stack metadata saved in SimpleDB. Added to_simpledb methods to several objects to push object serialization for metadata out of application_stack. Moved the existing inspect output to summarize methods and tweaked the output formatting a bit. Still need to figure out why terminate! goes to lunch because it stops the volumes from being deleted.
  • Loading branch information...
1 parent 0f4e5b8 commit 7900e36d21579bf9889958cbb0a8e233a7a25531 @onyx onyx committed Aug 8, 2009
View
4 lib/awsymandias.rb
@@ -9,6 +9,7 @@
require 'activeresource'
require 'net/telnet'
+ Dir[File.dirname(__FILE__) + "/awsymandias/extensions/**/*.rb"].each { |file| require file }
Dir[File.dirname(__FILE__) + "/awsymandias/**/*.rb"].each { |file| require file }
module Awsymandias
@@ -45,10 +46,9 @@ def verbose_output(message)
end
def describe_stacks
- puts "Stacks: "
Awsymandias.stack_names.each do |stack_name|
stack = EC2::ApplicationStack.find(stack_name)
- puts stack.inspect if stack
+ puts stack.summarize if stack
puts ""
end
end
View
13 lib/awsymandias/addons/right_elb_interface.rb
@@ -41,6 +41,13 @@ def self.bench_elb; @@bench.service; end
attr_reader :last_query_expression
+ def on_exception
+ super
+ rescue RightAws::AwsError => e
+ error = Hash.from_xml(last_response.body)['ErrorResponse']['Error']
+ raise RightAws::AwsError, "#{error['Code']}: #{error['Message']}"
+ end
+
# Creates new RightElb instance.
#
# Params:
@@ -127,8 +134,8 @@ def deregister_instances_from_lb(lb_name, instance_ids)
# :dns_name=>"RobTest-883635706.us-east-1.elb.amazonaws.com",
# :name=>"RobTest",
# :instances=>["i-5752453e"],
- # :listeners=> [{:protocol=>"HTTP", :lb_port=>80, :instance_port=>3080},
- # {:protocol=>"HTTP", :lb_port=>8080, :instance_port=>3081}
+ # :listeners=> [{:protocol=>"HTTP", :load_balancer_port=>80, :instance_port=>3080},
+ # {:protocol=>"HTTP", :load_balancer_port=>8080, :instance_port=>3081}
# ],
# :health_check=> { :healthy_threshold=>10,
# :unhealthy_threshold=>3,
@@ -270,7 +277,7 @@ def tagend(name)
when 'DNSName' then @lb[:dns_name] = @text
when 'Protocol' then @listener[:protocol] = @text
- when 'LoadBalancerPort' then @listener[:lb_port] = @text.to_i
+ when 'LoadBalancerPort' then @listener[:load_balancer_port] = @text.to_i
when 'InstancePort' then @listener[:instance_port] = @text.to_i
when 'HealthCheck' then @lb[:health_check] = @health_check
View
95 lib/awsymandias/ec2/application_stack.rb
@@ -1,7 +1,7 @@
module Awsymandias
module EC2
class ApplicationStack
- attr_reader :name, :simpledb_domain, :unlaunched_instances, :instances, :volumes, :roles
+ attr_reader :name, :simpledb_domain, :unlaunched_instances, :instances, :volumes, :roles, :unlaunched_load_balancers, :load_balancers
DEFAULT_SIMPLEDB_DOMAIN = "application-stack"
@@ -21,14 +21,17 @@ def launch(name, opts={})
end
def initialize(name, opts={})
- opts.assert_valid_keys :instances, :simpledb_domain, :volumes, :roles
+ opts.assert_valid_keys :instances, :simpledb_domain, :volumes, :roles, :load_balancers
- @name = name
+ @name = name
@simpledb_domain = opts[:simpledb_domain] || DEFAULT_SIMPLEDB_DOMAIN
@instances = {}
@unlaunched_instances = {}
@volumes = {}
@roles = {}
+ @load_balancers = {}
+ @unlaunched_load_balancers = {}
+ @terminating = false
if opts[:roles]
@roles = opts[:roles]
@@ -40,7 +43,11 @@ def initialize(name, opts={})
opts[:instances].each { |name, configuration| define_methods_for_instance(name) }
end
- opts[:volumes].each { |name, opts| volume(name, opts) } if opts[:volumes]
+ opts[:volumes].each { |name, configuration| volume(name, configuration) } if opts[:volumes]
+
+ if opts[:load_balancers]
+ opts[:load_balancers].each_pair { |lb_name, config| @unlaunched_load_balancers[lb_name] = config }
+ end
end
def self.define(name, &block)
@@ -78,9 +85,20 @@ def launch
@instances[instance_name].name = instance_name
@unlaunched_instances.delete instance_name
end
+ store_app_stack_metadata!
+
+ @unlaunched_load_balancers.each_pair do |lb_name, params|
+ instance_names = params[:instances]
+ params[:instances] = params.delete(:instances).map { |instance_name| @instances[instance_name].instance_id } if params[:instances]
+ params[:name] = lb_name
+ @load_balancers[lb_name] = Awsymandias::LoadBalancer.launch(params)
+ @unlaunched_load_balancers.delete lb_name
+ end
+ store_app_stack_metadata!
attach_volumes
store_app_stack_metadata!
+
self
end
@@ -121,20 +139,33 @@ def create_and_attach_volumes_to_instances(instances, options)
end
def reload
- raise "Can't reload unless launched" unless launched?
+ raise "Can't reload unless launched" unless (launched? || terminating?)
@instances.values.each(&:reload)
+ @load_balancers.values.each(&:reload)
self
end
def terminate!
+ @terminating = true
store_app_stack_metadata!
instances.each do |instance|
instance.terminate! if instance.running?
+ @instances.delete(instance.name)
+ end
+
+ load_balancers.values.each do |load_balancer|
+ load_balancer.terminate! if load_balancer.launched?
+ @load_balancers.delete(load_balancer.name)
end
+
remove_app_stack_metadata!
self
end
+ def terminating?
+ @terminating
+ end
+
def launched?
instances.any?
end
@@ -156,37 +187,35 @@ def running_cost
@instances.values.sum { |instance| instance.running_cost }
end
- def to_s
- inspect
- end
-
- def inspect
+ def summarize
output = []
- output << " #{name}"
- @instances.each_pair do |instance_name, instance|
- output << " #{instance_name}\t#{instance.instance_id}\t#{instance.aws_state}\t#{instance.aws_availability_zone}\t#{instance.aws_instance_type.name}\t#{instance.aws_image_id}\t#{instance.public_dns}\tLaunched #{instance.aws_launch_time}"
- instance.attached_volumes.each do |volume|
- output << " #{volume.aws_id} -> #{volume.aws_device}"
- end
+ output << "Stack '#{name}'"
+ @instances.each_pair do |name, instance|
+ output << instance.summarize
+ output << ''
+ end
+ @load_balancers.each_pair do |lb_name, lb|
+ output << lb.summarize
+ output << ''
end
- output
+ output.flatten.join("\n")
end
private
def store_app_stack_metadata!
metadata = {}
- metadata[:unlaunched_instances] = @unlaunched_instances
-
- metadata[:instances] = {}
- @instances.each_pair do |instance_name, instance|
- metadata[:instances][instance_name] = { :aws_instance_id => instance.aws_instance_id,
- :name => instance.name,
- :attached_volumes => instance.attached_volumes.map { |vol| vol.aws_id }
- }
- end
- metadata[:roles] = @roles
+ [:unlaunched_instances, :unlaunched_load_balancers, :roles].each do |item_name|
+ metadata[item_name] = instance_variable_get "@#{item_name}"
+ end
+
+ [:instances, :load_balancers].each do |collection|
+ metadata[collection] = {}
+ instance_variable_get("@#{collection}").each_pair do |item_name, item|
+ metadata[collection][item_name] = item.to_simpledb
+ end
+ end
Awsymandias::SimpleDB.put @simpledb_domain, @name, metadata
end
@@ -199,9 +228,19 @@ def reload_from_metadata!
metadata = Awsymandias::SimpleDB.get @simpledb_domain, @name
unless metadata.empty?
+ metadata[:unlaunched_load_balancers] ||= []
+ metadata[:load_balancers] ||= []
+ @unlaunched_load_balancers = metadata[:unlaunched_load_balancers]
+ unless metadata[:load_balancers].empty?
+ live_lbs = Awsymandias::LoadBalancer.find( metadata[:load_balancers].keys ).index_by(&:name)
+ metadata[:load_balancers].each_pair do |lb_name, lb|
+ @load_balancers[lb_name] = live_lbs[lb_name]
+ end
+ end
+
@unlaunched_instances = metadata[:unlaunched_instances]
- if !metadata[:instances].empty?
+ unless metadata[:instances].empty?
live_instances = Awsymandias::Instance.find(:all, :instance_ids =>
metadata[:instances].values.map { |inst| inst[:aws_instance_id] }
).index_by(&:instance_id)
View
17 lib/awsymandias/instance.rb
@@ -13,6 +13,16 @@ def public_dns; dns_name; end
def private_dns; private_dns_name; end
def dns_hostname; name.to_s.gsub(/_/,'-'); end
+ def summarize
+ output = []
+ output << " Instance '#{name}'\t#{instance_id}\t#{public_dns}\tLaunched #{aws_launch_time}"
+ output << " #{aws_state}\t#{aws_availability_zone}\t#{aws_instance_type.name}\t#{aws_image_id}"
+ attached_volumes.each do |volume|
+ output << " #{volume.aws_id} -> #{volume.aws_device}"
+ end
+ output.join("\n")
+ end
+
def attached_volumes
Awsymandias::RightAws.describe_volumes.select { |volume| volume.aws_instance_id == instance_id }
end
@@ -69,6 +79,13 @@ def to_params
}
end
+ def to_simpledb
+ { :aws_instance_id => aws_instance_id,
+ :name => name,
+ :attached_volumes => attached_volumes.map { |vol| vol.to_simpledb }
+ }
+ end
+
def aws_instance_type
Awsymandias::EC2.instance_types[@attributes['aws_instance_type']]
end
View
157 lib/awsymandias/load_balancer.rb
@@ -0,0 +1,157 @@
+module Awsymandias
+ class LoadBalancer < ActiveResource::Base
+ attr_reader :name, :aws_created_at, :availability_zones, :dns_name, :name, :instances, :listeners, :health_check
+
+ def self.find(*names)
+ names = nil if names.is_a?(Array) && names.empty?
+ Awsymandias::RightElb.connection.describe_lbs(names).map { |lb| Awsymandias::LoadBalancer.new lb }
+ end
+
+ def self.launch(attribs)
+ new_lb = LoadBalancer.new(attribs)
+ new_lb.launch
+ new_lb
+ end
+
+ def self.valid_load_balancer_name?(lb_name); (lb_name.to_s =~ /\A[a-zA-Z0-9-]+\Z/) != nil; end
+
+ def initialize(attribs)
+ raise "Load balancer name can only contain alphanumeric characters or a dash." unless LoadBalancer.valid_load_balancer_name?(attribs[:name])
+
+ @listeners = [attribs[:listeners]].flatten.map { |listener| Listener.new listener }
+ @terminated = false
+
+ [:availability_zones, :dns_name, :name, :instances].each do |attribute_name|
+ instance_variable_set "@#{attribute_name.to_s}", attribs[attribute_name]
+ end
+
+ self.health_check = attribs[:health_check]
+ end
+
+ def availability_zones=(zones = [])
+ zones = [zones].flatten
+
+ zones_to_disable = @availability_zones - zones
+ Awsymandias::RightElb.connection.disable_availability_zones_for_lb @name, zones_to_disable if !zones_to_disable.empty?
+
+ zones_to_enable = zones - @availability_zones
+ Awsymandias::RightElb.connection.enable_availability_zones_for_lb @name, zones_to_enable if !zones_to_enable.empty?
+
+ @availability_zones = Awsymandias::RightElb.connection.describe_lbs([@name]).first[:availability_zones]
+ end
+
+ def health_check=(attribs)
+ need_to_save = !@health_check.nil?
+ @health_check = HealthCheck.new(self, attribs || {})
+ @health_check.save if launched? && need_to_save
+ end
+
+ def instances=(instance_ids = [])
+ instance_ids = [instance_ids].flatten
+
+ instances_to_deregister = @instances - instance_ids
+ Awsymandias::RightElb.connection.deregister_instances_from_lb @name, instances_to_deregister if !instances_to_deregister.empty?
+
+ instances_to_register = instance_ids - @instances
+ Awsymandias::RightElb.connection.register_instances_with_lb @name, instances_to_register if !instances_to_register.empty?
+
+ @instances = Awsymandias::RightElb.connection.describe_lbs([@name]).first[:instances]
+ end
+
+ def instance_health
+ Awsymandias::RightElb.connection.describe_instance_health @name
+ end
+
+ def launch
+ raise "Load balancers must have at least one listener defined." if @listeners.empty?
+ raise "Load balancers must have at least one availability zone defined." if @availability_zones.empty?
+
+ listener_params = @listeners.map { |l| l.attributes }
+ @dns_name = Awsymandias::RightElb.connection.create_lb @name, @availability_zones, listener_params
+ end
+
+ def launched?
+ !@dns_name.nil?
+ end
+
+ def reload
+ return unless launched?
+ data = Awsymandias::RightElb.connection.describe_lbs([self.name]).first
+ data.symbolize_keys!
+ data.keys.each do |attribute_name|
+ instance_variable_set "@#{attribute_name}", data[attribute_name]
+ end
+ self
+ end
+
+ def summarize
+ output = []
+ output << " Load Balancer '#{name}':\t#{dns_name || "Not Launched"}"
+ output << " Health Check: "
+ health_check.attributes.each_pair {|attrib, value| output.last << "#{attrib}: #{value}\t"}
+ output << " Avail. Zones: #{availability_zones.join "," }"
+ output << " Instances: #{instances.join "," }"
+ output << " Listeners:"
+ listeners.each do |listener|
+ output << " "
+ listener.attributes.each_pair {|attrib, value| output.last << "#{attrib}: #{value}\t"}
+ end
+ output.join("\n")
+ end
+
+ def terminate!
+ Awsymandias::RightElb.connection.delete_lb name
+ @terminated = true
+ end
+
+ def terminated?
+ @terminated
+ end
+
+ def to_simpledb
+ name
+ end
+
+ class HealthCheck
+ ATTRIBUTE_NAMES = [:healthy_threshold, :unhealthy_threshold, :interval, :target, :timeout]
+ attr_accessor *ATTRIBUTE_NAMES
+
+ DEFAULT_SETTINGS = {:healthy_threshold => 3,
+ :unhealthy_threshold => 5,
+ :interval => 30,
+ :target => "HTTP:80",
+ :timeout => 5
+ }
+
+ def initialize(lb, attribs = {})
+ @lb = lb
+ attribs.merge!(HealthCheck::DEFAULT_SETTINGS)
+
+ HealthCheck::DEFAULT_SETTINGS.each_pair { |key, value| instance_variable_set "@#{key}", value }
+ end
+
+ def attributes
+ returning({}) do |attribs|
+ HealthCheck::ATTRIBUTE_NAMES.each { |attrib| attribs[attrib] = instance_variable_get "@#{attrib}"}
+ end
+ end
+
+ def save
+ Awsymandias::RightElb.connection.configure_health_check attributes
+ end
+ end
+
+ class Listener
+ ATTRIBUTE_NAMES = [:protocol, :load_balancer_port, :instance_port]
+ hash_initializer *ATTRIBUTE_NAMES
+ attr_reader *ATTRIBUTE_NAMES
+
+ def attributes
+ returning({}) do |attribs|
+ Listener::ATTRIBUTE_NAMES.each { |attrib| attribs[attrib] = instance_variable_get "@#{attrib}"}
+ end
+ end
+ end
+
+ end
+end
View
16 lib/awsymandias/right_elb.rb
@@ -0,0 +1,16 @@
+module Awsymandias
+ module RightElb
+ class << self
+ def connection
+ @connection ||= ::RightAws::ElbInterface.new(Awsymandias.access_key_id,
+ Awsymandias.secret_access_key,
+ {:logger => Logger.new("/dev/null")})
+ end
+
+ def describe_lbs(list = [])
+ LoadBalancer.find(*list)
+ end
+
+ end
+ end
+end
View
4 lib/awsymandias/snapshot.rb
@@ -25,5 +25,9 @@ def tag(name)
SimpleDB.put('snapshots', name, :snapshot_id => id)
end
+ def to_simpledb
+ id
+ end
+
end
end
View
10 lib/awsymandias/stack_definition.rb
@@ -1,12 +1,13 @@
module Awsymandias
class StackDefinition
- attr_reader :name, :defined_instances, :defined_volumes, :defined_roles
+ attr_reader :name, :defined_instances, :defined_volumes, :defined_roles, :defined_load_balancers
def initialize(name)
@name = name
@defined_instances = {}
@defined_volumes = {}
@defined_roles = {}
+ @defined_load_balancers = {}
end
def instance(name, config={})
@@ -23,6 +24,10 @@ def instances(*names)
end
end
+ def load_balancer(name, configuration = {})
+ @defined_load_balancers[name] = configuration
+ end
+
def role(name, *instance_names)
@defined_roles[name] ||= []
@defined_roles[name] += instance_names
@@ -41,7 +46,8 @@ def build_stack
Awsymandias::EC2::ApplicationStack.new(name,
:instances => defined_instances,
:volumes => defined_volumes,
- :roles => defined_roles
+ :roles => defined_roles,
+ :load_balancers => defined_load_balancers
)
end
View
4 lib/awsymandias/volume.rb
@@ -62,7 +62,9 @@ def reload
self
end
-
+ def to_simpledb
+ aws_id
+ end
end
end
View
51 spec/ec2/application_stack_spec.rb
@@ -57,6 +57,17 @@ def stub_instance(stubs={})
instance
end
+ def stub_load_balancer(stubs={})
+ lb_defaults = {:aws_created_at => "Tue Aug 04 11:14:27 UTC 2009",
+ :availability_zones=>["us-east-1b"],
+ :dns_name=>nil,
+ :name=>"RobTest",
+ :instances=>["i-5752453e"],
+ :listeners=> [{:protocol=>"HTTP", :load_balancer_port=>80, :instance_port=>3080}],
+ }
+ LoadBalancer.new(lb_defaults.merge(stubs))
+ end
+
before :each do
@simpledb = SimpleDBStub.new
SimpleDB.stub!(:connection).and_return @simpledb
@@ -361,6 +372,46 @@ def stub_instance(stubs={})
s.launch
end
+
+ it "should launch its load balancers when launched" do
+ lb_params = {:availability_zones=>["us-east-1b"],
+ :instances=>[:db],
+ :listeners=> [{:protocol=>"HTTP", :load_balancer_port=>80, :instance_port=>3080}]
+ }
+ s = ApplicationStack.define("test") do |s|
+ s.instance :db, :instance_type => InstanceTypes::C1_XLARGE
+ s.load_balancer :lb, lb_params
+ end
+
+ Instance.should_receive(:launch).and_return(stub_instance)
+ s.should_receive(:store_app_stack_metadata!).any_number_of_times.and_return(nil)
+ lb = stub_load_balancer
+
+ LoadBalancer.should_receive(:launch).with(lb_params).and_return(lb)
+ s.launch
+ end
+
+ it "should remove the load balancer from unlaunched_load_balancers after it has been launched" do
+ lb_params = {:availability_zones=>["us-east-1b"],
+ :instances=>[:db],
+ :listeners=> [{:protocol=>"HTTP", :load_balancer_port=>80, :instance_port=>3080}]
+ }
+ s = ApplicationStack.define("test") do |s|
+ s.instance :db, :instance_type => InstanceTypes::C1_XLARGE
+ s.load_balancer :lb, lb_params
+ end
+ s.should_receive(:store_app_stack_metadata!).any_number_of_times.and_return(nil)
+
+ Instance.should_receive(:launch).and_return(stub_instance)
+
+ lb = stub_load_balancer
+ LoadBalancer.should_receive(:launch).with(lb_params).and_return(lb)
+
+ s.unlaunched_load_balancers[:lb].should_not be_nil
+ s.launch
+ s.unlaunched_load_balancers[:lb].should be_nil
+ end
+
end
describe "launched?" do
View
250 spec/load_balancer_spec.rb
@@ -0,0 +1,250 @@
+require 'rubygems'
+require 'spec'
+require File.expand_path(File.dirname(__FILE__) + "/../lib/awsymandias")
+
+module Awsymandias
+ describe LoadBalancer do
+
+ before :each do
+ Awsymandias::RightElb.should_receive(:connection).any_number_of_times.and_return(@elb_connection = mock('connection'))
+ @elb_connection.should_receive(:configure_health_check).any_number_of_times.and_return(lambda { @lb.health_check })
+ end
+
+ def raises_error(message)
+ @error_raised = false
+ yield
+ rescue => error
+ @error_raised = true
+ fail "Expected an exception to be raised with message '#{message}' but got '#{error.message}'" if message != error.message
+ ensure
+ fail "Expected an exception to be raised with message '#{message}' but no exception was raised" unless @error_raised
+ end
+
+ DEFAULT_LB_ATTRIBUTES = {:aws_created_at => "Tue Aug 04 11:14:27 UTC 2009",
+ :availability_zones=>["us-east-1b"],
+ :dns_name=>"RobTest-883635706.us-east-1.elb.amazonaws.com",
+ :name=>"RobTest",
+ :instances=>["i-5752453e"],
+ :listeners=> [{:protocol=>"HTTP", :load_balancer_port=>80, :instance_port=>3080},
+ {:protocol=>"HTTP", :load_balancer_port=>8080, :instance_port=>3081}
+ ],
+ :health_check=> { :healthy_threshold=>10,
+ :unhealthy_threshold=>3,
+ :interval=>31,
+ :target=>"TCP:3081",
+ :timeout=>6
+ }
+ }
+
+ def populated_load_balancer(attribs = {})
+ @lb = LoadBalancer.new DEFAULT_LB_ATTRIBUTES.merge(attribs)
+ @lb
+ end
+
+ describe "valid_load_balancer_name" do
+ it "should return true for a valid load balancer name" do
+ LoadBalancer.valid_load_balancer_name?('balancer-1').should == true
+ end
+
+ it "should return false for a load balancer name containing an underscore" do
+ LoadBalancer.valid_load_balancer_name?('balancer_1').should == false
+ end
+
+ it "should return false for a load balancer name containing a period" do
+ LoadBalancer.valid_load_balancer_name?('balancer.1').should == false
+ end
+
+ it "should return false for a load balancer name containing a colon" do
+ LoadBalancer.valid_load_balancer_name?('balancer:1').should == false
+ end
+
+ it "should return false for a load balancer name containing a slash" do
+ LoadBalancer.valid_load_balancer_name?('balancer/1').should == false
+ end
+
+ it "should return false for an invalid load balancer name that is a symbol" do
+ LoadBalancer.valid_load_balancer_name?(:balancer_1).should == false
+ end
+ end
+
+ describe 'initialize' do
+ it "should initialize new HealthCheck and Listener objects" do
+ lb = populated_load_balancer
+ lb.health_check.is_a?(LoadBalancer::HealthCheck).should == true
+ lb.listeners.first.is_a?(LoadBalancer::Listener).should == true
+ end
+
+ it "should raise an error when trying to initialize a load balancer with an invalid name" do
+ raises_error("Load balancer name can only contain alphanumeric characters or a dash.") do
+ lb = populated_load_balancer :name => "invalid_name"
+ end
+ end
+ end
+
+ describe "availability_zones=" do
+ it "should remove availability_zones that are not in the passed list but are enabled in the load balancer" do
+ lb = populated_load_balancer :availability_zones => [:zone_1, :zone_2]
+
+ desired_zones = [:zone_2]
+
+ @elb_connection.should_receive(:disable_availability_zones_for_lb).with(lb.name, [:zone_1]).and_return(desired_zones)
+ @elb_connection.should_receive(:describe_lbs).with([lb.name]).and_return([{:availability_zones => desired_zones}])
+ lb.availability_zones = desired_zones
+ end
+
+ it "should add availability_zones that are in the passed list but not already in the load balancer" do
+ lb = populated_load_balancer :availability_zones => [:zone_1]
+
+ desired_zones = [:zone_1, :zone_2]
+
+ @elb_connection.should_receive(:enable_availability_zones_for_lb).with(lb.name, [:zone_2]).and_return(desired_zones)
+ @elb_connection.should_receive(:describe_lbs).with([lb.name]).and_return([{:availability_zones => desired_zones}])
+
+ lb.availability_zones = desired_zones
+ end
+
+ it "should update the availability_zones attribute" do
+ lb = populated_load_balancer :availability_zones => [:zone_1]
+
+ desired_zones = [:zone_1, :zone_2]
+
+ @elb_connection.should_receive(:enable_availability_zones_for_lb).with(lb.name, [:zone_2]).and_return(desired_zones)
+ @elb_connection.should_receive(:describe_lbs).with([lb.name]).and_return([{:availability_zones => desired_zones}])
+
+ lb.availability_zones = desired_zones
+ lb.availability_zones.should == desired_zones
+ end
+ end
+
+ describe "find" do
+ # it "should return an array of Awsymandias::Snapshot objects." do
+ # connection = mock('connection')
+ # connection.should_receive(:describe_snapshots).and_return(
+ # [{:aws_id => :some_snapshot_id}, {:aws_id => :another_snapshot_id}]
+ # )
+ # Awsymandias::RightAws.should_receive(:connection).and_return(connection)
+ #
+ # snapshots = Snapshot.find
+ # snapshots.map(&:aws_id).should == [:some_snapshot_id, :another_snapshot_id]
+ # snapshots.map(&:class).uniq.should == [Awsymandias::Snapshot]
+ # end
+ end
+
+ describe "health_check=" do
+ it "should not call save if the load_balancer is not launched" do
+ health_check = LoadBalancer::HealthCheck.new populated_load_balancer
+ LoadBalancer::HealthCheck.should_receive(:new).and_return(health_check)
+ health_check.should_receive(:save).never
+
+ lb = populated_load_balancer :dns_name => nil
+ end
+
+ it "should try to configure health check with new parameters" do
+ custom_settings = {:healthy_threshold => :custom_healthy_threshold,
+ :unhealthy_threshold => :custom_unhealthy_threshold,
+ :interval => :custom_interval,
+ :target => :custom_target,
+ :timeout => :custom_timeout
+ }
+
+ lb = populated_load_balancer
+ health_check = LoadBalancer::HealthCheck.new lb, custom_settings
+
+ custom_settings.each_pair { |setting_name, value| health_check.send(setting_name).should == value }
+ end
+
+ it "should use defaults for attributes that are not passed in" do
+ lb = populated_load_balancer
+ health_check = LoadBalancer::HealthCheck.new lb
+
+ LoadBalancer::HealthCheck::DEFAULT_SETTINGS.each_pair { |setting_name, value| health_check.send(setting_name).should == value }
+ end
+ end
+
+ describe "instances=" do
+ it "should deregister instances that are not in the passed list but are registered with the load balancer" do
+ lb = populated_load_balancer :instances => [:instance_1, :instance_2]
+
+ desired_instances = [:instance_2]
+
+ @elb_connection.should_receive(:deregister_instances_from_lb).with(lb.name, [:instance_1]).and_return(desired_instances)
+ @elb_connection.should_receive(:describe_lbs).with([lb.name]).and_return([{:instances => desired_instances}])
+ lb.instances = desired_instances
+ end
+
+ it "should register instances that are in the passed list but are not registered with the load balancer" do
+ lb = populated_load_balancer :instances => [:instance_1]
+
+ desired_instances = [:instance_1, :instance_2]
+
+ @elb_connection.should_receive(:register_instances_with_lb).with(lb.name, [:instance_2]).and_return(desired_instances)
+ @elb_connection.should_receive(:describe_lbs).with([lb.name]).and_return([{:instances => desired_instances}])
+ lb.instances = desired_instances
+ end
+
+ it "should update the instances attribute" do
+ lb = populated_load_balancer :instances => [:instance_1]
+
+ desired_instances = [:instance_1, :instance_2]
+
+ @elb_connection.should_receive(:register_instances_with_lb).with(lb.name, [:instance_2]).and_return(desired_instances)
+ @elb_connection.should_receive(:describe_lbs).with([lb.name]).and_return([{:instances => desired_instances}])
+
+ lb.instances = desired_instances
+ lb.instances.should == desired_instances
+ end
+ end
+
+ describe "launch" do
+ it ", the class method, should instantiate a new load balancer and launch it" do
+ listener_hash = {:protocol => 'HTTP', :load_balancer_port => 80, :instance_port => 8080}
+ @elb_connection.should_receive(:create_lb).with(:lbname, [:some_availability_zones], [ listener_hash ])
+ LoadBalancer.launch({:name => :lbname,
+ :availability_zones => [:some_availability_zones],
+ :listeners => [ listener_hash ]})
+ end
+
+ it "should raise an error if you try to launch a load balancer with no availability zones" do
+ lb = populated_load_balancer :availability_zones => []
+ raises_error("Load balancers must have at least one availability zone defined.") do
+ lb.launch
+ end
+ end
+
+ it "should raise an error if you try to launch a load balancer with no listeners" do
+ lb = populated_load_balancer :listeners => []
+ raises_error("Load balancers must have at least one listener defined.") do
+ lb.launch.should
+ end
+ end
+
+ it "should set the dns_name when launched" do
+ lb = populated_load_balancer :dns_name => nil
+ @elb_connection.should_receive(:create_lb).and_return(:a_dns_name)
+ lb.launch
+ lb.dns_name.should == :a_dns_name
+ end
+ end
+
+ describe "reload" do
+ it "should not try to refresh if not launched" do
+ lb = populated_load_balancer :dns_name => nil
+ @elb_connection.should_receive(:describe_lbs).never
+ lb.reload
+ end
+
+ it "should refresh itself from AWS data" do
+ last_hour = Time.now - 1.hour
+ lb = populated_load_balancer :aws_created_at => last_hour.to_s
+
+ time_now = Time.now
+ expected_attributes = DEFAULT_LB_ATTRIBUTES.merge({ :aws_created_at => time_now.to_s })
+ @elb_connection.should_receive(:describe_lbs).with([lb.name]).and_return([expected_attributes])
+
+ lb.aws_created_at.to_s == last_hour.to_s
+ lb.reload
+ lb.aws_created_at.to_s.should == time_now.to_s
+ end
+ end
+ end
+end

0 comments on commit 7900e36

Please sign in to comment.