-
-
Notifications
You must be signed in to change notification settings - Fork 18
/
models.py
170 lines (135 loc) · 5.5 KB
/
models.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
"""
Copyright (C) 2017-2021 Vanessa Sochat.
This Source Code Form is subject to the terms of the
Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed
with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
"""
from sqlalchemy import (
create_engine,
Column,
DateTime,
Integer,
String,
Text,
ForeignKey,
func,
)
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.schema import UniqueConstraint
from sqlalchemy.orm import backref, relationship, scoped_session, sessionmaker
from sregistry.logger import bot
from sregistry.defaults import SREGISTRY_STORAGE
from uuid import uuid4
import os
Base = declarative_base()
class Collection(Base):
"""A participant in a local assessment. id must be unique. If a token is
revoked or finished, it will end with `_revoked` or `_finished`. A
user generated without a token will have value of None
"""
__tablename__ = "collection"
id = Column(Integer, primary_key=True)
name = Column(String(150), unique=True)
token = Column(String(50))
created_at = Column(DateTime, default=func.now())
containers = relationship(
"Container",
lazy="select",
cascade="delete,all",
backref=backref("collection", lazy="joined"),
)
def __init__(self, name=None, token=None):
self.name = name
if token is None:
token = str(uuid4())
self.token = token
def __repr__(self):
return "<Collection %r>" % (self.name)
def __str__(self):
return "<Collection %r>" % (self.name)
def url(self):
"""return the collection url"""
return "file://"
class Container(Base):
"""a container belongs to a collection
Parameters
==========
created_at: the creation date of the image / container
metrics: typically the inspection of the image. If not possible, then the
basic name (uri) derived from the user is used.
tag: the image tag
image: the path to the image on the filesystem (can be Null)
url: the url where the imate was ultimately retrieved, call be Null
client: the client backend associated with the image, the type(client)
version: a version string associated with the image
:collection_id: the id of the colletion to which the image belongs.
We index / filter containers based on the full uri, which is assembled
from the <collection>/<namespace>:<tag>@<version>, then stored
as a variable, and maintained separately for easier query.
"""
__tablename__ = "container"
id = Column(Integer, primary_key=True)
created_at = Column(DateTime, default=func.now())
metrics = Column(Text, nullable=False)
name = Column(String(250), nullable=False)
tag = Column(String(250), nullable=False)
image = Column(String(250), nullable=True)
url = Column(String(500), nullable=True)
uri = Column(String(500), nullable=True)
client = Column(String(50), nullable=False)
version = Column(String(250), nullable=True)
collection_id = Column(Integer, ForeignKey("collection.id"), nullable=False)
__table_args__ = (
UniqueConstraint(
"collection_id", "name", "tag", "client", "version", name="_container_uc"
),
)
def __repr__(self):
if self.uri is None:
return "<Container %r>" % (self.name)
return "<Container %r>" % (self.uri)
def __str__(self):
if self.uri is None:
return "<Container %r>" % (self.name)
return "<Container %r>" % (self.uri)
def get_uri(self):
"""generate a uri on the fly from database parameters if one is not
saved with the initial model (it should be, but might not be possible)
"""
uri = "%s/%s:%s" % (self.collection.name, self.name, self.tag)
if self.version not in [None, ""]:
uri = "%s@%s" % (uri, self.version)
return uri
def init_db(self, db_path):
"""initialize the database, with the default database path or custom of
the format sqlite:////home/<username>/sregistry.db
The custom path can be set with the environment var SREGISTRY_DATABASE
when a user creates the client, we must initialize this db
the database should use the .singularity cache folder to cache
layers and images, and .singularity/sregistry.db as a database
"""
# Database Setup, use default if uri not provided
self.database = "sqlite:///%s" % db_path
self.storage = SREGISTRY_STORAGE
# If the path isn't defined, cut out early
if not db_path:
return
# Ensure that the parent_folder exists)
parent_folder = os.path.dirname(db_path)
# Case 1: Does not exist
if not os.path.exists(parent_folder):
bot.exit("Database location {} does not exist.".format(parent_folder))
# Case 2: Insufficient permission for write
if not os.access(parent_folder, os.W_OK):
bot.exit("Insufficient permission to write to {}".format(parent_folder))
bot.debug("Database located at %s" % self.database)
self.engine = create_engine(self.database, convert_unicode=True)
self.session = scoped_session(
sessionmaker(autocommit=False, autoflush=False, bind=self.engine)
)
Base.query = self.session.query_property()
# import all modules here that might define models so that
# they will be registered properly on the metadata. Otherwise
# you will have to import them first before calling init_db()
Base.metadata.create_all(bind=self.engine)
self.Base = Base