diff --git a/atst/domain/csp/cloud/azure_cloud_provider.py b/atst/domain/csp/cloud/azure_cloud_provider.py index d5ef5204..85feae24 100644 --- a/atst/domain/csp/cloud/azure_cloud_provider.py +++ b/atst/domain/csp/cloud/azure_cloud_provider.py @@ -6,7 +6,13 @@ from uuid import uuid4 from atst.utils import sha256_hex from .cloud_provider_interface import CloudProviderInterface -from .exceptions import AuthenticationException, UserProvisioningException +from .exceptions import ( + AuthenticationException, + UserProvisioningException, + ConnectionException, + UnknownServerException, +) + from .models import ( SubscriptionCreationCSPPayload, SubscriptionCreationCSPResult, @@ -83,7 +89,8 @@ class AzureSDKProvider(object): self.graphrbac = graphrbac self.credentials = credentials self.identity = identity - self.exceptions = exceptions + self.azure_exceptions = exceptions + self.requests_exceptions = requests.exceptions self.secrets = secrets self.requests = requests self.cloud = AZURE_PUBLIC_CLOUD @@ -116,7 +123,7 @@ class AzureCloudProvider(CloudProviderInterface): ) try: return secret_client.set_secret(secret_key, secret_value) - except self.exceptions.HttpResponseError: + except self.azure_exceptions.HttpResponseError: app.logger.error( f"Could not SET secret in Azure keyvault for key {secret_key}.", exc_info=1, @@ -129,7 +136,7 @@ class AzureCloudProvider(CloudProviderInterface): ) try: return secret_client.get_secret(secret_key).value - except self.exceptions.HttpResponseError: + except self.azure_exceptions.HttpResponseError: app.logger.error( f"Could not GET secret in Azure keyvault for key {secret_key}.", exc_info=1, @@ -292,11 +299,30 @@ class AzureCloudProvider(CloudProviderInterface): "Authorization": f"Bearer {sp_token}", } - result = self.sdk.requests.post( - f"{self.sdk.cloud.endpoints.resource_manager}/providers/Microsoft.SignUp/createTenant?api-version=2020-01-01-preview", - json=create_tenant_body, - headers=create_tenant_headers, - ) + try: + result = self.sdk.requests.post( + f"{self.sdk.cloud.endpoints.resource_manager}/providers/Microsoft.SignUp/createTenant?api-version=2020-01-01-preview", + json=create_tenant_body, + headers=create_tenant_headers, + timeout=30, + ) + + except self.requests_exceptions.ConnectionError: + app.logger.error( + f"Could not create tenant. Connection Error", exc_info=1, + ) + raise ConnectionException("connection error creating tenant") + + except self.requests_exceptions.Timeout: + app.logger.error( + f"Could not create tenant. Request timed out.", exc_info=1, + ) + raise ConnectionException("timout error creating tenant") + + try: + response.raise_for_status() + except requests_exceptions.HTTPError: + raise UnknownServerException("azure application error creating tenant") if result.status_code == 200: result_dict = result.json() diff --git a/atst/models/portfolio_state_machine.py b/atst/models/portfolio_state_machine.py index b0e72c8d..f83a7bac 100644 --- a/atst/models/portfolio_state_machine.py +++ b/atst/models/portfolio_state_machine.py @@ -158,6 +158,7 @@ class PortfolioStateMachine( payload = event.kwargs.get("csp_data") payload_data_cls = get_stage_csp_class(stage, "payload") + if not payload_data_cls: app.logger.info(f"could not resolve payload data class for stage {stage}") self.fail_stage(stage) @@ -178,27 +179,34 @@ class PortfolioStateMachine( try: func_name = f"create_{stage}" response = getattr(self.csp, func_name)(payload_data) - if self.portfolio.csp_data is None: - self.portfolio.csp_data = {} - self.portfolio.csp_data.update(response.dict()) - db.session.add(self.portfolio) - db.session.commit() - except PydanticValidationError as exc: - app.logger.error( - f"Failed to cast response to valid result class {self.__repr__()}:", - exc_info=1, - ) - app.logger.info(exc.json()) - print(exc.json()) - app.logger.info(payload_data) - self.fail_stage(stage) except (ConnectionException, UnknownServerException) as exc: app.logger.error( f"CSP api call. Caught exception for {self.__repr__()}.", exc_info=1, ) self.fail_stage(stage) - self.finish_stage(stage) + if response.get("status") == "error": + self.fail_stage(stage) + + elif response.get("status") == "ok": + try: + + if self.portfolio.csp_data is None: + self.portfolio.csp_data = {} + self.portfolio.csp_data.update(response.dict()) + db.session.add(self.portfolio) + db.session.commit() + except PydanticValidationError as exc: + app.logger.error( + f"Failed to cast response to valid result class {self.__repr__()}:", + exc_info=1, + ) + app.logger.info(exc.json()) + print(exc.json()) + app.logger.info(payload_data) + self.fail_stage(stage) + + self.finish_stage(stage) def is_csp_data_valid(self, event): """ @@ -214,7 +222,7 @@ class PortfolioStateMachine( def is_ready_resume_progress(self, event): """ - This function guards advancing states from *_FAILED to *_IN_PROGRESS. + This function guards advancing states from FAILED to *_IN_PROGRESS. """ return True