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, CoprChrootsLogic 
 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 copr_chroot = CoprChrootsLogic.get_by_name_safe(task.build.copr, task.mock_chroot.name) 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 120 if copr_chroot.module_toggle_array: 121 array = [{'enable': m} for m in copr_chroot.module_toggle_array] 122 build_record["modules"] = {'toggle': array} 123 124 if short: 125 return build_record 126 127 build_config = BuildConfigLogic.generate_build_config(task.build.copr, task.mock_chroot.name) 128 build_record["repos"] = build_config.get("repos") 129 build_record["buildroot_pkgs"] = build_config.get("additional_packages") 130 build_record["use_bootstrap_container"] = build_config.get("use_bootstrap_container") 131 build_record["with_opts"] = build_config.get("with_opts") 132 build_record["without_opts"] = build_config.get("without_opts") 133 134 except Exception as err: 135 app.logger.exception(err) 136 return None 137 138 return build_record
139
140 141 -def get_srpm_build_record(task):
142 if not task: 143 return None 144 145 if task.source_type_text == "custom": 146 chroot = task.source_json_dict['chroot'] 147 else: 148 chroot = None 149 150 try: 151 build_record = { 152 "task_id": task.task_id, 153 "build_id": task.id, 154 "project_owner": task.copr.owner_name, 155 "project_name": task.copr_name, 156 "project_dirname": task.copr_dirname, 157 "submitter": task.submitter[0], 158 "sandbox": task.sandbox, 159 "source_type": task.source_type, 160 "source_json": task.source_json, 161 "chroot": chroot, 162 } 163 164 except Exception as err: 165 app.logger.exception(err) 166 return None 167 168 return build_record
169
170 171 @backend_ns.route("/pending-action/") 172 -def pending_action():
173 """ 174 Return a single action. 175 """ 176 action_record = None 177 action = actions_logic.ActionsLogic.get_waiting().first() 178 if action: 179 action_record = action.to_dict(options={ 180 "__columns_except__": ["result", "message", "ended_on"] 181 }) 182 return flask.jsonify(action_record)
183
184 185 @backend_ns.route("/pending-actions/") 186 -def pending_actions():
187 'get the list of actions backand should take care of' 188 actions = actions_logic.ActionsLogic.get_waiting() 189 data = [{'id': action.id} for action in actions] 190 return flask.jsonify(data)
191
192 193 @backend_ns.route("/action/<int:action_id>/") 194 -def get_action(action_id):
195 action = actions_logic.ActionsLogic.get(action_id).one() 196 action_record = action.to_dict() 197 return flask.jsonify(action_record)
198
199 200 @backend_ns.route("/pending-action-count/") 201 -def pending_action_count():
202 """ 203 Return pending action count. 204 """ 205 return flask.jsonify(actions_logic.ActionsLogic.get_waiting().count())
206
207 208 @backend_ns.route("/pending-jobs/") 209 -def pending_jobs():
210 """ 211 Return the job queue. 212 """ 213 srpm_tasks = [build for build in BuildsLogic.get_pending_srpm_build_tasks() if not build.blocked] 214 build_records = ( 215 [get_srpm_build_record(task) for task in srpm_tasks] + 216 [get_build_record(task, short=True) for task in BuildsLogic.get_pending_build_tasks()] 217 ) 218 log.info('Selected build records: {}'.format(build_records)) 219 return flask.jsonify(build_records)
220
221 222 @backend_ns.route("/get-build-task/<task_id>") 223 -def get_build_task(task_id):
224 try: 225 task = BuildsLogic.get_build_task(task_id) 226 except MalformedArgumentException: 227 jsonout = flask.jsonify({'msg': 'Invalid task ID'}) 228 jsonout.status_code = 500 229 return jsonout 230 except sqlalchemy.orm.exc.NoResultFound: 231 jsonout = flask.jsonify({'msg': 'Specified task ID not found'}) 232 jsonout.status_code = 404 233 return jsonout 234 build_record = get_build_record(task) 235 return flask.jsonify(build_record)
236
237 238 @backend_ns.route("/get-srpm-build-task/<build_id>") 239 -def get_srpm_build_task(build_id):
240 try: 241 task = BuildsLogic.get_srpm_build_task(build_id) 242 except sqlalchemy.orm.exc.NoResultFound: 243 jsonout = flask.jsonify({'msg': 'Specified task ID not found'}) 244 jsonout.status_code = 404 245 return jsonout 246 build_record = get_srpm_build_record(task) 247 return flask.jsonify(build_record)
248
249 250 @backend_ns.route("/update/", methods=["POST", "PUT"]) 251 @misc.backend_authenticated 252 -def update():
253 result = {} 254 255 request_data = flask.request.json 256 for typ, logic_cls in [("actions", actions_logic.ActionsLogic), 257 ("builds", BuildsLogic)]: 258 259 if typ not in request_data: 260 continue 261 262 to_update = {} 263 for obj in request_data[typ]: 264 to_update[obj["id"]] = obj 265 266 existing = {} 267 for obj in logic_cls.get_by_ids(to_update.keys()).all(): 268 existing[obj.id] = obj 269 270 non_existing_ids = list(set(to_update.keys()) - set(existing.keys())) 271 272 for i, obj in existing.items(): 273 logic_cls.update_state_from_dict(obj, to_update[i]) 274 275 db.session.commit() 276 result.update({"updated_{0}_ids".format(typ): list(existing.keys()), 277 "non_existing_{0}_ids".format(typ): non_existing_ids}) 278 279 return flask.jsonify(result)
280
281 282 @backend_ns.route("/starting_build/", methods=["POST", "PUT"]) 283 @misc.backend_authenticated 284 -def starting_build():
285 """ 286 Check if the build is not cancelled and set it to starting state 287 """ 288 data = flask.request.json 289 290 try: 291 build = ComplexLogic.get_build_safe(data.get('build_id')) 292 except ObjectNotFound: 293 return flask.jsonify({"can_start": False}) 294 295 if build.canceled: 296 return flask.jsonify({"can_start": False}) 297 298 BuildsLogic.update_state_from_dict(build, data) 299 db.session.commit() 300 return flask.jsonify({"can_start": True})
301
302 303 @backend_ns.route("/reschedule_all_running/", methods=["POST", "PUT"]) 304 @misc.backend_authenticated 305 -def reschedule_all_running():
306 to_reschedule = \ 307 BuildsLogic.get_build_tasks(StatusEnum("starting")).all() + \ 308 BuildsLogic.get_build_tasks(StatusEnum("running")).all() 309 310 for build_chroot in to_reschedule: 311 build_chroot.status = StatusEnum("pending") 312 db.session.add(build_chroot) 313 314 to_reschedule = \ 315 BuildsLogic.get_srpm_build_tasks(StatusEnum("starting")).all() + \ 316 BuildsLogic.get_srpm_build_tasks(StatusEnum("running")).all() 317 318 for build in to_reschedule: 319 build.source_status = StatusEnum("pending") 320 db.session.add(build) 321 322 db.session.commit() 323 324 return "OK", 200
325
326 327 @backend_ns.route("/reschedule_build_chroot/", methods=["POST", "PUT"]) 328 @misc.backend_authenticated 329 -def reschedule_build_chroot():
330 response = {} 331 build_id = flask.request.json.get("build_id") 332 task_id = flask.request.json.get("task_id") 333 chroot = flask.request.json.get("chroot") 334 335 try: 336 build = ComplexLogic.get_build_safe(build_id) 337 except ObjectNotFound: 338 response["result"] = "noop" 339 response["msg"] = "Build {} wasn't found".format(build_id) 340 return flask.jsonify(response) 341 342 if build.canceled: 343 response["result"] = "noop" 344 response["msg"] = "build was cancelled, ignoring" 345 return flask.jsonify(response) 346 347 run_statuses = set([StatusEnum("starting"), StatusEnum("running")]) 348 349 if task_id == build.task_id: 350 if build.source_status in run_statuses: 351 log.info("rescheduling srpm build {}".format(build.id)) 352 BuildsLogic.update_state_from_dict(build, { 353 "task_id": task_id, 354 "status": StatusEnum("pending") 355 }) 356 db.session.commit() 357 response["result"] = "done" 358 else: 359 response["result"] = "noop" 360 response["msg"] = "build is not in running states, ignoring" 361 else: 362 build_chroot = build.chroots_dict_by_name.get(chroot) 363 if build_chroot and build_chroot.status in run_statuses: 364 log.info("rescheduling build {} chroot: {}".format(build.id, build_chroot.name)) 365 BuildsLogic.update_state_from_dict(build, { 366 "task_id": task_id, 367 "chroot": chroot, 368 "status": StatusEnum("pending") 369 }) 370 db.session.commit() 371 response["result"] = "done" 372 else: 373 response["result"] = "noop" 374 response["msg"] = "build chroot is not in running states, ignoring" 375 376 return flask.jsonify(response)
377
378 @backend_ns.route("/chroots-prunerepo-status/") 379 -def chroots_prunerepo_status():
380 return flask.jsonify(MockChrootsLogic.chroots_prunerepo_status())
381
382 @backend_ns.route("/final-prunerepo-done/", methods=["POST", "PUT"]) 383 @misc.backend_authenticated 384 -def final_prunerepo_done():
385 chroots_pruned = flask.request.get_json() 386 return flask.jsonify(MockChrootsLogic.prunerepo_finished(chroots_pruned))
387