Skip to content

Commit

Permalink
Check Greenwave status when updates are created or edited.
Browse files Browse the repository at this point in the history
fixes fedora-infra#1514

Signed-off-by: Randy Barlow <randy@electronsweatshop.com>
  • Loading branch information
bowlofeggs committed Dec 16, 2017
1 parent 6f97701 commit 3a1ce5f
Show file tree
Hide file tree
Showing 7 changed files with 136 additions and 36 deletions.
15 changes: 14 additions & 1 deletion bodhi/server/config.py
Expand Up @@ -221,6 +221,19 @@ def _validate_path(value):
return six.text_type(value)


def _validate_rstripped_str(value):
"""
Ensure that value is a str that is rstripped of the / character.
Args:
value (six.text_type): The value to be validated and rstripped.
Returns:
six.text_type: The rstripped value.
"""
value = six.text_type(value)
return value.rstrip('/')


def _validate_secret(value):
"""Ensure that the value is not CHANGEME and convert it to unicode.
Expand Down Expand Up @@ -413,7 +426,7 @@ class BodhiConfig(dict):
'validator': six.text_type},
'greenwave_api_url': {
'value': 'https://greenwave.fedoraproject.org/api/v1.0',
'validator': six.text_type},
'validator': _validate_rstripped_str},
'koji_hub': {
'value': 'https://koji.stg.fedoraproject.org/kojihub',
'validator': str},
Expand Down
5 changes: 5 additions & 0 deletions bodhi/server/consumers/updates.py
Expand Up @@ -135,6 +135,11 @@ def consume(self, message):
self.work_on_bugs(session, update, bugs)
self.fetch_test_cases(session, update)

if config['test_gating.required']:
with self.db_factory() as session:
update = Update.get(alias, session)
update.update_test_gating_status()

log.info("Updates Handler done with %s, %s" % (alias, topic))

def fetch_test_cases(self, session, update):
Expand Down
31 changes: 30 additions & 1 deletion bodhi/server/models.py
Expand Up @@ -43,7 +43,7 @@
from sqlalchemy.types import SchemaType, TypeDecorator, Enum
import six

from bodhi.server import bugs, buildsys, log, mail, notifications, Session
from bodhi.server import bugs, buildsys, log, mail, notifications, Session, util
from bodhi.server.config import config
from bodhi.server.exceptions import BodhiException, LockedUpdateException
from bodhi.server.util import (
Expand Down Expand Up @@ -1720,6 +1720,35 @@ def greenwave_subject_json(self):
"""
return json.dumps(self.greenwave_subject)

def update_test_gating_status(self):
"""Query Greenwave about this update and set the test_gating_status as appropriate."""
# We retrieve updates going to testing (status=pending) and updates
# (status=testing) going to stable.
# If the update is pending, we want to know if it can go to testing
decision_context = u'bodhi_update_push_testing'
if self.status == UpdateStatus.testing:
# Update is already in testing, let's ask if it can go to stable
decision_context = u'bodhi_update_push_stable'

data = {
'product_version': self.product_version,
'decision_context': decision_context,
'subject': self.greenwave_subject
}
api_url = '{}/decision'.format(config.get('greenwave_api_url'))

decision = util.greenwave_api_post(api_url, data)
if decision['policies_satisfied']:
# If an unrestricted policy is applied and no tests are required
# on this update, let's set the test gating as ignored in Bodhi.
if decision['summary'] == 'no tests are required':
self.test_gating_status = TestGatingStatus.ignored
else:
self.test_gating_status = TestGatingStatus.passed
else:
self.test_gating_status = TestGatingStatus.failed
self.greenwave_summary_string = decision['summary']

