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