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