Source code for renga_deployer.models
# -*- coding: utf-8 -*-
#
# Copyright 2017 - Swiss Data Science Center (SDSC)
# A partnership between École Polytechnique Fédérale de Lausanne (EPFL) and
# Eidgenössische Technische Hochschule Zürich (ETHZ).
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Models sub-module."""
import uuid
from collections import namedtuple
from enum import Enum
from flask import g, has_request_context
from flask_sqlalchemy import SQLAlchemy
from sqlalchemy.types import String
from sqlalchemy_utils.models import Timestamp
from sqlalchemy_utils.types import JSONType, UUIDType
db = SQLAlchemy()
"""Core database object."""
[docs]def load_jwt():
"""Load JWT from a context."""
if has_request_context():
return g.jwt
[docs]class ExecutionStates(Enum):
"""Valid execution states."""
RUNNING = 'running'
EXITED = 'exited'
UNAVAILABLE = 'unavailable'
[docs]class Context(db.Model, Timestamp):
"""Execution context.
Additionally it contains two columns ``created`` and ``updated``
with automatically managed timestamps.
"""
__tablename__ = 'contexts'
id = db.Column(UUIDType, primary_key=True, default=uuid.uuid4)
"""Context identifier."""
spec = db.Column(
db.JSON(none_as_null=True).with_variant(JSONType, 'sqlite'))
"""Context specification."""
jwt = db.Column(
db.JSON(none_as_null=True).with_variant(JSONType, 'sqlite'),
default=load_jwt)
"""JWT with which the context has been created."""
creator = db.Column(String)
"""Creator of the context."""
[docs] @classmethod
def create(cls, spec=None):
"""Create a new context."""
if 'ports' in spec:
spec['ports'] = list(filter(None, spec['ports']))
context = cls(spec=spec, id=uuid.uuid4())
return context
[docs]class Execution(db.Model, Timestamp):
"""Represent an execution of a context.
Additionally it contains two columns ``created`` and ``updated``
with automatically managed timestamps.
"""
__tablename__ = 'executions'
id = db.Column(UUIDType, primary_key=True, default=uuid.uuid4)
"""Execution identifier."""
engine = db.Column(db.String, index=True)
"""Engine name."""
engine_id = db.Column(db.String, index=True)
"""Internal identifier returned by an engine."""
namespace = db.Column(db.String)
"""Namespace name."""
environment = db.Column(
db.JSON(none_as_null=True).with_variant(JSONType, 'sqlite'),
default=dict)
context_id = db.Column(UUIDType, db.ForeignKey(Context.id))
"""Context identifier from which the execution started."""
context = db.relationship(
Context,
backref=db.backref(
'executions', lazy='dynamic', cascade='all, delete-orphan'),
lazy='joined')
jwt = db.Column(
db.JSON(none_as_null=True).with_variant(JSONType, 'sqlite'),
default=load_jwt)
"""JWT with which the execution has been created."""
[docs] @classmethod
def from_context(cls, context, **kwargs):
"""Create a new execution for a given context."""
kwargs.setdefault('environment', {})
kwargs.setdefault('id', uuid.uuid4())
kwargs['environment']['RENGA_CONTEXT_ID'] = str(context.id)
execution = cls(context=context, **kwargs)
return execution
[docs] def check_state(self, states, engine):
"""Check whether the execution is in one of the specified states."""
if isinstance(states, ExecutionStates):
states = {states}
if not isinstance(states, set):
states = set(states)
return engine.get_state(self) in states