amazon-archives/aws-sdk-core-ruby

Can't interact with RDS GovCloud since v2.0.3 when you updated to Api Version to 2014-09-01

Closed this issue · 5 comments

Exception Class: Aws::RDS::Errors::InvalidAction
Exception Message: Could not find operation DescribeReservedDBInstances for version 2014-09-01
Exception Backtrace: 
aws-sdk-core-ruby-e8df800ff372/aws-sdk-core/lib/seahorse/client/plugins/raise_response_errors.rb:15:in `call'
aws-sdk-core-ruby-e8df800ff372/aws-sdk-core/lib/seahorse/client/plugins/param_conversion.rb:22:in `call'
aws-sdk-core-ruby-e8df800ff372/aws-sdk-core/lib/aws-sdk-core/plugins/response_paging.rb:10:in `call'
aws-sdk-core-ruby-e8df800ff372/aws-sdk-core/lib/seahorse/client/request.rb:70:in `send_request'
aws-sdk-core-ruby-e8df800ff372/aws-sdk-core/lib/seahorse/client/base.rb:215:in `describe_reserved_db_instances'

If I revert aws-sdk-core/apis/RDS.api.json all calls to GovCloud RDS work again.

Possible solution is when you create an instance of a client you can specify which api version to use. However, you'd need to support packaging multiple files, which could be a lot of code changes.

By design, we only package the latest stable API version for each service, unless there are backwards incompatible changes. In this case, the API is backwards compatible, but the newest version does not appear to be deployed to every region.

I can offer the following workaround. If you take a copy of the RDS.api.json file from the older gem release, you can do the following:

Aws.add_service(:OlderRDS, {
  api: '/path/to/older/RDS.api.json',
  paginators: File.join(Aws::API_DIR, 'RDS.paginators.json'),
  waiters: File.join(Aws::API_DIR, 'RDS.waiters.json'),
})

# this one uses the older API version
rds = Aws::OlderRDS::Client.new

# 2014-09-01 api version
rds = Aws::RDS::Client.new

This is a supported public interface of the SDK. You simply need the older API file and give it a name. It will create a new namespace, isolating it from the existing RDS client.

Very acceptable solution. Thank you!

One annoying aspect of this solution though is that I now need to handle errors in the new namepsace.

Such as Aws::RDSGovCloud::Errors::ReservedDBInstanceNotFound alongside our existing Aws::RDS::Errors::ReservedDBInstanceNotFound

Do you need to access multiple RDS regions in the same process? If not, you could simply replace the Client API model at runtime for the Aws::RDS::Client.

# changes the api for the life the process
if region_name == 'us-gov-west-1'
  Aws::RDS::Client.set_api('/path/to/older/api.json')
end
rds = Aws::RDS::Client.new(region: region_name)

A little bit more complex solution would be to create a plugin for RDS that changes the API based on the region name given the constructor:

class RegionalApi< Seahorse::Client::Plugin
  option(:api) do |cfg|
    if config.region == 'us-gov-west-1'
      Seahorse::Model::Api.new(MultiJson.load('/path/to/older/api.json')
    end
  end
end

# register the plugin with RDS
Aws::RDS::Client.add_plugin(RegionalApi)

# uses the default API
rds = Aws::RDS::Client.new(region: 'us-east-1')

# uses the older API
rds_gov = Aws::RDS::Client.new(region: 'us-gov-west-1')

In order for this to work generically, you should restrict using features only present in the older API version.

Nice solution. Thanks for the idea!