diff --git a/lib/fog/aws/models/rds/server.rb b/lib/fog/aws/models/rds/server.rb index 33f8ee8d3..5ca7383ab 100644 --- a/lib/fog/aws/models/rds/server.rb +++ b/lib/fog/aws/models/rds/server.rb @@ -60,6 +60,24 @@ module Fog connection.snapshots(:server => self) end + def tags + requires :id + connection.list_tags_for_resource(id). + body['ListTagsForResourceResult']['TagList'] + end + + def add_tags(new_tags) + requires :id + connection.add_tags_to_resource(id, new_tags) + tags + end + + def remove_tags(tag_keys) + requires :id + connection.remove_tags_from_resource(id, tag_keys) + tags + end + def modify(immediately, options) options[:security_group_names] ||= options['DBSecurityGroups'] params = self.class.new(options).attributes_to_params diff --git a/lib/fog/aws/parsers/rds/tag_list_parser.rb b/lib/fog/aws/parsers/rds/tag_list_parser.rb new file mode 100644 index 000000000..2441f46fd --- /dev/null +++ b/lib/fog/aws/parsers/rds/tag_list_parser.rb @@ -0,0 +1,35 @@ +module Fog + module Parsers + module AWS + module RDS + # parses an XML-formatted list of resource tags from AWS + class TagListParser < Fog::Parsers::Base + + # each tag is modeled as a String pair (2-element Array) + def reset + @this_key = nil + @this_value = nil + @tags = Hash.new + @response = {'ListTagsForResourceResult' => {'TagList' => {}}} + end + + def end_element(name) + super + case name + when 'Tag' + @tags[@this_key] = @this_value + @this_key, @this_value = nil, nil + when 'Key' + @this_key = value + when 'Value' + @this_value = value + when 'TagList' + @response['ListTagsForResourceResult']['TagList'] = @tags + end + end + + end + end + end + end +end diff --git a/lib/fog/aws/rds.rb b/lib/fog/aws/rds.rb index 0d8a1503f..b6a7a0759 100644 --- a/lib/fog/aws/rds.rb +++ b/lib/fog/aws/rds.rb @@ -23,6 +23,10 @@ module Fog request :describe_db_engine_versions request :describe_db_reserved_instances + request :add_tags_to_resource + request :list_tags_for_resource + request :remove_tags_from_resource + request :describe_db_snapshots request :create_db_snapshot request :delete_db_snapshot @@ -118,6 +122,8 @@ module Fog end class Real + attr_reader :region + include Fog::AWS::CredentialFetcher::ConnectionMethods # Initialize connection to ELB # @@ -142,8 +148,8 @@ module Fog setup_credentials(options) @connection_options = options[:connection_options] || {} - options[:region] ||= 'us-east-1' - @host = options[:host] || "rds.#{options[:region]}.amazonaws.com" + @region = options[:region] || 'us-east-1' + @host = options[:host] || "rds.#{@region}.amazonaws.com" @path = options[:path] || '/' @persistent = options[:persistent] || false @port = options[:port] || 443 @@ -151,6 +157,10 @@ module Fog @connection = Fog::Connection.new("#{@scheme}://#{@host}:#{@port}#{@path}", @persistent, @connection_options) end + def owner_id + @owner_id ||= Fog::AWS[:rds].security_groups.get('default').owner_id + end + def reload @connection.reset end @@ -181,7 +191,7 @@ module Fog :host => @host, :path => @path, :port => @port, - :version => '2012-01-15' #'2011-04-01' + :version => '2012-09-17' #'2011-04-01' } ) diff --git a/lib/fog/aws/requests/rds/add_tags_to_resource.rb b/lib/fog/aws/requests/rds/add_tags_to_resource.rb new file mode 100644 index 000000000..07bba85b9 --- /dev/null +++ b/lib/fog/aws/requests/rds/add_tags_to_resource.rb @@ -0,0 +1,46 @@ +module Fog + module AWS + class RDS + class Real + + # adds tags to a database instance + # http://docs.amazonwebservices.com/AmazonRDS/latest/APIReference/API_AddTagsToResource.html + # ==== Parameters + # * rds_id <~String> - name of the RDS instance whose tags are to be retrieved + # * tags <~Hash> A Hash of (String) key-value pairs + # ==== Returns + # * response<~Excon::Response>: + # * body<~Hash>: + def add_tags_to_resource(rds_id, tags) + keys = tags.keys.sort + values = keys.map {|key| tags[key]} + request({ + 'Action' => 'AddTagsToResource', + 'ResourceName' => "arn:aws:rds:#{@region}:#{owner_id}:db:#{rds_id}", + :parser => Fog::Parsers::AWS::RDS::Base.new, + }.merge(Fog::AWS.indexed_param('Tags.member.%d.Key', keys)). + merge(Fog::AWS.indexed_param('Tags.member.%d.Value', values))) + end + + end + + class Mock + + def add_tags_to_resource(rds_id, tags) + response = Excon::Response.new + if server = self.data[:servers][rds_id] + self.data[:tags][rds_id].merge! tags + response.status = 200 + response.body = { + "ResponseMetadata"=>{ "RequestId"=> Fog::AWS::Mock.request_id } + } + response + else + raise Fog::AWS::RDS::NotFound.new("DBInstance #{rds_id} not found") + end + end + + end + end + end +end diff --git a/lib/fog/aws/requests/rds/create_db_instance.rb b/lib/fog/aws/requests/rds/create_db_instance.rb index e3f19867b..1cd81ea37 100644 --- a/lib/fog/aws/requests/rds/create_db_instance.rb +++ b/lib/fog/aws/requests/rds/create_db_instance.rb @@ -115,6 +115,8 @@ module Fog response.status = 200 # This values aren't showed at creating time but at available time self.data[:servers][db_name]["InstanceCreateTime"] = Time.now + self.data[:tags] ||= {} + self.data[:tags][db_name] = {} response end diff --git a/lib/fog/aws/requests/rds/list_tags_for_resource.rb b/lib/fog/aws/requests/rds/list_tags_for_resource.rb new file mode 100644 index 000000000..6c37a5ce3 --- /dev/null +++ b/lib/fog/aws/requests/rds/list_tags_for_resource.rb @@ -0,0 +1,44 @@ +module Fog + module AWS + class RDS + class Real + + require 'fog/aws/parsers/rds/tag_list_parser' + + # returns a Hash of tags for a database instance + # http://docs.amazonwebservices.com/AmazonRDS/latest/APIReference/API_ListTagsForResource.html + # ==== Parameters + # * rds_id <~String> - name of the RDS instance whose tags are to be retrieved + # ==== Returns + # * response<~Excon::Response>: + # * body<~Hash>: + def list_tags_for_resource(rds_id) + request( + 'Action' => 'ListTagsForResource', + 'ResourceName' => "arn:aws:rds:#{@region}:#{owner_id}:db:#{rds_id}", + :parser => Fog::Parsers::AWS::RDS::TagListParser.new + ) + end + + end + + class Mock + + def list_tags_for_resource(rds_id) + response = Excon::Response.new + if server = self.data[:servers][rds_id] + response.status = 200 + response.body = { + "ListTagsForResourceResult" => + {"TagList" => self.data[:tags][rds_id]} + } + response + else + raise Fog::AWS::RDS::NotFound.new("DBInstance #{rds_id} not found") + end + end + + end + end + end +end diff --git a/lib/fog/aws/requests/rds/remove_tags_from_resource.rb b/lib/fog/aws/requests/rds/remove_tags_from_resource.rb new file mode 100644 index 000000000..19b297f69 --- /dev/null +++ b/lib/fog/aws/requests/rds/remove_tags_from_resource.rb @@ -0,0 +1,44 @@ +module Fog + module AWS + class RDS + class Real + + # removes tags from a database instance + # http://docs.amazonwebservices.com/AmazonRDS/latest/APIReference/API_RemoveTagsFromResource.html + # ==== Parameters + # * rds_id <~String> - name of the RDS instance whose tags are to be retrieved + # * keys <~Array> A list of String keys for the tags to remove + # ==== Returns + # * response<~Excon::Response>: + # * body<~Hash>: + def remove_tags_from_resource(rds_id, keys) + request( + { 'Action' => 'RemoveTagsFromResource', + 'ResourceName' => "arn:aws:rds:#{@region}:#{owner_id}:db:#{rds_id}", + :parser => Fog::Parsers::AWS::RDS::Base.new, + }.merge(Fog::AWS.indexed_param('TagKeys.member.%d', keys)) + ) + end + + end + + class Mock + + def remove_tags_from_resource(rds_id, keys) + response = Excon::Response.new + if server = self.data[:servers][rds_id] + keys.each {|key| self.data[:tags][rds_id].delete key} + response.status = 200 + response.body = { + "ResponseMetadata"=>{ "RequestId"=> Fog::AWS::Mock.request_id } + } + response + else + raise Fog::AWS::RDS::NotFound.new("DBInstance #{rds_id} not found") + end + end + + end + end + end +end diff --git a/tests/aws/models/rds/tagging_tests.rb b/tests/aws/models/rds/tagging_tests.rb new file mode 100644 index 000000000..62d711646 --- /dev/null +++ b/tests/aws/models/rds/tagging_tests.rb @@ -0,0 +1,20 @@ +Shindo.tests("AWS::RDS | tagging", ['aws', 'rds']) do + + @server = Fog::AWS[:rds].servers.create(rds_default_server_params) + Formatador.display_line "Creating RDS instance #{@server.id}" + Formatador.display_line "Waiting for instance #{@server.id} to be ready" + @server.wait_for { ready? } + + tags1 = {'key1' => 'val1'} + tags2 = {'key2' => 'val2'} + + tests "add and remove tags from a running RDS model" do + returns({}) { @server.tags } + returns(tags1) { @server.add_tags tags1 } + returns(tags1.merge tags2) { @server.add_tags tags2 } + returns(tags2) { @server.remove_tags tags1.keys } + returns(tags2) { @server.tags } + end + + @server.destroy +end diff --git a/tests/aws/requests/rds/helper.rb b/tests/aws/requests/rds/helper.rb index f106af490..73ad10ae7 100644 --- a/tests/aws/requests/rds/helper.rb +++ b/tests/aws/requests/rds/helper.rb @@ -223,6 +223,12 @@ class AWS } }) + LIST_TAGS_FOR_RESOURCE = { + 'ListTagsForResourceResult' => { + 'TagList' => Fog::Nullable::Hash + } + } + end end diff --git a/tests/aws/requests/rds/tagging_tests.rb b/tests/aws/requests/rds/tagging_tests.rb new file mode 100644 index 000000000..413b592ff --- /dev/null +++ b/tests/aws/requests/rds/tagging_tests.rb @@ -0,0 +1,78 @@ +Shindo.tests('AWS::RDS | tagging requests', ['aws', 'rds']) do + @rds = Fog::AWS[:rds] + @db_instance_id = "fog-test-#{rand(65536).to_s(16)}" + Formatador.display_line "Creating RDS instance #{@db_instance_id}" + @rds.create_db_instance(@db_instance_id, 'AllocatedStorage' => 5, + 'DBInstanceClass' => 'db.t1.micro', 'Engine' => 'mysql', + 'MasterUsername' => 'foguser', 'MasterUserPassword' => 'fogpassword') + Formatador.display_line "Waiting for instance #{@db_instance_id} to be ready" + @db = @rds.servers.get(@db_instance_id) + @db.wait_for { ready? } + + tests('success') do + + single_tag = {'key1' => 'value1'} + two_tags = {'key2' => 'value2', 'key3' => 'value3'} + + tests("#add_tags_to_resource with a single tag"). + formats(AWS::RDS::Formats::BASIC) do + result = @rds.add_tags_to_resource(@db_instance_id, single_tag).body + returns(single_tag) do + @rds.list_tags_for_resource(@db_instance_id). + body['ListTagsForResourceResult']['TagList'] + end + result + end + + tests("#add_tags_to_resource with a multiple tags"). + formats(AWS::RDS::Formats::BASIC) do + result = @rds.add_tags_to_resource(@db_instance_id, two_tags).body + returns(single_tag.merge(two_tags)) do + @rds.list_tags_for_resource(@db_instance_id). + body['ListTagsForResourceResult']['TagList'] + end + result + end + + tests("#remove_tags_from_resource").formats(AWS::RDS::Formats::BASIC) do + result = @rds.remove_tags_from_resource( + @db_instance_id, single_tag.keys).body + returns(two_tags) do + @rds.list_tags_for_resource(@db_instance_id). + body['ListTagsForResourceResult']['TagList'] + end + result + end + + tests("#list_tags_for_resource"). + formats(AWS::RDS::Formats::LIST_TAGS_FOR_RESOURCE) do + result = @rds.list_tags_for_resource(@db_instance_id).body + returns(two_tags) do + result['ListTagsForResourceResult']['TagList'] + end + result + end + + end + + tests('failure') do + tests "tagging a nonexisting instance" do + raises(Fog::AWS::RDS::NotFound) do + @rds.add_tags_to_resource('doesnexist', {'key1' => 'value1'}) + end + end + tests "listing tags for a nonexisting instance" do + raises(Fog::AWS::RDS::NotFound) do + @rds.list_tags_for_resource('doesnexist') + end + end + tests "removing tags for a nonexisting instance" do + raises(Fog::AWS::RDS::NotFound) do + @rds.remove_tags_from_resource('doesnexist', ['key1']) + end + end + end + + Formatador.display_line "Destroying DB instance #{@db_instance_id}" + @db.destroy +end