[copr] master: [frontend][API] covered Build Chroot; minor fixes (b670e8a)
by vgologuz@fedoraproject.org
Repository : http://git.fedorahosted.org/cgit/copr.git
On branch : master
>---------------------------------------------------------------
commit b670e8af2ac9819257a0bc459d81825c03cd11bc
Author: Valentin Gologuzov <vgologuz(a)redhat.com>
Date: Mon Sep 7 15:25:47 2015 +0200
[frontend][API] covered Build Chroot; minor fixes
>---------------------------------------------------------------
.../coprs/rest_api/resources/project_chroot.py | 3 -
frontend/docs/api_2/source/Resources/build.rst | 15 +-
.../docs/api_2/source/Resources/build_chroot.rst | 139 ++++++++++++++++++++
frontend/docs/api_2/source/Resources/project.rst | 9 +-
.../docs/api_2/source/Resources/project_chroot.rst | 6 +-
5 files changed, 154 insertions(+), 18 deletions(-)
diff --git a/frontend/coprs_frontend/coprs/rest_api/resources/project_chroot.py b/frontend/coprs_frontend/coprs/rest_api/resources/project_chroot.py
index e22de4f..ff7a3b1 100644
--- a/frontend/coprs_frontend/coprs/rest_api/resources/project_chroot.py
+++ b/frontend/coprs_frontend/coprs/rest_api/resources/project_chroot.py
@@ -51,9 +51,6 @@ class ProjectChrootListR(Resource):
except (MalformedArgumentException, ObjectNotFoundError) as err:
raise MalformedRequest("Bad mock chroot name: {}. Error: {}".format(name, err))
- if mock_chroot is None:
- raise MalformedRequest("Mock chroot `{}` doesn't exists"
- .format(name))
CoprChrootsLogic.create_chroot(flask.g.user, project, mock_chroot, **req)
try:
db.session.commit()
diff --git a/frontend/docs/api_2/source/Resources/build.rst b/frontend/docs/api_2/source/Resources/build.rst
index 9da70a9..3769808 100644
--- a/frontend/docs/api_2/source/Resources/build.rst
+++ b/frontend/docs/api_2/source/Resources/build.rst
@@ -4,7 +4,7 @@ Build
Build resources allows to submit new builds and access current build progress.
Build in fact consists of the few tasks, one per chroot, and detail information
-is available through sub-resource BuildChroot_.
+is available through sub-resource :doc:`./build_chroot`.
@@ -47,7 +47,7 @@ Build fields
Field Type Description
================== ==================== ===============
id int unique build identifier
-state string current state of the build
+state string current state of the build, value is aggregated from build chroots
submitted_on int(unixtime UTC) time of the build submission
started_on int(unixtime UTC) time when the first build chroot started, otherwise ``null``
ended_on int(unixtime UTC) time when the last build chroot ended, otherwise ``null``
@@ -268,8 +268,8 @@ Get build details
Returns details about build
- :param int project_id: a unique identifier of the build
- :query bool show_chroots: embed BuildChroot_ sub-resources into the result, default is False
+ :param int build_id: a unique identifier of the build
+ :query bool show_chroots: embed :doc:`./build_chroot` sub-resources into the result, default is False
:statuscode 200: no error
:statuscode 404: build not found
@@ -278,7 +278,7 @@ Get build details
.. sourcecode:: http
- GET HTTP/1.1
+ GET /api_2/builds/106897 HTTP/1.1
Host: copr.fedoraproject.org
**Response**
@@ -352,8 +352,7 @@ Build cancellation is done be setting build state to ``cancelled``.
**REQUIRE AUTH**
- :param int project_id: a unique identifier of the build
- :query bool show_chroots: embed BuildChroot_ sub-resources into the result, default is False
+ :param int build_id: a unique identifier of the build
:statuscode 204: build was updated
:statuscode 400: malformed request, most probably build can't be canceled at the moment
@@ -386,7 +385,7 @@ Delete build
Deletes build and schedules deletion of build result at Copr backend
- :param int project_id: a unique identifier of the build
+ :param int build_id: a unique identifier of the build
:statuscode 204: build was removed
:statuscode 400: could not delete build right now, most probably due to unfinished build
diff --git a/frontend/docs/api_2/source/Resources/build_chroot.rst b/frontend/docs/api_2/source/Resources/build_chroot.rst
index e69de29..f767fbe 100644
--- a/frontend/docs/api_2/source/Resources/build_chroot.rst
+++ b/frontend/docs/api_2/source/Resources/build_chroot.rst
@@ -0,0 +1,139 @@
+Build Chroot
+============
+
+Build chroot represents information about individual build tasks for each chroot.
+
+Structure of the build chroot entity
+------------------------------------
+
+.. code-block:: javascript
+
+ {
+ "name": "fedora-rawhide-x86_64",
+ "started_on": 1440753865,
+ "ended_on": 1440753919,
+ "state": "succeeded",
+ "result_dir_url": "http://copr-be-dev.cloud.fedoraproject.org/results/vgologuz/aeghqawgt/fed...",
+ "git_hash": "d241064b14f9dcd5d9032d0aca3b4e78fbd1aafd"
+ }
+
+Build chroot fields
+~~~~~~~~~~~~~~~~~~~
+================== ==================== ===============
+Field Type Description
+================== ==================== ===============
+name str chroot name
+state str current build state
+started_on int(unixtime UTC) time when the build chroot started
+ended_on int(unixtime UTC) time when the build chroot ended
+git_hash str hash of the git commit in dist-git used for the build
+result_dir_url str(URL) location of the build results
+================== ==================== ===============
+
+
+.. note::
+ Build Chroot doesn't currently support any modifications,
+ so all fields are read-only.
+
+List build chroots
+------------------
+
+.. http:get:: /api_2/builds/(int:build_id)/chroots
+
+ Returns list of build chroots contained in the one build
+
+ :param int build_id: a unique identifier of the build
+
+ :statuscode 200: no error
+ :statuscode 404: build not found
+
+ **Example request**
+
+ .. sourcecode:: http
+
+ GET /api_2/builds/106882/chroots HTTP/1.1
+ Host: copr.fedoraproject.org
+
+ **Response**
+
+ .. sourcecode:: http
+
+ HTTP/1.1 200 OK
+ Content-Type: application/json
+
+ {
+ "chroots": [
+ {
+ "chroot": {
+ "name": "fedora-rawhide-x86_64",
+ "started_on": 1440753865,
+ "state": "succeeded",
+ "ended_on": 1440753919,
+ "result_dir_url": "http://copr-be-dev.cloud.fedoraproject.org/results/vgologuz/aeghqawgt/fed...",
+ "git_hash": "d241064b14f9dcd5d9032d0aca3b4e78fbd1aafd"
+ },
+ "_links": {
+ "project": {
+ "href": "/api_2/projects/3985"
+ },
+ "self": {
+ "href": "/api_2/builds/106882/chroots/fedora-rawhide-x86_64"
+ }
+ }
+ }
+ ],
+ "_links": {
+ "self": {
+ "href": "/api_2/builds/106882/chroots"
+ }
+ }
+ }
+
+
+
+Get build chroot details
+------------------------
+
+.. http:get:: /api_2/builds/(int:build_id)/chroots/(str:name)
+
+ Returns details about one build chroot
+
+ :param int build_id: a unique identifier of the build
+ :param str name: chroot name
+
+ :statuscode 200: no error
+ :statuscode 404: build or build chroot not found
+
+ **Example request**
+
+ .. sourcecode:: http
+
+ GET /api_2/builds/106882/chroots/fedora-rawhide-x86_64 HTTP/1.1
+ Host: copr.fedoraproject.org
+
+ **Response**
+
+ .. sourcecode:: http
+
+ HTTP/1.1 200 OK
+ Content-Type: application/json
+
+ {
+ "chroot": {
+ "name": "fedora-rawhide-x86_64",
+ "started_on": 1440753865,
+ "state": "succeeded",
+ "ended_on": 1440753919,
+ "result_dir_url": "http://copr-be-dev.cloud.fedoraproject.org/results/vgologuz/aeghqawgt/fed...",
+ "git_hash": "d241064b14f9dcd5d9032d0aca3b4e78fbd1aafd"
+ },
+ "_links": {
+ "project": {
+ "href": "/api_2/projects/3985"
+ },
+ "self": {
+ "href": "/api_2/builds/106882/chroots/fedora-rawhide-x86_64"
+ }
+ }
+ }
+
diff --git a/frontend/docs/api_2/source/Resources/project.rst b/frontend/docs/api_2/source/Resources/project.rst
index c977f4d..aaad18f 100644
--- a/frontend/docs/api_2/source/Resources/project.rst
+++ b/frontend/docs/api_2/source/Resources/project.rst
@@ -41,6 +41,7 @@ homepage string(URL) yes project homepage
contact string(URL or email) yes contact with the project maintainer
disable_createrepo bool yes disables automatic repository metadata generation
build_enable_net bool yes set default value for new builds option `enable_net`
+repos list of string yes list of additional repositories to be enabled during the build
================== ==================== ========= ===============
List projects
@@ -105,7 +106,7 @@ Create new project
Creates new Copr project.
Additionally to described before `Project fields`_ the user could specify field `chroots` which contains list of chroots to be enabled.
- Available `chroot` names could be obtained from MockChrootResource_
+ Available `chroot` names could be obtained from :doc:`./mock_chroot`
:resheader Location: contains URL to the newly created project entity
@@ -150,8 +151,8 @@ Get project details
:param int project_id: a unique identifier of the Copr project.
- :query bool show_builds: embed Build_ entities owned by this project into the result, default is False
- :query bool show_chroots: embed ProjectChroot_ sub-resources into the result, default is False
+ :query bool show_builds: embed :doc:`./build` entities owned by this project into the result, default is False
+ :query bool show_chroots: embed :doc:`./project_chroot` sub-resources into the result, default is False
:statuscode 200: no error
:statuscode 404: project not found
@@ -266,7 +267,7 @@ Modify project
Updates Copr project.
- .. note:: You couldn't enabled or disable project chroots here, use ProjectChroots_ resource.
+ .. note:: You couldn't enabled or disable project chroots here, use :doc:`./project_chroot` resource.
:param project_id: a unique identifier of the Copr project.
diff --git a/frontend/docs/api_2/source/Resources/project_chroot.rst b/frontend/docs/api_2/source/Resources/project_chroot.rst
index a865afd..a4d6829 100644
--- a/frontend/docs/api_2/source/Resources/project_chroot.rst
+++ b/frontend/docs/api_2/source/Resources/project_chroot.rst
@@ -1,7 +1,7 @@
Project Chroot
==============
-Projects Chroots allows to view and modify project settings dedicated for different chroots.
+Projects Chroots allows to enable and disable target chroots and modify project settings dedicated for specific chroots.
Structure of the project chroot entity
--------------------------------------
@@ -92,7 +92,7 @@ Enable chroot for project
**REQUIRE AUTH**
Enables chroot for the Copr project.
- Available `chroot` names could be obtained from MockChrootResource_
+ Available `chroot` names could be obtained from :doc:`./mock_chroot` resource.
:param int project_id: a unique identifier of the Copr project.
@@ -184,7 +184,7 @@ Disable chroot for project
:param int project_id: a unique identifier of the Copr project.
:param str chroot_name: name of the project chroot
- :statuscode 204: project was removed
+ :statuscode 204: chroot was disabled
:statuscode 403: authorization failed
:statuscode 404: project not found or chroot isn't enabled for the project
8 years, 9 months
[copr] master: fix tests (5e2a175)
by asamalik@fedoraproject.org
Repository : http://git.fedorahosted.org/cgit/copr.git
On branch : master
>---------------------------------------------------------------
commit 5e2a175e652ddde9a3e32500902bee3d3ae4389e
Author: Adam Samalik <asamalik(a)redhat.com>
Date: Mon Sep 7 15:03:00 2015 +0200
fix tests
>---------------------------------------------------------------
.../test_views/test_coprs_ns/test_coprs_builds.py | 7 ++++---
1 files changed, 4 insertions(+), 3 deletions(-)
diff --git a/frontend/coprs_frontend/tests/test_views/test_coprs_ns/test_coprs_builds.py b/frontend/coprs_frontend/tests/test_views/test_coprs_ns/test_coprs_builds.py
index 7d0e1be..42ee9af 100644
--- a/frontend/coprs_frontend/tests/test_views/test_coprs_ns/test_coprs_builds.py
+++ b/frontend/coprs_frontend/tests/test_views/test_coprs_ns/test_coprs_builds.py
@@ -234,8 +234,9 @@ class TestCoprRepeatBuild(CoprsTestCase):
follow_redirects=True)
assert r.status_code == 200
- assert self.b_few_chroots.pkgs in r.data
- assert "Adding Build for user1/foocopr" in r.data
+ # no longer using URL
+ #assert self.b_few_chroots.pkgs in r.data
+ assert "Resubmit build {}".format(self.b_few_chroots.id) in r.data
# TODO: maybe test, that only failed chroots are selected
@@ -263,7 +264,7 @@ class TestCoprRepeatBuild(CoprsTestCase):
data={},
follow_redirects=True)
- assert "Adding Build for user1/foocopr" in r.data
+ assert "Resubmit build {}".format(self.b1.id) in r.data
assert r.status_code == 200
# assert "Build was resubmitted" in r.data
# assert len(self.models.Build.query
8 years, 9 months
[copr] master: [frontend] resubmitting build ... (4e635ea)
by asamalik@fedoraproject.org
Repository : http://git.fedorahosted.org/cgit/copr.git
On branch : master
>---------------------------------------------------------------
commit 4e635ea8e603d652a38a33d0f68b76c8bfe19b0f
Author: Adam Samalik <asamalik(a)redhat.com>
Date: Thu Sep 3 16:10:46 2015 +0200
[frontend] resubmitting build ...
Using resubmit:
- will just rebuild sources from dist git
- allows to use chroots from the original build only
Resubmitting an old build which is not in dist git:
- will try to import it into dist git from URL as a new one
>---------------------------------------------------------------
frontend/coprs_frontend/coprs/forms.py | 43 ++++++++++++++++
.../coprs/logic/.builds_logic.py.swo | Bin 0 -> 16384 bytes
.../coprs_frontend/coprs/logic/builds_logic.py | 48 +++++++++++++++++-
frontend/coprs_frontend/coprs/models.py | 2 -
.../templates/coprs/detail/_builds_forms.html | 12 ++++-
.../templates/coprs/detail/_packages_table.html | 48 ++++++++++++++++++
.../templates/coprs/detail/add_build/rebuild.html | 29 +++++++++++
.../templates/coprs/detail/add_build/url.html | 2 +-
.../coprs/templates/coprs/detail/packages.html | 16 ++++++
.../coprs/views/coprs_ns/coprs_builds.py | 53 ++++++++++++++++++--
10 files changed, 243 insertions(+), 10 deletions(-)
diff --git a/frontend/coprs_frontend/coprs/forms.py b/frontend/coprs_frontend/coprs/forms.py
index 1a33a2b..766ada3 100644
--- a/frontend/coprs_frontend/coprs/forms.py
+++ b/frontend/coprs_frontend/coprs/forms.py
@@ -358,6 +358,49 @@ class BuildFormUploadFactory(object):
return F
+class BuildFormRebuildFactory(object):
+ @staticmethod
+ def create_form_cls(active_chroots):
+ class F(wtf.Form):
+ @property
+ def selected_chroots(self):
+ selected = []
+ for ch in self.chroots_list:
+ if getattr(self, ch).data:
+ selected.append(ch)
+ return selected
+
+ memory_reqs = wtforms.IntegerField(
+ "Memory requirements",
+ validators=[
+ wtforms.validators.NumberRange(
+ min=constants.MIN_BUILD_MEMORY,
+ max=constants.MAX_BUILD_MEMORY)],
+ default=constants.DEFAULT_BUILD_MEMORY)
+
+ timeout = wtforms.IntegerField(
+ "Timeout",
+ validators=[
+ wtforms.validators.NumberRange(
+ min=constants.MIN_BUILD_TIMEOUT,
+ max=constants.MAX_BUILD_TIMEOUT)],
+ default=constants.DEFAULT_BUILD_TIMEOUT)
+
+ enable_net = wtforms.BooleanField()
+
+ F.chroots_list = map(lambda x: x.name, active_chroots)
+ F.chroots_list.sort()
+ F.chroots_sets = {}
+ for ch in F.chroots_list:
+ setattr(F, ch, wtforms.BooleanField(ch, default=True))
+ if ch[0] in F.chroots_sets:
+ F.chroots_sets[ch[0]].append(ch)
+ else:
+ F.chroots_sets[ch[0]] = [ch]
+
+ return F
+
+
class ChrootForm(wtf.Form):
"""
diff --git a/frontend/coprs_frontend/coprs/logic/.builds_logic.py.swo b/frontend/coprs_frontend/coprs/logic/.builds_logic.py.swo
new file mode 100644
index 0000000..d8fd7ab
Binary files /dev/null and b/frontend/coprs_frontend/coprs/logic/.builds_logic.py.swo differ
diff --git a/frontend/coprs_frontend/coprs/logic/builds_logic.py b/frontend/coprs_frontend/coprs/logic/builds_logic.py
index 9524189..ace4bfa 100644
--- a/frontend/coprs_frontend/coprs/logic/builds_logic.py
+++ b/frontend/coprs_frontend/coprs/logic/builds_logic.py
@@ -174,6 +174,46 @@ class BuildsLogic(object):
return models.Build.query.get(build_id)
@classmethod
+ def create_new_from_other_build(cls, user, copr, source_build,
+ chroot_names=None, **build_options):
+ # check which chroots we need
+ chroots = []
+ for chroot in copr.active_chroots:
+ if chroot.name in chroot_names:
+ chroots.append(chroot)
+
+ # I don't want to import anything, just rebuild what's in dist git
+ skip_import = True
+ git_hashes = {}
+ for chroot in source_build.build_chroots:
+ if not chroot.git_hash:
+ # I got an old build from time we didn't use dist git
+ # So I'll submit it as a new build using it's link
+ skip_import = False
+ git_hashes = None
+ flask.flash("This build is not in Dist Git. Trying to import the package again.")
+ break
+ git_hashes[chroot.name] = chroot.git_hash
+
+ # try:
+ build = cls.add(
+ user=user,
+ pkgs=source_build.pkgs,
+ copr=copr,
+ chroots=chroots,
+ source_type=source_build.source_type,
+ source_json=source_build.source_json,
+ enable_net=build_options.get("enabled_net", True),
+ git_hashes=git_hashes,
+ skip_import=skip_import)
+
+ if user.proven:
+ if "timeout" in build_options:
+ build.timeout = build_options["timeout"]
+
+ return build
+
+ @classmethod
def create_new_from_url(cls, user, copr, srpm_url,
chroot_names=None, **build_options):
"""
@@ -264,7 +304,7 @@ class BuildsLogic(object):
@classmethod
def add(cls, user, pkgs, copr, source_type=None, source_json=None,
repos=None, chroots=None, timeout=None, enable_net=True,
- git_hashes=None):
+ git_hashes=None, skip_import=False):
if chroots is None:
chroots = []
@@ -308,12 +348,18 @@ class BuildsLogic(object):
if not chroots:
chroots = copr.active_chroots
+ status = helpers.StatusEnum("importing")
+
+ if skip_import:
+ status = StatusEnum("pending")
+
for chroot in chroots:
git_hash = None
if git_hashes:
git_hash = git_hashes.get(chroot.name)
buildchroot = models.BuildChroot(
build=build,
+ status=status,
mock_chroot=chroot,
git_hash=git_hash)
diff --git a/frontend/coprs_frontend/coprs/models.py b/frontend/coprs_frontend/coprs/models.py
index eac9535..e52087c 100644
--- a/frontend/coprs_frontend/coprs/models.py
+++ b/frontend/coprs_frontend/coprs/models.py
@@ -516,8 +516,6 @@ class Build(db.Model, helpers.Serializer):
Build is repeatable only if it's not pending, starting or running
"""
- if self.source_type == BuildSourceEnum("srpm_upload"):
- return False
return self.status not in [StatusEnum("pending"),
StatusEnum("starting"),
StatusEnum("running"), ]
diff --git a/frontend/coprs_frontend/coprs/templates/coprs/detail/_builds_forms.html b/frontend/coprs_frontend/coprs/templates/coprs/detail/_builds_forms.html
index f59d57d..fdbbb79 100644
--- a/frontend/coprs_frontend/coprs/templates/coprs/detail/_builds_forms.html
+++ b/frontend/coprs_frontend/coprs/templates/coprs/detail/_builds_forms.html
@@ -26,9 +26,13 @@
{##### GENERAL FORM HELPERS #####}
-{% macro copr_build_form_begin(form, view, copr) %}
+{% macro copr_build_form_begin(form, view, copr, build=None) %}
{{ render_form_errors(form, [form._mock_chroots_error]) }}
+ {% if not build %}
<form class="form-horizontal" action="{{ url_for(view, username=copr.owner.name, coprname=copr.name) }}" method="post" enctype="multipart/form-data">
+ {% else %}
+ <form class="form-horizontal" action="{{ url_for(view, username=copr.owner.name, coprname=copr.name, build_id=build.id) }}" method="post" enctype="multipart/form-data">
+ {% endif %}
<div class="form-group">
<label class="col-sm-2 control-label" for="textInput-markup">
Chroots
@@ -102,6 +106,12 @@
{% endmacro %}
+{% macro copr_build_form_rebuild(form, view, copr, build) %}
+ {{ copr_build_form_begin(form, view, copr, build) }}
+ {{ copr_build_form_end(form, view, copr) }}
+{% endmacro %}
+
+
{% macro copr_build_cancel_form(build, page) %}
{% if build.cancelable %}
diff --git a/frontend/coprs_frontend/coprs/templates/coprs/detail/_packages_table.html b/frontend/coprs_frontend/coprs/templates/coprs/detail/_packages_table.html
new file mode 100644
index 0000000..47c1004
--- /dev/null
+++ b/frontend/coprs_frontend/coprs/templates/coprs/detail/_packages_table.html
@@ -0,0 +1,48 @@
+{% macro packages_table(packages) %}
+{% if packages %}
+ <table class="datatable table table-striped table-bordered">
+ <thead>
+ <tr>
+ <th>Name</th>
+ <th>Current Version</th>
+ <th>Builds</th>
+ <th>Last Built</th>
+ <th>Dist Git</th>
+ </tr>
+ </thead>
+ <tbody>
+ {% for package in packages %}
+ <tr>
+ <td>
+ {{ package.name }}
+ </td>
+ <td>
+ no idea
+ </td>
+ <td>
+ {% for b in package.builds %}{{b.id}} ; {%endfor%}
+ </td>
+ <td>
+ no idea
+ </td>
+ <td>
+ {{ package.dist_git }}
+ </td>
+ </tr>
+ {% endfor %}
+ </tbody>
+ </table>
+{% else %}
+ <h3>No packages so far</h3>
+{% endif %}
+
+<script>
+ // Initialize Datatables
+ $(document).ready(function() {
+ $('.datatable').dataTable({
+ "order": [[ 0, "desc" ]]
+ });
+ });
+</script>
+
+{% endmacro %}
diff --git a/frontend/coprs_frontend/coprs/templates/coprs/detail/add_build/rebuild.html b/frontend/coprs_frontend/coprs/templates/coprs/detail/add_build/rebuild.html
new file mode 100644
index 0000000..f42fcda
--- /dev/null
+++ b/frontend/coprs_frontend/coprs/templates/coprs/detail/add_build/rebuild.html
@@ -0,0 +1,29 @@
+{% extends "coprs/detail.html" %}
+{% from "coprs/detail/_builds_forms.html" import copr_build_form_rebuild with context %}
+
+{% block new_build_rebuild_selected %}active{% endblock %}
+
+{%block project_breadcrumb %}
+<li>
+ <a href="{{ url_for('coprs_ns.copr_builds', username=copr.owner.name, coprname=copr.name) }}">Builds</a>
+</li>
+<li>
+ <a href="{{url_for("coprs_ns.copr_build", username = build.copr.owner.name, coprname = build.copr.name, build_id = build.id)}}">
+ {{build.id}}
+ </a>
+</li>
+<li class="active">
+ resubmit
+</li>
+{%endblock%}
+
+
+{% block detail_body %}
+<div class="row">
+ <div class="col-sm-12">
+ <h2> Resubmit build {{ build.id }} </h2>
+ <p> Resubmitting a build will rebuild the same sources again. </p>
+ {{ copr_build_form_rebuild(form, 'coprs_ns.copr_new_build_rebuild', copr, build) }}
+ </div>
+</div>
+{% endblock %}
diff --git a/frontend/coprs_frontend/coprs/templates/coprs/detail/add_build/url.html b/frontend/coprs_frontend/coprs/templates/coprs/detail/add_build/url.html
index 575a03f..cb5568c 100644
--- a/frontend/coprs_frontend/coprs/templates/coprs/detail/add_build/url.html
+++ b/frontend/coprs_frontend/coprs/templates/coprs/detail/add_build/url.html
@@ -10,7 +10,7 @@
{% block build_form %}
<h3> Build From URL of a SRPM Package</h3>
-<p> Provide an external URL link to the package you want to build. You can provide more than one URL.
+<p> Provide an external URL link to the package you want to build. You can provide more than one URL. </p>
{{ copr_build_form_url(form, 'coprs_ns.copr_new_build', copr) }}
diff --git a/frontend/coprs_frontend/coprs/templates/coprs/detail/packages.html b/frontend/coprs_frontend/coprs/templates/coprs/detail/packages.html
new file mode 100644
index 0000000..ce63cee
--- /dev/null
+++ b/frontend/coprs_frontend/coprs/templates/coprs/detail/packages.html
@@ -0,0 +1,16 @@
+{% extends "coprs/detail.html" %}
+{% block title %}Packages for {{ copr.owner.name }}/{{ copr.name }}{% endblock %}
+{% block packages_selected %}active{% endblock %}
+{% from "_helpers.html" import render_pagination %}
+{% from "coprs/detail/_packages_table.html" import packages_table with context %}
+{%block project_breadcrumb %}
+<li class="active">
+ Packages
+</li>
+{%endblock%}
+
+{% block detail_body %}
+<h2>Project Packages</h2>
+<p>This view shows all packages in the project</p>
+ {{ packages_table(packages) }}
+{% endblock %}
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 2dffef3..7c9d27e 100644
--- a/frontend/coprs_frontend/coprs/views/coprs_ns/coprs_builds.py
+++ b/frontend/coprs_frontend/coprs/views/coprs_ns/coprs_builds.py
@@ -230,6 +230,49 @@ def copr_new_build(username, coprname):
return copr_add_build(username=username, coprname=coprname, form=form)
+(a)coprs_ns.route("/<username>/<coprname>/new_build_rebuild/<int:build_id>/", methods=["POST"])
+@login_required
+def copr_new_build_rebuild(username, coprname, build_id):
+ source_build = builds_logic.BuildsLogic.get(build_id).first()
+ copr = coprs_logic.CoprsLogic.get(username, coprname).first()
+ if not copr:
+ return page_not_found(
+ "Project {0}/{1} does not exist.".format(username, coprname))
+
+ if not source_build:
+ return page_not_found(
+ "Build {} does not exist!".format(form.build_id.data))
+
+ form = forms.BuildFormRebuildFactory.create_form_cls(copr.active_chroots)()
+
+ if form.validate_on_submit():
+ try:
+ build_options = {
+ "enable_net": form.enable_net.data,
+ "timeout": form.timeout.data,
+ }
+
+ BuildsLogic.create_new_from_other_build(
+ flask.g.user, copr, source_build,
+ chroot_names=form.selected_chroots,
+ **build_options
+ )
+
+ except (ActionInProgressException, InsufficientRightsException) as e:
+ flask.flash(str(e), "error")
+ db.session.rollback()
+ else:
+ flask.flash("New build has been created", "success")
+
+ db.session.commit()
+
+ return flask.redirect(flask.url_for("coprs_ns.copr_builds",
+ username=username,
+ coprname=copr.name))
+ else:
+ return copr_add_build(username=username, coprname=coprname, form=form)
+
+
@coprs_ns.route("/<username>/<coprname>/cancel_build/<int:build_id>/",
defaults={"page": 1},
methods=["POST"])
@@ -277,12 +320,12 @@ def copr_repeat_build(username, coprname, build_id, page=1):
if not flask.g.user.can_build_in(build.copr):
flask.flash("You are not allowed to repeat this build.")
- form = forms.BuildFormFactory.create_form_cls(copr.active_chroots)(
- pkgs=build.pkgs, enable_net=build.enable_net,
+ form = forms.BuildFormRebuildFactory.create_form_cls(build.chroots)(
+ build_id=build_id, enable_net=build.enable_net,
)
# remove all checkboxes by default
- for ch in copr.active_chroots:
+ for ch in build.chroots:
field = getattr(form, ch.name)
field.data = False
@@ -302,8 +345,8 @@ def copr_repeat_build(username, coprname, build_id, page=1):
if ch.name in chroots_to_select:
getattr(form, ch.name).data = True
- return flask.render_template("coprs/detail/add_build/url.html",
- copr=copr, form=form)
+ return flask.render_template("coprs/detail/add_build/rebuild.html",
+ copr=copr, build=build, form=form)
@coprs_ns.route("/<username>/<coprname>/delete_build/<int:build_id>/",
8 years, 9 months
[copr] master: [frontend][API] Wraps error into json; fix unittests (489e3e8)
by vgologuz@fedoraproject.org
Repository : http://git.fedorahosted.org/cgit/copr.git
On branch : master
>---------------------------------------------------------------
commit 489e3e8e4200fe22b2609116a292b0437ae2cb7e
Author: Valentin Gologuzov <vgologuz(a)redhat.com>
Date: Mon Sep 7 11:33:46 2015 +0200
[frontend][API] Wraps error into json; fix unittests
>---------------------------------------------------------------
frontend/coprs_frontend/config/copr_unit_test.conf | 8 ++-
frontend/coprs_frontend/coprs/models.py | 5 ++-
frontend/coprs_frontend/coprs/rest_api/__init__.py | 21 +++++---
frontend/coprs_frontend/coprs/rest_api/common.py | 43 ++++++++++++++++-
.../coprs_frontend/coprs/rest_api/exceptions.py | 50 +++++++++++++-------
.../coprs/rest_api/resources/build.py | 35 +++++---------
.../coprs/rest_api/resources/project.py | 12 ++--
.../coprs/rest_api/resources/project_chroot.py | 28 ++++++------
frontend/coprs_frontend/coprs/rest_api/util.py | 24 +++++++--
.../coprs_frontend/tests/test_api/test_build_r.py | 6 +-
.../tests/test_api/test_project_chroot_r.py | 16 ++++---
.../tests/test_api/test_project_r.py | 6 +-
frontend/docs/api_2/source/Resources/build.rst | 6 +-
13 files changed, 164 insertions(+), 96 deletions(-)
diff --git a/frontend/coprs_frontend/config/copr_unit_test.conf b/frontend/coprs_frontend/config/copr_unit_test.conf
index 9d3cb35..51a6e7f 100644
--- a/frontend/coprs_frontend/config/copr_unit_test.conf
+++ b/frontend/coprs_frontend/config/copr_unit_test.conf
@@ -7,10 +7,10 @@ import os
PROJECT_ROOT = os.path.abspath(os.path.join(os.path.dirname( __file__ ), '..', '..', '..'))
LOCAL_TMP_DIR = os.path.join(PROJECT_ROOT, '_tmp', str(int(time.time())) )
-print(LOCAL_TMP_DIR)
+# print(LOCAL_TMP_DIR)
#DATABASE = './tmp/copr.db' # when executing >1 test instances use different db
-DATABASE = os.path.join(LOCAL_TMP_DIR, 'copr.db')
+#DATABASE = os.path.join(LOCAL_TMP_DIR, 'copr.db')
OPENID_STORE = os.path.join(LOCAL_TMP_DIR, 'openid_store')
@@ -25,7 +25,9 @@ WHOOSHEE_DIR = os.path.join(LOCAL_TMP_DIR, 'whooshee')
#USE_ALLOWED_USERS = False
#ALLOWED_USERS = ['bonnie', 'clyde']
-SQLALCHEMY_DATABASE_URI = 'sqlite:///' + DATABASE
+#SQLALCHEMY_DATABASE_URI = 'sqlite:///' + DATABASE
+# in-memory
+SQLALCHEMY_DATABASE_URI = 'sqlite://'
# Token length, defaults to 30 (max 255)
#API_TOKEN_LENGTH = 30
diff --git a/frontend/coprs_frontend/coprs/models.py b/frontend/coprs_frontend/coprs/models.py
index 5359ebc..eac9535 100644
--- a/frontend/coprs_frontend/coprs/models.py
+++ b/frontend/coprs_frontend/coprs/models.py
@@ -425,9 +425,12 @@ class Build(db.Model, helpers.Serializer):
@property
def source_metadata(self):
+ if self.source_json is None:
+ return None
+
try:
return json.loads(self.source_json)
- except ValueError:
+ except (TypeError, ValueError):
return None
@property
diff --git a/frontend/coprs_frontend/coprs/rest_api/__init__.py b/frontend/coprs_frontend/coprs/rest_api/__init__.py
index 2debb8f..7276c2f 100644
--- a/frontend/coprs_frontend/coprs/rest_api/__init__.py
+++ b/frontend/coprs_frontend/coprs/rest_api/__init__.py
@@ -1,5 +1,6 @@
# coding: utf-8
-from flask import Response, url_for, Blueprint
+import json
+from flask import Response, url_for, Blueprint, make_response
from flask_restful import Resource, Api
from coprs.exceptions import InsufficientRightsException
@@ -68,12 +69,16 @@ api.add_resource(BuildChrootR, "/builds/<int:build_id>/chroots/<name>")
def register_api_error_handler(app):
@app.errorhandler(ApiError)
def handle_api_error(error):
- kwargs = dict(
- status=error.code,
- mimetype="text/plain",
- headers=error.headers,
- )
+ """
+ :param ApiError error:
+ """
+
+ content = {
+ "message": error.msg,
+ }
if error.data:
- kwargs["response"] = "{}\n".format(error.data)
+ content["data"] = error.data
- return Response(**kwargs)
+ response = make_response(json.dumps(content), error.code)
+ response.headers["Content-Type"] = "application/json"
+ return response
diff --git a/frontend/coprs_frontend/coprs/rest_api/common.py b/frontend/coprs_frontend/coprs/rest_api/common.py
index 01a787e..72c5eae 100644
--- a/frontend/coprs_frontend/coprs/rest_api/common.py
+++ b/frontend/coprs_frontend/coprs/rest_api/common.py
@@ -3,16 +3,20 @@ import base64
import datetime
import functools
from logging import getLogger
+from coprs.logic.builds_logic import BuildsLogic
+from coprs.logic.coprs_logic import CoprsLogic
from coprs.rest_api.schemas import BuildChrootSchema
-from coprs.rest_api.util import mm_serialize_one
+from coprs.rest_api.util import mm_serialize_one, get_one_safe
log = getLogger(__name__)
from flask import url_for
import flask
+from .. import models
from ..logic.users_logic import UsersLogic
-from .exceptions import AuthFailed
+
+from .exceptions import AuthFailed, ObjectNotFoundError
from .schemas import CoprChrootSchema, BuildSchema, ProjectSchema
from .util import mm_serialize_one
@@ -105,3 +109,38 @@ def rest_api_auth_required(f):
return f(*args, **kwargs)
return decorated_function
+
+def get_project_safe(project_id):
+ """
+ :param int project_id:
+ :rtype: models.Copr
+ """
+ return get_one_safe(
+ CoprsLogic.get_by_id(project_id),
+ msg="Project with id `{}` not found".format(project_id),
+ data={"project_id": project_id}
+ )
+
+
+def get_build_safe(build_id):
+ """
+ :param int build_id:
+ :rtype: models.Build
+ """
+ return get_one_safe(
+ BuildsLogic.get(build_id),
+ msg="Build with id `{}` not found".format(build_id),
+ data={"build_id": build_id}
+ )
+
+
+def get_user_safe(username):
+ """
+ :param str username:
+ :rtype: models.User
+ """
+ return get_one_safe(
+ UsersLogic.get(username),
+ msg="User `{}` doesn't have any project".format(username),
+ data={"username": username}
+ )
diff --git a/frontend/coprs_frontend/coprs/rest_api/exceptions.py b/frontend/coprs_frontend/coprs/rest_api/exceptions.py
index 1752101..4dba5b5 100644
--- a/frontend/coprs_frontend/coprs/rest_api/exceptions.py
+++ b/frontend/coprs_frontend/coprs/rest_api/exceptions.py
@@ -3,11 +3,12 @@ import six
class ApiError(Exception):
- def __init__(self, code, data, **kwargs):
+ def __init__(self, code, msg, data=None, **kwargs):
super(ApiError, self).__init__(**kwargs)
self.code = code
self.data = data
+ self.msg = msg
self.headers = kwargs.get("headers", {})
@@ -20,38 +21,53 @@ class ApiError(Exception):
class AuthFailed(ApiError):
- def __init__(self, data, **kwargs):
-
- super(AuthFailed, self).__init__(401, data, **kwargs)
+ def __init__(self, msg=None, data=None, **kwargs):
+ if msg is None:
+ msg = "Authorization failed"
+ super(AuthFailed, self).__init__(401, msg=msg, data=data, **kwargs)
self.headers["Authorization"] = "Basic"
class AccessForbidden(ApiError):
- def __init__(self, data, **kwargs):
-
- super(AccessForbidden, self).__init__(403, data, **kwargs)
+ def __init__(self, msg=None, data=None, **kwargs):
+ if msg is None:
+ msg = "Access forbidden"
+ super(AccessForbidden, self).__init__(403, msg=msg, data=data, **kwargs)
class ObjectNotFoundError(ApiError):
- def __init__(self, data, **kwargs):
- super(ObjectNotFoundError, self).__init__(404, data, **kwargs)
+ def __init__(self, msg=None, data=None, **kwargs):
+ if msg is None:
+ msg = "Requested object wasn't found"
+ super(ObjectNotFoundError, self).__init__(404, msg=msg, data=data, **kwargs)
class ObjectAlreadyExists(ApiError):
- def __init__(self, data, **kwargs):
- super(ObjectAlreadyExists, self).__init__(409, data, **kwargs)
+ def __init__(self, msg=None, data=None, **kwargs):
+ if msg is None:
+ msg = "Operational error, trying to create existing object"
+
+ super(ObjectAlreadyExists, self).__init__(409, msg=msg, data=data, **kwargs)
class MalformedRequest(ApiError):
- def __init__(self, data=None, **kwargs):
- super(MalformedRequest, self).__init__(400, data, **kwargs)
+ def __init__(self, msg=None, data=None, **kwargs):
+ if msg is None:
+ msg = "Given request contains errors or couldn't be executed in the current context"
+
+ super(MalformedRequest, self).__init__(400, msg=msg, data=data, **kwargs)
class CannotProcessRequest(ApiError):
- def __init__(self, data=None, **kwargs):
- super(CannotProcessRequest, self).__init__(400, data, **kwargs)
+ def __init__(self, msg=None, data=None, **kwargs):
+ if msg is None:
+ msg = "Cannot process given request"
+
+ super(CannotProcessRequest, self).__init__(400, msg=msg, data=data, **kwargs)
class ServerError(ApiError):
- def __init__(self, data=None, **kwargs):
- super(ServerError, self).__init__(500, data, **kwargs)
+ def __init__(self, msg=None, data=None, **kwargs):
+ if msg is None:
+ msg = "Unhandled server error, please contact site administrator"
+ super(ServerError, self).__init__(500, msg=msg, data=data, **kwargs)
diff --git a/frontend/coprs_frontend/coprs/rest_api/resources/build.py b/frontend/coprs_frontend/coprs/rest_api/resources/build.py
index d2ee451..6f4a792 100644
--- a/frontend/coprs_frontend/coprs/rest_api/resources/build.py
+++ b/frontend/coprs_frontend/coprs/rest_api/resources/build.py
@@ -5,17 +5,16 @@ from flask import url_for, make_response
# from flask_restful_swagger import swagger
-from coprs import db, models
+from coprs import db
from coprs.exceptions import ActionInProgressException, InsufficientRightsException, RequestCannotBeExecuted
-from coprs.logic.coprs_logic import CoprsLogic
from coprs.logic.builds_logic import BuildsLogic
-from coprs.logic.users_logic import UsersLogic
+from coprs.rest_api.common import get_project_safe
from coprs.rest_api.exceptions import MalformedRequest, CannotProcessRequest, AccessForbidden
-from ..common import render_build, rest_api_auth_required, render_build_chroot
+from ..common import render_build, rest_api_auth_required, render_build_chroot, get_build_safe, get_user_safe
from coprs.rest_api.schemas import BuildSchema, BuildCreateSchema, BuildCreateFromUrlSchema
-from coprs.rest_api.util import get_one_safe, mm_deserialize
+from coprs.rest_api.util import mm_deserialize
from flask_restful import Resource, reqparse
@@ -37,10 +36,10 @@ class BuildListR(Resource):
req_args = parser.parse_args()
if req_args["project_id"] is not None:
- copr = get_one_safe(CoprsLogic.get_by_id(req_args["project_id"]))
- query = BuildsLogic.get_multiple_by_copr(copr)
+ project = get_project_safe(req_args["project_id"])
+ query = BuildsLogic.get_multiple_by_copr(project)
elif req_args["owner"] is not None:
- user = get_one_safe(UsersLogic.get(req_args["owner"]))
+ user = get_user_safe(req_args["owner"])
query = BuildsLogic.get_multiple_by_owner(user)
else:
query = BuildsLogic.get_multiple()
@@ -62,7 +61,6 @@ class BuildListR(Resource):
self_params = dict(req_args)
self_params["limit"] = limit
return {
-
"builds": [
render_build(build) for build in builds
],
@@ -76,10 +74,8 @@ class BuildListR(Resource):
:return: if of the created build or raise Exception
"""
build_params = mm_deserialize(BuildCreateFromUrlSchema(), req.data).data
- project_id = build_params["project_id"]
+ project = get_project_safe(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:
@@ -118,8 +114,7 @@ class BuildListR(Resource):
build_params = mm_deserialize(BuildCreateSchema(), metadata).data
project_id = build_params["project_id"]
- project = get_one_safe(CoprsLogic.get_by_id(project_id))
- """:type : models.Copr """
+ project = get_project_safe(project_id)
chroot_names = build_params.pop("chroots")
try:
@@ -167,9 +162,7 @@ class BuildR(Resource):
parser.add_argument('show_chroots', type=bool, default=False)
req_args = parser.parse_args()
- build = get_one_safe(BuildsLogic.get(build_id),
- "Not found build with id: {}".format(build_id))
- """:type : models.Build """
+ build = get_build_safe(build_id)
self_params = {}
if req_args["show_chroots"]:
@@ -186,8 +179,7 @@ class BuildR(Resource):
@rest_api_auth_required
def delete(self, build_id):
- build = get_one_safe(BuildsLogic.get(build_id),
- "Not found build with id: {}".format(build_id))
+ build = get_build_safe(build_id)
try:
BuildsLogic.delete_build(flask.g.user, build)
db.session.commit()
@@ -202,10 +194,7 @@ class BuildR(Resource):
@rest_api_auth_required
def put(self, build_id):
- build = get_one_safe(BuildsLogic.get(build_id),
- "Not found build with id: {}".format(build_id))
- """:type : models.Build """
-
+ build = get_build_safe(build_id)
build_dict = mm_deserialize(BuildSchema(), flask.request.data).data
try:
if not build.canceled and build_dict["state"] == "canceled":
diff --git a/frontend/coprs_frontend/coprs/rest_api/resources/project.py b/frontend/coprs_frontend/coprs/rest_api/resources/project.py
index 48ace15..148891b 100644
--- a/frontend/coprs_frontend/coprs/rest_api/resources/project.py
+++ b/frontend/coprs_frontend/coprs/rest_api/resources/project.py
@@ -16,10 +16,10 @@ from ...exceptions import ActionInProgressException, InsufficientRightsException
from ...exceptions import DuplicateException
-from ..common import rest_api_auth_required, render_copr_chroot, render_build, render_project
+from ..common import rest_api_auth_required, render_copr_chroot, render_build, render_project, get_project_safe
from ..schemas import ProjectSchema, ProjectCreateSchema
from ..exceptions import ObjectAlreadyExists, CannotProcessRequest, AccessForbidden
-from ..util import get_one_safe, mm_deserialize
+from ..util import mm_deserialize
class ProjectListR(Resource):
@@ -45,7 +45,7 @@ class ProjectListR(Resource):
)
db.session.commit()
except DuplicateException as error:
- raise ObjectAlreadyExists(data=error)
+ raise ObjectAlreadyExists(msg=str(error))
resp = make_response("", 201)
resp.headers["Location"] = url_for(".projectr", project_id=project.id)
@@ -98,7 +98,7 @@ class ProjectR(Resource):
@rest_api_auth_required
def delete(self, project_id):
- project = get_one_safe(CoprsLogic.get_by_id(int(project_id)))
+ project = get_project_safe(project_id)
try:
ComplexLogic.delete_copr(project)
@@ -116,7 +116,7 @@ class ProjectR(Resource):
parser.add_argument('show_chroots', type=bool, default=False)
req_args = parser.parse_args()
- project = get_one_safe(CoprsLogic.get_by_id(int(project_id)))
+ project = get_project_safe(project_id)
self_params = {}
if req_args["show_builds"]:
self_params["show_builds"] = req_args["show_builds"]
@@ -144,7 +144,7 @@ class ProjectR(Resource):
"""
Modifies project by replacement of provided fields
"""
- project = get_one_safe(CoprsLogic.get_by_id(int(project_id)))
+ project = get_project_safe(project_id)
project_dict = mm_deserialize(ProjectSchema(), flask.request.data).data
# pprint(project_dict)
diff --git a/frontend/coprs_frontend/coprs/rest_api/resources/project_chroot.py b/frontend/coprs_frontend/coprs/rest_api/resources/project_chroot.py
index f5ec11d..e22de4f 100644
--- a/frontend/coprs_frontend/coprs/rest_api/resources/project_chroot.py
+++ b/frontend/coprs_frontend/coprs/rest_api/resources/project_chroot.py
@@ -16,7 +16,7 @@ from ...exceptions import InsufficientRightsException, MalformedArgumentExceptio
from ..exceptions import AccessForbidden, MalformedRequest, \
ObjectAlreadyExists, ServerError, ObjectNotFoundError
-from ..common import rest_api_auth_required, render_copr_chroot
+from ..common import rest_api_auth_required, render_copr_chroot, get_project_safe
from ..schemas import CoprChrootSchema, CoprChrootCreateSchema
from ..util import get_one_safe, mm_deserialize
@@ -24,12 +24,12 @@ from ..util import get_one_safe, mm_deserialize
class ProjectChrootListR(Resource):
def get(self, project_id):
- copr = get_one_safe(CoprsLogic.get_by_id(int(project_id)))
+ project = get_project_safe(project_id)
return {
"chroots": [
render_copr_chroot(chroot)
- for chroot in copr.active_copr_chroots
+ for chroot in project.active_copr_chroots
],
"_links": {
"self": {"href": url_for(".projectchrootlistr", project_id=project_id)}
@@ -38,7 +38,7 @@ class ProjectChrootListR(Resource):
@rest_api_auth_required
def post(self, project_id):
- copr = get_one_safe(CoprsLogic.get_by_id(int(project_id)))
+ project = get_project_safe(project_id)
chroot_data = mm_deserialize(CoprChrootCreateSchema(),
flask.request.data)
@@ -54,22 +54,22 @@ class ProjectChrootListR(Resource):
if mock_chroot is None:
raise MalformedRequest("Mock chroot `{}` doesn't exists"
.format(name))
- CoprChrootsLogic.create_chroot(flask.g.user, copr, mock_chroot, **req)
+ CoprChrootsLogic.create_chroot(flask.g.user, project, mock_chroot, **req)
try:
db.session.commit()
except IntegrityError as err:
# assuming conflict with existing chroot
db.session.rollback()
- if get_one_safe(CoprChrootsLogic.get_by_name(copr, name)) is not None:
+ if get_one_safe(CoprChrootsLogic.get_by_name(project, name)) is not None:
raise ObjectAlreadyExists("Copr {} already has chroot {} enabled"
- .format(copr.full_name, name))
+ .format(project.full_name, name))
else:
raise ServerError("Unexpected error, contact site administrator: {}"
.format(err))
resp = make_response("", 201)
resp.headers["Location"] = url_for(".projectchrootr",
- project_id=copr.id, name=name)
+ project_id=project.id, name=name)
return resp
@@ -84,15 +84,15 @@ class ProjectChrootR(Resource):
return chroot
def get(self, project_id, name):
- copr = get_one_safe(CoprsLogic.get_by_id(int(project_id)))
- chroot = self._get_chroot_safe(copr, name)
+ project = get_project_safe(project_id)
+ chroot = self._get_chroot_safe(project, name)
return render_copr_chroot(chroot)
@rest_api_auth_required
def delete(self, project_id, name):
- copr = get_one_safe(CoprsLogic.get_by_id(int(project_id)))
- chroot = CoprChrootsLogic.get_by_name_safe(copr, name)
+ project = get_project_safe(project_id)
+ chroot = CoprChrootsLogic.get_by_name_safe(project, name)
if chroot:
try:
@@ -106,8 +106,8 @@ class ProjectChrootR(Resource):
@rest_api_auth_required
def put(self, project_id, name):
- copr = get_one_safe(CoprsLogic.get_by_id(int(project_id)))
- chroot = self._get_chroot_safe(copr, name)
+ project = get_project_safe(project_id)
+ chroot = self._get_chroot_safe(project, name)
chroot_data = mm_deserialize(CoprChrootSchema(), flask.request.data)
try:
diff --git a/frontend/coprs_frontend/coprs/rest_api/util.py b/frontend/coprs_frontend/coprs/rest_api/util.py
index eec3085..d937784 100644
--- a/frontend/coprs_frontend/coprs/rest_api/util.py
+++ b/frontend/coprs_frontend/coprs/rest_api/util.py
@@ -19,11 +19,17 @@ def render_allowed_method(method, doc, require_auth=True, params=None):
return AllowedMethodSchema().dump(AllowedMethod(method, doc, require_auth, params))[0]
-def get_one_safe(query, data_on_error=None):
+def get_one_safe(query, msg=None, data=None):
+ """
+ :type query: sqlalchemy.Query
+ :param str msg: message used in error when object not found
+ :param Any data: any serializable data to include into error
+ :raises ObjectNotFoundError: when query failed to return anything
+ """
try:
return query.one()
except sqlalchemy.orm.exc.NoResultFound:
- raise ObjectNotFoundError(data_on_error)
+ raise ObjectNotFoundError(msg=msg, data=data)
def json_loads_safe(raw, data_on_error=None):
@@ -38,12 +44,18 @@ def mm_deserialize(schema, json_string):
try:
result = schema.loads(json_string)
except ValueError as err:
- raise MalformedRequest(data="Failed to parse request: {}"
- .format(err))
+ raise MalformedRequest(
+ msg="Failed to parse request: {}".format(err),
+ data={"request_string": json_string}
+ )
if result.errors:
- raise MalformedRequest(data="Failed to parse request: {}"
- .format(result.errors))
+ raise MalformedRequest(
+ msg="Failed to parse request: Validation Error",
+ data={
+ "validation_errors": result.errors
+ }
+ )
return result
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 5c4512c..1f0075d 100644
--- a/frontend/coprs_frontend/tests/test_api/test_build_r.py
+++ b/frontend/coprs_frontend/tests/test_api/test_build_r.py
@@ -169,9 +169,9 @@ class TestBuildResource(CoprsTestCase):
assert r1.status_code == 200
build_obj = json.loads(r1.data)
build_dict = build_obj["build"]
- assert json.loads(build_dict["source_json"])["url"] == \
+ assert build_dict["source_metadata"]["url"] == \
metadata["srpm_url"]
- assert build_dict["source_type"] == BuildSourceEnum("srpm_link")
+ assert build_dict["source_type"] == "srpm_link"
def test_post_json_on_wrong_user(
self, f_users, f_coprs, f_db, f_mock_chroots,
@@ -293,7 +293,7 @@ class TestBuildResource(CoprsTestCase):
assert r1.status_code == 200
build_obj = json.loads(r1.data)
- assert build_obj["build"]["source_type"] == BuildSourceEnum("srpm_upload")
+ assert build_obj["build"]["source_type"] == "srpm_upload"
chroots_href = build_obj["_links"]["chroots"]["href"]
r2 = self.tc.get(chroots_href)
diff --git a/frontend/coprs_frontend/tests/test_api/test_project_chroot_r.py b/frontend/coprs_frontend/tests/test_api/test_project_chroot_r.py
index fef831d..fdb20fa 100644
--- a/frontend/coprs_frontend/tests/test_api/test_project_chroot_r.py
+++ b/frontend/coprs_frontend/tests/test_api/test_project_chroot_r.py
@@ -14,15 +14,16 @@ from tests.coprs_test_case import CoprsTestCase, TransactionDecorator
class TestProjectChrootResource(CoprsTestCase):
- def test_remove_chroot(self, f_users, f_coprs,f_db, f_users_api,
- f_mock_chroots_many, f_build_few_chroots):
+ def test_remove_chroot(self, f_users, f_coprs, f_db, f_users_api,
+ f_mock_chroots_many):
+ init_len = len([mc for mc in self.mc_list if mc.is_active])
chroot_name = self.mc_list[0].name
self.db.session.commit()
r0 = self.tc.get("/api_2/projects/1/chroots")
assert r0.status_code == 200
- assert len(json.loads(r0.data)["chroots"]) == len(self.mc_list)
+ assert len(json.loads(r0.data)["chroots"]) == init_len
r1 = self.request_rest_api_with_auth(
"/api_2/projects/1/chroots/{}".format(chroot_name),
@@ -32,7 +33,7 @@ class TestProjectChrootResource(CoprsTestCase):
r2 = self.tc.get("/api_2/projects/1/chroots")
assert r2.status_code == 200
- assert len(json.loads(r2.data)["chroots"]) == len(self.mc_list) - 1
+ assert len(json.loads(r2.data)["chroots"]) == init_len - 1
# test idempotency
r3 = self.request_rest_api_with_auth(
@@ -42,12 +43,13 @@ class TestProjectChrootResource(CoprsTestCase):
assert r3.status_code == 204
r4 = self.tc.get("/api_2/projects/1/chroots")
assert r4.status_code == 200
- assert len(json.loads(r4.data)["chroots"]) == len(self.mc_list) - 1
+ assert len(json.loads(r4.data)["chroots"]) == init_len - 1
def test_remove_chroot_other_user(
self, f_users, f_coprs,f_db, f_users_api,
f_mock_chroots_many, f_build_few_chroots):
+ init_len = len([mc for mc in self.mc_list if mc.is_active])
chroot_name = self.mc_list[0].name
login = self.u2.api_login
token = self.u2.api_token
@@ -55,7 +57,7 @@ class TestProjectChrootResource(CoprsTestCase):
r0 = self.tc.get("/api_2/projects/1/chroots")
assert r0.status_code == 200
- assert len(json.loads(r0.data)["chroots"]) == len(self.mc_list)
+ assert len(json.loads(r0.data)["chroots"]) == init_len
r1 = self.request_rest_api_with_auth(
"/api_2/projects/1/chroots/{}".format(chroot_name),
@@ -66,7 +68,7 @@ class TestProjectChrootResource(CoprsTestCase):
r2 = self.tc.get("/api_2/projects/1/chroots")
assert r2.status_code == 200
- assert len(json.loads(r2.data)["chroots"]) == len(self.mc_list)
+ assert len(json.loads(r2.data)["chroots"]) == init_len
def test_put_correct(self, f_users, f_coprs, f_db, f_users_api, f_mock_chroots):
chroot_name = self.mc1.name
diff --git a/frontend/coprs_frontend/tests/test_api/test_project_r.py b/frontend/coprs_frontend/tests/test_api/test_project_r.py
index b73489c..dff95fe 100644
--- a/frontend/coprs_frontend/tests/test_api/test_project_r.py
+++ b/frontend/coprs_frontend/tests/test_api/test_project_r.py
@@ -366,9 +366,9 @@ class TestProjectResource(CoprsTestCase):
method="put",
content=self.put_update_dict
)
- assert r0.status_code == 201
+ assert r0.status_code == 204
- r1 = self.tc.get(r0.headers["Location"])
+ r1 = self.tc.get(href)
obj = json.loads(r1.data)
updated_project = obj["project"]
for k, v in self.put_update_dict.items():
@@ -449,4 +449,4 @@ class TestProjectResource(CoprsTestCase):
method="put",
content=test_case
)
- assert r0.status_code == 201
+ assert r0.status_code == 204
diff --git a/frontend/docs/api_2/source/Resources/build.rst b/frontend/docs/api_2/source/Resources/build.rst
index f84f3fb..9da70a9 100644
--- a/frontend/docs/api_2/source/Resources/build.rst
+++ b/frontend/docs/api_2/source/Resources/build.rst
@@ -49,9 +49,9 @@ Field Type Description
id int unique build identifier
state string current state of the build
submitted_on int(unixtime UTC) time of the build submission
-started_on int(unixtime UTC) time of when the first build chroot started, otherwise ``null``
-ended_on int(unixtime UTC) time of when the last build chroot ended, otherwise ``null``
-source_type string name of the method which was used for build creation
+started_on int(unixtime UTC) time when the first build chroot started, otherwise ``null``
+ended_on int(unixtime UTC) time when the last build chroot ended, otherwise ``null``
+source_type string method used for build creation
source_metadata json object build source information
package_version string version of the source package
package_name string name of the source package
8 years, 9 months
[copr] master: [frontend][API docs] covered Builds (4fe4414)
by vgologuz@fedoraproject.org
Repository : http://git.fedorahosted.org/cgit/copr.git
On branch : master
>---------------------------------------------------------------
commit 4fe44146965e1bb61afed307c32c7c5e7f815528
Author: Valentin Gologuzov <vgologuz(a)redhat.com>
Date: Fri Sep 4 16:33:23 2015 +0200
[frontend][API docs] covered Builds
>---------------------------------------------------------------
.../coprs_frontend/coprs/logic/builds_logic.py | 12 +-
frontend/coprs_frontend/coprs/models.py | 15 +
.../coprs/rest_api/resources/build.py | 2 +-
frontend/coprs_frontend/coprs/rest_api/schemas.py | 39 ++-
frontend/docs/api_2/source/Resources/build.rst | 411 ++++++++++++++++++++
.../docs/api_2/source/Resources/mock_chroot.rst | 5 -
frontend/docs/api_2/source/Resources/project.rst | 10 +-
frontend/docs/api_2/source/index.rst | 2 +-
8 files changed, 470 insertions(+), 26 deletions(-)
diff --git a/frontend/coprs_frontend/coprs/logic/builds_logic.py b/frontend/coprs_frontend/coprs/logic/builds_logic.py
index 48a11da..9524189 100644
--- a/frontend/coprs_frontend/coprs/logic/builds_logic.py
+++ b/frontend/coprs_frontend/coprs/logic/builds_logic.py
@@ -184,11 +184,13 @@ class BuildsLogic(object):
:rtype: models.Build
"""
- # check which chroots we need
- chroots = []
- for chroot in copr.active_chroots:
- if chroot.name in chroot_names:
- chroots.append(chroot)
+ if chroot_names is None:
+ chroots = [c for c in copr.active_chroots]
+ else:
+ 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})
diff --git a/frontend/coprs_frontend/coprs/models.py b/frontend/coprs_frontend/coprs/models.py
index a8844f4..5359ebc 100644
--- a/frontend/coprs_frontend/coprs/models.py
+++ b/frontend/coprs_frontend/coprs/models.py
@@ -424,6 +424,13 @@ class Build(db.Model, helpers.Serializer):
return helpers.BuildSourceEnum(self.source_type)
@property
+ def source_metadata(self):
+ try:
+ return json.loads(self.source_json)
+ except ValueError:
+ return None
+
+ @property
def chroot_states(self):
return map(lambda chroot: chroot.status, self.build_chroots)
@@ -537,6 +544,7 @@ class Build(db.Model, helpers.Serializer):
def src_pkg_name(self):
"""
Extract source package name from source name or url
+ todo: obsolete
"""
src_rpm_name = self.pkgs.split("/")[-1]
if src_rpm_name.endswith(".src.rpm"):
@@ -544,6 +552,13 @@ class Build(db.Model, helpers.Serializer):
else:
return src_rpm_name
+ @property
+ def package_name(self):
+ try:
+ return self.package.name
+ except:
+ return None
+
def to_dict(self, options=None):
result = super(Build, self).to_dict(options)
result["src_pkg"] = result["pkgs"]
diff --git a/frontend/coprs_frontend/coprs/rest_api/resources/build.py b/frontend/coprs_frontend/coprs/rest_api/resources/build.py
index dfd5868..d2ee451 100644
--- a/frontend/coprs_frontend/coprs/rest_api/resources/build.py
+++ b/frontend/coprs_frontend/coprs/rest_api/resources/build.py
@@ -149,7 +149,7 @@ class BuildListR(Resource):
req = flask.request
if "application/json" in req.content_type:
build_id = self.handle_post_json(req)
- elif "multipart/form-data" in req.content_type :
+ elif "multipart/form-data" in req.content_type:
build_id = self.handle_post_multipart(req)
else:
raise MalformedRequest("Got unexpected content type: {}"
diff --git a/frontend/coprs_frontend/coprs/rest_api/schemas.py b/frontend/coprs_frontend/coprs/rest_api/schemas.py
index a11bbcd..cb6f867 100644
--- a/frontend/coprs_frontend/coprs/rest_api/schemas.py
+++ b/frontend/coprs_frontend/coprs/rest_api/schemas.py
@@ -43,6 +43,29 @@ class SpaceSeparatedList(fields.Field):
else:
return " ".join(value)
+class BuiltPackages(fields.Field):
+ """ stored in db as a string:
+ "python3-marshmallow 2.0.0b5\npython-marshmallow 2.0.0b5"
+ we would represent them as
+ { name: "pkg", version: "pkg version" }
+ we implement only the serialization, since field is read-only
+ """
+ def _serialize(self, value, attr, obj):
+ if value is None:
+ return []
+ result = []
+ try:
+ for chunk in value.split("\n"):
+ pkg, version = chunk.split()
+ result.append({
+ "name": pkg,
+ "version": version
+ })
+ except:
+ pass
+
+ return result
+
class AllowedMethodSchema(Schema):
method = fields.Str()
@@ -121,8 +144,9 @@ class BuildSchema(Schema):
id = fields.Int(dump_only=True)
state = fields.Str()
- build_packages = fields.Str(dump_only=True)
- pkg_version = fields.Str(dump_only=True)
+ built_packages = BuiltPackages(dump_only=True)
+ package_version = fields.Str(dump_only=True, attribute="pkg_version")
+ package_name = fields.Str(dump_only=True)
repos = SpaceSeparatedList(dump_only=True)
@@ -130,14 +154,11 @@ class BuildSchema(Schema):
started_on = fields.Int(dump_only=True)
ended_on = fields.Int(dump_only=True)
- results = fields.Str(dump_only=True)
-
- timeout = fields.Int(dump_only=True)
-
+ # timeout = fields.Int(dump_only=True) # currently has no use
enable_net = fields.Bool(dump_only=True)
- source_type = fields.Int(dump_only=True)
- source_json = fields.Str(dump_only=True)
+ source_type = fields.Str(dump_only=True, attribute="source_type_text")
+ source_metadata = fields.Raw(dump_only=True)
class BuildCreateSchema(BuildSchema):
@@ -145,6 +166,8 @@ class BuildCreateSchema(BuildSchema):
chroots = fields.List(fields.Str())
enable_net = fields.Bool()
+ state = fields.Str(dump_only=True)
+
class BuildCreateFromUrlSchema(BuildCreateSchema):
srpm_url = fields.Url(required=True, validate=lambda u: u.startswith("http"))
diff --git a/frontend/docs/api_2/source/Resources/build.rst b/frontend/docs/api_2/source/Resources/build.rst
index e69de29..f84f3fb 100644
--- a/frontend/docs/api_2/source/Resources/build.rst
+++ b/frontend/docs/api_2/source/Resources/build.rst
@@ -0,0 +1,411 @@
+Build
+=====
+
+Build resources allows to submit new builds and access current build progress.
+
+Build in fact consists of the few tasks, one per chroot, and detail information
+is available through sub-resource BuildChroot_.
+
+
+
+Structure of the build
+----------------------
+
+.. code-block:: javascript
+
+ {
+ "enable_net": true,
+ "source_metadata": {
+ "tmp": "tmpUNPJWO",
+ "pkg": "python-marshmallow-2.0.0b5-1.fc22.src.rpm"
+ },
+ "submitted_on": 1440753750,
+ "repos": [],
+ "built_packages": [
+ {
+ "version": "2.0.0b5",
+ "name": "python3-marshmallow"
+ },
+ {
+ "version": "2.0.0b5",
+ "name": "python-marshmallow"
+ }
+ ],
+ "started_on": null,
+ "source_type": "srpm_upload",
+ "state": "succeeded",
+ "ended_on": 1440754058,
+ "package_version": "2.0.0b5-1.fc22",
+ "package_name": "python-marshmallow",
+ "id": 106882
+ }
+
+
+Build fields
+~~~~~~~~~~~~
+================== ==================== ===============
+Field Type Description
+================== ==================== ===============
+id int unique build identifier
+state string current state of the build
+submitted_on int(unixtime UTC) time of the build submission
+started_on int(unixtime UTC) time of when the first build chroot started, otherwise ``null``
+ended_on int(unixtime UTC) time of when the last build chroot ended, otherwise ``null``
+source_type string name of the method which was used for build creation
+source_metadata json object build source information
+package_version string version of the source package
+package_name string name of the source package
+enable_net bool defines if network is available during the build
+repos list of string list of additional repositories enabled during the build
+built_packages list of hash maps list of the built packages, each hash map has two keys: ``name`` and ``version``
+================== ==================== ===============
+
+.. note::
+ Only the ``state`` field is editable by the PUT method.
+ All other fields are read only.
+ There is a different structure used for build creation, see details at `Submit new build`_
+
+
+List builds
+-----------
+.. http:post:: /api_2/builds
+
+ Return a list of build according to the given query parameters.
+
+ :query str owner: select only builds from projects owned by this user
+ :query str project_id: select only projects owned by this project
+ :query int offset: offset number, default value is 0
+ :query int limit: limit number, default value is 100
+
+ :statuscode 200: no error
+
+ **Example request**:
+
+ .. sourcecode:: http
+
+ GET /api_2/builds?project_id=3985&limit=1 HTTP/1.1
+ Host: copr.fedoraproject.org
+
+ **Response**:
+
+ .. sourcecode:: http
+
+ HTTP/1.1 200 OK
+ Content-Type: application/json
+
+ {
+ "_links": {
+ "self": {
+ "href": "/api_2/builds?project_id=3985&limit=1"
+ }
+ },
+ "builds": [
+ {
+ "_links": {
+ "project": {
+ "href": "/api_2/projects/3985"
+ },
+ "self": {
+ "href": "/api_2/builds/106897"
+ },
+ "chroots": {
+ "href": "/api_2/builds/106897/chroots"
+ }
+ },
+ "build": {
+ "enable_net": true,
+ "source_metadata": {
+ "url": "http://miroslav.suchy.cz/copr/copr-ping-1-1.fc20.src.rpm"
+ },
+ "package_name": "copr-ping",
+ "submitted_on": 1441366834,
+ "package_version": "1-1.fc20",
+ "built_packages": [
+ {
+ "version": "1",
+ "name": "copr-ping"
+ }
+ ],
+ "started_on": null,
+ "source_type": "srpm_link",
+ "state": "succeeded",
+ "ended_on": 1441366969,
+ "id": 106897,
+ "repos": []
+ }
+ }
+ ]
+ }
+
+
+Submit new build
+----------------
+**REQUIRE AUTH**
+
+Allows to submit new build. Copr services currently provides the following options for build submission:
+
+From srpm url
+~~~~~~~~~~~~~
+ .. code-block:: javascript
+
+ {
+ "project_id": 3985,
+ "chroots": ["fedora-22-i386", "fedora-21-i386"],
+ "srpm_url": "http://miroslav.suchy.cz/copr/copr-ping-1-1.fc20.src.rpm"
+ }
+
+
+ ================== ==================== ===============
+ Field Type Description
+ ================== ==================== ===============
+ project_id int identifier of the parent project
+ chroots list of strings what chroots should be used for build
+ srpm_url string(URL) url to the publicly available source package
+ enable_net bool allows to disable network access during the build, default: True
+ ================== ==================== ===============
+
+.. http:post:: /api_2/builds
+
+ :reqheader Content-Type: MUST be a ``application/json``
+
+ :resheader Location: contains URL to the submitted build
+
+ :statuscode 201: build was successfully submitted
+ :statuscode 400: user data doesn't satisfy some requirements
+ :statuscode 403: authorization failed
+
+
+ **Example request**:
+
+ .. sourcecode:: http
+
+ POST /api_2/builds HTTP/1.1
+ Host: copr.fedoraproject.org
+ Authorization: Basic base64=encoded=string
+ Content-Type: application/json
+
+ {
+ "project_id": 3985,
+ "chroots": ["fedora-22-i386", "fedora-21-i386"],
+ "srpm_url": "http://miroslav.suchy.cz/copr/copr-ping-1-1.fc20.src.rpm"
+ }
+
+ **Response**:
+
+ .. sourcecode:: http
+
+ HTTP/1.1 201 CREATED
+ Location: /api_2/builds/106897
+
+Using file upload
+~~~~~~~~~~~~~~~~~
+To upload source package you MUST use ``multipart/form-data`` content type.
+Addition build information MUST present in ``metadata`` part in JSON format. Source package
+MUST be uploaded as binary ``srpm`` file.
+
+
+ **Build info**
+
+ .. code-block:: javascript
+
+ {
+ "project_id": 3985,
+ "chroots": ["fedora-22-i386", "fedora-21-i386"],
+ "enable_net": false
+ }
+
+ ================== ==================== ===============
+ Field Type Description
+ ================== ==================== ===============
+ project_id int identifier of the parent project
+ chroots list of strings what chroots should be used for build
+ enable_net bool allows to disable network access during the build, default: True
+ ================== ==================== ===============
+
+
+.. http:post:: /api_2/builds
+
+ :reqheader Content-Type: MUST be a ``multipart/form-data``
+ :formparam metadata: JSON with the build info
+ :formparam srpm: file with source package
+
+ :resheader Location: contains URL to the created build
+
+ :statuscode 201: build was successfully submitted
+ :statuscode 400: user data doesn't satisfy some requirements
+ :statuscode 403: authorization failed
+
+ **Example**
+
+ Here we use python-requests_ lib:
+
+ .. code-block:: python
+
+ >>> import json
+ >>> from requests import post
+ >>> api_url = "http://copr-fe-dev.cloud.fedoraproject.org/api_2/builds"
+ >>> api_login = "my api login"
+ >>> api_token = "my api token"
+ >>> metadata = {
+ >>> 'chroots': ['fedora-22-i386', 'fedora-21-i386'],
+ >>> 'project_id': 3985,
+ >>> }
+ >>> files = {
+ >>> "srpm": ('pkg.src.rpm', open('/path/to/pkg.src.rpm'), 'application/x-rpm'),
+ >>> "metadata": ('', json.dumps(metadata))
+ >>> # here some requests specific, see http://stackoverflow.com/questions/12385179
+ >>> }
+ >>> r = post(api_url, auth=(api_login, api_token), files=files)
+ >>> r.status_code
+ 201
+ >>> r.headers["Location"]
+ http://copr-fe-dev.cloud.fedoraproject.org/api_2/builds/106899
+
+Get build details
+-----------------
+
+.. http:get:: /api_2/builds/(int:build_id)
+
+ Returns details about build
+
+ :param int project_id: a unique identifier of the build
+ :query bool show_chroots: embed BuildChroot_ sub-resources into the result, default is False
+
+ :statuscode 200: no error
+ :statuscode 404: build not found
+
+ **Example request**
+
+ .. sourcecode:: http
+
+ GET HTTP/1.1
+ Host: copr.fedoraproject.org
+
+ **Response**
+
+ .. sourcecode:: http
+
+ HTTP/1.1 200 OK
+ Content-Type: application/json
+
+ {
+ "build_chroots": [
+ {
+ "chroot": {
+ "name": "fedora-21-i386",
+ "started_on": 1441366860,
+ "state": "succeeded",
+ "ended_on": 1441366969,
+ "result_dir_url": "http://copr-be-dev.cloud.fedoraproject.org/results/vgologuz/aeghqawgt/fed...",
+ "git_hash": "8daed2e23140243d8beaafb0fee436c1bca3fdf7"
+ },
+ "_links": {
+ "project": {
+ "href": "/api_2/projects/3985"
+ },
+ "self": {
+ "href": "/api_2/builds/106897/chroots/fedora-21-i386"
+ }
+ }
+ }
+ ],
+ "_links": {
+ "project": {
+ "href": "/api_2/projects/3985"
+ },
+ "self": {
+ "href": "/api_2/builds/106897?show_chroots=True"
+ },
+ "chroots": {
+ "href": "/api_2/builds/106897/chroots"
+ }
+ },
+ "build": {
+ "enable_net": true,
+ "source_metadata": {
+ "url": "http://miroslav.suchy.cz/copr/copr-ping-1-1.fc20.src.rpm"
+ },
+ "package_name": "copr-ping",
+ "submitted_on": 1441366834,
+ "package_version": "1-1.fc20",
+ "built_packages": [
+ {
+ "version": "1",
+ "name": "copr-ping"
+ }
+ ],
+ "started_on": null,
+ "source_type": "srpm_link",
+ "state": "succeeded",
+ "ended_on": 1441366969,
+ "id": 106897,
+ "repos": []
+ }
+ }
+
+Cancel build
+------------
+
+Build cancellation is done be setting build state to ``cancelled``.
+
+.. http:put:: /api_2/builds/(int:build_id)
+
+ **REQUIRE AUTH**
+
+ :param int project_id: a unique identifier of the build
+ :query bool show_chroots: embed BuildChroot_ sub-resources into the result, default is False
+
+ :statuscode 204: build was updated
+ :statuscode 400: malformed request, most probably build can't be canceled at the moment
+ :statuscode 404: build not found
+
+ **Example request**:
+
+ .. sourcecode:: http
+
+ PUT /api_2/builds/1 HTTP/1.1
+ Host: copr.fedoraproject.org
+ Authorization: Basic base64=encoded=string
+ Content-Type: application/json
+
+ {
+ "state": "cancelled"
+ }
+
+ **Response**
+
+ .. sourcecode:: http
+
+ HTTP/1.1 204 NO CONTENT
+
+Delete build
+------------
+.. http:delete:: /api_2/builds/(int:build_id)
+
+ **REQUIRE AUTH**
+
+ Deletes build and schedules deletion of build result at Copr backend
+
+ :param int project_id: a unique identifier of the build
+
+ :statuscode 204: build was removed
+ :statuscode 400: could not delete build right now, most probably due to unfinished build
+ :statuscode 403: authorization failed
+ :statuscode 404: build not found
+
+ **Example request**:
+
+ .. sourcecode:: http
+
+ DELETE /api_2/builds/1 HTTP/1.1
+ Host: copr.fedoraproject.org
+ Authorization: Basic base64=encoded=string
+
+ **Response**
+
+ .. sourcecode:: http
+
+ HTTP/1.1 204 NO CONTENT
+
+
+.. _python-requests: http://docs.python-requests.org/
diff --git a/frontend/docs/api_2/source/Resources/mock_chroot.rst b/frontend/docs/api_2/source/Resources/mock_chroot.rst
index a01d11a..a1c8d9d 100644
--- a/frontend/docs/api_2/source/Resources/mock_chroot.rst
+++ b/frontend/docs/api_2/source/Resources/mock_chroot.rst
@@ -45,7 +45,6 @@ List mock chroots
GET /api_2/mock_chroots?active_only=True HTTP/1.1
Host: copr.fedoraproject.org
- Accept: application/json
**Response**:
@@ -97,7 +96,6 @@ Get mock chroot details
GET /api_2/mock_chroots/fedora-rawhide-i386 HTTP/1.1
Host: copr.fedoraproject.org
- Accept: application/json
**Response**
@@ -117,9 +115,6 @@ Get mock chroot details
"_links": {
"self": {
"href": "/api_2/mock_chroots/fedora-rawhide-i386"
- },
- "all_chroots": {
- "href": "/api_2/mock_chroots"
}
}
}
diff --git a/frontend/docs/api_2/source/Resources/project.rst b/frontend/docs/api_2/source/Resources/project.rst
index a28e882..c977f4d 100644
--- a/frontend/docs/api_2/source/Resources/project.rst
+++ b/frontend/docs/api_2/source/Resources/project.rst
@@ -63,7 +63,6 @@ List projects
GET /api_2/projects HTTP/1.1
Host: copr.fedoraproject.org
- Accept: application/json
**Response**:
@@ -122,7 +121,7 @@ Create new project
POST /api_2/projects HTTP/1.1
Host: copr.fedoraproject.org
Authorization: Basic base64=encoded=string
- Accept: application/json
+ Content-Type: application/json
{
"disable_createrepo": false,
@@ -163,7 +162,6 @@ Get project details
GET /api_2/projects/2482?show_chroots=True&show_builds=True HTTP/1.1
Host: copr.fedoraproject.org
- Accept: application/json
**Response**
@@ -250,7 +248,7 @@ Delete project
.. sourcecode:: http
- DELETE /api_2/projects HTTP/1.1
+ DELETE /api_2/projects/1 HTTP/1.1
Host: copr.fedoraproject.org
Authorization: Basic base64=encoded=string
@@ -272,7 +270,7 @@ Modify project
:param project_id: a unique identifier of the Copr project.
- :statuscode 201: project was updated
+ :statuscode 204: project was updated
:statuscode 400: malformed request, see response content for details
:statuscode 403: authorization failed
:statuscode 404: project not found
@@ -284,7 +282,7 @@ Modify project
PUT /api_2/projects/1 HTTP/1.1
Host: copr.fedoraproject.org
Authorization: Basic base64=encoded=string
- Accept: application/json
+ Content-Type: application/json
{
"disable_createrepo": true,
diff --git a/frontend/docs/api_2/source/index.rst b/frontend/docs/api_2/source/index.rst
index 49cae2b..f860a63 100644
--- a/frontend/docs/api_2/source/index.rst
+++ b/frontend/docs/api_2/source/index.rst
@@ -17,7 +17,7 @@ BasicAuth_ . Token can be obtained and renewed at the CoprAPI_ page.
Resources
---------
.. toctree::
- :maxdepth: 1
+ :maxdepth: 2
Resources/project
Resources/project_chroot
8 years, 9 months
[copr] master: [frontend] validate uploaded file to .src.rpm extension (452c82b)
by frostyx@fedoraproject.org
Repository : http://git.fedorahosted.org/cgit/copr.git
On branch : master
>---------------------------------------------------------------
commit 452c82b53605558fbf4e7f78eee5c8dcfb16c826
Author: Jakub Kadlčík <jkadlcik(a)redhat.com>
Date: Fri Sep 4 09:57:13 2015 +0200
[frontend] validate uploaded file to .src.rpm extension
>---------------------------------------------------------------
frontend/coprs_frontend/coprs/forms.py | 16 +++++++++++++++-
1 files changed, 15 insertions(+), 1 deletions(-)
diff --git a/frontend/coprs_frontend/coprs/forms.py b/frontend/coprs_frontend/coprs/forms.py
index 3df51ea..1a33a2b 100644
--- a/frontend/coprs_frontend/coprs/forms.py
+++ b/frontend/coprs_frontend/coprs/forms.py
@@ -69,6 +69,18 @@ class UrlSrpmListValidator(UrlListValidator):
return True
+class SrpmValidator(object):
+ def __init__(self, message=None):
+ if not message:
+ message = "You can upload only .src.rpm and .nosrc.rpm files"
+ self.message = message
+
+ def __call__(self, form, field):
+ filename = field.data.filename.lower()
+ if not filename.endswith((".src.rpm", ".nosrc.rpm")):
+ raise wtforms.ValidationError(self.message)
+
+
class CoprUniqueNameValidator(object):
def __init__(self, message=None, owner=None):
@@ -311,7 +323,9 @@ class BuildFormUploadFactory(object):
selected.append(ch)
return selected
- pkgs = FileField('srpm', validators=[FileRequired()])
+ pkgs = FileField('srpm', validators=[
+ FileRequired(),
+ SrpmValidator()])
memory_reqs = wtforms.IntegerField(
"Memory requirements",
8 years, 9 months
[copr] master: [backend] replace python-bunch with python-munch (9763ea5)
by frostyx@fedoraproject.org
Repository : http://git.fedorahosted.org/cgit/copr.git
On branch : master
>---------------------------------------------------------------
commit 9763ea50479dcd0c6674586079723468cbd32ab0
Author: Jakub Kadlčík <jkadlcik(a)redhat.com>
Date: Fri Sep 4 08:53:48 2015 +0200
[backend] replace python-bunch with python-munch
>---------------------------------------------------------------
backend/backend/actions.py | 4 ++--
backend/backend/daemons/backend.py | 4 ++--
backend/backend/daemons/dispatcher.py | 2 +-
backend/backend/daemons/job_grab.py | 2 +-
backend/backend/helpers.py | 6 +++---
backend/backend/mockremote/__init__.py | 6 +++---
backend/backend/sign.py | 2 +-
backend/backend/vm_manage/manager.py | 2 +-
backend/copr-backend.spec | 5 ++---
backend/run/copr_be.py | 4 ++--
backend/tests/deamons/test_backend.py | 6 +++---
backend/tests/deamons/test_dispatcher.py | 6 +++---
backend/tests/deamons/test_job_grab.py | 4 ++--
backend/tests/deamons/test_log.py | 4 ++--
backend/tests/deamons/test_vm_master.py | 6 +++---
backend/tests/mockremote/test_builder.py | 8 ++++----
backend/tests/mockremote/test_mockremote.py | 6 +++---
backend/tests/run/test_copr_prune_results.py | 4 ++--
backend/tests/test_action.py | 4 ++--
backend/tests/test_frontend.py | 4 ++--
backend/tests/test_helpers.py | 4 ++--
backend/tests/test_sign.py | 4 ++--
backend/tests/vm_manager/test_check.py | 6 +++---
backend/tests/vm_manager/test_event_handle.py | 6 +++---
backend/tests/vm_manager/test_executor.py | 6 +++---
backend/tests/vm_manager/test_manager.py | 6 +++---
backend/tests/vm_manager/test_spawn.py | 6 +++---
backend/tests/vm_manager/test_terminate.py | 6 +++---
28 files changed, 66 insertions(+), 67 deletions(-)
diff --git a/backend/backend/actions.py b/backend/backend/actions.py
index 8c0103d..7548757 100644
--- a/backend/backend/actions.py
+++ b/backend/backend/actions.py
@@ -4,7 +4,7 @@ import shutil
import time
from urllib import urlretrieve
-from bunch import Bunch
+from munch import Munch
from .createrepo import createrepo, createrepo_unsafe
from exceptions import CreateRepoError
@@ -212,7 +212,7 @@ class Action(object):
def run(self):
""" Handle action (other then builds) - like rename or delete of project """
- result = Bunch()
+ result = Munch()
result.id = self.data["id"]
action_type = self.data["action_type"]
diff --git a/backend/backend/daemons/backend.py b/backend/backend/daemons/backend.py
index ddf1565..a78791d 100644
--- a/backend/backend/daemons/backend.py
+++ b/backend/backend/daemons/backend.py
@@ -49,7 +49,7 @@ class CoprBackend(object):
def __init__(self, config_file=None, ext_opts=None):
# read in config file
- # put all the config items into a single self.opts bunch
+ # put all the config items into a single self.opts munch
if not config_file:
raise CoprBackendError("Must specify config_file")
@@ -303,7 +303,7 @@ def run_backend(opts):
"""
Start main backend daemon
- :param opts: Bunch object with command line options
+ :param opts: Munch object with command line options
Expected **opts** fields:
- `config_file` - path to the backend config file
diff --git a/backend/backend/daemons/dispatcher.py b/backend/backend/daemons/dispatcher.py
index 70953e8..3b379b2 100644
--- a/backend/backend/daemons/dispatcher.py
+++ b/backend/backend/daemons/dispatcher.py
@@ -36,7 +36,7 @@ class Worker(multiprocessing.Process):
Worker listens for the new tasks from :py:class:`retask.Queue` associated with its group_id
- :param Bunch opts: backend config
+ :param Munch opts: backend config
:param int worker_num: worker number
:param int group_id: group_id from the set of groups defined in config
:param lock: (:py:class:`multiprocessing.Lock`) global backend lock
diff --git a/backend/backend/daemons/job_grab.py b/backend/backend/daemons/job_grab.py
index 36aea77..92d8504 100644
--- a/backend/backend/daemons/job_grab.py
+++ b/backend/backend/daemons/job_grab.py
@@ -38,7 +38,7 @@ class CoprJobGrab(Process):
- run Action handler for action tasks
- :param Bunch opts: backend config
+ :param Munch opts: backend config
:type frontend_client: FrontendClient
:param lock: :py:class:`multiprocessing.Lock` global backend lock
diff --git a/backend/backend/helpers.py b/backend/backend/helpers.py
index 148c61a..0bcf97f 100644
--- a/backend/backend/helpers.py
+++ b/backend/backend/helpers.py
@@ -19,7 +19,7 @@ from datetime import datetime
import pytz
# from dateutil.parser import parse as dt_parse
-from bunch import Bunch
+from munch import Munch
from redis import StrictRedis
from . import constants
@@ -112,7 +112,7 @@ class BackendConfigReader(object):
cp = ConfigParser.ConfigParser()
cp.read(self.config_file)
- opts = Bunch()
+ opts = Munch()
opts.results_baseurl = _get_conf(
cp, "backend", "results_baseurl", "http://copr-be")
@@ -220,7 +220,7 @@ class BackendConfigReader(object):
opts.prune_days = _get_conf(cp, "backend", "prune_days", None, mode="int")
# ssh options
- opts.ssh = Bunch()
+ opts.ssh = Munch()
# TODO: ansible Runner show some magic bugs with transport "ssh", using paramiko
opts.ssh.transport = _get_conf(
cp, "ssh", "transport", "paramiko")
diff --git a/backend/backend/mockremote/__init__.py b/backend/backend/mockremote/__init__.py
index c96190d..aec2081 100755
--- a/backend/backend/mockremote/__init__.py
+++ b/backend/backend/mockremote/__init__.py
@@ -36,7 +36,7 @@ import fcntl
import logging
import os
-from bunch import Bunch
+from munch import Munch
import time
from ..constants import DEF_REMOTE_BASEDIR, DEF_BUILD_TIMEOUT, DEF_REPOS, \
@@ -104,7 +104,7 @@ class MockRemote(object):
:param multiprocessing.Lock lock: instance of Lock shared between
Copr backend process
- :param Bunch opts: builder options, used keys::
+ :param Munch opts: builder options, used keys::
:ivar build_user: user to run as/connect as on builder systems
:ivar do_sign: enable package signing, require configured
signer host and correct /etc/sign.conf
@@ -118,7 +118,7 @@ class MockRemote(object):
# :param bool recurse: if more than one pkg and it fails to build,
# try to build the rest and come back to it
"""
- self.opts = Bunch(
+ self.opts = Munch(
do_sign=False,
frontend_base_url=None,
results_baseurl=u"",
diff --git a/backend/backend/sign.py b/backend/backend/sign.py
index 5b49599..4965c9c 100755
--- a/backend/backend/sign.py
+++ b/backend/backend/sign.py
@@ -101,7 +101,7 @@ def sign_rpms_in_dir(username, projectname, path, opts, log):
:param username: copr username
:param projectname: copr projectname
:param path: directory with rpms to be signed
- :param Bunch opts: backend config
+ :param Munch opts: backend config
:type log: logging.Logger
diff --git a/backend/backend/vm_manage/manager.py b/backend/backend/vm_manage/manager.py
index fee4b07..11010b0 100644
--- a/backend/backend/vm_manage/manager.py
+++ b/backend/backend/vm_manage/manager.py
@@ -115,7 +115,7 @@ class VmManager(object):
- Client to acquire and release VM in builder process
:param opts: Global backend configuration
- :type opts: Bunch
+ :type opts: Munch
:type logger: logging.Logger
:param logger: Logger instance to use inside of manager, if None manager would create
diff --git a/backend/copr-backend.spec b/backend/copr-backend.spec
index bc985fe..3cea572 100644
--- a/backend/copr-backend.spec
+++ b/backend/copr-backend.spec
@@ -34,8 +34,7 @@ BuildRequires: pytest
BuildRequires: python-pytest-cov
BuildRequires: python-mock
BuildRequires: python-six
-# missing python3 - migrate to python3-munch?
-BuildRequires: python-bunch
+BuildRequires: python-munch
# missing python3
BuildRequires: python-daemon
BuildRequires: python-lockfile
@@ -67,7 +66,7 @@ Requires: openssh-clients
Requires: mock
Requires: yum-utils
Requires: createrepo_c >= 0.2.1-3
-Requires: python-bunch
+Requires: python-munch
Requires: python-daemon
Requires: python-lockfile
Requires: python-requests
diff --git a/backend/run/copr_be.py b/backend/run/copr_be.py
index c42980c..7c2565d 100755
--- a/backend/run/copr_be.py
+++ b/backend/run/copr_be.py
@@ -10,7 +10,7 @@ import optparse
import os
import sys
-from bunch import Bunch
+from munch import Munch
from backend.daemons.backend import run_backend
@@ -43,7 +43,7 @@ def parse_args(args):
sys.exit(1)
opts.config_file = os.path.abspath(opts.config_file)
- ret_opts = Bunch()
+ ret_opts = Munch()
for o in ("daemonize", "exit_on_worker", "pidfile",
"config_file", "daemon_user", "daemon_group"):
setattr(ret_opts, o, getattr(opts, o))
diff --git a/backend/tests/deamons/test_backend.py b/backend/tests/deamons/test_backend.py
index d36797a..01e61f5 100644
--- a/backend/tests/deamons/test_backend.py
+++ b/backend/tests/deamons/test_backend.py
@@ -3,7 +3,7 @@ import tempfile
import shutil
import time
-from bunch import Bunch
+from munch import Munch
import pwd
import pytest
import retask
@@ -78,7 +78,7 @@ class TestBackend(object):
# effective config options
self.bc_obj = MagicMock()
- self.opts = Bunch(
+ self.opts = Munch(
build_groups=[
{
"id": 0,
@@ -111,7 +111,7 @@ class TestBackend(object):
self.grp = self.grp_patcher.start()
self.pwd = self.pwd_patcher.start()
- self.run_opts = Bunch(
+ self.run_opts = Munch(
daemonize=True,
pidfile=self.pidfile_path,
config_file=self.config_file,
diff --git a/backend/tests/deamons/test_dispatcher.py b/backend/tests/deamons/test_dispatcher.py
index 1364100..7fca938 100644
--- a/backend/tests/deamons/test_dispatcher.py
+++ b/backend/tests/deamons/test_dispatcher.py
@@ -5,7 +5,7 @@ import pprint
from subprocess import CalledProcessError
from ansible.errors import AnsibleError
-from bunch import Bunch
+from munch import Munch
import pytest
import tempfile
import shutil
@@ -125,8 +125,8 @@ class TestDispatcher(object):
self.spawn_pb = "/spawn.yml"
self.terminate_pb = "/terminate.yml"
- self.opts = Bunch(
- ssh=Bunch(transport="paramiko"),
+ self.opts = Munch(
+ ssh=Munch(transport="paramiko"),
frontend_base_url="http://example.com",
frontend_auth="12345678",
build_groups={
diff --git a/backend/tests/deamons/test_job_grab.py b/backend/tests/deamons/test_job_grab.py
index fc06315..0ff8068 100644
--- a/backend/tests/deamons/test_job_grab.py
+++ b/backend/tests/deamons/test_job_grab.py
@@ -9,7 +9,7 @@ import copy
import json
import logging
-from bunch import Bunch
+from munch import Munch
import time
import requests
@@ -84,7 +84,7 @@ class TestJobGrab(object):
self.log_dir = os.path.join(self.tmp_dir_path, "copr")
self.log_file = os.path.join(self.log_dir, "copr.log")
- self.opts = Bunch(
+ self.opts = Munch(
verbose=False,
build_groups=[
{"id": 0, "name": "x86",
diff --git a/backend/tests/deamons/test_log.py b/backend/tests/deamons/test_log.py
index 8b4ed52..616be67 100644
--- a/backend/tests/deamons/test_log.py
+++ b/backend/tests/deamons/test_log.py
@@ -1,7 +1,7 @@
# coding: utf-8
import logging
-from bunch import Bunch
+from munch import Munch
import time
import tempfile
@@ -51,7 +51,7 @@ class TestLog(object):
self.log_dir = os.path.join(self.tmp_dir_path, "copr")
self.log_file = os.path.join(self.log_dir, "copr.log")
- self.opts = Bunch(
+ self.opts = Munch(
verbose=False,
log_dir=self.log_dir
)
diff --git a/backend/tests/deamons/test_vm_master.py b/backend/tests/deamons/test_vm_master.py
index 0eb286a..df236d8 100644
--- a/backend/tests/deamons/test_vm_master.py
+++ b/backend/tests/deamons/test_vm_master.py
@@ -11,7 +11,7 @@ from collections import defaultdict
import json
from random import choice
import types
-from bunch import Bunch
+from munch import Munch
import time
from multiprocessing import Queue
@@ -78,10 +78,10 @@ class TestVmMaster(object):
def setup_method(self, method):
self.vm_spawn_min_interval = 30
- self.opts = Bunch(
+ self.opts = Munch(
redis_db=9,
redis_port=7777,
- ssh=Bunch(
+ ssh=Munch(
transport="ssh"
),
build_groups_count=2,
diff --git a/backend/tests/mockremote/test_builder.py b/backend/tests/mockremote/test_builder.py
index fd1e1cf..1b648a5 100644
--- a/backend/tests/mockremote/test_builder.py
+++ b/backend/tests/mockremote/test_builder.py
@@ -4,7 +4,7 @@ import copy
from collections import defaultdict
from pprint import pprint
import socket
-from bunch import Bunch
+from munch import Munch
from backend.exceptions import BuilderError, BuilderTimeOutError, AnsibleCallError, AnsibleResponseError, VmError
import tempfile
@@ -78,8 +78,8 @@ class TestBuilder(object):
STDERR = "stderr"
RESULT_DIR = "/tmp"
- opts = Bunch(
- ssh=Bunch(
+ opts = Munch(
+ ssh=Munch(
transport="paramiko"
),
build_user=BUILDER_USER,
@@ -112,7 +112,7 @@ class TestBuilder(object):
"package_name": self.BUILDER_PKG_NAME,
"package_version": self.BUILDER_PKG_VERSION
- }, Bunch({
+ }, Munch({
"timeout": 1800,
"destdir": self.test_root_path,
"results_baseurl": "/tmp",
diff --git a/backend/tests/mockremote/test_mockremote.py b/backend/tests/mockremote/test_mockremote.py
index d9986b8..fb13c13 100644
--- a/backend/tests/mockremote/test_mockremote.py
+++ b/backend/tests/mockremote/test_mockremote.py
@@ -2,7 +2,7 @@
import copy
from collections import defaultdict
-from bunch import Bunch
+from munch import Munch
from backend.exceptions import MockRemoteError, CoprSignError, BuilderError
import tempfile
@@ -77,13 +77,13 @@ class TestMockRemote(object):
"package_name": self.PKG_NAME,
"package_version": self.PKG_VERSION
- }, Bunch({
+ }, Munch({
"timeout": 1800,
"destdir": self.test_root_path,
"results_baseurl": "/tmp/",
}))
- self.OPTS = Bunch({
+ self.OPTS = Munch({
"do_sign": False,
"results_baseurl": self.BASE_URL,
"frontend_base_url": self.FRONT_URL,
diff --git a/backend/tests/run/test_copr_prune_results.py b/backend/tests/run/test_copr_prune_results.py
index f599656..62ba57c 100644
--- a/backend/tests/run/test_copr_prune_results.py
+++ b/backend/tests/run/test_copr_prune_results.py
@@ -6,7 +6,7 @@ import shutil
import tarfile
import tempfile
import time
-from bunch import Bunch
+from munch import Munch
from subprocess import Popen, PIPE
from copr.client.exceptions import CoprException
@@ -78,7 +78,7 @@ class TestPruneResults(object):
self.pkg_2_obsolete = "hello-1.0.fc20"
self.prj = "foox"
self.chroots = ["fedora-20-i386", "fedora-20-x86_64"]
- self.opts = Bunch(
+ self.opts = Munch(
prune_days=14,
find_obsolete_script=os.path.abspath(
diff --git a/backend/tests/test_action.py b/backend/tests/test_action.py
index cdd6bc7..252d39d 100644
--- a/backend/tests/test_action.py
+++ b/backend/tests/test_action.py
@@ -4,7 +4,7 @@ import tempfile
import shutil
import time
import tarfile
-from bunch import Bunch
+from munch import Munch
import pytest
import six
@@ -45,7 +45,7 @@ class TestAction(object):
"chroots": ["fedora20", "epel7"]
})
- self.opts = Bunch(
+ self.opts = Munch(
redis_db=9,
redis_port=7777,
diff --git a/backend/tests/test_frontend.py b/backend/tests/test_frontend.py
index b2ee80f..8f5fb94 100644
--- a/backend/tests/test_frontend.py
+++ b/backend/tests/test_frontend.py
@@ -2,7 +2,7 @@
import multiprocessing
-from bunch import Bunch
+from munch import Munch
from requests import RequestException
import six
@@ -34,7 +34,7 @@ def mc_time():
class TestFrontendClient(object):
def setup_method(self, method):
- self.opts = Bunch(
+ self.opts = Munch(
frontend_base_url="http://example.com/",
frontend_auth="12345678",
)
diff --git a/backend/tests/test_helpers.py b/backend/tests/test_helpers.py
index 861fa7b..f987a35 100644
--- a/backend/tests/test_helpers.py
+++ b/backend/tests/test_helpers.py
@@ -10,7 +10,7 @@ import time
from multiprocessing import Queue
import types
-from bunch import Bunch
+from munch import Munch
from redis import ConnectionError
import six
from backend.exceptions import CoprSpawnFailError
@@ -39,7 +39,7 @@ MODULE_REF = "backend.helpers"
class TestHelpers(object):
def setup_method(self, method):
- self.opts = Bunch(
+ self.opts = Munch(
redis_db=9,
redis_port=7777,
)
diff --git a/backend/tests/test_sign.py b/backend/tests/test_sign.py
index 8c857b9..df20b05 100644
--- a/backend/tests/test_sign.py
+++ b/backend/tests/test_sign.py
@@ -3,7 +3,7 @@ import tempfile
import shutil
import time
-from bunch import Bunch
+from munch import Munch
import pytest
import six
@@ -33,7 +33,7 @@ class TestSign(object):
self.test_time = time.time()
self.tmp_dir_path = None
- self.opts = Bunch(keygen_host="example.com")
+ self.opts = Munch(keygen_host="example.com")
def teardown_method(self, method):
if self.tmp_dir_path:
diff --git a/backend/tests/vm_manager/test_check.py b/backend/tests/vm_manager/test_check.py
index 896786f..5e66d92 100644
--- a/backend/tests/vm_manager/test_check.py
+++ b/backend/tests/vm_manager/test_check.py
@@ -8,7 +8,7 @@ import time
from multiprocessing import Queue
import types
-from bunch import Bunch
+from munch import Munch
from redis import ConnectionError
import six
from backend.exceptions import CoprSpawnFailError
@@ -73,10 +73,10 @@ class TestChecker(object):
def setup_method(self, method):
self.test_root_path = tempfile.mkdtemp()
self.terminate_pb_path = "{}/terminate.yml".format(self.test_root_path)
- self.opts = Bunch(
+ self.opts = Munch(
redis_db=9,
redis_port=7777,
- ssh=Bunch(
+ ssh=Munch(
transport="ssh"
),
build_groups={
diff --git a/backend/tests/vm_manager/test_event_handle.py b/backend/tests/vm_manager/test_event_handle.py
index f1e7192..591bb8b 100644
--- a/backend/tests/vm_manager/test_event_handle.py
+++ b/backend/tests/vm_manager/test_event_handle.py
@@ -7,7 +7,7 @@ import time
from multiprocessing import Queue
import types
-from bunch import Bunch
+from munch import Munch
from redis.client import Script
import six
@@ -77,10 +77,10 @@ class TestEventHandle(object):
def setup_method(self, method):
self.test_root_path = tempfile.mkdtemp()
self.terminate_pb_path = "{}/terminate.yml".format(self.test_root_path)
- self.opts = Bunch(
+ self.opts = Munch(
redis_db=9,
redis_port=7777,
- ssh=Bunch(
+ ssh=Munch(
transport="ssh"
),
build_groups={
diff --git a/backend/tests/vm_manager/test_executor.py b/backend/tests/vm_manager/test_executor.py
index 9d20037..79d8410 100644
--- a/backend/tests/vm_manager/test_executor.py
+++ b/backend/tests/vm_manager/test_executor.py
@@ -3,7 +3,7 @@
from multiprocessing import Queue
import types
-from bunch import Bunch
+from munch import Munch
import six
import time
@@ -35,10 +35,10 @@ def mc_time():
class TestExecutor(object):
def setup_method(self, method):
- self.opts = Bunch(
+ self.opts = Munch(
redis_db=9,
redis_port=7777,
- ssh=Bunch(
+ ssh=Munch(
transport="ssh"
),
build_groups={
diff --git a/backend/tests/vm_manager/test_manager.py b/backend/tests/vm_manager/test_manager.py
index 787cb6a..b8f60d2 100644
--- a/backend/tests/vm_manager/test_manager.py
+++ b/backend/tests/vm_manager/test_manager.py
@@ -6,7 +6,7 @@ import types
import time
from multiprocessing import Queue
-from bunch import Bunch
+from munch import Munch
import six
from backend import exceptions
@@ -47,10 +47,10 @@ class TestCallback(object):
class TestManager(object):
def setup_method(self, method):
- self.opts = Bunch(
+ self.opts = Munch(
redis_db=9,
redis_port=7777,
- ssh=Bunch(
+ ssh=Munch(
transport="ssh"
),
build_groups_count=1,
diff --git a/backend/tests/vm_manager/test_spawn.py b/backend/tests/vm_manager/test_spawn.py
index 3b9e849..277270c 100644
--- a/backend/tests/vm_manager/test_spawn.py
+++ b/backend/tests/vm_manager/test_spawn.py
@@ -6,7 +6,7 @@ import time
from multiprocessing import Queue
import types
-from bunch import Bunch
+from munch import Munch
from redis import ConnectionError
import six
from backend.exceptions import CoprSpawnFailError
@@ -70,10 +70,10 @@ class TestSpawner(object):
def setup_method(self, method):
self.test_root_path = tempfile.mkdtemp()
self.spawn_pb_path = "{}/spawn.yml".format(self.test_root_path)
- self.opts = Bunch(
+ self.opts = Munch(
redis_db=9,
redis_port=7777,
- ssh=Bunch(
+ ssh=Munch(
transport="ssh"
),
build_groups={
diff --git a/backend/tests/vm_manager/test_terminate.py b/backend/tests/vm_manager/test_terminate.py
index ef79ad4..f60329a 100644
--- a/backend/tests/vm_manager/test_terminate.py
+++ b/backend/tests/vm_manager/test_terminate.py
@@ -7,7 +7,7 @@ import time
from multiprocessing import Queue
import types
-from bunch import Bunch
+from munch import Munch
from redis import ConnectionError
import six
from backend.exceptions import CoprSpawnFailError
@@ -72,9 +72,9 @@ class TestTerminate(object):
def setup_method(self, method):
self.test_root_path = tempfile.mkdtemp()
self.terminate_pb_path = "{}/terminate.yml".format(self.test_root_path)
- self.opts = Bunch(
+ self.opts = Munch(
redis_port=7777,
- ssh=Bunch(
+ ssh=Munch(
transport="ssh"
),
build_groups={
8 years, 9 months
[copr] master: python-flask-openid now provides python3 subpackage (9888b05)
by Miroslav Suchý
Repository : http://git.fedorahosted.org/cgit/copr.git
On branch : master
>---------------------------------------------------------------
commit 9888b0564724bc3d7dbfb2b8ad76aa2dd781a4f1
Author: Miroslav Suchý <msuchy(a)redhat.com>
Date: Fri Sep 4 08:35:51 2015 +0200
python-flask-openid now provides python3 subpackage
>---------------------------------------------------------------
frontend/copr-frontend.spec | 1 -
1 files changed, 0 insertions(+), 1 deletions(-)
diff --git a/frontend/copr-frontend.spec b/frontend/copr-frontend.spec
index 01b9aec..fd80c27 100644
--- a/frontend/copr-frontend.spec
+++ b/frontend/copr-frontend.spec
@@ -38,7 +38,6 @@ Requires: mod_wsgi
Requires: passwd
Requires: python-alembic
Requires: python-flask
-# missing python3
Requires: python-flask-openid
Requires: python-flask-wtf
Requires: python-flask-sqlalchemy
8 years, 9 months
[copr] master: [frontend][API docs] covered MockChroots (d8d5e4c)
by vgologuz@fedoraproject.org
Repository : http://git.fedorahosted.org/cgit/copr.git
On branch : master
>---------------------------------------------------------------
commit d8d5e4c4ec8f1b1ab6221bc3130910716cbc723d
Author: Valentin Gologuzov <vgologuz(a)redhat.com>
Date: Thu Sep 3 23:27:05 2015 +0200
[frontend][API docs] covered MockChroots
>---------------------------------------------------------------
frontend/coprs_frontend/coprs/rest_api/common.py | 1 -
.../coprs/rest_api/resources/mock_chroot.py | 1 -
.../docs/api_2/source/Resources/mock_chroot.rst | 126 ++++++++++++++++++++
.../docs/api_2/source/Resources/project_chroot.rst | 3 +-
frontend/docs/api_2/source/index.rst | 30 +++--
5 files changed, 143 insertions(+), 18 deletions(-)
diff --git a/frontend/coprs_frontend/coprs/rest_api/common.py b/frontend/coprs_frontend/coprs/rest_api/common.py
index 124ed83..01a787e 100644
--- a/frontend/coprs_frontend/coprs/rest_api/common.py
+++ b/frontend/coprs_frontend/coprs/rest_api/common.py
@@ -71,7 +71,6 @@ def render_build_chroot(chroot):
}
-
def rest_api_auth_required(f):
# todo: move to common.py and test this
@functools.wraps(f)
diff --git a/frontend/coprs_frontend/coprs/rest_api/resources/mock_chroot.py b/frontend/coprs_frontend/coprs/rest_api/resources/mock_chroot.py
index 2255d4b..c7135de 100644
--- a/frontend/coprs_frontend/coprs/rest_api/resources/mock_chroot.py
+++ b/frontend/coprs_frontend/coprs/rest_api/resources/mock_chroot.py
@@ -14,7 +14,6 @@ def render_mock_chroot(chroot):
"chroot": MockChrootSchema().dump(chroot)[0],
"_links": {
"self": {"href": url_for(".mockchrootr", name=chroot.name)},
- "all_chroots": {"href": url_for(".mockchrootlistr")}
},
}
diff --git a/frontend/docs/api_2/source/Resources/mock_chroot.rst b/frontend/docs/api_2/source/Resources/mock_chroot.rst
index e69de29..a01d11a 100644
--- a/frontend/docs/api_2/source/Resources/mock_chroot.rst
+++ b/frontend/docs/api_2/source/Resources/mock_chroot.rst
@@ -0,0 +1,126 @@
+Mock Chroot
+===========
+
+Mock chroot resources represents available chroots for builds. API provides only read-only access,
+since configuration of the build chroots is done by the service administrator.
+
+Structure of the mock chroot entity
+-----------------------------------
+
+.. code-block:: javascript
+
+ {
+ "name": "epel-6-i386",
+ "os_release": "epel",
+ "os_version": "6",
+ "arch": "i386",
+ "is_active": true
+ }
+
+Mock Chroot fields
+~~~~~~~~~~~~~~~~~~
+================== ==================== ===============
+Field Type Description
+================== ==================== ===============
+name str chroot name
+os_release str name of distribution system, e.g.: epel, fedora
+os_version str version of distribution system, e.g.: 7, 22
+arch str architecture of distribution, e.g.: i386, x86_64, ppc64le
+is_active bool defines if this chroot is available for builds
+================== ==================== ===============
+
+List mock chroots
+-----------------
+.. http:get:: /api_2/mock_chroots
+
+ Returns a list of mock chroots
+
+ :query param active_only: when set to True shows only active mock chroots
+
+ :statuscode 200: no error
+
+ **Example request**:
+
+ .. sourcecode:: http
+
+ GET /api_2/mock_chroots?active_only=True HTTP/1.1
+ Host: copr.fedoraproject.org
+ Accept: application/json
+
+ **Response**:
+
+ .. sourcecode:: http
+
+ HTTP/1.1 200 OK
+ Content-Type: application/json
+
+ {
+ "chroots": [
+ {
+ "chroot": {
+ "name": "epel-6-i386",
+ "os_release": "epel",
+ "os_version": "6",
+ "arch": "i386",
+ "is_active": true
+ },
+ "_links": {
+ "self": {
+ "href": "/api_2/mock_chroots/epel-6-i386"
+ }
+ }
+ },
+ { },
+ ],
+ "_links": {
+ "self": {
+ "href": "/api_2/mock_chroots?active_only=True"
+ }
+ }
+ }
+
+Get mock chroot details
+-----------------------
+.. http:get:: /api_2/mock_chroots/(str:chroot_name)
+
+ Returns mock chroot details
+
+ :param str chroot_name: Uniquer mock chroot name
+
+
+ :statuscode 200: no error
+ :statuscode 404: mock chroot not found by the given name
+
+ **Example request**
+
+ .. sourcecode:: http
+
+ GET /api_2/mock_chroots/fedora-rawhide-i386 HTTP/1.1
+ Host: copr.fedoraproject.org
+ Accept: application/json
+
+ **Response**
+
+ .. sourcecode:: http
+
+ HTTP/1.1 200 OK
+ Content-Type: application/json
+
+ {
+ "chroot": {
+ "name": "fedora-rawhide-i386",
+ "os_release": "fedora",
+ "os_version": "rawhide",
+ "arch": "i386",
+ "is_active": true
+ },
+ "_links": {
+ "self": {
+ "href": "/api_2/mock_chroots/fedora-rawhide-i386"
+ },
+ "all_chroots": {
+ "href": "/api_2/mock_chroots"
+ }
+ }
+ }
+
diff --git a/frontend/docs/api_2/source/Resources/project_chroot.rst b/frontend/docs/api_2/source/Resources/project_chroot.rst
index d63dec7..a865afd 100644
--- a/frontend/docs/api_2/source/Resources/project_chroot.rst
+++ b/frontend/docs/api_2/source/Resources/project_chroot.rst
@@ -3,7 +3,7 @@ Project Chroot
Projects Chroots allows to view and modify project settings dedicated for different chroots.
-Structure if the project chroot entity
+Structure of the project chroot entity
--------------------------------------
.. code-block:: javascript
@@ -206,7 +206,6 @@ Modify project chroot
---------------------
.. http:put:: /api_2/projects/(int:project_id)/chroots/(str:chroot_name)
-
**REQUIRE AUTH**
Updated project chroot settings
diff --git a/frontend/docs/api_2/source/index.rst b/frontend/docs/api_2/source/index.rst
index 5570793..49cae2b 100644
--- a/frontend/docs/api_2/source/index.rst
+++ b/frontend/docs/api_2/source/index.rst
@@ -14,6 +14,22 @@ Endpoint of the the API is ``/api_2``, public data is available without authoriz
To create new projects, submit builds and do other modification requests you will need to provide API token using
BasicAuth_ . Token can be obtained and renewed at the CoprAPI_ page.
+Resources
+---------
+.. toctree::
+ :maxdepth: 1
+
+ Resources/project
+ Resources/project_chroot
+ Resources/build
+ Resources/build_chroot
+ Resources/mock_chroot
+
+
+HETEOAS
+-------
+
+
This API implements HETEOAS_ in the very simple form: each entity is accompanied with set of relative links
to other related entities. HETEOAS makes API self discoverable, so you shouldn't learn how to access sub-resources.
Here is a short example with the content of API root:
@@ -89,20 +105,6 @@ Errors
______
todo:
-
-Resources
----------
-
-================== ============
-Resource Description
-================== ============
-Project_ represents copr projects and operations with them.
-`Project Chroot`_ allows to view and edit project settings specific for different chroots
-================== ============
-
.. _BasicAuth: https://en.wikipedia.org/wiki/Basic_access_authentication
.. _CoprAPI: https://copr.fedoraproject.org/api
.. _HETEOAS: https://en.wikipedia.org/wiki/HATEOAS
-
-.. _Project: Resources/project.html
-.. _Project Chroot: Resources/project_chroot.html
8 years, 9 months
[copr] master: [frontend][API docs] added docs for ProjectChroot (05bdd3e)
by vgologuz@fedoraproject.org
Repository : http://git.fedorahosted.org/cgit/copr.git
On branch : master
>---------------------------------------------------------------
commit 05bdd3e7f6d71e9285be1fd798b33fa88187d0d1
Author: Valentin Gologuzov <vgologuz(a)redhat.com>
Date: Thu Sep 3 20:27:43 2015 +0200
[frontend][API docs] added docs for ProjectChroot
>---------------------------------------------------------------
frontend/coprs_frontend/coprs/models.py | 7 +
.../coprs/rest_api/resources/project.py | 4 +-
.../coprs/rest_api/resources/project_chroot.py | 2 +-
frontend/docs/api_2/source/Resources/project.rst | 22 +-
.../docs/api_2/source/Resources/project_chroot.rst | 237 ++++++++++++++++++++
5 files changed, 257 insertions(+), 15 deletions(-)
diff --git a/frontend/coprs_frontend/coprs/models.py b/frontend/coprs_frontend/coprs/models.py
index 79fd77e..a8844f4 100644
--- a/frontend/coprs_frontend/coprs/models.py
+++ b/frontend/coprs_frontend/coprs/models.py
@@ -190,6 +190,13 @@ class Copr(db.Model, helpers.Serializer):
return filter(lambda x: x.is_active, self.mock_chroots)
@property
+ def active_copr_chroots(self):
+ """
+ :rtype: list of CoprChroot
+ """
+ return [c for c in self.copr_chroots if c.is_active]
+
+ @property
def active_chroots_sorted(self):
"""
Return list of active mock_chroots of this copr
diff --git a/frontend/coprs_frontend/coprs/rest_api/resources/project.py b/frontend/coprs_frontend/coprs/rest_api/resources/project.py
index cee7bf7..48ace15 100644
--- a/frontend/coprs_frontend/coprs/rest_api/resources/project.py
+++ b/frontend/coprs_frontend/coprs/rest_api/resources/project.py
@@ -158,7 +158,5 @@ class ProjectR(Resource):
except InsufficientRightsException as err:
raise AccessForbidden(str(err))
- resp = make_response("", 201)
- resp.headers["Location"] = url_for(".projectr", project_id=project.id)
- return resp
+ return "", 204
diff --git a/frontend/coprs_frontend/coprs/rest_api/resources/project_chroot.py b/frontend/coprs_frontend/coprs/rest_api/resources/project_chroot.py
index 294114c..f5ec11d 100644
--- a/frontend/coprs_frontend/coprs/rest_api/resources/project_chroot.py
+++ b/frontend/coprs_frontend/coprs/rest_api/resources/project_chroot.py
@@ -29,7 +29,7 @@ class ProjectChrootListR(Resource):
return {
"chroots": [
render_copr_chroot(chroot)
- for chroot in copr.copr_chroots
+ for chroot in copr.active_copr_chroots
],
"_links": {
"self": {"href": url_for(".projectchrootlistr", project_id=project_id)}
diff --git a/frontend/docs/api_2/source/Resources/project.rst b/frontend/docs/api_2/source/Resources/project.rst
index 450e8f6..a28e882 100644
--- a/frontend/docs/api_2/source/Resources/project.rst
+++ b/frontend/docs/api_2/source/Resources/project.rst
@@ -108,6 +108,13 @@ Create new project
Additionally to described before `Project fields`_ the user could specify field `chroots` which contains list of chroots to be enabled.
Available `chroot` names could be obtained from MockChrootResource_
+ :resheader Location: contains URL to the newly created project entity
+
+ :statuscode 201: project was successfully created
+ :statuscode 400: given data for project creation doesn't satisfy some requirements
+ :statuscode 401: the user already has project with the same name
+ :statuscode 403: authorization failed
+
**Example request**:
.. sourcecode:: http
@@ -135,12 +142,6 @@ Create new project
HTTP/1.1 201 CREATED
Location: /api_2/projects/<new project id>
- :resheader Location: contains URL to the newly created project entity
-
- :statuscode 201: project was successfully created
- :statuscode 400: given data for project creation doesn't satisfy some requirements
- :statuscode 401: the user already has project with the same name
- :statuscode 403: authorization failed
Get project details
-------------------
@@ -148,7 +149,7 @@ Get project details
Returns details about Copr project
- :param project_id: a unique identifier of the Copr project.
+ :param int project_id: a unique identifier of the Copr project.
:query bool show_builds: embed Build_ entities owned by this project into the result, default is False
:query bool show_chroots: embed ProjectChroot_ sub-resources into the result, default is False
@@ -160,7 +161,7 @@ Get project details
.. sourcecode:: http
- GET /api_2/projects/2482 HTTP/1.1
+ GET /api_2/projects/2482?show_chroots=True&show_builds=True HTTP/1.1
Host: copr.fedoraproject.org
Accept: application/json
@@ -267,7 +268,7 @@ Modify project
Updates Copr project.
- .. note:: You couldn't add or remove project chroots here, use ProjectChroots_ resource.
+ .. note:: You couldn't enabled or disable project chroots here, use ProjectChroots_ resource.
:param project_id: a unique identifier of the Copr project.
@@ -295,5 +296,4 @@ Modify project
.. sourcecode:: http
- HTTP/1.1 201 NO CONTENT
- Location: /api_2/projects/1
+ HTTP/1.1 204 NO CONTENT
diff --git a/frontend/docs/api_2/source/Resources/project_chroot.rst b/frontend/docs/api_2/source/Resources/project_chroot.rst
index 59da2f8..d63dec7 100644
--- a/frontend/docs/api_2/source/Resources/project_chroot.rst
+++ b/frontend/docs/api_2/source/Resources/project_chroot.rst
@@ -1,4 +1,241 @@
Project Chroot
==============
+Projects Chroots allows to view and modify project settings dedicated for different chroots.
+Structure if the project chroot entity
+--------------------------------------
+
+.. code-block:: javascript
+
+ {
+ "comps": null,
+ "comps_len": 0,
+ "buildroot_pkgs": [
+ "scl-build-utils",
+ "foobar"
+ ],
+ "name": "fedora-22-i386",
+ "comps_name": null
+ }
+
+Project Chroot fields
+~~~~~~~~~~~~~~~~~~~~~
+================== ==================== ========= ===============
+Field Type Can edit? Description
+================== ==================== ========= ===============
+name string no chroot name
+buildroot_pkgs list of strings yes packages to be installed into the buildroot
+comps string yes content of the `comps.xml`_
+comps_name string yes name of the uploaded comps file
+comps_len int no size of the uploaded comps file (bytes)
+================== ==================== ========= ===============
+
+List project chroots
+--------------------
+.. http:get:: /api_2/projects/(int:project_id)/chroots
+
+ Returns a list of project chroots
+
+ :param int project_id: a unique identifier of the Copr project.
+
+ :statuscode 200: no error
+ :statuscode 404: project not found
+
+ **Example request**
+
+ .. sourcecode:: http
+
+ GET /api_2/projects/2482/chroots HTTP/1.1
+ Host: copr.fedoraproject.org
+ Accept: application/json
+
+ **Response**
+
+ .. sourcecode:: http
+
+ HTTP/1.1 200 OK
+ Content-Type: application/json
+
+ {
+ "chroots": [
+ {
+ "chroot": {
+ "comps": null,
+ "comps_len": 0,
+ "buildroot_pkgs": [],
+ "name": "fedora-21-x86_64",
+ "comps_name": null
+ },
+ "_links": {
+ "project": {
+ "href": "/api_2/projects/2482"
+ },
+ "self": {
+ "href": "/api_2/projects/2482/chroots/fedora-21-x86_64"
+ }
+ }
+ },
+ { }
+ ],
+ "_links": {
+ "self": {
+ "href": "/api_2/projects/2482/chroots"
+ }
+ }
+ }
+
+Enable chroot for project
+-------------------------
+.. http:post:: /api_2/projects/(int:project_id)/chroots
+
+ **REQUIRE AUTH**
+
+ Enables chroot for the Copr project.
+ Available `chroot` names could be obtained from MockChrootResource_
+
+ :param int project_id: a unique identifier of the Copr project.
+
+ :resheader Location: contains URL to the enabled project chroot
+
+ :statuscode 201: project was successfully created
+ :statuscode 400: given data doesn't satisfy some requirements
+ :statuscode 401: this chroot is already enabled
+ :statuscode 403: authorization failed
+
+
+ **Example request**:
+
+ .. sourcecode:: http
+
+ POST HTTP/1.1
+ Host: copr.fedoraproject.org
+ Authorization: Basic base64=encoded=string
+ Accept: application/json
+
+ {
+ "buildroot_pkgs": ["foo", "bar"],
+ "name": "fedora-22-x86_64"
+ }
+
+ **Response**:
+
+ .. sourcecode:: http
+
+ HTTP/1.1 201 CREATED
+ Location: /api_2/projects/2482/chroots/fedora-22-x86_64
+
+Get project chroot details
+--------------------------
+.. http:get:: /api_2/projects/(int:project_id)/chroots/(str:chroot_name)
+
+ Returns details about Copr project
+
+ :param int project_id: a unique identifier of the Copr project.
+ :param str chroot_name: name of the project chroot
+
+ :statuscode 200: no error
+ :statuscode 404: project not found or chroot isn't enabled for the project
+
+ **Example request**
+
+ .. sourcecode:: http
+
+ GET /api_2/projects/2482/chroots/fedora-22-x86_64 HTTP/1.1
+ Host: copr.fedoraproject.org
+ Accept: application/json
+
+ **Response**
+
+ .. sourcecode:: http
+
+ HTTP/1.1 200 OK
+ Content-Type: application/json
+
+ {
+ "chroot": {
+ "comps": null,
+ "comps_len": 0,
+ "buildroot_pkgs": [
+ "foo",
+ "bar"
+ ],
+ "name": "fedora-22-x86_64",
+ "comps_name": null
+ },
+ "_links": {
+ "project": {
+ "href": "/api_2/projects/2482"
+ },
+ "self": {
+ "href": "/api_2/projects/2482/chroots/fedora-22-x86_64"
+ }
+ }
+ }
+
+Disable chroot for project
+--------------------------
+.. http:delete:: /api_2/projects/(int:project_id)/chroots/(str:chroot_name)
+
+ **REQUIRE AUTH**
+
+ Disables chroot for the Copr project
+
+ :param int project_id: a unique identifier of the Copr project.
+ :param str chroot_name: name of the project chroot
+
+ :statuscode 204: project was removed
+ :statuscode 403: authorization failed
+ :statuscode 404: project not found or chroot isn't enabled for the project
+
+ **Example request**:
+
+ .. sourcecode:: http
+
+ DELETE /api_2/projects/2482/chroots/fedora-22-x86_64 HTTP/1.1
+ Host: copr.fedoraproject.org
+ Authorization: Basic base64=encoded=string
+
+ **Response**
+
+ .. sourcecode:: http
+
+ HTTP/1.1 204 NO CONTENT
+
+Modify project chroot
+---------------------
+.. http:put:: /api_2/projects/(int:project_id)/chroots/(str:chroot_name)
+
+
+ **REQUIRE AUTH**
+
+ Updated project chroot settings
+
+ :param int project_id: a unique identifier of the Copr project.
+ :param str chroot_name: name of the project chroot
+
+ :statuscode 201: project chroot was updated
+ :statuscode 400: malformed request, see response content for details
+ :statuscode 403: authorization failed
+ :statuscode 404: project not found or chroot isn't enabled for the project
+
+ **Example request**:
+
+ .. sourcecode:: http
+
+ PUT /api_2/projects/2482/chroots/fedora-22-x86_64 HTTP/1.1
+ Host: copr.fedoraproject.org
+ Authorization: Basic base64=encoded=string
+
+ {
+ "buildroot_pkgs": []
+ }
+
+ **Response**
+
+ .. sourcecode:: http
+
+ HTTP/1.1 204 NO CONTENT
+
+
+.. _comps.xml: https://fedorahosted.org/comps/
8 years, 9 months