atst/atat/domain/environments.py
2020-03-04 11:51:15 -05:00

141 lines
4.2 KiB
Python

from sqlalchemy import func, or_, and_
from sqlalchemy.orm.exc import NoResultFound
from typing import List
from uuid import UUID
from atat.database import db
from atat.models import (
Environment,
Application,
Portfolio,
TaskOrder,
CLIN,
)
from atat.domain.environment_roles import EnvironmentRoles
from atat.utils import commit_or_raise_already_exists_error
from .exceptions import NotFoundError, DisabledError
class Environments(object):
@classmethod
def create(cls, user, application, name):
environment = Environment(application=application, name=name, creator=user)
db.session.add(environment)
commit_or_raise_already_exists_error(message="environment")
return environment
@classmethod
def create_many(cls, user, application, names):
environments = []
for name in names:
environment = Environments.create(user, application, name)
environments.append(environment)
db.session.add_all(environments)
return environments
@classmethod
def update(cls, environment, name=None):
if name is not None:
environment.name = name
db.session.add(environment)
commit_or_raise_already_exists_error(message="environment")
return environment
@classmethod
def get(cls, environment_id):
try:
env = (
db.session.query(Environment)
.filter_by(id=environment_id, deleted=False)
.one()
)
except NoResultFound:
raise NotFoundError("environment")
return env
@classmethod
def update_env_role(cls, environment, application_role, new_role):
env_role = EnvironmentRoles.get_for_update(application_role.id, environment.id)
if env_role and new_role and (env_role.disabled or env_role.deleted):
raise DisabledError("environment_role", env_role.id)
if env_role and env_role.role != new_role and not env_role.disabled:
env_role.role = new_role
db.session.add(env_role)
elif not env_role and new_role:
env_role = EnvironmentRoles.create(
application_role=application_role,
environment=environment,
role=new_role,
)
db.session.add(env_role)
if env_role and not new_role and not env_role.disabled:
EnvironmentRoles.disable(env_role.id)
db.session.commit()
@classmethod
def revoke_access(cls, environment, target_user):
EnvironmentRoles.delete(environment.id, target_user.id)
@classmethod
def delete(cls, environment, commit=False):
environment.deleted = True
db.session.add(environment)
for role in environment.roles:
role.deleted = True
db.session.add(role)
if commit:
db.session.commit()
# TODO: How do we work around environment deletion being a largely manual process in the CSPs
return environment
@classmethod
def base_provision_query(cls, now):
return (
db.session.query(Environment.id)
.join(Application)
.join(Portfolio)
.join(TaskOrder)
.join(CLIN)
.filter(CLIN.start_date <= now)
.filter(CLIN.end_date > now)
.filter(Environment.deleted == False)
.filter(
or_(
Environment.claimed_until == None,
Environment.claimed_until <= func.now(),
)
)
)
@classmethod
def get_environments_pending_creation(cls, now) -> List[UUID]:
"""
Any environment with an active CLIN that doesn't yet have a `cloud_id`.
"""
results = (
cls.base_provision_query(now)
.filter(
and_(
Application.cloud_id != None,
Environment.cloud_id.is_(None),
or_(
Environment.claimed_until.is_(None),
Environment.claimed_until <= func.now(),
),
)
)
.all()
)
return [id_ for id_, in results]