1 import copy
2 import datetime
3 import os
4 import json
5 import base64
6 import uuid
7 from fnmatch import fnmatch
8
9 from sqlalchemy import outerjoin
10 from sqlalchemy.ext.associationproxy import association_proxy
11 from sqlalchemy.orm import column_property, validates
12 from six.moves.urllib.parse import urljoin
13 from libravatar import libravatar_url
14 import zlib
15
16 from copr_common.enums import ActionTypeEnum, BackendResultEnum, FailTypeEnum, ModuleStatusEnum, StatusEnum
17 from coprs import constants
18 from coprs import db
19 from coprs import helpers
20 from coprs import app
21
22 import itertools
23 import operator
24 from coprs.helpers import JSONEncodedDict
25
26 import gi
27 gi.require_version('Modulemd', '1.0')
28 from gi.repository import Modulemd
34
59
62 """
63 Records all the private information for a user.
64 """
65
66 user_id = db.Column(db.Integer, db.ForeignKey("user.id"), primary_key=True,
67 nullable=False)
68
69
70 mail = db.Column(db.String(150), nullable=False)
71
72
73 timezone = db.Column(db.String(50), nullable=True)
74
75
76 api_login = db.Column(db.String(40), nullable=False, default="abc")
77 api_token = db.Column(db.String(40), nullable=False, default="abc")
78 api_token_expiration = db.Column(
79 db.Date, nullable=False, default=datetime.date(2000, 1, 1))
80
81
82 -class User(db.Model, helpers.Serializer):
83 __table__ = outerjoin(_UserPublic.__table__, _UserPrivate.__table__)
84 id = column_property(_UserPublic.__table__.c.id, _UserPrivate.__table__.c.user_id)
85
86 @property
88 """
89 Return the short username of the user, e.g. bkabrda
90 """
91
92 return self.username
93
95 """
96 Get permissions of this user for the given copr.
97 Caches the permission during one request,
98 so use this if you access them multiple times
99 """
100
101 if not hasattr(self, "_permissions_for_copr"):
102 self._permissions_for_copr = {}
103 if copr.name not in self._permissions_for_copr:
104 self._permissions_for_copr[copr.name] = (
105 CoprPermission.query
106 .filter_by(user=self)
107 .filter_by(copr=copr)
108 .first()
109 )
110 return self._permissions_for_copr[copr.name]
111
131
132 @property
138
139 @property
142
144 """
145 :type group: Group
146 """
147 if group.fas_name in self.user_teams:
148 return True
149 else:
150 return False
151
170
171 @property
173
174 return ["id", "name"]
175
176 @property
178 """
179 Get number of coprs for this user.
180 """
181
182 return (Copr.query.filter_by(user=self).
183 filter_by(deleted=False).
184 filter_by(group_id=None).
185 count())
186
187 @property
189 """
190 Return url to libravatar image.
191 """
192
193 try:
194 return libravatar_url(email=self.mail, https=True)
195 except IOError:
196 return ""
197
198
199 -class _CoprPublic(db.Model, helpers.Serializer, CoprSearchRelatedData):
200 """
201 Represents public part of a single copr (personal repo with builds, mock
202 chroots, etc.).
203 """
204
205 __tablename__ = "copr"
206 __table_args__ = (
207 db.Index('copr_name_group_id_idx', 'name', 'group_id'),
208 )
209
210 id = db.Column(db.Integer, primary_key=True)
211
212 name = db.Column(db.String(100), nullable=False)
213 homepage = db.Column(db.Text)
214 contact = db.Column(db.Text)
215
216
217 repos = db.Column(db.Text)
218
219 created_on = db.Column(db.Integer)
220
221 description = db.Column(db.Text)
222 instructions = db.Column(db.Text)
223 deleted = db.Column(db.Boolean, default=False)
224 playground = db.Column(db.Boolean, default=False)
225
226
227 auto_createrepo = db.Column(db.Boolean, default=True)
228
229
230 user_id = db.Column(db.Integer, db.ForeignKey("user.id"), index=True)
231 group_id = db.Column(db.Integer, db.ForeignKey("group.id"))
232 forked_from_id = db.Column(db.Integer, db.ForeignKey("copr.id"))
233
234
235 build_enable_net = db.Column(db.Boolean, default=True,
236 server_default="1", nullable=False)
237
238 unlisted_on_hp = db.Column(db.Boolean, default=False, nullable=False)
239
240
241 latest_indexed_data_update = db.Column(db.Integer)
242
243
244 persistent = db.Column(db.Boolean, default=False, nullable=False, server_default="0")
245
246
247 auto_prune = db.Column(db.Boolean, default=True, nullable=False, server_default="1")
248
249
250 use_bootstrap_container = db.Column(db.Boolean, default=False, nullable=False, server_default="0")
251
252
253 follow_fedora_branching = db.Column(db.Boolean, default=True, nullable=False, server_default="1")
254
255
256 scm_repo_url = db.Column(db.Text)
257 scm_api_type = db.Column(db.Text)
258
259
260 delete_after = db.Column(db.DateTime, index=True, nullable=True)
261
262 __mapper_args__ = {
263 "order_by": created_on.desc()
264 }
265
268 """
269 Represents private part of a single copr (personal repo with builds, mock
270 chroots, etc.).
271 """
272
273 __table_args__ = (
274 db.Index('copr_private_webhook_secret', 'webhook_secret'),
275 )
276
277
278 copr_id = db.Column(db.Integer, db.ForeignKey("copr.id"), index=True,
279 nullable=False, primary_key=True)
280
281
282 webhook_secret = db.Column(db.String(100))
283
284
285 scm_api_auth_json = db.Column(db.Text)
286
287
288 -class Copr(db.Model, helpers.Serializer):
289 """
290 Represents private a single copr (personal repo with builds, mock chroots,
291 etc.).
292 """
293
294
295
296 __table__ = outerjoin(_CoprPublic.__table__, _CoprPrivate.__table__)
297 id = column_property(
298 _CoprPublic.__table__.c.id,
299 _CoprPrivate.__table__.c.copr_id
300 )
301
302
303 user = db.relationship("User", backref=db.backref("coprs"))
304 group = db.relationship("Group", backref=db.backref("groups"))
305 mock_chroots = association_proxy("copr_chroots", "mock_chroot")
306 forked_from = db.relationship("Copr",
307 remote_side=_CoprPublic.id,
308 foreign_keys=[_CoprPublic.forked_from_id],
309 backref=db.backref("forks"))
310
311 @property
312 - def main_dir(self):
313 """
314 Return main copr dir for a Copr
315 """
316 return CoprDir.query.filter(CoprDir.copr_id==self.id).filter(CoprDir.main==True).one()
317
318 @property
323
324 @property
326 """
327 Return True if copr belongs to a group
328 """
329 return self.group is not None
330
331 @property
337
338 @property
344
345 @property
347 """
348 Return repos of this copr as a list of strings
349 """
350 return self.repos.split()
351
352 @property
358
359 @property
361 """
362 :rtype: list of CoprChroot
363 """
364 return [c for c in self.copr_chroots if c.is_active]
365
366 @property
368 """
369 Return list of active mock_chroots of this copr
370 """
371 return sorted(self.active_chroots, key=lambda ch: ch.name)
372
373 @property
377
378 @property
380 """
381 Return list of active mock_chroots of this copr
382 """
383 chroots = [("{} {}".format(c.os_release, c.os_version), c.arch) for c in self.active_chroots_sorted]
384 output = []
385 for os, chs in itertools.groupby(chroots, operator.itemgetter(0)):
386 output.append((os, [ch[1] for ch in chs]))
387
388 return output
389
390 @property
392 """
393 Return number of builds in this copr
394 """
395 return len(self.builds)
396
397 @property
400
401 @disable_createrepo.setter
404
405 @property
408
409 @property
421
427
428 @property
431
432 @property
435
436 @property
441
442 @property
444 return "-".join([self.owner_name.replace("@", "group_"), self.name])
445
446 @property
448 return "/".join([self.repo_url, "modules"])
449
450 - def to_dict(self, private=False, show_builds=True, show_chroots=True):
451 result = {}
452 for key in ["id", "name", "description", "instructions"]:
453 result[key] = str(copy.copy(getattr(self, key)))
454 result["owner"] = self.owner_name
455 return result
456
457 @property
462
465
466 @property
469
470 @enable_net.setter
473
476
477 @property
479 if self.delete_after is None:
480 return None
481
482 delta = self.delete_after - datetime.datetime.now()
483 return delta.days if delta.days > 0 else 0
484
485 @delete_after_days.setter
494
495 @property
500
501 @property
508
510 """
511 Association class for Copr<->Permission relation
512 """
513
514
515
516 copr_builder = db.Column(db.SmallInteger, default=0)
517
518 copr_admin = db.Column(db.SmallInteger, default=0)
519
520
521 user_id = db.Column(db.Integer, db.ForeignKey("user.id"), primary_key=True)
522 user = db.relationship("User", backref=db.backref("copr_permissions"))
523 copr_id = db.Column(db.Integer, db.ForeignKey("copr.id"), primary_key=True)
524 copr = db.relationship("Copr", backref=db.backref("copr_permissions"))
525
527 if name == 'admin':
528 self.copr_admin = value
529 elif name == 'builder':
530 self.copr_builder = value
531 else:
532 raise KeyError("{0} is not a valid copr permission".format(name))
533
540
543 """
544 Represents one of data directories for a copr.
545 """
546 id = db.Column(db.Integer, primary_key=True)
547
548 name = db.Column(db.Text, index=True)
549 main = db.Column(db.Boolean, default=False, server_default="0", nullable=False)
550
551 ownername = db.Column(db.Text, index=True, nullable=False)
552
553 copr_id = db.Column(db.Integer, db.ForeignKey("copr.id"), index=True, nullable=False)
554 copr = db.relationship("Copr", backref=db.backref("dirs"))
555
556 __table_args__ = (
557 db.Index('only_one_main_copr_dir', copr_id, main,
558 unique=True, postgresql_where=(main==True)),
559
560 db.UniqueConstraint('ownername', 'name',
561 name='ownername_copr_dir_uniq'),
562 )
563
568
569 @property
572
573 @property
576
577 @property
581
582 @property
588
589
590 -class Package(db.Model, helpers.Serializer, CoprSearchRelatedData):
591 """
592 Represents a single package in a project_dir.
593 """
594
595 __table_args__ = (
596 db.UniqueConstraint('copr_dir_id', 'name', name='packages_copr_dir_pkgname'),
597 db.Index('package_webhook_sourcetype', 'webhook_rebuild', 'source_type'),
598 )
599
604
605 id = db.Column(db.Integer, primary_key=True)
606 name = db.Column(db.String(100), nullable=False)
607
608 source_type = db.Column(db.Integer, default=helpers.BuildSourceEnum("unset"))
609
610 source_json = db.Column(db.Text)
611
612 webhook_rebuild = db.Column(db.Boolean, default=False)
613
614 enable_net = db.Column(db.Boolean, default=False, server_default="0", nullable=False)
615
616
617 max_builds = db.Column(db.Integer, index=True)
618
619 @validates('max_builds')
621 return None if value == 0 else value
622
623 builds = db.relationship("Build", order_by="Build.id")
624
625
626 copr_id = db.Column(db.Integer, db.ForeignKey("copr.id"), index=True)
627 copr = db.relationship("Copr", backref=db.backref("packages"))
628
629 copr_dir_id = db.Column(db.Integer, db.ForeignKey("copr_dir.id"), index=True)
630 copr_dir = db.relationship("CoprDir", backref=db.backref("packages"))
631
632
633
634 chroot_blacklist_raw = db.Column(db.Text)
635
636 @property
639
640 @property
645
646 @property
649
650 @property
652 """
653 Package's source type (and source_json) is being derived from its first build, which works except
654 for "link" and "upload" cases. Consider these being equivalent to source_type being unset.
655 """
656 return self.source_type and self.source_type_text != "link" and self.source_type_text != "upload"
657
658 @property
663
664 @property
670
676
677 - def to_dict(self, with_latest_build=False, with_latest_succeeded_build=False, with_all_builds=False):
678 package_dict = super(Package, self).to_dict()
679 package_dict['source_type'] = helpers.BuildSourceEnum(package_dict['source_type'])
680
681 if with_latest_build:
682 build = self.last_build(successful=False)
683 package_dict['latest_build'] = build.to_dict(with_chroot_states=True) if build else None
684 if with_latest_succeeded_build:
685 build = self.last_build(successful=True)
686 package_dict['latest_succeeded_build'] = build.to_dict(with_chroot_states=True) if build else None
687 if with_all_builds:
688 package_dict['builds'] = [build.to_dict(with_chroot_states=True) for build in reversed(self.builds)]
689
690 return package_dict
691
694
695
696 @property
698 if not self.chroot_blacklist_raw:
699 return []
700
701 blacklisted = []
702 for pattern in self.chroot_blacklist_raw.split(','):
703 pattern = pattern.strip()
704 if not pattern:
705 continue
706 blacklisted.append(pattern)
707
708 return blacklisted
709
710
711 @staticmethod
713 for pattern in patterns:
714 if fnmatch(chroot.name, pattern):
715 return True
716 return False
717
718
719 @property
720 - def main_pkg(self):
721 if self.copr_dir.main:
722 return self
723
724 main_pkg = Package.query.filter_by(
725 name=self.name,
726 copr_dir_id=self.copr.main_dir.id
727 ).first()
728 return main_pkg
729
730
731 @property
743
744
745 -class Build(db.Model, helpers.Serializer):
746 """
747 Representation of one build in one copr
748 """
749
750 SCM_COMMIT = 'commit'
751 SCM_PULL_REQUEST = 'pull-request'
752
753 __table_args__ = (db.Index('build_canceled', "canceled"),
754 db.Index('build_order', "is_background", "id"),
755 db.Index('build_filter', "source_type", "canceled"),
756 db.Index('build_canceled_is_background_source_status_id_idx', 'canceled', "is_background", "source_status", "id"),
757 )
758
772
773 id = db.Column(db.Integer, primary_key=True)
774
775 pkgs = db.Column(db.Text)
776
777 built_packages = db.Column(db.Text)
778
779 pkg_version = db.Column(db.Text)
780
781 canceled = db.Column(db.Boolean, default=False)
782
783 repos = db.Column(db.Text)
784
785
786 submitted_on = db.Column(db.Integer, nullable=False)
787
788 result_dir = db.Column(db.Text, default='', server_default='', nullable=False)
789
790 memory_reqs = db.Column(db.Integer, default=constants.DEFAULT_BUILD_MEMORY)
791
792 timeout = db.Column(db.Integer, default=constants.DEFAULT_BUILD_TIMEOUT)
793
794 enable_net = db.Column(db.Boolean, default=False,
795 server_default="0", nullable=False)
796
797 source_type = db.Column(db.Integer, default=helpers.BuildSourceEnum("unset"))
798
799 source_json = db.Column(db.Text)
800
801 fail_type = db.Column(db.Integer, default=FailTypeEnum("unset"))
802
803 is_background = db.Column(db.Boolean, default=False, server_default="0", nullable=False)
804
805 source_status = db.Column(db.Integer, default=StatusEnum("waiting"))
806 srpm_url = db.Column(db.Text)
807
808
809 user_id = db.Column(db.Integer, db.ForeignKey("user.id"), index=True)
810 user = db.relationship("User", backref=db.backref("builds"))
811 copr_id = db.Column(db.Integer, db.ForeignKey("copr.id"), index=True)
812 copr = db.relationship("Copr", backref=db.backref("builds"))
813 package_id = db.Column(db.Integer, db.ForeignKey("package.id"), index=True)
814 package = db.relationship("Package")
815
816 chroots = association_proxy("build_chroots", "mock_chroot")
817
818 batch_id = db.Column(db.Integer, db.ForeignKey("batch.id"))
819 batch = db.relationship("Batch", backref=db.backref("builds"))
820
821 module_id = db.Column(db.Integer, db.ForeignKey("module.id"), index=True)
822 module = db.relationship("Module", backref=db.backref("builds"))
823
824 copr_dir_id = db.Column(db.Integer, db.ForeignKey("copr_dir.id"), index=True)
825 copr_dir = db.relationship("CoprDir", backref=db.backref("builds"))
826
827
828 scm_object_id = db.Column(db.Text)
829 scm_object_type = db.Column(db.Text)
830 scm_object_url = db.Column(db.Text)
831
832
833 update_callback = db.Column(db.Text)
834
835 @property
838
839 @property
842
843 @property
846
847 @property
850
851 @property
854
855 @property
856 - def fail_type_text(self):
857 return FailTypeEnum(self.fail_type)
858
859 @property
861 if self.repos is None:
862 return list()
863 else:
864 return self.repos.split()
865
866 @property
869
870 @property
872 return "{:08d}".format(self.id)
873
879
880 @property
882 if app.config["COPR_DIST_GIT_LOGS_URL"]:
883 return "{}/{}.log".format(app.config["COPR_DIST_GIT_LOGS_URL"],
884 self.task_id.replace('/', '_'))
885 return None
886
887 @property
893
894 @property
899
900 @property
903
904 @property
912
913 @property
916
917 @property
924
925 @property
928
929 @property
932
933 @property
936
937 @property
946
947 @property
950
952 """
953 Get build chroots with states which present in `states` list
954 If states == None, function returns build_chroots
955 """
956 chroot_states_map = dict(zip(self.build_chroots, self.chroot_states))
957 if statuses is not None:
958 statuses = set(statuses)
959 else:
960 return self.build_chroots
961
962 return [
963 chroot for chroot, status in chroot_states_map.items()
964 if status in statuses
965 ]
966
967 @property
969 return {b.name: b for b in self.build_chroots}
970
971 @property
973 """
974 Return build status.
975 """
976 if self.canceled:
977 return StatusEnum("canceled")
978
979 use_src_statuses = ["starting", "pending", "running", "failed"]
980 if self.source_status in [StatusEnum(s) for s in use_src_statuses]:
981 return self.source_status
982
983 for state in ["running", "starting", "pending", "failed", "succeeded", "skipped", "forked", "waiting"]:
984 if StatusEnum(state) in self.chroot_states:
985 if state == "waiting":
986 return self.source_status
987 else:
988 return StatusEnum(state)
989
990 return None
991
992 @property
994 """
995 Return text representation of status of this build.
996 """
997 if self.status != None:
998 return StatusEnum(self.status)
999 return "unknown"
1000
1001 @property
1003 """
1004 Find out if this build is cancelable.
1005 """
1006 return not self.finished and self.status != StatusEnum("starting")
1007
1008 @property
1010 """
1011 Find out if this build is repeatable.
1012
1013 Build is repeatable only if sources has been imported.
1014 """
1015 return self.source_status == StatusEnum("succeeded")
1016
1017 @property
1019 """
1020 Find out if this build is in finished state.
1021
1022 Build is finished only if all its build_chroots are in finished state or
1023 the build was canceled.
1024 """
1025 if self.canceled:
1026 return True
1027 if not self.build_chroots:
1028 return StatusEnum(self.source_status) in helpers.FINISHED_STATUSES
1029 return all([chroot.finished for chroot in self.build_chroots])
1030
1031 @property
1034
1035 @property
1037 """
1038 Find out if this build is persistent.
1039
1040 This property is inherited from the project.
1041 """
1042 return self.copr.persistent
1043
1044 @property
1046 try:
1047 return self.package.name
1048 except:
1049 return None
1050
1051 - def to_dict(self, options=None, with_chroot_states=False):
1064
1067 """
1068 1:N mapping: branch -> chroots
1069 """
1070
1071
1072 name = db.Column(db.String(50), primary_key=True)
1073
1074
1075 -class MockChroot(db.Model, helpers.Serializer):
1076 """
1077 Representation of mock chroot
1078 """
1079
1080 __table_args__ = (
1081 db.UniqueConstraint('os_release', 'os_version', 'arch', name='mock_chroot_uniq'),
1082 )
1083
1084 id = db.Column(db.Integer, primary_key=True)
1085
1086 os_release = db.Column(db.String(50), nullable=False)
1087
1088 os_version = db.Column(db.String(50), nullable=False)
1089
1090 arch = db.Column(db.String(50), nullable=False)
1091 is_active = db.Column(db.Boolean, default=True)
1092
1093
1094 distgit_branch_name = db.Column(db.String(50),
1095 db.ForeignKey("dist_git_branch.name"),
1096 nullable=False)
1097
1098 distgit_branch = db.relationship("DistGitBranch",
1099 backref=db.backref("chroots"))
1100
1101
1102
1103 final_prunerepo_done = db.Column(db.Boolean, default=False, server_default="0", nullable=False)
1104
1105 @classmethod
1114
1115 @property
1117 """
1118 Textual representation of name of this chroot
1119 """
1120 return "{}-{}-{}".format(self.os_release, self.os_version, self.arch)
1121
1122 @property
1124 """
1125 Textual representation of name of this or release
1126 """
1127 return "{}-{}".format(self.os_release, self.os_version)
1128
1129 @property
1131 """
1132 Textual representation of the operating system name
1133 """
1134 return "{0} {1}".format(self.os_release, self.os_version)
1135
1136 @property
1141
1142
1143 -class CoprChroot(db.Model, helpers.Serializer):
1144 """
1145 Representation of Copr<->MockChroot relation
1146 """
1147
1148 buildroot_pkgs = db.Column(db.Text)
1149 repos = db.Column(db.Text, default="", server_default="", nullable=False)
1150 mock_chroot_id = db.Column(
1151 db.Integer, db.ForeignKey("mock_chroot.id"), primary_key=True)
1152 mock_chroot = db.relationship(
1153 "MockChroot", backref=db.backref("copr_chroots"))
1154 copr_id = db.Column(db.Integer, db.ForeignKey("copr.id"), primary_key=True)
1155 copr = db.relationship("Copr",
1156 backref=db.backref(
1157 "copr_chroots",
1158 single_parent=True,
1159 cascade="all,delete,delete-orphan"))
1160
1161 comps_zlib = db.Column(db.LargeBinary(), nullable=True)
1162 comps_name = db.Column(db.String(127), nullable=True)
1163
1164 with_opts = db.Column(db.Text, default="", server_default="", nullable=False)
1165 without_opts = db.Column(db.Text, default="", server_default="", nullable=False)
1166
1167
1168
1169 delete_after = db.Column(db.DateTime, index=True)
1170 delete_notify = db.Column(db.DateTime, index=True)
1171
1173 if isinstance(comps_xml, str):
1174 data = comps_xml.encode("utf-8")
1175 else:
1176 data = comps_xml
1177 self.comps_zlib = zlib.compress(data)
1178
1179 @property
1182
1183 @property
1185 return (self.repos or "").split()
1186
1187 @property
1191
1192 @property
1198
1199 @property
1202
1203 @property
1206
1207 @property
1209 if not self.delete_after:
1210 return None
1211 now = datetime.datetime.now()
1212 days = (self.delete_after - now).days
1213 return days if days > 0 else 0
1214
1216 options = {"__columns_only__": [
1217 "buildroot_pkgs", "repos", "comps_name", "copr_id", "with_opts", "without_opts"
1218 ]}
1219 d = super(CoprChroot, self).to_dict(options=options)
1220 d["mock_chroot"] = self.mock_chroot.name
1221 return d
1222
1225 """
1226 Representation of Build<->MockChroot relation
1227 """
1228
1229 __table_args__ = (db.Index('build_chroot_status_started_on_idx', "status", "started_on"),)
1230
1231 mock_chroot_id = db.Column(db.Integer, db.ForeignKey("mock_chroot.id"),
1232 primary_key=True)
1233 mock_chroot = db.relationship("MockChroot", backref=db.backref("builds"))
1234 build_id = db.Column(db.Integer, db.ForeignKey("build.id"),
1235 primary_key=True)
1236 build = db.relationship("Build", backref=db.backref("build_chroots"))
1237 git_hash = db.Column(db.String(40))
1238 status = db.Column(db.Integer, default=StatusEnum("waiting"))
1239
1240 started_on = db.Column(db.Integer, index=True)
1241 ended_on = db.Column(db.Integer, index=True)
1242
1243
1244 result_dir = db.Column(db.Text, default='', server_default='', nullable=False)
1245
1246 build_requires = db.Column(db.Text)
1247
1248 @property
1250 """
1251 Textual representation of name of this chroot
1252 """
1253 return self.mock_chroot.name
1254
1255 @property
1257 """
1258 Return text representation of status of this build chroot
1259 """
1260 if self.status is not None:
1261 return StatusEnum(self.status)
1262 return "unknown"
1263
1264 @property
1267
1268 @property
1271
1272 @property
1286
1287 @property
1293
1294
1295 -class LegalFlag(db.Model, helpers.Serializer):
1296 id = db.Column(db.Integer, primary_key=True)
1297
1298 raise_message = db.Column(db.Text)
1299
1300 raised_on = db.Column(db.Integer)
1301
1302 resolved_on = db.Column(db.Integer)
1303
1304
1305 copr_id = db.Column(db.Integer, db.ForeignKey("copr.id"), nullable=True)
1306
1307 copr = db.relationship(
1308 "Copr", backref=db.backref("legal_flags", cascade="all"))
1309
1310 reporter_id = db.Column(db.Integer, db.ForeignKey("user.id"))
1311 reporter = db.relationship("User",
1312 backref=db.backref("legal_flags_raised"),
1313 foreign_keys=[reporter_id],
1314 primaryjoin="LegalFlag.reporter_id==User.id")
1315
1316 resolver_id = db.Column(
1317 db.Integer, db.ForeignKey("user.id"), nullable=True)
1318 resolver = db.relationship("User",
1319 backref=db.backref("legal_flags_resolved"),
1320 foreign_keys=[resolver_id],
1321 primaryjoin="LegalFlag.resolver_id==User.id")
1322
1323
1324 -class Action(db.Model, helpers.Serializer):
1325 """
1326 Representation of a custom action that needs
1327 backends cooperation/admin attention/...
1328 """
1329
1330 id = db.Column(db.Integer, primary_key=True)
1331
1332 action_type = db.Column(db.Integer, nullable=False)
1333
1334 object_type = db.Column(db.String(20))
1335
1336 object_id = db.Column(db.Integer)
1337
1338 old_value = db.Column(db.String(255))
1339 new_value = db.Column(db.String(255))
1340
1341 data = db.Column(db.Text)
1342
1343 result = db.Column(
1344 db.Integer, default=BackendResultEnum("waiting"))
1345
1346 message = db.Column(db.Text)
1347
1348 created_on = db.Column(db.Integer)
1349
1350 ended_on = db.Column(db.Integer)
1351
1354
1363
1376
1377
1378 -class Krb5Login(db.Model, helpers.Serializer):
1379 """
1380 Represents additional user information for kerberos authentication.
1381 """
1382
1383 __tablename__ = "krb5_login"
1384
1385
1386 user_id = db.Column(db.Integer, db.ForeignKey("user.id"), nullable=False)
1387
1388
1389 config_name = db.Column(db.String(30), nullable=False, primary_key=True)
1390
1391
1392 primary = db.Column(db.String(80), nullable=False, primary_key=True)
1393
1394 user = db.relationship("User", backref=db.backref("krb5_logins"))
1395
1398 """
1399 Generic store for simple statistics.
1400 """
1401
1402 name = db.Column(db.String(127), primary_key=True)
1403 counter_type = db.Column(db.String(30))
1404
1405 counter = db.Column(db.Integer, default=0, server_default="0")
1406
1407
1408 -class Group(db.Model, helpers.Serializer):
1409
1410 """
1411 Represents FAS groups and their aliases in Copr
1412 """
1413
1414 id = db.Column(db.Integer, primary_key=True)
1415 name = db.Column(db.String(127))
1416
1417
1418 fas_name = db.Column(db.String(127))
1419
1420 @property
1422 return u"@{}".format(self.name)
1423
1426
1429
1430
1431 -class Batch(db.Model):
1432 id = db.Column(db.Integer, primary_key=True)
1433 blocked_by_id = db.Column(db.Integer, db.ForeignKey("batch.id"), nullable=True)
1434 blocked_by = db.relationship("Batch", remote_side=[id])
1435
1436 @property
1439
1440
1441 -class Module(db.Model, helpers.Serializer):
1442 id = db.Column(db.Integer, primary_key=True)
1443 name = db.Column(db.String(100), nullable=False)
1444 stream = db.Column(db.String(100), nullable=False)
1445 version = db.Column(db.BigInteger, nullable=False)
1446 summary = db.Column(db.String(100), nullable=False)
1447 description = db.Column(db.Text)
1448 created_on = db.Column(db.Integer, nullable=True)
1449
1450
1451
1452
1453
1454
1455
1456 yaml_b64 = db.Column(db.Text)
1457
1458
1459 copr_id = db.Column(db.Integer, db.ForeignKey("copr.id"))
1460 copr = db.relationship("Copr", backref=db.backref("modules"))
1461
1462 __table_args__ = (
1463 db.UniqueConstraint("copr_id", "name", "stream", "version", name="copr_name_stream_version_uniq"),
1464 )
1465
1466 @property
1468 return base64.b64decode(self.yaml_b64)
1469
1470 @property
1472 mmd = Modulemd.ModuleStream()
1473 mmd.import_from_string(self.yaml.decode("utf-8"))
1474 return mmd
1475
1476 @property
1479
1480 @property
1483
1484 @property
1487
1488 @property
1490 """
1491 Return numeric representation of status of this build
1492 """
1493 if any(b for b in self.builds if b.status == StatusEnum("failed")):
1494 return ModuleStatusEnum("failed")
1495 return self.action.result if self.action else ModuleStatusEnum("pending")
1496
1497 @property
1499 """
1500 Return text representation of status of this build
1501 """
1502 return ModuleStatusEnum(self.status)
1503
1504 @property
1507
1508 @property
1511
1512 @property
1514 return {k: v.get_rpms().get() for k, v in self.modulemd.get_profiles().items()}
1515
1522