diff --git a/alembic/versions/4477b211a07c_add_environment_role_status.py b/alembic/versions/4477b211a07c_add_environment_role_status.py new file mode 100644 index 00000000..e659eb2b --- /dev/null +++ b/alembic/versions/4477b211a07c_add_environment_role_status.py @@ -0,0 +1,28 @@ +"""add environment_role status + +Revision ID: 4477b211a07c +Revises: e3d93f9caba7 +Create Date: 2019-09-20 11:04:10.961311 + +""" +from alembic import op +import sqlalchemy as sa +from sqlalchemy.dialects import postgresql + +# revision identifiers, used by Alembic. +revision = '4477b211a07c' # pragma: allowlist secret +down_revision = 'e3d93f9caba7' # pragma: allowlist secret +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.add_column('environment_roles', sa.Column('status', sa.Enum('PENDING', 'COMPLETED', 'PENDING_DELETE', name='status', native_enum=False), nullable=True)) + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.drop_column('environment_roles', 'status') + # ### end Alembic commands ### diff --git a/atst/domain/environment_roles.py b/atst/domain/environment_roles.py index 00ac730f..d8475f0e 100644 --- a/atst/domain/environment_roles.py +++ b/atst/domain/environment_roles.py @@ -1,7 +1,7 @@ from sqlalchemy.orm.exc import NoResultFound from atst.database import db -from atst.models import EnvironmentRole, ApplicationRole +from atst.models import EnvironmentRole, ApplicationRole, Environment from atst.domain.exceptions import NotFoundError from uuid import UUID from typing import List @@ -73,7 +73,10 @@ class EnvironmentRoles(object): def get_environment_roles_pending_creation(cls) -> List[UUID]: results = ( db.session.query(EnvironmentRole.id) - # TODO - .filter(EnvironmentRole.status == "PENDING").all() + .join(Environment) + .filter(Environment.deleted == False) + .filter(Environment.baseline_info != None) + .filter(EnvironmentRole.status == EnvironmentRole.Status.PENDING) + .all() ) return [id_ for id_, in results] diff --git a/atst/jobs.py b/atst/jobs.py index f702f13f..e861d8b5 100644 --- a/atst/jobs.py +++ b/atst/jobs.py @@ -102,14 +102,14 @@ def do_create_environment_baseline(csp: CloudProviderInterface, environment_id=N db.session.commit() -def do_create_user(csp: CloudProviderInterface, environment_role_id=None): +def do_provision_user(csp: CloudProviderInterface, environment_role_id=None): environment_role = EnvironmentRoles.get_by_id(environment_role_id) with claim_for_update(environment_role) as environment_role: - credentials = environment_role.environment.root_user_info["credentials"] + credentials = environment_role.environment.csp_credentials csp_user_id = csp.create_or_update_user( - credentials, environment_role, "role_id" + credentials, environment_role, environment_role.role ) environment_role.csp_user_id = csp_user_id db.session.add(environment_role) @@ -146,9 +146,9 @@ def create_environment_baseline(self, environment_id=None): @celery.task(bind=True) -def create_user(self, environment_role_id=None): +def provision_user(self, environment_role_id=None): do_work( - do_create_user, self, app.csp.cloud, environment_role_id=environment_role_id + do_provision_user, self, app.csp.cloud, environment_role_id=environment_role_id ) @@ -181,4 +181,4 @@ def dispatch_provision_user(self): for ( environment_role_id ) in EnvironmentRoles.get_environment_roles_pending_creation(): - create_user.delay(environment_role_id=environment_role_id) + provision_user.delay(environment_role_id=environment_role_id) diff --git a/atst/models/environment.py b/atst/models/environment.py index f3d85502..b8129fe5 100644 --- a/atst/models/environment.py +++ b/atst/models/environment.py @@ -84,3 +84,11 @@ class Environment( @property def history(self): return self.get_changes() + + @property + def csp_credentials(self): + return ( + self.root_user_info.get("credentials") + if self.root_user_info is not None + else None + ) diff --git a/atst/models/environment_role.py b/atst/models/environment_role.py index 9b23c300..988d19ab 100644 --- a/atst/models/environment_role.py +++ b/atst/models/environment_role.py @@ -1,5 +1,5 @@ from enum import Enum -from sqlalchemy import Index, ForeignKey, Column, String, TIMESTAMP +from sqlalchemy import Index, ForeignKey, Column, String, TIMESTAMP, Enum as SQLAEnum from sqlalchemy.dialects.postgresql import UUID from sqlalchemy.orm import relationship @@ -36,6 +36,13 @@ class EnvironmentRole( csp_user_id = Column(String()) claimed_until = Column(TIMESTAMP(timezone=True)) + class Status(Enum): + PENDING = "pending" + COMPLETED = "completed" + PENDING_DELETE = "pending_delete" + + status = Column(SQLAEnum(Status, native_enum=False), default=Status.PENDING) + def __repr__(self): return "".format( self.role, self.application_role.user_name, self.environment.name, self.id diff --git a/tests/test_jobs.py b/tests/test_jobs.py index 89ff9be5..d8ffd319 100644 --- a/tests/test_jobs.py +++ b/tests/test_jobs.py @@ -16,11 +16,12 @@ from atst.jobs import ( dispatch_create_atat_admin_user, dispatch_create_environment_baseline, create_environment, - do_create_user, + dispatch_provision_user, ) from atst.models.utils import claim_for_update from atst.domain.exceptions import ClaimFailedException from tests.factories import EnvironmentFactory, EnvironmentRoleFactory, PortfolioFactory +from atst.models import EnvironmentRole @pytest.fixture(autouse=True, scope="function") @@ -294,13 +295,30 @@ def test_claim_for_update(session): assert environment.claimed_until is None -def test_create_user(csp, session): - environment = EnvironmentFactory.create( - root_user_info={"credentials": MockCloudProvider({})._auth_credentials} +def test_dispatch_provision_user(csp, session, celery_app, celery_worker, monkeypatch): + # Given that I have three 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 and has a provisioned environment + provisioned_environment = EnvironmentFactory.create( + cloud_id="cloud_id", root_user_info={}, baseline_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=provisioned_environment, status=EnvironmentRole.Status.PENDING ) - environment_role = EnvironmentRoleFactory.create(environment=environment) - do_create_user(csp, environment_role_id=environment_role.id) - session.refresh(environment_role) + mock = Mock() + monkeypatch.setattr("atst.jobs.provision_user", mock) - assert environment_role.csp_user_id + # When I dispatch the user provisioning task + dispatch_provision_user.run() + + # I expect it to dispatch only one call, to EnvironmentRole C + mock.delay.assert_called_once_with(environment_role_id=er_c.id)