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