From 94c7ca967f990c6b96121fdaec5f70c4eb06c1d6 Mon Sep 17 00:00:00 2001 From: Randy Barlow Date: Wed, 17 Jan 2018 14:38:21 -0500 Subject: [PATCH] Don't use autoflush when setting the release on a new Update. fixes #2117 Signed-off-by: Randy Barlow --- bodhi/server/models.py | 9 ++++++--- bodhi/server/util.py | 28 +++++++++++++++++++++++++++- bodhi/tests/server/test_util.py | 27 ++++++++++++++++++++++++++- 3 files changed, 59 insertions(+), 5 deletions(-) diff --git a/bodhi/server/models.py b/bodhi/server/models.py index 45a91f7a82..b8f3546ce2 100644 --- a/bodhi/server/models.py +++ b/bodhi/server/models.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright © 2011-2017 Red Hat, Inc. and others. +# Copyright © 2011-2018 Red Hat, Inc. and others. # # This file is part of Bodhi. # @@ -48,7 +48,7 @@ from bodhi.server.exceptions import BodhiException, LockedUpdateException from bodhi.server.util import ( avatar as get_avatar, build_evr, flash_log, get_critpath_components, - get_nvr, get_rpm_header, header, packagename_from_nvr, tokenize, pagure_api_get) + get_nvr, get_rpm_header, header, packagename_from_nvr, tokenize, pagure_api_get, no_autoflush) import bodhi.server.util @@ -1773,7 +1773,10 @@ def new(cls, request, data): log.debug("Creating new Update(**data) object.") release = data.pop('release', None) up = Update(**data) - up.release = release + # Autoflush will cause a problem for Update.validate_release(). + # https://github.com/fedora-infra/bodhi/issues/2117 + with no_autoflush(db): + up.release = release # Assign the alias before setting the request. # Setting the request publishes a fedmsg message, and it is nice to diff --git a/bodhi/server/util.py b/bodhi/server/util.py index 795607daa0..03b60526a4 100644 --- a/bodhi/server/util.py +++ b/bodhi/server/util.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright © 2007-2017 Red Hat, Inc. and others. +# Copyright © 2007-2018 Red Hat, Inc. and others. # # This file is part of Bodhi. # @@ -1282,3 +1282,29 @@ def greenwave_api_post(greenwave_api_url, data): # based on the error message return call_api(greenwave_api_url, service_name='Greenwave', method='POST', data=data, retries=3) + + +class no_autoflush(object): + """ + A content manager that disables sqlalchemy's autoflush, restoring it afterwards. + + Adapted from https://bitbucket.org/zzzeek/sqlalchemy/wiki/UsageRecipes/DisableAutoflush + """ + + def __init__(self, session): + """ + Store the session and remember its entrant state. + + Args: + session (sqlalchemy.orm.session.Session): The session to disable autoflush on. + """ + self.session = session + self.autoflush = session.autoflush + + def __enter__(self): + """Disable autoflush.""" + self.session.autoflush = False + + def __exit__(self, *args, **kwargs): + """Restore autoflush to its entrant state. Args unused.""" + self.session.autoflush = self.autoflush diff --git a/bodhi/tests/server/test_util.py b/bodhi/tests/server/test_util.py index 376f4625c1..0a26bbfa5d 100644 --- a/bodhi/tests/server/test_util.py +++ b/bodhi/tests/server/test_util.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright © 2007-2017 Red Hat, Inc. and others. +# Copyright © 2007-2018 Red Hat, Inc. and others. # # This file is part of Bodhi. # @@ -151,6 +151,31 @@ def json(self): sleep.assert_called_once_with(1) +class TestNoAutoflush(unittest.TestCase): + """Test the no_autoflush context manager.""" + def test_autoflush_disabled(self): + """Test correct behavior when autoflush is disabled.""" + session = mock.MagicMock() + session.autoflush = False + + with util.no_autoflush(session): + self.assertFalse(session.autoflush) + + # autoflush should still be False since that was the starting condition. + self.assertFalse(session.autoflush) + + def test_autoflush_enabled(self): + """Test correct behavior when autoflush is enabled.""" + session = mock.MagicMock() + session.autoflush = True + + with util.no_autoflush(session): + self.assertFalse(session.autoflush) + + # autoflush should again be True since that was the starting condition. + self.assertTrue(session.autoflush) + + class TestPushToBatchedOrStableButton(base.BaseTestCase): """Test the push_to_batched_or_stable_button() function.""" def test_request_is_batched(self):