modular_sdk/modular.py (249 lines of code) (raw):

import os from modular_sdk.commons import validate_params, SingletonMeta from modular_sdk.commons.constants import SERVICE_MODE_DOCKER, \ MODULAR_SERVICE_MODE_ENV, \ PARAM_MONGO_DB_NAME, PARAM_MONGO_URL, PARAM_MONGO_PASSWORD, \ PARAM_MONGO_USER, PARAM_ASSUME_ROLE_ARN, SERVICE_MODE_SAAS, \ ASSUMES_ROLE_SESSION_NAME, MODULAR_AWS_ACCESS_KEY_ID_ENV, \ MODULAR_AWS_SECRET_ACCESS_KEY_ENV, MODULAR_AWS_SESSION_TOKEN_ENV, \ MODULAR_AWS_CREDENTIALS_EXPIRATION_ENV from modular_sdk.services.impl.maestro_http_transport_service import \ MaestroHTTPConfig class Modular(metaclass=SingletonMeta): # services __rabbit_conn = None __environment_service = None __customer_service = None __application_service = None __parent_service = None __region_service = None __tenant_service = None __tenant_settings_service = None __customer_settings_service = None __sts_service = None __sqs_service = None __lambda_service = None __events_service = None __rabbit_transport_service = None __http_transport_service = None __settings_service = None __instantiated_setting_group = [] __credentials_service = None __thread_local_storage_service = None __ssm_service = None __assume_role_ssm_service = None def __init__(self, *args, **kwargs): kwargs = self.__collect_kwargs(kwargs) service_mode = kwargs.get(MODULAR_SERVICE_MODE_ENV, SERVICE_MODE_SAAS) assume_role_arn = kwargs.get( PARAM_ASSUME_ROLE_ARN) if service_mode == SERVICE_MODE_SAAS and assume_role_arn: sts_service = self.sts_service() assumed_credentials = sts_service.assume_roles_chain( list(sts_service.assume_roles_default_payloads( assume_role_arn.split(','), ASSUMES_ROLE_SESSION_NAME )) ) os.environ[MODULAR_AWS_ACCESS_KEY_ID_ENV] = assumed_credentials[ 'aws_access_key_id'] os.environ[MODULAR_AWS_SECRET_ACCESS_KEY_ENV] = \ assumed_credentials['aws_secret_access_key'] os.environ[MODULAR_AWS_SESSION_TOKEN_ENV] = assumed_credentials[ 'aws_session_token'] os.environ[MODULAR_AWS_CREDENTIALS_EXPIRATION_ENV] = \ assumed_credentials['expiration'].isoformat() os.environ[PARAM_ASSUME_ROLE_ARN] = assume_role_arn elif service_mode == SERVICE_MODE_DOCKER: required_mongodb_attrs = ( MODULAR_SERVICE_MODE_ENV, PARAM_MONGO_USER, PARAM_MONGO_PASSWORD, PARAM_MONGO_URL, PARAM_MONGO_DB_NAME) validate_params(kwargs, required_mongodb_attrs) for attr in required_mongodb_attrs: os.environ[attr] = kwargs.get(attr) @staticmethod def __collect_kwargs(kwargs): """ PARAM_ASSUME_ROLE_ARN is string, but it can contain multiple roles divided by ',', hence: TODO, in case kwargs are given, we should expect modular_assume_role_arn to be a list. Or, better, use environment_service here instead of os.environ. ES already converts values. :param kwargs: :return: """ allowed_attrs = ( MODULAR_SERVICE_MODE_ENV, PARAM_MONGO_USER, PARAM_MONGO_PASSWORD, PARAM_MONGO_URL, PARAM_MONGO_DB_NAME, PARAM_ASSUME_ROLE_ARN) kwargs = {k: v for k, v in kwargs.items() if k in allowed_attrs} for attr in allowed_attrs: if attr not in kwargs and attr in os.environ: kwargs[attr] = os.environ.get(attr) if not kwargs.get(MODULAR_SERVICE_MODE_ENV): kwargs[MODULAR_SERVICE_MODE_ENV] = SERVICE_MODE_SAAS return kwargs def __str__(self): return str(id(self)) def environment_service(self): if not self.__environment_service: from modular_sdk.services.environment_service import \ EnvironmentService self.__environment_service = EnvironmentService() return self.__environment_service def application_service(self): if not self.__application_service: from modular_sdk.services.application_service import \ ApplicationService self.__application_service = ApplicationService( customer_service=self.customer_service() ) return self.__application_service def customer_service(self): if not self.__customer_service: from modular_sdk.services.customer_service import CustomerService self.__customer_service = CustomerService() return self.__customer_service def parent_service(self): if not self.__parent_service: from modular_sdk.services.parent_service import ParentService self.__parent_service = ParentService( tenant_service=self.tenant_service(), customer_service=self.customer_service() ) return self.__parent_service def region_service(self): if not self.__region_service: from modular_sdk.services.region_service import RegionService self.__region_service = RegionService( tenant_service=self.tenant_service() ) return self.__region_service def tenant_service(self): if not self.__tenant_service: from modular_sdk.services.tenant_service import TenantService self.__tenant_service = TenantService() return self.__tenant_service def tenant_settings_service(self): if not self.__tenant_settings_service: from modular_sdk.services.tenant_settings_service import \ TenantSettingsService self.__tenant_settings_service = TenantSettingsService() return self.__tenant_settings_service def customer_settings_service(self): if not self.__customer_settings_service: from modular_sdk.services.customer_settings_service import \ CustomerSettingsService self.__customer_settings_service = CustomerSettingsService() return self.__customer_settings_service def sts_service(self): if not self.__sts_service: from modular_sdk.services.sts_service import StsService self.__sts_service = StsService( environment_service=self.environment_service(), aws_region=self.environment_service().aws_region()) return self.__sts_service def sqs_service(self): if not self.__sqs_service: from modular_sdk.services.sqs_service import SQSService self.__sqs_service = SQSService( aws_region=self.environment_service().aws_region(), environment_service=self.environment_service() ) return self.__sqs_service def lambda_service(self): if not self.__lambda_service: from modular_sdk.services.lambda_service import LambdaService self.__lambda_service = LambdaService( aws_region=self.environment_service().aws_region()) return self.__lambda_service def events_service(self): if not self.__events_service: from modular_sdk.services.events_service import EventsService self.__events_service = EventsService( aws_region=self.environment_service().aws_region()) return self.__events_service def rabbit(self, connection_url, timeout=None, refresh=False): if not self.__rabbit_conn or refresh: from modular_sdk.connections.rabbit_connection import \ RabbitMqConnection self.__rabbit_conn = RabbitMqConnection( connection_url=connection_url, timeout=timeout ) return self.__rabbit_conn def rabbit_transport_service(self, connection_url, config, timeout=None): if not self.__rabbit_transport_service: from modular_sdk.services.impl.maestro_rabbit_transport_service import \ MaestroRabbitMQTransport rabbit_connection = self.rabbit( connection_url=connection_url, timeout=timeout, refresh=True ) self.__rabbit_transport_service = MaestroRabbitMQTransport( rabbit_connection=rabbit_connection, config=config ) return self.__rabbit_transport_service def http_transport_service( self, api_link: str, config: MaestroHTTPConfig, timeout: int | None = None, ): if not self.__http_transport_service: from modular_sdk.services.impl.maestro_http_transport_service import \ MaestroHTTPTransport self.__http_transport_service = MaestroHTTPTransport( config=config, api_link=api_link, timeout=timeout, ) return self.__http_transport_service def settings_service(self, group_name): if not self.__settings_service or \ group_name not in self.__instantiated_setting_group: from modular_sdk.services.settings_management_service import \ SettingsManagementService self.__settings_service = SettingsManagementService( group_name=group_name ) return self.__settings_service def ssm_service(self): if not self.__ssm_service: from modular_sdk.services.ssm_service import VaultSSMClient, \ SSMService, SSMClientCachingWrapper if self.environment_service().is_docker(): self.__ssm_service = VaultSSMClient() else: self.__ssm_service = SSMService( aws_region=self.environment_service().aws_region() ) self.__ssm_service = SSMClientCachingWrapper( client=self.__ssm_service, environment_service=self.environment_service() ) return self.__ssm_service def assume_role_ssm_service(self): if not self.__assume_role_ssm_service: from modular_sdk.services.ssm_service import VaultSSMClient, \ ModularAssumeRoleSSMService, SSMClientCachingWrapper if self.environment_service().is_docker(): self.__assume_role_ssm_service = VaultSSMClient() else: self.__assume_role_ssm_service = ModularAssumeRoleSSMService() self.__assume_role_ssm_service = SSMClientCachingWrapper( client=self.__assume_role_ssm_service, environment_service=self.environment_service() ) return self.__assume_role_ssm_service def maestro_credentials_service(self): if not self.__credentials_service: from modular_sdk.services.impl.maestro_credentials_service import \ MaestroCredentialsService self.__credentials_service = \ MaestroCredentialsService.build() return self.__credentials_service def thread_local_storage_service(self): if not self.__thread_local_storage_service: from modular_sdk.services.thread_local_storage_service \ import ThreadLocalStorageService self.__thread_local_storage_service = ThreadLocalStorageService() return self.__thread_local_storage_service def reset(self, service: str): """Removes the saved instance of the service. It is useful, for example, in case of gitlab service - when we want to use different rule-sources configurations""" private_service_name = f'_MODULAR__{service}' if not hasattr(self, private_service_name): raise AssertionError( f'In case you are using this method, make sure your ' f'service {private_service_name} exists amongst the ' f'private attributes') setattr(self, private_service_name, None)