aws-partitions 1.479.0 → 1.946.0

Sign up to get free protection for your applications and to get access to all the features.
data/VERSION CHANGED
@@ -1 +1 @@
1
- 1.479.0
1
+ 1.946.0
@@ -37,31 +37,108 @@ module Aws
37
37
  # "monitoring" for cloudwatch.
38
38
  # @param [String] sts_regional_endpoints [STS only] Whether to use
39
39
  # `legacy` (global endpoint for legacy regions) or `regional` mode for
40
- # using regional endpoint for supported regions except 'aws-global'
40
+ # using regional endpoint for supported regions except 'aws-global'
41
+ # @param [Hash] variants Endpoint variants such as 'fips' or 'dualstack'
42
+ # @option variants [Boolean] :dualstack When true, resolve a dualstack
43
+ # endpoint.
44
+ # @option variants [Boolean] :fips When true, resolve a FIPS endpoint.
41
45
  # @api private Use the static class methods instead.
42
- def resolve(region, service, sts_regional_endpoints)
43
- 'https://' + endpoint_for(region, service, sts_regional_endpoints)
46
+ def resolve(region, service, sts_regional_endpoints, variants)
47
+ 'https://' + endpoint_for(region, service, build_is_global_fn(sts_regional_endpoints), variants)
44
48
  end
45
49
 
46
50
  # @api private Use the static class methods instead.
47
- def signing_region(region, service)
48
- get_partition(region)
49
- .fetch('services', {})
50
- .fetch(service, {})
51
- .fetch('endpoints', {})
52
- .fetch(region, {})
53
- .fetch('credentialScope', {})
51
+ def signing_region(region, service, sts_regional_endpoints)
52
+ credential_scope(region, service, build_is_global_fn(sts_regional_endpoints))
54
53
  .fetch('region', region)
55
54
  end
56
55
 
57
56
  # @api private Use the static class methods instead.
58
- def dns_suffix_for(region)
59
- get_partition(region)['dnsSuffix']
57
+ def signing_service(region, service)
58
+ # don't default to the service name
59
+ # signers should prefer the api metadata's signingName
60
+ # if no service is set in the credentialScope
61
+ credential_scope(region, service, build_is_global_fn('regional'))
62
+ .fetch('service', nil)
63
+ end
64
+
65
+ # @param [String] region The region used to fetch the partition.
66
+ # @param [String] service Used only if dualstack is true. Used to find a
67
+ # DNS suffix for a specific service.
68
+ # @param [Hash] variants Endpoint variants such as 'fips' or 'dualstack'
69
+ # @option variants [Boolean] :dualstack When true, resolve a dualstack
70
+ # endpoint.
71
+ # @option variants [Boolean] :fips When true, resolve a FIPS endpoint.
72
+ # @api private Use the static class methods instead.
73
+ def dns_suffix_for(region, service, variants)
74
+ if configured_variants?(variants)
75
+ resolve_variant(region, service, variants)['dnsSuffix']
76
+ else
77
+ get_partition(region)['dnsSuffix']
78
+ end
60
79
  end
61
80
 
62
81
  private
63
82
 