@classmethod
def new(cls, request, data):
"""
Expand Down
29 changes: 1 addition & 28 deletions bodhi/server/scripts/check_policies.py
Expand Up @@ -27,7 +27,6 @@
from sqlalchemy.sql.expression import false

from bodhi.server import config, initialize_db, models, Session
from bodhi.server.util import greenwave_api_post


@click.command()
Expand All @@ -41,34 +40,8 @@ def check():
.filter(models.Update.status.in_(
[models.UpdateStatus.pending, models.UpdateStatus.testing]))
for update in updates:
# We retrieve updates going to testing (status=pending) and updates
# (status=testing) going to stable.
# If the update is pending, we want to know if it can go to testing
decision_context = u'bodhi_update_push_testing'
if update.status == models.UpdateStatus.testing:
# Update is already in testing, let's ask if it can go to stable
decision_context = u'bodhi_update_push_stable'

data = {
'product_version': update.product_version,
'decision_context': decision_context,
'subject': update.greenwave_subject
}
api_url = '{}/decision'.format(
config.config.get('greenwave_api_url').rstrip('/'))

try:
decision = greenwave_api_post(api_url, data)
if decision['policies_satisfied']:
# If an unrestricted policy is applied and no tests are required
# on this update, let's set the test gating as ignored in Bodhi.
if decision['summary'] == 'no tests are required':
update.test_gating_status = models.TestGatingStatus.ignored
else:
update.test_gating_status = models.TestGatingStatus.passed
else:
update.test_gating_status = models.TestGatingStatus.failed
update.greenwave_summary_string = decision['summary']
update.update_test_gating_status()
session.commit()
except Exception as e:
# If there is a problem talking to Greenwave server, print the error.
Expand Down
67 changes: 66 additions & 1 deletion bodhi/tests/server/consumers/test_updates.py
@@ -1,5 +1,8 @@
# -*- coding: utf-8 -*-

# Copyright © 2016-2017 Red Hat, Inc. and Caleigh Runge-Hotman
#
# 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
Expand Down Expand Up @@ -83,6 +86,68 @@ def test_edited_update_bugs_in_update(self, work_on_bugs, fetch_test_cases):
self.assertTrue(isinstance(fetch_test_cases.mock_calls[0][1][1],
sqlalchemy.orm.session.Session))

@mock.patch.dict('bodhi.server.config.config', {'test_gating.required': False})
def test_gating_required_false(self):
"""Assert that test_gating_status is not updated if test_gating is not enabled."""
update = models.Update.query.filter_by(title=u'bodhi-2.0-1.fc17').one()
update.test_gating_status = None
hub = mock.MagicMock()
hub.config = {'environment': 'environment',
'topic_prefix': 'topic_prefix'}
h = updates.UpdatesHandler(hub)
h.db_factory = base.TransactionalSessionMaker(self.Session)
# Throw a bogus bug id in there to ensure it doesn't raise AssertionError.
message = {
'topic': 'bodhi.update.request.testing',
'body': {'msg': {'update': {'alias': u'bodhi-2.0-1.fc17'},
'new_bugs': []}}}

with mock.patch('bodhi.server.models.util.greenwave_api_post') as mock_greenwave:
greenwave_response = {
'policies_satisfied': False,
'summary': u'what have you done‽',
'applicable_policies': ['taskotron_release_critical_tasks'],
'unsatisfied_requirements': ['some arbitrary test you disagree with']
}
mock_greenwave.return_value = greenwave_response

h.consume(message)

update = models.Update.query.filter_by(title=u'bodhi-2.0-1.fc17').one()
self.assertIsNone(update.test_gating_status)
self.assertIsNone(update.greenwave_summary_string)

@mock.patch.dict('bodhi.server.config.config', {'test_gating.required': True})
def test_gating_required_true(self):
"""Assert that test_gating_status is updated when test_gating is enabled."""
update = models.Update.query.filter_by(title=u'bodhi-2.0-1.fc17').one()
update.test_gating_status = None
hub = mock.MagicMock()
hub.config = {'environment': 'environment',
'topic_prefix': 'topic_prefix'}
h = updates.UpdatesHandler(hub)
h.db_factory = base.TransactionalSessionMaker(self.Session)
# Throw a bogus bug id in there to ensure it doesn't raise AssertionError.
message = {
'topic': 'bodhi.update.request.testing',
'body': {'msg': {'update': {'alias': u'bodhi-2.0-1.fc17'},
'new_bugs': []}}}

with mock.patch('bodhi.server.models.util.greenwave_api_post') as mock_greenwave:
greenwave_response = {
'policies_satisfied': False,
'summary': u'what have you done‽',
'applicable_policies': ['taskotron_release_critical_tasks'],
'unsatisfied_requirements': ['some arbitrary test you disagree with']
}
mock_greenwave.return_value = greenwave_response

h.consume(message)

update = models.Update.query.filter_by(title=u'bodhi-2.0-1.fc17').one()
self.assertEqual(update.test_gating_status, models.TestGatingStatus.failed)
self.assertEqual(update.greenwave_summary_string, u'what have you done‽')

# We're going to use side effects to mock but still call work_on_bugs and fetch_test_cases so we
# can ensure that we aren't raising Exceptions from them, while allowing us to only assert that
# we called them correctly without having to assert all of their behaviors as well.
Expand Down
10 changes: 5 additions & 5 deletions bodhi/tests/server/scripts/test_check_policies.py
Expand Up @@ -36,7 +36,7 @@ def test_policies_satisfied(self):
update = self.db.query(models.Update).all()[0]
update.status = models.UpdateStatus.testing
self.db.commit()
with patch('bodhi.server.scripts.check_policies.greenwave_api_post') as mock_greenwave:
with patch('bodhi.server.models.util.greenwave_api_post') as mock_greenwave:
greenwave_response = {
'policies_satisfied': True,
'summary': 'All tests passed',
Expand Down Expand Up @@ -66,7 +66,7 @@ def test_policies_pending_satisfied(self):
update = self.db.query(models.Update).all()[0]
update.status = models.UpdateStatus.pending
self.db.commit()
with patch('bodhi.server.scripts.check_policies.greenwave_api_post') as mock_greenwave:
with patch('bodhi.server.models.util.greenwave_api_post') as mock_greenwave:
greenwave_response = {
'policies_satisfied': True,
'summary': 'All tests passed',
Expand Down Expand Up @@ -95,7 +95,7 @@ def test_policies_unsatisfied(self):
update = self.db.query(models.Update).all()[0]
update.status = models.UpdateStatus.testing
self.db.commit()
with patch('bodhi.server.scripts.check_policies.greenwave_api_post') as mock_greenwave:
with patch('bodhi.server.models.util.greenwave_api_post') as mock_greenwave:
greenwave_response = {
'policies_satisfied': False,
'summary': '1 of 2 tests are failed',
Expand Down Expand Up @@ -134,7 +134,7 @@ def test_no_policies_enforced(self):
update.status = models.UpdateStatus.testing
update.test_gating_status = None
self.db.commit()
with patch('bodhi.server.scripts.check_policies.greenwave_api_post') as mock_greenwave:
with patch('bodhi.server.models.util.greenwave_api_post') as mock_greenwave:
mock_greenwave.return_value = RuntimeError('The error was blablabla')

result = runner.invoke(check_policies.check, [])
Expand All @@ -158,7 +158,7 @@ def test_unrestricted_policy(self):
update = self.db.query(models.Update).all()[0]
update.status = models.UpdateStatus.testing
self.db.commit()
with patch('bodhi.server.scripts.check_policies.greenwave_api_post') as mock_greenwave:
with patch('bodhi.server.models.util.greenwave_api_post') as mock_greenwave:
greenwave_response = {
'policies_satisfied': True,
'summary': 'no tests are required',
Expand Down
15 changes: 15 additions & 0 deletions bodhi/tests/server/test_config.py
Expand Up @@ -410,6 +410,21 @@ def test_path_exists(self):
self.assertTrue(isinstance(result, six.text_type))


class ValidateRstrippedStrTests(unittest.TestCase):
"""Test the _validate_rstripped_str() function."""
def test_with_trailing_slash(self):
"""Ensure that a trailing slash is removed."""
result = config._validate_rstripped_str('this/should/be/rstripped/')

self.assertEqual(result, 'this/should/be/rstripped')

def test_without_trailing_slash(self):
"""With no trailing slash, the string should be returned as is."""
result = config._validate_rstripped_str('this/should/stay/the/same')

self.assertEqual(result, 'this/should/stay/the/same')


class ValidateSecretTests(unittest.TestCase):
"""Test the _validate_secret() function."""
def test_with_changeme(self):
Expand Down

0 comments on commit 3a1ce5f

Please sign in to comment.