Package coprs :: Package views :: Package backend_ns :: Module backend_general
[hide private]
[frames] | no frames]

Source Code for Module coprs.views.backend_ns.backend_general

  1  import flask 
  2  import sqlalchemy 
  3   
  4  from copr_common.enums import StatusEnum 
  5  from coprs import db, app 
  6  from coprs import models 
  7  from coprs.logic import actions_logic 
  8  from coprs.logic.builds_logic import BuildsLogic 
  9  from coprs.logic.complex_logic import ComplexLogic, BuildConfigLogic 
 10  from coprs.logic.packages_logic import PackagesLogic 
 11  from coprs.logic.coprs_logic import MockChrootsLogic, CoprChrootsLogic 
 12  from coprs.exceptions import MalformedArgumentException, ObjectNotFound 
 13   
 14  from coprs.views import misc 
 15  from coprs.views.backend_ns import backend_ns 
 16  from sqlalchemy.sql import false, true 
 17   
 18  import logging 
 19   
 20  log = logging.getLogger(__name__) 
21 22 23 @backend_ns.route("/importing/") 24 -def dist_git_importing_queue():
25 """ 26 Return list of builds that are waiting for dist-git to import the sources. 27 """ 28 tasks = [] 29 30 builds_for_import = BuildsLogic.get_build_importing_queue().filter(models.Build.is_background == false()).limit(100).all() 31 if not builds_for_import: 32 builds_for_import = BuildsLogic.get_build_importing_queue().filter(models.Build.is_background == true()).limit(30).all() 33 34 for build in builds_for_import: 35 branches = set() 36 for b_ch in build.build_chroots: 37 branches.add(b_ch.mock_chroot.distgit_branch_name) 38 39 tasks.append({ 40 "build_id": build.id, 41 "owner": build.copr.owner_name, 42 "project": build.copr_dirname, 43 # TODO: we mix PR with normal builds here :-( 44 "branches": list(branches), 45 "pkg_name": build.package.name, 46 "srpm_url": build.srpm_url, 47 }) 48 49 return flask.jsonify(tasks)
50
51 52 @backend_ns.route("/import-completed/", methods=["POST", "PUT"]) 53 @misc.backend_authenticated 54 -def dist_git_upload_completed():
55 app.logger.debug(flask.request.json) 56 build_id = flask.request.json.get("build_id") 57 58 try: 59 build = ComplexLogic.get_build_safe(build_id) 60 except ObjectNotFound: 61 return flask.jsonify({"updated": False}) 62 63 collected_branch_chroots = [] 64 for branch, git_hash in flask.request.json.get("branch_commits", {}).items(): 65 branch_chroots = BuildsLogic.get_buildchroots_by_build_id_and_branch(build_id, branch) 66 for ch in branch_chroots: 67 ch.status = StatusEnum("pending") 68 ch.git_hash = git_hash 69 db.session.add(ch) 70 collected_branch_chroots.append((ch.task_id)) 71 72 final_source_status = StatusEnum("succeeded") 73 for ch in build.build_chroots: 74 if ch.task_id not in collected_branch_chroots: 75 final_source_status = StatusEnum("failed") 76 ch.status = StatusEnum("failed") 77 db.session.add(ch) 78 79 build.source_status = final_source_status 80 db.session.add(build) 81 db.session.commit() 82 83 BuildsLogic.delete_local_source(build) 84 return flask.jsonify({"updated": True})
85
86 87 -def get_build_record(task, short=False):
88 if not task: 89 return None 90 91 build_record = None 92 try: 93 build_record = { 94 "task_id": task.task_id, 95 "build_id": task.build.id, 96 "project_owner": task.build.copr.owner_name, 97 "project_name": task.build.copr_name, 98 "project_dirname": task.build.copr_dirname, 99 "submitter": task.build.submitter[0], 100 "sandbox": task.build.sandbox, 101 "chroot": task.mock_chroot.name, 102 "repos": task.build.repos, 103 "memory_reqs": task.build.memory_reqs, 104 "timeout": task.build.timeout, 105 "enable_net": task.build.enable_net, 106 "git_repo": task.build.package.dist_git_clone_url, 107 "git_hash": task.git_hash, 108 "package_name": task.build.package.name, 109 "package_version": task.build.pkg_version, 110 "uses_devel_repo": task.build.copr.devel_mode, 111 } 112 113 114 if task.build.is_background: 115 build_record['background'] = True 116 117 if short: 118 return build_record 119 120 copr_chroot = CoprChrootsLogic.get_by_name_safe(task.build.copr, task.mock_chroot.name) 121 if copr_chroot.module_toggle_array: 122 array = [{'enable': m} for m in copr_chroot.module_toggle_array] 123 build_record["modules"] = {'toggle': array} 124 125 build_config = BuildConfigLogic.generate_build_config(task.build.copr, task.mock_chroot.name) 126 build_record["repos"] = build_config.get("repos") 127 build_record["buildroot_pkgs"] = build_config.get("additional_packages") 128 build_record["with_opts"] = build_config.get("with_opts") 129 build_record["without_opts"] = build_config.get("without_opts") 130 131 bch_bootstrap = BuildConfigLogic.build_bootstrap_setup( 132 build_config, task.build) 133 build_record.update(bch_bootstrap) 134 135 except Exception as err: 136 app.logger.exception(err) 137 return None 138 139 return build_record
140
141 142 -def get_srpm_build_record(task):
143 if not task: 144 return None 145 146 if task.source_type_text == "custom": 147 chroot = task.source_json_dict['chroot'] 148 else: 149 chroot = None 150 151 try: 152 build_record = { 153 "task_id": task.task_id, 154 "build_id": task.id, 155 "project_owner": task.copr.owner_name, 156 "project_name": task.copr_name, 157 "project_dirname": task.copr_dirname, 158 "submitter": task.submitter[0], 159 "sandbox": task.sandbox, 160 "source_type": task.source_type, 161 "source_json": task.source_json, 162 "chroot": chroot, 163 } 164 165 except Exception as err: 166 app.logger.exception(err) 167 return None 168 169 return build_record
170
171 172 @backend_ns.route("/pending-action/") 173 -def pending_action():
174 """ 175 Return a single action. 176 """ 177 action_record = None 178 action = actions_logic.ActionsLogic.get_waiting().first() 179 if action: 180 action_record = action.to_dict(options={ 181 "__columns_except__": ["result", "message", "ended_on"] 182 }) 183 return flask.jsonify(action_record)
184
185 186 @backend_ns.route("/build-tasks/cancel-requests/") 187 -def pending_cancel_builds():
188 """ 189 Return the list of task IDs to be canceled. 190 """ 191 task_ids = [x.what for x in models.CancelRequest.query.all()] 192 return flask.jsonify(task_ids)
193
194 195 @backend_ns.route("/build-tasks/canceled/<task_id>/", methods=["POST", "PUT"]) 196 @misc.backend_authenticated 197 -def build_task_canceled(task_id):
198 """ Report back to frontend that the task was canceled on backend """ 199 models.CancelRequest.query.filter_by(what=task_id).delete() 200 was_running = flask.request.json 201 if not was_running: 202 if '-' in task_id: 203 build_chroot = BuildsLogic.get_build_task(task_id) 204 if build_chroot: 205 build_chroot.status = StatusEnum("canceled") 206 else: 207 build = models.Build.query.filter_by(id=task_id).first() 208 if build: 209 build.source_status = StatusEnum("canceled") 210 db.session.commit() 211 return flask.jsonify("success")
212
213 214 @backend_ns.route("/pending-actions/") 215 -def pending_actions():
216 'get the list of actions backand should take care of' 217 data = [] 218 for action in actions_logic.ActionsLogic.get_waiting(): 219 data.append({ 220 'id': action.id, 221 'priority': action.priority or action.default_priority, 222 }) 223 return flask.json.dumps(data)
224
225 226 227 @backend_ns.route("/action/<int:action_id>/") 228 -def get_action(action_id):
229 action = actions_logic.ActionsLogic.get(action_id).one() 230 action_record = action.to_dict() 231 return flask.jsonify(action_record)
232
233 234 @backend_ns.route("/pending-action-count/") 235 -def pending_action_count():
236 """ 237 Return pending action count. 238 """ 239 return flask.jsonify(actions_logic.ActionsLogic.get_waiting().count())
240
241 242 @backend_ns.route("/pending-jobs/") 243 -def pending_jobs():
244 """ 245 Return the job queue. 246 """ 247 srpm_tasks = [build for build in BuildsLogic.get_pending_srpm_build_tasks() if not build.blocked] 248 build_records = ( 249 [get_srpm_build_record(task) for task in srpm_tasks] + 250 [get_build_record(task, short=True) 251 for task in BuildsLogic.get_pending_build_tasks(for_backend=True)] 252 ) 253 log.info('Selected build records: {}'.format(build_records)) 254 return flask.jsonify(build_records)
255
256 257 @backend_ns.route("/get-build-task/<task_id>/") 258 @backend_ns.route("/get-build-task/<task_id>") 259 -def get_build_task(task_id):
260 try: 261 task = BuildsLogic.get_build_task(task_id) 262 except MalformedArgumentException: 263 jsonout = flask.jsonify({'msg': 'Invalid task ID'}) 264 jsonout.status_code = 500 265 return jsonout 266 except sqlalchemy.orm.exc.NoResultFound: 267 jsonout = flask.jsonify({'msg': 'Specified task ID not found'}) 268 jsonout.status_code = 404 269 return jsonout 270 build_record = get_build_record(task) 271 return flask.jsonify(build_record)
272
273 274 @backend_ns.route("/get-srpm-build-task/<build_id>/") 275 @backend_ns.route("/get-srpm-build-task/<build_id>") 276 -def get_srpm_build_task(build_id):
277 try: 278 task = BuildsLogic.get_srpm_build_task(build_id) 279 except sqlalchemy.orm.exc.NoResultFound: 280 jsonout = flask.jsonify({'msg': 'Specified task ID not found'}) 281 jsonout.status_code = 404 282 return jsonout 283 build_record = get_srpm_build_record(task) 284 return flask.jsonify(build_record)
285
286 287 @backend_ns.route("/update/", methods=["POST", "PUT"]) 288 @misc.backend_authenticated 289 -def update():
290 result = {} 291 292 request_data = flask.request.json 293 for typ, logic_cls in [("actions", actions_logic.ActionsLogic), 294 ("builds", BuildsLogic)]: 295 296 if typ not in request_data: 297 continue 298 299 to_update = {} 300 for obj in request_data[typ]: 301 to_update[obj["id"]] = obj 302 303 existing = {} 304 for obj in logic_cls.get_by_ids(to_update.keys()).all(): 305 existing[obj.id] = obj 306 307 non_existing_ids = list(set(to_update.keys()) - set(existing.keys())) 308 309 for i, obj in existing.items(): 310 logic_cls.update_state_from_dict(obj, to_update[i]) 311 312 db.session.commit() 313 result.update({"updated_{0}_ids".format(typ): list(existing.keys()), 314 "non_existing_{0}_ids".format(typ): non_existing_ids}) 315 316 return flask.jsonify(result)
317
318 319 @backend_ns.route("/starting_build/", methods=["POST", "PUT"]) 320 @misc.backend_authenticated 321 -def starting_build():
322 """ 323 Check if the build is not cancelled and set it to starting state 324 """ 325 data = flask.request.json 326 327 try: 328 build = ComplexLogic.get_build_safe(data.get('build_id')) 329 except ObjectNotFound: 330 return flask.jsonify({"can_start": False}) 331 332 if build.canceled: 333 return flask.jsonify({"can_start": False}) 334 335 BuildsLogic.update_state_from_dict(build, data) 336 db.session.commit() 337 return flask.jsonify({"can_start": True})
338
339 340 @backend_ns.route("/reschedule_build_chroot/", methods=["POST", "PUT"]) 341 @misc.backend_authenticated 342 -def reschedule_build_chroot():
343 response = {} 344 build_id = flask.request.json.get("build_id") 345 task_id = flask.request.json.get("task_id") 346 chroot = flask.request.json.get("chroot") 347 348 try: 349 build = ComplexLogic.get_build_safe(build_id) 350 except ObjectNotFound: 351 response["result"] = "noop" 352 response["msg"] = "Build {} wasn't found".format(build_id) 353 return flask.jsonify(response) 354 355 if build.canceled: 356 response["result"] = "noop" 357 response["msg"] = "build was cancelled, ignoring" 358 return flask.jsonify(response) 359 360 run_statuses = set([StatusEnum("starting"), StatusEnum("running")]) 361 362 if task_id == build.task_id: 363 if build.source_status in run_statuses: 364 log.info("rescheduling source build %s", build.id) 365 BuildsLogic.update_state_from_dict(build, { 366 "task_id": task_id, 367 "status": StatusEnum("pending") 368 }) 369 db.session.commit() 370 response["result"] = "done" 371 else: 372 response["result"] = "noop" 373 response["msg"] = "build is not in running states, ignoring" 374 else: 375 build_chroot = build.chroots_dict_by_name.get(chroot) 376 if build_chroot and build_chroot.status in run_statuses: 377 log.info("rescheduling build {} chroot: {}".format(build.id, build_chroot.name)) 378 BuildsLogic.update_state_from_dict(build, { 379 "task_id": task_id, 380 "chroot": chroot, 381 "status": StatusEnum("pending") 382 }) 383 db.session.commit() 384 response["result"] = "done" 385 else: 386 response["result"] = "noop" 387 response["msg"] = "build chroot is not in running states, ignoring" 388 389 return flask.jsonify(response)
390
391 @backend_ns.route("/chroots-prunerepo-status/") 392 -def chroots_prunerepo_status():
393 return flask.jsonify(MockChrootsLogic.chroots_prunerepo_status())
394
395 @backend_ns.route("/final-prunerepo-done/", methods=["POST", "PUT"]) 396 @misc.backend_authenticated 397 -def final_prunerepo_done():
398 chroots_pruned = flask.request.get_json() 399 return flask.jsonify(MockChrootsLogic.prunerepo_finished(chroots_pruned))
400