From d1e146f5775e9e9d8b7ce42725d5d182da086de0 Mon Sep 17 00:00:00 2001 From: richard-dds Date: Wed, 18 Sep 2019 16:39:41 -0400 Subject: [PATCH] Add create_user task --- ...f9caba7_add_environment_role_csp_fields.py | 30 +++++++++++++++++++ atst/domain/csp/cloud.py | 2 +- atst/domain/environment_roles.py | 23 ++++++++++++++ atst/jobs.py | 30 +++++++++++++++++++ atst/models/environment_role.py | 5 +++- tests/test_jobs.py | 23 ++++++++++---- 6 files changed, 106 insertions(+), 7 deletions(-) create mode 100644 alembic/versions/e3d93f9caba7_add_environment_role_csp_fields.py diff --git a/alembic/versions/e3d93f9caba7_add_environment_role_csp_fields.py b/alembic/versions/e3d93f9caba7_add_environment_role_csp_fields.py new file mode 100644 index 00000000..481a865a --- /dev/null +++ b/alembic/versions/e3d93f9caba7_add_environment_role_csp_fields.py @@ -0,0 +1,30 @@ +"""add environment_role csp fields + +Revision ID: e3d93f9caba7 +Revises: 691b04ecd85e +Create Date: 2019-09-18 16:35:47.554060 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = 'e3d93f9caba7' # pragma: allowlist secret +down_revision = '691b04ecd85e' # 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('claimed_until', sa.TIMESTAMP(timezone=True), nullable=True)) + op.add_column('environment_roles', sa.Column('csp_user_id', sa.String(), nullable=True)) + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.drop_column('environment_roles', 'csp_user_id') + op.drop_column('environment_roles', 'claimed_until') + # ### end Alembic commands ### diff --git a/atst/domain/csp/cloud.py b/atst/domain/csp/cloud.py index 5f8689c1..4a6961e5 100644 --- a/atst/domain/csp/cloud.py +++ b/atst/domain/csp/cloud.py @@ -194,7 +194,7 @@ class MockCloudProvider(CloudProviderInterface): GeneralCSPException("Could not create user."), ) - return {"id": self._id()} + return self._id() def suspend_user(self, auth_credentials, csp_user_id): self._maybe_raise(self.NETWORK_FAILURE_PCT, self.NETWORK_EXCEPTION) diff --git a/atst/domain/environment_roles.py b/atst/domain/environment_roles.py index b28e94b2..00ac730f 100644 --- a/atst/domain/environment_roles.py +++ b/atst/domain/environment_roles.py @@ -1,5 +1,10 @@ +from sqlalchemy.orm.exc import NoResultFound + from atst.database import db from atst.models import EnvironmentRole, ApplicationRole +from atst.domain.exceptions import NotFoundError +from uuid import UUID +from typing import List class EnvironmentRoles(object): @@ -22,6 +27,15 @@ class EnvironmentRoles(object): ) return existing_env_role + @classmethod + def get_by_id(cls, id_) -> EnvironmentRole: + try: + return ( + db.session.query(EnvironmentRole).filter(EnvironmentRole.id == id_) + ).one() + except NoResultFound: + raise NotFoundError(cls.resource_name) + @classmethod def get_by_user_and_environment(cls, user_id, environment_id): existing_env_role = ( @@ -54,3 +68,12 @@ class EnvironmentRoles(object): .filter(EnvironmentRole.deleted != True) .all() ) + + @classmethod + def get_environment_roles_pending_creation(cls) -> List[UUID]: + results = ( + db.session.query(EnvironmentRole.id) + # TODO + .filter(EnvironmentRole.status == "PENDING").all() + ) + return [id_ for id_, in results] diff --git a/atst/jobs.py b/atst/jobs.py index 46717192..f702f13f 100644 --- a/atst/jobs.py +++ b/atst/jobs.py @@ -6,6 +6,7 @@ from atst.queue import celery from atst.models import EnvironmentJobFailure, EnvironmentRoleJobFailure from atst.domain.csp.cloud import CloudProviderInterface, GeneralCSPException from atst.domain.environments import Environments +from atst.domain.environment_roles import EnvironmentRoles from atst.models.utils import claim_for_update @@ -101,6 +102,20 @@ def do_create_environment_baseline(csp: CloudProviderInterface, environment_id=N db.session.commit() +def do_create_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"] + + csp_user_id = csp.create_or_update_user( + credentials, environment_role, "role_id" + ) + environment_role.csp_user_id = csp_user_id + db.session.add(environment_role) + db.session.commit() + + def do_work(fn, task, csp, **kwargs): try: fn(csp, **kwargs) @@ -130,6 +145,13 @@ def create_environment_baseline(self, environment_id=None): ) +@celery.task(bind=True) +def create_user(self, environment_role_id=None): + do_work( + do_create_user, self, app.csp.cloud, environment_role_id=environment_role_id + ) + + @celery.task(bind=True) def dispatch_create_environment(self): for environment_id in Environments.get_environments_pending_creation( @@ -152,3 +174,11 @@ def dispatch_create_environment_baseline(self): pendulum.now() ): create_environment_baseline.delay(environment_id=environment_id) + + +@celery.task(bind=True) +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) diff --git a/atst/models/environment_role.py b/atst/models/environment_role.py index 9f6754f8..9b23c300 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 +from sqlalchemy import Index, ForeignKey, Column, String, TIMESTAMP from sqlalchemy.dialects.postgresql import UUID from sqlalchemy.orm import relationship @@ -33,6 +33,9 @@ class EnvironmentRole( job_failures = relationship("EnvironmentRoleJobFailure") + csp_user_id = Column(String()) + claimed_until = Column(TIMESTAMP(timezone=True)) + 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 cef271bf..89ff9be5 100644 --- a/tests/test_jobs.py +++ b/tests/test_jobs.py @@ -16,12 +16,18 @@ from atst.jobs import ( dispatch_create_atat_admin_user, dispatch_create_environment_baseline, create_environment, + do_create_user, ) from atst.models.utils import claim_for_update from atst.domain.exceptions import ClaimFailedException from tests.factories import EnvironmentFactory, EnvironmentRoleFactory, PortfolioFactory +@pytest.fixture(autouse=True, scope="function") +def csp(): + return Mock(wraps=MockCloudProvider({}, with_delay=False, with_failure=False)) + + def test_environment_job_failure(celery_app, celery_worker): @celery_app.task(bind=True, base=RecordEnvironmentFailure) def _fail_hard(self, environment_id=None): @@ -63,11 +69,6 @@ yesterday = now.subtract(days=1) tomorrow = now.add(days=1) -@pytest.fixture(autouse=True, scope="function") -def csp(): - return Mock(wraps=MockCloudProvider({}, with_delay=False, with_failure=False)) - - def test_create_environment_job(session, csp): environment = EnvironmentFactory.create() do_create_environment(csp, environment.id) @@ -291,3 +292,15 @@ def test_claim_for_update(session): # The claim is released assert environment.claimed_until is None + + +def test_create_user(csp, session): + environment = EnvironmentFactory.create( + root_user_info={"credentials": MockCloudProvider({})._auth_credentials} + ) + environment_role = EnvironmentRoleFactory.create(environment=environment) + do_create_user(csp, environment_role_id=environment_role.id) + + session.refresh(environment_role) + + assert environment_role.csp_user_id