atst/atst/models/environment.py
dandds 3a23c54723 Add a beat processing schedule for environment provisioning jobs.
The beat schedule is set to once per minute for each of the three
environment provisioning tasks.

Adding a beat schedule surfaced two problems that are addressed here
with the following changes:
- Commit the SQLALchemy session in order to release the environment
  lock. Otherwise the change to the `claimed_until` field is not
  persisted.
- Set `none_as_null` on the JSOB fields on the `Environment`. This
  avoids problems with querying on Postgres JSON fields that are empty.

This also adds a small change to the development command for the Celery
worker. Multiple child processes were executing the beat jobs, which
lead to exceptions for environment locks and confusing log output. This
contrains the dev command to a single Celery worker.
2019-09-18 16:34:56 -04:00

83 lines
2.3 KiB
Python

from sqlalchemy import Column, ForeignKey, String, TIMESTAMP
from sqlalchemy.orm import relationship
from sqlalchemy.dialects.postgresql import JSONB
from enum import Enum
from atst.models import Base
from atst.models.types import Id
from atst.models import mixins
class Environment(
Base, mixins.TimestampsMixin, mixins.AuditableMixin, mixins.DeletableMixin
):
__tablename__ = "environments"
id = Id()
name = Column(String, nullable=False)
application_id = Column(ForeignKey("applications.id"), nullable=False)
application = relationship("Application")
# User user.id as the foreign key here beacuse the Environment creator may
# not have an application role. We may need to revisit this if we receive any
# requirements around tracking an environment's custodian.
creator_id = Column(ForeignKey("users.id"), nullable=False)
creator = relationship("User")
cloud_id = Column(String)
root_user_info = Column(JSONB(none_as_null=True))
baseline_info = Column(JSONB(none_as_null=True))
claimed_until = Column(TIMESTAMP(timezone=True))
job_failures = relationship("EnvironmentJobFailure")
class ProvisioningStatus(Enum):
PENDING = "pending"
COMPLETED = "completed"
@property
def users(self):
return {r.application_role.user for r in self.roles}
@property
def num_users(self):
return len(self.users)
@property
def displayname(self):
return self.name
@property
def portfolio(self):
return self.application.portfolio
@property
def portfolio_id(self):
return self.application.portfolio_id
@property
def provisioning_status(self) -> ProvisioningStatus:
if (
self.cloud_id is None
or self.root_user_info is None
or self.baseline_info is None
):
return self.ProvisioningStatus.PENDING
else:
return self.ProvisioningStatus.COMPLETED
def __repr__(self):
return "<Environment(name='{}', num_users='{}', application='{}', portfolio='{}', id='{}')>".format(
self.name,
self.num_users,
self.application.name,
self.application.portfolio.name,
self.id,
)
@property
def history(self):
return self.get_changes()