Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added mapping for critical packages for a release. #2476

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .coverage.bodhi-dev.example.com.8566.054810
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
!coverage.py: This is a private format, don't read it directly!{"arcs":{"/home/vagrant/bodhi/bodhi/__init__.py":[[-19,19],[19,-19]],"/home/vagrant/bodhi/bodhi/server/notifications.py":[],"/home/vagrant/bodhi/bodhi/server/metadata.py":[],"/home/vagrant/bodhi/bodhi/server/buildsys.py":[],"/home/vagrant/bodhi/bodhi/server/ffmarkdown.py":[],"/home/vagrant/bodhi/bodhi/server/config.py":[],"/home/vagrant/bodhi/bodhi/server/validators.py":[],"/home/vagrant/bodhi/bodhi/server/mail.py":[],"/home/vagrant/bodhi/bodhi/server/captcha.py":[],"/home/vagrant/bodhi/bodhi/server/webapp.py":[],"/home/vagrant/bodhi/bodhi/server/exceptions.py":[],"/home/vagrant/bodhi/bodhi/server/security.py":[],"/home/vagrant/bodhi/bodhi/server/__init__.py":[],"/home/vagrant/bodhi/bodhi/server/models.py":[],"/home/vagrant/bodhi/bodhi/server/util.py":[],"/home/vagrant/bodhi/bodhi/server/bugs.py":[],"/home/vagrant/bodhi/bodhi/server/push.py":[],"/home/vagrant/bodhi/bodhi/server/schemas.py":[],"/home/vagrant/bodhi/bodhi/server/renderers.py":[],"/home/vagrant/bodhi/bodhi/server/scripts/untag_branched.py":[],"/home/vagrant/bodhi/bodhi/server/scripts/expire_overrides.py":[],"/home/vagrant/bodhi/bodhi/server/scripts/check_policies.py":[],"/home/vagrant/bodhi/bodhi/server/scripts/monitor_composes.py":[],"/home/vagrant/bodhi/bodhi/server/scripts/approve_testing.py":[],"/home/vagrant/bodhi/bodhi/server/scripts/__init__.py":[],"/home/vagrant/bodhi/bodhi/server/scripts/initializedb.py":[],"/home/vagrant/bodhi/bodhi/server/scripts/clean_old_mashes.py":[],"/home/vagrant/bodhi/bodhi/server/scripts/dequeue_stable.py":[],"/home/vagrant/bodhi/bodhi/server/scripts/manage_releases.py":[],"/home/vagrant/bodhi/bodhi/server/scripts/skopeo_lite.py":[],"/home/vagrant/bodhi/bodhi/server/consumers/updates.py":[],"/home/vagrant/bodhi/bodhi/server/consumers/masher.py":[],"/home/vagrant/bodhi/bodhi/server/consumers/__init__.py":[],"/home/vagrant/bodhi/bodhi/server/consumers/signed.py":[],"/home/vagrant/bodhi/bodhi/server/services/composes.py":[],"/home/vagrant/bodhi/bodhi/server/services/builds.py":[],"/home/vagrant/bodhi/bodhi/server/services/updates.py":[],"/home/vagrant/bodhi/bodhi/server/services/csrf.py":[],"/home/vagrant/bodhi/bodhi/server/services/markdown.py":[],"/home/vagrant/bodhi/bodhi/server/services/packages.py":[],"/home/vagrant/bodhi/bodhi/server/services/__init__.py":[],"/home/vagrant/bodhi/bodhi/server/services/releases.py":[],"/home/vagrant/bodhi/bodhi/server/services/stacks.py":[],"/home/vagrant/bodhi/bodhi/server/services/user.py":[],"/home/vagrant/bodhi/bodhi/server/services/overrides.py":[],"/home/vagrant/bodhi/bodhi/server/services/errors.py":[],"/home/vagrant/bodhi/bodhi/server/services/comments.py":[],"/home/vagrant/bodhi/bodhi/server/services/zz_redirects.py":[],"/home/vagrant/bodhi/bodhi/server/views/generic.py":[],"/home/vagrant/bodhi/bodhi/server/views/metrics.py":[],"/home/vagrant/bodhi/bodhi/server/views/search.py":[],"/home/vagrant/bodhi/bodhi/server/views/__init__.py":[],"/home/vagrant/bodhi/bodhi/server/views/admin.py":[],"/home/vagrant/bodhi/bodhi/client/__init__.py":[],"/home/vagrant/bodhi/bodhi/client/bindings.py":[]}}
Binary file added bodhi-tests.sqlite-journal
Binary file not shown.
122 changes: 122 additions & 0 deletions bodhi/client/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,14 @@ def _set_logging_debug(ctx, param, value):
debug_option]


# Common manage critpath packages options
manage_critpath_options = [
click.option('--user'),
click.option('--password', hide_input=True),
click.argument('name'),
click.argument('packages', nargs=-1)]


