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:
parent
02ec54a310
commit
02438dc39b
@ -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 ###
|
@ -1,5 +1,9 @@
|
|||||||
from . import BaseDomainClass
|
|
||||||
from flask import g
|
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.database import db
|
||||||
from atst.domain.application_roles import ApplicationRoles
|
from atst.domain.application_roles import ApplicationRoles
|
||||||
from atst.domain.environments import Environments
|
from atst.domain.environments import Environments
|
||||||
@ -10,7 +14,10 @@ from atst.models import (
|
|||||||
ApplicationRole,
|
ApplicationRole,
|
||||||
ApplicationRoleStatus,
|
ApplicationRoleStatus,
|
||||||
EnvironmentRole,
|
EnvironmentRole,
|
||||||
|
Portfolio,
|
||||||
|
PortfolioStateMachine,
|
||||||
)
|
)
|
||||||
|
from atst.models.mixins.state_machines import FSMStates
|
||||||
from atst.utils import first_or_none, commit_or_raise_already_exists_error
|
from atst.utils import first_or_none, commit_or_raise_already_exists_error
|
||||||
|
|
||||||
|
|
||||||
@ -118,3 +125,21 @@ class Applications(BaseDomainClass):
|
|||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
|
||||||
return invitation
|
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]
|
||||||
|
@ -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 sqlalchemy.orm import relationship, synonym
|
||||||
|
|
||||||
from atst.models.base import Base
|
from atst.models.base import Base
|
||||||
@ -40,6 +40,9 @@ class Application(
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
cloud_id = Column(String)
|
||||||
|
claimed_until = Column(TIMESTAMP(timezone=True))
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def users(self):
|
def users(self):
|
||||||
return set(role.user for role in self.members)
|
return set(role.user for role in self.members)
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
from datetime import datetime, timedelta
|
||||||
import pytest
|
import pytest
|
||||||
from uuid import uuid4
|
from uuid import uuid4
|
||||||
|
|
||||||
@ -196,3 +197,20 @@ def test_update_does_not_duplicate_names_within_portfolio():
|
|||||||
|
|
||||||
with pytest.raises(AlreadyExistsError):
|
with pytest.raises(AlreadyExistsError):
|
||||||
Applications.update(dupe_application, {"name": name})
|
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
|
||||||
|
@ -7,6 +7,7 @@ import datetime
|
|||||||
|
|
||||||
from atst.forms import data
|
from atst.forms import data
|
||||||
from atst.models import *
|
from atst.models import *
|
||||||
|
from atst.models.mixins.state_machines import FSMStates
|
||||||
|
|
||||||
from atst.domain.invitations import PortfolioInvitations
|
from atst.domain.invitations import PortfolioInvitations
|
||||||
from atst.domain.permission_sets import PermissionSets
|
from atst.domain.permission_sets import PermissionSets
|
||||||
@ -121,6 +122,7 @@ class PortfolioFactory(Base):
|
|||||||
owner = kwargs.pop("owner", UserFactory.create())
|
owner = kwargs.pop("owner", UserFactory.create())
|
||||||
members = kwargs.pop("members", [])
|
members = kwargs.pop("members", [])
|
||||||
with_task_orders = kwargs.pop("task_orders", [])
|
with_task_orders = kwargs.pop("task_orders", [])
|
||||||
|
state = kwargs.pop("state", None)
|
||||||
|
|
||||||
portfolio = super()._create(model_class, *args, **kwargs)
|
portfolio = super()._create(model_class, *args, **kwargs)
|
||||||
|
|
||||||
@ -161,6 +163,12 @@ class PortfolioFactory(Base):
|
|||||||
permission_sets=perms_set,
|
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.applications = applications
|
||||||
portfolio.task_orders = task_orders
|
portfolio.task_orders = task_orders
|
||||||
return portfolio
|
return portfolio
|
||||||
|
Loading…
x
Reference in New Issue
Block a user