Package coprs :: Package logic :: Module complex_logic
[hide private]
[frames] | no frames]

Source Code for Module coprs.logic.complex_logic

  1  # coding: utf-8 
  2   
  3  import datetime 
  4  import time 
  5  import flask 
  6  import sqlalchemy 
  7   
  8  from copr_common.enums import StatusEnum 
  9  from coprs import db 
 10  from coprs import helpers 
 11  from coprs import models 
 12  from coprs import exceptions 
 13  from coprs.exceptions import ObjectNotFound, ActionInProgressException 
 14  from coprs.logic.builds_logic import BuildsLogic 
 15  from coprs.logic.batches_logic import BatchesLogic 
 16  from coprs.logic.packages_logic import PackagesLogic 
 17  from coprs.logic.actions_logic import ActionsLogic 
 18   
 19  from coprs.logic.users_logic import UsersLogic 
 20  from coprs.models import User, Copr 
 21  from coprs.logic.coprs_logic import (CoprsLogic, CoprDirsLogic, CoprChrootsLogic, 
 22                                       PinnedCoprsLogic, MockChrootsLogic) 
 23   
 24   
 25  @sqlalchemy.event.listens_for(models.Copr.deleted, "set") 
26 -def unpin_projects_on_delete(copr, deleted, oldvalue, event):
27 if not deleted: 28 return 29 PinnedCoprsLogic.delete_by_copr(copr)
30
31 32 -class ComplexLogic(object):
33 """ 34 Used for manipulation which affects multiply models 35 """ 36 37 @classmethod
38 - def delete_copr(cls, copr, admin_action=False):
39 """ 40 Delete copr and all its builds. 41 42 :param copr: 43 :param admin_action: set to True to bypass permission check 44 :raises ActionInProgressException: 45 :raises InsufficientRightsException: 46 """ 47 48 if admin_action: 49 user = copr.user 50 else: 51 user = flask.g.user 52 53 builds_query = BuildsLogic.get_multiple_by_copr(copr=copr) 54 55 if copr.persistent: 56 raise exceptions.InsufficientRightsException("This project is protected against deletion.") 57 58 for build in builds_query: 59 # Don't send delete action for each build, rather send an action to delete 60 # a whole project as a part of CoprsLogic.delete_unsafe() method. 61 BuildsLogic.delete_build(user, build, send_delete_action=False) 62 63 CoprsLogic.delete_unsafe(user, copr)
64 65 66 @classmethod
67 - def delete_expired_projects(cls):
68 query = ( 69 models.Copr.query 70 .filter(models.Copr.delete_after.isnot(None)) 71 .filter(models.Copr.delete_after < datetime.datetime.now()) 72 .filter(models.Copr.deleted.isnot(True)) 73 ) 74 for copr in query.all(): 75 print("deleting project '{}'".format(copr.full_name)) 76 try: 77 cls.delete_copr(copr, admin_action=True) 78 except ActionInProgressException as e: 79 print(e) 80 print("project {} postponed".format(copr.full_name))
81 82 83 @classmethod
84 - def fork_copr(cls, copr, user, dstname, dstgroup=None):
85 forking = ProjectForking(user, dstgroup) 86 created = (not bool(forking.get(copr, dstname))) 87 fcopr = forking.fork_copr(copr, dstname) 88 89 if fcopr.full_name == copr.full_name: 90 raise exceptions.DuplicateException("Source project should not be same as destination") 91 92 builds_map = {} 93 srpm_builds_src = [] 94 srpm_builds_dst = [] 95 96 for package in copr.main_dir.packages: 97 fpackage = forking.fork_package(package, fcopr) 98 99 builds = PackagesLogic.last_successful_build_chroots(package) 100 if not builds: 101 continue 102 103 for build, build_chroots in builds.items(): 104 fbuild = forking.fork_build(build, fcopr, fpackage, build_chroots) 105 106 if build.result_dir: 107 srpm_builds_src.append(build.result_dir) 108 srpm_builds_dst.append(fbuild.result_dir) 109 110 for chroot, fchroot in zip(build_chroots, fbuild.build_chroots): 111 if not chroot.result_dir: 112 continue 113 if chroot.name not in builds_map: 114 builds_map[chroot.name] = {chroot.result_dir: fchroot.result_dir} 115 else: 116 builds_map[chroot.name][chroot.result_dir] = fchroot.result_dir 117 118 builds_map['srpm-builds'] = dict(zip(srpm_builds_src, srpm_builds_dst)) 119 120 db.session.commit() 121 ActionsLogic.send_fork_copr(copr, fcopr, builds_map) 122 return fcopr, created
123 124 @staticmethod
125 - def get_group_copr_safe(group_name, copr_name, **kwargs):
126 group = ComplexLogic.get_group_by_name_safe(group_name) 127 try: 128 return CoprsLogic.get_by_group_id( 129 group.id, copr_name, **kwargs).one() 130 except sqlalchemy.orm.exc.NoResultFound: 131 raise ObjectNotFound( 132 message="Project @{}/{} does not exist." 133 .format(group_name, copr_name))
134 135 @staticmethod
136 - def get_copr_safe(user_name, copr_name, **kwargs):
137 """ Get one project. 138 139 This always return personal project. For group projects see get_group_copr_safe(). 140 """ 141 try: 142 return CoprsLogic.get(user_name, copr_name, **kwargs).filter(Copr.group_id.is_(None)).one() 143 except sqlalchemy.orm.exc.NoResultFound: 144 raise ObjectNotFound( 145 message="Project {}/{} does not exist." 146 .format(user_name, copr_name))
147 148 @staticmethod
149 - def get_copr_by_owner_safe(owner_name, copr_name, **kwargs):
150 if owner_name[0] == "@": 151 return ComplexLogic.get_group_copr_safe(owner_name[1:], copr_name, **kwargs) 152 return ComplexLogic.get_copr_safe(owner_name, copr_name, **kwargs)
153 154 @staticmethod
155 - def get_copr_by_repo_safe(repo_url):
156 copr_repo = helpers.copr_repo_fullname(repo_url) 157 if not copr_repo: 158 return None 159 try: 160 owner, copr = copr_repo.split("/") 161 except: 162 # invalid format, e.g. multiple slashes in copr_repo 163 return None 164 return ComplexLogic.get_copr_by_owner_safe(owner, copr)
165 166 @staticmethod
167 - def get_copr_dir_safe(ownername, copr_dirname, **kwargs):
168 try: 169 return CoprDirsLogic.get_by_ownername(ownername, copr_dirname).one() 170 except sqlalchemy.orm.exc.NoResultFound: 171 raise ObjectNotFound(message="copr dir {}/{} does not exist." 172 .format(ownername, copr_dirname))
173 174 @staticmethod
175 - def get_copr_by_id_safe(copr_id):
176 try: 177 return CoprsLogic.get_by_id(copr_id).one() 178 except sqlalchemy.orm.exc.NoResultFound: 179 raise ObjectNotFound( 180 message="Project with id {} does not exist." 181 .format(copr_id))
182 183 @staticmethod
184 - def get_build_safe(build_id):
185 try: 186 return BuildsLogic.get_by_id(build_id).one() 187 except sqlalchemy.orm.exc.NoResultFound: 188 raise ObjectNotFound( 189 message="Build {} does not exist.".format(build_id))
190 191 @staticmethod
192 - def get_build_chroot(build_id, chrootname):
193 """ 194 Return a `models.BuildChroot` instance based on build ID and name of the chroot. 195 If there is no such chroot, `ObjectNotFound` execption is raised. 196 """ 197 build = ComplexLogic.get_build_safe(build_id) 198 try: 199 return build.chroots_dict_by_name[chrootname] 200 except KeyError: 201 msg = "Build {} was not submitted to {} chroot.".format(build_id, chrootname) 202 if not MockChrootsLogic.get_from_name(chrootname).one_or_none(): 203 msg = "Chroot {} does not exist".format(chrootname) 204 raise ObjectNotFound(message=msg)
205 206 @staticmethod
207 - def get_package_by_id_safe(package_id):
208 try: 209 return PackagesLogic.get_by_id(package_id).one() 210 except sqlalchemy.orm.exc.NoResultFound: 211 raise ObjectNotFound( 212 message="Package {} does not exist.".format(package_id))
213 214 @staticmethod
215 - def get_package_safe(copr_dir, package_name):
216 try: 217 return PackagesLogic.get(copr_dir.id, package_name).one() 218 except sqlalchemy.orm.exc.NoResultFound: 219 raise ObjectNotFound( 220 message="Package {} in the copr_dir {} does not exist." 221 .format(package_name, copr_dir))
222 223 @staticmethod
224 - def get_group_by_name_safe(group_name):
225 try: 226 group = UsersLogic.get_group_by_alias(group_name).one() 227 except sqlalchemy.orm.exc.NoResultFound: 228 raise ObjectNotFound( 229 message="Group {} does not exist.".format(group_name)) 230 return group
231 232 @staticmethod
233 - def get_copr_chroot_safe(copr, chroot_name):
234 try: 235 chroot = CoprChrootsLogic.get_by_name_safe(copr, chroot_name) 236 except (ValueError, KeyError, RuntimeError) as e: 237 raise ObjectNotFound(message=str(e)) 238 239 if not chroot: 240 raise ObjectNotFound( 241 message="Chroot name {} does not exist.".format(chroot_name)) 242 243 return chroot
244 245 @staticmethod
246 - def get_active_groups_by_user(user_name):
247 names = flask.g.user.user_groups 248 if names: 249 query = UsersLogic.get_groups_by_names_list(names) 250 return query.filter(User.name == user_name) 251 else: 252 return []
253 254 @staticmethod
255 - def get_queue_sizes():
256 importing = BuildsLogic.get_build_importing_queue(background=False).count() 257 pending = BuildsLogic.get_pending_build_tasks(background=False).count() +\ 258 BuildsLogic.get_pending_srpm_build_tasks(background=False).count() 259 running = BuildsLogic.get_build_tasks(StatusEnum("running")).count() +\ 260 BuildsLogic.get_srpm_build_tasks(StatusEnum("running")).count() 261 starting = BuildsLogic.get_build_tasks(StatusEnum("starting")).count() +\ 262 BuildsLogic.get_srpm_build_tasks(StatusEnum("starting")).count() 263 264 return dict( 265 importing=importing, 266 pending=pending, 267 running=running, 268 starting=starting, 269 batches=len(BatchesLogic.pending_batches()), 270 )
271 272 @classmethod
273 - def get_coprs_permissible_by_user(cls, user):
274 coprs = CoprsLogic.filter_without_group_projects( 275 CoprsLogic.get_multiple_owned_by_username( 276 user.username, include_unlisted_on_hp=False)).all() 277 278 for group in user.user_groups: 279 coprs.extend(CoprsLogic.get_multiple_by_group_id(group.id).all()) 280 281 coprs += [perm.copr for perm in user.copr_permissions if 282 perm.get_permission("admin") == helpers.PermissionEnum("approved") or 283 perm.get_permission("builder") == helpers.PermissionEnum("approved")] 284 285 return set(coprs)
286 287 @classmethod
288 - def get_coprs_pinnable_by_owner(cls, owner):
296
297 298 -class ProjectForking(object):
299 - def __init__(self, user, group=None):
300 self.user = user 301 self.group = group 302 303 if group and not user.can_build_in_group(group): 304 raise exceptions.InsufficientRightsException( 305 "Only members may create projects in the particular groups.")
306
307 - def get(self, copr, name):
308 return CoprsLogic.get_by_group_id(self.group.id, name).first() if self.group \ 309 else CoprsLogic.filter_without_group_projects(CoprsLogic.get(flask.g.user.name, name)).first()
310
311 - def fork_copr(self, copr, name):
312 fcopr = self.get(copr, name) 313 if not fcopr: 314 fcopr = self.create_object(models.Copr, copr, 315 exclude=["id", "group_id", "created_on", 316 "scm_repo_url", "scm_api_type", "scm_api_auth_json", 317 "persistent", "auto_prune", "contact", "webhook_secret"]) 318 319 fcopr.forked_from_id = copr.id 320 fcopr.user = self.user 321 fcopr.user_id = self.user.id 322 fcopr.created_on = int(time.time()) 323 if name: 324 fcopr.name = name 325 if self.group: 326 fcopr.group = self.group 327 fcopr.group_id = self.group.id 328 329 fcopr_dir = models.CoprDir(name=fcopr.name, copr=fcopr, main=True) 330 331 for chroot in list(copr.copr_chroots): 332 CoprChrootsLogic.create_chroot(self.user, fcopr, chroot.mock_chroot, chroot.buildroot_pkgs, 333 chroot.repos, comps=chroot.comps, comps_name=chroot.comps_name, 334 with_opts=chroot.with_opts, without_opts=chroot.without_opts) 335 db.session.add(fcopr) 336 db.session.add(fcopr_dir) 337 338 return fcopr
339
340 - def fork_package(self, package, fcopr):
341 fpackage = PackagesLogic.get(fcopr.main_dir.id, package.name).first() 342 if not fpackage: 343 fpackage = self.create_object(models.Package, package, exclude=["id", "copr_id", "copr_dir_id", "webhook_rebuild"]) 344 fpackage.copr = fcopr 345 fpackage.copr_dir = fcopr.main_dir 346 db.session.add(fpackage) 347 return fpackage
348
349 - def fork_build(self, build, fcopr, fpackage, build_chroots):
350 fbuild = self.create_object(models.Build, build, exclude=["id", "copr_id", "copr_dir_id", "package_id", "result_dir"]) 351 fbuild.copr = fcopr 352 fbuild.package = fpackage 353 fbuild.copr_dir = fcopr.main_dir 354 db.session.add(fbuild) 355 db.session.flush() 356 357 fbuild.result_dir = '{:08}'.format(fbuild.id) 358 fbuild.build_chroots = [ 359 self.create_object(models.BuildChroot, c, 360 exclude=["id", "build_id", "result_dir", 361 "copr_chroot_id"]) 362 for c in build_chroots 363 ] 364 for chroot in fbuild.build_chroots: 365 chroot.result_dir = '{:08}-{}'.format(fbuild.id, fpackage.name) 366 chroot.status = StatusEnum("forked") 367 # the CoprChroot could be disabled in project (when we fork directly 368 # by fork_build(), without parent fork_copr(), hence one_or_none() 369 chroot.copr_chroot = CoprChrootsLogic.get_by_mock_chroot_id( 370 fcopr, 371 chroot.mock_chroot_id, 372 ).one_or_none() 373 db.session.add(fbuild) 374 return fbuild
375
376 - def create_object(self, clazz, from_object, exclude=list()):
377 arguments = {} 378 for name, column in from_object.__mapper__.columns.items(): 379 if not name in exclude: 380 arguments[name] = getattr(from_object, name) 381 return clazz(**arguments)
382
383 384 -class BuildConfigLogic(object):
385 386 @classmethod
387 - def generate_build_config(cls, copr, chroot_id):
388 """ Return dict with proper build config contents """ 389 chroot = None 390 for i in copr.copr_chroots: 391 if i.mock_chroot.name == chroot_id: 392 chroot = i 393 break 394 if not chroot: 395 return {} 396 397 packages = "" if not chroot.buildroot_pkgs else chroot.buildroot_pkgs 398 399 repos = [{ 400 "id": "copr_base", 401 "baseurl": copr.repo_url + "/{}/".format(chroot_id), 402 "name": "Copr repository", 403 }] 404 405 if copr.module_hotfixes: 406 repos[0]["module_hotfixes"] = True 407 408 if not copr.auto_createrepo: 409 repos.append({ 410 "id": "copr_base_devel", 411 "baseurl": copr.repo_url + "/{}/devel/".format(chroot_id), 412 "name": "Copr buildroot", 413 }) 414 415 repos.extend(cls.get_additional_repo_views(copr.repos_list, chroot_id)) 416 repos.extend(cls.get_additional_repo_views(chroot.repos_list, chroot_id)) 417 418 config_dict = { 419 'project_id': copr.repo_id, 420 'additional_packages': packages.split(), 421 'repos': repos, 422 'chroot': chroot_id, 423 'with_opts': chroot.with_opts.split(), 424 'without_opts': chroot.without_opts.split(), 425 } 426 config_dict.update(chroot.bootstrap_setup) 427 return config_dict
428 429 @classmethod
430 - def build_bootstrap_setup(cls, build_config, build):
431 """ Get bootstrap setup from build_config, and override it by build """ 432 build_record = {} 433 build_record["bootstrap"] = build_config.get("bootstrap", "default") 434 build_record["bootstrap_image"] = build_config.get("bootstrap_image") 435 436 # config overrides per-build 437 if build.bootstrap_set: 438 build_record["bootstrap"] = build.bootstrap 439 440 # drop unnecessary (default) fields 441 if build_record["bootstrap"] == "default": 442 del build_record['bootstrap'] 443 del build_record['bootstrap_image'] 444 elif build_record["bootstrap"] != "custom_image": 445 del build_record['bootstrap_image'] 446 return build_record
447 448 @classmethod
449 - def get_additional_repo_views(cls, repos_list, chroot_id):
450 repos = [] 451 for repo in repos_list: 452 params = helpers.parse_repo_params(repo) 453 repo_view = { 454 "id": helpers.generate_repo_name(repo), 455 "baseurl": helpers.pre_process_repo_url(chroot_id, repo), 456 "name": "Additional repo " + helpers.generate_repo_name(repo), 457 } 458 459 # We ask get_copr_by_repo_safe() here only to resolve the 460 # module_hotfixes attribute. If the asked project doesn't exist, we 461 # still adjust the 'repos' variable -- the build will eventually 462 # fail on repo downloading, but at least the copr maintainer will be 463 # notified about the misconfiguration. Better than just skip the 464 # repo. 465 try: 466 copr = ComplexLogic.get_copr_by_repo_safe(repo) 467 except ObjectNotFound: 468 copr = None 469 if copr and copr.module_hotfixes: 470 params["module_hotfixes"] = True 471 472 repo_view.update(params) 473 repos.append(repo_view) 474 return repos
475 476 @classmethod
477 - def generate_additional_repos(cls, copr_chroot):
478 base_repo = "copr://{}".format(copr_chroot.copr.full_name) 479 repos = [base_repo] + copr_chroot.repos_list + copr_chroot.copr.repos_list 480 if not copr_chroot.copr.auto_createrepo: 481 repos.append("copr://{}/devel".format(copr_chroot.copr.full_name)) 482 return repos
483