portfolio provision process. add logging, __repr___ method, fix broken tests
This commit is contained in:
parent
00b10c484f
commit
597ea32e42
@ -4,6 +4,8 @@ from uuid import uuid4
|
|||||||
|
|
||||||
from pydantic import BaseModel, validator
|
from pydantic import BaseModel, validator
|
||||||
|
|
||||||
|
from flask import current_app as app
|
||||||
|
|
||||||
from atst.models.user import User
|
from atst.models.user import User
|
||||||
from atst.models.application import Application
|
from atst.models.application import Application
|
||||||
from atst.models.environment import Environment
|
from atst.models.environment import Environment
|
||||||
@ -754,6 +756,7 @@ class AzureSDKProvider(object):
|
|||||||
self.graphrbac = graphrbac
|
self.graphrbac = graphrbac
|
||||||
self.credentials = credentials
|
self.credentials = credentials
|
||||||
self.identity = identity
|
self.identity = identity
|
||||||
|
self.exceptions = exceptions
|
||||||
self.secrets = secrets
|
self.secrets = secrets
|
||||||
self.requests = requests
|
self.requests = requests
|
||||||
# may change to a JEDI cloud
|
# may change to a JEDI cloud
|
||||||
@ -781,14 +784,21 @@ class AzureCloudProvider(CloudProviderInterface):
|
|||||||
secret_client = self.secrets.SecretClient(
|
secret_client = self.secrets.SecretClient(
|
||||||
vault_url=self.vault_url, credential=credential,
|
vault_url=self.vault_url, credential=credential,
|
||||||
)
|
)
|
||||||
return secret_client.set_secret(secret_key, secret_value)
|
try:
|
||||||
|
return secret_client.set_secret(secret_key, secret_value)
|
||||||
|
except self.exceptions.HttpResponseError as exc:
|
||||||
|
app.logger.error(f"Could not SET secret in Azure keyvault for key {secret_key}.", exc_info=1)
|
||||||
|
|
||||||
|
|
||||||
def get_secret(secret_key):
|
def get_secret(secret_key):
|
||||||
credential = self._get_client_secret_credential_obj()
|
credential = self._get_client_secret_credential_obj()
|
||||||
secret_client = self.secrets.SecretClient(
|
secret_client = self.secrets.SecretClient(
|
||||||
vault_url=self.vault_url, credential=credential,
|
vault_url=self.vault_url, credential=credential,
|
||||||
)
|
)
|
||||||
return secret_client.get_secret(secret_key).value
|
try:
|
||||||
|
return secret_client.get_secret(secret_key).value
|
||||||
|
except self.exceptions.HttpResponseError as exc:
|
||||||
|
app.logger.error(f"Could not GET secret in Azure keyvault for key {secret_key}.", exc_info=1)
|
||||||
|
|
||||||
def create_environment(
|
def create_environment(
|
||||||
self, auth_credentials: Dict, user: User, environment: Environment
|
self, auth_credentials: Dict, user: User, environment: Environment
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
from enum import Enum
|
from enum import Enum
|
||||||
|
|
||||||
|
from flask import current_app as app
|
||||||
|
|
||||||
|
|
||||||
class StageStates(Enum):
|
class StageStates(Enum):
|
||||||
CREATED = "created"
|
CREATED = "created"
|
||||||
@ -107,10 +109,12 @@ class FSMMixin:
|
|||||||
fail_trigger = "fail" + stage
|
fail_trigger = "fail" + stage
|
||||||
if fail_trigger in self.machine.get_triggers(self.current_state.name):
|
if fail_trigger in self.machine.get_triggers(self.current_state.name):
|
||||||
self.trigger(fail_trigger)
|
self.trigger(fail_trigger)
|
||||||
|
app.logger.info(f"calling fail trigger '{fail_trigger}' for '{self.__repr__()}'")
|
||||||
|
|
||||||
def finish_stage(self, stage):
|
def finish_stage(self, stage):
|
||||||
finish_trigger = "finish_" + stage
|
finish_trigger = "finish_" + stage
|
||||||
if finish_trigger in self.machine.get_triggers(self.current_state.name):
|
if finish_trigger in self.machine.get_triggers(self.current_state.name):
|
||||||
|
app.logger.info(f"calling finish trigger '{finish_trigger}' for '{self.__repr__()}'")
|
||||||
self.trigger(finish_trigger)
|
self.trigger(finish_trigger)
|
||||||
|
|
||||||
def prepare_init(self, event):
|
def prepare_init(self, event):
|
||||||
|
@ -64,6 +64,10 @@ class PortfolioStateMachine(
|
|||||||
db.session.add(self)
|
db.session.add(self)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return f"<PortfolioStateMachine(state='{self.current_state.name}', portfolio='{self.portfolio.name}'"
|
||||||
|
|
||||||
|
|
||||||
@reconstructor
|
@reconstructor
|
||||||
def attach_machine(self):
|
def attach_machine(self):
|
||||||
"""
|
"""
|
||||||
@ -108,6 +112,7 @@ class PortfolioStateMachine(
|
|||||||
if create_trigger:
|
if create_trigger:
|
||||||
self.trigger(create_trigger, **kwargs)
|
self.trigger(create_trigger, **kwargs)
|
||||||
else:
|
else:
|
||||||
|
app.logger.info(f"could not locate 'create trigger' for {self.__repr__()}")
|
||||||
self.fail_stage(stage)
|
self.fail_stage(stage)
|
||||||
|
|
||||||
elif state_obj.is_CREATED:
|
elif state_obj.is_CREATED:
|
||||||
@ -133,15 +138,14 @@ class PortfolioStateMachine(
|
|||||||
|
|
||||||
payload_data_cls = get_stage_csp_class(stage, "payload")
|
payload_data_cls = get_stage_csp_class(stage, "payload")
|
||||||
if not payload_data_cls:
|
if not payload_data_cls:
|
||||||
print("could not resolve payload data class")
|
app.logger.info(f"could not resolve payload data class for stage {stage}")
|
||||||
self.fail_stage(stage)
|
self.fail_stage(stage)
|
||||||
try:
|
try:
|
||||||
payload_data = payload_data_cls(**payload)
|
payload_data = payload_data_cls(**payload)
|
||||||
except PydanticValidationError as exc:
|
except PydanticValidationError as exc:
|
||||||
print("Payload Validation Error:")
|
app.logger.error(f"Payload Validation Error in {self.__repr__()}:", exc_info=1)
|
||||||
print(exc.json())
|
app.logger.info(exc.json())
|
||||||
print("got")
|
app.logger.info(payload)
|
||||||
print(payload)
|
|
||||||
self.fail_stage(stage)
|
self.fail_stage(stage)
|
||||||
|
|
||||||
# TODO: Determine best place to do this, maybe @reconstructor
|
# TODO: Determine best place to do this, maybe @reconstructor
|
||||||
@ -151,18 +155,19 @@ class PortfolioStateMachine(
|
|||||||
else:
|
else:
|
||||||
self.csp = MockCSP(app).cloud
|
self.csp = MockCSP(app).cloud
|
||||||
|
|
||||||
for attempt in range(5):
|
attempts_count = 5
|
||||||
|
for attempt in range(attempts_count):
|
||||||
try:
|
try:
|
||||||
func_name = f"create_{stage}"
|
func_name = f"create_{stage}"
|
||||||
response = getattr(self.csp, func_name)(payload_data)
|
response = getattr(self.csp, func_name)(payload_data)
|
||||||
except (ConnectionException, UnknownServerException) as exc:
|
except (ConnectionException, UnknownServerException) as exc:
|
||||||
print("caught exception. retry", attempt)
|
app.logger.error(f"CSP api call. Caught exception for {self.__repr__()}. Retry attempt {attempt}", exc_info=1)
|
||||||
continue
|
continue
|
||||||
else:
|
else:
|
||||||
break
|
break
|
||||||
else:
|
else:
|
||||||
# failed all attempts
|
# failed all attempts
|
||||||
print("failed")
|
logger.info(f"CSP api call failed after {attempts_count} attempts.")
|
||||||
self.fail_stage(stage)
|
self.fail_stage(stage)
|
||||||
|
|
||||||
if self.portfolio.csp_data is None:
|
if self.portfolio.csp_data is None:
|
||||||
@ -177,7 +182,6 @@ class PortfolioStateMachine(
|
|||||||
|
|
||||||
def is_csp_data_valid(self, event):
|
def is_csp_data_valid(self, event):
|
||||||
# check portfolio csp details json field for fields
|
# check portfolio csp details json field for fields
|
||||||
|
|
||||||
if self.portfolio.csp_data is None or not isinstance(
|
if self.portfolio.csp_data is None or not isinstance(
|
||||||
self.portfolio.csp_data, dict
|
self.portfolio.csp_data, dict
|
||||||
):
|
):
|
||||||
@ -199,9 +203,10 @@ class PortfolioStateMachine(
|
|||||||
# self.store_creds(self.portfolio, new_creds)
|
# self.store_creds(self.portfolio, new_creds)
|
||||||
|
|
||||||
except PydanticValidationError as exc:
|
except PydanticValidationError as exc:
|
||||||
print("is_csp_data_valid: False")
|
app.logger.error(f"Payload Validation Error in {self.__repr__()}:", exc_info=1)
|
||||||
print(cls)
|
app.logger.info(exc.json())
|
||||||
print(exc.json())
|
app.logger.info(payload)
|
||||||
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
@ -40,13 +40,6 @@ def test_state_machine_compose_state(portfolio):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def test_state_machine_first_stage_create_trigger(portfolio):
|
|
||||||
sm = PortfolioStateMachineFactory.create(portfolio=portfolio)
|
|
||||||
first_stage_create_trigger = sm._get_first_stage_create_trigger()
|
|
||||||
first_stage_name = list(AzureStages)[0].name.lower()
|
|
||||||
assert "create_" + first_stage_name == first_stage_create_trigger
|
|
||||||
|
|
||||||
|
|
||||||
def test_state_machine_valid_data_classes_for_stages(portfolio):
|
def test_state_machine_valid_data_classes_for_stages(portfolio):
|
||||||
sm = PortfolioStateMachineFactory.create(portfolio=portfolio)
|
sm = PortfolioStateMachineFactory.create(portfolio=portfolio)
|
||||||
for stage in AzureStages:
|
for stage in AzureStages:
|
||||||
@ -74,8 +67,14 @@ def test_state_machine_initialization(portfolio):
|
|||||||
] == in_progress_triggers
|
] == in_progress_triggers
|
||||||
|
|
||||||
started_triggers = sm.machine.get_triggers("STARTED")
|
started_triggers = sm.machine.get_triggers("STARTED")
|
||||||
first_stage_create_trigger = sm._get_first_stage_create_trigger()
|
create_trigger = next(
|
||||||
assert ["reset", "fail", first_stage_create_trigger] == started_triggers
|
filter(
|
||||||
|
lambda trigger: trigger.startswith("create_"),
|
||||||
|
sm.machine.get_triggers(FSMStates.STARTED.name),
|
||||||
|
),
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
assert ["reset", "fail", create_trigger] == started_triggers
|
||||||
|
|
||||||
|
|
||||||
def test_fsm_transition_start(portfolio):
|
def test_fsm_transition_start(portfolio):
|
||||||
@ -96,18 +95,19 @@ def test_fsm_transition_start(portfolio):
|
|||||||
else:
|
else:
|
||||||
csp_data = {}
|
csp_data = {}
|
||||||
|
|
||||||
ppoc = portfolio.owner
|
# ppoc = portfolio.owner
|
||||||
user_id = f"{ppoc.first_name[0]}{ppoc.last_name}".lower()
|
# user_id = f"{ppoc.first_name[0]}{ppoc.last_name}".lower()
|
||||||
|
user_id = "abcdefg"
|
||||||
domain_name = re.sub("[^0-9a-zA-Z]+", "", portfolio.name).lower()
|
domain_name = re.sub("[^0-9a-zA-Z]+", "", portfolio.name).lower()
|
||||||
|
|
||||||
portfolio_data = {
|
portfolio_data = {
|
||||||
"user_id": user_id,
|
"user_id": user_id,
|
||||||
"password": "jklfsdNCVD83nklds2#202",
|
"password": "jklfsdNCVD83nklds2#202",
|
||||||
"domain_name": domain_name,
|
"domain_name": domain_name,
|
||||||
"first_name": ppoc.first_name,
|
"first_name": "john", # ppoc.first_name,
|
||||||
"last_name": ppoc.last_name,
|
"last_name": "doe", # ppoc.last_name,
|
||||||
"country_code": "US",
|
"country_code": "US",
|
||||||
"password_recovery_email_address": ppoc.email,
|
"password_recovery_email_address": "email@example.com", # ppoc.email,
|
||||||
"address": {
|
"address": {
|
||||||
"company_name": "",
|
"company_name": "",
|
||||||
"address_line_1": "",
|
"address_line_1": "",
|
||||||
@ -128,7 +128,7 @@ def test_fsm_transition_start(portfolio):
|
|||||||
|
|
||||||
assert sm.state == FSMStates.TENANT_CREATED
|
assert sm.state == FSMStates.TENANT_CREATED
|
||||||
assert portfolio.csp_data.get("tenant_id", None) is not None
|
assert portfolio.csp_data.get("tenant_id", None) is not None
|
||||||
print(portfolio.csp_data.keys())
|
#print(portfolio.csp_data.keys())
|
||||||
if portfolio.csp_data is not None:
|
if portfolio.csp_data is not None:
|
||||||
csp_data = portfolio.csp_data
|
csp_data = portfolio.csp_data
|
||||||
else:
|
else:
|
||||||
@ -137,6 +137,6 @@ def test_fsm_transition_start(portfolio):
|
|||||||
list(csp_data.items()) + list(portfolio_data.items()) + list(config.items())
|
list(csp_data.items()) + list(portfolio_data.items()) + list(config.items())
|
||||||
)
|
)
|
||||||
sm.trigger_next_transition(creds=creds, csp_data=collected_data)
|
sm.trigger_next_transition(creds=creds, csp_data=collected_data)
|
||||||
assert sm.state == FSMStates.BILLING_PROFILE_CREATED
|
assert sm.state == FSMStates.BILLING_PROFILE_CREATION_CREATED
|
||||||
|
|
||||||
print(portfolio.csp_data.keys())
|
#print(portfolio.csp_data.keys())
|
||||||
|
Loading…
x
Reference in New Issue
Block a user