Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

connection spec passes

  • Loading branch information...
commit f6cb9c755b543dc3529d7c56fbd6986afe59a1e7 1 parent 7f465c2
@juozasg juozasg authored
View
80 lib/maws/connection.rb
@@ -1,6 +1,7 @@
require 'right_aws'
require 'maws/mash'
require 'maws/description'
+require 'maws/logger'
class Connection
attr_reader :ec2, :rds, :elb
@@ -62,60 +63,77 @@ def descriptions(services = nil)
end
def ec2_descriptions
- # convert aws description to Description
- descriptions = descriptions = ec2.describe_instances.map {|description|
- description[:service] = :ec2
- Description.create(description)
- }
-
- # filter out terminated when same name exists as a living one and terminated
- by_name = descriptions.group_by { |d| d.name }
-
- # if there is one than more for the same name: delete terminated descriptions, take the last one (trust AWS sorting)
- by_name.map {|name, descriptions|
- if descriptions.count > 1
- descriptions.delete_if {|d| d.status == "terminated"}
- descriptions.replace([descriptions.last]).compact!
- end
- }
+ filter_current_profile_prefix(filter_terminated_ec2_descriptions(all_ec2_descriptions))
+ end
- filter_current_profile_prefix(by_name.values.flatten)
+ def ebs_descriptions
+ filter_current_profile_prefix(all_ebs_descriptions)
end
def rds_descriptions
return [] unless rds
- descriptions = rds.describe_db_instances.map { |description|
- description[:service] = :rds
- Description.create(description)
- }
-
- filter_current_profile_prefix(descriptions)
+ filter_current_profile_prefix(all_rds_descriptions)
end
def elb_descriptions
return [] unless elb
- descriptions = elb.describe_load_balancers.map { |description|
- description[:service] = :elb
+ filter_current_profile_prefix(all_elb_descriptions)
+ end
+
+
+ private
+
+ def all_ec2_descriptions
+ # convert aws description to Description
+ ec2.describe_instances.map {|description|
+ description[:service] = :ec2
Description.create(description)
}
-
- filter_current_profile_prefix(descriptions)
end
- def ebs_descriptions
- descriptions = ec2.describe_volumes.map { |description|
+ def all_ebs_descriptions
+ ec2.describe_volumes.map { |description|
description[:service] = :ebs
Description.create(description)
}
+ end
- filter_current_profile_prefix(descriptions)
+ def all_rds_descriptions
+ rds.describe_db_instances.map { |description|
+ description[:service] = :rds
+ Description.create(description)
+ }
end
- private
+ def all_elb_descriptions
+ elb.describe_load_balancers.map { |description|
+ description[:service] = :elb
+ Description.create(description)
+ }
+ end
+
+ def filter_terminated_ec2_descriptions(descriptions)
+ # filter out terminated when same name exists as a living one and terminated
+ by_name = descriptions.group_by { |d| d.name }
+
+ # if there is one than more for the same name: delete terminated descriptions, take the last one (trust AWS sorting)
+ by_name.map {|name, descriptions|
+ if descriptions.count > 1
+ if descriptions.count {|d| d.status == "terminated"} != descriptions.count
+ # don't delete all of them if all are terminated
+ descriptions.delete_if {|d| d.status == "terminated"}
+ end
+ descriptions.replace([descriptions.last]).compact!
+ end
+ }
+
+ by_name.values.flatten
+ end
def filter_current_profile_prefix(descriptions)
descriptions.delete_if {|d| d.profile != @config.profile.name || d.prefix != @config.prefix}
end
+
end
View
196 spec/aws_connection_spec.rb
@@ -1,196 +0,0 @@
-require 'spec/spec_helper'
-require 'maws/connection'
-require 'maws/instance'
-
-describe 'Connection' do
- before do
- @keyid, @key = aws_test_key
- @ac = Connection.new(@keyid, @key, mash({:region => 'us-west-1', :logger => $right_aws_logger}))
- end
-
- describe "for EC2" do
- it "initializes the interface" do
- @ac.ec2.should_not be_nil
- end
-
- it "the interface uses the region specified in options" do
- c = Connection.new(@keyid, @key, mash({:region => 'ap-northeast-1', :logger => $right_aws_logger}))
- c.ec2.params[:server].should == "ap-northeast-1.ec2.amazonaws.com"
- end
-
- it "lists descriptions" do
- @ac.ec2.should_receive(:describe_instances).and_return([{:aws_id => "ec2_instance1"}, {:aws_id => "ec2_instance2"}])
- @ac.ec2_descriptions.should == [{:aws_id => "ec2_instance1"}, {:aws_id => "ec2_instance2"}]
- end
-
- it "sorts descriptions to list terminated first" do
- @ac.ec2.should_receive(:describe_instances).and_return([
- {:aws_id => "ec2_instance1", :aws_state => "running"},
- {:aws_id => "ec2_instance2", :aws_state => "terminated"},
- {:aws_id => "ec2_instance3", :aws_state => "pending"}])
-
- @ac.ec2_descriptions.should == ([
- {:aws_id => "ec2_instance2", :aws_state => "terminated"},
- {:aws_id => "ec2_instance1", :aws_state => "running"},
- {:aws_id => "ec2_instance3", :aws_state => "pending"}])
-
- end
-
- it "caches descriptions" do
- @ac.ec2.should_receive(:describe_instances).once.and_return([{:aws_id => "ec2_instance1"}, {:aws_id => "ec2_instance2"}])
- @ac.ec2_descriptions.should == [{:aws_id => "ec2_instance1"}, {:aws_id => "ec2_instance2"}]
- @ac.ec2_descriptions.should == [{:aws_id => "ec2_instance1"}, {:aws_id => "ec2_instance2"}]
- end
-
- it "clears cached descriptions" do
- @ac.ec2.should_receive(:describe_instances).twice.and_return([{:aws_id => "ec2_instance1"}, {:aws_id => "ec2_instance2"}])
- @ac.ec2_descriptions.should == [{:aws_id => "ec2_instance1"}, {:aws_id => "ec2_instance2"}]
- @ac.clear_cached_descriptions
- @ac.ec2_descriptions.should == [{:aws_id => "ec2_instance1"}, {:aws_id => "ec2_instance2"}]
- end
- end
-
- describe "for RDS" do
- it "initializes the interface" do
- @ac.rds.should_not be_nil
- end
-
- it "the interface uses the region specified in options" do
- c = Connection.new(@keyid, @key, mash({:region => 'ap-northeast-1', :logger => $right_aws_logger}))
- c.rds.params[:server].should == "ap-northeast-1.rds.amazonaws.com"
- end
-
- it "lists descriptions" do
- @ac.rds.should_receive(:describe_db_instances).and_return([:rds1, :rds2])
- @ac.rds_descriptions.should == [:rds1, :rds2]
- end
-
- it "caches descriptions" do
- @ac.rds.should_receive(:describe_db_instances).once.and_return([:rds1, :rds2])
- @ac.rds_descriptions.should == [:rds1, :rds2]
- @ac.rds_descriptions.should == [:rds1, :rds2]
- end
-
- it "clears cached descriptions" do
- @ac.rds.should_receive(:describe_db_instances).twice.and_return([:rds1, :rds2])
- @ac.rds_descriptions.should == [:rds1, :rds2]
- @ac.clear_cached_descriptions
- @ac.rds_descriptions.should == [:rds1, :rds2]
- end
- end
-
- describe "for ELB" do
- it "initializes the interface" do
- @ac.elb.should_not be_nil
- end
-
- it "the interface uses the region specified in options" do
- c = Connection.new(@keyid, @key, mash({:region => 'ap-northeast-1', :logger => $right_aws_logger}))
- c.elb.params[:server].should == "ap-northeast-1.elasticloadbalancing.amazonaws.com"
- end
-
- it "lists descriptions" do
- @ac.elb.should_receive(:describe_load_balancers).and_return([:elb1, :elb2])
- @ac.elb_descriptions.should == [:elb1, :elb2]
- end
-
- it "caches descriptions" do
- @ac.elb.should_receive(:describe_load_balancers).once.and_return([:elb1, :elb2])
- @ac.elb_descriptions.should == [:elb1, :elb2]
- @ac.elb_descriptions.should == [:elb1, :elb2]
- end
-
- it "clears cached descriptions" do
- @ac.elb.should_receive(:describe_load_balancers).twice.and_return([:elb1, :elb2])
- @ac.elb_descriptions.should == [:elb1, :elb2]
- @ac.clear_cached_descriptions
- @ac.elb_descriptions.should == [:elb1, :elb2]
- end
- end
-
-
- describe "descriptions grouped by name" do
- before do
- Instance::EC2.should_receive(:description_name).any_number_of_times.and_return {|x| x[:aws_id]}
- Instance::RDS.should_receive(:description_name).any_number_of_times.and_return {|x| x.to_s}
- Instance::ELB.should_receive(:description_name).any_number_of_times.and_return {|x| x.to_s}
- end
-
- it "can be selected by name" do
- @ac.ec2.should_receive(:describe_instances).once.and_return([{:aws_id => "ec2_instance1"}, {:aws_id => "ec2_instance2"}])
-
- @ac.description_for_name('ec2_instance1', :ec2).should == {:aws_id => "ec2_instance1"}
- @ac.description_for_name('ec2_instance2', :ec2).should == {:aws_id => "ec2_instance2"}
- end
-
- it "are fetched for ec2 service only" do
- @ac.ec2.should_receive(:describe_instances).once.and_return([])
- @ac.rds.should_not_receive(:describe_db_instances)
- @ac.elb.should_not_receive(:describe_load_balancers)
- @ac.description_for_name('n/a-name', :ec2)
- end
-
- it "are fetched for rds service only" do
- @ac.ec2.should_not_receive(:describe_instances)
- @ac.rds.should_receive(:describe_db_instances).once.and_return([])
- @ac.elb.should_not_receive(:describe_load_balancers)
- @ac.description_for_name('n/a-name', :rds)
- end
-
- it "are fetched for elb service only" do
- @ac.ec2.should_not_receive(:describe_instances)
- @ac.rds.should_not_receive(:describe_db_instances)
- @ac.elb.should_receive(:describe_load_balancers).once.and_return([])
- @ac.description_for_name('n/a-name', :elb)
- end
-
- it "are cached" do
- @ac.ec2.should_receive(:describe_instances).once.and_return([])
- @ac.description_for_name('n/a-name', :ec2)
- @ac.description_for_name('n/a-name', :ec2)
- end
-
- it "are re-fetched after cache is cleared" do
- @ac.ec2.should_receive(:describe_instances).twice.and_return([])
- @ac.description_for_name('n/a-name', :ec2)
- @ac.clear_cached_descriptions
- @ac.description_for_name('n/a-name', :ec2)
- end
- end
-
- it "lists availability zones" do
- @ac.ec2.should_receive(:describe_availability_zones).once.and_return([{:zone_name => 'zone1'}, {:zone_name => 'zone2'}])
- @ac.availability_zones.should == ['zone1', 'zone2']
- end
-
- it "caches availability zones" do
- @ac.ec2.should_receive(:describe_availability_zones).once.and_return([{:zone_name => 'zone1'}, {:zone_name => 'zone2'}])
- @ac.availability_zones.should == ['zone1', 'zone2']
- @ac.availability_zones.should == ['zone1', 'zone2']
- end
-
- it "can look up an AMI id by name" do
- @ac.ec2.should_receive(:describe_images).once.
- with(hash_including(:filters => {'tag:Name' => 'myfavoriteimage'})).
- and_return([{:aws_id => 'ami1'}])
-
- @ac.image_id_for_image_name('myfavoriteimage').should == 'ami1'
- end
-
- it "returns nil when looking up AMI that has no name" do
- @ac.ec2.should_not_receive(:describe_images)
-
- @ac.image_id_for_image_name(nil).should be_nil
- @ac.image_id_for_image_name("").should be_nil
- end
-
- it "will not return AMI id when names are duplicate" do
- @ac.ec2.should_receive(:describe_images).once.
- with(hash_including(:filters => {'tag:Name' => 'myfavoriteimage'})).
- and_return([{:aws_id => 'ami1'}, {:aws_id => 'ami2'}])
-
- @ac.image_id_for_image_name('myfavoriteimage').should be_nil
- end
-
-end
-
View
185 spec/connection_spec.rb
@@ -0,0 +1,185 @@
+require 'spec/spec_helper'
+require 'maws/connection'
+require 'maws/instance'
+
+describe 'Connection' do
+ before do
+ mock_config
+
+ @c = Connection.new(@config)
+ end
+
+ describe "all service interfaces" do
+ it "are not available without connecting" do
+ @c.ec2.should be_nil
+ @c.rds.should be_nil
+ @c.elb.should be_nil
+ end
+
+ it "only EC2 is available when connecting by default" do
+ @c.connect([])
+
+ @c.ec2.should_not be_nil
+ @c.rds.should be_nil
+ @c.elb.should be_nil
+ end
+
+ it "all interfaces are available when specified when connecting" do
+ @c.connect([:elb, :rds])
+
+ @c.ec2.should_not be_nil
+ @c.rds.should_not be_nil
+ @c.elb.should_not be_nil
+ end
+
+ it "use the region specified in options" do
+ @config.region = 'ap-northeast-1'
+ c = Connection.new(@config)
+ c.connect([:elb, :rds])
+
+ c.ec2.params[:server].should == "ap-northeast-1.ec2.amazonaws.com"
+ c.rds.params[:server].should == "ap-northeast-1.rds.amazonaws.com"
+ c.elb.params[:server].should == "ap-northeast-1.elasticloadbalancing.amazonaws.com"
+ end
+ end
+
+ describe "EC2 interface" do
+ before do
+ @c.connect([])
+ end
+
+ it "loads descriptions from AWS EC2" do
+ @c.should_receive(:filter_terminated_ec2_descriptions) {|ds| ds}
+ @c.should_receive(:filter_current_profile_prefix) {|ds| ds}
+
+ @c.ec2.should_receive(:describe_instances).and_return([{:aws_id => "ec2_instance1"}, {:aws_id => "ec2_instance2"}])
+
+ Description.should_receive(:create).with({:aws_id => "ec2_instance1", :service => :ec2}).and_return(:maws_description_1)
+ Description.should_receive(:create).with({:aws_id => "ec2_instance2", :service => :ec2}).and_return(:maws_description_2)
+
+ @c.ec2_descriptions.should == [:maws_description_1, :maws_description_2]
+ end
+
+ it "filters out extra terminated descriptions" do
+ descriptions = [
+ {:name => "d1", :status => "running"},
+
+ {:name => "d2", :status => "running"},
+ {:name => "d2", :status => "terminated"},
+ {:name => "d2", :status => "terminated"},
+
+ {:name => "d3", :status => "terminated"},
+ {:name => "d3", :status => "terminated"},
+
+ {:name => "d4", :status => "terminated"},
+ ].map {|d| mash(d)}
+
+ @c.should_receive(:all_ec2_descriptions).and_return(descriptions.dup)
+ @c.should_receive(:filter_current_profile_prefix) {|ds| ds}
+
+ results = @c.ec2_descriptions
+ results.map {|d| d.name}.should == %w(d1 d2 d3 d4)
+ results.map {|d| d.status}.should == %w(running running terminated terminated)
+ end
+
+ it "filters descriptions for the selected profile and prefix" do
+ @config.profile = mash(:name => "prf")
+ @config.prefix = "new"
+
+ d1 = mash(:name => "prf-app-1", :profile => "prf", :prefix => "")
+ d2 = mash(:name => "prf-app-2", :profile => "prf", :prefix => "new")
+ d3 = mash(:name => "app-2", :profile => "", :prefix => "")
+
+ @c.should_receive(:all_ec2_descriptions).and_return([d1, d2, d3])
+ @c.should_receive(:filter_terminated_ec2_descriptions) {|ds| ds}
+
+ @c.ec2_descriptions.should == [d2]
+ end
+
+ end
+
+ describe "RDS interface" do
+ it "list no descriptions when not connected" do
+ @c.connect([]) # won't connect to RDS
+
+ @c.rds_descriptions.should == []
+ end
+
+ it "loads descriptions from AWS RDS" do
+ @c.connect([:rds])
+
+ @c.should_receive(:filter_current_profile_prefix) {|ds| ds}
+
+ @c.rds.should_receive(:describe_db_instances).and_return([{:aws_id => "rds_instance1"}, {:aws_id => "rds_instance2"}])
+
+ Description.should_receive(:create).with({:aws_id => "rds_instance1", :service => :rds}).and_return(:maws_description_1)
+ Description.should_receive(:create).with({:aws_id => "rds_instance2", :service => :rds}).and_return(:maws_description_2)
+
+ @c.rds_descriptions.should == [:maws_description_1, :maws_description_2]
+ end
+ end
+
+ describe "ELB interface" do
+ it "list no descriptions when not connected" do
+ @c.connect([]) # won't connect to ELB
+
+ @c.elb_descriptions.should == []
+ end
+
+ it "loads descriptions from AWS ELB" do
+ @c.connect([:elb])
+
+ @c.should_receive(:filter_current_profile_prefix) {|ds| ds}
+
+ @c.elb.should_receive(:describe_load_balancers).and_return([{:aws_id => "elb_instance1"}, {:aws_id => "elb_instance2"}])
+
+ Description.should_receive(:create).with({:aws_id => "elb_instance1", :service => :elb}).and_return(:maws_description_1)
+ Description.should_receive(:create).with({:aws_id => "elb_instance2", :service => :elb}).and_return(:maws_description_2)
+
+ @c.elb_descriptions.should == [:maws_description_1, :maws_description_2]
+ end
+ end
+
+
+
+ it "lists available zones" do
+ @c.connect([])
+
+ @c.ec2.should_receive(:describe_availability_zones).once.and_return(
+ [{:zone_name => 'test-region-1a', :zone_state => "unavailable"},
+ {:zone_name => 'test-region-1b', :zone_state => "available"}])
+ @c.available_zones.should == %w(b)
+ end
+
+
+ describe "AMI lookup" do
+ before do
+ @c.connect([])
+ end
+
+ it "finds AMI id from name" do
+ @c.ec2.should_receive(:describe_images).once.
+ with(hash_including(:filters => {'tag:Name' => 'myfavoriteimage'})).
+ and_return([{:aws_id => 'ami1'}])
+
+ @c.image_id_for_image_name('myfavoriteimage').should == 'ami1'
+ end
+
+ it "returns nil when looking up AMI that has no name" do
+ @c.ec2.should_not_receive(:describe_images)
+
+ @c.image_id_for_image_name(nil).should be_nil
+ @c.image_id_for_image_name("").should be_nil
+ end
+
+ it "will not return AMI id when names are duplicate" do
+ @c.ec2.should_receive(:describe_images).once.
+ with(hash_including(:filters => {'tag:Name' => 'myfavoriteimage'})).
+ and_return([{:aws_id => 'ami1'}, {:aws_id => 'ami2'}])
+
+ @c.image_id_for_image_name('myfavoriteimage').should be_nil
+ end
+ end
+
+end
+
View
8 spec/spec_helper.rb
@@ -16,6 +16,14 @@ def aws_test_key
return 'no', 'good'
end
+def mock_config
+ @config = mash()
+ @config.aws_key = mash({:key_id => "keyid", :secret_key => "secretkey"})
+ @config.region = "test-region-1"
+
+ @config
+end
+
class SpecLogger
def initialize
@log_path = "logs/spec.log"
Please sign in to comment.
Something went wrong with that request. Please try again.