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 flask import url_for
17
18 from copr_common.enums import ActionTypeEnum, BackendResultEnum, FailTypeEnum, ModuleStatusEnum, StatusEnum
19 from coprs import constants
20 from coprs import db
21 from coprs import helpers
22 from coprs import app
23
24 import itertools
25 import operator
26 from coprs.helpers import JSONEncodedDict
27
28 import gi
29 gi.require_version('Modulemd', '1.0')
30 from gi.repository import Modulemd
36
61
64 """
65 Records all the private information for a user.
66 """
67
68 user_id = db.Column(db.Integer, db.ForeignKey("user.id"), primary_key=True,
69 nullable=False)
70
71
72 mail = db.Column(db.String(150), nullable=False)
73
74
75 timezone = db.Column(db.String(50), nullable=True)
76
77
78 api_login = db.Column(db.String(40), nullable=False, default="abc")
79 api_token = db.Column(db.String(40), nullable=False, default="abc")
80 api_token_expiration = db.Column(
81 db.Date, nullable=False, default=datetime.date(2000, 1, 1))
82
83
84 -class User(db.Model, helpers.Serializer):
85 __table__ = outerjoin(_UserPublic.__table__, _UserPrivate.__table__)
86 id = column_property(_UserPublic.__table__.c.id, _UserPrivate.__table__.c.user_id)
87
88 @property
90 """
91 Return the short username of the user, e.g. bkabrda
92 """
93
94 return self.username
95
97 """
98 Get permissions of this user for the given copr.
99 Caches the permission during one request,
100 so use this if you access them multiple times
101 """
102
103 if not hasattr(self, "_permissions_for_copr"):
104 self._permissions_for_copr = {}
105 if copr.name not in self._permissions_for_copr:
106 self._permissions_for_copr[copr.name] = (
107 CoprPermission.query
108 .filter_by(user=self)
109 .filter_by(copr=copr)
110 .first()
111 )
112 return self._permissions_for_copr[copr.name]
113
130
131 @property
137
138 @property
141
143 """
144 :type group: Group
145 """
146 if group.fas_name in self.user_teams:
147 return True
148 else:
149 return False
150
169
170 @property
172
173 return ["id", "name"]
174
175 @property
177 """
178 Get number of coprs for this user.
179 """
180
181 return (Copr.query.filter_by(user=self).
182 filter_by(deleted=False).
183 filter_by(group_id=None).
184 count())
185
186 @property
188 """
189 Return url to libravatar image.
190 """
191
192 try:
193 return libravatar_url(email=self.mail, https=True)
194 except IOError:
195 return ""
196
199 """
200 Representation of User or Group <-> Copr relation
201 """
202 id = db.Column(db.Integer, primary_key=True)
203
204 copr_id = db.Column(db.Integer, db.ForeignKey("copr.id"))
205 user_id = db.Column(db.Integer, db.ForeignKey("user.id"), nullable=True, index=True)
206 group_id = db.Column(db.Integer, db.ForeignKey("group.id"), nullable=True, index=True)
207 position = db.Column(db.Integer, nullable=False)
208
209 copr = db.relationship("Copr")
210 user = db.relationship("User")
211 group = db.relationship("Group")
212
213
214 -class _CoprPublic(db.Model, helpers.Serializer, CoprSearchRelatedData):
215 """
216 Represents public part of a single copr (personal repo with builds, mock
217 chroots, etc.).
218 """
219
220 __tablename__ = "copr"
221 __table_args__ = (
222 db.Index('copr_name_group_id_idx', 'name', 'group_id'),
223 )
224
225 id = db.Column(db.Integer, primary_key=True)
226
227 name = db.Column(db.String(100), nullable=False)
228 homepage = db.Column(db.Text)
229 contact = db.Column(db.Text)
230
231
232 repos = db.Column(db.Text)
233
234 created_on = db.Column(db.Integer)
235
236 description = db.Column(db.Text)
237 instructions = db.Column(db.Text)
238 deleted = db.Column(db.Boolean, default=False)
239 playground = db.Column(db.Boolean, default=False)
240
241
242 auto_createrepo = db.Column(db.Boolean, default=True)
243
244
245 user_id = db.Column(db.Integer, db.ForeignKey("user.id"), index=True)
246 group_id = db.Column(db.Integer, db.ForeignKey("group.id"))
247 forked_from_id = db.Column(db.Integer, db.ForeignKey("copr.id"))
248
249
250 build_enable_net = db.Column(db.Boolean, default=True,
251 server_default="1", nullable=False)
252
253 unlisted_on_hp = db.Column(db.Boolean, default=False, nullable=False)
254
255
256 latest_indexed_data_update = db.Column(db.Integer)
257
258
259 persistent = db.Column(db.Boolean, default=False, nullable=False, server_default="0")
260
261
262 auto_prune = db.Column(db.Boolean, default=True, nullable=False, server_default="1")
263
264
265 use_bootstrap_container = db.Column(db.Boolean, default=False, nullable=False, server_default="0")
266
267
268 follow_fedora_branching = db.Column(db.Boolean, default=True, nullable=False, server_default="1")
269
270
271 scm_repo_url = db.Column(db.Text)
272 scm_api_type = db.Column(db.Text)
273
274
275 delete_after = db.Column(db.DateTime, index=True, nullable=True)
276
277 multilib = db.Column(db.Boolean, default=False, nullable=False, server_default="0")
278
281 """
282 Represents private part of a single copr (personal repo with builds, mock
283 chroots, etc.).
284 """
285
286 __table_args__ = (
287 db.Index('copr_private_webhook_secret', 'webhook_secret'),
288 )
289
290
291 copr_id = db.Column(db.Integer, db.ForeignKey("copr.id"), index=True,
292 nullable=False, primary_key=True)
293
294
295 webhook_secret = db.Column(db.String(100))
296
297
298 scm_api_auth_json = db.Column(db.Text)
299
300
301 -class Copr(db.Model, helpers.Serializer):
302 """
303 Represents private a single copr (personal repo with builds, mock chroots,
304 etc.).
305 """
306
307
308
309 __table__ = outerjoin(_CoprPublic.__table__, _CoprPrivate.__table__)
310 id = column_property(
311 _CoprPublic.__table__.c.id,
312 _CoprPrivate.__table__.c.copr_id
313 )
314
315
316 user = db.relationship("User", backref=db.backref("coprs"))
317 group = db.relationship("Group", backref=db.backref("groups"))
318 mock_chroots = association_proxy("copr_chroots", "mock_chroot")
319 forked_from = db.relationship("Copr",
320 remote_side=_CoprPublic.id,
321 foreign_keys=[_CoprPublic.forked_from_id],
322 backref=db.backref("all_forks"))
323
324 @property
326 return [fork for fork in self.all_forks if not fork.deleted]
327
328 @property
329 - def main_dir(self):
330 """
331 Return main copr dir for a Copr
332 """
333 return CoprDir.query.filter(CoprDir.copr_id==self.id).filter(CoprDir.main==True).one()
334
335 @property
340
341 @property
343 """
344 Return True if copr belongs to a group
345 """
346 return self.group is not None
347
348 @property
354
355 @property
361
362 @property
364 """
365 Return repos of this copr as a list of strings
366 """
367 return self.repos.split()
368
369 @property
375
376 @property
396
397
398 @property
400 """
401 :rtype: list of CoprChroot
402 """
403 return [c for c in self.copr_chroots if c.is_active]
404
405 @property
407 """
408 Return list of active mock_chroots of this copr
409 """
410 return sorted(self.active_chroots, key=lambda ch: ch.name)
411
412 @property
416
417 @property
419 """
420 Return list of active mock_chroots of this copr
421 """
422 chroots = [("{} {}".format(c.os_release, c.os_version), c.arch) for c in self.active_chroots_sorted]
423 output = []
424 for os, chs in itertools.groupby(chroots, operator.itemgetter(0)):
425 output.append((os, [ch[1] for ch in chs]))
426
427 return output
428
429 @property
431 """
432 Return number of builds in this copr
433 """
434 return len(self.builds)
435
436 @property
439
440 @disable_createrepo.setter
443
444 @property
447
448 @property
460
466
467 @property
470
471 @property
474
475 @property
480
481 @property
483 return "-".join([self.owner_name.replace("@", "group_"), self.name])
484
485 @property
487 return "/".join([self.repo_url, "modules"])
488
489 - def to_dict(self, private=False, show_builds=True, show_chroots=True):
490 result = {}
491 for key in ["id", "name", "description", "instructions"]:
492 result[key] = str(copy.copy(getattr(self, key)))
493 result["owner"] = self.owner_name
494 return result
495
496 @property
501
504
505 @property
508
509 @enable_net.setter
512
515
516 @property
518 if self.delete_after is None:
519 return None
520
521 delta = self.delete_after - datetime.datetime.now()
522 return delta.days if delta.days > 0 else 0
523
524 @delete_after_days.setter
533
534 @property
539
540 @property
547
549 """
550 Association class for Copr<->Permission relation
551 """
552
553
554
555 copr_builder = db.Column(db.SmallInteger, default=0)
556
557 copr_admin = db.Column(db.SmallInteger, default=0)
558
559
560 user_id = db.Column(db.Integer, db.ForeignKey("user.id"), primary_key=True)
561 user = db.relationship("User", backref=db.backref("copr_permissions"))
562 copr_id = db.Column(db.Integer, db.ForeignKey("copr.id"), primary_key=True)
563 copr = db.relationship("Copr", backref=db.backref("copr_permissions"))
564
566 if name == 'admin':
567 self.copr_admin = value
568 elif name == 'builder':
569 self.copr_builder = value
570 else:
571 raise KeyError("{0} is not a valid copr permission".format(name))
572
579
582 """
583 Represents one of data directories for a copr.
584 """
585 id = db.Column(db.Integer, primary_key=True)
586
587 name = db.Column(db.Text, index=True)
588 main = db.Column(db.Boolean, index=True, default=False, server_default="0", nullable=False)
589
590 ownername = db.Column(db.Text, index=True, nullable=False)
591
592 copr_id = db.Column(db.Integer, db.ForeignKey("copr.id"), index=True, nullable=False)
593 copr = db.relationship("Copr", backref=db.backref("dirs"))
594
595 __table_args__ = (
596 db.Index('only_one_main_copr_dir', copr_id, main,
597 unique=True, postgresql_where=(main==True)),
598
599 db.UniqueConstraint('ownername', 'name',
600 name='ownername_copr_dir_uniq'),
601 )
602
607
608 @property
611
612 @property
615
616 @property
620
621 @property
627
628
629 -class Package(db.Model, helpers.Serializer, CoprSearchRelatedData):
630 """
631 Represents a single package in a project_dir.
632 """
633
634 __table_args__ = (
635 db.UniqueConstraint('copr_dir_id', 'name', name='packages_copr_dir_pkgname'),
636 db.Index('package_webhook_sourcetype', 'webhook_rebuild', 'source_type'),
637 )
638
643
644 id = db.Column(db.Integer, primary_key=True)
645 name = db.Column(db.String(100), nullable=False)
646
647 source_type = db.Column(db.Integer, default=helpers.BuildSourceEnum("unset"))
648
649 source_json = db.Column(db.Text)
650
651 webhook_rebuild = db.Column(db.Boolean, default=False)
652
653 enable_net = db.Column(db.Boolean, default=False, server_default="0", nullable=False)
654
655
656 max_builds = db.Column(db.Integer, index=True)
657
658 @validates('max_builds')
660 return None if value == 0 else value
661
662 builds = db.relationship("Build", order_by="Build.id")
663
664
665 copr_id = db.Column(db.Integer, db.ForeignKey("copr.id"), index=True)
666 copr = db.relationship("Copr", backref=db.backref("packages"))
667
668 copr_dir_id = db.Column(db.Integer, db.ForeignKey("copr_dir.id"), index=True)
669 copr_dir = db.relationship("CoprDir", backref=db.backref("packages"))
670
671
672
673 chroot_blacklist_raw = db.Column(db.Text)
674
675 @property
678
679 @property
684
685 @property
688
689 @property
691 """
692 Package's source type (and source_json) is being derived from its first build, which works except
693 for "link" and "upload" cases. Consider these being equivalent to source_type being unset.
694 """
695 return self.source_type and self.source_type_text != "link" and self.source_type_text != "upload"
696
697 @property
702
703 @property
709
715
716 - def to_dict(self, with_latest_build=False, with_latest_succeeded_build=False, with_all_builds=False):
717 package_dict = super(Package, self).to_dict()
718 package_dict['source_type'] = helpers.BuildSourceEnum(package_dict['source_type'])
719
720 if with_latest_build:
721 build = self.last_build(successful=False)
722 package_dict['latest_build'] = build.to_dict(with_chroot_states=True) if build else None
723 if with_latest_succeeded_build:
724 build = self.last_build(successful=True)
725 package_dict['latest_succeeded_build'] = build.to_dict(with_chroot_states=True) if build else None
726 if with_all_builds:
727 package_dict['builds'] = [build.to_dict(with_chroot_states=True) for build in reversed(self.builds)]
728
729 return package_dict
730
733
734
735 @property
737 if not self.chroot_blacklist_raw:
738 return []
739
740 blacklisted = []
741 for pattern in self.chroot_blacklist_raw.split(','):
742 pattern = pattern.strip()
743 if not pattern:
744 continue
745 blacklisted.append(pattern)
746
747 return blacklisted
748
749
750 @staticmethod
752 for pattern in patterns:
753 if fnmatch(chroot.name, pattern):
754 return True
755 return False
756
757
758 @property
759 - def main_pkg(self):
760 if self.copr_dir.main:
761 return self
762
763 main_pkg = Package.query.filter_by(
764 name=self.name,
765 copr_dir_id=self.copr.main_dir.id
766 ).first()
767 return main_pkg
768
769
770 @property
782
783
784 -class Build(db.Model, helpers.Serializer):
785 """
786 Representation of one build in one copr
787 """
788
789 SCM_COMMIT = 'commit'
790 SCM_PULL_REQUEST = 'pull-request'
791
792 __table_args__ = (db.Index('build_canceled', "canceled"),
793 db.Index('build_order', "is_background", "id"),
794 db.Index('build_filter', "source_type", "canceled"),
795 db.Index('build_canceled_is_background_source_status_id_idx', 'canceled', "is_background", "source_status", "id"),
796 )
797
811
812 id = db.Column(db.Integer, primary_key=True)
813
814 pkgs = db.Column(db.Text)
815
816 built_packages = db.Column(db.Text)
817
818 pkg_version = db.Column(db.Text)
819
820 canceled = db.Column(db.Boolean, default=False)
821
822 repos = db.Column(db.Text)
823
824
825 submitted_on = db.Column(db.Integer, nullable=False)
826
827 result_dir = db.Column(db.Text, default='', server_default='', nullable=False)
828
829 memory_reqs = db.Column(db.Integer, default=constants.DEFAULT_BUILD_MEMORY)
830
831 timeout = db.Column(db.Integer, default=constants.DEFAULT_BUILD_TIMEOUT)
832
833 enable_net = db.Column(db.Boolean, default=False,
834 server_default="0", nullable=False)
835
836 source_type = db.Column(db.Integer, default=helpers.BuildSourceEnum("unset"))
837
838 source_json = db.Column(db.Text)
839
840 fail_type = db.Column(db.Integer, default=FailTypeEnum("unset"))
841
842 is_background = db.Column(db.Boolean, default=False, server_default="0", nullable=False)
843
844 source_status = db.Column(db.Integer, default=StatusEnum("waiting"))
845 srpm_url = db.Column(db.Text)
846
847
848 user_id = db.Column(db.Integer, db.ForeignKey("user.id"), index=True)
849 user = db.relationship("User", backref=db.backref("builds"))
850 copr_id = db.Column(db.Integer, db.ForeignKey("copr.id"), index=True)
851 copr = db.relationship("Copr", backref=db.backref("builds"))
852 package_id = db.Column(db.Integer, db.ForeignKey("package.id"), index=True)
853 package = db.relationship("Package")
854
855 chroots = association_proxy("build_chroots", "mock_chroot")
856
857 batch_id = db.Column(db.Integer, db.ForeignKey("batch.id"))
858 batch = db.relationship("Batch", backref=db.backref("builds"))
859
860 module_id = db.Column(db.Integer, db.ForeignKey("module.id"), index=True)
861 module = db.relationship("Module", backref=db.backref("builds"))
862
863 copr_dir_id = db.Column(db.Integer, db.ForeignKey("copr_dir.id"), index=True)
864 copr_dir = db.relationship("CoprDir", backref=db.backref("builds"))
865
866
867 scm_object_id = db.Column(db.Text)
868 scm_object_type = db.Column(db.Text)
869 scm_object_url = db.Column(db.Text)
870
871
872 update_callback = db.Column(db.Text)
873
874
875 submitted_by = db.Column(db.Text)
876
877 @property
880
881 @property
884
885 @property
888
889 @property
892
893 @property
896
897 @property
898 - def fail_type_text(self):
899 return FailTypeEnum(self.fail_type)
900
901 @property
903 if self.repos is None:
904 return list()
905 else:
906 return self.repos.split()
907
908 @property
911
912 @property
914 return "{:08d}".format(self.id)
915
921
922 @property
924 if app.config["COPR_DIST_GIT_LOGS_URL"]:
925 return "{}/{}.log".format(app.config["COPR_DIST_GIT_LOGS_URL"],
926 self.task_id.replace('/', '_'))
927 return None
928
929 @property
937
938 @property
943
944 @property
947
948 @property
956
957 @property
960
961 @property
968
969 @property
972
973 @property
976
977 @property
980
981 @property
990
991 @property
994
996 """
997 Get build chroots with states which present in `states` list
998 If states == None, function returns build_chroots
999 """
1000 chroot_states_map = dict(zip(self.build_chroots, self.chroot_states))
1001 if statuses is not None:
1002 statuses = set(statuses)
1003 else:
1004 return self.build_chroots
1005
1006 return [
1007 chroot for chroot, status in chroot_states_map.items()
1008 if status in statuses
1009 ]
1010
1011 @property
1013 return {b.name: b for b in self.build_chroots}
1014
1015 @property
1017 """
1018 Return build status.
1019 """
1020 if self.canceled:
1021 return StatusEnum("canceled")
1022
1023 use_src_statuses = ["starting", "pending", "running", "importing", "failed"]
1024 if self.source_status in [StatusEnum(s) for s in use_src_statuses]:
1025 return self.source_status
1026
1027 if not self.chroot_states:
1028
1029
1030
1031
1032
1033
1034
1035 app.logger.error("Build %s has source_status succeeded, but "
1036 "no build_chroots", self.id)
1037 return StatusEnum("waiting")
1038
1039 for state in ["running", "starting", "pending", "failed", "succeeded", "skipped", "forked"]:
1040 if StatusEnum(state) in self.chroot_states:
1041 return StatusEnum(state)
1042
1043 if StatusEnum("waiting") in self.chroot_states:
1044
1045
1046
1047
1048 app.logger.error("Build chroots pending, even though build %s"
1049 " has succeeded source_status", self.id)
1050 return StatusEnum("pending")
1051
1052 return None
1053
1054 @property
1056 """
1057 Return text representation of status of this build.
1058 """
1059 if self.status != None:
1060 return StatusEnum(self.status)
1061 return "unknown"
1062
1063 @property
1065 """
1066 Find out if this build is cancelable.
1067 """
1068 return not self.finished and self.status != StatusEnum("starting")
1069
1070 @property
1072 """
1073 Find out if this build is repeatable.
1074
1075 Build is repeatable only if sources has been imported.
1076 """
1077 return self.source_status == StatusEnum("succeeded")
1078
1079 @property
1081 """
1082 Find out if this build is in finished state.
1083
1084 Build is finished only if all its build_chroots are in finished state or
1085 the build was canceled.
1086 """
1087 if self.canceled:
1088 return True
1089 if not self.build_chroots:
1090 return StatusEnum(self.source_status) in helpers.FINISHED_STATUSES
1091 return all([chroot.finished for chroot in self.build_chroots])
1092
1093 @property
1096
1097 @property
1099 """
1100 Find out if this build is persistent.
1101
1102 This property is inherited from the project.
1103 """
1104 return self.copr.persistent
1105
1106 @property
1108 try:
1109 return self.package.name
1110 except:
1111 return None
1112
1113 - def to_dict(self, options=None, with_chroot_states=False):
1126
1127 @property
1129 """
1130 Return tuple (submitter_string, submitter_link), while the
1131 submitter_link may be empty if we are not able to detect it
1132 wisely.
1133 """
1134 if self.user:
1135 user = self.user.name
1136 return (user, url_for('coprs_ns.coprs_by_user', username=user))
1137
1138 if self.submitted_by:
1139 links = ['http://', 'https://']
1140 if any([self.submitted_by.startswith(x) for x in links]):
1141 return (self.submitted_by, self.submitted_by)
1142
1143 return (self.submitted_by, None)
1144
1145 return (None, None)
1146
1147 @property
1149 """
1150 Return a string unique to project + submitter. At this level copr
1151 backend later applies builder user-VM separation policy (VMs are only
1152 re-used for builds which have the same build.sandbox value)
1153 """
1154 submitter, _ = self.submitter
1155 if not submitter:
1156
1157
1158 submitter = uuid.uuid4()
1159
1160 return '{0}--{1}'.format(self.copr.full_name, submitter)
1161
1164 """
1165 1:N mapping: branch -> chroots
1166 """
1167
1168
1169 name = db.Column(db.String(50), primary_key=True)
1170
1171
1172 -class MockChroot(db.Model, helpers.Serializer):
1173 """
1174 Representation of mock chroot
1175 """
1176
1177 __table_args__ = (
1178 db.UniqueConstraint('os_release', 'os_version', 'arch', name='mock_chroot_uniq'),
1179 )
1180
1181 id = db.Column(db.Integer, primary_key=True)
1182
1183 os_release = db.Column(db.String(50), nullable=False)
1184
1185 os_version = db.Column(db.String(50), nullable=False)
1186
1187 arch = db.Column(db.String(50), nullable=False)
1188 is_active = db.Column(db.Boolean, default=True)
1189
1190
1191 distgit_branch_name = db.Column(db.String(50),
1192 db.ForeignKey("dist_git_branch.name"),
1193 nullable=False)
1194
1195 distgit_branch = db.relationship("DistGitBranch",
1196 backref=db.backref("chroots"))
1197
1198
1199
1200 final_prunerepo_done = db.Column(db.Boolean, default=False, server_default="0", nullable=False)
1201
1202 comment = db.Column(db.Text, nullable=True)
1203
1204 multilib_pairs = {
1205 'x86_64': 'i386',
1206 }
1207
1208 @classmethod
1217
1218 @property
1220 """
1221 Textual representation of name of this chroot
1222 """
1223 return "{}-{}-{}".format(self.os_release, self.os_version, self.arch)
1224
1225 @property
1227 """
1228 Textual representation of name of this or release
1229 """
1230 return "{}-{}".format(self.os_release, self.os_version)
1231
1232 @property
1234 """
1235 Textual representation of the operating system name
1236 """
1237 return "{0} {1}".format(self.os_release, self.os_version)
1238
1239 @property
1244
1245
1246 -class CoprChroot(db.Model, helpers.Serializer):
1247 """
1248 Representation of Copr<->MockChroot relation
1249 """
1250
1251 buildroot_pkgs = db.Column(db.Text)
1252 repos = db.Column(db.Text, default="", server_default="", nullable=False)
1253 mock_chroot_id = db.Column(
1254 db.Integer, db.ForeignKey("mock_chroot.id"), primary_key=True)
1255 mock_chroot = db.relationship(
1256 "MockChroot", backref=db.backref("copr_chroots"))
1257 copr_id = db.Column(db.Integer, db.ForeignKey("copr.id"), primary_key=True)
1258 copr = db.relationship("Copr",
1259 backref=db.backref(
1260 "copr_chroots",
1261 single_parent=True,
1262 cascade="all,delete,delete-orphan"))
1263
1264 comps_zlib = db.Column(db.LargeBinary(), nullable=True)
1265 comps_name = db.Column(db.String(127), nullable=True)
1266
1267 with_opts = db.Column(db.Text, default="", server_default="", nullable=False)
1268 without_opts = db.Column(db.Text, default="", server_default="", nullable=False)
1269
1270
1271
1272 delete_after = db.Column(db.DateTime, index=True)
1273 delete_notify = db.Column(db.DateTime, index=True)
1274
1276 if isinstance(comps_xml, str):
1277 data = comps_xml.encode("utf-8")
1278 else:
1279 data = comps_xml
1280 self.comps_zlib = zlib.compress(data)
1281
1282 @property
1285
1286 @property
1288 return (self.repos or "").split()
1289
1290 @property
1294
1295 @property
1301
1302 @property
1305
1306 @property
1309
1310 @property
1312 if not self.delete_after:
1313 return None
1314 now = datetime.datetime.now()
1315 days = (self.delete_after - now).days
1316 return days if days > 0 else 0
1317
1319 options = {"__columns_only__": [
1320 "buildroot_pkgs", "repos", "comps_name", "copr_id", "with_opts", "without_opts"
1321 ]}
1322 d = super(CoprChroot, self).to_dict(options=options)
1323 d["mock_chroot"] = self.mock_chroot.name
1324 return d
1325
1328 """
1329 Representation of Build<->MockChroot relation
1330 """
1331
1332 __table_args__ = (db.Index('build_chroot_status_started_on_idx', "status", "started_on"),)
1333
1334 mock_chroot_id = db.Column(db.Integer, db.ForeignKey("mock_chroot.id"),
1335 primary_key=True)
1336 mock_chroot = db.relationship("MockChroot", backref=db.backref("builds"))
1337 build_id = db.Column(db.Integer, db.ForeignKey("build.id", ondelete="CASCADE"),
1338 primary_key=True)
1339 build = db.relationship("Build", backref=db.backref("build_chroots", cascade="all, delete-orphan",
1340 passive_deletes=True))
1341 git_hash = db.Column(db.String(40))
1342 status = db.Column(db.Integer, default=StatusEnum("waiting"))
1343
1344 started_on = db.Column(db.Integer, index=True)
1345 ended_on = db.Column(db.Integer, index=True)
1346
1347
1348 result_dir = db.Column(db.Text, default='', server_default='', nullable=False)
1349
1350 build_requires = db.Column(db.Text)
1351
1352 @property
1354 """
1355 Textual representation of name of this chroot
1356 """
1357 return self.mock_chroot.name
1358
1359 @property
1361 """
1362 Return text representation of status of this build chroot
1363 """
1364 if self.status is not None:
1365 return StatusEnum(self.status)
1366 return "unknown"
1367
1368 @property
1371
1372 @property
1375
1376 @property
1390
1391 @property
1397
1398 @property
1400 if not self.build.package:
1401 return None
1402
1403 if not (self.finished or self.state == "running"):
1404 return None
1405
1406 if not self.result_dir_url:
1407 return None
1408
1409 return os.path.join(self.result_dir_url,
1410 "builder-live.log" if self.state == 'running' else "builder-live.log.gz")
1411
1412
1413 -class LegalFlag(db.Model, helpers.Serializer):
1414 id = db.Column(db.Integer, primary_key=True)
1415
1416 raise_message = db.Column(db.Text)
1417
1418 raised_on = db.Column(db.Integer)
1419
1420 resolved_on = db.Column(db.Integer)
1421
1422
1423 copr_id = db.Column(db.Integer, db.ForeignKey("copr.id"), nullable=True)
1424
1425 copr = db.relationship(
1426 "Copr", backref=db.backref("legal_flags", cascade="all"))
1427
1428 reporter_id = db.Column(db.Integer, db.ForeignKey("user.id"))
1429 reporter = db.relationship("User",
1430 backref=db.backref("legal_flags_raised"),
1431 foreign_keys=[reporter_id],
1432 primaryjoin="LegalFlag.reporter_id==User.id")
1433
1434 resolver_id = db.Column(
1435 db.Integer, db.ForeignKey("user.id"), nullable=True)
1436 resolver = db.relationship("User",
1437 backref=db.backref("legal_flags_resolved"),
1438 foreign_keys=[resolver_id],
1439 primaryjoin="LegalFlag.resolver_id==User.id")
1440
1441
1442 -class Action(db.Model, helpers.Serializer):
1443 """
1444 Representation of a custom action that needs
1445 backends cooperation/admin attention/...
1446 """
1447
1448 id = db.Column(db.Integer, primary_key=True)
1449
1450 action_type = db.Column(db.Integer, nullable=False)
1451
1452 object_type = db.Column(db.String(20))
1453
1454 object_id = db.Column(db.Integer)
1455
1456 old_value = db.Column(db.String(255))
1457 new_value = db.Column(db.String(255))
1458
1459 data = db.Column(db.Text)
1460
1461 result = db.Column(
1462 db.Integer, default=BackendResultEnum("waiting"))
1463
1464 message = db.Column(db.Text)
1465
1466 created_on = db.Column(db.Integer)
1467
1468 ended_on = db.Column(db.Integer)
1469
1472
1481
1494
1495
1496 -class Krb5Login(db.Model, helpers.Serializer):
1497 """
1498 Represents additional user information for kerberos authentication.
1499 """
1500
1501 __tablename__ = "krb5_login"
1502
1503
1504 user_id = db.Column(db.Integer, db.ForeignKey("user.id"), nullable=False)
1505
1506
1507 config_name = db.Column(db.String(30), nullable=False, primary_key=True)
1508
1509
1510 primary = db.Column(db.String(80), nullable=False, primary_key=True)
1511
1512 user = db.relationship("User", backref=db.backref("krb5_logins"))
1513
1516 """
1517 Generic store for simple statistics.
1518 """
1519
1520 name = db.Column(db.String(127), primary_key=True)
1521 counter_type = db.Column(db.String(30))
1522
1523 counter = db.Column(db.Integer, default=0, server_default="0")
1524
1525
1526 -class Group(db.Model, helpers.Serializer):
1527
1528 """
1529 Represents FAS groups and their aliases in Copr
1530 """
1531
1532 id = db.Column(db.Integer, primary_key=True)
1533 name = db.Column(db.String(127))
1534
1535
1536 fas_name = db.Column(db.String(127))
1537
1538 @property
1540 return u"@{}".format(self.name)
1541
1544
1547
1548
1549 -class Batch(db.Model):
1550 id = db.Column(db.Integer, primary_key=True)
1551 blocked_by_id = db.Column(db.Integer, db.ForeignKey("batch.id"), nullable=True)
1552 blocked_by = db.relationship("Batch", remote_side=[id])
1553
1554 @property
1557
1558
1559 -class Module(db.Model, helpers.Serializer):
1560 id = db.Column(db.Integer, primary_key=True)
1561 name = db.Column(db.String(100), nullable=False)
1562 stream = db.Column(db.String(100), nullable=False)
1563 version = db.Column(db.BigInteger, nullable=False)
1564 summary = db.Column(db.String(100), nullable=False)
1565 description = db.Column(db.Text)
1566 created_on = db.Column(db.Integer, nullable=True)
1567
1568
1569
1570
1571
1572
1573
1574 yaml_b64 = db.Column(db.Text)
1575
1576
1577 copr_id = db.Column(db.Integer, db.ForeignKey("copr.id"))
1578 copr = db.relationship("Copr", backref=db.backref("modules"))
1579
1580 __table_args__ = (
1581 db.UniqueConstraint("copr_id", "name", "stream", "version", name="copr_name_stream_version_uniq"),
1582 )
1583
1584 @property
1586 return base64.b64decode(self.yaml_b64)
1587
1588 @property
1590 mmd = Modulemd.ModuleStream()
1591 mmd.import_from_string(self.yaml.decode("utf-8"))
1592 return mmd
1593
1594 @property
1597
1598 @property
1601
1602 @property
1605
1606 @property
1608 """
1609 Return numeric representation of status of this build
1610 """
1611 if self.action:
1612 return { BackendResultEnum("success"): ModuleStatusEnum("succeeded"),
1613 BackendResultEnum("failure"): ModuleStatusEnum("failed"),
1614 BackendResultEnum("waiting"): ModuleStatusEnum("waiting"),
1615 }[self.action.result]
1616 build_statuses = [b.status for b in self.builds]
1617 for state in ["canceled", "running", "starting", "pending", "failed", "succeeded"]:
1618 if ModuleStatusEnum(state) in build_statuses:
1619 return ModuleStatusEnum(state)
1620
1621 @property
1623 """
1624 Return text representation of status of this build
1625 """
1626 return ModuleStatusEnum(self.status)
1627
1628 @property
1631
1632 @property
1635
1636 @property
1638 return {k: v.get_rpms().get() for k, v in self.modulemd.get_profiles().items()}
1639
1646