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('srpm_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("srpm_link") 378 source_json = json.dumps({"url": srpm_url}) 379 return cls.create_new(user, copr, source_type, source_json, chroot_names, pkgs=srpm_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, 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 "spec": spec}) 415 return cls.create_new(user, copr, source_type, source_json, chroot_names, **build_options)416 417 @classmethod418 - def create_new_from_pypi(cls, user, copr, pypi_package_name, pypi_package_version, python_versions, 419 chroot_names=None, **build_options):420 """ 421 :type user: models.User 422 :type copr: models.Copr 423 :type package_name: str 424 :type version: str 425 :type python_versions: List[str] 426 427 :type chroot_names: List[str] 428 429 :rtype: models.Build 430 """ 431 source_type = helpers.BuildSourceEnum("pypi") 432 source_json = json.dumps({"pypi_package_name": pypi_package_name, 433 "pypi_package_version": pypi_package_version, 434 "python_versions": python_versions}) 435 return cls.create_new(user, copr, source_type, source_json, chroot_names, **build_options)436 437 @classmethod438 - def create_new_from_rubygems(cls, user, copr, gem_name, 439 chroot_names=None, **build_options):440 """ 441 :type user: models.User 442 :type copr: models.Copr 443 :type gem_name: str 444 :type chroot_names: List[str] 445 :rtype: models.Build 446 """ 447 source_type = helpers.BuildSourceEnum("rubygems") 448 source_json = json.dumps({"gem_name": gem_name}) 449 return cls.create_new(user, copr, source_type, source_json, chroot_names, **build_options)450 451 @classmethod452 - def create_new_from_distgit(cls, user, copr, clone_url, branch, 453 chroot_names=None, **build_options):454 """ 455 :type user: models.User 456 :type copr: models.Copr 457 :type clone_url: str 458 :type branch: str 459 :type chroot_names: List[str] 460 :rtype: models.Build 461 """ 462 source_type = helpers.BuildSourceEnum("distgit") 463 source_json = json.dumps({ 464 "clone_url": clone_url, 465 "branch": branch 466 }) 467 return cls.create_new(user, copr, source_type, source_json, chroot_names, **build_options)468 469 @classmethod470 - def create_new_from_upload(cls, user, copr, f_uploader, orig_filename, 471 chroot_names=None, **build_options):472 """ 473 :type user: models.User 474 :type copr: models.Copr 475 :param f_uploader(file_path): function which stores data at the given `file_path` 476 :return: 477 """ 478 tmp = tempfile.mkdtemp(dir=app.config["SRPM_STORAGE_DIR"]) 479 tmp_name = os.path.basename(tmp) 480 filename = secure_filename(orig_filename) 481 file_path = os.path.join(tmp, filename) 482 f_uploader(file_path) 483 484 # make the pkg public 485 pkg_url = "https://{hostname}/tmp/{tmp_dir}/{srpm}".format( 486 hostname=app.config["PUBLIC_COPR_HOSTNAME"], 487 tmp_dir=tmp_name, 488 srpm=filename) 489 490 # create json describing the build source 491 source_type = helpers.BuildSourceEnum("srpm_upload") 492 source_json = json.dumps({"tmp": tmp_name, "pkg": filename}) 493 try: 494 build = cls.create_new(user, copr, source_type, source_json, 495 chroot_names, pkgs=pkg_url, **build_options) 496 except Exception: 497 shutil.rmtree(tmp) # todo: maybe we should delete in some cleanup procedure? 498 raise 499 500 return build501 502 @classmethod503 - def create_new(cls, user, copr, source_type, source_json, chroot_names=None, 504 pkgs="", git_hashes=None, skip_import=False, background=False, **build_options):505 """ 506 :type user: models.User 507 :type copr: models.Copr 508 :type chroot_names: List[str] 509 :type source_type: int value from helpers.BuildSourceEnum 510 :type source_json: str in json format 511 :type pkgs: str 512 :type git_hashes: dict 513 :type skip_import: bool 514 :type background: bool 515 :rtype: models.Build 516 """ 517 if chroot_names is None: 518 chroots = [c for c in copr.active_chroots] 519 else: 520 chroots = [] 521 for chroot in copr.active_chroots: 522 if chroot.name in chroot_names: 523 chroots.append(chroot) 524 525 build = cls.add( 526 user=user, 527 pkgs=pkgs, 528 copr=copr, 529 chroots=chroots, 530 source_type=source_type, 531 source_json=source_json, 532 enable_net=build_options.get("enable_net", copr.build_enable_net), 533 background=background, 534 git_hashes=git_hashes, 535 skip_import=skip_import) 536 537 if user.proven: 538 if "timeout" in build_options: 539 build.timeout = build_options["timeout"] 540 541 return build542 543 @classmethod544 - def add(cls, user, pkgs, copr, source_type=None, source_json=None, 545 repos=None, chroots=None, timeout=None, enable_net=True, 546 git_hashes=None, skip_import=False, background=False):547 if chroots is None: 548 chroots = [] 549 550 coprs_logic.CoprsLogic.raise_if_unfinished_blocking_action( 551 copr, "Can't build while there is an operation in progress: {action}") 552 users_logic.UsersLogic.raise_if_cant_build_in_copr( 553 user, copr, 554 "You don't have permissions to build in this copr.") 555 556 if not repos: 557 repos = copr.repos 558 559 # todo: eliminate pkgs and this check 560 if pkgs and (" " in pkgs or "\n" in pkgs or "\t" in pkgs or pkgs.strip() != pkgs): 561 raise exceptions.MalformedArgumentException("Trying to create a build using src_pkg " 562 "with bad characters. Forgot to split?") 563 564 # just temporary to keep compatibility 565 if not source_type or not source_json: 566 source_type = helpers.BuildSourceEnum("srpm_link") 567 source_json = json.dumps({"url":pkgs}) 568 569 build = models.Build( 570 user=user, 571 pkgs=pkgs, 572 copr=copr, 573 repos=repos, 574 source_type=source_type, 575 source_json=source_json, 576 submitted_on=int(time.time()), 577 enable_net=bool(enable_net), 578 is_background=bool(background), 579 ) 580 581 if timeout: 582 build.timeout = timeout or DEFAULT_BUILD_TIMEOUT 583 584 db.session.add(build) 585 586 # add BuildChroot object for each active (or selected) chroot 587 # this copr is assigned to 588 if not chroots: 589 chroots = copr.active_chroots 590 591 status = helpers.StatusEnum("importing") 592 593 if skip_import: 594 status = StatusEnum("pending") 595 596 for chroot in chroots: 597 git_hash = None 598 if git_hashes: 599 git_hash = git_hashes.get(chroot.name) 600 buildchroot = models.BuildChroot( 601 build=build, 602 status=status, 603 mock_chroot=chroot, 604 git_hash=git_hash) 605 606 db.session.add(buildchroot) 607 608 return build609 610 @classmethod612 build = models.Build( 613 user=None, 614 pkgs=None, 615 package_id=package.id, 616 copr=package.copr, 617 repos=package.copr.repos, 618 source_type=package.source_type, 619 source_json=package.source_json, 620 submitted_on=int(time.time()), 621 enable_net=package.copr.build_enable_net, 622 timeout=DEFAULT_BUILD_TIMEOUT 623 ) 624 625 db.session.add(build) 626 627 chroots = package.copr.active_chroots 628 629 status = helpers.StatusEnum("importing") 630 631 for chroot in chroots: 632 buildchroot = models.BuildChroot( 633 build=build, 634 status=status, 635 mock_chroot=chroot, 636 git_hash=None 637 ) 638 639 db.session.add(buildchroot) 640 641 return build642 643 644 terminal_states = {StatusEnum("failed"), StatusEnum("succeeded"), StatusEnum("canceled")} 645 646 @classmethod648 """ 649 Returns a list of BuildChroots identified by build_id and dist-git 650 branch name. 651 """ 652 return ( 653 models.BuildChroot.query 654 .join(models.MockChroot) 655 .filter(models.BuildChroot.build_id==build_id) 656 .filter(models.MockChroot.distgit_branch_name==branch) 657 ).all()658 659 660 @classmethod662 """ 663 Deletes the source rpm locally stored for upload (if exists) 664 """ 665 # is it hosted on the copr frontend? 666 if build.source_type == helpers.BuildSourceEnum("srpm_upload"): 667 data = json.loads(build.source_json) 668 tmp = data["tmp"] 669 storage_path = app.config["SRPM_STORAGE_DIR"] 670 try: 671 shutil.rmtree(os.path.join(storage_path, tmp)) 672 except: 673 pass674 675 676 @classmethod678 """ 679 :param build: 680 :param upd_dict: 681 example: 682 { 683 "builds":[ 684 { 685 "id": 1, 686 "copr_id": 2, 687 "started_on": 139086644000 688 }, 689 { 690 "id": 2, 691 "copr_id": 1, 692 "status": 0, 693 "chroot": "fedora-18-x86_64", 694 "results": "http://server/results/foo/bar/", 695 "ended_on": 139086644000 696 }] 697 } 698 """ 699 log.info("Updating build: {} by: {}".format(build.id, upd_dict)) 700 if "chroot" in upd_dict: 701 # update respective chroot status 702 for build_chroot in build.build_chroots: 703 if build_chroot.name == upd_dict["chroot"]: 704 705 if "status" in upd_dict and build_chroot.status not in BuildsLogic.terminal_states: 706 build_chroot.status = upd_dict["status"] 707 708 if upd_dict.get("status") in BuildsLogic.terminal_states: 709 build_chroot.ended_on = upd_dict.get("ended_on") or time.time() 710 711 if upd_dict.get("status") == StatusEnum("starting"): 712 build_chroot.started_on = upd_dict.get("started_on") or time.time() 713 714 if "last_deferred" in upd_dict: 715 build_chroot.last_deferred = upd_dict["last_deferred"] 716 717 db.session.add(build_chroot) 718 719 for attr in ["results", "built_packages"]: 720 value = upd_dict.get(attr, None) 721 if value: 722 setattr(build, attr, value) 723 724 db.session.add(build)725 726 @classmethod728 if not user.can_build_in(build.copr): 729 raise exceptions.InsufficientRightsException( 730 "You are not allowed to cancel this build.") 731 if not build.cancelable: 732 if build.status == StatusEnum("starting"): 733 err_msg = "Cannot cancel build {} in state 'starting'".format(build.id) 734 else: 735 err_msg = "Cannot cancel build {}".format(build.id) 736 raise exceptions.RequestCannotBeExecuted(err_msg) 737 738 if build.status == StatusEnum("running"): # otherwise the build is just in frontend 739 ActionsLogic.send_cancel_build(build) 740 741 build.canceled = True 742 for chroot in build.build_chroots: 743 chroot.status = 2 # canceled 744 if chroot.ended_on is not None: 745 chroot.ended_on = time.time()746 747 @classmethod749 """ 750 :type user: models.User 751 :type build: models.Build 752 """ 753 if not user.can_edit(build.copr) or build.persistent: 754 raise exceptions.InsufficientRightsException( 755 "You are not allowed to delete build `{}`.".format(build.id)) 756 757 if not build.finished: 758 # from celery.contrib import rdb; rdb.set_trace() 759 raise exceptions.ActionInProgressException( 760 "You can not delete build `{}` which is not finished.".format(build.id), 761 "Unfinished build") 762 763 if send_delete_action: 764 ActionsLogic.send_delete_build(build) 765 766 for build_chroot in build.build_chroots: 767 db.session.delete(build_chroot) 768 db.session.delete(build)769 770 @classmethod772 """ 773 Marks build as failed on all its non-finished chroots 774 """ 775 build = cls.get(build_id).one() 776 chroots = filter(lambda x: x.status != helpers.StatusEnum("succeeded"), build.build_chroots) 777 for chroot in chroots: 778 chroot.status = helpers.StatusEnum("failed") 779 return build780 781 @classmethod783 """ Get build datetime (as epoch) of last successful build 784 785 :arg copr: object of copr 786 """ 787 builds = cls.get_multiple_by_copr(copr) 788 789 last_build = ( 790 builds.join(models.BuildChroot) 791 .filter((models.BuildChroot.status == helpers.StatusEnum("succeeded")) 792 | (models.BuildChroot.status == helpers.StatusEnum("skipped"))) 793 .filter(models.BuildChroot.ended_on.isnot(None)) 794 .order_by(models.BuildChroot.ended_on.desc()) 795 ).first() 796 if last_build: 797 return last_build.ended_on 798 else: 799 return None800 801 @classmethod803 # todo: check that ended_on is set correctly for all cases 804 # e.g.: failed dist-git import, cancellation 805 if is_finished: 806 return query.join(models.BuildChroot).filter(models.BuildChroot.ended_on.isnot(None)) 807 else: 808 return query.join(models.BuildChroot).filter(models.BuildChroot.ended_on.is_(None))809 810 @classmethod 813816 @classmethod857818 mc = MockChrootsLogic.get_from_name(name).one() 819 820 return ( 821 BuildChroot.query 822 .filter(BuildChroot.build_id == build_id) 823 .filter(BuildChroot.mock_chroot_id == mc.id) 824 )825 826 @classmethod828 query = ( 829 models.BuildChroot.query 830 .join(models.BuildChroot.build) 831 .join(models.BuildChroot.mock_chroot) 832 .join(models.Build.copr) 833 .join(models.Copr.user) 834 .outerjoin(models.Group) 835 ) 836 return query837 838 @classmethod 841 842 @classmethod 845 846 @classmethod 849 850 @classmethod 853 854 @classmethod860 @classmethod897862 query = """ 863 SELECT 864 package.id as package_id, 865 package.name AS package_name, 866 build.id AS build_id, 867 build_chroot.status AS build_chroot_status, 868 build.pkg_version AS build_pkg_version, 869 mock_chroot.id AS mock_chroot_id, 870 mock_chroot.os_release AS mock_chroot_os_release, 871 mock_chroot.os_version AS mock_chroot_os_version, 872 mock_chroot.arch AS mock_chroot_arch 873 FROM package 874 JOIN (SELECT 875 MAX(build.id) AS max_build_id_for_chroot, 876 build.package_id AS package_id, 877 build_chroot.mock_chroot_id AS mock_chroot_id 878 FROM build 879 JOIN build_chroot 880 ON build.id = build_chroot.build_id 881 WHERE build.copr_id = {copr_id} 882 AND build_chroot.status != 2 883 GROUP BY build.package_id, 884 build_chroot.mock_chroot_id) AS max_build_ids_for_a_chroot 885 ON package.id = max_build_ids_for_a_chroot.package_id 886 JOIN build 887 ON build.id = max_build_ids_for_a_chroot.max_build_id_for_chroot 888 JOIN build_chroot 889 ON build_chroot.mock_chroot_id = max_build_ids_for_a_chroot.mock_chroot_id 890 AND build_chroot.build_id = max_build_ids_for_a_chroot.max_build_id_for_chroot 891 JOIN mock_chroot 892 ON mock_chroot.id = max_build_ids_for_a_chroot.mock_chroot_id 893 ORDER BY package.name ASC, package.id ASC, mock_chroot.os_release ASC, mock_chroot.os_version ASC, mock_chroot.arch ASC 894 """.format(copr_id=copr.id) 895 rows = db.session.execute(query) 896 return rows
Trees | Indices | Help |
---|
Generated by Epydoc 3.0.1 | http://epydoc.sourceforge.net |