From 41e2ae5253a9b61e550655f637f5704f03784ab8 Mon Sep 17 00:00:00 2001 From: Philip Kalinsky Date: Tue, 11 Feb 2020 15:32:47 -0500 Subject: [PATCH] azure csp tests catch exceptions resulting from raised requests exceptions --- Pipfile | 1 - Pipfile.lock | 28 +- atst/domain/csp/cloud/azure_cloud_provider.py | 172 ++++---- atst/models/mixins/state_machines.py | 1 - atst/models/portfolio_state_machine.py | 17 +- tests/domain/cloud/test_azure_csp.py | 381 +++++++++++++----- tests/domain/cloud/test_mock_csp.py | 1 - tests/domain/test_portfolio_state_machine.py | 74 +++- tests/mock_azure.py | 4 +- 9 files changed, 440 insertions(+), 239 deletions(-) diff --git a/Pipfile b/Pipfile index 8e5dc976..810da811 100644 --- a/Pipfile +++ b/Pipfile @@ -54,7 +54,6 @@ selenium = "*" honcho = "*" blinker = "*" pytest-mock = "*" -requests-mock = "*" detect-secrets = "*" beautifulsoup4 = "*" mypy = "*" diff --git a/Pipfile.lock b/Pipfile.lock index d6032259..f34446d9 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "dcc985866bfecd1e2abcd40722a657f0da223c3c9c11aee32fd4b532eca31cb8" + "sha256": "44296f145fcb42cff5fadf14a706ec9598f4436ccbdf05e1d69fcd8316c89e8d" }, "pipfile-spec": 6, "requires": { @@ -62,11 +62,11 @@ }, "azure-identity": { "hashes": [ - "sha256:4ce65058461c277991763ed3f121efc6b9eb9c2edefb62c414dfa85c814690d3", - "sha256:b32acd1cdb6202bfe10d9a0858dc463d8960295da70ae18097eb3b85ab12cb91" + "sha256:17fa904e0447fd2a2dc19909379edb769b05656dbaf4863b8c4fdfb2bb54350c", + "sha256:7e9c85e3f82f1e29e5edfc7beb3030b25e8b8fd02b65d5ea1c67f13cde01da0f" ], "index": "pypi", - "version": "==1.2.0" + "version": "==1.3.0" }, "azure-keyvault": { "hashes": [ @@ -78,17 +78,17 @@ }, "azure-keyvault-keys": { "hashes": [ - "sha256:2983fa42e20a0e6bf6b87976716129c108e613e0292d34c5b0f0c8dc1d488e89", - "sha256:38c27322637a2c52620a8b96da1942ad6a8d22d09b5a01f6fa257f7a51e52ed0" + "sha256:1c230c052b9f0b9ecaee97347fe4ebf3fcc798f92edbfd618ea264efc61ad554", + "sha256:711af402a0000ac329406253470c1198cc452adc8638608461ae54c8dce92afc" ], - "version": "==4.0.0" + "version": "==4.0.1" }, "azure-keyvault-secrets": { "hashes": [ - "sha256:2eae9264a8f6f59277e1a9bfdbc8b0a15969ee5a80d8efe403d7744805b4a481", - "sha256:97a602406a833e8f117c540c66059c818f4321a35168dd17365fab1e4527d718" + "sha256:0afd85eaa94962fc8ad9e71348ba0873d723d531163894ee3175f138c3180fe4", + "sha256:0f92705444f55ca0d5a892172eda898f42678602834f03fa8012e9979e5fe619" ], - "version": "==4.0.0" + "version": "==4.0.1" }, "azure-mgmt-authorization": { "hashes": [ @@ -1217,14 +1217,6 @@ "index": "pypi", "version": "==2.22.0" }, - "requests-mock": { - "hashes": [ - "sha256:510df890afe08d36eca5bb16b4aa6308a6f85e3159ad3013bac8b9de7bd5a010", - "sha256:88d3402dd8b3c69a9e4f9d3a73ad11b15920c6efd36bc27bf1f701cf4a8e4646" - ], - "index": "pypi", - "version": "==1.7.0" - }, "rope": { "hashes": [ "sha256:52423a7eebb5306a6d63bdc91a7c657db51ac9babfb8341c9a1440831ecf3203", diff --git a/atst/domain/csp/cloud/azure_cloud_provider.py b/atst/domain/csp/cloud/azure_cloud_provider.py index aa5022e9..7ab53806 100644 --- a/atst/domain/csp/cloud/azure_cloud_provider.py +++ b/atst/domain/csp/cloud/azure_cloud_provider.py @@ -3,6 +3,7 @@ from secrets import token_urlsafe from typing import Any, Dict from uuid import uuid4 import pydantic +from flask import current_app as app from atst.utils import sha256_hex @@ -281,18 +282,17 @@ class AzureCloudProvider(CloudProviderInterface): timeout=30, ) result.raise_for_status() - - except self.sdk.requests.ConnectionError: - #app.logger.error( - # f"Could not create tenant. Connection Error", exc_info=1, - #) + except self.sdk.requests.exceptions.ConnectionError: + app.logger.error( + f"Could not create tenant. Connection Error", exc_info=1, + ) raise ConnectionException("connection error creating tenant") - except self.sdk.requests.Timeout: + except self.sdk.requests.exceptions.Timeout: app.logger.error( f"Could not create tenant. Request timed out.", exc_info=1, ) raise ConnectionException("timout error creating tenant") - except self.sdk.requests.HTTPError: + except self.sdk.requests.exceptions.HTTPError: raise UnknownServerException("azure application error creating tenant") result_dict = result.json() @@ -300,22 +300,17 @@ class AzureCloudProvider(CloudProviderInterface): tenant_admin_username = ( f"{payload.user_id}@{payload.domain_name}.onmicrosoft.com" ) - try: - creds = KeyVaultCredentials( - tenant_id=tenant_id, - tenant_admin_username=tenant_admin_username, - tenant_admin_password=payload.password, - ) - except pydantic.ValidationError as val_exc: - return self.update_tenant_creds( tenant_id, - creds, + KeyVaultCredentials( + tenant_id=tenant_id, + tenant_admin_username=tenant_admin_username, + tenant_admin_password=payload.password, + ), ) return TenantCSPResult(domain_name=payload.domain_name, **result_dict) - def create_billing_profile_creation( self, payload: BillingProfileCreationCSPPayload ): @@ -348,17 +343,17 @@ class AzureCloudProvider(CloudProviderInterface): # NB: Swagger docs imply call can sometimes resolve immediately return BillingProfileVerificationCSPResult(**result.json()) - except self.sdk.requests.ConnectionError: + except self.sdk.requests.exceptions.ConnectionError: app.logger.error( f"Could not create tenant. Connection Error", exc_info=1, ) raise ConnectionException("connection error creating tenant") - except self.sdk.requests.Timeout: + except self.sdk.requests.exceptions.Timeout: app.logger.error( f"Could not create tenant. Request timed out.", exc_info=1, ) raise ConnectionException("timout error creating tenant") - except self.sdk.requests.HTTPError: + except self.sdk.requests.exceptions.HTTPError: raise UnknownServerException("azure application error creating tenant") def create_billing_profile_verification( @@ -375,9 +370,7 @@ class AzureCloudProvider(CloudProviderInterface): } try: result = self.sdk.requests.get( - payload.billing_profile_verify_url, - headers=auth_header, - timeout=30, + payload.billing_profile_verify_url, headers=auth_header, timeout=30, ) result.raise_for_status() @@ -387,17 +380,17 @@ class AzureCloudProvider(CloudProviderInterface): elif result.status_code == 200: return BillingProfileVerificationCSPResult(**result.json()) - except self.sdk.requests.ConnectionError: + except self.sdk.requests.exceptions.ConnectionError: app.logger.error( f"Could not create tenant. Connection Error", exc_info=1, ) raise ConnectionException("connection error creating tenant") - except self.sdk.requests.Timeout: + except self.sdk.requests.exceptions.Timeout: app.logger.error( f"Could not create tenant. Request timed out.", exc_info=1, ) raise ConnectionException("timout error creating tenant") - except self.sdk.requests.HTTPError: + except self.sdk.requests.exceptions.HTTPError: raise UnknownServerException("azure application error creating tenant") def create_billing_profile_tenant_access( @@ -419,25 +412,22 @@ class AzureCloudProvider(CloudProviderInterface): url = f"{self.sdk.cloud.endpoints.resource_manager}/providers/Microsoft.Billing/billingAccounts/{payload.billing_account_name}/billingProfiles/{payload.billing_profile_name}/createBillingRoleAssignment?api-version=2019-10-01-preview" try: result = self.sdk.requests.post( - url, - headers=headers, - json=request_body, - timeout=30, + url, headers=headers, json=request_body, timeout=30, ) if result.status_code == 201: return BillingProfileTenantAccessCSPResult(**result.json()) - except self.sdk.requests.ConnectionError: + except self.sdk.requests.exceptions.ConnectionError: app.logger.error( f"Could not create tenant. Connection Error", exc_info=1, ) raise ConnectionException("connection error creating tenant") - except self.sdk.requests.Timeout: + except self.sdk.requests.exceptions.Timeout: app.logger.error( f"Could not create tenant. Request timed out.", exc_info=1, ) raise ConnectionException("timout error creating tenant") - except self.sdk.requests.HTTPError: + except self.sdk.requests.exceptions.HTTPError: raise UnknownServerException("azure application error creating tenant") def create_task_order_billing_creation( @@ -470,17 +460,17 @@ class AzureCloudProvider(CloudProviderInterface): elif result.status_code == 200: return TaskOrderBillingVerificationCSPResult(**result.json()) - except self.sdk.requests.ConnectionError: + except self.sdk.requests.exceptions.ConnectionError: app.logger.error( f"Could not create tenant. Connection Error", exc_info=1, ) raise ConnectionException("connection error creating tenant") - except self.sdk.requests.Timeout: + except self.sdk.requests.exceptions.Timeout: app.logger.error( f"Could not create tenant. Request timed out.", exc_info=1, ) raise ConnectionException("timout error creating tenant") - except self.sdk.requests.HTTPError: + except self.sdk.requests.exceptions.HTTPError: raise UnknownServerException("azure application error creating tenant") def create_task_order_billing_verification( @@ -508,17 +498,17 @@ class AzureCloudProvider(CloudProviderInterface): elif result.status_code == 200: return TaskOrderBillingVerificationCSPResult(**result.json()) - except self.sdk.requests.ConnectionError: + except self.sdk.requests.exceptions.ConnectionError: app.logger.error( f"Could not create tenant. Connection Error", exc_info=1, ) raise ConnectionException("connection error creating tenant") - except self.sdk.requests.Timeout: + except self.sdk.requests.exceptions.Timeout: app.logger.error( f"Could not create tenant. Request timed out.", exc_info=1, ) raise ConnectionException("timout error creating tenant") - except self.sdk.requests.HTTPError: + except self.sdk.requests.exceptions.HTTPError: raise UnknownServerException("azure application error creating tenant") def create_billing_instruction(self, payload: BillingInstructionCSPPayload): @@ -543,21 +533,23 @@ class AzureCloudProvider(CloudProviderInterface): } try: - result = self.sdk.requests.put(url, headers=auth_header, json=request_body, timeout=30) + result = self.sdk.requests.put( + url, headers=auth_header, json=request_body, timeout=30 + ) result.raise_for_status() return BillingInstructionCSPResult(**result.json()) - except self.sdk.requests.ConnectionError: + except self.sdk.requests.exceptions.ConnectionError: app.logger.error( f"Could not create tenant. Connection Error", exc_info=1, ) raise ConnectionException("connection error creating tenant") - except self.sdk.requests.Timeout: + except self.sdk.requests.exceptions.Timeout: app.logger.error( f"Could not create tenant. Request timed out.", exc_info=1, ) raise ConnectionException("timout error creating tenant") - except self.sdk.requests.HTTPError: + except self.sdk.requests.exceptions.HTTPError: raise UnknownServerException("azure application error creating tenant") def create_subscription(self, payload: SubscriptionCreationCSPPayload): @@ -580,22 +572,24 @@ class AzureCloudProvider(CloudProviderInterface): } try: - result = self.sdk.requests.put(url, headers=auth_header, json=request_body, timeout=30) + result = self.sdk.requests.put( + url, headers=auth_header, json=request_body, timeout=30 + ) if result.status_code in [200, 202]: # 202 has location/retry after headers return SubscriptionCreationCSPResult(**result.headers, **result.json()) - except self.sdk.requests.ConnectionError: + except self.sdk.requests.exceptions.ConnectionError: app.logger.error( f"Could not create tenant. Connection Error", exc_info=1, ) raise ConnectionException("connection error creating tenant") - except self.sdk.requests.Timeout: + except self.sdk.requests.exceptions.Timeout: app.logger.error( f"Could not create tenant. Request timed out.", exc_info=1, ) raise ConnectionException("timout error creating tenant") - except self.sdk.requests.HTTPError: + except self.sdk.requests.exceptions.HTTPError: raise UnknownServerException("azure application error creating tenant") def create_subscription_creation(self, payload: SubscriptionCreationCSPPayload): @@ -623,17 +617,17 @@ class AzureCloudProvider(CloudProviderInterface): # 202 has location/retry after headers return SuscriptionVerificationCSPResult(**result.json()) - except self.sdk.requests.ConnectionError: + except self.sdk.requests.exceptions.ConnectionError: app.logger.error( f"Could not create tenant. Connection Error", exc_info=1, ) raise ConnectionException("connection error creating tenant") - except self.sdk.requests.Timeout: + except self.sdk.requests.exceptions.Timeout: app.logger.error( f"Could not create tenant. Request timed out.", exc_info=1, ) raise ConnectionException("timout error creating tenant") - except self.sdk.requests.HTTPError: + except self.sdk.requests.exceptions.HTTPError: raise UnknownServerException("azure application error creating tenant") def create_product_purchase(self, payload: ProductPurchaseCSPPayload): @@ -670,17 +664,17 @@ class AzureCloudProvider(CloudProviderInterface): # NB: Swagger docs imply call can sometimes resolve immediately return ProductPurchaseVerificationCSPResult(**result.json()) - except self.sdk.requests.ConnectionError: + except self.sdk.requests.exceptions.ConnectionError: app.logger.error( f"Could not create tenant. Connection Error", exc_info=1, ) raise ConnectionException("connection error creating tenant") - except self.sdk.requests.Timeout: + except self.sdk.requests.exceptions.Timeout: app.logger.error( f"Could not create tenant. Request timed out.", exc_info=1, ) raise ConnectionException("timout error creating tenant") - except self.sdk.requests.HTTPError: + except self.sdk.requests.exceptions.HTTPError: raise UnknownServerException("azure application error creating tenant") def create_product_purchase_verification( @@ -710,17 +704,17 @@ class AzureCloudProvider(CloudProviderInterface): premium_purchase_date=premium_purchase_date ) - except self.sdk.requests.ConnectionError: + except self.sdk.requests.exceptions.ConnectionError: app.logger.error( f"Could not create tenant. Connection Error", exc_info=1, ) raise ConnectionException("connection error creating tenant") - except self.sdk.requests.Timeout: + except self.sdk.requests.exceptions.Timeout: app.logger.error( f"Could not create tenant. Request timed out.", exc_info=1, ) raise ConnectionException("timout error creating tenant") - except self.sdk.requests.HTTPError: + except self.sdk.requests.exceptions.HTTPError: raise UnknownServerException("azure application error creating tenant") def create_tenant_admin_ownership(self, payload: TenantAdminOwnershipCSPPayload): @@ -751,17 +745,17 @@ class AzureCloudProvider(CloudProviderInterface): return TenantAdminOwnershipCSPResult(**response.json()) - except self.sdk.requests.ConnectionError: + except self.sdk.requests.exceptions.ConnectionError: app.logger.error( f"Could not create tenant. Connection Error", exc_info=1, ) raise ConnectionException("connection error creating tenant") - except self.sdk.requests.Timeout: + except self.sdk.requests.exceptions.Timeout: app.logger.error( f"Could not create tenant. Request timed out.", exc_info=1, ) raise ConnectionException("timout error creating tenant") - except self.sdk.requests.HTTPError: + except self.sdk.requests.exceptions.HTTPError: raise UnknownServerException("azure application error creating tenant") def create_tenant_principal_ownership( @@ -794,17 +788,17 @@ class AzureCloudProvider(CloudProviderInterface): response.raise_for_status() return TenantPrincipalOwnershipCSPResult(**response.json()) - except self.sdk.requests.ConnectionError: + except self.sdk.requests.exceptions.ConnectionError: app.logger.error( f"Could not create tenant. Connection Error", exc_info=1, ) raise ConnectionException("connection error creating tenant") - except self.sdk.requests.Timeout: + except self.sdk.requests.exceptions.Timeout: app.logger.error( f"Could not create tenant. Request timed out.", exc_info=1, ) raise ConnectionException("timout error creating tenant") - except self.sdk.requests.HTTPError: + except self.sdk.requests.exceptions.HTTPError: raise UnknownServerException("azure application error creating tenant") def create_tenant_principal_app(self, payload: TenantPrincipalAppCSPPayload): @@ -831,17 +825,17 @@ class AzureCloudProvider(CloudProviderInterface): response.raise_for_status() return TenantPrincipalAppCSPResult(**response.json()) - except self.sdk.requests.ConnectionError: + except self.sdk.requests.exceptions.ConnectionError: app.logger.error( f"Could not create tenant. Connection Error", exc_info=1, ) raise ConnectionException("connection error creating tenant") - except self.sdk.requests.Timeout: + except self.sdk.requests.exceptions.Timeout: app.logger.error( f"Could not create tenant. Request timed out.", exc_info=1, ) raise ConnectionException("timout error creating tenant") - except self.sdk.requests.HTTPError: + except self.sdk.requests.exceptions.HTTPError: raise UnknownServerException("azure application error creating tenant") def create_tenant_principal(self, payload: TenantPrincipalCSPPayload): @@ -868,17 +862,17 @@ class AzureCloudProvider(CloudProviderInterface): response.raise_for_status() return TenantPrincipalCSPResult(**response.json()) - except self.sdk.requests.ConnectionError: + except self.sdk.requests.exceptions.ConnectionError: app.logger.error( f"Could not create tenant. Connection Error", exc_info=1, ) raise ConnectionException("connection error creating tenant") - except self.sdk.requests.Timeout: + except self.sdk.requests.exceptions.Timeout: app.logger.error( f"Could not create tenant. Request timed out.", exc_info=1, ) raise ConnectionException("timout error creating tenant") - except self.sdk.requests.HTTPError: + except self.sdk.requests.exceptions.HTTPError: raise UnknownServerException("azure application error creating tenant") def create_tenant_principal_credential( @@ -921,17 +915,17 @@ class AzureCloudProvider(CloudProviderInterface): principal_creds_established=True, ) - except self.sdk.requests.ConnectionError: + except self.sdk.requests.exceptions.ConnectionError: app.logger.error( f"Could not create tenant. Connection Error", exc_info=1, ) raise ConnectionException("connection error creating tenant") - except self.sdk.requests.Timeout: + except self.sdk.requests.exceptions.Timeout: app.logger.error( f"Could not create tenant. Request timed out.", exc_info=1, ) raise ConnectionException("timout error creating tenant") - except self.sdk.requests.HTTPError: + except self.sdk.requests.exceptions.HTTPError: raise UnknownServerException("azure application error creating tenant") def create_admin_role_definition(self, payload: AdminRoleDefinitionCSPPayload): @@ -967,17 +961,17 @@ class AzureCloudProvider(CloudProviderInterface): return AdminRoleDefinitionCSPResult(admin_role_def_id=admin_role_def_id) - except self.sdk.requests.ConnectionError: + except self.sdk.requests.exceptions.ConnectionError: app.logger.error( f"Could not create tenant. Connection Error", exc_info=1, ) raise ConnectionException("connection error creating tenant") - except self.sdk.requests.Timeout: + except self.sdk.requests.exceptions.Timeout: app.logger.error( f"Could not create tenant. Request timed out.", exc_info=1, ) raise ConnectionException("timout error creating tenant") - except self.sdk.requests.HTTPError: + except self.sdk.requests.exceptions.HTTPError: raise UnknownServerException("azure application error creating tenant") def create_principal_admin_role(self, payload: PrincipalAdminRoleCSPPayload): @@ -1008,17 +1002,17 @@ class AzureCloudProvider(CloudProviderInterface): response.raise_for_status() return PrincipalAdminRoleCSPResult(**response.json()) - except self.sdk.requests.ConnectionError: + except self.sdk.requests.exceptions.ConnectionError: app.logger.error( f"Could not create tenant. Connection Error", exc_info=1, ) raise ConnectionException("connection error creating tenant") - except self.sdk.requests.Timeout: + except self.sdk.requests.exceptions.Timeout: app.logger.error( f"Could not create tenant. Request timed out.", exc_info=1, ) raise ConnectionException("timout error creating tenant") - except self.sdk.requests.HTTPError: + except self.sdk.requests.exceptions.HTTPError: raise UnknownServerException("azure application error creating tenant") def force_tenant_admin_pw_update(self, creds, tenant_owner_id): @@ -1129,17 +1123,17 @@ class AzureCloudProvider(CloudProviderInterface): # raise UserProvisioningException(f"Failed to create user: {response.json()}") - except self.sdk.requests.ConnectionError: + except self.sdk.requests.exceptions.ConnectionError: app.logger.error( f"Could not create tenant. Connection Error", exc_info=1, ) raise ConnectionException("connection error creating tenant") - except self.sdk.requests.Timeout: + except self.sdk.requests.exceptions.Timeout: app.logger.error( f"Could not create tenant. Request timed out.", exc_info=1, ) raise ConnectionException("timout error creating tenant") - except self.sdk.requests.HTTPError: + except self.sdk.requests.exceptions.HTTPError: raise UnknownServerException("azure application error creating tenant") def _update_active_directory_user_email( @@ -1166,17 +1160,17 @@ class AzureCloudProvider(CloudProviderInterface): f"Failed update user email: {response.json()}" ) - except self.sdk.requests.ConnectionError: + except self.sdk.requests.exceptions.ConnectionError: app.logger.error( f"Could not create tenant. Connection Error", exc_info=1, ) raise ConnectionException("connection error creating tenant") - except self.sdk.requests.Timeout: + except self.sdk.requests.exceptions.Timeout: app.logger.error( f"Could not create tenant. Request timed out.", exc_info=1, ) raise ConnectionException("timout error creating tenant") - except self.sdk.requests.HTTPError: + except self.sdk.requests.exceptions.HTTPError: raise UnknownServerException("azure application error creating tenant") def _extract_subscription_id(self, subscription_url): @@ -1281,17 +1275,17 @@ class AzureCloudProvider(CloudProviderInterface): return mgmt_token - except self.sdk.requests.ConnectionError: + except self.sdk.requests.exceptions.ConnectionError: app.logger.error( f"Could not create tenant. Connection Error", exc_info=1, ) raise ConnectionException("connection error creating tenant") - except self.sdk.requests.Timeout: + except self.sdk.requests.exceptions.Timeout: app.logger.error( f"Could not create tenant. Request timed out.", exc_info=1, ) raise ConnectionException("timout error creating tenant") - except self.sdk.requests.HTTPError: + except self.sdk.requests.exceptions.HTTPError: raise UnknownServerException("azure application error creating tenant") def _source_creds(self, tenant_id=None) -> KeyVaultCredentials: @@ -1360,15 +1354,15 @@ class AzureCloudProvider(CloudProviderInterface): if result.ok: return CostManagementQueryCSPResult(**result.json()) - except self.sdk.requests.ConnectionError: + except self.sdk.requests.exceptions.ConnectionError: app.logger.error( f"Could not create tenant. Connection Error", exc_info=1, ) raise ConnectionException("connection error creating tenant") - except self.sdk.requests.Timeout: + except self.sdk.requests.exceptions.Timeout: app.logger.error( f"Could not create tenant. Request timed out.", exc_info=1, ) raise ConnectionException("timout error creating tenant") - except self.sdk.requests.HTTPError: + except self.sdk.requests.exceptions.HTTPError: raise UnknownServerException("azure application error creating tenant") diff --git a/atst/models/mixins/state_machines.py b/atst/models/mixins/state_machines.py index 5741476d..f4b6fae5 100644 --- a/atst/models/mixins/state_machines.py +++ b/atst/models/mixins/state_machines.py @@ -135,7 +135,6 @@ class FSMMixin: f"could not locate fail trigger '{fail_trigger}' for '{self.__repr__()}'" ) - def finish_stage(self, stage): finish_trigger = f"finish_{stage}" if finish_trigger in self.machine.get_triggers(self.current_state.name): diff --git a/atst/models/portfolio_state_machine.py b/atst/models/portfolio_state_machine.py index d4fff482..39fe727a 100644 --- a/atst/models/portfolio_state_machine.py +++ b/atst/models/portfolio_state_machine.py @@ -19,7 +19,7 @@ from atst.models.mixins.state_machines import ( FSMStates, AzureStages, StageStates, - _build_transitions + _build_transitions, ) @@ -52,7 +52,9 @@ def get_stage_csp_class(stage, class_type): importlib.import_module("atst.domain.csp.cloud.models"), cls_name ) except AttributeError: - raise StateMachineMisconfiguredError(f"could not import CSP Payload/Result class {cls_name}") + raise StateMachineMisconfiguredError( + f"could not import CSP Payload/Result class {cls_name}" + ) @add_state_features(Tags) @@ -138,7 +140,7 @@ class PortfolioStateMachine( app.logger.info( f"could not locate 'create trigger' for {self.__repr__()}" ) - self.trigger('fail') + self.trigger("fail") elif self.current_state == FSMStates.FAILED: # get the first trigger that starts with 'create_' @@ -169,15 +171,18 @@ class PortfolioStateMachine( if create_trigger is not None: self.trigger(create_trigger, **kwargs) - def after_in_progress_callback(self, event): # Accumulate payload w/ creds payload = event.kwargs.get("csp_data") - current_stage = _stage_state_to_stage_name(self.current_state, StageStates.IN_PROGRESS) + current_stage = _stage_state_to_stage_name( + self.current_state, StageStates.IN_PROGRESS + ) payload_data_cls = get_stage_csp_class(current_stage, "payload") if not payload_data_cls: - app.logger.info(f"could not resolve payload data class for stage {current_stage}") + app.logger.info( + f"could not resolve payload data class for stage {current_stage}" + ) self.fail_stage(current_stage) try: payload_data = payload_data_cls(**payload) diff --git a/tests/domain/cloud/test_azure_csp.py b/tests/domain/cloud/test_azure_csp.py index 820a47c5..b39743ad 100644 --- a/tests/domain/cloud/test_azure_csp.py +++ b/tests/domain/cloud/test_azure_csp.py @@ -4,8 +4,6 @@ from uuid import uuid4 import pendulum import pydantic import pytest -#import requests -from secrets import token_urlsafe from tests.factories import ApplicationFactory, EnvironmentFactory from tests.mock_azure import AUTH_CREDENTIALS, mock_azure @@ -67,7 +65,6 @@ from atst.domain.csp.cloud.models import ( BILLING_ACCOUNT_NAME = "52865e4c-52e8-5a6c-da6b-c58f0814f06f:7ea5de9d-b8ce-4901-b1c5-d864320c7b03_2019-05-31" - def mock_management_group_create(mock_azure, spec_dict): mock_azure.sdk.managementgroups.ManagementGroupsAPI.return_value.management_groups.create_or_update.return_value.result.return_value = ( spec_dict @@ -103,6 +100,7 @@ def mock_get_secret(azure, val=None): return azure + def test_create_application_succeeds(mock_azure: AzureCloudProvider): application = ApplicationFactory.create() mock_management_group_create(mock_azure, {"id": "Test Id"}) @@ -141,6 +139,7 @@ def test_create_policy_definition_succeeds(mock_azure: AzureCloudProvider): parameters=mock_policy_definition, ) + def test_create_tenant(mock_azure: AzureCloudProvider): mock_result = Mock() @@ -151,10 +150,11 @@ def test_create_tenant(mock_azure: AzureCloudProvider): } mock_result.status_code = 200 - mock_azure.sdk.requests.post.return_value = mock_result mock_azure.sdk.requests.post.side_effect = [ mock_azure.sdk.requests.exceptions.ConnectionError, - mock_result + mock_azure.sdk.requests.exceptions.Timeout, + mock_azure.sdk.requests.exceptions.HTTPError, + mock_result, ] payload = TenantCSPPayload( @@ -172,79 +172,13 @@ def test_create_tenant(mock_azure: AzureCloudProvider): with pytest.raises(ConnectionException): mock_azure.create_tenant(payload) - - result: TenantCSPResult = mock_azure.create_tenant(payload) - assert result.tenant_id == "60ff9d34-82bf-4f21-b565-308ef0533435" - - -def test_create_tenant_req_mock(mock_azure: AzureCloudProvider, requests_mock): - url = f"{mock_azure.sdk.cloud.endpoints.resource_manager}providers/Microsoft.SignUp/createTenant" - requests_mock.register_uri( - "POST", "?".join([url, "api-version=2020-01-01-preview"]), - json={ - "objectId": "0a5f4926-e3ee-4f47-a6e3-8b0a30a40e3d", - "tenantId": "60ff9d34-82bf-4f21-b565-308ef0533435", - "userId": "1153801116406515559", - }, - ) - mock_azure = mock_get_secret(mock_azure) - payload = TenantCSPPayload( - **dict( - user_id="admin", - password="JediJan13$coot", # pragma: allowlist secret - domain_name="jediccpospawnedtenant2", - first_name="Tedry", - last_name="Tenet", - country_code="US", - password_recovery_email_address="thomas@promptworks.com", - ) - ) - result: TenantCSPResult = mock_azure.create_tenant(payload) - assert result.tenant_id == "60ff9d34-82bf-4f21-b565-308ef0533435" - - -def test_create_tenant_req_mock_fail(mock_azure: AzureCloudProvider, requests_mock): - url = f"{mock_azure.sdk.cloud.endpoints.resource_manager}providers/Microsoft.SignUp/createTenant" - requests_mock.register_uri( - "POST", "?".join([url, "api-version=2020-01-01-preview"]), - exc=requests.exceptions.ConnectionError, - ) - mock_azure = mock_get_secret(mock_azure) - payload = TenantCSPPayload( - **dict( - user_id="admin", - password="JediJan13$coot", # pragma: allowlist secret - domain_name="jediccpospawnedtenant2", - first_name="Tedry", - last_name="Tenet", - country_code="US", - password_recovery_email_address="thomas@promptworks.com", - ) - ) - with pytest.raises(ConnectionException): - assert mock_azure.create_tenant(payload) + mock_azure.create_tenant(payload) + with pytest.raises(UnknownServerException): + mock_azure.create_tenant(payload) - -# def test_create_tenant_fails(mock_azure: AzureCloudProvider): -# mock_result = Mock() -# mock_result.status_code = 200 -# mock_azure.sdk.requests.post.return_value = mock_result -# payload = TenantCSPPayload( -# **dict( -# user_id="admin", -# password="JediJan13$coot", # pragma: allowlist secret -# domain_name="jediccpospawnedtenant2", -# first_name="Tedry", -# last_name="Tenet", -# country_code="US", -# password_recovery_email_address="thomas@promptworks.com", -# ) -# ) -# mock_azure = mock_get_secret(mock_azure) - -# with pytest.raises(pydantic.ValidationError): -# assert mock_azure.create_tenant(payload) + result: TenantCSPResult = mock_azure.create_tenant(payload) + assert result.tenant_id == "60ff9d34-82bf-4f21-b565-308ef0533435" def test_create_billing_profile_creation(mock_azure: AzureCloudProvider): @@ -258,7 +192,14 @@ def test_create_billing_profile_creation(mock_azure: AzureCloudProvider): "Retry-After": "10", } mock_result.status_code = 202 - mock_azure.sdk.requests.post.return_value = mock_result + + mock_azure.sdk.requests.post.side_effect = [ + mock_azure.sdk.requests.exceptions.ConnectionError, + mock_azure.sdk.requests.exceptions.Timeout, + mock_azure.sdk.requests.exceptions.HTTPError, + mock_result, + ] + payload = BillingProfileCreationCSPPayload( **dict( address=dict( @@ -274,6 +215,13 @@ def test_create_billing_profile_creation(mock_azure: AzureCloudProvider): billing_account_name=BILLING_ACCOUNT_NAME, ) ) + with pytest.raises(ConnectionException): + mock_azure.create_billing_profile_creation(payload) + with pytest.raises(ConnectionException): + mock_azure.create_billing_profile_creation(payload) + with pytest.raises(UnknownServerException): + mock_azure.create_billing_profile_creation(payload) + body: BillingProfileCreationCSPResult = mock_azure.create_billing_profile_creation( payload ) @@ -316,7 +264,12 @@ def test_validate_billing_profile_creation(mock_azure: AzureCloudProvider): }, "type": "Microsoft.Billing/billingAccounts/billingProfiles", } - mock_azure.sdk.requests.get.return_value = mock_result + mock_azure.sdk.requests.get.side_effect = [ + mock_azure.sdk.requests.exceptions.ConnectionError, + mock_azure.sdk.requests.exceptions.Timeout, + mock_azure.sdk.requests.exceptions.HTTPError, + mock_result, + ] payload = BillingProfileVerificationCSPPayload( **dict( @@ -324,6 +277,12 @@ def test_validate_billing_profile_creation(mock_azure: AzureCloudProvider): billing_profile_verify_url="https://management.azure.com/providers/Microsoft.Billing/billingAccounts/7c89b735-b22b-55c0-ab5a-c624843e8bf6:de4416ce-acc6-44b1-8122-c87c4e903c91_2019-05-31/operationResults/createBillingProfile_478d5706-71f9-4a8b-8d4e-2cbaca27a668?api-version=2019-10-01-preview", ) ) + with pytest.raises(ConnectionException): + mock_azure.create_billing_profile_verification(payload) + with pytest.raises(ConnectionException): + mock_azure.create_billing_profile_verification(payload) + with pytest.raises(UnknownServerException): + mock_azure.create_billing_profile_verification(payload) body: BillingProfileVerificationCSPResult = mock_azure.create_billing_profile_verification( payload @@ -356,7 +315,12 @@ def test_create_billing_profile_tenant_access(mock_azure: AzureCloudProvider): "type": "Microsoft.Billing/billingRoleAssignments", } - mock_azure.sdk.requests.post.return_value = mock_result + mock_azure.sdk.requests.post.side_effect = [ + mock_azure.sdk.requests.exceptions.ConnectionError, + mock_azure.sdk.requests.exceptions.Timeout, + mock_azure.sdk.requests.exceptions.HTTPError, + mock_result, + ] payload = BillingProfileTenantAccessCSPPayload( **dict( @@ -366,6 +330,12 @@ def test_create_billing_profile_tenant_access(mock_azure: AzureCloudProvider): billing_profile_name="KQWI-W2SU-BG7-TGB", ) ) + with pytest.raises(ConnectionException): + mock_azure.create_billing_profile_tenant_access(payload) + with pytest.raises(ConnectionException): + mock_azure.create_billing_profile_tenant_access(payload) + with pytest.raises(UnknownServerException): + mock_azure.create_billing_profile_tenant_access(payload) body: BillingProfileTenantAccessCSPResult = mock_azure.create_billing_profile_tenant_access( payload @@ -388,7 +358,12 @@ def test_create_task_order_billing_creation(mock_azure: AzureCloudProvider): "Retry-After": "10", } - mock_azure.sdk.requests.patch.return_value = mock_result + mock_azure.sdk.requests.patch.side_effect = [ + mock_azure.sdk.requests.exceptions.ConnectionError, + mock_azure.sdk.requests.exceptions.Timeout, + mock_azure.sdk.requests.exceptions.HTTPError, + mock_result, + ] payload = TaskOrderBillingCreationCSPPayload( **dict( @@ -397,6 +372,12 @@ def test_create_task_order_billing_creation(mock_azure: AzureCloudProvider): billing_profile_name="KQWI-W2SU-BG7-TGB", ) ) + with pytest.raises(ConnectionException): + mock_azure.create_task_order_billing_creation(payload) + with pytest.raises(ConnectionException): + mock_azure.create_task_order_billing_creation(payload) + with pytest.raises(UnknownServerException): + mock_azure.create_task_order_billing_creation(payload) body: TaskOrderBillingCreationCSPResult = mock_azure.create_task_order_billing_creation( payload @@ -450,7 +431,12 @@ def test_create_task_order_billing_verification(mock_azure): }, "type": "Microsoft.Billing/billingAccounts/billingProfiles", } - mock_azure.sdk.requests.get.return_value = mock_result + mock_azure.sdk.requests.get.side_effect = [ + mock_azure.sdk.requests.exceptions.ConnectionError, + mock_azure.sdk.requests.exceptions.Timeout, + mock_azure.sdk.requests.exceptions.HTTPError, + mock_result, + ] payload = TaskOrderBillingVerificationCSPPayload( **dict( @@ -458,6 +444,12 @@ def test_create_task_order_billing_verification(mock_azure): task_order_billing_verify_url="https://management.azure.com/providers/Microsoft.Billing/billingAccounts/7c89b735-b22b-55c0-ab5a-c624843e8bf6:de4416ce-acc6-44b1-8122-c87c4e903c91_2019-05-31/operationResults/createBillingProfile_478d5706-71f9-4a8b-8d4e-2cbaca27a668?api-version=2019-10-01-preview", ) ) + with pytest.raises(ConnectionException): + mock_azure.create_task_order_billing_verification(payload) + with pytest.raises(ConnectionException): + mock_azure.create_task_order_billing_verification(payload) + with pytest.raises(UnknownServerException): + mock_azure.create_task_order_billing_verification(payload) body: TaskOrderBillingVerificationCSPResult = mock_azure.create_task_order_billing_verification( payload @@ -486,7 +478,12 @@ def test_create_billing_instruction(mock_azure: AzureCloudProvider): "type": "Microsoft.Billing/billingAccounts/billingProfiles/billingInstructions", } - mock_azure.sdk.requests.put.return_value = mock_result + mock_azure.sdk.requests.put.side_effect = [ + mock_azure.sdk.requests.exceptions.ConnectionError, + mock_azure.sdk.requests.exceptions.Timeout, + mock_azure.sdk.requests.exceptions.HTTPError, + mock_result, + ] payload = BillingInstructionCSPPayload( **dict( @@ -500,6 +497,12 @@ def test_create_billing_instruction(mock_azure: AzureCloudProvider): billing_profile_name="KQWI-W2SU-BG7-TGB", ) ) + with pytest.raises(ConnectionException): + mock_azure.create_billing_instruction(payload) + with pytest.raises(ConnectionException): + mock_azure.create_billing_instruction(payload) + with pytest.raises(UnknownServerException): + mock_azure.create_billing_instruction(payload) body: BillingInstructionCSPResult = mock_azure.create_billing_instruction(payload) assert body.reported_clin_name == "TO1:CLIN001" @@ -516,7 +519,12 @@ def test_create_product_purchase(mock_azure: AzureCloudProvider): "Retry-After": "10", } - mock_azure.sdk.requests.post.return_value = mock_result + mock_azure.sdk.requests.post.side_effect = [ + mock_azure.sdk.requests.exceptions.ConnectionError, + mock_azure.sdk.requests.exceptions.Timeout, + mock_azure.sdk.requests.exceptions.HTTPError, + mock_result, + ] payload = ProductPurchaseCSPPayload( **dict( @@ -525,6 +533,12 @@ def test_create_product_purchase(mock_azure: AzureCloudProvider): billing_profile_name="KQWI-W2SU-BG7-TGB", ) ) + with pytest.raises(ConnectionException): + mock_azure.create_product_purchase(payload) + with pytest.raises(ConnectionException): + mock_azure.create_product_purchase(payload) + with pytest.raises(UnknownServerException): + mock_azure.create_product_purchase(payload) body: ProductPurchaseCSPResult = mock_azure.create_product_purchase(payload) assert ( @@ -561,7 +575,12 @@ def test_create_product_purchase_verification(mock_azure): "type": "Microsoft.Billing/billingAccounts/billingProfiles/invoiceSections/products", } - mock_azure.sdk.requests.get.return_value = mock_result + mock_azure.sdk.requests.get.side_effect = [ + mock_azure.sdk.requests.exceptions.ConnectionError, + mock_azure.sdk.requests.exceptions.Timeout, + mock_azure.sdk.requests.exceptions.HTTPError, + mock_result, + ] payload = ProductPurchaseVerificationCSPPayload( **dict( @@ -569,6 +588,12 @@ def test_create_product_purchase_verification(mock_azure): product_purchase_verify_url="https://management.azure.com/providers/Microsoft.Billing/billingAccounts/7c89b735-b22b-55c0-ab5a-c624843e8bf6:de4416ce-acc6-44b1-8122-c87c4e903c91_2019-05-31/operationResults/createBillingProfile_478d5706-71f9-4a8b-8d4e-2cbaca27a668?api-version=2019-10-01-preview", ) ) + with pytest.raises(ConnectionException): + mock_azure.create_product_purchase_verification(payload) + with pytest.raises(ConnectionException): + mock_azure.create_product_purchase_verification(payload) + with pytest.raises(UnknownServerException): + mock_azure.create_product_purchase_verification(payload) body: ProductPurchaseVerificationCSPResult = mock_azure.create_product_purchase_verification( payload @@ -588,12 +613,25 @@ def test_create_tenant_principal_app(mock_azure: AzureCloudProvider): mock_result.ok = True mock_result.json.return_value = {"appId": "appId", "id": "id"} - mock_azure.sdk.requests.post.return_value = mock_result + mock_azure.sdk.requests.post.side_effect = [ + mock_azure.sdk.requests.exceptions.ConnectionError, + mock_azure.sdk.requests.exceptions.Timeout, + mock_azure.sdk.requests.exceptions.HTTPError, + mock_result, + ] + mock_azure = mock_get_secret(mock_azure) payload = TenantPrincipalAppCSPPayload( **{"tenant_id": "6d2d2d6c-a6d6-41e1-8bb1-73d11475f8f4"} ) + with pytest.raises(ConnectionException): + mock_azure.create_tenant_principal_app(payload) + with pytest.raises(ConnectionException): + mock_azure.create_tenant_principal_app(payload) + with pytest.raises(UnknownServerException): + mock_azure.create_tenant_principal_app(payload) + result: TenantPrincipalAppCSPResult = mock_azure.create_tenant_principal_app( payload ) @@ -613,7 +651,13 @@ def test_create_tenant_principal(mock_azure: AzureCloudProvider): mock_result.ok = True mock_result.json.return_value = {"id": "principal_id"} - mock_azure.sdk.requests.post.return_value = mock_result + mock_azure.sdk.requests.post.side_effect = [ + mock_azure.sdk.requests.exceptions.ConnectionError, + mock_azure.sdk.requests.exceptions.Timeout, + mock_azure.sdk.requests.exceptions.HTTPError, + mock_result, + ] + mock_azure = mock_get_secret(mock_azure) payload = TenantPrincipalCSPPayload( @@ -622,6 +666,12 @@ def test_create_tenant_principal(mock_azure: AzureCloudProvider): "principal_app_id": "appId", } ) + with pytest.raises(ConnectionException): + mock_azure.create_tenant_principal(payload) + with pytest.raises(ConnectionException): + mock_azure.create_tenant_principal(payload) + with pytest.raises(UnknownServerException): + mock_azure.create_tenant_principal(payload) result: TenantPrincipalCSPResult = mock_azure.create_tenant_principal(payload) @@ -640,7 +690,12 @@ def test_create_tenant_principal_credential(mock_azure: AzureCloudProvider): mock_result.ok = True mock_result.json.return_value = {"secretText": "new secret key"} - mock_azure.sdk.requests.post.return_value = mock_result + mock_azure.sdk.requests.post.side_effect = [ + mock_azure.sdk.requests.exceptions.ConnectionError, + mock_azure.sdk.requests.exceptions.Timeout, + mock_azure.sdk.requests.exceptions.HTTPError, + mock_result, + ] mock_azure = mock_get_secret(mock_azure) @@ -651,6 +706,12 @@ def test_create_tenant_principal_credential(mock_azure: AzureCloudProvider): "principal_app_object_id": "appObjId", } ) + with pytest.raises(ConnectionException): + mock_azure.create_tenant_principal_credential(payload) + with pytest.raises(ConnectionException): + mock_azure.create_tenant_principal_credential(payload) + with pytest.raises(UnknownServerException): + mock_azure.create_tenant_principal_credential(payload) result: TenantPrincipalCredentialCSPResult = mock_azure.create_tenant_principal_credential( payload @@ -676,12 +737,23 @@ def test_create_admin_role_definition(mock_azure: AzureCloudProvider): ] } - mock_azure.sdk.requests.get.return_value = mock_result + mock_azure.sdk.requests.get.side_effect = [ + mock_azure.sdk.requests.exceptions.ConnectionError, + mock_azure.sdk.requests.exceptions.Timeout, + mock_azure.sdk.requests.exceptions.HTTPError, + mock_result, + ] mock_azure = mock_get_secret(mock_azure) payload = AdminRoleDefinitionCSPPayload( **{"tenant_id": "6d2d2d6c-a6d6-41e1-8bb1-73d11475f8f4"} ) + with pytest.raises(ConnectionException): + mock_azure.create_admin_role_definition(payload) + with pytest.raises(ConnectionException): + mock_azure.create_admin_role_definition(payload) + with pytest.raises(UnknownServerException): + mock_azure.create_admin_role_definition(payload) result: AdminRoleDefinitionCSPResult = mock_azure.create_admin_role_definition( payload @@ -702,7 +774,12 @@ def test_create_tenant_admin_ownership(mock_azure: AzureCloudProvider): mock_result.ok = True mock_result.json.return_value = {"id": "id"} - mock_azure.sdk.requests.put.return_value = mock_result + mock_azure.sdk.requests.put.side_effect = [ + mock_azure.sdk.requests.exceptions.ConnectionError, + mock_azure.sdk.requests.exceptions.Timeout, + mock_azure.sdk.requests.exceptions.HTTPError, + mock_result, + ] payload = TenantAdminOwnershipCSPPayload( **{ @@ -710,6 +787,12 @@ def test_create_tenant_admin_ownership(mock_azure: AzureCloudProvider): "user_object_id": "971efe4d-1e80-4e39-b3b9-4e5c63ad446d", } ) + with pytest.raises(ConnectionException): + mock_azure.create_tenant_admin_ownership(payload) + with pytest.raises(ConnectionException): + mock_azure.create_tenant_admin_ownership(payload) + with pytest.raises(UnknownServerException): + mock_azure.create_tenant_admin_ownership(payload) result: TenantAdminOwnershipCSPResult = mock_azure.create_tenant_admin_ownership( payload @@ -730,7 +813,12 @@ def test_create_tenant_principal_ownership(mock_azure: AzureCloudProvider): mock_result.ok = True mock_result.json.return_value = {"id": "id"} - mock_azure.sdk.requests.put.return_value = mock_result + mock_azure.sdk.requests.put.side_effect = [ + mock_azure.sdk.requests.exceptions.ConnectionError, + mock_azure.sdk.requests.exceptions.Timeout, + mock_azure.sdk.requests.exceptions.HTTPError, + mock_result, + ] payload = TenantPrincipalOwnershipCSPPayload( **{ @@ -738,6 +826,12 @@ def test_create_tenant_principal_ownership(mock_azure: AzureCloudProvider): "principal_id": "971efe4d-1e80-4e39-b3b9-4e5c63ad446d", } ) + with pytest.raises(ConnectionException): + mock_azure.create_tenant_principal_ownership(payload) + with pytest.raises(ConnectionException): + mock_azure.create_tenant_principal_ownership(payload) + with pytest.raises(UnknownServerException): + mock_azure.create_tenant_principal_ownership(payload) result: TenantPrincipalOwnershipCSPResult = mock_azure.create_tenant_principal_ownership( payload @@ -758,7 +852,12 @@ def test_create_principal_admin_role(mock_azure: AzureCloudProvider): mock_result.ok = True mock_result.json.return_value = {"id": "id"} - mock_azure.sdk.requests.post.return_value = mock_result + mock_azure.sdk.requests.post.side_effect = [ + mock_azure.sdk.requests.exceptions.ConnectionError, + mock_azure.sdk.requests.exceptions.Timeout, + mock_azure.sdk.requests.exceptions.HTTPError, + mock_result, + ] payload = PrincipalAdminRoleCSPPayload( **{ @@ -767,6 +866,12 @@ def test_create_principal_admin_role(mock_azure: AzureCloudProvider): "admin_role_def_id": uuid4().hex, } ) + with pytest.raises(ConnectionException): + mock_azure.create_principal_admin_role(payload) + with pytest.raises(ConnectionException): + mock_azure.create_principal_admin_role(payload) + with pytest.raises(UnknownServerException): + mock_azure.create_principal_admin_role(payload) result: PrincipalAdminRoleCSPResult = mock_azure.create_principal_admin_role( payload @@ -790,7 +895,14 @@ def test_create_subscription_creation(mock_azure: AzureCloudProvider): "Retry-After": 10, } mock_result.json.return_value = {} - mock_azure.sdk.requests.put.return_value = mock_result + + mock_azure.sdk.requests.put.side_effect = [ + mock_azure.sdk.requests.exceptions.ConnectionError, + mock_azure.sdk.requests.exceptions.Timeout, + mock_azure.sdk.requests.exceptions.HTTPError, + mock_result, + ] + management_group_id = str(uuid4()) payload = SubscriptionCreationCSPPayload( **dict( @@ -802,6 +914,12 @@ def test_create_subscription_creation(mock_azure: AzureCloudProvider): invoice_section_name="6HMZ-2HLO-PJA-TGB", ) ) + with pytest.raises(ConnectionException): + mock_azure.create_subscription_creation(payload) + with pytest.raises(ConnectionException): + mock_azure.create_subscription_creation(payload) + with pytest.raises(UnknownServerException): + mock_azure.create_subscription_creation(payload) result: SubscriptionCreationCSPResult = mock_azure.create_subscription_creation( payload @@ -823,7 +941,13 @@ def test_create_subscription_verification(mock_azure: AzureCloudProvider): mock_result.json.return_value = { "subscriptionLink": "/subscriptions/60fbbb72-0516-4253-ab18-c92432ba3230" } - mock_azure.sdk.requests.get.return_value = mock_result + + mock_azure.sdk.requests.get.side_effect = [ + mock_azure.sdk.requests.exceptions.ConnectionError, + mock_azure.sdk.requests.exceptions.Timeout, + mock_azure.sdk.requests.exceptions.HTTPError, + mock_result, + ] payload = SubscriptionVerificationCSPPayload( **dict( @@ -831,6 +955,12 @@ def test_create_subscription_verification(mock_azure: AzureCloudProvider): subscription_verify_url="https://verify.me", ) ) + with pytest.raises(ConnectionException): + mock_azure.create_subscription_verification(payload) + with pytest.raises(ConnectionException): + mock_azure.create_subscription_verification(payload) + with pytest.raises(UnknownServerException): + mock_azure.create_subscription_verification(payload) result: SuscriptionVerificationCSPResult = mock_azure.create_subscription_verification( payload @@ -859,7 +989,14 @@ def test_get_reporting_data(mock_azure: AzureCloudProvider): "type": "Microsoft.CostManagement/query", } mock_result.ok = True - mock_azure.sdk.requests.post.return_value = mock_result + + mock_azure.sdk.requests.post.side_effect = [ + mock_azure.sdk.requests.exceptions.ConnectionError, + mock_azure.sdk.requests.exceptions.Timeout, + mock_azure.sdk.requests.exceptions.HTTPError, + mock_result, + ] + mock_azure = mock_get_secret(mock_azure) # Subset of a profile's CSP data that we care about for reporting @@ -873,14 +1010,19 @@ def test_get_reporting_data(mock_azure: AzureCloudProvider): ], }, } - - data: CostManagementQueryCSPResult = mock_azure.get_reporting_data( - ReportingCSPPayload( - from_date=pendulum.now().subtract(years=1).add(days=1).format("YYYY-MM-DD"), - to_date=pendulum.now().format("YYYY-MM-DD"), - **csp_data, - ) + payload = ReportingCSPPayload( + from_date=pendulum.now().subtract(years=1).add(days=1).format("YYYY-MM-DD"), + to_date=pendulum.now().format("YYYY-MM-DD"), + **csp_data, ) + with pytest.raises(ConnectionException): + mock_azure.get_reporting_data(payload) + with pytest.raises(ConnectionException): + mock_azure.get_reporting_data(payload) + with pytest.raises(UnknownServerException): + mock_azure.get_reporting_data(payload) + + data: CostManagementQueryCSPResult = mock_azure.get_reporting_data(payload) assert isinstance(data, CostManagementQueryCSPResult) assert data.name == "e82d0cda-2ffb-4476-a98a-425c83c216f9" @@ -890,7 +1032,14 @@ def test_get_reporting_data(mock_azure: AzureCloudProvider): def test_get_reporting_data_malformed_payload(mock_azure: AzureCloudProvider): mock_result = Mock() mock_result.ok = True - mock_azure.sdk.requests.post.return_value = mock_result + + mock_azure.sdk.requests.post.side_effect = [ + mock_azure.sdk.requests.exceptions.ConnectionError, + mock_azure.sdk.requests.exceptions.Timeout, + mock_azure.sdk.requests.exceptions.HTTPError, + mock_result, + ] + mock_azure = mock_get_secret(mock_azure) # Malformed csp_data payloads that should throw pydantic validation errors @@ -946,7 +1095,12 @@ def test_create_active_directory_user(mock_azure: AzureCloudProvider): mock_result = Mock() mock_result.ok = True mock_result.json.return_value = {"id": "id"} - mock_azure.sdk.requests.post.return_value = mock_result + mock_azure.sdk.requests.post.side_effect = [ + mock_azure.sdk.requests.exceptions.ConnectionError, + mock_azure.sdk.requests.exceptions.Timeout, + mock_azure.sdk.requests.exceptions.HTTPError, + mock_result, + ] payload = UserCSPPayload( tenant_id=uuid4().hex, @@ -955,6 +1109,12 @@ def test_create_active_directory_user(mock_azure: AzureCloudProvider): email="test@testerson.test", password="asdfghjkl", # pragma: allowlist secret ) + with pytest.raises(ConnectionException): + mock_azure._create_active_directory_user("token", payload) + with pytest.raises(ConnectionException): + mock_azure._create_active_directory_user("token", payload) + with pytest.raises(UnknownServerException): + mock_azure._create_active_directory_user("token", payload) result = mock_azure._create_active_directory_user("token", payload) @@ -964,8 +1124,12 @@ def test_create_active_directory_user(mock_azure: AzureCloudProvider): def test_update_active_directory_user_email(mock_azure: AzureCloudProvider): mock_result = Mock() mock_result.ok = True - mock_azure.sdk.requests.patch.return_value = mock_result - + mock_azure.sdk.requests.patch.side_effect = [ + mock_azure.sdk.requests.exceptions.ConnectionError, + mock_azure.sdk.requests.exceptions.Timeout, + mock_azure.sdk.requests.exceptions.HTTPError, + mock_result, + ] payload = UserCSPPayload( tenant_id=uuid4().hex, display_name="Test Testerson", @@ -973,6 +1137,12 @@ def test_update_active_directory_user_email(mock_azure: AzureCloudProvider): email="test@testerson.test", password="asdfghjkl", # pragma: allowlist secret ) + with pytest.raises(ConnectionException): + mock_azure._update_active_directory_user_email("token", uuid4().hex, payload) + with pytest.raises(ConnectionException): + mock_azure._update_active_directory_user_email("token", uuid4().hex, payload) + with pytest.raises(UnknownServerException): + mock_azure._update_active_directory_user_email("token", uuid4().hex, payload) result = mock_azure._update_active_directory_user_email( "token", uuid4().hex, payload @@ -1007,5 +1177,4 @@ def test_create_user(mock_azure: AzureCloudProvider): ) result = mock_azure.create_user(payload) - assert result.id == "id" diff --git a/tests/domain/cloud/test_mock_csp.py b/tests/domain/cloud/test_mock_csp.py index fc5ead11..098e91b1 100644 --- a/tests/domain/cloud/test_mock_csp.py +++ b/tests/domain/cloud/test_mock_csp.py @@ -45,7 +45,6 @@ def test_create_tenant(mock_csp: MockCloudProvider): last_name="Tenet", country_code="US", password_recovery_email_address="thomas@promptworks.com", - ) ) result = mock_csp.create_tenant(payload) diff --git a/tests/domain/test_portfolio_state_machine.py b/tests/domain/test_portfolio_state_machine.py index a8dddbb2..02ed4e58 100644 --- a/tests/domain/test_portfolio_state_machine.py +++ b/tests/domain/test_portfolio_state_machine.py @@ -34,6 +34,7 @@ from atst.models.portfolio_state_machine import ( class AzureStagesTest(Enum): TENANT = "tenant" + @pytest.fixture(scope="function") def portfolio(): # TODO: setup clin/to as active/funded/ready @@ -62,27 +63,56 @@ def test_state_machine_compose_state(): == FSMStates.TENANT_CREATED ) + def test_stage_to_classname(): - assert (_stage_to_classname(AzureStages.BILLING_PROFILE_CREATION.name) == "BillingProfileCreation") + assert ( + _stage_to_classname(AzureStages.BILLING_PROFILE_CREATION.name) + == "BillingProfileCreation" + ) + def test_get_stage_csp_class(): csp_class = get_stage_csp_class(list(AzureStages)[0].name.lower(), "payload") assert isinstance(csp_class, pydantic.main.ModelMetaclass) + def test_get_stage_csp_class_import_fail(): with pytest.raises(StateMachineMisconfiguredError): csp_class = get_stage_csp_class("doesnotexist", "payload") + def test_build_transitions(): states, transitions = _build_transitions(AzureStagesTest) - assert [s.get('name').name for s in states] == ['TENANT_CREATED', 'TENANT_IN_PROGRESS', 'TENANT_FAILED'] - assert [s.get('tags') for s in states] == [['TENANT', 'CREATED'], ['TENANT', 'IN_PROGRESS'], ['TENANT', 'FAILED']] - assert [t.get('trigger') for t in transitions] == ['create_tenant', 'finish_tenant', 'fail_tenant', 'resume_progress_tenant'] + assert [s.get("name").name for s in states] == [ + "TENANT_CREATED", + "TENANT_IN_PROGRESS", + "TENANT_FAILED", + ] + assert [s.get("tags") for s in states] == [ + ["TENANT", "CREATED"], + ["TENANT", "IN_PROGRESS"], + ["TENANT", "FAILED"], + ] + assert [t.get("trigger") for t in transitions] == [ + "create_tenant", + "finish_tenant", + "fail_tenant", + "resume_progress_tenant", + ] + def test_build_csp_states(): states = _build_csp_states(AzureStagesTest) - assert list(states) == ['UNSTARTED', 'STARTING', 'STARTED', 'COMPLETED', 'FAILED', 'TENANT_CREATED', 'TENANT_IN_PROGRESS', 'TENANT_FAILED'] - + assert list(states) == [ + "UNSTARTED", + "STARTING", + "STARTED", + "COMPLETED", + "FAILED", + "TENANT_CREATED", + "TENANT_IN_PROGRESS", + "TENANT_FAILED", + ] def test_state_machine_valid_data_classes_for_stages(portfolio): @@ -96,21 +126,34 @@ def test_attach_machine(portfolio): sm = PortfolioStateMachineFactory.create(portfolio=portfolio) sm.machine = None sm.attach_machine(stages=AzureStagesTest) - assert list(sm.machine.events) == ['init', 'start', 'reset', 'fail', 'create_tenant', 'finish_tenant', 'fail_tenant', 'resume_progress_tenant'] + assert list(sm.machine.events) == [ + "init", + "start", + "reset", + "fail", + "create_tenant", + "finish_tenant", + "fail_tenant", + "resume_progress_tenant", + ] def test_fail_stage(portfolio): sm = PortfolioStateMachineFactory.create(portfolio=portfolio) sm.state = FSMStates.TENANT_IN_PROGRESS - sm.fail_stage('tenant') + sm.fail_stage("tenant") assert sm.state == FSMStates.TENANT_FAILED - #import ipdb;ipdb.set_trace() + # import ipdb;ipdb.set_trace() + def test_stage_state_to_stage_name(): - #sm = PortfolioStateMachineFactory.create(portfolio=portfolio) - stage = _stage_state_to_stage_name(FSMStates.TENANT_IN_PROGRESS, StageStates.IN_PROGRESS) + # sm = PortfolioStateMachineFactory.create(portfolio=portfolio) + stage = _stage_state_to_stage_name( + FSMStates.TENANT_IN_PROGRESS, StageStates.IN_PROGRESS + ) assert stage == "tenant" + def test_state_machine_initialization(portfolio): sm = PortfolioStateMachineFactory.create(portfolio=portfolio) @@ -180,7 +223,7 @@ def test_fsm_transition_start(mock_cloud_provider, portfolio: Portfolio): csp_data = {} ppoc = portfolio.owner - user_id = "johndoe" # f"{ppoc.first_name[0]}{ppoc.last_name}".lower() + user_id = f"{ppoc.first_name[0]}{ppoc.last_name}".lower() domain_name = re.sub("[^0-9a-zA-Z]+", "", portfolio.name).lower() initial_task_order: TaskOrder = portfolio.task_orders[0] @@ -190,10 +233,10 @@ def test_fsm_transition_start(mock_cloud_provider, portfolio: Portfolio): "user_id": user_id, "password": "jklfsdNCVD83nklds2#202", # pragma: allowlist secret "domain_name": domain_name, - "first_name": "123", # ppoc.first_name, - "last_name": "123", # ppoc.last_name, + "first_name": ppoc.first_name, + "last_name": ppoc.last_name, "country_code": "US", - "password_recovery_email_address": "email@example.com", # ppoc.email, + "password_recovery_email_address": ppoc.email, "address": { # TODO: TBD if we're sourcing this from data or config "company_name": "", "address_line_1": "", @@ -222,4 +265,3 @@ def test_fsm_transition_start(mock_cloud_provider, portfolio: Portfolio): csp_data = portfolio.csp_data else: csp_data = {} - diff --git a/tests/mock_azure.py b/tests/mock_azure.py index 37a106ab..7fbc351a 100644 --- a/tests/mock_azure.py +++ b/tests/mock_azure.py @@ -79,7 +79,9 @@ def mock_adal(): def mock_requests(): import requests - return Mock(wraps=requests) + mock_requests = Mock(wraps=requests) + mock_requests.exceptions = requests.exceptions + return mock_requests def mock_secrets():