Repository: fedora-infra/bodhi

Issue:
Updates should get obsoleted when their release becomes archived
We should mark all non-stable updates in a release as obsolete when the release gets archived.

We should also write a migration to obsolete existing updates that are associated with archived releases.

Once we've done this, we won't need the feature from #1845 anymore, so we can consider removing that as well.
Updates should get obsoleted when their release becomes archived
We should mark all non-stable updates in a release as obsolete when the release gets archived.

We should also write a migration to obsolete existing updates that are associated with archived releases.

Once we've done this, we won't need the feature from #1845 anymore, so we can consider removing that as well.


Discussion:

Resolving patch:
diff --git a/bodhi/server/migrations/versions/8e9dc57e082d_obsolete_updates_for_archived_release.py b/bodhi/server/migrations/versions/8e9dc57e082d_obsolete_updates_for_archived_release.py
new file mode 100644
--- /dev/null
+++ b/bodhi/server/migrations/versions/8e9dc57e082d_obsolete_updates_for_archived_release.py
@@ -0,0 +1,43 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2019 Sebastian Wojciechowski
+#
+# 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.
+"""
+Obsolete updates for archived release.
+
+Revision ID: 8e9dc57e082d
+Revises: d986618207bc
+Create Date: 2019-01-06 13:04:35.158562
+"""
+from alembic import op
+
+
+# revision identifiers, used by Alembic.
+revision = '8e9dc57e082d'
+down_revision = 'd986618207bc'
+
+
+def upgrade():
+    """Set archived releases updates status to 'obsolete'."""
+    op.execute("UPDATE updates SET status='obsolete' WHERE release_id in \
+                (SELECT id FROM releases WHERE state='archived') AND status NOT IN \
+                ('unpushed', 'obsolete', 'stable')")
+
+
+def downgrade():
+    """Raise an exception explaining that this migration cannot be reversed."""
+    raise NotImplementedError('This migration cannot be reversed.')
diff --git a/bodhi/server/services/releases.py b/bodhi/server/services/releases.py
--- a/bodhi/server/services/releases.py
+++ b/bodhi/server/services/releases.py
@@ -324,6 +324,23 @@ def save_release(request):
             log.info("Editing release: %s" % edited)
             r = request.db.query(Release).filter(Release.name == edited).one()
             for k, v in data.items():
+                # We have to change updates status to obsolete
+                # if state of release changes to archived
+                if k == "state" and v == ReleaseState.archived and \
+                        r.state != ReleaseState.archived:
+                    updates = request.db.query(Update).filter(Update.release_id == r.id).filter(
+                        Update.status.notin_(
+                            [UpdateStatus.obsolete, UpdateStatus.stable, UpdateStatus.unpushed]
+                        )
+                    ).all()
+                    for u in updates:
+                        u.status = UpdateStatus.obsolete
+                        u.comment(
+                            request.db,
+                            'This update is marked obsolete because '
+                            'the {} release is archived.'.format(u.release.name),
+                            author='bodhi',
+                        )
                 setattr(r, k, v)
 
     except Exception as e:


Test patch:
diff --git a/bodhi/tests/server/services/test_releases.py b/bodhi/tests/server/services/test_releases.py
--- a/bodhi/tests/server/services/test_releases.py
+++ b/bodhi/tests/server/services/test_releases.py
@@ -23,7 +23,7 @@
 
 from bodhi import server
 from bodhi.server.config import config
-from bodhi.server.models import Release, ReleaseState, Update
+from bodhi.server.models import Release, ReleaseState, Update, UpdateStatus
 from bodhi.server.util import get_absolute_path
 from bodhi.tests.server import base, create_update
 
@@ -368,6 +368,59 @@ def test_edit_mail_template_with_invalid_value(self):
         self.assertEqual(res.json_body['errors'][0]['description'],
                          u'"invalid_template_name" is not one of {}'.format(template_vals))
 
+    def test_change_release_state_to_archived(self):
+        """
+        Test that when we make release archived, all release updates state will change to
+        'obsolete' or or stay 'stable'/'unpushed'
+        """
+        python_nose = self.create_update([u'python-nose-1.3.7-11.fc17'])
+        python_paste_deploy = self.create_update([u'python-paste-deploy-1.5.2-8.fc17'])
+        firefox = self.create_update([u'firefox-61.0.2-3.fc17'])
+        python_test_update = self.create_update([u'python-test-update.fc22'], 'F22')
+        # Change status of F17 updates
+        python_nose.status = UpdateStatus.stable
+        python_paste_deploy.status = UpdateStatus.obsolete
+        firefox.status = UpdateStatus.unpushed
+        self.db.commit()
+        name = u"F17"
+
+        res = self.app.get('/releases/%s' % name, status=200)
+        r = res.json_body
+
+        r["edited"] = name
+        r["state"] = "archived"
+        r["csrf_token"] = self.get_csrf_token()
+
+        res = self.app.post("/releases/", r, status=200)
+
+        r = self.db.query(Release).filter(Release.name == name).one()
+        self.assertEqual(r.state, ReleaseState.archived)
+
+        # Expect update status not changed
+        python_nose = self.db.query(Update).filter_by(
+            title=u'python-nose-1.3.7-11.fc17').one()
+        self.assertEqual(python_nose.status, UpdateStatus.stable)
+        # Expect update status not changed
+        python_paste_deploy = self.db.query(Update).filter_by(
+            title=u'python-paste-deploy-1.5.2-8.fc17').one()
+        self.assertEqual(python_paste_deploy.status, UpdateStatus.obsolete)
+        # Expect update status not changed
+        firefox = self.db.query(Update).filter_by(
+            title=u'firefox-61.0.2-3.fc17').one()
+        self.assertEqual(firefox.status, UpdateStatus.unpushed)
+        # Expect update status changed to 'obsolete'
+        bodhi_update = self.db.query(Update).filter_by(
+            title=u'bodhi-2.0-1.fc17').one()
+        self.assertEqual(bodhi_update.status, UpdateStatus.obsolete)
+        # Check for the comment
+        expected_comment = (u'This update is marked obsolete because the F17 release '
+                            'is archived.')
+        self.assertEqual(bodhi_update.comments[-1].text, expected_comment)
+        # Expect update status not changed
+        python_test_update = self.db.query(Update).filter_by(
+            title=u'python-test-update.fc22').one()
+        self.assertEqual(python_test_update.status, UpdateStatus.pending)
+
     def test_get_single_release_html(self):
         res = self.app.get('/releases/f17', headers={'Accept': 'text/html'})
         self.assertEqual(res.content_type, 'text/html')