2018-11-22 18:31:59 +00:00
|
|
|
import os
|
2019-01-21 10:43:36 +00:00
|
|
|
import time
|
2018-11-22 18:31:59 +00:00
|
|
|
|
|
|
|
import boto3
|
|
|
|
import botocore.session
|
|
|
|
from botocore import credentials
|
|
|
|
|
|
|
|
import logging
|
|
|
|
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
2019-02-07 15:36:16 +00:00
|
|
|
|
2022-02-22 10:04:29 +00:00
|
|
|
class BotoConnection:
|
2019-02-07 15:36:16 +00:00
|
|
|
_sessions = {}
|
2018-11-22 18:31:59 +00:00
|
|
|
_clients = {}
|
|
|
|
|
|
|
|
def __init__(self, profile=None, region=None):
|
|
|
|
self.region = region
|
|
|
|
self.profile = profile
|
|
|
|
|
|
|
|
def _get_session(self, profile=None, region=None):
|
2019-02-07 15:36:16 +00:00
|
|
|
if self._sessions.get((profile, region)):
|
|
|
|
return self._sessions[(profile, region)]
|
2018-11-22 18:31:59 +00:00
|
|
|
|
|
|
|
# Construct botocore session with cache
|
|
|
|
# Setup boto to cache STS tokens for MFA
|
|
|
|
# Change the cache path from the default of ~/.aws/boto/cache to the one used by awscli
|
|
|
|
session_vars = {}
|
|
|
|
if profile:
|
2022-02-22 10:04:29 +00:00
|
|
|
session_vars["profile"] = (None, None, profile, None)
|
|
|
|
if region and region != "global":
|
|
|
|
session_vars["region"] = (None, None, region, None)
|
2018-11-22 18:31:59 +00:00
|
|
|
|
|
|
|
session = botocore.session.Session(session_vars=session_vars)
|
2022-02-22 10:04:29 +00:00
|
|
|
cli_cache = os.path.join(os.path.expanduser("~"), ".aws/cli/cache")
|
|
|
|
session.get_component("credential_provider").get_provider(
|
|
|
|
"assume-role"
|
|
|
|
).cache = credentials.JSONFileCache(cli_cache)
|
2018-11-22 18:31:59 +00:00
|
|
|
|
2019-02-07 15:36:16 +00:00
|
|
|
self._sessions[(profile, region)] = session
|
2018-11-22 18:31:59 +00:00
|
|
|
|
|
|
|
return session
|
|
|
|
|
|
|
|
def _get_client(self, service, profile=None, region=None):
|
2019-02-07 15:36:16 +00:00
|
|
|
if self._clients.get((profile, region, service)):
|
2022-02-22 10:04:29 +00:00
|
|
|
logger.debug(
|
|
|
|
"Reusing boto session for {} {} {}".format(profile, region, service)
|
|
|
|
)
|
2019-02-07 15:36:16 +00:00
|
|
|
return self._clients[(profile, region, service)]
|
2018-11-22 18:31:59 +00:00
|
|
|
|
2019-02-07 15:36:16 +00:00
|
|
|
session = self._get_session(profile, region)
|
2018-11-22 18:31:59 +00:00
|
|
|
client = boto3.Session(botocore_session=session).client(service)
|
2020-08-12 16:20:37 +00:00
|
|
|
logger.debug("New boto session for {} {} {}".format(profile, region, service))
|
2018-11-22 18:31:59 +00:00
|
|
|
|
2019-02-07 15:36:16 +00:00
|
|
|
self._clients[(profile, region, service)] = client
|
2018-11-22 18:31:59 +00:00
|
|
|
return client
|
|
|
|
|
|
|
|
def call(self, service, command, kwargs={}, profile=None, region=None):
|
2019-01-21 10:43:36 +00:00
|
|
|
while True:
|
|
|
|
try:
|
|
|
|
client = self._get_client(service, profile, region)
|
2020-08-12 16:20:37 +00:00
|
|
|
logger.debug("Calling {}:{}".format(client, command))
|
2019-01-21 10:43:36 +00:00
|
|
|
return getattr(client, command)(**kwargs)
|
|
|
|
|
|
|
|
except botocore.exceptions.ClientError as e:
|
2022-02-22 10:04:29 +00:00
|
|
|
if e.response["Error"]["Code"] == "Throttling":
|
|
|
|
logger.warning(
|
|
|
|
"Throttling exception occured during {} - retry after 3s".format(
|
|
|
|
command
|
|
|
|
)
|
|
|
|
)
|
2019-01-21 10:43:36 +00:00
|
|
|
time.sleep(3)
|
|
|
|
pass
|
|
|
|
else:
|
|
|
|
raise e
|