aws-sdk-core 2.0.3 → 2.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (62) hide show
  1. checksums.yaml +4 -4
  2. data/apis/CognitoIdentity.api.json +16 -4
  3. data/apis/EC2.paginators.json +3 -0
  4. data/apis/ElastiCache.api.json +164 -15
  5. data/apis/IAM.api.json +326 -1
  6. data/apis/IAM.resources.json +2 -29
  7. data/apis/Route53Domains.api.json +56 -0
  8. data/apis/S3.api.json +1 -1
  9. data/endpoints.json +62 -76
  10. data/lib/aws-sdk-core.rb +4 -14
  11. data/lib/aws-sdk-core/api/documenter.rb +1 -1
  12. data/lib/aws-sdk-core/api/service_customizations.rb +1 -0
  13. data/lib/aws-sdk-core/assume_role_credentials.rb +46 -0
  14. data/lib/aws-sdk-core/autoscaling.rb +3 -3
  15. data/lib/aws-sdk-core/cloudformation.rb +3 -3
  16. data/lib/aws-sdk-core/cloudfront.rb +4 -4
  17. data/lib/aws-sdk-core/cloudsearch.rb +3 -3
  18. data/lib/aws-sdk-core/cloudsearchdomain.rb +2 -2
  19. data/lib/aws-sdk-core/cloudtrail.rb +3 -3
  20. data/lib/aws-sdk-core/cloudwatch.rb +3 -3
  21. data/lib/aws-sdk-core/cloudwatchlogs.rb +3 -3
  22. data/lib/aws-sdk-core/cognitoidentity.rb +2 -2
  23. data/lib/aws-sdk-core/cognitosync.rb +2 -2
  24. data/lib/aws-sdk-core/datapipeline.rb +3 -3
  25. data/lib/aws-sdk-core/directconnect.rb +3 -3
  26. data/lib/aws-sdk-core/dynamodb.rb +4 -4
  27. data/lib/aws-sdk-core/ec2.rb +5 -5
  28. data/lib/aws-sdk-core/elasticache.rb +3 -3
  29. data/lib/aws-sdk-core/elasticbeanstalk.rb +3 -3
  30. data/lib/aws-sdk-core/elasticloadbalancing.rb +3 -3
  31. data/lib/aws-sdk-core/elastictranscoder.rb +4 -4
  32. data/lib/aws-sdk-core/emr.rb +3 -3
  33. data/lib/aws-sdk-core/endpoint_provider.rb +16 -78
  34. data/lib/aws-sdk-core/glacier.rb +5 -5
  35. data/lib/aws-sdk-core/iam.rb +4 -4
  36. data/lib/aws-sdk-core/importexport.rb +3 -3
  37. data/lib/aws-sdk-core/instance_profile_credentials.rb +13 -45
  38. data/lib/aws-sdk-core/kinesis.rb +3 -3
  39. data/lib/aws-sdk-core/opsworks.rb +4 -4
  40. data/lib/aws-sdk-core/pageable_response.rb +18 -0
  41. data/lib/aws-sdk-core/plugins/regional_endpoint.rb +1 -5
  42. data/lib/aws-sdk-core/plugins/request_signer.rb +3 -9
  43. data/lib/aws-sdk-core/plugins/s3_region_detection.rb +157 -0
  44. data/lib/aws-sdk-core/plugins/stub_responses.rb +13 -1
  45. data/lib/aws-sdk-core/rds.rb +4 -4
  46. data/lib/aws-sdk-core/redshift.rb +4 -4
  47. data/lib/aws-sdk-core/refreshing_credentials.rb +73 -0
  48. data/lib/aws-sdk-core/route53.rb +3 -3
  49. data/lib/aws-sdk-core/route53domains.rb +2 -2
  50. data/lib/aws-sdk-core/s3.rb +24 -5
  51. data/lib/aws-sdk-core/s3/bucket_region_cache.rb +75 -0
  52. data/lib/aws-sdk-core/ses.rb +4 -4
  53. data/lib/aws-sdk-core/signers/v4.rb +1 -1
  54. data/lib/aws-sdk-core/simpledb.rb +3 -3
  55. data/lib/aws-sdk-core/sns.rb +4 -4
  56. data/lib/aws-sdk-core/sqs.rb +4 -4
  57. data/lib/aws-sdk-core/storagegateway.rb +3 -3
  58. data/lib/aws-sdk-core/sts.rb +2 -2
  59. data/lib/aws-sdk-core/support.rb +3 -3
  60. data/lib/aws-sdk-core/swf.rb +3 -3
  61. data/lib/aws-sdk-core/version.rb +1 -1
  62. metadata +6 -2