64
- def endpoint_for(region, service, sts_regional_endpoints)
83
+ def configured_variants?(variants)
84
+ variants.values.any?
85
+ end
86
+
87
+ def fetch_variant(cfg, tags)
88
+ variants = cfg.fetch('variants', [])
89
+ variants.find { |v| tags == Set.new(v['tags']) } || {}
90
+ end
91
+
92
+ def resolve_variant(region, service, config_variants)
93
+ tags = Set.new(config_variants.select { |_k,v| v == true }.map { |k,_v| k.to_s })
94
+ is_global_fn = build_is_global_fn # ignore legacy STS config for variants
95
+
96
+ partition_cfg = get_partition(region)
97
+ service_cfg = partition_cfg.fetch('services', {})
98
+ .fetch(service, {})
99
+
100
+ endpoints_cfg = service_cfg.fetch('endpoints', {})
101
+
102
+ if is_global_fn.call(service, region, endpoints_cfg, service_cfg)
103
+ region = service_cfg.fetch('partitionEndpoint', region)
104
+ end
105
+
106
+ region_cfg = endpoints_cfg.fetch(region, {})
107
+ warn_deprecation(service, region) if region_cfg['deprecated']
108
+
109
+ partition_defaults = fetch_variant(partition_cfg.fetch('defaults', {}), tags)
110
+ service_defaults = fetch_variant(service_cfg.fetch('defaults', {}), tags)
111
+ endpoint_cfg = fetch_variant(region_cfg, tags)
112
+
113
+ # merge upwards, preferring values from endpoint > service > partition
114
+ partition_defaults.merge(service_defaults.merge(endpoint_cfg))
115
+ end
116
+
117
+ def validate_variant!(config_variants, resolved_variant)
118
+ unless resolved_variant['hostname'] && resolved_variant['dnsSuffix']
119
+ enabled_variants = config_variants.select { |_k, v| v}.map { |k, _v| k.to_s }.join(', ')
120
+ raise ArgumentError,
121
+ "#{enabled_variants} not supported for this region and partition."
122
+ end
123
+ end
124
+
125
+ def endpoint_for(region, service, is_global_fn, variants)
126
+ if configured_variants?(variants)
127
+ endpoint_with_variants_for(region, service, variants)
128
+ else
129
+ endpoint_no_variants_for(region, service, is_global_fn)
130
+ end
131
+ end
132
+
133
+ def endpoint_with_variants_for(region, service, variants)
134
+ variant = resolve_variant(region, service, variants)
135
+ validate_variant!(variants, variant)
136
+ variant['hostname'].sub('{region}', region)
137
+ .sub('{service}', service)
138
+ .sub('{dnsSuffix}', variant['dnsSuffix'])
139
+ end
140
+
141
+ def endpoint_no_variants_for(region, service, is_global_fn)
65
142
  partition = get_partition(region)
66
143
  service_cfg = partition.fetch('services', {}).fetch(service, {})
67
144
 
@@ -72,33 +149,70 @@ module Aws
72
149
 
73
150
  endpoints = service_cfg.fetch('endpoints', {})
74
151
 
75
- # Check for sts legacy behavior
76
- sts_legacy = service == 'sts' &&
77
- sts_regional_endpoints == 'legacy' &&
78
- STS_LEGACY_REGIONS.include?(region)
79
-
80
- is_global = !endpoints.key?(region) &&
81
- service_cfg['isRegionalized'] == false
82
-
83
152
  # Check for global endpoint.
84
- if sts_legacy || is_global
153
+ if is_global_fn.call(service, region, endpoints, service_cfg)
85
154
  region = service_cfg.fetch('partitionEndpoint', region)
86
155
  end
87
156
 
88
157
  # Check for service/region level endpoint.
89
- endpoint = endpoints
158
+ region_cfg = endpoints
90
159
  .fetch(region, {})
160
+ endpoint = region_cfg
91
161
  .fetch('hostname', default_endpoint)
92
162
 
163
+ warn_deprecation(service, region) if region_cfg['deprecated']
164
+
93
165
  # Replace placeholders from the endpoints
94
166
  endpoint.sub('{region}', region)
95
167
  .sub('{service}', service)
96
168
  .sub('{dnsSuffix}', partition['dnsSuffix'])
97
169
  end
98
170
 
99
- def get_partition(region)
100
- partition_containing_region(region) ||
101
- partition_matching_region(region) ||
171
+ def warn_deprecation(service, region)
172
+ warn("The endpoint for service: #{service}, region: #{region}"\
173
+ ' is deprecated.')
174
+ end
175
+
176
+ # returns a callable that takes a region
177
+ # and returns true if the service is global
178
+ def build_is_global_fn(sts_regional_endpoints='regional')
179
+ lambda do |service, region, endpoints, service_cfg|
180
+ # Check for sts legacy behavior
181
+ sts_legacy = service == 'sts' &&
182
+ sts_regional_endpoints == 'legacy' &&
183
+ STS_LEGACY_REGIONS.include?(region)
184
+
185
+ is_global = !endpoints.key?(region) &&
186
+ service_cfg['isRegionalized'] == false
187
+
188
+ sts_legacy || is_global
189
+ end
190
+ end
191
+
192
+ def credential_scope(region, service, is_global_fn)
193
+ partition = get_partition(region)
194
+ service_cfg = partition.fetch('services', {})
195
+ .fetch(service, {})
196
+ endpoints = service_cfg.fetch('endpoints', {})
197
+
198
+ # Check for global endpoint.
199
+ if is_global_fn.call(service, region, endpoints, service_cfg)
200
+ region = service_cfg.fetch('partitionEndpoint', region)
201
+ end
202
+
203
+ default_credential_scope = service_cfg
204
+ .fetch('defaults', {})
205
+ .fetch('credentialScope', {})
206
+
207
+ endpoints
208
+ .fetch(region, {})
209
+ .fetch('credentialScope', default_credential_scope)
210
+ end
211
+
212
+ def get_partition(region_or_partition)
213
+ partition_containing_region(region_or_partition) ||
214
+ partition_matching_region(region_or_partition) ||
215
+ partition_matching_name(region_or_partition) ||
102
216
  default_partition
