Discussion: Using copr for building SCLs
by Bohuslav Kabrda
Hi list,
I'd like to start yet another discussion about some functionality of copr, here it is:
One of nice use cases for copr would be building software collections [1]. These however require modified mock chroot (modified config_opts['root'] and config_opts['chroot_setup_cmd']).
I'm trying to think of a way how to do this:
- We would need to allow users to create their own chroots (or more precisely allow them to do certain modifications).
- We would need to be able to send these chroots from frontend to backend.
- We would need some mechanism for sane naming, so that we can distinguish between builds of collections for different releases and architectures. E.g. ruby193-x86_64 is not enough, we need to also store the information about os name (epel/fedora) and os version (epel 6, fedora 18, ...).
My proposal:
- Use chroot names like ruby193_fedora-18-x86_64.
- Allow users to create these chroots by modifying config_opts['chroot_setup_cmd'] (config_opts['root'] would be autogenerated from the chroot name).
- Store the modified information in the MockChroot table and create these on backend using communication channel proposed in [2] (parts of it already implemented in frontend).
- Allow selecting these on copr creation/modification as more options for mock chroots, by default hidden under some kind of fancy clickable JS dropdown.
Thoughts?
--
Regards,
Bohuslav "Slavek" Kabrda.
[1] https://fedorahosted.org/SoftwareCollections/
[2] https://fedorahosted.org/copr/ticket/8
10 years, 8 months
Repo file generation + tests
by Tomas Radej
Hi, after a lengthy fight and several reconsideration, repo generation
is here.
NOTE: If there is a copr at frontend, but no finished build in backend
(which actually contains the baseurl - it's nowhere else in the system),
404 with an according error message is returned.
TR
>From c9f3e24d288ed89297f6a255873e0a92429101e0 Mon Sep 17 00:00:00 2001
From: Tomas Radej <tradej(a)redhat.com>
Date: Thu, 11 Apr 2013 12:50:15 +0200
Subject: [PATCH] Repo file generation
---
.../coprs/templates/coprs/coprchroot.repo | 7 ++++++
.../coprs/views/coprs_ns/coprs_general.py | 21 ++++++++++++++++
.../test_views/test_coprs_ns/test_coprs_general.py | 29 ++++++++++++++++++++++
3 files changed, 57 insertions(+)
create mode 100644 coprs_frontend/coprs/templates/coprs/coprchroot.repo
diff --git a/coprs_frontend/coprs/templates/coprs/coprchroot.repo b/coprs_frontend/coprs/templates/coprs/coprchroot.repo
new file mode 100644
index 0000000..98d79d3
--- /dev/null
+++ b/coprs_frontend/coprs/templates/coprs/coprchroot.repo
@@ -0,0 +1,7 @@
+[{{ copr.owner.name }}-{{ copr.name }}]
+
+name=Copr repo for {{ copr.name }} owned by {{ copr.owner.name }}
+description={{ copr.description }}
+baseurl={{ url }}
+skip_if_unavailable=True
+
diff --git a/coprs_frontend/coprs/views/coprs_ns/coprs_general.py b/coprs_frontend/coprs/views/coprs_ns/coprs_general.py
index 9d5be98..ee4145d 100644
--- a/coprs_frontend/coprs/views/coprs_ns/coprs_general.py
+++ b/coprs_frontend/coprs/views/coprs_ns/coprs_general.py
@@ -16,6 +16,8 @@ from coprs.views.coprs_ns import coprs_ns
from coprs.logic import builds_logic
from coprs.logic import coprs_logic
+from sqlalchemy.orm.exc import NoResultFound
+
@coprs_ns.route('/', defaults = {'page': 1})
@coprs_ns.route('/<int:page>/')
def coprs_show(page=1):
@@ -272,3 +274,22 @@ def copr_legal_flag(username, coprname):
db.session.commit()
flask.flash('Admin was noticed about your report and will investigate the copr shortly.')
return flask.redirect(flask.url_for('coprs_ns.copr_detail', username=username, coprname=coprname))
+
+(a)coprs_ns.route('/detail/<username>/<coprname>/<chrootname>.repo', methods = ['GET'])
+def generate_repo_file(username, coprname, chrootname):
+ ''' Generate repo file for a given copr/mock-chroot '''
+ try:
+ # We're taking the baseurl from any finished build in the given copr.
+ # It's ugly, but so far we didn't come up with anything else.
+ copr = coprs_logic.CoprsLogic.get(flask.g.user, username, coprname,
+ with_mock_chroots=True, with_builds=True).filter(models.Build.results != None).one()
+ except NoResultFound:
+ if not coprs_logic.CoprsLogic.get(flask.g.user, username, coprname).first():
+ return page_not_found('Copr: {0}/{1} does not exist'.format(username, coprname))
+ else:
+ return page_not_found('Repository not initialized: No builds in {0}/{1} are finished.'.format(username, coprname))
+
+ response = flask.make_response(flask.render_template('coprs/coprchroot.repo', copr=copr, url=copr.builds[0].results))
+ response.mimetype='text/plain'
+ return response
+
diff --git a/coprs_frontend/tests/test_views/test_coprs_ns/test_coprs_general.py b/coprs_frontend/tests/test_views/test_coprs_ns/test_coprs_general.py
index 380d59d..ad4a77c 100644
--- a/coprs_frontend/tests/test_views/test_coprs_ns/test_coprs_general.py
+++ b/coprs_frontend/tests/test_views/test_coprs_ns/test_coprs_general.py
@@ -1,8 +1,10 @@
import flask
+import pytest
from flexmock import flexmock
from coprs.signals import copr_created
+from coprs import models
from tests.coprs_test_case import CoprsTestCase
@@ -456,3 +458,30 @@ class TestCoprDelete(CoprsTestCase):
assert 'Copr was deleted successfully' not in r.data
assert not self.models.Action.query.first()
assert self.models.Copr.query.filter(self.models.Copr.id==self.c1.id).first()
+
+class TestCoprRepoGeneration(CoprsTestCase):
+ @pytest.fixture
+ def f_custom_builds(self):
+ ''' Custom builds are used in order not to break the default ones '''
+ self.b5 = models.Build(copr=self.c1, user=self.u1, chroots='fedora-18-x86_64', submitted_on=9, ended_on=200, results='foo://bar.baz')
+ self.b6 = models.Build(copr=self.c1, user=self.u1, chroots='fedora-18-x86_64', submitted_on=11)
+ self.b7 = models.Build(copr=self.c1, user=self.u1, chroots='fedora-18-x86_64', submitted_on=10, ended_on=150, results='foo://bar.baz')
+
+ self.db.session.add_all([self.b5, self.b6, self.b7])
+
+ def test_fail_on_no_copr(self):
+ r = self.tc.get('/coprs/detail/bogus/copr/name.repo')
+ assert r.status_code == 404
+ assert 'bogus/copr does not exist' in r.data
+
+ def test_fail_on_no_finished_builds(self, f_users, f_coprs, f_mock_chroots, f_db):
+ r = self.tc.get(
+ '/coprs/detail/{0}/{1}/{2}.repo'.format(self.u1.name, self.c1.name,
+ self.c1.mock_chroots[0].chroot_name))
+ assert r.status_code == 404
+ assert 'Repository not initialized' in r.data
+
+ def test_older_and_newer_builds_give_same_url(self, f_users, f_coprs, f_mock_chroots, f_custom_builds, f_db):
+ r = self.tc.get('/coprs/detail/{0}/{1}/{2}.repo'.format(self.u1.name, self.c1.name, self.c1.mock_chroots[0].chroot_name))
+ assert r.status_code == 200
+ assert 'baseurl=foo://bar.baz' in r.data
--
1.8.1.4
--
Tomas Radej <tradej(a)redhat.com>
10 years, 11 months
coprs deadline
by Seth Vidal
So - I have been struggling to make enough time to work on coprs which
is irritating and frustrating.
So -I have an idea - why don't we set a drop-dead for expanding the
coprs beta? Maybe 2 weeks? a month from now? At which point we open it
up to all people in the fedora packagers group and announce it for
open-beta?
-sv
10 years, 11 months
[copr] master: Fix LegalFlag<->User relations for SQLAlchemy < 0.8.0 (a912c14)
by bkabrda@fedoraproject.org
Repository : http://git.fedorahosted.org/cgit/copr.git
On branch : master
>---------------------------------------------------------------
commit a912c142b1d916f82dea5ac999133f62bae76fb4
Author: Bohuslav Kabrda <bkabrda(a)redhat.com>
Date: Thu Apr 11 15:26:56 2013 +0200
Fix LegalFlag<->User relations for SQLAlchemy < 0.8.0
>---------------------------------------------------------------
coprs_frontend/coprs/models.py | 6 ++++--
1 files changed, 4 insertions(+), 2 deletions(-)
diff --git a/coprs_frontend/coprs/models.py b/coprs_frontend/coprs/models.py
index f31de29..b1c684e 100644
--- a/coprs_frontend/coprs/models.py
+++ b/coprs_frontend/coprs/models.py
@@ -288,12 +288,14 @@ class LegalFlag(db.Model, Serializer):
reporter_id = db.Column(db.Integer, db.ForeignKey('user.id'))
reporter = db.relationship('User',
backref=db.backref('legal_flags_raised'),
- foreign_keys=[reporter_id])
+ foreign_keys=[reporter_id],
+ primaryjoin='LegalFlag.reporter_id==User.id')
# admin who resolved the problem
resolver_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=True)
resolver = db.relationship('User',
backref=db.backref('legal_flags_resolved'),
- foreign_keys=[resolver_id])
+ foreign_keys=[resolver_id],
+ primaryjoin='LegalFlag.resolver_id==User.id')
class Action(db.Model, Serializer):
10 years, 11 months
[copr] master: Don't delete coprs, rather mark them as deleted and don't show them (c539b68)
by bkabrda@fedoraproject.org
Repository : http://git.fedorahosted.org/cgit/copr.git
On branch : master
>---------------------------------------------------------------
commit c539b68d736fcbcc3a2186ee809adbf9c9ab6004
Author: Bohuslav Kabrda <bkabrda(a)redhat.com>
Date: Wed Apr 3 11:55:30 2013 +0200
Don't delete coprs, rather mark them as deleted and don't show them
>---------------------------------------------------------------
coprs_frontend/coprs/logic/coprs_logic.py | 23 ++++++++++++++-----
coprs_frontend/coprs/models.py | 5 +++-
.../coprs/templates/admin/legal-flag.html | 21 +++++++++++++-----
.../test_views/test_coprs_ns/test_coprs_general.py | 22 ++++++++++++++++++-
4 files changed, 57 insertions(+), 14 deletions(-)
diff --git a/coprs_frontend/coprs/logic/coprs_logic.py b/coprs_frontend/coprs/logic/coprs_logic.py
index 0066b1d..c304fdd 100644
--- a/coprs_frontend/coprs/logic/coprs_logic.py
+++ b/coprs_frontend/coprs/logic/coprs_logic.py
@@ -20,7 +20,8 @@ class CoprsLogic(object):
join(models.Copr.owner).\
options(db.contains_eager(models.Copr.owner)).\
filter(models.Copr.name == coprname).\
- filter(models.User.openid_name == models.User.openidize_name(username))
+ filter(models.User.openid_name == models.User.openidize_name(username)).\
+ filter(models.Copr.deleted==False)
if with_builds:
query = query.outerjoin(models.Copr.builds).\
@@ -40,6 +41,7 @@ class CoprsLogic(object):
user_relation = kwargs.get('user_relation', None)
username = kwargs.get('username', None)
with_mock_chroots = kwargs.get('with_mock_chroots', None)
+ incl_deleted = kwargs.get('incl_deleted', None)
ids = kwargs.get('ids', None)
query = db.session.query(models.Copr).\
@@ -47,6 +49,9 @@ class CoprsLogic(object):
options(db.contains_eager(models.Copr.owner)).\
order_by(models.Copr.id.desc())
+ if not incl_deleted:
+ query = query.filter(models.Copr.deleted==False)
+
if isinstance(ids, list): # can be an empty list
query = query.filter(models.Copr.id.in_(ids))
@@ -69,7 +74,9 @@ class CoprsLogic(object):
@classmethod
def get_multiple_fulltext(cls, user, search_string):
- query = models.Copr.query.join(models.User).whooshee_search(search_string)
+ query = models.Copr.query.join(models.User).\
+ filter(models.Copr.deleted==False).\
+ whooshee_search(search_string)
return query
@classmethod
@@ -88,7 +95,7 @@ class CoprsLogic(object):
return copr
@classmethod
- def new(cls, user, copr, check_for_duplicates = True):
+ def new(cls, user, copr, check_for_duplicates=True):
if check_for_duplicates and cls.exists_for_user(user, copr.name).all():
raise exceptions.DuplicateException(
'Copr: "{0}" already exists'.format(copr.name))
@@ -109,7 +116,9 @@ class CoprsLogic(object):
else: # we're renaming
# if we fire a models.Copr.query, it will use the modified copr in session
# -> workaround this by just getting the name
- old_copr_name = db.session.query(models.Copr.name).filter(models.Copr.id==copr.id).first()[0]
+ old_copr_name = db.session.query(models.Copr.name).filter(models.Copr.id==copr.id).\
+ filter(models.Copr.deleted==False).\
+ first()[0]
action = models.Action(action_type=helpers.ActionTypeEnum('rename'),
object_type='copr',
object_id=copr.id,
@@ -131,16 +140,18 @@ class CoprsLogic(object):
old_value='{0}/{1}'.format(copr.owner.name, copr.name),
new_value='',
created_on=int(time.time()))
+ copr.deleted = True
db.session.add(action)
- db.session.delete(copr)
return copr
@classmethod
- def exists_for_user(cls, user, coprname):
+ def exists_for_user(cls, user, coprname, incl_deleted=False):
existing = models.Copr.query.filter(models.Copr.name==coprname).\
filter(models.Copr.owner_id==user.id)
+ if not incl_deleted:
+ existing = existing.filter(models.Copr.deleted==False)
return existing
diff --git a/coprs_frontend/coprs/models.py b/coprs_frontend/coprs/models.py
index d6d5aee..f31de29 100644
--- a/coprs_frontend/coprs/models.py
+++ b/coprs_frontend/coprs/models.py
@@ -122,7 +122,9 @@ class User(db.Model, Serializer):
@property
def coprs_count(self):
"""Get number of coprs for this user."""
- return Copr.query.filter_by(owner=self).count()
+ return Copr.query.filter_by(owner=self).\
+ filter_by(deleted=False).\
+ count()
class Copr(db.Model, Serializer):
@@ -140,6 +142,7 @@ class Copr(db.Model, Serializer):
instructions = db.Column(db.Text)
# duplicate information, but speeds up a lot and makes queries simpler
build_count = db.Column(db.Integer, default = 0)
+ deleted = db.Column(db.Boolean, default=False)
# relations
owner_id = db.Column(db.Integer, db.ForeignKey('user.id'))
diff --git a/coprs_frontend/coprs/templates/admin/legal-flag.html b/coprs_frontend/coprs/templates/admin/legal-flag.html
index 6dc0e2a..75f61c1 100644
--- a/coprs_frontend/coprs/templates/admin/legal-flag.html
+++ b/coprs_frontend/coprs/templates/admin/legal-flag.html
@@ -5,18 +5,27 @@
{% block admin_body %}
{% for flag in legal_flags %}
<div class="legal-flag">
- <div>
+ <div>
+ {% if not flag.copr.deleted %}
<a href={{ url_for('coprs_ns.copr_detail', username=flag.copr.owner.name, coprname=flag.copr.name) }}>
- {{ flag.copr.owner.name }}/{{ flag.copr.name }}
- </a>
- | <a href="">Freeze</a>
- | <a href="{{ url_for('coprs_ns.copr_delete', username=flag.copr.owner.name, coprname=flag.copr.name) }}">Delete</a>
+ {% endif %}
+ {{ flag.copr.owner.name }}/{{ flag.copr.name }}
+ {% if not flag.copr.deleted %}
+ </a>
+ {% endif %}
+ |
+ {% if flag.copr.deleted %}
+ Already deleted
+ {% else %}
+ <a href="">Freeze</a>
+ | <a href="{{ url_for('coprs_ns.copr_delete', username=flag.copr.owner.name, coprname=flag.copr.name) }}">Delete</a>
+ {% endif %}
<form method="post" action="{{ url_for('admin_ns.legal_flag_resolve', flag_id=flag.id) }}">
<input type="submit" value="Resolve">
</form>
</div>
<div class="message">
- {{ flag.message }}
+ {{ flag.raise_message }}
</div>
</div>
{% else %}
diff --git a/coprs_frontend/tests/test_views/test_coprs_ns/test_coprs_general.py b/coprs_frontend/tests/test_views/test_coprs_ns/test_coprs_general.py
index 6fa8e19..380d59d 100644
--- a/coprs_frontend/tests/test_views/test_coprs_ns/test_coprs_general.py
+++ b/coprs_frontend/tests/test_views/test_coprs_ns/test_coprs_general.py
@@ -132,6 +132,25 @@ class TestCoprNew(CoprsTestCase):
assert copr.build_count == 1
assert 'Initial packages were successfully submitted' in r.data
+ def test_copr_new_is_allowed_even_if_deleted_has_same_name(self, f_users, f_coprs, f_mock_chroots, f_db):
+ with self.tc as c:
+ with c.session_transaction() as s:
+ s['openid'] = self.u1.openid_name
+
+ self.db.session.add(self.c1)
+ self.c1.deleted = True
+ self.c1.owner = self.u1
+ self.db.session.commit()
+
+ self.db.session.add(self.c1)
+ r = c.post('/coprs/new/', data = {'name': self.c1.name, 'fedora-rawhide-i386': 'y', 'arches': ['i386']}, follow_redirects = True)
+ self.db.session.add_all([self.c1, self.u1])
+ assert len(self.models.Copr.query.filter(self.models.Copr.name==self.c1.name).\
+ filter(self.models.Copr.owner==self.u1).\
+ all()) == 2
+ assert self.success_string in r.data
+
+
class TestCoprDetail(CoprsTestCase):
def test_copr_detail_not_found(self):
r = self.tc.get('/coprs/detail/foo/bar/')
@@ -407,8 +426,9 @@ class TestCoprDelete(CoprsTestCase):
data = {'verify': 'yes'},
follow_redirects = True)
assert 'Copr was deleted successfully' in r.data
+ self.db.session.add(self.c1)
assert self.models.Action.query.first().id == self.c1.id
- assert not self.models.Copr.query.filter(self.models.Copr.id==self.c1.id).first()
+ assert self.models.Copr.query.filter(self.models.Copr.id==self.c1.id).first().deleted
def test_copr_delete_does_not_delete_if_verify_filled_wrongly(self, f_users, f_coprs, f_db):
with self.tc as c:
10 years, 11 months
[copr] master: Make legal flag a separate db entity (147f3c8)
by bkabrda@fedoraproject.org
Repository : http://git.fedorahosted.org/cgit/copr.git
On branch : master
>---------------------------------------------------------------
commit 147f3c85efd237b611531c2a1da780f5a9d15e80
Author: Bohuslav Kabrda <bkabrda(a)redhat.com>
Date: Wed Apr 3 10:47:02 2013 +0200
Make legal flag a separate db entity
- and rename CoprLegalForm to CoprLegalFlagForm to keep things consistent
>---------------------------------------------------------------
.../versions/246fd2dbf398_add_legal_flag.py | 32 ++++++++++++++++++++
coprs_frontend/coprs/forms.py | 2 +-
coprs_frontend/coprs/models.py | 25 +++++++++++++++
.../coprs/templates/admin/legal-flag.html | 16 ++++------
.../coprs/templates/coprs/_coprs_forms.html | 2 +-
.../coprs/templates/coprs/detail/overview.html | 4 +-
.../coprs/views/admin_ns/admin_general.py | 28 ++++++-----------
.../coprs/views/coprs_ns/coprs_general.py | 17 ++++------
8 files changed, 84 insertions(+), 42 deletions(-)
diff --git a/coprs_frontend/alembic/versions/246fd2dbf398_add_legal_flag.py b/coprs_frontend/alembic/versions/246fd2dbf398_add_legal_flag.py
new file mode 100644
index 0000000..bea0d38
--- /dev/null
+++ b/coprs_frontend/alembic/versions/246fd2dbf398_add_legal_flag.py
@@ -0,0 +1,32 @@
+"""add_legal_flag
+
+Revision ID: 246fd2dbf398
+Revises: d062c3d9c00
+Create Date: 2013-04-03 10:39:54.837803
+
+"""
+
+# revision identifiers, used by Alembic.
+revision = '246fd2dbf398'
+down_revision = 'd062c3d9c00'
+
+from alembic import op
+import sqlalchemy as sa
+
+
+def upgrade():
+ ### commands auto generated by Alembic - please adjust! ###
+ op.add_column('legal_flag', sa.Column('resolved_on', sa.Integer(), nullable=True))
+ op.add_column('legal_flag', sa.Column('raised_on', sa.Integer(), nullable=True))
+ op.drop_column('legal_flag', u'state')
+ op.drop_column('legal_flag', u'resolve_message')
+ ### end Alembic commands ###
+
+
+def downgrade():
+ ### commands auto generated by Alembic - please adjust! ###
+ op.add_column('legal_flag', sa.Column(u'resolve_message', sa.TEXT(), nullable=True))
+ op.add_column('legal_flag', sa.Column(u'state', sa.INTEGER(), nullable=True))
+ op.drop_column('legal_flag', 'raised_on')
+ op.drop_column('legal_flag', 'resolved_on')
+ ### end Alembic commands ###
diff --git a/coprs_frontend/coprs/forms.py b/coprs_frontend/coprs/forms.py
index 6b533cd..02272d8 100644
--- a/coprs_frontend/coprs/forms.py
+++ b/coprs_frontend/coprs/forms.py
@@ -138,7 +138,7 @@ class BuildForm(wtf.Form):
validators = [wtf.NumberRange(min = constants.MIN_BUILD_TIMEOUT, max = constants.MAX_BUILD_TIMEOUT)],
default = constants.DEFAULT_BUILD_TIMEOUT)
-class CoprLegalForm(wtf.Form):
+class CoprLegalFlagForm(wtf.Form):
comment = wtf.TextAreaField('Comment')
class PermissionsApplierFormFactory(object):
diff --git a/coprs_frontend/coprs/models.py b/coprs_frontend/coprs/models.py
index b905831..d6d5aee 100644
--- a/coprs_frontend/coprs/models.py
+++ b/coprs_frontend/coprs/models.py
@@ -268,6 +268,31 @@ class CoprChroot(db.Model, Serializer):
single_parent=True,
cascade='all,delete,delete-orphan'))
+class LegalFlag(db.Model, Serializer):
+ id = db.Column(db.Integer, primary_key=True)
+ # message from user who raised the flag (what he thinks is wrong)
+ raise_message = db.Column(db.Text)
+ # time of raising the flag as returned by int(time.time())
+ raised_on = db.Column(db.Integer)
+ # time of resolving the flag by admin as returned by int(time.time())
+ resolved_on = db.Column(db.Integer)
+
+ # relations
+ copr_id = db.Column(db.Integer, db.ForeignKey('copr.id'), nullable=True)
+ # cascade='all' means that we want to keep these even if copr is deleted
+ copr = db.relationship('Copr', backref=db.backref('legal_flags', cascade='all'))
+ # user who reported the problem
+ reporter_id = db.Column(db.Integer, db.ForeignKey('user.id'))
+ reporter = db.relationship('User',
+ backref=db.backref('legal_flags_raised'),
+ foreign_keys=[reporter_id])
+ # admin who resolved the problem
+ resolver_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=True)
+ resolver = db.relationship('User',
+ backref=db.backref('legal_flags_resolved'),
+ foreign_keys=[resolver_id])
+
+
class Action(db.Model, Serializer):
"""Representation of a custom action that needs backends cooperation/admin attention/..."""
id = db.Column(db.Integer, primary_key=True)
diff --git a/coprs_frontend/coprs/templates/admin/legal-flag.html b/coprs_frontend/coprs/templates/admin/legal-flag.html
index 4cce6e5..6dc0e2a 100644
--- a/coprs_frontend/coprs/templates/admin/legal-flag.html
+++ b/coprs_frontend/coprs/templates/admin/legal-flag.html
@@ -6,16 +6,12 @@
{% for flag in legal_flags %}
<div class="legal-flag">
<div>
- {% if flag.copr %}
- <a href={{ url_for('coprs_ns.copr_detail', username=flag.copr.owner.name, coprname=flag.copr.name) }}>
- {{ flag.copr.owner.name }}/{{ flag.copr.name }}
- </a>
- | <a href="">Freeze</a>
- | <a href="{{ url_for('coprs_ns.copr_delete', username=flag.copr.owner.name, coprname=flag.copr.name) }}">Delete</a>
- {% else %}
- {{ flag.old_value }} - copr was deleted
- {% endif %}
- <form method="post" action="{{ url_for('admin_ns.legal_flag_resolve', action_id=flag.id) }}">
+ <a href={{ url_for('coprs_ns.copr_detail', username=flag.copr.owner.name, coprname=flag.copr.name) }}>
+ {{ flag.copr.owner.name }}/{{ flag.copr.name }}
+ </a>
+ | <a href="">Freeze</a>
+ | <a href="{{ url_for('coprs_ns.copr_delete', username=flag.copr.owner.name, coprname=flag.copr.name) }}">Delete</a>
+ <form method="post" action="{{ url_for('admin_ns.legal_flag_resolve', flag_id=flag.id) }}">
<input type="submit" value="Resolve">
</form>
</div>
diff --git a/coprs_frontend/coprs/templates/coprs/_coprs_forms.html b/coprs_frontend/coprs/templates/coprs/_coprs_forms.html
index e8856e1..dd13b40 100644
--- a/coprs_frontend/coprs/templates/coprs/_coprs_forms.html
+++ b/coprs_frontend/coprs/templates/coprs/_coprs_forms.html
@@ -77,7 +77,7 @@
{% endif %}
{% endmacro %}
-{% macro copr_legal_form(form, copr) %}
+{% macro copr_legal_flag_form(form, copr) %}
<form class="legal-flag" method="post" action={{ url_for('coprs_ns.copr_legal_flag', username=copr.owner.name, coprname=copr.name) }}>
If you think that this copr may be violating our policies, you can mark it for legal review.
You can add an optional comment describing what you think is wrong here.
diff --git a/coprs_frontend/coprs/templates/coprs/detail/overview.html b/coprs_frontend/coprs/templates/coprs/detail/overview.html
index b0bf4e2..a038913 100644
--- a/coprs_frontend/coprs/templates/coprs/detail/overview.html
+++ b/coprs_frontend/coprs/templates/coprs/detail/overview.html
@@ -1,6 +1,6 @@
{% extends "coprs/detail.html" %}
-{% from "coprs/_coprs_forms.html" import copr_legal_form with context %}
+{% from "coprs/_coprs_forms.html" import copr_legal_flag_form with context %}
{% block overview_selected %}selected{% endblock %}
@@ -51,5 +51,5 @@
{% endif %}
<hr>
- {{ copr_legal_form(form, copr) }}
+ {{ copr_legal_flag_form(form, copr) }}
{% endblock %}
diff --git a/coprs_frontend/coprs/views/admin_ns/admin_general.py b/coprs_frontend/coprs/views/admin_ns/admin_general.py
index d094b36..b05058b 100644
--- a/coprs_frontend/coprs/views/admin_ns/admin_general.py
+++ b/coprs_frontend/coprs/views/admin_ns/admin_general.py
@@ -19,29 +19,21 @@ def admin_index():
@admin_ns.route('/legal-flag/')
@login_required(role=helpers.RoleEnum('admin'))
def legal_flag():
- legal_flags = models.Action.query.filter(models.Action.action_type==helpers.ActionTypeEnum('legal-flag')).\
- filter(models.Action.object_type=='copr').\
- filter(models.Action.ended_on==None).\
- order_by(models.Action.created_on.desc()).\
- all()
-
- ids = map(lambda x: x.object_id, legal_flags)
- # if there are no ids, we would trigger "IN" query with empty set, which
- # is not very good (and sqlalchemy complains about it)
- coprs = coprs_logic.CoprsLogic.get_multiple(flask.g.user, ids=ids).all() if ids else []
- for flag in legal_flags:
- # handle the situation where copr was deleted in the meanwhile
- copr = filter(lambda x: flag.object_id == x.id, coprs)
- flag.copr = copr[0] if copr else None
+ legal_flags = models.LegalFlag.query.outerjoin(models.LegalFlag.copr).\
+ options(db.contains_eager(models.LegalFlag.copr)).\
+ filter(models.LegalFlag.resolved_on==None).\
+ order_by(models.LegalFlag.raised_on.desc()).\
+ all()
return flask.render_template('admin/legal-flag.html',
legal_flags=legal_flags)
-(a)admin_ns.route('/legal-flag/<int:action_id>/resolve/', methods=['POST'])
+(a)admin_ns.route('/legal-flag/<int:flag_id>/resolve/', methods=['POST'])
@login_required(role=helpers.RoleEnum('admin'))
-def legal_flag_resolve(action_id):
- action = models.Action.query.filter(models.Action.id==action_id).\
- update({'ended_on': int(time.time())})
+def legal_flag_resolve(flag_id):
+ legal_flag = models.LegalFlag.query.filter(models.LegalFlag.id==flag_id).\
+ update({'resolved_on': int(time.time()),
+ 'resolver_id': flask.g.user.id})
db.session.commit()
flask.flash('Legal flag resolved')
return flask.redirect(flask.url_for('admin_ns.legal_flag'))
diff --git a/coprs_frontend/coprs/views/coprs_ns/coprs_general.py b/coprs_frontend/coprs/views/coprs_ns/coprs_general.py
index 4203af5..9d5be98 100644
--- a/coprs_frontend/coprs/views/coprs_ns/coprs_general.py
+++ b/coprs_frontend/coprs/views/coprs_ns/coprs_general.py
@@ -104,7 +104,7 @@ def copr_new():
@coprs_ns.route('/detail/<username>/<coprname>/')
def copr_detail(username, coprname):
query = coprs_logic.CoprsLogic.get(flask.g.user, username, coprname, with_mock_chroots=True)
- form = forms.CoprLegalForm()
+ form = forms.CoprLegalFlagForm()
try:
copr = query.one()
except sqlalchemy.orm.exc.NoResultFound:
@@ -261,17 +261,14 @@ def copr_delete(username, coprname):
@coprs_ns.route('/detail/<username>/<coprname>/legal_flag/', methods=['POST'])
@login_required
def copr_legal_flag(username, coprname):
- form = forms.CoprLegalForm()
+ form = forms.CoprLegalFlagForm()
copr = coprs_logic.CoprsLogic.get(flask.g.user, username, coprname).first()
- action = models.Action(action_type=helpers.ActionTypeEnum('legal-flag'),
- object_type='copr',
- object_id=copr.id,
- message=form.comment.data,
- old_value='{0}/{1}'.format(username, coprname),
- new_value='{0}/{1}'.format(username, coprname),
- created_on=int(time.time()))
- db.session.add(action)
+ legal_flag = models.LegalFlag(raise_message=form.comment.data,
+ raised_on=int(time.time()),
+ copr=copr,
+ reporter=flask.g.user)
+ db.session.add(legal_flag)
db.session.commit()
flask.flash('Admin was noticed about your report and will investigate the copr shortly.')
return flask.redirect(flask.url_for('coprs_ns.copr_detail', username=username, coprname=coprname))
10 years, 11 months
[copr] master: Finish generalization of Action by renaming backend_result to result (f553221)
by bkabrda@fedoraproject.org
Repository : http://git.fedorahosted.org/cgit/copr.git
On branch : master
>---------------------------------------------------------------
commit f5532213a344b0b2ddaa424d6965f015c5a5d554
Author: Bohuslav Kabrda <bkabrda(a)redhat.com>
Date: Wed Apr 3 10:13:24 2013 +0200
Finish generalization of Action by renaming backend_result to result
>---------------------------------------------------------------
.../d062c3d9c00_backend_result_to_result.py | 28 ++++++++++++++++++++
coprs_frontend/coprs/logic/coprs_logic.py | 2 +-
coprs_frontend/coprs/models.py | 4 +-
.../coprs/views/backend_ns/backend_general.py | 6 ++--
coprs_frontend/tests/coprs_test_case.py | 2 +-
.../test_backend_ns/test_backend_general.py | 14 +++++-----
6 files changed, 42 insertions(+), 14 deletions(-)
diff --git a/coprs_frontend/alembic/versions/d062c3d9c00_backend_result_to_result.py b/coprs_frontend/alembic/versions/d062c3d9c00_backend_result_to_result.py
new file mode 100644
index 0000000..1a5bd3a
--- /dev/null
+++ b/coprs_frontend/alembic/versions/d062c3d9c00_backend_result_to_result.py
@@ -0,0 +1,28 @@
+"""backend_result_to_result
+
+Revision ID: d062c3d9c00
+Revises: 451e9507b866
+Create Date: 2013-04-03 10:10:35.990681
+
+"""
+
+# revision identifiers, used by Alembic.
+revision = 'd062c3d9c00'
+down_revision = '451e9507b866'
+
+from alembic import op
+import sqlalchemy as sa
+
+
+def upgrade():
+ ### commands auto generated by Alembic - please adjust! ###
+ op.add_column('action', sa.Column('result', sa.Integer(), nullable=True))
+ op.drop_column('action', u'backend_result')
+ ### end Alembic commands ###
+
+
+def downgrade():
+ ### commands auto generated by Alembic - please adjust! ###
+ op.add_column('action', sa.Column(u'backend_result', sa.INTEGER(), nullable=True))
+ op.drop_column('action', 'result')
+ ### end Alembic commands ###
diff --git a/coprs_frontend/coprs/logic/coprs_logic.py b/coprs_frontend/coprs/logic/coprs_logic.py
index 2c15df0..0066b1d 100644
--- a/coprs_frontend/coprs/logic/coprs_logic.py
+++ b/coprs_frontend/coprs/logic/coprs_logic.py
@@ -155,7 +155,7 @@ class CoprsLogic(object):
helpers.ActionTypeEnum('delete')]
actions = models.Action.query.filter(models.Action.object_type=='copr').\
filter(models.Action.object_id==copr.id).\
- filter(models.Action.backend_result==helpers.BackendResultEnum('waiting')).\
+ filter(models.Action.result==helpers.BackendResultEnum('waiting')).\
filter(models.Action.action_type.in_(blocking_actions))
return actions
diff --git a/coprs_frontend/coprs/models.py b/coprs_frontend/coprs/models.py
index 3beac64..b905831 100644
--- a/coprs_frontend/coprs/models.py
+++ b/coprs_frontend/coprs/models.py
@@ -280,8 +280,8 @@ class Action(db.Model, Serializer):
# old and new values of the changed property
old_value = db.Column(db.String(255))
new_value = db.Column(db.String(255))
- # backend result, see helpers.BackendResultEnum
- backend_result = db.Column(db.Integer, default=helpers.BackendResultEnum('waiting'))
+ # result of the action, see helpers.BackendResultEnum
+ result = db.Column(db.Integer, default=helpers.BackendResultEnum('waiting'))
# optional message from the backend/whatever
message = db.Column(db.Text)
# time created as returned by int(time.time())
diff --git a/coprs_frontend/coprs/views/backend_ns/backend_general.py b/coprs_frontend/coprs/views/backend_ns/backend_general.py
index 1f9c4f1..dfdc97d 100644
--- a/coprs_frontend/coprs/views/backend_ns/backend_general.py
+++ b/coprs_frontend/coprs/views/backend_ns/backend_general.py
@@ -44,9 +44,9 @@ def update_builds():
@backend_ns.route('/waiting_actions/')
def waiting_actions():
- actions = models.Action.query.filter(models.Action.backend_result==helpers.BackendResultEnum('waiting')).all()
+ actions = models.Action.query.filter(models.Action.result==helpers.BackendResultEnum('waiting')).all()
- return flask.jsonify({'actions': [action.to_dict(options={'__columns_except__': ['backend_result', 'message', 'ended_on']}) for action in actions]})
+ return flask.jsonify({'actions': [action.to_dict(options={'__columns_except__': ['result', 'message', 'ended_on']}) for action in actions]})
# TODO: this is very similar to update_builds, we should pull out the common functionality into a single function
@backend_ns.route('/update_actions/', methods=['POST', 'PUT'])
@@ -66,7 +66,7 @@ def update_actions():
non_existing_ids = list(set(to_update.keys()) - set(existing.keys()))
for i, action in existing.items():
- existing[i].backend_result = to_update[i]['backend_result']
+ existing[i].result = to_update[i]['result']
existing[i].message = to_update[i]['message']
existing[i].ended_on = to_update[i]['ended_on']
diff --git a/coprs_frontend/tests/coprs_test_case.py b/coprs_frontend/tests/coprs_test_case.py
index bea1f58..6659bbf 100644
--- a/coprs_frontend/tests/coprs_test_case.py
+++ b/coprs_frontend/tests/coprs_test_case.py
@@ -119,6 +119,6 @@ class CoprsTestCase(object):
object_id=100,
old_value='asd/qwe',
new_value=None,
- backend_result=helpers.BackendResultEnum('success'),
+ result=helpers.BackendResultEnum('success'),
created_on=int(time.time()))
self.db.session.add_all([self.a1, self.a2, self.a3])
diff --git a/coprs_frontend/tests/test_views/test_backend_ns/test_backend_general.py b/coprs_frontend/tests/test_views/test_backend_ns/test_backend_general.py
index 2d3270c..235ed98 100644
--- a/coprs_frontend/tests/test_views/test_backend_ns/test_backend_general.py
+++ b/coprs_frontend/tests/test_views/test_backend_ns/test_backend_general.py
@@ -142,7 +142,7 @@ class TestUpdateActions(CoprsTestCase):
"actions":[
{
"id": 1,
- "backend_result": 1,
+ "result": 1,
"message": "no problem",
"ended_on": 123
}
@@ -153,19 +153,19 @@ class TestUpdateActions(CoprsTestCase):
"actions":[
{
"id": 1,
- "backend_result": 1,
+ "result": 1,
"message": null,
"ended_on": 1234
},
{
"id": 2,
- "backend_result": 2,
+ "result": 2,
"message": "problem!",
"ended_on": 12345
},
{
"id": 100,
- "backend_result": 123,
+ "result": 123,
"message": "wheeeee!",
"ended_on": 123456
}
@@ -181,7 +181,7 @@ class TestUpdateActions(CoprsTestCase):
assert json.loads(r.data)["non_existing_actions_ids"] == []
updated = self.models.Action.query.filter(self.models.Action.id==1).first()
- assert updated.backend_result == 1
+ assert updated.result == 1
assert updated.message == "no problem"
assert updated.ended_on == 123
@@ -194,11 +194,11 @@ class TestUpdateActions(CoprsTestCase):
assert json.loads(r.data)["non_existing_actions_ids"] == [100]
updated = self.models.Action.query.filter(self.models.Action.id==1).first()
- assert updated.backend_result == 1
+ assert updated.result == 1
assert updated.message == None
assert updated.ended_on == 1234
updated2 = self.models.Action.query.filter(self.models.Action.id==2).first()
- assert updated2.backend_result == 2
+ assert updated2.result == 2
assert updated2.message == "problem!"
assert updated2.ended_on == 12345
10 years, 11 months
[copr] master: Enable marking legal flags as resolved (761921f)
by bkabrda@fedoraproject.org
Repository : http://git.fedorahosted.org/cgit/copr.git
On branch : master
>---------------------------------------------------------------
commit 761921f2f026c9991da659bc325e0892771089d1
Author: Bohuslav Kabrda <bkabrda(a)redhat.com>
Date: Tue Apr 2 15:23:45 2013 +0200
Enable marking legal flags as resolved
>---------------------------------------------------------------
coprs_frontend/coprs/static/copr.css | 6 ++++++
.../coprs/templates/admin/legal-flag.html | 6 +++++-
.../coprs/views/admin_ns/admin_general.py | 12 ++++++++++++
3 files changed, 23 insertions(+), 1 deletions(-)
diff --git a/coprs_frontend/coprs/static/copr.css b/coprs_frontend/coprs/static/copr.css
index a1fa40e..dcc68d0 100644
--- a/coprs_frontend/coprs/static/copr.css
+++ b/coprs_frontend/coprs/static/copr.css
@@ -328,6 +328,12 @@ div.legal-flag div.message {
font-size: 1em;
}
+div.legal-flag form {
+ display: inline;
+ text-align: right;
+ float: right;
+}
+
hr {
margin-top: 25px;
margin-bottom: 25px;
diff --git a/coprs_frontend/coprs/templates/admin/legal-flag.html b/coprs_frontend/coprs/templates/admin/legal-flag.html
index f7252c2..4cce6e5 100644
--- a/coprs_frontend/coprs/templates/admin/legal-flag.html
+++ b/coprs_frontend/coprs/templates/admin/legal-flag.html
@@ -15,11 +15,15 @@
{% else %}
{{ flag.old_value }} - copr was deleted
{% endif %}
- | <a href="">Resolve</a>
+ <form method="post" action="{{ url_for('admin_ns.legal_flag_resolve', action_id=flag.id) }}">
+ <input type="submit" value="Resolve">
+ </form>
</div>
<div class="message">
{{ flag.message }}
</div>
</div>
+ {% else %}
+ <h2>No coprs marked for legal review</h2>
{% endfor %}
{% endblock %}
diff --git a/coprs_frontend/coprs/views/admin_ns/admin_general.py b/coprs_frontend/coprs/views/admin_ns/admin_general.py
index c1b6146..d094b36 100644
--- a/coprs_frontend/coprs/views/admin_ns/admin_general.py
+++ b/coprs_frontend/coprs/views/admin_ns/admin_general.py
@@ -1,5 +1,8 @@
+import time
+
import flask
+from coprs import db
from coprs import helpers
from coprs import models
@@ -33,3 +36,12 @@ def legal_flag():
return flask.render_template('admin/legal-flag.html',
legal_flags=legal_flags)
+
+(a)admin_ns.route('/legal-flag/<int:action_id>/resolve/', methods=['POST'])
+(a)login_required(role=helpers.RoleEnum('admin'))
+def legal_flag_resolve(action_id):
+ action = models.Action.query.filter(models.Action.id==action_id).\
+ update({'ended_on': int(time.time())})
+ db.session.commit()
+ flask.flash('Legal flag resolved')
+ return flask.redirect(flask.url_for('admin_ns.legal_flag'))
10 years, 11 months
[copr] master: Admins can delete coprs now (f21e346)
by bkabrda@fedoraproject.org
Repository : http://git.fedorahosted.org/cgit/copr.git
On branch : master
>---------------------------------------------------------------
commit f21e3462ff5c9d63f2bb6b661819c726029b2306
Author: Bohuslav Kabrda <bkabrda(a)redhat.com>
Date: Tue Apr 2 13:47:15 2013 +0200
Admins can delete coprs now
>---------------------------------------------------------------
coprs_frontend/coprs/logic/coprs_logic.py | 11 +++++++++--
1 files changed, 9 insertions(+), 2 deletions(-)
diff --git a/coprs_frontend/coprs/logic/coprs_logic.py b/coprs_frontend/coprs/logic/coprs_logic.py
index d5cf340..2c15df0 100644
--- a/coprs_frontend/coprs/logic/coprs_logic.py
+++ b/coprs_frontend/coprs/logic/coprs_logic.py
@@ -121,8 +121,7 @@ class CoprsLogic(object):
@classmethod
def delete(cls, user, copr, check_for_duplicates=True):
- if not copr.owner == user:
- raise exceptions.InsufficientRightsException('Only owners may delete their Coprs.')
+ cls.raise_if_cant_delete(user, copr)
# TODO: do we want to dump the information somewhere, so that we can search it in future?
cls.raise_if_unfinished_blocking_action(user, copr,
'Can\'t delete this Copr, another operation is in progress: {action}')
@@ -170,6 +169,14 @@ class CoprsLogic(object):
if unfinished_actions:
raise exceptions.ActionInProgressException(message, unfinished_actions[0])
+ @classmethod
+ def raise_if_cant_delete(cls, user, copr):
+ """This method raises InsufficientRightsException if given copr cant be deleted
+ by given user. Returns None otherwise.
+ """
+ if not user.admin and user != copr.owner:
+ raise exceptions.InsufficientRightsException('Only owners may delete their Coprs.')
+
class CoprPermissionsLogic(object):
@classmethod
def get(cls, user, copr, searched_user):
10 years, 11 months
[copr] master: Add links to delete/freeze (freeze doesn't work yet) copr in admin (3d9a60f)
by bkabrda@fedoraproject.org
Repository : http://git.fedorahosted.org/cgit/copr.git
On branch : master
>---------------------------------------------------------------
commit 3d9a60f0c42c7a55b9306d994e3acd2c9702fbaa
Author: Bohuslav Kabrda <bkabrda(a)redhat.com>
Date: Tue Apr 2 13:39:15 2013 +0200
Add links to delete/freeze (freeze doesn't work yet) copr in admin
>---------------------------------------------------------------
.../coprs/templates/admin/legal-flag.html | 13 ++++++++++---
1 files changed, 10 insertions(+), 3 deletions(-)
diff --git a/coprs_frontend/coprs/templates/admin/legal-flag.html b/coprs_frontend/coprs/templates/admin/legal-flag.html
index e683c38..f7252c2 100644
--- a/coprs_frontend/coprs/templates/admin/legal-flag.html
+++ b/coprs_frontend/coprs/templates/admin/legal-flag.html
@@ -6,9 +6,16 @@
{% for flag in legal_flags %}
<div class="legal-flag">
<div>
- <a href={{ url_for('coprs_ns.copr_detail', username=flag.copr.owner.name, coprname=flag.copr.name) }}>
- {{ flag.copr.owner.name }}/{{ flag.copr.name }}
- </a>
+ {% if flag.copr %}
+ <a href={{ url_for('coprs_ns.copr_detail', username=flag.copr.owner.name, coprname=flag.copr.name) }}>
+ {{ flag.copr.owner.name }}/{{ flag.copr.name }}
+ </a>
+ | <a href="">Freeze</a>
+ | <a href="{{ url_for('coprs_ns.copr_delete', username=flag.copr.owner.name, coprname=flag.copr.name) }}">Delete</a>
+ {% else %}
+ {{ flag.old_value }} - copr was deleted
+ {% endif %}
+ | <a href="">Resolve</a>
</div>
<div class="message">
{{ flag.message }}
10 years, 11 months