Trees | Indices | Help |
---|
|
1 import tempfile 2 import shutil 3 import json 4 import os 5 import pprint 6 import time 7 import flask 8 import sqlite3 9 from sqlalchemy.sql import text 10 from sqlalchemy import or_ 11 from sqlalchemy import and_ 12 from sqlalchemy.orm import joinedload 13 from sqlalchemy.orm.exc import NoResultFound 14 from sqlalchemy.sql import false,true 15 from werkzeug.utils import secure_filename 16 from sqlalchemy import desc,asc, bindparam, Integer 17 from collections import defaultdict 18 19 from coprs import app 20 from coprs import db 21 from coprs import exceptions 22 from coprs import models 23 from coprs import helpers 24 from coprs.constants import DEFAULT_BUILD_TIMEOUT, MAX_BUILD_TIMEOUT, DEFER_BUILD_SECONDS 25 from coprs.exceptions import MalformedArgumentException, ActionInProgressException, InsufficientRightsException 26 from coprs.helpers import StatusEnum 27 28 from coprs.logic import coprs_logic 29 from coprs.logic import users_logic 30 from coprs.logic.actions_logic import ActionsLogic 31 from coprs.models import BuildChroot,Build,Package,MockChroot 32 from .coprs_logic import MockChrootsLogic 33 34 log = app.logger38 @classmethod 41 42 # todo: move methods operating with BuildChroot to BuildChrootLogic 43 @classmethod118 119 @classmethod45 """ Returns tasks with given status. If background is specified then 46 returns normal jobs (false) or background jobs (true) 47 """ 48 result = models.BuildChroot.query.join(models.Build)\ 49 .filter(models.BuildChroot.status == status)\ 50 .order_by(models.BuildChroot.build_id.asc()) 51 if background is not None: 52 result = result.filter(models.Build.is_background == (true() if background else false())) 53 return result54 55 @classmethod57 if not limit: 58 limit = 100 59 60 query = models.Build.query 61 if user is not None: 62 query = query.filter(models.Build.user_id == user.id) 63 64 query = query.join( 65 models.BuildChroot.query 66 .filter(models.BuildChroot.ended_on.isnot(None)) 67 .order_by(models.BuildChroot.ended_on.desc()) 68 .subquery() 69 ).order_by(models.Build.id.desc()) 70 71 # Workaround - otherwise it could take less records than `limit`even though there are more of them. 72 query = query.limit(limit if limit > 100 else 100) 73 return list(query.all()[:5])74 75 @classmethod77 """ 78 Returns Builds which are waiting to be uploaded to dist git 79 """ 80 query = (models.Build.query.join(models.BuildChroot) 81 .filter(models.Build.canceled == false()) 82 .filter(models.BuildChroot.status == helpers.StatusEnum("importing")) 83 ) 84 query = query.order_by(models.BuildChroot.build_id.asc()) 85 return query86 87 @classmethod 89 """ 90 Returns BuildChroots which are - waiting to be built or 91 - older than 2 hours and unfinished 92 """ 93 # todo: filter out build without package 94 query = (models.BuildChroot.query.join(models.Build) 95 .filter(models.Build.canceled == false()) 96 .filter(models.Build.is_background == (true() if is_background else false())) 97 .filter(or_( 98 models.BuildChroot.status == helpers.StatusEnum("pending"), 99 models.BuildChroot.status == helpers.StatusEnum("starting"), 100 and_( 101 # We are moving ended_on to the BuildChroot, now it should be reliable, 102 # so we don't want to reschedule failed chroots 103 # models.BuildChroot.status.in_([ 104 # # Bug 1206562 - Cannot delete Copr because it incorrectly thinks 105 # # there are unfinished builds. Solution: `failed` but unfinished 106 # # (ended_on is null) builds should be rescheduled. 107 # # todo: we need to be sure that correct `failed` set is set together wtih `ended_on` 108 # helpers.StatusEnum("running"), 109 # helpers.StatusEnum("failed") 110 #]), 111 models.BuildChroot.status == helpers.StatusEnum("running"), 112 models.BuildChroot.started_on < int(time.time() - 1.1 * MAX_BUILD_TIMEOUT), 113 models.BuildChroot.ended_on.is_(None) 114 )) 115 )) 116 query = query.order_by(models.BuildChroot.build_id.asc()) 117 return query121 query = (models.BuildChroot.query.join(models.Build) 122 .filter(models.Build.canceled == false()) 123 .filter(or_( 124 models.BuildChroot.status == helpers.StatusEnum("pending"), 125 and_( 126 models.BuildChroot.status == helpers.StatusEnum("running"), 127 models.BuildChroot.started_on < int(time.time() - 1.1 * MAX_BUILD_TIMEOUT), 128 models.BuildChroot.ended_on.is_(None) 129 ) 130 )) 131 .filter(or_( 132 models.BuildChroot.last_deferred.is_(None), 133 models.BuildChroot.last_deferred < int(time.time() - DEFER_BUILD_SECONDS) 134 )) 135 ).order_by(models.Build.is_background.asc(), models.BuildChroot.build_id.asc()) 136 return query.first()137 138 @classmethod140 try: 141 build_id, chroot_name = task_id.split("-", 1) 142 except ValueError: 143 raise MalformedArgumentException("Invalid task_id {}".format(task_id)) 144 145 build_chroot = BuildChrootsLogic.get_by_build_id_and_name(build_id, chroot_name) 146 return build_chroot.join(models.Build).first()147 148 149 @classmethod 152 153 @classmethod155 """ Get collection of builds in copr sorted by build_id descending 156 """ 157 return cls.get_multiple().filter(models.Build.copr == copr)158 159 @classmethod161 """ Get collection of builds in copr sorted by build_id descending 162 form the copr belonging to `user` 163 """ 164 return cls.get_multiple().join(models.Build.copr).filter( 165 models.Copr.user == user)166 167 168 @classmethod170 if db.engine.url.drivername == "sqlite": 171 return 172 173 status_to_order = """ 174 CREATE OR REPLACE FUNCTION status_to_order (x integer) 175 RETURNS integer AS $$ BEGIN 176 RETURN CASE WHEN x = 0 THEN 0 177 WHEN x = 3 THEN 1 178 WHEN x = 6 THEN 2 179 WHEN x = 7 THEN 3 180 WHEN x = 4 THEN 4 181 WHEN x = 1 THEN 5 182 WHEN x = 5 THEN 6 183 ELSE 1000 184 END; END; 185 $$ LANGUAGE plpgsql; 186 """ 187 188 order_to_status = """ 189 CREATE OR REPLACE FUNCTION order_to_status (x integer) 190 RETURNS integer AS $$ BEGIN 191 RETURN CASE WHEN x = 0 THEN 0 192 WHEN x = 1 THEN 3 193 WHEN x = 2 THEN 6 194 WHEN x = 3 THEN 7 195 WHEN x = 4 THEN 4 196 WHEN x = 5 THEN 1 197 WHEN x = 6 THEN 5 198 ELSE 1000 199 END; END; 200 $$ LANGUAGE plpgsql; 201 """ 202 203 db.engine.connect() 204 db.engine.execute(status_to_order) 205 db.engine.execute(order_to_status)206 207 @classmethod209 query_select = """ 210 SELECT build.id, MAX(package.name) AS pkg_name, build.pkg_version, build.submitted_on, 211 MIN(statuses.started_on) AS started_on, MAX(statuses.ended_on) AS ended_on, order_to_status(MIN(statuses.st)) AS status, 212 build.canceled, MIN("group".name) AS group_name, MIN(copr.name) as copr_name, MIN("user".username) as user_name 213 FROM build 214 LEFT OUTER JOIN package 215 ON build.package_id = package.id 216 LEFT OUTER JOIN (SELECT build_chroot.build_id, started_on, ended_on, status_to_order(status) AS st FROM build_chroot) AS statuses 217 ON statuses.build_id=build.id 218 LEFT OUTER JOIN copr 219 ON copr.id = build.copr_id 220 LEFT OUTER JOIN "user" 221 ON copr.user_id = "user".id 222 LEFT OUTER JOIN "group" 223 ON copr.group_id = "group".id 224 WHERE build.copr_id = :copr_id 225 GROUP BY 226 build.id; 227 """ 228 229 if db.engine.url.drivername == "sqlite": 230 def sqlite_status_to_order(x): 231 if x == 3: 232 return 1 233 elif x == 6: 234 return 2 235 elif x == 7: 236 return 3 237 elif x == 4: 238 return 4 239 elif x == 0: 240 return 5 241 elif x == 1: 242 return 6 243 elif x == 5: 244 return 7 245 elif x == 8: 246 return 8 247 return 1000248 249 def sqlite_order_to_status(x): 250 if x == 1: 251 return 3 252 elif x == 2: 253 return 6 254 elif x == 3: 255 return 7 256 elif x == 4: 257 return 4 258 elif x == 5: 259 return 0 260 elif x == 6: 261 return 1 262 elif x == 7: 263 return 5 264 elif x == 8: 265 return 8 266 return 1000 267 268 conn = db.engine.connect() 269 conn.connection.create_function("status_to_order", 1, sqlite_status_to_order) 270 conn.connection.create_function("order_to_status", 1, sqlite_order_to_status) 271 statement = text(query_select) 272 statement.bindparams(bindparam("copr_id", Integer)) 273 result = conn.execute(statement, {"copr_id": copr.id}) 274 else: 275 statement = text(query_select) 276 statement.bindparams(bindparam("copr_id", Integer)) 277 result = db.engine.execute(statement, {"copr_id": copr.id}) 278 279 return result 280 281 @classmethod 284 285 @classmethod287 query = cls.get_multiple() 288 return (query.join(models.Build.copr) 289 .options(db.contains_eager(models.Build.copr)) 290 .join(models.Copr.user) 291 .filter(models.Copr.name == coprname) 292 .filter(models.User.username == username))293 294 @classmethod296 """ 297 Return builds that are waiting for dist git to import the sources. 298 """ 299 query = (models.Build.query.join(models.Build.copr) 300 .join(models.User) 301 .join(models.BuildChroot) 302 .options(db.contains_eager(models.Build.copr)) 303 .options(db.contains_eager("copr.user")) 304 .filter((models.BuildChroot.started_on == None) 305 | (models.BuildChroot.started_on < int(time.time() - 7200))) 306 .filter(models.BuildChroot.ended_on == None) 307 .filter(models.Build.canceled == False) 308 .order_by(models.Build.submitted_on.asc())) 309 return query310 311 @classmethod313 """ 314 Return builds that aren't both started and finished 315 (if build start submission fails, we still want to mark 316 the build as non-waiting, if it ended) 317 this has very different goal then get_multiple, so implement it alone 318 """ 319 320 query = (models.Build.query.join(models.Build.copr) 321 .join(models.User).join(models.BuildChroot) 322 .options(db.contains_eager(models.Build.copr)) 323 .options(db.contains_eager("copr.user")) 324 .filter((models.BuildChroot.started_on.is_(None)) 325 | (models.BuildChroot.started_on < int(time.time() - 7200))) 326 .filter(models.BuildChroot.ended_on.is_(None)) 327 .filter(models.Build.canceled == false()) 328 .order_by(models.Build.submitted_on.asc())) 329 return query330 331 @classmethod 334 335 @classmethod 338 339 @classmethod340 - def create_new_from_other_build(cls, user, copr, source_build, 341 chroot_names=None, **build_options):342 skip_import = False 343 git_hashes = {} 344 345 if source_build.source_type == helpers.BuildSourceEnum('upload'): 346 # I don't have the source 347 # so I don't want to import anything, just rebuild what's in dist git 348 skip_import = True 349 350 for chroot in source_build.build_chroots: 351 if not chroot.git_hash: 352 # I got an old build from time we didn't use dist git 353 # So I'll submit it as a new build using it's link 354 skip_import = False 355 git_hashes = None 356 flask.flash("This build is not in Dist Git. Trying to import the package again.") 357 break 358 git_hashes[chroot.name] = chroot.git_hash 359 360 build = cls.create_new(user, copr, source_build.source_type, source_build.source_json, chroot_names, 361 pkgs=source_build.pkgs, git_hashes=git_hashes, skip_import=skip_import, **build_options) 362 build.package_id = source_build.package_id 363 build.pkg_version = source_build.pkg_version 364 return build365 366 @classmethod369 """ 370 :type user: models.User 371 :type copr: models.Copr 372 373 :type chroot_names: List[str] 374 375 :rtype: models.Build 376 """ 377 source_type = helpers.BuildSourceEnum("link") 378 source_json = json.dumps({"url": url}) 379 return cls.create_new(user, copr, source_type, source_json, chroot_names, pkgs=url, **build_options)380 381 @classmethod382 - def create_new_from_tito(cls, user, copr, git_url, git_dir, git_branch, tito_test, 383 chroot_names=None, **build_options):384 """ 385 :type user: models.User 386 :type copr: models.Copr 387 388 :type chroot_names: List[str] 389 390 :rtype: models.Build 391 """ 392 source_type = helpers.BuildSourceEnum("git_and_tito") 393 source_json = json.dumps({"git_url": git_url, 394 "git_dir": git_dir, 395 "git_branch": git_branch, 396 "tito_test": tito_test}) 397 return cls.create_new(user, copr, source_type, source_json, chroot_names, **build_options)398 399 @classmethod400 - def create_new_from_mock(cls, user, copr, scm_type, scm_url, scm_branch, scm_subdir, spec, 401 chroot_names=None, **build_options):402 """ 403 :type user: models.User 404 :type copr: models.Copr 405 406 :type chroot_names: List[str] 407 408 :rtype: models.Build 409 """ 410 source_type = helpers.BuildSourceEnum("mock_scm") 411 source_json = json.dumps({"scm_type": scm_type, 412 "scm_url": scm_url, 413 "scm_branch": scm_branch, 414 "scm_subdir": scm_subdir, 415 "spec": spec}) 416 return cls.create_new(user, copr, source_type, source_json, chroot_names, **build_options)417 418 @classmethod419 - def create_new_from_pypi(cls, user, copr, pypi_package_name, pypi_package_version, python_versions, 420 chroot_names=None, **build_options):421 """ 422 :type user: models.User 423 :type copr: models.Copr 424 :type package_name: str 425 :type version: str 426 :type python_versions: List[str] 427 428 :type chroot_names: List[str] 429 430 :rtype: models.Build 431 """ 432 source_type = helpers.BuildSourceEnum("pypi") 433 source_json = json.dumps({"pypi_package_name": pypi_package_name, 434 "pypi_package_version": pypi_package_version, 435 "python_versions": python_versions}) 436 return cls.create_new(user, copr, source_type, source_json, chroot_names, **build_options)437 438 @classmethod439 - def create_new_from_rubygems(cls, user, copr, gem_name, 440 chroot_names=None, **build_options):441 """ 442 :type user: models.User 443 :type copr: models.Copr 444 :type gem_name: str 445 :type chroot_names: List[str] 446 :rtype: models.Build 447 """ 448 source_type = helpers.BuildSourceEnum("rubygems") 449 source_json = json.dumps({"gem_name": gem_name}) 450 return cls.create_new(user, copr, source_type, source_json, chroot_names, **build_options)451 452 @classmethod453 - def create_new_from_distgit(cls, user, copr, clone_url, branch, 454 chroot_names=None, **build_options):455 """ 456 :type user: models.User 457 :type copr: models.Copr 458 :type clone_url: str 459 :type branch: str 460 :type chroot_names: List[str] 461 :rtype: models.Build 462 """ 463 source_type = helpers.BuildSourceEnum("distgit") 464 source_json = json.dumps({ 465 "clone_url": clone_url, 466 "branch": branch 467 }) 468 return cls.create_new(user, copr, source_type, source_json, chroot_names, **build_options)469 470 @classmethod471 - def create_new_from_upload(cls, user, copr, f_uploader, orig_filename, 472 chroot_names=None, **build_options):473 """ 474 :type user: models.User 475 :type copr: models.Copr 476 :param f_uploader(file_path): function which stores data at the given `file_path` 477 :return: 478 """ 479 tmp = tempfile.mkdtemp(dir=app.config["SRPM_STORAGE_DIR"]) 480 tmp_name = os.path.basename(tmp) 481 filename = secure_filename(orig_filename) 482 file_path = os.path.join(tmp, filename) 483 f_uploader(file_path) 484 485 # make the pkg public 486 pkg_url = "{baseurl}/tmp/{tmp_dir}/{filename}".format( 487 baseurl=app.config["PUBLIC_COPR_BASE_URL"], 488 tmp_dir=tmp_name, 489 filename=filename) 490 491 # create json describing the build source 492 source_type = helpers.BuildSourceEnum("upload") 493 source_json = json.dumps({"url": pkg_url, "pkg": filename, "tmp": tmp_name}) 494 try: 495 build = cls.create_new(user, copr, source_type, source_json, 496 chroot_names, pkgs=pkg_url, **build_options) 497 except Exception: 498 shutil.rmtree(tmp) # todo: maybe we should delete in some cleanup procedure? 499 raise 500 501 return build502 503 @classmethod504 - def create_new(cls, user, copr, source_type, source_json, chroot_names=None, 505 pkgs="", git_hashes=None, skip_import=False, background=False, **build_options):506 """ 507 :type user: models.User 508 :type copr: models.Copr 509 :type chroot_names: List[str] 510 :type source_type: int value from helpers.BuildSourceEnum 511 :type source_json: str in json format 512 :type pkgs: str 513 :type git_hashes: dict 514 :type skip_import: bool 515 :type background: bool 516 :rtype: models.Build 517 """ 518 if chroot_names is None: 519 chroots = [c for c in copr.active_chroots] 520 else: 521 chroots = [] 522 for chroot in copr.active_chroots: 523 if chroot.name in chroot_names: 524 chroots.append(chroot) 525 526 build = cls.add( 527 user=user, 528 pkgs=pkgs, 529 copr=copr, 530 chroots=chroots, 531 source_type=source_type, 532 source_json=source_json, 533 enable_net=build_options.get("enable_net", copr.build_enable_net), 534 background=background, 535 git_hashes=git_hashes, 536 skip_import=skip_import) 537 538 if user.proven: 539 if "timeout" in build_options: 540 build.timeout = build_options["timeout"] 541 542 return build543 544 @classmethod545 - def add(cls, user, pkgs, copr, source_type=None, source_json=None, 546 repos=None, chroots=None, timeout=None, enable_net=True, 547 git_hashes=None, skip_import=False, background=False):548 if chroots is None: 549 chroots = [] 550 551 coprs_logic.CoprsLogic.raise_if_unfinished_blocking_action( 552 copr, "Can't build while there is an operation in progress: {action}") 553 users_logic.UsersLogic.raise_if_cant_build_in_copr( 554 user, copr, 555 "You don't have permissions to build in this copr.") 556 557 if not repos: 558 repos = copr.repos 559 560 # todo: eliminate pkgs and this check 561 if pkgs and (" " in pkgs or "\n" in pkgs or "\t" in pkgs or pkgs.strip() != pkgs): 562 raise exceptions.MalformedArgumentException("Trying to create a build using src_pkg " 563 "with bad characters. Forgot to split?") 564 565 # just temporary to keep compatibility 566 if not source_type or not source_json: 567 source_type = helpers.BuildSourceEnum("link") 568 source_json = json.dumps({"url":pkgs}) 569 570 build = models.Build( 571 user=user, 572 pkgs=pkgs, 573 copr=copr, 574 repos=repos, 575 source_type=source_type, 576 source_json=source_json, 577 submitted_on=int(time.time()), 578 enable_net=bool(enable_net), 579 is_background=bool(background), 580 ) 581 582 if timeout: 583 build.timeout = timeout or DEFAULT_BUILD_TIMEOUT 584 585 db.session.add(build) 586 587 # add BuildChroot object for each active (or selected) chroot 588 # this copr is assigned to 589 if not chroots: 590 chroots = copr.active_chroots 591 592 status = helpers.StatusEnum("importing") 593 594 if skip_import: 595 status = StatusEnum("pending") 596 597 for chroot in chroots: 598 git_hash = None 599 if git_hashes: 600 git_hash = git_hashes.get(chroot.name) 601 buildchroot = models.BuildChroot( 602 build=build, 603 status=status, 604 mock_chroot=chroot, 605 git_hash=git_hash) 606 607 db.session.add(buildchroot) 608 609 return build610 611 @classmethod613 build = models.Build( 614 user=None, 615 pkgs=None, 616 package_id=package.id, 617 copr=package.copr, 618 repos=package.copr.repos, 619 source_type=package.source_type, 620 source_json=package.source_json, 621 submitted_on=int(time.time()), 622 enable_net=package.copr.build_enable_net, 623 timeout=DEFAULT_BUILD_TIMEOUT 624 ) 625 626 db.session.add(build) 627 628 chroots = package.copr.active_chroots 629 630 status = helpers.StatusEnum("importing") 631 632 for chroot in chroots: 633 buildchroot = models.BuildChroot( 634 build=build, 635 status=status, 636 mock_chroot=chroot, 637 git_hash=None 638 ) 639 640 db.session.add(buildchroot) 641 642 return build643 644 645 terminal_states = {StatusEnum("failed"), StatusEnum("succeeded"), StatusEnum("canceled")} 646 647 @classmethod649 """ 650 Returns a list of BuildChroots identified by build_id and dist-git 651 branch name. 652 """ 653 return ( 654 models.BuildChroot.query 655 .join(models.MockChroot) 656 .filter(models.BuildChroot.build_id==build_id) 657 .filter(models.MockChroot.distgit_branch_name==branch) 658 ).all()659 660 661 @classmethod663 """ 664 Deletes the source (rpm or .spec) locally stored for upload (if exists) 665 """ 666 # is it hosted on the copr frontend? 667 if build.source_type == helpers.BuildSourceEnum("upload"): 668 data = json.loads(build.source_json) 669 tmp = data["tmp"] 670 storage_path = app.config["SRPM_STORAGE_DIR"] 671 try: 672 shutil.rmtree(os.path.join(storage_path, tmp)) 673 except: 674 pass675 676 677 @classmethod679 """ 680 :param build: 681 :param upd_dict: 682 example: 683 { 684 "builds":[ 685 { 686 "id": 1, 687 "copr_id": 2, 688 "started_on": 139086644000 689 }, 690 { 691 "id": 2, 692 "copr_id": 1, 693 "status": 0, 694 "chroot": "fedora-18-x86_64", 695 "results": "http://server/results/foo/bar/", 696 "ended_on": 139086644000 697 }] 698 } 699 """ 700 log.info("Updating build: {} by: {}".format(build.id, upd_dict)) 701 if "chroot" in upd_dict: 702 # update respective chroot status 703 for build_chroot in build.build_chroots: 704 if build_chroot.name == upd_dict["chroot"]: 705 706 if "status" in upd_dict and build_chroot.status not in BuildsLogic.terminal_states: 707 build_chroot.status = upd_dict["status"] 708 709 if upd_dict.get("status") in BuildsLogic.terminal_states: 710 build_chroot.ended_on = upd_dict.get("ended_on") or time.time() 711 712 if upd_dict.get("status") == StatusEnum("starting"): 713 build_chroot.started_on = upd_dict.get("started_on") or time.time() 714 715 if "last_deferred" in upd_dict: 716 build_chroot.last_deferred = upd_dict["last_deferred"] 717 718 db.session.add(build_chroot) 719 720 for attr in ["results", "built_packages"]: 721 value = upd_dict.get(attr, None) 722 if value: 723 setattr(build, attr, value) 724 725 db.session.add(build)726 727 @classmethod729 if not user.can_build_in(build.copr): 730 raise exceptions.InsufficientRightsException( 731 "You are not allowed to cancel this build.") 732 if not build.cancelable: 733 if build.status == StatusEnum("starting"): 734 err_msg = "Cannot cancel build {} in state 'starting'".format(build.id) 735 else: 736 err_msg = "Cannot cancel build {}".format(build.id) 737 raise exceptions.RequestCannotBeExecuted(err_msg) 738 739 if build.status == StatusEnum("running"): # otherwise the build is just in frontend 740 ActionsLogic.send_cancel_build(build) 741 742 build.canceled = True 743 for chroot in build.build_chroots: 744 chroot.status = 2 # canceled 745 if chroot.ended_on is not None: 746 chroot.ended_on = time.time()747 748 @classmethod750 """ 751 :type user: models.User 752 :type build: models.Build 753 """ 754 if not user.can_edit(build.copr) or build.persistent: 755 raise exceptions.InsufficientRightsException( 756 "You are not allowed to delete build `{}`.".format(build.id)) 757 758 if not build.finished: 759 # from celery.contrib import rdb; rdb.set_trace() 760 raise exceptions.ActionInProgressException( 761 "You can not delete build `{}` which is not finished.".format(build.id), 762 "Unfinished build") 763 764 if send_delete_action: 765 ActionsLogic.send_delete_build(build) 766 767 for build_chroot in build.build_chroots: 768 db.session.delete(build_chroot) 769 db.session.delete(build)770 771 @classmethod773 """ 774 Marks build as failed on all its non-finished chroots 775 """ 776 build = cls.get(build_id).one() 777 chroots = filter(lambda x: x.status != helpers.StatusEnum("succeeded"), build.build_chroots) 778 for chroot in chroots: 779 chroot.status = helpers.StatusEnum("failed") 780 return build781 782 @classmethod784 """ Get build datetime (as epoch) of last successful build 785 786 :arg copr: object of copr 787 """ 788 builds = cls.get_multiple_by_copr(copr) 789 790 last_build = ( 791 builds.join(models.BuildChroot) 792 .filter((models.BuildChroot.status == helpers.StatusEnum("succeeded")) 793 | (models.BuildChroot.status == helpers.StatusEnum("skipped"))) 794 .filter(models.BuildChroot.ended_on.isnot(None)) 795 .order_by(models.BuildChroot.ended_on.desc()) 796 ).first() 797 if last_build: 798 return last_build.ended_on 799 else: 800 return None801 802 @classmethod804 # todo: check that ended_on is set correctly for all cases 805 # e.g.: failed dist-git import, cancellation 806 if is_finished: 807 return query.join(models.BuildChroot).filter(models.BuildChroot.ended_on.isnot(None)) 808 else: 809 return query.join(models.BuildChroot).filter(models.BuildChroot.ended_on.is_(None))810 811 @classmethod 814817 @classmethod858819 mc = MockChrootsLogic.get_from_name(name).one() 820 821 return ( 822 BuildChroot.query 823 .filter(BuildChroot.build_id == build_id) 824 .filter(BuildChroot.mock_chroot_id == mc.id) 825 )826 827 @classmethod829 query = ( 830 models.BuildChroot.query 831 .join(models.BuildChroot.build) 832 .join(models.BuildChroot.mock_chroot) 833 .join(models.Build.copr) 834 .join(models.Copr.user) 835 .outerjoin(models.Group) 836 ) 837 return query838 839 @classmethod 842 843 @classmethod 846 847 @classmethod 850 851 @classmethod 854 855 @classmethod861 @classmethod898863 query = """ 864 SELECT 865 package.id as package_id, 866 package.name AS package_name, 867 build.id AS build_id, 868 build_chroot.status AS build_chroot_status, 869 build.pkg_version AS build_pkg_version, 870 mock_chroot.id AS mock_chroot_id, 871 mock_chroot.os_release AS mock_chroot_os_release, 872 mock_chroot.os_version AS mock_chroot_os_version, 873 mock_chroot.arch AS mock_chroot_arch 874 FROM package 875 JOIN (SELECT 876 MAX(build.id) AS max_build_id_for_chroot, 877 build.package_id AS package_id, 878 build_chroot.mock_chroot_id AS mock_chroot_id 879 FROM build 880 JOIN build_chroot 881 ON build.id = build_chroot.build_id 882 WHERE build.copr_id = {copr_id} 883 AND build_chroot.status != 2 884 GROUP BY build.package_id, 885 build_chroot.mock_chroot_id) AS max_build_ids_for_a_chroot 886 ON package.id = max_build_ids_for_a_chroot.package_id 887 JOIN build 888 ON build.id = max_build_ids_for_a_chroot.max_build_id_for_chroot 889 JOIN build_chroot 890 ON build_chroot.mock_chroot_id = max_build_ids_for_a_chroot.mock_chroot_id 891 AND build_chroot.build_id = max_build_ids_for_a_chroot.max_build_id_for_chroot 892 JOIN mock_chroot 893 ON mock_chroot.id = max_build_ids_for_a_chroot.mock_chroot_id 894 ORDER BY package.name ASC, package.id ASC, mock_chroot.os_release ASC, mock_chroot.os_version ASC, mock_chroot.arch ASC 895 """.format(copr_id=copr.id) 896 rows = db.session.execute(query) 897 return rows
Trees | Indices | Help |
---|
Generated by Epydoc 3.0.1 on Wed Aug 2 13:52:11 2017 | http://epydoc.sourceforge.net |