Initial commit

This commit is contained in:
Senad Uka
2019-11-17 12:44:16 +01:00
parent e41eae7301
commit a3ef27c7a0
4894 changed files with 1771218 additions and 0 deletions

View File

@@ -0,0 +1,92 @@
# Copyright (c) 2012-2013 Mitch Garnaat http://garnaat.org/
# Copyright 2012-2014 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.
import os
import re
import logging
__version__ = '1.13.14'
class NullHandler(logging.Handler):
def emit(self, record):
pass
# Configure default logger to do nothing
log = logging.getLogger('botocore')
log.addHandler(NullHandler())
_first_cap_regex = re.compile('(.)([A-Z][a-z]+)')
_end_cap_regex = re.compile('([a-z0-9])([A-Z])')
# The regex below handles the special case where some acryonym
# name is pluralized, e.g GatewayARNs, ListWebACLs, SomeCNAMEs.
_special_case_transform = re.compile('[A-Z]{3,}s$')
# Prepopulate the cache with special cases that don't match
# our regular transformation.
_xform_cache = {
('CreateCachediSCSIVolume', '_'): 'create_cached_iscsi_volume',
('CreateCachediSCSIVolume', '-'): 'create-cached-iscsi-volume',
('DescribeCachediSCSIVolumes', '_'): 'describe_cached_iscsi_volumes',
('DescribeCachediSCSIVolumes', '-'): 'describe-cached-iscsi-volumes',
('DescribeStorediSCSIVolumes', '_'): 'describe_stored_iscsi_volumes',
('DescribeStorediSCSIVolumes', '-'): 'describe-stored-iscsi-volumes',
('CreateStorediSCSIVolume', '_'): 'create_stored_iscsi_volume',
('CreateStorediSCSIVolume', '-'): 'create-stored-iscsi-volume',
('ListHITsForQualificationType', '_'): 'list_hits_for_qualification_type',
('ListHITsForQualificationType', '-'): 'list-hits-for-qualification-type',
}
# The items in this dict represent partial renames to apply globally to all
# services which might have a matching argument or operation. This way a
# common mis-translation can be fixed without having to call out each
# individual case.
ScalarTypes = ('string', 'integer', 'boolean', 'timestamp', 'float', 'double')
BOTOCORE_ROOT = os.path.dirname(os.path.abspath(__file__))
# Used to specify anonymous (unsigned) request signature
class UNSIGNED(object):
def __copy__(self):
return self
def __deepcopy__(self, memodict):
return self
UNSIGNED = UNSIGNED()
def xform_name(name, sep='_', _xform_cache=_xform_cache):
"""Convert camel case to a "pythonic" name.
If the name contains the ``sep`` character, then it is
returned unchanged.
"""
if sep in name:
# If the sep is in the name, assume that it's already
# transformed and return the string unchanged.
return name
key = (name, sep)
if key not in _xform_cache:
if _special_case_transform.search(name) is not None:
is_special = _special_case_transform.search(name)
matched = is_special.group()
# Replace something like ARNs, ACLs with _arns, _acls.
name = name[:-len(matched)] + sep + matched.lower()
s1 = _first_cap_regex.sub(r'\1' + sep + r'\2', name)
transformed = _end_cap_regex.sub(r'\1' + sep + r'\2', s1).lower()
_xform_cache[key] = transformed
return _xform_cache[key]

View File