@@ -0,0 +1,157 @@
1
+ module Aws
2
+ module Plugins
3
+ # This plugin is an implementation detail and may be modified.
4
+ # @api private
5
+ class S3RegionDetection < Seahorse::Client::Plugin
6
+
7
+ # Intentionally not documented - this should go away when all
8
+ # services support signature version 4 in every region.
9
+ option(:signature_version) do |cfg|
10
+ if S3.sigv2_region?(cfg.region)
11
+ 's3'
12
+ else
13
+ 'v4'
14
+ end
15
+ end
16
+
17
+ class Handler < Seahorse::Client::Handler
18
+
19
+ private
20
+
21
+ def new_hostname(context, region)
22
+ bucket = context.params[:bucket]
23
+ if region == 'us-east-1'
24
+ "#{bucket}.s3-external-1.amazonaws.com"
25
+ else
26
+ "#{bucket}.s3.#{region}.amazonaws.com"
27
+ end
28
+ end
29
+
30
+ end
31
+
32
+ class CachedBucketRegionHandler < Handler
33
+
34
+ def call(context)
35
+ if bucket = context.params[:bucket]
36
+ use_regional_endpoint_when_known(context, bucket)
37
+ end
38
+ @handler.call(context)
39
+ end
40
+
41
+ private
42
+
43
+ def use_regional_endpoint_when_known(context, bucket)
44
+ cached_region = S3::BUCKET_REGIONS[bucket]
45
+ if cached_region && cached_region != context.config.region
46
+ context.http_request.endpoint.host = new_hostname(context, cached_region)
47
+ context[:sigv4_region] = cached_region
48
+ context[:signature_version] =
49
+ S3.sigv2_region?(cached_region) ? 's3' : 'v4'
50
+ end
51
+ end
52
+
53
+ end
54
+
55
+ class DetectRegionHandler < Handler
56
+
57
+ def call(context)
58
+ response = @handler.call(context)
59
+ handle_region_errors(response)
60
+ end
61
+
62
+ private
63
+
64
+ def handle_region_errors(response)
65
+ if requires_sigv4?(response)
66
+ detect_region_and_retry(response)
67
+ elsif wrong_sigv4_region?(response)
68
+ extract_body_region_and_retry(response.context)
69
+ elsif moved_permanently?(response) and dns_style?(response.context)
70
+ # region detection not supported for 301 moved permanently
71
+ # when using path style
72
+ detect_region_and_retry(response) else
73
+ response
74
+ end
75
+ end
76
+
77
+ def requires_sigv4?(resp)
78
+ resp.context.http_response.status_code == 400 &&
79
+ resp.context.http_response.body_contents.include?('Please use AWS4-HMAC-SHA256') &&
80
+ resp.context.http_response.body.respond_to?(:truncate)
81
+ end
82
+
83
+ def wrong_sigv4_region?(resp)
84
+ resp.context.http_response.status_code == 400 &&
85
+ resp.context.http_response.body_contents.match(/<Region>.+?<\/Region>/)
86
+ end
87
+
88
+ def moved_permanently?(resp)
89
+ resp.context.http_response.status_code == 301
90
+ end
91
+
92
+ def extract_body_region_and_retry(context)
93
+ actual_region = region_from_body(context)
94
+ updgrade_to_v4(context, actual_region)
95
+ log_warning(context, actual_region)
96
+ @handler.call(context)
97
+ end
98
+
99
+ def region_from_body(context)
100
+ context.http_response.body_contents.match(/<Region>(.+?)<\/Region>/)[1]
101
+ end
102
+
103
+ def detect_region_and_retry(resp)
104
+ context = resp.context
105
+ updgrade_to_v4(context, 'us-east-1')
106
+ resp = @handler.call(context)
107
+ actual_region = region_from_location_header(context)
108
+ updgrade_to_v4(context, actual_region)
109
+ log_warning(context, actual_region)
110
+ @handler.call(context)
111
+ end
112
+
113
+ def updgrade_to_v4(context, region)
114
+ bucket = context.params[:bucket]
115
+ context.http_response.body.truncate(0)
116
+ context.http_request.headers.delete('authorization')
117
+ context.http_request.headers.delete('x-amz-security-token')
118
+ context.http_request.endpoint.host = new_hostname(context, region)
119
+ signer = Signers::V4.new(context.config.credentials, 's3', region)
120
+ signer.sign(context.http_request)
121
+ end
122
+
123
+ def dns_style?(context)
124
+ bucket = context.params[:bucket]
125
+ context.http_request.endpoint.host.match(/^#{Regexp.escape(bucket)}\.s3/)
126
+ end
127
+
128
+ def region_from_location_header(context)
129
+ location = context.http_response.headers['location']
130
+ location.match(/s3\.(.+?)\.amazonaws\.com/)[1]
131
+ end
132
+
133
+ def log_warning(context, actual_region)
134
+ S3::BUCKET_REGIONS[context.params[:bucket]] = actual_region
135
+ msg = "S3 client configured for #{context.config.region.inspect} " +
136
+ "but the bucket #{context.params[:bucket].inspect} is in " +
137
+ "#{actual_region.inspect}; Please configure the proper region " +
138
+ "to avoid multiple unecessary redirects and signing attempts"
139
+ if logger = context.config.logger
140
+ logger.warn(msg)
141
+ else
142
+ warn(msg)
143
+ end
144
+
145
+ end
146
+
147
+ end
148
+
149
+ # BEFORE the request is signed
150
+ handle(CachedBucketRegionHandler, step: :sign, priority: 60)
151
+
152
+ # AFTER the request is signed
153
+ handle(DetectRegionHandler, step: :sign, priority: 40)
154
+
155
+ end
156
+ end
157
+ end
@@ -14,13 +14,25 @@ module Aws
14
14
 
15
15
  option(:stub_responses, false)
16
16
 
17
+ option(:region) do |config|
18
+ 'stubbed-region' if config.stub_responses
19
+ end
20
+
21
+ option(:credentials) do |config|
22
+ if config.stub_responses
23
+ Credentials.new('stubbed-akid', 'stubbed-secret')
24
+ end
25
+ end
26
+
17
27
  def add_handlers(handlers, config)
18
28
  handlers.add(Handler, step: :send) if config.stub_responses
19
29
  end
20
30
 
21
31
  def after_initialize(client)
22
32
  # disable retries when stubbing responses
23
- client.config.retry_limit = 0 if client.config.stub_responses
33
+ if client.config.stub_responses
34
+ client.handlers.remove(RetryErrors::Handler)
35
+ end
24
36
  end
25
37
 
26
38
  class Handler < Seahorse::Client::Handler
@@ -1,6 +1,6 @@
1
1
  Aws.add_service(:RDS, {
2
- api: File.join(Aws::APIS_DIR, 'RDS.api.json'),
3
- docs: File.join(Aws::APIS_DIR, 'RDS.docs.json'),
4
- paginators: File.join(Aws::APIS_DIR, 'RDS.paginators.json'),
5
- waiters: File.join(Aws::APIS_DIR, 'RDS.waiters.json'),
2
+ api: File.join(Aws::API_DIR, 'RDS.api.json'),
3
+ docs: File.join(Aws::API_DIR, 'RDS.docs.json'),
4
+ paginators: File.join(Aws::API_DIR, 'RDS.paginators.json'),
5
+ waiters: File.join(Aws::API_DIR, 'RDS.waiters.json'),
6
6
  })
@@ -1,6 +1,6 @@
1
1
  Aws.add_service(:Redshift, {
2
- api: File.join(Aws::APIS_DIR, 'Redshift.api.json'),
3
- docs: File.join(Aws::APIS_DIR, 'Redshift.docs.json'),
4
- paginators: File.join(Aws::APIS_DIR, 'Redshift.paginators.json'),
5
- waiters: File.join(Aws::APIS_DIR, 'Redshift.waiters.json'),
2
+ api: File.join(Aws::API_DIR, 'Redshift.api.json'),
3
+ docs: File.join(Aws::API_DIR, 'Redshift.docs.json'),
4
+ paginators: File.join(Aws::API_DIR, 'Redshift.paginators.json'),
5
+ waiters: File.join(Aws::API_DIR, 'Redshift.waiters.json'),
6
6
  })
@@ -0,0 +1,73 @@
1
+ require 'thread'
2
+
3
+ module Aws
4
+
5
+ # Base class used credential classes that can be refreshed. This
6
+ # provides basic refresh logic in a thread-safe manor. Classes mixing in
7
+ # this module are expected to implement a #refresh method that populates
8
+ # the following instance variables:
9
+ #
10
+ # * `@access_key_id`
11
+ # * `@secret_access_key`
12
+ # * `@session_token`
13
+ # * `@expiration`
14
+ #
15
+ # @api private
16
+ module RefreshingCredentials
17
+
18
+ def initialize(options = {})
19
+ @mutex = Mutex.new
20
+ refresh
21
+ end
22
+
23
+ # @return [String,nil]
24
+ def access_key_id
25
+ refresh_if_near_expiration
26
+ @access_key_id
27
+ end
28
+
29
+ # @return [String,nil]
30
+ def secret_access_key
31
+ refresh_if_near_expiration
32
+ @secret_access_key
33
+ end
34
+
35
+ # @return [String,nil]
36
+ def session_token
37
+ refresh_if_near_expiration
38
+ @session_token
39
+ end
40
+
41
+ # @return [Time,nil]
42
+ def expiration
43
+ refresh_if_near_expiration
44
+ @expiration
45
+ end
46
+
47
+ # Refresh credentials.
48
+ # @return [void]
49
+ def refresh!
50
+ @mutex.synchronize { refresh }
51
+ end
52
+
53
+ private
54
+
55
+ # Refreshes instance metadata credentials if they are within
56
+ # 5 minutes of expiration.
57
+ def refresh_if_near_expiration
58
+ if near_expiration?
59
+ @mutex.synchronize do
60
+ refresh if near_expiration?
61
+ end
62
+ end
63
+ end
64
+
65
+ def near_expiration?
66
+ if @expiration
67
+ # are we within 5 minutes of expiration?
68
+ (Time.now.to_i + 5 * 60) > @expiration.to_i
69
+ end
70
+ end
71
+
72
+ end
73
+ end
@@ -1,5 +1,5 @@
1
1
  Aws.add_service(:Route53, {
2
- api: File.join(Aws::APIS_DIR, 'Route53.api.json'),
3
- docs: File.join(Aws::APIS_DIR, 'Route53.docs.json'),
4
- paginators: File.join(Aws::APIS_DIR, 'Route53.paginators.json'),
2
+ api: File.join(Aws::API_DIR, 'Route53.api.json'),
3
+ docs: File.join(Aws::API_DIR, 'Route53.docs.json'),
4
+ paginators: File.join(Aws::API_DIR, 'Route53.paginators.json'),
5
5
  })
@@ -1,4 +1,4 @@
1
1
  Aws.add_service(:Route53Domains, {
2
- api: File.join(Aws::APIS_DIR, 'Route53Domains.api.json'),
3
- docs: File.join(Aws::APIS_DIR, 'Route53Domains.docs.json'),
2
+ api: File.join(Aws::API_DIR, 'Route53Domains.api.json'),
3
+ docs: File.join(Aws::API_DIR, 'Route53Domains.docs.json'),
4
4
  })
@@ -1,13 +1,32 @@
1
1
  Aws.add_service(:S3, {
2
- api: File.join(Aws::APIS_DIR, 'S3.api.json'),
3
- docs: File.join(Aws::APIS_DIR, 'S3.docs.json'),
4
- paginators: File.join(Aws::APIS_DIR, 'S3.paginators.json'),
5
- resources: File.join(Aws::APIS_DIR, 'S3.resources.json'),
6
- waiters: File.join(Aws::APIS_DIR, 'S3.waiters.json'),
2
+ api: File.join(Aws::API_DIR, 'S3.api.json'),
3
+ docs: File.join(Aws::API_DIR, 'S3.docs.json'),
4
+ paginators: File.join(Aws::API_DIR, 'S3.paginators.json'),
5
+ resources: File.join(Aws::API_DIR, 'S3.resources.json'),
6
+ waiters: File.join(Aws::API_DIR, 'S3.waiters.json'),
7
7
  })
8
8
 
9
9
  module Aws
10
10
  module S3
11
+
11
12
  autoload :Presigner, 'aws-sdk-core/s3/presigner'
13
+ autoload :BucketRegionCache, 'aws-sdk-core/s3/bucket_region_cache'
14
+
15
+ # A cache of discovered bucket regions. You can call `#bucket_added`
16
+ # on this to be notified when you must configure the proper region
17
+ # to access a bucket.
18
+ #
19
+ # This cache is considered an implementation detail.
20
+ #
21
+ # @api private
22
+ BUCKET_REGIONS = BucketRegionCache.new
23
+
24
+ # @param [String] region
25
+ # @return [Boolean]
26
+ # @api private
27
+ def self.sigv2_region?(region)
28
+ Client.api.metadata('sigv2Regions').include?(region)
29
+ end
30
+
12
31
  end
13
32
  end
@@ -0,0 +1,75 @@
1
+ require 'thread'
2
+
3
+ module Aws
4
+ module S3
5
+ class BucketRegionCache
6
+
7
+ def initialize
8
+ @regions = {}
9
+ @listeners = []
10
+ @mutex = Mutex.new
11
+ end
12
+
13
+ # Registers a block as a callback. This listener is called when a
14
+ # new bucket/region pair is added to the cache.
15
+ #
16
+ # S3::BUCKET_REGIONS.bucket_added do |bucket_name, region_name|
17
+ # # ...
18
+ # end
19
+ #
20
+ # This happens when a request is made against the classic endpoint,
21
+ # "s3.amazonaws.com" and an error is returned requiring the request
22
+ # to be resent with Signature Version 4. At this point, multiple
23
+ # requests are made to discover the bucket region so that a v4
24
+ # signature can be generated.
25
+ #
26
+ # An application can register listeners here to avoid these extra
27
+ # requests in the future. By constructing an {S3::Client} with
28
+ # the proper region, a proper signature can be generated and redirects
29
+ # avoided.
30
+ # @return [void]
31
+ def bucket_added(&block)
32
+ if block
33
+ @mutex.synchronize { @listeners << block }
34
+ else
35
+ raise ArgumentError, 'missing required block'
36
+ end
37
+ end
38
+
39
+ # @param [String] bucket_name
40
+ # @return [String,nil] Returns the cached region for the named bucket.
41
+ # Returns `nil` if the bucket is not in the cache.
42
+ # @api private
43
+ def [](bucket_name)
44
+ @mutex.synchronize { @regions[bucket_name] }
45
+ end
46
+
47
+ # Caches a bucket's region. Calling this method will trigger each
48
+ # of the {#bucket_added} listener callbacks.
49
+ # @param [String] bucket_name
50
+ # @param [String] region_name
51
+ # @return [void]
52
+ # @api private
53
+ def []=(bucket_name, region_name)
54
+ @mutex.synchronize do
55
+ @regions[bucket_name] = region_name
56
+ @listeners.each { |block| block.call(bucket_name, region_name) }
57
+ end
58
+ end
59
+
60
+ # @api private
61
+ def clear
62
+ @mutex.synchronize { @regions = {} }
63
+ end
64
+
65
+ # @return [Hash] Returns a hash of cached bucket names and region names.
66
+ def to_hash
67
+ @mutex.synchronize do
68
+ @regions.dup
69
+ end
70
+ end
71
+ alias to_h to_hash
72
+
73
+ end
74
+ end
75
+ end
@@ -1,6 +1,6 @@
1
1
  Aws.add_service(:SES, {
2
- api: File.join(Aws::APIS_DIR, 'SES.api.json'),
3
- docs: File.join(Aws::APIS_DIR, 'SES.docs.json'),
4
- paginators: File.join(Aws::APIS_DIR, 'SES.paginators.json'),
5
- waiters: File.join(Aws::APIS_DIR, 'SES.waiters.json'),
2
+ api: File.join(Aws::API_DIR, 'SES.api.json'),
3
+ docs: File.join(Aws::API_DIR, 'SES.docs.json'),
4
+ paginators: File.join(Aws::API_DIR, 'SES.paginators.json'),
5
+ waiters: File.join(Aws::API_DIR, 'SES.waiters.json'),
6
6
  })
@@ -9,7 +9,7 @@ module Aws
9
9
  new(
10
10
  context.config.credentials,
11
11
  context.config.sigv4_name,
12
- context.config.sigv4_region
12
+ context[:sigv4_region] || context.config.sigv4_region
13
13
  ).sign(context.http_request)
14
14
  end
15
15
 
@@ -1,5 +1,5 @@
1
1
  Aws.add_service(:SimpleDB, {
2
- api: File.join(Aws::APIS_DIR, 'SimpleDB.api.json'),
3
- docs: File.join(Aws::APIS_DIR, 'SimpleDB.docs.json'),
4
- paginators: File.join(Aws::APIS_DIR, 'SimpleDB.paginators.json'),
2
+ api: File.join(Aws::API_DIR, 'SimpleDB.api.json'),
3
+ docs: File.join(Aws::API_DIR, 'SimpleDB.docs.json'),
4
+ paginators: File.join(Aws::API_DIR, 'SimpleDB.paginators.json'),
5
5
  })
@@ -1,6 +1,6 @@
1
1
  Aws.add_service(:SNS, {
2
- api: File.join(Aws::APIS_DIR, 'SNS.api.json'),
3
- docs: File.join(Aws::APIS_DIR, 'SNS.docs.json'),
4
- paginators: File.join(Aws::APIS_DIR, 'SNS.paginators.json'),
5
- resources: File.join(Aws::APIS_DIR, 'SNS.resources.json'),
2
+ api: File.join(Aws::API_DIR, 'SNS.api.json'),
3
+ docs: File.join(Aws::API_DIR, 'SNS.docs.json'),
4
+ paginators: File.join(Aws::API_DIR, 'SNS.paginators.json'),
5
+ resources: File.join(Aws::API_DIR, 'SNS.resources.json'),
6
6
  })