Package coprs :: Package views :: Package apiv3_ns :: Module apiv3_builds
[hide private]
[frames] | no frames]

Source Code for Module coprs.views.apiv3_ns.apiv3_builds

  1  import os 
  2  import flask 
  3   
  4  from werkzeug.datastructures import MultiDict 
  5  from werkzeug.utils import secure_filename 
  6   
  7  from . import get_copr, file_upload, query_params, pagination, Paginator, json2form, GET, POST, PUT, DELETE 
  8  from .json2form import get_form_compatible_data 
  9  from copr_common.enums import StatusEnum 
 10  from coprs import db, forms, models 
 11  from coprs.exceptions import (BadRequest, AccessRestricted) 
 12  from coprs.views.misc import api_login_required 
 13  from coprs.views.apiv3_ns import apiv3_ns 
 14  from coprs.logic.complex_logic import ComplexLogic 
 15  from coprs.logic.builds_logic import BuildsLogic 
16 17 18 -def to_dict(build):
19 return { 20 "id": build.id, 21 "state": build.state, 22 "projectname": build.copr.name, 23 "ownername": build.copr.owner_name, 24 "repo_url": build.copr.repo_url, 25 "source_package": {"name": build.package_name, "version": build.pkg_version, "url": build.srpm_url}, 26 "submitted_on": build.submitted_on, 27 "started_on": build.min_started_on, 28 "ended_on": build.max_ended_on, 29 "submitter": build.user.name if build.user else None, 30 "chroots": [chroot.name for chroot in build.build_chroots], 31 "project_dirname": build.copr_dir.name, 32 }
33
34 35 -def to_source_chroot(build):
36 return { 37 "state": StatusEnum(build.source_status), 38 "result_url": os.path.dirname(build.source_live_log_url), 39 # @TODO Do we have such information stored? 40 # "started_on": None, 41 # "ended_on": None 42 }
43
44 45 -def to_source_build_config(build):
46 return { 47 "source_type": build.source_type_text, 48 "source_dict": build.source_json_dict, 49 "memory_limit": build.memory_reqs, 50 "timeout": build.timeout, 51 "is_background": build.is_background, 52 }
53
54 55 -def rename_fields(input):
56 replace = { 57 "source_build_method": "srpm_build_method", 58 } 59 output = input.copy() 60 for from_name, to_name in replace.items(): 61 if from_name not in output: 62 continue 63 output[to_name] = output.pop(from_name) 64 return output
65
66 67 -def render_build(build):
68 return flask.jsonify(to_dict(build))
69
70 71 @apiv3_ns.route("/build/<int:build_id>/", methods=GET) 72 -def get_build(build_id):
73 build = ComplexLogic.get_build_safe(build_id) 74 return render_build(build)
75
76 77 @apiv3_ns.route("/build/list/", methods=GET) 78 @pagination() 79 @query_params() 80 -def get_build_list(ownername, projectname, packagename=None, status=None, **kwargs):
81 copr = get_copr(ownername, projectname) 82 query = BuildsLogic.get_multiple_by_copr(copr) 83 if packagename: 84 query = BuildsLogic.filter_by_package_name(query, packagename) 85 86 # WORKAROUND 87 # We can't filter builds by status directly in the database, because we 88 # use a logic in Build.status property to determine a build status. 89 # Therefore if we want to filter by `status`, we need to query all builds 90 # and filter them in the application and then return the desired number. 91 limit = kwargs["limit"] 92 paginator_limit = None if status else kwargs["limit"] 93 del kwargs["limit"] 94 95 paginator = Paginator(query, models.Build, limit=paginator_limit, **kwargs) 96 builds = paginator.map(to_dict) 97 98 if status: 99 builds = [b for b in builds if b["state"] == status][:limit] 100 paginator.limit = limit 101 102 return flask.jsonify(items=builds, meta=paginator.meta)
103
104 105 @apiv3_ns.route("/build/source-chroot/<int:build_id>/", methods=GET) 106 -def get_source_chroot(build_id):
107 build = ComplexLogic.get_build_safe(build_id) 108 return flask.jsonify(to_source_chroot(build))
109
110 111 @apiv3_ns.route("/build/source-build-config/<int:build_id>/", methods=GET) 112 -def get_source_build_config(build_id):
113 build = ComplexLogic.get_build_safe(build_id) 114 return flask.jsonify(to_source_build_config(build))
115
116 117 @apiv3_ns.route("/build/cancel/<int:build_id>", methods=PUT) 118 @api_login_required 119 -def cancel_build(build_id):
120 build = ComplexLogic.get_build_safe(build_id) 121 BuildsLogic.cancel_build(flask.g.user, build) 122 db.session.commit() 123 return render_build(build)
124
125 126 @apiv3_ns.route("/build/create/url", methods=POST) 127 @api_login_required 128 -def create_from_url():
129 copr = get_copr() 130 data = get_form_compatible_data() 131 form = forms.BuildFormUrlFactory(copr.active_chroots)(data, meta={'csrf': False}) 132 133 def create_new_build(options): 134 # create separate build for each package 135 pkgs = form.pkgs.data.split("\n") 136 return [BuildsLogic.create_new_from_url( 137 flask.g.user, copr, 138 url=pkg, 139 **options, 140 ) for pkg in pkgs]
141 return process_creating_new_build(copr, form, create_new_build) 142
143 144 @apiv3_ns.route("/build/create/upload", methods=POST) 145 @api_login_required 146 @file_upload() 147 -def create_from_upload():
148 copr = get_copr() 149 data = get_form_compatible_data() 150 form = forms.BuildFormUploadFactory(copr.active_chroots)(data, meta={'csrf': False}) 151 152 def create_new_build(options): 153 return BuildsLogic.create_new_from_upload( 154 flask.g.user, copr, 155 f_uploader=lambda path: form.pkgs.data.save(path), 156 orig_filename=secure_filename(form.pkgs.data.filename), 157 **options, 158 )
159 return process_creating_new_build(copr, form, create_new_build) 160
161 162 @apiv3_ns.route("/build/create/scm", methods=POST) 163 @api_login_required 164 -def create_from_scm():
165 copr = get_copr() 166 data = rename_fields(get_form_compatible_data()) 167 form = forms.BuildFormScmFactory(copr.active_chroots)(data, meta={'csrf': False}) 168 169 def create_new_build(options): 170 return BuildsLogic.create_new_from_scm( 171 flask.g.user, 172 copr, 173 scm_type=form.scm_type.data, 174 clone_url=form.clone_url.data, 175 committish=form.committish.data, 176 subdirectory=form.subdirectory.data, 177 spec=form.spec.data, 178 srpm_build_method=form.srpm_build_method.data, 179 **options, 180 )
181 return process_creating_new_build(copr, form, create_new_build) 182
183 @apiv3_ns.route("/build/create/distgit", methods=POST) 184 @api_login_required 185 -def create_from_distgit():
186 """ 187 route for v3.proxies.create_from_distgit() call 188 """ 189 copr = get_copr() 190 data = rename_fields(get_form_compatible_data()) 191 # pylint: disable=not-callable 192 form = forms.BuildFormDistGitSimpleFactory(copr.active_chroots)(data, meta={'csrf': False}) 193 194 def create_new_build(options): 195 return BuildsLogic.create_new_from_distgit( 196 flask.g.user, 197 copr, 198 package_name=form.package_name.data, 199 distgit_name=form.distgit.data, 200 distgit_namespace=form.namespace.data, 201 committish=form.committish.data, 202 **options, 203 )
204 return process_creating_new_build(copr, form, create_new_build) 205
206 @apiv3_ns.route("/build/create/pypi", methods=POST) 207 @api_login_required 208 -def create_from_pypi():
209 copr = get_copr() 210 data = MultiDict(json2form.without_empty_fields(json2form.get_input())) 211 form = forms.BuildFormPyPIFactory(copr.active_chroots)(data, meta={'csrf': False}) 212 213 # TODO: automatically prepopulate all form fields with their defaults 214 if not form.python_versions.data: 215 form.python_versions.data = form.python_versions.default 216 217 def create_new_build(options): 218 return BuildsLogic.create_new_from_pypi( 219 flask.g.user, 220 copr, 221 form.pypi_package_name.data, 222 form.pypi_package_version.data, 223 form.spec_template.data, 224 form.python_versions.data, 225 **options, 226 )
227 return process_creating_new_build(copr, form, create_new_build) 228
229 230 @apiv3_ns.route("/build/create/rubygems", methods=POST) 231 @api_login_required 232 -def create_from_rubygems():
233 copr = get_copr() 234 data = get_form_compatible_data() 235 form = forms.BuildFormRubyGemsFactory(copr.active_chroots)(data, meta={'csrf': False}) 236 237 def create_new_build(options): 238 return BuildsLogic.create_new_from_rubygems( 239 flask.g.user, 240 copr, 241 form.gem_name.data, 242 **options, 243 )
244 return process_creating_new_build(copr, form, create_new_build) 245
246 247 @apiv3_ns.route("/build/create/custom", methods=POST) 248 @api_login_required 249 -def create_from_custom():
250 copr = get_copr() 251 data = get_form_compatible_data() 252 form = forms.BuildFormCustomFactory(copr.active_chroots)(data, meta={'csrf': False}) 253 254 def create_new_build(options): 255 return BuildsLogic.create_new_from_custom( 256 flask.g.user, 257 copr, 258 form.script.data, 259 form.chroot.data, 260 form.builddeps.data, 261 form.resultdir.data, 262 **options, 263 )
264 return process_creating_new_build(copr, form, create_new_build) 265
266 267 -def process_creating_new_build(copr, form, create_new_build):
268 if not form.validate_on_submit(): 269 raise BadRequest("Bad request parameters: {0}".format(form.errors)) 270 271 if not flask.g.user.can_build_in(copr): 272 raise AccessRestricted("User {} is not allowed to build in the copr: {}" 273 .format(flask.g.user.username, copr.full_name)) 274 275 generic_build_options = { 276 'chroot_names': form.selected_chroots, 277 'background': form.background.data, 278 'copr_dirname': form.project_dirname.data, 279 'timeout': form.timeout.data, 280 'bootstrap': form.bootstrap.data, 281 'after_build_id': form.after_build_id.data, 282 'with_build_id': form.with_build_id.data, 283 } 284 285 # From URLs it can be created multiple builds at once 286 # so it can return a list 287 build = create_new_build(generic_build_options) 288 db.session.commit() 289 290 if type(build) == list: 291 builds = [build] if type(build) != list else build 292 return flask.jsonify(items=[to_dict(b) for b in builds], meta={}) 293 return flask.jsonify(to_dict(build))
294
295 296 @apiv3_ns.route("/build/delete/<int:build_id>", methods=DELETE) 297 @api_login_required 298 -def delete_build(build_id):
299 build = ComplexLogic.get_build_safe(build_id) 300 build_dict = to_dict(build) 301 BuildsLogic.delete_build(flask.g.user, build) 302 db.session.commit() 303 return flask.jsonify(build_dict)
304
305 306 @apiv3_ns.route("/build/delete/list", methods=POST) 307 @api_login_required 308 -def delete_builds():
309 """ 310 Delete builds specified by a list of IDs. 311 """ 312 build_ids = flask.request.json["builds"] 313 BuildsLogic.delete_builds(flask.g.user, build_ids) 314 db.session.commit() 315 return flask.jsonify({"builds": build_ids})
316