aws-sdk-core 2.0.3 → 2.0.4

Sign up to get free protection for your applications and to get access to all the features.
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
  })