# Copyright 2011-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"). You # may not use this file except in compliance with the License. A copy of # the License is located at # # http://aws.amazon.com/apache2.0/ # # or in the "license" file accompanying this file. This file is # distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF # ANY KIND, either express or implied. See the License for the specific # language governing permissions and limitations under the License. require 'set' require 'uri' module AWS module Core # A configuration object for AWS interfaces and clients. # # == Configuring Credentials # # In order to do anything with AWS you will need to assign credentials. # The simplest method is to assign your credentials into the default # configuration: # # AWS.config(:access_key_id => 'KEY', :secret_access_key => 'SECRET') # # You can also export them into your environment and they will be picked up # automatically: # # export AWS_ACCESS_KEY_ID='YOUR_KEY_ID_HERE' # export AWS_SECRET_ACCESS_KEY='YOUR_SECRET_KEY_HERE' # # For compatability with other AWS gems, the credentials can also be # exported like: # # export AMAZON_ACCESS_KEY_ID='YOUR_KEY_ID_HERE' # export AMAZON_SECRET_ACCESS_KEY='YOUR_SECRET_KEY_HERE' # # == Modifying a Configuration # # Configuration objects are read-only. If you need a different set of # configuration values, call {#with}, passing in the updates # and a new configuration object will be returned. # # config = Configuration.new(:max_retires => 3) # new_config = config.with(:max_retries => 2) # # config.max_retries #=> 3 # new_config.max_retries #=> 2 # # == Global Configuration # # The global default configuration can be found at {AWS.config} # # @attr_reader [String,nil] access_key_id (nil) # AWS access key id credential. # # @attr_reader [String,nil] secret_access_key (nil) # AWS secret access key credential. # # @attr_reader [String,nil] session_token (nil) AWS secret token credential. # # @attr_reader [String] auto_scaling_endpoint ('autoscaling.us-east-1.amazonaws.com') # The service endpoint for Auto Scaling. # # @attr_reader [String] cloud_formation_endpoint ('cloudformation.us-east-1.amazonaws.com') # The service endpoint for AWS CloudFormation. # # @attr_reader [String] cloud_front_endpoint ('cloudfront.amazonaws.com') # The service endpoint for Amazon CloudFront. # # @attr_reader [String] cloud_search ('cloudsearch.us-east-1.amazonaws.com') # The service endpoint for Amazon CloudSearch. # # @attr_reader [String] cloud_watch_endpoint ('monitoring.us-east-1.amazonaws.com') # The service endpoint for Amazon CloudWatch. # # @attr_reader [Boolean] dynamo_db_big_decimals (true) When +true+, # {DynamoDB} will convert number values returned by {DynamoDB::Client} # from strings to BigDecimal objects. If you set this to +false+, # they will be converted from strings into floats (with a potential # loss of precision). # # @attr_reader [String] dynamo_db_endpoint ('dynamodb.us-east-1.amazonaws.com') # The service endpoint for Amazon DynamoDB. # # @attr_reader [String] dynamo_db_retry_throughput_errors (true) When # true, AWS::DynamoDB::Errors::ProvisionedThroughputExceededException # errors will be retried. # # @attr_reader [String] ec2_endpoint ('ec2.amazonaws.com') # The service endpoint for Amazon EC2. # # @attr_reader [String] elasticache_endpoint ('elasticache.us-east-1.amazonaws.com') # # @attr_reader [String] elastic_beanstalk_endpoint ('elasticbeanstalk.us-east-1.amazonaws.com') # The service endpoint for AWS Elastic Beanstalk. # # @attr_reader [String] elastic_transcoder_endpoint ('elastictranscoder.us-east-1.amazonaws.com') # The service endpoint for Elastic Transcoder. # # @attr_reader [String] elb_endpoint ('elasticloadbalancing.us-east-1.amazonaws.com') # The service endpoint for Elastic Load Balancing. # # @attr_reader [String] glacier_endpoint ('glacier.us-east-1.amazonaws.com') # The service endpoint for Amazon Glacier. # # @attr_reader [Object] http_handler The http handler that sends requests # to AWS. Defaults to an HTTP handler built on net/http. # # @attr_reader [Integer] http_idle_timeout The number of seconds a # persistent connection is allowed to sit idle before it should no # longer be used. # # @attr_reader [Integer] http_open_timeout The number of seconds before # the +http_handler+ should timeout while trying to open a new HTTP # session. # # @attr_reader [Integer] http_read_timeout The number of seconds before # the +http_handler+ should timeout while waiting for a HTTP # response. # # @attr_reader [Boolean] http_wire_trace When +true+, the http handler # will log all wire traces to the +:logger+. If a +:logger+ is not # configured, then wire traces will be sent to standard out. # # @attr_reader [String] iam_endpoint ('iam.amazonaws.com') # The service endpoint for AWS Identity Access Management (IAM). # # @attr_reader [String] import_export_endpoint ('importexport.amazonaws.com') # The service endpoint for AWS Import/Export. # # @attr_reader [Logger,nil] logger (nil) The logging interface. # # @attr_reader [Symbol] log_level (:info) The log level. # # @attr_reader [LogFormatter] log_formatter The log message formatter. # # @attr_reader [Integer] max_retries (3) The maximum number of times # service errors (500) should be retried. There is an exponential # backoff in between service request retries, so the more retries the # longer it can take to fail. # # @attr_reader [URI,nil] proxy_uri (nil) The URI of the proxy # to send service requests through. # # @attr_reader [URI,nil] route_53_endpoint ('route53.amazonaws.com') # The service endpoint for Amazon Route 53. # # @attr_reader [URI,nil] redshift_endpoint ('redshift.us-east-1.amazonaws.com') # The service endpoint for Amazon Redshift. # # @attr_reader [URI,nil] rds_endpoint ('rds.us-east-1.amazonaws.com') # The service endpoint for Amazon Relational Database Service (RDS). # # @attr_reader [String] s3_endpoint ('s3.amazonaws.com') # The service endpoint for Amazon S3. # # @attr_reader [Boolean] s3_force_path_style (false) When # +true+, requests will always use path style. This can be useful # for testing environments. # # @attr_reader [Integer] s3_multipart_max_parts (10000) # The maximum number of parts to split a file into when uploading # in parts to S3. # # @attr_reader [Integer] s3_multipart_threshold (16777216) When uploading # data to S3, if the number of bytes to send exceeds # +:s3_multipart_threshold+ then a multi part session is automatically # started and the data is sent up in chunks. The size of each part # is specified by +:s3_multipart_min_part_size+. Defaults to # 16777216 (16MB). # # @attr_reader [Integer] s3_multipart_min_part_size (5242880) # The absolute minimum size (in bytes) each S3 multipart # segment should be defaults to 5242880 (5MB). # # @attr_reader [Symbol] s3_server_side_encryption The algorithm to # use when encrypting object data on the server side. The only # valid value is +:aes256+, which specifies that the object # should be stored using the AES encryption algorithm with 256 # bit keys. Defaults to +nil+, meaning server side encryption # is not used unless specified on each individual call to upload # an object. This option controls the default behavior for the # following method: # # * {S3::S3Object#write} # * {S3::S3Object#multipart_upload} # * {S3::S3Object#copy_from} and {S3::S3Object#copy_to} # * {S3::S3Object#presigned_post} # * {S3::Bucket#presigned_post} # # You can construct an interface to Amazon S3 which always # stores data using server side encryption as follows: # # s3 = AWS::S3.new(:s3_server_side_encryption => :aes256) # # @attr_reader [OpenSSL::PKey::RSA, String] s3_encryption_key # If this is set, AWS::S3::S3Object #read and #write methods will always # perform client-side encryption with this key. The key can be overridden # at runtime by using the :encryption_key option. A value of nil # means that client-side encryption will not be used. # # @attr_reader [Symbol] s3_encryption_materials_location # When set to +:instruction_file+, AWS::S3::S3Object will store # encryption materials in a separate object, instead of the object # metadata. # # @attr_reader [String] simple_db_endpoint ('sdb.amazonaws.com') # The service endpoint for Amazon SimpleDB. # # @attr_reader [Boolean] simple_db_consistent_reads (false) Determines # if all SimpleDB read requests should be done consistently. # Consistent reads are slower, but reflect all changes to SDB. # # @attr_reader [String] simple_email_service_endpoint ('email.us-east-1.amazonaws.com') # The service endpoint for Amazon Simple Email Service. # # @attr_reader [String] simple_workflow_endpoint ('swf.us-east-1.amazonaws.com') # The service endpoint for Amazon Simple Workflow Service. # # @attr_reader [CredentialProvider::Provider] credential_provider # Returns the object that is responsible for loading credentials. # # @attr_reader [String] ssl_ca_file The path to a CA cert bundle in # PEM format. # # If +ssl_verify_peer+ is true (the default) this bundle will be # used to validate the server certificate in each HTTPS request. # The AWS SDK for Ruby ships with a CA cert bundle, which is the # default value for this option. # # @attr_reader [String] ssl_ca_path (nil) # The path the a CA cert directory. # # @attr_reader [Boolean] ssl_verify_peer (true) When +true+ # the HTTP handler validate server certificates for HTTPS requests. # # This option should only be disabled for diagnostic purposes; # leaving this option set to +false+ exposes your application to # man-in-the-middle attacks and can pose a serious security # risk. # # @attr_reader [Boolean] stub_requests (false) When +true+ requests are not # sent to AWS, instead empty responses are generated and returned to # each service request. # # @attr_reader [String] sns_endpoint ('sns.us-east-1.amazonaws.com') # The service endpoint for Amazon SNS. # # @attr_reader [String] sqs_endpoint ('sqs.us-east-1.amazonaws.com') # The service endpoint for Amazon SQS. # # @attr_reader [String] :storage_gateway_endpoint ('storagegateway.us-east-1.amazonaws.com') # The service endpoint for AWS Storage Gateway. # # @attr_reader [String] sts_endpoint ('sts.amazonaws.com') # The service endpoint for AWS Security Token Service. # # @attr_reader [Boolean] use_ssl (true) When +true+, all requests # to AWS are sent using HTTPS instead vanilla HTTP. # # @attr_reader [String] user_agent_prefix (nil) A string prefix to # append to all requests against AWS services. This should be set # for clients and applications built on top of the aws-sdk gem. # class Configuration # Creates a new Configuration object. # @param options (see AWS.config) # @option options (see AWS.config) def initialize options = {} @created = options.delete(:__created__) || {} # :signer is now a deprecated option, this ensures it will still # work, but its now preferred to set :credential_provider instead. # Credential providers don't have to provide a #sign method. if signer = options.delete(:signer) options[:credential_provider] = signer end options.each_pair do |opt_name, value| opt_name = opt_name.to_sym if self.class.accepted_options.include?(opt_name) supplied[opt_name] = value end end end # @return [Hash] Returns a hash with your configured credentials. def credentials credentials = {} [:access_key_id, :secret_access_key, :session_token].each do |opt| if value = credential_provider.send(opt) credentials[opt] = value end end credentials end # Used to create a new Configuration object with the given modifications. # The current configuration object is not modified. # # AWS.config(:max_retries => 2) # # no_retries_config = AWS.config.with(:max_retries => 0) # # AWS.config.max_retries #=> 2 # no_retries_config.max_retries #=> 0 # # You can use these configuration objects returned by #with to create # AWS objects: # # AWS::S3.new(:config => no_retries_config) # AWS::SQS.new(:config => no_retries_config) # # @param options (see AWS.config) # @option options (see AWS.config) # @return [Configuration] Copies the current configuration and returns # a new one with modifications as provided in +:options+. def with options = {} # symbolize option keys options = options.inject({}) {|h,kv| h[kv.first.to_sym] = kv.last; h } values = supplied.merge(options) if supplied == values self # nothing changed else self.class.new(values.merge(:__created__ => @created.dup)) end end # @return [Hash] Returns a hash of all configuration values. def to_h self.class.accepted_options.inject({}) do |h,k| h.merge(k => send(k)) end end alias_method :to_hash, :to_h # @return [Boolean] Returns true if the two configuration objects have # the same values. def eql? other other.is_a?(self.class) and self.supplied == other.supplied end alias_method :==, :eql? # @private def inspect "<#{self.class.name}>" end protected def supplied @supplied ||= {} end class << self # @private def accepted_options @options ||= Set.new end # @private def add_option name, default_value = nil, options = {}, &transform accepted_options << name define_method(name) do |&default_override| value = if supplied.has_key?(name) supplied[name] elsif default_override default_override.call else default_value end transform ? transform.call(self, value) : value end alias_method("#{name}?", name) if options[:boolean] end # Configuration options that have dependencies are re-recreated # anytime one of their dependent configuration values are # changed. # @private def add_option_with_needs name, needs, &create_block accepted_options << name define_method(name) do return supplied[name] if supplied.has_key?(name) needed = needs.inject({}) {|h,need| h.merge(need => send(need)) } unless @created.key?(name) and @created[name][:needed] == needed @created[name] = {} @created[name][:object] = create_block.call(self,needed) @created[name][:needed] = needed end @created[name][:object] end end def add_service name, ruby_name, default_endpoint add_option :"#{ruby_name}_endpoint", default_endpoint add_option(:"#{ruby_name}_port") do |config,value| value || (config.use_ssl? ? 443 : 80) end # users only need to specify service regions when they use # a test endpoint with a sigv4 service add_option(:"#{ruby_name}_region") do |config,value| value || begin endpoint = config.send("#{ruby_name}_endpoint") if endpoint =~ /us-gov/ if matches = endpoint.match(/(us-gov-west-\d+)/) matches[1] else 'us-gov-west-1' # e.g. iam.us-gov.amazonaws.com end elsif matches = endpoint.match(/^.+\.(.+)\.amazonaws.com$/) matches[1] else 'us-east-1' end end end needs = [ :"#{ruby_name}_endpoint", :"#{ruby_name}_port", :"#{ruby_name}_region", :credential_provider, :http_handler, :http_read_timeout, :log_formatter, :log_level, :logger, :proxy_uri, :max_retries, :stub_requests?, :ssl_verify_peer?, :ssl_ca_file, :ssl_ca_path, :use_ssl?, :user_agent_prefix, ] create_block = lambda do |config,client_options| AWS.const_get(name)::Client.new(:config => config) end add_option_with_needs :"#{ruby_name}_client", needs, &create_block end end add_option :access_key_id add_option :secret_access_key add_option :session_token add_option_with_needs :credential_provider, [:access_key_id, :secret_access_key, :session_token] do |cfg,static_creds| CredentialProviders::DefaultProvider.new(static_creds) end add_option :http_open_timeout, 15 add_option :http_read_timeout, 60 add_option :http_idle_timeout, 60 add_option :http_wire_trace, false, :boolean => true add_option_with_needs :http_handler, [ :http_open_timeout, :http_idle_timeout, :http_wire_trace, :logger, ] do |config,handler_options| Http::NetHttpHandler.new(handler_options) end add_option :logger add_option :log_level, :info add_option :log_formatter, LogFormatter.default add_option :max_retries, 3 add_option :proxy_uri do |config,uri| uri ? URI.parse(uri.to_s) : nil end add_option :ssl_verify_peer, true, :boolean => true add_option :ssl_ca_file, File.expand_path(File.dirname(__FILE__) + "/../../../ca-bundle.crt") add_option :ssl_ca_path add_option :stub_requests, false, :boolean => true add_option :use_ssl, true, :boolean => true add_option :user_agent_prefix end end end