1 import flask
2 import time
3 import sqlalchemy
4
5 from coprs import db, app
6 from coprs import helpers
7 from coprs import models
8 from coprs import exceptions
9 from coprs.helpers import StatusEnum
10 from coprs.logic import actions_logic
11 from coprs.logic.builds_logic import BuildsLogic
12 from coprs.logic.complex_logic import ComplexLogic
13 from coprs.logic.coprs_logic import CoprChrootsLogic
14 from coprs.logic.packages_logic import PackagesLogic
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 logging
21 log = logging.getLogger(__name__)
56
57
58 @backend_ns.route("/import-completed/", methods=["POST", "PUT"])
61 """
62 Mark BuildChroot in a Build as uploaded, which means:
63 - set it to pending state
64 - set BuildChroot.git_hash
65 - if it's the last BuildChroot in a Build:
66 - delete local source
67 BuildChroot is identified with task_id which is build id + git branch name
68 - For example: 56-f22 -> build 55, chroots fedora-22-*
69 """
70 result = {"updated": False}
71
72 if "task_id" in flask.request.json and 'branch' in flask.request.json:
73 app.logger.debug(flask.request.data)
74 task_id = flask.request.json["task_id"]
75 branch = flask.request.json["branch"]
76 build_chroots = BuildsLogic.get_buildchroots_by_build_id_and_branch(task_id, branch)
77 build = build_chroots[0].build
78
79
80 if "git_hash" in flask.request.json and "repo_name" in flask.request.json:
81 git_hash = flask.request.json["git_hash"]
82 pkg_name = flask.request.json["pkg_name"]
83 pkg_version = flask.request.json["pkg_version"]
84
85
86 if not PackagesLogic.get(build.copr.id, pkg_name).first():
87 try:
88 package = PackagesLogic.add(build.copr.user, build.copr, pkg_name, build.source_type, build.source_json)
89 db.session.add(package)
90 db.session.commit()
91 except (sqlalchemy.exc.IntegrityError, exceptions.DuplicateException) as e:
92 db.session.rollback()
93
94 package = PackagesLogic.get(build.copr.id, pkg_name).first()
95 build.package_id = package.id
96 build.pkg_version = pkg_version
97
98 for ch in build_chroots:
99 if ch.status == helpers.StatusEnum("importing"):
100 ch.status = helpers.StatusEnum("pending")
101 ch.git_hash = git_hash
102
103
104 elif "error" in flask.request.json:
105 error_type = flask.request.json["error"]
106
107 try:
108 build.fail_type = helpers.FailTypeEnum(error_type)
109 except KeyError:
110 build.fail_type = helpers.FailTypeEnum("unknown_error")
111
112 for ch in build_chroots:
113 ch.status = helpers.StatusEnum("failed")
114
115
116 if not build.has_importing_chroot:
117 BuildsLogic.delete_local_source(build)
118
119 db.session.commit()
120
121 result.update({"updated": True})
122
123 return flask.jsonify(result)
124
126 build_config = helpers.generate_build_config(task.build.copr, task.mock_chroot.name)
127
128 build_record = None
129 try:
130 build_record = {
131 "task_id": task.task_id,
132 "build_id": task.build.id,
133 "project_owner": task.build.copr.owner_name,
134 "project_name": task.build.copr.name,
135 "submitter": task.build.user.name if task.build.user else None,
136 "chroot": task.mock_chroot.name,
137
138 "repos": task.build.repos,
139 "memory_reqs": task.build.memory_reqs,
140 "timeout": task.build.timeout,
141 "enable_net": task.build.enable_net,
142 "git_repo": task.build.package.dist_git_repo,
143 "git_hash": task.git_hash,
144 "git_branch": task.mock_chroot.distgit_branch_name,
145 "package_name": task.build.package.name,
146 "package_version": task.build.pkg_version,
147 "repos": build_config.get("repos"),
148 "buildroot_pkgs": build_config.get("additional_packages"),
149 "use_bootstrap_container": build_config.get("use_bootstrap_container")
150 }
151
152 except Exception as err:
153 app.logger.exception(err)
154 return build_record
155
156 @backend_ns.route("/waiting/")
157
158 -def waiting():
177
178
179 @backend_ns.route("/get-build-task/<task_id>")
180 -def get_build_task(task_id):
181 try:
182 task = BuildsLogic.get_build_task(task_id)
183 except exceptions.MalformedArgumentException:
184 jsonout = flask.jsonify({'msg': 'Invalid task ID'})
185 jsonout.status_code = 500
186 return jsonout
187 except sqlalchemy.orm.exc.NoResultFound:
188 jsonout = flask.jsonify({'msg': 'Specified task ID not found'})
189 jsonout.status_code = 404
190 return jsonout
191 build_record = get_build_record(task)
192 return flask.jsonify(build_record)
193
194
195 @backend_ns.route("/update/", methods=["POST", "PUT"])
198 result = {}
199
200 request_data = flask.request.json
201 for typ, logic_cls in [("actions", actions_logic.ActionsLogic),
202 ("builds", BuildsLogic)]:
203
204 if typ not in request_data:
205 continue
206
207 to_update = {}
208 for obj in request_data[typ]:
209 to_update[obj["id"]] = obj
210
211 existing = {}
212 for obj in logic_cls.get_by_ids(to_update.keys()).all():
213 existing[obj.id] = obj
214
215 non_existing_ids = list(set(to_update.keys()) - set(existing.keys()))
216
217 for i, obj in existing.items():
218 logic_cls.update_state_from_dict(obj, to_update[i])
219
220 db.session.commit()
221 result.update({"updated_{0}_ids".format(typ): list(existing.keys()),
222 "non_existing_{0}_ids".format(typ): non_existing_ids})
223
224 return flask.jsonify(result)
225
226
227 @backend_ns.route("/starting_build/", methods=["POST", "PUT"])
230 """
231 Check if the build is not cancelled and set it to running state
232 """
233
234 result = {"can_start": False}
235
236 if "build_id" in flask.request.json and "chroot" in flask.request.json:
237 build = ComplexLogic.get_build_safe(flask.request.json["build_id"])
238 chroot = flask.request.json.get("chroot")
239
240 if build and chroot and not build.canceled:
241 log.info("mark build {} chroot {} as starting".format(build.id, chroot))
242 BuildsLogic.update_state_from_dict(build, {
243 "chroot": chroot,
244 "status": StatusEnum("starting")
245 })
246 db.session.commit()
247 result["can_start"] = True
248
249 return flask.jsonify(result)
250
251
252 @backend_ns.route("/defer_build/", methods=["POST", "PUT"])
255 """
256 Defer build (keep it out of waiting jobs for some time).
257 """
258
259 result = {"was_deferred": False}
260
261 if "build_id" in flask.request.json and "chroot" in flask.request.json:
262 build = ComplexLogic.get_build_safe(flask.request.json["build_id"])
263 chroot = flask.request.json.get("chroot")
264
265 if build and chroot:
266 log.info("Defer build {}, chroot {}".format(build.id, chroot))
267 BuildsLogic.update_state_from_dict(build, {
268 "chroot": chroot,
269 "last_deferred": int(time.time()),
270 })
271 db.session.commit()
272 result["was_deferred"] = True
273
274 return flask.jsonify(result)
275
276
277 @backend_ns.route("/reschedule_all_running/", methods=["POST"])
295
296
297 @backend_ns.route("/reschedule_build_chroot/", methods=["POST", "PUT"])
300 response = {}
301 if "build_id" in flask.request.json and "chroot" in flask.request.json:
302 build = ComplexLogic.get_build_safe(flask.request.json["build_id"])
303 else:
304 response["result"] = "bad request"
305 response["msg"] = "Request missing `build_id` and/or `chroot`"
306 return flask.jsonify(response)
307
308 if build:
309 if build.canceled:
310 response["result"] = "noop"
311 response["msg"] = "build was cancelled, ignoring"
312 else:
313 chroot = flask.request.json["chroot"]
314 build_chroot = build.chroots_dict_by_name.get(chroot)
315 run_statuses = set([StatusEnum("starting"), StatusEnum("running")])
316 if build_chroot and build_chroot.status in run_statuses:
317 log.info("rescheduling build {} chroot: {}".format(build.id, build_chroot.name))
318 BuildsLogic.update_state_from_dict(build, {
319 "chroot": chroot,
320 "status": StatusEnum("pending")
321 })
322 db.session.commit()
323 response["result"] = "done"
324 else:
325 response["result"] = "noop"
326 response["msg"] = "build is not in running states, ignoring"
327
328 else:
329 response["result"] = "noop"
330 response["msg"] = "Build {} wasn't found".format(flask.request.json["build_id"])
331
332 return flask.jsonify(response)
333