103
217
  end
104
218
 
@@ -110,29 +224,37 @@ module Aws
110
224
 
111
225
  def partition_matching_region(region)
112
226
  @rules['partitions'].find do |p|
113
- region.match(p['regionRegex']) ||
227
+ p['regionRegex'] && region.match(p['regionRegex']) ||
114
228
  p['services'].values.find do |svc|
115
229
  svc['endpoints'].key?(region) if svc.key?('endpoints')
116
230
  end
117
231
  end
118
232
  end
119
233
 
234
+ def partition_matching_name(partition_name)
235
+ @rules['partitions'].find { |p| p['partition'] == partition_name }
236
+ end
237
+
120
238
  def default_partition
121
239
  @rules['partitions'].find { |p| p['partition'] == 'aws' } ||
122
240
  @rules['partitions'].first
123
241
  end
124
242
 
125
243
  class << self
126
- def resolve(region, service, sts_regional_endpoints = 'regional')
127
- default_provider.resolve(region, service, sts_regional_endpoints)
244
+ def resolve(region, service, sts_endpoint = 'regional', variants = {})
245
+ default_provider.resolve(region, service, sts_endpoint, variants)
246
+ end
247
+
248
+ def signing_region(region, service, sts_regional_endpoints = 'regional')
249
+ default_provider.signing_region(region, service, sts_regional_endpoints)
128
250
  end
129
251
 
130
- def signing_region(region, service)
131
- default_provider.signing_region(region, service)
252
+ def signing_service(region, service)
253
+ default_provider.signing_service(region, service)
132
254
  end
133
255
 
134
- def dns_suffix_for(region)
135
- default_provider.dns_suffix_for(region)
256
+ def dns_suffix_for(region, service = nil, variants = {})
257
+ default_provider.dns_suffix_for(region, service, variants)
136
258
  end
137
259
 
138
260
  private
@@ -10,12 +10,20 @@ module Aws
10
10
  def initialize(options = {})
11
11
  @name = options[:name]
12
12
  @regions = options[:regions]
13
+ @region_regex = options[:region_regex]
13
14
  @services = options[:services]
15
+ @metadata = options[:metadata]
14
16
  end
15
17
 
16
18
  # @return [String] The partition name, e.g. "aws", "aws-cn", "aws-us-gov".
17
19
  attr_reader :name
18
20
 
21
+ # @return [String] The regex representing the region format.
22
+ attr_reader :region_regex
23
+
24
+ # @return [Metadata] The metadata for the partition.
25
+ attr_reader :metadata
26
+
19
27
  # @param [String] region_name The name of the region, e.g. "us-east-1".
20
28
  # @return [Region]
21
29
  # @raise [ArgumentError] Raises `ArgumentError` for unknown region name.
@@ -70,6 +78,7 @@ module Aws
70
78
  Partition.new(
71
79
  name: partition['partition'],
72
80
  regions: build_regions(partition),
81
+ region_regex: partition['regionRegex'],
73
82
  services: build_services(partition)
74
83
  )
75
84
  end
@@ -79,8 +88,7 @@ module Aws
79
88
  # @param [Hash] partition
80
89
  # @return [Hash<String,Region>]
81
90
  def build_regions(partition)
82
- partition['regions'].each_with_object({}) do
83
- |(region_name, region), regions|
91
+ partition['regions'].each_with_object({}) do |(region_name, region), regions|
84
92
  next if region_name == "#{partition['partition']}-global"
85
93
 