@@ -0,0 +1,296 @@
# 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 socket
import botocore.exceptions
import botocore.serialize
import botocore.utils
from botocore.signers import RequestSigner
from botocore.config import Config
from botocore.endpoint import EndpointCreator
logger = logging.getLogger(__name__)
VALID_STS_REGIONAL_ENDPOINTS_CONFIG = [
'legacy',
'regional',
]
LEGACY_GLOBAL_STS_REGIONS = [
'ap-northeast-1',
'ap-south-1',
'ap-southeast-1',
'ap-southeast-2',
'aws-global',
'ca-central-1',
'eu-central-1',
'eu-north-1',
'eu-west-1',
'eu-west-2',
'eu-west-3',
'sa-east-1',
'us-east-1',
'us-east-2',
'us-west-1',
'us-west-2',
]
class ClientArgsCreator(object):
def __init__(self, event_emitter, user_agent, response_parser_factory,
loader, exceptions_factory, config_store):
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
self._config_store = config_store
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)
socket_options = final_args['socket_options']
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),
socket_options=socket_options,
client_cert=new_config.client_cert)
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 = scoped_config.get('parameter_validation')
if raw_value is not None:
parameter_validation = botocore.utils.ensure_boolean(raw_value)
# 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
endpoint_config = self._compute_endpoint_config(
service_name=service_name,
region_name=region_name,
endpoint_url=endpoint_url,
is_secure=is_secure,
endpoint_bridge=endpoint_bridge,
)
# 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,
client_cert=client_config.client_cert,
inject_host_prefix=client_config.inject_host_prefix,
)
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,
'socket_options': self._compute_socket_options(scoped_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 _compute_endpoint_config(self, service_name, region_name, endpoint_url,
is_secure, endpoint_bridge):
endpoint_config = endpoint_bridge.resolve(
service_name, region_name, endpoint_url, is_secure)
if self._should_set_global_sts_endpoint(
service_name, region_name, endpoint_url):
self._set_global_sts_endpoint(endpoint_config, is_secure)
return endpoint_config
def _should_set_global_sts_endpoint(self, service_name, region_name,
endpoint_url):
if service_name != 'sts':
return False
if endpoint_url:
return False
return (
self._get_sts_regional_endpoints_config() == 'legacy' and
region_name in LEGACY_GLOBAL_STS_REGIONS
)
def _get_sts_regional_endpoints_config(self):
sts_regional_endpoints_config = self._config_store.get_config_variable(
'sts_regional_endpoints')
if not sts_regional_endpoints_config:
sts_regional_endpoints_config = 'legacy'
if sts_regional_endpoints_config not in \
VALID_STS_REGIONAL_ENDPOINTS_CONFIG:
raise botocore.exceptions.InvalidSTSRegionalEndpointsConfigError(
sts_regional_endpoints_config=sts_regional_endpoints_config)
return sts_regional_endpoints_config
def _set_global_sts_endpoint(self, endpoint_config, is_secure):
scheme = 'https' if is_secure else 'http'
endpoint_config['endpoint_url'] = '%s://sts.amazonaws.com' % scheme
endpoint_config['signing_region'] = 'us-east-1'
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:
config_copy[key] = botocore.utils.ensure_boolean(config_copy[key])
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
def _compute_socket_options(self, scoped_config):
# This disables Nagle's algorithm and is the default socket options
# in urllib3.
socket_options = [(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)]
if scoped_config:
# Enables TCP Keepalive if specified in shared config file.
if self._ensure_boolean(scoped_config.get('tcp_keepalive', False)):
socket_options.append(
(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1))
return socket_options
def _ensure_boolean(self, val):
if isinstance(val, bool):
return val
else:
return val.lower() == 'true'

View File

@@ -0,0 +1,872 @@
# Copyright (c) 2012-2013 Mitch Garnaat http://garnaat.org/
# Copyright 2012-2014 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.
import base64
import datetime
from hashlib import sha256
from hashlib import sha1
import hmac
import logging
from email.utils import formatdate
from operator import itemgetter
import functools
import time
import calendar
import json
from botocore.exceptions import NoCredentialsError
from botocore.utils import normalize_url_path, percent_encode_sequence
from botocore.compat import HTTPHeaders
from botocore.compat import quote, unquote, urlsplit, parse_qs
from botocore.compat import urlunsplit
from botocore.compat import encodebytes
from botocore.compat import six
from botocore.compat import json
from botocore.compat import MD5_AVAILABLE
from botocore.compat import ensure_unicode
logger = logging.getLogger(__name__)
EMPTY_SHA256_HASH = (
'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855')
# This is the buffer size used when calculating sha256 checksums.
# Experimenting with various buffer sizes showed that this value generally
# gave the best result (in terms of performance).
PAYLOAD_BUFFER = 1024 * 1024
ISO8601 = '%Y-%m-%dT%H:%M:%SZ'
SIGV4_TIMESTAMP = '%Y%m%dT%H%M%SZ'
SIGNED_HEADERS_BLACKLIST = [
'expect',
'user-agent',
'x-amzn-trace-id',
]
UNSIGNED_PAYLOAD = 'UNSIGNED-PAYLOAD'
class BaseSigner(object):
REQUIRES_REGION = False
def add_auth(self, request):
raise NotImplementedError("add_auth")
class SigV2Auth(BaseSigner):
"""
Sign a request with Signature V2.
"""
def __init__(self, credentials):
self.credentials = credentials
def calc_signature(self, request, params):
logger.debug("Calculating signature using v2 auth.")
split = urlsplit(request.url)
path = split.path
if len(path) == 0:
path = '/'
string_to_sign = '%s\n%s\n%s\n' % (request.method,
split.netloc,
path)
lhmac = hmac.new(self.credentials.secret_key.encode('utf-8'),
digestmod=sha256)
pairs = []
for key in sorted(params):
# Any previous signature should not be a part of this
# one, so we skip that particular key. This prevents
# issues during retries.
if key == 'Signature':
continue
value = six.text_type(params[key])
pairs.append(quote(key.encode('utf-8'), safe='') + '=' +
quote(value.encode('utf-8'), safe='-_~'))
qs = '&'.join(pairs)
string_to_sign += qs
logger.debug('String to sign: %s', string_to_sign)
lhmac.update(string_to_sign.encode('utf-8'))
b64 = base64.b64encode(lhmac.digest()).strip().decode('utf-8')
return (qs, b64)
def add_auth(self, request):
# The auth handler is the last thing called in the
# preparation phase of a prepared request.
# Because of this we have to parse the query params
# from the request body so we can update them with
# the sigv2 auth params.
if self.credentials is None:
raise NoCredentialsError
if request.data:
# POST
params = request.data
else:
# GET
params = request.params
params['AWSAccessKeyId'] = self.credentials.access_key
params['SignatureVersion'] = '2'
params['SignatureMethod'] = 'HmacSHA256'
params['Timestamp'] = time.strftime(ISO8601, time.gmtime())
if self.credentials.token:
params['SecurityToken'] = self.credentials.token
qs, signature = self.calc_signature(request, params)
params['Signature'] = signature
return request
class SigV3Auth(BaseSigner):
def __init__(self, credentials):
self.credentials = credentials
def add_auth(self, request):
if self.credentials is None:
raise NoCredentialsError
if 'Date' in request.headers:
del request.headers['Date']
request.headers['Date'] = formatdate(usegmt=True)
if self.credentials.token:
if 'X-Amz-Security-Token' in request.headers:
del request.headers['X-Amz-Security-Token']
request.headers['X-Amz-Security-Token'] = self.credentials.token
new_hmac = hmac.new(self.credentials.secret_key.encode('utf-8'),
digestmod=sha256)
new_hmac.update(request.headers['Date'].encode('utf-8'))
encoded_signature = encodebytes(new_hmac.digest()).strip()
signature = ('AWS3-HTTPS AWSAccessKeyId=%s,Algorithm=%s,Signature=%s' %
(self.credentials.access_key, 'HmacSHA256',
encoded_signature.decode('utf-8')))
if 'X-Amzn-Authorization' in request.headers:
del request.headers['X-Amzn-Authorization']
request.headers['X-Amzn-Authorization'] = signature
class SigV4Auth(BaseSigner):
"""
Sign a request with Signature V4.
"""
REQUIRES_REGION = True
def __init__(self, credentials, service_name, region_name):
self.credentials = credentials
# We initialize these value here so the unit tests can have
# valid values. But these will get overriden in ``add_auth``
# later for real requests.
self._region_name = region_name
self._service_name = service_name
def _sign(self, key, msg, hex=False):
if hex:
sig = hmac.new(key, msg.encode('utf-8'), sha256).hexdigest()
else:
sig = hmac.new(key, msg.encode('utf-8'), sha256).digest()
return sig
def headers_to_sign(self, request):
"""
Select the headers from the request that need to be included
in the StringToSign.
"""
header_map = HTTPHeaders()
for name, value in request.headers.items():
lname = name.lower()
if lname not in SIGNED_HEADERS_BLACKLIST:
header_map[lname] = value
if 'host' not in header_map:
# Ensure we sign the lowercased version of the host, as that
# is what will ultimately be sent on the wire.
# TODO: We should set the host ourselves, instead of relying on our
# HTTP client to set it for us.
header_map['host'] = self._canonical_host(request.url).lower()
return header_map
def _canonical_host(self, url):
url_parts = urlsplit(url)
default_ports = {
'http': 80,
'https': 443
}
if any(url_parts.scheme == scheme and url_parts.port == port
for scheme, port in default_ports.items()):
# No need to include the port if it's the default port.
return url_parts.hostname
# Strip out auth if it's present in the netloc.
return url_parts.netloc.rsplit('@', 1)[-1]
def canonical_query_string(self, request):
# The query string can come from two parts. One is the
# params attribute of the request. The other is from the request
# url (in which case we have to re-split the url into its components
# and parse out the query string component).
if request.params:
return self._canonical_query_string_params(request.params)
else:
return self._canonical_query_string_url(urlsplit(request.url))
def _canonical_query_string_params(self, params):
l = []
for param in sorted(params):
value = str(params[param])
l.append('%s=%s' % (quote(param, safe='-_.~'),
quote(value, safe='-_.~')))
cqs = '&'.join(l)
return cqs
def _canonical_query_string_url(self, parts):
canonical_query_string = ''
if parts.query:
# [(key, value), (key2, value2)]
key_val_pairs = []
for pair in parts.query.split('&'):
key, _, value = pair.partition('=')
key_val_pairs.append((key, value))
sorted_key_vals = []
# Sort by the key names, and in the case of
# repeated keys, then sort by the value.
for key, value in sorted(key_val_pairs):
sorted_key_vals.append('%s=%s' % (key, value))
canonical_query_string = '&'.join(sorted_key_vals)
return canonical_query_string
def canonical_headers(self, headers_to_sign):
"""
Return the headers that need to be included in the StringToSign
in their canonical form by converting all header keys to lower
case, sorting them in alphabetical order and then joining
them into a string, separated by newlines.
"""
headers = []
sorted_header_names = sorted(set(headers_to_sign))
for key in sorted_header_names:
value = ','.join(self._header_value(v) for v in
sorted(headers_to_sign.get_all(key)))
headers.append('%s:%s' % (key, ensure_unicode(value)))
return '\n'.join(headers)
def _header_value(self, value):
# From the sigv4 docs:
# Lowercase(HeaderName) + ':' + Trimall(HeaderValue)
#
# The Trimall function removes excess white space before and after
# values, and converts sequential spaces to a single space.
return ' '.join(value.split())
def signed_headers(self, headers_to_sign):
l = ['%s' % n.lower().strip() for n in set(headers_to_sign)]
l = sorted(l)
return ';'.join(l)
def payload(self, request):
if not self._should_sha256_sign_payload(request):
# When payload signing is disabled, we use this static string in
# place of the payload checksum.
return UNSIGNED_PAYLOAD
request_body = request.body
if request_body and hasattr(request_body, 'seek'):
position = request_body.tell()
read_chunksize = functools.partial(request_body.read,
PAYLOAD_BUFFER)
checksum = sha256()
for chunk in iter(read_chunksize, b''):
checksum.update(chunk)
hex_checksum = checksum.hexdigest()
request_body.seek(position)
return hex_checksum
elif request_body:
# The request serialization has ensured that
# request.body is a bytes() type.
return sha256(request_body).hexdigest()
else:
return EMPTY_SHA256_HASH
def _should_sha256_sign_payload(self, request):
# Payloads will always be signed over insecure connections.
if not request.url.startswith('https'):
return True
# Certain operations may have payload signing disabled by default.
# Since we don't have access to the operation model, we pass in this
# bit of metadata through the request context.
return request.context.get('payload_signing_enabled', True)
def canonical_request(self, request):
cr = [request.method.upper()]
path = self._normalize_url_path(urlsplit(request.url).path)
cr.append(path)
cr.append(self.canonical_query_string(request))
headers_to_sign = self.headers_to_sign(request)
cr.append(self.canonical_headers(headers_to_sign) + '\n')
cr.append(self.signed_headers(headers_to_sign))
if 'X-Amz-Content-SHA256' in request.headers:
body_checksum = request.headers['X-Amz-Content-SHA256']
else:
body_checksum = self.payload(request)
cr.append(body_checksum)
return '\n'.join(cr)
def _normalize_url_path(self, path):
normalized_path = quote(normalize_url_path(path), safe='/~')
return normalized_path
def scope(self, request):
scope = [self.credentials.access_key]
scope.append(request.context['timestamp'][0:8])
scope.append(self._region_name)
scope.append(self._service_name)
scope.append('aws4_request')
return '/'.join(scope)
def credential_scope(self, request):
scope = []
scope.append(request.context['timestamp'][0:8])
scope.append(self._region_name)
scope.append(self._service_name)
scope.append('aws4_request')
return '/'.join(scope)
def string_to_sign(self, request, canonical_request):
"""
Return the canonical StringToSign as well as a dict
containing the original version of all headers that
were included in the StringToSign.
"""
sts = ['AWS4-HMAC-SHA256']
sts.append(request.context['timestamp'])
sts.append(self.credential_scope(request))
sts.append(sha256(canonical_request.encode('utf-8')).hexdigest())
return '\n'.join(sts)
def signature(self, string_to_sign, request):
key = self.credentials.secret_key
k_date = self._sign(('AWS4' + key).encode('utf-8'),
request.context['timestamp'][0:8])
k_region = self._sign(k_date, self._region_name)
k_service = self._sign(k_region, self._service_name)
k_signing = self._sign(k_service, 'aws4_request')
return self._sign(k_signing, string_to_sign, hex=True)
def add_auth(self, request):
if self.credentials is None:
raise NoCredentialsError
datetime_now = datetime.datetime.utcnow()
request.context['timestamp'] = datetime_now.strftime(SIGV4_TIMESTAMP)
# This could be a retry. Make sure the previous
# authorization header is removed first.
self._modify_request_before_signing(request)
canonical_request = self.canonical_request(request)
logger.debug("Calculating signature using v4 auth.")
logger.debug('CanonicalRequest:\n%s', canonical_request)
string_to_sign = self.string_to_sign(request, canonical_request)
logger.debug('StringToSign:\n%s', string_to_sign)
signature = self.signature(string_to_sign, request)
logger.debug('Signature:\n%s', signature)
self._inject_signature_to_request(request, signature)
def _inject_signature_to_request(self, request, signature):
l = ['AWS4-HMAC-SHA256 Credential=%s' % self.scope(request)]
headers_to_sign = self.headers_to_sign(request)
l.append('SignedHeaders=%s' % self.signed_headers(headers_to_sign))
l.append('Signature=%s' % signature)
request.headers['Authorization'] = ', '.join(l)
return request
def _modify_request_before_signing(self, request):
if 'Authorization' in request.headers:
del request.headers['Authorization']
self._set_necessary_date_headers(request)
if self.credentials.token:
if 'X-Amz-Security-Token' in request.headers:
del request.headers['X-Amz-Security-Token']
request.headers['X-Amz-Security-Token'] = self.credentials.token
if not request.context.get('payload_signing_enabled', True):
if 'X-Amz-Content-SHA256' in request.headers:
del request.headers['X-Amz-Content-SHA256']
request.headers['X-Amz-Content-SHA256'] = UNSIGNED_PAYLOAD
def _set_necessary_date_headers(self, request):
# The spec allows for either the Date _or_ the X-Amz-Date value to be
# used so we check both. If there's a Date header, we use the date
# header. Otherwise we use the X-Amz-Date header.
if 'Date' in request.headers:
del request.headers['Date']
datetime_timestamp = datetime.datetime.strptime(
request.context['timestamp'], SIGV4_TIMESTAMP)
request.headers['Date'] = formatdate(
int(calendar.timegm(datetime_timestamp.timetuple())))
if 'X-Amz-Date' in request.headers:
del request.headers['X-Amz-Date']
else:
if 'X-Amz-Date' in request.headers:
del request.headers['X-Amz-Date']
request.headers['X-Amz-Date'] = request.context['timestamp']
class S3SigV4Auth(SigV4Auth):
def __init__(self, credentials, service_name, region_name):
super(S3SigV4Auth, self).__init__(
credentials, service_name, region_name)
self._default_region_name = region_name
def add_auth(self, request):
# If we ever decide to share auth sessions, this could potentially be
# a source of concurrency bugs.
signing_context = request.context.get('signing', {})
self._region_name = signing_context.get(
'region', self._default_region_name)
super(S3SigV4Auth, self).add_auth(request)
def _modify_request_before_signing(self, request):
super(S3SigV4Auth, self)._modify_request_before_signing(request)
if 'X-Amz-Content-SHA256' in request.headers:
del request.headers['X-Amz-Content-SHA256']
request.headers['X-Amz-Content-SHA256'] = self.payload(request)
def _should_sha256_sign_payload(self, request):
# S3 allows optional body signing, so to minimize the performance
# impact, we opt to not SHA256 sign the body on streaming uploads,
# provided that we're on https.
client_config = request.context.get('client_config')
s3_config = getattr(client_config, 's3', None)
# The config could be None if it isn't set, or if the customer sets it
# to None.
if s3_config is None:
s3_config = {}
# The explicit configuration takes precedence over any implicit
# configuration.
sign_payload = s3_config.get('payload_signing_enabled', None)
if sign_payload is not None:
return sign_payload
# We require that both content-md5 be present and https be enabled
# to implicitly disable body signing. The combination of TLS and
# content-md5 is sufficiently secure and durable for us to be
# confident in the request without body signing.
if not request.url.startswith('https') or \
'Content-MD5' not in request.headers:
return True
# If the input is streaming we disable body signing by default.
if request.context.get('has_streaming_input', False):
return False
# If the S3-specific checks had no results, delegate to the generic
# checks.
return super(S3SigV4Auth, self)._should_sha256_sign_payload(request)
def _normalize_url_path(self, path):
# For S3, we do not normalize the path.
return path
class SigV4QueryAuth(SigV4Auth):
DEFAULT_EXPIRES = 3600
def __init__(self, credentials, service_name, region_name,
expires=DEFAULT_EXPIRES):
super(SigV4QueryAuth, self).__init__(credentials, service_name,
region_name)
self._expires = expires
def _modify_request_before_signing(self, request):
# We automatically set this header, so if it's the auto-set value we
# want to get rid of it since it doesn't make sense for presigned urls.
content_type = request.headers.get('content-type')
blacklisted_content_type = (
'application/x-www-form-urlencoded; charset=utf-8'
)
if content_type == blacklisted_content_type:
del request.headers['content-type']
# Note that we're not including X-Amz-Signature.
# From the docs: "The Canonical Query String must include all the query
# parameters from the preceding table except for X-Amz-Signature.
signed_headers = self.signed_headers(self.headers_to_sign(request))
auth_params = {
'X-Amz-Algorithm': 'AWS4-HMAC-SHA256',
'X-Amz-Credential': self.scope(request),
'X-Amz-Date': request.context['timestamp'],
'X-Amz-Expires': self._expires,
'X-Amz-SignedHeaders': signed_headers,
}
if self.credentials.token is not None:
auth_params['X-Amz-Security-Token'] = self.credentials.token
# Now parse the original query string to a dict, inject our new query
# params, and serialize back to a query string.
url_parts = urlsplit(request.url)
# parse_qs makes each value a list, but in our case we know we won't
# have repeated keys so we know we have single element lists which we
# can convert back to scalar values.
query_dict = dict(
[(k, v[0]) for k, v in
parse_qs(url_parts.query, keep_blank_values=True).items()])
# The spec is particular about this. It *has* to be:
# https://<endpoint>?<operation params>&<auth params>
# You can't mix the two types of params together, i.e just keep doing
# new_query_params.update(op_params)
# new_query_params.update(auth_params)
# percent_encode_sequence(new_query_params)
operation_params = ''
if request.data:
# We also need to move the body params into the query string. To
# do this, we first have to convert it to a dict.
query_dict.update(self._get_body_as_dict(request))
request.data = ''
if query_dict:
operation_params = percent_encode_sequence(query_dict) + '&'
new_query_string = (operation_params +
percent_encode_sequence(auth_params))
# url_parts is a tuple (and therefore immutable) so we need to create
# a new url_parts with the new query string.
# <part> - <index>
# scheme - 0
# netloc - 1
# path - 2
# query - 3 <-- we're replacing this.
# fragment - 4
p = url_parts
new_url_parts = (p[0], p[1], p[2], new_query_string, p[4])
request.url = urlunsplit(new_url_parts)
def _get_body_as_dict(self, request):
# For query services, request.data is form-encoded and is already a
# dict, but for other services such as rest-json it could be a json
# string or bytes. In those cases we attempt to load the data as a
# dict.
data = request.data
if isinstance(data, six.binary_type):
data = json.loads(data.decode('utf-8'))
elif isinstance(data, six.string_types):
data = json.loads(data)
return data
def _inject_signature_to_request(self, request, signature):
# Rather than calculating an "Authorization" header, for the query
# param quth, we just append an 'X-Amz-Signature' param to the end
# of the query string.
request.url += '&X-Amz-Signature=%s' % signature
class S3SigV4QueryAuth(SigV4QueryAuth):
"""S3 SigV4 auth using query parameters.
This signer will sign a request using query parameters and signature
version 4, i.e a "presigned url" signer.
Based off of:
http://docs.aws.amazon.com/AmazonS3/latest/API/sigv4-query-string-auth.html
"""
def _normalize_url_path(self, path):
# For S3, we do not normalize the path.
return path
def payload(self, request):
# From the doc link above:
# "You don't include a payload hash in the Canonical Request, because
# when you create a presigned URL, you don't know anything about the
# payload. Instead, you use a constant string "UNSIGNED-PAYLOAD".
return UNSIGNED_PAYLOAD
class S3SigV4PostAuth(SigV4Auth):
"""
Presigns a s3 post
Implementation doc here:
http://docs.aws.amazon.com/AmazonS3/latest/API/sigv4-UsingHTTPPOST.html
"""
def add_auth(self, request):
datetime_now = datetime.datetime.utcnow()
request.context['timestamp'] = datetime_now.strftime(SIGV4_TIMESTAMP)
fields = {}
if request.context.get('s3-presign-post-fields', None) is not None:
fields = request.context['s3-presign-post-fields']
policy = {}
conditions = []
if request.context.get('s3-presign-post-policy', None) is not None:
policy = request.context['s3-presign-post-policy']
if policy.get('conditions', None) is not None:
conditions = policy['conditions']
policy['conditions'] = conditions
fields['x-amz-algorithm'] = 'AWS4-HMAC-SHA256'
fields['x-amz-credential'] = self.scope(request)
fields['x-amz-date'] = request.context['timestamp']
conditions.append({'x-amz-algorithm': 'AWS4-HMAC-SHA256'})
conditions.append({'x-amz-credential': self.scope(request)})
conditions.append({'x-amz-date': request.context['timestamp']})
if self.credentials.token is not None:
fields['x-amz-security-token'] = self.credentials.token
conditions.append({'x-amz-security-token': self.credentials.token})
# Dump the base64 encoded policy into the fields dictionary.
fields['policy'] = base64.b64encode(
json.dumps(policy).encode('utf-8')).decode('utf-8')
fields['x-amz-signature'] = self.signature(fields['policy'], request)
request.context['s3-presign-post-fields'] = fields
request.context['s3-presign-post-policy'] = policy
class HmacV1Auth(BaseSigner):
# List of Query String Arguments of Interest
QSAOfInterest = ['accelerate', 'acl', 'cors', 'defaultObjectAcl',
'location', 'logging', 'partNumber', 'policy',
'requestPayment', 'torrent',
'versioning', 'versionId', 'versions', 'website',
'uploads', 'uploadId', 'response-content-type',
'response-content-language', 'response-expires',
'response-cache-control', 'response-content-disposition',
'response-content-encoding', 'delete', 'lifecycle',
'tagging', 'restore', 'storageClass', 'notification',
'replication', 'requestPayment', 'analytics', 'metrics',
'inventory', 'select', 'select-type']
def __init__(self, credentials, service_name=None, region_name=None):
self.credentials = credentials
def sign_string(self, string_to_sign):
new_hmac = hmac.new(self.credentials.secret_key.encode('utf-8'),
digestmod=sha1)
new_hmac.update(string_to_sign.encode('utf-8'))
return encodebytes(new_hmac.digest()).strip().decode('utf-8')
def canonical_standard_headers(self, headers):
interesting_headers = ['content-md5', 'content-type', 'date']
hoi = []
if 'Date' in headers:
del headers['Date']
headers['Date'] = self._get_date()
for ih in interesting_headers:
found = False
for key in headers:
lk = key.lower()
if headers[key] is not None and lk == ih:
hoi.append(headers[key].strip())
found = True
if not found:
hoi.append('')
return '\n'.join(hoi)
def canonical_custom_headers(self, headers):
hoi = []
custom_headers = {}
for key in headers:
lk = key.lower()
if headers[key] is not None:
if lk.startswith('x-amz-'):
custom_headers[lk] = ','.join(v.strip() for v in
headers.get_all(key))
sorted_header_keys = sorted(custom_headers.keys())
for key in sorted_header_keys:
hoi.append("%s:%s" % (key, custom_headers[key]))
return '\n'.join(hoi)
def unquote_v(self, nv):
"""
TODO: Do we need this?
"""
if len(nv) == 1:
return nv
else:
return (nv[0], unquote(nv[1]))
def canonical_resource(self, split, auth_path=None):
# don't include anything after the first ? in the resource...
# unless it is one of the QSA of interest, defined above
# NOTE:
# The path in the canonical resource should always be the
# full path including the bucket name, even for virtual-hosting
# style addressing. The ``auth_path`` keeps track of the full
# path for the canonical resource and would be passed in if
# the client was using virtual-hosting style.
if auth_path is not None:
buf = auth_path
else:
buf = split.path
if split.query:
qsa = split.query.split('&')
qsa = [a.split('=', 1) for a in qsa]
qsa = [self.unquote_v(a) for a in qsa
if a[0] in self.QSAOfInterest]
if len(qsa) > 0:
qsa.sort(key=itemgetter(0))
qsa = ['='.join(a) for a in qsa]
buf += '?'
buf += '&'.join(qsa)
return buf
def canonical_string(self, method, split, headers, expires=None,
auth_path=None):
cs = method.upper() + '\n'
cs += self.canonical_standard_headers(headers) + '\n'
custom_headers = self.canonical_custom_headers(headers)
if custom_headers:
cs += custom_headers + '\n'
cs += self.canonical_resource(split, auth_path=auth_path)
return cs
def get_signature(self, method, split, headers, expires=None,
auth_path=None):
if self.credentials.token:
del headers['x-amz-security-token']
headers['x-amz-security-token'] = self.credentials.token
string_to_sign = self.canonical_string(method,
split,
headers,
auth_path=auth_path)
logger.debug('StringToSign:\n%s', string_to_sign)
return self.sign_string(string_to_sign)
def add_auth(self, request):
if self.credentials is None:
raise NoCredentialsError
logger.debug("Calculating signature using hmacv1 auth.")
split = urlsplit(request.url)
logger.debug('HTTP request method: %s', request.method)
signature = self.get_signature(request.method, split,
request.headers,
auth_path=request.auth_path)
self._inject_signature(request, signature)
def _get_date(self):
return formatdate(usegmt=True)
def _inject_signature(self, request, signature):
if 'Authorization' in request.headers:
# We have to do this because request.headers is not
# normal dictionary. It has the (unintuitive) behavior
# of aggregating repeated setattr calls for the same
# key value. For example:
# headers['foo'] = 'a'; headers['foo'] = 'b'
# list(headers) will print ['foo', 'foo'].
del request.headers['Authorization']
request.headers['Authorization'] = (
"AWS %s:%s" % (self.credentials.access_key, signature))
class HmacV1QueryAuth(HmacV1Auth):
"""
Generates a presigned request for s3.
Spec from this document:
http://docs.aws.amazon.com/AmazonS3/latest/dev/RESTAuthentication.html
#RESTAuthenticationQueryStringAuth
"""
DEFAULT_EXPIRES = 3600
def __init__(self, credentials, expires=DEFAULT_EXPIRES):
self.credentials = credentials
self._expires = expires
def _get_date(self):
return str(int(time.time() + int(self._expires)))
def _inject_signature(self, request, signature):
query_dict = {}
query_dict['AWSAccessKeyId'] = self.credentials.access_key
query_dict['Signature'] = signature
for header_key in request.headers:
lk = header_key.lower()
# For query string requests, Expires is used instead of the
# Date header.
if header_key == 'Date':
query_dict['Expires'] = request.headers['Date']
# We only want to include relevant headers in the query string.
# These can be anything that starts with x-amz, is Content-MD5,
# or is Content-Type.
elif lk.startswith('x-amz-') or lk in ['content-md5',
'content-type']:
query_dict[lk] = request.headers[lk]
# Combine all of the identified headers into an encoded
# query string
new_query_string = percent_encode_sequence(query_dict)
# Create a new url with the presigned url.
p = urlsplit(request.url)
if p[3]:
# If there was a pre-existing query string, we should
# add that back before injecting the new query string.
new_query_string = '%s&%s' % (p[3], new_query_string)
new_url_parts = (p[0], p[1], p[2], new_query_string, p[4])
request.url = urlunsplit(new_url_parts)
class HmacV1PostAuth(HmacV1Auth):
"""
Generates a presigned post for s3.
Spec from this document:
http://docs.aws.amazon.com/AmazonS3/latest/dev/UsingHTTPPOST.html
"""
def add_auth(self, request):
fields = {}
if request.context.get('s3-presign-post-fields', None) is not None:
fields = request.context['s3-presign-post-fields']
policy = {}
conditions = []
if request.context.get('s3-presign-post-policy', None) is not None:
policy = request.context['s3-presign-post-policy']
if policy.get('conditions', None) is not None:
conditions = policy['conditions']
policy['conditions'] = conditions
fields['AWSAccessKeyId'] = self.credentials.access_key
if self.credentials.token is not None:
fields['x-amz-security-token'] = self.credentials.token
conditions.append({'x-amz-security-token': self.credentials.token})
# Dump the base64 encoded policy into the fields dictionary.
fields['policy'] = base64.b64encode(
json.dumps(policy).encode('utf-8')).decode('utf-8')
fields['signature'] = self.sign_string(fields['policy'])
request.context['s3-presign-post-fields'] = fields
request.context['s3-presign-post-policy'] = policy
# Defined at the bottom instead of the top of the module because the Auth
# classes weren't defined yet.
AUTH_TYPE_MAPS = {
'v2': SigV2Auth,
'v4': SigV4Auth,
'v4-query': SigV4QueryAuth,
'v3': SigV3Auth,
'v3https': SigV3Auth,
's3': HmacV1Auth,
's3-query': HmacV1QueryAuth,
's3-presign-post': HmacV1PostAuth,
's3v4': S3SigV4Auth,
's3v4-query': S3SigV4QueryAuth,
's3v4-presign-post': S3SigV4PostAuth,
}

View File

@@ -0,0 +1,649 @@
# Copyright (c) 2012-2013 Mitch Garnaat http://garnaat.org/
# Copyright 2012-2014 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.
import sys
import logging
import functools
import socket
import collections
import urllib3.util
from urllib3.connection import VerifiedHTTPSConnection
from urllib3.connection import HTTPConnection
from urllib3.connectionpool import HTTPConnectionPool
from urllib3.connectionpool import HTTPSConnectionPool
import botocore.utils
from botocore.compat import six
from botocore.compat import HTTPHeaders, HTTPResponse, urlunsplit, urlsplit, \
urlencode
from botocore.exceptions import UnseekableStreamError
logger = logging.getLogger(__name__)
class AWSHTTPResponse(HTTPResponse):
# The *args, **kwargs is used because the args are slightly
# different in py2.6 than in py2.7/py3.
def __init__(self, *args, **kwargs):
self._status_tuple = kwargs.pop('status_tuple')
HTTPResponse.__init__(self, *args, **kwargs)
def _read_status(self):
if self._status_tuple is not None:
status_tuple = self._status_tuple
self._status_tuple = None
return status_tuple
else:
return HTTPResponse._read_status(self)
class AWSConnection(object):
"""Mixin for HTTPConnection that supports Expect 100-continue.
This when mixed with a subclass of httplib.HTTPConnection (though
technically we subclass from urllib3, which subclasses
httplib.HTTPConnection) and we only override this class to support Expect
100-continue, which we need for S3. As far as I can tell, this is
general purpose enough to not be specific to S3, but I'm being
tentative and keeping it in botocore because I've only tested
this against AWS services.
"""
def __init__(self, *args, **kwargs):
super(AWSConnection, self).__init__(*args, **kwargs)
self._original_response_cls = self.response_class
# We'd ideally hook into httplib's states, but they're all
# __mangled_vars so we use our own state var. This variable is set
# when we receive an early response from the server. If this value is
# set to True, any calls to send() are noops. This value is reset to
# false every time _send_request is called. This is to workaround the
# fact that py2.6 (and only py2.6) has a separate send() call for the
# body in _send_request, as opposed to endheaders(), which is where the
# body is sent in all versions > 2.6.
self._response_received = False
self._expect_header_set = False
def close(self):
super(AWSConnection, self).close()
# Reset all of our instance state we were tracking.
self._response_received = False
self._expect_header_set = False
self.response_class = self._original_response_cls
def _tunnel(self):
# Works around a bug in py26 which is fixed in later versions of
# python. Bug involves hitting an infinite loop if readline() returns
# nothing as opposed to just ``\r\n``.
# As much as I don't like having if py2: <foo> code blocks, this seems
# the cleanest way to handle this workaround. Fortunately, the
# difference from py26 to py3 is very minimal. We're essentially
# just overriding the while loop.
if sys.version_info[:2] != (2, 6):
return super(AWSConnection, self)._tunnel()
# Otherwise we workaround the issue.
self._set_hostport(self._tunnel_host, self._tunnel_port)
self.send("CONNECT %s:%d HTTP/1.0\r\n" % (self.host, self.port))
for header, value in self._tunnel_headers.iteritems():
self.send("%s: %s\r\n" % (header, value))
self.send("\r\n")
response = self.response_class(self.sock, strict=self.strict,
method=self._method)
(version, code, message) = response._read_status()
if code != 200:
self.close()
raise socket.error("Tunnel connection failed: %d %s" %
(code, message.strip()))
while True:
line = response.fp.readline()
if not line:
break
if line in (b'\r\n', b'\n', b''):
break
def _send_request(self, method, url, body, headers, *args, **kwargs):
self._response_received = False
if headers.get('Expect', b'') == b'100-continue':
self._expect_header_set = True
else:
self._expect_header_set = False
self.response_class = self._original_response_cls
rval = super(AWSConnection, self)._send_request(
method, url, body, headers, *args, **kwargs)
self._expect_header_set = False
return rval
def _convert_to_bytes(self, mixed_buffer):
# Take a list of mixed str/bytes and convert it
# all into a single bytestring.
# Any six.text_types will be encoded as utf-8.
bytes_buffer = []
for chunk in mixed_buffer:
if isinstance(chunk, six.text_type):
bytes_buffer.append(chunk.encode('utf-8'))
else:
bytes_buffer.append(chunk)
msg = b"\r\n".join(bytes_buffer)
return msg
def _send_output(self, message_body=None, *args, **kwargs):
self._buffer.extend((b"", b""))
msg = self._convert_to_bytes(self._buffer)
del self._buffer[:]
# If msg and message_body are sent in a single send() call,
# it will avoid performance problems caused by the interaction
# between delayed ack and the Nagle algorithm.
if isinstance(message_body, bytes):
msg += message_body
message_body = None
self.send(msg)
if self._expect_header_set:
# This is our custom behavior. If the Expect header was
# set, it will trigger this custom behavior.
logger.debug("Waiting for 100 Continue response.")
# Wait for 1 second for the server to send a response.
if urllib3.util.wait_for_read(self.sock, 1):
self._handle_expect_response(message_body)
return
else:
# From the RFC:
# Because of the presence of older implementations, the
# protocol allows ambiguous situations in which a client may
# send "Expect: 100-continue" without receiving either a 417
# (Expectation Failed) status or a 100 (Continue) status.
# Therefore, when a client sends this header field to an origin
# server (possibly via a proxy) from which it has never seen a
# 100 (Continue) status, the client SHOULD NOT wait for an
# indefinite period before sending the request body.
logger.debug("No response seen from server, continuing to "
"send the response body.")
if message_body is not None:
# message_body was not a string (i.e. it is a file), and
# we must run the risk of Nagle.
self.send(message_body)
def _consume_headers(self, fp):
# Most servers (including S3) will just return
# the CLRF after the 100 continue response. However,
# some servers (I've specifically seen this for squid when
# used as a straight HTTP proxy) will also inject a
# Connection: keep-alive header. To account for this
# we'll read until we read '\r\n', and ignore any headers
# that come immediately after the 100 continue response.
current = None
while current != b'\r\n':
current = fp.readline()
def _handle_expect_response(self, message_body):
# This is called when we sent the request headers containing
# an Expect: 100-continue header and received a response.
# We now need to figure out what to do.
fp = self.sock.makefile('rb', 0)
try:
maybe_status_line = fp.readline()
parts = maybe_status_line.split(None, 2)
if self._is_100_continue_status(maybe_status_line):
self._consume_headers(fp)
logger.debug("100 Continue response seen, "
"now sending request body.")
self._send_message_body(message_body)
elif len(parts) == 3 and parts[0].startswith(b'HTTP/'):
# From the RFC:
# Requirements for HTTP/1.1 origin servers:
#
# - Upon receiving a request which includes an Expect
# request-header field with the "100-continue"
# expectation, an origin server MUST either respond with
# 100 (Continue) status and continue to read from the
# input stream, or respond with a final status code.
#
# So if we don't get a 100 Continue response, then
# whatever the server has sent back is the final response
# and don't send the message_body.
logger.debug("Received a non 100 Continue response "
"from the server, NOT sending request body.")
status_tuple = (parts[0].decode('ascii'),
int(parts[1]), parts[2].decode('ascii'))
response_class = functools.partial(
AWSHTTPResponse, status_tuple=status_tuple)
self.response_class = response_class
self._response_received = True
finally:
fp.close()
def _send_message_body(self, message_body):
if message_body is not None:
self.send(message_body)
def send(self, str):
if self._response_received:
logger.debug("send() called, but reseponse already received. "
"Not sending data.")
return
return super(AWSConnection, self).send(str)
def _is_100_continue_status(self, maybe_status_line):
parts = maybe_status_line.split(None, 2)
# Check for HTTP/<version> 100 Continue\r\n
return (
len(parts) >= 3 and parts[0].startswith(b'HTTP/') and
parts[1] == b'100')
class AWSHTTPConnection(AWSConnection, HTTPConnection):
""" An HTTPConnection that supports 100 Continue behavior. """
class AWSHTTPSConnection(AWSConnection, VerifiedHTTPSConnection):
""" An HTTPSConnection that supports 100 Continue behavior. """
class AWSHTTPConnectionPool(HTTPConnectionPool):
ConnectionCls = AWSHTTPConnection
class AWSHTTPSConnectionPool(HTTPSConnectionPool):
ConnectionCls = AWSHTTPSConnection
def prepare_request_dict(request_dict, endpoint_url, context=None,
user_agent=None):
"""
This method prepares a request dict to be created into an
AWSRequestObject. This prepares the request dict by adding the
url and the user agent to the request dict.
:type request_dict: dict
:param request_dict: The request dict (created from the
``serialize`` module).
:type user_agent: string
:param user_agent: The user agent to use for this request.
:type endpoint_url: string
:param endpoint_url: The full endpoint url, which contains at least
the scheme, the hostname, and optionally any path components.
"""
r = request_dict
if user_agent is not None:
headers = r['headers']
headers['User-Agent'] = user_agent
host_prefix = r.get('host_prefix')
url = _urljoin(endpoint_url, r['url_path'], host_prefix)
if r['query_string']:
# NOTE: This is to avoid circular import with utils. This is being
# done to avoid moving classes to different modules as to not cause
# breaking chainges.
percent_encode_sequence = botocore.utils.percent_encode_sequence
encoded_query_string = percent_encode_sequence(r['query_string'])
if '?' not in url:
url += '?%s' % encoded_query_string
else:
url += '&%s' % encoded_query_string
r['url'] = url
r['context'] = context
if context is None:
r['context'] = {}
def create_request_object(request_dict):
"""
This method takes a request dict and creates an AWSRequest object
from it.
:type request_dict: dict
:param request_dict: The request dict (created from the
``prepare_request_dict`` method).
:rtype: ``botocore.awsrequest.AWSRequest``
:return: An AWSRequest object based on the request_dict.
"""
r = request_dict
request_object = AWSRequest(
method=r['method'], url=r['url'], data=r['body'], headers=r['headers'])
request_object.context = r['context']
return request_object
def _urljoin(endpoint_url, url_path, host_prefix):
p = urlsplit(endpoint_url)
# <part> - <index>
# scheme - p[0]
# netloc - p[1]
# path - p[2]
# query - p[3]
# fragment - p[4]
if not url_path or url_path == '/':
# If there's no path component, ensure the URL ends with
# a '/' for backwards compatibility.
if not p[2]:
new_path = '/'
else:
new_path = p[2]
elif p[2].endswith('/') and url_path.startswith('/'):
new_path = p[2][:-1] + url_path
else:
new_path = p[2] + url_path
new_netloc = p[1]
if host_prefix is not None:
new_netloc = host_prefix + new_netloc
reconstructed = urlunsplit((p[0], new_netloc, new_path, p[3], p[4]))
return reconstructed
class AWSRequestPreparer(object):
"""
This class performs preparation on AWSRequest objects similar to that of
the PreparedRequest class does in the requests library. However, the logic
has been boiled down to meet the specific use cases in botocore. Of note
there are the following differences:
This class does not heavily prepare the URL. Requests performed many
validations and corrections to ensure the URL is properly formatted.
Botocore either performs these validations elsewhere or otherwise
consistently provides well formatted URLs.
This class does not heavily prepare the body. Body preperation is
simple and supports only the cases that we document: bytes and
file-like objects to determine the content-length. This will also
additionally prepare a body that is a dict to be url encoded params
string as some signers rely on this. Finally, this class does not
support multipart file uploads.
This class does not prepare the method, auth or cookies.
"""
def prepare(self, original):
method = original.method
url = self._prepare_url(original)
body = self._prepare_body(original)
headers = self._prepare_headers(original, body)
stream_output = original.stream_output
return AWSPreparedRequest(method, url, headers, body, stream_output)
def _prepare_url(self, original):
url = original.url
if original.params:
params = urlencode(list(original.params.items()), doseq=True)
url = '%s?%s' % (url, params)
return url
def _prepare_headers(self, original, prepared_body=None):
headers = HeadersDict(original.headers.items())
# If the transfer encoding or content length is already set, use that
if 'Transfer-Encoding' in headers or 'Content-Length' in headers:
return headers
# Ensure we set the content length when it is expected
if original.method not in ('GET', 'HEAD', 'OPTIONS'):
length = self._determine_content_length(prepared_body)
if length is not None:
headers['Content-Length'] = str(length)
else:
# Failed to determine content length, using chunked
# NOTE: This shouldn't ever happen in practice
body_type = type(prepared_body)
logger.debug('Failed to determine length of %s', body_type)
headers['Transfer-Encoding'] = 'chunked'
return headers
def _to_utf8(self, item):
key, value = item
if isinstance(key, six.text_type):
key = key.encode('utf-8')
if isinstance(value, six.text_type):
value = value.encode('utf-8')
return key, value
def _prepare_body(self, original):
"""Prepares the given HTTP body data."""
body = original.data
if body == b'':
body = None
if isinstance(body, dict):
params = [self._to_utf8(item) for item in body.items()]
body = urlencode(params, doseq=True)
return body
def _determine_content_length(self, body):
# No body, content length of 0
if not body:
return 0
# Try asking the body for it's length
try:
return len(body)
except (AttributeError, TypeError) as e:
pass
# Try getting the length from a seekable stream
if hasattr(body, 'seek') and hasattr(body, 'tell'):
orig_pos = body.tell()
body.seek(0, 2)
end_file_pos = body.tell()
body.seek(orig_pos)
return end_file_pos - orig_pos
# Failed to determine the length
return None
class AWSRequest(object):
"""Represents the elements of an HTTP request.
This class was originally inspired by requests.models.Request, but has been
boiled down to meet the specific use cases in botocore. That being said this
class (even in requests) is effectively a named-tuple.
"""
_REQUEST_PREPARER_CLS = AWSRequestPreparer
def __init__(self,
method=None,
url=None,
headers=None,
data=None,
params=None,
auth_path=None,
stream_output=False):
self._request_preparer = self._REQUEST_PREPARER_CLS()
# Default empty dicts for dict params.
params = {} if params is None else params
self.method = method
self.url = url
self.headers = HTTPHeaders()
self.data = data
self.params = params
self.auth_path = auth_path
self.stream_output = stream_output
if headers is not None:
for key, value in headers.items():
self.headers[key] = value
# This is a dictionary to hold information that is used when
# processing the request. What is inside of ``context`` is open-ended.
# For example, it may have a timestamp key that is used for holding
# what the timestamp is when signing the request. Note that none
# of the information that is inside of ``context`` is directly
# sent over the wire; the information is only used to assist in
# creating what is sent over the wire.
self.context = {}
def prepare(self):
"""Constructs a :class:`AWSPreparedRequest <AWSPreparedRequest>`."""
return self._request_preparer.prepare(self)
@property
def body(self):
body = self.prepare().body
if isinstance(body, six.text_type):
body = body.encode('utf-8')
return body
class AWSPreparedRequest(object):
"""A data class representing a finalized request to be sent over the wire.
Requests at this stage should be treated as final, and the properties of
the request should not be modified.
:ivar method: The HTTP Method
:ivar url: The full url
:ivar headers: The HTTP headers to send.
:ivar body: The HTTP body.
:ivar stream_output: If the response for this request should be streamed.
"""
def __init__(self, method, url, headers, body, stream_output):
self.method = method
self.url = url
self.headers = headers
self.body = body
self.stream_output = stream_output
def __repr__(self):
fmt = (
'<AWSPreparedRequest stream_output=%s, method=%s, url=%s, '
'headers=%s>'
)
return fmt % (self.stream_output, self.method, self.url, self.headers)
def reset_stream(self):
"""Resets the streaming body to it's initial position.
If the request contains a streaming body (a streamable file-like object)
seek to the object's initial position to ensure the entire contents of
the object is sent. This is a no-op for static bytes-like body types.
"""
# Trying to reset a stream when there is a no stream will
# just immediately return. It's not an error, it will produce
# the same result as if we had actually reset the stream (we'll send
# the entire body contents again if we need to).
# Same case if the body is a string/bytes/bytearray type.
non_seekable_types = (six.binary_type, six.text_type, bytearray)
if self.body is None or isinstance(self.body, non_seekable_types):
return
try:
logger.debug("Rewinding stream: %s", self.body)
self.body.seek(0)
except Exception as e:
logger.debug("Unable to rewind stream: %s", e)
raise UnseekableStreamError(stream_object=self.body)
class AWSResponse(object):
"""A data class representing an HTTP response.
This class was originally inspired by requests.models.Response, but has
been boiled down to meet the specific use cases in botocore. This has
effectively been reduced to a named tuple.
:ivar url: The full url.
:ivar status_code: The status code of the HTTP response.
:ivar headers: The HTTP headers received.
:ivar body: The HTTP response body.
"""
def __init__(self, url, status_code, headers, raw):
self.url = url
self.status_code = status_code
self.headers = HeadersDict(headers)
self.raw = raw
self._content = None
@property
def content(self):
"""Content of the response as bytes."""
if self._content is None:
# Read the contents.
# NOTE: requests would attempt to call stream and fall back
# to a custom generator that would call read in a loop, but
# we don't rely on this behavior
self._content = bytes().join(self.raw.stream()) or bytes()
return self._content
@property
def text(self):
"""Content of the response as a proper text type.
Uses the encoding type provided in the reponse headers to decode the
response content into a proper text type. If the encoding is not
present in the headers, UTF-8 is used as a default.
"""
encoding = botocore.utils.get_encoding_from_headers(self.headers)
if encoding:
return self.content.decode(encoding)
else:
return self.content.decode('utf-8')
class _HeaderKey(object):
def __init__(self, key):
self._key = key
self._lower = key.lower()
def __hash__(self):
return hash(self._lower)
def __eq__(self, other):
return isinstance(other, _HeaderKey) and self._lower == other._lower
def __str__(self):
return self._key
def __repr__(self):
return repr(self._key)
class HeadersDict(collections.MutableMapping):
"""A case-insenseitive dictionary to represent HTTP headers. """
def __init__(self, *args, **kwargs):
self._dict = {}
self.update(*args, **kwargs)
def __setitem__(self, key, value):
self._dict[_HeaderKey(key)] = value
def __getitem__(self, key):
return self._dict[_HeaderKey(key)]
def __delitem__(self, key):
del self._dict[_HeaderKey(key)]
def __iter__(self):
return (str(key) for key in self._dict)
def __len__(self):
return len(self._dict)
def __repr__(self):
return repr(self._dict)
def copy(self):
return HeadersDict(self.items())

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,928 @@
# Copyright 2014 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.
import logging
import functools
from botocore import waiter, xform_name
from botocore.auth import AUTH_TYPE_MAPS
from botocore.awsrequest import prepare_request_dict
from botocore.docs.docstring import ClientMethodDocstring
from botocore.docs.docstring import PaginatorDocstring
from botocore.exceptions import ClientError, DataNotFoundError
from botocore.exceptions import OperationNotPageableError
from botocore.exceptions import UnknownSignatureVersionError
from botocore.hooks import first_non_none_response
from botocore.model import ServiceModel
from botocore.paginate import Paginator
from botocore.utils import CachedProperty
from botocore.utils import get_service_module_name
from botocore.utils import switch_host_s3_accelerate
from botocore.utils import S3RegionRedirector
from botocore.utils import fix_s3_host
from botocore.utils import switch_to_virtual_host_style
from botocore.utils import S3_ACCELERATE_WHITELIST
from botocore.args import ClientArgsCreator
from botocore.compat import urlsplit
from botocore import UNSIGNED
# Keep this imported. There's pre-existing code that uses
# "from botocore.client import Config".
from botocore.config import Config
from botocore.history import get_global_history_recorder
from botocore.discovery import (
EndpointDiscoveryHandler, EndpointDiscoveryManager,
block_endpoint_discovery_required_operations
)
logger = logging.getLogger(__name__)
history_recorder = get_global_history_recorder()
class ClientCreator(object):
"""Creates client objects for a service."""
def __init__(self, loader, endpoint_resolver, user_agent, event_emitter,
retry_handler_factory, retry_config_translator,
response_parser_factory=None, exceptions_factory=None,
config_store=None):
self._loader = loader
self._endpoint_resolver = endpoint_resolver
self._user_agent = user_agent
self._event_emitter = event_emitter
self._retry_handler_factory = retry_handler_factory
self._retry_config_translator = retry_config_translator
self._response_parser_factory = response_parser_factory
self._exceptions_factory = exceptions_factory
# TODO: Migrate things away from scoped_config in favor of the
# config_store. The config store can pull things from both the scoped
# config and environment variables (and potentially more in the
# future).
self._config_store = config_store
def create_client(self, service_name, region_name, is_secure=True,
endpoint_url=None, verify=None,
credentials=None, scoped_config=None,
api_version=None,
client_config=None):
responses = self._event_emitter.emit(
'choose-service-name', service_name=service_name)
service_name = first_non_none_response(responses, default=service_name)
service_model = self._load_service_model(service_name, api_version)
cls = self._create_client_class(service_name, service_model)
endpoint_bridge = ClientEndpointBridge(
self._endpoint_resolver, scoped_config, client_config,
service_signing_name=service_model.metadata.get('signingName'))
client_args = self._get_client_args(
service_model, region_name, is_secure, endpoint_url,
verify, credentials, scoped_config, client_config, endpoint_bridge)
service_client = cls(**client_args)
self._register_retries(service_client)
self._register_s3_events(
service_client, endpoint_bridge, endpoint_url, client_config,
scoped_config)
self._register_endpoint_discovery(
service_client, endpoint_url, client_config
)
return service_client
def create_client_class(self, service_name, api_version=None):
service_model = self._load_service_model(service_name, api_version)
return self._create_client_class(service_name, service_model)
def _create_client_class(self, service_name, service_model):
class_attributes = self._create_methods(service_model)
py_name_to_operation_name = self._create_name_mapping(service_model)
class_attributes['_PY_TO_OP_NAME'] = py_name_to_operation_name
bases = [BaseClient]
service_id = service_model.service_id.hyphenize()
self._event_emitter.emit(
'creating-client-class.%s' % service_id,
class_attributes=class_attributes,
base_classes=bases)
class_name = get_service_module_name(service_model)
cls = type(str(class_name), tuple(bases), class_attributes)
return cls
def _load_service_model(self, service_name, api_version=None):
json_model = self._loader.load_service_model(service_name, 'service-2',
api_version=api_version)
service_model = ServiceModel(json_model, service_name=service_name)
return service_model
def _register_retries(self, client):
endpoint_prefix = client.meta.service_model.endpoint_prefix
service_id = client.meta.service_model.service_id
service_event_name = service_id.hyphenize()
# First, we load the entire retry config for all services,
# then pull out just the information we need.
original_config = self._loader.load_data('_retry')
if not original_config:
return
retry_config = self._retry_config_translator.build_retry_config(
endpoint_prefix, original_config.get('retry', {}),
original_config.get('definitions', {}),
client.meta.config.retries
)
logger.debug("Registering retry handlers for service: %s",
client.meta.service_model.service_name)
handler = self._retry_handler_factory.create_retry_handler(
retry_config, endpoint_prefix)
unique_id = 'retry-config-%s' % service_event_name
client.meta.events.register(
'needs-retry.%s' % service_event_name, handler,
unique_id=unique_id
)
def _register_endpoint_discovery(self, client, endpoint_url, config):
if endpoint_url is not None:
# Don't register any handlers in the case of a custom endpoint url
return
# Only attach handlers if the service supports discovery
if client.meta.service_model.endpoint_discovery_operation is None:
return
events = client.meta.events
service_id = client.meta.service_model.service_id.hyphenize()
enabled = False
if config and config.endpoint_discovery_enabled is not None:
enabled = config.endpoint_discovery_enabled
elif self._config_store:
enabled = self._config_store.get_config_variable(
'endpoint_discovery_enabled')
if enabled:
manager = EndpointDiscoveryManager(client)
handler = EndpointDiscoveryHandler(manager)
handler.register(events, service_id)
else:
events.register('before-parameter-build',
block_endpoint_discovery_required_operations)
def _register_s3_events(self, client, endpoint_bridge, endpoint_url,
client_config, scoped_config):
if client.meta.service_model.service_name != 's3':
return
S3RegionRedirector(endpoint_bridge, client).register()
self._set_s3_addressing_style(
endpoint_url, client.meta.config.s3, client.meta.events,
client.meta.partition
)
# Enable accelerate if the configuration is set to to true or the
# endpoint being used matches one of the accelerate endpoints.
if self._is_s3_accelerate(endpoint_url, client.meta.config.s3):
# Also make sure that the hostname gets switched to
# s3-accelerate.amazonaws.com
client.meta.events.register_first(
'before-sign.s3', switch_host_s3_accelerate)
self._set_s3_presign_signature_version(
client.meta, client_config, scoped_config)
def _set_s3_addressing_style(self, endpoint_url, s3_config, event_emitter,
partition):
if s3_config is None:
s3_config = {}
addressing_style = self._get_s3_addressing_style(
endpoint_url, s3_config)
handler = self._get_s3_addressing_handler(
endpoint_url, s3_config, addressing_style, partition)
if handler is not None:
event_emitter.register('before-sign.s3', handler)
def _get_s3_addressing_style(self, endpoint_url, s3_config):
# Use virtual host style addressing if accelerate is enabled or if
# the given endpoint url is an accelerate endpoint.
accelerate = s3_config.get('use_accelerate_endpoint', False)
if accelerate or self._is_s3_accelerate(endpoint_url, s3_config):
return 'virtual'
# If a particular addressing style is configured, use it.
configured_addressing_style = s3_config.get('addressing_style')
if configured_addressing_style:
return configured_addressing_style
def _get_s3_addressing_handler(self, endpoint_url, s3_config,
addressing_style, partition):
# If virtual host style was configured, use it regardless of whether
# or not the bucket looks dns compatible.
if addressing_style == 'virtual':
logger.debug("Using S3 virtual host style addressing.")
return switch_to_virtual_host_style
# If path style is configured, no additional steps are needed. If
# endpoint_url was specified, don't default to virtual. We could
# potentially default provided endpoint urls to virtual hosted
# style, but for now it is avoided.
if addressing_style == 'path' or endpoint_url is not None:
logger.debug("Using S3 path style addressing.")
return None
logger.debug("Defaulting to S3 virtual host style addressing with "
"path style addressing fallback.")
# By default, try to use virtual style with path fallback.
return fix_s3_host
def _is_s3_accelerate(self, endpoint_url, s3_config):
# Accelerate has been explicitly configured.
if s3_config is not None and s3_config.get('use_accelerate_endpoint'):
return True
# Accelerate mode is turned on automatically if an endpoint url is
# provided that matches the accelerate scheme.
if endpoint_url is None:
return False
# Accelerate is only valid for Amazon endpoints.
netloc = urlsplit(endpoint_url).netloc
if not netloc.endswith('amazonaws.com'):
return False
# The first part of the url should always be s3-accelerate.
parts = netloc.split('.')
if parts[0] != 's3-accelerate':
return False
# Url parts between 's3-accelerate' and 'amazonaws.com' which
# represent different url features.
feature_parts = parts[1:-2]
# There should be no duplicate url parts.
if len(feature_parts) != len(set(feature_parts)):
return False
# Remaining parts must all be in the whitelist.
return all(p in S3_ACCELERATE_WHITELIST for p in feature_parts)
def _set_s3_presign_signature_version(self, client_meta,
client_config, scoped_config):
# This will return the manually configured signature version, or None
# if none was manually set. If a customer manually sets the signature
# version, we always want to use what they set.
provided_signature_version = _get_configured_signature_version(
's3', client_config, scoped_config)
if provided_signature_version is not None:
return
# Check to see if the region is a region that we know about. If we
# don't know about a region, then we can safely assume it's a new
# region that is sigv4 only, since all new S3 regions only allow sigv4.
regions = self._endpoint_resolver.get_available_endpoints(
's3', client_meta.partition)
if client_meta.region_name not in regions:
return
# If it is a region we know about, we want to default to sigv2, so here
# we check to see if it is available.
endpoint = self._endpoint_resolver.construct_endpoint(
's3', client_meta.region_name)
signature_versions = endpoint['signatureVersions']
if 's3' not in signature_versions:
return
# We now know that we're in a known region that supports sigv2 and
# the customer hasn't set a signature version so we default the
# signature version to sigv2.
client_meta.events.register(
'choose-signer.s3', self._default_s3_presign_to_sigv2)
def _default_s3_presign_to_sigv2(self, signature_version, **kwargs):
"""
Returns the 's3' (sigv2) signer if presigning an s3 request. This is
intended to be used to set the default signature version for the signer
to sigv2.
:type signature_version: str
:param signature_version: The current client signature version.
:type signing_name: str
:param signing_name: The signing name of the service.
:return: 's3' if the request is an s3 presign request, None otherwise
"""
for suffix in ['-query', '-presign-post']:
if signature_version.endswith(suffix):
return 's3' + suffix
def _get_client_args(self, service_model, region_name, is_secure,
endpoint_url, verify, credentials,
scoped_config, client_config, endpoint_bridge):
args_creator = ClientArgsCreator(
self._event_emitter, self._user_agent,
self._response_parser_factory, self._loader,
self._exceptions_factory, config_store=self._config_store)
return args_creator.get_client_args(
service_model, region_name, is_secure, endpoint_url,
verify, credentials, scoped_config, client_config, endpoint_bridge)
def _create_methods(self, service_model):
op_dict = {}
for operation_name in service_model.operation_names:
py_operation_name = xform_name(operation_name)
op_dict[py_operation_name] = self._create_api_method(
py_operation_name, operation_name, service_model)
return op_dict
def _create_name_mapping(self, service_model):
# py_name -> OperationName, for every operation available
# for a service.
mapping = {}
for operation_name in service_model.operation_names:
py_operation_name = xform_name(operation_name)
mapping[py_operation_name] = operation_name
return mapping
def _create_api_method(self, py_operation_name, operation_name,
service_model):
def _api_call(self, *args, **kwargs):
# We're accepting *args so that we can give a more helpful
# error message than TypeError: _api_call takes exactly
# 1 argument.
if args:
raise TypeError(
"%s() only accepts keyword arguments." % py_operation_name)
# The "self" in this scope is referring to the BaseClient.
return self._make_api_call(operation_name, kwargs)
_api_call.__name__ = str(py_operation_name)
# Add the docstring to the client method
operation_model = service_model.operation_model(operation_name)
docstring = ClientMethodDocstring(
operation_model=operation_model,
method_name=operation_name,
event_emitter=self._event_emitter,
method_description=operation_model.documentation,
example_prefix='response = client.%s' % py_operation_name,
include_signature=False
)
_api_call.__doc__ = docstring
return _api_call
class ClientEndpointBridge(object):
"""Bridges endpoint data and client creation
This class handles taking out the relevant arguments from the endpoint
resolver and determining which values to use, taking into account any
client configuration options and scope configuration options.
This class also handles determining what, if any, region to use if no
explicit region setting is provided. For example, Amazon S3 client will
utilize "us-east-1" by default if no region can be resolved."""
DEFAULT_ENDPOINT = '{service}.{region}.amazonaws.com'
_DUALSTACK_ENABLED_SERVICES = ['s3', 's3-control']
def __init__(self, endpoint_resolver, scoped_config=None,
client_config=None, default_endpoint=None,
service_signing_name=None):
self.service_signing_name = service_signing_name
self.endpoint_resolver = endpoint_resolver
self.scoped_config = scoped_config
self.client_config = client_config
self.default_endpoint = default_endpoint or self.DEFAULT_ENDPOINT
def resolve(self, service_name, region_name=None, endpoint_url=None,
is_secure=True):
region_name = self._check_default_region(service_name, region_name)
resolved = self.endpoint_resolver.construct_endpoint(
service_name, region_name)
if resolved:
return self._create_endpoint(
resolved, service_name, region_name, endpoint_url, is_secure)
else:
return self._assume_endpoint(service_name, region_name,
endpoint_url, is_secure)
def _check_default_region(self, service_name, region_name):
if region_name is not None:
return region_name
# Use the client_config region if no explicit region was provided.
if self.client_config and self.client_config.region_name is not None:
return self.client_config.region_name
def _create_endpoint(self, resolved, service_name, region_name,
endpoint_url, is_secure):
region_name, signing_region = self._pick_region_values(
resolved, region_name, endpoint_url)
if endpoint_url is None:
if self._is_s3_dualstack_mode(service_name):
endpoint_url = self._create_dualstack_endpoint(
service_name, region_name,
resolved['dnsSuffix'], is_secure)
else:
# Use the sslCommonName over the hostname for Python 2.6 compat.
hostname = resolved.get('sslCommonName', resolved.get('hostname'))
endpoint_url = self._make_url(hostname, is_secure,
resolved.get('protocols', []))
signature_version = self._resolve_signature_version(
service_name, resolved)
signing_name = self._resolve_signing_name(service_name, resolved)
return self._create_result(
service_name=service_name, region_name=region_name,
signing_region=signing_region, signing_name=signing_name,
endpoint_url=endpoint_url, metadata=resolved,
signature_version=signature_version)
def _is_s3_dualstack_mode(self, service_name):
if service_name not in self._DUALSTACK_ENABLED_SERVICES:
return False
# TODO: This normalization logic is duplicated from the
# ClientArgsCreator class. Consolidate everything to
# ClientArgsCreator. _resolve_signature_version also has similarly
# duplicated logic.
client_config = self.client_config
if client_config is not None and client_config.s3 is not None and \
'use_dualstack_endpoint' in client_config.s3:
# Client config trumps scoped config.
return client_config.s3['use_dualstack_endpoint']
if self.scoped_config is None:
return False
enabled = self.scoped_config.get('s3', {}).get(
'use_dualstack_endpoint', False)
if enabled in [True, 'True', 'true']:
return True
return False
def _create_dualstack_endpoint(self, service_name, region_name,
dns_suffix, is_secure):
hostname = '{service}.dualstack.{region}.{dns_suffix}'.format(
service=service_name, region=region_name,
dns_suffix=dns_suffix)
# Dualstack supports http and https so were hardcoding this value for
# now. This can potentially move into the endpoints.json file.
return self._make_url(hostname, is_secure, ['http', 'https'])
def _assume_endpoint(self, service_name, region_name, endpoint_url,
is_secure):
if endpoint_url is None:
# Expand the default hostname URI template.
hostname = self.default_endpoint.format(
service=service_name, region=region_name)
endpoint_url = self._make_url(hostname, is_secure,
['http', 'https'])
logger.debug('Assuming an endpoint for %s, %s: %s',
service_name, region_name, endpoint_url)
# We still want to allow the user to provide an explicit version.
signature_version = self._resolve_signature_version(
service_name, {'signatureVersions': ['v4']})
signing_name = self._resolve_signing_name(service_name, resolved={})
return self._create_result(
service_name=service_name, region_name=region_name,
signing_region=region_name, signing_name=signing_name,
signature_version=signature_version, endpoint_url=endpoint_url,
metadata={})
def _create_result(self, service_name, region_name, signing_region,
signing_name, endpoint_url, signature_version,
metadata):
return {
'service_name': service_name,
'region_name': region_name,
'signing_region': signing_region,
'signing_name': signing_name,
'endpoint_url': endpoint_url,
'signature_version': signature_version,
'metadata': metadata
}
def _make_url(self, hostname, is_secure, supported_protocols):
if is_secure and 'https' in supported_protocols:
scheme = 'https'
else:
scheme = 'http'
return '%s://%s' % (scheme, hostname)
def _resolve_signing_name(self, service_name, resolved):
# CredentialScope overrides everything else.
if 'credentialScope' in resolved \
and 'service' in resolved['credentialScope']:
return resolved['credentialScope']['service']
# Use the signingName from the model if present.
if self.service_signing_name:
return self.service_signing_name
# Just assume is the same as the service name.
return service_name
def _pick_region_values(self, resolved, region_name, endpoint_url):
signing_region = region_name
if endpoint_url is None:
# Do not use the region name or signing name from the resolved
# endpoint if the user explicitly provides an endpoint_url. This
# would happen if we resolve to an endpoint where the service has
# a "defaults" section that overrides all endpoint with a single
# hostname and credentialScope. This has been the case historically
# for how STS has worked. The only way to resolve an STS endpoint
# was to provide a region_name and an endpoint_url. In that case,
# we would still resolve an endpoint, but we would not use the
# resolved endpointName or signingRegion because we want to allow
# custom endpoints.
region_name = resolved['endpointName']
signing_region = region_name
if 'credentialScope' in resolved \
and 'region' in resolved['credentialScope']:
signing_region = resolved['credentialScope']['region']
return region_name, signing_region
def _resolve_signature_version(self, service_name, resolved):
configured_version = _get_configured_signature_version(
service_name, self.client_config, self.scoped_config)
if configured_version is not None:
return configured_version
# Pick a signature version from the endpoint metadata if present.
if 'signatureVersions' in resolved:
potential_versions = resolved['signatureVersions']
if service_name == 's3':
return 's3v4'
if 'v4' in potential_versions:
return 'v4'
# Now just iterate over the signature versions in order until we
# find the first one that is known to Botocore.
for known in potential_versions:
if known in AUTH_TYPE_MAPS:
return known
raise UnknownSignatureVersionError(
signature_version=resolved.get('signatureVersions'))
class BaseClient(object):
# This is actually reassigned with the py->op_name mapping
# when the client creator creates the subclass. This value is used
# because calls such as client.get_paginator('list_objects') use the
# snake_case name, but we need to know the ListObjects form.
# xform_name() does the ListObjects->list_objects conversion, but
# we need the reverse mapping here.
_PY_TO_OP_NAME = {}
def __init__(self, serializer, endpoint, response_parser,
event_emitter, request_signer, service_model, loader,
client_config, partition, exceptions_factory):
self._serializer = serializer
self._endpoint = endpoint
self._response_parser = response_parser
self._request_signer = request_signer
self._cache = {}
self._loader = loader
self._client_config = client_config
self.meta = ClientMeta(event_emitter, self._client_config,
endpoint.host, service_model,
self._PY_TO_OP_NAME, partition)
self._exceptions_factory = exceptions_factory
self._exceptions = None
self._register_handlers()
def __getattr__(self, item):
event_name = 'getattr.%s.%s' % (
self._service_model.service_id.hyphenize(), item
)
handler, event_response = self.meta.events.emit_until_response(
event_name, client=self)
if event_response is not None:
return event_response
raise AttributeError(
"'%s' object has no attribute '%s'" % (
self.__class__.__name__, item)
)
def _register_handlers(self):
# Register the handler required to sign requests.
service_id = self.meta.service_model.service_id.hyphenize()
self.meta.events.register(
'request-created.%s' % service_id,
self._request_signer.handler
)
@property
def _service_model(self):
return self.meta.service_model
def _make_api_call(self, operation_name, api_params):
operation_model = self._service_model.operation_model(operation_name)
service_name = self._service_model.service_name
history_recorder.record('API_CALL', {
'service': service_name,
'operation': operation_name,
'params': api_params,
})
if operation_model.deprecated:
logger.debug('Warning: %s.%s() is deprecated',
service_name, operation_name)
request_context = {
'client_region': self.meta.region_name,
'client_config': self.meta.config,
'has_streaming_input': operation_model.has_streaming_input,
'auth_type': operation_model.auth_type,
}
request_dict = self._convert_to_request_dict(
api_params, operation_model, context=request_context)
service_id = self._service_model.service_id.hyphenize()
handler, event_response = self.meta.events.emit_until_response(
'before-call.{service_id}.{operation_name}'.format(
service_id=service_id,
operation_name=operation_name),
model=operation_model, params=request_dict,
request_signer=self._request_signer, context=request_context)
if event_response is not None:
http, parsed_response = event_response
else:
http, parsed_response = self._make_request(
operation_model, request_dict, request_context)
self.meta.events.emit(
'after-call.{service_id}.{operation_name}'.format(
service_id=service_id,
operation_name=operation_name),
http_response=http, parsed=parsed_response,
model=operation_model, context=request_context
)
if http.status_code >= 300:
error_code = parsed_response.get("Error", {}).get("Code")
error_class = self.exceptions.from_code(error_code)
raise error_class(parsed_response, operation_name)
else:
return parsed_response
def _make_request(self, operation_model, request_dict, request_context):
try:
return self._endpoint.make_request(operation_model, request_dict)
except Exception as e:
self.meta.events.emit(
'after-call-error.{service_id}.{operation_name}'.format(
service_id=self._service_model.service_id.hyphenize(),
operation_name=operation_model.name),
exception=e, context=request_context
)
raise
def _convert_to_request_dict(self, api_params, operation_model,
context=None):
api_params = self._emit_api_params(
api_params, operation_model, context)
request_dict = self._serializer.serialize_to_request(
api_params, operation_model)
if not self._client_config.inject_host_prefix:
request_dict.pop('host_prefix', None)
prepare_request_dict(request_dict, endpoint_url=self._endpoint.host,
user_agent=self._client_config.user_agent,
context=context)
return request_dict
def _emit_api_params(self, api_params, operation_model, context):
# Given the API params provided by the user and the operation_model
# we can serialize the request to a request_dict.
operation_name = operation_model.name
# Emit an event that allows users to modify the parameters at the
# beginning of the method. It allows handlers to modify existing
# parameters or return a new set of parameters to use.
service_id = self._service_model.service_id.hyphenize()
responses = self.meta.events.emit(
'provide-client-params.{service_id}.{operation_name}'.format(
service_id=service_id,
operation_name=operation_name),
params=api_params, model=operation_model, context=context)
api_params = first_non_none_response(responses, default=api_params)
event_name = (
'before-parameter-build.{service_id}.{operation_name}')
self.meta.events.emit(
event_name.format(
service_id=service_id,
operation_name=operation_name),
params=api_params, model=operation_model, context=context)
return api_params
def get_paginator(self, operation_name):
"""Create a paginator for an operation.
:type operation_name: string
:param operation_name: The operation name. This is the same name
as the method name on the client. For example, if the
method name is ``create_foo``, and you'd normally invoke the
operation as ``client.create_foo(**kwargs)``, if the
``create_foo`` operation can be paginated, you can use the
call ``client.get_paginator("create_foo")``.
:raise OperationNotPageableError: Raised if the operation is not
pageable. You can use the ``client.can_paginate`` method to
check if an operation is pageable.
:rtype: L{botocore.paginate.Paginator}
:return: A paginator object.
"""
if not self.can_paginate(operation_name):
raise OperationNotPageableError(operation_name=operation_name)
else:
actual_operation_name = self._PY_TO_OP_NAME[operation_name]
# Create a new paginate method that will serve as a proxy to
# the underlying Paginator.paginate method. This is needed to
# attach a docstring to the method.
def paginate(self, **kwargs):
return Paginator.paginate(self, **kwargs)
paginator_config = self._cache['page_config'][
actual_operation_name]
# Add the docstring for the paginate method.
paginate.__doc__ = PaginatorDocstring(
paginator_name=actual_operation_name,
event_emitter=self.meta.events,
service_model=self.meta.service_model,
paginator_config=paginator_config,
include_signature=False
)
# Rename the paginator class based on the type of paginator.
paginator_class_name = str('%s.Paginator.%s' % (
get_service_module_name(self.meta.service_model),
actual_operation_name))
# Create the new paginator class
documented_paginator_cls = type(
paginator_class_name, (Paginator,), {'paginate': paginate})
operation_model = self._service_model.operation_model(actual_operation_name)
paginator = documented_paginator_cls(
getattr(self, operation_name),
paginator_config,
operation_model)
return paginator
def can_paginate(self, operation_name):
"""Check if an operation can be paginated.
:type operation_name: string
:param operation_name: The operation name. This is the same name
as the method name on the client. For example, if the
method name is ``create_foo``, and you'd normally invoke the
operation as ``client.create_foo(**kwargs)``, if the
``create_foo`` operation can be paginated, you can use the
call ``client.get_paginator("create_foo")``.
:return: ``True`` if the operation can be paginated,
``False`` otherwise.
"""
if 'page_config' not in self._cache:
try:
page_config = self._loader.load_service_model(
self._service_model.service_name,
'paginators-1',
self._service_model.api_version)['pagination']
self._cache['page_config'] = page_config
except DataNotFoundError:
self._cache['page_config'] = {}
actual_operation_name = self._PY_TO_OP_NAME[operation_name]
return actual_operation_name in self._cache['page_config']
def _get_waiter_config(self):
if 'waiter_config' not in self._cache:
try:
waiter_config = self._loader.load_service_model(
self._service_model.service_name,
'waiters-2',
self._service_model.api_version)
self._cache['waiter_config'] = waiter_config
except DataNotFoundError:
self._cache['waiter_config'] = {}
return self._cache['waiter_config']
def get_waiter(self, waiter_name):
"""Returns an object that can wait for some condition.
:type waiter_name: str
:param waiter_name: The name of the waiter to get. See the waiters
section of the service docs for a list of available waiters.
:returns: The specified waiter object.
:rtype: botocore.waiter.Waiter
"""
config = self._get_waiter_config()
if not config:
raise ValueError("Waiter does not exist: %s" % waiter_name)
model = waiter.WaiterModel(config)
mapping = {}
for name in model.waiter_names:
mapping[xform_name(name)] = name
if waiter_name not in mapping:
raise ValueError("Waiter does not exist: %s" % waiter_name)
return waiter.create_waiter_with_client(
mapping[waiter_name], model, self)
@CachedProperty
def waiter_names(self):
"""Returns a list of all available waiters."""
config = self._get_waiter_config()
if not config:
return []
model = waiter.WaiterModel(config)
# Waiter configs is a dict, we just want the waiter names
# which are the keys in the dict.
return [xform_name(name) for name in model.waiter_names]
@property
def exceptions(self):
if self._exceptions is None:
self._exceptions = self._load_exceptions()
return self._exceptions
def _load_exceptions(self):
return self._exceptions_factory.create_client_exceptions(
self._service_model)
class ClientMeta(object):
"""Holds additional client methods.
This class holds additional information for clients. It exists for
two reasons:
* To give advanced functionality to clients
* To namespace additional client attributes from the operation
names which are mapped to methods at runtime. This avoids
ever running into collisions with operation names.
"""
def __init__(self, events, client_config, endpoint_url, service_model,
method_to_api_mapping, partition):
self.events = events
self._client_config = client_config
self._endpoint_url = endpoint_url
self._service_model = service_model
self._method_to_api_mapping = method_to_api_mapping
self._partition = partition
@property
def service_model(self):
return self._service_model
@property
def region_name(self):
return self._client_config.region_name
@property
def endpoint_url(self):
return self._endpoint_url
@property
def config(self):
return self._client_config
@property
def method_to_api_mapping(self):
return self._method_to_api_mapping
@property
def partition(self):
return self._partition
def _get_configured_signature_version(service_name, client_config,
scoped_config):
"""
Gets the manually configured signature version.
:returns: the customer configured signature version, or None if no
signature version was configured.
"""
# Client config overrides everything.
if client_config and client_config.signature_version is not None:
return client_config.signature_version
# Scoped config overrides picking from the endpoint metadata.
if scoped_config is not None:
# A given service may have service specific configuration in the
# config file, so we need to check there as well.
service_config = scoped_config.get(service_name)
if service_config is not None and isinstance(service_config, dict):
version = service_config.get('signature_version')
if version:
logger.debug(
"Switching signature version for service %s "
"to version %s based on config file override.",
service_name, version)
return version
return None

View File

@@ -0,0 +1,379 @@
# Copyright 2012-2014 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.
import copy
import datetime
import sys
import inspect
import warnings
import hashlib
import logging
import shlex
from math import floor
from botocore.vendored import six
from botocore.exceptions import MD5UnavailableError
from urllib3 import exceptions
logger = logging.getLogger(__name__)
if six.PY3:
from botocore.vendored.six.moves import http_client
class HTTPHeaders(http_client.HTTPMessage):
pass
from urllib.parse import quote
from urllib.parse import urlencode
from urllib.parse import unquote
from urllib.parse import unquote_plus
from urllib.parse import urlparse
from urllib.parse import urlsplit
from urllib.parse import urlunsplit
from urllib.parse import urljoin
from urllib.parse import parse_qsl
from urllib.parse import parse_qs
from http.client import HTTPResponse
from io import IOBase as _IOBase
from base64 import encodebytes
from email.utils import formatdate
from itertools import zip_longest
file_type = _IOBase
zip = zip
# In python3, unquote takes a str() object, url decodes it,
# then takes the bytestring and decodes it to utf-8.
# Python2 we'll have to do this ourself (see below).
unquote_str = unquote_plus
def set_socket_timeout(http_response, timeout):
"""Set the timeout of the socket from an HTTPResponse.
:param http_response: An instance of ``httplib.HTTPResponse``
"""
http_response._fp.fp.raw._sock.settimeout(timeout)
def accepts_kwargs(func):
# In python3.4.1, there's backwards incompatible
# changes when using getargspec with functools.partials.
return inspect.getfullargspec(func)[2]
def ensure_unicode(s, encoding=None, errors=None):
# NOOP in Python 3, because every string is already unicode
return s
def ensure_bytes(s, encoding='utf-8', errors='strict'):
if isinstance(s, str):
return s.encode(encoding, errors)
if isinstance(s, bytes):
return s
raise ValueError("Expected str or bytes, received %s." % type(s))
else:
from urllib import quote
from urllib import urlencode
from urllib import unquote
from urllib import unquote_plus
from urlparse import urlparse
from urlparse import urlsplit
from urlparse import urlunsplit
from urlparse import urljoin
from urlparse import parse_qsl
from urlparse import parse_qs
from email.message import Message
from email.Utils import formatdate
file_type = file
from itertools import izip as zip
from itertools import izip_longest as zip_longest
from httplib import HTTPResponse
from base64 import encodestring as encodebytes
class HTTPHeaders(Message):
# The __iter__ method is not available in python2.x, so we have
# to port the py3 version.
def __iter__(self):
for field, value in self._headers:
yield field
def unquote_str(value, encoding='utf-8'):
# In python2, unquote() gives us a string back that has the urldecoded
# bits, but not the unicode parts. We need to decode this manually.
# unquote has special logic in which if it receives a unicode object it
# will decode it to latin1. This is hard coded. To avoid this, we'll
# encode the string with the passed in encoding before trying to
# unquote it.
byte_string = value.encode(encoding)
return unquote_plus(byte_string).decode(encoding)
def set_socket_timeout(http_response, timeout):
"""Set the timeout of the socket from an HTTPResponse.
:param http_response: An instance of ``httplib.HTTPResponse``
"""
http_response._fp.fp._sock.settimeout(timeout)
def accepts_kwargs(func):
return inspect.getargspec(func)[2]
def ensure_unicode(s, encoding='utf-8', errors='strict'):
if isinstance(s, six.text_type):
return s
return unicode(s, encoding, errors)
def ensure_bytes(s, encoding='utf-8', errors='strict'):
if isinstance(s, unicode):
return s.encode(encoding, errors)
if isinstance(s, str):
return s
raise ValueError("Expected str or unicode, received %s." % type(s))
try:
from collections import OrderedDict
except ImportError:
# Python2.6 we use the 3rd party back port.
from ordereddict import OrderedDict
if sys.version_info[:2] == (2, 6):
import simplejson as json
# In py26, invalid xml parsed by element tree
# will raise a plain old SyntaxError instead of
# a real exception, so we need to abstract this change.
XMLParseError = SyntaxError
# Handle https://github.com/shazow/urllib3/issues/497 for py2.6. In
# python2.6, there is a known issue where sometimes we cannot read the SAN
# from an SSL cert (http://bugs.python.org/issue13034). However, newer
# versions of urllib3 will warn you when there is no SAN. While we could
# just turn off this warning in urllib3 altogether, we _do_ want warnings
# when they're legitimate warnings. This method tries to scope the warning
# filter to be as specific as possible.
def filter_ssl_san_warnings():
warnings.filterwarnings(
'ignore',
message="Certificate has no.*subjectAltName.*",
category=exceptions.SecurityWarning,
module=r".*urllib3\.connection")
else:
import xml.etree.cElementTree
XMLParseError = xml.etree.cElementTree.ParseError
import json
def filter_ssl_san_warnings():
# Noop for non-py26 versions. We will parse the SAN
# appropriately.
pass
def filter_ssl_warnings():
# Ignore warnings related to SNI as it is not being used in validations.
warnings.filterwarnings(
'ignore',
message="A true SSLContext object is not available.*",
category=exceptions.InsecurePlatformWarning,
module=r".*urllib3\.util\.ssl_")
filter_ssl_san_warnings()
@classmethod
def from_dict(cls, d):
new_instance = cls()
for key, value in d.items():
new_instance[key] = value
return new_instance
@classmethod
def from_pairs(cls, pairs):
new_instance = cls()
for key, value in pairs:
new_instance[key] = value
return new_instance
HTTPHeaders.from_dict = from_dict
HTTPHeaders.from_pairs = from_pairs
def copy_kwargs(kwargs):
"""
There is a bug in Python versions < 2.6.5 that prevents you
from passing unicode keyword args (#4978). This function
takes a dictionary of kwargs and returns a copy. If you are
using Python < 2.6.5, it also encodes the keys to avoid this bug.
Oh, and version_info wasn't a namedtuple back then, either!
"""
vi = sys.version_info
if vi[0] == 2 and vi[1] <= 6 and vi[3] < 5:
copy_kwargs = {}
for key in kwargs:
copy_kwargs[key.encode('utf-8')] = kwargs[key]
else:
copy_kwargs = copy.copy(kwargs)
return copy_kwargs
def total_seconds(delta):
"""
Returns the total seconds in a ``datetime.timedelta``.
Python 2.6 does not have ``timedelta.total_seconds()``, so we have
to calculate this ourselves. On 2.7 or better, we'll take advantage of the
built-in method.
The math was pulled from the ``datetime`` docs
(http://docs.python.org/2.7/library/datetime.html#datetime.timedelta.total_seconds).
:param delta: The timedelta object
:type delta: ``datetime.timedelta``
"""
if sys.version_info[:2] != (2, 6):
return delta.total_seconds()
day_in_seconds = delta.days * 24 * 3600.0
micro_in_seconds = delta.microseconds / 10.0**6
return day_in_seconds + delta.seconds + micro_in_seconds
# Checks to see if md5 is available on this system. A given system might not
# have access to it for various reasons, such as FIPS mode being enabled.
try:
hashlib.md5()
MD5_AVAILABLE = True
except ValueError:
MD5_AVAILABLE = False
def get_md5(*args, **kwargs):
"""
Attempts to get an md5 hashing object.
:param raise_error_if_unavailable: raise an error if md5 is unavailable on
this system. If False, None will be returned if it is unavailable.
:type raise_error_if_unavailable: bool
:param args: Args to pass to the MD5 constructor
:param kwargs: Key word arguments to pass to the MD5 constructor
:return: An MD5 hashing object if available. If it is unavailable, None
is returned if raise_error_if_unavailable is set to False.
"""
if MD5_AVAILABLE:
return hashlib.md5(*args, **kwargs)
else:
raise MD5UnavailableError()
def compat_shell_split(s, platform=None):
if platform is None:
platform = sys.platform
if platform == "win32":
return _windows_shell_split(s)
else:
return shlex.split(s)
def _windows_shell_split(s):
"""Splits up a windows command as the built-in command parser would.
Windows has potentially bizarre rules depending on where you look. When
spawning a process via the Windows C runtime (which is what python does
when you call popen) the rules are as follows:
https://docs.microsoft.com/en-us/cpp/cpp/parsing-cpp-command-line-arguments
To summarize:
* Only space and tab are valid delimiters
* Double quotes are the only valid quotes
* Backslash is interpreted literally unless it is part of a chain that
leads up to a double quote. Then the backslashes escape the backslashes,
and if there is an odd number the final backslash escapes the quote.
:param s: The command string to split up into parts.
:return: A list of command components.
"""
if not s:
return []
components = []
buff = []
is_quoted = False
num_backslashes = 0
for character in s:
if character == '\\':
# We can't simply append backslashes because we don't know if
# they are being used as escape characters or not. Instead we
# keep track of how many we've encountered and handle them when
# we encounter a different character.
num_backslashes += 1
elif character == '"':
if num_backslashes > 0:
# The backslashes are in a chain leading up to a double
# quote, so they are escaping each other.
buff.append('\\' * int(floor(num_backslashes / 2)))
remainder = num_backslashes % 2
num_backslashes = 0
if remainder == 1:
# The number of backslashes is uneven, so they are also
# escaping the double quote, so it needs to be added to
# the current component buffer.
buff.append('"')
continue
# We've encountered a double quote that is not escaped,
# so we toggle is_quoted.
is_quoted = not is_quoted
# If there are quotes, then we may want an empty string. To be
# safe, we add an empty string to the buffer so that we make
# sure it sticks around if there's nothing else between quotes.
# If there is other stuff between quotes, the empty string will
# disappear during the joining process.
buff.append('')
elif character in [' ', '\t'] and not is_quoted:
# Since the backslashes aren't leading up to a quote, we put in
# the exact number of backslashes.
if num_backslashes > 0:
buff.append('\\' * num_backslashes)
num_backslashes = 0
# Excess whitespace is ignored, so only add the components list
# if there is anything in the buffer.
if buff:
components.append(''.join(buff))
buff = []
else:
# Since the backslashes aren't leading up to a quote, we put in
# the exact number of backslashes.
if num_backslashes > 0:
buff.append('\\' * num_backslashes)
num_backslashes = 0
buff.append(character)
# Quotes must be terminated.
if is_quoted:
raise ValueError('No closing quotation in string: %s' % s)
# There may be some leftover backslashes, so we need to add them in.
# There's no quote so we add the exact number.
if num_backslashes > 0:
buff.append('\\' * num_backslashes)
# Add the final component in if there is anything in the buffer.
if buff:
components.append(''.join(buff))
return components

View File

@@ -0,0 +1,231 @@
# 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.
import copy
from botocore.compat import OrderedDict
from botocore.endpoint import DEFAULT_TIMEOUT, MAX_POOL_CONNECTIONS
from botocore.exceptions import InvalidS3AddressingStyleError
from botocore.exceptions import InvalidRetryConfigurationError
from botocore.exceptions import InvalidMaxRetryAttemptsError
class Config(object):
"""Advanced configuration for Botocore clients.
:type region_name: str
:param region_name: The region to use in instantiating the client
:type signature_version: str
:param signature_version: The signature version when signing requests.
:type user_agent: str
:param user_agent: The value to use in the User-Agent header.
:type user_agent_extra: str
:param user_agent_extra: The value to append to the current User-Agent
header value.
:type connect_timeout: float or int
:param connect_timeout: The time in seconds till a timeout exception is
thrown when attempting to make a connection. The default is 60
seconds.
:type read_timeout: float or int
:param read_timeout: The time in seconds till a timeout exception is
thrown when attempting to read from a connection. The default is
60 seconds.
:type parameter_validation: bool
:param parameter_validation: Whether parameter validation should occur
when serializing requests. The default is True. You can disable
parameter validation for performance reasons. Otherwise, it's
recommended to leave parameter validation enabled.
:type max_pool_connections: int
:param max_pool_connections: The maximum number of connections to
keep in a connection pool. If this value is not set, the default
value of 10 is used.
:type proxies: dict
:param proxies: A dictionary of proxy servers to use by protocol or
endpoint, e.g.:
{'http': 'foo.bar:3128', 'http://hostname': 'foo.bar:4012'}.
The proxies are used on each request.
:type s3: dict
:param s3: A dictionary of s3 specific configurations.
Valid keys are:
* 'use_accelerate_endpoint' -- Refers to whether to use the S3
Accelerate endpoint. The value must be a boolean. If True, the
client will use the S3 Accelerate endpoint. If the S3 Accelerate
endpoint is being used then the addressing style will always
be virtual.
* 'payload_signing_enabled' -- Refers to whether or not to SHA256
sign sigv4 payloads. By default, this is disabled for streaming
uploads (UploadPart and PutObject).
* 'addressing_style' -- Refers to the style in which to address
s3 endpoints. Values must be a string that equals:
* auto -- Addressing style is chosen for user. Depending
on the configuration of client, the endpoint may be addressed in
the virtual or the path style. Note that this is the default
behavior if no style is specified.
* virtual -- Addressing style is always virtual. The name of the
bucket must be DNS compatible or an exception will be thrown.
Endpoints will be addressed as such: mybucket.s3.amazonaws.com
* path -- Addressing style is always by path. Endpoints will be
addressed as such: s3.amazonaws.com/mybucket
:type retries: dict
:param retries: A dictionary for retry specific configurations.
Valid keys are:
* 'max_attempts' -- An integer representing the maximum number of
retry attempts that will be made on a single request. For
example, setting this value to 2 will result in the request
being retried at most two times after the initial request. Setting
this value to 0 will result in no retries ever being attempted on
the initial request. If not provided, the number of retries will
default to whatever is modeled, which is typically four retries.
:type client_cert: str, (str, str)
:param client_cert: The path to a certificate for TLS client authentication.
When a str is provided it is treated as a path to a client certificate
to be used when creating a TLS connection.
If a client key is to be provided alongside the client certificate the
client_cert should be set to a tuple of length two where the first
element is the path to the client certificate and the second element is
the path to the certificate key.
:type inject_host_prefix: bool
:param inject_host_prefix: Whether host prefix injection should occur.
Defaults to True.
Setting this to False disables the injection of operation parameters
into the prefix of the hostname. This is useful for clients providing
custom endpoints that should not have their host prefix modified.
"""
OPTION_DEFAULTS = OrderedDict([
('region_name', None),
('signature_version', None),
('user_agent', None),
('user_agent_extra', None),
('connect_timeout', DEFAULT_TIMEOUT),
('read_timeout', DEFAULT_TIMEOUT),
('parameter_validation', True),
('max_pool_connections', MAX_POOL_CONNECTIONS),
('proxies', None),
('s3', None),
('retries', None),
('client_cert', None),
('inject_host_prefix', True),
('endpoint_discovery_enabled', None),
])
def __init__(self, *args, **kwargs):
self._user_provided_options = self._record_user_provided_options(
args, kwargs)
# Merge the user_provided options onto the default options
config_vars = copy.copy(self.OPTION_DEFAULTS)
config_vars.update(self._user_provided_options)
# Set the attributes based on the config_vars
for key, value in config_vars.items():
setattr(self, key, value)
# Validate the s3 options
self._validate_s3_configuration(self.s3)
self._validate_retry_configuration(self.retries)
def _record_user_provided_options(self, args, kwargs):
option_order = list(self.OPTION_DEFAULTS)
user_provided_options = {}
# Iterate through the kwargs passed through to the constructor and
# map valid keys to the dictionary
for key, value in kwargs.items():
if key in self.OPTION_DEFAULTS:
user_provided_options[key] = value
# The key must exist in the available options
else:
raise TypeError(
'Got unexpected keyword argument \'%s\'' % key)
# The number of args should not be longer than the allowed
# options
if len(args) > len(option_order):
raise TypeError(
'Takes at most %s arguments (%s given)' % (
len(option_order), len(args)))
# Iterate through the args passed through to the constructor and map
# them to appropriate keys.
for i, arg in enumerate(args):
# If it a kwarg was specified for the arg, then error out
if option_order[i] in user_provided_options:
raise TypeError(
'Got multiple values for keyword argument \'%s\'' % (
option_order[i]))
user_provided_options[option_order[i]] = arg
return user_provided_options
def _validate_s3_configuration(self, s3):
if s3 is not None:
addressing_style = s3.get('addressing_style')
if addressing_style not in ['virtual', 'auto', 'path', None]:
raise InvalidS3AddressingStyleError(
s3_addressing_style=addressing_style)
def _validate_retry_configuration(self, retries):
if retries is not None:
for key in retries:
if key not in ['max_attempts']:
raise InvalidRetryConfigurationError(
retry_config_option=key)
if key == 'max_attempts' and retries[key] < 0:
raise InvalidMaxRetryAttemptsError(
provided_max_attempts=retries[key]
)
def merge(self, other_config):
"""Merges the config object with another config object
This will merge in all non-default values from the provided config
and return a new config object
:type other_config: botocore.config.Config
:param other config: Another config object to merge with. The values
in the provided config object will take precedence in the merging
:returns: A config object built from the merged values of both
config objects.
"""
# Make a copy of the current attributes in the config object.
config_options = copy.copy(self._user_provided_options)
# Merge in the user provided options from the other config
config_options.update(other_config._user_provided_options)
# Return a new config object with the merged properties.
return Config(**config_options)

View File

@@ -0,0 +1,272 @@
# Copyright (c) 2012-2013 Mitch Garnaat http://garnaat.org/
# Copyright 2012-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.
import os
import shlex
import copy
import sys
from botocore.compat import six
import botocore.exceptions
def multi_file_load_config(*filenames):
"""Load and combine multiple INI configs with profiles.
This function will take a list of filesnames and return
a single dictionary that represents the merging of the loaded
config files.
If any of the provided filenames does not exist, then that file
is ignored. It is therefore ok to provide a list of filenames,
some of which may not exist.
Configuration files are **not** deep merged, only the top level
keys are merged. The filenames should be passed in order of
precedence. The first config file has precedence over the
second config file, which has precedence over the third config file,
etc. The only exception to this is that the "profiles" key is
merged to combine profiles from multiple config files into a
single profiles mapping. However, if a profile is defined in
multiple config files, then the config file with the highest
precedence is used. Profile values themselves are not merged.
For example::
FileA FileB FileC
[foo] [foo] [bar]
a=1 a=2 a=3
b=2
[bar] [baz] [profile a]
a=2 a=3 region=e
[profile a] [profile b] [profile c]
region=c region=d region=f
The final result of ``multi_file_load_config(FileA, FileB, FileC)``
would be::
{"foo": {"a": 1}, "bar": {"a": 2}, "baz": {"a": 3},
"profiles": {"a": {"region": "c"}}, {"b": {"region": d"}},
{"c": {"region": "f"}}}
Note that the "foo" key comes from A, even though it's defined in both
FileA and FileB. Because "foo" was defined in FileA first, then the values
for "foo" from FileA are used and the values for "foo" from FileB are
ignored. Also note where the profiles originate from. Profile "a"
comes FileA, profile "b" comes from FileB, and profile "c" comes
from FileC.
"""
configs = []
profiles = []
for filename in filenames:
try:
loaded = load_config(filename)
except botocore.exceptions.ConfigNotFound:
continue
profiles.append(loaded.pop('profiles'))
configs.append(loaded)
merged_config = _merge_list_of_dicts(configs)
merged_profiles = _merge_list_of_dicts(profiles)
merged_config['profiles'] = merged_profiles
return merged_config
def _merge_list_of_dicts(list_of_dicts):
merged_dicts = {}
for single_dict in list_of_dicts:
for key, value in single_dict.items():
if key not in merged_dicts:
merged_dicts[key] = value
return merged_dicts
def load_config(config_filename):
"""Parse a INI config with profiles.
This will parse an INI config file and map top level profiles
into a top level "profile" key.
If you want to parse an INI file and map all section names to
top level keys, use ``raw_config_parse`` instead.
"""
parsed = raw_config_parse(config_filename)
return build_profile_map(parsed)
def raw_config_parse(config_filename, parse_subsections=True):
"""Returns the parsed INI config contents.
Each section name is a top level key.
:param config_filename: The name of the INI file to parse
:param parse_subsections: If True, parse indented blocks as
subsections that represent their own configuration dictionary.
For example, if the config file had the contents::
s3 =
signature_version = s3v4
addressing_style = path
The resulting ``raw_config_parse`` would be::
{'s3': {'signature_version': 's3v4', 'addressing_style': 'path'}}
If False, do not try to parse subsections and return the indented
block as its literal value::
{'s3': '\nsignature_version = s3v4\naddressing_style = path'}
:returns: A dict with keys for each profile found in the config
file and the value of each key being a dict containing name
value pairs found in that profile.
:raises: ConfigNotFound, ConfigParseError
"""
config = {}
path = config_filename
if path is not None:
path = os.path.expandvars(path)
path = os.path.expanduser(path)
if not os.path.isfile(path):
raise botocore.exceptions.ConfigNotFound(path=_unicode_path(path))
cp = six.moves.configparser.RawConfigParser()
try:
cp.read([path])
except (six.moves.configparser.Error, UnicodeDecodeError):
raise botocore.exceptions.ConfigParseError(
path=_unicode_path(path))
else:
for section in cp.sections():
config[section] = {}
for option in cp.options(section):
config_value = cp.get(section, option)
if parse_subsections and config_value.startswith('\n'):
# Then we need to parse the inner contents as
# hierarchical. We support a single level
# of nesting for now.
try:
config_value = _parse_nested(config_value)
except ValueError:
raise botocore.exceptions.ConfigParseError(
path=_unicode_path(path))
config[section][option] = config_value
return config
def _unicode_path(path):
if isinstance(path, six.text_type):
return path
# According to the documentation getfilesystemencoding can return None
# on unix in which case the default encoding is used instead.
filesystem_encoding = sys.getfilesystemencoding()
if filesystem_encoding is None:
filesystem_encoding = sys.getdefaultencoding()
return path.decode(filesystem_encoding, 'replace')
def _parse_nested(config_value):
# Given a value like this:
# \n
# foo = bar
# bar = baz
# We need to parse this into
# {'foo': 'bar', 'bar': 'baz}
parsed = {}
for line in config_value.splitlines():
line = line.strip()
if not line:
continue
# The caller will catch ValueError
# and raise an appropriate error
# if this fails.
key, value = line.split('=', 1)
parsed[key.strip()] = value.strip()
return parsed
def build_profile_map(parsed_ini_config):
"""Convert the parsed INI config into a profile map.
The config file format requires that every profile except the
default to be prepended with "profile", e.g.::
[profile test]
aws_... = foo
aws_... = bar
[profile bar]
aws_... = foo
aws_... = bar
# This is *not* a profile
[preview]
otherstuff = 1
# Neither is this
[foobar]
morestuff = 2
The build_profile_map will take a parsed INI config file where each top
level key represents a section name, and convert into a format where all
the profiles are under a single top level "profiles" key, and each key in
the sub dictionary is a profile name. For example, the above config file
would be converted from::
{"profile test": {"aws_...": "foo", "aws...": "bar"},
"profile bar": {"aws...": "foo", "aws...": "bar"},
"preview": {"otherstuff": ...},
"foobar": {"morestuff": ...},
}
into::
{"profiles": {"test": {"aws_...": "foo", "aws...": "bar"},
"bar": {"aws...": "foo", "aws...": "bar"},
"preview": {"otherstuff": ...},
"foobar": {"morestuff": ...},
}
If there are no profiles in the provided parsed INI contents, then
an empty dict will be the value associated with the ``profiles`` key.
.. note::
This will not mutate the passed in parsed_ini_config. Instead it will
make a deepcopy and return that value.
"""
parsed_config = copy.deepcopy(parsed_ini_config)
profiles = {}
final_config = {}
for key, values in parsed_config.items():
if key.startswith("profile"):
try:
parts = shlex.split(key)
except ValueError:
continue
if len(parts) == 2:
profiles[parts[1]] = values
elif key == 'default':
# default section is special and is considered a profile
# name but we don't require you use 'profile "default"'
# as a section.
profiles[key] = values
else:
final_config[key] = values
final_config['profiles'] = profiles
return final_config

View File

@@ -0,0 +1,441 @@
# Copyright 2018 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.
"""This module contains the inteface for controlling how configuration
is loaded.
"""
import os
from botocore import utils
#: A default dictionary that maps the logical names for session variables
#: to the specific environment variables and configuration file names
#: that contain the values for these variables.
#: When creating a new Session object, you can pass in your own dictionary
#: to remap the logical names or to add new logical names. You can then
#: get the current value for these variables by using the
#: ``get_config_variable`` method of the :class:`botocore.session.Session`
#: class.
#: These form the keys of the dictionary. The values in the dictionary
#: are tuples of (<config_name>, <environment variable>, <default value>,
#: <conversion func>).
#: The conversion func is a function that takes the configuration value
#: as an argument and returns the converted value. If this value is
#: None, then the configuration value is returned unmodified. This
#: conversion function can be used to type convert config values to
#: values other than the default values of strings.
#: The ``profile`` and ``config_file`` variables should always have a
#: None value for the first entry in the tuple because it doesn't make
#: sense to look inside the config file for the location of the config
#: file or for the default profile to use.
#: The ``config_name`` is the name to look for in the configuration file,
#: the ``env var`` is the OS environment variable (``os.environ``) to
#: use, and ``default_value`` is the value to use if no value is otherwise
#: found.
BOTOCORE_DEFAUT_SESSION_VARIABLES = {
# logical: config_file, env_var, default_value, conversion_func
'profile': (None, ['AWS_DEFAULT_PROFILE', 'AWS_PROFILE'], None, None),
'region': ('region', 'AWS_DEFAULT_REGION', None, None),
'data_path': ('data_path', 'AWS_DATA_PATH', None, None),
'config_file': (None, 'AWS_CONFIG_FILE', '~/.aws/config', None),
'ca_bundle': ('ca_bundle', 'AWS_CA_BUNDLE', None, None),
'api_versions': ('api_versions', None, {}, None),
# This is the shared credentials file amongst sdks.
'credentials_file': (None, 'AWS_SHARED_CREDENTIALS_FILE',
'~/.aws/credentials', None),
# These variables only exist in the config file.
# This is the number of seconds until we time out a request to
# the instance metadata service.
'metadata_service_timeout': (
'metadata_service_timeout',
'AWS_METADATA_SERVICE_TIMEOUT', 1, int),
# This is the number of request attempts we make until we give
# up trying to retrieve data from the instance metadata service.
'metadata_service_num_attempts': (
'metadata_service_num_attempts',
'AWS_METADATA_SERVICE_NUM_ATTEMPTS', 1, int),
'parameter_validation': ('parameter_validation', None, True, None),
# Client side monitoring configurations.
# Note: These configurations are considered internal to botocore.
# Do not use them until publicly documented.
'csm_enabled': (
'csm_enabled', 'AWS_CSM_ENABLED', False, utils.ensure_boolean),
'csm_host': ('csm_host', 'AWS_CSM_HOST', '127.0.0.1', None),
'csm_port': ('csm_port', 'AWS_CSM_PORT', 31000, int),
'csm_client_id': ('csm_client_id', 'AWS_CSM_CLIENT_ID', '', None),
# Endpoint discovery configuration
'endpoint_discovery_enabled': (
'endpoint_discovery_enabled', 'AWS_ENDPOINT_DISCOVERY_ENABLED',
False, utils.ensure_boolean),
'sts_regional_endpoints': (
'sts_regional_endpoints', 'AWS_STS_REGIONAL_ENDPOINTS', 'legacy',
None
),
}
def create_botocore_default_config_mapping(chain_builder):
mapping = {}
for logical_name, config in BOTOCORE_DEFAUT_SESSION_VARIABLES.items():
mapping[logical_name] = chain_builder.create_config_chain(
instance_name=logical_name,
env_var_names=config[1],
config_property_name=config[0],
default=config[2],
conversion_func=config[3]
)
return mapping
class ConfigChainFactory(object):
"""Factory class to create our most common configuration chain case.
This is a convenience class to construct configuration chains that follow
our most common pattern. This is to prevent ordering them incorrectly,
and to make the config chain construction more readable.
"""
def __init__(self, session, environ=None):
"""Initialize a ConfigChainFactory.
:type session: :class:`botocore.session.Session`
:param session: This is the session that should be used to look up
values from the config file.
:type environ: dict
:param environ: A mapping to use for environment variables. If this
is not provided it will default to use os.environ.
"""
self._session = session
if environ is None:
environ = os.environ
self._environ = environ
def create_config_chain(self, instance_name=None, env_var_names=None,
config_property_name=None, default=None,
conversion_func=None):
"""Build a config chain following the standard botocore pattern.
In botocore most of our config chains follow the the precendence:
session_instance_variables, environment, config_file, default_value.
This is a convenience function for creating a chain that follow
that precendence.
:type instance_name: str
:param instance_name: This indicates what session instance variable
corresponds to this config value. If it is None it will not be
added to the chain.
:type env_var_names: str or list of str or None
:param env_var_names: One or more environment variable names to
search for this value. They are searched in order. If it is None
it will not be added to the chain.
:type config_property_name: str or None
:param config_property_name: The string name of the key in the config
file for this config option. If it is None it will not be added to
the chain.
:type default: Any
:param default: Any constant value to be returned.
:type conversion_func: None or callable
:param conversion_func: If this value is None then it has no effect on
the return type. Otherwise, it is treated as a function that will
conversion_func our provided type.
:rvalue: ConfigChain
:returns: A ConfigChain that resolves in the order env_var_names ->
config_property_name -> default. Any values that were none are
omitted form the chain.
"""
providers = []
if instance_name is not None:
providers.append(
InstanceVarProvider(
instance_var=instance_name,
session=self._session
)
)
if env_var_names is not None:
providers.append(
EnvironmentProvider(
names=env_var_names,
env=self._environ,
)
)
if config_property_name is not None:
providers.append(
ScopedConfigProvider(
config_var_name=config_property_name,
session=self._session,
)
)
if default is not None:
providers.append(ConstantProvider(value=default))
return ChainProvider(
providers=providers,
conversion_func=conversion_func,
)
class ConfigValueStore(object):
"""The ConfigValueStore object stores configuration values."""
def __init__(self, mapping=None):
"""Initialize a ConfigValueStore.
:type mapping: dict
:param mapping: The mapping parameter is a map of string to a subclass
of BaseProvider. When a config variable is asked for via the
get_config_variable method, the corresponding provider will be
invoked to load the value.
"""
self._overrides = {}
self._mapping = {}
if mapping is not None:
for logical_name, provider in mapping.items():
self.set_config_provider(logical_name, provider)
def get_config_variable(self, logical_name):
"""
Retrieve the value associeated with the specified logical_name
from the corresponding provider. If no value is found None will
be returned.
:type logical_name: str
:param logical_name: The logical name of the session variable
you want to retrieve. This name will be mapped to the
appropriate environment variable name for this session as
well as the appropriate config file entry.
:returns: value of variable or None if not defined.
"""
if logical_name in self._overrides:
return self._overrides[logical_name]
if logical_name not in self._mapping:
return None
provider = self._mapping[logical_name]
return provider.provide()
def set_config_variable(self, logical_name, value):
"""Set a configuration variable to a specific value.
By using this method, you can override the normal lookup
process used in ``get_config_variable`` by explicitly setting
a value. Subsequent calls to ``get_config_variable`` will
use the ``value``. This gives you per-session specific
configuration values.
::
>>> # Assume logical name 'foo' maps to env var 'FOO'
>>> os.environ['FOO'] = 'myvalue'
>>> s.get_config_variable('foo')
'myvalue'
>>> s.set_config_variable('foo', 'othervalue')
>>> s.get_config_variable('foo')
'othervalue'
:type logical_name: str
:param logical_name: The logical name of the session variable
you want to set. These are the keys in ``SESSION_VARIABLES``.
:param value: The value to associate with the config variable.
"""
self._overrides[logical_name] = value
def clear_config_variable(self, logical_name):
"""Remove an override config variable from the session.
:type logical_name: str
:param logical_name: The name of the parameter to clear the override
value from.
"""
self._overrides.pop(logical_name, None)
def set_config_provider(self, logical_name, provider):
"""Set the provider for a config value.
This provides control over how a particular configuration value is
loaded. This replaces the provider for ``logical_name`` with the new
``provider``.
:type logical_name: str
:param logical_name: The name of the config value to change the config
provider for.
:type provider: :class:`botocore.configprovider.BaseProvider`
:param provider: The new provider that should be responsible for
providing a value for the config named ``logical_name``.
"""
self._mapping[logical_name] = provider
class BaseProvider(object):
"""Base class for configuration value providers.
A configuration provider has some method of providing a configuration
value.
"""
def provide(self):
"""Provide a config value."""
raise NotImplementedError('provide')
class ChainProvider(BaseProvider):
"""This provider wraps one or more other providers.
Each provider in the chain is called, the first one returning a non-None
value is then returned.
"""
def __init__(self, providers=None, conversion_func=None):
"""Initalize a ChainProvider.
:type providers: list
:param providers: The initial list of providers to check for values
when invoked.
:type conversion_func: None or callable
:param conversion_func: If this value is None then it has no affect on
the return type. Otherwise, it is treated as a function that will
transform provided value.
"""
if providers is None:
providers = []
self._providers = providers
self._conversion_func = conversion_func
def provide(self):
"""Provide the value from the first provider to return non-None.
Each provider in the chain has its provide method called. The first
one in the chain to return a non-None value is the returned from the
ChainProvider. When no non-None value is found, None is returned.
"""
for provider in self._providers:
value = provider.provide()
if value is not None:
return self._convert_type(value)
return None
def _convert_type(self, value):
if self._conversion_func is not None:
return self._conversion_func(value)
return value
def __repr__(self):
return '[%s]' % ', '.join([str(p) for p in self._providers])
class InstanceVarProvider(BaseProvider):
"""This class loads config values from the session instance vars."""
def __init__(self, instance_var, session):
"""Initialize InstanceVarProvider.
:type instance_var: str
:param instance_var: The instance variable to load from the session.
:type session: :class:`botocore.session.Session`
:param session: The botocore session to get the loaded configuration
file variables from.
"""
self._instance_var = instance_var
self._session = session
def provide(self):
"""Provide a config value from the session instance vars."""
instance_vars = self._session.instance_variables()
value = instance_vars.get(self._instance_var)
return value
def __repr__(self):
return 'InstanceVarProvider(instance_var=%s, session=%s)' % (
self._instance_var,
self._session,
)
class ScopedConfigProvider(BaseProvider):
def __init__(self, config_var_name, session):
"""Initialize ScopedConfigProvider.
:type config_var_name: str
:param config_var_name: The name of the config variable to load from
the configuration file.
:type session: :class:`botocore.session.Session`
:param session: The botocore session to get the loaded configuration
file variables from.
"""
self._config_var_name = config_var_name
self._session = session
def provide(self):
"""Provide a value from a config file property."""
config = self._session.get_scoped_config()
value = config.get(self._config_var_name)
return value
def __repr__(self):
return 'ScopedConfigProvider(config_var_name=%s, session=%s)' % (
self._config_var_name,
self._session,
)
class EnvironmentProvider(BaseProvider):
"""This class loads config values from environment variables."""
def __init__(self, names, env):
"""Initialize with the keys in the dictionary to check.
:type names: str or list
:param names: If this is a str, the key with that name will
be loaded and returned. If this variable is
a list, then it must be a list of str. The same process will be
repeated for each string in the list, the first that returns non
None will be returned.
:type env: dict
:param env: Environment variables dictionary to get variables from.
"""
self._names = names
self._env = env
def provide(self):
"""Provide a config value from a source dictionary."""
names = self._names
if not isinstance(names, list):
names = [names]
for name in names:
if name in self._env:
return self._env[name]
return None
def __repr__(self):
return 'EnvironmentProvider(names=%s, env=%s)' % (self._names,
self._env)
class ConstantProvider(BaseProvider):
"""This provider provides a constant value."""
def __init__(self, value):
self._value = value
def provide(self):
"""Provide the constant value given during initialization."""
return self._value
def __repr__(self):
return 'ConstantProvider(value=%s)' % self._value

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,284 @@
{
"definitions": {
"throttling": {
"applies_when": {
"response": {
"service_error_code": "Throttling",
"http_status_code": 400
}
}
},
"throttling_exception": {
"applies_when": {
"response": {
"service_error_code": "ThrottlingException",
"http_status_code": 400
}
}
},
"throttled_exception": {
"applies_when": {
"response": {
"service_error_code": "ThrottledException",
"http_status_code": 400
}
}
},
"request_throttled_exception": {
"applies_when": {
"response": {
"service_error_code": "RequestThrottledException",
"http_status_code": 400
}
}
},
"too_many_requests": {
"applies_when": {
"response": {
"http_status_code": 429
}
}
},
"general_socket_errors": {
"applies_when": {
"socket_errors": ["GENERAL_CONNECTION_ERROR"]
}
},
"general_server_error": {
"applies_when": {
"response": {
"http_status_code": 500
}
}
},
"bad_gateway": {
"applies_when": {
"response": {
"http_status_code": 502
}
}
},
"service_unavailable": {
"applies_when": {
"response": {
"http_status_code": 503
}
}
},
"gateway_timeout": {
"applies_when": {
"response": {
"http_status_code": 504
}
}
},
"limit_exceeded": {
"applies_when": {
"response": {
"http_status_code": 509
}
}
},
"throughput_exceeded": {
"applies_when": {
"response": {
"service_error_code": "ProvisionedThroughputExceededException",
"http_status_code": 400
}
}
}
},
"retry": {
"__default__": {
"max_attempts": 5,
"delay": {
"type": "exponential",
"base": "rand",
"growth_factor": 2
},
"policies": {
"general_socket_errors": {"$ref": "general_socket_errors"},
"general_server_error": {"$ref": "general_server_error"},
"bad_gateway": {"$ref": "bad_gateway"},
"service_unavailable": {"$ref": "service_unavailable"},
"gateway_timeout": {"$ref": "gateway_timeout"},
"limit_exceeded": {"$ref": "limit_exceeded"},
"throttling_exception": {"$ref": "throttling_exception"},
"throttled_exception": {"$ref": "throttled_exception"},
"request_throttled_exception": {"$ref": "request_throttled_exception"},
"throttling": {"$ref": "throttling"},
"too_many_requests": {"$ref": "too_many_requests"},
"throughput_exceeded": {"$ref": "throughput_exceeded"}
}
},
"organizations": {
"__default__": {
"policies": {
"too_many_requests": {
"applies_when": {
"response": {
"service_error_code": "TooManyRequestsException",
"http_status_code": 400
}
}
}
}
}
},
"dynamodb": {
"__default__": {
"max_attempts": 10,
"delay": {
"type": "exponential",
"base": 0.05,
"growth_factor": 2
},
"policies": {
"still_processing": {
"applies_when": {
"response": {
"service_error_code": "TransactionInProgressException",
"http_status_code": 400
}
}
},
"crc32": {
"applies_when": {
"response": {
"crc32body": "x-amz-crc32"
}
}
}
}
}
},
"ec2": {
"__default__": {
"policies": {
"request_limit_exceeded": {
"applies_when": {
"response": {
"service_error_code": "RequestLimitExceeded",
"http_status_code": 503
}
}
}
}
}
},
"cloudsearch": {
"__default__": {
"policies": {
"request_limit_exceeded": {
"applies_when": {
"response": {
"service_error_code": "BandwidthLimitExceeded",
"http_status_code": 509
}
}
}
}
}
},
"kinesis": {
"__default__": {
"policies": {
"request_limit_exceeded": {
"applies_when": {
"response": {
"service_error_code": "LimitExceededException",
"http_status_code": 400
}
}
}
}
}
},
"sqs": {
"__default__": {
"policies": {
"request_limit_exceeded": {
"applies_when": {
"response": {
"service_error_code": "RequestThrottled",
"http_status_code": 403
}
}
}
}
}
},
"s3": {
"__default__": {
"policies": {
"timeouts": {
"applies_when": {
"response": {
"http_status_code": 400,
"service_error_code": "RequestTimeout"
}
}
},
"contentmd5": {
"applies_when": {
"response": {
"http_status_code": 400,
"service_error_code": "BadDigest"
}
}
}
}
}
},
"glacier": {
"__default__": {
"policies": {
"timeouts": {
"applies_when": {
"response": {
"http_status_code": 408,
"service_error_code": "RequestTimeoutException"
}
}
}
}
}
},
"route53": {
"__default__": {
"policies": {
"request_limit_exceeded": {
"applies_when": {
"response": {
"service_error_code": "Throttling",
"http_status_code": 400
}
}
},
"still_processing": {
"applies_when": {
"response": {
"service_error_code": "PriorRequestNotComplete",
"http_status_code": 400
}
}
}
}
}
},
"sts": {
"__default__": {
"policies": {
"idp_unreachable_error": {
"applies_when": {
"response": {
"service_error_code": "IDPCommunicationError",
"http_status_code": 400
}
}
}
}
}
}
}
}

View File

@@ -0,0 +1,22 @@
{
"pagination": {
"ListCertificateAuthorities": {
"input_token": "NextToken",
"limit_key": "MaxResults",
"output_token": "NextToken",
"result_key": "CertificateAuthorities"
},
"ListTags": {
"input_token": "NextToken",
"limit_key": "MaxResults",
"output_token": "NextToken",
"result_key": "Tags"
},
"ListPermissions": {
"input_token": "NextToken",
"limit_key": "MaxResults",
"output_token": "NextToken",
"result_key": "Permissions"
}
}
}

View File

@@ -0,0 +1,61 @@
{
"version": 2,
"waiters": {
"CertificateAuthorityCSRCreated": {
"description": "Wait until a Certificate Authority CSR is created",
"operation": "GetCertificateAuthorityCsr",
"delay": 3,
"maxAttempts": 60,
"acceptors": [
{
"state": "success",
"matcher": "status",
"expected": 200
},
{
"state": "retry",
"matcher": "error",
"expected": "RequestInProgressException"
}
]
},
"CertificateIssued": {
"description": "Wait until a certificate is issued",
"operation": "GetCertificate",
"delay": 3,
"maxAttempts": 60,
"acceptors": [
{
"state": "success",
"matcher": "status",
"expected": 200
},
{
"state": "retry",
"matcher": "error",
"expected": "RequestInProgressException"
}
]
},
"AuditReportCreated": {
"description": "Wait until a Audit Report is created",
"operation": "DescribeCertificateAuthorityAuditReport",
"delay": 3,
"maxAttempts": 60,
"acceptors": [
{
"state": "success",
"matcher": "path",
"argument": "AuditReportStatus",
"expected": "SUCCESS"
},
{
"state": "failure",
"matcher": "path",
"argument": "AuditReportStatus",
"expected": "FAILED"
}
]
}
}
}

View File

@@ -0,0 +1,5 @@
{
"version": "1.0",
"examples": {
}
}

View File

@@ -0,0 +1,10 @@
{
"pagination": {
"ListCertificates": {
"input_token": "NextToken",
"output_token": "NextToken",
"limit_key": "MaxItems",
"result_key": "CertificateSummaryList"
}
}
}

View File

@@ -0,0 +1,35 @@
{
"version": 2,
"waiters": {
"CertificateValidated": {
"delay": 60,
"maxAttempts": 40,
"operation": "DescribeCertificate",
"acceptors": [
{
"matcher": "pathAll",
"expected": "SUCCESS",
"argument": "Certificate.DomainValidationOptions[].ValidationStatus",
"state": "success"
},
{
"matcher": "pathAny",
"expected": "PENDING_VALIDATION",
"argument": "Certificate.DomainValidationOptions[].ValidationStatus",
"state": "retry"
},
{
"matcher": "path",
"expected": "FAILED",
"argument": "Certificate.Status",
"state": "failure"
},
{
"matcher": "error",
"expected": "ResourceNotFoundException",
"state": "failure"
}
]
}
}
}

View File

@@ -0,0 +1,82 @@
{
"pagination": {
"ListSkills": {
"result_key": "SkillSummaries",
"output_token": "NextToken",
"input_token": "NextToken",
"limit_key": "MaxResults"
},
"SearchUsers": {
"result_key": "Users",
"output_token": "NextToken",
"input_token": "NextToken",
"limit_key": "MaxResults"
},
"ListTags": {
"result_key": "Tags",
"output_token": "NextToken",
"input_token": "NextToken",
"limit_key": "MaxResults"
},
"SearchProfiles": {
"result_key": "Profiles",
"output_token": "NextToken",
"input_token": "NextToken",
"limit_key": "MaxResults"
},
"SearchSkillGroups": {
"result_key": "SkillGroups",
"output_token": "NextToken",
"input_token": "NextToken",
"limit_key": "MaxResults"
},
"SearchDevices": {
"result_key": "Devices",
"output_token": "NextToken",
"input_token": "NextToken",
"limit_key": "MaxResults"
},
"SearchRooms": {
"result_key": "Rooms",
"output_token": "NextToken",
"input_token": "NextToken",
"limit_key": "MaxResults"
},
"ListBusinessReportSchedules": {
"input_token": "NextToken",
"limit_key": "MaxResults",
"output_token": "NextToken",
"result_key": "BusinessReportSchedules"
},
"ListConferenceProviders": {
"input_token": "NextToken",
"limit_key": "MaxResults",
"output_token": "NextToken",
"result_key": "ConferenceProviders"
},
"ListDeviceEvents": {
"input_token": "NextToken",
"limit_key": "MaxResults",
"output_token": "NextToken",
"result_key": "DeviceEvents"
},
"ListSkillsStoreCategories": {
"input_token": "NextToken",
"limit_key": "MaxResults",
"output_token": "NextToken",
"result_key": "CategoryList"
},
"ListSkillsStoreSkillsByCategory": {
"input_token": "NextToken",
"limit_key": "MaxResults",
"output_token": "NextToken",
"result_key": "SkillsStoreSkills"
},
"ListSmartHomeAppliances": {
"input_token": "NextToken",
"limit_key": "MaxResults",
"output_token": "NextToken",
"result_key": "SmartHomeAppliances"
}
}
}

View File

@@ -0,0 +1,28 @@
{
"pagination": {
"ListApps": {
"input_token": "nextToken",
"limit_key": "maxResults",
"output_token": "nextToken",
"result_key": "apps"
},
"ListBranches": {
"input_token": "nextToken",
"limit_key": "maxResults",
"output_token": "nextToken",
"result_key": "branches"
},
"ListDomainAssociations": {
"input_token": "nextToken",
"limit_key": "maxResults",
"output_token": "nextToken",
"result_key": "domainAssociations"
},
"ListJobs": {
"input_token": "nextToken",
"limit_key": "maxResults",
"output_token": "nextToken",
"result_key": "jobSummaries"
}
}
}

View File

@@ -0,0 +1,5 @@
{
"version": "1.0",
"examples": {
}
}

View File

@@ -0,0 +1,112 @@
{
"pagination": {
"GetApiKeys": {
"input_token": "position",
"output_token": "position",
"limit_key": "limit",
"result_key": "items"
},
"GetBasePathMappings": {
"input_token": "position",
"output_token": "position",
"limit_key": "limit",
"result_key": "items"
},
"GetClientCertificates": {
"input_token": "position",
"output_token": "position",
"limit_key": "limit",
"result_key": "items"
},
"GetDeployments": {
"input_token": "position",
"output_token": "position",
"limit_key": "limit",
"result_key": "items"
},
"GetDomainNames": {
"input_token": "position",
"output_token": "position",
"limit_key": "limit",
"result_key": "items"
},
"GetModels": {
"input_token": "position",
"output_token": "position",
"limit_key": "limit",
"result_key": "items"
},
"GetResources": {
"input_token": "position",
"output_token": "position",
"limit_key": "limit",
"result_key": "items"
},
"GetRestApis": {
"input_token": "position",
"output_token": "position",
"limit_key": "limit",
"result_key": "items"
},
"GetUsage": {
"input_token": "position",
"output_token": "position",
"limit_key": "limit",
"result_key": "items"
},
"GetUsagePlans": {
"input_token": "position",
"output_token": "position",
"limit_key": "limit",
"result_key": "items"
},
"GetUsagePlanKeys": {
"input_token": "position",
"output_token": "position",
"limit_key": "limit",
"result_key": "items"
},
"GetVpcLinks": {
"input_token": "position",
"limit_key": "limit",
"output_token": "position",
"result_key": "items"
},
"GetAuthorizers": {
"input_token": "position",
"limit_key": "limit",
"output_token": "position",
"result_key": "items"
},
"GetDocumentationParts": {
"input_token": "position",
"limit_key": "limit",
"output_token": "position",
"result_key": "items"
},
"GetDocumentationVersions": {
"input_token": "position",
"limit_key": "limit",
"output_token": "position",
"result_key": "items"
},
"GetGatewayResponses": {
"input_token": "position",
"limit_key": "limit",
"output_token": "position",
"result_key": "items"
},
"GetRequestValidators": {
"input_token": "position",
"limit_key": "limit",
"output_token": "position",
"result_key": "items"
},
"GetSdkTypes": {
"input_token": "position",
"limit_key": "limit",
"output_token": "position",
"result_key": "items"
}
}
}

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,217 @@
{
"metadata" : {
"apiVersion" : "2018-11-29",
"endpointPrefix" : "execute-api",
"signingName" : "execute-api",
"serviceFullName" : "AmazonApiGatewayManagementApi",
"serviceId" : "ApiGatewayManagementApi",
"protocol" : "rest-json",
"jsonVersion" : "1.1",
"uid" : "apigatewaymanagementapi-2018-11-29",
"signatureVersion" : "v4"
},
"operations" : {
"DeleteConnection" : {
"name" : "DeleteConnection",
"http" : {
"method" : "DELETE",
"requestUri" : "/@connections/{connectionId}",
"responseCode" : 204
},
"input" : {
"shape" : "DeleteConnectionRequest"
},
"errors" : [ {
"shape" : "GoneException",
"documentation" : "<p>The connection with the provided id no longer exists.</p>"
}, {
"shape" : "LimitExceededException",
"documentation" : "<p>The client is sending more than the allowed number of requests per unit of time or the WebSocket client side buffer is full.</p>"
}, {
"shape" : "ForbiddenException",
"documentation" : "<p>The caller is not authorized to invoke this operation.</p>"
} ],
"documentation" : "<p>Delete the connection with the provided id.</p>"
},
"GetConnection" : {
"name" : "GetConnection",
"http" : {
"method" : "GET",
"requestUri" : "/@connections/{connectionId}",
"responseCode" : 200
},
"input" : {
"shape" : "GetConnectionRequest"
},
"output" : {
"shape" : "GetConnectionResponse"
},
"errors" : [ {
"shape" : "GoneException",
"documentation" : "<p>The connection with the provided id no longer exists.</p>"
}, {
"shape" : "LimitExceededException",
"documentation" : "<p>The client is sending more than the allowed number of requests per unit of time or the WebSocket client side buffer is full.</p>"
}, {
"shape" : "ForbiddenException",
"documentation" : "<p>The caller is not authorized to invoke this operation.</p>"
} ],
"documentation" : "<p>Get information about the connection with the provided id.</p>"
},
"PostToConnection" : {
"name" : "PostToConnection",
"http" : {
"method" : "POST",
"requestUri" : "/@connections/{connectionId}",
"responseCode" : 200
},
"input" : {
"shape" : "PostToConnectionRequest"
},
"errors" : [ {
"shape" : "GoneException",
"documentation" : "<p>The connection with the provided id no longer exists.</p>"
}, {
"shape" : "LimitExceededException",
"documentation" : "<p>The client is sending more than the allowed number of requests per unit of time or the WebSocket client side buffer is full.</p>"
}, {
"shape" : "PayloadTooLargeException",
"documentation" : "<p>The data has exceeded the maximum size allowed.</p>"
}, {
"shape" : "ForbiddenException",
"documentation" : "<p>The caller is not authorized to invoke this operation.</p>"
} ],
"documentation" : "<p>Sends the provided data to the specified connection.</p>"
}
},
"shapes" : {
"Data" : {
"type" : "blob",
"max" : 131072,
"documentation" : "<p>The data to be sent to the client specified by its connection id.</p>"
},
"DeleteConnectionRequest" : {
"type" : "structure",
"members" : {
"ConnectionId" : {
"shape" : "__string",
"location" : "uri",
"locationName" : "connectionId"
}
},
"required" : [ "ConnectionId" ]
},
"ForbiddenException" : {
"type" : "structure",
"members" : { },
"documentation" : "<p>The caller is not authorized to invoke this operation.</p>",
"exception" : true,
"error" : {
"httpStatusCode" : 403
}
},
"GetConnectionRequest" : {
"type" : "structure",
"members" : {
"ConnectionId" : {
"shape" : "__string",
"location" : "uri",
"locationName" : "connectionId"
}
},
"required" : [ "ConnectionId" ]
},
"GetConnectionResponse" : {
"type" : "structure",
"members" : {
"ConnectedAt" : {
"shape" : "__timestampIso8601",
"locationName" : "connectedAt",
"documentation" : "<p>The time in ISO 8601 format for when the connection was established.</p>"
},
"Identity" : {
"shape" : "Identity",
"locationName" : "identity"
},
"LastActiveAt" : {
"shape" : "__timestampIso8601",
"locationName" : "lastActiveAt",
"documentation" : "<p>The time in ISO 8601 format for when the connection was last active.</p>"
}
}
},
"GoneException" : {
"type" : "structure",
"members" : { },
"documentation" : "<p>The connection with the provided id no longer exists.</p>",
"exception" : true,
"error" : {
"httpStatusCode" : 410
}
},
"Identity" : {
"type" : "structure",
"members" : {
"SourceIp" : {
"shape" : "__string",
"locationName" : "sourceIp",
"documentation" : "<p>The source IP address of the TCP connection making the request to API Gateway.</p>"
},
"UserAgent" : {
"shape" : "__string",
"locationName" : "userAgent",
"documentation" : "<p>The User Agent of the API caller.</p>"
}
},
"required" : [ "SourceIp", "UserAgent" ]
},
"PayloadTooLargeException" : {
"type" : "structure",
"members" : {
"Message" : {
"shape" : "__string",
"locationName" : "message"
}
},
"documentation" : "<p>The data has exceeded the maximum size allowed.</p>",
"exception" : true,
"error" : {
"httpStatusCode" : 413
}
},
"PostToConnectionRequest" : {
"type" : "structure",
"members" : {
"Data" : {
"shape" : "Data",
"documentation" : "<p>The data to be sent to the client specified by its connection id.</p>"
},
"ConnectionId" : {
"shape" : "__string",
"location" : "uri",
"locationName" : "connectionId",
"documentation" : "<p>The identifier of the connection that a specific client is using.</p>"
}
},
"required" : [ "ConnectionId", "Data" ],
"payload" : "Data"
},
"LimitExceededException" : {
"type" : "structure",
"members" : { },
"documentation" : "<p>The client is sending more than the allowed number of requests per unit of time or the WebSocket client side buffer is full.</p>",
"exception" : true,
"error" : {
"httpStatusCode" : 429
}
},
"__string" : {
"type" : "string"
},
"__timestampIso8601" : {
"type" : "timestamp",
"timestampFormat" : "iso8601"
}
},
"documentation" : "<p>The Amazon API Gateway Management API allows you to directly manage runtime aspects of your deployed APIs. To use it, you must explicitly set the SDK's endpoint to point to the endpoint of your deployed API. The endpoint will be of the form https://{api-id}.execute-api.{region}.amazonaws.com/{stage}, or will be the endpoint corresponding to your API's custom domain and base path, if applicable.</p>"
}

View File

@@ -0,0 +1,64 @@
{
"pagination": {
"GetApis": {
"input_token": "NextToken",
"limit_key": "MaxResults",
"output_token": "NextToken",
"result_key": "Items"
},
"GetAuthorizers": {
"input_token": "NextToken",
"limit_key": "MaxResults",
"output_token": "NextToken",
"result_key": "Items"
},
"GetDeployments": {
"input_token": "NextToken",
"limit_key": "MaxResults",
"output_token": "NextToken",
"result_key": "Items"
},
"GetDomainNames": {
"input_token": "NextToken",
"limit_key": "MaxResults",
"output_token": "NextToken",
"result_key": "Items"
},
"GetIntegrationResponses": {
"input_token": "NextToken",
"limit_key": "MaxResults",
"output_token": "NextToken",
"result_key": "Items"
},
"GetIntegrations": {
"input_token": "NextToken",
"limit_key": "MaxResults",
"output_token": "NextToken",
"result_key": "Items"
},
"GetModels": {
"input_token": "NextToken",
"limit_key": "MaxResults",
"output_token": "NextToken",
"result_key": "Items"
},
"GetRouteResponses": {
"input_token": "NextToken",
"limit_key": "MaxResults",
"output_token": "NextToken",
"result_key": "Items"
},
"GetRoutes": {
"input_token": "NextToken",
"limit_key": "MaxResults",
"output_token": "NextToken",
"result_key": "Items"
},
"GetStages": {
"input_token": "NextToken",
"limit_key": "MaxResults",
"output_token": "NextToken",
"result_key": "Items"
}
}
}

View File

@@ -0,0 +1,257 @@
{
"version": "1.0",
"examples": {
"DeleteScalingPolicy": [
{
"input": {
"PolicyName": "web-app-cpu-lt-25",
"ResourceId": "service/default/web-app",
"ScalableDimension": "ecs:service:DesiredCount",
"ServiceNamespace": "ecs"
},
"output": {
},
"comments": {
"input": {
},
"output": {
}
},
"description": "This example deletes a scaling policy for the Amazon ECS service called web-app, which is running in the default cluster.",
"id": "to-delete-a-scaling-policy-1470863892689",
"title": "To delete a scaling policy"
}
],
"DeregisterScalableTarget": [
{
"input": {
"ResourceId": "service/default/web-app",
"ScalableDimension": "ecs:service:DesiredCount",
"ServiceNamespace": "ecs"
},
"output": {
},
"comments": {
"input": {
},
"output": {
}
},
"description": "This example deregisters a scalable target for an Amazon ECS service called web-app that is running in the default cluster.",
"id": "to-deregister-a-scalable-target-1470864164895",
"title": "To deregister a scalable target"
}
],
"DescribeScalableTargets": [
{
"input": {
"ServiceNamespace": "ecs"
},
"output": {
"ScalableTargets": [
{
"CreationTime": "2016-05-06T11:21:46.199Z",
"MaxCapacity": 10,
"MinCapacity": 1,
"ResourceId": "service/default/web-app",
"RoleARN": "arn:aws:iam::012345678910:role/ApplicationAutoscalingECSRole",
"ScalableDimension": "ecs:service:DesiredCount",
"ServiceNamespace": "ecs"
}
]
},
"comments": {
"input": {
},
"output": {
}
},
"description": "This example describes the scalable targets for the ecs service namespace.",
"id": "to-describe-scalable-targets-1470864286961",
"title": "To describe scalable targets"
}
],
"DescribeScalingActivities": [
{
"input": {
"ResourceId": "service/default/web-app",
"ScalableDimension": "ecs:service:DesiredCount",
"ServiceNamespace": "ecs"
},
"output": {
"ScalingActivities": [
{
"ActivityId": "e6c5f7d1-dbbb-4a3f-89b2-51f33e766399",
"Cause": "monitor alarm web-app-cpu-lt-25 in state ALARM triggered policy web-app-cpu-lt-25",
"Description": "Setting desired count to 1.",
"EndTime": "2016-05-06T16:04:32.111Z",
"ResourceId": "service/default/web-app",
"ScalableDimension": "ecs:service:DesiredCount",
"ServiceNamespace": "ecs",
"StartTime": "2016-05-06T16:03:58.171Z",
"StatusCode": "Successful",
"StatusMessage": "Successfully set desired count to 1. Change successfully fulfilled by ecs."
}
]
},
"comments": {
"input": {
},
"output": {
}
},
"description": "This example describes the scaling activities for an Amazon ECS service called web-app that is running in the default cluster.",
"id": "to-describe-scaling-activities-for-a-scalable-target-1470864398629",
"title": "To describe scaling activities for a scalable target"
}
],
"DescribeScalingPolicies": [
{
"input": {
"ServiceNamespace": "ecs"
},
"output": {
"NextToken": "",
"ScalingPolicies": [
{
"Alarms": [
{
"AlarmARN": "arn:aws:cloudwatch:us-west-2:012345678910:alarm:web-app-cpu-gt-75",
"AlarmName": "web-app-cpu-gt-75"
}
],
"CreationTime": "2016-05-06T12:11:39.230Z",
"PolicyARN": "arn:aws:autoscaling:us-west-2:012345678910:scalingPolicy:6d8972f3-efc8-437c-92d1-6270f29a66e7:resource/ecs/service/default/web-app:policyName/web-app-cpu-gt-75",
"PolicyName": "web-app-cpu-gt-75",
"PolicyType": "StepScaling",
"ResourceId": "service/default/web-app",
"ScalableDimension": "ecs:service:DesiredCount",
"ServiceNamespace": "ecs",
"StepScalingPolicyConfiguration": {
"AdjustmentType": "PercentChangeInCapacity",
"Cooldown": 60,
"StepAdjustments": [
{
"MetricIntervalLowerBound": 0,
"ScalingAdjustment": 200
}
]
}
}
]
},
"comments": {
"input": {
},
"output": {
}
},
"description": "This example describes the scaling policies for the ecs service namespace.",
"id": "to-describe-scaling-policies-1470864609734",
"title": "To describe scaling policies"
}
],
"PutScalingPolicy": [
{
"input": {
"PolicyName": "web-app-cpu-gt-75",
"PolicyType": "StepScaling",
"ResourceId": "service/default/web-app",
"ScalableDimension": "ecs:service:DesiredCount",
"ServiceNamespace": "ecs",
"StepScalingPolicyConfiguration": {
"AdjustmentType": "PercentChangeInCapacity",
"Cooldown": 60,
"StepAdjustments": [
{
"MetricIntervalLowerBound": 0,
"ScalingAdjustment": 200
}
]
}
},
"output": {
"PolicyARN": "arn:aws:autoscaling:us-west-2:012345678910:scalingPolicy:6d8972f3-efc8-437c-92d1-6270f29a66e7:resource/ecs/service/default/web-app:policyName/web-app-cpu-gt-75"
},
"comments": {
"input": {
},
"output": {
}
},
"description": "This example applies a scaling policy to an Amazon ECS service called web-app in the default cluster. The policy increases the desired count of the service by 200%, with a cool down period of 60 seconds.",
"id": "to-apply-a-scaling-policy-to-an-amazon-ecs-service-1470864779862",
"title": "To apply a scaling policy to an Amazon ECS service"
},
{
"input": {
"PolicyName": "fleet-cpu-gt-75",
"PolicyType": "StepScaling",
"ResourceId": "spot-fleet-request/sfr-45e69d8a-be48-4539-bbf3-3464e99c50c3",
"ScalableDimension": "ec2:spot-fleet-request:TargetCapacity",
"ServiceNamespace": "ec2",
"StepScalingPolicyConfiguration": {
"AdjustmentType": "PercentChangeInCapacity",
"Cooldown": 180,
"StepAdjustments": [
{
"MetricIntervalLowerBound": 0,
"ScalingAdjustment": 200
}
]
}
},
"output": {
"PolicyARN": "arn:aws:autoscaling:us-east-1:012345678910:scalingPolicy:89406401-0cb7-4130-b770-d97cca0e446b:resource/ec2/spot-fleet-request/sfr-45e69d8a-be48-4539-bbf3-3464e99c50c3:policyName/fleet-cpu-gt-75"
},
"comments": {
"input": {
},
"output": {
}
},
"description": "This example applies a scaling policy to an Amazon EC2 Spot fleet. The policy increases the target capacity of the spot fleet by 200%, with a cool down period of 180 seconds.\",\n ",
"id": "to-apply-a-scaling-policy-to-an-amazon-ec2-spot-fleet-1472073278469",
"title": "To apply a scaling policy to an Amazon EC2 Spot fleet"
}
],
"RegisterScalableTarget": [
{
"input": {
"MaxCapacity": 10,
"MinCapacity": 1,
"ResourceId": "service/default/web-app",
"RoleARN": "arn:aws:iam::012345678910:role/ApplicationAutoscalingECSRole",
"ScalableDimension": "ecs:service:DesiredCount",
"ServiceNamespace": "ecs"
},
"comments": {
"input": {
},
"output": {
}
},
"description": "This example registers a scalable target from an Amazon ECS service called web-app that is running on the default cluster, with a minimum desired count of 1 task and a maximum desired count of 10 tasks.",
"id": "to-register-a-new-scalable-target-1470864910380",
"title": "To register an ECS service as a scalable target"
},
{
"input": {
"MaxCapacity": 10,
"MinCapacity": 1,
"ResourceId": "spot-fleet-request/sfr-45e69d8a-be48-4539-bbf3-3464e99c50c3",
"RoleARN": "arn:aws:iam::012345678910:role/ApplicationAutoscalingSpotRole",
"ScalableDimension": "ec2:spot-fleet-request:TargetCapacity",
"ServiceNamespace": "ec2"
},
"output": {
},
"comments": {
},
"description": "This example registers a scalable target from an Amazon EC2 Spot fleet with a minimum target capacity of 1 and a maximum of 10.",
"id": "to-register-an-ec2-spot-fleet-as-a-scalable-target-1472072899649",
"title": "To register an EC2 Spot fleet as a scalable target"
}
]
}
}

View File

@@ -0,0 +1,28 @@
{
"pagination": {
"DescribeScalableTargets": {
"input_token": "NextToken",
"output_token": "NextToken",
"limit_key": "MaxResults",
"result_key": "ScalableTargets"
},
"DescribeScalingActivities": {
"input_token": "NextToken",
"output_token": "NextToken",
"limit_key": "MaxResults",
"result_key": "ScalingActivities"
},
"DescribeScalingPolicies": {
"input_token": "NextToken",
"output_token": "NextToken",
"limit_key": "MaxResults",
"result_key": "ScalingPolicies"
},
"DescribeScheduledActions": {
"input_token": "NextToken",
"limit_key": "MaxResults",
"output_token": "NextToken",
"result_key": "ScheduledActions"
}
}
}

View File

@@ -0,0 +1,28 @@
{
"pagination": {
"ListMeshes": {
"input_token": "nextToken",
"limit_key": "limit",
"output_token": "nextToken",
"result_key": "meshes"
},
"ListRoutes": {
"input_token": "nextToken",
"limit_key": "limit",
"output_token": "nextToken",
"result_key": "routes"
},
"ListVirtualNodes": {
"input_token": "nextToken",
"limit_key": "limit",
"output_token": "nextToken",
"result_key": "virtualNodes"
},
"ListVirtualRouters": {
"input_token": "nextToken",
"limit_key": "limit",
"output_token": "nextToken",
"result_key": "virtualRouters"
}
}
}

View File

@@ -0,0 +1,40 @@
{
"pagination": {
"ListMeshes": {
"input_token": "nextToken",
"limit_key": "limit",
"output_token": "nextToken",
"result_key": "meshes"
},
"ListRoutes": {
"input_token": "nextToken",
"limit_key": "limit",
"output_token": "nextToken",
"result_key": "routes"
},
"ListVirtualNodes": {
"input_token": "nextToken",
"limit_key": "limit",
"output_token": "nextToken",
"result_key": "virtualNodes"
},
"ListVirtualRouters": {
"input_token": "nextToken",
"limit_key": "limit",
"output_token": "nextToken",
"result_key": "virtualRouters"
},
"ListVirtualServices": {
"input_token": "nextToken",
"limit_key": "limit",
"output_token": "nextToken",
"result_key": "virtualServices"
},
"ListTagsForResource": {
"input_token": "nextToken",
"limit_key": "limit",
"output_token": "nextToken",
"result_key": "tags"
}
}
}

View File

@@ -0,0 +1,5 @@
{
"version": "1.0",
"examples": {
}
}

View File

@@ -0,0 +1,60 @@
{
"pagination": {
"DescribeDirectoryConfigs": {
"input_token": "NextToken",
"output_token": "NextToken",
"limit_key": "MaxResults",
"result_key": "DirectoryConfigs"
},
"DescribeFleets": {
"input_token": "NextToken",
"output_token": "NextToken",
"result_key": "Fleets"
},
"DescribeImageBuilders": {
"input_token": "NextToken",
"output_token": "NextToken",
"limit_key": "MaxResults",
"result_key": "ImageBuilders"
},
"DescribeImages": {
"input_token": "NextToken",
"output_token": "NextToken",
"limit_key": "MaxResults",
"result_key": "Images"
},
"DescribeSessions": {
"input_token": "NextToken",
"output_token": "NextToken",
"limit_key": "Limit",
"result_key": "Sessions"
},
"DescribeStacks": {
"input_token": "NextToken",
"output_token": "NextToken",
"result_key": "Stacks"
},
"DescribeUserStackAssociations": {
"input_token": "NextToken",
"output_token": "NextToken",
"limit_key": "MaxResults",
"result_key": "UserStackAssociations"
},
"DescribeUsers": {
"input_token": "NextToken",
"output_token": "NextToken",
"limit_key": "MaxResults",
"result_key": "Users"
},
"ListAssociatedFleets": {
"input_token": "NextToken",
"output_token": "NextToken",
"result_key": "Names"
},
"ListAssociatedStacks": {
"input_token": "NextToken",
"output_token": "NextToken",
"result_key": "Names"
}
}
}

View File

@@ -0,0 +1,55 @@
{
"version": 2,
"waiters": {
"FleetStarted": {
"delay": 30,
"maxAttempts": 40,
"operation": "DescribeFleets",
"acceptors": [
{
"state": "success",
"matcher": "pathAll",
"argument": "Fleets[].State",
"expected": "ACTIVE"
},
{
"state": "failure",
"matcher": "pathAny",
"argument": "Fleets[].State",
"expected": "PENDING_DEACTIVATE"
},
{
"state": "failure",
"matcher": "pathAny",
"argument": "Fleets[].State",
"expected": "INACTIVE"
}
]
},
"FleetStopped": {
"delay": 30,
"maxAttempts": 40,
"operation": "DescribeFleets",
"acceptors": [
{
"state": "success",
"matcher": "pathAll",
"argument": "Fleets[].State",
"expected": "INACTIVE"
},
{
"state": "failure",
"matcher": "pathAny",
"argument": "Fleets[].State",
"expected": "PENDING_ACTIVATE"
},
{
"state": "failure",
"matcher": "pathAny",
"argument": "Fleets[].State",
"expected": "ACTIVE"
}
]
}
}
}

View File

@@ -0,0 +1,46 @@
{
"pagination": {
"ListApiKeys": {
"input_token": "nextToken",
"limit_key": "maxResults",
"output_token": "nextToken",
"result_key": "apiKeys"
},
"ListDataSources": {
"input_token": "nextToken",
"limit_key": "maxResults",
"output_token": "nextToken",
"result_key": "dataSources"
},
"ListFunctions": {
"input_token": "nextToken",
"limit_key": "maxResults",
"output_token": "nextToken",
"result_key": "functions"
},
"ListGraphqlApis": {
"input_token": "nextToken",
"limit_key": "maxResults",
"output_token": "nextToken",
"result_key": "graphqlApis"
},
"ListResolvers": {
"input_token": "nextToken",
"limit_key": "maxResults",
"output_token": "nextToken",
"result_key": "resolvers"
},
"ListResolversByFunction": {
"input_token": "nextToken",
"limit_key": "maxResults",
"output_token": "nextToken",
"result_key": "resolvers"
},
"ListTypes": {
"input_token": "nextToken",
"limit_key": "maxResults",
"output_token": "nextToken",
"result_key": "types"
}
}
}

View File

@@ -0,0 +1,26 @@
{
"pagination": {
"ListNamedQueries": {
"input_token": "NextToken",
"output_token": "NextToken",
"limit_key": "MaxResults",
"result_key": "NamedQueryIds"
},
"ListQueryExecutions": {
"input_token": "NextToken",
"output_token": "NextToken",
"limit_key": "MaxResults",
"result_key": "QueryExecutionIds"
},
"GetQueryResults": {
"input_token": "NextToken",
"output_token": "NextToken",
"limit_key": "MaxResults",
"result_key": "ResultSet.Rows",
"non_aggregate_keys": [
"ResultSet.ResultSetMetadata",
"UpdateCount"
]
}
}
}

View File

@@ -0,0 +1,16 @@
{
"pagination": {
"DescribeScalingPlanResources": {
"input_token": "NextToken",
"limit_key": "MaxResults",
"output_token": "NextToken",
"result_key": "ScalingPlanResources"
},
"DescribeScalingPlans": {
"input_token": "NextToken",
"limit_key": "MaxResults",
"output_token": "NextToken",
"result_key": "ScalingPlans"
}
}
}

View File

@@ -0,0 +1,967 @@
{
"version":"2.0",
"metadata":{
"apiVersion":"2018-01-06",
"endpointPrefix":"autoscaling",
"jsonVersion":"1.1",
"protocol":"json",
"serviceFullName":"AWS Auto Scaling Plans",
"serviceId":"Auto Scaling Plans",
"signatureVersion":"v4",
"signingName":"autoscaling-plans",
"targetPrefix":"AnyScaleScalingPlannerFrontendService",
"uid":"autoscaling-plans-2018-01-06"
},
"operations":{
"CreateScalingPlan":{
"name":"CreateScalingPlan",
"http":{
"method":"POST",
"requestUri":"/"
},
"input":{"shape":"CreateScalingPlanRequest"},
"output":{"shape":"CreateScalingPlanResponse"},
"errors":[
{"shape":"ValidationException"},
{"shape":"LimitExceededException"},
{"shape":"ConcurrentUpdateException"},
{"shape":"InternalServiceException"}
],
"documentation":"<p>Creates a scaling plan.</p>"
},
"DeleteScalingPlan":{
"name":"DeleteScalingPlan",
"http":{
"method":"POST",
"requestUri":"/"
},
"input":{"shape":"DeleteScalingPlanRequest"},
"output":{"shape":"DeleteScalingPlanResponse"},
"errors":[
{"shape":"ValidationException"},
{"shape":"ObjectNotFoundException"},
{"shape":"ConcurrentUpdateException"},
{"shape":"InternalServiceException"}
],
"documentation":"<p>Deletes the specified scaling plan.</p> <p>Deleting a scaling plan deletes the underlying <a>ScalingInstruction</a> for all of the scalable resources that are covered by the plan.</p> <p>If the plan has launched resources or has scaling activities in progress, you must delete those resources separately.</p>"
},
"DescribeScalingPlanResources":{
"name":"DescribeScalingPlanResources",
"http":{
"method":"POST",
"requestUri":"/"
},
"input":{"shape":"DescribeScalingPlanResourcesRequest"},
"output":{"shape":"DescribeScalingPlanResourcesResponse"},
"errors":[
{"shape":"ValidationException"},
{"shape":"InvalidNextTokenException"},
{"shape":"ConcurrentUpdateException"},
{"shape":"InternalServiceException"}
],
"documentation":"<p>Describes the scalable resources in the specified scaling plan.</p>"
},
"DescribeScalingPlans":{
"name":"DescribeScalingPlans",
"http":{
"method":"POST",
"requestUri":"/"
},
"input":{"shape":"DescribeScalingPlansRequest"},
"output":{"shape":"DescribeScalingPlansResponse"},
"errors":[
{"shape":"ValidationException"},
{"shape":"InvalidNextTokenException"},
{"shape":"ConcurrentUpdateException"},
{"shape":"InternalServiceException"}
],
"documentation":"<p>Describes one or more of your scaling plans.</p>"
},
"GetScalingPlanResourceForecastData":{
"name":"GetScalingPlanResourceForecastData",
"http":{
"method":"POST",
"requestUri":"/"
},
"input":{"shape":"GetScalingPlanResourceForecastDataRequest"},
"output":{"shape":"GetScalingPlanResourceForecastDataResponse"},
"errors":[
{"shape":"ValidationException"},
{"shape":"InternalServiceException"}
],
"documentation":"<p>Retrieves the forecast data for a scalable resource.</p> <p>Capacity forecasts are represented as predicted values, or data points, that are calculated using historical data points from a specified CloudWatch load metric. Data points are available for up to 56 days. </p>"
},
"UpdateScalingPlan":{
"name":"UpdateScalingPlan",
"http":{
"method":"POST",
"requestUri":"/"
},
"input":{"shape":"UpdateScalingPlanRequest"},
"output":{"shape":"UpdateScalingPlanResponse"},
"errors":[
{"shape":"ValidationException"},
{"shape":"ConcurrentUpdateException"},
{"shape":"InternalServiceException"},
{"shape":"ObjectNotFoundException"}
],
"documentation":"<p>Updates the specified scaling plan.</p> <p>You cannot update a scaling plan if it is in the process of being created, updated, or deleted.</p>"
}
},
"shapes":{
"ApplicationSource":{
"type":"structure",
"members":{
"CloudFormationStackARN":{
"shape":"XmlString",
"documentation":"<p>The Amazon Resource Name (ARN) of a AWS CloudFormation stack.</p>"
},
"TagFilters":{
"shape":"TagFilters",
"documentation":"<p>A set of tags (up to 50).</p>"
}
},
"documentation":"<p>Represents an application source.</p>"
},
"ApplicationSources":{
"type":"list",
"member":{"shape":"ApplicationSource"}
},
"ConcurrentUpdateException":{
"type":"structure",
"members":{
"Message":{"shape":"ErrorMessage"}
},
"documentation":"<p>Concurrent updates caused an exception, for example, if you request an update to a scaling plan that already has a pending update.</p>",
"exception":true
},
"Cooldown":{"type":"integer"},
"CreateScalingPlanRequest":{
"type":"structure",
"required":[
"ScalingPlanName",
"ApplicationSource",
"ScalingInstructions"
],
"members":{
"ScalingPlanName":{
"shape":"ScalingPlanName",
"documentation":"<p>The name of the scaling plan. Names cannot contain vertical bars, colons, or forward slashes.</p>"
},
"ApplicationSource":{
"shape":"ApplicationSource",
"documentation":"<p>A CloudFormation stack or set of tags. You can create one scaling plan per application source.</p>"
},
"ScalingInstructions":{
"shape":"ScalingInstructions",
"documentation":"<p>The scaling instructions.</p>"
}
}
},
"CreateScalingPlanResponse":{
"type":"structure",
"required":["ScalingPlanVersion"],
"members":{
"ScalingPlanVersion":{
"shape":"ScalingPlanVersion",
"documentation":"<p>The version number of the scaling plan. This value is always 1.</p> <p>Currently, you cannot specify multiple scaling plan versions.</p>"
}
}
},
"CustomizedLoadMetricSpecification":{
"type":"structure",
"required":[
"MetricName",
"Namespace",
"Statistic"
],
"members":{
"MetricName":{
"shape":"MetricName",
"documentation":"<p>The name of the metric.</p>"
},
"Namespace":{
"shape":"MetricNamespace",
"documentation":"<p>The namespace of the metric.</p>"
},
"Dimensions":{
"shape":"MetricDimensions",
"documentation":"<p>The dimensions of the metric.</p> <p>Conditional: If you published your metric with dimensions, you must specify the same dimensions in your customized load metric specification.</p>"
},
"Statistic":{
"shape":"MetricStatistic",
"documentation":"<p>The statistic of the metric. Currently, the value must always be <code>Sum</code>. </p>"
},
"Unit":{
"shape":"MetricUnit",
"documentation":"<p>The unit of the metric.</p>"
}
},
"documentation":"<p>Represents a CloudWatch metric of your choosing that can be used for predictive scaling. </p> <p>For predictive scaling to work with a customized load metric specification, AWS Auto Scaling needs access to the <code>Sum</code> and <code>Average</code> statistics that CloudWatch computes from metric data. Statistics are calculations used to aggregate data over specified time periods.</p> <p>When you choose a load metric, make sure that the required <code>Sum</code> and <code>Average</code> statistics for your metric are available in CloudWatch and that they provide relevant data for predictive scaling. The <code>Sum</code> statistic must represent the total load on the resource, and the <code>Average</code> statistic must represent the average load per capacity unit of the resource. For example, there is a metric that counts the number of requests processed by your Auto Scaling group. If the <code>Sum</code> statistic represents the total request count processed by the group, then the <code>Average</code> statistic for the specified metric must represent the average request count processed by each instance of the group.</p> <p>For information about terminology, available metrics, or how to publish new metrics, see <a href=\"https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/cloudwatch_concepts.html\">Amazon CloudWatch Concepts</a> in the <i>Amazon CloudWatch User Guide</i>. </p>"
},
"CustomizedScalingMetricSpecification":{
"type":"structure",
"required":[
"MetricName",
"Namespace",
"Statistic"
],
"members":{
"MetricName":{
"shape":"MetricName",
"documentation":"<p>The name of the metric.</p>"
},
"Namespace":{
"shape":"MetricNamespace",
"documentation":"<p>The namespace of the metric.</p>"
},
"Dimensions":{
"shape":"MetricDimensions",
"documentation":"<p>The dimensions of the metric.</p> <p>Conditional: If you published your metric with dimensions, you must specify the same dimensions in your customized scaling metric specification.</p>"
},
"Statistic":{
"shape":"MetricStatistic",
"documentation":"<p>The statistic of the metric.</p>"
},
"Unit":{
"shape":"MetricUnit",
"documentation":"<p>The unit of the metric. </p>"
}
},
"documentation":"<p>Represents a CloudWatch metric of your choosing that can be used for dynamic scaling as part of a target tracking scaling policy. </p> <p>To create your customized scaling metric specification:</p> <ul> <li> <p>Add values for each required parameter from CloudWatch. You can use an existing metric, or a new metric that you create. To use your own metric, you must first publish the metric to CloudWatch. For more information, see <a href=\"https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/publishingMetrics.html\">Publish Custom Metrics</a> in the <i>Amazon CloudWatch User Guide</i>.</p> </li> <li> <p>Choose a metric that changes proportionally with capacity. The value of the metric should increase or decrease in inverse proportion to the number of capacity units. That is, the value of the metric should decrease when capacity increases. </p> </li> </ul> <p>For more information about CloudWatch, see <a href=\"https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/cloudwatch_concepts.html\">Amazon CloudWatch Concepts</a>. </p>"
},
"Datapoint":{
"type":"structure",
"members":{
"Timestamp":{
"shape":"TimestampType",
"documentation":"<p>The time stamp for the data point in UTC format.</p>"
},
"Value":{
"shape":"MetricScale",
"documentation":"<p>The value of the data point.</p>"
}
},
"documentation":"<p>Represents a single value in the forecast data used for predictive scaling.</p>"
},
"Datapoints":{
"type":"list",
"member":{"shape":"Datapoint"}
},
"DeleteScalingPlanRequest":{
"type":"structure",
"required":[
"ScalingPlanName",
"ScalingPlanVersion"
],
"members":{
"ScalingPlanName":{
"shape":"ScalingPlanName",
"documentation":"<p>The name of the scaling plan.</p>"
},
"ScalingPlanVersion":{
"shape":"ScalingPlanVersion",
"documentation":"<p>The version number of the scaling plan.</p>"
}
}
},
"DeleteScalingPlanResponse":{
"type":"structure",
"members":{
}
},
"DescribeScalingPlanResourcesRequest":{
"type":"structure",
"required":[
"ScalingPlanName",
"ScalingPlanVersion"
],
"members":{
"ScalingPlanName":{
"shape":"ScalingPlanName",
"documentation":"<p>The name of the scaling plan.</p>"
},
"ScalingPlanVersion":{
"shape":"ScalingPlanVersion",
"documentation":"<p>The version number of the scaling plan.</p>"
},
"MaxResults":{
"shape":"MaxResults",
"documentation":"<p>The maximum number of scalable resources to return. The value must be between 1 and 50. The default value is 50.</p>"
},
"NextToken":{
"shape":"NextToken",
"documentation":"<p>The token for the next set of results.</p>"
}
}
},
"DescribeScalingPlanResourcesResponse":{
"type":"structure",
"members":{
"ScalingPlanResources":{
"shape":"ScalingPlanResources",
"documentation":"<p>Information about the scalable resources.</p>"
},
"NextToken":{
"shape":"NextToken",
"documentation":"<p>The token required to get the next set of results. This value is <code>null</code> if there are no more results to return.</p>"
}
}
},
"DescribeScalingPlansRequest":{
"type":"structure",
"members":{
"ScalingPlanNames":{
"shape":"ScalingPlanNames",
"documentation":"<p>The names of the scaling plans (up to 10). If you specify application sources, you cannot specify scaling plan names.</p>"
},
"ScalingPlanVersion":{
"shape":"ScalingPlanVersion",
"documentation":"<p>The version number of the scaling plan. If you specify a scaling plan version, you must also specify a scaling plan name.</p>"
},
"ApplicationSources":{
"shape":"ApplicationSources",
"documentation":"<p>The sources for the applications (up to 10). If you specify scaling plan names, you cannot specify application sources.</p>"
},
"MaxResults":{
"shape":"MaxResults",
"documentation":"<p>The maximum number of scalable resources to return. This value can be between 1 and 50. The default value is 50.</p>"
},
"NextToken":{
"shape":"NextToken",
"documentation":"<p>The token for the next set of results.</p>"
}
}
},
"DescribeScalingPlansResponse":{
"type":"structure",
"members":{
"ScalingPlans":{
"shape":"ScalingPlans",
"documentation":"<p>Information about the scaling plans.</p>"
},
"NextToken":{
"shape":"NextToken",
"documentation":"<p>The token required to get the next set of results. This value is <code>null</code> if there are no more results to return.</p>"
}
}
},
"DisableDynamicScaling":{"type":"boolean"},
"DisableScaleIn":{"type":"boolean"},
"ErrorMessage":{"type":"string"},
"ForecastDataType":{
"type":"string",
"enum":[
"CapacityForecast",
"LoadForecast",
"ScheduledActionMinCapacity",
"ScheduledActionMaxCapacity"
]
},
"GetScalingPlanResourceForecastDataRequest":{
"type":"structure",
"required":[
"ScalingPlanName",
"ScalingPlanVersion",
"ServiceNamespace",
"ResourceId",
"ScalableDimension",
"ForecastDataType",
"StartTime",
"EndTime"
],
"members":{
"ScalingPlanName":{
"shape":"ScalingPlanName",
"documentation":"<p>The name of the scaling plan.</p>"
},
"ScalingPlanVersion":{
"shape":"ScalingPlanVersion",
"documentation":"<p>The version number of the scaling plan.</p>"
},
"ServiceNamespace":{
"shape":"ServiceNamespace",
"documentation":"<p>The namespace of the AWS service.</p>"
},
"ResourceId":{
"shape":"XmlString",
"documentation":"<p>The ID of the resource. This string consists of the resource type and unique identifier. </p> <ul> <li> <p>Auto Scaling group - The resource type is <code>autoScalingGroup</code> and the unique identifier is the name of the Auto Scaling group. Example: <code>autoScalingGroup/my-asg</code>.</p> </li> <li> <p>ECS service - The resource type is <code>service</code> and the unique identifier is the cluster name and service name. Example: <code>service/default/sample-webapp</code>.</p> </li> <li> <p>Spot Fleet request - The resource type is <code>spot-fleet-request</code> and the unique identifier is the Spot Fleet request ID. Example: <code>spot-fleet-request/sfr-73fbd2ce-aa30-494c-8788-1cee4EXAMPLE</code>.</p> </li> <li> <p>DynamoDB table - The resource type is <code>table</code> and the unique identifier is the resource ID. Example: <code>table/my-table</code>.</p> </li> <li> <p>DynamoDB global secondary index - The resource type is <code>index</code> and the unique identifier is the resource ID. Example: <code>table/my-table/index/my-table-index</code>.</p> </li> <li> <p>Aurora DB cluster - The resource type is <code>cluster</code> and the unique identifier is the cluster name. Example: <code>cluster:my-db-cluster</code>.</p> </li> </ul>"
},
"ScalableDimension":{
"shape":"ScalableDimension",
"documentation":"<p>The scalable dimension for the resource.</p>"
},
"ForecastDataType":{
"shape":"ForecastDataType",
"documentation":"<p>The type of forecast data to get.</p> <ul> <li> <p> <code>LoadForecast</code>: The load metric forecast. </p> </li> <li> <p> <code>CapacityForecast</code>: The capacity forecast. </p> </li> <li> <p> <code>ScheduledActionMinCapacity</code>: The minimum capacity for each scheduled scaling action. This data is calculated as the larger of two values: the capacity forecast or the minimum capacity in the scaling instruction.</p> </li> <li> <p> <code>ScheduledActionMaxCapacity</code>: The maximum capacity for each scheduled scaling action. The calculation used is determined by the predictive scaling maximum capacity behavior setting in the scaling instruction.</p> </li> </ul>"
},
"StartTime":{
"shape":"TimestampType",
"documentation":"<p>The inclusive start time of the time range for the forecast data to get. The date and time can be at most 56 days before the current date and time. </p>"
},
"EndTime":{
"shape":"TimestampType",
"documentation":"<p>The exclusive end time of the time range for the forecast data to get. The maximum time duration between the start and end time is seven days. </p> <p>Although this parameter can accept a date and time that is more than two days in the future, the availability of forecast data has limits. AWS Auto Scaling only issues forecasts for periods of two days in advance.</p>"
}
}
},
"GetScalingPlanResourceForecastDataResponse":{
"type":"structure",
"required":["Datapoints"],
"members":{
"Datapoints":{
"shape":"Datapoints",
"documentation":"<p>The data points to return.</p>"
}
}
},
"InternalServiceException":{
"type":"structure",
"members":{
"Message":{"shape":"ErrorMessage"}
},
"documentation":"<p>The service encountered an internal error.</p>",
"exception":true
},
"InvalidNextTokenException":{
"type":"structure",
"members":{
"Message":{"shape":"ErrorMessage"}
},
"documentation":"<p>The token provided is not valid.</p>",
"exception":true
},
"LimitExceededException":{
"type":"structure",
"members":{
"Message":{"shape":"ErrorMessage"}
},
"documentation":"<p>Your account exceeded a limit. This exception is thrown when a per-account resource limit is exceeded.</p>",
"exception":true
},
"LoadMetricType":{
"type":"string",
"enum":[
"ASGTotalCPUUtilization",
"ASGTotalNetworkIn",
"ASGTotalNetworkOut",
"ALBTargetGroupRequestCount"
]
},
"MaxResults":{"type":"integer"},
"MetricDimension":{
"type":"structure",
"required":[
"Name",
"Value"
],
"members":{
"Name":{
"shape":"MetricDimensionName",
"documentation":"<p>The name of the dimension.</p>"
},
"Value":{
"shape":"MetricDimensionValue",
"documentation":"<p>The value of the dimension.</p>"
}
},
"documentation":"<p>Represents a dimension for a customized metric.</p>"
},
"MetricDimensionName":{"type":"string"},
"MetricDimensionValue":{"type":"string"},
"MetricDimensions":{
"type":"list",
"member":{"shape":"MetricDimension"}
},
"MetricName":{"type":"string"},
"MetricNamespace":{"type":"string"},
"MetricScale":{"type":"double"},
"MetricStatistic":{
"type":"string",
"enum":[
"Average",
"Minimum",
"Maximum",
"SampleCount",
"Sum"
]
},
"MetricUnit":{"type":"string"},
"NextToken":{"type":"string"},
"ObjectNotFoundException":{
"type":"structure",
"members":{
"Message":{"shape":"ErrorMessage"}
},
"documentation":"<p>The specified object could not be found.</p>",
"exception":true
},
"PolicyName":{
"type":"string",
"max":256,
"min":1,
"pattern":"\\p{Print}+"
},
"PolicyType":{
"type":"string",
"enum":["TargetTrackingScaling"]
},
"PredefinedLoadMetricSpecification":{
"type":"structure",
"required":["PredefinedLoadMetricType"],
"members":{
"PredefinedLoadMetricType":{
"shape":"LoadMetricType",
"documentation":"<p>The metric type.</p>"
},
"ResourceLabel":{
"shape":"ResourceLabel",
"documentation":"<p>Identifies the resource associated with the metric type. You can't specify a resource label unless the metric type is <code>ALBRequestCountPerTarget</code> and there is a target group for an Application Load Balancer attached to the Auto Scaling group.</p> <p>The format is app/&lt;load-balancer-name&gt;/&lt;load-balancer-id&gt;/targetgroup/&lt;target-group-name&gt;/&lt;target-group-id&gt;, where:</p> <ul> <li> <p>app/&lt;load-balancer-name&gt;/&lt;load-balancer-id&gt; is the final portion of the load balancer ARN.</p> </li> <li> <p>targetgroup/&lt;target-group-name&gt;/&lt;target-group-id&gt; is the final portion of the target group ARN.</p> </li> </ul>"
}
},
"documentation":"<p>Represents a predefined metric that can be used for predictive scaling. </p>"
},
"PredefinedScalingMetricSpecification":{
"type":"structure",
"required":["PredefinedScalingMetricType"],
"members":{
"PredefinedScalingMetricType":{
"shape":"ScalingMetricType",
"documentation":"<p>The metric type. The <code>ALBRequestCountPerTarget</code> metric type applies only to Auto Scaling groups, Spot Fleet requests, and ECS services.</p>"
},
"ResourceLabel":{
"shape":"ResourceLabel",
"documentation":"<p>Identifies the resource associated with the metric type. You can't specify a resource label unless the metric type is <code>ALBRequestCountPerTarget</code> and there is a target group for an Application Load Balancer attached to the Auto Scaling group, Spot Fleet request, or ECS service.</p> <p>The format is app/&lt;load-balancer-name&gt;/&lt;load-balancer-id&gt;/targetgroup/&lt;target-group-name&gt;/&lt;target-group-id&gt;, where:</p> <ul> <li> <p>app/&lt;load-balancer-name&gt;/&lt;load-balancer-id&gt; is the final portion of the load balancer ARN.</p> </li> <li> <p>targetgroup/&lt;target-group-name&gt;/&lt;target-group-id&gt; is the final portion of the target group ARN.</p> </li> </ul>"
}
},
"documentation":"<p>Represents a predefined metric that can be used for dynamic scaling as part of a target tracking scaling policy.</p>"
},
"PredictiveScalingMaxCapacityBehavior":{
"type":"string",
"enum":[
"SetForecastCapacityToMaxCapacity",
"SetMaxCapacityToForecastCapacity",
"SetMaxCapacityAboveForecastCapacity"
]
},
"PredictiveScalingMode":{
"type":"string",
"enum":[
"ForecastAndScale",
"ForecastOnly"
]
},
"ResourceCapacity":{"type":"integer"},
"ResourceIdMaxLen1600":{
"type":"string",
"max":1600,
"min":1,
"pattern":"[\\u0020-\\uD7FF\\uE000-\\uFFFD\\uD800\\uDC00-\\uDBFF\\uDFFF\\r\\n\\t]*"
},
"ResourceLabel":{
"type":"string",
"max":1023,
"min":1
},
"ScalableDimension":{
"type":"string",
"enum":[
"autoscaling:autoScalingGroup:DesiredCapacity",
"ecs:service:DesiredCount",
"ec2:spot-fleet-request:TargetCapacity",
"rds:cluster:ReadReplicaCount",
"dynamodb:table:ReadCapacityUnits",
"dynamodb:table:WriteCapacityUnits",
"dynamodb:index:ReadCapacityUnits",
"dynamodb:index:WriteCapacityUnits"
]
},
"ScalingInstruction":{
"type":"structure",
"required":[
"ServiceNamespace",
"ResourceId",
"ScalableDimension",
"MinCapacity",
"MaxCapacity",
"TargetTrackingConfigurations"
],
"members":{
"ServiceNamespace":{
"shape":"ServiceNamespace",
"documentation":"<p>The namespace of the AWS service.</p>"
},
"ResourceId":{
"shape":"ResourceIdMaxLen1600",
"documentation":"<p>The ID of the resource. This string consists of the resource type and unique identifier.</p> <ul> <li> <p>Auto Scaling group - The resource type is <code>autoScalingGroup</code> and the unique identifier is the name of the Auto Scaling group. Example: <code>autoScalingGroup/my-asg</code>.</p> </li> <li> <p>ECS service - The resource type is <code>service</code> and the unique identifier is the cluster name and service name. Example: <code>service/default/sample-webapp</code>.</p> </li> <li> <p>Spot Fleet request - The resource type is <code>spot-fleet-request</code> and the unique identifier is the Spot Fleet request ID. Example: <code>spot-fleet-request/sfr-73fbd2ce-aa30-494c-8788-1cee4EXAMPLE</code>.</p> </li> <li> <p>DynamoDB table - The resource type is <code>table</code> and the unique identifier is the resource ID. Example: <code>table/my-table</code>.</p> </li> <li> <p>DynamoDB global secondary index - The resource type is <code>index</code> and the unique identifier is the resource ID. Example: <code>table/my-table/index/my-table-index</code>.</p> </li> <li> <p>Aurora DB cluster - The resource type is <code>cluster</code> and the unique identifier is the cluster name. Example: <code>cluster:my-db-cluster</code>.</p> </li> </ul>"
},
"ScalableDimension":{
"shape":"ScalableDimension",
"documentation":"<p>The scalable dimension associated with the resource.</p> <ul> <li> <p> <code>autoscaling:autoScalingGroup:DesiredCapacity</code> - The desired capacity of an Auto Scaling group.</p> </li> <li> <p> <code>ecs:service:DesiredCount</code> - The desired task count of an ECS service.</p> </li> <li> <p> <code>ec2:spot-fleet-request:TargetCapacity</code> - The target capacity of a Spot Fleet request.</p> </li> <li> <p> <code>dynamodb:table:ReadCapacityUnits</code> - The provisioned read capacity for a DynamoDB table.</p> </li> <li> <p> <code>dynamodb:table:WriteCapacityUnits</code> - The provisioned write capacity for a DynamoDB table.</p> </li> <li> <p> <code>dynamodb:index:ReadCapacityUnits</code> - The provisioned read capacity for a DynamoDB global secondary index.</p> </li> <li> <p> <code>dynamodb:index:WriteCapacityUnits</code> - The provisioned write capacity for a DynamoDB global secondary index.</p> </li> <li> <p> <code>rds:cluster:ReadReplicaCount</code> - The count of Aurora Replicas in an Aurora DB cluster. Available for Aurora MySQL-compatible edition and Aurora PostgreSQL-compatible edition.</p> </li> </ul>"
},
"MinCapacity":{
"shape":"ResourceCapacity",
"documentation":"<p>The minimum capacity of the resource. </p>"
},
"MaxCapacity":{
"shape":"ResourceCapacity",
"documentation":"<p>The maximum capacity of the resource. The exception to this upper limit is if you specify a non-default setting for <b>PredictiveScalingMaxCapacityBehavior</b>. </p>"
},
"TargetTrackingConfigurations":{
"shape":"TargetTrackingConfigurations",
"documentation":"<p>The structure that defines new target tracking configurations (up to 10). Each of these structures includes a specific scaling metric and a target value for the metric, along with various parameters to use with dynamic scaling. </p> <p>With predictive scaling and dynamic scaling, the resource scales based on the target tracking configuration that provides the largest capacity for both scale in and scale out. </p> <p>Condition: The scaling metric must be unique across target tracking configurations.</p>"
},
"PredefinedLoadMetricSpecification":{
"shape":"PredefinedLoadMetricSpecification",
"documentation":"<p>The predefined load metric to use for predictive scaling. This parameter or a <b>CustomizedLoadMetricSpecification</b> is required when configuring predictive scaling, and cannot be used otherwise. </p>"
},
"CustomizedLoadMetricSpecification":{
"shape":"CustomizedLoadMetricSpecification",
"documentation":"<p>The customized load metric to use for predictive scaling. This parameter or a <b>PredefinedLoadMetricSpecification</b> is required when configuring predictive scaling, and cannot be used otherwise. </p>"
},
"ScheduledActionBufferTime":{
"shape":"ScheduledActionBufferTime",
"documentation":"<p>The amount of time, in seconds, to buffer the run time of scheduled scaling actions when scaling out. For example, if the forecast says to add capacity at 10:00 AM, and the buffer time is 5 minutes, then the run time of the corresponding scheduled scaling action will be 9:55 AM. The intention is to give resources time to be provisioned. For example, it can take a few minutes to launch an EC2 instance. The actual amount of time required depends on several factors, such as the size of the instance and whether there are startup scripts to complete. </p> <p>The value must be less than the forecast interval duration of 3600 seconds (60 minutes). The default is 300 seconds. </p> <p>Only valid when configuring predictive scaling. </p>"
},
"PredictiveScalingMaxCapacityBehavior":{
"shape":"PredictiveScalingMaxCapacityBehavior",
"documentation":"<p>Defines the behavior that should be applied if the forecast capacity approaches or exceeds the maximum capacity specified for the resource. The default value is <code>SetForecastCapacityToMaxCapacity</code>.</p> <p>The following are possible values:</p> <ul> <li> <p> <code>SetForecastCapacityToMaxCapacity</code> - AWS Auto Scaling cannot scale resource capacity higher than the maximum capacity. The maximum capacity is enforced as a hard limit. </p> </li> <li> <p> <code>SetMaxCapacityToForecastCapacity</code> - AWS Auto Scaling may scale resource capacity higher than the maximum capacity to equal but not exceed forecast capacity.</p> </li> <li> <p> <code>SetMaxCapacityAboveForecastCapacity</code> - AWS Auto Scaling may scale resource capacity higher than the maximum capacity by a specified buffer value. The intention is to give the target tracking scaling policy extra capacity if unexpected traffic occurs. </p> </li> </ul> <p>Only valid when configuring predictive scaling.</p>"
},
"PredictiveScalingMaxCapacityBuffer":{
"shape":"ResourceCapacity",
"documentation":"<p>The size of the capacity buffer to use when the forecast capacity is close to or exceeds the maximum capacity. The value is specified as a percentage relative to the forecast capacity. For example, if the buffer is 10, this means a 10 percent buffer, such that if the forecast capacity is 50, and the maximum capacity is 40, then the effective maximum capacity is 55.</p> <p>Only valid when configuring predictive scaling. Required if the <b>PredictiveScalingMaxCapacityBehavior</b> is set to <code>SetMaxCapacityAboveForecastCapacity</code>, and cannot be used otherwise.</p> <p>The range is 1-100.</p>"
},
"PredictiveScalingMode":{
"shape":"PredictiveScalingMode",
"documentation":"<p>The predictive scaling mode. The default value is <code>ForecastAndScale</code>. Otherwise, AWS Auto Scaling forecasts capacity but does not create any scheduled scaling actions based on the capacity forecast. </p>"
},
"ScalingPolicyUpdateBehavior":{
"shape":"ScalingPolicyUpdateBehavior",
"documentation":"<p>Controls whether a resource's externally created scaling policies are kept or replaced. </p> <p>The default value is <code>KeepExternalPolicies</code>. If the parameter is set to <code>ReplaceExternalPolicies</code>, any scaling policies that are external to AWS Auto Scaling are deleted and new target tracking scaling policies created. </p> <p>Only valid when configuring dynamic scaling. </p> <p>Condition: The number of existing policies to be replaced must be less than or equal to 50. If there are more than 50 policies to be replaced, AWS Auto Scaling keeps all existing policies and does not create new ones.</p>"
},
"DisableDynamicScaling":{
"shape":"DisableDynamicScaling",
"documentation":"<p>Controls whether dynamic scaling by AWS Auto Scaling is disabled. When dynamic scaling is enabled, AWS Auto Scaling creates target tracking scaling policies based on the specified target tracking configurations. </p> <p>The default is enabled (<code>false</code>). </p>"
}
},
"documentation":"<p>Describes a scaling instruction for a scalable resource.</p> <p>The scaling instruction is used in combination with a scaling plan, which is a set of instructions for configuring dynamic scaling and predictive scaling for the scalable resources in your application. Each scaling instruction applies to one resource.</p> <p>AWS Auto Scaling creates target tracking scaling policies based on the scaling instructions. Target tracking scaling policies adjust the capacity of your scalable resource as required to maintain resource utilization at the target value that you specified. </p> <p>AWS Auto Scaling also configures predictive scaling for your Amazon EC2 Auto Scaling groups using a subset of parameters, including the load metric, the scaling metric, the target value for the scaling metric, the predictive scaling mode (forecast and scale or forecast only), and the desired behavior when the forecast capacity exceeds the maximum capacity of the resource. With predictive scaling, AWS Auto Scaling generates forecasts with traffic predictions for the two days ahead and schedules scaling actions that proactively add and remove resource capacity to match the forecast. </p> <p>We recommend waiting a minimum of 24 hours after creating an Auto Scaling group to configure predictive scaling. At minimum, there must be 24 hours of historical data to generate a forecast.</p> <p>For more information, see <a href=\"https://docs.aws.amazon.com/autoscaling/plans/userguide/auto-scaling-getting-started.html\">Getting Started with AWS Auto Scaling</a>.</p>"
},
"ScalingInstructions":{
"type":"list",
"member":{"shape":"ScalingInstruction"}
},
"ScalingMetricType":{
"type":"string",
"enum":[
"ASGAverageCPUUtilization",
"ASGAverageNetworkIn",
"ASGAverageNetworkOut",
"DynamoDBReadCapacityUtilization",
"DynamoDBWriteCapacityUtilization",
"ECSServiceAverageCPUUtilization",
"ECSServiceAverageMemoryUtilization",
"ALBRequestCountPerTarget",
"RDSReaderAverageCPUUtilization",
"RDSReaderAverageDatabaseConnections",
"EC2SpotFleetRequestAverageCPUUtilization",
"EC2SpotFleetRequestAverageNetworkIn",
"EC2SpotFleetRequestAverageNetworkOut"
]
},
"ScalingPlan":{
"type":"structure",
"required":[
"ScalingPlanName",
"ScalingPlanVersion",
"ApplicationSource",
"ScalingInstructions",
"StatusCode"
],
"members":{
"ScalingPlanName":{
"shape":"ScalingPlanName",
"documentation":"<p>The name of the scaling plan.</p>"
},
"ScalingPlanVersion":{
"shape":"ScalingPlanVersion",
"documentation":"<p>The version number of the scaling plan.</p>"
},
"ApplicationSource":{
"shape":"ApplicationSource",
"documentation":"<p>The application source.</p>"
},
"ScalingInstructions":{
"shape":"ScalingInstructions",
"documentation":"<p>The scaling instructions.</p>"
},
"StatusCode":{
"shape":"ScalingPlanStatusCode",
"documentation":"<p>The status of the scaling plan.</p> <ul> <li> <p> <code>Active</code> - The scaling plan is active.</p> </li> <li> <p> <code>ActiveWithProblems</code> - The scaling plan is active, but the scaling configuration for one or more resources could not be applied.</p> </li> <li> <p> <code>CreationInProgress</code> - The scaling plan is being created.</p> </li> <li> <p> <code>CreationFailed</code> - The scaling plan could not be created.</p> </li> <li> <p> <code>DeletionInProgress</code> - The scaling plan is being deleted.</p> </li> <li> <p> <code>DeletionFailed</code> - The scaling plan could not be deleted.</p> </li> <li> <p> <code>UpdateInProgress</code> - The scaling plan is being updated.</p> </li> <li> <p> <code>UpdateFailed</code> - The scaling plan could not be updated.</p> </li> </ul>"
},
"StatusMessage":{
"shape":"XmlString",
"documentation":"<p>A simple message about the current status of the scaling plan.</p>"
},
"StatusStartTime":{
"shape":"TimestampType",
"documentation":"<p>The Unix time stamp when the scaling plan entered the current status.</p>"
},
"CreationTime":{
"shape":"TimestampType",
"documentation":"<p>The Unix time stamp when the scaling plan was created.</p>"
}
},
"documentation":"<p>Represents a scaling plan.</p>"
},
"ScalingPlanName":{
"type":"string",
"max":128,
"min":1,
"pattern":"[\\p{Print}&&[^|:/]]+"
},
"ScalingPlanNames":{
"type":"list",
"member":{"shape":"ScalingPlanName"}
},
"ScalingPlanResource":{
"type":"structure",
"required":[
"ScalingPlanName",
"ScalingPlanVersion",
"ServiceNamespace",
"ResourceId",
"ScalableDimension",
"ScalingStatusCode"
],
"members":{
"ScalingPlanName":{
"shape":"ScalingPlanName",
"documentation":"<p>The name of the scaling plan.</p>"
},
"ScalingPlanVersion":{
"shape":"ScalingPlanVersion",
"documentation":"<p>The version number of the scaling plan.</p>"
},
"ServiceNamespace":{
"shape":"ServiceNamespace",
"documentation":"<p>The namespace of the AWS service.</p>"
},
"ResourceId":{
"shape":"ResourceIdMaxLen1600",
"documentation":"<p>The ID of the resource. This string consists of the resource type and unique identifier.</p> <ul> <li> <p>Auto Scaling group - The resource type is <code>autoScalingGroup</code> and the unique identifier is the name of the Auto Scaling group. Example: <code>autoScalingGroup/my-asg</code>.</p> </li> <li> <p>ECS service - The resource type is <code>service</code> and the unique identifier is the cluster name and service name. Example: <code>service/default/sample-webapp</code>.</p> </li> <li> <p>Spot Fleet request - The resource type is <code>spot-fleet-request</code> and the unique identifier is the Spot Fleet request ID. Example: <code>spot-fleet-request/sfr-73fbd2ce-aa30-494c-8788-1cee4EXAMPLE</code>.</p> </li> <li> <p>DynamoDB table - The resource type is <code>table</code> and the unique identifier is the resource ID. Example: <code>table/my-table</code>.</p> </li> <li> <p>DynamoDB global secondary index - The resource type is <code>index</code> and the unique identifier is the resource ID. Example: <code>table/my-table/index/my-table-index</code>.</p> </li> <li> <p>Aurora DB cluster - The resource type is <code>cluster</code> and the unique identifier is the cluster name. Example: <code>cluster:my-db-cluster</code>.</p> </li> </ul>"
},
"ScalableDimension":{
"shape":"ScalableDimension",
"documentation":"<p>The scalable dimension for the resource.</p> <ul> <li> <p> <code>autoscaling:autoScalingGroup:DesiredCapacity</code> - The desired capacity of an Auto Scaling group.</p> </li> <li> <p> <code>ecs:service:DesiredCount</code> - The desired task count of an ECS service.</p> </li> <li> <p> <code>ec2:spot-fleet-request:TargetCapacity</code> - The target capacity of a Spot Fleet request.</p> </li> <li> <p> <code>dynamodb:table:ReadCapacityUnits</code> - The provisioned read capacity for a DynamoDB table.</p> </li> <li> <p> <code>dynamodb:table:WriteCapacityUnits</code> - The provisioned write capacity for a DynamoDB table.</p> </li> <li> <p> <code>dynamodb:index:ReadCapacityUnits</code> - The provisioned read capacity for a DynamoDB global secondary index.</p> </li> <li> <p> <code>dynamodb:index:WriteCapacityUnits</code> - The provisioned write capacity for a DynamoDB global secondary index.</p> </li> <li> <p> <code>rds:cluster:ReadReplicaCount</code> - The count of Aurora Replicas in an Aurora DB cluster. Available for Aurora MySQL-compatible edition and Aurora PostgreSQL-compatible edition.</p> </li> </ul>"
},
"ScalingPolicies":{
"shape":"ScalingPolicies",
"documentation":"<p>The scaling policies.</p>"
},
"ScalingStatusCode":{
"shape":"ScalingStatusCode",
"documentation":"<p>The scaling status of the resource.</p> <ul> <li> <p> <code>Active</code> - The scaling configuration is active.</p> </li> <li> <p> <code>Inactive</code> - The scaling configuration is not active because the scaling plan is being created or the scaling configuration could not be applied. Check the status message for more information.</p> </li> <li> <p> <code>PartiallyActive</code> - The scaling configuration is partially active because the scaling plan is being created or deleted or the scaling configuration could not be fully applied. Check the status message for more information.</p> </li> </ul>"
},
"ScalingStatusMessage":{
"shape":"XmlString",
"documentation":"<p>A simple message about the current scaling status of the resource.</p>"
}
},
"documentation":"<p>Represents a scalable resource.</p>"
},
"ScalingPlanResources":{
"type":"list",
"member":{"shape":"ScalingPlanResource"}
},
"ScalingPlanStatusCode":{
"type":"string",
"enum":[
"Active",
"ActiveWithProblems",
"CreationInProgress",
"CreationFailed",
"DeletionInProgress",
"DeletionFailed",
"UpdateInProgress",
"UpdateFailed"
]
},
"ScalingPlanVersion":{"type":"long"},
"ScalingPlans":{
"type":"list",
"member":{"shape":"ScalingPlan"}
},
"ScalingPolicies":{
"type":"list",
"member":{"shape":"ScalingPolicy"}
},
"ScalingPolicy":{
"type":"structure",
"required":[
"PolicyName",
"PolicyType"
],
"members":{
"PolicyName":{
"shape":"PolicyName",
"documentation":"<p>The name of the scaling policy.</p>"
},
"PolicyType":{
"shape":"PolicyType",
"documentation":"<p>The type of scaling policy.</p>"
},
"TargetTrackingConfiguration":{
"shape":"TargetTrackingConfiguration",
"documentation":"<p>The target tracking scaling policy. Includes support for predefined or customized metrics.</p>"
}
},
"documentation":"<p>Represents a scaling policy.</p>"
},
"ScalingPolicyUpdateBehavior":{
"type":"string",
"enum":[
"KeepExternalPolicies",
"ReplaceExternalPolicies"
]
},
"ScalingStatusCode":{
"type":"string",
"enum":[
"Inactive",
"PartiallyActive",
"Active"
]
},
"ScheduledActionBufferTime":{
"type":"integer",
"min":0
},
"ServiceNamespace":{
"type":"string",
"enum":[
"autoscaling",
"ecs",
"ec2",
"rds",
"dynamodb"
]
},
"TagFilter":{
"type":"structure",
"members":{
"Key":{
"shape":"XmlStringMaxLen128",
"documentation":"<p>The tag key.</p>"
},
"Values":{
"shape":"TagValues",
"documentation":"<p>The tag values (0 to 20).</p>"
}
},
"documentation":"<p>Represents a tag.</p>"
},
"TagFilters":{
"type":"list",
"member":{"shape":"TagFilter"}
},
"TagValues":{
"type":"list",
"member":{"shape":"XmlStringMaxLen256"}
},
"TargetTrackingConfiguration":{
"type":"structure",
"required":["TargetValue"],
"members":{
"PredefinedScalingMetricSpecification":{
"shape":"PredefinedScalingMetricSpecification",
"documentation":"<p>A predefined metric. You can specify either a predefined metric or a customized metric.</p>"
},
"CustomizedScalingMetricSpecification":{
"shape":"CustomizedScalingMetricSpecification",
"documentation":"<p>A customized metric. You can specify either a predefined metric or a customized metric. </p>"
},
"TargetValue":{
"shape":"MetricScale",
"documentation":"<p>The target value for the metric. The range is 8.515920e-109 to 1.174271e+108 (Base 10) or 2e-360 to 2e360 (Base 2).</p>"
},
"DisableScaleIn":{
"shape":"DisableScaleIn",
"documentation":"<p>Indicates whether scale in by the target tracking scaling policy is disabled. If the value is <code>true</code>, scale in is disabled and the target tracking scaling policy doesn't remove capacity from the scalable resource. Otherwise, scale in is enabled and the target tracking scaling policy can remove capacity from the scalable resource. </p> <p>The default value is <code>false</code>.</p>"
},
"ScaleOutCooldown":{
"shape":"Cooldown",
"documentation":"<p>The amount of time, in seconds, after a scale-out activity completes before another scale-out activity can start. This value is not used if the scalable resource is an Auto Scaling group.</p> <p>While the cooldown period is in effect, the capacity that has been added by the previous scale-out event that initiated the cooldown is calculated as part of the desired capacity for the next scale out. The intention is to continuously (but not excessively) scale out.</p>"
},
"ScaleInCooldown":{
"shape":"Cooldown",
"documentation":"<p>The amount of time, in seconds, after a scale in activity completes before another scale in activity can start. This value is not used if the scalable resource is an Auto Scaling group.</p> <p>The cooldown period is used to block subsequent scale in requests until it has expired. The intention is to scale in conservatively to protect your application's availability. However, if another alarm triggers a scale-out policy during the cooldown period after a scale-in, AWS Auto Scaling scales out your scalable target immediately.</p>"
},
"EstimatedInstanceWarmup":{
"shape":"Cooldown",
"documentation":"<p>The estimated time, in seconds, until a newly launched instance can contribute to the CloudWatch metrics. This value is used only if the resource is an Auto Scaling group.</p>"
}
},
"documentation":"<p>Describes a target tracking configuration to use with AWS Auto Scaling. Used with <a>ScalingInstruction</a> and <a>ScalingPolicy</a>.</p>"
},
"TargetTrackingConfigurations":{
"type":"list",
"member":{"shape":"TargetTrackingConfiguration"}
},
"TimestampType":{"type":"timestamp"},
"UpdateScalingPlanRequest":{
"type":"structure",
"required":[
"ScalingPlanName",
"ScalingPlanVersion"
],
"members":{
"ScalingPlanName":{
"shape":"ScalingPlanName",
"documentation":"<p>The name of the scaling plan.</p>"
},
"ScalingPlanVersion":{
"shape":"ScalingPlanVersion",
"documentation":"<p>The version number of the scaling plan.</p>"
},
"ApplicationSource":{
"shape":"ApplicationSource",
"documentation":"<p>A CloudFormation stack or set of tags.</p>"
},
"ScalingInstructions":{
"shape":"ScalingInstructions",
"documentation":"<p>The scaling instructions.</p>"
}
}
},
"UpdateScalingPlanResponse":{
"type":"structure",
"members":{
}
},
"ValidationException":{
"type":"structure",
"members":{
"Message":{"shape":"ErrorMessage"}
},
"documentation":"<p>An exception was thrown for a validation issue. Review the parameters provided.</p>",
"exception":true
},
"XmlString":{
"type":"string",
"pattern":"[\\u0020-\\uD7FF\\uE000-\\uFFFD\\uD800\\uDC00-\\uDBFF\\uDFFF\\r\\n\\t]*"
},
"XmlStringMaxLen128":{
"type":"string",
"max":128,
"min":1,
"pattern":"[\\u0020-\\uD7FF\\uE000-\\uFFFD\\uD800\\uDC00-\\uDBFF\\uDFFF\\r\\n\\t]*"
},
"XmlStringMaxLen256":{
"type":"string",
"max":256,
"min":1,
"pattern":"[\\u0020-\\uD7FF\\uE000-\\uFFFD\\uD800\\uDC00-\\uDBFF\\uDFFF\\r\\n\\t]*"
}
},
"documentation":"<fullname>AWS Auto Scaling</fullname> <p>Use AWS Auto Scaling to quickly discover all the scalable AWS resources for your application and configure dynamic scaling and predictive scaling for your resources using scaling plans. Use this service in conjunction with the Amazon EC2 Auto Scaling, Application Auto Scaling, Amazon CloudWatch, and AWS CloudFormation services. </p> <p>Currently, predictive scaling is only available for Amazon EC2 Auto Scaling groups.</p> <p>For more information about AWS Auto Scaling, including information about granting IAM users required permissions for AWS Auto Scaling actions, see the <a href=\"https://docs.aws.amazon.com/autoscaling/plans/userguide/what-is-aws-auto-scaling.html\">AWS Auto Scaling User Guide</a>.</p>"
}

View File

@@ -0,0 +1,64 @@
{
"pagination": {
"DescribeAutoScalingGroups": {
"input_token": "NextToken",
"output_token": "NextToken",
"limit_key": "MaxRecords",
"result_key": "AutoScalingGroups"
},
"DescribeAutoScalingInstances": {
"input_token": "NextToken",
"output_token": "NextToken",
"limit_key": "MaxRecords",
"result_key": "AutoScalingInstances"
},
"DescribeLaunchConfigurations": {
"input_token": "NextToken",
"output_token": "NextToken",
"limit_key": "MaxRecords",
"result_key": "LaunchConfigurations"
},
"DescribeNotificationConfigurations": {
"input_token": "NextToken",
"output_token": "NextToken",
"limit_key": "MaxRecords",
"result_key": "NotificationConfigurations"
},
"DescribePolicies": {
"input_token": "NextToken",
"output_token": "NextToken",
"limit_key": "MaxRecords",
"result_key": "ScalingPolicies"
},
"DescribeScalingActivities": {
"input_token": "NextToken",
"output_token": "NextToken",
"limit_key": "MaxRecords",
"result_key": "Activities"
},
"DescribeScheduledActions": {
"input_token": "NextToken",
"output_token": "NextToken",
"limit_key": "MaxRecords",
"result_key": "ScheduledUpdateGroupActions"
},
"DescribeTags": {
"input_token": "NextToken",
"output_token": "NextToken",
"limit_key": "MaxRecords",
"result_key": "Tags"
},
"DescribeLoadBalancerTargetGroups": {
"input_token": "NextToken",
"limit_key": "MaxRecords",
"output_token": "NextToken",
"result_key": "LoadBalancerTargetGroups"
},
"DescribeLoadBalancers": {
"input_token": "NextToken",
"limit_key": "MaxRecords",
"output_token": "NextToken",
"result_key": "LoadBalancers"
}
}
}

View File

@@ -0,0 +1,3 @@
{
"pagination": {}
}

View File

@@ -0,0 +1,589 @@
{
"version": "1.0",
"examples": {
"CancelJob": [
{
"input": {
"jobId": "1d828f65-7a4d-42e8-996d-3b900ed59dc4",
"reason": "Cancelling job."
},
"output": {
},
"comments": {
"input": {
},
"output": {
}
},
"description": "This example cancels a job with the specified job ID.",
"id": "to-cancel-a-job-1481152314733",
"title": "To cancel a job"
}
],
"CreateComputeEnvironment": [
{
"input": {
"type": "MANAGED",
"computeEnvironmentName": "C4OnDemand",
"computeResources": {
"type": "EC2",
"desiredvCpus": 48,
"ec2KeyPair": "id_rsa",
"instanceRole": "ecsInstanceRole",
"instanceTypes": [
"c4.large",
"c4.xlarge",
"c4.2xlarge",
"c4.4xlarge",
"c4.8xlarge"
],
"maxvCpus": 128,
"minvCpus": 0,
"securityGroupIds": [
"sg-cf5093b2"
],
"subnets": [
"subnet-220c0e0a",
"subnet-1a95556d",
"subnet-978f6dce"
],
"tags": {
"Name": "Batch Instance - C4OnDemand"
}
},
"serviceRole": "arn:aws:iam::012345678910:role/AWSBatchServiceRole",
"state": "ENABLED"
},
"output": {
"computeEnvironmentArn": "arn:aws:batch:us-east-1:012345678910:compute-environment/C4OnDemand",
"computeEnvironmentName": "C4OnDemand"
},
"comments": {
"input": {
},
"output": {
}
},
"description": "This example creates a managed compute environment with specific C4 instance types that are launched on demand. The compute environment is called C4OnDemand.",
"id": "to-create-a-managed-ec2-compute-environment-1481152600017",
"title": "To create a managed EC2 compute environment"
},
{
"input": {
"type": "MANAGED",
"computeEnvironmentName": "M4Spot",
"computeResources": {
"type": "SPOT",
"bidPercentage": 20,
"desiredvCpus": 4,
"ec2KeyPair": "id_rsa",
"instanceRole": "ecsInstanceRole",
"instanceTypes": [
"m4"
],
"maxvCpus": 128,
"minvCpus": 0,
"securityGroupIds": [
"sg-cf5093b2"
],
"spotIamFleetRole": "arn:aws:iam::012345678910:role/aws-ec2-spot-fleet-role",
"subnets": [
"subnet-220c0e0a",
"subnet-1a95556d",
"subnet-978f6dce"
],
"tags": {
"Name": "Batch Instance - M4Spot"
}
},
"serviceRole": "arn:aws:iam::012345678910:role/AWSBatchServiceRole",
"state": "ENABLED"
},
"output": {
"computeEnvironmentArn": "arn:aws:batch:us-east-1:012345678910:compute-environment/M4Spot",
"computeEnvironmentName": "M4Spot"
},
"comments": {
"input": {
},
"output": {
}
},
"description": "This example creates a managed compute environment with the M4 instance type that is launched when the Spot bid price is at or below 20% of the On-Demand price for the instance type. The compute environment is called M4Spot.",
"id": "to-create-a-managed-ec2-spot-compute-environment-1481152844190",
"title": "To create a managed EC2 Spot compute environment"
}
],
"CreateJobQueue": [
{
"input": {
"computeEnvironmentOrder": [
{
"computeEnvironment": "M4Spot",
"order": 1
}
],
"jobQueueName": "LowPriority",
"priority": 10,
"state": "ENABLED"
},
"output": {
"jobQueueArn": "arn:aws:batch:us-east-1:012345678910:job-queue/LowPriority",
"jobQueueName": "LowPriority"
},
"comments": {
"input": {
},
"output": {
}
},
"description": "This example creates a job queue called LowPriority that uses the M4Spot compute environment.",
"id": "to-create-a-job-queue-with-a-single-compute-environment-1481152967946",
"title": "To create a job queue with a single compute environment"
},
{
"input": {
"computeEnvironmentOrder": [
{
"computeEnvironment": "C4OnDemand",
"order": 1
},
{
"computeEnvironment": "M4Spot",
"order": 2
}
],
"jobQueueName": "HighPriority",
"priority": 1,
"state": "ENABLED"
},
"output": {
"jobQueueArn": "arn:aws:batch:us-east-1:012345678910:job-queue/HighPriority",
"jobQueueName": "HighPriority"
},
"comments": {
"input": {
},
"output": {
}
},
"description": "This example creates a job queue called HighPriority that uses the C4OnDemand compute environment with an order of 1 and the M4Spot compute environment with an order of 2.",
"id": "to-create-a-job-queue-with-multiple-compute-environments-1481153027051",
"title": "To create a job queue with multiple compute environments"
}
],
"DeleteComputeEnvironment": [
{
"input": {
"computeEnvironment": "P2OnDemand"
},
"output": {
},
"comments": {
"input": {
},
"output": {
}
},
"description": "This example deletes the P2OnDemand compute environment.",
"id": "to-delete-a-compute-environment-1481153105644",
"title": "To delete a compute environment"
}
],
"DeleteJobQueue": [
{
"input": {
"jobQueue": "GPGPU"
},
"output": {
},
"comments": {
"input": {
},
"output": {
}
},
"description": "This example deletes the GPGPU job queue.",
"id": "to-delete-a-job-queue-1481153508134",
"title": "To delete a job queue"
}
],
"DeregisterJobDefinition": [
{
"input": {
"jobDefinition": "sleep10"
},
"output": {
},
"comments": {
"input": {
},
"output": {
}
},
"description": "This example deregisters a job definition called sleep10.",
"id": "to-deregister-a-job-definition-1481153579565",
"title": "To deregister a job definition"
}
],
"DescribeComputeEnvironments": [
{
"input": {
"computeEnvironments": [
"P2OnDemand"
]
},
"output": {
"computeEnvironments": [
{
"type": "MANAGED",
"computeEnvironmentArn": "arn:aws:batch:us-east-1:012345678910:compute-environment/P2OnDemand",
"computeEnvironmentName": "P2OnDemand",
"computeResources": {
"type": "EC2",
"desiredvCpus": 48,
"ec2KeyPair": "id_rsa",
"instanceRole": "ecsInstanceRole",
"instanceTypes": [
"p2"
],
"maxvCpus": 128,
"minvCpus": 0,
"securityGroupIds": [
"sg-cf5093b2"
],
"subnets": [
"subnet-220c0e0a",
"subnet-1a95556d",
"subnet-978f6dce"
],
"tags": {
"Name": "Batch Instance - P2OnDemand"
}
},
"ecsClusterArn": "arn:aws:ecs:us-east-1:012345678910:cluster/P2OnDemand_Batch_2c06f29d-d1fe-3a49-879d-42394c86effc",
"serviceRole": "arn:aws:iam::012345678910:role/AWSBatchServiceRole",
"state": "ENABLED",
"status": "VALID",
"statusReason": "ComputeEnvironment Healthy"
}
]
},
"comments": {
"input": {
},
"output": {
}
},
"description": "This example describes the P2OnDemand compute environment.",
"id": "to-describe-a-compute-environment-1481153713334",
"title": "To describe a compute environment"
}
],
"DescribeJobDefinitions": [
{
"input": {
"status": "ACTIVE"
},
"output": {
"jobDefinitions": [
{
"type": "container",
"containerProperties": {
"command": [
"sleep",
"60"
],
"environment": [
],
"image": "busybox",
"memory": 128,
"mountPoints": [
],
"ulimits": [
],
"vcpus": 1,
"volumes": [
]
},
"jobDefinitionArn": "arn:aws:batch:us-east-1:012345678910:job-definition/sleep60:1",
"jobDefinitionName": "sleep60",
"revision": 1,
"status": "ACTIVE"
}
]
},
"comments": {
"input": {
},
"output": {
}
},
"description": "This example describes all of your active job definitions.",
"id": "to-describe-active-job-definitions-1481153895831",
"title": "To describe active job definitions"
}
],
"DescribeJobQueues": [
{
"input": {
"jobQueues": [
"HighPriority"
]
},
"output": {
"jobQueues": [
{
"computeEnvironmentOrder": [
{
"computeEnvironment": "arn:aws:batch:us-east-1:012345678910:compute-environment/C4OnDemand",
"order": 1
}
],
"jobQueueArn": "arn:aws:batch:us-east-1:012345678910:job-queue/HighPriority",
"jobQueueName": "HighPriority",
"priority": 1,
"state": "ENABLED",
"status": "VALID",
"statusReason": "JobQueue Healthy"
}
]
},
"comments": {
"input": {
},
"output": {
}
},
"description": "This example describes the HighPriority job queue.",
"id": "to-describe-a-job-queue-1481153995804",
"title": "To describe a job queue"
}
],
"DescribeJobs": [
{
"input": {
"jobs": [
"24fa2d7a-64c4-49d2-8b47-f8da4fbde8e9"
]
},
"output": {
"jobs": [
{
"container": {
"command": [
"sleep",
"60"
],
"containerInstanceArn": "arn:aws:ecs:us-east-1:012345678910:container-instance/5406d7cd-58bd-4b8f-9936-48d7c6b1526c",
"environment": [
],
"exitCode": 0,
"image": "busybox",
"memory": 128,
"mountPoints": [
],
"ulimits": [
],
"vcpus": 1,
"volumes": [
]
},
"createdAt": 1480460782010,
"dependsOn": [
],
"jobDefinition": "sleep60",
"jobId": "24fa2d7a-64c4-49d2-8b47-f8da4fbde8e9",
"jobName": "example",
"jobQueue": "arn:aws:batch:us-east-1:012345678910:job-queue/HighPriority",
"parameters": {
},
"startedAt": 1480460816500,
"status": "SUCCEEDED",
"stoppedAt": 1480460880699
}
]
},
"comments": {
"input": {
},
"output": {
}
},
"description": "This example describes a job with the specified job ID.",
"id": "to-describe-a-specific-job-1481154090490",
"title": "To describe a specific job"
}
],
"ListJobs": [
{
"input": {
"jobQueue": "HighPriority"
},
"output": {
"jobSummaryList": [
{
"jobId": "e66ff5fd-a1ff-4640-b1a2-0b0a142f49bb",
"jobName": "example"
}
]
},
"comments": {
"input": {
},
"output": {
}
},
"description": "This example lists the running jobs in the HighPriority job queue.",
"id": "to-list-running-jobs-1481154202164",
"title": "To list running jobs"
},
{
"input": {
"jobQueue": "HighPriority",
"jobStatus": "SUBMITTED"
},
"output": {
"jobSummaryList": [
{
"jobId": "68f0c163-fbd4-44e6-9fd1-25b14a434786",
"jobName": "example"
}
]
},
"comments": {
"input": {
},
"output": {
}
},
"description": "This example lists jobs in the HighPriority job queue that are in the SUBMITTED job status.",
"id": "to-list-submitted-jobs-1481154251623",
"title": "To list submitted jobs"
}
],
"RegisterJobDefinition": [
{
"input": {
"type": "container",
"containerProperties": {
"command": [
"sleep",
"10"
],
"image": "busybox",
"memory": 128,
"vcpus": 1
},
"jobDefinitionName": "sleep10"
},
"output": {
"jobDefinitionArn": "arn:aws:batch:us-east-1:012345678910:job-definition/sleep10:1",
"jobDefinitionName": "sleep10",
"revision": 1
},
"comments": {
"input": {
},
"output": {
}
},
"description": "This example registers a job definition for a simple container job.",
"id": "to-register-a-job-definition-1481154325325",
"title": "To register a job definition"
}
],
"SubmitJob": [
{
"input": {
"jobDefinition": "sleep60",
"jobName": "example",
"jobQueue": "HighPriority"
},
"output": {
"jobId": "876da822-4198-45f2-a252-6cea32512ea8",
"jobName": "example"
},
"comments": {
"input": {
},
"output": {
}
},
"description": "This example submits a simple container job called example to the HighPriority job queue.",
"id": "to-submit-a-job-to-a-queue-1481154481673",
"title": "To submit a job to a queue"
}
],
"TerminateJob": [
{
"input": {
"jobId": "61e743ed-35e4-48da-b2de-5c8333821c84",
"reason": "Terminating job."
},
"output": {
},
"comments": {
"input": {
},
"output": {
}
},
"description": "This example terminates a job with the specified job ID.",
"id": "to-terminate-a-job-1481154558276",
"title": "To terminate a job"
}
],
"UpdateComputeEnvironment": [
{
"input": {
"computeEnvironment": "P2OnDemand",
"state": "DISABLED"
},
"output": {
"computeEnvironmentArn": "arn:aws:batch:us-east-1:012345678910:compute-environment/P2OnDemand",
"computeEnvironmentName": "P2OnDemand"
},
"comments": {
"input": {
},
"output": {
}
},
"description": "This example disables the P2OnDemand compute environment so it can be deleted.",
"id": "to-update-a-compute-environment-1481154702731",
"title": "To update a compute environment"
}
],
"UpdateJobQueue": [
{
"input": {
"jobQueue": "GPGPU",
"state": "DISABLED"
},
"output": {
"jobQueueArn": "arn:aws:batch:us-east-1:012345678910:job-queue/GPGPU",
"jobQueueName": "GPGPU"
},
"comments": {
"input": {
},
"output": {
}
},
"description": "This example disables a job queue so that it can be deleted.",
"id": "to-update-a-job-queue-1481154806981",
"title": "To update a job queue"
}
]
}
}

View File

@@ -0,0 +1,28 @@
{
"pagination": {
"DescribeComputeEnvironments": {
"input_token": "nextToken",
"limit_key": "maxResults",
"output_token": "nextToken",
"result_key": "computeEnvironments"
},
"DescribeJobDefinitions": {
"input_token": "nextToken",
"limit_key": "maxResults",
"output_token": "nextToken",
"result_key": "jobDefinitions"
},
"DescribeJobQueues": {
"input_token": "nextToken",
"limit_key": "maxResults",
"output_token": "nextToken",
"result_key": "jobQueues"
},
"ListJobs": {
"input_token": "nextToken",
"limit_key": "maxResults",
"output_token": "nextToken",
"result_key": "jobSummaryList"
}
}
}

View File

@@ -0,0 +1,5 @@
{
"version": "1.0",
"examples": {
}
}

View File

@@ -0,0 +1,22 @@
{
"pagination": {
"DescribeBudgets": {
"input_token": "NextToken",
"limit_key": "MaxResults",
"output_token": "NextToken",
"result_key": "Budgets"
},
"DescribeNotificationsForBudget": {
"input_token": "NextToken",
"limit_key": "MaxResults",
"output_token": "NextToken",
"result_key": "Notifications"
},
"DescribeSubscribersForNotification": {
"input_token": "NextToken",
"limit_key": "MaxResults",
"output_token": "NextToken",
"result_key": "Subscribers"
}
}
}

View File

@@ -0,0 +1,3 @@
{
"pagination": {}
}

View File

@@ -0,0 +1,16 @@
{
"pagination": {
"ListAccounts": {
"input_token": "NextToken",
"limit_key": "MaxResults",
"output_token": "NextToken",
"result_key": "Accounts"
},
"ListUsers": {
"input_token": "NextToken",
"limit_key": "MaxResults",
"output_token": "NextToken",
"result_key": "Users"
}
}
}

View File

@@ -0,0 +1,16 @@
{
"pagination": {
"DescribeEnvironmentMemberships": {
"result_key": "memberships",
"output_token": "nextToken",
"input_token": "nextToken",
"limit_key": "maxResults"
},
"ListEnvironments": {
"result_key": "environmentIds",
"output_token": "nextToken",
"input_token": "nextToken",
"limit_key": "maxResults"
}
}
}

Some files were not shown because too many files have changed in this diff Show More