Repository :
http://git.fedorahosted.org/cgit/copr.git
On branch : master
---------------------------------------------------------------
commit 6548b5cdf68a15f7e50b907ef708de792c8aed47
Author: Valentin Gologuzov <vgologuz(a)redhat.com>
Date: Fri Aug 28 12:12:21 2015 +0200
[frontend][api] Implemented and tested build creation. (POST to /builds)
---------------------------------------------------------------
.../coprs_frontend/coprs/logic/builds_logic.py | 49 +++++--
frontend/coprs_frontend/coprs/logic/coprs_logic.py | 12 +-
.../coprs/rest_api/resources/build.py | 39 ++++-
frontend/coprs_frontend/coprs/rest_api/schemas.py | 2 +-
.../coprs/views/coprs_ns/coprs_builds.py | 57 +++++---
.../coprs_frontend/tests/test_api/test_build_r.py | 152 +++++++++++++++++++-
6 files changed, 264 insertions(+), 47 deletions(-)
diff --git a/frontend/coprs_frontend/coprs/logic/builds_logic.py
b/frontend/coprs_frontend/coprs/logic/builds_logic.py
index cd21a6d..250f82e 100644
--- a/frontend/coprs_frontend/coprs/logic/builds_logic.py
+++ b/frontend/coprs_frontend/coprs/logic/builds_logic.py
@@ -174,10 +174,45 @@ class BuildsLogic(object):
return models.Build.query.get(build_id)
@classmethod
+ def create_new_from_url(cls, user, copr, srpm_url,
+ chroot_names=None, **build_options):
+ """
+ :type user: models.User
+ :type copr: models.Copr
+
+ :type chroot_names: List[str]
+
+ :rtype: models.Build
+ """
+ # check which chroots we need
+ chroots = []
+ for chroot in copr.active_chroots:
+ if chroot.name in chroot_names:
+ chroots.append(chroot)
+
+ source_type = helpers.BuildSourceEnum("srpm_link")
+ source_json = json.dumps({"url": srpm_url})
+
+ # try:
+ build = cls.add(
+ user=user,
+ pkgs=srpm_url,
+ copr=copr,
+ chroots=chroots,
+ source_type=source_type,
+ source_json=source_json,
+ enable_net=build_options.get("enabled_net", True))
+
+ if user.proven:
+ if "timeout" in build_options:
+ build.timeout = build_options["timeout"]
+
+ return build
+
+ @classmethod
def create_new_from_upload(cls, user, copr, f_uploader, orig_filename,
chroot_names=None, **build_options):
"""
-
:type user: models.User
:type copr: models.Copr
:param f_uploader(file_path): function which stores data at the given
`file_path`
@@ -195,7 +230,6 @@ class BuildsLogic(object):
tmp_dir=tmp_name,
srpm=filename)
- # import ipdb; ipdb.set_trace()
# check which chroots we need
chroots = []
for chroot in copr.active_chroots:
@@ -218,19 +252,16 @@ class BuildsLogic(object):
if user.proven:
if "timeout" in build_options:
build.timeout = build_options["timeout"]
- except (ActionInProgressException, InsufficientRightsException) as e:
- db.session.rollback()
- shutil.rmtree(tmp)
+
+ except Exception:
+ shutil.rmtree(tmp) # todo: maybe we should delete in some cleanup
procedure?
raise
- else:
- flask.flash("New build has been created.")
return build
@classmethod
def add(cls, user, pkgs, copr, source_type=None, source_json=None,
- repos=None, chroots=None,
- memory_reqs=None, timeout=None, enable_net=True,
+ repos=None, chroots=None, timeout=None, enable_net=True,
git_hashes=None):
if chroots is None:
chroots = []
diff --git a/frontend/coprs_frontend/coprs/logic/coprs_logic.py
b/frontend/coprs_frontend/coprs/logic/coprs_logic.py
index 35e9b96..0957462 100644
--- a/frontend/coprs_frontend/coprs/logic/coprs_logic.py
+++ b/frontend/coprs_frontend/coprs/logic/coprs_logic.py
@@ -209,6 +209,13 @@ class CoprsLogic(object):
copr, "Can't delete this project,"
" another operation is in progress: {action}")
+ cls.create_delete_action(copr)
+ copr.deleted = True
+
+ return copr
+
+ @classmethod
+ def create_delete_action(cls, copr):
action = models.Action(action_type=helpers.ActionTypeEnum("delete"),
object_type="copr",
object_id=copr.id,
@@ -216,11 +223,8 @@ class CoprsLogic(object):
copr.name),
new_value="",
created_on=int(time.time()))
- copr.deleted = True
-
db.session.add(action)
-
- return copr
+ return action
@classmethod
def exists_for_user(cls, user, coprname, incl_deleted=False):
diff --git a/frontend/coprs_frontend/coprs/rest_api/resources/build.py
b/frontend/coprs_frontend/coprs/rest_api/resources/build.py
index c5eab02..5c2a2b5 100644
--- a/frontend/coprs_frontend/coprs/rest_api/resources/build.py
+++ b/frontend/coprs_frontend/coprs/rest_api/resources/build.py
@@ -54,7 +54,7 @@ class BuildListR(Resource):
else:
query = BuildsLogic.get_multiple()
- if "limit" in req_args:
+ if req_args["limit"] is not None:
limit = req_args["limit"]
if limit <= 0 or limit > 100:
limit = 100
@@ -63,7 +63,7 @@ class BuildListR(Resource):
query = query.limit(limit)
- if "offset" in req_args:
+ if req_args["offset"] is not None:
query = query.offset(req_args["offset"])
builds = query.all()
@@ -84,11 +84,34 @@ class BuildListR(Resource):
"""
:return: if of the created build or raise Exception
"""
- build_data = mm_deserialize(BuildCreateFromUrlSchema(), req.data)
- raise NotImplementedError()
+ build_params = mm_deserialize(BuildCreateFromUrlSchema(), req.data).data
+ project_id = build_params["project_id"]
+ project = get_one_safe(CoprsLogic.get_by_id(project_id))
+ """:type : models.Copr """
+ chroot_names = build_params.pop("chroots")
+ srpm_url = build_params.pop("srpm_url")
+ try:
+ build = BuildsLogic.create_new_from_url(
+ flask.g.user, project,
+ srpm_url=srpm_url,
+ chroot_names=chroot_names,
+ **build_params
+ )
+ db.session.commit()
+ except ActionInProgressException as err:
+ db.session.rollback()
+ raise CannotProcessRequest("Cannot create new build due to: {}"
+ .format(err))
+ except InsufficientRightsException as err:
+ db.session.rollback()
+ raise AccessForbidden("User {} cannon create build in project {}:
{}"
+ .format(flask.g.user.username,
+ project.full_name, err))
+ return build.id
- def handle_post_multipart(self, req):
+ @staticmethod
+ def handle_post_multipart(req):
"""
:return: if of the created build or raise Exception
"""
@@ -118,12 +141,14 @@ class BuildListR(Resource):
)
db.session.commit()
except ActionInProgressException as err:
+ db.session.rollback()
raise CannotProcessRequest("Cannot create new build due to: {}"
.format(err))
except InsufficientRightsException as err:
- raise AccessForbidden("User {} cannon create build in project {}"
+ db.session.rollback()
+ raise AccessForbidden("User {} cannon create build in project {}:
{}"
.format(flask.g.user.username,
- project.full_name))
+ project.full_name, err))
return build.id
diff --git a/frontend/coprs_frontend/coprs/rest_api/schemas.py
b/frontend/coprs_frontend/coprs/rest_api/schemas.py
index 057660b..26f4452 100644
--- a/frontend/coprs_frontend/coprs/rest_api/schemas.py
+++ b/frontend/coprs_frontend/coprs/rest_api/schemas.py
@@ -105,4 +105,4 @@ class BuildCreateSchema(BuildSchema):
class BuildCreateFromUrlSchema(BuildCreateSchema):
- source_url = fields.Url()
+ srpm_url = fields.Url(required=True, validate=lambda u:
u.startswith("http"))
diff --git a/frontend/coprs_frontend/coprs/views/coprs_ns/coprs_builds.py
b/frontend/coprs_frontend/coprs/views/coprs_ns/coprs_builds.py
index 40ef9bb..2dffef3 100644
--- a/frontend/coprs_frontend/coprs/views/coprs_ns/coprs_builds.py
+++ b/frontend/coprs_frontend/coprs/views/coprs_ns/coprs_builds.py
@@ -148,7 +148,10 @@ def copr_new_build_upload(username, coprname):
)
db.session.commit()
except (ActionInProgressException, InsufficientRightsException) as e:
+ db.session.rollback()
flask.flash(str(e), "error")
+ else:
+ flask.flash("New build has been created.")
return flask.redirect(flask.url_for("coprs_ns.copr_builds",
username=username,
@@ -173,31 +176,43 @@ def copr_new_build(username, coprname):
if not pkgs:
flask.flash("No builds submitted")
else:
- # check which chroots we need
- chroots = []
- for chroot in copr.active_chroots:
- if chroot.name in form.selected_chroots:
- chroots.append(chroot)
+ # # check which chroots we need
+ # chroots = []
+ # for chroot in copr.active_chroots:
+ # if chroot.name in form.selected_chroots:
+ # chroots.append(chroot)
# build each package as a separate build
try:
for pkg in pkgs:
- # create json describing the build source
- source_type = helpers.BuildSourceEnum("srpm_link")
- source_json = json.dumps({"url": pkg})
-
- build = BuildsLogic.add(
- user=flask.g.user,
- pkgs=pkg,
- copr=copr,
- chroots=chroots,
- source_type=source_type,
- source_json=source_json,
- enable_net=form.enable_net.data)
-
- if flask.g.user.proven:
- build.memory_reqs = form.memory_reqs.data
- build.timeout = form.timeout.data
+ build_options = {
+ "enable_net": form.enable_net.data,
+ "timeout": form.timeout.data,
+ }
+ BuildsLogic.create_new_from_url(
+ flask.g.user, copr, pkg,
+ chroot_names=form.selected_chroots,
+ **build_options
+ )
+
+ # # create json describing the build source
+ # source_type = helpers.BuildSourceEnum("srpm_link")
+ # source_json = json.dumps({"url": pkg})
+
+
+
+ # build = BuildsLogic.add(
+ # user=flask.g.user,
+ # pkgs=pkg,
+ # copr=copr,
+ # chroots=chroots,
+ # source_type=source_type,
+ # source_json=source_json,
+ # enable_net=form.enable_net.data)
+ #
+ # if flask.g.user.proven:
+ # build.memory_reqs = form.memory_reqs.data
+ # build.timeout = form.timeout.data
except (ActionInProgressException, InsufficientRightsException) as e:
flask.flash(str(e), "error")
diff --git a/frontend/coprs_frontend/tests/test_api/test_build_r.py
b/frontend/coprs_frontend/tests/test_api/test_build_r.py
index 5aefd36..0d9c9b9 100644
--- a/frontend/coprs_frontend/tests/test_api/test_build_r.py
+++ b/frontend/coprs_frontend/tests/test_api/test_build_r.py
@@ -7,6 +7,7 @@ import math
import pytest
import sqlalchemy
+from coprs import models
from coprs.helpers import BuildSourceEnum
from coprs.logic.users_logic import UsersLogic
@@ -110,16 +111,57 @@ class TestBuildResource(CoprsTestCase):
assert builds[:delta] == obj1["builds"]
assert builds[delta:2 * delta] == obj2["builds"]
- # todo: implement
- def _test_post_json(
- self, f_users, f_coprs, f_builds, f_db, f_mock_chroots,
+ # for coverage
+ href = "/api_2/builds?limit={}".format(1000000)
+ r = self.tc.get(href)
+ assert r.status_code == 200
+
+ def test_post_bad_content_type(
+ self, f_users, f_coprs, f_db, f_mock_chroots,
+ f_mock_chroots_many, f_build_many_chroots,
+ f_users_api):
+ chroot_name_list = [c.name for c in self.c1.active_chroots]
+ self.db.session.commit()
+ metadata = {
+ "project_id": 1,
+ "srpm_url": "http://example.com/mypkg.src.rpm",
+ "chroots": chroot_name_list
+ }
+ r0 = self.request_rest_api_with_auth(
+ "/api_2/builds",
+ method="post",
+ content_type="plain/test"
+ )
+ assert r0.status_code == 400
+
+ def test_post_json_bad_url(
+ self, f_users, f_coprs, f_db, f_mock_chroots,
+ f_mock_chroots_many, f_build_many_chroots,
+ f_users_api):
+ chroot_name_list = [c.name for c in self.c1.active_chroots]
+ for url in [None, "", "dsafasdga",
"gopher://mp.src.rpm"]:
+ metadata = {
+ "project_id": 1,
+ "srpm_url": url,
+ "chroots": chroot_name_list
+ }
+ self.db.session.commit()
+ r0 = self.request_rest_api_with_auth(
+ "/api_2/builds",
+ method="post",
+ content=metadata
+ )
+ assert r0.status_code == 400
+
+ def test_post_json(
+ self, f_users, f_coprs, f_db, f_mock_chroots,
f_mock_chroots_many, f_build_many_chroots,
f_users_api):
chroot_name_list = [c.name for c in self.c1.active_chroots]
metadata = {
"project_id": 1,
- "url": "http://example.com/mypkg.src.rpm",
+ "srpm_url": "http://example.com/mypkg.src.rpm",
"chroots": chroot_name_list
}
self.db.session.commit()
@@ -128,8 +170,108 @@ class TestBuildResource(CoprsTestCase):
method="post",
content=metadata
)
- print(r0.data)
assert r0.status_code == 201
+ r1 = self.tc.get(r0.headers["Location"])
+ assert r1.status_code == 200
+ build_obj = json.loads(r1.data)
+ build_dict = build_obj["build"]
+ assert json.loads(build_dict["source_json"])["url"] == \
+ metadata["srpm_url"]
+ assert build_dict["source_type"] ==
BuildSourceEnum("srpm_link")
+
+ def test_post_json_on_wrong_user(
+ self, f_users, f_coprs, f_db, f_mock_chroots,
+ f_mock_chroots_many, f_build_many_chroots,
+ f_users_api):
+
+ login = self.u2.api_login
+ token = self.u2.api_token
+
+ chroot_name_list = [c.name for c in self.c1.active_chroots]
+ metadata = {
+ "project_id": 1,
+ "srpm_url": "http://example.com/mypkg.src.rpm",
+ "chroots": chroot_name_list
+ }
+ self.db.session.commit()
+ r0 = self.request_rest_api_with_auth(
+ "/api_2/builds",
+ method="post",
+ login=login, token=token,
+ content=metadata,
+ )
+ assert r0.status_code == 403
+
+ def test_post_json_on_project_during_action(
+ self, f_users, f_coprs, f_db, f_mock_chroots,
+ f_mock_chroots_many, f_build_many_chroots,
+ f_users_api):
+
+ CoprsLogic.create_delete_action(self.c1)
+ chroot_name_list = [c.name for c in self.c1.active_chroots]
+ metadata = {
+ "project_id": 1,
+ "srpm_url": "http://example.com/mypkg.src.rpm",
+ "chroots": chroot_name_list
+ }
+ self.db.session.commit()
+ r0 = self.request_rest_api_with_auth(
+ "/api_2/builds",
+ method="post",
+ content=metadata,
+ )
+ assert r0.status_code == 400
+
+ def test_post_multipart_wrong_user(
+ self, f_users, f_coprs, f_builds, f_db, f_mock_chroots,
+ f_mock_chroots_many, f_build_many_chroots,
+ f_users_api):
+ chroot_name_list = [c.name for c in self.c1.active_chroots]
+ metadata = {
+ "project_id": 1,
+ "enable_net": True,
+ "chroots": chroot_name_list
+ }
+ data = {
+ "metadata": json.dumps(metadata),
+ "srpm": (StringIO(u'my file contents'), 'hello
world.src.rpm')
+ }
+ login = self.u2.api_login
+ token = self.u2.api_token
+ self.db.session.commit()
+ r0 = self.request_rest_api_with_auth(
+ "/api_2/builds",
+ method="post",
+ login=login, token=token,
+ content_type="multipart/form-data",
+ data=data
+ )
+ assert r0.status_code == 403
+
+ def test_post_multipart_on_project_during_action(
+ self, f_users, f_coprs, f_builds, f_db, f_mock_chroots,
+ f_mock_chroots_many, f_build_many_chroots,
+ f_users_api):
+
+ CoprsLogic.create_delete_action(self.c1)
+ chroot_name_list = [c.name for c in self.c1.active_chroots]
+ metadata = {
+ "project_id": 1,
+ "enable_net": True,
+ "chroots": chroot_name_list
+ }
+ data = {
+ "metadata": json.dumps(metadata),
+ "srpm": (StringIO(u'my file contents'), 'hello
world.src.rpm')
+ }
+ self.db.session.commit()
+ r0 = self.request_rest_api_with_auth(
+ "/api_2/builds",
+ method="post",
+ content_type="multipart/form-data",
+ data=data
+ )
+ assert r0.status_code == 400
def test_post_multipart(
self, f_users, f_coprs, f_builds, f_db, f_mock_chroots,