From c73a853c260fec51551b931a06b587e42c48a4ca Mon Sep 17 00:00:00 2001
From: Yashvardhan Nanavati <yashn@bu.edu>
Date: Jun 24 2019 14:14:06 +0000
Subject: Add support for manual dependent rebuilds on existing events


---

diff --git a/freshmaker/events.py b/freshmaker/events.py
index ded8806..3f5c16c 100644
--- a/freshmaker/events.py
+++ b/freshmaker/events.py
@@ -317,7 +317,7 @@ class ManualRebuildWithAdvisoryEvent(ErrataAdvisoryRPMsSignedEvent):
     """
 
     def __init__(self, msg_id, advisory, container_images,
-                 requester_metadata_json=None, **kwargs):
+                 requester_metadata_json=None, freshmaker_event_id=None, **kwargs):
         """
         Creates new ManualRebuildWithAdvisoryEvent.
 
@@ -325,11 +325,14 @@ class ManualRebuildWithAdvisoryEvent(ErrataAdvisoryRPMsSignedEvent):
         :param ErrataAdvisory advisory: Errata advisory associated with event.
         :param list container_images: List of NVRs of images to rebuild or
             empty list to rebuild all images affected by the advisory.
+        :param freshmaker_event_id: Freshmaker event id on which this manual rebuild
+            is based off on.
         """
         super(ManualRebuildWithAdvisoryEvent, self).__init__(
             msg_id, advisory, **kwargs)
         self.container_images = container_images
         self.requester_metadata_json = requester_metadata_json
+        self.freshmaker_event_id = freshmaker_event_id
 
 
 class BrewSignRPMEvent(BaseEvent):
diff --git a/freshmaker/models.py b/freshmaker/models.py
index 8bb3c47..7379f30 100644
--- a/freshmaker/models.py
+++ b/freshmaker/models.py
@@ -244,6 +244,10 @@ class Event(FreshmakerBase):
         return session.query(cls).filter(cls.released == false(),
                                          cls.state.in_(states)).all()
 
+    @classmethod
+    def get_by_event_id(cls, session, event_id):
+        return session.query(cls).filter_by(id=event_id).first()
+
     def get_image_builds_in_first_batch(self, session):
         return session.query(ArtifactBuild).filter_by(
             dep_on=None,
diff --git a/freshmaker/parsers/internal/manual_rebuild.py b/freshmaker/parsers/internal/manual_rebuild.py
index 69e5aaf..51f0aa6 100644
--- a/freshmaker/parsers/internal/manual_rebuild.py
+++ b/freshmaker/parsers/internal/manual_rebuild.py
@@ -52,7 +52,7 @@ class FreshmakerManualRebuildParser(BaseParser):
 
         event = ManualRebuildWithAdvisoryEvent(
             msg_id, advisory, data.get("container_images", []), data.get("metadata", None),
-            manual=True, dry_run=dry_run)
+            freshmaker_event_id=data.get('freshmaker_event_id'), manual=True, dry_run=dry_run)
 
         return event
 
diff --git a/freshmaker/views.py b/freshmaker/views.py
index fbd39ec..ba2fd33 100644
--- a/freshmaker/views.py
+++ b/freshmaker/views.py
@@ -329,6 +329,10 @@ class BuildAPI(MethodView):
         parser = FreshmakerManualRebuildParser()
         event = parser.parse_post_data(data)
 
+        dependent_event = None
+        if event.freshmaker_event_id:
+            dependent_event = models.Event.get_by_event_id(db.session, event.freshmaker_event_id)
+
         # Store the event into database, so it gets the ID which we can return
         # to client sending this POST request. The client can then use the ID
         # to check for the event status.
@@ -337,6 +341,11 @@ class BuildAPI(MethodView):
         db_event.requested_rebuilds = " ".join(event.container_images)
         if event.requester_metadata_json:
             db_event.requester_metadata = json.dumps(event.requester_metadata_json)
+        if dependent_event:
+            dependency = db_event.add_event_dependency(db.session, dependent_event)
+            if not dependency:
+                log.warn('Dependency between {} and {} could not be added!'.format(
+                    event.freshmaker_event_id, dependent_event.id))
         db.session.commit()
 
         # Forward the POST data (including the msg_id of the database event we
diff --git a/tests/test_views.py b/tests/test_views.py
index c54fa6a..0e0bb92 100644
--- a/tests/test_views.py
+++ b/tests/test_views.py
@@ -620,6 +620,36 @@ class TestManualTriggerRebuild(helpers.ModelsTestCase):
             {'msg_id': 'manual_rebuild_123', u'errata_id': 1,
              'metadata': {"foo": ["bar"]}})
 
+    @patch('freshmaker.messaging.publish')
+    @patch('freshmaker.parsers.internal.manual_rebuild.ErrataAdvisory.'
+           'from_advisory_id')
+    @patch('freshmaker.parsers.internal.manual_rebuild.time.time')
+    @patch('freshmaker.models.Event.add_event_dependency')
+    def test_dependent_manual_rebuild_on_existing_event(self, add_dependency, time,
+                                                        from_advisory_id, publish):
+        models.Event.create(db.session,
+                            "2017-00000000-0000-0000-0000-000000000003",
+                            "RHSA-2018-103", events.TestingEvent)
+        db.session.commit()
+        time.return_value = 123
+        from_advisory_id.return_value = ErrataAdvisory(
+            123, 'name', 'REL_PREP', ['rpm'])
+
+        resp = self.client.post(
+            '/api/1/builds/', data=json.dumps({
+                'errata_id': 1, 'container_images': ["foo-1-1"],
+                'freshmaker_event_id': 1}),
+            content_type='application/json')
+        data = json.loads(resp.get_data(as_text=True))
+        # Other fields are predictible.
+        self.assertEqual(data['requested_rebuilds'], ["foo-1-1"])
+        assert add_dependency.call_count == 1
+        assert "RHSA-2018-103" == add_dependency.call_args.args[1].search_key
+        publish.assert_called_once_with(
+            'manual.rebuild',
+            {'msg_id': 'manual_rebuild_123', u'errata_id': 1,
+             'container_images': ["foo-1-1"], 'freshmaker_event_id': 1})
+
 
 class TestOpenIDCLogin(ViewBaseTest):
     """Test that OpenIDC login"""

