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.
- checksums.yaml +4 -4
- data/apis/CognitoIdentity.api.json +16 -4
- data/apis/EC2.paginators.json +3 -0
- data/apis/ElastiCache.api.json +164 -15
- data/apis/IAM.api.json +326 -1
- data/apis/IAM.resources.json +2 -29
- data/apis/Route53Domains.api.json +56 -0
- data/apis/S3.api.json +1 -1
- data/endpoints.json +62 -76
- data/lib/aws-sdk-core.rb +4 -14
- data/lib/aws-sdk-core/api/documenter.rb +1 -1
- data/lib/aws-sdk-core/api/service_customizations.rb +1 -0
- data/lib/aws-sdk-core/assume_role_credentials.rb +46 -0
- data/lib/aws-sdk-core/autoscaling.rb +3 -3
- data/lib/aws-sdk-core/cloudformation.rb +3 -3
- data/lib/aws-sdk-core/cloudfront.rb +4 -4
- data/lib/aws-sdk-core/cloudsearch.rb +3 -3
- data/lib/aws-sdk-core/cloudsearchdomain.rb +2 -2
- data/lib/aws-sdk-core/cloudtrail.rb +3 -3
- data/lib/aws-sdk-core/cloudwatch.rb +3 -3
- data/lib/aws-sdk-core/cloudwatchlogs.rb +3 -3
- data/lib/aws-sdk-core/cognitoidentity.rb +2 -2
- data/lib/aws-sdk-core/cognitosync.rb +2 -2
- data/lib/aws-sdk-core/datapipeline.rb +3 -3
- data/lib/aws-sdk-core/directconnect.rb +3 -3
- data/lib/aws-sdk-core/dynamodb.rb +4 -4
- data/lib/aws-sdk-core/ec2.rb +5 -5
- data/lib/aws-sdk-core/elasticache.rb +3 -3
- data/lib/aws-sdk-core/elasticbeanstalk.rb +3 -3
- data/lib/aws-sdk-core/elasticloadbalancing.rb +3 -3
- data/lib/aws-sdk-core/elastictranscoder.rb +4 -4
- data/lib/aws-sdk-core/emr.rb +3 -3
- data/lib/aws-sdk-core/endpoint_provider.rb +16 -78
- data/lib/aws-sdk-core/glacier.rb +5 -5
- data/lib/aws-sdk-core/iam.rb +4 -4
- data/lib/aws-sdk-core/importexport.rb +3 -3
- data/lib/aws-sdk-core/instance_profile_credentials.rb +13 -45
- data/lib/aws-sdk-core/kinesis.rb +3 -3
- data/lib/aws-sdk-core/opsworks.rb +4 -4
- data/lib/aws-sdk-core/pageable_response.rb +18 -0
- data/lib/aws-sdk-core/plugins/regional_endpoint.rb +1 -5
- data/lib/aws-sdk-core/plugins/request_signer.rb +3 -9
- data/lib/aws-sdk-core/plugins/s3_region_detection.rb +157 -0
- data/lib/aws-sdk-core/plugins/stub_responses.rb +13 -1
- data/lib/aws-sdk-core/rds.rb +4 -4
- data/lib/aws-sdk-core/redshift.rb +4 -4
- data/lib/aws-sdk-core/refreshing_credentials.rb +73 -0
- data/lib/aws-sdk-core/route53.rb +3 -3
- data/lib/aws-sdk-core/route53domains.rb +2 -2
- data/lib/aws-sdk-core/s3.rb +24 -5
- data/lib/aws-sdk-core/s3/bucket_region_cache.rb +75 -0
- data/lib/aws-sdk-core/ses.rb +4 -4
- data/lib/aws-sdk-core/signers/v4.rb +1 -1
- data/lib/aws-sdk-core/simpledb.rb +3 -3
- data/lib/aws-sdk-core/sns.rb +4 -4
- data/lib/aws-sdk-core/sqs.rb +4 -4
- data/lib/aws-sdk-core/storagegateway.rb +3 -3
- data/lib/aws-sdk-core/sts.rb +2 -2
- data/lib/aws-sdk-core/support.rb +3 -3
- data/lib/aws-sdk-core/swf.rb +3 -3
- data/lib/aws-sdk-core/version.rb +1 -1
- 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
|
-
|
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
|
data/lib/aws-sdk-core/rds.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
Aws.add_service(:RDS, {
|
2
|
-
api: File.join(Aws::
|
3
|
-
docs: File.join(Aws::
|
4
|
-
paginators: File.join(Aws::
|
5
|
-
waiters: File.join(Aws::
|
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::
|
3
|
-
docs: File.join(Aws::
|
4
|
-
paginators: File.join(Aws::
|
5
|
-
waiters: File.join(Aws::
|
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
|
data/lib/aws-sdk-core/route53.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
Aws.add_service(:Route53, {
|
2
|
-
api: File.join(Aws::
|
3
|
-
docs: File.join(Aws::
|
4
|
-
paginators: File.join(Aws::
|
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::
|
3
|
-
docs: File.join(Aws::
|
2
|
+
api: File.join(Aws::API_DIR, 'Route53Domains.api.json'),
|
3
|
+
docs: File.join(Aws::API_DIR, 'Route53Domains.docs.json'),
|
4
4
|
})
|
data/lib/aws-sdk-core/s3.rb
CHANGED
@@ -1,13 +1,32 @@
|
|
1
1
|
Aws.add_service(:S3, {
|
2
|
-
api: File.join(Aws::
|
3
|
-
docs: File.join(Aws::
|
4
|
-
paginators: File.join(Aws::
|
5
|
-
resources: File.join(Aws::
|
6
|
-
waiters: File.join(Aws::
|
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
|
data/lib/aws-sdk-core/ses.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
Aws.add_service(:SES, {
|
2
|
-
api: File.join(Aws::
|
3
|
-
docs: File.join(Aws::
|
4
|
-
paginators: File.join(Aws::
|
5
|
-
waiters: File.join(Aws::
|
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
|
})
|
@@ -1,5 +1,5 @@
|
|
1
1
|
Aws.add_service(:SimpleDB, {
|
2
|
-
api: File.join(Aws::
|
3
|
-
docs: File.join(Aws::
|
4
|
-
paginators: File.join(Aws::
|
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
|
})
|
data/lib/aws-sdk-core/sns.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
Aws.add_service(:SNS, {
|
2
|
-
api: File.join(Aws::
|
3
|
-
docs: File.join(Aws::
|
4
|
-
paginators: File.join(Aws::
|
5
|
-
resources: File.join(Aws::
|
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
|
})
|