def add_options(options):
"""
Generate a click.option decorator with the given options.
Expand Down Expand Up @@ -1032,6 +1040,101 @@ def info_release(name, url, **kwargs):
print_release(res)


@releases.group(name='critpath')
def critpath_pkgs():
"""Manage critical path packages."""
pass # pragma: no cover


@critpath_pkgs.command(name='list')
@handle_errors
@click.argument('name')
@url_option
@staging_option
def list_critpath_packages(name, url, staging, **kwargs):
# User Docs that show in the --help
"""
List critical path packages of a release.

NAME: Release name (e.g. F27)
"""
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For this one we do want to show both developer docs (argument types) and user docs (what you have here), similar to what we've done on other functions in this file.

# Developer Docs
"""
Args:
name (unicode): The release name to query.
url (unicode): The URL of a Bodhi server to query on. Ignored if staging is
True.
staging (bool): Whether to use the staging server or not.
kwargs (dict): Other keyword arguments passed to us by click.
"""
client = bindings.BodhiClient(base_url=url, staging=staging)
res = client.get_critpath_packages(release=name)

print_critpath_packages(res)


@critpath_pkgs.command(name='add')
@handle_errors
@add_options(manage_critpath_options)
@url_option
@staging_option
def add_critpath_packages(user, password, name, packages, url, staging, **kwargs):
# User Docs that show in the --help
"""
Add critical path packages to a release.

NAME: Release name (e.g. F27)

[PACKAGES]: Space-separated list of packages to add (e.g. firefox vim python)
"""
# Developer Docs
"""
Args:
name (unicode): The release name to add critpath packages to.
packages (list): A list of package names to add.
url (unicode): The URL of a Bodhi server to query on. Ignored if staging is
True.
staging (bool): Whether to use the staging server or not.
kwargs (dict): Other keyword arguments passed to us by click.
"""
client = bindings.BodhiClient(base_url=url, username=user, password=password, staging=staging)

res = client.add_critpath_packages(release=name, packages=packages)

print_critpath_packages(res)


@critpath_pkgs.command(name='remove')
@handle_errors
@add_options(manage_critpath_options)
@url_option
@staging_option
def remove_critpath_packages(user, password, name, packages, url, staging, **kwargs):
# User Docs that show in the --help
"""
Remove critical path packages from a release.

NAME: Release name (e.g. F27)

[PACKAGES]: Space-separated list of packages to remove (e.g. firefox vim python)
"""
# Developer Docs
"""
Args:
name (unicode): The release name to remove critpath packages from.
packages (list): A list of package names to remove.
url (unicode): The URL of a Bodhi server to query on. Ignored if staging is
True.
staging (bool): Whether to use the staging server or not.
kwargs (dict): Other keyword arguments passed to us by click.
"""
client = bindings.BodhiClient(base_url=url, username=user, password=password, staging=staging)

res = client.remove_critpath_packages(release=name, packages=packages)

print_critpath_packages(res)


def save(client, **kwargs):
"""
Save a new or edited release.
Expand Down Expand Up @@ -1075,6 +1178,25 @@ def print_release(release):
print(" Email Template: %s" % release['mail_template'])


def print_critpath_packages(response):
"""
Print critical path packages of a release.

Args:
response (dict): A dictionary with a single key, packages, mapping to a list of Package
objects that are in critical path for this Release.
"""
packages = response['packages']

if packages:
package_names = sorted([pkg['name'] for pkg in packages])
click.echo("Critical path packages:")
for pkg_name in package_names:
click.echo(" - {}".format(pkg_name))
else:
click.echo("No critical path packages for this release.")


def print_errors(data):
"""
Print errors to the terminal and exit with code 1.
Expand Down
75 changes: 75 additions & 0 deletions bodhi/client/bindings.py
Original file line number Diff line number Diff line change
Expand Up @@ -858,6 +858,81 @@ def get_releases(self, **kwargs):
"""
return self.send_request('releases/', verb='GET', params=kwargs)

@errorhandled
def get_critpath_packages(self, release):
"""
Get a list of critical path packages for a release.

Args:
release (unicode): The release name to query for.
Returns:
dict: A dictionary returned by critpath_package_requests() method.
"""
return self.critpath_package_request(release=release, verb='GET', auth=False, data=None)

@errorhandled
def add_critpath_packages(self, release, packages):
"""
Add a list of critical path packages to a release.

Args:
release (unicode): The release name to add packages to.
packages (list): List of critpath package names to add.
Returns:
dict: A dictionary returned by critpath_package_requests() method.
"""
package_str = ','.join(packages)
return self.critpath_package_request(release=release, verb='PUT', auth=True,
data={'packages': package_str})

@errorhandled
def remove_critpath_packages(self, release, packages):
"""
Remove a list of critical path packages from a release.

Args:
release (unicode): The release name to remove packages from.
packages (list): List of critpath package names to remove.
Returns:
dict: A dictionary returned by critpath_package_requests() method.
"""
package_str = ','.join(packages)
# DELETE request method ignores the body sometimes, so passing dictionary doesn't work
# However, passing it's string representation works somehow.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should identify why this is the case. Is it due to a bug in python-fedora? Is it the REST API?

