Query for applications that need to be provisioned.

Adds a method to the Applications domain class that can return a list of
UUIDs for applications that are ready to be provisioned. It requires
that:

- the associated portfolio and state machine have a state of COMPLETED
- the application not have been marked deleted
- the application not have an existing cloud_id
- the application does not have an existing claim on it
This commit is contained in:
dandds 2020-01-25 14:30:17 -05:00
parent 02ec54a310
commit 02438dc39b
5 changed files with 86 additions and 2 deletions

View File

@ -0,0 +1,30 @@
"""add applications.claimed_until
Revision ID: 07e0598199f6
Revises: 26319c44a8d5
Create Date: 2020-01-25 13:33:17.711548
"""
from alembic import op
import sqlalchemy as sa
from sqlalchemy.dialects import postgresql
# revision identifiers, used by Alembic.
revision = '07e0598199f6' # pragma: allowlist secret
down_revision = '26319c44a8d5' # pragma: allowlist secret
branch_labels = None
depends_on = None
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.add_column('applications', sa.Column('claimed_until', sa.TIMESTAMP(timezone=True), nullable=True))
op.add_column('applications', sa.Column('cloud_id', sa.String(), nullable=True))
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.drop_column('applications', 'claimed_until')
op.drop_column('applications', 'cloud_id')
# ### end Alembic commands ###

View File

@ -1,5 +1,9 @@
from . import BaseDomainClass
from flask import g
from sqlalchemy import func, or_
from typing import List
from uuid import UUID
from . import BaseDomainClass
from atst.database import db
from atst.domain.application_roles import ApplicationRoles
from atst.domain.environments import Environments
@ -10,7 +14,10 @@ from atst.models import (
ApplicationRole,
ApplicationRoleStatus,
EnvironmentRole,
Portfolio,
PortfolioStateMachine,
)
from atst.models.mixins.state_machines import FSMStates
from atst.utils import first_or_none, commit_or_raise_already_exists_error
@ -118,3 +125,21 @@ class Applications(BaseDomainClass):
db.session.commit()
return invitation
@classmethod
def get_applications_pending_creation(cls) -> List[UUID]:
results = (
db.session.query(Application.id)
.join(Portfolio)
.join(PortfolioStateMachine)
.filter(PortfolioStateMachine.state == FSMStates.COMPLETED)
.filter(Application.deleted == False)
.filter(Application.cloud_id == None)
.filter(
or_(
Application.claimed_until == None,
Application.claimed_until >= func.now(),
)
)
).all()
return [id_ for id_, in results]

View File

@ -1,4 +1,4 @@
from sqlalchemy import and_, Column, ForeignKey, String, UniqueConstraint
from sqlalchemy import and_, Column, ForeignKey, String, UniqueConstraint, TIMESTAMP
from sqlalchemy.orm import relationship, synonym
from atst.models.base import Base
@ -40,6 +40,9 @@ class Application(
),
)
cloud_id = Column(String)
claimed_until = Column(TIMESTAMP(timezone=True))
@property
def users(self):
return set(role.user for role in self.members)

View File

@ -1,3 +1,4 @@
from datetime import datetime, timedelta
import pytest
from uuid import uuid4
@ -196,3 +197,20 @@ def test_update_does_not_duplicate_names_within_portfolio():
with pytest.raises(AlreadyExistsError):
Applications.update(dupe_application, {"name": name})
def test_get_applications_pending_creation():
now = datetime.now()
later = now + timedelta(minutes=30)
portfolio1 = PortfolioFactory.create(state="COMPLETED")
app_ready = ApplicationFactory.create(portfolio=portfolio1)
app_claimed = ApplicationFactory.create(portfolio=portfolio1, claimed_until=later)
portfolio2 = PortfolioFactory.create(state="UNSTARTED")
app_not_ready = ApplicationFactory.create(portfolio=portfolio2)
uuids = Applications.get_applications_pending_creation()
assert [app_ready.id] == uuids

View File

@ -7,6 +7,7 @@ import datetime
from atst.forms import data
from atst.models import *
from atst.models.mixins.state_machines import FSMStates
from atst.domain.invitations import PortfolioInvitations
from atst.domain.permission_sets import PermissionSets
@ -121,6 +122,7 @@ class PortfolioFactory(Base):
owner = kwargs.pop("owner", UserFactory.create())
members = kwargs.pop("members", [])
with_task_orders = kwargs.pop("task_orders", [])
state = kwargs.pop("state", None)
portfolio = super()._create(model_class, *args, **kwargs)
@ -161,6 +163,12 @@ class PortfolioFactory(Base):
permission_sets=perms_set,
)
if state:
state = getattr(FSMStates, state)
fsm = PortfolioStateMachineFactory.create(state=state, portfolio=portfolio)
# setting it in the factory is not working for some reason
fsm.state = state
portfolio.applications = applications
portfolio.task_orders = task_orders
return portfolio