Skip to content

Commit 5d4ecca

Browse files
Merge branch 'release-1.23.0'
* release-1.23.0: Bumping version to 1.23.0 Update to latest models Adding changelog entry for modeled endpoints changes Fixing config and exception descriptions (#2559) Adding modeled endpoint variants support Update create_urllib3_context to latest version (#2557) Update six from 1.10.0 to 1.16.0 (#2551)
2 parents 0266c34 + 400e187 commit 5d4ecca

File tree

25 files changed

+2241
-356
lines changed

25 files changed

+2241
-356
lines changed

.changes/1.23.0.json

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
[
2+
{
3+
"category": "EndpointResolver",
4+
"description": "Adding support for resolving modeled FIPS and Dualstack endpoints.",
5+
"type": "feature"
6+
},
7+
{
8+
"category": "``six``",
9+
"description": "Updated vendored version of ``six`` from 1.10.0 to 1.16.0",
10+
"type": "feature"
11+
},
12+
{
13+
"category": "``sagemaker``",
14+
"description": "SageMaker CreateEndpoint and UpdateEndpoint APIs now support additional deployment configuration to manage traffic shifting options and automatic rollback monitoring. DescribeEndpoint now shows new in-progress deployment details with stage status.",
15+
"type": "api-change"
16+
},
17+
{
18+
"category": "``chime-sdk-meetings``",
19+
"description": "Updated format validation for ids and regions.",
20+
"type": "api-change"
21+
},
22+
{
23+
"category": "``wafv2``",
24+
"description": "You can now configure rules to run a CAPTCHA check against web requests and, as needed, send a CAPTCHA challenge to the client.",
25+
"type": "api-change"
26+
},
27+
{
28+
"category": "``ec2``",
29+
"description": "This release adds internal validation on the GatewayAssociationState field",
30+
"type": "api-change"
31+
}
32+
]

CHANGELOG.rst

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,17 @@
22
CHANGELOG
33
=========
44

5+
1.23.0
6+
======
7+
8+
* feature:EndpointResolver: Adding support for resolving modeled FIPS and Dualstack endpoints.
9+
* feature:``six``: Updated vendored version of ``six`` from 1.10.0 to 1.16.0
10+
* api-change:``sagemaker``: SageMaker CreateEndpoint and UpdateEndpoint APIs now support additional deployment configuration to manage traffic shifting options and automatic rollback monitoring. DescribeEndpoint now shows new in-progress deployment details with stage status.
11+
* api-change:``chime-sdk-meetings``: Updated format validation for ids and regions.
12+
* api-change:``wafv2``: You can now configure rules to run a CAPTCHA check against web requests and, as needed, send a CAPTCHA challenge to the client.
13+
* api-change:``ec2``: This release adds internal validation on the GatewayAssociationState field
14+
15+
516
1.22.12
617
=======
718

botocore/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
import re
1717
import logging
1818

19-
__version__ = '1.22.12'
19+
__version__ = '1.23.0'
2020

2121

2222
class NullHandler(logging.Handler):

botocore/args.py

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -153,13 +153,18 @@ def compute_client_args(self, service_model, client_config,
153153
endpoint_bridge=endpoint_bridge,
154154
s3_config=s3_config,
155155
)
156+
endpoint_variant_tags = endpoint_config['metadata'].get('tags', [])
156157
# Create a new client config to be passed to the client based
157158
# on the final values. We do not want the user to be able
158159
# to try to modify an existing client with a client config.
159160
config_kwargs = dict(
160161
region_name=endpoint_config['region_name'],
161162
signature_version=endpoint_config['signature_version'],
162163
user_agent=user_agent)
164+
if 'dualstack' in endpoint_variant_tags:
165+
config_kwargs.update(use_dualstack_endpoint=True)
166+
if 'fips' in endpoint_variant_tags:
167+
config_kwargs.update(use_fips_endpoint=True)
163168
if client_config is not None:
164169
config_kwargs.update(
165170
connect_timeout=client_config.connect_timeout,
@@ -173,6 +178,14 @@ def compute_client_args(self, service_model, client_config,
173178
)
174179
self._compute_retry_config(config_kwargs)
175180
s3_config = self.compute_s3_config(client_config)
181+
182+
is_s3_service = service_name in ['s3', 's3-control']
183+
184+
if is_s3_service and 'dualstack' in endpoint_variant_tags:
185+
if s3_config is None:
186+
s3_config = {}
187+
s3_config['use_dualstack_endpoint'] = True
188+
176189
return {
177190
'service_name': service_name,
178191
'parameter_validation': parameter_validation,
@@ -267,14 +280,18 @@ def _set_region_if_custom_s3_endpoint(self, endpoint_config,
267280
def _compute_sts_endpoint_config(self, **resolve_endpoint_kwargs):
268281
endpoint_config = self._resolve_endpoint(**resolve_endpoint_kwargs)
269282
if self._should_set_global_sts_endpoint(
270-
resolve_endpoint_kwargs['region_name'],
271-
resolve_endpoint_kwargs['endpoint_url']):
283+
resolve_endpoint_kwargs['region_name'],
284+
resolve_endpoint_kwargs['endpoint_url'],
285+
endpoint_config
286+
):
272287
self._set_global_sts_endpoint(
273288
endpoint_config, resolve_endpoint_kwargs['is_secure'])
274289
return endpoint_config
275290

276-
def _should_set_global_sts_endpoint(self, region_name, endpoint_url):
277-
if endpoint_url:
291+
def _should_set_global_sts_endpoint(self, region_name, endpoint_url,
292+
endpoint_config):
293+
endpoint_variant_tags = endpoint_config['metadata'].get('tags')
294+
if endpoint_url or endpoint_variant_tags:
278295
return False
279296
return (
280297
self._get_sts_regional_endpoints_config() == 'legacy' and

botocore/client.py

Lines changed: 81 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
from botocore.docs.docstring import PaginatorDocstring
2121
from botocore.exceptions import (
2222
DataNotFoundError, OperationNotPageableError, UnknownSignatureVersionError,
23-
InvalidEndpointDiscoveryConfigurationError, UnknownFIPSEndpointError,
23+
InvalidEndpointDiscoveryConfigurationError
2424
)
2525
from botocore.hooks import first_non_none_response
2626
from botocore.model import ServiceModel
@@ -82,9 +82,12 @@ def create_client(self, service_name, region_name, is_secure=True,
8282
service_name = first_non_none_response(responses, default=service_name)
8383
service_model = self._load_service_model(service_name, api_version)
8484
cls = self._create_client_class(service_name, service_model)
85+
region_name, client_config = self._normalize_fips_region(
86+
region_name, client_config)
8587
endpoint_bridge = ClientEndpointBridge(
8688
self._endpoint_resolver, scoped_config, client_config,
87-
service_signing_name=service_model.metadata.get('signingName'))
89+
service_signing_name=service_model.metadata.get('signingName'),
90+
config_store=self._config_store)
8891
client_args = self._get_client_args(
8992
service_model, region_name, is_secure, endpoint_url,
9093
verify, credentials, scoped_config, client_config, endpoint_bridge)
@@ -99,7 +102,6 @@ def create_client(self, service_name, region_name, is_secure=True,
99102
self._register_endpoint_discovery(
100103
service_client, endpoint_url, client_config
101104
)
102-
self._register_lazy_block_unknown_fips_pseudo_regions(service_client)
103105
return service_client
104106

105107
def create_client_class(self, service_name, api_version=None):
@@ -120,6 +122,30 @@ def _create_client_class(self, service_name, service_model):
120122
cls = type(str(class_name), tuple(bases), class_attributes)
121123
return cls
122124

125+
def _normalize_fips_region(self, region_name,
126+
client_config):
127+
if region_name is not None:
128+
normalized_region_name = region_name.replace(
129+
'fips-', '').replace('-fips', '')
130+
# If region has been transformed then set flag
131+
if normalized_region_name != region_name:
132+
config_use_fips_endpoint = Config(use_fips_endpoint=True)
133+
if client_config:
134+
# Keeping endpoint setting client specific
135+
client_config = client_config.merge(
136+
config_use_fips_endpoint)
137+
else:
138+
client_config = config_use_fips_endpoint
139+
logger.warn(
140+
'transforming region from %s to %s and setting '
141+
'use_fips_endpoint to true. client should not '
142+
'be configured with a fips psuedo region.' % (
143+
region_name, normalized_region_name
144+
)
145+
)
146+
region_name = normalized_region_name
147+
return region_name, client_config
148+
123149
def _load_service_model(self, service_name, api_version=None):
124150
json_model = self._loader.load_service_model(service_name, 'service-2',
125151
api_version=api_version)
@@ -235,45 +261,20 @@ def _requires_endpoint_discovery(self, client, enabled):
235261
return client.meta.service_model.endpoint_discovery_required
236262
return enabled
237263

238-
def _register_lazy_block_unknown_fips_pseudo_regions(self, client):
239-
# This function blocks usage of FIPS pseudo-regions when the endpoint
240-
# is not explicitly known to exist to the client to prevent accidental
241-
# usage of incorrect or non-FIPS endpoints. This is done lazily by
242-
# registering an exception on the before-sign event to allow for
243-
# general client usage such as can_paginate, exceptions, etc.
244-
region_name = client.meta.region_name
245-
if not region_name or 'fips' not in region_name.lower():
246-
return
247-
248-
partition = client.meta.partition
249-
endpoint_prefix = client.meta.service_model.endpoint_prefix
250-
known_regions = self._endpoint_resolver.get_available_endpoints(
251-
endpoint_prefix,
252-
partition,
253-
allow_non_regional=True,
254-
)
255-
256-
if region_name not in known_regions:
257-
def _lazy_fips_exception(**kwargs):
258-
service_name = client.meta.service_model.service_name
259-
raise UnknownFIPSEndpointError(
260-
region_name=region_name,
261-
service_name=service_name,
262-
)
263-
client.meta.events.register('before-sign', _lazy_fips_exception)
264-
265264
def _register_s3_events(self, client, endpoint_bridge, endpoint_url,
266265
client_config, scoped_config):
267266
if client.meta.service_model.service_name != 's3':
268267
return
269268
S3RegionRedirector(endpoint_bridge, client).register()
270269
S3ArnParamHandler().register(client.meta.events)
270+
use_fips_endpoint = client.meta.config.use_fips_endpoint
271271
S3EndpointSetter(
272272
endpoint_resolver=self._endpoint_resolver,
273273
region=client.meta.region_name,
274274
s3_config=client.meta.config.s3,
275275
endpoint_url=endpoint_url,
276-
partition=client.meta.partition
276+
partition=client.meta.partition,
277+
use_fips_endpoint=use_fips_endpoint
277278
).register(client.meta.events)
278279
self._set_s3_presign_signature_version(
279280
client.meta, client_config, scoped_config)
@@ -284,13 +285,15 @@ def _register_s3_control_events(
284285
):
285286
if client.meta.service_model.service_name != 's3control':
286287
return
288+
use_fips_endpoint = client.meta.config.use_fips_endpoint
287289
S3ControlArnParamHandler().register(client.meta.events)
288290
S3ControlEndpointSetter(
289291
endpoint_resolver=self._endpoint_resolver,
290292
region=client.meta.region_name,
291293
s3_config=client.meta.config.s3,
292294
endpoint_url=endpoint_url,
293-
partition=client.meta.partition
295+
partition=client.meta.partition,
296+
use_fips_endpoint=use_fips_endpoint
294297
).register(client.meta.events)
295298

296299
def _set_s3_presign_signature_version(self, client_meta,
@@ -415,30 +418,42 @@ class ClientEndpointBridge(object):
415418
utilize "us-east-1" by default if no region can be resolved."""
416419

417420
DEFAULT_ENDPOINT = '{service}.{region}.amazonaws.com'
418-
_DUALSTACK_ENABLED_SERVICES = ['s3', 's3-control']
421+
_DUALSTACK_CUSTOMIZED_SERVICES = ['s3', 's3-control']
419422

420423
def __init__(self, endpoint_resolver, scoped_config=None,
421424
client_config=None, default_endpoint=None,
422-
service_signing_name=None):
425+
service_signing_name=None, config_store=None):
423426
self.service_signing_name = service_signing_name
424427
self.endpoint_resolver = endpoint_resolver
425428
self.scoped_config = scoped_config
426429
self.client_config = client_config
427430
self.default_endpoint = default_endpoint or self.DEFAULT_ENDPOINT
431+
self.config_store = config_store
428432

429433
def resolve(self, service_name, region_name=None, endpoint_url=None,
430434
is_secure=True):
431435
region_name = self._check_default_region(service_name, region_name)
436+
use_dualstack_endpoint = self._resolve_use_dualstack_endpoint(
437+
service_name)
438+
use_fips_endpoint = self._resolve_endpoint_variant_config_var(
439+
'use_fips_endpoint'
440+
)
432441
resolved = self.endpoint_resolver.construct_endpoint(
433-
service_name, region_name)
442+
service_name, region_name,
443+
use_dualstack_endpoint=use_dualstack_endpoint,
444+
use_fips_endpoint=use_fips_endpoint,
445+
)
434446

435447
# If we can't resolve the region, we'll attempt to get a global
436448
# endpoint for non-regionalized services (iam, route53, etc)
437449
if not resolved:
438450
# TODO: fallback partition_name should be configurable in the
439451
# future for users to define as needed.
440452
resolved = self.endpoint_resolver.construct_endpoint(
441-
service_name, region_name, partition_name='aws')
453+
service_name, region_name, partition_name='aws',
454+
use_dualstack_endpoint=use_dualstack_endpoint,
455+
use_fips_endpoint=use_fips_endpoint,
456+
)
442457

443458
if resolved:
444459
return self._create_endpoint(
@@ -456,20 +471,13 @@ def _check_default_region(self, service_name, region_name):
456471

457472
def _create_endpoint(self, resolved, service_name, region_name,
458473
endpoint_url, is_secure):
459-
explicit_region = region_name is not None
460474
region_name, signing_region = self._pick_region_values(
461475
resolved, region_name, endpoint_url)
462476
if endpoint_url is None:
463-
if self._is_s3_dualstack_mode(service_name):
464-
endpoint_url = self._create_dualstack_endpoint(
465-
service_name, region_name,
466-
resolved['dnsSuffix'], is_secure, explicit_region)
467-
else:
468-
# Use the sslCommonName over the hostname for Python 2.6 compat.
469-
hostname = resolved.get('sslCommonName', resolved.get('hostname'))
470-
endpoint_url = self._make_url(
471-
hostname, is_secure, resolved.get('protocols', [])
472-
)
477+
# Use the sslCommonName over the hostname for Python 2.6 compat.
478+
hostname = resolved.get('sslCommonName', resolved.get('hostname'))
479+
endpoint_url = self._make_url(hostname, is_secure,
480+
resolved.get('protocols', []))
473481
signature_version = self._resolve_signature_version(
474482
service_name, resolved)
475483
signing_name = self._resolve_signing_name(service_name, resolved)
@@ -479,9 +487,28 @@ def _create_endpoint(self, resolved, service_name, region_name,
479487
endpoint_url=endpoint_url, metadata=resolved,
480488
signature_version=signature_version)
481489

490+
def _resolve_endpoint_variant_config_var(self, config_var):
491+
client_config = self.client_config
492+
config_val = False
493+
494+
# Client configuration arg has precedence
495+
if client_config and getattr(client_config, config_var) is not None:
496+
return getattr(client_config, config_var)
497+
elif self.config_store is not None:
498+
# Check config store
499+
config_val = self.config_store.get_config_variable(config_var)
500+
return config_val
501+
502+
def _resolve_use_dualstack_endpoint(self, service_name):
503+
s3_dualstack_mode = self._is_s3_dualstack_mode(service_name)
504+
if s3_dualstack_mode is not None:
505+
return s3_dualstack_mode
506+
return self._resolve_endpoint_variant_config_var(
507+
'use_dualstack_endpoint')
508+
482509
def _is_s3_dualstack_mode(self, service_name):
483-
if service_name not in self._DUALSTACK_ENABLED_SERVICES:
484-
return False
510+
if service_name not in self._DUALSTACK_CUSTOMIZED_SERVICES:
511+
return None
485512
# TODO: This normalization logic is duplicated from the
486513
# ClientArgsCreator class. Consolidate everything to
487514
# ClientArgsCreator. _resolve_signature_version also has similarly
@@ -491,27 +518,11 @@ def _is_s3_dualstack_mode(self, service_name):
491518
'use_dualstack_endpoint' in client_config.s3:
492519
# Client config trumps scoped config.
493520
return client_config.s3['use_dualstack_endpoint']
494-
if self.scoped_config is None:
495-
return False
496-
enabled = self.scoped_config.get('s3', {}).get(
497-
'use_dualstack_endpoint', False)
498-
if enabled in [True, 'True', 'true']:
499-
return True
500-
return False
501-
502-
def _create_dualstack_endpoint(self, service_name, region_name,
503-
dns_suffix, is_secure, explicit_region):
504-
if not explicit_region and region_name == 'aws-global':
505-
# If the region_name passed was not explicitly set, default to
506-
# us-east-1 instead of the modeled default aws-global. Dualstack
507-
# does not support aws-global
508-
region_name = 'us-east-1'
509-
hostname = '{service}.dualstack.{region}.{dns_suffix}'.format(
510-
service=service_name, region=region_name,
511-
dns_suffix=dns_suffix)
512-
# Dualstack supports http and https so were hardcoding this value for
513-
# now. This can potentially move into the endpoints.json file.
514-
return self._make_url(hostname, is_secure, ['http', 'https'])
521+
if self.scoped_config is not None:
522+
enabled = self.scoped_config.get('s3', {}).get(
523+
'use_dualstack_endpoint')
524+
if enabled in [True, 'True', 'true']:
525+
return True
515526

516527
def _assume_endpoint(self, service_name, region_name, endpoint_url,
517528
is_secure):

0 commit comments

Comments
 (0)