return self.critpath_package_request(release=release, verb='DELETE', auth=True,
data='{"packages": "%s"}' % package_str)

@errorhandled
def critpath_package_request(self, release, verb, auth, data):
"""
Return a list of critical path packages for a release after request.

Request can be for adding new critpath packages, remove existing critpath
packages or simply getting critpath packages for the release.

This method returns a dictionary in the following format::

{"packages": [
{"stack_id": null, "type": "rpm", "requirements": null,
"name": "python", "stack": null}]}

Args:
release (unicode): The release name to interact with.
verb (unicode): Type of request to make.
'GET': list packages
'PUT': add packages
'DELETE': remove packages
auth (bool): Whether to authenticate for send_request.
data (dict or unicode): data to be sent along the request.
Returns:
dict: A dictionary with a single key, packages, mapping to a list of Package
objects that are in critical path for this Release.
"""
if verb not in ('GET', 'PUT', 'DELETE'):
raise BodhiClientException("Unsupported request type.")
return self.send_request('releases/{}/critpath-packages'.format(release), verb=verb,
auth=auth, data=data)

def get_koji_session(self):
"""
Return an authenticated koji session.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# Copyright (c) 2018 Red Hat, Inc.
#
# This file is part of Bodhi.
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
"""
Add critpath field for package and release.

Revision ID: 7a9ac7812e80
Revises: 385acbb51075
Create Date: 2018-07-24 18:12:13.854859
"""
from alembic import op
import sqlalchemy as sa


# revision identifiers, used by Alembic.
revision = '7a9ac7812e80'
down_revision = '385acbb51075'


def upgrade():
"""Add relationship to store the critical path packages."""
op.create_table('critical_path_packages_table',
sa.Column('package_id', sa.Integer(), nullable=False),
sa.Column('release_id', sa.Integer(), nullable=False),
sa.ForeignKeyConstraint(['package_id'], ['packages.id'], ),
sa.ForeignKeyConstraint(['release_id'], ['releases.id'], ))


def downgrade():
"""Drop the relationship for storing the critical path packages."""
op.drop_table('critical_path_packages_table')
34 changes: 33 additions & 1 deletion bodhi/server/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -791,6 +791,12 @@ class ComposeState(DeclEnum):
Column('user_id', Integer, ForeignKey('users.id')),
Column('package_id', Integer, ForeignKey('packages.id')))

critical_path_packages_table = Table(
'critical_path_packages_table', Base.metadata,
Column('package_id', Integer, ForeignKey('packages.id'), nullable=False),
Column('release_id', Integer, ForeignKey('releases.id'), nullable=False)
)


class Release(Base):
"""
Expand Down Expand Up @@ -958,6 +964,28 @@ def from_tags(cls, tags, session):
if release:
return release

def add_critpath_pkgs(self, packages):
"""
Add critical path packages to this Release.

Args:
packages (list): A list of packages to add to critical path for this Release.
"""
for pkg in packages:
if pkg not in self.critpath_pkgs:
self.critpath_pkgs.append(pkg)

def remove_critpath_pkgs(self, packages):
"""
Remove critical path packages for this Release.

Args:
packages (list): A list of packages to remove from critical path for this Release.
"""
for pkg in packages:
if pkg in self.critpath_pkgs:
self.critpath_pkgs.remove(pkg)


class TestCase(Base):
"""
Expand Down Expand Up @@ -1000,7 +1028,7 @@ class to create when loading rows from the database.

__tablename__ = 'packages'
__get_by__ = ('name',)
__exclude_columns__ = ('id', 'committers', 'test_cases', 'builds',)
__exclude_columns__ = ('id', 'committers', 'test_cases', 'builds', 'critpath_releases',)

name = Column(UnicodeText, nullable=False)
requirements = Column(UnicodeText)
Expand All @@ -1011,6 +1039,10 @@ class to create when loading rows from the database.
committers = relationship('User', secondary=user_package_table,
backref='packages')

# Many-to-many relationship
critpath_releases = relationship('Release', secondary=critical_path_packages_table,
backref='critpath_pkgs')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's make sure this does not get serialized with the release, otherwise the critpath packages table will have to be queried every time a release is queried. You can do that by adding this to __exclude_columns__.


stack_id = Column(Integer, ForeignKey('stacks.id'))

__mapper_args__ = {
Expand Down
16 changes: 16 additions & 0 deletions bodhi/server/schemas.py
Original file line number Diff line number Diff line change
Expand Up @@ -414,6 +414,22 @@ class SaveReleaseSchema(CSRFProtectedSchema, colander.MappingSchema):
)


class UpdateCriticalPackages(colander.MappingSchema):
"""
An API schema for critical path packages.

This schema is used by bodhi.server.services.releases.add_critpath_packages()
and bodhi.server.services.releases.remove_critpath_packages().
"""

packages = Packages(
colander.Sequence(accept_scalar=True),
location='body',
missing=None,
preparer=[util.splitter],
)


class ListStackSchema(PaginatedSchema, SearchableSchema):
"""An API schema for bodhi.server.services.stacks.query_stacks()."""

Expand Down
Loading