syndicate/core/build/warmup_processor.py (227 lines of code) (raw):
import os
import boto3
import requests
from requests_aws_sign import AWSV4Sign
from syndicate.commons.log_helper import get_logger, get_user_logger
from syndicate.core import ResourceProvider
from syndicate.core.build.bundle_processor import load_deploy_output
from syndicate.core.conf.processor import ConfigHolder
_LOG = get_logger(__name__)
USER_LOG = get_user_logger()
NODEJS_RUNTIME = 'nodejs'
PYTHON_RUNTIME = 'python'
GET_METHOD = 'GET'
POST_METHOD = 'POST'
PUT_METHOD = 'PUT'
PATCH_METHOD = 'PATCH'
DELETE_METHOD = 'DELETE'
methods_check = {
POST_METHOD: requests.post,
GET_METHOD: requests.get,
PUT_METHOD: requests.put,
PATCH_METHOD: requests.patch,
DELETE_METHOD: requests.delete
}
def process_api_gw_resources(paths_to_be_triggered,
resource_path_warmup_key_mapping):
resource_method_mapping = {}
resource_warmup_key_mapping = {}
for api_gw_link, path_method_mapping in paths_to_be_triggered.items():
for path, method in path_method_mapping.items():
resource_link = api_gw_link + path
resource_method_mapping.update({resource_link: method})
resource_warmup_key_mapping.update(
{resource_link: resource_path_warmup_key_mapping[api_gw_link]
[path]})
return resource_method_mapping, resource_warmup_key_mapping
def get_aws_sign():
config = __get_config()
boto3_session = boto3.session.Session()
credentials = boto3_session.get_credentials()
credentials.access_key = config.aws_access_key_id
credentials.secret_key = config.aws_secret_access_key
region = config.region
service = 'execute-api'
auth = AWSV4Sign(credentials, region, service)
return auth
def warm_upper(resource_method_mapping, resource_warmup_key_mapping,
lambda_auth, header_name, header_value):
for uri, methods in resource_method_mapping.items():
for method in methods:
warmup_method = methods_check.get(method)
warmup_key = resource_warmup_key_mapping[uri]
params = {warmup_key: "true"}
if lambda_auth:
headers = {header_name: header_value}
warmup_method(uri, headers=headers, params=params)
else:
auth = get_aws_sign()
warmup_method(uri, auth=auth, params=params)
def _get_api_gw_client():
return ResourceProvider.instance.api_gw().connection.client
def get_api_stage(rest_api_id, user_input_stage_name):
api_gw_client = _get_api_gw_client()
stages_info = api_gw_client.get_stages(restApiId=rest_api_id)
stages = stages_info.get('item')
all_stage_names = [stage.get('stageName') for stage in stages]
if not user_input_stage_name:
if len(all_stage_names) == 1:
stage_name = all_stage_names[0]
else:
USER_LOG.info(f'Stage name(s) for {rest_api_id} API ID: '
f'{", ".join(all_stage_names)}')
stage_name = input('Select Stage from existing: ')
if stage_name not in all_stage_names:
raise AssertionError(f'Provided Stage name does not exists')
else:
if isinstance(user_input_stage_name, str):
stage_name = [user_input_stage_name] \
if user_input_stage_name in all_stage_names else None
else:
stage_name = [stage for stage in user_input_stage_name
if stage in all_stage_names]
if not stage_name:
raise AssertionError(
f'Provided Stage name does not exists, available stage '
f'name(s): {", ".join(all_stage_names)}')
stage_name = stage_name[0]
return stage_name
def get_warmup_param(allowed_path_warmup_key_mapping, api_gw_path, lambda_arn):
lambda_client = ResourceProvider.instance.lambda_resource().lambda_conn. \
client
lambda_name = lambda_arn.split('function:')[-1]
func_name = lambda_name.split('/')[0]
lambda_meta = lambda_client.get_function(FunctionName=func_name)
lambda_runtime = lambda_meta['Configuration']['Runtime']
warmup_param = 'warmUp'
if lambda_runtime.startswith(PYTHON_RUNTIME) or \
lambda_runtime.startswith(NODEJS_RUNTIME):
warmup_param = 'warm_up'
allowed_path_warmup_key_mapping.update({api_gw_path: warmup_param})
return allowed_path_warmup_key_mapping
def get_api_gw_resources(api_gw_client, rest_api_id):
resource = api_gw_client.get_resources(
restApiId=rest_api_id,
limit=500
)
resource_items = resource['items']
resource_items_id = {item['id']: item['resourceMethods']
for item in resource_items
if 'resourceMethods' in item}
resource_items_path = {item['id']: item['path']
for item in resource_items}
return resource_items_path, resource_items_id
def get_api_gw_integration(rest_api_id):
api_gw_client = _get_api_gw_client()
resource_items_path, resource_items_id = get_api_gw_resources(
api_gw_client, rest_api_id)
affected_lambda = []
allowed_path_method_mapping = {}
allowed_path_warmup_key_mapping = {}
for resource_id, methods in resource_items_id.items():
for method in methods:
integration = api_gw_client.get_integration(restApiId=rest_api_id,
resourceId=resource_id,
httpMethod=method)
api_gw_path = resource_items_path[resource_id]
lambda_arn = integration.get('uri')
if lambda_arn and lambda_arn not in affected_lambda:
affected_lambda.append(lambda_arn)
if not api_gw_path in allowed_path_method_mapping:
allowed_path_method_mapping.update({api_gw_path: [method]})
else:
allowed_path_method_mapping[api_gw_path].append(method)
get_warmup_param(allowed_path_warmup_key_mapping, api_gw_path,
lambda_arn)
return allowed_path_method_mapping, allowed_path_warmup_key_mapping
def __get_config():
conf_path = os.environ.get('SDCT_CONF')
config = ConfigHolder(conf_path)
return config
def get_api_gw_link(rest_api_id, stage_name):
api_gw_link_template = 'https://{rest_api_id}.execute-api.{region}.' \
'amazonaws.com/{stage_name}'
config = __get_config()
region = config.region
api_gw_link = api_gw_link_template.format(rest_api_id=rest_api_id,
region=region,
stage_name=stage_name)
return api_gw_link
def process_deploy_resources(bundle_name, deploy_name):
output = load_deploy_output(bundle_name, deploy_name)
output = {key: value for key, value in output.items() if
value['resource_meta'].get('resource_type') == 'api_gateway'}
paths_to_be_triggered = {}
resource_path_warmup_key_mapping = {}
if not output:
_LOG.warning('No resources to warmup, exiting')
return paths_to_be_triggered, resource_path_warmup_key_mapping
for resource_arn, meta in output.items():
rest_api_id = resource_arn.split('/')[-1]
stage_name = meta.get('resource_meta', {}).get('deploy_stage')
paths_to_be_triggered, resource_path_warmup_key_mapping = \
handle_paths_to_be_triggered(rest_api_id=rest_api_id,
stage_name=stage_name,
paths_to_be_triggered=
paths_to_be_triggered,
resource_path_warmup_key_mapping=
resource_path_warmup_key_mapping)
return paths_to_be_triggered, resource_path_warmup_key_mapping
def process_inputted_api_gw_id(api_id, stage_name):
api_gw_client = _get_api_gw_client()
all_apis = api_gw_client.get_rest_apis().get('items', {})
allowed_id = [api['id'] for api in all_apis]
paths_to_be_triggered = {}
resource_path_warmup_key_mapping = {}
for rest_api_id in api_id:
if rest_api_id not in allowed_id:
USER_LOG.info(f'Provided {rest_api_id} API ID does not exists')
continue
stage_name = get_api_stage(rest_api_id, stage_name)
paths_to_be_triggered, resource_path_warmup_key_mapping = \
handle_paths_to_be_triggered(rest_api_id=rest_api_id,
stage_name=stage_name,
paths_to_be_triggered=
paths_to_be_triggered,
resource_path_warmup_key_mapping=
resource_path_warmup_key_mapping)
return paths_to_be_triggered, resource_path_warmup_key_mapping
def process_existing_api_gw_id(stage_name):
api_gw_client = _get_api_gw_client()
all_apis = api_gw_client.get_rest_apis().get('items', {})
all_api_name = {api['name']: api['id'] for api in all_apis}
USER_LOG.info(f'Existed API Gateway: {", ".join(all_api_name)}')
user_input_id = input('Select API from existing (multiple names must be'
' separated by commas): ')
user_input_id = user_input_id.split(",")
user_input_id = {user_id.strip() for user_id in user_input_id}
if not user_input_id.issubset(all_api_name):
raise AssertionError(
f'Specify only allowed IDs: {", ".join(all_api_name)}')
paths_to_be_triggered = {}
resource_path_warmup_key_mapping = {}
for api_name in user_input_id:
rest_api_id = all_api_name[api_name]
stage_name = get_api_stage(rest_api_id, stage_name)
handle_paths_to_be_triggered(rest_api_id, stage_name,
paths_to_be_triggered,
resource_path_warmup_key_mapping)
paths_to_be_triggered, resource_path_warmup_key_mapping = \
handle_paths_to_be_triggered(rest_api_id=rest_api_id,
stage_name=stage_name,
paths_to_be_triggered=
paths_to_be_triggered,
resource_path_warmup_key_mapping=
resource_path_warmup_key_mapping)
return paths_to_be_triggered, resource_path_warmup_key_mapping
def handle_paths_to_be_triggered(rest_api_id, stage_name,
paths_to_be_triggered,
resource_path_warmup_key_mapping):
api_gw_link = get_api_gw_link(rest_api_id, stage_name)
allowed_path_method_mapping, allowed_path_warmup_key_mapping = \
get_api_gw_integration(rest_api_id)
paths_to_be_triggered.update(
{api_gw_link: allowed_path_method_mapping})
resource_path_warmup_key_mapping.update(
{api_gw_link: allowed_path_warmup_key_mapping})
return paths_to_be_triggered, resource_path_warmup_key_mapping