86
94
  regions[region_name] = Region.build(
@@ -42,12 +42,62 @@ module Aws
42
42
  end
43
43
  end
44
44
 
45
+ # @param [Partition] partitions_metadata
46
+ # @api private
47
+ def merge_metadata(partitions_metadata)
48
+ partitions_metadata['partitions'].each do |partition_metadata|
49
+ outputs = partition_metadata['outputs']
50
+
51
+ if existing = @partitions[partition_metadata['id']]
52
+ @partitions[partition_metadata['id']] = Partition.new(
53
+ name: existing.name,
54
+ regions: build_metadata_regions(
55
+ partition_metadata['id'],
56
+ partition_metadata['regions'],
57
+ existing),
58
+ region_regex: partition_metadata['regionRegex'],
59
+ services: existing.services.each_with_object({}) do |s, services|
60
+ services[s.name] = s
61
+ end,
62
+ metadata: outputs
63
+ )
64
+ else
65
+ @partitions[partition_metadata['id']] = Partition.new(
66
+ name: partition_metadata['id'],
67
+ regions: build_metadata_regions(
68
+ partition_metadata['id'], partition_metadata['regions']
69
+ ),
70
+ region_regex: partition_metadata['regionRegex'],
71
+ services: {},
72
+ metadata: outputs
73
+ )
74
+ end
75
+ end
76
+ end
77
+
45
78
  # Removed all partitions.
46
79
  # @api private
47
80
  def clear
48
81
  @partitions = {}
49
82
  end
50
83
 
84
+ private
85
+
86
+ def build_metadata_regions(partition_name, metadata_regions, existing = nil)
87
+ metadata_regions.each_with_object({}) do |(region_name, region), regions|
88
+ if existing && existing.region?(region_name)
89
+ regions[region_name] = existing.region(region_name)
90
+ else
91
+ regions[region_name] = Region.new(
92
+ name: region_name,
93
+ description: region['description'],
94
+ partition_name: partition_name,
95
+ services: Set.new
96
+ )
97
+ end
98
+ end
99
+ end
100
+
51
101
  class << self
52
102
 
53
103
  # @api private
@@ -16,6 +16,8 @@ module Aws
16
16
  @name = options[:name]
17
17
  @partition_name = options[:partition_name]
18
18
  @regions = options[:regions]
19
+ @fips_regions = options[:fips_regions]
20
+ @dualstack_regions = options[:dualstack_regions]
19
21
  @regionalized = options[:regionalized]
20
22
  @partition_region = options[:partition_region]
21
23
  end
@@ -31,6 +33,14 @@ module Aws
31
33
  # Regions are scoped to the partition.
32
34
  attr_reader :regions
33
35
 
36
+ # @return [Set<String>] The FIPS compatible regions this service is
37
+ # available in. Regions are scoped to the partition.
38
+ attr_reader :fips_regions
39
+
40
+ # @return [Set<String>] The Dualstack compatible regions this service is
41
+ # available in. Regions are scoped to the partition.
42
+ attr_reader :dualstack_regions
43
+
34
44
  # @return [String,nil] The global patition endpoint for this service.
35
45
  # May be `nil`.
36
46
  attr_reader :partition_region
@@ -54,6 +64,8 @@ module Aws
54
64
  name: service_name,
55
65
  partition_name: partition['partition'],
56
66
  regions: regions(service, partition),
67
+ fips_regions: variant_regions('fips', service, partition),
68
+ dualstack_regions: variant_regions('dualstack', service, partition),
57
69
  regionalized: service['isRegionalized'] != false,
58
70
  partition_region: partition_region(service)
59
71
  )
@@ -67,6 +79,21 @@ module Aws
67
79
  names - ["#{partition['partition']}-global"]
68
80
  end
69
81
 
82
+ def variant_regions(variant_name, service, partition)
83
+ svc_endpoints = service.fetch('endpoints', {})
84
+ names = Set.new
85
+ svc_endpoints.each do |key, value|
86
+ variants = value.fetch('variants', [])
87
+ variants.each do |variant|
88
+ tags = variant.fetch('tags', [])
89
+ if tags.include?(variant_name) && partition['regions'].key?(key)
90
+ names << key
91
+ end
92
+ end
93
+ end
94
+ names - ["#{partition['partition']}-global"]
95
+ end
96
+
70
97
  def partition_region(service)
71
98
  service['partitionEndpoint']
72
99
  end