CloudBender/cloudbender/connection.py

93 lines
3.1 KiB
Python
Raw Normal View History

2018-11-22 18:31:59 +00:00
import os
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__)
sessions = {}
clients = {}
2018-11-22 18:31:59 +00:00
class BotoConnection:
2018-11-22 18:31:59 +00:00
def __init__(self, profile=None, region=None):
self.region = region
self.profile = profile
def _get_session(self, profile=None, region=None):
if sessions.get((profile, region)):
return 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
sessions[(profile, region)] = session
2018-11-22 18:31:59 +00:00
return session
def _get_client(self, service, profile=None, region=None):
if clients.get((profile, region, service)):
2022-02-22 10:04:29 +00:00
logger.debug(
"Reusing boto session for {} {} {}".format(profile, region, service)
)
return clients[(profile, region, service)]
2018-11-22 18:31:59 +00:00
session = self._get_session(profile, region)
2018-11-22 18:31:59 +00:00
client = boto3.Session(botocore_session=session).client(service)
logger.debug("New boto session for {} {} {}".format(profile, region, service))
2018-11-22 18:31:59 +00:00
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):
while True:
try:
client = self._get_client(service, profile, region)
logger.debug("Calling {}:{}".format(client, command))
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
)
)
time.sleep(3)
pass
else:
raise e
def exportProfileEnv(self):
"""
Set AWS os.env variables based on our connection profile to allow external programs use
same profile, region. Eg. Pulumi or Steampipe
"""
credentials = self._get_session(self.profile, self.region).get_credentials()
if credentials.token:
os.environ["AWS_SESSION_TOKEN"] = credentials.token
os.environ["AWS_ACCESS_KEY_ID"] = credentials.access_key
os.environ["AWS_SECRET_ACCESS_KEY"] = credentials.secret_key
if self.region and self.region != "global":
os.environ["AWS_DEFAULT_REGION"] = self.region