python-botocore/botocore/args.py
2018-10-04 15:50:52 +09:00

210 lines
9.1 KiB
Python

# Copyright 2016 Amazon.com, Inc. or its affiliates. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"). You
# may not use this file except in compliance with the License. A copy of
# the License is located at
#
# http://aws.amazon.com/apache2.0/
#
# or in the "license" file accompanying this file. This file is
# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
# ANY KIND, either express or implied. See the License for the specific
# language governing permissions and limitations under the License.
"""Internal module to help with normalizing botocore client args.
This module (and all function/classes within this module) should be
considered internal, and *not* a public API.
"""
import copy
import logging
import botocore.serialize
from botocore.signers import RequestSigner
from botocore.config import Config
from botocore.endpoint import EndpointCreator
logger = logging.getLogger(__name__)
class ClientArgsCreator(object):
def __init__(self, event_emitter, user_agent, response_parser_factory,
loader, exceptions_factory):
self._event_emitter = event_emitter
self._user_agent = user_agent
self._response_parser_factory = response_parser_factory
self._loader = loader
self._exceptions_factory = exceptions_factory
def get_client_args(self, service_model, region_name, is_secure,
endpoint_url, verify, credentials, scoped_config,
client_config, endpoint_bridge):
final_args = self.compute_client_args(
service_model, client_config, endpoint_bridge, region_name,
endpoint_url, is_secure, scoped_config)
service_name = final_args['service_name']
parameter_validation = final_args['parameter_validation']
endpoint_config = final_args['endpoint_config']
protocol = final_args['protocol']
config_kwargs = final_args['config_kwargs']
s3_config = final_args['s3_config']
partition = endpoint_config['metadata'].get('partition', None)
signing_region = endpoint_config['signing_region']
endpoint_region_name = endpoint_config['region_name']
if signing_region is None and endpoint_region_name is None:
signing_region, endpoint_region_name = \
self._get_default_s3_region(service_name, endpoint_bridge)
config_kwargs['region_name'] = endpoint_region_name
event_emitter = copy.copy(self._event_emitter)
signer = RequestSigner(
service_model.service_id, signing_region,
endpoint_config['signing_name'],
endpoint_config['signature_version'],
credentials, event_emitter
)
config_kwargs['s3'] = s3_config
new_config = Config(**config_kwargs)
endpoint_creator = EndpointCreator(event_emitter)
endpoint = endpoint_creator.create_endpoint(
service_model, region_name=endpoint_region_name,
endpoint_url=endpoint_config['endpoint_url'], verify=verify,
response_parser_factory=self._response_parser_factory,
max_pool_connections=new_config.max_pool_connections,
proxies=new_config.proxies,
timeout=(new_config.connect_timeout, new_config.read_timeout))
serializer = botocore.serialize.create_serializer(
protocol, parameter_validation)
response_parser = botocore.parsers.create_parser(protocol)
return {
'serializer': serializer,
'endpoint': endpoint,
'response_parser': response_parser,
'event_emitter': event_emitter,
'request_signer': signer,
'service_model': service_model,
'loader': self._loader,
'client_config': new_config,
'partition': partition,
'exceptions_factory': self._exceptions_factory
}
def compute_client_args(self, service_model, client_config,
endpoint_bridge, region_name, endpoint_url,
is_secure, scoped_config):
service_name = service_model.endpoint_prefix
protocol = service_model.metadata['protocol']
parameter_validation = True
if client_config and not client_config.parameter_validation:
parameter_validation = False
elif scoped_config:
raw_value = str(scoped_config.get('parameter_validation', ''))
if raw_value.lower() == 'false':
parameter_validation = False
endpoint_config = endpoint_bridge.resolve(
service_name, region_name, endpoint_url, is_secure)
# Override the user agent if specified in the client config.
user_agent = self._user_agent
if client_config is not None:
if client_config.user_agent is not None:
user_agent = client_config.user_agent
if client_config.user_agent_extra is not None:
user_agent += ' %s' % client_config.user_agent_extra
# Create a new client config to be passed to the client based
# on the final values. We do not want the user to be able
# to try to modify an existing client with a client config.
config_kwargs = dict(
region_name=endpoint_config['region_name'],
signature_version=endpoint_config['signature_version'],
user_agent=user_agent)
if client_config is not None:
config_kwargs.update(
connect_timeout=client_config.connect_timeout,
read_timeout=client_config.read_timeout,
max_pool_connections=client_config.max_pool_connections,
proxies=client_config.proxies,
retries=client_config.retries
)
s3_config = self.compute_s3_config(scoped_config,
client_config)
return {
'service_name': service_name,
'parameter_validation': parameter_validation,
'user_agent': user_agent,
'endpoint_config': endpoint_config,
'protocol': protocol,
'config_kwargs': config_kwargs,
's3_config': s3_config,
}
def compute_s3_config(self, scoped_config, client_config):
s3_configuration = None
# Check the scoped config first.
if scoped_config is not None:
s3_configuration = scoped_config.get('s3')
# Until we have proper validation of the config file (including
# nested types), we have to account for the fact that the s3
# key could be parsed as a string, e.g 's3 = foo'.
# In the case we'll ignore the key for now.
if not isinstance(s3_configuration, dict):
logger.debug("The s3 config key is not a dictionary type, "
"ignoring its value of: %s", s3_configuration)
s3_configuration = None
# Convert logic for several s3 keys in the scoped config
# so that the various strings map to the appropriate boolean value.
if s3_configuration:
boolean_keys = ['use_accelerate_endpoint',
'use_dualstack_endpoint',
'payload_signing_enabled']
s3_configuration = self._convert_config_to_bool(
s3_configuration, boolean_keys)
# Next specific client config values takes precedence over
# specific values in the scoped config.
if client_config is not None:
if client_config.s3 is not None:
if s3_configuration is None:
s3_configuration = client_config.s3
else:
# The current s3_configuration dictionary may be
# from a source that only should be read from so
# we want to be safe and just make a copy of it to modify
# before it actually gets updated.
s3_configuration = s3_configuration.copy()
s3_configuration.update(client_config.s3)
return s3_configuration
def _convert_config_to_bool(self, config_dict, keys):
# Make sure any further modifications to this section of the config
# will not affect the scoped config by making a copy of it.
config_copy = config_dict.copy()
present_keys = [k for k in keys if k in config_copy]
for key in present_keys:
# Normalize on different possible values of True
if config_copy[key] in [True, 'True', 'true']:
config_copy[key] = True
else:
config_copy[key] = False
return config_copy
def _get_default_s3_region(self, service_name, endpoint_bridge):
# If a user is providing a custom URL, the endpoint resolver will
# refuse to infer a signing region. If we want to default to s3v4,
# we have to account for this.
if service_name == 's3':
endpoint = endpoint_bridge.resolve('s3')
return endpoint['signing_region'], endpoint['region_name']
return None, None