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 @classmethod117 118 @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 BuildChroots which are waiting to be uploaded to dist git 79 """ 80 query = (models.BuildChroot.query.join(models.Build) 81 .filter(models.Build.canceled == false()) 82 .filter(models.BuildChroot.status == helpers.StatusEnum("importing"))) 83 query = query.order_by(models.BuildChroot.build_id.asc()) 84 return query85 86 @classmethod 88 """ 89 Returns BuildChroots which are - waiting to be built or 90 - older than 2 hours and unfinished 91 """ 92 # todo: filter out build without package 93 query = (models.BuildChroot.query.join(models.Build) 94 .filter(models.Build.canceled == false()) 95 .filter(models.Build.is_background == (true() if is_background else false())) 96 .filter(or_( 97 models.BuildChroot.status == helpers.StatusEnum("pending"), 98 models.BuildChroot.status == helpers.StatusEnum("starting"), 99 and_( 100 # We are moving ended_on to the BuildChroot, now it should be reliable, 101 # so we don't want to reschedule failed chroots 102 # models.BuildChroot.status.in_([ 103 # # Bug 1206562 - Cannot delete Copr because it incorrectly thinks 104 # # there are unfinished builds. Solution: `failed` but unfinished 105 # # (ended_on is null) builds should be rescheduled. 106 # # todo: we need to be sure that correct `failed` set is set together wtih `ended_on` 107 # helpers.StatusEnum("running"), 108 # helpers.StatusEnum("failed") 109 #]), 110 models.BuildChroot.status == helpers.StatusEnum("running"), 111 models.BuildChroot.started_on < int(time.time() - 1.1 * MAX_BUILD_TIMEOUT), 112 models.BuildChroot.ended_on.is_(None) 113 )) 114 )) 115 query = query.order_by(models.BuildChroot.build_id.asc()) 116 return query120 query = (models.BuildChroot.query.join(models.Build) 121 .filter(models.Build.canceled == false()) 122 .filter(or_( 123 models.BuildChroot.status == helpers.StatusEnum("pending"), 124 and_( 125 models.BuildChroot.status == helpers.StatusEnum("running"), 126 models.BuildChroot.started_on < int(time.time() - 1.1 * MAX_BUILD_TIMEOUT), 127 models.BuildChroot.ended_on.is_(None) 128 ) 129 )) 130 .filter(or_( 131 models.BuildChroot.last_deferred.is_(None), 132 models.BuildChroot.last_deferred < int(time.time() - DEFER_BUILD_SECONDS) 133 )) 134 ).order_by(models.Build.is_background.asc(), models.BuildChroot.build_id.asc()) 135 return query.first()136 137 @classmethod 140 141 @classmethod143 """ Get collection of builds in copr sorted by build_id descending 144 """ 145 return cls.get_multiple().filter(models.Build.copr == copr)146 147 @classmethod149 """ Get collection of builds in copr sorted by build_id descending 150 form the copr belonging to `user` 151 """ 152 return cls.get_multiple().join(models.Build.copr).filter( 153 models.Copr.user == user)154 155 156 @classmethod158 if db.engine.url.drivername == "sqlite": 159 return 160 161 status_to_order = """ 162 CREATE OR REPLACE FUNCTION status_to_order (x integer) 163 RETURNS integer AS $$ BEGIN 164 RETURN CASE WHEN x = 0 THEN 0 165 WHEN x = 3 THEN 1 166 WHEN x = 6 THEN 2 167 WHEN x = 7 THEN 3 168 WHEN x = 4 THEN 4 169 WHEN x = 1 THEN 5 170 WHEN x = 5 THEN 6 171 ELSE 1000 172 END; END; 173 $$ LANGUAGE plpgsql; 174 """ 175 176 order_to_status = """ 177 CREATE OR REPLACE FUNCTION order_to_status (x integer) 178 RETURNS integer AS $$ BEGIN 179 RETURN CASE WHEN x = 0 THEN 0 180 WHEN x = 1 THEN 3 181 WHEN x = 2 THEN 6 182 WHEN x = 3 THEN 7 183 WHEN x = 4 THEN 4 184 WHEN x = 5 THEN 1 185 WHEN x = 6 THEN 5 186 ELSE 1000 187 END; END; 188 $$ LANGUAGE plpgsql; 189 """ 190 191 db.engine.connect() 192 db.engine.execute(status_to_order) 193 db.engine.execute(order_to_status)194 195 @classmethod197 query_select = """ 198 SELECT build.id, MAX(package.name) AS pkg_name, build.pkg_version, build.submitted_on, 199 MIN(statuses.started_on) AS started_on, MAX(statuses.ended_on) AS ended_on, order_to_status(MIN(statuses.st)) AS status, 200 build.canceled, MIN("group".name) AS group_name, MIN(copr.name) as copr_name, MIN("user".username) as user_name 201 FROM build 202 LEFT OUTER JOIN package 203 ON build.package_id = package.id 204 LEFT OUTER JOIN (SELECT build_chroot.build_id, started_on, ended_on, status_to_order(status) AS st FROM build_chroot) AS statuses 205 ON statuses.build_id=build.id 206 LEFT OUTER JOIN copr 207 ON copr.id = build.copr_id 208 LEFT OUTER JOIN "user" 209 ON copr.user_id = "user".id 210 LEFT OUTER JOIN "group" 211 ON copr.group_id = "group".id 212 WHERE build.copr_id = :copr_id 213 GROUP BY 214 build.id; 215 """ 216 217 if db.engine.url.drivername == "sqlite": 218 def sqlite_status_to_order(x): 219 if x == 3: 220 return 1 221 elif x == 6: 222 return 2 223 elif x == 7: 224 return 3 225 elif x == 4: 226 return 4 227 elif x == 0: 228 return 5 229 elif x == 1: 230 return 6 231 elif x == 5: 232 return 7 233 elif x == 8: 234 return 8 235 return 1000236 237 def sqlite_order_to_status(x): 238 if x == 1: 239 return 3 240 elif x == 2: 241 return 6 242 elif x == 3: 243 return 7 244 elif x == 4: 245 return 4 246 elif x == 5: 247 return 0 248 elif x == 6: 249 return 1 250 elif x == 7: 251 return 5 252 elif x == 8: 253 return 8 254 return 1000 255 256 conn = db.engine.connect() 257 conn.connection.create_function("status_to_order", 1, sqlite_status_to_order) 258 conn.connection.create_function("order_to_status", 1, sqlite_order_to_status) 259 statement = text(query_select) 260 statement.bindparams(bindparam("copr_id", Integer)) 261 result = conn.execute(statement, {"copr_id": copr.id}) 262 else: 263 statement = text(query_select) 264 statement.bindparams(bindparam("copr_id", Integer)) 265 result = db.engine.execute(statement, {"copr_id": copr.id}) 266 267 return result 268 269 @classmethod 272 273 @classmethod275 query = cls.get_multiple() 276 return (query.join(models.Build.copr) 277 .options(db.contains_eager(models.Build.copr)) 278 .join(models.Copr.user) 279 .filter(models.Copr.name == coprname) 280 .filter(models.User.username == username))281 282 @classmethod284 """ 285 Return builds that are waiting for dist git to import the sources. 286 """ 287 query = (models.Build.query.join(models.Build.copr) 288 .join(models.User) 289 .join(models.BuildChroot) 290 .options(db.contains_eager(models.Build.copr)) 291 .options(db.contains_eager("copr.user")) 292 .filter((models.BuildChroot.started_on == None) 293 | (models.BuildChroot.started_on < int(time.time() - 7200))) 294 .filter(models.BuildChroot.ended_on == None) 295 .filter(models.Build.canceled == False) 296 .order_by(models.Build.submitted_on.asc())) 297 return query298 299 @classmethod301 """ 302 Return builds that aren't both started and finished 303 (if build start submission fails, we still want to mark 304 the build as non-waiting, if it ended) 305 this has very different goal then get_multiple, so implement it alone 306 """ 307 308 query = (models.Build.query.join(models.Build.copr) 309 .join(models.User).join(models.BuildChroot) 310 .options(db.contains_eager(models.Build.copr)) 311 .options(db.contains_eager("copr.user")) 312 .filter((models.BuildChroot.started_on.is_(None)) 313 | (models.BuildChroot.started_on < int(time.time() - 7200))) 314 .filter(models.BuildChroot.ended_on.is_(None)) 315 .filter(models.Build.canceled == false()) 316 .order_by(models.Build.submitted_on.asc())) 317 return query318 319 @classmethod 322 323 @classmethod 326 327 @classmethod328 - def create_new_from_other_build(cls, user, copr, source_build, 329 chroot_names=None, **build_options):330 skip_import = False 331 git_hashes = {} 332 333 if source_build.source_type == helpers.BuildSourceEnum('srpm_upload'): 334 # I don't have the source 335 # so I don't want to import anything, just rebuild what's in dist git 336 skip_import = True 337 338 for chroot in source_build.build_chroots: 339 if not chroot.git_hash: 340 # I got an old build from time we didn't use dist git 341 # So I'll submit it as a new build using it's link 342 skip_import = False 343 git_hashes = None 344 flask.flash("This build is not in Dist Git. Trying to import the package again.") 345 break 346 git_hashes[chroot.name] = chroot.git_hash 347 348 build = cls.create_new(user, copr, source_build.source_type, source_build.source_json, chroot_names, 349 pkgs=source_build.pkgs, git_hashes=git_hashes, skip_import=skip_import, **build_options) 350 build.package_id = source_build.package_id 351 build.pkg_version = source_build.pkg_version 352 return build353 354 @classmethod357 """ 358 :type user: models.User 359 :type copr: models.Copr 360 361 :type chroot_names: List[str] 362 363 :rtype: models.Build 364 """ 365 source_type = helpers.BuildSourceEnum("srpm_link") 366 source_json = json.dumps({"url": srpm_url}) 367 return cls.create_new(user, copr, source_type, source_json, chroot_names, pkgs=srpm_url, **build_options)368 369 @classmethod370 - def create_new_from_tito(cls, user, copr, git_url, git_dir, git_branch, tito_test, 371 chroot_names=None, **build_options):372 """ 373 :type user: models.User 374 :type copr: models.Copr 375 376 :type chroot_names: List[str] 377 378 :rtype: models.Build 379 """ 380 source_type = helpers.BuildSourceEnum("git_and_tito") 381 source_json = json.dumps({"git_url": git_url, 382 "git_dir": git_dir, 383 "git_branch": git_branch, 384 "tito_test": tito_test}) 385 return cls.create_new(user, copr, source_type, source_json, chroot_names, **build_options)386 387 @classmethod388 - def create_new_from_mock(cls, user, copr, scm_type, scm_url, scm_branch, spec, 389 chroot_names=None, **build_options):390 """ 391 :type user: models.User 392 :type copr: models.Copr 393 394 :type chroot_names: List[str] 395 396 :rtype: models.Build 397 """ 398 source_type = helpers.BuildSourceEnum("mock_scm") 399 source_json = json.dumps({"scm_type": scm_type, 400 "scm_url": scm_url, 401 "scm_branch": scm_branch, 402 "spec": spec}) 403 return cls.create_new(user, copr, source_type, source_json, chroot_names, **build_options)404 405 @classmethod406 - def create_new_from_pypi(cls, user, copr, pypi_package_name, pypi_package_version, python_versions, 407 chroot_names=None, **build_options):408 """ 409 :type user: models.User 410 :type copr: models.Copr 411 :type package_name: str 412 :type version: str 413 :type python_versions: List[str] 414 415 :type chroot_names: List[str] 416 417 :rtype: models.Build 418 """ 419 source_type = helpers.BuildSourceEnum("pypi") 420 source_json = json.dumps({"pypi_package_name": pypi_package_name, 421 "pypi_package_version": pypi_package_version, 422 "python_versions": python_versions}) 423 return cls.create_new(user, copr, source_type, source_json, chroot_names, **build_options)424 425 @classmethod426 - def create_new_from_rubygems(cls, user, copr, gem_name, 427 chroot_names=None, **build_options):428 """ 429 :type user: models.User 430 :type copr: models.Copr 431 :type gem_name: str 432 :type chroot_names: List[str] 433 :rtype: models.Build 434 """ 435 source_type = helpers.BuildSourceEnum("rubygems") 436 source_json = json.dumps({"gem_name": gem_name}) 437 return cls.create_new(user, copr, source_type, source_json, chroot_names, **build_options)438 439 @classmethod440 - def create_new_from_distgit(cls, user, copr, clone_url, branch, 441 chroot_names=None, **build_options):442 """ 443 :type user: models.User 444 :type copr: models.Copr 445 :type clone_url: str 446 :type branch: str 447 :type chroot_names: List[str] 448 :rtype: models.Build 449 """ 450 source_type = helpers.BuildSourceEnum("distgit") 451 source_json = json.dumps({ 452 "clone_url": clone_url, 453 "branch": branch 454 }) 455 return cls.create_new(user, copr, source_type, source_json, chroot_names, **build_options)456 457 @classmethod458 - def create_new_from_upload(cls, user, copr, f_uploader, orig_filename, 459 chroot_names=None, **build_options):460 """ 461 :type user: models.User 462 :type copr: models.Copr 463 :param f_uploader(file_path): function which stores data at the given `file_path` 464 :return: 465 """ 466 tmp = tempfile.mkdtemp(dir=app.config["SRPM_STORAGE_DIR"]) 467 tmp_name = os.path.basename(tmp) 468 filename = secure_filename(orig_filename) 469 file_path = os.path.join(tmp, filename) 470 f_uploader(file_path) 471 472 # make the pkg public 473 pkg_url = "https://{hostname}/tmp/{tmp_dir}/{srpm}".format( 474 hostname=app.config["PUBLIC_COPR_HOSTNAME"], 475 tmp_dir=tmp_name, 476 srpm=filename) 477 478 # create json describing the build source 479 source_type = helpers.BuildSourceEnum("srpm_upload") 480 source_json = json.dumps({"tmp": tmp_name, "pkg": filename}) 481 try: 482 build = cls.create_new(user, copr, source_type, source_json, 483 chroot_names, pkgs=pkg_url, **build_options) 484 except Exception: 485 shutil.rmtree(tmp) # todo: maybe we should delete in some cleanup procedure? 486 raise 487 488 return build489 490 @classmethod491 - def create_new(cls, user, copr, source_type, source_json, chroot_names=None, 492 pkgs="", git_hashes=None, skip_import=False, background=False, **build_options):493 """ 494 :type user: models.User 495 :type copr: models.Copr 496 :type chroot_names: List[str] 497 :type source_type: int value from helpers.BuildSourceEnum 498 :type source_json: str in json format 499 :type pkgs: str 500 :type git_hashes: dict 501 :type skip_import: bool 502 :type background: bool 503 :rtype: models.Build 504 """ 505 if chroot_names is None: 506 chroots = [c for c in copr.active_chroots] 507 else: 508 chroots = [] 509 for chroot in copr.active_chroots: 510 if chroot.name in chroot_names: 511 chroots.append(chroot) 512 513 build = cls.add( 514 user=user, 515 pkgs=pkgs, 516 copr=copr, 517 chroots=chroots, 518 source_type=source_type, 519 source_json=source_json, 520 enable_net=build_options.get("enable_net", copr.build_enable_net), 521 background=background, 522 git_hashes=git_hashes, 523 skip_import=skip_import) 524 525 if user.proven: 526 if "timeout" in build_options: 527 build.timeout = build_options["timeout"] 528 529 return build530 531 @classmethod532 - def add(cls, user, pkgs, copr, source_type=None, source_json=None, 533 repos=None, chroots=None, timeout=None, enable_net=True, 534 git_hashes=None, skip_import=False, background=False):535 if chroots is None: 536 chroots = [] 537 538 coprs_logic.CoprsLogic.raise_if_unfinished_blocking_action( 539 copr, "Can't build while there is an operation in progress: {action}") 540 users_logic.UsersLogic.raise_if_cant_build_in_copr( 541 user, copr, 542 "You don't have permissions to build in this copr.") 543 544 if not repos: 545 repos = copr.repos 546 547 # todo: eliminate pkgs and this check 548 if pkgs and (" " in pkgs or "\n" in pkgs or "\t" in pkgs or pkgs.strip() != pkgs): 549 raise exceptions.MalformedArgumentException("Trying to create a build using src_pkg " 550 "with bad characters. Forgot to split?") 551 552 # just temporary to keep compatibility 553 if not source_type or not source_json: 554 source_type = helpers.BuildSourceEnum("srpm_link") 555 source_json = json.dumps({"url":pkgs}) 556 557 build = models.Build( 558 user=user, 559 pkgs=pkgs, 560 copr=copr, 561 repos=repos, 562 source_type=source_type, 563 source_json=source_json, 564 submitted_on=int(time.time()), 565 enable_net=bool(enable_net), 566 is_background=bool(background), 567 ) 568 569 if timeout: 570 build.timeout = timeout or DEFAULT_BUILD_TIMEOUT 571 572 db.session.add(build) 573 574 # add BuildChroot object for each active (or selected) chroot 575 # this copr is assigned to 576 if not chroots: 577 chroots = copr.active_chroots 578 579 status = helpers.StatusEnum("importing") 580 581 if skip_import: 582 status = StatusEnum("pending") 583 584 for chroot in chroots: 585 git_hash = None 586 if git_hashes: 587 git_hash = git_hashes.get(chroot.name) 588 buildchroot = models.BuildChroot( 589 build=build, 590 status=status, 591 mock_chroot=chroot, 592 git_hash=git_hash) 593 594 db.session.add(buildchroot) 595 596 return build597 598 @classmethod600 build = models.Build( 601 user=None, 602 pkgs=None, 603 package_id=package.id, 604 copr=package.copr, 605 repos=package.copr.repos, 606 source_type=package.source_type, 607 source_json=package.source_json, 608 submitted_on=int(time.time()), 609 enable_net=package.enable_net, 610 timeout=DEFAULT_BUILD_TIMEOUT 611 ) 612 613 db.session.add(build) 614 615 chroots = package.copr.active_chroots 616 617 status = helpers.StatusEnum("importing") 618 619 for chroot in chroots: 620 buildchroot = models.BuildChroot( 621 build=build, 622 status=status, 623 mock_chroot=chroot, 624 git_hash=None 625 ) 626 627 db.session.add(buildchroot) 628 629 return build630 631 632 terminal_states = {StatusEnum("failed"), StatusEnum("succeeded"), StatusEnum("canceled")} 633 634 @classmethod636 """ 637 Returns a list of BuildChroots identified with task_id 638 task_id consists of a name of git branch + build id 639 Example: 42-f22 -> build id 42, chroots fedora-22-* 640 """ 641 build_id, branch = task_id.split("-") 642 build = cls.get_by_id(build_id).one() 643 build_chroots = build.build_chroots 644 os, version = helpers.branch_to_os_version(branch) 645 chroot_halfname = "{}-{}".format(os, version) 646 matching = [ch for ch in build_chroots if chroot_halfname in ch.name] 647 return matching648 649 650 @classmethod652 """ 653 Deletes the source rpm locally stored for upload (if exists) 654 """ 655 # is it hosted on the copr frontend? 656 if build.source_type == helpers.BuildSourceEnum("srpm_upload"): 657 data = json.loads(build.source_json) 658 tmp = data["tmp"] 659 storage_path = app.config["SRPM_STORAGE_DIR"] 660 try: 661 shutil.rmtree(os.path.join(storage_path, tmp)) 662 except: 663 pass664 665 666 @classmethod668 """ 669 :param build: 670 :param upd_dict: 671 example: 672 { 673 "builds":[ 674 { 675 "id": 1, 676 "copr_id": 2, 677 "started_on": 139086644000 678 }, 679 { 680 "id": 2, 681 "copr_id": 1, 682 "status": 0, 683 "chroot": "fedora-18-x86_64", 684 "results": "http://server/results/foo/bar/", 685 "ended_on": 139086644000 686 }] 687 } 688 """ 689 log.info("Updating build: {} by: {}".format(build.id, upd_dict)) 690 if "chroot" in upd_dict: 691 # update respective chroot status 692 for build_chroot in build.build_chroots: 693 if build_chroot.name == upd_dict["chroot"]: 694 695 if "status" in upd_dict and build_chroot.status not in BuildsLogic.terminal_states: 696 build_chroot.status = upd_dict["status"] 697 698 if upd_dict.get("status") in BuildsLogic.terminal_states: 699 build_chroot.ended_on = upd_dict.get("ended_on") or time.time() 700 701 if upd_dict.get("status") == StatusEnum("starting"): 702 build_chroot.started_on = upd_dict.get("started_on") or time.time() 703 704 if "last_deferred" in upd_dict: 705 build_chroot.last_deferred = upd_dict["last_deferred"] 706 707 db.session.add(build_chroot) 708 709 for attr in ["results", "built_packages"]: 710 value = upd_dict.get(attr, None) 711 if value: 712 setattr(build, attr, value) 713 714 db.session.add(build)715 716 @classmethod718 if not user.can_build_in(build.copr): 719 raise exceptions.InsufficientRightsException( 720 "You are not allowed to cancel this build.") 721 if not build.cancelable: 722 if build.status == StatusEnum("starting"): 723 err_msg = "Cannot cancel build {} in state 'starting'".format(build.id) 724 else: 725 err_msg = "Cannot cancel build {}".format(build.id) 726 raise exceptions.RequestCannotBeExecuted(err_msg) 727 728 if build.status == StatusEnum("running"): # otherwise the build is just in frontend 729 ActionsLogic.send_cancel_build(build) 730 731 build.canceled = True 732 for chroot in build.build_chroots: 733 chroot.status = 2 # canceled 734 if chroot.ended_on is not None: 735 chroot.ended_on = time.time()736 737 @classmethod739 """ 740 :type user: models.User 741 :type build: models.Build 742 """ 743 if not user.can_edit(build.copr) or build.persistent: 744 raise exceptions.InsufficientRightsException( 745 "You are not allowed to delete build `{}`.".format(build.id)) 746 747 if not build.finished: 748 # from celery.contrib import rdb; rdb.set_trace() 749 raise exceptions.ActionInProgressException( 750 "You can not delete build `{}` which is not finished.".format(build.id), 751 "Unfinished build") 752 753 if send_delete_action and build.state not in ["canceled"]: # cancelled builds should have nothing in backend to delete 754 ActionsLogic.send_delete_build(build) 755 756 for build_chroot in build.build_chroots: 757 db.session.delete(build_chroot) 758 db.session.delete(build)759 760 @classmethod762 """ 763 Marks build as failed on all its non-finished chroots 764 """ 765 build = cls.get(build_id).one() 766 chroots = filter(lambda x: x.status != helpers.StatusEnum("succeeded"), build.build_chroots) 767 for chroot in chroots: 768 chroot.status = helpers.StatusEnum("failed") 769 return build770 771 @classmethod773 """ Get build datetime (as epoch) of last successful build 774 775 :arg copr: object of copr 776 """ 777 builds = cls.get_multiple_by_copr(copr) 778 779 last_build = ( 780 builds.join(models.BuildChroot) 781 .filter((models.BuildChroot.status == helpers.StatusEnum("succeeded")) 782 | (models.BuildChroot.status == helpers.StatusEnum("skipped"))) 783 .filter(models.BuildChroot.ended_on.isnot(None)) 784 .order_by(models.BuildChroot.ended_on.desc()) 785 ).first() 786 if last_build: 787 return last_build.ended_on 788 else: 789 return None790 791 @classmethod793 # todo: check that ended_on is set correctly for all cases 794 # e.g.: failed dist-git import, cancellation 795 if is_finished: 796 return query.join(models.BuildChroot).filter(models.BuildChroot.ended_on.isnot(None)) 797 else: 798 return query.join(models.BuildChroot).filter(models.BuildChroot.ended_on.is_(None))799 800 @classmethod 803806 @classmethod847808 mc = MockChrootsLogic.get_from_name(name).one() 809 810 return ( 811 BuildChroot.query 812 .filter(BuildChroot.build_id == build_id) 813 .filter(BuildChroot.mock_chroot_id == mc.id) 814 )815 816 @classmethod818 query = ( 819 models.BuildChroot.query 820 .join(models.BuildChroot.build) 821 .join(models.BuildChroot.mock_chroot) 822 .join(models.Build.copr) 823 .join(models.Copr.user) 824 .outerjoin(models.Group) 825 ) 826 return query827 828 @classmethod 831 832 @classmethod 835 836 @classmethod 839 840 @classmethod 843 844 @classmethod850 @classmethod887852 query = """ 853 SELECT 854 package.id as package_id, 855 package.name AS package_name, 856 build.id AS build_id, 857 build_chroot.status AS build_chroot_status, 858 build.pkg_version AS build_pkg_version, 859 mock_chroot.id AS mock_chroot_id, 860 mock_chroot.os_release AS mock_chroot_os_release, 861 mock_chroot.os_version AS mock_chroot_os_version, 862 mock_chroot.arch AS mock_chroot_arch 863 FROM package 864 JOIN (SELECT 865 MAX(build.id) AS max_build_id_for_chroot, 866 build.package_id AS package_id, 867 build_chroot.mock_chroot_id AS mock_chroot_id 868 FROM build 869 JOIN build_chroot 870 ON build.id = build_chroot.build_id 871 WHERE build.copr_id = {copr_id} 872 AND build_chroot.status != 2 873 GROUP BY build.package_id, 874 build_chroot.mock_chroot_id) AS max_build_ids_for_a_chroot 875 ON package.id = max_build_ids_for_a_chroot.package_id 876 JOIN build 877 ON build.id = max_build_ids_for_a_chroot.max_build_id_for_chroot 878 JOIN build_chroot 879 ON build_chroot.mock_chroot_id = max_build_ids_for_a_chroot.mock_chroot_id 880 AND build_chroot.build_id = max_build_ids_for_a_chroot.max_build_id_for_chroot 881 JOIN mock_chroot 882 ON mock_chroot.id = max_build_ids_for_a_chroot.mock_chroot_id 883 ORDER BY package.name ASC, package.id ASC, mock_chroot.os_release ASC, mock_chroot.os_version ASC, mock_chroot.arch ASC 884 """.format(copr_id=copr.id) 885 rows = db.session.execute(query) 886 return rows
Trees | Indices | Help |
---|
Generated by Epydoc 3.0.1 on Mon Dec 5 22:37:25 2016 | http://epydoc.sourceforge.net |