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 @classmethod119 120 @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 .filter(models.Build.srpm_url.isnot(None)) 84 ) 85 query = query.order_by(models.BuildChroot.build_id.asc()) 86 return query87 88 @classmethod 90 """ 91 Returns BuildChroots which are - waiting to be built or 92 - older than 2 hours and unfinished 93 """ 94 # todo: filter out build without package 95 query = (models.BuildChroot.query.join(models.Build) 96 .filter(models.Build.canceled == false()) 97 .filter(models.Build.is_background == (true() if is_background else false())) 98 .filter(or_( 99 models.BuildChroot.status == helpers.StatusEnum("pending"), 100 models.BuildChroot.status == helpers.StatusEnum("starting"), 101 and_( 102 # We are moving ended_on to the BuildChroot, now it should be reliable, 103 # so we don't want to reschedule failed chroots 104 # models.BuildChroot.status.in_([ 105 # # Bug 1206562 - Cannot delete Copr because it incorrectly thinks 106 # # there are unfinished builds. Solution: `failed` but unfinished 107 # # (ended_on is null) builds should be rescheduled. 108 # # todo: we need to be sure that correct `failed` set is set together with `ended_on` 109 # helpers.StatusEnum("running"), 110 # helpers.StatusEnum("failed") 111 #]), 112 models.BuildChroot.status == helpers.StatusEnum("running"), 113 models.BuildChroot.started_on < int(time.time() - 1.1 * MAX_BUILD_TIMEOUT), 114 models.BuildChroot.ended_on.is_(None) 115 )) 116 )) 117 query = query.order_by(models.BuildChroot.build_id.asc()) 118 return query122 query = (models.Build.query.join(models.BuildChroot) 123 .filter(models.Build.srpm_url.is_(None)) 124 .filter(models.Build.canceled == false()) 125 .filter(models.BuildChroot.status == helpers.StatusEnum("importing")) 126 ).order_by(models.Build.is_background.asc(), models.Build.id.asc()) 127 return query.first()128 129 @classmethod131 query = (models.BuildChroot.query.join(models.Build) 132 .filter(models.Build.srpm_url.isnot(None)) 133 .filter(models.Build.canceled == false()) 134 .filter(or_( 135 models.BuildChroot.status == helpers.StatusEnum("pending"), 136 and_( 137 models.BuildChroot.status == helpers.StatusEnum("running"), 138 models.BuildChroot.started_on < int(time.time() - 1.1 * MAX_BUILD_TIMEOUT), 139 models.BuildChroot.ended_on.is_(None) 140 ) 141 )) 142 .filter(or_( 143 models.BuildChroot.last_deferred.is_(None), 144 models.BuildChroot.last_deferred < int(time.time() - DEFER_BUILD_SECONDS) 145 )) 146 ).order_by(models.Build.is_background.asc(), models.BuildChroot.build_id.asc()) 147 return query.first()148 149 @classmethod151 try: 152 build_id, chroot_name = task_id.split("-", 1) 153 except ValueError: 154 raise MalformedArgumentException("Invalid task_id {}".format(task_id)) 155 156 build_chroot = BuildChrootsLogic.get_by_build_id_and_name(build_id, chroot_name) 157 return build_chroot.join(models.Build).first()158 159 @classmethod 162 163 @classmethod 166 167 @classmethod169 """ Get collection of builds in copr sorted by build_id descending 170 """ 171 return cls.get_multiple().filter(models.Build.copr == copr)172 173 @classmethod175 """ Get collection of builds in copr sorted by build_id descending 176 form the copr belonging to `user` 177 """ 178 return cls.get_multiple().join(models.Build.copr).filter( 179 models.Copr.user == user)180 181 182 @classmethod184 if db.engine.url.drivername == "sqlite": 185 return 186 187 status_to_order = """ 188 CREATE OR REPLACE FUNCTION status_to_order (x integer) 189 RETURNS integer AS $$ BEGIN 190 RETURN CASE WHEN x = 0 THEN 0 191 WHEN x = 3 THEN 1 192 WHEN x = 6 THEN 2 193 WHEN x = 7 THEN 3 194 WHEN x = 4 THEN 4 195 WHEN x = 1 THEN 5 196 WHEN x = 5 THEN 6 197 ELSE 1000 198 END; END; 199 $$ LANGUAGE plpgsql; 200 """ 201 202 order_to_status = """ 203 CREATE OR REPLACE FUNCTION order_to_status (x integer) 204 RETURNS integer AS $$ BEGIN 205 RETURN CASE WHEN x = 0 THEN 0 206 WHEN x = 1 THEN 3 207 WHEN x = 2 THEN 6 208 WHEN x = 3 THEN 7 209 WHEN x = 4 THEN 4 210 WHEN x = 5 THEN 1 211 WHEN x = 6 THEN 5 212 ELSE 1000 213 END; END; 214 $$ LANGUAGE plpgsql; 215 """ 216 217 db.engine.connect() 218 db.engine.execute(status_to_order) 219 db.engine.execute(order_to_status)220 221 @classmethod223 query_select = """ 224 SELECT build.id, MAX(package.name) AS pkg_name, build.pkg_version, build.submitted_on, 225 MIN(statuses.started_on) AS started_on, MAX(statuses.ended_on) AS ended_on, order_to_status(MIN(statuses.st)) AS status, 226 build.canceled, MIN("group".name) AS group_name, MIN(copr.name) as copr_name, MIN("user".username) as user_name 227 FROM build 228 LEFT OUTER JOIN package 229 ON build.package_id = package.id 230 LEFT OUTER JOIN (SELECT build_chroot.build_id, started_on, ended_on, status_to_order(status) AS st FROM build_chroot) AS statuses 231 ON statuses.build_id=build.id 232 LEFT OUTER JOIN copr 233 ON copr.id = build.copr_id 234 LEFT OUTER JOIN "user" 235 ON copr.user_id = "user".id 236 LEFT OUTER JOIN "group" 237 ON copr.group_id = "group".id 238 WHERE build.copr_id = :copr_id 239 GROUP BY 240 build.id; 241 """ 242 243 if db.engine.url.drivername == "sqlite": 244 def sqlite_status_to_order(x): 245 if x == 3: 246 return 1 247 elif x == 6: 248 return 2 249 elif x == 7: 250 return 3 251 elif x == 4: 252 return 4 253 elif x == 0: 254 return 5 255 elif x == 1: 256 return 6 257 elif x == 5: 258 return 7 259 elif x == 8: 260 return 8 261 return 1000262 263 def sqlite_order_to_status(x): 264 if x == 1: 265 return 3 266 elif x == 2: 267 return 6 268 elif x == 3: 269 return 7 270 elif x == 4: 271 return 4 272 elif x == 5: 273 return 0 274 elif x == 6: 275 return 1 276 elif x == 7: 277 return 5 278 elif x == 8: 279 return 8 280 return 1000 281 282 conn = db.engine.connect() 283 conn.connection.create_function("status_to_order", 1, sqlite_status_to_order) 284 conn.connection.create_function("order_to_status", 1, sqlite_order_to_status) 285 statement = text(query_select) 286 statement.bindparams(bindparam("copr_id", Integer)) 287 result = conn.execute(statement, {"copr_id": copr.id}) 288 else: 289 statement = text(query_select) 290 statement.bindparams(bindparam("copr_id", Integer)) 291 result = db.engine.execute(statement, {"copr_id": copr.id}) 292 293 return result 294 295 @classmethod 298 299 @classmethod301 query = cls.get_multiple() 302 return (query.join(models.Build.copr) 303 .options(db.contains_eager(models.Build.copr)) 304 .join(models.Copr.user) 305 .filter(models.Copr.name == coprname) 306 .filter(models.User.username == username))307 308 @classmethod310 """ 311 Return builds that are waiting for dist git to import the sources. 312 """ 313 query = (models.Build.query.join(models.Build.copr) 314 .join(models.User) 315 .join(models.BuildChroot) 316 .options(db.contains_eager(models.Build.copr)) 317 .options(db.contains_eager("copr.user")) 318 .filter((models.BuildChroot.started_on == None) 319 | (models.BuildChroot.started_on < int(time.time() - 7200))) 320 .filter(models.BuildChroot.ended_on == None) 321 .filter(models.Build.canceled == False) 322 .order_by(models.Build.submitted_on.asc())) 323 return query324 325 @classmethod327 """ 328 Return builds that aren't both started and finished 329 (if build start submission fails, we still want to mark 330 the build as non-waiting, if it ended) 331 this has very different goal then get_multiple, so implement it alone 332 """ 333 334 query = (models.Build.query.join(models.Build.copr) 335 .join(models.User).join(models.BuildChroot) 336 .options(db.contains_eager(models.Build.copr)) 337 .options(db.contains_eager("copr.user")) 338 .filter((models.BuildChroot.started_on.is_(None)) 339 | (models.BuildChroot.started_on < int(time.time() - 7200))) 340 .filter(models.BuildChroot.ended_on.is_(None)) 341 .filter(models.Build.canceled == false()) 342 .order_by(models.Build.submitted_on.asc())) 343 return query344 345 @classmethod 348 349 @classmethod 352 353 @classmethod354 - def create_new_from_other_build(cls, user, copr, source_build, 355 chroot_names=None, **build_options):356 skip_import = False 357 git_hashes = {} 358 359 if source_build.source_type == helpers.BuildSourceEnum('upload'): 360 # I don't have the source 361 # so I don't want to import anything, just rebuild what's in dist git 362 skip_import = True 363 364 for chroot in source_build.build_chroots: 365 if not chroot.git_hash: 366 # I got an old build from time we didn't use dist git 367 # So I'll submit it as a new build using it's link 368 skip_import = False 369 git_hashes = None 370 flask.flash("This build is not in Dist Git. Trying to import the package again.") 371 break 372 git_hashes[chroot.name] = chroot.git_hash 373 374 build = cls.create_new(user, copr, source_build.source_type, source_build.source_json, chroot_names, 375 pkgs=source_build.pkgs, git_hashes=git_hashes, skip_import=skip_import, 376 srpm_url=source_build.srpm_url, **build_options) 377 build.package_id = source_build.package_id 378 build.pkg_version = source_build.pkg_version 379 return build380 381 @classmethod384 """ 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("link") 393 source_json = json.dumps({"url": url}) 394 srpm_url = None if url.endswith('.spec') else url 395 return cls.create_new(user, copr, source_type, source_json, chroot_names, 396 pkgs=url, srpm_url=srpm_url, **build_options)397 398 @classmethod399 - def create_new_from_tito(cls, user, copr, git_url, git_dir, git_branch, tito_test, 400 chroot_names=None, **build_options):401 """ 402 :type user: models.User 403 :type copr: models.Copr 404 405 :type chroot_names: List[str] 406 407 :rtype: models.Build 408 """ 409 source_type = helpers.BuildSourceEnum("git_and_tito") 410 source_json = json.dumps({"git_url": git_url, 411 "git_dir": git_dir, 412 "git_branch": git_branch, 413 "tito_test": tito_test}) 414 return cls.create_new(user, copr, source_type, source_json, chroot_names, **build_options)415 416 @classmethod417 - def create_new_from_mock(cls, user, copr, scm_type, scm_url, scm_branch, scm_subdir, spec, 418 chroot_names=None, **build_options):419 """ 420 :type user: models.User 421 :type copr: models.Copr 422 423 :type chroot_names: List[str] 424 425 :rtype: models.Build 426 """ 427 source_type = helpers.BuildSourceEnum("mock_scm") 428 source_json = json.dumps({"scm_type": scm_type, 429 "scm_url": scm_url, 430 "scm_branch": scm_branch, 431 "scm_subdir": scm_subdir, 432 "spec": spec}) 433 return cls.create_new(user, copr, source_type, source_json, chroot_names, **build_options)434 435 @classmethod436 - def create_new_from_pypi(cls, user, copr, pypi_package_name, pypi_package_version, python_versions, 437 chroot_names=None, **build_options):438 """ 439 :type user: models.User 440 :type copr: models.Copr 441 :type package_name: str 442 :type version: str 443 :type python_versions: List[str] 444 445 :type chroot_names: List[str] 446 447 :rtype: models.Build 448 """ 449 source_type = helpers.BuildSourceEnum("pypi") 450 source_json = json.dumps({"pypi_package_name": pypi_package_name, 451 "pypi_package_version": pypi_package_version, 452 "python_versions": python_versions}) 453 return cls.create_new(user, copr, source_type, source_json, chroot_names, **build_options)454 455 @classmethod456 - def create_new_from_rubygems(cls, user, copr, gem_name, 457 chroot_names=None, **build_options):458 """ 459 :type user: models.User 460 :type copr: models.Copr 461 :type gem_name: str 462 :type chroot_names: List[str] 463 :rtype: models.Build 464 """ 465 source_type = helpers.BuildSourceEnum("rubygems") 466 source_json = json.dumps({"gem_name": gem_name}) 467 return cls.create_new(user, copr, source_type, source_json, chroot_names, **build_options)468 469 @classmethod470 - def create_new_from_distgit(cls, user, copr, clone_url, branch, 471 chroot_names=None, **build_options):472 """ 473 :type user: models.User 474 :type copr: models.Copr 475 :type clone_url: str 476 :type branch: str 477 :type chroot_names: List[str] 478 :rtype: models.Build 479 """ 480 source_type = helpers.BuildSourceEnum("distgit") 481 source_json = json.dumps({ 482 "clone_url": clone_url, 483 "branch": branch 484 }) 485 return cls.create_new(user, copr, source_type, source_json, chroot_names, **build_options)486 487 @classmethod488 - def create_new_from_upload(cls, user, copr, f_uploader, orig_filename, 489 chroot_names=None, **build_options):490 """ 491 :type user: models.User 492 :type copr: models.Copr 493 :param f_uploader(file_path): function which stores data at the given `file_path` 494 :return: 495 """ 496 tmp = tempfile.mkdtemp(dir=app.config["SRPM_STORAGE_DIR"]) 497 tmp_name = os.path.basename(tmp) 498 filename = secure_filename(orig_filename) 499 file_path = os.path.join(tmp, filename) 500 f_uploader(file_path) 501 502 # make the pkg public 503 pkg_url = "{baseurl}/tmp/{tmp_dir}/{filename}".format( 504 baseurl=app.config["PUBLIC_COPR_BASE_URL"], 505 tmp_dir=tmp_name, 506 filename=filename) 507 508 # create json describing the build source 509 source_type = helpers.BuildSourceEnum("upload") 510 source_json = json.dumps({"url": pkg_url, "pkg": filename, "tmp": tmp_name}) 511 srpm_url = None if pkg_url.endswith('.spec') else pkg_url 512 513 try: 514 build = cls.create_new(user, copr, source_type, source_json, 515 chroot_names, pkgs=pkg_url, srpm_url=srpm_url, **build_options) 516 except Exception: 517 shutil.rmtree(tmp) # todo: maybe we should delete in some cleanup procedure? 518 raise 519 520 return build521 522 @classmethod523 - def create_new(cls, user, copr, source_type, source_json, chroot_names=None, pkgs="", 524 git_hashes=None, skip_import=False, background=False, batch=None, 525 srpm_url=None, **build_options):526 """ 527 :type user: models.User 528 :type copr: models.Copr 529 :type chroot_names: List[str] 530 :type source_type: int value from helpers.BuildSourceEnum 531 :type source_json: str in json format 532 :type pkgs: str 533 :type git_hashes: dict 534 :type skip_import: bool 535 :type background: bool 536 :type batch: models.Batch 537 :rtype: models.Build 538 """ 539 if chroot_names is None: 540 chroots = [c for c in copr.active_chroots] 541 else: 542 chroots = [] 543 for chroot in copr.active_chroots: 544 if chroot.name in chroot_names: 545 chroots.append(chroot) 546 547 build = cls.add( 548 user=user, 549 pkgs=pkgs, 550 copr=copr, 551 chroots=chroots, 552 source_type=source_type, 553 source_json=source_json, 554 enable_net=build_options.get("enable_net", copr.build_enable_net), 555 background=background, 556 git_hashes=git_hashes, 557 skip_import=skip_import, 558 batch=batch, 559 srpm_url=srpm_url, 560 ) 561 562 if user.proven: 563 if "timeout" in build_options: 564 build.timeout = build_options["timeout"] 565 566 return build567 568 @classmethod569 - def add(cls, user, pkgs, copr, source_type=None, source_json=None, 570 repos=None, chroots=None, timeout=None, enable_net=True, 571 git_hashes=None, skip_import=False, background=False, batch=None, 572 srpm_url=None):573 if chroots is None: 574 chroots = [] 575 576 coprs_logic.CoprsLogic.raise_if_unfinished_blocking_action( 577 copr, "Can't build while there is an operation in progress: {action}") 578 users_logic.UsersLogic.raise_if_cant_build_in_copr( 579 user, copr, 580 "You don't have permissions to build in this copr.") 581 582 if not repos: 583 repos = copr.repos 584 585 # todo: eliminate pkgs and this check 586 if pkgs and (" " in pkgs or "\n" in pkgs or "\t" in pkgs or pkgs.strip() != pkgs): 587 raise exceptions.MalformedArgumentException("Trying to create a build using src_pkg " 588 "with bad characters. Forgot to split?") 589 590 # just temporary to keep compatibility 591 if not source_type or not source_json: 592 source_type = helpers.BuildSourceEnum("link") 593 source_json = json.dumps({"url":pkgs}) 594 595 build = models.Build( 596 user=user, 597 pkgs=pkgs, 598 copr=copr, 599 repos=repos, 600 source_type=source_type, 601 source_json=source_json, 602 submitted_on=int(time.time()), 603 enable_net=bool(enable_net), 604 is_background=bool(background), 605 batch=batch, 606 srpm_url=srpm_url, 607 ) 608 609 if timeout: 610 build.timeout = timeout or DEFAULT_BUILD_TIMEOUT 611 612 db.session.add(build) 613 614 # add BuildChroot object for each active (or selected) chroot 615 # this copr is assigned to 616 if not chroots: 617 chroots = copr.active_chroots 618 619 status = helpers.StatusEnum("importing") 620 621 if skip_import: 622 status = StatusEnum("pending") 623 624 for chroot in chroots: 625 git_hash = None 626 if git_hashes: 627 git_hash = git_hashes.get(chroot.name) 628 buildchroot = models.BuildChroot( 629 build=build, 630 status=status, 631 mock_chroot=chroot, 632 git_hash=git_hash) 633 634 db.session.add(buildchroot) 635 636 return build637 638 @classmethod640 build = models.Build( 641 user=None, 642 pkgs=None, 643 package_id=package.id, 644 copr=package.copr, 645 repos=package.copr.repos, 646 source_type=package.source_type, 647 source_json=package.source_json, 648 submitted_on=int(time.time()), 649 enable_net=package.copr.build_enable_net, 650 timeout=DEFAULT_BUILD_TIMEOUT 651 ) 652 653 db.session.add(build) 654 655 chroots = package.copr.active_chroots 656 657 status = helpers.StatusEnum("importing") 658 659 for chroot in chroots: 660 buildchroot = models.BuildChroot( 661 build=build, 662 status=status, 663 mock_chroot=chroot, 664 git_hash=None 665 ) 666 667 db.session.add(buildchroot) 668 669 return build670 671 672 terminal_states = {StatusEnum("failed"), StatusEnum("succeeded"), StatusEnum("canceled")} 673 674 @classmethod676 """ 677 Returns a list of BuildChroots identified by build_id and dist-git 678 branch name. 679 """ 680 return ( 681 models.BuildChroot.query 682 .join(models.MockChroot) 683 .filter(models.BuildChroot.build_id==build_id) 684 .filter(models.MockChroot.distgit_branch_name==branch) 685 ).all()686 687 688 @classmethod690 """ 691 Deletes the source (rpm or .spec) locally stored for upload (if exists) 692 """ 693 # is it hosted on the copr frontend? 694 if build.source_type == helpers.BuildSourceEnum("upload"): 695 data = json.loads(build.source_json) 696 tmp = data["tmp"] 697 storage_path = app.config["SRPM_STORAGE_DIR"] 698 try: 699 shutil.rmtree(os.path.join(storage_path, tmp)) 700 except: 701 pass702 703 704 @classmethod706 """ 707 :param build: 708 :param upd_dict: 709 example: 710 { 711 "builds":[ 712 { 713 "id": 1, 714 "copr_id": 2, 715 "started_on": 139086644000 716 }, 717 { 718 "id": 2, 719 "copr_id": 1, 720 "status": 0, 721 "chroot": "fedora-18-x86_64", 722 "results": "http://server/results/foo/bar/", 723 "ended_on": 139086644000 724 }] 725 } 726 """ 727 log.info("Updating build: {} by: {}".format(build.id, upd_dict)) 728 if "chroot" in upd_dict: 729 if upd_dict["chroot"] == "srpm-builds": 730 if upd_dict.get("status") == StatusEnum("failed") and not build.canceled: 731 build.fail_type = helpers.FailTypeEnum("srpm_build_error") 732 for ch in build.build_chroots: 733 ch.status = helpers.StatusEnum("failed") 734 ch.ended_on = upd_dict.get("ended_on") or time.time() 735 db.session.add(ch) 736 737 # update respective chroot status 738 for build_chroot in build.build_chroots: 739 if build_chroot.name == upd_dict["chroot"]: 740 741 if "status" in upd_dict and build_chroot.status not in BuildsLogic.terminal_states: 742 build_chroot.status = upd_dict["status"] 743 744 if upd_dict.get("status") in BuildsLogic.terminal_states: 745 build_chroot.ended_on = upd_dict.get("ended_on") or time.time() 746 747 if upd_dict.get("status") == StatusEnum("starting"): 748 build_chroot.started_on = upd_dict.get("started_on") or time.time() 749 750 if "last_deferred" in upd_dict: 751 build_chroot.last_deferred = upd_dict["last_deferred"] 752 753 db.session.add(build_chroot) 754 755 for attr in ["results", "built_packages", "srpm_url"]: 756 value = upd_dict.get(attr, None) 757 if value: 758 setattr(build, attr, value) 759 760 db.session.add(build)761 762 @classmethod764 if not user.can_build_in(build.copr): 765 raise exceptions.InsufficientRightsException( 766 "You are not allowed to cancel this build.") 767 if not build.cancelable: 768 if build.status == StatusEnum("starting"): 769 err_msg = "Cannot cancel build {} in state 'starting'".format(build.id) 770 else: 771 err_msg = "Cannot cancel build {}".format(build.id) 772 raise exceptions.RequestCannotBeExecuted(err_msg) 773 774 if build.status == StatusEnum("running"): # otherwise the build is just in frontend 775 ActionsLogic.send_cancel_build(build) 776 777 build.canceled = True 778 for chroot in build.build_chroots: 779 chroot.status = 2 # canceled 780 if chroot.ended_on is not None: 781 chroot.ended_on = time.time()782 783 @classmethod785 """ 786 :type user: models.User 787 :type build: models.Build 788 """ 789 if not user.can_edit(build.copr) or build.persistent: 790 raise exceptions.InsufficientRightsException( 791 "You are not allowed to delete build `{}`.".format(build.id)) 792 793 if not build.finished: 794 # from celery.contrib import rdb; rdb.set_trace() 795 raise exceptions.ActionInProgressException( 796 "You can not delete build `{}` which is not finished.".format(build.id), 797 "Unfinished build") 798 799 if send_delete_action: 800 ActionsLogic.send_delete_build(build) 801 802 for build_chroot in build.build_chroots: 803 db.session.delete(build_chroot) 804 db.session.delete(build)805 806 @classmethod808 """ 809 Marks build as failed on all its non-finished chroots 810 """ 811 build = cls.get(build_id).one() 812 chroots = filter(lambda x: x.status != helpers.StatusEnum("succeeded"), build.build_chroots) 813 for chroot in chroots: 814 chroot.status = helpers.StatusEnum("failed") 815 return build816 817 @classmethod819 """ Get build datetime (as epoch) of last successful build 820 821 :arg copr: object of copr 822 """ 823 builds = cls.get_multiple_by_copr(copr) 824 825 last_build = ( 826 builds.join(models.BuildChroot) 827 .filter((models.BuildChroot.status == helpers.StatusEnum("succeeded")) 828 | (models.BuildChroot.status == helpers.StatusEnum("skipped"))) 829 .filter(models.BuildChroot.ended_on.isnot(None)) 830 .order_by(models.BuildChroot.ended_on.desc()) 831 ).first() 832 if last_build: 833 return last_build.ended_on 834 else: 835 return None836 837 @classmethod839 # todo: check that ended_on is set correctly for all cases 840 # e.g.: failed dist-git import, cancellation 841 if is_finished: 842 return query.join(models.BuildChroot).filter(models.BuildChroot.ended_on.isnot(None)) 843 else: 844 return query.join(models.BuildChroot).filter(models.BuildChroot.ended_on.is_(None))845 846 @classmethod 849852 @classmethod893854 mc = MockChrootsLogic.get_from_name(name).one() 855 856 return ( 857 BuildChroot.query 858 .filter(BuildChroot.build_id == build_id) 859 .filter(BuildChroot.mock_chroot_id == mc.id) 860 )861 862 @classmethod864 query = ( 865 models.BuildChroot.query 866 .join(models.BuildChroot.build) 867 .join(models.BuildChroot.mock_chroot) 868 .join(models.Build.copr) 869 .join(models.Copr.user) 870 .outerjoin(models.Group) 871 ) 872 return query873 874 @classmethod 877 878 @classmethod 881 882 @classmethod 885 886 @classmethod 889 890 @classmethod896 @classmethod933898 query = """ 899 SELECT 900 package.id as package_id, 901 package.name AS package_name, 902 build.id AS build_id, 903 build_chroot.status AS build_chroot_status, 904 build.pkg_version AS build_pkg_version, 905 mock_chroot.id AS mock_chroot_id, 906 mock_chroot.os_release AS mock_chroot_os_release, 907 mock_chroot.os_version AS mock_chroot_os_version, 908 mock_chroot.arch AS mock_chroot_arch 909 FROM package 910 JOIN (SELECT 911 MAX(build.id) AS max_build_id_for_chroot, 912 build.package_id AS package_id, 913 build_chroot.mock_chroot_id AS mock_chroot_id 914 FROM build 915 JOIN build_chroot 916 ON build.id = build_chroot.build_id 917 WHERE build.copr_id = {copr_id} 918 AND build_chroot.status != 2 919 GROUP BY build.package_id, 920 build_chroot.mock_chroot_id) AS max_build_ids_for_a_chroot 921 ON package.id = max_build_ids_for_a_chroot.package_id 922 JOIN build 923 ON build.id = max_build_ids_for_a_chroot.max_build_id_for_chroot 924 JOIN build_chroot 925 ON build_chroot.mock_chroot_id = max_build_ids_for_a_chroot.mock_chroot_id 926 AND build_chroot.build_id = max_build_ids_for_a_chroot.max_build_id_for_chroot 927 JOIN mock_chroot 928 ON mock_chroot.id = max_build_ids_for_a_chroot.mock_chroot_id 929 ORDER BY package.name ASC, package.id ASC, mock_chroot.os_release ASC, mock_chroot.os_version ASC, mock_chroot.arch ASC 930 """.format(copr_id=copr.id) 931 rows = db.session.execute(query) 932 return rows
Trees | Indices | Help |
---|
Generated by Epydoc 3.0.1 | http://epydoc.sourceforge.net |