resolve conflict with staging
This commit is contained in:
@@ -5,6 +5,8 @@ from uuid import uuid4
|
||||
import pytest
|
||||
from tests.factories import ApplicationFactory, EnvironmentFactory
|
||||
from tests.mock_azure import AUTH_CREDENTIALS, mock_azure
|
||||
import pendulum
|
||||
import pydantic
|
||||
|
||||
from atst.domain.csp.cloud import AzureCloudProvider
|
||||
from atst.domain.csp.cloud.models import (
|
||||
@@ -24,10 +26,12 @@ from atst.domain.csp.cloud.models import (
|
||||
ManagementGroupCSPResponse,
|
||||
ManagementGroupGetCSPPayload,
|
||||
ManagementGroupGetCSPResponse,
|
||||
CostManagementQueryCSPResult,
|
||||
ProductPurchaseCSPPayload,
|
||||
ProductPurchaseCSPResult,
|
||||
ProductPurchaseVerificationCSPPayload,
|
||||
ProductPurchaseVerificationCSPResult,
|
||||
ReportingCSPPayload,
|
||||
SubscriptionCreationCSPPayload,
|
||||
SubscriptionCreationCSPResult,
|
||||
SubscriptionVerificationCSPPayload,
|
||||
@@ -765,3 +769,77 @@ def test_create_subscription_verification(mock_azure: AzureCloudProvider):
|
||||
payload
|
||||
)
|
||||
assert result.subscription_id == "60fbbb72-0516-4253-ab18-c92432ba3230"
|
||||
|
||||
|
||||
def test_get_reporting_data(mock_azure: AzureCloudProvider):
|
||||
mock_result = Mock()
|
||||
mock_result.json.return_value = {
|
||||
"eTag": None,
|
||||
"id": "providers/Microsoft.Billing/billingAccounts/52865e4c-52e8-5a6c-da6b-c58f0814f06f:7ea5de9d-b8ce-4901-b1c5-d864320c7b03_2019-05-31/billingProfiles/XQDJ-6LB4-BG7-TGB/invoiceSections/P73M-XC7J-PJA-TGB/providers/Microsoft.CostManagement/query/e82d0cda-2ffb-4476-a98a-425c83c216f9",
|
||||
"location": None,
|
||||
"name": "e82d0cda-2ffb-4476-a98a-425c83c216f9",
|
||||
"properties": {
|
||||
"columns": [
|
||||
{"name": "PreTaxCost", "type": "Number"},
|
||||
{"name": "UsageDate", "type": "Number"},
|
||||
{"name": "InvoiceId", "type": "String"},
|
||||
{"name": "Currency", "type": "String"},
|
||||
],
|
||||
"nextLink": None,
|
||||
"rows": [],
|
||||
},
|
||||
"sku": None,
|
||||
"type": "Microsoft.CostManagement/query",
|
||||
}
|
||||
mock_result.ok = True
|
||||
mock_azure.sdk.requests.post.return_value = mock_result
|
||||
mock_azure = mock_get_secret(mock_azure)
|
||||
|
||||
# Subset of a profile's CSP data that we care about for reporting
|
||||
csp_data = {
|
||||
"tenant_id": "6d2d2d6c-a6d6-41e1-8bb1-73d11475f8f4",
|
||||
"billing_profile_properties": {
|
||||
"invoice_sections": [
|
||||
{
|
||||
"invoice_section_id": "providers/Microsoft.Billing/billingAccounts/52865e4c-52e8-5a6c-da6b-c58f0814f06f:7ea5de9d-b8ce-4901-b1c5-d864320c7b03_2019-05-31/billingProfiles/XQDJ-6LB4-BG7-TGB/invoiceSections/P73M-XC7J-PJA-TGB",
|
||||
}
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
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,
|
||||
)
|
||||
)
|
||||
|
||||
assert isinstance(data, CostManagementQueryCSPResult)
|
||||
assert data.name == "e82d0cda-2ffb-4476-a98a-425c83c216f9"
|
||||
assert len(data.properties.columns) == 4
|
||||
|
||||
|
||||
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 = mock_get_secret(mock_azure)
|
||||
|
||||
# Malformed csp_data payloads that should throw pydantic validation errors
|
||||
index_error = {
|
||||
"tenant_id": "6d2d2d6c-a6d6-41e1-8bb1-73d11475f8f4",
|
||||
"billing_profile_properties": {"invoice_sections": [],},
|
||||
}
|
||||
key_error = {
|
||||
"tenant_id": "6d2d2d6c-a6d6-41e1-8bb1-73d11475f8f4",
|
||||
"billing_profile_properties": {"invoice_sections": [{}],},
|
||||
}
|
||||
|
||||
for malformed_payload in [key_error, index_error]:
|
||||
with pytest.raises(pydantic.ValidationError):
|
||||
assert mock_azure.get_reporting_data(
|
||||
ReportingCSPPayload(
|
||||
from_date="foo", to_date="bar", **malformed_payload,
|
||||
)
|
||||
)
|
||||
|
@@ -7,6 +7,7 @@ from atst.domain.csp.cloud.models import (
|
||||
KeyVaultCredentials,
|
||||
ManagementGroupCSPPayload,
|
||||
ManagementGroupCSPResponse,
|
||||
UserCSPPayload,
|
||||
)
|
||||
|
||||
|
||||
@@ -97,3 +98,26 @@ def test_KeyVaultCredentials_enforce_root_creds():
|
||||
assert KeyVaultCredentials(
|
||||
root_tenant_id="an id", root_sp_client_id="C3PO", root_sp_key="beep boop"
|
||||
)
|
||||
|
||||
|
||||
user_payload = {
|
||||
"tenant_id": "123",
|
||||
"display_name": "Han Solo",
|
||||
"tenant_host_name": "rebelalliance",
|
||||
"email": "han@moseisley.cantina",
|
||||
}
|
||||
|
||||
|
||||
def test_UserCSPPayload_mail_nickname():
|
||||
payload = UserCSPPayload(**user_payload)
|
||||
assert payload.mail_nickname == f"han.solo"
|
||||
|
||||
|
||||
def test_UserCSPPayload_user_principal_name():
|
||||
payload = UserCSPPayload(**user_payload)
|
||||
assert payload.user_principal_name == f"han.solo@rebelalliance.onmicrosoft.com"
|
||||
|
||||
|
||||
def test_UserCSPPayload_password():
|
||||
payload = UserCSPPayload(**user_payload)
|
||||
assert payload.password
|
||||
|
@@ -86,3 +86,79 @@ def test_disable(session):
|
||||
session.refresh(environment_role)
|
||||
assert member_role.status == ApplicationRoleStatus.DISABLED
|
||||
assert environment_role.deleted
|
||||
|
||||
|
||||
def test_get_pending_creation():
|
||||
|
||||
# ready Applications belonging to the same Portfolio
|
||||
portfolio_one = PortfolioFactory.create()
|
||||
ready_app = ApplicationFactory.create(cloud_id="123", portfolio=portfolio_one)
|
||||
ready_app2 = ApplicationFactory.create(cloud_id="321", portfolio=portfolio_one)
|
||||
|
||||
# ready Application belonging to a new Portfolio
|
||||
ready_app3 = ApplicationFactory.create(cloud_id="567")
|
||||
unready_app = ApplicationFactory.create()
|
||||
|
||||
# two distinct Users
|
||||
user_one = UserFactory.create()
|
||||
user_two = UserFactory.create()
|
||||
|
||||
# Two ApplicationRoles belonging to the same User and
|
||||
# different Applications. These should sort together because
|
||||
# they are all under the same portfolio (portfolio_one).
|
||||
role_one = ApplicationRoleFactory.create(
|
||||
user=user_one, application=ready_app, status=ApplicationRoleStatus.ACTIVE
|
||||
)
|
||||
role_two = ApplicationRoleFactory.create(
|
||||
user=user_one, application=ready_app2, status=ApplicationRoleStatus.ACTIVE
|
||||
)
|
||||
|
||||
# An ApplicationRole belonging to a different User. This will
|
||||
# be included but sort separately because it belongs to a
|
||||
# different user.
|
||||
role_three = ApplicationRoleFactory.create(
|
||||
user=user_two, application=ready_app, status=ApplicationRoleStatus.ACTIVE
|
||||
)
|
||||
|
||||
# An ApplicationRole belonging to one of the existing users
|
||||
# but under a different portfolio. It will sort separately.
|
||||
role_four = ApplicationRoleFactory.create(
|
||||
user=user_one, application=ready_app3, status=ApplicationRoleStatus.ACTIVE
|
||||
)
|
||||
|
||||
# This ApplicationRole will not be in the results because its
|
||||
# application is not ready (implicitly, its cloud_id is not
|
||||
# set.)
|
||||
ApplicationRoleFactory.create(
|
||||
user=UserFactory.create(),
|
||||
application=unready_app,
|
||||
status=ApplicationRoleStatus.ACTIVE,
|
||||
)
|
||||
|
||||
# This ApplicationRole will not be in the results because it
|
||||
# does not have a user associated.
|
||||
ApplicationRoleFactory.create(
|
||||
user=None, application=ready_app, status=ApplicationRoleStatus.ACTIVE,
|
||||
)
|
||||
|
||||
# This ApplicationRole will not be in the results because its
|
||||
# status is not ACTIVE.
|
||||
ApplicationRoleFactory.create(
|
||||
user=UserFactory.create(),
|
||||
application=unready_app,
|
||||
status=ApplicationRoleStatus.DISABLED,
|
||||
)
|
||||
|
||||
app_ids = ApplicationRoles.get_pending_creation()
|
||||
expected_ids = [[role_one.id, role_two.id], [role_three.id], [role_four.id]]
|
||||
# Sort them to produce the same order.
|
||||
assert sorted(app_ids) == sorted(expected_ids)
|
||||
|
||||
|
||||
def test_get_many():
|
||||
ar1 = ApplicationRoleFactory.create()
|
||||
ar2 = ApplicationRoleFactory.create()
|
||||
ApplicationRoleFactory.create()
|
||||
|
||||
result = ApplicationRoles.get_many([ar1.id, ar2.id])
|
||||
assert result == [ar1, ar2]
|
||||
|
@@ -1,5 +1,4 @@
|
||||
import pytest
|
||||
import pendulum
|
||||
from uuid import uuid4
|
||||
|
||||
from atst.domain.environments import Environments
|
||||
@@ -14,6 +13,7 @@ from tests.factories import (
|
||||
EnvironmentRoleFactory,
|
||||
ApplicationRoleFactory,
|
||||
)
|
||||
from tests.utils import EnvQueryTest
|
||||
|
||||
|
||||
def test_create_environments():
|
||||
@@ -119,40 +119,6 @@ def test_update_does_not_duplicate_names_within_application():
|
||||
Environments.update(dupe_env, name)
|
||||
|
||||
|
||||
class EnvQueryTest:
|
||||
@property
|
||||
def NOW(self):
|
||||
return pendulum.now()
|
||||
|
||||
@property
|
||||
def YESTERDAY(self):
|
||||
return self.NOW.subtract(days=1)
|
||||
|
||||
@property
|
||||
def TOMORROW(self):
|
||||
return self.NOW.add(days=1)
|
||||
|
||||
def create_portfolio_with_clins(self, start_and_end_dates, env_data=None):
|
||||
env_data = env_data or {}
|
||||
return PortfolioFactory.create(
|
||||
applications=[
|
||||
{
|
||||
"name": "Mos Eisley",
|
||||
"description": "Where Han shot first",
|
||||
"environments": [{"name": "thebar", **env_data}],
|
||||
}
|
||||
],
|
||||
task_orders=[
|
||||
{
|
||||
"create_clins": [
|
||||
{"start_date": start_date, "end_date": end_date}
|
||||
for (start_date, end_date) in start_and_end_dates
|
||||
]
|
||||
}
|
||||
],
|
||||
)
|
||||
|
||||
|
||||
class TestGetEnvironmentsPendingCreate(EnvQueryTest):
|
||||
def test_with_expired_clins(self, session):
|
||||
self.create_portfolio_with_clins([(self.YESTERDAY, self.YESTERDAY)])
|
||||
|
@@ -26,6 +26,7 @@ from tests.factories import (
|
||||
PortfolioStateMachineFactory,
|
||||
get_all_portfolio_permission_sets,
|
||||
)
|
||||
from tests.utils import EnvQueryTest
|
||||
|
||||
|
||||
@pytest.fixture(scope="function")
|
||||
@@ -263,10 +264,44 @@ def test_create_state_machine(portfolio):
|
||||
assert fsm
|
||||
|
||||
|
||||
def test_get_portfolios_pending_provisioning(session):
|
||||
for x in range(5):
|
||||
portfolio = PortfolioFactory.create()
|
||||
sm = PortfolioStateMachineFactory.create(portfolio=portfolio)
|
||||
if x == 2:
|
||||
sm.state = FSMStates.COMPLETED
|
||||
assert len(Portfolios.get_portfolios_pending_provisioning()) == 4
|
||||
class TestGetPortfoliosPendingCreate(EnvQueryTest):
|
||||
def test_finds_unstarted(self):
|
||||
for x in range(5):
|
||||
if x == 2:
|
||||
state = "COMPLETED"
|
||||
else:
|
||||
state = "UNSTARTED"
|
||||
self.create_portfolio_with_clins(
|
||||
[(self.YESTERDAY, self.TOMORROW)], state_machine_status=state
|
||||
)
|
||||
assert len(Portfolios.get_portfolios_pending_provisioning(self.NOW)) == 4
|
||||
|
||||
def test_finds_created(self):
|
||||
self.create_portfolio_with_clins(
|
||||
[(self.YESTERDAY, self.TOMORROW)], state_machine_status="TENANT_CREATED"
|
||||
)
|
||||
assert len(Portfolios.get_portfolios_pending_provisioning(self.NOW)) == 1
|
||||
|
||||
def test_does_not_find_failed(self):
|
||||
self.create_portfolio_with_clins(
|
||||
[(self.YESTERDAY, self.TOMORROW)], state_machine_status="TENANT_FAILED"
|
||||
)
|
||||
assert len(Portfolios.get_portfolios_pending_provisioning(self.NOW)) == 0
|
||||
|
||||
def test_with_expired_clins(self):
|
||||
self.create_portfolio_with_clins([(self.YESTERDAY, self.YESTERDAY)])
|
||||
assert len(Portfolios.get_portfolios_pending_provisioning(self.NOW)) == 0
|
||||
|
||||
def test_with_active_clins(self):
|
||||
portfolio = self.create_portfolio_with_clins([(self.YESTERDAY, self.TOMORROW)])
|
||||
Portfolios.get_portfolios_pending_provisioning(self.NOW) == [portfolio.id]
|
||||
|
||||
def test_with_future_clins(self):
|
||||
self.create_portfolio_with_clins([(self.TOMORROW, self.TOMORROW)])
|
||||
assert len(Portfolios.get_portfolios_pending_provisioning(self.NOW)) == 0
|
||||
|
||||
def test_with_already_provisioned_env(self):
|
||||
self.create_portfolio_with_clins(
|
||||
[(self.YESTERDAY, self.TOMORROW)], env_data={"cloud_id": uuid4().hex}
|
||||
)
|
||||
assert len(Portfolios.get_portfolios_pending_provisioning(self.NOW)) == 0
|
||||
|
104
tests/models/test_utils.py
Normal file
104
tests/models/test_utils.py
Normal file
@@ -0,0 +1,104 @@
|
||||
from threading import Thread
|
||||
|
||||
from atst.domain.exceptions import ClaimFailedException
|
||||
from atst.models.utils import claim_for_update, claim_many_for_update
|
||||
|
||||
from tests.factories import EnvironmentFactory
|
||||
|
||||
|
||||
def test_claim_for_update(session):
|
||||
environment = EnvironmentFactory.create()
|
||||
|
||||
satisfied_claims = []
|
||||
exceptions = []
|
||||
|
||||
# Two threads race to do work on environment and check out the lock
|
||||
class FirstThread(Thread):
|
||||
def run(self):
|
||||
try:
|
||||
with claim_for_update(environment) as env:
|
||||
assert env.claimed_until
|
||||
satisfied_claims.append("FirstThread")
|
||||
except ClaimFailedException:
|
||||
exceptions.append("FirstThread")
|
||||
|
||||
class SecondThread(Thread):
|
||||
def run(self):
|
||||
try:
|
||||
with claim_for_update(environment) as env:
|
||||
assert env.claimed_until
|
||||
satisfied_claims.append("SecondThread")
|
||||
except ClaimFailedException:
|
||||
exceptions.append("SecondThread")
|
||||
|
||||
t1 = FirstThread()
|
||||
t2 = SecondThread()
|
||||
t1.start()
|
||||
t2.start()
|
||||
t1.join()
|
||||
t2.join()
|
||||
|
||||
session.refresh(environment)
|
||||
|
||||
assert len(satisfied_claims) == 1
|
||||
assert len(exceptions) == 1
|
||||
|
||||
if satisfied_claims == ["FirstThread"]:
|
||||
assert exceptions == ["SecondThread"]
|
||||
else:
|
||||
assert satisfied_claims == ["SecondThread"]
|
||||
assert exceptions == ["FirstThread"]
|
||||
|
||||
# The claim is released
|
||||
assert environment.claimed_until is None
|
||||
|
||||
|
||||
def test_claim_many_for_update(session):
|
||||
environments = [
|
||||
EnvironmentFactory.create(),
|
||||
EnvironmentFactory.create(),
|
||||
]
|
||||
|
||||
satisfied_claims = []
|
||||
exceptions = []
|
||||
|
||||
# Two threads race to do work on environment and check out the lock
|
||||
class FirstThread(Thread):
|
||||
def run(self):
|
||||
try:
|
||||
with claim_many_for_update(environments) as envs:
|
||||
assert all([e.claimed_until for e in envs])
|
||||
satisfied_claims.append("FirstThread")
|
||||
except ClaimFailedException:
|
||||
exceptions.append("FirstThread")
|
||||
|
||||
class SecondThread(Thread):
|
||||
def run(self):
|
||||
try:
|
||||
with claim_many_for_update(environments) as envs:
|
||||
assert all([e.claimed_until for e in envs])
|
||||
satisfied_claims.append("SecondThread")
|
||||
except ClaimFailedException:
|
||||
exceptions.append("SecondThread")
|
||||
|
||||
t1 = FirstThread()
|
||||
t2 = SecondThread()
|
||||
t1.start()
|
||||
t2.start()
|
||||
t1.join()
|
||||
t2.join()
|
||||
|
||||
for env in environments:
|
||||
session.refresh(env)
|
||||
|
||||
assert len(satisfied_claims) == 1
|
||||
assert len(exceptions) == 1
|
||||
|
||||
if satisfied_claims == ["FirstThread"]:
|
||||
assert exceptions == ["SecondThread"]
|
||||
else:
|
||||
assert satisfied_claims == ["SecondThread"]
|
||||
assert exceptions == ["FirstThread"]
|
||||
|
||||
# The claim is released
|
||||
# assert environment.claimed_until is None
|
@@ -2,27 +2,25 @@ import pendulum
|
||||
import pytest
|
||||
from uuid import uuid4
|
||||
from unittest.mock import Mock
|
||||
from threading import Thread
|
||||
|
||||
from atst.domain.csp.cloud import MockCloudProvider
|
||||
from atst.domain.portfolios import Portfolios
|
||||
from atst.models import ApplicationRoleStatus
|
||||
|
||||
from atst.jobs import (
|
||||
RecordFailure,
|
||||
dispatch_create_environment,
|
||||
dispatch_create_application,
|
||||
dispatch_create_user,
|
||||
dispatch_create_atat_admin_user,
|
||||
dispatch_provision_portfolio,
|
||||
dispatch_provision_user,
|
||||
create_environment,
|
||||
do_provision_user,
|
||||
do_create_user,
|
||||
do_provision_portfolio,
|
||||
do_create_environment,
|
||||
do_create_application,
|
||||
do_create_atat_admin_user,
|
||||
)
|
||||
from atst.models.utils import claim_for_update
|
||||
from atst.domain.exceptions import ClaimFailedException
|
||||
from tests.factories import (
|
||||
EnvironmentFactory,
|
||||
EnvironmentRoleFactory,
|
||||
@@ -30,6 +28,7 @@ from tests.factories import (
|
||||
PortfolioStateMachineFactory,
|
||||
ApplicationFactory,
|
||||
ApplicationRoleFactory,
|
||||
UserFactory,
|
||||
)
|
||||
from atst.models import CSPRole, EnvironmentRole, ApplicationRoleStatus, JobFailure
|
||||
|
||||
@@ -126,6 +125,30 @@ def test_create_application_job_is_idempotent(csp):
|
||||
csp.create_application.assert_not_called()
|
||||
|
||||
|
||||
def test_create_user_job(session, csp):
|
||||
portfolio = PortfolioFactory.create(
|
||||
csp_data={
|
||||
"tenant_id": str(uuid4()),
|
||||
"domain_name": "rebelalliance.onmicrosoft.com",
|
||||
}
|
||||
)
|
||||
application = ApplicationFactory.create(portfolio=portfolio, cloud_id="321")
|
||||
user = UserFactory.create(
|
||||
first_name="Han", last_name="Solo", email="han@example.com"
|
||||
)
|
||||
app_role = ApplicationRoleFactory.create(
|
||||
application=application,
|
||||
user=user,
|
||||
status=ApplicationRoleStatus.ACTIVE,
|
||||
cloud_id=None,
|
||||
)
|
||||
|
||||
do_create_user(csp, [app_role.id])
|
||||
session.refresh(app_role)
|
||||
|
||||
assert app_role.cloud_id
|
||||
|
||||
|
||||
def test_create_atat_admin_user(csp, session):
|
||||
environment = EnvironmentFactory.create(cloud_id="something")
|
||||
do_create_atat_admin_user(csp, environment.id)
|
||||
@@ -181,6 +204,29 @@ def test_dispatch_create_application(monkeypatch):
|
||||
mock.delay.assert_called_once_with(application_id=app.id)
|
||||
|
||||
|
||||
def test_dispatch_create_user(monkeypatch):
|
||||
application = ApplicationFactory.create(cloud_id="123")
|
||||
user = UserFactory.create(
|
||||
first_name="Han", last_name="Solo", email="han@example.com"
|
||||
)
|
||||
app_role = ApplicationRoleFactory.create(
|
||||
application=application,
|
||||
user=user,
|
||||
status=ApplicationRoleStatus.ACTIVE,
|
||||
cloud_id=None,
|
||||
)
|
||||
|
||||
mock = Mock()
|
||||
monkeypatch.setattr("atst.jobs.create_user", mock)
|
||||
|
||||
# When dispatch_create_user is called
|
||||
dispatch_create_user.run()
|
||||
|
||||
# It should cause the create_user task to be called once
|
||||
# with the application id
|
||||
mock.delay.assert_called_once_with(application_role_ids=[app_role.id])
|
||||
|
||||
|
||||
def test_dispatch_create_atat_admin_user(session, monkeypatch):
|
||||
portfolio = PortfolioFactory.create(
|
||||
applications=[
|
||||
@@ -240,11 +286,8 @@ def test_create_environment_no_dupes(session, celery_app, celery_worker):
|
||||
assert environment.claimed_until == None
|
||||
|
||||
|
||||
def test_claim_for_update(session):
|
||||
def test_dispatch_provision_portfolio(csp, monkeypatch):
|
||||
portfolio = PortfolioFactory.create(
|
||||
applications=[
|
||||
{"environments": [{"cloud_id": uuid4().hex, "root_user_info": {}}]}
|
||||
],
|
||||
task_orders=[
|
||||
{
|
||||
"create_clins": [
|
||||
@@ -256,115 +299,6 @@ def test_claim_for_update(session):
|
||||
}
|
||||
],
|
||||
)
|
||||
environment = portfolio.applications[0].environments[0]
|
||||
|
||||
satisfied_claims = []
|
||||
exceptions = []
|
||||
|
||||
# Two threads race to do work on environment and check out the lock
|
||||
class FirstThread(Thread):
|
||||
def run(self):
|
||||
try:
|
||||
with claim_for_update(environment):
|
||||
satisfied_claims.append("FirstThread")
|
||||
except ClaimFailedException:
|
||||
exceptions.append("FirstThread")
|
||||
|
||||
class SecondThread(Thread):
|
||||
def run(self):
|
||||
try:
|
||||
with claim_for_update(environment):
|
||||
satisfied_claims.append("SecondThread")
|
||||
except ClaimFailedException:
|
||||
exceptions.append("SecondThread")
|
||||
|
||||
t1 = FirstThread()
|
||||
t2 = SecondThread()
|
||||
t1.start()
|
||||
t2.start()
|
||||
t1.join()
|
||||
t2.join()
|
||||
|
||||
session.refresh(environment)
|
||||
|
||||
assert len(satisfied_claims) == 1
|
||||
assert len(exceptions) == 1
|
||||
|
||||
if satisfied_claims == ["FirstThread"]:
|
||||
assert exceptions == ["SecondThread"]
|
||||
else:
|
||||
assert satisfied_claims == ["SecondThread"]
|
||||
assert exceptions == ["FirstThread"]
|
||||
|
||||
# The claim is released
|
||||
assert environment.claimed_until is None
|
||||
|
||||
|
||||
def test_dispatch_provision_user(csp, session, celery_app, celery_worker, monkeypatch):
|
||||
|
||||
# Given that I have four environment roles:
|
||||
# (A) one of which has a completed status
|
||||
# (B) one of which has an environment that has not been provisioned
|
||||
# (C) one of which is pending, has a provisioned environment but an inactive application role
|
||||
# (D) one of which is pending, has a provisioned environment and has an active application role
|
||||
provisioned_environment = EnvironmentFactory.create(
|
||||
cloud_id="cloud_id", root_user_info={}
|
||||
)
|
||||
unprovisioned_environment = EnvironmentFactory.create()
|
||||
_er_a = EnvironmentRoleFactory.create(
|
||||
environment=provisioned_environment, status=EnvironmentRole.Status.COMPLETED
|
||||
)
|
||||
_er_b = EnvironmentRoleFactory.create(
|
||||
environment=unprovisioned_environment, status=EnvironmentRole.Status.PENDING
|
||||
)
|
||||
_er_c = EnvironmentRoleFactory.create(
|
||||
environment=unprovisioned_environment,
|
||||
status=EnvironmentRole.Status.PENDING,
|
||||
application_role=ApplicationRoleFactory(status=ApplicationRoleStatus.PENDING),
|
||||
)
|
||||
er_d = EnvironmentRoleFactory.create(
|
||||
environment=provisioned_environment,
|
||||
status=EnvironmentRole.Status.PENDING,
|
||||
application_role=ApplicationRoleFactory(status=ApplicationRoleStatus.ACTIVE),
|
||||
)
|
||||
|
||||
mock = Mock()
|
||||
monkeypatch.setattr("atst.jobs.provision_user", mock)
|
||||
|
||||
# When I dispatch the user provisioning task
|
||||
dispatch_provision_user.run()
|
||||
|
||||
# I expect it to dispatch only one call, to EnvironmentRole D
|
||||
mock.delay.assert_called_once_with(environment_role_id=er_d.id)
|
||||
|
||||
|
||||
def test_do_provision_user(csp, session):
|
||||
# Given that I have an EnvironmentRole with a provisioned environment
|
||||
credentials = MockCloudProvider(())._auth_credentials
|
||||
provisioned_environment = EnvironmentFactory.create(
|
||||
cloud_id="cloud_id", root_user_info={"credentials": credentials}
|
||||
)
|
||||
environment_role = EnvironmentRoleFactory.create(
|
||||
environment=provisioned_environment,
|
||||
status=EnvironmentRole.Status.PENDING,
|
||||
role="ADMIN",
|
||||
)
|
||||
|
||||
# When I call the user provisoning task
|
||||
do_provision_user(csp=csp, environment_role_id=environment_role.id)
|
||||
|
||||
session.refresh(environment_role)
|
||||
# I expect that the CSP create_or_update_user method will be called
|
||||
csp.create_or_update_user.assert_called_once_with(
|
||||
credentials, environment_role, CSPRole.ADMIN
|
||||
)
|
||||
# I expect that the EnvironmentRole now has a csp_user_id
|
||||
assert environment_role.csp_user_id
|
||||
|
||||
|
||||
def test_dispatch_provision_portfolio(
|
||||
csp, session, portfolio, celery_app, celery_worker, monkeypatch
|
||||
):
|
||||
sm = PortfolioStateMachineFactory.create(portfolio=portfolio)
|
||||
mock = Mock()
|
||||
monkeypatch.setattr("atst.jobs.provision_portfolio", mock)
|
||||
|
@@ -5,9 +5,12 @@ from unittest.mock import Mock
|
||||
from OpenSSL import crypto
|
||||
from cryptography.hazmat.backends import default_backend
|
||||
from flask import template_rendered
|
||||
import pendulum
|
||||
|
||||
from atst.utils.notification_sender import NotificationSender
|
||||
|
||||
import tests.factories as factories
|
||||
|
||||
|
||||
@contextmanager
|
||||
def captured_templates(app):
|
||||
@@ -62,3 +65,40 @@ def make_crl_list(x509_obj, x509_path):
|
||||
issuer = x509_obj.issuer.public_bytes(default_backend())
|
||||
filename = os.path.basename(x509_path)
|
||||
return [(filename, issuer.hex())]
|
||||
|
||||
|
||||
class EnvQueryTest:
|
||||
@property
|
||||
def NOW(self):
|
||||
return pendulum.now()
|
||||
|
||||
@property
|
||||
def YESTERDAY(self):
|
||||
return self.NOW.subtract(days=1)
|
||||
|
||||
@property
|
||||
def TOMORROW(self):
|
||||
return self.NOW.add(days=1)
|
||||
|
||||
def create_portfolio_with_clins(
|
||||
self, start_and_end_dates, env_data=None, state_machine_status=None
|
||||
):
|
||||
env_data = env_data or {}
|
||||
return factories.PortfolioFactory.create(
|
||||
state=state_machine_status,
|
||||
applications=[
|
||||
{
|
||||
"name": "Mos Eisley",
|
||||
"description": "Where Han shot first",
|
||||
"environments": [{"name": "thebar", **env_data}],
|
||||
}
|
||||
],
|
||||
task_orders=[
|
||||
{
|
||||
"create_clins": [
|
||||
{"start_date": start_date, "end_date": end_date}
|
||||
for (start_date, end_date) in start_and_end_dates
|
||||
]
|
||||
}
|
||||
],
|
||||
)
|
||||
|
Reference in New Issue
Block a user